BIND 10 trac2427, updated. e47b08e8a356c1bf498aa5a615fcce86958f1fe0 [2427] Basic $ORIGIN handling

BIND 10 source code commits bind10-changes at lists.isc.org
Fri Dec 14 12:54:07 UTC 2012


The branch, trac2427 has been updated
  discards  538575ebb0da6dd4da36116dd585fda06788c041 (commit)
       via  e47b08e8a356c1bf498aa5a615fcce86958f1fe0 (commit)
       via  8fd5cc452a30a35df0a24b7bd1c30e76ea6bb54d (commit)
       via  a7e16e85fac60062df06be2f371868571d08e5ed (commit)
       via  8652d1d4c6e2f9538b20443eb0cd0f37672915ef (commit)
       via  ac36820764982483b3b25a2d2ed6e05cea6cf322 (commit)
       via  6260781b99d5b7c1f495e584abe9f74ea4e33e03 (commit)
       via  2c565f3b83fd09baffbeb82a7e8a4c56990f45dd (commit)
       via  ea9d025cbcd9a318a2946c1a7f00283885ff381f (commit)
       via  5d7e273a02e5f5a1f83db02260a2a22e729f3a10 (commit)
       via  57ad11888879be61a4d877b5ea4a29d504263e90 (commit)
       via  b25d6802e2236e06c9eb83a9508cb9bfca048563 (commit)
       via  0140368ed066c722e5d11d7f9cf1c01462cf7e13 (commit)
       via  fecfe82bdfc93fedc24263a8f1bbdcedf59a7b71 (commit)
       via  588ff1eef555b3362954296388e93cf8729f267b (commit)
       via  bb1f38e03fb01fb164a58d91bd96bc9fcf4c07df (commit)
       via  58664e2ecc10b71b6ce0126ccf8c5f336f7cd380 (commit)
       via  ce492ecf70bdd3fe483a7f4282e98443f72b6989 (commit)
       via  fd3ff10fe28782177dff77b15293bbb3306522b3 (commit)
       via  91d77e0da5203fe0d925d297d448258f40f21099 (commit)
       via  27033b59dcaf34216802401f19ec0b51e572b098 (commit)
       via  af1da46eef54e96be6765922f42a786b84db2014 (commit)
       via  b8fde1ab760f06e76c06e795b127f66784eb6aa7 (commit)
       via  01461d51fa2a533092b135604b33747908891792 (commit)
       via  a6709a421381c61dae6208d3cc503ef14fbebd43 (commit)
       via  807b5a13c25760f09f10bb8ea9242277b8928fdb (commit)
       via  c1ba429f5eb098d7b1a9498fe11b354bca8283bd (commit)
       via  235a1870910da0a8b567bf47c5fc8467d4bcb102 (commit)
       via  a8326a6fcae7f3b0fb708323750e781221511ffc (commit)
       via  c9825a5714cebba054619150cc6dc74dee8f94f5 (commit)
       via  c842187ad91270660a91ac054953649d3f011d54 (commit)
       via  86233a2049f66fc932915367dc7f26e499b0303c (commit)
       via  62df1d46455dc5cef85adc07031a07fb3d0e4b6b (commit)
       via  8743e25c800ac5994e11c932a2196fd8b485a737 (commit)
       via  c2d2a53b14e6330281b2c519e1bdd129d4de86b5 (commit)
       via  9672fb595c581044a01cb4836d33afe6b58f5ab3 (commit)
       via  572d3b1ae55ebbca06289bc53035042472a50138 (commit)
       via  70f71c5413f70d9cf5716911101e48face70d080 (commit)
       via  a70fd03351709b036a8ee48cd8276b400661e741 (commit)
       via  3666cb4d97d21903f1637796edab95bb466c213e (commit)
       via  42c5261913f5277e51be25a4e3de08223cf8949a (commit)
       via  fe8a9e7c2c315207cf643f27115a78e8b2b4eb7f (commit)
       via  550a5e0da25f7b3fa8051d988d70c116490b0f74 (commit)
       via  286787bed2ffcf6ea6ff2da240edc9f78ef0660d (commit)
       via  f4c12ef1784ce3abe462de0328446ab5c7702339 (commit)
       via  c405fba738036d7df5fe2b3792bb70a9b9eda9f6 (commit)
       via  d7441b27a527262614446fd678513b8d6a819214 (commit)
       via  52d965040ccb6e919a34abd95240fee3718c0247 (commit)
       via  bc8aae68ec152b7dce35c158758537bd9912688f (commit)
       via  c328aed83b7d9506291283b6afbeabb4a5455b07 (commit)
       via  a69ebe7341bfbbc01037e71e66b0c6e7121ca6cd (commit)
       via  92758ecffc245b5f9a86dbc8a2201a9c1397f7d8 (commit)
       via  de29c07129d41c96ee0d5eebdd30a1ea7fb9ac8a (commit)
       via  8aa69ddf3d2d99fd9050a07c20eb01dd4287de95 (commit)
       via  eb65b11ca44fb2b2c128b317449272240ce337c3 (commit)
       via  7f6c9d057cc0a7a10f41ce7da9c8565b9ee85246 (commit)
       via  5c72873a1145a7bcdc70d8b1f9ddce1198548c3c (commit)
       via  a56f7ff8c099d2d97a177cba073ee413ba3e9bd9 (commit)
       via  6fe86386be0e7598633fe35999112c1a6e3b0370 (commit)
       via  b193ad87c312d2e6ceb54f0f2746425ba4f59d63 (commit)
       via  b1c2cce67f50c0da6ff45744f98142353166482f (commit)
       via  cbb1e342dfef163cef8dba429aa8841093aff7af (commit)
       via  a2b2bf47cbbe033774013a875e3197c37fd71cce (commit)
       via  8792cbaca81edb2106863f8043fe490d148db54a (commit)
       via  9c2193e32b9c07dfe1db532cb26de1c1e16d1b48 (commit)
       via  6aa3a421c244e6caa57e42d9dd67fb2326ce51b5 (commit)
       via  0fab5f49d919476e7c58024081f3b939be51ff15 (commit)
       via  a40fac0707fac08e85b06974092bbdc15720f37f (commit)
       via  196c4d239ece786aa29b86e6b48bef337ca330fc (commit)
       via  1f943baf9448dcec53f5bcde3e9d21fae2eeb121 (commit)
       via  0462436914ec6bccff8e7825ba3f55d50acda03e (commit)
       via  e65b7b36f60f14b7abe083da411e6934cdfbae7a (commit)
       via  d81727d1263a242de4ce8155f7e7708a91b37058 (commit)
       via  0a4faa07777189ed9c25211987a1a9b574015a95 (commit)
       via  99b2935b21ba5ae3dd524ad5b781e70e4914f0e4 (commit)
       via  3de3abb8876fd8037712c9b6b5f216a0d13e59ef (commit)
       via  74a0abe5a6d10b28e4a3e360e87b129c232dea68 (commit)
       via  27442da4487da8da452967438bb8c784d0ac05c5 (commit)
       via  422a1fcd67e40f3e398c292b50952cc09bce2623 (commit)
       via  044c4779138290c78c20018d7f083aad7604d823 (commit)
       via  a9eef903b04db257e42e05b1a296251d4ebc6c04 (commit)
       via  7d7b75217f01f143f759b075005c5da834b172e6 (commit)
       via  230ccceb4112042ce3aadc7cfd6c9780afa23bbb (commit)
       via  c0be62cb0f27a0855c007beabeb0c3ba3c4fb0ff (commit)
       via  3f9c7bb8a987dc1b4e2a9266ac95cf231d8fe621 (commit)
       via  0cf505f04be53e6f198cb0d091bd90d74e597cbe (commit)
       via  669a650ec38562f5459a5144cf8549df1b3f1cc4 (commit)
       via  b73262553443c404f3211913272a2ff89be904da (commit)
       via  8627b6eb8915407f8bf29786b70916b4b6ac0567 (commit)
       via  ea1a4ef9ac12b377a8c6b407ac488a1cb3805ba0 (commit)
       via  172e704b668189d6057c4bfc80403edd59aadd5c (commit)
       via  8ad7de967cfee9788fa269640f1ce9e0d302ee04 (commit)
       via  3a5d06d3901871929d310ea0e968611e7bfa0f27 (commit)
       via  e5ae1675a59c6116c1d0c471b760e647bf6d31b5 (commit)
       via  0ece883214d68bb606764dd8c6572487af976833 (commit)
       via  00554310f20b5a29fbad6a6a9ba9856c5570853a (commit)
       via  eb5b276de3dc4c556c818ebfab63a14bf2f562eb (commit)
       via  0a9dafbece0545af535494a7e5745a14c87ed9f5 (commit)
       via  8065343bc1c50353260809254e1994642f2b1763 (commit)
       via  4f17a81c04b1364a0835e9a9b9c26adc6c159cd3 (commit)
       via  7942b605f69740f853100f36abad05e9a98d1e90 (commit)
       via  1572782282cd6b5835ad20f5d8538511616a7e2b (commit)
       via  e4e8de317591b5ac9fec818693fb2cdc5ce9e2a3 (commit)
       via  03b92733bfd3c13bc86c8f878b274cf33740ef15 (commit)
       via  8afd799648279d29c8fd9f2505383bf6ebbf0730 (commit)
       via  807cc540e16eedd53d714a4d9b61a2c24ae31c30 (commit)
       via  d87597de14b3c81e481b51ea6de8b3273305487a (commit)
       via  2a69f5cd6a1badf9fb1538cd8f1ca56cef054ba7 (commit)
       via  7e30c020cd6c0babcf1d2c426ce72eb6a83a7fe4 (commit)
       via  17fb43189b8e6a8defa8f6e0a01b13d8ded65d16 (commit)
       via  084a16684541f0d19e99998a2a29a92619ee0b94 (commit)
       via  8bf2c98d2e62d95c8f86d41895f4cfaca3371d68 (commit)
       via  12dba9fb977f4ca3e62da1f0e29739ca5a534ea8 (commit)
       via  c4b7c85d1a314e59b20aa055d81cff553b3b8de3 (commit)
       via  3e2d8b00ba86773f3cba03f935e5847cee109f84 (commit)
       via  32011f9a367b479a983dbb32e877590b18fbc275 (commit)
       via  02f306c854846cf972886c8f42e1b6e642c89e4a (commit)
       via  6af3de2d2ee4b43bb5056d8b11cba164ac693346 (commit)
       via  366f210b8217811fb1cd96ed996f3491c7d9766f (commit)
       via  93eddf78d2db40f314323dc708252c87ed846a48 (commit)
       via  835553eb309d100b062051f7ef18422d2e8e3ae4 (commit)
       via  a6b558c0d09f1812f347497857ab8aa42b33b0f7 (commit)
       via  cb4d9b014ea7d80efadfbdbfe2d1ab1dbde68bc1 (commit)
       via  5c9dba4001ebbb432a7313a91cb3bc7b440bb396 (commit)
       via  06eeaf025beb42ff26032d79b325718c97ed4550 (commit)
       via  52a60eb5c950491c450ec933f66a56318b4da669 (commit)
       via  331a48e08d6c155f68eda2218fc04ec7cc276cbb (commit)
       via  755a3b5fa194d08e883fec5899ac4b7bcdcbfd18 (commit)
       via  ce7db48d3ff5d5aad12b1da5e67ae60073cb2607 (commit)
       via  1c59115d79bc9a80821a72cfab2c1bb6a80137f2 (commit)
       via  fca31329405278e34049e2e19982a82bc6fdf4f2 (commit)
       via  512ccc6d8b7bc49b085289226407fe741ebe7a54 (commit)
       via  126aeb9d11c7f1d670a30bb2a5a3cad31cbd9c0d (commit)
       via  c23f87e8ac3ea781b38d688f8f7b58539f85e35a (commit)
       via  1378932f029f1e74cc606013778863161feca8e4 (commit)
       via  dfebd6128b4088ba489080cea7c53f141dce90ab (commit)
       via  8e39d7eb39a7174ac39b93e5425b9fc8f35feecf (commit)
       via  678ce7c7ba26b9728ae3a870896e8c880a618918 (commit)
       via  303f3a44c2676bba5248d2e17b93823ac127c7c4 (commit)
       via  c366a4f8deb99014add34a7c79715beaeb1d63f3 (commit)
       via  d9d6998c28cfcf825fb2845acc3b4eb705e991a4 (commit)
       via  181226ac0ced78a1092c14d54764828637a4d814 (commit)
       via  9430b1fef5b2cc99e97b4cfaf6ccd518bcd76198 (commit)
       via  0d69a3ddefcc66ffcf05683763ef2c7f27b7c392 (commit)
       via  e19573d76a7f11827ee1f2dec889f3edcf6385b9 (commit)
       via  261a171c3bd856a32cb40a280320af1d8179c78c (commit)
       via  c59714604459a820819a288ff03c4dadebda098b (commit)
       via  4a9ea530abdc7aff1f58f6c92c281e5ed35d6741 (commit)
       via  7e8e8bcaaba5f836b3868f6601e8b4e8314a431e (commit)
       via  4e8325e1b309f1d388a3055ec1e1df98c377f383 (commit)
       via  cbf6d3808e596d35fc8c83a5f35e13dfb4c2449b (commit)
       via  e6ebe1de8e6ab2982869ba27f1719e63fddeb0e3 (commit)
       via  de66fdd73b8f545768166b7406be878c137cc019 (commit)
       via  cdbe6dedf437b052cb60777c470cfda312f50b43 (commit)
       via  029ac581f439485d797d195914f52a60eb593a60 (commit)
       via  e8a23d31110a2824368bff0c41f79016d696f613 (commit)
       via  f7e301237c7806b582172fcf80a892659a3d9e32 (commit)
       via  4f806512c8b2a99f3b1dc43fda7130ec81d21c1f (commit)
       via  87cd15b3cc34738753d97e62a07681b245350f25 (commit)
       via  3c17e9586c94a0e4331cafe79e379e71f3d8a561 (commit)
       via  bae03c0fbf0a82c2b88cb683fc81c30ab7f70ad0 (commit)
       via  231bfc60a9327295a2431f7d04ed5a26962d6236 (commit)
       via  f1f6c26aa9f45367159229de64ca1545c87d51ac (commit)
       via  a6865a7686a02c7026150be1304ee682e8ddbb51 (commit)
       via  ea88e8907ab138e2fa1c5271c8a03ec53b4b08d0 (commit)
       via  05fc975a3d825a10c8b9c8df509854f9f69d9c87 (commit)
       via  dd2d08d8cb0ed409fd8a423c988db7a0979e08eb (commit)
       via  0589469f4a57ee95f16ea7e316365ae0641c4c3c (commit)
       via  c84efb2586d129061f0fc0e1bcf291e0582b0f9d (commit)
       via  bdeb3c67d12483d7ed6945e7d190195764e24fcc (commit)
       via  65a83c79b14885d4a4106e1a707ef431b72053e4 (commit)
       via  2fd85850524510bb6d4452f2c9b863400b3949dc (commit)
       via  233aebcb70f15914e141fef4982ad7d0028fbd9f (commit)
       via  ec4581da1a15031128504cf97c8c578669422126 (commit)
       via  bedf7f9f268f67f5522e620a385452cf6083b4ff (commit)
       via  70db0c50bf842765d90e2a6b3abf4f5360f31281 (commit)
       via  609e5bf4005731160716d1d63d3eebf402a14f1d (commit)
       via  71cf0a22ba08d32908a610b749e9aefe1c4dda1a (commit)
       via  40d54f7e2b04dc53be81a24e50f8e66be968e6cb (commit)
       via  30e529ac56321e0654bf8bbe48412b851bd06bb9 (commit)
       via  9002ffc08e7c7fba799e8b417a1d80b77a20973e (commit)
       via  de92f31ea5fc5991e10bc61c175373b67f78deab (commit)
       via  20ca327e9bcccb2e83ed437e7d1dfc9fa6d8fb3c (commit)
       via  ec4fcc79563b93bfc1e300c46287d8f102261d68 (commit)
       via  8201bad569bfda39027198bd920e8da6b8af9539 (commit)
       via  435309b12de7683b06fa29d0b2b9a0bb57fbd9ef (commit)
       via  59861491526ae8b4f61f94e8ad4a6926178d9aea (commit)
       via  9bdc1899a220d369cb7ca1b86fd9a2ddde44eeb9 (commit)
       via  c831a1e66e88bceb6a317dd8de0796b315681f4f (commit)
       via  1780e7b66ee62456880ac5ed0d93c9830f233163 (commit)
       via  238b343266ec6c5e7c610781021d235b7fcfb22d (commit)
       via  e0c2fad3471896467ad0ae3576f93b39bcb76a7d (commit)
       via  501a5e337373d10c40c05d0838f5711fbb961ee2 (commit)
       via  dbf7313f6bb3fd3ace97b50509a4208864c4c4ea (commit)
       via  d506b61e5f2e50d228ac8541d6927594861a25e5 (commit)
       via  e66472386a716a31089a2306b9e5d51f7618feeb (commit)
       via  90641997b368ea093686c97d839b2794b927180c (commit)
       via  38db6aff7d759e5f271cf0d369459b361c8e60a7 (commit)
       via  816eacf47e4168d336c2f64b1b621f13f8ca6cb4 (commit)
       via  1d4751ed52516da9036c61a16946ec74ea1f1ba5 (commit)
       via  09d70952ca89ef967f8652046ed6fe869625b42e (commit)
       via  6944e7758d326b69482bcfd7d3f501df5113b394 (commit)
       via  970ee33b88d3268780fc4a7d8950ba2a4a8202fd (commit)
       via  d6fb00a3198fe327cdbf4ebfe14f47eb23086325 (commit)
       via  f2a03c4538343e2a6941e5037925a63a7e71f68c (commit)
       via  8bc5490b0e4718fcf5759a0b24c02a83c4518e17 (commit)
       via  936137b0b10508b395cbd4a71ad07c1ec0ffb655 (commit)
       via  cacc927a7096327c8e19479058258cecec0eb27e (commit)
       via  3da9d6198bdedd9317b290f70c9bbe613894037b (commit)
       via  549e44164f1e4203bf1acb8d6c137f1de5a48f5a (commit)
       via  b1d0e42774ef51b404be44ee53cb930a630a63bb (commit)
       via  fca9ec102de26345c4dafb193f85a0560322f295 (commit)
       via  ee0836a4c450db59764158e9f9c58acad7a1c4a4 (commit)
       via  7c0a4abbb4cce4460c8b0cfd9f1447e0cc37644d (commit)
       via  8f6903dc068a22c2966e9c28c90c14b6d83543cc (commit)
       via  9b8747917cc300904783b403a64c2a11ebdb5ec0 (commit)
       via  1545ba5efdff605b12188442e34b5845c925256f (commit)
       via  e4036de3cb6229d38270225eff7a00f5c07a4573 (commit)
       via  3b154a69231eb838886c82deba2b1bb9439ac3ad (commit)
       via  11bbe308dff30dc66a6e72a1d357637757a53a77 (commit)
       via  aad4df7a662b0668592aaae63e66a49cd19ed614 (commit)
       via  95a87b15ab32e307563eb8c4b5928a0919aeae56 (commit)
       via  7b148d962b1d211eab660ac79e6dcb37e1aa8831 (commit)
       via  c0d55602e3304a95d7956b401ab1ce6c55d11dc1 (commit)
       via  9f8aabb7951987f3e247ad4263e259d82211e5b3 (commit)
       via  5819758217d6e95348c722bafe0f79c1e28ee43a (commit)
       via  6edf83daa67ac95bb45c2975e0511a830e64ac59 (commit)
       via  31dd54a026b5427979a932f0735bacf8723ec7cb (commit)
       via  2c9904e1543d72dac392f1592580d9f8351f5e51 (commit)
       via  8da8a38bcf022e5ad4d99a64be6cda1e557c5692 (commit)
       via  5911431e8fcb1728b1a4765a6f3c1352c4879e8c (commit)
       via  5f03da43d3b7cc5225b64f085320d501c8e1cfde (commit)
       via  3ee8c9cdab87054662af5a67993c9a3ced593256 (commit)
       via  c0f25aade83aaf90bef0abd8bd7eff66cdaa7684 (commit)
       via  3666f50858de194cbca5f0e3e8d9fba747bb3250 (commit)
       via  2abaf1a0335fc470556c2506a9dc7ff002cca8d0 (commit)
       via  d5f52afdc5419756f514f79ecd53f59090555c43 (commit)
       via  203b4a05936d027793b03316bffa333e539876ad (commit)
       via  20ff1f4962b99620990c326c50cad7fcef0d16dc (commit)
       via  ec24cb1c279c83107c9058cbdbd9115d84d3ea71 (commit)
       via  1a2787f32f2376d76367516f5cfb3a0059b76474 (commit)
       via  7237139f1b8ec9248fa26e9ae2e6987bfb54f288 (commit)
       via  2f554072bc7b82a18d898073802f545305a90110 (commit)
       via  191d96a58702a5a3059993a1287136833ec94a4e (commit)
       via  b6c480a59485c8a53d9beea2dba85a667aef7556 (commit)
       via  daca34509f65af73d06ce071340668258539f6c6 (commit)
       via  650e0b501ab48af61704efe9cb503490716c8eec (commit)
       via  45c04beca6f7d07f1a2c779baf3a7a8ba26763a2 (commit)
       via  0b06fc6c1993467408bdb7f109affe029a834e3d (commit)
       via  bc44ee8bf3c35cacaad05751d75e8789aa5c9108 (commit)
       via  b561ee0919676f6e9c34329151c2b46d60f1824b (commit)
       via  372f3a19f5bad9ed51e50d083063fccbe1ff6900 (commit)
       via  495d4e2f6970ef05f5f764f1fcd6661b5403cefb (commit)
       via  f634e891b21b4533c16871bab7bcd65f6422986d (commit)
       via  a0662ae5601ee75f3e99b2b53d4f3376b5e6a037 (commit)
       via  10e87104e0b7977804c8c55275a403602dcbb2e5 (commit)
       via  6b59d1bc4fe7976ea6c1cf7820649824b4135e07 (commit)
       via  5c50504446f431198d329d31b62e076d65e3eacb (commit)
       via  403af3331c71af8cda87fd6ff5fcce407d4f935a (commit)
       via  594916b524ff40cd66ce7891ce743c70a9d0aa68 (commit)
       via  59903362374b1fca9612fe5a872579dd4a4d9873 (commit)
       via  3e5f22520515b1f1675c3217ff686106201fdfdf (commit)
       via  947a2d7aec3208efe49e266271860982d355c0d8 (commit)
       via  292db149be50cc3a38965be1e14ca1cf26cba2bb (commit)
       via  7cf0d504794bbf42d15015af1d42ba1cf7fcd82f (commit)
       via  ad7b96467ceeeade2ca9bc16ba8f02ecadc1ed82 (commit)
       via  a5330334a54791088394fe6b726666b9ae6671b9 (commit)
       via  9c4e97e7f8cfe7f8001c1491f9f7c2e632c736b2 (commit)
       via  8ab540a511481e83c8db435c439e291d0f3e52ea (commit)
       via  1dc0f0871d7045e8fb57bb0767fc31a2f7e3fe80 (commit)
       via  1bbe13152cea9e759bb0423750f244be58eff578 (commit)
       via  8a21830a250996c93b1b053c56a4dc9bb7bb4750 (commit)
       via  ef364ccb84c6b8eea4952c0a59c6bb560ff43ca4 (commit)
       via  45b4e930c9fa6e5eb6f8ef7c0c9cdd6818e82f18 (commit)
       via  9e98b4768d26c1ea8accf3f53f9c019ff66347b6 (commit)
       via  43292d60d91cd8656856c88dfefce186412b64b1 (commit)
       via  ac111116c588c5b611e1363d4fe11c7ee8f4e495 (commit)
       via  e3afa9c5286228971db62ff868785602e90a5513 (commit)
       via  bbefd6bc4137c3345845d09e86be68b81770718f (commit)
       via  84e441c75606aff87131e7d84010679a11f242ce (commit)
       via  159af7d5de17a74269cd4af69ac3b569495c4a4e (commit)
       via  54663a3c7bda67a6813d2478ff44c8a07d9c48b8 (commit)
       via  00eee13bde95c741890ad3ca52967c11076c5087 (commit)
       via  22a33dfddc0b29a506b57c3f76bace8d15ec6df6 (commit)
       via  7793f98f619c3422ca3fa26795a5861b0fbab4fa (commit)
       via  c806c0c3184caf046e124f36028122a965078408 (commit)
       via  f1035ba18d68d4a1fa89719a9dc8e5eb6ae24d84 (commit)
       via  0740a5d8df7f65518b2b87662b4375630e1838ef (commit)
       via  045d32e4a9f14e7deec58e5e7d55867a34325764 (commit)
       via  eba06eeedde4091fed26761f696a2aab5535074a (commit)
       via  93d27cba1a47182b5f94e45b5d569386f2a1ad25 (commit)
       via  8a966b8d7359a852478e380e89e94cfe2f12d5bd (commit)
       via  2c2459fd09fd1fb9b2c741eca0b93a8550f3f9b3 (commit)
       via  145a4ed5a89c92d93303604ceff83ec3d17ff87d (commit)
       via  41ccdad87bfc96af06dd83ecd143e4277795eb5f (commit)
       via  8682cdcb920f12df639aaa0e7720afb9b03bf849 (commit)
       via  0688fdb85ded658b2ecf5aceb6e4691cb921ddfb (commit)
       via  46cfa0779257292e55c048a3854f0df4fe1cbd08 (commit)
       via  cfdfb58ce442816cce069453d4051cc008691b74 (commit)
       via  5b1850bbc20932642495b8ae8e5f14ae5cd9c1dc (commit)
       via  3b2c2c14fb9f99e1f189c70441d23063c7a26b6e (commit)
       via  a08894f45157cd19450927d2e8b80ce49a86acaf (commit)
       via  7e6602a873c952f8c01e141b4a40f4f2639758cd (commit)
       via  3eab2458f1e6b122161361a50bff80e4f5d24825 (commit)
       via  59c65b476bbc17417361b99234a7001a894b71e0 (commit)
       via  af71689ee49cd8668dd830e7a90f06c9b7fc7de7 (commit)
       via  61071513480693ec4d32a3aa73cd60bf63bbb808 (commit)
       via  08a5a3d31220c660530bd69cccc8c27e1a789312 (commit)
       via  f15be5f7bf0fa3e5381afe4bb820a06af434a5af (commit)
       via  01ee67bc74edb15536ae92b558eddd496da13fce (commit)
       via  d135a0e1f84ed2700d283ce20c7d96e5be614619 (commit)
       via  7f6ecb5e40c6acede82fb08a7d4c477030a48b99 (commit)
       via  f6c776bc4a3cea0eae628d1580ab10843be06dfe (commit)
       via  bc4926dac23a819a909992abf58df64d62647c68 (commit)
       via  fa3e24bb027671b5c53a5fa1b004920cf8252f0f (commit)
       via  a11abf30b29eda75f8680c8287b57db1abe612e8 (commit)
       via  b7bce1d66baed1e01a951a6b13151783b2d74444 (commit)
       via  996d6ff3b0e79c723332d03296cbbe88b552d373 (commit)
       via  924c7a481efc5aa34c8bc73efb395c2c80282cc0 (commit)
       via  8cb9e9efb9cd47301e6784840142931631e1664f (commit)
       via  f117a66b8be41e88a18a27bd7b2f1275b624767a (commit)
       via  77c012c47a2bd2ff161b8b62df065b3daa87ff73 (commit)
       via  d51421238cd7aa392c6baa967ee977a6d3dc8b7a (commit)
       via  a9ebc379b9d2f2501c8a4d497bb715f6c100cea8 (commit)
       via  1ab0c5acf4fa6b8a1a99068685bb2a27d6dd3dc8 (commit)
       via  15249e567c9cfae135c1a2bc1a110b4a635b11ab (commit)
       via  da089cd9b9dc3aa3cb13f1c02e362b870e524b20 (commit)
       via  5450b258dbeaaf876fd35c52a1191e4f083a620e (commit)
       via  b1c8e1ce93d760fa32e7b977910faa9ed5fc4c30 (commit)
       via  58f0aac3a8f36f335c0546f44122ff43f42f5761 (commit)
       via  84fbdaeddb176af0a5cd73caf76698208b4e94a7 (commit)
       via  09f21f49fa1e214af117262c305efb3968becd8f (commit)
       via  36d2b848c6badf00bae61bd958b891b7e2acc8cd (commit)
       via  4f52e898a7b69228cf4a8ab70d78844ad9dc45e1 (commit)
       via  c2c64eae1f3e6140bbe30e963797b9a4efa5a9f8 (commit)
       via  230de14b1b3e31b2f436cfd370e149a98926522e (commit)
       via  54a707d39ae1943731ed2043f8d44424c434f0e3 (commit)
       via  51bd304db036e8d2e6008a98b2dae01eea126e41 (commit)
       via  da1b2d7dc0a367ed168b366df1ab5c5af82260d4 (commit)
       via  d5c0c97ef8bf7d4b9ae2709b8a92eb1532f4c9b9 (commit)
       via  a7b31b7b074db8359b5c93c3b0d37c807a78ad7e (commit)
       via  7503458897ccad85875ef3bc929ec803750179e8 (commit)
       via  37da25f2eca6b2af80cc5bc2a7e65c5644acf2fb (commit)
       via  0f228f2d53a626d647aa728585342c889ff8f846 (commit)
       via  8b31b93c4c62b76b3ec70d0e9c1a18c0f51bd459 (commit)
       via  f9445ffa2d128ab0730abb3592e75475023ba755 (commit)
       via  494bbca16cea584ba6ad5d2c5f9a273d809f902d (commit)
       via  67f4798a8a2457b8d284b75dcf1a6bcfeb8a0fa5 (commit)
       via  32e88854b4ab9b2484d1f5ad0597869c8d1f5da4 (commit)
       via  9d23c50d12b04b51c10889fe9b476823550bd733 (commit)
       via  83d4f414a296dfc757a7a4f6e8c8fcf7bd24ca2e (commit)
       via  424b8d83f6059738f2edb01564ec9ce1db223b08 (commit)
       via  3590bad22f52396c91663cc75b8548f6df134c60 (commit)
       via  4a9b794019090fd826688bca0aee4dc565756cc1 (commit)
       via  180a437742219f6266934738962663c65ab748f6 (commit)
       via  6210dae2a6749d8c1e328de433c91058fd54b756 (commit)
       via  6b8c62acd872220aeb061f7c6e0fe50348ba3d60 (commit)
       via  b0db66ca291884dd9220cda078d9ad3e91d0eb67 (commit)
       via  71739287e490437d2f1278e02ba9232afa925a48 (commit)
       via  7ba306a2e14dd31e1bf8350c2d27b9741af7e101 (commit)
       via  9b6c74f8e85b29db87fb4724698d9fb9cc53dcc4 (commit)
       via  356a8b2e2cdbd2470eb134bfe08f80fb54028abb (commit)
       via  d0af0ecaadd8950008ef65ee2b40b56fa21a1e82 (commit)
       via  142825f0caeba214a4d1884be4067c62d1cc3551 (commit)
       via  4b59798e65bfc49e153a4fd96fcc684c1fbe91b6 (commit)
       via  d2c2edbfa7d78ebeae4c79eef532814ac7072556 (commit)
       via  552657fff4e4538628e7d2b98c4fbe93a07d307c (commit)
       via  650a6c1b4f315e6e89de673502d5a82359f424d8 (commit)

This update added new revisions after undoing existing revisions.  That is
to say, the old revision is not a strict subset of the new revision.  This
situation occurs when you --force push a change and generate a repository
containing something like this:

 * -- * -- B -- O -- O -- O (538575ebb0da6dd4da36116dd585fda06788c041)
            \
             N -- N -- N (e47b08e8a356c1bf498aa5a615fcce86958f1fe0)

When this happens we assume that you've already had alert emails for all
of the O revisions, and so we here report only the revisions in the N
branch from the common base, B.

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 e47b08e8a356c1bf498aa5a615fcce86958f1fe0
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date:   Fri Dec 14 13:53:31 2012 +0100

    [2427] Basic $ORIGIN handling

commit 8fd5cc452a30a35df0a24b7bd1c30e76ea6bb54d
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date:   Fri Dec 14 13:45:48 2012 +0100

    [2427] Test for $ORIGIN handling

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

Summary of changes:
 ChangeLog                                          |   76 ++
 configure.ac                                       |    6 +-
 doc/Doxyfile                                       |  623 +++++++---
 doc/devel/02-dhcp.dox                              |    4 +-
 doc/devel/mainpage.dox                             |   14 +-
 doc/guide/bind10-guide.xml                         |  117 +-
 src/bin/auth/main.cc                               |    6 +-
 src/bin/auth/tests/auth_srv_unittest.cc            |    2 +-
 src/bin/bind10/bind10_src.py.in                    |   81 +-
 src/bin/bind10/tests/bind10_test.py.in             |  765 ++++++++++++-
 src/bin/cfgmgr/b10-cfgmgr.py.in                    |    2 +-
 src/bin/cmdctl/.gitignore                          |    8 +-
 src/bin/cmdctl/cmdctl.py.in                        |    2 +-
 src/bin/ddns/ddns.py.in                            |    2 +-
 src/bin/dhcp4/Makefile.am                          |    2 +
 src/bin/dhcp4/config_parser.cc                     |  772 +++++++++++++
 src/bin/{dhcp6 => dhcp4}/config_parser.h           |   95 +-
 src/bin/dhcp4/ctrl_dhcp4_srv.cc                    |   27 +-
 src/bin/dhcp4/ctrl_dhcp4_srv.h                     |    4 +-
 src/bin/dhcp4/dhcp4.dox                            |   68 ++
 src/bin/dhcp4/dhcp4.spec                           |   80 +-
 src/bin/dhcp4/dhcp4_messages.mes                   |   20 +
 src/bin/dhcp4/dhcp4_srv.h                          |    2 +-
 src/bin/dhcp4/main.cc                              |    8 +-
 src/bin/dhcp4/tests/Makefile.am                    |    3 +
 src/bin/dhcp4/tests/config_parser_unittest.cc      |  294 +++++
 src/bin/dhcp6/config_parser.cc                     |   41 +-
 src/bin/dhcp6/config_parser.h                      |   25 +-
 src/bin/dhcp6/ctrl_dhcp6_srv.cc                    |    1 -
 src/bin/dhcp6/ctrl_dhcp6_srv.h                     |    4 +-
 src/bin/dhcp6/dhcp6.dox                            |    8 +-
 src/bin/dhcp6/dhcp6_messages.mes                   |   27 +
 src/bin/dhcp6/dhcp6_srv.cc                         |  332 ++++--
 src/bin/dhcp6/dhcp6_srv.h                          |   80 +-
 src/bin/dhcp6/main.cc                              |   10 +-
 src/bin/dhcp6/tests/dhcp6_srv_unittest.cc          |  354 +++++-
 src/bin/resolver/main.cc                           |   12 +-
 src/bin/stats/stats.py.in                          |    4 +-
 src/bin/stats/stats_httpd.py.in                    |    4 +-
 src/bin/xfrin/b10-xfrin.xml                        |   10 +-
 src/bin/xfrin/tests/xfrin_test.py                  |   50 +-
 src/bin/xfrin/xfrin.py.in                          |   47 +-
 src/bin/xfrin/xfrin_messages.mes                   |    9 +-
 src/bin/xfrout/xfrout.py.in                        |    2 +-
 src/bin/zonemgr/zonemgr.py.in                      |    5 +-
 src/bin/zonemgr/zonemgr_messages.mes               |    2 +-
 src/lib/asiolink/io_address.cc                     |   15 +
 src/lib/asiolink/io_address.h                      |   23 +-
 src/lib/asiolink/tests/io_address_unittest.cc      |   41 +
 src/lib/datasrc/Makefile.am                        |    3 +-
 src/lib/datasrc/{exceptions.h => client.cc}        |   55 +-
 src/lib/datasrc/client.h                           |   44 +-
 src/lib/datasrc/database.cc                        |   55 +-
 src/lib/datasrc/database.h                         |   28 +
 src/lib/datasrc/datasrc_messages.mes               |   10 +
 src/lib/datasrc/memory/memory_client.cc            |   55 +-
 src/lib/datasrc/memory/zone_data.cc                |   22 +-
 src/lib/datasrc/memory/zone_data.h                 |   14 +-
 src/lib/datasrc/memory/zone_data_updater.cc        |    2 +-
 src/lib/datasrc/memory/zone_finder.cc              |   13 +-
 src/lib/datasrc/sqlite3_accessor.cc                |   35 +-
 src/lib/datasrc/sqlite3_accessor.h                 |  201 ++--
 src/lib/datasrc/tests/Makefile.am                  |    1 +
 src/lib/datasrc/tests/client_unittest.cc           |    4 +
 src/lib/datasrc/tests/database_unittest.cc         |   61 +-
 .../datasrc/tests/memory/memory_client_unittest.cc |   19 +
 .../datasrc/tests/memory/testdata/2503-test.zone   |   13 +
 .../datasrc/tests/memory/testdata/2504-test.zone   |   10 +
 src/lib/datasrc/tests/memory/testdata/Makefile.am  |    3 +
 src/lib/datasrc/tests/memory/zone_data_unittest.cc |   34 +-
 .../datasrc/tests/memory/zone_finder_unittest.cc   |   50 +
 src/lib/datasrc/tests/sqlite3_accessor_unittest.cc |   77 ++
 src/lib/datasrc/tests/zone_loader_unittest.cc      |  395 +++++++
 src/lib/datasrc/zone_loader.cc                     |  132 +++
 src/lib/datasrc/zone_loader.h                      |  158 +++
 src/lib/dhcp/Makefile.am                           |    4 +-
 src/lib/dhcp/duid.cc                               |   24 +-
 src/lib/dhcp/duid.h                                |   52 +-
 src/lib/dhcp/libdhcp++.cc                          |   68 +-
 src/lib/dhcp/libdhcp++.h                           |   13 +-
 src/lib/dhcp/option.cc                             |    4 +
 src/lib/dhcp/option.h                              |   19 +-
 src/lib/dhcp/option4_addrlst.h                     |    4 +-
 src/lib/dhcp/option6_addrlst.h                     |    4 +-
 src/lib/dhcp/option6_ia.h                          |    9 +-
 src/lib/dhcp/option_custom.cc                      |  334 +++++-
 src/lib/dhcp/option_custom.h                       |  191 ++-
 src/lib/dhcp/option_data_types.cc                  |   41 +-
 src/lib/dhcp/option_data_types.h                   |   44 +-
 src/lib/dhcp/option_definition.cc                  |   95 +-
 src/lib/dhcp/option_definition.h                   |    2 +-
 src/lib/dhcp/pkt4.cc                               |   57 +-
 src/lib/dhcp/pkt4.h                                |   22 +-
 src/lib/dhcp/pkt6.cc                               |   72 +-
 src/lib/dhcp/pkt6.h                                |   57 +-
 src/lib/dhcp/std_option_defs.h                     |  184 +++
 src/lib/dhcp/tests/Makefile.am                     |    1 +
 src/lib/dhcp/tests/libdhcp++_unittest.cc           |  172 ++-
 src/lib/dhcp/tests/option6_ia_unittest.cc          |   11 +-
 src/lib/dhcp/tests/option_custom_unittest.cc       |  553 ++++++++-
 src/lib/dhcp/tests/option_data_types_unittest.cc   |  491 ++++++++
 src/lib/dhcp/tests/option_definition_unittest.cc   |    3 +-
 src/lib/dhcp/tests/option_unittest.cc              |   23 +
 src/lib/dhcp/tests/pkt4_unittest.cc                |   11 +-
 src/lib/dhcp/tests/pkt6_unittest.cc                |   73 ++
 src/lib/dhcpsrv/Makefile.am                        |    2 +-
 src/lib/dhcpsrv/addr_utilities.h                   |    8 +-
 src/lib/dhcpsrv/alloc_engine.cc                    |    2 +-
 src/lib/dhcpsrv/cfgmgr.cc                          |    8 +
 src/lib/dhcpsrv/cfgmgr.h                           |   22 +-
 src/lib/dhcpsrv/database_backends.dox              |   15 +-
 src/lib/dhcpsrv/dhcpdb_create.mysql                |   11 +-
 src/lib/dhcpsrv/lease_mgr.cc                       |   53 +-
 src/lib/dhcpsrv/lease_mgr.h                        |  265 +++--
 .../{dhcp/libdhcsrv.dox => dhcpsrv/libdhcpsrv.dox} |    0
 src/lib/dhcpsrv/memfile_lease_mgr.cc               |   28 +-
 src/lib/dhcpsrv/memfile_lease_mgr.h                |   27 +-
 src/lib/dhcpsrv/mysql_lease_mgr.cc                 | 1211 ++++++++++++++------
 src/lib/dhcpsrv/mysql_lease_mgr.h                  |  273 ++++-
 src/lib/dhcpsrv/subnet.h                           |    2 +-
 .../dhcpsrv/tests/lease_mgr_factory_unittest.cc    |    2 +-
 src/lib/dhcpsrv/tests/lease_mgr_unittest.cc        |  406 ++++++-
 .../dhcpsrv/tests/memfile_lease_mgr_unittest.cc    |    6 +-
 src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc  | 1067 +++++++++++++----
 src/lib/dhcpsrv/tests/schema_copy.h                |   10 +-
 src/lib/dhcpsrv/triplet.h                          |    2 +-
 src/lib/dns/Makefile.am                            |   12 +-
 src/lib/dns/character_string.cc                    |    7 +-
 src/lib/dns/character_string.h                     |    5 +-
 src/lib/dns/gen-rdatacode.py.in                    |   21 +-
 src/lib/dns/master_lexer.cc                        |   11 +-
 src/lib/dns/master_lexer.h                         |    8 +-
 src/lib/dns/master_loader.cc                       |   33 +-
 src/lib/dns/python/tests/rdata_python_test.py      |    2 +-
 src/lib/dns/python/tests/tsigkey_python_test.py    |    6 +
 src/lib/dns/python/tsigkey_python.cc               |   17 +-
 src/lib/dns/rdata.cc                               |   31 +-
 src/lib/dns/rdata.h                                |    2 +-
 src/lib/dns/rdata/generic/detail/char_string.cc    |   98 ++
 src/lib/dns/rdata/generic/detail/char_string.h     |   63 +
 src/lib/dns/rdata/generic/detail/txt_like.h        |  123 +-
 src/lib/dns/rdata/generic/hinfo_13.cc              |   24 +-
 src/lib/dns/rdata/generic/hinfo_13.h               |    9 +-
 src/lib/dns/rdata/generic/spf_99.cc                |   26 +-
 src/lib/dns/rdata/generic/spf_99.h                 |    4 +-
 src/lib/dns/rdata/generic/txt_16.cc                |   18 +-
 src/lib/dns/rdata/generic/txt_16.h                 |    4 +-
 src/lib/dns/rrparamregistry-placeholder.cc         |   78 +-
 src/lib/dns/tests/Makefile.am                      |    1 +
 src/lib/dns/tests/character_string_unittest.cc     |    7 +-
 src/lib/dns/tests/master_lexer_unittest.cc         |   31 +-
 src/lib/dns/tests/master_loader_unittest.cc        |   46 +
 src/lib/dns/tests/rdata_char_string_unittest.cc    |  147 +++
 src/lib/dns/tests/rdata_hinfo_unittest.cc          |   19 +-
 src/lib/dns/tests/rdata_txt_like_unittest.cc       |  218 +++-
 src/lib/dns/tests/rdata_unittest.cc                |   28 +-
 src/lib/dns/tests/testdata/rdata_txt_fromWire1     |    6 +-
 src/lib/dns/tests/tsigkey_unittest.cc              |    9 +
 src/lib/dns/tsigkey.cc                             |   12 +-
 src/lib/dns/tsigkey.h                              |   22 +
 src/lib/log/Makefile.am                            |    1 +
 src/lib/log/README                                 |   14 +-
 src/lib/log/buffer_appender_impl.cc                |   96 ++
 src/lib/log/buffer_appender_impl.h                 |  118 ++
 src/lib/log/logger_manager.cc                      |    6 +-
 src/lib/log/logger_manager.h                       |   24 +-
 src/lib/log/logger_manager_impl.cc                 |   80 +-
 src/lib/log/logger_manager_impl.h                  |   50 +-
 src/lib/log/logger_support.cc                      |    4 +-
 src/lib/log/logger_support.h                       |    6 +-
 src/lib/log/tests/Makefile.am                      |   13 +
 src/lib/log/tests/buffer_appender_unittest.cc      |  146 +++
 src/lib/log/tests/buffer_logger_test.cc            |   71 ++
 ...er_lock_test.sh.in => buffer_logger_test.sh.in} |   40 +-
 src/lib/python/isc/config/cfgmgr.py                |    9 +-
 src/lib/python/isc/datasrc/client_inc.cc           |   29 +
 src/lib/python/isc/datasrc/client_python.cc        |   33 +
 src/lib/python/isc/datasrc/tests/datasrc_test.py   |   49 +-
 src/lib/python/isc/log/log.cc                      |   29 +-
 src/lib/python/isc/log/tests/log_test.py           |   22 +
 src/lib/util/python/gen_wiredata.py.in             |    2 +-
 tests/lettuce/configurations/xfrin/.gitignore      |    1 +
 tests/lettuce/features/xfrin_bind10.feature        |    2 +-
 tests/tools/perfdhcp/Makefile.am                   |    2 +-
 tests/tools/perfdhcp/templates/Makefile.am         |   10 -
 tests/tools/perfdhcp/tests/Makefile.am             |    6 +-
 .../tools/perfdhcp/tests/test_control_unittest.cc  |   34 +-
 .../{templates => tests/testdata}/.gitignore       |    0
 tests/tools/perfdhcp/tests/testdata/Makefile.am    |    4 +
 .../testdata}/discover-example.hex                 |    0
 .../testdata}/request4-example.hex                 |    0
 .../testdata}/request6-example.hex                 |    0
 .../testdata}/solicit-example.hex                  |    0
 193 files changed, 11660 insertions(+), 2130 deletions(-)
 create mode 100644 src/bin/dhcp4/config_parser.cc
 copy src/bin/{dhcp6 => dhcp4}/config_parser.h (62%)
 create mode 100644 src/bin/dhcp4/dhcp4.dox
 create mode 100644 src/bin/dhcp4/tests/config_parser_unittest.cc
 copy src/lib/datasrc/{exceptions.h => client.cc} (50%)
 create mode 100644 src/lib/datasrc/tests/memory/testdata/2503-test.zone
 create mode 100644 src/lib/datasrc/tests/memory/testdata/2504-test.zone
 create mode 100644 src/lib/datasrc/tests/zone_loader_unittest.cc
 create mode 100644 src/lib/datasrc/zone_loader.cc
 create mode 100644 src/lib/datasrc/zone_loader.h
 create mode 100644 src/lib/dhcp/std_option_defs.h
 create mode 100644 src/lib/dhcp/tests/option_data_types_unittest.cc
 rename src/lib/{dhcp/libdhcsrv.dox => dhcpsrv/libdhcpsrv.dox} (100%)
 create mode 100644 src/lib/dns/rdata/generic/detail/char_string.cc
 create mode 100644 src/lib/dns/rdata/generic/detail/char_string.h
 create mode 100644 src/lib/dns/tests/rdata_char_string_unittest.cc
 create mode 100644 src/lib/log/buffer_appender_impl.cc
 create mode 100644 src/lib/log/buffer_appender_impl.h
 create mode 100644 src/lib/log/tests/buffer_appender_unittest.cc
 create mode 100644 src/lib/log/tests/buffer_logger_test.cc
 copy src/lib/log/tests/{logger_lock_test.sh.in => buffer_logger_test.sh.in} (51%)
 delete mode 100644 tests/tools/perfdhcp/templates/Makefile.am
 rename tests/tools/perfdhcp/{templates => tests/testdata}/.gitignore (100%)
 create mode 100644 tests/tools/perfdhcp/tests/testdata/Makefile.am
 rename tests/tools/perfdhcp/{templates => tests/testdata}/discover-example.hex (100%)
 rename tests/tools/perfdhcp/{templates => tests/testdata}/request4-example.hex (100%)
 rename tests/tools/perfdhcp/{templates => tests/testdata}/request6-example.hex (100%)
 rename tests/tools/perfdhcp/{templates => tests/testdata}/solicit-example.hex (100%)

-----------------------------------------------------------------------
diff --git a/ChangeLog b/ChangeLog
index 4b3a4c4..eb15baf 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,79 @@
+526.	[bug]		syephen
+	Miscellaneous fixes to DHCP code including rationalisation of
+	some methods in LeaseMgr and resolving some Doxygen/cppcheck
+	issues.
+	(Trac #2546, git 0140368ed066c722e5d11d7f9cf1c01462cf7e13)
+
+525.	[func]		tomek
+	b10-dhcp4: DHCPv4 server is now able to parse configuration. It
+	is possible to specify IPv4 subnets with dynamic pools within
+	them. Although configuration is accepted, it is not used yet. This
+	will be implemented shortly.
+	(Trac #2270, git de29c07129d41c96ee0d5eebdd30a1ea7fb9ac8a)
+
+524.	[func]		tomek
+	b10-dhcp6 is now able to handle RENEW messages. Leases are
+	renewed and REPLY responses are sent back to clients.
+	(Trac #2325, git 7f6c9d057cc0a7a10f41ce7da9c8565b9ee85246)
+
+523.	[bug]		muks
+	Fixed a problem in inmem NSEC3 lookup (for, instance when using a
+	zone with no non-apex names) which caused exceptions when the zone
+	origin was not added as an explicit NSEC3 record.
+	(Trac #2503, git 6fe86386be0e7598633fe35999112c1a6e3b0370)
+
+522.	[func]*		jelte
+	Configuration of TSIG keys for b10-xfrin has changed; instead of
+	specifying the full TSIG key (<name>:<base64>:<algo>) it now expects
+	just the name, and uses the global TSIG Key Ring like all the other
+	components (configuration list /tsig_keys/keys).
+	Note: this is not automatically updated, so if you use TSIG in
+	xfrin, you need to update your configuration.
+	(Trac #1351, git e65b7b36f60f14b7abe083da411e6934cdfbae7a)
+
+521.	[func]		marcin
+	Implemented definitions for DHCPv6 standard options identified
+	by codes up to 48. These definitions are now used by the DHCPv6
+	server to create instances of options being sent to a client.
+	(Trac #2491, git 0a4faa07777189ed9c25211987a1a9b574015a95)
+
+520.	[func]		jelte
+	The system no longer prints initial log messages to stdout
+	regardless of what logging configuration is present, but it
+	temporarily stores any log messages until the configuration is
+	processed. If there is no specific configuration, or if the
+	configuration cannot be accessed, it will still fall back to stdout.
+	Note that there are still a few instances where output is printed,
+	these shall be addressed separately.
+	Note also that, currently, in case it falls back to stdout (such as
+	when it cannot connect to b10-cfgmgr), all log messages are always
+	printed (including debug messages), regardless of whether -v was
+	used. This shall also be addressed in a future change.
+	(Trac #2445, git 74a0abe5a6d10b28e4a3e360e87b129c232dea68)
+
+519.	[bug]		muks
+	Fixed a problem in inmem NSEC lookup which caused returning an
+	incorrect NSEC record or (in rare cases) assert failures
+	when a non-existent domain was queried, which was a sub-domain of
+	a domain that existed.
+	(Trac #2504, git 835553eb309d100b062051f7ef18422d2e8e3ae4)
+
+518.	[func]		stephen
+	Extend DHCP MySQL backend to handle IPv4 addresses.
+	(Trac #2404, git ce7db48d3ff5d5aad12b1da5e67ae60073cb2607)
+
+517.	[func]		stephen
+	Added IOAddress::toBytes() to get byte representation of address.
+	Also added convenience methods for V4/V6 address determination.
+	(Trac #2396, git c23f87e8ac3ea781b38d688f8f7b58539f85e35a)
+
+516.	[bug]		marcin
+	Fixed 'make distcheck' failure when running perfdhcp unit tests.
+	The unit tests used to read files from the folder specified
+	with the path relative to current folder, thus when the test was
+	run from a different folder the files could not be found.
+	(Trac #2479, git 4e8325e1b309f1d388a3055ec1e1df98c377f383)
+
 515.	[bug]		jinmei
 	The in-memory data source now accepts an RRSIG provided without
 	a covered RRset in loading.  A subsequent query for its owner name
diff --git a/configure.ac b/configure.ac
index 636f8aa..3f7a269 100644
--- a/configure.ac
+++ b/configure.ac
@@ -903,11 +903,12 @@ AC_SUBST(MULTITHREADING_FLAG)
 #
 GTEST_LDFLAGS=
 GTEST_LDADD=
-# TODO: set DISTCHECK_GTEST_CONFIGURE_FLAG for --with-gtest too
 DISTCHECK_GTEST_CONFIGURE_FLAG=
 
 if test "x$enable_gtest" = "xyes" ; then
 
+    DISTCHECK_GTEST_CONFIGURE_FLAG="--with-gtest=$gtest_path"
+
     if test -n "$with_gtest_source" ; then
 
           if test "x$GTEST_SOURCE" = "xyes" ; then
@@ -1308,7 +1309,7 @@ AC_CONFIG_FILES([Makefile
                  tests/tools/badpacket/tests/Makefile
                  tests/tools/perfdhcp/Makefile
                  tests/tools/perfdhcp/tests/Makefile
-                 tests/tools/perfdhcp/templates/Makefile
+                 tests/tools/perfdhcp/tests/testdata/Makefile
                  dns++.pc
                ])
 AC_OUTPUT([doc/version.ent
@@ -1375,6 +1376,7 @@ AC_OUTPUT([doc/version.ent
            src/lib/log/tests/console_test.sh
            src/lib/log/tests/destination_test.sh
            src/lib/log/tests/init_logger_test.sh
+           src/lib/log/tests/buffer_logger_test.sh
            src/lib/log/tests/local_file_test.sh
            src/lib/log/tests/logger_lock_test.sh
            src/lib/log/tests/severity_test.sh
diff --git a/doc/Doxyfile b/doc/Doxyfile
index cc3b595..5c071c1 100644
--- a/doc/Doxyfile
+++ b/doc/Doxyfile
@@ -1,14 +1,14 @@
-# Doxyfile 1.6.1
+# Doxyfile 1.8.2
 
 # This file describes the settings to be used by the documentation system
-# doxygen (www.doxygen.org) for a project
+# doxygen (www.doxygen.org) for a project.
 #
-# All text after a hash (#) is considered a comment and will be ignored
+# All text after a hash (#) is considered a comment and will be ignored.
 # The format is:
 #       TAG = value [value, ...]
 # For lists items can also be appended using:
 #       TAG += value [value, ...]
-# Values that contain spaces should be placed between quotes (" ")
+# Values that contain spaces should be placed between quotes (" ").
 
 #---------------------------------------------------------------------------
 # Project related configuration options
@@ -22,8 +22,9 @@
 
 DOXYFILE_ENCODING      = UTF-8
 
-# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
-# by quotes) that should identify the project.
+# The PROJECT_NAME tag is a single word (or sequence of words) that should
+# identify the project. Note that if you do not use Doxywizard you need
+# to put quotes around the project name if it contains spaces.
 
 PROJECT_NAME           = BIND10
 
@@ -31,12 +32,21 @@ PROJECT_NAME           = BIND10
 # This could be handy for archiving the generated documentation or
 # if some version control system is used.
 
-# Currently this variable is overwritten (see devel target in Makefile.am)
-# If the number of parameters to overwrite increases, we should generate
-# Doxyfile (rename it to Doxyfile.in and generate during configure phase)
-
 PROJECT_NUMBER         =
 
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer
+# a quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF          =
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is
+# included in the documentation. The maximum height of the logo should not
+# exceed 55 pixels and the maximum width should not exceed 200 pixels.
+# Doxygen will copy the logo to the output directory.
+
+PROJECT_LOGO           =
+
 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
 # base path where the generated documentation will be put.
 # If a relative path is entered, it will be relative to the location
@@ -61,7 +71,7 @@ CREATE_SUBDIRS         = YES
 # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
 # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
 # messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
-# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak,
 # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
 
 OUTPUT_LANGUAGE        = English
@@ -116,7 +126,9 @@ FULL_PATH_NAMES        = No
 # only done if one of the specified strings matches the left-hand part of
 # the path. The tag can be used to show relative paths in the file list.
 # If left blank the directory from which doxygen is run is used as the
-# path to strip.
+# path to strip. Note that you specify absolute paths here, but also
+# relative paths, which will be relative from the directory where doxygen is
+# started.
 
 STRIP_FROM_PATH        =
 
@@ -130,7 +142,7 @@ STRIP_FROM_PATH        =
 STRIP_FROM_INC_PATH    =
 
 # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
-# (but less readable) file names. This can be useful is your file systems
+# (but less readable) file names. This can be useful if your file system
 # doesn't support long names like on DOS, Mac, or CD-ROM.
 
 SHORT_NAMES            = NO
@@ -185,6 +197,13 @@ TAB_SIZE               = 4
 
 ALIASES                =
 
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding
+# "class=itcl::class" will allow you to use the command class in the
+# itcl::class meaning.
+
+TCL_SUBST              =
+
 # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
 # sources only. Doxygen will then generate output that is more tailored for C.
 # For instance, some of the names that are used will be different. The list
@@ -211,25 +230,43 @@ OPTIMIZE_FOR_FORTRAN   = NO
 
 OPTIMIZE_OUTPUT_VHDL   = NO
 
-# Doxygen selects the parser to use depending on the extension of the files it parses.
-# With this tag you can assign which parser to use for a given extension.
-# Doxygen has a built-in mapping, but you can override or extend it using this tag.
-# The format is ext=language, where ext is a file extension, and language is one of
-# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP,
-# Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat
-# .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran),
-# use: inc=Fortran f=C. Note that for custom extensions you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension,
+# and language is one of the parsers supported by doxygen: IDL, Java,
+# Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C,
+# C++. For instance to make doxygen treat .inc files as Fortran files (default
+# is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note
+# that for custom extensions you also need to set FILE_PATTERNS otherwise the
+# files are not read by doxygen.
 
 EXTENSION_MAPPING      =
 
+# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all
+# comments according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you
+# can mix doxygen, HTML, and XML commands with Markdown formatting.
+# Disable only in case of backward compatibilities issues.
+
+MARKDOWN_SUPPORT       = YES
+
+# When enabled doxygen tries to link words that correspond to documented classes,
+# or namespaces to their corresponding documentation. Such a link can be
+# prevented in individual cases by by putting a % sign in front of the word or
+# globally by setting AUTOLINK_SUPPORT to NO.
+
+AUTOLINK_SUPPORT       = YES
+
 # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
 # to include (a tag file for) the STL sources as input, then you should
 # set this tag to YES in order to let doxygen match functions declarations and
 # definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
-# func(std::string) {}). This also make the inheritance and collaboration
+# func(std::string) {}). This also makes the inheritance and collaboration
 # diagrams that involve STL classes more complete and accurate.
 
-BUILTIN_STL_SUPPORT    = NO
+BUILTIN_STL_SUPPORT    = YES
 
 # If you use Microsoft's C++/CLI language, you should set this option to YES to
 # enable parsing support.
@@ -242,12 +279,7 @@ CPP_CLI_SUPPORT        = NO
 
 SIP_SUPPORT            = NO
 
-# For Microsoft's IDL there are propget and propput attributes to indicate getter
-# and setter methods for a property. Setting this option to YES (the default)
-# will make doxygen to replace the get and set methods by a property in the
-# documentation. This will only work if the methods are indeed getting or
-# setting a simple type. If this is not the case, or you want to show the
-# methods anyway, you should set this option to NO.
+# For Microsoft's IDL there are propget and propput attributes to indicate getter and setter methods for a property. Setting this option to YES (the default) will make doxygen replace the get and set methods by a property in the documentation. This will only work if the methods are indeed getting or setting a simple type. If this is not the case, or you want to show the methods anyway, you should set this option to NO.
 
 IDL_PROPERTY_SUPPORT   = YES
 
@@ -266,6 +298,22 @@ DISTRIBUTE_GROUP_DOC   = NO
 
 SUBGROUPING            = YES
 
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and
+# unions are shown inside the group in which they are included (e.g. using
+# @ingroup) instead of on a separate page (for HTML and Man pages) or
+# section (for LaTeX and RTF).
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and
+# unions with only public data fields will be shown inline in the documentation
+# of the scope in which they are defined (i.e. file, namespace, or group
+# documentation), provided this scope is documented. If set to NO (the default),
+# structs, classes, and unions are shown on a separate page (for HTML and Man
+# pages) or section (for LaTeX and RTF).
+
+INLINE_SIMPLE_STRUCTS  = NO
+
 # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
 # is documented as struct, union, or enum with the name of the typedef. So
 # typedef struct TypeS {} TypeT, will appear in the documentation as a struct
@@ -282,16 +330,27 @@ TYPEDEF_HIDES_STRUCT   = NO
 # For small to medium size projects (<1000 input files) the default value is
 # probably good enough. For larger projects a too small cache size can cause
 # doxygen to be busy swapping symbols to and from disk most of the time
-# causing a significant performance penality.
+# causing a significant performance penalty.
 # If the system has enough physical memory increasing the cache will improve the
 # performance by keeping more symbols in memory. Note that the value works on
 # a logarithmic scale so increasing the size by one will roughly double the
 # memory usage. The cache size is given by this formula:
 # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
-# corresponding to a cache size of 2^16 = 65536 symbols
+# corresponding to a cache size of 2^16 = 65536 symbols.
 
 SYMBOL_CACHE_SIZE      = 0
 
+# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be
+# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given
+# their name and scope. Since this can be an expensive process and often the
+# same symbol appear multiple times in the code, doxygen keeps a cache of
+# pre-resolved symbols. If the cache is too small doxygen will become slower.
+# If the cache is too large, memory is wasted. The cache size is given by this
+# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols.
+
+LOOKUP_CACHE_SIZE      = 0
+
 #---------------------------------------------------------------------------
 # Build related configuration options
 #---------------------------------------------------------------------------
@@ -308,6 +367,11 @@ EXTRACT_ALL            = YES
 
 EXTRACT_PRIVATE        = NO
 
+# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal
+# scope will be included in the documentation.
+
+EXTRACT_PACKAGE        = NO
+
 # If the EXTRACT_STATIC tag is set to YES all static members of a file
 # will be included in the documentation.
 
@@ -330,7 +394,7 @@ EXTRACT_LOCAL_METHODS  = NO
 # extracted and appear in the documentation as a namespace called
 # 'anonymous_namespace{file}', where file will be replaced with the base
 # name of the file that contains the anonymous namespace. By default
-# anonymous namespace are hidden.
+# anonymous namespaces are hidden.
 
 EXTRACT_ANON_NSPACES   = NO
 
@@ -376,7 +440,7 @@ INTERNAL_DOCS          = NO
 # in case and if your file system supports case sensitive file names. Windows
 # and Mac users are advised to set this option to NO.
 
-CASE_SENSE_NAMES       = NO
+CASE_SENSE_NAMES       = YES
 
 # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
 # will show members with their full class and namespace scopes in the
@@ -388,12 +452,18 @@ HIDE_SCOPE_NAMES       = NO
 # will put a list of the files that are included by a file in the documentation
 # of that file.
 
-SHOW_INCLUDE_FILES     = NO
+SHOW_INCLUDE_FILES     = YES
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES   = NO
 
 # If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
 # is inserted in the documentation for inline members.
 
-INLINE_INFO            = NO
+INLINE_INFO            = YES
 
 # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
 # will sort the (detailed) documentation of file and class members
@@ -407,17 +477,23 @@ SORT_MEMBER_DOCS       = YES
 # by member name. If set to NO (the default) the members will appear in
 # declaration order.
 
-SORT_BRIEF_DOCS        = NO
+SORT_BRIEF_DOCS        = YES
 
-# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the (brief and detailed) documentation of class members so that constructors and destructors are listed first. If set to NO (the default) the constructors will appear in the respective orders defined by SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
+# will sort the (brief and detailed) documentation of class members so that
+# constructors and destructors are listed first. If set to NO (the default)
+# the constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
+# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
+# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
 
-SORT_MEMBERS_CTORS_1ST = NO
+SORT_MEMBERS_CTORS_1ST = YES
 
 # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
 # hierarchy of group names into alphabetical order. If set to NO (the default)
 # the group names will appear in their defined order.
 
-SORT_GROUP_NAMES       = NO
+SORT_GROUP_NAMES       = YES
 
 # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
 # sorted by fully-qualified names, including namespaces. If set to
@@ -429,6 +505,15 @@ SORT_GROUP_NAMES       = NO
 
 SORT_BY_SCOPE_NAME     = NO
 
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to
+# do proper type resolution of all parameters of a function it will reject a
+# match between the prototype and the implementation of a member function even
+# if there is only one candidate or it is obvious which candidate to choose
+# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen
+# will still accept a match between prototype and implementation in such cases.
+
+STRICT_PROTO_MATCHING  = NO
+
 # The GENERATE_TODOLIST tag can be used to enable (YES) or
 # disable (NO) the todo list. This list is created by putting \todo
 # commands in the documentation.
@@ -459,10 +544,10 @@ GENERATE_DEPRECATEDLIST= YES
 ENABLED_SECTIONS       =
 
 # The MAX_INITIALIZER_LINES tag determines the maximum number of lines
-# the initial value of a variable or define consists of for it to appear in
+# the initial value of a variable or macro consists of for it to appear in
 # the documentation. If the initializer consists of more lines than specified
 # here it will be hidden. Use a value of 0 to hide initializers completely.
-# The appearance of the initializer of individual variables and defines in the
+# The appearance of the initializer of individual variables and macros in the
 # documentation can be controlled using \showinitializer or \hideinitializer
 # command in the documentation regardless of this setting.
 
@@ -474,12 +559,6 @@ MAX_INITIALIZER_LINES  = 30
 
 SHOW_USED_FILES        = YES
 
-# If the sources in your project are distributed over multiple directories
-# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
-# in the documentation. The default is NO.
-
-SHOW_DIRECTORIES       = NO
-
 # Set the SHOW_FILES tag to NO to disable the generation of the Files page.
 # This will remove the Files entry from the Quick Index and from the
 # Folder Tree View (if specified). The default is YES.
@@ -503,15 +582,25 @@ SHOW_NAMESPACES        = YES
 
 FILE_VERSION_FILTER    =
 
-# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by
-# doxygen. The layout file controls the global structure of the generated output files
-# in an output format independent way. The create the layout file that represents
-# doxygen's defaults, run doxygen with the -l option. You can optionally specify a
-# file name after the option, if omitted DoxygenLayout.xml will be used as the name
-# of the layout file.
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option.
+# You can optionally specify a file name after the option, if omitted
+# DoxygenLayout.xml will be used as the name of the layout file.
 
 LAYOUT_FILE            =
 
+# The CITE_BIB_FILES tag can be used to specify one or more bib files
+# containing the references data. This must be a list of .bib files. The
+# .bib extension is automatically appended if omitted. Using this command
+# requires the bibtex tool to be installed. See also
+# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style
+# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this
+# feature you need bibtex and perl available in the search path.
+
+CITE_BIB_FILES         =
+
 #---------------------------------------------------------------------------
 # configuration options related to warning and progress messages
 #---------------------------------------------------------------------------
@@ -519,7 +608,7 @@ LAYOUT_FILE            =
 # The QUIET tag can be used to turn on/off the messages that are generated
 # by doxygen. Possible values are YES and NO. If left blank NO is used.
 
-QUIET                  = NO
+QUIET                  = YES
 
 # The WARNINGS tag can be used to turn on/off the warning messages that are
 # generated by doxygen. Possible values are YES and NO. If left blank
@@ -540,7 +629,7 @@ WARN_IF_UNDOCUMENTED   = YES
 
 WARN_IF_DOC_ERROR      = YES
 
-# This WARN_NO_PARAMDOC option can be abled to get warnings for
+# The WARN_NO_PARAMDOC option can be enabled to get warnings for
 # functions that are documented, but have no documentation for their parameters
 # or return value. If set to NO (the default) doxygen will only warn about
 # wrong or incomplete parameter documentation, but not about the absence of
@@ -572,16 +661,36 @@ WARN_LOGFILE           =
 # directories like "/usr/src/myproject". Separate the files or directories
 # with spaces.
 
-INPUT                  = ../src/lib/exceptions ../src/lib/cc \
-    ../src/lib/config ../src/lib/cryptolink ../src/lib/dns \
-    ../src/lib/datasrc ../src/lib/datasrc/memory \
-    ../src/bin/auth ../src/bin/resolver ../src/lib/bench ../src/lib/log \
-    ../src/lib/log/compiler ../src/lib/asiolink/ ../src/lib/nsas \
-    ../src/lib/testutils ../src/lib/cache ../src/lib/server_common/ \
-    ../src/bin/sockcreator/ ../src/lib/util/ ../src/lib/util/io/ \
-    ../src/lib/util/threads/ ../src/lib/resolve ../src/lib/acl \
-    ../src/lib/statistics ../src/bin/dhcp6 ../src/lib/dhcp ../src/bin/dhcp4 \
-    ../tests/tools/perfdhcp devel
+INPUT                  = ../src/lib/exceptions \
+                         ../src/lib/cc \
+                         ../src/lib/config \
+                         ../src/lib/cryptolink \
+                         ../src/lib/dns \
+                         ../src/lib/datasrc \
+                         ../src/lib/datasrc/memory \
+                         ../src/bin/auth \
+                         ../src/bin/resolver \
+                         ../src/lib/bench \
+                         ../src/lib/log \
+                         ../src/lib/log/compiler \
+                         ../src/lib/asiolink/ \
+                         ../src/lib/nsas \
+                         ../src/lib/testutils \
+                         ../src/lib/cache \
+                         ../src/lib/server_common/ \
+                         ../src/bin/sockcreator/ \
+                         ../src/lib/util/ \
+                         ../src/lib/util/io/ \
+                         ../src/lib/util/threads/ \
+                         ../src/lib/resolve \
+                         ../src/lib/acl \
+                         ../src/lib/statistics \
+                         ../src/bin/dhcp6 \
+                         ../src/lib/dhcp \
+                         ../src/lib/dhcpsrv \
+                         ../src/bin/dhcp4 \
+                         ../tests/tools/perfdhcp \
+                         devel
 
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
@@ -595,10 +704,15 @@ INPUT_ENCODING         = UTF-8
 # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
 # and *.h) to filter out the source-files in the directories. If left
 # blank the following patterns are tested:
-# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
-# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
+# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh
+# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py
+# *.f90 *.f *.for *.vhd *.vhdl
 
-FILE_PATTERNS          = *.c *.cc *.h *.hpp *.dox
+FILE_PATTERNS          = *.c \
+                         *.cc \
+                         *.h \
+                         *.hpp \
+                         *.dox
 
 # The RECURSIVE tag can be used to turn specify whether or not subdirectories
 # should be searched for input files as well. Possible values are YES and NO.
@@ -606,14 +720,16 @@ FILE_PATTERNS          = *.c *.cc *.h *.hpp *.dox
 
 RECURSIVE              = NO
 
-# The EXCLUDE tag can be used to specify files and/or directories that should
+# The EXCLUDE tag can be used to specify files and/or directories that should be
 # excluded from the INPUT source files. This way you can easily exclude a
 # subdirectory from a directory tree whose root is specified with the INPUT tag.
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
 
 EXCLUDE                =
 
-# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
-# directories that are symbolic links (a Unix filesystem feature) are excluded
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
 # from the input.
 
 EXCLUDE_SYMLINKS       = NO
@@ -624,7 +740,8 @@ EXCLUDE_SYMLINKS       = NO
 # against the file with absolute path, so to exclude all test directories
 # for example use the pattern */test/*
 
-EXCLUDE_PATTERNS       = */*-placeholder.* */cpp/*.py
+EXCLUDE_PATTERNS       = */*-placeholder.* \
+                         */cpp/*.py
 
 # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
 # (namespaces, classes, functions, etc.) that should be excluded from the
@@ -677,8 +794,8 @@ INPUT_FILTER           =
 # filter if there is a match.
 # The filters are a list of the form:
 # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
-# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
-# is applied to all files.
+# info on how filters are used. If FILTER_PATTERNS is empty or if
+# non of the patterns match the file name, INPUT_FILTER is applied.
 
 FILTER_PATTERNS        =
 
@@ -688,6 +805,14 @@ FILTER_PATTERNS        =
 
 FILTER_SOURCE_FILES    = NO
 
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any)
+# and it is also possible to disable source filtering for a specific pattern
+# using *.ext= (so without naming a filter). This option only has effect when
+# FILTER_SOURCE_FILES is enabled.
+
+FILTER_SOURCE_PATTERNS =
+
 #---------------------------------------------------------------------------
 # configuration options related to source browsing
 #---------------------------------------------------------------------------
@@ -697,7 +822,7 @@ FILTER_SOURCE_FILES    = NO
 # Note: To get rid of all source code in the generated output, make sure also
 # VERBATIM_HEADERS is set to NO.
 
-SOURCE_BROWSER         = NO
+SOURCE_BROWSER         = YES
 
 # Setting the INLINE_SOURCES tag to YES will include the body
 # of functions and classes directly in the documentation.
@@ -706,7 +831,7 @@ INLINE_SOURCES         = NO
 
 # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
 # doxygen to hide any special comment blocks from generated source code
-# fragments. Normal C and C++ comments will always remain visible.
+# fragments. Normal C, C++ and Fortran comments will always remain visible.
 
 STRIP_CODE_COMMENTS    = YES
 
@@ -790,7 +915,14 @@ HTML_FILE_EXTENSION    = .html
 
 # The HTML_HEADER tag can be used to specify a personal HTML header for
 # each generated HTML page. If it is left blank doxygen will generate a
-# standard header.
+# standard header. Note that when using a custom header you are responsible
+#  for the proper inclusion of any scripts and style sheets that doxygen
+# needs, which is dependent on the configuration options used.
+# It is advised to generate a default header using "doxygen -w html
+# header.html footer.html stylesheet.css YourConfigFile" and then modify
+# that header. Note that the header is subject to change so you typically
+# have to redo this when upgrading to a newer version of doxygen or when
+# changing the value of configuration settings such as GENERATE_TREEVIEW!
 
 HTML_HEADER            =
 
@@ -802,26 +934,79 @@ HTML_FOOTER            =
 
 # The HTML_STYLESHEET tag can be used to specify a user-defined cascading
 # style sheet that is used by each HTML page. It can be used to
-# fine-tune the look of the HTML output. If the tag is left blank doxygen
-# will generate a default style sheet. Note that doxygen will try to copy
-# the style sheet file to the HTML output directory, so don't put your own
-# stylesheet in the HTML output directory as well, or it will be erased!
+# fine-tune the look of the HTML output. If left blank doxygen will
+# generate a default style sheet. Note that it is recommended to use
+# HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this
+# tag will in the future become obsolete.
 
 HTML_STYLESHEET        =
 
-# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
-# files or namespaces will be aligned in HTML using tables. If set to
-# NO a bullet list will be used.
+# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional
+# user-defined cascading style sheet that is included after the standard
+# style sheets created by doxygen. Using this option one can overrule
+# certain style aspects. This is preferred over using HTML_STYLESHEET
+# since it does not replace the standard style sheet and is therefor more
+# robust against future updates. Doxygen will copy the style sheet file to
+# the output directory.
+
+HTML_EXTRA_STYLESHEET  =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that
+# the files will be copied as-is; there are no commands or markers available.
+
+HTML_EXTRA_FILES       =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
+# Doxygen will adjust the colors in the style sheet and background images
+# according to this color. Hue is specified as an angle on a colorwheel,
+# see http://en.wikipedia.org/wiki/Hue for more information.
+# For instance the value 0 represents red, 60 is yellow, 120 is green,
+# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
+# The allowed range is 0 to 359.
+
+HTML_COLORSTYLE_HUE    = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
+# the colors in the HTML output. For a value of 0 the output will use
+# grayscales only. A value of 255 will produce the most vivid colors.
+
+HTML_COLORSTYLE_SAT    = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
+# the luminance component of the colors in the HTML output. Values below
+# 100 gradually make the output lighter, whereas values above 100 make
+# the output darker. The value divided by 100 is the actual gamma applied,
+# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
+# and 100 does not change the gamma.
+
+HTML_COLORSTYLE_GAMMA  = 80
 
-HTML_ALIGN_MEMBERS     = YES
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
+# this to NO can help when comparing the output of multiple runs.
+
+HTML_TIMESTAMP         = YES
 
 # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
 # documentation will contain sections that can be hidden and shown after the
-# page has loaded. For this to work a browser that supports
-# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
-# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+# page has loaded.
+
+HTML_DYNAMIC_SECTIONS  = YES
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of
+# entries shown in the various tree structured indices initially; the user
+# can expand and collapse entries dynamically later on. Doxygen will expand
+# the tree to such a level that at most the specified number of entries are
+# visible (unless a fully collapsed tree already exceeds this amount).
+# So setting the number of entries 1 will produce a full collapsed tree by
+# default. 0 is a special value representing an infinite number of entries
+# and will result in a full expanded tree by default.
 
-HTML_DYNAMIC_SECTIONS  = NO
+HTML_INDEX_NUM_ENTRIES = 100
 
 # If the GENERATE_DOCSET tag is set to YES, additional index files
 # will be generated that can be used as input for Apple's Xcode 3
@@ -831,7 +1016,8 @@ HTML_DYNAMIC_SECTIONS  = NO
 # directory and running "make install" will install the docset in
 # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
 # it at startup.
-# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
 
 GENERATE_DOCSET        = NO
 
@@ -849,6 +1035,16 @@ DOCSET_FEEDNAME        = "Doxygen generated docs"
 
 DOCSET_BUNDLE_ID       = org.doxygen.Project
 
+# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely
+# identify the documentation publisher. This should be a reverse domain-name
+# style string, e.g. com.mycompany.MyDocSet.documentation.
+
+DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
+
+# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
+
+DOCSET_PUBLISHER_NAME  = Publisher
+
 # If the GENERATE_HTMLHELP tag is set to YES, additional index files
 # will be generated that can be used as input for tools like the
 # Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
@@ -893,10 +1089,10 @@ BINARY_TOC             = NO
 
 TOC_EXPAND             = NO
 
-# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER
-# are set, an additional index file will be generated that can be used as input for
-# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated
-# HTML documentation.
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
+# that can be used as input for Qt's qhelpgenerator to generate a
+# Qt Compressed Help (.qch) of the generated HTML documentation.
 
 GENERATE_QHP           = NO
 
@@ -918,20 +1114,24 @@ QHP_NAMESPACE          =
 
 QHP_VIRTUAL_FOLDER     = doc
 
-# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add.
-# For more information please see
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
+# add. For more information please see
 # http://doc.trolltech.com/qthelpproject.html#custom-filters
 
 QHP_CUST_FILTER_NAME   =
 
-# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see
-# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">Qt Help Project / Custom Filters</a>.
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
+# Qt Help Project / Custom Filters</a>.
 
 QHP_CUST_FILTER_ATTRS  =
 
-# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's
 # filter section matches.
-# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">Qt Help Project / Filter Attributes</a>.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
+# Qt Help Project / Filter Attributes</a>.
 
 QHP_SECT_FILTER_ATTRS  =
 
@@ -942,16 +1142,30 @@ QHP_SECT_FILTER_ATTRS  =
 
 QHG_LOCATION           =
 
-# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
-# top of each HTML page. The value NO (the default) enables the index and
-# the value YES disables it.
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+#  will be generated, which together with the HTML files, form an Eclipse help
+# plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
+# the help appears.
 
-DISABLE_INDEX          = NO
+GENERATE_ECLIPSEHELP   = NO
 
-# This tag can be used to set the number of enum values (range [1..20])
-# that doxygen will group on one line in the generated HTML documentation.
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
+# this name.
 
-ENUM_VALUES_PER_LINE   = 4
+ECLIPSE_DOC_ID         = org.doxygen.Project
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs)
+# at top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it. Since the tabs have the same information as the
+# navigation tree you can set this option to NO if you already set
+# GENERATE_TREEVIEW to YES.
+
+DISABLE_INDEX          = NO
 
 # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
 # structure should be generated to display hierarchical information.
@@ -960,13 +1174,17 @@ ENUM_VALUES_PER_LINE   = 4
 # is generated for HTML Help). For this to work a browser that supports
 # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
 # Windows users are probably better off using the HTML help feature.
+# Since the tree basically has the same information as the tab index you
+# could consider to set DISABLE_INDEX to NO when enabling this option.
 
 GENERATE_TREEVIEW      = YES
 
-# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
-# and Class Hierarchy pages using a tree view instead of an ordered list.
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values
+# (range [0,1..20]) that doxygen will group on one line in the generated HTML
+# documentation. Note that a value of 0 will completely suppress the enum
+# values from appearing in the overview section.
 
-USE_INLINE_TREES       = NO
+ENUM_VALUES_PER_LINE   = 4
 
 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
 # used to set the initial width (in pixels) of the frame in which the tree
@@ -974,6 +1192,11 @@ USE_INLINE_TREES       = NO
 
 TREEVIEW_WIDTH         = 180
 
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
+# links to external symbols imported via tag files in a separate window.
+
+EXT_LINKS_IN_WINDOW    = NO
+
 # Use this tag to change the font size of Latex formulas included
 # as images in the HTML documentation. The default is 10. Note that
 # when you change the font size after a successful doxygen run you need
@@ -982,13 +1205,60 @@ TREEVIEW_WIDTH         = 180
 
 FORMULA_FONTSIZE       = 10
 
-# When the SEARCHENGINE tag is enable doxygen will generate a search box for the HTML output. The underlying search engine uses javascript
-# and DHTML and should work on any modern browser. Note that when using HTML help (GENERATE_HTMLHELP) or Qt help (GENERATE_QHP)
-# there is already a search function so this one should typically
-# be disabled.
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are
+# not supported properly for IE 6.0, but are supported on all modern browsers.
+# Note that when changing this option you need to delete any form_*.png files
+# in the HTML output before the changes have effect.
+
+FORMULA_TRANSPARENT    = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax
+# (see http://www.mathjax.org) which uses client side Javascript for the
+# rendering instead of using prerendered bitmaps. Use this if you do not
+# have LaTeX installed or if you want to formulas look prettier in the HTML
+# output. When enabled you may also need to install MathJax separately and
+# configure the path to it using the MATHJAX_RELPATH option.
+
+USE_MATHJAX            = NO
+
+# When MathJax is enabled you need to specify the location relative to the
+# HTML output directory using the MATHJAX_RELPATH option. The destination
+# directory should contain the MathJax.js script. For instance, if the mathjax
+# directory is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to
+# the MathJax Content Delivery Network so you can quickly see the result without
+# installing MathJax.
+# However, it is strongly recommended to install a local
+# copy of MathJax from http://www.mathjax.org before deployment.
+
+MATHJAX_RELPATH        = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension
+# names that should be enabled during MathJax rendering.
+
+MATHJAX_EXTENSIONS     =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using
+# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
+# (GENERATE_DOCSET) there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
 
 SEARCHENGINE           = NO
 
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a PHP enabled web server instead of at the web client
+# using Javascript. Doxygen will generate the search PHP script and index
+# file to put on the web server. The advantage of the server
+# based approach is that it scales better to large projects and allows
+# full text search. The disadvantages are that it is more difficult to setup
+# and does not have live searching capabilities.
+
+SERVER_BASED_SEARCH    = NO
+
 #---------------------------------------------------------------------------
 # configuration options related to the LaTeX output
 #---------------------------------------------------------------------------
@@ -1006,6 +1276,9 @@ LATEX_OUTPUT           = latex
 
 # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
 # invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
+# Makefile that is written to the output directory.
 
 LATEX_CMD_NAME         = latex
 
@@ -1022,7 +1295,7 @@ MAKEINDEX_CMD_NAME     = makeindex
 COMPACT_LATEX          = NO
 
 # The PAPER_TYPE tag can be used to set the paper type that is used
-# by the printer. Possible values are: a4, a4wide, letter, legal and
+# by the printer. Possible values are: a4, letter, legal and
 # executive. If left blank a4wide will be used.
 
 PAPER_TYPE             = a4wide
@@ -1039,6 +1312,13 @@ EXTRA_PACKAGES         =
 
 LATEX_HEADER           =
 
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for
+# the generated latex document. The footer should contain everything after
+# the last chapter. If it is left blank doxygen will generate a
+# standard footer. Notice: only use this tag if you know what you are doing!
+
+LATEX_FOOTER           =
+
 # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
 # is prepared for conversion to pdf (using ps2pdf). The pdf file will
 # contain links (just like the HTML output) instead of page references
@@ -1065,10 +1345,19 @@ LATEX_BATCHMODE        = NO
 
 LATEX_HIDE_INDICES     = NO
 
-# If LATEX_SOURCE_CODE is set to YES then doxygen will include source code with syntax highlighting in the LaTeX output. Note that which sources are shown also depends on other settings such as SOURCE_BROWSER.
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include
+# source code with syntax highlighting in the LaTeX output.
+# Note that which sources are shown also depends on other settings
+# such as SOURCE_BROWSER.
 
 LATEX_SOURCE_CODE      = NO
 
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See
+# http://en.wikipedia.org/wiki/BibTeX for more info.
+
+LATEX_BIB_STYLE        = plain
+
 #---------------------------------------------------------------------------
 # configuration options related to the RTF output
 #---------------------------------------------------------------------------
@@ -1100,7 +1389,7 @@ COMPACT_RTF            = NO
 
 RTF_HYPERLINKS         = NO
 
-# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# Load style sheet definitions from file. Syntax is similar to doxygen's
 # config file, i.e. a series of assignments. You only have to provide
 # replacements, missing definitions are set to their default value.
 
@@ -1245,7 +1534,7 @@ MACRO_EXPANSION        = YES
 EXPAND_ONLY_PREDEF     = NO
 
 # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
-# in the INCLUDE_PATH (see below) will be search if a #include is found.
+# pointed to by INCLUDE_PATH will be searched when a #include is found.
 
 SEARCH_INCLUDES        = YES
 
@@ -1275,15 +1564,15 @@ PREDEFINED             =
 # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
 # this tag can be used to specify a list of macro names that should be expanded.
 # The macro definition that is found in the sources will be used.
-# Use the PREDEFINED tag if you want to use a different macro definition.
+# Use the PREDEFINED tag if you want to use a different macro definition that
+# overrules the definition found in the source code.
 
 EXPAND_AS_DEFINED      =
 
 # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
-# doxygen's preprocessor will remove all function-like macros that are alone
-# on a line, have an all uppercase name, and do not end with a semicolon. Such
-# function macros are typically used for boiler-plate code, and will confuse
-# the parser if not removed.
+# doxygen's preprocessor will remove all references to function-like macros
+# that are alone on a line, have an all uppercase name, and do not end with a
+# semicolon, because these will confuse the parser if not removed.
 
 SKIP_FUNCTION_MACROS   = YES
 
@@ -1291,22 +1580,18 @@ SKIP_FUNCTION_MACROS   = YES
 # Configuration::additions related to external references
 #---------------------------------------------------------------------------
 
-# The TAGFILES option can be used to specify one or more tagfiles.
-# Optionally an initial location of the external documentation
-# can be added for each tagfile. The format of a tag file without
-# this location is as follows:
+# The TAGFILES option can be used to specify one or more tagfiles. For each
+# tag file the location of the external documentation should be added. The
+# format of a tag file without this location is as follows:
 #
 # TAGFILES = file1 file2 ...
 # Adding location for the tag files is done as follows:
 #
 # TAGFILES = file1=loc1 "file2 = loc2" ...
-# where "loc1" and "loc2" can be relative or absolute paths or
-# URLs. If a location is present for each tag, the installdox tool
-# does not have to be run to correct the links.
-# Note that each tag file must have a unique name
-# (where the name does NOT include the path)
-# If a tag file is not located in the directory in which doxygen
-# is run, you must also specify the path to the tagfile here.
+# where "loc1" and "loc2" can be relative or absolute paths
+# or URLs. Note that each tag file must have a unique name (where the name does
+# NOT include the path). If a tag file is not located in the directory in which
+# doxygen is run, you must also specify the path to the tagfile here.
 
 TAGFILES               =
 
@@ -1339,9 +1624,8 @@ PERL_PATH              = /usr/bin/perl
 # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
 # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
 # or super classes. Setting the tag to NO turns the diagrams off. Note that
-# this option is superseded by the HAVE_DOT option below. This is only a
-# fallback. It is recommended to install and use dot, since it yields more
-# powerful graphs.
+# this option also works with HAVE_DOT disabled, but it is recommended to
+# install and use dot, since it yields more powerful graphs.
 
 CLASS_DIAGRAMS         = YES
 
@@ -1367,14 +1651,20 @@ HIDE_UNDOC_RELATIONS   = YES
 
 HAVE_DOT               = NO
 
-# By default doxygen will write a font called FreeSans.ttf to the output
-# directory and reference it in all dot files that doxygen generates. This
-# font does not include all possible unicode characters however, so when you need
-# these (or just want a differently looking font) you can specify the font name
-# using DOT_FONTNAME. You need need to make sure dot is able to find the font,
-# which can be done by putting it in a standard location or by setting the
-# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
-# containing the font.
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
+# allowed to run in parallel. When set to 0 (the default) doxygen will
+# base this on the number of processors available in the system. You can set it
+# explicitly to a value larger than 0 to get control over the balance
+# between CPU load and processing speed.
+
+DOT_NUM_THREADS        = 0
+
+# By default doxygen will use the Helvetica font for all dot files that
+# doxygen generates. When you want a differently looking font you can specify
+# the font name using DOT_FONTNAME. You need to make sure dot is able to find
+# the font, which can be done by putting it in a standard location or by setting
+# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the
+# directory containing the font.
 
 DOT_FONTNAME           = FreeSans
 
@@ -1383,17 +1673,16 @@ DOT_FONTNAME           = FreeSans
 
 DOT_FONTSIZE           = 10
 
-# By default doxygen will tell dot to use the output directory to look for the
-# FreeSans.ttf font (which doxygen will put there itself). If you specify a
-# different font using DOT_FONTNAME you can set the path where dot
-# can find it using this tag.
+# By default doxygen will tell dot to use the Helvetica font.
+# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to
+# set the path where dot can find it.
 
 DOT_FONTPATH           =
 
 # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
 # will generate a graph for each documented class showing the direct and
 # indirect inheritance relations. Setting this tag to YES will force the
-# the CLASS_DIAGRAMS tag to NO.
+# CLASS_DIAGRAMS tag to NO.
 
 CLASS_GRAPH            = YES
 
@@ -1415,6 +1704,15 @@ GROUP_GRAPHS           = YES
 
 UML_LOOK               = NO
 
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside
+# the class node. If there are many fields or methods and many nodes the
+# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS
+# threshold limits the number of items for each type to make the size more
+# managable. Set this to 0 for no limit. Note that the threshold may be
+# exceeded by 50% before the limit is enforced.
+
+UML_LIMIT_NUM_FIELDS   = 10
+
 # If set to YES, the inheritance and collaboration graphs will show the
 # relations between templates and their instances.
 
@@ -1451,11 +1749,11 @@ CALL_GRAPH             = NO
 CALLER_GRAPH           = NO
 
 # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
-# will graphical hierarchy of all classes instead of a textual one.
+# will generate a graphical hierarchy of all classes instead of a textual one.
 
 GRAPHICAL_HIERARCHY    = YES
 
-# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES
 # then doxygen will show the dependencies a directory has on other directories
 # in a graphical way. The dependency relations are determined by the #include
 # relations between the files in the directories.
@@ -1463,11 +1761,22 @@ GRAPHICAL_HIERARCHY    = YES
 DIRECTORY_GRAPH        = YES
 
 # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
-# generated by dot. Possible values are png, jpg, or gif
-# If left blank png will be used.
+# generated by dot. Possible values are svg, png, jpg, or gif.
+# If left blank png will be used. If you choose svg you need to set
+# HTML_FILE_EXTENSION to xhtml in order to make the SVG files
+# visible in IE 9+ (other browsers do not have this requirement).
 
 DOT_IMAGE_FORMAT       = png
 
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+# Note that this requires a modern browser other than Internet Explorer.
+# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you
+# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files
+# visible. Older versions of IE do not have SVG support.
+
+INTERACTIVE_SVG        = NO
+
 # The tag DOT_PATH can be used to specify the path where the dot tool can be
 # found. If left blank, it is assumed the dot tool can be found in the path.
 
@@ -1479,6 +1788,12 @@ DOT_PATH               =
 
 DOTFILE_DIRS           =
 
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the
+# \mscfile command).
+
+MSCFILE_DIRS           =
+
 # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
 # nodes that will be shown in the graph. If the number of nodes in a graph
 # becomes larger than this value, doxygen will truncate the graph, which is
diff --git a/doc/devel/02-dhcp.dox b/doc/devel/02-dhcp.dox
index 5c59daa..b98920f 100644
--- a/doc/devel/02-dhcp.dox
+++ b/doc/devel/02-dhcp.dox
@@ -19,7 +19,7 @@
  *
  * @section dhcpv4Session BIND10 message queue integration
  *
- * DHCPv4 server component is now integrated with BIND10 message queue.
+ * DHCPv4 server component is now integrated with the BIND10 message queue.
  * The integration is performed by establishSession() and disconnectSession()
  * functions in isc::dhcp::ControlledDhcpv4Srv class. main() method deifined
  * in the src/bin/dhcp4/main.cc file instantiates isc::dhcp::ControlledDhcpv4Srv
@@ -57,4 +57,4 @@
  * that does not support msgq. That is useful for embedded environments.
  * It may also be useful in validation.
  *
- */
\ No newline at end of file
+ */
diff --git a/doc/devel/mainpage.dox b/doc/devel/mainpage.dox
index db42c14..295fd03 100644
--- a/doc/devel/mainpage.dox
+++ b/doc/devel/mainpage.dox
@@ -20,12 +20,14 @@
  * - @subpage DataScrubbing
  *
  * @section DHCP
- * - @subpage dhcpv4
+ * - @subpage dhcp4
  *   - @subpage dhcpv4Session
- * - @subpage dhcpv6
- *   - @subpage dhcpv6-session
- *   - @subpage dhcpv6-config-parser
- *   - @subpage dhcpv6-config-inherit
+ *   - @subpage dhcpv4ConfigParser
+ *   - @subpage dhcpv4ConfigInherit
+ * - @subpage dhcp6
+ *   - @subpage dhcpv6Session
+ *   - @subpage dhcpv6ConfigParser
+ *   - @subpage dhcpv6ConfigInherit
  * - @subpage libdhcp
  *   - @subpage libdhcpIntro
  *   - @subpage libdhcpIfaceMgr
@@ -33,7 +35,7 @@
  *   - @subpage leasemgr
  *   - @subpage cfgmgr
  *   - @subpage allocengine
- * - @subpage dhcp-database-backends
+ * - @subpage dhcpDatabaseBackends
  * - @subpage perfdhcpInternals
  *
  * @section misc Miscellaneous topics
diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml
index 9d8f932..61a9ee4 100644
--- a/doc/guide/bind10-guide.xml
+++ b/doc/guide/bind10-guide.xml
@@ -472,7 +472,7 @@ var/
       <title>Packages</title>
 
       <para>
-        Some operating systems or softare package vendors may
+        Some operating systems or software package vendors may
         provide ready-to-use, pre-built software packages for
         the BIND 10 suite.
         Installing a pre-built package means you do not need to
@@ -758,7 +758,7 @@ as a dependency earlier -->
           If the configure fails, it may be due to missing or old
           dependencies.
         </para>
-        
+
         <note>
           <para>For notes on configuring and building DHCPv6 with MySQL see <xref linkend="dhcp6-install">.</xref></para>
         </note>
@@ -1841,10 +1841,8 @@ config set /Boss/components/b10-zonemgr/kind dispensable
         <para>
           The key ring lives in the configuration in "tsig_keys/keys". Most of
           the system uses the keys from there — ACLs, authoritative server to
-          sign responses to signed queries, and <command>b10-xfrout</command>
-          to sign transfers. The <command>b10-xfrin</command> uses its own
-          configuration for keys, but that will be fixed in Trac ticket
-          <ulink url="http://bind10.isc.org/ticket/1351">#1351</ulink>.
+          sign responses to signed queries, and <command>b10-xfrin</command>
+          and <command>b10-xfrout</command> to sign transfers.
         </para>
 
         <para>
@@ -2157,7 +2155,7 @@ AND_MATCH := "ALL": [ RULE_RAW, RULE_RAW, ... ]
 	you indicate that the system is not usable without the
 	component and if such component fails, the system shuts
 	down no matter when the failure happened.  This is the
-	behaviour of the core components (the ones you can't turn
+	behavior of the core components (the ones you can't turn
 	off), but you can declare any other components as core as
 	well if you wish (but you can turn these off, they just
 	can't fail).
@@ -2722,6 +2720,15 @@ TODO
     </section>
 
     <section>
+        <title>TSIG</title>
+        If you want to use TSIG for incoming transfers, a system wide TSIG
+        key ring must be configured (see <xref linkend="tsig-key-ring"/>).
+        To specify a key to use, set tsig_key value to the name of the key
+        to use from the key ring.
+> <userinput>config set Xfrin/zones[0]/tsig_key "<option>example.key</option>"</userinput>
+    </section>
+
+    <section>
       <title>Enabling IXFR</title>
       <para>
         As noted above, <command>b10-xfrin</command> uses AXFR for
@@ -3397,16 +3404,94 @@ then change those defaults with config set Resolver/forward_addresses[0]/address
     <section id="dhcp4-config">
       <title>DHCPv4 Server Configuration</title>
       <para>
-        The DHCPv4 server does not have a lease database implemented yet
-        nor any support for configuration, so the same set
-        of configuration options (including the same fixed address)
-        will be assigned every time.
+        Once the server is started, it can be configured. To view the
+        current configuration, use the following command in <command>bindctl</command>:
+        <screen>
+> <userinput>config show Dhcp4</userinput></screen>
+        When starting Dhcp4 daemon for the first time, the default configuration
+        will be available. It will look similar to this:
+        <screen>
+> <userinput>config show Dhcp4</userinput>
+Dhcp4/interface/                list    (default)
+Dhcp4/renew-timer        1000   integer	(default)
+Dhcp4/rebind-timer       2000   integer	(default)
+Dhcp4/preferred-lifetime 3000   integer	(default)
+Dhcp4/valid-lifetime	 4000   integer	(default)
+Dhcp4/subnet4	         []     list    (default)</screen>
       </para>
+
+      <para>
+        To change one of the parameters, simply follow
+        the usual <command>bindctl</command> procedure. For example, to make the
+        leases longer, change their valid-lifetime parameter:
+        <screen>
+> <userinput>config set Dhcp4/valid-lifetime 7200</userinput>
+> <userinput>config commit</userinput></screen>
+        Please note that most Dhcp4 parameters are of global scope
+        and apply to all defined subnets, unless they are overridden on a
+        per-subnet basis.
+      </para>
+
+      <para>
+        The essential role of DHCPv4 server is address assignment. The server
+        has to be configured with at least one subnet and one pool of dynamic
+        addresses to be managed. For example, assume that the server
+        is connected to a network segment that uses the 192.0.2.0/24
+        prefix. The Administrator of that network has decided that addresses from range
+        192.0.2.10 to 192.0.2.20 are going to be managed by the Dhcp4
+        server. Such a configuration can be achieved in the following way:
+        <screen>
+> <userinput>config add Dhcp4/subnet4</userinput>
+> <userinput>config set Dhcp4/subnet4[0]/subnet "192.0.2.0/24"</userinput>
+> <userinput>config set Dhcp4/subnet4[0]/pool [ "192.0.2.10 - 192.0.2.20" ]</userinput>
+> <userinput>config commit</userinput></screen>
+        Note that subnet is defined as a simple string, but the pool parameter
+        is actually a list of pools: for this reason, the pool definition is
+        enclosed in square brackets, even though only one range of addresses
+        is specified.</para>
+        <para>It is possible to define more than one pool in a
+        subnet: continuing the previous example, further assume that
+        192.0.2.64/26 should be also be managed by the server. It could be written as
+        192.0.2.64 to 192.0.2.127. Alternatively, it can be expressed more simply as
+        192.0.2.64/26. Both formats are supported by Dhcp4 and can be mixed in the pool list.
+        For example, one could define the following pools:
+        <screen>
+> <userinput>config set Dhcp4/subnet4[0]/pool [ "192.0.2.10-192.0.2.20", "192.0.2.64/26" ]</userinput>
+> <userinput>config commit</userinput></screen>
+        The number of pools is not limited, but for performance reasons it is recommended to
+        use as few as possible. Space and tabulations in pool definitions are ignored, so
+        spaces before and after hyphen are optional. They can be used to improve readability.
+      </para>
+      <para>
+         The server may be configured to serve more than one subnet. To add a second subnet,
+         use a command similar to the following:
+        <screen>
+> <userinput>config add Dhcp4/subnet4</userinput>
+> <userinput>config set Dhcp4/subnet4[1]/subnet "192.0.3.0/24"</userinput>
+> <userinput>config set Dhcp4/subnet4[1]/pool [ "192.0.3.0/24" ]</userinput>
+> <userinput>config commit</userinput></screen>
+        Arrays are counted from 0. subnet[0] refers to the subnet defined in the
+        previous example.  The <command>config add Dhcp4/subnet4</command> adds
+        another (second) subnet. It can be referred to as
+        <command>Dhcp4/subnet4[1]</command>. In this example, we allow server to
+        dynamically assign all addresses available in the whole subnet.
+      </para>
+      <para>
+        When configuring a DHCPv4 server using prefix/length notation, please pay
+        attention to the boundary values. When specifying that the server should use
+        a given pool, it will be able to allocate also first (typically network
+        address) and the last (typically broadcast address) address from that pool.
+        In the aforementioned example of pool 192.0.3.0/24, both 192.0.3.0 and
+        192.0.3.255 addresses may be assigned as well. This may be invalid in some
+        network configurations. If you want to avoid this, please use min-max notation.
+      </para>
+
       <para>
-        At this stage of development, the only way to alter the server
-        configuration is to modify its source code. To do so, please
-        edit src/bin/dhcp4/dhcp4_srv.cc file and modify following
-        parameters and recompile:
+        Note: Although configuration is now accepted, it is not internally used
+        by they server yet.  At this stage of development, the only way to alter
+        server configuration is to modify its source code. To do so, please edit
+        src/bin/dhcp6/dhcp4_srv.cc file, modify the following parameters and
+        recompile:
         <screen>
 const std::string HARDCODED_LEASE = "192.0.2.222"; // assigned lease
 const std::string HARDCODED_NETMASK = "255.255.255.0";
@@ -3416,7 +3501,7 @@ const std::string HARDCODED_DNS_SERVER = "192.0.2.2";
 const std::string HARDCODED_DOMAIN_NAME = "isc.example.com";
 const std::string HARDCODED_SERVER_ID = "192.0.2.1";</screen>
 
-        Lease database and configuration support is planned for 2012.
+        Lease database and configuration support is planned for end of 2012.
       </para>
     </section>
 
diff --git a/src/bin/auth/main.cc b/src/bin/auth/main.cc
index 1fe0f48..e90d199 100644
--- a/src/bin/auth/main.cc
+++ b/src/bin/auth/main.cc
@@ -147,7 +147,7 @@ main(int argc, char* argv[]) {
     // Initialize logging.  If verbose, we'll use maximum verbosity.
     isc::log::initLogger(AUTH_NAME,
                          (verbose ? isc::log::DEBUG : isc::log::INFO),
-                         isc::log::MAX_DEBUG_LEVEL, NULL);
+                         isc::log::MAX_DEBUG_LEVEL, NULL, true);
 
     int ret = 0;
 
@@ -256,7 +256,9 @@ main(int argc, char* argv[]) {
 
     // If we haven't registered callback for data sources, this will be just
     // no-op.
-    config_session->removeRemoteConfig("data_sources");
+    if (config_session != NULL) {
+        config_session->removeRemoteConfig("data_sources");
+    }
 
     delete xfrin_session;
     delete config_session;
diff --git a/src/bin/auth/tests/auth_srv_unittest.cc b/src/bin/auth/tests/auth_srv_unittest.cc
index 8015043..7f89fda 100644
--- a/src/bin/auth/tests/auth_srv_unittest.cc
+++ b/src/bin/auth/tests/auth_srv_unittest.cc
@@ -225,7 +225,7 @@ createBuiltinVersionResponse(const qid_t qid, vector<uint8_t>& data) {
     message.setHeaderFlag(Message::HEADERFLAG_AA);
     RRsetPtr rrset_version = RRsetPtr(new RRset(version_name, RRClass::CH(),
                                                 RRType::TXT(), RRTTL(0)));
-    rrset_version->addRdata(generic::TXT(PACKAGE_STRING));
+    rrset_version->addRdata(generic::TXT("\"" PACKAGE_STRING "\""));
     message.addRRset(Message::SECTION_ANSWER, rrset_version);
 
     RRsetPtr rrset_version_ns = RRsetPtr(new RRset(apex_name, RRClass::CH(),
diff --git a/src/bin/bind10/bind10_src.py.in b/src/bin/bind10/bind10_src.py.in
index 36ad760..882653b 100755
--- a/src/bin/bind10/bind10_src.py.in
+++ b/src/bin/bind10/bind10_src.py.in
@@ -48,7 +48,7 @@ else:
     PREFIX = "@prefix@"
     DATAROOTDIR = "@datarootdir@"
     SPECFILE_LOCATION = "@datadir@/@PACKAGE@/bob.spec".replace("${datarootdir}", DATAROOTDIR).replace("${prefix}", PREFIX)
-    
+
 import subprocess
 import signal
 import re
@@ -76,7 +76,7 @@ import isc.bind10.socket_cache
 import libutil_io_python
 import tempfile
 
-isc.log.init("b10-boss")
+isc.log.init("b10-boss", buffer=True)
 logger = isc.log.Logger("boss")
 
 # Pending system-wide debug level definitions, the ones we
@@ -166,14 +166,14 @@ class ProcessStartError(Exception): pass
 
 class BoB:
     """Boss of BIND class."""
-    
+
     def __init__(self, msgq_socket_file=None, data_path=None,
                  config_filename=None, clear_config=False,
                  verbose=False, nokill=False, setuid=None, setgid=None,
                  username=None, cmdctl_port=None, wait_time=10):
         """
             Initialize the Boss of BIND. This is a singleton (only one can run).
-        
+
             The msgq_socket_file specifies the UNIX domain socket file that the
             msgq process listens on.  If verbose is True, then the boss reports
             what it is doing.
@@ -216,6 +216,12 @@ class BoB:
         self.clear_config = clear_config
         self.cmdctl_port = cmdctl_port
         self.wait_time = wait_time
+        self.msgq_timeout = 5
+
+        # _run_under_unittests is only meant to be used when testing. It
+        # bypasses execution of some code to help with testing.
+        self._run_under_unittests = False
+
         self._component_configurator = isc.bind10.component.Configurator(self,
             isc.bind10.special_component.get_specials())
         # The priorities here make them start in the correct order. First
@@ -332,6 +338,7 @@ class BoB:
         """
         logger.info(BIND10_KILLING_ALL_PROCESSES)
         self.__kill_children(True)
+        self.components = {}
 
     def _read_bind10_config(self):
         """
@@ -400,7 +407,7 @@ class BoB:
                     logger.error(BIND10_STARTUP_UNEXPECTED_MESSAGE, msg)
             except:
                 logger.error(BIND10_STARTUP_UNRECOGNISED_MESSAGE, msg)
-        
+
         return False
 
     # The next few methods start the individual processes of BIND-10.  They
@@ -408,21 +415,34 @@ class BoB:
     # raised which is caught by the caller of start_all_processes(); this kills
     # processes started up to that point before terminating the program.
 
+    def _make_process_info(self, name, args, env,
+                           dev_null_stdout=False, dev_null_stderr=False):
+        """
+            Wrapper around ProcessInfo(), useful to override
+            ProcessInfo() creation during testing.
+        """
+        return ProcessInfo(name, args, env, dev_null_stdout, dev_null_stderr)
+
     def start_msgq(self):
         """
             Start the message queue and connect to the command channel.
         """
         self.log_starting("b10-msgq")
-        msgq_proc = ProcessInfo("b10-msgq", ["b10-msgq"], self.c_channel_env,
-                                True, not self.verbose)
+        msgq_proc = self._make_process_info("b10-msgq", ["b10-msgq"],
+                                            self.c_channel_env,
+                                            True, not self.verbose)
         msgq_proc.spawn()
         self.log_started(msgq_proc.pid)
 
         # Now connect to the c-channel
         cc_connect_start = time.time()
         while self.cc_session is None:
+            # if we are run under unittests, break
+            if self._run_under_unittests:
+                break
+
             # if we have been trying for "a while" give up
-            if (time.time() - cc_connect_start) > 5:
+            if (time.time() - cc_connect_start) > self.msgq_timeout:
                 logger.error(BIND10_CONNECTING_TO_CC_FAIL)
                 raise CChannelConnectError("Unable to connect to c-channel after 5 seconds")
 
@@ -434,7 +454,8 @@ class BoB:
 
         # Subscribe to the message queue.  The only messages we expect to receive
         # on this channel are once relating to process startup.
-        self.cc_session.group_subscribe("Boss")
+        if self.cc_session is not None:
+            self.cc_session.group_subscribe("Boss")
 
         return msgq_proc
 
@@ -450,13 +471,14 @@ class BoB:
             args.append("--config-filename=" + self.config_filename)
         if self.clear_config:
             args.append("--clear-config")
-        bind_cfgd = ProcessInfo("b10-cfgmgr", args,
-                                self.c_channel_env)
+        bind_cfgd = self._make_process_info("b10-cfgmgr", args,
+                                            self.c_channel_env)
         bind_cfgd.spawn()
         self.log_started(bind_cfgd.pid)
 
-        # Wait for the configuration manager to start up as subsequent initialization
-        # cannot proceed without it.  The time to wait can be set on the command line.
+        # Wait for the configuration manager to start up as
+        # subsequent initialization cannot proceed without it.  The
+        # time to wait can be set on the command line.
         time_remaining = self.wait_time
         msg, env = self.cc_session.group_recvmsg()
         while time_remaining > 0 and not self.process_running(msg, "ConfigManager"):
@@ -464,7 +486,7 @@ class BoB:
             time.sleep(1)
             time_remaining = time_remaining - 1
             msg, env = self.cc_session.group_recvmsg()
-        
+
         if not self.process_running(msg, "ConfigManager"):
             raise ProcessStartError("Configuration manager process has not started")
 
@@ -481,7 +503,7 @@ class BoB:
             process, the log_starting/log_started methods are not used.
         """
         logger.info(BIND10_STARTING_CC)
-        self.ccs = isc.config.ModuleCCSession(SPECFILE_LOCATION, 
+        self.ccs = isc.config.ModuleCCSession(SPECFILE_LOCATION,
                                       self.config_handler,
                                       self.command_handler,
                                       socket_file = self.msgq_socket_file)
@@ -499,7 +521,7 @@ class BoB:
             The port and address arguments are for log messages only.
         """
         self.log_starting(name, port, address)
-        newproc = ProcessInfo(name, args, c_channel_env)
+        newproc = self._make_process_info(name, args, c_channel_env)
         newproc.spawn()
         self.log_started(newproc.pid)
         return newproc
@@ -611,7 +633,6 @@ class BoB:
         if self.msgq_socket_file is not None:
              c_channel_env["BIND10_MSGQ_SOCKET_FILE"] = self.msgq_socket_file
         logger.debug(DBG_PROCESS, BIND10_CHECK_MSGQ_ALREADY_RUNNING)
-        # try to connect, and if we can't wait a short while
         try:
             self.cc_session = isc.cc.Session(self.msgq_socket_file)
             logger.fatal(BIND10_MSGQ_ALREADY_RUNNING)
@@ -679,7 +700,7 @@ class BoB:
         except:
             pass
         # XXX: some delay probably useful... how much is uncertain
-        # I have changed the delay from 0.5 to 1, but sometime it's 
+        # I have changed the delay from 0.5 to 1, but sometime it's
         # still not enough.
         time.sleep(1)
         self.reap_children()
@@ -728,17 +749,19 @@ class BoB:
         return os.waitpid(-1, os.WNOHANG)
 
     def reap_children(self):
-        """Check to see if any of our child processes have exited, 
-        and note this for later handling. 
+        """Check to see if any of our child processes have exited,
+        and note this for later handling.
         """
         while True:
             try:
                 (pid, exit_status) = self._get_process_exit_status()
             except OSError as o:
-                if o.errno == errno.ECHILD: break
+                if o.errno == errno.ECHILD:
+                    break
                 # XXX: should be impossible to get any other error here
                 raise
-            if pid == 0: break
+            if pid == 0:
+                break
             if pid in self.components:
                 # One of the components we know about.  Get information on it.
                 component = self.components.pop(pid)
@@ -760,11 +783,11 @@ class BoB:
         """
             Restart any dead processes:
 
-            * Returns the time when the next process is ready to be restarted. 
+            * Returns the time when the next process is ready to be restarted.
             * If the server is shutting down, returns 0.
             * If there are no processes, returns None.
 
-            The values returned can be safely passed into select() as the 
+            The values returned can be safely passed into select() as the
             timeout value.
 
         """
@@ -909,8 +932,10 @@ class BoB:
         """
         if self._srv_socket is not None:
             self._srv_socket.close()
-            os.remove(self._socket_path)
-            os.rmdir(self._tmpdir)
+            if os.path.exists(self._socket_path):
+                os.remove(self._socket_path)
+            if os.path.isdir(self._tmpdir):
+                os.rmdir(self._tmpdir)
 
     def _srv_accept(self):
         """
@@ -1006,7 +1031,7 @@ boss_of_bind = None
 
 def reaper(signal_number, stack_frame):
     """A child process has died (SIGCHLD received)."""
-    # don't do anything... 
+    # don't do anything...
     # the Python signal handler has been set up to write
     # down a pipe, waking up our select() bit
     pass
@@ -1173,7 +1198,7 @@ and the created lock file must be writable for that user.
         except KeyError:
             pass
 
-        # Next try getting information about the user, assuming user name 
+        # Next try getting information about the user, assuming user name
         # passed.
         # If the information is both a valid user name and user number, we
         # prefer the name because we try it second. A minor point, hopefully.
diff --git a/src/bin/bind10/tests/bind10_test.py.in b/src/bin/bind10/tests/bind10_test.py.in
index ece6370..409790c 100644
--- a/src/bin/bind10/tests/bind10_test.py.in
+++ b/src/bin/bind10/tests/bind10_test.py.in
@@ -25,6 +25,7 @@ import bind10_src
 import unittest
 import sys
 import os
+import os.path
 import copy
 import signal
 import socket
@@ -34,6 +35,7 @@ import isc
 import isc.log
 import isc.bind10.socket_cache
 import errno
+import random
 
 from isc.testutils.parse_args import TestOptParser, OptsError
 from isc.testutils.ccsession_mock import MockModuleCCSession
@@ -366,6 +368,53 @@ class TestBoB(unittest.TestCase):
         self.assertEqual(creator, bob._socket_cache._creator)
         self.assertRaises(ValueError, bob.set_creator, creator)
 
+    def test_socket_srv(self):
+        """Tests init_socket_srv() and remove_socket_srv() work as expected."""
+        bob = BoB()
+
+        self.assertIsNone(bob._srv_socket)
+        self.assertIsNone(bob._tmpdir)
+        self.assertIsNone(bob._socket_path)
+
+        bob.init_socket_srv()
+
+        self.assertIsNotNone(bob._srv_socket)
+        self.assertNotEqual(-1, bob._srv_socket.fileno())
+        self.assertEqual(os.path.join(bob._tmpdir, 'sockcreator'),
+                         bob._srv_socket.getsockname())
+
+        self.assertIsNotNone(bob._tmpdir)
+        self.assertTrue(os.path.isdir(bob._tmpdir))
+        self.assertIsNotNone(bob._socket_path)
+        self.assertTrue(os.path.exists(bob._socket_path))
+
+        # Check that it's possible to connect to the socket file (this
+        # only works if the socket file exists and the server listens on
+        # it).
+        s = socket.socket(socket.AF_UNIX)
+        try:
+            s.connect(bob._socket_path)
+            can_connect = True
+            s.close()
+        except socket.error as e:
+            can_connect = False
+
+        self.assertTrue(can_connect)
+
+        bob.remove_socket_srv()
+
+        self.assertEqual(-1, bob._srv_socket.fileno())
+        self.assertFalse(os.path.exists(bob._socket_path))
+        self.assertFalse(os.path.isdir(bob._tmpdir))
+
+        # These should not fail either:
+
+        # second call
+        bob.remove_socket_srv()
+
+        bob._srv_socket = None
+        bob.remove_socket_srv()
+
     def test_init_alternate_socket(self):
         bob = BoB("alt_socket_file")
         self.assertEqual(bob.verbose, False)
@@ -461,6 +510,22 @@ class TestBoB(unittest.TestCase):
         self.assertEqual({'command': ['shutdown', {'pid': 42}]},
                          bob.cc_session.msg)
 
+# Mock class for testing BoB's usage of ProcessInfo
+class MockProcessInfo:
+    def __init__(self, name, args, env={}, dev_null_stdout=False,
+                 dev_null_stderr=False):
+        self.name = name
+        self.args = args
+        self.env = env
+        self.dev_null_stdout = dev_null_stdout
+        self.dev_null_stderr = dev_null_stderr
+        self.process = None
+        self.pid = None
+
+    def spawn(self):
+        # set some pid (only used for testing that it is not None anymore)
+        self.pid = 42147
+
 # Class for testing the BoB without actually starting processes.
 # This is used for testing the start/stop components routines and
 # the BoB commands.
@@ -490,6 +555,7 @@ class MockBob(BoB):
         self.c_channel_env = {}
         self.components = { }
         self.creator = False
+        self.get_process_exit_status_called = False
 
         class MockSockCreator(isc.bind10.component.Component):
             def __init__(self, process, boss, kind, address=None, params=None):
@@ -661,6 +727,52 @@ class MockBob(BoB):
             del self.components[12]
         self.cmdctl = False
 
+    def _get_process_exit_status(self):
+        if self.get_process_exit_status_called:
+            return (0, 0)
+        self.get_process_exit_status_called = True
+        return (53, 0)
+
+    def _get_process_exit_status_unknown_pid(self):
+        if self.get_process_exit_status_called:
+            return (0, 0)
+        self.get_process_exit_status_called = True
+        return (42, 0)
+
+    def _get_process_exit_status_raises_oserror_echild(self):
+        raise OSError(errno.ECHILD, 'Mock error')
+
+    def _get_process_exit_status_raises_oserror_other(self):
+        raise OSError(0, 'Mock error')
+
+    def _get_process_exit_status_raises_other(self):
+        raise Exception('Mock error')
+
+    def _make_mock_process_info(self, name, args, c_channel_env,
+                                dev_null_stdout=False, dev_null_stderr=False):
+        return MockProcessInfo(name, args, c_channel_env,
+                               dev_null_stdout, dev_null_stderr)
+
+class MockBobSimple(BoB):
+    def __init__(self):
+        BoB.__init__(self)
+        # Set which process has been started
+        self.started_process_name = None
+        self.started_process_args = None
+        self.started_process_env = None
+
+    def _make_mock_process_info(self, name, args, c_channel_env,
+                                dev_null_stdout=False, dev_null_stderr=False):
+        return MockProcessInfo(name, args, c_channel_env,
+                               dev_null_stdout, dev_null_stderr)
+
+    def start_process(self, name, args, c_channel_env, port=None,
+                      address=None):
+        self.started_process_name = name
+        self.started_process_args = args
+        self.started_process_env = c_channel_env
+        return None
+
 class TestStartStopProcessesBob(unittest.TestCase):
     """
     Check that the start_all_components method starts the right combination
@@ -930,6 +1042,9 @@ class MockComponent:
         self.pid = lambda: pid
         self.address = lambda: address
         self.restarted = False
+        self.forceful = False
+        self.running = True
+        self.has_failed = False
 
     def get_restart_time(self):
         return 0                # arbitrary dummy value
@@ -938,6 +1053,15 @@ class MockComponent:
         self.restarted = True
         return True
 
+    def is_running(self):
+        return self.running
+
+    def failed(self, status):
+        return self.has_failed
+
+    def kill(self, forceful):
+        self.forceful = forceful
+
 class TestBossCmd(unittest.TestCase):
     def test_ping(self):
         """
@@ -1107,6 +1231,20 @@ class TestBossComponents(unittest.TestCase):
                 'process': 'cat'
             }
         }
+        self._tmp_time = None
+        self._tmp_sleep = None
+        self._tmp_module_cc_session = None
+        self._tmp_cc_session = None
+
+    def tearDown(self):
+        if self._tmp_time is not None:
+            time.time = self._tmp_time
+        if self._tmp_sleep is not None:
+            time.sleep = self._tmp_sleep
+        if self._tmp_module_cc_session is not None:
+            isc.config.ModuleCCSession = self._tmp_module_cc_session
+        if self._tmp_cc_session is not None:
+            isc.cc.Session = self._tmp_cc_session
 
     def __unary_hook(self, param):
         """
@@ -1324,14 +1462,618 @@ class TestBossComponents(unittest.TestCase):
         bob._component_configurator._components['test'] = (None, component)
         self.__setup_restart(bob, component)
         self.assertTrue(component.restarted)
-        self.assertFalse(component in bob.components_to_restart)
+        self.assertNotIn(component, bob.components_to_restart)
 
         # Remove the component from the configuration.  It won't be restarted
         # even if scheduled, nor will remain in the to-be-restarted list.
         del bob._component_configurator._components['test']
         self.__setup_restart(bob, component)
         self.assertFalse(component.restarted)
-        self.assertFalse(component in bob.components_to_restart)
+        self.assertNotIn(component, bob.components_to_restart)
+
+    def test_get_processes(self):
+        '''Test that procsses are returned correctly, sorted by pid.'''
+        bob = MockBob()
+
+        pids = list(range(0, 20))
+        random.shuffle(pids)
+
+        for i in range(0, 20):
+            pid = pids[i]
+            component = MockComponent('test' + str(pid), pid,
+                                      'Test' + str(pid))
+            bob.components[pid] = component
+
+        process_list = bob.get_processes()
+        self.assertEqual(20, len(process_list))
+
+        last_pid = -1
+        for process in process_list:
+            pid = process[0]
+            self.assertLessEqual(last_pid, pid)
+            last_pid = pid
+            self.assertEqual([pid, 'test' + str(pid), 'Test' + str(pid)],
+                             process)
+
+    def _test_reap_children_helper(self, runnable, is_running, failed):
+        '''Construct a BoB instance, set various data in it according to
+        passed args and check if the component was added to the list of
+        components to restart.'''
+        bob = MockBob()
+        bob.runnable = runnable
+
+        component = MockComponent('test', 53)
+        component.running = is_running
+        component.has_failed = failed
+        bob.components[53] = component
+
+        self.assertNotIn(component, bob.components_to_restart)
+
+        bob.reap_children()
+
+        if runnable and is_running and not failed:
+            self.assertIn(component, bob.components_to_restart)
+        else:
+            self.assertEqual([], bob.components_to_restart)
+
+    def test_reap_children(self):
+        '''Test that children are queued to be restarted when they ask for it.'''
+        # test various combinations of 3 booleans
+        # (BoB.runnable, component.is_running(), component.failed())
+        self._test_reap_children_helper(False, False, False)
+        self._test_reap_children_helper(False, False, True)
+        self._test_reap_children_helper(False, True,  False)
+        self._test_reap_children_helper(False, True,  True)
+        self._test_reap_children_helper(True,  False, False)
+        self._test_reap_children_helper(True,  False, True)
+        self._test_reap_children_helper(True,  True,  False)
+        self._test_reap_children_helper(True,  True,  True)
+
+        # setup for more tests below
+        bob = MockBob()
+        bob.runnable = True
+        component = MockComponent('test', 53)
+        bob.components[53] = component
+
+        # case where the returned pid is unknown to us. nothing should
+        # happpen then.
+        bob.get_process_exit_status_called = False
+        bob._get_process_exit_status = bob._get_process_exit_status_unknown_pid
+        bob.components_to_restart = []
+        # this should do nothing as the pid is unknown
+        bob.reap_children()
+        self.assertEqual([], bob.components_to_restart)
+
+        # case where bob._get_process_exit_status() raises OSError with
+        # errno.ECHILD
+        bob._get_process_exit_status = \
+            bob._get_process_exit_status_raises_oserror_echild
+        bob.components_to_restart = []
+        # this should catch and handle the OSError
+        bob.reap_children()
+        self.assertEqual([], bob.components_to_restart)
+
+        # case where bob._get_process_exit_status() raises OSError with
+        # errno other than ECHILD
+        bob._get_process_exit_status = \
+            bob._get_process_exit_status_raises_oserror_other
+        with self.assertRaises(OSError):
+            bob.reap_children()
+
+        # case where bob._get_process_exit_status() raises something
+        # other than OSError
+        bob._get_process_exit_status = \
+            bob._get_process_exit_status_raises_other
+        with self.assertRaises(Exception):
+            bob.reap_children()
+
+    def test_kill_started_components(self):
+        '''Test that started components are killed.'''
+        bob = MockBob()
+
+        component = MockComponent('test', 53, 'Test')
+        bob.components[53] = component
+
+        self.assertEqual([[53, 'test', 'Test']], bob.get_processes())
+        bob.kill_started_components()
+        self.assertEqual([], bob.get_processes())
+        self.assertTrue(component.forceful)
+
+    def _start_msgq_helper(self, bob, verbose):
+        bob.verbose = verbose
+        pi = bob.start_msgq()
+        self.assertEqual('b10-msgq', pi.name)
+        self.assertEqual(['b10-msgq'], pi.args)
+        self.assertTrue(pi.dev_null_stdout)
+        self.assertEqual(pi.dev_null_stderr, not verbose)
+        self.assertEqual({'FOO': 'an env string'}, pi.env)
+
+        # this is set by ProcessInfo.spawn()
+        self.assertEqual(42147, pi.pid)
+
+    def test_start_msgq(self):
+        '''Test that b10-msgq is started.'''
+        bob = MockBobSimple()
+        bob.c_channel_env = {'FOO': 'an env string'}
+        bob._run_under_unittests = True
+
+        # use the MockProcessInfo creator
+        bob._make_process_info = bob._make_mock_process_info
+
+        # non-verbose case
+        self._start_msgq_helper(bob, False)
+
+        # verbose case
+        self._start_msgq_helper(bob, True)
+
+    def test_start_msgq_timeout(self):
+        '''Test that b10-msgq startup attempts connections several times
+        and times out eventually.'''
+        bob = MockBobSimple()
+        bob.c_channel_env = {}
+        # set the timeout to an arbitrary pre-determined value (which
+        # code below depends on)
+        bob.msgq_timeout = 1
+        bob._run_under_unittests = False
+
+        # use the MockProcessInfo creator
+        bob._make_process_info = bob._make_mock_process_info
+
+        global attempts
+        global tsec
+        attempts = 0
+        tsec = 0
+        self._tmp_time = time.time
+        self._tmp_sleep = time.sleep
+        def _my_time():
+            global attempts
+            global tsec
+            attempts += 1
+            return tsec
+        def _my_sleep(nsec):
+            global tsec
+            tsec += nsec
+        time.time = _my_time
+        time.sleep = _my_sleep
+
+        global cc_sub
+        cc_sub = None
+        class DummySessionAlwaysFails():
+            def __init__(self, socket_file):
+                raise isc.cc.session.SessionError('Connection fails')
+            def group_subscribe(self, s):
+                global cc_sub
+                cc_sub = s
+
+        isc.cc.Session = DummySessionAlwaysFails
+
+        with self.assertRaises(bind10_src.CChannelConnectError):
+            # An exception will be thrown here when it eventually times
+            # out.
+            pi = bob.start_msgq()
+
+        # time.time() should be called 12 times within the while loop:
+        # starting from 0, and 11 more times from 0.1 to 1.1. There's
+        # another call to time.time() outside the loop, which makes it
+        # 13.
+        self.assertEqual(attempts, 13)
+
+        # group_subscribe() should not have been called here.
+        self.assertIsNone(cc_sub)
+
+        global cc_socket_file
+        cc_socket_file = None
+        cc_sub = None
+        class DummySession():
+            def __init__(self, socket_file):
+                global cc_socket_file
+                cc_socket_file = socket_file
+            def group_subscribe(self, s):
+                global cc_sub
+                cc_sub = s
+
+        isc.cc.Session = DummySession
+
+        # reset values
+        attempts = 0
+        tsec = 0
+
+        pi = bob.start_msgq()
+
+        # just one attempt, but 2 calls to time.time()
+        self.assertEqual(attempts, 2)
+
+        self.assertEqual(cc_socket_file, bob.msgq_socket_file)
+        self.assertEqual(cc_sub, 'Boss')
+
+        # isc.cc.Session, time.time() and time.sleep() are restored
+        # during tearDown().
+
+    def _start_cfgmgr_helper(self, bob, data_path, filename, clear_config):
+        expect_args = ['b10-cfgmgr']
+        if data_path is not None:
+            bob.data_path = data_path
+            expect_args.append('--data-path=' + data_path)
+        if filename is not None:
+            bob.config_filename = filename
+            expect_args.append('--config-filename=' + filename)
+        if clear_config:
+            bob.clear_config = clear_config
+            expect_args.append('--clear-config')
+
+        pi = bob.start_cfgmgr()
+        self.assertEqual('b10-cfgmgr', pi.name)
+        self.assertEqual(expect_args, pi.args)
+        self.assertEqual({'TESTENV': 'A test string'}, pi.env)
+
+        # this is set by ProcessInfo.spawn()
+        self.assertEqual(42147, pi.pid)
+
+    def test_start_cfgmgr(self):
+        '''Test that b10-cfgmgr is started.'''
+        class DummySession():
+            def __init__(self):
+                self._tries = 0
+            def group_recvmsg(self):
+                self._tries += 1
+                # return running on the 3rd try onwards
+                if self._tries >= 3:
+                    return ({'running': 'ConfigManager'}, None)
+                else:
+                    return ({}, None)
+
+        bob = MockBobSimple()
+        bob.c_channel_env = {'TESTENV': 'A test string'}
+        bob.cc_session = DummySession()
+        bob.wait_time = 5
+
+        # use the MockProcessInfo creator
+        bob._make_process_info = bob._make_mock_process_info
+
+        global attempts
+        attempts = 0
+        self._tmp_sleep = time.sleep
+        def _my_sleep(nsec):
+            global attempts
+            attempts += 1
+        time.sleep = _my_sleep
+
+        # defaults
+        self._start_cfgmgr_helper(bob, None, None, False)
+
+        # check that 2 attempts were made. on the 3rd attempt,
+        # process_running() returns that ConfigManager is running.
+        self.assertEqual(attempts, 2)
+
+        # data_path is specified
+        self._start_cfgmgr_helper(bob, '/var/lib/test', None, False)
+
+        # config_filename is specified. Because `bob` is not
+        # reconstructed, data_path is retained from the last call to
+        # _start_cfgmgr_helper().
+        self._start_cfgmgr_helper(bob, '/var/lib/test', 'foo.cfg', False)
+
+        # clear_config is specified. Because `bob` is not reconstructed,
+        # data_path and config_filename are retained from the last call
+        # to _start_cfgmgr_helper().
+        self._start_cfgmgr_helper(bob, '/var/lib/test', 'foo.cfg', True)
+
+    def test_start_cfgmgr_timeout(self):
+        '''Test that b10-cfgmgr startup attempts connections several times
+        and times out eventually.'''
+        class DummySession():
+            def group_recvmsg(self):
+                return (None, None)
+        bob = MockBobSimple()
+        bob.c_channel_env = {}
+        bob.cc_session = DummySession()
+        # set wait_time to an arbitrary pre-determined value (which code
+        # below depends on)
+        bob.wait_time = 2
+
+        # use the MockProcessInfo creator
+        bob._make_process_info = bob._make_mock_process_info
+
+        global attempts
+        attempts = 0
+        self._tmp_sleep = time.sleep
+        def _my_sleep(nsec):
+            global attempts
+            attempts += 1
+        time.sleep = _my_sleep
+
+        # We just check that an exception was thrown, and that several
+        # attempts were made to connect.
+        with self.assertRaises(bind10_src.ProcessStartError):
+            pi = bob.start_cfgmgr()
+
+        # 2 seconds of attempts every 1 second should result in 2 attempts
+        self.assertEqual(attempts, 2)
+
+        # time.sleep() is restored during tearDown().
+
+    def test_start_ccsession(self):
+        '''Test that CC session is started.'''
+        class DummySession():
+            def __init__(self, specfile, config_handler, command_handler,
+                         socket_file):
+                self.specfile = specfile
+                self.config_handler = config_handler
+                self.command_handler = command_handler
+                self.socket_file = socket_file
+                self.started = False
+            def start(self):
+                self.started = True
+        bob = MockBobSimple()
+        self._tmp_module_cc_session = isc.config.ModuleCCSession
+        isc.config.ModuleCCSession = DummySession
+
+        bob.start_ccsession({})
+        self.assertEqual(bind10_src.SPECFILE_LOCATION, bob.ccs.specfile)
+        self.assertEqual(bob.config_handler, bob.ccs.config_handler)
+        self.assertEqual(bob.command_handler, bob.ccs.command_handler)
+        self.assertEqual(bob.msgq_socket_file, bob.ccs.socket_file)
+        self.assertTrue(bob.ccs.started)
+
+        # isc.config.ModuleCCSession is restored during tearDown().
+
+    def test_start_process(self):
+        '''Test that processes can be started.'''
+        bob = MockBob()
+
+        # use the MockProcessInfo creator
+        bob._make_process_info = bob._make_mock_process_info
+
+        pi = bob.start_process('Test Process', ['/bin/true'], {})
+        self.assertEqual('Test Process', pi.name)
+        self.assertEqual(['/bin/true'], pi.args)
+        self.assertEqual({}, pi.env)
+
+        # this is set by ProcessInfo.spawn()
+        self.assertEqual(42147, pi.pid)
+
+    def test_register_process(self):
+        '''Test that processes can be registered with BoB.'''
+        bob = MockBob()
+        component = MockComponent('test', 53, 'Test')
+
+        self.assertFalse(53 in bob.components)
+        bob.register_process(53, component)
+        self.assertTrue(53 in bob.components)
+        self.assertEqual(bob.components[53].name(), 'test')
+        self.assertEqual(bob.components[53].pid(), 53)
+        self.assertEqual(bob.components[53].address(), 'Test')
+
+    def _start_simple_helper(self, bob, verbose):
+        bob.verbose = verbose
+
+        args = ['/bin/true']
+        if verbose:
+            args.append('-v')
+
+        bob.start_simple('/bin/true')
+        self.assertEqual('/bin/true', bob.started_process_name)
+        self.assertEqual(args, bob.started_process_args)
+        self.assertEqual({'TESTENV': 'A test string'}, bob.started_process_env)
+
+    def test_start_simple(self):
+        '''Test simple process startup.'''
+        bob = MockBobSimple()
+        bob.c_channel_env = {'TESTENV': 'A test string'}
+
+        # non-verbose case
+        self._start_simple_helper(bob, False)
+
+        # verbose case
+        self._start_simple_helper(bob, True)
+
+    def _start_auth_helper(self, bob, verbose):
+        bob.verbose = verbose
+
+        args = ['b10-auth']
+        if verbose:
+            args.append('-v')
+
+        bob.start_auth()
+        self.assertEqual('b10-auth', bob.started_process_name)
+        self.assertEqual(args, bob.started_process_args)
+        self.assertEqual({'FOO': 'an env string'}, bob.started_process_env)
+
+    def test_start_auth(self):
+        '''Test that b10-auth is started.'''
+        bob = MockBobSimple()
+        bob.c_channel_env = {'FOO': 'an env string'}
+
+        # non-verbose case
+        self._start_auth_helper(bob, False)
+
+        # verbose case
+        self._start_auth_helper(bob, True)
+
+    def _start_resolver_helper(self, bob, verbose):
+        bob.verbose = verbose
+
+        args = ['b10-resolver']
+        if verbose:
+            args.append('-v')
+
+        bob.start_resolver()
+        self.assertEqual('b10-resolver', bob.started_process_name)
+        self.assertEqual(args, bob.started_process_args)
+        self.assertEqual({'BAR': 'an env string'}, bob.started_process_env)
+
+    def test_start_resolver(self):
+        '''Test that b10-resolver is started.'''
+        bob = MockBobSimple()
+        bob.c_channel_env = {'BAR': 'an env string'}
+
+        # non-verbose case
+        self._start_resolver_helper(bob, False)
+
+        # verbose case
+        self._start_resolver_helper(bob, True)
+
+    def _start_cmdctl_helper(self, bob, verbose, port = None):
+        bob.verbose = verbose
+
+        args = ['b10-cmdctl']
+
+        if port is not None:
+            bob.cmdctl_port = port
+            args.append('--port=9353')
+
+        if verbose:
+            args.append('-v')
+
+        bob.start_cmdctl()
+        self.assertEqual('b10-cmdctl', bob.started_process_name)
+        self.assertEqual(args, bob.started_process_args)
+        self.assertEqual({'BAZ': 'an env string'}, bob.started_process_env)
+
+    def test_start_cmdctl(self):
+        '''Test that b10-cmdctl is started.'''
+        bob = MockBobSimple()
+        bob.c_channel_env = {'BAZ': 'an env string'}
+
+        # non-verbose case
+        self._start_cmdctl_helper(bob, False)
+
+        # verbose case
+        self._start_cmdctl_helper(bob, True)
+
+        # with port, non-verbose case
+        self._start_cmdctl_helper(bob, False, 9353)
+
+        # with port, verbose case
+        self._start_cmdctl_helper(bob, True, 9353)
+
+    def test_socket_data(self):
+        '''Test that BoB._socket_data works as expected.'''
+        class MockSock:
+            def __init__(self, fd, throw):
+                self.fd = fd
+                self.throw = throw
+                self.buf = b'Hello World.\nYou are so nice today.\nXX'
+                self.i = 0
+
+            def recv(self, bufsize, flags = 0):
+                if bufsize != 1:
+                    raise Exception('bufsize != 1')
+                if flags != socket.MSG_DONTWAIT:
+                    raise Exception('flags != socket.MSG_DONTWAIT')
+                # after 15 recv()s, throw a socket.error with EAGAIN to
+                # get _socket_data() to save back what's been read. The
+                # number 15 is arbitrarily chosen, but the checks then
+                # depend on this being 15, i.e., if you adjust this
+                # number, you may have to adjust the checks below too.
+                if self.throw and self.i > 15:
+                    raise socket.error(errno.EAGAIN, 'Try again')
+                if self.i >= len(self.buf):
+                    return b'';
+                t = self.i
+                self.i += 1
+                return self.buf[t:t+1]
+
+            def close(self):
+                return
+
+        class MockBobSocketData(BoB):
+            def __init__(self, throw):
+                self._unix_sockets = {42: (MockSock(42, throw), b'')}
+                self.requests = []
+                self.dead = []
+
+            def socket_request_handler(self, previous, sock):
+                self.requests.append({sock.fd: previous})
+
+            def socket_consumer_dead(self, sock):
+                self.dead.append(sock.fd)
+
+        # Case where we get data every time we call recv()
+        bob = MockBobSocketData(False)
+        bob._socket_data(42)
+        self.assertEqual(bob.requests,
+                         [{42: b'Hello World.'},
+                          {42: b'You are so nice today.'}])
+        self.assertEqual(bob.dead, [42])
+        self.assertEqual({}, bob._unix_sockets)
+
+        # Case where socket.recv() raises EAGAIN. In this case, the
+        # routine is supposed to save what it has back to
+        # BoB._unix_sockets.
+        bob = MockBobSocketData(True)
+        bob._socket_data(42)
+        self.assertEqual(bob.requests, [{42: b'Hello World.'}])
+        self.assertFalse(bob.dead)
+        self.assertEqual(len(bob._unix_sockets), 1)
+        self.assertEqual(bob._unix_sockets[42][1], b'You')
+
+    def test_startup(self):
+        '''Test that BoB.startup() handles failures properly.'''
+        class MockBobStartup(BoB):
+            def __init__(self, throw):
+                self.throw = throw
+                self.started = False
+                self.killed = False
+                self.msgq_socket_file = None
+                self.curproc = 'myproc'
+                self.runnable = False
+
+            def start_all_components(self):
+                self.started = True
+                if self.throw:
+                    raise Exception('Assume starting components has failed.')
+
+            def kill_started_components(self):
+                self.killed = True
+
+        class DummySession():
+            def __init__(self, socket_file):
+                raise isc.cc.session.SessionError('This is the expected case.')
+
+        class DummySessionSocketExists():
+            def __init__(self, socket_file):
+                # simulate that connect passes
+                return
+
+        isc.cc.Session = DummySession
+
+        # All is well case, where all components are started
+        # successfully. We check that the actual call to
+        # start_all_components() is made, and BoB.runnable is true.
+        bob = MockBobStartup(False)
+        r = bob.startup()
+        self.assertIsNone(r)
+        self.assertTrue(bob.started)
+        self.assertFalse(bob.killed)
+        self.assertTrue(bob.runnable)
+        self.assertEqual({}, bob.c_channel_env)
+
+        # Case where starting components fails. We check that
+        # kill_started_components() is called right after, and
+        # BoB.runnable is not modified.
+        bob = MockBobStartup(True)
+        r = bob.startup()
+        # r contains an error message
+        self.assertEqual(r, 'Unable to start myproc: Assume starting components has failed.')
+        self.assertTrue(bob.started)
+        self.assertTrue(bob.killed)
+        self.assertFalse(bob.runnable)
+        self.assertEqual({}, bob.c_channel_env)
+
+        # Check if msgq_socket_file is carried over
+        bob = MockBobStartup(False)
+        bob.msgq_socket_file = 'foo'
+        r = bob.startup()
+        self.assertEqual({'BIND10_MSGQ_SOCKET_FILE': 'foo'}, bob.c_channel_env)
+
+        # Check the case when socket file already exists
+        isc.cc.Session = DummySessionSocketExists
+        bob = MockBobStartup(False)
+        r = bob.startup()
+        self.assertIn('already running', r)
+
+        # isc.cc.Session is restored during tearDown().
 
 class SocketSrvTest(unittest.TestCase):
     """
@@ -1563,6 +2305,25 @@ class TestFunctions(unittest.TestCase):
         # second call should not assert anyway
         bind10_src.remove_lock_files()
 
+    def test_get_signame(self):
+        # just test with some samples
+        signame = bind10_src.get_signame(signal.SIGTERM)
+        self.assertEqual('SIGTERM', signame)
+        signame = bind10_src.get_signame(signal.SIGKILL)
+        self.assertEqual('SIGKILL', signame)
+        # 59426 is hopefully an unused signal on most platforms
+        signame = bind10_src.get_signame(59426)
+        self.assertEqual('Unknown signal 59426', signame)
+
+    def test_fatal_signal(self):
+        self.assertIsNone(bind10_src.boss_of_bind)
+        bind10_src.boss_of_bind = BoB()
+        bind10_src.boss_of_bind.runnable = True
+        bind10_src.fatal_signal(signal.SIGTERM, None)
+        # Now, runnable must be False
+        self.assertFalse(bind10_src.boss_of_bind.runnable)
+        bind10_src.boss_of_bind = None
+
 if __name__ == '__main__':
     # store os.environ for test_unchanged_environment
     original_os_environ = copy.deepcopy(os.environ)
diff --git a/src/bin/cfgmgr/b10-cfgmgr.py.in b/src/bin/cfgmgr/b10-cfgmgr.py.in
index a3763c9..315e3c5 100755
--- a/src/bin/cfgmgr/b10-cfgmgr.py.in
+++ b/src/bin/cfgmgr/b10-cfgmgr.py.in
@@ -27,7 +27,7 @@ import glob
 import os.path
 import imp
 import isc.log
-isc.log.init("b10-cfgmgr")
+isc.log.init("b10-cfgmgr", buffer=True)
 from isc.config.cfgmgr import ConfigManager, ConfigManagerDataReadError, logger
 from isc.log_messages.cfgmgr_messages import *
 
diff --git a/src/bin/cmdctl/.gitignore b/src/bin/cmdctl/.gitignore
index dd6bbc2..e702e49 100644
--- a/src/bin/cmdctl/.gitignore
+++ b/src/bin/cmdctl/.gitignore
@@ -1,8 +1,10 @@
+/b10-certgen
+/b10-certgen.1
 /b10-cmdctl
+/b10-cmdctl.8
+/cmdctl-certfile.pem
+/cmdctl-keyfile.pem
 /cmdctl.py
 /cmdctl.spec
 /cmdctl.spec.pre
 /run_b10-cmdctl.sh
-/b10-cmdctl.8
-/cmdctl-keyfile.pem
-/cmdctl-certfile.pem
diff --git a/src/bin/cmdctl/cmdctl.py.in b/src/bin/cmdctl/cmdctl.py.in
index 4076f4c..52af54a 100755
--- a/src/bin/cmdctl/cmdctl.py.in
+++ b/src/bin/cmdctl/cmdctl.py.in
@@ -49,7 +49,7 @@ from hashlib import sha1
 from isc.util import socketserver_mixin
 from isc.log_messages.cmdctl_messages import *
 
-isc.log.init("b10-cmdctl")
+isc.log.init("b10-cmdctl", buffer=True)
 logger = isc.log.Logger("cmdctl")
 
 # Debug level for communication with BIND10
diff --git a/src/bin/ddns/ddns.py.in b/src/bin/ddns/ddns.py.in
index eaeb06c..094e0ec 100755
--- a/src/bin/ddns/ddns.py.in
+++ b/src/bin/ddns/ddns.py.in
@@ -45,7 +45,7 @@ import os.path
 import signal
 import socket
 
-isc.log.init("b10-ddns")
+isc.log.init("b10-ddns", buffer=True)
 logger = isc.log.Logger("ddns")
 TRACE_BASIC = logger.DBGLVL_TRACE_BASIC
 
diff --git a/src/bin/dhcp4/Makefile.am b/src/bin/dhcp4/Makefile.am
index e0c97d1..c896591 100644
--- a/src/bin/dhcp4/Makefile.am
+++ b/src/bin/dhcp4/Makefile.am
@@ -44,6 +44,7 @@ pkglibexec_PROGRAMS = b10-dhcp4
 
 b10_dhcp4_SOURCES  = main.cc
 b10_dhcp4_SOURCES += ctrl_dhcp4_srv.cc ctrl_dhcp4_srv.h
+b10_dhcp4_SOURCES += config_parser.cc config_parser.h
 b10_dhcp4_SOURCES += dhcp4_log.cc dhcp4_log.h
 b10_dhcp4_SOURCES += dhcp4_srv.cc dhcp4_srv.h
 
@@ -57,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/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
 b10_dhcp4_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
diff --git a/src/bin/dhcp4/config_parser.cc b/src/bin/dhcp4/config_parser.cc
new file mode 100644
index 0000000..08d16f8
--- /dev/null
+++ b/src/bin/dhcp4/config_parser.cc
@@ -0,0 +1,772 @@
+// 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/ccsession.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcp4/config_parser.h>
+#include <dhcp4/dhcp4_log.h>
+#include <boost/foreach.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/algorithm/string.hpp>
+#include <limits>
+#include <iostream>
+#include <vector>
+#include <map>
+
+using namespace std;
+using namespace isc::data;
+using namespace isc::asiolink;
+
+namespace isc {
+namespace dhcp {
+
+/// @brief auxiliary type used for storing element name and its parser
+typedef pair<string, ConstElementPtr> ConfigPair;
+
+/// @brief a factory method that will create a parser for a given element name
+typedef Dhcp4ConfigParser* ParserFactory(const std::string& config_id);
+
+/// @brief a collection of factories that creates parsers for specified element names
+typedef std::map<std::string, ParserFactory*> FactoryMap;
+
+/// @brief a collection of pools
+///
+/// That type is used as intermediate storage, when pools are parsed, but there is
+/// no subnet object created yet to store them.
+typedef std::vector<Pool4Ptr> PoolStorage;
+
+/// @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 a dummy configuration parser
+///
+/// It is a debugging parser. It does not configure anything,
+/// will accept any configuration and will just print it out
+/// on commit. Useful for debugging existing configurations and
+/// adding new ones.
+class DebugParser : public Dhcp4ConfigParser {
+public:
+
+    /// @brief Constructor
+    ///
+    /// See \ref Dhcp4ConfigParser class for details.
+    ///
+    /// @param param_name name of the parsed parameter
+    DebugParser(const std::string& param_name)
+        :param_name_(param_name) {
+    }
+
+    /// @brief builds parameter value
+    ///
+    /// See \ref Dhcp4ConfigParser class for details.
+    ///
+    /// @param new_config pointer to the new configuration
+    virtual void build(ConstElementPtr new_config) {
+        std::cout << "Build for token: [" << param_name_ << "] = ["
+                  << value_->str() << "]" << std::endl;
+        value_ = new_config;
+    }
+
+    /// @brief pretends to apply the configuration
+    ///
+    /// This is a method required by base class. It pretends to apply the
+    /// configuration, but in fact it only prints the parameter out.
+    ///
+    /// See \ref Dhcp4ConfigParser class for details.
+    virtual void commit() {
+        // Debug message. The whole DebugParser class is used only for parser
+        // debugging, and is not used in production code. It is very convenient
+        // to keep it around. Please do not turn this cout into logger calls.
+        std::cout << "Commit for token: [" << param_name_ << "] = ["
+                  << value_->str() << "]" << std::endl;
+    }
+
+    /// @brief factory that constructs DebugParser objects
+    ///
+    /// @param param_name name of the parameter to be parsed
+    static Dhcp4ConfigParser* Factory(const std::string& param_name) {
+        return (new DebugParser(param_name));
+    }
+
+private:
+    /// name of the parsed parameter
+    std::string param_name_;
+
+    /// pointer to the actual value of the parameter
+    ConstElementPtr value_;
+};
+
+/// @brief Configuration parser for uint32 parameters
+///
+/// This class is a generic parser that is able to handle any uint32 integer
+/// type. By default it stores the value in external global container
+/// (uint32_defaults). If used in smaller scopes (e.g. to parse parameters
+/// in subnet config), it can be pointed to a different storage, using
+/// setStorage() method. This class follows the parser interface, laid out
+/// in its base class, \ref Dhcp4ConfigParser.
+///
+/// For overview of usability of this generic purpose parser, see
+/// \ref dhcpv4ConfigInherit page.
+class Uint32Parser : public Dhcp4ConfigParser {
+public:
+
+    /// @brief constructor for Uint32Parser
+    /// @param param_name name of the configuration parameter being parsed
+    Uint32Parser(const std::string& param_name)
+        :storage_(&uint32_defaults), param_name_(param_name) {
+    }
+
+    /// @brief builds parameter value
+    ///
+    /// Parses configuration entry and stores it in a storage. See
+    /// \ref setStorage() for details.
+    ///
+    /// @param value pointer to the content of parsed values
+    /// @throw BadValue if supplied value could not be base to uint32_t
+    virtual void build(ConstElementPtr value) {
+        int64_t check;
+        string x = value->str();
+        try {
+            check = boost::lexical_cast<int64_t>(x);
+        } catch (const boost::bad_lexical_cast &) {
+            isc_throw(BadValue, "Failed to parse value " << value->str()
+                      << " as unsigned 32-bit integer.");
+        }
+        if (check > std::numeric_limits<uint32_t>::max()) {
+            isc_throw(BadValue, "Value " << value->str() << "is too large"
+                      << " for unsigned 32-bit integer.");
+        }
+        if (check < 0) {
+            isc_throw(BadValue, "Value " << value->str() << "is negative."
+                      << " Only 0 or larger are allowed for unsigned 32-bit integer.");
+        }
+
+        // value is small enough to fit
+        value_ = static_cast<uint32_t>(check);
+
+        (*storage_)[param_name_] = value_;
+    }
+
+    /// @brief does nothing
+    ///
+    /// This method is required for all parsers. The value itself
+    /// is not commited anywhere. Higher level parsers are expected to
+    /// use values stored in the storage, e.g. renew-timer for a given
+    /// subnet is stored in subnet-specific storage. It is not commited
+    /// here, but is rather used by \ref Subnet4ConfigParser when constructing
+    /// the subnet.
+    virtual void commit() {
+    }
+
+    /// @brief factory that constructs Uint32Parser objects
+    ///
+    /// @param param_name name of the parameter to be parsed
+    static Dhcp4ConfigParser* Factory(const std::string& param_name) {
+        return (new Uint32Parser(param_name));
+    }
+
+    /// @brief sets storage for value of this parameter
+    ///
+    /// See \ref dhcpv4ConfigInherit for details.
+    ///
+    /// @param storage pointer to the storage container
+    void setStorage(Uint32Storage* storage) {
+        storage_ = storage;
+    }
+
+private:
+    /// pointer to the storage, where parsed value will be stored
+    Uint32Storage* storage_;
+
+    /// name of the parameter to be parsed
+    std::string param_name_;
+
+    /// the actual parsed value
+    uint32_t value_;
+};
+
+/// @brief Configuration parser for string parameters
+///
+/// This class is a generic parser that is able to handle any string
+/// parameter. By default it stores the value in external global container
+/// (string_defaults). If used in smaller scopes (e.g. to parse parameters
+/// in subnet config), it can be pointed to a different storage, using
+/// setStorage() method. This class follows the parser interface, laid out
+/// in its base class, \ref Dhcp4ConfigParser.
+///
+/// For overview of usability of this generic purpose parser, see
+/// \ref dhcpv4ConfigInherit page.
+class StringParser : public Dhcp4ConfigParser {
+public:
+
+    /// @brief constructor for StringParser
+    /// @param param_name name of the configuration parameter being parsed
+    StringParser(const std::string& param_name)
+        :storage_(&string_defaults), param_name_(param_name) {
+    }
+
+    /// @brief parses parameter value
+    ///
+    /// Parses configuration entry and stores it in storage. See
+    /// \ref setStorage() for details.
+    ///
+    /// @param value pointer to the content of parsed values
+    virtual void build(ConstElementPtr value) {
+        value_ = value->str();
+        boost::erase_all(value_, "\"");
+
+        (*storage_)[param_name_] = value_;
+    }
+
+    /// @brief does nothing
+    ///
+    /// This method is required for all parser. The value itself
+    /// is not commited anywhere. Higher level parsers are expected to
+    /// use values stored in the storage, e.g. renew-timer for a given
+    /// subnet is stored in subnet-specific storage. It is not commited
+    /// here, but is rather used by its parent parser when constructing
+    /// an object, e.g. the subnet.
+    virtual void commit() {
+    }
+
+    /// @brief factory that constructs StringParser objects
+    ///
+    /// @param param_name name of the parameter to be parsed
+    static Dhcp4ConfigParser* Factory(const std::string& param_name) {
+        return (new StringParser(param_name));
+    }
+
+    /// @brief sets storage for value of this parameter
+    ///
+    /// See \ref dhcpv4ConfigInherit for details.
+    ///
+    /// @param storage pointer to the storage container
+    void setStorage(StringStorage* storage) {
+        storage_ = storage;
+    }
+
+private:
+    /// pointer to the storage, where parsed value will be stored
+    StringStorage* storage_;
+
+    /// name of the parameter to be parsed
+    std::string param_name_;
+
+    /// the actual parsed value
+    std::string value_;
+};
+
+
+/// @brief parser for interface list definition
+///
+/// This parser handles Dhcp4/interface entry.
+/// It contains a list of network interfaces that the server listens on.
+/// In particular, it can contain an entry called "all" or "any" that
+/// designates all interfaces.
+///
+/// It is useful for parsing Dhcp4/interface parameter.
+class InterfaceListConfigParser : public Dhcp4ConfigParser {
+public:
+
+    /// @brief constructor
+    ///
+    /// As this is a dedicated parser, it must be used to parse
+    /// "interface" parameter only. All other types will throw exception.
+    ///
+    /// @param param_name name of the configuration parameter being parsed
+    /// @throw BadValue if supplied parameter name is not "interface"
+    InterfaceListConfigParser(const std::string& param_name) {
+        if (param_name != "interface") {
+            isc_throw(BadValue, "Internal error. Interface configuration "
+                      "parser called for the wrong parameter: " << param_name);
+        }
+    }
+
+    /// @brief parses parameters value
+    ///
+    /// Parses configuration entry (list of parameters) and adds each element
+    /// to the interfaces list.
+    ///
+    /// @param value pointer to the content of parsed values
+    virtual void build(ConstElementPtr value) {
+        BOOST_FOREACH(ConstElementPtr iface, value->listValue()) {
+            interfaces_.push_back(iface->str());
+        }
+    }
+
+    /// @brief commits interfaces list configuration
+    virtual void commit() {
+        /// @todo: Implement per interface listening. Currently always listening
+        /// on all interfaces.
+    }
+
+    /// @brief factory that constructs InterfaceListConfigParser objects
+    ///
+    /// @param param_name name of the parameter to be parsed
+    static Dhcp4ConfigParser* Factory(const std::string& param_name) {
+        return (new InterfaceListConfigParser(param_name));
+    }
+
+private:
+    /// contains list of network interfaces
+    vector<string> interfaces_;
+};
+
+/// @brief parser for pool definition
+///
+/// This parser handles pool definitions, i.e. a list of entries of one
+/// of two syntaxes: min-max and prefix/len. Pool4 objects are created
+/// and stored in chosen PoolStorage container.
+///
+/// As there are no default values for pool, setStorage() must be called
+/// before build(). Otherwise exception will be thrown.
+///
+/// It is useful for parsing Dhcp4/subnet4[X]/pool parameters.
+class PoolParser : public Dhcp4ConfigParser {
+public:
+
+    /// @brief constructor.
+    PoolParser(const std::string& /*param_name*/)
+        :pools_(NULL) {
+        // ignore parameter name, it is always Dhcp4/subnet4[X]/pool
+    }
+
+    /// @brief parses the actual list
+    ///
+    /// This method parses the actual list of interfaces.
+    /// No validation is done at this stage, everything is interpreted as
+    /// interface name.
+    /// @param pools_list list of pools defined for a subnet
+    /// @throw InvalidOperation if storage was not specified (setStorage() not called)
+    /// @throw Dhcp4ConfigError when pool parsing fails
+    void build(ConstElementPtr pools_list) {
+        // setStorage() should have been called before build
+        if (!pools_) {
+            isc_throw(InvalidOperation, "Parser logic error. No pool storage set,"
+                      " but pool parser asked to parse pools");
+        }
+
+        BOOST_FOREACH(ConstElementPtr text_pool, pools_list->listValue()) {
+
+            // That should be a single pool representation. It should contain
+            // text is form prefix/len or first - last. Note that spaces
+            // are allowed
+            string txt = text_pool->stringValue();
+
+            // first let's remove any whitespaces
+            boost::erase_all(txt, " "); // space
+            boost::erase_all(txt, "\t"); // tabulation
+
+            // Is this prefix/len notation?
+            size_t pos = txt.find("/");
+            if (pos != string::npos) {
+                IOAddress addr("::");
+                uint8_t len = 0;
+                try {
+                    addr = IOAddress(txt.substr(0, pos));
+
+                    // start with the first character after /
+                    string prefix_len = txt.substr(pos + 1);
+
+                    // It is lexical cast to int and then downcast to uint8_t.
+                    // Direct cast to uint8_t (which is really an unsigned char)
+                    // will result in interpreting the first digit as output
+                    // value and throwing exception if length is written on two
+                    // digits (because there are extra characters left over).
+
+                    // No checks for values over 128. Range correctness will
+                    // be checked in Pool4 constructor.
+                    len = boost::lexical_cast<int>(prefix_len);
+                } catch (...)  {
+                    isc_throw(Dhcp4ConfigError, "Failed to parse pool "
+                              "definition: " << text_pool->stringValue());
+                }
+
+                Pool4Ptr pool(new Pool4(addr, len));
+                pools_->push_back(pool);
+                continue;
+            }
+
+            // Is this min-max notation?
+            pos = txt.find("-");
+            if (pos != string::npos) {
+                // using min-max notation
+                IOAddress min(txt.substr(0,pos));
+                IOAddress max(txt.substr(pos + 1));
+
+                Pool4Ptr pool(new Pool4(min, max));
+
+                pools_->push_back(pool);
+                continue;
+            }
+
+            isc_throw(Dhcp4ConfigError, "Failed to parse pool definition:"
+                      << text_pool->stringValue() <<
+                      ". Does not contain - (for min-max) nor / (prefix/len)");
+        }
+    }
+
+    /// @brief sets storage for value of this parameter
+    ///
+    /// See \ref dhcpv4ConfigInherit for details.
+    ///
+    /// @param storage pointer to the storage container
+    void setStorage(PoolStorage* storage) {
+        pools_ = storage;
+    }
+
+    /// @brief does nothing.
+    ///
+    /// This method is required for all parsers. The value itself
+    /// is not commited anywhere. Higher level parsers (for subnet) are expected
+    /// to use values stored in the storage.
+    virtual void commit() {}
+
+    /// @brief factory that constructs PoolParser objects
+    ///
+    /// @param param_name name of the parameter to be parsed
+    static Dhcp4ConfigParser* Factory(const std::string& param_name) {
+        return (new PoolParser(param_name));
+    }
+
+private:
+    /// @brief pointer to the actual Pools storage
+    ///
+    /// That is typically a storage somewhere in Subnet parser
+    /// (an upper level parser).
+    PoolStorage* pools_;
+};
+
+/// @brief this class parses a single subnet
+///
+/// This class parses the whole subnet definition. It creates parsers
+/// for received configuration parameters as needed.
+class Subnet4ConfigParser : public Dhcp4ConfigParser {
+public:
+
+    /// @brief constructor
+    Subnet4ConfigParser(const std::string& ) {
+        // The parameter should always be "subnet", but we don't check here
+        // against it in case someone wants to reuse this parser somewhere.
+    }
+
+    /// @brief parses parameter value
+    ///
+    /// @param subnet pointer to the content of subnet definition
+    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_);
+                    }
+                }
+            }
+
+            parser->build(param.second);
+            parsers_.push_back(parser);
+        }
+
+        // Ok, we now have subnet parsed
+    }
+
+    /// @brief commits received configuration.
+    ///
+    /// This method does most of the configuration. Many other parsers are just
+    /// storing the values that are actually consumed here. Pool definitions
+    /// created in other parsers are used here and added to newly created Subnet4
+    /// objects. Subnet4 are then added to DHCP CfgMgr.
+    /// @throw Dhcp4ConfigError if there are any issues encountered during commit
+    void commit() {
+
+        StringStorage::const_iterator it = string_values_.find("subnet");
+        if (it == string_values_.end()) {
+            isc_throw(Dhcp4ConfigError,
+                      "Mandatory subnet definition in subnet missing");
+        }
+        string subnet_txt = it->second;
+        boost::erase_all(subnet_txt, " ");
+        boost::erase_all(subnet_txt, "\t");
+
+        size_t pos = subnet_txt.find("/");
+        if (pos == string::npos) {
+            isc_throw(Dhcp4ConfigError,
+                      "Invalid subnet syntax (prefix/len expected):" << it->second);
+        }
+        IOAddress addr(subnet_txt.substr(0, pos));
+        uint8_t len = boost::lexical_cast<unsigned int>(subnet_txt.substr(pos + 1));
+
+        Triplet<uint32_t> t1 = getParam("renew-timer");
+        Triplet<uint32_t> t2 = getParam("rebind-timer");
+        Triplet<uint32_t> valid = getParam("valid-lifetime");
+
+        /// @todo: Convert this to logger once the parser is working reliably
+        stringstream tmp;
+        tmp << addr.toText() << "/" << (int)len
+            << " with params t1=" << t1 << ", t2=" << t2 << ", valid=" << valid;
+
+        LOG_INFO(dhcp4_logger, DHCP4_CONFIG_NEW_SUBNET).arg(tmp.str());
+
+        Subnet4Ptr subnet(new Subnet4(addr, len, t1, t2, valid));
+
+        for (PoolStorage::iterator it = pools_.begin(); it != pools_.end(); ++it) {
+            subnet->addPool4(*it);
+        }
+
+        CfgMgr::instance().addSubnet4(subnet);
+    }
+
+private:
+
+    /// @brief creates parsers for entries in subnet definition
+    ///
+    /// @todo Add subnet-specific things here (e.g. subnet-specific options)
+    ///
+    /// @param config_id name od the entry
+    /// @return parser object for specified entry name
+    /// @throw NotImplemented if trying to create a parser for unknown config element
+    Dhcp4ConfigParser* createSubnet4ConfigParser(const std::string& config_id) {
+        FactoryMap factories;
+
+        factories["valid-lifetime"] = Uint32Parser::Factory;
+        factories["renew-timer"] = Uint32Parser::Factory;
+        factories["rebind-timer"] = Uint32Parser::Factory;
+        factories["subnet"] = StringParser::Factory;
+        factories["pool"] = PoolParser::Factory;
+
+        FactoryMap::iterator f = factories.find(config_id);
+        if (f == factories.end()) {
+            // Used for debugging only.
+            // return new DebugParser(config_id);
+
+            isc_throw(NotImplemented,
+                      "Parser error: Subnet4 parameter not supported: "
+                      << config_id);
+        }
+        return (f->second(config_id));
+    }
+
+    /// @brief returns value for a given parameter (after using inheritance)
+    ///
+    /// This method implements inheritance. For a given parameter name, it first
+    /// checks if there is a global value for it and overwrites it with specific
+    /// value if such value was defined in subnet.
+    ///
+    /// @param name name of the parameter
+    /// @return triplet with the parameter name
+    /// @throw Dhcp4ConfigError when requested parameter is not present
+    Triplet<uint32_t> getParam(const std::string& name) {
+        uint32_t value = 0;
+        bool found = false;
+        Uint32Storage::iterator global = uint32_defaults.find(name);
+        if (global != uint32_defaults.end()) {
+            value = global->second;
+            found = true;
+        }
+
+        Uint32Storage::iterator local = uint32_values_.find(name);
+        if (local != uint32_values_.end()) {
+            value = local->second;
+            found = true;
+        }
+
+        if (found) {
+            return (Triplet<uint32_t>(value));
+        } else {
+            isc_throw(Dhcp4ConfigError, "Mandatory parameter " << name
+                      << " missing (no global default and no subnet-"
+                      << "specific value)");
+        }
+    }
+
+    /// storage for subnet-specific uint32 values
+    Uint32Storage uint32_values_;
+
+    /// storage for subnet-specific integer values
+    StringStorage string_values_;
+
+    /// storage for pools belonging to this subnet
+    PoolStorage pools_;
+
+    /// parsers are stored here
+    ParserCollection parsers_;
+};
+
+/// @brief this class parses list of subnets
+///
+/// This is a wrapper parser that handles the whole list of Subnet4
+/// definitions. It iterates over all entries and creates Subnet4ConfigParser
+/// for each entry.
+class Subnets4ListConfigParser : public Dhcp4ConfigParser {
+public:
+
+    /// @brief constructor
+    ///
+    Subnets4ListConfigParser(const std::string&) {
+        /// parameter name is ignored
+    }
+
+    /// @brief parses contents of the list
+    ///
+    /// Iterates over all entries on the list and creates Subnet4ConfigParser
+    /// for each entry.
+    ///
+    /// @param subnets_list pointer to a list of IPv4 subnets
+    void build(ConstElementPtr subnets_list) {
+
+        // No need to define FactoryMap here. There's only one type
+        // used: Subnet4ConfigParser
+
+        BOOST_FOREACH(ConstElementPtr subnet, subnets_list->listValue()) {
+
+            ParserPtr parser(new Subnet4ConfigParser("subnet"));
+            parser->build(subnet);
+            subnets_.push_back(parser);
+        }
+
+    }
+
+    /// @brief commits subnets definitions.
+    ///
+    /// Iterates over all Subnet4 parsers. Each parser contains definitions
+    /// of a single subnet and its parameters and commits each subnet separately.
+    void commit() {
+        // @todo: Implement more subtle reconfiguration than toss
+        // the old one and replace with the new one.
+
+        // remove old subnets
+        CfgMgr::instance().deleteSubnets4();
+
+        BOOST_FOREACH(ParserPtr subnet, subnets_) {
+            subnet->commit();
+        }
+
+    }
+
+    /// @brief Returns Subnet4ListConfigParser object
+    /// @param param_name name of the parameter
+    /// @return Subnets4ListConfigParser object
+    static Dhcp4ConfigParser* Factory(const std::string& param_name) {
+        return (new Subnets4ListConfigParser(param_name));
+    }
+
+    /// @brief collection of subnet parsers.
+    ParserCollection subnets_;
+};
+
+/// @brief creates global parsers
+///
+/// This method creates global parsers that parse global parameters, i.e.
+/// those that take format of Dhcp4/param1, Dhcp4/param2 and so forth.
+///
+/// @param config_id pointer to received global configuration entry
+/// @return parser for specified global DHCPv4 parameter
+/// @throw NotImplemented if trying to create a parser for unknown config element
+Dhcp4ConfigParser* createGlobalDhcp4ConfigParser(const std::string& config_id) {
+    FactoryMap factories;
+
+    factories["valid-lifetime"] = Uint32Parser::Factory;
+    factories["renew-timer"] = Uint32Parser::Factory;
+    factories["rebind-timer"] = Uint32Parser::Factory;
+    factories["interface"] = InterfaceListConfigParser::Factory;
+    factories["subnet4"] = Subnets4ListConfigParser::Factory;
+    factories["version"] = StringParser::Factory;
+
+    FactoryMap::iterator f = factories.find(config_id);
+    if (f == factories.end()) {
+        // Used for debugging only.
+        // return new DebugParser(config_id);
+
+        isc_throw(NotImplemented,
+                  "Parser error: Global configuration parameter not supported: "
+                  << config_id);
+    }
+    return (f->second(config_id));
+}
+
+isc::data::ConstElementPtr
+configureDhcp4Server(Dhcpv4Srv& , ConstElementPtr config_set) {
+    if (!config_set) {
+        ConstElementPtr answer = isc::config::createAnswer(1,
+                                 string("Can't parse NULL config"));
+        return (answer);
+    }
+
+    /// @todo: append most essential info here (like "2 new subnets configured")
+    string config_details;
+
+    LOG_DEBUG(dhcp4_logger, DBG_DHCP4_COMMAND, DHCP4_CONFIG_START).arg(config_set->str());
+
+    ParserCollection parsers;
+    try {
+        BOOST_FOREACH(ConfigPair config_pair, config_set->mapValue()) {
+
+            ParserPtr parser(createGlobalDhcp4ConfigParser(config_pair.first));
+            parser->build(config_pair.second);
+            parsers.push_back(parser);
+        }
+    } catch (const isc::Exception& ex) {
+        ConstElementPtr answer = isc::config::createAnswer(1,
+                                 string("Configuration parsing failed:") + ex.what());
+        return (answer);
+    } catch (...) {
+        // for things like bad_cast in boost::lexical_cast
+        ConstElementPtr answer = isc::config::createAnswer(1,
+                                 string("Configuration parsing failed"));
+    }
+
+    try {
+        BOOST_FOREACH(ParserPtr parser, parsers) {
+            parser->commit();
+        }
+    }
+    catch (const isc::Exception& ex) {
+        ConstElementPtr answer = isc::config::createAnswer(2,
+                                 string("Configuration commit failed:") + ex.what());
+        return (answer);
+    } catch (...) {
+        // for things like bad_cast in boost::lexical_cast
+        ConstElementPtr answer = isc::config::createAnswer(2,
+                                 string("Configuration commit failed"));
+    }
+
+    LOG_INFO(dhcp4_logger, DHCP4_CONFIG_COMPLETE).arg(config_details);
+
+    ConstElementPtr answer = isc::config::createAnswer(0, "Configuration commited.");
+    return (answer);
+}
+
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
diff --git a/src/bin/dhcp4/config_parser.h b/src/bin/dhcp4/config_parser.h
new file mode 100644
index 0000000..2ee9cf6
--- /dev/null
+++ b/src/bin/dhcp4/config_parser.h
@@ -0,0 +1,167 @@
+// 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 <cc/data.h>
+#include <string>
+
+#ifndef DHCP4_CONFIG_PARSER_H
+#define DHCP4_CONFIG_PARSER_H
+
+/// @todo: This header file and its .cc counterpart are very similar between
+/// DHCPv4 and DHCPv6. They should be merged. A ticket #2355.
+
+namespace isc {
+namespace dhcp {
+
+class Dhcpv4Srv;
+
+/// @brief a collection of elements that store uint32 values (e.g. renew-timer = 900)
+typedef std::map<std::string, uint32_t> Uint32Storage;
+
+/// @brief a collection of elements that store string values
+typedef std::map<std::string, std::string> StringStorage;
+
+/// An exception that is thrown if an error occurs while configuring an
+/// \c Dhcpv4Srv object.
+class Dhcp4ConfigError : public isc::Exception {
+public:
+
+    /// @brief constructor
+    ///
+    /// @param file name of the file, where exception occurred
+    /// @param line line of the file, where exception occurred
+    /// @param what text description of the issue that caused exception
+    Dhcp4ConfigError(const char* file, size_t line, const char* what)
+        : isc::Exception(file, line, what) {}
+};
+
+/// @brief Base abstract class for all DHCPv4 parsers
+///
+/// Each instance of a class derived from this class parses one specific config
+/// element. Sometimes elements are simple (e.g. a string) and sometimes quite
+/// complex (e.g. a subnet). In such case, it is likely that a parser will
+/// spawn child parsers to parse child elements in the configuration.
+/// @todo: Merge this class with DhcpConfigParser in src/bin/dhcp6
+class Dhcp4ConfigParser {
+    ///
+    /// \name Constructors and Destructor
+    ///
+    /// Note: The copy constructor and the assignment operator are
+    /// intentionally defined as private to make it explicit that this is a
+    /// pure base class.
+    //@{
+private:
+
+    // Private construtor and assignment operator assures that nobody
+    // will be able to copy or assign a parser. There are no defined
+    // bodies for them.
+    Dhcp4ConfigParser(const Dhcp4ConfigParser& source);
+    Dhcp4ConfigParser& operator=(const Dhcp4ConfigParser& source);
+protected:
+    /// \brief The default constructor.
+    ///
+    /// This is intentionally defined as \c protected as this base class should
+    /// never be instantiated (except as part of a derived class).
+    Dhcp4ConfigParser() {}
+public:
+    /// The destructor.
+    virtual ~Dhcp4ConfigParser() {}
+    //@}
+
+    /// \brief Prepare configuration value.
+    ///
+    /// This method parses the "value part" of the configuration identifier
+    /// that corresponds to this derived class and prepares a new value to
+    /// apply to the server.
+    ///
+    /// This method must validate the given value both in terms of syntax
+    /// and semantics of the configuration, so that the server will be
+    /// validly configured at the time of \c commit().  Note: the given
+    /// configuration value is normally syntactically validated, but the
+    /// \c build() implementation must also expect invalid input.  If it
+    /// detects an error it may throw an exception of a derived class
+    /// of \c isc::Exception.
+    ///
+    /// Preparing a configuration value will often require resource
+    /// allocation.  If it fails, it may throw a corresponding standard
+    /// exception.
+    ///
+    /// This method is not expected to be called more than once in the
+    /// life of the object. Although multiple calls are not prohibited
+    /// by the interface, the behavior is undefined.
+    ///
+    /// \param config_value The configuration value for the identifier
+    /// corresponding to the derived class.
+    virtual void build(isc::data::ConstElementPtr config_value) = 0;
+
+    /// \brief Apply the prepared configuration value to the server.
+    ///
+    /// This method is expected to be exception free, and, as a consequence,
+    /// it should normally not involve resource allocation.
+    /// Typically it would simply perform exception free assignment or swap
+    /// operation on the value prepared in \c build().
+    /// In some cases, however, it may be very difficult to meet this
+    /// condition in a realistic way, while the failure case should really
+    /// be very rare.  In such a case it may throw, and, if the parser is
+    /// called via \c configureDhcp4Server(), the caller will convert the
+    /// exception as a fatal error.
+    ///
+    /// This method is expected to be called after \c build(), and only once.
+    /// The result is undefined otherwise.
+    virtual void commit() = 0;
+};
+
+/// @brief a pointer to configuration parser
+typedef boost::shared_ptr<Dhcp4ConfigParser> ParserPtr;
+
+/// @brief a collection of parsers
+///
+/// This container is used to store pointer to parsers for a given scope.
+typedef std::vector<ParserPtr> ParserCollection;
+
+
+/// \brief Configure DHCPv4 server (\c Dhcpv4Srv) with a set of configuration values.
+///
+/// This function parses configuration information stored in \c config_set
+/// and configures the \c server by applying the configuration to it.
+/// It provides the strong exception guarantee as long as the underlying
+/// derived class implementations of \c DhcpConfigParser meet the assumption,
+/// that is, it ensures that either configuration is fully applied or the
+/// state of the server is intact.
+///
+/// If a syntax or semantics level error happens during the configuration
+/// (such as malformed configuration or invalid configuration parameter),
+/// this function returns appropriate error code.
+///
+/// This function is called every time a new configuration is received. The extra
+/// parameter is a reference to DHCPv4 server component. It is currently not used
+/// and CfgMgr::instance() is accessed instead.
+///
+/// This method does not throw. It catches all exceptions and returns them as
+/// reconfiguration statuses. It may return the following response codes:
+/// 0 - configuration successful
+/// 1 - malformed configuration (parsing failed)
+/// 2 - logical error (parsing was successful, but the values are invalid)
+///
+/// @param config_set a new configuration (JSON) for DHCPv4 server
+/// @return answer that contains result of reconfiguration
+isc::data::ConstElementPtr
+configureDhcp4Server(Dhcpv4Srv&,
+                     isc::data::ConstElementPtr config_set);
+
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
+
+#endif // DHCP4_CONFIG_PARSER_H
diff --git a/src/bin/dhcp4/ctrl_dhcp4_srv.cc b/src/bin/dhcp4/ctrl_dhcp4_srv.cc
index b02bf72..eb5d482 100644
--- a/src/bin/dhcp4/ctrl_dhcp4_srv.cc
+++ b/src/bin/dhcp4/ctrl_dhcp4_srv.cc
@@ -22,8 +22,11 @@
 #include <dhcp4/ctrl_dhcp4_srv.h>
 #include <dhcp4/dhcp4_log.h>
 #include <dhcp4/spec_config.h>
+#include <dhcp4/config_parser.h>
 #include <exceptions/exceptions.h>
 #include <util/buffer.h>
+#include <cassert>
+#include <iostream>
 
 #include <cassert>
 #include <iostream>
@@ -46,8 +49,14 @@ ConstElementPtr
 ControlledDhcpv4Srv::dhcp4ConfigHandler(ConstElementPtr new_config) {
     LOG_DEBUG(dhcp4_logger, DBG_DHCP4_COMMAND, DHCP4_CONFIG_UPDATE)
               .arg(new_config->str());
-    ConstElementPtr answer = isc::config::createAnswer(0,
-                             "Thank you for sending config.");
+    if (server_) {
+        return (configureDhcp4Server(*server_, new_config));
+    }
+
+    // That should never happen as we install config_handler after we instantiate
+    // the server.
+    ConstElementPtr answer = isc::config::createAnswer(1,
+           "Configuration rejected, server is during startup/shutdown phase.");
     return (answer);
 }
 
@@ -100,10 +109,22 @@ void ControlledDhcpv4Srv::establishSession() {
               .arg(specfile);
     cc_session_ = new Session(io_service_.get_io_service());
     config_session_ = new ModuleCCSession(specfile, *cc_session_,
-                                          dhcp4ConfigHandler,
+                                          NULL,
                                           dhcp4CommandHandler, false);
     config_session_->start();
 
+    // We initially create ModuleCCSession() without configHandler, as
+    // the session module is too eager to send partial configuration.
+    // We want to get the full configuration, so we explicitly call
+    // getFullConfig() and then pass it to our configHandler.
+    config_session_->setConfigHandler(dhcp4ConfigHandler);
+
+    try {
+        configureDhcp4Server(*this, config_session_->getFullConfig());
+    } catch (const Dhcp4ConfigError& ex) {
+        LOG_ERROR(dhcp4_logger, DHCP4_CONFIG_LOAD_FAIL).arg(ex.what());
+    }
+
     /// Integrate the asynchronous I/O model of BIND 10 configuration
     /// control with the "select" model of the DHCP server.  This is
     /// fully explained in \ref dhcpv4Session.
diff --git a/src/bin/dhcp4/ctrl_dhcp4_srv.h b/src/bin/dhcp4/ctrl_dhcp4_srv.h
index 9fd7668..9bd261c 100644
--- a/src/bin/dhcp4/ctrl_dhcp4_srv.h
+++ b/src/bin/dhcp4/ctrl_dhcp4_srv.h
@@ -66,7 +66,9 @@ public:
 
     /// @brief Session callback, processes received commands.
     ///
-    /// @param command_id text represenation of the command (e.g. "shutdown")
+    /// @param command Text represenation of the command (e.g. "shutdown")
+    /// @param args Optional parameters
+    /// @param command text represenation of the command (e.g. "shutdown")
     /// @param args optional parameters
     ///
     /// @return status of the command
diff --git a/src/bin/dhcp4/dhcp4.dox b/src/bin/dhcp4/dhcp4.dox
new file mode 100644
index 0000000..05f1670
--- /dev/null
+++ b/src/bin/dhcp4/dhcp4.dox
@@ -0,0 +1,68 @@
+/**
+ @page dhcp4 DHCPv4 Server Component
+
+BIND10 offers DHCPv4 server implementation. It is implemented as
+b10-dhcp4 component.  Its primary code is located in
+isc::dhcp::Dhcpv4Srv class. It uses \ref libdhcp extensively,
+especially isc::dhcp::Pkt4, isc::dhcp::Option and
+isc::dhcp::IfaceMgr classes. Currently this code offers skeleton
+functionality, i.e. it is able to receive and process incoming
+requests and trasmit responses. However, it does not have database
+management, so it returns only one, hardcoded lease to whoever asks
+for it.
+
+DHCPv4 server component does not support direct traffic (relayed
+only), as support for transmission to hosts without IPv4 address
+assigned is not implemented in IfaceMgr yet.
+
+ at section dhcpv4Session BIND10 message queue integration
+
+DHCPv4 server component is now integrated with BIND10 message queue.
+The integration is performed by establishSession() and disconnectSession()
+functions in isc::dhcp::ControlledDhcpv4Srv class. main() method deifined
+in the src/bin/dhcp4/main.cc file instantiates isc::dhcp::ControlledDhcpv4Srv
+class that establishes connection with msgq and install necessary handlers
+for receiving commands and configuration updates. It is derived from
+a base isc::dhcp::Dhcpv4Srv class that implements DHCPv4 server functionality,
+without any controlling mechanisms.
+
+ControlledDhcpv4Srv instantiates several components to make management
+session possible. In particular, isc::cc::Session cc_session
+object uses ASIO for establishing connection. It registers its socket
+in isc::asiolink::IOService io_service object. Typically, other components
+(e.g. auth or resolver) that use ASIO for their communication, register their
+other sockets in the
+same io_service and then just call io_service.run() method that does
+not return, until one of the callback decides that it is time to shut down
+the whole component cal calls io_service.stop(). DHCPv4 works in a
+different way. It does receive messages using select()
+(see isc::dhcp::IfaceMgr::receive4()), which is incompatible with ASIO.
+To solve this problem, socket descriptor is extracted from cc_session
+object and is passed to IfaceMgr by using isc::dhcp::IfaceMgr::set_session_socket().
+IfaceMgr then uses this socket in its select() call. If there is some
+data to be read, it calls registered callback that is supposed to
+read and process incoming data.
+
+This somewhat complicated approach is needed for a simple reason. In
+embedded deployments there will be no message queue. Not referring directly
+to anything related to message queue in isc::dhcp::Dhcpv4Srv and
+isc::dhcp::IfaceMgr classes brings in two benefits. First, the can
+be used with and without message queue. Second benefit is related to the
+first one: \ref libdhcp is supposed to be simple and robust and not require
+many dependencies. One notable example of a use case that benefits from
+this approach is a perfdhcp tool. Finally, the idea is that it should be
+possible to instantiate Dhcpv4Srv object directly, thus getting a server
+that does not support msgq. That is useful for embedded environments.
+It may also be useful in validation.
+
+ at section dhcpv4ConfigParser Configuration Parser in DHCPv4
+
+This parser follows exactly the same logic as its DHCPv6 counterpart.
+See \ref dhcpv6ConfigParser.
+
+ at section dhcpv4ConfigInherit DHCPv4 configuration inheritance
+
+Configuration inheritance in DHCPv4 follows exactly the same logic as its DHCPv6
+counterpart. See \ref dhcpv6ConfigInherit.
+
+*/
diff --git a/src/bin/dhcp4/dhcp4.spec b/src/bin/dhcp4/dhcp4.spec
index 7584b48..d5066e8 100644
--- a/src/bin/dhcp4/dhcp4.spec
+++ b/src/bin/dhcp4/dhcp4.spec
@@ -4,9 +4,85 @@
     "module_description": "DHCPv4 server daemon",
     "config_data": [
       { "item_name": "interface",
-        "item_type": "string",
+        "item_type": "list",
         "item_optional": false,
-        "item_default": "eth0"
+        "item_default": [ "all" ],
+        "list_item_spec":
+        {
+          "item_name": "interface_name",
+          "item_type": "string",
+          "item_optional": false,
+          "item_default": "all"
+        }
+      } ,
+
+      { "item_name": "renew-timer",
+        "item_type": "integer",
+        "item_optional": false,
+        "item_default": 1000
+      },
+
+      { "item_name": "rebind-timer",
+        "item_type": "integer",
+        "item_optional": false,
+        "item_default": 2000
+      },
+
+      { "item_name": "valid-lifetime",
+        "item_type": "integer",
+        "item_optional": false,
+        "item_default": 4000
+      },
+
+      { "item_name": "subnet4",
+        "item_type": "list",
+        "item_optional": false,
+        "item_default": [],
+        "list_item_spec":
+        {
+            "item_name": "single-subnet4",
+            "item_type": "map",
+            "item_optional": false,
+            "item_default": {},
+            "map_item_spec": [
+
+                { "item_name": "subnet",
+                  "item_type": "string",
+                  "item_optional": false,
+                  "item_default": ""
+                },
+
+                { "item_name": "renew-timer",
+                  "item_type": "integer",
+                  "item_optional": false,
+                  "item_default": 1000
+                },
+
+                { "item_name": "rebind-timer",
+                  "item_type": "integer",
+                  "item_optional": false,
+                  "item_default": 2000
+                },
+
+                { "item_name": "valid-lifetime",
+                  "item_type": "integer",
+                  "item_optional": false,
+                  "item_default": 7200
+                },
+                { "item_name": "pool",
+                  "item_type": "list",
+                  "item_optional": false,
+                  "item_default": [],
+                    "list_item_spec":
+                    {
+                        "item_name": "type",
+                        "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 392b332..994f004 100644
--- a/src/bin/dhcp4/dhcp4_messages.mes
+++ b/src/bin/dhcp4/dhcp4_messages.mes
@@ -26,10 +26,30 @@ 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_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_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_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_NOT_RUNNING IPv4 DHCP server is not running
 A warning message is issued when an attempt is made to shut down the
 IPv4 DHCP server but it is not running.
diff --git a/src/bin/dhcp4/dhcp4_srv.h b/src/bin/dhcp4/dhcp4_srv.h
index 48ccde6..db4b4bc 100644
--- a/src/bin/dhcp4/dhcp4_srv.h
+++ b/src/bin/dhcp4/dhcp4_srv.h
@@ -36,7 +36,7 @@ namespace dhcp {
 /// appropriate responses.
 ///
 /// This class does not support any controlling mechanisms directly.
-/// See derived \ref ControlledDhcv4Srv class for support for
+/// See the derived \ref ControlledDhcpv4Srv class for support for
 /// command and configuration updates over msgq.
 ///
 /// For detailed explanation or relations between main(), ControlledDhcpv4Srv,
diff --git a/src/bin/dhcp4/main.cc b/src/bin/dhcp4/main.cc
index bd3be1b..45f1de1 100644
--- a/src/bin/dhcp4/main.cc
+++ b/src/bin/dhcp4/main.cc
@@ -17,6 +17,7 @@
 #include <dhcp4/ctrl_dhcp4_srv.h>
 #include <dhcp4/dhcp4_log.h>
 #include <log/logger_support.h>
+#include <log/logger_manager.h>
 
 #include <boost/lexical_cast.hpp>
 
@@ -93,9 +94,10 @@ main(int argc, char* argv[]) {
     }
 
     // Initialize logging.  If verbose, we'll use maximum verbosity.
+    // If standalone is enabled, do not buffer initial log messages
     isc::log::initLogger(DHCP4_NAME,
                          (verbose_mode ? isc::log::DEBUG : isc::log::INFO),
-                         isc::log::MAX_DEBUG_LEVEL, NULL);
+                         isc::log::MAX_DEBUG_LEVEL, NULL, !stand_alone);
     LOG_INFO(dhcp4_logger, DHCP4_STARTING);
     LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_START_INFO)
               .arg(getpid()).arg(port_number).arg(verbose_mode ? "yes" : "no")
@@ -112,6 +114,10 @@ main(int argc, char* argv[]) {
                 LOG_ERROR(dhcp4_logger, DHCP4_SESSION_FAIL).arg(ex.what());
                 // Let's continue. It is useful to have the ability to run
                 // DHCP server in stand-alone mode, e.g. for testing
+                // We do need to make sure logging is no longer buffered
+                // since then it would not print until dhcp6 is stopped
+                isc::log::LoggerManager log_manager;
+                log_manager.process();
             }
         } else {
             LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_STANDALONE);
diff --git a/src/bin/dhcp4/tests/Makefile.am b/src/bin/dhcp4/tests/Makefile.am
index ddc3000..e601919 100644
--- a/src/bin/dhcp4/tests/Makefile.am
+++ b/src/bin/dhcp4/tests/Makefile.am
@@ -49,9 +49,11 @@ TESTS += dhcp4_unittests
 
 dhcp4_unittests_SOURCES = ../dhcp4_srv.h ../dhcp4_srv.cc ../ctrl_dhcp4_srv.cc
 dhcp4_unittests_SOURCES += ../dhcp4_log.h ../dhcp4_log.cc
+dhcp4_unittests_SOURCES += ../config_parser.cc ../config_parser.h
 dhcp4_unittests_SOURCES += dhcp4_unittests.cc
 dhcp4_unittests_SOURCES += dhcp4_srv_unittest.cc
 dhcp4_unittests_SOURCES += ctrl_dhcp4_srv_unittest.cc
+dhcp4_unittests_SOURCES += config_parser_unittest.cc
 nodist_dhcp4_unittests_SOURCES = ../dhcp4_messages.h ../dhcp4_messages.cc
 
 if USE_CLANGPP
@@ -65,6 +67,7 @@ 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/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
diff --git a/src/bin/dhcp4/tests/config_parser_unittest.cc b/src/bin/dhcp4/tests/config_parser_unittest.cc
new file mode 100644
index 0000000..a4ccabd
--- /dev/null
+++ b/src/bin/dhcp4/tests/config_parser_unittest.cc
@@ -0,0 +1,294 @@
+// 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 <arpa/inet.h>
+#include <gtest/gtest.h>
+
+#include <dhcp4/dhcp4_srv.h>
+#include <dhcp4/config_parser.h>
+#include <config/ccsession.h>
+#include <dhcpsrv/subnet.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <limits.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::asiolink;
+using namespace isc::data;
+using namespace isc::config;
+
+namespace isc {
+namespace dhcp {
+extern Uint32Storage uint32_defaults;
+}
+}
+
+namespace {
+
+class Dhcp4ParserTest : public ::testing::Test {
+public:
+    Dhcp4ParserTest()
+    :rcode_(-1) {
+        // Open port 0 means to not do anything at all. We don't want to
+        // deal with sockets here, just check if configuration handling
+        // is sane.
+        srv_ = new Dhcpv4Srv(0);
+    }
+
+    // Checks if global parameter of name have expected_value
+    void checkGlobalUint32(string name, uint32_t expected_value) {
+        Uint32Storage::const_iterator it = uint32_defaults.find(name);
+        if (it == uint32_defaults.end()) {
+            ADD_FAILURE() << "Expected uint32 with name " << name
+                          << " not found";
+            return;
+        }
+        EXPECT_EQ(expected_value, it->second);
+    }
+
+    // Checks if config_result (result of DHCP server configuration) has
+    // expected code (0 for success, other for failures).
+    // Also stores result in rcode_ and comment_.
+    void checkResult(ConstElementPtr status, int expected_code) {
+        ASSERT_TRUE(status);
+        comment_ = parseAnswer(rcode_, status);
+        EXPECT_EQ(expected_code, rcode_);
+    }
+
+    ~Dhcp4ParserTest() {
+        delete srv_;
+    };
+
+    Dhcpv4Srv* srv_;
+
+    int rcode_;
+    ConstElementPtr comment_;
+};
+
+// Goal of this test is a verification if a very simple config update
+// with just a bumped version number. That's the simplest possible
+// config update.
+TEST_F(Dhcp4ParserTest, version) {
+
+    ConstElementPtr x;
+
+    EXPECT_NO_THROW(x = configureDhcp4Server(*srv_,
+                    Element::fromJSON("{\"version\": 0}")));
+
+    // returned value must be 0 (configuration accepted)
+    checkResult(x, 0);
+}
+
+/// The goal of this test is to verify that the code accepts only
+/// valid commands and malformed or unsupported parameters are rejected.
+TEST_F(Dhcp4ParserTest, bogusCommand) {
+
+    ConstElementPtr x;
+
+    EXPECT_NO_THROW(x = configureDhcp4Server(*srv_,
+                    Element::fromJSON("{\"bogus\": 5}")));
+
+    // returned value must be 1 (configuration parse error)
+    checkResult(x, 1);
+}
+
+/// The goal of this test is to verify if wrongly defined subnet will
+/// be rejected. Properly defined subnet must include at least one
+/// pool definition.
+TEST_F(Dhcp4ParserTest, emptySubnet) {
+
+    ConstElementPtr status;
+
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_,
+                    Element::fromJSON("{ \"interface\": [ \"all\" ],"
+                                      "\"rebind-timer\": 2000, "
+                                      "\"renew-timer\": 1000, "
+                                      "\"subnet4\": [  ], "
+                                      "\"valid-lifetime\": 4000 }")));
+
+    // returned value should be 0 (success)
+    checkResult(status, 0);
+
+    checkGlobalUint32("rebind-timer", 2000);
+    checkGlobalUint32("renew-timer", 1000);
+    checkGlobalUint32("valid-lifetime", 4000);
+}
+
+/// The goal of this test is to verify if defined subnet uses global
+/// parameter timer definitions.
+TEST_F(Dhcp4ParserTest, subnetGlobalDefaults) {
+
+    ConstElementPtr status;
+
+    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\" } ],"
+        "\"valid-lifetime\": 4000 }";
+    cout << config << endl;
+
+    ElementPtr json = Element::fromJSON(config);
+
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
+
+    // check if returned status is OK
+    checkResult(status, 0);
+
+    // Now check if the configuration was indeed handled and we have
+    // expected pool configured.
+    Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.200"));
+    ASSERT_TRUE(subnet);
+    EXPECT_EQ(1000, subnet->getT1());
+    EXPECT_EQ(2000, subnet->getT2());
+    EXPECT_EQ(4000, subnet->getValid());
+}
+
+// This test checks if it is possible to override global values
+// on a per subnet basis.
+TEST_F(Dhcp4ParserTest, subnetLocal) {
+
+    ConstElementPtr status;
+
+    string config = "{ \"interface\": [ \"all\" ],"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet4\": [ { "
+        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"renew-timer\": 1, "
+        "    \"rebind-timer\": 2, "
+        "    \"valid-lifetime\": 4,"
+        "    \"subnet\": \"192.0.2.0/24\" } ],"
+        "\"valid-lifetime\": 4000 }";
+    cout << config << endl;
+
+    ElementPtr json = Element::fromJSON(config);
+
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
+
+    // returned value should be 0 (configuration success)
+    checkResult(status, 0);
+
+    Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.200"));
+    ASSERT_TRUE(subnet);
+    EXPECT_EQ(1, subnet->getT1());
+    EXPECT_EQ(2, subnet->getT2());
+    EXPECT_EQ(4, subnet->getValid());
+}
+
+// Test verifies that a subnet with pool values that do not belong to that
+// pool are rejected.
+TEST_F(Dhcp4ParserTest, poolOutOfSubnet) {
+
+    ConstElementPtr status;
+
+    string config = "{ \"interface\": [ \"all\" ],"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet4\": [ { "
+        "    \"pool\": [ \"192.0.4.0/28\" ],"
+        "    \"subnet\": \"192.0.2.0/24\" } ],"
+        "\"valid-lifetime\": 4000 }";
+    cout << config << endl;
+
+    ElementPtr json = Element::fromJSON(config);
+
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
+
+    // returned value must be 2 (values error)
+    // as the pool does not belong to that subnet
+    checkResult(status, 2);
+}
+
+// Goal of this test is to verify if pools can be defined
+// using prefix/length notation. There is no separate test for min-max
+// notation as it was tested in several previous tests.
+TEST_F(Dhcp4ParserTest, poolPrefixLen) {
+
+    ConstElementPtr status;
+
+    string config = "{ \"interface\": [ \"all\" ],"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet4\": [ { "
+        "    \"pool\": [ \"192.0.2.128/28\" ],"
+        "    \"subnet\": \"192.0.2.0/24\" } ],"
+        "\"valid-lifetime\": 4000 }";
+    cout << config << endl;
+
+    ElementPtr json = Element::fromJSON(config);
+
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
+
+    // returned value must be 0 (configuration accepted)
+    checkResult(status, 0);
+
+    Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.200"));
+    ASSERT_TRUE(subnet);
+    EXPECT_EQ(1000, subnet->getT1());
+    EXPECT_EQ(2000, subnet->getT2());
+    EXPECT_EQ(4000, subnet->getValid());
+}
+
+/// 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
+/// parameter renew-timer and the results are stored in uint32_defaults.
+TEST_F(Dhcp4ParserTest, DISABLED_Uint32Parser) {
+
+    ConstElementPtr status;
+
+    // CASE 1: 0 - minimum value, should work
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_,
+                    Element::fromJSON("{\"version\": 0,"
+                                      "\"renew-timer\": 0}")));
+
+    // returned value must be ok (0 is a proper value)
+    checkResult(status, 0);
+    checkGlobalUint32("renew-timer", 0);
+
+    // CASE 2: 4294967295U (UINT_MAX) should work as well
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_,
+                    Element::fromJSON("{\"version\": 0,"
+                                      "\"renew-timer\": 4294967295}")));
+
+    // returned value must be ok (0 is a proper value)
+    checkResult(status, 0);
+    checkGlobalUint32("renew-timer", 4294967295U);
+
+    // CASE 3: 4294967296U (UINT_MAX + 1) should not work
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_,
+                    Element::fromJSON("{\"version\": 0,"
+                                      "\"renew-timer\": 4294967296}")));
+
+    // returned value must be rejected (1 configuration error)
+    checkResult(status, 1);
+
+    // CASE 4: -1 (UINT_MIN -1 ) should not work
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_,
+                    Element::fromJSON("{\"version\": 0,"
+                                      "\"renew-timer\": -1}")));
+
+    // returned value must be rejected (1 configuration error)
+    checkResult(status, 1);
+}
+
+};
diff --git a/src/bin/dhcp6/config_parser.cc b/src/bin/dhcp6/config_parser.cc
index 1c55649..4c6fab1 100644
--- a/src/bin/dhcp6/config_parser.cc
+++ b/src/bin/dhcp6/config_parser.cc
@@ -129,7 +129,7 @@ public:
         return (new DebugParser(param_name));
     }
 
-protected:
+private:
     /// name of the parsed parameter
     std::string param_name_;
 
@@ -147,7 +147,7 @@ protected:
 /// in its base class, \ref DhcpConfigParser.
 ///
 /// For overview of usability of this generic purpose parser, see
-/// \ref dhcpv6-config-inherit page.
+/// \ref dhcpv6ConfigInherit page.
 ///
 /// @todo this class should be turned into the template class which
 /// will handle all uintX_types of data (see ticket #2415).
@@ -163,7 +163,7 @@ public:
     /// @brief builds parameter value
     ///
     /// Parses configuration entry and stores it in a storage. See
-    /// \ref setStorage() for details.
+    /// \ref Uint32Parser::setStorage() for details.
     ///
     /// @param value pointer to the content of parsed values
     virtual void build(ConstElementPtr value) {
@@ -222,14 +222,14 @@ public:
 
     /// @brief sets storage for value of this parameter
     ///
-    /// See \ref dhcpv6-config-inherit for details.
+    /// See \ref dhcpv6ConfigInherit for details.
     ///
     /// @param storage pointer to the storage container
     void setStorage(Uint32Storage* storage) {
         storage_ = storage;
     }
 
-protected:
+private:
     /// pointer to the storage, where parsed value will be stored
     Uint32Storage* storage_;
 
@@ -250,7 +250,7 @@ protected:
 /// in its base class, \ref DhcpConfigParser.
 ///
 /// For overview of usability of this generic purpose parser, see
-/// \ref dhcpv6-config-inherit page.
+/// \ref dhcpv6ConfigInherit page.
 class StringParser : public DhcpConfigParser {
 public:
 
@@ -269,6 +269,7 @@ public:
     virtual void build(ConstElementPtr value) {
         value_ = value->str();
         boost::erase_all(value_, "\"");
+
         // If a given parameter already exists in the storage we override
         // its value. If it doesn't we insert a new element.
         (*storage_)[param_name_] = value_;
@@ -293,14 +294,14 @@ public:
 
     /// @brief sets storage for value of this parameter
     ///
-    /// See \ref dhcpv6-config-inherit for details.
+    /// See \ref dhcpv6ConfigInherit for details.
     ///
     /// @param storage pointer to the storage container
     void setStorage(StringStorage* storage) {
         storage_ = storage;
     }
 
-protected:
+private:
     /// pointer to the storage, where parsed value will be stored
     StringStorage* storage_;
 
@@ -329,9 +330,10 @@ public:
     /// "interface" parameter only. All other types will throw exception.
     ///
     /// @param param_name name of the configuration parameter being parsed
+    /// @throw BadValue if supplied parameter name is not "interface"
     InterfaceListConfigParser(const std::string& param_name) {
         if (param_name != "interface") {
-            isc_throw(NotImplemented, "Internal error. Interface configuration "
+            isc_throw(BadValue, "Internal error. Interface configuration "
                       "parser called for the wrong parameter: " << param_name);
         }
     }
@@ -339,7 +341,7 @@ public:
     /// @brief parses parameters value
     ///
     /// Parses configuration entry (list of parameters) and stores it in
-    /// storage. See \ref setStorage() for details.
+    /// storage.
     ///
     /// @param value pointer to the content of parsed values
     virtual void build(ConstElementPtr value) {
@@ -361,7 +363,7 @@ public:
         return (new InterfaceListConfigParser(param_name));
     }
 
-protected:
+private:
     /// contains list of network interfaces
     vector<string> interfaces_;
 };
@@ -390,6 +392,8 @@ public:
     /// This method parses the actual list of interfaces.
     /// No validation is done at this stage, everything is interpreted as
     /// interface name.
+    /// @param pools_list list of pools defined for a subnet
+    /// @throw BadValue if storage was not specified (setStorage() not called)
     void build(ConstElementPtr pools_list) {
         // setStorage() should have been called before build
         if (!pools_) {
@@ -442,7 +446,7 @@ public:
             pos = txt.find("-");
             if (pos != string::npos) {
                 // using min-max notation
-                IOAddress min(txt.substr(0,pos - 1));
+                IOAddress min(txt.substr(0, pos));
                 IOAddress max(txt.substr(pos + 1));
 
                 Pool6Ptr pool(new Pool6(Pool6::TYPE_IA, min, max));
@@ -459,7 +463,7 @@ public:
 
     /// @brief sets storage for value of this parameter
     ///
-    /// See \ref dhcpv6-config-inherit for details.
+    /// See \ref dhcpv6ConfigInherit for details.
     ///
     /// @param storage pointer to the storage container
     void setStorage(PoolStorage* storage) {
@@ -480,7 +484,7 @@ public:
         return (new PoolParser(param_name));
     }
 
-protected:
+private:
     /// @brief pointer to the actual Pools storage
     ///
     /// This is typically a storage somewhere in Subnet parser
@@ -1005,6 +1009,7 @@ private:
     ///
     /// @param config_id name od the entry
     /// @return parser object for specified entry name
+    /// @throw NotImplemented if trying to create a parser for unknown config element
     DhcpConfigParser* createSubnet6ConfigParser(const std::string& config_id) {
         FactoryMap factories;
 
@@ -1047,6 +1052,7 @@ private:
     ///
     /// @param name name of the parameter
     /// @return triplet with the parameter name
+    /// @throw Dhcp6ConfigError when requested parameter is not present
     Triplet<uint32_t> getParam(const std::string& name) {
         uint32_t value = 0;
         bool found = false;
@@ -1156,6 +1162,7 @@ public:
 ///
 /// @param config_id pointer to received global configuration entry
 /// @return parser for specified global DHCPv6 parameter
+/// @throw NotImplemented if trying to create a parser for unknown config element
 DhcpConfigParser* createGlobalDhcpConfigParser(const std::string& config_id) {
     FactoryMap factories;
 
@@ -1205,11 +1212,13 @@ DhcpConfigParser* createGlobalDhcpConfigParser(const std::string& config_id) {
 ///
 /// @param config_set a new configuration for DHCPv6 server
 /// @return answer that contains result of reconfiguration
+/// @throw Dhcp6ConfigError if trying to create a parser for NULL config
 ConstElementPtr
 configureDhcp6Server(Dhcpv6Srv& , ConstElementPtr config_set) {
     if (!config_set) {
-        isc_throw(Dhcp6ConfigError,
-                  "Null pointer is passed to configuration parser");
+        ConstElementPtr answer = isc::config::createAnswer(1,
+                                 string("Can't parse NULL config"));
+        return (answer);
     }
 
     /// @todo: append most essential info here (like "2 new subnets configured")
diff --git a/src/bin/dhcp6/config_parser.h b/src/bin/dhcp6/config_parser.h
index 9f7c3ae..ed44bb9 100644
--- a/src/bin/dhcp6/config_parser.h
+++ b/src/bin/dhcp6/config_parser.h
@@ -15,9 +15,11 @@
 #ifndef DHCP6_CONFIG_PARSER_H
 #define DHCP6_CONFIG_PARSER_H
 
+/// @todo: This header file and its .cc counterpart are very similar between
+/// DHCPv4 and DHCPv6. They should be merged. See ticket #2355.
+
 #include <cc/data.h>
 #include <exceptions/exceptions.h>
-
 #include <string>
 
 namespace isc {
@@ -30,15 +32,22 @@ class Dhcpv6Srv;
 class Dhcp6ConfigError : public isc::Exception {
 public:
 
-/// @brief constructor
-///
-/// @param file name of the file, where exception occurred
-/// @param line line of the file, where exception occurred
-/// @param what text description of the issue that caused exception
-Dhcp6ConfigError(const char* file, size_t line, const char* what) :
-    isc::Exception(file, line, what) {}
+    /// @brief constructor
+    ///
+    /// @param file name of the file, where exception occurred
+    /// @param line line of the file, where exception occurred
+    /// @param what text description of the issue that caused exception
+    Dhcp6ConfigError(const char* file, size_t line, const char* what)
+        : isc::Exception(file, line, what) {}
 };
 
+/// @brief Base abstract class for all DHCPv6 parsers
+///
+/// Each instance of a class derived from this class parses one specific config
+/// element. Sometimes elements are simple (e.g. a string) and sometimes quite
+/// complex (e.g. a subnet). In such case, it is likely that a parser will
+/// spawn child parsers to parse child elements in the configuration.
+/// @todo: Merge this class with Dhcp4ConfigParser in src/bin/dhcp4
 class DhcpConfigParser {
     ///
     /// \name Constructors and Destructor
diff --git a/src/bin/dhcp6/ctrl_dhcp6_srv.cc b/src/bin/dhcp6/ctrl_dhcp6_srv.cc
index 5d4d990..8611365 100644
--- a/src/bin/dhcp6/ctrl_dhcp6_srv.cc
+++ b/src/bin/dhcp6/ctrl_dhcp6_srv.cc
@@ -17,7 +17,6 @@
 #include <asiolink/asiolink.h>
 #include <cc/data.h>
 #include <cc/session.h>
-#include <cc/session.h>
 #include <config/ccsession.h>
 #include <dhcp/iface_mgr.h>
 #include <dhcp6/config_parser.h>
diff --git a/src/bin/dhcp6/ctrl_dhcp6_srv.h b/src/bin/dhcp6/ctrl_dhcp6_srv.h
index 22cb3b6..ef1acab 100644
--- a/src/bin/dhcp6/ctrl_dhcp6_srv.h
+++ b/src/bin/dhcp6/ctrl_dhcp6_srv.h
@@ -68,8 +68,8 @@ public:
 
     /// @brief Session callback, processes received commands.
     ///
-    /// @param command_id text represenation of the command (e.g. "shutdown")
-    /// @param args optional parameters
+    /// @param command Text represenation of the command (e.g. "shutdown")
+    /// @param args Optional parameters
     ///
     /// @return status of the command
     static isc::data::ConstElementPtr
diff --git a/src/bin/dhcp6/dhcp6.dox b/src/bin/dhcp6/dhcp6.dox
index c234f40..5350fd8 100644
--- a/src/bin/dhcp6/dhcp6.dox
+++ b/src/bin/dhcp6/dhcp6.dox
@@ -1,5 +1,5 @@
 /**
- @page dhcpv6 DHCPv6 Server Component
+ @page dhcp6 DHCPv6 Server Component
 
  BIND10 offers DHCPv6 server implementation. It is implemented as
  b10-dhcp6 component. Its primary code is located in
@@ -16,13 +16,13 @@
 
  DHCPv6 server component does not use BIND10 logging yet.
 
- @section dhcpv6-session BIND10 message queue integration
+ @section dhcpv6Session BIND10 message queue integration
 
  DHCPv4 server component is now integrated with BIND10 message queue.
  It follows the same principle as DHCPv4. See \ref dhcpv4Session for
  details.
 
- @section dhcpv6-config-parser Configuration Parser in DHCPv6
+ @section dhcpv6ConfigParser Configuration Parser in DHCPv6
 
  b10-dhcp6 component uses BIND10 cfgmgr for commands and configuration. During
  initial configuration (See \ref
@@ -49,7 +49,7 @@
  elements and creates parsers for a given scope. This process may be repeated
  (sort of) recursively.
 
- @section dhcpv6-config-inherit DHCPv6 Configuration Inheritance
+ @section dhcpv6ConfigInherit DHCPv6 Configuration Inheritance
 
  One notable useful feature of DHCP configuration is its parameter inheritance.
  For example, renew-timer value may be specified at a global scope and it then
diff --git a/src/bin/dhcp6/dhcp6_messages.mes b/src/bin/dhcp6/dhcp6_messages.mes
index 6b82344..6ab42b3 100644
--- a/src/bin/dhcp6/dhcp6_messages.mes
+++ b/src/bin/dhcp6/dhcp6_messages.mes
@@ -22,6 +22,11 @@ successfully established a session with the BIND 10 control channel.
 This debug message is issued just before the IPv6 DHCP server attempts
 to establish a session with the BIND 10 control channel.
 
+% DHCP6_CLIENTID_MISSING mandatory client-id option is missing, message from %1 dropped
+This error message indicates that the received message is being dropped
+because it does not include the mandatory client-id option necessary for
+address assignment.  The most likely cause is a problem with the client.
+
 % DHCP6_COMMAND_RECEIVED received command %1, arguments: %2
 A debug message listing the command (and possible arguments) received
 from the BIND 10 control system by the IPv6 DHCP server.
@@ -81,6 +86,13 @@ 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_NOT_RUNNING IPv6 DHCP server is not running
 A warning message is issued when an attempt is made to shut down the
 IPv6 DHCP server but it is not running.
@@ -189,3 +201,18 @@ which the DHCPv6 server has not been configured. The cause is most likely due
 to a misconfiguration of the server. The packet processing will continue, but
 the response will only contain generic configuration parameters and no
 addresses or prefixes.
+
+% DHCP6_UNKNOWN_RENEW received RENEW from client (duid=%1, iaid=%2) in subnet %3
+This warning message is printed when client attempts to renew a lease,
+but no such lease is known by the server. It typically means that
+client has attempted to use its lease past its lifetime: causes of this
+include a adjustment of the clients date/time setting or poor support
+for sleep/recovery. A properly implemented client will recover from such
+a situation by restarting the lease allocation process after receiving
+a negative reply from the server.
+
+An alternative cause could be that the server has lost its database
+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.
diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc
index 02c0dd0..2482833 100644
--- a/src/bin/dhcp6/dhcp6_srv.cc
+++ b/src/bin/dhcp6/dhcp6_srv.cc
@@ -24,6 +24,7 @@
 #include <dhcp/option6_iaaddr.h>
 #include <dhcp/option6_iaaddr.h>
 #include <dhcp/option6_int_array.h>
+#include <dhcp/option_custom.h>
 #include <dhcp/pkt6.h>
 #include <dhcp6/dhcp6_log.h>
 #include <dhcp6/dhcp6_srv.h>
@@ -126,50 +127,57 @@ bool Dhcpv6Srv::run() {
                 continue;
             }
             LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_PACKET_RECEIVED)
-                      .arg(serverReceivedPacketName(query->getType()));
+                      .arg(query->getName());
             LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL_DATA, DHCP6_QUERY_DATA)
                       .arg(static_cast<int>(query->getType()))
                       .arg(query->getBuffer().getLength())
                       .arg(query->toText());
 
-            switch (query->getType()) {
-            case DHCPV6_SOLICIT:
-                rsp = processSolicit(query);
-                break;
+            try {
+                switch (query->getType()) {
+                case DHCPV6_SOLICIT:
+                    rsp = processSolicit(query);
+                    break;
 
-            case DHCPV6_REQUEST:
-                rsp = processRequest(query);
-                break;
+                case DHCPV6_REQUEST:
+                    rsp = processRequest(query);
+                    break;
 
-            case DHCPV6_RENEW:
-                rsp = processRenew(query);
-                break;
+                case DHCPV6_RENEW:
+                    rsp = processRenew(query);
+                    break;
 
-            case DHCPV6_REBIND:
-                rsp = processRebind(query);
-                break;
+                case DHCPV6_REBIND:
+                    rsp = processRebind(query);
+                    break;
 
-            case DHCPV6_CONFIRM:
-                rsp = processConfirm(query);
-                break;
+                case DHCPV6_CONFIRM:
+                    rsp = processConfirm(query);
+                    break;
 
-            case DHCPV6_RELEASE:
+                case DHCPV6_RELEASE:
                 rsp = processRelease(query);
                 break;
 
-            case DHCPV6_DECLINE:
-                rsp = processDecline(query);
-                break;
+                case DHCPV6_DECLINE:
+                    rsp = processDecline(query);
+                    break;
 
-            case DHCPV6_INFORMATION_REQUEST:
-                rsp = processInfRequest(query);
-                break;
+                case DHCPV6_INFORMATION_REQUEST:
+                    rsp = processInfRequest(query);
+                    break;
 
-            default:
-                // Only action is to output a message if debug is enabled,
-                // and that will be covered by the debug statement before
-                // the "switch" statement.
-                ;
+                default:
+                    // Only action is to output a message if debug is enabled,
+                    // and that will be covered by the debug statement before
+                    // the "switch" statement.
+                    ;
+                }
+            } catch (const RFCViolation& e) {
+                LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, DHCP6_REQUIRED_OPTIONS_CHECK_FAIL)
+                    .arg(query->getName())
+                    .arg(query->getRemoteAddr())
+                    .arg(e.what());
             }
 
             if (rsp) {
@@ -195,9 +203,6 @@ bool Dhcpv6Srv::run() {
                 }
             }
         }
-
-        // TODO add support for config session (see src/bin/auth/main.cc)
-        //      so this daemon can be controlled from bob
     }
 
     return (true);
@@ -348,13 +353,77 @@ void Dhcpv6Srv::appendRequestedOptions(const Pkt6Ptr& question, Pkt6Ptr& answer)
 }
 
 OptionPtr Dhcpv6Srv::createStatusCode(uint16_t code, const std::string& text) {
+    // @todo This function uses OptionCustom class to manage contents
+    // of the data fields. Since this this option is frequently used
+    // it may be good to implement dedicated class to avoid performance
+    // impact.
+
+    // Get the definition of the option holding status code.
+    OptionDefinitionPtr status_code_def =
+        LibDHCP::getOptionDef(Option::V6, D6O_STATUS_CODE);
+    // This definition is assumed to be initialized in LibDHCP.
+    assert(status_code_def);
+
+    // As there is no dedicated class to represent Status Code
+    // the OptionCustom class should be returned here.
+    boost::shared_ptr<OptionCustom> option_status =
+        boost::dynamic_pointer_cast<
+            OptionCustom>(status_code_def->optionFactory(Option::V6, D6O_STATUS_CODE));
+    assert(option_status);
+
+    // Set status code to 'code' (0 - means data field #0).
+    option_status->writeInteger(code, 0);
+    // Set a message (1 - means data field #1).
+    option_status->writeString(text, 1);
+    return (option_status);
+}
+
+void Dhcpv6Srv::sanityCheck(const Pkt6Ptr& pkt, RequirementLevel clientid,
+                            RequirementLevel serverid) {
+    Option::OptionCollection client_ids = pkt->getOptions(D6O_CLIENTID);
+    switch (clientid) {
+    case MANDATORY:
+        if (client_ids.size() != 1) {
+            isc_throw(RFCViolation, "Exactly 1 client-id option expected in "
+                      << pkt->getName() << ", but " << client_ids.size()
+                      << " received");
+        }
+        break;
+    case OPTIONAL:
+        if (client_ids.size() > 1) {
+            isc_throw(RFCViolation, "Too many (" << client_ids.size()
+                      << ") client-id options received in " << pkt->getName());
+        }
+        break;
+
+    case FORBIDDEN:
+        // doesn't make sense - client-id is always allowed
+        break;
+    }
 
-    // @todo: Implement Option6_StatusCode and rewrite this code here
-    vector<uint8_t> data(text.c_str(), text.c_str() + text.length());
-    data.insert(data.begin(), static_cast<uint8_t>(code % 256));
-    data.insert(data.begin(), static_cast<uint8_t>(code >> 8));
-    OptionPtr status(new Option(Option::V6, D6O_STATUS_CODE, data));
-    return (status);
+    Option::OptionCollection server_ids = pkt->getOptions(D6O_SERVERID);
+    switch (serverid) {
+    case FORBIDDEN:
+        if (server_ids.size() > 0) {
+            isc_throw(RFCViolation, "Exactly 1 server-id option expected, but "
+                      << server_ids.size() << " received in " << pkt->getName());
+        }
+        break;
+
+    case MANDATORY:
+        if (server_ids.size() != 1) {
+            isc_throw(RFCViolation, "Invalid number of server-id options received ("
+                      << server_ids.size() << "), exactly 1 expected in message "
+                      << pkt->getName());
+        }
+        break;
+
+    case OPTIONAL:
+        if (server_ids.size() > 1) {
+            isc_throw(RFCViolation, "Too many (" << server_ids.size()
+                      << ") server-id options received in " << pkt->getName());
+        }
+    }
 }
 
 Subnet6Ptr Dhcpv6Srv::selectSubnet(const Pkt6Ptr& question) {
@@ -370,7 +439,7 @@ void Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer) {
 
     // We need to select a subnet the client is connected in.
     Subnet6Ptr subnet = selectSubnet(question);
-    if (subnet) {
+    if (!subnet) {
         // This particular client is out of luck today. We do not have
         // information about the subnet he is connected to. This likely means
         // misconfiguration of the server (or some relays). We will continue to
@@ -378,12 +447,13 @@ void Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer) {
         // addresses or prefixes, no subnet specific configuration etc. The only
         // thing this client can get is some global information (like DNS
         // servers).
-        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL_DATA, DHCP6_SUBNET_SELECTED)
-            .arg(subnet->toText());
-    } else {
+
         // perhaps this should be logged on some higher level? This is most likely
         // configuration bug.
-        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, DHCP6_SUBNET_SELECTION_FAILED);
+        LOG_ERROR(dhcp6_logger, DHCP6_SUBNET_SELECTION_FAILED);
+    } else {
+        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL_DATA, DHCP6_SUBNET_SELECTED)
+            .arg(subnet->toText());
     }
 
     // @todo: We should implement Option6Duid some day, but we can do without it
@@ -397,6 +467,10 @@ void Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer) {
     OptionPtr opt_duid = question->getOption(D6O_CLIENTID);
     if (opt_duid) {
         duid = DuidPtr(new DUID(opt_duid->getData()));
+    } else {
+        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, DHCP6_CLIENTID_MISSING);
+        // Let's drop the message. This client is not sane.
+        isc_throw(RFCViolation, "Mandatory client-id is missing in received message");
     }
 
     // Now that we have all information about the client, let's iterate over all
@@ -409,7 +483,7 @@ void Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer) {
          opt != question->options_.end(); ++opt) {
         switch (opt->second->getType()) {
         case D6O_IA_NA: {
-            OptionPtr answer_opt = handleIA_NA(subnet, duid, question,
+            OptionPtr answer_opt = assignIA_NA(subnet, duid, question,
                                    boost::dynamic_pointer_cast<Option6IA>(opt->second));
             if (answer_opt) {
                 answer->addOption(answer_opt);
@@ -422,14 +496,18 @@ void Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer) {
     }
 }
 
-OptionPtr Dhcpv6Srv::handleIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid, Pkt6Ptr question,
-                                 boost::shared_ptr<Option6IA> ia) {
+OptionPtr Dhcpv6Srv::assignIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
+                                 Pkt6Ptr question, boost::shared_ptr<Option6IA> ia) {
     // If there is no subnet selected for handling this IA_NA, the only thing to do left is
     // to say that we are sorry, but the user won't get an address. As a convenience, we
     // use a different status text to indicate that (compare to the same status code,
     // but different wording below)
     if (!subnet) {
         // Create empty IA_NA option with IAID matching the request.
+        // Note that we don't use OptionDefinition class to create this option.
+        // This is because we prefer using a constructor of Option6IA that
+        // initializes IAID. Otherwise we would have to use setIAID() after
+        // creation of the option which has some performance implications.
         boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
 
         // Insert status code NoAddrsAvail.
@@ -471,6 +549,8 @@ OptionPtr Dhcpv6Srv::handleIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
                                                       hint, fake_allocation);
 
     // Create IA_NA that we will put in the response.
+    // Do not use OptionDefinition to create option's instance so
+    // as we can initialize IAID using a constructor.
     boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
 
     if (lease) {
@@ -512,8 +592,111 @@ OptionPtr Dhcpv6Srv::handleIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
     return (ia_rsp);
 }
 
+OptionPtr Dhcpv6Srv::renewIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
+                                Pkt6Ptr question, boost::shared_ptr<Option6IA> ia) {
+    Lease6Ptr lease = LeaseMgrFactory::instance().getLease6(*duid, ia->getIAID(),
+                                                            subnet->getID());
+
+    if (!lease) {
+        // client renewing a lease that we don't know about.
+
+        // Create empty IA_NA option with IAID matching the request.
+        boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
+
+        // Insert status code NoAddrsAvail.
+        ia_rsp->addOption(createStatusCode(STATUS_NoAddrsAvail,
+                          "Sorry, no known leases for this duid/iaid."));
+
+        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_UNKNOWN_RENEW)
+            .arg(duid->toText())
+            .arg(ia->getIAID())
+            .arg(subnet->toText());
+
+        return (ia_rsp);
+    }
+
+    lease->preferred_lft_ = subnet->getPreferred();
+    lease->valid_lft_ = subnet->getValid();
+    lease->t1_ = subnet->getT1();
+    lease->t2_ = subnet->getT2();
+    lease->cltt_ = time(NULL);
+
+    LeaseMgrFactory::instance().updateLease6(lease);
+
+    // Create empty IA_NA option with IAID matching the request.
+    boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
+
+    ia_rsp->setT1(subnet->getT1());
+    ia_rsp->setT2(subnet->getT2());
+
+    boost::shared_ptr<Option6IAAddr> addr(new Option6IAAddr(D6O_IAADDR,
+                                          lease->addr_, lease->preferred_lft_,
+                                          lease->valid_lft_));
+    ia_rsp->addOption(addr);
+    return (ia_rsp);
+}
+
+void Dhcpv6Srv::renewLeases(const Pkt6Ptr& renew, Pkt6Ptr& reply) {
+
+    // We need to renew addresses for all IA_NA options in the client's
+    // RENEW message.
+
+    // We need to select a subnet the client is connected in.
+    Subnet6Ptr subnet = selectSubnet(renew);
+    if (!subnet) {
+        // This particular client is out of luck today. We do not have
+        // information about the subnet he is connected to. This likely means
+        // misconfiguration of the server (or some relays). We will continue to
+        // process this message, but our response will be almost useless: no
+        // addresses or prefixes, no subnet specific configuration etc. The only
+        // thing this client can get is some global information (like DNS
+        // servers).
+
+        // perhaps this should be logged on some higher level? This is most likely
+        // configuration bug.
+        LOG_ERROR(dhcp6_logger, DHCP6_SUBNET_SELECTION_FAILED);
+    } else {
+        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL_DATA, DHCP6_SUBNET_SELECTED)
+            .arg(subnet->toText());
+    }
+
+    // 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 = renew->getOption(D6O_CLIENTID);
+    if (!opt_duid) {
+        // This should not happen. We have checked this before.
+        reply->addOption(createStatusCode(STATUS_UnspecFail,
+                         "You did not include mandatory client-id"));
+        return;
+    }
+    DuidPtr duid(new DUID(opt_duid->getData()));
+
+    for (Option::OptionCollection::iterator opt = renew->options_.begin();
+         opt != renew->options_.end(); ++opt) {
+        switch (opt->second->getType()) {
+        case D6O_IA_NA: {
+            OptionPtr answer_opt = renewIA_NA(subnet, duid, renew,
+                                   boost::dynamic_pointer_cast<Option6IA>(opt->second));
+            if (answer_opt) {
+                reply->addOption(answer_opt);
+            }
+            break;
+        }
+        default:
+            break;
+        }
+    }
+
+
+
+}
+
 Pkt6Ptr Dhcpv6Srv::processSolicit(const Pkt6Ptr& solicit) {
 
+    sanityCheck(solicit, MANDATORY, FORBIDDEN);
+
     Pkt6Ptr advertise(new Pkt6(DHCPV6_ADVERTISE, solicit->getTransid()));
 
     copyDefaultOptions(solicit, advertise);
@@ -526,6 +709,9 @@ Pkt6Ptr Dhcpv6Srv::processSolicit(const Pkt6Ptr& solicit) {
 }
 
 Pkt6Ptr Dhcpv6Srv::processRequest(const Pkt6Ptr& request) {
+
+    sanityCheck(request, MANDATORY, MANDATORY);
+
     Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, request->getTransid()));
 
     copyDefaultOptions(request, reply);
@@ -538,8 +724,17 @@ Pkt6Ptr Dhcpv6Srv::processRequest(const Pkt6Ptr& request) {
 }
 
 Pkt6Ptr Dhcpv6Srv::processRenew(const Pkt6Ptr& renew) {
-    /// @todo: Implement this
+
+    sanityCheck(renew, MANDATORY, MANDATORY);
+
     Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, renew->getTransid()));
+
+    copyDefaultOptions(renew, reply);
+    appendDefaultOptions(renew, reply);
+    appendRequestedOptions(renew, reply);
+
+    renewLeases(renew, reply);
+
     return reply;
 }
 
@@ -573,48 +768,5 @@ Pkt6Ptr Dhcpv6Srv::processInfRequest(const Pkt6Ptr& infRequest) {
     return reply;
 }
 
-const char*
-Dhcpv6Srv::serverReceivedPacketName(uint8_t type) {
-    static const char* CONFIRM = "CONFIRM";
-    static const char* DECLINE = "DECLINE";
-    static const char* INFORMATION_REQUEST = "INFORMATION_REQUEST";
-    static const char* REBIND = "REBIND";
-    static const char* RELEASE = "RELEASE";
-    static const char* RENEW = "RENEW";
-    static const char* REQUEST = "REQUEST";
-    static const char* SOLICIT = "SOLICIT";
-    static const char* UNKNOWN = "UNKNOWN";
-
-    switch (type) {
-    case DHCPV6_CONFIRM:
-        return (CONFIRM);
-
-    case DHCPV6_DECLINE:
-        return (DECLINE);
-
-    case DHCPV6_INFORMATION_REQUEST:
-        return (INFORMATION_REQUEST);
-
-    case DHCPV6_REBIND:
-        return (REBIND);
-
-    case DHCPV6_RELEASE:
-        return (RELEASE);
-
-    case DHCPV6_RENEW:
-        return (RENEW);
-
-    case DHCPV6_REQUEST:
-        return (REQUEST);
-
-    case DHCPV6_SOLICIT:
-        return (SOLICIT);
-
-    default:
-        ;
-    }
-    return (UNKNOWN);
-}
-
 };
 };
diff --git a/src/bin/dhcp6/dhcp6_srv.h b/src/bin/dhcp6/dhcp6_srv.h
index de9ee36..b6b0306 100644
--- a/src/bin/dhcp6/dhcp6_srv.h
+++ b/src/bin/dhcp6/dhcp6_srv.h
@@ -30,6 +30,23 @@
 
 namespace isc {
 namespace dhcp {
+
+/// An exception that is thrown if a DHCPv6 protocol violation occurs while
+/// processing a message (e.g. a mandatory option is missing)
+class RFCViolation : public isc::Exception {
+public:
+
+/// @brief constructor
+///
+/// @param file name of the file, where exception occurred
+/// @param line line of the file, where exception occurred
+/// @param what text description of the issue that caused exception
+RFCViolation(const char* file, size_t line, const char* what) :
+    isc::Exception(file, line, what) {}
+};
+
+
+
 /// @brief DHCPv6 server service.
 ///
 /// This class represents DHCPv6 server. It contains all
@@ -45,6 +62,12 @@ namespace dhcp {
 class Dhcpv6Srv : public boost::noncopyable {
 
 public:
+    /// @brief defines if certain option may, must or must not appear
+    typedef enum {
+        FORBIDDEN,
+        MANDATORY,
+        OPTIONAL
+    } RequirementLevel;
 
     /// @brief Minimum length of a MAC address to be used in DUID generation.
     static const size_t MIN_MAC_LEN = 6;
@@ -83,24 +106,20 @@ public:
     /// @brief Instructs the server to shut down.
     void shutdown();
 
-    /// @brief Return textual type of packet received by server.
-    ///
-    /// Returns the name of valid packet received by the server (e.g. SOLICIT).
-    /// If the packet is unknown - or if it is a valid DHCP packet but not one
-    /// expected to be received by the server (such as an ADVERTISE), the string
-    /// "UNKNOWN" is returned.  This method is used in debug messages.
-    ///
-    /// As the operation of the method does not depend on any server state, it
-    /// is declared static.
+protected:
+
+    /// @brief verifies if specified packet meets RFC requirements
     ///
-    /// @param type DHCPv4 packet type
+    /// Checks if mandatory option is really there, that forbidden option
+    /// is not there, and that client-id or server-id appears only once.
     ///
-    /// @return Pointer to "const" string containing the packet name.
-    ///         Note that this string is statically allocated and MUST NOT
-    ///         be freed by the caller.
-    static const char* serverReceivedPacketName(uint8_t type);
+    /// @param pkt packet to be checked
+    /// @param clientid expectation regarding client-id option
+    /// @param serverid expectation regarding server-id option
+    /// @throw RFCViolation if any issues are detected
+    void sanityCheck(const Pkt6Ptr& pkt, RequirementLevel clientid,
+                     RequirementLevel serverid);
 
-protected:
     /// @brief Processes incoming SOLICIT and returns response.
     ///
     /// Processes received SOLICIT message and verifies that its sender
@@ -186,11 +205,24 @@ protected:
     /// @param question client's message (typically SOLICIT or REQUEST)
     /// @param ia pointer to client's IA_NA option (client's request)
     /// @return IA_NA option (server's response)
-    OptionPtr handleIA_NA(const isc::dhcp::Subnet6Ptr& subnet,
+    OptionPtr assignIA_NA(const isc::dhcp::Subnet6Ptr& subnet,
                           const isc::dhcp::DuidPtr& duid,
                           isc::dhcp::Pkt6Ptr question,
                           boost::shared_ptr<Option6IA> ia);
 
+    /// @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.
+    ///
+    /// @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
+    OptionPtr renewIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
+                         Pkt6Ptr question, 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)
@@ -221,14 +253,24 @@ protected:
 
     /// @brief Assigns leases.
     ///
-    /// TODO: This method is currently a stub. It just appends one
-    /// hardcoded lease. It supports addresses (IA_NA) only. It does NOT
-    /// support temporary addresses (IA_TA) nor prefixes (IA_PD).
+    /// It supports addresses (IA_NA) only. It does NOT support temporary
+    /// addresses (IA_TA) nor prefixes (IA_PD).
+    /// @todo: Extend this method once TA and PD becomes supported
     ///
     /// @param question client's message (with requested IA_NA)
     /// @param answer server's message (IA_NA options will be added here)
     void assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer);
 
+    /// @brief Attempts to renew received addresses
+    ///
+    /// It iterates through received IA_NA options and attempts to renew
+    /// received addresses. If no such leases are found, proper status
+    /// code is added to reply message. Renewed addresses are added
+    /// as IA_NA/IAADDR to reply packet.
+    /// @param renew client's message asking for renew
+    /// @param reply server's response
+    void renewLeases(const Pkt6Ptr& renew, Pkt6Ptr& reply);
+
     /// @brief Sets server-identifier.
     ///
     /// This method attempts to set server-identifier DUID. It loads it
diff --git a/src/bin/dhcp6/main.cc b/src/bin/dhcp6/main.cc
index ae299e5..ced7422 100644
--- a/src/bin/dhcp6/main.cc
+++ b/src/bin/dhcp6/main.cc
@@ -17,6 +17,7 @@
 #include <dhcp6/ctrl_dhcp6_srv.h>
 #include <dhcp6/dhcp6_log.h>
 #include <log/logger_support.h>
+#include <log/logger_manager.h>
 
 #include <boost/lexical_cast.hpp>
 
@@ -103,9 +104,10 @@ main(int argc, char* argv[]) {
     }
 
     // Initialize logging.  If verbose, we'll use maximum verbosity.
+    // If standalone is enabled, do not buffer initial log messages
     isc::log::initLogger(DHCP6_NAME,
                          (verbose_mode ? isc::log::DEBUG : isc::log::INFO),
-                         isc::log::MAX_DEBUG_LEVEL, NULL);
+                         isc::log::MAX_DEBUG_LEVEL, NULL, !stand_alone);
     LOG_INFO(dhcp6_logger, DHCP6_STARTING);
     LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_START_INFO)
               .arg(getpid()).arg(port_number).arg(verbose_mode ? "yes" : "no")
@@ -119,8 +121,12 @@ main(int argc, char* argv[]) {
                 server.establishSession();
             } catch (const std::exception& ex) {
                 LOG_ERROR(dhcp6_logger, DHCP6_SESSION_FAIL).arg(ex.what());
-                // Let's continue. It is useful to have the ability to run 
+                // Let's continue. It is useful to have the ability to run
                 // DHCP server in stand-alone mode, e.g. for testing
+                // We do need to make sure logging is no longer buffered
+                // since then it would not print until dhcp6 is stopped
+                isc::log::LoggerManager log_manager;
+                log_manager.process();
             }
         } else {
             LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_STANDALONE);
diff --git a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
index 335fb7b..de0dc28 100644
--- a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
+++ b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
@@ -19,6 +19,7 @@
 #include <dhcp/dhcp6.h>
 #include <dhcp/duid.h>
 #include <dhcp/option.h>
+#include <dhcp/option_custom.h>
 #include <dhcp/option6_addrlst.h>
 #include <dhcp/option6_ia.h>
 #include <dhcp/option6_iaaddr.h>
@@ -57,8 +58,10 @@ public:
 
     using Dhcpv6Srv::processSolicit;
     using Dhcpv6Srv::processRequest;
+    using Dhcpv6Srv::processRenew;
     using Dhcpv6Srv::createStatusCode;
     using Dhcpv6Srv::selectSubnet;
+    using Dhcpv6Srv::sanityCheck;
 };
 
 class Dhcpv6SrvTest : public ::testing::Test {
@@ -139,6 +142,33 @@ public:
         return (addr);
     }
 
+    // Checks that server rejected IA_NA, i.e. that it has no addresses and
+    // that expected status code really appears there.
+    // 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,
+                            uint16_t expected_status_code) {
+        // Make sure there is no address assigned.
+        EXPECT_FALSE(ia->getOption(D6O_IAADDR));
+
+        // T1, T2 should be zeroed
+        EXPECT_EQ(0, ia->getT1());
+        EXPECT_EQ(0, ia->getT2());
+
+        boost::shared_ptr<OptionCustom> status =
+            boost::dynamic_pointer_cast<OptionCustom>(ia->getOption(D6O_STATUS_CODE));
+        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_code),
+                      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,
@@ -617,7 +647,6 @@ TEST_F(Dhcpv6SrvTest, ManySolicits) {
     cout << "Offered address to client3=" << addr3->getAddress().toText() << endl;
 }
 
-
 // This test verifies that incoming REQUEST can be handled properly, that a
 // REPLY is generated, that the response has an address and that address
 // really belongs to the configured pool.
@@ -651,6 +680,9 @@ TEST_F(Dhcpv6SrvTest, RequestBasic) {
     OptionPtr clientid = generateClientId();
     req->addOption(clientid);
 
+    // server-id is mandatory in REQUEST
+    req->addOption(srv->getServerID());
+
     // Pass it to the server and hope for a REPLY
     Pkt6Ptr reply = srv->processRequest(req);
 
@@ -674,7 +706,7 @@ TEST_F(Dhcpv6SrvTest, RequestBasic) {
     // check that the lease is really in the database
     Lease6Ptr l = checkLease(duid_, reply->getOption(D6O_IA_NA), addr);
     EXPECT_TRUE(l);
-    LeaseMgrFactory::instance().deleteLease6(addr->getAddress());
+    LeaseMgrFactory::instance().deleteLease(addr->getAddress());
 }
 
 // This test checks that the server is offering different addresses to different
@@ -709,6 +741,11 @@ TEST_F(Dhcpv6SrvTest, ManyRequests) {
     req2->addOption(clientid2);
     req3->addOption(clientid3);
 
+    // server-id is mandatory in REQUEST
+    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);
@@ -749,50 +786,207 @@ TEST_F(Dhcpv6SrvTest, ManyRequests) {
     cout << "Assigned address to client3=" << addr3->getAddress().toText() << endl;
 }
 
+// This test verifies that incoming (positive) RENEW can be handled properly, that a
+// REPLY is generated, that the response has an address and that address
+// really belongs to the configured pool and that lease is actually renewed.
+//
+// expected:
+// - returned REPLY message has copy of client-id
+// - returned REPLY message has server-id
+// - 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)) );
 
-TEST_F(Dhcpv6SrvTest, serverReceivedPacketName) {
-    // Check all possible packet types
-    for (int itype = 0; itype < 256; ++itype) {
-        uint8_t type = itype;
+    const IOAddress addr("2001:db8:1:1::cafe:babe");
+    const uint32_t iaid = 234;
 
-        switch (type) {
-        case DHCPV6_CONFIRM:
-            EXPECT_STREQ("CONFIRM", Dhcpv6Srv::serverReceivedPacketName(type));
-            break;
+    // Generate client-id also duid_
+    OptionPtr clientid = generateClientId();
 
-        case DHCPV6_DECLINE:
-            EXPECT_STREQ("DECLINE", Dhcpv6Srv::serverReceivedPacketName(type));
-            break;
+    // 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);
+
+    // Check that T1, T2, preferred, valid and cltt really set and not using
+    // previous (500, 501, etc.) values
+    EXPECT_NE(l->t1_, subnet_->getT1());
+    EXPECT_NE(l->t2_, subnet_->getT2());
+    EXPECT_NE(l->preferred_lft_, subnet_->getPreferred());
+    EXPECT_NE(l->valid_lft_, subnet_->getValid());
+    EXPECT_NE(l->cltt_, time(NULL));
+
+    // Let's create a RENEW
+    Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RENEW, 1234));
+    req->setRemoteAddr(IOAddress("fe80::abcd"));
+    boost::shared_ptr<Option6IA> ia = generateIA(iaid, 1500, 3000);
 
-        case DHCPV6_INFORMATION_REQUEST:
-            EXPECT_STREQ("INFORMATION_REQUEST",
-                         Dhcpv6Srv::serverReceivedPacketName(type));
-            break;
+    OptionPtr renewed_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
+    ia->addOption(renewed_addr_opt);
+    req->addOption(ia);
+    req->addOption(clientid);
 
-        case DHCPV6_REBIND:
-            EXPECT_STREQ("REBIND", Dhcpv6Srv::serverReceivedPacketName(type));
-            break;
+    // Server-id is mandatory in RENEW
+    req->addOption(srv->getServerID());
 
-        case DHCPV6_RELEASE:
-            EXPECT_STREQ("RELEASE", Dhcpv6Srv::serverReceivedPacketName(type));
-            break;
+    // Pass it to the server and hope for a REPLY
+    Pkt6Ptr reply = srv->processRenew(req);
 
-        case DHCPV6_RENEW:
-            EXPECT_STREQ("RENEW", Dhcpv6Srv::serverReceivedPacketName(type));
-            break;
+    // Check if we get response at all
+    checkResponse(reply, DHCPV6_REPLY, 1234);
 
-        case DHCPV6_REQUEST:
-            EXPECT_STREQ("REQUEST", Dhcpv6Srv::serverReceivedPacketName(type));
-            break;
+    OptionPtr tmp = reply->getOption(D6O_IA_NA);
+    ASSERT_TRUE(tmp);
 
-        case DHCPV6_SOLICIT:
-            EXPECT_STREQ("SOLICIT", Dhcpv6Srv::serverReceivedPacketName(type));
-            break;
+    // Check that IA_NA was returned and that there's an address included
+    boost::shared_ptr<Option6IAAddr> addr_opt = checkIA_NA(reply, 234, subnet_->getT1(),
+                                                           subnet_->getT2());
 
-        default:
-            EXPECT_STREQ("UNKNOWN", Dhcpv6Srv::serverReceivedPacketName(type));
-        }
-    }
+    // Check that we've got the address we requested
+    checkIAAddr(addr_opt, addr, subnet_->getPreferred(), subnet_->getValid());
+
+    // Check DUIDs
+    checkServerId(reply, srv->getServerID());
+    checkClientId(reply, clientid);
+
+    // Check that the lease is really in the database
+    l = checkLease(duid_, reply->getOption(D6O_IA_NA), addr_opt);
+    ASSERT_TRUE(l);
+
+    // Check that T1, T2, preferred, valid and cltt were really updated
+    EXPECT_EQ(l->t1_, subnet_->getT1());
+    EXPECT_EQ(l->t2_, subnet_->getT2());
+    EXPECT_EQ(l->preferred_lft_, subnet_->getPreferred());
+    EXPECT_EQ(l->valid_lft_, subnet_->getValid());
+
+    // Checking for CLTT is a bit tricky if we want to avoid off by 1 errors
+    int32_t cltt = static_cast<int32_t>(l->cltt_);
+    int32_t expected = static_cast<int32_t>(time(NULL));
+    // equality or difference by 1 between cltt and expected is ok.
+    EXPECT_GE(1, abs(cltt - expected));
+
+    EXPECT_TRUE(LeaseMgrFactory::instance().deleteLease(addr_opt->getAddress()));
+}
+
+// This test verifies that incoming (invalid) RENEW 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, RenewReject) {
+
+    boost::scoped_ptr<NakedDhcpv6Srv> srv;
+    ASSERT_NO_THROW( srv.reset(new NakedDhcpv6Srv(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 RENEW
+    Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RENEW, transid));
+    req->setRemoteAddr(IOAddress("fe80::abcd"));
+    boost::shared_ptr<Option6IA> ia = generateIA(bogus_iaid, 1500, 3000);
+
+    OptionPtr renewed_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
+    ia->addOption(renewed_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
+
+    // Pass it to the server and hope for a REPLY
+    Pkt6Ptr reply = srv->processRenew(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);
+    checkRejectedIA_NA(ia, STATUS_NoAddrsAvail);
+
+    // Check that there is no lease added
+    l = LeaseMgrFactory::instance().getLease6(addr);
+    ASSERT_FALSE(l);
+
+    // CASE 2: Lease is known and belongs to this client, but to a different IAID
+
+    // 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_, valid_iaid,
+                               501, 502, 503, 504, subnet_->getID(), 0));
+    lease->cltt_ = 123; // Let's use it as an indicator that the lease
+                        // was NOT updated.
+    ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
+
+    // Pass it to the server and hope for a REPLY
+    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);
+
+    // There is a iaid mis-match, so server should respond that there is
+    // no such address to renew.
+
+    // 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->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);
+
+    lease = LeaseMgrFactory::instance().getLease6(addr);
+    ASSERT_TRUE(lease);
+    // Verify that the lease was not updated.
+    EXPECT_EQ(123, lease->cltt_);
+
+    EXPECT_TRUE(LeaseMgrFactory::instance().deleteLease(addr));
 }
 
 // This test verifies if the status code option is generated properly.
@@ -801,16 +995,27 @@ TEST_F(Dhcpv6SrvTest, StatusCode) {
     ASSERT_NO_THROW( srv.reset(new NakedDhcpv6Srv(0)) );
 
     // a dummy content for client-id
-    uint8_t expected[] = {0x0, 0x3, 0x41, 0x42, 0x43, 0x44, 0x45};
-    OptionBuffer exp(expected, expected + sizeof(expected));
-
+    uint8_t expected[] = {
+        0x0, 0xD, // option code = 13
+        0x0, 0x7, // option length = 7
+        0x0, 0x3, // status code = 3
+        0x41, 0x42, 0x43, 0x44, 0x45 // string value ABCDE
+    };
+    // Create the option.
     OptionPtr status = srv->createStatusCode(3, "ABCDE");
-
-    EXPECT_TRUE(status->getData() == exp);
+    // Allocate an output buffer. We will store the option
+    // in wire format here.
+    OutputBuffer buf(sizeof(expected));
+    // Prepare the wire format.
+    ASSERT_NO_THROW(status->pack(buf));
+    // Check that the option buffer has valid length (option header + data).
+    ASSERT_EQ(sizeof(expected), buf.getLength());
+    // Verify the contents of the option.
+    EXPECT_EQ(0, memcmp(expected, buf.getData(), sizeof(expected)));
 }
 
-// This test verifies if the selectSubnet() method works as expected.
-TEST_F(Dhcpv6SrvTest, SelectSubnet) {
+// 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)) );
 
@@ -818,18 +1023,63 @@ TEST_F(Dhcpv6SrvTest, SelectSubnet) {
 
     // check that the packets originating from local addresses can be
     pkt->setRemoteAddr(IOAddress("fe80::abcd"));
-    EXPECT_EQ(subnet_, srv->selectSubnet(pkt));
 
-    // packets originating from subnet A will select subnet A
-    pkt->setRemoteAddr(IOAddress("2001:db8:1::6789"));
-    EXPECT_EQ(subnet_, srv->selectSubnet(pkt));
+    // client-id is optional for information-request, so
+    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),
+                 RFCViolation);
+
+    // This doesn't make much sense, but let's check it for completeness
+    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));
+
+    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));
+
+    // sane section ends here, let's do some negative tests as well
+
+    pkt->addOption(clientid);
+    pkt->addOption(clientid);
+
+    // with more than one client-id it should throw, no matter what
+    EXPECT_THROW(srv->sanityCheck(pkt, Dhcpv6Srv::OPTIONAL, Dhcpv6Srv::OPTIONAL),
+                 RFCViolation);
+    EXPECT_THROW(srv->sanityCheck(pkt, Dhcpv6Srv::MANDATORY, Dhcpv6Srv::OPTIONAL),
+                 RFCViolation);
+    EXPECT_THROW(srv->sanityCheck(pkt, Dhcpv6Srv::OPTIONAL, Dhcpv6Srv::MANDATORY),
+                 RFCViolation);
+    EXPECT_THROW(srv->sanityCheck(pkt, Dhcpv6Srv::MANDATORY, Dhcpv6Srv::MANDATORY),
+                 RFCViolation);
+
+    pkt->delOption(D6O_CLIENTID);
+    pkt->delOption(D6O_CLIENTID);
+
+    // 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());
+
+    // with more than one server-id it should throw, no matter what
+    EXPECT_THROW(srv->sanityCheck(pkt, Dhcpv6Srv::OPTIONAL, Dhcpv6Srv::OPTIONAL),
+                 RFCViolation);
+    EXPECT_THROW(srv->sanityCheck(pkt, Dhcpv6Srv::MANDATORY, Dhcpv6Srv::OPTIONAL),
+                 RFCViolation);
+    EXPECT_THROW(srv->sanityCheck(pkt, Dhcpv6Srv::OPTIONAL, Dhcpv6Srv::MANDATORY),
+                 RFCViolation);
+    EXPECT_THROW(srv->sanityCheck(pkt, Dhcpv6Srv::MANDATORY, Dhcpv6Srv::MANDATORY),
+                 RFCViolation);
 
-    // packets from a subnet that is not supported will not get
-    // a subnet
-    pkt->setRemoteAddr(IOAddress("3000::faf"));
-    EXPECT_FALSE(srv->selectSubnet(pkt));
 
-    /// @todo: expand this test once support for relays is implemented
 }
 
 }   // end of anonymous namespace
diff --git a/src/bin/resolver/main.cc b/src/bin/resolver/main.cc
index d14fb0b..457f285 100644
--- a/src/bin/resolver/main.cc
+++ b/src/bin/resolver/main.cc
@@ -143,7 +143,7 @@ main(int argc, char* argv[]) {
     // temporary initLogger() code.  If verbose, we'll use maximum verbosity.
     isc::log::initLogger(RESOLVER_NAME,
                          (verbose ? isc::log::DEBUG : isc::log::INFO),
-                         isc::log::MAX_DEBUG_LEVEL, NULL);
+                         isc::log::MAX_DEBUG_LEVEL, NULL, true);
 
     // Print the starting message
     string cmdline = argv[0];
@@ -177,7 +177,7 @@ main(int argc, char* argv[]) {
 
         isc::cache::ResolverCache cache;
         resolver->setCache(cache);
-        
+
         // TODO priming query, remove root from direct
         // Fake a priming query result here (TODO2 how to flag non-expiry?)
         // propagation to runningquery. And check for forwarder mode?
@@ -185,21 +185,21 @@ main(int argc, char* argv[]) {
                                             isc::dns::Name("."),
                                             isc::dns::RRClass::IN(),
                                             isc::dns::RRType::NS()));
-        isc::dns::RRsetPtr root_ns_rrset(new isc::dns::RRset(isc::dns::Name("."), 
+        isc::dns::RRsetPtr root_ns_rrset(new isc::dns::RRset(isc::dns::Name("."),
                                          isc::dns::RRClass::IN(),
                                          isc::dns::RRType::NS(),
                                          isc::dns::RRTTL(8888)));
         root_ns_rrset->addRdata(isc::dns::rdata::createRdata(isc::dns::RRType::NS(),
                                                              isc::dns::RRClass::IN(),
                                                              "l.root-servers.net."));
-        isc::dns::RRsetPtr root_a_rrset(new isc::dns::RRset(isc::dns::Name("l.root-servers.net"), 
+        isc::dns::RRsetPtr root_a_rrset(new isc::dns::RRset(isc::dns::Name("l.root-servers.net"),
                                         isc::dns::RRClass::IN(),
                                         isc::dns::RRType::A(),
                                         isc::dns::RRTTL(8888)));
         root_a_rrset->addRdata(isc::dns::rdata::createRdata(isc::dns::RRType::A(),
                                                              isc::dns::RRClass::IN(),
                                                              "199.7.83.42"));
-        isc::dns::RRsetPtr root_aaaa_rrset(new isc::dns::RRset(isc::dns::Name("l.root-servers.net"), 
+        isc::dns::RRsetPtr root_aaaa_rrset(new isc::dns::RRset(isc::dns::Name("l.root-servers.net"),
                                         isc::dns::RRClass::IN(),
                                         isc::dns::RRType::AAAA(),
                                         isc::dns::RRTTL(8888)));
@@ -216,7 +216,7 @@ main(int argc, char* argv[]) {
         cache.update(root_ns_rrset);
         cache.update(root_a_rrset);
         cache.update(root_aaaa_rrset);
-        
+
         DNSService dns_service(io_service, checkin, lookup, answer);
         resolver->setDNSService(dns_service);
         LOG_DEBUG(resolver_logger, RESOLVER_DBG_INIT, RESOLVER_SERVICE_CREATED);
diff --git a/src/bin/stats/stats.py.in b/src/bin/stats/stats.py.in
index 36e028a..7123c53 100755
--- a/src/bin/stats/stats.py.in
+++ b/src/bin/stats/stats.py.in
@@ -31,7 +31,7 @@ import isc.util.process
 import isc.log
 from isc.log_messages.stats_messages import *
 
-isc.log.init("b10-stats")
+isc.log.init("b10-stats", buffer=True)
 logger = isc.log.Logger("stats")
 
 # Some constants for debug levels.
@@ -682,7 +682,7 @@ if __name__ == "__main__":
             help="enable maximum debug logging")
         (options, args) = parser.parse_args()
         if options.verbose:
-            isc.log.init("b10-stats", "DEBUG", 99)
+            isc.log.init("b10-stats", "DEBUG", 99, buffer=True)
         stats = Stats()
         stats.start()
     except OptionValueError as ove:
diff --git a/src/bin/stats/stats_httpd.py.in b/src/bin/stats/stats_httpd.py.in
index 63eb0f3..057b8ca 100644
--- a/src/bin/stats/stats_httpd.py.in
+++ b/src/bin/stats/stats_httpd.py.in
@@ -39,7 +39,7 @@ import isc.util.process
 import isc.log
 from isc.log_messages.stats_httpd_messages import *
 
-isc.log.init("b10-stats-httpd")
+isc.log.init("b10-stats-httpd", buffer=True)
 logger = isc.log.Logger("stats-httpd")
 
 # Some constants for debug levels.
@@ -609,7 +609,7 @@ if __name__ == "__main__":
             help="enable maximum debug logging")
         (options, args) = parser.parse_args()
         if options.verbose:
-            isc.log.init("b10-stats-httpd", "DEBUG", 99)
+            isc.log.init("b10-stats-httpd", "DEBUG", 99, buffer=True)
         stats_httpd = StatsHttpd()
         stats_httpd.start()
     except OptionValueError as ove:
diff --git a/src/bin/xfrin/b10-xfrin.xml b/src/bin/xfrin/b10-xfrin.xml
index 231681c..eb16ab3 100644
--- a/src/bin/xfrin/b10-xfrin.xml
+++ b/src/bin/xfrin/b10-xfrin.xml
@@ -112,20 +112,18 @@ in separate zonemgr process.
       <varname>master_addr</varname> (the zone master to transfer from),
       <varname>master_port</varname> (defaults to 53),
       <varname>use_ixfr</varname> (defaults to false), and
-      <varname>tsig_key</varname> (optional TSIG key to use).
-      The <varname>tsig_key</varname> is specified using a full string
-      colon-delimited name:key:algorithm representation (e.g.
-      <quote>foo.example.org:EvABsfU2h7uofnmqaRCrhHunGsd=:hmac-sha1</quote>).
+      <varname>tsig_key</varname> (optional TSIG key name to use).
+      The <varname>tsig_key</varname> is specified using a name that
+      corresponds to one of the TSIG keys configured in the global
+      TSIG key ring (<quote>/tsig_keys/keys</quote>).
     </para>
 <!-- TODO: document this better -->
-<!-- TODO: the tsig_key format may change -->
 
     <para>
       (The site-wide <varname>master_addr</varname> and
       <varname>master_port</varname> configurations are deprecated;
       use the <varname>zones</varname> list configuration instead.)
     </para>
-<!-- NOTE: also tsig_key but not mentioning since so short lived. -->
 
 <!-- TODO: formating -->
     <para>
diff --git a/src/bin/xfrin/tests/xfrin_test.py b/src/bin/xfrin/tests/xfrin_test.py
index 4e8d6ab..b7fe056 100644
--- a/src/bin/xfrin/tests/xfrin_test.py
+++ b/src/bin/xfrin/tests/xfrin_test.py
@@ -26,6 +26,7 @@ from xfrin import *
 import xfrin
 from isc.xfrin.diff import Diff
 import isc.log
+from isc.server_common.tsig_keyring import init_keyring, get_keyring
 # If we use any python library that is basically a wrapper for
 # a library we use as well (like sqlite3 in our datasources),
 # we must make sure we import ours first; If we have special
@@ -139,6 +140,16 @@ class MockCC(MockModuleCCSession):
         if identifier == "zones/use_ixfr":
             return False
 
+    def add_remote_config_by_name(self, name, callback):
+        pass
+
+    def get_remote_config_value(self, module, identifier):
+        if module == 'tsig_keys' and identifier == 'keys':
+            return (['example.com.key.:EvAAsfU2h7uofnmqaTCrhHunGsc='], True)
+        else:
+            raise Exception('MockCC requested for unknown config value ' +
+                            + module + "/" + identifier)
+
     def remove_remote_config(self, module_name):
         pass
 
@@ -229,6 +240,7 @@ class MockXfrin(Xfrin):
     def _cc_setup(self):
         self._tsig_key = None
         self._module_cc = MockCC()
+        init_keyring(self._module_cc)
         pass
 
     def _get_db_file(self):
@@ -2427,9 +2439,10 @@ class TestXfrin(unittest.TestCase):
             self.assertEqual(str(zone_info.master_addr), zone_config['master_addr'])
             self.assertEqual(zone_info.master_port, zone_config['master_port'])
             if 'tsig_key' in zone_config:
-                self.assertEqual(zone_info.tsig_key.to_text(), TSIGKey(zone_config['tsig_key']).to_text())
+                self.assertEqual(zone_info.tsig_key_name.to_text(),
+                                 Name(zone_config['tsig_key']).to_text())
             else:
-                self.assertIsNone(zone_info.tsig_key)
+                self.assertIsNone(zone_info.tsig_key_name)
             if 'use_ixfr' in zone_config and\
                zone_config.get('use_ixfr'):
                 self.assertTrue(zone_info.use_ixfr)
@@ -2562,7 +2575,7 @@ class TestXfrin(unittest.TestCase):
                   { 'name': 'test2.example.',
                     'master_addr': '192.0.2.9',
                     'master_port': 53,
-                    'tsig_key': 'badkey'
+                    'tsig_key': 'badkey..'
                   }
                 ]}
         self.assertEqual(self.xfr.config_handler(zones)['result'][0], 1)
@@ -2581,13 +2594,14 @@ class TestXfrin(unittest.TestCase):
         self.assertEqual(self.xfr.config_handler(config)['result'][0], 0)
         self._check_zones_config(config)
 
-    def common_ixfr_setup(self, xfr_mode, use_ixfr):
+    def common_ixfr_setup(self, xfr_mode, use_ixfr, tsig_key_str = None):
         # This helper method explicitly sets up a zone configuration with
         # use_ixfr, and invokes either retransfer or refresh.
         # Shared by some of the following test cases.
         config = {'zones': [
                 {'name': 'example.com.',
                  'master_addr': '192.0.2.1',
+                 'tsig_key': tsig_key_str,
                  'use_ixfr': use_ixfr}]}
         self.assertEqual(self.xfr.config_handler(config)['result'][0], 0)
         self.assertEqual(self.xfr.command_handler(xfr_mode,
@@ -2603,6 +2617,34 @@ class TestXfrin(unittest.TestCase):
         self.common_ixfr_setup('refresh', True)
         self.assertEqual(RRType.IXFR(), self.xfr.xfrin_started_request_type)
 
+    def test_command_handler_retransfer_with_tsig(self):
+        self.common_ixfr_setup('retransfer', False, 'example.com.key')
+        self.assertEqual(RRType.AXFR(), self.xfr.xfrin_started_request_type)
+
+    def test_command_handler_retransfer_with_tsig_bad_key(self):
+        # bad keys should not reach xfrin, but should they somehow,
+        # they are ignored (and result in 'key not found' + error log).
+        self.assertRaises(XfrinZoneInfoException, self.common_ixfr_setup,
+                          'retransfer', False, 'bad.key')
+
+    def test_command_handler_retransfer_with_tsig_unknown_key(self):
+        self.assertRaises(XfrinZoneInfoException, self.common_ixfr_setup,
+                          'retransfer', False, 'no.such.key')
+
+    def test_command_handler_refresh_with_tsig(self):
+        self.common_ixfr_setup('refresh', False, 'example.com.key')
+        self.assertEqual(RRType.AXFR(), self.xfr.xfrin_started_request_type)
+
+    def test_command_handler_refresh_with_tsig_bad_key(self):
+        # bad keys should not reach xfrin, but should they somehow,
+        # they are ignored (and result in 'key not found' + error log).
+        self.assertRaises(XfrinZoneInfoException, self.common_ixfr_setup,
+                          'refresh', False, 'bad.key')
+
+    def test_command_handler_refresh_with_tsig_unknown_key(self):
+        self.assertRaises(XfrinZoneInfoException, self.common_ixfr_setup,
+                          'refresh', False, 'no.such.key')
+
     def test_command_handler_retransfer_ixfr_disabled(self):
         # Similar to the previous case, but explicitly disabled.  AXFR should
         # be used.
diff --git a/src/bin/xfrin/xfrin.py.in b/src/bin/xfrin/xfrin.py.in
index 8e92db6..da0f207 100755
--- a/src/bin/xfrin/xfrin.py.in
+++ b/src/bin/xfrin/xfrin.py.in
@@ -34,9 +34,10 @@ from isc.datasrc import DataSourceClient, ZoneFinder
 import isc.net.parse
 from isc.xfrin.diff import Diff
 from isc.server_common.auth_command import auth_loadzone_command
+from isc.server_common.tsig_keyring import init_keyring, get_keyring
 from isc.log_messages.xfrin_messages import *
 
-isc.log.init("b10-xfrin")
+isc.log.init("b10-xfrin", buffer=True)
 logger = isc.log.Logger("xfrin")
 
 # Pending system-wide debug level definitions, the ones we
@@ -69,7 +70,10 @@ AUTH_SPECFILE_LOCATION = AUTH_SPECFILE_PATH + "/auth.spec"
 
 AUTH_MODULE_NAME = 'Auth'
 XFROUT_MODULE_NAME = 'Xfrout'
+
+# Remote module and identifiers (according to their spec files)
 ZONE_MANAGER_MODULE_NAME = 'Zonemgr'
+
 REFRESH_FROM_ZONEMGR = 'refresh_from_zonemgr'
 
 # Constants for debug levels.
@@ -1179,7 +1183,7 @@ class ZoneInfo:
 
         self.set_master_port(config_data.get('master_port'))
         self.set_zone_class(config_data.get('class'))
-        self.set_tsig_key(config_data.get('tsig_key'))
+        self.set_tsig_key_name(config_data.get('tsig_key'))
         self.set_use_ixfr(config_data.get('use_ixfr'))
 
     def set_name(self, name_str):
@@ -1240,20 +1244,32 @@ class ZoneInfo:
                 errmsg = "invalid zone class: " + zone_class_str
                 raise XfrinZoneInfoException(errmsg)
 
-    def set_tsig_key(self, tsig_key_str):
-        """Set the tsig_key for this zone, given a TSIG key string
-           representation. If tsig_key_str is None, no TSIG key will
-           be set. Raises XfrinZoneInfoException if tsig_key_str cannot
-           be parsed."""
+    def set_tsig_key_name(self, tsig_key_str):
+        """Set the name of the tsig_key for this zone. If tsig_key_str
+           is None, no TSIG key will be used. This name is used to
+           find the TSIG key to use for transfers in the global TSIG
+           key ring.
+           Raises XfrinZoneInfoException if tsig_key_str is not a valid
+           (dns) name."""
         if tsig_key_str is None:
-            self.tsig_key = None
+            self.tsig_key_name = None
         else:
+            # can throw a number of exceptions but it is just one
+            # call, so Exception should be OK here
             try:
-                self.tsig_key = TSIGKey(tsig_key_str)
-            except InvalidParameter as ipe:
-                logger.error(XFRIN_BAD_TSIG_KEY_STRING, tsig_key_str)
-                errmsg = "bad TSIG key string: " + tsig_key_str
-                raise XfrinZoneInfoException(errmsg)
+                self.tsig_key_name = Name(tsig_key_str)
+            except Exception as exc:
+                raise XfrinZoneInfoException("Bad TSIG key name: " + str(exc))
+
+    def get_tsig_key(self):
+        if self.tsig_key_name is None:
+            return None
+        result, key = get_keyring().find(self.tsig_key_name)
+        if result != isc.dns.TSIGKeyRing.SUCCESS:
+            raise XfrinZoneInfoException("TSIG key not found in keyring: " +
+                                         self.tsig_key_name.to_text())
+        else:
+            return key
 
     def set_use_ixfr(self, use_ixfr):
         """Set use_ixfr. If set to True, it will use
@@ -1308,6 +1324,7 @@ class Xfrin:
         self.config_handler(config_data)
         self._module_cc.add_remote_config(AUTH_SPECFILE_LOCATION,
                                           self._auth_config_handler)
+        init_keyring(self._module_cc)
 
     def _cc_check_command(self):
         '''This is a straightforward wrapper for cc.check_command,
@@ -1468,7 +1485,7 @@ class Xfrin:
                                                rrclass,
                                                self._get_db_file(),
                                                master_addr,
-                                               zone_info.tsig_key, request_type,
+                                               zone_info.get_tsig_key(), request_type,
                                                True)
                         answer = create_answer(ret[0], ret[1])
                     else:
@@ -1491,7 +1508,7 @@ class Xfrin:
                 tsig_key = None
                 request_type = RRType.AXFR()
                 if zone_info:
-                    tsig_key = zone_info.tsig_key
+                    tsig_key = zone_info.get_tsig_key()
                     if zone_info.use_ixfr:
                         request_type = RRType.IXFR()
                 db_file = args.get('db_file') or self._get_db_file()
diff --git a/src/bin/xfrin/xfrin_messages.mes b/src/bin/xfrin/xfrin_messages.mes
index 554a195..837cafa 100644
--- a/src/bin/xfrin/xfrin_messages.mes
+++ b/src/bin/xfrin/xfrin_messages.mes
@@ -43,7 +43,7 @@ The master port as read from the configuration is not a valid port number.
 
 % XFRIN_BAD_TSIG_KEY_STRING bad TSIG key string: %1
 The TSIG key string as read from the configuration does not represent
-a valid TSIG key.
+a valid TSIG key. The key is ignored.
 
 % XFRIN_BAD_ZONE_CLASS Invalid zone class: %1
 The zone class as read from the configuration is not a valid DNS class.
@@ -160,6 +160,13 @@ run time: Time (in seconds) the complete axfr took
 
 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
+key name was not found in the TSIG key ring (configuration option
+tsig_keys/keys). The transfer is aborted. The key name that could not be
+found is shown in the log message. Check the configuration and the
+TSIG key ring.
+
 % XFRIN_UNKNOWN_ERROR unknown error: %1
 An uncaught exception was raised while running the xfrin daemon. The
 exception message is printed in the log message.
diff --git a/src/bin/xfrout/xfrout.py.in b/src/bin/xfrout/xfrout.py.in
index 835696e..f869955 100755
--- a/src/bin/xfrout/xfrout.py.in
+++ b/src/bin/xfrout/xfrout.py.in
@@ -38,7 +38,7 @@ import isc.server_common.tsig_keyring
 
 from isc.log_messages.xfrout_messages import *
 
-isc.log.init("b10-xfrout")
+isc.log.init("b10-xfrout", buffer=True)
 logger = isc.log.Logger("xfrout")
 
 # Pending system-wide debug level definitions, the ones we
diff --git a/src/bin/zonemgr/zonemgr.py.in b/src/bin/zonemgr/zonemgr.py.in
index 8cb616d..0412e3f 100755
--- a/src/bin/zonemgr/zonemgr.py.in
+++ b/src/bin/zonemgr/zonemgr.py.in
@@ -42,7 +42,7 @@ from isc.log_messages.zonemgr_messages import *
 from isc.notify import notify_out
 
 # Initialize logging for called modules.
-isc.log.init("b10-zonemgr")
+isc.log.init("b10-zonemgr", buffer=True)
 logger = isc.log.Logger("zonemgr")
 
 # Pending system-wide debug level definitions, the ones we
@@ -193,7 +193,8 @@ class ZonemgrRefresh:
     def zone_handle_notify(self, zone_name_class, master):
         """Handle zone notify"""
         if (self._zone_not_exist(zone_name_class)):
-            logger.error(ZONEMGR_UNKNOWN_ZONE_NOTIFIED, zone_name_class[0], zone_name_class[1])
+            logger.error(ZONEMGR_UNKNOWN_ZONE_NOTIFIED, zone_name_class[0],
+                         zone_name_class[1], master)
             raise ZonemgrException("[b10-zonemgr] Notified zone (%s, %s) "
                                    "doesn't belong to zonemgr" % zone_name_class)
         self._set_zone_notifier_master(zone_name_class, master)
diff --git a/src/bin/zonemgr/zonemgr_messages.mes b/src/bin/zonemgr/zonemgr_messages.mes
index 88f8dcf..4f58271 100644
--- a/src/bin/zonemgr/zonemgr_messages.mes
+++ b/src/bin/zonemgr/zonemgr_messages.mes
@@ -138,7 +138,7 @@ zone, or, if this error appears without the administrator giving transfer
 commands, it can indicate an error in the program, as it should not have
 initiated transfers of unknown zones on its own.
 
-% ZONEMGR_UNKNOWN_ZONE_NOTIFIED notified zone %1 (class %2) is not known to the zone manager
+% ZONEMGR_UNKNOWN_ZONE_NOTIFIED notified zone %1/%2 from %3 is not known to the zone manager
 A NOTIFY was received but the zone that was the subject of the operation
 is not being managed by the zone manager.  This may indicate an error
 in the program (as the operation should not have been initiated if this
diff --git a/src/lib/asiolink/io_address.cc b/src/lib/asiolink/io_address.cc
index c805d51..90bdf57 100644
--- a/src/lib/asiolink/io_address.cc
+++ b/src/lib/asiolink/io_address.cc
@@ -76,6 +76,21 @@ IOAddress::fromBytes(short family, const uint8_t* data) {
     return IOAddress(string(addr_str));
 }
 
+std::vector<uint8_t>
+IOAddress::toBytes() const {
+    if (asio_address_.is_v4()) {
+        const asio::ip::address_v4::bytes_type bytes4 =
+            asio_address_.to_v4().to_bytes();
+        return (std::vector<uint8_t>(bytes4.begin(), bytes4.end()));
+    }
+
+    // Not V4 address, so must be a V6 address (else we could never construct
+    // this object).
+    const asio::ip::address_v6::bytes_type bytes6 =
+        asio_address_.to_v6().to_bytes();
+    return (std::vector<uint8_t>(bytes6.begin(), bytes6.end()));
+}
+
 short
 IOAddress::getFamily() const {
     if (asio_address_.is_v4()) {
diff --git a/src/lib/asiolink/io_address.h b/src/lib/asiolink/io_address.h
index 042588c..5b11b87 100644
--- a/src/lib/asiolink/io_address.h
+++ b/src/lib/asiolink/io_address.h
@@ -24,6 +24,7 @@
 
 #include <functional>
 #include <string>
+#include <vector>
 
 #include <exceptions/exceptions.h>
 
@@ -103,6 +104,19 @@ public:
     /// \return AF_INET for IPv4 or AF_INET6 for IPv6.
     short getFamily() const;
 
+    /// \brief Convenience function to check for an IPv4 address
+    ///
+    /// \return true if the address is a V4 address
+    bool isV4() const {
+        return (asio_address_.is_v4());
+    }
+
+    /// \brief Convenience function to check for an IPv6 address
+    ///
+    /// \return true if the address is a V6 address
+    bool isV6() const {
+        return (asio_address_.is_v6());
+    }
 
     /// \brief Creates an address from over wire data.
     ///
@@ -110,8 +124,13 @@ public:
     /// \param data pointer to first char of data
     ///
     /// \return Created IOAddress object
-    static IOAddress
-    fromBytes(short family, const uint8_t* data);
+    static IOAddress fromBytes(short family, const uint8_t* data);
+
+    /// \brief Return address as set of bytes
+    ///
+    /// \return Contents of the address as a set of bytes in network-byte
+    ///         order.
+    std::vector<uint8_t> toBytes() const;
 
     /// \brief Compare addresses for equality
     ///
diff --git a/src/lib/asiolink/tests/io_address_unittest.cc b/src/lib/asiolink/tests/io_address_unittest.cc
index 5e621f3..4bd7626 100644
--- a/src/lib/asiolink/tests/io_address_unittest.cc
+++ b/src/lib/asiolink/tests/io_address_unittest.cc
@@ -18,7 +18,9 @@
 #include <asiolink/io_error.h>
 #include <asiolink/io_address.h>
 
+#include <algorithm>
 #include <cstring>
+#include <vector>
 
 using namespace isc::asiolink;
 
@@ -84,6 +86,45 @@ TEST(IOAddressTest, fromBytes) {
     EXPECT_EQ(addr.toText(), IOAddress("192.0.2.3").toText());
 }
 
+TEST(IOAddressTest, toBytesV4) {
+    // Address and network byte-order representation of the address.
+    const char* V4STRING = "192.0.2.1";
+    uint8_t V4[] = {0xc0, 0x00, 0x02, 0x01};
+
+    std::vector<uint8_t> actual = IOAddress(V4STRING).toBytes();
+    ASSERT_EQ(sizeof(V4), actual.size());
+    EXPECT_TRUE(std::equal(actual.begin(), actual.end(), V4));
+}
+
+TEST(IOAddressTest, toBytesV6) {
+    // Address and network byte-order representation of the address.
+    const char* V6STRING = "2001:db8:1::dead:beef";
+    uint8_t V6[] = {
+        0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef 
+    };
+
+    std::vector<uint8_t> actual = IOAddress(V6STRING).toBytes();
+    ASSERT_EQ(sizeof(V6), actual.size());
+    EXPECT_TRUE(std::equal(actual.begin(), actual.end(), V6));
+}
+
+TEST(IOAddressTest, isV4) {
+    const IOAddress address4("192.0.2.1");
+    const IOAddress address6("2001:db8:1::dead:beef");
+
+    EXPECT_TRUE(address4.isV4());
+    EXPECT_FALSE(address6.isV4());
+}
+
+TEST(IOAddressTest, isV6) {
+    const IOAddress address4("192.0.2.1");
+    const IOAddress address6("2001:db8:1::dead:beef");
+
+    EXPECT_FALSE(address4.isV6());
+    EXPECT_TRUE(address6.isV6());
+}
+
 TEST(IOAddressTest, uint32) {
     IOAddress addr1("192.0.2.5");
 
diff --git a/src/lib/datasrc/Makefile.am b/src/lib/datasrc/Makefile.am
index f4e768a..a4edd4e 100644
--- a/src/lib/datasrc/Makefile.am
+++ b/src/lib/datasrc/Makefile.am
@@ -31,13 +31,14 @@ libb10_datasrc_la_SOURCES += zonetable.h zonetable.cc
 libb10_datasrc_la_SOURCES += zone.h zone_finder.cc zone_finder_context.cc
 libb10_datasrc_la_SOURCES += result.h
 libb10_datasrc_la_SOURCES += logger.h logger.cc
-libb10_datasrc_la_SOURCES += client.h iterator.h
+libb10_datasrc_la_SOURCES += client.h client.cc iterator.h
 libb10_datasrc_la_SOURCES += database.h database.cc
 libb10_datasrc_la_SOURCES += factory.h factory.cc
 libb10_datasrc_la_SOURCES += client_list.h client_list.cc
 libb10_datasrc_la_SOURCES += memory_datasrc.h memory_datasrc.cc
 libb10_datasrc_la_SOURCES += master_loader_callbacks.h
 libb10_datasrc_la_SOURCES += master_loader_callbacks.cc
+libb10_datasrc_la_SOURCES += zone_loader.h zone_loader.cc
 nodist_libb10_datasrc_la_SOURCES = datasrc_messages.h datasrc_messages.cc
 libb10_datasrc_la_LDFLAGS = -no-undefined -version-info 1:0:1
 
diff --git a/src/lib/datasrc/client.cc b/src/lib/datasrc/client.cc
new file mode 100644
index 0000000..919e9ab
--- /dev/null
+++ b/src/lib/datasrc/client.cc
@@ -0,0 +1,48 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <datasrc/client.h>
+
+#include <exceptions/exceptions.h>
+
+/// This file defines a few default implementations for methods.
+///
+/// While some of the detail of the API are worked out, we define
+/// default implementations to ease development for some of the
+/// more tentative methods (those that are not (yet) pure virtual)
+/// They should all throw NotImplemented
+
+namespace isc {
+namespace datasrc {
+
+ZoneIteratorPtr
+DataSourceClient::getIterator(const isc::dns::Name&, bool) const {
+    isc_throw(isc::NotImplemented,
+              "Data source doesn't support iteration");
+}
+
+unsigned int
+DataSourceClient::getZoneCount() const {
+    isc_throw(isc::NotImplemented,
+              "Data source doesn't support getZoneCount");
+}
+
+bool
+DataSourceClient::createZone(const dns::Name&) {
+    isc_throw(isc::NotImplemented,
+              "Data source doesn't support addZone");
+}
+
+} // end namespace datasrc
+} // end namespace isc
diff --git a/src/lib/datasrc/client.h b/src/lib/datasrc/client.h
index 3756a68..8739489 100644
--- a/src/lib/datasrc/client.h
+++ b/src/lib/datasrc/client.h
@@ -20,8 +20,6 @@
 #include <boost/noncopyable.hpp>
 #include <boost/shared_ptr.hpp>
 
-#include <exceptions/exceptions.h>
-
 #include <datasrc/zone.h>
 
 /// \file
@@ -225,15 +223,7 @@ public:
     ///                     adjusted to the lowest one found.
     /// \return Pointer to the iterator.
     virtual ZoneIteratorPtr getIterator(const isc::dns::Name& name,
-                                        bool separate_rrs = false) const {
-        // This is here to both document the parameter in doxygen (therefore it
-        // needs a name) and avoid unused parameter warning.
-        static_cast<void>(name);
-        static_cast<void>(separate_rrs);
-
-        isc_throw(isc::NotImplemented,
-                  "Data source doesn't support iteration");
-    }
+                                        bool separate_rrs = false) const;
 
     /// Return an updater to make updates to a specific zone.
     ///
@@ -368,16 +358,36 @@ public:
     /// This is an optional convenience method, currently only implemented
     /// by the InMemory datasource. By default, it throws NotImplemented
     ///
+    /// \note This is a tentative API, and this method is likely to change
+    /// or be removed in the near future. For that reason, it currently
+    /// provides a default implementation that throws NotImplemented.
+    ///
     /// \exception NotImplemented Thrown if this method is not supported
     ///            by the datasource
     ///
-    /// \note This is a tentative API, and this method may likely to be
-    ///       removed in the near future.
     /// \return The number of zones known to this datasource
-    virtual unsigned int getZoneCount() const {
-        isc_throw(isc::NotImplemented,
-                  "Data source doesn't support getZoneCount");
-    }
+    virtual unsigned int getZoneCount() const;
+
+    /// \brief Create a zone in the data source
+    ///
+    /// Creates a new (empty) zone in the data source backend, which
+    /// can subsequently be filled with data (through getUpdater()).
+    ///
+    /// \note This is a tentative API, and this method is likely to change
+    /// or be removed in the near future. For that reason, it currently
+    /// provides a default implementation that throws NotImplemented.
+    ///
+    /// Apart from the two exceptions mentioned below, in theory this
+    /// call can throw anything, depending on the implementation of
+    /// the datasource backend.
+    ///
+    /// \throw NotImplemented If the datasource backend does not support
+    ///                       direct zone creation.
+    /// \throw DataSourceError If something goes wrong in the data source
+    ///                        while creating the zone.
+    /// \param name The (fully qualified) name of the zone to create
+    /// \return True if the zone was added, false if it already existed
+    virtual bool createZone(const dns::Name& name);
 };
 }
 }
diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc
index fbada44..b65bc08 100644
--- a/src/lib/datasrc/database.cc
+++ b/src/lib/datasrc/database.cc
@@ -43,6 +43,42 @@ using boost::scoped_ptr;
 
 namespace isc {
 namespace datasrc {
+// RAII-style transaction holder; roll back the transaction unless explicitly
+// committed
+namespace {
+class TransactionHolder {
+public:
+    TransactionHolder(DatabaseAccessor& accessor) : accessor_(accessor),
+                                                    committed_(false)
+    {
+        accessor_.startTransaction();
+    }
+    ~TransactionHolder() {
+        if (!committed_) {
+            try {
+                accessor_.rollback();
+            } catch (const DataSourceError& e) {
+                // We generally expect that rollback always succeeds, and
+                // it should in fact succeed in a way we execute it.  But
+                // as the public API allows rollback() to fail and
+                // throw, we should expect it.  Obviously we cannot re-throw
+                // it.  The best we can do is to log it as a critical error.
+                logger.error(DATASRC_DATABASE_TRANSACTION_ROLLBACKFAIL).
+                            arg(accessor_.getDBName()).
+                            arg(e.what());
+            }
+        }
+    }
+    void commit() {
+        accessor_.commit();
+        committed_ = true;
+    }
+private:
+    DatabaseAccessor& accessor_;
+    bool committed_;
+};
+} // end unnamed namespace
+
 
 DatabaseClient::DatabaseClient(RRClass rrclass,
                                boost::shared_ptr<DatabaseAccessor>
@@ -80,6 +116,18 @@ DatabaseClient::findZone(const Name& name) const {
     return (FindResult(result::NOTFOUND, ZoneFinderPtr()));
 }
 
+bool
+DatabaseClient::createZone(const Name& name) {
+    TransactionHolder transaction(*accessor_);
+    std::pair<bool, int> zone(accessor_->getZone(name.toText()));
+    if (zone.first) {
+        return (false);
+    }
+    accessor_->addZone(name.toText());
+    transaction.commit();
+    return (true);
+}
+
 DatabaseClient::Finder::Finder(boost::shared_ptr<DatabaseAccessor> accessor,
                                int zone_id, const isc::dns::Name& origin) :
     accessor_(accessor),
@@ -1349,11 +1397,8 @@ public:
                 logger.info(DATASRC_DATABASE_UPDATER_ROLLBACK)
                     .arg(zone_name_).arg(zone_class_).arg(db_name_);
             } catch (const DataSourceError& e) {
-                // We generally expect that rollback always succeeds, and
-                // it should in fact succeed in a way we execute it.  But
-                // as the public API allows rollback() to fail and
-                // throw, we should expect it.  Obviously we cannot re-throw
-                // it.  The best we can do is to log it as a critical error.
+                // See The destructor ~TransactionHolder() for the
+                // reason to catch this.
                 logger.error(DATASRC_DATABASE_UPDATER_ROLLBACKFAIL)
                     .arg(zone_name_).arg(zone_class_).arg(db_name_)
                     .arg(e.what());
diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h
index 320f327..9db8a8f 100644
--- a/src/lib/datasrc/database.h
+++ b/src/lib/datasrc/database.h
@@ -177,6 +177,24 @@ public:
     ///     an opaque handle.
     virtual std::pair<bool, int> getZone(const std::string& name) const = 0;
 
+    /// \brief Add a new zone to the database
+    ///
+    /// This method creates a new (and empty) zone in the database.
+    ///
+    /// Like for addRecordToZone, implementations are not required to
+    /// check for the existence of the given zone name, it is the
+    /// responsibility of the caller to do so.
+    ///
+    /// Callers must also start a transaction before calling this method,
+    /// implementations should throw DataSourceError if this has not been
+    /// done. Callers should also expect DataSourceErrors for other potential
+    /// problems.
+    ///
+    /// \param name The (fully qualified) domain name of the zone to add.
+    /// \return The internal zone id of the zone (whether is existed already
+    ///         or was created by this call).
+    virtual int addZone(const std::string& name) = 0;
+
     /// \brief This holds the internal context of ZoneIterator for databases
     ///
     /// While the ZoneIterator implementation from DatabaseClient does all the
@@ -1373,6 +1391,16 @@ public:
     ///     should use it as a ZoneFinder only.
     virtual FindResult findZone(const isc::dns::Name& name) const;
 
+    /// \brief Create a zone in the database
+    ///
+    /// This method implements \c DataSourceClient::createZone()
+    ///
+    /// It starts a transaction, checks if the zone exists, and if it
+    /// does not, creates it, commits, and returns true. If the zone
+    /// does exist already, it does nothing (except abort the transaction)
+    /// and returns false.
+    virtual bool createZone(const isc::dns::Name& name);
+
     /// \brief Get the zone iterator
     ///
     /// The iterator allows going through the whole zone content. If the
diff --git a/src/lib/datasrc/datasrc_messages.mes b/src/lib/datasrc/datasrc_messages.mes
index e7cb9d3..e9b4c90 100644
--- a/src/lib/datasrc/datasrc_messages.mes
+++ b/src/lib/datasrc/datasrc_messages.mes
@@ -216,6 +216,16 @@ to find any invalid data and fix it.
 No match (not even a wildcard) was found in the named data source for the given
 name/type/class in the data source.
 
+% DATASRC_DATABASE_TRANSACTION_ROLLBACKFAIL failed to roll back transaction on %1: %2
+A transaction on the database was rolled back without committing the
+changes to the database, but the rollback itself unexpectedly fails.
+The higher level implementation does not expect it to fail, so this means
+either a serious operational error in the underlying data source (such as a
+system failure of a database) or software bug in the underlying data source
+implementation.  In either case if this message is logged the administrator
+should carefully examine the underlying data source to see what exactly
+happens and whether the data is still valid.
+
 % DATASRC_DATABASE_UPDATER_COMMIT updates committed for '%1/%2' on %3
 Debug information.  A set of updates to a zone has been successfully
 committed to the corresponding database backend.  The zone name,
diff --git a/src/lib/datasrc/memory/memory_client.cc b/src/lib/datasrc/memory/memory_client.cc
index 65e1e3b..66e61a2 100644
--- a/src/lib/datasrc/memory/memory_client.cc
+++ b/src/lib/datasrc/memory/memory_client.cc
@@ -209,14 +209,20 @@ private:
     RdataIteratorPtr rdata_iterator_;
     bool separate_rrs_;
     bool ready_;
+    bool examined_rrsigs_;
+    // In case there's nsec3 namespace in the zone, it is represented the same
+    // way as the usual namespace. So we reuse the iterator implementation for
+    // it.
+    ZoneIteratorPtr nsec3_namespace_;
 public:
     MemoryIterator(const RRClass& rrclass,
-                   const ZoneTree& tree, const Name& origin,
-                   bool separate_rrs) :
+                   const ZoneTree& tree, const NSEC3Data* nsec3_data,
+                   const Name& origin, bool separate_rrs) :
         rrclass_(rrclass),
         tree_(tree),
         separate_rrs_(separate_rrs),
-        ready_(true)
+        ready_(true),
+        examined_rrsigs_(false)
     {
         // Find the first node (origin) and preserve the node chain for future
         // searches
@@ -235,10 +241,25 @@ public:
                 rdata_iterator_ = rrset_->getRdataIterator();
             }
         }
+
+        // If we have the NSEC3 namespace, get an iterator for it so we can
+        // delegate to it later.
+        if (nsec3_data != NULL) {
+            nsec3_namespace_ =
+                ZoneIteratorPtr(new MemoryIterator(rrclass,
+                                                   nsec3_data->getNSEC3Tree(),
+                                                   NULL, origin,
+                                                   separate_rrs));
+        }
     }
 
     virtual ConstRRsetPtr getNextRRset() {
         if (!ready_) {
+            // We are done iterating. But in case there's the nsec3 one,
+            // iterate through that one.
+            if (nsec3_namespace_ != ZoneIteratorPtr()) {
+                return (nsec3_namespace_->getNextRRset());
+            }
             isc_throw(Unexpected, "Iterating past the zone end");
         }
         /*
@@ -259,13 +280,19 @@ public:
                     rrset_.reset(new TreeNodeRRset(rrclass_,
                                                    node_, set_node_, true));
                     rdata_iterator_ = rrset_->getRdataIterator();
+                    examined_rrsigs_ = false;
                 }
             }
         }
         if (node_ == NULL) {
             // That's all, folks
             ready_ = false;
-            return (ConstRRsetPtr());
+            if (nsec3_namespace_ != ZoneIteratorPtr()) {
+                // In case we have the NSEC3 namespace, get one from there.
+                return (nsec3_namespace_->getNextRRset());
+            } else {
+                return (ConstRRsetPtr());
+            }
         }
 
         if (separate_rrs_) {
@@ -273,10 +300,24 @@ public:
             // 'current' rdata
             RRsetPtr result(new RRset(rrset_->getName(),
                                       rrset_->getClass(),
-                                      rrset_->getType(),
+                                      // If we are looking into the signature,
+                                      // we need to adjust the type too.
+                                      examined_rrsigs_ ? RRType::RRSIG() :
+                                          rrset_->getType(),
                                       rrset_->getTTL()));
             result->addRdata(rdata_iterator_->getCurrent());
             rdata_iterator_->next();
+            if (!examined_rrsigs_ && rdata_iterator_->isLast()) {
+                // We got to the last RR of the RRset, but we need to look at
+                // the signatures too, if there are any.
+                examined_rrsigs_ = true;
+                const ConstRRsetPtr rrsig = rrset_->getRRsig();
+                if (rrsig != ConstRRsetPtr()) {
+                    rrset_ = rrsig;
+                    rdata_iterator_ = rrsig->getRdataIterator();
+                } // else - no RRSIG. rdata_iterator_ stays at last, next
+                  // condition applies
+            }
             if (rdata_iterator_->isLast()) {
                 // all used up, next.
                 set_node_ = set_node_->getNext();
@@ -286,6 +327,7 @@ public:
                     rrset_.reset(new TreeNodeRRset(rrclass_,
                                                    node_, set_node_, true));
                     rdata_iterator_ = rrset_->getRdataIterator();
+                    examined_rrsigs_ = false;
                 }
             }
             return (result);
@@ -317,7 +359,8 @@ InMemoryClient::getIterator(const Name& name, bool separate_rrs) const {
 
     return (ZoneIteratorPtr(new MemoryIterator(
                                 getClass(),
-                                result.zone_data->getZoneTree(), name,
+                                result.zone_data->getZoneTree(),
+                                result.zone_data->getNSEC3Data(), name,
                                 separate_rrs)));
 }
 
diff --git a/src/lib/datasrc/memory/zone_data.cc b/src/lib/datasrc/memory/zone_data.cc
index 1bf9c9c..cc31419 100644
--- a/src/lib/datasrc/memory/zone_data.cc
+++ b/src/lib/datasrc/memory/zone_data.cc
@@ -61,21 +61,28 @@ nullDeleter(RdataSet* rdataset_head) {
 
 NSEC3Data*
 NSEC3Data::create(util::MemorySegment& mem_sgmt,
+                  const Name& zone_origin,
                   const generic::NSEC3PARAM& rdata)
 {
-    return (NSEC3Data::create(mem_sgmt, rdata.getHashalg(), rdata.getFlags(),
+    return (NSEC3Data::create(mem_sgmt, zone_origin,
+                              rdata.getHashalg(), rdata.getFlags(),
                               rdata.getIterations(), rdata.getSalt()));
 }
 
 NSEC3Data*
-NSEC3Data::create(util::MemorySegment& mem_sgmt, const generic::NSEC3& rdata) {
-    return (NSEC3Data::create(mem_sgmt, rdata.getHashalg(), rdata.getFlags(),
+NSEC3Data::create(util::MemorySegment& mem_sgmt,
+                  const Name& zone_origin,
+                  const generic::NSEC3& rdata)
+{
+    return (NSEC3Data::create(mem_sgmt, zone_origin,
+                              rdata.getHashalg(), rdata.getFlags(),
                               rdata.getIterations(), rdata.getSalt()));
 }
 
 NSEC3Data*
-NSEC3Data::create(util::MemorySegment& mem_sgmt, uint8_t hashalg,
-                  uint8_t flags, uint16_t iterations,
+NSEC3Data::create(util::MemorySegment& mem_sgmt,
+                  const Name& zone_origin,
+                  uint8_t hashalg, uint8_t flags, uint16_t iterations,
                   const std::vector<uint8_t>& salt)
 {
     // NSEC3Data allocation can throw.  To avoid leaking the tree, we manage
@@ -87,6 +94,11 @@ NSEC3Data::create(util::MemorySegment& mem_sgmt, uint8_t hashalg,
         mem_sgmt, ZoneTree::create(mem_sgmt, true),
         boost::bind(nullDeleter, _1));
 
+    ZoneTree* tree = holder.get();
+    const ZoneTree::Result result =
+        tree->insert(mem_sgmt, zone_origin, NULL);
+    assert(result == ZoneTree::SUCCESS);
+
     const size_t salt_len = salt.size();
 
     void* p = mem_sgmt.allocate(sizeof(NSEC3Data) + 1 + salt_len);
diff --git a/src/lib/datasrc/memory/zone_data.h b/src/lib/datasrc/memory/zone_data.h
index 09306d7..974ce24 100644
--- a/src/lib/datasrc/memory/zone_data.h
+++ b/src/lib/datasrc/memory/zone_data.h
@@ -90,9 +90,11 @@ public:
     ///
     /// \param mem_sgmt A \c MemorySegment from which memory for the new
     /// \c NSEC3Data is allocated.
+    /// \param zone_origin The zone origin.
     /// \param rdata An NSEC3PARAM RDATA that specifies the NSEC3 parameters
     /// to be stored.
     static NSEC3Data* create(util::MemorySegment& mem_sgmt,
+                             const dns::Name& zone_origin,
                              const dns::rdata::generic::NSEC3PARAM& rdata);
 
     /// \brief Allocate and construct \c NSEC3Data from NSEC3 Rdata.
@@ -104,9 +106,11 @@ public:
     ///
     /// \param mem_sgmt A \c MemorySegment from which memory for the new
     /// \c NSEC3Data is allocated.
+    /// \param zone_origin The zone origin.
     /// \param rdata An NSEC3 RDATA that specifies the NSEC3 parameters
     /// to be stored.
     static NSEC3Data* create(util::MemorySegment& mem_sgmt,
+                             const dns::Name& zone_origin,
                              const dns::rdata::generic::NSEC3& rdata);
 
     /// \brief Destruct and deallocate \c NSEC3Data.
@@ -193,8 +197,10 @@ public:
 
 private:
     // Common subroutine for the public versions of create().
-    static NSEC3Data* create(util::MemorySegment& mem_sgmt, uint8_t hashalg,
-                             uint8_t flags, uint16_t iterations,
+    static NSEC3Data* create(util::MemorySegment& mem_sgmt,
+                             const dns::Name& zone_origin,
+                             uint8_t hashalg, uint8_t flags,
+                             uint16_t iterations,
                              const std::vector<uint8_t>& salt);
 
     /// \brief The constructor.
@@ -366,9 +372,9 @@ public:
     ///
     /// \param mem_sgmt A \c MemorySegment from which memory for the new
     /// \c ZoneData is allocated.
-    /// \param name The zone name.
+    /// \param zone_origin The zone origin.
     static ZoneData* create(util::MemorySegment& mem_sgmt,
-                            const dns::Name& zone_name);
+                            const dns::Name& zone_origin);
 
     /// \brief Destruct and deallocate \c ZoneData.
     ///
diff --git a/src/lib/datasrc/memory/zone_data_updater.cc b/src/lib/datasrc/memory/zone_data_updater.cc
index 7605644..51ec03c 100644
--- a/src/lib/datasrc/memory/zone_data_updater.cc
+++ b/src/lib/datasrc/memory/zone_data_updater.cc
@@ -233,7 +233,7 @@ ZoneDataUpdater::setupNSEC3(const ConstRRsetPtr rrset) {
 
     NSEC3Data* nsec3_data = zone_data_.getNSEC3Data();
     if (nsec3_data == NULL) {
-        nsec3_data = NSEC3Data::create(mem_sgmt_, nsec3_rdata);
+        nsec3_data = NSEC3Data::create(mem_sgmt_, zone_name_, nsec3_rdata);
         zone_data_.setNSEC3Data(nsec3_data);
         zone_data_.setSigned(true);
     } else {
diff --git a/src/lib/datasrc/memory/zone_finder.cc b/src/lib/datasrc/memory/zone_finder.cc
index 4240c21..7f57d8e 100644
--- a/src/lib/datasrc/memory/zone_finder.cc
+++ b/src/lib/datasrc/memory/zone_finder.cc
@@ -305,8 +305,16 @@ getClosestNSEC(const ZoneData& zone_data,
     }
 
     const ZoneNode* prev_node;
-    while ((prev_node = zone_data.getZoneTree().previousNode(node_path))
-           != NULL) {
+    if (node_path.getLastComparisonResult().getRelation() ==
+        NameComparisonResult::SUBDOMAIN) {
+         // In case the search ended as a sub-domain, the previous node
+         // is already at the top of node_path.
+         prev_node = node_path.getLastComparedNode();
+    } else {
+         prev_node = zone_data.getZoneTree().previousNode(node_path);
+    }
+
+    while (prev_node != NULL) {
         if (!prev_node->isEmpty()) {
             const RdataSet* found =
                 RdataSet::find(prev_node->getData(), RRType::NSEC());
@@ -314,6 +322,7 @@ getClosestNSEC(const ZoneData& zone_data,
                 return (ConstNodeRRset(prev_node, found));
             }
         }
+        prev_node = zone_data.getZoneTree().previousNode(node_path);
     }
     // This must be impossible and should be an internal bug.
     // See the description at the method declaration.
diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc
index 68d6554..4ef49d0 100644
--- a/src/lib/datasrc/sqlite3_accessor.cc
+++ b/src/lib/datasrc/sqlite3_accessor.cc
@@ -78,7 +78,8 @@ enum StatementID {
     ADD_NSEC3_RECORD = 19,
     DEL_ZONE_NSEC3_RECORDS = 20,
     DEL_NSEC3_RECORD = 21,
-    NUM_STATEMENTS = 22
+    ADD_ZONE = 22,
+    NUM_STATEMENTS = 23
 };
 
 const char* const text_statements[NUM_STATEMENTS] = {
@@ -161,7 +162,10 @@ const char* const text_statements[NUM_STATEMENTS] = {
     "DELETE FROM nsec3 WHERE zone_id=?1",
     // DEL_NSEC3_RECORD: delete specified NSEC3-related records
     "DELETE FROM nsec3 WHERE zone_id=?1 AND hash=?2 "
-    "AND rdtype=?3 AND rdata=?4"
+    "AND rdtype=?3 AND rdata=?4",
+
+    // ADD_ZONE: add a zone to the zones table
+    "INSERT INTO zones (name, rdclass) VALUES (?1, ?2)" // ADD_ZONE
 };
 
 struct SQLite3Parameters {
@@ -612,6 +616,33 @@ SQLite3Accessor::getZone(const std::string& name) const {
     return (std::pair<bool, int>(false, 0));
 }
 
+int
+SQLite3Accessor::addZone(const std::string& name) {
+    // Transaction should have been started by the caller
+    if (!dbparameters_->in_transaction) {
+        isc_throw(DataSourceError, "performing addZone on SQLite3 "
+                  "data source without transaction");
+    }
+
+    StatementProcessor proc(*dbparameters_, ADD_ZONE, "add zone");
+    // note: using TRANSIENT here, STATIC would be slightly more
+    // efficient, but TRANSIENT is safer and performance is not
+    // as important in this specific code path.
+    proc.bindText(1, name.c_str(), SQLITE_TRANSIENT);
+    proc.bindText(2, class_.c_str(), SQLITE_TRANSIENT);
+    proc.exec();
+
+    // There are tricks to getting this in one go, but it is safer
+    // to do a new lookup (sqlite3_last_insert_rowid is unsafe
+    // regarding threads and triggers). This requires two
+    // statements, and is unpredictable in the case a zone is added
+    // twice, but this method assumes the caller does not do that
+    // anyway
+    std::pair<bool, int> getzone_result = getZone(name);
+    assert(getzone_result.first);
+    return (getzone_result.second);
+}
+
 namespace {
 
 // Conversion to plain char
diff --git a/src/lib/datasrc/sqlite3_accessor.h b/src/lib/datasrc/sqlite3_accessor.h
index a8112d4..c5773d0 100644
--- a/src/lib/datasrc/sqlite3_accessor.h
+++ b/src/lib/datasrc/sqlite3_accessor.h
@@ -34,13 +34,11 @@ class RRClass;
 
 namespace datasrc {
 
-/**
- * \brief Low-level database error
- *
- * This exception is thrown when the SQLite library complains about something.
- * It might mean corrupt database file, invalid request or that something is
- * rotten in the library.
- */
+/// \brief Low-level database error
+///
+/// This exception is thrown when the SQLite library complains about something.
+/// It might mean corrupt database file, invalid request or that something is
+/// rotten in the library.
 class SQLite3Error : public DataSourceError {
 public:
     SQLite3Error(const char* file, size_t line, const char* what) :
@@ -53,24 +51,20 @@ public:
         isc::Exception(file, line, what) {}
 };
 
-/**
- * \brief Too Much Data
- *
- * Thrown if a query expecting a certain number of rows back returned too
- * many rows.
- */
+/// \brief Too Much Data
+///
+/// Thrown if a query expecting a certain number of rows back returned too
+/// many rows.
 class TooMuchData : public DataSourceError {
 public:
     TooMuchData(const char* file, size_t line, const char* what) :
         DataSourceError(file, line, what) {}
 };
 
-/**
- * \brief Too Little Data
- *
- * Thrown if a query expecting a certain number of rows back returned too
- * few rows (including none).
- */
+/// \brief Too Little Data
+///
+/// Thrown if a query expecting a certain number of rows back returned too
+/// few rows (including none).
 class TooLittleData : public DataSourceError {
 public:
     TooLittleData(const char* file, size_t line, const char* what) :
@@ -79,70 +73,83 @@ public:
 
 struct SQLite3Parameters;
 
-/**
- * \brief Concrete implementation of DatabaseAccessor for SQLite3 databases
- *
- * This opens one database file with our schema and serves data from there.
- * According to the design, it doesn't interpret the data in any way, it just
- * provides unified access to the DB.
- */
+/// \brief Concrete implementation of DatabaseAccessor for SQLite3 databases
+///
+/// This opens one database file with our schema and serves data from there.
+/// According to the design, it doesn't interpret the data in any way, it just
+/// provides unified access to the DB.
 class SQLite3Accessor : public DatabaseAccessor,
     public boost::enable_shared_from_this<SQLite3Accessor> {
 public:
-    /**
-     * \brief Constructor
-     *
-     * This opens the database and becomes ready to serve data from there.
-     *
-     * \exception SQLite3Error will be thrown if the given database file
-     * doesn't work (it is broken, doesn't exist and can't be created, etc).
-     *
-     * \param filename The database file to be used.
-     * \param rrclass Textual representation of RR class ("IN", "CH", etc),
-     *     specifying which class of data it should serve (while the database
-     *     file can contain multiple classes of data, a single accessor can
-     *     work with only one class).
-     */
+    /// \brief Constructor
+    ///
+    /// This opens the database and becomes ready to serve data from there.
+    ///
+    /// \exception SQLite3Error will be thrown if the given database file
+    /// doesn't work (it is broken, doesn't exist and can't be created, etc).
+    ///
+    /// \param filename The database file to be used.
+    /// \param rrclass Textual representation of RR class ("IN", "CH", etc),
+    ///    specifying which class of data it should serve (while the database
+    ///    file can contain multiple classes of data, a single accessor can
+    ///    work with only one class).
     SQLite3Accessor(const std::string& filename, const std::string& rrclass);
 
-    /**
-     * \brief Destructor
-     *
-     * Closes the database.
-     */
-    ~SQLite3Accessor();
+    /// \brief Destructor
+    ///
+    /// Closes the database.
+    virtual ~SQLite3Accessor();
 
     /// This implementation internally opens a new sqlite3 database for the
     /// same file name specified in the constructor of the original accessor.
     virtual boost::shared_ptr<DatabaseAccessor> clone();
 
-    /**
-     * \brief Look up a zone
-     *
-     * This implements the getZone from DatabaseAccessor and looks up a zone
-     * in the data. It looks for a zone with the exact given origin and class
-     * passed to the constructor.
-     *
-     * \exception SQLite3Error if something about the database is broken.
-     *
-     * \param name The (fully qualified) domain name of zone to look up
-     * \return The pair contains if the lookup was successful in the first
-     *     element and the zone id in the second if it was.
-     */
+    /// \brief Look up a zone
+    ///
+    /// This implements the getZone from DatabaseAccessor and looks up a zone
+    /// in the data. It looks for a zone with the exact given origin and class
+    /// passed to the constructor.
+    ///
+    /// \exception SQLite3Error if something about the database is broken.
+    ///
+    /// \param name The (fully qualified) domain name of zone to look up
+    /// \return The pair contains if the lookup was successful in the first
+    ///    element and the zone id in the second if it was.
     virtual std::pair<bool, int> getZone(const std::string& name) const;
 
-    /** \brief Look up all resource records for a name
-     *
-     * This implements the getRecords() method from DatabaseAccessor
-     *
-     * \exception SQLite3Error if there is an sqlite3 error when performing
-     *                         the query
-     *
-     * \param name the name to look up
-     * \param id the zone id, as returned by getZone()
-     * \param subdomains Match subdomains instead of the name.
-     * \return Iterator that contains all records with the given name
-     */
+    /// \brief Add a zone
+    ///
+    /// This implements the addZone from DatabaseAccessor and adds a zone
+    /// into the zones table. If the zone exists already, it is still added,
+    /// so the caller should make sure this does not happen (by making
+    /// sure the zone does not exist). In the case of duplicate addition,
+    /// it is undefined which zone id is returned.
+    ///
+    /// The class of the newly created zone is the class passed at construction
+    /// time of the accessor.
+    ///
+    /// This method requires a transaction has been started (with
+    /// \c beginTransaction) by the caller.
+    ///
+    /// \exception DataSourceError if no transaction is active, or if there
+    ///                           is an SQLite3 error when performing the
+    ///                           queries.
+    ///
+    /// \param name The origin name of the zone to add
+    /// \return the id of the zone that has been added
+    virtual int addZone(const std::string& name);
+
+    /// \brief Look up all resource records for a name
+    ///
+    /// This implements the getRecords() method from DatabaseAccessor
+    ///
+    /// \exception SQLite3Error if there is an sqlite3 error when performing
+    ///                        the query
+    ///
+    /// \param name the name to look up
+    /// \param id the zone id, as returned by getZone()
+    /// \param subdomains Match subdomains instead of the name.
+    /// \return Iterator that contains all records with the given name
     virtual IteratorContextPtr getRecords(const std::string& name,
                                           int id,
                                           bool subdomains = false) const;
@@ -155,35 +162,33 @@ public:
     virtual IteratorContextPtr getNSEC3Records(const std::string& hash,
                                                int id) const;
 
-    /** \brief Look up all resource records for a zone
-     *
-     * This implements the getRecords() method from DatabaseAccessor
-     *
-     * \exception SQLite3Error if there is an sqlite3 error when performing
-     *                         the query
-     *
-     * \param id the zone id, as returned by getZone()
-     * \return Iterator that contains all records in the given zone
-     */
+    /// \brief Look up all resource records for a zone
+    ///
+    /// This implements the getRecords() method from DatabaseAccessor
+    ///
+    /// \exception SQLite3Error if there is an sqlite3 error when performing
+    ///                        the query
+    ///
+    /// \param id the zone id, as returned by getZone()
+    /// \return Iterator that contains all records in the given zone
     virtual IteratorContextPtr getAllRecords(int id) const;
 
-    /** \brief Creates an iterator context for a set of differences.
-     *
-     * Implements the getDiffs() method from DatabaseAccessor
-     *
-     * \exception NoSuchSerial if either of the versions do not exist in
-     *            the difference table.
-     * \exception SQLite3Error if there is an sqlite3 error when performing
-     *            the query
-     *
-     * \param id The ID of the zone, returned from getZone().
-     * \param start The SOA serial number of the version of the zone from
-     *        which the difference sequence should start.
-     * \param end The SOA serial number of the version of the zone at which
-     *        the difference sequence should end.
-     *
-     * \return Iterator containing difference records.
-     */
+    /// \brief Creates an iterator context for a set of differences.
+    ///
+    /// Implements the getDiffs() method from DatabaseAccessor
+    ///
+    /// \exception NoSuchSerial if either of the versions do not exist in
+    ///           the difference table.
+    /// \exception SQLite3Error if there is an sqlite3 error when performing
+    ///           the query
+    ///
+    /// \param id The ID of the zone, returned from getZone().
+    /// \param start The SOA serial number of the version of the zone from
+    ///       which the difference sequence should start.
+    /// \param end The SOA serial number of the version of the zone at which
+    ///       the difference sequence should end.
+    ///
+    /// \return Iterator containing difference records.
     virtual IteratorContextPtr
     getDiffs(int id, uint32_t start, uint32_t end) const;
 
diff --git a/src/lib/datasrc/tests/Makefile.am b/src/lib/datasrc/tests/Makefile.am
index d3ef3bf..61858bd 100644
--- a/src/lib/datasrc/tests/Makefile.am
+++ b/src/lib/datasrc/tests/Makefile.am
@@ -60,6 +60,7 @@ run_unittests_SOURCES += zone_finder_context_unittest.cc
 run_unittests_SOURCES += faked_nsec3.h faked_nsec3.cc
 run_unittests_SOURCES += client_list_unittest.cc
 run_unittests_SOURCES += master_loader_callbacks_test.cc
+run_unittests_SOURCES += zone_loader_unittest.cc
 
 # We need the actual module implementation in the tests (they are not part
 # of libdatasrc)
diff --git a/src/lib/datasrc/tests/client_unittest.cc b/src/lib/datasrc/tests/client_unittest.cc
index 87ab5e0..915e3b7 100644
--- a/src/lib/datasrc/tests/client_unittest.cc
+++ b/src/lib/datasrc/tests/client_unittest.cc
@@ -60,4 +60,8 @@ TEST_F(ClientTest, defaultGetZoneCount) {
     EXPECT_THROW(client_.getZoneCount(), isc::NotImplemented);
 }
 
+TEST_F(ClientTest, defaultCreateZone) {
+    EXPECT_THROW(client_.createZone(Name("example.com.")), isc::NotImplemented);
+}
+
 }
diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc
index dda4de2..174da04 100644
--- a/src/lib/datasrc/tests/database_unittest.cc
+++ b/src/lib/datasrc/tests/database_unittest.cc
@@ -274,6 +274,11 @@ public:
         }
     }
 
+    virtual int addZone(const std::string&) {
+        isc_throw(isc::NotImplemented,
+                  "This database datasource can't add zones");
+    }
+
     virtual boost::shared_ptr<DatabaseAccessor> clone() {
         // This accessor is stateless, so we can simply return a new instance.
         return (boost::shared_ptr<DatabaseAccessor>(new NopAccessor));
@@ -329,6 +334,7 @@ public:
         isc_throw(isc::NotImplemented,
                   "This test database knows nothing about NSEC3 nor order");
     }
+
 private:
     const std::string database_name_;
 
@@ -1780,7 +1786,7 @@ doFindAllTestResult(ZoneFinder& finder, const isc::dns::Name& name,
               expected_name, result->rrset->getName());
 }
 
-// When asking for an RRset where RRs somehow have different TTLs, it should 
+// When asking for an RRset where RRs somehow have different TTLs, it should
 // convert to the lowest one.
 TEST_F(MockDatabaseClientTest, ttldiff) {
     ZoneIteratorPtr it(this->client_->getIterator(Name("example.org")));
@@ -4092,4 +4098,57 @@ TYPED_TEST(DatabaseClientTest, findNSEC3) {
     performNSEC3Test(*finder, true);
 }
 
+TYPED_TEST(DatabaseClientTest, createZone) {
+    const Name new_name("example.com");
+    const DataSourceClient::FindResult
+        zone(this->client_->findZone(new_name));
+    ASSERT_EQ(result::NOTFOUND, zone.code);
+
+    // The mock implementation does not do createZone,
+    // in which case it should throw NotImplemented (from
+    // the base class)
+    if (this->is_mock_) {
+        ASSERT_THROW(this->client_->createZone(new_name), isc::NotImplemented);
+    } else {
+        // But in the real case, it should work and return true
+        ASSERT_TRUE(this->client_->createZone(new_name));
+        const DataSourceClient::FindResult
+            zone2(this->client_->findZone(new_name));
+        ASSERT_EQ(result::SUCCESS, zone2.code);
+        // And the second call should return false since
+        // it already exists
+        ASSERT_FALSE(this->client_->createZone(new_name));
+    }
+}
+
+TYPED_TEST(DatabaseClientTest, createZoneRollbackOnLocked) {
+    // skip test for mock
+    if (this->is_mock_) {
+        return;
+    }
+
+    const Name new_name("example.com");
+    isc::datasrc::ZoneUpdaterPtr updater =
+        this->client_->getUpdater(this->zname_, true);
+    ASSERT_THROW(this->client_->createZone(new_name), DataSourceError);
+    // createZone started a transaction as well, but since it failed,
+    // it should have been rolled back. Roll back the other one as
+    // well, and the next attempt should succeed
+    updater.reset();
+    ASSERT_TRUE(this->client_->createZone(new_name));
+}
+
+TYPED_TEST(DatabaseClientTest, createZoneRollbackOnExists) {
+    // skip test for mock
+    if (this->is_mock_) {
+        return;
+    }
+
+    const Name new_name("example.com");
+    ASSERT_FALSE(this->client_->createZone(this->zname_));
+    // createZone started a transaction, but since it failed,
+    // it should have been rolled back, and the next attempt should succeed
+    ASSERT_TRUE(this->client_->createZone(new_name));
+}
+
 }
diff --git a/src/lib/datasrc/tests/memory/memory_client_unittest.cc b/src/lib/datasrc/tests/memory/memory_client_unittest.cc
index 2a9a35d..0a03645 100644
--- a/src/lib/datasrc/tests/memory/memory_client_unittest.cc
+++ b/src/lib/datasrc/tests/memory/memory_client_unittest.cc
@@ -690,6 +690,25 @@ TEST_F(MemoryClientTest, getIteratorSeparateRRs) {
     EXPECT_EQ(ConstRRsetPtr(), iterator2->getNextRRset());
 }
 
+// Test we get RRSIGs and NSEC3s too for iterating with separate RRs
+TEST_F(MemoryClientTest, getIteratorSeparateSigned) {
+    client_->load(Name("example.org"),
+                       TEST_DATA_DIR "/example.org-nsec3-signed.zone");
+    ZoneIteratorPtr iterator(client_->getIterator(Name("example.org"), true));
+    bool seen_rrsig = false, seen_nsec3 = false;
+    for (ConstRRsetPtr rrset = iterator->getNextRRset();
+         rrset != ConstRRsetPtr(); rrset = iterator->getNextRRset()) {
+        if (rrset->getType() == RRType::RRSIG()) {
+            seen_rrsig = true;
+        } else if (rrset->getType() == RRType::NSEC3()) {
+            seen_nsec3 = true;
+        }
+    }
+
+    EXPECT_TRUE(seen_rrsig);
+    EXPECT_TRUE(seen_nsec3);
+}
+
 TEST_F(MemoryClientTest, getIteratorGetSOAThrowsNotImplemented) {
     client_->load(Name("example.org"), TEST_DATA_DIR "/example.org-empty.zone");
     ZoneIteratorPtr iterator(client_->getIterator(Name("example.org")));
diff --git a/src/lib/datasrc/tests/memory/testdata/2503-test.zone b/src/lib/datasrc/tests/memory/testdata/2503-test.zone
new file mode 100644
index 0000000..1609dee
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/2503-test.zone
@@ -0,0 +1,13 @@
+example.com.	3600	IN	SOA	ns.example.com. admin.example.com. 1234 3600 1800 2419200 7200
+example.com.	3600	IN	RRSIG	SOA 7 2 3600 20130107110921 20121210110921 55148 example.com. 3yVTkwBE98bAcJJHKwk4WL2bKHQqfejzMdcmtteH/A2H07QwexShqQiRhNNyTRj3s1FfmMYlbd67TDZhUnWAwCdMQICj2BS0hzqiZr9iftGsAkIah6LCFx9nRPNIz2CsqXMf9D7rvzKBDUNBIliYGa5h9d7MalAlM7tZAY9YnH0=
+example.com.	3600	IN	A	192.0.2.1
+example.com.	3600	IN	RRSIG	A 7 2 3600 20130107110921 20121210110921 55148 example.com. uCgf94psLo1wWZqAFEcy+5hM1Uh2hAwEVLyiiLESW+jz/cPeOjNFpa2PN1DIUjf2Dj40dDokM3Ev1rSjR6Z+rfHri8rUsahOEw3ZU+UrEDd5cf9MYeo4MlQIrBMAuz3AKOU3YXy7AiPEwIxtlrqrI6o1qGeX8+zKqhjGgnS0/vI=
+example.com.	3600	IN	NS	example.com.
+example.com.	3600	IN	RRSIG	NS 7 2 3600 20130107110921 20121210110921 55148 example.com. dDUDANrEdt46e7793dHxl1zOCWm88gLbr3mHYGSbIBfbtEBtISNGL1nwslejyZKnzhYM2kT8bZ3YGe5ibc9wPlGj29nUJgFwqeyIsWJhffhUCaWKYrDWod0WZLJw5uZcS2QHCuQ0+ZMm8dQoL1qswcahmh0VUI7BEKTHk9RJgzc=
+example.com.	3600	IN	DNSKEY	256 3 5 AwEAAZIOhUpUld/OBPeNJ26O1twKj3fRLPt4X8H6N01t4s+VT5v9jaCnCVX4O1LbALdJUv5uPwL4gy4qvf+7Z3Xanp7QCZ5i7ivS1qfiz2tfacXwtVv4aI4EqS7deYN6yD4S/vIpwW+2FoqUWhQtdhC68ex1YfjeEI+CUbAKlF5XgQR5 ;{id = 5196 (zsk), size = 1024b}
+example.com.	3600	IN	DNSKEY	256 3 7 AwEAAe1s2ycmRzexpaUVjRZAd25ybIv7qP+Rh/tF8zIm2bMGLZl5ByxeyajZpPaGRK2xXWdY4aKBAcDELbOPQ+lLPI4RsiZBY+jY5JuzE5inkpyTtwBFjQl6o3AdBC08BmZjrpWnh9rFx/o7tk0ekU6UblUlhpqxRCDwZPvRSIVurrIr ;{id = 55148 (zsk), size = 1024b}
+example.com.	3600	IN	RRSIG	DNSKEY 7 2 3600 20130107110921 20121210110921 55148 example.com. FmOXL+p4GIyRA+LEWRjmoSfvWLkimw58dYrO3LS3hS0hRXP7dkhgMmuYFTOlfcfi53R9sQ9DsH6YsZwR5l0rkBE+GDq+MCfYcKXmQ7OLJZjH/3BnJgQtAlktVTfyie+kWOmwPl3l9jfim7Rmr6anXz3fYvy4S06iTsJ9z2weNJU=
+example.com.	3600	IN	NSEC3PARAM	1 0 1 - 
+example.com.	3600	IN	RRSIG	NSEC3PARAM 7 2 3600 20130107110921 20121210110921 55148 example.com. zIVekIFafb9uc20ATatQJeSV6QULdvg4VWDAWS4tnQgipPdA6VUggeBXSBaAdZu24wh5ObEdeKMChZOuiHcBYKtveQxdWzYEP0gEVvNXK+jTHpHh+siONJetWdfeCRgVma3F134ckCsS2wDSB9pzjV5lJfDmFa4muaTYZswAM8I=
+9vq38lj9qs6s1aruer131mbtsfnvek2p.example.com.	7200	IN	NSEC3	1 0 1 -  9vq38lj9qs6s1aruer131mbtsfnvek2p A NS SOA RRSIG DNSKEY NSEC3PARAM 
+9vq38lj9qs6s1aruer131mbtsfnvek2p.example.com.	7200	IN	RRSIG	NSEC3 7 3 7200 20130107110921 20121210110921 55148 example.com. q3j367t58w/NJ41I27dSFvPFQYiAFMl9hh3nwtZCaSc+KxFK+zvWP7Cm21oRDr+4oJCFkmm0kCSFGwarbFHmqJcVuorH6AKtm8Aiy3uQDvRdBZh/P+uBu4gruQSUaT7jVQLi9WdqR25nPtPC8zuxE3Yy4iRZxtZqQ/JVJFlQ/VM=
diff --git a/src/lib/datasrc/tests/memory/testdata/2504-test.zone b/src/lib/datasrc/tests/memory/testdata/2504-test.zone
new file mode 100644
index 0000000..bbbcb83
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/2504-test.zone
@@ -0,0 +1,10 @@
+example.com.	3600	IN	SOA	ns.example.com. admin.example.com. 1234 3600 1800 2419200 7200
+example.com.	3600	IN	RRSIG	SOA 5 2 3600 20130104152459 20121207152459 5196 example.com. ijAvh4ZzAfMCKKWN64aR5CWaHYTAvhJjgBLV+1/RFPG3150DwDr9EI0bCdVyRIMDDLOeQnn0N70rtc051rA2pJVNpEzG2hPIy3Yd2kDsbFBn0atiz3rvjpRLcmmWWYoZihpkFwKRxD41OzJPLg83/yJr1nAu3qSQXh4LmuNfgwI=
+example.com.	3600	IN	A	192.0.2.1
+example.com.	3600	IN	RRSIG	A 5 2 3600 20130104152459 20121207152459 5196 example.com. XnVKYuSH+BANWiULSJAk6wmwnba8hUS9j2cgrCMFcn43XlAUCCNigoMCWhaGbsssMaOyjdwfL3sQZr/NHaQ0ITSL8IZj7t8rOiCvCrUAktuG5UuHmHET6XKPkf03JaoPXIzO9smBqvjFHz1HsuPGxwGh8ztY0p291iXk0zbRwc8=
+example.com.	3600	IN	NS	example.com.
+example.com.	3600	IN	RRSIG	NS 5 2 3600 20130104152459 20121207152459 5196 example.com. W5v7dr/WV8FGdxWFS0h1crd1MRxkSrkcmcAs7CT0+uhmVvbx06PsBxGRuCHyL8Y5NimsMs2RLjhkUkJw1+aSLVtTlbC8Pg5dFDK3bGkzBEq3wRcIXf5bM2Lf+l/cWxGY0NgR1Wrq0ckXsnFxegGm9G3OtpCZgTv0L+9cCO4MS7c=
+example.com.	3600	IN	DNSKEY	256 3 5 AwEAAZIOhUpUld/OBPeNJ26O1twKj3fRLPt4X8H6N01t4s+VT5v9jaCnCVX4O1LbALdJUv5uPwL4gy4qvf+7Z3Xanp7QCZ5i7ivS1qfiz2tfacXwtVv4aI4EqS7deYN6yD4S/vIpwW+2FoqUWhQtdhC68ex1YfjeEI+CUbAKlF5XgQR5 ;{id = 5196 (zsk), size = 1024b}
+example.com.	3600	IN	RRSIG	DNSKEY 5 2 3600 20130104152459 20121207152459 5196 example.com. OiN+DBuEDnyEmMe0Qa4MN4SIJ71e+INNhOvoGBpWARiWu83QlPkoJhdU1GADBaanYdFL8UI0os6w2dkwp4aghChD+KWO40NuhUY2LrEUS2jbO3hEcCT3/acaVyucwVv1FjfC4d561Lkfnh8DM9nk5i75IeWVLklMqret1t/f0uY=
+example.com.	7200	IN	NSEC	example.com. A NS SOA RRSIG NSEC DNSKEY
+example.com.	7200	IN	RRSIG	NSEC 5 2 7200 20130104152459 20121207152459 5196 example.com. P4eCHTNJImfwQ+wLa3jeySkVeJnzCmb4zFUDQEZIk3GSjLGUHZhHswqSAhpgevx6ZNX/pOqN/Dybf9oadFCZXyoMQDijP02LcDEl4X1ccNiakg6i/9RG9PcEx5ZWJlCFAJ0tOhp1kauZu/HUlkscTAVgBRud0qEclWJacH1k80E=
diff --git a/src/lib/datasrc/tests/memory/testdata/Makefile.am b/src/lib/datasrc/tests/memory/testdata/Makefile.am
index dddbf0a..ef5f08b 100644
--- a/src/lib/datasrc/tests/memory/testdata/Makefile.am
+++ b/src/lib/datasrc/tests/memory/testdata/Makefile.am
@@ -29,3 +29,6 @@ EXTRA_DIST += example.org-rrsigs.zone
 EXTRA_DIST += example.org-wildcard-dname.zone
 EXTRA_DIST += example.org-wildcard-ns.zone
 EXTRA_DIST += example.org-wildcard-nsec3.zone
+
+EXTRA_DIST += 2503-test.zone
+EXTRA_DIST += 2504-test.zone
diff --git a/src/lib/datasrc/tests/memory/zone_data_unittest.cc b/src/lib/datasrc/tests/memory/zone_data_unittest.cc
index 3c28cec..1605fa2 100644
--- a/src/lib/datasrc/tests/memory/zone_data_unittest.cc
+++ b/src/lib/datasrc/tests/memory/zone_data_unittest.cc
@@ -85,11 +85,16 @@ protected:
 // Shared by both test cases using NSEC3 and NSEC3PARAM Rdata
 template <typename RdataType>
 void
-checkNSEC3Data(MemorySegmentTest& mem_sgmt, const RdataType& expect_rdata) {
-    NSEC3Data* nsec3_data = NSEC3Data::create(mem_sgmt, expect_rdata);
+checkNSEC3Data(MemorySegmentTest& mem_sgmt,
+               const Name& zone_name,
+               const RdataType& expect_rdata)
+{
+    NSEC3Data* nsec3_data = NSEC3Data::create(mem_sgmt, zone_name,
+                                              expect_rdata);
 
-    // Internal tree should be created and empty.
-    EXPECT_EQ(0, nsec3_data->getNSEC3Tree().getNodeCount());
+    // Internal tree should be created and must contain just the origin
+    // node.
+    EXPECT_EQ(1, nsec3_data->getNSEC3Tree().getNodeCount());
 
     EXPECT_EQ(expect_rdata.getHashalg(), nsec3_data->hashalg);
     EXPECT_EQ(expect_rdata.getFlags(), nsec3_data->flags);
@@ -117,18 +122,18 @@ checkFindRdataSet(const ZoneTree& tree, const Name& name, RRType type,
 TEST_F(ZoneDataTest, createNSEC3Data) {
     // Create an NSEC3Data object from various types of RDATA (of NSEC3PARAM
     // and of NSEC3), check if the resulting parameters match.
-    checkNSEC3Data(mem_sgmt_, param_rdata_); // one 'usual' form of params
-    checkNSEC3Data(mem_sgmt_, param_rdata_nosalt_); // empty salt
-    checkNSEC3Data(mem_sgmt_, param_rdata_largesalt_); // max-len salt
+    checkNSEC3Data(mem_sgmt_, zname_, param_rdata_); // one 'usual' form
+    checkNSEC3Data(mem_sgmt_, zname_, param_rdata_nosalt_); // empty salt
+    checkNSEC3Data(mem_sgmt_, zname_, param_rdata_largesalt_); // max-len salt
 
     // Same concepts of the tests, using NSEC3 RDATA.
-    checkNSEC3Data(mem_sgmt_, nsec3_rdata_);
-    checkNSEC3Data(mem_sgmt_, nsec3_rdata_nosalt_);
-    checkNSEC3Data(mem_sgmt_, nsec3_rdata_largesalt_);
+    checkNSEC3Data(mem_sgmt_, zname_, nsec3_rdata_);
+    checkNSEC3Data(mem_sgmt_, zname_, nsec3_rdata_nosalt_);
+    checkNSEC3Data(mem_sgmt_, zname_, nsec3_rdata_largesalt_);
 }
 
 TEST_F(ZoneDataTest, addNSEC3) {
-    nsec3_data_ = NSEC3Data::create(mem_sgmt_, param_rdata_);
+    nsec3_data_ = NSEC3Data::create(mem_sgmt_, zname_, param_rdata_);
 
     ZoneNode* node = NULL;
     nsec3_data_->insertName(mem_sgmt_, nsec3_rrset_->getName(), &node);
@@ -161,7 +166,8 @@ TEST_F(ZoneDataTest, exceptionSafetyOnCreate) {
     // will fail due to bad_alloc.  It shouldn't cause memory leak
     // (that would be caught in TearDown()).
     mem_sgmt_.setThrowCount(2);
-    EXPECT_THROW(NSEC3Data::create(mem_sgmt_, param_rdata_), std::bad_alloc);
+    EXPECT_THROW(NSEC3Data::create(mem_sgmt_, zname_, param_rdata_),
+                 std::bad_alloc);
 
     // allocate() will throw on the insertion of the origin node.
     mem_sgmt_.setThrowCount(2);
@@ -214,7 +220,7 @@ TEST_F(ZoneDataTest, getSetNSEC3Data) {
 
     // Set a new one.  The set method should return NULL.  The get method
     // should return the new one.
-    NSEC3Data* nsec3_data = NSEC3Data::create(mem_sgmt_, param_rdata_);
+    NSEC3Data* nsec3_data = NSEC3Data::create(mem_sgmt_, zname_, param_rdata_);
     NSEC3Data* old_nsec3_data = zone_data_->setNSEC3Data(nsec3_data);
     EXPECT_EQ(static_cast<NSEC3Data*>(NULL), old_nsec3_data);
     EXPECT_EQ(nsec3_data, zone_data_->getNSEC3Data());
@@ -222,7 +228,7 @@ TEST_F(ZoneDataTest, getSetNSEC3Data) {
 
     // Replace an existing one with a yet another one.
     // We're responsible for destroying the old one.
-    NSEC3Data* nsec3_data2 = NSEC3Data::create(mem_sgmt_, nsec3_rdata_);
+    NSEC3Data* nsec3_data2 = NSEC3Data::create(mem_sgmt_, zname_, nsec3_rdata_);
     old_nsec3_data = zone_data_->setNSEC3Data(nsec3_data2);
     EXPECT_EQ(nsec3_data, old_nsec3_data);
     EXPECT_EQ(nsec3_data2, zone_data_->getNSEC3Data());
diff --git a/src/lib/datasrc/tests/memory/zone_finder_unittest.cc b/src/lib/datasrc/tests/memory/zone_finder_unittest.cc
index f112119..42667da 100644
--- a/src/lib/datasrc/tests/memory/zone_finder_unittest.cc
+++ b/src/lib/datasrc/tests/memory/zone_finder_unittest.cc
@@ -13,6 +13,7 @@
 // PERFORMANCE OF THIS SOFTWARE.
 
 #include "memory_segment_test.h"
+#include "zone_table_segment_test.h"
 
 // NOTE: this faked_nsec3 inclusion (and all related code below)
 // was ported during #2109 for the convenience of implementing #2218
@@ -25,7 +26,10 @@
 #include <datasrc/memory/zone_finder.h>
 #include <datasrc/memory/zone_data_updater.h>
 #include <datasrc/memory/rdata_serialization.h>
+#include <datasrc/memory/zone_table_segment.h>
+#include <datasrc/memory/memory_client.h>
 #include <datasrc/data_source.h>
+#include <datasrc/client.h>
 #include <testutils/dnsmessage_test.h>
 
 #include <boost/foreach.hpp>
@@ -1426,6 +1430,27 @@ TEST_F(InMemoryZoneFinderTest, findOrphanRRSIG) {
              ZoneFinder::DELEGATION, true, ns_rrset);
 }
 
+// \brief testcase for #2504 (Problem in inmem NSEC denial of existence
+// handling)
+TEST_F(InMemoryZoneFinderTest, NSECNonExistentTest) {
+    shared_ptr<ZoneTableSegment> ztable_segment(
+         new ZoneTableSegmentTest(class_, mem_sgmt_));
+    InMemoryClient client(ztable_segment, class_);
+    Name name("example.com.");
+
+    client.load(name, TEST_DATA_DIR "/2504-test.zone");
+    DataSourceClient::FindResult result(client.findZone(name));
+
+    // Check for a non-existing name
+    Name search_name("nonexist.example.com.");
+    ZoneFinderContextPtr find_result(
+        result.zone_finder->find(search_name,
+                                 RRType::A(), ZoneFinder::FIND_DNSSEC));
+    // We don't find the domain, but find() must complete (not throw or
+    // assert).
+    EXPECT_EQ(ZoneFinder::NXDOMAIN, find_result->code);
+}
+
 /// \brief NSEC3 specific tests fixture for the InMemoryZoneFinder class
 class InMemoryZoneFinderNSEC3Test : public InMemoryZoneFinderTest {
 public:
@@ -1562,4 +1587,29 @@ TEST_F(InMemoryZoneFinderNSEC3Test, RRSIGOnly) {
                  DataSourceError);
 }
 
+// \brief testcase for #2503 (Problem in inmem NSEC3 denial of existence
+// handling)
+TEST_F(InMemoryZoneFinderNSEC3Test, findNSEC3MissingOrigin) {
+     // Set back the default hash calculator.
+     DefaultNSEC3HashCreator creator;
+     setNSEC3HashCreator(&creator);
+
+     shared_ptr<ZoneTableSegment> ztable_segment(
+          new ZoneTableSegmentTest(class_, mem_sgmt_));
+     InMemoryClient client(ztable_segment, class_);
+     Name name("example.com.");
+
+     client.load(name, TEST_DATA_DIR "/2503-test.zone");
+     DataSourceClient::FindResult result(client.findZone(name));
+
+     // Check for a non-existing name
+     Name search_name("nonexist.example.com.");
+     ZoneFinder::FindNSEC3Result find_result(
+          result.zone_finder->findNSEC3(search_name, true));
+     // findNSEC3() must have completed (not throw or assert). Because
+     // the find was recursive, it always must find something and return
+     // true.
+     EXPECT_TRUE(find_result.matched);
+}
+
 }
diff --git a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc
index 100a0dd..8dff6d5 100644
--- a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc
+++ b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc
@@ -667,6 +667,64 @@ TEST_F(SQLite3Create, creationtest) {
     ASSERT_TRUE(isReadable(SQLITE_NEW_DBFILE));
 }
 
+// Test addZone works. This is done on the 'createtest' fixture so we
+// can easily be sure it does not exist yet.
+TEST_F(SQLite3Create, addZone) {
+    // Need shared_ptr for the getAllRecords at the end of the test
+    boost::shared_ptr<SQLite3Accessor> accessor(
+        new SQLite3Accessor(SQLITE_NEW_DBFILE, "IN"));
+
+    const std::string zone_name("example.com");
+    EXPECT_FALSE(accessor->getZone(zone_name).first);
+
+    // Calling addZone without transaction should fail
+    EXPECT_THROW(accessor->addZone(zone_name), DataSourceError);
+
+    // Add the zone. Returns the new zone id
+    accessor->startTransaction();
+    const int new_zone_id = accessor->addZone(zone_name);
+    accessor->commit();
+
+    // Check that it exists now, but has no records at this point
+    const std::pair<bool, int> zone_info(accessor->getZone(zone_name));
+    ASSERT_TRUE(zone_info.first);
+    EXPECT_EQ(new_zone_id, zone_info.second);
+
+    DatabaseAccessor::IteratorContextPtr context =
+        accessor->getAllRecords(zone_info.second);
+    string data[DatabaseAccessor::COLUMN_COUNT];
+    EXPECT_NE(DatabaseAccessor::IteratorContextPtr(), context);
+    EXPECT_FALSE(context->getNext(data));
+}
+
+// Test addZone does not get confused with different classes
+TEST_F(SQLite3Create, addZoneDifferentClass) {
+    const std::string zone_name("example.com.");
+
+    // Add IN zone
+    boost::shared_ptr<SQLite3Accessor> accessor(
+        new SQLite3Accessor(SQLITE_NEW_DBFILE, "IN"));
+    accessor->startTransaction();
+    const int new_zone_id_IN = accessor->addZone(zone_name);
+    accessor->commit();
+
+    // Add CH zone
+    accessor.reset(new SQLite3Accessor(SQLITE_NEW_DBFILE, "CH"));
+    accessor->startTransaction();
+    const int new_zone_id_CH = accessor->addZone(zone_name);
+    accessor->commit();
+
+    // id's should differ
+    ASSERT_NE(new_zone_id_IN, new_zone_id_CH);
+
+    // Reopen the database for both classes, and make sure
+    // we get the right zone id on searches
+    accessor.reset(new SQLite3Accessor(SQLITE_NEW_DBFILE, "IN"));
+    EXPECT_EQ(new_zone_id_IN, accessor->getZone(zone_name).second);
+    accessor.reset(new SQLite3Accessor(SQLITE_NEW_DBFILE, "CH"));
+    EXPECT_EQ(new_zone_id_CH, accessor->getZone(zone_name).second);
+}
+
 TEST_F(SQLite3Create, emptytest) {
     ASSERT_FALSE(isReadable(SQLITE_NEW_DBFILE));
 
@@ -1538,4 +1596,23 @@ TEST_F(SQLite3Update, addDiffWithUpdate) {
 
     checkDiffs(expected_stored, accessor->getDiffs(zone_id, 1234, 1300));
 }
+
+TEST_F(SQLite3Update, addZoneWhileLocked) {
+    const std::string existing_zone = "example.com.";
+    const std::string new_zone = "example2.com.";
+
+    // Start 'replacing' an existing zone, it should lock the db
+    zone_id = accessor->startUpdateZone(existing_zone, true).second;
+
+    // addZone should throw an exception that it is locked
+    another_accessor->startTransaction();
+    EXPECT_THROW(another_accessor->addZone(new_zone), DataSourceError);
+    // Commit should do nothing, but not fail
+    another_accessor->commit();
+
+    accessor->rollback();
+    // New zone should not exist
+    EXPECT_FALSE(accessor->getZone(new_zone).first);
+}
+
 } // end anonymous namespace
diff --git a/src/lib/datasrc/tests/zone_loader_unittest.cc b/src/lib/datasrc/tests/zone_loader_unittest.cc
new file mode 100644
index 0000000..b19a843
--- /dev/null
+++ b/src/lib/datasrc/tests/zone_loader_unittest.cc
@@ -0,0 +1,395 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <datasrc/zone_loader.h>
+#include <datasrc/data_source.h>
+
+#include <datasrc/memory/zone_table_segment.h>
+#include <datasrc/memory/memory_client.h>
+
+#include <dns/rrclass.h>
+#include <dns/name.h>
+#include <dns/rrset.h>
+#include <util/memory_segment_local.h>
+#include <exceptions/exceptions.h>
+
+#include <gtest/gtest.h>
+
+#include <boost/shared_ptr.hpp>
+#include <string>
+#include <vector>
+
+using isc::dns::RRClass;
+using isc::dns::Name;
+using isc::dns::RRType;
+using isc::dns::ConstRRsetPtr;
+using std::string;
+using std::vector;
+using boost::shared_ptr;
+using namespace isc::datasrc;
+
+namespace {
+
+class MockClient : public DataSourceClient {
+public:
+    MockClient() :
+        commit_called_(false),
+        missing_zone_(false),
+        rrclass_(RRClass::IN())
+    {}
+    virtual FindResult findZone(const Name&) const {
+        isc_throw(isc::NotImplemented, "Method not used in tests");
+    };
+    virtual std::pair<ZoneJournalReader::Result, ZoneJournalReaderPtr>
+        getJournalReader(const Name&, uint32_t, uint32_t) const
+    {
+        isc_throw(isc::NotImplemented, "Method not used in tests");
+    }
+    virtual ZoneUpdaterPtr getUpdater(const Name& name, bool replace,
+                                      bool journaling) const;
+    // We store some information about what was happening here.
+    // It is publicly accessible, since this is private testing class
+    // anyway, so no need to dress it fancy into getters. Some are mutable,
+    // since many client methods are const, but we still want to know they
+    // were called.
+    mutable vector<Name> provided_updaters_;
+    // We store string representations of the RRsets. This is simpler than
+    // copying them and we can't really put them into shared pointers, because
+    // we get them as references.
+    vector<string> rrsets_;
+    bool commit_called_;
+    // If set to true, getUpdater returns NULL
+    bool missing_zone_;
+    // The pretended class of the client. Usualy IN, but can be overriden.
+    RRClass rrclass_;
+};
+
+// The updater isn't really correct according to the API. For example,
+// the whole client can be committed only once in its lifetime. The
+// updaters would influence each other if there were more. But we
+// don't need more updaters in the same test, so it doesn't matter
+// and this way, it is much simpler.
+class Updater : public ZoneUpdater {
+public:
+    Updater(MockClient* client) :
+        client_(client),
+        finder_(client_->rrclass_)
+    {}
+    virtual ZoneFinder& getFinder() {
+        return (finder_);
+    }
+    virtual void addRRset(const isc::dns::AbstractRRset& rrset) {
+        if (client_->commit_called_) {
+            isc_throw(DataSourceError, "Add after commit");
+        }
+        client_->rrsets_.push_back(rrset.toText());
+    }
+    virtual void deleteRRset(const isc::dns::AbstractRRset&) {
+        isc_throw(isc::NotImplemented, "Method not used in tests");
+    }
+    virtual void commit() {
+        client_->commit_called_ = true;
+    }
+private:
+    MockClient* client_;
+    class Finder : public ZoneFinder {
+    public:
+        Finder(const RRClass& rrclass) :
+            class_(rrclass)
+        {}
+        virtual RRClass getClass() const {
+            return (class_);
+        }
+        virtual Name getOrigin() const {
+            isc_throw(isc::NotImplemented, "Method not used in tests");
+        }
+        virtual shared_ptr<Context> find(const Name&, const RRType&,
+                                         const FindOptions)
+        {
+            isc_throw(isc::NotImplemented, "Method not used in tests");
+        }
+        virtual shared_ptr<Context> findAll(const Name&,
+                                            vector<ConstRRsetPtr>&,
+                                            const FindOptions)
+        {
+            isc_throw(isc::NotImplemented, "Method not used in tests");
+        }
+        virtual FindNSEC3Result findNSEC3(const Name&, bool) {
+            isc_throw(isc::NotImplemented, "Method not used in tests");
+        }
+    private:
+        const RRClass class_;
+    } finder_;
+};
+
+ZoneUpdaterPtr
+MockClient::getUpdater(const Name& name, bool replace, bool journaling) const {
+    if (missing_zone_) {
+        return (ZoneUpdaterPtr());
+    }
+    EXPECT_TRUE(replace);
+    EXPECT_FALSE(journaling);
+    provided_updaters_.push_back(name);
+    // const_cast is bad. But the const on getUpdater seems wrong in the first
+    // place, since updater will be modifying the data there. And the updater
+    // wants to store data into the client so we can examine it later.
+    return (ZoneUpdaterPtr(new Updater(const_cast<MockClient*>(this))));
+}
+
+class ZoneLoaderTest : public ::testing::Test {
+protected:
+    ZoneLoaderTest() :
+        rrclass_(RRClass::IN()),
+        ztable_segment_(memory::ZoneTableSegment::
+                        create(isc::data::NullElement(), rrclass_)),
+        source_client_(ztable_segment_, rrclass_)
+    {}
+    void prepareSource(const Name& zone, const char* filename) {
+        // TODO:
+        // Currently, load uses an urelated implementation. In the long term,
+        // the method will probably be deprecated. At that time, we should
+        // probably prepare the data in some other way (using sqlite3 or
+        // something). This is simpler for now.
+        source_client_.load(zone, string(TEST_DATA_DIR) + "/" + filename);
+    }
+private:
+    const RRClass rrclass_;
+    // This is because of the in-memory client. We use it to read data
+    // from. It is still easier than setting up sqlite3 client, since
+    // we have this one in the linked library.
+
+    // FIXME: We should be destroying it by ZoneTableSegment::destroy.
+    // But the shared pointer won't let us, will it?
+    shared_ptr<memory::ZoneTableSegment> ztable_segment_;
+protected:
+    memory::InMemoryClient source_client_;
+    // This one is mocked. It will help us see what is happening inside.
+    // Also, mocking it is simpler than setting up an sqlite3 client.
+    MockClient destination_client_;
+};
+
+// Use the loader to load an unsigned zone.
+TEST_F(ZoneLoaderTest, copyUnsigned) {
+    prepareSource(Name::ROOT_NAME(), "root.zone");
+    ZoneLoader loader(destination_client_, Name::ROOT_NAME(), source_client_);
+    // It gets the updater directly in the constructor
+    ASSERT_EQ(1, destination_client_.provided_updaters_.size());
+    EXPECT_EQ(Name::ROOT_NAME(), destination_client_.provided_updaters_[0]);
+    // Now load the whole zone
+    loader.load();
+    EXPECT_TRUE(destination_client_.commit_called_);
+    // We don't check the whole zone. We check the first and last and the
+    // count, which should be enough.
+
+    // The count is 34 because we expect the RRs to be separated.
+    EXPECT_EQ(34, destination_client_.rrsets_.size());
+    // Ensure known order.
+    std::sort(destination_client_.rrsets_.begin(),
+              destination_client_.rrsets_.end());
+    EXPECT_EQ(". 518400 IN NS a.root-servers.net.\n",
+              destination_client_.rrsets_.front());
+    EXPECT_EQ("m.root-servers.net. 3600000 IN AAAA 2001:dc3::35\n",
+              destination_client_.rrsets_.back());
+
+    // It isn't possible to try again now
+    EXPECT_THROW(loader.load(), isc::InvalidOperation);
+    EXPECT_THROW(loader.loadIncremental(1), isc::InvalidOperation);
+    // Even 0, which should load nothing, returns the error
+    EXPECT_THROW(loader.loadIncremental(0), isc::InvalidOperation);
+}
+
+// Try loading incrementally.
+TEST_F(ZoneLoaderTest, copyUnsignedIncremental) {
+    prepareSource(Name::ROOT_NAME(), "root.zone");
+    ZoneLoader loader(destination_client_, Name::ROOT_NAME(), source_client_);
+
+    // Try loading few RRs first.
+    loader.loadIncremental(10);
+    // We should get the 10 we asked for
+    EXPECT_EQ(10, destination_client_.rrsets_.size());
+    // Not committed yet, we didn't complete the loading
+    EXPECT_FALSE(destination_client_.commit_called_);
+
+    // This is unusual, but allowed. Check it doesn't do anything
+    loader.loadIncremental(0);
+    EXPECT_EQ(10, destination_client_.rrsets_.size());
+    EXPECT_FALSE(destination_client_.commit_called_);
+
+    // We can finish the rest
+    loader.loadIncremental(30);
+    EXPECT_EQ(34, destination_client_.rrsets_.size());
+    EXPECT_TRUE(destination_client_.commit_called_);
+
+    // No more loading now
+    EXPECT_THROW(loader.load(), isc::InvalidOperation);
+    EXPECT_THROW(loader.loadIncremental(1), isc::InvalidOperation);
+    EXPECT_THROW(loader.loadIncremental(0), isc::InvalidOperation);
+}
+
+// Check we can load RRSIGs and NSEC3 (which could break due to them being
+// in separate namespace)
+TEST_F(ZoneLoaderTest, copySigned) {
+    prepareSource(Name("example.org"), "example.org.nsec3-signed");
+    ZoneLoader loader(destination_client_, Name("example.org"),
+                      source_client_);
+    loader.load();
+
+    // All the RRs are there, including the ones in NSEC3 namespace
+    EXPECT_EQ(14, destination_client_.rrsets_.size());
+    EXPECT_TRUE(destination_client_.commit_called_);
+    // Same trick with sorting to know where they are
+    std::sort(destination_client_.rrsets_.begin(),
+              destination_client_.rrsets_.end());
+    // Due to the R at the beginning, this one should be last
+    EXPECT_EQ("09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN NSEC3 "
+              "1 0 10 AABBCCDD RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD A RRSIG\n",
+              destination_client_.rrsets_[0]);
+    EXPECT_EQ("09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN RRSIG "
+              "NSEC3 7 3 1200 20120301040838 20120131040838 19562 example.org."
+              " EdwMeepLf//lV+KpCAN+213Scv1rrZyj4i2OwoCP4XxxS3CWGSuvYuKOyfZc8w"
+              "KRcrD/4YG6nZVXE0s5O8NahjBJmDIyVt4WkfZ6QthxGg8ggLVvcD3dFksPyiKHf"
+              "/WrTOZPSsxvN5m/i1Ey6+YWS01Gf3WDCMWDauC7Nmh3CTM=\n",
+              destination_client_.rrsets_[1]);
+}
+
+// If the destination zone does not exist, it throws
+TEST_F(ZoneLoaderTest, copyMissingDestination) {
+    destination_client_.missing_zone_ = true;
+    prepareSource(Name::ROOT_NAME(), "root.zone");
+    EXPECT_THROW(ZoneLoader(destination_client_, Name::ROOT_NAME(),
+                            source_client_), DataSourceError);
+}
+
+// If the source zone does not exist, it throws
+TEST_F(ZoneLoaderTest, copyMissingSource) {
+    EXPECT_THROW(ZoneLoader(destination_client_, Name::ROOT_NAME(),
+                            source_client_), DataSourceError);
+}
+
+// The class of the source and destination are different
+TEST_F(ZoneLoaderTest, classMismatch) {
+    destination_client_.rrclass_ = RRClass::CH();
+    prepareSource(Name::ROOT_NAME(), "root.zone");
+    EXPECT_THROW(ZoneLoader(destination_client_, Name::ROOT_NAME(),
+                            source_client_), isc::InvalidParameter);
+}
+
+// Load an unsigned zone, all at once
+TEST_F(ZoneLoaderTest, loadUnsigned) {
+    ZoneLoader loader(destination_client_, Name::ROOT_NAME(),
+                      TEST_DATA_DIR "/root.zone");
+    // It gets the updater directly in the constructor
+    ASSERT_EQ(1, destination_client_.provided_updaters_.size());
+    EXPECT_EQ(Name::ROOT_NAME(), destination_client_.provided_updaters_[0]);
+    // Now load the whole zone
+    loader.load();
+    EXPECT_TRUE(destination_client_.commit_called_);
+    // We don't check the whole zone. We check the first and last and the
+    // count, which should be enough.
+
+    // The count is 34 because we expect the RRs to be separated.
+    EXPECT_EQ(34, destination_client_.rrsets_.size());
+    // Ensure known order.
+    std::sort(destination_client_.rrsets_.begin(),
+              destination_client_.rrsets_.end());
+    EXPECT_EQ(". 518400 IN NS a.root-servers.net.\n",
+              destination_client_.rrsets_.front());
+    EXPECT_EQ("m.root-servers.net. 3600000 IN AAAA 2001:dc3::35\n",
+              destination_client_.rrsets_.back());
+
+    // It isn't possible to try again now
+    EXPECT_THROW(loader.load(), isc::InvalidOperation);
+    EXPECT_THROW(loader.loadIncremental(1), isc::InvalidOperation);
+    // Even 0, which should load nothing, returns the error
+    EXPECT_THROW(loader.loadIncremental(0), isc::InvalidOperation);
+}
+
+// Try loading from master file incrementally.
+TEST_F(ZoneLoaderTest, loadUnsignedIncremental) {
+    ZoneLoader loader(destination_client_, Name::ROOT_NAME(),
+                      TEST_DATA_DIR "/root.zone");
+
+    // Try loading few RRs first.
+    loader.loadIncremental(10);
+    // We should get the 10 we asked for
+    EXPECT_EQ(10, destination_client_.rrsets_.size());
+    // Not committed yet, we didn't complete the loading
+    EXPECT_FALSE(destination_client_.commit_called_);
+
+    EXPECT_EQ(10, destination_client_.rrsets_.size());
+    EXPECT_FALSE(destination_client_.commit_called_);
+
+    // We can finish the rest
+    loader.loadIncremental(30);
+    EXPECT_EQ(34, destination_client_.rrsets_.size());
+    EXPECT_TRUE(destination_client_.commit_called_);
+
+    // No more loading now
+    EXPECT_THROW(loader.load(), isc::InvalidOperation);
+    EXPECT_THROW(loader.loadIncremental(1), isc::InvalidOperation);
+    EXPECT_THROW(loader.loadIncremental(0), isc::InvalidOperation);
+}
+
+// If the destination zone does not exist, it throws
+TEST_F(ZoneLoaderTest, loadMissingDestination) {
+    destination_client_.missing_zone_ = true;
+    EXPECT_THROW(ZoneLoader(destination_client_, Name::ROOT_NAME(),
+                            TEST_DATA_DIR "/root.zone"), DataSourceError);
+}
+
+// Check we can load RRSIGs and NSEC3 (which could break due to them being
+// in separate namespace)
+TEST_F(ZoneLoaderTest, loadSigned) {
+    ZoneLoader loader(destination_client_, Name("example.org"),
+                      TEST_DATA_DIR "/example.org.nsec3-signed");
+    loader.load();
+
+    // All the RRs are there, including the ones in NSEC3 namespace
+    EXPECT_EQ(14, destination_client_.rrsets_.size());
+    EXPECT_TRUE(destination_client_.commit_called_);
+    // Same trick with sorting to know where they are
+    std::sort(destination_client_.rrsets_.begin(),
+              destination_client_.rrsets_.end());
+    // Due to the R at the beginning, this one should be last
+    EXPECT_EQ("09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN NSEC3 "
+              "1 0 10 AABBCCDD RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD A RRSIG\n",
+              destination_client_.rrsets_[0]);
+    EXPECT_EQ("09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN RRSIG "
+              "NSEC3 7 3 1200 20120301040838 20120131040838 19562 example.org."
+              " EdwMeepLf//lV+KpCAN+213Scv1rrZyj4i2OwoCP4XxxS3CWGSuvYuKOyfZc8w"
+              "KRcrD/4YG6nZVXE0s5O8NahjBJmDIyVt4WkfZ6QthxGg8ggLVvcD3dFksPyiKHf"
+              "/WrTOZPSsxvN5m/i1Ey6+YWS01Gf3WDCMWDauC7Nmh3CTM=\n",
+              destination_client_.rrsets_[1]);
+}
+
+// Test it throws when there's no such file
+TEST_F(ZoneLoaderTest, loadNoSuchFile) {
+    ZoneLoader loader(destination_client_, Name::ROOT_NAME(),
+                      "This file does not exist");
+    EXPECT_THROW(loader.load(), MasterFileError);
+    EXPECT_FALSE(destination_client_.commit_called_);
+}
+
+// And it also throws when there's a syntax error in the master file
+TEST_F(ZoneLoaderTest, loadSyntaxError) {
+    ZoneLoader loader(destination_client_, Name::ROOT_NAME(),
+                      // This is not a master file for sure
+                      // (misusing a file that happens to be there
+                      // already).
+                      TEST_DATA_DIR "/example.org.sqlite3");
+    EXPECT_THROW(loader.load(), MasterFileError);
+    EXPECT_FALSE(destination_client_.commit_called_);
+}
+
+}
diff --git a/src/lib/datasrc/zone_loader.cc b/src/lib/datasrc/zone_loader.cc
new file mode 100644
index 0000000..01f216e
--- /dev/null
+++ b/src/lib/datasrc/zone_loader.cc
@@ -0,0 +1,132 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <datasrc/zone_loader.h>
+#include <datasrc/master_loader_callbacks.h>
+
+#include <datasrc/client.h>
+#include <datasrc/data_source.h>
+#include <datasrc/iterator.h>
+#include <datasrc/zone.h>
+
+#include <dns/rrset.h>
+
+using isc::dns::Name;
+using isc::dns::ConstRRsetPtr;
+using isc::dns::MasterLoader;
+
+namespace isc {
+namespace datasrc {
+
+ZoneLoader::ZoneLoader(DataSourceClient& destination, const Name& zone_name,
+                       DataSourceClient& source) :
+    // Separate the RRsets as that is possibly faster (the data source doesn't
+    // have to aggregate them) and also because our limit semantics.
+    iterator_(source.getIterator(zone_name, true)),
+    updater_(destination.getUpdater(zone_name, true, false)),
+    complete_(false)
+{
+    // The getIterator should never return NULL. So we check it.
+    // Or should we throw instead?
+    assert(iterator_ != ZoneIteratorPtr());
+    // In case the zone doesn't exist in the destination, throw
+    if (updater_ == ZoneUpdaterPtr()) {
+        isc_throw(DataSourceError, "Zone " << zone_name << " not found in "
+                  "destination data source, can't fill it with data");
+    }
+    // The dereference of zone_finder is safe, if we can get iterator, we can
+    // get a finder.
+    //
+    // TODO: We probably need a getClass on the data source itself.
+    if (source.findZone(zone_name).zone_finder->getClass() !=
+        updater_->getFinder().getClass()) {
+        isc_throw(isc::InvalidParameter,
+                  "Source and destination class mismatch");
+    }
+}
+
+ZoneLoader::ZoneLoader(DataSourceClient& destination, const Name& zone_name,
+                       const char* filename) :
+    updater_(destination.getUpdater(zone_name, true, false)),
+    complete_(false),
+    loaded_ok_(true)
+{
+    if (updater_ == ZoneUpdaterPtr()) {
+        isc_throw(DataSourceError, "Zone " << zone_name << " not found in "
+                  "destination data source, can't fill it with data");
+    } else {
+        loader_.reset(new
+                      MasterLoader(filename, zone_name,
+                                   // TODO: Maybe we should have getClass()
+                                   // on the data source?
+                                   updater_->getFinder().getClass(),
+                                   createMasterLoaderCallbacks(zone_name,
+                                       updater_->getFinder().getClass(),
+                                       &loaded_ok_),
+                                   createMasterLoaderAddCallback(*updater_)));
+    }
+}
+
+namespace {
+
+// Copy up to limit RRsets from source to destination
+bool
+copyRRsets(const ZoneUpdaterPtr& destination, const ZoneIteratorPtr& source,
+           size_t limit)
+{
+    size_t loaded = 0;
+    while (loaded < limit) {
+        const ConstRRsetPtr rrset(source->getNextRRset());
+        if (rrset == ConstRRsetPtr()) {
+            // Done loading, no more RRsets in the input.
+            return (true);
+        } else {
+            destination->addRRset(*rrset);
+        }
+        ++loaded;
+    }
+    return (false); // Not yet, there may be more
+}
+
+} // end unnamed namespace
+
+bool
+ZoneLoader::loadIncremental(size_t limit) {
+    if (complete_) {
+        isc_throw(isc::InvalidOperation,
+                  "Loading has been completed previously");
+    }
+
+    if (iterator_ == ZoneIteratorPtr()) {
+        assert(loader_.get() != NULL);
+        try {
+            complete_ = loader_->loadIncremental(limit);
+        } catch (const isc::dns::MasterLoaderError& e) {
+            isc_throw(MasterFileError, e.getMessage().c_str());
+        }
+        if (complete_ && !loaded_ok_) {
+            isc_throw(MasterFileError, "Error while loading master file");
+        }
+    } else {
+        complete_ = copyRRsets(updater_, iterator_, limit);
+    }
+
+    if (complete_) {
+        updater_->commit();
+    }
+    return (complete_);
+}
+
+} // end namespace datasrc
+} // end namespace isc
diff --git a/src/lib/datasrc/zone_loader.h b/src/lib/datasrc/zone_loader.h
new file mode 100644
index 0000000..2946116
--- /dev/null
+++ b/src/lib/datasrc/zone_loader.h
@@ -0,0 +1,158 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef DATASRC_ZONE_LOADER_H
+#define DATASRC_ZONE_LOADER_H
+
+#include <datasrc/data_source.h>
+
+#include <dns/master_loader.h>
+
+#include <cstdlib> // For size_t
+#include <boost/shared_ptr.hpp>
+#include <boost/scoped_ptr.hpp>
+
+namespace isc {
+namespace dns {
+// Forward declaration
+class Name;
+}
+namespace datasrc {
+
+// Forward declarations
+class DataSourceClient;
+class ZoneIterator;
+typedef boost::shared_ptr<ZoneIterator> ZoneIteratorPtr;
+class ZoneUpdater;
+typedef boost::shared_ptr<ZoneUpdater> ZoneUpdaterPtr;
+
+/// \brief Exception thrown when there's a problem with master file.
+///
+/// This is thrown by the ZoneLoader when there's a fatal problem with
+/// a master file being loaded.
+class MasterFileError : public DataSourceError {
+public:
+    MasterFileError(const char* file, size_t line, const char* what) :
+        DataSourceError(file, line, what)
+    {}
+};
+
+/// \brief Class to load data into a data source client.
+///
+/// This is a small wrapper class that is able to load data into a data source.
+/// It can load either from another data source or from a master file. The
+/// purpose of the class is only to hold the state for incremental loading.
+///
+/// The old content of zone is discarded and no journal is stored.
+class ZoneLoader {
+public:
+    /// \brief Constructor from master file.
+    ///
+    /// This initializes the zone loader to load from a master file.
+    ///
+    /// \param destination The data source into which the loaded data should
+    ///     go.
+    /// \param zone_name The origin of the zone. The class is implicit in the
+    ///     destination.
+    /// \param master_file Path to the master file to read data from.
+    /// \throw DataSourceError in case the zone does not exist in destination.
+    ///     This class does not support creating brand new zones, only loading
+    ///     data into them. In case a new zone is needed, it must be created
+    ///     beforehand.
+    /// \throw DataSourceError in case of other (possibly low-level) errors,
+    ///     such as read-only data source or database error.
+    ZoneLoader(DataSourceClient& destination, const isc::dns::Name& zone_name,
+               const char* master_file);
+
+    /// \brief Constructor from another data source.
+    ///
+    /// This initializes the zone loader to read from another data source.
+    /// It'll effectively copy data from one data source to another.
+    ///
+    /// \param destination The data source into which the loaded data should
+    ///     go.
+    /// \param zone_name The origin of the zone.
+    /// \param source The data source from which the data would be read.
+    /// \throw InvalidParameter in case the class of destination and source
+    ///     differs.
+    /// \throw NotImplemented in case the source data source client doesn't
+    ///     provide an iterator.
+    /// \throw DataSourceError in case the zone does not exist in destination.
+    ///     This class does not support creating brand new zones, only loading
+    ///     data into them. In case a new zone is needed, it must be created
+    ///     beforehand.
+    /// \throw DataSourceError in case the zone does not exist in the source.
+    /// \throw DataSourceError in case of other (possibly low-level) errors,
+    ///     such as read-only data source or database error.
+    ZoneLoader(DataSourceClient& destination, const isc::dns::Name& zone_name,
+               DataSourceClient& source);
+
+    /// \brief Perform the whole load.
+    ///
+    /// This performs the whole loading operation. It may take a long time.
+    ///
+    /// \throw InvalidOperation in case the loading was already completed
+    ///     before this call.
+    /// \throw DataSourceError in case some error (possibly low-level) happens.
+    /// \throw MasterFileError when the master_file is badly formatted or some
+    ///     similar problem is found when loading the master file.
+    void load() {
+        while (!loadIncremental(1000)) { // 1000 is arbitrary largish number
+            // Body intentionally left blank.
+        }
+    }
+
+    /// \brief Load up to limit RRs.
+    ///
+    /// This performs a part of the loading. In case there's enough data in the
+    /// source, it copies limit RRs. It can copy less RRs during the final call
+    /// (when there's less than limit left).
+    ///
+    /// This can be called repeatedly until the whole zone is loaded, having
+    /// pauses in the loading for some purposes (for example reporting
+    /// progress).
+    ///
+    /// \param limit The maximum allowed number of RRs to be loaded during this
+    ///     call.
+    /// \return True in case the loading is completed, false if there's more
+    ///     to load.
+    /// \throw InvalidOperation in case the loading was already completed
+    ///     before this call (by load() or by a loadIncremental that returned
+    ///     true).
+    /// \throw DataSourceError in case some error (possibly low-level) happens.
+    /// \throw MasterFileError when the master_file is badly formatted or some
+    ///     similar problem is found when loading the master file.
+    /// \note If the limit is exactly the number of RRs available to be loaded,
+    ///     the method still returns false and true'll be returned on the next
+    ///     call (which will load 0 RRs). This is because the end of iterator or
+    ///     master file is detected when reading past the end, not when the last
+    ///     one is read.
+    bool loadIncremental(size_t limit);
+private:
+    /// \brief The iterator used as source of data in case of the copy mode.
+    const ZoneIteratorPtr iterator_;
+    /// \brief The destination zone updater
+    const ZoneUpdaterPtr updater_;
+    /// \brief The master loader (for the master file mode)
+    boost::scoped_ptr<isc::dns::MasterLoader> loader_;
+    /// \brief Indicator if loading was completed
+    bool complete_;
+    /// \brief Was the loading successful?
+    bool loaded_ok_;
+};
+
+}
+}
+
+#endif
diff --git a/src/lib/dhcp/Makefile.am b/src/lib/dhcp/Makefile.am
index e9333e2..f41ce53 100644
--- a/src/lib/dhcp/Makefile.am
+++ b/src/lib/dhcp/Makefile.am
@@ -33,14 +33,16 @@ libb10_dhcp___la_SOURCES += option6_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
+libb10_dhcp___la_SOURCES += std_option_defs.h
 
 libb10_dhcp___la_CXXFLAGS = $(AM_CXXFLAGS)
 libb10_dhcp___la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
 libb10_dhcp___la_LIBADD   = $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
+libb10_dhcp___la_LIBADD  += $(top_builddir)/src/lib/dns/libb10-dns++.la
 libb10_dhcp___la_LIBADD  += $(top_builddir)/src/lib/util/libb10-util.la
 libb10_dhcp___la_LDFLAGS  = -no-undefined -version-info 2:0:0
 
-EXTRA_DIST  = README
+EXTRA_DIST  = README libdhcp++.dox
 
 if USE_CLANGPP
 # Disable unused parameter warning caused by some of the
diff --git a/src/lib/dhcp/duid.cc b/src/lib/dhcp/duid.cc
index c3f46da..91efe94 100644
--- a/src/lib/dhcp/duid.cc
+++ b/src/lib/dhcp/duid.cc
@@ -33,7 +33,7 @@ DUID::DUID(const std::vector<uint8_t>& duid) {
     }
 }
 
-DUID::DUID(const uint8_t * data, size_t len) {
+DUID::DUID(const uint8_t* data, size_t len) {
     if (len > MAX_DUID_LEN) {
         isc_throw(OutOfRange, "DUID too large");
     }
@@ -72,36 +72,36 @@ std::string DUID::toText() const {
     return (tmp.str());
 }
 
-bool DUID::operator == (const DUID& other) const {
+bool DUID::operator==(const DUID& other) const {
     return (this->duid_ == other.duid_);
 }
 
-bool DUID::operator != (const DUID& other) const {
+bool DUID::operator!=(const DUID& other) const {
     return (this->duid_ != other.duid_);
 }
 
-/// constructor based on vector<uint8_t>
+// Constructor based on vector<uint8_t>
 ClientId::ClientId(const std::vector<uint8_t>& clientid)
-    :DUID(clientid) {
+    : DUID(clientid) {
 }
 
-/// constructor based on C-style data
+// Constructor based on C-style data
 ClientId::ClientId(const uint8_t *clientid, size_t len)
-    :DUID(clientid, len) {
+    : DUID(clientid, len) {
 }
 
-/// @brief returns a copy of client-id data
+// Returns a copy of client-id data
 const std::vector<uint8_t> ClientId::getClientId() const {
     return (duid_);
 }
 
-// compares two client-ids
-bool ClientId::operator == (const ClientId& other) const {
+// Compares two client-ids
+bool ClientId::operator==(const ClientId& other) const {
     return (this->duid_ == other.duid_);
 }
 
-// compares two client-ids
-bool ClientId::operator != (const ClientId& other) const {
+// Compares two client-ids
+bool ClientId::operator!=(const ClientId& other) const {
     return (this->duid_ != other.duid_);
 }
 
diff --git a/src/lib/dhcp/duid.h b/src/lib/dhcp/duid.h
index 001e362..60b9706 100644
--- a/src/lib/dhcp/duid.h
+++ b/src/lib/dhcp/duid.h
@@ -45,13 +45,13 @@ class DUID {
         DUID_MAX          ///< not a real type, just maximum defined value + 1
     } DUIDType;
 
-    /// @brief creates a DUID
+    /// @brief Constructor from vector
     DUID(const std::vector<uint8_t>& duid);
 
-    /// @brief creates a DUID
-    DUID(const uint8_t *duid, size_t len);
+    /// @brief Constructor from array and array size
+    DUID(const uint8_t* duid, size_t len);
 
-    /// @brief returns a const reference to the actual DUID value
+    /// @brief Returns a const reference to the actual DUID value
     ///
     /// Note: For safety reasons, this method returns a copy of data as
     /// otherwise the reference would be only valid as long as the object that
@@ -60,49 +60,63 @@ class DUID {
     /// (e.g. storeSelf()) that will avoid data copying.
     const std::vector<uint8_t> getDuid() const;
 
-    /// @brief returns DUID type
+    /// @brief Returns the DUID type
     DUIDType getType() const;
 
-    /// returns textual prepresentation (e.g. 00:01:02:03:ff)
+    /// @brief Returns textual representation of a DUID (e.g. 00:01:02:03:ff)
     std::string toText() const;
 
-    /// compares two DUIDs
+    /// @brief Compares two DUIDs for equality
     bool operator==(const DUID& other) const;
 
-    /// compares two DUIDs
+    /// @brief Compares two DUIDs for inequality
     bool operator!=(const DUID& other) const;
 
  protected:
-    /// the actual content of the DUID
+    /// The actual content of the DUID
     std::vector<uint8_t> duid_;
 };
 
+/// @brief Shared pointer to a DUID
 typedef boost::shared_ptr<DUID> DuidPtr;
 
+
+
 /// @brief Holds Client identifier or client IPv4 address
 ///
 /// This class is intended to be a generic IPv4 client identifier. It can hold
 /// a client-id
 class ClientId : DUID {
- public:
+public:
+    /// @brief Maximum size of a client ID
+    ///
+    /// This is the same as the maximum size of the underlying DUID.
+    ///
+    /// @note RFC 2131 does not specify an upper length of a client ID, the
+    ///       value chosen here just being that of the underlying DUID.  For
+    ///       some backend database, there may be a possible (minor)
+    ///       performance enhancement if this were smaller.
+    static const size_t MAX_CLIENT_ID_LEN = DUID::MAX_DUID_LEN;
 
-    /// constructor based on vector<uint8_t>
+    /// @brief Constructor based on vector<uint8_t>
     ClientId(const std::vector<uint8_t>& clientid);
 
-    /// constructor based on C-style data
-    ClientId(const uint8_t *clientid, size_t len);
+    /// @brief Constructor based on array and array size
+    ClientId(const uint8_t* clientid, size_t len);
 
-    /// @brief returns reference to the client-id data
-    ///
+    /// @brief Returns reference to the client-id data
     const std::vector<uint8_t> getClientId() const;
 
-    // compares two client-ids
-    bool operator == (const ClientId& other) const;
+    /// @brief Compares two client-ids for equality
+    bool operator==(const ClientId& other) const;
 
-    // compares two client-ids
-    bool operator != (const ClientId& other) const;
+    /// @brief Compares two client-ids for inequality
+    bool operator!=(const ClientId& other) const;
 };
 
+/// @brief Shared pointer to a Client ID.
+typedef boost::shared_ptr<ClientId> ClientIdPtr;
+
 }; // end of isc::dhcp namespace
 }; // end of isc namespace
 
diff --git a/src/lib/dhcp/libdhcp++.cc b/src/lib/dhcp/libdhcp++.cc
index a997d31..2773d09 100644
--- a/src/lib/dhcp/libdhcp++.cc
+++ b/src/lib/dhcp/libdhcp++.cc
@@ -22,6 +22,7 @@
 #include <dhcp/option6_iaaddr.h>
 #include <dhcp/option6_int_array.h>
 #include <dhcp/option_definition.h>
+#include <dhcp/std_option_defs.h>
 #include <exceptions/exceptions.h>
 #include <util/buffer.h>
 
@@ -45,7 +46,7 @@ OptionDefContainer LibDHCP::v4option_defs_;
 OptionDefContainer LibDHCP::v6option_defs_;
 
 const OptionDefContainer&
-LibDHCP::getOptionDefs(Option::Universe u) {
+LibDHCP::getOptionDefs(const Option::Universe u) {
     switch (u) {
     case Option::V4:
         initStdOptionDefs4();
@@ -60,6 +61,17 @@ LibDHCP::getOptionDefs(Option::Universe u) {
     }
 }
 
+OptionDefinitionPtr
+LibDHCP::getOptionDef(const Option::Universe u, const uint16_t code) {
+    const OptionDefContainer& defs = getOptionDefs(u);
+    const OptionDefContainerTypeIndex& idx = defs.get<1>();
+    const OptionDefContainerTypeRange& range = idx.equal_range(code);
+    if (range.first != range.second) {
+        return (*range.first);
+    }
+    return (OptionDefinitionPtr());
+}
+
 OptionPtr
 LibDHCP::optionFactory(Option::Universe u,
                        uint16_t type,
@@ -254,52 +266,16 @@ void
 LibDHCP::initStdOptionDefs6() {
     v6option_defs_.clear();
 
-    struct OptionParams {
-        std::string name;
-        uint16_t code;
-        OptionDataType type;
-        bool array;
-    };
-    OptionParams params[] = {
-        { "CLIENTID", D6O_CLIENTID, OPT_BINARY_TYPE, false },
-        { "SERVERID", D6O_SERVERID, OPT_BINARY_TYPE, false },
-        { "IA_NA", D6O_IA_NA, OPT_RECORD_TYPE, false },
-        { "IAADDR", D6O_IAADDR, OPT_RECORD_TYPE, false },
-        { "ORO", D6O_ORO, OPT_UINT16_TYPE, true },
-        { "ELAPSED_TIME", D6O_ELAPSED_TIME, OPT_UINT16_TYPE, false },
-        { "STATUS_CODE", D6O_STATUS_CODE, OPT_RECORD_TYPE, false },
-        { "RAPID_COMMIT", D6O_RAPID_COMMIT, OPT_EMPTY_TYPE, false },
-        { "DNS_SERVERS", D6O_NAME_SERVERS, OPT_IPV6_ADDRESS_TYPE, true },
-        { "IA_PD", D6O_IA_PD, OPT_RECORD_TYPE, false }
-    };
-    const int params_size = sizeof(params) / sizeof(params[0]);
-
-    for (int i = 0; i < params_size; ++i) {
-        OptionDefinitionPtr definition(new OptionDefinition(params[i].name,
-                                                            params[i].code,
-                                                            params[i].type,
-                                                            params[i].array));
-        switch(params[i].code) {
-        case D6O_IA_NA:
-        case D6O_IA_PD:
-            for (int j = 0; j < 3; ++j) {
-                definition->addRecordField(OPT_UINT32_TYPE);
-            }
-            break;
-        case D6O_IAADDR:
-            definition->addRecordField(OPT_IPV6_ADDRESS_TYPE);
-            definition->addRecordField(OPT_UINT32_TYPE);
-            definition->addRecordField(OPT_UINT32_TYPE);
-            break;
-        case D6O_STATUS_CODE:
-            definition->addRecordField(OPT_UINT16_TYPE);
-            definition->addRecordField(OPT_STRING_TYPE);
-            break;
-        default:
-            // The default case is intentionally left empty
-            // as it does not need any processing.
-            ;
+    for (int i = 0; i < OPTION_DEF_PARAMS_SIZE6; ++i) {
+        OptionDefinitionPtr definition(new OptionDefinition(OPTION_DEF_PARAMS6[i].name,
+                                                            OPTION_DEF_PARAMS6[i].code,
+                                                            OPTION_DEF_PARAMS6[i].type,
+                                                            OPTION_DEF_PARAMS6[i].array));
+
+        for (int rec = 0; rec < OPTION_DEF_PARAMS6[i].records_size; ++rec) {
+            definition->addRecordField(OPTION_DEF_PARAMS6[i].records[rec]);
         }
+
         try {
             definition->validate();
         } catch (const Exception& ex) {
diff --git a/src/lib/dhcp/libdhcp++.h b/src/lib/dhcp/libdhcp++.h
index cab7928..e855070 100644
--- a/src/lib/dhcp/libdhcp++.h
+++ b/src/lib/dhcp/libdhcp++.h
@@ -42,7 +42,18 @@ public:
     /// @param u universe of the options (V4 or V6).
     ///
     /// @return collection of option definitions.
-    static const OptionDefContainer& getOptionDefs(Option::Universe u);
+    static const OptionDefContainer& getOptionDefs(const Option::Universe u);
+
+    /// @brief Return the first option definition matching a
+    /// particular option code.
+    ///
+    /// @param u universe (V4 or V6)
+    /// @param code option code.
+    ///
+    /// @return reference to an option definition being requested
+    /// or NULL pointer if option definition has not been found.
+    static OptionDefinitionPtr getOptionDef(const Option::Universe u,
+                                            const uint16_t code);
 
     /// @brief Factory function to create instance of option.
     ///
diff --git a/src/lib/dhcp/libdhcsrv.dox b/src/lib/dhcp/libdhcsrv.dox
deleted file mode 100644
index bb4a8ec..0000000
--- a/src/lib/dhcp/libdhcsrv.dox
+++ /dev/null
@@ -1,86 +0,0 @@
-/**
- @page libdhcpsrv libdhcpsrv - Server DHCP library
-
-This library contains code useful for DHCPv4 and DHCPv6 server operations, like
-Lease Manager that stores leases information, configuration manager that stores
-configuration etc. The code here is server specific. For generic (useful in
-server, client, relay and other tools like perfdhcp) code, please see
-\ref libdhcp.
-
-This library contains several crucial elements of the DHCP server operation:
-
-- isc::dhcp::LeaseMgr - Lease Manager is a name for database backend that stores
-  leases.
-- isc::dhcp::CfgMgr - Configuration Manager that holds DHCP specific
-  configuration information (subnets, pools, options, timer values etc.) in
-  easy to use format.
-- AllocEngine - allocation engine that handles new requestes and allocates new
-  leases.
-
- at section leasemgr Lease Manager
-
-LeaseMgr provides a common, unified abstract API for all database backends. All
-backends are derived from the base class isc::dhcp::LeaseMgr. Currently the
-only available backend is MySQL (see \ref isc::dhcp::MySqlLeaseMgr).
-
- at section cfgmgr Configuration Manager
-
-Configuration Manager (\ref isc::dhcp::CfgMgr) stores configuration information
-necessary for DHCPv4 and DHCPv6 server operation. In particular, it stores
-subnets (\ref isc::dhcp::Subnet4 and \ref isc::dhcp::Subnet6) together with
-their pools (\ref isc::dhcp::Pool4 and \ref isc::dhcp::Pool6), options and
-other information specified by the used in BIND10 configuration.
-
- at section allocengine Allocation Engine
-
-Allocation Engine (\ref isc::dhcp::AllocEngine) is what its name say - an engine
-that handles allocation of new leases. It takes parameters that the client
-provided (client-id, DUID, subnet, a hint if the user provided one, etc.) and
-then attempts to allocate a lease.
-
-There is no single best soluction to the address assignment problem. Server
-is expected to pick an address from its available pools is currently not used.
-There are many possible algorithms that can do that, each with its own advantages
-and drawbacks. This allocation engine must provide robust operation is radically
-different scenarios, so there address selection problem was abstracted into
-separate module, called allocator. Its sole purpose is to pick an address from
-a pool. Allocation engine will then check if the picked address is free and if
-it is not, then will ask allocator to pick again.
-
-At lease 3 allocators will be implemented:
-
-- Iterative - it iterates over all addresses in available pools, one
-by one. The advantages of this approach are speed (typically it only needs to
-increase last address), the guarantee to cover all addresses and predictability.
-This allocator behaves very good in case of nearing depletion. Even when pools
-are almost completely allocated, it still will be able to allocate outstanding
-leases efficiently. Predictability can also be considered a serious flaw in
-some environments, as prediction of the next address is trivial and can be
-leveraged by an attacker. Another drawback of this allocator is that it does
-not attempt to give the same address to returning clients (clients that released
-or expired their leases and are requesting a new lease will likely to get a 
-different lease). This allocator is implemented in \ref isc::dhcp::AllocEngine::IterativeAllocator.
-
-- Hashed - ISC-DHCP uses hash of the client-id or DUID to determine, which
-address is tried first. If that address is not available, the result is hashed
-again. That procedure is repeated until available address is found or there
-are no more addresses left. The benefit of that approach is that it provides
-a relative lease stability, so returning old clients are likely to get the same
-address again. The drawbacks are increased computation cost, as each iteration
-requires use of a hashing function. That is especially difficult when the 
-pools are almost depleted. It also may be difficult to guarantee that the
-repeated hashing will iterate over all available addresses in all pools. Flawed
-hash algorithm can go into cycles that iterate over only part of the addresses.
-It is difficult to detect such issues as only some initial seed (client-id
-or DUID) values may trigger short cycles. This allocator is currently not
-implemented.
-
-- Random - Another possible approach to address selection is randomization. This
-allocator can pick an address randomly from the configured pool. The benefit
-of this approach is that it is easy to implement and makes attacks based on
-address prediction more difficult. The drawback of this approach is that
-returning clients are almost guaranteed to get a different address. Another
-drawback is that with almost depleted pools it is increasingly difficult to
-"guess" an address that is free. This allocator is currently not implemented.
-
-*/
\ No newline at end of file
diff --git a/src/lib/dhcp/option.cc b/src/lib/dhcp/option.cc
index 4638025..2a53f0f 100644
--- a/src/lib/dhcp/option.cc
+++ b/src/lib/dhcp/option.cc
@@ -314,6 +314,10 @@ void Option::setData(const OptionBufferConstIter first,
     std::copy(first, last, data_.begin());
 }
 
+bool Option::equal(const OptionPtr& other) const {
+    return ( (getType() == other->getType()) &&
+             (getData() == other->getData()) );
+}
 
 Option::~Option() {
 
diff --git a/src/lib/dhcp/option.h b/src/lib/dhcp/option.h
index a6b0622..3c7799f 100644
--- a/src/lib/dhcp/option.h
+++ b/src/lib/dhcp/option.h
@@ -152,7 +152,7 @@ public:
     /// @brief returns option universe (V4 or V6)
     ///
     /// @return universe type
-    Universe  getUniverse() { return universe_; };
+    Universe  getUniverse() const { return universe_; };
 
     /// @brief Writes option in wire-format to a buffer.
     ///
@@ -197,7 +197,7 @@ public:
     /// Returns option type (0-255 for DHCPv4, 0-65535 for DHCPv6)
     ///
     /// @return option type
-    uint16_t getType() { return (type_); }
+    uint16_t getType() const { return (type_); }
 
     /// Returns length of the complete option (data length + DHCPv4/DHCPv6
     /// option header)
@@ -219,7 +219,7 @@ public:
     ///
     /// @return pointer to actual data (or reference to an empty vector
     ///         if there is no data)
-    virtual const OptionBuffer& getData() { return (data_); }
+    virtual const OptionBuffer& getData() const { return (data_); }
 
     /// Adds a sub-option.
     ///
@@ -303,6 +303,19 @@ public:
     /// just to force that every option has virtual dtor
     virtual ~Option();
 
+    /// @brief Checks if two options are equal
+    ///
+    /// Equality verifies option type and option content. Care should
+    /// be taken when using this method. Implementation for derived
+    /// classes should be provided when this method is expected to be
+    /// used. It is safe in general, as the first check (different types)
+    /// will detect differences between base Option and derived
+    /// objects.
+    ///
+    /// @param other the other option
+    /// @return true if both options are equal
+    virtual bool equal(const OptionPtr& other) const;
+
 protected:
     /// Builds raw (over-wire) buffer of this option, including all
     /// defined suboptions. Version for building DHCPv4 options.
diff --git a/src/lib/dhcp/option4_addrlst.h b/src/lib/dhcp/option4_addrlst.h
index b266cbf..927f75b 100644
--- a/src/lib/dhcp/option4_addrlst.h
+++ b/src/lib/dhcp/option4_addrlst.h
@@ -111,10 +111,10 @@ public:
     /// We return a copy of our list. Although this includes overhead,
     /// it also makes this list safe to use after this option object
     /// is no longer available. As options are expected to hold only
-    /// a couple (1-3) addresses, the overhead is not that big.
+    /// a few (1-3) addresses, the overhead is not that big.
     ///
     /// @return address container with addresses
-    AddressContainer getAddresses() { return addrs_; };
+    AddressContainer getAddresses() const { return addrs_; };
 
     /// @brief Sets addresses list.
     ///
diff --git a/src/lib/dhcp/option6_addrlst.h b/src/lib/dhcp/option6_addrlst.h
index b9c0deb..8327201 100644
--- a/src/lib/dhcp/option6_addrlst.h
+++ b/src/lib/dhcp/option6_addrlst.h
@@ -82,10 +82,10 @@ public:
     /// We return a copy of our list. Although this includes overhead,
     /// it also makes this list safe to use after this option object
     /// is no longer available. As options are expected to hold only
-    /// a couple (1-3) addresses, the overhead is not that big.
+    /// a few (1-3) addresses, the overhead is not that big.
     ///
     /// @return address container with addresses
-    AddressContainer getAddresses() { return addrs_; };
+    AddressContainer getAddresses() const { return addrs_; };
 
     // returns data length (data length + DHCPv4/DHCPv6 option header)
     virtual uint16_t len();
diff --git a/src/lib/dhcp/option6_ia.h b/src/lib/dhcp/option6_ia.h
index 54624d0..32f3667 100644
--- a/src/lib/dhcp/option6_ia.h
+++ b/src/lib/dhcp/option6_ia.h
@@ -68,12 +68,17 @@ public:
     /// Sets T1 timer.
     ///
     /// @param t1 t1 value to be set
-    void setT1(uint32_t t1) { t1_=t1; }
+    void setT1(uint32_t t1) { t1_ = t1; }
 
     /// Sets T2 timer.
     ///
     /// @param t2 t2 value to be set
-    void setT2(uint32_t t2) { t2_=t2; }
+    void setT2(uint32_t t2) { t2_ = t2; }
+
+    /// Sets Identity Association Identifier.
+    ///
+    /// @param iaid IAID value to be set
+    void setIAID(uint32_t iaid) { iaid_ = iaid; }
 
     /// Returns IA identifier.
     ///
diff --git a/src/lib/dhcp/option_custom.cc b/src/lib/dhcp/option_custom.cc
index 8b3ef11..0c3cb81 100644
--- a/src/lib/dhcp/option_custom.cc
+++ b/src/lib/dhcp/option_custom.cc
@@ -21,11 +21,28 @@ namespace isc {
 namespace dhcp {
 
 OptionCustom::OptionCustom(const OptionDefinition& def,
+                           Universe u)
+    : Option(u, def.getCode(), OptionBuffer()),
+      definition_(def) {
+    createBuffers();
+}
+
+OptionCustom::OptionCustom(const OptionDefinition& def,
                              Universe u,
                              const OptionBuffer& data)
     : Option(u, def.getCode(), data.begin(), data.end()),
       definition_(def) {
-    createBuffers();
+    // It is possible that no data is provided if an option
+    // is being created on a server side. In such case a bunch
+    // of buffers with default values is first created and then
+    // the values are replaced using writeXXX functions. Thus
+    // we need to detect that no data has been specified and
+    // take a different code path.
+    if (!data_.empty()) {
+        createBuffers(data_);
+    } else {
+        createBuffers();
+    }
 }
 
 OptionCustom::OptionCustom(const OptionDefinition& def,
@@ -34,25 +51,156 @@ OptionCustom::OptionCustom(const OptionDefinition& def,
                              OptionBufferConstIter last)
     : Option(u, def.getCode(), first, last),
       definition_(def) {
-    createBuffers();
+    createBuffers(data_);
+}
+
+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)) {
+        isc_throw(BadDataTypeCast, "invalid address specified "
+                  << address.toText() << ". Expected a valid IPv"
+                  << (definition_.getType() == OPT_IPV4_ADDRESS_TYPE ? "4" : "6")
+                  << " address.");
+    }
+
+    OptionBuffer buf;
+    OptionDataTypeUtil::writeAddress(address, buf);
+    buffers_.push_back(buf);
+}
+
+void
+OptionCustom::addArrayDataField(const bool value) {
+    checkArrayType();
+
+    OptionBuffer buf;
+    OptionDataTypeUtil::writeBool(value, buf);
+    buffers_.push_back(buf);
 }
 
 void
 OptionCustom::checkIndex(const uint32_t index) const {
     if (index >= buffers_.size()) {
         isc_throw(isc::OutOfRange, "specified data field index " << index
-                  << " is out of rangex.");
+                  << " is out of range.");
+    }
+}
+
+template<typename T>
+void
+OptionCustom::checkDataType(const uint32_t index) const {
+    // Check that the requested return type is a supported integer.
+    if (!OptionDataTypeTraits<T>::integer_type) {
+        isc_throw(isc::dhcp::InvalidDataType, "specified data type"
+                  " is not a supported integer type.");
+    }
+
+    // Get the option definition type.
+    OptionDataType data_type = definition_.getType();
+    if (data_type == OPT_RECORD_TYPE) {
+        const OptionDefinition::RecordFieldsCollection& record_fields =
+            definition_.getRecordFields();
+        // When we initialized buffers we have already checked that
+        // the number of these buffers is equal to number of option
+        // fields in the record so the condition below should be met.
+        assert(index < record_fields.size());
+        // Get the data type to be returned.
+        data_type = record_fields[index];
+    }
+
+    if (OptionDataTypeTraits<T>::type != data_type) {
+        isc_throw(isc::dhcp::InvalidDataType,
+                  "specified data type " << data_type << " does not"
+                  " match the data type in an option definition for field"
+                  " index " << index);
     }
 }
 
 void
 OptionCustom::createBuffers() {
+    definition_.validate();
+
+    std::vector<OptionBuffer> buffers;
+
+    OptionDataType data_type = definition_.getType();
+    // This function is called when an empty data buffer has been
+    // passed to the constructor. In such cases values for particular
+    // data fields will be set using modifier functions but for now
+    // we need to initialize a set of buffers that are specified
+    // for an option by its definition. Since there is no data yet,
+    // we are going to fill these buffers with default values.
+    if (data_type == OPT_RECORD_TYPE) {
+        // For record types we need to iterate over all data fields
+        // specified in option definition and create corresponding
+        // buffers for each of them.
+        const OptionDefinition::RecordFieldsCollection fields =
+            definition_.getRecordFields();
+
+        for (OptionDefinition::RecordFieldsConstIter field = fields.begin();
+             field != fields.end(); ++field) {
+            OptionBuffer buf;
+
+            // For data types that have a fixed size we can use the
+            // utility function to get the buffer's size.
+            size_t data_size = OptionDataTypeUtil::getDataTypeLen(*field);
+
+            // For variable data sizes the utility function returns zero.
+            // It is ok for string values because the default string
+            // is 'empty'. However for FQDN the empty value is not valid
+            // so we initialize it to '.'.
+            if (data_size == 0 &&
+                *field == OPT_FQDN_TYPE) {
+                OptionDataTypeUtil::writeFqdn(".", buf);
+            } else {
+                // At this point we can resize the buffer. Note that
+                // for string values we are setting the empty buffer
+                // here.
+                buf.resize(data_size);
+            }
+            // We have the buffer with default value prepared so we
+            // add it to the set of buffers.
+            buffers.push_back(buf);
+        }
+    } else if (!definition_.getArrayType() &&
+               data_type != OPT_EMPTY_TYPE) {
+        // For either 'empty' options we don't have to create any buffers
+        // for obvious reason. For arrays we also don't create any buffers
+        // yet because the set of fields that belong to the array is open
+        // ended so we can't allocate required buffers until we know how
+        // many of them are needed.
+        // For non-arrays we have a single value being held by the option
+        // so we have to allocate exactly one buffer.
+        OptionBuffer buf;
+        size_t data_size = OptionDataTypeUtil::getDataTypeLen(data_type);
+        if (data_size == 0 &&
+            data_type == OPT_FQDN_TYPE) {
+            OptionDataTypeUtil::writeFqdn(".", buf);
+        } else {
+            // Note that if our option holds a string value then
+            // we are making empty buffer here.
+            buf.resize(data_size);
+        }
+        // Add a buffer that we have created and leave.
+        buffers.push_back(buf);
+    }
+    // The 'swap' is used here because we want to make sure that we
+    // don't touch buffers_ until we successfully allocate all
+    // buffers to be stored there.
+    std::swap(buffers, buffers_);
+}
+
+void
+OptionCustom::createBuffers(const OptionBuffer& data_buf) {
     // Check that the option definition is correct as we are going
     // to use it to split the data_ buffer into set of sub buffers.
     definition_.validate();
 
     std::vector<OptionBuffer> buffers;
-    OptionBuffer::iterator data = data_.begin();
+    OptionBuffer::const_iterator data = data_buf.begin();
 
     OptionDataType data_type = definition_.getType();
     if (data_type == OPT_RECORD_TYPE) {
@@ -68,17 +216,31 @@ OptionCustom::createBuffers() {
             // For fixed-size data type such as boolean, integer, even
             // IP address we can use the utility function to get the required
             // buffer size.
-            int data_size = OptionDataTypeUtil::getDataTypeLen(*field);
-
-            // For variable size types (such as string) the function above
-            // will return 0 so we need to do a runtime check. Since variable
-            // length data fields may be laid only at the end of an option we
-            // consume the rest of this option. Note that validate() function
-            // in OptionDefinition object should have checked whether the
-            // data fields layout is correct (that the variable string fields
-            // are laid at the end).
+            size_t data_size = OptionDataTypeUtil::getDataTypeLen(*field);
+
+            // For variable size types (e.g. string) the function above will
+            // return 0 so we need to do a runtime check of the length.
             if (data_size == 0) {
-                data_size = std::distance(data, data_.end());
+                // FQDN is a special data type as it stores variable length data
+                // but the data length is encoded in the buffer. The easiest way
+                // to obtain the length of the data is to read the FQDN. The
+                // utility function will return the size of the buffer on success.
+                if (*field == OPT_FQDN_TYPE) {
+                    std::string fqdn =
+                        OptionDataTypeUtil::readFqdn(OptionBuffer(data, data_buf.end()));
+                    // The size of the buffer holding an FQDN is always
+                    // 1 byte larger than the size of the string
+                    // representation of this FQDN.
+                    data_size = fqdn.size() + 1;
+                } else {
+                    // In other case we are dealing with string or binary value
+                    // which size can't be determined. Thus we consume the
+                    // remaining part of the buffer for it. Note that variable
+                    // size data can be laid at the end of the option only and
+                    // that the validate() function in OptionDefinition object
+                    // should have checked wheter it is a case for this option.
+                    data_size = std::distance(data, data_buf.end());
+                }
                 if (data_size == 0) {
                     // If we reached the end of buffer we assume that this option is
                     // truncated because there is no remaining data to initialize
@@ -90,7 +252,7 @@ OptionCustom::createBuffers() {
             } else {
                 // Our data field requires that there is a certain chunk of
                 // data left in the buffer. If not, option is truncated.
-                if (std::distance(data, data_.end()) < data_size) {
+                if (std::distance(data, data_buf.end()) < data_size) {
                     isc_throw(OutOfRange, "option buffer truncated");
                 }
             }
@@ -105,39 +267,64 @@ OptionCustom::createBuffers() {
         // data fields of the same type. The type of those fields
         // is held in the data_type variable so let's use it to determine
         // a size of buffers.
-        int data_size = OptionDataTypeUtil::getDataTypeLen(data_type);
+        size_t data_size = OptionDataTypeUtil::getDataTypeLen(data_type);
         // The check below will fail if the input buffer is too short
         // for the data size being held by this option.
         // Note that data_size returned by getDataTypeLen may be zero
         // if variable length data is being held by the option but
         // this will not cause this check to throw exception.
-        if (std::distance(data, data_.end()) < data_size) {
+        if (std::distance(data, data_buf.end()) < data_size) {
             isc_throw(OutOfRange, "option buffer truncated");
         }
         // For an array of values we are taking different path because
         // we have to handle multiple buffers.
         if (definition_.getArrayType()) {
-            // We don't perform other checks for data types that can't be
-            // used together with array indicator such as strings, empty field
-            // etc. This is because OptionDefinition::validate function should
-            // have checked this already. Thus data_size must be greater than
-            // zero.
-            assert(data_size > 0);
-            // Get equal chunks of data and store as collection of buffers.
-            // Truncate any remaining part which length is not divisible by
-            // data_size. Note that it is ok to truncate the data if and only
-            // if the data buffer is long enough to keep at least one value.
-            // This has been checked above already.
-            do {
+            while (data != data_buf.end()) {
+                // FQDN is a special case because it is of a variable length.
+                // The actual length for a particular FQDN is encoded within
+                // a buffer so we have to actually read the FQDN from a buffer
+                // to get it.
+                if (data_type == OPT_FQDN_TYPE) {
+                    std::string fqdn =
+                        OptionDataTypeUtil::readFqdn(OptionBuffer(data, data_buf.end()));
+                    // The size of the buffer holding an FQDN is always
+                    // 1 byte larger than the size of the string
+                    // representation of this FQDN.
+                    data_size = fqdn.size() + 1;
+                }
+                // We don't perform other checks for data types that can't be
+                // used together with array indicator such as strings, empty field
+                // etc. This is because OptionDefinition::validate function should
+                // have checked this already. Thus data_size must be greater than
+                // zero.
+                assert(data_size > 0);
+                // Get chunks of data and store as a collection of buffers.
+                // Truncate any remaining part which length is not divisible by
+                // data_size. Note that it is ok to truncate the data if and only
+                // if the data buffer is long enough to keep at least one value.
+                // This has been checked above already.
+                if (std::distance(data, data_buf.end()) < data_size) {
+                    break;
+                }
                 buffers.push_back(OptionBuffer(data, data + data_size));
                 data += data_size;
-            } while (std::distance(data, data_.end()) >= data_size);
+            }
         } else {
             // For non-arrays the data_size can be zero because
             // getDataTypeLen returns zero for variable size data types
             // such as strings. Simply take whole buffer.
             if (data_size == 0) {
-                data_size = std::distance(data, data_.end());
+                // For FQDN we get the size by actually reading the FQDN.
+                if (data_type == OPT_FQDN_TYPE) {
+                    std::string fqdn =
+                        OptionDataTypeUtil::readFqdn(OptionBuffer(data, data_buf.end()));
+                    // The size of the buffer holding an FQDN is always
+                    // 1 bytes larger than the size of the string
+                    // representation of this FQDN.
+                    data_size = fqdn.size() + 1;
+                } else {
+                    data_size = std::distance(data, data_buf.end());
+                }
             }
             if (data_size > 0) {
                 buffers.push_back(OptionBuffer(data, data + data_size));
@@ -185,6 +372,9 @@ OptionCustom::dataFieldToText(const OptionDataType data_type,
     case OPT_IPV6_ADDRESS_TYPE:
         text << readAddress(index).toText();
         break;
+    case OPT_FQDN_TYPE:
+        text << readFqdn(index);
+        break;
     case OPT_STRING_TYPE:
         text << readString(index);
         break;
@@ -252,8 +442,31 @@ OptionCustom::readAddress(const uint32_t index) const {
         return (OptionDataTypeUtil::readAddress(buffers_[index], AF_INET6));
     } else {
         isc_throw(BadDataTypeCast, "unable to read data from the buffer as"
-                  << " IP address. Invalid buffer length " << buffers_[index].size());
+                  << " IP address. Invalid buffer length "
+                  << buffers_[index].size() << ".");
+    }
+}
+
+void
+OptionCustom::writeAddress(const asiolink::IOAddress& address,
+                           const uint32_t index) {
+    using namespace isc::asiolink;
+
+    checkIndex(index);
+
+    if ((address.getFamily() == AF_INET &&
+         buffers_[index].size() != V4ADDRESS_LEN) ||
+        (address.getFamily() == AF_INET6 &&
+         buffers_[index].size() != V6ADDRESS_LEN)) {
+        isc_throw(BadDataTypeCast, "invalid address specified "
+                  << address.toText() << ". Expected a valid IPv"
+                  << (buffers_[index].size() == V4ADDRESS_LEN ? "4" : "6")
+                  << " address.");
     }
+
+    OptionBuffer buf;
+    OptionDataTypeUtil::writeAddress(address, buf);
+    std::swap(buf, buffers_[index]);
 }
 
 const OptionBuffer&
@@ -262,12 +475,50 @@ OptionCustom::readBinary(const uint32_t index) const {
     return (buffers_[index]);
 }
 
+void
+OptionCustom::writeBinary(const OptionBuffer& buf,
+                          const uint32_t index) {
+    checkIndex(index);
+    buffers_[index] = buf;
+}
+
 bool
 OptionCustom::readBoolean(const uint32_t index) const {
     checkIndex(index);
     return (OptionDataTypeUtil::readBool(buffers_[index]));
 }
 
+void
+OptionCustom::writeBoolean(const bool value, const uint32_t index) {
+    checkIndex(index);
+
+    buffers_[index].clear();
+    OptionDataTypeUtil::writeBool(value, buffers_[index]);
+}
+
+std::string
+OptionCustom::readFqdn(const uint32_t index) const {
+    checkIndex(index);
+    return (OptionDataTypeUtil::readFqdn(buffers_[index]));
+}
+
+void
+OptionCustom::writeFqdn(const std::string& fqdn, const uint32_t index) {
+    checkIndex(index);
+
+    // Create a temporay buffer where the FQDN will be written.
+    OptionBuffer buf;
+    // Try to write to the temporary buffer rather than to the
+    // buffers_ member directly guarantees that we don't modify
+    // (clear) buffers_ until we are sure that the provided FQDN
+    // is valid.
+    OptionDataTypeUtil::writeFqdn(fqdn, buf);
+    // If we got to this point it means that the FQDN is valid.
+    // We can move the contents of the teporary buffer to the
+    // target buffer.
+    std::swap(buffers_[index], buf);
+}
+
 std::string
 OptionCustom::readString(const uint32_t index) const {
     checkIndex(index);
@@ -275,11 +526,26 @@ OptionCustom::readString(const uint32_t index) const {
 }
 
 void
+OptionCustom::writeString(const std::string& text, const uint32_t index) {
+    checkIndex(index);
+
+    // Let's clear a buffer as we want to replace the value of the
+    // whole buffer. If we fail to clear the buffer the data will
+    // be appended.
+    buffers_[index].clear();
+    // If the text value is empty we can leave because the buffer
+    // is already empty.
+    if (!text.empty()) {
+        OptionDataTypeUtil::writeString(text, buffers_[index]);
+    }
+}
+
+void
 OptionCustom::unpack(OptionBufferConstIter begin,
                      OptionBufferConstIter end) {
     data_ = OptionBuffer(begin, end);
     // Chop the buffer stored in data_ into set of sub buffers.
-    createBuffers();
+    createBuffers(data_);
 }
 
 uint16_t
@@ -311,7 +577,7 @@ void OptionCustom::setData(const OptionBufferConstIter first,
 
     // Chop the data_ buffer into set of buffers that represent
     // option fields data.
-    createBuffers();
+    createBuffers(data_);
 }
 
 std::string OptionCustom::toText(int indent) {
diff --git a/src/lib/dhcp/option_custom.h b/src/lib/dhcp/option_custom.h
index ad248c5..0ee4688 100644
--- a/src/lib/dhcp/option_custom.h
+++ b/src/lib/dhcp/option_custom.h
@@ -40,6 +40,17 @@ public:
 
     /// @brief Constructor, used for options to be sent.
     ///
+    /// This constructor creates an instance of an option with default
+    /// data set for all data fields. The option buffers are allocated
+    /// according to data size being stored in particular data fields.
+    /// For variable size data empty buffers are created.
+    ///
+    /// @param def option definition.
+    /// @param u specifies universe (V4 or V6)
+    OptionCustom(const OptionDefinition& def, Universe u);
+
+    /// @brief Constructor, used for options to be sent.
+    ///
     /// This constructor creates an instance of an option from the whole
     /// supplied buffer. This constructor is mainly used to create an
     /// instances of options to be stored in outgoing DHCP packets.
@@ -76,6 +87,37 @@ public:
     OptionCustom(const OptionDefinition& def, Universe u,
                  OptionBufferConstIter first, OptionBufferConstIter last);
 
+    /// @brief Create new buffer and set its value as an IP address.
+    ///
+    /// @param address IPv4 or IPv6 address to be written to
+    /// a buffer being created.
+    void addArrayDataField(const asiolink::IOAddress& address);
+
+    /// @brief Create new buffer and store boolean value in it.
+    ///
+    /// @param value value to be stored in the created buffer.
+    void addArrayDataField(const bool value);
+
+    /// @brief Create new buffer and store integer value in it.
+    ///
+    /// @param value value to be stored in the created buffer.
+    /// @tparam T integer type of the value being stored.
+    template<typename T>
+    void addArrayDataField(const T value) {
+        checkArrayType();
+
+        OptionDataType data_type = definition_.getType();
+        if (OptionDataTypeTraits<T>::type != data_type) {
+            isc_throw(isc::dhcp::InvalidDataType,
+                      "specified data type " << data_type << " does not"
+                      " match the data type in an option definition");
+        }
+
+        OptionBuffer buf;
+        OptionDataTypeUtil::writeInt<T>(value, buf);
+        buffers_.push_back(buf);
+    }
+
     /// @brief Return a number of the data fields.
     ///
     /// @return number of data fields held by the option.
@@ -87,7 +129,17 @@ public:
     ///
     /// @return IP address read from a buffer.
     /// @throw isc::OutOfRange if index is out of range.
-    asiolink::IOAddress readAddress(const uint32_t index) const;
+    asiolink::IOAddress readAddress(const uint32_t index = 0) const;
+
+    /// @brief Write an IP address into a buffer.
+    ///
+    /// @param address IP address being written.
+    /// @param index buffer index.
+    ///
+    /// @throw isc::OutOfRange if index is out of range.
+    /// @throw isc::dhcp::BadDataTypeCast if IP address is invalid.
+    void writeAddress(const asiolink::IOAddress& address,
+                      const uint32_t index = 0);
 
     /// @brief Read a buffer as binary data.
     ///
@@ -95,7 +147,13 @@ public:
     ///
     /// @throw isc::OutOfRange if index is out of range.
     /// @return read buffer holding binary data.
-    const OptionBuffer& readBinary(const uint32_t index) const;
+    const OptionBuffer& readBinary(const uint32_t index = 0) const;
+
+    /// @brief Write binary data into a buffer.
+    ///
+    /// @param buf buffer holding binary data to be written.
+    /// @param index buffer index.
+    void writeBinary(const OptionBuffer& buf, const uint32_t index = 0);
 
     /// @brief Read a buffer as boolean value.
     ///
@@ -103,7 +161,33 @@ public:
     ///
     /// @throw isc::OutOfRange if index is out of range.
     /// @return read boolean value.
-    bool readBoolean(const uint32_t index) const;
+    bool readBoolean(const uint32_t index = 0) const;
+
+    /// @brief Write a boolean value into a buffer.
+    ///
+    /// @param value boolean value to be written.
+    /// @param index buffer index.
+    ///
+    /// @throw isc::OutOfRange if index is out of range.
+    void writeBoolean(const bool value, const uint32_t index = 0);
+
+    /// @brief Read a buffer as FQDN.
+    ///
+    /// @param index buffer index.
+    ///
+    /// @throw isc::OutOfRange if buffer index is out of range.
+    /// @throw isc::dhcp::BadDataTypeCast if a buffer being read
+    /// does not hold a valid FQDN.
+    /// @return string representation if FQDN.
+    std::string readFqdn(const uint32_t index = 0) const;
+
+    /// @brief Write an FQDN into a buffer.
+    ///
+    /// @param fqdn text representation of FQDN.
+    /// @param index buffer index.
+    ///
+    /// @throw isc::OutOfRange if index is out of range.
+    void writeFqdn(const std::string& fqdn, const uint32_t index = 0);
 
     /// @brief Read a buffer as integer value.
     ///
@@ -111,38 +195,15 @@ public:
     /// @tparam integer type of a value being returned.
     ///
     /// @throw isc::OutOfRange if index is out of range.
+    /// @throw isc::dhcp::InvalidDataType if T is invalid.
     /// @return read integer value.
     template<typename T>
-    T readInteger(const uint32_t index) const {
+    T readInteger(const uint32_t index = 0) const {
+        // Check that the index is not out of range.
         checkIndex(index);
-
-        // Check that the requested return type is a supported integer.
-        if (!OptionDataTypeTraits<T>::integer_type) {
-            isc_throw(isc::dhcp::InvalidDataType, "specified data type to be returned"
-                      " by readInteger is not supported integer type");
-        }
-
-        // Get the option definition type.
-        OptionDataType data_type = definition_.getType();
-        if (data_type == OPT_RECORD_TYPE) {
-            const OptionDefinition::RecordFieldsCollection& record_fields =
-                definition_.getRecordFields();
-            // When we initialized buffers we have already checked that
-            // the number of these buffers is equal to number of option
-            // fields in the record so the condition below should be met.
-            assert(index < record_fields.size());
-            // Get the data type to be returned.
-            data_type = record_fields[index];
-        }
-
-        // Requested data type must match the data type in a record.
-        if (OptionDataTypeTraits<T>::type != data_type) {
-            isc_throw(isc::dhcp::InvalidDataType,
-                      "unable to read option field with index " << index
-                      << " as integer value. The field's data type"
-                      << data_type << " does not match the integer type"
-                      << "returned by the readInteger function.");
-        }
+        // Check that T points to a valid integer type and this type
+        // is consistent with an option definition.
+        checkDataType<T>(index);
         // When we created the buffer we have checked that it has a
         // valid size so this condition here should be always fulfiled.
         assert(buffers_[index].size() == OptionDataTypeTraits<T>::len);
@@ -150,13 +211,43 @@ public:
         return (OptionDataTypeUtil::readInt<T>(buffers_[index]));
     }
 
+    /// @brief Write an integer value into a buffer.
+    ///
+    /// @param value integer value to be written.
+    /// @param index buffer index.
+    /// @tparam T integer type of a value being written.
+    ///
+    /// @throw isc::OutOfRange if index is out of range.
+    /// @throw isc::dhcp::InvalidDataType if T is invalid.
+    template<typename T>
+    void writeInteger(const T value, const uint32_t index = 0) {
+        // Check that the index is not out of range.
+        checkIndex(index);
+        // Check that T points to a valid integer type and this type
+        // is consistent with an option definition.
+        checkDataType<T>(index);
+        // Get some temporary buffer.
+        OptionBuffer buf;
+        // Try to write to the buffer.
+        OptionDataTypeUtil::writeInt<T>(value, buf);
+        // If successful, replace the old buffer with new one.
+        std::swap(buffers_[index], buf);
+    }
+
     /// @brief Read a buffer as string value.
     ///
     /// @param index buffer index.
     ///
     /// @return string value read from buffer.
     /// @throw isc::OutOfRange if index is out of range.
-    std::string readString(const uint32_t index) const;
+    std::string readString(const uint32_t index = 0) const;
+
+    /// @brief Write a string value into a buffer.
+    ///
+    /// @param text the string value to be written.
+    /// @param index buffer index.
+    void writeString(const std::string& text,
+                     const uint32_t index = 0);
 
     /// @brief Parses received buffer.
     ///
@@ -201,6 +292,33 @@ protected:
 
 private:
 
+    /// @brief Verify that the option comprises an array of values.
+    ///
+    /// This helper function is used by createArrayEntry functions
+    /// and throws an exception if the particular option is not
+    /// an array.
+    ///
+    /// @throw isc::InvalidOperation if option is not an array.
+    inline void checkArrayType() const {
+        if (!definition_.getArrayType()) {
+            isc_throw(InvalidOperation, "failed to add new array entry to an"
+                      << " option. The option is not an array.");
+        }
+    }
+
+    /// @brief Verify that the integer type is consistent with option
+    /// field type.
+    ///
+    /// This convenience function checks that the data type specified as T
+    /// is consistent with a type of a data field identified by index.
+    ///
+    /// @param index data field index.
+    /// @tparam data type to be validated.
+    ///
+    /// @throw isc::dhcp::InvalidDataType if the type is invalid.
+    template<typename T>
+    void checkDataType(const uint32_t index) const;
+
     /// @brief Check if data field index is valid.
     ///
     /// @param index Data field index to check.
@@ -208,9 +326,14 @@ private:
     /// @throw isc::OutOfRange if index is out of range.
     void checkIndex(const uint32_t index) const;
 
-    /// @brief Create collection of buffers representing data field values.
+    /// @brief Create a collection of non initialized buffers.
     void createBuffers();
 
+    /// @brief Create collection of buffers representing data field values.
+    ///
+    /// @param data_buf a buffer to be parsed.
+    void createBuffers(const OptionBuffer& data_buf);
+
     /// @brief Return a text representation of a data field.
     ///
     /// @param data_type data type of a field.
diff --git a/src/lib/dhcp/option_data_types.cc b/src/lib/dhcp/option_data_types.cc
index 46aa663..0c512d7 100644
--- a/src/lib/dhcp/option_data_types.cc
+++ b/src/lib/dhcp/option_data_types.cc
@@ -13,6 +13,8 @@
 // PERFORMANCE OF THIS SOFTWARE.
 
 #include <dhcp/option_data_types.h>
+#include <dns/labelsequence.h>
+#include <dns/name.h>
 #include <util/encode/hex.h>
 
 namespace isc {
@@ -207,9 +209,46 @@ OptionDataTypeUtil::writeBool(const bool value,
 }
 
 std::string
+OptionDataTypeUtil::readFqdn(const std::vector<uint8_t>& buf) {
+    // If buffer is empty emit an error.
+    if (buf.empty()) {
+        isc_throw(BadDataTypeCast, "unable to read FQDN from a buffer."
+                  << " The buffer is empty.");
+    }
+    // Set up an InputBuffer so as we can use isc::dns::Name object to get the FQDN.
+    isc::util::InputBuffer in_buf(static_cast<const void*>(&buf[0]), buf.size());
+    try {
+        // Try to create an object from the buffer. If exception is thrown
+        // it means that the buffer doesn't hold a valid domain name (invalid
+        // syntax).
+        isc::dns::Name name(in_buf);
+        return (name.toText());
+    } catch (const isc::Exception& ex) {
+        // Unable to convert the data in the buffer into FQDN.
+        isc_throw(BadDataTypeCast, ex.what());
+    }
+}
+
+void
+OptionDataTypeUtil::writeFqdn(const std::string& fqdn,
+                              std::vector<uint8_t>& buf) {
+    try {
+        isc::dns::Name name(fqdn);
+        isc::dns::LabelSequence labels(name);
+        if (labels.getDataLength() > 0) {
+            size_t read_len = 0;
+            const uint8_t* data = labels.getData(&read_len);
+            buf.insert(buf.end(), data, data + read_len);
+        }
+    } catch (const isc::Exception& ex) {
+        isc_throw(BadDataTypeCast, ex.what());
+    }
+}
+
+std::string
 OptionDataTypeUtil::readString(const std::vector<uint8_t>& buf) {
     std::string value;
-    if (buf.size() > 0) {
+    if (!buf.empty()) {
         value.insert(value.end(), buf.begin(), buf.end());
     }
     return (value);
diff --git a/src/lib/dhcp/option_data_types.h b/src/lib/dhcp/option_data_types.h
index afc6d93..ff5789d 100644
--- a/src/lib/dhcp/option_data_types.h
+++ b/src/lib/dhcp/option_data_types.h
@@ -225,11 +225,13 @@ public:
     /// @return data type size or zero for variable length types.
     static int getDataTypeLen(const OptionDataType data_type);
 
-    /// @brief Read IPv4 or IPv6 addres from a buffer.
+    /// @brief Read IPv4 or IPv6 address from a buffer.
     ///
     /// @param buf input buffer.
     /// @param family address family: AF_INET or AF_INET6.
     /// 
+    /// @throw isc::dhcp::BadDataTypeCast when the data being read
+    /// is truncated.
     /// @return address being read.
     static asiolink::IOAddress readAddress(const std::vector<uint8_t>& buf,
                                            const short family);
@@ -252,6 +254,9 @@ public:
     /// @brief Read boolean value from a buffer.
     ///
     /// @param buf input buffer.
+    ///
+    /// @throw isc::dhcp::BadDataTypeCast when the data being read
+    /// is truncated or the value is invalid (neither 1 nor 0).
     /// @return boolean value read from a buffer.
     static bool readBool(const std::vector<uint8_t>& buf);
 
@@ -268,6 +273,9 @@ public:
     ///
     /// @param buf input buffer.
     /// @tparam integer type of the returned value.
+    ///
+    /// @throw isc::dhcp::BadDataTypeCast when the data in the buffer
+    /// is truncated.
     /// @return integer value being read.
     template<typename T>
     static T readInt(const std::vector<uint8_t>& buf) {
@@ -276,7 +284,12 @@ public:
                       " by readInteger is unsupported integer type");
         }
 
-        assert(buf.size() == OptionDataTypeTraits<T>::len);
+        if (buf.size() < OptionDataTypeTraits<T>::len) {
+            isc_throw(isc::dhcp::BadDataTypeCast,
+                      "failed to read an integer value from a buffer"
+                      << " - buffer is truncated.");
+        }
+
         T value;
         switch (OptionDataTypeTraits<T>::len) {
         case 1:
@@ -332,6 +345,33 @@ public:
         }
     }
 
+    /// @brief Read FQDN from a buffer as a string value.
+    ///
+    /// The format of an FQDN within a buffer complies with RFC1035,
+    /// section 3.1.
+    ///
+    /// @param buf input buffer holding a FQDN.
+    ///
+    /// @throw BadDataTypeCast if a FQDN stored within a buffer is
+    /// invalid (e.g. empty, contains invalid characters, truncated).
+    /// @return fully qualified domain name in a text form.
+    static std::string readFqdn(const std::vector<uint8_t>& buf);
+
+    /// @brief Append FQDN into a buffer.
+    ///
+    /// This method appends the Fully Qualified Domain Name (FQDN)
+    /// represented as string value into a buffer. The format of
+    /// the FQDN being stored into a buffer complies with RFC1035,
+    /// section 3.1.
+    ///
+    /// @param fqdn fully qualified domain name to be written.
+    /// @param [out] buf output buffer.
+    ///
+    /// @throw isc::dhcp::BadDataTypeCast if provided FQDN
+    /// is invalid.
+    static void writeFqdn(const std::string& fqdn,
+                          std::vector<uint8_t>& buf);
+
     /// @brief Read string value from a buffer.
     ///
     /// @param buf input buffer.
diff --git a/src/lib/dhcp/option_definition.cc b/src/lib/dhcp/option_definition.cc
index 58d0c4b..2248bd7 100644
--- a/src/lib/dhcp/option_definition.cc
+++ b/src/lib/dhcp/option_definition.cc
@@ -19,6 +19,7 @@
 #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 <util/encode/hex.h>
 
@@ -78,53 +79,81 @@ OptionDefinition::optionFactory(Option::Universe u, uint16_t type,
                                 OptionBufferConstIter begin,
                                 OptionBufferConstIter end) const {
     validate();
-    
+
     try {
-        if (type_ == OPT_BINARY_TYPE) {
+        switch(type_) {
+        case OPT_EMPTY_TYPE:
+            return (factoryEmpty(u, type));
+
+        case OPT_BINARY_TYPE:
             return (factoryGeneric(u, type, begin, end));
 
-        } else if (type_ == OPT_IPV6_ADDRESS_TYPE && array_type_) {
-            return (factoryAddrList6(type, begin, end));
+        case OPT_UINT8_TYPE:
+            return (array_type_ ? factoryGeneric(u, type, begin, end) :
+                    factoryInteger<uint8_t>(u, type, begin, end));
 
-        } else if (type_ == OPT_IPV4_ADDRESS_TYPE && array_type_) {
-            return (factoryAddrList4(type, begin, end));
+        case OPT_INT8_TYPE:
+            return (array_type_ ? factoryGeneric(u, type, begin, end) :
+                    factoryInteger<int8_t>(u, type, begin, end));
 
-        } else if (type_ == OPT_EMPTY_TYPE) {
-            return (factoryEmpty(u, type));
+        case OPT_UINT16_TYPE:
+            return (array_type_ ? factoryIntegerArray<uint16_t>(type, begin, end) :
+                    factoryInteger<uint16_t>(u, type, begin, end));
 
-        } else if (u == Option::V6 &&
-                   code_ == D6O_IA_NA &&
-                   haveIA6Format()) {
-            return (factoryIA6(type, begin, end));
+        case OPT_INT16_TYPE:
+            return (array_type_ ? factoryIntegerArray<uint16_t>(type, begin, end) :
+                    factoryInteger<int16_t>(u, type, begin, end));
 
-        } else if (u == Option::V6 &&
-                   code_ == D6O_IAADDR &&
-                   haveIAAddr6Format()) {
-            return (factoryIAAddr6(type, begin, end));
+        case OPT_UINT32_TYPE:
+            return (array_type_ ? factoryIntegerArray<uint32_t>(type, begin, end) :
+                    factoryInteger<uint32_t>(u, type, begin, end));
 
-        } else if (type_ == OPT_UINT8_TYPE) {
-            if (array_type_) {
-                return (factoryGeneric(u, type, begin, end));
-            } else {
-                return (factoryInteger<uint8_t>(u, type, begin, end));
-            }
+        case OPT_INT32_TYPE:
+            return (array_type_ ? factoryIntegerArray<uint32_t>(type, begin, end) :
+                    factoryInteger<int32_t>(u, type, begin, end));
 
-        } else if (type_ == OPT_UINT16_TYPE) {
+        case OPT_IPV4_ADDRESS_TYPE:
+            // If definition specifies that an option is an array
+            // of IPv4 addresses we return an instance of specialized
+            // class (OptionAddrLst4). For non-array types there is no
+            // specialized class yet implemented so we drop through
+            // to return an instance of OptionCustom.
             if (array_type_) {
-                return (factoryIntegerArray<uint16_t>(type, begin, end));
-            } else {
-                return (factoryInteger<uint16_t>(u, type, begin, end));
+                return (factoryAddrList4(type, begin, end));
             }
+            break;
 
-        } else if (type_ == OPT_UINT32_TYPE) {
+        case OPT_IPV6_ADDRESS_TYPE:
+            // Handle array type only here (see comments for
+            // OPT_IPV4_ADDRESS_TYPE case).
             if (array_type_) {
-                return (factoryIntegerArray<uint32_t>(type, begin, end));
-            } else {
-                return (factoryInteger<uint32_t>(u, type, begin, end));
+                return (factoryAddrList6(type, begin, end));
+            }
+            break;
+
+        default:
+            if (u == Option::V6) {
+                if ((code_ == D6O_IA_NA || code_ == D6O_IA_PD) &&
+                    haveIA6Format()) {
+                    // Return Option6IA instance for IA_PD and IA_NA option
+                    // types only. We don't want to return Option6IA for other
+                    // options that comprise 3 UINT32 data fields because
+                    // Option6IA accessors' and modifiers' names are derived
+                    // from the IA_NA and IA_PD options' field names: IAID,
+                    // T1, T2. Using functions such as getIAID, getT1 etc. for
+                    // options other than IA_NA and IA_PD would be bad practice
+                    // and cause confusion.
+                    return (factoryIA6(type, begin, end));
+
+                } else if (code_ == D6O_IAADDR && haveIAAddr6Format()) {
+                    // Rerurn Option6IAAddr option instance for the IAADDR
+                    // option only for the same reasons as described in
+                    // for IA_NA and IA_PD above.
+                    return (factoryIAAddr6(type, begin, end));
+                }
             }
-
         }
-        return (factoryGeneric(u, type, begin, end));
+        return (OptionPtr(new OptionCustom(*this, u, OptionBuffer(begin, end))));
 
     } catch (const Exception& ex) {
         isc_throw(InvalidOptionValue, ex.what());
@@ -144,7 +173,7 @@ OptionDefinition::optionFactory(Option::Universe u, uint16_t type,
 
     OptionBuffer buf;
     if (!array_type_ && type_ != OPT_RECORD_TYPE) {
-        if (values.size() == 0) {
+        if (values.empty()) {
             isc_throw(InvalidOptionValue, "no option value specified");
         }
         writeToBuffer(values[0], type_, buf);
diff --git a/src/lib/dhcp/option_definition.h b/src/lib/dhcp/option_definition.h
index 3d48ef2..ca40428 100644
--- a/src/lib/dhcp/option_definition.h
+++ b/src/lib/dhcp/option_definition.h
@@ -246,7 +246,7 @@ public:
     /// @throw MalformedOptionDefinition if option definition is invalid.
     /// @throw InvalidOptionValue if data for the option is invalid.
     OptionPtr optionFactory(Option::Universe u, uint16_t type,
-                            const OptionBuffer& buf) const;
+                            const OptionBuffer& buf = OptionBuffer()) const;
 
     /// @brief Option factory.
     ///
diff --git a/src/lib/dhcp/pkt4.cc b/src/lib/dhcp/pkt4.cc
index 5be8211..15c885c 100644
--- a/src/lib/dhcp/pkt4.cc
+++ b/src/lib/dhcp/pkt4.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2012  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -18,6 +18,7 @@
 #include <dhcp/pkt4.h>
 #include <exceptions/exceptions.h>
 
+#include <algorithm>
 #include <iostream>
 #include <sstream>
 
@@ -78,6 +79,9 @@ Pkt4::Pkt4(const uint8_t* data, size_t len)
         isc_throw(OutOfRange, "Truncated DHCPv4 packet (len=" << len
                   << ") received, at least " << DHCPV4_PKT_HDR_LEN
                   << " is expected.");
+
+    } else if (data == NULL) {
+        isc_throw(InvalidParameter, "data buffer passed to Pkt4 is NULL");
     }
 
     data_.resize(len);
@@ -158,7 +162,7 @@ Pkt4::unpack() {
         // this is *NOT* DHCP packet. It does not have any DHCPv4 options. In
         // particular, it does not have magic cookie, a 4 byte sequence that
         // differentiates between DHCP and BOOTP packets.
-        isc_throw(InvalidOperation, "Recevied BOOTP packet. BOOTP is not supported.");
+        isc_throw(InvalidOperation, "Received BOOTP packet. BOOTP is not supported.");
     }
 
     if (bufferIn.getLength() - bufferIn.getPosition() < 4) {
@@ -178,8 +182,8 @@ Pkt4::unpack() {
     bufferIn.readVector(optsBuffer, opts_len);
     LibDHCP::unpackOptions4(optsBuffer, options_);
 
-    // TODO: check will need to be called separately, so hooks can be called after
-    // packet is parsed, but before its content is verified
+    // @todo check will need to be called separately, so hooks can be called
+    // after the packet is parsed, but before its content is verified
     check();
 }
 
@@ -187,8 +191,9 @@ void Pkt4::check() {
     boost::shared_ptr<Option> typeOpt = getOption(DHO_DHCP_MESSAGE_TYPE);
     if (typeOpt) {
         uint8_t msg_type = typeOpt->getUint8();
-        if (msg_type>DHCPLEASEACTIVE) {
-            isc_throw(BadValue, "Invalid DHCP message type received:" << msg_type);
+        if (msg_type > DHCPLEASEACTIVE) {
+            isc_throw(BadValue, "Invalid DHCP message type received: "
+                      << msg_type);
         }
         msg_type_ = msg_type;
 
@@ -221,21 +226,21 @@ Pkt4::toText() {
 
 void
 Pkt4::setHWAddr(uint8_t hType, uint8_t hlen,
-                const std::vector<uint8_t>& macAddr) {
-    /// TODO Rewrite this once support for client-identifier option
+                const std::vector<uint8_t>& mac_addr) {
+    /// @todo Rewrite this once support for client-identifier option
     /// is implemented (ticket 1228?)
-    if (hlen>MAX_CHADDR_LEN) {
+    if (hlen > MAX_CHADDR_LEN) {
         isc_throw(OutOfRange, "Hardware address (len=" << hlen
                   << " too long. Max " << MAX_CHADDR_LEN << " supported.");
-    }
-    if ( (macAddr.size() == 0) && (hlen > 0) ) {
+
+    } else if (mac_addr.empty() && (hlen > 0) ) {
         isc_throw(OutOfRange, "Invalid HW Address specified");
     }
 
     htype_ = hType;
     hlen_ = hlen;
-    memset(chaddr_, 0, MAX_CHADDR_LEN);
-    memcpy(chaddr_, &macAddr[0], hlen);
+    std::copy(&mac_addr[0], &mac_addr[hlen], &chaddr_[0]);
+    std::fill(&chaddr_[hlen], &chaddr_[MAX_CHADDR_LEN], 0);
 }
 
 void
@@ -243,11 +248,15 @@ Pkt4::setSname(const uint8_t* sname, size_t snameLen /*= MAX_SNAME_LEN*/) {
     if (snameLen > MAX_SNAME_LEN) {
         isc_throw(OutOfRange, "sname field (len=" << snameLen
                   << ") too long, Max " << MAX_SNAME_LEN << " supported.");
+
+    } else if (sname == NULL) {
+        isc_throw(InvalidParameter, "Invalid sname specified");
     }
-    memset(sname_, 0, MAX_SNAME_LEN);
-    memcpy(sname_, sname, snameLen);
 
-    // no need to store snameLen as any empty space is filled with 0s
+    std::copy(&sname[0], &sname[snameLen], &sname_[0]);
+    std::fill(&sname_[snameLen], &sname_[MAX_SNAME_LEN], 0);
+
+    // No need to store snameLen as any empty space is filled with 0s
 }
 
 void
@@ -255,11 +264,15 @@ Pkt4::setFile(const uint8_t* file, size_t fileLen /*= MAX_FILE_LEN*/) {
     if (fileLen > MAX_FILE_LEN) {
         isc_throw(OutOfRange, "file field (len=" << fileLen
                   << ") too long, Max " << MAX_FILE_LEN << " supported.");
+
+    } else if (file == NULL) {
+        isc_throw(InvalidParameter, "Invalid file name specified");
     }
-    memset(file_, 0, MAX_FILE_LEN);
-    memcpy(file_, file, fileLen);
 
-    // no need to store fileLen as any empty space is filled with 0s
+    std::copy(&file[0], &file[fileLen], &file_[0]);
+    std::fill(&file_[fileLen], &file_[MAX_FILE_LEN], 0);
+
+    // No need to store fileLen as any empty space is filled with 0s
 }
 
 uint8_t
@@ -272,6 +285,7 @@ Pkt4::DHCPTypeToBootpType(uint8_t dhcpType) {
     case DHCPINFORM:
     case DHCPLEASEQUERY:
         return (BOOTREQUEST);
+
     case DHCPACK:
     case DHCPNAK:
     case DHCPOFFER:
@@ -279,6 +293,7 @@ Pkt4::DHCPTypeToBootpType(uint8_t dhcpType) {
     case DHCPLEASEUNKNOWN:
     case DHCPLEASEACTIVE:
         return (BOOTREPLY);
+
     default:
         isc_throw(OutOfRange, "Invalid message type: "
                   << static_cast<int>(dhcpType) );
@@ -287,7 +302,7 @@ Pkt4::DHCPTypeToBootpType(uint8_t dhcpType) {
 
 void
 Pkt4::addOption(boost::shared_ptr<Option> opt) {
-    // check for uniqueness (DHCPv4 options must be unique)
+    // Check for uniqueness (DHCPv4 options must be unique)
     if (getOption(opt->getType())) {
         isc_throw(BadValue, "Option " << opt->getType()
                   << " already present in this message.");
@@ -298,7 +313,7 @@ Pkt4::addOption(boost::shared_ptr<Option> opt) {
 boost::shared_ptr<isc::dhcp::Option>
 Pkt4::getOption(uint8_t type) {
     Option::OptionCollection::const_iterator x = options_.find(type);
-    if (x!=options_.end()) {
+    if (x != options_.end()) {
         return (*x).second;
     }
     return boost::shared_ptr<isc::dhcp::Option>(); // NULL
diff --git a/src/lib/dhcp/pkt4.h b/src/lib/dhcp/pkt4.h
index e09069c..5139458 100644
--- a/src/lib/dhcp/pkt4.h
+++ b/src/lib/dhcp/pkt4.h
@@ -89,7 +89,7 @@ public:
     /// reasonable value. This method is expected to grow significantly.
     /// It makes sense to separate unpack() and check() for testing purposes.
     ///
-    /// TODO: It is called from unpack() directly. It should be separated.
+    /// @todo It is called from unpack() directly. It should be separated.
     ///
     /// Method will throw exception if anomaly is found.
     void check();
@@ -262,16 +262,16 @@ public:
     /// @brief Sets hardware address.
     ///
     /// Sets parameters of hardware address. hlen specifies
-    /// length of macAddr buffer. Content of macAddr buffer
+    /// length of mac_addr buffer. Content of mac_addr buffer
     /// will be copied to appropriate field.
     ///
-    /// Note: macAddr must be a buffer of at least hlen bytes.
+    /// Note: mac_addr must be a buffer of at least hlen bytes.
     ///
     /// @param hType hardware type (will be sent in htype field)
     /// @param hlen hardware length (will be sent in hlen field)
-    /// @param macAddr pointer to hardware address
+    /// @param mac_addr pointer to hardware address
     void setHWAddr(uint8_t hType, uint8_t hlen,
-                   const std::vector<uint8_t>& macAddr);
+                   const std::vector<uint8_t>& mac_addr);
 
     /// Returns htype field
     ///
@@ -367,7 +367,7 @@ public:
     /// @brief Returns remote address
     ///
     /// @return remote address
-    const isc::asiolink::IOAddress& getRemoteAddr() {
+    const isc::asiolink::IOAddress& getRemoteAddr() const {
         return (remote_addr_);
     }
 
@@ -381,7 +381,7 @@ public:
     /// @brief Returns local address.
     ///
     /// @return local address
-    const isc::asiolink::IOAddress& getLocalAddr() {
+    const isc::asiolink::IOAddress& getLocalAddr() const {
         return (local_addr_);
     }
 
@@ -393,7 +393,7 @@ public:
     /// @brief Returns local port.
     ///
     /// @return local port
-    uint16_t getLocalPort() { return (local_port_); }
+    uint16_t getLocalPort() const { return (local_port_); }
 
     /// @brief Sets remote port.
     ///
@@ -403,7 +403,7 @@ public:
     /// @brief Returns remote port.
     ///
     /// @return remote port
-    uint16_t getRemotePort() { return (remote_port_); }
+    uint16_t getRemotePort() const { return (remote_port_); }
 
     /// @brief Update packet timestamp.
     ///
@@ -519,13 +519,13 @@ protected:
     std::vector<uint8_t> data_;
 
     /// message type (e.g. 1=DHCPDISCOVER)
-    /// TODO: this will eventually be replaced with DHCP Message Type
+    /// @todo this will eventually be replaced with DHCP Message Type
     /// option (option 53)
     uint8_t msg_type_;
 
     /// collection of options present in this message
     ///
-    /// @warnig This protected member is accessed by derived
+    /// @warning This protected member is accessed by derived
     /// classes directly. One of such derived classes is
     /// @ref perfdhcp::PerfPkt4. The impact on derived clasess'
     /// behavior must be taken into consideration before making
diff --git a/src/lib/dhcp/pkt6.cc b/src/lib/dhcp/pkt6.cc
index 12597c3..2c97b07 100644
--- a/src/lib/dhcp/pkt6.cc
+++ b/src/lib/dhcp/pkt6.cc
@@ -81,13 +81,6 @@ Pkt6::pack() {
 
 bool
 Pkt6::packUDP() {
-
-    // TODO: Once OutputBuffer is used here, some thing like this
-    // will be used. Yikes! That's ugly.
-    // bufferOut_.writeData(ciaddr_.getAddress().to_v6().to_bytes().data(), 16);
-    // It is better to implement a method in IOAddress that extracts
-    // vector<uint8_t>
-
     try {
         // DHCPv6 header: message-type (1 octect) + transaction id (3 octets)
         bufferOut_.writeUint8(msg_type_);
@@ -172,17 +165,30 @@ Pkt6::toText() {
     return tmp.str();
 }
 
-boost::shared_ptr<isc::dhcp::Option>
+OptionPtr
 Pkt6::getOption(uint16_t opt_type) {
     isc::dhcp::Option::OptionCollection::const_iterator x = options_.find(opt_type);
     if (x!=options_.end()) {
         return (*x).second;
     }
-    return boost::shared_ptr<isc::dhcp::Option>(); // NULL
+    return OptionPtr(); // NULL
+}
+
+isc::dhcp::Option::OptionCollection
+Pkt6::getOptions(uint16_t opt_type) {
+    isc::dhcp::Option::OptionCollection found;
+
+    for (Option::OptionCollection::const_iterator x = options_.begin();
+         x != options_.end(); ++x) {
+        if (x->first == opt_type) {
+            found.insert(make_pair(opt_type, x->second));
+        }
+    }
+    return (found);
 }
 
 void
-Pkt6::addOption(boost::shared_ptr<Option> opt) {
+Pkt6::addOption(const OptionPtr& opt) {
     options_.insert(pair<int, boost::shared_ptr<Option> >(opt->getType(), opt));
 }
 
@@ -205,6 +211,52 @@ Pkt6::updateTimestamp() {
     timestamp_ = boost::posix_time::microsec_clock::universal_time();
 }
 
+const char*
+Pkt6::getName(uint8_t type) {
+    static const char* CONFIRM = "CONFIRM";
+    static const char* DECLINE = "DECLINE";
+    static const char* INFORMATION_REQUEST = "INFORMATION_REQUEST";
+    static const char* REBIND = "REBIND";
+    static const char* RELEASE = "RELEASE";
+    static const char* RENEW = "RENEW";
+    static const char* REQUEST = "REQUEST";
+    static const char* SOLICIT = "SOLICIT";
+    static const char* UNKNOWN = "UNKNOWN";
+
+    switch (type) {
+    case DHCPV6_CONFIRM:
+        return (CONFIRM);
+
+    case DHCPV6_DECLINE:
+        return (DECLINE);
+
+    case DHCPV6_INFORMATION_REQUEST:
+        return (INFORMATION_REQUEST);
+
+    case DHCPV6_REBIND:
+        return (REBIND);
+
+    case DHCPV6_RELEASE:
+        return (RELEASE);
+
+    case DHCPV6_RENEW:
+        return (RENEW);
+
+    case DHCPV6_REQUEST:
+        return (REQUEST);
+
+    case DHCPV6_SOLICIT:
+        return (SOLICIT);
+
+    default:
+        ;
+    }
+    return (UNKNOWN);
+}
+
+const char* Pkt6::getName() const {
+    return (getName(getType()));
+}
 
 } // end of isc::dhcp namespace
 } // end of isc namespace
diff --git a/src/lib/dhcp/pkt6.h b/src/lib/dhcp/pkt6.h
index 5782737..6ffea2b 100644
--- a/src/lib/dhcp/pkt6.h
+++ b/src/lib/dhcp/pkt6.h
@@ -127,7 +127,7 @@ public:
     /// Returns message type (e.g. 1 = SOLICIT)
     ///
     /// @return message type
-    uint8_t getType() { return (msg_type_); }
+    uint8_t getType() const { return (msg_type_); }
 
     /// Sets message type (e.g. 1 = SOLICIT)
     ///
@@ -147,19 +147,28 @@ public:
     /// Adds an option to this packet.
     ///
     /// @param opt option to be added.
-    void addOption(OptionPtr opt);
+    void addOption(const OptionPtr& opt);
 
     /// @brief Returns the first option of specified type.
     ///
     /// Returns the first option of specified type. Note that in DHCPv6 several
     /// instances of the same option are allowed (and frequently used).
-    /// See getOptions().
+    /// Also see \ref getOptions().
     ///
     /// @param type option type we are looking for
     ///
     /// @return pointer to found option (or NULL)
     OptionPtr getOption(uint16_t type);
 
+    /// @brief Returns all instances of specified type.
+    ///
+    /// Returns all instances of options of the specified type. DHCPv6 protocol
+    /// allows (and uses frequently) multiple instances.
+    ///
+    /// @param type option type we are looking for
+    /// @return instance of option collection with requested options
+    isc::dhcp::Option::OptionCollection getOptions(uint16_t type);
+
     /// Attempts to delete first suboption of requested type
     ///
     /// @param type Type of option to be deleted.
@@ -180,7 +189,9 @@ public:
     /// @brief Returns remote address
     ///
     /// @return remote address
-    const isc::asiolink::IOAddress& getRemoteAddr() { return (remote_addr_); }
+    const isc::asiolink::IOAddress& getRemoteAddr() const {
+        return (remote_addr_);
+    }
 
     /// @brief Sets local address.
     ///
@@ -190,7 +201,9 @@ public:
     /// @brief Returns local address.
     ///
     /// @return local address
-    const isc::asiolink::IOAddress& getLocalAddr() { return (local_addr_); }
+    const isc::asiolink::IOAddress& getLocalAddr() const {
+        return (local_addr_);
+    }
 
     /// @brief Sets local port.
     ///
@@ -200,7 +213,7 @@ public:
     /// @brief Returns local port.
     ///
     /// @return local port
-    uint16_t getLocalPort() { return (local_port_); }
+    uint16_t getLocalPort() const { return (local_port_); }
 
     /// @brief Sets remote port.
     ///
@@ -210,7 +223,7 @@ public:
     /// @brief Returns remote port.
     ///
     /// @return remote port
-    uint16_t getRemotePort() { return (remote_port_); }
+    uint16_t getRemotePort() const { return (remote_port_); }
 
     /// @brief Sets interface index.
     ///
@@ -246,8 +259,6 @@ public:
     /// @return interface name
     void setIface(const std::string& iface ) { iface_ = iface; };
 
-    /// TODO Need to implement getOptions() as well
-
     /// collection of options present in this message
     ///
     /// @warning This protected member is accessed by derived
@@ -266,6 +277,34 @@ public:
     /// @throw isc::Unexpected if timestamp update failed
     void updateTimestamp();
 
+    /// @brief Return textual type of packet.
+    ///
+    /// Returns the name of valid packet received by the server (e.g. SOLICIT).
+    /// If the packet is unknown - or if it is a valid DHCP packet but not one
+    /// expected to be received by the server (such as an ADVERTISE), the string
+    /// "UNKNOWN" is returned.  This method is used in debug messages.
+    ///
+    /// As the operation of the method does not depend on any server state, it
+    /// is declared static. There is also non-static getName() method that
+    /// works on Pkt6 objects.
+    ///
+    /// @param type DHCPv6 packet type
+    ///
+    /// @return Pointer to "const" string containing the packet name.
+    ///         Note that this string is statically allocated and MUST NOT
+    ///         be freed by the caller.
+    static const char* getName(uint8_t type);
+
+    /// @brief returns textual representation of packet type.
+    ///
+    /// This method requires an object. There is also static version, which
+    /// requires one parameter (type).
+    ///
+    /// @return Pointer to "const" string containing packet name.
+    ///         Note that this string is statically allocated and MUST NOT
+    ///         be freed by the caller.
+    const char* getName() const;
+
 protected:
     /// Builds on wire packet for TCP transmission.
     ///
diff --git a/src/lib/dhcp/std_option_defs.h b/src/lib/dhcp/std_option_defs.h
new file mode 100644
index 0000000..d66dcf8
--- /dev/null
+++ b/src/lib/dhcp/std_option_defs.h
@@ -0,0 +1,184 @@
+// 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 STD_OPTION_DEFS_H
+#define STD_OPTION_DEFS_H
+
+#include <dhcp/option_data_types.h>
+
+namespace {
+
+/// @brief Declare an array holding parameters used to create instance
+/// of a definition for option comprising a record of data fields.
+///
+/// @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 }
+#endif
+
+/// @brief A pair of values: one pointing to the array holding types of
+/// data fields belonging to the record, and size of this array.
+///
+/// @param name name of the array holding data fields' types.
+#ifndef RECORD_DEF
+#define RECORD_DEF(name) name, sizeof(name) / sizeof(name[0])
+#endif
+
+#ifndef NO_RECORD_DEF
+#define NO_RECORD_DEF 0, 0
+#endif
+
+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
+};
+
+// client-fqdn
+RECORD_DECL(clientFqdnRecords, OPT_UINT8_TYPE, OPT_FQDN_TYPE);
+// geoconf-civic
+RECORD_DECL(geoconfCivicRecords, OPT_UINT8_TYPE, OPT_UINT16_TYPE,
+            OPT_BINARY_TYPE);
+// iaddr
+RECORD_DECL(iaaddrRecords, OPT_IPV6_ADDRESS_TYPE, OPT_UINT32_TYPE,
+            OPT_UINT32_TYPE);
+// ia-na
+RECORD_DECL(ianaRecords, OPT_UINT32_TYPE, OPT_UINT32_TYPE, OPT_UINT32_TYPE);
+// ia-pd
+RECORD_DECL(iapdRecords, OPT_UINT32_TYPE, OPT_UINT32_TYPE, OPT_UINT32_TYPE);
+// ia-prefix
+RECORD_DECL(iaPrefixRecords, OPT_UINT32_TYPE, OPT_UINT32_TYPE,
+            OPT_UINT8_TYPE, OPT_BINARY_TYPE);
+// lq-query
+RECORD_DECL(lqQueryRecords, OPT_UINT8_TYPE, OPT_IPV6_ADDRESS_TYPE);
+// lq-relay-data
+RECORD_DECL(lqRelayData, OPT_IPV6_ADDRESS_TYPE, OPT_BINARY_TYPE);
+// remote-id
+RECORD_DECL(remoteIdRecords, OPT_UINT32_TYPE, OPT_BINARY_TYPE);
+// status-code
+RECORD_DECL(statusCodeRecords, OPT_UINT16_TYPE, OPT_STRING_TYPE);
+// vendor-class
+RECORD_DECL(vendorClassRecords, OPT_UINT32_TYPE, OPT_BINARY_TYPE);
+// vendor-opts
+RECORD_DECL(vendorOptsRecords, OPT_UINT32_TYPE, OPT_BINARY_TYPE);
+
+/// Standard DHCPv6 option definitions.
+///
+/// @warning in this array, the initializers are provided for all
+/// OptionDefParams struct's members despite initializers for
+/// 'records' and 'record_size' could be ommited for entries for
+/// which 'type' does not equal to OPT_RECORD_TYPE. If initializers
+/// are ommitted the corresponding values should default to 0.
+/// 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[] = {
+    { "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-ta", D6O_IA_TA, OPT_UINT32_TYPE, false, NO_RECORD_DEF },
+    { "iaaddr", D6O_IAADDR, OPT_RECORD_TYPE, false, RECORD_DEF(iaaddrRecords) },
+    { "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 },
+    { "relay-msg", D6O_RELAY_MSG, OPT_BINARY_TYPE, false, NO_RECORD_DEF },
+    // Unfortunatelly the AUTH 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.
+    { "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) },
+    { "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) },
+    { "vendor-opts", D6O_VENDOR_OPTS, OPT_RECORD_TYPE, false,
+      RECORD_DEF(vendorOptsRecords) },
+    { "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,
+      NO_RECORD_DEF },
+    { "sip-server-dns", D6O_SIP_SERVERS_DNS, OPT_FQDN_TYPE, true,
+      NO_RECORD_DEF },
+    { "sip-server-addr", D6O_SIP_SERVERS_ADDR, OPT_IPV6_ADDRESS_TYPE, true,
+      NO_RECORD_DEF },
+    { "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) },
+    { "iaprefix", D6O_IAPREFIX, OPT_RECORD_TYPE, false,
+      RECORD_DEF(iaPrefixRecords) },
+    { "nis-servers", D6O_NIS_SERVERS, OPT_IPV6_ADDRESS_TYPE, true,
+      NO_RECORD_DEF },
+    { "nisp-servers", D6O_NISP_SERVERS, OPT_IPV6_ADDRESS_TYPE, true,
+      NO_RECORD_DEF },
+    { "nis-domain-name", D6O_NIS_DOMAIN_NAME, OPT_FQDN_TYPE, true,
+      NO_RECORD_DEF },
+    { "nisp-domain-name", D6O_NISP_DOMAIN_NAME, OPT_FQDN_TYPE, true,
+      NO_RECORD_DEF },
+    { "sntp-servers", D6O_SNTP_SERVERS, OPT_IPV6_ADDRESS_TYPE, true,
+      NO_RECORD_DEF },
+    { "information-refresh-time", D6O_INFORMATION_REFRESH_TIME,
+      OPT_UINT32_TYPE, false, NO_RECORD_DEF },
+    { "bcmcs-server-dns", D6O_BCMCS_SERVER_D, OPT_FQDN_TYPE, true,
+      NO_RECORD_DEF },
+    { "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) },
+    { "remote-id", D6O_REMOTE_ID, OPT_RECORD_TYPE, false,
+      RECORD_DEF(remoteIdRecords) },
+    { "subscriber-id", D6O_SUBSCRIBER_ID, OPT_BINARY_TYPE, false,
+      NO_RECORD_DEF },
+    { "client-fqdn", D6O_CLIENT_FQDN, OPT_RECORD_TYPE, false,
+      RECORD_DEF(clientFqdnRecords) },
+    { "pana-agent", D6O_PANA_AGENT, OPT_IPV6_ADDRESS_TYPE, true,
+      NO_RECORD_DEF },
+    { "new-posix-timezone", D6O_NEW_POSIX_TIMEZONE, OPT_STRING_TYPE, false,
+      NO_RECORD_DEF },
+    { "new-tzdb-timezone", D6O_NEW_TZDB_TIMEZONE, OPT_STRING_TYPE, false,
+      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) },
+    { "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) },
+    { "lq-client-link", D6O_LQ_CLIENT_LINK, OPT_IPV6_ADDRESS_TYPE, true,
+      NO_RECORD_DEF }
+
+    // @todo There is still a bunch of options for which we have to provide
+    // definitions but we don't do it because they are not really
+    // critical right now.
+};
+
+/// Number of option definitions defined.
+const int OPTION_DEF_PARAMS_SIZE6  =
+    sizeof(OPTION_DEF_PARAMS6) / sizeof(OPTION_DEF_PARAMS6[0]);
+
+}; // anonymous namespace
+
+#endif // STD_OPTION_DEFS_H
diff --git a/src/lib/dhcp/tests/Makefile.am b/src/lib/dhcp/tests/Makefile.am
index e66d700..5799d58 100644
--- a/src/lib/dhcp/tests/Makefile.am
+++ b/src/lib/dhcp/tests/Makefile.am
@@ -35,6 +35,7 @@ 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_data_types_unittest.cc
 libdhcp___unittests_SOURCES += option_definition_unittest.cc
 libdhcp___unittests_SOURCES += option_custom_unittest.cc
 libdhcp___unittests_SOURCES += option_unittest.cc
diff --git a/src/lib/dhcp/tests/libdhcp++_unittest.cc b/src/lib/dhcp/tests/libdhcp++_unittest.cc
index af3d6ca..8ead521 100644
--- a/src/lib/dhcp/tests/libdhcp++_unittest.cc
+++ b/src/lib/dhcp/tests/libdhcp++_unittest.cc
@@ -22,6 +22,7 @@
 #include <dhcp/option6_iaaddr.h>
 #include <dhcp/option6_int.h>
 #include <dhcp/option6_int_array.h>
+#include <dhcp/option_custom.h>
 #include <util/buffer.h>
 
 #include <gtest/gtest.h>
@@ -68,8 +69,8 @@ public:
                              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 deinitions here because they
-        // are initialized in the class'es constructor.
+        // We don't have to initialize option definitions here because they
+        // are initialized in the class's constructor.
         OptionDefContainer options = LibDHCP::getOptionDefs(Option::V6);
         // Get the container index #1. This one allows for searching
         // option definitions using option code.
@@ -90,13 +91,15 @@ public:
         ASSERT_NO_THROW(def->validate());
         OptionPtr option;
         // Create the option.
-        ASSERT_NO_THROW(option = def->optionFactory(Option::V6, code, buf));
+        ASSERT_NO_THROW(option = def->optionFactory(Option::V6, code, buf))
+            << "Option creation failed to option code " << code;
         // Make sure it is not NULL.
         ASSERT_TRUE(option);
         // And the actual object type is the one that we expect.
         // Note that for many options there are dedicated classes
         // derived from Option class to represent them.
-        EXPECT_TRUE(typeid(*option) == expected_type);
+        EXPECT_TRUE(typeid(*option) == expected_type)
+            << "Invalid class returned for option code " << code;
     }
 };
 
@@ -399,24 +402,149 @@ TEST_F(LibDhcpTest, unpackOptions4) {
 // This test have to be extended once all option definitions are
 // created.
 TEST_F(LibDhcpTest, stdOptionDefs6) {
-    LibDhcpTest::testStdOptionDefs6(D6O_CLIENTID, OptionBuffer(14, 1),
-                                     typeid(Option));
-    LibDhcpTest::testStdOptionDefs6(D6O_SERVERID, OptionBuffer(14, 1),
-                                     typeid(Option));
-    LibDhcpTest::testStdOptionDefs6(D6O_IA_NA, OptionBuffer(12, 1),
-                                     typeid(Option6IA));
-    LibDhcpTest::testStdOptionDefs6(D6O_IAADDR, OptionBuffer(24, 1),
-                                     typeid(Option6IAAddr));
-    LibDhcpTest::testStdOptionDefs6(D6O_ORO, OptionBuffer(10, 1),
-                                     typeid(Option6IntArray<uint16_t>));
-    LibDhcpTest::testStdOptionDefs6(D6O_ELAPSED_TIME, OptionBuffer(2, 1),
-                                     typeid(Option6Int<uint16_t>));
-    LibDhcpTest::testStdOptionDefs6(D6O_STATUS_CODE, OptionBuffer(10, 1),
-                                     typeid(Option));
-    LibDhcpTest::testStdOptionDefs6(D6O_RAPID_COMMIT, OptionBuffer(),
-                                     typeid(Option));
-    LibDhcpTest::testStdOptionDefs6(D6O_NAME_SERVERS, OptionBuffer(32, 1),
-                                     typeid(Option6AddrLst));
+
+    // 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);
+
+    // Prepare buffer holding an array of FQDNs.
+    const char data[] = {
+        8, 109, 121, 100, 111, 109, 97, 105, 110, // "mydomain"
+        7, 101, 120, 97, 109, 112, 108, 101,      // "example"
+        3, 99, 111, 109,                          // "com"
+        0,
+        7, 101, 120, 97, 109, 112, 108, 101,      // "example"
+        3, 99, 111, 109,                          // "com"
+        0
+    };
+    // Initialize a vector with the FQDN data.
+    std::vector<uint8_t> fqdn_buf(data, data + sizeof(data));
+
+    // The CLIENT_FQDN holds a uint8_t value and FQDN. We have
+    // to add the uint8_t value to it and then append the buffer
+    // holding some valid FQDN.
+    std::vector<uint8_t> client_fqdn_buf(1);
+    client_fqdn_buf.insert(client_fqdn_buf.end(), fqdn_buf.begin(),
+                           fqdn_buf.end());
+
+    // The actual test starts here for all supported option codes.
+    LibDhcpTest::testStdOptionDefs6(D6O_CLIENTID, buf, typeid(Option));
+
+    LibDhcpTest::testStdOptionDefs6(D6O_SERVERID, buf, typeid(Option));
+
+    LibDhcpTest::testStdOptionDefs6(D6O_IA_NA, buf, typeid(Option6IA));
+
+    LibDhcpTest::testStdOptionDefs6(D6O_IA_TA, buf,
+                                    typeid(Option6Int<uint32_t>));
+
+    LibDhcpTest::testStdOptionDefs6(D6O_IAADDR, buf, typeid(Option6IAAddr));
+
+    LibDhcpTest::testStdOptionDefs6(D6O_ORO, buf,
+                                    typeid(Option6IntArray<uint16_t>));
+
+    LibDhcpTest::testStdOptionDefs6(D6O_PREFERENCE, buf,
+                                    typeid(Option6Int<uint8_t>));
+
+    LibDhcpTest::testStdOptionDefs6(D6O_ELAPSED_TIME, buf,
+                                    typeid(Option6Int<uint16_t>));
+
+    LibDhcpTest::testStdOptionDefs6(D6O_RELAY_MSG, buf, typeid(Option));
+
+    LibDhcpTest::testStdOptionDefs6(D6O_STATUS_CODE, buf, typeid(OptionCustom));
+
+    LibDhcpTest::testStdOptionDefs6(D6O_RAPID_COMMIT, buf, typeid(Option));
+
+    LibDhcpTest::testStdOptionDefs6(D6O_USER_CLASS, buf, typeid(Option));
+
+    LibDhcpTest::testStdOptionDefs6(D6O_VENDOR_CLASS, buf,
+                                    typeid(OptionCustom));
+
+    LibDhcpTest::testStdOptionDefs6(D6O_VENDOR_OPTS, buf, typeid(OptionCustom));
+
+    LibDhcpTest::testStdOptionDefs6(D6O_INTERFACE_ID, buf, typeid(Option));
+
+    LibDhcpTest::testStdOptionDefs6(D6O_RECONF_MSG, buf,
+                                    typeid(Option6Int<uint8_t>));
+
+    LibDhcpTest::testStdOptionDefs6(D6O_RECONF_ACCEPT, buf, typeid(Option));
+
+    LibDhcpTest::testStdOptionDefs6(D6O_SIP_SERVERS_DNS, fqdn_buf,
+                                    typeid(OptionCustom));
+
+    LibDhcpTest::testStdOptionDefs6(D6O_SIP_SERVERS_ADDR, buf,
+                                    typeid(Option6AddrLst));
+
+    LibDhcpTest::testStdOptionDefs6(D6O_NAME_SERVERS, buf,
+                                    typeid(Option6AddrLst));
+
+    LibDhcpTest::testStdOptionDefs6(D6O_DOMAIN_SEARCH, fqdn_buf,
+                                    typeid(OptionCustom));
+
+    LibDhcpTest::testStdOptionDefs6(D6O_IA_PD, buf, typeid(Option6IA));
+
+    LibDhcpTest::testStdOptionDefs6(D6O_IAPREFIX, buf, typeid(OptionCustom));
+
+    LibDhcpTest::testStdOptionDefs6(D6O_NIS_SERVERS, buf,
+                                    typeid(Option6AddrLst));
+
+    LibDhcpTest::testStdOptionDefs6(D6O_NISP_SERVERS, buf,
+                                    typeid(Option6AddrLst));
+
+    LibDhcpTest::testStdOptionDefs6(D6O_NIS_DOMAIN_NAME, fqdn_buf,
+                                    typeid(OptionCustom));
+
+    LibDhcpTest::testStdOptionDefs6(D6O_NISP_DOMAIN_NAME, fqdn_buf,
+                                    typeid(OptionCustom));
+
+    LibDhcpTest::testStdOptionDefs6(D6O_SNTP_SERVERS, buf,
+                                    typeid(Option6AddrLst));
+
+    LibDhcpTest::testStdOptionDefs6(D6O_INFORMATION_REFRESH_TIME,
+                                    buf, typeid(Option6Int<uint32_t>));
+
+    LibDhcpTest::testStdOptionDefs6(D6O_BCMCS_SERVER_D, fqdn_buf,
+                                    typeid(OptionCustom));
+
+    LibDhcpTest::testStdOptionDefs6(D6O_BCMCS_SERVER_A, buf,
+                                    typeid(Option6AddrLst));
+
+    LibDhcpTest::testStdOptionDefs6(D6O_GEOCONF_CIVIC, buf,
+                                    typeid(OptionCustom));
+
+    LibDhcpTest::testStdOptionDefs6(D6O_REMOTE_ID, buf, typeid(OptionCustom));
+
+    LibDhcpTest::testStdOptionDefs6(D6O_SUBSCRIBER_ID, buf,typeid(Option));
+
+    LibDhcpTest::testStdOptionDefs6(D6O_CLIENT_FQDN, client_fqdn_buf,
+                                    typeid(OptionCustom));
+
+    LibDhcpTest::testStdOptionDefs6(D6O_PANA_AGENT, buf,
+                                    typeid(Option6AddrLst));
+
+    LibDhcpTest::testStdOptionDefs6(D6O_PANA_AGENT, buf,
+                                    typeid(Option6AddrLst));
+
+    LibDhcpTest::testStdOptionDefs6(D6O_NEW_POSIX_TIMEZONE, buf,
+                                    typeid(OptionCustom));
+
+    LibDhcpTest::testStdOptionDefs6(D6O_NEW_TZDB_TIMEZONE, buf,
+                                    typeid(OptionCustom));
+
+    LibDhcpTest::testStdOptionDefs6(D6O_ERO, buf,
+                                    typeid(Option6IntArray<uint16_t>));
+
+    LibDhcpTest::testStdOptionDefs6(D6O_LQ_QUERY, buf, typeid(OptionCustom));
+
+    LibDhcpTest::testStdOptionDefs6(D6O_CLIENT_DATA, buf, typeid(Option));
+
+    LibDhcpTest::testStdOptionDefs6(D6O_CLT_TIME, buf,
+                                    typeid(Option6Int<uint32_t>));
+
+    LibDhcpTest::testStdOptionDefs6(D6O_LQ_RELAY_DATA, buf,
+                                    typeid(OptionCustom));
+
+    LibDhcpTest::testStdOptionDefs6(D6O_LQ_CLIENT_LINK, buf,
+                                    typeid(Option6AddrLst));
 }
 
 }
diff --git a/src/lib/dhcp/tests/option6_ia_unittest.cc b/src/lib/dhcp/tests/option6_ia_unittest.cc
index 1ee618a..24f101d 100644
--- a/src/lib/dhcp/tests/option6_ia_unittest.cc
+++ b/src/lib/dhcp/tests/option6_ia_unittest.cc
@@ -109,7 +109,13 @@ TEST_F(Option6IATest, basic) {
 }
 
 TEST_F(Option6IATest, simple) {
-    Option6IA * ia = new Option6IA(D6O_IA_NA, 1234);
+    Option6IA* ia = new Option6IA(D6O_IA_NA, 1234);
+
+    // Check that the values are really different than what we are about
+    // to set them to.
+    EXPECT_NE(2345, ia->getT1());
+    EXPECT_NE(3456, ia->getT2());
+
     ia->setT1(2345);
     ia->setT2(3456);
 
@@ -119,6 +125,9 @@ TEST_F(Option6IATest, simple) {
     EXPECT_EQ(2345, ia->getT1());
     EXPECT_EQ(3456, ia->getT2());
 
+    ia->setIAID(890);
+    EXPECT_EQ(890, ia->getIAID());
+
     EXPECT_NO_THROW(
         delete ia;
     );
diff --git a/src/lib/dhcp/tests/option_custom_unittest.cc b/src/lib/dhcp/tests/option_custom_unittest.cc
index 34969ee..044279b 100644
--- a/src/lib/dhcp/tests/option_custom_unittest.cc
+++ b/src/lib/dhcp/tests/option_custom_unittest.cc
@@ -106,6 +106,17 @@ TEST_F(OptionCustomTest, constructor) {
 
     EXPECT_EQ(Option::V4, option->getUniverse());
     EXPECT_EQ(232, option->getType());
+
+    // Try to create an option using 'empty data' constructor
+    OptionDefinition opt_def3("OPTION_FOO", 1000, "uint32");
+
+    ASSERT_NO_THROW(
+        option.reset(new OptionCustom(opt_def3, Option::V6));
+    );
+    ASSERT_TRUE(option);
+
+    EXPECT_EQ(Option::V6, option->getUniverse());
+    EXPECT_EQ(1000, option->getType());
 }
 
 // The purpose of this test is to verify that 'empty' option definition can
@@ -113,9 +124,10 @@ TEST_F(OptionCustomTest, constructor) {
 TEST_F(OptionCustomTest, emptyData) {
     OptionDefinition opt_def("OPTION_FOO", 232, "empty");
 
+    OptionBuffer buf;
     boost::scoped_ptr<OptionCustom> option;
     ASSERT_NO_THROW(
-        option.reset(new OptionCustom(opt_def, Option::V4, OptionBuffer()));
+        option.reset(new OptionCustom(opt_def, Option::V4, buf.begin(), buf.end()));
     );
     ASSERT_TRUE(option);
 
@@ -159,8 +171,10 @@ TEST_F(OptionCustomTest, binaryData) {
     EXPECT_TRUE(std::equal(buf_in.begin(), buf_in.end(), buf_out.begin()));
 
     // Check that option with "no data" is rejected.
+    buf_in.clear();
     EXPECT_THROW(
-        option.reset(new OptionCustom(opt_def, Option::V4, OptionBuffer())),
+        option.reset(new OptionCustom(opt_def, Option::V4, buf_in.begin(),
+                                      buf_in.end())),
         isc::OutOfRange
     );
 }
@@ -197,12 +211,46 @@ TEST_F(OptionCustomTest, booleanData) {
     EXPECT_FALSE(value);
 
     // Check that the option with "no data" is rejected.
+    buf.clear();
     EXPECT_THROW(
-        option.reset(new OptionCustom(opt_def, Option::V6, OptionBuffer())),
+        option.reset(new OptionCustom(opt_def, Option::V6, buf.begin(), buf.end())),
         isc::OutOfRange
     );
 }
 
+// The purpose of this test is to verify that the data from a buffer
+// can be read as FQDN.
+TEST_F(OptionCustomTest, fqdnData) {
+    OptionDefinition opt_def("OPTION_FOO", 1000, "fqdn");
+
+    const char data[] = {
+        8, 109, 121, 100, 111, 109, 97, 105, 110, // "mydomain"
+        7, 101, 120, 97, 109, 112, 108, 101,      // "example"
+        3, 99, 111, 109,                          // "com"
+        0,
+    };
+
+    std::vector<uint8_t> buf(data, data + sizeof(data));
+
+    boost::scoped_ptr<OptionCustom> option;
+    ASSERT_NO_THROW(
+        option.reset(new OptionCustom(opt_def, Option::V6, buf.begin(), buf.end()));
+    );
+    ASSERT_TRUE(option);
+
+    ASSERT_EQ(1, option->getDataFieldsNum());
+
+    std::string domain0 = option->readFqdn(0);
+    EXPECT_EQ("mydomain.example.com.", domain0);
+
+    // Check that the option with truncated data can't be created.
+    EXPECT_THROW(
+        option.reset(new OptionCustom(opt_def, Option::V6,
+                                      buf.begin(), buf.begin() + 4)),
+        isc::dhcp::BadDataTypeCast
+    );
+}
+
 // The purpose of this test is to verify that the option definition comprising
 // 16-bit signed integer value can be used to create an instance of custom option.
 TEST_F(OptionCustomTest, int16Data) {
@@ -338,6 +386,7 @@ TEST_F(OptionCustomTest, ipv6AddressData) {
     );
 }
 
+
 // The purpose of this test is to verify that the option definition comprising
 // string value can be used to create an instance of custom option.
 TEST_F(OptionCustomTest, stringData) {
@@ -365,8 +414,9 @@ TEST_F(OptionCustomTest, stringData) {
     EXPECT_EQ("hello world!", value);
 
     // Check that option will not be created if empty buffer is provided.
+    buf.clear();
     EXPECT_THROW(
-        option.reset(new OptionCustom(opt_def, Option::V6, OptionBuffer())),
+        option.reset(new OptionCustom(opt_def, Option::V6, buf.begin(), buf.end())),
         isc::OutOfRange
     );
 }
@@ -419,8 +469,9 @@ TEST_F(OptionCustomTest, booleanDataArray) {
 
     // Check that empty buffer can't be used to create option holding
     // array of boolean values.
+    buf.clear();
     EXPECT_THROW(
-         option.reset(new OptionCustom(opt_def, Option::V6, OptionBuffer())),
+         option.reset(new OptionCustom(opt_def, Option::V6, buf.begin(), buf.end())),
          isc::OutOfRange
     );
 }
@@ -472,7 +523,6 @@ TEST_F(OptionCustomTest, uint32DataArray) {
                                       buf.begin() + 3)),
         isc::OutOfRange
     );
-
 }
 
 // The purpose of this test is to verify that the option definition comprising
@@ -575,6 +625,45 @@ TEST_F(OptionCustomTest, ipv6AddressDataArray) {
     );
 }
 
+// The purpose of this test is to verify that the option comprising
+// an array of FQDN values can be created from a buffer which holds
+// multiple FQDN values encoded as described in the RFC1035, section
+// 3.1
+TEST_F(OptionCustomTest, fqdnDataArray) {
+    OptionDefinition opt_def("OPTION_FOO", 1000, "fqdn", true);
+
+    const char data[] = {
+        8, 109, 121, 100, 111, 109, 97, 105, 110, // "mydomain"
+        7, 101, 120, 97, 109, 112, 108, 101,      // "example"
+        3, 99, 111, 109,                          // "com"
+        0,
+        7, 101, 120, 97, 109, 112, 108, 101,      // "example"
+        3, 99, 111, 109,                          // "com"
+        0
+    };
+
+    // Create a buffer that holds two FQDNs.
+    std::vector<uint8_t> buf(data, data + sizeof(data));
+
+    // Create an option from using a buffer.
+    boost::scoped_ptr<OptionCustom> option;
+    ASSERT_NO_THROW(
+        option.reset(new OptionCustom(opt_def, Option::V6, buf));
+    );
+    ASSERT_TRUE(option);
+
+    // We expect that two FQDN values have been extracted
+    // from a buffer.
+    ASSERT_EQ(2, option->getDataFieldsNum());
+
+    // Validate both values.
+    std::string domain0 = option->readFqdn(0);
+    EXPECT_EQ("mydomain.example.com.", domain0);
+
+    std::string domain1 = option->readFqdn(1);
+    EXPECT_EQ("example.com.", domain1);
+}
+
 // The purpose of this test is to verify that the option definition comprising
 // a record of various data fields can be used to create an instance of
 // custom option.
@@ -584,20 +673,30 @@ TEST_F(OptionCustomTest, recordData) {
     OptionDefinition opt_def("OPTION_FOO", 1000, "record");
     ASSERT_NO_THROW(opt_def.addRecordField("uint16"));
     ASSERT_NO_THROW(opt_def.addRecordField("boolean"));
+    ASSERT_NO_THROW(opt_def.addRecordField("fqdn"));
     ASSERT_NO_THROW(opt_def.addRecordField("ipv4-address"));
     ASSERT_NO_THROW(opt_def.addRecordField("ipv6-address"));
     ASSERT_NO_THROW(opt_def.addRecordField("string"));
 
+    const char fqdn_data[] = {
+        8, 109, 121, 100, 111, 109, 97, 105, 110, // "mydomain"
+        7, 101, 120, 97, 109, 112, 108, 101,      // "example"
+        3, 99, 111, 109,                          // "com"
+        0,
+    };
+
     OptionBuffer buf;
-    // Initialize field 0.
+    // Initialize field 0 to 8712.
     writeInt<uint16_t>(8712, buf);
     // Initialize field 1 to 'true'
     buf.push_back(static_cast<unsigned short>(1));
-    // Initialize field 2 to IPv4 address.
+    // Initialize field 2 to 'mydomain.example.com'.
+    buf.insert(buf.end(), fqdn_data, fqdn_data + sizeof(fqdn_data));
+    // Initialize field 3 to IPv4 address.
     writeAddress(IOAddress("192.168.0.1"), buf);
-    // Initialize field 3 to IPv6 address.
+    // Initialize field 4 to IPv6 address.
     writeAddress(IOAddress("2001:db8:1::1"), buf);
-    // Initialize field 4 to string value.
+    // Initialize field 5 to string value.
     writeString("ABCD", buf);
 
     boost::scoped_ptr<OptionCustom> option;
@@ -606,8 +705,8 @@ TEST_F(OptionCustomTest, recordData) {
     );
     ASSERT_TRUE(option);
 
-    // We should have 5 data fields.
-    ASSERT_EQ(5, option->getDataFieldsNum());
+    // We should have 6 data fields.
+    ASSERT_EQ(6, option->getDataFieldsNum());
 
     // Verify value in the field 0.
     uint16_t value0 = 0;
@@ -620,19 +719,24 @@ TEST_F(OptionCustomTest, recordData) {
     EXPECT_TRUE(value1);
 
     // Verify value in the field 2.
-    IOAddress value2("127.0.0.1");
-    ASSERT_NO_THROW(value2 = option->readAddress(2));
-    EXPECT_EQ("192.168.0.1", value2.toText());
+    std::string value2 = "";
+    ASSERT_NO_THROW(value2 = option->readFqdn(2));
+    EXPECT_EQ("mydomain.example.com.", value2);
 
     // Verify value in the field 3.
-    IOAddress value3("::1");
+    IOAddress value3("127.0.0.1");
     ASSERT_NO_THROW(value3 = option->readAddress(3));
-    EXPECT_EQ("2001:db8:1::1", value3.toText());
+    EXPECT_EQ("192.168.0.1", value3.toText());
 
     // Verify value in the field 4.
-    std::string value4;
-    ASSERT_NO_THROW(value4 = option->readString(4));
-    EXPECT_EQ("ABCD", value4);
+    IOAddress value4("::1");
+    ASSERT_NO_THROW(value4 = option->readAddress(4));
+    EXPECT_EQ("2001:db8:1::1", value4.toText());
+
+    // Verify value in the field 5.
+    std::string value5;
+    ASSERT_NO_THROW(value5 = option->readString(5));
+    EXPECT_EQ("ABCD", value5);
 }
 
 // The purpose of this test is to verify that truncated buffer
@@ -683,6 +787,413 @@ TEST_F(OptionCustomTest, recordDataTruncated) {
     );
 }
 
+// The purpose of this test is to verify that an option comprising
+// single data field with binary data can be used and that this
+// binary data is properly initialized to a default value. This
+// test also checks that it is possible to override this default
+// value.
+TEST_F(OptionCustomTest, setBinaryData) {
+    OptionDefinition opt_def("OPTION_FOO", 1000, "binary");
+
+    // Create an option and let the data field be initialized
+    // to default value (do not provide any data buffer).
+    boost::scoped_ptr<OptionCustom> option;
+    ASSERT_NO_THROW(
+        option.reset(new OptionCustom(opt_def, Option::V6));
+    );
+    ASSERT_TRUE(option);
+
+    // Get the default binary value.
+    OptionBuffer buf;
+    ASSERT_NO_THROW(option->readBinary());
+    // The buffer is by default empty.
+    EXPECT_TRUE(buf.empty());
+    // Prepare input buffer with some dummy data.
+    OptionBuffer buf_in(10);
+    for (int i = 0; i < buf_in.size(); ++i) {
+        buf_in[i] = i;
+    }
+    // Try to override the default binary buffer.
+    ASSERT_NO_THROW(option->writeBinary(buf_in));
+    // And check that it has been actually overriden.
+    ASSERT_NO_THROW(buf = option->readBinary());
+    ASSERT_EQ(buf_in.size(), buf.size());
+    EXPECT_TRUE(std::equal(buf_in.begin(), buf_in.end(), buf.begin()));
+}
+
+// The purpose of this test is to verify that an option comprising
+// single boolean data field can be created and that its default
+// value can be overriden by a new value.
+TEST_F(OptionCustomTest, setBooleanData) {
+    OptionDefinition opt_def("OPTION_FOO", 1000, "boolean");
+
+    // Create an option and let the data field be initialized
+    // to default value (do not provide any data buffer).
+    boost::scoped_ptr<OptionCustom> option;
+    ASSERT_NO_THROW(
+        option.reset(new OptionCustom(opt_def, Option::V6));
+    );
+    ASSERT_TRUE(option);
+    // Check that the default boolean value is false.
+    bool value = false;
+    ASSERT_NO_THROW(value = option->readBoolean());
+    EXPECT_FALSE(value);
+    // Check that we can override the default value.
+    ASSERT_NO_THROW(option->writeBoolean(true));
+    // Finally, check that it has been actually overriden.
+    ASSERT_NO_THROW(value = option->readBoolean());
+    EXPECT_TRUE(value);
+}
+
+/// The purpose of this test is to verify that the data field value
+/// can be overriden by a new value.
+TEST_F(OptionCustomTest, setUint32Data) {
+    // Create a definition of an option that holds single
+    // uint32 value.
+    OptionDefinition opt_def("OPTION_FOO", 1000, "uint32");
+
+    // Create an option and let the data field be initialized
+    // to default value (do not provide any data buffer).
+    boost::scoped_ptr<OptionCustom> option;
+    ASSERT_NO_THROW(
+        option.reset(new OptionCustom(opt_def, Option::V6));
+    );
+    ASSERT_TRUE(option);
+
+    // The default value for integer data fields is 0.
+    uint32_t value = 0;
+    ASSERT_NO_THROW(option->readInteger<uint32_t>());
+    EXPECT_EQ(0, value);
+
+    // Try to set the data field value to something different
+    // than 0.
+    ASSERT_NO_THROW(option->writeInteger<uint32_t>(1234));
+
+    // Verify that it has been set.
+    ASSERT_NO_THROW(value = option->readInteger<uint32_t>());
+    EXPECT_EQ(1234, value);
+}
+
+// The purpose of this test is to verify that an option comprising
+// single IPv4 address can be created and that this address can
+// be overriden by a new value.
+TEST_F(OptionCustomTest, setIpv4AddressData) {
+    OptionDefinition opt_def("OPTION_FOO", 232, "ipv4-address");
+
+    // Create an option and let the data field be initialized
+    // to default value (do not provide any data buffer).
+    boost::scoped_ptr<OptionCustom> option;
+    ASSERT_NO_THROW(
+        option.reset(new OptionCustom(opt_def, Option::V4));
+    );
+    ASSERT_TRUE(option);
+
+    asiolink::IOAddress address("127.0.0.1");
+    ASSERT_NO_THROW(address = option->readAddress());
+    EXPECT_EQ("0.0.0.0", address.toText());
+
+    EXPECT_NO_THROW(option->writeAddress(IOAddress("192.168.0.1")));
+
+    EXPECT_NO_THROW(address = option->readAddress());
+    EXPECT_EQ("192.168.0.1", address.toText());
+}
+
+// The purpose of this test is to verify that an opton comprising
+// single IPv6 address can be created and that this address can
+// be overriden by a new value.
+TEST_F(OptionCustomTest, setIpv6AddressData) {
+    OptionDefinition opt_def("OPTION_FOO", 1000, "ipv6-address");
+
+    // Create an option and let the data field be initialized
+    // to default value (do not provide any data buffer).
+    boost::scoped_ptr<OptionCustom> option;
+    ASSERT_NO_THROW(
+        option.reset(new OptionCustom(opt_def, Option::V6));
+    );
+    ASSERT_TRUE(option);
+
+    asiolink::IOAddress address("::1");
+    ASSERT_NO_THROW(address = option->readAddress());
+    EXPECT_EQ("::", address.toText());
+
+    EXPECT_NO_THROW(option->writeAddress(IOAddress("2001:db8:1::1")));
+
+    EXPECT_NO_THROW(address = option->readAddress());
+    EXPECT_EQ("2001:db8:1::1", address.toText());
+}
+
+// The purpose of this test is to verify that an option comprising
+// single string value can be created and that this value
+// is initialized to the default value. Also, this test checks that
+// this value can be overwritten by a new value.
+TEST_F(OptionCustomTest, setStringData) {
+    OptionDefinition opt_def("OPTION_FOO", 1000, "string");
+
+    // Create an option and let the data field be initialized
+    // to default value (do not provide any data buffer).
+    boost::scoped_ptr<OptionCustom> option;
+    ASSERT_NO_THROW(
+        option.reset(new OptionCustom(opt_def, Option::V6));
+    );
+    ASSERT_TRUE(option);
+
+    // Get the default value of the option.
+    std::string value;
+    ASSERT_NO_THROW(value = option->readString());
+    // By default the string data field is empty.
+    EXPECT_TRUE(value.empty());
+    // Write some text to this field.
+    ASSERT_NO_THROW(option->writeString("hello world"));
+    // Check that it has been actually written.
+    EXPECT_NO_THROW(value = option->readString());
+    EXPECT_EQ("hello world", value);
+}
+
+/// The purpose of this test is to verify that an option comprising
+/// a default FQDN value can be created and that this value can be
+/// overriden after the option has been created.
+TEST_F(OptionCustomTest, setFqdnData) {
+    OptionDefinition opt_def("OPTION_FOO", 1000, "fqdn");
+
+    // Create an option and let the data field be initialized
+    // to default value (do not provide any data buffer).
+    boost::scoped_ptr<OptionCustom> option;
+    ASSERT_NO_THROW(
+        option.reset(new OptionCustom(opt_def, Option::V6));
+    );
+    ASSERT_TRUE(option);
+    // Read a default FQDN value from the option.
+    std::string fqdn;
+    ASSERT_NO_THROW(fqdn = option->readFqdn());
+    EXPECT_EQ(".", fqdn);
+    // Try override the default FQDN value.
+    ASSERT_NO_THROW(option->writeFqdn("example.com"));
+    // Check that the value has been actually overriden.
+    ASSERT_NO_THROW(fqdn = option->readFqdn());
+    EXPECT_EQ("example.com.", fqdn);
+}
+
+// The purpose of this test is to verify that an option carrying
+// an array of boolean values can be created with no values
+// initially and that values can be later added to it.
+TEST_F(OptionCustomTest, setBooleanDataArray) {
+    OptionDefinition opt_def("OPTION_FOO", 1000, "boolean", true);
+
+    // Create an option and let the data field be initialized
+    // to default value (do not provide any data buffer).
+    boost::scoped_ptr<OptionCustom> option;
+    ASSERT_NO_THROW(
+        option.reset(new OptionCustom(opt_def, Option::V6));
+    );
+    ASSERT_TRUE(option);
+
+    // Initially, the array should contain no values.
+    ASSERT_EQ(0, option->getDataFieldsNum());
+
+    // Add some boolean values to it.
+    ASSERT_NO_THROW(option->addArrayDataField(true));
+    ASSERT_NO_THROW(option->addArrayDataField(false));
+    ASSERT_NO_THROW(option->addArrayDataField(true));
+
+    // Verify that the new data fields can be added.
+    bool value0 = false;
+    ASSERT_NO_THROW(value0 = option->readBoolean(0));
+    EXPECT_TRUE(value0);
+    bool value1 = true;
+    ASSERT_NO_THROW(value1 = option->readBoolean(1));
+    EXPECT_FALSE(value1);
+    bool value2 = false;
+    ASSERT_NO_THROW(value2 = option->readBoolean(2));
+    EXPECT_TRUE(value2);
+}
+
+// The purpose of this test is to verify that am option carying
+// an array of 16-bit signed integer values can be created with
+// no values initially and that the values can be later added to it.
+TEST_F(OptionCustomTest, setUint16DataArray) {
+    OptionDefinition opt_def("OPTION_FOO", 1000, "uint16", true);
+
+    // Create an option and let the data field be initialized
+    // to default value (do not provide any data buffer).
+    boost::scoped_ptr<OptionCustom> option;
+    ASSERT_NO_THROW(
+        option.reset(new OptionCustom(opt_def, Option::V6));
+    );
+    ASSERT_TRUE(option);
+
+    // Initially, the array should contain no values.
+    ASSERT_EQ(0, option->getDataFieldsNum());
+
+    // Add 3 new data fields holding integer values.
+    ASSERT_NO_THROW(option->addArrayDataField<uint16_t>(67));
+    ASSERT_NO_THROW(option->addArrayDataField<uint16_t>(876));
+    ASSERT_NO_THROW(option->addArrayDataField<uint16_t>(32222));
+
+    // We should now have 3 data fields.
+    ASSERT_EQ(3, option->getDataFieldsNum());
+
+    // Check that the values have been correctly set.
+    uint16_t value0;
+    ASSERT_NO_THROW(value0 = option->readInteger<uint16_t>(0));
+    EXPECT_EQ(67, value0);
+    uint16_t value1;
+    ASSERT_NO_THROW(value1 = option->readInteger<uint16_t>(1));
+    EXPECT_EQ(876, value1);
+    uint16_t value2;
+    ASSERT_NO_THROW(value2 = option->readInteger<uint16_t>(2));
+    EXPECT_EQ(32222, value2);
+}
+
+/// The purpose of this test is to verify that an option comprising
+/// array of IPv4 address can be created with no addresses and that
+/// multiple IPv4 addresses can be added to it after creation.
+TEST_F(OptionCustomTest, setIpv4AddressDataArray) {
+    OptionDefinition opt_def("OPTION_FOO", 232, "ipv4-address", true);
+
+    // Create an option and let the data field be initialized
+    // to default value (do not provide any data buffer).
+    boost::scoped_ptr<OptionCustom> option;
+    ASSERT_NO_THROW(
+        option.reset(new OptionCustom(opt_def, Option::V4));
+    );
+    ASSERT_TRUE(option);
+
+    // Expect that the array does not contain any data fields yet.
+    ASSERT_EQ(0, option->getDataFieldsNum());
+
+    // Add 3 IPv4 addresses.
+    ASSERT_NO_THROW(option->addArrayDataField(IOAddress("192.168.0.1")));
+    ASSERT_NO_THROW(option->addArrayDataField(IOAddress("192.168.0.2")));
+    ASSERT_NO_THROW(option->addArrayDataField(IOAddress("192.168.0.3")));
+
+    ASSERT_EQ(3, option->getDataFieldsNum());
+
+    // Check that all IP addresses have been set correctly.
+    IOAddress address0("127.0.0.1");
+    ASSERT_NO_THROW(address0 = option->readAddress(0));
+    EXPECT_EQ("192.168.0.1", address0.toText());
+    IOAddress address1("127.0.0.1");
+    ASSERT_NO_THROW(address1 = option->readAddress(1));
+    EXPECT_EQ("192.168.0.2", address1.toText());
+    IOAddress address2("127.0.0.1");
+    ASSERT_NO_THROW(address2 = option->readAddress(2));
+    EXPECT_EQ("192.168.0.3", address2.toText());
+
+    // Add invalid address (IPv6 instead of IPv4).
+    EXPECT_THROW(
+        option->addArrayDataField(IOAddress("2001:db8:1::1")),
+        isc::dhcp::BadDataTypeCast
+    );
+}
+
+/// The purpose of this test is to verify that an option comprising
+/// array of IPv6 address can be created with no addresses and that
+/// multiple IPv6 addresses can be added to it after creation.
+TEST_F(OptionCustomTest, setIpv6AddressDataArray) {
+    OptionDefinition opt_def("OPTION_FOO", 1000, "ipv6-address", true);
+
+    // Create an option and let the data field be initialized
+    // to default value (do not provide any data buffer).
+    boost::scoped_ptr<OptionCustom> option;
+    ASSERT_NO_THROW(
+        option.reset(new OptionCustom(opt_def, Option::V6));
+    );
+    ASSERT_TRUE(option);
+
+    // Initially, the array does not contain any data fields.
+    ASSERT_EQ(0, option->getDataFieldsNum());
+
+    // Add 3 new IPv6 addresses into the array.
+    ASSERT_NO_THROW(option->addArrayDataField(IOAddress("2001:db8:1::1")));
+    ASSERT_NO_THROW(option->addArrayDataField(IOAddress("2001:db8:1::2")));
+    ASSERT_NO_THROW(option->addArrayDataField(IOAddress("2001:db8:1::3")));
+
+    // We should have now 3 addresses added.
+    ASSERT_EQ(3, option->getDataFieldsNum());
+
+    // Check that they have correct values set.
+    IOAddress address0("::1");
+    ASSERT_NO_THROW(address0 = option->readAddress(0));
+    EXPECT_EQ("2001:db8:1::1", address0.toText());
+    IOAddress address1("::1");
+    ASSERT_NO_THROW(address1 = option->readAddress(1));
+    EXPECT_EQ("2001:db8:1::2", address1.toText());
+    IOAddress address2("::1");
+    ASSERT_NO_THROW(address2 = option->readAddress(2));
+    EXPECT_EQ("2001:db8:1::3", address2.toText());
+
+    // Add invalid address (IPv4 instead of IPv6).
+    EXPECT_THROW(
+        option->addArrayDataField(IOAddress("192.168.0.1")),
+        isc::dhcp::BadDataTypeCast
+    );
+}
+
+TEST_F(OptionCustomTest, setRecordData) {
+    OptionDefinition opt_def("OPTION_FOO", 1000, "record");
+
+    ASSERT_NO_THROW(opt_def.addRecordField("uint16"));
+    ASSERT_NO_THROW(opt_def.addRecordField("boolean"));
+    ASSERT_NO_THROW(opt_def.addRecordField("fqdn"));
+    ASSERT_NO_THROW(opt_def.addRecordField("ipv4-address"));
+    ASSERT_NO_THROW(opt_def.addRecordField("ipv6-address"));
+    ASSERT_NO_THROW(opt_def.addRecordField("string"));
+
+    // Create an option and let the data field be initialized
+    // to default value (do not provide any data buffer).
+    boost::scoped_ptr<OptionCustom> option;
+    ASSERT_NO_THROW(
+        option.reset(new OptionCustom(opt_def, Option::V6));
+    );
+    ASSERT_TRUE(option);
+
+    // The number of elements should be equal to number of elements
+    // in the record.
+    ASSERT_EQ(6, option->getDataFieldsNum());
+
+    // Check that the default values have been correctly set.
+    uint16_t value0;
+    ASSERT_NO_THROW(value0 = option->readInteger<uint16_t>(0));
+    EXPECT_EQ(0, value0);
+    bool value1 = true;
+    ASSERT_NO_THROW(value1 = option->readBoolean(1));
+    EXPECT_FALSE(value1);
+    std::string value2;
+    ASSERT_NO_THROW(value2 = option->readFqdn(2));
+    EXPECT_EQ(".", value2);
+    IOAddress value3("127.0.0.1");
+    ASSERT_NO_THROW(value3 = option->readAddress(3));
+    EXPECT_EQ("0.0.0.0", value3.toText());
+    IOAddress value4("2001:db8:1::1");
+    ASSERT_NO_THROW(value4 = option->readAddress(4));
+    EXPECT_EQ("::", value4.toText());
+    std::string value5 = "xyz";
+    ASSERT_NO_THROW(value5 = option->readString(5));
+    EXPECT_TRUE(value5.empty());
+
+    // Override each value with a new value.
+    ASSERT_NO_THROW(option->writeInteger<uint16_t>(1234, 0));
+    ASSERT_NO_THROW(option->writeBoolean(true, 1));
+    ASSERT_NO_THROW(option->writeFqdn("example.com", 2));
+    ASSERT_NO_THROW(option->writeAddress(IOAddress("192.168.0.1"), 3));
+    ASSERT_NO_THROW(option->writeAddress(IOAddress("2001:db8:1::100"), 4));
+    ASSERT_NO_THROW(option->writeString("hello world", 5));
+
+    // Check that the new values have been correctly set.
+    ASSERT_NO_THROW(value0 = option->readInteger<uint16_t>(0));
+    EXPECT_EQ(1234, value0);
+    ASSERT_NO_THROW(value1 = option->readBoolean(1));
+    EXPECT_TRUE(value1);
+    ASSERT_NO_THROW(value2 = option->readFqdn(2));
+    EXPECT_EQ("example.com.", value2);
+    ASSERT_NO_THROW(value3 = option->readAddress(3));
+    EXPECT_EQ("192.168.0.1", value3.toText());
+    ASSERT_NO_THROW(value4 = option->readAddress(4));
+    EXPECT_EQ("2001:db8:1::100", value4.toText());
+    ASSERT_NO_THROW(value5 = option->readString(5));
+    EXPECT_EQ(value5, "hello world");
+}
+
 // The purpose of this test is to verify that pack function for
 // DHCPv4 custom option works correctly.
 TEST_F(OptionCustomTest, pack4) {
@@ -901,6 +1412,4 @@ TEST_F(OptionCustomTest, invalidIndex) {
     EXPECT_THROW(option->readInteger<uint32_t>(11), isc::OutOfRange);
 }
 
-
-
 } // anonymous namespace
diff --git a/src/lib/dhcp/tests/option_data_types_unittest.cc b/src/lib/dhcp/tests/option_data_types_unittest.cc
new file mode 100644
index 0000000..748a84b
--- /dev/null
+++ b/src/lib/dhcp/tests/option_data_types_unittest.cc
@@ -0,0 +1,491 @@
+// 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/option_data_types.h>
+#include <gtest/gtest.h>
+
+using namespace isc;
+using namespace isc::dhcp;
+
+namespace {
+
+/// @brief Test class for option data type utilities.
+class OptionDataTypesTest : public ::testing::Test {
+public:
+
+    /// @brief Constructor.
+    OptionDataTypesTest() { }
+
+    /// @brief Write IP address into a buffer.
+    ///
+    /// @param address address to be written.
+    /// @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());
+        }
+    }
+
+    /// @brief Write integer (signed or unsigned) into a buffer.
+    ///
+    /// @param value integer value.
+    /// @param [out] buf output buffer.
+    /// @tparam integer type.
+    template<typename T>
+    void writeInt(T value, std::vector<uint8_t>& buf) {
+        for (int i = 0; i < sizeof(T); ++i) {
+            buf.push_back(value >> ((sizeof(T) - i - 1) * 8) & 0xFF);
+        }
+    }
+
+    /// @brief Write a string into a buffer.
+    ///
+    /// @param value string to be written into a buffer.
+    /// @param buf output buffer.
+    void writeString(const std::string& value,
+                     std::vector<uint8_t>& buf) {
+        buf.resize(buf.size() + value.size());
+        std::copy_backward(value.c_str(), value.c_str() + value.size(),
+                           buf.end());
+    }
+};
+
+// The goal of this test is to verify that an IPv4 address being
+// stored in a buffer (wire format) can be read into IOAddress
+// object.
+TEST_F(OptionDataTypesTest, readAddress) {
+    // Create some IPv4 address.
+    asiolink::IOAddress address("192.168.0.1");
+    // And store it in a buffer in a wire format.
+    std::vector<uint8_t> buf;
+    writeAddress(address, buf);
+
+    // Now, try to read the IP address with a utility function
+    // being under test.
+    asiolink::IOAddress address_out("127.0.0.1");
+    EXPECT_NO_THROW(address_out = OptionDataTypeUtil::readAddress(buf, AF_INET));
+
+    // Check that the read address matches address that
+    // we used as input.
+    EXPECT_EQ(address.toText(), address_out.toText());
+
+    // Check that an attempt to read the buffer as IPv6 address
+    // causes an error as the IPv6 address needs at least 16 bytes
+    // long buffer.
+    EXPECT_THROW(
+        OptionDataTypeUtil::readAddress(buf, AF_INET6),
+        isc::dhcp::BadDataTypeCast
+    );
+
+    buf.clear();
+
+    // Do another test like this for IPv6 address.
+    address = asiolink::IOAddress("2001:db8:1:0::1");
+    writeAddress(address, buf);
+    EXPECT_NO_THROW(address_out = OptionDataTypeUtil::readAddress(buf, AF_INET6));
+    EXPECT_EQ(address.toText(), address_out.toText());
+
+    // Truncate the buffer and expect an error to be reported when
+    // trying to read it.
+    buf.resize(buf.size() - 1);
+    EXPECT_THROW(
+        OptionDataTypeUtil::readAddress(buf, AF_INET6),
+        isc::dhcp::BadDataTypeCast
+    );
+}
+
+// The goal of this test is to verify that an IPv6 address
+// is properly converted to wire format and stored in a
+// buffer.
+TEST_F(OptionDataTypesTest, writeAddress) {
+    // Encode an IPv6 address 2001:db8:1::1 in wire format.
+    // This will be used as reference data to validate if
+    // an IPv6 address is stored in a buffer properly.
+    const uint8_t data[] = {
+        0x20, 0x01, 0x0d, 0xb8, 0x0, 0x1, 0x0, 0x0,
+        0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1
+    };
+    std::vector<uint8_t> buf_in(data, data + sizeof(data));
+
+    // Create IPv6 address object.
+    asiolink::IOAddress address("2001:db8:1::1");
+    // Define the output buffer to write IP address to.
+    std::vector<uint8_t> buf_out;
+    // Write the address to the buffer.
+    ASSERT_NO_THROW(OptionDataTypeUtil::writeAddress(address, buf_out));
+    // Make sure that input and output buffers have the same size
+    // so we can compare them.
+    ASSERT_EQ(buf_in.size(), buf_out.size());
+    // And finally compare them.
+    EXPECT_TRUE(std::equal(buf_in.begin(), buf_in.end(), buf_out.begin()));
+
+    buf_out.clear();
+
+    // Do similar test for IPv4 address.
+    address = asiolink::IOAddress("192.168.0.1");
+    ASSERT_NO_THROW(OptionDataTypeUtil::writeAddress(address, buf_out));
+    ASSERT_EQ(4, buf_out.size());
+    // Verify that the IP address has been written correctly.
+    EXPECT_EQ(192, buf_out[0]);
+    EXPECT_EQ(168, buf_out[1]);
+    EXPECT_EQ(0, buf_out[2]);
+    EXPECT_EQ(1, buf_out[3]);
+}
+
+// The purpose of this test is to verify that binary data represented
+// as a string of hexadecimal digits can be written to a buffer.
+TEST_F(OptionDataTypesTest, writeBinary) {
+    // Prepare the reference data.
+    const char data[] = {
+        0x0, 0x1, 0x2, 0x3, 0x4, 0x5,
+        0x6, 0x7, 0x8, 0x9, 0xA, 0xB
+    };
+    std::vector<uint8_t> buf_ref(data, data + sizeof(data));
+    // Create empty vector where binary data will be written to.
+    std::vector<uint8_t> buf;
+    ASSERT_NO_THROW(
+        OptionDataTypeUtil::writeBinary("000102030405060708090A0B", buf)
+    );
+    // Verify that the buffer contains valid data.
+    ASSERT_EQ(buf_ref.size(), buf.size());
+    EXPECT_TRUE(std::equal(buf_ref.begin(), buf_ref.end(), buf.begin()));
+}
+
+// The purpose of this test is to verify that the boolean value stored
+// in a buffer is correctly read from this buffer.
+TEST_F(OptionDataTypesTest, readBool) {
+    // Create an input buffer.
+    std::vector<uint8_t> buf;
+    // 'true' value is encoded as 1 ('false' is encoded as 0)
+    buf.push_back(1);
+
+    // Read the value from the buffer.
+    bool value = false;
+    ASSERT_NO_THROW(
+        value = OptionDataTypeUtil::readBool(buf);
+    );
+    // Verify the value.
+    EXPECT_TRUE(value);
+    // Check if 'false' is read correctly either.
+    buf[0] = 0;
+    ASSERT_NO_THROW(
+        value = OptionDataTypeUtil::readBool(buf);
+    );
+    EXPECT_FALSE(value);
+
+    // Check that invalid value causes exception.
+    buf[0] = 5;
+    ASSERT_THROW(
+        OptionDataTypeUtil::readBool(buf),
+        isc::dhcp::BadDataTypeCast
+    );
+}
+
+// The purpose of this test is to verify that boolean values
+// are correctly encoded in a buffer as '1' for 'true' and
+// '0' for 'false' values.
+TEST_F(OptionDataTypesTest, writeBool) {
+    // Create a buffer we will write to.
+    std::vector<uint8_t> buf;
+    // Write the 'true' value to the buffer.
+    ASSERT_NO_THROW(OptionDataTypeUtil::writeBool(true, buf));
+    // We should now have 'true' value stored in a buffer.
+    ASSERT_EQ(1, buf.size());
+    EXPECT_EQ(buf[0], 1);
+    // Let's append another value to make sure that it is not always
+    // 'true' value being written.
+    ASSERT_NO_THROW(OptionDataTypeUtil::writeBool(false, buf));
+    ASSERT_EQ(2, buf.size());
+    // Check that the first value has not changed.
+    EXPECT_EQ(buf[0], 1);
+    // Check the the second value is correct.
+    EXPECT_EQ(buf[1], 0);
+}
+
+// The purpose of this test is to verify that the integer values
+// of different types are correctly read from a buffer.
+TEST_F(OptionDataTypesTest, readInt) {
+    std::vector<uint8_t> buf;
+
+    // Write an 8-bit unsigned integer value to the buffer.
+    writeInt<uint8_t>(129, buf);
+    uint8_t valueUint8 = 0;
+    // Read the value and check that it is valid.
+    ASSERT_NO_THROW(
+        valueUint8 = OptionDataTypeUtil::readInt<uint8_t>(buf);
+    );
+    EXPECT_EQ(129, valueUint8);
+
+    // Try to read 16-bit value from a buffer holding 8-bit value.
+    // This should result in an exception.
+    EXPECT_THROW(
+        OptionDataTypeUtil::readInt<uint16_t>(buf),
+        isc::dhcp::BadDataTypeCast
+    );
+
+    // Clear the buffer for the next check we are going to do.
+    buf.clear();
+
+    // Test uint16_t value.
+    writeInt<uint16_t>(1234, buf);
+    uint16_t valueUint16 = 0;
+    ASSERT_NO_THROW(
+        valueUint16 = OptionDataTypeUtil::readInt<uint16_t>(buf);
+    );
+    EXPECT_EQ(1234, valueUint16);
+
+    // Try to read 32-bit value from a buffer holding 16-bit value.
+    // This should result in an exception.
+    EXPECT_THROW(
+        OptionDataTypeUtil::readInt<uint32_t>(buf),
+        isc::dhcp::BadDataTypeCast
+    );
+
+    buf.clear();
+
+    // Test uint32_t value.
+    writeInt<uint32_t>(56789, buf);
+    uint32_t valueUint32 = 0;
+    ASSERT_NO_THROW(
+        valueUint32 = OptionDataTypeUtil::readInt<uint32_t>(buf);
+    );
+    EXPECT_EQ(56789, valueUint32);
+    buf.clear();
+
+    // Test int8_t value.
+    writeInt<int8_t>(-65, buf);
+    int8_t valueInt8 = 0;
+    ASSERT_NO_THROW(
+        valueInt8 = OptionDataTypeUtil::readInt<int8_t>(buf);
+    );
+    EXPECT_EQ(-65, valueInt8);
+    buf.clear();
+
+    // Try to read 16-bit value from a buffer holding 8-bit value.
+    // This should result in an exception.
+    EXPECT_THROW(
+        OptionDataTypeUtil::readInt<int16_t>(buf),
+        isc::dhcp::BadDataTypeCast
+    );
+
+    // Test int16_t value.
+    writeInt<int16_t>(2345, buf);
+    int32_t valueInt16 = 0;
+    ASSERT_NO_THROW(
+        valueInt16 = OptionDataTypeUtil::readInt<int16_t>(buf);
+    );
+    EXPECT_EQ(2345, valueInt16);
+    buf.clear();
+
+    // Try to read 32-bit value from a buffer holding 16-bit value.
+    // This should result in an exception.
+    EXPECT_THROW(
+        OptionDataTypeUtil::readInt<int32_t>(buf),
+        isc::dhcp::BadDataTypeCast
+    );
+
+    // Test int32_t value.
+    writeInt<int32_t>(-16543, buf);
+    int32_t valueInt32 = 0;
+    ASSERT_NO_THROW(
+        valueInt32 = OptionDataTypeUtil::readInt<int32_t>(buf);
+    );
+    EXPECT_EQ(-16543, valueInt32);
+
+    buf.clear();
+}
+
+// The purpose of this test is to verify that integer values of different
+// types are correctly written to a buffer.
+TEST_F(OptionDataTypesTest, writeInt) {
+    // Prepare the reference buffer.
+    const uint8_t data[] = {
+        0x7F, // 127
+        0x03, 0xFF, // 1023
+        0x00, 0x00, 0x10, 0x00, // 4096
+        0xFF, 0xFF, 0xFC, 0x00, // -1024
+        0x02, 0x00, // 512
+        0x81 // -127
+    };
+    std::vector<uint8_t> buf_ref(data, data + sizeof(data));
+
+    // Fill in the buffer with data. Each write operation appends an
+    // integer value. Eventually the buffer holds all values and should
+    // match with the reference buffer.
+    std::vector<uint8_t> buf;
+    ASSERT_NO_THROW(OptionDataTypeUtil::writeInt<uint8_t>(127, buf));
+    ASSERT_NO_THROW(OptionDataTypeUtil::writeInt<uint16_t>(1023, buf));
+    ASSERT_NO_THROW(OptionDataTypeUtil::writeInt<uint32_t>(4096, buf));
+    ASSERT_NO_THROW(OptionDataTypeUtil::writeInt<int32_t>(-1024, buf));
+    ASSERT_NO_THROW(OptionDataTypeUtil::writeInt<int16_t>(512, buf));
+    ASSERT_NO_THROW(OptionDataTypeUtil::writeInt<int8_t>(-127, buf));
+
+    // Make sure that the buffer has the same size as the reference
+    // buffer.
+    ASSERT_EQ(buf_ref.size(), buf.size());
+    // Compare buffers.
+    EXPECT_TRUE(std::equal(buf_ref.begin(), buf_ref.end(), buf.begin()));
+}
+
+// The purpose of this test is to verify that FQDN is read from
+// a buffer and returned as a text. The representation of the FQDN
+// in the buffer complies with RFC1035, section 3.1.
+// This test also checks that if invalid (truncated) FQDN is stored
+// in a buffer the appropriate exception is returned when trying to
+// read it as a string.
+TEST_F(OptionDataTypesTest, readFqdn) {
+    // The binary representation of the "mydomain.example.com".
+    // Values: 8, 7, 3 and 0 specify the lengths of subsequent
+    // labels within the FQDN.
+    const char data[] = {
+        8, 109, 121, 100, 111, 109, 97, 105, 110, // "mydomain"
+        7, 101, 120, 97, 109, 112, 108, 101,      // "example"
+        3, 99, 111, 109,                          // "com"
+        0
+    };
+
+    // Make a vector out of the data.
+    std::vector<uint8_t> buf(data, data + sizeof(data));
+
+    // Read the buffer as FQDN and verify its correctness.
+    std::string fqdn;
+    EXPECT_NO_THROW(fqdn = OptionDataTypeUtil::readFqdn(buf));
+    EXPECT_EQ("mydomain.example.com.", fqdn);
+
+    // By resizing the buffer we simulate truncation. The first
+    // length field (8) indicate that the first label's size is
+    // 8 but the actual buffer size is 5. Expect that conversion
+    // fails.
+    buf.resize(5);
+    EXPECT_THROW(
+        OptionDataTypeUtil::readFqdn(buf),
+        isc::dhcp::BadDataTypeCast
+    );
+
+    // Another special case: provide an empty buffer.
+    buf.clear();
+    EXPECT_THROW(
+        OptionDataTypeUtil::readFqdn(buf),
+        isc::dhcp::BadDataTypeCast
+    );
+}
+
+// The purpose of this test is to verify that FQDN's syntax is validated
+// and that FQDN is correctly written to a buffer in a format described
+// in RFC1035 section 3.1.
+TEST_F(OptionDataTypesTest, writeFqdn) {
+    // Create empty buffer. The FQDN will be written to it.
+    OptionBuffer buf;
+    // Write a domain name into the buffer in the format described
+    // in RFC1035 section 3.1. This function should not throw
+    // exception because domain name is well formed.
+    EXPECT_NO_THROW(
+        OptionDataTypeUtil::writeFqdn("mydomain.example.com", buf)
+    );
+    // The length of the data is 22 (8 bytes for "mydomain" label,
+    // 7 bytes for "example" label, 3 bytes for "com" label and
+    // finally 4 bytes positions between labels where length
+    // information is stored.
+    ASSERT_EQ(22, buf.size());
+
+    // Verify that length fields between labels hold valid values.
+    EXPECT_EQ(8, buf[0]);  // length of "mydomain"
+    EXPECT_EQ(7, buf[9]);  // length of "example"
+    EXPECT_EQ(3, buf[17]); // length of "com"
+    EXPECT_EQ(0, buf[21]); // zero byte at the end.
+
+    // Verify that labels are valid.
+    std::string label0(buf.begin() + 1, buf.begin() + 9);
+    EXPECT_EQ("mydomain", label0);
+
+    std::string label1(buf.begin() + 10, buf.begin() + 17);
+    EXPECT_EQ("example", label1);
+
+    std::string label2(buf.begin() + 18, buf.begin() + 21);
+    EXPECT_EQ("com", label2);
+
+    // The tested function is supposed to append data to a buffer
+    // so let's check that it is a case by appending another domain.
+    OptionDataTypeUtil::writeFqdn("hello.net", buf);
+
+    // The buffer length should be now longer.
+    ASSERT_EQ(33, buf.size());
+
+    // Check the length fields for new labels being appended.
+    EXPECT_EQ(5, buf[22]);
+    EXPECT_EQ(3, buf[28]);
+
+    // And check that labels are ok.
+    std::string label3(buf.begin() + 23, buf.begin() + 28);
+    EXPECT_EQ("hello", label3);
+
+    std::string label4(buf.begin() + 29, buf.begin() + 32);
+    EXPECT_EQ("net", label4);
+
+    // Check that invalid (empty) FQDN is rejected and expected
+    // exception type is thrown.
+    buf.clear();
+    EXPECT_THROW(
+        OptionDataTypeUtil::writeFqdn("", buf),
+        isc::dhcp::BadDataTypeCast
+    );
+
+    // Check another invalid domain name (with repeated dot).
+    buf.clear();
+    EXPECT_THROW(
+        OptionDataTypeUtil::writeFqdn("example..com", buf),
+        isc::dhcp::BadDataTypeCast
+    );
+}
+
+// The purpose of this test is to verify that the string
+// can be read from a buffer correctly.
+TEST_F(OptionDataTypesTest, readString) {
+    // Prepare a buffer with some string in it.
+    std::vector<uint8_t> buf;
+    writeString("hello world", buf);
+
+    // Read the string from the buffer.
+    std::string value;
+    ASSERT_NO_THROW(
+        value = OptionDataTypeUtil::readString(buf);
+    );
+    // Check that it is valid.
+    EXPECT_EQ("hello world", value);
+}
+
+// The purpose of this test is to verify that a string can be
+// stored in a buffer correctly.
+TEST_F(OptionDataTypesTest, writeString) {
+    // Prepare a buffer with a reference data.
+    std::vector<uint8_t> buf_ref;
+    writeString("hello world!", buf_ref);
+    // Create empty buffer we will write to.
+    std::vector<uint8_t> buf;
+    ASSERT_NO_THROW(OptionDataTypeUtil::writeString("hello world!", buf));
+    // Compare two buffers.
+    ASSERT_EQ(buf_ref.size(), buf.size());
+    EXPECT_TRUE(std::equal(buf_ref.begin(), buf_ref.end(), buf.begin()));
+}
+
+} // anonymous namespace
diff --git a/src/lib/dhcp/tests/option_definition_unittest.cc b/src/lib/dhcp/tests/option_definition_unittest.cc
index 20c87d6..91b822c 100644
--- a/src/lib/dhcp/tests/option_definition_unittest.cc
+++ b/src/lib/dhcp/tests/option_definition_unittest.cc
@@ -23,6 +23,7 @@
 #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 <exceptions/exceptions.h>
 
@@ -883,7 +884,7 @@ TEST_F(OptionDefinitionTest, utf8StringTokenized) {
         option_v6 = opt_def.optionFactory(Option::V6, opt_code, values);
     );
     ASSERT_TRUE(option_v6);
-    ASSERT_TRUE(typeid(*option_v6) == typeid(Option));
+    ASSERT_TRUE(typeid(*option_v6) == typeid(OptionCustom));
     std::vector<uint8_t> data = option_v6->getData();
     std::vector<uint8_t> ref_data(values[0].c_str(), values[0].c_str()
                                   + values[0].length());
diff --git a/src/lib/dhcp/tests/option_unittest.cc b/src/lib/dhcp/tests/option_unittest.cc
index 50d143f..afa64d5 100644
--- a/src/lib/dhcp/tests/option_unittest.cc
+++ b/src/lib/dhcp/tests/option_unittest.cc
@@ -524,4 +524,27 @@ TEST_F(OptionTest, setData) {
     EXPECT_TRUE(0 == memcmp(&buf_[0], test_data + opt1->getHeaderLen(),
                             buf_.size()));
 }
+
+// This test verifies that options can be compared using equal() method.
+TEST_F(OptionTest, equal) {
+
+    // five options with varying lengths
+    OptionPtr opt1(new Option(Option::V6, 258, buf_.begin(), buf_.begin() + 1));
+    OptionPtr opt2(new Option(Option::V6, 258, buf_.begin(), buf_.begin() + 2));
+    OptionPtr opt3(new Option(Option::V6, 258, buf_.begin(), buf_.begin() + 3));
+
+    // the same content as opt2, but different type
+    OptionPtr opt4(new Option(Option::V6, 1, buf_.begin(), buf_.begin() + 2));
+
+    // another instance with the same type and content as opt2
+    OptionPtr opt5(new Option(Option::V6, 258, buf_.begin(), buf_.begin() + 2));
+
+    EXPECT_TRUE(opt1->equal(opt1));
+
+    EXPECT_FALSE(opt1->equal(opt2));
+    EXPECT_FALSE(opt1->equal(opt3));
+    EXPECT_FALSE(opt1->equal(opt4));
+
+    EXPECT_TRUE(opt2->equal(opt5));
+}
 }
diff --git a/src/lib/dhcp/tests/pkt4_unittest.cc b/src/lib/dhcp/tests/pkt4_unittest.cc
index 37af9c6..2ef982a 100644
--- a/src/lib/dhcp/tests/pkt4_unittest.cc
+++ b/src/lib/dhcp/tests/pkt4_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2012  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -416,6 +416,11 @@ TEST(Pkt4Test, sname) {
 
         delete pkt;
     }
+
+    // Check that a null argument generates an exception.
+    Pkt4 pkt4(DHCPOFFER, 1234);
+    EXPECT_THROW(pkt4.setSname(NULL, Pkt4::MAX_SNAME_LEN), InvalidParameter);
+    EXPECT_THROW(pkt4.setSname(NULL, 0), InvalidParameter);
 }
 
 TEST(Pkt4Test, file) {
@@ -451,6 +456,10 @@ TEST(Pkt4Test, file) {
         delete pkt;
     }
 
+    // Check that a null argument generates an exception.
+    Pkt4 pkt4(DHCPOFFER, 1234);
+    EXPECT_THROW(pkt4.setFile(NULL, Pkt4::MAX_FILE_LEN), InvalidParameter);
+    EXPECT_THROW(pkt4.setFile(NULL, 0), InvalidParameter);
 }
 
 static uint8_t v4Opts[] = {
diff --git a/src/lib/dhcp/tests/pkt6_unittest.cc b/src/lib/dhcp/tests/pkt6_unittest.cc
index b2dd16a..cdaad3b 100644
--- a/src/lib/dhcp/tests/pkt6_unittest.cc
+++ b/src/lib/dhcp/tests/pkt6_unittest.cc
@@ -170,6 +170,8 @@ TEST_F(Pkt6Test, packUnpack) {
     delete clone;
 }
 
+// This test verifies that options can be added (addOption()), retrieved
+// (getOption(), getOptions()) and deleted (delOption()).
 TEST_F(Pkt6Test, addGetDelOptions) {
     Pkt6* parent = new Pkt6(DHCPV6_SOLICIT, random() );
 
@@ -190,6 +192,25 @@ TEST_F(Pkt6Test, addGetDelOptions) {
     // now there are 2 options of type 2
     parent->addOption(opt3);
 
+    Option::OptionCollection options = parent->getOptions(2);
+    EXPECT_EQ(2, options.size()); // there should be 2 instances
+
+    // both options must be of type 2 and there must not be
+    // any other type returned
+    for (Option::OptionCollection::const_iterator x= options.begin();
+         x != options.end(); ++x) {
+        EXPECT_EQ(2, x->second->getType());
+    }
+
+    // Try to get a single option. Normally for singular options
+    // it is better to use getOption(), but getOptions() must work
+    // as well
+    options = parent->getOptions(1);
+    ASSERT_EQ(1, options.size());
+
+    EXPECT_EQ(1, (*options.begin()).second->getType());
+    EXPECT_EQ(opt1, options.begin()->second);
+
     // let's delete one of them
     EXPECT_EQ(true, parent->delOption(2));
 
@@ -205,6 +226,10 @@ TEST_F(Pkt6Test, addGetDelOptions) {
     // let's try to delete - should fail
     EXPECT_TRUE(false ==  parent->delOption(2));
 
+    // Finally try to get a non-existent option
+    options = parent->getOptions(1234);
+    EXPECT_EQ(0, options.size());
+
     delete parent;
 }
 
@@ -234,4 +259,52 @@ TEST_F(Pkt6Test, Timestamp) {
     EXPECT_TRUE(ts_period.length().total_microseconds() >= 0);
 }
 
+// This test verifies that getName() method returns proper
+// packet type names.
+TEST_F(Pkt6Test, getName) {
+    // Check all possible packet types
+    for (int itype = 0; itype < 256; ++itype) {
+        uint8_t type = itype;
+
+        switch (type) {
+        case DHCPV6_CONFIRM:
+            EXPECT_STREQ("CONFIRM", Pkt6::getName(type));
+            break;
+
+        case DHCPV6_DECLINE:
+            EXPECT_STREQ("DECLINE", Pkt6::getName(type));
+            break;
+
+        case DHCPV6_INFORMATION_REQUEST:
+            EXPECT_STREQ("INFORMATION_REQUEST",
+                         Pkt6::getName(type));
+            break;
+
+        case DHCPV6_REBIND:
+            EXPECT_STREQ("REBIND", Pkt6::getName(type));
+            break;
+
+        case DHCPV6_RELEASE:
+            EXPECT_STREQ("RELEASE", Pkt6::getName(type));
+            break;
+
+        case DHCPV6_RENEW:
+            EXPECT_STREQ("RENEW", Pkt6::getName(type));
+            break;
+
+        case DHCPV6_REQUEST:
+            EXPECT_STREQ("REQUEST", Pkt6::getName(type));
+            break;
+
+        case DHCPV6_SOLICIT:
+            EXPECT_STREQ("SOLICIT", Pkt6::getName(type));
+            break;
+
+        default:
+            EXPECT_STREQ("UNKNOWN", Pkt6::getName(type));
+        }
+    }
+}
+
+
 }
diff --git a/src/lib/dhcpsrv/Makefile.am b/src/lib/dhcpsrv/Makefile.am
index e205aee..6c08acd 100644
--- a/src/lib/dhcpsrv/Makefile.am
+++ b/src/lib/dhcpsrv/Makefile.am
@@ -48,5 +48,5 @@ libb10_dhcpsrv_la_CXXFLAGS += -Wno-unused-parameter
 endif
 
 # Distribute MySQL schema creation script and backend documentation
-EXTRA_DIST = dhcpdb_create.mysql database_backends.dox
+EXTRA_DIST = dhcpdb_create.mysql database_backends.dox libdhcpsrv.dox
 dist_pkgdata_DATA = dhcpdb_create.mysql
diff --git a/src/lib/dhcpsrv/addr_utilities.h b/src/lib/dhcpsrv/addr_utilities.h
index a1d856c..784aeab 100644
--- a/src/lib/dhcpsrv/addr_utilities.h
+++ b/src/lib/dhcpsrv/addr_utilities.h
@@ -26,8 +26,8 @@ namespace dhcp {
 
 /// @brief returns a first address in a given prefix
 ///
-/// Example: For 2001:db8:1::deaf:beef and length /120 the function will return
-/// 2001:db8:1::dead:be00. See also @ref lastAddrInPrefix.
+/// Example: For 2001:db8:1\::deaf:beef and length /120 the function will return
+/// 2001:db8:1\::dead:be00. See also @ref lastAddrInPrefix.
 ///
 /// @todo It currently works for v6 only and will throw if v4 address is passed.
 ///
@@ -40,8 +40,8 @@ isc::asiolink::IOAddress firstAddrInPrefix(const isc::asiolink::IOAddress& prefi
 
 /// @brief returns a last address in a given prefix
 ///
-/// Example: For 2001:db8:1::deaf:beef and length /112 the function will return
-/// 2001:db8:1::dead:ffff. See also @ref firstAddrInPrefix.
+/// Example: For 2001:db8:1\::deaf:beef and length /112 the function will return
+/// 2001:db8:1\::dead:ffff. See also @ref firstAddrInPrefix.
 ///
 /// @todo It currently works for v6 only and will throw if v4 address is passed.
 ///
diff --git a/src/lib/dhcpsrv/alloc_engine.cc b/src/lib/dhcpsrv/alloc_engine.cc
index 3a53887..77a2df4 100644
--- a/src/lib/dhcpsrv/alloc_engine.cc
+++ b/src/lib/dhcpsrv/alloc_engine.cc
@@ -67,7 +67,7 @@ AllocEngine::IterativeAllocator::pickAddress(const Subnet6Ptr& subnet,
 
     const Pool6Collection& pools = subnet->getPools();
 
-    if (pools.size() == 0) {
+    if (pools.empty()) {
         isc_throw(AllocFailed, "No pools defined in selected subnet");
     }
 
diff --git a/src/lib/dhcpsrv/cfgmgr.cc b/src/lib/dhcpsrv/cfgmgr.cc
index 3c46b13..7dc5f55 100644
--- a/src/lib/dhcpsrv/cfgmgr.cc
+++ b/src/lib/dhcpsrv/cfgmgr.cc
@@ -101,6 +101,14 @@ void CfgMgr::addSubnet4(const Subnet4Ptr& subnet) {
     subnets4_.push_back(subnet);
 }
 
+void CfgMgr::deleteSubnets4() {
+    subnets4_.clear();
+}
+
+void CfgMgr::deleteSubnets6() {
+    subnets6_.clear();
+}
+
 CfgMgr::CfgMgr() {
 }
 
diff --git a/src/lib/dhcpsrv/cfgmgr.h b/src/lib/dhcpsrv/cfgmgr.h
index 5bff64a..ac1b3f5 100644
--- a/src/lib/dhcpsrv/cfgmgr.h
+++ b/src/lib/dhcpsrv/cfgmgr.h
@@ -43,6 +43,7 @@ namespace dhcp {
 /// Below is a sketch of configuration inheritance (not implemented yet).
 /// Let's investigate the following configuration:
 ///
+/// @code
 /// preferred-lifetime 500;
 /// valid-lifetime 1000;
 /// subnet6 2001:db8:1::/48 {
@@ -52,6 +53,8 @@ namespace dhcp {
 ///     valid-lifetime 2000;
 ///     pool6 2001::db8:2::1 - 2001::db8:2::ff;
 /// };
+/// @endcode
+///
 /// Parameters defined in a global scope are applicable to everything until
 /// they are overwritten in a smaller scope, in this case subnet6.
 /// In the example above, the first subnet6 has preferred lifetime of 500s
@@ -101,9 +104,9 @@ public:
     /// needed is a dynamic server reconfiguration - a use case that is not
     /// planned to be supported any time soon.
 
-    /// @brief removes all subnets
+    /// @brief removes all IPv6 subnets
     ///
-    /// This method removes all existing subnets. It is used during
+    /// This method removes all existing IPv6 subnets. It is used during
     /// reconfiguration - old configuration is wiped and new definitions
     /// are used to recreate subnets.
     ///
@@ -111,9 +114,7 @@ public:
     /// between old and new configuration is tricky. For example: is
     /// 2000::/64 and 2000::/48 the same subnet or is it something
     /// completely new?
-    void deleteSubnets6() {
-        subnets6_.clear();
-    }
+    void deleteSubnets6();
 
     /// @brief get IPv4 subnet by address
     ///
@@ -130,7 +131,16 @@ public:
     void addSubnet4(const Subnet4Ptr& subnet);
 
     /// @brief removes all IPv4 subnets
-    void removeSubnets4();
+    ///
+    /// This method removes all existing IPv4 subnets. It is used during
+    /// reconfiguration - old configuration is wiped and new definitions
+    /// are used to recreate subnets.
+    ///
+    /// @todo Implement more intelligent approach. Note that comparison
+    /// between old and new configuration is tricky. For example: is
+    /// 192.0.2.0/23 and 192.0.2.0/24 the same subnet or is it something
+    /// completely new?
+    void deleteSubnets4();
 protected:
 
     /// @brief Protected constructor.
diff --git a/src/lib/dhcpsrv/database_backends.dox b/src/lib/dhcpsrv/database_backends.dox
index 8eeb5c5..a3b3b0c 100644
--- a/src/lib/dhcpsrv/database_backends.dox
+++ b/src/lib/dhcpsrv/database_backends.dox
@@ -1,5 +1,5 @@
 /**
-  @page dhcp-database-backends DHCP Database Back-Ends
+  @page dhcpDatabaseBackends DHCP Database Back-Ends
 
   All DHCP lease data is stored in some form of database, the interface
   to this being through the Lease Manager.
@@ -8,6 +8,17 @@
   the abstract isc::dhcp::LeaseMgr class.  This provides methods to
   create, retrieve, modify and delete leases in the database.
 
+  There are currently two available Lease Managers, MySQL and Memfile:
+
+  - The MySQL lease manager uses the freely available MySQL as its backend
+  database.  This is not included in BIND 10 DHCP by default:
+  the --with-dhcp-mysql switch must be supplied to "configure" for support
+  to be compiled into the software.
+  - Memfile is an in-memory lease database, with (currently) nothing being
+  written to persistent storage.  The long-term plans for the backend do
+  include the ability to store this on disk, but it is currently a
+  low-priority item.
+
   @section dhcpdb-instantiation Instantiation of Lease Managers
 
   A lease manager is instantiated through the LeaseMgrFactory class.  This
@@ -32,6 +43,8 @@
 
   - <b>type</b> - specifies the type of database backend.  The following values
   for the type keyword are supported:
+     - <B>memfile</b> - In-memory database.  Nothing is written to any
+       external storage, so this should not be used in production.
      - <b>mysql</b> - Use MySQL as the database
 
   The following sections list the database-specific keywords:
diff --git a/src/lib/dhcpsrv/dhcpdb_create.mysql b/src/lib/dhcpsrv/dhcpdb_create.mysql
index 7a292ec..695091d 100644
--- a/src/lib/dhcpsrv/dhcpdb_create.mysql
+++ b/src/lib/dhcpsrv/dhcpdb_create.mysql
@@ -34,7 +34,7 @@ CREATE TABLE lease4 (
     address INT UNSIGNED PRIMARY KEY NOT NULL,  # IPv4 address
     hwaddr VARBINARY(20),                       # Hardware address
     client_id VARBINARY(128),                   # Client ID
-    lease_time INT UNSIGNED,                    # Length of the lease (seconds)
+    valid_lifetime INT UNSIGNED,                # Length of the lease (seconds)
     expire TIMESTAMP,                           # Expiration time of the lease
     subnet_id INT UNSIGNED                      # Subnet identification
     ) ENGINE = INNODB;
@@ -43,7 +43,7 @@ CREATE TABLE lease4 (
 # N.B. The use of a VARCHAR for the address is temporary for development:
 # it will eventually be replaced by BINARY(16).
 CREATE TABLE lease6 (
-    address VARCHAR(40) PRIMARY KEY NOT NULL,   # IPv6 address
+    address VARCHAR(39) PRIMARY KEY NOT NULL,   # IPv6 address
     duid VARBINARY(128),                        # DUID
     valid_lifetime INT UNSIGNED,                # Length of the lease (seconds)
     expire TIMESTAMP,                           # Expiration time of the lease
@@ -72,12 +72,17 @@ COMMIT;
 # This table is only modified during schema upgrades.  For historical reasons
 # (related to the names of the columns in the BIND 10 DNS database file), the
 # first column is called "version" and not "major".
+#
+# NOTE: this MUST be kept in step with src/lib/dhcpsrv/tests/schema_copy.h,
+#       which defines the schema for the unit tests.  If you are updating
+#       the version number, the schema has changed: please ensure that
+#       schema_copy.h has been updated as well.
 CREATE TABLE schema_version (
     version INT PRIMARY KEY NOT NULL,       # Major version number
     minor INT                               # Minor version number
     );
 START TRANSACTION;
-INSERT INTO schema_version VALUES (0, 1);
+INSERT INTO schema_version VALUES (1, 0);
 COMMIT;
 
 # Notes:
diff --git a/src/lib/dhcpsrv/lease_mgr.cc b/src/lib/dhcpsrv/lease_mgr.cc
index 5f4dc2f..f7b6373 100644
--- a/src/lib/dhcpsrv/lease_mgr.cc
+++ b/src/lib/dhcpsrv/lease_mgr.cc
@@ -29,15 +29,16 @@
 
 using namespace std;
 
-using namespace isc::dhcp;
+namespace isc {
+namespace dhcp {
 
-Lease6::Lease6(LeaseType type, const isc::asiolink::IOAddress& addr, DuidPtr duid,
-               uint32_t iaid, uint32_t preferred, uint32_t valid, uint32_t t1,
-               uint32_t t2, SubnetID subnet_id, uint8_t prefixlen)
-    :type_(type), addr_(addr), prefixlen_(prefixlen), iaid_(iaid), duid_(duid),
-     preferred_lft_(preferred), valid_lft_(valid), t1_(t1), t2_(t2),
-     subnet_id_(subnet_id), fixed_(false), fqdn_fwd_(false),
-     fqdn_rev_(false) {
+Lease6::Lease6(LeaseType type, const isc::asiolink::IOAddress& addr,
+               DuidPtr duid, uint32_t iaid, uint32_t preferred, uint32_t valid,
+               uint32_t t1, uint32_t t2, SubnetID subnet_id, uint8_t prefixlen)
+    : addr_(addr), type_(type), prefixlen_(prefixlen), iaid_(iaid), duid_(duid),
+      preferred_lft_(preferred), valid_lft_(valid), t1_(t1), t2_(t2),
+      subnet_id_(subnet_id), fixed_(false), fqdn_fwd_(false),
+      fqdn_rev_(false) {
     if (!duid) {
         isc_throw(InvalidOperation, "DUID must be specified for a lease");
     }
@@ -83,16 +84,46 @@ Lease6::toText() {
 }
 
 bool
+Lease4::operator==(const Lease4& other) const {
+    return (
+        addr_ == other.addr_ &&
+        ext_ == other.ext_ &&
+        hwaddr_ == other.hwaddr_ &&
+        *client_id_ == *other.client_id_ &&
+        t1_ == other.t1_ &&
+        t2_ == other.t2_ &&
+        valid_lft_ == other.valid_lft_ &&
+        cltt_ == other.cltt_ &&
+        subnet_id_ == other.subnet_id_ &&
+        fixed_ == other.fixed_ &&
+        hostname_ == other.hostname_ &&
+        fqdn_fwd_ == other.fqdn_fwd_ &&
+        fqdn_rev_ == other.fqdn_rev_ &&
+        comments_ == other.comments_
+    );
+}
+
+bool
 Lease6::operator==(const Lease6& other) const {
     return (
-        type_ == other.type_ &&
         addr_ == other.addr_ &&
+        type_ == other.type_ &&
         prefixlen_ == other.prefixlen_ &&
         iaid_ == other.iaid_ &&
         *duid_ == *other.duid_ &&
         preferred_lft_ == other.preferred_lft_ &&
         valid_lft_ == other.valid_lft_ &&
+        t1_ == other.t1_ &&
+        t2_ == other.t2_ &&
         cltt_ == other.cltt_ &&
-        subnet_id_ == other.subnet_id_
-        );
+        subnet_id_ == other.subnet_id_ &&
+        fixed_ == other.fixed_ &&
+        hostname_ == other.hostname_ &&
+        fqdn_fwd_ == other.fqdn_fwd_ &&
+        fqdn_rev_ == other.fqdn_rev_ &&
+        comments_ == other.comments_
+    );
 }
+
+} // namespace isc::dhcp
+} // namespace isc
diff --git a/src/lib/dhcpsrv/lease_mgr.h b/src/lib/dhcpsrv/lease_mgr.h
index 0e1f8d8..7865e11 100644
--- a/src/lib/dhcpsrv/lease_mgr.h
+++ b/src/lib/dhcpsrv/lease_mgr.h
@@ -30,7 +30,7 @@
 #include <utility>
 #include <vector>
 
-/// @file dhcp/lease_mgr.h
+/// @file lease_mgr.h
 /// @brief An abstract API for lease database
 ///
 /// This file contains declarations of Lease4, Lease6 and LeaseMgr classes.
@@ -87,6 +87,13 @@ public:
         isc::Exception(file, line, what) {}
 };
 
+/// @brief Multiple lease records found where one expected
+class MultipleRecords : public Exception {
+public:
+    MultipleRecords(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
 /// @brief Attempt to update lease that was not there
 class NoSuchLease : public Exception {
 public:
@@ -94,6 +101,13 @@ public:
         isc::Exception(file, line, what) {}
 };
 
+/// @brief Data is truncated
+class DataTruncated : public Exception {
+public:
+    DataTruncated(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
 /// @brief Structure that holds a lease for IPv4 address
 ///
 /// For performance reasons it is a simple structure, not a class. If we chose
@@ -101,199 +115,264 @@ public:
 /// would be required. As this is a critical part of the code that will be used
 /// extensively, direct access is warranted.
 struct Lease4 {
+    /// @brief Maximum size of a hardware address
+    static const size_t HWADDR_MAX = 20;
+
+
     /// IPv4 address
     isc::asiolink::IOAddress addr_;
 
     /// @brief Address extension
     ///
-    /// It is envisaged that in some cases IPv4 address will be accompanied with some
-    /// additional data. One example of such use are Address + Port solutions (or
-    /// Port-restricted Addresses), where several clients may get the same address, but
-    /// different port ranges. This feature is not expected to be widely used.
-    /// Under normal circumstances, the value should be 0.
+    /// It is envisaged that in some cases IPv4 address will be accompanied
+    /// with some additional data. One example of such use are Address + Port
+    /// solutions (or Port-restricted Addresses), where several clients may get
+    /// the same address, but different port ranges. This feature is not
+    /// expected to be widely used.  Under normal circumstances, the value
+    /// should be 0.
     uint32_t ext_;
 
-    /// @brief hardware address
+    /// @brief Hardware address
     std::vector<uint8_t> hwaddr_;
 
-    /// @brief client identifier
-    boost::shared_ptr<ClientId> client_id_;
+    /// @brief Client identifier
+    ///
+    /// @todo Should this be a pointer to a client ID or the ID itself?
+    ///       Compare with the DUID in the Lease6 structure.
+    ClientIdPtr client_id_;
 
-    /// @brief renewal timer
+    /// @brief Renewal timer
     ///
-    /// Specifies renewal time. Although technically it is a property of IA container,
-    /// not the address itself, since our data model does not define separate IA
-    /// entity, we are keeping it in the lease. In case of multiple addresses/prefixes
-    /// for the same IA, each must have consistent T1 and T2 values. Specified in
-    /// seconds since cltt.
+    /// Specifies renewal time. Although technically it is a property of the
+    /// IA container and not the address itself, since our data model does not
+    /// define a separate IA entity, we are keeping it in the lease. In the
+    /// case of multiple addresses/prefixes for the same IA, each must have
+    /// consistent T1 and T2 values. This is specified in seconds since cltt.
     uint32_t t1_;
 
-    /// @brief rebinding timer
+    /// @brief Rebinding timer
     ///
-    /// Specifies rebinding time. Although technically it is a property of IA container,
-    /// not the address itself, since our data model does not define separate IA
-    /// entity, we are keeping it in the lease. In case of multiple addresses/prefixes
-    /// for the same IA, each must have consistent T1 and T2 values. Specified in
-    /// seconds since cltt.
+    /// Specifies rebinding time. Although technically it is a property of the
+    /// IA container and not the address itself, since our data model does not
+    /// define a separate IA entity, we are keeping it in the lease. In the
+    /// case of multiple addresses/prefixes for the same IA, each must have
+    /// consistent T1 and T2 values. This is specified in seconds since cltt.
     uint32_t t2_;
 
-    /// @brief valid lifetime
+    /// @brief Valid lifetime
     ///
-    /// Expressed as number of seconds since cltt
+    /// Expressed as number of seconds since cltt.
     uint32_t valid_lft_;
 
-    /// @brief client last transmission time
+    /// @brief Client last transmission time
     ///
-    /// Specifies a timestamp, when last transmission from a client was received.
+    /// Specifies a timestamp giving the time when the last transmission from a
+    /// client was received.
     time_t cltt_;
 
     /// @brief Subnet identifier
     ///
-    /// Specifies subnet-id of the subnet that the lease belongs to
+    /// Specifies the identification of the subnet to which the lease belongs.
     SubnetID subnet_id_;
 
-    /// @brief Is this a fixed lease?
+    /// @brief Fixed lease?
     ///
     /// Fixed leases are kept after they are released/expired.
     bool fixed_;
 
-    /// @brief client hostname
+    /// @brief Client hostname
     ///
     /// This field may be empty
     std::string hostname_;
 
-    /// @brief did we update AAAA record for this lease?
+    /// @brief Forward zone updated?
+    ///
+    /// Set true if the DNS AAAA record for this lease has been updated.
     bool fqdn_fwd_;
 
-    /// @brief did we update PTR record for this lease?
+    /// @brief Reverse zone updated?
+    ///
+    /// Set true if the DNS PTR record for this lease has been updated.
     bool fqdn_rev_;
 
-    /// @brief Lease comments.
+    /// @brief Lease comments
     ///
     /// Currently not used. It may be used for keeping comments made by the
     /// system administrator.
     std::string comments_;
 
-    /// @todo: Add DHCPv4 failover related fields here
-
     /// @brief Constructor
     ///
+    /// @param addr IPv4 address as unsigned 32-bit integer in network byte
+    ///        order.
+    /// @param hwaddr Hardware address buffer
+    /// @param hwaddr_len Length of hardware address buffer
+    /// @param clientid Client identification buffer
+    /// @param clientid_len Length of client identification buffer
+    /// @param valid_lft Lifetime of the lease
+    /// @param cltt Client last transmission time
+    /// @param subnet_id Subnet identification
+    Lease4(uint32_t addr, const uint8_t* hwaddr, size_t hwaddr_len,
+           const uint8_t* clientid, size_t clientid_len, uint32_t valid_lft,
+           time_t cltt, uint32_t subnet_id)
+        : addr_(addr), ext_(0), hwaddr_(hwaddr, hwaddr + hwaddr_len),
+          client_id_(new ClientId(clientid, clientid_len)), t1_(0), t2_(0),
+          valid_lft_(valid_lft), cltt_(cltt), subnet_id_(subnet_id),
+          fixed_(false), hostname_(), fqdn_fwd_(false), fqdn_rev_(false),
+          comments_()
+    {}
+
+    /// @brief Default Constructor
+    ///
     /// Initialize fields that don't have a default constructor.
-    /// @todo Remove this
-    Lease4() : addr_(0) {}
+    Lease4() : addr_(0), fixed_(false), fqdn_fwd_(false), fqdn_rev_(false)
+    {}
+
+    /// @brief Compare two leases for equality
+    ///
+    /// @param other lease6 object with which to compare
+    bool operator==(const Lease4& other) const;
+
+    /// @brief Compare two leases for inequality
+    ///
+    /// @param other lease6 object with which to compare
+    bool operator!=(const Lease4& other) const {
+        return (!operator==(other));
+    }
+
+    /// @todo: Add DHCPv4 failover related fields here
 };
 
 /// @brief Pointer to a Lease4 structure.
 typedef boost::shared_ptr<Lease4> Lease4Ptr;
 
 /// @brief A collection of IPv4 leases.
-typedef std::vector< boost::shared_ptr<Lease4Ptr> > Lease4Collection;
+typedef std::vector<Lease4Ptr> Lease4Collection;
+
+
 
 /// @brief Structure that holds a lease for IPv6 address and/or prefix
 ///
-/// For performance reasons it is a simple structure, not a class. Had we chose to
-/// make it a class, all fields would have to be made private and getters/setters
+/// For performance reasons it is a simple structure, not a class. If we chose
+/// make it a class, all fields would have to made private and getters/setters
 /// would be required. As this is a critical part of the code that will be used
-/// extensively, direct access rather than through getters/setters is warranted.
+/// extensively, direct access is warranted.
 struct Lease6 {
+
+    /// @brief Type of lease contents
     typedef enum {
         LEASE_IA_NA, /// the lease contains non-temporary IPv6 address
         LEASE_IA_TA, /// the lease contains temporary IPv6 address
         LEASE_IA_PD  /// the lease contains IPv6 prefix (for prefix delegation)
     } LeaseType;
 
-    Lease6(LeaseType type, const isc::asiolink::IOAddress& addr, DuidPtr duid,
-           uint32_t iaid, uint32_t preferred, uint32_t valid, uint32_t t1,
-           uint32_t t2, SubnetID subnet_id, uint8_t prefixlen_ = 0);
+    /// @brief IPv6 address
+    ///
+    /// IPv6 address or, in the case of a prefix delegation, the prefix.
+    isc::asiolink::IOAddress addr_;
 
-    /// @brief specifies lease type (normal addr, temporary addr, prefix)
+    /// @brief Lease type
+    ///
+    /// One of normal address, temporary address, or prefix.
     LeaseType type_;
 
-    /// IPv6 address
-    isc::asiolink::IOAddress addr_;
-
-    /// IPv6 prefix length (used only for PD)
+    /// @brief IPv6 prefix length
+    ///
+    /// This is used only for prefix delegations and is ignored otherwise.
     uint8_t prefixlen_;
 
-    /// @brief IAID
+    /// @brief Identity Association Identifier (IAID)
     ///
-    /// Identity Association IDentifier. DHCPv6 stores all addresses and prefixes
-    /// in IA containers (IA_NA, IA_TA, IA_PD). Most containers may appear more
-    /// than once in a message. To differentiate between them, IAID field is present
+    /// DHCPv6 stores all addresses and prefixes in IA containers (IA_NA,
+    /// IA_TA, IA_PD). All containers may appear more than once in a message.
+    /// To differentiate between them, the IAID field is present
     uint32_t iaid_;
 
-    /// @brief client identifier
-    boost::shared_ptr<DUID> duid_;
+    /// @brief Client identifier
+    DuidPtr duid_;
 
     /// @brief preferred lifetime
     ///
-    /// This parameter specifies preferred lifetime since the lease was assigned/renewed
-    /// (cltt), expressed in seconds.
+    /// This parameter specifies the preferred lifetime since the lease was
+    /// assigned or renewed (cltt), expressed in seconds.
     uint32_t preferred_lft_;
 
     /// @brief valid lifetime
     ///
-    /// This parameter specified valid lifetime since the lease was assigned/renewed
-    /// (cltt), expressed in seconds.
+    /// This parameter specifies the valid lifetime since the lease waa
+    /// assigned/renewed (cltt), expressed in seconds.
     uint32_t valid_lft_;
 
     /// @brief T1 timer
     ///
-    /// Specifies renewal time. Although technically it is a property of IA container,
-    /// not the address itself, since our data model does not define separate IA
-    /// entity, we are keeping it in the lease. In case of multiple addresses/prefixes
-    /// for the same IA, each must have consistent T1 and T2 values. Specified in
-    /// seconds since cltt.
-    /// This value will also be useful for failover to calculate the next expected
-    /// client transmission time.
+    /// Specifies renewal time. Although technically it is a property of the
+    /// IA container and not the address itself, since our data model does not
+    /// define a separate IA entity, we are keeping it in the lease. In the
+    /// case of multiple addresses/prefixes for the same IA, each must have
+    /// consistent T1 and T2 values. This is specified in seconds since cltt.
+    /// The value will also be useful for failover to calculate the next
+    /// expected client transmission time.
     uint32_t t1_;
 
     /// @brief T2 timer
     ///
-    /// Specifies rebinding time. Although technically it is a property of IA container,
-    /// not the address itself, since our data model does not define separate IA
-    /// entity, we are keeping it in the lease. In case of multiple addresses/prefixes
-    /// for the same IA, each must have consistent T1 and T2 values. Specified in
-    /// seconds since cltt.
+    /// Specifies rebinding time. Although technically it is a property of the
+    /// IA container and not the address itself, since our data model does not
+    /// define a separate IA entity, we are keeping it in the lease. In the
+    /// case of multiple addresses/prefixes for the same IA, each must have
+    /// consistent T1 and T2 values. This is specified in seconds since cltt.
     uint32_t t2_;
 
-    /// @brief client last transmission time
+    /// @brief Client last transmission time
     ///
-    /// Specifies a timestamp, when last transmission from a client was received.
+    /// Specifies a timestamp giving the time when the last transmission from a
+    /// client was received.
     time_t cltt_;
 
     /// @brief Subnet identifier
     ///
-    /// Specifies subnet-id of the subnet that the lease belongs to
+    /// Specifies the identification of the subnet to which the lease belongs.
     SubnetID subnet_id_;
 
-    /// @brief Is this a fixed lease?
+    /// @brief Fixed lease?
     ///
     /// Fixed leases are kept after they are released/expired.
     bool fixed_;
 
-    /// @brief client hostname
+    /// @brief Client hostname
     ///
     /// This field may be empty
     std::string hostname_;
 
-    /// @brief did we update AAAA record for this lease?
+    /// @brief Forward zone updated?
+    ///
+    /// Set true if the DNS AAAA record for this lease has been updated.
     bool fqdn_fwd_;
 
-    /// @brief did we update PTR record for this lease?
+    /// @brief Reverse zone updated?
+    ///
+    /// Set true if the DNS PTR record for this lease has been updated.
     bool fqdn_rev_;
 
     /// @brief Lease comments
     ///
-    /// This field is currently not used.
+    /// Currently not used. It may be used for keeping comments made by the
+    /// system administrator.
     std::string comments_;
 
     /// @todo: Add DHCPv6 failover related fields here
 
     /// @brief Constructor
+    Lease6(LeaseType type, const isc::asiolink::IOAddress& addr, DuidPtr duid,
+           uint32_t iaid, uint32_t preferred, uint32_t valid, uint32_t t1,
+           uint32_t t2, SubnetID subnet_id, uint8_t prefixlen_ = 0);
+
+    /// @brief Constructor
     ///
     /// Initialize fields that don't have a default constructor.
-    Lease6() : addr_("::") {}
+    Lease6() : addr_("::"), type_(LEASE_IA_NA), fixed_(false), fqdn_fwd_(false),
+               fqdn_rev_(false)
+    {}
 
     /// @brief Convert Lease6 to Printable Form
     ///
@@ -311,13 +390,12 @@ struct Lease6 {
     bool operator!=(const Lease6& other) const {
         return (!operator==(other));
     }
-
 };
 
 /// @brief Pointer to a Lease6 structure.
 typedef boost::shared_ptr<Lease6> Lease6Ptr;
 
-/// @brief Const pointer to a Lease6 structure.
+/// @brief Pointer to a const Lease6 structure.
 typedef boost::shared_ptr<const Lease6> ConstLease6Ptr;
 
 /// @brief A collection of IPv6 leases.
@@ -335,7 +413,7 @@ typedef std::vector<Lease6Ptr> Lease6Collection;
 /// see the documentation of those classes for details.
 class LeaseMgr {
 public:
-    /// Client Hardware address
+    /// Client hardware address
     typedef std::vector<uint8_t> HWAddr;
 
     /// Database configuration parameter map
@@ -368,19 +446,6 @@ public:
     ///         with the same address was already there).
     virtual bool addLease(const Lease6Ptr& lease) = 0;
 
-    /// @brief Returns IPv4 lease for specified IPv4 address and subnet_id
-    ///
-    /// This method is used to get a lease for specific subnet_id. There can be
-    /// at most one lease for any given subnet, so this method returns a single
-    /// pointer.
-    ///
-    /// @param addr address of the searched lease
-    /// @param subnet_id ID of the subnet the lease must belong to
-    ///
-    /// @return smart pointer to the lease (or NULL if a lease is not found)
-    virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress& addr,
-                                SubnetID subnet_id) const = 0;
-
     /// @brief Returns an IPv4 lease for specified IPv4 address
     ///
     /// This method return a lease that is associated with a given address.
@@ -493,17 +558,11 @@ public:
 
     /// @brief Deletes a lease.
     ///
-    /// @param addr IPv4 address of the lease to be deleted.
-    ///
-    /// @return true if deletion was successful, false if no such lease exists
-    virtual bool deleteLease4(const isc::asiolink::IOAddress& addr) = 0;
-
-    /// @brief Deletes a lease.
-    ///
-    /// @param addr IPv6 address of the lease to be deleted.
+    /// @param addr Address of the lease to be deleted. (This can be IPv4 or
+    ///        IPv6.)
     ///
     /// @return true if deletion was successful, false if no such lease exists
-    virtual bool deleteLease6(const isc::asiolink::IOAddress& addr) = 0;
+    virtual bool deleteLease(const isc::asiolink::IOAddress& addr) = 0;
 
     /// @brief Return backend type
     ///
diff --git a/src/lib/dhcpsrv/libdhcpsrv.dox b/src/lib/dhcpsrv/libdhcpsrv.dox
new file mode 100644
index 0000000..bb4a8ec
--- /dev/null
+++ b/src/lib/dhcpsrv/libdhcpsrv.dox
@@ -0,0 +1,86 @@
+/**
+ @page libdhcpsrv libdhcpsrv - Server DHCP library
+
+This library contains code useful for DHCPv4 and DHCPv6 server operations, like
+Lease Manager that stores leases information, configuration manager that stores
+configuration etc. The code here is server specific. For generic (useful in
+server, client, relay and other tools like perfdhcp) code, please see
+\ref libdhcp.
+
+This library contains several crucial elements of the DHCP server operation:
+
+- isc::dhcp::LeaseMgr - Lease Manager is a name for database backend that stores
+  leases.
+- isc::dhcp::CfgMgr - Configuration Manager that holds DHCP specific
+  configuration information (subnets, pools, options, timer values etc.) in
+  easy to use format.
+- AllocEngine - allocation engine that handles new requestes and allocates new
+  leases.
+
+ at section leasemgr Lease Manager
+
+LeaseMgr provides a common, unified abstract API for all database backends. All
+backends are derived from the base class isc::dhcp::LeaseMgr. Currently the
+only available backend is MySQL (see \ref isc::dhcp::MySqlLeaseMgr).
+
+ at section cfgmgr Configuration Manager
+
+Configuration Manager (\ref isc::dhcp::CfgMgr) stores configuration information
+necessary for DHCPv4 and DHCPv6 server operation. In particular, it stores
+subnets (\ref isc::dhcp::Subnet4 and \ref isc::dhcp::Subnet6) together with
+their pools (\ref isc::dhcp::Pool4 and \ref isc::dhcp::Pool6), options and
+other information specified by the used in BIND10 configuration.
+
+ at section allocengine Allocation Engine
+
+Allocation Engine (\ref isc::dhcp::AllocEngine) is what its name say - an engine
+that handles allocation of new leases. It takes parameters that the client
+provided (client-id, DUID, subnet, a hint if the user provided one, etc.) and
+then attempts to allocate a lease.
+
+There is no single best soluction to the address assignment problem. Server
+is expected to pick an address from its available pools is currently not used.
+There are many possible algorithms that can do that, each with its own advantages
+and drawbacks. This allocation engine must provide robust operation is radically
+different scenarios, so there address selection problem was abstracted into
+separate module, called allocator. Its sole purpose is to pick an address from
+a pool. Allocation engine will then check if the picked address is free and if
+it is not, then will ask allocator to pick again.
+
+At lease 3 allocators will be implemented:
+
+- Iterative - it iterates over all addresses in available pools, one
+by one. The advantages of this approach are speed (typically it only needs to
+increase last address), the guarantee to cover all addresses and predictability.
+This allocator behaves very good in case of nearing depletion. Even when pools
+are almost completely allocated, it still will be able to allocate outstanding
+leases efficiently. Predictability can also be considered a serious flaw in
+some environments, as prediction of the next address is trivial and can be
+leveraged by an attacker. Another drawback of this allocator is that it does
+not attempt to give the same address to returning clients (clients that released
+or expired their leases and are requesting a new lease will likely to get a 
+different lease). This allocator is implemented in \ref isc::dhcp::AllocEngine::IterativeAllocator.
+
+- Hashed - ISC-DHCP uses hash of the client-id or DUID to determine, which
+address is tried first. If that address is not available, the result is hashed
+again. That procedure is repeated until available address is found or there
+are no more addresses left. The benefit of that approach is that it provides
+a relative lease stability, so returning old clients are likely to get the same
+address again. The drawbacks are increased computation cost, as each iteration
+requires use of a hashing function. That is especially difficult when the 
+pools are almost depleted. It also may be difficult to guarantee that the
+repeated hashing will iterate over all available addresses in all pools. Flawed
+hash algorithm can go into cycles that iterate over only part of the addresses.
+It is difficult to detect such issues as only some initial seed (client-id
+or DUID) values may trigger short cycles. This allocator is currently not
+implemented.
+
+- Random - Another possible approach to address selection is randomization. This
+allocator can pick an address randomly from the configured pool. The benefit
+of this approach is that it is easy to implement and makes attacks based on
+address prediction more difficult. The drawback of this approach is that
+returning clients are almost guaranteed to get a different address. Another
+drawback is that with almost depleted pools it is increasingly difficult to
+"guess" an address that is free. This allocator is currently not implemented.
+
+*/
\ No newline at end of file
diff --git a/src/lib/dhcpsrv/memfile_lease_mgr.cc b/src/lib/dhcpsrv/memfile_lease_mgr.cc
index 8a59b73..b71b166 100644
--- a/src/lib/dhcpsrv/memfile_lease_mgr.cc
+++ b/src/lib/dhcpsrv/memfile_lease_mgr.cc
@@ -49,11 +49,6 @@ Lease4Collection Memfile_LeaseMgr::getLease4(const HWAddr& ) const {
     return (Lease4Collection());
 }
 
-Lease4Ptr Memfile_LeaseMgr::getLease4(const isc::asiolink::IOAddress&,
-                                      SubnetID) const {
-    return (Lease4Ptr());
-}
-
 Lease4Ptr Memfile_LeaseMgr::getLease4(const HWAddr&,
                                       SubnetID) const {
     return (Lease4Ptr());
@@ -102,18 +97,21 @@ void Memfile_LeaseMgr::updateLease6(const Lease6Ptr& ) {
 
 }
 
-bool Memfile_LeaseMgr::deleteLease4(const isc::asiolink::IOAddress&) {
-    return (false);
-}
-
-bool Memfile_LeaseMgr::deleteLease6(const isc::asiolink::IOAddress& addr) {
-    Lease6Storage::iterator l = storage6_.find(addr);
-    if (l == storage6_.end()) {
-        // no such lease
+bool Memfile_LeaseMgr::deleteLease(const isc::asiolink::IOAddress& addr) {
+    if (addr.isV4()) {
+        // V4 not implemented yet
         return (false);
+
     } else {
-        storage6_.erase(l);
-        return (true);
+        // V6 lease
+        Lease6Storage::iterator l = storage6_.find(addr);
+        if (l == storage6_.end()) {
+            // No such lease
+            return (false);
+        } else {
+            storage6_.erase(l);
+            return (true);
+        }
     }
 }
 
diff --git a/src/lib/dhcpsrv/memfile_lease_mgr.h b/src/lib/dhcpsrv/memfile_lease_mgr.h
index d9b40e5..268b722 100644
--- a/src/lib/dhcpsrv/memfile_lease_mgr.h
+++ b/src/lib/dhcpsrv/memfile_lease_mgr.h
@@ -68,16 +68,6 @@ public:
     /// @return a collection of leases
     virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress& addr) const;
 
-    /// @brief Returns existing IPv4 lease for specific address and subnet
-    ///
-    /// @todo Not implemented yet
-    /// @param addr address of the searched lease
-    /// @param subnet_id ID of the subnet the lease must belong to
-    ///
-    /// @return smart pointer to the lease (or NULL if a lease is not found)
-    virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress& addr,
-                                SubnetID subnet_id) const;
-
     /// @brief Returns existing IPv4 leases for specified hardware address.
     ///
     /// @todo Not implemented yet
@@ -176,17 +166,11 @@ public:
 
     /// @brief Deletes a lease.
     ///
-    /// @param addr IPv4 address of the lease to be deleted.
-    ///
-    /// @return true if deletion was successful, false if no such lease exists
-    virtual bool deleteLease4(const isc::asiolink::IOAddress& addr);
-
-    /// @brief Deletes a lease.
-    ///
-    /// @param addr IPv4 address of the lease to be deleted.
+    /// @param addr Address of the lease to be deleted. (This can be IPv4 or
+    ///        IPv6.)
     ///
     /// @return true if deletion was successful, false if no such lease exists
-    virtual bool deleteLease6(const isc::asiolink::IOAddress& addr);
+    virtual bool deleteLease(const isc::asiolink::IOAddress& addr);
 
     /// @brief Return backend type
     ///
@@ -199,12 +183,11 @@ public:
 
     /// @brief Returns backend name.
     ///
-    /// As there is no variation, in this case we return the type of the
-    /// backend.
+    /// For now, memfile can only store data in memory.
     ///
     /// @return Name of the backend.
     virtual std::string getName() const {
-        return ("memfile");
+        return ("memory");
     }
 
     /// @brief Returns description of the backend.
diff --git a/src/lib/dhcpsrv/mysql_lease_mgr.cc b/src/lib/dhcpsrv/mysql_lease_mgr.cc
index a3387ac..ad6e66c 100644
--- a/src/lib/dhcpsrv/mysql_lease_mgr.cc
+++ b/src/lib/dhcpsrv/mysql_lease_mgr.cc
@@ -15,10 +15,13 @@
 #include <config.h>
 
 #include <asiolink/io_address.h>
+#include <dhcp/duid.h>
 #include <dhcpsrv/mysql_lease_mgr.h>
 
+#include <boost/static_assert.hpp>
 #include <mysql/mysqld_error.h>
 
+#include <algorithm>
 #include <iostream>
 #include <iomanip>
 #include <string>
@@ -28,35 +31,122 @@ using namespace isc;
 using namespace isc::dhcp;
 using namespace std;
 
+/// @file
+///
+/// This file holds the implementation of the Lease Manager using MySQL.  The
+/// implementation uses MySQL's C API, as it comes as standard with the MySQL
+/// client libraries.
+///
+/// In general, each of the database access methods corresponds to one SQL
+/// statement.  To avoid the overhead of parsing a statement every time it is
+/// used, when the database is opened "prepared statements" are created -
+/// essentially doing the SQL parsing up front.  Every time a method is used
+/// to access data, the corresponding prepared statement is referenced. Each
+/// prepared statement contains a set of placeholders for data, each
+/// placeholder being for:
+///
+/// - data being added to the database (as in adding or updating a lease)
+/// - data being retrieved from the database (as in getting lease information)
+/// - selection criteria used to determine which records to update/retrieve.
+///
+/// All such data is associated with the prepared statment using an array of
+/// MYSQL_BIND structures.  Each element in the array corresponds to one
+/// parameter in the prepared statement - the first element in the array is
+/// associated with the first parameter, the second element with the second
+/// parameter etc.
+///
+/// Within this file, the setting up of the MYSQL_BIND arrays for data being
+/// passed to and retrieved from the database is handled in the
+/// isc::dhcp::MySqlLease4Exchange and isc::dhcp::MySqlLease6Exchange classes.
+/// The classes also hold intermediate variables required for exchanging some
+/// of the data.
+///
+/// With these exchange objects in place, many of the methods follow similar
+/// logic:
+/// - Set up the MYSQL_BIND array for data being transferred to/from the
+///   database.  For data being transferred to the database, some of the
+///   data is extracted from the lease to intermediate variables, whilst
+///   in other cases the MYSQL_BIND arrays point to the data in the lease.
+/// - Set up the MYSQL_BIND array for the data selection parameters.
+/// - Bind these arrays to the prepared statement.
+/// - Execute the statement.
+/// - If there is output, copy the data from the bound variables to the output
+///   lease object.
+
 namespace {
 ///@{
-/// @brief Maximum Size of Database Fields
+
+/// @brief Maximum size of database fields
 ///
 /// The following constants define buffer sizes for variable length database
 /// fields.  The values should be greater than or equal to the length set in
 /// the schema definition.
 ///
-/// The exception is the length of any VARCHAR fields: these should be set
-/// greater than or equal to the length of the field plus 2: this allows for
-/// the insertion of a trailing null regardless of whether the data returned
-/// contains a trailing null (the documentation is not clear on this point).
+/// The exception is the length of any VARCHAR fields: buffers for these should
+/// be set greater than or equal to the length of the field plus 1: this allows
+/// for the insertion of a trailing null whatever data is returned.
+
+/// @brief Maximum size of an IPv6 address represented as a text string.
+///
+/// This is 32 hexadecimal characters written in 8 groups of four, plus seven
+/// colon separators.
+const size_t ADDRESS6_TEXT_MAX_LEN = 39;
+
+/// @brief Maximum size of a hardware address.
+const size_t HWADDR_MAX_LEN = 20;
+
+/// @brief MySQL True/False constants
+///
+/// Declare typed values so as to avoid problems of data conversion.  These
+/// are local to the file but are given the prefix MLM (MySql Lease Manager) to
+/// avoid any likely conflicts with variables in header files named TRUE or
+/// FALSE.
+
+const my_bool MLM_FALSE = 0;                ///< False value
+const my_bool MLM_TRUE = 1;                 ///< True value
 
-const size_t ADDRESS6_TEXT_MAX_LEN = 42;    // Max size of a IPv6 text buffer
-const size_t DUID_MAX_LEN = 128;            // Max size of a DUID
 ///@}
 
 /// @brief MySQL Selection Statements
 ///
-/// Each statement is associated with the index, used by the various methods
-/// to access the prepared statement.
+/// Each statement is associated with an index, which is used to reference the
+/// associated prepared statement.
+
 struct TaggedStatement {
     MySqlLeaseMgr::StatementIndex index;
     const char*                   text;
 };
 
 TaggedStatement tagged_statements[] = {
+    {MySqlLeaseMgr::DELETE_LEASE4,
+                    "DELETE FROM lease4 WHERE address = ?"},
     {MySqlLeaseMgr::DELETE_LEASE6,
                     "DELETE FROM lease6 WHERE address = ?"},
+    {MySqlLeaseMgr::GET_LEASE4_ADDR,
+                    "SELECT address, hwaddr, client_id, "
+                        "valid_lifetime, expire, subnet_id "
+                            "FROM lease4 "
+                            "WHERE address = ?"},
+    {MySqlLeaseMgr::GET_LEASE4_CLIENTID,
+                    "SELECT address, hwaddr, client_id, "
+                        "valid_lifetime, expire, subnet_id "
+                            "FROM lease4 "
+                            "WHERE client_id = ?"},
+    {MySqlLeaseMgr::GET_LEASE4_CLIENTID_SUBID,
+                    "SELECT address, hwaddr, client_id, "
+                        "valid_lifetime, expire, subnet_id "
+                            "FROM lease4 "
+                            "WHERE client_id = ? AND subnet_id = ?"},
+    {MySqlLeaseMgr::GET_LEASE4_HWADDR,
+                    "SELECT address, hwaddr, client_id, "
+                        "valid_lifetime, expire, subnet_id "
+                            "FROM lease4 "
+                            "WHERE hwaddr = ?"},
+    {MySqlLeaseMgr::GET_LEASE4_HWADDR_SUBID,
+                    "SELECT address, hwaddr, client_id, "
+                        "valid_lifetime, expire, subnet_id "
+                            "FROM lease4 "
+                            "WHERE hwaddr = ? AND subnet_id = ?"},
     {MySqlLeaseMgr::GET_LEASE6_ADDR,
                     "SELECT address, duid, valid_lifetime, "
                         "expire, subnet_id, pref_lifetime, "
@@ -77,11 +167,20 @@ TaggedStatement tagged_statements[] = {
                             "WHERE duid = ? AND iaid = ? AND subnet_id = ?"},
     {MySqlLeaseMgr::GET_VERSION,
                     "SELECT version, minor FROM schema_version"},
+    {MySqlLeaseMgr::INSERT_LEASE4,
+                    "INSERT INTO lease4(address, hwaddr, client_id, "
+                        "valid_lifetime, expire, subnet_id) "
+                            "VALUES (?, ?, ?, ?, ?, ?)"},
     {MySqlLeaseMgr::INSERT_LEASE6,
                     "INSERT INTO lease6(address, duid, valid_lifetime, "
                         "expire, subnet_id, pref_lifetime, "
                         "lease_type, iaid, prefix_len) "
                             "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"},
+    {MySqlLeaseMgr::UPDATE_LEASE4,
+                    "UPDATE lease4 SET address = ?, hwaddr = ?, "
+                        "client_id = ?, valid_lifetime = ?, expire = ?, "
+                        "subnet_id = ? "
+                            "WHERE address = ?"},
     {MySqlLeaseMgr::UPDATE_LEASE6,
                     "UPDATE lease6 SET address = ?, duid = ?, "
                         "valid_lifetime = ?, expire = ?, subnet_id = ?, "
@@ -97,6 +196,300 @@ TaggedStatement tagged_statements[] = {
 namespace isc {
 namespace dhcp {
 
+/// @brief Common MySQL and Lease Data Methods
+///
+/// The MySqlLease4Exchange and MySqlLease6Exchange classes provide the
+/// functionaility to set up binding information between variables in the
+/// program and data extracted from the database.  This class is the common
+/// base to both of them, containing some common methods.
+
+class MySqlLeaseExchange {
+public:
+    /// @brief Set error indicators
+    ///
+    /// Sets the error indicator for each of the MYSQL_BIND elements.  It points
+    /// the "error" field within an element of the input array to the
+    /// corresponding element of the passed error array.
+    ///
+    /// @param bind Array of BIND elements
+    /// @param error Array of error elements.  If there is an error in getting
+    ///        data associated with one of the "bind" elements, the
+    ///        corresponding element in the error array is set to MLM_TRUE.
+    /// @param count Size of each of the arrays.
+    void setErrorIndicators(MYSQL_BIND* bind, my_bool* error, size_t count) {
+        for (size_t i = 0; i < count; ++i) {
+            error[i] = MLM_FALSE;
+            bind[i].error = reinterpret_cast<char*>(&error[i]);
+        }
+    }
+
+    /// @brief Return columns in error
+    ///
+    /// If an error is returned from a fetch (in particular, a truncated
+    /// status), this method can be called to get the names of the fields in
+    /// error.  It returns a string comprising the names of the fields
+    /// separated by commas.  In the case of there being no error indicators
+    /// set, it returns the string "(None)".
+    ///
+    /// @param error Array of error elements.  An element is set to MLM_TRUE
+    ///        if the corresponding column in the database is the source of
+    ///        the error.
+    /// @param names Array of column names, the same size as the error array.
+    /// @param count Size of each of the arrays.
+    std::string getColumnsInError(my_bool* error, std::string* names,
+                                  size_t count) {
+        std::string result = "";
+
+        // Accumulate list of column names
+        for (size_t i = 0; i < count; ++i) {
+            if (error[i] == MLM_TRUE) {
+                if (!result.empty()) {
+                    result += ", ";
+                }
+                result += names[i];
+            }
+        }
+
+        if (result.empty()) {
+            result = "(None)";
+        }
+
+        return (result);
+    }
+};
+
+
+/// @brief Exchange MySQL and Lease4 Data
+///
+/// On any MySQL operation, arrays of MYSQL_BIND structures must be built to
+/// describe the parameters in the prepared statements.  Where information is
+/// inserted or retrieved - INSERT, UPDATE, SELECT - a large amount of that
+/// structure is identical.  This class handles the creation of that array.
+///
+/// Owing to the MySQL API, the process requires some intermediate variables
+/// to hold things like data length etc.  This object holds those variables.
+///
+/// @note There are no unit tests for this class.  It is tested indirectly
+/// in all MySqlLeaseMgr::xxx4() calls where it is used.
+
+class MySqlLease4Exchange : public MySqlLeaseExchange {
+    /// @brief Set number of database columns for this lease structure
+    static const size_t LEASE_COLUMNS = 6;
+
+public:
+    /// @brief Constructor
+    ///
+    /// The initialization of the variables here is only to satisfy cppcheck -
+    /// all variables are initialized/set in the methods before they are used.
+    MySqlLease4Exchange() : addr4_(0), hwaddr_length_(0), client_id_length_(0) {
+        memset(hwaddr_buffer_, 0, sizeof(hwaddr_buffer_));
+        memset(client_id_buffer_, 0, sizeof(client_id_buffer_));
+        std::fill(&error_[0], &error_[LEASE_COLUMNS], MLM_FALSE);
+ 
+        // Set the column names (for error messages)
+        columns_[0] = "address";
+        columns_[1] = "hwaddr";
+        columns_[2] = "client_id";
+        columns_[3] = "valid_lifetime";
+        columns_[4] = "expire";
+        columns_[5] = "subnet_id";
+        BOOST_STATIC_ASSERT(5 < LEASE_COLUMNS);
+    }
+
+    /// @brief Create MYSQL_BIND objects for Lease4 Pointer
+    ///
+    /// Fills in the MYSQL_BIND array for sending data in the Lease4 object to
+    /// the database.
+    ///
+    /// @param lease Lease object to be added to the database.  None of the
+    ///        fields in the lease are modified - the lease data is only read.
+    ///
+    /// @return Vector of MySQL BIND objects representing the data to be added.
+    std::vector<MYSQL_BIND> createBindForSend(const Lease4Ptr& lease) {
+
+        // Store lease object to ensure it remains valid.
+        lease_ = lease;
+
+        // Initialize prior to constructing the array of MYSQL_BIND structures.
+        memset(bind_, 0, sizeof(bind_));
+
+        // Set up the structures for the various components of the lease4
+        // structure.
+
+        // Address: uint32_t
+        // The address in the Lease structure is an IOAddress object.  Convert
+        // this to an integer for storage.
+        addr4_ = static_cast<uint32_t>(lease_->addr_);
+        bind_[0].buffer_type = MYSQL_TYPE_LONG;
+        bind_[0].buffer = reinterpret_cast<char*>(&addr4_);
+        bind_[0].is_unsigned = MLM_TRUE;
+
+        // hwaddr: varbinary(128)
+        // For speed, we avoid copying the data into temporary storage and
+        // instead extract it from the lease structure directly.
+        hwaddr_length_ = lease_->hwaddr_.size();
+        bind_[1].buffer_type = MYSQL_TYPE_BLOB;
+        bind_[1].buffer = reinterpret_cast<char*>(&(lease_->hwaddr_[0]));
+        bind_[1].buffer_length = hwaddr_length_;
+        bind_[1].length = &hwaddr_length_;
+
+        // client_id: varbinary(128)
+        client_id_ = lease_->client_id_->getClientId();
+        client_id_length_ = client_id_.size();
+        bind_[2].buffer_type = MYSQL_TYPE_BLOB;
+        bind_[2].buffer = reinterpret_cast<char*>(&client_id_[0]);
+        bind_[2].buffer_length = client_id_length_;
+        bind_[2].length = &client_id_length_;
+
+        // valid lifetime: unsigned int
+        bind_[3].buffer_type = MYSQL_TYPE_LONG;
+        bind_[3].buffer = reinterpret_cast<char*>(&lease_->valid_lft_);
+        bind_[3].is_unsigned = MLM_TRUE;
+
+        // expire: timestamp
+        // The lease structure holds the client last transmission time (cltt_)
+        // For convenience for external tools, this is converted to lease
+        // expiry time (expire).  The relationship is given by:
+        //
+        // expire = cltt_ + valid_lft_
+        //
+        // @todo Handle overflows - a large enough valid_lft_ could cause
+        //       an overflow on a 32-bit system.
+        MySqlLeaseMgr::convertToDatabaseTime(lease_->cltt_, lease_->valid_lft_,
+                                             expire_);
+        bind_[4].buffer_type = MYSQL_TYPE_TIMESTAMP;
+        bind_[4].buffer = reinterpret_cast<char*>(&expire_);
+        bind_[4].buffer_length = sizeof(expire_);
+
+        // subnet_id: unsigned int
+        // Can use lease_->subnet_id_ directly as it is of type uint32_t.
+        bind_[5].buffer_type = MYSQL_TYPE_LONG;
+        bind_[5].buffer = reinterpret_cast<char*>(&lease_->subnet_id_);
+        bind_[5].is_unsigned = MLM_TRUE;
+
+        // Add the error flags
+        setErrorIndicators(bind_, error_, LEASE_COLUMNS);
+
+        // .. and check that we have the numbers correct at compile time.
+        BOOST_STATIC_ASSERT(5 < LEASE_COLUMNS);
+
+        // Add the data to the vector.  Note the end element is one after the
+        // end of the array.
+        return (std::vector<MYSQL_BIND>(&bind_[0], &bind_[LEASE_COLUMNS]));
+    }
+
+    /// @brief Create BIND array to receive data
+    ///
+    /// Creates a MYSQL_BIND array to receive Lease4 data from the database.
+    /// After data is successfully received, getLeaseData() can be used to copy
+    /// it to a Lease6 object.
+    ///
+    std::vector<MYSQL_BIND> createBindForReceive() {
+
+        // Initialize MYSQL_BIND array.
+        memset(bind_, 0, sizeof(bind_));
+
+        // address:  uint32_t
+        bind_[0].buffer_type = MYSQL_TYPE_LONG;
+        bind_[0].buffer = reinterpret_cast<char*>(&addr4_);
+        bind_[0].is_unsigned = MLM_TRUE;
+
+        // hwaddr: varbinary(20)
+        hwaddr_length_ = sizeof(hwaddr_buffer_);
+        bind_[1].buffer_type = MYSQL_TYPE_BLOB;
+        bind_[1].buffer = reinterpret_cast<char*>(hwaddr_buffer_);
+        bind_[1].buffer_length = hwaddr_length_;
+        bind_[1].length = &hwaddr_length_;
+
+        // client_id: varbinary(128)
+        client_id_length_ = sizeof(client_id_buffer_);
+        bind_[2].buffer_type = MYSQL_TYPE_BLOB;
+        bind_[2].buffer = reinterpret_cast<char*>(client_id_buffer_);
+        bind_[2].buffer_length = client_id_length_;
+        bind_[2].length = &client_id_length_;
+
+        // lease_time: unsigned int
+        bind_[3].buffer_type = MYSQL_TYPE_LONG;
+        bind_[3].buffer = reinterpret_cast<char*>(&valid_lifetime_);
+        bind_[3].is_unsigned = MLM_TRUE;
+
+        // expire: timestamp
+        bind_[4].buffer_type = MYSQL_TYPE_TIMESTAMP;
+        bind_[4].buffer = reinterpret_cast<char*>(&expire_);
+        bind_[4].buffer_length = sizeof(expire_);
+
+        // subnet_id: unsigned int
+        bind_[5].buffer_type = MYSQL_TYPE_LONG;
+        bind_[5].buffer = reinterpret_cast<char*>(&subnet_id_);
+        bind_[5].is_unsigned = MLM_TRUE;
+
+        // Add the error flags
+        setErrorIndicators(bind_, error_, LEASE_COLUMNS);
+
+        // .. and check that we have the numbers correct at compile time.
+        BOOST_STATIC_ASSERT(5 < LEASE_COLUMNS);
+
+        // Add the data to the vector.  Note the end element is one after the
+        // end of the array.
+        return(std::vector<MYSQL_BIND>(&bind_[0], &bind_[LEASE_COLUMNS]));
+    }
+
+    /// @brief Copy Received Data into Lease6 Object
+    ///
+    /// Called after the MYSQL_BIND array created by createBindForReceive()
+    /// has been used, this copies data from the internal member variables
+    /// into a Lease4 objec.
+    ///
+    /// @return Lease4Ptr Pointer to a Lease6 object holding the relevant
+    ///         data.
+    Lease4Ptr getLeaseData() {
+        // Convert times received from the database to times for the lease
+        // structure
+        time_t cltt = 0;
+        MySqlLeaseMgr::convertFromDatabaseTime(expire_, valid_lifetime_, cltt);
+
+        return (Lease4Ptr(new Lease4(addr4_, hwaddr_buffer_, hwaddr_length_,
+                                     client_id_buffer_, client_id_length_,
+                                     valid_lifetime_, cltt, subnet_id_)));
+    }
+
+    /// @brief Return columns in error
+    ///
+    /// If an error is returned from a fetch (in particular, a truncated
+    /// status), this method can be called to get the names of the fields in
+    /// error.  It returns a string comprising the names of the fields
+    /// separated by commas.  In the case of there being no error indicators
+    /// set, it returns the string "(None)".
+    ///
+    /// @return Comma-separated list of columns in error, or the string
+    ///         "(None)".
+    std::string getErrorColumns() {
+        return (getColumnsInError(error_, columns_, LEASE_COLUMNS));
+    }
+
+private:
+
+    // Note: All array lengths are equal to the corresponding variable in the
+    //       schema.
+    // Note: Arrays are declared fixed length for speed of creation
+    uint32_t        addr4_;             ///< IPv4 address
+    MYSQL_BIND      bind_[LEASE_COLUMNS]; ///< Bind array
+    std::string     columns_[LEASE_COLUMNS];///< Column names
+    my_bool         error_[LEASE_COLUMNS];  ///< Error array
+    std::vector<uint8_t> hwaddr_;       ///< Hardware address
+    uint8_t         hwaddr_buffer_[HWADDR_MAX_LEN];
+                                        ///< Hardware address buffer
+    unsigned long   hwaddr_length_;     ///< Hardware address length
+    std::vector<uint8_t> client_id_;    ///< Client identification
+    uint8_t         client_id_buffer_[ClientId::MAX_CLIENT_ID_LEN];
+                                        ///< Client ID buffer
+    unsigned long   client_id_length_;  ///< Client ID address length
+    MYSQL_TIME      expire_;            ///< Lease expiry time
+    Lease4Ptr       lease_;             ///< Pointer to lease object
+    uint32_t        subnet_id_;         ///< Subnet identification
+    uint32_t        valid_lifetime_;    ///< Lease time
+};
+
 
 
 /// @brief Exchange MySQL and Lease6 Data
@@ -104,33 +497,45 @@ namespace dhcp {
 /// On any MySQL operation, arrays of MYSQL_BIND structures must be built to
 /// describe the parameters in the prepared statements.  Where information is
 /// inserted or retrieved - INSERT, UPDATE, SELECT - a large amount of that
-/// structure is identical - it defines data values in the Lease6 structure.
-/// This class handles the creation of that array.
+/// structure is identical.  This class handles the creation of that array.
 ///
 /// Owing to the MySQL API, the process requires some intermediate variables
-/// to hold things like length etc.  This object holds the intermediate
-/// variables as well.
+/// to hold things like data length etc.  This object holds those variables.
 ///
 /// @note There are no unit tests for this class.  It is tested indirectly
 /// in all MySqlLeaseMgr::xxx6() calls where it is used.
 
-class MySqlLease6Exchange {
+class MySqlLease6Exchange : public MySqlLeaseExchange {
+    /// @brief Set number of database columns for this lease structure
+    static const size_t LEASE_COLUMNS = 9;
+
 public:
     /// @brief Constructor
     ///
-    /// Apart from the initialization of false_ and true_, the initialization
-    /// of addr6_length_, duid_length_, addr6_buffer_ and duid_buffer_ are
-    /// to satisfy cppcheck: none are really needed, as all variables are
-    /// initialized/set in the methods.
-    MySqlLease6Exchange() : addr6_length_(0), duid_length_(0),
-                            false_(0), true_(1) {
+    /// The initialization of the variables here is nonly to satisfy cppcheck -
+    /// all variables are initialized/set in the methods before they are used.
+    MySqlLease6Exchange() : addr6_length_(0), duid_length_(0) {
         memset(addr6_buffer_, 0, sizeof(addr6_buffer_));
         memset(duid_buffer_, 0, sizeof(duid_buffer_));
+        std::fill(&error_[0], &error_[LEASE_COLUMNS], MLM_FALSE);
+ 
+        // Set the column names (for error messages)
+        columns_[0] = "address";
+        columns_[1] = "duid";
+        columns_[2] = "valid_lifetime";
+        columns_[3] = "expire";
+        columns_[4] = "subnet_id";
+        columns_[5] = "pref_lifetime";
+        columns_[6] = "lease_type";
+        columns_[7] = "iaid";
+        columns_[8] = "prefix_len";
+        BOOST_STATIC_ASSERT(8 < LEASE_COLUMNS);
     }
 
     /// @brief Create MYSQL_BIND objects for Lease6 Pointer
     ///
-    /// Fills in the MYSQL_BIND objects for the Lease6 passed to it.
+    /// Fills in the MYSQL_BIND array for sending data in the Lease4 object to
+    /// the database.
     ///
     /// @param lease Lease object to be added to the database.
     ///
@@ -143,7 +548,7 @@ public:
         // for this lease.
         memset(bind_, 0, sizeof(bind_));
 
-        // address: varchar(40)
+        // address: varchar(39)
         addr6_ = lease_->addr_.toText();
         addr6_length_ = addr6_.size();
 
@@ -154,13 +559,13 @@ public:
         // is guaranteed to be valid until the next non-const operation on
         // addr6_.)
         //
-        // Note that the const_cast could be avoided by copying the string to
-        // a writeable buffer and storing the address of that in the "buffer"
-        // element.  However, this introduces a copy operation (with additional
-        // overhead) purely to get round the strictures introduced by design of
-        // the MySQL interface (which uses the area pointed to by "buffer" as
-        // input when specifying query parameters and as output when retrieving
-        // data).  For that reason, "const_cast" has been used.
+        // The const_cast could be avoided by copying the string to a writeable
+        // buffer and storing the address of that in the "buffer" element.
+        // However, this introduces a copy operation (with additional overhead)
+        // purely to get round the structures introduced by design of the
+        // MySQL interface (which uses the area pointed to by "buffer" as input
+        // when specifying query parameters and as output when retrieving data).
+        // For that reason, "const_cast" has been used.
         bind_[0].buffer_type = MYSQL_TYPE_STRING;
         bind_[0].buffer = const_cast<char*>(addr6_.c_str());
         bind_[0].buffer_length = addr6_length_;
@@ -177,8 +582,8 @@ public:
 
         // valid lifetime: unsigned int
         bind_[2].buffer_type = MYSQL_TYPE_LONG;
-        bind_[2].buffer = reinterpret_cast<char*>(&lease->valid_lft_);
-        bind_[2].is_unsigned = true_;
+        bind_[2].buffer = reinterpret_cast<char*>(&lease_->valid_lft_);
+        bind_[2].is_unsigned = MLM_TRUE;
 
         // expire: timestamp
         // The lease structure holds the client last transmission time (cltt_)
@@ -187,7 +592,7 @@ public:
         //
         // expire = cltt_ + valid_lft_
         //
-        // @TODO Handle overflows
+        // @todo Handle overflows
         MySqlLeaseMgr::convertToDatabaseTime(lease_->cltt_, lease_->valid_lft_,
                                              expire_);
         bind_[3].buffer_type = MYSQL_TYPE_TIMESTAMP;
@@ -198,36 +603,42 @@ public:
         // Can use lease_->subnet_id_ directly as it is of type uint32_t.
         bind_[4].buffer_type = MYSQL_TYPE_LONG;
         bind_[4].buffer = reinterpret_cast<char*>(&lease_->subnet_id_);
-        bind_[4].is_unsigned = true_;
+        bind_[4].is_unsigned = MLM_TRUE;
 
         // pref_lifetime: unsigned int
         // Can use lease_->preferred_lft_ directly as it is of type uint32_t.
         bind_[5].buffer_type = MYSQL_TYPE_LONG;
         bind_[5].buffer = reinterpret_cast<char*>(&lease_->preferred_lft_);
-        bind_[5].is_unsigned = true_;
+        bind_[5].is_unsigned = MLM_TRUE;
 
         // lease_type: tinyint
-        // Must convert to uint8_t as lease_->type_ is a LeaseType variable
+        // Must convert to uint8_t as lease_->type_ is a LeaseType variable.
         lease_type_ = lease_->type_;
         bind_[6].buffer_type = MYSQL_TYPE_TINY;
         bind_[6].buffer = reinterpret_cast<char*>(&lease_type_);
-        bind_[6].is_unsigned = true_;
+        bind_[6].is_unsigned = MLM_TRUE;
 
         // iaid: unsigned int
         // Can use lease_->iaid_ directly as it is of type uint32_t.
         bind_[7].buffer_type = MYSQL_TYPE_LONG;
         bind_[7].buffer = reinterpret_cast<char*>(&lease_->iaid_);
-        bind_[7].is_unsigned = true_;
+        bind_[7].is_unsigned = MLM_TRUE;
 
         // prefix_len: unsigned tinyint
         // Can use lease_->prefixlen_ directly as it is uint32_t.
         bind_[8].buffer_type = MYSQL_TYPE_TINY;
         bind_[8].buffer = reinterpret_cast<char*>(&lease_->prefixlen_);
-        bind_[8].is_unsigned = true_;
+        bind_[8].is_unsigned = MLM_TRUE;
+
+        // Add the error flags
+        setErrorIndicators(bind_, error_, LEASE_COLUMNS);
+
+        // .. and check that we have the numbers correct at compile time.
+        BOOST_STATIC_ASSERT(8 < LEASE_COLUMNS);
 
         // Add the data to the vector.  Note the end element is one after the
         // end of the array.
-        return (std::vector<MYSQL_BIND>(&bind_[0], &bind_[9]));
+        return (std::vector<MYSQL_BIND>(&bind_[0], &bind_[LEASE_COLUMNS]));
     }
 
     /// @brief Create BIND array to receive data
@@ -236,24 +647,22 @@ public:
     /// After data is successfully received, getLeaseData() is used to copy
     /// it to a Lease6 object.
     ///
-    /// @return Vector of MySQL BIND objects.
+    /// @return Vector of MySQL BIND objects passed to the MySQL data retrieval
+    ///         functions.
     std::vector<MYSQL_BIND> createBindForReceive() {
 
-        // Ensure both the array of MYSQL_BIND structures and the error array
-        // are clear.
+        // Initialize MYSQL_BIND array.
         memset(bind_, 0, sizeof(bind_));
-        memset(error_, 0, sizeof(error_));
 
-        // address:  varchar
+        // address:  varchar(39)
         // A Lease6_ address has a maximum of 39 characters.  The array is
-        // a few bites longer than this to guarantee that we can always null
-        // terminate it.
+        // one byte longer than this to guarantee that we can always null
+        // terminate it whatever is returned.
         addr6_length_ = sizeof(addr6_buffer_) - 1;
         bind_[0].buffer_type = MYSQL_TYPE_STRING;
         bind_[0].buffer = addr6_buffer_;
         bind_[0].buffer_length = addr6_length_;
         bind_[0].length = &addr6_length_;
-        bind_[0].error = &error_[0];
 
         // client_id: varbinary(128)
         duid_length_ = sizeof(duid_buffer_);
@@ -261,59 +670,57 @@ public:
         bind_[1].buffer = reinterpret_cast<char*>(duid_buffer_);
         bind_[1].buffer_length = duid_length_;
         bind_[1].length = &duid_length_;
-        bind_[1].error = &error_[1];
 
         // lease_time: unsigned int
         bind_[2].buffer_type = MYSQL_TYPE_LONG;
         bind_[2].buffer = reinterpret_cast<char*>(&valid_lifetime_);
-        bind_[2].is_unsigned = true_;
-        bind_[2].error = &error_[2];
+        bind_[2].is_unsigned = MLM_TRUE;
 
         // expire: timestamp
         bind_[3].buffer_type = MYSQL_TYPE_TIMESTAMP;
         bind_[3].buffer = reinterpret_cast<char*>(&expire_);
         bind_[3].buffer_length = sizeof(expire_);
-        bind_[3].error = &error_[3];
 
         // subnet_id: unsigned int
         bind_[4].buffer_type = MYSQL_TYPE_LONG;
         bind_[4].buffer = reinterpret_cast<char*>(&subnet_id_);
-        bind_[4].is_unsigned = true_;
-        bind_[4].error = &error_[4];
+        bind_[4].is_unsigned = MLM_TRUE;
 
         // pref_lifetime: unsigned int
         bind_[5].buffer_type = MYSQL_TYPE_LONG;
         bind_[5].buffer = reinterpret_cast<char*>(&pref_lifetime_);
-        bind_[5].is_unsigned = true_;
-        bind_[5].error = &error_[5];
+        bind_[5].is_unsigned = MLM_TRUE;
 
         // lease_type: tinyint
         bind_[6].buffer_type = MYSQL_TYPE_TINY;
         bind_[6].buffer = reinterpret_cast<char*>(&lease_type_);
-        bind_[6].is_unsigned = true_;
-        bind_[6].error = &error_[6];
+        bind_[6].is_unsigned = MLM_TRUE;
 
         // iaid: unsigned int
         bind_[7].buffer_type = MYSQL_TYPE_LONG;
         bind_[7].buffer = reinterpret_cast<char*>(&iaid_);
-        bind_[7].is_unsigned = true_;
-        bind_[7].error = &error_[7];
+        bind_[7].is_unsigned = MLM_TRUE;
 
         // prefix_len: unsigned tinyint
         bind_[8].buffer_type = MYSQL_TYPE_TINY;
         bind_[8].buffer = reinterpret_cast<char*>(&prefixlen_);
-        bind_[8].is_unsigned = true_;
-        bind_[8].error = &error_[8];
+        bind_[8].is_unsigned = MLM_TRUE;
+
+        // Add the error flags
+        setErrorIndicators(bind_, error_, LEASE_COLUMNS);
+
+        // .. and check that we have the numbers correct at compile time.
+        BOOST_STATIC_ASSERT(8 < LEASE_COLUMNS);
 
         // Add the data to the vector.  Note the end element is one after the
         // end of the array.
-        return(std::vector<MYSQL_BIND>(&bind_[0], &bind_[9]));
+        return(std::vector<MYSQL_BIND>(&bind_[0], &bind_[LEASE_COLUMNS]));
     }
 
     /// @brief Copy Received Data into Lease6 Object
     ///
     /// Called after the MYSQL_BIND array created by createBindForReceive()
-    /// has been used, this copies data from the internal member vairables
+    /// has been used, this copies data from the internal member variables
     /// into a Lease6 object.
     ///
     /// @return Lease6Ptr Pointer to a Lease6 object holding the relevant
@@ -321,74 +728,86 @@ public:
     ///
     /// @throw isc::BadValue Unable to convert Lease Type value in database
     Lease6Ptr getLeaseData() {
-
-        // Create the object to be returned.
-        Lease6Ptr result(new Lease6());
-
-        // Put the data in the lease object
-
         // The address buffer is declared larger than the buffer size passed
         // to the access function so that we can always append a null byte.
+        // Create the IOAddress object corresponding to the received data.
         addr6_buffer_[addr6_length_] = '\0';
         std::string address = addr6_buffer_;
+        isc::asiolink::IOAddress addr(address);
 
-        // Set the other data, converting time as needed.
-        result->addr_ = isc::asiolink::IOAddress(address);
-        result->duid_.reset(new DUID(duid_buffer_, duid_length_));
-        MySqlLeaseMgr::convertFromDatabaseTime(expire_, valid_lifetime_,
-                                               result->cltt_);
-        result->valid_lft_ = valid_lifetime_;
-        result->subnet_id_ = subnet_id_;
-        result->preferred_lft_ = pref_lifetime_;
-
-        // We can't convert from a numeric value to an enum, hence:
+        // Set the lease type in a variable of the appropriate data type, which
+        // has been initialized with an arbitrary (but valid) value.
+        Lease6::LeaseType type = Lease6::LEASE_IA_NA;
         switch (lease_type_) {
             case Lease6::LEASE_IA_NA:
-                result->type_ = Lease6::LEASE_IA_NA;
+                type = Lease6::LEASE_IA_NA;
                 break;
 
             case Lease6::LEASE_IA_TA:
-                result->type_ = Lease6::LEASE_IA_TA;
+                type = Lease6::LEASE_IA_TA;
                 break;
 
             case Lease6::LEASE_IA_PD:
-                result->type_ = Lease6::LEASE_IA_PD;
+                type = Lease6::LEASE_IA_PD;
                 break;
 
             default:
                 isc_throw(BadValue, "invalid lease type returned (" <<
                           lease_type_ << ") for lease with address " <<
-                          result->addr_.toText() << ". Only 0, 1, or 2 "
-                          "are allowed.");
+                          address << ". Only 0, 1, or 2 are allowed.");
         }
-        result->iaid_ = iaid_;
-        result->prefixlen_ = prefixlen_;
+
+        // Set up DUID,
+        DuidPtr duid_ptr(new DUID(duid_buffer_, duid_length_));
+
+        // Create the lease and set the cltt (after converting from the
+        // expire time retrieved from the database).
+        Lease6Ptr result(new Lease6(type, addr, duid_ptr, iaid_,
+                                    pref_lifetime_, valid_lifetime_, 0, 0,
+                                    subnet_id_, prefixlen_));
+        time_t cltt = 0;
+        MySqlLeaseMgr::convertFromDatabaseTime(expire_, valid_lifetime_, cltt);
+        result->cltt_ = cltt;
 
         return (result);
     }
 
+    /// @brief Return columns in error
+    ///
+    /// If an error is returned from a fetch (in particular, a truncated
+    /// status), this method can be called to get the names of the fields in
+    /// error.  It returns a string comprising the names of the fields
+    /// separated by commas.  In the case of there being no error indicators
+    /// set, it returns the string "(None)".
+    ///
+    /// @return Comma-separated list of columns in error, or the string
+    ///         "(None)".
+    std::string getErrorColumns() {
+        return (getColumnsInError(error_, columns_, LEASE_COLUMNS));
+    }
+
 private:
     // Note: All array lengths are equal to the corresponding variable in the
     // schema.
+    // Note: arrays are declared fixed length for speed of creation
     std::string     addr6_;             ///< String form of address
-    char            addr6_buffer_[ADDRESS6_TEXT_MAX_LEN];  ///< Character 
+    char            addr6_buffer_[ADDRESS6_TEXT_MAX_LEN + 1];  ///< Character 
                                         ///< array form of V6 address
     unsigned long   addr6_length_;      ///< Length of the address
-    MYSQL_BIND      bind_[9];           ///< Static array for speed of access
+    MYSQL_BIND      bind_[LEASE_COLUMNS]; ///< Bind array
+    std::string     columns_[LEASE_COLUMNS];///< Column names
     std::vector<uint8_t> duid_;         ///< Client identification
-    uint8_t         duid_buffer_[DUID_MAX_LEN]; ///< Buffer form of DUID
+    uint8_t         duid_buffer_[DUID::MAX_DUID_LEN]; ///< Buffer form of DUID
     unsigned long   duid_length_;       ///< Length of the DUID
-    my_bool         error_[9];          ///< For error reporting
+    my_bool         error_[LEASE_COLUMNS]; ///< Error indicators
     MYSQL_TIME      expire_;            ///< Lease expiry time
-    const my_bool   false_;             ///< "false" for MySql
     uint32_t        iaid_;              ///< Identity association ID
     Lease6Ptr       lease_;             ///< Pointer to lease object
-    uint32_t        valid_lifetime_;    ///< Lease time
     uint8_t         lease_type_;        ///< Lease type
     uint8_t         prefixlen_;         ///< Prefix length
     uint32_t        pref_lifetime_;     ///< Preferred lifetime
     uint32_t        subnet_id_;         ///< Subnet identification
-    const my_bool   true_;              ///< "true_" for MySql
+    uint32_t        valid_lifetime_;    ///< Lease time
 };
 
 
@@ -396,15 +815,16 @@ private:
 ///
 /// When a MySQL statement is exected, to fetch the results the function
 /// mysql_stmt_fetch() must be called.  As well as getting data, this
-/// allocated internal state.  Subsequent calls to mysql_stmt_fetch
-/// can be made, but when all the data is retrieved, mysql_stmt_free_result
-/// must be called to free up the resources allocated.
+/// allocates internal state.  Subsequent calls to mysql_stmt_fetch can be
+/// made, but when all the data is retrieved, mysql_stmt_free_result must be
+/// called to free up the resources allocated.
 ///
 /// Created prior to the first fetch, this class's destructor calls
 /// mysql_stmt_free_result, so eliminating the need for an explicit release
-/// in the method using mysql_stmt_free_result.  In this way, it guarantees
+/// in the method calling mysql_stmt_free_result.  In this way, it guarantees
 /// that the resources are released even if the MySqlLeaseMgr method concerned
 /// exits via an exception.
+
 class MySqlFreeResult {
 public:
 
@@ -433,7 +853,7 @@ private:
 };
 
 
-// MySqlLeaseMgr Methods
+// MySqlLeaseMgr Constructor and Destructor
 
 MySqlLeaseMgr::MySqlLeaseMgr(const LeaseMgr::ParameterMap& parameters) 
     : LeaseMgr(parameters), mysql_(NULL) {
@@ -444,11 +864,14 @@ MySqlLeaseMgr::MySqlLeaseMgr(const LeaseMgr::ParameterMap& parameters)
         isc_throw(DbOpenError, "unable to initialize MySQL");
     }
 
-    // Open the database
+    // Open the database.
     openDatabase();
 
-    // Enable autocommit.  For maximum speed, the global parameter
-    // innodb_flush_log_at_trx_commit should be set to 2.
+    // Enable autocommit.  To avoid a flush to disk on every commit, the global
+    // parameter innodb_flush_log_at_trx_commit should be set to 2.  This will
+    // cause the changes to be written to the log, but flushed to disk in the
+    // background every second.  Setting the parameter to that value will speed
+    // up the system, but at the risk of losing data if the system crashes.
     my_bool result = mysql_autocommit(mysql_, 1);
     if (result != 0) {
         isc_throw(DbOperationError, mysql_error(mysql_));
@@ -457,16 +880,17 @@ MySqlLeaseMgr::MySqlLeaseMgr(const LeaseMgr::ParameterMap& parameters)
     // Prepare all statements likely to be used.
     prepareStatements();
 
-    // Create the exchange object for use in exchanging data between the
+    // Create the exchange objects for use in exchanging data between the
     // program and the database.
+    exchange4_.reset(new MySqlLease4Exchange());
     exchange6_.reset(new MySqlLease6Exchange());
 }
 
 
 MySqlLeaseMgr::~MySqlLeaseMgr() {
     // Free up the prepared statements, ignoring errors. (What would we do
-    // about them - we're destroying this object and are not really concerned
-    // with errors on a database connection that it about to go away.)
+    // about them? We're destroying this object and are not really concerned
+    // with errors on a database connection that is about to go away.)
     for (int i = 0; i < statements_.size(); ++i) {
         if (statements_[i] != NULL) {
             (void) mysql_stmt_close(statements_[i]);
@@ -495,7 +919,7 @@ void
 MySqlLeaseMgr::convertToDatabaseTime(time_t cltt, uint32_t valid_lifetime,
                                     MYSQL_TIME& expire) {
 
-    // Calculate expiry time and convert to various date/time fields.
+    // Calculate expiry time.
     // @TODO: handle overflows
     time_t expire_time = cltt + valid_lifetime;
 
@@ -510,8 +934,8 @@ MySqlLeaseMgr::convertToDatabaseTime(time_t cltt, uint32_t valid_lifetime,
     expire.hour = expire_tm.tm_hour;
     expire.minute = expire_tm.tm_min;
     expire.second = expire_tm.tm_sec;
-    expire.second_part = 0;                    // No fractional seconds
-    expire.neg = static_cast<my_bool>(0);      // Not negative
+    expire.second_part = 0;                  // No fractional seconds
+    expire.neg = my_bool(0);                 // Not negative
 }
 
 void
@@ -535,6 +959,9 @@ MySqlLeaseMgr::convertFromDatabaseTime(const MYSQL_TIME& expire,
 }
 
 
+
+// Open the database using the parameters passed to the constructor.
+
 void
 MySqlLeaseMgr::openDatabase() {
 
@@ -555,7 +982,6 @@ MySqlLeaseMgr::openDatabase() {
         user = suser.c_str();
     } catch (...) {
         // No user.  Fine, we'll use NULL
-        ;
     }
 
     const char* password = NULL;
@@ -565,7 +991,6 @@ MySqlLeaseMgr::openDatabase() {
         password = spassword.c_str();
     } catch (...) {
         // No password.  Fine, we'll use NULL
-        ;
     }
 
     const char* name = NULL;
@@ -579,8 +1004,11 @@ MySqlLeaseMgr::openDatabase() {
     }
 
     // Set options for the connection:
-    // - automatic reconnection
-    my_bool auto_reconnect = 1;
+    // 
+    // Automatic reconnection: after a period of inactivity, the client will
+    // disconnect from the database.  This option causes it to automatically
+    // reconnect when another operation is about to be done.
+    my_bool auto_reconnect = MLM_TRUE;
     int result = mysql_options(mysql_, MYSQL_OPT_RECONNECT, &auto_reconnect);
     if (result != 0) {
         isc_throw(DbOpenError, "unable to set auto-reconnect option: " <<
@@ -596,7 +1024,7 @@ MySqlLeaseMgr::openDatabase() {
     // changed and so the "affected rows" (retrievable from MySQL) is zero.
     // This makes it hard to distinguish whether the UPDATE changed no rows
     // because no row matching the WHERE clause was found, or because a
-    // row was found by no data was altered.
+    // row was found but no data was altered.
     MYSQL* status = mysql_real_connect(mysql_, host, user, password, name,
                                        0, NULL, CLIENT_FOUND_ROWS);
     if (status != mysql_) {
@@ -604,6 +1032,12 @@ MySqlLeaseMgr::openDatabase() {
     }
 }
 
+// Prepared statement setup.  The textual form of an SQL statement is stored
+// in a vector of strings (text_statements_) and is used in the output of
+// error messages.  The SQL statement is also compiled into a "prepared
+// statement" (stored in statements_), which avoids the overhead of compilation
+// during use.  As prepared statements have resources allocated to them, the
+// class destructor explicitly destroys them.
 
 void
 MySqlLeaseMgr::prepareStatement(StatementIndex index, const char* text) {
@@ -617,11 +1051,10 @@ MySqlLeaseMgr::prepareStatement(StatementIndex index, const char* text) {
 
     // All OK, so prepare the statement
     text_statements_[index] = std::string(text);
-
     statements_[index] = mysql_stmt_init(mysql_);
     if (statements_[index] == NULL) {
         isc_throw(DbOperationError, "unable to allocate MySQL prepared "
-                  "statement structure" << mysql_error(mysql_));
+                  "statement structure, reason: " << mysql_error(mysql_));
     }
 
     int status = mysql_stmt_prepare(statements_[index], text, strlen(text));
@@ -648,21 +1081,13 @@ MySqlLeaseMgr::prepareStatements() {
     }
 }
 
+// Add leases to the database.  The two public methods accept a lease object
+// (either V4 of V6), bind the contents to the appropriate prepared
+// statement, then call common code to execute the statement.
 
 bool
-MySqlLeaseMgr::addLease(const Lease4Ptr& /* lease */) {
-    isc_throw(NotImplemented, "MySqlLeaseMgr::addLease(const Lease4Ptr&) "
-              "not implemented yet");
-    return (false);
-}
-
-
-bool
-MySqlLeaseMgr::addLease(const Lease6Ptr& lease) {
-    const StatementIndex stindex = INSERT_LEASE6;
-
-    // Create the MYSQL_BIND array for the lease
-    std::vector<MYSQL_BIND> bind = exchange6_->createBindForSend(lease);
+MySqlLeaseMgr::addLeaseCommon(StatementIndex stindex,
+                              std::vector<MYSQL_BIND>& bind) {
 
     // Bind the parameters to the statement
     int status = mysql_stmt_bind_param(statements_[stindex], &bind[0]);
@@ -685,87 +1110,276 @@ MySqlLeaseMgr::addLease(const Lease6Ptr& lease) {
     return (true);
 }
 
+bool
+MySqlLeaseMgr::addLease(const Lease4Ptr& lease) {
+    // Create the MYSQL_BIND array for the lease
+    std::vector<MYSQL_BIND> bind = exchange4_->createBindForSend(lease);
 
-Lease4Ptr
-MySqlLeaseMgr::getLease4(const isc::asiolink::IOAddress& /* addr */,
-                         SubnetID /* subnet_id */) const {
-    isc_throw(NotImplemented, "MySqlLeaseMgr::getLease4(const IOAddress&, SubnetID) "
-              "not implemented yet");
-    return (Lease4Ptr());
+    // ... and drop to common code.
+    return (addLeaseCommon(INSERT_LEASE4, bind));
 }
 
+bool
+MySqlLeaseMgr::addLease(const Lease6Ptr& lease) {
+    // Create the MYSQL_BIND array for the lease
+    std::vector<MYSQL_BIND> bind = exchange6_->createBindForSend(lease);
 
-Lease4Ptr
-MySqlLeaseMgr::getLease4(const isc::asiolink::IOAddress& /* addr */) const {
-    isc_throw(NotImplemented, "MySqlLeaseMgr::getLease4(const IOAddress&) "
-              "not implemented yet");
-    return (Lease4Ptr());
+    // ... and drop to common code.
+    return (addLeaseCommon(INSERT_LEASE6, bind));
 }
 
+// Extraction of leases from the database.
+//
+// All getLease() methods ultimately call getLeaseCollection().  This
+// binds the input parameters passed to it with the appropriate prepared
+// statement and executes the statement.  It then gets the results from the
+// database.  getlease() methods that expect a single result back call it
+// with the "single" parameter set true: this causes an exception to be
+// generated if multiple records can be retrieved from the result set. (Such
+// an occurrence either indicates corruption in the database, or that an
+// assumption that a query can only return a single record is incorrect.)
+// Methods that require a collection of records have "single" set to the
+// default value of false.  The logic is the same for both Lease4 and Lease6
+// objects,  so the code is templated.
+//
+// Methods that require a collection of objects access this method through
+// two interface methods (also called getLeaseCollection()).  These are
+// short enough as to be defined in the header file: all they do is to supply
+// the appropriate MySqlLeaseXExchange object depending on the type of the
+// LeaseCollection objects passed to them.
+//
+// Methods that require a single object to be returned access the method
+// through two interface methods (called getLease()).  As well as supplying
+// the appropriate exchange object, they convert between lease collection
+// holding zero or one leases into an appropriate Lease object.
+
+template <typename Exchange, typename LeaseCollection>
+void MySqlLeaseMgr::getLeaseCollection(StatementIndex stindex,
+                                       MYSQL_BIND* bind,
+                                       Exchange& exchange,
+                                       LeaseCollection& result,
+                                       bool single) const {
+
+    // Bind the selection parameters to the statement
+    int status = mysql_stmt_bind_param(statements_[stindex], bind);
+    checkError(status, stindex, "unable to bind WHERE clause parameter");
 
-Lease4Collection
-MySqlLeaseMgr::getLease4(const HWAddr& /* hwaddr */) const {
-    isc_throw(NotImplemented, "MySqlLeaseMgr::getLease4(const HWAddr&) "
-              "not implemented yet");
-    return (Lease4Collection());
+    // Set up the MYSQL_BIND array for the data being returned and bind it to
+    // the statement.
+    std::vector<MYSQL_BIND> outbind = exchange->createBindForReceive();
+    status = mysql_stmt_bind_result(statements_[stindex], &outbind[0]);
+    checkError(status, stindex, "unable to bind SELECT clause parameters");
+
+    // Execute the statement
+    status = mysql_stmt_execute(statements_[stindex]);
+    checkError(status, stindex, "unable to execute");
+
+    // Ensure that all the lease information is retrieved in one go to avoid
+    // overhead of going back and forth between client and server.
+    status = mysql_stmt_store_result(statements_[stindex]);
+    checkError(status, stindex, "unable to set up for storing all results");
+
+    // Set up the fetch "release" object to release resources associated
+    // with the call to mysql_stmt_fetch when this method exits, then
+    // retrieve the data.
+    MySqlFreeResult fetch_release(statements_[stindex]);
+    int count = 0;
+    while ((status = mysql_stmt_fetch(statements_[stindex])) == 0) {
+        try {
+            result.push_back(exchange->getLeaseData());
+
+        } catch (const isc::BadValue& ex) {
+            // Rethrow the exception with a bit more data.
+            isc_throw(BadValue, ex.what() << ". Statement is <" <<
+                      text_statements_[stindex] << ">");
+        }
+
+        if (single && (++count > 1)) {
+            isc_throw(MultipleRecords, "multiple records were found in the "
+                      "database where only one was expected for query "
+                      << text_statements_[stindex]);
+        }
+    }
+
+    // How did the fetch end?
+    if (status == 1) {
+        // Error - unable to fetch results
+        checkError(status, stindex, "unable to fetch results");
+    } else if (status == MYSQL_DATA_TRUNCATED) {
+        // Data truncated - throw an exception indicating what was at fault
+        isc_throw(DataTruncated, text_statements_[stindex]
+                  << " returned truncated data: columns affected are "
+                  << exchange->getErrorColumns());
+    }
+}
+
+
+void MySqlLeaseMgr::getLease(StatementIndex stindex, MYSQL_BIND* bind,
+                             Lease4Ptr& result) const {
+    // Create appropriate collection object and get all leases matching
+    // the selection criteria.  The "single" paraeter is true to indicate
+    // that the called method should throw an exception if multiple
+    // matching records are found: this particular method is called when only
+    // one or zero matches is expected.
+    Lease4Collection collection;
+    getLeaseCollection(stindex, bind, exchange4_, collection, true);
+
+    // Return single record if present, else clear the lease.
+    if (collection.empty()) {
+        result.reset();
+    } else {
+        result = *collection.begin();
+    }
 }
 
 
+void MySqlLeaseMgr::getLease(StatementIndex stindex, MYSQL_BIND* bind,
+                             Lease6Ptr& result) const {
+    // Create appropriate collection object and get all leases matching
+    // the selection criteria.  The "single" paraeter is true to indicate
+    // that the called method should throw an exception if multiple
+    // matching records are found: this particular method is called when only
+    // one or zero matches is expected.
+    Lease6Collection collection;
+    getLeaseCollection(stindex, bind, exchange6_, collection, true);
+
+    // Return single record if present, else clear the lease.
+    if (collection.empty()) {
+        result.reset();
+    } else {
+        result = *collection.begin();
+    }
+}
+
+
+// Basic lease access methods.  Obtain leases from the database using various
+// criteria.
+
 Lease4Ptr
-MySqlLeaseMgr::getLease4(const HWAddr& /* hwaddr */,
-                         SubnetID /* subnet_id */) const {
-    isc_throw(NotImplemented, "MySqlLeaseMgr::getLease4(const HWAddr&, SubnetID) "
-              "not implemented yet");
-    return (Lease4Ptr());
+MySqlLeaseMgr::getLease4(const isc::asiolink::IOAddress& addr) const {
+    // Set up the WHERE clause value
+    MYSQL_BIND inbind[1];
+    memset(inbind, 0, sizeof(inbind));
+
+    uint32_t addr4 = static_cast<uint32_t>(addr);
+    inbind[0].buffer_type = MYSQL_TYPE_LONG;
+    inbind[0].buffer = reinterpret_cast<char*>(&addr4);
+    inbind[0].is_unsigned = MLM_TRUE;
+
+    // Get the data
+    Lease4Ptr result;
+    getLease(GET_LEASE4_ADDR, inbind, result);
+
+    return (result);
 }
 
 
 Lease4Collection
-MySqlLeaseMgr::getLease4(const ClientId& /* clientid */) const {
-    isc_throw(NotImplemented, "MySqlLeaseMgr::getLease4(const ClientID&) "
-              "not implemented yet");
-    return (Lease4Collection());
+MySqlLeaseMgr::getLease4(const HWAddr& hwaddr) const {
+    // Set up the WHERE clause value
+    MYSQL_BIND inbind[1];
+    memset(inbind, 0, sizeof(inbind));
+
+    // As "buffer" is "char*" - even though the data is being read - we need
+    // to cast away the "const"ness as well as reinterpreting the data as
+    // a "char*". (We could avoid the "const_cast" by copying the data to a
+    // local variable, but as the data is only being read, this introduces
+    // an unnecessary copy).
+    unsigned long hwaddr_length = hwaddr.size();
+    uint8_t* data = const_cast<uint8_t*>(&hwaddr[0]);
+
+    inbind[0].buffer_type = MYSQL_TYPE_BLOB;
+    inbind[0].buffer = reinterpret_cast<char*>(data);
+    inbind[0].buffer_length = hwaddr_length;
+    inbind[0].length = &hwaddr_length;
+
+    // Get the data
+    Lease4Collection result;
+    getLeaseCollection(GET_LEASE4_HWADDR, inbind, result);
+
+    return (result);
 }
 
 
 Lease4Ptr
-MySqlLeaseMgr::getLease4(const ClientId& /* clientid */,
-                         SubnetID /* subnet_id */) const {
-    isc_throw(NotImplemented, "MySqlLeaseMgr::getLease4(const ClientID&, SubnetID) "
-              "not implemented yet");
-    return (Lease4Ptr());
+MySqlLeaseMgr::getLease4(const HWAddr& hwaddr, SubnetID subnet_id) const {
+    // Set up the WHERE clause value
+    MYSQL_BIND inbind[2];
+    memset(inbind, 0, sizeof(inbind));
+
+    // As "buffer" is "char*" - even though the data is being read - we need
+    // to cast away the "const"ness as well as reinterpreting the data as
+    // a "char*". (We could avoid the "const_cast" by copying the data to a
+    // local variable, but as the data is only being read, this introduces
+    // an unnecessary copy).
+    unsigned long hwaddr_length = hwaddr.size();
+    uint8_t* data = const_cast<uint8_t*>(&hwaddr[0]);
+
+    inbind[0].buffer_type = MYSQL_TYPE_BLOB;
+    inbind[0].buffer = reinterpret_cast<char*>(data);
+    inbind[0].buffer_length = hwaddr_length;
+    inbind[0].length = &hwaddr_length;
+
+    inbind[1].buffer_type = MYSQL_TYPE_LONG;
+    inbind[1].buffer = reinterpret_cast<char*>(&subnet_id);
+    inbind[1].is_unsigned = MLM_TRUE;
+
+    // Get the data
+    Lease4Ptr result;
+    getLease(GET_LEASE4_HWADDR_SUBID, inbind, result);
+
+    return (result);
 }
 
 
-// A convenience function used in the various getLease6() methods.  It binds
-// the selection parameters to the prepared statement, and binds the variables
-// that will receive the data.  These are stored in the MySqlLease6Exchange
-// object associated with the lease manager and converted to a Lease6 object
-// when retrieved.
-void
-MySqlLeaseMgr::bind6AndExecute(StatementIndex stindex, MYSQL_BIND* inbind) const {
+Lease4Collection
+MySqlLeaseMgr::getLease4(const ClientId& clientid) const {
+    // Set up the WHERE clause value
+    MYSQL_BIND inbind[1];
+    memset(inbind, 0, sizeof(inbind));
 
-    // Bind the input parameters to the statement
-    int status = mysql_stmt_bind_param(statements_[stindex], inbind);
-    checkError(status, stindex, "unable to bind WHERE clause parameter");
+    std::vector<uint8_t> client_data = clientid.getClientId();
+    unsigned long client_data_length = client_data.size();
+    inbind[0].buffer_type = MYSQL_TYPE_BLOB;
+    inbind[0].buffer = reinterpret_cast<char*>(&client_data[0]);
+    inbind[0].buffer_length = client_data_length;
+    inbind[0].length = &client_data_length;
 
-    // Set up the SELECT clause
-    std::vector<MYSQL_BIND> outbind = exchange6_->createBindForReceive();
+    // Get the data
+    Lease4Collection result;
+    getLeaseCollection(GET_LEASE4_CLIENTID, inbind, result);
 
-    // Bind the output parameters to the statement
-    status = mysql_stmt_bind_result(statements_[stindex], &outbind[0]);
-    checkError(status, stindex, "unable to bind SELECT caluse parameters");
+    return (result);
+}
 
-    // Execute the statement
-    status = mysql_stmt_execute(statements_[stindex]);
-    checkError(status, stindex, "unable to execute");
+
+Lease4Ptr
+MySqlLeaseMgr::getLease4(const ClientId& clientid, SubnetID subnet_id) const {
+    // Set up the WHERE clause value
+    MYSQL_BIND inbind[2];
+    memset(inbind, 0, sizeof(inbind));
+
+    std::vector<uint8_t> client_data = clientid.getClientId();
+    unsigned long client_data_length = client_data.size();
+    inbind[0].buffer_type = MYSQL_TYPE_BLOB;
+    inbind[0].buffer = reinterpret_cast<char*>(&client_data[0]);
+    inbind[0].buffer_length = client_data_length;
+    inbind[0].length = &client_data_length;
+
+    inbind[1].buffer_type = MYSQL_TYPE_LONG;
+    inbind[1].buffer = reinterpret_cast<char*>(&subnet_id);
+    inbind[1].is_unsigned = MLM_TRUE;
+
+    // Get the data
+    Lease4Ptr result;
+    getLease(GET_LEASE4_CLIENTID_SUBID, inbind, result);
+
+    return (result);
 }
 
 
 Lease6Ptr
 MySqlLeaseMgr::getLease6(const isc::asiolink::IOAddress& addr) const {
-    const StatementIndex stindex = GET_LEASE6_ADDR;
-
     // Set up the WHERE clause value
     MYSQL_BIND inbind[1];
     memset(inbind, 0, sizeof(inbind));
@@ -780,39 +1394,8 @@ MySqlLeaseMgr::getLease6(const isc::asiolink::IOAddress& addr) const {
     inbind[0].buffer_length = addr6_length;
     inbind[0].length = &addr6_length;
 
-    // Bind the input parameters to the statement and bind the output
-    // to fields in the exchange object, then execute the prepared statement.
-    bind6AndExecute(stindex, inbind);
-
-    // Fetch the data and set up the "release" object to release associated
-    // resources when this method exits.
-    MySqlFreeResult fetch_release(statements_[stindex]);
-    int status = mysql_stmt_fetch(statements_[stindex]);
-
     Lease6Ptr result;
-    if (status == 0) {
-        try {
-            result = exchange6_->getLeaseData();
-        } catch (const isc::BadValue& ex) {
-            // Lease type is returned, to rethrow the exception with a bit
-            // more data.
-            isc_throw(BadValue, ex.what() << ". Statement is <" <<
-                      text_statements_[stindex] << ">");
-        }
-
-        // As the address is the primary key in the table, we can't return
-        // two rows, so we don't bother checking whether multiple rows have
-        // been returned.
-
-    } else if (status == 1) {
-        checkError(status, stindex, "unable to fetch results");
-
-    } else {
-        // @TODO Handle truncation
-        // We are ignoring truncation for now, so the only other result is
-        // no data was found.  In that case, we return a null Lease6 structure.
-        // This has already been set, so no action is needed.
-    }
+    getLease(GET_LEASE6_ADDR, inbind, result);
 
     return (result);
 }
@@ -820,7 +1403,6 @@ MySqlLeaseMgr::getLease6(const isc::asiolink::IOAddress& addr) const {
 
 Lease6Collection
 MySqlLeaseMgr::getLease6(const DUID& duid, uint32_t iaid) const {
-    const StatementIndex stindex = GET_LEASE6_DUID_IAID;
 
     // Set up the WHERE clause value
     MYSQL_BIND inbind[2];
@@ -834,7 +1416,7 @@ MySqlLeaseMgr::getLease6(const DUID& duid, uint32_t iaid) const {
     // Note that the const_cast could be avoided by copying the DUID to
     // a writeable buffer and storing the address of that in the "buffer"
     // element.  However, this introduces a copy operation (with additional
-    // overhead) purely to get round the strictures introduced by design of
+    // overhead) purely to get round the structures introduced by design of
     // the MySQL interface (which uses the area pointed to by "buffer" as
     // input when specifying query parameters and as output when retrieving
     // data).  For that reason, "const_cast" has been used.
@@ -849,45 +1431,11 @@ MySqlLeaseMgr::getLease6(const DUID& duid, uint32_t iaid) const {
     // IAID
     inbind[1].buffer_type = MYSQL_TYPE_LONG;
     inbind[1].buffer = reinterpret_cast<char*>(&iaid);
-    inbind[1].is_unsigned = static_cast<my_bool>(1);
-
-    // Bind the input parameters to the statement and bind the output
-    // to fields in the exchange object, then execute the prepared statement.
-    bind6AndExecute(stindex, inbind);
-
-    // Ensure that all the lease information is retrieved in one go to avoid
-    // overhead of going back and forth between client and server.
-    int status = mysql_stmt_store_result(statements_[stindex]);
-    checkError(status, stindex, "unable to set up for storing all results");
+    inbind[1].is_unsigned = MLM_TRUE;
 
-    // Fetch the data.  There could be multiple rows, so we need to iterate
-    // until all data has been retrieved.
+    // ... and get the data
     Lease6Collection result;
-
-    // Set up the fetch "release" object to release resources associated
-    // with the call to mysql_stmt_fetch when this method exits, then
-    // retrieve the data.
-    MySqlFreeResult fetch_release(statements_[stindex]);
-    while ((status = mysql_stmt_fetch(statements_[stindex])) == 0) {
-        try {
-            Lease6Ptr lease = exchange6_->getLeaseData();
-            result.push_back(lease);
-
-        } catch (const isc::BadValue& ex) {
-            // Rethrow the exception with a bit more data.
-            isc_throw(BadValue, ex.what() << ". Statement is <" <<
-                      text_statements_[stindex] << ">");
-        }
-    }
-
-    // How did the fetch end?
-    if (status == 1) {
-        // Error - unable to fecth results
-        checkError(status, stindex, "unable to fetch results");
-    } else if (status == MYSQL_DATA_TRUNCATED) {
-        // @TODO Handle truncation
-        ;
-    }
+    getLeaseCollection(GET_LEASE6_DUID_IAID, inbind, result);
 
     return (result);
 }
@@ -896,7 +1444,6 @@ MySqlLeaseMgr::getLease6(const DUID& duid, uint32_t iaid) const {
 Lease6Ptr
 MySqlLeaseMgr::getLease6(const DUID& duid, uint32_t iaid,
                          SubnetID subnet_id) const {
-    const StatementIndex stindex = GET_LEASE6_DUID_IAID_SUBID;
 
     // Set up the WHERE clause value
     MYSQL_BIND inbind[3];
@@ -915,58 +1462,70 @@ MySqlLeaseMgr::getLease6(const DUID& duid, uint32_t iaid,
     // IAID
     inbind[1].buffer_type = MYSQL_TYPE_LONG;
     inbind[1].buffer = reinterpret_cast<char*>(&iaid);
-    inbind[1].is_unsigned = static_cast<my_bool>(1);
+    inbind[1].is_unsigned = MLM_TRUE;
 
     // Subnet ID
     inbind[2].buffer_type = MYSQL_TYPE_LONG;
     inbind[2].buffer = reinterpret_cast<char*>(&subnet_id);
-    inbind[2].is_unsigned = static_cast<my_bool>(1);
-
-    // Bind the input parameters to the statement and bind the output
-    // to fields in the exchange object, then execute the prepared statement.
-    bind6AndExecute(stindex, inbind);
+    inbind[2].is_unsigned = MLM_TRUE;
 
-    // Fetch the data and set up the "release" object to release associated
-    // resources when this method exits then retrieve the data.
     Lease6Ptr result;
-    MySqlFreeResult fetch_release(statements_[stindex]);
-    int status = mysql_stmt_fetch(statements_[stindex]);
-    if (status == 0) {
-        try {
-            result = exchange6_->getLeaseData();
+    getLease(GET_LEASE6_DUID_IAID_SUBID, inbind, result);
 
-            // TODO: check for more than one row returned.  At present, just
-            // ignore the excess and take the first.
+    return (result);
+}
 
-        } catch (const isc::BadValue& ex) {
-            // Lease type is returned, to rethrow the exception with a bit
-            // more data.
-            isc_throw(BadValue, ex.what() << ". Statement is <" <<
-                      text_statements_[stindex] << ">");
-        }
+// Update lease methods.  These comprise common code that handles the actual
+// update, and type-specific methods that set up the parameters for the prepared
+// statement depending on the type of lease.
 
-        // As the address is the primary key in the table, we can't return
-        // two rows, so we don't bother checking whether multiple rows have
-        // been returned.
+template <typename LeasePtr>
+void
+MySqlLeaseMgr::updateLeaseCommon(StatementIndex stindex, MYSQL_BIND* bind,
+                                 const LeasePtr& lease) {
 
-    } else if (status == 1) {
-        checkError(status, stindex, "unable to fetch results");
+    // Bind the parameters to the statement
+    int status = mysql_stmt_bind_param(statements_[stindex], bind);
+    checkError(status, stindex, "unable to bind parameters");
 
-    } else {
-        // @TODO Handle truncation
-        // We are ignoring truncation for now, so the only other result is
-        // no data was found.  In that case, we return a null Lease6 structure.
-        // This has already been set, so the action is a no-op.
-    }
+    // Execute
+    status = mysql_stmt_execute(statements_[stindex]);
+    checkError(status, stindex, "unable to execute");
 
-    return (result);
+    // See how many rows were affected.  The statement should only update a
+    // single row.
+    int affected_rows = mysql_stmt_affected_rows(statements_[stindex]);
+    if (affected_rows == 0) {
+        isc_throw(NoSuchLease, "unable to update lease for address " <<
+                  lease->addr_.toText() << " as it does not exist");
+    } else if (affected_rows > 1) {
+        // Should not happen - primary key constraint should only have selected
+        // one row.
+        isc_throw(DbOperationError, "apparently updated more than one lease "
+                  "that had the address " << lease->addr_.toText());
+    }
 }
 
 
 void
-MySqlLeaseMgr::updateLease4(const Lease4Ptr& /* lease4 */) {
-    isc_throw(NotImplemented, "MySqlLeaseMgr::updateLease4(const Lease4Ptr&) "
-              "not implemented yet");
+MySqlLeaseMgr::updateLease4(const Lease4Ptr& lease) {
+    const StatementIndex stindex = UPDATE_LEASE4;
+
+    // Create the MYSQL_BIND array for the data being updated
+    std::vector<MYSQL_BIND> bind = exchange4_->createBindForSend(lease);
+
+    // Set up the WHERE clause and append it to the MYSQL_BIND array
+    MYSQL_BIND where;
+    memset(&where, 0, sizeof(where));
+
+    uint32_t addr4 = static_cast<uint32_t>(lease->addr_);
+    where.buffer_type = MYSQL_TYPE_LONG;
+    where.buffer = reinterpret_cast<char*>(&addr4);
+    where.is_unsigned = MLM_TRUE;
+    bind.push_back(where);
+
+    // Drop to common update code
+    updateLeaseCommon(stindex, &bind[0], lease);
 }
 
 
@@ -992,68 +1551,64 @@ MySqlLeaseMgr::updateLease6(const Lease6Ptr& lease) {
     where.length = &addr6_length;
     bind.push_back(where);
 
-    // Bind the parameters to the statement
-    int status = mysql_stmt_bind_param(statements_[stindex], &bind[0]);
-    checkError(status, stindex, "unable to bind parameters");
+    // Drop to common update code
+    updateLeaseCommon(stindex, &bind[0], lease);
+}
+
+// Delete lease methods.  Similar to other groups of methods, these comprise
+// a per-type method that sets up the relevant MYSQL_BIND array (in this
+// case, a single method for both V4 and V6 addresses) and a common method that
+// handles the common processing.
+
+bool
+MySqlLeaseMgr::deleteLeaseCommon(StatementIndex stindex, MYSQL_BIND* bind) {
+
+    // Bind the input parameters to the statement
+    int status = mysql_stmt_bind_param(statements_[stindex], bind);
+    checkError(status, stindex, "unable to bind WHERE clause parameter");
 
     // Execute
     status = mysql_stmt_execute(statements_[stindex]);
     checkError(status, stindex, "unable to execute");
 
-    // See how many rows were affected.  The statement should only delete a
-    // single row.
-    int affected_rows = mysql_stmt_affected_rows(statements_[stindex]);
-    if (affected_rows == 0) {
-        isc_throw(NoSuchLease, "unable to update lease for address " <<
-                  addr6 << " as it does not exist");
-    } else if (affected_rows > 1) {
-        // Should not happen - primary key constraint should only have selected
-        // one row.
-        isc_throw(DbOperationError, "apparently updated more than one lease "
-                  "that had the address " << addr6);
-    }
-}
-
-
-bool
-MySqlLeaseMgr::deleteLease4(const isc::asiolink::IOAddress& /* addr */) {
-    isc_throw(NotImplemented, "MySqlLeaseMgr::deleteLease4(const IOAddress&) "
-              "not implemented yet");
-    return (false);
+    // See how many rows were affected.  Note that the statement may delete
+    // multiple rows.
+    return (mysql_stmt_affected_rows(statements_[stindex]) > 0);
 }
 
 
 bool
-MySqlLeaseMgr::deleteLease6(const isc::asiolink::IOAddress& addr) {
-    const StatementIndex stindex = DELETE_LEASE6;
+MySqlLeaseMgr::deleteLease(const isc::asiolink::IOAddress& addr) {
 
     // Set up the WHERE clause value
     MYSQL_BIND inbind[1];
     memset(inbind, 0, sizeof(inbind));
 
-    std::string addr6 = addr.toText();
-    unsigned long addr6_length = addr6.size();
+    if (addr.isV4()) {
+        uint32_t addr4 = static_cast<uint32_t>(addr);
 
-    // See the earlier description of the use of "const_cast" when accessing
-    // the address for an explanation of the reason.
-    inbind[0].buffer_type = MYSQL_TYPE_STRING;
-    inbind[0].buffer = const_cast<char*>(addr6.c_str());
-    inbind[0].buffer_length = addr6_length;
-    inbind[0].length = &addr6_length;
+        inbind[0].buffer_type = MYSQL_TYPE_LONG;
+        inbind[0].buffer = reinterpret_cast<char*>(&addr4);
+        inbind[0].is_unsigned = MLM_TRUE;
 
-    // Bind the input parameters to the statement
-    int status = mysql_stmt_bind_param(statements_[stindex], inbind);
-    checkError(status, stindex, "unable to bind WHERE clause parameter");
+        return (deleteLeaseCommon(DELETE_LEASE4, inbind));
 
-    // Execute
-    status = mysql_stmt_execute(statements_[stindex]);
-    checkError(status, stindex, "unable to execute");
+    } else {
+        std::string addr6 = addr.toText();
+        unsigned long addr6_length = addr6.size();
 
-    // See how many rows were affected.  Note that the statement may delete
-    // multiple rows.
-    return (mysql_stmt_affected_rows(statements_[stindex]) > 0);
+        // See the earlier description of the use of "const_cast" when accessing
+        // the address for an explanation of the reason.
+        inbind[0].buffer_type = MYSQL_TYPE_STRING;
+        inbind[0].buffer = const_cast<char*>(addr6.c_str());
+        inbind[0].buffer_length = addr6_length;
+        inbind[0].length = &addr6_length;
+
+        return (deleteLeaseCommon(DELETE_LEASE6, inbind));
+    }
 }
 
+// Miscellaneous database methods.
 
 std::string
 MySqlLeaseMgr::getName() const {
@@ -1061,7 +1616,7 @@ MySqlLeaseMgr::getName() const {
     try {
         name = getParameter("name");
     } catch (...) {
-        ;
+        // Return an empty name
     }
     return (name);
 }
diff --git a/src/lib/dhcpsrv/mysql_lease_mgr.h b/src/lib/dhcpsrv/mysql_lease_mgr.h
index 1884da6..68b0e1c 100644
--- a/src/lib/dhcpsrv/mysql_lease_mgr.h
+++ b/src/lib/dhcpsrv/mysql_lease_mgr.h
@@ -27,18 +27,22 @@ namespace dhcp {
 
 // Define the current database schema values
 
-const uint32_t CURRENT_VERSION_VERSION = 0;
-const uint32_t CURRENT_VERSION_MINOR = 1;
+const uint32_t CURRENT_VERSION_VERSION = 1;
+const uint32_t CURRENT_VERSION_MINOR = 0;
 
 
-// Forward declaration of the Lease6 exchange object.  This class is defined
+// Forward declaration of the Lease exchange objects.  These classes are defined
 // in the .cc file.
+class MySqlLease4Exchange;
 class MySqlLease6Exchange;
 
 
 /// @brief MySQL Lease Manager
 ///
-/// This is a concrete API for the backend for the MySQL database.
+/// This class provides the \ref isc::dhcp::LeaseMgr interface to the MySQL
+/// database.  Use of this backend presupposes that a MySQL database is
+/// available and that the Kea schema has been created within it.
+
 class MySqlLeaseMgr : public LeaseMgr {
 public:
     /// @brief Constructor
@@ -68,7 +72,7 @@ public:
     /// @brief Destructor (closes database)
     virtual ~MySqlLeaseMgr();
 
-    /// @brief Adds an IPv4 lease.
+    /// @brief Adds an IPv4 lease
     ///
     /// @param lease lease to be added
     ///
@@ -79,7 +83,7 @@ public:
     ///        failed.
     virtual bool addLease(const Lease4Ptr& lease);
 
-    /// @brief Adds an IPv6 lease.
+    /// @brief Adds an IPv6 lease
     ///
     /// @param lease lease to be added
     ///
@@ -90,23 +94,10 @@ public:
     ///        failed.
     virtual bool addLease(const Lease6Ptr& lease);
 
-    /// @brief Return IPv4 lease for specified IPv4 address and subnet_id
-    ///
-    /// This method is used to get a lease for specific subnet_id. There can be
-    /// at most one lease for any given subnet, so this method returns a single
-    /// pointer.
-    ///
-    /// @param addr address of the sought lease
-    /// @param subnet_id ID of the subnet the lease must belong to
-    ///
-    /// @return smart pointer to the lease (or NULL if a lease is not found)
-    virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress& addr,
-                                SubnetID subnet_id) const;
-
     /// @brief Returns an IPv4 lease for specified IPv4 address
     ///
     /// This method return a lease that is associated with a given address.
-    /// For other query types (by hardware addr, by DUID) there can be
+    /// For other query types (by hardware addr, by Client ID) there can be
     /// several leases in different subnets (e.g. for mobile clients that
     /// got address in different subnets). However, for a single address
     /// there can be only one lease, so this method returns a pointer to
@@ -115,6 +106,12 @@ public:
     /// @param addr address of the searched lease
     ///
     /// @return smart pointer to the lease (or NULL if a lease is not found)
+    ///
+    /// @throw isc::dhcp::DataTruncation Data was truncated on retrieval to
+    ///        fit into the space allocated for the result.  This indicates a
+    ///        programming error.
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
     virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress& addr) const;
 
 
@@ -128,6 +125,12 @@ public:
     /// @param hwaddr hardware address of the client
     ///
     /// @return lease collection
+    ///
+    /// @throw isc::dhcp::DataTruncation Data was truncated on retrieval to
+    ///        fit into the space allocated for the result.  This indicates a
+    ///        programming error.
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
     virtual Lease4Collection getLease4(const HWAddr& hwaddr) const;
 
     /// @brief Returns existing IPv4 leases for specified hardware address
@@ -140,6 +143,12 @@ public:
     /// @param subnet_id identifier of the subnet that lease must belong to
     ///
     /// @return a pointer to the lease (or NULL if a lease is not found)
+    ///
+    /// @throw isc::dhcp::DataTruncation Data was truncated on retrieval to
+    ///        fit into the space allocated for the result.  This indicates a
+    ///        programming error.
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
     virtual Lease4Ptr getLease4(const HWAddr& hwaddr,
                                 SubnetID subnet_id) const;
 
@@ -153,6 +162,12 @@ public:
     /// @param clientid client identifier
     ///
     /// @return lease collection
+    ///
+    /// @throw isc::dhcp::DataTruncation Data was truncated on retrieval to
+    ///        fit into the space allocated for the result.  This indicates a
+    ///        programming error.
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
     virtual Lease4Collection getLease4(const ClientId& clientid) const;
 
     /// @brief Returns existing IPv4 lease for specified client-id
@@ -164,6 +179,12 @@ public:
     /// @param subnet_id identifier of the subnet that lease must belong to
     ///
     /// @return a pointer to the lease (or NULL if a lease is not found)
+    ///
+    /// @throw isc::dhcp::DataTruncation Data was truncated on retrieval to
+    ///        fit into the space allocated for the result.  This indicates a
+    ///        programming error.
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
     virtual Lease4Ptr getLease4(const ClientId& clientid,
                                 SubnetID subnet_id) const;
 
@@ -179,6 +200,9 @@ public:
     ///
     /// @throw isc::BadValue record retrieved from database had an invalid
     ///        lease type field.
+    /// @throw isc::dhcp::DataTruncation Data was truncated on retrieval to
+    ///        fit into the space allocated for the result.  This indicates a
+    ///        programming error.
     /// @throw isc::dhcp::DbOperationError An operation on the open database has
     ///        failed.
     virtual Lease6Ptr getLease6(const isc::asiolink::IOAddress& addr) const;
@@ -194,6 +218,14 @@ public:
     /// @param iaid IA identifier
     ///
     /// @return smart pointer to the lease (or NULL if a lease is not found)
+    ///
+    /// @throw isc::BadValue record retrieved from database had an invalid
+    ///        lease type field.
+    /// @throw isc::dhcp::DataTruncation Data was truncated on retrieval to
+    ///        fit into the space allocated for the result.  This indicates a
+    ///        programming error.
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
     virtual Lease6Collection getLease6(const DUID& duid,
                                        uint32_t iaid) const;
 
@@ -204,18 +236,35 @@ public:
     /// @param subnet_id subnet id of the subnet the lease belongs to
     ///
     /// @return smart pointer to the lease (or NULL if a lease is not found)
+    ///
+    /// @throw isc::BadValue record retrieved from database had an invalid
+    ///        lease type field.
+    /// @throw isc::dhcp::DataTruncation Data was truncated on retrieval to
+    ///        fit into the space allocated for the result.  This indicates a
+    ///        programming error.
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
     virtual Lease6Ptr getLease6(const DUID& duid, uint32_t iaid,
                                 SubnetID subnet_id) const;
 
     /// @brief Updates IPv4 lease.
     ///
+    /// Updates the record of the lease in the database (as identified by the
+    /// address) with the data in the passed lease object.
+    ///
     /// @param lease4 The lease to be updated.
     ///
-    /// If no such lease is present, an exception will be thrown.
+    /// @throw isc::dhcp::NoSuchLease Attempt to update a lease that did not
+    ///        exist.
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
     virtual void updateLease4(const Lease4Ptr& lease4);
 
     /// @brief Updates IPv6 lease.
     ///
+    /// Updates the record of the lease in the database (as identified by the
+    /// address) with the data in the passed lease object.
+    ///
     /// @param lease6 The lease to be updated.
     ///
     /// @throw isc::dhcp::NoSuchLease Attempt to update a lease that did not
@@ -224,22 +273,16 @@ public:
     ///        failed.
     virtual void updateLease6(const Lease6Ptr& lease6);
 
-    /// @brief Deletes an IPv4 lease.
+    /// @brief Deletes a lease.
     ///
-    /// @param addr IPv4 address of the lease to be deleted.
-    ///
-    /// @return true if deletion was successful, false if no such lease exists
-    virtual bool deleteLease4(const isc::asiolink::IOAddress& addr);
-
-    /// @brief Deletes an IPv6 lease.
-    ///
-    /// @param addr IPv6 address of the lease to be deleted.
+    /// @param addr Address of the lease to be deleted.  This can be an IPv4
+    ///             address or an IPv6 address.
     ///
     /// @return true if deletion was successful, false if no such lease exists
     ///
     /// @throw isc::dhcp::DbOperationError An operation on the open database has
     ///        failed.
-    virtual bool deleteLease6(const isc::asiolink::IOAddress& addr);
+    virtual bool deleteLease(const isc::asiolink::IOAddress& addr);
 
     /// @brief Return backend type
     ///
@@ -343,12 +386,20 @@ public:
     ///
     /// The contents of the enum are indexes into the list of SQL statements
     enum StatementIndex {
+        DELETE_LEASE4,              // Delete from lease4 by address
         DELETE_LEASE6,              // Delete from lease6 by address
+        GET_LEASE4_ADDR,            // Get lease4 by address
+        GET_LEASE4_CLIENTID,        // Get lease4 by client ID
+        GET_LEASE4_CLIENTID_SUBID,  // Get lease4 by client ID & subnet ID
+        GET_LEASE4_HWADDR,          // Get lease4 by HW address
+        GET_LEASE4_HWADDR_SUBID,    // Get lease4 by HW address & subnet ID
         GET_LEASE6_ADDR,            // Get lease6 by address
         GET_LEASE6_DUID_IAID,       // Get lease6 by DUID and IAID
-        GET_LEASE6_DUID_IAID_SUBID, // Get lease6 by DUID, IAID and Subnet ID
+        GET_LEASE6_DUID_IAID_SUBID, // Get lease6 by DUID, IAID and subnet ID
         GET_VERSION,                // Obtain version number
+        INSERT_LEASE4,              // Add entry to lease4 table
         INSERT_LEASE6,              // Add entry to lease6 table
+        UPDATE_LEASE4,              // Update a Lease4 entry
         UPDATE_LEASE6,              // Update a Lease6 entry
         NUM_STATEMENTS              // Number of statements
     };
@@ -389,25 +440,150 @@ private:
     /// @throw DbOpenError Error opening the database
     void openDatabase();
 
-    /// @brief Binds Parameters and Executes
+    /// @brief Add Lease Common Code
+    ///
+    /// This method performs the common actions for both flavours (V4 and V6)
+    /// of the addLease method.  It binds the contents of the lease object to
+    /// the prepared statement and adds it to the database.
     ///
-    /// This method abstracts a lot of common processing from the getXxxx()
-    /// methods.  It binds the parameters passed to it to the appropriate
-    /// prepared statement, and binds the variables in the exchange6 object to
-    /// the output parameters of the statement.  It then executes the prepared
-    /// statement.
+    /// @param stindex Index of statemnent being executed
+    /// @param bind MYSQL_BIND array that has been created for the type
+    ///        of lease in question.
     ///
-    /// The data can be retrieved using mysql_stmt_fetch and the getLeaseData()
-    /// method on the exchange6 object.
+    /// @return true if the lease was added, false if it was not added because
+    ///         a lease with that address already exists in the database.
+    ///
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
+    bool addLeaseCommon(StatementIndex stindex, std::vector<MYSQL_BIND>& bind);
+
+    /// @brief Get Lease Collection Common Code
+    ///
+    /// This method performs the common actions for obtaining multiple leases
+    /// from the database.
+    ///
+    /// @param stindex Index of statement being executed
+    /// @param bind MYSQL_BIND array for input parameters
+    /// @param exchange Exchange object to use
+    /// @param lease LeaseCollection object returned.  Note that any leases in
+    ///        the collection when this method is called are not erased: the
+    ///        new data is appended to the end.
+    /// @param single If true, only a single data item is to be retrieved.
+    ///        If more than one is present, a MultipleRecords exception will
+    ///        be thrown.
+    ///
+    /// @throw isc::dhcp::BadValue Data retrieved from the database was invalid.
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
+    /// @throw isc::dhcp::MultipleRecords Multiple records were retrieved
+    ///        from the database where only one was expected.
+    template <typename Exchange, typename LeaseCollection>
+    void getLeaseCollection(StatementIndex stindex, MYSQL_BIND* bind,
+                            Exchange& exchange, LeaseCollection& result,
+                            bool single = false) const;
+
+    /// @brief Get Lease Collection
+    ///
+    /// Gets a collection of Lease4 objects.  This is just an interface to
+    /// the get lease collection common code.
+    ///
+    /// @param stindex Index of statement being executed
+    /// @param bind MYSQL_BIND array for input parameters
+    /// @param lease LeaseCollection object returned.  Note that any leases in
+    ///        the collection when this method is called are not erased: the
+    ///        new data is appended to the end.
+    ///
+    /// @throw isc::dhcp::BadValue Data retrieved from the database was invalid.
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
+    /// @throw isc::dhcp::MultipleRecords Multiple records were retrieved
+    ///        from the database where only one was expected.
+    void getLeaseCollection(StatementIndex stindex, MYSQL_BIND* bind,
+                            Lease4Collection& result) const {
+        getLeaseCollection(stindex, bind, exchange4_, result);
+    }
+
+    /// @brief Get Lease Collection
+    ///
+    /// Gets a collection of Lease6 objects.  This is just an interface to
+    /// the get lease collection common code.
+    ///
+    /// @param stindex Index of statement being executed
+    /// @param bind MYSQL_BIND array for input parameters
+    /// @param lease LeaseCollection object returned.  Note that any existing
+    ///        data in the collection is erased first.
+    ///
+    /// @throw isc::dhcp::BadValue Data retrieved from the database was invalid.
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
+    /// @throw isc::dhcp::MultipleRecords Multiple records were retrieved
+    ///        from the database where only one was expected.
+    void getLeaseCollection(StatementIndex stindex, MYSQL_BIND* bind,
+                            Lease6Collection& result) const {
+        getLeaseCollection(stindex, bind, exchange6_, result);
+    }
+
+    /// @brief Get Lease4 Common Code
+    ///
+    /// This method performs the common actions for the various getLease4()
+    /// methods.  It acts as an interface to the getLeaseCollection() method,
+    /// but retrieveing only a single lease.
+    ///
+    /// @param stindex Index of statement being executed
+    /// @param bind MYSQL_BIND array for input parameters
+    /// @param lease Lease4 object returned
+    void getLease(StatementIndex stindex, MYSQL_BIND* bind,
+                  Lease4Ptr& result) const;
+
+    /// @brief Get Lease6 Common Code
+    ///
+    /// This method performs the common actions for the various getLease46)
+    /// methods.  It acts as an interface to the getLeaseCollection() method,
+    /// but retrieveing only a single lease.
+    ///
+    /// @param stindex Index of statement being executed
+    /// @param bind MYSQL_BIND array for input parameters
+    /// @param lease Lease6 object returned
+    void getLease(StatementIndex stindex, MYSQL_BIND* bind,
+                   Lease6Ptr& result) const;
+
+    /// @brief Update lease common code
+    ///
+    /// Holds the common code for updating a lease.  It binds the parameters
+    /// to the prepared statement, executes it, then checks how many rows
+    /// were affected.
     ///
     /// @param stindex Index of prepared statement to be executed
-    /// @param inbind Array of MYSQL_BIND objects representing the parameters.
+    /// @param bind Array of MYSQL_BIND objects representing the parameters.
     ///        (Note that the number is determined by the number of parameters
     ///        in the statement.)
+    /// @param lease Pointer to the lease object whose record is being updated.
     ///
+    /// @throw NoSuchLease Could not update a lease because no lease matches
+    ///        the address given.
     /// @throw isc::dhcp::DbOperationError An operation on the open database has
     ///        failed.
-    void bind6AndExecute(StatementIndex stindex, MYSQL_BIND* inbind) const;
+    template <typename LeasePtr>
+    void updateLeaseCommon(StatementIndex stindex, MYSQL_BIND* bind,
+                           const LeasePtr& lease);
+
+    /// @brief Delete lease common code
+    ///
+    /// Holds the common code for deleting a lease.  It binds the parameters
+    /// to the prepared statement, executes the statement and checks to
+    /// see how many rows were deleted.
+    ///
+    /// @param stindex Index of prepared statement to be executed
+    /// @param bind Array of MYSQL_BIND objects representing the parameters.
+    ///        (Note that the number is determined by the number of parameters
+    ///        in the statement.)
+    ///
+    /// @return true if one or more rows were deleted, false if none were
+    ///         deleted.
+    ///
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
+    bool deleteLeaseCommon(StatementIndex stindex, MYSQL_BIND* bind);
 
     /// @brief Check Error and Throw Exception
     ///
@@ -433,14 +609,15 @@ private:
 
     // Members
 
-    /// Used for transfer of data to/from the database. This is a pointed-to
-    /// object as its contents may change in "const" calls, while the rest
-    /// of this object does not.  (At alternative would be to declare it as
-    /// "mutable".)
-    boost::scoped_ptr<MySqlLease6Exchange> exchange6_;
+    /// The exchange objects are used for transfer of data to/from the database.
+    /// They are pointed-to objects as the contents may change in "const" calls,
+    /// while the rest of this object does not.  (At alternative would be to
+    /// declare them as "mutable".)
+    boost::scoped_ptr<MySqlLease4Exchange> exchange4_; ///< Exchange object
+    boost::scoped_ptr<MySqlLease6Exchange> exchange6_; ///< Exchange object
     MYSQL*              mysql_;                 ///< MySQL context object
-    std::vector<std::string> text_statements_;  ///< Raw text of statements
     std::vector<MYSQL_STMT*> statements_;       ///< Prepared statements
+    std::vector<std::string> text_statements_;  ///< Raw text of statements
 };
 
 }; // end of isc::dhcp namespace
diff --git a/src/lib/dhcpsrv/subnet.h b/src/lib/dhcpsrv/subnet.h
index aa1ef1f..c7d7ac7 100644
--- a/src/lib/dhcpsrv/subnet.h
+++ b/src/lib/dhcpsrv/subnet.h
@@ -172,7 +172,7 @@ public:
                     // Use option type as the index key. The type is held
                     // in OptionPtr object so we have to call Option::getType
                     // to retrieve this key for each element.
-                    boost::multi_index::mem_fun<
+                    boost::multi_index::const_mem_fun<
                         Option,
                         uint16_t,
                         &Option::getType
diff --git a/src/lib/dhcpsrv/tests/lease_mgr_factory_unittest.cc b/src/lib/dhcpsrv/tests/lease_mgr_factory_unittest.cc
index 40ecba3..e343c44 100644
--- a/src/lib/dhcpsrv/tests/lease_mgr_factory_unittest.cc
+++ b/src/lib/dhcpsrv/tests/lease_mgr_factory_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2011-2012 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
diff --git a/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc
index 74a0fb4..a812811 100644
--- a/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc
+++ b/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2011-2012 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
@@ -22,6 +22,8 @@
 #include <iostream>
 #include <sstream>
 
+#include <time.h>
+
 using namespace std;
 using namespace isc;
 using namespace isc::asiolink;
@@ -71,16 +73,6 @@ public:
         return (Lease4Ptr());
     }
 
-    /// @brief Returns existing IPv4 lease for specific address and subnet
-    /// @param addr address of the searched lease
-    /// @param subnet_id ID of the subnet the lease must belong to
-    ///
-    /// @return smart pointer to the lease (or NULL if a lease is not found)
-    virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress&,
-                                SubnetID) const {
-        return (Lease4Ptr());
-    }
-
     /// @brief Returns existing IPv4 leases for specified hardware address.
     ///
     /// Although in the usual case there will be only one lease, for mobile
@@ -177,19 +169,11 @@ public:
 
     /// @brief Deletes a lease.
     ///
-    /// @param addr IPv4 address of the lease to be deleted.
+    /// @param addr Address of the lease to be deleted. (This can be either
+    ///        a V4 address or a V6 address.)
     ///
     /// @return true if deletion was successful, false if no such lease exists
-    virtual bool deleteLease4(const isc::asiolink::IOAddress&) {
-        return (false);
-    }
-
-    /// @brief Deletes a lease.
-    ///
-    /// @param addr IPv4 address of the lease to be deleted.
-    ///
-    /// @return true if deletion was successful, false if no such lease exists
-    virtual bool deleteLease6(const isc::asiolink::IOAddress&) {
+    virtual bool deleteLease(const isc::asiolink::IOAddress&) {
         return (false);
     }
 
@@ -236,16 +220,12 @@ public:
 };
 
 namespace {
-// empty class for now, but may be extended once Addr6 becomes bigger
-class LeaseMgrTest : public ::testing::Test {
-public:
-    LeaseMgrTest() {
-    }
-};
 
-// This test checks if the LeaseMgr can be instantiated and that it
-// parses parameters string properly.
-TEST_F(LeaseMgrTest, getParameter) {
+/// @brief getParameter test
+///
+/// This test checks if the LeaseMgr can be instantiated and that it
+/// parses parameters string properly.
+TEST(LeaseMgr, getParameter) {
 
     LeaseMgr::ParameterMap pmap;
     pmap[std::string("param1")] = std::string("value1");
@@ -261,36 +241,368 @@ TEST_F(LeaseMgrTest, getParameter) {
 // are purely virtual, so we would only call ConcreteLeaseMgr methods.
 // Those methods are just stubs that do not return anything.
 
+/// @brief Lease4 Constructor Test
+///
+/// Lease4 is also defined in lease_mgr.h, so is tested in this file as well.
+// This test checks if the Lease4 structure can be instantiated correctly
+TEST(Lease4, Lease4Constructor) {
+
+    // Random values for the tests
+    const uint8_t HWADDR[] = {0x08, 0x00, 0x2b, 0x02, 0x3f, 0x4e};
+    std::vector<uint8_t> hwaddr(HWADDR, HWADDR + sizeof(HWADDR));
+
+    const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54};
+    std::vector<uint8_t> clientid_vec(CLIENTID, CLIENTID + sizeof(CLIENTID));
+    ClientId clientid(clientid_vec);
+
+    // ...and a time
+    const time_t current_time = time(NULL);
+
+    // Other random constants. 
+    const uint32_t SUBNET_ID = 42;
+    const uint32_t VALID_LIFETIME = 500;
+
+    // We want to check that various addresses work, so let's iterate over
+    // these.
+    const uint32_t ADDRESS[] = {
+        0x00000000, 0x01020304, 0x7fffffff, 0x80000000, 0x80000001, 0xffffffff
+    };
+
+    for (int i = 0; i < sizeof(ADDRESS) / sizeof(ADDRESS[0]); ++i) {
+
+        // Create the lease
+        Lease4 lease(ADDRESS[i], HWADDR, sizeof(HWADDR),
+                     CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, current_time,
+                     SUBNET_ID);
+
+        EXPECT_EQ(ADDRESS[i], static_cast<uint32_t>(lease.addr_));
+        EXPECT_EQ(0, lease.ext_);
+        EXPECT_TRUE(hwaddr == lease.hwaddr_);
+        EXPECT_TRUE(clientid == *lease.client_id_);
+        EXPECT_EQ(0, lease.t1_);
+        EXPECT_EQ(0, lease.t2_);
+        EXPECT_EQ(VALID_LIFETIME, lease.valid_lft_);
+        EXPECT_EQ(current_time, lease.cltt_);
+        EXPECT_EQ(SUBNET_ID, lease.subnet_id_);
+        EXPECT_FALSE(lease.fixed_);
+        EXPECT_TRUE(lease.hostname_.empty());
+        EXPECT_FALSE(lease.fqdn_fwd_);
+        EXPECT_FALSE(lease.fqdn_rev_);
+        EXPECT_TRUE(lease.comments_.empty());
+    }
+}
+
+/// @brief Lease4 Equality Test
+///
+/// Checks that the operator==() correctly compares two leases for equality.
+/// As operator!=() is also defined for this class, every check on operator==()
+/// is followed by the reverse check on operator!=().
+TEST(Lease4, OperatorEquals) {
+
+    // Random values for the tests
+    const uint32_t ADDRESS = 0x01020304;
+    const uint8_t HWADDR[] = {0x08, 0x00, 0x2b, 0x02, 0x3f, 0x4e};
+    std::vector<uint8_t> hwaddr(HWADDR, HWADDR + sizeof(HWADDR));
+    const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54};
+    std::vector<uint8_t> clientid_vec(CLIENTID, CLIENTID + sizeof(CLIENTID));
+    ClientId clientid(clientid_vec);
+    const time_t current_time = time(NULL);
+    const uint32_t SUBNET_ID = 42;
+    const uint32_t VALID_LIFETIME = 500;
+
+    // Check when the leases are equal.
+    Lease4 lease1(ADDRESS, HWADDR, sizeof(HWADDR),
+                  CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, current_time,
+                  SUBNET_ID);
+    Lease4 lease2(ADDRESS, HWADDR, sizeof(HWADDR),
+                  CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, current_time,
+                  SUBNET_ID);
+    EXPECT_TRUE(lease1 == lease2);
+    EXPECT_FALSE(lease1 != lease2);
+
+    // Now vary individual fields in a lease and check that the leases compare
+    // not equal in every case.
+    lease1.addr_ = IOAddress(ADDRESS + 1);
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.addr_ = lease2.addr_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    ++lease1.ext_;
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.ext_ = lease2.ext_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    ++lease1.hwaddr_[0];
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.hwaddr_ = lease2.hwaddr_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    ++clientid_vec[0];
+    lease1.client_id_.reset(new ClientId(clientid_vec));
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    --clientid_vec[0];
+    lease1.client_id_.reset(new ClientId(clientid_vec));
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    ++lease1.t1_;
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.t1_ = lease2.t1_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    ++lease1.t2_;
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.t2_ = lease2.t2_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    ++lease1.valid_lft_;
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.valid_lft_ = lease2.valid_lft_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    ++lease1.cltt_;
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.cltt_ = lease2.cltt_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    ++lease1.subnet_id_;
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.subnet_id_ = lease2.subnet_id_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    lease1.fixed_ = !lease1.fixed_;
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.fixed_ = lease2.fixed_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    lease1.hostname_ += string("Something random");
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.hostname_ = lease2.hostname_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    lease1.fqdn_fwd_ = !lease1.fqdn_fwd_;
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.fqdn_fwd_ = lease2.fqdn_fwd_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    lease1.fqdn_rev_ = !lease1.fqdn_rev_;
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.fqdn_rev_ = lease2.fqdn_rev_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    lease1.comments_ += string("Something random");
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.comments_ = lease2.comments_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+}
+
+
+
 // Lease6 is also defined in lease_mgr.h, so is tested in this file as well.
 // This test checks if the Lease6 structure can be instantiated correctly
 TEST(Lease6, Lease6Constructor) {
 
-    IOAddress addr("2001:db8:1::456");
+    // check a variety of addresses with different bits set.
+    const char* ADDRESS[] = {
+        "::", "::1", "2001:db8:1::456",
+        "7fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
+        "8000::", "8000::1",
+        "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"
+    };
 
+    // Other values
     uint8_t llt[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
     DuidPtr duid(new DUID(llt, sizeof(llt)));
-
     uint32_t iaid = 7; // just a number
-
     SubnetID subnet_id = 8; // just another number
 
-    Lease6Ptr x(new Lease6(Lease6::LEASE_IA_NA, addr,
-                           duid, iaid, 100, 200, 50, 80,
-                           subnet_id));
-
-    EXPECT_TRUE(x->addr_ == addr);
-    EXPECT_TRUE(*x->duid_ == *duid);
-    EXPECT_TRUE(x->iaid_ == iaid);
-    EXPECT_TRUE(x->subnet_id_ == subnet_id);
-    EXPECT_TRUE(x->type_ == Lease6::LEASE_IA_NA);
-    EXPECT_TRUE(x->preferred_lft_ == 100);
-    EXPECT_TRUE(x->valid_lft_ == 200);
-    EXPECT_TRUE(x->t1_ == 50);
-    EXPECT_TRUE(x->t2_ == 80);
+    for (int i = 0; i < sizeof(ADDRESS) / sizeof(ADDRESS[0]); ++i) {
+        IOAddress addr(ADDRESS[i]);
+        Lease6Ptr lease(new Lease6(Lease6::LEASE_IA_NA, addr,
+                               duid, iaid, 100, 200, 50, 80,
+                               subnet_id));
+
+        EXPECT_TRUE(lease->addr_ == addr);
+        EXPECT_TRUE(*lease->duid_ == *duid);
+        EXPECT_TRUE(lease->iaid_ == iaid);
+        EXPECT_TRUE(lease->subnet_id_ == subnet_id);
+        EXPECT_TRUE(lease->type_ == Lease6::LEASE_IA_NA);
+        EXPECT_TRUE(lease->preferred_lft_ == 100);
+        EXPECT_TRUE(lease->valid_lft_ == 200);
+        EXPECT_TRUE(lease->t1_ == 50);
+        EXPECT_TRUE(lease->t2_ == 80);
+    }
 
     // Lease6 must be instantiated with a DUID, not with NULL pointer
+    IOAddress addr(ADDRESS[0]);
     EXPECT_THROW(new Lease6(Lease6::LEASE_IA_NA, addr,
                             DuidPtr(), iaid, 100, 200, 50, 80,
                             subnet_id), InvalidOperation);
 }
+
+/// @brief Lease6 Equality Test
+///
+/// Checks that the operator==() correctly compares two leases for equality.
+/// As operator!=() is also defined for this class, every check on operator==()
+/// is followed by the reverse check on operator!=().
+TEST(Lease6, OperatorEquals) {
+
+    // check a variety of addressemas with different bits set.
+    const IOAddress addr("2001:db8:1::456");
+    uint8_t duid_array[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
+    DuidPtr duid(new DUID(duid_array, sizeof(duid_array)));
+    uint32_t iaid = 7; // just a number
+    SubnetID subnet_id = 8; // just another number
+
+    // Check for equality.
+    Lease6 lease1(Lease6::LEASE_IA_NA, addr, duid, iaid, 100, 200, 50, 80,
+                               subnet_id);
+    Lease6 lease2(Lease6::LEASE_IA_NA, addr, duid, iaid, 100, 200, 50, 80,
+                               subnet_id);
+    EXPECT_TRUE(lease1 == lease2);
+    EXPECT_FALSE(lease1 != lease2);
+
+    // Go through and alter all the fields one by one
+
+    lease1.addr_ = IOAddress("::1");
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.addr_ = lease2.addr_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    lease1.type_ = Lease6::LEASE_IA_PD;
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.type_ = lease2.type_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    ++lease1.prefixlen_;
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.prefixlen_ = lease2.prefixlen_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    ++lease1.iaid_;
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.iaid_ = lease2.iaid_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    ++duid_array[0];
+    lease1.duid_.reset(new DUID(duid_array, sizeof(duid_array)));
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    --duid_array[0];
+    lease1.duid_.reset(new DUID(duid_array, sizeof(duid_array)));
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    ++lease1.preferred_lft_;
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.preferred_lft_ = lease2.preferred_lft_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    ++lease1.valid_lft_;
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.valid_lft_ = lease2.valid_lft_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    ++lease1.t1_;
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.t1_ = lease2.t1_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    ++lease1.t2_;
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.t2_ = lease2.t2_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    ++lease1.cltt_;
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.cltt_ = lease2.cltt_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    ++lease1.subnet_id_;
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.subnet_id_ = lease2.subnet_id_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    lease1.fixed_ = !lease1.fixed_;
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.fixed_ = lease2.fixed_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    lease1.hostname_ += string("Something random");
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.hostname_ = lease2.hostname_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    lease1.fqdn_fwd_ = !lease1.fqdn_fwd_;
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.fqdn_fwd_ = lease2.fqdn_fwd_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    lease1.fqdn_rev_ = !lease1.fqdn_rev_;
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.fqdn_rev_ = lease2.fqdn_rev_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+    lease1.comments_ += string("Something random");
+    EXPECT_FALSE(lease1 == lease2);
+    EXPECT_TRUE(lease1 != lease2);
+    lease1.comments_ = lease2.comments_;
+    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
+    EXPECT_FALSE(lease1 != lease2); // ... leases equal
+}
 }; // end of anonymous namespace
diff --git a/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc
index 0f026f2..08186dc 100644
--- a/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc
+++ b/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc
@@ -53,7 +53,7 @@ TEST_F(MemfileLeaseMgrTest, getTypeAndName) {
     boost::scoped_ptr<Memfile_LeaseMgr> lease_mgr(new Memfile_LeaseMgr(pmap));
 
     EXPECT_EQ(std::string("memfile"), lease_mgr->getType());
-    EXPECT_EQ(std::string("memfile"), lease_mgr->getName());
+    EXPECT_EQ(std::string("memory"), lease_mgr->getName());
 }
 
 // Checks that adding/getting/deleting a Lease6 object works.
@@ -120,10 +120,10 @@ TEST_F(MemfileLeaseMgrTest, addGetDelete6) {
     EXPECT_FALSE(y);
 
     // should return false - there's no such address
-    EXPECT_FALSE(lease_mgr->deleteLease6(IOAddress("2001:db8:1::789")));
+    EXPECT_FALSE(lease_mgr->deleteLease(IOAddress("2001:db8:1::789")));
 
     // this one should succeed
-    EXPECT_TRUE(lease_mgr->deleteLease6(IOAddress("2001:db8:1::456")));
+    EXPECT_TRUE(lease_mgr->deleteLease(IOAddress("2001:db8:1::456")));
 
     // after the lease is deleted, it should really be gone
     x = lease_mgr->getLease6(IOAddress("2001:db8:1::456"));
diff --git a/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc
index 930bc06..746ef00 100644
--- a/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc
+++ b/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2011-2012 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,6 +20,7 @@
 
 #include <gtest/gtest.h>
 
+#include <algorithm>
 #include <iostream>
 #include <sstream>
 #include <string>
@@ -32,21 +33,24 @@ using namespace std;
 
 namespace {
 
-// Creation of the schema
+// This holds statements to create and destroy the schema.
 #include "schema_copy.h"
 
-// IPv6 addresseses
-const char* ADDRESS_0 = "2001:db8::0";
-const char* ADDRESS_1 = "2001:db8::1";
-const char* ADDRESS_2 = "2001:db8::2";
-const char* ADDRESS_3 = "2001:db8::3";
-const char* ADDRESS_4 = "2001:db8::4";
-const char* ADDRESS_5 = "2001:db8::5";
-const char* ADDRESS_6 = "2001:db8::6";
-const char* ADDRESS_7 = "2001:db8::7";
-
-// Connection strings.  Assume:
+// IPv4 and IPv6 addresses used in the tests
+const char* ADDRESS4[] = {
+    "192.0.2.0", "192.0.2.1", "192.0.2.2", "192.0.2.3",
+    "192.0.2.4", "192.0.2.5", "192.0.2.6", "192.0.2.7",
+    NULL
+};
+const char* ADDRESS6[] = {
+    "2001:db8::0", "2001:db8::1", "2001:db8::2", "2001:db8::3",
+    "2001:db8::4", "2001:db8::5", "2001:db8::6", "2001:db8::7",
+    NULL
+};
+
+// Connection strings.
 // Database: keatest
+// Host: localhost
 // Username: keatest
 // Password: keatest
 const char* VALID_TYPE = "type=mysql";
@@ -69,7 +73,6 @@ string connectionString(const char* type, const char* name, const char* host,
     if (type != NULL) {
         result += string(type);
     }
-
     if (name != NULL) {
         if (! result.empty()) {
             result += space;
@@ -111,7 +114,7 @@ validConnectionString() {
 // @brief Clear everything from the database
 //
 // There is no error checking in this code: if something fails, one of the
-// tests will fall over.
+// tests will (should) fall over.
 void destroySchema() {
     // Initialise
     MYSQL handle;
@@ -145,7 +148,7 @@ void createSchema() {
     (void) mysql_real_connect(&handle, "localhost", "keatest",
                               "keatest", "keatest", 0, NULL, 0);
 
-    // Get rid of everything in it.
+    // Execute creation statements.
     for (int i = 0; create_statement[i] != NULL; ++i) {
         (void) mysql_query(&handle, create_statement[i]);
     }
@@ -154,32 +157,37 @@ void createSchema() {
     (void) mysql_close(&handle);
 }
 
-// Note: Doxygen "///" not used - even though Doxygen is used to
-// document class and methods - to avoid the comments appearing
-// in the programming manual.
-
-// @brief Test Fixture Class
-//
-// Opens the database prior to each test and closes it afterwards.
-// All pending transactions are deleted prior to closure.
+/// @brief Test fixture class for testing MySQL Lease Manager
+///
+/// Opens the database prior to each test and closes it afterwards.
+/// All pending transactions are deleted prior to closure.
 
 class MySqlLeaseMgrTest : public ::testing::Test {
 public:
-    // @brief Constructor
-    //
-    // Deletes everything from the database and opens it.
-    MySqlLeaseMgrTest() :
-        L0_ADDRESS(ADDRESS_0), L0_IOADDRESS(L0_ADDRESS), 
-        L1_ADDRESS(ADDRESS_1), L1_IOADDRESS(L1_ADDRESS), 
-        L2_ADDRESS(ADDRESS_2), L2_IOADDRESS(L2_ADDRESS), 
-        L3_ADDRESS(ADDRESS_3), L3_IOADDRESS(L3_ADDRESS), 
-        L4_ADDRESS(ADDRESS_4), L4_IOADDRESS(L4_ADDRESS), 
-        L5_ADDRESS(ADDRESS_5), L5_IOADDRESS(L5_ADDRESS), 
-        L6_ADDRESS(ADDRESS_6), L6_IOADDRESS(L6_ADDRESS), 
-        L7_ADDRESS(ADDRESS_7), L7_IOADDRESS(L7_ADDRESS) {
+    /// @brief Constructor
+    ///
+    /// Deletes everything from the database and opens it.
+    MySqlLeaseMgrTest() {
+        // Initialize address strings and IOAddresses
+        for (int i = 0; ADDRESS4[i] != NULL; ++i) {
+            string addr(ADDRESS4[i]);
+            straddress4_.push_back(addr);
+            IOAddress ioaddr(addr);
+            ioaddress4_.push_back(ioaddr);
+        }
 
+        for (int i = 0; ADDRESS6[i] != NULL; ++i) {
+            string addr(ADDRESS6[i]);
+            straddress6_.push_back(addr);
+            IOAddress ioaddr(addr);
+            ioaddress6_.push_back(ioaddr);
+        }
+
+        // Ensure schema is the correct one.
         destroySchema();
         createSchema();
+
+        // Connect to the database
         try {
             LeaseMgrFactory::create(validConnectionString());
         } catch (...) {
@@ -193,38 +201,146 @@ public:
         lmptr_ = &(LeaseMgrFactory::instance());
     }
 
-    // @brief Destructor
-    //
-    // Rolls back all pending transactions.  The deletion of the
-    // lmptr_ member variable will close the database.  Then
-    // reopen it and delete everything created by the test.
+    /// @brief Destructor
+    ///
+    /// Rolls back all pending transactions.  The deletion of lmptr_ will close
+    /// the database.  Then reopen it and delete everything created by the test.
     virtual ~MySqlLeaseMgrTest() {
         lmptr_->rollback();
         LeaseMgrFactory::destroy();
         destroySchema();
     }
 
-    // @brief Reopen the database
-    //
-    // Closes the database and re-open it.  Anything committed should be
-    // visible.
+    /// @brief Reopen the database
+    ///
+    /// Closes the database and re-open it.  Anything committed should be
+    /// visible.
     void reopen() {
         LeaseMgrFactory::destroy();
         LeaseMgrFactory::create(validConnectionString());
         lmptr_ = &(LeaseMgrFactory::instance());
     }
 
-    // @brief Initialize Lease6 Fields
-    //
-    // Returns a pointer to a Lease6 structure.  Different values are put
-    // in the lease according to the address passed.
-    //
-    // This is just a convenience function for the test methods.
-    //
-    // @param address Address to use for the initialization
-    //
-    // @return Lease6Ptr.  This will not point to anything if the initialization
-    //         failed (e.g. unknown address).
+    /// @brief Initialize Lease4 Fields
+    ///
+    /// Returns a pointer to a Lease4 structure.  Different values are put into
+    /// the lease according to the address passed.
+    ///
+    /// This is just a convenience function for the test methods.
+    ///
+    /// @param address Address to use for the initialization
+    ///
+    /// @return Lease4Ptr.  This will not point to anything if the
+    ///         initialization failed (e.g. unknown address).
+    Lease4Ptr initializeLease4(std::string address) {
+        Lease4Ptr lease(new Lease4());
+
+        // Set the address of the lease
+        lease->addr_ = IOAddress(address);
+
+        // Initialize unused fields.
+        lease->ext_ = 0;                            // Not saved
+        lease->t1_ = 0;                             // Not saved
+        lease->t2_ = 0;                             // Not saved
+        lease->fixed_ = false;                      // Unused
+        lease->hostname_ = std::string("");         // Unused
+        lease->fqdn_fwd_ = false;                   // Unused
+        lease->fqdn_rev_ = false;                   // Unused
+        lease->comments_ = std::string("");         // Unused
+
+        // Set other parameters.  For historical reasons, address 0 is not used.
+        if (address == straddress4_[0]) {
+            lease->hwaddr_ = vector<uint8_t>(6, 0x08);
+            lease->client_id_ = ClientIdPtr(
+                new ClientId(vector<uint8_t>(8, 0x42)));
+            lease->valid_lft_ = 8677;
+            lease->cltt_ = 168256;
+            lease->subnet_id_ = 23;
+
+        } else if (address == straddress4_[1]) {
+            lease->hwaddr_ = vector<uint8_t>(6, 0x19);
+            lease->client_id_ = ClientIdPtr(
+                new ClientId(vector<uint8_t>(8, 0x53)));
+            lease->valid_lft_ = 3677;
+            lease->cltt_ = 123456;
+            lease->subnet_id_ = 73;
+
+        } else if (address == straddress4_[2]) {
+            lease->hwaddr_ = vector<uint8_t>(6, 0x2a);
+            lease->client_id_ = ClientIdPtr(
+                new ClientId(vector<uint8_t>(8, 0x64)));
+            lease->valid_lft_ = 5412;
+            lease->cltt_ = 234567;
+            lease->subnet_id_ = 73;                         // Same as lease 1
+
+        } else if (address == straddress4_[3]) {
+            lease->hwaddr_ = vector<uint8_t>(6, 0x19);      // Same as lease 1
+            lease->client_id_ = ClientIdPtr(
+                new ClientId(vector<uint8_t>(8, 0x75)));
+
+            // The times used in the next tests are deliberately restricted - we
+            // should be able to cope with valid lifetimes up to 0xffffffff.
+            //  However, this will lead to overflows.
+            // @TODO: test overflow conditions when code has been fixed
+            lease->valid_lft_ = 7000;
+            lease->cltt_ = 234567;
+            lease->subnet_id_ = 37;
+
+        } else if (address == straddress4_[4]) {
+            lease->hwaddr_ = vector<uint8_t>(6, 0x4c);
+            // Same ClientId as straddr4_[1]
+            lease->client_id_ = ClientIdPtr(
+                new ClientId(vector<uint8_t>(8, 0x53)));    // Same as lease 1
+            lease->valid_lft_ = 7736;
+            lease->cltt_ = 222456;
+            lease->subnet_id_ = 85;
+
+        } else if (address == straddress4_[5]) {
+            lease->hwaddr_ = vector<uint8_t>(6, 0x19);      // Same as lease 1
+            // Same ClientId and IAID as straddress4_1
+            lease->client_id_ = ClientIdPtr(
+                new ClientId(vector<uint8_t>(8, 0x53)));    // Same as lease 1
+            lease->valid_lft_ = 7832;
+            lease->cltt_ = 227476;
+            lease->subnet_id_ = 175;
+
+        } else if (address == straddress4_[6]) {
+            lease->hwaddr_ = vector<uint8_t>(6, 0x6e);
+            // Same ClientId as straddress4_1
+            lease->client_id_ = ClientIdPtr(
+                new ClientId(vector<uint8_t>(8, 0x53)));    // Same as lease 1
+            lease->valid_lft_ = 1832;
+            lease->cltt_ = 627476;
+            lease->subnet_id_ = 112;
+
+        } else if (address == straddress4_[7]) {
+            lease->hwaddr_ = vector<uint8_t>();             // Empty
+            lease->client_id_ = ClientIdPtr(
+                new ClientId(vector<uint8_t>()));           // Empty
+            lease->valid_lft_ = 7975;
+            lease->cltt_ = 213876;
+            lease->subnet_id_ = 19;
+
+        } else {
+            // Unknown address, return an empty pointer.
+            lease.reset();
+
+        }
+
+        return (lease);
+    }
+
+    /// @brief Initialize Lease6 Fields
+    ///
+    /// Returns a pointer to a Lease6 structure.  Different values are put into
+    /// the lease according to the address passed.
+    ///
+    /// This is just a convenience function for the test methods.
+    ///
+    /// @param address Address to use for the initialization
+    ///
+    /// @return Lease6Ptr.  This will not point to anything if the initialization
+    ///         failed (e.g. unknown address).
     Lease6Ptr initializeLease6(std::string address) {
         Lease6Ptr lease(new Lease6());
 
@@ -240,38 +356,38 @@ public:
         lease->fqdn_rev_ = false;                   // Unused
         lease->comments_ = std::string("");         // Unused
 
-        // Set the other parameters.  For historical reasons, L0_ADDRESS is not used.
-        if (address == L0_ADDRESS) {
+        // Set other parameters.  For historical reasons, address 0 is not used.
+        if (address == straddress6_[0]) {
             lease->type_ = Lease6::LEASE_IA_TA;
             lease->prefixlen_ = 4;
             lease->iaid_ = 142;
-            lease->duid_ = boost::shared_ptr<DUID>(new DUID(vector<uint8_t>(8, 0x77)));
-            lease->preferred_lft_ = 900;   // Preferred lifetime
-            lease->valid_lft_ = 8677;      // Actual lifetime
-            lease->cltt_ = 168256;         // Current time of day
-            lease->subnet_id_ = 23;        // Arbitrary number
+            lease->duid_ = DuidPtr(new DUID(vector<uint8_t>(8, 0x77)));
+            lease->preferred_lft_ = 900;
+            lease->valid_lft_ = 8677;
+            lease->cltt_ = 168256;
+            lease->subnet_id_ = 23;
 
-        } else if (address == L1_ADDRESS) {
+        } else if (address == straddress6_[1]) {
             lease->type_ = Lease6::LEASE_IA_TA;
             lease->prefixlen_ = 0;
             lease->iaid_ = 42;
-            lease->duid_ = boost::shared_ptr<DUID>(new DUID(vector<uint8_t>(8, 0x42)));
-            lease->preferred_lft_ = 3600;  // Preferred lifetime
-            lease->valid_lft_ = 3677;      // Actual lifetime
-            lease->cltt_ = 123456;         // Current time of day
-            lease->subnet_id_ = 73;        // Arbitrary number
+            lease->duid_ = DuidPtr(new DUID(vector<uint8_t>(8, 0x42)));
+            lease->preferred_lft_ = 3600;
+            lease->valid_lft_ = 3677;
+            lease->cltt_ = 123456;
+            lease->subnet_id_ = 73;
 
-        } else if (address == L2_ADDRESS) {
+        } else if (address == straddress6_[2]) {
             lease->type_ = Lease6::LEASE_IA_PD;
             lease->prefixlen_ = 7;
             lease->iaid_ = 89;
-            lease->duid_ = boost::shared_ptr<DUID>(new DUID(vector<uint8_t>(8, 0x3a)));
-            lease->preferred_lft_ = 1800;  // Preferred lifetime
-            lease->valid_lft_ = 5412;      // Actual lifetime
-            lease->cltt_ = 234567;         // Current time of day
-            lease->subnet_id_ = 73;        // Same as for L1_ADDRESS
+            lease->duid_ = DuidPtr(new DUID(vector<uint8_t>(8, 0x3a)));
+            lease->preferred_lft_ = 1800;
+            lease->valid_lft_ = 5412;
+            lease->cltt_ = 234567;
+            lease->subnet_id_ = 73;                     // Same as lease 1
 
-        } else if (address == L3_ADDRESS) {
+        } else if (address == straddress6_[3]) {
             lease->type_ = Lease6::LEASE_IA_NA;
             lease->prefixlen_ = 28;
             lease->iaid_ = 0xfffffffe;
@@ -279,60 +395,62 @@ public:
             for (uint8_t i = 31; i < 126; ++i) {
                 duid.push_back(i);
             }
-            lease->duid_ = boost::shared_ptr<DUID>(new DUID(duid));
+            lease->duid_ = DuidPtr(new DUID(duid));
 
             // The times used in the next tests are deliberately restricted - we
             // should be able to cope with valid lifetimes up to 0xffffffff.
             //  However, this will lead to overflows.
             // @TODO: test overflow conditions when code has been fixed
-            lease->preferred_lft_ = 7200;  // Preferred lifetime
-            lease->valid_lft_ = 7000;      // Actual lifetime
-            lease->cltt_ = 234567;         // Current time of day
-            lease->subnet_id_ = 37;        // Different from L1 and L2
+            lease->preferred_lft_ = 7200;
+            lease->valid_lft_ = 7000;
+            lease->cltt_ = 234567;
+            lease->subnet_id_ = 37;
 
-        } else if (address == L4_ADDRESS) {
-            // Same DUID and IAID as L1_ADDRESS
+        } else if (address == straddress6_[4]) {
+            // Same DUID and IAID as straddress6_1
             lease->type_ = Lease6::LEASE_IA_PD;
             lease->prefixlen_ = 15;
             lease->iaid_ = 42;
-            lease->duid_ = boost::shared_ptr<DUID>(new DUID(vector<uint8_t>(8, 0x42)));
-            lease->preferred_lft_ = 4800;  // Preferred lifetime
-            lease->valid_lft_ = 7736;      // Actual lifetime
-            lease->cltt_ = 222456;         // Current time of day
-            lease->subnet_id_ = 75;        // Arbitrary number
-
-        } else if (address == L5_ADDRESS) {
-            // Same DUID and IAID as L1_ADDRESS
+            lease->duid_ = DuidPtr(new DUID(vector<uint8_t>(8, 0x42)));
+            lease->preferred_lft_ = 4800;
+            lease->valid_lft_ = 7736;
+            lease->cltt_ = 222456;
+            lease->subnet_id_ = 671;
+
+        } else if (address == straddress6_[5]) {
+            // Same DUID and IAID as straddress6_1
             lease->type_ = Lease6::LEASE_IA_PD;
             lease->prefixlen_ = 24;
-            lease->iaid_ = 42;
-            lease->duid_ = boost::shared_ptr<DUID>(new DUID(vector<uint8_t>(8, 0x42)));
-            lease->preferred_lft_ = 5400;  // Preferred lifetime
-            lease->valid_lft_ = 7832;      // Actual lifetime
-            lease->cltt_ = 227476;         // Current time of day
-            lease->subnet_id_ = 175;       // Arbitrary number
-
-        } else if (address == L6_ADDRESS) {
-            // Same DUID as L1_ADDRESS
+            lease->iaid_ = 42;                          // Same as lease 4
+            lease->duid_ = DuidPtr(new DUID(vector<uint8_t>(8, 0x42)));
+                                                        // Same as lease 4
+            lease->preferred_lft_ = 5400;
+            lease->valid_lft_ = 7832;
+            lease->cltt_ = 227476;
+            lease->subnet_id_ = 175;
+
+        } else if (address == straddress6_[6]) {
+            // Same DUID as straddress6_1
             lease->type_ = Lease6::LEASE_IA_PD;
             lease->prefixlen_ = 24;
             lease->iaid_ = 93;
-            lease->duid_ = boost::shared_ptr<DUID>(new DUID(vector<uint8_t>(8, 0x42)));
-            lease->preferred_lft_ = 5400;  // Preferred lifetime
-            lease->valid_lft_ = 1832;      // Actual lifetime
-            lease->cltt_ = 627476;         // Current time of day
-            lease->subnet_id_ = 112;       // Arbitrary number
-
-        } else if (address == L7_ADDRESS) {
-            // Same IAID as L1_ADDRESS
+            lease->duid_ = DuidPtr(new DUID(vector<uint8_t>(8, 0x42)));
+                                                        // Same as lease 4
+            lease->preferred_lft_ = 5400;
+            lease->valid_lft_ = 1832;
+            lease->cltt_ = 627476;
+            lease->subnet_id_ = 112;
+
+        } else if (address == straddress6_[7]) {
+            // Same IAID as straddress6_1
             lease->type_ = Lease6::LEASE_IA_PD;
             lease->prefixlen_ = 24;
             lease->iaid_ = 42;
-            lease->duid_ = boost::shared_ptr<DUID>(new DUID(vector<uint8_t>(8, 0xe5)));
-            lease->preferred_lft_ = 5600;  // Preferred lifetime
-            lease->valid_lft_ = 7975;      // Actual lifetime
-            lease->cltt_ = 213876;         // Current time of day
-            lease->subnet_id_ = 19;        // Arbitrary number
+            lease->duid_ = DuidPtr(new DUID(vector<uint8_t>(8, 0xe5)));
+            lease->preferred_lft_ = 5600;
+            lease->valid_lft_ = 7975;
+            lease->cltt_ = 213876;
+            lease->subnet_id_ = 19;
 
         } else {
             // Unknown address, return an empty pointer.
@@ -343,78 +461,130 @@ public:
         return (lease);
     }
 
-    // @brief Creates Leases for the test
-    //
-    // Creates all leases for the test and checks that they are different.
-    //
-    // @return vector<Lease6Ptr> Vector of pointers to leases
-    vector<Lease6Ptr> createLeases6() {
-
-        // Create leases
-        vector<Lease6Ptr> leases;
-        leases.push_back(initializeLease6(L0_ADDRESS));
-        leases.push_back(initializeLease6(L1_ADDRESS));
-        leases.push_back(initializeLease6(L2_ADDRESS));
-        leases.push_back(initializeLease6(L3_ADDRESS));
-        leases.push_back(initializeLease6(L4_ADDRESS));
-        leases.push_back(initializeLease6(L5_ADDRESS));
-        leases.push_back(initializeLease6(L6_ADDRESS));
-        leases.push_back(initializeLease6(L7_ADDRESS));
-
-        EXPECT_EQ(8, leases.size());
+    /// @brief Check Leases present and different
+    ///
+    /// Checks a vector of lease pointers and ensures that all the leases
+    /// they point to are present and different.  If not, a GTest assertion
+    /// will fail.
+    ///
+    /// @param leases Vector of pointers to leases
+    template <typename T>
+    void checkLeasesDifferent(const std::vector<T>& leases) const {
 
         // Check they were created
         for (int i = 0; i < leases.size(); ++i) {
-            EXPECT_TRUE(leases[i]);
+            ASSERT_TRUE(leases[i]);
         }
 
         // Check they are different
         for (int i = 0; i < (leases.size() - 1); ++i) {
             for (int j = (i + 1); j < leases.size(); ++j) {
-                EXPECT_TRUE(leases[i] != leases[j]);
+                stringstream s;
+                s << "Comparing leases " << i << " & " << j << " for equality";
+                SCOPED_TRACE(s.str());
+                EXPECT_TRUE(*leases[i] != *leases[j]);
             }
         }
+    }
+
+    /// @brief Creates leases for the test
+    ///
+    /// Creates all leases for the test and checks that they are different.
+    ///
+    /// @return vector<Lease4Ptr> Vector of pointers to leases
+    vector<Lease4Ptr> createLeases4() {
+
+        // Create leases for each address
+        vector<Lease4Ptr> leases;
+        for (int i = 0; i < straddress4_.size(); ++i) {
+            leases.push_back(initializeLease4(straddress4_[i]));
+        }
+        EXPECT_EQ(8, leases.size());
+
+        // Check all were created and that they are different.
+        checkLeasesDifferent(leases);
 
         return (leases);
     }
 
+    /// @brief Creates leases for the test
+    ///
+    /// Creates all leases for the test and checks that they are different.
+    ///
+    /// @return vector<Lease6Ptr> Vector of pointers to leases
+    vector<Lease6Ptr> createLeases6() {
 
-    // Member variables
+        // Create leases for each address
+        vector<Lease6Ptr> leases;
+        for (int i = 0; i < straddress6_.size(); ++i) {
+            leases.push_back(initializeLease6(straddress6_[i]));
+        }
+        EXPECT_EQ(8, leases.size());
+
+        // Check all were created and that they are different.
+        checkLeasesDifferent(leases);
 
-    LeaseMgr*   lmptr_;         // Pointer to the lease manager
+        return (leases);
+    }
 
-    string L0_ADDRESS;          // String form of address 1
-    IOAddress L0_IOADDRESS;     // IOAddress form of L1_ADDRESS
 
-    string L1_ADDRESS;          // String form of address 1
-    IOAddress L1_IOADDRESS;     // IOAddress form of L1_ADDRESS
+    // Member variables
 
-    string L2_ADDRESS;          // String form of address 2
-    IOAddress L2_IOADDRESS;     // IOAddress form of L2_ADDRESS
+    LeaseMgr*   lmptr_;             ///< Pointer to the lease manager
+    vector<string>  straddress4_;   ///< String forms of IPv4 addresses
+    vector<IOAddress> ioaddress4_;  ///< IOAddress forms of IPv4 addresses
+    vector<string>  straddress6_;   ///< String forms of IPv6 addresses
+    vector<IOAddress> ioaddress6_;  ///< IOAddress forms of IPv6 addresses
+};
 
-    string L3_ADDRESS;          // String form of address 3
-    IOAddress L3_IOADDRESS;     // IOAddress form of L3_ADDRESS
+///@{
+/// @brief Test Utilities
+///
+/// The follow are a set of functions used during the tests.
 
-    string L4_ADDRESS;          // String form of address 4
-    IOAddress L4_IOADDRESS;     // IOAddress form of L4_ADDRESS
+/// @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_);
+}
 
-    string L5_ADDRESS;          // String form of address 5
-    IOAddress L5_IOADDRESS;     // IOAddress form of L5_ADDRESS
+/// @brief Compare two Lease6 structures for equality
+void
+detailCompareLease(const Lease6Ptr& first, const Lease6Ptr& second) {
+    EXPECT_EQ(first->type_, second->type_);
 
-    string L6_ADDRESS;          // String form of address 6
-    IOAddress L6_IOADDRESS;     // IOAddress form of L6_ADDRESS
+    // 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_);
+}
 
-    string L7_ADDRESS;          // String form of address 7
-    IOAddress L7_IOADDRESS;     // IOAddress form of L7_ADDRESS
-};
+///@}
 
 
-// @brief Check that Database Can Be Opened
-//
-// This test checks if the MySqlLeaseMgr can be instantiated.  This happens
-// only if the database can be opened.  Note that this is not part of the
-// MySqlLeaseMgr test fixure set.  This test checks that the database can be
-// opened: the fixtures assume that and check basic operations.
+/// @brief Check that database can be opened
+///
+/// This test checks if the MySqlLeaseMgr can be instantiated.  This happens
+/// only if the database can be opened.  Note that this is not part of the
+/// MySqlLeaseMgr test fixure set.  This test checks that the database can be
+/// opened: the fixtures assume that and check basic operations.
 
 TEST(MySqlOpenTest, OpenDatabase) {
 
@@ -472,25 +642,25 @@ TEST(MySqlOpenTest, OpenDatabase) {
     destroySchema();
 }
 
-// @brief Check the getType() method
-//
-// getType() returns a string giving the type of the backend, which should
-// always be "mysql".
+/// @brief Check the getType() method
+///
+/// getType() returns a string giving the type of the backend, which should
+/// always be "mysql".
 TEST_F(MySqlLeaseMgrTest, getType) {
     EXPECT_EQ(std::string("mysql"), lmptr_->getType());
 }
 
-// @brief Check conversion functions
-//
-// The server works using cltt and valid_filetime.  In the database, the
-// information is stored as expire_time and valid-lifetime, which are
-// related by
-//
-// expire_time = cltt + valid_lifetime
-//
-// This test checks that the conversion is correct.  It does not check that the
-// data is entered into the database correctly, only that the MYSQL_TIME
-// structure used for the entry is correctly set up.
+/// @brief Check conversion functions
+///
+/// The server works using cltt and valid_filetime.  In the database, the
+/// information is stored as expire_time and valid-lifetime, which are
+/// related by
+///
+/// expire_time = cltt + valid_lifetime
+///
+/// This test checks that the conversion is correct.  It does not check that the
+/// data is entered into the database correctly, only that the MYSQL_TIME
+/// structure used for the entry is correctly set up.
 TEST_F(MySqlLeaseMgrTest, checkTimeConversion) {
     const time_t cltt = time(NULL);
     const uint32_t valid_lft = 86400;       // 1 day
@@ -522,14 +692,12 @@ TEST_F(MySqlLeaseMgrTest, checkTimeConversion) {
 }
 
 
-// @brief Check getName() returns correct database name
+/// @brief Check getName() returns correct database name
 TEST_F(MySqlLeaseMgrTest, getName) {
     EXPECT_EQ(std::string("keatest"), lmptr_->getName());
-
-    // @TODO: check for the negative
 }
 
-// @brief Check that getVersion() returns the expected version
+/// @brief Check that getVersion() returns the expected version
 TEST_F(MySqlLeaseMgrTest, checkVersion) {
     // Check version
     pair<uint32_t, uint32_t> version;
@@ -538,34 +706,56 @@ TEST_F(MySqlLeaseMgrTest, checkVersion) {
     EXPECT_EQ(CURRENT_VERSION_MINOR, version.second);
 }
 
-// @brief Compare two Lease6 structures for equality
-void
-detailCompareLease6(const Lease6Ptr& first, const Lease6Ptr& second) {
-    EXPECT_EQ(first->type_, second->type_);
+/// @brief Basic Lease4 Checks
+///
+/// Checks that the addLease, getLease4 (by address) and deleteLease (with an
+/// IPv4 address) works.
+TEST_F(MySqlLeaseMgrTest, basicLease4) {
+    // Get the leases to be used for the test.
+    vector<Lease4Ptr> leases = createLeases4();
 
-    // 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_);
-}
+    // Start the tests.  Add three leases to the database, read them back and
+    // check they are what we think they are.
+    EXPECT_TRUE(lmptr_->addLease(leases[1]));
+    EXPECT_TRUE(lmptr_->addLease(leases[2]));
+    EXPECT_TRUE(lmptr_->addLease(leases[3]));
+    lmptr_->commit();
+
+    // Reopen the database to ensure that they actually got stored.
+    reopen();
 
+    Lease4Ptr l_returned = lmptr_->getLease4(ioaddress4_[1]);
+    ASSERT_TRUE(l_returned);
+    detailCompareLease(leases[1], l_returned);
 
-// @brief Check individual Lease6 methods
-//
-// Checks that the add/update/delete works.  All are done within one
-// test so that "rollback" can be used to remove trace of the tests
-// from the database.
-//
-// Tests where a collection of leases can be returned are in the test
-// Lease6Collection.
+    l_returned = lmptr_->getLease4(ioaddress4_[2]);
+    ASSERT_TRUE(l_returned);
+    detailCompareLease(leases[2], l_returned);
+
+    l_returned = lmptr_->getLease4(ioaddress4_[3]);
+    ASSERT_TRUE(l_returned);
+    detailCompareLease(leases[3], l_returned);
+
+    // Check that we can't add a second lease with the same address
+    EXPECT_FALSE(lmptr_->addLease(leases[1]));
+
+    // Delete a lease, check that it's gone, and that we can't delete it
+    // a second time.
+    EXPECT_TRUE(lmptr_->deleteLease(ioaddress4_[1]));
+    l_returned = lmptr_->getLease4(ioaddress4_[1]);
+    EXPECT_FALSE(l_returned);
+    EXPECT_FALSE(lmptr_->deleteLease(ioaddress4_[1]));
+
+    // Check that the second address is still there.
+    l_returned = lmptr_->getLease4(ioaddress4_[2]);
+    ASSERT_TRUE(l_returned);
+    detailCompareLease(leases[2], l_returned);
+}
+
+/// @brief Basic Lease6 Checks
+///
+/// Checks that the addLease, getLease6 (by address) and deleteLease (with an
+/// IPv6 address) works.
 TEST_F(MySqlLeaseMgrTest, basicLease6) {
     // Get the leases to be used for the test.
     vector<Lease6Ptr> leases = createLeases6();
@@ -580,42 +770,321 @@ TEST_F(MySqlLeaseMgrTest, basicLease6) {
     // Reopen the database to ensure that they actually got stored.
     reopen();
 
-    Lease6Ptr l_returned = lmptr_->getLease6(L1_IOADDRESS);
-    EXPECT_TRUE(l_returned);
-    detailCompareLease6(leases[1], l_returned);
+    Lease6Ptr l_returned = lmptr_->getLease6(ioaddress6_[1]);
+    ASSERT_TRUE(l_returned);
+    detailCompareLease(leases[1], l_returned);
 
-    l_returned = lmptr_->getLease6(L2_IOADDRESS);
-    EXPECT_TRUE(l_returned);
-    detailCompareLease6(leases[2], l_returned);
+    l_returned = lmptr_->getLease6(ioaddress6_[2]);
+    ASSERT_TRUE(l_returned);
+    detailCompareLease(leases[2], l_returned);
 
-    l_returned = lmptr_->getLease6(L3_IOADDRESS);
-    EXPECT_TRUE(l_returned);
-    detailCompareLease6(leases[3], l_returned);
+    l_returned = lmptr_->getLease6(ioaddress6_[3]);
+    ASSERT_TRUE(l_returned);
+    detailCompareLease(leases[3], l_returned);
 
     // Check that we can't add a second lease with the same address
     EXPECT_FALSE(lmptr_->addLease(leases[1]));
 
     // Delete a lease, check that it's gone, and that we can't delete it
     // a second time.
-    EXPECT_TRUE(lmptr_->deleteLease6(L1_IOADDRESS));
-    l_returned = lmptr_->getLease6(L1_IOADDRESS);
+    EXPECT_TRUE(lmptr_->deleteLease(ioaddress6_[1]));
+    l_returned = lmptr_->getLease6(ioaddress6_[1]);
     EXPECT_FALSE(l_returned);
-    EXPECT_FALSE(lmptr_->deleteLease6(L1_IOADDRESS));
+    EXPECT_FALSE(lmptr_->deleteLease(ioaddress6_[1]));
 
     // Check that the second address is still there.
-    l_returned = lmptr_->getLease6(L2_IOADDRESS);
-    EXPECT_TRUE(l_returned);
-    detailCompareLease6(leases[2], l_returned);
+    l_returned = lmptr_->getLease6(ioaddress6_[2]);
+    ASSERT_TRUE(l_returned);
+    detailCompareLease(leases[2], l_returned);
+}
+
+/// @brief Check GetLease4 methods - access by Hardware Address
+///
+/// Adds leases to the database and checks that they can be accessed via
+/// a combination of DIUID and IAID.
+TEST_F(MySqlLeaseMgrTest, getLease4Hwaddr) {
+    // Get the leases to be used for the test and add to the database
+    vector<Lease4Ptr> leases = createLeases4();
+    for (int i = 0; i < leases.size(); ++i) {
+        EXPECT_TRUE(lmptr_->addLease(leases[i]));
+    }
+
+    // Get the leases matching the hardware address of lease 1
+    Lease4Collection returned = lmptr_->getLease4(leases[1]->hwaddr_);
+
+    // Should be three leases, matching leases[1], [3] and [5].
+    ASSERT_EQ(3, returned.size());
+
+    // Easiest way to check is to look at the addresses.
+    vector<string> addresses;
+    for (Lease4Collection::const_iterator i = returned.begin();
+         i != returned.end(); ++i) {
+        addresses.push_back((*i)->addr_.toText());
+    }
+    sort(addresses.begin(), addresses.end());
+    EXPECT_EQ(straddress4_[1], addresses[0]);
+    EXPECT_EQ(straddress4_[3], addresses[1]);
+    EXPECT_EQ(straddress4_[5], addresses[2]);
+
+    // Repeat test with just one expected match
+    returned = lmptr_->getLease4(leases[2]->hwaddr_);
+    EXPECT_EQ(1, returned.size());
+    detailCompareLease(leases[2], *returned.begin());
+
+    // Check that an empty vector is valid
+    EXPECT_TRUE(leases[7]->hwaddr_.empty());
+    returned = lmptr_->getLease4(leases[7]->hwaddr_);
+    EXPECT_EQ(1, returned.size());
+    detailCompareLease(leases[7], *returned.begin());
+
+    // Try to get something with invalid hardware address
+    vector<uint8_t> invalid(6, 0);
+    returned = lmptr_->getLease4(invalid);
+    EXPECT_EQ(0, returned.size());
+}
+
+// @brief Get lease4 by hardware address (2)
+//
+// Check that the system can cope with getting a hardware address of
+// any size.
+TEST_F(MySqlLeaseMgrTest, getLease4HwaddrSize) {
+
+    // Create leases, although we need only one.
+    vector<Lease4Ptr> leases = createLeases4();
+
+    // Now add leases with increasing hardware address size.
+    for (uint8_t i = 0; i <= Lease4::HWADDR_MAX; ++i) {
+        leases[1]->hwaddr_.resize(i, i);
+        EXPECT_TRUE(lmptr_->addLease(leases[1]));
+        Lease4Collection returned = lmptr_->getLease4(leases[1]->hwaddr_);
+        ASSERT_EQ(1, returned.size());
+        detailCompareLease(leases[1], *returned.begin());
+        (void) lmptr_->deleteLease(leases[1]->addr_);
+    }
+
+    // Expect some problem when accessing a lease that had too long a hardware
+    // address. (The 42 is a random value put in each byte of the address.)
+    // In fact the address is stored in a truncated form, so we won't find it
+    // when we look.
+    // @todo Check if there is some way of detecting that data added
+    //       to the database is truncated.  There does not appear to
+    //       be any indication in the C API.
+    leases[1]->hwaddr_.resize(Lease4::HWADDR_MAX + 100, 42);
+    EXPECT_TRUE(lmptr_->addLease(leases[1]));
+    Lease4Collection returned = lmptr_->getLease4(leases[1]->hwaddr_);
+    EXPECT_EQ(0, returned.size());
+}
+
+/// @brief Check GetLease4 methods - access by Hardware Address & Subnet ID
+///
+/// Adds leases to the database and checks that they can be accessed via
+/// a combination of hardware address and subnet ID
+TEST_F(MySqlLeaseMgrTest, getLease4HwaddrSubnetId) {
+    // Get the leases to be used for the test and add to the database
+    vector<Lease4Ptr> leases = createLeases4();
+    for (int i = 0; i < leases.size(); ++i) {
+        EXPECT_TRUE(lmptr_->addLease(leases[i]));
+    }
+
+    // Get the leases matching the hardware address of lease 1 and
+    // subnet ID of lease 1.  Result should be a single lease - lease 1.
+    Lease4Ptr returned = lmptr_->getLease4(leases[1]->hwaddr_,
+                                           leases[1]->subnet_id_);
+    ASSERT_TRUE(returned);
+    detailCompareLease(leases[1], returned);
+
+    // Try for a match to the hardware address of lease 1 and the wrong
+    // subnet ID.
+    returned = lmptr_->getLease4(leases[1]->hwaddr_,
+                                 leases[1]->subnet_id_ + 1);
+    EXPECT_FALSE(returned);
+
+    // Try for a match to the subnet ID of lease 1 (and lease 4) but
+    // the wrong hardware address.
+    vector<uint8_t> invalid_hwaddr(15, 0x77);
+    returned = lmptr_->getLease4(invalid_hwaddr,
+                                 leases[1]->subnet_id_);
+    EXPECT_FALSE(returned);
+
+    // Try for a match to an unknown hardware address and an unknown
+    // subnet ID.
+    returned = lmptr_->getLease4(invalid_hwaddr,
+                                 leases[1]->subnet_id_ + 1);
+    EXPECT_FALSE(returned);
+
+    // Add a second lease with the same values as the first and check that
+    // an attempt to access the database by these parameters throws a
+    // "multiple records" exception. (We expect there to be only one record
+    // with that combination, so getting them via getLeaseX() (as opposed
+    // to getLeaseXCollection() should throw an exception.)
+    EXPECT_TRUE(lmptr_->deleteLease(leases[2]->addr_));
+    leases[1]->addr_ = leases[2]->addr_;
+    EXPECT_TRUE(lmptr_->addLease(leases[1]));
+    EXPECT_THROW(returned = lmptr_->getLease4(leases[1]->hwaddr_,
+                                              leases[1]->subnet_id_),
+                 isc::dhcp::MultipleRecords);
+
+    // Delete all leases in the database
+    for (int i = 0; ADDRESS4[i] != NULL; ++i) {
+        IOAddress addr(ADDRESS4[i]);
+        (void) lmptr_->deleteLease(addr);
+    }
 }
 
-// @brief Check GetLease6 methods - Access by DUID/IAID
+// @brief Get lease4 by hardware address and subnet ID (2)
 //
-// Adds leases to the database and checks that they can be accessed via
-// a combination of DIUID and IAID.
-TEST_F(MySqlLeaseMgrTest, getLease6Extended1) {
+// Check that the system can cope with getting a hardware address of
+// any size.
+TEST_F(MySqlLeaseMgrTest, getLease4HwaddrSubnetIdSize) {
+
+    // Create leases, although we need only one.
+    vector<Lease4Ptr> leases = createLeases4();
+
+    // Now add leases with increasing hardware address size and check
+    // that they can be retrieved.
+    for (uint8_t i = 0; i <= Lease4::HWADDR_MAX; ++i) {
+        leases[1]->hwaddr_.resize(i, i);
+        EXPECT_TRUE(lmptr_->addLease(leases[1]));
+        Lease4Ptr returned = lmptr_->getLease4(leases[1]->hwaddr_,
+                                               leases[1]->subnet_id_);
+        ASSERT_TRUE(returned);
+        detailCompareLease(leases[1], returned);
+        (void) lmptr_->deleteLease(leases[1]->addr_);
+    }
+
+    // Expect some error when getting a lease with too long a hardware
+    // address.  Set the contents of each byte to 42, a random value.
+    // @todo Check if there is some way of detecting that data added
+    //       to the database is truncated.  There does not appear to
+    //       be any indication in the C API.
+    leases[1]->hwaddr_.resize(Lease4::HWADDR_MAX + 100, 42);
+    EXPECT_TRUE(lmptr_->addLease(leases[1]));
+    Lease4Ptr returned = lmptr_->getLease4(leases[1]->hwaddr_,
+                                           leases[1]->subnet_id_);
+    EXPECT_FALSE(returned);
+}
+
+/// @brief Check GetLease4 methods - access by Client ID
+///
+/// Adds leases to the database and checks that they can be accessed via
+/// the Client ID.
+TEST_F(MySqlLeaseMgrTest, getLease4ClientId) {
+    // Get the leases to be used for the test and add to the database
+    vector<Lease4Ptr> leases = createLeases4();
+    for (int i = 0; i < leases.size(); ++i) {
+        EXPECT_TRUE(lmptr_->addLease(leases[i]));
+    }
+
+    // Get the leases matching the Client ID address of lease 1
+    Lease4Collection returned = lmptr_->getLease4(*leases[1]->client_id_);
+
+    // Should be four leases, matching leases[1], [4], [5] and [6].
+    ASSERT_EQ(4, returned.size());
+
+    // Easiest way to check is to look at the addresses.
+    vector<string> addresses;
+    for (Lease4Collection::const_iterator i = returned.begin();
+         i != returned.end(); ++i) {
+        addresses.push_back((*i)->addr_.toText());
+    }
+    sort(addresses.begin(), addresses.end());
+    EXPECT_EQ(straddress4_[1], addresses[0]);
+    EXPECT_EQ(straddress4_[4], addresses[1]);
+    EXPECT_EQ(straddress4_[5], addresses[2]);
+    EXPECT_EQ(straddress4_[6], addresses[3]);
+
+    // Repeat test with just one expected match
+    returned = lmptr_->getLease4(*leases[3]->client_id_);
+    EXPECT_EQ(1, returned.size());
+    detailCompareLease(leases[3], *returned.begin());
+
+    // Check that an empty vector is valid
+    EXPECT_TRUE(leases[7]->client_id_->getClientId().empty());
+    returned = lmptr_->getLease4(leases[7]->hwaddr_);
+    EXPECT_EQ(1, returned.size());
+    detailCompareLease(leases[7], *returned.begin());
+
+    // Try to get something with invalid client ID
+    const uint8_t invalid_data[] = {0, 0, 0};
+    ClientId invalid(invalid_data, sizeof(invalid_data));
+    returned = lmptr_->getLease4(invalid);
+    EXPECT_EQ(0, returned.size());
+}
+
+// @brief Get Lease4 by client ID (2)
+//
+// Check that the system can cope with a client ID of any size.
+TEST_F(MySqlLeaseMgrTest, getLease4ClientIdSize) {
+
+    // Create leases, although we need only one.
+    vector<Lease4Ptr> leases = createLeases4();
+
+    // Now add leases with increasing Client ID size can be retrieved.
+    // For speed, go from 0 to 128 is steps of 16.
+    // Intermediate client_id_max is to overcome problem if
+    // ClientId::MAX_CLIENT_ID_LEN is used in an EXPECT_EQ.
+    int client_id_max = ClientId::MAX_CLIENT_ID_LEN;
+    EXPECT_EQ(128, client_id_max);
+    for (uint8_t i = 0; i <= client_id_max; i += 16) {
+        vector<uint8_t> clientid_vec(i, i);
+        leases[1]->client_id_.reset(new ClientId(clientid_vec));
+        EXPECT_TRUE(lmptr_->addLease(leases[1]));
+        Lease4Collection returned = lmptr_->getLease4(*leases[1]->client_id_);
+        ASSERT_TRUE(returned.size() == 1);
+        detailCompareLease(leases[1], *returned.begin());
+        (void) lmptr_->deleteLease(leases[1]->addr_);
+    }
+
+    // Don't bother to check client IDs longer than the maximum -
+    // these cannot be constructed, and that limitation is tested
+    // in the DUID/Client ID unit tests.
+}
+
+/// @brief Check GetLease4 methods - access by Client ID & Subnet ID
+///
+/// Adds leases to the database and checks that they can be accessed via
+/// a combination of client and subnet IDs.
+TEST_F(MySqlLeaseMgrTest, getLease4ClientIdSubnetId) {
+    // Get the leases to be used for the test and add to the database
+    vector<Lease4Ptr> leases = createLeases4();
+    for (int i = 0; i < leases.size(); ++i) {
+        EXPECT_TRUE(lmptr_->addLease(leases[i]));
+    }
+
+    // Get the leases matching the client ID of lease 1 and
+    // subnet ID of lease 1.  Result should be a single lease - lease 1.
+    Lease4Ptr returned = lmptr_->getLease4(*leases[1]->client_id_,
+                                           leases[1]->subnet_id_);
+    ASSERT_TRUE(returned);
+    detailCompareLease(leases[1], returned);
+
+    // Try for a match to the client ID of lease 1 and the wrong
+    // subnet ID.
+    returned = lmptr_->getLease4(*leases[1]->client_id_,
+                                 leases[1]->subnet_id_ + 1);
+    EXPECT_FALSE(returned);
+
+    // Try for a match to the subnet ID of lease 1 (and lease 4) but
+    // the wrong client ID
+    const uint8_t invalid_data[] = {0, 0, 0};
+    ClientId invalid(invalid_data, sizeof(invalid_data));
+    returned = lmptr_->getLease4(invalid, leases[1]->subnet_id_);
+    EXPECT_FALSE(returned);
+
+    // Try for a match to an unknown hardware address and an unknown
+    // subnet ID.
+    returned = lmptr_->getLease4(invalid, leases[1]->subnet_id_ + 1);
+    EXPECT_FALSE(returned);
+}
+
+/// @brief Check GetLease6 methods - access by DUID/IAID
+///
+/// Adds leases to the database and checks that they can be accessed via
+/// a combination of DIUID and IAID.
+TEST_F(MySqlLeaseMgrTest, getLease6DuidIaid) {
     // Get the leases to be used for the test.
     vector<Lease6Ptr> leases = createLeases6();
-    EXPECT_LE(6, leases.size());    // Expect to access leases 0 through 5
+    ASSERT_LE(6, leases.size());    // Expect to access leases 0 through 5
 
     // Add them to the database
     for (int i = 0; i < leases.size(); ++i) {
@@ -636,9 +1105,9 @@ TEST_F(MySqlLeaseMgrTest, getLease6Extended1) {
         addresses.push_back((*i)->addr_.toText());
     }
     sort(addresses.begin(), addresses.end());
-    EXPECT_EQ(L1_ADDRESS, addresses[0]);
-    EXPECT_EQ(L4_ADDRESS, addresses[1]);
-    EXPECT_EQ(L5_ADDRESS, addresses[2]);
+    EXPECT_EQ(straddress6_[1], addresses[0]);
+    EXPECT_EQ(straddress6_[4], addresses[1]);
+    EXPECT_EQ(straddress6_[5], addresses[2]);
 
     // Check that nothing is returned when either the IAID or DUID match
     // nothing.
@@ -653,18 +1122,41 @@ TEST_F(MySqlLeaseMgrTest, getLease6Extended1) {
     EXPECT_EQ(0, returned.size());
 }
 
-
-
-// @brief Check GetLease6 methods - Access by DUID/IAID/SubnetID
+// @brief Get Lease4 by DUID and IAID (2)
 //
-// Adds leases to the database and checks that they can be accessed via
-// a combination of DIUID and IAID.
-TEST_F(MySqlLeaseMgrTest, getLease6Extended2) {
-    // Get the leases to be used for the test.
+// Check that the system can cope with a DUID of any size.
+TEST_F(MySqlLeaseMgrTest, getLease6DuidIaidSize) {
+
+    // Create leases, although we need only one.
     vector<Lease6Ptr> leases = createLeases6();
-    EXPECT_LE(6, leases.size());    // Expect to access leases 0 through 5
 
-    // Add them to the database
+    // Now add leases with increasing DUID size can be retrieved.
+    // For speed, go from 0 to 128 is steps of 16.
+    int duid_max = DUID::MAX_DUID_LEN;
+    EXPECT_EQ(128, duid_max);
+    for (uint8_t i = 0; i <= duid_max; i += 16) {
+        vector<uint8_t> duid_vec(i, i);
+        leases[1]->duid_.reset(new DUID(duid_vec));
+        EXPECT_TRUE(lmptr_->addLease(leases[1]));
+        Lease6Collection returned = lmptr_->getLease6(*leases[1]->duid_,
+                                                      leases[1]->iaid_);
+        EXPECT_EQ(1, returned.size());
+        detailCompareLease(leases[1], *returned.begin());
+        (void) lmptr_->deleteLease(leases[1]->addr_);
+    }
+
+    // Don't bother to check DUIDs longer than the maximum - these cannot be
+    // constructed, and that limitation is tested in the DUID/Client ID unit
+    // tests.
+}
+
+/// @brief Check GetLease6 methods - access by DUID/IAID/SubnetID
+///
+/// Adds leases to the database and checks that they can be accessed via
+/// a combination of DIUID and IAID.
+TEST_F(MySqlLeaseMgrTest, getLease6DuidIaidSubnetId) {
+    // Get the leases to be used for the test and add them to the database.
+    vector<Lease6Ptr> leases = createLeases6();
     for (int i = 0; i < leases.size(); ++i) {
         EXPECT_TRUE(lmptr_->addLease(leases[i]));
     }
@@ -695,24 +1187,94 @@ TEST_F(MySqlLeaseMgrTest, getLease6Extended2) {
     EXPECT_FALSE(returned);
 }
 
+// @brief Get Lease4 by DUID, IAID & subnet ID (2)
+//
+// Check that the system can cope with a DUID of any size.
+TEST_F(MySqlLeaseMgrTest, getLease6DuidIaidSubnetIdSize) {
+
+    // Create leases, although we need only one.
+    vector<Lease6Ptr> leases = createLeases6();
 
+    // Now add leases with increasing DUID size can be retrieved.
+    // For speed, go from 0 to 128 is steps of 16.
+    int duid_max = DUID::MAX_DUID_LEN;
+    EXPECT_EQ(128, duid_max);
+    for (uint8_t i = 0; i <= duid_max; i += 16) {
+        vector<uint8_t> duid_vec(i, i);
+        leases[1]->duid_.reset(new DUID(duid_vec));
+        EXPECT_TRUE(lmptr_->addLease(leases[1]));
+        Lease6Ptr returned = lmptr_->getLease6(*leases[1]->duid_,
+                                               leases[1]->iaid_,
+                                               leases[1]->subnet_id_);
+        ASSERT_TRUE(returned);
+        detailCompareLease(leases[1], returned);
+        (void) lmptr_->deleteLease(leases[1]->addr_);
+    }
 
-// @brief Lease6 Update Tests
-//
-// Checks that we are able to update a lease in the database.
+    // Don't bother to check DUIDs longer than the maximum - these cannot be
+    // constructed, and that limitation is tested in the DUID/Client ID unit
+    // tests.
+}
+
+/// @brief Lease4 update tests
+///
+/// Checks that we are able to update a lease in the database.
+TEST_F(MySqlLeaseMgrTest, updateLease4) {
+    // Get the leases to be used for the test and add them to the database.
+    vector<Lease4Ptr> leases = createLeases4();
+    for (int i = 0; i < leases.size(); ++i) {
+        EXPECT_TRUE(lmptr_->addLease(leases[i]));
+    }
+
+    // Modify some fields in lease 1 (not the address) and update it.
+    ++leases[1]->subnet_id_;
+    leases[1]->valid_lft_ *= 2;
+    lmptr_->updateLease4(leases[1]);
+
+    // ... and check what is returned is what is expected.
+    Lease4Ptr l_returned = lmptr_->getLease4(ioaddress4_[1]);
+    ASSERT_TRUE(l_returned);
+    detailCompareLease(leases[1], l_returned);
+
+    // Alter the lease again and check.
+    ++leases[1]->subnet_id_;
+    leases[1]->cltt_ += 6;
+    lmptr_->updateLease4(leases[1]);
+
+    // Explicitly clear the returned pointer before getting new data to ensure
+    // that the new data is returned.
+    l_returned.reset();
+    l_returned = lmptr_->getLease4(ioaddress4_[1]);
+    ASSERT_TRUE(l_returned);
+    detailCompareLease(leases[1], l_returned);
+
+    // Check we can do an update without changing data.
+    lmptr_->updateLease4(leases[1]);
+    l_returned.reset();
+    l_returned = lmptr_->getLease4(ioaddress4_[1]);
+    ASSERT_TRUE(l_returned);
+    detailCompareLease(leases[1], l_returned);
+
+    // Try updating a lease not in the database.
+    lmptr_->deleteLease(ioaddress4_[2]);
+    EXPECT_THROW(lmptr_->updateLease4(leases[2]), isc::dhcp::NoSuchLease);
+}
+
+/// @brief Lease6 update tests
+///
+/// Checks that we are able to update a lease in the database.
 TEST_F(MySqlLeaseMgrTest, updateLease6) {
     // Get the leases to be used for the test.
     vector<Lease6Ptr> leases = createLeases6();
-    EXPECT_LE(3, leases.size());    // Expect to access leases 0 through 5
+    ASSERT_LE(3, leases.size());    // Expect to access leases 0 through 2
 
     // Add a lease to the database and check that the lease is there.
     EXPECT_TRUE(lmptr_->addLease(leases[1]));
     lmptr_->commit();
 
-    reopen();
-    Lease6Ptr l_returned = lmptr_->getLease6(L1_IOADDRESS);
-    EXPECT_TRUE(l_returned);
-    detailCompareLease6(leases[1], l_returned);
+    Lease6Ptr l_returned = lmptr_->getLease6(ioaddress6_[1]);
+    ASSERT_TRUE(l_returned);
+    detailCompareLease(leases[1], l_returned);
 
     // Modify some fields in lease 1 (not the address) and update it.
     ++leases[1]->iaid_;
@@ -720,13 +1282,12 @@ TEST_F(MySqlLeaseMgrTest, updateLease6) {
     leases[1]->valid_lft_ *= 2;
     lmptr_->updateLease6(leases[1]);
     lmptr_->commit();
-    reopen();
 
     // ... and check what is returned is what is expected.
     l_returned.reset();
-    l_returned = lmptr_->getLease6(L1_IOADDRESS);
-    EXPECT_TRUE(l_returned);
-    detailCompareLease6(leases[1], l_returned);
+    l_returned = lmptr_->getLease6(ioaddress6_[1]);
+    ASSERT_TRUE(l_returned);
+    detailCompareLease(leases[1], l_returned);
 
     // Alter the lease again and check.
     ++leases[1]->iaid_;
@@ -736,16 +1297,16 @@ TEST_F(MySqlLeaseMgrTest, updateLease6) {
     lmptr_->updateLease6(leases[1]);
 
     l_returned.reset();
-    l_returned = lmptr_->getLease6(L1_IOADDRESS);
-    EXPECT_TRUE(l_returned);
-    detailCompareLease6(leases[1], l_returned);
+    l_returned = lmptr_->getLease6(ioaddress6_[1]);
+    ASSERT_TRUE(l_returned);
+    detailCompareLease(leases[1], l_returned);
 
     // Check we can do an update without changing data.
     lmptr_->updateLease6(leases[1]);
     l_returned.reset();
-    l_returned = lmptr_->getLease6(L1_IOADDRESS);
-    EXPECT_TRUE(l_returned);
-    detailCompareLease6(leases[1], l_returned);
+    l_returned = lmptr_->getLease6(ioaddress6_[1]);
+    ASSERT_TRUE(l_returned);
+    detailCompareLease(leases[1], l_returned);
 
     // Try updating a lease not in the database.
     EXPECT_THROW(lmptr_->updateLease6(leases[2]), isc::dhcp::NoSuchLease);
diff --git a/src/lib/dhcpsrv/tests/schema_copy.h b/src/lib/dhcpsrv/tests/schema_copy.h
index 1dd796d..48a11ca 100644
--- a/src/lib/dhcpsrv/tests/schema_copy.h
+++ b/src/lib/dhcpsrv/tests/schema_copy.h
@@ -25,7 +25,9 @@ namespace {
 // by semicolons, and the strings must end with a comma.  The final line
 // statement must be NULL (not in quotes)
 
-// THIS MUST BE KEPT UP TO DATE AND UPDATED IF THE SCHEMA CHANGES
+// NOTE: This file mirrors the schema in src/lib/dhcpsrv/dhcpdb_create.mysql.
+//       If this file is altered, please ensure that any change is compatible
+//       with the schema in dhcpdb_create.mysql.
 
 // Deletion of existing tables.
 
@@ -44,13 +46,13 @@ const char* create_statement[] = {
         "address INT UNSIGNED PRIMARY KEY NOT NULL,"
         "hwaddr VARBINARY(20),"
         "client_id VARBINARY(128),"
-        "lease_time INT UNSIGNED,"
+        "valid_lifetime INT UNSIGNED,"
         "expire TIMESTAMP,"
         "subnet_id INT UNSIGNED"
         ") ENGINE = INNODB",
 
     "CREATE TABLE lease6 ("
-        "address VARCHAR(40) PRIMARY KEY NOT NULL,"
+        "address VARCHAR(39) PRIMARY KEY NOT NULL,"
         "duid VARBINARY(128),"
         "valid_lifetime INT UNSIGNED,"
         "expire TIMESTAMP,"
@@ -75,7 +77,7 @@ const char* create_statement[] = {
         "minor INT"
         ")",
 
-    "INSERT INTO schema_version VALUES (0, 1)",
+    "INSERT INTO schema_version VALUES (1, 0)",
 
     NULL
 };
diff --git a/src/lib/dhcpsrv/triplet.h b/src/lib/dhcpsrv/triplet.h
index d9388fe..c7b8156 100644
--- a/src/lib/dhcpsrv/triplet.h
+++ b/src/lib/dhcpsrv/triplet.h
@@ -37,7 +37,7 @@ public:
     ///
     /// Typically: uint32_t to Triplet assignment. It is very convenient
     /// to be able to simply write Triplet<uint32_t> x = 7;
-    Triplet<T> operator=(T other) {
+    Triplet<T>& operator=(T other) {
         min_ = other;
         default_ = other;
         max_ = other;
diff --git a/src/lib/dns/Makefile.am b/src/lib/dns/Makefile.am
index 70c65e7..7778b58 100644
--- a/src/lib/dns/Makefile.am
+++ b/src/lib/dns/Makefile.am
@@ -21,6 +21,8 @@ EXTRA_DIST += rdata/ch_3/a_1.cc
 EXTRA_DIST += rdata/ch_3/a_1.h
 EXTRA_DIST += rdata/generic/cname_5.cc
 EXTRA_DIST += rdata/generic/cname_5.h
+EXTRA_DIST += rdata/generic/detail/char_string.cc
+EXTRA_DIST += rdata/generic/detail/char_string.h
 EXTRA_DIST += rdata/generic/detail/nsec_bitmap.cc
 EXTRA_DIST += rdata/generic/detail/nsec_bitmap.h
 EXTRA_DIST += rdata/generic/detail/nsec3param_common.cc
@@ -82,6 +84,8 @@ EXTRA_DIST += rdata/in_1/srv_33.h
 #EXTRA_DIST += rdata/template.cc
 #EXTRA_DIST += rdata/template.h
 
+noinst_SCRIPTS = gen-rdatacode.py
+
 # auto-generate by gen-rdatacode.py:
 BUILT_SOURCES = rrclass.h rrtype.h rrparamregistry.cc
 BUILT_SOURCES += rdataclass.h rdataclass.cc
@@ -122,6 +126,8 @@ 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.h
+libb10_dns___la_SOURCES += rdata/generic/detail/char_string.h
+libb10_dns___la_SOURCES += rdata/generic/detail/char_string.cc
 libb10_dns___la_SOURCES += rdata/generic/detail/nsec_bitmap.h
 libb10_dns___la_SOURCES += rdata/generic/detail/nsec_bitmap.cc
 libb10_dns___la_SOURCES += rdata/generic/detail/nsec3param_common.cc
@@ -143,7 +149,7 @@ rrclass.h: rrclass-placeholder.h
 rrtype.h: rrtype-placeholder.h
 rrparamregistry.cc: rrparamregistry-placeholder.cc
 rrclass.h rrtype.h rrparamregistry.cc rdataclass.h rdataclass.cc: Makefile
-	./gen-rdatacode.py
+	$(PYTHON) ./gen-rdatacode.py
 
 libdns___includedir = $(includedir)/$(PACKAGE_NAME)/dns
 libdns___include_HEADERS = \
@@ -151,8 +157,10 @@ libdns___include_HEADERS = \
 	exceptions.h \
 	labelsequence.h \
 	message.h \
-	master_lexer.h \
 	masterload.h \
+	master_lexer.h \
+	master_loader.h \
+	master_loader_callbacks.h \
 	messagerenderer.h \
 	name.h \
 	question.h \
diff --git a/src/lib/dns/character_string.cc b/src/lib/dns/character_string.cc
index 3a289ac..8b31948 100644
--- a/src/lib/dns/character_string.cc
+++ b/src/lib/dns/character_string.cc
@@ -29,7 +29,8 @@ bool isDigit(char c) {
 
 std::string
 characterstr::getNextCharacterString(const std::string& input_str,
-                              std::string::const_iterator& input_iterator)
+                              std::string::const_iterator& input_iterator,
+                              bool* quoted)
 {
     string result;
 
@@ -119,6 +120,10 @@ characterstr::getNextCharacterString(const std::string& input_str,
         isc_throw(InvalidRdataText, "The quotes are not paired");
     }
 
+    if (quoted != NULL) {
+        *quoted = quotes_separated;
+    }
+
     return (result);
 }
 
diff --git a/src/lib/dns/character_string.h b/src/lib/dns/character_string.h
index 2a68778..0bfa38b 100644
--- a/src/lib/dns/character_string.h
+++ b/src/lib/dns/character_string.h
@@ -39,9 +39,12 @@ namespace characterstr {
     /// \param input_iterator The iterator from which to start extracting,
     ///        the iterator will be updated to new position after the function
     ///        is returned
+    /// \param quoted If not \c NULL, returns \c true at this address if
+    ///        the string is quoted, \cfalse otherwise
     /// \return A std::string that contains the extracted <character-string>
     std::string getNextCharacterString(const std::string& input_str,
-                                       std::string::const_iterator& input_iterator);
+                                       std::string::const_iterator& input_iterator,
+                                       bool* quoted = NULL);
 
     /// Get a <character-string> from a input buffer
     ///
diff --git a/src/lib/dns/gen-rdatacode.py.in b/src/lib/dns/gen-rdatacode.py.in
index 6a9ff8a..2bf9de6 100755
--- a/src/lib/dns/gen-rdatacode.py.in
+++ b/src/lib/dns/gen-rdatacode.py.in
@@ -24,11 +24,21 @@ from os.path import getmtime
 import re
 import sys
 
+# new_rdata_factory_users[] is a list of tuples of the form (rrtype,
+# rrclass). Items in the list use the (new) RdataFactory class, and
+# items which are not in the list use OldRdataFactory class.
+# Note: rrtype and rrclass must be specified in lowercase in
+# new_rdata_factory_users.
+#
+# Example:
+#     new_rdata_factory_users = [('a', 'in'), ('a', 'ch'), ('soa', 'generic')]
+new_rdata_factory_users = [('aaaa', 'in'), ('txt', 'generic'),
+                           ('spf', 'generic')]
+
 re_typecode = re.compile('([\da-z]+)_(\d+)')
 classcode2txt = {}
 typecode2txt = {}
 typeandclass = []
-new_rdatafactory_users = ['aaaa']
 generic_code = 65536            # something larger than any code value
 rdata_declarations = ''
 class_definitions = ''
@@ -294,8 +304,13 @@ def generate_rrparam(fileprefix, basemtime):
 
         # By default, we use OldRdataFactory (see bug #2497). If you
         # want to pick RdataFactory for a particular type, add it to
-        # new_rdatafactory_users.
-        if type_txt in new_rdatafactory_users:
+        # new_rdata_factory_users.  Note that we explicitly generate (for
+        # optimization) class-independent ("generic") factories for class IN
+        # for optimization.
+        if (((type_txt.lower(), class_txt.lower()) in
+             new_rdata_factory_users) or
+            ((class_txt.lower() == 'in') and
+             ((type_txt.lower(), 'generic') in new_rdata_factory_users))):
             rdf_class = 'RdataFactory'
         else:
             rdf_class = 'OldRdataFactory'
diff --git a/src/lib/dns/master_lexer.cc b/src/lib/dns/master_lexer.cc
index 4593b48..e6e2c78 100644
--- a/src/lib/dns/master_lexer.cc
+++ b/src/lib/dns/master_lexer.cc
@@ -149,6 +149,11 @@ MasterLexer::popSource() {
     impl_->has_previous_ = false;
 }
 
+size_t
+MasterLexer::getSourceCount() const {
+    return (impl_->sources_.size());
+}
+
 std::string
 MasterLexer::getSourceName() const {
     if (impl_->sources_.empty()) {
@@ -522,9 +527,10 @@ Number::handle(MasterLexer& lexer) const {
             getLexerImpl(lexer)->source_->getChar(), escaped);
         if (getLexerImpl(lexer)->isTokenEnd(c, escaped)) {
             getLexerImpl(lexer)->source_->ungetChar();
+            // We need to close the string whether it's digits-only (for
+            // lexical_cast) or not (see String::handle()).
+            data.push_back('\0');
             if (digits_only) {
-                // Close the string for lexical_cast
-                data.push_back('\0');
                 try {
                     const uint32_t number32 =
                         boost::lexical_cast<uint32_t, const char*>(&data[0]);
@@ -535,7 +541,6 @@ Number::handle(MasterLexer& lexer) const {
                     token = MasterToken(MasterToken::NUMBER_OUT_OF_RANGE);
                 }
             } else {
-                data.push_back('\0'); // see String::handle()
                 token = MasterToken(&data.at(0), data.size() - 1);
             }
             return;
diff --git a/src/lib/dns/master_lexer.h b/src/lib/dns/master_lexer.h
index 35586fe..cdf2866 100644
--- a/src/lib/dns/master_lexer.h
+++ b/src/lib/dns/master_lexer.h
@@ -87,8 +87,7 @@ public:
     ///
     /// Any character can be stored in the valid range of the region.
     /// In particular, there can be a nul character (\0) in the middle of
-    /// the region.  On the other hand, it is not ensured that the string
-    /// is nul-terminated.  So the usual string manipulation API may not work
+    /// the region.  So the usual string manipulation API may not work
     /// as expected.
     ///
     /// The `MasterLexer` implementation ensures that there are at least
@@ -405,6 +404,11 @@ public:
     /// \throw isc::InvalidOperation Called with no pushed source.
     void popSource();
 
+    /// \brief Get number of sources inside the lexer.
+    ///
+    /// This method never throws.
+    size_t getSourceCount() const;
+
     /// \brief Return the name of the current input source name.
     ///
     /// If it's a file, it will be the C string given at the corresponding
diff --git a/src/lib/dns/master_loader.cc b/src/lib/dns/master_loader.cc
index f542aff..d82f462 100644
--- a/src/lib/dns/master_loader.cc
+++ b/src/lib/dns/master_loader.cc
@@ -31,6 +31,8 @@ using boost::algorithm::iequals;
 namespace isc {
 namespace dns {
 
+namespace {
+
 // An internal exception, used to control the code flow in case of errors.
 // It is thrown during the loading and caught later, not to be propagated
 // outside of the file.
@@ -41,6 +43,8 @@ public:
     {}
 };
 
+} // end unnamed namespace
+
 class MasterLoader::MasterLoaderImpl {
 public:
     MasterLoaderImpl(const char* master_file,
@@ -51,6 +55,7 @@ public:
                      MasterLoader::Options options) :
         lexer_(),
         zone_origin_(zone_origin),
+        active_origin_(zone_origin),
         zone_class_(zone_class),
         callbacks_(callbacks),
         add_callback_(add_callback),
@@ -59,7 +64,6 @@ public:
         initialized_(false),
         ok_(true),
         many_errors_((options & MANY_ERRORS) != 0),
-        source_count_(0),
         complete_(false),
         seen_error_(false)
     {}
@@ -90,11 +94,10 @@ public:
             }
         }
         initialized_ = true;
-        ++source_count_;
     }
 
     bool popSource() {
-        if (--source_count_ == 0) {
+        if (lexer_.getSourceCount() == 1) {
             return (false);
         }
         lexer_.popSource();
@@ -104,7 +107,6 @@ public:
     void pushStreamSource(std::istream& stream) {
         lexer_.pushSource(stream);
         initialized_ = true;
-        ++source_count_;
     }
 
     // Get a string token. Handle it as error if it is not string.
@@ -137,13 +139,23 @@ public:
         pushSource(filename);
     }
 
+    void doOrigin() {
+        // Parse and create the new origin. It is relative to the previous
+        // one.
+        const MasterToken::StringRegion&
+            name_string(lexer_.getNextToken(MasterToken::QSTRING).
+                        getStringRegion());
+        active_origin_ = Name(name_string.beg, name_string.len,
+                              &active_origin_);
+        // Make sure there's the EOLN
+        eatUntilEOL(true);
+    }
+
     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();
         } else if (iequals(directive, "TTL")) {
             // TODO: Implement
             isc_throw(isc::NotImplemented,
@@ -186,6 +198,8 @@ public:
 private:
     MasterLexer lexer_;
     const Name zone_origin_;
+    Name active_origin_; // The origin used during parsing
+                         // (modifiable by $ORIGIN)
     const RRClass zone_class_;
     MasterLoaderCallbacks callbacks_;
     AddRRCallback add_callback_;
@@ -196,7 +210,6 @@ private:
     bool ok_;                   // Is it OK to continue loading?
     const bool many_errors_;    // Are many errors allowed (or should we abort
                                 // on the first)
-    size_t source_count_;       // How many sources are currently pushed.
 public:
     bool complete_;             // All work done.
     bool seen_error_;           // Was there at least one error during the
@@ -255,7 +268,7 @@ MasterLoader::MasterLoaderImpl::loadIncremental(size_t count_limit) {
             }
 
             const Name name(name_string.beg, name_string.len,
-                            &zone_origin_);
+                            &active_origin_);
             // TODO: Some more flexibility. We don't allow omitting
             // anything yet
 
@@ -275,7 +288,7 @@ MasterLoader::MasterLoaderImpl::loadIncremental(size_t count_limit) {
 
             const rdata::RdataPtr data(rdata::createRdata(rrtype, rrclass,
                                                           lexer_,
-                                                          &zone_origin_,
+                                                          &active_origin_,
                                                           options_,
                                                           callbacks_));
             // In case we get NULL, it means there was error creating
diff --git a/src/lib/dns/python/tests/rdata_python_test.py b/src/lib/dns/python/tests/rdata_python_test.py
index 81dea5f..3b8f9ad 100644
--- a/src/lib/dns/python/tests/rdata_python_test.py
+++ b/src/lib/dns/python/tests/rdata_python_test.py
@@ -38,7 +38,7 @@ class RdataTest(unittest.TestCase):
         self.assertRaises(InvalidRdataText, Rdata, RRType("A"), RRClass("IN"),
                           "Invalid Rdata Text")
         self.assertRaises(CharStringTooLong, Rdata, RRType("TXT"),
-                          RRClass("IN"), ' ' * 256)
+                          RRClass("IN"), 'x' * 256)
         self.assertRaises(InvalidRdataLength, Rdata, RRType("TXT"),
                           RRClass("IN"), bytes(65536))
         self.assertRaises(DNSMessageFORMERR, Rdata, RRType("TXT"),
diff --git a/src/lib/dns/python/tests/tsigkey_python_test.py b/src/lib/dns/python/tests/tsigkey_python_test.py
index 516bea4..ca7a61e 100644
--- a/src/lib/dns/python/tests/tsigkey_python_test.py
+++ b/src/lib/dns/python/tests/tsigkey_python_test.py
@@ -170,6 +170,12 @@ class TSIGKeyRingTest(unittest.TestCase):
         self.assertEqual(TSIGKey.HMACSHA256_NAME, key.get_algorithm_name())
         self.assertEqual(self.secret, key.get_secret())
 
+        (code, key) = self.keyring.find(self.key_name)
+        self.assertEqual(TSIGKeyRing.SUCCESS, code)
+        self.assertEqual(self.key_name, key.get_key_name())
+        self.assertEqual(TSIGKey.HMACSHA256_NAME, key.get_algorithm_name())
+        self.assertEqual(self.secret, key.get_secret())
+
         (code, key) = self.keyring.find(Name('different-key.example'),
                                         self.sha256_name)
         self.assertEqual(TSIGKeyRing.NOTFOUND, code)
diff --git a/src/lib/dns/python/tsigkey_python.cc b/src/lib/dns/python/tsigkey_python.cc
index cf79c1a..bcab59c 100644
--- a/src/lib/dns/python/tsigkey_python.cc
+++ b/src/lib/dns/python/tsigkey_python.cc
@@ -287,7 +287,9 @@ PyMethodDef TSIGKeyRing_methods[] = {
       METH_VARARGS,
       "Remove a TSIGKey for the given name from the TSIGKeyRing." },
     { "find", reinterpret_cast<PyCFunction>(TSIGKeyRing_find), METH_VARARGS,
-      "Find a TSIGKey for the given name in the TSIGKeyRing. "
+      "Find a TSIGKey for the given name in the TSIGKeyRing. Optional "
+      "second argument is an algorithm, in which case it only returns "
+      "a key if both match.\n"
       "It returns a tuple of (result_code, key)." },
     { NULL, NULL, 0, NULL }
 };
@@ -362,13 +364,16 @@ TSIGKeyRing_remove(const s_TSIGKeyRing* self, PyObject* args) {
 PyObject*
 TSIGKeyRing_find(const s_TSIGKeyRing* self, PyObject* args) {
     PyObject* key_name;
-    PyObject* algorithm_name;
+    PyObject* algorithm_name = NULL;
 
-    if (PyArg_ParseTuple(args, "O!O!", &name_type, &key_name,
+    if (PyArg_ParseTuple(args, "O!|O!", &name_type, &key_name,
                          &name_type, &algorithm_name)) {
-        const TSIGKeyRing::FindResult result =
-            self->cppobj->find(PyName_ToName(key_name),
-                               PyName_ToName(algorithm_name));
+        // Can't init TSIGKeyRing::FindResult without actual result,
+        // so use ternary operator
+        TSIGKeyRing::FindResult result = (algorithm_name == NULL) ?
+                    self->cppobj->find(PyName_ToName(key_name)) :
+                    self->cppobj->find(PyName_ToName(key_name),
+                                       PyName_ToName(algorithm_name));
         if (result.key != NULL) {
             s_TSIGKey* key = PyObject_New(s_TSIGKey, &tsigkey_type);
             if (key == NULL) {
diff --git a/src/lib/dns/rdata.cc b/src/lib/dns/rdata.cc
index 9e96378..081f855 100644
--- a/src/lib/dns/rdata.cc
+++ b/src/lib/dns/rdata.cc
@@ -12,6 +12,20 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include <exceptions/exceptions.h>
+
+#include <util/buffer.h>
+
+#include <dns/name.h>
+#include <dns/messagerenderer.h>
+#include <dns/master_lexer.h>
+#include <dns/rdata.h>
+#include <dns/rrparamregistry.h>
+#include <dns/rrtype.h>
+
+#include <boost/lexical_cast.hpp>
+#include <boost/shared_ptr.hpp>
+
 #include <algorithm>
 #include <cctype>
 #include <string>
@@ -24,17 +38,6 @@
 #include <stdint.h>
 #include <string.h>
 
-#include <boost/lexical_cast.hpp>
-#include <boost/shared_ptr.hpp>
-
-#include <util/buffer.h>
-#include <dns/name.h>
-#include <dns/messagerenderer.h>
-#include <dns/master_lexer.h>
-#include <dns/rdata.h>
-#include <dns/rrparamregistry.h>
-#include <dns/rrtype.h>
-
 using namespace std;
 using boost::lexical_cast;
 using namespace isc::util;
@@ -113,7 +116,11 @@ fromtextError(bool& error_issued, const MasterLexer& lexer,
                         token->getErrorText());
         break;
     default:
-        assert(false);
+        // This case shouldn't happen based on how we use MasterLexer in
+        // createRdata(), so we could assert() that here.  But since it
+        // depends on detailed behavior of other classes, we treat the case
+        // in a bit less harsh way.
+        isc_throw(Unexpected, "bug: createRdata() saw unexpected token type");
     }
 }
 }
diff --git a/src/lib/dns/rdata.h b/src/lib/dns/rdata.h
index 186ace2..4cd63cc 100644
--- a/src/lib/dns/rdata.h
+++ b/src/lib/dns/rdata.h
@@ -502,7 +502,7 @@ RdataPtr createRdata(const RRType& rrtype, const RRClass& rrclass,
 /// most of syntax and semantics errors of the input (reported as exceptions),
 /// calls the corresponding callback specified by the \c callbacks parameters,
 /// and returns a NULL smart pointer.  If the caller rather wants to get
-/// an exception in these cases, it can use pass a callback that internally
+/// an exception in these cases, it can pass a callback that internally
 /// throws on error.  Some critical exceptions such as \c std::bad_alloc are
 /// still propagated to the upper layer as it doesn't make sense to try
 /// recovery from such a situation within this function.
diff --git a/src/lib/dns/rdata/generic/detail/char_string.cc b/src/lib/dns/rdata/generic/detail/char_string.cc
new file mode 100644
index 0000000..fb4c9b4
--- /dev/null
+++ b/src/lib/dns/rdata/generic/detail/char_string.cc
@@ -0,0 +1,98 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <exceptions/exceptions.h>
+
+#include <dns/rdata.h>
+#include <dns/master_lexer.h>
+#include <dns/rdata/generic/detail/char_string.h>
+
+#include <boost/lexical_cast.hpp>
+
+#include <cassert>
+#include <cctype>
+#include <cstring>
+#include <vector>
+
+#include <stdint.h>
+
+namespace isc {
+namespace dns {
+namespace rdata {
+namespace generic {
+namespace detail {
+
+namespace {
+// Convert a DDD form to the corresponding integer
+int
+decimalToNumber(const char* s, const char* s_end) {
+    if (s_end - s < 3) {
+        isc_throw(InvalidRdataText, "Escaped digits too short");
+    }
+
+    const std::string num_str(s, s + 3);
+    try {
+        const int i = boost::lexical_cast<int>(num_str);
+        if (i > 255) {
+            isc_throw(InvalidRdataText, "Escaped digits too large: "
+                      << num_str);
+        }
+        return (i);
+    } catch (const boost::bad_lexical_cast&) {
+        isc_throw(InvalidRdataText,
+                  "Invalid form for escaped digits: " << num_str);
+    }
+}
+}
+
+void
+strToCharString(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);
+
+    bool escape = false;
+    const char* s = str_region.beg;
+    const char* const s_end = str_region.beg + str_region.len;
+
+    for (size_t n = str_region.len; n != 0; --n, ++s) {
+        int c = (*s & 0xff);
+        if (escape && std::isdigit(c) != 0) {
+            c = decimalToNumber(s, s_end);
+            assert(n >= 3);
+            n -= 2;
+            s += 2;
+        } else if (!escape && c == '\\') {
+            escape = true;
+            continue;
+        }
+        escape = false;
+        result.push_back(c);
+    }
+    if (escape) {               // terminated by non-escaped '\'
+        isc_throw(InvalidRdataText, "character-string ends with '\\'");
+    }
+    if (result.size() > MAX_CHARSTRING_LEN + 1) { // '+ 1' due to the len field
+        isc_throw(CharStringTooLong, "character-string is too long: " <<
+                  (result.size() - 1) << "(+1) characters");
+    }
+    result[0] = result.size() - 1;
+}
+
+} // end of detail
+} // end of generic
+} // end of rdata
+} // end of dns
+} // end of isc
diff --git a/src/lib/dns/rdata/generic/detail/char_string.h b/src/lib/dns/rdata/generic/detail/char_string.h
new file mode 100644
index 0000000..702af04
--- /dev/null
+++ b/src/lib/dns/rdata/generic/detail/char_string.h
@@ -0,0 +1,63 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef DNS_RDATA_CHARSTRING_H
+#define DNS_RDATA_CHARSTRING_H 1
+
+#include <dns/master_lexer.h>
+
+#include <vector>
+#include <stdint.h>
+
+namespace isc {
+namespace dns {
+namespace rdata {
+namespace generic {
+namespace detail {
+
+/// \brief Type for DNS character string.
+///
+/// A character string can contain any unsigned 8-bit value, so this cannot
+/// be the bare char basis.
+typedef std::vector<uint8_t> CharString;
+
+/// \brief Convert a DNS character-string into corresponding binary data.
+///
+/// This helper function takes a string object that is expected to be a
+/// textual representation of a valid DNS character-string, and dumps
+/// the corresponding binary sequence in the given placeholder (passed
+/// via the \c result parameter).  It handles escape notations of
+/// character-strings with a backslash ('\'), and checks the length
+/// restriction.
+///
+/// \throw CharStringTooLong The resulting binary data are too large for a
+/// valid character-string.
+/// \throw InvalidRdataText Other syntax errors.
+///
+/// \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);
+
+} // namespace detail
+} // namespace generic
+} // namespace rdata
+} // namespace dns
+} // namespace isc
+#endif  // DNS_RDATA_CHARSTRING_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/rdata/generic/detail/txt_like.h b/src/lib/dns/rdata/generic/detail/txt_like.h
index fdab6bf..d1916e3 100644
--- a/src/lib/dns/rdata/generic/detail/txt_like.h
+++ b/src/lib/dns/rdata/generic/detail/txt_like.h
@@ -15,13 +15,20 @@
 #ifndef TXT_LIKE_H
 #define TXT_LIKE_H 1
 
+#include <dns/master_lexer.h>
+#include <dns/rdata/generic/detail/char_string.h>
+
 #include <stdint.h>
 
 #include <string>
+#include <sstream>
 #include <vector>
 
-using namespace std;
-using namespace isc::util;
+namespace isc {
+namespace dns {
+namespace rdata {
+namespace generic {
+namespace detail {
 
 /// \brief \c rdata::TXTLikeImpl class represents the TXT-like RDATA for TXT
 /// and SPF types.
@@ -41,7 +48,7 @@ public:
     ///
     /// \c InvalidRdataLength is thrown if rdata_len exceeds the maximum.
     /// \c DNSMessageFORMERR is thrown if the RR is misformed.
-    TXTLikeImpl(InputBuffer& buffer, size_t rdata_len) {
+    TXTLikeImpl(util::InputBuffer& buffer, size_t rdata_len) {
         if (rdata_len > MAX_RDLENGTH) {
             isc_throw(InvalidRdataLength, "RDLENGTH too large: " << rdata_len);
         }
@@ -59,7 +66,7 @@ public:
                           " RDATA: character string length is too large: " <<
                           static_cast<int>(len));
             }
-            vector<uint8_t> data(len + 1);
+            std::vector<uint8_t> data(len + 1);
             data[0] = len;
             buffer.readData(&data[0] + 1, len);
             string_list_.push_back(data);
@@ -70,46 +77,61 @@ public:
 
     /// \brief Constructor from string.
     ///
-    /// <b>Exceptions</b>
-    ///
-    /// \c CharStringTooLong is thrown if the parameter string length exceeds
-    /// maximum.
-    /// \c InvalidRdataText is thrown if the method cannot process the
-    /// parameter data.
+    /// \throw CharStringTooLong the parameter string length exceeds maximum.
+    /// \throw InvalidRdataText the method cannot process the parameter data
     explicit TXTLikeImpl(const std::string& txtstr) {
-        // TBD: this is a simple, incomplete implementation that only supports
-        // a single character-string.
+        std::istringstream ss(txtstr);
+        MasterLexer lexer;
+        lexer.pushSource(ss);
+
+        try {
+            buildFromTextHelper(lexer);
+            if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) {
+                isc_throw(InvalidRdataText, "Failed to construct " <<
+                          RRType(typeCode) << " RDATA from '" << txtstr <<
+                          "': extra new line");
+            }
+        } catch (const MasterLexer::LexerError& ex) {
+            isc_throw(InvalidRdataText, "Failed to construct " <<
+                      RRType(typeCode) << " RDATA from '" << txtstr << "': "
+                      << ex.what());
+        }
+    }
 
-        size_t length = txtstr.size();
-        size_t pos_begin = 0;
+    /// \brief Constructor using the master lexer.
+    ///
+    /// \throw CharStringTooLong the parameter string length exceeds maximum.
+    /// \throw InvalidRdataText the method cannot process the parameter data
+    ///
+    /// \param lexer A \c MasterLexer object parsing a master file for this
+    /// RDATA.
+    TXTLikeImpl(MasterLexer& lexer) {
+        buildFromTextHelper(lexer);
+    }
 
-        if (length > 1 && txtstr[0] == '"' && txtstr[length - 1] == '"') {
-            pos_begin = 1;
-            length -= 2;
+private:
+    void buildFromTextHelper(MasterLexer& lexer) {
+        while (true) {
+            const MasterToken& token = lexer.getNextToken(
+                MasterToken::QSTRING, true);
+            if (token.getType() != MasterToken::STRING &&
+                token.getType() != MasterToken::QSTRING) {
+                break;
+            }
+            string_list_.push_back(std::vector<uint8_t>());
+            strToCharString(token.getStringRegion(), string_list_.back());
         }
 
-        if (length > MAX_CHARSTRING_LEN) {
-            isc_throw(CharStringTooLong, RRType(typeCode) <<
-                      " RDATA construction from text:"
-                      " string length is too long: " << length);
-        }
+        // Let upper layer handle eol/eof.
+        lexer.ungetToken();
 
-        // TBD: right now, we don't support escaped characters
-        if (txtstr.find('\\') != string::npos) {
-            isc_throw(InvalidRdataText, RRType(typeCode) <<
-                      " RDATA from text:"
-                      " escaped character is currently not supported: " <<
-                      txtstr);
+        if (string_list_.empty()) {
+            isc_throw(InvalidRdataText, "Failed to construct" <<
+                      RRType(typeCode) << " RDATA: empty input");
         }
-
-        vector<uint8_t> data;
-        data.reserve(length + 1);
-        data.push_back(length);
-        data.insert(data.end(), txtstr.begin() + pos_begin,
-                    txtstr.begin() + pos_begin + length);
-        string_list_.push_back(data);
     }
 
+public:
     /// \brief The copy constructor.
     ///
     /// Trivial for now, we could've used the default one.
@@ -122,9 +144,9 @@ public:
     ///
     /// \param buffer An output buffer to store the wire data.
     void
-    toWire(OutputBuffer& buffer) const {
-        for (vector<vector<uint8_t> >::const_iterator it =
-                                                          string_list_.begin();
+    toWire(util::OutputBuffer& buffer) const {
+        for (std::vector<std::vector<uint8_t> >::const_iterator it =
+                 string_list_.begin();
              it != string_list_.end();
              ++it)
         {
@@ -139,8 +161,8 @@ public:
     /// to.
     void
     toWire(AbstractMessageRenderer& renderer) const {
-        for (vector<vector<uint8_t> >::const_iterator it =
-                                                          string_list_.begin();
+        for (std::vector<std::vector<uint8_t> >::const_iterator it =
+                 string_list_.begin();
              it != string_list_.end();
              ++it)
         {
@@ -151,14 +173,14 @@ public:
     /// \brief Convert the TXT-like data to a string.
     ///
     /// \return A \c string object that represents the TXT-like data.
-    string
+    std::string
     toText() const {
-        string s;
+        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 (vector<vector<uint8_t> >::const_iterator it =
-                                                          string_list_.begin();
+        for (std::vector<std::vector<uint8_t> >::const_iterator it =
+                 string_list_.begin();
              it != string_list_.end();
              ++it)
         {
@@ -189,7 +211,7 @@ public:
         OutputBuffer this_buffer(0);
         toWire(this_buffer);
         uint8_t const* const this_data = (uint8_t const*)this_buffer.getData();
-        size_t this_len = this_buffer.getLength();
+        const size_t this_len = this_buffer.getLength();
 
         OutputBuffer other_buffer(0);
         other.toWire(other_buffer);
@@ -214,11 +236,14 @@ private:
     std::vector<std::vector<uint8_t> > string_list_;
 };
 
-// END_RDATA_NAMESPACE
-// END_ISC_NAMESPACE
+}      // namespace detail
+}      // namespace generic
+}      // namespace rdata
+}      // namespace dns
+}      // namespace isc
 
 #endif //  TXT_LIKE_H
 
-// Local Variables: 
+// Local Variables:
 // mode: c++
-// End: 
+// End:
diff --git a/src/lib/dns/rdata/generic/hinfo_13.cc b/src/lib/dns/rdata/generic/hinfo_13.cc
index 12f034c..77bfdfd 100644
--- a/src/lib/dns/rdata/generic/hinfo_13.cc
+++ b/src/lib/dns/rdata/generic/hinfo_13.cc
@@ -39,11 +39,22 @@ using namespace isc::dns::characterstr;
 
 HINFO::HINFO(const std::string& hinfo_str) {
     string::const_iterator input_iterator = hinfo_str.begin();
-    cpu_ = getNextCharacterString(hinfo_str, input_iterator);
 
-    skipLeftSpaces(hinfo_str, input_iterator);
+    bool quoted;
+    cpu_ = getNextCharacterString(hinfo_str, input_iterator, &quoted);
+
+    skipLeftSpaces(hinfo_str, input_iterator, quoted);
 
     os_ = getNextCharacterString(hinfo_str, input_iterator);
+
+    // Skip whitespace at the end.
+    while (input_iterator < hinfo_str.end() && isspace(*input_iterator)) {
+        ++input_iterator;
+    }
+    if (input_iterator < hinfo_str.end()) {
+        isc_throw(InvalidRdataText,
+                  "Invalid HINFO text format: too many fields.");
+    }
 }
 
 HINFO::HINFO(InputBuffer& buffer, size_t rdata_len) {
@@ -108,16 +119,17 @@ HINFO::getOS() const {
 
 void
 HINFO::skipLeftSpaces(const std::string& input_str,
-                      std::string::const_iterator& input_iterator)
+                      std::string::const_iterator& input_iterator,
+                      bool optional)
 {
     if (input_iterator >= input_str.end()) {
         isc_throw(InvalidRdataText,
-                  "Invalid HINFO text format, field is missing.");
+                  "Invalid HINFO text format: field is missing.");
     }
 
-    if (!isspace(*input_iterator)) {
+    if (!isspace(*input_iterator) && !optional) {
         isc_throw(InvalidRdataText,
-            "Invalid HINFO text format, fields are not separated by space.");
+            "Invalid HINFO text format: fields are not separated by space.");
     }
     // Skip white spaces
     while (input_iterator < input_str.end() && isspace(*input_iterator)) {
diff --git a/src/lib/dns/rdata/generic/hinfo_13.h b/src/lib/dns/rdata/generic/hinfo_13.h
index 8513419..5534a7e 100644
--- a/src/lib/dns/rdata/generic/hinfo_13.h
+++ b/src/lib/dns/rdata/generic/hinfo_13.h
@@ -46,10 +46,17 @@ public:
 private:
     /// Skip the left whitespaces of the input string
     ///
+    /// If \c optional argument is \c true and no spaces occur at the
+    /// current location, then nothing happens. If \c optional is
+    /// \c false and no spaces occur at the current location, then
+    /// the \c InvalidRdataText exception is thrown.
+    ///
     /// \param input_str The input string
     /// \param input_iterator From which the skipping started
+    /// \param optional If true, the spaces are optionally skipped.
     void skipLeftSpaces(const std::string& input_str,
-                        std::string::const_iterator& input_iterator);
+                        std::string::const_iterator& input_iterator,
+                        bool optional);
 
     /// Helper template function for toWire()
     ///
diff --git a/src/lib/dns/rdata/generic/spf_99.cc b/src/lib/dns/rdata/generic/spf_99.cc
index aa3e4a1..4bf24e9 100644
--- a/src/lib/dns/rdata/generic/spf_99.cc
+++ b/src/lib/dns/rdata/generic/spf_99.cc
@@ -24,18 +24,17 @@
 #include <dns/rdata.h>
 #include <dns/rdataclass.h>
 
+/// This class implements the basic interfaces inherited from the abstract
+/// \c rdata::Rdata class. The semantics of the class is provided by
+/// a copy of instantiated TXTLikeImpl class common to both TXT and SPF.
+#include <dns/rdata/generic/detail/txt_like.h>
+
 using namespace std;
 using namespace isc::util;
 
 // BEGIN_ISC_NAMESPACE
 // BEGIN_RDATA_NAMESPACE
 
-/// This class implements the basic interfaces inherited from the abstract
-/// \c rdata::Rdata class. The semantics of the class is provided by
-/// a copy of instantiated TXTLikeImpl class common to both TXT and SPF.
-
-#include <dns/rdata/generic/detail/txt_like.h>
-
 /// \brief The assignment operator
 ///
 /// It internally allocates a resource, and if it fails a corresponding
@@ -67,6 +66,21 @@ SPF::SPF(InputBuffer& buffer, size_t rdata_len) :
     impl_(new SPFImpl(buffer, rdata_len))
 {}
 
+/// \brief Constructor using the master lexer.
+///
+/// This implementation only uses the \c lexer parameters; others are
+/// ignored.
+///
+/// \throw CharStringTooLong the parameter string length exceeds maximum.
+/// \throw InvalidRdataText the method cannot process the parameter data
+///
+/// \param lexer A \c MasterLexer object parsing a master file for this
+/// RDATA.
+SPF::SPF(MasterLexer& lexer, const Name*, MasterLoader::Options,
+         MasterLoaderCallbacks&) :
+    impl_(new SPFImpl(lexer))
+{}
+
 /// \brief Constructor from string.
 ///
 /// It internally allocates a resource, and if it fails a corresponding
diff --git a/src/lib/dns/rdata/generic/spf_99.h b/src/lib/dns/rdata/generic/spf_99.h
index 04ac99b..eece47b 100644
--- a/src/lib/dns/rdata/generic/spf_99.h
+++ b/src/lib/dns/rdata/generic/spf_99.h
@@ -28,7 +28,9 @@
 
 // BEGIN_RDATA_NAMESPACE
 
+namespace detail {
 template<class Type, uint16_t typeCode> class TXTLikeImpl;
+}
 
 /// \brief \c rdata::SPF class represents the SPF RDATA as defined %in
 /// RFC4408.
@@ -65,7 +67,7 @@ public:
     const std::vector<std::vector<uint8_t> >& getString() const;
 
 private:
-    typedef TXTLikeImpl<SPF, 99> SPFImpl;
+    typedef isc::dns::rdata::generic::detail::TXTLikeImpl<SPF, 99> SPFImpl;
     SPFImpl* impl_;
 };
 
diff --git a/src/lib/dns/rdata/generic/txt_16.cc b/src/lib/dns/rdata/generic/txt_16.cc
index 418bc05..1bd2eb1 100644
--- a/src/lib/dns/rdata/generic/txt_16.cc
+++ b/src/lib/dns/rdata/generic/txt_16.cc
@@ -23,6 +23,7 @@
 #include <dns/messagerenderer.h>
 #include <dns/rdata.h>
 #include <dns/rdataclass.h>
+#include <dns/rdata/generic/detail/txt_like.h>
 
 using namespace std;
 using namespace isc::util;
@@ -30,8 +31,6 @@ using namespace isc::util;
 // BEGIN_ISC_NAMESPACE
 // BEGIN_RDATA_NAMESPACE
 
-#include <dns/rdata/generic/detail/txt_like.h>
-
 TXT&
 TXT::operator=(const TXT& source) {
     if (impl_ == source.impl_) {
@@ -53,6 +52,21 @@ TXT::TXT(InputBuffer& buffer, size_t rdata_len) :
     impl_(new TXTImpl(buffer, rdata_len))
 {}
 
+/// \brief Constructor using the master lexer.
+///
+/// This implementation only uses the \c lexer parameters; others are
+/// ignored.
+///
+/// \throw CharStringTooLong the parameter string length exceeds maximum.
+/// \throw InvalidRdataText the method cannot process the parameter data
+///
+/// \param lexer A \c MasterLexer object parsing a master file for this
+/// RDATA.
+TXT::TXT(MasterLexer& lexer, const Name*, MasterLoader::Options,
+         MasterLoaderCallbacks&) :
+    impl_(new TXTImpl(lexer))
+{}
+
 TXT::TXT(const std::string& txtstr) :
     impl_(new TXTImpl(txtstr))
 {}
diff --git a/src/lib/dns/rdata/generic/txt_16.h b/src/lib/dns/rdata/generic/txt_16.h
index d99d69b..e434085 100644
--- a/src/lib/dns/rdata/generic/txt_16.h
+++ b/src/lib/dns/rdata/generic/txt_16.h
@@ -28,7 +28,9 @@
 
 // BEGIN_RDATA_NAMESPACE
 
+namespace detail {
 template<class Type, uint16_t typeCode> class TXTLikeImpl;
+}
 
 class TXT : public Rdata {
 public:
@@ -39,7 +41,7 @@ public:
     ~TXT();
 
 private:
-    typedef TXTLikeImpl<TXT, 16> TXTImpl;
+    typedef isc::dns::rdata::generic::detail::TXTLikeImpl<TXT, 16> TXTImpl;
     TXTImpl* impl_;
 };
 
diff --git a/src/lib/dns/rrparamregistry-placeholder.cc b/src/lib/dns/rrparamregistry-placeholder.cc
index 2208d3f..16ec23c 100644
--- a/src/lib/dns/rrparamregistry-placeholder.cc
+++ b/src/lib/dns/rrparamregistry-placeholder.cc
@@ -34,7 +34,6 @@
 #include <dns/rdataclass.h>
 
 using namespace std;
-using namespace boost;
 
 using namespace isc::util;
 using namespace isc::dns::rdata;
@@ -512,6 +511,27 @@ RRParamRegistry::codeToClassText(uint16_t code) const {
                                                      impl_->code2classmap));
 }
 
+namespace {
+inline const AbstractRdataFactory*
+findRdataFactory(RRParamRegistryImpl* reg_impl,
+                 const RRType& rrtype, const RRClass& rrclass)
+{
+    RdataFactoryMap::const_iterator found;
+    found = reg_impl->rdata_factories.find(RRTypeClass(rrtype, rrclass));
+    if (found != reg_impl->rdata_factories.end()) {
+        return (found->second.get());
+    }
+
+    GenericRdataFactoryMap::const_iterator genfound =
+        reg_impl->genericrdata_factories.find(rrtype);
+    if (genfound != reg_impl->genericrdata_factories.end()) {
+        return (genfound->second.get());
+    }
+
+    return (NULL);
+}
+}
+
 RdataPtr
 RRParamRegistry::createRdata(const RRType& rrtype, const RRClass& rrclass,
                              const std::string& rdata_string)
@@ -519,16 +539,10 @@ RRParamRegistry::createRdata(const RRType& rrtype, const RRClass& rrclass,
     // If the text indicates that it's rdata of an "unknown" type (beginning
     // with '\# n'), parse it that way. (TBD)
 
-    RdataFactoryMap::const_iterator found;
-    found = impl_->rdata_factories.find(RRTypeClass(rrtype, rrclass));
-    if (found != impl_->rdata_factories.end()) {
-        return (found->second->create(rdata_string));
-    }
-
-    GenericRdataFactoryMap::const_iterator genfound =
-        impl_->genericrdata_factories.find(rrtype);
-    if (genfound != impl_->genericrdata_factories.end()) {
-        return (genfound->second->create(rdata_string));
+    const AbstractRdataFactory* factory =
+        findRdataFactory(impl_, rrtype, rrclass);
+    if (factory != NULL) {
+        return (factory->create(rdata_string));
     }
 
     return (RdataPtr(new generic::Generic(rdata_string)));
@@ -538,16 +552,10 @@ RdataPtr
 RRParamRegistry::createRdata(const RRType& rrtype, const RRClass& rrclass,
                              InputBuffer& buffer, size_t rdata_len)
 {
-    RdataFactoryMap::const_iterator found =
-        impl_->rdata_factories.find(RRTypeClass(rrtype, rrclass));
-    if (found != impl_->rdata_factories.end()) {
-        return (found->second->create(buffer, rdata_len));
-    }
-
-    GenericRdataFactoryMap::const_iterator genfound =
-        impl_->genericrdata_factories.find(rrtype);
-    if (genfound != impl_->genericrdata_factories.end()) {
-        return (genfound->second->create(buffer, rdata_len));
+    const AbstractRdataFactory* factory =
+        findRdataFactory(impl_, rrtype, rrclass);
+    if (factory != NULL) {
+        return (factory->create(buffer, rdata_len));
     }
 
     return (RdataPtr(new generic::Generic(buffer, rdata_len)));
@@ -557,16 +565,10 @@ RdataPtr
 RRParamRegistry::createRdata(const RRType& rrtype, const RRClass& rrclass,
                              const Rdata& source)
 {
-    RdataFactoryMap::const_iterator found =
-        impl_->rdata_factories.find(RRTypeClass(rrtype, rrclass));
-    if (found != impl_->rdata_factories.end()) {
-        return (found->second->create(source));
-    }
-
-    GenericRdataFactoryMap::const_iterator genfound =
-        impl_->genericrdata_factories.find(rrtype);
-    if (genfound != impl_->genericrdata_factories.end()) {
-        return (genfound->second->create(source));
+    const AbstractRdataFactory* factory =
+        findRdataFactory(impl_, rrtype, rrclass);
+    if (factory != NULL) {
+        return (factory->create(source));
     }
 
     return (RdataPtr(new rdata::generic::Generic(
@@ -579,16 +581,10 @@ RRParamRegistry::createRdata(const RRType& rrtype, const RRClass& rrclass,
                              MasterLoader::Options options,
                              MasterLoaderCallbacks& callbacks)
 {
-    RdataFactoryMap::const_iterator found =
-        impl_->rdata_factories.find(RRTypeClass(rrtype, rrclass));
-    if (found != impl_->rdata_factories.end()) {
-        return (found->second->create(lexer, name, options, callbacks));
-    }
-
-    GenericRdataFactoryMap::const_iterator genfound =
-        impl_->genericrdata_factories.find(rrtype);
-    if (genfound != impl_->genericrdata_factories.end()) {
-        return (genfound->second->create(lexer, name, options, callbacks));
+    const AbstractRdataFactory* factory =
+        findRdataFactory(impl_, rrtype, rrclass);
+    if (factory != NULL) {
+        return (factory->create(lexer, name, options, callbacks));
     }
 
     return (RdataPtr(new generic::Generic(lexer, name, options, callbacks)));
diff --git a/src/lib/dns/tests/Makefile.am b/src/lib/dns/tests/Makefile.am
index fa166fe..5f2ae5c 100644
--- a/src/lib/dns/tests/Makefile.am
+++ b/src/lib/dns/tests/Makefile.am
@@ -37,6 +37,7 @@ run_unittests_SOURCES += opcode_unittest.cc
 run_unittests_SOURCES += rcode_unittest.cc
 run_unittests_SOURCES += rdata_unittest.h rdata_unittest.cc
 run_unittests_SOURCES += rdatafields_unittest.cc
+run_unittests_SOURCES += rdata_char_string_unittest.cc
 run_unittests_SOURCES += rdata_in_a_unittest.cc rdata_in_aaaa_unittest.cc
 run_unittests_SOURCES += rdata_ns_unittest.cc rdata_soa_unittest.cc
 run_unittests_SOURCES += rdata_txt_like_unittest.cc
diff --git a/src/lib/dns/tests/character_string_unittest.cc b/src/lib/dns/tests/character_string_unittest.cc
index 5fed9eb..e8f5884 100644
--- a/src/lib/dns/tests/character_string_unittest.cc
+++ b/src/lib/dns/tests/character_string_unittest.cc
@@ -33,11 +33,13 @@ class CharacterString {
 public:
     CharacterString(const string& str){
         string::const_iterator it = str.begin();
-        characterStr_ = getNextCharacterString(str, it);
+        characterStr_ = getNextCharacterString(str, it, &is_quoted_);
     }
     const string& str() const { return characterStr_; }
+    bool quoted() const { return (is_quoted_); }
 private:
     string characterStr_;
+    bool is_quoted_;
 };
 
 TEST(CharacterStringTest, testNormalCase) {
@@ -47,14 +49,17 @@ TEST(CharacterStringTest, testNormalCase) {
     // Test <character-string> that separated by space
     CharacterString cstr2("foo bar");
     EXPECT_EQ(string("foo"), cstr2.str());
+    EXPECT_FALSE(cstr2.quoted());
 
     // Test <character-string> that separated by quotes
     CharacterString cstr3("\"foo bar\"");
     EXPECT_EQ(string("foo bar"), cstr3.str());
+    EXPECT_TRUE(cstr3.quoted());
 
     // Test <character-string> that not separate by quotes but ended with quotes
     CharacterString cstr4("foo\"");
     EXPECT_EQ(string("foo\""), cstr4.str());
+    EXPECT_FALSE(cstr4.quoted());
 }
 
 TEST(CharacterStringTest, testBadCase) {
diff --git a/src/lib/dns/tests/master_lexer_unittest.cc b/src/lib/dns/tests/master_lexer_unittest.cc
index b751da8..42ed3fb 100644
--- a/src/lib/dns/tests/master_lexer_unittest.cc
+++ b/src/lib/dns/tests/master_lexer_unittest.cc
@@ -60,8 +60,10 @@ TEST_F(MasterLexerTest, preOpen) {
 }
 
 TEST_F(MasterLexerTest, pushStream) {
+    EXPECT_EQ(0, lexer.getSourceCount());
     lexer.pushSource(ss);
     EXPECT_EQ(expected_stream_name, lexer.getSourceName());
+    EXPECT_EQ(1, lexer.getSourceCount());
 
     // From the point of view of this test, we only have to check (though
     // indirectly) getSourceLine calls InputSource::getCurrentLine.  It should
@@ -70,18 +72,22 @@ TEST_F(MasterLexerTest, pushStream) {
 
     // By popping it the stack will be empty again.
     lexer.popSource();
+    EXPECT_EQ(0, lexer.getSourceCount());
     checkEmptySource(lexer);
 }
 
 TEST_F(MasterLexerTest, pushFile) {
     // We use zone file (-like) data, but in this test that actually doesn't
     // matter.
+    EXPECT_EQ(0, lexer.getSourceCount());
     EXPECT_TRUE(lexer.pushSource(TEST_DATA_SRCDIR "/masterload.txt"));
+    EXPECT_EQ(1, lexer.getSourceCount());
     EXPECT_EQ(TEST_DATA_SRCDIR "/masterload.txt", lexer.getSourceName());
     EXPECT_EQ(1, lexer.getSourceLine());
 
     lexer.popSource();
     checkEmptySource(lexer);
+    EXPECT_EQ(0, lexer.getSourceCount());
 
     // If we give a non NULL string pointer, its content will be intact
     // if pushSource succeeds.
@@ -300,6 +306,11 @@ lexerErrorCheck(MasterLexer& lexer, MasterToken::Type expect,
 }
 
 // Common checks regarding expected/unexpected end-of-line
+//
+// The 'lexer' should be at a position before two consecutive '\n's.
+// The first one will be recognized, and the second one will be considered an
+// unexpected token.  Then this helper consumes the second '\n', so the caller
+// can continue the test after these '\n's.
 void
 eolCheck(MasterLexer& lexer, MasterToken::Type expect) {
     // If EOL is found and eol_ok is true, we get it.
@@ -313,9 +324,14 @@ eolCheck(MasterLexer& lexer, MasterToken::Type expect) {
 
     // And also check the error token set in the exception object.
     lexerErrorCheck(lexer, expect, MasterToken::UNEXPECTED_END);
+
+    // Then skip the 2nd '\n'
+    EXPECT_EQ(MasterToken::END_OF_LINE, lexer.getNextToken().getType());
 }
 
 // Common checks regarding expected/unexpected end-of-file
+//
+// The 'lexer' should be at a position just before an end-of-file.
 void
 eofCheck(MasterLexer& lexer, MasterToken::Type expect) {
     EXPECT_EQ(MasterToken::END_OF_FILE,
@@ -335,9 +351,6 @@ TEST_F(MasterLexerTest, getNextTokenString) {
               lexer.getNextToken(MasterToken::STRING).getString());
     eolCheck(lexer, MasterToken::STRING);
 
-    // Skip the 2nd '\n'
-    EXPECT_EQ(MasterToken::END_OF_LINE, lexer.getNextToken().getType());
-
     // Same set of tests but for end-of-file
     EXPECT_EQ("another-string",
               lexer.getNextToken(MasterToken::STRING, true).getString());
@@ -355,9 +368,6 @@ TEST_F(MasterLexerTest, getNextTokenQString) {
               lexer.getNextToken(MasterToken::QSTRING).getString());
     eolCheck(lexer, MasterToken::QSTRING);
 
-    // Skip the 2nd '\n'
-    EXPECT_EQ(MasterToken::END_OF_LINE, lexer.getNextToken().getType());
-
     // Expecting a quoted string but see a normal string.  It's okay.
     EXPECT_EQ("normal-string",
               lexer.getNextToken(MasterToken::QSTRING).getString());
@@ -369,6 +379,7 @@ TEST_F(MasterLexerTest, getNextTokenNumber) {
     ss << "\n";
     ss << "4294967296 ";        // =2^32, out of range
     ss << "not-a-number ";
+    ss << "123abc "; // starting with digits, but resulting in a string
     ss << "86400";
     lexer.pushSource(ss);
 
@@ -377,9 +388,6 @@ TEST_F(MasterLexerTest, getNextTokenNumber) {
               lexer.getNextToken(MasterToken::NUMBER).getNumber());
     eolCheck(lexer, MasterToken::NUMBER);
 
-    // Skip the 2nd '\n'
-    EXPECT_EQ(MasterToken::END_OF_LINE, lexer.getNextToken().getType());
-
     // Expecting a number, but it's too big for uint32.
     lexerErrorCheck(lexer, MasterToken::NUMBER,
                     MasterToken::NUMBER_OUT_OF_RANGE);
@@ -391,6 +399,11 @@ TEST_F(MasterLexerTest, getNextTokenNumber) {
     // The unexpected string should have been "ungotten".  Re-read and skip it.
     EXPECT_EQ(MasterToken::STRING, lexer.getNextToken().getType());
 
+    // Expecting a number, but see a string.
+    lexerErrorCheck(lexer, MasterToken::NUMBER, MasterToken::BAD_NUMBER);
+    // The unexpected string should have been "ungotten".  Re-read and skip it.
+    EXPECT_EQ(MasterToken::STRING, lexer.getNextToken().getType());
+
     // Unless we specify NUMBER, decimal number string should be recognized
     // as a string.
     EXPECT_EQ("86400",
diff --git a/src/lib/dns/tests/master_loader_unittest.cc b/src/lib/dns/tests/master_loader_unittest.cc
index 59fd9d0..f882def 100644
--- a/src/lib/dns/tests/master_loader_unittest.cc
+++ b/src/lib/dns/tests/master_loader_unittest.cc
@@ -123,6 +123,10 @@ public:
         checkRR("www.example.org", RRType::A(), "192.0.2.1");
     }
 
+    void checkARR(const string& name) {
+        checkRR(name, RRType::A(), "192.0.2.1");
+    }
+
     MasterLoaderCallbacks callbacks_;
     boost::scoped_ptr<MasterLoader> loader_;
     vector<string> errors_;
@@ -179,6 +183,46 @@ TEST_F(MasterLoaderTest, include) {
     }
 }
 
+TEST_F(MasterLoaderTest, origin) {
+    // Varius 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());
+        EXPECT_TRUE(warnings_.empty());
+
+        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
@@ -283,6 +327,8 @@ struct ErrorCase {
     // 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",
diff --git a/src/lib/dns/tests/rdata_char_string_unittest.cc b/src/lib/dns/tests/rdata_char_string_unittest.cc
new file mode 100644
index 0000000..9d23622
--- /dev/null
+++ b/src/lib/dns/tests/rdata_char_string_unittest.cc
@@ -0,0 +1,147 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <util/unittests/wiredata.h>
+
+#include <dns/rdata.h>
+#include <dns/rdata/generic/detail/char_string.h>
+
+#include <gtest/gtest.h>
+
+#include <string>
+#include <vector>
+
+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::util::unittests::matchWireData;
+
+namespace {
+const uint8_t test_charstr[] = {
+    sizeof("Test String") - 1,
+    'T', 'e', 's', 't', ' ', 'S', 't', 'r', 'i', 'n', 'g'
+};
+
+class CharStringTest : public ::testing::Test {
+protected:
+    CharStringTest() :
+        // char-string representation for test data using two types of escape
+        // ('r' = 114)
+        test_str("Test\\ St\\114ing")
+    {
+        str_region.beg = &test_str[0];
+        str_region.len = test_str.size();
+    }
+    CharString chstr;           // place holder
+    const std::string test_str;
+    MasterToken::StringRegion str_region;
+};
+
+MasterToken::StringRegion
+createStringRegion(const std::string& str) {
+    MasterToken::StringRegion region;
+    region.beg = &str[0]; // note std ensures this works even if str is empty
+    region.len = str.size();
+    return (region);
+}
+
+TEST_F(CharStringTest, normalConversion) {
+    uint8_t tmp[3];             // placeholder for expected sequence
+
+    strToCharString(str_region, chstr);
+    matchWireData(test_charstr, sizeof(test_charstr), &chstr[0], chstr.size());
+
+    // Empty string
+    chstr.clear();
+    strToCharString(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);
+    std::vector<uint8_t> expected;
+    expected.push_back(255);    // len of char string
+    expected.insert(expected.end(), long_str.begin(), long_str.end());
+    matchWireData(&expected[0], expected.size(), &chstr[0], chstr.size());
+
+    // Same data as the previous case, but the original string is longer than
+    // the max; this shouldn't be rejected
+    chstr.clear();
+    long_str.at(254) = '\\';    // replace the last 'x' with '\'
+    long_str.append("120");     // 'x' = 120
+    strToCharString(createStringRegion(long_str), chstr);
+    matchWireData(&expected[0], expected.size(), &chstr[0], chstr.size());
+
+    // Escaped '\'
+    chstr.clear();
+    tmp[0] = 1;
+    tmp[1] = '\\';
+    strToCharString(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);
+    matchWireData(tmp, 2, &chstr[0], chstr.size());
+
+    chstr.clear();
+    strToCharString(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);
+    tmp[0] = 2;                 // string len is now 2
+    tmp[2] = '0';
+    matchWireData(tmp, 3, &chstr[0], chstr.size());
+}
+
+TEST_F(CharStringTest, badConversion) {
+    // string cannot exceed 255 bytes
+    EXPECT_THROW(strToCharString(createStringRegion(std::string(256, 'a')),
+                                 chstr),
+                 CharStringTooLong);
+
+    // input string ending with (non escaped) '\'
+    chstr.clear();
+    EXPECT_THROW(strToCharString(createStringRegion("foo\\"), chstr),
+                 InvalidRdataText);
+}
+
+TEST_F(CharStringTest, badDDD) {
+    // Check various type of bad form of \DDD
+
+    // Not a number
+    EXPECT_THROW(strToCharString(createStringRegion("\\1a2"), chstr),
+                 InvalidRdataText);
+    EXPECT_THROW(strToCharString(createStringRegion("\\12a"), chstr),
+                 InvalidRdataText);
+
+    // Not in the range of uint8_t
+    EXPECT_THROW(strToCharString(createStringRegion("\\256"), chstr),
+                 InvalidRdataText);
+
+    // Short buffer
+    EXPECT_THROW(strToCharString(createStringRegion("\\42"), chstr),
+                 InvalidRdataText);
+}
+
+} // unnamed namespace
diff --git a/src/lib/dns/tests/rdata_hinfo_unittest.cc b/src/lib/dns/tests/rdata_hinfo_unittest.cc
index 7469047..f592066 100644
--- a/src/lib/dns/tests/rdata_hinfo_unittest.cc
+++ b/src/lib/dns/tests/rdata_hinfo_unittest.cc
@@ -41,6 +41,7 @@ static uint8_t hinfo_rdata[] = {0x07,0x50,0x65,0x6e,0x74,0x69,0x75,0x6d,0x05,
 static const char *hinfo_str = "\"Pentium\" \"Linux\"";
 static const char *hinfo_str1 = "\"Pen\\\"tium\" \"Linux\"";
 
+static const char *hinfo_str_equal = "\"Pentium\"\"Linux\"";
 static const char *hinfo_str_small1 = "\"Lentium\" \"Linux\"";
 static const char *hinfo_str_small2 = "\"Pentium\" \"Kinux\"";
 static const char *hinfo_str_large1 = "\"Qentium\" \"Linux\"";
@@ -57,8 +58,11 @@ TEST_F(Rdata_HINFO_Test, createFromText) {
 }
 
 TEST_F(Rdata_HINFO_Test, badText) {
-    // Fields must be seperated by spaces
-    EXPECT_THROW(const HINFO hinfo("\"Pentium\"\"Linux\""), InvalidRdataText);
+    // Only 2 fields must exist
+    EXPECT_THROW(const HINFO hinfo("\"Pentium\"\"Linux\"\"Computer\""),
+                 InvalidRdataText);
+    EXPECT_THROW(const HINFO hinfo("\"Pentium\" \"Linux\" \"Computer\""),
+                 InvalidRdataText);
     // Field cannot be missing
     EXPECT_THROW(const HINFO hinfo("Pentium"), InvalidRdataText);
     // The <character-string> cannot exceed 255 characters
@@ -82,10 +86,16 @@ TEST_F(Rdata_HINFO_Test, createFromLexer) {
     EXPECT_EQ(0, rdata_hinfo.compare(
         *test::createRdataUsingLexer(RRType::HINFO(), RRClass::IN(),
                                      hinfo_str)));
-
+    EXPECT_EQ(0, rdata_hinfo.compare(
+        *test::createRdataUsingLexer(RRType::HINFO(), RRClass::IN(),
+                                     "\"Pentium\"\"Linux\"")));
     // Exceptions cause NULL to be returned.
     EXPECT_FALSE(test::createRdataUsingLexer(RRType::HINFO(), RRClass::IN(),
-                                             "\"Pentium\"\"Linux\""));
+                                             "\"Pentium\"\"Linux\""
+                                             "\"Computer\""));
+    EXPECT_FALSE(test::createRdataUsingLexer(RRType::HINFO(), RRClass::IN(),
+                                             "\"Pentium\" \"Linux\" "
+                                             "\"Computer\""));
 }
 
 TEST_F(Rdata_HINFO_Test, toText) {
@@ -118,6 +128,7 @@ TEST_F(Rdata_HINFO_Test, compare) {
     HINFO hinfo_large2(hinfo_str_large2);
 
     EXPECT_EQ(0, hinfo.compare(HINFO(hinfo_str)));
+    EXPECT_EQ(0, hinfo.compare(HINFO(hinfo_str_equal)));
     EXPECT_EQ(1, hinfo.compare(HINFO(hinfo_str_small1)));
     EXPECT_EQ(1, hinfo.compare(HINFO(hinfo_str_small2)));
     EXPECT_EQ(-1, hinfo.compare(HINFO(hinfo_str_large1)));
diff --git a/src/lib/dns/tests/rdata_txt_like_unittest.cc b/src/lib/dns/tests/rdata_txt_like_unittest.cc
index 7ff9ac9..b0a572d 100644
--- a/src/lib/dns/tests/rdata_txt_like_unittest.cc
+++ b/src/lib/dns/tests/rdata_txt_like_unittest.cc
@@ -17,17 +17,25 @@
 #include <util/buffer.h>
 #include <dns/exceptions.h>
 #include <dns/rdataclass.h>
-#include <gtest/gtest.h>
 
 #include <dns/tests/unittest_util.h>
 #include <dns/tests/rdata_unittest.h>
 
+#include <gtest/gtest.h>
+
+#include <boost/bind.hpp>
+
+#include <string>
+#include <sstream>
+#include <vector>
+
 using isc::UnitTestUtil;
 using namespace std;
 using namespace isc::dns;
 using namespace isc::util;
 using namespace isc::dns::rdata;
 
+namespace {
 
 template<class T>
 class RRTYPE : public RRType {
@@ -38,36 +46,42 @@ public:
 template<> RRTYPE<generic::TXT>::RRTYPE() : RRType(RRType::TXT()) {}
 template<> RRTYPE<generic::SPF>::RRTYPE() : RRType(RRType::SPF()) {}
 
-namespace {
 const uint8_t wiredata_txt_like[] = {
-    sizeof("Test String") - 1,
-    'T', 'e', 's', 't', ' ', 'S', 't', 'r', 'i', 'n', 'g'
+    sizeof("Test-String") - 1,
+    'T', 'e', 's', 't', '-', 'S', 't', 'r', 'i', 'n', 'g'
 };
 
 const uint8_t wiredata_nulltxt[] = { 0 };
-vector<uint8_t> wiredata_longesttxt(256, 'a');
+
+// 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() {
+    Rdata_TXT_LIKE_Test() :
+        callback(boost::bind(&dummyCallback, _1, _2, _3)),
+        loader_cb(callback, callback),
+        wiredata_longesttxt(256, 'a'),
+        rdata_txt_like("Test-String"),
+        rdata_txt_like_empty("\"\""),
+        rdata_txt_like_quoted("\"Test-String\"")
+    {
         wiredata_longesttxt[0] = 255; // adjust length
     }
 
-    static const TXT_LIKE rdata_txt_like;
-    static const TXT_LIKE rdata_txt_like_empty;
-    static const TXT_LIKE rdata_txt_like_quoted;
-};
-
-template<class TXT_LIKE>
-const TXT_LIKE Rdata_TXT_LIKE_Test<TXT_LIKE>::rdata_txt_like("Test String");
-
-template<class TXT_LIKE>
-const TXT_LIKE Rdata_TXT_LIKE_Test<TXT_LIKE>::rdata_txt_like_empty("");
+private:
+    const MasterLoaderCallbacks::IssueCallback callback;
 
-template<class TXT_LIKE>
-const TXT_LIKE Rdata_TXT_LIKE_Test<TXT_LIKE>::rdata_txt_like_quoted
-                                                          ("\"Test String\"");
+protected:
+    MasterLoaderCallbacks loader_cb;
+    vector<uint8_t> wiredata_longesttxt;
+    const TXT_LIKE rdata_txt_like;
+    const TXT_LIKE rdata_txt_like_empty;
+    const TXT_LIKE rdata_txt_like_quoted;
+};
 
 // The list of types we want to test.
 typedef testing::Types<generic::TXT, generic::SPF> Implementations;
@@ -75,37 +89,155 @@ typedef testing::Types<generic::TXT, generic::SPF> Implementations;
 TYPED_TEST_CASE(Rdata_TXT_LIKE_Test, Implementations);
 
 TYPED_TEST(Rdata_TXT_LIKE_Test, createFromText) {
-    // normal case is covered in toWireBuffer.
+    // Below we check the behavior for the "from text" constructors, both
+    // from std::string and with MasterLexer.  The underlying implementation
+    // is the same, so both should work exactly same, but we confirm both
+    // cases.
+
+    const std::string multi_line = "(\n \"Test-String\" )";
+    const std::string escaped_txt = "Test\\045Strin\\g";
+
+    // test input for the lexer version
+    std::stringstream ss;
+    ss << "Test-String\n";
+    ss << "\"Test-String\"\n";   // explicitly surrounded by '"'s
+    ss << multi_line << "\n";   // multi-line text with ()
+    ss << escaped_txt << "\n";   // using the two types of escape with '\'
+    ss << "\"\"\n";              // empty string (note: still valid char-str)
+    ss << string(255, 'a') << "\n"; // Longest possible character-string.
+    ss << string(256, 'a') << "\n"; // char-string too long
+    ss << "\"Test-String\\\"\n";    // unbalanced quote
+    ss << "\"Test-String\\\"\"\n";
+    this->lexer.pushSource(ss);
+
+    // commonly used Rdata to compare below, created from wire
+    ConstRdataPtr const rdata =
+        this->rdataFactoryFromFile(RRTYPE<TypeParam>(),
+                                   RRClass("IN"), "rdata_txt_fromWire1");
+
+    // normal case is covered in toWireBuffer.  First check the std::string
+    // case, then with MasterLexer.  For the latter, we need to read and skip
+    // '\n'.  These apply to most of the other cases below.
+    EXPECT_EQ(0, this->rdata_txt_like.compare(*rdata));
+    EXPECT_EQ(0, TypeParam(this->lexer, NULL, MasterLoader::MANY_ERRORS,
+                           this->loader_cb).compare(*rdata));
+    EXPECT_EQ(MasterToken::END_OF_LINE, this->lexer.getNextToken().getType());
 
     // surrounding double-quotes shouldn't change the result.
-    EXPECT_EQ(0, this->rdata_txt_like.compare(this->rdata_txt_like_quoted));
+    EXPECT_EQ(0, this->rdata_txt_like_quoted.compare(*rdata));
+    EXPECT_EQ(0, TypeParam(this->lexer, NULL, MasterLoader::MANY_ERRORS,
+                           this->loader_cb).compare(*rdata));
+    EXPECT_EQ(MasterToken::END_OF_LINE, this->lexer.getNextToken().getType());
+
+    // multi-line input with ()
+    EXPECT_EQ(0, TypeParam(multi_line).compare(*rdata));
+    EXPECT_EQ(0, TypeParam(this->lexer, NULL, MasterLoader::MANY_ERRORS,
+                           this->loader_cb).compare(*rdata));
+    EXPECT_EQ(MasterToken::END_OF_LINE, this->lexer.getNextToken().getType());
+
+    // for the same data using escape
+    EXPECT_EQ(0, TypeParam(escaped_txt).compare(*rdata));
+    EXPECT_EQ(0, TypeParam(this->lexer, NULL, MasterLoader::MANY_ERRORS,
+                           this->loader_cb).compare(*rdata));
+    EXPECT_EQ(MasterToken::END_OF_LINE, this->lexer.getNextToken().getType());
 
     // Null character-string.
     this->obuffer.clear();
-    TypeParam(string("")).toWire(this->obuffer);
+    TypeParam(string("\"\"")).toWire(this->obuffer);
     EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
-                        this->obuffer.getData(),
-                        this->obuffer.getLength(),
+                        this->obuffer.getData(), this->obuffer.getLength(),
                         wiredata_nulltxt, sizeof(wiredata_nulltxt));
+    this->obuffer.clear();
+    TypeParam(this->lexer, NULL, MasterLoader::MANY_ERRORS, this->loader_cb).
+        toWire(this->obuffer);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
+                        this->obuffer.getData(), this->obuffer.getLength(),
+                        wiredata_nulltxt, sizeof(wiredata_nulltxt));
+    EXPECT_EQ(MasterToken::END_OF_LINE, this->lexer.getNextToken().getType());
 
     // Longest possible character-string.
     this->obuffer.clear();
     TypeParam(string(255, 'a')).toWire(this->obuffer);
     EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
-                        this->obuffer.getData(),
-                        this->obuffer.getLength(),
-                        &wiredata_longesttxt[0], wiredata_longesttxt.size());
+                        this->obuffer.getData(), this->obuffer.getLength(),
+                        &this->wiredata_longesttxt[0],
+                        this->wiredata_longesttxt.size());
+    this->obuffer.clear();
+    TypeParam(this->lexer, NULL, MasterLoader::MANY_ERRORS, this->loader_cb).
+        toWire(this->obuffer);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
+                        this->obuffer.getData(), this->obuffer.getLength(),
+                        &this->wiredata_longesttxt[0],
+                        this->wiredata_longesttxt.size());
+    EXPECT_EQ(MasterToken::END_OF_LINE, this->lexer.getNextToken().getType());
 
     // Too long text for a valid character-string.
     EXPECT_THROW(TypeParam(string(256, 'a')), CharStringTooLong);
+    EXPECT_THROW(TypeParam(this->lexer, NULL, MasterLoader::MANY_ERRORS,
+                           this->loader_cb), CharStringTooLong);
+    EXPECT_EQ(MasterToken::END_OF_LINE, this->lexer.getNextToken().getType());
 
     // The escape character makes the double quote a part of character-string,
     // so this is invalid input and should be rejected.
-    EXPECT_THROW(TypeParam("\"Test String\\\""), InvalidRdataText);
+    EXPECT_THROW(TypeParam("\"Test-String\\\""), InvalidRdataText);
+    EXPECT_THROW(TypeParam(this->lexer, NULL, MasterLoader::MANY_ERRORS,
+                           this->loader_cb), MasterLexer::LexerError);
+    EXPECT_EQ(MasterToken::END_OF_LINE, this->lexer.getNextToken().getType());
+}
 
-    // Terminating double-quote is provided, so this is valid, but in this
-    // version of implementation we reject escaped characters.
-    EXPECT_THROW(TypeParam("\"Test String\\\"\""), InvalidRdataText);
+TYPED_TEST(Rdata_TXT_LIKE_Test, createMultiStringsFromText) {
+    // Tests for "from text" variants construction with various forms of
+    // multi character-strings.
+
+    std::vector<std::string > texts;
+    texts.push_back("\"Test-String\" \"Test-String\""); // most common form
+    texts.push_back("\"Test-String\"\"Test-String\"");  // no space between'em
+    texts.push_back("\"Test-String\" Test-String");  // no '"' for one
+    texts.push_back("\"Test-String\"Test-String"); // and no space either
+    texts.push_back("Test-String \"Test-String\""); // no '"' for the other
+    // This one currently doesn't work
+    //texts.push_back("Test-String\"Test-String\""); // and no space either
+
+    std::stringstream ss;
+    for (std::vector<std::string >::const_iterator it = texts.begin();
+         it != texts.end(); ++it) {
+        ss << *it << "\n";
+    }
+    this->lexer.pushSource(ss);
+
+    // The corresponding Rdata built from wire to compare in the checks below.
+    ConstRdataPtr const rdata =
+        this->rdataFactoryFromFile(RRTYPE<TypeParam>(),
+                                   RRClass("IN"), "rdata_txt_fromWire3.wire");
+
+    // Confirm we can construct the Rdata from the test text, both from
+    // std::string and with lexer, and that matches the from-wire data.
+    for (std::vector<std::string >::const_iterator it = texts.begin();
+         it != texts.end(); ++it) {
+        SCOPED_TRACE(*it);
+        EXPECT_EQ(0, TypeParam(*it).compare(*rdata));
+
+        EXPECT_EQ(0, TypeParam(this->lexer, NULL, MasterLoader::MANY_ERRORS,
+                               this->loader_cb).compare(*rdata));
+        EXPECT_EQ(MasterToken::END_OF_LINE,
+                  this->lexer.getNextToken().getType());
+    }
+}
+
+TYPED_TEST(Rdata_TXT_LIKE_Test, createFromTextExtra) {
+    // This is for the std::string version only: the input must end with EOF;
+    // an extra new-line will result in an exception.
+    EXPECT_THROW(TypeParam("\"Test-String\"\n"), InvalidRdataText);
+    // Same if there's a space before '\n'
+    EXPECT_THROW(TypeParam("\"Test-String\" \n"), InvalidRdataText);
+}
+
+TYPED_TEST(Rdata_TXT_LIKE_Test, fromTextEmpty) {
+    // If the input text doesn't contain any character-string, it should be
+    // rejected
+    EXPECT_THROW(TypeParam(""), InvalidRdataText);
+    EXPECT_THROW(TypeParam(" "), InvalidRdataText); // even with a space
+    EXPECT_THROW(TypeParam("(\n)"), InvalidRdataText); // or multi-line with ()
 }
 
 void
@@ -129,13 +261,15 @@ makeLargest(vector<uint8_t>& data) {
 
 TYPED_TEST(Rdata_TXT_LIKE_Test, createFromWire) {
     EXPECT_EQ(0, this->rdata_txt_like.compare(
-                  *this->rdataFactoryFromFile(RRTYPE<TypeParam>(), RRClass("IN"),
-                                        "rdata_txt_fromWire1")));
+                  *this->rdataFactoryFromFile(RRTYPE<TypeParam>(),
+                                              RRClass("IN"),
+                                              "rdata_txt_fromWire1")));
 
     // Empty character string
     EXPECT_EQ(0, this->rdata_txt_like_empty.compare(
-                  *this->rdataFactoryFromFile(RRTYPE<TypeParam>(), RRClass("IN"),
-                                        "rdata_txt_fromWire2.wire")));
+                  *this->rdataFactoryFromFile(RRTYPE<TypeParam>(),
+                                              RRClass("IN"),
+                                              "rdata_txt_fromWire2.wire")));
 
     // Multiple character strings
     this->obuffer.clear();
@@ -188,13 +322,7 @@ TYPED_TEST(Rdata_TXT_LIKE_Test, createFromWire) {
 TYPED_TEST(Rdata_TXT_LIKE_Test, createFromLexer) {
     EXPECT_EQ(0, this->rdata_txt_like.compare(
         *test::createRdataUsingLexer(RRTYPE<TypeParam>(), RRClass::IN(),
-                                     "Test String")));
-    EXPECT_EQ(0, this->rdata_txt_like_empty.compare(
-        *test::createRdataUsingLexer(RRTYPE<TypeParam>(), RRClass::IN(),
-                                     "")));
-    EXPECT_EQ(0, this->rdata_txt_like_quoted.compare(
-        *test::createRdataUsingLexer(RRTYPE<TypeParam>(), RRClass::IN(),
-                                     "\"Test String\"")));
+                                     "Test-String")));
 }
 
 TYPED_TEST(Rdata_TXT_LIKE_Test, toWireBuffer) {
@@ -214,7 +342,7 @@ 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("\"Test-String\"", this->rdata_txt_like.toText());
 }
 
 TYPED_TEST(Rdata_TXT_LIKE_Test, assignment) {
@@ -244,8 +372,8 @@ TYPED_TEST(Rdata_TXT_LIKE_Test, compare) {
 
     EXPECT_EQ(TypeParam(txt1).compare(TypeParam(txt1)), 0);
 
-    EXPECT_LT(TypeParam("").compare(TypeParam(txt1)), 0);
-    EXPECT_GT(TypeParam(txt1).compare(TypeParam("")), 0);
+    EXPECT_LT(TypeParam("\"\"").compare(TypeParam(txt1)), 0);
+    EXPECT_GT(TypeParam(txt1).compare(TypeParam("\"\"")), 0);
 
     EXPECT_LT(TypeParam(txt1).compare(TypeParam(txt2)), 0);
     EXPECT_GT(TypeParam(txt2).compare(TypeParam(txt1)), 0);
diff --git a/src/lib/dns/tests/rdata_unittest.cc b/src/lib/dns/tests/rdata_unittest.cc
index bb39d05..7f0dd65 100644
--- a/src/lib/dns/tests/rdata_unittest.cc
+++ b/src/lib/dns/tests/rdata_unittest.cc
@@ -132,6 +132,7 @@ TEST_F(RdataTest, createRdataWithLexer) {
     stringstream ss;
     const string src_name = "stream-" + boost::lexical_cast<string>(&ss);
     ss << aaaa_rdata.toText() << "\n"; // valid case
+    ss << aaaa_rdata.toText() << "; comment, should be ignored\n";
     ss << aaaa_rdata.toText() << " extra-token\n"; // extra token
     ss << aaaa_rdata.toText() << " extra token\n"; // 2 extra tokens
     ss << ")\n"; // causing lexer error in parsing the RDATA text
@@ -146,54 +147,71 @@ TEST_F(RdataTest, createRdataWithLexer) {
         boost::bind(&CreateRdataCallback::callback, &callback,
                     CreateRdataCallback::WARN,  _1, _2, _3));
 
+    size_t line = 0;
+
     // Valid case.
+    ++line;
     ConstRdataPtr rdata = createRdata(RRType::AAAA(), RRClass::IN(), lexer,
                                       NULL, MasterLoader::MANY_ERRORS,
                                       callbacks);
     EXPECT_EQ(0, aaaa_rdata.compare(*rdata));
     EXPECT_FALSE(callback.isCalled());
 
+    // Similar to the previous case, but RDATA is followed by a comment.
+    // It should cause any confusion.
+    ++line;
+    callback.clear();
+    rdata = createRdata(RRType::AAAA(), RRClass::IN(), lexer, NULL,
+                        MasterLoader::MANY_ERRORS, callbacks);
+    EXPECT_EQ(0, aaaa_rdata.compare(*rdata));
+    EXPECT_FALSE(callback.isCalled());
+
     // Broken RDATA text: extra token.  createRdata() returns NULL, error
     // callback is called.
+    ++line;
     callback.clear();
     EXPECT_FALSE(createRdata(RRType::AAAA(), RRClass::IN(), lexer, NULL,
                              MasterLoader::MANY_ERRORS, callbacks));
-    callback.check(src_name, 2, CreateRdataCallback::ERROR,
+    callback.check(src_name, line, CreateRdataCallback::ERROR,
                    "createRdata from text failed near 'extra-token': "
                    "extra input text");
 
     // Similar to the previous case, but only the first extra token triggers
     // callback.
+    ++line;
     callback.clear();
     EXPECT_FALSE(createRdata(RRType::AAAA(), RRClass::IN(), lexer, NULL,
                              MasterLoader::MANY_ERRORS, callbacks));
-    callback.check(src_name, 3, CreateRdataCallback::ERROR,
+    callback.check(src_name, line, CreateRdataCallback::ERROR,
                    "createRdata from text failed near 'extra': "
                    "extra input text");
 
     // Lexer error will happen, corresponding error callback will be triggered.
+    ++line;
     callback.clear();
     EXPECT_FALSE(createRdata(RRType::AAAA(), RRClass::IN(), lexer, NULL,
                              MasterLoader::MANY_ERRORS, callbacks));
-    callback.check(src_name, 4, CreateRdataCallback::ERROR,
+    callback.check(src_name, line, CreateRdataCallback::ERROR,
                    "createRdata from text failed: unbalanced parentheses");
 
     // Semantics level error will happen, corresponding error callback will be
     // triggered.
+    ++line;
     callback.clear();
     EXPECT_FALSE(createRdata(RRType::AAAA(), RRClass::IN(), lexer, NULL,
                              MasterLoader::MANY_ERRORS, callbacks));
-    callback.check(src_name, 5, CreateRdataCallback::ERROR,
+    callback.check(src_name, line, CreateRdataCallback::ERROR,
                    "createRdata from text failed: Failed to convert "
                    "'192.0.2.1' to IN/AAAA RDATA");
 
     // Input is valid and parse will succeed, but with a warning that the
     // file is not ended with a newline.
+    ++line;
     callback.clear();
     rdata = createRdata(RRType::AAAA(), RRClass::IN(), lexer, NULL,
                         MasterLoader::MANY_ERRORS, callbacks);
     EXPECT_EQ(0, aaaa_rdata.compare(*rdata));
-    callback.check(src_name, 6, CreateRdataCallback::WARN,
+    callback.check(src_name, line, CreateRdataCallback::WARN,
                    "file does not end with newline");
 }
 
diff --git a/src/lib/dns/tests/testdata/rdata_txt_fromWire1 b/src/lib/dns/tests/testdata/rdata_txt_fromWire1
index 2c51efe..95980db 100644
--- a/src/lib/dns/tests/testdata/rdata_txt_fromWire1
+++ b/src/lib/dns/tests/testdata/rdata_txt_fromWire1
@@ -1,9 +1,9 @@
 #
 # various kinds of TXT RDATA stored in an input buffer
 #
-# Valid RDATA for "Test String"
+# Valid RDATA for "Test-String"
 #
 # RDLENGHT=12 bytes
  00 0c
-#    T  e  s  t     S  t  r  i  n  g
- 0b 54 65 73 74 20 53 74 72 69 6e 67
+#    T  e  s  t  -  S  t  r  i  n  g
+ 0b 54 65 73 74 2d 53 74 72 69 6e 67
diff --git a/src/lib/dns/tests/tsigkey_unittest.cc b/src/lib/dns/tests/tsigkey_unittest.cc
index 20ee802..c1367be 100644
--- a/src/lib/dns/tests/tsigkey_unittest.cc
+++ b/src/lib/dns/tests/tsigkey_unittest.cc
@@ -251,6 +251,15 @@ TEST_F(TSIGKeyRingTest, find) {
     const TSIGKeyRing::FindResult result3 = keyring.find(key_name, md5_name);
     EXPECT_EQ(TSIGKeyRing::NOTFOUND, result3.code);
     EXPECT_EQ(static_cast<const TSIGKey*>(NULL), result3.key);
+
+    // But with just the name it should work
+    const TSIGKeyRing::FindResult result4(keyring.find(key_name));
+    EXPECT_EQ(TSIGKeyRing::SUCCESS, result4.code);
+    EXPECT_EQ(key_name, result4.key->getKeyName());
+    EXPECT_EQ(TSIGKey::HMACSHA256_NAME(), result4.key->getAlgorithmName());
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, secret, secret_len,
+                        result4.key->getSecret(),
+                        result4.key->getSecretLength());
 }
 
 TEST_F(TSIGKeyRingTest, findFromSome) {
diff --git a/src/lib/dns/tsigkey.cc b/src/lib/dns/tsigkey.cc
index d7d60eb..e55cce3 100644
--- a/src/lib/dns/tsigkey.cc
+++ b/src/lib/dns/tsigkey.cc
@@ -51,7 +51,7 @@ namespace {
         if (name == TSIGKey::HMACSHA512_NAME()) {
             return (isc::cryptolink::SHA512);
         }
- 
+
         return (isc::cryptolink::UNKNOWN_HASH);
     }
 }
@@ -270,6 +270,16 @@ TSIGKeyRing::remove(const Name& key_name) {
 }
 
 TSIGKeyRing::FindResult
+TSIGKeyRing::find(const Name& key_name) const {
+    TSIGKeyRingImpl::TSIGKeyMap::const_iterator found =
+        impl_->keys.find(key_name);
+    if (found == impl_->keys.end()) {
+        return (FindResult(NOTFOUND, NULL));
+    }
+    return (FindResult(SUCCESS, &((*found).second)));
+}
+
+TSIGKeyRing::FindResult
 TSIGKeyRing::find(const Name& key_name, const Name& algorithm_name) const {
     TSIGKeyRingImpl::TSIGKeyMap::const_iterator found =
         impl_->keys.find(key_name);
diff --git a/src/lib/dns/tsigkey.h b/src/lib/dns/tsigkey.h
index 1bbd3fe..b10660c 100644
--- a/src/lib/dns/tsigkey.h
+++ b/src/lib/dns/tsigkey.h
@@ -327,6 +327,27 @@ public:
     /// Find a \c TSIGKey for the given name in the \c TSIGKeyRing.
     ///
     /// It searches the internal storage for a \c TSIGKey whose name is
+    /// \c key_name.
+    /// It returns the result in the form of a \c FindResult
+    /// object as follows:
+    /// - \c code: \c SUCCESS if a key is found; otherwise \c NOTFOUND.
+    /// - \c key: A pointer to the found \c TSIGKey object if one is found;
+    /// otherwise \c NULL.
+    ///
+    /// The pointer returned in the \c FindResult object is only valid until
+    /// the corresponding key is removed from the key ring.
+    /// The caller must ensure that the key is held in the key ring while
+    /// it needs to refer to it, or it must make a local copy of the key.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \param key_name The name of the key to be found.
+    /// \return A \c FindResult object enclosing the search result (see above).
+    FindResult find(const Name& key_name) const;
+
+    /// Find a \c TSIGKey for the given name in the \c TSIGKeyRing.
+    ///
+    /// It searches the internal storage for a \c TSIGKey whose name is
     /// \c key_name and that uses the hash algorithm identified by
     /// \c algorithm_name.
     /// It returns the result in the form of a \c FindResult
@@ -346,6 +367,7 @@ public:
     /// \param algorithm_name The name of the algorithm of the found key.
     /// \return A \c FindResult object enclosing the search result (see above).
     FindResult find(const Name& key_name, const Name& algorithm_name) const;
+
 private:
     struct TSIGKeyRingImpl;
     TSIGKeyRingImpl* impl_;
diff --git a/src/lib/log/Makefile.am b/src/lib/log/Makefile.am
index 6207655..56918e9 100644
--- a/src/lib/log/Makefile.am
+++ b/src/lib/log/Makefile.am
@@ -31,6 +31,7 @@ libb10_log_la_SOURCES += message_initializer.cc message_initializer.h
 libb10_log_la_SOURCES += message_reader.cc message_reader.h
 libb10_log_la_SOURCES += message_types.h
 libb10_log_la_SOURCES += output_option.cc output_option.h
+libb10_log_la_SOURCES += buffer_appender_impl.cc buffer_appender_impl.h
 
 EXTRA_DIST  = README
 EXTRA_DIST += logimpl_messages.mes
diff --git a/src/lib/log/README b/src/lib/log/README
index 3693abb..2a7af28 100644
--- a/src/lib/log/README
+++ b/src/lib/log/README
@@ -338,7 +338,8 @@ Variant #1, Used by Production Programs
 ---------------------------------------
 void isc::log::initLogger(const std::string& root,
                           isc::log::Severity severity = isc::log::INFO,
-                          int dbglevel = 0, const char* file = NULL);
+                          int dbglevel = 0, const char* file = NULL,
+                          bool buffer = false);
 
 This is the call that should be used by production programs:
 
@@ -359,6 +360,17 @@ file
 The name of a local message file.  This will be read and its definitions used
 to replace the compiled-in text of the messages.
 
+buffer
+If set to true, initial log messages will be internally buffered, until the
+first time a logger specification is processed. This way the program can
+use logging before even processing its logging configuration. As soon as any
+specification is processed (even an empty one), the buffered log messages will
+be flushed according to the specification. Note that if this option is used,
+the program SHOULD call one of the LoggerManager's process() calls (if you
+are using the built-in logging configuration handling in ModuleCCSession,
+this is automatically handled). If the program exits before this is done,
+all log messages are dumped in a raw format to stdout (so that no messages
+get lost).
 
 Variant #2, Used by Unit Tests
 ------------------------------
diff --git a/src/lib/log/buffer_appender_impl.cc b/src/lib/log/buffer_appender_impl.cc
new file mode 100644
index 0000000..8899c4f
--- /dev/null
+++ b/src/lib/log/buffer_appender_impl.cc
@@ -0,0 +1,96 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <log/buffer_appender_impl.h>
+
+#include <log4cplus/loglevel.h>
+#include <boost/scoped_ptr.hpp>
+#include <cstdio>
+
+namespace isc {
+namespace log {
+namespace internal {
+
+BufferAppender::~BufferAppender() {
+    // If there is anything left in the buffer,
+    // it means no reconfig has been done, and
+    // we can assume the logging system was either
+    // never setup, or broke while doing so.
+    // So dump all that is left to stdout
+    try {
+        flushStdout();
+    } catch (...) {
+        // Ok if we can't even seem to dump to stdout, never mind.
+    }
+}
+
+void
+BufferAppender::flushStdout() {
+    // This does not show a bit of information normal log messages
+    // do, so perhaps we should try and setup a new logger here
+    // However, as this is called from a destructor, it may not
+    // be a good idea; as we can't reliably know whether in what
+    // state the logger instance is now (or what the specific logger's
+    // settings were).
+    LogEventList::const_iterator it;
+    for (it = stored_.begin(); it != stored_.end(); ++it) {
+        const std::string level(it->first);
+        LogEventPtr event(it->second);
+        std::printf("%s [%s]: %s\n", level.c_str(),
+                    event->getLoggerName().c_str(),
+                    event->getMessage().c_str());
+    }
+    stored_.clear();
+}
+
+void
+BufferAppender::flush() {
+    LogEventList stored_copy;
+    stored_.swap(stored_copy);
+
+    LogEventList::const_iterator it;
+    for (it = stored_copy.begin(); it != stored_copy.end(); ++it) {
+        LogEventPtr event(it->second);
+        log4cplus::Logger logger =
+            log4cplus::Logger::getInstance(event->getLoggerName());
+
+        logger.log(event->getLogLevel(), event->getMessage());
+    }
+    flushed_ = true;
+}
+
+size_t
+BufferAppender::getBufferSize() const {
+    return (stored_.size());
+}
+
+void
+BufferAppender::append(const log4cplus::spi::InternalLoggingEvent& event) {
+    if (flushed_) {
+        isc_throw(LogBufferAddAfterFlush,
+                  "Internal log buffer has been flushed already");
+    }
+    // get a clone, and put the pointer in a shared_ptr in the list
+    std::auto_ptr<log4cplus::spi::InternalLoggingEvent> event_aptr =
+        event.clone();
+    // Also store the string representation of the log level, to be
+    // used in flushStdout if necessary
+    stored_.push_back(LevelAndEvent(
+                log4cplus::LogLevelManager().toString(event.getLogLevel()),
+                LogEventPtr(event_aptr.release())));
+}
+
+} // end namespace internal
+} // end namespace log
+} // end namespace isc
diff --git a/src/lib/log/buffer_appender_impl.h b/src/lib/log/buffer_appender_impl.h
new file mode 100644
index 0000000..00d3633
--- /dev/null
+++ b/src/lib/log/buffer_appender_impl.h
@@ -0,0 +1,118 @@
+// 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 LOG_BUFFER_H
+#define LOG_BUFFER_H
+
+#include <exceptions/exceptions.h>
+
+#include <log4cplus/logger.h>
+#include <log4cplus/spi/loggingevent.h>
+#include <boost/shared_ptr.hpp>
+
+namespace isc {
+namespace log {
+namespace internal {
+
+/// \brief Buffer add after flush
+///
+/// This exception is thrown if the log buffer's add() method
+/// is called after the log buffer has been flushed; the buffer
+/// is only supposed to be used once (until the first time a
+/// logger specification is processed)
+class LogBufferAddAfterFlush : public isc::Exception {
+public:
+    LogBufferAddAfterFlush(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what)
+    {}
+};
+
+/// Convenience typedef for a pointer to a log event
+typedef boost::shared_ptr<log4cplus::spi::InternalLoggingEvent> LogEventPtr;
+
+/// Convenience typedef for a pair string/logeventptr, the string representing
+/// the logger level, as returned by LogLevelManager::toString() at the
+/// time of initial logging
+typedef std::pair<std::string, LogEventPtr> LevelAndEvent;
+
+/// Convenience typedef for a vector of LevelAndEvent instances
+typedef std::vector<LevelAndEvent> LogEventList;
+
+/// \brief Buffering Logger Appender
+///
+/// This class can be set as an Appender for log4cplus loggers,
+/// and is used to store logging events; it simply keeps any
+/// event that is passed to \c append(), and will replay them to the
+/// logger that they were originally created for when \c flush() is
+/// called.
+///
+/// The idea is that initially, a program may want to do some logging,
+/// but does not know where to yet (for instance because it has yet to
+/// read and parse its configuration). Any log messages before this time
+/// would normally go to some default (say, stdout), and be lost in the
+/// real logging destination. By buffering them (and flushing them once
+/// the logger has been configured), these log messages are kept in a
+/// consistent place, and are not lost.
+///
+/// Given this goal, this class has an extra check; it will raise
+/// an exception if \c append() is called after flush().
+///
+/// If the BufferAppender instance is destroyed before being flushed,
+/// it will dump any event it has left to stdout.
+class BufferAppender : public log4cplus::Appender {
+public:
+    /// \brief Constructor
+    ///
+    /// Constructs a BufferAppender that buffers log evens
+    BufferAppender() : flushed_(false) {}
+
+    /// \brief Destructor
+    ///
+    /// Any remaining events are flushed to stdout (there should
+    /// only be any events remaining if flush() was never called)
+    virtual ~BufferAppender();
+
+    /// \brief Close the appender
+    ///
+    /// This class has no specialized handling for this method
+    virtual void close() {}
+
+    /// \brief Flush the internal buffer
+    ///
+    /// Events that have been stored (after calls to \c append()
+    /// are replayed to the logger. Should only be called after
+    /// new appenders have been set to the logger.
+    void flush();
+
+    /// \brief Returns the number of stored logging events
+    ///
+    /// Mainly useful for testing
+    size_t getBufferSize() const;
+
+protected:
+    virtual void append(const log4cplus::spi::InternalLoggingEvent& event);
+private:
+    /// \brief Helper for the destructor, flush events to stdout
+    void flushStdout();
+
+    LogEventList stored_;
+    bool flushed_;
+};
+
+} // end namespace internal
+} // end namespace log
+} // end namespace isc
+
+#endif // LOG_BUFFER_H
+
diff --git a/src/lib/log/logger_manager.cc b/src/lib/log/logger_manager.cc
index 8431c2e..77893d0 100644
--- a/src/lib/log/logger_manager.cc
+++ b/src/lib/log/logger_manager.cc
@@ -94,7 +94,7 @@ LoggerManager::processEnd() {
 
 void
 LoggerManager::init(const std::string& root, isc::log::Severity severity,
-                    int dbglevel, const char* file)
+                    int dbglevel, const char* file, bool buffer)
 {
     // Load in the messages declared in the program and registered by
     // statically-declared MessageInitializer objects.
@@ -114,7 +114,9 @@ LoggerManager::init(const std::string& root, isc::log::Severity severity,
 
     // Initialize the implementation logging.  After this point, some basic
     // logging has been set up and messages can be logged.
-    LoggerManagerImpl::init(severity, dbglevel);
+    // However, they will not appear until a logging specification has been
+    // processed (or the program exits), see TODO
+    LoggerManagerImpl::init(severity, dbglevel, buffer);
     setLoggingInitialized();
 
     // Check if there were any duplicate message IDs in the default dictionary
diff --git a/src/lib/log/logger_manager.h b/src/lib/log/logger_manager.h
index 63699c9..da49ae4 100644
--- a/src/lib/log/logger_manager.h
+++ b/src/lib/log/logger_manager.h
@@ -76,6 +76,21 @@ public:
         processEnd();
     }
 
+    /// \brief Process 'empty' specification
+    ///
+    /// This will disable any existing output options, and set
+    /// the logging to go to the built-in default (stdout).
+    /// If the logger has been initialized with buffering enabled,
+    /// all log messages up to now shall be printed to stdout.
+    ///
+    /// This is mainly useful in scenarios where buffering is needed,
+    /// but it turns out there are no logging specifications to
+    /// handle.
+    void process() {
+        processInit();
+        processEnd();
+    }
+
     /// \brief Run-Time Initialization
     ///
     /// Performs run-time initialization of the logger system, in particular
@@ -91,13 +106,18 @@ public:
     /// \param dbglevel Debug severity (ignored if "severity" is not "DEBUG")
     /// \param file Name of the local message file.  This must be NULL if there
     ///        is no local message file.
+    /// \param buffer If true, all log messages will be buffered until one of
+    ///        the \c process() methods is called. If false, initial logging
+    ///        shall go to the default output (i.e. stdout)
     static void init(const std::string& root,
                     isc::log::Severity severity = isc::log::INFO,
-                    int dbglevel = 0, const char* file = NULL);
+                    int dbglevel = 0, const char* file = NULL,
+                    bool buffer = false);
 
     /// \brief Reset logging
     ///
-    /// Resets logging to whatever was set in the call to init().
+    /// Resets logging to whatever was set in the call to init(), expect for
+    /// the buffer option.
     static void reset();
 
     /// \brief Read local message file
diff --git a/src/lib/log/logger_manager_impl.cc b/src/lib/log/logger_manager_impl.cc
index 6357e1f..6862d0c 100644
--- a/src/lib/log/logger_manager_impl.cc
+++ b/src/lib/log/logger_manager_impl.cc
@@ -23,12 +23,14 @@
 #include <log4cplus/syslogappender.h>
 
 #include <log/logger.h>
+#include <log/logger_support.h>
 #include <log/logger_level_impl.h>
 #include <log/logger_manager.h>
 #include <log/logger_manager_impl.h>
 #include <log/log_messages.h>
 #include <log/logger_name.h>
 #include <log/logger_specification.h>
+#include <log/buffer_appender_impl.h>
 
 using namespace std;
 
@@ -40,19 +42,24 @@ namespace log {
 // passed back to the parent) and resets the root logger to logging
 // informational messages.  (This last is not a log4cplus default, so we have to
 // explicitly reset the logging severity.)
-
 void
 LoggerManagerImpl::processInit() {
+    storeBufferAppenders();
+
     log4cplus::Logger::getDefaultHierarchy().resetConfiguration();
     initRootLogger();
 }
 
+// Flush the BufferAppenders at the end of processing a new specification
+void
+LoggerManagerImpl::processEnd() {
+    flushBufferAppenders();
+}
+
 // Process logging specification.  Set up the common states then dispatch to
 // add output specifications.
-
 void
 LoggerManagerImpl::processSpecification(const LoggerSpecification& spec) {
-
     log4cplus::Logger logger = log4cplus::Logger::getInstance(
                                    expandLoggerName(spec.getName()));
 
@@ -65,8 +72,7 @@ LoggerManagerImpl::processSpecification(const LoggerSpecification& spec) {
 
     // Output options given?
     if (spec.optionCount() > 0) {
-
-        // Yes, so replace all appenders for this logger.
+        // Replace all appenders for this logger.
         logger.removeAllAppenders();
 
         // Now process output specifications.
@@ -134,7 +140,17 @@ LoggerManagerImpl::createFileAppender(log4cplus::Logger& logger,
     logger.addAppender(fileapp);
 }
 
-// Syslog appender. 
+void
+LoggerManagerImpl::createBufferAppender(log4cplus::Logger& logger) {
+    log4cplus::SharedAppenderPtr bufferapp(new internal::BufferAppender());
+    bufferapp->setName("buffer");
+    logger.addAppender(bufferapp);
+    // Since we do not know at what level the loggers will end up
+    // running, set it to the highest for now
+    logger.setLogLevel(log4cplus::TRACE_LOG_LEVEL);
+}
+
+// Syslog appender.
 void
 LoggerManagerImpl::createSyslogAppender(log4cplus::Logger& logger,
                                          const OutputOption& opt)
@@ -147,10 +163,10 @@ LoggerManagerImpl::createSyslogAppender(log4cplus::Logger& logger,
 
 
 // One-time initialization of the log4cplus system
-
 void
-LoggerManagerImpl::init(isc::log::Severity severity, int dbglevel) {
-
+LoggerManagerImpl::init(isc::log::Severity severity, int dbglevel,
+                        bool buffer)
+{
     // Set up basic configurator.  This attaches a ConsoleAppender to the
     // root logger with suitable output.  This is used until we we have
     // actually read the logging configuration, in which case the output
@@ -161,22 +177,22 @@ LoggerManagerImpl::init(isc::log::Severity severity, int dbglevel) {
     // Add the additional debug levels
     LoggerLevelImpl::init();
 
-    reset(severity, dbglevel);
+    initRootLogger(severity, dbglevel, buffer);
 }
 
 // Reset logging to default configuration.  This closes all appenders
 // and resets the root logger to output INFO messages to the console.
 // It is principally used in testing.
 void
-LoggerManagerImpl::reset(isc::log::Severity severity, int dbglevel) {
-
+LoggerManagerImpl::reset(isc::log::Severity severity, int dbglevel)
+{
     // Initialize the root logger
     initRootLogger(severity, dbglevel);
 }
 
 // Initialize the root logger
 void LoggerManagerImpl::initRootLogger(isc::log::Severity severity,
-                                       int dbglevel)
+                                       int dbglevel, bool buffer)
 {
     log4cplus::Logger::getDefaultHierarchy().resetConfiguration();
 
@@ -191,14 +207,14 @@ void LoggerManagerImpl::initRootLogger(isc::log::Severity severity,
     b10root.setLogLevel(LoggerLevelImpl::convertFromBindLevel(
                                                     Level(severity, dbglevel)));
 
-    // Set the BIND 10 root to use a console logger.
-    OutputOption opt;
-    createConsoleAppender(b10root, opt);
+    if (buffer) {
+        createBufferAppender(b10root);
+    } else {
+        OutputOption opt;
+        createConsoleAppender(b10root, opt);
+    }
 }
 
-// Set the the "console" layout for the given appenders.  This layout includes
-// a date/time and the name of the logger.
-
 void LoggerManagerImpl::setConsoleAppenderLayout(
         log4cplus::SharedAppenderPtr& appender)
 {
@@ -225,5 +241,31 @@ void LoggerManagerImpl::setSyslogAppenderLayout(
     appender->setLayout(layout);
 }
 
+void LoggerManagerImpl::storeBufferAppenders() {
+    // Walk through all loggers, and find any buffer appenders there
+    log4cplus::LoggerList loggers = log4cplus::Logger::getCurrentLoggers();
+    log4cplus::LoggerList::iterator it;
+    for (it = loggers.begin(); it != loggers.end(); ++it) {
+        log4cplus::SharedAppenderPtr buffer_appender =
+            it->getAppender("buffer");
+        if (buffer_appender) {
+            buffer_appender_store_.push_back(buffer_appender);
+        }
+    }
+}
+
+void LoggerManagerImpl::flushBufferAppenders() {
+    std::vector<log4cplus::SharedAppenderPtr> copy;
+    buffer_appender_store_.swap(copy);
+
+    std::vector<log4cplus::SharedAppenderPtr>::iterator it;
+    for (it = copy.begin(); it != copy.end(); ++it) {
+        internal::BufferAppender* app =
+            dynamic_cast<internal::BufferAppender*>(it->get());
+        assert(app != NULL);
+        app->flush();
+    }
+}
+
 } // namespace log
 } // namespace isc
diff --git a/src/lib/log/logger_manager_impl.h b/src/lib/log/logger_manager_impl.h
index 2bce655..bcb2fc7 100644
--- a/src/lib/log/logger_manager_impl.h
+++ b/src/lib/log/logger_manager_impl.h
@@ -51,15 +51,14 @@ class LoggerManagerImpl {
 public:
 
     /// \brief Constructor
-    LoggerManagerImpl()
-    {}
+    LoggerManagerImpl() {}
 
     /// \brief Initialize Processing
     ///
     /// This resets the hierachy of loggers back to their defaults.  This means
     /// that all non-root loggers (if they exist) are set to NOT_SET, and the
     /// root logger reset to logging informational messages.
-    static void processInit();
+    void processInit();
 
     /// \brief Process Specification
     ///
@@ -71,8 +70,7 @@ public:
     /// \brief End Processing
     ///
     /// Terminates the processing of the logging specifications.
-    static void processEnd()
-    {}
+    void processEnd();
 
     /// \brief Implementation-specific initialization
     ///
@@ -87,8 +85,11 @@ public:
     ///
     /// \param severity Severity to be associated with this logger
     /// \param dbglevel Debug level associated with the root logger
+    /// \param buffer If true, all log messages will be buffered until one of
+    ///        the \c process() methods is called. If false, initial logging
+    ///        shall go to the default output (i.e. stdout)
     static void init(isc::log::Severity severity = isc::log::INFO,
-                     int dbglevel = 0);
+                     int dbglevel = 0, bool buffer = false);
 
     /// \brief Reset logging
     ///
@@ -132,15 +133,27 @@ private:
     static void createSyslogAppender(log4cplus::Logger& logger,
                                      const OutputOption& opt);
 
+    /// \brief Create buffered appender
+    ///
+    /// Appends an object to the logger that will store the log events sent
+    /// to the logger. These log messages are replayed to the logger in
+    /// processEnd().
+    ///
+    /// \param logger Log4cplus logger to which the appender must be attached.
+    static void createBufferAppender(log4cplus::Logger& logger);
+
     /// \brief Set default layout and severity for root logger
     ///
-    /// Initializes the root logger to BIND 10 defaults - console output and
-    /// the passed severity/debug level.
+    /// Initializes the root logger to BIND 10 defaults - console or buffered
+    /// output and the passed severity/debug level.
     ///
     /// \param severity Severity of messages that the logger should output.
     /// \param dbglevel Debug level if severity = DEBUG
+    /// \param buffer If true, all log messages will be buffered until one of
+    ///        the \c process() methods is called. If false, initial logging
+    ///        shall go to the default output (i.e. stdout)
     static void initRootLogger(isc::log::Severity severity = isc::log::INFO,
-                               int dbglevel = 0);
+                               int dbglevel = 0, bool buffer = false);
 
     /// \brief Set layout for console appender
     ///
@@ -161,6 +174,25 @@ private:
     ///
     /// \param appender Appender for which this pattern is to be set.
     static void setSyslogAppenderLayout(log4cplus::SharedAppenderPtr& appender);
+
+    /// \brief Store all buffer appenders
+    ///
+    /// When processing a new specification, this method can be used
+    /// to keep a list of the buffer appenders; the caller can then
+    /// process the specification, and call \c flushBufferAppenders()
+    /// to flush and clear the list
+    void storeBufferAppenders();
+
+    /// \brief Flush the stored buffer appenders
+    ///
+    /// This flushes the list of buffer appenders stored in
+    /// \c storeBufferAppenders(), and clears it
+    void flushBufferAppenders();
+
+    /// Only used between processInit() and processEnd(), to temporarily
+    /// store the buffer appenders in order to flush them after
+    /// processSpecification() calls have been completed
+    std::vector<log4cplus::SharedAppenderPtr> buffer_appender_store_;
 };
 
 } // namespace log
diff --git a/src/lib/log/logger_support.cc b/src/lib/log/logger_support.cc
index 2097136..cab52ad 100644
--- a/src/lib/log/logger_support.cc
+++ b/src/lib/log/logger_support.cc
@@ -46,8 +46,8 @@ setLoggingInitialized(bool state) {
 
 void
 initLogger(const string& root, isc::log::Severity severity, int dbglevel,
-    const char* file) {
-    LoggerManager::init(root, severity, dbglevel, file);
+           const char* file, bool buffer) {
+    LoggerManager::init(root, severity, dbglevel, file, buffer);
 }
 
 } // namespace log
diff --git a/src/lib/log/logger_support.h b/src/lib/log/logger_support.h
index f59be60..bd3b5b3 100644
--- a/src/lib/log/logger_support.h
+++ b/src/lib/log/logger_support.h
@@ -61,9 +61,13 @@ void setLoggingInitialized(bool state = true);
 /// \param severity Severity at which to log
 /// \param dbglevel Debug severity (ignored if "severity" is not "DEBUG")
 /// \param file Name of the local message file.
+/// \param buffer If true, all log messages will be buffered until one of
+///        the \c process() methods is called. If false, initial logging
+///        shall go to the default output (i.e. stdout)
 void initLogger(const std::string& root,
                 isc::log::Severity severity = isc::log::INFO,
-                int dbglevel = 0, const char* file = NULL);
+                int dbglevel = 0, const char* file = NULL,
+                bool buffer = false);
 
 } // namespace log
 } // namespace isc
diff --git a/src/lib/log/tests/Makefile.am b/src/lib/log/tests/Makefile.am
index 7f96077..5683842 100644
--- a/src/lib/log/tests/Makefile.am
+++ b/src/lib/log/tests/Makefile.am
@@ -37,6 +37,15 @@ init_logger_test_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
 init_logger_test_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
 init_logger_test_LDADD += $(AM_LDADD) $(LOG4CPLUS_LIBS)
 
+noinst_PROGRAMS += buffer_logger_test
+buffer_logger_test_SOURCES = buffer_logger_test.cc
+buffer_logger_test_CPPFLAGS = $(AM_CPPFLAGS)
+buffer_logger_test_LDFLAGS = $(AM_LDFLAGS)
+buffer_logger_test_LDADD  = $(top_builddir)/src/lib/log/libb10-log.la
+buffer_logger_test_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
+buffer_logger_test_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
+buffer_logger_test_LDADD += $(AM_LDADD) $(LOG4CPLUS_LIBS)
+
 noinst_PROGRAMS += logger_lock_test
 logger_lock_test_SOURCES = logger_lock_test.cc
 nodist_logger_lock_test_SOURCES = log_test_messages.cc log_test_messages.h
@@ -82,11 +91,13 @@ run_unittests_SOURCES += logger_specification_unittest.cc
 run_unittests_SOURCES += message_dictionary_unittest.cc
 run_unittests_SOURCES += message_reader_unittest.cc
 run_unittests_SOURCES += output_option_unittest.cc
+run_unittests_SOURCES += buffer_appender_unittest.cc
 nodist_run_unittests_SOURCES = log_test_messages.cc log_test_messages.h
 
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS)
 run_unittests_CXXFLAGS = $(AM_CXXFLAGS)
 run_unittests_LDADD    = $(AM_LDADD)
+run_unittests_LDADD    +=  $(LOG4CPLUS_LIBS)
 run_unittests_LDFLAGS  = $(AM_LDFLAGS)
 
 # logging initialization tests.  These are put in separate programs to
@@ -124,6 +135,7 @@ check-local:
 	$(SHELL) $(abs_builddir)/console_test.sh
 	$(SHELL) $(abs_builddir)/destination_test.sh
 	$(SHELL) $(abs_builddir)/init_logger_test.sh
+	$(SHELL) $(abs_builddir)/buffer_logger_test.sh
 	$(SHELL) $(abs_builddir)/local_file_test.sh
 	$(SHELL) $(abs_builddir)/logger_lock_test.sh
 	$(SHELL) $(abs_builddir)/severity_test.sh
@@ -131,6 +143,7 @@ check-local:
 noinst_SCRIPTS  = console_test.sh
 noinst_SCRIPTS += destination_test.sh
 noinst_SCRIPTS += init_logger_test.sh
+noinst_SCRIPTS += buffer_logger_test.sh
 noinst_SCRIPTS += local_file_test.sh
 noinst_SCRIPTS += logger_lock_test.sh
 noinst_SCRIPTS += severity_test.sh
diff --git a/src/lib/log/tests/buffer_appender_unittest.cc b/src/lib/log/tests/buffer_appender_unittest.cc
new file mode 100644
index 0000000..781fcbe
--- /dev/null
+++ b/src/lib/log/tests/buffer_appender_unittest.cc
@@ -0,0 +1,146 @@
+// 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 <gtest/gtest.h>
+
+#include <log/macros.h>
+#include <log/logger_support.h>
+#include <log/log_messages.h>
+#include <log/buffer_appender_impl.h>
+
+#include <log4cplus/loggingmacros.h>
+#include <log4cplus/logger.h>
+#include <log4cplus/nullappender.h>
+#include <log4cplus/spi/loggingevent.h>
+
+using namespace isc::log;
+using namespace isc::log::internal;
+
+namespace isc {
+namespace log {
+
+/// \brief Specialized test class
+///
+/// In order to test append() somewhat directly, this
+/// class implements one more method (addEvent)
+class TestBufferAppender : public BufferAppender {
+public:
+    void addEvent(const log4cplus::spi::InternalLoggingEvent& event) {
+        append(event);
+    }
+};
+
+class BufferAppenderTest : public ::testing::Test {
+protected:
+    BufferAppenderTest() : buffer_appender1(new TestBufferAppender()),
+                      appender1(buffer_appender1),
+                      buffer_appender2(new TestBufferAppender()),
+                      appender2(buffer_appender2),
+                      logger(log4cplus::Logger::getInstance("buffer"))
+    {
+        logger.setLogLevel(log4cplus::TRACE_LOG_LEVEL);
+    }
+
+    ~BufferAppenderTest() {
+        // If any log messages are left, we don't care, get rid of them,
+        // by flushing them to a null appender
+        // Given the 'messages should not get lost' approach of the logging
+        // system, not flushing them to a null appender would cause them
+        // to be dumped to stdout as the test is destroyed, making
+        // unnecessarily messy test output.
+        log4cplus::SharedAppenderPtr null_appender(
+            new log4cplus::NullAppender());
+        logger.removeAllAppenders();
+        logger.addAppender(null_appender);
+        buffer_appender1->flush();
+        buffer_appender2->flush();
+    }
+
+    TestBufferAppender* buffer_appender1;
+    log4cplus::SharedAppenderPtr appender1;
+    TestBufferAppender* buffer_appender2;
+    log4cplus::SharedAppenderPtr appender2;
+    log4cplus::Logger logger;
+};
+
+// Test that log events are indeed stored, and that they are
+// flushed to the new appenders of their logger
+TEST_F(BufferAppenderTest, flush) {
+    ASSERT_EQ(0, buffer_appender1->getBufferSize());
+    ASSERT_EQ(0, buffer_appender2->getBufferSize());
+
+    // Create a Logger, log a few messages with the first appender
+    logger.addAppender(appender1);
+    LOG4CPLUS_INFO(logger, "Foo");
+    ASSERT_EQ(1, buffer_appender1->getBufferSize());
+    LOG4CPLUS_INFO(logger, "Foo");
+    ASSERT_EQ(2, buffer_appender1->getBufferSize());
+    LOG4CPLUS_INFO(logger, "Foo");
+    ASSERT_EQ(3, buffer_appender1->getBufferSize());
+
+    // Second buffer should still be empty
+    ASSERT_EQ(0, buffer_appender2->getBufferSize());
+
+    // Replace the appender by the second one, and call flush;
+    // this should cause all events to be moved to the second buffer
+    logger.removeAllAppenders();
+    logger.addAppender(appender2);
+    buffer_appender1->flush();
+    ASSERT_EQ(0, buffer_appender1->getBufferSize());
+    ASSERT_EQ(3, buffer_appender2->getBufferSize());
+}
+
+// Once flushed, logging new messages with the same buffer should fail
+TEST_F(BufferAppenderTest, addAfterFlush) {
+    logger.addAppender(appender1);
+    buffer_appender1->flush();
+    EXPECT_THROW(LOG4CPLUS_INFO(logger, "Foo"), LogBufferAddAfterFlush);
+    // It should not have been added
+    ASSERT_EQ(0, buffer_appender1->getBufferSize());
+
+    // But logging should work again as long as a different buffer is used
+    logger.removeAllAppenders();
+    logger.addAppender(appender2);
+    LOG4CPLUS_INFO(logger, "Foo");
+    ASSERT_EQ(1, buffer_appender2->getBufferSize());
+}
+
+TEST_F(BufferAppenderTest, addDirectly) {
+    // A few direct calls
+    log4cplus::spi::InternalLoggingEvent event("buffer",
+                                               log4cplus::INFO_LOG_LEVEL,
+                                               "Bar", "file", 123);
+    buffer_appender1->addEvent(event);
+    ASSERT_EQ(1, buffer_appender1->getBufferSize());
+
+    // Do one from a smaller scope to make sure destruction doesn't harm
+    {
+        log4cplus::spi::InternalLoggingEvent event2("buffer",
+                                                    log4cplus::INFO_LOG_LEVEL,
+                                                    "Bar", "file", 123);
+        buffer_appender1->addEvent(event2);
+    }
+    ASSERT_EQ(2, buffer_appender1->getBufferSize());
+
+    // And flush them to the next
+    logger.removeAllAppenders();
+    logger.addAppender(appender2);
+    buffer_appender1->flush();
+    ASSERT_EQ(0, buffer_appender1->getBufferSize());
+    ASSERT_EQ(2, buffer_appender2->getBufferSize());
+}
+
+}
+}
diff --git a/src/lib/log/tests/buffer_logger_test.cc b/src/lib/log/tests/buffer_logger_test.cc
new file mode 100644
index 0000000..8d1b3cf
--- /dev/null
+++ b/src/lib/log/tests/buffer_logger_test.cc
@@ -0,0 +1,71 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <log/macros.h>
+#include <log/logger_support.h>
+#include <log/logger_manager.h>
+#include <log/log_messages.h>
+#include <util/interprocess_sync_null.h>
+
+using namespace isc::log;
+
+namespace {
+void usage() {
+    std::cout << "Usage: buffer_logger_test [-n]" << std::endl;
+}
+} // end unnamed namespace
+
+/// \brief Test InitLogger
+///
+/// A program used in testing the logger that initializes logging with
+/// buffering enabled, so that initial log messages are not immediately
+/// logged, but are not lost (they should be logged the moment process()
+/// is called.
+///
+/// If -n is given as an argument, process() is never called. In this
+/// case, upon exit, all leftover log messages should be printed to
+/// stdout, but without normal logging additions (such as time and
+/// logger name)
+int
+main(int argc, char** argv) {
+    bool do_process = true;
+    int opt;
+    while ((opt = getopt(argc, argv, "n")) != -1) {
+        switch (opt) {
+        case 'n':
+            do_process = false;
+            break;
+        default:
+            usage();
+            return (1);
+        }
+    }
+
+    // Note, level is INFO, so DEBUG should normally not show
+    // up. Unless process is never called (at which point it
+    // will end up in the dump at the end).
+    initLogger("buffertest", isc::log::INFO, 0, NULL, true);
+    Logger logger("log");
+    // No need for file interprocess locking in this test
+    logger.setInterprocessSync(new isc::util::InterprocessSyncNull("logger"));
+    LOG_INFO(logger, LOG_BAD_SEVERITY).arg("info");
+    LOG_DEBUG(logger, 50, LOG_BAD_DESTINATION).arg("debug-50");
+    LOG_INFO(logger, LOG_BAD_SEVERITY).arg("info");
+    // process should cause them to be logged
+    if (do_process) {
+        LoggerManager logger_manager;
+        logger_manager.process();
+    }
+    return (0);
+}
diff --git a/src/lib/log/tests/buffer_logger_test.sh.in b/src/lib/log/tests/buffer_logger_test.sh.in
new file mode 100755
index 0000000..696f829
--- /dev/null
+++ b/src/lib/log/tests/buffer_logger_test.sh.in
@@ -0,0 +1,61 @@
+#!/bin/sh
+# Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+# Checks that the initLogger() call uses for unit tests respects the setting of
+# the buffer value
+#
+
+testname="bufferLogger test"
+echo $testname
+
+failcount=0
+tempfile=@abs_builddir@/buffer_logger_test_tempfile_$$
+
+passfail() {
+    if [ $1 -eq 0 ]; then
+        echo " pass"
+    else
+        echo " FAIL"
+        failcount=`expr $failcount + $1`
+    fi
+}
+
+echo "1. Checking that buffer initialization works"
+
+echo -n  "   - Buffer including process() call: "
+cat > $tempfile << .
+INFO  [buffertest.log] LOG_BAD_SEVERITY unrecognized log severity: info
+INFO  [buffertest.log] LOG_BAD_SEVERITY unrecognized log severity: info
+.
+./buffer_logger_test 2>&1 | \
+    sed -e 's/\[\([a-z0-9\.]\{1,\}\)\/\([0-9]\{1,\}\)\]/[\1]/' | \
+    cut -d' ' -f3- | diff $tempfile -
+passfail $?
+
+echo -n  "   - Buffer excluding process() call: "
+cat > $tempfile << .
+INFO [buffertest.log]: LOG_BAD_SEVERITY unrecognized log severity: info
+DEBUG [buffertest.log]: LOG_BAD_DESTINATION unrecognized log destination: debug-50
+INFO [buffertest.log]: LOG_BAD_SEVERITY unrecognized log severity: info
+.
+./buffer_logger_test -n 2>&1 | diff $tempfile -
+passfail $?
+
+
+
+# Tidy up.
+rm -f $tempfile
+
+exit $failcount
diff --git a/src/lib/python/isc/config/cfgmgr.py b/src/lib/python/isc/config/cfgmgr.py
index aa0547b..a200dfc 100644
--- a/src/lib/python/isc/config/cfgmgr.py
+++ b/src/lib/python/isc/config/cfgmgr.py
@@ -34,7 +34,7 @@ import bind10_config
 import isc.log
 from isc.log_messages.cfgmgr_messages import *
 
-logger = isc.log.Logger("cfgmgr")
+logger = isc.log.Logger("cfgmgr", buffer=True)
 
 class ConfigManagerDataReadError(Exception):
     """This exception is thrown when there is an error while reading
@@ -224,8 +224,13 @@ class ConfigManager:
 
     def check_logging_config(self, config):
         if self.log_module_name in config:
+            # If there is logging config, apply it.
             ccsession.default_logconfig_handler(config[self.log_module_name],
                                                 self.log_config_data)
+        else:
+            # If there is no logging config, we still need to trigger the
+            # handler, so make it use defaults (and flush any buffered logs)
+            ccsession.default_logconfig_handler({}, self.log_config_data)
 
     def notify_boss(self):
         """Notifies the Boss module that the Config Manager is running"""
@@ -313,11 +318,11 @@ class ConfigManager:
             self.config = ConfigManagerData.read_from_file(self.data_path,
                                                            self.\
                                                            database_filename)
-            self.check_logging_config(self.config.data);
         except ConfigManagerDataEmpty:
             # ok, just start with an empty config
             self.config = ConfigManagerData(self.data_path,
                                             self.database_filename)
+        self.check_logging_config(self.config.data);
 
     def write_config(self):
         """Write the current configuration to the file specificied at init()"""
diff --git a/src/lib/python/isc/datasrc/client_inc.cc b/src/lib/python/isc/datasrc/client_inc.cc
index e0c0f06..83bf0ed 100644
--- a/src/lib/python/isc/datasrc/client_inc.cc
+++ b/src/lib/python/isc/datasrc/client_inc.cc
@@ -88,6 +88,35 @@ Return Value(s): A tuple containing a result value and a ZoneFinder object or\n\
 None\n\
 ";
 
+const char* const DataSourceClient_createZone_doc = "\
+create_zone(name) -> bool\n\
+\n\
+Create a zone in the data source.\n\
+\n\
+Creates a new (empty) zone in the data source backend, which can\n\
+subsequently be filled with data (through get_updater()).\n\
+\n\
+Note: This is a tentative API, and this method is likely to change or\n\
+be removed in the near future. For that reason, it currently provides\n\
+a default implementation that throws NotImplemented.\n\
+\n\
+Apart from the two exceptions mentioned below, in theory this call can\n\
+throw anything, depending on the implementation of the datasource\n\
+backend.\n\
+\n\
+Exceptions:\n\
+  NotImplemented If the datasource backend does not support direct\n\
+             zone creation.\n\
+  DataSourceError If something goes wrong in the data source while\n\
+             creating the zone.\n\
+\n\
+Parameters:\n\
+  name       The (fully qualified) name of the zone to create\n\
+\n\
+Return Value(s): True if the zone was added, False if it already\n\
+existed\n\
+";
+
 const char* const DataSourceClient_getIterator_doc = "\
 get_iterator(name, separate_rrs=False) -> ZoneIterator\n\
 \n\
diff --git a/src/lib/python/isc/datasrc/client_python.cc b/src/lib/python/isc/datasrc/client_python.cc
index 1a9ae47..d0206d1 100644
--- a/src/lib/python/isc/datasrc/client_python.cc
+++ b/src/lib/python/isc/datasrc/client_python.cc
@@ -92,6 +92,37 @@ DataSourceClient_findZone(PyObject* po_self, PyObject* args) {
 }
 
 PyObject*
+DataSourceClient_createZone(PyObject* po_self, PyObject* args) {
+    s_DataSourceClient* const self = static_cast<s_DataSourceClient*>(po_self);
+    PyObject* name;
+    if (PyArg_ParseTuple(args, "O!", &name_type, &name)) {
+        try {
+            if (self->client->createZone(PyName_ToName(name))) {
+                Py_RETURN_TRUE;
+            } else {
+                Py_RETURN_FALSE;
+            }
+        } catch (const DataSourceError& dse) {
+            PyErr_SetString(getDataSourceException("Error"), dse.what());
+            return (NULL);
+        } catch (const isc::NotImplemented& ni) {
+            PyErr_SetString(getDataSourceException("NotImplemented"),
+                            ni.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);
+        }
+    } else {
+        return (NULL);
+    }
+}
+
+PyObject*
 DataSourceClient_getIterator(PyObject* po_self, PyObject* args) {
     s_DataSourceClient* const self = static_cast<s_DataSourceClient*>(po_self);
     PyObject* name_obj;
@@ -230,6 +261,8 @@ DataSourceClient_getJournalReader(PyObject* po_self, PyObject* args) {
 PyMethodDef DataSourceClient_methods[] = {
     { "find_zone", DataSourceClient_findZone, METH_VARARGS,
       DataSourceClient_findZone_doc },
+    { "create_zone", DataSourceClient_createZone, METH_VARARGS,
+      DataSourceClient_createZone_doc },
     { "get_iterator",
       DataSourceClient_getIterator, METH_VARARGS,
       DataSourceClient_getIterator_doc },
diff --git a/src/lib/python/isc/datasrc/tests/datasrc_test.py b/src/lib/python/isc/datasrc/tests/datasrc_test.py
index b8e6fb2..fef04a4 100644
--- a/src/lib/python/isc/datasrc/tests/datasrc_test.py
+++ b/src/lib/python/isc/datasrc/tests/datasrc_test.py
@@ -200,7 +200,7 @@ class DataSrcClient(unittest.TestCase):
                   ])
         # For RRSIGS, we can't add the fake data through the API, so we
         # simply pass no rdata at all (which is skipped by the check later)
-        
+
         # Since we passed separate_rrs = True to get_iterator, we get several
         # sets of RRSIGs, one for each TTL
         add_rrset(expected_rrset_list, name, rrclass,
@@ -634,6 +634,53 @@ class DataSrcUpdater(unittest.TestCase):
         self.assertEqual(None, iterator.get_soa())
         self.assertEqual(None, iterator.get_next_rrset())
 
+    def test_create_zone_args(self):
+        dsc = isc.datasrc.DataSourceClient("sqlite3", WRITE_ZONE_DB_CONFIG)
+
+        self.assertRaises(TypeError, dsc.create_zone)
+        self.assertRaises(TypeError, dsc.create_zone, 1)
+        self.assertRaises(TypeError, dsc.create_zone, None)
+        self.assertRaises(TypeError, dsc.create_zone, "foo.")
+        self.assertRaises(TypeError, dsc.create_zone,
+                          isc.dns.Name("example.org"), 1)
+
+    def test_create_zone(self):
+        dsc = isc.datasrc.DataSourceClient("sqlite3", WRITE_ZONE_DB_CONFIG)
+        # Note, using example.org here, which should not exist
+        zone_name = isc.dns.Name("example.org")
+        self.assertIsNone(dsc.get_updater(zone_name, True))
+        self.assertRaises(isc.datasrc.Error, dsc.get_iterator, zone_name)
+
+        self.assertTrue(dsc.create_zone(zone_name))
+
+        # should exist now, we should be able to get an updater
+        # and currently it should be empty
+        self.assertIsNotNone(dsc.get_updater(zone_name, True))
+        iterator = dsc.get_iterator(zone_name)
+        self.assertEqual(None, iterator.get_soa())
+        self.assertEqual(None, iterator.get_next_rrset())
+
+        # Trying to create it again should return False
+        self.assertFalse(dsc.create_zone(zone_name))
+
+    def test_create_zone_locked(self):
+        zone_name = isc.dns.Name("example.org")
+        dsc = isc.datasrc.DataSourceClient("sqlite3", WRITE_ZONE_DB_CONFIG)
+        updater = dsc.get_updater(isc.dns.Name("example.com"), True)
+
+        # Should fail since db is locked
+        self.assertRaises(isc.datasrc.Error, dsc.create_zone, zone_name)
+
+        # Cancel updater, then create should succeed
+        updater = None
+        self.assertTrue(dsc.create_zone(zone_name))
+
+    def test_create_zone_not_implemented(self):
+        mem_cfg = '{ "type": "memory", "class": "IN", "zones": [] }';
+        dsc = isc.datasrc.DataSourceClient("memory", mem_cfg)
+        self.assertRaises(isc.datasrc.NotImplemented, dsc.create_zone,
+                          isc.dns.Name("example.com"))
+
 class JournalWrite(unittest.TestCase):
     def setUp(self):
         # Make a fresh copy of the writable database with all original content
diff --git a/src/lib/python/isc/log/log.cc b/src/lib/python/isc/log/log.cc
index 69e70b7..7b95201 100644
--- a/src/lib/python/isc/log/log.cc
+++ b/src/lib/python/isc/log/log.cc
@@ -166,17 +166,23 @@ reset(PyObject*, PyObject*) {
 }
 
 PyObject*
-init(PyObject*, PyObject* args) {
+init(PyObject*, PyObject* args, PyObject* arg_keywords) {
     const char* root;
     const char* file(NULL);
     const char* severity("INFO");
+    bool buffer = false;
     int dbglevel(0);
-    if (!PyArg_ParseTuple(args, "s|siz", &root, &severity, &dbglevel, &file)) {
+    const char* const keywords[] = { "name", "severity", "debuglevel", "file",
+                                     "buffer", NULL };
+    if (!PyArg_ParseTupleAndKeywords(args, arg_keywords, "s|sizb",
+                                     const_cast<char**>(keywords), &root,
+                                     &severity, &dbglevel, &file, &buffer)) {
         return (NULL);
     }
 
     try {
-        LoggerManager::init(root, getSeverity(severity), dbglevel, file);
+        LoggerManager::init(root, getSeverity(severity), dbglevel, file,
+                            buffer);
     }
     catch (const std::exception& e) {
         PyErr_SetString(PyExc_RuntimeError, e.what());
@@ -266,12 +272,19 @@ PyMethodDef methods[] = {
         "need to call it. It returns None if the message does not exist."},
     {"reset", reset, METH_NOARGS,
         "Reset all logging. For testing purposes only, do not use."},
-    {"init", init, METH_VARARGS,
+    {"init", reinterpret_cast<PyCFunction>(init), METH_VARARGS | METH_KEYWORDS,
         "Run-time initialization. You need to call this before you do any "
         "logging, to configure the root logger name. You may also provide "
-        "logging severity (one of 'DEBUG', 'INFO', 'WARN', 'ERROR' or "
-        "'FATAL'), a debug level (integer in the range 0-99) and a file name "
-        "of a dictionary with message text translations."},
+        "Arguments:\n"
+        "name: root logger name\n"
+        "severity (optional): one of 'DEBUG', 'INFO', 'WARN', 'ERROR' or "
+        "'FATAL'\n"
+        "debuglevel (optional): a debug level (integer in the range 0-99) "
+        "file (optional): a file name of a dictionary with message text "
+        "translations\n"
+        "buffer (optional), boolean, when True, causes all log messages "
+        "to be stored internally until log_config_update is called, at "
+        "which point they shall be logged."},
     {"resetUnitTestRootLogger", resetUnitTestRootLogger, METH_VARARGS,
         "Resets the configuration of the root logger to that set by the "
         "B10_XXX environment variables.  It is aimed at unit tests, where "
@@ -655,7 +668,7 @@ PyTypeObject logger_type = {
     NULL,                               // tp_as_number
     NULL,                               // tp_as_sequence
     NULL,                               // tp_as_mapping
-    NULL,                               // tp_hash 
+    NULL,                               // tp_hash
     NULL,                               // tp_call
     NULL,                               // tp_str
     NULL,                               // tp_getattro
diff --git a/src/lib/python/isc/log/tests/log_test.py b/src/lib/python/isc/log/tests/log_test.py
index 1337654..2059caa 100644
--- a/src/lib/python/isc/log/tests/log_test.py
+++ b/src/lib/python/isc/log/tests/log_test.py
@@ -56,6 +56,28 @@ class Manager(unittest.TestCase):
         # ignore errors like missing file?
         isc.log.init("root", "INFO", 0, "/no/such/file");
 
+    def test_init_keywords(self):
+        isc.log.init(name="root", severity="DEBUG", debuglevel=50, file=None,
+                     buffer=True)
+        # unknown keyword
+        self.assertRaises(TypeError, isc.log.init, name="root", foo="bar")
+        # Replace the values for each keyword by a wrong type, one by one
+        self.assertRaises(TypeError, isc.log.init, name=1,
+                          severity="DEBUG", debuglevel=50, file=None,
+                          buffer=True)
+        self.assertRaises(TypeError, isc.log.init, name="root",
+                          severity=2, debuglevel=50, file=None,
+                          buffer=True)
+        self.assertRaises(TypeError, isc.log.init, name="root",
+                          severity="DEBUG", debuglevel="50", file=None,
+                          buffer=True)
+        self.assertRaises(TypeError, isc.log.init, name="root",
+                          severity="DEBUG", debuglevel=50, file=1,
+                          buffer=True)
+        self.assertRaises(TypeError, isc.log.init, name="root",
+                          severity="DEBUG", debuglevel=50, file=None,
+                          buffer=None)
+
     def test_log_config_update(self):
         log_spec = json.dumps(isc.config.module_spec_from_file(path_search('logging.spec', bind10_config.PLUGIN_PATHS)).get_full_spec())
 
diff --git a/src/lib/util/python/gen_wiredata.py.in b/src/lib/util/python/gen_wiredata.py.in
index f997701..55724c9 100755
--- a/src/lib/util/python/gen_wiredata.py.in
+++ b/src/lib/util/python/gen_wiredata.py.in
@@ -770,7 +770,7 @@ class TXT(RR):
 
     nstring = 1
     stringlen = None
-    string = 'Test String'
+    string = 'Test-String'
 
     def dump(self, f):
         stringlen_list = []
diff --git a/tests/lettuce/configurations/xfrin/.gitignore b/tests/lettuce/configurations/xfrin/.gitignore
index 4de8c4b..5d56912 100644
--- a/tests/lettuce/configurations/xfrin/.gitignore
+++ b/tests/lettuce/configurations/xfrin/.gitignore
@@ -1 +1,2 @@
 /retransfer_master.conf
+/retransfer_slave.conf
diff --git a/tests/lettuce/features/xfrin_bind10.feature b/tests/lettuce/features/xfrin_bind10.feature
index 3c7fa1c..34674ca 100644
--- a/tests/lettuce/features/xfrin_bind10.feature
+++ b/tests/lettuce/features/xfrin_bind10.feature
@@ -79,7 +79,7 @@ Feature: Xfrin
     When I send bind10 the following commands:
     """
     config add tsig_keys/keys "example.key.:c2VjcmV0"
-    config set Xfrin/zones[0]/tsig_key  "example.key.:c2VjcmV0"
+    config set Xfrin/zones[0]/tsig_key  "example.key."
     config commit
     """
 
diff --git a/tests/tools/perfdhcp/Makefile.am b/tests/tools/perfdhcp/Makefile.am
index 08a21a4..c4b82b5 100644
--- a/tests/tools/perfdhcp/Makefile.am
+++ b/tests/tools/perfdhcp/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = . tests templates
+SUBDIRS = . tests
 
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
 AM_CPPFLAGS += -I$(top_srcdir)/src/lib/log -I$(top_builddir)/src/lib/log
diff --git a/tests/tools/perfdhcp/templates/.gitignore b/tests/tools/perfdhcp/templates/.gitignore
deleted file mode 100644
index 6f865da..0000000
--- a/tests/tools/perfdhcp/templates/.gitignore
+++ /dev/null
@@ -1,5 +0,0 @@
-/test1.hex
-/test2.hex
-/test3.hex
-/test4.hex
-/test5.hex
diff --git a/tests/tools/perfdhcp/templates/Makefile.am b/tests/tools/perfdhcp/templates/Makefile.am
deleted file mode 100644
index c22787f..0000000
--- a/tests/tools/perfdhcp/templates/Makefile.am
+++ /dev/null
@@ -1,10 +0,0 @@
-SUBDIRS = .
-
-# The test[1-5].hex are created by the TestControl.PacketTemplates
-# unit tests and have to be removed.
-CLEANFILES = test1.hex test2.hex test3.hex test4.hex test5.hex
-
-perfdhcpdir = $(pkgdatadir)
-
-EXTRA_DIST = discover-example.hex request4-example.hex
-EXTRA_DIST += solicit-example.hex request6-example.hex
diff --git a/tests/tools/perfdhcp/templates/discover-example.hex b/tests/tools/perfdhcp/templates/discover-example.hex
deleted file mode 100644
index 9a6e5ea..0000000
--- a/tests/tools/perfdhcp/templates/discover-example.hex
+++ /dev/null
@@ -1 +0,0 @@
-01010601008b45d200000000000000000000000000000000ac100102000c0102030400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000638253633501013707011c02030f060cff
\ No newline at end of file
diff --git a/tests/tools/perfdhcp/templates/request4-example.hex b/tests/tools/perfdhcp/templates/request4-example.hex
deleted file mode 100644
index 32447d6..0000000
--- a/tests/tools/perfdhcp/templates/request4-example.hex
+++ /dev/null
@@ -1 +0,0 @@
-01010601007b23f800000000000000000000000000000000ac100102000c0102030400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000638253633204ac1001813501033604ac1001013707011c02030f060cff
\ No newline at end of file
diff --git a/tests/tools/perfdhcp/templates/request6-example.hex b/tests/tools/perfdhcp/templates/request6-example.hex
deleted file mode 100644
index 1e3e76f..0000000
--- a/tests/tools/perfdhcp/templates/request6-example.hex
+++ /dev/null
@@ -1 +0,0 @@
-03da30c60001000e0001000117cf8e76000c010203060002000e0001000117cf8a5c080027a87b3400030028000000010000000a0000000e0005001820010db800010000000000000001b568000000be000000c8000800020000
\ No newline at end of file
diff --git a/tests/tools/perfdhcp/templates/solicit-example.hex b/tests/tools/perfdhcp/templates/solicit-example.hex
deleted file mode 100644
index 41c5ad3..0000000
--- a/tests/tools/perfdhcp/templates/solicit-example.hex
+++ /dev/null
@@ -1 +0,0 @@
-015f4e650001000e0001000117cf8e76000c010203040003000c0000000100000e01000015180006000400170018000800020000
\ No newline at end of file
diff --git a/tests/tools/perfdhcp/tests/Makefile.am b/tests/tools/perfdhcp/tests/Makefile.am
index 54602af..aa4c0cf 100644
--- a/tests/tools/perfdhcp/tests/Makefile.am
+++ b/tests/tools/perfdhcp/tests/Makefile.am
@@ -1,6 +1,7 @@
-SUBDIRS = .
+SUBDIRS = . testdata
 
 AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(abs_srcdir)/testdata\"
 AM_CPPFLAGS += $(BOOST_INCLUDES)
 AM_CXXFLAGS = $(B10_CXXFLAGS)
 
@@ -9,6 +10,9 @@ AM_LDFLAGS = -static
 endif
 
 CLEANFILES = *.gcno *.gcda
+# The test[1-5].hex are created by the TestControl.PacketTemplates
+# unit tests and have to be removed.
+CLEANFILES += test1.hex test2.hex test3.hex test4.hex test5.hex
 
 TESTS_ENVIRONMENT = \
         $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
diff --git a/tests/tools/perfdhcp/tests/test_control_unittest.cc b/tests/tools/perfdhcp/tests/test_control_unittest.cc
index abe8282..6c27731 100644
--- a/tests/tools/perfdhcp/tests/test_control_unittest.cc
+++ b/tests/tools/perfdhcp/tests/test_control_unittest.cc
@@ -185,6 +185,18 @@ public:
         return ("");
     }
 
+    /// \brief Get full path to a file in testdata directory.
+    ///
+    /// \param filename filename being appended to absolute
+    /// path to testdata directory
+    ///
+    /// \return full path to a file in testdata directory.
+    std::string getFullPath(const std::string& filename) const {
+        std::ostringstream stream;
+        stream << TEST_DATA_DIR << "/" << filename;
+        return (stream.str());
+    }
+
     /// \brief Match requested options in the buffer with given list.
     ///
     /// This method iterates through options provided in the buffer
@@ -896,7 +908,7 @@ TEST_F(TestControlTest, Packet6) {
     }
 }
 
-TEST_F(TestControlTest, DISABLED_Packet4Exchange) {
+TEST_F(TestControlTest, Packet4Exchange) {
     // Get the local loopback interface to open socket on
     // it and test packets exchanges. We don't want to fail
     // the test if interface is not available.
@@ -925,8 +937,8 @@ TEST_F(TestControlTest, DISABLED_Packet4Exchange) {
     // Use templates for this test.
     processCmdLine("perfdhcp -l " + loopback_iface
                    + " -r 100 -R 20 -n 20 -D 10% -L 10547"
-                   + " -T ../templates/discover-example.hex"
-                   + " -T ../templates/request4-example.hex"
+                   + " -T " + getFullPath("discover-example.hex")
+                   + " -T " + getFullPath("request4-example.hex")
                    + " 127.0.0.1");
     // The number iterations is restricted by the percentage of
     // dropped packets (-D 10%). We also have to bump up the number
@@ -939,7 +951,7 @@ TEST_F(TestControlTest, DISABLED_Packet4Exchange) {
     EXPECT_EQ(12, iterations_performed);
 }
 
-TEST_F(TestControlTest, DISABLED_Packet6Exchange) {
+TEST_F(TestControlTest, Packet6Exchange) {
     // Get the local loopback interface to open socket on
     // it and test packets exchanges. We don't want to fail
     // the test if interface is not available.
@@ -967,8 +979,8 @@ TEST_F(TestControlTest, DISABLED_Packet6Exchange) {
     use_templates = true;
     processCmdLine("perfdhcp -l " + loopback_iface
                    + " -6 -r 100 -n 10 -R 20 -D 3 -L 10547"
-                   + " -T ../templates/solicit-example.hex"
-                   + " -T ../templates/request6-example.hex ::1");
+                   + " -T " + getFullPath("solicit-example.hex")
+                   + " -T " + getFullPath("request6-example.hex ::1"));
     // For the first 3 packets we are simulating responses from server.
     // For other packets we don't so packet as 4,5,6 will be dropped and
     // then test should be interrupted and actual number of iterations will
@@ -981,9 +993,9 @@ TEST_F(TestControlTest, DISABLED_Packet6Exchange) {
 
 TEST_F(TestControlTest, PacketTemplates) {
     std::vector<uint8_t> template1(256);
-    std::string file1("../templates/test1.hex");
+    std::string file1("test1.hex");
     std::vector<uint8_t> template2(233);
-    std::string file2("../templates/test2.hex");
+    std::string file2("test2.hex");
     for (int i = 0; i < template1.size(); ++i) {
         template1[i] = static_cast<uint8_t>(random() % 256);
     }
@@ -1011,7 +1023,7 @@ TEST_F(TestControlTest, PacketTemplates) {
     EXPECT_TRUE(std::equal(template2.begin(), template2.end(), buf2.begin()));
 
     // Try to read template file with odd number of digits.
-    std::string file3("../templates/test3.hex");
+    std::string file3("test3.hex");
     // Size of the file is 2 times larger than binary data size and it is always
     // even number. Substracting 1 makes file size odd.
     ASSERT_TRUE(createTemplateFile(file3, template1, template1.size() * 2 - 1));
@@ -1021,7 +1033,7 @@ TEST_F(TestControlTest, PacketTemplates) {
     EXPECT_THROW(tc.initPacketTemplates(), isc::OutOfRange);
 
     // Try to read empty file.
-    std::string file4("../templates/test4.hex");
+    std::string file4("test4.hex");
     ASSERT_TRUE(createTemplateFile(file4, template2, 0));
     ASSERT_NO_THROW(
         processCmdLine("perfdhcp -l 127.0.0.1 -T " + file4 + " all")
@@ -1029,7 +1041,7 @@ TEST_F(TestControlTest, PacketTemplates) {
     EXPECT_THROW(tc.initPacketTemplates(), isc::OutOfRange);
 
     // Try reading file with non hexadecimal characters.
-    std::string file5("../templates/test5.hex");
+    std::string file5("test5.hex");
     ASSERT_TRUE(createTemplateFile(file5, template1, template1.size() * 2, true));
     ASSERT_NO_THROW(
         processCmdLine("perfdhcp -l 127.0.0.1 -T " + file5 + " all")
diff --git a/tests/tools/perfdhcp/tests/testdata/.gitignore b/tests/tools/perfdhcp/tests/testdata/.gitignore
new file mode 100644
index 0000000..6f865da
--- /dev/null
+++ b/tests/tools/perfdhcp/tests/testdata/.gitignore
@@ -0,0 +1,5 @@
+/test1.hex
+/test2.hex
+/test3.hex
+/test4.hex
+/test5.hex
diff --git a/tests/tools/perfdhcp/tests/testdata/Makefile.am b/tests/tools/perfdhcp/tests/testdata/Makefile.am
new file mode 100644
index 0000000..2de1643
--- /dev/null
+++ b/tests/tools/perfdhcp/tests/testdata/Makefile.am
@@ -0,0 +1,4 @@
+SUBDIRS = .
+
+EXTRA_DIST = discover-example.hex request4-example.hex
+EXTRA_DIST += solicit-example.hex request6-example.hex
diff --git a/tests/tools/perfdhcp/tests/testdata/discover-example.hex b/tests/tools/perfdhcp/tests/testdata/discover-example.hex
new file mode 100644
index 0000000..9a6e5ea
--- /dev/null
+++ b/tests/tools/perfdhcp/tests/testdata/discover-example.hex
@@ -0,0 +1 @@
+01010601008b45d200000000000000000000000000000000ac100102000c0102030400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000638253633501013707011c02030f060cff
\ No newline at end of file
diff --git a/tests/tools/perfdhcp/tests/testdata/request4-example.hex b/tests/tools/perfdhcp/tests/testdata/request4-example.hex
new file mode 100644
index 0000000..32447d6
--- /dev/null
+++ b/tests/tools/perfdhcp/tests/testdata/request4-example.hex
@@ -0,0 +1 @@
+01010601007b23f800000000000000000000000000000000ac100102000c0102030400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000638253633204ac1001813501033604ac1001013707011c02030f060cff
\ No newline at end of file
diff --git a/tests/tools/perfdhcp/tests/testdata/request6-example.hex b/tests/tools/perfdhcp/tests/testdata/request6-example.hex
new file mode 100644
index 0000000..1e3e76f
--- /dev/null
+++ b/tests/tools/perfdhcp/tests/testdata/request6-example.hex
@@ -0,0 +1 @@
+03da30c60001000e0001000117cf8e76000c010203060002000e0001000117cf8a5c080027a87b3400030028000000010000000a0000000e0005001820010db800010000000000000001b568000000be000000c8000800020000
\ No newline at end of file
diff --git a/tests/tools/perfdhcp/tests/testdata/solicit-example.hex b/tests/tools/perfdhcp/tests/testdata/solicit-example.hex
new file mode 100644
index 0000000..41c5ad3
--- /dev/null
+++ b/tests/tools/perfdhcp/tests/testdata/solicit-example.hex
@@ -0,0 +1 @@
+015f4e650001000e0001000117cf8e76000c010203040003000c0000000100000e01000015180006000400170018000800020000
\ No newline at end of file



More information about the bind10-changes mailing list