BIND 10 trac2981, updated. 3c285e7ef1b8890caa12ea83353b2e03f2bec23e [2981] Merge branch 'master' into trac2981

BIND 10 source code commits bind10-changes at lists.isc.org
Wed Jul 24 16:27:50 UTC 2013


The branch, trac2981 has been updated
       via  3c285e7ef1b8890caa12ea83353b2e03f2bec23e (commit)
       via  c706b28ae11ad17fde72a2f06a2cdf17f62a5514 (commit)
       via  c845a96005b28acde2d62a93ee8c046162da4cb4 (commit)
       via  0f845ed94f462dee85b67f056656b2a197878b04 (commit)
       via  fe2331e7e0cadd2e70dfecdb9cf5a67f6484a5bd (commit)
       via  135b47d64fba42eda8cd194f257ed77efb441797 (commit)
       via  616213225fee89adc57c8ea19a2195f8273dc5fa (commit)
       via  589f61f8a630643bdee4d8bb2be68a79fba64112 (commit)
       via  be65cfba939a6a7abd3c93931ce35c33d3e8247b (commit)
       via  17a2fab7ec8ccc6886e887f9d9bff9e51fe080a9 (commit)
       via  f48a3bff3fbbd15584d788a264d5966154394f04 (commit)
       via  4084c5f2a9a24009d645def53697b139bf989124 (commit)
       via  89d6be56eefa1ed79d9c0cfb8112243e1435beb6 (commit)
       via  088a0124e731718b6e8be8092a47a087436ec1dc (commit)
       via  6b65ea8a618d32efcdc0697cf0acc534b6f13404 (commit)
       via  0d4abba272e2e77132b374a657aa96f65505f26c (commit)
       via  ca5c18086bcc1fb712a33c0f4f96c27cb76348cd (commit)
       via  92ebabdbcf6168666b03d7f7fbb31f899be39322 (commit)
       via  d7c133c2e26b0174c96ef48781352feec5f9e03b (commit)
       via  949a25220c81c7bd4faed53e0434446c18171ec9 (commit)
       via  473c1e672ab88928d1f9bda61a1c2690393c018b (commit)
       via  da1318c5b12541c6a101408e49108b41c2d4aff1 (commit)
       via  bf471e6c6228edca829deb48f810944ea650a19b (commit)
       via  0657510f32552e1d2510ec34e807d41c1a0f964e (commit)
       via  5b097c33037c8891a50d77f71ee0518e9c7c955a (commit)
       via  18dd133a37d37df043d36008286bb769838617be (commit)
       via  049d11fa1b6b08e8ad866686e57726e5906ced1c (commit)
       via  ecd9691c1791c973b9afabe17b95c108596be0f2 (commit)
       via  2cbcb533f792251d19339c8b38ac7b87db94b28a (commit)
       via  02141f2524808a5ccf1ac9b620fc702e37607e31 (commit)
       via  07074c5295156356a0608fbaf8d706f5eaaaaef8 (commit)
       via  a076d799d926d23e67f495df40812cead7425aa4 (commit)
       via  01bb436c45f4d92a3575ff4ad2f5c1cb348ce399 (commit)
       via  a8a8909b511278b2abe1679a3c801bd1fd1ece74 (commit)
       via  15471a94d7cd89204b1a7de89398255662590458 (commit)
       via  ca9233574b5bea413b38916a0e3593ff8c581b1c (commit)
       via  5b10e071de6d724d42e2f6eba4cc78bb7fbacd23 (commit)
       via  1626b91e6d458b0dfe8fb71434b52273edaeb795 (commit)
       via  272122f27fdf10a479c94e6270326f6ab028939f (commit)
       via  d3c480a5ef3803787564cb8f1bbd92df4b7185d9 (commit)
       via  3c1f096e54e42bb79fec8a28abc6528b3c5fc857 (commit)
       via  8e13f44e871bde3f44ffc080a1996149d61fc13d (commit)
       via  56393640a8c2d3b11b549f1c1133991452c7a088 (commit)
       via  a6c4e80483303e6fb4deabb586a357987b79b194 (commit)
       via  0f8f7995fa2fd6f340d405305783a97b34307ca9 (commit)
       via  87ba16e0b1db1a831e394650c7713b028a7d42c8 (commit)
       via  d6de376f97313ba40fef989e4a437d184fdf70cc (commit)
       via  14018a4e7e240d9770dfd565284abdf2f82f9b30 (commit)
       via  2221a1de37ddc5c35e52ad0b8b99d7dec430b00c (commit)
       via  36815d6a2501933007ae6e8f823be08a64064058 (commit)
       via  75df517663d7743adfa1295e87f8a77ca97a059d (commit)
       via  39fa196b6b7eda249caa0501feb6d049cdfd9123 (commit)
       via  98c85cb61b5bcbf948e1ea0923adcfcdca753cbb (commit)
       via  abad8addef410474f0bb6bf6e15831902017e579 (commit)
       via  78fdf6daaf77f5e341e921bb1cb3ac7084060c75 (commit)
       via  4337b26b8553e2815c61e979a3c6442b38687f8c (commit)
       via  9f8d746079cad652e014d482327b6a73d5ee2f12 (commit)
       via  0a5d26dfc2ee6a4a84d596cbcb39e894e7432f3c (commit)
       via  bb7465ef1ba3853c069c44f8f1ab6491782fcd5f (commit)
       via  465c3330313daf9f80471056dc41eb53eaaa3531 (commit)
       via  9517e24b3bc274e4a8f080cb15358db8017c3f44 (commit)
       via  42de7b42fd0fd6653489efe5412b34365d3a4a77 (commit)
       via  01f159a6b4277ccbbb76b18e1de811a0a71b80cd (commit)
       via  c2dfa3796558372cb69c9bdad6963835c18b3937 (commit)
       via  339d60a7b7a5b83a2b9c5064af5d621e3047582f (commit)
       via  54c8dec80aa3198a5f6dd7123c1ca415a85a27f8 (commit)
       via  009e306698d3c8018fc2a83aa2c9cb6bd787a875 (commit)
       via  ffdc326d413b2eb9d95dc73f3948788ca7ece728 (commit)
       via  72b06693a049e925d240971d92dd8427d3fa8f73 (commit)
       via  721eff49ce716e79b898ad578e21efb96e684684 (commit)
       via  ed363b36987c46c37452c3e83b9efce0a5117cf1 (commit)
       via  8c65dc3a4cc369eab2235b0ebc6d56dae4589c5a (commit)
       via  fd70aa81aeebbb2768d89185d1859520cd535b58 (commit)
       via  dd01c785f0d8eb616ee4179299dc53e0d2c0015c (commit)
       via  82c997a72890a12af135ace5b9ee100e41c5534e (commit)
       via  0d98fbf5f919f2ca5a5134bd6d3527bed6606d10 (commit)
       via  4ae6b8af324d8e7bcd13f3e6ce5a632f3d704ccf (commit)
       via  5a67a8982baa1fd6b796c063eeb13850c633702c (commit)
       via  67a39b06a802b161dd6c4c93892bf3309d30d892 (commit)
       via  3430cf9ae922d5af87fef9ca4c202db482f29391 (commit)
       via  e746e58a9fec019dd8a944baeed75c685a6cdf28 (commit)
       via  196c9f80b51bb0c4d9e9f6540fc0c5258b69cd06 (commit)
       via  7f4bf2e2f9b9eaf3410bf22f918478cf4a5f0ec2 (commit)
       via  e45f325ec7438e0ecd09d37a7d938348232b994d (commit)
       via  ec4df20fd7827f622fd9aecf21faae072337b32d (commit)
       via  4ef6830ed357ceb859ebb3e5e821a064bd8797bb (commit)
       via  016c6b97353dd57158d1da0f574912f2823e74e1 (commit)
       via  d406f5702264d689e6c1b7dd6c285b7d5ab06db0 (commit)
       via  682cac0da7b318a81746d93424a638faa867ee7c (commit)
       via  bfcf73f7b86036c774b90e1df948e12399269385 (commit)
       via  a8a6595db650be70a46d4cd78f9cf8202b2c74a1 (commit)
       via  5b7b932c171d48009f722ea20ecdbe852ad2b8aa (commit)
       via  bc25129780f807429a8c9f4cec569ed35f6b066c (commit)
       via  48aa711b39c5c57d569d6738257ed9e5b4d34995 (commit)
       via  dbabac08932ffda4307f7176be732b8aea655e41 (commit)
       via  4bebf8b48d3e6b9b9b1b50618671e772b7e7ebab (commit)
       via  e1b4215d69747ee068893e167e96428dfdbab1ce (commit)
       via  30e7a2c7499274f1feb8a02fe37ab35144c20689 (commit)
       via  b9c531514beef9d0ee0d706f2850a30ba20c5cd1 (commit)
       via  19c93b0845947d0bb5ff60c602d5fd3f64c531f2 (commit)
       via  18f8355aae2aabb011a57176ec7381b656e52260 (commit)
       via  4d6b150d0605cec1086f05c7557c7990e4524188 (commit)
       via  bdeeb21d2f97586d4bf754b4a1156412810ef441 (commit)
       via  4c9ef2963127e8d887a2ca38435e61a28f1c7c63 (commit)
       via  43ea555f6ae497ec40672b951d8a00c437b89c0a (commit)
       via  04e36988a16fb36ab84ec548a174b148924ed0d7 (commit)
       via  d2cf6a85554d5d9e02f1073bf41c61206a82effc (commit)
       via  32044b839f83bba409fad29df68e94247e7a94a0 (commit)
       via  6ad2bdd472dd5ef1156577667c09bc0bd90d3f39 (commit)
       via  7076a02b24e63b0fa542af760585e6ad95cc421f (commit)
       via  73656261a319aad29c3b9c405e8a7a6d1b9445f6 (commit)
       via  64b9c9084608a24a11c4acd75bb2bdd1b231cdcc (commit)
       via  d0d6ad3146dcecd50b276f26ce4ebaa3e6fa84a2 (commit)
       via  43e5ea02d51f60045318fde277b77d3603f5194f (commit)
       via  c0153581c3533ef045a92e68e0464aab00947cbb (commit)
       via  962c4c299ee7fe733267e7ffdb1cf3f6ff3ec394 (commit)
       via  0583652b5a391ec078361254b6d473fa8ffdb4a2 (commit)
       via  79154442df1acbc7b00e57091b94a97086134a71 (commit)
       via  81ddf65784bb1089f4c6761f018ed1ada5a45ad2 (commit)
       via  675e72e818b0a0e61ee8e4c62611ce41e1b5fd98 (commit)
       via  6cc73bde44fc16387788cf53906d1c9ecb4fd947 (commit)
       via  2cf3214c48f9825347dad3b3bca6e6e6d8b8b6e2 (commit)
       via  b61e894db16aa458c9d3d405210c357e1fdde657 (commit)
       via  dc342c8d7ff4eff388bc596e625e54ac9617c09c (commit)
       via  326b3dc5b7383a9357dfe2f9d75e1e05b780153e (commit)
       via  ca87835d1ec6e3393a98dcad72b6c96d43331e39 (commit)
       via  497bf6025e70ca767c703dc192a11d968c0c031d (commit)
       via  ca3f4ecf2c5dc37dab7d9e89f4bc41ee000f12c1 (commit)
       via  fcea4de8a92427f8b0910048ecb3c7c420d6daf9 (commit)
       via  8288bf44eb68ee5f7f098beeff11e9372065b757 (commit)
       via  6d1f4e151097981b21acb32175756afadf432fe6 (commit)
       via  d3c2b9a2fde279369ebac19e23153963fa855d1b (commit)
       via  313bac393cc4a097be0bdeaac4e21bbef1ba0aa2 (commit)
       via  4d0494f1618492afa57a70d036d63a6ac67ee894 (commit)
       via  3945e7047b3e94919601d9d4bfca36e54310c43c (commit)
       via  f623694374d70db2e5cae9f78b44fc74d4580f9e (commit)
       via  cf1828a29fe1afd7382ebf41417b98c027ac5bfd (commit)
       via  4ab333e8a81f1d83d03a1f78100823293ff3415b (commit)
       via  e6bd8c1c59c87dbbed3160350e063031839d6425 (commit)
       via  680e748149d99afa27cf4a5a7de83d1652db06f9 (commit)
       via  2ae1f990955e7a2b5aabef0ec93448c6c7e8c89f (commit)
       via  338cfe287476bdbb2867cf3e745738435a0e2462 (commit)
       via  23059244dc6933f7eed78bf46b6f7f8233301618 (commit)
       via  5c03ce929bbda1f0fb271b35b5b3dbe1a12696c9 (commit)
       via  6d40ecfc230408137edcc43f2653c70fe12589d6 (commit)
       via  ed2fb666188ea2c005b36d767bb7bb2861178dcc (commit)
       via  f33bdd59c6a8c8ea883f11578b463277d01c2b70 (commit)
       via  a3e8b0ad37ebb999e29c390332045bc9babfdc3c (commit)
       via  45e404c1b58487d180e91d4b4eb0f5059902b190 (commit)
       via  c04fb71fa44c2a458aac57ae54eeb1711c017a49 (commit)
       via  e773dac499b952107dfc5e34290ec421b8bbf7c6 (commit)
       via  a50149705d6fe5e9b50ab963560ae9eee7458c45 (commit)
       via  5e61d601d9424aac561c0661061e19188ed8d93f (commit)
       via  57a25a015e23dca348c1e1a3c5821d64981277b1 (commit)
       via  3ce8c82c2a3ef441510d2c7b0765169c2177f358 (commit)
       via  d1c359da0db07875b967e616b07e5d56bcc51588 (commit)
       via  41d5b2ead11a334e89a7f04ab38571e2795083d0 (commit)
       via  d432cd1889063c53a6d5249be9bf9c6029736364 (commit)
       via  6917e1335e35a4465b9e242a6c3e9b38c909d201 (commit)
       via  2e7cd3c5a4935c2957d800c9e9f29e191e9bcece (commit)
       via  0fc40f1677c6bdaf1e2920c6f86b2bc6c15b4cb0 (commit)
       via  1a0742f1fd61234a6ec395729b0c4c4cad64aaac (commit)
       via  e7c52fec35a1f6a49351f58877d21fd43b4082e0 (commit)
       via  eac5e751473e238dee1ebf16491634a1fbea25e2 (commit)
       via  351b82d0ed7d964803be52b433706377d7b2f780 (commit)
       via  e42db3d2834457c04e19c256a67f4059cd36860d (commit)
       via  971e77005e139347d5d4fedf0cc87bfb26b2685f (commit)
       via  6e99f2cb85dd4722f7a5c2d15ebf2458cecc7b32 (commit)
       via  18b3c00bb06e51f6a3b16fa634983a43f65b8e97 (commit)
       via  79fe7149c3d2cc20d4bef791d27d5316702de53b (commit)
       via  fa4e16aaa9ee6e680606f0211c71c92669b8e74f (commit)
       via  61dcb03fbe2c9a4e3df6e8afe269c0d94f00d925 (commit)
       via  60a57d2997cfd0bfc27d580df175b31589fdedbf (commit)
       via  bf1529f69378453d8fb0b75cabb5d560e278d1c3 (commit)
       via  58f2a7f08242fad52c397bbf7681b209dc84c896 (commit)
       via  407655388fe055d2a8a950ca31e36a9102b59682 (commit)
       via  41e40c5cb4a62d32889fbbaf704792add24b02d2 (commit)
       via  1949be3727367e2b4f9a399f3678a8fa1e969bc1 (commit)
       via  ee58a132fe6313d7d2d9ecc7eba8b455975ebac7 (commit)
       via  e15f18aff64c8f7481546e877f059290653b9926 (commit)
       via  6916c32a3f5f409a01157d1957ebd3c9e296ed99 (commit)
       via  ee90ee28b431aac9b513330e31032a20d2c0ac27 (commit)
       via  c3ef6db1a2b58b3732f1d2aa169127ade79fcb77 (commit)
       via  5fe6fd9977984ddc71d71266afb41f963012ddf9 (commit)
       via  a4ff0d97db3b04c67d3bdd246c04d1dc4c47fb4d (commit)
       via  4afd4b896c591245f51b992296039ffd5b3991b5 (commit)
       via  489316fd7418436e94de387ce9477c18e2b4da59 (commit)
       via  8b4019ff1577d1d32e9400c2baba77c948d0bfe4 (commit)
       via  603b0180bc4c1e9b7501c9e3292cad6f3427bf65 (commit)
       via  ac446f02802bff0a91af4caa6e6717b8f0ff350c (commit)
       via  95d6573794927ff46242625736d894ca4758f55a (commit)
       via  4507063fc988329516dccad11ff28936aac8a08e (commit)
       via  c6b3ce6d16f92b80db77487900fb57a7a67b3203 (commit)
       via  20b0efb43cff8020da949df03ce13d58ee6e4eac (commit)
       via  9785efa0afab6667a61d322f9a3a3dee048dd0dc (commit)
       via  7f644c2532ecf0f3dab826a091fbe900dc5352f4 (commit)
       via  5b0a2957d3a806ce84ad86dd7789edf3ef495d92 (commit)
       via  df2ee17ce15c5259ec2632ebf536f9f117419c88 (commit)
       via  2ba18911d1daf85d52fea1e380f9425b7e8c3101 (commit)
       via  a81a52389a3ce477db992da27cf09cb1adc0e49e (commit)
       via  d2846e4ecf8679ec53b2cd16e543b7b2488f2692 (commit)
       via  ef242ca52d93c62bfc2ef42260ebf5069d31cde5 (commit)
       via  5e4b2f06b8dfe4cf1eb7f26836942d10e3c719e4 (commit)
       via  c5b11e54a93c528d549a66206f0ff2c2ddf4273d (commit)
       via  5eea9d893ec4ac21e89f8b3b0f3e68af625eed48 (commit)
       via  5d9916e93986dbeb890cbce2782d67b9f535adb4 (commit)
       via  cdb34f7debacbbbc4adec956f682c07e0a77d706 (commit)
       via  5482fb745b8fc0f3a3f80b25416752682c345211 (commit)
       via  a63b3077dd102512b9002e204a955f5b8d783ff4 (commit)
       via  16156f814b54dffdc0304016aa41b0746c613d3a (commit)
       via  328ab84c1ddd6cf3f7e45a1b6fb6e1552bb26f68 (commit)
       via  b4b1eca7131cedca72a8973718881c1f18d2010f (commit)
       via  5e8a8cc152ef5cd151f99389f475b2a16832fb9c (commit)
       via  d63e340ac318b68e1a04458ba9669f159d6eb508 (commit)
       via  1954874770a322999e04315397c25a56cd1f1b3a (commit)
       via  570fc14873220c13f62e622fd3f227a73ae9a106 (commit)
       via  c77a043a8a6f6216499cc9697d5881c65bd9ad2d (commit)
       via  58875c2089c61e5174db855530e9de237920c669 (commit)
       via  2bde65105b86a512aa798cac5bcad723bafb38c9 (commit)
       via  418f35f58458de0480d6a58299f06721481d9196 (commit)
       via  010bda2e96f5763e6eec226ac1cee96bce2588e5 (commit)
       via  ba91cff481836c5c8c1993174727646213cea60b (commit)
       via  d72ba709f11dee0d0bb90868fc042bd8caaf8626 (commit)
       via  b4bf719aa55ad684dbfa5faa39aa93ebf89bac18 (commit)
       via  49ee4eaf84ad72a044cf4957af016d1e8e15cab0 (commit)
       via  87fc0368a47156638c189fbf071471ee0d9c4463 (commit)
       via  eff9543714f70df752ab02db33b7a7b579b6a225 (commit)
       via  db3f2d81e975c2c82e575ba2947340f520f763a9 (commit)
       via  2b6d8c99eedfc5e77477461963bff7ae031192bf (commit)
       via  cc8e7feaf34f1028438854453b4811d7283852c0 (commit)
       via  8b7ae5faff5a12fb2841e8b35b43253c0d67c22f (commit)
       via  b021224a6314396eccfb9c7e0af223474079e864 (commit)
       via  7c20771f4296820b7efc22b19a0783e7d226736a (commit)
       via  ae6f706d85ae90ca971d2cfef2abfe7e5bc7856d (commit)
       via  9a306b863a628351a84cfafdabe5700d4840be6a (commit)
       via  42e6736faa21f98de53a4ac0bc52306041581d91 (commit)
       via  dc50d73a4980ad1900e0445a1ea0dd46bf37245c (commit)
       via  017278ca8084b9420fa94a2e9cc44aac9ebcae04 (commit)
       via  e61a5182aa295c0a96428d09e1d259c9b8b09d0d (commit)
       via  8e94ab0a3baabd2b20f181243626168df4a56ec1 (commit)
       via  390c1a763df3dbfcf0f24394464c0db89aaede9f (commit)
       via  ca59c5c8d5f309657b3f24f9edcc7100fd9d07a5 (commit)
       via  e9ac9994ee59019ec0689b94ef4d5cb15c0e5699 (commit)
       via  53f6ad9e37ee9df633af7256bcda6bafd3cdc1a2 (commit)
       via  32f82dfe9ca5b9a66104b0cec748a16dc648a0d0 (commit)
       via  78981c6ff6b82a580786f332e58b5d3cfeed4e2c (commit)
       via  c25adf51ab0d73e162e520592daaa3e70ead8c15 (commit)
       via  e09ae1968e2c225fb42175e3962168953d4399e1 (commit)
       via  a57ce9dae4185a4542401a5ff6212f0bdf752488 (commit)
       via  be6122f520871344324be1ceb9f118195d508bba (commit)
       via  0a4612cfe59ee2149b885550e467f4de42236bbb (commit)
       via  03db051a722775e59b803bd44e02d1faad2a0a9a (commit)
       via  5b6251fb9bb2cd793fd9f86719d1e359aa427cca (commit)
       via  6e6cff79121a939cc961c8f36a489cbb2d0b7074 (commit)
       via  7ef14b47d3bd773155e4b6d9c5a739b0213e8f28 (commit)
       via  dac0b87d5b0466e3aaf0f49ec7b2362606c03415 (commit)
      from  ed780cbba41428341c5956ff5b032d829d059cab (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 3c285e7ef1b8890caa12ea83353b2e03f2bec23e
Merge: c706b28 c845a96
Author: Stephen Morris <stephen at isc.org>
Date:   Wed Jul 24 17:27:34 2013 +0100

    [2981] Merge branch 'master' into trac2981
    
    Conflicts:
    	src/bin/dhcp6/config_parser.cc
    	src/bin/dhcp6/dhcp6.spec
    	src/bin/dhcp6/tests/Makefile.am
    	src/bin/dhcp6/tests/config_parser_unittest.cc
    	src/lib/dhcpsrv/dhcp_parsers.h
    	src/lib/dhcpsrv/tests/Makefile.am
    	src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc

commit c706b28ae11ad17fde72a2f06a2cdf17f62a5514
Author: Stephen Morris <stephen at isc.org>
Date:   Wed Jul 24 14:51:10 2013 +0100

    [2981] Checkpoint prior to rebasing on updated master

commit 721eff49ce716e79b898ad578e21efb96e684684
Author: Stephen Morris <stephen at isc.org>
Date:   Mon Jul 1 15:01:24 2013 +0100

    [2981] Checkpoint
    
    Now awaiting the merging of #2980 (the hooks code) so that the
    configuration validation code can check if the libraries are valid
    before accepting the configuration.

commit ed363b36987c46c37452c3e83b9efce0a5117cf1
Author: Stephen Morris <stephen at isc.org>
Date:   Mon Jul 1 12:06:20 2013 +0100

    [2981] Added storage of hooks library names to the DHCP config manager

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

Summary of changes:
 .gitignore                                         |    4 +
 ChangeLog                                          |   72 ++
 configure.ac                                       |   26 +-
 doc/Doxyfile                                       |    1 +
 doc/design/datasrc/auth-local.txt                  |  137 +--
 doc/design/datasrc/auth-mapped.txt                 |   15 +-
 doc/design/datasrc/memmgr-mapped-init.txt          |   25 +-
 doc/design/datasrc/memmgr-mapped-reload.txt        |   40 +-
 doc/design/datasrc/overview.txt                    |   70 +-
 doc/devel/mainpage.dox                             |   49 +-
 doc/guide/bind10-guide.xml                         |  111 +-
 m4macros/ax_boost_for_bind10.m4                    |   14 +
 src/bin/auth/auth_messages.mes                     |    4 +
 src/bin/auth/auth_srv.cc                           |    1 +
 src/bin/auth/datasrc_clients_mgr.h                 |  200 +++-
 .../auth/tests/datasrc_clients_builder_unittest.cc |  137 ++-
 src/bin/auth/tests/datasrc_clients_mgr_unittest.cc |   60 +-
 src/bin/auth/tests/test_datasrc_clients_mgr.cc     |   15 +-
 src/bin/auth/tests/test_datasrc_clients_mgr.h      |   30 +-
 src/bin/cmdctl/tests/b10-certgen_test.py           |    2 +
 src/bin/cmdctl/tests/cmdctl_test.py                |   20 +-
 src/bin/d2/Makefile.am                             |   13 +-
 src/bin/d2/d2_cfg_mgr.cc                           |  132 +++
 src/bin/d2/d2_cfg_mgr.h                            |  175 +++
 src/bin/d2/d2_config.cc                            |  605 ++++++++++
 src/bin/d2/d2_config.h                             |  941 +++++++++++++++
 src/bin/d2/d2_controller.h                         |    2 +-
 src/bin/d2/d2_messages.mes                         |   40 +-
 src/bin/d2/d2_process.cc                           |   19 +-
 src/bin/d2/d2_process.h                            |    6 +-
 src/bin/d2/d2_update_message.cc                    |  221 ++++
 src/bin/d2/d2_update_message.h                     |  341 ++++++
 .../hooks/library_handle.cc => bin/d2/d2_zone.cc}  |   27 +-
 src/bin/d2/d2_zone.h                               |  117 ++
 src/bin/d2/d_cfg_mgr.cc                            |  240 ++++
 src/bin/d2/d_cfg_mgr.h                             |  331 ++++++
 src/bin/d2/d_controller.cc                         |   25 +-
 src/bin/d2/d_controller.h                          |   24 +-
 src/bin/d2/d_process.h                             |   24 +-
 src/bin/d2/dhcp-ddns.spec                          |  210 +++-
 src/bin/d2/dns_client.cc                           |  247 ++++
 src/bin/d2/dns_client.h                            |  192 +++
 src/bin/d2/main.cc                                 |    3 +-
 src/bin/d2/ncr_msg.cc                              |  459 ++++++++
 src/bin/d2/ncr_msg.h                               |  487 ++++++++
 src/bin/d2/tests/.gitignore                        |    1 +
 src/bin/d2/tests/Makefile.am                       |   19 +
 src/bin/d2/tests/d2_cfg_mgr_unittests.cc           | 1238 ++++++++++++++++++++
 src/bin/d2/tests/d2_controller_unittests.cc        |   28 +-
 src/bin/d2/tests/d2_process_unittests.cc           |   17 +-
 src/bin/d2/tests/d2_update_message_unittests.cc    |  591 ++++++++++
 src/bin/d2/tests/d2_zone_unittests.cc              |   75 ++
 src/bin/d2/tests/d_cfg_mgr_unittests.cc            |  386 ++++++
 src/bin/d2/tests/d_test_stubs.cc                   |  126 +-
 src/bin/d2/tests/d_test_stubs.h                    |  219 +++-
 src/bin/d2/tests/dns_client_unittests.cc           |  408 +++++++
 src/bin/d2/tests/ncr_unittests.cc                  |  401 +++++++
 .../d2/tests/test_data_files_config.h.in}          |   30 +-
 src/bin/dhcp4/Makefile.am                          |    1 +
 src/bin/dhcp4/config_parser.cc                     |   94 +-
 src/bin/dhcp4/config_parser.h                      |    4 +-
 src/bin/dhcp4/ctrl_dhcp4_srv.cc                    |   36 +-
 src/bin/dhcp4/ctrl_dhcp4_srv.h                     |    3 +-
 src/bin/dhcp4/dhcp4.dox                            |    4 +
 src/bin/dhcp4/dhcp4.spec                           |    6 +-
 src/bin/dhcp4/dhcp4_hooks.dox                      |  138 +++
 src/bin/dhcp4/dhcp4_log.h                          |    3 +
 src/bin/dhcp4/dhcp4_messages.mes                   |   36 +-
 src/bin/dhcp4/dhcp4_srv.cc                         |  238 +++-
 src/bin/dhcp4/dhcp4_srv.h                          |   69 +-
 src/bin/dhcp4/tests/Makefile.am                    |    1 +
 src/bin/dhcp4/tests/config_parser_unittest.cc      |   99 +-
 src/bin/dhcp4/tests/dhcp4_srv_unittest.cc          |  824 ++++++++++++-
 src/bin/dhcp6/Makefile.am                          |    1 +
 src/bin/dhcp6/config_parser.cc                     |  111 +-
 src/bin/dhcp6/config_parser.h                      |    6 +-
 src/bin/dhcp6/ctrl_dhcp6_srv.cc                    |   31 +-
 src/bin/dhcp6/dhcp6.dox                            |    4 +
 src/bin/dhcp6/dhcp6.spec                           |   24 +-
 src/bin/dhcp6/dhcp6_hooks.dox                      |  139 +++
 src/bin/dhcp6/dhcp6_log.h                          |    3 +
 src/bin/dhcp6/dhcp6_messages.mes                   |   44 +-
 src/bin/dhcp6/dhcp6_srv.cc                         |  214 +++-
 src/bin/dhcp6/dhcp6_srv.h                          |   58 +-
 src/bin/dhcp6/tests/Makefile.am                    |   19 +-
 .../dhcp6/tests/callout_library_1.cc}              |   31 +-
 .../dhcp6/tests/callout_library_2.cc}              |   31 +-
 src/bin/dhcp6/tests/callout_library_common.h       |   80 ++
 src/bin/dhcp6/tests/config_parser_unittest.cc      |  426 ++++++-
 src/bin/dhcp6/tests/dhcp6_srv_unittest.cc          |  763 +++++++++++-
 .../dhcp6/tests/marker_file.h.in}                  |   29 +-
 src/bin/dhcp6/tests/test_libraries.h.in            |   51 +
 src/bin/memmgr/.gitignore                          |    1 +
 src/bin/memmgr/memmgr.py.in                        |   60 +
 src/bin/memmgr/tests/memmgr_test.py                |   28 +-
 src/bin/usermgr/tests/b10-cmdctl-usermgr_test.py   |   13 +
 src/bin/xfrin/b10-xfrin.xml                        |  175 +--
 src/bin/xfrin/tests/xfrin_test.py                  |    1 +
 src/bin/xfrin/xfrin.py.in                          |   22 +-
 src/bin/xfrin/xfrin.spec                           |  204 ++--
 src/bin/xfrout/b10-xfrout.xml                      |   70 +-
 src/bin/xfrout/tests/xfrout_test.py.in             |   30 +-
 src/bin/xfrout/xfrout.py.in                        |    6 +-
 src/bin/xfrout/xfrout.spec.pre.in                  |   96 +-
 src/bin/zonemgr/tests/zonemgr_test.py              |  137 ++-
 src/bin/zonemgr/zonemgr.py.in                      |  252 ++--
 src/bin/zonemgr/zonemgr_messages.mes               |   12 +-
 src/lib/Makefile.am                                |    2 +-
 src/lib/dhcp/iface_mgr.cc                          |    9 +-
 src/lib/dhcp/iface_mgr.h                           |    8 +
 src/lib/dhcpsrv/Makefile.am                        |    4 +
 src/lib/dhcpsrv/alloc_engine.cc                    |  201 +++-
 src/lib/dhcpsrv/alloc_engine.h                     |   30 +-
 src/lib/dhcpsrv/cfgmgr.cc                          |   55 +-
 src/lib/dhcpsrv/cfgmgr.h                           |   92 ++
 src/lib/dhcpsrv/dhcp_parsers.cc                    |  279 +++--
 src/lib/dhcpsrv/dhcp_parsers.h                     |  243 ++--
 src/lib/dhcpsrv/dhcpsrv_log.h                      |    3 +
 src/lib/dhcpsrv/dhcpsrv_messages.mes               |   26 +
 src/lib/dhcpsrv/tests/Makefile.am                  |    3 +-
 src/lib/dhcpsrv/tests/alloc_engine_unittest.cc     |  498 +++++++-
 src/lib/dhcpsrv/tests/basic_callout_library.cc     |  115 ++
 src/lib/dhcpsrv/tests/cfgmgr_unittest.cc           |   45 +
 src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc     |  287 ++++-
 src/lib/dhcpsrv/tests/full_callout_library.cc      |  137 +++
 src/lib/dhcpsrv/tests/test_libraries.h.in          |   55 +
 src/lib/hooks/Makefile.am                          |   56 +
 src/lib/{util => }/hooks/callout_handle.cc         |   57 +-
 src/lib/{util => }/hooks/callout_handle.h          |   54 +-
 src/lib/{util => }/hooks/callout_manager.cc        |  101 +-
 src/lib/{util => }/hooks/callout_manager.h         |  166 ++-
 .../hooks/library_handle.cc => hooks/hooks.h}      |   37 +-
 src/lib/hooks/hooks_component_developer.dox        |  483 ++++++++
 .../hooks/library_handle.cc => hooks/hooks_log.cc} |   29 +-
 src/lib/hooks/hooks_log.h                          |   50 +
 src/lib/hooks/hooks_manager.cc                     |  200 ++++
 src/lib/hooks/hooks_manager.h                      |  323 +++++
 src/lib/hooks/hooks_messages.mes                   |  162 +++
 src/lib/hooks/library_handle.cc                    |   76 ++
 src/lib/{util => }/hooks/library_handle.h          |   38 +-
 src/lib/hooks/library_manager.cc                   |  353 ++++++
 src/lib/hooks/library_manager.h                    |  231 ++++
 src/lib/hooks/library_manager_collection.cc        |  129 ++
 src/lib/hooks/library_manager_collection.h         |  170 +++
 src/lib/hooks/pointer_converter.h                  |  121 ++
 src/lib/{util => }/hooks/server_hooks.cc           |  110 +-
 src/lib/{util => }/hooks/server_hooks.h            |  140 +--
 src/lib/hooks/tests/Makefile.am                    |  104 ++
 src/lib/hooks/tests/basic_callout_library.cc       |  115 ++
 .../tests/callout_handle_unittest.cc               |   22 +-
 .../tests/callout_manager_unittest.cc              |  205 +++-
 src/lib/hooks/tests/common_test_class.h            |  142 +++
 .../tests/framework_exception_library.cc}          |   40 +-
 src/lib/hooks/tests/full_callout_library.cc        |  137 +++
 src/lib/{util => hooks}/tests/handles_unittest.cc  |   91 +-
 src/lib/hooks/tests/hooks_manager_unittest.cc      |  524 +++++++++
 .../tests/incorrect_version_library.cc}            |   34 +-
 .../tests/library_manager_collection_unittest.cc   |  251 ++++
 src/lib/hooks/tests/library_manager_unittest.cc    |  569 +++++++++
 src/lib/hooks/tests/load_callout_library.cc        |  119 ++
 .../tests/load_error_callout_library.cc}           |   42 +-
 .../tests/marker_file.h.in}                        |   28 +-
 .../tests/no_version_library.cc}                   |   33 +-
 .../tests/run_unittests.cc}                        |   32 +-
 .../{util => hooks}/tests/server_hooks_unittest.cc |  174 +--
 src/lib/hooks/tests/test_libraries.h.in            |   79 ++
 .../tests/unload_callout_library.cc}               |   47 +-
 src/lib/python/isc/config/ccsession.py             |   84 +-
 src/lib/python/isc/config/tests/ccsession_test.py  |   71 ++
 src/lib/python/isc/memmgr/Makefile.am              |    2 +-
 src/lib/python/isc/memmgr/builder.py               |   99 ++
 src/lib/python/isc/memmgr/tests/Makefile.am        |    2 +-
 src/lib/python/isc/memmgr/tests/builder_tests.py   |  121 ++
 src/lib/python/isc/notify/notify_out.py            |    8 +-
 src/lib/python/isc/notify/tests/notify_out_test.py |   24 +-
 src/lib/python/isc/server_common/.gitignore        |    1 +
 .../python/isc/server_common/bind10_server.py.in   |   78 +-
 .../isc/server_common/tests/bind10_server_test.py  |   42 +
 src/lib/python/isc/statistics/counters.py          |  156 ++-
 .../python/isc/statistics/tests/counters_test.py   |   43 +-
 .../isc/statistics/tests/testdata/test_spec2.spec  |   96 +-
 .../isc/statistics/tests/testdata/test_spec3.spec  |  204 ++--
 src/lib/util/Makefile.am                           |    4 -
 src/lib/util/tests/Makefile.am                     |    4 -
 .../util/tests/memory_segment_mapped_unittest.cc   |    9 +
 .../xfrin/retransfer_slave.conf.orig               |    1 -
 .../xfrin/retransfer_slave_diffs.conf              |    1 -
 .../xfrin/retransfer_slave_notify.conf.orig        |    1 -
 .../xfrin/retransfer_slave_notify_v4.conf          |    1 -
 tests/lettuce/features/terrain/terrain.py          |    2 +-
 .../lettuce/features/xfrin_notify_handling.feature |   18 +-
 191 files changed, 21097 insertions(+), 2122 deletions(-)
 create mode 100644 src/bin/d2/d2_cfg_mgr.cc
 create mode 100644 src/bin/d2/d2_cfg_mgr.h
 create mode 100644 src/bin/d2/d2_config.cc
 create mode 100644 src/bin/d2/d2_config.h
 create mode 100644 src/bin/d2/d2_update_message.cc
 create mode 100644 src/bin/d2/d2_update_message.h
 copy src/{lib/util/hooks/library_handle.cc => bin/d2/d2_zone.cc} (58%)
 create mode 100644 src/bin/d2/d2_zone.h
 create mode 100644 src/bin/d2/d_cfg_mgr.cc
 create mode 100644 src/bin/d2/d_cfg_mgr.h
 create mode 100644 src/bin/d2/dns_client.cc
 create mode 100644 src/bin/d2/dns_client.h
 create mode 100644 src/bin/d2/ncr_msg.cc
 create mode 100644 src/bin/d2/ncr_msg.h
 create mode 100644 src/bin/d2/tests/d2_cfg_mgr_unittests.cc
 create mode 100644 src/bin/d2/tests/d2_update_message_unittests.cc
 create mode 100644 src/bin/d2/tests/d2_zone_unittests.cc
 create mode 100644 src/bin/d2/tests/d_cfg_mgr_unittests.cc
 create mode 100644 src/bin/d2/tests/dns_client_unittests.cc
 create mode 100644 src/bin/d2/tests/ncr_unittests.cc
 copy src/{lib/util/hooks/library_handle.cc => bin/d2/tests/test_data_files_config.h.in} (50%)
 create mode 100644 src/bin/dhcp4/dhcp4_hooks.dox
 create mode 100644 src/bin/dhcp6/dhcp6_hooks.dox
 copy src/{lib/util/hooks/library_handle.cc => bin/dhcp6/tests/callout_library_1.cc} (55%)
 copy src/{lib/util/hooks/library_handle.cc => bin/dhcp6/tests/callout_library_2.cc} (55%)
 create mode 100644 src/bin/dhcp6/tests/callout_library_common.h
 copy src/{lib/util/hooks/library_handle.cc => bin/dhcp6/tests/marker_file.h.in} (55%)
 create mode 100644 src/bin/dhcp6/tests/test_libraries.h.in
 create mode 100644 src/lib/dhcpsrv/tests/basic_callout_library.cc
 create mode 100644 src/lib/dhcpsrv/tests/full_callout_library.cc
 create mode 100644 src/lib/dhcpsrv/tests/test_libraries.h.in
 create mode 100644 src/lib/hooks/Makefile.am
 rename src/lib/{util => }/hooks/callout_handle.cc (64%)
 rename src/lib/{util => }/hooks/callout_handle.h (85%)
 rename src/lib/{util => }/hooks/callout_manager.cc (65%)
 rename src/lib/{util => }/hooks/callout_manager.h (64%)
 copy src/lib/{util/hooks/library_handle.cc => hooks/hooks.h} (55%)
 create mode 100644 src/lib/hooks/hooks_component_developer.dox
 copy src/lib/{util/hooks/library_handle.cc => hooks/hooks_log.cc} (53%)
 create mode 100644 src/lib/hooks/hooks_log.h
 create mode 100644 src/lib/hooks/hooks_manager.cc
 create mode 100644 src/lib/hooks/hooks_manager.h
 create mode 100644 src/lib/hooks/hooks_messages.mes
 create mode 100644 src/lib/hooks/library_handle.cc
 rename src/lib/{util => }/hooks/library_handle.h (72%)
 create mode 100644 src/lib/hooks/library_manager.cc
 create mode 100644 src/lib/hooks/library_manager.h
 create mode 100644 src/lib/hooks/library_manager_collection.cc
 create mode 100644 src/lib/hooks/library_manager_collection.h
 create mode 100644 src/lib/hooks/pointer_converter.h
 rename src/lib/{util => }/hooks/server_hooks.cc (61%)
 rename src/lib/{util => }/hooks/server_hooks.h (55%)
 create mode 100644 src/lib/hooks/tests/Makefile.am
 create mode 100644 src/lib/hooks/tests/basic_callout_library.cc
 rename src/lib/{util => hooks}/tests/callout_handle_unittest.cc (95%)
 rename src/lib/{util => hooks}/tests/callout_manager_unittest.cc (82%)
 create mode 100644 src/lib/hooks/tests/common_test_class.h
 copy src/lib/{util/hooks/library_handle.cc => hooks/tests/framework_exception_library.cc} (55%)
 create mode 100644 src/lib/hooks/tests/full_callout_library.cc
 rename src/lib/{util => hooks}/tests/handles_unittest.cc (94%)
 create mode 100644 src/lib/hooks/tests/hooks_manager_unittest.cc
 copy src/lib/{util/hooks/library_handle.cc => hooks/tests/incorrect_version_library.cc} (55%)
 create mode 100644 src/lib/hooks/tests/library_manager_collection_unittest.cc
 create mode 100644 src/lib/hooks/tests/library_manager_unittest.cc
 create mode 100644 src/lib/hooks/tests/load_callout_library.cc
 copy src/lib/{util/hooks/library_handle.cc => hooks/tests/load_error_callout_library.cc} (55%)
 copy src/lib/{util/hooks/library_handle.cc => hooks/tests/marker_file.h.in} (55%)
 copy src/lib/{util/hooks/library_handle.cc => hooks/tests/no_version_library.cc} (55%)
 copy src/lib/{util/hooks/library_handle.cc => hooks/tests/run_unittests.cc} (50%)
 rename src/lib/{util => hooks}/tests/server_hooks_unittest.cc (52%)
 create mode 100644 src/lib/hooks/tests/test_libraries.h.in
 rename src/lib/{util/hooks/library_handle.cc => hooks/tests/unload_callout_library.cc} (51%)
 create mode 100644 src/lib/python/isc/memmgr/builder.py
 create mode 100644 src/lib/python/isc/memmgr/tests/builder_tests.py
 create mode 100644 src/lib/python/isc/server_common/.gitignore

-----------------------------------------------------------------------
diff --git a/.gitignore b/.gitignore
index 7bc41b4..45f9424 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,6 +11,9 @@ Makefile
 Makefile.in
 TAGS
 
+*.log
+*.trs
+
 /aclocal.m4
 /autom4te.cache/
 /config.guess
@@ -30,6 +33,7 @@ TAGS
 /missing
 /py-compile
 /stamp-h1
+/test-driver
 
 /all.info
 /coverage-cpp-html
diff --git a/ChangeLog b/ChangeLog
index 6c025f1..91b9569 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,75 @@
+646.	[func]		stephen
+	Extended the hooks framework to add a "validate libraries" function.
+	This will be used to check libraries specified during BIND 10
+	configuration.
+	(Trac #3054, git 0f845ed94f462dee85b67f056656b2a197878b04)
+
+645.	[func]		tomek
+	Added initial set of hooks (pk4_receive, subnet4_select,
+	lease4_select, pkt4_send) to the DHCPv6 server.
+	(Trac #2994, git be65cfba939a6a7abd3c93931ce35c33d3e8247b)
+
+644.	[func]		marcin
+	b10-dhcp4, b10-dhcp6: Implemented selection of the interfaces
+	that server listens on, using Configuration Manager. It is
+	possible to specify interface names explicitly or use asterisk
+	to specify that server should listen on all available interfaces.
+	Sockets are reopened according to the new configuration as
+	soon as it is committed.
+	(Trac #1555, git f48a3bff3fbbd15584d788a264d5966154394f04)
+
+643.	[bug]		muks
+	When running some unittests as root that depended on insufficient
+	file permissions, the tests used to fail because the root user
+	could still access such files. Such tests are now skipped when
+	they are run as the root user.
+	(Trac #3056, git 92ebabdbcf6168666b03d7f7fbb31f899be39322)
+
+642.	[func]		tomek
+	Added initial set of hooks (pk6_receive, subnet6_select,
+	lease6_select, pkt6_send) to the DHCPv6 server.
+	(Trac #2995, git d6de376f97313ba40fef989e4a437d184fdf70cc)
+
+641.	[func]		stephen
+	Added the hooks framework. This allows shared libraries of
+	user-written functions to be loaded at run-time and the
+	functions called during packet processing.
+	(Trac #2980, git 82c997a72890a12af135ace5b9ee100e41c5534e)
+
+640.	[func]		marcin
+	b10-dhcp-ddns: Implemented DNSClient class which implements
+	asynchronous DNS updates using UDP. The TCP and TSIG support
+	will be	implemented at later time. Nevertheless, class API
+	accomodates the use of TCP and TSIG.
+	(Trac #2977, git 5a67a8982baa1fd6b796c063eeb13850c633702c)
+
+639.	[bug]		muks
+	Added workaround for build failure on Fedora 19 between GCC 4.8.x
+	and boost versions less than 1.54. Fedora 19 currently ships
+	boost-1.53.
+	(Trac #3039, git 4ef6830ed357ceb859ebb3e5e821a064bd8797bb)
+
+638.	[bug]*		naokikambe
+	Per-zone statistics counters are distinguished by zone class,
+	e.g. IN, CH, and HS. A class name is added onto a zone name in
+	structure of per-zone statistics.
+	(Trac #2884, git c0153581c3533ef045a92e68e0464aab00947cbb)
+
+637.	[func]		[tmark]
+	Added initial implementation of NameChangeRequest,
+	which embodies DNS update requests sent to DHCP-DDNS
+	by its clients.
+	(trac3007 git f33bdd59c6a8c8ea883f11578b463277d01c2b70)
+
+636.	[func]		[tmark]
+	Added the initial implementation of configuration parsing for
+	DCHP-DDNS.
+	(Trac #2957, git c04fb71fa44c2a458aac57ae54eeb1711c017a49)
+
+635.	[func]		marcin
+	b10-dhcp-ddns: Implemented DNS Update message construction.
+	(Trac #2796, git eac5e751473e238dee1ebf16491634a1fbea25e2)
+
 634.	[bug]		muks
 	When processing DDNS updates, we now check the zone more
 	thoroughly with the received zone data updates to check if it is
diff --git a/configure.ac b/configure.ac
index 8a0118e..3a94efa 100644
--- a/configure.ac
+++ b/configure.ac
@@ -430,6 +430,7 @@ AC_SUBST(B10_CXXFLAGS)
 AC_SEARCH_LIBS(inet_pton, [nsl])
 AC_SEARCH_LIBS(recvfrom, [socket])
 AC_SEARCH_LIBS(nanosleep, [rt])
+AC_SEARCH_LIBS(dlsym, [dl])
 
 # Checks for header files.
 
@@ -469,6 +470,17 @@ AM_COND_IF([OS_BSD], [AC_DEFINE([OS_BSD], [1], [Running on BSD?])])
 AM_CONDITIONAL(OS_SOLARIS, test $OS_TYPE = Solaris)
 AM_COND_IF([OS_SOLARIS], [AC_DEFINE([OS_SOLARIS], [1], [Running on Solaris?])])
 
+# Deal with variants
+AM_CONDITIONAL(OS_FREEBSD, test $system = FreeBSD)
+AM_COND_IF([OS_FREEBSD], [AC_DEFINE([OS_FREEBSD], [1], [Running on FreeBSD?])])
+AM_CONDITIONAL(OS_NETBSD, test $system = NetBSD)
+AM_COND_IF([OS_NETBSD], [AC_DEFINE([OS_NETBSD], [1], [Running on NetBSD?])])
+AM_CONDITIONAL(OS_OPENBSD, test $system = OpenBSD)
+AM_COND_IF([OS_OPENBSD], [AC_DEFINE([OS_OPENBSD], [1], [Running on OpenBSD?])])
+AM_CONDITIONAL(OS_OSX, test $system = Darwin)
+AM_COND_IF([OS_OSX], [AC_DEFINE([OS_OSX], [1], [Running on OSX?])])
+
+
 AC_MSG_CHECKING(for sa_len in struct sockaddr)
 AC_TRY_COMPILE([
 #include <sys/types.h>
@@ -871,10 +883,14 @@ LIBS=$LIBS_SAVED
 AX_BOOST_FOR_BIND10
 # Boost offset_ptr is required in one library and not optional right now, so
 # we unconditionally fail here if it doesn't work.
-if test "$BOOST_OFFSET_PTR_FAILURE" = "yes"; then
+if test "$BOOST_OFFSET_PTR_WOULDFAIL" = "yes"; then
     AC_MSG_ERROR([Failed to compile a required header file.  Try upgrading Boost to 1.44 or higher (when using clang++) or specifying --without-werror.  See the ChangeLog entry for Trac no. 2147 for more details.])
 fi
 
+if test "$BOOST_STATIC_ASSERT_WOULDFAIL" = "yes" -a X"$werror_ok" = X1; then
+    AC_MSG_ERROR([Failed to use Boost static assertions. Try upgrading Boost to 1.54 or higher (when using GCC 4.8) or specifying --without-werror.  See trac ticket no. 3039 for more details.])
+fi
+
 # There's a known bug in FreeBSD ports for Boost that would trigger a false
 # warning in build with g++ and -Werror (we exclude clang++ explicitly to
 # avoid unexpected false positives).
@@ -1313,6 +1329,8 @@ AC_CONFIG_FILES([Makefile
                  src/lib/datasrc/tests/memory/testdata/Makefile
                  src/lib/xfr/Makefile
                  src/lib/xfr/tests/Makefile
+                 src/lib/hooks/Makefile
+                 src/lib/hooks/tests/Makefile
                  src/lib/log/Makefile
                  src/lib/log/interprocess/Makefile
                  src/lib/log/interprocess/tests/Makefile
@@ -1363,6 +1381,8 @@ AC_OUTPUT([doc/version.ent
            src/bin/dbutil/run_dbutil.sh
            src/bin/dbutil/tests/dbutil_test.sh
            src/bin/ddns/ddns.py
+           src/bin/dhcp6/tests/marker_file.h
+           src/bin/dhcp6/tests/test_libraries.h
            src/bin/xfrin/tests/xfrin_test
            src/bin/xfrin/xfrin.py
            src/bin/xfrin/run_b10-xfrin.sh
@@ -1405,8 +1425,10 @@ AC_OUTPUT([doc/version.ent
            src/bin/dhcp4/spec_config.h.pre
            src/bin/dhcp6/spec_config.h.pre
            src/bin/d2/spec_config.h.pre
+           src/bin/d2/tests/test_data_files_config.h
            src/bin/tests/process_rename_test.py
            src/lib/config/tests/data_def_unittests_config.h
+           src/lib/dhcpsrv/tests/test_libraries.h
            src/lib/python/isc/config/tests/config_test
            src/lib/python/isc/cc/tests/cc_test
            src/lib/python/isc/notify/tests/notify_out_test
@@ -1418,6 +1440,8 @@ AC_OUTPUT([doc/version.ent
            src/lib/cc/session_config.h.pre
            src/lib/cc/tests/session_unittests_config.h
            src/lib/datasrc/datasrc_config.h.pre
+           src/lib/hooks/tests/marker_file.h
+           src/lib/hooks/tests/test_libraries.h
            src/lib/log/tests/console_test.sh
            src/lib/log/tests/destination_test.sh
            src/lib/log/tests/init_logger_test.sh
diff --git a/doc/Doxyfile b/doc/Doxyfile
index bcb9285..57c6ce1 100644
--- a/doc/Doxyfile
+++ b/doc/Doxyfile
@@ -679,6 +679,7 @@ INPUT                  = ../src/lib/exceptions \
                          ../src/lib/cache \
                          ../src/lib/server_common/ \
                          ../src/bin/sockcreator/ \
+                         ../src/lib/hooks/ \
                          ../src/lib/util/ \
                          ../src/lib/util/io/ \
                          ../src/lib/util/threads/ \
diff --git a/doc/design/datasrc/auth-local.txt b/doc/design/datasrc/auth-local.txt
index 79d7399..afe8ad8 100644
--- a/doc/design/datasrc/auth-local.txt
+++ b/doc/design/datasrc/auth-local.txt
@@ -1,26 +1,28 @@
 @startuml
 
-participant auth as "b10-auth"
+participant auth as ":b10-auth"
 [-> auth: new/initial config\n(datasrc cfg)
 activate auth
 
-participant list as "Configurable\nClientList"
+participant list as ":Configurable\nClientList"
 create list
 auth -> list: <<construct>>
 
 auth -> list: configure(cfg)
 activate list
 
-create CacheConfig
-list -> CacheConfig: <<construct>> (cfg)
+participant cache_config as ":CacheConfig"
+create cache_config
+list -> cache_config: <<construct>> (cfg)
 
-participant zt_segment as "ZoneTable\nSegment\n(Local)"
+participant zt_segment as ":ZoneTable\nSegment\n(Local)"
 create zt_segment
 list -> zt_segment: <<construct>>
 activate zt_segment
 
-create ZoneTable
-zt_segment -> ZoneTable: <<construct>>
+participant zone_table as ":ZoneTable"
+create zone_table
+zt_segment -> zone_table: <<construct>>
 
 deactivate zt_segment
 
@@ -30,47 +32,50 @@ note over zt_segment: Local segments are\nalways writable
 zt_segment --> list: true
 deactivate zt_segment
 
-loop for each zone in CacheConfig
-list -> CacheConfig: getLoadAction()
-activate CacheConfig
+loop for each zone in cache_config
+list -> cache_config: getLoadAction()
+activate cache_config
 
-create LoadAction
-CacheConfig -> LoadAction: <<construct>>
+participant la1 as "la1:LoadAction"
+create la1
+cache_config -> la1: <<construct>>
 
-participant LoadAction.2
+participant la2 as "la2:LoadAction"
 
-CacheConfig --> list : LoadAction
+cache_config --> list : la1
 
-deactivate CacheConfig
+deactivate cache_config
 
-create ZoneWriter
-list -> ZoneWriter: <<construct>> (load_action)
+participant w1 as "w1:ZoneWriter"
+create w1
+list -> w1: <<construct>> (la1)
 
-participant ZoneWriter.2
+participant w2 as "w2:ZoneWriter"
 
-list -> ZoneWriter: load()
-activate ZoneWriter
-ZoneWriter -> LoadAction: (funcall)
-activate LoadAction
+list -> w1: load()
+activate w1
+w1 -> la1: (funcall)
+activate la1
 
-create ZoneData
-LoadAction -> ZoneData: <<construct>> via helpers
+participant zd1 as "zd1:ZoneData"
+create zd1
+la1 -> zd1: <<construct>> via helpers
 
-participant ZoneData.2
+participant zd2 as "zd2:ZoneData"
 
-LoadAction --> ZoneWriter: ZoneData
-deactivate LoadAction
-deactivate ZoneWriter
+la1 --> w1: zd1
+deactivate la1
+deactivate w1
 
-list -> ZoneWriter: install()
-activate ZoneWriter
+list -> w1: install()
+activate w1
 
-ZoneWriter -> ZoneTable: addZone(ZoneData)
-activate ZoneTable
-ZoneTable --> ZoneWriter: NULL (no old data)
-deactivate ZoneTable
+w1 -> zone_table: addZone(zd1)
+activate zone_table
+zone_table --> w1: NULL (no old data)
+deactivate zone_table
 
-deactivate ZoneWriter
+deactivate w1
 
 end
 
@@ -82,55 +87,55 @@ deactivate auth
 [-> auth: reload\n(zonename)
 activate auth
 
-auth -> list: getCachedZoneWriter\n(zone_name)
+auth -> list: getCachedw1\n(zone_name)
 activate list
 
-list -> CacheConfig: getLoadAction()
-activate CacheConfig
+list -> cache_config: getLoadAction()
+activate cache_config
 
-create LoadAction.2
-CacheConfig -> LoadAction.2: <<construct>>
+create la2
+cache_config -> la2: <<construct>>
 
-CacheConfig --> list : LoadAction.2
+cache_config --> list : la2
 
-deactivate CacheConfig
+deactivate cache_config
 
-create ZoneWriter.2
-list -> ZoneWriter.2: <<construct>> (load_action)
+create w2
+list -> w2: <<construct>> (la2)
 
-list --> auth: ZoneWriter.2
+list --> auth: w2
 
 deactivate list
 
 
-auth -> ZoneWriter.2: load()
-activate ZoneWriter.2
-ZoneWriter.2 -> LoadAction.2: (funcall)
-activate LoadAction.2
+auth -> w2: load()
+activate w2
+w2 -> la2: (funcall)
+activate la2
 
-create ZoneData.2
-LoadAction.2 -> ZoneData.2: <<construct>> via helpers
+create zd2
+la2 -> zd2: <<construct>> via helpers
 
-LoadAction.2 --> ZoneWriter.2: ZoneData.2
-deactivate LoadAction.2
-deactivate ZoneWriter.2
+la2 --> w2: zd2
+deactivate la2
+deactivate w2
 
-auth -> ZoneWriter.2: install()
-activate ZoneWriter.2
+auth -> w2: install()
+activate w2
 
-ZoneWriter.2 -> ZoneTable: addZone(ZoneData.2)
-activate ZoneTable
-ZoneTable --> ZoneWriter.2: ZoneData (old data)
-deactivate ZoneTable
+w2 -> zone_table: addZone(zd2)
+activate zone_table
+zone_table --> w2: zd1 (old data)
+deactivate zone_table
 
-deactivate ZoneWriter.2
+deactivate w2
 
-auth -> ZoneWriter.2: cleanup()
-activate ZoneWriter.2
+auth -> w2: cleanup()
+activate w2
 
-ZoneWriter.2 -> ZoneData: <<destroy>>
-destroy ZoneData
-deactivate ZoneWriter.2
+w2 -> zd1: <<destroy>>
+destroy zd1
+deactivate w2
 
 deactivate auth
 
diff --git a/doc/design/datasrc/auth-mapped.txt b/doc/design/datasrc/auth-mapped.txt
index d656220..b5b1a39 100644
--- a/doc/design/datasrc/auth-mapped.txt
+++ b/doc/design/datasrc/auth-mapped.txt
@@ -1,20 +1,21 @@
 @startuml
 
-participant auth as "b10-auth"
+participant auth as ":b10-auth"
 [-> auth: new/initial config\n(datasrc cfg)
 activate auth
 
-participant list as "Configurable\nClientList"
+participant list as ":Configurable\nClientList"
 create list
 auth -> list: <<construct>>
 
 auth -> list: configure(cfg)
 activate list
 
+participant CacheConfig as ":CacheConfig"
 create CacheConfig
 list -> CacheConfig: <<construct>> (cfg)
 
-participant zt_segment as "ZoneTable\nSegment\n(Mapped)"
+participant zt_segment as ":ZoneTable\nSegment\n(Mapped)"
 create zt_segment
 list -> zt_segment: <<construct>>
 
@@ -46,7 +47,7 @@ activate list
 list -> zt_segment: reset\n(READ_ONLY,\nsegmentparam)
 activate zt_segment
 
-participant segment as "Memory\nSegment\n(Mapped)"
+participant segment as "seg1:Memory\nSegment\n(Mapped)"
 create segment
 zt_segment -> segment: <<construct>>
 
@@ -68,9 +69,9 @@ activate zt_segment
 zt_segment -> segment: <<destroy>>
 destroy segment
 
-participant segment.2 as "Memory\nSegment\n(Mapped)\n2"
-create segment.2
-zt_segment -> segment.2: <<construct>>
+participant segment2 as "seg2:Memory\nSegment\n(Mapped)"
+create segment2
+zt_segment -> segment2: <<construct>>
 
 deactivate zt_segment
 deactivate list
diff --git a/doc/design/datasrc/memmgr-mapped-init.txt b/doc/design/datasrc/memmgr-mapped-init.txt
index 52ca783..9daea13 100644
--- a/doc/design/datasrc/memmgr-mapped-init.txt
+++ b/doc/design/datasrc/memmgr-mapped-init.txt
@@ -1,20 +1,21 @@
 @startuml
 
-participant memmgr as "memmgr"
+participant memmgr as ":b10-memmgr"
 [-> memmgr: new/initial config\n(datasrc cfg)
 activate memmgr
 
-participant list as "Configurable\nClientList"
+participant list as ":Configurable\nClientList"
 create list
 memmgr -> list: <<construct>>
 
 memmgr -> list: configure(cfg)
 activate list
 
+participant CacheConfig as ":CacheConfig"
 create CacheConfig
 list -> CacheConfig: <<construct>> (cfg)
 
-participant zt_segment as "ZoneTable\nSegment\n(Mapped)"
+participant zt_segment as ":ZoneTable\nSegment\n(Mapped)"
 create zt_segment
 list -> zt_segment: <<construct>>
 
@@ -39,12 +40,13 @@ activate list
 list -> zt_segment: reset\n(READ_WRITE,\nsegmentparam)
 activate zt_segment
 
-participant segment as "Memory\nSegment\n(Mapped)"
+participant segment as "seg1:Memory\nSegment\n(Mapped)"
 create segment
 zt_segment -> segment: <<construct>>
 
-participant segment.2 as "Memory\nSegment\n(Mapped)\n2"
+participant segment.2 as "seg2:Memory\nSegment\n(Mapped)"
 
+participant ZoneTable as ":ZoneTable"
 create ZoneTable
 zt_segment -> ZoneTable: <<construct>>
 
@@ -65,17 +67,19 @@ activate list
 list -> CacheConfig: getLoadAction()
 activate CacheConfig
 
+participant LoadAction as "la:LoadAction"
 create LoadAction
 CacheConfig -> LoadAction: <<construct>>
 
-CacheConfig --> list : LoadAction
+CacheConfig --> list : la
 
 deactivate CacheConfig
 
+participant ZoneWriter as "zw:ZoneWriter"
 create ZoneWriter
-list -> ZoneWriter: <<construct>> (load_action)
+list -> ZoneWriter: <<construct>> (la)
 
-list --> memmgr: ZoneWriter
+list --> memmgr: zw
 
 deactivate list
 
@@ -85,17 +89,18 @@ activate ZoneWriter
 ZoneWriter -> LoadAction: (funcall)
 activate LoadAction
 
+participant ZoneData as "zd:ZoneData"
 create ZoneData
 LoadAction -> ZoneData: <<construct>> via helpers
 
-LoadAction --> ZoneWriter: ZoneData
+LoadAction --> ZoneWriter: zd
 deactivate LoadAction
 deactivate ZoneWriter
 
 memmgr -> ZoneWriter: install()
 activate ZoneWriter
 
-ZoneWriter -> ZoneTable: addZone(ZoneData)
+ZoneWriter -> ZoneTable: addZone(zd)
 activate ZoneTable
 ZoneTable --> ZoneWriter: NULL (no old data)
 deactivate ZoneTable
diff --git a/doc/design/datasrc/memmgr-mapped-reload.txt b/doc/design/datasrc/memmgr-mapped-reload.txt
index 83dbf46..676e961 100644
--- a/doc/design/datasrc/memmgr-mapped-reload.txt
+++ b/doc/design/datasrc/memmgr-mapped-reload.txt
@@ -1,18 +1,18 @@
 @startuml
 
-participant memmgr as "memmgr"
+participant memmgr as ":b10-memmgr"
 [-> memmgr: reload\n(zonename)
 activate memmgr
 
-participant list as "Configurable\nClientList"
+participant list as ":Configurable\nClientList"
 memmgr -> list: getCachedZoneWriter\n(zone_name)
 activate list
 
-participant CacheConfig
+participant CacheConfig as ":CacheConfig"
 
-participant zt_segment as "ZoneTable\nSegment\n(Mapped)"
-participant segment as "Memory\nSegment\n(Mapped)"
-participant segment.2 as "Memory\nSegment\n(Mapped)\n2"
+participant zt_segment as ":ZoneTable\nSegment\n(Mapped)"
+participant segment as "existing:Memory\nSegment\n(Mapped)"
+participant segment2 as "new:Memory\nSegment\n(Mapped)"
 
 list -> zt_segment: isWritable()
 activate zt_segment
@@ -22,17 +22,18 @@ deactivate zt_segment
 list -> CacheConfig: getLoadAction()
 activate CacheConfig
 
-participant ZoneTable
-participant ZoneWriter
+participant ZoneTable as ":ZoneTable"
+participant ZoneWriter as "zw:ZoneWriter"
 
+participant LoadAction as "la:LoadAction"
 create LoadAction
 CacheConfig -> LoadAction: <<construct>>
-CacheConfig --> list: LoadAction
+CacheConfig --> list: la
 deactivate CacheConfig
 
 create ZoneWriter
-list -> ZoneWriter: <<construct>> (LoadAction)
-list --> memmgr: ZoneWriter
+list -> ZoneWriter: <<construct>> (la)
+list --> memmgr: zw
 deactivate list
 
 memmgr -> ZoneWriter: load()
@@ -40,21 +41,22 @@ activate ZoneWriter
 ZoneWriter -> LoadAction: (funcall)
 activate LoadAction
 
-participant ZoneData
+participant ZoneData as "zd_existing\n:ZoneData"
+participant ZoneData2 as "zd_new\n:ZoneData"
 
-create ZoneData.2
-LoadAction -> ZoneData.2: <<construct>> via helpers
+create ZoneData2
+LoadAction -> ZoneData2: <<construct>> via helpers
 
-LoadAction --> ZoneWriter: ZoneData.2
+LoadAction --> ZoneWriter: zd_new
 deactivate LoadAction
 deactivate ZoneWriter
 
 memmgr -> ZoneWriter: install()
 activate ZoneWriter
 
-ZoneWriter -> ZoneTable: addZone(ZoneData.2)
+ZoneWriter -> ZoneTable: addZone(zd_new)
 activate ZoneTable
-ZoneTable --> ZoneWriter: ZoneData (old data)
+ZoneTable --> ZoneWriter: zd_existing (old data)
 deactivate ZoneTable
 
 deactivate ZoneWriter
@@ -77,8 +79,8 @@ activate zt_segment
 
 zt_segment -> segment: <<destroy>>
 destroy segment
-create segment.2
-zt_segment -> segment.2: <<construct>>
+create segment2
+zt_segment -> segment2: <<construct>>
 
 deactivate zt_segment
 deactivate list
diff --git a/doc/design/datasrc/overview.txt b/doc/design/datasrc/overview.txt
index 49aae9d..4ee971c 100644
--- a/doc/design/datasrc/overview.txt
+++ b/doc/design/datasrc/overview.txt
@@ -4,65 +4,65 @@ hide members
 
 note "Automatic placement of classes\ndoesn't look good. This diagram\nhas to be improved." as n1
 
-Auth "1" *-d-> "*" ConfigurableClientList
-Auth -d-> DataSourceClient
-Auth -d-> ZoneWriter
-Auth -d-> ZoneTableAccessor
-Auth -d-> DataSourceStatus
-Auth -d-> ZoneTableIterator
-
-ConfigurableClientList "1" *-d-> "*" DataSourceInfo
+Auth "1" *--> "*" ConfigurableClientList
+Auth --> DataSourceClient
+Auth --> ZoneWriter
+Auth --> ZoneTableAccessor
+Auth --> DataSourceStatus
+Auth --> ZoneTableIterator
+
+ConfigurableClientList "1" *--> "*" DataSourceInfo
 ConfigurableClientList ..> ZoneTableSegment : <<reset>>
-ConfigurableClientList ..d-> DataSourceStatus : <<create>>
+ConfigurableClientList ..> DataSourceStatus : <<create>>
 ConfigurableClientList ..> ZoneWriter : <<create>>
 ConfigurableClientList ..> ZoneTableAccessor : <<create>>
 
-DataSourceInfo "1" *-u-> "*" DataSourceClient
-DataSourceInfo "1" *-r-> "*" CacheConfig
-DataSourceInfo "1" *-d-> "*" ZoneTableSegment
+DataSourceInfo "1" *--> "*" DataSourceClient
+DataSourceInfo "1" *--> "*" CacheConfig
+DataSourceInfo "1" *--> "*" ZoneTableSegment
 
 ZoneTableAccessor ..> ZoneTableIterator : <<create>>
 
-ZoneTableAccessorCache -> CacheConfig
+ZoneTableAccessorCache --> CacheConfig
 ZoneTableAccessorCache ..> ZoneTableIteratorCache : <<create>>
-ZoneTableAccessorCache -u-o ZoneTableAccessor
+ZoneTableAccessorCache --o ZoneTableAccessor
 
-ZoneTableIteratorCache -u-o ZoneTableIterator
-ZoneTableIteratorCache -u-> CacheConfig
+ZoneTableIteratorCache --o ZoneTableIterator
+ZoneTableIteratorCache --> CacheConfig
 
-ZoneWriter -d-> ZoneTableSegment
+ZoneWriter --> ZoneTableSegment
 ZoneWriter ..> ZoneData : add/replace
 
-ZoneTableSegment "1" *-r-> "1" ZoneTableHeader
-ZoneTableSegment "1" *-d-> "1" MemorySegment
+ZoneTableSegment "1" *--> "1" ZoneTableHeader
+ZoneTableSegment "1" *--> "1" MemorySegment
 
 CacheConfig ..> LoadAction
 
 LoadAction ..> ZoneData : create
-LoadAction *-> ZoneDataLoader
+LoadAction *--> ZoneDataLoader
 
-ZoneDataLoader -> ZoneData
-ZoneDataLoader *-> ZoneDataUpdater
-ZoneDataLoader -> MemorySegment
+ZoneDataLoader --> ZoneData
+ZoneDataLoader *--> ZoneDataUpdater
+ZoneDataLoader --> MemorySegment
 
-ZoneDataUpdater -> ZoneData
+ZoneDataUpdater --> ZoneData
 ZoneDataUpdater ..> RdataSet : create
 ZoneDataUpdater ..> RdataSet : add
 
-ZoneTableHeader "1" *-d-> "1" ZoneTable
-ZoneTable "1" *-d-> "1" ZoneData
-ZoneData "1" *-d-> "1" RdataSet
+ZoneTableHeader "1" *--> "1" ZoneTable
+ZoneTable "1" *--> "1" ZoneData
+ZoneData "1" *--> "1" RdataSet
 
-loadFromFile -d-o LoadAction
-IteratorLoader -d-o LoadAction
+LoadFromFile --o LoadAction
+IteratorLoader --o LoadAction
 
-MemorySegmentMapped -d-o MemorySegment
-MemorySegmentLocal -d-o MemorySegment
+MemorySegmentMapped --o MemorySegment
+MemorySegmentLocal --o MemorySegment
 
-ZoneTableSegmentMapped -d-o ZoneTableSegment
-ZoneTableSegmentLocal -d-o ZoneTableSegment
+ZoneTableSegmentMapped --o ZoneTableSegment
+ZoneTableSegmentLocal --o ZoneTableSegment
 
-ZoneTableSegmentMapped *-d-> MemorySegmentMapped
-ZoneTableSegmentLocal *-d-> MemorySegmentLocal
+ZoneTableSegmentMapped *--> MemorySegmentMapped
+ZoneTableSegmentLocal *--> MemorySegmentLocal
 
 @enduml
diff --git a/doc/devel/mainpage.dox b/doc/devel/mainpage.dox
index 92f86eb..9b8d2a1 100644
--- a/doc/devel/mainpage.dox
+++ b/doc/devel/mainpage.dox
@@ -1,11 +1,33 @@
+// Copyright (C) 2012-2013  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.
+
 /**
- *
  * @mainpage BIND10 Developer's Guide
  *
  * Welcome to BIND10 Developer's Guide. This documentation is addressed
- * at existing and prospecting developers and programmers, who would like
- * to gain insight into internal workings of BIND 10. It could also be useful
- * for existing and prospective contributors.
+ * at existing and prospecting developers and programmers and provides
+ * information needed to both extend and maintain BIND 10.
+ *
+ * If you wish to write "hook" code - code that is loaded by BIND 10 at
+ * run-time and modifies its behavior you should read the section
+ * @ref hookDevelopersGuide.
+ *
+ * BIND 10 maintanenace information is divided into a number of sections
+ * depending on focus.  DNS-specific issues are covered in the
+ * @ref dnsMaintenanceGuide while information on DHCP-specific topics can
+ * be found in the @ref dhcpMaintenanceGuide.  General BIND 10 topics, not
+ * specific to any protocol, are discussed in @ref miscellaneousTopics.
  *
  * If you are a user or system administrator, rather than software engineer,
  * you should read <a href="http://bind10.isc.org/docs/bind10-guide.html">BIND10
@@ -13,21 +35,27 @@
  *
  * Regardless of your field of expertise, you are encouraged to visit
  * <a href="http://bind10.isc.org/">BIND10 webpage (http://bind10.isc.org)</a>
+ * @section hooksFramework Hooks Framework
+ * - @subpage hooksComponentDeveloperGuide
+ * - @subpage dhcpv4Hooks
+ * - @subpage dhcpv6Hooks
  *
- * @section DNS
+ * @section dnsMaintenanceGuide DNS Maintenance Guide
  * - Authoritative DNS (todo)
  * - Recursive resolver (todo)
  * - @subpage DataScrubbing
  *
- * @section DHCP
+ * @section dhcpMaintenanceGuide DHCP Maintenance Guide
  * - @subpage dhcp4
  *   - @subpage dhcpv4Session
  *   - @subpage dhcpv4ConfigParser
  *   - @subpage dhcpv4ConfigInherit
+ *   - @subpage dhcpv4Other
  * - @subpage dhcp6
  *   - @subpage dhcpv6Session
  *   - @subpage dhcpv6ConfigParser
  *   - @subpage dhcpv6ConfigInherit
+ *   - @subpage dhcpv6Other
  * - @subpage libdhcp
  *   - @subpage libdhcpIntro
  *   - @subpage libdhcpRelay
@@ -39,7 +67,7 @@
  * - @subpage dhcpDatabaseBackends
  * - @subpage perfdhcpInternals
  *
- * @section misc Miscellaneous topics
+ * @section miscellaneousTopics Miscellaneous topics
  * - @subpage LoggingApi
  *   - @subpage LoggingApiOverview
  *   - @subpage LoggingApiLoggerNames
@@ -47,7 +75,10 @@
  * - @subpage SocketSessionUtility
  * - <a href="./doxygen-error.log">Documentation warnings and errors</a>
  *
- * @todo: Move this logo to the right (and possibly up). Not sure what
- * is the best way to do it in Doxygen, without using CSS hacks.
  * @image html isc-logo.png
  */
+/*
+ * @todo: Move the logo to the right (and possibly up). Not sure what
+ * is the best way to do it in Doxygen, without using CSS hacks.
+ */
+
diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml
index 88f2536..432fb8d 100644
--- a/doc/guide/bind10-guide.xml
+++ b/doc/guide/bind10-guide.xml
@@ -3599,7 +3599,7 @@ $</screen>
         will be available. It will look similar to this:
 <screen>
 > <userinput>config show Dhcp4</userinput>
-Dhcp4/interface/	list	(default)
+Dhcp4/interfaces/	list	(default)
 Dhcp4/renew-timer	1000	integer	(default)
 Dhcp4/rebind-timer	2000	integer	(default)
 Dhcp4/valid-lifetime	4000	integer	(default)
@@ -3686,6 +3686,60 @@ Dhcp4/subnet4	[]	list	(default)
       </note>
       </section>
 
+      <section id="dhcp4-interface-selection">
+      <title>Interface selection</title>
+      <para>
+        When DHCPv4 server starts up, by default it will listen to the DHCP
+        traffic and respond to it on all interfaces detected during startup.
+        However, in many cases it is desired to configure the server to listen and
+        respond on selected interfaces only. The sample commands in this section
+        show how to make interface selection using bindctl.
+      </para>
+      <para>
+        The default configuration can be presented with the following command:
+        <screen>
+> <userinput>config show Dhcp4/interfaces</userinput>
+<userinput>Dhcp4/interfaces[0] "*" string</userinput></screen>
+        An asterisk sign plays a role of the wildcard and means "listen on all interfaces".
+      </para>
+      <para>
+        In order to override the default configuration, the existing entry can be replaced
+        with the actual interface name:
+        <screen>
+> <userinput>config set Dhcp4/interfaces[0] eth1</userinput>
+> <userinput>config commit</userinput></screen>
+        Other interface names can be added on one-by-one basis:
+        <screen>
+> <userinput>config add Dhcp4/interfaces eth2</userinput>
+> <userinput>config commit</userinput></screen>
+        Configuration will now contain two interfaces which can be presented as follows:
+        <screen>
+> <userinput>config show Dhcp4/interfaces</userinput>
+<userinput>Dhcp4/interfaces[0]	"eth1"	string</userinput>
+<userinput>Dhcp4/interfaces[1]	"eth2"	string</userinput></screen>
+        When configuration gets committed, the server will start to listen on
+        eth1 and eth2 interfaces only.
+      </para>
+      <para>
+        It is possible to use wildcard interface name (asterisk) concurrently with explicit
+        interface names:
+        <screen>
+> <userinput>config add Dhcp4/interfaces *</userinput>
+> <userinput>config commit</userinput></screen>
+        This will result in the following configuration:
+        <screen>
+> <userinput>config show Dhcp4/interfaces</userinput>
+<userinput>Dhcp4/interfaces[0]	"eth1"	string</userinput>
+<userinput>Dhcp4/interfaces[1]	"eth2"	string</userinput>
+<userinput>Dhcp4/interfaces[2]	"*"	string</userinput></screen>
+        The presence of the wildcard name implies that server will listen on all interfaces.
+        In order to fall back to the previous configuration when server listens on eth1 and eth2:
+        <screen>
+> <userinput>config remove Dhcp4/interfaces[2]</userinput>
+> <userinput>config commit</userinput></screen>
+      </para>
+      </section>
+
       <section id="dhcp4-address-config">
       <title>Configuration of Address Pools</title>
       <para>
@@ -4366,7 +4420,7 @@ Dhcp4/renew-timer	1000	integer	(default)
         will be available. It will look similar to this:
 <screen>
 > <userinput>config show Dhcp6</userinput>
-Dhcp6/interface/	list	(default)
+Dhcp6/interfaces/	list	(default)
 Dhcp6/renew-timer	1000	integer	(default)
 Dhcp6/rebind-timer	2000	integer	(default)
 Dhcp6/preferred-lifetime	3000	integer	(default)
@@ -4459,6 +4513,59 @@ Dhcp6/subnet6/	list
       </note>
       </section>
 
+      <section id="dhcp6-interface-selection">
+      <title>Interface selection</title>
+      <para>
+        When DHCPv6 server starts up, by default it will listen to the DHCP
+        traffic and respond to it on all interfaces detected during startup.
+        However, in many cases it is desired to configure the server to listen and
+        respond on selected interfaces only. The sample commands in this section
+        show how to make interface selection using bindctl.
+      </para>
+      <para>
+        The default configuration can be presented with the following command:
+        <screen>
+> <userinput>config show Dhcp6/interfaces</userinput>
+<userinput>Dhcp6/interfaces[0] "*" string</userinput></screen>
+        An asterisk sign plays a role of the wildcard and means "listen on all interfaces".
+      </para>
+      <para>
+        In order to override the default configuration, the existing entry can be replaced
+        with the actual interface name:
+        <screen>
+> <userinput>config set Dhcp6/interfaces[0] eth1</userinput>
+> <userinput>config commit</userinput></screen>
+        Other interface names can be added on one-by-one basis:
+        <screen>
+> <userinput>config add Dhcp6/interfaces eth2</userinput>
+> <userinput>config commit</userinput></screen>
+        Configuration will now contain two interfaces which can be presented as follows:
+        <screen>
+> <userinput>config show Dhcp6/interfaces</userinput>
+<userinput>Dhcp6/interfaces[0]	"eth1"	string</userinput>
+<userinput>Dhcp6/interfaces[1]	"eth2"	string</userinput></screen>
+        When configuration gets committed, the server will start to listen on
+        eth1 and eth2 interfaces only.
+      </para>
+      <para>
+        It is possible to use wildcard interface name (asterisk) concurrently with explicit
+        interface names:
+        <screen>
+> <userinput>config add Dhcp6/interfaces *</userinput>
+> <userinput>config commit</userinput></screen>
+        This will result in the following configuration:
+        <screen>
+> <userinput>config show Dhcp6/interfaces</userinput>
+<userinput>Dhcp6/interfaces[0]	"eth1"	string</userinput>
+<userinput>Dhcp6/interfaces[1]	"eth2"	string</userinput>
+<userinput>Dhcp6/interfaces[2]	"*"	string</userinput></screen>
+        The presence of the wildcard name implies that server will listen on all interfaces.
+        In order to fall back to the previous configuration when server listens on eth1 and eth2:
+        <screen>
+> <userinput>config remove Dhcp6/interfaces[2]</userinput>
+> <userinput>config commit</userinput></screen>
+      </para>
+    </section>
 
     <section>
       <title>Subnet and Address Pool</title>
diff --git a/m4macros/ax_boost_for_bind10.m4 b/m4macros/ax_boost_for_bind10.m4
index 577af6b..3a71337 100644
--- a/m4macros/ax_boost_for_bind10.m4
+++ b/m4macros/ax_boost_for_bind10.m4
@@ -28,6 +28,8 @@ dnl                               cause build failure; otherwise set to "no"
 dnl   BOOST_MAPPED_FILE_CXXFLAG set to the compiler flag that would need to
 dnl                             compile managed_mapped_file (can be empty).
 dnl                             It is of no use if "WOULDFAIL" is yes.
+dnl   BOOST_STATIC_ASSERT_WOULDFAIL set to "yes" if BOOST_STATIC_ASSERT would
+dnl                                 cause build error; otherwise set to "no"
 
 AC_DEFUN([AX_BOOST_FOR_BIND10], [
 AC_LANG_SAVE
@@ -146,6 +148,18 @@ if test $BOOST_MAPPED_FILE_WOULDFAIL = yes; then
   AC_MSG_RESULT(no)
 fi
 
+# BOOST_STATIC_ASSERT in versions below Boost 1.54.0 is known to result
+# in warnings with GCC 4.8.  Detect it.
+AC_MSG_CHECKING([BOOST_STATIC_ASSERT compiles])
+AC_TRY_COMPILE([
+#include <boost/static_assert.hpp>
+void testfn(void) { BOOST_STATIC_ASSERT(true); }
+],,
+[AC_MSG_RESULT(yes)
+ BOOST_STATIC_ASSERT_WOULDFAIL=no],
+[AC_MSG_RESULT(no)
+ BOOST_STATIC_ASSERT_WOULDFAIL=yes])
+
 CXXFLAGS="$CXXFLAGS_SAVED"
 
 AC_SUBST(BOOST_INCLUDES)
diff --git a/src/bin/auth/auth_messages.mes b/src/bin/auth/auth_messages.mes
index f0e9e46..e38468a 100644
--- a/src/bin/auth/auth_messages.mes
+++ b/src/bin/auth/auth_messages.mes
@@ -151,6 +151,10 @@ A separate thread for maintaining data source clients has been started.
 % AUTH_DATASRC_CLIENTS_BUILDER_STOPPED data source builder thread stopped
 The separate thread for maintaining data source clients has been stopped.
 
+% AUTH_DATASRC_CLIENTS_BUILDER_WAKE_ERR failed to wake up main thread: %1
+A low-level error happened when trying to send data to the main thread to wake
+it up. Terminating to prevent inconsistent state and possiblu hang ups.
+
 % AUTH_DATASRC_CLIENTS_SHUTDOWN_ERROR error on waiting for data source builder thread: %1
 This indicates that the separate thread for maintaining data source
 clients had been terminated due to an uncaught exception, and the
diff --git a/src/bin/auth/auth_srv.cc b/src/bin/auth/auth_srv.cc
index ee0306f..27779a9 100644
--- a/src/bin/auth/auth_srv.cc
+++ b/src/bin/auth/auth_srv.cc
@@ -319,6 +319,7 @@ AuthSrvImpl::AuthSrvImpl(AbstractXfroutClient& xfrout_client,
     xfrin_session_(NULL),
     counters_(),
     keyring_(NULL),
+    datasrc_clients_mgr_(io_service_),
     ddns_base_forwarder_(ddns_forwarder),
     ddns_forwarder_(NULL),
     xfrout_connected_(false),
diff --git a/src/bin/auth/datasrc_clients_mgr.h b/src/bin/auth/datasrc_clients_mgr.h
index 11475e2..a2d3d1f 100644
--- a/src/bin/auth/datasrc_clients_mgr.h
+++ b/src/bin/auth/datasrc_clients_mgr.h
@@ -29,6 +29,9 @@
 #include <datasrc/client_list.h>
 #include <datasrc/memory/zone_writer.h>
 
+#include <asiolink/io_service.h>
+#include <asiolink/local_socket.h>
+
 #include <auth/auth_log.h>
 #include <auth/datasrc_config.h>
 
@@ -36,11 +39,16 @@
 #include <boost/bind.hpp>
 #include <boost/shared_ptr.hpp>
 #include <boost/noncopyable.hpp>
+#include <boost/function.hpp>
+#include <boost/foreach.hpp>
 
 #include <exception>
 #include <cassert>
+#include <cerrno>
 #include <list>
 #include <utility>
+#include <sys/types.h>
+#include <sys/socket.h>
 
 namespace isc {
 namespace auth {
@@ -77,13 +85,40 @@ enum CommandID {
     NUM_COMMANDS
 };
 
+/// \brief Callback to be called when the command is completed.
+typedef boost::function<void ()> FinishedCallback;
+
 /// \brief The data type passed from DataSrcClientsMgr to
-/// DataSrcClientsBuilder.
+///     DataSrcClientsBuilder.
 ///
-/// The first element of the pair is the command ID, and the second element
-/// is its argument.  If the command doesn't take an argument it should be
-/// a null pointer.
-typedef std::pair<CommandID, data::ConstElementPtr> Command;
+/// This just holds the data items together, no logic or protection
+/// is present here.
+struct Command {
+    /// \brief Constructor
+    ///
+    /// It just initializes the member variables of the same names
+    /// as the parameters.
+    Command(CommandID id, const data::ConstElementPtr& params,
+            const FinishedCallback& callback) :
+        id(id),
+        params(params),
+        callback(callback)
+    {}
+    /// \brief The command to execute
+    CommandID id;
+    /// \brief Argument of the command.
+    ///
+    /// If the command takes no argument, it should be null pointer.
+    ///
+    /// This may be a null pointer if the command takes no parameters.
+    data::ConstElementPtr params;
+    /// \brief A callback to be called once the command finishes.
+    ///
+    /// This may be an empty boost::function. In such case, no callback
+    /// will be called after completion.
+    FinishedCallback callback;
+};
+
 } // namespace datasrc_clientmgr_internal
 
 /// \brief Frontend to the manager object for data source clients.
@@ -113,6 +148,24 @@ private:
                      boost::shared_ptr<datasrc::ConfigurableClientList> >
     ClientListsMap;
 
+    class FDGuard : boost::noncopyable {
+    public:
+        FDGuard(DataSrcClientsMgrBase *mgr) :
+            mgr_(mgr)
+        {}
+        ~FDGuard() {
+            if (mgr_->read_fd_ != -1) {
+                close(mgr_->read_fd_);
+            }
+            if (mgr_->write_fd_ != -1) {
+                close(mgr_->write_fd_);
+            }
+        }
+    private:
+        DataSrcClientsMgrBase* mgr_;
+    };
+    friend class FDGuard;
+
 public:
     /// \brief Thread-safe accessor to the data source client lists.
     ///
@@ -176,12 +229,20 @@ public:
     ///
     /// \throw std::bad_alloc internal memory allocation failure.
     /// \throw isc::Unexpected general unexpected system errors.
-    DataSrcClientsMgrBase() :
+    DataSrcClientsMgrBase(asiolink::IOService& service) :
         clients_map_(new ClientListsMap),
-        builder_(&command_queue_, &cond_, &queue_mutex_, &clients_map_,
-                 &map_mutex_),
-        builder_thread_(boost::bind(&BuilderType::run, &builder_))
-    {}
+        fd_guard_(new FDGuard(this)),
+        read_fd_(-1), write_fd_(-1),
+        builder_(&command_queue_, &callback_queue_, &cond_, &queue_mutex_,
+                 &clients_map_, &map_mutex_, createFds()),
+        builder_thread_(boost::bind(&BuilderType::run, &builder_)),
+        wakeup_socket_(service, read_fd_)
+    {
+        // Schedule wakeups when callbacks are pushed.
+        wakeup_socket_.asyncRead(
+            boost::bind(&DataSrcClientsMgrBase::processCallbacks, this, _1),
+            buffer, 1);
+    }
 
     /// \brief The destructor.
     ///
@@ -220,6 +281,7 @@ public:
                       AUTH_DATASRC_CLIENTS_SHUTDOWN_UNEXPECTED_ERROR);
         }
 
+        processCallbacks(); // Any leftover callbacks
         cleanup();              // see below
     }
 
@@ -234,11 +296,18 @@ public:
     /// \brief std::bad_alloc
     ///
     /// \param config_arg The new data source configuration.  Must not be NULL.
-    void reconfigure(data::ConstElementPtr config_arg) {
+    /// \param callback Called once the reconfigure command completes. It is
+    ///     called in the main thread (not in the work one). It should be
+    ///     exceptionless.
+    void reconfigure(const data::ConstElementPtr& config_arg,
+                     const datasrc_clientmgr_internal::FinishedCallback&
+                     callback = datasrc_clientmgr_internal::FinishedCallback())
+    {
         if (!config_arg) {
             isc_throw(InvalidParameter, "Invalid null config argument");
         }
-        sendCommand(datasrc_clientmgr_internal::RECONFIGURE, config_arg);
+        sendCommand(datasrc_clientmgr_internal::RECONFIGURE, config_arg,
+                    callback);
         reconfigureHook();      // for test's customization
     }
 
@@ -257,12 +326,18 @@ public:
     /// \param args Element argument that should be a map of the form
     /// { "class": "IN", "origin": "example.com" }
     /// (but class is optional and will default to IN)
+    /// \param callback Called once the loadZone command completes. It
+    ///     is called in the main thread, not in the work thread. It should
+    ///     be exceptionless.
     ///
     /// \exception CommandError if the args value is null, or not in
     ///                                 the expected format, or contains
     ///                                 a bad origin or class string
     void
-    loadZone(data::ConstElementPtr args) {
+    loadZone(const data::ConstElementPtr& args,
+             const datasrc_clientmgr_internal::FinishedCallback& callback =
+             datasrc_clientmgr_internal::FinishedCallback())
+    {
         if (!args) {
             isc_throw(CommandError, "loadZone argument empty");
         }
@@ -303,7 +378,7 @@ public:
         // implement it would be to factor out the code from
         // the start of doLoadZone(), and call it here too
 
-        sendCommand(datasrc_clientmgr_internal::LOADZONE, args);
+        sendCommand(datasrc_clientmgr_internal::LOADZONE, args, callback);
     }
 
 private:
@@ -317,30 +392,79 @@ private:
     void reconfigureHook() {}
 
     void sendCommand(datasrc_clientmgr_internal::CommandID command,
-                     data::ConstElementPtr arg)
+                     const data::ConstElementPtr& arg,
+                     const datasrc_clientmgr_internal::FinishedCallback&
+                     callback = datasrc_clientmgr_internal::FinishedCallback())
     {
         // The lock will be held until the end of this method.  Only
         // push_back has to be protected, but we can avoid having an extra
         // block this way.
         typename MutexType::Locker locker(queue_mutex_);
         command_queue_.push_back(
-            datasrc_clientmgr_internal::Command(command, arg));
+            datasrc_clientmgr_internal::Command(command, arg, callback));
         cond_.signal();
     }
 
+    int createFds() {
+        int fds[2];
+        int result = socketpair(AF_LOCAL, SOCK_STREAM, 0, fds);
+        if (result != 0) {
+            isc_throw(Unexpected, "Can't create socket pair: " <<
+                      strerror(errno));
+        }
+        read_fd_ = fds[0];
+        write_fd_ = fds[1];
+        return write_fd_;
+    }
+
+    void processCallbacks(const std::string& error = std::string()) {
+        // Schedule the next read.
+        wakeup_socket_.asyncRead(
+            boost::bind(&DataSrcClientsMgrBase::processCallbacks, this, _1),
+            buffer, 1);
+        if (!error.empty()) {
+            // Generally, there should be no errors (as we are the other end
+            // as well), but check just in case.
+            isc_throw(Unexpected, error);
+        }
+
+        // Steal the callbacks into local copy.
+        std::list<datasrc_clientmgr_internal::FinishedCallback> queue;
+        {
+            typename MutexType::Locker locker(queue_mutex_);
+            queue.swap(callback_queue_);
+        }
+
+        // Execute the callbacks
+        BOOST_FOREACH(const datasrc_clientmgr_internal::FinishedCallback&
+                      callback, queue) {
+            callback();
+        }
+    }
+
     //
     // The following are shared with the builder.
     //
     // The list is used as a one-way queue: back-in, front-out
     std::list<datasrc_clientmgr_internal::Command> command_queue_;
+    // Similar to above, for the callbacks that are ready to be called.
+    // While the command queue is for sending commands from the main thread
+    // to the work thread, this one is for the other direction. Protected
+    // by the same mutex (queue_mutex_).
+    std::list<datasrc_clientmgr_internal::FinishedCallback> callback_queue_;
     CondVarType cond_;          // condition variable for queue operations
     MutexType queue_mutex_;     // mutex to protect the queue
     datasrc::ClientListMapPtr clients_map_;
                                 // map of actual data source client objects
+    boost::scoped_ptr<FDGuard> fd_guard_; // A guard to close the fds.
+    int read_fd_, write_fd_;    // Descriptors for wakeup
     MutexType map_mutex_;       // mutex to protect the clients map
 
     BuilderType builder_;
     ThreadType builder_thread_; // for safety this should be placed last
+    isc::asiolink::LocalSocket wakeup_socket_; // For integration of read_fd_
+                                               // to the asio loop
+    char buffer[1];   // Buffer for the wakeup socket.
 };
 
 namespace datasrc_clientmgr_internal {
@@ -385,12 +509,15 @@ public:
     ///
     /// \throw None
     DataSrcClientsBuilderBase(std::list<Command>* command_queue,
+                              std::list<FinishedCallback>* callback_queue,
                               CondVarType* cond, MutexType* queue_mutex,
                               datasrc::ClientListMapPtr* clients_map,
-                              MutexType* map_mutex
+                              MutexType* map_mutex,
+                              int wake_fd
         ) :
-        command_queue_(command_queue), cond_(cond), queue_mutex_(queue_mutex),
-        clients_map_(clients_map), map_mutex_(map_mutex)
+        command_queue_(command_queue), callback_queue_(callback_queue),
+        cond_(cond), queue_mutex_(queue_mutex),
+        clients_map_(clients_map), map_mutex_(map_mutex), wake_fd_(wake_fd)
     {}
 
     /// \brief The main loop.
@@ -457,10 +584,12 @@ private:
 
     // The following are shared with the manager
     std::list<Command>* command_queue_;
+    std::list<FinishedCallback> *callback_queue_;
     CondVarType* cond_;
     MutexType* queue_mutex_;
     datasrc::ClientListMapPtr* clients_map_;
     MutexType* map_mutex_;
+    int wake_fd_;
 };
 
 // Shortcut typedef for normal use
@@ -494,6 +623,31 @@ DataSrcClientsBuilderBase<MutexType, CondVarType>::run() {
                               AUTH_DATASRC_CLIENTS_BUILDER_COMMAND_ERROR).
                         arg(e.what());
                 }
+                if (current_commands.front().callback) {
+                    // Lock the queue
+                    typename MutexType::Locker locker(*queue_mutex_);
+                    callback_queue_->
+                        push_back(current_commands.front().callback);
+                    // Wake up the other end. If it would block, there are data
+                    // and it'll wake anyway.
+                    int result = send(wake_fd_, "w", 1, MSG_DONTWAIT);
+                    if (result == -1 &&
+                        (errno != EWOULDBLOCK && errno != EAGAIN)) {
+                        // Note: the strerror might not be thread safe, as
+                        // subsequent call to it might change the returned
+                        // string. But that is unlikely and strerror_r is
+                        // not portable and we are going to terminate anyway,
+                        // so that's better than nothing.
+                        //
+                        // Also, this error handler is not tested. It should
+                        // be generally impossible to happen, so it is hard
+                        // to trigger in controlled way.
+                        LOG_FATAL(auth_logger,
+                                  AUTH_DATASRC_CLIENTS_BUILDER_WAKE_ERR).
+                            arg(strerror(errno));
+                        std::terminate();
+                    }
+                }
                 current_commands.pop_front();
             }
         }
@@ -515,7 +669,7 @@ bool
 DataSrcClientsBuilderBase<MutexType, CondVarType>::handleCommand(
     const Command& command)
 {
-    const CommandID cid = command.first;
+    const CommandID cid = command.id;
     if (cid >= NUM_COMMANDS) {
         // This shouldn't happen except for a bug within this file.
         isc_throw(Unexpected, "internal bug: invalid command, ID: " << cid);
@@ -526,12 +680,12 @@ DataSrcClientsBuilderBase<MutexType, CondVarType>::handleCommand(
     };
     LOG_DEBUG(auth_logger, DBGLVL_TRACE_BASIC,
               AUTH_DATASRC_CLIENTS_BUILDER_COMMAND).arg(command_desc.at(cid));
-    switch (command.first) {
+    switch (command.id) {
     case RECONFIGURE:
-        doReconfigure(command.second);
+        doReconfigure(command.params);
         break;
     case LOADZONE:
-        doLoadZone(command.second);
+        doLoadZone(command.params);
         break;
     case SHUTDOWN:
         return (false);
diff --git a/src/bin/auth/tests/datasrc_clients_builder_unittest.cc b/src/bin/auth/tests/datasrc_clients_builder_unittest.cc
index 8aa223b..48559ad 100644
--- a/src/bin/auth/tests/datasrc_clients_builder_unittest.cc
+++ b/src/bin/auth/tests/datasrc_clients_builder_unittest.cc
@@ -36,9 +36,13 @@
 
 #include <boost/function.hpp>
 
+#include <sys/types.h>
+#include <sys/socket.h>
+
 #include <cstdlib>
 #include <string>
 #include <sstream>
+#include <cerrno>
 
 using isc::data::ConstElementPtr;
 using namespace isc::dns;
@@ -54,17 +58,24 @@ protected:
     DataSrcClientsBuilderTest() :
         clients_map(new std::map<RRClass,
                     boost::shared_ptr<ConfigurableClientList> >),
-        builder(&command_queue, &cond, &queue_mutex, &clients_map, &map_mutex),
+        write_end(-1), read_end(-1),
+        builder(&command_queue, &callback_queue, &cond, &queue_mutex,
+                &clients_map, &map_mutex, generateSockets()),
         cond(command_queue, delayed_command_queue), rrclass(RRClass::IN()),
-        shutdown_cmd(SHUTDOWN, ConstElementPtr()),
-        noop_cmd(NOOP, ConstElementPtr())
+        shutdown_cmd(SHUTDOWN, ConstElementPtr(), FinishedCallback()),
+        noop_cmd(NOOP, ConstElementPtr(), FinishedCallback())
     {}
+    ~ DataSrcClientsBuilderTest() {
+
+    }
 
     void configureZones();      // used for loadzone related tests
 
     ClientListMapPtr clients_map; // configured clients
     std::list<Command> command_queue; // test command queue
     std::list<Command> delayed_command_queue; // commands available after wait
+    std::list<FinishedCallback> callback_queue; // Callbacks from commands
+    int write_end, read_end;
     TestDataSrcClientsBuilder builder;
     TestCondVar cond;
     TestMutex queue_mutex;
@@ -72,6 +83,15 @@ protected:
     const RRClass rrclass;
     const Command shutdown_cmd;
     const Command noop_cmd;
+private:
+    int generateSockets() {
+        int pair[2];
+        int result = socketpair(AF_LOCAL, SOCK_STREAM, 0, pair);
+        assert(result == 0);
+        write_end = pair[0];
+        read_end = pair[1];
+        return write_end;
+    }
 };
 
 TEST_F(DataSrcClientsBuilderTest, runSingleCommand) {
@@ -82,6 +102,45 @@ TEST_F(DataSrcClientsBuilderTest, runSingleCommand) {
     EXPECT_EQ(0, cond.wait_count); // no wait because command queue is not empty
     EXPECT_EQ(1, queue_mutex.lock_count);
     EXPECT_EQ(1, queue_mutex.unlock_count);
+    // No callback scheduled, none called.
+    EXPECT_TRUE(callback_queue.empty());
+    // Not woken up.
+    char c;
+    int result = recv(read_end, &c, 1, MSG_DONTWAIT);
+    EXPECT_EQ(-1, result);
+    EXPECT_TRUE(errno == EAGAIN || errno == EWOULDBLOCK);
+}
+
+// Just to have a valid function callback to pass
+void emptyCallsback() {}
+
+// Check a command finished callback is passed
+TEST_F(DataSrcClientsBuilderTest, commandFinished) {
+    command_queue.push_back(Command(SHUTDOWN, ConstElementPtr(),
+                                    emptyCallsback));
+    builder.run();
+    EXPECT_EQ(0, cond.wait_count); // no wait because command queue is not empty
+    // Once for picking up data, once for putting the callback there
+    EXPECT_EQ(2, queue_mutex.lock_count);
+    EXPECT_EQ(2, queue_mutex.unlock_count);
+    // There's one callback in the queue
+    ASSERT_EQ(1, callback_queue.size());
+    // Not using EXPECT_EQ, as that produces warning in printing out the result
+    EXPECT_TRUE(emptyCallsback == callback_queue.front());
+    // And we are woken up.
+    char c;
+    int result = recv(read_end, &c, 1, MSG_DONTWAIT);
+    EXPECT_EQ(1, result);
+}
+
+// Test that low-level errors with the synchronization socket
+// (an unexpected condition) is detected and program aborted.
+TEST_F(DataSrcClientsBuilderTest, finishedCrash) {
+    command_queue.push_back(Command(SHUTDOWN, ConstElementPtr(),
+                                    emptyCallsback));
+    // Break the socket
+    close(write_end);
+    EXPECT_DEATH_IF_SUPPORTED({builder.run();}, "");
 }
 
 TEST_F(DataSrcClientsBuilderTest, runMultiCommands) {
@@ -138,7 +197,7 @@ TEST_F(DataSrcClientsBuilderTest, reconfigure) {
     // the error handling
 
     // A command structure we'll modify to send different commands
-    Command reconfig_cmd(RECONFIGURE, ConstElementPtr());
+    Command reconfig_cmd(RECONFIGURE, ConstElementPtr(), FinishedCallback());
 
     // Initially, no clients should be there
     EXPECT_TRUE(clients_map->empty());
@@ -166,7 +225,7 @@ TEST_F(DataSrcClientsBuilderTest, reconfigure) {
         "}"
     );
 
-    reconfig_cmd.second = good_config;
+    reconfig_cmd.params = good_config;
     EXPECT_TRUE(builder.handleCommand(reconfig_cmd));
     EXPECT_EQ(1, clients_map->size());
     EXPECT_EQ(1, map_mutex.lock_count);
@@ -177,7 +236,7 @@ TEST_F(DataSrcClientsBuilderTest, reconfigure) {
     // If a 'bad' command argument got here, the config validation should
     // have failed already, but still, the handler should return true,
     // and the clients_map should not be updated.
-    reconfig_cmd.second = Element::create("{ \"foo\": \"bar\" }");
+    reconfig_cmd.params = Element::create("{ \"foo\": \"bar\" }");
     EXPECT_TRUE(builder.handleCommand(reconfig_cmd));
     EXPECT_EQ(working_config_clients, clients_map);
     // Building failed, so map mutex should not have been locked again
@@ -185,7 +244,7 @@ TEST_F(DataSrcClientsBuilderTest, reconfigure) {
 
     // The same for a configuration that has bad data for the type it
     // specifies
-    reconfig_cmd.second = bad_config;
+    reconfig_cmd.params = bad_config;
     builder.handleCommand(reconfig_cmd);
     EXPECT_TRUE(builder.handleCommand(reconfig_cmd));
     EXPECT_EQ(working_config_clients, clients_map);
@@ -194,21 +253,21 @@ TEST_F(DataSrcClientsBuilderTest, reconfigure) {
 
     // The same goes for an empty parameter (it should at least be
     // an empty map)
-    reconfig_cmd.second = ConstElementPtr();
+    reconfig_cmd.params = ConstElementPtr();
     EXPECT_TRUE(builder.handleCommand(reconfig_cmd));
     EXPECT_EQ(working_config_clients, clients_map);
     EXPECT_EQ(1, map_mutex.lock_count);
 
     // Reconfigure again with the same good clients, the result should
     // be a different map than the original, but not an empty one.
-    reconfig_cmd.second = good_config;
+    reconfig_cmd.params = good_config;
     EXPECT_TRUE(builder.handleCommand(reconfig_cmd));
     EXPECT_NE(working_config_clients, clients_map);
     EXPECT_EQ(1, clients_map->size());
     EXPECT_EQ(2, map_mutex.lock_count);
 
     // And finally, try an empty config to disable all datasource clients
-    reconfig_cmd.second = Element::createMap();
+    reconfig_cmd.params = Element::createMap();
     EXPECT_TRUE(builder.handleCommand(reconfig_cmd));
     EXPECT_EQ(0, clients_map->size());
     EXPECT_EQ(3, map_mutex.lock_count);
@@ -224,7 +283,8 @@ TEST_F(DataSrcClientsBuilderTest, shutdown) {
 TEST_F(DataSrcClientsBuilderTest, badCommand) {
     // out-of-range command ID
     EXPECT_THROW(builder.handleCommand(Command(NUM_COMMANDS,
-                                               ConstElementPtr())),
+                                               ConstElementPtr(),
+                                               FinishedCallback())),
                  isc::Unexpected);
 }
 
@@ -308,7 +368,8 @@ TEST_F(DataSrcClientsBuilderTest, loadZone) {
 
     const Command loadzone_cmd(LOADZONE, Element::fromJSON(
                                    "{\"class\": \"IN\","
-                                   " \"origin\": \"test1.example\"}"));
+                                   " \"origin\": \"test1.example\"}"),
+                               FinishedCallback());
     EXPECT_TRUE(builder.handleCommand(loadzone_cmd));
 
     // loadZone involves two critical sections: one for getting the zone
@@ -369,7 +430,8 @@ TEST_F(DataSrcClientsBuilderTest,
     // Now send the command to reload it
     const Command loadzone_cmd(LOADZONE, Element::fromJSON(
                                    "{\"class\": \"IN\","
-                                   " \"origin\": \"example.org\"}"));
+                                   " \"origin\": \"example.org\"}"),
+                               FinishedCallback());
     EXPECT_TRUE(builder.handleCommand(loadzone_cmd));
     // And now it should be present too.
     EXPECT_EQ(ZoneFinder::SUCCESS,
@@ -380,7 +442,8 @@ TEST_F(DataSrcClientsBuilderTest,
     // An error case: the zone has no configuration. (note .com here)
     const Command nozone_cmd(LOADZONE, Element::fromJSON(
                                  "{\"class\": \"IN\","
-                                 " \"origin\": \"example.com\"}"));
+                                 " \"origin\": \"example.com\"}"),
+                             FinishedCallback());
     EXPECT_THROW(builder.handleCommand(nozone_cmd),
                  TestDataSrcClientsBuilder::InternalCommandError);
     // The previous zone is not hurt in any way
@@ -403,7 +466,8 @@ TEST_F(DataSrcClientsBuilderTest,
     builder.handleCommand(
                      Command(LOADZONE, Element::fromJSON(
                                  "{\"class\": \"IN\","
-                                 " \"origin\": \"example.org\"}")));
+                                 " \"origin\": \"example.org\"}"),
+                             FinishedCallback()));
     // Only one mutex was needed because there was no actual reload.
     EXPECT_EQ(orig_lock_count + 1, map_mutex.lock_count);
     EXPECT_EQ(orig_unlock_count + 1, map_mutex.unlock_count);
@@ -421,7 +485,8 @@ TEST_F(DataSrcClientsBuilderTest,
         builder.handleCommand(
             Command(LOADZONE, Element::fromJSON(
                         "{\"class\": \"IN\","
-                        " \"origin\": \"nosuchzone.example\"}"))),
+                        " \"origin\": \"nosuchzone.example\"}"),
+                    FinishedCallback())),
         TestDataSrcClientsBuilder::InternalCommandError);
 
     // basically impossible case: in-memory cache is completely disabled.
@@ -441,7 +506,8 @@ TEST_F(DataSrcClientsBuilderTest,
     EXPECT_THROW(builder.handleCommand(
                      Command(LOADZONE, Element::fromJSON(
                                  "{\"class\": \"IN\","
-                                 " \"origin\": \"example.org\"}"))),
+                                 " \"origin\": \"example.org\"}"),
+                             FinishedCallback())),
                  TestDataSrcClientsBuilder::InternalCommandError);
 }
 
@@ -454,13 +520,21 @@ TEST_F(DataSrcClientsBuilderTest, loadBrokenZone) {
     // there's an error in the new zone file.  reload will be rejected.
     const Command loadzone_cmd(LOADZONE, Element::fromJSON(
                                    "{\"class\": \"IN\","
-                                   " \"origin\": \"test1.example\"}"));
+                                   " \"origin\": \"test1.example\"}"),
+                               FinishedCallback());
     EXPECT_THROW(builder.handleCommand(loadzone_cmd),
                  TestDataSrcClientsBuilder::InternalCommandError);
     zoneChecks(clients_map, rrclass);     // zone shouldn't be replaced
 }
 
 TEST_F(DataSrcClientsBuilderTest, loadUnreadableZone) {
+    // If the test is run as the root user, it will fail as insufficient
+    // permissions will not stop the root user from using a file.
+    if (getuid() == 0) {
+        std::cerr << "Skipping test as it's run as the root user" << std::endl;
+        return;
+    }
+
     configureZones();
 
     // install the zone file as unreadable
@@ -469,7 +543,8 @@ TEST_F(DataSrcClientsBuilderTest, loadUnreadableZone) {
                              TEST_DATA_BUILDDIR "/test1.zone.copied"));
     const Command loadzone_cmd(LOADZONE, Element::fromJSON(
                                    "{\"class\": \"IN\","
-                                   " \"origin\": \"test1.example\"}"));
+                                   " \"origin\": \"test1.example\"}"),
+                               FinishedCallback());
     EXPECT_THROW(builder.handleCommand(loadzone_cmd),
                  TestDataSrcClientsBuilder::InternalCommandError);
     zoneChecks(clients_map, rrclass);     // zone shouldn't be replaced
@@ -482,7 +557,8 @@ TEST_F(DataSrcClientsBuilderTest, loadZoneWithoutDataSrc) {
                      Command(LOADZONE,
                              Element::fromJSON(
                                  "{\"class\": \"IN\", "
-                                 " \"origin\": \"test1.example\"}"))),
+                                 " \"origin\": \"test1.example\"}"),
+                             FinishedCallback())),
                  TestDataSrcClientsBuilder::InternalCommandError);
 }
 
@@ -492,7 +568,8 @@ TEST_F(DataSrcClientsBuilderTest, loadZoneInvalidParams) {
     if (!isc::util::unittests::runningOnValgrind()) {
         // null arg: this causes assertion failure
         EXPECT_DEATH_IF_SUPPORTED({
-                builder.handleCommand(Command(LOADZONE, ElementPtr()));
+                builder.handleCommand(Command(LOADZONE, ElementPtr(),
+                                              FinishedCallback()));
             }, "");
     }
 
@@ -501,7 +578,8 @@ TEST_F(DataSrcClientsBuilderTest, loadZoneInvalidParams) {
                      Command(LOADZONE,
                              Element::fromJSON(
                                  "{\"origin\": \"test1.example\","
-                                 " \"class\": \"no_such_class\"}"))),
+                                 " \"class\": \"no_such_class\"}"),
+                             FinishedCallback())),
                  InvalidRRClass);
 
     // not a string
@@ -509,7 +587,8 @@ TEST_F(DataSrcClientsBuilderTest, loadZoneInvalidParams) {
                      Command(LOADZONE,
                              Element::fromJSON(
                                  "{\"origin\": \"test1.example\","
-                                 " \"class\": 1}"))),
+                                 " \"class\": 1}"),
+                             FinishedCallback())),
                  isc::data::TypeError);
 
     // class or origin is missing: result in assertion failure
@@ -517,7 +596,8 @@ TEST_F(DataSrcClientsBuilderTest, loadZoneInvalidParams) {
         EXPECT_DEATH_IF_SUPPORTED({
                 builder.handleCommand(Command(LOADZONE,
                                               Element::fromJSON(
-                                                  "{\"class\": \"IN\"}")));
+                                                  "{\"class\": \"IN\"}"),
+                                              FinishedCallback()));
             }, "");
     }
 
@@ -525,12 +605,14 @@ TEST_F(DataSrcClientsBuilderTest, loadZoneInvalidParams) {
     EXPECT_THROW(builder.handleCommand(
                      Command(LOADZONE,
                              Element::fromJSON(
-                                 "{\"class\": \"IN\", \"origin\": \"...\"}"))),
+                                 "{\"class\": \"IN\", \"origin\": \"...\"}"),
+                             FinishedCallback())),
                  EmptyLabel);
     EXPECT_THROW(builder.handleCommand(
                      Command(LOADZONE,
                              Element::fromJSON(
-                                 "{\"origin\": 10, \"class\": 1}"))),
+                                 "{\"origin\": 10, \"class\": 1}"),
+                             FinishedCallback())),
                  isc::data::TypeError);
 }
 
@@ -561,7 +643,8 @@ TEST_F(DataSrcClientsBuilderTest,
                      Command(LOADZONE,
                              Element::fromJSON(
                                  "{\"origin\": \"test1.example\","
-                                 " \"class\": \"IN\"}"))),
+                                 " \"class\": \"IN\"}"),
+                             FinishedCallback())),
                  TestDataSrcClientsBuilder::InternalCommandError);
 }
 
diff --git a/src/bin/auth/tests/datasrc_clients_mgr_unittest.cc b/src/bin/auth/tests/datasrc_clients_mgr_unittest.cc
index c37ef11..1bf8101 100644
--- a/src/bin/auth/tests/datasrc_clients_mgr_unittest.cc
+++ b/src/bin/auth/tests/datasrc_clients_mgr_unittest.cc
@@ -38,13 +38,13 @@ void
 shutdownCheck() {
     // Check for common points on shutdown.  The manager should have acquired
     // the lock, put a SHUTDOWN command to the queue, and should have signaled
-    // the builder.
-    EXPECT_EQ(1, FakeDataSrcClientsBuilder::queue_mutex->lock_count);
+    // the builder. It should check again for the callback queue, with the lock
+    EXPECT_EQ(2, FakeDataSrcClientsBuilder::queue_mutex->lock_count);
     EXPECT_EQ(1, FakeDataSrcClientsBuilder::cond->signal_count);
     EXPECT_EQ(1, FakeDataSrcClientsBuilder::command_queue->size());
     const Command& cmd = FakeDataSrcClientsBuilder::command_queue->front();
-    EXPECT_EQ(SHUTDOWN, cmd.first);
-    EXPECT_FALSE(cmd.second);   // no argument
+    EXPECT_EQ(SHUTDOWN, cmd.id);
+    EXPECT_FALSE(cmd.params);   // no argument
 
     // Finally, the manager should wait for the thread to terminate.
     EXPECT_TRUE(FakeDataSrcClientsBuilder::thread_waited);
@@ -130,8 +130,8 @@ TEST(DataSrcClientsMgrTest, reconfigure) {
     // touch or refer to the map, so it shouldn't acquire the map lock.
     checkSharedMembers(1, 1, 0, 0, 1, 1);
     const Command& cmd1 = FakeDataSrcClientsBuilder::command_queue->front();
-    EXPECT_EQ(RECONFIGURE, cmd1.first);
-    EXPECT_EQ(reconfigure_arg, cmd1.second);
+    EXPECT_EQ(RECONFIGURE, cmd1.id);
+    EXPECT_EQ(reconfigure_arg, cmd1.params);
 
     // Non-null, but semantically invalid argument.  The manager doesn't do
     // this check, so it should result in the same effect.
@@ -140,8 +140,8 @@ TEST(DataSrcClientsMgrTest, reconfigure) {
     mgr.reconfigure(reconfigure_arg);
     checkSharedMembers(2, 2, 0, 0, 2, 1);
     const Command& cmd2 = FakeDataSrcClientsBuilder::command_queue->front();
-    EXPECT_EQ(RECONFIGURE, cmd2.first);
-    EXPECT_EQ(reconfigure_arg, cmd2.second);
+    EXPECT_EQ(RECONFIGURE, cmd2.id);
+    EXPECT_EQ(reconfigure_arg, cmd2.params);
 
     // Passing NULL argument is immediately rejected
     EXPECT_THROW(mgr.reconfigure(ConstElementPtr()), isc::InvalidParameter);
@@ -245,10 +245,52 @@ TEST(DataSrcClientsMgrTest, reload) {
     EXPECT_EQ(3, FakeDataSrcClientsBuilder::command_queue->size());
 }
 
+void
+callback(bool* called, int *tag_target, int tag_value) {
+    *called = true;
+    *tag_target = tag_value;
+}
+
+// Test we can wake up the main thread by writing to the file descriptor and
+// that the callbacks are executed and removed when woken up.
+TEST(DataSrcClientsMgrTest, wakeup) {
+    bool called = false;
+    int tag;
+    {
+        TestDataSrcClientsMgr mgr;
+        // There's some real file descriptor (or something that looks so)
+        ASSERT_GT(FakeDataSrcClientsBuilder::wakeup_fd, 0);
+        // Push a callback in and wake the manager
+        FakeDataSrcClientsBuilder::callback_queue->
+            push_back(boost::bind(callback, &called, &tag, 1));
+        EXPECT_EQ(1, write(FakeDataSrcClientsBuilder::wakeup_fd, "w", 1));
+        mgr.run_one();
+        EXPECT_TRUE(called);
+        EXPECT_EQ(1, tag);
+        EXPECT_TRUE(FakeDataSrcClientsBuilder::callback_queue->empty());
+
+        called = false;
+        // If we wake up and don't push anything, it doesn't break.
+        EXPECT_EQ(1, write(FakeDataSrcClientsBuilder::wakeup_fd, "w", 1));
+        mgr.run_one();
+        EXPECT_FALSE(called);
+
+        // When we terminate, it should process whatever is left
+        // of the callbacks. So push and terminate (and don't directly
+        // wake).
+        FakeDataSrcClientsBuilder::callback_queue->
+            push_back(boost::bind(callback, &called, &tag, 2));
+    }
+    EXPECT_TRUE(called);
+    EXPECT_EQ(2, tag);
+    EXPECT_TRUE(FakeDataSrcClientsBuilder::callback_queue->empty());
+}
+
 TEST(DataSrcClientsMgrTest, realThread) {
     // Using the non-test definition with a real thread.  Just checking
     // no disruption happens.
-    DataSrcClientsMgr mgr;
+    isc::asiolink::IOService service;
+    DataSrcClientsMgr mgr(service);
 }
 
 } // unnamed namespace
diff --git a/src/bin/auth/tests/test_datasrc_clients_mgr.cc b/src/bin/auth/tests/test_datasrc_clients_mgr.cc
index 82937c0..80bc97c 100644
--- a/src/bin/auth/tests/test_datasrc_clients_mgr.cc
+++ b/src/bin/auth/tests/test_datasrc_clients_mgr.cc
@@ -26,7 +26,9 @@ namespace datasrc_clientmgr_internal {
 // Define static DataSrcClientsBuilder member variables.
 bool FakeDataSrcClientsBuilder::started = false;
 std::list<Command>* FakeDataSrcClientsBuilder::command_queue = NULL;
+std::list<FinishedCallback>* FakeDataSrcClientsBuilder::callback_queue = NULL;
 std::list<Command> FakeDataSrcClientsBuilder::command_queue_copy;
+std::list<FinishedCallback> FakeDataSrcClientsBuilder::callback_queue_copy;
 TestCondVar* FakeDataSrcClientsBuilder::cond = NULL;
 TestCondVar FakeDataSrcClientsBuilder::cond_copy;
 TestMutex* FakeDataSrcClientsBuilder::queue_mutex = NULL;
@@ -38,6 +40,7 @@ bool FakeDataSrcClientsBuilder::thread_waited = false;
 FakeDataSrcClientsBuilder::ExceptionFromWait
 FakeDataSrcClientsBuilder::thread_throw_on_wait =
     FakeDataSrcClientsBuilder::NOTHROW;
+int FakeDataSrcClientsBuilder::wakeup_fd = -1;
 
 template<>
 void
@@ -58,7 +61,7 @@ TestDataSrcClientsBuilder::doNoop() {
 
 template<>
 void
-TestDataSrcClientsMgr::cleanup() {
+TestDataSrcClientsMgrBase::cleanup() {
     using namespace datasrc_clientmgr_internal;
     // Make copy of some of the manager's member variables and reset the
     // corresponding pointers.  The currently pointed objects are in the
@@ -73,17 +76,21 @@ TestDataSrcClientsMgr::cleanup() {
     FakeDataSrcClientsBuilder::cond_copy = cond_;
     FakeDataSrcClientsBuilder::cond =
         &FakeDataSrcClientsBuilder::cond_copy;
+    FakeDataSrcClientsBuilder::callback_queue_copy =
+        *FakeDataSrcClientsBuilder::callback_queue;
+    FakeDataSrcClientsBuilder::callback_queue =
+        &FakeDataSrcClientsBuilder::callback_queue_copy;
 }
 
 template<>
 void
-TestDataSrcClientsMgr::reconfigureHook() {
+TestDataSrcClientsMgrBase::reconfigureHook() {
     using namespace datasrc_clientmgr_internal;
 
     // Simply replace the local map, ignoring bogus config value.
-    assert(command_queue_.front().first == RECONFIGURE);
+    assert(command_queue_.front().id == RECONFIGURE);
     try {
-        clients_map_ = configureDataSource(command_queue_.front().second);
+        clients_map_ = configureDataSource(command_queue_.front().params);
     } catch (...) {}
 }
 
diff --git a/src/bin/auth/tests/test_datasrc_clients_mgr.h b/src/bin/auth/tests/test_datasrc_clients_mgr.h
index 9b1a367..34097da 100644
--- a/src/bin/auth/tests/test_datasrc_clients_mgr.h
+++ b/src/bin/auth/tests/test_datasrc_clients_mgr.h
@@ -20,6 +20,8 @@
 #include <auth/datasrc_clients_mgr.h>
 #include <datasrc/datasrc_config.h>
 
+#include <asiolink/io_service.h>
+
 #include <boost/function.hpp>
 
 #include <list>
@@ -131,15 +133,18 @@ public:
     // true iff a builder has started.
     static bool started;
 
-    // These three correspond to the resource shared with the manager.
+    // These five correspond to the resource shared with the manager.
     // xxx_copy will be set in the manager's destructor to record the
     // final state of the manager.
     static std::list<Command>* command_queue;
+    static std::list<FinishedCallback>* callback_queue;
     static TestCondVar* cond;
     static TestMutex* queue_mutex;
+    static int wakeup_fd;
     static isc::datasrc::ClientListMapPtr* clients_map;
     static TestMutex* map_mutex;
     static std::list<Command> command_queue_copy;
+    static std::list<FinishedCallback> callback_queue_copy;
     static TestCondVar cond_copy;
     static TestMutex queue_mutex_copy;
 
@@ -153,15 +158,18 @@ public:
 
     FakeDataSrcClientsBuilder(
         std::list<Command>* command_queue,
+        std::list<FinishedCallback>* callback_queue,
         TestCondVar* cond,
         TestMutex* queue_mutex,
         isc::datasrc::ClientListMapPtr* clients_map,
-        TestMutex* map_mutex)
+        TestMutex* map_mutex, int wakeup_fd)
     {
         FakeDataSrcClientsBuilder::started = false;
         FakeDataSrcClientsBuilder::command_queue = command_queue;
+        FakeDataSrcClientsBuilder::callback_queue = callback_queue;
         FakeDataSrcClientsBuilder::cond = cond;
         FakeDataSrcClientsBuilder::queue_mutex = queue_mutex;
+        FakeDataSrcClientsBuilder::wakeup_fd = wakeup_fd;
         FakeDataSrcClientsBuilder::clients_map = clients_map;
         FakeDataSrcClientsBuilder::map_mutex = map_mutex;
         FakeDataSrcClientsBuilder::thread_waited = false;
@@ -201,18 +209,30 @@ typedef DataSrcClientsMgrBase<
     datasrc_clientmgr_internal::TestThread,
     datasrc_clientmgr_internal::FakeDataSrcClientsBuilder,
     datasrc_clientmgr_internal::TestMutex,
-    datasrc_clientmgr_internal::TestCondVar> TestDataSrcClientsMgr;
+    datasrc_clientmgr_internal::TestCondVar> TestDataSrcClientsMgrBase;
 
 // A specialization of manager's "cleanup" called at the end of the
 // destructor.  We use this to record the final values of some of the class
 // member variables.
 template<>
 void
-TestDataSrcClientsMgr::cleanup();
+TestDataSrcClientsMgrBase::cleanup();
 
 template<>
 void
-TestDataSrcClientsMgr::reconfigureHook();
+TestDataSrcClientsMgrBase::reconfigureHook();
+
+// A (hackish) trick how to not require the IOService to be passed from the
+// tests. We can't create the io service as a member, because it would
+// get initialized too late.
+class TestDataSrcClientsMgr :
+    public asiolink::IOService,
+    public TestDataSrcClientsMgrBase {
+public:
+    TestDataSrcClientsMgr() :
+        TestDataSrcClientsMgrBase(*static_cast<asiolink::IOService*>(this))
+    {}
+};
 } // namespace auth
 } // namespace isc
 
diff --git a/src/bin/cmdctl/tests/b10-certgen_test.py b/src/bin/cmdctl/tests/b10-certgen_test.py
index 56630bc..8054aa1 100644
--- a/src/bin/cmdctl/tests/b10-certgen_test.py
+++ b/src/bin/cmdctl/tests/b10-certgen_test.py
@@ -200,6 +200,8 @@ class TestCertGenTool(unittest.TestCase):
         # No such file
         self.run_check(105, None, None, [self.TOOL, '-c', 'foo'])
 
+    @unittest.skipIf(os.getuid() == 0,
+                     'test cannot be run as root user')
     def test_permissions(self):
         """
         Test some combinations of correct and bad permissions.
diff --git a/src/bin/cmdctl/tests/cmdctl_test.py b/src/bin/cmdctl/tests/cmdctl_test.py
index 4a6b0e3..2dd30c8 100644
--- a/src/bin/cmdctl/tests/cmdctl_test.py
+++ b/src/bin/cmdctl/tests/cmdctl_test.py
@@ -680,11 +680,15 @@ class TestSecureHTTPServer(unittest.TestCase):
         # Just some file that we know exists
         file_name = BUILD_FILE_PATH + 'cmdctl-keyfile.pem'
         check_file(file_name)
-        with UnreadableFile(file_name):
-            self.assertRaises(CmdctlException, check_file, file_name)
         self.assertRaises(CmdctlException, check_file, '/local/not-exist')
         self.assertRaises(CmdctlException, check_file, '/')
 
+    @unittest.skipIf(os.getuid() == 0,
+                     'test cannot be run as root user')
+    def test_check_file_for_unreadable(self):
+        file_name = BUILD_FILE_PATH + 'cmdctl-keyfile.pem'
+        with UnreadableFile(file_name):
+            self.assertRaises(CmdctlException, check_file, file_name)
 
     def test_check_key_and_cert(self):
         keyfile = BUILD_FILE_PATH + 'cmdctl-keyfile.pem'
@@ -702,6 +706,15 @@ class TestSecureHTTPServer(unittest.TestCase):
         self.assertRaises(CmdctlException, self.server._check_key_and_cert,
                          '/', certfile)
 
+        # All OK (also happens to check the context code above works)
+        self.server._check_key_and_cert(keyfile, certfile)
+
+    @unittest.skipIf(os.getuid() == 0,
+                     'test cannot be run as root user')
+    def test_check_key_and_cert_for_unreadable(self):
+        keyfile = BUILD_FILE_PATH + 'cmdctl-keyfile.pem'
+        certfile = BUILD_FILE_PATH + 'cmdctl-certfile.pem'
+
         # no read permission
         with UnreadableFile(certfile):
             self.assertRaises(CmdctlException,
@@ -713,9 +726,6 @@ class TestSecureHTTPServer(unittest.TestCase):
                               self.server._check_key_and_cert,
                               keyfile, certfile)
 
-        # All OK (also happens to check the context code above works)
-        self.server._check_key_and_cert(keyfile, certfile)
-
     def test_wrap_sock_in_ssl_context(self):
         sock = socket.socket()
 
diff --git a/src/bin/d2/Makefile.am b/src/bin/d2/Makefile.am
index 7cf3019..2308505 100644
--- a/src/bin/d2/Makefile.am
+++ b/src/bin/d2/Makefile.am
@@ -48,10 +48,16 @@ pkglibexec_PROGRAMS = b10-dhcp-ddns
 
 b10_dhcp_ddns_SOURCES  = main.cc
 b10_dhcp_ddns_SOURCES += d2_log.cc d2_log.h
-b10_dhcp_ddns_SOURCES += d_process.h
 b10_dhcp_ddns_SOURCES += d2_process.cc d2_process.h
 b10_dhcp_ddns_SOURCES += d_controller.cc d_controller.h
 b10_dhcp_ddns_SOURCES += d2_controller.cc d2_controller.h
+b10_dhcp_ddns_SOURCES += d_cfg_mgr.cc d_cfg_mgr.h
+b10_dhcp_ddns_SOURCES += d2_config.cc d2_config.h
+b10_dhcp_ddns_SOURCES += d2_cfg_mgr.cc d2_cfg_mgr.h
+b10_dhcp_ddns_SOURCES += d2_update_message.cc d2_update_message.h
+b10_dhcp_ddns_SOURCES += d2_zone.cc d2_zone.h
+b10_dhcp_ddns_SOURCES += dns_client.cc dns_client.h
+b10_dhcp_ddns_SOURCES += ncr_msg.cc ncr_msg.h
 
 nodist_b10_dhcp_ddns_SOURCES = d2_messages.h d2_messages.cc
 EXTRA_DIST += d2_messages.mes
@@ -59,8 +65,13 @@ EXTRA_DIST += d2_messages.mes
 b10_dhcp_ddns_LDADD = $(top_builddir)/src/lib/log/libb10-log.la
 b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
 b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
+b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/asiodns/libb10-asiodns.la
 b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
 b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
+b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/dhcpsrv/libb10-dhcpsrv.la 
+b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/dns/libb10-dns++.la
+b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
+b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/hooks/libb10-hooks.la
 
 b10_dhcp_ddnsdir = $(pkgdatadir)
 b10_dhcp_ddns_DATA = dhcp-ddns.spec
diff --git a/src/bin/d2/d2_cfg_mgr.cc b/src/bin/d2/d2_cfg_mgr.cc
new file mode 100644
index 0000000..07068d6
--- /dev/null
+++ b/src/bin/d2/d2_cfg_mgr.cc
@@ -0,0 +1,132 @@
+// Copyright (C) 2013 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 <d2/d2_log.h>
+#include <d2/d2_cfg_mgr.h>
+
+#include <boost/foreach.hpp>
+
+using namespace std;
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::data;
+using namespace isc::asiolink;
+
+namespace isc {
+namespace d2 {
+
+// *********************** D2CfgContext  *************************
+
+D2CfgContext::D2CfgContext()
+    : forward_mgr_(new DdnsDomainListMgr("forward_mgr")),
+      reverse_mgr_(new DdnsDomainListMgr("reverse_mgr")),
+      keys_(new TSIGKeyInfoMap()) {
+}
+
+D2CfgContext::D2CfgContext(const D2CfgContext& rhs) : DCfgContextBase(rhs) {
+    if (rhs.forward_mgr_) {
+        forward_mgr_.reset(new DdnsDomainListMgr(rhs.forward_mgr_->getName()));
+        forward_mgr_->setDomains(rhs.forward_mgr_->getDomains());
+    }
+
+    if (rhs.reverse_mgr_) {
+        reverse_mgr_.reset(new DdnsDomainListMgr(rhs.reverse_mgr_->getName()));
+        reverse_mgr_->setDomains(rhs.reverse_mgr_->getDomains());
+    }
+
+    keys_ = rhs.keys_; 
+}
+
+D2CfgContext::~D2CfgContext() {
+}
+
+// *********************** D2CfgMgr  *************************
+
+D2CfgMgr::D2CfgMgr() : DCfgMgrBase(DCfgContextBasePtr(new D2CfgContext())) {
+    // TSIG keys need to parse before the Domains, so we can catch Domains
+    // that specify undefined keys. Create the necessary parsing order now.
+    addToParseOrder("interface");
+    addToParseOrder("ip_address");
+    addToParseOrder("port");
+    addToParseOrder("tsig_keys");
+    addToParseOrder("forward_ddns");
+    addToParseOrder("reverse_ddns");
+}
+
+D2CfgMgr::~D2CfgMgr() {
+}
+
+bool
+D2CfgMgr::matchForward(const std::string& fqdn, DdnsDomainPtr& domain) {
+    if (fqdn.empty()) {
+        // This is a programmatic error and should not happen.
+        isc_throw(D2CfgError, "matchForward passed an empty fqdn");
+    }
+
+    // Fetch the forward manager from the D2 context.
+    DdnsDomainListMgrPtr mgr = getD2CfgContext()->getForwardMgr();
+
+    // Call the manager's match method and return the result.
+    return (mgr->matchDomain(fqdn, domain));
+}
+
+bool
+D2CfgMgr::matchReverse(const std::string& fqdn, DdnsDomainPtr& domain) {
+    if (fqdn.empty()) {
+        // This is a programmatic error and should not happen.
+        isc_throw(D2CfgError, "matchReverse passed a null or empty fqdn");
+    }
+
+    // Fetch the reverse manager from the D2 context.
+    DdnsDomainListMgrPtr mgr = getD2CfgContext()->getReverseMgr();
+
+    // Call the manager's match method and return the result.
+    return (mgr->matchDomain(fqdn, domain));
+}
+
+
+isc::dhcp::ParserPtr
+D2CfgMgr::createConfigParser(const std::string& config_id) {
+    // Get D2 specific context.
+    D2CfgContextPtr context = getD2CfgContext();
+
+    // Create parser instance based on element_id.
+    DhcpConfigParser* parser = NULL;
+    if ((config_id == "interface")  ||
+        (config_id == "ip_address")) {
+        parser = new StringParser(config_id, context->getStringStorage());
+    } else if (config_id == "port") {
+        parser = new Uint32Parser(config_id, context->getUint32Storage());
+    } else if (config_id ==  "forward_ddns") {
+        parser = new DdnsDomainListMgrParser("forward_mgr",
+                                             context->getForwardMgr(),
+                                             context->getKeys());
+    } else if (config_id ==  "reverse_ddns") {
+        parser = new DdnsDomainListMgrParser("reverse_mgr",
+                                             context->getReverseMgr(),
+                                             context->getKeys());
+    } else if (config_id ==  "tsig_keys") {
+        parser = new TSIGKeyInfoListParser("tsig_key_list", context->getKeys());
+    } else {
+        isc_throw(NotImplemented,
+                  "parser error: D2CfgMgr parameter not supported: "
+                  << config_id);
+    }
+
+    return (isc::dhcp::ParserPtr(parser));
+}
+
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
+
diff --git a/src/bin/d2/d2_cfg_mgr.h b/src/bin/d2/d2_cfg_mgr.h
new file mode 100644
index 0000000..f9089cb
--- /dev/null
+++ b/src/bin/d2/d2_cfg_mgr.h
@@ -0,0 +1,175 @@
+// Copyright (C) 2013 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 D2_CFG_MGR_H
+#define D2_CFG_MGR_H
+
+#include <asiolink/io_address.h>
+#include <cc/data.h>
+#include <exceptions/exceptions.h>
+#include <d2/d_cfg_mgr.h>
+#include <d2/d2_config.h>
+
+#include <stdint.h>
+#include <string>
+
+namespace isc {
+namespace d2 {
+
+class D2CfgContext;
+/// @brief Pointer to a configuration context.
+typedef boost::shared_ptr<D2CfgContext> D2CfgContextPtr;
+
+/// @brief  DHCP-DDNS Configuration Context
+///
+/// Implements the storage container for configuration context.
+/// It provides a single enclosure for the storage of configuration parameters
+/// and any other DHCP-DDNS specific information that needs to be accessible
+/// during configuration parsing as well as to the application as a whole.
+/// It is derived from the context base class, DCfgContextBase.
+class D2CfgContext : public DCfgContextBase {
+public:
+    /// @brief Constructor
+    D2CfgContext();
+
+    /// @brief Destructor
+    virtual ~D2CfgContext();
+
+    /// @brief Creates a clone of this context object.
+    ///
+    /// @return returns a pointer to the new clone.
+    virtual DCfgContextBasePtr clone() {
+        return (DCfgContextBasePtr(new D2CfgContext(*this)));
+    }
+
+    /// @brief Fetches the forward DNS domain list manager.
+    ///
+    /// @return returns a pointer to the forward manager.
+    DdnsDomainListMgrPtr getForwardMgr() {
+        return (forward_mgr_);
+    }
+
+    /// @brief Fetches the reverse DNS domain list manager.
+    ///
+    /// @return returns a pointer to the reverse manager.
+    DdnsDomainListMgrPtr getReverseMgr() {
+        return (reverse_mgr_);
+    }
+
+    /// @brief Fetches the map of TSIG keys.
+    ///
+    /// @return returns a pointer to the key map.
+    TSIGKeyInfoMapPtr getKeys() {
+        return (keys_);
+    }
+
+protected:
+    /// @brief Copy constructor for use by derivations in clone().
+    D2CfgContext(const D2CfgContext& rhs);
+
+private:
+    /// @brief Private assignment operator to avoid potential for slicing.
+    D2CfgContext& operator=(const D2CfgContext& rhs);
+
+    /// @brief Forward domain list manager.
+    DdnsDomainListMgrPtr forward_mgr_;
+
+    /// @brief Reverse domain list manager.
+    DdnsDomainListMgrPtr reverse_mgr_;
+
+    /// @brief Storage for the map of TSIGKeyInfos
+    TSIGKeyInfoMapPtr keys_;
+};
+
+/// @brief Defines a pointer for DdnsDomain instances.
+typedef boost::shared_ptr<DdnsDomainListMgr> DdnsDomainListMgrPtr;
+
+
+
+/// @brief DHCP-DDNS Configuration Manager
+///
+/// Provides the mechanisms for managing the DHCP-DDNS application's
+/// configuration.  This includes services for parsing sets of configuration
+/// values, storing the parsed information in its converted form,
+/// and retrieving the information on demand.
+class D2CfgMgr : public DCfgMgrBase {
+public:
+    /// @brief Constructor
+    D2CfgMgr();
+
+    /// @brief Destructor
+    virtual ~D2CfgMgr();
+
+    /// @brief Convenience method that returns the D2 configuration context.
+    ///
+    /// @return returns a pointer to the configuration context.
+    D2CfgContextPtr getD2CfgContext() {
+        return (boost::dynamic_pointer_cast<D2CfgContext>(getContext()));
+    }
+
+    /// @brief Matches a given FQDN to a forward domain.
+    /// 
+    /// This calls the matchDomain method of the forward domain manager to
+    /// match the given FQDN to a forward domain.  
+    ///
+    /// @param fqdn is the name for which to look.
+    /// @param domain receives the matching domain. Note that it will be reset
+    /// upon entry and only set if a match is subsequently found.
+    ///
+    /// @return returns true if a match is found, false otherwise.
+    /// @throw throws D2CfgError if given an invalid fqdn. 
+    bool matchForward(const std::string& fqdn, DdnsDomainPtr &domain);
+
+    /// @brief Matches a given FQDN to a reverse domain.
+    ///
+    /// This calls the matchDomain method of the reverse domain manager to
+    /// match the given FQDN to a forward domain.  
+    ///
+    /// @param fqdn is the name for which to look.
+    /// @param domain receives the matching domain. Note that it will be reset
+    /// upon entry and only set if a match is subsequently found.
+    ///
+    /// @return returns true if a match is found, false otherwise.
+    /// @throw throws D2CfgError if given an invalid fqdn. 
+    bool matchReverse(const std::string& fqdn, DdnsDomainPtr &domain);
+
+protected:
+    /// @brief Given an element_id returns an instance of the appropriate
+    /// parser.
+    ///
+    /// It is responsible for top-level or outermost DHCP-DDNS configuration
+    /// elements (see dhcp-ddns.spec):
+    ///     1. interface
+    ///     2. ip_address
+    ///     3. port
+    ///     4. forward_ddns
+    ///     5. reverse_ddns
+    ///
+    /// @param element_id is the string name of the element as it will appear
+    /// in the configuration set.
+    ///
+    /// @return returns a ParserPtr to the parser instance.
+    /// @throw throws DCfgMgrBaseError if an error occurs.
+    virtual isc::dhcp::ParserPtr
+    createConfigParser(const std::string& element_id);
+};
+
+/// @brief Defines a shared pointer to D2CfgMgr.
+typedef boost::shared_ptr<D2CfgMgr> D2CfgMgrPtr;
+
+
+}; // end of isc::d2 namespace
+}; // end of isc namespace
+
+#endif // D2_CFG_MGR_H
diff --git a/src/bin/d2/d2_config.cc b/src/bin/d2/d2_config.cc
new file mode 100644
index 0000000..fe5ad0b
--- /dev/null
+++ b/src/bin/d2/d2_config.cc
@@ -0,0 +1,605 @@
+// Copyright (C) 2013 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 <d2/d2_log.h>
+#include <d2/d2_cfg_mgr.h>
+#include <dhcpsrv/dhcp_parsers.h>
+#include <exceptions/exceptions.h>
+#include <asiolink/io_error.h>
+
+#include <boost/foreach.hpp>
+#include <boost/lexical_cast.hpp>
+
+namespace isc {
+namespace d2 {
+
+// *********************** TSIGKeyInfo  *************************
+
+TSIGKeyInfo::TSIGKeyInfo(const std::string& name, const std::string& algorithm,
+                         const std::string& secret)
+    :name_(name), algorithm_(algorithm), secret_(secret) {
+}
+
+TSIGKeyInfo::~TSIGKeyInfo() {
+}
+
+
+// *********************** DnsServerInfo  *************************
+
+const char* DnsServerInfo::EMPTY_IP_STR = "0.0.0.0";
+
+DnsServerInfo::DnsServerInfo(const std::string& hostname,
+                             isc::asiolink::IOAddress ip_address, uint32_t port,
+                             bool enabled)
+    :hostname_(hostname), ip_address_(ip_address), port_(port),
+    enabled_(enabled) {
+}
+
+DnsServerInfo::~DnsServerInfo() {
+}
+
+// *********************** DdnsDomain  *************************
+
+DdnsDomain::DdnsDomain(const std::string& name, const std::string& key_name,
+                       DnsServerInfoStoragePtr servers)
+    : name_(name), key_name_(key_name), servers_(servers) {
+}
+
+DdnsDomain::~DdnsDomain() {
+}
+
+// *********************** DdnsDomainLstMgr  *************************
+
+const char* DdnsDomainListMgr::wildcard_domain_name_ = "*";
+
+DdnsDomainListMgr::DdnsDomainListMgr(const std::string& name) : name_(name),
+    domains_(new DdnsDomainMap()) {
+}
+
+
+DdnsDomainListMgr::~DdnsDomainListMgr () {
+}
+
+void
+DdnsDomainListMgr::setDomains(DdnsDomainMapPtr domains) {
+    if (!domains) {
+        isc_throw(D2CfgError,
+                  "DdnsDomainListMgr::setDomains: Domain list may not be null");
+    }
+
+    domains_ = domains;
+
+    // Look for the wild card domain. If present, set the member variable
+    // to remember it.  This saves us from having to look for it every time
+    // we attempt a match.
+    DdnsDomainMap::iterator gotit = domains_->find(wildcard_domain_name_);
+    if (gotit != domains_->end()) {
+            wildcard_domain_ = gotit->second;
+    }
+}
+
+bool
+DdnsDomainListMgr::matchDomain(const std::string& fqdn, DdnsDomainPtr& domain) {
+    // Clear the return parameter.
+    domain.reset();
+
+    // First check the case of one domain to rule them all.
+    if ((size() == 1) && (wildcard_domain_)) {
+        domain = wildcard_domain_;
+        return (true);
+    }
+
+    // Start with the longest version of the fqdn and search the list.
+    // Continue looking for shorter versions of fqdn so long as no match is
+    // found.
+    // @todo This can surely be optimized, time permitting.
+    std::string match_name = fqdn;
+    std::size_t start_pos = 0;
+    while (start_pos != std::string::npos) {
+        match_name = match_name.substr(start_pos, std::string::npos);
+        DdnsDomainMap::iterator gotit = domains_->find(match_name);
+        if (gotit != domains_->end()) {
+            domain = gotit->second;
+            return (true);
+        }
+
+        start_pos = match_name.find_first_of(".");
+        if (start_pos != std::string::npos) {
+            ++start_pos;
+        }
+    }
+
+    // There's no match. If they specified a wild card domain use it
+    // otherwise there's no domain for this entry.
+    if (wildcard_domain_) {
+        domain = wildcard_domain_;
+        return (true);
+    }
+
+    LOG_WARN(dctl_logger, DHCP_DDNS_NO_MATCH).arg(fqdn);
+    return (false);
+}
+
+// *************************** PARSERS ***********************************
+
+// *********************** TSIGKeyInfoParser  *************************
+
+TSIGKeyInfoParser::TSIGKeyInfoParser(const std::string& entry_name,
+    TSIGKeyInfoMapPtr keys)
+    : entry_name_(entry_name), keys_(keys), local_scalars_() {
+    if (!keys_) {
+        isc_throw(D2CfgError, "TSIGKeyInfoParser ctor:"
+                  " key storage cannot be null");
+    }
+}
+
+TSIGKeyInfoParser::~TSIGKeyInfoParser() {
+}
+
+void
+TSIGKeyInfoParser::build(isc::data::ConstElementPtr key_config) {
+    isc::dhcp::ConfigPair config_pair;
+    // For each element in the key configuration:
+    // 1. Create a parser for the element.
+    // 2. Invoke the parser's build method passing in the element's
+    // configuration.
+    // 3. Invoke the parser's commit method to store the element's parsed
+    // data to the parser's local storage.
+    BOOST_FOREACH (config_pair, key_config->mapValue()) {
+        isc::dhcp::ParserPtr parser(createConfigParser(config_pair.first));
+        parser->build(config_pair.second);
+        parser->commit();
+    }
+}
+
+isc::dhcp::ParserPtr
+TSIGKeyInfoParser::createConfigParser(const std::string& config_id) {
+    DhcpConfigParser* parser = NULL;
+    // Based on the configuration id of the element, create the appropriate
+    // parser. Scalars are set to use the parser's local scalar storage.
+    if ((config_id == "name")  ||
+        (config_id == "algorithm") ||
+        (config_id == "secret")) {
+        parser = new isc::dhcp::StringParser(config_id,
+                                             local_scalars_.getStringStorage());
+    } else {
+        isc_throw(NotImplemented,
+                  "parser error: TSIGKeyInfo parameter not supported: "
+                  << config_id);
+    }
+
+    // Return the new parser instance.
+    return (isc::dhcp::ParserPtr(parser));
+}
+
+void
+TSIGKeyInfoParser::commit() {
+    std::string name;
+    std::string algorithm;
+    std::string secret;
+
+    // Fetch the key configuration's parsed scalar values from parser's
+    // local storage.
+    local_scalars_.getParam("name", name);
+    local_scalars_.getParam("algorithm", algorithm);
+    local_scalars_.getParam("secret", secret);
+
+    // @todo Validation here is very superficial. This will expand as TSIG
+    // Key use is more fully implemented.
+
+    // Name cannot be blank.
+    if (name.empty()) {
+        isc_throw(D2CfgError, "TSIG Key Info must specify name");
+    }
+
+    // Algorithme cannot be blank.
+    if (algorithm.empty()) {
+        isc_throw(D2CfgError, "TSIG Key Info must specify algorithm");
+    }
+
+    // Secret cannot be blank.
+    if (secret.empty()) {
+        isc_throw(D2CfgError, "TSIG Key Info must specify secret");
+    }
+
+    // Currently, the premise is that key storage is always empty prior to
+    // parsing so we are always adding keys never replacing them. Duplicates
+    // are not allowed and should be flagged as a configuration error.
+    if (keys_->find(name) != keys_->end()) {
+        isc_throw(D2CfgError, "Duplicate TSIG key specified:" << name);
+    }
+
+    TSIGKeyInfoPtr key_info(new TSIGKeyInfo(name, algorithm, secret));
+
+    // Add the new TSIGKeyInfo to the key storage.
+    (*keys_)[name]=key_info;
+}
+
+// *********************** TSIGKeyInfoListParser  *************************
+
+TSIGKeyInfoListParser::TSIGKeyInfoListParser(const std::string& list_name,
+                                       TSIGKeyInfoMapPtr keys)
+    :list_name_(list_name), keys_(keys), parsers_() {
+    if (!keys_) {
+        isc_throw(D2CfgError, "TSIGKeyInfoListParser ctor:"
+                  " key storage cannot be null");
+    }
+}
+
+TSIGKeyInfoListParser::~TSIGKeyInfoListParser(){
+}
+
+void
+TSIGKeyInfoListParser::
+build(isc::data::ConstElementPtr key_list){
+    int i = 0;
+    isc::data::ConstElementPtr key_config;
+    // For each key element in the key list:
+    // 1. Create a parser for the key element.
+    // 2. Invoke the parser's build method passing in the key's
+    // configuration.
+    // 3. Add the parser to a local collection of parsers.
+    BOOST_FOREACH(key_config, key_list->listValue()) {
+        // Create a name for the parser based on its position in the list.
+        std::string entry_name = boost::lexical_cast<std::string>(i++);
+        isc::dhcp::ParserPtr parser(new TSIGKeyInfoParser(entry_name,
+                                                            keys_));
+        parser->build(key_config);
+        parsers_.push_back(parser);
+    }
+}
+
+void
+TSIGKeyInfoListParser::commit() {
+    // Invoke commit on each server parser. This will cause each one to
+    // create it's server instance and commit it to storage.
+    BOOST_FOREACH(isc::dhcp::ParserPtr parser, parsers_) {
+        parser->commit();
+    }
+}
+
+// *********************** DnsServerInfoParser  *************************
+
+DnsServerInfoParser::DnsServerInfoParser(const std::string& entry_name,
+    DnsServerInfoStoragePtr servers)
+    : entry_name_(entry_name), servers_(servers), local_scalars_() {
+    if (!servers_) {
+        isc_throw(D2CfgError, "DnsServerInfoParser ctor:"
+                  " server storage cannot be null");
+    }
+}
+
+DnsServerInfoParser::~DnsServerInfoParser() {
+}
+
+void
+DnsServerInfoParser::build(isc::data::ConstElementPtr server_config) {
+    isc::dhcp::ConfigPair config_pair;
+    // For each element in the server configuration:
+    // 1. Create a parser for the element.
+    // 2. Invoke the parser's build method passing in the element's
+    // configuration.
+    // 3. Invoke the parser's commit method to store the element's parsed
+    // data to the parser's local storage.
+    BOOST_FOREACH (config_pair, server_config->mapValue()) {
+        isc::dhcp::ParserPtr parser(createConfigParser(config_pair.first));
+        parser->build(config_pair.second);
+        parser->commit();
+    }
+
+}
+
+isc::dhcp::ParserPtr
+DnsServerInfoParser::createConfigParser(const std::string& config_id) {
+    DhcpConfigParser* parser = NULL;
+    // Based on the configuration id of the element, create the appropriate
+    // parser. Scalars are set to use the parser's local scalar storage.
+    if ((config_id == "hostname")  ||
+        (config_id == "ip_address")) {
+        parser = new isc::dhcp::StringParser(config_id,
+                                             local_scalars_.getStringStorage());
+    } else if (config_id == "port") {
+        parser = new isc::dhcp::Uint32Parser(config_id,
+                                             local_scalars_.getUint32Storage());
+    } else {
+        isc_throw(NotImplemented,
+                  "parser error: DnsServerInfo parameter not supported: "
+                  << config_id);
+    }
+
+    // Return the new parser instance.
+    return (isc::dhcp::ParserPtr(parser));
+}
+
+void
+DnsServerInfoParser::commit() {
+    std::string hostname;
+    std::string ip_address;
+    uint32_t port = DnsServerInfo::STANDARD_DNS_PORT;
+
+    // Fetch the server configuration's parsed scalar values from parser's
+    // local storage.
+    local_scalars_.getParam("hostname", hostname, DCfgContextBase::OPTIONAL);
+    local_scalars_.getParam("ip_address", ip_address,
+                            DCfgContextBase::OPTIONAL);
+    local_scalars_.getParam("port", port, DCfgContextBase::OPTIONAL);
+
+    // The configuration must specify one or the other.
+    if (hostname.empty() == ip_address.empty()) {
+        isc_throw(D2CfgError, "Dns Server must specify one or the other"
+                  " of hostname and IP address");
+    }
+
+    DnsServerInfoPtr serverInfo;
+    if (!hostname.empty()) {
+        // When  hostname is specified, create a valid, blank IOAddress and
+        // then create the DnsServerInfo.
+        isc::asiolink::IOAddress io_addr(DnsServerInfo::EMPTY_IP_STR);
+        serverInfo.reset(new DnsServerInfo(hostname, io_addr, port));
+    } else {
+        try {
+            // Create an IOAddress from the IP address string given and then
+            // create the DnsServerInfo.
+            isc::asiolink::IOAddress io_addr(ip_address);
+            serverInfo.reset(new DnsServerInfo(hostname, io_addr, port));
+        } catch (const isc::asiolink::IOError& ex) {
+            isc_throw(D2CfgError, "Invalid IP address:" << ip_address);
+        }
+    }
+
+    // Add the new DnsServerInfo to the server storage.
+    servers_->push_back(serverInfo);
+}
+
+// *********************** DnsServerInfoListParser  *************************
+
+DnsServerInfoListParser::DnsServerInfoListParser(const std::string& list_name,
+                                       DnsServerInfoStoragePtr servers)
+    :list_name_(list_name), servers_(servers), parsers_() {
+    if (!servers_) {
+        isc_throw(D2CfgError, "DdnsServerInfoListParser ctor:"
+                  " server storage cannot be null");
+    }
+}
+
+DnsServerInfoListParser::~DnsServerInfoListParser(){
+}
+
+void
+DnsServerInfoListParser::
+build(isc::data::ConstElementPtr server_list){
+    int i = 0;
+    isc::data::ConstElementPtr server_config;
+    // For each server element in the server list:
+    // 1. Create a parser for the server element.
+    // 2. Invoke the parser's build method passing in the server's
+    // configuration.
+    // 3. Add the parser to a local collection of parsers.
+    BOOST_FOREACH(server_config, server_list->listValue()) {
+        // Create a name for the parser based on its position in the list.
+        std::string entry_name = boost::lexical_cast<std::string>(i++);
+        isc::dhcp::ParserPtr parser(new DnsServerInfoParser(entry_name,
+                                                            servers_));
+        parser->build(server_config);
+        parsers_.push_back(parser);
+    }
+}
+
+void
+DnsServerInfoListParser::commit() {
+    // Domains must have at least one server.
+    if (parsers_.size() == 0) {
+        isc_throw (D2CfgError, "Server List must contain at least one server");
+    }
+
+    // Invoke commit on each server parser. This will cause each one to
+    // create it's server instance and commit it to storage.
+    BOOST_FOREACH(isc::dhcp::ParserPtr parser, parsers_) {
+        parser->commit();
+    }
+}
+
+// *********************** DdnsDomainParser  *************************
+
+DdnsDomainParser::DdnsDomainParser(const std::string& entry_name,
+                                   DdnsDomainMapPtr domains,
+                                   TSIGKeyInfoMapPtr keys)
+    : entry_name_(entry_name), domains_(domains), keys_(keys),
+    local_servers_(new DnsServerInfoStorage()), local_scalars_() {
+    if (!domains_) {
+        isc_throw(D2CfgError,
+                  "DdnsDomainParser ctor, domain storage cannot be null");
+    }
+}
+
+
+DdnsDomainParser::~DdnsDomainParser() {
+}
+
+void
+DdnsDomainParser::build(isc::data::ConstElementPtr domain_config) {
+    // For each element in the domain configuration:
+    // 1. Create a parser for the element.
+    // 2. Invoke the parser's build method passing in the element's
+    // configuration.
+    // 3. Invoke the parser's commit method to store the element's parsed
+    // data to the parser's local storage.
+    isc::dhcp::ConfigPair config_pair;
+    BOOST_FOREACH(config_pair, domain_config->mapValue()) {
+        isc::dhcp::ParserPtr parser(createConfigParser(config_pair.first));
+        parser->build(config_pair.second);
+        parser->commit();
+    }
+}
+
+isc::dhcp::ParserPtr
+DdnsDomainParser::createConfigParser(const std::string& config_id) {
+    DhcpConfigParser* parser = NULL;
+    // Based on the configuration id of the element, create the appropriate
+    // parser. Scalars are set to use the parser's local scalar storage.
+    if ((config_id == "name")  ||
+        (config_id == "key_name")) {
+        parser = new isc::dhcp::StringParser(config_id,
+                                             local_scalars_.getStringStorage());
+    } else if (config_id == "dns_servers") {
+       // Server list parser is given in our local server storage. It will pass
+       // this down to its server parsers and is where they will write their
+       // server instances upon commit.
+       parser = new DnsServerInfoListParser(config_id, local_servers_);
+    } else {
+       isc_throw(NotImplemented,
+                "parser error: DdnsDomain parameter not supported: "
+                << config_id);
+    }
+
+    // Return the new domain parser instance.
+    return (isc::dhcp::ParserPtr(parser));
+}
+
+void
+DdnsDomainParser::commit() {
+    std::string name;
+    std::string key_name;
+
+    // Domain name is not optional. The get will throw if its not there.
+    local_scalars_.getParam("name", name);
+
+    // Blank domain names are not allowed.
+    if (name.empty()) {
+        isc_throw(D2CfgError, "Domain name cannot be blank");
+    }
+
+    // Currently, the premise is that domain storage is always empty
+    // prior to parsing so always adding domains never replacing them.
+    // Duplicates are not allowed and should be flagged as a configuration
+    // error.
+    if (domains_->find(name) != domains_->end()) {
+        isc_throw(D2CfgError, "Duplicate domain specified:" << name);
+    }
+
+    // Key name is optional. If it is not blank, then validate it against
+    // the defined list of keys.
+    local_scalars_.getParam("key_name", key_name, DCfgContextBase::OPTIONAL);
+    if (!key_name.empty()) {
+        if ((!keys_) || (keys_->find(key_name) == keys_->end())) {
+            isc_throw(D2CfgError, "DdnsDomain :" << name <<
+                     " specifies and undefined key:" << key_name);
+        }
+    }
+
+    // Instantiate the new domain and add it to domain storage.
+    DdnsDomainPtr domain(new DdnsDomain(name, key_name, local_servers_));
+
+    // Add the new domain to the domain storage.
+    (*domains_)[name]=domain;
+}
+
+// *********************** DdnsDomainListParser  *************************
+
+DdnsDomainListParser::DdnsDomainListParser(const std::string& list_name,
+                                           DdnsDomainMapPtr domains,
+                                           TSIGKeyInfoMapPtr keys)
+    :list_name_(list_name), domains_(domains), keys_(keys), parsers_() {
+    if (!domains_) {
+        isc_throw(D2CfgError, "DdnsDomainListParser ctor:"
+                  " domain storage cannot be null");
+    }
+}
+
+DdnsDomainListParser::~DdnsDomainListParser(){
+}
+
+void
+DdnsDomainListParser::
+build(isc::data::ConstElementPtr domain_list){
+    // For each domain element in the domain list:
+    // 1. Create a parser for the domain element.
+    // 2. Invoke the parser's build method passing in the domain's
+    // configuration.
+    // 3. Add the parser to the local collection of parsers.
+    int i = 0;
+    isc::data::ConstElementPtr domain_config;
+    BOOST_FOREACH(domain_config, domain_list->listValue()) {
+        std::string entry_name = boost::lexical_cast<std::string>(i++);
+        isc::dhcp::ParserPtr parser(new DdnsDomainParser(entry_name,
+                                                         domains_, keys_));
+        parser->build(domain_config);
+        parsers_.push_back(parser);
+    }
+}
+
+void
+DdnsDomainListParser::commit() {
+    // Invoke commit on each server parser. This will cause each one to
+    // create it's server instance and commit it to storage.
+    BOOST_FOREACH(isc::dhcp::ParserPtr parser, parsers_) {
+        parser->commit();
+    }
+}
+
+
+// *********************** DdnsDomainListMgrParser  *************************
+
+DdnsDomainListMgrParser::DdnsDomainListMgrParser(const std::string& entry_name,
+                              DdnsDomainListMgrPtr mgr, TSIGKeyInfoMapPtr keys)
+    : entry_name_(entry_name), mgr_(mgr), keys_(keys),
+    local_domains_(new DdnsDomainMap()), local_scalars_() {
+}
+
+
+DdnsDomainListMgrParser::~DdnsDomainListMgrParser() {
+}
+
+void
+DdnsDomainListMgrParser::build(isc::data::ConstElementPtr domain_config) {
+    // For each element in the domain manager configuration:
+    // 1. Create a parser for the element.
+    // 2. Invoke the parser's build method passing in the element's
+    // configuration.
+    // 3. Invoke the parser's commit method to store the element's parsed
+    // data to the parser's local storage.
+    isc::dhcp::ConfigPair config_pair;
+    BOOST_FOREACH(config_pair, domain_config->mapValue()) {
+        isc::dhcp::ParserPtr parser(createConfigParser(config_pair.first));
+        parser->build(config_pair.second);
+        parser->commit();
+    }
+}
+
+isc::dhcp::ParserPtr
+DdnsDomainListMgrParser::createConfigParser(const std::string& config_id) {
+    DhcpConfigParser* parser = NULL;
+    if (config_id == "ddns_domains") {
+       // Domain list parser is given our local domain storage. It will pass
+       // this down to its domain parsers and is where they will write their
+       // domain instances upon commit.
+       parser = new DdnsDomainListParser(config_id, local_domains_, keys_);
+    } else {
+       isc_throw(NotImplemented, "parser error: "
+                 "DdnsDomainListMgr parameter not supported: " << config_id);
+    }
+
+    // Return the new domain parser instance.
+    return (isc::dhcp::ParserPtr(parser));
+}
+
+void
+DdnsDomainListMgrParser::commit() {
+    // Add the new domain to the domain storage.
+    mgr_->setDomains(local_domains_);
+}
+
+
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
diff --git a/src/bin/d2/d2_config.h b/src/bin/d2/d2_config.h
new file mode 100644
index 0000000..7350291
--- /dev/null
+++ b/src/bin/d2/d2_config.h
@@ -0,0 +1,941 @@
+// Copyright (C) 2013 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 D2_CONFIG_H
+#define D2_CONFIG_H
+
+#include <asiolink/io_address.h>
+#include <cc/data.h>
+#include <d2/d_cfg_mgr.h>
+#include <dhcpsrv/dhcp_parsers.h>
+#include <exceptions/exceptions.h>
+
+#include <boost/foreach.hpp>
+
+#include <stdint.h>
+#include <string>
+
+namespace isc {
+namespace d2 {
+
+/// @file d2_config.h
+/// @brief A collection of classes for housing and parsing the application
+/// configuration necessary for the DHCP-DDNS application (aka D2).
+///
+/// This file contains the class declarations for the class hierarchy created
+/// from the D2 configuration and the parser classes used to create it.
+/// The application configuration consists of a set of scalar parameters,
+/// a list of TSIG keys, and two managed lists of domains: one list for
+/// forward domains and one list for reverse domains.
+///
+/// The key list consists of one or more TSIG keys, each entry described by
+/// a name, the algorithm method name, and its secret key component.
+///
+/// @todo  NOTE that TSIG configuration parsing is functional, the use of
+/// TSIG Keys during the actual DNS update transactions is not.  This will be
+/// implemented in a future release.
+///
+/// Each managed domain list consists of a list one or more domains and is
+/// represented by the class DdnsDomainListMgr.
+///
+/// Each domain consists of a set of scalars parameters and a list of DNS
+/// servers which support that domain. Among its scalars, is key_name, which
+/// is the name of the TSIG Key to use for with this domain.  This value should
+/// map to one of the TSIG Keys in the key list.  Domains are represented by
+/// the class, DdnsDomain.
+///
+/// Each server consists of a set of scalars used to describe the server such
+/// that the application can carry out DNS update exchanges with it. Servers
+/// are represented by the class, DnsServerInfo.
+///
+/// The configuration specification for use with BIND10 is detailed in the file
+/// dhcp-ddns.spec.
+///
+/// The parsing class hierarchy reflects this same scheme.  Working top down:
+///
+/// A DdnsDomainListMgrParser parses a managed domain list entry. It handles
+/// any scalars which belong to the manager as well as creating and invoking a
+/// DdnsDomainListParser to parse its list of domain entries.
+///
+/// A DdnsDomainLiatParser creates and invokes DdnsDomainListParser for each
+/// domain entry in its list.
+///
+/// A DdnsDomainParser handles the scalars which belong to the domain as well as
+/// creating and invoking a DnsSeverInfoListParser to parse its list of server
+/// entries.
+///
+/// A DnsServerInfoListParser creates and invokes a DnsServerInfoParser for
+/// each server entry in its list.
+///
+/// A DdnsServerInfoParser handles the scalars which belong to the server.
+/// The following is sample configuration in JSON form with extra spacing
+/// for clarity:
+///
+/// @code
+/// {
+///  "interface" : "eth1" ,
+///  "ip_address" : "192.168.1.33" ,
+///  "port" : 88 ,
+///  "tsig_keys":
+//// [
+///    {
+///     "name": "d2_key.tmark.org" ,
+///     "algorithm": "md5" ,
+///     "secret": "0123456989"
+///    }
+///  ],
+///  "forward_ddns" :
+///  {
+///    "ddns_domains":
+///    [
+///      {
+///        "name": "tmark.org." ,
+///        "key_name": "d2_key.tmark.org" ,
+///        "dns_servers" :
+///        [
+///          { "hostname": "fserver.tmark.org" },
+///          { "hostname": "f2server.tmark.org" }
+///        ]
+///      },
+///      {
+///        "name": "pub.tmark.org." ,
+///        "key_name": "d2_key.tmark.org" ,
+///        "dns_servers" :
+///        [
+///          { "hostname": "f3server.tmark.org" }
+///        ]
+///      }
+///    ]
+///  },
+///  "reverse_ddns" :
+///  {
+///    "ddns_domains":
+///    [
+///      {
+///        "name": " 0.168.192.in.addr.arpa." ,
+///        "key_name": "d2_key.tmark.org" ,
+///        "dns_servers" :
+///        [
+///          { "ip_address": "127.0.0.101" , "port": 100 }
+///        ]
+///      }
+///    ]
+///  }
+/// }
+/// @endcode
+
+/// @brief Exception thrown when the error during configuration handling
+/// occurs.
+class D2CfgError : public isc::Exception {
+public:
+    D2CfgError(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) { };
+};
+
+/// @brief Represents a TSIG Key.
+///
+/// Currently, this is simple storage class containing the basic attributes of
+/// a TSIG Key.  It is intended primarily as a reference for working with
+/// actual keys and may eventually be replaced by isc::dns::TSIGKey.  TSIG Key
+/// functionality at this stage is strictly limited to configuration parsing.
+/// @todo full functionality for using TSIG during DNS updates will be added
+/// in a future release.
+class TSIGKeyInfo {
+public:
+
+    /// @brief Constructor
+    ///
+    /// @param name the unique label used to identify this key
+    /// @param algorithm the name of the encryption alogirthm this key uses.
+    /// (@todo This will be a fixed list of choices)
+    /// @param secret the secret component of this key
+    TSIGKeyInfo(const std::string& name, const std::string& algorithm,
+                const std::string& secret);
+
+    /// @brief Destructor
+    virtual ~TSIGKeyInfo();
+
+    /// @brief Getter which returns the key's name.
+    ///
+    /// @return returns the name as as std::string.
+    const std::string getName() const {
+        return (name_);
+    }
+
+    /// @brief Getter which returns the key's algorithm.
+    ///
+    /// @return returns the algorithm as as std::string.
+    const std::string getAlgorithm() const {
+        return (algorithm_);
+    }
+
+    /// @brief Getter which returns the key's secret.
+    ///
+    /// @return returns the secret as as std::string.
+    const std::string getSecret() const {
+        return (secret_);
+    }
+
+private:
+    /// @brief The name of the key.
+    ///
+    /// This value is the unique identifeir thay domains use to
+    /// to specify which TSIG key they need.
+    std::string name_;
+
+    /// @brief The algorithm that should be used for this key.
+    std::string algorithm_;
+
+    /// @brief The secret value component of this key.
+    std::string secret_;
+};
+
+/// @brief Defines a pointer for TSIGKeyInfo instances.
+typedef boost::shared_ptr<TSIGKeyInfo> TSIGKeyInfoPtr;
+
+/// @brief Defines a map of TSIGKeyInfos, keyed by the name.
+typedef std::map<std::string, TSIGKeyInfoPtr> TSIGKeyInfoMap;
+
+/// @brief Defines a iterator pairing of name and TSIGKeyInfo
+typedef std::pair<std::string, TSIGKeyInfoPtr> TSIGKeyInfoMapPair;
+
+/// @brief Defines a pointer to map of TSIGkeyInfos
+typedef boost::shared_ptr<TSIGKeyInfoMap> TSIGKeyInfoMapPtr;
+
+
+/// @brief Represents a specific DNS Server.
+/// It provides information about the server's network identity and typically
+/// belongs to a list of servers supporting DNS for a given domain. It will
+/// be used to establish communications with the server to carry out DNS
+/// updates.
+class DnsServerInfo {
+public:
+
+    /// @brief defines DNS standard port value
+    static const uint32_t STANDARD_DNS_PORT = 53;
+
+    /// @brief defines an "empty" string version of an ip address.
+    static const char* EMPTY_IP_STR;
+
+
+    /// @brief Constructor
+    ///
+    /// @param hostname is the resolvable name of the server. If not blank,
+    /// then the server address should be resolved at runtime.
+    /// @param ip_address is the static IP address of the server. If hostname
+    /// is blank, then this address should be used to connect to the server.
+    /// @param port is the port number on which the server listens.
+    /// primarily meant for testing purposes.  Normally, DNS traffic is on
+    /// is port 53. (NOTE the constructing code is responsible for setting
+    /// the default.)
+    /// @param enabled is a flag that indicates whether this server is
+    /// enabled for use. It defaults to true.
+    DnsServerInfo(const std::string& hostname,
+                  isc::asiolink::IOAddress ip_address,
+                  uint32_t port = STANDARD_DNS_PORT,
+                  bool enabled=true);
+
+    /// @brief Destructor
+    virtual ~DnsServerInfo();
+
+    /// @brief Getter which returns the server's hostname.
+    ///
+    /// @return returns the hostname as as std::string.
+    const std::string getHostname() const {
+        return (hostname_);
+    }
+
+    /// @brief Getter which returns the server's port number.
+    ///
+    /// @return returns the port number as a unsigned integer.
+    uint32_t getPort() const {
+        return (port_);
+    }
+
+    /// @brief Getter which returns the server's ip_address.
+    ///
+    /// @return returns the address as an IOAddress reference.
+    const isc::asiolink::IOAddress& getIpAddress() const {
+        return (ip_address_);
+    }
+
+    /// @brief Convenience method which returns whether or not the
+    /// server is enabled.
+    ///
+    /// @return returns true if the server is enabled, false otherwise.
+    bool isEnabled() const {
+        return (enabled_);
+    }
+
+    /// @brief Sets the server's enabled flag to true.
+    void enable() {
+        enabled_ = true;
+    }
+
+    /// @brief Sets the server's enabled flag to false.
+    void disable() {
+        enabled_ = false;
+    }
+
+
+private:
+    /// @brief The resolvable name of the server. If not blank, then the
+    /// server's IP address should be dynamically resolved at runtime.
+    std::string hostname_;
+
+    /// @brief The static IP address of the server. When hostname is blank,
+    /// then this address should be used to connect to the server.
+    isc::asiolink::IOAddress ip_address_;
+
+    /// @brief The port number on which the server listens for DNS traffic.
+    uint32_t port_;
+
+    /// @param enabled is a flag that indicates whether this server is
+    /// enabled for use. It defaults to true.
+    bool enabled_;
+};
+
+/// @brief Defines a pointer for DnsServerInfo instances.
+typedef boost::shared_ptr<DnsServerInfo> DnsServerInfoPtr;
+
+/// @brief Defines a storage container for DnsServerInfo pointers.
+typedef std::vector<DnsServerInfoPtr> DnsServerInfoStorage;
+
+/// @brief Defines a pointer to DnsServerInfo storage containers.
+typedef boost::shared_ptr<DnsServerInfoStorage> DnsServerInfoStoragePtr;
+
+
+/// @brief Represents a DNS domain that is may be updated dynamically.
+/// This class specifies a DNS domain and the list of DNS servers that support
+/// it.  It's primary use is to map a domain to the DNS server(s) responsible
+/// for it.
+/// @todo Currently the name entry for a domain is just an std::string. It
+/// may be worthwhile to change this to a dns::Name for purposes of better 
+/// validation and matching capabilities. 
+class DdnsDomain {
+public:
+    /// @brief Constructor
+    ///
+    /// @param name is the domain name of the domain.
+    /// @param key_name is the TSIG key name for use with this domain.
+    /// @param servers is the list of server(s) supporting this domain.
+    DdnsDomain(const std::string& name, const std::string& key_name,
+               DnsServerInfoStoragePtr servers);
+
+    /// @brief Destructor
+    virtual ~DdnsDomain();
+
+    /// @brief Getter which returns the domain's name.
+    ///
+    /// @return returns the name in an std::string.
+    const std::string getName() const {
+        return (name_);
+    }
+
+    /// @brief Getter which returns the domain's TSIG key name.
+    ///
+    /// @return returns the key name in an std::string.
+    const std::string getKeyName() const {
+        return (key_name_);
+    }
+
+    /// @brief Getter which returns the domain's list of servers.
+    ///
+    /// @return returns the pointer to the server storage.
+    const DnsServerInfoStoragePtr& getServers() {
+        return (servers_);
+    }
+
+private:
+    /// @brief The domain name of the domain.
+    std::string name_;
+
+    /// @brief The name of the TSIG key for use with this domain.
+    std::string key_name_;
+
+    /// @brief The list of server(s) supporting this domain.
+    DnsServerInfoStoragePtr servers_;
+};
+
+/// @brief Defines a pointer for DdnsDomain instances.
+typedef boost::shared_ptr<DdnsDomain> DdnsDomainPtr;
+
+/// @brief Defines a map of DdnsDomains, keyed by the domain name.
+typedef std::map<std::string, DdnsDomainPtr> DdnsDomainMap;
+
+/// @brief Defines a iterator pairing domain name and DdnsDomain
+typedef std::pair<std::string, DdnsDomainPtr> DdnsDomainMapPair;
+
+/// @brief Defines a pointer to DdnsDomain storage containers.
+typedef boost::shared_ptr<DdnsDomainMap> DdnsDomainMapPtr;
+
+/// @brief Provides storage for and management of a list of DNS domains.
+/// In addition to housing the domain list storage, it provides domain matching
+/// services.  These services are used to match a FQDN to a domain.  Currently
+/// it supports a single matching service, which will return the matching
+/// domain or a wild card domain if one is specified.  The wild card domain is
+/// specified as a domain whose name is "*".
+/// As matching capabilities evolve this class is expected to expand.
+class DdnsDomainListMgr {
+public:
+    /// @brief defines the domain name for denoting the wildcard domain.
+    static const char* wildcard_domain_name_;
+
+    /// @brief Constructor
+    ///
+    /// @param name is an arbitrary label assigned to this manager.
+    DdnsDomainListMgr(const std::string& name);
+
+    /// @brief Destructor
+    virtual ~DdnsDomainListMgr ();
+
+    /// @brief Matches a given name to a domain based on a longest match
+    /// scheme.
+    ///
+    /// Given a FQDN, search the list of domains, successively removing a
+    /// sub-domain from the FQDN until a match is found.  If no match is found
+    /// and the wild card domain is present in the list, then return it as the
+    /// match.  If the wild card domain is the only domain in the list, then
+    /// it will be returned immediately for any FQDN.
+    ///
+    /// @param fqdn is the name for which to look.
+    /// @param domain receives the matching domain. Note that it will be reset
+    /// upon entry and only set if a match is subsequently found.
+    ///
+    /// @return returns true if a match is found, false otherwise.
+    /// @todo This is a very basic match method, which expects valid FQDNs
+    /// both as input and for the DdnsDomain::getName().  Currently both are
+    /// simple strings and there is no normalization (i.e. added trailing dots
+    /// if missing).
+    virtual bool matchDomain(const std::string& fqdn, DdnsDomainPtr& domain);
+
+    /// @brief Fetches the manager's name.
+    ///
+    /// @return returns a std::string containing the name of the manager.
+    const std::string getName() const {
+        return (name_);
+    }
+
+    /// @brief Returns the number of domains in the domain list.
+    ///
+    /// @brief returns an unsigned int containing the domain count.
+    uint32_t size() const {
+        return (domains_->size());
+    }
+
+    /// @brief Fetches the wild card domain.
+    ///
+    /// @return returns a pointer reference to the domain.  The pointer will
+    /// empty if the wild card domain is not present.
+    const DdnsDomainPtr& getWildcardDomain() {
+        return (wildcard_domain_);
+    }
+
+    /// @brief Fetches the domain list.
+    ///
+    /// @return returns a pointer reference to the list of domains.
+    const DdnsDomainMapPtr &getDomains() {
+        return (domains_);
+    }
+
+    /// @brief Sets the manger's domain list to the given list of domains.
+    /// This method will scan the inbound list for the wild card domain and
+    /// set the internal wild card domain pointer accordingly.
+    void setDomains(DdnsDomainMapPtr domains);
+
+private:
+    /// @brief An arbitrary label assigned to this manager.
+    std::string name_;
+
+    /// @brief Map of the domains, keyed by name.
+    DdnsDomainMapPtr domains_;
+
+    /// @brief Pointer to the wild card domain.
+    DdnsDomainPtr wildcard_domain_;
+};
+
+/// @brief Defines a pointer for DdnsDomain instances.
+typedef boost::shared_ptr<DdnsDomainListMgr> DdnsDomainListMgrPtr;
+
+/// @brief Storage container for scalar configuration parameters.
+///
+/// This class is useful for implementing parsers for more complex configuration
+/// elements (e.g. those of item type "map").  It provides a convenient way to
+/// add storage to the parser for an arbitrary number and variety of scalar
+/// configuration items (e.g. ints, bools, strings...) without explicitly adding
+/// storage for each individual type needed by the parser.
+///
+/// This class implements a concrete version of the base class by supplying a
+/// "clone" method.
+class DScalarContext : public DCfgContextBase {
+public:
+
+    /// @brief Constructor
+    DScalarContext() {
+    };
+
+    /// @brief Destructor
+    virtual ~DScalarContext() {
+    }
+
+    /// @brief Creates a clone of a DStubContext.
+    ///
+    /// @return returns a pointer to the new clone.
+    virtual DCfgContextBasePtr clone() {
+        return (DCfgContextBasePtr(new DScalarContext(*this)));
+    }
+
+protected:
+    /// @brief Copy constructor
+    DScalarContext(const DScalarContext& rhs) : DCfgContextBase(rhs) {
+    }
+
+private:
+    /// @brief Private assignment operator, not implemented.
+    DScalarContext& operator=(const DScalarContext& rhs);
+};
+
+/// @brief Defines a pointer for DScalarContext instances.
+typedef boost::shared_ptr<DScalarContext> DScalarContextPtr;
+
+/// @brief Parser for  TSIGKeyInfo
+///
+/// This class parses the configuration element "tsig_key" defined in
+/// src/bin/d2/dhcp-ddns.spec and creates an instance of a TSIGKeyInfo.
+class TSIGKeyInfoParser : public  isc::dhcp::DhcpConfigParser {
+public:
+    /// @brief Constructor
+    ///
+    /// @param entry_name is an arbitrary label assigned to this configuration
+    /// definition. Since servers are specified in a list this value is likely
+    /// be something akin to "key:0", set during parsing.
+    /// @param keys is a pointer to the storage area to which the parser
+    /// should commit the newly created TSIGKeyInfo instance.
+    TSIGKeyInfoParser(const std::string& entry_name, TSIGKeyInfoMapPtr keys);
+
+    /// @brief Destructor
+    virtual ~TSIGKeyInfoParser();
+
+    /// @brief Performs the actual parsing of the given  "tsig_key" element.
+    ///
+    /// The results of the parsing are retained internally for use during
+    /// commit.
+    ///
+    /// @param key_config is the "tsig_key" configuration to parse
+    virtual void build(isc::data::ConstElementPtr key_config);
+
+    /// @brief Creates a parser for the given "tsig_key" member element id.
+    ///
+    /// The key elements currently supported are(see dhcp-ddns.spec):
+    ///   1. name
+    ///   2. algorithm
+    ///   3. secret
+    ///
+    /// @param config_id is the "item_name" for a specific member element of
+    /// the "tsig_key" specification.
+    ///
+    /// @return returns a pointer to newly created parser.
+    virtual isc::dhcp::ParserPtr createConfigParser(const std::string&
+                                                    config_id);
+
+    /// @brief Instantiates a DnsServerInfo from internal data values
+    /// saves it to the storage area pointed to by servers_.
+    virtual void commit();
+
+private:
+    /// @brief Arbitrary label assigned to this parser instance.
+    /// Since servers are specified in a list this value is likely be something
+    /// akin to "key:0", set during parsing.  Primarily here for diagnostics.
+    std::string entry_name_;
+
+    /// @brief Pointer to the storage area to which the parser should commit
+    /// the newly created TSIGKeyInfo instance. This is given to us as a
+    /// constructor argument by an upper level.
+    TSIGKeyInfoMapPtr keys_;
+
+    /// @brief Local storage area for scalar parameter values. Use to hold
+    /// data until time to commit.
+    DScalarContext local_scalars_;
+};
+
+/// @brief Parser for a list of TSIGKeyInfos
+///
+/// This class parses a list of "tsig_key" configuration elements.
+/// (see src/bin/d2/dhcp-ddns.spec). The TSIGKeyInfo instances are added
+/// to the given storage upon commit.
+class TSIGKeyInfoListParser : public isc::dhcp::DhcpConfigParser {
+public:
+
+    /// @brief Constructor
+    ///
+    /// @param list_name is an arbitrary label assigned to this parser instance.
+    /// @param keys is a pointer to the storage area to which the parser
+    /// should commit the newly created TSIGKeyInfo instance.
+    TSIGKeyInfoListParser(const std::string& list_name, TSIGKeyInfoMapPtr keys);
+
+    /// @brief Destructor
+    virtual ~TSIGKeyInfoListParser();
+
+    /// @brief Performs the parsing of the given list "tsig_key" elements.
+    ///
+    /// It iterates over each key entry in the list:
+    ///   1. Instantiate a TSIGKeyInfoParser for the entry
+    ///   2. Pass the element configuration to the parser's build method
+    ///   3. Add the parser instance to local storage
+    ///
+    /// The net effect is to parse all of the key entries in the list
+    /// prepping them for commit.
+    ///
+    /// @param key_list_config is the list of "tsig_key" elements to parse.
+    virtual void build(isc::data::ConstElementPtr key_list_config);
+
+    /// @brief Iterates over the internal list of TSIGKeyInfoParsers,
+    /// invoking commit on each.  This causes each parser to instantiate a
+    /// TSIGKeyInfo from its internal data values and add that that key
+    /// instance to the storage area, keys_.
+    virtual void commit();
+
+private:
+    /// @brief Arbitrary label assigned to this parser instance.
+    std::string list_name_;
+
+    /// @brief Pointer to the storage area to which the parser should commit
+    /// the list of newly created TSIGKeyInfo instances. This is given to us
+    /// as a constructor argument by an upper level.
+    TSIGKeyInfoMapPtr keys_;
+
+    /// @brief Local storage of TSIGKeyInfoParser instances
+    isc::dhcp::ParserCollection parsers_;
+};
+
+/// @brief Parser for  DnsServerInfo
+///
+/// This class parses the configuration element "dns_server" defined in
+/// src/bin/d2/dhcp-ddns.spec and creates an instance of a DnsServerInfo.
+class DnsServerInfoParser : public  isc::dhcp::DhcpConfigParser {
+public:
+    /// @brief Constructor
+    ///
+    /// @param entry_name is an arbitrary label assigned to this configuration
+    /// definition. Since servers are specified in a list this value is likely
+    /// be something akin to "server:0", set during parsing.
+    /// @param servers is a pointer to the storage area to which the parser
+    /// should commit the newly created DnsServerInfo instance.
+    DnsServerInfoParser(const std::string& entry_name,
+                        DnsServerInfoStoragePtr servers);
+
+    /// @brief Destructor
+    virtual ~DnsServerInfoParser();
+
+    /// @brief Performs the actual parsing of the given  "dns_server" element.
+    /// The results of the parsing are retained internally for use during
+    /// commit.
+    ///
+    /// @param server_config is the "dns_server" configuration to parse
+    virtual void build(isc::data::ConstElementPtr server_config);
+
+    /// @brief Creates a parser for the given "dns_server" member element id.
+    ///
+    /// The server elements currently supported are(see dhcp-ddns.spec):
+    ///   1. hostname
+    ///   2. ip_address
+    ///   3. port
+    ///
+    /// @param config_id is the "item_name" for a specific member element of
+    /// the "dns_server" specification.
+    ///
+    /// @return returns a pointer to newly created parser.
+    virtual isc::dhcp::ParserPtr createConfigParser(const std::string&
+                                                    config_id);
+
+    /// @brief Instantiates a DnsServerInfo from internal data values
+    /// saves it to the storage area pointed to by servers_.
+    virtual void commit();
+
+private:
+    /// @brief Arbitrary label assigned to this parser instance.
+    /// Since servers are specified in a list this value is likely be something
+    /// akin to "server:0", set during parsing.  Primarily here for diagnostics.
+    std::string entry_name_;
+
+    /// @brief Pointer to the storage area to which the parser should commit
+    /// the newly created DnsServerInfo instance. This is given to us as a
+    /// constructor argument by an upper level.
+    DnsServerInfoStoragePtr servers_;
+
+    /// @brief Local storage area for scalar parameter values. Use to hold
+    /// data until time to commit.
+    DScalarContext local_scalars_;
+};
+
+/// @brief Parser for a list of DnsServerInfos
+///
+/// This class parses a list of "dns_server" configuration elements.
+/// (see src/bin/d2/dhcp-ddns.spec). The DnsServerInfo instances are added
+/// to the given storage upon commit.
+class DnsServerInfoListParser : public isc::dhcp::DhcpConfigParser {
+public:
+
+    /// @brief Constructor
+    ///
+    /// @param list_name is an arbitrary label assigned to this parser instance.
+    /// @param servers is a pointer to the storage area to which the parser
+    /// should commit the newly created DnsServerInfo instance.
+    DnsServerInfoListParser(const std::string& list_name,
+                            DnsServerInfoStoragePtr servers);
+
+    /// @brief Destructor
+    virtual ~DnsServerInfoListParser();
+
+    /// @brief Performs the actual parsing of the given list "dns_server"
+    /// elements.
+    /// It iterates over each server entry in the list:
+    ///   1. Instantiate a DnsServerInfoParser for the entry
+    ///   2. Pass the element configuration to the parser's build method
+    ///   3. Add the parser instance to local storage
+    ///
+    /// The net effect is to parse all of the server entries in the list
+    /// prepping them for commit.
+    ///
+    /// @param server_list_config is the list of "dns_server" elements to parse.
+    virtual void build(isc::data::ConstElementPtr server_list_config);
+
+    /// @brief Iterates over the internal list of DnsServerInfoParsers,
+    /// invoking commit on each.  This causes each parser to instantiate a
+    /// DnsServerInfo from its internal data values and add that that server
+    /// instance to the storage area, servers_.
+    virtual void commit();
+
+private:
+    /// @brief Arbitrary label assigned to this parser instance.
+    std::string list_name_;
+
+    /// @brief Pointer to the storage area to which the parser should commit
+    /// the list of newly created DnsServerInfo instances. This is given to us
+    /// as a constructor argument by an upper level.
+    DnsServerInfoStoragePtr servers_;
+
+    /// @brief Local storage of DnsServerInfoParser instances
+    isc::dhcp::ParserCollection parsers_;
+};
+
+/// @brief Parser for  DdnsDomain
+///
+/// This class parses the configuration element "ddns_domain" defined in
+/// src/bin/d2/dhcp-ddns.spec and creates an instance of a DdnsDomain.
+class DdnsDomainParser : public isc::dhcp::DhcpConfigParser {
+public:
+    /// @brief Constructor
+    ///
+    /// @param entry_name is an arbitrary label assigned to this configuration
+    /// definition. Since domains are specified in a list this value is likely
+    /// be something akin to "forward_ddns:0", set during parsing.
+    /// @param domains is a pointer to the storage area to which the parser
+    /// @param keys is a pointer to a map of the defined TSIG keys.
+    /// should commit the newly created DdnsDomain instance.
+    DdnsDomainParser(const std::string& entry_name, DdnsDomainMapPtr domains,
+                     TSIGKeyInfoMapPtr keys);
+
+    /// @brief Destructor
+    virtual ~DdnsDomainParser();
+
+    /// @brief Performs the actual parsing of the given  "ddns_domain" element.
+    /// The results of the parsing are retained internally for use during
+    /// commit.
+    ///
+    /// @param domain_config is the "ddns_domain" configuration to parse
+    virtual void build(isc::data::ConstElementPtr domain_config);
+
+    /// @brief Creates a parser for the given "ddns_domain" member element id.
+    ///
+    /// The domain elements currently supported are(see dhcp-ddns.spec):
+    ///   1. name
+    ///   2. key_name
+    ///   3. dns_servers
+    ///
+    /// @param config_id is the "item_name" for a specific member element of
+    /// the "ddns_domain" specification.
+    ///
+    /// @return returns a pointer to newly created parser.
+    virtual isc::dhcp::ParserPtr createConfigParser(const std::string&
+                                                    config_id);
+
+    /// @brief Instantiates a DdnsDomain from internal data values
+    /// saves it to the storage area pointed to by domains_.
+    virtual void commit();
+
+private:
+
+    /// @brief Arbitrary label assigned to this parser instance.
+    std::string entry_name_;
+
+    /// @brief Pointer to the storage area to which the parser should commit
+    /// the newly created DdnsDomain instance. This is given to us as a
+    /// constructor argument by an upper level.
+    DdnsDomainMapPtr domains_;
+
+    /// @brief Pointer to the map of defined TSIG keys.
+    /// This map is passed into us and contains all of the TSIG keys defined
+    /// for this configuration.  It is used to validate the key name entry of
+    /// DdnsDomains that specify one.
+    TSIGKeyInfoMapPtr keys_;
+
+    /// @brief Local storage for DnsServerInfo instances. This is passed into
+    /// DnsServerInfoListParser(s), which in turn passes it into each
+    /// DnsServerInfoParser.  When the DnsServerInfoParsers "commit" they add
+    /// their server instance to this storage.
+    DnsServerInfoStoragePtr local_servers_;
+
+    /// @brief Local storage area for scalar parameter values. Use to hold
+    /// data until time to commit.
+    DScalarContext local_scalars_;
+};
+
+/// @brief Parser for a list of DdnsDomains
+///
+/// This class parses a list of "ddns_domain" configuration elements.
+/// (see src/bin/d2/dhcp-ddns.spec). The DdnsDomain instances are added
+/// to the given storage upon commit.
+class DdnsDomainListParser : public isc::dhcp::DhcpConfigParser {
+public:
+
+    /// @brief Constructor
+    ///
+    /// @param list_name is an arbitrary label assigned to this parser instance.
+    /// @param domains is a pointer to the storage area to which the parser
+    /// @param keys is a pointer to a map of the defined TSIG keys.
+    /// should commit the newly created DdnsDomain instance.
+    DdnsDomainListParser(const std::string& list_name,
+                         DdnsDomainMapPtr domains, TSIGKeyInfoMapPtr keys);
+
+    /// @brief Destructor
+    virtual ~DdnsDomainListParser();
+
+    /// @brief Performs the actual parsing of the given list "ddns_domain"
+    /// elements.
+    /// It iterates over each server entry in the list:
+    ///   1. Instantiate a DdnsDomainParser for the entry
+    ///   2. Pass the element configuration to the parser's build method
+    ///   3. Add the parser instance to local storage
+    ///
+    /// The net effect is to parse all of the domain entries in the list
+    /// prepping them for commit.
+    ///
+    /// @param domain_list_config is the list of "ddns_domain" elements to
+    /// parse.
+    virtual void build(isc::data::ConstElementPtr domain_list_config);
+
+    /// @brief Iterates over the internal list of DdnsDomainParsers, invoking
+    /// commit on each.  This causes each parser to instantiate a DdnsDomain
+    /// from its internal data values and add that domain instance to the
+    /// storage area, domains_.
+    virtual void commit();
+
+private:
+    /// @brief Arbitrary label assigned to this parser instance.
+    std::string list_name_;
+
+    /// @brief Pointer to the storage area to which the parser should commit
+    /// the list of newly created DdnsDomain instances. This is given to us
+    /// as a constructor argument by an upper level.
+    DdnsDomainMapPtr domains_;
+
+    /// @brief Pointer to the map of defined TSIG keys.
+    /// This map is passed into us and contains all of the TSIG keys defined
+    /// for this configuration.  It is used to validate the key name entry of
+    /// DdnsDomains that specify one.
+    TSIGKeyInfoMapPtr keys_;
+
+    /// @brief Local storage of DdnsDomainParser instances
+    isc::dhcp::ParserCollection parsers_;
+};
+
+/// @brief Parser for DdnsDomainListMgr
+///
+/// This class parses the configuration elements "forward_ddns" and
+/// "reverse_ddns" as defined in src/bin/d2/dhcp-ddns.spec.  It populates the
+/// given DdnsDomainListMgr with parsed information upon commit.  Note that
+/// unlike other parsers, this parser does NOT instantiate the final object
+/// during the commit phase, it populates it.  It must pre-exist.
+class DdnsDomainListMgrParser : public isc::dhcp::DhcpConfigParser {
+public:
+    /// @brief Constructor
+    ///
+    /// @param entry_name is an arbitrary label assigned to this configuration
+    /// definition.
+    /// @param mgr is a pointer to the DdnsDomainListMgr to populate.
+    /// @param keys is a pointer to a map of the defined TSIG keys.
+    /// @throw throws D2CfgError if mgr pointer is empty.
+    DdnsDomainListMgrParser(const std::string& entry_name,
+                     DdnsDomainListMgrPtr mgr, TSIGKeyInfoMapPtr keys);
+
+    /// @brief Destructor
+    virtual ~DdnsDomainListMgrParser();
+
+    /// @brief Performs the actual parsing of the given manager element.
+    /// The results of the parsing are retained internally for use during
+    /// commit.
+    ///
+    /// @param mgr_config is the manager configuration to parse
+    virtual void build(isc::data::ConstElementPtr mgr_config);
+
+    /// @brief Creates a parser for the given manager member element id.
+    ///
+    /// The manager elements currently supported are (see dhcp-ddns.spec):
+    ///     1. ddns_domains
+    ///
+    /// @param config_id is the "item_name" for a specific member element of
+    /// the manager specification.
+    ///
+    /// @return returns a pointer to newly created parser.
+    virtual isc::dhcp::ParserPtr createConfigParser(const std::string&
+                                                    config_id);
+
+    /// @brief Populates the DdnsDomainListMgr from internal data values
+    /// set during parsing.
+    virtual void commit();
+
+private:
+    /// @brief Arbitrary label assigned to this parser instance.
+    std::string entry_name_;
+
+    /// @brief Pointer to manager instance to which the parser should commit
+    /// the parsed data. This is given to us as a constructor argument by an
+    /// upper level.
+    DdnsDomainListMgrPtr mgr_;
+
+    /// @brief Pointer to the map of defined TSIG keys.
+    /// This map is passed into us and contains all of the TSIG keys defined
+    /// for this configuration.  It is used to validate the key name entry of
+    /// DdnsDomains that specify one.
+    TSIGKeyInfoMapPtr keys_;
+
+    /// @brief Local storage for DdnsDomain instances. This is passed into a
+    /// DdnsDomainListParser(s), which in turn passes it into each
+    /// DdnsDomainParser.  When the DdnsDomainParsers "commit" they add their
+    /// domain instance to this storage.
+    DdnsDomainMapPtr local_domains_;
+
+    /// @brief Local storage area for scalar parameter values. Use to hold
+    /// data until time to commit.
+    /// @todo Currently, the manager has no scalars but this is likely to
+    /// change as matching capabilities expand.
+    DScalarContext local_scalars_;
+};
+
+
+}; // end of isc::d2 namespace
+}; // end of isc namespace
+
+#endif // D2_CONFIG_H
diff --git a/src/bin/d2/d2_controller.h b/src/bin/d2/d2_controller.h
index 5ee15b1..0290f87 100644
--- a/src/bin/d2/d2_controller.h
+++ b/src/bin/d2/d2_controller.h
@@ -24,7 +24,7 @@ namespace d2 {
 /// This class is the DHCP-DDNS specific derivation of DControllerBase. It
 /// creates and manages an instance of the DHCP-DDNS application process,
 /// D2Process.
-/// @TODO Currently, this class provides only the minimum required specialized
+/// @todo Currently, this class provides only the minimum required specialized
 /// behavior to run the DHCP-DDNS service. It may very well expand as the
 /// service implementation evolves.  Some thought was given to making
 /// DControllerBase a templated class but the labor savings versus the
diff --git a/src/bin/d2/d2_messages.mes b/src/bin/d2/d2_messages.mes
index 27778dc..d2358ce 100644
--- a/src/bin/d2/d2_messages.mes
+++ b/src/bin/d2/d2_messages.mes
@@ -26,11 +26,22 @@ to establish a session with the BIND10 control channel.
 A debug message listing the command (and possible arguments) received
 from the BIND10 control system by the controller.
 
+% DCTL_CONFIG_COMPLETE 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.
+
 % DCTL_CONFIG_LOAD_FAIL %1 configuration failed to load: %2
 This critical error message indicates that the initial application
 configuration has failed. The service will start, but will not
 process requests until the configuration has been corrected.
 
+% DCTL_CONFIG_START parsing new configuration: %1
+A debug message indicating that the application process has received an
+updated configuration and has passed it to its configuration manager
+for parsing.
+
 % DCTL_CONFIG_STUB %1 configuration stub handler called
 This debug message is issued when the dummy handler for configuration
 events is called.  This only happens during initial startup.
@@ -55,7 +66,24 @@ application and will exit.
 
 % DCTL_NOT_RUNNING %1 application instance is not running
 A warning message is issued when an attempt is made to shut down the
-the application when it is not running.
+application when it is not running.
+
+% DCTL_ORDER_ERROR configuration contains more elements than the parsing order
+An error message which indicates that configuration being parsed includes
+element ids not specified the configuration manager's parse order list. This 
+is a programmatic error.
+
+% DCTL_ORDER_NO_ELEMENT element: %1 is in the parsing order but is missing from the configuration
+An error message output during a configuration update.  The program is
+expecting an item but has not found it in the new configuration.  This may
+mean that the BIND 10 configuration database is corrupt.
+
+% DCTL_PARSER_FAIL configuration parsing failed for configuration element: %1, reason: %2 
+On receipt of message containing details to a change of its configuration,
+the server failed to create a parser to decode the contents of the named
+configuration element, or the creation succeeded but the parsing actions
+and committal of changes failed.  The reason for the failure is given in
+the message.
 
 % DCTL_PROCESS_FAILED %1 application execution failed: %2
 The controller has encountered a fatal error while running the
@@ -96,6 +124,16 @@ has been invoked.
 This is a debug message issued when the Dhcp-Ddns application encounters an
 unrecoverable error from within the event loop.
 
+% DHCP_DDNS_NO_MATCH No DNS servers match FQDN: %1
+This is warning message issued when there are no domains in the configuration
+which match the cited fully qualified domain name (FQDN).  The DNS Update 
+request for the FQDN cannot be processed.
+
+% DHCP_DDNS_INVALID_RESPONSE received response to DNS Update message is malformed: %1
+This is a debug message issued when the DHCP-DDNS application encountered an error
+while decoding a response to DNS Update message. Typically, this error will be
+encountered when a response message is malformed.
+
 % DHCP_DDNS_PROCESS_INIT application init invoked
 This is a debug message issued when the Dhcp-Ddns application enters
 its init method.
diff --git a/src/bin/d2/d2_process.cc b/src/bin/d2/d2_process.cc
index 130bcc1..74cb93c 100644
--- a/src/bin/d2/d2_process.cc
+++ b/src/bin/d2/d2_process.cc
@@ -14,6 +14,7 @@
 
 #include <config/ccsession.h>
 #include <d2/d2_log.h>
+#include <d2/d2_cfg_mgr.h>
 #include <d2/d2_process.h>
 
 using namespace asio;
@@ -22,7 +23,7 @@ namespace isc {
 namespace d2 {
 
 D2Process::D2Process(const char* name, IOServicePtr io_service)
-    : DProcessBase(name, io_service) {
+    : DProcessBase(name, io_service, DCfgMgrBasePtr(new D2CfgMgr())) {
 };
 
 void
@@ -60,23 +61,23 @@ D2Process::shutdown() {
 
 isc::data::ConstElementPtr
 D2Process::configure(isc::data::ConstElementPtr config_set) {
-    // @TODO This is the initial implementation which simply accepts
-    // any content in config_set as valid.  This is sufficient to
-    // allow participation as a BIND10 module, while D2 configuration support
-    // is being developed.
+    // @todo This is the initial implementation passes the configuration onto
+    // the D2CfgMgr.  There may be additional steps taken added to handle
+    // configuration changes but for now, assume that D2CfgMgr is handling it
+    // all. 
     LOG_DEBUG(dctl_logger, DBGLVL_TRACE_BASIC,
               DHCP_DDNS_CONFIGURE).arg(config_set->str());
 
-    return (isc::config::createAnswer(0, "Configuration accepted."));
+    return (getCfgMgr()->parseConfig(config_set));
 }
 
 isc::data::ConstElementPtr
 D2Process::command(const std::string& command, isc::data::ConstElementPtr args){
-    // @TODO This is the initial implementation.  If and when D2 is extended
+    // @todo This is the initial implementation.  If and when D2 is extended
     // to support its own commands, this implementation must change. Otherwise
     // it should reject all commands as it does now.
-    LOG_DEBUG(dctl_logger, DBGLVL_TRACE_BASIC,
-              DHCP_DDNS_COMMAND).arg(command).arg(args->str());
+    LOG_DEBUG(dctl_logger, DBGLVL_TRACE_BASIC, DHCP_DDNS_COMMAND)
+        .arg(command).arg(args ? args->str() : "(no args)");
 
     return (isc::config::createAnswer(COMMAND_INVALID, "Unrecognized command: "
                                       + command));
diff --git a/src/bin/d2/d2_process.h b/src/bin/d2/d2_process.h
index 4ddf8be..6f50f0c 100644
--- a/src/bin/d2/d2_process.h
+++ b/src/bin/d2/d2_process.h
@@ -41,7 +41,7 @@ public:
     D2Process(const char* name, IOServicePtr io_service);
 
     /// @brief Will be used after instantiation to perform initialization
-    /// unique to D2. @TODO This will likely include interactions with
+    /// unique to D2. @todo This will likely include interactions with
     /// QueueMgr and UpdateMgr, to prepare for request receipt and processing.
     /// Current implementation successfully does nothing.
     /// @throw throws a DProcessBaseError if the initialization fails.
@@ -58,7 +58,7 @@ public:
     /// @brief Implements the process's shutdown processing. When invoked, it
     /// should ensure that the process gracefully exits the run method.
     /// Current implementation simply sets the shutdown flag monitored by the
-    /// run method. @TODO this may need to expand as the implementation evolves.
+    /// run method. @todo this may need to expand as the implementation evolves.
     /// @throw throws a DProcessBaseError if an error is encountered.
     virtual void shutdown();
 
@@ -85,7 +85,7 @@ public:
     ///
     /// @param command is a string label representing the command to execute.
     /// @param args is a set of arguments (if any) required for the given
-    /// command.
+    /// command. It can be a NULL pointer if no arguments exist for a command.
     /// @return an Element that contains the results of command composed
     /// of an integer status value (0 means successful, non-zero means failure),
     /// and a string explanation of the outcome.
diff --git a/src/bin/d2/d2_update_message.cc b/src/bin/d2/d2_update_message.cc
new file mode 100644
index 0000000..71fb9f3
--- /dev/null
+++ b/src/bin/d2/d2_update_message.cc
@@ -0,0 +1,221 @@
+// Copyright (C) 2013  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 <d2/d2_update_message.h>
+#include <dns/messagerenderer.h>
+#include <dns/name.h>
+#include <dns/opcode.h>
+#include <dns/question.h>
+
+namespace isc {
+namespace d2 {
+
+using namespace isc::dns;
+
+D2UpdateMessage::D2UpdateMessage(const Direction direction)
+    : message_(direction == INBOUND ?
+               dns::Message::PARSE : dns::Message::RENDER) {
+    // If this object is to create an outgoing message, we have to
+    // set the proper Opcode field and QR flag here.
+    if (direction == OUTBOUND) {
+        message_.setOpcode(Opcode(Opcode::UPDATE_CODE));
+        message_.setHeaderFlag(dns::Message::HEADERFLAG_QR, false);
+
+    }
+}
+
+D2UpdateMessage::QRFlag
+D2UpdateMessage::getQRFlag() const {
+    return (message_.getHeaderFlag(dns::Message::HEADERFLAG_QR) ?
+            RESPONSE : REQUEST);
+}
+
+uint16_t
+D2UpdateMessage::getId() const {
+    return (message_.getQid());
+}
+
+void
+D2UpdateMessage::setId(const uint16_t id) {
+    message_.setQid(id);
+}
+
+
+const dns::Rcode&
+D2UpdateMessage::getRcode() const {
+    return (message_.getRcode());
+}
+
+void
+D2UpdateMessage::setRcode(const dns::Rcode& rcode) {
+    message_.setRcode(rcode);
+}
+
+unsigned int
+D2UpdateMessage::getRRCount(const UpdateMsgSection section) const {
+    return (message_.getRRCount(ddnsToDnsSection(section)));
+}
+
+const dns::RRsetIterator
+D2UpdateMessage::beginSection(const UpdateMsgSection section) const {
+    return (message_.beginSection(ddnsToDnsSection(section)));
+}
+
+const dns::RRsetIterator
+D2UpdateMessage::endSection(const UpdateMsgSection section) const {
+    return (message_.endSection(ddnsToDnsSection(section)));
+}
+
+void
+D2UpdateMessage::setZone(const Name& zone, const RRClass& rrclass) {
+    // The Zone data is kept in the underlying Question class. If there
+    // is a record stored there already, we need to remove it, because
+    // we may have at most one Zone record in the DNS Update message.
+    if (message_.getRRCount(dns::Message::SECTION_QUESTION) > 0) {
+        message_.clearSection(dns::Message::SECTION_QUESTION);
+    }
+    // Add the new record...
+    Question question(zone, rrclass, RRType::SOA());
+    message_.addQuestion(question);
+    // ... and update the local class member holding the D2Zone object.
+    zone_.reset(new D2Zone(question.getName(), question.getClass()));
+}
+
+D2ZonePtr
+D2UpdateMessage::getZone() const {
+    return (zone_);
+}
+
+void
+D2UpdateMessage::addRRset(const UpdateMsgSection section,
+                          const dns::RRsetPtr& rrset) {
+    if (section == SECTION_ZONE) {
+        isc_throw(isc::BadValue, "unable to add RRset to the Zone section"
+                  " of the DNS Update message, use setZone instead");
+    }
+    message_.addRRset(ddnsToDnsSection(section), rrset);
+}
+
+void
+D2UpdateMessage::toWire(AbstractMessageRenderer& renderer) {
+    // We are preparing the wire format of the message, meaning
+    // that this message will be sent as a request to the DNS.
+    // Therefore, we expect that this message is a REQUEST.
+    if (getQRFlag() != REQUEST) {
+        isc_throw(InvalidQRFlag, "QR flag must be cleared for the outgoing"
+                  " DNS Update message");
+    }
+    // According to RFC2136, the ZONE section may contain exactly one
+    // record.
+    if (getRRCount(SECTION_ZONE) != 1) {
+        isc_throw(InvalidZoneSection, "Zone section of the DNS Update message"
+                  " must comprise exactly one record (RFC2136, section 2.3)");
+    }
+    message_.toWire(renderer);
+}
+
+void
+D2UpdateMessage::fromWire(isc::util::InputBuffer& buffer) {
+    // First, use the underlying dns::Message implementation to get the
+    // contents of the DNS response message. Note that it may or may
+    // not be the message that we are interested in, but needs to be
+    // parsed so as we can check its ID, Opcode etc.
+    message_.fromWire(buffer);
+    // This class exposes the getZone() function. This function will return
+    // pointer to the D2Zone object if non-empty Zone section exists in the
+    // received message. It will return NULL pointer if it doesn't exist.
+    // The pointer is held in the D2UpdateMessage class member. We need to
+    // update this pointer every time we parse the message.
+    if (getRRCount(D2UpdateMessage::SECTION_ZONE) > 0) {
+        // There is a Zone section in the received message. Replace
+        // Zone pointer with the new value.
+        QuestionPtr question = *message_.beginQuestion();
+        // If the Zone counter is greater than 0 (which we have checked)
+        // there must be a valid Question pointer stored in the message_
+        // object. If there isn't, it is a programming error.
+        assert(question);
+        zone_.reset(new D2Zone(question->getName(), question->getClass()));
+
+    } else {
+        // Zone section doesn't hold any pointers, so set the pointer to NULL.
+        zone_.reset();
+
+    }
+    // Check that the content of the received message is sane.
+    // One of the basic checks to do is to verify that we have
+    // received the DNS update message. If not, it can be dropped
+    // or an error message can be printed. Other than that, we
+    // will check that there is at most one Zone record and QR flag
+    // is set.
+    validateResponse();
+}
+
+dns::Message::Section
+D2UpdateMessage::ddnsToDnsSection(const UpdateMsgSection section) {
+    /// The following switch maps the enumerator values from the
+    /// DNS Update message to the corresponding enumerator values
+    /// representing fields of the DNS message.
+    switch(section) {
+    case SECTION_ZONE :
+        return (dns::Message::SECTION_QUESTION);
+
+    case SECTION_PREREQUISITE:
+        return (dns::Message::SECTION_ANSWER);
+
+    case SECTION_UPDATE:
+        return (dns::Message::SECTION_AUTHORITY);
+
+    case SECTION_ADDITIONAL:
+        return (dns::Message::SECTION_ADDITIONAL);
+
+    default:
+        ;
+    }
+    isc_throw(dns::InvalidMessageSection,
+              "unknown message section " << section);
+}
+
+void
+D2UpdateMessage::validateResponse() const {
+    // Verify that we are dealing with the DNS Update message. According to
+    // RFC 2136, section 3.8 server will copy the Opcode from the query.
+    // If we are dealing with a different type of message, we may simply
+    // stop further processing, because it is likely that the message was
+    // directed to someone else.
+    if (message_.getOpcode() != Opcode::UPDATE()) {
+        isc_throw(NotUpdateMessage, "received message is not a DDNS update,"
+                  << " received message code is "
+                  << message_.getOpcode().getCode());
+    }
+    // Received message should have QR flag set, which indicates that it is
+    // a RESPONSE.
+    if (getQRFlag() == REQUEST) {
+        isc_throw(InvalidQRFlag, "received message should have QR flag set,"
+                  " to indicate that it is a RESPONSE message; the QR"
+                  << " flag in received message is unset");
+    }
+    // DNS server may copy a Zone record from the query message. Since query
+    // must comprise exactly one Zone record (RFC 2136, section 2.3), the
+    // response message may contain 1 record at most. It may also contain no
+    // records if a server chooses not to copy Zone section.
+    if (getRRCount(SECTION_ZONE) > 1) {
+        isc_throw(InvalidZoneSection, "received message contains "
+                  << getRRCount(SECTION_ZONE) << " Zone records,"
+                  << " it should contain at most 1 record");
+    }
+}
+
+} // namespace d2
+} // namespace isc
+
diff --git a/src/bin/d2/d2_update_message.h b/src/bin/d2/d2_update_message.h
new file mode 100644
index 0000000..955e5c0
--- /dev/null
+++ b/src/bin/d2/d2_update_message.h
@@ -0,0 +1,341 @@
+// Copyright (C) 2013 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 D2_UPDATE_MESSAGE_H
+#define D2_UPDATE_MESSAGE_H
+
+#include <d2/d2_zone.h>
+#include <dns/message.h>
+#include <dns/name.h>
+#include <dns/rcode.h>
+#include <dns/rrclass.h>
+#include <dns/rrset.h>
+
+#include <map>
+
+namespace isc {
+namespace d2 {
+
+/// @brief Exception indicating that Zone section contains invalid content.
+///
+/// This exception is thrown when ZONE section of the DNS Update message
+/// is invalid. According to RFC2136, section 2.3, the zone section is
+/// allowed to contain exactly one record. When Request message contains
+/// more records or is empty, this exception is thrown.
+class InvalidZoneSection : public Exception {
+public:
+    InvalidZoneSection(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
+/// @brief Exception indicating that QR flag has invalid value.
+///
+/// This exception is thrown when QR flag has invalid value for
+/// the operation performed on the particular message. For instance,
+/// the QR flag must be set to indicate that the given message is
+/// a RESPONSE when @c D2UpdateMessage::fromWire is performed.
+/// The QR flag must be cleared when @c D2UpdateMessage::toWire
+/// is executed.
+class InvalidQRFlag : public Exception {
+public:
+    InvalidQRFlag(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
+/// @brief Exception indicating that the parsed message is not DNS Update.
+///
+/// This exception is thrown when decoding the DNS message which is not
+/// a DNS Update.
+class NotUpdateMessage : public Exception {
+public:
+    NotUpdateMessage(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
+class D2UpdateMessage;
+
+/// @brief Pointer to the DNS Update Message.
+typedef boost::shared_ptr<D2UpdateMessage> D2UpdateMessagePtr;
+
+/// @brief The @c D2UpdateMessage encapsulates a DNS Update message.
+///
+/// This class represents the DNS Update message. Functions exposed by this
+/// class allow to specify the data sections carried by the message and create
+/// an on-wire format of this message. This class is also used to decode
+/// messages received from the DNS server in the on-wire format.
+///
+/// <b>Design choice:</b> A dedicated class has been created to encapsulate
+/// DNS Update message because existing @c isc::dns::Message is designed to
+/// support regular DNS messages (described in RFC 1035) only. Although DNS
+/// Update has the same format, particular sections serve different purposes.
+/// In order to avoid rewrite of significant portions of @c isc::dns::Message
+/// class, this class is implemented in-terms-of @c isc::dns::Message class
+/// to reuse its functionality where possible.
+class D2UpdateMessage {
+public:
+
+    /// @brief Indicates if the @c D2UpdateMessage object encapsulates Inbound
+    /// or Outbound message.
+    enum Direction {
+        INBOUND,
+        OUTBOUND
+    };
+
+    /// @brief Indicates whether DNS Update message is a REQUEST or RESPONSE.
+    enum QRFlag {
+        REQUEST,
+        RESPONSE
+    };
+
+    /// @brief Identifies sections in the DNS Update Message.
+    ///
+    /// Each message comprises message Header and may contain the following
+    /// sections:
+    /// - ZONE
+    /// - PREREQUISITE
+    /// - UPDATE
+    /// - ADDITIONAL
+    ///
+    /// The enum elements are used by functions such as @c getRRCount (to get
+    /// the number of records in a corresponding section) and @c beginSection
+    /// and @c endSection (to access data in the corresponding section).
+    enum UpdateMsgSection {
+        SECTION_ZONE,
+        SECTION_PREREQUISITE,
+        SECTION_UPDATE,
+        SECTION_ADDITIONAL
+    };
+
+public:
+    /// @brief Constructor used to create an instance of the DNS Update Message
+    /// (either outgoing or incoming).
+    ///
+    /// This constructor is used to create an instance of either incoming or
+    /// outgoing DNS Update message. The boolean argument indicates wheteher it
+    /// is incoming (true) or outgoing (false) message. For incoming messages
+    /// the @c D2UpdateMessage::fromWire function is used to parse on-wire data.
+    /// For outgoing messages, modifier functions should be used to set the
+    /// message contents and @c D2UpdateMessage::toWire function to create
+    /// on-wire data.
+    ///
+    /// @param direction indicates if this is an inbound or outbound message.
+    D2UpdateMessage(const Direction direction = OUTBOUND);
+
+    ///
+    /// @name Copy constructor and assignment operator
+    ///
+    /// Copy constructor and assignment operator are private because we assume
+    /// there will be no need to copy messages on the client side.
+    //@{
+private:
+    D2UpdateMessage(const D2UpdateMessage& source);
+    D2UpdateMessage& operator=(const D2UpdateMessage& source);
+    //@}
+
+public:
+
+    /// @brief Returns enum value indicating if the message is a
+    /// REQUEST or RESPONSE
+    ///
+    /// The returned value is REQUEST if the message is created as an outgoing
+    /// message. In such case the QR flag bit in the message header is cleared.
+    /// The returned value is RESPONSE if the message is created as an incoming
+    /// message and the QR flag bit was set in the received message header.
+    ///
+    /// @return An enum value indicating whether the message is a
+    /// REQUEST or RESPONSE.
+    QRFlag getQRFlag() const;
+
+    /// @brief Returns message ID.
+    ///
+    /// @return message ID.
+    uint16_t getId() const;
+
+    /// @brief Sets message ID.
+    ///
+    /// @param id 16-bit value of the message id.
+    void setId(const uint16_t id);
+
+    /// @brief Returns an object representing message RCode.
+    ///
+    /// @return An object representing message RCode.
+    const dns::Rcode& getRcode() const;
+
+    /// @brief Sets message RCode.
+    ///
+    /// @param rcode An object representing message RCode.
+    void setRcode(const dns::Rcode& rcode);
+
+    /// @brief Returns number of RRsets in the specified message section.
+    ///
+    /// @param section An @c UpdateMsgSection enum specifying a message section
+    /// for which the number of RRsets is to be returned.
+    ///
+    /// @return A number of RRsets in the specified message section.
+    unsigned int getRRCount(const UpdateMsgSection section) const;
+
+    /// @name Functions returning iterators to RRsets in message sections.
+    ///
+    //@{
+    /// @brief Return iterators pointing to the beginning of the list of RRsets,
+    /// which belong to the specified section.
+    ///
+    /// @param section An @c UpdateMsgSection enum specifying a message section
+    /// for which the iterator should be returned.
+    ///
+    /// @return An iterator pointing to the beginning of the list of the
+    /// RRsets, which belong to the specified section.
+    const dns::RRsetIterator beginSection(const UpdateMsgSection section) const;
+
+    /// @brief Return iterators pointing to the end of the list of RRsets,
+    /// which belong to the specified section.
+    ///
+    /// @param section An @c UpdateMsgSection enum specifying a message section
+    /// for which the iterator should be returned.
+    ///
+    /// @return An iterator pointing to the end of the list of the
+    /// RRsets, which belong to the specified section.
+    const dns::RRsetIterator endSection(const UpdateMsgSection section) const;
+    //@}
+
+    /// @brief Sets the Zone record.
+    ///
+    /// This function creates the @c D2Zone object, representing a Zone record
+    /// for the outgoing message. If the Zone record is already set, it is
+    /// replaced by the new record being set by this function. The RRType for
+    /// the record is always SOA.
+    ///
+    /// @param zone A name of the zone being updated.
+    /// @param rrclass A class of the zone record.
+    void setZone(const dns::Name& zone, const dns::RRClass& rrclass);
+
+    /// @brief Returns a pointer to the object representing Zone record.
+    ///
+    /// @return A pointer to the object representing Zone record.
+    D2ZonePtr getZone() const;
+
+    /// @brief Adds an RRset to the specified section.
+    ///
+    /// This function may throw exception if the specified section is
+    /// out of bounds or Zone section update is attempted. For Zone
+    /// section @c D2UpdateMessage::setZone function should be used instead.
+    /// Also, this function expects that @c rrset argument is non-NULL.
+    ///
+    /// @param section A message section where the RRset should be added.
+    /// @param rrset A reference to a RRset which should be added.
+    void addRRset(const UpdateMsgSection section, const dns::RRsetPtr& rrset);
+
+    /// @name Functions to handle message encoding and decoding.
+    ///
+    //@{
+    /// @brief Encode outgoing message into wire format.
+    ///
+    /// This function encodes the DNS Update into the wire format. The format of
+    /// such a message is described in the RFC2136, section 2. Some of the
+    /// sections which belong to encoded message may be empty. If a particular
+    /// message section is empty (does not comprise any RRs), the corresponding
+    /// counter in the message header is set to 0. These counters are: PRCOUNT,
+    /// UPCOUNT, ADCOUNT for the Prerequisites, Update RRs and Additional Data
+    /// RRs respectively. The ZOCOUNT must be equal to 1 because RFC2136
+    /// requires that the message comprises exactly one Zone record.
+    ///
+    /// This function does not guarantee exception safety. However, exceptions
+    /// should be rare because @c D2UpdateMessage class API prevents invalid
+    /// use of the class. The typical case, when this function may throw an
+    /// exception is when this it is called on the object representing
+    /// incoming (instead of outgoing) message. In such case, the QR field
+    /// will be set to RESPONSE, which is invalid setting when calling this
+    /// function.
+    ///
+    /// @param renderer A renderer object used to generate the message wire
+    /// format.
+    void toWire(dns::AbstractMessageRenderer& renderer);
+
+    /// @brief Decode incoming message from the wire format.
+    ///
+    /// This function decodes the DNS Update message stored in the buffer
+    /// specified by the function argument. In the first turn, this function
+    /// parses message header and extracts the section counters: ZOCOUNT,
+    /// PRCOUNT, UPCOUNT and ADCOUNT. Using these counters, function identifies
+    /// message sections, which follow message header. These sections can be
+    /// later accessed using: @c D2UpdateMessage::getZone,
+    /// @c D2UpdateMessage::beginSection and @c D2UpdateMessage::endSection
+    /// functions.
+    ///
+    /// This function is NOT exception safe. It signals message decoding errors
+    /// through exceptions. Message decoding error may occur if the received
+    /// message does not conform to the general DNS Message format, specified in
+    /// RFC 1035. Errors which are specific to DNS Update messages include:
+    /// - Invalid Opcode - not an UPDATE.
+    /// - Invalid QR flag - the QR bit should be set to indicate that the
+    /// message is the server response.
+    /// - The number of records in the Zone section is greater than 1.
+    ///
+    /// @param buffer input buffer, holding DNS Update message to be parsed.
+    void fromWire(isc::util::InputBuffer& buffer);
+    //@}
+
+private:
+    /// Maps the values of the @c UpdateMessageSection field to the
+    /// corresponding values in the @c isc::dns::Message class. This
+    /// mapping is required here because this class uses @c isc::dns::Message
+    /// class to do the actual processing of the DNS Update message.
+    ///
+    /// @param section An enum indicating the section for which the
+    /// corresponding  enum value from @c isc::dns::Message will be returned.
+    ///
+    /// @return The enum value indicating the section in the DNS message
+    /// represented by the @c isc::dns::Message class.
+    static
+    dns::Message::Section ddnsToDnsSection(const UpdateMsgSection section);
+
+    /// @brief Checks received response message for correctness.
+    ///
+    /// This function verifies that the received response from a server is
+    /// correct. Currently this function checks the following:
+    /// - Opcode is 'DNS Update',
+    /// - QR flag is RESPONSE (flag bit is set),
+    /// - Zone section comprises at most one record.
+    ///
+    /// The function will throw exception if any of the conditions above are
+    /// not met.
+    ///
+    /// @throw isc::d2::NotUpdateMessage if invalid Opcode.
+    /// @throw isc::d2::InvalidQRFlag if QR flag is not set to RESPONSE
+    /// @throw isc::d2::InvalidZone section, if Zone section comprises more
+    /// than one record.
+    void validateResponse() const;
+
+    /// @brief An object representing DNS Message which is used by the
+    /// implementation of @c D2UpdateMessage to perform low level.
+    ///
+    /// Declaration of this object pollutes the header with the details
+    /// of @c D2UpdateMessage implementation. It might be cleaner to use
+    /// Pimpl idiom to hide this object in an D2UpdateMessageImpl. However,
+    /// it would bring additional complications to the implementation
+    /// while the benefit would low - this header is not a part of any
+    /// common library. Therefore, if implementation is changed, modification of
+    /// private members of this class in the header has low impact.
+    dns::Message message_;
+
+    /// @brief Holds a pointer to the object, representing Zone in the DNS
+    /// Update.
+    D2ZonePtr zone_;
+
+};
+
+} // namespace d2
+} // namespace isc
+
+#endif // D2_UPDATE_MESSAGE_H
diff --git a/src/bin/d2/d2_zone.cc b/src/bin/d2/d2_zone.cc
new file mode 100644
index 0000000..96aa2bb
--- /dev/null
+++ b/src/bin/d2/d2_zone.cc
@@ -0,0 +1,36 @@
+// Copyright (C) 2013  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 <d2/d2_zone.h>
+
+namespace isc {
+namespace d2 {
+
+D2Zone::D2Zone(const dns::Name& name, const dns::RRClass& rrclass)
+    : name_(name), rrclass_(rrclass) {
+}
+
+std::string D2Zone::toText() const {
+    return (name_.toText() + " " + rrclass_.toText() + " SOA\n");
+}
+
+std::ostream&
+operator<<(std::ostream& os, const D2Zone& zone) {
+    os << zone.toText();
+    return (os);
+}
+
+} // namespace d2
+} // namespace isc
+
diff --git a/src/bin/d2/d2_zone.h b/src/bin/d2/d2_zone.h
new file mode 100644
index 0000000..60d43c8
--- /dev/null
+++ b/src/bin/d2/d2_zone.h
@@ -0,0 +1,117 @@
+// Copyright (C) 2013 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 D2_ZONE_H
+#define D2_ZONE_H
+
+#include <dns/name.h>
+#include <dns/rrclass.h>
+
+#include <boost/shared_ptr.hpp>
+
+namespace isc {
+namespace d2 {
+
+/// @brief The @c D2Zone encapsulates the Zone section in DNS Update message.
+///
+/// This class is used by the @c D2UpdateMessage to encapsulate the Zone section
+/// of the DNS Update message. Class members hold corresponding values of
+/// section's fields: NAME, CLASS. This class does not hold the RTYPE field
+/// value because RTYPE is always equal to SOA for DNS Update message (see
+/// RFC 2136, section 2.3).
+///
+/// Note, that this @c D2Zone class neither exposes functions to decode messages
+/// from wire format nor to encode to wire format. This is not needed, because
+/// @c isc::d2::D2UpdateMessage class uses @c D2Zone only to return the parsed
+/// Zone information to the caller. Internally, D2UpdateMessage parses and
+/// stores Zone section using @c isc::dns::Question class, and the @c toWire
+/// and @c fromWire functions of the @c isc::dns::Question class are used.
+class D2Zone {
+public:
+    /// @brief Constructor from Name and RRClass.
+    ///
+    /// @param name The name of the Zone.
+    /// @param rrclass The RR class of the Zone.
+    D2Zone(const dns::Name& name, const dns::RRClass& rrclass);
+
+    ///
+    /// @name Getters
+    ///
+    //@{
+    /// @brief Returns the Zone name.
+    ///
+    /// @return A reference to the Zone name.
+    const dns::Name& getName() const { return (name_); }
+
+    /// @brief Returns the Zone class.
+    ///
+    /// @return A reference to the Zone class.
+    const dns::RRClass& getClass() const { return (rrclass_); }
+    //@}
+
+    /// @brief Returns text representation of the Zone.
+    ///
+    /// This function concatenates the name of the Zone, Class and Type.
+    /// The type is always SOA.
+    ///
+    /// @return A text representation of the Zone.
+    std::string toText() const;
+
+    ///
+    /// @name Comparison Operators
+    ///
+    //@{
+    /// @brief Equality operator to compare @c D2Zone objects in query and
+    /// response messages.
+    ///
+    /// @param rhs Zone to compare against.
+    ///
+    /// @return true if name and class are equal, false otherwise.
+    bool operator==(const D2Zone& rhs) const {
+        return ((rrclass_ == rhs.rrclass_) && (name_ == rhs.name_));
+    }
+
+    /// @brief Inequality operator to compare @c D2Zone objects in query and
+    /// response messages.
+    ///
+    /// @param rhs Zone to compare against.
+    ///
+    /// @return true if any of name or class are unequal, false otherwise.
+    bool operator!=(const D2Zone& rhs) const {
+        return (!operator==(rhs));
+    }
+    //@}
+
+private:
+    dns::Name name_;       ///< Holds the Zone name.
+    dns::RRClass rrclass_; ///< Holds the Zone class.
+};
+
+typedef boost::shared_ptr<D2Zone> D2ZonePtr;
+
+/// @brief Insert the @c D2Zone as a string into stream.
+///
+/// @param os A @c std::ostream object on which the insertion operation is
+/// performed.
+/// @param zone A reference to the @c D2Zone object output by the
+/// operation.
+///
+/// @return A reference to the same @c std::ostream object referenced by
+/// parameter @c os after the insertion operation.
+std::ostream& operator<<(std::ostream& os, const D2Zone& zone);
+
+} // namespace d2
+} // namespace isc
+
+#endif // D2_ZONE_H
diff --git a/src/bin/d2/d_cfg_mgr.cc b/src/bin/d2/d_cfg_mgr.cc
new file mode 100644
index 0000000..1e6bb57
--- /dev/null
+++ b/src/bin/d2/d_cfg_mgr.cc
@@ -0,0 +1,240 @@
+// Copyright (C) 2013 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 <d2/d2_log.h>
+#include <dhcp/libdhcp++.h>
+#include <d2/d_cfg_mgr.h>
+#include <dhcpsrv/dhcp_parsers.h>
+#include <util/encode/hex.h>
+#include <util/strutil.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;
+using namespace isc::dhcp;
+using namespace isc::data;
+using namespace isc::asiolink;
+
+namespace isc {
+namespace d2 {
+
+// *********************** DCfgContextBase  *************************
+
+DCfgContextBase::DCfgContextBase():
+        boolean_values_(new BooleanStorage()),
+        uint32_values_(new Uint32Storage()),
+        string_values_(new StringStorage()) {
+    }
+
+DCfgContextBase::DCfgContextBase(const DCfgContextBase& rhs):
+        boolean_values_(new BooleanStorage(*(rhs.boolean_values_))),
+        uint32_values_(new Uint32Storage(*(rhs.uint32_values_))),
+        string_values_(new StringStorage(*(rhs.string_values_))) {
+}
+
+void
+DCfgContextBase::getParam(const std::string& name, bool& value, bool optional) {
+    try {
+        value = boolean_values_->getParam(name);
+    } catch (DhcpConfigError& ex) {
+        // If the parameter is not optional, re-throw the exception.
+        if (!optional) {
+            throw;
+        }
+    }
+}
+
+
+void
+DCfgContextBase::getParam(const std::string& name, uint32_t& value,
+                          bool optional) {
+    try {
+        value = uint32_values_->getParam(name);
+    } catch (DhcpConfigError& ex) {
+        // If the parameter is not optional, re-throw the exception.
+        if (!optional) {
+            throw;
+        }
+    }
+}
+
+void
+DCfgContextBase::getParam(const std::string& name, std::string& value,
+                          bool optional) {
+    try {
+        value = string_values_->getParam(name);
+    } catch (DhcpConfigError& ex) {
+        // If the parameter is not optional, re-throw the exception.
+        if (!optional) {
+            throw;
+        }
+    }
+}
+
+DCfgContextBase::~DCfgContextBase() {
+}
+
+// *********************** DCfgMgrBase  *************************
+
+DCfgMgrBase::DCfgMgrBase(DCfgContextBasePtr context)
+    : parse_order_(), context_(context) {
+    if (!context_) {
+        isc_throw(DCfgMgrBaseError, "DCfgMgrBase ctor: context cannot be NULL");
+    }
+}
+
+DCfgMgrBase::~DCfgMgrBase() {
+}
+
+isc::data::ConstElementPtr
+DCfgMgrBase::parseConfig(isc::data::ConstElementPtr config_set) {
+    LOG_DEBUG(dctl_logger, DBGLVL_COMMAND,
+                DCTL_CONFIG_START).arg(config_set->str());
+
+    if (!config_set) {
+        return (isc::config::createAnswer(1,
+                                    std::string("Can't parse NULL config")));
+    }
+
+    // The parsers implement data inheritance by directly accessing
+    // configuration context. For this reason the data parsers must store
+    // the parsed data into context immediately. This may cause data
+    // inconsistency if the parsing operation fails after the context has been
+    // modified. We need to preserve the original context here
+    // so as we can rollback changes when an error occurs.
+    DCfgContextBasePtr original_context = context_->clone();
+
+    // Answer will hold the result returned to the caller.
+    ConstElementPtr answer;
+
+    // Holds the name of the element being parsed.
+    std::string element_id;
+
+    try {
+        // Grab a map of element_ids and their data values from the new
+        // configuration set.
+        const std::map<std::string, ConstElementPtr>& values_map =
+                                                        config_set->mapValue();
+
+        // Use a pre-ordered list of element ids to parse the elements in a
+        // specific order if the list (parser_order_) is not empty; otherwise
+        // elements are parsed in the order the value_map presents them.
+
+        if (!parse_order_.empty()) {
+            // For each element_id in the parse order list, look for it in the
+            // value map.  If the element exists in the map, pass it and it's
+            // associated data in for parsing.
+            // If there is no matching entry in the value map an error is
+            // thrown.  Note, that elements tagged as "optional" from the user
+            // perspective must still have default or empty entries in the
+            // configuration set to be parsed.
+            int parsed_count = 0;
+            std::map<std::string, ConstElementPtr>::const_iterator it;
+            BOOST_FOREACH(element_id, parse_order_) {
+                it = values_map.find(element_id);
+                if (it != values_map.end()) {
+                    ++parsed_count;
+                    buildAndCommit(element_id, it->second);
+                }
+                else {
+                    LOG_ERROR(dctl_logger, DCTL_ORDER_NO_ELEMENT)
+                              .arg(element_id);
+                    isc_throw(DCfgMgrBaseError, "Element:" << element_id <<
+                              " is listed in the parse order but is not "
+                              " present in the configuration");
+                }
+            }
+
+            // NOTE: When using ordered parsing, the parse order list MUST
+            // include every possible element id that the value_map may contain.
+            // Entries in the map that are not in the parse order, would not be
+            // parsed. For now we will flag this as a programmatic error.  One
+            // could attempt to adjust for this, by identifying such entries
+            // and parsing them either first or last but which would be correct?
+            // Better to hold the engineer accountable.  So, if we parsed none
+            // or we parsed fewer than are in the map; then either the parse i
+            // order is incomplete OR the map has unsupported values.
+            if (!parsed_count ||
+                (parsed_count && ((parsed_count + 1) < values_map.size()))) {
+                LOG_ERROR(dctl_logger, DCTL_ORDER_ERROR);
+                isc_throw(DCfgMgrBaseError,
+                        "Configuration contains elements not in parse order");
+            }
+        } else {
+            // Order doesn't matter so iterate over the value map directly.
+            // Pass each element and it's associated data in to be parsed.
+            ConfigPair config_pair;
+            BOOST_FOREACH(config_pair, values_map) {
+                element_id = config_pair.first;
+                buildAndCommit(element_id, config_pair.second);
+            }
+        }
+
+        // Everything was fine. Configuration set processed successfully.
+        LOG_INFO(dctl_logger, DCTL_CONFIG_COMPLETE).arg("");
+        answer = isc::config::createAnswer(0, "Configuration committed.");
+
+    } catch (const isc::Exception& ex) {
+        LOG_ERROR(dctl_logger, DCTL_PARSER_FAIL).arg(element_id).arg(ex.what());
+        answer = isc::config::createAnswer(1,
+                     string("Configuration parsing failed: ") + ex.what());
+
+        // An error occurred, so make sure that we restore original context.
+        context_ = original_context;
+        return (answer);
+    }
+
+    return (answer);
+}
+
+void DCfgMgrBase::buildAndCommit(std::string& element_id,
+                                 isc::data::ConstElementPtr value) {
+    // Call derivation's implementation to create the appropriate parser
+    // based on the element id.
+    ParserPtr parser = createConfigParser(element_id);
+    if (!parser) {
+        isc_throw(DCfgMgrBaseError, "Could not create parser");
+    }
+
+    try {
+        // Invoke the parser's build method passing in the value. This will
+        // "convert" the Element form of value into the actual data item(s)
+        // and store them in parser's local storage.
+        parser->build(value);
+
+        // Invoke the parser's commit method. This "writes" the the data
+        // item(s) stored locally by the parser into the context.  (Note that
+        // parsers are free to do more than update the context, but that is an
+        // nothing something we are concerned with here.)
+        parser->commit();
+    } catch (const isc::Exception& ex) {
+        isc_throw(DCfgMgrBaseError,
+                  "Could not build and commit: " << ex.what());
+    } catch (...) {
+        isc_throw(DCfgMgrBaseError, "Non-ISC exception occurred");
+    }
+}
+
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
+
diff --git a/src/bin/d2/d_cfg_mgr.h b/src/bin/d2/d_cfg_mgr.h
new file mode 100644
index 0000000..a0bd9bb
--- /dev/null
+++ b/src/bin/d2/d_cfg_mgr.h
@@ -0,0 +1,331 @@
+// Copyright (C) 2013 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 D_CFG_MGR_H
+#define D_CFG_MGR_H
+
+#include <cc/data.h>
+#include <exceptions/exceptions.h>
+#include <dhcpsrv/dhcp_parsers.h>
+
+#include <stdint.h>
+#include <string>
+
+namespace isc {
+namespace d2 {
+
+/// @brief Exception thrown if the configuration manager encounters an error.
+class DCfgMgrBaseError : public isc::Exception {
+public:
+    DCfgMgrBaseError(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) { };
+};
+
+class DCfgContextBase;
+/// @brief Pointer to a configuration context.
+typedef boost::shared_ptr<DCfgContextBase> DCfgContextBasePtr;
+
+/// @brief Abstract class that implements a container for configuration context.
+/// It provides a single enclosure for the storage of configuration parameters
+/// and any other context specific information that needs to be accessible
+/// during configuration parsing as well as to the application as a whole.
+/// The base class supports storage for a small set of simple data types.
+/// Derivations simply add additional storage as needed.  Note that this class
+/// declares the pure virtual clone() method, its copy constructor is protected,
+/// and its copy operator is inaccessible.  Derivations must supply an
+/// implementation of clone that calls the base class copy constructor.
+/// This allows the management class to perform context backup and restoration
+/// without derivation specific knowledge using logic like
+/// the following:
+///
+///    // Make a backup copy
+///    DCfgContextBasePtr backup_copy(context_->clone());
+///    :
+///    // Restore from backup
+///    context_ = backup_copy;
+///
+class DCfgContextBase {
+public:
+    /// @brief Indicator that a configuration parameter is optional.
+    static const bool OPTIONAL = true;
+    static const bool REQUIRED = false;
+
+    /// @brief Constructor
+    DCfgContextBase();
+
+    /// @brief Destructor
+    virtual ~DCfgContextBase();
+
+    /// @brief Fetches the value for a given boolean configuration parameter
+    /// from the context.
+    ///
+    /// @param name is the name of the parameter to retrieve.
+    /// @param value is an output parameter in which to return the retrieved
+    /// value.
+    /// @param optional if true, the parameter is optional and the method
+    /// will not throw if the parameter is not found in the context. The
+    /// contents of the output parameter, value, will not be altered.
+    /// It defaults to false if not specified.
+    /// @throw throws DhcpConfigError if the context does not contain the
+    /// parameter and optional is false.
+    void getParam(const std::string& name, bool& value, bool optional=false);
+
+    /// @brief Fetches the value for a given uint32_t configuration parameter
+    /// from the context.
+    ///
+    /// @param name is the name of the parameter to retrieve.
+    /// @param value is an output parameter in which to return the retrieved
+    /// value.
+    /// @param optional if true, the parameter is optional and the method
+    /// will not throw if the parameter is not found in the context. The
+    /// contents of the output parameter, value, will not be altered.
+    /// @throw throws DhcpConfigError if the context does not contain the
+    /// parameter and optional is false.
+    void getParam(const std::string& name, uint32_t& value,
+                 bool optional=false);
+
+    /// @brief Fetches the value for a given string configuration parameter
+    /// from the context.
+    ///
+    /// @param name is the name of the parameter to retrieve.
+    /// @param value is an output parameter in which to return the retrieved
+    /// value.
+    /// @param optional if true, the parameter is optional and the method
+    /// will not throw if the parameter is not found in the context. The
+    /// contents of the output parameter, value, will not be altered.
+    /// @throw throws DhcpConfigError if the context does not contain the
+    /// parameter and optional is false.
+    void getParam(const std::string& name, std::string& value,
+                  bool optional=false);
+
+    /// @brief Fetches the Boolean Storage. Typically used for passing
+    /// into parsers.
+    ///
+    /// @return returns a pointer to the Boolean Storage.
+    isc::dhcp::BooleanStoragePtr getBooleanStorage() {
+        return (boolean_values_);
+    }
+
+    /// @brief Fetches the uint32 Storage. Typically used for passing
+    /// into parsers.
+    ///
+    /// @return returns a pointer to the uint32 Storage.
+    isc::dhcp::Uint32StoragePtr getUint32Storage() {
+        return (uint32_values_);
+    }
+
+    /// @brief Fetches the string Storage. Typically used for passing
+    /// into parsers.
+    ///
+    /// @return returns a pointer to the string Storage.
+    isc::dhcp::StringStoragePtr getStringStorage() {
+        return (string_values_);
+    }
+
+    /// @brief Creates a clone of this context object.
+    ///
+    /// As mentioned in the the class brief, derivation must supply an
+    /// implementation that initializes the base class storage as well as its
+    /// own.  Typically the derivation's clone method would return the result
+    /// of passing  "*this" into its own copy constructor:
+    ///
+    /// @code
+    /// class DStubContext : public DCfgContextBase {
+    /// public:
+    ///  :
+    ///     // Clone calls its own copy constructor
+    ///     virtual DCfgContextBasePtr clone() {
+    ///         return (DCfgContextBasePtr(new DStubContext(*this)));
+    ///     }
+    ///
+    ///     // Note that the copy constructor calls the base class copy ctor
+    ///     // then initializes its additional storage.
+    ///     DStubContext(const DStubContext& rhs) : DCfgContextBase(rhs),
+    ///         extra_values_(new Uint32Storage(*(rhs.extra_values_))) {
+    ///     }
+    ///  :
+    ///    // Here's the derivation's additional storage.
+    ///    isc::dhcp::Uint32StoragePtr extra_values_;
+    ///  :
+    /// @endcode
+    ///
+    /// @return returns a pointer to the new clone.
+    virtual DCfgContextBasePtr clone() = 0;
+
+protected:
+    /// @brief Copy constructor for use by derivations in clone().
+    DCfgContextBase(const DCfgContextBase& rhs);
+
+private:
+    /// @brief Private assignment operator to avoid potential for slicing.
+    DCfgContextBase& operator=(const DCfgContextBase& rhs);
+
+    /// @brief Storage for boolean parameters.
+    isc::dhcp::BooleanStoragePtr boolean_values_;
+
+    /// @brief Storage for uint32 parameters.
+    isc::dhcp::Uint32StoragePtr uint32_values_;
+
+    /// @brief Storage for string parameters.
+    isc::dhcp::StringStoragePtr string_values_;
+};
+
+/// @brief Defines an unsorted, list of string Element IDs.
+typedef std::vector<std::string> ElementIdList;
+
+/// @brief Configuration Manager
+///
+/// DCfgMgrBase is an abstract class that provides the mechanisms for managing
+/// an application's configuration.  This includes services for parsing sets of
+/// configuration values, storing the parsed information in its converted form,
+/// and retrieving the information on demand.  It is intended to be the worker
+/// class which is handed a set of configuration values to process by upper
+/// application management layers.
+///
+/// The class presents a public method for receiving new configurations,
+/// parseConfig.  This method coordinates the parsing effort as follows:
+///
+/// @code
+///    make backup copy of configuration context
+///    for each top level element in new configuration
+///        get derivation-specific parser for element
+///        run parser
+///        update context with parsed results
+///        break on error
+///
+///    if an error occurred
+///        restore configuration context from backup
+/// @endcode
+///
+/// After making a backup of the current context, it iterates over the top-level
+/// elements in the new configuration.  The order in which the elements are
+/// processed is either:
+///
+///    1. Natural order presented by the configuration set
+///    2. Specific order determined by a list of element ids
+///
+/// This allows a derivation to specify the order in which its elements are
+/// parsed if there are dependencies between elements.
+///
+/// To parse a given element, its id is passed into createConfigParser,
+/// which returns an instance of the appropriate parser.  This method is
+/// abstract so the derivation's implementation determines the type of parser
+/// created. This isolates the knowledge of specific element ids and which
+/// application specific parsers to derivation.
+///
+/// Once the parser has been created, it is used to parse the data value
+/// associated with the element id and update the context with the parsed
+/// results.
+///
+/// In the event that an error occurs, parsing is halted and the
+/// configuration context is restored from backup.
+class DCfgMgrBase {
+public:
+    /// @brief Constructor
+    ///
+    /// @param context is a pointer to the configuration context the manager
+    /// will use for storing parsed results.
+    ///
+    /// @throw throws DCfgMgrBaseError if context is null
+    DCfgMgrBase(DCfgContextBasePtr context);
+
+    /// @brief Destructor
+    virtual ~DCfgMgrBase();
+
+    /// @brief Acts as the receiver of new configurations and coordinates
+    /// the parsing as described in the class brief.
+    ///
+    /// @param config_set is a set of configuration elements to parsed.
+    ///
+    /// @return an Element that contains the results of configuration composed
+    /// of an integer status value (0 means successful, non-zero means failure),
+    /// and a string explanation of the outcome.
+    isc::data::ConstElementPtr parseConfig(isc::data::ConstElementPtr
+                                           config_set);
+
+    /// @brief Adds a given element id to the end of the parse order list.
+    ///
+    /// The order in which elements are retrieved from this is the order in
+    /// which they are added to the list. Derivations should use this method
+    /// to populate the parse order as part of their constructor.
+    ///
+    /// @param element_id is the string name of the element as it will appear
+    /// in the configuration set.
+    void addToParseOrder(const std::string& element_id){
+        parse_order_.push_back(element_id);
+    }
+
+    /// @brief Fetches the parse order list.
+    ///
+    /// @return returns a const reference to the list.
+    const ElementIdList& getParseOrder() const {
+        return (parse_order_);
+    }
+
+    /// @brief Fetches the configuration context.
+    ///
+    /// @return returns a pointer reference to the configuration context.
+    DCfgContextBasePtr& getContext() {
+        return (context_);
+    }
+
+protected:
+    /// @brief  Create a parser instance based on an element id.
+    ///
+    /// Given an element_id returns an instance of the appropriate parser.
+    /// This method is abstract, isolating any direct knowledge of element_ids
+    /// and parsers to within the application-specific derivation.
+    ///
+    /// @param element_id is the string name of the element as it will appear
+    /// in the configuration set.
+    ///
+    /// @return returns a ParserPtr to the parser instance.
+    /// @throw throws DCfgMgrBaseError if an error occurs.
+    virtual isc::dhcp::ParserPtr
+    createConfigParser(const std::string& element_id) = 0;
+
+private:
+
+    /// @brief Parse a configuration element.
+    ///
+    /// Given an element_id and data value, instantiate the appropriate
+    /// parser,  parse the data value, and commit the results.
+    ///
+    /// @param element_id is the string name of the element as it will appear
+    /// in the configuration set.
+    /// @param value is the data value to be parsed and associated with
+    /// element_id.
+    ///
+    /// @throw throws DCfgMgrBaseError if an error occurs.
+    void buildAndCommit(std::string& element_id,
+                        isc::data::ConstElementPtr value);
+
+    /// @brief A list of element ids which specifies the element parsing order.
+    ///
+    /// If the list is empty, the natural order in the configuration set
+    /// it used.
+    ElementIdList parse_order_;
+
+    /// @brief Pointer to the configuration context instance.
+    DCfgContextBasePtr context_;
+};
+
+/// @brief Defines a shared pointer to DCfgMgrBase.
+typedef boost::shared_ptr<DCfgMgrBase> DCfgMgrBasePtr;
+
+
+}; // end of isc::d2 namespace
+}; // end of isc namespace
+
+#endif // D_CFG_MGR_H
diff --git a/src/bin/d2/d_controller.cc b/src/bin/d2/d_controller.cc
index b32fc45..ed38cd8 100644
--- a/src/bin/d2/d_controller.cc
+++ b/src/bin/d2/d_controller.cc
@@ -46,7 +46,7 @@ DControllerBase::setController(const DControllerBasePtr& controller) {
 }
 
 void
-DControllerBase::launch(int argc, char* argv[]) {
+DControllerBase::launch(int argc, char* argv[], const bool test_mode) {
     // Step 1 is to parse the command line arguments.
     try {
         parseArgs(argc, argv);
@@ -55,12 +55,16 @@ DControllerBase::launch(int argc, char* argv[]) {
         throw; // rethrow it
     }
 
-    // Now that we know what the mode flags are, we can init logging.
-    // If standalone is enabled, do not buffer initial log messages
-    isc::log::initLogger(bin_name_,
-                         ((verbose_ && stand_alone_)
-                          ? isc::log::DEBUG : isc::log::INFO),
-                         isc::log::MAX_DEBUG_LEVEL, NULL, !stand_alone_);
+    // Do not initialize logger here if we are running unit tests. It would
+    // replace an instance of unit test specific logger.
+    if (!test_mode) {
+        // Now that we know what the mode flags are, we can init logging.
+        // If standalone is enabled, do not buffer initial log messages
+        isc::log::initLogger(bin_name_,
+                             ((verbose_ && stand_alone_)
+                              ? isc::log::DEBUG : isc::log::INFO),
+                             isc::log::MAX_DEBUG_LEVEL, NULL, !stand_alone_);
+    }
 
     LOG_DEBUG(dctl_logger, DBGLVL_START_SHUT, DCTL_STARTING)
               .arg(app_name_).arg(getpid());
@@ -295,7 +299,8 @@ DControllerBase::commandHandler(const std::string& command,
                                 isc::data::ConstElementPtr args) {
 
     LOG_DEBUG(dctl_logger, DBGLVL_COMMAND, DCTL_COMMAND_RECEIVED)
-              .arg(controller_->getAppName()).arg(command).arg(args->str());
+        .arg(controller_->getAppName()).arg(command)
+        .arg(args ? args->str() : "(no args)");
 
     // Invoke the instance method on the controller singleton.
     return (controller_->executeCommand(command, args));
@@ -305,7 +310,7 @@ isc::data::ConstElementPtr
 DControllerBase::updateConfig(isc::data::ConstElementPtr new_config) {
     isc::data::ConstElementPtr full_config;
     if (stand_alone_) {
-        // @TODO Until there is a configuration manager to provide retrieval
+        // @todo Until there is a configuration manager to provide retrieval
         // we'll just assume the incoming config is the full configuration set.
         // It may also make more sense to isolate the controller from the
         // configuration manager entirely. We could do something like
@@ -368,7 +373,7 @@ DControllerBase::executeCommand(const std::string& command,
         if (rcode == COMMAND_INVALID)
         {
             // It wasn't controller command, so may be an application command.
-            answer = process_->command(command,args);
+            answer = process_->command(command, args);
         }
     }
 
diff --git a/src/bin/d2/d_controller.h b/src/bin/d2/d_controller.h
index bf7a607..4dab2e8 100644
--- a/src/bin/d2/d_controller.h
+++ b/src/bin/d2/d_controller.h
@@ -144,8 +144,19 @@ public:
     /// arguments. Note this method is deliberately not virtual to ensure the
     /// proper sequence of events occur.
     ///
+    /// This function can be run in the test mode. It prevents initialization
+    /// of D2 module logger. This is used in unit tests which initialize logger
+    /// in their main function. Such logger uses environmental variables to
+    /// control severity, verbosity etc. Reinitialization of logger by this
+    /// function would replace unit tests specific logger configuration with
+    /// this suitable for D2 running as a bind10 module.
+    ///
     /// @param argc  is the number of command line arguments supplied
     /// @param argv  is the array of string (char *) command line arguments
+    /// @param test_mode is a bool value which indicates if
+    /// @c DControllerBase::launch should be run in the test mode (if true).
+    /// This parameter doesn't have default value to force test implementers to
+    /// enable test mode explicitly.
     ///
     /// @throw throws one of the following exceptions:
     /// InvalidUsage - Indicates invalid command line.
@@ -156,7 +167,7 @@ public:
     /// process event loop.
     /// SessionEndError - Could not disconnect from BIND10 (integrated mode
     /// only).
-    void launch(int argc, char* argv[]);
+    void launch(int argc, char* argv[], const bool test_mode);
 
     /// @brief A dummy configuration handler that always returns success.
     ///
@@ -171,7 +182,7 @@ public:
     /// various configuration values. Installing the dummy handler
     /// that guarantees to return success causes initial configuration
     /// to be stored for the session being created and that it can
-    /// be later accessed with \ref isc::ConfigData::getFullConfig.
+    /// be later accessed with \ref isc::config::ConfigData::getFullConfig.
     ///
     /// @param new_config new configuration.
     ///
@@ -198,7 +209,8 @@ public:
     /// the virtual instance method, executeCommand.
     ///
     /// @param command textual representation of the command
-    /// @param args parameters of the command
+    /// @param args parameters of the command. It can be NULL pointer if no
+    /// arguments exist for a particular command.
     ///
     /// @return status of the processed command
     static isc::data::ConstElementPtr
@@ -210,7 +222,7 @@ public:
     /// implementation will merge the configuration update into the existing
     /// configuration and then invoke the application process' configure method.
     ///
-    /// @TODO This implementation is will evolve as the D2 configuration
+    /// @todo This implementation is will evolve as the D2 configuration
     /// management task is implemented (trac #2957).
     ///
     /// @param  new_config is the new configuration
@@ -261,7 +273,7 @@ protected:
     ///
     /// @param option is the option "character" from the command line, without
     /// any prefixing hyphen(s)
-    /// @optarg optarg is the argument value (if one) associated with the option
+    /// @param optarg is the argument value (if one) associated with the option
     ///
     /// @return must return true if the option was valid, false is it is
     /// invalid. (Note the default implementation always returns false.)
@@ -395,7 +407,7 @@ protected:
 
     /// @brief Setter for setting the name of the controller's BIND10 spec file.
     ///
-    /// @param value is the file name string.
+    /// @param spec_file_name the file name string.
     void setSpecFileName(const std::string& spec_file_name) {
         spec_file_name_ = spec_file_name;
     }
diff --git a/src/bin/d2/d_process.h b/src/bin/d2/d_process.h
index 11b0a09..d1a7a55 100644
--- a/src/bin/d2/d_process.h
+++ b/src/bin/d2/d_process.h
@@ -17,6 +17,8 @@
 
 #include <asiolink/asiolink.h>
 #include <cc/data.h>
+#include <d2/d_cfg_mgr.h>
+
 #include <boost/shared_ptr.hpp>
 
 #include <exceptions/exceptions.h>
@@ -58,13 +60,21 @@ public:
     /// in log statements, but otherwise arbitrary.
     /// @param io_service is the io_service used by the caller for
     /// asynchronous event handling.
+    /// @param cfg_mgr the configuration manager instance that handles
+    /// configuration parsing.
     ///
     /// @throw DProcessBaseError is io_service is NULL.
-    DProcessBase(const char* app_name, IOServicePtr io_service)
-        : app_name_(app_name), io_service_(io_service), shut_down_flag_(false) {
+    DProcessBase(const char* app_name, IOServicePtr io_service, 
+                 DCfgMgrBasePtr cfg_mgr)
+        : app_name_(app_name), io_service_(io_service), shut_down_flag_(false),
+        cfg_mgr_(cfg_mgr) {
         if (!io_service_) {
             isc_throw (DProcessBaseError, "IO Service cannot be null");
         }
+
+        if (!cfg_mgr_) {
+            isc_throw (DProcessBaseError, "CfgMgr cannot be null");
+        }
     };
 
     /// @brief May be used after instantiation to perform initialization unique
@@ -159,6 +169,13 @@ public:
         io_service_->stop();
     }
 
+    /// @brief Fetches the process's configuration manager.
+    ///
+    /// @return returns a reference to the configuration manager.
+    DCfgMgrBasePtr& getCfgMgr() {
+        return (cfg_mgr_);
+    }
+
 private:
     /// @brief Text label for the process. Generally used in log statements,
     /// but otherwise can be arbitrary.
@@ -169,6 +186,9 @@ private:
 
     /// @brief Boolean flag set when shutdown has been requested.
     bool shut_down_flag_;
+
+    /// @brief  Pointer to the configuration manager.
+    DCfgMgrBasePtr cfg_mgr_;
 };
 
 /// @brief Defines a shared pointer to DProcessBase.
diff --git a/src/bin/d2/dhcp-ddns.spec b/src/bin/d2/dhcp-ddns.spec
index 1098ada..e29bc88 100644
--- a/src/bin/d2/dhcp-ddns.spec
+++ b/src/bin/d2/dhcp-ddns.spec
@@ -1,21 +1,205 @@
+{ 
+"module_spec": 
 {
-  "module_spec": {
     "module_name": "DhcpDdns",
     "module_description": "DHPC-DDNS Service",
     "config_data": [
-    ],
+    { 
+        "item_name": "interface",
+        "item_type": "string",
+        "item_optional": true,
+        "item_default": "eth0"
+    },
+
+    { 
+        "item_name": "ip_address",
+        "item_type": "string",
+        "item_optional": false,
+        "item_default": "127.0.0.1" 
+    },
+
+    { 
+        "item_name": "port",
+        "item_type": "integer",
+        "item_optional": true,
+        "item_default": 51771 
+    },
+    {
+        "item_name": "tsig_keys",
+        "item_type": "list",
+        "item_optional": true, 
+        "item_default": [],
+        "list_item_spec":
+        {
+            "item_name": "tsig_key",
+            "item_type": "map",
+            "item_optional": false,
+            "item_default": {"algorithm" : "hmac_md5"},
+            "map_item_spec": [ 
+            {
+                "item_name": "name",
+                "item_type": "string",
+                "item_optional": false,
+                "item_default": ""
+            },
+            {
+                "item_name": "algorithm",
+                "item_type": "string",
+                "item_optional": false,
+                "item_default": ""
+            },
+            {
+                "item_name": "secret",
+                "item_type": "string",
+                "item_optional": false,
+                "item_default": ""
+            }]
+        }
+    },
+    {
+        "item_name": "forward_ddns",
+        "item_type": "map",
+        "item_optional": true,
+         "item_default": {},
+         "map_item_spec": [ 
+         {
+            "item_name": "ddns_domains",
+            "item_type": "list",
+            "item_optional": false, 
+            "item_default": [],
+            "list_item_spec":
+            {
+                "item_name": "ddns_domain",
+                "item_type": "map",
+                "item_optional": false,
+                "item_default": {},
+                "map_item_spec": [ 
+                { 
+                    "item_name": "name",
+                    "item_type": "string",
+                    "item_optional": false,
+                    "item_default": ""
+                },
+
+                { 
+                    "item_name": "key_name",
+                    "item_type": "string",
+                    "item_optional": true,
+                    "item_default": "" 
+                },
+    
+                {
+                    "item_name": "dns_servers",
+                    "item_type": "list",
+                    "item_optional": false, 
+                    "item_default": [],
+                    "list_item_spec":
+                    {
+                        "item_name": "dns_server",
+                        "item_type": "map",
+                        "item_optional": false, 
+                        "item_default": {},
+                        "map_item_spec": [ 
+                        { 
+                            "item_name": "hostname",
+                            "item_type": "string",
+                            "item_optional": true,
+                            "item_default": ""
+                        },
+                        { 
+                            "item_name": "ip_address",
+                            "item_type": "string",
+                            "item_optional": true,
+                            "item_default": ""
+                        },
+                        { 
+                            "item_name": "port",
+                            "item_type": "integer",
+                            "item_optional": true,
+                            "item_default": 53 
+                        }]
+                    }
+                }]
+            }
+        }]
+    },
+
+    {
+        "item_name": "reverse_ddns",
+        "item_type": "map",
+        "item_optional": true,
+         "item_default": {},
+         "map_item_spec": [ 
+         { 
+            "item_name": "ddns_domains",
+            "item_type": "list",
+            "item_optional": false, 
+            "item_default": [],
+            "list_item_spec":
+            {
+                "item_name": "ddns_domain",
+                "item_type": "map",
+                "item_optional": false,
+                "item_default": {},
+                "map_item_spec": [ 
+                { 
+                    "item_name": "name",
+                    "item_type": "string",
+                    "item_optional": false,
+                    "item_default": ""
+                },
+
+                { 
+                    "item_name": "key_name",
+                    "item_type": "string",
+                    "item_optional": true,
+                    "item_default": "" 
+                },
+    
+                {
+                    "item_name": "dns_servers",
+                    "item_type": "list",
+                    "item_optional": false, 
+                    "item_default": [],
+                    "list_item_spec":
+                    {
+                        "item_name": "dns_server",
+                        "item_type": "map",
+                        "item_optional": false, 
+                        "item_default": {},
+                        "map_item_spec": [ 
+                        { 
+                            "item_name": "hostname",
+                            "item_type": "string",
+                            "item_optional": true,
+                            "item_default": ""
+                        },
+                        { 
+                            "item_name": "ip_address",
+                            "item_type": "string",
+                            "item_optional": true,
+                            "item_default": ""
+                        },
+                        { 
+                            "item_name": "port",
+                            "item_type": "integer",
+                            "item_optional": true,
+                            "item_default": 53 
+                        }]
+                    }
+                }]
+            }
+        }]
+    }],
+
     "commands": [
-      {
-        "command_name": "shutdown",
-        "command_description": "Shut down the DHCP-DDNS service",
-        "command_args": [
-          {
-            "item_name": "pid",
-            "item_type": "integer",
-            "item_optional": true
-          }
-        ]
-      }
+        {
+            "command_name": "shutdown",
+            "command_description": "Shuts down DHCPv6 server.",
+            "command_args": [
+            ]
+        }
     ]
   }
 }
+
diff --git a/src/bin/d2/dns_client.cc b/src/bin/d2/dns_client.cc
new file mode 100644
index 0000000..a3bff81
--- /dev/null
+++ b/src/bin/d2/dns_client.cc
@@ -0,0 +1,247 @@
+// Copyright (C) 2013 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 <d2/dns_client.h>
+#include <d2/d2_log.h>
+#include <dns/messagerenderer.h>
+#include <limits>
+
+namespace isc {
+namespace d2 {
+
+namespace {
+
+// OutputBuffer objects are pre-allocated before data is written to them.
+// This is a default number of bytes for the buffers we create within
+// DNSClient class.
+const size_t DEFAULT_BUFFER_SIZE = 128;
+
+}
+
+using namespace isc::util;
+using namespace isc::asiolink;
+using namespace isc::asiodns;
+using namespace isc::dns;
+
+// This class provides the implementation for the DNSClient. This allows for
+// the separation of the DNSClient interface from the implementation details.
+// Currently, implementation uses IOFetch object to handle asynchronous
+// communication with the DNS. This design may be revisited in the future. If
+// implementation is changed, the DNSClient API will remain unchanged thanks
+// to this separation.
+class DNSClientImpl : public asiodns::IOFetch::Callback {
+public:
+    // A buffer holding response from a DNS.
+    util::OutputBufferPtr in_buf_;
+    // A caller-supplied object holding a parsed response from DNS.
+    D2UpdateMessagePtr response_;
+    // A caller-supplied external callback which is invoked when DNS message
+    // exchange is complete or interrupted.
+    DNSClient::Callback* callback_;
+    // A Transport Layer protocol used to communicate with a DNS.
+    DNSClient::Protocol proto_;
+
+    // Constructor and Destructor
+    DNSClientImpl(D2UpdateMessagePtr& response_placeholder,
+                  DNSClient::Callback* callback,
+                  const DNSClient::Protocol proto);
+    virtual ~DNSClientImpl();
+
+    // This internal callback is called when the DNS update message exchange is
+    // complete. It further invokes the external callback provided by a caller.
+    // Before external callback is invoked, an object of the D2UpdateMessage
+    // type, representing a response from the server is set.
+    virtual void operator()(asiodns::IOFetch::Result result);
+
+    // Starts asynchronous DNS Update.
+    void doUpdate(asiolink::IOService& io_service,
+                  const asiolink::IOAddress& ns_addr,
+                  const uint16_t ns_port,
+                  D2UpdateMessage& update,
+                  const unsigned int wait);
+
+    // This function maps the IO error to the DNSClient error.
+    DNSClient::Status getStatus(const asiodns::IOFetch::Result);
+};
+
+DNSClientImpl::DNSClientImpl(D2UpdateMessagePtr& response_placeholder,
+                             DNSClient::Callback* callback,
+                             const DNSClient::Protocol proto)
+    : in_buf_(new OutputBuffer(DEFAULT_BUFFER_SIZE)),
+      response_(response_placeholder), callback_(callback), proto_(proto) {
+
+    // @todo Currently we only support UDP. The support for TCP is planned for
+    // the future release.
+    if (proto_ == DNSClient::TCP) {
+        isc_throw(isc::NotImplemented, "TCP is currently not supported as a"
+                  << " Transport protocol for DNS Updates; please use UDP");
+    }
+
+    // Given that we already eliminated the possibility that TCP is used, it
+    // would be sufficient  to check that (proto != DNSClient::UDP). But, once
+    // support TCP is added the check above will disappear and the extra check
+    // will be needed here anyway.
+    // Note that cascaded check is used here instead of:
+    //   if (proto_ != DNSClient::TCP && proto_ != DNSClient::UDP)..
+    // because some versions of GCC compiler complain that check above would
+    // be always 'false' due to limited range of enumeration. In fact, it is
+    // possible to pass out of range integral value through enum and it should
+    // be caught here.
+    if (proto_ != DNSClient::TCP) {
+        if (proto_ != DNSClient::UDP) {
+            isc_throw(isc::NotImplemented, "invalid transport protocol type '"
+                      << proto_ << "' specified for DNS Updates");
+        }
+    }
+
+    if (!response_) {
+        isc_throw(BadValue, "a pointer to an object to encapsulate the DNS"
+                  " server must be provided; found NULL value");
+
+    }
+}
+
+DNSClientImpl::~DNSClientImpl() {
+}
+
+void
+DNSClientImpl::operator()(asiodns::IOFetch::Result result) {
+    // Get the status from IO. If no success, we just call user's callback
+    // and pass the status code.
+    DNSClient::Status status = getStatus(result);
+    if (status == DNSClient::SUCCESS) {
+        InputBuffer response_buf(in_buf_->getData(), in_buf_->getLength());
+        // Server's response may be corrupted. In such case, fromWire will
+        // throw an exception. We want to catch this exception to return
+        // appropriate status code to the caller and log this event.
+        try {
+            response_->fromWire(response_buf);
+
+        } catch (const Exception& ex) {
+            status = DNSClient::INVALID_RESPONSE;
+            LOG_DEBUG(dctl_logger, DBGLVL_TRACE_DETAIL,
+                      DHCP_DDNS_INVALID_RESPONSE).arg(ex.what());
+
+        }
+    }
+
+    // Once we are done with internal business, let's call a callback supplied
+    // by a caller.
+    if (callback_ != NULL) {
+        (*callback_)(status);
+    }
+}
+
+DNSClient::Status
+DNSClientImpl::getStatus(const asiodns::IOFetch::Result result) {
+    switch (result) {
+    case IOFetch::SUCCESS:
+        return (DNSClient::SUCCESS);
+
+    case IOFetch::TIME_OUT:
+        return (DNSClient::TIMEOUT);
+
+    case IOFetch::STOPPED:
+        return (DNSClient::IO_STOPPED);
+
+    default:
+        ;
+    }
+    return (DNSClient::OTHER);
+}
+
+void
+DNSClientImpl::doUpdate(IOService& io_service,
+                        const IOAddress& ns_addr,
+                        const uint16_t ns_port,
+                        D2UpdateMessage& update,
+                        const unsigned int wait) {
+    // A renderer is used by the toWire function which creates the on-wire data
+    // from the DNS Update message. A renderer has its internal buffer where it
+    // renders data by default. However, this buffer can't be directly accessed.
+    // Fortunately, the renderer's API accepts user-supplied buffers. So, let's
+    // create our own buffer and pass it to the renderer so as the message is
+    // rendered to this buffer. Finally, we pass this buffer to IOFetch.
+    dns::MessageRenderer renderer;
+    OutputBufferPtr msg_buf(new OutputBuffer(DEFAULT_BUFFER_SIZE));
+    renderer.setBuffer(msg_buf.get());
+
+    // Render DNS Update message. This may throw a bunch of exceptions if
+    // invalid message object is given.
+    update.toWire(renderer);
+
+    // IOFetch has all the mechanisms that we need to perform asynchronous
+    // communication with the DNS server. The last but one argument points to
+    // this object as a completion callback for the message exchange. As a
+    // result operator()(Status) will be called.
+
+    // Timeout value is explicitly cast to the int type to avoid warnings about
+    // overflows when doing implicit cast. It should have been checked by the
+    // caller that the unsigned timeout value will fit into int.
+    IOFetch io_fetch(IOFetch::UDP, io_service, msg_buf, ns_addr, ns_port,
+                     in_buf_, this, static_cast<int>(wait));
+    // Post the task to the task queue in the IO service. Caller will actually
+    // run these tasks by executing IOService::run.
+    io_service.post(io_fetch);
+}
+
+
+DNSClient::DNSClient(D2UpdateMessagePtr& response_placeholder,
+                     Callback* callback, const DNSClient::Protocol proto)
+    : impl_(new DNSClientImpl(response_placeholder, callback, proto)) {
+}
+
+DNSClient::~DNSClient() {
+    delete (impl_);
+}
+
+unsigned int
+DNSClient::getMaxTimeout() {
+    static const unsigned int max_timeout = std::numeric_limits<int>::max();
+    return (max_timeout);
+}
+
+void
+DNSClient::doUpdate(IOService&,
+                    const IOAddress&,
+                    const uint16_t,
+                    D2UpdateMessage&,
+                    const unsigned int,
+                    const dns::TSIGKey&) {
+    isc_throw(isc::NotImplemented, "TSIG is currently not supported for"
+              "DNS Update message");
+}
+
+void
+DNSClient::doUpdate(IOService& io_service,
+                    const IOAddress& ns_addr,
+                    const uint16_t ns_port,
+                    D2UpdateMessage& update,
+                    const unsigned int wait) {
+    // The underlying implementation which we use to send DNS Updates uses
+    // signed integers for timeout. If we want to avoid overflows we need to
+    // respect this limitation here.
+    if (wait > getMaxTimeout()) {
+        isc_throw(isc::BadValue, "A timeout value for DNS Update request must"
+                  " not exceed " << getMaxTimeout()
+                  << ". Provided timeout value is '" << wait << "'");
+    }
+    impl_->doUpdate(io_service, ns_addr, ns_port, update, wait);
+}
+
+
+
+} // namespace d2
+} // namespace isc
+
diff --git a/src/bin/d2/dns_client.h b/src/bin/d2/dns_client.h
new file mode 100644
index 0000000..c1c54f6
--- /dev/null
+++ b/src/bin/d2/dns_client.h
@@ -0,0 +1,192 @@
+// Copyright (C) 2013 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_CLIENT_H
+#define DNS_CLIENT_H
+
+#include <d2/d2_update_message.h>
+
+#include <asiolink/io_service.h>
+#include <util/buffer.h>
+
+#include <asiodns/io_fetch.h>
+#include <dns/tsig.h>
+
+namespace isc {
+namespace d2 {
+
+class DNSClient;
+typedef boost::shared_ptr<DNSClient> DNSClientPtr;
+
+/// DNSClient class implementation.
+class DNSClientImpl;
+
+/// @brief The @c DNSClient class handles communication with the DNS server.
+///
+/// Communication with the DNS server is asynchronous. Caller must provide a
+/// callback, which will be invoked when the response from the DNS server is
+/// received, a timeout has occurred or IO service has been stopped for any
+/// reason. The caller-supplied callback is called by the internal callback
+/// operator implemented by @c DNSClient. This callback is responsible for
+/// initializing the @c D2UpdateMessage instance which encapsulates the response
+/// from the DNS. This initialization does not take place if the response from
+/// DNS is not received.
+///
+/// Caller must supply a pointer to the @c D2UpdateMessage object, which will
+/// encapsulate DNS response, through class constructor. An exception will be
+/// thrown if the pointer is not initialized by the caller.
+///
+/// @todo Ultimately, this class will support both TCP and UDP Transport.
+/// Currently only UDP is supported and can be specified as a preferred
+/// protocol. @c DNSClient constructor will throw an exception if TCP is
+/// specified. Once both protocols are supported, the @c DNSClient logic will
+/// try to obey caller's preference. However, it may use the other protocol if
+/// on its own discretion, when there is a legitimate reason to do so. For
+/// example, if communication with the server using preferred protocol fails.
+class DNSClient {
+public:
+
+    /// @brief Transport layer protocol used by a DNS Client to communicate
+    /// with a server.
+    enum Protocol {
+        UDP,
+        TCP
+    };
+
+    /// @brief A status code of the DNSClient.
+    enum Status {
+        SUCCESS,           ///< Response received and is ok.
+        TIMEOUT,           ///< No response, timeout.
+        IO_STOPPED,        ///< IO was stopped.
+        INVALID_RESPONSE,  ///< Response received but invalid.
+        OTHER              ///< Other, unclassified error.
+    };
+
+    /// @brief Callback for the @c DNSClient class.
+    ///
+    /// This is is abstract class which represents the external callback for the
+    /// @c DNSClient. Caller must implement this class and supply its instance
+    /// in the @c DNSClient constructor to get callbacks when the DNS Update
+    /// exchange is complete (@see @c DNSClient).
+    class Callback {
+    public:
+        /// @brief Virtual destructor.
+        virtual ~Callback() { }
+
+        /// @brief Function operator implementing a callback.
+        ///
+        /// @param status a @c DNSClient::Status enum representing status code
+        /// of DNSClient operation.
+        virtual void operator()(DNSClient::Status status) = 0;
+    };
+
+    /// @brief Constructor.
+    ///
+    /// @param response_placeholder Pointer to an object which will hold a
+    /// DNS server's response. Caller is responsible for allocating this object.
+    /// @param callback Pointer to an object implementing @c DNSClient::Callback
+    /// class. This object will be called when DNS message exchange completes or
+    /// if an error occurs. NULL value disables callback invocation.
+    /// @param proto caller's preference regarding Transport layer protocol to
+    /// be used by DNS Client to communicate with a server.
+    DNSClient(D2UpdateMessagePtr& response_placeholder, Callback* callback,
+              const Protocol proto = UDP);
+
+    /// @brief Virtual destructor, does nothing.
+    ~DNSClient();
+
+    ///
+    /// @name Copy constructor and assignment operator
+    ///
+    /// Copy constructor and assignment operator are private because there are
+    /// no use cases when @DNSClient instance will need to be copied. Also, it
+    /// is desired to avoid copying @DNSClient::impl_ pointer and external
+    /// callbacks.
+    ///
+    //@{
+private:
+    DNSClient(const DNSClient& source);
+    DNSClient& operator=(const DNSClient& source);
+    //@}
+
+public:
+
+    /// @brief Returns maximal allowed timeout value accepted by
+    /// @c DNSClient::doUpdate.
+    ///
+    /// @return maximal allowed timeout value accepted by @c DNSClient::doUpdate
+    static unsigned int getMaxTimeout();
+
+    /// @brief Start asynchronous DNS Update with TSIG.
+    ///
+    /// This function starts asynchronous DNS Update and returns. The DNS Update
+    /// will be executed by the specified IO service. Once the message exchange
+    /// with a DNS server is complete, timeout occurs or IO operation is
+    /// interrupted, the caller-supplied callback function will be invoked.
+    ///
+    /// An address and port of the DNS server is specified through the function
+    /// arguments so as the same instance of the @c DNSClient can be used to
+    /// initiate multiple message exchanges.
+    ///
+    /// @param io_service IO service to be used to run the message exchange.
+    /// @param ns_addr DNS server address.
+    /// @param ns_port DNS server port.
+    /// @param update A DNS Update message to be sent to the server.
+    /// @param wait A timeout (in seconds) for the response. If a response is
+    /// not received within the timeout, exchange is interrupted. This value
+    /// must not exceed maximal value for 'int' data type.
+    /// @param tsig_key An @c isc::dns::TSIGKey object representing TSIG
+    /// context which will be used to render the DNS Update message.
+    ///
+    /// @todo Implement TSIG Support. Currently any attempt to call this
+    /// function will result in exception.
+    void doUpdate(asiolink::IOService& io_service,
+                  const asiolink::IOAddress& ns_addr,
+                  const uint16_t ns_port,
+                  D2UpdateMessage& update,
+                  const unsigned int wait,
+                  const dns::TSIGKey& tsig_key);
+
+    /// @brief Start asynchronous DNS Update without TSIG.
+    ///
+    /// This function starts asynchronous DNS Update and returns. The DNS Update
+    /// will be executed by the specified IO service. Once the message exchange
+    /// with a DNS server is complete, timeout occurs or IO operation is
+    /// interrupted, the caller-supplied callback function will be invoked.
+    ///
+    /// An address and port of the DNS server is specified through the function
+    /// arguments so as the same instance of the @c DNSClient can be used to
+    /// initiate multiple message exchanges.
+    ///
+    /// @param io_service IO service to be used to run the message exchange.
+    /// @param ns_addr DNS server address.
+    /// @param ns_port DNS server port.
+    /// @param update A DNS Update message to be sent to the server.
+    /// @param wait A timeout (in seconds) for the response. If a response is
+    /// not received within the timeout, exchange is interrupted. This value
+    /// must not exceed maximal value for 'int' data type.
+    void doUpdate(asiolink::IOService& io_service,
+                  const asiolink::IOAddress& ns_addr,
+                  const uint16_t ns_port,
+                  D2UpdateMessage& update,
+                  const unsigned int wait);
+
+private:
+    DNSClientImpl* impl_;  ///< Pointer to DNSClient implementation.
+};
+
+} // namespace d2
+} // namespace isc
+
+#endif // DNS_CLIENT_H
diff --git a/src/bin/d2/main.cc b/src/bin/d2/main.cc
index fda31038..4b5b8bc 100644
--- a/src/bin/d2/main.cc
+++ b/src/bin/d2/main.cc
@@ -38,7 +38,8 @@ int main(int argc, char* argv[]) {
     // Launch the controller passing in command line arguments.
     // Exit program with the controller's return code.
     try  {
-        controller->launch(argc, argv);
+        // 'false' value disables test mode.
+        controller->launch(argc, argv, false);
     } catch (const isc::Exception& ex) {
         std::cerr << "Service failed:" << ex.what() << std::endl;
         ret = EXIT_FAILURE;
diff --git a/src/bin/d2/ncr_msg.cc b/src/bin/d2/ncr_msg.cc
new file mode 100644
index 0000000..cf47176
--- /dev/null
+++ b/src/bin/d2/ncr_msg.cc
@@ -0,0 +1,459 @@
+// Copyright (C) 2013 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 <d2/ncr_msg.h>
+#include <asiolink/io_address.h>
+#include <asiolink/io_error.h>
+
+#include <sstream>
+#include <limits>
+
+namespace isc {
+namespace d2 {
+
+/********************************* D2Dhcid ************************************/
+
+D2Dhcid::D2Dhcid() {
+}
+
+D2Dhcid::D2Dhcid(const std::string& data) {
+    fromStr(data);
+}
+
+void
+D2Dhcid::fromStr(const std::string& data) {
+    bytes_.clear();
+    try {
+        isc::util::encode::decodeHex(data, bytes_);
+    } catch (const isc::Exception& ex) {
+        isc_throw(NcrMessageError, "Invalid data in Dhcid:" << ex.what());
+    }
+}
+
+std::string
+D2Dhcid::toStr() const {
+    return (isc::util::encode::encodeHex(bytes_));
+
+
+}
+
+
+/**************************** NameChangeRequest ******************************/
+
+NameChangeRequest::NameChangeRequest()
+    : change_type_(CHG_ADD), forward_change_(false),
+    reverse_change_(false), fqdn_(""), ip_address_(""),
+    dhcid_(), lease_expires_on_(), lease_length_(0), status_(ST_NEW) {
+}
+
+NameChangeRequest::NameChangeRequest(const NameChangeType change_type,
+            const bool forward_change, const bool reverse_change,
+            const std::string& fqdn, const std::string& ip_address,
+            const D2Dhcid& dhcid,
+            const uint64_t lease_expires_on,
+            const uint32_t lease_length)
+    : change_type_(change_type), forward_change_(forward_change),
+    reverse_change_(reverse_change), fqdn_(fqdn), ip_address_(ip_address),
+    dhcid_(dhcid), lease_expires_on_(lease_expires_on),
+    lease_length_(lease_length), status_(ST_NEW) {
+
+    // Validate the contents. This will throw a NcrMessageError if anything
+    // is invalid.
+    validateContent();
+}
+
+NameChangeRequestPtr
+NameChangeRequest::fromFormat(const NameChangeFormat format,
+                              isc::util::InputBuffer& buffer) {
+    // Based on the format requested, pull the marshalled request from
+    // InputBuffer and pass it into the appropriate format-specific factory.
+    NameChangeRequestPtr ncr;
+    switch (format) {
+    case FMT_JSON: {
+        try {
+            // Get the length of the JSON text.
+            size_t len = buffer.readUint16();
+
+            // Read the text from the buffer into a vector.
+            std::vector<uint8_t> vec;
+            buffer.readVector(vec, len);
+
+            // Turn the vector into a string.
+            std::string string_data(vec.begin(), vec.end());
+
+            // Pass the string of JSON text into JSON factory to create the
+            // NameChangeRequest instance.  Note the factory may throw
+            // NcrMessageError.
+            ncr = NameChangeRequest::fromJSON(string_data);
+        } catch (isc::util::InvalidBufferPosition& ex) {
+            // Read error accessing data in InputBuffer.
+            isc_throw(NcrMessageError, "fromFormat: buffer read error: "
+                      << ex.what());
+        }
+
+        break;
+        }
+    default:
+        // Programmatic error, shouldn't happen.
+        isc_throw(NcrMessageError, "fromFormat - invalid format");
+        break;
+    }
+
+    return (ncr);
+}
+
+void
+NameChangeRequest::toFormat(const NameChangeFormat format,
+                            isc::util::OutputBuffer& buffer) const {
+    // Based on the format requested, invoke the appropriate format handler
+    // which will marshal this request's contents into the OutputBuffer.
+    switch (format) {
+    case FMT_JSON: {
+        // Invoke toJSON to create a JSON text of this request's contents.
+        std::string json = toJSON();
+        uint16_t length = json.size();
+
+        // Write the length of the JSON text to the OutputBuffer first, then
+        // write the JSON text itself.
+        buffer.writeUint16(length);
+        buffer.writeData(json.c_str(), length);
+        break;
+        }
+    default:
+        // Programmatic error, shouldn't happen.
+        isc_throw(NcrMessageError, "toFormat - invalid format");
+        break;
+    }
+}
+
+NameChangeRequestPtr
+NameChangeRequest::fromJSON(const std::string& json) {
+    // This method leverages the existing JSON parsing provided by isc::data
+    // library.  Should this prove to be a performance issue, it may be that
+    // lighter weight solution would be appropriate.
+
+    // Turn the string of JSON text into an Element set.
+    isc::data::ElementPtr elements;
+    try {
+        elements = isc::data::Element::fromJSON(json);
+    } catch (isc::data::JSONError& ex) {
+        isc_throw(NcrMessageError,
+                  "Malformed NameChangeRequest JSON: " << ex.what());
+    }
+
+    // Get a map of the Elements, keyed by element name.
+    ElementMap element_map = elements->mapValue();
+    isc::data::ConstElementPtr element;
+
+    // Use default constructor to create a "blank" NameChangeRequest.
+    NameChangeRequestPtr ncr(new NameChangeRequest());
+
+    // For each member of NameChangeRequest, find its element in the map and
+    // call the appropriate Element-based setter.  These setters may throw
+    // NcrMessageError if the given Element is the wrong type or its data
+    // content is lexically invalid.   If the element is NOT found in the
+    // map, getElement will throw NcrMessageError indicating the missing
+    // member. Currently there are no optional values.
+    element = ncr->getElement("change_type", element_map);
+    ncr->setChangeType(element);
+
+    element = ncr->getElement("forward_change", element_map);
+    ncr->setForwardChange(element);
+
+    element = ncr->getElement("reverse_change", element_map);
+    ncr->setReverseChange(element);
+
+    element = ncr->getElement("fqdn", element_map);
+    ncr->setFqdn(element);
+
+    element = ncr->getElement("ip_address", element_map);
+    ncr->setIpAddress(element);
+
+    element = ncr->getElement("dhcid", element_map);
+    ncr->setDhcid(element);
+
+    element = ncr->getElement("lease_expires_on", element_map);
+    ncr->setLeaseExpiresOn(element);
+
+    element = ncr->getElement("lease_length", element_map);
+    ncr->setLeaseLength(element);
+
+    // All members were in the Element set and were correct lexically. Now
+    // validate the overall content semantically.  This will throw an
+    // NcrMessageError if anything is amiss.
+    ncr->validateContent();
+
+    // Everything is valid, return the new instance.
+    return (ncr);
+}
+
+std::string
+NameChangeRequest::toJSON() const  {
+    // Create a JSON string of this request's contents.  Note that this method
+    // does NOT use the isc::data library as generating the output is straight
+    // forward.
+    std::ostringstream stream;
+
+    stream << "{\"change_type\":" << getChangeType() << ","
+        << "\"forward_change\":"
+        << (isForwardChange() ? "true" : "false") << ","
+        << "\"reverse_change\":"
+        << (isReverseChange() ? "true" : "false") << ","
+        << "\"fqdn\":\"" << getFqdn() << "\","
+        << "\"ip_address\":\"" << getIpAddress() << "\","
+        << "\"dhcid\":\"" << getDhcid().toStr() << "\","
+        << "\"lease_expires_on\":\""  << getLeaseExpiresOnStr() << "\","
+        << "\"lease_length\":" << getLeaseLength() << "}";
+
+    return (stream.str());
+}
+
+
+void
+NameChangeRequest::validateContent() {
+    //@todo This is an initial implementation which provides a minimal amount
+    // of validation.  FQDN, DHCID, and IP Address members are all currently
+    // strings, these may be replaced with richer classes.
+    if (fqdn_ == "") {
+        isc_throw(NcrMessageError, "FQDN cannot be blank");
+    }
+
+    // Validate IP Address.
+    try {
+        isc::asiolink::IOAddress io_addr(ip_address_);
+    } catch (const isc::asiolink::IOError& ex) {
+        isc_throw(NcrMessageError,
+                  "Invalid ip address string for ip_address: " << ip_address_);
+    }
+
+    // Validate the DHCID.
+    if (dhcid_.getBytes().size()  == 0) {
+        isc_throw(NcrMessageError, "DHCID cannot be blank");
+    }
+
+    // Ensure the request specifies at least one direction to update.
+    if (!forward_change_ && !reverse_change_) {
+        isc_throw(NcrMessageError,
+                  "Invalid Request, forward and reverse flags are both false");
+    }
+}
+
+isc::data::ConstElementPtr
+NameChangeRequest::getElement(const std::string& name,
+                              const ElementMap& element_map) const {
+    // Look for "name" in the element map.
+    ElementMap::const_iterator it = element_map.find(name);
+    if (it == element_map.end()) {
+        // Didn't find the element, so throw.
+        isc_throw(NcrMessageError,
+                  "NameChangeRequest value missing for: " << name );
+    }
+
+    // Found the element, return it.
+    return (it->second);
+}
+
+void
+NameChangeRequest::setChangeType(const NameChangeType value) {
+    change_type_ = value;
+}
+
+
+void
+NameChangeRequest::setChangeType(isc::data::ConstElementPtr element) {
+    long raw_value = -1;
+    try {
+        // Get the element's integer value.
+        raw_value = element->intValue();
+    } catch (isc::data::TypeError& ex) {
+        // We expect a integer Element type, don't have one.
+        isc_throw(NcrMessageError,
+                  "Wrong data type for change_type: " << ex.what());
+    }
+
+    if ((raw_value != CHG_ADD) && (raw_value != CHG_REMOVE)) {
+        // Value is not a valid change type.
+        isc_throw(NcrMessageError,
+                  "Invalid data value for change_type: " << raw_value);
+    }
+
+    // Good to go, make the assignment.
+    setChangeType(static_cast<NameChangeType>(raw_value));
+}
+
+void
+NameChangeRequest::setForwardChange(const bool value) {
+    forward_change_ = value;
+}
+
+void
+NameChangeRequest::setForwardChange(isc::data::ConstElementPtr element) {
+    bool value;
+    try {
+        // Get the element's boolean value.
+        value = element->boolValue();
+    } catch (isc::data::TypeError& ex) {
+        // We expect a boolean Element type, don't have one.
+        isc_throw(NcrMessageError,
+                  "Wrong data type for forward_change :" << ex.what());
+    }
+
+    // Good to go, make the assignment.
+    setForwardChange(value);
+}
+
+void
+NameChangeRequest::setReverseChange(const bool value) {
+    reverse_change_ = value;
+}
+
+void
+NameChangeRequest::setReverseChange(isc::data::ConstElementPtr element) {
+    bool value;
+    try {
+        // Get the element's boolean value.
+        value = element->boolValue();
+    } catch (isc::data::TypeError& ex) {
+        // We expect a boolean Element type, don't have one.
+        isc_throw(NcrMessageError,
+                  "Wrong data type for reverse_change :" << ex.what());
+    }
+
+    // Good to go, make the assignment.
+    setReverseChange(value);
+}
+
+
+void
+NameChangeRequest::setFqdn(isc::data::ConstElementPtr element) {
+    setFqdn(element->stringValue());
+}
+
+void
+NameChangeRequest::setFqdn(const std::string& value) {
+    fqdn_ = value;
+}
+
+void
+NameChangeRequest::setIpAddress(const std::string& value) {
+    ip_address_ = value;
+}
+
+
+void
+NameChangeRequest::setIpAddress(isc::data::ConstElementPtr element) {
+    setIpAddress(element->stringValue());
+}
+
+
+void
+NameChangeRequest::setDhcid(const std::string& value) {
+    dhcid_.fromStr(value);
+}
+
+void
+NameChangeRequest::setDhcid(isc::data::ConstElementPtr element) {
+    setDhcid(element->stringValue());
+}
+
+std::string
+NameChangeRequest::getLeaseExpiresOnStr() const {
+    return (isc::util::timeToText64(lease_expires_on_));
+}
+
+void
+NameChangeRequest::setLeaseExpiresOn(const std::string&  value) {
+    try {
+        lease_expires_on_ = isc::util::timeFromText64(value);
+    } catch(...) {
+        // We were given an invalid string, so throw.
+        isc_throw(NcrMessageError,
+            "Invalid date-time string: [" << value << "]");
+    }
+
+}
+
+void NameChangeRequest::setLeaseExpiresOn(isc::data::ConstElementPtr element) {
+    // Pull out the string value and pass it into the string setter.
+    setLeaseExpiresOn(element->stringValue());
+}
+
+void
+NameChangeRequest::setLeaseLength(const uint32_t value) {
+    lease_length_ = value;
+}
+
+void
+NameChangeRequest::setLeaseLength(isc::data::ConstElementPtr element) {
+    long value = -1;
+    try {
+        // Get the element's integer value.
+        value = element->intValue();
+    } catch (isc::data::TypeError& ex) {
+        // We expect a integer Element type, don't have one.
+        isc_throw(NcrMessageError,
+                  "Wrong data type for lease_length: " << ex.what());
+    }
+
+    // Make sure we the range is correct and value is positive.
+    if (value > std::numeric_limits<uint32_t>::max()) {
+        isc_throw(NcrMessageError, "lease_length value " << value <<
+                "is too large for unsigned 32-bit integer.");
+    }
+    if (value < 0) {
+        isc_throw(NcrMessageError, "lease_length value " << value <<
+             "is negative.  It must greater than or equal to zero ");
+    }
+
+    // Good to go, make the assignment.
+    setLeaseLength(static_cast<uint32_t>(value));
+}
+
+void
+NameChangeRequest::setStatus(const NameChangeStatus value) {
+    status_ = value;
+}
+
+std::string
+NameChangeRequest::toText() const {
+    std::ostringstream stream;
+
+    stream << "Type: " << static_cast<int>(change_type_) << " (";
+    switch (change_type_) {
+    case CHG_ADD:
+        stream << "CHG_ADD)\n";
+        break;
+    case CHG_REMOVE:
+        stream << "CHG_REMOVE)\n";
+        break;
+    default:
+        // Shouldn't be possible.
+        stream << "Invalid Value\n";
+    }
+
+    stream << "Forward Change: " << (forward_change_ ? "yes" : "no")
+           << std::endl
+           << "Reverse Change: " << (reverse_change_ ? "yes" : "no")
+           << std::endl
+           << "FQDN: [" << fqdn_ << "]" << std::endl
+           << "IP Address: [" << ip_address_  << "]" << std::endl
+           << "DHCID: [" << dhcid_.toStr() << "]" << std::endl
+           << "Lease Expires On: " << getLeaseExpiresOnStr() << std::endl
+           << "Lease Length: " << lease_length_ << std::endl;
+
+    return (stream.str());
+}
+
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
diff --git a/src/bin/d2/ncr_msg.h b/src/bin/d2/ncr_msg.h
new file mode 100644
index 0000000..b3a4153
--- /dev/null
+++ b/src/bin/d2/ncr_msg.h
@@ -0,0 +1,487 @@
+// Copyright (C) 2013 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 NCR_MSG_H
+#define NCR_MSG_H
+
+/// @file ncr_msg.h
+/// @brief This file provides the classes needed to embody, compose, and
+/// decompose DNS update requests that are sent by DHCP-DDNS clients to
+/// DHCP-DDNS. These requests are referred to as NameChangeRequests.
+
+#include <cc/data.h>
+#include <exceptions/exceptions.h>
+#include <util/buffer.h>
+#include <util/encode/hex.h>
+#include <util/time_utilities.h>
+
+#include <time.h>
+#include <string>
+
+namespace isc {
+namespace d2 {
+
+/// @brief Exception thrown when NameChangeRequest marshalling error occurs.
+class NcrMessageError : public isc::Exception {
+public:
+    NcrMessageError(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) { };
+};
+
+/// @brief Defines the types of DNS updates that can be requested.
+enum NameChangeType {
+  CHG_ADD,
+  CHG_REMOVE
+};
+
+/// @brief Defines the runtime processing status values for requests.
+enum NameChangeStatus  {
+  ST_NEW,
+  ST_PENDING,
+  ST_COMPLETED,
+  ST_FAILED,
+};
+
+/// @brief Defines the list of data wire formats supported.
+enum NameChangeFormat {
+  FMT_JSON
+};
+
+/// @brief Container class for handling the DHCID value within a
+/// NameChangeRequest. It provides conversion to and from string for JSON
+/// formatting, but stores the data internally as unsigned bytes.
+class D2Dhcid {
+public:
+    /// @brief Default constructor
+    D2Dhcid();
+
+    /// @brief Constructor - Creates a new instance, populated by converting
+    /// a given string of digits into an array of unsigned bytes.
+    ///
+    /// @param data is a string of hexadecimal digits. The format is simply
+    /// a contiguous stream of digits, with no delimiters. For example a string
+    /// containing "14A3" converts to a byte array containing:  0x14, 0xA3.
+    ///
+    /// @throw throws a NcrMessageError if the input data contains non-digits
+    /// or there is an odd number of digits.
+    D2Dhcid(const std::string& data);
+
+    /// @brief Returns the DHCID value as a string of hexadecimal digits.
+    ///
+    /// @return returns a string containing a contiguous stream of digits.
+    std::string toStr() const;
+
+    /// @brief Sets the DHCID value based on the given string.
+    ///
+    /// @param data is a string of hexadecimal digits. The format is simply
+    /// a contiguous stream of digits, with no delimiters. For example a string
+    /// containing "14A3" converts to a byte array containing:  0x14, 0xA3.
+    ///
+    /// @throw throws a NcrMessageError if the input data contains non-digits
+    /// or there is an odd number of digits.
+    void fromStr(const std::string& data);
+
+    /// @brief Returns a reference to the DHCID byte vector.
+    ///
+    /// @return returns a reference to the vector.
+    const std::vector<uint8_t>& getBytes() {
+        return (bytes_);
+    }
+
+private:
+    /// @brief Storage for the DHCID value in unsigned bytes.
+    std::vector<uint8_t> bytes_;
+};
+
+class NameChangeRequest;
+/// @brief Defines a pointer to a NameChangeRequest.
+typedef boost::shared_ptr<NameChangeRequest> NameChangeRequestPtr;
+
+/// @brief Defines a map of Elements, keyed by their string name.
+typedef std::map<std::string, isc::data::ConstElementPtr> ElementMap;
+
+/// @brief  Represents a DHCP-DDNS client request.
+/// This class is used by DHCP-DDNS clients (e.g. DHCP4, DHCP6) to
+/// request DNS updates.  Each message contains a single DNS change (either an
+/// add/update or a remove) for a single FQDN.  It provides marshalling services
+/// for moving instances to and from the wire.  Currently, the only format
+/// supported is JSON, however the class provides an interface such that other
+/// formats can be readily supported.
+class NameChangeRequest {
+public:
+    /// @brief Default Constructor.
+    NameChangeRequest();
+
+    /// @brief Constructor.  Full constructor, which provides parameters for
+    /// all of the class members, except status.
+    ///
+    /// @param change_type the type of change (Add or Update)
+    /// @param forward_change indicates if this change should be sent to forward
+    /// DNS servers.
+    /// @param reverse_change indicates if this change should be sent to reverse
+    /// DNS servers.
+    /// @param fqdn the domain name whose pointer record(s) should be
+    /// updated.
+    /// @param ip_address the ip address leased to the given FQDN.
+    /// @param dhcid the lease client's unique DHCID.
+    /// @param lease_expires_on a timestamp containing the date/time the lease
+    /// expires.
+    /// @param lease_length the amount of time in seconds for which the
+    /// lease is valid (TTL).
+    NameChangeRequest(const NameChangeType change_type,
+                      const bool forward_change, const bool reverse_change,
+                      const std::string& fqdn, const std::string& ip_address,
+                      const D2Dhcid& dhcid,
+                      const uint64_t lease_expires_on,
+                      const uint32_t lease_length);
+
+    /// @brief Static method for creating a NameChangeRequest from a
+    /// buffer containing a marshalled request in a given format.
+    ///
+    /// When the format is:
+    ///
+    /// JSON: The buffer is expected to contain a two byte unsigned integer
+    /// which specified the length of the JSON text; followed by the JSON
+    /// text itself.  This method attempts to extract "length" characters
+    /// from the buffer. This data is used to create a character string that
+    /// is than treated as JSON which is then parsed into the data needed
+    /// to create a request instance.
+    ///
+    /// (NOTE currently only JSON is supported.)
+    ///
+    /// @param format indicates the data format to use
+    /// @param buffer is the input buffer containing the marshalled request
+    ///
+    /// @return returns a pointer to the new NameChangeRequest
+    ///
+    /// @throw throws NcrMessageError if an error occurs creating new
+    /// request.
+    static NameChangeRequestPtr fromFormat(const NameChangeFormat format,
+                                           isc::util::InputBuffer& buffer);
+
+    /// @brief Instance method for marshalling the contents of the request
+    /// into the given buffer in the given format.
+    ///
+    /// When the format is:
+    ///
+    /// JSON: Upon completion, the buffer will contain a two byte unsigned
+    /// integer which specifies the length of the JSON text; followed by the
+    /// JSON text itself. The JSON text contains the names and values for all
+    /// the request data needed to reassemble the request on the receiving
+    /// end. The JSON text in the buffer is NOT null-terminated.
+    ///
+    /// (NOTE currently only JSON is supported.)
+    ///
+    /// @param format indicates the data format to use
+    /// @param buffer is the output buffer to which the request should be
+    /// marshalled.
+    void toFormat(const NameChangeFormat format,
+                  isc::util::OutputBuffer& buffer) const;
+
+    /// @brief Static method for creating a NameChangeRequest from a
+    /// string containing a JSON rendition of a request.
+    ///
+    /// @param json is a string containing the JSON text
+    ///
+    /// @return returns a pointer to the new NameChangeRequest
+    ///
+    /// @throw throws NcrMessageError if an error occurs creating new request.
+    static NameChangeRequestPtr fromJSON(const std::string& json);
+
+    /// @brief Instance method for marshalling the contents of the request
+    /// into a string of JSON text.
+    ///
+    /// @return returns a string containing the JSON rendition of the request
+    std::string toJSON() const;
+
+    /// @brief Validates the content of a populated request.  This method is
+    /// used by both the full constructor and from-wire marshalling to ensure
+    /// that the request is content valid.  Currently it enforces the
+    /// following rules:
+    ///
+    ///  - FQDN must not be blank.
+    ///  - The IP address must be a valid address.
+    ///  - The DHCID must not be blank.
+    ///  - The lease expiration date must be a valid date/time.
+    ///  - That at least one of the two direction flags, forward change and
+    ///    reverse change is true.
+    ///
+    /// @todo This is an initial implementation which provides a minimal amount
+    /// of validation.  FQDN, DHCID, and IP Address members are all currently
+    /// strings, these may be replaced with richer classes.
+    ///
+    /// @throw throws a NcrMessageError if the request content violates any
+    /// of the validation rules.
+    void validateContent();
+
+    /// @brief Fetches the request change type.
+    ///
+    /// @return returns the change type
+    NameChangeType getChangeType() const {
+        return (change_type_);
+    }
+
+    /// @brief Sets the change type to the given value.
+    ///
+    /// @param value is the NameChangeType value to assign to the request.
+    void setChangeType(const NameChangeType value);
+
+    /// @brief Sets the change type to the value of the given Element.
+    ///
+    /// @param element is an integer Element containing the change type value.
+    ///
+    /// @throw throws a NcrMessageError if the element is not an integer
+    /// Element or contains an invalid value.
+    void setChangeType(isc::data::ConstElementPtr element);
+
+    /// @brief Checks forward change flag.
+    ///
+    /// @return returns a true if the forward change flag is true.
+    bool isForwardChange() const {
+        return (forward_change_);
+    }
+
+    /// @brief Sets the forward change flag to the given value.
+    ///
+    /// @param value contains the new value to assign to the forward change
+    /// flag
+    void setForwardChange(const bool value);
+
+    /// @brief Sets the forward change flag to the value of the given Element.
+    ///
+    /// @param element is a boolean Element containing the forward change flag
+    /// value.
+    ///
+    /// @throw throws a NcrMessageError if the element is not a boolean
+    /// Element
+    void setForwardChange(isc::data::ConstElementPtr element);
+
+    /// @brief Checks reverse change flag.
+    ///
+    /// @return returns a true if the reverse change flag is true.
+    bool isReverseChange() const {
+        return (reverse_change_);
+    }
+
+    /// @brief Sets the reverse change flag to the given value.
+    ///
+    /// @param value contains the new value to assign to the reverse change
+    /// flag
+    void setReverseChange(const bool value);
+
+    /// @brief Sets the reverse change flag to the value of the given Element.
+    ///
+    /// @param element is a boolean Element containing the reverse change flag
+    /// value.
+    ///
+    /// @throw throws a NcrMessageError if the element is not a boolean
+    /// Element
+    void setReverseChange(isc::data::ConstElementPtr element);
+
+    /// @brief Fetches the request FQDN
+    ///
+    /// @return returns a string containing the FQDN
+    const std::string getFqdn() const {
+        return (fqdn_);
+    }
+
+    /// @brief Sets the FQDN to the given value.
+    ///
+    /// @param value contains the new value to assign to the FQDN
+    void setFqdn(const std::string& value);
+
+    /// @brief Sets the FQDN to the value of the given Element.
+    ///
+    /// @param element is a string Element containing the FQDN
+    ///
+    /// @throw throws a NcrMessageError if the element is not a string
+    /// Element
+    void setFqdn(isc::data::ConstElementPtr element);
+
+    /// @brief Fetches the request IP address.
+    ///
+    /// @return returns a string containing the IP address
+    const std::string& getIpAddress() const {
+        return (ip_address_);
+    }
+
+    /// @brief Sets the IP address to the given value.
+    ///
+    /// @param value contains the new value to assign to the IP address
+    void setIpAddress(const std::string& value);
+
+    /// @brief Sets the IP address to the value of the given Element.
+    ///
+    /// @param element is a string Element containing the IP address
+    ///
+    /// @throw throws a NcrMessageError if the element is not a string
+    /// Element
+    void setIpAddress(isc::data::ConstElementPtr element);
+
+    /// @brief Fetches the request DHCID
+    ///
+    /// @return returns a reference to the request's D2Dhcid
+    const D2Dhcid& getDhcid() const {
+        return (dhcid_);
+    }
+
+    /// @brief Sets the DHCID based on the given string value.
+    ///
+    /// @param value is a string of hexadecimal digits. The format is simply
+    /// a contiguous stream of digits, with no delimiters. For example a string
+    /// containing "14A3" converts to a byte array containing:  0x14, 0xA3.
+    ///
+    /// @throw throws a NcrMessageError if the input data contains non-digits
+    /// or there is an odd number of digits.
+    void setDhcid(const std::string& value);
+
+    /// @brief Sets the DHCID based on the value of the given Element.
+    ///
+    /// @param element is a string Element containing the string of hexadecimal
+    /// digits. (See setDhcid(std::string&) above.)
+    ///
+    /// @throw throws a NcrMessageError if the input data contains non-digits
+    /// or there is an odd number of digits.
+    void setDhcid(isc::data::ConstElementPtr element);
+
+    /// @brief Fetches the request lease expiration
+    ///
+    /// @return returns the lease expiration as the number of seconds since
+    /// the (00:00:00 January 1, 1970)
+    uint64_t getLeaseExpiresOn() const {
+        return (lease_expires_on_);
+    }
+
+    /// @brief Fetches the request lease expiration as string.
+    ///
+    /// The format of the string returned is:
+    ///
+    ///    YYYYMMDDHHmmSS
+    ///
+    /// Example: 18:54:54 June 26, 2013 would be: 20130626185455
+    /// NOTE This is always UTC time.
+    ///
+    /// @return returns a ISO date-time string of the lease expiration.
+    std::string getLeaseExpiresOnStr() const;
+
+    /// @brief Sets the lease expiration based on the given string.
+    ///
+    /// @param value is an date-time string from which to set the
+    /// lease expiration. The format of the input is:
+    ///
+    ///    YYYYMMDDHHmmSS
+    ///
+    /// Example: 18:54:54 June 26, 2013 would be: 20130626185455
+    /// NOTE This is always UTC time.
+    ///
+    /// @throw throws a NcrMessageError if the ISO string is invalid.
+    void setLeaseExpiresOn(const std::string& value);
+
+    /// @brief Sets the lease expiration based on the given Element.
+    ///
+    /// @param element is string Element containing a date-time string.
+    ///
+    /// @throw throws a NcrMessageError if the element is not a string
+    /// Element, or if the element value is an invalid date-time string.
+    void setLeaseExpiresOn(isc::data::ConstElementPtr element);
+
+    /// @brief Fetches the request lease length.
+    ///
+    /// @return returns an integer containing the lease length
+    uint32_t getLeaseLength() const {
+        return (lease_length_);
+    }
+
+    /// @brief Sets the lease length to the given value.
+    ///
+    /// @param value contains the new value to assign to the lease length
+    void setLeaseLength(const uint32_t value);
+
+    /// @brief Sets the lease length to the value of the given Element.
+    ///
+    /// @param element is a integer Element containing the lease length
+    ///
+    /// @throw throws a NcrMessageError if the element is not a string
+    /// Element
+    void setLeaseLength(isc::data::ConstElementPtr element);
+
+    /// @brief Fetches the request status.
+    ///
+    /// @return returns the request status as a NameChangeStatus
+    NameChangeStatus getStatus() const {
+        return (status_);
+    }
+
+    /// @brief Sets the request status to the given value.
+    ///
+    /// @param value contains the new value to assign to request status
+    void setStatus(const NameChangeStatus value);
+
+    /// @brief Given a name, finds and returns an element from a map of
+    /// elements.
+    ///
+    /// @param name is the name of the desired element
+    /// @param element_map is the map of elements to search
+    ///
+    /// @return returns a pointer to the element if located
+    /// @throw throws a NcrMessageError if the element cannot be found within
+    /// the map
+    isc::data::ConstElementPtr getElement(const std::string& name,
+                                          const ElementMap& element_map) const;
+
+    /// @brief Returns a text rendition of the contents of the request.
+    /// This method is primarily for logging purposes.
+    ///
+    /// @return returns a string containing the text.
+    std::string toText() const;
+
+private:
+    /// @brief Denotes the type of this change as either an Add or a Remove.
+    NameChangeType change_type_;
+
+    /// @brief Indicates if this change should sent to forward DNS servers.
+    bool forward_change_;
+
+    /// @brief Indicates if this change should sent to reverse DNS servers.
+    bool reverse_change_;
+
+    /// @brief The domain name whose DNS entry(ies) are to be updated.
+    /// @todo Currently, this is a std::string but may be replaced with
+    /// dns::Name which provides additional validation and domain name
+    /// manipulation.
+    std::string fqdn_;
+
+    /// @brief The ip address leased to the FQDN.
+    std::string ip_address_;
+
+    /// @brief The lease client's unique DHCID.
+    /// @todo Currently, this is uses D2Dhcid it but may be replaced with
+    /// dns::DHCID which provides additional validation.
+    D2Dhcid dhcid_;
+
+    /// @brief The date-time the lease expires.
+    uint64_t lease_expires_on_;
+
+    /// @brief The amount of time in seconds for which the lease is valid (TTL).
+    uint32_t lease_length_;
+
+    /// @brief The processing status of the request.  Used internally.
+    NameChangeStatus status_;
+};
+
+
+}; // end of isc::d2 namespace
+}; // end of isc namespace
+
+#endif
diff --git a/src/bin/d2/tests/.gitignore b/src/bin/d2/tests/.gitignore
index 9a1b143..3e1a1b7 100644
--- a/src/bin/d2/tests/.gitignore
+++ b/src/bin/d2/tests/.gitignore
@@ -1 +1,2 @@
 /d2_unittests
+/test_data_files_config.h
diff --git a/src/bin/d2/tests/Makefile.am b/src/bin/d2/tests/Makefile.am
index bd7773b..00bd36f 100644
--- a/src/bin/d2/tests/Makefile.am
+++ b/src/bin/d2/tests/Makefile.am
@@ -56,11 +56,24 @@ d2_unittests_SOURCES += ../d_process.h
 d2_unittests_SOURCES += ../d_controller.cc ../d2_controller.h
 d2_unittests_SOURCES += ../d2_process.cc ../d2_process.h
 d2_unittests_SOURCES += ../d2_controller.cc ../d2_controller.h
+d2_unittests_SOURCES += ../d_cfg_mgr.cc ../d_cfg_mgr.h
+d2_unittests_SOURCES += ../d2_config.cc ../d2_config.h
+d2_unittests_SOURCES += ../d2_cfg_mgr.cc ../d2_cfg_mgr.h
+d2_unittests_SOURCES += ../d2_update_message.cc ../d2_update_message.h
+d2_unittests_SOURCES += ../d2_zone.cc ../d2_zone.h
+d2_unittests_SOURCES += ../dns_client.cc ../dns_client.h
+d2_unittests_SOURCES += ../ncr_msg.cc ../ncr_msg.h
 d2_unittests_SOURCES += d_test_stubs.cc d_test_stubs.h
 d2_unittests_SOURCES += d2_unittests.cc
 d2_unittests_SOURCES += d2_process_unittests.cc
 d2_unittests_SOURCES += d_controller_unittests.cc
 d2_unittests_SOURCES += d2_controller_unittests.cc
+d2_unittests_SOURCES += d_cfg_mgr_unittests.cc
+d2_unittests_SOURCES += d2_cfg_mgr_unittests.cc
+d2_unittests_SOURCES += d2_update_message_unittests.cc
+d2_unittests_SOURCES += d2_zone_unittests.cc
+d2_unittests_SOURCES += dns_client_unittests.cc
+d2_unittests_SOURCES += ncr_unittests.cc
 nodist_d2_unittests_SOURCES = ../d2_messages.h ../d2_messages.cc
 
 d2_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
@@ -68,9 +81,15 @@ d2_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
 d2_unittests_LDADD = $(GTEST_LDADD)
 d2_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
 d2_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
+d2_unittests_LDADD += $(top_builddir)/src/lib/asiodns/libb10-asiodns.la
 d2_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
 d2_unittests_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
 d2_unittests_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
+d2_unittests_LDADD += $(top_builddir)/src/lib/dhcpsrv/libb10-dhcpsrv.la
+d2_unittests_LDADD += $(top_builddir)/src/lib/dns/libb10-dns++.la
+d2_unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
+d2_unittests_LDADD += $(top_builddir)/src/lib/hooks/libb10-hooks.la
+
 endif
 
 noinst_PROGRAMS = $(TESTS)
diff --git a/src/bin/d2/tests/d2_cfg_mgr_unittests.cc b/src/bin/d2/tests/d2_cfg_mgr_unittests.cc
new file mode 100644
index 0000000..9f5a660
--- /dev/null
+++ b/src/bin/d2/tests/d2_cfg_mgr_unittests.cc
@@ -0,0 +1,1238 @@
+// Copyright (C) 2013  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/module_spec.h>
+#include <d2/d2_config.h>
+#include <d2/d2_cfg_mgr.h>
+#include <d_test_stubs.h>
+#include <test_data_files_config.h>
+
+#include <boost/foreach.hpp>
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::d2;
+
+namespace {
+
+std::string specfile(const std::string& name) {
+    return (std::string(D2_SRC_DIR) + "/" + name);
+}
+
+/// @brief Test fixture class for testing D2CfgMgr class.
+/// It maintains an member instance of D2CfgMgr and provides methods for
+/// converting JSON strings to configuration element sets, checking parse
+/// results, and accessing the configuration context.
+class D2CfgMgrTest : public ConfigParseTest {
+public:
+
+    /// @brief Constructor
+    D2CfgMgrTest():cfg_mgr_(new D2CfgMgr) {
+    }
+
+    /// @brief Destructor
+    ~D2CfgMgrTest() {
+    }
+
+    /// @brief Configuration manager instance.
+    D2CfgMgrPtr cfg_mgr_;
+};
+
+/// @brief Tests that the spec file is valid.
+/// Verifies that the BIND10 DHCP-DDNS configuration specification file
+//  is valid.
+TEST(D2SpecTest, basicSpecTest) {
+    ASSERT_NO_THROW(isc::config::
+                    moduleSpecFromFile(specfile("dhcp-ddns.spec")));
+}
+
+/// @brief Convenience function which compares the contents of the given
+/// DnsServerInfo against the given set of values.
+///
+/// It is structured in such a way that each value is checked, and output
+/// is generate for all that do not match.
+///
+/// @param server is a pointer to the server to check against.
+/// @param hostname is the value to compare against server's hostname_.
+/// @param ip_address is the string value to compare against server's
+/// ip_address_.
+/// @param port is the value to compare against server's port.
+///
+/// @return returns true if there is a match across the board, otherwise it
+/// returns false.
+bool checkServer(DnsServerInfoPtr server, const char* hostname,
+                 const char *ip_address, uint32_t port)
+{
+    // Return value, assume its a match.
+    bool result = true;
+
+    if (!server) {
+        EXPECT_TRUE(server);
+        return false;
+    }
+
+    // Check hostname.
+    if (server->getHostname() != hostname) {
+        EXPECT_EQ(hostname, server->getHostname());
+        result = false;
+    }
+
+    // Check IP address.
+    if (server->getIpAddress().toText() != ip_address) {
+        EXPECT_EQ(ip_address, server->getIpAddress().toText());
+        result = false;
+    }
+
+    // Check port.
+    if (server->getPort() !=  port) {
+        EXPECT_EQ (port, server->getPort());
+        result = false;
+    }
+
+    return (result);
+}
+
+/// @brief Convenience function which compares the contents of the given
+/// TSIGKeyInfo against the given set of values.
+///
+/// It is structured in such a way that each value is checked, and output
+/// is generate for all that do not match.
+///
+/// @param key is a pointer to the key to check against.
+/// @param name is the value to compare against key's name_.
+/// @param algorithm is the string value to compare against key's algorithm.
+/// @param secret is the value to compare against key's secret.
+///
+/// @return returns true if there is a match across the board, otherwise it
+/// returns false.
+bool checkKey(TSIGKeyInfoPtr key, const char* name,
+                 const char *algorithm, const char* secret)
+{
+    // Return value, assume its a match.
+    bool result = true;
+    if (!key) {
+        EXPECT_TRUE(key);
+        return false;
+    }
+
+    // Check name.
+    if (key->getName() != name) {
+        EXPECT_EQ(name, key->getName());
+        result = false;
+    }
+
+    // Check algorithm.
+    if (key->getAlgorithm() != algorithm) {
+        EXPECT_EQ(algorithm, key->getAlgorithm());
+        result = false;
+    }
+
+    // Check secret.
+    if (key->getSecret() !=  secret) {
+        EXPECT_EQ (secret, key->getSecret());
+        result = false;
+    }
+
+    return (result);
+}
+
+/// @brief Test fixture class for testing DnsServerInfo parsing.
+class TSIGKeyInfoTest : public ConfigParseTest {
+public:
+
+    /// @brief Constructor
+    TSIGKeyInfoTest() {
+        reset();
+    }
+
+    /// @brief Destructor
+    ~TSIGKeyInfoTest() {
+    }
+
+    /// @brief Wipe out the current storage and parser and replace
+    /// them with new ones.
+    void reset() {
+        keys_.reset(new TSIGKeyInfoMap());
+        parser_.reset(new TSIGKeyInfoParser("test", keys_));
+    }
+
+    /// @brief Storage for "committing" keys.
+    TSIGKeyInfoMapPtr keys_;
+
+    /// @brief Pointer to the current parser instance.
+    isc::dhcp::ParserPtr parser_;
+};
+
+/// @brief Test fixture class for testing DnsServerInfo parsing.
+class DnsServerInfoTest : public ConfigParseTest {
+public:
+
+    /// @brief Constructor
+    DnsServerInfoTest() {
+        reset();
+    }
+
+    /// @brief Destructor
+    ~DnsServerInfoTest() {
+    }
+
+    /// @brief Wipe out the current storage and parser and replace
+    /// them with new ones.
+    void reset() {
+        servers_.reset(new DnsServerInfoStorage());
+        parser_.reset(new DnsServerInfoParser("test", servers_));
+    }
+
+    /// @brief Storage for "committing" servers.
+    DnsServerInfoStoragePtr servers_;
+
+    /// @brief Pointer to the current parser instance.
+    isc::dhcp::ParserPtr parser_;
+};
+
+
+/// @brief Test fixture class for testing DDnsDomain parsing.
+class DdnsDomainTest : public ConfigParseTest {
+public:
+
+    /// @brief Constructor
+    DdnsDomainTest() {
+        reset();
+    }
+
+    /// @brief Destructor
+    ~DdnsDomainTest() {
+    }
+
+    /// @brief Wipe out the current storage and parser and replace
+    /// them with new ones.
+    void reset() {
+        keys_.reset(new TSIGKeyInfoMap());
+        domains_.reset(new DdnsDomainMap());
+        parser_.reset(new DdnsDomainParser("test", domains_, keys_));
+    }
+
+    /// @brief Add TSIGKeyInfos to the key map
+    ///
+    /// @param name the name of the key
+    /// @param algorithm the algorithm of the key
+    /// @param secret the secret value of the key
+    void addKey(const std::string& name, const std::string& algorithm,
+                const std::string& secret) {
+        TSIGKeyInfoPtr key_info(new TSIGKeyInfo(name, algorithm, secret));
+        (*keys_)[name]=key_info;
+    }
+
+    /// @brief Storage for "committing" domains.
+    DdnsDomainMapPtr domains_;
+
+    /// @brief Storage for TSIGKeys
+    TSIGKeyInfoMapPtr keys_;
+
+    /// @brief Pointer to the current parser instance.
+    isc::dhcp::ParserPtr parser_;
+};
+
+/// @brief Tests the enforcement of data validation when parsing TSIGKeyInfos.
+/// It verifies that:
+/// 1. Name cannot be blank.
+/// 2. Algorithm cannot be blank.
+/// 3. Secret cannot be blank.
+/// @TODO TSIG keys are not fully functional. Only basic validation is
+/// currently supported. This test will need to expand as they evolve.
+TEST_F(TSIGKeyInfoTest, invalidEntryTests) {
+    // Config with a blank name entry.
+    std::string config = "{"
+                         " \"name\": \"\" , "
+                         " \"algorithm\": \"md5\" , "
+                         " \"secret\": \"0123456789\" "
+                         "}";
+    ASSERT_TRUE(fromJSON(config));
+
+    // Verify that build succeeds but commit fails on blank name.
+    EXPECT_NO_THROW(parser_->build(config_set_));
+    EXPECT_THROW(parser_->commit(), D2CfgError);
+
+    // Config with a blank algorithm entry.
+    config = "{"
+                         " \"name\": \"d2_key_one\" , "
+                         " \"algorithm\": \"\" , "
+                         " \"secret\": \"0123456789\" "
+                         "}";
+
+    ASSERT_TRUE(fromJSON(config));
+
+    // Verify that build succeeds but commit fails on blank algorithm.
+    EXPECT_NO_THROW(parser_->build(config_set_));
+    EXPECT_THROW(parser_->commit(), D2CfgError);
+
+    // Config with a blank secret entry.
+    config = "{"
+                         " \"name\": \"d2_key_one\" , "
+                         " \"algorithm\": \"md5\" , "
+                         " \"secret\": \"\" "
+                         "}";
+
+    ASSERT_TRUE(fromJSON(config));
+
+    // Verify that build succeeds but commit fails on blank secret.
+    EXPECT_NO_THROW(parser_->build(config_set_));
+    EXPECT_THROW(parser_->commit(), D2CfgError);
+}
+
+/// @brief Verifies that TSIGKeyInfo parsing creates a proper TSIGKeyInfo
+/// when given a valid combination of entries.
+TEST_F(TSIGKeyInfoTest, validEntryTests) {
+    // Valid entries for TSIG key, all items are required.
+    std::string config = "{"
+                         " \"name\": \"d2_key_one\" , "
+                         " \"algorithm\": \"md5\" , "
+                         " \"secret\": \"0123456789\" "
+                         "}";
+    ASSERT_TRUE(fromJSON(config));
+
+    // Verify that it builds and commits without throwing.
+    ASSERT_NO_THROW(parser_->build(config_set_));
+    ASSERT_NO_THROW(parser_->commit());
+
+    // Verify the correct number of keys are present
+    int count =  keys_->size();
+    EXPECT_EQ(1, count);
+
+    // Find the key and retrieve it.
+    TSIGKeyInfoMap::iterator gotit = keys_->find("d2_key_one");
+    ASSERT_TRUE(gotit != keys_->end());
+    TSIGKeyInfoPtr& key = gotit->second;
+
+    // Verify the key contents.
+    EXPECT_TRUE(checkKey(key, "d2_key_one", "md5", "0123456789"));
+}
+
+/// @brief Verifies that attempting to parse an invalid list of TSIGKeyInfo
+/// entries is detected.
+TEST_F(TSIGKeyInfoTest, invalidTSIGKeyList) {
+    // Construct a list of keys with an invalid key entry.
+    std::string config = "["
+
+                         " { \"name\": \"key1\" , "
+                         "   \"algorithm\": \"algo1\" ,"
+                         "   \"secret\": \"secret11\" "
+                         " },"
+                         " { \"name\": \"key2\" , "
+                         "   \"algorithm\": \"\" ,"
+                         "   \"secret\": \"secret12\" "
+                         " },"
+                         " { \"name\": \"key3\" , "
+                         "   \"algorithm\": \"algo3\" ,"
+                         "   \"secret\": \"secret13\" "
+                         " }"
+                         " ]";
+
+    ASSERT_TRUE(fromJSON(config));
+
+    // Create the list parser.
+    isc::dhcp::ParserPtr parser;
+    ASSERT_NO_THROW(parser.reset(new TSIGKeyInfoListParser("test", keys_)));
+
+    // Verify that the list builds without errors.
+    ASSERT_NO_THROW(parser->build(config_set_));
+
+    // Verify that the list commit fails.
+    EXPECT_THROW(parser->commit(), D2CfgError);
+}
+
+/// @brief Verifies that attempting to parse an invalid list of TSIGKeyInfo
+/// entries is detected.
+TEST_F(TSIGKeyInfoTest, duplicateTSIGKey) {
+    // Construct a list of keys with an invalid key entry.
+    std::string config = "["
+
+                         " { \"name\": \"key1\" , "
+                         "   \"algorithm\": \"algo1\" ,"
+                         "   \"secret\": \"secret11\" "
+                         " },"
+                         " { \"name\": \"key2\" , "
+                         "   \"algorithm\": \"algo2\" ,"
+                         "   \"secret\": \"secret12\" "
+                         " },"
+                         " { \"name\": \"key1\" , "
+                         "   \"algorithm\": \"algo3\" ,"
+                         "   \"secret\": \"secret13\" "
+                         " }"
+                         " ]";
+
+    ASSERT_TRUE(fromJSON(config));
+
+    // Create the list parser.
+    isc::dhcp::ParserPtr parser;
+    ASSERT_NO_THROW(parser.reset(new TSIGKeyInfoListParser("test", keys_)));
+
+    // Verify that the list builds without errors.
+    ASSERT_NO_THROW(parser->build(config_set_));
+
+    // Verify that the list commit fails.
+    EXPECT_THROW(parser->commit(), D2CfgError);
+}
+
+/// @brief Verifies a valid list of TSIG Keys parses correctly.
+TEST_F(TSIGKeyInfoTest, validTSIGKeyList) {
+    // Construct a valid list of keys.
+    std::string config = "["
+
+                         " { \"name\": \"key1\" , "
+                         "   \"algorithm\": \"algo1\" ,"
+                         "   \"secret\": \"secret1\" "
+                         " },"
+                         " { \"name\": \"key2\" , "
+                         "   \"algorithm\": \"algo2\" ,"
+                         "   \"secret\": \"secret2\" "
+                         " },"
+                         " { \"name\": \"key3\" , "
+                         "   \"algorithm\": \"algo3\" ,"
+                         "   \"secret\": \"secret3\" "
+                         " }"
+                         " ]";
+
+    ASSERT_TRUE(fromJSON(config));
+
+    // Verify that the list builds and commits without errors.
+    // Create the list parser.
+    isc::dhcp::ParserPtr parser;
+    ASSERT_NO_THROW(parser.reset(new TSIGKeyInfoListParser("test", keys_)));
+    ASSERT_NO_THROW(parser->build(config_set_));
+    ASSERT_NO_THROW(parser->commit());
+
+    // Verify the correct number of keys are present
+    int count =  keys_->size();
+    ASSERT_EQ(count, 3);
+
+    // Find the 1st key and retrieve it.
+    TSIGKeyInfoMap::iterator gotit = keys_->find("key1");
+    ASSERT_TRUE(gotit != keys_->end());
+    TSIGKeyInfoPtr& key = gotit->second;
+
+    // Verify the key contents.
+    EXPECT_TRUE(checkKey(key, "key1", "algo1", "secret1"));
+
+    // Find the 2nd key and retrieve it.
+    gotit = keys_->find("key2");
+    ASSERT_TRUE(gotit != keys_->end());
+    key = gotit->second;
+
+    // Verify the key contents.
+    EXPECT_TRUE(checkKey(key, "key2", "algo2", "secret2"));
+
+    // Find the 3rd key and retrieve it.
+    gotit = keys_->find("key3");
+    ASSERT_TRUE(gotit != keys_->end());
+    key = gotit->second;
+
+    // Verify the key contents.
+    EXPECT_TRUE(checkKey(key, "key3", "algo3", "secret3"));
+}
+
+/// @brief Tests the enforcement of data validation when parsing DnsServerInfos.
+/// It verifies that:
+/// 1. Specifying both a hostname and an ip address is not allowed.
+/// 2. Specifying both blank a hostname and blank ip address is not allowed.
+/// 3. Specifying a negative port number is not allowed.
+TEST_F(DnsServerInfoTest, invalidEntryTests) {
+    // Create a config in which both host and ip address are supplied.
+    // Verify that it builds without throwing but commit fails.
+    std::string config = "{ \"hostname\": \"pegasus.tmark\", "
+                         "  \"ip_address\": \"127.0.0.1\" } ";
+    ASSERT_TRUE(fromJSON(config));
+    EXPECT_NO_THROW(parser_->build(config_set_));
+    EXPECT_THROW(parser_->commit(), D2CfgError);
+
+    // Neither host nor ip address supplied
+    // Verify that it builds without throwing but commit fails.
+    config = "{ \"hostname\": \"\", "
+             "  \"ip_address\": \"\" } ";
+    ASSERT_TRUE(fromJSON(config));
+    EXPECT_NO_THROW(parser_->build(config_set_));
+    EXPECT_THROW(parser_->commit(), D2CfgError);
+
+    // Create a config with a negative port number.
+    // Verify that build fails.
+    config = "{ \"ip_address\": \"192.168.5.6\" ,"
+             "  \"port\": -100 }";
+    ASSERT_TRUE(fromJSON(config));
+    EXPECT_THROW (parser_->build(config_set_), isc::BadValue);
+}
+
+
+/// @brief Verifies that DnsServerInfo parsing creates a proper DnsServerInfo
+/// when given a valid combination of entries.
+/// It verifies that:
+/// 1. A DnsServerInfo entry is correctly made, when given only a hostname.
+/// 2. A DnsServerInfo entry is correctly made, when given ip address and port.
+/// 3. A DnsServerInfo entry is correctly made, when given only an ip address.
+TEST_F(DnsServerInfoTest, validEntryTests) {
+    // Valid entries for dynamic host
+    std::string config = "{ \"hostname\": \"pegasus.tmark\" }";
+    ASSERT_TRUE(fromJSON(config));
+
+    // Verify that it builds and commits without throwing.
+    ASSERT_NO_THROW(parser_->build(config_set_));
+    ASSERT_NO_THROW(parser_->commit());
+
+    // Verify the correct number of servers are present
+    int count =  servers_->size();
+    EXPECT_EQ(1, count);
+
+    // Verify the server exists and has the correct values.
+    DnsServerInfoPtr server = (*servers_)[0];
+    EXPECT_TRUE(checkServer(server, "pegasus.tmark",
+                            DnsServerInfo::EMPTY_IP_STR,
+                            DnsServerInfo::STANDARD_DNS_PORT));
+
+    // Start over for a new test.
+    reset();
+
+    // Valid entries for static ip
+    config = " { \"ip_address\": \"127.0.0.1\" , "
+             "  \"port\": 100 }";
+    ASSERT_TRUE(fromJSON(config));
+
+    // Verify that it builds and commits without throwing.
+    ASSERT_NO_THROW(parser_->build(config_set_));
+    ASSERT_NO_THROW(parser_->commit());
+
+    // Verify the correct number of servers are present
+    count =  servers_->size();
+    EXPECT_EQ(1, count);
+
+    // Verify the server exists and has the correct values.
+    server = (*servers_)[0];
+    EXPECT_TRUE(checkServer(server, "", "127.0.0.1", 100));
+
+    // Start over for a new test.
+    reset();
+
+    // Valid entries for static ip, no port
+    config = " { \"ip_address\": \"192.168.2.5\" }";
+    ASSERT_TRUE(fromJSON(config));
+
+    // Verify that it builds and commits without throwing.
+    ASSERT_NO_THROW(parser_->build(config_set_));
+    ASSERT_NO_THROW(parser_->commit());
+
+    // Verify the correct number of servers are present
+    count =  servers_->size();
+    EXPECT_EQ(1, count);
+
+    // Verify the server exists and has the correct values.
+    server = (*servers_)[0];
+    EXPECT_TRUE(checkServer(server, "", "192.168.2.5",
+                            DnsServerInfo::STANDARD_DNS_PORT));
+}
+
+/// @brief Verifies that attempting to parse an invalid list of DnsServerInfo
+/// entries is detected.
+TEST_F(ConfigParseTest, invalidServerList) {
+    // Construct a list of servers with an invalid server entry.
+    std::string config = "[ { \"hostname\": \"one.tmark\" }, "
+                        "{ \"hostname\": \"\" }, "
+                        "{ \"hostname\": \"three.tmark\" } ]";
+    ASSERT_TRUE(fromJSON(config));
+
+    // Create the server storage and list parser.
+    DnsServerInfoStoragePtr servers(new DnsServerInfoStorage());
+    isc::dhcp::ParserPtr parser;
+    ASSERT_NO_THROW(parser.reset(new DnsServerInfoListParser("test", servers)));
+
+    // Verify that the list builds without errors.
+    ASSERT_NO_THROW(parser->build(config_set_));
+
+    // Verify that the list commit fails.
+    EXPECT_THROW(parser->commit(), D2CfgError);
+}
+
+/// @brief Verifies that a list of DnsServerInfo entries parses correctly given
+/// a valid configuration.
+TEST_F(ConfigParseTest, validServerList) {
+    // Create a valid list of servers.
+    std::string config = "[ { \"hostname\": \"one.tmark\" }, "
+                        "{ \"hostname\": \"two.tmark\" }, "
+                        "{ \"hostname\": \"three.tmark\" } ]";
+    ASSERT_TRUE(fromJSON(config));
+
+    // Create the server storage and list parser.
+    DnsServerInfoStoragePtr servers(new DnsServerInfoStorage());
+    isc::dhcp::ParserPtr parser;
+    ASSERT_NO_THROW(parser.reset(new DnsServerInfoListParser("test", servers)));
+
+    // Verfiy that the list builds and commits without error.
+    ASSERT_NO_THROW(parser->build(config_set_));
+    ASSERT_NO_THROW(parser->commit());
+
+    // Verify that the server storage contains the correct number of servers.
+    int count =  servers->size();
+    EXPECT_EQ(3, count);
+
+    // Verify the first server exists and has the correct values.
+    DnsServerInfoPtr server = (*servers)[0];
+    EXPECT_TRUE(checkServer(server, "one.tmark", DnsServerInfo::EMPTY_IP_STR,
+                            DnsServerInfo::STANDARD_DNS_PORT));
+
+    // Verify the second server exists and has the correct values.
+    server = (*servers)[1];
+    EXPECT_TRUE(checkServer(server, "two.tmark", DnsServerInfo::EMPTY_IP_STR,
+                            DnsServerInfo::STANDARD_DNS_PORT));
+
+    // Verify the third server exists and has the correct values.
+    server = (*servers)[2];
+    EXPECT_TRUE(checkServer(server, "three.tmark", DnsServerInfo::EMPTY_IP_STR,
+                            DnsServerInfo::STANDARD_DNS_PORT));
+}
+
+/// @brief Tests the enforcement of data validation when parsing DdnsDomains.
+/// It verifies that:
+/// 1. Domain storage cannot be null when constructing a DdnsDomainParser.
+/// 2. The name entry is not optional.
+/// 3. The server list man not be empty.
+/// 4. That a mal-formed server entry is detected.
+/// 5. That an undefined key name is detected.
+TEST_F(DdnsDomainTest, invalidDdnsDomainEntry) {
+    // Verify that attempting to construct the parser with null storage fails.
+    DdnsDomainMapPtr domains;
+    ASSERT_THROW(isc::dhcp::ParserPtr(
+                 new DdnsDomainParser("test", domains, keys_)), D2CfgError);
+
+    // Create a domain configuration without a name
+    std::string config = "{  \"key_name\": \"d2_key.tmark.org\" , "
+                         "  \"dns_servers\" : [ "
+                         "  {  \"ip_address\": \"127.0.0.1\" , "
+                         "    \"port\": 100 },"
+                         "  { \"ip_address\": \"127.0.0.2\" , "
+                         "    \"port\": 200 },"
+                         "  {  \"ip_address\": \"127.0.0.3\" , "
+                         "    \"port\": 300 } ] } ";
+    ASSERT_TRUE(fromJSON(config));
+
+    // Verify that the domain configuration builds but commit fails.
+    ASSERT_NO_THROW(parser_->build(config_set_));
+    ASSERT_THROW(parser_->commit(), isc::dhcp::DhcpConfigError);
+
+    // Create a domain configuration with an empty server list.
+    config = "{ \"name\": \"tmark.org\" , "
+             "  \"key_name\": \"d2_key.tmark.org\" , "
+             "  \"dns_servers\" : [ "
+             "   ] } ";
+    ASSERT_TRUE(fromJSON(config));
+
+    // Verify that the domain configuration build fails.
+    ASSERT_THROW(parser_->build(config_set_), D2CfgError);
+
+    // Create a domain configuration with a mal-formed server entry.
+    config = "{ \"name\": \"tmark.org\" , "
+             "  \"key_name\": \"d2_key.tmark.org\" , "
+             "  \"dns_servers\" : [ "
+             "  {  \"ip_address\": \"127.0.0.3\" , "
+             "    \"port\": -1 } ] } ";
+    ASSERT_TRUE(fromJSON(config));
+
+    // Verify that the domain configuration build fails.
+    ASSERT_THROW(parser_->build(config_set_), isc::BadValue);
+
+    // Create a domain configuration without an defined key name
+    config = "{ \"name\": \"tmark.org\" , "
+             "  \"key_name\": \"d2_key.tmark.org\" , "
+             "  \"dns_servers\" : [ "
+             "  {  \"ip_address\": \"127.0.0.3\" , "
+             "    \"port\": 300 } ] } ";
+    ASSERT_TRUE(fromJSON(config));
+
+    // Verify that the domain configuration build succeeds but commit fails.
+    ASSERT_NO_THROW(parser_->build(config_set_));
+    ASSERT_THROW(parser_->commit(), D2CfgError);
+}
+
+/// @brief Verifies the basics of parsing DdnsDomains.
+/// It verifies that:
+/// 1. Valid construction of DdnsDomainParser functions.
+/// 2. Given a valid, configuration entry, DdnsDomainParser parses
+/// correctly.
+/// (It indirectly verifies the operation of DdnsDomainMap).
+TEST_F(DdnsDomainTest, ddnsDomainParsing) {
+    // Create a valid domain configuration entry containing three valid
+    // servers.
+    std::string config =
+                        "{ \"name\": \"tmark.org\" , "
+                        "  \"key_name\": \"d2_key.tmark.org\" , "
+                        "  \"dns_servers\" : [ "
+                        "  {  \"ip_address\": \"127.0.0.1\" , "
+                        "    \"port\": 100 },"
+                        "  { \"ip_address\": \"127.0.0.2\" , "
+                        "    \"port\": 200 },"
+                        "  {  \"ip_address\": \"127.0.0.3\" , "
+                        "    \"port\": 300 } ] } ";
+    ASSERT_TRUE(fromJSON(config));
+
+    // Add a TSIG key to the test key map, so key validation will pass.
+    addKey("d2_key.tmark.org", "md5", "0123456789");
+
+    // Verify that the domain configuration builds and commits without error.
+    ASSERT_NO_THROW(parser_->build(config_set_));
+    ASSERT_NO_THROW(parser_->commit());
+
+    // Verify that the domain storage contains the correct number of domains.
+    int count =  domains_->size();
+    EXPECT_EQ(1, count);
+
+    // Verify that the expected domain exists and can be retrieved from
+    // the storage.
+    DdnsDomainMap::iterator gotit = domains_->find("tmark.org");
+    ASSERT_TRUE(gotit != domains_->end());
+    DdnsDomainPtr& domain = gotit->second;
+
+    // Verify the name and key_name values.
+    EXPECT_EQ("tmark.org", domain->getName());
+    EXPECT_EQ("d2_key.tmark.org", domain->getKeyName());
+
+    // Verify that the server list exists and contains the correct number of
+    // servers.
+    const DnsServerInfoStoragePtr& servers = domain->getServers();
+    EXPECT_TRUE(servers);
+    count =  servers->size();
+    EXPECT_EQ(3, count);
+
+    // Fetch each server and verify its contents.
+    DnsServerInfoPtr server = (*servers)[0];
+    EXPECT_TRUE(server);
+
+    EXPECT_TRUE(checkServer(server, "", "127.0.0.1", 100));
+
+    server = (*servers)[1];
+    EXPECT_TRUE(server);
+
+    EXPECT_TRUE(checkServer(server, "", "127.0.0.2", 200));
+
+    server = (*servers)[2];
+    EXPECT_TRUE(server);
+
+    EXPECT_TRUE(checkServer(server, "", "127.0.0.3", 300));
+}
+
+/// @brief Tests the fundamentals of parsing DdnsDomain lists.
+/// This test verifies that given a valid domain list configuration
+/// it will accurately parse and populate each domain in the list.
+TEST_F(DdnsDomainTest, DdnsDomainListParsing) {
+    // Create a valid domain list configuration, with two domains
+    // that have three servers each.
+    std::string config =
+                        "[ "
+                        "{ \"name\": \"tmark.org\" , "
+                        "  \"key_name\": \"d2_key.tmark.org\" , "
+                        "  \"dns_servers\" : [ "
+                        "  { \"ip_address\": \"127.0.0.1\" , "
+                        "    \"port\": 100 },"
+                        "  { \"ip_address\": \"127.0.0.2\" , "
+                        "    \"port\": 200 },"
+                        "  { \"ip_address\": \"127.0.0.3\" , "
+                        "    \"port\": 300 } ] } "
+                        ", "
+                        "{ \"name\": \"billcat.net\" , "
+                        "  \"key_name\": \"d2_key.billcat.net\" , "
+                        "  \"dns_servers\" : [ "
+                        "  { \"ip_address\": \"127.0.0.4\" , "
+                        "    \"port\": 400 },"
+                        "  { \"ip_address\": \"127.0.0.5\" , "
+                        "    \"port\": 500 },"
+                        "  { \"ip_address\": \"127.0.0.6\" , "
+                        "    \"port\": 600 } ] } "
+                        "] ";
+
+    ASSERT_TRUE(fromJSON(config));
+
+    // Add keys to key map so key validation passes.
+    addKey("d2_key.tmark.org", "algo1", "secret1");
+    addKey("d2_key.billcat.net", "algo2", "secret2");
+
+    // Create the list parser
+    isc::dhcp::ParserPtr list_parser;
+    ASSERT_NO_THROW(list_parser.reset(
+                    new DdnsDomainListParser("test", domains_, keys_)));
+
+    // Verify that the domain configuration builds and commits without error.
+    ASSERT_NO_THROW(list_parser->build(config_set_));
+    ASSERT_NO_THROW(list_parser->commit());
+
+    // Verify that the domain storage contains the correct number of domains.
+    int count =  domains_->size();
+    EXPECT_EQ(2, count);
+
+    // Verify that the first domain exists and can be retrieved.
+    DdnsDomainMap::iterator gotit = domains_->find("tmark.org");
+    ASSERT_TRUE(gotit != domains_->end());
+    DdnsDomainPtr& domain = gotit->second;
+
+    // Verify the name and key_name values of the first domain.
+    EXPECT_EQ("tmark.org", domain->getName());
+    EXPECT_EQ("d2_key.tmark.org", domain->getKeyName());
+
+    // Verify the each of the first domain's servers
+    DnsServerInfoStoragePtr servers = domain->getServers();
+    EXPECT_TRUE(servers);
+    count =  servers->size();
+    EXPECT_EQ(3, count);
+
+    DnsServerInfoPtr server = (*servers)[0];
+    EXPECT_TRUE(server);
+    EXPECT_TRUE(checkServer(server, "", "127.0.0.1", 100));
+
+    server = (*servers)[1];
+    EXPECT_TRUE(server);
+    EXPECT_TRUE(checkServer(server, "", "127.0.0.2", 200));
+
+    server = (*servers)[2];
+    EXPECT_TRUE(server);
+    EXPECT_TRUE(checkServer(server, "", "127.0.0.3", 300));
+
+    // Verify second domain
+    gotit = domains_->find("billcat.net");
+    ASSERT_TRUE(gotit != domains_->end());
+    domain = gotit->second;
+
+    // Verify the name and key_name values of the second domain.
+    EXPECT_EQ("billcat.net", domain->getName());
+    EXPECT_EQ("d2_key.billcat.net", domain->getKeyName());
+
+    // Verify the each of second domain's servers
+    servers = domain->getServers();
+    EXPECT_TRUE(servers);
+    count =  servers->size();
+    EXPECT_EQ(3, count);
+
+    server = (*servers)[0];
+    EXPECT_TRUE(server);
+    EXPECT_TRUE(checkServer(server, "", "127.0.0.4", 400));
+
+    server = (*servers)[1];
+    EXPECT_TRUE(server);
+    EXPECT_TRUE(checkServer(server, "", "127.0.0.5", 500));
+
+    server = (*servers)[2];
+    EXPECT_TRUE(server);
+    EXPECT_TRUE(checkServer(server, "", "127.0.0.6", 600));
+}
+
+/// @brief Tests that a domain list configuration cannot contain duplicates.
+TEST_F(DdnsDomainTest, duplicateDomainTest) {
+    // Create a domain list configuration that contains two domains with
+    // the same name.
+    std::string config =
+                        "[ "
+                        "{ \"name\": \"tmark.org\" , "
+                        "  \"dns_servers\" : [ "
+                        "  { \"ip_address\": \"127.0.0.3\" , "
+                        "    \"port\": 300 } ] } "
+                        ", "
+                        "{ \"name\": \"tmark.org\" , "
+                        "  \"dns_servers\" : [ "
+                        "  { \"ip_address\": \"127.0.0.3\" , "
+                        "    \"port\": 300 } ] } "
+                        "] ";
+    ASSERT_TRUE(fromJSON(config));
+
+    // Create the list parser
+    isc::dhcp::ParserPtr list_parser;
+    ASSERT_NO_THROW(list_parser.reset(
+                    new DdnsDomainListParser("test", domains_, keys_)));
+
+    // Verify that the parse build succeeds but the commit fails.
+    ASSERT_NO_THROW(list_parser->build(config_set_));
+    ASSERT_THROW(list_parser->commit(), D2CfgError);
+}
+
+/// @brief Tests construction of D2CfgMgr
+/// This test verifies that a D2CfgMgr constructs properly.
+TEST(D2CfgMgr, construction) {
+    D2CfgMgr *cfg_mgr = NULL;
+
+    // Verify that configuration manager constructions without error.
+    ASSERT_NO_THROW(cfg_mgr = new D2CfgMgr());
+
+    // Verify that the context can be retrieved and is not null.
+    D2CfgContextPtr context;
+    ASSERT_NO_THROW(context = cfg_mgr->getD2CfgContext());
+    EXPECT_TRUE(context);
+
+    // Verify that the forward manager can be retrieved and is not null.
+    EXPECT_TRUE(context->getForwardMgr());
+
+    // Verify that the reverse manager can be retrieved and is not null.
+    EXPECT_TRUE(context->getReverseMgr());
+
+    // Verify that the manager can be destructed without error.
+    EXPECT_NO_THROW(delete cfg_mgr);
+}
+
+/// @brief Tests the parsing of a complete, valid DHCP-DDNS configuration.
+/// This tests passes the configuration into an instance of D2CfgMgr just
+/// as it would be done by d2_process in response to a configuration update
+/// event.
+TEST_F(D2CfgMgrTest, fullConfigTest) {
+    // Create a configuration with all of application level parameters, plus
+    // both the forward and reverse ddns managers.  Both managers have two
+    // domains with three servers per domain.
+    std::string config = "{ "
+                        "\"interface\" : \"eth1\" , "
+                        "\"ip_address\" : \"192.168.1.33\" , "
+                        "\"port\" : 88 , "
+                        "\"tsig_keys\": ["
+                        "{"
+                        "  \"name\": \"d2_key.tmark.org\" , "
+                        "  \"algorithm\": \"md5\" , "
+                        "  \"secret\": \"ssh-dont-tell\"  "
+                        "},"
+                        "{"
+                        "  \"name\": \"d2_key.billcat.net\" , "
+                        "  \"algorithm\": \"md5\" , "
+                        "  \"secret\": \"ollie-ollie-in-free\"  "
+                        "}"
+                        "],"
+                        "\"forward_ddns\" : {"
+                        "\"ddns_domains\": [ "
+                        "{ \"name\": \"tmark.org\" , "
+                        "  \"key_name\": \"d2_key.tmark.org\" , "
+                        "  \"dns_servers\" : [ "
+                        "  { \"hostname\": \"one.tmark\" } , "
+                        "  { \"hostname\": \"two.tmark\" } , "
+                        "  { \"hostname\": \"three.tmark\"} "
+                        "  ] } "
+                        ", "
+                        "{ \"name\": \"billcat.net\" , "
+                        "  \"key_name\": \"d2_key.billcat.net\" , "
+                        "  \"dns_servers\" : [ "
+                        "  { \"hostname\": \"four.billcat\" } , "
+                        "  { \"hostname\": \"five.billcat\" } , "
+                        "  { \"hostname\": \"six.billcat\" } "
+                        "  ] } "
+                        "] },"
+                        "\"reverse_ddns\" : {"
+                        "\"ddns_domains\": [ "
+                        "{ \"name\": \" 0.168.192.in.addr.arpa.\" , "
+                        "  \"key_name\": \"d2_key.tmark.org\" , "
+                        "  \"dns_servers\" : [ "
+                        "  { \"hostname\": \"one.rev\" } , "
+                        "  { \"hostname\": \"two.rev\" } , "
+                        "  { \"hostname\": \"three.rev\" } "
+                        "  ] } "
+                        ", "
+                        "{ \"name\": \" 0.247.106.in.addr.arpa.\" , "
+                        "  \"key_name\": \"d2_key.billcat.net\" , "
+                        "  \"dns_servers\" : [ "
+                        "  { \"hostname\": \"four.rev\" }, "
+                        "  { \"hostname\": \"five.rev\" } , "
+                        "  { \"hostname\": \"six.rev\" } "
+                        "  ] } "
+                        "] } }";
+    ASSERT_TRUE(fromJSON(config));
+
+    // Verify that we can parse the configuration.
+    answer_ = cfg_mgr_->parseConfig(config_set_);
+    ASSERT_TRUE(checkAnswer(0));
+
+    // Verify that the D2 context can be retrieved and is not null.
+    D2CfgContextPtr context;
+    ASSERT_NO_THROW(context = cfg_mgr_->getD2CfgContext());
+
+    // Verify that the application level scalars have the proper values.
+    std::string interface;
+    EXPECT_NO_THROW (context->getParam("interface", interface));
+    EXPECT_EQ("eth1", interface);
+
+    std::string ip_address;
+    EXPECT_NO_THROW (context->getParam("ip_address", ip_address));
+    EXPECT_EQ("192.168.1.33", ip_address);
+
+    uint32_t port = 0;
+    EXPECT_NO_THROW (context->getParam("port", port));
+    EXPECT_EQ(88, port);
+
+    // Verify that the forward manager can be retrieved.
+    DdnsDomainListMgrPtr mgr = context->getForwardMgr();
+    ASSERT_TRUE(mgr);
+
+    // Verify that the forward manager has the correct number of domains.
+    DdnsDomainMapPtr domains = mgr->getDomains();
+    ASSERT_TRUE(domains);
+    int count =  domains->size();
+    EXPECT_EQ(2, count);
+
+    // Verify that the server count in each of the forward manager domains.
+    // NOTE that since prior tests have validated server parsing, we are are
+    // assuming that the servers did in fact parse correctly if the correct
+    // number of them are there.
+    DdnsDomainMapPair domain_pair;
+    BOOST_FOREACH(domain_pair, (*domains)) {
+        DdnsDomainPtr domain = domain_pair.second;
+        DnsServerInfoStoragePtr servers = domain->getServers();
+        count = servers->size();
+        EXPECT_TRUE(servers);
+        EXPECT_EQ(3, count);
+    }
+
+    // Verify that the reverse manager can be retrieved.
+    mgr = context->getReverseMgr();
+    ASSERT_TRUE(mgr);
+
+    // Verify that the reverse manager has the correct number of domains.
+    domains = mgr->getDomains();
+    count =  domains->size();
+    EXPECT_EQ(2, count);
+
+    // Verify that the server count in each of the reverse manager domains.
+    // NOTE that since prior tests have validated server parsing, we are are
+    // assuming that the servers did in fact parse correctly if the correct
+    // number of them are there.
+    BOOST_FOREACH(domain_pair, (*domains)) {
+        DdnsDomainPtr domain = domain_pair.second;
+        DnsServerInfoStoragePtr servers = domain->getServers();
+        count = servers->size();
+        EXPECT_TRUE(servers);
+        EXPECT_EQ(3, count);
+    }
+}
+
+/// @brief Tests the basics of the D2CfgMgr FQDN-domain matching
+/// This test uses a valid configuration to exercise the D2CfgMgr
+/// forward FQDN-to-domain matching.
+/// It verifies that:
+/// 1. Given an FQDN which exactly matches a domain's name, that domain is
+/// returned as match.
+/// 2. Given a FQDN for sub-domain in the list, returns the proper match.
+/// 3. Given a FQDN that matches no domain name, returns the wild card domain
+/// as a match.
+TEST_F(D2CfgMgrTest, forwardMatchTest) {
+    // Create  configuration with one domain, one sub domain, and the wild
+    // card.
+    std::string config = "{ "
+                        "\"interface\" : \"eth1\" , "
+                        "\"ip_address\" : \"192.168.1.33\" , "
+                        "\"port\" : 88 , "
+                        "\"tsig_keys\": [] ,"
+                        "\"forward_ddns\" : {"
+                        "\"ddns_domains\": [ "
+                        "{ \"name\": \"tmark.org\" , "
+                        "  \"dns_servers\" : [ "
+                        "  { \"ip_address\": \"127.0.0.1\" } "
+                        "  ] } "
+                        ", "
+                        "{ \"name\": \"one.tmark.org\" , "
+                        "  \"dns_servers\" : [ "
+                        "  { \"ip_address\": \"127.0.0.2\" } "
+                        "  ] } "
+                        ", "
+                        "{ \"name\": \"*\" , "
+                        "  \"dns_servers\" : [ "
+                        "  { \"hostname\": \"global.net\" } "
+                        "  ] } "
+                        "] }, "
+                        "\"reverse_ddns\" : {} "
+                        "}";
+
+
+    ASSERT_TRUE(fromJSON(config));
+    // Verify that we can parse the configuration.
+    answer_ = cfg_mgr_->parseConfig(config_set_);
+    ASSERT_TRUE(checkAnswer(0));
+
+    // Verify that the D2 context can be retrieved and is not null.
+    D2CfgContextPtr context;
+    ASSERT_NO_THROW(context = cfg_mgr_->getD2CfgContext());
+
+    DdnsDomainPtr match;
+    // Verify that an exact match works.
+    EXPECT_TRUE(cfg_mgr_->matchForward("tmark.org", match));
+    EXPECT_EQ("tmark.org", match->getName());
+
+    // Verify that an exact match works.
+    EXPECT_TRUE(cfg_mgr_->matchForward("one.tmark.org", match));
+    EXPECT_EQ("one.tmark.org", match->getName());
+
+    // Verify that a FQDN for sub-domain matches.
+    EXPECT_TRUE(cfg_mgr_->matchForward("blue.tmark.org", match));
+    EXPECT_EQ("tmark.org", match->getName());
+
+    // Verify that a FQDN for sub-domain matches.
+    EXPECT_TRUE(cfg_mgr_->matchForward("red.one.tmark.org", match));
+    EXPECT_EQ("one.tmark.org", match->getName());
+
+    // Verify that an FQDN with no match, returns the wild card domain.
+    EXPECT_TRUE(cfg_mgr_->matchForward("shouldbe.wildcard", match));
+    EXPECT_EQ("*", match->getName());
+
+    // Verify that an attempt to match an empty FQDN throws.
+    ASSERT_THROW(cfg_mgr_->matchForward("", match), D2CfgError);
+}
+
+/// @brief Tests domain matching when there is no wild card domain.
+/// This test verifies that matches are found only for FQDNs that match
+/// some or all of a domain name.  FQDNs without matches should not return
+/// a match.
+TEST_F(D2CfgMgrTest, matchNoWildcard) {
+    // Create a configuration with one domain, one sub-domain, and NO wild card.
+    std::string config = "{ "
+                        "\"interface\" : \"eth1\" , "
+                        "\"ip_address\" : \"192.168.1.33\" , "
+                        "\"port\" : 88 , "
+                        "\"tsig_keys\": [] ,"
+                        "\"forward_ddns\" : {"
+                        "\"ddns_domains\": [ "
+                        "{ \"name\": \"tmark.org\" , "
+                        "  \"dns_servers\" : [ "
+                        "  { \"ip_address\": \"127.0.0.1\" } "
+                        "  ] } "
+                        ", "
+                        "{ \"name\": \"one.tmark.org\" , "
+                        "  \"dns_servers\" : [ "
+                        "  { \"ip_address\": \"127.0.0.2\" } "
+                        "  ] } "
+                        "] }, "
+                        "\"reverse_ddns\" : {} "
+                        " }";
+
+    ASSERT_TRUE(fromJSON(config));
+
+    // Verify that we can parse the configuration.
+    answer_ = cfg_mgr_->parseConfig(config_set_);
+    ASSERT_TRUE(checkAnswer(0));
+
+    // Verify that the D2 context can be retrieved and is not null.
+    D2CfgContextPtr context;
+    ASSERT_NO_THROW(context = cfg_mgr_->getD2CfgContext());
+
+    DdnsDomainPtr match;
+    // Verify that full or partial matches, still match.
+    EXPECT_TRUE(cfg_mgr_->matchForward("tmark.org", match));
+    EXPECT_EQ("tmark.org", match->getName());
+
+    EXPECT_TRUE(cfg_mgr_->matchForward("blue.tmark.org", match));
+    EXPECT_EQ("tmark.org", match->getName());
+
+    EXPECT_TRUE(cfg_mgr_->matchForward("red.one.tmark.org", match));
+    EXPECT_EQ("one.tmark.org", match->getName());
+
+    // Verify that a FQDN with no match, fails to match.
+    EXPECT_FALSE(cfg_mgr_->matchForward("shouldbe.wildcard", match));
+}
+
+/// @brief Tests domain matching when there is ONLY a wild card domain.
+/// This test verifies that any FQDN matches the wild card.
+TEST_F(D2CfgMgrTest, matchAll) {
+    std::string config = "{ "
+                        "\"interface\" : \"eth1\" , "
+                        "\"ip_address\" : \"192.168.1.33\" , "
+                        "\"port\" : 88 , "
+                        "\"tsig_keys\": [] ,"
+                        "\"forward_ddns\" : {"
+                        "\"ddns_domains\": [ "
+                        "{ \"name\": \"*\" , "
+                        "  \"dns_servers\" : [ "
+                        "  { \"ip_address\": \"127.0.0.1\" } "
+                        "  ] } "
+                        "] }, "
+                        "\"reverse_ddns\" : {} "
+                        "}";
+
+    ASSERT_TRUE(fromJSON(config));
+
+    // Verify that we can parse the configuration.
+    answer_ = cfg_mgr_->parseConfig(config_set_);
+    ASSERT_TRUE(checkAnswer(0));
+
+    // Verify that the D2 context can be retrieved and is not null.
+    D2CfgContextPtr context;
+    ASSERT_NO_THROW(context = cfg_mgr_->getD2CfgContext());
+
+    // Verify that wild card domain is returned for any FQDN.
+    DdnsDomainPtr match;
+    EXPECT_TRUE(cfg_mgr_->matchForward("tmark.org", match));
+    EXPECT_EQ("*", match->getName());
+    EXPECT_TRUE(cfg_mgr_->matchForward("shouldbe.wildcard", match));
+    EXPECT_EQ("*", match->getName());
+
+    // Verify that an attempt to match an empty FQDN still throws.
+    ASSERT_THROW(cfg_mgr_->matchReverse("", match), D2CfgError);
+
+}
+
+/// @brief Tests the basics of the D2CfgMgr reverse FQDN-domain matching
+/// This test uses a valid configuration to exercise the D2CfgMgr's
+/// reverse FQDN-to-domain matching.
+/// It verifies that:
+/// 1. Given an FQDN which exactly matches a domain's name, that domain is
+/// returned as match.
+/// 2. Given a FQDN for sub-domain in the list, returns the proper match.
+/// 3. Given a FQDN that matches no domain name, returns the wild card domain
+/// as a match.
+TEST_F(D2CfgMgrTest, matchReverse) {
+    std::string config = "{ "
+                        "\"interface\" : \"eth1\" , "
+                        "\"ip_address\" : \"192.168.1.33\" , "
+                        "\"port\" : 88 , "
+                        "\"tsig_keys\": [] ,"
+                        "\"forward_ddns\" : {}, "
+                        "\"reverse_ddns\" : {"
+                        "\"ddns_domains\": [ "
+                        "{ \"name\": \"100.168.192.in-addr.arpa\" , "
+                        "  \"dns_servers\" : [ "
+                        "  { \"ip_address\": \"127.0.0.1\" } "
+                        "  ] }, "
+                        "{ \"name\": \"168.192.in-addr.arpa\" , "
+                        "  \"dns_servers\" : [ "
+                        "  { \"ip_address\": \"127.0.0.1\" } "
+                        "  ] }, "
+                        "{ \"name\": \"*\" , "
+                        "  \"dns_servers\" : [ "
+                        "  { \"ip_address\": \"127.0.0.1\" } "
+                        "  ] } "
+                        "] } }";
+
+    ASSERT_TRUE(fromJSON(config));
+
+    // Verify that we can parse the configuration.
+    answer_ = cfg_mgr_->parseConfig(config_set_);
+    ASSERT_TRUE(checkAnswer(0));
+
+    // Verify that the D2 context can be retrieved and is not null.
+    D2CfgContextPtr context;
+    ASSERT_NO_THROW(context = cfg_mgr_->getD2CfgContext());
+
+    DdnsDomainPtr match;
+    // Verify an exact match.
+    EXPECT_TRUE(cfg_mgr_->matchReverse("100.168.192.in-addr.arpa", match));
+    EXPECT_EQ("100.168.192.in-addr.arpa", match->getName());
+
+    // Verify a sub-domain match.
+    EXPECT_TRUE(cfg_mgr_->matchReverse("27.100.168.192.in-addr.arpa", match));
+    EXPECT_EQ("100.168.192.in-addr.arpa", match->getName());
+
+    // Verify a sub-domain match.
+    EXPECT_TRUE(cfg_mgr_->matchReverse("30.133.168.192.in-addr.arpa", match));
+    EXPECT_EQ("168.192.in-addr.arpa", match->getName());
+
+    // Verify a wild card match.
+    EXPECT_TRUE(cfg_mgr_->matchReverse("shouldbe.wildcard", match));
+    EXPECT_EQ("*", match->getName());
+
+    // Verify that an attempt to match an empty FQDN throws.
+    ASSERT_THROW(cfg_mgr_->matchReverse("", match), D2CfgError);
+}
+
+} // end of anonymous namespace
diff --git a/src/bin/d2/tests/d2_controller_unittests.cc b/src/bin/d2/tests/d2_controller_unittests.cc
index a2b3374..f4b4d41 100644
--- a/src/bin/d2/tests/d2_controller_unittests.cc
+++ b/src/bin/d2/tests/d2_controller_unittests.cc
@@ -54,7 +54,7 @@ public:
 };
 
 /// @brief Basic Controller instantiation testing.
-/// Verfies that the controller singleton gets created and that the
+/// Verifies that the controller singleton gets created and that the
 /// basic derivation from the base class is intact.
 TEST_F(D2ControllerTest, basicInstanceTesting) {
     // Verify the we can the singleton instance can be fetched and that
@@ -80,12 +80,12 @@ TEST_F(D2ControllerTest, basicInstanceTesting) {
 }
 
 /// @brief Tests basic command line processing.
-/// Verfies that:
+/// Verifies that:
 /// 1. Standard command line options are supported.
 /// 2. Invalid options are detected.
 TEST_F(D2ControllerTest, commandLineArgs) {
-    char* argv[] = { const_cast<char*>("progName"), 
-                     const_cast<char*>("-s"), 
+    char* argv[] = { const_cast<char*>("progName"),
+                     const_cast<char*>("-s"),
                      const_cast<char*>("-v") };
     int argc = 3;
 
@@ -101,7 +101,7 @@ TEST_F(D2ControllerTest, commandLineArgs) {
     EXPECT_TRUE(checkVerbose(true));
 
     // Verify that an unknown option is detected.
-    char* argv2[] = { const_cast<char*>("progName"), 
+    char* argv2[] = { const_cast<char*>("progName"),
                       const_cast<char*>("-x") };
     argc = 2;
     EXPECT_THROW(parseArgs(argc, argv2), InvalidUsage);
@@ -119,8 +119,8 @@ TEST_F(D2ControllerTest, initProcessTesting) {
 /// launches with a valid, stand-alone command line and no simulated errors.
 TEST_F(D2ControllerTest, launchNormalShutdown) {
     // command line to run standalone
-    char* argv[] = { const_cast<char*>("progName"), 
-                     const_cast<char*>("-s"), 
+    char* argv[] = { const_cast<char*>("progName"),
+                     const_cast<char*>("-s"),
                      const_cast<char*>("-v") };
     int argc = 3;
 
@@ -165,10 +165,9 @@ TEST_F(D2ControllerTest, configUpdateTests) {
     ASSERT_NO_THROW(initProcess());
     EXPECT_TRUE(checkProcess());
 
-    // Create a configuration set. Content is arbitrary, just needs to be
-    // valid JSON.
-    std::string config = "{ \"test-value\": 1000 } ";
-    isc::data::ElementPtr config_set = isc::data::Element::fromJSON(config);
+    // Create a configuration set using a small, valid D2 configuration.
+    isc::data::ElementPtr config_set =
+                                isc::data::Element::fromJSON(valid_d2_config);
 
     // We are not stand-alone, so configuration should be rejected as there is
     // no session.  This is a pretty contrived situation that shouldn't be
@@ -182,6 +181,13 @@ TEST_F(D2ControllerTest, configUpdateTests) {
     answer = DControllerBase::configHandler(config_set);
     isc::config::parseAnswer(rcode, answer);
     EXPECT_EQ(0, rcode);
+
+    // Use an invalid configuration to verify parsing error return.
+    std::string config = "{ \"bogus\": 1000 } ";
+    config_set = isc::data::Element::fromJSON(config);
+    answer = DControllerBase::configHandler(config_set);
+    isc::config::parseAnswer(rcode, answer);
+    EXPECT_EQ(1, rcode);
 }
 
 /// @brief Command execution tests.
diff --git a/src/bin/d2/tests/d2_process_unittests.cc b/src/bin/d2/tests/d2_process_unittests.cc
index 40c85f3..c507f2c 100644
--- a/src/bin/d2/tests/d2_process_unittests.cc
+++ b/src/bin/d2/tests/d2_process_unittests.cc
@@ -15,6 +15,7 @@
 
 #include <config/ccsession.h>
 #include <d2/d2_process.h>
+#include <d_test_stubs.h>
 
 #include <boost/date_time/posix_time/posix_time.hpp>
 #include <gtest/gtest.h>
@@ -83,17 +84,23 @@ TEST(D2Process, construction) {
 }
 
 /// @brief Verifies basic configure method behavior.
-/// @TODO This test is simplistic and will need to be augmented as configuration
+///  This test is simplistic and will need to be augmented as configuration
 /// ability is implemented.
 TEST_F(D2ProcessTest, configure) {
-    // Verify that given a configuration "set", configure returns
-    // a successful response.
     int rcode = -1;
-    string config = "{ \"test-value\": 1000 } ";
-    isc::data::ElementPtr json = isc::data::Element::fromJSON(config);
+
+    // Use a small, valid D2 configuration to verify successful parsing.
+    isc::data::ElementPtr json = isc::data::Element::fromJSON(valid_d2_config);
     isc::data::ConstElementPtr answer = process_->configure(json);
     isc::config::parseAnswer(rcode, answer);
     EXPECT_EQ(0, rcode);
+
+    // Use an invalid configuration to verify parsing error return.
+    string config = "{ \"bogus\": 1000 } ";
+    json = isc::data::Element::fromJSON(config);
+    answer = process_->configure(json);
+    isc::config::parseAnswer(rcode, answer);
+    EXPECT_EQ(1, rcode);
 }
 
 /// @brief Verifies basic command method behavior.
diff --git a/src/bin/d2/tests/d2_update_message_unittests.cc b/src/bin/d2/tests/d2_update_message_unittests.cc
new file mode 100644
index 0000000..28e01c7
--- /dev/null
+++ b/src/bin/d2/tests/d2_update_message_unittests.cc
@@ -0,0 +1,591 @@
+// Copyright (C) 2013 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 <d2/d2_update_message.h>
+#include <d2/d2_zone.h>
+#include <dns/messagerenderer.h>
+#include <dns/rdataclass.h>
+#include <dns/rdata.h>
+#include <dns/rrttl.h>
+
+#include <boost/scoped_ptr.hpp>
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::d2;
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+using namespace isc::util;
+
+namespace {
+
+// @brief Test fixture class for testing D2UpdateMessage object.
+class D2UpdateMessageTest : public ::testing::Test {
+public:
+    // @brief Constructor.
+    //
+    // Does nothing.
+    D2UpdateMessageTest() { }
+
+    // @brief Destructor.
+    //
+    // Does nothing.
+    ~D2UpdateMessageTest() { };
+
+    // @brief Return string representation of the name encoded in wire format.
+    //
+    // This function reads the number of bytes specified in the second
+    // argument from the buffer. It doesn't check if buffer has sufficient
+    // length for reading given number of bytes. Caller should verify it
+    // prior to calling this function.
+    //
+    // @param buf input buffer, its internal pointer will be moved to
+    //        the position after a name being read from it.
+    // @param name_length length of the name stored in the buffer
+    // @param no_zero_byte if true it indicates that the given buffer does not
+    //        comprise the zero byte, which signals end of the name. This is
+    //        the case, when dealing with compressed messages which don't have
+    //        this byte.
+    //
+    // @return string representation of the name.
+    std::string readNameFromWire(InputBuffer& buf, size_t name_length,
+                                 const bool no_zero_byte = false) {
+        std::vector<uint8_t> name_data;
+        // Create another InputBuffer which holds only the name in the wire
+        // format.
+        buf.readVector(name_data, name_length);
+        if (no_zero_byte) {
+            ++name_length;
+            name_data.push_back(0);
+        }
+        InputBuffer name_buf(&name_data[0], name_length);
+        // Parse the name and return its textual representation.
+        Name name(name_buf);
+        return (name.toText());
+    }
+};
+
+// This test verifies that DNS Update message ID can be set using
+// setId function.
+TEST_F(D2UpdateMessageTest, setId) {
+    // Message ID is initialized to 0.
+    D2UpdateMessage msg;
+    EXPECT_EQ(0, msg.getId());
+    // Override the default value and verify that it has been set.
+    msg.setId(0x1234);
+    EXPECT_EQ(0x1234, msg.getId());
+}
+
+// This test verifies that the DNS Update message RCODE can be set
+// using setRcode function.
+TEST_F(D2UpdateMessageTest, setRcode) {
+    D2UpdateMessage msg;
+    // Rcode must be explicitly set before it is accessed.
+    msg.setRcode(Rcode::NOERROR());
+    EXPECT_EQ(Rcode::NOERROR().getCode(), msg.getRcode().getCode());
+    // Let's override current value to make sure that getter does
+    // not return fixed value.
+    msg.setRcode(Rcode::NOTIMP());
+    EXPECT_EQ(Rcode::NOTIMP().getCode(), msg.getRcode().getCode());
+}
+
+// This test verifies that the Zone section in the DNS Update message
+// can be set.
+TEST_F(D2UpdateMessageTest, setZone) {
+    D2UpdateMessage msg;
+    // The zone pointer is initialized to NULL.
+    D2ZonePtr zone = msg.getZone();
+    EXPECT_FALSE(zone);
+    // Let's create a new Zone and check that it is returned
+    // via getter.
+    msg.setZone(Name("example.com"), RRClass::ANY());
+    zone = msg.getZone();
+    EXPECT_TRUE(zone);
+    EXPECT_EQ("example.com.", zone->getName().toText());
+    EXPECT_EQ(RRClass::ANY().getCode(), zone->getClass().getCode());
+
+    // Now, let's check that the existing Zone object can be
+    // overriden with a new one.
+    msg.setZone(Name("foo.example.com"), RRClass::NONE());
+    zone = msg.getZone();
+    EXPECT_TRUE(zone);
+    EXPECT_EQ("foo.example.com.", zone->getName().toText());
+    EXPECT_EQ(RRClass::NONE().getCode(), zone->getClass().getCode());
+}
+
+// This test verifies that the DNS message is properly decoded from the
+// wire format.
+TEST_F(D2UpdateMessageTest, fromWire) {
+    // The following table holds the DNS response in on-wire format.
+    // This message comprises the following sections:
+    // - HEADER
+    // - PREREQUISITE section with one RR
+    // - UPDATE section with 1 RR.
+    // Such a response may be generated by the DNS server as a result
+    // of copying the contents of the REQUEST message sent by DDNS client.
+    const uint8_t bin_msg[] = {
+        // HEADER section starts here (see RFC 2136, section 2).
+        0x05, 0xAF, // ID=0x05AF
+        0xA8, 0x6,  // QR=1, Opcode=6, RCODE=YXDOMAIN
+        0x0, 0x1,   // ZOCOUNT=1
+        0x0, 0x2,   // PRCOUNT=2
+        0x0, 0x1,   // UPCOUNT=1
+        0x0, 0x0,   // ADCOUNT=0
+
+        // Zone section starts here. The The first field comprises
+        // the Zone name encoded as a set of labels, each preceded
+        // by a length of the following label. The whole Zone name is
+        // terminated with a NULL char.
+        // For Zone section format see (RFC 2136, section 2.3).
+        0x7, 0x65, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65, // example (7 is length)
+        0x3, 0x63, 0x6F, 0x6D, //.com. (0x3 is a length)
+        0x0,      // NULL character terminates the Zone name.
+        0x0, 0x6, // ZTYPE='SOA'
+        0x0, 0x1, // ZCLASS='IN'
+
+        // Prerequisite section starts here. This section comprises two
+        // prerequisites:
+        // - 'Name is not in use'
+        // - 'Name is in use'
+        // See RFC 2136, section 2.4 for the format of Prerequisite section.
+        // Each prerequisite RR starts with its name. It is expressed in the
+        // compressed format as described in RFC 1035, section 4.1.4. The first
+        // label is expressed as in case of non-compressed name. It is preceded
+        // by the length value. The following two bytes are the pointer to the
+        // offset in the message where 'example.com' was used. That is, in the
+        // Zone name at offset 12. Pointer starts with two bits set - they
+        // mark start of the pointer.
+
+        // First prerequisite. NONE class indicates that the update requires
+        // that the name 'foo.example.com' is not use/
+        0x03, 0x66, 0x6F, 0x6F, // foo.
+        0xC0, 0x0C,             // pointer to example.com.
+        0x0, 0x1C,              // TYPE=AAAA
+        0x0, 0xFE,              // CLASS=NONE
+        0x0, 0x0, 0x0, 0x0,     // TTL=0
+        0x0, 0x0,               // RDLENGTH=0
+
+        // Second prerequisite. ANY class indicates tha the update requires
+        // that the name 'bar.example.com' exists.
+        0x03, 0x62, 0x61, 0x72, // bar.
+        0xC0, 0x0C,             // pointer to example.com.
+        0x0, 0x1C,              // TYPE=AAAA
+        0x0, 0xFF,              // CLASS=ANY
+        0x0, 0x0, 0x0, 0x0,     // TTL=0
+        0x0, 0x0,               // RDLENGTH=0
+
+        // Update section starts here. The format of this section conforms to
+        // RFC 2136, section 2.5. The name of the RR is again expressed in
+        // compressed format. The two pointer bytes point to the offset in the
+        // message where 'foo.example.com' was used already - 29.
+        0xC0, 0x1D,             // pointer to foo.example.com.
+        0x0, 0x1C,              // TYPE=AAAA
+        0x0, 0x1,               // CLASS=IN
+        0xAA, 0xBB, 0xCC, 0xDD, // TTL=0xAABBCCDD
+        0x0, 0x10,              // RDLENGTH=16
+        // The following 16 bytes of RDATA hold IPv6 address: 2001:db8:1::1.
+        0x20, 0x01, 0x0D, 0xB8, 0x00, 0x01, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
+    };
+    InputBuffer buf(bin_msg, sizeof(bin_msg));
+
+    // Create an object to be used to decode the message from the wire format.
+    D2UpdateMessage msg(D2UpdateMessage::INBOUND);
+    // Decode the message.
+    ASSERT_NO_THROW(msg.fromWire(buf));
+
+    // Check that the message header is valid.
+    EXPECT_EQ(0x05AF, msg.getId());
+    EXPECT_EQ(D2UpdateMessage::RESPONSE, msg.getQRFlag());
+    EXPECT_EQ(Rcode::YXDOMAIN_CODE, msg.getRcode().getCode());
+
+    // The ZOCOUNT must contain exactly one zone. If it does, we should get
+    // the name, class and type of the zone and verify they are valid.
+    ASSERT_EQ(1, msg.getRRCount(D2UpdateMessage::SECTION_ZONE));
+    D2ZonePtr zone = msg.getZone();
+    ASSERT_TRUE(zone);
+    EXPECT_EQ("example.com.", zone->getName().toText());
+    EXPECT_EQ(RRClass::IN().getCode(), zone->getClass().getCode());
+
+    // Check the Prerequisite section. It should contain two records.
+    ASSERT_EQ(2, msg.getRRCount(D2UpdateMessage::SECTION_PREREQUISITE));
+
+    // Proceed to the first prerequisite.
+    RRsetIterator rrset_it =
+        msg.beginSection(D2UpdateMessage::SECTION_PREREQUISITE);
+    RRsetPtr prereq1 = *rrset_it;
+    ASSERT_TRUE(prereq1);
+    // Check record fields.
+    EXPECT_EQ("foo.example.com.", prereq1->getName().toText()); // NAME
+    EXPECT_EQ(RRType::AAAA().getCode(), prereq1->getType().getCode()); // TYPE
+    EXPECT_EQ(RRClass::NONE().getCode(),
+              prereq1->getClass().getCode()); // CLASS
+    EXPECT_EQ(0, prereq1->getTTL().getValue()); // TTL
+    EXPECT_EQ(0, prereq1->getRdataCount()); // RDLENGTH
+
+    // Move to next prerequisite section.
+    ++rrset_it;
+    RRsetPtr prereq2 = *rrset_it;
+    ASSERT_TRUE(prereq2);
+    // Check record fields.
+    EXPECT_EQ("bar.example.com.", prereq2->getName().toText()); // NAME
+    EXPECT_EQ(RRType::AAAA().getCode(), prereq2->getType().getCode()); // TYPE
+    EXPECT_EQ(RRClass::ANY().getCode(), prereq2->getClass().getCode()); // CLASS
+    EXPECT_EQ(0, prereq2->getTTL().getValue()); // TTL
+    EXPECT_EQ(0, prereq2->getRdataCount()); // RDLENGTH
+
+    // Check the Update section. There is only one record, so beginSection()
+    // should return the pointer to this sole record.
+    ASSERT_EQ(1, msg.getRRCount(D2UpdateMessage::SECTION_UPDATE));
+    rrset_it = msg.beginSection(D2UpdateMessage::SECTION_UPDATE);
+    RRsetPtr update = *rrset_it;
+    ASSERT_TRUE(update);
+    // Check the record fields.
+    EXPECT_EQ("foo.example.com.", update->getName().toText()); // NAME
+    EXPECT_EQ(RRType::AAAA().getCode(), update->getType().getCode()); // TYPE
+    EXPECT_EQ(RRClass::IN().getCode(), update->getClass().getCode()); // CLASS
+    EXPECT_EQ(0xAABBCCDD, update->getTTL().getValue()); // TTL
+    // There should be exactly one record holding the IPv6 address.
+    // This record can be accessed using RdataIterator. This record
+    // can be compared with the reference record, holding expected IPv6
+    // address using compare function.
+    ASSERT_EQ(1, update->getRdataCount());
+    RdataIteratorPtr rdata_it = update->getRdataIterator();
+    ASSERT_TRUE(rdata_it);
+    in::AAAA rdata_ref("2001:db8:1::1");
+    EXPECT_EQ(0, rdata_ref.compare(rdata_it->getCurrent()));
+
+    // @todo: at this point we don't test Additional Data records. We may
+    // consider implementing tests for it in the future.
+}
+
+// This test verifies that the fromWire function throws appropriate exception
+// if the message being parsed comprises invalid Opcode (is not a DNS Update).
+TEST_F(D2UpdateMessageTest, fromWireInvalidOpcode) {
+    // This is a binary representation of the DNS message.
+    // It comprises invalid Opcode=3, expected value is 6
+    // (Update).
+    const uint8_t bin_msg[] = {
+        0x05, 0xAF, // ID=0x05AF
+        0x98, 0x6,  // QR=1, Opcode=3, RCODE=YXDOMAIN
+        0x0, 0x0,   // ZOCOUNT=0
+        0x0, 0x0,   // PRCOUNT=0
+        0x0, 0x0,   // UPCOUNT=0
+        0x0, 0x0    // ADCOUNT=0
+    };
+    InputBuffer buf(bin_msg, sizeof(bin_msg));
+    // The 'true' argument passed to the constructor turns the
+    // message into the parse mode in which the fromWire function
+    // can be used to decode the binary mesasage data.
+    D2UpdateMessage msg(D2UpdateMessage::INBOUND);
+    // When using invalid Opcode, the fromWire function should
+    // throw NotUpdateMessage exception.
+    EXPECT_THROW(msg.fromWire(buf), isc::d2::NotUpdateMessage);
+}
+
+// This test verifies that the fromWire function throws appropriate exception
+// if the message being parsed comprises invalid QR flag. The QR bit is
+// expected to be set to indicate that the message is a RESPONSE.
+TEST_F(D2UpdateMessageTest, fromWireInvalidQRFlag) {
+    // This is a binary representation of the DNS message.
+    // It comprises invalid QR flag = 0.
+    const uint8_t bin_msg[] = {
+        0x05, 0xAF, // ID=0x05AF
+        0x28, 0x6,  // QR=0, Opcode=6, RCODE=YXDOMAIN
+        0x0, 0x0,   // ZOCOUNT=0
+        0x0, 0x0,   // PRCOUNT=0
+        0x0, 0x0,   // UPCOUNT=0
+        0x0, 0x0    // ADCOUNT=0
+    };
+    InputBuffer buf(bin_msg, sizeof(bin_msg));
+    // The 'true' argument passed to the constructor turns the
+    // message into the parse mode in which the fromWire function
+    // can be used to decode the binary mesasage data.
+    D2UpdateMessage msg(D2UpdateMessage::INBOUND);
+    // When using invalid QR flag, the fromWire function should
+    // throw InvalidQRFlag exception.
+    EXPECT_THROW(msg.fromWire(buf), isc::d2::InvalidQRFlag);
+}
+
+// This test verifies that the fromWire function throws appropriate exception
+// if the message being parsed comprises more than one (two in this case)
+// Zone records.
+TEST_F(D2UpdateMessageTest, fromWireTooManyZones) {
+    // This is a binary representation of the DNS message. This message
+    // comprises two Zone records.
+    const uint8_t bin_msg[] = {
+        0x05, 0xAF, // ID=0x05AF
+        0xA8, 0x6,  // QR=1, Opcode=6, RCODE=YXDOMAIN
+        0x0, 0x2,   // ZOCOUNT=2
+        0x0, 0x0,   // PRCOUNT=0
+        0x0, 0x0,   // UPCOUNT=0
+        0x0, 0x0,   // ADCOUNT=0
+
+        // Start first Zone record.
+        0x7, 0x65, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65, // example (7 is length)
+        0x3, 0x63, 0x6F, 0x6D, //.com. (0x3 is a length)
+        0x0,      // NULL character terminates the Zone name.
+        0x0, 0x6, // ZTYPE='SOA'
+        0x0, 0x1, // ZCLASS='IN'
+
+        // Start second Zone record. Presence of this record should result
+        // in error when parsing this message.
+        0x3, 0x63, 0x6F, 0x6D, // com. (0x3 is a length)
+        0x0,      // NULL character terminates the Zone name.
+        0x0, 0x6, // ZTYPE='SOA'
+        0x0, 0x1  // ZCLASS='IN'
+    };
+    InputBuffer buf(bin_msg, sizeof(bin_msg));
+
+    // The 'true' argument passed to the constructor turns the
+    // message into the parse mode in which the fromWire function
+    // can be used to decode the binary mesasage data.
+    D2UpdateMessage msg(D2UpdateMessage::INBOUND);
+    // When parsing a message with more than one Zone record,
+    // exception should be thrown.
+    EXPECT_THROW(msg.fromWire(buf), isc::d2::InvalidZoneSection);
+}
+
+// This test verifies that the wire format of the message is produced
+// in the render mode.
+TEST_F(D2UpdateMessageTest, toWire) {
+    D2UpdateMessage msg;
+    // Set message ID.
+    msg.setId(0x1234);
+    // Rcode to NOERROR.
+    msg.setRcode(Rcode(Rcode::NOERROR_CODE));
+
+    // Set Zone section. This section must comprise exactly
+    // one Zone. toWire function would fail if Zone is not set.
+    msg.setZone(Name("example.com"), RRClass::IN());
+
+    // Set prerequisities.
+
+    // 'Name Is Not In Use' prerequisite (RFC 2136, section 2.4.5)
+    RRsetPtr prereq1(new RRset(Name("foo.example.com"), RRClass::NONE(),
+                               RRType::ANY(), RRTTL(0)));
+    msg.addRRset(D2UpdateMessage::SECTION_PREREQUISITE, prereq1);
+
+    // 'Name is In Use' prerequisite (RFC 2136, section 2.4.4)
+    RRsetPtr prereq2(new RRset(Name("bar.example.com"), RRClass::ANY(),
+                               RRType::ANY(), RRTTL(0)));
+    msg.addRRset(D2UpdateMessage::SECTION_PREREQUISITE, prereq2);
+
+    // Set Update Section.
+
+    // Create RR holding a name being added. This RR is constructed
+    // in conformance to RFC 2136, section 2.5.1.
+    RRsetPtr updaterr1(new RRset(Name("foo.example.com"), RRClass::IN(),
+                                 RRType::A(), RRTTL(10)));
+    // RR record is of the type A, thus RDATA holds 4 octet Internet
+    // address. This address is 10.10.1.1.
+    char rdata1[] = {
+        0xA, 0xA , 0x1, 0x1
+    };
+    InputBuffer buf_rdata1(rdata1, 4);
+    updaterr1->addRdata(createRdata(RRType::A(), RRClass::IN(), buf_rdata1,
+                                    buf_rdata1.getLength()));
+    // Add the RR to the message.
+    msg.addRRset(D2UpdateMessage::SECTION_UPDATE, updaterr1);
+
+    // Render message into the wire format.
+    MessageRenderer renderer;
+    ASSERT_NO_THROW(msg.toWire(renderer));
+
+    // Make sure that created packet is not truncated.
+    ASSERT_EQ(77, renderer.getLength());
+
+    // Create input buffer from the rendered data. InputBuffer
+    // is handy to validate the byte contents of the rendered
+    // message.
+    InputBuffer buf(renderer.getData(), renderer.getLength());
+
+    // Start validating the message header.
+
+    // Verify message ID.
+    EXPECT_EQ(0x1234, buf.readUint16());
+    // The 2-bytes following message ID comprise the following fields:
+    // - QR - 1 bit indicating that it is REQUEST. Should be 0.
+    // - Opcode - 4 bits which should hold value of 5 indicating this is
+    //            an Update message. Binary form is "0101".
+    // - Z - These bits are unused for Update Message and should be 0.
+    // - RCODE - Response code, set to NOERROR for REQUEST. It is 0.
+    //8706391835
+    // The binary value is:
+    //   0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
+    // +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+    // | QR|     Opcode    |           Z               |     RCODE     |
+    // +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+    // | 0 | 0   1   0   1 | 0   0   0   0   0   0   0 | 0   0   0   0 |
+    // +---+---+---+-------+---+---+---+---+---+---+---+---+---+---+---+
+    // and the hexadecimal representation is 0x2800.
+    EXPECT_EQ(0x2800, buf.readUint16());
+
+    // ZOCOUNT - holds the number of zones for the update. For Request
+    // message it must be exactly one record (RFC2136, section 2.3).
+    EXPECT_EQ(1, buf.readUint16());
+
+    // PRCOUNT - holds the number of prerequisites. Earlier we have added
+    // two prerequisites. Thus, expect that this conter is 2.
+    EXPECT_EQ(2, buf.readUint16());
+
+    // UPCOUNT - holds the number of RRs in the Update Section. We have
+    // added 1 RR, which adds the name foo.example.com to the Zone.
+    EXPECT_EQ(1, buf.readUint16());
+
+    // ADCOUNT - holds the number of RRs in the Additional Data Section.
+    EXPECT_EQ(0, buf.readUint16());
+
+    // Start validating the Zone section. This section comprises the
+    // following data:
+    // - ZNAME
+    // - ZTYPE
+    // - ZCLASS
+
+    // ZNAME holds 'example.com.' encoded as set of labels. Each label
+    // is preceded by its length. The name is ended with the byte holding
+    // zero value. This yields the total size of the name in wire format
+    // of 13 bytes.
+
+    // The simplest way to convert the name from wire format to a string
+    // is to use dns::Name class. It should be ok to rely on the Name class
+    // to decode the name, because it is unit tested elswhere.
+    std::string zone_name = readNameFromWire(buf, 13);
+    EXPECT_EQ("example.com.", zone_name);
+
+    // ZTYPE of the Zone section must be SOA according to RFC 2136,
+    // section 2.3.
+    EXPECT_EQ(RRType::SOA().getCode(), buf.readUint16());
+
+    // ZCLASS of the Zone section is IN.
+    EXPECT_EQ(RRClass::IN().getCode(), buf.readUint16());
+
+    // Start checks on Prerequisite section. Each prerequisite comprises
+    // the following fields:
+    // - NAME - name of the RR in wire format
+    // - TYPE - two octets with one of the RR TYPE codes
+    // - CLASS - two octets with one of the RR CLASS codes
+    // - TTL - a 32-bit signed integer specifying Time-To-Live
+    // - RDLENGTH - length of the RDATA field
+    // - RDATA - a variable length string of octets containing
+    //           resource data.
+    // In case of this message, we expect to have two prerequisite RRs.
+    // Their structure is checked below.
+
+    // First prerequisite should comprise the 'Name is not in use prerequisite'
+    // for 'foo.example.com'.
+
+    // Check the name first. Message renderer is using compression for domain
+    // names as described in RFC 1035, section 4.1.4. The name in this RR is
+    // foo.example.com. The name of the zone is example.com and it has occured
+    // in this message already at offset 12 (the size of the header is 12).
+    // Therefore, name of this RR is encoded as 'foo', followed by a pointer
+    // to offset in this message where the remainder of this name was used.
+    // This pointer has the following format:
+    // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // | 1  1|                 OFFSET                  |
+    // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // | 1  1| 0  0  0  0  0  0  0  0  0  0  1  1  0  0|
+    // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // which has a following hexadecimal representation: 0xC00C
+
+    // Let's read the non-compressed part first - 'foo.'
+    std::string name_prereq1 = readNameFromWire(buf, 4, true);
+    EXPECT_EQ("foo.", name_prereq1);
+    // The remaining two bytes hold the pointer to 'example.com'.
+    EXPECT_EQ(0xC00C, buf.readUint16());
+    // TYPE is ANY
+    EXPECT_EQ(RRType::ANY().getCode(), buf.readUint16());
+    // CLASS is NONE
+    EXPECT_EQ(RRClass::NONE().getCode(), buf.readUint16());
+    // TTL is a 32-but value, expecting 0
+    EXPECT_EQ(0, buf.readUint32());
+    // There is no RDATA, so RDLENGTH is 0
+    EXPECT_EQ(0, buf.readUint16());
+
+    // Start checking second prerequisite.
+
+    std::string name_prereq2 = readNameFromWire(buf, 4, true);
+    EXPECT_EQ("bar.", name_prereq2);
+    // The remaining two bytes hold the pointer to 'example.com'.
+    EXPECT_EQ(0xC00C, buf.readUint16());
+    // TYPE is ANY
+    EXPECT_EQ(RRType::ANY().getCode(), buf.readUint16());
+    // CLASS is ANY
+    EXPECT_EQ(RRClass::ANY().getCode(), buf.readUint16());
+    // TTL is a 32-but value, expecting 0
+    EXPECT_EQ(0, buf.readUint32());
+    // There is no RDATA, so RDLENGTH is 0
+    EXPECT_EQ(0, buf.readUint16());
+
+    // Start checking Update section. This section contains RRset with
+    // one A RR.
+
+    // The name of the RR is 'foo.example.com'. It is encoded in the
+    // compressed format - as a pointer to the name of prerequisite 1.
+    // This name is in offset 0x1D in this message.
+    EXPECT_EQ(0xC01D, buf.readUint16());
+    // TYPE is A
+    EXPECT_EQ(RRType::A().getCode(), buf.readUint16());
+    // CLASS is IN (same as zone class)
+    EXPECT_EQ(RRClass::IN().getCode(), buf.readUint16());
+    // TTL is a 32-but value, set here to 10.
+    EXPECT_EQ(10, buf.readUint32());
+    // For A records, the RDATA comprises the 4-byte Internet address.
+    // So, RDLENGTH is 4.
+    EXPECT_EQ(4, buf.readUint16());
+    // We have stored the following address in RDATA field: 10.10.1.1
+    // (which is 0A 0A 01 01) in hexadecimal format.
+    EXPECT_EQ(0x0A0A0101, buf.readUint32());
+
+    // @todo: consider extending this test to verify Additional Data
+    // section.
+}
+
+// This test verifies that an attempt to call toWire function on the
+// received message will result in an exception.
+TEST_F(D2UpdateMessageTest, toWireInvalidQRFlag) {
+    // This is a binary representation of the DNS message.
+    // This message is valid and should be parsed with no
+    // error.
+    const uint8_t bin_msg[] = {
+        0x05, 0xAF, // ID=0x05AF
+        0xA8, 0x6,  // QR=1, Opcode=6, RCODE=YXDOMAIN
+        0x0, 0x0,   // ZOCOUNT=0
+        0x0, 0x0,   // PRCOUNT=0
+        0x0, 0x0,   // UPCOUNT=0
+        0x0, 0x0    // ADCOUNT=0
+    };
+
+    InputBuffer buf(bin_msg, sizeof(bin_msg));
+    // The 'true' argument passed to the constructor turns the
+    // message into the parse mode in which the fromWire function
+    // can be used to decode the binary mesasage data.
+    D2UpdateMessage msg(D2UpdateMessage::INBOUND);
+    ASSERT_NO_THROW(msg.fromWire(buf));
+
+    // The message is parsed. The QR Flag should now indicate that
+    // it is a Response message.
+    ASSERT_EQ(D2UpdateMessage::RESPONSE, msg.getQRFlag());
+
+    // An attempt to call toWire on the Response message should
+    // result in the InvalidQRFlag exception.
+    MessageRenderer renderer;
+    EXPECT_THROW(msg.toWire(renderer), isc::d2::InvalidQRFlag);
+}
+
+} // End of anonymous namespace
diff --git a/src/bin/d2/tests/d2_zone_unittests.cc b/src/bin/d2/tests/d2_zone_unittests.cc
new file mode 100644
index 0000000..853cdbe
--- /dev/null
+++ b/src/bin/d2/tests/d2_zone_unittests.cc
@@ -0,0 +1,75 @@
+// Copyright (C) 2013 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 <d2/d2_zone.h>
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::d2;
+using namespace isc::dns;
+
+namespace {
+
+// This test verifies that Zone object is created and its constructor sets
+// appropriate values for its members.
+TEST(D2ZoneTest, constructor) {
+    // Create first object.
+    D2Zone zone1(Name("example.com"), RRClass::ANY());
+    EXPECT_EQ("example.com.", zone1.getName().toText());
+    EXPECT_EQ(RRClass::ANY().getCode(), zone1.getClass().getCode());
+    // Create another object to make sure that constructor doesn't assign
+    // fixed values, but they change when constructor's parameters change.
+    D2Zone zone2(Name("foo.example.com"), RRClass::IN());
+    EXPECT_EQ("foo.example.com.", zone2.getName().toText());
+    EXPECT_EQ(RRClass::IN().getCode(), zone2.getClass().getCode());
+}
+
+// This test verifies that toText() function returns text representation of
+// of the zone in expected format.
+TEST(D2ZoneTest, toText) {
+    // Create first object.
+    D2Zone zone1(Name("example.com"), RRClass::ANY());
+    EXPECT_EQ("example.com. ANY SOA\n", zone1.toText());
+    // Create another object with different parameters to make sure that the
+    // function's output changes accordingly.
+    D2Zone zone2(Name("foo.example.com"), RRClass::IN());
+    EXPECT_EQ("foo.example.com. IN SOA\n", zone2.toText());
+}
+
+// This test verifies that the equality and inequality operators behave as
+// expected.
+TEST(D2ZoneTest, compare) {
+    const Name a("a"), b("b");
+    const RRClass in(RRClass::IN()), any(RRClass::ANY());
+
+    // Equality check
+    EXPECT_TRUE(D2Zone(a, any) == D2Zone(a, any));
+    EXPECT_FALSE(D2Zone(a, any) != D2Zone(a, any));
+
+    // Inequality check, objects differ by class.
+    EXPECT_FALSE(D2Zone(a, any) == D2Zone(a, in));
+    EXPECT_TRUE(D2Zone(a, any) != D2Zone(a, in));
+
+    // Inequality check, objects differ by name.
+    EXPECT_FALSE(D2Zone(a, any) == D2Zone(b, any));
+    EXPECT_TRUE(D2Zone(a, any) != D2Zone(b, any));
+
+    // Inequality check, objects differ by name and class.
+    EXPECT_FALSE(D2Zone(a, any) == D2Zone(b, in));
+    EXPECT_TRUE(D2Zone(a, any) != D2Zone(b, in));
+}
+
+} // End of anonymous namespace
diff --git a/src/bin/d2/tests/d_cfg_mgr_unittests.cc b/src/bin/d2/tests/d_cfg_mgr_unittests.cc
new file mode 100644
index 0000000..512b896
--- /dev/null
+++ b/src/bin/d2/tests/d_cfg_mgr_unittests.cc
@@ -0,0 +1,386 @@
+// Copyright (C) 2013  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 <config/module_spec.h>
+#include <dhcpsrv/dhcp_parsers.h>
+#include <d2/d_cfg_mgr.h>
+#include <d_test_stubs.h>
+
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <gtest/gtest.h>
+
+#include <config.h>
+#include <sstream>
+
+using namespace std;
+using namespace isc;
+using namespace isc::config;
+using namespace isc::d2;
+using namespace boost::posix_time;
+
+namespace {
+
+/// @brief Test Class for verifying that configuration context cannot be null
+/// during construction.
+class DCtorTestCfgMgr : public DCfgMgrBase {
+public:
+    /// @brief Constructor - Note that is passes in an empty configuration
+    /// pointer to the base class constructor.
+    DCtorTestCfgMgr() : DCfgMgrBase(DCfgContextBasePtr()) {
+    }
+
+    /// @brief Destructor
+    virtual ~DCtorTestCfgMgr() {
+    }
+
+    /// @brief Dummy implementation as this method is abstract.
+    virtual isc::dhcp::ParserPtr
+    createConfigParser(const std::string& /* element_id */) {
+        return (isc::dhcp::ParserPtr());
+    }
+};
+
+/// @brief Test fixture class for testing DCfgMgrBase class.
+/// It maintains an member instance of DStubCfgMgr and derives from
+/// ConfigParseTest fixture, thus providing methods for converting JSON
+/// strings to configuration element sets, checking parse results, and
+/// accessing the configuration context.
+class DStubCfgMgrTest : public ConfigParseTest {
+public:
+
+    /// @brief Constructor
+    DStubCfgMgrTest():cfg_mgr_(new DStubCfgMgr) {
+    }
+
+    /// @brief Destructor
+    ~DStubCfgMgrTest() {
+    }
+
+    /// @brief Convenience method which returns a DStubContextPtr to the
+    /// configuration context.
+    ///
+    /// @return returns a DStubContextPtr.
+    DStubContextPtr getStubContext() {
+        return (boost::dynamic_pointer_cast<DStubContext>
+                (cfg_mgr_->getContext()));
+    }
+
+    /// @brief Configuration manager instance.
+    DStubCfgMgrPtr cfg_mgr_;
+};
+
+///@brief Tests basic construction/destruction of configuration manager.
+/// Verifies that:
+/// 1. Proper construction succeeds.
+/// 2. Configuration context is initialized by construction.
+/// 3. Destruction works properly.
+/// 4. Construction with a null context is not allowed.
+TEST(DCfgMgrBase, construction) {
+    DCfgMgrBasePtr cfg_mgr;
+
+    // Verify that configuration manager constructions without error.
+    ASSERT_NO_THROW(cfg_mgr.reset(new DStubCfgMgr()));
+
+    // Verify that the context can be retrieved and is not null.
+    DCfgContextBasePtr context = cfg_mgr->getContext();
+    EXPECT_TRUE(context);
+
+    // Verify that the manager can be destructed without error.
+    EXPECT_NO_THROW(cfg_mgr.reset());
+
+    // Verify that an attempt to construct a manger with a null context fails.
+    ASSERT_THROW(DCtorTestCfgMgr(), DCfgMgrBaseError);
+}
+
+///@brief Tests fundamental aspects of configuration parsing.
+/// Verifies that:
+/// 1. A correctly formed simple configuration parses without error.
+/// 2. An error building the element is handled.
+/// 3. An error committing the element is handled.
+/// 4. An unknown element error is handled.
+TEST_F(DStubCfgMgrTest, basicParseTest) {
+    // Create a simple configuration.
+    string config = "{ \"test-value\": 1000 } ";
+    ASSERT_TRUE(fromJSON(config));
+
+    // Verify that we can parse a simple configuration.
+    answer_ = cfg_mgr_->parseConfig(config_set_);
+    EXPECT_TRUE(checkAnswer(0));
+
+    // Verify that an error building the element is caught and returns a
+    // failed parse result.
+    SimFailure::set(SimFailure::ftElementBuild);
+    answer_ = cfg_mgr_->parseConfig(config_set_);
+    EXPECT_TRUE(checkAnswer(1));
+
+    // Verify that an error committing the element is caught and returns a
+    // failed parse result.
+    SimFailure::set(SimFailure::ftElementCommit);
+    answer_ = cfg_mgr_->parseConfig(config_set_);
+    EXPECT_TRUE(checkAnswer(1));
+
+    // Verify that an unknown element error is caught and returns a failed
+    // parse result.
+    SimFailure::set(SimFailure::ftElementUnknown);
+    answer_ = cfg_mgr_->parseConfig(config_set_);
+    EXPECT_TRUE(checkAnswer(1));
+}
+
+///@brief Tests ordered and non-ordered element parsing
+/// This test verifies that:
+/// 1. Non-ordered parsing parses elements in the order they are presented
+/// by the configuration set (as-they-come).
+/// 2. A parse order list with too few elements is detected.
+/// 3. Ordered parsing parses the elements in the order specified by the
+/// configuration manager's parse order list.
+/// 4. A parse order list with too many elements is detected.
+TEST_F(DStubCfgMgrTest, parseOrderTest) {
+    // Element ids used for test.
+    std::string charlie("charlie");
+    std::string bravo("bravo");
+    std::string alpha("alpha");
+
+    // Create the test configuration with the elements in "random" order.
+
+    // NOTE that element sets produced by  isc::data::Element::fromJSON(),
+    // are in lexical order by element_id. This means that iterating over
+    // such an element set, will present the elements in lexical order. Should
+    // this change, this test will need to be modified accordingly.
+    string config = "{ \"bravo\": 2,  "
+                     " \"alpha\": 1,  "
+                     " \"charlie\": 3 } ";
+    ASSERT_TRUE(fromJSON(config));
+
+    // Verify that non-ordered parsing, results in an as-they-come parse order.
+    // Create an expected parse order.
+    // (NOTE that iterating over Element sets produced by fromJSON() will
+    // present the elements in lexical order.  Should this change, the expected
+    // order list below would need to be changed accordingly).
+    ElementIdList order_expected;
+    order_expected.push_back(alpha);
+    order_expected.push_back(bravo);
+    order_expected.push_back(charlie);
+
+    // Verify that the manager has an EMPTY parse order list. (Empty list
+    // instructs the manager to parse them as-they-come.)
+    EXPECT_EQ(0, cfg_mgr_->getParseOrder().size());
+
+    // Parse the configuration, verify it parses without error.
+    answer_ = cfg_mgr_->parseConfig(config_set_);
+    EXPECT_TRUE(checkAnswer(0));
+
+    // Verify that the parsed order matches what we expected.
+    EXPECT_TRUE(cfg_mgr_->parsed_order_ ==  order_expected);
+
+    // Clear the manager's parse order "memory".
+    cfg_mgr_->parsed_order_.clear();
+
+    // Create a parse order list that has too few entries.  Verify that
+    // when parsing the test config, it fails.
+    cfg_mgr_->addToParseOrder(charlie);
+    // Verify the parse order list is the size we expect.
+    EXPECT_EQ(1, cfg_mgr_->getParseOrder().size());
+
+    // Verify the configuration fails.
+    answer_ = cfg_mgr_->parseConfig(config_set_);
+    EXPECT_TRUE(checkAnswer(1));
+
+    // Verify that the configuration parses correctly, when the parse order
+    // is correct.  Add the needed entries to the parse order
+    cfg_mgr_->addToParseOrder(bravo);
+    cfg_mgr_->addToParseOrder(alpha);
+
+    // Verify the parse order list is the size we expect.
+    EXPECT_EQ(3, cfg_mgr_->getParseOrder().size());
+
+    // Clear the manager's parse order "memory".
+    cfg_mgr_->parsed_order_.clear();
+
+    // Verify the configuration parses without error.
+    answer_ = cfg_mgr_->parseConfig(config_set_);
+    EXPECT_TRUE(checkAnswer(0));
+
+    // Verify that the parsed order is the order we configured.
+    EXPECT_TRUE(cfg_mgr_->getParseOrder() == cfg_mgr_->parsed_order_);
+
+    // Create a parse order list that has too many entries.  Verify that
+    // when parsing the test config, it fails.
+    cfg_mgr_->addToParseOrder("delta");
+
+    // Verify the parse order list is the size we expect.
+    EXPECT_EQ(4, cfg_mgr_->getParseOrder().size());
+
+    // Verify the configuration fails.
+    answer_ = cfg_mgr_->parseConfig(config_set_);
+    EXPECT_TRUE(checkAnswer(1));
+}
+
+/// @brief Tests that element ids supported by the base class as well as those
+/// added by the derived class function properly.
+/// This test verifies that:
+/// 1. Boolean parameters can be parsed and retrieved.
+/// 2. Uint32 parameters can be parsed and retrieved.
+/// 3. String parameters can be parsed and retrieved.
+/// 4. Derivation-specific parameters can be parsed and retrieved.
+/// 5. Parsing a second configuration, updates the existing context values
+/// correctly.
+TEST_F(DStubCfgMgrTest, simpleTypesTest) {
+    // Fetch a derivation specific pointer to the context.
+    DStubContextPtr context = getStubContext();
+    ASSERT_TRUE(context);
+
+    // Create a configuration with all of the parameters.
+    string config = "{ \"bool_test\": true , "
+                    "  \"uint32_test\": 77 , "
+                    "  \"string_test\": \"hmmm chewy\" , "
+                    "  \"extra_test\": 430 } ";
+    ASSERT_TRUE(fromJSON(config));
+
+    // Verify that the configuration parses without error.
+    answer_ = cfg_mgr_->parseConfig(config_set_);
+    ASSERT_TRUE(checkAnswer(0));
+
+    // Verify that the boolean parameter was parsed correctly by retrieving
+    // its value from the context.
+    bool actual_bool = false;
+    EXPECT_NO_THROW(context->getParam("bool_test", actual_bool));
+    EXPECT_EQ(true, actual_bool);
+
+    // Verify that the uint32 parameter was parsed correctly by retrieving
+    // its value from the context.
+    uint32_t actual_uint32 = 0;
+    EXPECT_NO_THROW(context->getParam("uint32_test", actual_uint32));
+    EXPECT_EQ(77, actual_uint32);
+
+    // Verify that the string parameter was parsed correctly by retrieving
+    // its value from the context.
+    std::string actual_string = "";
+    EXPECT_NO_THROW(context->getParam("string_test", actual_string));
+    EXPECT_EQ("hmmm chewy", actual_string);
+
+    // Verify that the "extra" parameter was parsed correctly by retrieving
+    // its value from the context.
+    uint32_t actual_extra = 0;
+    EXPECT_NO_THROW(context->getExtraParam("extra_test", actual_extra));
+    EXPECT_EQ(430, actual_extra);
+
+    // Create a configuration which "updates" all of the parameter values.
+    string config2 = "{ \"bool_test\": false , "
+                    "  \"uint32_test\": 88 , "
+                    "  \"string_test\": \"ewww yuk!\" , "
+                    "  \"extra_test\": 11 } ";
+    ASSERT_TRUE(fromJSON(config2));
+
+    // Verify that the configuration parses without error.
+    answer_ = cfg_mgr_->parseConfig(config_set_);
+    EXPECT_TRUE(checkAnswer(0));
+
+    // Verify that the boolean parameter was updated correctly by retrieving
+    // its value from the context.
+    actual_bool = true;
+    EXPECT_NO_THROW(context->getParam("bool_test", actual_bool));
+    EXPECT_FALSE(actual_bool);
+
+    // Verify that the uint32 parameter was updated correctly by retrieving
+    // its value from the context.
+    actual_uint32 = 0;
+    EXPECT_NO_THROW(context->getParam("uint32_test", actual_uint32));
+    EXPECT_EQ(88, actual_uint32);
+
+    // Verify that the string parameter was updated correctly by retrieving
+    // its value from the context.
+    actual_string = "";
+    EXPECT_NO_THROW(context->getParam("string_test", actual_string));
+    EXPECT_EQ("ewww yuk!", actual_string);
+
+    // Verify that the "extra" parameter was updated correctly by retrieving
+    // its value from the context.
+    actual_extra = 0;
+    EXPECT_NO_THROW(context->getExtraParam("extra_test", actual_extra));
+    EXPECT_EQ(11, actual_extra);
+}
+
+/// @brief Tests that the configuration context is preserved after failure
+/// during parsing causes a rollback.
+/// 1. Verifies configuration context rollback.
+TEST_F(DStubCfgMgrTest, rollBackTest) {
+    // Fetch a derivation specific pointer to the context.
+    DStubContextPtr context = getStubContext();
+    ASSERT_TRUE(context);
+
+    // Create a configuration with all of the parameters.
+    string config = "{ \"bool_test\": true , "
+                    "  \"uint32_test\": 77 , "
+                    "  \"string_test\": \"hmmm chewy\" , "
+                    "  \"extra_test\": 430 } ";
+    ASSERT_TRUE(fromJSON(config));
+
+    // Verify that the configuration parses without error.
+    answer_ = cfg_mgr_->parseConfig(config_set_);
+    EXPECT_TRUE(checkAnswer(0));
+
+    // Verify that all of parameters have the expected values.
+    bool actual_bool = false;
+    EXPECT_NO_THROW(context->getParam("bool_test", actual_bool));
+    EXPECT_EQ(true, actual_bool);
+
+    uint32_t actual_uint32 = 0;
+    EXPECT_NO_THROW(context->getParam("uint32_test", actual_uint32));
+    EXPECT_EQ(77, actual_uint32);
+
+    std::string actual_string = "";
+    EXPECT_NO_THROW(context->getParam("string_test", actual_string));
+    EXPECT_EQ("hmmm chewy", actual_string);
+
+    uint32_t actual_extra = 0;
+    EXPECT_NO_THROW(context->getExtraParam("extra_test", actual_extra));
+    EXPECT_EQ(430, actual_extra);
+
+    // Create a configuration which "updates" all of the parameter values
+    // plus one unknown at the end.
+    string config2 = "{ \"bool_test\": false , "
+                    "  \"uint32_test\": 88 , "
+                    "  \"string_test\": \"ewww yuk!\" , "
+                    "  \"extra_test\": 11 , "
+                    "  \"zeta_unknown\": 33 } ";
+    ASSERT_TRUE(fromJSON(config2));
+
+    // Force a failure on the last element
+    SimFailure::set(SimFailure::ftElementUnknown);
+    answer_ = cfg_mgr_->parseConfig(config_set_);
+    EXPECT_TRUE(checkAnswer(1));
+
+    // Refresh our local pointer.
+    context = getStubContext();
+
+    // Verify that all of parameters have the original values.
+    actual_bool = false;
+    EXPECT_NO_THROW(context->getParam("bool_test", actual_bool));
+    EXPECT_EQ(true, actual_bool);
+
+    actual_uint32 = 0;
+    EXPECT_NO_THROW(context->getParam("uint32_test", actual_uint32));
+    EXPECT_EQ(77, actual_uint32);
+
+    actual_string = "";
+    EXPECT_NO_THROW(context->getParam("string_test", actual_string));
+    EXPECT_EQ("hmmm chewy", actual_string);
+
+    actual_extra = 0;
+    EXPECT_NO_THROW(context->getExtraParam("extra_test", actual_extra));
+    EXPECT_EQ(430, actual_extra);
+}
+
+} // end of anonymous namespace
diff --git a/src/bin/d2/tests/d_test_stubs.cc b/src/bin/d2/tests/d_test_stubs.cc
index 33a2ff6..4e1cee6 100644
--- a/src/bin/d2/tests/d_test_stubs.cc
+++ b/src/bin/d2/tests/d_test_stubs.cc
@@ -21,6 +21,31 @@ using namespace asio;
 namespace isc {
 namespace d2 {
 
+const char* valid_d2_config = "{ "
+                        "\"interface\" : \"eth1\" , "
+                        "\"ip_address\" : \"192.168.1.33\" , "
+                        "\"port\" : 88 , "
+                        "\"tsig_keys\": ["
+                        "{ \"name\": \"d2_key.tmark.org\" , "
+                        "   \"algorithm\": \"md5\" ,"
+                        "   \"secret\": \"0123456989\" "
+                        "} ],"
+                        "\"forward_ddns\" : {"
+                        "\"ddns_domains\": [ "
+                        "{ \"name\": \"tmark.org\" , "
+                        "  \"key_name\": \"d2_key.tmark.org\" , "
+                        "  \"dns_servers\" : [ "
+                        "  { \"hostname\": \"one.tmark\" } "
+                        "] } ] }, "
+                        "\"reverse_ddns\" : {"
+                        "\"ddns_domains\": [ "
+                        "{ \"name\": \" 0.168.192.in.addr.arpa.\" , "
+                        "  \"key_name\": \"d2_key.tmark.org\" , "
+                        "  \"dns_servers\" : [ "
+                        "  { \"ip_address\": \"127.0.0.101\" , "
+                        "    \"port\": 100 } ] } "
+                        "] } }";
+
 // Initialize the static failure flag.
 SimFailure::FailureType SimFailure::failure_type_ = SimFailure::ftNoFailure;
 
@@ -28,7 +53,7 @@ SimFailure::FailureType SimFailure::failure_type_ = SimFailure::ftNoFailure;
 const char*  DStubProcess::stub_proc_command_("cool_proc_cmd");
 
 DStubProcess::DStubProcess(const char* name, IOServicePtr io_service)
-    : DProcessBase(name, io_service) {
+    : DProcessBase(name, io_service, DCfgMgrBasePtr(new DStubCfgMgr())) {
 };
 
 void
@@ -130,7 +155,7 @@ DStubController::DStubController()
 
     if (getenv("B10_FROM_BUILD")) {
         setSpecFileName(std::string(getenv("B10_FROM_BUILD")) +
-            "/src/bin/d2/d2.spec");
+            "/src/bin/d2/dhcp-ddns.spec");
     } else {
         setSpecFileName(D2_SPECFILE_LOCATION);
     }
@@ -191,5 +216,102 @@ DStubController::~DStubController() {
 // Initialize controller wrapper's static instance getter member.
 DControllerTest::InstanceGetter DControllerTest::instanceGetter_ = NULL;
 
+//************************** TestParser *************************
+
+TestParser::TestParser(const std::string& param_name):param_name_(param_name) {
+}
+
+TestParser::~TestParser(){
+}
+
+void
+TestParser::build(isc::data::ConstElementPtr new_config) {
+    if (SimFailure::shouldFailOn(SimFailure::ftElementBuild)) {
+        // Simulates an error during element data parsing.
+        isc_throw (DCfgMgrBaseError, "Simulated build exception");
+    }
+
+    value_ = new_config;
+}
+
+void
+TestParser::commit() {
+    if (SimFailure::shouldFailOn(SimFailure::ftElementCommit)) {
+        // Simulates an error while committing the parsed element data.
+        throw std::runtime_error("Simulated commit exception");
+    }
+}
+
+//************************** DStubContext *************************
+
+DStubContext::DStubContext(): extra_values_(new isc::dhcp::Uint32Storage()) {
+}
+
+DStubContext::~DStubContext() {
+}
+
+void
+DStubContext::getExtraParam(const std::string& name, uint32_t& value) {
+    value = extra_values_->getParam(name);
+}
+
+isc::dhcp::Uint32StoragePtr
+DStubContext::getExtraStorage() {
+    return (extra_values_);
+}
+
+DCfgContextBasePtr
+DStubContext::clone() {
+    return (DCfgContextBasePtr(new DStubContext(*this)));
+}
+
+DStubContext::DStubContext(const DStubContext& rhs): DCfgContextBase(rhs),
+    extra_values_(new isc::dhcp::Uint32Storage(*(rhs.extra_values_))) {
+}
+
+//************************** DStubCfgMgr *************************
+
+DStubCfgMgr::DStubCfgMgr()
+    : DCfgMgrBase(DCfgContextBasePtr(new DStubContext())) {
+}
+
+DStubCfgMgr::~DStubCfgMgr() {
+}
+
+isc::dhcp::ParserPtr
+DStubCfgMgr::createConfigParser(const std::string& element_id) {
+    isc::dhcp::DhcpConfigParser* parser = NULL;
+    DStubContextPtr context =
+                    boost::dynamic_pointer_cast<DStubContext>(getContext());
+
+    if (element_id == "bool_test") {
+        parser = new isc::dhcp::BooleanParser(element_id,
+                                              context->getBooleanStorage());
+    } else if (element_id == "uint32_test") {
+        parser = new isc::dhcp::Uint32Parser(element_id,
+                                             context->getUint32Storage());
+    } else if (element_id == "string_test") {
+        parser = new isc::dhcp::StringParser(element_id,
+                                             context->getStringStorage());
+    } else if (element_id == "extra_test") {
+        parser = new isc::dhcp::Uint32Parser(element_id,
+                                             context->getExtraStorage());
+    } else {
+        // Fail only if SimFailure dictates we should.  This makes it easier
+        // to test parse ordering, by permitting a wide range of element ids
+        // to "succeed" without specifically supporting them.
+        if (SimFailure::shouldFailOn(SimFailure::ftElementUnknown)) {
+            isc_throw(DCfgMgrBaseError, "Configuration parameter not supported: "
+                      << element_id);
+        }
+
+        parsed_order_.push_back(element_id);
+        parser = new TestParser(element_id);
+    }
+
+    return (isc::dhcp::ParserPtr(parser));
+}
+
+
 }; // namespace isc::d2
 }; // namespace isc
diff --git a/src/bin/d2/tests/d_test_stubs.h b/src/bin/d2/tests/d_test_stubs.h
index e634762..99670de 100644
--- a/src/bin/d2/tests/d_test_stubs.h
+++ b/src/bin/d2/tests/d_test_stubs.h
@@ -21,11 +21,18 @@
 #include <config/ccsession.h>
 
 #include <d2/d_controller.h>
+#include <d2/d_cfg_mgr.h>
+
 #include <gtest/gtest.h>
 
 namespace isc {
 namespace d2 {
 
+/// @brief Provides a valid DHCP-DDNS configuration for testing basic
+/// parsing fundamentals.
+extern const char* valid_d2_config;
+
+
 /// @brief Class is used to set a globally accessible value that indicates
 /// a specific type of failure to simulate.  Test derivations of base classes
 /// can exercise error handling code paths by testing for specific SimFailure
@@ -43,7 +50,10 @@ public:
         ftProcessConfigure,
         ftControllerCommand,
         ftProcessCommand,
-        ftProcessShutdown
+        ftProcessShutdown,
+        ftElementBuild,
+        ftElementCommit,
+        ftElementUnknown
     };
 
     /// @brief Sets the SimFailure value to the given value.
@@ -80,10 +90,12 @@ public:
         return (false);
     }
 
+    /// @brief Resets the failure type to none.
     static void clear() {
        failure_type_ = ftNoFailure;
     }
 
+    /// @brief Static value for holding the failure type to simulate.
     static enum FailureType failure_type_;
 };
 
@@ -397,7 +409,7 @@ public:
     /// DControllerBase::launch for details.
     void launch(int argc, char* argv[]) {
         optind = 1;
-        getController()->launch(argc, argv);
+        getController()->launch(argc, argv, true);
     }
 
     /// @Wrapper to invoke the Controller's disconnectSession method.  Please
@@ -433,6 +445,209 @@ public:
     }
 };
 
+/// @brief Simple parser derivation for testing the basics of configuration
+/// parsing.
+class TestParser : public isc::dhcp::DhcpConfigParser {
+public:
+
+    /// @brief Constructor
+    ///
+    /// See @ref DhcpConfigParser class for details.
+    ///
+    /// @param param_name name of the parsed parameter
+    TestParser(const std::string& param_name);
+
+    /// @brief Destructor
+    virtual ~TestParser();
+
+    /// @brief Builds parameter value.
+    ///
+    /// See @ref DhcpConfigParser class for details.
+    ///
+    /// @param new_config pointer to the new configuration
+    /// @throw throws DCfgMgrBaseError if the SimFailure is set to
+    /// ftElementBuild. This allows for the simulation of an
+    /// exception during the build portion of parsing an element.
+    virtual void build(isc::data::ConstElementPtr new_config);
+
+    /// @brief Commits the parsed value to storage.
+    ///
+    /// See @ref DhcpConfigParser class for details.
+    ///
+    /// @throw throws DCfgMgrBaseError if SimFailure is set to ftElementCommit.
+    /// This allows for the simulation of an exception during the commit
+    /// portion of parsing an element.
+    virtual void commit();
+
+private:
+    /// name of the parsed parameter
+    std::string param_name_;
+
+    /// pointer to the parsed value of the parameter
+    isc::data::ConstElementPtr value_;
+};
+
+/// @brief Test Derivation of the DCfgContextBase class.
+///
+/// This class is used to test basic functionality of configuration context.
+/// It adds an additional storage container "extra values" to mimic an
+/// application extension of configuration storage.  This permits testing that
+/// both the base class content as well as the application content is
+/// correctly copied during cloning.  This is vital to configuration backup
+/// and rollback during configuration parsing.
+class DStubContext : public DCfgContextBase {
+public:
+
+    /// @brief Constructor
+    DStubContext();
+
+    /// @brief Destructor
+    virtual ~DStubContext();
+
+    /// @brief Fetches the value for a given "extra" configuration parameter
+    /// from the context.
+    ///
+    /// @param name is the name of the parameter to retrieve.
+    /// @param value is an output parameter in which to return the retrieved
+    /// value.
+    /// @throw throws DhcpConfigError if the context does not contain the
+    /// parameter.
+    void getExtraParam(const std::string& name, uint32_t& value);
+
+    /// @brief Fetches the extra storage.
+    ///
+    /// @return returns a pointer to the extra storage.
+    isc::dhcp::Uint32StoragePtr getExtraStorage();
+
+    /// @brief Creates a clone of a DStubContext.
+    ///
+    /// @return returns a pointer to the new clone.
+    virtual DCfgContextBasePtr clone();
+
+protected:
+    /// @brief Copy constructor
+    DStubContext(const DStubContext& rhs);
+
+private:
+    /// @brief Private assignment operator, not implemented.
+    DStubContext& operator=(const DStubContext& rhs);
+
+    /// @brief Extra storage for uint32 parameters.
+    isc::dhcp::Uint32StoragePtr extra_values_;
+};
+
+/// @brief Defines a pointer to DStubContext.
+typedef boost::shared_ptr<DStubContext> DStubContextPtr;
+
+/// @brief Test Derivation of the DCfgMgrBase class.
+///
+/// This class is used to test basic functionality of configuration management.
+/// It supports the following configuration elements:
+///
+/// "bool_test" - Boolean element, tests parsing and committing a boolean
+///               configuration parameter.
+/// "uint32_test" - Uint32 element, tests parsing and committing a uint32_t
+///               configuration parameter.
+/// "string_test" - String element, tests parsing and committing a string
+///               configuration parameter.
+/// "extra_test" - "Extra" element, tests parsing and committing an extra
+///               configuration parameter. (This is used to demonstrate
+///               derivation's addition of storage to configuration context.
+///
+/// It also keeps track of the element ids that are parsed in the order they
+/// are parsed.  This is used to test ordered and non-ordered parsing.
+class DStubCfgMgr : public DCfgMgrBase {
+public:
+    /// @brief Constructor
+    DStubCfgMgr();
+
+    /// @brief Destructor
+    virtual ~DStubCfgMgr();
+
+    /// @brief Given an element_id returns an instance of the appropriate
+    /// parser. It supports the element ids as described in the class brief.
+    ///
+    /// @param element_id is the string name of the element as it will appear
+    /// in the configuration set.
+    ///
+    /// @return returns a ParserPtr to the parser instance.
+    /// @throw throws DCfgMgrBaseError if SimFailure is ftElementUnknown.
+    virtual isc::dhcp::ParserPtr
+    createConfigParser(const std::string& element_id);
+
+    /// @brief A list for remembering the element ids in the order they were
+    /// parsed.
+    ElementIdList parsed_order_;
+};
+
+/// @brief Defines a pointer to DStubCfgMgr.
+typedef boost::shared_ptr<DStubCfgMgr> DStubCfgMgrPtr;
+
+/// @brief Test fixture base class for any fixtures which test parsing.
+/// It provides methods for converting JSON strings to configuration element
+/// sets and checking parse results
+class ConfigParseTest : public ::testing::Test {
+public:
+
+    /// @brief Constructor
+    ConfigParseTest(){
+    }
+
+    /// @brief Destructor
+    ~ConfigParseTest() {
+    }
+
+    /// @brief Converts a given JSON string into an Element set and stores the
+    /// result the member variable, config_set_.
+    ///
+    /// @param json_text contains the configuration text in JSON format to
+    /// convert.
+    /// @return returns AssertionSuccess if there were no parsing errors,
+    /// AssertionFailure otherwise.
+    ::testing::AssertionResult fromJSON(std::string& json_text) {
+        try  {
+            config_set_ = isc::data::Element::fromJSON(json_text);
+        } catch (const isc::Exception &ex) {
+            return (::testing::AssertionFailure(::testing::Message() << 
+                                                "JSON text failed to parse:" 
+                                                << ex.what())); 
+        }
+
+        return (::testing::AssertionSuccess());
+    }
+
+
+    /// @brief Compares the status in the  parse result stored in member
+    /// variable answer_ to a given value.
+    ///
+    /// @param should_be is an integer against which to compare the status.
+    ///
+    /// @return returns AssertionSuccess if there were no parsing errors,
+    /// AssertionFailure otherwise.
+    ::testing::AssertionResult checkAnswer(int should_be) {
+        int rcode = 0;
+        isc::data::ConstElementPtr comment;
+        comment = isc::config::parseAnswer(rcode, answer_);
+        if (rcode == should_be) {
+            return (testing::AssertionSuccess());
+        }
+
+        return (::testing::AssertionFailure(::testing::Message() << 
+                                            "checkAnswer rcode:" << rcode 
+                                            << " comment: " << *comment));
+    }
+
+    /// @brief Configuration set being tested.
+    isc::data::ElementPtr config_set_;
+
+    /// @brief Results of most recent element parsing.
+    isc::data::ConstElementPtr answer_;
+};
+
+/// @brief Defines a small but valid DHCP-DDNS compliant configuration for
+/// testing configuration parsing fundamentals.
+extern const char* valid_d2_config;
+
 }; // namespace isc::d2
 }; // namespace isc
 
diff --git a/src/bin/d2/tests/dns_client_unittests.cc b/src/bin/d2/tests/dns_client_unittests.cc
new file mode 100644
index 0000000..9105ab8
--- /dev/null
+++ b/src/bin/d2/tests/dns_client_unittests.cc
@@ -0,0 +1,408 @@
+// Copyright (C) 2013 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 <d2/dns_client.h>
+#include <asiodns/io_fetch.h>
+#include <asiodns/logger.h>
+#include <asiolink/interval_timer.h>
+#include <dns/rcode.h>
+#include <dns/rrclass.h>
+#include <dns/tsig.h>
+#include <asio/ip/udp.hpp>
+#include <asio/socket_base.hpp>
+#include <boost/bind.hpp>
+#include <boost/scoped_ptr.hpp>
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::asiolink;
+using namespace isc::asiodns;
+using namespace isc::d2;
+
+using namespace isc;
+using namespace isc::dns;
+using namespace isc::util;
+using namespace asio;
+using namespace asio::ip;
+
+namespace {
+
+const char* TEST_ADDRESS = "127.0.0.1";
+const uint16_t TEST_PORT = 5301;
+const size_t MAX_SIZE = 1024;
+const long TEST_TIMEOUT = 5 * 1000;
+
+// @brief Test Fixture class.
+//
+// This test fixture class implements DNSClient::Callback so as it can be
+// installed as a completion callback for tests it implements. This callback
+// is called when a DDNS transaction (send and receive) completes. This allows
+// for the callback function to directly access class members. In particular,
+// the callback function can access IOService on which run() was called and
+// call stop() on it.
+//
+// Many of the tests defined here schedule execution of certain tasks and block
+// until tasks are completed or a timeout is hit. However, if timeout is not
+// properly handled a task may be hanging for a long time. In order to prevent
+// it, the asiolink::IntervalTimer is used to break a running test if test
+// timeout is hit. This will result in test failure.
+class DNSClientTest : public virtual ::testing::Test, DNSClient::Callback {
+public:
+    IOService service_;
+    D2UpdateMessagePtr response_;
+    DNSClient::Status status_;
+    uint8_t receive_buffer_[MAX_SIZE];
+    DNSClientPtr dns_client_;
+    bool corrupt_response_;
+    bool expect_response_;
+    asiolink::IntervalTimer test_timer_;
+
+    // @brief Constructor.
+    //
+    // This constructor overrides the default logging level of asiodns logger to
+    // prevent it from emitting debug messages from IOFetch class. Such an error
+    // message can be emitted if timeout occurs when DNSClient class is
+    // waiting for a response. Some of the tests are checking DNSClient behavior
+    // in case when response from the server is not received. Tests output would
+    // become messy if such errors were logged.
+    DNSClientTest()
+        : service_(),
+          status_(DNSClient::SUCCESS),
+          corrupt_response_(false),
+          expect_response_(true),
+          test_timer_(service_) {
+        asiodns::logger.setSeverity(isc::log::INFO);
+        response_.reset(new D2UpdateMessage(D2UpdateMessage::INBOUND));
+        dns_client_.reset(new DNSClient(response_, this));
+
+        // Set the test timeout to break any running tasks if they hang.
+        test_timer_.setup(boost::bind(&DNSClientTest::testTimeoutHandler, this),
+                          TEST_TIMEOUT);
+    }
+
+    // @brief Destructor.
+    //
+    // Sets the asiodns logging level back to DEBUG.
+    virtual ~DNSClientTest() {
+        asiodns::logger.setSeverity(isc::log::DEBUG);
+    };
+
+    // @brief Exchange completion callback.
+    //
+    // This callback is called when the exchange with the DNS server is
+    // complete or an error occurred. This includes the occurrence of a timeout.
+    //
+    // @param status A status code returned by DNSClient.
+    virtual void operator()(DNSClient::Status status) {
+        status_ = status;
+        service_.stop();
+
+        if (expect_response_) {
+            if (!corrupt_response_) {
+                // We should have received a response.
+                EXPECT_EQ(DNSClient::SUCCESS, status_);
+
+                ASSERT_TRUE(response_);
+                EXPECT_EQ(D2UpdateMessage::RESPONSE, response_->getQRFlag());
+                ASSERT_EQ(1,
+                          response_->getRRCount(D2UpdateMessage::SECTION_ZONE));
+                D2ZonePtr zone = response_->getZone();
+                ASSERT_TRUE(zone);
+                EXPECT_EQ("example.com.", zone->getName().toText());
+                EXPECT_EQ(RRClass::IN().getCode(), zone->getClass().getCode());
+
+            } else {
+                EXPECT_EQ(DNSClient::INVALID_RESPONSE, status_);
+
+            }
+        // If we don't expect a response, the status should indicate a timeout.
+        } else {
+            EXPECT_EQ(DNSClient::TIMEOUT, status_);
+
+        }
+    }
+
+    // @brief Handler invoked when test timeout is hit.
+    //
+    // This callback stops all running (hanging) tasks on IO service.
+    void testTimeoutHandler() {
+        service_.stop();
+        FAIL() << "Test timeout hit.";
+    }
+
+    // @brief Handler invoked when test request is received.
+    //
+    // This callback handler is installed when performing async read on a
+    // socket to emulate reception of the DNS Update request by a server.
+    // As a result, this handler will send an appropriate DNS Update response
+    // message back to the address from which the request has come.
+    //
+    // @param socket A pointer to a socket used to receive a query and send a
+    // response.
+    // @param remote A pointer to an object which specifies the host (address
+    // and port) from which a request has come.
+    // @param receive_length A length (in bytes) of the received data.
+    // @param corrupt_response A bool value which indicates that the server's
+    // response should be invalid (true) or valid (false)
+    void udpReceiveHandler(udp::socket* socket, udp::endpoint* remote,
+                           size_t receive_length, const bool corrupt_response) {
+        // The easiest way to create a response message is to copy the entire
+        // request.
+        OutputBuffer response_buf(receive_length);
+        response_buf.writeData(receive_buffer_, receive_length);
+        // If a response is to be valid, we have to modify it slightly. If not,
+        // we leave it as is.
+        if (!corrupt_response) {
+            // For a valid response the QR bit must be set. This bit
+            // differentiates both types of messages. Note that the 3rd byte of
+            // the message header comprises this bit in the front followed by
+            // the message code and reserved zeros. Therefore, this byte
+            // has the following value:
+            //             10101000,
+            // where a leading bit is a QR flag. The hexadecimal value is 0xA8.
+            // Write it at message offset 2.
+            response_buf.writeUint8At(0xA8, 2);
+        }
+        // A response message is now ready to send. Send it!
+        socket->send_to(asio::buffer(response_buf.getData(),
+                                     response_buf.getLength()),
+                        *remote);
+    }
+
+    // This test verifies that when invalid response placeholder object is
+    // passed to a constructor, constructor throws the appropriate exception.
+    // It also verifies that the constructor will not throw if the supplied
+    // callback object is NULL.
+    void runConstructorTest() {
+        D2UpdateMessagePtr null_response;
+        EXPECT_THROW(DNSClient(null_response, this, DNSClient::UDP),
+                     isc::BadValue);
+        EXPECT_NO_THROW(DNSClient(response_, NULL, DNSClient::UDP));
+
+        // The TCP Transport is not supported right now. So, we return exception
+        // if caller specified TCP as a preferred protocol. This test will be
+        // removed once TCP is supported.
+        EXPECT_THROW(DNSClient(response_, NULL, DNSClient::TCP),
+                     isc::NotImplemented);
+    }
+
+    // This test verifies that it accepted timeout values belong to the range of
+    // <0, DNSClient::getMaxTimeout()>.
+    void runInvalidTimeoutTest() {
+
+        expect_response_ = false;
+
+        // Create outgoing message. Simply set the required message fields:
+        // error code and Zone section. This is enough to create on-wire format
+        // of this message and send it.
+        D2UpdateMessage message(D2UpdateMessage::OUTBOUND);
+        ASSERT_NO_THROW(message.setRcode(Rcode(Rcode::NOERROR_CODE)));
+        ASSERT_NO_THROW(message.setZone(Name("example.com"), RRClass::IN()));
+
+        // Start with a valid timeout equal to maximal allowed. This way we will
+        // ensure that doUpdate doesn't throw an exception for valid timeouts.
+        unsigned int timeout = DNSClient::getMaxTimeout();
+        EXPECT_NO_THROW(dns_client_->doUpdate(service_, IOAddress(TEST_ADDRESS),
+                                           TEST_PORT, message, timeout));
+
+        // Cross the limit and expect that exception is thrown this time.
+        timeout = DNSClient::getMaxTimeout() + 1;
+        EXPECT_THROW(dns_client_->doUpdate(service_, IOAddress(TEST_ADDRESS),
+                                           TEST_PORT, message, timeout),
+                     isc::BadValue);
+    }
+
+    // This test verifies that isc::NotImplemented exception is thrown when
+    // attempt to send DNS Update message with TSIG is attempted.
+    void runTSIGTest() {
+        // Create outgoing message. Simply set the required message fields:
+        // error code and Zone section. This is enough to create on-wire format
+        // of this message and send it.
+        D2UpdateMessage message(D2UpdateMessage::OUTBOUND);
+        ASSERT_NO_THROW(message.setRcode(Rcode(Rcode::NOERROR_CODE)));
+        ASSERT_NO_THROW(message.setZone(Name("example.com"), RRClass::IN()));
+
+        const int timeout = 0;
+        // Try to send DNS Update with TSIG key. Currently TSIG is not supported
+        // and therefore we expect an exception.
+        TSIGKey tsig_key("key.example:MSG6Ng==");
+        EXPECT_THROW(dns_client_->doUpdate(service_, IOAddress(TEST_ADDRESS),
+                                           TEST_PORT, message, timeout,
+                                           tsig_key),
+                     isc::NotImplemented);
+    }
+
+    // This test verifies the DNSClient behavior when a server does not respond
+    // do the DNS Update message. In such case, the callback function is
+    // expected to be called and the TIME_OUT error code should be returned.
+    void runSendNoReceiveTest() {
+        // We expect no response from a server.
+        expect_response_ = false;
+
+        // Create outgoing message. Simply set the required message fields:
+        // error code and Zone section. This is enough to create on-wire format
+        // of this message and send it.
+        D2UpdateMessage message(D2UpdateMessage::OUTBOUND);
+        ASSERT_NO_THROW(message.setRcode(Rcode(Rcode::NOERROR_CODE)));
+        ASSERT_NO_THROW(message.setZone(Name("example.com"), RRClass::IN()));
+
+        // Set the response wait time to 0 so as our test is not hanging. This
+        // should cause instant timeout.
+        const int timeout = 0;
+        // The doUpdate() function starts asynchronous message exchange with DNS
+        // server. When message exchange is done or timeout occurs, the
+        // completion callback will be triggered. The doUpdate function returns
+        // immediately.
+        EXPECT_NO_THROW(dns_client_->doUpdate(service_, IOAddress(TEST_ADDRESS),
+                                             TEST_PORT, message, timeout));
+
+        // This starts the execution of tasks posted to IOService. run() blocks
+        // until stop() is called in the completion callback function.
+        service_.run();
+
+    }
+
+    // This test verifies that DNSClient can send DNS Update and receive a
+    // corresponding response from a server.
+    void runSendReceiveTest(const bool corrupt_response,
+                            const bool two_sends) {
+        corrupt_response_ = corrupt_response;
+
+        // Create a request DNS Update message.
+        D2UpdateMessage message(D2UpdateMessage::OUTBOUND);
+        ASSERT_NO_THROW(message.setRcode(Rcode(Rcode::NOERROR_CODE)));
+        ASSERT_NO_THROW(message.setZone(Name("example.com"), RRClass::IN()));
+
+        // In order to perform the full test, when the client sends the request
+        // and receives a response from the server, we have to emulate the
+        // server's response in the test. A request will be sent via loopback
+        // interface to 127.0.0.1 and known test port. Response must be sent
+        // to 127.0.0.1 and a source port which has been used to send the
+        // request. A new socket is created, specifically to handle sending
+        // responses. The reuse address option is set so as both sockets can
+        // use the same address. This new socket is bound to the test address
+        // and port, where requests will be sent.
+        udp::socket udp_socket(service_.get_io_service(), asio::ip::udp::v4());
+        udp_socket.set_option(socket_base::reuse_address(true));
+        udp_socket.bind(udp::endpoint(address::from_string(TEST_ADDRESS),
+                                      TEST_PORT));
+        // Once socket is created, we can post an IO request to receive some
+        // a packet from this socket. This is asynchronous operation and
+        // nothing is received until another IO request to send a query is
+        // posted and the run() is invoked on this IO. A callback function is
+        // attached to this asynchronous read. This callback function requires
+        // that a socket object used to receive the request is passed to it,
+        // because the same socket will be used by the callback function to send
+        // a response. Also, the remote object is passed to the callback,
+        // because it holds a source address and port where request originated.
+        // Callback function will send a response to this address and port.
+        // The last parameter holds a length of the received request. It is
+        // required to construct a response.
+        udp::endpoint remote;
+        udp_socket.async_receive_from(asio::buffer(receive_buffer_,
+                                                   sizeof(receive_buffer_)),
+                                      remote,
+                                      boost::bind(&DNSClientTest::udpReceiveHandler,
+                                                  this, &udp_socket, &remote, _2,
+                                                  corrupt_response));
+
+        // The socket is now ready to receive the data. Let's post some request
+        // message then.
+        const int timeout = 5;
+        dns_client_->doUpdate(service_, IOAddress(TEST_ADDRESS), TEST_PORT,
+                             message, timeout);
+
+        // It is possible to request that two packets are sent concurrently.
+        if (two_sends) {
+            dns_client_->doUpdate(service_, IOAddress(TEST_ADDRESS), TEST_PORT,
+                                  message, timeout);
+
+        }
+
+        // Kick of the message exchange by actually running the scheduled
+        // "send" and "receive" operations.
+        service_.run();
+
+        udp_socket.close();
+
+    }
+};
+
+// Verify that the DNSClient object can be created if provided parameters are
+// valid. Constructor should throw exceptions when parameters are invalid.
+TEST_F(DNSClientTest, constructor) {
+    runConstructorTest();
+}
+
+// This test verifies that the maximal allowed timeout value is maximal int
+// value.
+TEST_F(DNSClientTest, getMaxTimeout) {
+    EXPECT_EQ(std::numeric_limits<int>::max(), DNSClient::getMaxTimeout());
+}
+
+// Verify that timeout is reported when no response is received from DNS.
+TEST_F(DNSClientTest, timeout) {
+    runSendNoReceiveTest();
+}
+
+// Verify that exception is thrown when invalid (too high) timeout value is
+// specified for asynchronous DNS Update.
+TEST_F(DNSClientTest, invalidTimeout) {
+    runInvalidTimeoutTest();
+}
+
+// Verify that exception is thrown when an attempt to send DNS Update with TSIG
+// is made. This test will be removed/changed once TSIG support is added.
+TEST_F(DNSClientTest, runTSIGTest) {
+    runTSIGTest();
+}
+
+// Verify that the DNSClient receives the response from DNS and the received
+// buffer can be decoded as DNS Update Response.
+TEST_F(DNSClientTest, sendReceive) {
+    // false means that server response is not corrupted.
+    runSendReceiveTest(false, false);
+}
+
+// Verify that the DNSClient reports an error when the response is received from
+// a DNS and this response is corrupted.
+TEST_F(DNSClientTest, sendReceiveCurrupted) {
+    // true means that server's response is corrupted.
+    runSendReceiveTest(true, false);
+}
+
+// Verify that it is possible to use the same DNSClient instance to
+// perform the following sequence of message exchanges:
+// 1. send
+// 2. receive
+// 3. send
+// 4. receive
+TEST_F(DNSClientTest, sendReceiveTwice) {
+    runSendReceiveTest(false, false);
+    runSendReceiveTest(false, false);
+}
+
+// Verify that it is possible to use the DNSClient instance to perform the
+// following  sequence of message exchanges:
+// 1. send
+// 2. send
+// 3. receive
+// 4. receive
+TEST_F(DNSClientTest, concurrentSendReceive) {
+    runSendReceiveTest(false, true);
+}
+
+} // End of anonymous namespace
diff --git a/src/bin/d2/tests/ncr_unittests.cc b/src/bin/d2/tests/ncr_unittests.cc
new file mode 100644
index 0000000..8c7a9ac
--- /dev/null
+++ b/src/bin/d2/tests/ncr_unittests.cc
@@ -0,0 +1,401 @@
+// Copyright (C) 2013  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 <d2/ncr_msg.h>
+#include <util/time_utilities.h>
+
+#include <gtest/gtest.h>
+#include <algorithm>
+
+using namespace std;
+using namespace isc;
+using namespace isc::d2;
+
+namespace {
+
+/// @brief Defines a list of valid JSON NameChangeRequest renditions.
+/// They are used as input to test conversion from JSON.
+const char *valid_msgs[] =
+{
+    // Valid Add.
+     "{"
+     " \"change_type\" : 0 , "
+     " \"forward_change\" : true , "
+     " \"reverse_change\" : false , "
+     " \"fqdn\" : \"walah.walah.com\" , "
+     " \"ip_address\" : \"192.168.2.1\" , "
+     " \"dhcid\" : \"010203040A7F8E3D\" , "
+     " \"lease_expires_on\" : \"20130121132405\" , "
+     " \"lease_length\" : 1300 "
+     "}",
+    // Valid Remove.
+     "{"
+     " \"change_type\" : 1 , "
+     " \"forward_change\" : true , "
+     " \"reverse_change\" : false , "
+     " \"fqdn\" : \"walah.walah.com\" , "
+     " \"ip_address\" : \"192.168.2.1\" , "
+     " \"dhcid\" : \"010203040A7F8E3D\" , "
+     " \"lease_expires_on\" : \"20130121132405\" , "
+     " \"lease_length\" : 1300 "
+     "}",
+     // Valid Add with IPv6 address
+     "{"
+     " \"change_type\" : 0 , "
+     " \"forward_change\" : true , "
+     " \"reverse_change\" : false , "
+     " \"fqdn\" : \"walah.walah.com\" , "
+     " \"ip_address\" : \"fe80::2acf:e9ff:fe12:e56f\" , "
+     " \"dhcid\" : \"010203040A7F8E3D\" , "
+     " \"lease_expires_on\" : \"20130121132405\" , "
+     " \"lease_length\" : 1300 "
+     "}"
+};
+
+/// @brief Defines a list of invalid JSON NameChangeRequest renditions.
+/// They are used as input to test conversion from JSON.
+const char *invalid_msgs[] =
+{
+    // Invalid change type.
+     "{"
+     " \"change_type\" : 7 , "
+     " \"forward_change\" : true , "
+     " \"reverse_change\" : false , "
+     " \"fqdn\" : \"walah.walah.com\" , "
+     " \"ip_address\" : \"192.168.2.1\" , "
+     " \"dhcid\" : \"010203040A7F8E3D\" , "
+     " \"lease_expires_on\" : \"20130121132405\" , "
+     " \"lease_length\" : 1300 "
+     "}",
+    // Invalid forward change.
+     "{"
+     " \"change_type\" : 0 , "
+     " \"forward_change\" : \"bogus\" , "
+     " \"reverse_change\" : false , "
+     " \"fqdn\" : \"walah.walah.com\" , "
+     " \"ip_address\" : \"192.168.2.1\" , "
+     " \"dhcid\" : \"010203040A7F8E3D\" , "
+     " \"lease_expires_on\" : \"20130121132405\" , "
+     " \"lease_length\" : 1300 "
+     "}",
+    // Invalid reverse change.
+     "{"
+     " \"change_type\" : 0 , "
+     " \"forward_change\" : true , "
+     " \"reverse_change\" : 500 , "
+     " \"fqdn\" : \"walah.walah.com\" , "
+     " \"ip_address\" : \"192.168.2.1\" , "
+     " \"dhcid\" : \"010203040A7F8E3D\" , "
+     " \"lease_expires_on\" : \"20130121132405\" , "
+     " \"lease_length\" : 1300 "
+     "}",
+    // Forward and reverse change both false.
+     "{"
+     " \"change_type\" : 0 , "
+     " \"forward_change\" : false , "
+     " \"reverse_change\" : false , "
+     " \"fqdn\" : \"walah.walah.com\" , "
+     " \"ip_address\" : \"192.168.2.1\" , "
+     " \"dhcid\" : \"010203040A7F8E3D\" , "
+     " \"lease_expires_on\" : \"20130121132405\" , "
+     " \"lease_length\" : 1300 "
+     "}",
+    // Blank FQDN
+     "{"
+     " \"change_type\" : 0 , "
+     " \"forward_change\" : true , "
+     " \"reverse_change\" : false , "
+     " \"fqdn\" : \"\" , "
+     " \"ip_address\" : \"192.168.2.1\" , "
+     " \"dhcid\" : \"010203040A7F8E3D\" , "
+     " \"lease_expires_on\" : \"20130121132405\" , "
+     " \"lease_length\" : 1300 "
+     "}",
+    // Bad IP address
+     "{"
+     " \"change_type\" : 0 , "
+     " \"forward_change\" : true , "
+     " \"reverse_change\" : false , "
+     " \"fqdn\" : \"walah.walah.com\" , "
+     " \"ip_address\" : \"xxxxxx\" , "
+     " \"dhcid\" : \"010203040A7F8E3D\" , "
+     " \"lease_expires_on\" : \"20130121132405\" , "
+     " \"lease_length\" : 1300 "
+     "}",
+    // Blank DHCID
+     "{"
+     " \"change_type\" : 0 , "
+     " \"forward_change\" : true , "
+     " \"reverse_change\" : false , "
+     " \"fqdn\" : \"walah.walah.com\" , "
+     " \"ip_address\" : \"192.168.2.1\" , "
+     " \"dhcid\" : \"\" , "
+     " \"lease_expires_on\" : \"20130121132405\" , "
+     " \"lease_length\" : 1300 "
+     "}",
+    // Odd number of digits in DHCID
+     "{"
+     " \"change_type\" : 0 , "
+     " \"forward_change\" : true , "
+     " \"reverse_change\" : false , "
+     " \"fqdn\" : \"walah.walah.com\" , "
+     " \"ip_address\" : \"192.168.2.1\" , "
+     " \"dhcid\" : \"010203040A7F8E3\" , "
+     " \"lease_expires_on\" : \"20130121132405\" , "
+     " \"lease_length\" : 1300 "
+     "}",
+    // Text in DHCID
+     "{"
+     " \"change_type\" : 0 , "
+     " \"forward_change\" : true , "
+     " \"reverse_change\" : false , "
+     " \"fqdn\" : \"walah.walah.com\" , "
+     " \"ip_address\" : \"192.168.2.1\" , "
+     " \"dhcid\" : \"THIS IS BOGUS!!!\" , "
+     " \"lease_expires_on\" : \"20130121132405\" , "
+     " \"lease_length\" : 1300 "
+     "}",
+    // Invalid lease expiration string
+     "{"
+     " \"change_type\" : 0 , "
+     " \"forward_change\" : true , "
+     " \"reverse_change\" : false , "
+     " \"fqdn\" : \"walah.walah.com\" , "
+     " \"ip_address\" : \"192.168.2.1\" , "
+     " \"dhcid\" : \"010203040A7F8E3D\" , "
+     " \"lease_expires_on\" : \"Wed Jun 26 13:46:46 EDT 2013\" , "
+     " \"lease_length\" : 1300 "
+     "}",
+    // Non-integer for lease length.
+     "{"
+     " \"change_type\" : 0 , "
+     " \"forward_change\" : true , "
+     " \"reverse_change\" : false , "
+     " \"fqdn\" : \"walah.walah.com\" , "
+     " \"ip_address\" : \"192.168.2.1\" , "
+     " \"dhcid\" : \"010203040A7F8E3D\" , "
+     " \"lease_expires_on\" : \"20130121132405\" , "
+     " \"lease_length\" : \"BOGUS\" "
+     "}"
+
+};
+
+/// @brief Tests the NameChangeRequest constructors.
+/// This test verifies that:
+/// 1. Default constructor works.
+/// 2. "Full" constructor, when given valid parameter values, works.
+/// 3. "Full" constructor, given a blank FQDN fails
+/// 4. "Full" constructor, given an invalid IP Address FQDN fails
+/// 5. "Full" constructor, given a blank DHCID fails
+/// 6. "Full" constructor, given false for both forward and reverse fails
+TEST(NameChangeRequestTest, constructionTests) {
+    // Verify the default constructor works.
+    NameChangeRequestPtr ncr;
+    EXPECT_NO_THROW(ncr.reset(new NameChangeRequest()));
+    EXPECT_TRUE(ncr);
+
+    // Verify that full constructor works.
+    uint64_t expiry = isc::util::detail::gettimeWrapper();
+    D2Dhcid dhcid("010203040A7F8E3D");
+
+    EXPECT_NO_THROW(ncr.reset(new NameChangeRequest(
+                    CHG_ADD, true, true, "walah.walah.com",
+                    "192.168.1.101", dhcid, expiry, 1300)));
+    EXPECT_TRUE(ncr);
+    ncr.reset();
+
+    // Verify blank FQDN is detected.
+    EXPECT_THROW(NameChangeRequest(CHG_ADD, true, true, "",
+                 "192.168.1.101", dhcid, expiry, 1300), NcrMessageError);
+
+    // Verify that an invalid IP address is detected.
+    EXPECT_THROW(NameChangeRequest(CHG_ADD, true, true, "valid.fqdn",
+                 "xxx.168.1.101", dhcid, expiry, 1300), NcrMessageError);
+
+    // Verify that a blank DHCID is detected.
+    D2Dhcid blank_dhcid;
+    EXPECT_THROW(NameChangeRequest(CHG_ADD, true, true, "walah.walah.com",
+                 "192.168.1.101", blank_dhcid, expiry, 1300), NcrMessageError);
+
+    // Verify that one or both of direction flags must be true.
+    EXPECT_THROW(NameChangeRequest(CHG_ADD, false, false, "valid.fqdn",
+                "192.168.1.101", dhcid, expiry, 1300), NcrMessageError);
+
+}
+
+/// @brief Tests the basic workings of D2Dhcid to and from string conversions.
+/// It verifies that:
+/// 1. DHCID input strings must contain an even number of characters
+/// 2. DHCID input strings must contain only hexadecimal character digits
+/// 3. A valid DHCID string converts correctly.
+/// 4. Converting a D2Dhcid to a string works correctly.
+TEST(NameChangeRequestTest, dhcidTest) {
+    D2Dhcid dhcid;
+
+    // Odd number of digits should be rejected.
+    std::string test_str = "010203040A7F8E3";
+    EXPECT_THROW(dhcid.fromStr(test_str), NcrMessageError);
+
+    // Non digit content should be rejected.
+    test_str = "0102BOGUSA7F8E3D";
+    EXPECT_THROW(dhcid.fromStr(test_str), NcrMessageError);
+
+    // Verify that valid input converts into a proper byte array.
+    test_str = "010203040A7F8E3D";
+    ASSERT_NO_THROW(dhcid.fromStr(test_str));
+
+    // Create a test vector of expected byte contents.
+    const uint8_t bytes[] = { 0x1, 0x2, 0x3, 0x4, 0xA, 0x7F, 0x8E, 0x3D };
+    std::vector<uint8_t> expected_bytes(bytes, bytes + sizeof(bytes));
+
+    // Fetch the byte vector from the dhcid and verify if equals the expected
+    // content.
+    const std::vector<uint8_t>& converted_bytes = dhcid.getBytes();
+    EXPECT_EQ(expected_bytes.size(), converted_bytes.size());
+    EXPECT_TRUE (std::equal(expected_bytes.begin(),
+                            expected_bytes.begin()+expected_bytes.size(),
+                            converted_bytes.begin()));
+
+    // Convert the new dhcid back to string and verify it matches the original
+    // DHCID input string.
+    std::string next_str = dhcid.toStr();
+    EXPECT_EQ(test_str, next_str);
+}
+
+/// @brief Verifies the fundamentals of converting from and to JSON.
+/// It verifies that:
+/// 1. A NameChangeRequest can be created from a valid JSON string.
+/// 2. A valid JSON string can be created from a NameChangeRequest
+TEST(NameChangeRequestTest, basicJsonTest) {
+    // Define valid JSON rendition of a request.
+    std::string msg_str = "{"
+                            "\"change_type\":1,"
+                            "\"forward_change\":true,"
+                            "\"reverse_change\":false,"
+                            "\"fqdn\":\"walah.walah.com\","
+                            "\"ip_address\":\"192.168.2.1\","
+                            "\"dhcid\":\"010203040A7F8E3D\","
+                            "\"lease_expires_on\":\"20130121132405\","
+                            "\"lease_length\":1300"
+                          "}";
+
+    // Verify that a NameChangeRequests can be instantiated from the
+    // a valid JSON rendition.
+    NameChangeRequestPtr ncr;
+    ASSERT_NO_THROW(ncr  = NameChangeRequest::fromJSON(msg_str));
+    ASSERT_TRUE(ncr);
+
+    // Verify that the JSON string created by the new request equals the
+    // original input string.
+    std::string json_str = ncr->toJSON();
+    EXPECT_EQ(msg_str, json_str);
+}
+
+/// @brief Tests a variety of invalid JSON message strings.
+/// This test iterates over a list of JSON messages, each containing a single
+/// content error. The list of messages is defined by the global array,
+/// invalid_messages. Currently that list contains the following invalid
+/// conditions:
+///  1. Invalid change type
+///  2. Invalid forward change
+///  3. Invalid reverse change
+///  4. Forward and reverse change both false
+///  5. Invalid forward change
+///  6. Blank FQDN
+///  7. Bad IP address
+///  8. Blank DHCID
+///  9. Odd number of digits in DHCID
+/// 10. Text in DHCID
+/// 11. Invalid lease expiration string
+/// 12. Non-integer for lease length.
+/// If more permutations arise they can easily be added to the list.
+TEST(NameChangeRequestTest, invalidMsgChecks) {
+    // Iterate over the list of JSON strings, attempting to create a
+    // NameChangeRequest. The attempt should throw a NcrMessageError.
+    int num_msgs = sizeof(invalid_msgs)/sizeof(char*);
+    for (int i = 0; i < num_msgs; i++) {
+        EXPECT_THROW(NameChangeRequest::fromJSON(invalid_msgs[i]),
+                     NcrMessageError) << "Invalid message not caught idx: "
+                     << i << std::endl << " text:[" << invalid_msgs[i] << "]"
+                     << std::endl;
+    }
+}
+
+/// @brief Tests a variety of valid JSON message strings.
+/// This test iterates over a list of JSON messages, each containing a single
+/// valid request rendition. The list of messages is defined by the global
+/// array, valid_messages. Currently that list contains the following valid
+/// messages:
+///  1. Valid, IPv4 Add
+///  2. Valid, IPv4 Remove
+///  3. Valid, IPv6 Add
+/// If more permutations arise they can easily be added to the list.
+TEST(NameChangeRequestTest, validMsgChecks) {
+    // Iterate over the list of JSON strings, attempting to create a
+    // NameChangeRequest. The attempt should succeed.
+    int num_msgs = sizeof(valid_msgs)/sizeof(char*);
+    for (int i = 0; i < num_msgs; i++) {
+        EXPECT_NO_THROW(NameChangeRequest::fromJSON(valid_msgs[i]))
+                        << "Valid message failed,  message idx: " << i
+                        << std::endl << " text:[" << valid_msgs[i] << "]"
+                        << std::endl;
+    }
+}
+
+/// @brief Tests converting to and from JSON via isc::util buffer classes.
+/// This test verifies that:
+/// 1. A NameChangeRequest can be rendered in JSON written to an OutputBuffer
+/// 2. A InputBuffer containing a valid JSON request rendition can be used
+/// to create a NameChangeRequest.
+TEST(NameChangeRequestTest, toFromBufferTest) {
+    // Define a string containing a valid JSON NameChangeRequest rendition.
+    std::string msg_str = "{"
+                            "\"change_type\":1,"
+                            "\"forward_change\":true,"
+                            "\"reverse_change\":false,"
+                            "\"fqdn\":\"walah.walah.com\","
+                            "\"ip_address\":\"192.168.2.1\","
+                            "\"dhcid\":\"010203040A7F8E3D\","
+                            "\"lease_expires_on\":\"20130121132405\","
+                            "\"lease_length\":1300"
+                          "}";
+
+    // Create a request from JSON directly.
+    NameChangeRequestPtr ncr;
+    ASSERT_NO_THROW(ncr = NameChangeRequest::fromJSON(msg_str));
+
+    // Verify that we output the request as JSON text to a buffer
+    // without error.
+    isc::util::OutputBuffer output_buffer(1024);
+    ASSERT_NO_THROW(ncr->toFormat(FMT_JSON, output_buffer));
+
+    // Make an InputBuffer from the OutputBuffer.
+    isc::util::InputBuffer input_buffer(output_buffer.getData(),
+                                        output_buffer.getLength());
+
+    // Verify that we can create a new request from the InputBuffer.
+    NameChangeRequestPtr ncr2;
+    ASSERT_NO_THROW(ncr2 =
+                    NameChangeRequest::fromFormat(FMT_JSON, input_buffer));
+
+    // Convert the new request to JSON directly.
+    std::string final_str = ncr2->toJSON();
+
+    // Verify that the final string matches the original.
+    ASSERT_EQ(final_str, msg_str);
+}
+
+
+} // end of anonymous namespace
+
diff --git a/src/bin/d2/tests/test_data_files_config.h.in b/src/bin/d2/tests/test_data_files_config.h.in
new file mode 100644
index 0000000..6064d3d
--- /dev/null
+++ b/src/bin/d2/tests/test_data_files_config.h.in
@@ -0,0 +1,17 @@
+// Copyright (C) 2009  Internet Systems Consortium, Inc. ("ISC")   
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+/// @brief Path to D2 source dir so tests against the dhcp-ddns.spec file
+/// can find it reliably.
+#define D2_SRC_DIR "@abs_top_srcdir@/src/bin/d2"
diff --git a/src/bin/dhcp4/Makefile.am b/src/bin/dhcp4/Makefile.am
index b3818c7..80b7fc3 100644
--- a/src/bin/dhcp4/Makefile.am
+++ b/src/bin/dhcp4/Makefile.am
@@ -63,6 +63,7 @@ b10_dhcp4_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
 b10_dhcp4_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
 b10_dhcp4_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
 b10_dhcp4_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
+b10_dhcp4_LDADD += $(top_builddir)/src/lib/hooks/libb10-hooks.la
 
 b10_dhcp4dir = $(pkgdatadir)
 b10_dhcp4_DATA = dhcp4.spec
diff --git a/src/bin/dhcp4/config_parser.cc b/src/bin/dhcp4/config_parser.cc
index f30ff21..726a4a6 100644
--- a/src/bin/dhcp4/config_parser.cc
+++ b/src/bin/dhcp4/config_parser.cc
@@ -53,10 +53,10 @@ public:
     /// @param dummy first param, option names are always "Dhcp4/option-data[n]"
     /// @param options is the option storage in which to store the parsed option
     /// upon "commit".
-    /// @param global_context is a pointer to the global context which 
+    /// @param global_context is a pointer to the global context which
     /// stores global scope parameters, options, option defintions.
-    Dhcp4OptionDataParser(const std::string&, 
-        OptionStoragePtr options, ParserContextPtr global_context) 
+    Dhcp4OptionDataParser(const std::string&,
+        OptionStoragePtr options, ParserContextPtr global_context)
         :OptionDataParser("", options, global_context) {
     }
 
@@ -64,7 +64,7 @@ public:
     ///
     /// @param param_name name of the parameter to be parsed.
     /// @param options storage where the parameter value is to be stored.
-    /// @param global_context is a pointer to the global context which 
+    /// @param global_context is a pointer to the global context which
     /// stores global scope parameters, options, option defintions.
     /// @return returns a pointer to a new OptionDataParser. Caller is
     /// is responsible for deleting it when it is no longer needed.
@@ -75,16 +75,16 @@ public:
 
 protected:
     /// @brief Finds an option definition within the server's option space
-    /// 
-    /// Given an option space and an option code, find the correpsonding 
+    ///
+    /// Given an option space and an option code, find the correpsonding
     /// option defintion within the server's option defintion storage.
     ///
-    /// @param option_space name of the parameter option space 
-    /// @param option_code numeric value of the parameter to find 
-    /// @return OptionDefintionPtr of the option defintion or an 
+    /// @param option_space name of the parameter option space
+    /// @param option_code numeric value of the parameter to find
+    /// @return OptionDefintionPtr of the option defintion or an
     /// empty OptionDefinitionPtr if not found.
-    /// @throw DhcpConfigError if the option space requested is not valid 
-    /// for this server. 
+    /// @throw DhcpConfigError if the option space requested is not valid
+    /// for this server.
     virtual OptionDefinitionPtr findServerSpaceOptionDefinition (
                 std::string& option_space, uint32_t option_code) {
         OptionDefinitionPtr def;
@@ -100,11 +100,11 @@ protected:
     }
 };
 
-/// @brief Parser for IPv4 pool definitions.  
+/// @brief Parser for IPv4 pool definitions.
 ///
-/// This is the IPv4 derivation of the PoolParser class and handles pool 
-/// definitions, i.e. a list of entries of one of two syntaxes: min-max and 
-/// prefix/len for IPv4 pools. Pool4 objects are created and stored in chosen 
+/// This is the IPv4 derivation of the PoolParser class and handles pool
+/// definitions, i.e. a list of entries of one of two syntaxes: min-max and
+/// prefix/len for IPv4 pools. Pool4 objects are created and stored in chosen
 /// PoolStorage container.
 ///
 /// It is useful for parsing Dhcp4/subnet4[X]/pool parameters.
@@ -126,9 +126,9 @@ protected:
     ///
     /// @param addr is the IPv4 prefix of the pool.
     /// @param len is the prefix length.
-    /// @param ignored dummy parameter to provide symmetry between the 
+    /// @param ignored dummy parameter to provide symmetry between the
     /// PoolParser derivations. The V6 derivation requires a third value.
-    /// @return returns a PoolPtr to the new Pool4 object. 
+    /// @return returns a PoolPtr to the new Pool4 object.
     PoolPtr poolMaker (IOAddress &addr, uint32_t len, int32_t) {
         return (PoolPtr(new Pool4(addr, len)));
     }
@@ -137,9 +137,9 @@ protected:
     ///
     /// @param min is the first IPv4 address in the pool.
     /// @param max is the last IPv4 address in the pool.
-    /// @param ignored dummy parameter to provide symmetry between the 
+    /// @param ignored dummy parameter to provide symmetry between the
     /// PoolParser derivations. The V6 derivation requires a third value.
-    /// @return returns a PoolPtr to the new Pool4 object. 
+    /// @return returns a PoolPtr to the new Pool4 object.
     PoolPtr poolMaker (IOAddress &min, IOAddress &max, int32_t) {
         return (PoolPtr(new Pool4(min, max)));
     }
@@ -147,8 +147,8 @@ protected:
 
 /// @brief This class parses a single IPv4 subnet.
 ///
-/// This is the IPv4 derivation of the SubnetConfigParser class and it parses 
-/// the whole subnet definition. It creates parsersfor received configuration 
+/// This is the IPv4 derivation of the SubnetConfigParser class and it parses
+/// the whole subnet definition. It creates parsersfor received configuration
 /// parameters as needed.
 class Subnet4ConfigParser : public SubnetConfigParser {
 public:
@@ -158,7 +158,7 @@ public:
     /// stores global scope parameters, options, option defintions.
     Subnet4ConfigParser(const std::string&)
         :SubnetConfigParser("", globalContext()) {
-    } 
+    }
 
     /// @brief Adds the created subnet to a server's configuration.
     /// @throw throws Unexpected if dynamic cast fails.
@@ -167,7 +167,7 @@ public:
             Subnet4Ptr sub4ptr = boost::dynamic_pointer_cast<Subnet4>(subnet_);
             if (!sub4ptr) {
                 // If we hit this, it is a programming error.
-                isc_throw(Unexpected, 
+                isc_throw(Unexpected,
                           "Invalid cast in Subnet4ConfigParser::commit");
             }
 
@@ -191,13 +191,13 @@ protected:
             (config_id.compare("renew-timer") == 0)  ||
             (config_id.compare("rebind-timer") == 0))  {
             parser = new Uint32Parser(config_id, uint32_values_);
-        } else if ((config_id.compare("subnet") == 0) || 
+        } else if ((config_id.compare("subnet") == 0) ||
                    (config_id.compare("interface") == 0)) {
             parser = new StringParser(config_id, string_values_);
         } else if (config_id.compare("pool") == 0) {
             parser = new Pool4Parser(config_id, pools_);
         } else if (config_id.compare("option-data") == 0) {
-           parser = new OptionDataListParser(config_id, options_, 
+           parser = new OptionDataListParser(config_id, options_,
                                              global_context_,
                                              Dhcp4OptionDataParser::factory);
         } else {
@@ -210,7 +210,7 @@ protected:
 
 
     /// @brief Determines if the given option space name and code describe
-    /// a standard option for the DCHP4 server. 
+    /// a standard option for the DCHP4 server.
     ///
     /// @param option_space is the name of the option space to consider
     /// @param code is the numeric option code to consider
@@ -230,12 +230,12 @@ protected:
     }
 
     /// @brief Issues a DHCP4 server specific warning regarding duplicate subnet
-    /// options. 
-    /// 
+    /// options.
+    ///
     /// @param code is the numeric option code of the duplicate option
-    /// @param addr is the subnet address 
+    /// @param addr is the subnet address
     /// @todo a means to know the correct logger and perhaps a common
-    /// message would allow this method to be emitted by the base class. 
+    /// message would allow this method to be emitted by the base class.
     virtual void duplicate_option_warning(uint32_t code,
                                          isc::asiolink::IOAddress& addr) {
         LOG_WARN(dhcp4_logger, DHCP4_CONFIG_OPTION_DUPLICATE)
@@ -243,10 +243,10 @@ protected:
     }
 
     /// @brief Instantiates the IPv4 Subnet based on a given IPv4 address
-    /// and prefix length.  
-    /// 
+    /// and prefix length.
+    ///
     /// @param addr is IPv4 address of the subnet.
-    /// @param len is the prefix length 
+    /// @param len is the prefix length
     void initSubnet(isc::asiolink::IOAddress addr, uint8_t len) {
         // Get all 'time' parameters using inheritance.
         // If the subnet-specific value is defined then use it, else
@@ -338,32 +338,32 @@ namespace dhcp {
 ///
 /// @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 
+/// @throw NotImplemented if trying to create a parser for unknown
 /// config element
 DhcpConfigParser* createGlobalDhcp4ConfigParser(const std::string& config_id) {
     DhcpConfigParser* parser = NULL;
     if ((config_id.compare("valid-lifetime") == 0)  ||
         (config_id.compare("renew-timer") == 0)  ||
         (config_id.compare("rebind-timer") == 0))  {
-        parser = new Uint32Parser(config_id, 
+        parser = new Uint32Parser(config_id,
                                  globalContext()->uint32_values_);
-    } else if (config_id.compare("interface") == 0) {
+    } else if (config_id.compare("interfaces") == 0) {
         parser = new InterfaceListConfigParser(config_id);
     } else if (config_id.compare("subnet4") == 0) {
         parser = new Subnets4ListConfigParser(config_id);
     } else if (config_id.compare("option-data") == 0) {
-        parser = new OptionDataListParser(config_id, 
-                                          globalContext()->options_, 
+        parser = new OptionDataListParser(config_id,
+                                          globalContext()->options_,
                                           globalContext(),
                                           Dhcp4OptionDataParser::factory);
     } else if (config_id.compare("option-def") == 0) {
-        parser  = new OptionDefListParser(config_id, 
+        parser  = new OptionDefListParser(config_id,
                                           globalContext()->option_defs_);
     } else if (config_id.compare("version") == 0) {
-        parser  = new StringParser(config_id, 
+        parser  = new StringParser(config_id,
                                     globalContext()->string_values_);
     } else if (config_id.compare("lease-database") == 0) {
-        parser = new DbAccessParser(config_id); 
+        parser = new DbAccessParser(config_id);
     } else {
         isc_throw(NotImplemented,
                 "Parser error: Global configuration parameter not supported: "
@@ -384,7 +384,7 @@ configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set) {
     /// @todo: Append most essential info here (like "2 new subnets configured")
     string config_details;
 
-    LOG_DEBUG(dhcp4_logger, DBG_DHCP4_COMMAND, 
+    LOG_DEBUG(dhcp4_logger, DBG_DHCP4_COMMAND,
               DHCP4_CONFIG_START).arg(config_set->str());
 
     // Some of the values specified in the configuration depend on
@@ -397,6 +397,7 @@ configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set) {
     ParserCollection independent_parsers;
     ParserPtr subnet_parser;
     ParserPtr option_parser;
+    ParserPtr iface_parser;
 
     // The subnet parsers implement data inheritance by directly
     // accessing global storage. For this reason the global data
@@ -428,6 +429,11 @@ configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set) {
                 subnet_parser = parser;
             } else if (config_pair.first == "option-data") {
                 option_parser = parser;
+            } else if (config_pair.first == "interfaces") {
+                // The interface parser is independent from any other
+                // parser and can be run here before any other parsers.
+                iface_parser = parser;
+                parser->build(config_pair.second);
             } else {
                 // Those parsers should be started before other
                 // parsers so we can call build straight away.
@@ -483,6 +489,10 @@ configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set) {
             if (subnet_parser) {
                 subnet_parser->commit();
             }
+
+            if (iface_parser) {
+                iface_parser->commit();
+            }
         }
         catch (const isc::Exception& ex) {
             LOG_ERROR(dhcp4_logger, DHCP4_PARSER_COMMIT_FAIL).arg(ex.what());
diff --git a/src/bin/dhcp4/config_parser.h b/src/bin/dhcp4/config_parser.h
index ea84cc6..3af9911 100644
--- a/src/bin/dhcp4/config_parser.h
+++ b/src/bin/dhcp4/config_parser.h
@@ -30,7 +30,7 @@ namespace dhcp {
 
 class Dhcpv4Srv;
 
-/// @brief Configure DHCPv4 server (@c Dhcpv4Srv) with a set of configuration 
+/// @brief Configure DHCPv4 server (@c Dhcpv4Srv) with a set of configuration
 /// values.
 ///
 /// This function parses configuration information stored in @c config_set
@@ -44,7 +44,7 @@ class Dhcpv4Srv;
 /// (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 
+/// 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.
 ///
diff --git a/src/bin/dhcp4/ctrl_dhcp4_srv.cc b/src/bin/dhcp4/ctrl_dhcp4_srv.cc
index eb7d559..11f76da 100644
--- a/src/bin/dhcp4/ctrl_dhcp4_srv.cc
+++ b/src/bin/dhcp4/ctrl_dhcp4_srv.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2013 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,17 +20,17 @@
 #include <config/ccsession.h>
 #include <dhcp/iface_mgr.h>
 #include <dhcpsrv/dhcp_config_parser.h>
+#include <dhcpsrv/cfgmgr.h>
 #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>
+#include <sstream>
 
 using namespace isc::asiolink;
 using namespace isc::cc;
@@ -101,7 +101,27 @@ ControlledDhcpv4Srv::dhcp4ConfigHandler(ConstElementPtr new_config) {
     }
 
     // Configure the server.
-    return (configureDhcp4Server(*server_, merged_config));
+    ConstElementPtr answer = configureDhcp4Server(*server_, merged_config);
+
+    // Check that configuration was successful. If not, do not reopen sockets.
+    int rcode = 0;
+    parseAnswer(rcode, answer);
+    if (rcode != 0) {
+        return (answer);
+    }
+
+    // Configuration may change active interfaces. Therefore, we have to reopen
+    // sockets according to new configuration. This operation is not exception
+    // safe and we really don't want to emit exceptions to the callback caller.
+    // Instead, catch an exception and create appropriate answer.
+    try {
+        server_->openActiveSockets(server_->getPort(), server_->useBroadcast());
+    } catch (std::exception& ex) {
+        std::ostringstream err;
+        err << "failed to open sockets after server reconfiguration: " << ex.what();
+        answer = isc::config::createAnswer(1, err.str());
+    }
+    return (answer);
 }
 
 ConstElementPtr
@@ -121,6 +141,8 @@ ControlledDhcpv4Srv::dhcp4CommandHandler(const string& command, ConstElementPtr
         ConstElementPtr answer = isc::config::createAnswer(0,
                                  "Shutting down.");
         return (answer);
+    } else if (command == "libreload") {
+        // TODO Reload libraries
     }
 
     ConstElementPtr answer = isc::config::createAnswer(1,
@@ -172,8 +194,13 @@ void ControlledDhcpv4Srv::establishSession() {
 
     try {
         configureDhcp4Server(*this, config_session_->getFullConfig());
+        // Configuration may disable or enable interfaces so we have to
+        // reopen sockets according to new configuration.
+        openActiveSockets(getPort(), useBroadcast());
+
     } catch (const DhcpConfigError& ex) {
         LOG_ERROR(dhcp4_logger, DHCP4_CONFIG_LOAD_FAIL).arg(ex.what());
+
     }
 
     /// Integrate the asynchronous I/O model of BIND 10 configuration
@@ -228,6 +255,5 @@ ControlledDhcpv4Srv::execDhcpv4ServerCommand(const std::string& command_id,
     }
 }
 
-
 };
 };
diff --git a/src/bin/dhcp4/ctrl_dhcp4_srv.h b/src/bin/dhcp4/ctrl_dhcp4_srv.h
index 8f14f4b..526d987 100644
--- a/src/bin/dhcp4/ctrl_dhcp4_srv.h
+++ b/src/bin/dhcp4/ctrl_dhcp4_srv.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2013  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
@@ -130,6 +130,7 @@ protected:
     /// when there is a new command or configuration sent over msgq.
     static void sessionReader(void);
 
+
     /// @brief IOService object, used for all ASIO operations.
     isc::asiolink::IOService io_service_;
 
diff --git a/src/bin/dhcp4/dhcp4.dox b/src/bin/dhcp4/dhcp4.dox
index 86ae845..bd490fb 100644
--- a/src/bin/dhcp4/dhcp4.dox
+++ b/src/bin/dhcp4/dhcp4.dox
@@ -79,4 +79,8 @@ See \ref dhcpv6ConfigParser.
 Configuration inheritance in DHCPv4 follows exactly the same logic as its DHCPv6
 counterpart. See \ref dhcpv6ConfigInherit.
 
+ at section dhcpv4Other Other DHCPv4 topics
+
+ For hooks API support in DHCPv4, see @ref dhcpv4Hooks.
+
 */
diff --git a/src/bin/dhcp4/dhcp4.spec b/src/bin/dhcp4/dhcp4.spec
index 59d727e..e940cb7 100644
--- a/src/bin/dhcp4/dhcp4.spec
+++ b/src/bin/dhcp4/dhcp4.spec
@@ -3,16 +3,16 @@
     "module_name": "Dhcp4",
     "module_description": "DHCPv4 server daemon",
     "config_data": [
-      { "item_name": "interface",
+      { "item_name": "interfaces",
         "item_type": "list",
         "item_optional": false,
-        "item_default": [ "all" ],
+        "item_default": [ "*" ],
         "list_item_spec":
         {
           "item_name": "interface_name",
           "item_type": "string",
           "item_optional": false,
-          "item_default": "all"
+          "item_default": "*"
         }
       } ,
 
diff --git a/src/bin/dhcp4/dhcp4_hooks.dox b/src/bin/dhcp4/dhcp4_hooks.dox
new file mode 100644
index 0000000..44704a8
--- /dev/null
+++ b/src/bin/dhcp4/dhcp4_hooks.dox
@@ -0,0 +1,138 @@
+// Copyright (C) 2013  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.
+
+/**
+ @page dhcpv4Hooks The Hooks API for the DHCPv4 Server
+
+ @section dhcpv4HooksIntroduction Introduction
+ BIND10 features an API (the "Hooks" API) that allows user-written code to
+ be integrated into BIND 10 and called at specific points in its processing.
+ An overview of the API and a tutorial for writing such code can be found in
+ the @ref hooksDevelopersGuide.  Information for BIND 10 maintainers can be
+ found in the @ref hooksComponentDeveloperGuide.
+
+ This manual is more specialised and is aimed at developers of hook
+ code for the DHCPv4 server. It describes each hook point, what the callouts
+ attached to the hook are able to do, and the arguments passed to the
+ callouts.  Each entry in this manual has the following information:
+
+ - Name of the hook point.
+ - Arguments for the callout.  As well as the argument name and data type, the
+   information includes the direction, which can be one of:
+   - @b in - the server passes values to the callout but ignored any data
+     returned.
+   - @b out - the callout is expected to set this value.
+   - <b>in/out</b> - the server passes a value to the callout and uses whatever
+     value the callout sends back.  Note that the callout may choose not to
+     do any modification, in which case the server will use whatever value
+     it sent to the callout.
+ - Description of the hook. This explains where in the processing the hook
+   is located, the possible actions a callout attached to this hook could take,
+   and a description of the data passed to the callouts.
+ - Skip flag action: the action taken by the server if a callout chooses to set
+    the "skip" flag.
+
+ at section dhcpv4HooksHookPoints Hooks in the DHCPv4 Server
+
+The following list is ordered by appearance of specific hook points during
+packet processing. Hook points that are not specific to packet processing
+(e.g. lease expiration) will be added to the end of this list.
+
+ @subsection dhcpv4HooksPkt4Receive pkt4_receive
+
+ - @b Arguments:
+   - name: @b query4, type: isc::dhcp::Pkt4Ptr, direction: <b>in/out</b>
+
+ - @b Description: this callout is executed when an incoming DHCPv4
+   packet is received and its content is parsed. The sole argument -
+   query4 - contains a pointer to an isc::dhcp::Pkt4 object that contains
+   all information regarding incoming packet, including its source and
+   destination addresses, interface over which it was received, a list
+   of all options present within and relay information.  All fields of
+   the Pkt4 object can be modified at this time, except data_. (data_
+   contains the incoming packet as raw buffer. By the time this hook is
+   reached, that information has already parsed and is available though
+   other fields in the Pkt4 object.  For this reason, it doesn't make
+   sense to modify it.)
+
+ - <b>Skip flag action</b>: If any callout sets the skip flag, the server will
+   drop the packet and start processing the next one.  The reason for the drop
+   will be logged if logging is set to the appropriate debug level.
+
+ at subsection dhcpv4HooksSubnet4Select subnet4_select
+
+ - @b Arguments:
+   - name: @b query4, type: isc::dhcp::Pkt4Ptr, direction: <b>in/out</b>
+   - name: @b subnet4, type: isc::dhcp::Subnet4Ptr, direction: <b>in/out</b>
+   - name: @b subnet4collection, type: const isc::dhcp::Subnet4Collection *, direction: <b>in</b>
+
+ - @b Description: this callout is executed when a subnet is being
+   selected for the incoming packet. All parameters and addresses
+   will be assigned from that subnet. A callout can select a
+   different subnet if it wishes so, the list of all subnets currently
+   configured being provided as 'subnet4collection'. The list itself must
+   not be modified.
+
+ - <b>Skip flag action</b>: If any callout installed on 'subnet4_select'
+   sets the skip flag, the server will not select any subnet. Packet processing
+   will continue, but will be severely limited (i.e. only global options
+   will be assigned).
+
+ at subsection dhcpv4HooksLeaseSelect lease4_select
+
+ - @b Arguments:
+   - name: @b subnet4, type: isc::dhcp::Subnet4Ptr, direction: <b>in</b>
+   - name: @b fake_allocation, type: bool, direction: <b>in</b>
+   - name: @b lease4, type: isc::dhcp::Lease4Ptr, direction: <b>in/out</b>
+
+ - @b Description: this callout is executed after the server engine
+   has selected a lease for client's request but before the lease
+   has been inserted into the database. Any modifications made to the
+   isc::dhcp::Lease4 object will be stored in the lease's record in the
+   database. The callout should make sure that any modifications are
+   sanity checked as the server will use that data as is with no further
+   checking.\n\n The server processes lease requests for DISCOVER and
+   REQUEST in a very similar way. The only major difference is that
+   for DISCOVER the lease is just selected, but not inserted into
+   the database.  It is possible to distinguish between DISCOVER and
+   REQUEST by checking value of the fake_allocation flag: a value of true
+   means that the lease won't be inserted into the database (DISCOVER),
+   a value of false means that it will (REQUEST).
+
+ - <b>Skip flag action</b>: If any callout installed on 'lease4_select'
+   sets the skip flag, the server will not assign any lease. Packet
+   processing will continue, but client will not get an address.
+
+ at subsection dhcpv4HooksPkt4Send pkt4_send
+
+ - @b Arguments:
+   - name: @b response4, type: isc::dhcp::Pkt4Ptr, direction: <b>in/out</b>
+
+ - @b Description: this callout is executed when server's response
+   is about to be send back to the client. The sole argument - response4 -
+   contains a pointer to an isc::dhcp::Pkt4 object that contains the
+   packet, with set source and destination addresses, interface over which
+   it will be send, list of all options and relay information.  All fields
+   of the Pkt4 object can be modified at this time, except bufferOut_.
+   (This is scratch space used for constructing the packet after all
+   pkt4_send callouts are complete, so any changes to that field will
+   be overwritten.)
+
+ - <b>Skip flag action</b>: if any callout sets the skip flag, the server
+   will drop this response packet. However, the original request packet
+   from a client was processed, so server's state was most likely changed
+   (e.g. lease was allocated). Setting this flag merely stops the change
+   being communicated to the client.
+
+*/
diff --git a/src/bin/dhcp4/dhcp4_log.h b/src/bin/dhcp4/dhcp4_log.h
index 07d009a..54dbb40 100644
--- a/src/bin/dhcp4/dhcp4_log.h
+++ b/src/bin/dhcp4/dhcp4_log.h
@@ -38,6 +38,9 @@ const int DBG_DHCP4_COMMAND = DBGLVL_COMMAND;
 // Trace basic operations within the code.
 const int DBG_DHCP4_BASIC = DBGLVL_TRACE_BASIC;
 
+// Trace hook related operations
+const int DBG_DHCP4_HOOKS = DBGLVL_TRACE_BASIC;
+
 // Trace detailed operations, including errors raised when processing invalid
 // packets.  (These are not logged at severities of WARN or higher for fear
 // that a set of deliberately invalid packets set to the server could overwhelm
diff --git a/src/bin/dhcp4/dhcp4_messages.mes b/src/bin/dhcp4/dhcp4_messages.mes
index 8b3e255..5af51f9 100644
--- a/src/bin/dhcp4/dhcp4_messages.mes
+++ b/src/bin/dhcp4/dhcp4_messages.mes
@@ -14,6 +14,11 @@
 
 $NAMESPACE isc::dhcp
 
+% DHCP4_ACTIVATE_INTERFACE activating interface %1
+This message is printed when DHCPv4 server enabled an interface to be used
+to receive DHCPv4 traffic. IPv4 socket on this interface will be opened once
+Interface Manager starts up procedure of opening sockets.
+
 % DHCP4_CCSESSION_STARTED control channel session started on socket %1
 A debug message issued during startup after the IPv4 DHCP server has
 successfully established a session with the BIND 10 control channel.
@@ -37,7 +42,7 @@ 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_NEW_SUBNET A new subnet has been added to configuration: %1
+% 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.
 
@@ -60,6 +65,30 @@ This informational message is printed every time DHCPv4 server is started
 and gives both the type and name of the database being used to store
 lease and other information.
 
+% DHCP4_DEACTIVATE_INTERFACE deactivate interface %1
+This message is printed when DHCPv4 server disables an interface from being
+used to receive DHCPv4 traffic. Sockets on this interface will not be opened
+by the Interface Manager until interface is enabled.
+
+% DHCP4_HOOK_PACKET_RCVD_SKIP received DHCPv4 packet was dropped, because a callout set the skip flag.
+This debug message is printed when a callout installed on the pkt4_receive
+hook point sets the skip flag. For this particular hook point, the
+setting of the flag instructs the server to drop the packet.
+
+% DHCP4_HOOK_PACKET_SEND_SKIP prepared DHCPv6 response was not sent, because a callout set skip flag.
+This debug message is printed when a callout installed on the pkt4_send
+hook point sets the skip flag. For this particular hook point, the setting
+of the flag instructs the server to drop the packet. This means that
+the client will not get any response, even though the server processed
+client's request and acted on it (e.g. possibly allocated a lease).
+
+% DHCP4_HOOK_SUBNET4_SELECT_SKIP no subnet was selected, because a callout set skip flag.
+This debug message is printed when a callout installed on the
+subnet4_select hook point sets the skip flag. For this particular hook
+point, the setting of the flag instructs the server not to choose a
+subnet, an action that severely limits further processing; the server
+will be only able to offer global options - no addresses will be assigned.
+
 % DHCP4_LEASE_ADVERT lease %1 advertised (client client-id %2, hwaddr %3)
 This debug message indicates that the server successfully advertised
 a lease. It is up to the client to choose one server out of othe advertised
@@ -82,6 +111,11 @@ specified client after receiving a REQUEST message from it.  There are many
 possible reasons for such a failure. Additional messages will indicate the
 reason.
 
+% DHCP4_NO_SOCKETS_OPEN no interface configured to listen to DHCP traffic
+This warning message is issued when current server configuration specifies
+no interfaces that server should listen on, or specified interfaces are not
+configured to receive the traffic.
+
 % 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.cc b/src/bin/dhcp4/dhcp4_srv.cc
index 8aa913c..35ef7e6 100644
--- a/src/bin/dhcp4/dhcp4_srv.cc
+++ b/src/bin/dhcp4/dhcp4_srv.cc
@@ -30,6 +30,8 @@
 #include <dhcpsrv/subnet.h>
 #include <dhcpsrv/utils.h>
 #include <dhcpsrv/addr_utilities.h>
+#include <hooks/hooks_manager.h>
+#include <hooks/callout_handle.h>
 
 #include <boost/algorithm/string/erase.hpp>
 
@@ -39,9 +41,30 @@
 using namespace isc;
 using namespace isc::asiolink;
 using namespace isc::dhcp;
+using namespace isc::hooks;
 using namespace isc::log;
 using namespace std;
 
+/// Structure that holds registered hook indexes
+struct Dhcp6Hooks {
+    int hook_index_pkt4_receive_;   ///< index for "pkt4_receive" hook point
+    int hook_index_subnet4_select_; ///< index for "subnet4_select" hook point
+    int hook_index_pkt4_send_;      ///< index for "pkt4_send" hook point
+
+    /// Constructor that registers hook points for DHCPv6 engine
+    Dhcp6Hooks() {
+        hook_index_pkt4_receive_   = HooksManager::registerHook("pkt4_receive");
+        hook_index_subnet4_select_ = HooksManager::registerHook("subnet4_select");
+        hook_index_pkt4_send_      = HooksManager::registerHook("pkt4_send");
+    }
+};
+
+// Declare a Hooks object. As this is outside any function or method, it
+// will be instantiated (and the constructor run) when the module is loaded.
+// As a result, the hook indexes will be defined before any method in this
+// module is called.
+Dhcp6Hooks Hooks;
+
 namespace isc {
 namespace dhcp {
 
@@ -58,7 +81,11 @@ static const char* SERVER_ID_FILE = "b10-dhcp4-serverid";
 // grants those options and a single, fixed, hardcoded lease.
 
 Dhcpv4Srv::Dhcpv4Srv(uint16_t port, const char* dbconfig, const bool use_bcast,
-                     const bool direct_response_desired) {
+                     const bool direct_response_desired)
+: serverid_(), shutdown_(true), alloc_engine_(), port_(port), 
+    use_bcast_(use_bcast), hook_index_pkt4_receive_(-1), 
+    hook_index_subnet4_select_(-1), hook_index_pkt4_send_(-1) {
+
     LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_OPEN_SOCKET).arg(port);
     try {
         // First call to instance() will create IfaceMgr (it's a singleton)
@@ -73,7 +100,7 @@ Dhcpv4Srv::Dhcpv4Srv(uint16_t port, const char* dbconfig, const bool use_bcast,
         if (port) {
             // open sockets only if port is non-zero. Port 0 is used
             // for non-socket related testing.
-            IfaceMgr::instance().openSockets4(port, use_bcast);
+            IfaceMgr::instance().openSockets4(port_, use_bcast_);
         }
 
         string srvid_file = CfgMgr::instance().getDataDir() + "/" + string(SERVER_ID_FILE);
@@ -103,6 +130,13 @@ Dhcpv4Srv::Dhcpv4Srv(uint16_t port, const char* dbconfig, const bool use_bcast,
         // Instantiate allocation engine
         alloc_engine_.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100));
 
+        // Register hook points
+        hook_index_pkt4_receive_   = Hooks.hook_index_pkt4_receive_;
+        hook_index_subnet4_select_ = Hooks.hook_index_subnet4_select_;
+        hook_index_pkt4_send_      = Hooks.hook_index_pkt4_send_;
+
+        /// @todo call loadLibraries() when handling configuration changes
+
     } catch (const std::exception &e) {
         LOG_ERROR(dhcp4_logger, DHCP4_SRV_CONSTRUCT_ERROR).arg(e.what());
         shutdown_ = true;
@@ -122,6 +156,16 @@ Dhcpv4Srv::shutdown() {
     shutdown_ = true;
 }
 
+Pkt4Ptr
+Dhcpv4Srv::receivePacket(int timeout) {
+    return (IfaceMgr::instance().receive4(timeout));
+}
+
+void
+Dhcpv4Srv::sendPacket(const Pkt4Ptr& packet) {
+    IfaceMgr::instance().send(packet);
+}
+
 bool
 Dhcpv4Srv::run() {
     while (!shutdown_) {
@@ -134,7 +178,7 @@ Dhcpv4Srv::run() {
         Pkt4Ptr rsp;
 
         try {
-            query = IfaceMgr::instance().receive4(timeout);
+            query = receivePacket(timeout);
         } catch (const std::exception& e) {
             LOG_ERROR(dhcp4_logger, DHCP4_PACKET_RECEIVE_FAIL).arg(e.what());
         }
@@ -154,8 +198,34 @@ Dhcpv4Srv::run() {
                       .arg(query->getType())
                       .arg(query->getIface());
             LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL_DATA, DHCP4_QUERY_DATA)
+                      .arg(static_cast<int>(query->getType()))
                       .arg(query->toText());
 
+            // Let's execute all callouts registered for packet_received
+            if (HooksManager::calloutsPresent(hook_index_pkt4_receive_)) {
+                CalloutHandlePtr callout_handle = getCalloutHandle(query);
+
+                // Delete previously set arguments
+                callout_handle->deleteAllArguments();
+
+                // Pass incoming packet as argument
+                callout_handle->setArgument("query4", query);
+
+                // Call callouts
+                HooksManager::callCallouts(hook_index_pkt4_receive_,
+                                           *callout_handle);
+
+                // Callouts decided to skip the next processing step. The next
+                // processing step would to process the packet, so skip at this
+                // stage means drop.
+                if (callout_handle->getSkip()) {
+                    LOG_DEBUG(dhcp4_logger, DBG_DHCP4_HOOKS, DHCP4_HOOK_PACKET_RCVD_SKIP);
+                    continue;
+                }
+
+                callout_handle->getArgument("query4", query);
+            }
+
             try {
                 switch (query->getType()) {
                 case DHCPDISCOVER:
@@ -220,13 +290,39 @@ Dhcpv4Srv::run() {
                 rsp->setIface(query->getIface());
                 rsp->setIndex(query->getIndex());
 
+                // Execute all callouts registered for packet6_send
+                if (HooksManager::calloutsPresent(hook_index_pkt4_send_)) {
+                    CalloutHandlePtr callout_handle = getCalloutHandle(query);
+
+                    // Delete all previous arguments
+                    callout_handle->deleteAllArguments();
+
+                    // Clear skip flag if it was set in previous callouts
+                    callout_handle->setSkip(false);
+
+                    // Set our response
+                    callout_handle->setArgument("response4", rsp);
+
+                    // Call all installed callouts
+                    HooksManager::callCallouts(hook_index_pkt4_send_,
+                                               *callout_handle);
+
+                    // Callouts decided to skip the next processing step. The next
+                    // processing step would to send the packet, so skip at this
+                    // stage means "drop response".
+                    if (callout_handle->getSkip()) {
+                        LOG_DEBUG(dhcp4_logger, DBG_DHCP4_HOOKS, DHCP4_HOOK_PACKET_SEND_SKIP);
+                        continue;
+                    }
+                }
+
                 LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL_DATA,
                           DHCP4_RESPONSE_DATA)
                           .arg(rsp->getType()).arg(rsp->toText());
 
                 if (rsp->pack()) {
                     try {
-                        IfaceMgr::instance().send(rsp);
+                        sendPacket(rsp);
                     } catch (const std::exception& e) {
                         LOG_ERROR(dhcp4_logger, DHCP4_PACKET_SEND_FAIL).arg(e.what());
                     }
@@ -514,12 +610,15 @@ Dhcpv4Srv::assignLease(const Pkt4Ptr& question, Pkt4Ptr& answer) {
     // allocation.
     bool fake_allocation = (question->getType() == DHCPDISCOVER);
 
+    CalloutHandlePtr callout_handle = getCalloutHandle(question);
+
     // Use allocation engine to pick a lease for this client. Allocation engine
     // will try to honour the hint, but it is just a hint - some other address
     // may be used instead. If fake_allocation is set to false, the lease will
     // be inserted into the LeaseMgr as well.
     Lease4Ptr lease = alloc_engine_->allocateAddress4(subnet, client_id, hwaddr,
-                                                      hint, fake_allocation);
+                                                      hint, fake_allocation,
+                                                      callout_handle);
 
     if (lease) {
         // We have a lease! Let's set it in the packet and send it back to
@@ -632,6 +731,9 @@ Dhcpv4Srv::getNetmaskOption(const Subnet4Ptr& subnet) {
 
 Pkt4Ptr
 Dhcpv4Srv::processDiscover(Pkt4Ptr& discover) {
+
+    sanityCheck(discover, FORBIDDEN);
+
     Pkt4Ptr offer = Pkt4Ptr
         (new Pkt4(DHCPOFFER, discover->getTransid()));
 
@@ -782,17 +884,50 @@ Dhcpv4Srv::serverReceivedPacketName(uint8_t type) {
 Subnet4Ptr
 Dhcpv4Srv::selectSubnet(const Pkt4Ptr& question) {
 
+    Subnet4Ptr subnet;
     // Is this relayed message?
     IOAddress relay = question->getGiaddr();
-    if (relay.toText() == "0.0.0.0") {
+    static const IOAddress notset("0.0.0.0");
 
+    if (relay != notset) {
         // Yes: Use relay address to select subnet
-        return (CfgMgr::instance().getSubnet4(relay));
+        subnet = CfgMgr::instance().getSubnet4(relay);
     } else {
 
         // No: Use client's address to select subnet
-        return (CfgMgr::instance().getSubnet4(question->getRemoteAddr()));
+        subnet = CfgMgr::instance().getSubnet4(question->getRemoteAddr());
     }
+
+    /// @todo Implement getSubnet4(interface-name)
+
+    // Let's execute all callouts registered for packet_received
+    if (HooksManager::calloutsPresent(hook_index_subnet4_select_)) {
+        CalloutHandlePtr callout_handle = getCalloutHandle(question);
+
+        // We're reusing callout_handle from previous calls
+        callout_handle->deleteAllArguments();
+
+        // Set new arguments
+        callout_handle->setArgument("query4", question);
+        callout_handle->setArgument("subnet4", subnet);
+        callout_handle->setArgument("subnet4collection", CfgMgr::instance().getSubnets4());
+
+        // Call user (and server-side) callouts
+        HooksManager::callCallouts(hook_index_subnet4_select_, *callout_handle);
+
+        // Callouts decided to skip this step. This means that no subnet will be
+        // selected. Packet processing will continue, but it will be severly limited
+        // (i.e. only global options will be assigned)
+        if (callout_handle->getSkip()) {
+            LOG_DEBUG(dhcp4_logger, DBG_DHCP4_HOOKS, DHCP4_HOOK_SUBNET4_SELECT_SKIP);
+            return (Subnet4Ptr());
+        }
+
+        // Use whatever subnet was specified by the callout
+        callout_handle->getArgument("subnet4", subnet);
+    }
+
+    return (subnet);
 }
 
 void
@@ -818,7 +953,94 @@ Dhcpv4Srv::sanityCheck(const Pkt4Ptr& pkt, RequirementLevel serverid) {
         // do nothing here
         ;
     }
+
+    // If there is HWAddress set and it is non-empty, then we're good
+    if (pkt->getHWAddr() && !pkt->getHWAddr()->hwaddr_.empty()) {
+        return;
+    }
+
+    // There has to be something to uniquely identify the client:
+    // either non-zero MAC address or client-id option present (or both)
+    OptionPtr client_id = pkt->getOption(DHO_DHCP_CLIENT_IDENTIFIER);
+
+    // If there's no client-id (or a useless one is provided, i.e. 0 length)
+    if (!client_id || client_id->len() == client_id->getHeaderLen()) {
+        isc_throw(RFCViolation, "Missing or useless client-id and no HW address "
+                  " provided in message "
+                  << serverReceivedPacketName(pkt->getType()));
+    }
 }
 
+isc::hooks::CalloutHandlePtr Dhcpv4Srv::getCalloutHandle(const Pkt4Ptr& pkt) {
+    // This method returns a CalloutHandle for a given packet. It is guaranteed
+    // to return the same callout_handle (so user library contexts are
+    // preserved). This method works well if the server processes one packet
+    // at a time. Once the server architecture is extended to cover parallel
+    // packets processing (e.g. delayed-ack, some form of buffering etc.), this
+    // method has to be extended (e.g. store callouts in a map and use pkt as
+    // a key). Additional code would be required to release the callout handle
+    // once the server finished processing.
+
+    CalloutHandlePtr callout_handle;
+    static Pkt4Ptr old_pointer;
+
+    if (!callout_handle ||
+        old_pointer != pkt) {
+        // This is the first packet or a different packet than previously
+        // passed to getCalloutHandle()
+
+        // Remember the pointer to this packet
+        old_pointer = pkt;
+
+        callout_handle = HooksManager::createCalloutHandle();
+    }
+
+    return (callout_handle);
+}
+
+void
+Dhcpv4Srv::openActiveSockets(const uint16_t port,
+                             const bool use_bcast) {
+    IfaceMgr::instance().closeSockets();
+
+    // Get the reference to the collection of interfaces. This reference should
+    // be valid as long as the program is run because IfaceMgr is a singleton.
+    // Therefore we can safely iterate over instances of all interfaces and
+    // modify their flags. Here we modify flags which indicate whether socket
+    // should be open for a particular interface or not.
+    const IfaceMgr::IfaceCollection& ifaces = IfaceMgr::instance().getIfaces();
+    for (IfaceMgr::IfaceCollection::const_iterator iface = ifaces.begin();
+         iface != ifaces.end(); ++iface) {
+        Iface* iface_ptr = IfaceMgr::instance().getIface(iface->getName());
+        if (iface_ptr == NULL) {
+            isc_throw(isc::Unexpected, "Interface Manager returned NULL"
+                      << " instance of the interface when DHCPv4 server was"
+                      << " trying to reopen sockets after reconfiguration");
+        }
+        if (CfgMgr::instance().isActiveIface(iface->getName())) {
+            iface_ptr->inactive4_ = false;
+            LOG_INFO(dhcp4_logger, DHCP4_ACTIVATE_INTERFACE)
+                .arg(iface->getFullName());
+
+        } else {
+            // For deactivating interface, it should be sufficient to log it
+            // on the debug level because it is more useful to know what
+            // interface is activated which is logged on the info level.
+            LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC,
+                      DHCP4_DEACTIVATE_INTERFACE).arg(iface->getName());
+            iface_ptr->inactive4_ = true;
+
+        }
+    }
+    // Let's reopen active sockets. openSockets4 will check internally whether
+    // sockets are marked active or inactive.
+    // @todo Optimization: we should not reopen all sockets but rather select
+    // those that have been affected by the new configuration.
+    if (!IfaceMgr::instance().openSockets4(port, use_bcast)) {
+        LOG_WARN(dhcp4_logger, DHCP4_NO_SOCKETS_OPEN);
+    }
+}
+
+
 }   // namespace dhcp
 }   // namespace isc
diff --git a/src/bin/dhcp4/dhcp4_srv.h b/src/bin/dhcp4/dhcp4_srv.h
index f98233c..432327c 100644
--- a/src/bin/dhcp4/dhcp4_srv.h
+++ b/src/bin/dhcp4/dhcp4_srv.h
@@ -20,6 +20,7 @@
 #include <dhcp/option.h>
 #include <dhcpsrv/subnet.h>
 #include <dhcpsrv/alloc_engine.h>
+#include <hooks/callout_handle.h>
 
 #include <boost/noncopyable.hpp>
 
@@ -79,7 +80,7 @@ public:
               const bool direct_response_desired = true);
 
     /// @brief Destructor. Used during DHCPv4 service shutdown.
-    ~Dhcpv4Srv();
+    virtual ~Dhcpv4Srv();
 
     /// @brief Main server processing loop.
     ///
@@ -113,6 +114,46 @@ public:
     ///         be freed by the caller.
     static const char* serverReceivedPacketName(uint8_t type);
 
+    ///
+    /// @name Public accessors returning values required to (re)open sockets.
+    ///
+    /// These accessors must be public because sockets are reopened from the
+    /// static configuration callback handler. This callback handler invokes
+    /// @c ControlledDhcpv4Srv::openActiveSockets which requires parameters
+    /// which has to be retrieved from the @c ControlledDhcpv4Srv object.
+    /// They are retrieved using these public functions
+    //@{
+    ///
+    /// @brief Get UDP port on which server should listen.
+    ///
+    /// Typically, server listens on UDP port number 67. Other ports are used
+    /// for testing purposes only.
+    ///
+    /// @return UDP port on which server should listen.
+    uint16_t getPort() const {
+        return (port_);
+    }
+
+    /// @brief Return bool value indicating that broadcast flags should be set
+    /// on sockets.
+    ///
+    /// @return A bool value indicating that broadcast should be used (if true).
+    bool useBroadcast() const {
+        return (use_bcast_);
+    }
+    //@}
+
+    /// @brief Open sockets which are marked as active in @c CfgMgr.
+    ///
+    /// This function reopens sockets according to the current settings in the
+    /// Configuration Manager. It holds the list of the interfaces which server
+    /// should listen on. This function will open sockets on these interfaces
+    /// only. This function is not exception safe.
+    ///
+    /// @param port UDP port on which server should listen.
+    /// @param use_bcast should broadcast flags be set on the sockets.
+    static void openActiveSockets(const uint16_t port, const bool use_bcast);
+
 protected:
 
     /// @brief verifies if specified packet meets RFC requirements
@@ -296,6 +337,18 @@ protected:
     /// initiate server shutdown procedure.
     volatile bool shutdown_;
 
+    /// @brief dummy wrapper around IfaceMgr::receive4
+    ///
+    /// This method is useful for testing purposes, where its replacement
+    /// simulates reception of a packet. For that purpose it is protected.
+    virtual Pkt4Ptr receivePacket(int timeout);
+
+    /// @brief dummy wrapper around IfaceMgr::send()
+    ///
+    /// This method is useful for testing purposes, where its replacement
+    /// simulates transmission of a packet. For that purpose it is protected.
+    virtual void sendPacket(const Pkt4Ptr& pkt);
+
 private:
 
     /// @brief Constructs netmask option based on subnet4
@@ -310,6 +363,20 @@ private:
     /// during normal operation (e.g. to use different allocators)
     boost::shared_ptr<AllocEngine> alloc_engine_;
 
+    uint16_t port_;  ///< UDP port number on which server listens.
+    bool use_bcast_; ///< Should broadcast be enabled on sockets (if true).
+
+    /// @brief returns callout handle for specified packet
+    ///
+    /// @param pkt packet for which the handle should be returned
+    ///
+    /// @return a callout handle to be used in hooks related to said packet
+    isc::hooks::CalloutHandlePtr getCalloutHandle(const Pkt4Ptr& pkt);
+
+    /// Indexes for registered hook points
+    int hook_index_pkt4_receive_;
+    int hook_index_subnet4_select_;
+    int hook_index_pkt4_send_;
 };
 
 }; // namespace isc::dhcp
diff --git a/src/bin/dhcp4/tests/Makefile.am b/src/bin/dhcp4/tests/Makefile.am
index 3e10c3b..d3c7171 100644
--- a/src/bin/dhcp4/tests/Makefile.am
+++ b/src/bin/dhcp4/tests/Makefile.am
@@ -71,6 +71,7 @@ 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/util/libb10-util.la
+dhcp4_unittests_LDADD += $(top_builddir)/src/lib/hooks/libb10-hooks.la
 endif
 
 noinst_PROGRAMS = $(TESTS)
diff --git a/src/bin/dhcp4/tests/config_parser_unittest.cc b/src/bin/dhcp4/tests/config_parser_unittest.cc
index 32770b3..05c951d 100644
--- a/src/bin/dhcp4/tests/config_parser_unittest.cc
+++ b/src/bin/dhcp4/tests/config_parser_unittest.cc
@@ -51,11 +51,12 @@ public:
         // deal with sockets here, just check if configuration handling
         // is sane.
         srv_.reset(new Dhcpv4Srv(0));
+        CfgMgr::instance().deleteActiveIfaces();
     }
 
     // Checks if global parameter of name have expected_value
     void checkGlobalUint32(string name, uint32_t expected_value) {
-        const Uint32StoragePtr uint32_defaults = 
+        const Uint32StoragePtr uint32_defaults =
                                         globalContext()->uint32_values_;
         try {
             uint32_t actual_value = uint32_defaults->getParam(name);
@@ -138,7 +139,7 @@ public:
     /// describing an option.
     std::string createConfigWithOption(const std::map<std::string, std::string>& params) {
         std::ostringstream stream;
-        stream << "{ \"interface\": [ \"all\" ],"
+        stream << "{ \"interfaces\": [ \"*\" ],"
             "\"rebind-timer\": 2000, "
             "\"renew-timer\": 1000, "
             "\"subnet4\": [ { "
@@ -245,7 +246,7 @@ public:
     void resetConfiguration() {
         ConstElementPtr status;
 
-        string config = "{ \"interface\": [ \"all\" ],"
+        string config = "{ \"interfaces\": [ \"*\" ],"
             "\"rebind-timer\": 2000, "
             "\"renew-timer\": 1000, "
             "\"valid-lifetime\": 4000, "
@@ -322,7 +323,7 @@ TEST_F(Dhcp4ParserTest, emptySubnet) {
     ConstElementPtr status;
 
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_,
-                    Element::fromJSON("{ \"interface\": [ \"all\" ],"
+                    Element::fromJSON("{ \"interfaces\": [ \"*\" ],"
                                       "\"rebind-timer\": 2000, "
                                       "\"renew-timer\": 1000, "
                                       "\"subnet4\": [  ], "
@@ -342,7 +343,7 @@ TEST_F(Dhcp4ParserTest, subnetGlobalDefaults) {
 
     ConstElementPtr status;
 
-    string config = "{ \"interface\": [ \"all\" ],"
+    string config = "{ \"interfaces\": [ \"*\" ],"
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet4\": [ { "
@@ -372,7 +373,7 @@ TEST_F(Dhcp4ParserTest, subnetLocal) {
 
     ConstElementPtr status;
 
-    string config = "{ \"interface\": [ \"all\" ],"
+    string config = "{ \"interfaces\": [ \"*\" ],"
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet4\": [ { "
@@ -403,7 +404,7 @@ TEST_F(Dhcp4ParserTest, poolOutOfSubnet) {
 
     ConstElementPtr status;
 
-    string config = "{ \"interface\": [ \"all\" ],"
+    string config = "{ \"interfaces\": [ \"*\" ],"
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet4\": [ { "
@@ -427,7 +428,7 @@ TEST_F(Dhcp4ParserTest, poolPrefixLen) {
 
     ConstElementPtr status;
 
-    string config = "{ \"interface\": [ \"all\" ],"
+    string config = "{ \"interfaces\": [ \"*\" ],"
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet4\": [ { "
@@ -949,7 +950,7 @@ TEST_F(Dhcp4ParserTest, optionStandardDefOverride) {
 // configuration does not include options configuration.
 TEST_F(Dhcp4ParserTest, optionDataDefaults) {
     ConstElementPtr x;
-    string config = "{ \"interface\": [ \"all\" ],"
+    string config = "{ \"interfaces\": [ \"*\" ],"
         "\"rebind-timer\": 2000,"
         "\"renew-timer\": 1000,"
         "\"option-data\": [ {"
@@ -1022,7 +1023,7 @@ TEST_F(Dhcp4ParserTest, optionDataTwoSpaces) {
     // The definition is not required for the option that
     // belongs to the 'dhcp4' option space as it is the
     // standard option.
-    string config = "{ \"interface\": [ \"all\" ],"
+    string config = "{ \"interfaces\": [ \"*\" ],"
         "\"rebind-timer\": 2000,"
         "\"renew-timer\": 1000,"
         "\"option-data\": [ {"
@@ -1100,7 +1101,7 @@ TEST_F(Dhcp4ParserTest, optionDataEncapsulate) {
     // at the very end (when all other parameters are configured).
 
     // Starting stage 1. Configure sub-options and their definitions.
-    string config = "{ \"interface\": [ \"all\" ],"
+    string config = "{ \"interfaces\": [ \"*\" ],"
         "\"rebind-timer\": 2000,"
         "\"renew-timer\": 1000,"
         "\"option-data\": [ {"
@@ -1149,7 +1150,7 @@ TEST_F(Dhcp4ParserTest, optionDataEncapsulate) {
     // the configuration from the stage 2 is repeated because BIND
     // configuration manager sends whole configuration for the lists
     // where at least one element is being modified or added.
-    config = "{ \"interface\": [ \"all\" ],"
+    config = "{ \"interfaces\": [ \"*\" ],"
         "\"rebind-timer\": 2000,"
         "\"renew-timer\": 1000,"
         "\"option-data\": [ {"
@@ -1245,7 +1246,7 @@ TEST_F(Dhcp4ParserTest, optionDataEncapsulate) {
 // option setting.
 TEST_F(Dhcp4ParserTest, optionDataInSingleSubnet) {
     ConstElementPtr x;
-    string config = "{ \"interface\": [ \"all\" ],"
+    string config = "{ \"interfaces\": [ \"*\" ],"
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"option-data\": [ {"
@@ -1317,7 +1318,7 @@ TEST_F(Dhcp4ParserTest, optionDataInSingleSubnet) {
 // for multiple subnets.
 TEST_F(Dhcp4ParserTest, optionDataInMultipleSubnets) {
     ConstElementPtr x;
-    string config = "{ \"interface\": [ \"all\" ],"
+    string config = "{ \"interfaces\": [ \"*\" ],"
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet4\": [ { "
@@ -1597,7 +1598,7 @@ TEST_F(Dhcp4ParserTest, stdOptionDataEncapsulate) {
     // In the first stahe we create definitions of suboptions
     // that we will add to the base option.
     // Let's create some dummy options: foo and foo2.
-    string config = "{ \"interface\": [ \"all\" ],"
+    string config = "{ \"interfaces\": [ \"*\" ],"
         "\"rebind-timer\": 2000,"
         "\"renew-timer\": 1000,"
         "\"option-data\": [ {"
@@ -1650,7 +1651,7 @@ TEST_F(Dhcp4ParserTest, stdOptionDataEncapsulate) {
     // We add our dummy options to this option space and thus
     // they should be included as sub-options in the 'vendor-opts'
     // option.
-    config = "{ \"interface\": [ \"all\" ],"
+    config = "{ \"interfaces\": [ \"*\" ],"
         "\"rebind-timer\": 2000,"
         "\"renew-timer\": 1000,"
         "\"option-data\": [ {"
@@ -1749,5 +1750,69 @@ TEST_F(Dhcp4ParserTest, stdOptionDataEncapsulate) {
     EXPECT_FALSE(desc.option->getOption(3));
 }
 
+// This test verifies that it is possible to select subset of interfaces
+// on which server should listen.
+TEST_F(Dhcp4ParserTest, selectedInterfaces) {
+    ConstElementPtr x;
+    string config = "{ \"interfaces\": [ \"eth0\", \"eth1\" ],"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"valid-lifetime\": 4000 }";
 
-};
+    ElementPtr json = Element::fromJSON(config);
+
+    ConstElementPtr status;
+
+    // Make sure the config manager is clean and there is no hanging
+    // interface configuration.
+    ASSERT_FALSE(CfgMgr::instance().isActiveIface("eth0"));
+    ASSERT_FALSE(CfgMgr::instance().isActiveIface("eth1"));
+    ASSERT_FALSE(CfgMgr::instance().isActiveIface("eth2"));
+
+    // Apply configuration.
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
+    ASSERT_TRUE(status);
+    checkResult(status, 0);
+
+    // eth0 and eth1 were explicitly selected. eth2 was not.
+    EXPECT_TRUE(CfgMgr::instance().isActiveIface("eth0"));
+    EXPECT_TRUE(CfgMgr::instance().isActiveIface("eth1"));
+    EXPECT_FALSE(CfgMgr::instance().isActiveIface("eth2"));
+}
+
+// This test verifies that it is possible to configure the server in such a way
+// that it listens on all interfaces.
+TEST_F(Dhcp4ParserTest, allInterfaces) {
+    ConstElementPtr x;
+    // This configuration specifies two interfaces on which server should listen
+    // but it also includes asterisk. The asterisk switches server into the
+    // mode when it listens on all interfaces regardless of what interface names
+    // were specified in the "interfaces" parameter.
+    string config = "{ \"interfaces\": [ \"eth0\", \"*\", \"eth1\" ],"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"valid-lifetime\": 4000 }";
+
+    ElementPtr json = Element::fromJSON(config);
+
+    ConstElementPtr status;
+
+    // Make sure there is no old configuration.
+    ASSERT_FALSE(CfgMgr::instance().isActiveIface("eth0"));
+    ASSERT_FALSE(CfgMgr::instance().isActiveIface("eth1"));
+    ASSERT_FALSE(CfgMgr::instance().isActiveIface("eth2"));
+
+    // Apply configuration.
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
+    ASSERT_TRUE(status);
+    checkResult(status, 0);
+
+    // All interfaces should be now active.
+    EXPECT_TRUE(CfgMgr::instance().isActiveIface("eth0"));
+    EXPECT_TRUE(CfgMgr::instance().isActiveIface("eth1"));
+    EXPECT_TRUE(CfgMgr::instance().isActiveIface("eth2"));
+}
+
+
+
+}
diff --git a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
index c39c56a..2cdf900 100644
--- a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
+++ b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
@@ -16,6 +16,7 @@
 #include <sstream>
 
 #include <asiolink/io_address.h>
+#include <config/ccsession.h>
 #include <dhcp/dhcp4.h>
 #include <dhcp/iface_mgr.h>
 #include <dhcp/option.h>
@@ -26,11 +27,15 @@
 #include <dhcp/pkt_filter_inet.h>
 #include <dhcp4/dhcp4_srv.h>
 #include <dhcp4/dhcp4_log.h>
+#include <dhcp4/config_parser.h>
+#include <hooks/server_hooks.h>
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/lease_mgr.h>
 #include <dhcpsrv/lease_mgr_factory.h>
 #include <dhcpsrv/utils.h>
 #include <gtest/gtest.h>
+#include <hooks/server_hooks.h>
+#include <hooks/hooks_manager.h>
 
 #include <boost/scoped_ptr.hpp>
 
@@ -42,7 +47,9 @@
 using namespace std;
 using namespace isc;
 using namespace isc::dhcp;
+using namespace isc::data;
 using namespace isc::asiolink;
+using namespace isc::hooks;
 
 namespace {
 
@@ -79,6 +86,55 @@ public:
         : Dhcpv4Srv(port, "type=memfile", false, false) {
     }
 
+    /// @brief fakes packet reception
+    /// @param timeout ignored
+    ///
+    /// The method receives all packets queued in receive queue, one after
+    /// another. Once the queue is empty, it initiates the shutdown procedure.
+    ///
+    /// See fake_received_ field for description
+    virtual Pkt4Ptr receivePacket(int /*timeout*/) {
+
+        // If there is anything prepared as fake incoming traffic, use it
+        if (!fake_received_.empty()) {
+            Pkt4Ptr pkt = fake_received_.front();
+            fake_received_.pop_front();
+            return (pkt);
+        }
+
+        // If not, just trigger shutdown and return immediately
+        shutdown();
+        return (Pkt4Ptr());
+    }
+
+    /// @brief fake packet sending
+    ///
+    /// Pretend to send a packet, but instead just store it in fake_send_ list
+    /// where test can later inspect server's response.
+    virtual void sendPacket(const Pkt4Ptr& pkt) {
+        fake_sent_.push_back(pkt);
+    }
+
+    /// @brief adds a packet to fake receive queue
+    ///
+    /// See fake_received_ field for description
+    void fakeReceive(const Pkt4Ptr& pkt) {
+        fake_received_.push_back(pkt);
+    }
+
+    virtual ~NakedDhcpv4Srv() {
+    }
+
+    /// @brief packets we pretend to receive
+    ///
+    /// Instead of setting up sockets on interfaces that change between OSes, it
+    /// is much easier to fake packet reception. This is a list of packets that
+    /// we pretend to have received. You can schedule new packets to be received
+    /// using fakeReceive() and NakedDhcpv4Srv::receivePacket() methods.
+    list<Pkt4Ptr> fake_received_;
+
+    list<Pkt4Ptr> fake_sent_;
+
     using Dhcpv4Srv::adjustRemoteAddr;
     using Dhcpv4Srv::processDiscover;
     using Dhcpv4Srv::processRequest;
@@ -97,11 +153,11 @@ static const char* SRVID_FILE = "server-id-test.txt";
 
 /// @brief Dummy Packet Filtering class.
 ///
-/// This class reports capability to respond directly to the
-/// client which doesn't have address configured yet.
+/// This class reports capability to respond directly to the client which
+/// doesn't have address configured yet.
 ///
-/// All packet and socket handling functions do nothing because
-/// they are not used in unit tests.
+/// All packet and socket handling functions do nothing because they are not
+/// used in unit tests.
 class PktFilterTest : public PktFilter {
 public:
 
@@ -137,7 +193,9 @@ public:
     ///
     /// Initializes common objects used in many tests.
     /// Also sets up initial configuration in CfgMgr.
-    Dhcpv4SrvTest() {
+    Dhcpv4SrvTest() :
+        rcode_(0)
+    {
         subnet_ = Subnet4Ptr(new Subnet4(IOAddress("192.0.2.0"), 24, 1000,
                                          2000, 3000));
         pool_ = Pool4Ptr(new Pool4(IOAddress("192.0.2.100"), IOAddress("192.0.2.110")));
@@ -153,6 +211,19 @@ public:
 
         // it's ok if that fails. There should not be such a file anyway
         unlink(SRVID_FILE);
+
+        const IfaceMgr::IfaceCollection& ifaces = IfaceMgr::instance().getIfaces();
+
+        // There must be some interface detected
+        if (ifaces.empty()) {
+            // We can't use ASSERT in constructor
+            ADD_FAILURE() << "No interfaces detected.";
+        }
+
+        valid_iface_ = ifaces.begin()->getName();
+    }
+
+    virtual ~Dhcpv4SrvTest() {
     }
 
     /// @brief Add 'Parameter Request List' option to the packet.
@@ -561,6 +632,13 @@ public:
 
     /// @brief A client-id used in most tests
     ClientIdPtr client_id_;
+
+    int rcode_;
+
+    ConstElementPtr comment_;
+
+    // Name of a valid network interface
+    string valid_iface_;
 };
 
 // Sanity check. Verifies that both Dhcpv4Srv and its derived
@@ -964,6 +1042,7 @@ TEST_F(Dhcpv4SrvTest, DiscoverNoClientId) {
     Pkt4Ptr dis = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
     dis->setRemoteAddr(IOAddress("192.0.2.1"));
     dis->setYiaddr(hint);
+    dis->setHWAddr(generateHWAddr(6));
 
     // Pass it to the server and get an offer
     Pkt4Ptr offer = srv->processDiscover(dis);
@@ -1325,8 +1404,9 @@ TEST_F(Dhcpv4SrvTest, sanityCheck) {
     ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
 
     Pkt4Ptr pkt = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
+    pkt->setHWAddr(generateHWAddr(6));
 
-    // Client-id is optional for information-request, so
+    // Server-id is optional for information-request, so
     EXPECT_NO_THROW(srv->sanityCheck(pkt, Dhcpv4Srv::OPTIONAL));
 
     // Empty packet, no server-id
@@ -1340,6 +1420,11 @@ TEST_F(Dhcpv4SrvTest, sanityCheck) {
     // Server-id is forbidden, but present => exception
     EXPECT_THROW(srv->sanityCheck(pkt, Dhcpv4Srv::FORBIDDEN),
                  RFCViolation);
+
+    // There's no client-id and no HWADDR. Server needs something to
+    // identify the client
+    pkt->setHWAddr(generateHWAddr(0));
+    EXPECT_THROW(srv->sanityCheck(pkt, Dhcpv4Srv::MANDATORY), RFCViolation);
 }
 
 // This test verifies that incoming (positive) RELEASE can be handled properly.
@@ -1535,4 +1620,731 @@ TEST_F(Dhcpv4SrvTest, ServerID) {
     EXPECT_EQ(srvid_text, text);
 }
 
+/// @todo Implement tests for subnetSelect See tests in dhcp6_srv_unittest.cc:
+/// selectSubnetAddr, selectSubnetIface, selectSubnetRelayLinkaddr,
+/// selectSubnetRelayInterfaceId. Note that the concept of interface-id is not
+/// present in the DHCPv4, so not everything is applicable directly.
+/// See ticket #3057
+
+// Checks if hooks are registered properly.
+TEST_F(Dhcpv4SrvTest, Hooks) {
+    NakedDhcpv4Srv srv(0);
+
+    // check if appropriate hooks are registered
+    int hook_index_pkt4_received = -1;
+    int hook_index_select_subnet = -1;
+    int hook_index_pkt4_send     = -1;
+
+    // check if appropriate indexes are set
+    EXPECT_NO_THROW(hook_index_pkt4_received = ServerHooks::getServerHooks()
+                    .getIndex("pkt4_receive"));
+    EXPECT_NO_THROW(hook_index_select_subnet = ServerHooks::getServerHooks()
+                    .getIndex("subnet4_select"));
+    EXPECT_NO_THROW(hook_index_pkt4_send     = ServerHooks::getServerHooks()
+                    .getIndex("pkt4_send"));
+
+    EXPECT_TRUE(hook_index_pkt4_received > 0);
+    EXPECT_TRUE(hook_index_select_subnet > 0);
+    EXPECT_TRUE(hook_index_pkt4_send > 0);
+}
+
+    // a dummy MAC address
+    const uint8_t dummyMacAddr[] = {0, 1, 2, 3, 4, 5};
+
+    // A dummy MAC address, padded with 0s
+    const uint8_t dummyChaddr[16] = {0, 1, 2, 3, 4, 5, 0, 0,
+                                     0, 0, 0, 0, 0, 0, 0, 0 };
+
+    // Let's use some creative test content here (128 chars + \0)
+    const uint8_t dummyFile[] = "Lorem ipsum dolor sit amet, consectetur "
+        "adipiscing elit. Proin mollis placerat metus, at "
+        "lacinia orci ornare vitae. Mauris amet.";
+
+    // Yet another type of test content (64 chars + \0)
+    const uint8_t dummySname[] = "Lorem ipsum dolor sit amet, consectetur "
+        "adipiscing elit posuere.";
+
+/// @brief a class dedicated to Hooks testing in DHCPv4 server
+///
+/// This class has a number of static members, because each non-static
+/// method has implicit 'this' parameter, so it does not match callout
+/// signature and couldn't be registered. Furthermore, static methods
+/// can't modify non-static members (for obvious reasons), so many
+/// fields are declared static. It is still better to keep them as
+/// one class rather than unrelated collection of global objects.
+class HooksDhcpv4SrvTest : public Dhcpv4SrvTest {
+
+public:
+
+    /// @brief creates Dhcpv4Srv and prepares buffers for callouts
+    HooksDhcpv4SrvTest() {
+
+        // Allocate new DHCPv6 Server
+        srv_ = new NakedDhcpv4Srv(0);
+
+        // clear static buffers
+        resetCalloutBuffers();
+    }
+
+    /// @brief destructor (deletes Dhcpv4Srv)
+    virtual ~HooksDhcpv4SrvTest() {
+
+        HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("pkt4_receive");
+        HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("pkt4_send");
+        HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("subnet4_select");
+
+        delete srv_;
+    }
+
+    /// @brief creates an option with specified option code
+    ///
+    /// This method is static, because it is used from callouts
+    /// that do not have a pointer to HooksDhcpv4SSrvTest object
+    ///
+    /// @param option_code code of option to be created
+    ///
+    /// @return pointer to create option object
+    static OptionPtr createOption(uint16_t option_code) {
+
+        char payload[] = {
+            0xa, 0xb, 0xc, 0xe, 0xf, 0x10, 0x11, 0x12, 0x13, 0x14
+        };
+
+        OptionBuffer tmp(payload, payload + sizeof(payload));
+        return OptionPtr(new Option(Option::V4, option_code, tmp));
+    }
+
+    /// @brief Generates test packet.
+    ///
+    /// Allocates and generates on-wire buffer that represents test packet, with all
+    /// fixed fields set to non-zero values.  Content is not always reasonable.
+    ///
+    /// See generateTestPacket1() function that returns exactly the same packet as
+    /// Pkt4 object.
+    ///
+    /// @return pointer to allocated Pkt4 object
+    // Returns a vector containing a DHCPv4 packet header.
+    Pkt4Ptr
+    generateSimpleDiscover() {
+
+        // That is only part of the header. It contains all "short" fields,
+        // larger fields are constructed separately.
+        uint8_t hdr[] = {
+            1, 6, 6, 13,            // op, htype, hlen, hops,
+            0x12, 0x34, 0x56, 0x78, // transaction-id
+            0, 42, 0x80, 0x00,      // 42 secs, BROADCAST flags
+            192, 0, 2, 1,           // ciaddr
+            1, 2, 3, 4,             // yiaddr
+            192, 0, 2, 255,         // siaddr
+            255, 255, 255, 255,     // giaddr
+        };
+
+        // Initialize the vector with the header fields defined above.
+        vector<uint8_t> buf(hdr, hdr + sizeof(hdr));
+
+        // Append the large header fields.
+        copy(dummyChaddr, dummyChaddr + Pkt4::MAX_CHADDR_LEN, back_inserter(buf));
+        copy(dummySname, dummySname + Pkt4::MAX_SNAME_LEN, back_inserter(buf));
+        copy(dummyFile, dummyFile + Pkt4::MAX_FILE_LEN, back_inserter(buf));
+
+        // Should now have all the header, so check.  The "static_cast" is used
+        // to get round an odd bug whereby the linker appears not to find the
+        // definition of DHCPV4_PKT_HDR_LEN if it appears within an EXPECT_EQ().
+        EXPECT_EQ(static_cast<size_t>(Pkt4::DHCPV4_PKT_HDR_LEN), buf.size());
+
+        // Add magic cookie
+        buf.push_back(0x63);
+        buf.push_back(0x82);
+        buf.push_back(0x53);
+        buf.push_back(0x63);
+
+        // Add message type DISCOVER
+        buf.push_back(static_cast<uint8_t>(DHO_DHCP_MESSAGE_TYPE));
+        buf.push_back(1); // length (just one byte)
+        buf.push_back(static_cast<uint8_t>(DHCPDISCOVER));
+
+        return (Pkt4Ptr(new Pkt4(&buf[0], buf.size())));
+    }
+
+    /// test callback that stores received callout name and pkt4 value
+    /// @param callout_handle handle passed by the hooks framework
+    /// @return always 0
+    static int
+    pkt4_receive_callout(CalloutHandle& callout_handle) {
+        callback_name_ = string("pkt4_receive");
+
+        callout_handle.getArgument("query4", callback_pkt4_);
+
+        callback_argument_names_ = callout_handle.getArgumentNames();
+        return (0);
+    }
+
+    /// test callback that changes client-id value
+    /// @param callout_handle handle passed by the hooks framework
+    /// @return always 0
+    static int
+    pkt4_receive_change_clientid(CalloutHandle& callout_handle) {
+
+        Pkt4Ptr pkt;
+        callout_handle.getArgument("query4", pkt);
+
+        // get rid of the old client-id
+        pkt->delOption(DHO_DHCP_CLIENT_IDENTIFIER);
+
+        // add a new option
+        pkt->addOption(createOption(DHO_DHCP_CLIENT_IDENTIFIER));
+
+        // carry on as usual
+        return pkt4_receive_callout(callout_handle);
+    }
+
+    /// test callback that deletes client-id
+    /// @param callout_handle handle passed by the hooks framework
+    /// @return always 0
+    static int
+    pkt4_receive_delete_clientid(CalloutHandle& callout_handle) {
+
+        Pkt4Ptr pkt;
+        callout_handle.getArgument("query4", pkt);
+
+        // get rid of the old client-id (and no HWADDR)
+        vector<uint8_t> mac;
+        pkt->delOption(DHO_DHCP_CLIENT_IDENTIFIER);
+        pkt->setHWAddr(1, 0, mac); // HWtype 1, hwardware len = 0
+
+        // carry on as usual
+        return pkt4_receive_callout(callout_handle);
+    }
+
+    /// test callback that sets skip flag
+    /// @param callout_handle handle passed by the hooks framework
+    /// @return always 0
+    static int
+    pkt4_receive_skip(CalloutHandle& callout_handle) {
+
+        Pkt4Ptr pkt;
+        callout_handle.getArgument("query4", pkt);
+
+        callout_handle.setSkip(true);
+
+        // carry on as usual
+        return pkt4_receive_callout(callout_handle);
+    }
+
+    /// Test callback that stores received callout name and pkt4 value
+    /// @param callout_handle handle passed by the hooks framework
+    /// @return always 0
+    static int
+    pkt4_send_callout(CalloutHandle& callout_handle) {
+        callback_name_ = string("pkt4_send");
+
+        callout_handle.getArgument("response4", callback_pkt4_);
+
+        callback_argument_names_ = callout_handle.getArgumentNames();
+        return (0);
+    }
+
+    // Test callback that changes server-id
+    /// @param callout_handle handle passed by the hooks framework
+    /// @return always 0
+    static int
+    pkt4_send_change_serverid(CalloutHandle& callout_handle) {
+
+        Pkt4Ptr pkt;
+        callout_handle.getArgument("response4", pkt);
+
+        // get rid of the old server-id
+        pkt->delOption(DHO_DHCP_SERVER_IDENTIFIER);
+
+        // add a new option
+        pkt->addOption(createOption(DHO_DHCP_SERVER_IDENTIFIER));
+
+        // carry on as usual
+        return pkt4_send_callout(callout_handle);
+    }
+
+    /// test callback that deletes server-id
+    /// @param callout_handle handle passed by the hooks framework
+    /// @return always 0
+    static int
+    pkt4_send_delete_serverid(CalloutHandle& callout_handle) {
+
+        Pkt4Ptr pkt;
+        callout_handle.getArgument("response4", pkt);
+
+        // get rid of the old client-id
+        pkt->delOption(DHO_DHCP_SERVER_IDENTIFIER);
+
+        // carry on as usual
+        return pkt4_send_callout(callout_handle);
+    }
+
+    /// Test callback that sets skip flag
+    /// @param callout_handle handle passed by the hooks framework
+    /// @return always 0
+    static int
+    pkt4_send_skip(CalloutHandle& callout_handle) {
+
+        Pkt4Ptr pkt;
+        callout_handle.getArgument("response4", pkt);
+
+        callout_handle.setSkip(true);
+
+        // carry on as usual
+        return pkt4_send_callout(callout_handle);
+    }
+
+    /// Test callback that stores received callout name and subnet4 values
+    /// @param callout_handle handle passed by the hooks framework
+    /// @return always 0
+    static int
+    subnet4_select_callout(CalloutHandle& callout_handle) {
+        callback_name_ = string("subnet4_select");
+
+        callout_handle.getArgument("query4", callback_pkt4_);
+        callout_handle.getArgument("subnet4", callback_subnet4_);
+        callout_handle.getArgument("subnet4collection", callback_subnet4collection_);
+
+        callback_argument_names_ = callout_handle.getArgumentNames();
+        return (0);
+    }
+
+    /// Test callback that picks the other subnet if possible.
+    /// @param callout_handle handle passed by the hooks framework
+    /// @return always 0
+    static int
+    subnet4_select_different_subnet_callout(CalloutHandle& callout_handle) {
+
+        // Call the basic calllout to record all passed values
+        subnet4_select_callout(callout_handle);
+
+        const Subnet4Collection* subnets;
+        Subnet4Ptr subnet;
+        callout_handle.getArgument("subnet4", subnet);
+        callout_handle.getArgument("subnet4collection", subnets);
+
+        // Let's change to a different subnet
+        if (subnets->size() > 1) {
+            subnet = (*subnets)[1]; // Let's pick the other subnet
+            callout_handle.setArgument("subnet4", subnet);
+        }
+
+        return (0);
+    }
+
+    /// resets buffers used to store data received by callouts
+    void resetCalloutBuffers() {
+        callback_name_ = string("");
+        callback_pkt4_.reset();
+        callback_subnet4_.reset();
+        callback_subnet4collection_ = NULL;
+        callback_argument_names_.clear();
+    }
+
+    /// pointer to Dhcpv4Srv that is used in tests
+    NakedDhcpv4Srv* srv_;
+
+    // The following fields are used in testing pkt4_receive_callout
+
+    /// String name of the received callout
+    static string callback_name_;
+
+    /// Pkt4 structure returned in the callout
+    static Pkt4Ptr callback_pkt4_;
+
+    /// Pointer to a subnet received by callout
+    static Subnet4Ptr callback_subnet4_;
+
+    /// A list of all available subnets (received by callout)
+    static const Subnet4Collection* callback_subnet4collection_;
+
+    /// A list of all received arguments
+    static vector<string> callback_argument_names_;
+};
+
+// The following fields are used in testing pkt4_receive_callout.
+// See fields description in the class for details
+string HooksDhcpv4SrvTest::callback_name_;
+Pkt4Ptr HooksDhcpv4SrvTest::callback_pkt4_;
+Subnet4Ptr HooksDhcpv4SrvTest::callback_subnet4_;
+const Subnet4Collection* HooksDhcpv4SrvTest::callback_subnet4collection_;
+vector<string> HooksDhcpv4SrvTest::callback_argument_names_;
+
+
+// Checks if callouts installed on pkt4_received are indeed called and the
+// all necessary parameters are passed.
+//
+// Note that the test name does not follow test naming convention,
+// but the proper hook name is "pkt4_receive".
+TEST_F(HooksDhcpv4SrvTest, simple_pkt4_receive) {
+
+    // Install pkt4_receive_callout
+    EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                        "pkt4_receive", pkt4_receive_callout));
+
+    // Let's create a simple DISCOVER
+    Pkt4Ptr sol = Pkt4Ptr(generateSimpleDiscover());
+
+    // Simulate that we have received that traffic
+    srv_->fakeReceive(sol);
+
+    // Server will now process to run its normal loop, but instead of calling
+    // IfaceMgr::receive4(), it will read all packets from the list set by
+    // fakeReceive()
+    // In particular, it should call registered pkt4_receive callback.
+    srv_->run();
+
+    // check that the callback called is indeed the one we installed
+    EXPECT_EQ("pkt4_receive", callback_name_);
+
+    // check that pkt4 argument passing was successful and returned proper value
+    EXPECT_TRUE(callback_pkt4_.get() == sol.get());
+
+    // Check that all expected parameters are there
+    vector<string> expected_argument_names;
+    expected_argument_names.push_back(string("query4"));
+
+    EXPECT_TRUE(expected_argument_names == callback_argument_names_);
+}
+
+// Checks if callouts installed on pkt4_received is able to change
+// the values and the parameters are indeed used by the server.
+TEST_F(HooksDhcpv4SrvTest, valueChange_pkt4_receive) {
+
+    // Install pkt4_receive_callout
+    EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                        "pkt4_receive", pkt4_receive_change_clientid));
+
+    // Let's create a simple DISCOVER
+    Pkt4Ptr sol = Pkt4Ptr(generateSimpleDiscover());
+
+    // Simulate that we have received that traffic
+    srv_->fakeReceive(sol);
+
+    // Server will now process to run its normal loop, but instead of calling
+    // IfaceMgr::receive4(), it will read all packets from the list set by
+    // fakeReceive()
+    // In particular, it should call registered pkt4_receive callback.
+    srv_->run();
+
+    // check that the server did send a reposonce
+    ASSERT_EQ(1, srv_->fake_sent_.size());
+
+    // Make sure that we received a response
+    Pkt4Ptr adv = srv_->fake_sent_.front();
+    ASSERT_TRUE(adv);
+
+    // Get client-id...
+    OptionPtr clientid = adv->getOption(DHO_DHCP_CLIENT_IDENTIFIER);
+
+    // ... and check if it is the modified value
+    OptionPtr expected = createOption(DHO_DHCP_CLIENT_IDENTIFIER);
+    EXPECT_TRUE(clientid->equal(expected));
+}
+
+// Checks if callouts installed on pkt4_received is able to delete
+// existing options and that change impacts server processing (mandatory
+// client-id option is deleted, so the packet is expected to be dropped)
+TEST_F(HooksDhcpv4SrvTest, deleteClientId_pkt4_receive) {
+
+    // Install pkt4_receive_callout
+    EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                        "pkt4_receive", pkt4_receive_delete_clientid));
+
+    // Let's create a simple DISCOVER
+    Pkt4Ptr sol = Pkt4Ptr(generateSimpleDiscover());
+
+    // Simulate that we have received that traffic
+    srv_->fakeReceive(sol);
+
+    // Server will now process to run its normal loop, but instead of calling
+    // IfaceMgr::receive4(), it will read all packets from the list set by
+    // fakeReceive()
+    // In particular, it should call registered pkt4_receive callback.
+    srv_->run();
+
+    // Check that the server dropped the packet and did not send a response
+    ASSERT_EQ(0, srv_->fake_sent_.size());
+}
+
+// Checks if callouts installed on pkt4_received is able to set skip flag that
+// will cause the server to not process the packet (drop), even though it is valid.
+TEST_F(HooksDhcpv4SrvTest, skip_pkt4_receive) {
+
+    // Install pkt4_receive_callout
+    EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                        "pkt4_receive", pkt4_receive_skip));
+
+    // Let's create a simple DISCOVER
+    Pkt4Ptr sol = Pkt4Ptr(generateSimpleDiscover());
+
+    // Simulate that we have received that traffic
+    srv_->fakeReceive(sol);
+
+    // Server will now process to run its normal loop, but instead of calling
+    // IfaceMgr::receive4(), it will read all packets from the list set by
+    // fakeReceive()
+    // In particular, it should call registered pkt4_receive callback.
+    srv_->run();
+
+    // check that the server dropped the packet and did not produce any response
+    ASSERT_EQ(0, srv_->fake_sent_.size());
+}
+
+
+// Checks if callouts installed on pkt4_send are indeed called and the
+// all necessary parameters are passed.
+TEST_F(HooksDhcpv4SrvTest, simple_pkt4_send) {
+
+    // Install pkt4_receive_callout
+    EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                        "pkt4_send", pkt4_send_callout));
+
+    // Let's create a simple DISCOVER
+    Pkt4Ptr sol = Pkt4Ptr(generateSimpleDiscover());
+
+    // Simulate that we have received that traffic
+    srv_->fakeReceive(sol);
+
+    // Server will now process to run its normal loop, but instead of calling
+    // IfaceMgr::receive4(), it will read all packets from the list set by
+    // fakeReceive()
+    // In particular, it should call registered pkt4_receive callback.
+    srv_->run();
+
+    // Check that the callback called is indeed the one we installed
+    EXPECT_EQ("pkt4_send", callback_name_);
+
+    // Check that there is one packet sent
+    ASSERT_EQ(1, srv_->fake_sent_.size());
+    Pkt4Ptr adv = srv_->fake_sent_.front();
+
+    // Check that pkt4 argument passing was successful and returned proper value
+    EXPECT_TRUE(callback_pkt4_.get() == adv.get());
+
+    // Check that all expected parameters are there
+    vector<string> expected_argument_names;
+    expected_argument_names.push_back(string("response4"));
+    EXPECT_TRUE(expected_argument_names == callback_argument_names_);
+}
+
+// Checks if callouts installed on pkt4_send is able to change
+// the values and the packet sent contains those changes
+TEST_F(HooksDhcpv4SrvTest, valueChange_pkt4_send) {
+
+    // Install pkt4_receive_callout
+    EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                        "pkt4_send", pkt4_send_change_serverid));
+
+    // Let's create a simple DISCOVER
+    Pkt4Ptr sol = Pkt4Ptr(generateSimpleDiscover());
+
+    // Simulate that we have received that traffic
+    srv_->fakeReceive(sol);
+
+    // Server will now process to run its normal loop, but instead of calling
+    // IfaceMgr::receive4(), it will read all packets from the list set by
+    // fakeReceive()
+    // In particular, it should call registered pkt4_receive callback.
+    srv_->run();
+
+    // check that the server did send a reposonce
+    ASSERT_EQ(1, srv_->fake_sent_.size());
+
+    // Make sure that we received a response
+    Pkt4Ptr adv = srv_->fake_sent_.front();
+    ASSERT_TRUE(adv);
+
+    // Get client-id...
+    OptionPtr clientid = adv->getOption(DHO_DHCP_SERVER_IDENTIFIER);
+
+    // ... and check if it is the modified value
+    OptionPtr expected = createOption(DHO_DHCP_SERVER_IDENTIFIER);
+    EXPECT_TRUE(clientid->equal(expected));
+}
+
+// Checks if callouts installed on pkt4_send is able to delete
+// existing options and that server applies those changes. In particular,
+// we are trying to send a packet without server-id. The packet should
+// be sent
+TEST_F(HooksDhcpv4SrvTest, deleteServerId_pkt4_send) {
+
+    // Install pkt4_receive_callout
+    EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                        "pkt4_send", pkt4_send_delete_serverid));
+
+    // Let's create a simple DISCOVER
+    Pkt4Ptr sol = Pkt4Ptr(generateSimpleDiscover());
+
+    // Simulate that we have received that traffic
+    srv_->fakeReceive(sol);
+
+    // Server will now process to run its normal loop, but instead of calling
+    // IfaceMgr::receive4(), it will read all packets from the list set by
+    // fakeReceive()
+    // In particular, it should call registered pkt4_receive callback.
+    srv_->run();
+
+    // Check that the server indeed sent a malformed ADVERTISE
+    ASSERT_EQ(1, srv_->fake_sent_.size());
+
+    // Get that ADVERTISE
+    Pkt4Ptr adv = srv_->fake_sent_.front();
+    ASSERT_TRUE(adv);
+
+    // Make sure that it does not have server-id
+    EXPECT_FALSE(adv->getOption(DHO_DHCP_SERVER_IDENTIFIER));
+}
+
+// Checks if callouts installed on pkt4_skip is able to set skip flag that
+// will cause the server to not process the packet (drop), even though it is valid.
+TEST_F(HooksDhcpv4SrvTest, skip_pkt4_send) {
+
+    // Install pkt4_receive_callout
+    EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                        "pkt4_send", pkt4_send_skip));
+
+    // Let's create a simple REQUEST
+    Pkt4Ptr sol = Pkt4Ptr(generateSimpleDiscover());
+
+    // Simulate that we have received that traffic
+    srv_->fakeReceive(sol);
+
+    // Server will now process to run its normal loop, but instead of calling
+    // IfaceMgr::receive4(), it will read all packets from the list set by
+    // fakeReceive()
+    // In particular, it should call registered pkt4_receive callback.
+    srv_->run();
+
+    // check that the server dropped the packet and did not produce any response
+    ASSERT_EQ(0, srv_->fake_sent_.size());
+}
+
+// This test checks if subnet4_select callout is triggered and reports
+// valid parameters
+TEST_F(HooksDhcpv4SrvTest, subnet4_select) {
+
+    // Install pkt4_receive_callout
+    EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                        "subnet4_select", subnet4_select_callout));
+
+    // Configure 2 subnets, both directly reachable over local interface
+    // (let's not complicate the matter with relays)
+    string config = "{ \"interfaces\": [ \"*\" ],"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet4\": [ { "
+        "    \"pool\": [ \"192.0.2.0/25\" ],"
+        "    \"subnet\": \"192.0.2.0/24\", "
+        "    \"interface\": \"" + valid_iface_ + "\" "
+        " }, {"
+        "    \"pool\": [ \"192.0.3.0/25\" ],"
+        "    \"subnet\": \"192.0.3.0/24\" "
+        " } ],"
+        "\"valid-lifetime\": 4000 }";
+
+    ElementPtr json = Element::fromJSON(config);
+    ConstElementPtr status;
+
+    // Configure the server and make sure the config is accepted
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
+    ASSERT_TRUE(status);
+    comment_ = config::parseAnswer(rcode_, status);
+    ASSERT_EQ(0, rcode_);
+
+    // Prepare discover packet. Server should select first subnet for it
+    Pkt4Ptr sol = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
+    sol->setRemoteAddr(IOAddress("192.0.2.1"));
+    sol->setIface(valid_iface_);
+    OptionPtr clientid = generateClientId();
+    sol->addOption(clientid);
+
+    // Pass it to the server and get an advertise
+    Pkt4Ptr adv = srv_->processDiscover(sol);
+
+    // check if we get response at all
+    ASSERT_TRUE(adv);
+
+    // Check that the callback called is indeed the one we installed
+    EXPECT_EQ("subnet4_select", callback_name_);
+
+    // Check that pkt4 argument passing was successful and returned proper value
+    EXPECT_TRUE(callback_pkt4_.get() == sol.get());
+
+    const Subnet4Collection* exp_subnets = CfgMgr::instance().getSubnets4();
+
+    // The server is supposed to pick the first subnet, because of matching
+    // interface. Check that the value is reported properly.
+    ASSERT_TRUE(callback_subnet4_);
+    EXPECT_EQ(exp_subnets->front().get(), callback_subnet4_.get());
+
+    // Server is supposed to report two subnets
+    ASSERT_EQ(exp_subnets->size(), callback_subnet4collection_->size());
+
+    // Compare that the available subnets are reported as expected
+    EXPECT_TRUE((*exp_subnets)[0].get() == (*callback_subnet4collection_)[0].get());
+    EXPECT_TRUE((*exp_subnets)[1].get() == (*callback_subnet4collection_)[1].get());
+}
+
+// This test checks if callout installed on subnet4_select hook point can pick
+// a different subnet.
+TEST_F(HooksDhcpv4SrvTest, subnet_select_change) {
+
+    // Install pkt4_receive_callout
+    EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                        "subnet4_select", subnet4_select_different_subnet_callout));
+
+    // Configure 2 subnets, both directly reachable over local interface
+    // (let's not complicate the matter with relays)
+    string config = "{ \"interfaces\": [ \"*\" ],"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet4\": [ { "
+        "    \"pool\": [ \"192.0.2.0/25\" ],"
+        "    \"subnet\": \"192.0.2.0/24\", "
+        "    \"interface\": \"" + valid_iface_ + "\" "
+        " }, {"
+        "    \"pool\": [ \"192.0.3.0/25\" ],"
+        "    \"subnet\": \"192.0.3.0/24\" "
+        " } ],"
+        "\"valid-lifetime\": 4000 }";
+
+    ElementPtr json = Element::fromJSON(config);
+    ConstElementPtr status;
+
+    // Configure the server and make sure the config is accepted
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
+    ASSERT_TRUE(status);
+    comment_ = config::parseAnswer(rcode_, status);
+    ASSERT_EQ(0, rcode_);
+
+    // Prepare discover packet. Server should select first subnet for it
+    Pkt4Ptr sol = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
+    sol->setRemoteAddr(IOAddress("192.0.2.1"));
+    sol->setIface(valid_iface_);
+    OptionPtr clientid = generateClientId();
+    sol->addOption(clientid);
+
+    // Pass it to the server and get an advertise
+    Pkt4Ptr adv = srv_->processDiscover(sol);
+
+    // check if we get response at all
+    ASSERT_TRUE(adv);
+
+    // The response should have an address from second pool, so let's check it
+    IOAddress addr = adv->getYiaddr();
+    EXPECT_NE("0.0.0.0", addr.toText());
+
+    // Get all subnets and use second subnet for verification
+    const Subnet4Collection* subnets = CfgMgr::instance().getSubnets4();
+    ASSERT_EQ(2, subnets->size());
+
+    // Advertised address must belong to the second pool (in subnet's range,
+    // in dynamic pool)
+    EXPECT_TRUE((*subnets)[1]->inRange(addr));
+    EXPECT_TRUE((*subnets)[1]->inPool(addr));
+}
+
+
+
 } // end of anonymous namespace
diff --git a/src/bin/dhcp6/Makefile.am b/src/bin/dhcp6/Makefile.am
index 3b07510..a7a9811 100644
--- a/src/bin/dhcp6/Makefile.am
+++ b/src/bin/dhcp6/Makefile.am
@@ -65,6 +65,7 @@ b10_dhcp6_LDADD += $(top_builddir)/src/lib/dhcpsrv/libb10-dhcpsrv.la
 b10_dhcp6_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
 b10_dhcp6_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
 b10_dhcp6_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
+b10_dhcp6_LDADD += $(top_builddir)/src/lib/hooks/libb10-hooks.la
 
 b10_dhcp6dir = $(pkgdatadir)
 b10_dhcp6_DATA = dhcp6.spec
diff --git a/src/bin/dhcp6/config_parser.cc b/src/bin/dhcp6/config_parser.cc
index 74012ac..fb92f88 100644
--- a/src/bin/dhcp6/config_parser.cc
+++ b/src/bin/dhcp6/config_parser.cc
@@ -67,10 +67,10 @@ public:
     /// @param dummy first param, option names are always "Dhcp6/option-data[n]"
     /// @param options is the option storage in which to store the parsed option
     /// upon "commit".
-    /// @param global_context is a pointer to the global context which 
+    /// @param global_context is a pointer to the global context which
     /// stores global scope parameters, options, option defintions.
-    Dhcp6OptionDataParser(const std::string&, OptionStoragePtr options, 
-                         ParserContextPtr global_context) 
+    Dhcp6OptionDataParser(const std::string&, OptionStoragePtr options,
+                         ParserContextPtr global_context)
         :OptionDataParser("", options, global_context) {
     }
 
@@ -78,7 +78,7 @@ public:
     ///
     /// @param param_name name of the parameter to be parsed.
     /// @param options storage where the parameter value is to be stored.
-    /// @param global_context is a pointer to the global context which 
+    /// @param global_context is a pointer to the global context which
     /// stores global scope parameters, options, option defintions.
     /// @return returns a pointer to a new OptionDataParser. Caller is
     /// is responsible for deleting it when it is no longer needed.
@@ -90,16 +90,16 @@ public:
 
 protected:
     /// @brief Finds an option definition within the server's option space
-    /// 
-    /// Given an option space and an option code, find the correpsonding 
+    ///
+    /// Given an option space and an option code, find the correpsonding
     /// option defintion within the server's option defintion storage.
     ///
-    /// @param option_space name of the parameter option space 
-    /// @param option_code numeric value of the parameter to find 
-    /// @return OptionDefintionPtr of the option defintion or an 
+    /// @param option_space name of the parameter option space
+    /// @param option_code numeric value of the parameter to find
+    /// @return OptionDefintionPtr of the option defintion or an
     /// empty OptionDefinitionPtr if not found.
-    /// @throw DhcpConfigError if the option space requested is not valid 
-    /// for this server. 
+    /// @throw DhcpConfigError if the option space requested is not valid
+    /// for this server.
     virtual OptionDefinitionPtr findServerSpaceOptionDefinition (
                             std::string& option_space, uint32_t option_code) {
         OptionDefinitionPtr def;
@@ -115,11 +115,11 @@ protected:
     }
 };
 
-/// @brief Parser for IPv4 pool definitions.  
+/// @brief Parser for IPv4 pool definitions.
 ///
-/// This is the IPv6 derivation of the PoolParser class and handles pool 
-/// definitions, i.e. a list of entries of one of two syntaxes: min-max and 
-/// prefix/len for IPv6 pools. Pool6 objects are created and stored in chosen 
+/// This is the IPv6 derivation of the PoolParser class and handles pool
+/// definitions, i.e. a list of entries of one of two syntaxes: min-max and
+/// prefix/len for IPv6 pools. Pool6 objects are created and stored in chosen
 /// PoolStorage container.
 ///
 /// It is useful for parsing Dhcp6/subnet6[X]/pool parameters.
@@ -142,9 +142,9 @@ protected:
     /// @param addr is the IPv6 prefix of the pool.
     /// @param len is the prefix length.
     /// @param ptype is the type of IPv6 pool (Pool6::Pool6Type). Note this is
-    /// passed in as an int32_t and cast to Pool6Type to accommodate a 
+    /// passed in as an int32_t and cast to Pool6Type to accommodate a
     /// polymorphic interface.
-    /// @return returns a PoolPtr to the new Pool4 object. 
+    /// @return returns a PoolPtr to the new Pool4 object.
     PoolPtr poolMaker (IOAddress &addr, uint32_t len, int32_t ptype)
     {
         return (PoolPtr(new Pool6(static_cast<isc::dhcp::Pool6::Pool6Type>
@@ -156,9 +156,9 @@ protected:
     /// @param min is the first IPv6 address in the pool.
     /// @param max is the last IPv6 address in the pool.
     /// @param ptype is the type of IPv6 pool (Pool6::Pool6Type). Note this is
-    /// passed in as an int32_t and cast to Pool6Type to accommodate a 
+    /// passed in as an int32_t and cast to Pool6Type to accommodate a
     /// polymorphic interface.
-    /// @return returns a PoolPtr to the new Pool4 object. 
+    /// @return returns a PoolPtr to the new Pool4 object.
     PoolPtr poolMaker (IOAddress &min, IOAddress &max, int32_t ptype)
     {
         return (PoolPtr(new Pool6(static_cast<isc::dhcp::Pool6::Pool6Type>
@@ -168,8 +168,8 @@ protected:
 
 /// @brief This class parses a single IPv6 subnet.
 ///
-/// This is the IPv6 derivation of the SubnetConfigParser class and it parses 
-/// the whole subnet definition. It creates parsersfor received configuration 
+/// This is the IPv6 derivation of the SubnetConfigParser class and it parses
+/// the whole subnet definition. It creates parsersfor received configuration
 /// parameters as needed.
 class Subnet6ConfigParser : public SubnetConfigParser {
 public:
@@ -178,7 +178,7 @@ public:
     ///
     /// @param ignored first parameter
     /// stores global scope parameters, options, option defintions.
-    Subnet6ConfigParser(const std::string&) 
+    Subnet6ConfigParser(const std::string&)
         :SubnetConfigParser("", globalContext()) {
     }
 
@@ -220,7 +220,7 @@ protected:
         } else if (config_id.compare("pool") == 0) {
             parser = new Pool6Parser(config_id, pools_);
         } else if (config_id.compare("option-data") == 0) {
-           parser = new OptionDataListParser(config_id, options_, 
+           parser = new OptionDataListParser(config_id, options_,
                                              global_context_,
                                              Dhcp6OptionDataParser::factory);
         } else {
@@ -233,14 +233,14 @@ protected:
 
 
     /// @brief Determines if the given option space name and code describe
-    /// a standard option for the DHCP6 server. 
+    /// a standard option for the DHCP6 server.
     ///
     /// @param option_space is the name of the option space to consider
     /// @param code is the numeric option code to consider
     /// @return returns true if the space and code are part of the server's
     /// standard options.
     bool isServerStdOption(std::string option_space, uint32_t code) {
-        return ((option_space.compare("dhcp6") == 0) 
+        return ((option_space.compare("dhcp6") == 0)
                 && LibDHCP::isStandardOption(Option::V6, code));
     }
 
@@ -253,23 +253,23 @@ protected:
     }
 
     /// @brief Issues a DHCP6 server specific warning regarding duplicate subnet
-    /// options. 
-    /// 
+    /// options.
+    ///
     /// @param code is the numeric option code of the duplicate option
     /// @param addr is the subnet address
     /// @todo A means to know the correct logger and perhaps a common
     /// message would allow this message to be emitted by the base class.
-    virtual void duplicate_option_warning(uint32_t code, 
+    virtual void duplicate_option_warning(uint32_t code,
                                          isc::asiolink::IOAddress& addr) {
         LOG_WARN(dhcp6_logger, DHCP6_CONFIG_OPTION_DUPLICATE)
             .arg(code).arg(addr.toText());
     }
 
     /// @brief Instantiates the IPv6 Subnet based on a given IPv6 address
-    /// and prefix length.  
-    /// 
+    /// and prefix length.
+    ///
     /// @param addr is IPv6 prefix of the subnet.
-    /// @param len is the prefix length 
+    /// @param len is the prefix length
     void initSubnet(isc::asiolink::IOAddress addr, uint8_t len) {
         // Get all 'time' parameters using inheritance.
         // If the subnet-specific value is defined then use it, else
@@ -292,13 +292,13 @@ protected:
 
         // Specifying both interface for locally reachable subnets and
         // interface id for relays is mutually exclusive. Need to test for
-        // this condition. 
+        // this condition.
         if (!ifaceid.empty()) {
             std::string iface;
             try {
                 iface = string_values_->getParam("interface");
             } catch (const DhcpConfigError &) {
-                // iface not mandatory 
+                // iface not mandatory
             }
 
             if (!iface.empty()) {
@@ -403,7 +403,7 @@ namespace dhcp {
 ///
 /// @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 
+/// @throw NotImplemented if trying to create a parser for unknown config
 /// element
 DhcpConfigParser* createGlobal6DhcpConfigParser(const std::string& config_id) {
     DhcpConfigParser* parser = NULL;
@@ -411,25 +411,27 @@ DhcpConfigParser* createGlobal6DhcpConfigParser(const std::string& config_id) {
         (config_id.compare("valid-lifetime") == 0)  ||
         (config_id.compare("renew-timer") == 0)  ||
         (config_id.compare("rebind-timer") == 0))  {
-        parser = new Uint32Parser(config_id, 
+        parser = new Uint32Parser(config_id,
                                  globalContext()->uint32_values_);
-    } else if (config_id.compare("interface") == 0) {
+    } else if (config_id.compare("interfaces") == 0) {
         parser = new InterfaceListConfigParser(config_id);
     } else if (config_id.compare("subnet6") == 0) {
         parser = new Subnets6ListConfigParser(config_id);
     } else if (config_id.compare("option-data") == 0) {
-        parser = new OptionDataListParser(config_id, 
-                                          globalContext()->options_, 
+        parser = new OptionDataListParser(config_id,
+                                          globalContext()->options_,
                                           globalContext(),
                                           Dhcp6OptionDataParser::factory);
     } else if (config_id.compare("option-def") == 0) {
-        parser  = new OptionDefListParser(config_id, 
+        parser  = new OptionDefListParser(config_id,
                                           globalContext()->option_defs_);
     } else if (config_id.compare("version") == 0) {
-        parser  = new StringParser(config_id, 
+        parser  = new StringParser(config_id,
                                    globalContext()->string_values_);
     } else if (config_id.compare("lease-database") == 0) {
         parser = new DbAccessParser(config_id);
+    } else if (config_id.compare("hooks-libraries") == 0) {
+        parser = new HooksLibrariesParser(config_id);
     } else {
         isc_throw(NotImplemented,
                 "Parser error: Global configuration parameter not supported: "
@@ -450,7 +452,7 @@ configureDhcp6Server(Dhcpv6Srv&, isc::data::ConstElementPtr config_set) {
     /// @todo: Append most essential info here (like "2 new subnets configured")
     string config_details;
 
-    LOG_DEBUG(dhcp6_logger, DBG_DHCP6_COMMAND, 
+    LOG_DEBUG(dhcp6_logger, DBG_DHCP6_COMMAND,
               DHCP6_CONFIG_START).arg(config_set->str());
 
     // Some of the values specified in the configuration depend on
@@ -463,6 +465,12 @@ configureDhcp6Server(Dhcpv6Srv&, isc::data::ConstElementPtr config_set) {
     ParserCollection independent_parsers;
     ParserPtr subnet_parser;
     ParserPtr option_parser;
+    ParserPtr iface_parser;
+
+    // Some of the parsers alter state of the system that can't easily
+    // be undone. (Or alter it in a way such that undoing the change
+    // has the same risk of failure as doing the change.)
+    ParserPtr hooks_parser;
 
     // The subnet parsers implement data inheritance by directly
     // accessing global storage. For this reason the global data
@@ -495,6 +503,18 @@ configureDhcp6Server(Dhcpv6Srv&, isc::data::ConstElementPtr config_set) {
                 subnet_parser = parser;
             } else if (config_pair.first == "option-data") {
                 option_parser = parser;
+            } else if (config_pair.first == "hooks-libraries") {
+                // Executing the commit will alter currently loaded hooks
+                // libraries. Check if the supplied libraries are valid,
+                // but defer the commit until after everything else has
+                // committed.
+                hooks_parser = parser;
+                hooks_parser->build(config_pair.second);
+            } else if (config_pair.first == "interfaces") {
+                // The interface parser is independent from any other parser and
+                // can be run here before other parsers.
+                parser->build(config_pair.second);
+                iface_parser = parser;
             } else {
                 // Those parsers should be started before other
                 // parsers so we can call build straight away.
@@ -548,6 +568,10 @@ configureDhcp6Server(Dhcpv6Srv&, isc::data::ConstElementPtr config_set) {
             if (subnet_parser) {
                 subnet_parser->commit();
             }
+
+            if (iface_parser) {
+                iface_parser->commit();
+            }
         }
         catch (const isc::Exception& ex) {
             LOG_ERROR(dhcp6_logger, DHCP6_PARSER_COMMIT_FAIL).arg(ex.what());
@@ -571,6 +595,11 @@ configureDhcp6Server(Dhcpv6Srv&, isc::data::ConstElementPtr config_set) {
         return (answer);
     }
 
+    // Now commit any changes that have been validated but not yet committed.
+    if (hooks_parser) {
+        hooks_parser->commit();
+    }
+
     LOG_INFO(dhcp6_logger, DHCP6_CONFIG_COMPLETE).arg(config_details);
 
     // Everything was fine. Configuration is successful.
diff --git a/src/bin/dhcp6/config_parser.h b/src/bin/dhcp6/config_parser.h
index 2dce79e..bd571af 100644
--- a/src/bin/dhcp6/config_parser.h
+++ b/src/bin/dhcp6/config_parser.h
@@ -31,8 +31,8 @@ class Dhcpv6Srv;
 
 /// @brief Configures DHCPv6 server
 ///
-/// This function is called every time a new configuration is received. The 
-/// extra parameter is a reference to DHCPv6 server component. It is currently 
+/// This function is called every time a new configuration is received. The
+/// extra parameter is a reference to DHCPv6 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
@@ -53,7 +53,7 @@ configureDhcp6Server(Dhcpv6Srv& server, isc::data::ConstElementPtr config_set);
 ///
 /// @returns a reference to the global context
 ParserContextPtr& globalContext();
- 
+
 }; // end of isc::dhcp namespace
 }; // end of isc namespace
 
diff --git a/src/bin/dhcp6/ctrl_dhcp6_srv.cc b/src/bin/dhcp6/ctrl_dhcp6_srv.cc
index c3488e5..dffac61 100644
--- a/src/bin/dhcp6/ctrl_dhcp6_srv.cc
+++ b/src/bin/dhcp6/ctrl_dhcp6_srv.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2013  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2013 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 <config/ccsession.h>
 #include <dhcp/iface_mgr.h>
 #include <dhcpsrv/dhcp_config_parser.h>
+#include <dhcpsrv/cfgmgr.h>
 #include <dhcp6/config_parser.h>
 #include <dhcp6/ctrl_dhcp6_srv.h>
 #include <dhcp6/dhcp6_log.h>
@@ -100,7 +101,27 @@ ControlledDhcpv6Srv::dhcp6ConfigHandler(ConstElementPtr new_config) {
     }
 
     // Configure the server.
-    return (configureDhcp6Server(*server_, merged_config));
+    ConstElementPtr answer = configureDhcp6Server(*server_, merged_config);
+
+    // Check that configuration was successful. If not, do not reopen sockets.
+    int rcode = 0;
+    parseAnswer(rcode, answer);
+    if (rcode != 0) {
+        return (answer);
+    }
+
+    // Configuration may change active interfaces. Therefore, we have to reopen
+    // sockets according to new configuration. This operation is not exception
+    // safe and we really don't want to emit exceptions to the callback caller.
+    // Instead, catch an exception and create appropriate answer.
+    try {
+        server_->openActiveSockets(server_->getPort());
+    } catch (const std::exception& ex) {
+        std::ostringstream err;
+        err << "failed to open sockets after server reconfiguration: " << ex.what();
+        answer = isc::config::createAnswer(1, err.str());
+    }
+    return (answer);
 }
 
 ConstElementPtr
@@ -172,8 +193,13 @@ void ControlledDhcpv6Srv::establishSession() {
     try {
         // Pull the full configuration out from the session.
         configureDhcp6Server(*this, config_session_->getFullConfig());
+        // Configuration may disable or enable interfaces so we have to
+        // reopen sockets according to new configuration.
+        openActiveSockets(getPort());
+
     } catch (const DhcpConfigError& ex) {
         LOG_ERROR(dhcp6_logger, DHCP6_CONFIG_LOAD_FAIL).arg(ex.what());
+
     }
 
     /// Integrate the asynchronous I/O model of BIND 10 configuration
@@ -228,6 +254,5 @@ ControlledDhcpv6Srv::execDhcpv6ServerCommand(const std::string& command_id,
     }
 }
 
-
 };
 };
diff --git a/src/bin/dhcp6/dhcp6.dox b/src/bin/dhcp6/dhcp6.dox
index 4376a2a..1396e9b 100644
--- a/src/bin/dhcp6/dhcp6.dox
+++ b/src/bin/dhcp6/dhcp6.dox
@@ -92,4 +92,8 @@
 
  @todo Add section about setting up options and their definitions with bindctl.
 
+ @section dhcpv6Other Other DHCPv6 topics
+
+ For hooks API support in DHCPv6, see @ref dhcpv6Hooks.
+
  */
diff --git a/src/bin/dhcp6/dhcp6.spec b/src/bin/dhcp6/dhcp6.spec
index bb5de0a..1235cfd 100644
--- a/src/bin/dhcp6/dhcp6.spec
+++ b/src/bin/dhcp6/dhcp6.spec
@@ -3,16 +3,29 @@
     "module_name": "Dhcp6",
     "module_description": "DHCPv6 server daemon",
     "config_data": [
-      { "item_name": "interface",
+      {
+        "item_name": "hooks-libraries",
+        "item_type": "list",
+        "item_optional": true,
+        "item_default": [],
+        "list_item_spec":
+        {
+          "item_name": "hooks-library",
+          "item_type": "string",
+          "item_optional": true,
+        }
+      },
+ 
+      { "item_name": "interfaces",
         "item_type": "list",
         "item_optional": false,
-        "item_default": [ "all" ],
+        "item_default": [ "*" ],
         "list_item_spec":
         {
           "item_name": "interface_name",
           "item_type": "string",
           "item_optional": false,
-          "item_default": "all"
+          "item_default": "*"
         }
       } ,
 
@@ -295,6 +308,11 @@
                     "item_optional": true
                 }
             ]
+        },
+
+        {
+            "command_name": "libreload",
+            "command_description": "Reloads the current hooks libraries.", 
         }
     ]
   }
diff --git a/src/bin/dhcp6/dhcp6_hooks.dox b/src/bin/dhcp6/dhcp6_hooks.dox
new file mode 100644
index 0000000..a584179
--- /dev/null
+++ b/src/bin/dhcp6/dhcp6_hooks.dox
@@ -0,0 +1,139 @@
+// Copyright (C) 2013  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.
+
+/**
+ @page dhcpv6Hooks The Hooks API for the DHCPv6 Server
+
+ @section dhcpv6HooksIntroduction Introduction
+ BIND10 features an API (the "Hooks" API) that allows user-written code to
+ be integrated into BIND 10 and called at specific points in its processing.
+ An overview of the API and a tutorial for writing such code can be found in
+ the @ref hooksDevelopersGuide.  Information for BIND 10 maintainers can be
+ found in the @ref hooksComponentDeveloperGuide.
+
+ This manual is more specialised and is aimed at developers of hook
+ code for the DHCPv6 server. It describes each hook point, what the callouts
+ attached to the hook are able to do, and the arguments passed to the
+ callouts.  Each entry in this manual has the following information:
+
+ - Name of the hook point.
+ - Arguments for the callout.  As well as the argument name and data type, the
+   information includes the direction, which can be one of:
+   - @b in - the server passes values to the callout but ignored any data
+     returned.
+   - @b out - the callout is expected to set this value.
+   - <b>in/out</b> - the server passes a value to the callout and uses whatever
+     value the callout sends back.  Note that the callout may choose not to
+     do any modification, in which case the server will use whatever value
+     it sent to the callout.
+ - Description of the hook. This explains where in the processing the hook
+   is located, the possible actions a callout attached to this hook could take,
+   and a description of the data passed to the callouts.
+ - Skip flag action: the action taken by the server if a callout chooses to set
+    the "skip" flag.
+
+ at section dhcpv6HooksHookPoints Hooks in the DHCPv6 Server
+
+The following list is ordered by appearance of specific hook points during
+packet processing. Hook points that are not specific to packet processing
+(e.g. lease expiration) will be added to the end of this list.
+
+ @subsection dhcpv6HooksPkt6Receive pkt6_receive
+
+ - @b Arguments:
+   - name: @b query6, type: isc::dhcp::Pkt6Ptr, direction: <b>in/out</b>
+
+ - @b Description: this callout is executed when an incoming DHCPv6
+   packet is received and its content is parsed. The sole argument -
+   query6 - contains a pointer to an isc::dhcp::Pkt6 object that contains
+   all information regarding incoming packet, including its source and
+   destination addresses, interface over which it was received, a list
+   of all options present within and relay information.  All fields of
+   the Pkt6 object can be modified at this time, except data_. (data_
+   contains the incoming packet as raw buffer. By the time this hook is
+   reached, that information has already parsed and is available though
+   other fields in the Pkt6 object.  For this reason, it doesn't make
+   sense to modify it.)
+
+ - <b>Skip flag action</b>: If any callout sets the skip flag, the server will
+   drop the packet and start processing the next one.  The reason for the drop
+   will be logged if logging is set to the appropriate debug level.
+
+ at subsection dhcpv6HooksSubnet6Select subnet6_select
+
+ - @b Arguments:
+   - name: @b query6, type: isc::dhcp::Pkt6Ptr, direction: <b>in/out</b>
+   - name: @b subnet6, type: isc::dhcp::Subnet6Ptr, direction: <b>in/out</b>
+   - name: @b subnet6collection, type: const isc::dhcp::Subnet6Collection *, direction: <b>in</b>
+
+ - @b Description: this callout is executed when a subnet is being
+   selected for the incoming packet. All parameters, addresses and
+   prefixes will be assigned from that subnet. A callout can select a
+   different subnet if it wishes so, the list of all subnets currently
+   configured being provided as 'subnet6collection'. The list itself must
+   not be modified.
+
+ - <b>Skip flag action</b>: If any callout installed on 'subnet6_select'
+   sets the skip flag, the server will not select any subnet. Packet processing
+   will continue, but will be severely limited (i.e. only global options
+   will be assigned).
+
+ at subsection dhcpv6HooksLeaseSelect lease6_select
+
+ - @b Arguments:
+   - name: @b subnet6, type: isc::dhcp::Subnet6Ptr, direction: <b>in</b>
+   - name: @b fake_allocation, type: bool, direction: <b>in</b>
+   - name: @b lease6, type: isc::dhcp::Lease6Ptr, direction: <b>in/out</b>
+
+ - @b Description: this callout is executed after the server engine
+   has selected a lease for client's request but before the lease
+   has been inserted into the database. Any modifications made to the
+   isc::dhcp::Lease6 object will be stored in the lease's record in the
+   database. The callout should make sure that any modifications are
+   sanity checked as the server will use that data as is with no further
+   checking.\n\n The server processes lease requests for SOLICIT and
+   REQUEST in a very similar way. The only major difference is that
+   for SOLICIT the lease is just selected; it is not inserted into
+   the database.  It is possible to distinguish between SOLICIT and
+   REQUEST by checking value of the fake_allocation flag: a value of true
+   means that the lease won't be inserted into the database (SOLICIT),
+   a value of false means that it will (REQUEST).
+
+ - <b>Skip flag action</b>: If any callout installed on 'lease6_select'
+   sets the skip flag, the server will not assign that particular lease.
+   Packet processing will continue and the client may get other addresses
+   or prefixes if it requested more than one address and/or prefix.
+
+ at subsection dhcpv6HooksPkt6Send pkt6_send
+
+ - @b Arguments:
+   - name: @b response6, type: isc::dhcp::Pkt6Ptr, direction: <b>in/out</b>
+
+ - @b Description: this callout is executed when server's response
+   is about to be send back to the client. The sole argument - response6 -
+   contains a pointer to an isc::dhcp::Pkt6 object that contains the
+   packet, with set source and destination addresses, interface over which
+   it will be send, list of all options and relay information.  All fields
+   of the Pkt6 object can be modified at this time, except bufferOut_.
+   (This is scratch space used for constructing the packet after all
+   pkt6_send callouts are complete, so any changes to that field will
+   be overwritten.)
+
+ - <b>Skip flag action</b>: if any callout sets the skip flag, the server
+   will drop this response packet. However, the original request packet
+   from a client was processed, so server's state was most likely changed
+   (e.g. lease was allocated). Setting this flag merely stops the change
+   being communicated to the client.
+
+*/
diff --git a/src/bin/dhcp6/dhcp6_log.h b/src/bin/dhcp6/dhcp6_log.h
index 23202da..244151c 100644
--- a/src/bin/dhcp6/dhcp6_log.h
+++ b/src/bin/dhcp6/dhcp6_log.h
@@ -38,6 +38,9 @@ const int DBG_DHCP6_COMMAND = DBGLVL_COMMAND;
 // Trace basic operations within the code.
 const int DBG_DHCP6_BASIC = DBGLVL_TRACE_BASIC;
 
+// Trace hook related operations
+const int DBG_DHCP6_HOOKS = DBGLVL_TRACE_BASIC;
+
 // Trace detailed operations, including errors raised when processing invalid
 // packets.  (These are not logged at severities of WARN or higher for fear
 // that a set of deliberately invalid packets set to the server could overwhelm
diff --git a/src/bin/dhcp6/dhcp6_messages.mes b/src/bin/dhcp6/dhcp6_messages.mes
index 6b61473..f85fcb4 100644
--- a/src/bin/dhcp6/dhcp6_messages.mes
+++ b/src/bin/dhcp6/dhcp6_messages.mes
@@ -14,6 +14,11 @@
 
 $NAMESPACE isc::dhcp
 
+% DHCP6_ACTIVATE_INTERFACE activating interface %1
+This message is printed when DHCPv6 server enabled an interface to be used
+to receive DHCPv6 traffic. IPv6 socket on this interface will be opened once
+Interface Manager starts up procedure of opening sockets.
+
 % DHCP6_CCSESSION_STARTED control channel session started on socket %1
 A debug message issued during startup after the IPv6 DHCP server has
 successfully established a session with the BIND 10 control channel.
@@ -65,6 +70,32 @@ This informational message is printed every time the IPv6 DHCP server
 is started.  It indicates what database backend type is being to store
 lease and other information.
 
+% DHCP6_DEACTIVATE_INTERFACE deactivate interface %1
+This message is printed when DHCPv6 server disables an interface from being
+used to receive DHCPv6 traffic. Sockets on this interface will not be opened
+by the Interface Manager until interface is enabled.
+
+% DHCP6_HOOK_PACKET_RCVD_SKIP received DHCPv6 packet was dropped, because a callout set skip flag.
+This debug message is printed when a callout installed on the pkt6_receive
+hook point sets the skip flag. For this particular hook point, the
+setting of the flag by a callout instructs the server to drop the packet.
+
+% DHCP6_HOOK_PACKET_SEND_SKIP prepared DHCPv6 response was not sent, because a callout set skip flag.
+This debug message is printed when a callout installed on the pkt6_send
+hook point sets the skip flag. For this particular hook point, the setting
+of the flag by a callout instructs the server to drop the packet. This
+effectively means that the client will not get any response, even though
+the server processed client's request and acted on it (e.g. possibly
+allocated a lease).
+
+% DHCP6_HOOK_SUBNET6_SELECT_SKIP no subnet was selected, because a callout set skip flag.
+This debug message is printed when a callout installed on the
+subnet6_select hook point sets the skip flag. For this particular hook
+point, the setting of the flag instructs the server not to choose a
+subnet, an action that severely limits further processing; the server
+will be only able to offer global options - no addresses or prefixes
+will be assigned.
+
 % DHCP6_LEASE_ADVERT lease %1 advertised (client duid=%2, iaid=%3)
 This debug message indicates that the server successfully advertised
 a lease. It is up to the client to choose one server out of the
@@ -101,6 +132,11 @@ IPv6 DHCP server but it is not running.
 During startup the IPv6 DHCP server failed to detect any network
 interfaces and is therefore shutting down.
 
+% DHCP6_NO_SOCKETS_OPEN no interface configured to listen to DHCP traffic
+This warning message is issued when current server configuration specifies
+no interfaces that server should listen on, or specified interfaces are not
+configured to receive the traffic.
+
 % DHCP6_OPEN_SOCKET opening sockets on port %1
 A debug message issued during startup, this indicates that the IPv6 DHCP
 server is about to open sockets on the specified port.
@@ -159,10 +195,10 @@ actions and committal of changes failed.  The message has been output in
 response to a non-BIND 10 exception being raised.  Additional messages
 may give further information.
 
-The most likely cause of this is that the specification file for the server
-(which details the allowable contents of the configuration) is not correct for
-this version of BIND 10.  This former may be the result of an interrupted
-installation of an update to BIND 10.
+The most likely cause of this is that the specification file for the
+server (which details the allowable contents of the configuration) is
+not correct for this version of BIND 10.  This may be the result of an
+interrupted installation of an update to BIND 10.
 
 % DHCP6_PARSER_FAIL failed to create or run parser for configuration element %1: %2
 On receipt of message containing details to a change of its configuration,
diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc
index ef69a94..b97e82a 100644
--- a/src/bin/dhcp6/dhcp6_srv.cc
+++ b/src/bin/dhcp6/dhcp6_srv.cc
@@ -37,6 +37,8 @@
 #include <util/io_utilities.h>
 #include <util/range_utilities.h>
 #include <util/encode/hex.h>
+#include <hooks/hooks_manager.h>
+#include <hooks/callout_handle.h>
 
 #include <boost/foreach.hpp>
 #include <boost/tokenizer.hpp>
@@ -50,9 +52,34 @@
 using namespace isc;
 using namespace isc::asiolink;
 using namespace isc::dhcp;
+using namespace isc::hooks;
 using namespace isc::util;
 using namespace std;
 
+namespace {
+
+/// Structure that holds registered hook indexes
+struct Dhcp6Hooks {
+    int hook_index_pkt6_receive_;   ///< index for "pkt6_receive" hook point
+    int hook_index_subnet6_select_; ///< index for "subnet6_select" hook point
+    int hook_index_pkt6_send_;      ///< index for "pkt6_send" hook point
+
+    /// Constructor that registers hook points for DHCPv6 engine
+    Dhcp6Hooks() {
+        hook_index_pkt6_receive_   = HooksManager::registerHook("pkt6_receive");
+        hook_index_subnet6_select_ = HooksManager::registerHook("subnet6_select");
+        hook_index_pkt6_send_      = HooksManager::registerHook("pkt6_send");
+    }
+};
+
+// Declare a Hooks object. As this is outside any function or method, it
+// will be instantiated (and the constructor run) when the module is loaded.
+// As a result, the hook indexes will be defined before any method in this
+// module is called.
+Dhcp6Hooks Hooks;
+
+}; // anonymous namespace
+
 namespace isc {
 namespace dhcp {
 
@@ -67,7 +94,9 @@ namespace dhcp {
 static const char* SERVER_DUID_FILE = "b10-dhcp6-serverid";
 
 Dhcpv6Srv::Dhcpv6Srv(uint16_t port)
-    : alloc_engine_(), serverid_(), shutdown_(true) {
+:alloc_engine_(), serverid_(), shutdown_(true), hook_index_pkt6_receive_(-1),
+    hook_index_subnet6_select_(-1), hook_index_pkt6_send_(-1), port_(port)
+{
 
     LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_OPEN_SOCKET).arg(port);
 
@@ -82,7 +111,7 @@ Dhcpv6Srv::Dhcpv6Srv(uint16_t port)
                 LOG_ERROR(dhcp6_logger, DHCP6_NO_INTERFACES);
                 return;
             }
-            IfaceMgr::instance().openSockets6(port);
+            IfaceMgr::instance().openSockets6(port_);
         }
 
         string duid_file = CfgMgr::instance().getDataDir() + "/" + string(SERVER_DUID_FILE);
@@ -106,6 +135,13 @@ Dhcpv6Srv::Dhcpv6Srv(uint16_t port)
         // Instantiate allocation engine
         alloc_engine_.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100));
 
+        // Register hook points
+        hook_index_pkt6_receive_   = Hooks.hook_index_pkt6_receive_;
+        hook_index_subnet6_select_ = Hooks.hook_index_subnet6_select_;
+        hook_index_pkt6_send_      = Hooks.hook_index_pkt6_send_;
+
+        /// @todo call loadLibraries() when handling configuration changes
+
     } catch (const std::exception &e) {
         LOG_ERROR(dhcp6_logger, DHCP6_SRV_CONSTRUCT_ERROR).arg(e.what());
         return;
@@ -126,9 +162,17 @@ void Dhcpv6Srv::shutdown() {
     shutdown_ = true;
 }
 
+Pkt6Ptr Dhcpv6Srv::receivePacket(int timeout) {
+    return (IfaceMgr::instance().receive6(timeout));
+}
+
+void Dhcpv6Srv::sendPacket(const Pkt6Ptr& packet) {
+    IfaceMgr::instance().send(packet);
+}
+
 bool Dhcpv6Srv::run() {
     while (!shutdown_) {
-        /// @todo: calculate actual timeout to the next event (e.g. lease
+        /// @todo Calculate actual timeout to the next event (e.g. lease
         /// expiration) once we have lease database. The idea here is that
         /// it is possible to do everything in a single process/thread.
         /// For now, we are just calling select for 1000 seconds. There
@@ -142,7 +186,7 @@ bool Dhcpv6Srv::run() {
         Pkt6Ptr rsp;
 
         try {
-            query = IfaceMgr::instance().receive6(timeout);
+            query = receivePacket(timeout);
         } catch (const std::exception& e) {
             LOG_ERROR(dhcp6_logger, DHCP6_PACKET_RECEIVE_FAIL).arg(e.what());
         }
@@ -160,6 +204,30 @@ bool Dhcpv6Srv::run() {
                       .arg(query->getBuffer().getLength())
                       .arg(query->toText());
 
+            // Let's execute all callouts registered for packet_received
+            if (HooksManager::getHooksManager().calloutsPresent(hook_index_pkt6_receive_)) {
+                CalloutHandlePtr callout_handle = getCalloutHandle(query);
+
+                // Delete previously set arguments
+                callout_handle->deleteAllArguments();
+
+                // Pass incoming packet as argument
+                callout_handle->setArgument("query6", query);
+
+                // Call callouts
+                HooksManager::callCallouts(hook_index_pkt6_receive_, *callout_handle);
+
+                // Callouts decided to skip the next processing step. The next
+                // processing step would to process the packet, so skip at this
+                // stage means drop.
+                if (callout_handle->getSkip()) {
+                    LOG_DEBUG(dhcp6_logger, DBG_DHCP6_HOOKS, DHCP6_HOOK_PACKET_RCVD_SKIP);
+                    continue;
+                }
+
+                callout_handle->getArgument("query6", query);
+            }
+
             try {
                 switch (query->getType()) {
                 case DHCPV6_SOLICIT:
@@ -204,7 +272,7 @@ bool Dhcpv6Srv::run() {
             } catch (const RFCViolation& e) {
                 LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, DHCP6_REQUIRED_OPTIONS_CHECK_FAIL)
                     .arg(query->getName())
-                    .arg(query->getRemoteAddr())
+                    .arg(query->getRemoteAddr().toText())
                     .arg(e.what());
 
             } catch (const isc::Exception& e) {
@@ -218,7 +286,7 @@ bool Dhcpv6Srv::run() {
                 // packets.)
                 LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, DHCP6_PACKET_PROCESS_FAIL)
                     .arg(query->getName())
-                    .arg(query->getRemoteAddr())
+                    .arg(query->getRemoteAddr().toText())
                     .arg(e.what());
             }
 
@@ -230,13 +298,35 @@ bool Dhcpv6Srv::run() {
                 rsp->setIndex(query->getIndex());
                 rsp->setIface(query->getIface());
 
+                // Execute all callouts registered for packet6_send
+                if (HooksManager::getHooksManager().calloutsPresent(hook_index_pkt6_send_)) {
+                    CalloutHandlePtr callout_handle = getCalloutHandle(query);
+
+                    // Delete all previous arguments
+                    callout_handle->deleteAllArguments();
+
+                    // Set our response
+                    callout_handle->setArgument("response6", rsp);
+
+                    // Call all installed callouts
+                    HooksManager::callCallouts(hook_index_pkt6_send_, *callout_handle);
+
+                    // Callouts decided to skip the next processing step. The next
+                    // processing step would to send the packet, so skip at this
+                    // stage means "drop response".
+                    if (callout_handle->getSkip()) {
+                        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_HOOKS, DHCP6_HOOK_PACKET_SEND_SKIP);
+                        continue;
+                    }
+                }
+
                 LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL_DATA,
                           DHCP6_RESPONSE_DATA)
                     .arg(static_cast<int>(rsp->getType())).arg(rsp->toText());
 
                 if (rsp->pack()) {
                     try {
-                        IfaceMgr::instance().send(rsp);
+                        sendPacket(rsp);
                     } catch (const std::exception& e) {
                         LOG_ERROR(dhcp6_logger, DHCP6_PACKET_SEND_FAIL).arg(e.what());
                     }
@@ -560,6 +650,37 @@ Dhcpv6Srv::selectSubnet(const Pkt6Ptr& question) {
         }
     }
 
+    // Let's execute all callouts registered for packet_received
+    if (HooksManager::getHooksManager().calloutsPresent(hook_index_subnet6_select_)) {
+        CalloutHandlePtr callout_handle = getCalloutHandle(question);
+
+        // We're reusing callout_handle from previous calls
+        callout_handle->deleteAllArguments();
+
+        // Set new arguments
+        callout_handle->setArgument("query6", question);
+        callout_handle->setArgument("subnet6", subnet);
+
+        // We pass pointer to const collection for performance reasons.
+        // Otherwise we would get a non-trivial performance penalty each
+        // time subnet6_select is called.
+        callout_handle->setArgument("subnet6collection", CfgMgr::instance().getSubnets6());
+
+        // Call user (and server-side) callouts
+        HooksManager::callCallouts(hook_index_subnet6_select_, *callout_handle);
+
+        // Callouts decided to skip this step. This means that no subnet will be
+        // selected. Packet processing will continue, but it will be severly limited
+        // (i.e. only global options will be assigned)
+        if (callout_handle->getSkip()) {
+            LOG_DEBUG(dhcp6_logger, DBG_DHCP6_HOOKS, DHCP6_HOOK_SUBNET6_SELECT_SKIP);
+            return (Subnet6Ptr());
+        }
+
+        // Use whatever subnet was specified by the callout
+        callout_handle->getArgument("subnet6", subnet);
+    }
+
     return (subnet);
 }
 
@@ -619,7 +740,8 @@ Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer) {
         switch (opt->second->getType()) {
         case D6O_IA_NA: {
             OptionPtr answer_opt = assignIA_NA(subnet, duid, question,
-                                   boost::dynamic_pointer_cast<Option6IA>(opt->second));
+                                               boost::dynamic_pointer_cast<Option6IA>(opt->second),
+                                               question);
             if (answer_opt) {
                 answer->addOption(answer_opt);
             }
@@ -633,7 +755,7 @@ Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer) {
 
 OptionPtr
 Dhcpv6Srv::assignIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
-                       Pkt6Ptr question, boost::shared_ptr<Option6IA> ia) {
+                       Pkt6Ptr question, boost::shared_ptr<Option6IA> ia, const Pkt6Ptr& query) {
     // 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,
@@ -677,12 +799,15 @@ Dhcpv6Srv::assignIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
         fake_allocation = true;
     }
 
+    CalloutHandlePtr callout_handle = getCalloutHandle(query);
+
     // Use allocation engine to pick a lease for this client. Allocation engine
     // will try to honour the hint, but it is just a hint - some other address
     // may be used instead. If fake_allocation is set to false, the lease will
     // be inserted into the LeaseMgr as well.
     Lease6Ptr lease = alloc_engine_->allocateAddress6(subnet, duid, ia->getIAID(),
-                                                      hint, fake_allocation);
+                                                      hint, fake_allocation,
+                                                      callout_handle);
 
     // Create IA_NA that we will put in the response.
     // Do not use OptionDefinition to create option's instance so
@@ -1103,5 +1228,74 @@ Dhcpv6Srv::processInfRequest(const Pkt6Ptr& infRequest) {
     return reply;
 }
 
+isc::hooks::CalloutHandlePtr Dhcpv6Srv::getCalloutHandle(const Pkt6Ptr& pkt) {
+    // This method returns a CalloutHandle for a given packet. It is guaranteed
+    // to return the same callout_handle (so user library contexts are
+    // preserved). This method works well if the server processes one packet
+    // at a time. Once the server architecture is extended to cover parallel
+    // packets processing (e.g. delayed-ack, some form of buffering etc.), this
+    // method has to be extended (e.g. store callouts in a map and use pkt as
+    // a key). Additional code would be required to release the callout handle
+    // once the server finished processing.
+
+    CalloutHandlePtr callout_handle;
+    static Pkt6Ptr old_pointer;
+
+    if (!callout_handle ||
+        old_pointer != pkt) {
+        // This is the first packet or a different packet than previously
+        // passed to getCalloutHandle()
+
+        // Remember the pointer to this packet
+        old_pointer = pkt;
+
+        callout_handle = HooksManager::getHooksManager().createCalloutHandle();
+    }
+
+    return (callout_handle);
+}
+
+void
+Dhcpv6Srv::openActiveSockets(const uint16_t port) {
+    IfaceMgr::instance().closeSockets();
+
+    // Get the reference to the collection of interfaces. This reference should be
+    // valid as long as the program is run because IfaceMgr is a singleton.
+    // Therefore we can safely iterate over instances of all interfaces and modify
+    // their flags. Here we modify flags which indicate wheter socket should be
+    // open for a particular interface or not.
+    const IfaceMgr::IfaceCollection& ifaces = IfaceMgr::instance().getIfaces();
+    for (IfaceMgr::IfaceCollection::const_iterator iface = ifaces.begin();
+         iface != ifaces.end(); ++iface) {
+        Iface* iface_ptr = IfaceMgr::instance().getIface(iface->getName());
+        if (iface_ptr == NULL) {
+            isc_throw(isc::Unexpected, "Interface Manager returned NULL"
+                      << " instance of the interface when DHCPv6 server was"
+                      << " trying to reopen sockets after reconfiguration");
+        }
+        if (CfgMgr::instance().isActiveIface(iface->getName())) {
+            iface_ptr->inactive4_ = false;
+            LOG_INFO(dhcp6_logger, DHCP6_ACTIVATE_INTERFACE)
+                .arg(iface->getFullName());
+
+        } else {
+            // For deactivating interface, it should be sufficient to log it
+            // on the debug level because it is more useful to know what
+            // interface is activated which is logged on the info level.
+            LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC,
+                      DHCP6_DEACTIVATE_INTERFACE).arg(iface->getName());
+            iface_ptr->inactive6_ = true;
+
+        }
+    }
+    // Let's reopen active sockets. openSockets6 will check internally whether
+    // sockets are marked active or inactive.
+    // @todo Optimization: we should not reopen all sockets but rather select
+    // those that have been affected by the new configuration.
+    if (!IfaceMgr::instance().openSockets6(port)) {
+        LOG_WARN(dhcp6_logger, DHCP6_NO_SOCKETS_OPEN);
+    }
+}
+
 };
 };
diff --git a/src/bin/dhcp6/dhcp6_srv.h b/src/bin/dhcp6/dhcp6_srv.h
index c7b1f0f..0a4fafd 100644
--- a/src/bin/dhcp6/dhcp6_srv.h
+++ b/src/bin/dhcp6/dhcp6_srv.h
@@ -23,6 +23,7 @@
 #include <dhcp/pkt6.h>
 #include <dhcpsrv/alloc_engine.h>
 #include <dhcpsrv/subnet.h>
+#include <hooks/callout_handle.h>
 
 #include <boost/noncopyable.hpp>
 
@@ -87,6 +88,32 @@ public:
     /// @brief Instructs the server to shut down.
     void shutdown();
 
+    /// @brief Get UDP port on which server should listen.
+    ///
+    /// Typically, server listens on UDP port 547. Other ports are only
+    /// used for testing purposes.
+    ///
+    /// This accessor must be public because sockets are reopened from the
+    /// static configuration callback handler. This callback handler invokes
+    /// @c ControlledDhcpv4Srv::openActiveSockets which requires port parameter
+    /// which has to be retrieved from the @c ControlledDhcpv4Srv object.
+    /// They are retrieved using this public function.
+    ///
+    /// @return UDP port on which server should listen.
+    uint16_t getPort() const {
+        return (port_);
+    }
+
+    /// @brief Open sockets which are marked as active in @c CfgMgr.
+    ///
+    /// This function reopens sockets according to the current settings in the
+    /// Configuration Manager. It holds the list of the interfaces which server
+    /// should listen on. This function will open sockets on these interfaces
+    /// only. This function is not exception safe.
+    ///
+    /// @param port UDP port on which server should listen.
+    static void openActiveSockets(const uint16_t port);
+
 protected:
 
     /// @brief verifies if specified packet meets RFC requirements
@@ -189,7 +216,8 @@ protected:
     OptionPtr assignIA_NA(const isc::dhcp::Subnet6Ptr& subnet,
                           const isc::dhcp::DuidPtr& duid,
                           isc::dhcp::Pkt6Ptr question,
-                          boost::shared_ptr<Option6IA> ia);
+                          boost::shared_ptr<Option6IA> ia,
+                          const Pkt6Ptr& query);
 
     /// @brief Renews specific IA_NA option
     ///
@@ -321,6 +349,19 @@ protected:
     /// @return string representation
     static std::string duidToString(const OptionPtr& opt);
 
+
+    /// @brief dummy wrapper around IfaceMgr::receive6
+    ///
+    /// This method is useful for testing purposes, where its replacement
+    /// simulates reception of a packet. For that purpose it is protected.
+    virtual Pkt6Ptr receivePacket(int timeout);
+
+    /// @brief dummy wrapper around IfaceMgr::send()
+    ///
+    /// This method is useful for testing purposes, where its replacement
+    /// simulates transmission of a packet. For that purpose it is protected.
+    virtual void sendPacket(const Pkt6Ptr& pkt);
+
 private:
     /// @brief Allocation Engine.
     /// Pointer to the allocation engine that we are currently using
@@ -334,6 +375,21 @@ private:
     /// Indicates if shutdown is in progress. Setting it to true will
     /// initiate server shutdown procedure.
     volatile bool shutdown_;
+
+    /// @brief returns callout handle for specified packet
+    ///
+    /// @param pkt packet for which the handle should be returned
+    ///
+    /// @return a callout handle to be used in hooks related to said packet
+    isc::hooks::CalloutHandlePtr getCalloutHandle(const Pkt6Ptr& pkt);
+
+    /// Indexes for registered hook points
+    int hook_index_pkt6_receive_;
+    int hook_index_subnet6_select_;
+    int hook_index_pkt6_send_;
+
+    /// UDP port number on which server listens.
+    uint16_t port_;
 };
 
 }; // namespace isc::dhcp
diff --git a/src/bin/dhcp6/tests/Makefile.am b/src/bin/dhcp6/tests/Makefile.am
index a2e2ed0..be40ea5 100644
--- a/src/bin/dhcp6/tests/Makefile.am
+++ b/src/bin/dhcp6/tests/Makefile.am
@@ -27,7 +27,8 @@ AM_CPPFLAGS += -I$(top_srcdir)/src/bin
 AM_CPPFLAGS += $(BOOST_INCLUDES)
 AM_CPPFLAGS += -DINSTALL_PROG=\"$(abs_top_srcdir)/install-sh\"
 
-CLEANFILES = $(builddir)/interfaces.txt $(builddir)/logger_lockfile
+CLEANFILES  = $(builddir)/interfaces.txt $(builddir)/logger_lockfile
+CLEANFILES += $(builddir)/load_marker.txt $(builddir)/unload_marker.txt
 
 AM_CXXFLAGS = $(B10_CXXFLAGS)
 if USE_CLANGPP
@@ -44,9 +45,18 @@ TESTS_ENVIRONMENT = \
 
 TESTS =
 if HAVE_GTEST
+# Build shared libraries for testing.
+lib_LTLIBRARIES = libco1.la libco2.la
 
-TESTS += dhcp6_unittests
+libco1_la_SOURCES  = callout_library_1.cc callout_library_common.h
+libco1_la_CXXFLAGS = $(AM_CXXFLAGS)
+libco1_la_CPPFLAGS = $(AM_CPPFLAGS)
+
+libco2_la_SOURCES  = callout_library_2.cc callout_library_common.h
+libco2_la_CXXFLAGS = $(AM_CXXFLAGS)
+libco2_la_CPPFLAGS = $(AM_CPPFLAGS)
 
+TESTS += dhcp6_unittests
 dhcp6_unittests_SOURCES  = dhcp6_unittests.cc
 dhcp6_unittests_SOURCES += dhcp6_srv_unittest.cc
 dhcp6_unittests_SOURCES += ctrl_dhcp6_srv_unittest.cc
@@ -55,7 +65,9 @@ dhcp6_unittests_SOURCES += ../dhcp6_srv.h ../dhcp6_srv.cc
 dhcp6_unittests_SOURCES += ../dhcp6_log.h ../dhcp6_log.cc
 dhcp6_unittests_SOURCES += ../ctrl_dhcp6_srv.cc
 dhcp6_unittests_SOURCES += ../config_parser.cc ../config_parser.h
-nodist_dhcp6_unittests_SOURCES = ../dhcp6_messages.h ../dhcp6_messages.cc
+
+nodist_dhcp6_unittests_SOURCES  = ../dhcp6_messages.h ../dhcp6_messages.cc
+nodist_dhcp6_unittests_SOURCES += marker_file.h test_libraries.h
 
 dhcp6_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 dhcp6_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
@@ -65,6 +77,7 @@ dhcp6_unittests_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
 dhcp6_unittests_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
 dhcp6_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
 dhcp6_unittests_LDADD += $(top_builddir)/src/lib/dhcpsrv/libb10-dhcpsrv.la
+dhcp6_unittests_LDADD += $(top_builddir)/src/lib/hooks/libb10-hooks.la
 dhcp6_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
 dhcp6_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
 dhcp6_unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
diff --git a/src/bin/dhcp6/tests/callout_library_1.cc b/src/bin/dhcp6/tests/callout_library_1.cc
new file mode 100644
index 0000000..471bb6f
--- /dev/null
+++ b/src/bin/dhcp6/tests/callout_library_1.cc
@@ -0,0 +1,22 @@
+// Copyright (C) 2013  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.
+
+/// @file
+/// @brief Marker file callout library
+///
+/// This is the source of a test library for the DHCP parser and configuration
+/// tests.  See callout_common.cc for details.
+
+static const int LIBRARY_NUMBER = 1;
+#include "callout_library_common.h"
diff --git a/src/bin/dhcp6/tests/callout_library_2.cc b/src/bin/dhcp6/tests/callout_library_2.cc
new file mode 100644
index 0000000..b0b4637
--- /dev/null
+++ b/src/bin/dhcp6/tests/callout_library_2.cc
@@ -0,0 +1,22 @@
+// Copyright (C) 2013  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.
+
+/// @file
+/// @brief Marker file callout library
+///
+/// This is the source of a test library for the DHCP parser and configuration
+/// tests.  See callout_common.cc for details.
+
+static const int LIBRARY_NUMBER = 2;
+#include "callout_library_common.h"
diff --git a/src/bin/dhcp6/tests/callout_library_common.h b/src/bin/dhcp6/tests/callout_library_common.h
new file mode 100644
index 0000000..e8d4b5a
--- /dev/null
+++ b/src/bin/dhcp6/tests/callout_library_common.h
@@ -0,0 +1,80 @@
+// Copyright (C) 2013  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.
+
+/// @file
+/// @brief Marker file callout library
+///
+/// This is the source of a test library for the DHCP parser and configuration
+/// tests.
+///
+/// To check that they libraries are loaded and unloaded correctly, the load
+/// and unload functions in this library maintain two marker files - the load
+/// marker file and the unload marker file.  The functions append a single
+/// to the single line in the file, creating the file if need be.  In
+/// this way, the test code can determine whether the load/unload functions
+/// have been run and, if so, in what order.
+///
+/// This file is the common library file for the tests.  It will not compile
+/// by itself - it is included into each callout library which specifies the
+/// missing constant LIBRARY_NUMBER before the inclusion.
+
+#include <hooks/hooks.h>
+#include "marker_file.h"
+
+#include <fstream>
+
+using namespace isc::hooks;
+using namespace std;
+
+extern "C" {
+
+/// @brief Append digit to marker file
+///
+/// If the marker file does not exist, create it.  Then append the single
+/// digit (given by the constant LIBRARY_NUMBER) defined earlier to it and
+/// close the file.
+///
+/// @param name Name of the file to open
+///
+/// @return 0 on success, non-zero on error.
+int
+appendDigit(const char* name) {
+    // Open the file and check if successful.
+    fstream file(name, fstream::out | fstream::app);
+    if (!file.good()) {
+        return (1);
+    }
+
+    // Add the library number to it and close.
+    file << LIBRARY_NUMBER;
+    file.close();
+
+    return (0);
+}
+
+// Framework functions
+int
+version() {
+    return (BIND10_HOOKS_VERSION);
+}
+
+int load(LibraryHandle&) {
+    return (appendDigit(LOAD_MARKER_FILE));
+}
+
+int unload() {
+    return (appendDigit(UNLOAD_MARKER_FILE));
+}
+
+};
diff --git a/src/bin/dhcp6/tests/config_parser_unittest.cc b/src/bin/dhcp6/tests/config_parser_unittest.cc
index d9c5859..16f460a 100644
--- a/src/bin/dhcp6/tests/config_parser_unittest.cc
+++ b/src/bin/dhcp6/tests/config_parser_unittest.cc
@@ -24,15 +24,23 @@
 #include <dhcp6/dhcp6_srv.h>
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/subnet.h>
+#include <hooks/hooks_manager.h>
+
+#include "test_libraries.h"
+#include "marker_file.h"
 
 #include <boost/foreach.hpp>
 #include <gtest/gtest.h>
 
 #include <fstream>
 #include <iostream>
+#include <fstream>
 #include <sstream>
+#include <string>
+#include <vector>
 
 #include <arpa/inet.h>
+#include <unistd.h>
 
 using namespace std;
 using namespace isc;
@@ -40,6 +48,7 @@ using namespace isc::dhcp;
 using namespace isc::asiolink;
 using namespace isc::data;
 using namespace isc::config;
+using namespace isc::hooks;
 
 namespace {
 
@@ -66,11 +75,18 @@ public:
                           << " while the test assumes that it doesn't, to execute"
                           << " some negative scenarios. Can't continue this test.";
         }
+
+        // Reset configuration for each test.
+        resetConfiguration();
     }
 
     ~Dhcp6ParserTest() {
         // Reset configuration database after each test.
         resetConfiguration();
+
+        // ... and delete the hooks library marker files if present
+        unlink(LOAD_MARKER_FILE);
+        unlink(UNLOAD_MARKER_FILE);
     };
 
     // Checks if config_result (result of DHCP server configuration) has
@@ -133,7 +149,7 @@ public:
                                        std::string>& params)
     {
         std::ostringstream stream;
-        stream << "{ \"interface\": [ \"all\" ],"
+        stream << "{ \"interfaces\": [ \"*\" ],"
             "\"preferred-lifetime\": 3000,"
             "\"rebind-timer\": 2000, "
             "\"renew-timer\": 1000, "
@@ -169,50 +185,80 @@ public:
         return (stream.str());
     }
 
-    /// @brief Reset configuration database.
+    /// @brief Parse configuration
     ///
-    /// This function resets configuration data base by
-    /// removing all subnets and option-data. Reset must
-    /// be performed after each test to make sure that
-    /// contents of the database do not affect result of
-    /// subsequent tests.
-    void resetConfiguration() {
+    /// Parses a configuration and executes a configuration of the server.
+    /// If the operation fails, the current test will register a failure.
+    ///
+    /// @param config Configuration to parse
+    /// @param operation Operation being performed.  In the case of an error,
+    ///        the error text will include the string "unable to <operation>.".
+    ///
+    /// @return true if the configuration succeeded, false if not.  In the
+    ///         latter case, a failure will have been added to the current test.
+    bool
+    executeConfiguration(const std::string& config, const char* operation) {
         ConstElementPtr status;
-
-        string config = "{ \"interface\": [ \"all\" ],"
-            "\"preferred-lifetime\": 3000,"
-            "\"rebind-timer\": 2000, "
-            "\"renew-timer\": 1000, "
-            "\"valid-lifetime\": 4000, "
-            "\"subnet6\": [ ], "
-            "\"option-def\": [ ], "
-            "\"option-data\": [ ] }";
-
         try {
             ElementPtr json = Element::fromJSON(config);
             status = configureDhcp6Server(srv_, json);
         } catch (const std::exception& ex) {
-            FAIL() << "Fatal error: unable to reset configuration database"
-                   << " after the test. The following configuration was used"
-                   << " to reset database: " << std::endl
+            ADD_FAILURE() << "Unable to " << operation << ". "
+                   << "The following configuration was used: " << std::endl
                    << config << std::endl
                    << " and the following error message was returned:"
                    << ex.what() << std::endl;
+            return (false);
         }
 
-        // status object must not be NULL
+        // The status object must not be NULL
         if (!status) {
-            FAIL() << "Fatal error: unable to reset configuration database"
-                   << " after the test. Configuration function returned"
-                   << " NULL pointer" << std::endl;
+            ADD_FAILURE() << "Unable to " << operation << ". "
+                   << "The configuration function returned a null pointer.";
+            return (false);
         }
+
+        // Store the answer if we need it.
+
+        // Returned value should be 0 (configuration success)
         comment_ = parseAnswer(rcode_, status);
-        // returned value should be 0 (configuration success)
         if (rcode_ != 0) {
-            FAIL() << "Fatal error: unable to reset configuration database"
-                   << " after the test. Configuration function returned"
-                   << " error code " << rcode_ << std::endl;
+            string reason = "";
+            if (comment_) {
+                reason = string(" (") + comment_->stringValue() + string(")");
+            }
+            ADD_FAILURE() << "Unable to " << operation << ". "
+                   << "The configuration function returned error code "
+                   << rcode_ << reason;
+            return (false);
         }
+
+        return (true);
+    }
+
+    /// @brief Reset configuration database.
+    ///
+    /// This function resets configuration data base by removing all subnets
+    /// option-data, and hooks libraries. The reset must be performed after each
+    /// test to make sure that contents of the database do not affect the
+    /// results of subsequent tests.
+    void resetConfiguration() {
+        string config = "{ \"interfaces\": [ \"all\" ],"
+            "\"hooks-libraries\": [ ],"
+            "\"preferred-lifetime\": 3000,"
+            "\"rebind-timer\": 2000, "
+            "\"renew-timer\": 1000, "
+            "\"valid-lifetime\": 4000, "
+            "\"subnet6\": [ ], "
+            "\"option-def\": [ ], "
+            "\"option-data\": [ ] }";
+        static_cast<void>(executeConfiguration(config,
+                                               "reset configuration database"));
+        // The default setting is to listen on all interfaces. In order to
+        // properly test interface configuration we disable listening on
+        // all interfaces before each test and later check that this setting
+        // has been overriden by the configuration used in the test.
+        CfgMgr::instance().deleteActiveIfaces();
     }
 
     /// @brief Test invalid option parameter value.
@@ -277,13 +323,66 @@ public:
                             expected_data_len));
     }
 
-    int rcode_; ///< return core (see @ref isc::config::parseAnswer)
-    Dhcpv6Srv srv_; ///< instance of the Dhcp6Srv used during tests
+    /// @brief Check marker file
+    ///
+    /// Marker files are used by the load/unload functions in the hooks
+    /// libraries in these tests to signal whether they have been loaded or
+    /// unloaded.  The file (if present) contains a single line holding
+    /// a set of characters.
+    ///
+    /// This convenience function checks the file to see if the characters
+    /// are those expected.
+    ///
+    /// @param name Name of the marker file.
+    /// @param expected Characters expected.  If a marker file is present,
+    ///        it is expected to contain characters.  Therefore a value of NULL
+    ///        is used to signify that the marker file is not expected to be
+    ///        present.
+    ///
+    /// @return true if all tests pass, false if not (in which case a failure
+    ///         will have been logged).
+    bool
+    checkMarkerFile(const char* name, const char* expected) {
+        // Open the file for input
+        fstream file(name, fstream::in);
+
+        // Is it open?
+        if (!file.is_open()) {
+
+            // No.  This is OK if we don't expected is to be present but is
+            // a failure otherwise.
+            if (expected == NULL) {
+                return (true);
+            }
+            ADD_FAILURE() << "Unable to open " << name << ". It was expected "
+                          << "to be present and to contain the string '"
+                          << expected << "'";
+            return (false);
+        } else if (expected == NULL) {
+
+            // File is open but we don't expect it to be present.
+            ADD_FAILURE() << "Opened " << name << " but it is not expected to "
+                          << "be present.";
+            return (false);
+        }
+
+        // OK, is open, so read the data and see what we have.  Compare it
+        // against what is expected.
+        string content;
+        getline(file, content);
 
-    ConstElementPtr comment_; ///< comment (see @ref isc::config::parseAnswer)
+        string expected_str(expected);
+        EXPECT_EQ(expected_str, content) << "Data was read from " << name;
+        file.close();
 
-    string valid_iface_; ///< name of a valid network interface (present in system)
-    string bogus_iface_; ///< name of a invalid network interface (not present in system)
+        return (expected_str == content);
+    }
+
+    int rcode_;          ///< Return code (see @ref isc::config::parseAnswer)
+    Dhcpv6Srv srv_;      ///< Instance of the Dhcp6Srv used during tests
+    ConstElementPtr comment_; ///< Comment (see @ref isc::config::parseAnswer)
+    string valid_iface_; ///< Valid network interface name (present in system)
+    string bogus_iface_; ///< invalid network interface name (not in system)
 };
 
 // Goal of this test is a verification if a very simple config update
@@ -324,7 +423,7 @@ TEST_F(Dhcp6ParserTest, emptySubnet) {
     ConstElementPtr status;
 
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_,
-                    Element::fromJSON("{ \"interface\": [ \"all\" ],"
+                    Element::fromJSON("{ \"interfaces\": [ \"*\" ],"
                                       "\"preferred-lifetime\": 3000,"
                                       "\"rebind-timer\": 2000, "
                                       "\"renew-timer\": 1000, "
@@ -343,7 +442,7 @@ TEST_F(Dhcp6ParserTest, subnetGlobalDefaults) {
 
     ConstElementPtr status;
 
-    string config = "{ \"interface\": [ \"all\" ],"
+    string config = "{ \"interfaces\": [ \"*\" ],"
         "\"preferred-lifetime\": 3000,"
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
@@ -377,7 +476,7 @@ TEST_F(Dhcp6ParserTest, subnetLocal) {
 
     ConstElementPtr status;
 
-    string config = "{ \"interface\": [ \"all\" ],"
+    string config = "{ \"interfaces\": [ \"*\" ],"
         "\"preferred-lifetime\": 3000,"
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
@@ -415,7 +514,7 @@ TEST_F(Dhcp6ParserTest, subnetInterface) {
 
     // There should be at least one interface
 
-    string config = "{ \"interface\": [ \"all\" ],"
+    string config = "{ \"interfaces\": [ \"*\" ],"
         "\"preferred-lifetime\": 3000,"
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
@@ -448,7 +547,7 @@ TEST_F(Dhcp6ParserTest, subnetInterfaceBogus) {
 
     // There should be at least one interface
 
-    string config = "{ \"interface\": [ \"all\" ],"
+    string config = "{ \"interfaces\": [ \"*\" ],"
         "\"preferred-lifetime\": 3000,"
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
@@ -479,7 +578,7 @@ TEST_F(Dhcp6ParserTest, interfaceGlobal) {
 
     ConstElementPtr status;
 
-    string config = "{ \"interface\": [ \"all\" ],"
+    string config = "{ \"interfaces\": [ \"*\" ],"
         "\"preferred-lifetime\": 3000,"
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
@@ -549,7 +648,7 @@ TEST_F(Dhcp6ParserTest, subnetInterfaceId) {
 // parameter.
 TEST_F(Dhcp6ParserTest, interfaceIdGlobal) {
 
-    const string config = "{ \"interface\": [ \"all\" ],"
+    const string config = "{ \"interfaces\": [ \"*\" ],"
         "\"preferred-lifetime\": 3000,"
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
@@ -604,7 +703,7 @@ TEST_F(Dhcp6ParserTest, poolOutOfSubnet) {
 
     ConstElementPtr status;
 
-    string config = "{ \"interface\": [ \"all\" ],"
+    string config = "{ \"interfaces\": [ \"*\" ],"
         "\"preferred-lifetime\": 3000,"
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
@@ -632,7 +731,7 @@ TEST_F(Dhcp6ParserTest, poolPrefixLen) {
 
     ConstElementPtr x;
 
-    string config = "{ \"interface\": [ \"all\" ],"
+    string config = "{ \"interfaces\": [ \"*\" ],"
         "\"preferred-lifetime\": 3000,"
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
@@ -1152,7 +1251,7 @@ TEST_F(Dhcp6ParserTest, optionStandardDefOverride) {
 // configuration does not include options configuration.
 TEST_F(Dhcp6ParserTest, optionDataDefaults) {
     ConstElementPtr x;
-    string config = "{ \"interface\": [ \"all\" ],"
+    string config = "{ \"interfaces\": [ \"*\" ],"
         "\"preferred-lifetime\": 3000,"
         "\"rebind-timer\": 2000,"
         "\"renew-timer\": 1000,"
@@ -1234,7 +1333,7 @@ TEST_F(Dhcp6ParserTest, optionDataTwoSpaces) {
     // The definition is not required for the option that
     // belongs to the 'dhcp6' option space as it is the
     // standard option.
-    string config = "{ \"interface\": [ \"all\" ],"
+    string config = "{ \"interfaces\": [ \"*\" ],"
         "\"rebind-timer\": 2000,"
         "\"renew-timer\": 1000,"
         "\"option-data\": [ {"
@@ -1312,7 +1411,7 @@ TEST_F(Dhcp6ParserTest, optionDataEncapsulate) {
     // at the very end (when all other parameters are configured).
 
     // Starting stage 1. Configure sub-options and their definitions.
-    string config = "{ \"interface\": [ \"all\" ],"
+    string config = "{ \"interfaces\": [ \"*\" ],"
         "\"rebind-timer\": 2000,"
         "\"renew-timer\": 1000,"
         "\"option-data\": [ {"
@@ -1361,7 +1460,7 @@ TEST_F(Dhcp6ParserTest, optionDataEncapsulate) {
     // the configuration from the stage 2 is repeated because BIND
     // configuration manager sends whole configuration for the lists
     // where at least one element is being modified or added.
-    config = "{ \"interface\": [ \"all\" ],"
+    config = "{ \"interfaces\": [ \"*\" ],"
         "\"rebind-timer\": 2000,"
         "\"renew-timer\": 1000,"
         "\"option-data\": [ {"
@@ -1455,7 +1554,7 @@ TEST_F(Dhcp6ParserTest, optionDataEncapsulate) {
 // for multiple subnets.
 TEST_F(Dhcp6ParserTest, optionDataInMultipleSubnets) {
     ConstElementPtr x;
-    string config = "{ \"interface\": [ \"all\" ],"
+    string config = "{ \"interfaces\": [ \"*\" ],"
         "\"preferred-lifetime\": 3000,"
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
@@ -1698,7 +1797,7 @@ TEST_F(Dhcp6ParserTest, stdOptionDataEncapsulate) {
     // In the first stahe we create definitions of suboptions
     // that we will add to the base option.
     // Let's create some dummy options: foo and foo2.
-    string config = "{ \"interface\": [ \"all\" ],"
+    string config = "{ \"interfaces\": [ \"*\" ],"
         "\"rebind-timer\": 2000,"
         "\"renew-timer\": 1000,"
         "\"option-data\": [ {"
@@ -1751,7 +1850,7 @@ TEST_F(Dhcp6ParserTest, stdOptionDataEncapsulate) {
     // We add our dummy options to this option space and thus
     // they should be included as sub-options in the 'vendor-opts'
     // option.
-    config = "{ \"interface\": [ \"all\" ],"
+    config = "{ \"interfaces\": [ \"*\" ],"
         "\"rebind-timer\": 2000,"
         "\"renew-timer\": 1000,"
         "\"option-data\": [ {"
@@ -1850,4 +1949,231 @@ TEST_F(Dhcp6ParserTest, stdOptionDataEncapsulate) {
     EXPECT_FALSE(desc.option->getOption(112));
 }
 
+// Tests of the hooks libraries configuration.
+
+// Helper function to return a configuration containing an arbitrary number
+// of hooks libraries.
+std::string
+buildHooksLibrariesConfig(const std::vector<std::string>& libraries) {
+    const string quote("\"");
+
+    // Create the first part of the configuration string.
+    string config =
+        "{ \"interfaces\": [ \"all\" ],"
+            "\"hooks-libraries\": [";
+
+    // Append the libraries (separated by commas if needed)
+    for (int i = 0; i < libraries.size(); ++i) {
+        if (i > 0) {
+            config += string(", ");
+        }
+        config += (quote + libraries[i] + quote);
+    }
+
+    // Append the remainder of the configuration.
+    config += string(
+        "],"
+        "\"rebind-timer\": 2000,"
+        "\"renew-timer\": 1000,"
+        "\"option-data\": [ {"
+        "    \"name\": \"foo\","
+        "    \"space\": \"vendor-opts-space\","
+        "    \"code\": 110,"
+        "    \"data\": \"1234\","
+        "    \"csv-format\": True"
+        " },"
+        " {"
+        "    \"name\": \"foo2\","
+        "    \"space\": \"vendor-opts-space\","
+        "    \"code\": 111,"
+        "    \"data\": \"192.168.2.1\","
+        "    \"csv-format\": True"
+        " } ],"
+        "\"option-def\": [ {"
+        "    \"name\": \"foo\","
+        "    \"code\": 110,"
+        "    \"type\": \"uint32\","
+        "    \"array\": False,"
+        "    \"record-types\": \"\","
+        "    \"space\": \"vendor-opts-space\","
+        "    \"encapsulate\": \"\""
+        " },"
+        " {"
+        "    \"name\": \"foo2\","
+        "    \"code\": 111,"
+        "    \"type\": \"ipv4-address\","
+        "    \"array\": False,"
+        "    \"record-types\": \"\","
+        "    \"space\": \"vendor-opts-space\","
+        "    \"encapsulate\": \"\""
+        " } ]"
+        "}");
+
+    return (config);
+}
+
+// Convenience function for creating hooks library configuration with one or
+// two character string constants.
+std::string
+buildHooksLibrariesConfig(const char* library1 = NULL,
+                          const char* library2 = NULL) {
+    std::vector<std::string> libraries;
+    if (library1 != NULL) {
+        libraries.push_back(string(library1));
+        if (library2 != NULL) {
+            libraries.push_back(string(library2));
+        }
+    }
+    return (buildHooksLibrariesConfig(libraries));
+}
+
+
+// The goal of this test is to verify the configuration of hooks libraries if
+// none are specified.
+TEST_F(Dhcp6ParserTest, NoHooksLibraries) {
+//    std::vector<std::string> libraries = HooksManager::getLibraryNames();
+//   ASSERT_TRUE(libraries.empty());
+
+    // Parse a configuration containing no names.
+    string config = buildHooksLibrariesConfig();
+    if (!executeConfiguration(config,
+                              "set configuration with no hooks libraries")) {
+        return;
+    }
+//    libraries = HooksManager::getLibraryNames();
+//   ASSERT_TRUE(libraries.empty());
+}
+
+// Verify parsing fails with one library that will fail validation.
+TEST_F(Dhcp6ParserTest, InvalidLibrary) {
+//    std::vector<std::string> libraries = HooksManager::getLibraryNames();
+//   ASSERT_TRUE(libraries.empty());
+
+    // Parse a configuration containing a failing library.
+    string config = buildHooksLibrariesConfig(NOT_PRESENT_LIBRARY);
+
+    ConstElementPtr status;
+    ElementPtr json = Element::fromJSON(config);
+    ASSERT_NO_THROW(status = configureDhcp6Server(srv_, json));
+
+    // The status object must not be NULL
+    ASSERT_FALSE(status);
+
+    // Returned value should not be 0
+    comment_ = parseAnswer(rcode_, status);
+    EXPECT_NE(0, rcode_);
+    std::cerr << "Reason for success: " << comment_;
+}
+
+// Verify the configuration of hooks libraries with two being specified.
+TEST_F(Dhcp6ParserTest, LibrariesSpecified) {
+//    std::vector<std::string> libraries = HooksManager::getLibraryNames();
+//   ASSERT_TRUE(libraries.empty());
+
+    // Marker files should not be present.
+    EXPECT_TRUE(checkMarkerFile(LOAD_MARKER_FILE, NULL));
+    EXPECT_TRUE(checkMarkerFile(UNLOAD_MARKER_FILE, NULL));
+
+    // Set up the configuration with two libraries and load them.
+    string config = buildHooksLibrariesConfig(CALLOUT_LIBRARY_1,
+                                              CALLOUT_LIBRARY_2);
+    ASSERT_TRUE(executeConfiguration(config,
+                                     "load two valid libraries"));
+
+    // Expect two libraries to be loaded in the correct order (load marker file
+    // is present, no unload marker file).
+//   std::vector<std::string> libraries = HooksManager::getLibraryNames();
+//   ASSERT_EQ(2, libraries.size());
+     EXPECT_TRUE(checkMarkerFile(LOAD_MARKER_FILE, "12"));
+     EXPECT_TRUE(checkMarkerFile(UNLOAD_MARKER_FILE, NULL));
+
+    // Unload the libraries.  The load file should not have changed, but
+    // the unload one should indicate the unload() functions have been run.
+    config = buildHooksLibrariesConfig();
+    ASSERT_TRUE(executeConfiguration(config, "unloading libraries"));
+    EXPECT_TRUE(checkMarkerFile(LOAD_MARKER_FILE, "12"));
+    EXPECT_TRUE(checkMarkerFile(UNLOAD_MARKER_FILE, "21"));
+
+    // Expect the hooks system to say that none are loaded.
+    // libraries = HooksManager::getLibraryNames();
+    // EXPECT_TRUE(libraries.empty());
+
+    }
+//    libraries = HooksManager::getLibraryNames();
+//   ASSERT_TRUE(libraries.empty());
+
+
+// This test verifies that it is possible to select subset of interfaces on
+// which server should listen.
+TEST_F(Dhcp6ParserTest, selectedInterfaces) {
+
+    // Make sure there is no garbage interface configuration in the CfgMgr.
+    ASSERT_FALSE(CfgMgr::instance().isActiveIface("eth0"));
+    ASSERT_FALSE(CfgMgr::instance().isActiveIface("eth1"));
+    ASSERT_FALSE(CfgMgr::instance().isActiveIface("eth2"));
+
+    ConstElementPtr status;
+
+    string config = "{ \"interfaces\": [ \"eth0\", \"eth1\" ],"
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"valid-lifetime\": 4000 }";
+
+
+    ElementPtr json = Element::fromJSON(config);
+
+    EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
+
+    // returned value must be 1 (values error)
+    // as the pool does not belong to that subnet
+    ASSERT_TRUE(status);
+    comment_ = parseAnswer(rcode_, status);
+    EXPECT_EQ(0, rcode_);
+
+    // eth0 and eth1 were explicitly selected. eth2 was not.
+    EXPECT_TRUE(CfgMgr::instance().isActiveIface("eth0"));
+    EXPECT_TRUE(CfgMgr::instance().isActiveIface("eth1"));
+    EXPECT_FALSE(CfgMgr::instance().isActiveIface("eth2"));
+}
+
+// This test verifies that it is possible to configure the server to listen on
+// all interfaces.
+TEST_F(Dhcp6ParserTest, allInterfaces) {
+
+    // Make sure there is no garbage interface configuration in the CfgMgr.
+    ASSERT_FALSE(CfgMgr::instance().isActiveIface("eth0"));
+    ASSERT_FALSE(CfgMgr::instance().isActiveIface("eth1"));
+    ASSERT_FALSE(CfgMgr::instance().isActiveIface("eth2"));
+
+    ConstElementPtr status;
+
+    // This configuration specifies two interfaces on which server should listen
+    // bu also includes keyword 'all'. This keyword switches server into the
+    // mode when it listens on all interfaces regardless of what interface names
+    // were specified in the "interfaces" parameter.
+    string config = "{ \"interfaces\": [ \"eth0\", \"eth1\", \"*\" ],"
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"valid-lifetime\": 4000 }";
+
+
+    ElementPtr json = Element::fromJSON(config);
+
+    EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
+
+    // returned value must be 1 (values error)
+    // as the pool does not belong to that subnet
+    ASSERT_TRUE(status);
+    comment_ = parseAnswer(rcode_, status);
+    EXPECT_EQ(0, rcode_);
+
+    // All interfaces should be now active.
+    EXPECT_TRUE(CfgMgr::instance().isActiveIface("eth0"));
+    EXPECT_TRUE(CfgMgr::instance().isActiveIface("eth1"));
+    EXPECT_TRUE(CfgMgr::instance().isActiveIface("eth2"));
+}
+
+
 };
diff --git a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
index 328a3c5..c5ced8d 100644
--- a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
+++ b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
@@ -24,14 +24,18 @@
 #include <dhcp/option6_ia.h>
 #include <dhcp/option6_iaaddr.h>
 #include <dhcp/option_int_array.h>
+#include <dhcp/iface_mgr.h>
 #include <dhcp6/config_parser.h>
 #include <dhcp6/dhcp6_srv.h>
+#include <dhcp/dhcp6.h>
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/lease_mgr.h>
 #include <dhcpsrv/lease_mgr_factory.h>
 #include <dhcpsrv/utils.h>
 #include <util/buffer.h>
 #include <util/range_utilities.h>
+#include <hooks/server_hooks.h>
+#include <hooks/hooks_manager.h>
 
 #include <boost/scoped_ptr.hpp>
 #include <gtest/gtest.h>
@@ -39,6 +43,7 @@
 #include <fstream>
 #include <iostream>
 #include <sstream>
+#include <list>
 
 using namespace isc;
 using namespace isc::asiolink;
@@ -46,6 +51,7 @@ using namespace isc::config;
 using namespace isc::data;
 using namespace isc::dhcp;
 using namespace isc::util;
+using namespace isc::hooks;
 using namespace std;
 
 // namespace has to be named, because friends are defined in Dhcpv6Srv class
@@ -61,6 +67,46 @@ public:
         LeaseMgrFactory::create(memfile);
     }
 
+    /// @brief fakes packet reception
+    /// @param timeout ignored
+    ///
+    /// The method receives all packets queued in receive
+    /// queue, one after another. Once the queue is empty,
+    /// it initiates the shutdown procedure.
+    ///
+    /// See fake_received_ field for description
+    virtual Pkt6Ptr receivePacket(int /*timeout*/) {
+
+        // If there is anything prepared as fake incoming
+        // traffic, use it
+        if (!fake_received_.empty()) {
+            Pkt6Ptr pkt = fake_received_.front();
+            fake_received_.pop_front();
+            return (pkt);
+        }
+
+        // If not, just trigger shutdown and
+        // return immediately
+        shutdown();
+        return (Pkt6Ptr());
+    }
+
+    /// @brief fake packet sending
+    ///
+    /// Pretend to send a packet, but instead just store
+    /// it in fake_send_ list where test can later inspect
+    /// server's response.
+    virtual void sendPacket(const Pkt6Ptr& pkt) {
+        fake_sent_.push_back(pkt);
+    }
+
+    /// @brief adds a packet to fake receive queue
+    ///
+    /// See fake_received_ field for description
+    void fakeReceive(const Pkt6Ptr& pkt) {
+        fake_received_.push_back(pkt);
+    }
+
     virtual ~NakedDhcpv6Srv() {
         // Close the lease database
         LeaseMgrFactory::destroy();
@@ -75,6 +121,16 @@ public:
     using Dhcpv6Srv::sanityCheck;
     using Dhcpv6Srv::loadServerID;
     using Dhcpv6Srv::writeServerID;
+
+    /// @brief packets we pretend to receive
+    ///
+    /// Instead of setting up sockets on interfaces that change between OSes, it
+    /// is much easier to fake packet reception. This is a list of packets that
+    /// we pretend to have received. You can schedule new packets to be received
+    /// using fakeReceive() and NakedDhcpv6Srv::receivePacket() methods.
+    list<Pkt6Ptr> fake_received_;
+
+    list<Pkt6Ptr> fake_sent_;
 };
 
 static const char* DUID_FILE = "server-id-test.txt";
@@ -87,6 +143,16 @@ public:
     NakedDhcpv6SrvTest() : rcode_(-1) {
         // it's ok if that fails. There should not be such a file anyway
         unlink(DUID_FILE);
+
+        const IfaceMgr::IfaceCollection& ifaces = IfaceMgr::instance().getIfaces();
+
+        // There must be some interface detected
+        if (ifaces.empty()) {
+            // We can't use ASSERT in constructor
+            ADD_FAILURE() << "No interfaces detected.";
+        }
+
+        valid_iface_ = ifaces.begin()->getName();
     }
 
     // Generate IA_NA option with specified parameters
@@ -228,6 +294,13 @@ public:
     virtual ~NakedDhcpv6SrvTest() {
         // Let's clean up if there is such a file.
         unlink(DUID_FILE);
+        HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts(
+                                                 "pkt6_receive");
+        HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts(
+                                                 "pkt6_send");
+        HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts(
+                                                 "subnet6_select");
+
     };
 
     // A DUID used in most tests (typically as client-id)
@@ -235,6 +308,9 @@ public:
 
     int rcode_;
     ConstElementPtr comment_;
+
+    // Name of a valid network interface
+    string valid_iface_;
 };
 
 // Provides suport for tests against a preconfigured subnet6
@@ -480,7 +556,7 @@ TEST_F(Dhcpv6SrvTest, DUID) {
 
     boost::scoped_ptr<Dhcpv6Srv> srv;
     ASSERT_NO_THROW( {
-        srv.reset(new Dhcpv6Srv(0));
+        srv.reset(new NakedDhcpv6Srv(0));
     });
 
     OptionPtr srvid = srv->getServerID();
@@ -555,7 +631,7 @@ TEST_F(Dhcpv6SrvTest, DUID) {
 // and the requested options are actually assigned.
 TEST_F(Dhcpv6SrvTest, advertiseOptions) {
     ConstElementPtr x;
-    string config = "{ \"interface\": [ \"all\" ],"
+    string config = "{ \"interfaces\": [ \"all\" ],"
         "\"preferred-lifetime\": 3000,"
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
@@ -1756,6 +1832,689 @@ TEST_F(Dhcpv6SrvTest, ServerID) {
     EXPECT_EQ(duid1_text, text);
 }
 
+// Checks if hooks are implemented properly.
+TEST_F(Dhcpv6SrvTest, Hooks) {
+    NakedDhcpv6Srv srv(0);
+
+    // check if appropriate hooks are registered
+    int hook_index_pkt6_received = -1;
+    int hook_index_select_subnet = -1;
+    int hook_index_pkt6_send     = -1;
+
+    // check if appropriate indexes are set
+    EXPECT_NO_THROW(hook_index_pkt6_received = ServerHooks::getServerHooks()
+                    .getIndex("pkt6_receive"));
+    EXPECT_NO_THROW(hook_index_select_subnet = ServerHooks::getServerHooks()
+                    .getIndex("subnet6_select"));
+    EXPECT_NO_THROW(hook_index_pkt6_send     = ServerHooks::getServerHooks()
+                    .getIndex("pkt6_send"));
+
+    EXPECT_TRUE(hook_index_pkt6_received > 0);
+    EXPECT_TRUE(hook_index_select_subnet > 0);
+    EXPECT_TRUE(hook_index_pkt6_send > 0);
+}
+
+// This function returns buffer for very simple Solicit
+Pkt6* captureSimpleSolicit() {
+    Pkt6* pkt;
+    uint8_t data[] = {
+        1,  // type 1 = SOLICIT
+        0xca, 0xfe, 0x01, // trans-id = 0xcafe01
+        0, 1, // option type 1 (client-id)
+        0, 10, // option lenth 10
+        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, // DUID
+        0, 3, // option type 3 (IA_NA)
+        0, 12, // option length 12
+        0, 0, 0, 1, // iaid = 1
+        0, 0, 0, 0, // T1 = 0
+        0, 0, 0, 0  // T2 = 0
+    };
+
+    pkt = new Pkt6(data, sizeof(data));
+    pkt->setRemotePort(546);
+    pkt->setRemoteAddr(IOAddress("fe80::1"));
+    pkt->setLocalPort(0);
+    pkt->setLocalAddr(IOAddress("ff02::1:2"));
+    pkt->setIndex(2);
+    pkt->setIface("eth0");
+
+    return (pkt);
+}
+
+/// @brief a class dedicated to Hooks testing in DHCPv6 server
+///
+/// This class has a number of static members, because each non-static
+/// method has implicit 'this' parameter, so it does not match callout
+/// signature and couldn't be registered. Furthermore, static methods
+/// can't modify non-static members (for obvious reasons), so many
+/// fields are declared static. It is still better to keep them as
+/// one class rather than unrelated collection of global objects.
+class HooksDhcpv6SrvTest : public Dhcpv6SrvTest {
+
+public:
+
+    /// @brief creates Dhcpv6Srv and prepares buffers for callouts
+    HooksDhcpv6SrvTest() {
+
+        // Allocate new DHCPv6 Server
+        srv_ = new NakedDhcpv6Srv(0);
+
+        // clear static buffers
+        resetCalloutBuffers();
+    }
+
+    /// @brief destructor (deletes Dhcpv6Srv)
+    ~HooksDhcpv6SrvTest() {
+        delete srv_;
+    }
+
+    /// @brief creates an option with specified option code
+    ///
+    /// This method is static, because it is used from callouts
+    /// that do not have a pointer to HooksDhcpv6SSrvTest object
+    ///
+    /// @param option_code code of option to be created
+    ///
+    /// @return pointer to create option object
+    static OptionPtr createOption(uint16_t option_code) {
+
+        char payload[] = {
+            0xa, 0xb, 0xc, 0xe, 0xf, 0x10, 0x11, 0x12, 0x13, 0x14
+        };
+
+        OptionBuffer tmp(payload, payload + sizeof(payload));
+        return OptionPtr(new Option(Option::V6, option_code, tmp));
+    }
+
+    /// test callback that stores received callout name and pkt6 value
+    /// @param callout_handle handle passed by the hooks framework
+    /// @return always 0
+    static int
+    pkt6_receive_callout(CalloutHandle& callout_handle) {
+        callback_name_ = string("pkt6_receive");
+
+        callout_handle.getArgument("query6", callback_pkt6_);
+
+        callback_argument_names_ = callout_handle.getArgumentNames();
+        return (0);
+    }
+
+    /// test callback that changes client-id value
+    /// @param callout_handle handle passed by the hooks framework
+    /// @return always 0
+    static int
+    pkt6_receive_change_clientid(CalloutHandle& callout_handle) {
+
+        Pkt6Ptr pkt;
+        callout_handle.getArgument("query6", pkt);
+
+        // get rid of the old client-id
+        pkt->delOption(D6O_CLIENTID);
+
+        // add a new option
+        pkt->addOption(createOption(D6O_CLIENTID));
+
+        // carry on as usual
+        return pkt6_receive_callout(callout_handle);
+    }
+
+    /// test callback that deletes client-id
+    /// @param callout_handle handle passed by the hooks framework
+    /// @return always 0
+    static int
+    pkt6_receive_delete_clientid(CalloutHandle& callout_handle) {
+
+        Pkt6Ptr pkt;
+        callout_handle.getArgument("query6", pkt);
+
+        // get rid of the old client-id
+        pkt->delOption(D6O_CLIENTID);
+
+        // carry on as usual
+        return pkt6_receive_callout(callout_handle);
+    }
+
+    /// test callback that sets skip flag
+    /// @param callout_handle handle passed by the hooks framework
+    /// @return always 0
+    static int
+    pkt6_receive_skip(CalloutHandle& callout_handle) {
+
+        Pkt6Ptr pkt;
+        callout_handle.getArgument("query6", pkt);
+
+        callout_handle.setSkip(true);
+
+        // carry on as usual
+        return pkt6_receive_callout(callout_handle);
+    }
+
+    /// Test callback that stores received callout name and pkt6 value
+    /// @param callout_handle handle passed by the hooks framework
+    /// @return always 0
+    static int
+    pkt6_send_callout(CalloutHandle& callout_handle) {
+        callback_name_ = string("pkt6_send");
+
+        callout_handle.getArgument("response6", callback_pkt6_);
+
+        callback_argument_names_ = callout_handle.getArgumentNames();
+        return (0);
+    }
+
+    // Test callback that changes server-id
+    /// @param callout_handle handle passed by the hooks framework
+    /// @return always 0
+    static int
+    pkt6_send_change_serverid(CalloutHandle& callout_handle) {
+
+        Pkt6Ptr pkt;
+        callout_handle.getArgument("response6", pkt);
+
+        // get rid of the old server-id
+        pkt->delOption(D6O_SERVERID);
+
+        // add a new option
+        pkt->addOption(createOption(D6O_SERVERID));
+
+        // carry on as usual
+        return pkt6_send_callout(callout_handle);
+    }
+
+    /// test callback that deletes server-id
+    /// @param callout_handle handle passed by the hooks framework
+    /// @return always 0
+    static int
+    pkt6_send_delete_serverid(CalloutHandle& callout_handle) {
+
+        Pkt6Ptr pkt;
+        callout_handle.getArgument("response6", pkt);
+
+        // get rid of the old client-id
+        pkt->delOption(D6O_SERVERID);
+
+        // carry on as usual
+        return pkt6_send_callout(callout_handle);
+    }
+
+    /// Test callback that sets skip flag
+    /// @param callout_handle handle passed by the hooks framework
+    /// @return always 0
+    static int
+    pkt6_send_skip(CalloutHandle& callout_handle) {
+
+        Pkt6Ptr pkt;
+        callout_handle.getArgument("response6", pkt);
+
+        callout_handle.setSkip(true);
+
+        // carry on as usual
+        return pkt6_send_callout(callout_handle);
+    }
+
+    /// Test callback that stores received callout name and subnet6 values
+    /// @param callout_handle handle passed by the hooks framework
+    /// @return always 0
+    static int
+    subnet6_select_callout(CalloutHandle& callout_handle) {
+        callback_name_ = string("subnet6_select");
+
+        callout_handle.getArgument("query6", callback_pkt6_);
+        callout_handle.getArgument("subnet6", callback_subnet6_);
+        callout_handle.getArgument("subnet6collection", callback_subnet6collection_);
+
+        callback_argument_names_ = callout_handle.getArgumentNames();
+        return (0);
+    }
+
+    /// Test callback that picks the other subnet if possible.
+    /// @param callout_handle handle passed by the hooks framework
+    /// @return always 0
+    static int
+    subnet6_select_different_subnet_callout(CalloutHandle& callout_handle) {
+
+        // Call the basic calllout to record all passed values
+        subnet6_select_callout(callout_handle);
+
+        const Subnet6Collection* subnets;
+        Subnet6Ptr subnet;
+        callout_handle.getArgument("subnet6", subnet);
+        callout_handle.getArgument("subnet6collection", subnets);
+
+        // Let's change to a different subnet
+        if (subnets->size() > 1) {
+            subnet = (*subnets)[1]; // Let's pick the other subnet
+            callout_handle.setArgument("subnet6", subnet);
+        }
+
+        return (0);
+    }
+
+    /// resets buffers used to store data received by callouts
+    void resetCalloutBuffers() {
+        callback_name_ = string("");
+        callback_pkt6_.reset();
+        callback_subnet6_.reset();
+        callback_subnet6collection_ = NULL;
+        callback_argument_names_.clear();
+    }
+
+    /// pointer to Dhcpv6Srv that is used in tests
+    NakedDhcpv6Srv* srv_;
+
+    // The following fields are used in testing pkt6_receive_callout
+
+    /// String name of the received callout
+    static string callback_name_;
+
+    /// Pkt6 structure returned in the callout
+    static Pkt6Ptr callback_pkt6_;
+
+    /// Pointer to a subnet received by callout
+    static Subnet6Ptr callback_subnet6_;
+
+    /// A list of all available subnets (received by callout)
+    static const Subnet6Collection* callback_subnet6collection_;
+
+    /// A list of all received arguments
+    static vector<string> callback_argument_names_;
+};
+
+// The following fields are used in testing pkt6_receive_callout.
+// See fields description in the class for details
+string HooksDhcpv6SrvTest::callback_name_;
+Pkt6Ptr HooksDhcpv6SrvTest::callback_pkt6_;
+Subnet6Ptr HooksDhcpv6SrvTest::callback_subnet6_;
+const Subnet6Collection* HooksDhcpv6SrvTest::callback_subnet6collection_;
+vector<string> HooksDhcpv6SrvTest::callback_argument_names_;
+
+
+// Checks if callouts installed on pkt6_received are indeed called and the
+// all necessary parameters are passed.
+//
+// Note that the test name does not follow test naming convention,
+// but the proper hook name is "pkt6_receive".
+TEST_F(HooksDhcpv6SrvTest, simple_pkt6_receive) {
+
+    // Install pkt6_receive_callout
+    EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                        "pkt6_receive", pkt6_receive_callout));
+
+    // Let's create a simple SOLICIT
+    Pkt6Ptr sol = Pkt6Ptr(captureSimpleSolicit());
+
+    // Simulate that we have received that traffic
+    srv_->fakeReceive(sol);
+
+    // Server will now process to run its normal loop, but instead of calling
+    // IfaceMgr::receive6(), it will read all packets from the list set by
+    // fakeReceive()
+    // In particular, it should call registered pkt6_receive callback.
+    srv_->run();
+
+    // check that the callback called is indeed the one we installed
+    EXPECT_EQ("pkt6_receive", callback_name_);
+
+    // check that pkt6 argument passing was successful and returned proper value
+    EXPECT_TRUE(callback_pkt6_.get() == sol.get());
+
+    // Check that all expected parameters are there
+    vector<string> expected_argument_names;
+    expected_argument_names.push_back(string("query6"));
+
+    EXPECT_TRUE(expected_argument_names == callback_argument_names_);
+}
+
+// Checks if callouts installed on pkt6_received is able to change
+// the values and the parameters are indeed used by the server.
+TEST_F(HooksDhcpv6SrvTest, valueChange_pkt6_receive) {
+
+    // Install pkt6_receive_callout
+    EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                        "pkt6_receive", pkt6_receive_change_clientid));
+
+    // Let's create a simple SOLICIT
+    Pkt6Ptr sol = Pkt6Ptr(captureSimpleSolicit());
+
+    // Simulate that we have received that traffic
+    srv_->fakeReceive(sol);
+
+    // Server will now process to run its normal loop, but instead of calling
+    // IfaceMgr::receive6(), it will read all packets from the list set by
+    // fakeReceive()
+    // In particular, it should call registered pkt6_receive callback.
+    srv_->run();
+
+    // check that the server did send a reposonce
+    ASSERT_EQ(1, srv_->fake_sent_.size());
+
+    // Make sure that we received a response
+    Pkt6Ptr adv = srv_->fake_sent_.front();
+    ASSERT_TRUE(adv);
+
+    // Get client-id...
+    OptionPtr clientid = adv->getOption(D6O_CLIENTID);
+
+    // ... and check if it is the modified value
+    OptionPtr expected = createOption(D6O_CLIENTID);
+    EXPECT_TRUE(clientid->equal(expected));
+}
+
+// Checks if callouts installed on pkt6_received is able to delete
+// existing options and that change impacts server processing (mandatory
+// client-id option is deleted, so the packet is expected to be dropped)
+TEST_F(HooksDhcpv6SrvTest, deleteClientId_pkt6_receive) {
+
+    // Install pkt6_receive_callout
+    EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                        "pkt6_receive", pkt6_receive_delete_clientid));
+
+    // Let's create a simple SOLICIT
+    Pkt6Ptr sol = Pkt6Ptr(captureSimpleSolicit());
+
+    // Simulate that we have received that traffic
+    srv_->fakeReceive(sol);
+
+    // Server will now process to run its normal loop, but instead of calling
+    // IfaceMgr::receive6(), it will read all packets from the list set by
+    // fakeReceive()
+    // In particular, it should call registered pkt6_receive callback.
+    srv_->run();
+
+    // Check that the server dropped the packet and did not send a response
+    ASSERT_EQ(0, srv_->fake_sent_.size());
+}
+
+// Checks if callouts installed on pkt6_received is able to set skip flag that
+// will cause the server to not process the packet (drop), even though it is valid.
+TEST_F(HooksDhcpv6SrvTest, skip_pkt6_receive) {
+
+    // Install pkt6_receive_callout
+    EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                        "pkt6_receive", pkt6_receive_skip));
+
+    // Let's create a simple SOLICIT
+    Pkt6Ptr sol = Pkt6Ptr(captureSimpleSolicit());
+
+    // Simulate that we have received that traffic
+    srv_->fakeReceive(sol);
+
+    // Server will now process to run its normal loop, but instead of calling
+    // IfaceMgr::receive6(), it will read all packets from the list set by
+    // fakeReceive()
+    // In particular, it should call registered pkt6_receive callback.
+    srv_->run();
+
+    // check that the server dropped the packet and did not produce any response
+    ASSERT_EQ(0, srv_->fake_sent_.size());
+}
+
+
+// Checks if callouts installed on pkt6_send are indeed called and the
+// all necessary parameters are passed.
+TEST_F(HooksDhcpv6SrvTest, simple_pkt6_send) {
+
+    // Install pkt6_receive_callout
+    EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                        "pkt6_send", pkt6_send_callout));
+
+    // Let's create a simple SOLICIT
+    Pkt6Ptr sol = Pkt6Ptr(captureSimpleSolicit());
+
+    // Simulate that we have received that traffic
+    srv_->fakeReceive(sol);
+
+    // Server will now process to run its normal loop, but instead of calling
+    // IfaceMgr::receive6(), it will read all packets from the list set by
+    // fakeReceive()
+    // In particular, it should call registered pkt6_receive callback.
+    srv_->run();
+
+    // Check that the callback called is indeed the one we installed
+    EXPECT_EQ("pkt6_send", callback_name_);
+
+    // Check that there is one packet sent
+    ASSERT_EQ(1, srv_->fake_sent_.size());
+    Pkt6Ptr adv = srv_->fake_sent_.front();
+
+    // Check that pkt6 argument passing was successful and returned proper value
+    EXPECT_TRUE(callback_pkt6_.get() == adv.get());
+
+    // Check that all expected parameters are there
+    vector<string> expected_argument_names;
+    expected_argument_names.push_back(string("response6"));
+    EXPECT_TRUE(expected_argument_names == callback_argument_names_);
+}
+
+// Checks if callouts installed on pkt6_send is able to change
+// the values and the packet sent contains those changes
+TEST_F(HooksDhcpv6SrvTest, valueChange_pkt6_send) {
+
+    // Install pkt6_receive_callout
+    EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                        "pkt6_send", pkt6_send_change_serverid));
+
+    // Let's create a simple SOLICIT
+    Pkt6Ptr sol = Pkt6Ptr(captureSimpleSolicit());
+
+    // Simulate that we have received that traffic
+    srv_->fakeReceive(sol);
+
+    // Server will now process to run its normal loop, but instead of calling
+    // IfaceMgr::receive6(), it will read all packets from the list set by
+    // fakeReceive()
+    // In particular, it should call registered pkt6_receive callback.
+    srv_->run();
+
+    // check that the server did send a reposonce
+    ASSERT_EQ(1, srv_->fake_sent_.size());
+
+    // Make sure that we received a response
+    Pkt6Ptr adv = srv_->fake_sent_.front();
+    ASSERT_TRUE(adv);
+
+    // Get client-id...
+    OptionPtr clientid = adv->getOption(D6O_SERVERID);
+
+    // ... and check if it is the modified value
+    OptionPtr expected = createOption(D6O_SERVERID);
+    EXPECT_TRUE(clientid->equal(expected));
+}
+
+// Checks if callouts installed on pkt6_send is able to delete
+// existing options and that server applies those changes. In particular,
+// we are trying to send a packet without server-id. The packet should
+// be sent
+TEST_F(HooksDhcpv6SrvTest, deleteServerId_pkt6_send) {
+
+    // Install pkt6_receive_callout
+    EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                        "pkt6_send", pkt6_send_delete_serverid));
+
+    // Let's create a simple SOLICIT
+    Pkt6Ptr sol = Pkt6Ptr(captureSimpleSolicit());
+
+    // Simulate that we have received that traffic
+    srv_->fakeReceive(sol);
+
+    // Server will now process to run its normal loop, but instead of calling
+    // IfaceMgr::receive6(), it will read all packets from the list set by
+    // fakeReceive()
+    // In particular, it should call registered pkt6_receive callback.
+    srv_->run();
+
+    // Check that the server indeed sent a malformed ADVERTISE
+    ASSERT_EQ(1, srv_->fake_sent_.size());
+
+    // Get that ADVERTISE
+    Pkt6Ptr adv = srv_->fake_sent_.front();
+    ASSERT_TRUE(adv);
+
+    // Make sure that it does not have server-id
+    EXPECT_FALSE(adv->getOption(D6O_SERVERID));
+}
+
+// Checks if callouts installed on pkt6_skip is able to set skip flag that
+// will cause the server to not process the packet (drop), even though it is valid.
+TEST_F(HooksDhcpv6SrvTest, skip_pkt6_send) {
+
+    // Install pkt6_receive_callout
+    EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                        "pkt6_send", pkt6_send_skip));
+
+    // Let's create a simple REQUEST
+    Pkt6Ptr sol = Pkt6Ptr(captureSimpleSolicit());
+
+    // Simulate that we have received that traffic
+    srv_->fakeReceive(sol);
+
+    // Server will now process to run its normal loop, but instead of calling
+    // IfaceMgr::receive6(), it will read all packets from the list set by
+    // fakeReceive()
+    // In particular, it should call registered pkt6_receive callback.
+    srv_->run();
+
+    // check that the server dropped the packet and did not produce any response
+    ASSERT_EQ(0, srv_->fake_sent_.size());
+}
+
+// This test checks if subnet6_select callout is triggered and reports
+// valid parameters
+TEST_F(HooksDhcpv6SrvTest, subnet6_select) {
+
+    // Install pkt6_receive_callout
+    EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                        "subnet6_select", subnet6_select_callout));
+
+    // Configure 2 subnets, both directly reachable over local interface
+    // (let's not complicate the matter with relays)
+    string config = "{ \"interfaces\": [ \"*\" ],"
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet6\": [ { "
+        "    \"pool\": [ \"2001:db8:1::/64\" ],"
+        "    \"subnet\": \"2001:db8:1::/48\", "
+        "    \"interface\": \"" + valid_iface_ + "\" "
+        " }, {"
+        "    \"pool\": [ \"2001:db8:2::/64\" ],"
+        "    \"subnet\": \"2001:db8:2::/48\" "
+        " } ],"
+        "\"valid-lifetime\": 4000 }";
+
+    ElementPtr json = Element::fromJSON(config);
+    ConstElementPtr status;
+
+    // Configure the server and make sure the config is accepted
+    EXPECT_NO_THROW(status = configureDhcp6Server(*srv_, json));
+    ASSERT_TRUE(status);
+    comment_ = parseAnswer(rcode_, status);
+    ASSERT_EQ(0, rcode_);
+
+    // Prepare solicit packet. Server should select first subnet for it
+    Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
+    sol->setRemoteAddr(IOAddress("fe80::abcd"));
+    sol->setIface(valid_iface_);
+    sol->addOption(generateIA(234, 1500, 3000));
+    OptionPtr clientid = generateClientId();
+    sol->addOption(clientid);
+
+    // Pass it to the server and get an advertise
+    Pkt6Ptr adv = srv_->processSolicit(sol);
+
+    // check if we get response at all
+    ASSERT_TRUE(adv);
+
+    // Check that the callback called is indeed the one we installed
+    EXPECT_EQ("subnet6_select", callback_name_);
+
+    // Check that pkt6 argument passing was successful and returned proper value
+    EXPECT_TRUE(callback_pkt6_.get() == sol.get());
+
+    const Subnet6Collection* exp_subnets = CfgMgr::instance().getSubnets6();
+
+    // The server is supposed to pick the first subnet, because of matching
+    // interface. Check that the value is reported properly.
+    ASSERT_TRUE(callback_subnet6_);
+    EXPECT_EQ(callback_subnet6_.get(), exp_subnets->front().get());
+
+    // Server is supposed to report two subnets
+    ASSERT_EQ(exp_subnets->size(), callback_subnet6collection_->size());
+
+    // Compare that the available subnets are reported as expected
+    EXPECT_TRUE((*exp_subnets)[0].get() == (*callback_subnet6collection_)[0].get());
+    EXPECT_TRUE((*exp_subnets)[1].get() == (*callback_subnet6collection_)[1].get());
+}
+
+// This test checks if callout installed on subnet6_select hook point can pick
+// a different subnet.
+TEST_F(HooksDhcpv6SrvTest, subnet_select_change) {
+
+    // Install pkt6_receive_callout
+    EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                        "subnet6_select", subnet6_select_different_subnet_callout));
+
+    // Configure 2 subnets, both directly reachable over local interface
+    // (let's not complicate the matter with relays)
+    string config = "{ \"interfaces\": [ \"*\" ],"
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet6\": [ { "
+        "    \"pool\": [ \"2001:db8:1::/64\" ],"
+        "    \"subnet\": \"2001:db8:1::/48\", "
+        "    \"interface\": \"" + valid_iface_ + "\" "
+        " }, {"
+        "    \"pool\": [ \"2001:db8:2::/64\" ],"
+        "    \"subnet\": \"2001:db8:2::/48\" "
+        " } ],"
+        "\"valid-lifetime\": 4000 }";
+
+    ElementPtr json = Element::fromJSON(config);
+    ConstElementPtr status;
+
+    // Configure the server and make sure the config is accepted
+    EXPECT_NO_THROW(status = configureDhcp6Server(*srv_, json));
+    ASSERT_TRUE(status);
+    comment_ = parseAnswer(rcode_, status);
+    ASSERT_EQ(0, rcode_);
+
+    // Prepare solicit packet. Server should select first subnet for it
+    Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
+    sol->setRemoteAddr(IOAddress("fe80::abcd"));
+    sol->setIface(valid_iface_);
+    sol->addOption(generateIA(234, 1500, 3000));
+    OptionPtr clientid = generateClientId();
+    sol->addOption(clientid);
+
+    // Pass it to the server and get an advertise
+    Pkt6Ptr adv = srv_->processSolicit(sol);
+
+    // check if we get response at all
+    ASSERT_TRUE(adv);
+
+    // The response should have an address from second pool, so let's check it
+    OptionPtr tmp = adv->getOption(D6O_IA_NA);
+    ASSERT_TRUE(tmp);
+    boost::shared_ptr<Option6IA> ia = boost::dynamic_pointer_cast<Option6IA>(tmp);
+    ASSERT_TRUE(ia);
+    tmp = ia->getOption(D6O_IAADDR);
+    ASSERT_TRUE(tmp);
+    boost::shared_ptr<Option6IAAddr> addr_opt =
+        boost::dynamic_pointer_cast<Option6IAAddr>(tmp);
+    ASSERT_TRUE(addr_opt);
+
+    // Get all subnets and use second subnet for verification
+    const Subnet6Collection* subnets = CfgMgr::instance().getSubnets6();
+    ASSERT_EQ(2, subnets->size());
+
+    // Advertised address must belong to the second pool (in subnet's range,
+    // in dynamic pool)
+    EXPECT_TRUE((*subnets)[1]->inRange(addr_opt->getAddress()));
+    EXPECT_TRUE((*subnets)[1]->inPool(addr_opt->getAddress()));
+}
+
+
 /// @todo: Add more negative tests for processX(), e.g. extend sanityCheck() test
 /// to call processX() methods.
 
diff --git a/src/bin/dhcp6/tests/marker_file.h.in b/src/bin/dhcp6/tests/marker_file.h.in
new file mode 100644
index 0000000..11b98ee
--- /dev/null
+++ b/src/bin/dhcp6/tests/marker_file.h.in
@@ -0,0 +1,28 @@
+// Copyright (C) 2013  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 MARKER_FILE_H
+#define MARKER_FILE_H
+
+/// @file
+/// Define a marker file that is used in tests to prove that an "unload"
+/// function has been called.
+
+namespace {
+const char* LOAD_MARKER_FILE = "@abs_builddir@/load_marker.txt";
+const char* UNLOAD_MARKER_FILE = "@abs_builddir@/unload_marker.txt";
+}
+
+#endif // MARKER_FILE_H
+
diff --git a/src/bin/dhcp6/tests/test_libraries.h.in b/src/bin/dhcp6/tests/test_libraries.h.in
new file mode 100644
index 0000000..b5e80a0
--- /dev/null
+++ b/src/bin/dhcp6/tests/test_libraries.h.in
@@ -0,0 +1,51 @@
+// Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef TEST_LIBRARIES_H
+#define TEST_LIBRARIES_H
+
+#include <config.h>
+
+namespace {
+
+
+// Take care of differences in DLL naming between operating systems.
+
+#ifdef OS_OSX
+#define DLL_SUFFIX ".dylib"
+
+#else
+#define DLL_SUFFIX ".so"
+
+#endif
+
+
+// Names of the libraries used in these tests.  These libraries are built using
+// libtool, so we need to look in the hidden ".libs" directory to locate the
+// shared library.
+
+// Library with load/unload functions creating marker files to check their
+// operation.
+static const char* CALLOUT_LIBRARY_1 = "@abs_builddir@/.libs/libco1"
+                                           DLL_SUFFIX;
+static const char* CALLOUT_LIBRARY_2 = "@abs_builddir@/.libs/libco2"
+                                           DLL_SUFFIX;
+
+// Name of a library which is not present.
+static const char* NOT_PRESENT_LIBRARY = "@abs_builddir@/.libs/libnothere"
+                                         DLL_SUFFIX;
+} // anonymous namespace
+
+
+#endif // TEST_LIBRARIES_H
diff --git a/src/bin/memmgr/.gitignore b/src/bin/memmgr/.gitignore
index 3743d58..b92e3f5 100644
--- a/src/bin/memmgr/.gitignore
+++ b/src/bin/memmgr/.gitignore
@@ -2,3 +2,4 @@
 /memmgr.py
 /memmgr.spec
 /b10-memmgr.8
+/memmgr.spec.pre
diff --git a/src/bin/memmgr/memmgr.py.in b/src/bin/memmgr/memmgr.py.in
index 6f3d0c6..5c9040f 100755
--- a/src/bin/memmgr/memmgr.py.in
+++ b/src/bin/memmgr/memmgr.py.in
@@ -19,6 +19,8 @@ import copy
 import os
 import sys
 import signal
+import socket
+import threading
 
 sys.path.append('@@PYTHONPATH@@')
 import isc.log
@@ -28,6 +30,7 @@ from isc.server_common.bind10_server import BIND10Server, BIND10ServerFatal
 from isc.server_common.datasrc_clients_mgr \
     import DataSrcClientsMgr, ConfigError
 from isc.memmgr.datasrc_info import DataSrcInfo
+from isc.memmgr.builder import MemorySegmentBuilder
 import isc.util.process
 
 MODULE_NAME = 'memmgr'
@@ -43,6 +46,7 @@ class ConfigError(Exception):
 
 class Memmgr(BIND10Server):
     def __init__(self):
+        BIND10Server.__init__(self)
         # Running configurable parameters: on initial configuration this will
         # be a dict: str=>config_value.
         # This is defined as "protected" so tests can inspect it; others
@@ -57,6 +61,10 @@ class Memmgr(BIND10Server):
         # active configuration generations.  Allow tests to inspec it.
         self._datasrc_info_list = []
 
+        self._builder_setup = False
+        self._builder_command_queue = []
+        self._builder_response_queue = []
+
     def _config_handler(self, new_config):
         """Configuration handler, called via BIND10Server.
 
@@ -116,6 +124,52 @@ class Memmgr(BIND10Server):
         # All copy, switch to the new configuration.
         self._config_params = new_config_params
 
+    def __notify_from_builder(self):
+        # Nothing is implemented here for now. This method should have
+        # code to handle responses from the builder in
+        # self._builder_response_queue[]. Access must be synchronized
+        # using self._builder_lock.
+        pass
+
+    def __create_builder_thread(self):
+        # We get responses from the builder thread on this socket pair.
+        (self._master_sock, self._builder_sock) = \
+            socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM)
+        self.watch_fileno(self._master_sock, rcallback=self.__notify_from_builder)
+
+        # See the documentation for MemorySegmentBuilder on how the
+        # following are used.
+        self._builder_lock = threading.Lock()
+        self._builder_cv = threading.Condition(lock=self._builder_lock)
+
+        self._builder = MemorySegmentBuilder(self._builder_sock,
+                                             self._builder_cv,
+                                             self._builder_command_queue,
+                                             self._builder_response_queue)
+        self._builder_thread = threading.Thread(target=self._builder.run)
+        self._builder_thread.start()
+
+        self._builder_setup = True
+
+    def __shutdown_builder_thread(self):
+        # Some unittests do not create the builder thread, so we check
+        # that.
+        if not self._builder_setup:
+            return
+
+        self._builder_setup = False
+
+        # This makes the MemorySegmentBuilder exit its main loop. It
+        # should make the builder thread joinable.
+        with self._builder_cv:
+            self._builder_command_queue.append('shutdown')
+            self._builder_cv.notify_all()
+
+        self._builder_thread.join()
+
+        self._master_sock.close()
+        self._builder_sock.close()
+
     def _setup_module(self):
         """Module specific initialization for BIND10Server."""
         try:
@@ -129,6 +183,12 @@ class Memmgr(BIND10Server):
             logger.error(MEMMGR_NO_DATASRC_CONF, ex)
             raise BIND10ServerFatal('failed to setup memmgr module')
 
+        self.__create_builder_thread()
+
+    def _shutdown_module(self):
+        """Module specific finalization."""
+        self.__shutdown_builder_thread()
+
     def _datasrc_config_handler(self, new_config, config_data):
         """Callback of data_sources configuration update.
 
diff --git a/src/bin/memmgr/tests/memmgr_test.py b/src/bin/memmgr/tests/memmgr_test.py
index 0fec7e3..3dae17f 100755
--- a/src/bin/memmgr/tests/memmgr_test.py
+++ b/src/bin/memmgr/tests/memmgr_test.py
@@ -74,6 +74,15 @@ class TestMemmgr(unittest.TestCase):
         self.__orig_isdir = os.path.isdir
 
     def tearDown(self):
+        # Not all unittests cause this method to be called, so we call
+        # it explicitly as it may be necessary in some cases where the
+        # builder thread has been created.
+        self.__mgr._shutdown_module()
+
+        # Assert that all commands sent to the builder thread were
+        # handled.
+        self.assertEqual(len(self.__mgr._builder_command_queue), 0)
+
         # Restore faked values
         os.access = self.__orig_os_access
         os.path.isdir = self.__orig_isdir
@@ -128,9 +137,24 @@ class TestMemmgr(unittest.TestCase):
         self.assertEqual(1, answer[0])
         self.assertIsNotNone(re.search('not a directory', answer[1]))
 
-        # Bad update: directory exists but is not readable.
-        os.mkdir(self.__test_mapped_file_dir, 0o500) # drop writable bit
+    @unittest.skipIf(os.getuid() == 0,
+                     'test cannot be run as root user')
+    def test_configure_bad_permissions(self):
+        self.__mgr._setup_ccsession()
+
+        # Pretend specified directories exist and writable
+        os.path.isdir = lambda x: True
+        os.access = lambda x, y: True
+
+        # Initial configuration.
+        self.assertEqual((0, None),
+                         parse_answer(self.__mgr._config_handler({})))
+
+        os.path.isdir = self.__orig_isdir
         os.access = self.__orig_os_access
+
+        # Bad update: directory exists but is not writable.
+        os.mkdir(self.__test_mapped_file_dir, 0o500) # drop writable bit
         user_cfg = {'mapped_file_dir': self.__test_mapped_file_dir}
         answer = parse_answer(self.__mgr._config_handler(user_cfg))
         self.assertEqual(1, answer[0])
diff --git a/src/bin/usermgr/tests/b10-cmdctl-usermgr_test.py b/src/bin/usermgr/tests/b10-cmdctl-usermgr_test.py
index 0766b7c..2da418d 100644
--- a/src/bin/usermgr/tests/b10-cmdctl-usermgr_test.py
+++ b/src/bin/usermgr/tests/b10-cmdctl-usermgr_test.py
@@ -399,6 +399,19 @@ Options:
                          'add', 'user1', 'pass1'
                        ])
 
+    @unittest.skipIf(os.getuid() == 0,
+                     'test cannot be run as root user')
+    def test_bad_file_permissions(self):
+        """
+        Check for graceful handling of bad file argument
+        """
+        # Create the test file
+        self.run_check(0, None, None,
+                       [ self.TOOL,
+                         '-f', self.OUTPUT_FILE,
+                         'add', 'user1', 'pass1'
+                       ])
+
         # Make it non-writable (don't worry about cleanup, the
         # file should be deleted after each test anyway
         os.chmod(self.OUTPUT_FILE, stat.S_IRUSR)
diff --git a/src/bin/xfrin/b10-xfrin.xml b/src/bin/xfrin/b10-xfrin.xml
index 5063732..903a3a0 100644
--- a/src/bin/xfrin/b10-xfrin.xml
+++ b/src/bin/xfrin/b10-xfrin.xml
@@ -231,98 +231,107 @@ operation
           <variablelist>
 
             <varlistentry>
-              <term><replaceable>zonename</replaceable></term>
+              <term><replaceable>classname</replaceable></term>
               <listitem><simpara>
-                An actual zone name or special zone name
-                <quote>_SERVER_</quote> representing the entire server.
-                Zone classes (e.g. IN, CH, and HS) are mixed and counted so
-                far. But these will be distinguished in future release.
+                An actual RR class name of the zone, e.g. IN, CH, and HS
                 </simpara>
                 <variablelist>
 
                   <varlistentry>
-                    <term>soaoutv4</term>
+                    <term><replaceable>zonename</replaceable></term>
                     <listitem><simpara>
-                      Number of IPv4 SOA queries sent from Xfrin
-                    </simpara></listitem>
-                  </varlistentry>
-
-                  <varlistentry>
-                    <term>soaoutv6</term>
-                    <listitem><simpara>
-                      Number of IPv6 SOA queries sent from Xfrin
-                    </simpara></listitem>
-                  </varlistentry>
-
-                  <varlistentry>
-                    <term>axfrreqv4</term>
-                    <listitem><simpara>
-                      Number of IPv4 AXFR requests sent from Xfrin
-                    </simpara></listitem>
-                  </varlistentry>
-
-                  <varlistentry>
-                    <term>axfrreqv6</term>
-                    <listitem><simpara>
-                      Number of IPv6 AXFR requests sent from Xfrin
-                    </simpara></listitem>
-                  </varlistentry>
-
-                  <varlistentry>
-                    <term>ixfrreqv4</term>
-                    <listitem><simpara>
-                      Number of IPv4 IXFR requests sent from Xfrin
-                    </simpara></listitem>
-                  </varlistentry>
-
-                  <varlistentry>
-                    <term>ixfrreqv6</term>
-                    <listitem><simpara>
-                      Number of IPv6 IXFR requests sent from Xfrin
-                    </simpara></listitem>
-                  </varlistentry>
-
-                  <varlistentry>
-                    <term>xfrsuccess</term>
-                    <listitem><simpara>
-                      Number of zone transfer requests succeeded.
-                      These include the case where the zone turns
-                      out to be the latest as a result of an
-                      initial SOA query (and there is actually no
-                      AXFR or IXFR transaction).
-                    </simpara></listitem>
-                  </varlistentry>
-
-                  <varlistentry>
-                    <term>xfrfail</term>
-                    <listitem><simpara>
-                      Number of zone transfer requests failed
-                    </simpara></listitem>
-                  </varlistentry>
-
-                  <varlistentry>
-                    <term>last_axfr_duration</term>
-                    <listitem><simpara>
-                      Duration in seconds of the last successful AXFR.  0.0
-                      means no successful AXFR done or means a successful AXFR
-                      done in less than a microsecond.  If an AXFR is aborted
-                      due to some failure, this duration won't be updated.
-                    </simpara></listitem>
-                  </varlistentry>
-
-                  <varlistentry>
-                    <term>last_ixfr_duration</term>
-                    <listitem><simpara>
-                      Duration in seconds of the last successful IXFR.  0.0
-                      means no successful IXFR done or means a successful IXFR
-                      done in less than a microsecond.  If an IXFR is aborted
-                      due to some failure, this duration won't be updated.
-                    </simpara></listitem>
-                  </varlistentry>
+                      An actual zone name or special zone name
+                      <quote>_SERVER_</quote> representing the entire server
+                      </simpara>
+                      <variablelist>
+
+                        <varlistentry>
+                          <term>soaoutv4</term>
+                          <listitem><simpara>
+                            Number of IPv4 SOA queries sent from Xfrin
+                          </simpara></listitem>
+                        </varlistentry>
+
+                        <varlistentry>
+                          <term>soaoutv6</term>
+                          <listitem><simpara>
+                            Number of IPv6 SOA queries sent from Xfrin
+                          </simpara></listitem>
+                        </varlistentry>
+
+                        <varlistentry>
+                          <term>axfrreqv4</term>
+                          <listitem><simpara>
+                            Number of IPv4 AXFR requests sent from Xfrin
+                          </simpara></listitem>
+                        </varlistentry>
+
+                        <varlistentry>
+                          <term>axfrreqv6</term>
+                          <listitem><simpara>
+                            Number of IPv6 AXFR requests sent from Xfrin
+                          </simpara></listitem>
+                        </varlistentry>
+
+                        <varlistentry>
+                          <term>ixfrreqv4</term>
+                          <listitem><simpara>
+                            Number of IPv4 IXFR requests sent from Xfrin
+                          </simpara></listitem>
+                        </varlistentry>
+
+                        <varlistentry>
+                          <term>ixfrreqv6</term>
+                          <listitem><simpara>
+                            Number of IPv6 IXFR requests sent from Xfrin
+                          </simpara></listitem>
+                        </varlistentry>
+
+                        <varlistentry>
+                          <term>xfrsuccess</term>
+                          <listitem><simpara>
+                            Number of zone transfer requests succeeded.
+                            These include the case where the zone turns
+                            out to be the latest as a result of an
+                            initial SOA query (and there is actually no
+                            AXFR or IXFR transaction).
+                          </simpara></listitem>
+                        </varlistentry>
+
+                        <varlistentry>
+                          <term>xfrfail</term>
+                          <listitem><simpara>
+                            Number of zone transfer requests failed
+                          </simpara></listitem>
+                        </varlistentry>
+
+                        <varlistentry>
+                          <term>last_axfr_duration</term>
+                          <listitem><simpara>
+                            Duration in seconds of the last successful AXFR.  0.0
+                            means no successful AXFR done or means a successful AXFR
+                            done in less than a microsecond.  If an AXFR is aborted
+                            due to some failure, this duration won't be updated.
+                          </simpara></listitem>
+                        </varlistentry>
+
+                        <varlistentry>
+                          <term>last_ixfr_duration</term>
+                          <listitem><simpara>
+                            Duration in seconds of the last successful IXFR.  0.0
+                            means no successful IXFR done or means a successful IXFR
+                            done in less than a microsecond.  If an IXFR is aborted
+                            due to some failure, this duration won't be updated.
+                          </simpara></listitem>
+                        </varlistentry>
+
+                      </variablelist>
+                    </listitem>
+                  </varlistentry><!-- end of zonename -->
 
                 </variablelist>
               </listitem>
-            </varlistentry><!-- end of zonename -->
+            </varlistentry><!-- end of classname -->
 
           </variablelist>
         </listitem>
diff --git a/src/bin/xfrin/tests/xfrin_test.py b/src/bin/xfrin/tests/xfrin_test.py
index c431f4c..0a10311 100644
--- a/src/bin/xfrin/tests/xfrin_test.py
+++ b/src/bin/xfrin/tests/xfrin_test.py
@@ -2182,6 +2182,7 @@ class TestStatisticsXfrinConn(TestXfrinConnection):
         name2count.update(overwrite)
         for (name, exp) in name2count.items():
             act = self.conn._counters.get(self.__zones,
+                                          TEST_RRCLASS_STR,
                                           TEST_ZONE_NAME_STR,
                                           name)
             msg = '%s is expected %s but actually %s' % (name, exp, act)
diff --git a/src/bin/xfrin/xfrin.py.in b/src/bin/xfrin/xfrin.py.in
index 6ff2805..ec38952 100755
--- a/src/bin/xfrin/xfrin.py.in
+++ b/src/bin/xfrin/xfrin.py.in
@@ -875,8 +875,9 @@ class XfrinConnection(asyncore.dispatcher):
 
         self._send_query(RRType.SOA)
         # count soaoutv4 or soaoutv6 requests
-        self._counters.inc('zones', self._zone_name.to_text(),
-                           'soaout' + self._get_ipver_str())
+        self._counters.inc('zones', self._rrclass.to_text(),
+                           self._zone_name.to_text(), 'soaout' +
+                           self._get_ipver_str())
         data_len = self._get_request_response(2)
         msg_len = socket.htons(struct.unpack('H', data_len)[0])
         soa_response = self._get_request_response(msg_len)
@@ -918,12 +919,17 @@ class XfrinConnection(asyncore.dispatcher):
             # Note: If the timer for the zone is already started but
             # not yet stopped due to some error, the last start time
             # is overwritten at this point.
-            self._counters.start_timer('zones', self._zone_name.to_text(),
-                                       'last_' + req_str.lower() + '_duration')
+            self._counters.start_timer('zones',
+                                       self._rrclass.to_text(),
+                                       self._zone_name.to_text(),
+                                       'last_' + req_str.lower() +
+                                       '_duration')
             logger.info(XFRIN_XFR_TRANSFER_STARTED, req_str, self.zone_str())
             # An AXFR or IXFR is being requested.
-            self._counters.inc('zones', self._zone_name.to_text(),
-                               req_str.lower() + 'req' + self._get_ipver_str())
+            self._counters.inc('zones', self._rrclass.to_text(),
+                               self._zone_name.to_text(),
+                               req_str.lower() + 'req' +
+                               self._get_ipver_str())
             self._send_query(self._request_type)
             self.__state = XfrinInitialSOA()
             self._handle_xfrin_responses()
@@ -988,11 +994,13 @@ class XfrinConnection(asyncore.dispatcher):
             # A xfrsuccess or xfrfail counter is incremented depending on
             # the result.
             result = {XFRIN_OK: 'xfrsuccess', XFRIN_FAIL: 'xfrfail'}[ret]
-            self._counters.inc('zones', self._zone_name.to_text(), result)
+            self._counters.inc('zones', self._rrclass.to_text(),
+                               self._zone_name.to_text(), result)
             # The started statistics timer is finally stopped only in
             # a successful case.
             if ret == XFRIN_OK:
                 self._counters.stop_timer('zones',
+                                          self._rrclass.to_text(),
                                           self._zone_name.to_text(),
                                           'last_' + req_str.lower() +
                                           '_duration')
diff --git a/src/bin/xfrin/xfrin.spec b/src/bin/xfrin/xfrin.spec
index 6d1e3a0..d09685b 100644
--- a/src/bin/xfrin/xfrin.spec
+++ b/src/bin/xfrin/xfrin.spec
@@ -135,110 +135,120 @@
         "item_type": "named_set",
         "item_optional": false,
         "item_default": {
-          "_SERVER_" : {
-            "soaoutv4": 0,
-            "soaoutv6": 0,
-            "axfrreqv4": 0,
-            "axfrreqv6": 0,
-            "ixfrreqv4": 0,
-            "ixfrreqv6": 0,
-            "xfrsuccess": 0,
-            "xfrfail": 0,
-            "last_ixfr_duration": 0.0,
-            "last_axfr_duration": 0.0
+          "IN" : {
+            "_SERVER_" : {
+              "soaoutv4": 0,
+              "soaoutv6": 0,
+              "axfrreqv4": 0,
+              "axfrreqv6": 0,
+              "ixfrreqv4": 0,
+              "ixfrreqv6": 0,
+              "xfrsuccess": 0,
+              "xfrfail": 0,
+              "last_ixfr_duration": 0.0,
+              "last_axfr_duration": 0.0
+            }
           }
         },
         "item_title": "Zone names",
         "item_description": "A directory name of per-zone statistics",
         "named_set_item_spec": {
-          "item_name": "zonename",
-          "item_type": "map",
+          "item_name": "classname",
+          "item_type": "named_set",
           "item_optional": false,
           "item_default": {},
-          "item_title": "Zone name",
-          "item_description": "An actual zone name or special zone name _SERVER_ representing the entire server. Zone classes (e.g. IN, CH, and HS) are mixed and counted so far. But these will be distinguished in future release.",
-          "map_item_spec": [
-            {
-              "item_name": "soaoutv4",
-              "item_type": "integer",
-              "item_optional": false,
-              "item_default": 0,
-              "item_title": "SOAOutv4",
-              "item_description": "Number of IPv4 SOA queries sent from Xfrin"
-            },
-            {
-              "item_name": "soaoutv6",
-              "item_type": "integer",
-              "item_optional": false,
-              "item_default": 0,
-              "item_title": "SOAOutv6",
-              "item_description": "Number of IPv6 SOA queries sent from Xfrin"
-            },
-            {
-              "item_name": "axfrreqv4",
-              "item_type": "integer",
-              "item_optional": false,
-              "item_default": 0,
-              "item_title": "AXFRReqv4",
-              "item_description": "Number of IPv4 AXFR requests sent from Xfrin"
-            },
-            {
-              "item_name": "axfrreqv6",
-              "item_type": "integer",
-              "item_optional": false,
-              "item_default": 0,
-              "item_title": "AXFRReqv6",
-              "item_description": "Number of IPv6 AXFR requests sent from Xfrin"
-            },
-            {
-              "item_name": "ixfrreqv4",
-              "item_type": "integer",
-              "item_optional": false,
-              "item_default": 0,
-              "item_title": "IXFRReqv4",
-              "item_description": "Number of IPv4 IXFR requests sent from Xfrin"
-            },
-            {
-              "item_name": "ixfrreqv6",
-              "item_type": "integer",
-              "item_optional": false,
-              "item_default": 0,
-              "item_title": "IXFRReqv6",
-              "item_description": "Number of IPv6 IXFR requests sent from Xfrin"
-            },
-            {
-              "item_name": "xfrsuccess",
-              "item_type": "integer",
-              "item_optional": false,
-              "item_default": 0,
-              "item_title": "XfrSuccess",
-              "item_description": "Number of zone transfer requests succeeded. These include the case where the zone turns out to be the latest as a result of an initial SOA query (and there is actually no AXFR or IXFR transaction)."
-            },
-            {
-              "item_name": "xfrfail",
-              "item_type": "integer",
-              "item_optional": false,
-              "item_default": 0,
-              "item_title": "XfrFail",
-              "item_description": "Number of zone transfer requests failed"
-            },
-            {
-              "item_name": "last_axfr_duration",
-              "item_type": "real",
-              "item_optional": false,
-              "item_default": 0.0,
-              "item_title": "Last AXFR duration",
-              "item_description": "Duration in seconds of the last successful AXFR.  0.0 means no successful AXFR done or means a successful AXFR done in less than a microsecond.  If an AXFR is aborted due to some failure, this duration won't be updated."
-            },
-            {
-              "item_name": "last_ixfr_duration",
-              "item_type": "real",
-              "item_optional": false,
-              "item_default": 0.0,
-              "item_title": "Last IXFR duration",
-              "item_description": "Duration in seconds of the last successful IXFR.  0.0 means no successful IXFR done or means a successful IXFR done in less than a microsecond.  If an IXFR is aborted due to some failure, this duration won't be updated."
-            }
-          ]
+          "item_title": "RR class name",
+          "item_description": "An actual RR class name of the zone, e.g. IN, CH, and HS",
+          "named_set_item_spec": {
+            "item_name": "zonename",
+            "item_type": "map",
+            "item_optional": false,
+            "item_default": {},
+            "item_title": "Zone name",
+            "item_description": "An actual zone name or special zone name _SERVER_ representing the entire server",
+            "map_item_spec": [
+              {
+                "item_name": "soaoutv4",
+                "item_type": "integer",
+                "item_optional": false,
+                "item_default": 0,
+                "item_title": "SOAOutv4",
+                "item_description": "Number of IPv4 SOA queries sent from Xfrin"
+              },
+              {
+                "item_name": "soaoutv6",
+                "item_type": "integer",
+                "item_optional": false,
+                "item_default": 0,
+                "item_title": "SOAOutv6",
+                "item_description": "Number of IPv6 SOA queries sent from Xfrin"
+              },
+              {
+                "item_name": "axfrreqv4",
+                "item_type": "integer",
+                "item_optional": false,
+                "item_default": 0,
+                "item_title": "AXFRReqv4",
+                "item_description": "Number of IPv4 AXFR requests sent from Xfrin"
+              },
+              {
+                "item_name": "axfrreqv6",
+                "item_type": "integer",
+                "item_optional": false,
+                "item_default": 0,
+                "item_title": "AXFRReqv6",
+                "item_description": "Number of IPv6 AXFR requests sent from Xfrin"
+              },
+              {
+                "item_name": "ixfrreqv4",
+                "item_type": "integer",
+                "item_optional": false,
+                "item_default": 0,
+                "item_title": "IXFRReqv4",
+                "item_description": "Number of IPv4 IXFR requests sent from Xfrin"
+              },
+              {
+                "item_name": "ixfrreqv6",
+                "item_type": "integer",
+                "item_optional": false,
+                "item_default": 0,
+                "item_title": "IXFRReqv6",
+                "item_description": "Number of IPv6 IXFR requests sent from Xfrin"
+              },
+              {
+                "item_name": "xfrsuccess",
+                "item_type": "integer",
+                "item_optional": false,
+                "item_default": 0,
+                "item_title": "XfrSuccess",
+                "item_description": "Number of zone transfer requests succeeded. These include the case where the zone turns out to be the latest as a result of an initial SOA query (and there is actually no AXFR or IXFR transaction)."
+              },
+              {
+                "item_name": "xfrfail",
+                "item_type": "integer",
+                "item_optional": false,
+                "item_default": 0,
+                "item_title": "XfrFail",
+                "item_description": "Number of zone transfer requests failed"
+              },
+              {
+                "item_name": "last_axfr_duration",
+                "item_type": "real",
+                "item_optional": false,
+                "item_default": 0.0,
+                "item_title": "Last AXFR duration",
+                "item_description": "Duration in seconds of the last successful AXFR.  0.0 means no successful AXFR done or means a successful AXFR done in less than a microsecond.  If an AXFR is aborted due to some failure, this duration won't be updated."
+              },
+              {
+                "item_name": "last_ixfr_duration",
+                "item_type": "real",
+                "item_optional": false,
+                "item_default": 0.0,
+                "item_title": "Last IXFR duration",
+                "item_description": "Duration in seconds of the last successful IXFR.  0.0 means no successful IXFR done or means a successful IXFR done in less than a microsecond.  If an IXFR is aborted due to some failure, this duration won't be updated."
+              }
+            ]
+          }
         }
       }
     ]
diff --git a/src/bin/xfrout/b10-xfrout.xml b/src/bin/xfrout/b10-xfrout.xml
index 5c71e05..468c6af 100644
--- a/src/bin/xfrout/b10-xfrout.xml
+++ b/src/bin/xfrout/b10-xfrout.xml
@@ -171,44 +171,56 @@
           <variablelist>
 
             <varlistentry>
-              <term><replaceable>zonename</replaceable></term>
+              <term><replaceable>classname</replaceable></term>
               <listitem><simpara>
-                A actual zone name or special zone name <quote>_SERVER_</quote>
-                representing an entire server
+                An actual RR class name of the zone, e.g. IN, CH, and HS
                 </simpara>
                 <variablelist>
 
                   <varlistentry>
-                    <term>notifyoutv4</term>
+                    <term><replaceable>zonename</replaceable></term>
                     <listitem><simpara>
-                      Number of IPv4 notifies per zone name sent out from Xfrout
-                    </simpara></listitem>
-                  </varlistentry>
-
-                  <varlistentry>
-                    <term>notifyoutv6</term>
-                    <listitem><simpara>
-                      Number of IPv6 notifies per zone name sent out from Xfrout
-                    </simpara></listitem>
-                  </varlistentry>
-
-                  <varlistentry>
-                    <term>xfrrej</term>
-                    <listitem><simpara>
-                      Number of XFR requests per zone name rejected by Xfrout
-                    </simpara></listitem>
-                  </varlistentry>
-
-                  <varlistentry>
-                    <term>xfrreqdone</term>
-                    <listitem><simpara>
-                      Number of requested zone transfers per zone name completed
-                    </simpara></listitem>
-                  </varlistentry>
+                      An actual zone name or special zone
+                      name <quote>_SERVER_</quote> representing an entire
+                      server
+                      </simpara>
+                      <variablelist>
+
+                        <varlistentry>
+                          <term>notifyoutv4</term>
+                          <listitem><simpara>
+                            Number of IPv4 notifies per zone name sent out from Xfrout
+                          </simpara></listitem>
+                        </varlistentry>
+
+                        <varlistentry>
+                          <term>notifyoutv6</term>
+                          <listitem><simpara>
+                            Number of IPv6 notifies per zone name sent out from Xfrout
+                          </simpara></listitem>
+                        </varlistentry>
+
+                        <varlistentry>
+                          <term>xfrrej</term>
+                          <listitem><simpara>
+                            Number of XFR requests per zone name rejected by Xfrout
+                          </simpara></listitem>
+                        </varlistentry>
+
+                        <varlistentry>
+                          <term>xfrreqdone</term>
+                          <listitem><simpara>
+                            Number of requested zone transfers per zone name completed
+                          </simpara></listitem>
+                        </varlistentry>
+
+                      </variablelist>
+                    </listitem>
+                  </varlistentry><!-- end of zonename -->
 
                 </variablelist>
               </listitem>
-            </varlistentry><!-- end of zonename -->
+            </varlistentry><!-- end of classname -->
 
           </variablelist>
         </listitem>
diff --git a/src/bin/xfrout/tests/xfrout_test.py.in b/src/bin/xfrout/tests/xfrout_test.py.in
index 7c71e78..d20ebe8 100644
--- a/src/bin/xfrout/tests/xfrout_test.py.in
+++ b/src/bin/xfrout/tests/xfrout_test.py.in
@@ -1,4 +1,4 @@
-# Copyright (C) 2010-2012  Internet Systems Consortium.
+# Copyright (C) 2010-2013  Internet Systems Consortium.
 #
 # Permission to use, copy, modify, and distribute this software for any
 # purpose with or without fee is hereby granted, provided that the above
@@ -41,6 +41,7 @@ TSIG_KEY = TSIGKey("example.com:SFuWd/q99SzF8Yzd1QbB9g==")
 TEST_ZONE_NAME_STR = "example.com."
 TEST_ZONE_NAME = Name(TEST_ZONE_NAME_STR)
 TEST_RRCLASS = RRClass.IN
+TEST_RRCLASS_STR = TEST_RRCLASS.to_text()
 IXFR_OK_VERSION = 2011111802
 IXFR_NG_VERSION = 2011111803
 SOA_CURRENT_VERSION = 2011112001
@@ -441,7 +442,8 @@ class TestXfroutSession(TestXfroutSessionBase):
         # check the 'xfrrej' counter initially
         self.assertRaises(isc.cc.data.DataNotFoundError,
                           self.xfrsess._counters.get, 'zones',
-                          TEST_ZONE_NAME_STR, 'xfrrej')
+                          TEST_RRCLASS_STR, TEST_ZONE_NAME_STR,
+                          'xfrrej')
         # Localhost (the default in this test) is accepted
         rcode, msg = self.xfrsess._parse_query_message(self.mdata)
         self.assertEqual(rcode.to_text(), "NOERROR")
@@ -457,7 +459,8 @@ class TestXfroutSession(TestXfroutSessionBase):
         self.assertEqual(rcode.to_text(), "REFUSED")
         # check the 'xfrrej' counter after incrementing
         self.assertEqual(self.xfrsess._counters.get(
-                'zones', TEST_ZONE_NAME_STR, 'xfrrej'), 1)
+                'zones', TEST_RRCLASS_STR, TEST_ZONE_NAME_STR,
+                'xfrrej'), 1)
 
         # TSIG signed request
         request_data = self.create_request_data(with_tsig=True)
@@ -488,7 +491,8 @@ class TestXfroutSession(TestXfroutSessionBase):
         self.assertEqual(rcode.to_text(), "REFUSED")
         # check the 'xfrrej' counter after incrementing
         self.assertEqual(self.xfrsess._counters.get(
-                'zones', TEST_ZONE_NAME_STR, 'xfrrej'), 2)
+                'zones', TEST_RRCLASS_STR, TEST_ZONE_NAME_STR,
+                'xfrrej'), 2)
 
         # ACL using TSIG: no TSIG; should be rejected
         acl_setter(isc.acl.dns.REQUEST_LOADER.load([
@@ -498,7 +502,8 @@ class TestXfroutSession(TestXfroutSessionBase):
         self.assertEqual(rcode.to_text(), "REFUSED")
         # check the 'xfrrej' counter after incrementing
         self.assertEqual(self.xfrsess._counters.get(
-                'zones', TEST_ZONE_NAME_STR, 'xfrrej'), 3)
+                'zones', TEST_RRCLASS_STR, TEST_ZONE_NAME_STR,
+                'xfrrej'), 3)
 
         #
         # ACL using IP + TSIG: both should match
@@ -520,7 +525,8 @@ class TestXfroutSession(TestXfroutSessionBase):
         self.assertEqual(rcode.to_text(), "REFUSED")
         # check the 'xfrrej' counter after incrementing
         self.assertEqual(self.xfrsess._counters.get(
-                'zones', TEST_ZONE_NAME_STR, 'xfrrej'), 4)
+                'zones', TEST_RRCLASS_STR, TEST_ZONE_NAME_STR,
+                'xfrrej'), 4)
         # Address matches, but TSIG doesn't (not included)
         self.xfrsess._remote = (socket.AF_INET, socket.SOCK_STREAM,
                                 ('192.0.2.1', 12345))
@@ -528,7 +534,8 @@ class TestXfroutSession(TestXfroutSessionBase):
         self.assertEqual(rcode.to_text(), "REFUSED")
         # check the 'xfrrej' counter after incrementing
         self.assertEqual(self.xfrsess._counters.get(
-                'zones', TEST_ZONE_NAME_STR, 'xfrrej'), 5)
+                'zones', TEST_RRCLASS_STR, TEST_ZONE_NAME_STR,
+                'xfrrej'), 5)
         # Neither address nor TSIG matches
         self.xfrsess._remote = (socket.AF_INET, socket.SOCK_STREAM,
                                 ('192.0.2.2', 12345))
@@ -536,7 +543,8 @@ class TestXfroutSession(TestXfroutSessionBase):
         self.assertEqual(rcode.to_text(), "REFUSED")
         # check the 'xfrrej' counter after incrementing
         self.assertEqual(self.xfrsess._counters.get(
-                'zones', TEST_ZONE_NAME_STR, 'xfrrej'), 6)
+                'zones', TEST_RRCLASS_STR, TEST_ZONE_NAME_STR,
+                'xfrrej'), 6)
 
     def test_transfer_acl(self):
         # ACL checks only with the default ACL
@@ -936,12 +944,14 @@ class TestXfroutSession(TestXfroutSessionBase):
 
         self.assertRaises(isc.cc.data.DataNotFoundError,
                           self.xfrsess._counters.get,
-                          'zones', TEST_ZONE_NAME_STR, 'xfrreqdone')
+                          'zones', TEST_RRCLASS_STR,
+                          TEST_ZONE_NAME_STR, 'xfrreqdone')
         self.xfrsess._reply_xfrout_query = myreply
         self.xfrsess.dns_xfrout_start(self.sock, self.mdata)
         self.assertEqual(self.sock.readsent(), b"success")
         self.assertGreater(self.xfrsess._counters.get(
-            'zones', TEST_ZONE_NAME_STR, 'xfrreqdone'), 0)
+            'zones', TEST_RRCLASS_STR, TEST_ZONE_NAME_STR,
+            'xfrreqdone'), 0)
 
     def test_reply_xfrout_query_axfr(self):
         self.xfrsess._soa = self.soa_rrset
diff --git a/src/bin/xfrout/xfrout.py.in b/src/bin/xfrout/xfrout.py.in
index 0055bb1..e26bc9a 100755
--- a/src/bin/xfrout/xfrout.py.in
+++ b/src/bin/xfrout/xfrout.py.in
@@ -301,7 +301,8 @@ class XfroutSession():
             return None, None
         elif acl_result == REJECT:
             # count rejected Xfr request by each zone name
-            self._counters.inc('zones', zone_name.to_text(), 'xfrrej')
+            self._counters.inc('zones', zone_class.to_text(),
+                               zone_name.to_text(), 'xfrrej')
             logger.debug(DBG_XFROUT_TRACE, XFROUT_QUERY_REJECTED,
                          self._request_type, format_addrinfo(self._remote),
                          format_zone_str(zone_name, zone_class))
@@ -571,7 +572,8 @@ class XfroutSession():
             else:
                 self._counters.dec('ixfr_running')
         # count done Xfr requests by each zone name
-        self._counters.inc('zones', zone_name.to_text(), 'xfrreqdone')
+        self._counters.inc('zones', zone_class.to_text(),
+                           zone_name.to_text(), 'xfrreqdone')
         logger.info(XFROUT_XFR_TRANSFER_DONE, self._request_typestr,
                     format_addrinfo(self._remote), zone_str)
 
diff --git a/src/bin/xfrout/xfrout.spec.pre.in b/src/bin/xfrout/xfrout.spec.pre.in
index 4277c3b..570f73e 100644
--- a/src/bin/xfrout/xfrout.spec.pre.in
+++ b/src/bin/xfrout/xfrout.spec.pre.in
@@ -121,56 +121,66 @@
           "item_type": "named_set",
           "item_optional": false,
           "item_default": {
-            "_SERVER_" : {
-              "notifyoutv4" : 0,
-              "notifyoutv6" : 0,
-              "xfrrej" : 0,
-              "xfrreqdone" : 0
+            "IN" : {
+              "_SERVER_" : {
+                "notifyoutv4" : 0,
+                "notifyoutv6" : 0,
+                "xfrrej" : 0,
+                "xfrreqdone" : 0
+              }
             }
           },
           "item_title": "Zone names",
           "item_description": "A directory name of per-zone statistics",
           "named_set_item_spec": {
-            "item_name": "zonename",
-            "item_type": "map",
+            "item_name": "classname",
+            "item_type": "named_set",
             "item_optional": false,
             "item_default": {},
-            "item_title": "Zone name",
-            "item_description": "A actual zone name or special zone name _SERVER_ representing an entire server",
-            "map_item_spec": [
-              {
-                "item_name": "notifyoutv4",
-                "item_type": "integer",
-                "item_optional": false,
-                "item_default": 0,
-                "item_title": "IPv4 notifies",
-                "item_description": "Number of IPv4 notifies per zone name sent out from Xfrout"
-              },
-              {
-                "item_name": "notifyoutv6",
-                "item_type": "integer",
-                "item_optional": false,
-                "item_default": 0,
-                "item_title": "IPv6 notifies",
-                "item_description": "Number of IPv6 notifies per zone name sent out from Xfrout"
-              },
-              {
-                "item_name": "xfrrej",
-                "item_type": "integer",
-                "item_optional": false,
-                "item_default": 0,
-                "item_title": "XFR rejected requests",
-                "item_description": "Number of XFR requests per zone name rejected by Xfrout"
-              },
-              {
-                "item_name": "xfrreqdone",
-                "item_type": "integer",
-                "item_optional": false,
-                "item_default": 0,
-                "item_title": "Requested zone transfers",
-                "item_description": "Number of requested zone transfers completed per zone name"
-              }
-            ]
+            "item_title": "RR class name",
+            "item_description": "An actual RR class name of the zone, e.g. IN, CH, and HS",
+            "named_set_item_spec": {
+              "item_name": "zonename",
+              "item_type": "map",
+              "item_optional": false,
+              "item_default": {},
+              "item_title": "Zone name",
+              "item_description": "An actual zone name or special zone name _SERVER_ representing an entire server",
+              "map_item_spec": [
+                {
+                  "item_name": "notifyoutv4",
+                  "item_type": "integer",
+                  "item_optional": false,
+                  "item_default": 0,
+                  "item_title": "IPv4 notifies",
+                  "item_description": "Number of IPv4 notifies per zone name sent out from Xfrout"
+                },
+                {
+                  "item_name": "notifyoutv6",
+                  "item_type": "integer",
+                  "item_optional": false,
+                  "item_default": 0,
+                  "item_title": "IPv6 notifies",
+                  "item_description": "Number of IPv6 notifies per zone name sent out from Xfrout"
+                },
+                {
+                  "item_name": "xfrrej",
+                  "item_type": "integer",
+                  "item_optional": false,
+                  "item_default": 0,
+                  "item_title": "XFR rejected requests",
+                  "item_description": "Number of XFR requests per zone name rejected by Xfrout"
+                },
+                {
+                  "item_name": "xfrreqdone",
+                  "item_type": "integer",
+                  "item_optional": false,
+                  "item_default": 0,
+                  "item_title": "Requested zone transfers",
+                  "item_description": "Number of requested zone transfers completed per zone name"
+                }
+              ]
+            }
           }
         },
         {
diff --git a/src/bin/zonemgr/tests/zonemgr_test.py b/src/bin/zonemgr/tests/zonemgr_test.py
index 111d650..5a17476 100644
--- a/src/bin/zonemgr/tests/zonemgr_test.py
+++ b/src/bin/zonemgr/tests/zonemgr_test.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2010  Internet Systems Consortium.
+# Copyright (C) 2010-2013  Internet Systems Consortium.
 #
 # Permission to use, copy, modify, and distribute this software for any
 # purpose with or without fee is hereby granted, provided that the above
@@ -22,6 +22,7 @@ import tempfile
 from zonemgr import *
 from isc.testutils.ccsession_mock import MockModuleCCSession
 from isc.notify import notify_out
+from isc.datasrc import ZoneFinder
 
 ZONE_NAME_CLASS1_IN = ("example.net.", "IN")
 ZONE_NAME_CLASS1_CH = ("example.net.", "CH")
@@ -36,8 +37,6 @@ LOWERBOUND_RETRY = 5
 REFRESH_JITTER = 0.10
 RELOAD_JITTER = 0.75
 
-TEST_SQLITE3_DBFILE = os.getenv("TESTDATAOBJDIR") + '/initdb.file'
-
 class ZonemgrTestException(Exception):
     pass
 
@@ -46,16 +45,53 @@ class FakeCCSession(isc.config.ConfigData, MockModuleCCSession):
         module_spec = isc.config.module_spec_from_file(SPECFILE_LOCATION)
         ConfigData.__init__(self, module_spec)
         MockModuleCCSession.__init__(self)
+        # For inspection
+        self.added_remote_modules = []
+
+    def add_remote_config_by_name(self, name, callback):
+        self.added_remote_modules.append((name, callback))
 
     def rpc_call(self, command, module, instance="*", to="*", params=None):
         if module not in ("Auth", "Xfrin"):
             raise ZonemgrTestException("module name not exist")
 
-    def get_remote_config_value(self, module_name, identifier):
-        if module_name == "Auth" and identifier == "database_file":
-            return TEST_SQLITE3_DBFILE, False
+class MockDataSourceClient():
+    '''A simple mock data source client.'''
+    def __init__(self):
+        self.rdata_net = 'a.example.net. root.example.net. 2009073106 ' + \
+            '7200 3600 2419200 21600'
+        self.rdata_org = 'a.example.org. root.example.org. 2009073112 ' + \
+            '7200 3600 2419200 21600'
+
+    def find_zone(self, zone_name):
+        '''Mock version of DataSourceClient.find_zone().'''
+        return (isc.datasrc.DataSourceClient.SUCCESS, self)
+
+    def find(self, name, rrtype, options=ZoneFinder.FIND_DEFAULT):
+        '''Mock version of ZoneFinder.find().'''
+        if name == Name('example.net'):
+            rdata = Rdata(RRType.SOA, RRClass.IN, self.rdata_net)
+        elif name == 'example.org.':
+            rdata = Rdata(RRType.SOA, RRClass.IN, self.rdata_org)
         else:
-            return "unknown", False
+            return (ZoneFinder.NXDOMAIN, None, 0)
+        rrset = RRset(name, RRClass.IN, RRType.SOA, RRTTL(3600))
+        rrset.add_rdata(rdata)
+        return (ZoneFinder.SUCCESS, rrset, 0)
+
+class MockDataSrcClientsMgr():
+    '''A simple mock data source client manager.'''
+    def __init__(self):
+        self.datasrc_client = MockDataSourceClient()
+
+    def get_client_list(self, rrclass):
+        return self
+
+    def find(self, zone_name, want_exact_match, want_finder):
+        """Pretending find method on the object returned by get_client_list"""
+        if issubclass(type(self.datasrc_client), Exception):
+            raise self.datasrc_client
+        return self.datasrc_client, None, None
 
 class MyZonemgrRefresh(ZonemgrRefresh):
     def __init__(self):
@@ -66,19 +102,8 @@ class MyZonemgrRefresh(ZonemgrRefresh):
         self._reload_jitter = 0.75
         self._refresh_jitter = 0.25
 
-        def get_zone_soa(zone_name, db_file):
-            if zone_name == 'example.net.':
-                return (1, 2, 'example.net.', 'example.net.sd.', 21600, 'SOA', None,
-                        'a.example.net. root.example.net. 2009073106 7200 3600 2419200 21600')
-            elif zone_name == 'example.org.':
-                return (1, 2, 'example.org.', 'example.org.sd.', 21600, 'SOA', None,
-                        'a.example.org. root.example.org. 2009073112 7200 3600 2419200 21600')
-            else:
-                return None
-        sqlite3_ds.get_zone_soa = get_zone_soa
-
-        ZonemgrRefresh.__init__(self, TEST_SQLITE3_DBFILE, self._slave_socket,
-                                FakeCCSession())
+        ZonemgrRefresh.__init__(self, self._slave_socket, FakeCCSession())
+        self._datasrc_clients_mgr = MockDataSrcClientsMgr()
         current_time = time.time()
         self._zonemgr_refresh_info = {
          ('example.net.', 'IN'): {
@@ -95,19 +120,23 @@ class MyZonemgrRefresh(ZonemgrRefresh):
 
 class TestZonemgrRefresh(unittest.TestCase):
     def setUp(self):
-        if os.path.exists(TEST_SQLITE3_DBFILE):
-            os.unlink(TEST_SQLITE3_DBFILE)
         self.stderr_backup = sys.stderr
         sys.stderr = open(os.devnull, 'w')
         self.zone_refresh = MyZonemgrRefresh()
         self.cc_session = FakeCCSession()
 
     def tearDown(self):
-        if os.path.exists(TEST_SQLITE3_DBFILE):
-            os.unlink(TEST_SQLITE3_DBFILE)
         sys.stderr.close()
         sys.stderr = self.stderr_backup
 
+    def test_init(self):
+        """Check some initial configuration after construction"""
+        # data source "module" should have been registrered as a necessary
+        # remote config
+        self.assertEqual([('data_sources',
+                           self.zone_refresh._datasrc_config_handler)],
+                         self.zone_refresh._module_cc.added_remote_modules)
+
     def test_random_jitter(self):
         max = 100025.120
         jitter = 0
@@ -195,16 +224,9 @@ class TestZonemgrRefresh(unittest.TestCase):
 
     def test_zonemgr_reload_zone(self):
         soa_rdata = 'a.example.net. root.example.net. 2009073106 1800 900 2419200 21600'
-        # We need to restore this not to harm other tests
-        old_get_zone_soa = sqlite3_ds.get_zone_soa
-        def get_zone_soa(zone_name, db_file):
-            return (1, 2, 'example.net.', 'example.net.sd.', 21600, 'SOA', None,
-                    'a.example.net. root.example.net. 2009073106 1800 900 2419200 21600')
-        sqlite3_ds.get_zone_soa = get_zone_soa
-
+        self.zone_refresh._datasrc_clients_mgr.datasrc_client.rdata_net = soa_rdata
         self.zone_refresh.zonemgr_reload_zone(ZONE_NAME_CLASS1_IN)
         self.assertEqual(soa_rdata, self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["zone_soa_rdata"])
-        sqlite3_ds.get_zone_soa = old_get_zone_soa
 
     def test_get_zone_notifier_master(self):
         notify_master = "192.168.1.1"
@@ -275,24 +297,10 @@ class TestZonemgrRefresh(unittest.TestCase):
     def test_send_command(self):
         self.assertRaises(ZonemgrTestException, self.zone_refresh._send_command, "Unknown", "Notify", None)
 
-    def test_zone_mgr_is_empty(self):
-        self.assertFalse(self.zone_refresh._zone_mgr_is_empty())
-        self.zone_refresh._zonemgr_refresh_info = {}
-        self.assertTrue(self.zone_refresh._zone_mgr_is_empty())
-
     def test_zonemgr_add_zone(self):
         soa_rdata = 'a.example.net. root.example.net. 2009073106 1800 900 2419200 21600'
-        # This needs to be restored. The following test actually failed if we left
-        # this unclean
-        old_get_zone_soa = sqlite3_ds.get_zone_soa
+        self.zone_refresh._datasrc_clients_mgr.datasrc_client.rdata_net = soa_rdata
         time1 = time.time()
-
-        def get_zone_soa(zone_name, db_file):
-            return (1, 2, 'example.net.', 'example.net.sd.', 21600, 'SOA', None,
-                    'a.example.net. root.example.net. 2009073106 1800 900 2419200 21600')
-
-        sqlite3_ds.get_zone_soa = get_zone_soa
-
         self.zone_refresh._zonemgr_refresh_info = {}
         self.zone_refresh.zonemgr_add_zone(ZONE_NAME_CLASS1_IN)
         self.assertEqual(1, len(self.zone_refresh._zonemgr_refresh_info))
@@ -306,12 +314,13 @@ class TestZonemgrRefresh(unittest.TestCase):
         self.assertTrue((time1 + 900 * (1 - self.zone_refresh._reload_jitter)) <= zone_timeout)
         self.assertTrue(zone_timeout <= time2 + 900)
 
-        def get_zone_soa2(zone_name, db_file):
+        old_get_zone_soa = self.zone_refresh._get_zone_soa
+        def get_zone_soa2(zone_name_class):
             return None
-        sqlite3_ds.get_zone_soa = get_zone_soa2
+        self.zone_refresh._get_zone_soa = get_zone_soa2
         self.zone_refresh.zonemgr_add_zone(ZONE_NAME_CLASS2_IN)
         self.assertTrue(self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS2_IN]["zone_soa_rdata"] is None)
-        sqlite3_ds.get_zone_soa = old_get_zone_soa
+        self.zone_refresh._get_zone_soa = old_get_zone_soa
 
     def test_zone_handle_notify(self):
         self.assertTrue(self.zone_refresh.zone_handle_notify(
@@ -333,10 +342,7 @@ class TestZonemgrRefresh(unittest.TestCase):
 
     def test_zone_refresh_success(self):
         soa_rdata = 'a.example.net. root.example.net. 2009073106 1800 900 2419200 21600'
-        def get_zone_soa(zone_name, db_file):
-            return (1, 2, 'example.net.', 'example.net.sd.', 21600, 'SOA', None,
-                    'a.example.net. root.example.net. 2009073106 1800 900 2419200 21600')
-        sqlite3_ds.get_zone_soa = get_zone_soa
+        self.zone_refresh._datasrc_clients_mgr.datasrc_client.rdata_net = soa_rdata
         time1 = time.time()
         self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["zone_state"] = ZONE_REFRESHING
         self.zone_refresh.zone_refresh_success(ZONE_NAME_CLASS1_IN)
@@ -373,14 +379,14 @@ class TestZonemgrRefresh(unittest.TestCase):
         self.assertRaises(ZonemgrException, self.zone_refresh.zone_refresh_fail, ZONE_NAME_CLASS3_CH)
         self.assertRaises(ZonemgrException, self.zone_refresh.zone_refresh_fail, ZONE_NAME_CLASS3_IN)
 
-        old_get_zone_soa = sqlite3_ds.get_zone_soa
-        def get_zone_soa(zone_name, db_file):
+        old_get_zone_soa = self.zone_refresh._get_zone_soa
+        def get_zone_soa(zone_name_class):
             return None
-        sqlite3_ds.get_zone_soa = get_zone_soa
+        self.zone_refresh._get_zone_soa = get_zone_soa
         self.zone_refresh.zone_refresh_fail(ZONE_NAME_CLASS1_IN)
         self.assertEqual(self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["zone_state"],
                          ZONE_EXPIRED)
-        sqlite3_ds.get_zone_soa = old_get_zone_soa
+        self.zone_refresh._get_zone_soa = old_get_zone_soa
 
     def test_find_need_do_refresh_zone(self):
         time1 = time.time()
@@ -628,7 +634,6 @@ class MyZonemgr(Zonemgr):
         def __exit__(self, type, value, traceback): pass
 
     def __init__(self):
-        self._db_file = TEST_SQLITE3_DBFILE
         self._zone_refresh = None
         self._shutdown_event = threading.Event()
         self._module_cc = FakeCCSession()
@@ -649,14 +654,8 @@ class MyZonemgr(Zonemgr):
 class TestZonemgr(unittest.TestCase):
 
     def setUp(self):
-        if os.path.exists(TEST_SQLITE3_DBFILE):
-            os.unlink(TEST_SQLITE3_DBFILE)
         self.zonemgr = MyZonemgr()
 
-    def tearDown(self):
-        if os.path.exists(TEST_SQLITE3_DBFILE):
-            os.unlink(TEST_SQLITE3_DBFILE)
-
     def test_config_handler(self):
         config_data1 = {
                     "lowerbound_refresh" : 60,
@@ -676,9 +675,8 @@ class TestZonemgr(unittest.TestCase):
         config_data3 = {"refresh_jitter" : 0.7}
         self.zonemgr.config_handler(config_data3)
         self.assertEqual(0.5, self.zonemgr._config_data.get("refresh_jitter"))
-        # The zone doesn't exist in database, simply skip loading soa for it and log an warning
-        self.zonemgr._zone_refresh = ZonemgrRefresh(TEST_SQLITE3_DBFILE, None,
-                                                    FakeCCSession())
+        # The zone doesn't exist in database, simply skip loading soa for it and log a warning
+        self.zonemgr._zone_refresh = ZonemgrRefresh(None, FakeCCSession())
         config_data1["secondary_zones"] = [{"name": "nonexistent.example",
                                             "class": "IN"}]
         self.assertEqual(self.zonemgr.config_handler(config_data1),
@@ -689,9 +687,6 @@ class TestZonemgr(unittest.TestCase):
                         is None)
         self.assertEqual(0.1, self.zonemgr._config_data.get("refresh_jitter"))
 
-    def test_get_db_file(self):
-        self.assertEqual(TEST_SQLITE3_DBFILE, self.zonemgr.get_db_file())
-
     def test_parse_cmd_params(self):
         params1 = {"zone_name" : "example.com.", "zone_class" : "CH",
                    "master" : "127.0.0.1"}
diff --git a/src/bin/zonemgr/zonemgr.py.in b/src/bin/zonemgr/zonemgr.py.in
index 71c7aae..2d5167b 100755
--- a/src/bin/zonemgr/zonemgr.py.in
+++ b/src/bin/zonemgr/zonemgr.py.in
@@ -1,6 +1,6 @@
 #!@PYTHON@
 
-# Copyright (C) 2010  Internet Systems Consortium.
+# Copyright (C) 2010-2013  Internet Systems Consortium.
 #
 # Permission to use, copy, modify, and distribute this software for any
 # purpose with or without fee is hereby granted, provided that the above
@@ -34,12 +34,14 @@ import threading
 import select
 import socket
 import errno
-from isc.datasrc import sqlite3_ds
 from optparse import OptionParser, OptionValueError
 from isc.config.ccsession import *
 import isc.util.process
 from isc.log_messages.zonemgr_messages import *
 from isc.notify import notify_out
+from isc.server_common.datasrc_clients_mgr import DataSrcClientsMgr, ConfigError
+from isc.datasrc import DataSourceClient, ZoneFinder
+from isc.dns import *
 
 # Initialize logging for called modules.
 isc.log.init("b10-zonemgr", buffer=True)
@@ -66,7 +68,9 @@ if "B10_FROM_BUILD" in os.environ:
 else:
     PREFIX = "@prefix@"
     DATAROOTDIR = "@datarootdir@"
-    SPECFILE_PATH = "@datadir@/@PACKAGE@".replace("${datarootdir}", DATAROOTDIR).replace("${prefix}", PREFIX)
+    SPECFILE_PATH = "@datadir@/@PACKAGE@".replace("${datarootdir}",
+                                          DATAROOTDIR).replace("${prefix}",
+                                                               PREFIX)
     AUTH_SPECFILE_PATH = SPECFILE_PATH
 
 SPECFILE_LOCATION = SPECFILE_PATH + "/zonemgr.spec"
@@ -76,7 +80,6 @@ __version__ = "BIND10"
 
 # define module name
 XFRIN_MODULE_NAME = 'Xfrin'
-AUTH_MODULE_NAME = 'Auth'
 
 # define command name
 ZONE_REFRESH_COMMAND = 'refresh_from_zonemgr'
@@ -103,18 +106,23 @@ class ZonemgrRefresh:
     can be stopped by calling shutdown() in another thread.
     """
 
-    def __init__(self, db_file, slave_socket, module_cc_session):
-        self._mccs = module_cc_session
+    def __init__(self, slave_socket, module_cc):
+        self._module_cc = module_cc
         self._check_sock = slave_socket
-        self._db_file = db_file
         self._zonemgr_refresh_info = {}
         self._lowerbound_refresh = None
         self._lowerbound_retry = None
         self._max_transfer_timeout = None
         self._refresh_jitter = None
         self._reload_jitter = None
-        self.update_config_data(module_cc_session.get_full_config(),
-                                module_cc_session)
+        # This is essentially private, but we allow tests to customize it.
+        self._datasrc_clients_mgr = DataSrcClientsMgr()
+        # data_sources configuration should be ready with cfgmgr, so this
+        # shouldn't fail; if it ever does we simply propagate the exception
+        # to terminate the program.
+        self._module_cc.add_remote_config_by_name('data_sources',
+                                                  self._datasrc_config_handler)
+        self.update_config_data(module_cc.get_full_config(), module_cc)
         self._running = False
 
     def _random_jitter(self, max, jitter):
@@ -133,27 +141,32 @@ class ZonemgrRefresh:
     def _set_zone_timer(self, zone_name_class, max, jitter):
         """Set zone next refresh time.
         jitter should not be bigger than half the original value."""
-        self._set_zone_next_refresh_time(zone_name_class, self._get_current_time() + \
+        self._set_zone_next_refresh_time(zone_name_class,
+                                         self._get_current_time() +
                                             self._random_jitter(max, jitter))
 
     def _set_zone_refresh_timer(self, zone_name_class):
         """Set zone next refresh time after zone refresh success.
            now + refresh - refresh_jitter <= next_refresh_time <= now + refresh
            """
-        zone_refresh_time = float(self._get_zone_soa_rdata(zone_name_class).split(" ")[REFRESH_OFFSET])
+        zone_refresh_time = float(self._get_zone_soa_rdata(zone_name_class).
+                                      split(" ")[REFRESH_OFFSET])
         zone_refresh_time = max(self._lowerbound_refresh, zone_refresh_time)
-        self._set_zone_timer(zone_name_class, zone_refresh_time, self._refresh_jitter * zone_refresh_time)
+        self._set_zone_timer(zone_name_class, zone_refresh_time,
+                             self._refresh_jitter * zone_refresh_time)
 
     def _set_zone_retry_timer(self, zone_name_class):
         """Set zone next refresh time after zone refresh fail.
            now + retry - retry_jitter <= next_refresh_time <= now + retry
            """
-        if (self._get_zone_soa_rdata(zone_name_class) is not None):
-            zone_retry_time = float(self._get_zone_soa_rdata(zone_name_class).split(" ")[RETRY_OFFSET])
+        if self._get_zone_soa_rdata(zone_name_class) is not None:
+            zone_retry_time = float(self._get_zone_soa_rdata(zone_name_class).
+                                        split(" ")[RETRY_OFFSET])
         else:
             zone_retry_time = 0.0
         zone_retry_time = max(self._lowerbound_retry, zone_retry_time)
-        self._set_zone_timer(zone_name_class, zone_retry_time, self._refresh_jitter * zone_retry_time)
+        self._set_zone_timer(zone_name_class, zone_retry_time,
+                             self._refresh_jitter * zone_retry_time)
 
     def _set_zone_notify_timer(self, zone_name_class):
         """Set zone next refresh time after receiving notify
@@ -167,19 +180,22 @@ class ZonemgrRefresh:
 
     def zone_refresh_success(self, zone_name_class):
         """Update zone info after zone refresh success"""
-        if (self._zone_not_exist(zone_name_class)):
-            logger.error(ZONEMGR_UNKNOWN_ZONE_SUCCESS, zone_name_class[0], zone_name_class[1])
+        if self._zone_not_exist(zone_name_class):
+            logger.error(ZONEMGR_UNKNOWN_ZONE_SUCCESS, zone_name_class[0],
+                         zone_name_class[1])
             raise ZonemgrException("[b10-zonemgr] Zone (%s, %s) doesn't "
                                    "belong to zonemgr" % zone_name_class)
         self.zonemgr_reload_zone(zone_name_class)
         self._set_zone_refresh_timer(zone_name_class)
         self._set_zone_state(zone_name_class, ZONE_OK)
-        self._set_zone_last_refresh_time(zone_name_class, self._get_current_time())
+        self._set_zone_last_refresh_time(zone_name_class,
+                                         self._get_current_time())
 
     def zone_refresh_fail(self, zone_name_class):
         """Update zone info after zone refresh fail"""
-        if (self._zone_not_exist(zone_name_class)):
-            logger.error(ZONEMGR_UNKNOWN_ZONE_FAIL, zone_name_class[0], zone_name_class[1])
+        if self._zone_not_exist(zone_name_class):
+            logger.error(ZONEMGR_UNKNOWN_ZONE_FAIL, zone_name_class[0],
+                         zone_name_class[1])
             raise ZonemgrException("[b10-zonemgr] Zone (%s, %s) doesn't "
                                    "belong to zonemgr" % zone_name_class)
         # Is zone expired?
@@ -199,11 +215,6 @@ class ZonemgrRefresh:
         zone; the Auth module should have rejected the case where it's not
         even authoritative for the zone.
 
-        Note: to be more robust and less independent from other module's
-        behavior, it's probably safer to check the authority condition here,
-        too.  But right now it uses SQLite3 specific API (to be deprecated),
-        so we rather rely on Auth.
-
         Parameters:
         zone_name_class (Name, RRClass): the notified zone name and class.
         master (str): textual address of the NOTIFY sender.
@@ -219,35 +230,86 @@ class ZonemgrRefresh:
 
     def zonemgr_reload_zone(self, zone_name_class):
         """ Reload a zone."""
-        zone_soa = sqlite3_ds.get_zone_soa(str(zone_name_class[0]), self._db_file)
-        self._zonemgr_refresh_info[zone_name_class]["zone_soa_rdata"] = zone_soa[7]
+        self._zonemgr_refresh_info[zone_name_class]["zone_soa_rdata"] = \
+            self._get_zone_soa(zone_name_class)
 
     def zonemgr_add_zone(self, zone_name_class):
         """ Add a zone into zone manager."""
-
-        logger.debug(DBG_ZONEMGR_BASIC, ZONEMGR_LOAD_ZONE, zone_name_class[0], zone_name_class[1])
+        logger.debug(DBG_ZONEMGR_BASIC, ZONEMGR_LOAD_ZONE, zone_name_class[0],
+                     zone_name_class[1])
         zone_info = {}
-        zone_soa = sqlite3_ds.get_zone_soa(str(zone_name_class[0]), self._db_file)
+        zone_soa = self._get_zone_soa(zone_name_class)
         if zone_soa is None:
             logger.warn(ZONEMGR_NO_SOA, zone_name_class[0], zone_name_class[1])
             zone_info["zone_soa_rdata"] = None
             zone_reload_time = 0.0
         else:
-            zone_info["zone_soa_rdata"] = zone_soa[7]
-            zone_reload_time = float(zone_soa[7].split(" ")[RETRY_OFFSET])
+            zone_info["zone_soa_rdata"] = zone_soa
+            zone_reload_time = float(zone_soa.split(" ")[RETRY_OFFSET])
         zone_info["zone_state"] = ZONE_OK
         zone_info["last_refresh_time"] = self._get_current_time()
         self._zonemgr_refresh_info[zone_name_class] = zone_info
-        # Imposes some random jitters to avoid many zones need to do refresh at the same time.
+        # Imposes some random jitters to avoid many zones need to do refresh
+        # at the same time.
         zone_reload_time = max(self._lowerbound_retry, zone_reload_time)
-        self._set_zone_timer(zone_name_class, zone_reload_time, self._reload_jitter * zone_reload_time)
+        self._set_zone_timer(zone_name_class, zone_reload_time,
+                             self._reload_jitter * zone_reload_time)
+
+    def _get_zone_soa(self, zone_name_class):
+        """Retrieve the current SOA RR of the zone to be transferred."""
+
+        def get_zone_soa_rrset(datasrc_client, zone_name, zone_class):
+            """Retrieve the current SOA RR of the zone to be transferred."""
+            # get the zone finder.  this must be SUCCESS (not even
+            # PARTIALMATCH) because we are specifying the zone origin name.
+            result, finder = datasrc_client.find_zone(zone_name)
+            if result != DataSourceClient.SUCCESS:
+                # The data source doesn't know the zone.  In the context in
+                # which this function is called, this shouldn't happen.
+                raise ZonemgrException(
+                    "unexpected result: zone %s/%s doesn't exist" %
+                    (zone_name.to_text(True), str(zone_class)))
+            result, soa_rrset, _ = finder.find(zone_name, RRType.SOA)
+            if result != ZoneFinder.SUCCESS:
+                logger.warn(ZONEMGR_NO_SOA,
+                            zone_name.to_text(True), str(zone_class))
+                return None
+            return soa_rrset
+
+        # Identify the data source to which the zone content is transferred,
+        # and get the current zone SOA from the data source (if available).
+        datasrc_client = None
+        clist = self._datasrc_clients_mgr.get_client_list(zone_name_class[1])
+        if clist is None:
+            return None
+        try:
+            datasrc_client = clist.find(zone_name_class[0], True, False)[0]
+            if datasrc_client is None: # can happen, so log it separately.
+                logger.error(ZONEMGR_DATASRC_UNKNOWN,
+                             zone_name_class[0] + '/' + zone_name_class[1])
+                return None
+            zone_soa = get_zone_soa_rrset(datasrc_client,
+                                          Name(zone_name_class[0]),
+                                          RRClass(zone_name_class[1]))
+            if zone_soa == None:
+                return None
+            else:
+                return zone_soa.get_rdata()[0].to_text()
+        except isc.datasrc.Error as ex:
+            # rare case error. re-raise as ZonemgrException so it'll be logged
+            # in command_handler().
+            raise ZonemgrException('unexpected failure in datasrc module: ' +
+                                   str(ex))
 
     def _zone_is_expired(self, zone_name_class):
         """Judge whether a zone is expired or not."""
-        zone_expired_time = float(self._get_zone_soa_rdata(zone_name_class).split(" ")[EXPIRED_OFFSET])
-        zone_last_refresh_time = self._get_zone_last_refresh_time(zone_name_class)
+        zone_expired_time = float(self._get_zone_soa_rdata(zone_name_class).
+                                      split(" ")[EXPIRED_OFFSET])
+        zone_last_refresh_time = \
+            self._get_zone_last_refresh_time(zone_name_class)
         if (ZONE_EXPIRED == self._get_zone_state(zone_name_class) or
-            zone_last_refresh_time + zone_expired_time <= self._get_current_time()):
+            zone_last_refresh_time + zone_expired_time <=
+                self._get_current_time()):
             return True
 
         return False
@@ -262,16 +324,19 @@ class ZonemgrRefresh:
         self._zonemgr_refresh_info[zone_name_class]["last_refresh_time"] = time
 
     def _get_zone_notifier_master(self, zone_name_class):
-        if ("notify_master" in self._zonemgr_refresh_info[zone_name_class].keys()):
+        if ("notify_master" in
+                self._zonemgr_refresh_info[zone_name_class].keys()):
             return self._zonemgr_refresh_info[zone_name_class]["notify_master"]
 
         return None
 
     def _set_zone_notifier_master(self, zone_name_class, master_addr):
-        self._zonemgr_refresh_info[zone_name_class]["notify_master"] = master_addr
+        self._zonemgr_refresh_info[zone_name_class]["notify_master"] = \
+            master_addr
 
     def _clear_zone_notifier_master(self, zone_name_class):
-        if ("notify_master" in self._zonemgr_refresh_info[zone_name_class].keys()):
+        if ("notify_master" in
+                self._zonemgr_refresh_info[zone_name_class].keys()):
             del self._zonemgr_refresh_info[zone_name_class]["notify_master"]
 
     def _get_zone_state(self, zone_name_class):
@@ -295,7 +360,7 @@ class ZonemgrRefresh:
     def _send_command(self, module_name, command_name, params):
         """Send command between modules."""
         try:
-            self._mccs.rpc_call(command_name, module_name, params=params)
+            self._module_cc.rpc_call(command_name, module_name, params=params)
         except socket.error:
             # FIXME: WTF? Where does socket.error come from? And how do we ever
             # dare ignore such serious error? It can only be broken link to
@@ -314,7 +379,8 @@ class ZonemgrRefresh:
             # If hasn't received refresh response but are within refresh
             # timeout, skip the zone
             if (ZONE_REFRESHING == zone_state and
-                (self._get_zone_refresh_timeout(zone_name_class) > self._get_current_time())):
+                (self._get_zone_refresh_timeout(zone_name_class) >
+                     self._get_current_time())):
                 continue
 
             # Get the zone with minimum next_refresh_time
@@ -324,7 +390,8 @@ class ZonemgrRefresh:
                 zone_need_refresh = zone_name_class
 
             # Find the zone need do refresh
-            if (self._get_zone_next_refresh_time(zone_need_refresh) < self._get_current_time()):
+            if (self._get_zone_next_refresh_time(zone_need_refresh) <
+                    self._get_current_time()):
                 break
 
         return zone_need_refresh
@@ -332,9 +399,12 @@ class ZonemgrRefresh:
 
     def _do_refresh(self, zone_name_class):
         """Do zone refresh."""
-        logger.debug(DBG_ZONEMGR_BASIC, ZONEMGR_REFRESH_ZONE, zone_name_class[0], zone_name_class[1])
+        logger.debug(DBG_ZONEMGR_BASIC, ZONEMGR_REFRESH_ZONE,
+                     zone_name_class[0], zone_name_class[1])
         self._set_zone_state(zone_name_class, ZONE_REFRESHING)
-        self._set_zone_refresh_timeout(zone_name_class, self._get_current_time() + self._max_transfer_timeout)
+        self._set_zone_refresh_timeout(zone_name_class,
+                                       self._get_current_time() +
+                                           self._max_transfer_timeout)
         notify_master = self._get_zone_notifier_master(zone_name_class)
         # If the zone has notify master, send notify command to xfrin module
         if notify_master:
@@ -351,13 +421,6 @@ class ZonemgrRefresh:
                     }
             self._send_command(XFRIN_MODULE_NAME, ZONE_REFRESH_COMMAND, param)
 
-    def _zone_mgr_is_empty(self):
-        """Does zone manager has no zone?"""
-        if not len(self._zonemgr_refresh_info):
-            return True
-
-        return False
-
     def _run_timer(self, start_event):
         while self._running:
             # Notify run_timer that we already started and are inside the loop.
@@ -367,24 +430,29 @@ class ZonemgrRefresh:
             if start_event:
                 start_event.set()
                 start_event = None
-            # If zonemgr has no zone, set timer timeout to self._lowerbound_retry.
-            if self._zone_mgr_is_empty():
+            # If zonemgr has no zone, set timeout to minimum
+            if not self._zonemgr_refresh_info:
                 timeout = self._lowerbound_retry
             else:
                 zone_need_refresh = self._find_need_do_refresh_zone()
-                # If don't get zone with minimum next refresh time, set timer timeout to self._lowerbound_retry.
+                # If don't get zone with minimum next refresh time, set
+                # timeout to minimum
                 if not zone_need_refresh:
                     timeout = self._lowerbound_retry
                 else:
-                    timeout = self._get_zone_next_refresh_time(zone_need_refresh) - self._get_current_time()
-                    if (timeout < 0):
+                    timeout = \
+                        self._get_zone_next_refresh_time(zone_need_refresh) - \
+                        self._get_current_time()
+                    if timeout < 0:
                         self._do_refresh(zone_need_refresh)
                         continue
 
             """ Wait for the socket notification for a maximum time of timeout
             in seconds (as float)."""
             try:
-                rlist, wlist, xlist = select.select([self._check_sock, self._read_sock], [], [], timeout)
+                rlist, wlist, xlist = \
+                    select.select([self._check_sock, self._read_sock],
+                                  [], [], timeout)
             except select.error as e:
                 if e.args[0] == errno.EINTR:
                     (rlist, wlist, xlist) = ([], [], [])
@@ -403,8 +471,8 @@ class ZonemgrRefresh:
 
     def run_timer(self, daemon=False):
         """
-        Keep track of zone timers. Spawns and starts a thread. The thread object
-        is returned.
+        Keep track of zone timers. Spawns and starts a thread. The thread
+        object is returned.
 
         You can stop it by calling shutdown().
         """
@@ -429,6 +497,20 @@ class ZonemgrRefresh:
         # Return the thread to anyone interested
         return self._thread
 
+    def _datasrc_config_handler(self, new_config, config_data):
+        """Configuration handler of the 'data_sources' module.
+
+        The actual handling is delegated to the DataSrcClientsMgr class;
+        this method is a simple wrapper.
+
+        This is essentially private, but implemented as 'protected' so tests
+        can refer to it; other external use is prohibited.
+        """
+        try:
+            self._datasrc_clients_mgr.reconfigure(new_config, config_data)
+        except isc.server_common.datasrc_clients_mgr.ConfigError as ex:
+            logger.error(ZONEMGR_DATASRC_CONFIG_ERROR, ex)
+
     def shutdown(self):
         """
         Stop the run_timer() thread. Block until it finished. This must be
@@ -450,7 +532,7 @@ class ZonemgrRefresh:
         self._read_sock = None
         self._write_sock = None
 
-    def update_config_data(self, new_config, module_cc_session):
+    def update_config_data(self, new_config, module_cc):
         """ update ZonemgrRefresh config """
         # Get a new value, but only if it is defined (commonly used below)
         # We don't use "value or default", because if value would be
@@ -499,7 +581,7 @@ class ZonemgrRefresh:
                     # Currently we use an explicit get_default_value call
                     # in case the class hasn't been set. Alternatively, we
                     # could use
-                    # module_cc_session.get_value('secondary_zones[INDEX]/class')
+                    # module_cc.get_value('secondary_zones[INDEX]/class')
                     # To get either the value that was set, or the default if
                     # it wasn't set.
                     # But the real solution would be to make new_config a type
@@ -509,7 +591,7 @@ class ZonemgrRefresh:
                     if 'class' in secondary_zone:
                         rr_class = secondary_zone['class']
                     else:
-                        rr_class = module_cc_session.get_default_value(
+                        rr_class = module_cc.get_default_value(
                                         'secondary_zones/class')
                     # Convert rr_class to and from RRClass to check its value
                     try:
@@ -521,10 +603,12 @@ class ZonemgrRefresh:
                     required[name_class] = True
                     # Add it only if it isn't there already
                     if not name_class in self._zonemgr_refresh_info:
-                        # If we are not able to find it in database, log an warning
+                        # If we are not able to find it in database, log an
+                        # warning
                         self.zonemgr_add_zone(name_class)
                 # Drop the zones that are no longer there
-                # Do it in two phases, python doesn't like deleting while iterating
+                # Do it in two phases, python doesn't like deleting while
+                # iterating
                 to_drop = []
                 for old_zone in self._zonemgr_refresh_info:
                     if not old_zone in required:
@@ -539,10 +623,11 @@ class Zonemgr:
     def __init__(self):
         self._zone_refresh = None
         self._setup_session()
-        self._db_file = self.get_db_file()
-        # Create socket pair for communicating between main thread and zonemgr timer thread
-        self._master_socket, self._slave_socket = socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM)
-        self._zone_refresh = ZonemgrRefresh(self._db_file, self._slave_socket, self._module_cc)
+        # Create socket pair for communicating between main thread and zonemgr
+        # timer thread
+        self._master_socket, self._slave_socket = \
+            socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM)
+        self._zone_refresh = ZonemgrRefresh(self._slave_socket, self._module_cc)
         self._zone_refresh.run_timer()
 
         self._lock = threading.Lock()
@@ -550,9 +635,10 @@ class Zonemgr:
         self.running = False
 
     def _setup_session(self):
-        """Setup two sessions for zonemgr, one(self._module_cc) is used for receiving
-        commands and config data sent from other modules, another one (self._cc)
-        is used to send commands to proper modules."""
+        """Setup two sessions for zonemgr, one(self._module_cc) is used for
+        receiving commands and config data sent from other modules, another
+        one (self._cc) is used to send commands to proper modules.
+        """
         self._module_cc = isc.config.ModuleCCSession(SPECFILE_LOCATION,
                                                   self.config_handler,
                                                   self.command_handler)
@@ -561,18 +647,9 @@ class Zonemgr:
         self._config_data_check(self._config_data)
         self._module_cc.start()
 
-    def get_db_file(self):
-        db_file, is_default = self._module_cc.get_remote_config_value(AUTH_MODULE_NAME, "database_file")
-        # this too should be unnecessary, but currently the
-        # 'from build' override isn't stored in the config
-        # (and we don't have indirect python access to datasources yet)
-        if is_default and "B10_FROM_BUILD" in os.environ:
-            db_file = os.environ["B10_FROM_BUILD"] + "/bind10_zones.sqlite3"
-        return db_file
-
     def shutdown(self):
         """Shutdown the zonemgr process. The thread which is keeping track of
-           zone timers should be terminated.
+        zone timers should be terminated.
         """
         self._zone_refresh.shutdown()
 
@@ -596,7 +673,8 @@ class Zonemgr:
         self._config_data_check(complete)
         if self._zone_refresh is not None:
             try:
-                self._zone_refresh.update_config_data(complete, self._module_cc)
+                self._zone_refresh.update_config_data(complete,
+                                                      self._module_cc)
             except Exception as e:
                 answer = create_answer(1, str(e))
                 ok = False
@@ -608,7 +686,8 @@ class Zonemgr:
     def _config_data_check(self, config_data):
         """Check whether the new config data is valid or
         not. It contains only basic logic, not full check against
-        database."""
+        database.
+        """
         # jitter should not be bigger than half of the original value
         if config_data.get('refresh_jitter') > 0.5:
             config_data['refresh_jitter'] = 0.5
@@ -625,7 +704,7 @@ class Zonemgr:
             logger.error(ZONEMGR_NO_ZONE_CLASS)
             raise ZonemgrException("zone class should be provided")
 
-        if (command != ZONE_NOTIFY_COMMAND):
+        if command != ZONE_NOTIFY_COMMAND:
             return (zone_name, zone_class)
 
         master_str = args.get("master")
@@ -641,7 +720,8 @@ class Zonemgr:
         ZONE_NOTIFY_COMMAND is issued by Auth process;
         ZONE_NEW_DATA_READY_CMD and ZONE_XFRIN_FAILED are issued by
         Xfrin process;
-        shutdown is issued by a user or Init process. """
+        shutdown is issued by a user or Init process.
+        """
         answer = create_answer(0)
         if command == ZONE_NOTIFY_COMMAND:
             """ Handle Auth notify command"""
diff --git a/src/bin/zonemgr/zonemgr_messages.mes b/src/bin/zonemgr/zonemgr_messages.mes
index 0334858..e749e3b 100644
--- a/src/bin/zonemgr/zonemgr_messages.mes
+++ b/src/bin/zonemgr/zonemgr_messages.mes
@@ -1,4 +1,4 @@
-# Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+# Copyright (C) 2011-2013  Internet Systems Consortium, Inc. ("ISC")
 #
 # Permission to use, copy, modify, and/or distribute this software for any
 # purpose with or without fee is hereby granted, provided that the above
@@ -19,6 +19,16 @@
 An error was encountered on the command channel.  The message indicates
 the nature of the error.
 
+% ZONEMGR_DATASRC_CONFIG_ERROR failed to update data source configuration: %1
+Configuration for the global data sources is updated, but the update
+cannot be applied to zonemgr.  The zonemgr module will still keep running
+with the previous configuration, but the cause of the failure and
+other log messages must be carefully examined because if only zonemgr
+rejects the new configuration then the entire BIND 10 system will have
+inconsistent state among different modules.  If other modules accept
+the update but zonemgr produces this error, the zonemgr module should
+probably be restarted.
+
 % ZONEMGR_JITTER_TOO_BIG refresh_jitter is too big, setting to 0.5
 The value specified in the configuration for the refresh jitter is too large
 so its value has been set to the maximum of 0.5.
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
index f636c0d..79a8263 100644
--- a/src/lib/Makefile.am
+++ b/src/lib/Makefile.am
@@ -1,3 +1,3 @@
-SUBDIRS = exceptions util log cryptolink dns cc config acl xfr bench \
+SUBDIRS = exceptions util log hooks cryptolink dns cc config acl xfr bench \
           asiolink asiodns nsas cache resolve testutils datasrc \
           server_common python dhcp dhcpsrv statistics
diff --git a/src/lib/dhcp/iface_mgr.cc b/src/lib/dhcp/iface_mgr.cc
index 1e97205..128aafe 100644
--- a/src/lib/dhcp/iface_mgr.cc
+++ b/src/lib/dhcp/iface_mgr.cc
@@ -51,7 +51,8 @@ IfaceMgr::instance() {
 Iface::Iface(const std::string& name, int ifindex)
     :name_(name), ifindex_(ifindex), mac_len_(0), hardware_type_(0),
      flag_loopback_(false), flag_up_(false), flag_running_(false),
-     flag_multicast_(false), flag_broadcast_(false), flags_(0)
+     flag_multicast_(false), flag_broadcast_(false), flags_(0),
+     inactive4_(false), inactive6_(false)
 {
     memset(mac_, 0, sizeof(mac_));
 }
@@ -295,7 +296,8 @@ bool IfaceMgr::openSockets4(const uint16_t port, const bool use_bcast) {
 
         if (iface->flag_loopback_ ||
             !iface->flag_up_ ||
-            !iface->flag_running_) {
+            !iface->flag_running_ ||
+            iface->inactive4_) {
             continue;
         }
 
@@ -361,7 +363,8 @@ bool IfaceMgr::openSockets6(const uint16_t port) {
 
         if (iface->flag_loopback_ ||
             !iface->flag_up_ ||
-            !iface->flag_running_) {
+            !iface->flag_running_ ||
+            iface->inactive6_) {
             continue;
         }
 
diff --git a/src/lib/dhcp/iface_mgr.h b/src/lib/dhcp/iface_mgr.h
index 35b4dc0..217524d 100644
--- a/src/lib/dhcp/iface_mgr.h
+++ b/src/lib/dhcp/iface_mgr.h
@@ -309,6 +309,14 @@ public:
     /// Interface flags (this value is as is returned by OS,
     /// it may mean different things on different OSes).
     uint32_t flags_;
+
+    /// Indicates that IPv4 sockets should (true) or should not (false)
+    /// be opened on this interface.
+    bool inactive4_;
+
+    /// Indicates that IPv6 sockets should (true) or should not (false)
+    /// be opened on this interface.
+    bool inactive6_;
 };
 
 /// @brief Handles network interfaces, transmission and reception.
diff --git a/src/lib/dhcpsrv/Makefile.am b/src/lib/dhcpsrv/Makefile.am
index 29e8c2f..8641f4f 100644
--- a/src/lib/dhcpsrv/Makefile.am
+++ b/src/lib/dhcpsrv/Makefile.am
@@ -59,8 +59,12 @@ libb10_dhcpsrv_la_CXXFLAGS = $(AM_CXXFLAGS)
 libb10_dhcpsrv_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
 libb10_dhcpsrv_la_LIBADD   = $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
 libb10_dhcpsrv_la_LIBADD  += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
+libb10_dhcpsrv_la_LIBADD  += $(top_builddir)/src/lib/hooks/libb10-hooks.la
+libb10_dhcpsrv_la_LIBADD  += $(top_builddir)/src/lib/log/libb10-log.la
 libb10_dhcpsrv_la_LIBADD  += $(top_builddir)/src/lib/util/libb10-util.la
 libb10_dhcpsrv_la_LIBADD  += $(top_builddir)/src/lib/cc/libb10-cc.la
+libb10_dhcpsrv_la_LIBADD  += $(top_builddir)/src/lib/hooks/libb10-hooks.la
+
 libb10_dhcpsrv_la_LDFLAGS  = -no-undefined -version-info 3:0:0
 if HAVE_MYSQL
 libb10_dhcpsrv_la_LDFLAGS += $(MYSQL_LIBS)
diff --git a/src/lib/dhcpsrv/alloc_engine.cc b/src/lib/dhcpsrv/alloc_engine.cc
index 9c5bdeb..d7417d2 100644
--- a/src/lib/dhcpsrv/alloc_engine.cc
+++ b/src/lib/dhcpsrv/alloc_engine.cc
@@ -16,11 +16,37 @@
 #include <dhcpsrv/dhcpsrv_log.h>
 #include <dhcpsrv/lease_mgr_factory.h>
 
+#include <hooks/server_hooks.h>
+#include <hooks/hooks_manager.h>
+
 #include <cstring>
 #include <vector>
 #include <string.h>
 
 using namespace isc::asiolink;
+using namespace isc::hooks;
+
+namespace {
+
+/// Structure that holds registered hook indexes
+struct AllocEngineHooks {
+    int hook_index_lease4_select_; ///< index for "lease4_receive" hook point
+    int hook_index_lease6_select_; ///< index for "lease6_receive" hook point
+
+    /// Constructor that registers hook points for AllocationEngine
+    AllocEngineHooks() {
+        hook_index_lease4_select_ = HooksManager::registerHook("lease4_select");
+        hook_index_lease6_select_ = HooksManager::registerHook("lease6_select");
+    }
+};
+
+// Declare a Hooks object. As this is outside any function or method, it
+// will be instantiated (and the constructor run) when the module is loaded.
+// As a result, the hook indexes will be defined before any method in this
+// module is called.
+AllocEngineHooks Hooks;
+
+}; // anonymous namespace
 
 namespace isc {
 namespace dhcp {
@@ -161,6 +187,10 @@ AllocEngine::AllocEngine(AllocType engine_type, unsigned int attempts)
     default:
         isc_throw(BadValue, "Invalid/unsupported allocation algorithm");
     }
+
+    // Register hook points
+    hook_index_lease4_select_ = Hooks.hook_index_lease4_select_;
+    hook_index_lease6_select_ = Hooks.hook_index_lease6_select_;
 }
 
 Lease6Ptr
@@ -168,7 +198,8 @@ AllocEngine::allocateAddress6(const Subnet6Ptr& subnet,
                               const DuidPtr& duid,
                               uint32_t iaid,
                               const IOAddress& hint,
-                              bool fake_allocation /* = false */ ) {
+                              bool fake_allocation,
+                              const isc::hooks::CalloutHandlePtr& callout_handle) {
 
     try {
         // That check is not necessary. We create allocator in AllocEngine
@@ -201,7 +232,8 @@ AllocEngine::allocateAddress6(const Subnet6Ptr& subnet,
                 /// implemented
 
                 // the hint is valid and not currently used, let's create a lease for it
-                Lease6Ptr lease = createLease6(subnet, duid, iaid, hint, fake_allocation);
+                Lease6Ptr lease = createLease6(subnet, duid, iaid, hint, callout_handle,
+                                               fake_allocation);
 
                 // It can happen that the lease allocation failed (we could have lost
                 // the race condition. That means that the hint is lo longer usable and
@@ -212,7 +244,7 @@ AllocEngine::allocateAddress6(const Subnet6Ptr& subnet,
             } else {
                 if (existing->expired()) {
                     return (reuseExpiredLease(existing, subnet, duid, iaid,
-                                              fake_allocation));
+                                              callout_handle, fake_allocation));
                 }
 
             }
@@ -246,7 +278,7 @@ AllocEngine::allocateAddress6(const Subnet6Ptr& subnet,
                 // there's no existing lease for selected candidate, so it is
                 // free. Let's allocate it.
                 Lease6Ptr lease = createLease6(subnet, duid, iaid, candidate,
-                                              fake_allocation);
+                                               callout_handle, fake_allocation);
                 if (lease) {
                     return (lease);
                 }
@@ -257,7 +289,7 @@ AllocEngine::allocateAddress6(const Subnet6Ptr& subnet,
             } else {
                 if (existing->expired()) {
                     return (reuseExpiredLease(existing, subnet, duid, iaid,
-                                              fake_allocation));
+                                              callout_handle, fake_allocation));
                 }
             }
 
@@ -283,7 +315,8 @@ AllocEngine::allocateAddress4(const SubnetPtr& subnet,
                               const ClientIdPtr& clientid,
                               const HWAddrPtr& hwaddr,
                               const IOAddress& hint,
-                              bool fake_allocation /* = false */ ) {
+                              bool fake_allocation,
+                              const isc::hooks::CalloutHandlePtr& callout_handle) {
 
     try {
         // Allocator is always created in AllocEngine constructor and there is
@@ -336,7 +369,8 @@ AllocEngine::allocateAddress4(const SubnetPtr& subnet,
                 /// implemented
 
                 // The hint is valid and not currently used, let's create a lease for it
-                Lease4Ptr lease = createLease4(subnet, clientid, hwaddr, hint, fake_allocation);
+                Lease4Ptr lease = createLease4(subnet, clientid, hwaddr, hint,
+                                               callout_handle, fake_allocation);
 
                 // It can happen that the lease allocation failed (we could have lost
                 // the race condition. That means that the hint is lo longer usable and
@@ -347,7 +381,7 @@ AllocEngine::allocateAddress4(const SubnetPtr& subnet,
             } else {
                 if (existing->expired()) {
                     return (reuseExpiredLease(existing, subnet, clientid, hwaddr,
-                                              fake_allocation));
+                                              callout_handle, fake_allocation));
                 }
 
             }
@@ -381,7 +415,7 @@ AllocEngine::allocateAddress4(const SubnetPtr& subnet,
                 // there's no existing lease for selected candidate, so it is
                 // free. Let's allocate it.
                 Lease4Ptr lease = createLease4(subnet, clientid, hwaddr, candidate,
-                                              fake_allocation);
+                                               callout_handle, fake_allocation);
                 if (lease) {
                     return (lease);
                 }
@@ -392,7 +426,7 @@ AllocEngine::allocateAddress4(const SubnetPtr& subnet,
             } else {
                 if (existing->expired()) {
                     return (reuseExpiredLease(existing, subnet, clientid, hwaddr,
-                                              fake_allocation));
+                                              callout_handle, fake_allocation));
                 }
             }
 
@@ -438,6 +472,7 @@ Lease6Ptr AllocEngine::reuseExpiredLease(Lease6Ptr& expired,
                                          const Subnet6Ptr& subnet,
                                          const DuidPtr& duid,
                                          uint32_t iaid,
+                                         const isc::hooks::CalloutHandlePtr& callout_handle,
                                          bool fake_allocation /*= false */ ) {
 
     if (!expired->expired()) {
@@ -461,6 +496,39 @@ Lease6Ptr AllocEngine::reuseExpiredLease(Lease6Ptr& expired,
     /// @todo: log here that the lease was reused (there's ticket #2524 for
     /// logging in libdhcpsrv)
 
+    // Let's execute all callouts registered for lease6_select
+    if (callout_handle &&
+        HooksManager::getHooksManager().calloutsPresent(hook_index_lease6_select_)) {
+
+        // Delete all previous arguments
+        callout_handle->deleteAllArguments();
+
+        // Pass necessary arguments
+        // Subnet from which we do the allocation
+        callout_handle->setArgument("subnet6", subnet);
+
+        // Is this solicit (fake = true) or request (fake = false)
+        callout_handle->setArgument("fake_allocation", fake_allocation);
+
+        // The lease that will be assigned to a client
+        callout_handle->setArgument("lease6", expired);
+
+        // Call the callouts
+        HooksManager::callCallouts(hook_index_lease6_select_, *callout_handle);
+
+        // Callouts decided to skip the action. This means that the lease is not
+        // assigned, so the client will get NoAddrAvail as a result. The lease
+        // won't be inserted into the database.
+        if (callout_handle->getSkip()) {
+            LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_HOOKS, DHCPSRV_HOOK_LEASE6_SELECT_SKIP);
+            return (Lease6Ptr());
+        }
+
+        // Let's use whatever callout returned. Hopefully it is the same lease
+        // we handled to it.
+        callout_handle->getArgument("lease6", expired);
+    }
+
     if (!fake_allocation) {
         // for REQUEST we do update the lease
         LeaseMgrFactory::instance().updateLease6(expired);
@@ -478,6 +546,7 @@ Lease4Ptr AllocEngine::reuseExpiredLease(Lease4Ptr& expired,
                                          const SubnetPtr& subnet,
                                          const ClientIdPtr& clientid,
                                          const HWAddrPtr& hwaddr,
+                                         const isc::hooks::CalloutHandlePtr& callout_handle,
                                          bool fake_allocation /*= false */ ) {
 
     if (!expired->expired()) {
@@ -500,6 +569,44 @@ Lease4Ptr AllocEngine::reuseExpiredLease(Lease4Ptr& expired,
     /// @todo: log here that the lease was reused (there's ticket #2524 for
     /// logging in libdhcpsrv)
 
+    // Let's execute all callouts registered for lease4_select
+    if (callout_handle &&
+        HooksManager::getHooksManager().calloutsPresent(hook_index_lease4_select_)) {
+
+        // Delete all previous arguments
+        callout_handle->deleteAllArguments();
+
+        // Pass necessary arguments
+
+        // Subnet from which we do the allocation. Convert the general subnet
+        // pointer to a pointer to a Subnet4.  Note that because we are using
+        // boost smart pointers here, we need to do the cast using the boost
+        // version of dynamic_pointer_cast.
+        Subnet4Ptr subnet4 = boost::dynamic_pointer_cast<Subnet4>(subnet);
+        callout_handle->setArgument("subnet4", subnet4);
+
+        // Is this solicit (fake = true) or request (fake = false)
+        callout_handle->setArgument("fake_allocation", fake_allocation);
+
+        // The lease that will be assigned to a client
+        callout_handle->setArgument("lease4", expired);
+
+        // Call the callouts
+        HooksManager::callCallouts(hook_index_lease6_select_, *callout_handle);
+
+        // Callouts decided to skip the action. This means that the lease is not
+        // assigned, so the client will get NoAddrAvail as a result. The lease
+        // won't be inserted into the database.
+        if (callout_handle->getSkip()) {
+            LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_HOOKS, DHCPSRV_HOOK_LEASE4_SELECT_SKIP);
+            return (Lease4Ptr());
+        }
+
+        // Let's use whatever callout returned. Hopefully it is the same lease
+        // we handled to it.
+        callout_handle->getArgument("lease4", expired);
+    }
+
     if (!fake_allocation) {
         // for REQUEST we do update the lease
         LeaseMgrFactory::instance().updateLease4(expired);
@@ -517,12 +624,45 @@ Lease6Ptr AllocEngine::createLease6(const Subnet6Ptr& subnet,
                                     const DuidPtr& duid,
                                     uint32_t iaid,
                                     const IOAddress& addr,
+                                    const isc::hooks::CalloutHandlePtr& callout_handle,
                                     bool fake_allocation /*= false */ ) {
 
     Lease6Ptr lease(new Lease6(Lease6::LEASE_IA_NA, addr, duid, iaid,
                                subnet->getPreferred(), subnet->getValid(),
                                subnet->getT1(), subnet->getT2(), subnet->getID()));
 
+    // Let's execute all callouts registered for lease6_select
+    if (callout_handle &&
+        HooksManager::getHooksManager().calloutsPresent(hook_index_lease6_select_)) {
+
+        // Delete all previous arguments
+        callout_handle->deleteAllArguments();
+
+        // Pass necessary arguments
+
+        // Subnet from which we do the allocation
+        callout_handle->setArgument("subnet6", subnet);
+
+        // Is this solicit (fake = true) or request (fake = false)
+        callout_handle->setArgument("fake_allocation", fake_allocation);
+        callout_handle->setArgument("lease6", lease);
+
+        // This is the first callout, so no need to clear any arguments
+        HooksManager::callCallouts(hook_index_lease6_select_, *callout_handle);
+
+        // Callouts decided to skip the action. This means that the lease is not
+        // assigned, so the client will get NoAddrAvail as a result. The lease
+        // won't be inserted into the database.
+        if (callout_handle->getSkip()) {
+            LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_HOOKS, DHCPSRV_HOOK_LEASE6_SELECT_SKIP);
+            return (Lease6Ptr());
+        }
+
+        // Let's use whatever callout returned. Hopefully it is the same lease
+        // we handled to it.
+        callout_handle->getArgument("lease6", lease);
+    }
+
     if (!fake_allocation) {
         // That is a real (REQUEST) allocation
         bool status = LeaseMgrFactory::instance().addLease(lease);
@@ -555,6 +695,7 @@ Lease4Ptr AllocEngine::createLease4(const SubnetPtr& subnet,
                                     const DuidPtr& clientid,
                                     const HWAddrPtr& hwaddr,
                                     const IOAddress& addr,
+                                    const isc::hooks::CalloutHandlePtr& callout_handle,
                                     bool fake_allocation /*= false */ ) {
     if (!hwaddr) {
         isc_throw(BadValue, "Can't create a lease with NULL HW address");
@@ -572,6 +713,46 @@ Lease4Ptr AllocEngine::createLease4(const SubnetPtr& subnet,
                                subnet->getT1(), subnet->getT2(), now,
                                subnet->getID()));
 
+    // Let's execute all callouts registered for lease4_select
+    if (callout_handle &&
+        HooksManager::getHooksManager().calloutsPresent(hook_index_lease4_select_)) {
+
+        // Delete all previous arguments
+        callout_handle->deleteAllArguments();
+
+        // Pass necessary arguments
+
+        // Subnet from which we do the allocation (That's as far as we can go
+        // with using SubnetPtr to point to Subnet4 object. Users should not
+        // be confused with dynamic_pointer_casts. They should get a concrete
+        // pointer (Subnet4Ptr) pointing to a Subnet4 object.
+        Subnet4Ptr subnet4 = boost::dynamic_pointer_cast<Subnet4>(subnet);
+        callout_handle->setArgument("subnet4", subnet4);
+
+        // Is this solicit (fake = true) or request (fake = false)
+        callout_handle->setArgument("fake_allocation", fake_allocation);
+
+        // Pass the intended lease as well
+        callout_handle->setArgument("lease4", lease);
+
+        // This is the first callout, so no need to clear any arguments
+        HooksManager::callCallouts(hook_index_lease4_select_, *callout_handle);
+
+        // Callouts decided to skip the action. This means that the lease is not
+        // assigned, so the client will get NoAddrAvail as a result. The lease
+        // won't be inserted into the database.
+        if (callout_handle->getSkip()) {
+            LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_HOOKS, DHCPSRV_HOOK_LEASE4_SELECT_SKIP);
+            return (Lease4Ptr());
+        }
+
+        // Let's use whatever callout returned. Hopefully it is the same lease
+        // we handled to it.
+        callout_handle->getArgument("lease4", lease);
+    }
+
+
+
     if (!fake_allocation) {
         // That is a real (REQUEST) allocation
         bool status = LeaseMgrFactory::instance().addLease(lease);
diff --git a/src/lib/dhcpsrv/alloc_engine.h b/src/lib/dhcpsrv/alloc_engine.h
index 7e3d136..08381ab 100644
--- a/src/lib/dhcpsrv/alloc_engine.h
+++ b/src/lib/dhcpsrv/alloc_engine.h
@@ -20,6 +20,7 @@
 #include <dhcp/hwaddr.h>
 #include <dhcpsrv/subnet.h>
 #include <dhcpsrv/lease_mgr.h>
+#include <hooks/callout_handle.h>
 
 #include <boost/shared_ptr.hpp>
 #include <boost/noncopyable.hpp>
@@ -193,13 +194,16 @@ protected:
     /// @param hint a hint that the client provided
     /// @param fake_allocation is this real i.e. REQUEST (false) or just picking
     ///        an address for DISCOVER that is not really allocated (true)
+    /// @param callout_handle a callout handle (used in hooks). A lease callouts
+    ///        will be executed if this parameter is passed.
     /// @return Allocated IPv4 lease (or NULL if allocation failed)
     Lease4Ptr
     allocateAddress4(const SubnetPtr& subnet,
                      const ClientIdPtr& clientid,
                      const HWAddrPtr& hwaddr,
                      const isc::asiolink::IOAddress& hint,
-                     bool fake_allocation);
+                     bool fake_allocation,
+                     const isc::hooks::CalloutHandlePtr& callout_handle);
 
     /// @brief Renews a IPv4 lease
     ///
@@ -235,13 +239,17 @@ protected:
     /// @param hint a hint that the client provided
     /// @param fake_allocation is this real i.e. REQUEST (false) or just picking
     ///        an address for SOLICIT that is not really allocated (true)
+    /// @param callout_handle a callout handle (used in hooks). A lease callouts
+    ///        will be executed if this parameter is passed.
+    ///
     /// @return Allocated IPv6 lease (or NULL if allocation failed)
     Lease6Ptr
     allocateAddress6(const Subnet6Ptr& subnet,
                      const DuidPtr& duid,
                      uint32_t iaid,
                      const isc::asiolink::IOAddress& hint,
-                     bool fake_allocation);
+                     bool fake_allocation,
+                     const isc::hooks::CalloutHandlePtr& callout_handle);
 
     /// @brief Destructor. Used during DHCPv6 service shutdown.
     virtual ~AllocEngine();
@@ -257,6 +265,9 @@ private:
     /// @param clientid client identifier
     /// @param hwaddr client's hardware address
     /// @param addr an address that was selected and is confirmed to be available
+    /// @param callout_handle a callout handle (used in hooks). A lease callouts
+    ///        will be executed if this parameter is passed (and there are callouts
+    ///        registered)
     /// @param fake_allocation is this real i.e. REQUEST (false) or just picking
     ///        an address for DISCOVER that is not really allocated (true)
     /// @return allocated lease (or NULL in the unlikely case of the lease just
@@ -264,6 +275,7 @@ private:
     Lease4Ptr createLease4(const SubnetPtr& subnet, const DuidPtr& clientid,
                            const HWAddrPtr& hwaddr,
                            const isc::asiolink::IOAddress& addr,
+                           const isc::hooks::CalloutHandlePtr& callout_handle,
                            bool fake_allocation = false);
 
     /// @brief creates a lease and inserts it in LeaseMgr if necessary
@@ -276,12 +288,16 @@ private:
     /// @param duid client's DUID
     /// @param iaid IAID from the IA_NA container the client sent to us
     /// @param addr an address that was selected and is confirmed to be available
+    /// @param callout_handle a callout handle (used in hooks). A lease callouts
+    ///        will be executed if this parameter is passed (and there are callouts
+    ///        registered)
     /// @param fake_allocation is this real i.e. REQUEST (false) or just picking
     ///        an address for SOLICIT that is not really allocated (true)
     /// @return allocated lease (or NULL in the unlikely case of the lease just
     ///        becomed unavailable)
     Lease6Ptr createLease6(const Subnet6Ptr& subnet, const DuidPtr& duid,
                            uint32_t iaid, const isc::asiolink::IOAddress& addr,
+                           const isc::hooks::CalloutHandlePtr& callout_handle,
                            bool fake_allocation = false);
 
     /// @brief Reuses expired IPv4 lease
@@ -294,6 +310,8 @@ private:
     /// @param subnet subnet the lease is allocated from
     /// @param clientid client identifier
     /// @param hwaddr client's hardware address
+    /// @param callout_handle a callout handle (used in hooks). A lease callouts
+    ///        will be executed if this parameter is passed.
     /// @param fake_allocation is this real i.e. REQUEST (false) or just picking
     ///        an address for DISCOVER that is not really allocated (true)
     /// @return refreshed lease
@@ -301,6 +319,7 @@ private:
     Lease4Ptr reuseExpiredLease(Lease4Ptr& expired, const SubnetPtr& subnet,
                                 const ClientIdPtr& clientid,
                                 const HWAddrPtr& hwaddr,
+                                const isc::hooks::CalloutHandlePtr& callout_handle,
                                 bool fake_allocation = false);
 
     /// @brief Reuses expired IPv6 lease
@@ -313,12 +332,15 @@ private:
     /// @param subnet subnet the lease is allocated from
     /// @param duid client's DUID
     /// @param iaid IAID from the IA_NA container the client sent to us
+    /// @param callout_handle a callout handle (used in hooks). A lease callouts
+    ///        will be executed if this parameter is passed.
     /// @param fake_allocation is this real i.e. REQUEST (false) or just picking
     ///        an address for SOLICIT that is not really allocated (true)
     /// @return refreshed lease
     /// @throw BadValue if trying to recycle lease that is still valid
     Lease6Ptr reuseExpiredLease(Lease6Ptr& expired, const Subnet6Ptr& subnet,
                                 const DuidPtr& duid, uint32_t iaid,
+                                const isc::hooks::CalloutHandlePtr& callout_handle,
                                 bool fake_allocation = false);
 
     /// @brief a pointer to currently used allocator
@@ -326,6 +348,10 @@ private:
 
     /// @brief number of attempts before we give up lease allocation (0=unlimited)
     unsigned int attempts_;
+
+    // hook name indexes (used in hooks callouts)
+    int hook_index_lease4_select_; ///< index for lease4_select hook
+    int hook_index_lease6_select_; ///< index for lease6_select hook
 };
 
 }; // namespace isc::dhcp
diff --git a/src/lib/dhcpsrv/cfgmgr.cc b/src/lib/dhcpsrv/cfgmgr.cc
index 592efb7..56e4c8e 100644
--- a/src/lib/dhcpsrv/cfgmgr.cc
+++ b/src/lib/dhcpsrv/cfgmgr.cc
@@ -262,13 +262,66 @@ void CfgMgr::deleteSubnets6() {
     subnets6_.clear();
 }
 
+
 std::string CfgMgr::getDataDir() {
     return (datadir_);
 }
 
+void
+CfgMgr::addActiveIface(const std::string& iface) {
+    if (isIfaceListedActive(iface)) {
+        isc_throw(DuplicateListeningIface,
+                  "attempt to add duplicate interface '" << iface << "'"
+                  " to the set of interfaces on which server listens");
+    }
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_ADD_IFACE)
+        .arg(iface);
+    active_ifaces_.push_back(iface);
+}
+
+void
+CfgMgr::activateAllIfaces() {
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE,
+              DHCPSRV_CFGMGR_ALL_IFACES_ACTIVE);
+    all_ifaces_active_ = true;
+}
+
+void
+CfgMgr::deleteActiveIfaces() {
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE,
+              DHCPSRV_CFGMGR_CLEAR_ACTIVE_IFACES);
+    active_ifaces_.clear();
+    all_ifaces_active_ = false;
+}
+
+bool
+CfgMgr::isActiveIface(const std::string& iface) const {
+
+    // @todo Verify that the interface with the specified name is
+    // present in the system.
+
+    // If all interfaces are marked active, there is no need to check that
+    // the name of this interface has been explicitly listed.
+    if (all_ifaces_active_) {
+        return (true);
+    }
+    return (isIfaceListedActive(iface));
+}
+
+bool
+CfgMgr::isIfaceListedActive(const std::string& iface) const {
+    for (ActiveIfacesCollection::const_iterator it = active_ifaces_.begin();
+         it != active_ifaces_.end(); ++it) {
+        if (iface == *it) {
+            return (true);
+        }
+    }
+    return (false);
+}
 
 CfgMgr::CfgMgr()
-    :datadir_(DHCP_DATA_DIR) {
+    : datadir_(DHCP_DATA_DIR),
+      all_ifaces_active_(false) {
     // DHCP_DATA_DIR must be set set with -DDHCP_DATA_DIR="..." in Makefile.am
     // Note: the definition of DHCP_DATA_DIR needs to include quotation marks
     // See AM_CPPFLAGS definition in Makefile.am
diff --git a/src/lib/dhcpsrv/cfgmgr.h b/src/lib/dhcpsrv/cfgmgr.h
index 05c1752..0ec51d0 100644
--- a/src/lib/dhcpsrv/cfgmgr.h
+++ b/src/lib/dhcpsrv/cfgmgr.h
@@ -30,10 +30,21 @@
 #include <map>
 #include <string>
 #include <vector>
+#include <list>
 
 namespace isc {
 namespace dhcp {
 
+/// @brief Exception thrown when the same interface has been specified twice.
+///
+/// In particular, this exception is thrown when adding interface to the set
+/// of interfaces on which server is supposed to listen.
+class DuplicateListeningIface : public Exception {
+public:
+    DuplicateListeningIface(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) { };
+};
+
 
 /// @brief Configuration Manager
 ///
@@ -201,6 +212,26 @@ public:
     /// completely new?
     void deleteSubnets6();
 
+    /// @brief returns const reference to all subnets6
+    ///
+    /// This is used in a hook (subnet4_select), where the hook is able
+    /// to choose a different subnet. Server code has to offer a list
+    /// of possible choices (i.e. all subnets).
+    /// @return a pointer to const Subnet6 collection
+    const Subnet4Collection* getSubnets4() {
+        return (&subnets4_);
+    }
+
+    /// @brief returns const reference to all subnets6
+    ///
+    /// This is used in a hook (subnet6_select), where the hook is able
+    /// to choose a different subnet. Server code has to offer a list
+    /// of possible choices (i.e. all subnets).
+    /// @return a pointer to const Subnet6 collection
+    const Subnet6Collection* getSubnets6() {
+        return (&subnets6_);
+    }
+
     /// @brief get IPv4 subnet by address
     ///
     /// Finds a matching subnet, based on an address. This can be used
@@ -237,6 +268,43 @@ public:
     /// @return data directory
     std::string getDataDir();
 
+    /// @brief Adds the name of the interface to the set of interfaces on which
+    /// server should listen.
+    ///
+    /// @param iface A name of the interface being added to the listening set.
+    void addActiveIface(const std::string& iface);
+
+    /// @brief Sets the flag which indicates that server is supposed to listen
+    /// on all available interfaces.
+    ///
+    /// This function does not close or open sockets. It simply marks that
+    /// server should start to listen on all interfaces the next time sockets
+    /// are reopened. Server should examine this flag when it gets reconfigured
+    /// and configuration changes the interfaces that server should listen on.
+    void activateAllIfaces();
+
+    /// @brief Clear the collection of the interfaces that server should listen
+    /// on.
+    ///
+    /// Apart from clearing the list of interfaces specified with
+    /// @c CfgMgr::addListeningInterface, it also disables listening on all
+    /// interfaces if it has been enabled using
+    /// @c CfgMgr::activateAllInterfaces.
+    /// Likewise @c CfgMgr::activateAllIfaces, this function does not close or
+    /// open sockets. It marks all interfaces inactive for DHCP traffic.
+    /// Server should examine this new setting when it attempts to
+    /// reopen sockets (as a result of reconfiguration).
+    void deleteActiveIfaces();
+
+    /// @brief Check if specified interface should be used to listen to DHCP
+    /// traffic.
+    ///
+    /// @param iface A name of the interface to be checked.
+    ///
+    /// @return true if the specified interface belongs to the set of the
+    /// interfaces on which server is configured to listen.
+    bool isActiveIface(const std::string& iface) const;
+
 protected:
 
     /// @brief Protected constructor.
@@ -268,6 +336,20 @@ protected:
 
 private:
 
+    /// @brief Checks if the specified interface is listed as active.
+    ///
+    /// This function searches for the specified interface name on the list of
+    /// active interfaces: @c CfgMgr::active_ifaces_. It does not take into
+    /// account @c CfgMgr::all_ifaces_active_ flag. If this flag is set to true
+    /// but the specified interface does not belong to
+    /// @c CfgMgr::active_ifaces_, it will return false.
+    ///
+    /// @param iface interface name.
+    ///
+    /// @return true if specified interface belongs to
+    /// @c CfgMgr::active_ifaces_.
+    bool isIfaceListedActive(const std::string& iface) const;
+
     /// @brief A collection of option definitions.
     ///
     /// A collection of option definitions that can be accessed
@@ -283,6 +365,16 @@ private:
 
     /// @brief directory where data files (e.g. server-id) are stored
     std::string datadir_;
+
+    /// @name A collection of interface names on which server listens.
+    //@{
+    typedef std::list<std::string> ActiveIfacesCollection;
+    std::list<std::string> active_ifaces_;
+    //@}
+
+    /// A flag which indicates that server should listen on all available
+    /// interfaces.
+    bool all_ifaces_active_;
 };
 
 } // namespace isc::dhcp
diff --git a/src/lib/dhcpsrv/dhcp_parsers.cc b/src/lib/dhcpsrv/dhcp_parsers.cc
index d21538b..826b957 100644
--- a/src/lib/dhcpsrv/dhcp_parsers.cc
+++ b/src/lib/dhcpsrv/dhcp_parsers.cc
@@ -16,23 +16,30 @@
 #include <dhcp/libdhcp++.h>
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/dhcp_parsers.h>
+#include <hooks/hooks_manager.h>
 #include <util/encode/hex.h>
 #include <util/strutil.h>
 
+#include <boost/algorithm/string.hpp>
 #include <boost/foreach.hpp>
 #include <boost/lexical_cast.hpp>
-#include <boost/algorithm/string.hpp>
 #include <boost/lexical_cast.hpp>
 
-#include <string>
 #include <map>
+#include <string>
+#include <vector>
 
 using namespace std;
 using namespace isc::data;
+using namespace isc::hooks;
 
 namespace isc {
 namespace dhcp {
 
+namespace {
+const char* ALL_IFACES_KEYWORD = "*";
+}
+
 // *********************** ParserContext  *************************
 
 ParserContext::ParserContext(Option::Universe universe):
@@ -53,17 +60,17 @@ ParserContext::ParserContext(const ParserContext& rhs):
         universe_(rhs.universe_) {
     }
 
-ParserContext& 
+ParserContext&
 ParserContext::operator=(const ParserContext& rhs) {
         if (this != &rhs) {
-            boolean_values_ = 
+            boolean_values_ =
                 BooleanStoragePtr(new BooleanStorage(*(rhs.boolean_values_)));
-            uint32_values_ = 
+            uint32_values_ =
                 Uint32StoragePtr(new Uint32Storage(*(rhs.uint32_values_)));
-            string_values_ = 
+            string_values_ =
                 StringStoragePtr(new StringStorage(*(rhs.string_values_)));
             options_ = OptionStoragePtr(new OptionStorage(*(rhs.options_)));
-            option_defs_ = 
+            option_defs_ =
                 OptionDefStoragePtr(new OptionDefStorage(*(rhs.option_defs_)));
             universe_ = rhs.universe_;
         }
@@ -77,14 +84,14 @@ DebugParser::DebugParser(const std::string& param_name)
     :param_name_(param_name) {
 }
 
-void 
+void
 DebugParser::build(ConstElementPtr new_config) {
-    std::cout << "Build for token: [" << param_name_ << "] = ["
-        << value_->str() << "]" << std::endl; 
     value_ = new_config;
+    std::cout << "Build for token: [" << param_name_ << "] = ["
+        << value_->str() << "]" << std::endl;
 }
 
-void 
+void
 DebugParser::commit() {
     // Debug message. The whole DebugParser class is used only for parser
     // debugging, and is not used in production code. It is very convenient
@@ -102,7 +109,7 @@ template<> void ValueParser<bool>::build(isc::data::ConstElementPtr value) {
     try {
         value_ = value->boolValue();
     } catch (const isc::data::TypeError &) {
-        isc_throw(BadValue, " Wrong value type for " << param_name_ 
+        isc_throw(BadValue, " Wrong value type for " << param_name_
                   << " : build called with a non-boolean element.");
     }
 }
@@ -140,33 +147,147 @@ template <> void ValueParser<std::string>::build(ConstElementPtr value) {
 
 // ******************** InterfaceListConfigParser *************************
 
-InterfaceListConfigParser::InterfaceListConfigParser(const std::string& 
-                                                     param_name) {
-    if (param_name != "interface") {
+InterfaceListConfigParser::
+InterfaceListConfigParser(const std::string& param_name)
+    : activate_all_(false),
+      param_name_(param_name) {
+    if (param_name_ != "interfaces") {
         isc_throw(BadValue, "Internal error. Interface configuration "
             "parser called for the wrong parameter: " << param_name);
     }
 }
 
-void 
+void
 InterfaceListConfigParser::build(ConstElementPtr value) {
+    // First, we iterate over all specified entries and add it to the
+    // local container so as we can do some basic validation, e.g. eliminate
+    // duplicates.
     BOOST_FOREACH(ConstElementPtr iface, value->listValue()) {
-        interfaces_.push_back(iface->str());
+        std::string iface_name = iface->stringValue();
+        if (iface_name != ALL_IFACES_KEYWORD) {
+            // Let's eliminate duplicates. We could possibly allow duplicates,
+            // but if someone specified duplicated interface name it is likely
+            // that he mistyped the configuration. Failing here should draw his
+            // attention.
+            if (isIfaceAdded(iface_name)) {
+                isc_throw(isc::dhcp::DhcpConfigError, "duplicate interface"
+                          << " name '" << iface_name << "' specified in '"
+                          << param_name_ << "' configuration parameter");
+            }
+            // @todo check that this interface exists in the system!
+            // The IfaceMgr exposes mechanisms to check this.
+
+            // Add the interface name if ok.
+            interfaces_.push_back(iface_name);
+
+        } else {
+            activate_all_ = true;
+
+        }
     }
 }
 
-void 
+void
 InterfaceListConfigParser::commit() {
-    /// @todo: Implement per interface listening. Currently always listening
-    /// on all interfaces.
+    CfgMgr& cfg_mgr = CfgMgr::instance();
+    // Remove active interfaces and clear a flag which marks all interfaces
+    // active
+    cfg_mgr.deleteActiveIfaces();
+
+    if (activate_all_) {
+        // Activate all interfaces. There is not need to add their names
+        // explicitly.
+        cfg_mgr.activateAllIfaces();
+
+    } else {
+        // Explicitly add names of the interfaces which server should listen on.
+        BOOST_FOREACH(std::string iface, interfaces_) {
+            cfg_mgr.addActiveIface(iface);
+        }
+    }
+}
+
+bool
+InterfaceListConfigParser::isIfaceAdded(const std::string& iface) const {
+    for (IfaceListStorage::const_iterator it = interfaces_.begin();
+         it != interfaces_.end(); ++it) {
+        if (iface == *it) {
+            return (true);
+        }
+    }
+    return (false);
+}
+
+// ******************** HooksLibrariesParser *************************
+
+HooksLibrariesParser::HooksLibrariesParser(const std::string& param_name)
+    : libraries_(), changed_(false)
+{
+    // Sanity check on the name.
+    if (param_name != "hooks-libraries") {
+        isc_throw(BadValue, "Internal error. Hooks libraries "
+            "parser called for the wrong parameter: " << param_name);
+    }
+}
+
+void 
+HooksLibrariesParser::build(ConstElementPtr value) {
+    // Initialize.
+    libraries_.clear();
+    changed_ = false;
+
+    // Extract the list of libraries.
+    BOOST_FOREACH(ConstElementPtr iface, value->listValue()) {
+        string libname = iface->str();
+        boost::erase_all(libname, "\"");
+        libraries_.push_back(libname);
+    }
+
+    // Check if the list of libraries has changed.  If not, nothing is done
+    // - the command "DhcpN libreload" is required to reload the same
+    // libraries (this prevents needless reloads when anything else in the
+    // configuration is changed).
+/*
+    vector<string> current_libraries = HooksManager::getLibraryNames();
+    if (current_libraries == libraries_) {
+        return;
+    }
+
+    // Library list has changed, validate each of the libraries specified.
+    string error_libs = HooksManager::validateLibraries(libraries_);
+    if (!error_libs.empty()) {
+        isc_throw(DhcpConfigError, "hooks libraries failed to validate - "
+                  "library or libraries in error are: " + error_libs);
+    }
+*/
+    // The library list has changed and the libraries are valid, so flag for
+    // update when commit() is called.
+    changed_ = true;
+}
+
+void 
+HooksLibrariesParser::commit() {
+    /// Commits the list of libraries to the configuration manager storage if
+    /// the list of libraries has changed.
+    if (changed_) {
+        HooksManager::loadLibraries(libraries_);
+    }
+}
+
+// Method for testing
+void
+HooksLibrariesParser::getLibraries(std::vector<std::string>& libraries,
+                                   bool& changed) {
+    libraries = libraries_;
+    changed = changed_;
 }
 
 // **************************** OptionDataParser *************************
 OptionDataParser::OptionDataParser(const std::string&, OptionStoragePtr options,
                                   ParserContextPtr global_context)
-    : boolean_values_(new BooleanStorage()), 
-    string_values_(new StringStorage()), uint32_values_(new Uint32Storage()), 
-    options_(options), option_descriptor_(false), 
+    : boolean_values_(new BooleanStorage()),
+    string_values_(new StringStorage()), uint32_values_(new Uint32Storage()),
+    options_(options), option_descriptor_(false),
     global_context_(global_context) {
     if (!options_) {
         isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
@@ -179,22 +300,22 @@ OptionDataParser::OptionDataParser(const std::string&, OptionStoragePtr options,
     }
 }
 
-void 
+void
 OptionDataParser::build(ConstElementPtr option_data_entries) {
     BOOST_FOREACH(ConfigPair param, option_data_entries->mapValue()) {
         ParserPtr parser;
         if (param.first == "name" || param.first == "data" ||
             param.first == "space") {
-            StringParserPtr name_parser(new StringParser(param.first, 
-                                        string_values_)); 
+            StringParserPtr name_parser(new StringParser(param.first,
+                                        string_values_));
             parser = name_parser;
         } else if (param.first == "code") {
-            Uint32ParserPtr code_parser(new Uint32Parser(param.first, 
-                                       uint32_values_)); 
+            Uint32ParserPtr code_parser(new Uint32Parser(param.first,
+                                       uint32_values_));
             parser = code_parser;
         } else if (param.first == "csv-format") {
-            BooleanParserPtr value_parser(new BooleanParser(param.first, 
-                                         boolean_values_)); 
+            BooleanParserPtr value_parser(new BooleanParser(param.first,
+                                         boolean_values_));
             parser = value_parser;
         } else {
             isc_throw(DhcpConfigError,
@@ -216,12 +337,12 @@ OptionDataParser::build(ConstElementPtr option_data_entries) {
     createOption();
 }
 
-void 
+void
 OptionDataParser::commit() {
     if (!option_descriptor_.option) {
-        // Before we can commit the new option should be configured. If it is 
+        // Before we can commit the new option should be configured. If it is
         // not than somebody must have called commit() before build().
-        isc_throw(isc::InvalidOperation, 
+        isc_throw(isc::InvalidOperation,
             "parser logic error: no option has been configured and"
             " thus there is nothing to commit. Has build() been called?");
     }
@@ -245,7 +366,7 @@ OptionDataParser::commit() {
     options_->addItem(option_descriptor_, option_space_);
 }
 
-void 
+void
 OptionDataParser::createOption() {
     // Option code is held in the uint32_t storage but is supposed to
     // be uint16_t value. We need to check that value in the configuration
@@ -262,7 +383,7 @@ OptionDataParser::createOption() {
 
     // Check that the option name has been specified, is non-empty and does not
     // contain spaces
-    std::string option_name = string_values_->getParam("name"); 
+    std::string option_name = string_values_->getParam("name");
     if (option_name.empty()) {
         isc_throw(DhcpConfigError, "name of the option with code '"
                 << option_code << "' is empty");
@@ -271,7 +392,7 @@ OptionDataParser::createOption() {
                 << "', space character is not allowed");
     }
 
-    std::string option_space = string_values_->getParam("space"); 
+    std::string option_space = string_values_->getParam("space");
     if (!OptionSpace::validateName(option_space)) {
         isc_throw(DhcpConfigError, "invalid option space name '"
                 << option_space << "' specified for option '"
@@ -287,7 +408,7 @@ OptionDataParser::createOption() {
         // need to search for its definition among user-configured
         // options. They are expected to be in the global storage
         // already.
-        OptionDefContainerPtr defs = 
+        OptionDefContainerPtr defs =
             global_context_->option_defs_->getItems(option_space);
 
         // The getItems() should never return the NULL pointer. If there are
@@ -341,16 +462,16 @@ OptionDataParser::createOption() {
                       << " does not have a definition.");
         }
 
-        // @todo We have a limited set of option definitions intiialized at 
-        // the moment.  In the future we want to initialize option definitions 
-        // for all options.  Consequently an error will be issued if an option 
+        // @todo We have a limited set of option definitions intiialized at
+        // the moment.  In the future we want to initialize option definitions
+        // for all options.  Consequently an error will be issued if an option
         // definition does not exist for a particular option code. For now it is
         // ok to create generic option if definition does not exist.
-        OptionPtr option(new Option(global_context_->universe_, 
+        OptionPtr option(new Option(global_context_->universe_,
                         static_cast<uint16_t>(option_code), binary));
-        // The created option is stored in option_descriptor_ class member 
-        // until the commit stage when it is inserted into the main storage. 
-        // If an option with the same code exists in main storage already the 
+        // The created option is stored in option_descriptor_ class member
+        // until the commit stage when it is inserted into the main storage.
+        // If an option with the same code exists in main storage already the
         // old option is replaced.
         option_descriptor_.option = option;
         option_descriptor_.persistent = false;
@@ -372,9 +493,9 @@ OptionDataParser::createOption() {
         // an instance of our option.
         try {
             OptionPtr option = csv_format ?
-                def->optionFactory(global_context_->universe_, 
+                def->optionFactory(global_context_->universe_,
                                   option_code, data_tokens) :
-                def->optionFactory(global_context_->universe_, 
+                def->optionFactory(global_context_->universe_,
                                   option_code, binary);
             Subnet::OptionDescriptor desc(option, false);
             option_descriptor_.option = option;
@@ -392,10 +513,10 @@ OptionDataParser::createOption() {
 }
 
 // **************************** OptionDataListParser *************************
-OptionDataListParser::OptionDataListParser(const std::string&, 
+OptionDataListParser::OptionDataListParser(const std::string&,
     OptionStoragePtr options, ParserContextPtr global_context,
     OptionDataParserFactory* optionDataParserFactory)
-    : options_(options), local_options_(new OptionStorage()), 
+    : options_(options), local_options_(new OptionStorage()),
     global_context_(global_context),
     optionDataParserFactory_(optionDataParserFactory) {
     if (!options_) {
@@ -414,11 +535,11 @@ OptionDataListParser::OptionDataListParser(const std::string&,
     }
 }
 
-void 
+void
 OptionDataListParser::build(ConstElementPtr option_data_list) {
     BOOST_FOREACH(ConstElementPtr option_value, option_data_list->listValue()) {
-        boost::shared_ptr<OptionDataParser> 
-            parser((*optionDataParserFactory_)("option-data", 
+        boost::shared_ptr<OptionDataParser>
+            parser((*optionDataParserFactory_)("option-data",
                     local_options_, global_context_));
 
         // options_ member will hold instances of all options thus
@@ -430,7 +551,7 @@ OptionDataListParser::build(ConstElementPtr option_data_list) {
     }
 }
 
-void 
+void
 OptionDataListParser::commit() {
     BOOST_FOREACH(ParserPtr parser, parsers_) {
         parser->commit();
@@ -443,7 +564,7 @@ OptionDataListParser::commit() {
 }
 
 // ******************************** OptionDefParser ****************************
-OptionDefParser::OptionDefParser(const std::string&, 
+OptionDefParser::OptionDefParser(const std::string&,
                                 OptionDefStoragePtr storage)
     : storage_(storage), boolean_values_(new BooleanStorage()),
     string_values_(new StringStorage()), uint32_values_(new Uint32Storage()) {
@@ -453,23 +574,23 @@ OptionDefParser::OptionDefParser(const std::string&,
     }
 }
 
-void 
+void
 OptionDefParser::build(ConstElementPtr option_def) {
     // Parse the elements that make up the option definition.
      BOOST_FOREACH(ConfigPair param, option_def->mapValue()) {
         std::string entry(param.first);
         ParserPtr parser;
-        if (entry == "name" || entry == "type" || entry == "record-types" 
+        if (entry == "name" || entry == "type" || entry == "record-types"
             || entry == "space" || entry == "encapsulate") {
-            StringParserPtr str_parser(new StringParser(entry, 
+            StringParserPtr str_parser(new StringParser(entry,
                                        string_values_));
             parser = str_parser;
         } else if (entry == "code") {
-            Uint32ParserPtr code_parser(new Uint32Parser(entry, 
+            Uint32ParserPtr code_parser(new Uint32Parser(entry,
                                         uint32_values_));
             parser = code_parser;
         } else if (entry == "array") {
-            BooleanParserPtr array_parser(new BooleanParser(entry, 
+            BooleanParserPtr array_parser(new BooleanParser(entry,
                                          boolean_values_));
             parser = array_parser;
         } else {
@@ -501,7 +622,7 @@ OptionDefParser::build(ConstElementPtr option_def) {
     }
 }
 
-void 
+void
 OptionDefParser::commit() {
     if (storage_ && option_definition_ &&
         OptionSpace::validateName(option_space_name_)) {
@@ -509,7 +630,7 @@ OptionDefParser::commit() {
     }
 }
 
-void 
+void
 OptionDefParser::createOptionDef() {
     // Get the option space name and validate it.
     std::string space = string_values_->getParam("space");
@@ -589,7 +710,7 @@ OptionDefParser::createOptionDef() {
 }
 
 // ******************************** OptionDefListParser ************************
-OptionDefListParser::OptionDefListParser(const std::string&, 
+OptionDefListParser::OptionDefListParser(const std::string&,
     OptionDefStoragePtr storage) :storage_(storage) {
     if (!storage_) {
         isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
@@ -597,7 +718,7 @@ OptionDefListParser::OptionDefListParser(const std::string&,
     }
 }
 
-void 
+void
 OptionDefListParser::build(ConstElementPtr option_def_list) {
     // Clear existing items in the storage.
     // We are going to replace all of them.
@@ -616,7 +737,7 @@ OptionDefListParser::build(ConstElementPtr option_def_list) {
     }
 }
 
-void 
+void
 OptionDefListParser::commit() {
     CfgMgr& cfg_mgr = CfgMgr::instance();
     cfg_mgr.deleteOptionDefs();
@@ -648,7 +769,7 @@ PoolParser::PoolParser(const std::string&,  PoolStoragePtr pools)
     }
 }
 
-void 
+void
 PoolParser::build(ConstElementPtr pools_list) {
     BOOST_FOREACH(ConstElementPtr text_pool, pools_list->listValue()) {
         // That should be a single pool representation. It should contain
@@ -676,7 +797,7 @@ PoolParser::build(ConstElementPtr pools_list) {
                 // 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);
@@ -708,7 +829,7 @@ PoolParser::build(ConstElementPtr pools_list) {
         }
 }
 
-void 
+void
 PoolParser::commit() {
     if (pools_) {
         // local_pools_ holds the values produced by the build function.
@@ -720,9 +841,9 @@ PoolParser::commit() {
 
 //****************************** SubnetConfigParser *************************
 
-SubnetConfigParser::SubnetConfigParser(const std::string&, 
-                                       ParserContextPtr global_context) 
-    : uint32_values_(new Uint32Storage()), string_values_(new StringStorage()), 
+SubnetConfigParser::SubnetConfigParser(const std::string&,
+                                       ParserContextPtr global_context)
+    : uint32_values_(new Uint32Storage()), string_values_(new StringStorage()),
     pools_(new PoolStorage()), options_(new OptionStorage()),
     global_context_(global_context) {
     // The first parameter should always be "subnet", but we don't check
@@ -733,7 +854,7 @@ SubnetConfigParser::SubnetConfigParser(const std::string&,
     }
 }
 
-void 
+void
 SubnetConfigParser::build(ConstElementPtr subnet) {
     BOOST_FOREACH(ConfigPair param, subnet->mapValue()) {
         ParserPtr parser(createSubnetConfigParser(param.first));
@@ -757,8 +878,8 @@ SubnetConfigParser::build(ConstElementPtr subnet) {
     createSubnet();
 }
 
-void 
-SubnetConfigParser::appendSubOptions(const std::string& option_space, 
+void
+SubnetConfigParser::appendSubOptions(const std::string& option_space,
                                      OptionPtr& option) {
     // Only non-NULL options are stored in option container.
     // If this option pointer is NULL this is a serious error.
@@ -812,7 +933,7 @@ SubnetConfigParser::appendSubOptions(const std::string& option_space,
     }
 }
 
-void 
+void
 SubnetConfigParser::createSubnet() {
     std::string subnet_txt;
     try {
@@ -843,11 +964,11 @@ SubnetConfigParser::createSubnet() {
     isc::asiolink::IOAddress addr(subnet_txt.substr(0, pos));
     uint8_t len = boost::lexical_cast<unsigned int>(subnet_txt.substr(pos + 1));
 
-    // Call the subclass's method to instantiate the subnet 
+    // Call the subclass's method to instantiate the subnet
     initSubnet(addr, len);
 
     // Add pools to it.
-    for (PoolStorage::iterator it = pools_->begin(); it != pools_->end(); 
+    for (PoolStorage::iterator it = pools_->begin(); it != pools_->end();
          ++it) {
         subnet_->addPool(*it);
     }
@@ -922,7 +1043,7 @@ SubnetConfigParser::createSubnet() {
             // values we don't add option from the global storage
             // if there is one already.
             Subnet::OptionDescriptor existing_desc =
-                    subnet_->getOptionDescriptor(option_space, 
+                    subnet_->getOptionDescriptor(option_space,
                                                 desc.option->getType());
             if (!existing_desc.option) {
                 // Add sub-options (if any).
@@ -933,15 +1054,15 @@ SubnetConfigParser::createSubnet() {
     }
 }
 
-isc::dhcp::Triplet<uint32_t> 
+isc::dhcp::Triplet<uint32_t>
 SubnetConfigParser::getParam(const std::string& name) {
     uint32_t value = 0;
     try {
-        // look for local value 
+        // look for local value
         value = uint32_values_->getParam(name);
     } catch (const DhcpConfigError &) {
         try {
-            // no local, use global value 
+            // no local, use global value
             value = global_context_->uint32_values_->getParam(name);
         } catch (const DhcpConfigError &) {
             isc_throw(DhcpConfigError, "Mandatory parameter " << name
diff --git a/src/lib/dhcpsrv/dhcp_parsers.h b/src/lib/dhcpsrv/dhcp_parsers.h
index e453204..49c917c 100644
--- a/src/lib/dhcpsrv/dhcp_parsers.h
+++ b/src/lib/dhcpsrv/dhcp_parsers.h
@@ -23,6 +23,8 @@
 #include <dhcpsrv/subnet.h>
 #include <exceptions/exceptions.h>
 
+#include <boost/shared_ptr.hpp>
+
 #include <stdint.h>
 #include <string>
 #include <vector>
@@ -33,7 +35,6 @@ namespace dhcp {
 /// @brief Storage for option definitions.
 typedef OptionSpaceContainer<OptionDefContainer,
                              OptionDefinitionPtr> OptionDefStorage;
-
 /// @brief Shared pointer to option definitions storage.
 typedef boost::shared_ptr<OptionDefStorage> OptionDefStoragePtr;
 
@@ -44,11 +45,13 @@ typedef OptionSpaceContainer<Subnet::OptionContainer,
 /// @brief Shared pointer to option storage.
 typedef boost::shared_ptr<OptionStorage> OptionStoragePtr;
 
+
+
 /// @brief A template class that stores named elements of a given data type.
 ///
 /// This template class is provides data value storage for configuration parameters
-/// of a given data type.  The values are stored by parameter name and as instances 
-/// of type "ValueType". 
+/// of a given data type.  The values are stored by parameter name and as instances
+/// of type "ValueType".
 ///
 /// @param ValueType is the data type of the elements to store.
 template<typename ValueType>
@@ -57,7 +60,7 @@ class ValueStorage {
         /// @brief  Stores the the parameter and its value in the store.
         ///
         /// If the parameter does not exist in the store, then it will be added,
-        /// otherwise its data value will be updated with the given value. 
+        /// otherwise its data value will be updated with the given value.
         ///
         /// @param name is the name of the paramater to store.
         /// @param value is the data value to store.
@@ -71,10 +74,10 @@ class ValueStorage {
         /// @param name is the name of the parameter for which the data
         /// value is desired.
         ///
-        /// @return The paramater's data value of type <ValueType>.
+        /// @return The paramater's data value of type @c ValueType.
         /// @throw DhcpConfigError if the parameter is not found.
         ValueType getParam(const std::string& name) const {
-            typename std::map<std::string, ValueType>::const_iterator param 
+            typename std::map<std::string, ValueType>::const_iterator param
                 = values_.find(name);
 
             if (param == values_.end()) {
@@ -87,8 +90,8 @@ class ValueStorage {
 
         /// @brief  Remove the parameter from the store.
         ///
-        /// Deletes the entry for the given parameter from the store if it 
-        /// exists. 
+        /// Deletes the entry for the given parameter from the store if it
+        /// exists.
         ///
         /// @param name is the name of the paramater to delete.
         void delParam(const std::string& name) {
@@ -108,7 +111,7 @@ class ValueStorage {
 };
 
 
-/// @brief a collection of elements that store uint32 values 
+/// @brief a collection of elements that store uint32 values
 typedef ValueStorage<uint32_t> Uint32Storage;
 typedef boost::shared_ptr<Uint32Storage> Uint32StoragePtr;
 
@@ -128,9 +131,9 @@ typedef boost::shared_ptr<BooleanStorage> BooleanStoragePtr;
 class ParserContext {
 public:
     /// @brief Constructor
-    /// 
+    ///
     /// @param universe is the Option::Universe value of this
-    /// context. 
+    /// context.
     ParserContext(Option::Universe universe);
 
     /// @brief Copy constructor
@@ -151,6 +154,20 @@ public:
     /// @brief Storage for option definitions.
     OptionDefStoragePtr option_defs_;
 
+    /// @brief Hooks libraries pointer.
+    ///
+    /// The hooks libraries information is a vector of strings, each containing
+    /// the name of a library.  Hooks libraries should only be reloaded if the
+    /// list of names has changed, so the list of current DHCP parameters
+    /// (in isc::dhcp::CfgMgr) contains an indication as to whether the list has
+    /// altered.  This indication is implemented by storing a pointer to the
+    /// list of library names which is cleared when the libraries are loaded.
+    /// So either the pointer is null (meaning don't reload the libraries and
+    /// the list of current names can be obtained from the HooksManager) or it
+    /// is non-null (this is the new list of names, reload the libraries when
+    /// possible).
+    boost::shared_ptr<std::vector<std::string> > hooks_libraries_;
+
     /// @brief The parsing universe of this context.
     Option::Universe universe_;
 
@@ -161,12 +178,12 @@ public:
 /// @brief Pointer to various parser context.
 typedef boost::shared_ptr<ParserContext> ParserContextPtr;
 
-/// @brief Simple data-type parser template class 
+/// @brief Simple data-type parser template class
 ///
 /// This is the template class for simple data-type parsers. It supports
-/// parsing a configuration parameter with specific data-type for its 
-/// possible values. It provides a common constructor, commit, and templated 
-/// data storage.  The "build" method implementation must be provided by a 
+/// parsing a configuration parameter with specific data-type for its
+/// possible values. It provides a common constructor, commit, and templated
+/// data storage.  The "build" method implementation must be provided by a
 /// declaring type.
 /// @param ValueType is the data type of the configuration paramater value
 /// the parser should handle.
@@ -182,7 +199,7 @@ public:
     /// @throw isc::dhcp::DhcpConfigError if a provided parameter's
     /// name is empty.
     /// @throw isc::dhcp::DhcpConfigError if storage is null.
-    ValueParser(const std::string& param_name, 
+    ValueParser(const std::string& param_name,
         boost::shared_ptr<ValueStorage<ValueType> > storage)
         : storage_(storage), param_name_(param_name), value_() {
         // Empty parameter name is invalid.
@@ -199,12 +216,12 @@ public:
     }
 
 
-    /// @brief Parse a given element into a value of type <ValueType>
+    /// @brief Parse a given element into a value of type @c ValueType
     ///
     /// @param value a value to be parsed.
     ///
     /// @throw isc::BadValue Typically the implementing type will throw
-    /// a BadValue exception when given an invalid Element to parse. 
+    /// a BadValue exception when given an invalid Element to parse.
     void build(isc::data::ConstElementPtr value);
 
     /// @brief Put a parsed value to the storage.
@@ -213,7 +230,7 @@ public:
         // its value. If it doesn't we insert a new element.
         storage_->setParam(param_name_, value_);
     }
-    
+ 
 private:
     /// Pointer to the storage where committed value is stored.
     boost::shared_ptr<ValueStorage<ValueType> > storage_;
@@ -302,10 +319,95 @@ public:
     virtual void commit();
 
 private:
+    /// @brief Check that specified interface exists in
+    /// @c InterfaceListConfigParser::interfaces_.
+    ///
+    /// @param iface A name of the interface.
+    ///
+    /// @return true if specified interface name was found.
+    bool isIfaceAdded(const std::string& iface) const;
+
     /// contains list of network interfaces
-    std::vector<std::string> interfaces_;
+    typedef std::list<std::string> IfaceListStorage;
+    IfaceListStorage interfaces_;
+
+    // Should server listen on all interfaces.
+    bool activate_all_;
+
+    // Parsed parameter name
+    std::string param_name_;
 };
 
+/// @brief parser for hooks library list
+///
+/// This parser handles the list of hooks libraries.  This is an optional list,
+/// which may be empty.
+///
+/// However, the parser does more than just check the list of library names.
+/// It does two other things:
+///
+/// -# The problem faced with the hooks libraries is that we wish to avoid
+/// reloading the libraries if they have not changed.  (This would cause the
+/// "unload" and "load" methods to run.  Although libraries should be written
+/// to cope with this, it is feasible that such an action may be constly in
+/// terms of time and resources, or may cause side effects such as clearning
+/// an internal cache.)  To this end, the parser also checks the list against
+/// the list of libraries current loaded and notes if there are changes.
+/// -# If there are, the parser validates the libraries; it opens them and
+/// checks that the "version" function exists and returns the correct value.
+///
+/// Only if the library list has changed and the libraries are valid will the
+/// change be applied.
+class HooksLibrariesParser : public DhcpConfigParser {
+public:
+
+    /// @brief Constructor
+    ///
+    /// As this is a dedicated parser, it must be used to parse
+    /// "hooks_libraries" 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 "hooks_libraries"
+    HooksLibrariesParser(const std::string& param_name);
+
+    /// @brief Parses parameters value
+    ///
+    /// Parses configuration entry (list of parameters) and adds each element
+    /// to the hooks libraries list.  The  method also checks whether the
+    /// list of libraries is the same as that already loaded.  If not, it
+    /// checks each of the libraries in the list for validity (they exist and
+    /// have a "version" function that returns the correct value).
+    ///
+    /// @param value pointer to the content of parsed values
+    virtual void build(isc::data::ConstElementPtr value);
+
+    /// @brief Commits hooks libraries data
+    ///
+    /// Providing that the specified libraries are valid and are different
+    /// to those already loaded, this method loads the new set of libraries
+    /// (and unloads the existing set).
+    virtual void commit();
+
+    /// @brief Returns list of parsed libraries
+    ///
+    /// Principally for testing, this returns the list of libraries as well as
+    /// an indication as to whether the list is different from the list of
+    /// libraries already loaded.
+    ///
+    /// @param libraries (out) List of libraries that were specified in the
+    ///        new configuration.
+    /// @param changed (out) true if the list is different from that currently
+    ///        loaded.
+    void getLibraries(std::vector<std::string>& libraries, bool& changed);
+
+private:
+    /// List of hooks libraries.
+    std::vector<std::string> libraries_;
+
+    /// Indicator flagging that the list of libraries has changed.
+    bool changed_;
+};
 
 /// @brief Parser for option data value.
 ///
@@ -332,11 +434,11 @@ public:
     /// @param dummy first argument is ignored, all Parser constructors
     /// accept string as first argument.
     /// @param options is the option storage in which to store the parsed option
-    /// upon "commit". 
-    /// @param global_context is a pointer to the global context which 
-    /// stores global scope parameters, options, option defintions. 
+    /// upon "commit".
+    /// @param global_context is a pointer to the global context which
+    /// stores global scope parameters, options, option defintions.
     /// @throw isc::dhcp::DhcpConfigError if options or global_context are null.
-    OptionDataParser(const std::string&, OptionStoragePtr options, 
+    OptionDataParser(const std::string& dummy, OptionStoragePtr options,
                     ParserContextPtr global_context);
 
     /// @brief Parses the single option data.
@@ -356,31 +458,31 @@ public:
 
     /// @brief Commits option value.
     ///
-    /// This function adds a new option to the storage or replaces an existing 
+    /// This function adds a new option to the storage or replaces an existing
     /// option with the same code.
     ///
-    /// @throw isc::InvalidOperation if failed to set pointer to storage or 
+    /// @throw isc::InvalidOperation if failed to set pointer to storage or
     /// failed
     /// to call build() prior to commit. If that happens data in the storage
     /// remain un-modified.
     virtual void commit();
 
-    /// @brief virtual destructor to ensure orderly destruction of derivations. 
+    /// @brief virtual destructor to ensure orderly destruction of derivations.
     virtual ~OptionDataParser(){};
 
 protected:
     /// @brief Finds an option definition within the server's option space
-    /// 
-    /// Given an option space and an option code, find the correpsonding 
+    ///
+    /// Given an option space and an option code, find the correpsonding
     /// option defintion within the server's option defintion storage. This
     /// method is pure virtual requiring derivations to manage which option
     /// space(s) is valid for search.
     ///
-    /// @param option_space name of the parameter option space 
-    /// @param option_code numeric value of the parameter to find 
-    /// @return OptionDefintionPtr of the option defintion or an 
+    /// @param option_space name of the parameter option space
+    /// @param option_code numeric value of the parameter to find
+    /// @return OptionDefintionPtr of the option defintion or an
     /// empty OptionDefinitionPtr if not found.
-    /// @throw DhcpConfigError if the option space requested is not valid 
+    /// @throw DhcpConfigError if the option space requested is not valid
     /// for this server.
     virtual OptionDefinitionPtr findServerSpaceOptionDefinition (
             std::string& option_space, uint32_t option_code) = 0;
@@ -420,13 +522,13 @@ private:
     /// Option space name where the option belongs to.
     std::string option_space_;
 
-    /// Parsing context which contains global values, options and option 
+    /// Parsing context which contains global values, options and option
     /// definitions.
     ParserContextPtr global_context_;
 };
 
 ///@brief Function pointer for OptionDataParser factory methods
-typedef OptionDataParser *OptionDataParserFactory(const std::string&, 
+typedef OptionDataParser *OptionDataParserFactory(const std::string&,
                      OptionStoragePtr options, ParserContextPtr global_context);
 
 /// @brief Parser for option data values within a subnet.
@@ -439,15 +541,15 @@ class OptionDataListParser : public DhcpConfigParser {
 public:
     /// @brief Constructor.
     ///
-    /// @param string& nominally would be param name, this is always ignored.
+    /// @param dummy nominally would be param name, this is always ignored.
     /// @param options parsed option storage for options in this list
-    /// @param global_context is a pointer to the global context which 
-    /// stores global scope parameters, options, option defintions. 
-    /// @param optionDataParserFactory factory method for creating individual 
-    /// option parsers 
+    /// @param global_context is a pointer to the global context which
+    /// stores global scope parameters, options, option defintions.
+    /// @param optionDataParserFactory factory method for creating individual
+    /// option parsers
     /// @throw isc::dhcp::DhcpConfigError if options or global_context are null.
-    OptionDataListParser(const std::string&, OptionStoragePtr options, 
-                        ParserContextPtr global_context, 
+    OptionDataListParser(const std::string& dummy, OptionStoragePtr options,
+                        ParserContextPtr global_context,
                         OptionDataParserFactory *optionDataParserFactory);
 
     /// @brief Parses entries that define options' data for a subnet.
@@ -477,7 +579,7 @@ private:
     /// Collection of parsers;
     ParserCollection parsers_;
 
-    /// Parsing context which contains global values, options and option 
+    /// Parsing context which contains global values, options and option
     /// definitions.
     ParserContextPtr global_context_;
 
@@ -495,10 +597,10 @@ public:
     ///
     /// @param dummy first argument is ignored, all Parser constructors
     /// accept string as first argument.
-    /// @param storage is the definition storage in which to store the parsed 
-    /// definition upon "commit". 
+    /// @param storage is the definition storage in which to store the parsed
+    /// definition upon "commit".
     /// @throw isc::dhcp::DhcpConfigError if storage is null.
-    OptionDefParser(const std::string&, OptionDefStoragePtr storage);
+    OptionDefParser(const std::string& dummy, OptionDefStoragePtr storage);
 
     /// @brief Parses an entry that describes single option definition.
     ///
@@ -546,10 +648,10 @@ public:
     ///
     /// @param dummy first argument is ignored, all Parser constructors
     /// accept string as first argument.
-    /// @param storage is the definition storage in which to store the parsed 
-    /// definitions in this list 
+    /// @param storage is the definition storage in which to store the parsed
+    /// definitions in this list
     /// @throw isc::dhcp::DhcpConfigError if storage is null.
-    OptionDefListParser(const std::string&, OptionDefStoragePtr storage);
+    OptionDefListParser(const std::string& dummy, OptionDefStoragePtr storage);
 
     /// @brief Parse configuration entries.
     ///
@@ -566,7 +668,7 @@ public:
 
 private:
     /// @brief storage for option definitions.
-    OptionDefStoragePtr storage_; 
+    OptionDefStoragePtr storage_;
 };
 
 /// @brief a collection of pools
@@ -587,14 +689,13 @@ class PoolParser : public DhcpConfigParser {
 public:
 
     /// @brief constructor.
-   
-
+    ///
     /// @param dummy first argument is ignored, all Parser constructors
     /// accept string as first argument.
-    /// @param pools is the storage in which to store the parsed pool 
-    /// upon "commit". 
+    /// @param pools is the storage in which to store the parsed pool
+    /// upon "commit".
     /// @throw isc::dhcp::DhcpConfigError if storage is null.
-    PoolParser(const std::string&,  PoolStoragePtr pools);
+    PoolParser(const std::string& dummy, PoolStoragePtr pools);
 
     /// @brief parses the actual list
     ///
@@ -614,9 +715,9 @@ protected:
     ///
     /// @param addr is the IP  prefix of the pool.
     /// @param len is the prefix length.
-    /// @param ignored dummy parameter to provide symmetry between 
-    /// @return returns a PoolPtr to the new Pool object. 
-    virtual PoolPtr poolMaker(isc::asiolink::IOAddress &addr, uint32_t len, 
+    /// @param ptype is the type of pool to create.
+    /// @return returns a PoolPtr to the new Pool object.
+    virtual PoolPtr poolMaker(isc::asiolink::IOAddress &addr, uint32_t len,
                            int32_t ptype=0) = 0;
 
     /// @brief Creates a Pool object given starting and ending IP addresses.
@@ -625,7 +726,7 @@ protected:
     /// @param max is the last IP address in the pool.
     /// @param ptype is the type of pool to create (not used by all derivations)
     /// @return returns a PoolPtr to the new Pool object.
-    virtual PoolPtr poolMaker(isc::asiolink::IOAddress &min, 
+    virtual PoolPtr poolMaker(isc::asiolink::IOAddress &min,
                            isc::asiolink::IOAddress &max, int32_t ptype=0) = 0;
 
     /// @brief pointer to the actual Pools storage
@@ -654,7 +755,7 @@ public:
     /// @param subnet pointer to the content of subnet definition
     ///
     /// @throw isc::DhcpConfigError if subnet configuration parsing failed.
-    virtual void build(isc::data::ConstElementPtr subnet); 
+    virtual void build(isc::data::ConstElementPtr subnet);
 
     /// @brief Adds the created subnet to a server's configuration.
     virtual void commit() = 0;
@@ -671,7 +772,7 @@ protected:
                                             const std::string& config_id) = 0;
 
     /// @brief Determines if the given option space name and code describe
-    /// a standard option for the  server. 
+    /// a standard option for the  server.
     ///
     /// @param option_space is the name of the option space to consider
     /// @param code is the numeric option code to consider
@@ -687,20 +788,20 @@ protected:
                                                              uint32_t code) = 0;
 
     /// @brief Issues a server specific warning regarding duplicate subnet
-    /// options. 
-    /// 
+    /// options.
+    ///
     /// @param code is the numeric option code of the duplicate option
-    /// @param addr is the subnet address 
+    /// @param addr is the subnet address
     /// @todo a means to know the correct logger and perhaps a common
     /// message would allow this method to be emitted by the base class.
-    virtual void duplicate_option_warning(uint32_t code, 
+    virtual void duplicate_option_warning(uint32_t code,
         isc::asiolink::IOAddress& addr) = 0;
 
-    /// @brief Instantiates the subnet based on a given IP prefix and prefix 
-    /// length.  
-    /// 
+    /// @brief Instantiates the subnet based on a given IP prefix and prefix
+    /// length.
+    ///
     /// @param addr is the IP prefix of the subnet.
-    /// @param len is the prefix length 
+    /// @param len is the prefix length
     virtual void initSubnet(isc::asiolink::IOAddress addr, uint8_t len) = 0;
 
     /// @brief Returns value for a given parameter (after using inheritance)
@@ -724,7 +825,7 @@ private:
 
     /// @brief Create a new subnet using a data from child parsers.
     ///
-    /// @throw isc::dhcp::DhcpConfigError if subnet configuration parsing 
+    /// @throw isc::dhcp::DhcpConfigError if subnet configuration parsing
     /// failed.
     void createSubnet();
 
@@ -748,7 +849,7 @@ protected:
     /// Pointer to the created subnet object.
     isc::dhcp::SubnetPtr subnet_;
 
-    /// Parsing context which contains global values, options and option 
+    /// Parsing context which contains global values, options and option
     /// definitions.
     ParserContextPtr global_context_;
 };
diff --git a/src/lib/dhcpsrv/dhcpsrv_log.h b/src/lib/dhcpsrv/dhcpsrv_log.h
index 9b6350a..fe997ff 100644
--- a/src/lib/dhcpsrv/dhcpsrv_log.h
+++ b/src/lib/dhcpsrv/dhcpsrv_log.h
@@ -50,6 +50,9 @@ const int DHCPSRV_DBG_TRACE_DETAIL = DBGLVL_TRACE_DETAIL;
 /// Record detailed (and verbose) data on the server.
 const int DHCPSRV_DBG_TRACE_DETAIL_DATA = DBGLVL_TRACE_DETAIL_DATA;
 
+// Trace hook related operations
+const int DHCPSRV_DBG_HOOKS = DBGLVL_TRACE_BASIC;
+
 ///@}
 
 
diff --git a/src/lib/dhcpsrv/dhcpsrv_messages.mes b/src/lib/dhcpsrv/dhcpsrv_messages.mes
index b2a7807..3e47a35 100644
--- a/src/lib/dhcpsrv/dhcpsrv_messages.mes
+++ b/src/lib/dhcpsrv/dhcpsrv_messages.mes
@@ -54,6 +54,10 @@ consider reducing the lease lifetime.  In this way, addresses allocated
 to clients that are no longer active on the network will become available
 available sooner.
 
+% DHCPSRV_CFGMGR_ADD_IFACE adding listening interface %1
+A debug message issued when new interface is being added to the collection of
+interfaces on which server listens to DHCP messages.
+
 % DHCPSRV_CFGMGR_ADD_SUBNET4 adding subnet %1
 A debug message reported when the DHCP configuration manager is adding the
 specified IPv4 subnet to its database.
@@ -62,6 +66,16 @@ specified IPv4 subnet to its database.
 A debug message reported when the DHCP configuration manager is adding the
 specified IPv6 subnet to its database.
 
+% DHCPSRV_CFGMGR_ALL_IFACES_ACTIVE enabling listening on all interfaces
+A debug message issued when server is being configured to listen on all
+interfaces.
+
+% DHCPSRV_CFGMGR_CLEAR_ACTIVE_IFACES stop listening on all interfaces
+A debug message issued when configuration manager clears the internal list
+of active interfaces. This doesn't prevent the server from listening to
+the DHCP traffic through open sockets, but will rather be used by Interface
+Manager to select active interfaces when sockets are re-opened.
+
 % DHCPSRV_CFGMGR_DELETE_SUBNET4 deleting all IPv4 subnets
 A debug message noting that the DHCP configuration manager has deleted all IPv4
 subnets in its database.
@@ -121,6 +135,18 @@ the database access parameters are changed: in the latter case, the
 server closes the currently open database, and opens a database using
 the new parameters.
 
+% DHCPSRV_HOOK_LEASE4_SELECT_SKIP Lease4 creation was skipped, because of callout skip flag.
+This debug message is printed when a callout installed on lease4_select
+hook point sets the skip flag. It means that the server was told that
+no lease4 should be assigned. The server will not put that lease in its
+database and the client will get a NAK packet.
+
+% DHCPSRV_HOOK_LEASE6_SELECT_SKIP Lease6 (non-temporary) creation was skipped, because of callout skip flag.
+This debug message is printed when a callout installed on lease6_select
+hook point sets the skip flag. It means that the server was told that
+no lease6 should be assigned. The server will not put that lease in its
+database and the client will get a NoAddrsAvail for that IA_NA option.
+
 % DHCPSRV_INVALID_ACCESS invalid database access string: %1
 This is logged when an attempt has been made to parse a database access string
 and the attempt ended in error.  The access string in question - which
diff --git a/src/lib/dhcpsrv/tests/Makefile.am b/src/lib/dhcpsrv/tests/Makefile.am
index 9a81ef6..ba1e39b 100644
--- a/src/lib/dhcpsrv/tests/Makefile.am
+++ b/src/lib/dhcpsrv/tests/Makefile.am
@@ -67,8 +67,9 @@ libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
 libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
 libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
 libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
-libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
+libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/hooks/libb10-hooks.la
 libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
+libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
 libdhcpsrv_unittests_LDADD += $(GTEST_LDADD)
 endif
 
diff --git a/src/lib/dhcpsrv/tests/alloc_engine_unittest.cc b/src/lib/dhcpsrv/tests/alloc_engine_unittest.cc
index 05f3741..2f0894d 100644
--- a/src/lib/dhcpsrv/tests/alloc_engine_unittest.cc
+++ b/src/lib/dhcpsrv/tests/alloc_engine_unittest.cc
@@ -25,18 +25,24 @@
 
 #include <dhcpsrv/tests/test_utils.h>
 
+#include <hooks/server_hooks.h>
+#include <hooks/callout_manager.h>
+#include <hooks/hooks_manager.h>
+
 #include <boost/shared_ptr.hpp>
 #include <boost/scoped_ptr.hpp>
 #include <gtest/gtest.h>
 
 #include <iostream>
 #include <sstream>
+#include <algorithm>
 #include <set>
 #include <time.h>
 
 using namespace std;
 using namespace isc;
 using namespace isc::asiolink;
+using namespace isc::hooks;
 using namespace isc::dhcp;
 using namespace isc::dhcp::test;
 
@@ -105,7 +111,7 @@ public:
         // @todo: check cltt
      }
 
-    ~AllocEngine6Test() {
+    virtual ~AllocEngine6Test() {
         factory_.destroy();
     }
 
@@ -173,7 +179,7 @@ public:
         // @todo: check cltt
      }
 
-    ~AllocEngine4Test() {
+    virtual ~AllocEngine4Test() {
         factory_.destroy();
     }
 
@@ -203,7 +209,7 @@ TEST_F(AllocEngine6Test, simpleAlloc6) {
     ASSERT_TRUE(engine);
 
     Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_, IOAddress("::"),
-                                               false);
+                                               false, CalloutHandlePtr());
 
     // Check that we got a lease
     ASSERT_TRUE(lease);
@@ -226,7 +232,7 @@ TEST_F(AllocEngine6Test, fakeAlloc6) {
     ASSERT_TRUE(engine);
 
     Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_, IOAddress("::"),
-                                               true);
+                                               true, CalloutHandlePtr());
 
     // Check that we got a lease
     ASSERT_TRUE(lease);
@@ -248,7 +254,7 @@ TEST_F(AllocEngine6Test, allocWithValidHint6) {
 
     Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_,
                                                IOAddress("2001:db8:1::15"),
-                                               false);
+                                               false, CalloutHandlePtr());
 
     // Check that we got a lease
     ASSERT_TRUE(lease);
@@ -286,7 +292,7 @@ TEST_F(AllocEngine6Test, allocWithUsedHint6) {
     // twice.
     Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_,
                                                IOAddress("2001:db8:1::1f"),
-                                               false);
+                                               false, CalloutHandlePtr());
     // Check that we got a lease
     ASSERT_TRUE(lease);
 
@@ -319,7 +325,7 @@ TEST_F(AllocEngine6Test, allocBogusHint6) {
     // with the normal allocation
     Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_,
                                                IOAddress("3000::abc"),
-                                               false);
+                                               false, CalloutHandlePtr());
     // Check that we got a lease
     ASSERT_TRUE(lease);
 
@@ -345,12 +351,12 @@ TEST_F(AllocEngine6Test, allocateAddress6Nulls) {
 
     // Allocations without subnet are not allowed
     Lease6Ptr lease = engine->allocateAddress6(Subnet6Ptr(), duid_, iaid_,
-                                               IOAddress("::"), false);
+                                               IOAddress("::"), false, CalloutHandlePtr());
     ASSERT_FALSE(lease);
 
     // Allocations without DUID are not allowed either
     lease = engine->allocateAddress6(subnet_, DuidPtr(), iaid_,
-                                     IOAddress("::"), false);
+                                     IOAddress("::"), false, CalloutHandlePtr());
     ASSERT_FALSE(lease);
 }
 
@@ -439,7 +445,7 @@ TEST_F(AllocEngine6Test, smallPool6) {
     cfg_mgr.addSubnet6(subnet_);
 
     Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_, IOAddress("::"),
-                                               false);
+                                               false, CalloutHandlePtr());
 
     // Check that we got that single lease
     ASSERT_TRUE(lease);
@@ -485,7 +491,7 @@ TEST_F(AllocEngine6Test, outOfAddresses6) {
     // There is just a single address in the pool and allocated it to someone
     // else, so the allocation should fail
     Lease6Ptr lease2 = engine->allocateAddress6(subnet_, duid_, iaid_,
-                                                IOAddress("::"), false);
+                                                IOAddress("::"), false, CalloutHandlePtr());
     EXPECT_FALSE(lease2);
 }
 
@@ -519,7 +525,7 @@ TEST_F(AllocEngine6Test, solicitReuseExpiredLease6) {
 
     // CASE 1: Asking for any address
     lease = engine->allocateAddress6(subnet_, duid_, iaid_, IOAddress("::"),
-                                     true);
+                                     true, CalloutHandlePtr());
     // Check that we got that single lease
     ASSERT_TRUE(lease);
     EXPECT_EQ(addr.toText(), lease->addr_.toText());
@@ -529,7 +535,7 @@ TEST_F(AllocEngine6Test, solicitReuseExpiredLease6) {
 
     // CASE 2: Asking specifically for this address
     lease = engine->allocateAddress6(subnet_, duid_, iaid_, IOAddress(addr.toText()),
-                                     true);
+                                     true, CalloutHandlePtr());
     // Check that we got that single lease
     ASSERT_TRUE(lease);
     EXPECT_EQ(addr.toText(), lease->addr_.toText());
@@ -563,7 +569,8 @@ TEST_F(AllocEngine6Test, requestReuseExpiredLease6) {
 
     // A client comes along, asking specifically for this address
     lease = engine->allocateAddress6(subnet_, duid_, iaid_,
-                                     IOAddress(addr.toText()), false);
+                                     IOAddress(addr.toText()), false,
+                                     CalloutHandlePtr());
 
     // Check that he got that single lease
     ASSERT_TRUE(lease);
@@ -586,7 +593,8 @@ TEST_F(AllocEngine4Test, simpleAlloc4) {
     ASSERT_TRUE(engine);
 
     Lease4Ptr lease = engine->allocateAddress4(subnet_, clientid_, hwaddr_,
-                                               IOAddress("0.0.0.0"), false);
+                                               IOAddress("0.0.0.0"), false,
+                                               CalloutHandlePtr());
 
     // Check that we got a lease
     ASSERT_TRUE(lease);
@@ -609,7 +617,8 @@ TEST_F(AllocEngine4Test, fakeAlloc4) {
     ASSERT_TRUE(engine);
 
     Lease4Ptr lease = engine->allocateAddress4(subnet_, clientid_, hwaddr_,
-                                               IOAddress("0.0.0.0"), true);
+                                               IOAddress("0.0.0.0"), true,
+                                               CalloutHandlePtr());
 
     // Check that we got a lease
     ASSERT_TRUE(lease);
@@ -632,7 +641,7 @@ TEST_F(AllocEngine4Test, allocWithValidHint4) {
 
     Lease4Ptr lease = engine->allocateAddress4(subnet_, clientid_, hwaddr_,
                                                IOAddress("192.0.2.105"),
-                                               false);
+                                               false, CalloutHandlePtr());
 
     // Check that we got a lease
     ASSERT_TRUE(lease);
@@ -672,7 +681,7 @@ TEST_F(AllocEngine4Test, allocWithUsedHint4) {
     // twice.
     Lease4Ptr lease = engine->allocateAddress4(subnet_, clientid_, hwaddr_,
                                                IOAddress("192.0.2.106"),
-                                               false);
+                                               false, CalloutHandlePtr());
     // Check that we got a lease
     ASSERT_TRUE(lease);
 
@@ -706,7 +715,7 @@ TEST_F(AllocEngine4Test, allocBogusHint4) {
     // with the normal allocation
     Lease4Ptr lease = engine->allocateAddress4(subnet_, clientid_, hwaddr_,
                                                IOAddress("10.1.1.1"),
-                                               false);
+                                               false, CalloutHandlePtr());
     // Check that we got a lease
     ASSERT_TRUE(lease);
 
@@ -733,18 +742,19 @@ TEST_F(AllocEngine4Test, allocateAddress4Nulls) {
 
     // Allocations without subnet are not allowed
     Lease4Ptr lease = engine->allocateAddress4(SubnetPtr(), clientid_, hwaddr_,
-                                               IOAddress("0.0.0.0"), false);
+                                               IOAddress("0.0.0.0"), false,
+                                               CalloutHandlePtr());
     EXPECT_FALSE(lease);
 
     // Allocations without HW address are not allowed
     lease = engine->allocateAddress4(subnet_, clientid_, HWAddrPtr(),
-                                     IOAddress("0.0.0.0"), false);
+                                     IOAddress("0.0.0.0"), false, CalloutHandlePtr());
     EXPECT_FALSE(lease);
 
     // Allocations without client-id are allowed
     clientid_ = ClientIdPtr();
     lease = engine->allocateAddress4(subnet_, ClientIdPtr(), hwaddr_,
-                                     IOAddress("0.0.0.0"), false);
+                                     IOAddress("0.0.0.0"), false, CalloutHandlePtr());
     // Check that we got a lease
     ASSERT_TRUE(lease);
 
@@ -848,7 +858,7 @@ TEST_F(AllocEngine4Test, smallPool4) {
     cfg_mgr.addSubnet4(subnet_);
 
     Lease4Ptr lease = engine->allocateAddress4(subnet_, clientid_, hwaddr_, IOAddress("0.0.0.0"),
-                                               false);
+                                               false, CalloutHandlePtr());
 
     // Check that we got that single lease
     ASSERT_TRUE(lease);
@@ -896,7 +906,8 @@ TEST_F(AllocEngine4Test, outOfAddresses4) {
     // else, so the allocation should fail
 
     Lease4Ptr lease2 = engine->allocateAddress4(subnet_, clientid_, hwaddr_,
-                                                IOAddress("0.0.0.0"), false);
+                                                IOAddress("0.0.0.0"), false,
+                                                CalloutHandlePtr());
     EXPECT_FALSE(lease2);
 }
 
@@ -929,7 +940,7 @@ TEST_F(AllocEngine4Test, discoverReuseExpiredLease4) {
 
     // CASE 1: Asking for any address
     lease = engine->allocateAddress4(subnet_, clientid_, hwaddr_, IOAddress("0.0.0.0"),
-                                     true);
+                                     true, CalloutHandlePtr());
     // Check that we got that single lease
     ASSERT_TRUE(lease);
     EXPECT_EQ(addr.toText(), lease->addr_.toText());
@@ -939,7 +950,7 @@ TEST_F(AllocEngine4Test, discoverReuseExpiredLease4) {
 
     // CASE 2: Asking specifically for this address
     lease = engine->allocateAddress4(subnet_, clientid_, hwaddr_, IOAddress(addr.toText()),
-                                     true);
+                                     true, CalloutHandlePtr());
     // Check that we got that single lease
     ASSERT_TRUE(lease);
     EXPECT_EQ(addr.toText(), lease->addr_.toText());
@@ -966,7 +977,8 @@ TEST_F(AllocEngine4Test, requestReuseExpiredLease4) {
 
     // A client comes along, asking specifically for this address
     lease = engine->allocateAddress4(subnet_, clientid_, hwaddr_,
-                                     IOAddress(addr.toText()), false);
+                                     IOAddress(addr.toText()), false,
+                                     CalloutHandlePtr());
 
     // Check that he got that single lease
     ASSERT_TRUE(lease);
@@ -980,6 +992,8 @@ TEST_F(AllocEngine4Test, requestReuseExpiredLease4) {
     detailCompareLease(lease, from_mgr);
 }
 
+/// @todo write renewLease6
+
 // This test checks if a lease is really renewed when renewLease4 method is
 // called
 TEST_F(AllocEngine4Test, renewLease4) {
@@ -1020,4 +1034,434 @@ TEST_F(AllocEngine4Test, renewLease4) {
     detailCompareLease(lease, from_mgr);
 }
 
+/// @brief helper class used in Hooks testing in AllocEngine6
+///
+/// It features a couple of callout functions and buffers to store
+/// the data that is accessible via callouts.
+class HookAllocEngine6Test : public AllocEngine6Test {
+public:
+    HookAllocEngine6Test() {
+        resetCalloutBuffers();
+    }
+
+    virtual ~HookAllocEngine6Test() {
+        HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts(
+            "lease6_select");
+    }
+
+    /// @brief clears out buffers, so callouts can store received arguments
+    void resetCalloutBuffers() {
+        callback_name_ = string("");
+        callback_subnet6_.reset();
+        callback_fake_allocation_ = false;
+        callback_lease6_.reset();
+        callback_argument_names_.clear();
+        callback_addr_original_ = IOAddress("::");
+        callback_addr_updated_ = IOAddress("::");
+    }
+
+    /// callback that stores received callout name and received values
+    static int
+    lease6_select_callout(CalloutHandle& callout_handle) {
+
+        callback_name_ = string("lease6_select");
+
+        callout_handle.getArgument("subnet6", callback_subnet6_);
+        callout_handle.getArgument("fake_allocation", callback_fake_allocation_);
+        callout_handle.getArgument("lease6", callback_lease6_);
+
+        callback_addr_original_ = callback_lease6_->addr_;
+
+        callback_argument_names_ = callout_handle.getArgumentNames();
+        return (0);
+    }
+
+    /// callback that overrides the lease with different values
+    static int
+    lease6_select_different_callout(CalloutHandle& callout_handle) {
+
+        // Let's call the basic callout, so it can record all parameters
+        lease6_select_callout(callout_handle);
+
+        // Now we need to tweak the least a bit
+        Lease6Ptr lease;
+        callout_handle.getArgument("lease6", lease);
+        callback_addr_updated_ = addr_override_;
+        lease->addr_ = callback_addr_updated_;
+        lease->t1_ = t1_override_;
+        lease->t2_ = t2_override_;
+        lease->preferred_lft_ = pref_override_;
+        lease->valid_lft_ = valid_override_;
+
+        return (0);
+    }
+
+    // Values to be used in callout to override lease6 content
+    static const IOAddress addr_override_;
+    static const uint32_t t1_override_;
+    static const uint32_t t2_override_;
+    static const uint32_t pref_override_;
+    static const uint32_t valid_override_;
+
+    // Callback will store original and overridden values here
+    static IOAddress callback_addr_original_;
+    static IOAddress callback_addr_updated_;
+
+    // Buffers (callback will store received values here)
+    static string callback_name_;
+    static Subnet6Ptr callback_subnet6_;
+    static Lease6Ptr callback_lease6_;
+    static bool callback_fake_allocation_;
+    static vector<string> callback_argument_names_;
+};
+
+// For some reason intialization within a class makes the linker confused.
+// linker complains about undefined references if they are defined within
+// the class declaration.
+const IOAddress HookAllocEngine6Test::addr_override_("2001:db8::abcd");
+const uint32_t HookAllocEngine6Test::t1_override_ = 6000;
+const uint32_t HookAllocEngine6Test::t2_override_ = 7000;
+const uint32_t HookAllocEngine6Test::pref_override_ = 8000;
+const uint32_t HookAllocEngine6Test::valid_override_ = 9000;
+
+IOAddress HookAllocEngine6Test::callback_addr_original_("::");
+IOAddress HookAllocEngine6Test::callback_addr_updated_("::");
+
+string HookAllocEngine6Test::callback_name_;
+Subnet6Ptr HookAllocEngine6Test::callback_subnet6_;
+Lease6Ptr HookAllocEngine6Test::callback_lease6_;
+bool HookAllocEngine6Test::callback_fake_allocation_;
+vector<string> HookAllocEngine6Test::callback_argument_names_;
+
+// This test checks if the lease6_select callout is executed and expected
+// parameters as passed.
+TEST_F(HookAllocEngine6Test, lease6_select) {
+
+    // Note: The following order is working as expected:
+    // 1. create AllocEngine (that register hook points)
+    // 2. call loadLibraries()
+    //
+    // This order, however, causes segfault in HooksManager
+    // 1. call loadLibraries()
+    // 2. create AllocEngine (that register hook points)
+
+    // Create allocation engine (hook names are registered in its ctor)
+    boost::scoped_ptr<AllocEngine> engine;
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_TRUE(engine);
+
+    // Initialize Hooks Manager
+    vector<string> libraries; // no libraries at this time
+    HooksManager::loadLibraries(libraries);
+
+    // Install pkt6_receive_callout
+    EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                        "lease6_select", lease6_select_callout));
+
+    CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
+
+    Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_, IOAddress("::"),
+                                               false, callout_handle);
+    // Check that we got a lease
+    ASSERT_TRUE(lease);
+
+    // Do all checks on the lease
+    checkLease6(lease);
+
+    // Check that the lease is indeed in LeaseMgr
+    Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(lease->addr_);
+    ASSERT_TRUE(from_mgr);
+
+    // Check that callouts were indeed called
+    EXPECT_EQ("lease6_select", callback_name_);
+
+    // Now check that the lease in LeaseMgr has the same parameters
+    ASSERT_TRUE(callback_lease6_);
+    detailCompareLease(callback_lease6_, from_mgr);
+
+    ASSERT_TRUE(callback_subnet6_);
+    EXPECT_EQ(subnet_->toText(), callback_subnet6_->toText());
+
+    EXPECT_FALSE(callback_fake_allocation_);
+
+    // Check if all expected parameters are reported. It's a bit tricky, because
+    // order may be different. If the test starts failing, because someone tweaked
+    // hooks engine, we'll have to implement proper vector matching (ignoring order)
+    vector<string> expected_argument_names;
+    expected_argument_names.push_back("fake_allocation");
+    expected_argument_names.push_back("lease6");
+    expected_argument_names.push_back("subnet6");
+
+    sort(callback_argument_names_.begin(), callback_argument_names_.end());
+    sort(expected_argument_names.begin(), expected_argument_names.end());
+
+    EXPECT_TRUE(callback_argument_names_ == expected_argument_names);
+}
+
+// This test checks if lease6_select callout is able to override the values
+// in a lease6.
+TEST_F(HookAllocEngine6Test, change_lease6_select) {
+
+    // Make sure that the overridden values are different than the ones from
+    // subnet originally used to create the lease
+    ASSERT_NE(t1_override_, subnet_->getT1());
+    ASSERT_NE(t2_override_, subnet_->getT2());
+    ASSERT_NE(pref_override_, subnet_->getPreferred());
+    ASSERT_NE(valid_override_, subnet_->getValid());
+    ASSERT_FALSE(subnet_->inRange(addr_override_));
+
+    // Create allocation engine (hook names are registered in its ctor)
+    boost::scoped_ptr<AllocEngine> engine;
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_TRUE(engine);
+
+    // Initialize Hooks Manager
+    vector<string> libraries; // no libraries at this time
+    HooksManager::loadLibraries(libraries);
+
+    // Install a callout
+    EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                        "lease6_select", lease6_select_different_callout));
+
+    // Normally, dhcpv6_srv would passed the handle when calling allocateAddress6,
+    // but in tests we need to create it on our own.
+    CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
+
+    // Call allocateAddress6. Callouts should be triggered here.
+    Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_, IOAddress("::"),
+                                               false, callout_handle);
+    // Check that we got a lease
+    ASSERT_TRUE(lease);
+
+    // See if the values overridden by callout are there
+    EXPECT_TRUE(lease->addr_.equals(addr_override_));
+    EXPECT_EQ(t1_override_, lease->t1_);
+    EXPECT_EQ(t2_override_, lease->t2_);
+    EXPECT_EQ(pref_override_, lease->preferred_lft_);
+    EXPECT_EQ(valid_override_, lease->valid_lft_);
+
+    // Now check if the lease is in the database
+    Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(lease->addr_);
+    ASSERT_TRUE(from_mgr);
+
+    // Check if values in the database are overridden
+    EXPECT_TRUE(from_mgr->addr_.equals(addr_override_));
+    EXPECT_EQ(t1_override_, from_mgr->t1_);
+    EXPECT_EQ(t2_override_, from_mgr->t2_);
+    EXPECT_EQ(pref_override_, from_mgr->preferred_lft_);
+    EXPECT_EQ(valid_override_, from_mgr->valid_lft_);
+}
+
+
+/// @brief helper class used in Hooks testing in AllocEngine4
+///
+/// It features a couple of callout functions and buffers to store
+/// the data that is accessible via callouts.
+class HookAllocEngine4Test : public AllocEngine4Test {
+public:
+    HookAllocEngine4Test() {
+        resetCalloutBuffers();
+    }
+
+    virtual ~HookAllocEngine4Test() {
+        HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts(
+            "lease4_select");
+    }
+
+    /// @brief clears out buffers, so callouts can store received arguments
+    void resetCalloutBuffers() {
+        callback_name_ = string("");
+        callback_subnet4_.reset();
+        callback_fake_allocation_ = false;
+        callback_lease4_.reset();
+        callback_argument_names_.clear();
+        callback_addr_original_ = IOAddress("::");
+        callback_addr_updated_ = IOAddress("::");
+    }
+
+    /// callback that stores received callout name and received values
+    static int
+    lease4_select_callout(CalloutHandle& callout_handle) {
+
+        callback_name_ = string("lease4_select");
+
+        callout_handle.getArgument("subnet4", callback_subnet4_);
+        callout_handle.getArgument("fake_allocation", callback_fake_allocation_);
+        callout_handle.getArgument("lease4", callback_lease4_);
+
+        callback_addr_original_ = callback_lease4_->addr_;
+
+        callback_argument_names_ = callout_handle.getArgumentNames();
+        return (0);
+    }
+
+    /// callback that overrides the lease with different values
+    static int
+    lease4_select_different_callout(CalloutHandle& callout_handle) {
+
+        // Let's call the basic callout, so it can record all parameters
+        lease4_select_callout(callout_handle);
+
+        // Now we need to tweak the least a bit
+        Lease4Ptr lease;
+        callout_handle.getArgument("lease4", lease);
+        callback_addr_updated_ = addr_override_;
+        lease->addr_ = callback_addr_updated_;
+        lease->t1_ = t1_override_;
+        lease->t2_ = t2_override_;
+        lease->valid_lft_ = valid_override_;
+
+        return (0);
+    }
+
+    // Values to be used in callout to override lease4 content
+    static const IOAddress addr_override_;
+    static const uint32_t t1_override_;
+    static const uint32_t t2_override_;
+    static const uint32_t valid_override_;
+
+    // Callback will store original and overridden values here
+    static IOAddress callback_addr_original_;
+    static IOAddress callback_addr_updated_;
+
+    // Buffers (callback will store received values here)
+    static string callback_name_;
+    static Subnet4Ptr callback_subnet4_;
+    static Lease4Ptr callback_lease4_;
+    static bool callback_fake_allocation_;
+    static vector<string> callback_argument_names_;
+};
+
+// For some reason intialization within a class makes the linker confused.
+// linker complains about undefined references if they are defined within
+// the class declaration.
+const IOAddress HookAllocEngine4Test::addr_override_("192.0.3.1");
+const uint32_t HookAllocEngine4Test::t1_override_ = 4000;
+const uint32_t HookAllocEngine4Test::t2_override_ = 7000;
+const uint32_t HookAllocEngine4Test::valid_override_ = 9000;
+
+IOAddress HookAllocEngine4Test::callback_addr_original_("::");
+IOAddress HookAllocEngine4Test::callback_addr_updated_("::");
+
+string HookAllocEngine4Test::callback_name_;
+Subnet4Ptr HookAllocEngine4Test::callback_subnet4_;
+Lease4Ptr HookAllocEngine4Test::callback_lease4_;
+bool HookAllocEngine4Test::callback_fake_allocation_;
+vector<string> HookAllocEngine4Test::callback_argument_names_;
+
+// This test checks if the lease4_select callout is executed and expected
+// parameters as passed.
+TEST_F(HookAllocEngine4Test, lease4_select) {
+
+    // Note: The following order is working as expected:
+    // 1. create AllocEngine (that register hook points)
+    // 2. call loadLibraries()
+    //
+    // This order, however, causes segfault in HooksManager
+    // 1. call loadLibraries()
+    // 2. create AllocEngine (that register hook points)
+
+    // Create allocation engine (hook names are registered in its ctor)
+    boost::scoped_ptr<AllocEngine> engine;
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_TRUE(engine);
+
+    // Initialize Hooks Manager
+    vector<string> libraries; // no libraries at this time
+    HooksManager::loadLibraries(libraries);
+
+    // Install pkt4_receive_callout
+    EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                        "lease4_select", lease4_select_callout));
+
+    CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
+
+    Lease4Ptr lease = engine->allocateAddress4(subnet_, clientid_, hwaddr_,
+                                               IOAddress("0.0.0.0"),
+                                               false, callout_handle);
+    // Check that we got a lease
+    ASSERT_TRUE(lease);
+
+    // Do all checks on the lease
+    checkLease4(lease);
+
+    // Check that the lease is indeed in LeaseMgr
+    Lease4Ptr from_mgr = LeaseMgrFactory::instance().getLease4(lease->addr_);
+    ASSERT_TRUE(from_mgr);
+
+    // Check that callouts were indeed called
+    EXPECT_EQ("lease4_select", callback_name_);
+
+    // Now check that the lease in LeaseMgr has the same parameters
+    ASSERT_TRUE(callback_lease4_);
+    detailCompareLease(callback_lease4_, from_mgr);
+
+    ASSERT_TRUE(callback_subnet4_);
+    EXPECT_EQ(subnet_->toText(), callback_subnet4_->toText());
+
+    EXPECT_EQ(callback_fake_allocation_, false);
+
+    // Check if all expected parameters are reported. It's a bit tricky, because
+    // order may be different. If the test starts failing, because someone tweaked
+    // hooks engine, we'll have to implement proper vector matching (ignoring order)
+    vector<string> expected_argument_names;
+    expected_argument_names.push_back("fake_allocation");
+    expected_argument_names.push_back("lease4");
+    expected_argument_names.push_back("subnet4");
+    EXPECT_TRUE(callback_argument_names_ == expected_argument_names);
+}
+
+// This test checks if lease4_select callout is able to override the values
+// in a lease4.
+TEST_F(HookAllocEngine4Test, change_lease4_select) {
+
+    // Make sure that the overridden values are different than the ones from
+    // subnet originally used to create the lease
+    ASSERT_NE(t1_override_, subnet_->getT1());
+    ASSERT_NE(t2_override_, subnet_->getT2());
+    ASSERT_NE(valid_override_, subnet_->getValid());
+    ASSERT_FALSE(subnet_->inRange(addr_override_));
+
+    // Create allocation engine (hook names are registered in its ctor)
+    boost::scoped_ptr<AllocEngine> engine;
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_TRUE(engine);
+
+    // Initialize Hooks Manager
+    vector<string> libraries; // no libraries at this time
+    HooksManager::loadLibraries(libraries);
+
+    // Install a callout
+    EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                        "lease4_select", lease4_select_different_callout));
+
+    // Normally, dhcpv4_srv would passed the handle when calling allocateAddress4,
+    // but in tests we need to create it on our own.
+    CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
+
+    // Call allocateAddress4. Callouts should be triggered here.
+    Lease4Ptr lease = engine->allocateAddress4(subnet_, clientid_, hwaddr_, IOAddress("0.0.0.0"),
+                                               false, callout_handle);
+    // Check that we got a lease
+    ASSERT_TRUE(lease);
+
+    // See if the values overridden by callout are there
+    EXPECT_TRUE(lease->addr_.equals(addr_override_));
+    EXPECT_EQ(t1_override_, lease->t1_);
+    EXPECT_EQ(t2_override_, lease->t2_);
+    EXPECT_EQ(valid_override_, lease->valid_lft_);
+
+    // Now check if the lease is in the database
+    Lease4Ptr from_mgr = LeaseMgrFactory::instance().getLease4(lease->addr_);
+    ASSERT_TRUE(from_mgr);
+
+    // Check if values in the database are overridden
+    EXPECT_TRUE(from_mgr->addr_.equals(addr_override_));
+    EXPECT_EQ(t1_override_, from_mgr->t1_);
+    EXPECT_EQ(t2_override_, from_mgr->t2_);
+    EXPECT_EQ(valid_override_, from_mgr->valid_lft_);
+}
+
+
+
 }; // End of anonymous namespace
diff --git a/src/lib/dhcpsrv/tests/basic_callout_library.cc b/src/lib/dhcpsrv/tests/basic_callout_library.cc
new file mode 100644
index 0000000..ff39a9c
--- /dev/null
+++ b/src/lib/dhcpsrv/tests/basic_callout_library.cc
@@ -0,0 +1,115 @@
+// Copyright (C) 2013  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.
+
+/// @file
+/// @brief Basic callout library
+///
+/// This is source of a test library for various test (LibraryManager and
+/// HooksManager).  The characteristics of the library produced from this
+/// file are:
+///
+/// - Only the "version" framework function is supplied.
+///
+/// - A context_create callout is supplied.
+///
+/// - Three "standard" callouts are supplied corresponding to the hooks
+///   "lm_one", "lm_two", "lm_three".  All do some trivial calculations
+///   on the arguments supplied to it and the context variables, returning
+///   intermediate results through the "result" argument. The result of
+///   executing all four callouts in order is:
+///
+///   @f[ (10 + data_1) * data_2 - data_3 @f]
+///
+///   ...where data_1, data_2 and data_3 are the values passed in arguments of
+///   the same name to the three callouts (data_1 passed to lm_one, data_2 to
+///   lm_two etc.) and the result is returned in the argument "result".
+
+#include <hooks/hooks.h>
+#include <fstream>
+
+using namespace isc::hooks;
+using namespace std;
+
+extern "C" {
+
+// Callouts.  All return their result through the "result" argument.
+
+int
+context_create(CalloutHandle& handle) {
+    handle.setContext("result", static_cast<int>(10));
+    handle.setArgument("result", static_cast<int>(10));
+    return (0);
+}
+
+// First callout adds the passed "data_1" argument to the initialized context
+// value of 10. (Note that the value set by context_create is accessed through
+// context and not the argument, so checking that context is correctly passed
+// between callouts in the same library.)
+
+int
+lm_one(CalloutHandle& handle) {
+    int data;
+    handle.getArgument("data_1", data);
+
+    int result;
+    handle.getArgument("result", result);
+
+    result += data;
+    handle.setArgument("result", result);
+
+    return (0);
+}
+
+// Second callout multiplies the current context value by the "data_2"
+// argument.
+
+int
+lm_two(CalloutHandle& handle) {
+    int data;
+    handle.getArgument("data_2", data);
+
+    int result;
+    handle.getArgument("result", result);
+
+    result *= data;
+    handle.setArgument("result", result);
+
+    return (0);
+}
+
+// Final callout subtracts the result in "data_3".
+
+int
+lm_three(CalloutHandle& handle) {
+    int data;
+    handle.getArgument("data_3", data);
+
+    int result;
+    handle.getArgument("result", result);
+
+    result -= data;
+    handle.setArgument("result", result);
+
+    return (0);
+}
+
+// Framework functions.  Only version() is supplied here.
+
+int
+version() {
+    return (BIND10_HOOKS_VERSION);
+}
+
+};
+
diff --git a/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc b/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc
index 77c3e36..38d2f0a 100644
--- a/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc
+++ b/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc
@@ -165,6 +165,7 @@ public:
         CfgMgr::instance().deleteSubnets4();
         CfgMgr::instance().deleteSubnets6();
         CfgMgr::instance().deleteOptionDefs();
+        CfgMgr::instance().deleteActiveIfaces();
     }
 
     /// @brief generates interface-id option based on provided text
@@ -573,6 +574,50 @@ TEST_F(CfgMgrTest, optionSpace6) {
     // @todo decide if a duplicate vendor space is allowed.
 }
 
+// This test verifies that it is possible to specify interfaces that server
+// should listen on.
+TEST_F(CfgMgrTest, addActiveIface) {
+    CfgMgr& cfg_mgr = CfgMgr::instance();
+
+    cfg_mgr.addActiveIface("eth0");
+    cfg_mgr.addActiveIface("eth1");
+
+    EXPECT_TRUE(cfg_mgr.isActiveIface("eth0"));
+    EXPECT_TRUE(cfg_mgr.isActiveIface("eth1"));
+    EXPECT_FALSE(cfg_mgr.isActiveIface("eth2"));
+
+    cfg_mgr.deleteActiveIfaces();
+
+    EXPECT_FALSE(cfg_mgr.isActiveIface("eth0"));
+    EXPECT_FALSE(cfg_mgr.isActiveIface("eth1"));
+    EXPECT_FALSE(cfg_mgr.isActiveIface("eth2"));
+}
+
+// This test verifies that it is possible to set the flag which configures the
+// server to listen on all interfaces.
+TEST_F(CfgMgrTest, activateAllIfaces) {
+    CfgMgr& cfg_mgr = CfgMgr::instance();
+
+    cfg_mgr.addActiveIface("eth0");
+    cfg_mgr.addActiveIface("eth1");
+
+    ASSERT_TRUE(cfg_mgr.isActiveIface("eth0"));
+    ASSERT_TRUE(cfg_mgr.isActiveIface("eth1"));
+    ASSERT_FALSE(cfg_mgr.isActiveIface("eth2"));
+
+    cfg_mgr.activateAllIfaces();
+
+    EXPECT_TRUE(cfg_mgr.isActiveIface("eth0"));
+    EXPECT_TRUE(cfg_mgr.isActiveIface("eth1"));
+    EXPECT_TRUE(cfg_mgr.isActiveIface("eth2"));
+
+    cfg_mgr.deleteActiveIfaces();
+
+    EXPECT_FALSE(cfg_mgr.isActiveIface("eth0"));
+    EXPECT_FALSE(cfg_mgr.isActiveIface("eth1"));
+    EXPECT_FALSE(cfg_mgr.isActiveIface("eth2"));
+}
+
 // No specific tests for getSubnet6. That method (2 overloaded versions) is tested
 // in Dhcpv6SrvTest.selectSubnetAddr and Dhcpv6SrvTest.selectSubnetIface
 // (see src/bin/dhcp6/tests/dhcp6_srv_unittest.cc)
diff --git a/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc b/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc
index 687ef92..747e38d 100644
--- a/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc
+++ b/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc
@@ -20,19 +20,23 @@
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/subnet.h>
 #include <dhcpsrv/dhcp_parsers.h>
+#include <dhcpsrv/tests/test_libraries.h>
 #include <exceptions/exceptions.h>
+#include <hooks/hooks_manager.h>
 
 #include <gtest/gtest.h>
 #include <boost/foreach.hpp>
+#include <boost/pointer_cast.hpp>
 
 #include <map>
 #include <string>
 
 using namespace std;
 using namespace isc;
-using namespace isc::dhcp;
-using namespace isc::data;
 using namespace isc::config;
+using namespace isc::data;
+using namespace isc::dhcp;
+using namespace isc::hooks;
 
 namespace {
 
@@ -42,12 +46,13 @@ public:
     /// @brief Constructor
     ///
     DhcpParserTest() {
+        CfgMgr::instance().deleteActiveIfaces();
     }
 };
 
 
 /// @brief Check BooleanParser basic functionality.
-/// 
+///
 /// Verifies that the parser:
 /// 1. Does not allow empty for storage.
 /// 2. Rejects a non-boolean element.
@@ -94,7 +99,7 @@ TEST_F(DhcpParserTest, booleanParserTest) {
 }
 
 /// @brief Check StringParser basic functionality
-/// 
+///
 /// Verifies that the parser:
 /// 1. Does not allow empty for storage.
 /// 2. Builds with a nont string value.
@@ -134,7 +139,7 @@ TEST_F(DhcpParserTest, stringParserTest) {
 }
 
 /// @brief Check Uint32Parser basic functionality
-/// 
+///
 /// Verifies that the parser:
 /// 1. Does not allow empty for storage.
 /// 2. Rejects a non-integer element.
@@ -163,8 +168,8 @@ TEST_F(DhcpParserTest, uint32ParserTest) {
     ElementPtr int_element = Element::create(-1);
     EXPECT_THROW(parser.build(int_element), isc::BadValue);
 
-    // Verify that parser with rejects too large a value provided we are on 
-    // 64-bit platform. 
+    // Verify that parser with rejects too large a value provided we are on
+    // 64-bit platform.
     if (sizeof(long) > sizeof(uint32_t)) {
         long max = (long)(std::numeric_limits<uint32_t>::max()) + 1;
         int_element->setValue(max);
@@ -197,30 +202,61 @@ TEST_F(DhcpParserTest, uint32ParserTest) {
 ///
 /// Verifies that the parser:
 /// 1. Does not allow empty for storage.
-/// 2. Does not allow name other than "interface"
-///
-/// InterfaceListParser doesn't do very much, this test will need to 
-/// expand once it does.
+/// 2. Does not allow name other than "interfaces"
+/// 3. Parses list of interfaces and adds them to CfgMgr
+/// 4. Parses wildcard interface name and sets a CfgMgr flag which indicates
+/// that server will listen on all interfaces.
 TEST_F(DhcpParserTest, interfaceListParserTest) {
 
-    const std::string name = "interface";
+    const std::string name = "interfaces";
 
     // Verify that parser constructor fails if parameter name isn't "interface"
     EXPECT_THROW(InterfaceListConfigParser("bogus_name"), isc::BadValue);
 
-    InterfaceListConfigParser parser(name);
+    boost::scoped_ptr<InterfaceListConfigParser>
+        parser(new InterfaceListConfigParser(name));
     ElementPtr list_element = Element::createList();
     list_element->add(Element::create("eth0"));
     list_element->add(Element::create("eth1"));
+
+    // Make sure there are no interfaces added yet.
+    ASSERT_FALSE(CfgMgr::instance().isActiveIface("eth0"));
+    ASSERT_FALSE(CfgMgr::instance().isActiveIface("eth1"));
+
+    // This should parse the configuration and add eth0 and eth1 to the list
+    // of interfaces that server should listen on.
+    parser->build(list_element);
+    parser->commit();
+
+    // Use CfgMgr instance to check if eth0 and eth1 was added, and that
+    // eth2 was not added.
+    CfgMgr& cfg_mgr = CfgMgr::instance();
+    EXPECT_TRUE(cfg_mgr.isActiveIface("eth0"));
+    EXPECT_TRUE(cfg_mgr.isActiveIface("eth1"));
+    EXPECT_FALSE(cfg_mgr.isActiveIface("eth2"));
+
+    // Add keyword all to the configuration. This should activate all
+    // interfaces, including eth2, even though it has not been explicitly
+    // added.
+    list_element->add(Element::create("*"));
+
+    // Reset parser's state.
+    parser.reset(new InterfaceListConfigParser(name));
+    parser->build(list_element);
+    parser->commit();
+
+    EXPECT_TRUE(cfg_mgr.isActiveIface("eth0"));
+    EXPECT_TRUE(cfg_mgr.isActiveIface("eth1"));
+    EXPECT_TRUE(cfg_mgr.isActiveIface("eth2"));
 }
 
-/// @brief Test Implementation of abstract OptionDataParser class. Allows 
-/// testing basic option parsing.   
+/// @brief Test Implementation of abstract OptionDataParser class. Allows
+/// testing basic option parsing.
 class UtestOptionDataParser : public OptionDataParser {
 public:
 
-    UtestOptionDataParser(const std::string&, 
-        OptionStoragePtr options, ParserContextPtr global_context) 
+    UtestOptionDataParser(const std::string&,
+        OptionStoragePtr options, ParserContextPtr global_context)
         :OptionDataParser("", options, global_context) {
     }
 
@@ -234,12 +270,12 @@ protected:
     virtual OptionDefinitionPtr findServerSpaceOptionDefinition (
                 std::string&, uint32_t) {
         OptionDefinitionPtr def;
-        // always return empty 
+        // always return empty
         return (def);
     }
 };
 
-/// @brief Test Fixture class which provides basic structure for testing 
+/// @brief Test Fixture class which provides basic structure for testing
 /// configuration parsing.  This is essentially the same structure provided
 /// by dhcp servers.
 class ParseConfigTest : public ::testing::Test {
@@ -253,15 +289,15 @@ public:
         reset_context();
     }
 
-    /// @brief Parses a configuration.   
+    /// @brief Parses a configuration.
     ///
     /// Parse the given configuration, populating the context storage with
-    /// the parsed elements.  
-    /// 
+    /// the parsed elements.
+    ///
     /// @param config_set is the set of elements to parse.
     /// @return returns an ConstElementPtr containing the numeric result
     /// code and outcome comment.
-    isc::data::ConstElementPtr parseElementSet(isc::data::ConstElementPtr 
+    isc::data::ConstElementPtr parseElementSet(isc::data::ConstElementPtr
                                            config_set) {
         // Answer will hold the result.
         ConstElementPtr answer;
@@ -276,7 +312,7 @@ public:
 
         ConfigPair config_pair;
         try {
-            // Iteraate over the config elements.
+            // Iterate over the config elements.
             const std::map<std::string, ConstElementPtr>& values_map =
                                                       config_set->mapValue();
             BOOST_FOREACH(config_pair, values_map) {
@@ -293,7 +329,7 @@ public:
             }
 
             // The option values parser is the next one to be run.
-            std::map<std::string, ConstElementPtr>::const_iterator 
+            std::map<std::string, ConstElementPtr>::const_iterator
                                 option_config = values_map.find("option-data");
             if (option_config != values_map.end()) {
                 option_parser->build(option_config->second);
@@ -316,22 +352,34 @@ public:
 
     /// @brief Create an element parser based on the element name.
     ///
-    /// Note that currently it only supports option-defs and option-data, 
+    /// Creates a parser for the appropriate element and stores a pointer to it
+    /// in the appropriate class variable.
+    ///
+    /// Note that the method currently it only supports option-defs, option-data
+    /// and hooks-libraries.
     /// 
     /// @param config_id is the name of the configuration element. 
-    /// @return returns a raw pointer to DhcpConfigParser. Note caller is
-    /// responsible for deleting it once no longer needed.
+    ///
+    /// @return returns a shared pointer to DhcpConfigParser.
+    ///
     /// @throw throws NotImplemented if element name isn't supported.
-    DhcpConfigParser* createConfigParser(const std::string& config_id) {
-        DhcpConfigParser* parser = NULL;
+    ParserPtr createConfigParser(const std::string& config_id) {
+        ParserPtr parser;
         if (config_id.compare("option-data") == 0) {
-            parser = new OptionDataListParser(config_id, 
-                                          parser_context_->options_, 
-                                          parser_context_,
-                                          UtestOptionDataParser::factory);
+            parser.reset(new OptionDataListParser(config_id, 
+                                              parser_context_->options_, 
+                                              parser_context_,
+                                              UtestOptionDataParser::factory));
+
         } else if (config_id.compare("option-def") == 0) {
-            parser  = new OptionDefListParser(config_id, 
-                                          parser_context_->option_defs_);
+            parser.reset(new OptionDefListParser(config_id, 
+                                              parser_context_->option_defs_));
+
+        } else if (config_id.compare("hooks-libraries") == 0) {
+            parser.reset(new HooksLibrariesParser(config_id));
+            hooks_libraries_parser_ =
+                boost::dynamic_pointer_cast<HooksLibrariesParser>(parser);
+
         } else {
             isc_throw(NotImplemented,
                 "Parser error: configuration parameter not supported: "
@@ -341,15 +389,15 @@ public:
         return (parser);
     }
 
-    /// @brief Convenicee method for parsing a configuration 
+    /// @brief Convenience method for parsing a configuration 
     /// 
     /// Given a configuration string, convert it into Elements
-    /// and parse them. 
+    /// and parse them.
     /// @param config is the configuration string to parse
     ///
-    /// @return retuns 0 if the configuration parsed successfully, 
+    /// @return retuns 0 if the configuration parsed successfully,
     /// non-zero otherwise failure.
-    int parseConfiguration (std::string &config) {    
+    int parseConfiguration(const std::string& config) {    
         int rcode_ = 1;
         // Turn config into elements.
         // Test json just to make sure its valid.
@@ -357,23 +405,24 @@ public:
         EXPECT_TRUE(json);
         if (json) {
             ConstElementPtr status = parseElementSet(json);
-            ConstElementPtr comment_ = parseAnswer(rcode_, status);
+            ConstElementPtr comment = parseAnswer(rcode_, status);
+            error_text_ = comment->stringValue();
         }
 
         return (rcode_);
     }
 
-    /// @brief Find an option definition for a given space and code within 
+    /// @brief Find an option definition for a given space and code within
     /// the parser context.
     /// @param space is the space name of the desired option.
     /// @param code is the numeric "type" of the desired option.
     /// @return returns an OptionDefinitionPtr which points to the found
     /// definition or is empty.
-    /// ASSERT_ tests don't work inside functions that return values 
+    /// ASSERT_ tests don't work inside functions that return values
     OptionDefinitionPtr getOptionDef(std::string space, uint32_t code)
     {
         OptionDefinitionPtr def;
-        OptionDefContainerPtr defs = 
+        OptionDefContainerPtr defs =
                             parser_context_->option_defs_->getItems(space);
         // Should always be able to get definitions list even if it is empty.
         EXPECT_TRUE(defs);
@@ -387,41 +436,41 @@ public:
                 def = *(idx.begin());
             }
         }
-        return (def); 
+        return (def);
     }
 
-    /// @brief Find an option for a given space and code within the parser 
+    /// @brief Find an option for a given space and code within the parser
     /// context.
     /// @param space is the space name of the desired option.
     /// @param code is the numeric "type" of the desired option.
     /// @return returns an OptionPtr which points to the found
     /// option or is empty.
-    /// ASSERT_ tests don't work inside functions that return values 
+    /// ASSERT_ tests don't work inside functions that return values
     OptionPtr getOptionPtr(std::string space, uint32_t code)
     {
         OptionPtr option_ptr;
-        Subnet::OptionContainerPtr options = 
+        Subnet::OptionContainerPtr options =
                             parser_context_->options_->getItems(space);
         // Should always be able to get options list even if it is empty.
         EXPECT_TRUE(options);
         if (options) {
             // Attempt to find desired option.
             const Subnet::OptionContainerTypeIndex& idx = options->get<1>();
-            const Subnet::OptionContainerTypeRange& range = 
+            const Subnet::OptionContainerTypeRange& range =
                                                         idx.equal_range(code);
             int cnt = std::distance(range.first, range.second);
             EXPECT_EQ(1, cnt);
             if (cnt == 1) {
-                Subnet::OptionDescriptor desc = *(idx.begin()); 
-                option_ptr = desc.option; 
+                Subnet::OptionDescriptor desc = *(idx.begin());
+                option_ptr = desc.option;
                 EXPECT_TRUE(option_ptr);
             }
         }
 
-        return (option_ptr); 
+        return (option_ptr);
     }
 
-    /// @brief Wipes the contents of the context to allowing another parsing 
+    /// @brief Wipes the contents of the context to allowing another parsing
     /// during a given test if needed.
     void reset_context(){
         // Note set context universe to V6 as it has to be something.
@@ -429,14 +478,25 @@ public:
         CfgMgr::instance().deleteSubnets6();
         CfgMgr::instance().deleteOptionDefs();
         parser_context_.reset(new ParserContext(Option::V6));
+
+        // Ensure no hooks libraries are loaded.
+        HooksManager::unloadLibraries();
     }
 
+    /// @brief Parsers used in the parsing of the configuration
+    ///
+    /// Allows the tests to interrogate the state of the parsers (if required).
+    boost::shared_ptr<HooksLibrariesParser> hooks_libraries_parser_;
+
     /// @brief Parser context - provides storage for options and definitions
     ParserContextPtr parser_context_;
+
+    /// @brief Error string if the parsing failed
+    std::string error_text_;
 };
 
 /// @brief Check Basic parsing of option definitions.
-/// 
+///
 /// Note that this tests basic operation of the OptionDefinitionListParser and
 /// OptionDefinitionParser.  It uses a simple configuration consisting of one
 /// one definition and verifies that it is parsed and committed to storage
@@ -460,8 +520,9 @@ TEST_F(ParseConfigTest, basicOptionDefTest) {
     int rcode = parseConfiguration(config);
     ASSERT_TRUE(rcode == 0);
 
+
     // Verify that the option definition can be retrieved.
-    OptionDefinitionPtr def = getOptionDef("isc", 100); 
+    OptionDefinitionPtr def = getOptionDef("isc", 100);
     ASSERT_TRUE(def);
 
     // Verify that the option definition is correct.
@@ -473,7 +534,7 @@ TEST_F(ParseConfigTest, basicOptionDefTest) {
 }
 
 /// @brief Check Basic parsing of options.
-/// 
+///
 /// Note that this tests basic operation of the OptionDataListParser and
 /// OptionDataParser.  It uses a simple configuration consisting of one
 /// one definition and matching option data.  It verifies that the option
@@ -517,3 +578,117 @@ TEST_F(ParseConfigTest, basicOptionDataTest) {
 
 };  // Anonymous namespace
 
+/// These tests check basic operation of the HooksLibrariesParser.
+
+// hooks-libraries that do not contain anything.
+TEST_F(ParseConfigTest, noHooksLibrariesTest) {
+
+    // Configuration with hooks-libraries not present.
+    string config = "{ \"hooks-libraries\": [] }";
+
+    // Verify that the configuration string parses.
+    int rcode = parseConfiguration(config);
+    ASSERT_TRUE(rcode == 0) << error_text_;
+
+    // Check that the parser recorded no change to the current state
+    // (as the test starts with no hooks libraries loaded).
+    std::vector<std::string> libraries;
+    bool changed;
+    hooks_libraries_parser_->getLibraries(libraries, changed);
+    EXPECT_TRUE(libraries.empty());
+    EXPECT_FALSE(changed);
+
+    // Load a single library and repeat the parse.
+    vector<string> basic_library;
+    basic_library.push_back(string(BASIC_CALLOUT_LIBRARY));
+    HooksManager::loadLibraries(basic_library);
+
+    rcode = parseConfiguration(config);
+    ASSERT_TRUE(rcode == 0) << error_text_;
+
+    // This time the change should have been recorded.
+    hooks_libraries_parser_->getLibraries(libraries, changed);
+    EXPECT_TRUE(libraries.empty());
+    EXPECT_TRUE(changed);
+
+    // But repeating it again and we are back to no change.
+    rcode = parseConfiguration(config);
+    ASSERT_TRUE(rcode == 0) << error_text_;
+    hooks_libraries_parser_->getLibraries(libraries, changed);
+    EXPECT_TRUE(libraries.empty());
+    EXPECT_FALSE(changed);
+
+}
+
+
+TEST_F(ParseConfigTest, validHooksLibrariesTest) {
+
+    // Configuration string.  This contains a set of valid libraries.
+    const std::string quote("\"");
+    const std::string comma(", ");
+
+    const std::string config =
+        std::string("{ ") +
+            std::string("\"hooks-libraries\": [") +
+                quote + std::string(BASIC_CALLOUT_LIBRARY) + quote + comma +
+                quote + std::string(FULL_CALLOUT_LIBRARY)  + quote +
+            std::string("]") +
+        std::string("}");
+
+    // Verify that the configuration string parses.
+    int rcode = parseConfiguration(config);
+    ASSERT_TRUE(rcode == 0) << error_text_;
+
+    // Check that the parser holds two libraries and the configuration is
+    // recorded as having changed.
+    std::vector<std::string> libraries;
+    bool changed;
+    hooks_libraries_parser_->getLibraries(libraries, changed);
+    EXPECT_EQ(2, libraries.size());
+    EXPECT_TRUE(changed);
+
+    // The expected libraries should be the list of libraries specified
+    // in the given order.
+    std::vector<std::string> expected;
+    expected.push_back(BASIC_CALLOUT_LIBRARY);
+    expected.push_back(FULL_CALLOUT_LIBRARY);
+    EXPECT_TRUE(expected == libraries);
+
+    // Parse the string again.
+    rcode = parseConfiguration(config);
+    ASSERT_TRUE(rcode == 0) << error_text_;
+
+    // The list has not changed, and this is what we should see.
+    hooks_libraries_parser_->getLibraries(libraries, changed);
+    EXPECT_EQ(2, libraries.size());
+    EXPECT_FALSE(changed);
+}
+
+// Check with a set of libraries, some of which are invalid.
+TEST_F(ParseConfigTest, invalidHooksLibrariesTest) {
+
+    // @todo Initialize global library context to null
+
+    // Configuration string.  This contains an invalid library which should
+    // trigger an error in the "build" stage.
+    const std::string quote("\"");
+    const std::string comma(", ");
+
+    const std::string config =
+        std::string("{ ") +
+            std::string("\"hooks-libraries\": [") +
+                quote + std::string(BASIC_CALLOUT_LIBRARY) + quote + comma +
+                quote + std::string(NOT_PRESENT_LIBRARY) + quote + comma +
+                quote + std::string(FULL_CALLOUT_LIBRARY)  + quote +
+            std::string("]") +
+        std::string("}");
+
+    // Verify that the configuration fails to parse. (Syntactically it's OK,
+    // but the library is invalid).
+    int rcode = parseConfiguration(config);
+    ASSERT_FALSE(rcode == 0) << error_text_;
+
+    // Check that the message contains the library in error.
+    EXPECT_FALSE(error_text_.find(NOT_PRESENT_LIBRARY) == string::npos) <<
+        "Error text returned from parse failure is " << error_text_;
+}
diff --git a/src/lib/dhcpsrv/tests/full_callout_library.cc b/src/lib/dhcpsrv/tests/full_callout_library.cc
new file mode 100644
index 0000000..df7a76f
--- /dev/null
+++ b/src/lib/dhcpsrv/tests/full_callout_library.cc
@@ -0,0 +1,137 @@
+// Copyright (C) 2013  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.
+
+/// @file
+/// @brief Full callout library
+///
+/// This is source of a test library for various test (LibraryManager and
+/// HooksManager).  The characteristics of the library produced from this
+/// file are:
+///
+/// The characteristics of this library are:
+///
+/// - All three framework functions are supplied (version(), load() and
+///   unload()), with unload() creating a marker file.  The test code checks
+///   for the presence of this file, so verifying that unload() has been run.
+///
+/// - One standard and two non-standard callouts are supplied, with the latter
+///   being registered by the load() function.
+///
+///   All callouts do trivial calculations, the result of all being called in
+///   sequence being
+///
+///   @f[ ((7 * data_1) - data_2) * data_3 @f]
+///
+///   ...where data_1, data_2 and data_3 are the values passed in arguments of
+///   the same name to the three callouts (data_1 passed to lm_one, data_2 to
+///   lm_two etc.) and the result is returned in the argument "result".
+
+#include <hooks/hooks.h>
+#include <hooks/tests/marker_file.h>
+
+#include <fstream>
+
+using namespace isc::hooks;
+
+extern "C" {
+
+// Callouts
+
+int
+context_create(CalloutHandle& handle) {
+    handle.setContext("result", static_cast<int>(7));
+    handle.setArgument("result", static_cast<int>(7));
+    return (0);
+}
+
+// First callout adds the passed "data_1" argument to the initialized context
+// value of 7. (Note that the value set by context_create is accessed through
+// context and not the argument, so checking that context is correctly passed
+// between callouts in the same library.)
+
+int
+lm_one(CalloutHandle& handle) {
+    int data;
+    handle.getArgument("data_1", data);
+
+    int result;
+    handle.getArgument("result", result);
+
+    result *= data;
+    handle.setArgument("result", result);
+
+    return (0);
+}
+
+// Second callout subtracts the passed value of data_2 from the current
+// running total.
+
+static int
+lm_nonstandard_two(CalloutHandle& handle) {
+    int data;
+    handle.getArgument("data_2", data);
+
+    int result;
+    handle.getArgument("result", result);
+
+    result -= data;
+    handle.setArgument("result", result);
+
+    return (0);
+}
+
+// Final callout multplies the current running total by data_3.
+
+static int
+lm_nonstandard_three(CalloutHandle& handle) {
+    int data;
+    handle.getArgument("data_3", data);
+
+    int result;
+    handle.getArgument("result", result);
+
+    result *= data;
+    handle.setArgument("result", result);
+
+    return (0);
+}
+
+// Framework functions
+
+int
+version() {
+    return (BIND10_HOOKS_VERSION);
+}
+
+int
+load(LibraryHandle& handle) {
+    // Register the non-standard functions
+    handle.registerCallout("lm_two", lm_nonstandard_two);
+    handle.registerCallout("lm_three", lm_nonstandard_three);
+
+    return (0);
+}
+
+int
+unload() {
+    // Create the marker file.
+    std::fstream marker;
+    marker.open(MARKER_FILE, std::fstream::out);
+    marker.close();
+
+    return (0);
+}
+
+};
+
diff --git a/src/lib/dhcpsrv/tests/test_libraries.h.in b/src/lib/dhcpsrv/tests/test_libraries.h.in
new file mode 100644
index 0000000..730fd84
--- /dev/null
+++ b/src/lib/dhcpsrv/tests/test_libraries.h.in
@@ -0,0 +1,55 @@
+// Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef TEST_LIBRARIES_H
+#define TEST_LIBRARIES_H
+
+#include <config.h>
+
+namespace {
+
+
+// Take carse of differences in DLL naming between operating systems.
+
+#ifdef OS_BSD
+#define DLL_SUFFIX ".dylib"
+
+#else
+#define DLL_SUFFIX ".so"
+
+#endif
+
+
+// Names of the libraries used in these tests.  These libraries are built using
+// libtool, so we need to look in the hidden ".libs" directory to locate the
+// .so file.  Note that we access the .so file - libtool creates this as a
+// like to the real shared library.
+
+// Basic library with context_create and three "standard" callouts.
+static const char* BASIC_CALLOUT_LIBRARY = "@abs_builddir@/.libs/libbcl"
+                                           DLL_SUFFIX;
+
+// Library with context_create and three "standard" callouts, as well as
+// load() and unload() functions.
+static const char* FULL_CALLOUT_LIBRARY = "@abs_builddir@/.libs/libfcl"
+                                          DLL_SUFFIX;
+
+// Name of a library which is not present.
+static const char* NOT_PRESENT_LIBRARY = "@abs_builddir@/.libs/libnothere"
+                                         DLL_SUFFIX;
+
+} // anonymous namespace
+
+
+#endif // TEST_LIBRARIES_H
diff --git a/src/lib/hooks/Makefile.am b/src/lib/hooks/Makefile.am
new file mode 100644
index 0000000..838564a
--- /dev/null
+++ b/src/lib/hooks/Makefile.am
@@ -0,0 +1,56 @@
+SUBDIRS = . tests
+
+AM_CPPFLAGS  = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CXXFLAGS  = $(B10_CXXFLAGS)
+
+# Some versions of GCC warn about some versions of Boost regarding
+# missing initializer for members in its posix_time.
+# https://svn.boost.org/trac/boost/ticket/3477
+# But older GCC compilers don't have the flag.
+AM_CXXFLAGS += $(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
+
+
+# Define rule to build logging source files from message file
+hooks_messages.h hooks_messages.cc: hooks_messages.mes
+	$(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/lib/hooks/hooks_messages.mes
+
+# Tell automake that the message files are built as part of the build process
+# (so that they are built before the main library is built).
+BUILT_SOURCES = hooks_messages.h hooks_messages.cc
+
+# Ensure that the message file is included in the distribution
+EXTRA_DIST = hooks_messages.mes
+
+# Get rid of generated message files on a clean
+CLEANFILES = *.gcno *.gcda hooks_messages.h hooks_messages.cc
+
+lib_LTLIBRARIES = libb10-hooks.la
+libb10_hooks_la_SOURCES  =
+libb10_hooks_la_SOURCES += callout_handle.cc callout_handle.h
+libb10_hooks_la_SOURCES += callout_manager.cc callout_manager.h
+libb10_hooks_la_SOURCES += hooks.h
+libb10_hooks_la_SOURCES += hooks_log.cc hooks_log.h
+libb10_hooks_la_SOURCES += hooks_manager.cc hooks_manager.h
+libb10_hooks_la_SOURCES += library_handle.cc library_handle.h
+libb10_hooks_la_SOURCES += library_manager.cc library_manager.h
+libb10_hooks_la_SOURCES += library_manager_collection.cc library_manager_collection.h
+libb10_hooks_la_SOURCES += pointer_converter.h
+libb10_hooks_la_SOURCES += server_hooks.cc server_hooks.h
+
+nodist_libb10_hooks_la_SOURCES = hooks_messages.cc hooks_messages.h
+
+libb10_hooks_la_CXXFLAGS = $(AM_CXXFLAGS)
+libb10_hooks_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
+libb10_hooks_la_LDFLAGS  = $(AM_LDFLAGS)
+libb10_hooks_la_LIBADD  =
+libb10_hooks_la_LIBADD += $(top_builddir)/src/lib/log/libb10-log.la
+libb10_hooks_la_LIBADD += $(top_builddir)/src/lib/util/libb10-util.la
+libb10_hooks_la_LIBADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
+
+
+if USE_CLANGPP
+# Disable unused parameter warning caused by some of the
+# Boost headers when compiling with clang.
+libb10_hooks_la_CXXFLAGS += -Wno-unused-parameter
+endif
diff --git a/src/lib/hooks/callout_handle.cc b/src/lib/hooks/callout_handle.cc
new file mode 100644
index 0000000..ce9ef82
--- /dev/null
+++ b/src/lib/hooks/callout_handle.cc
@@ -0,0 +1,162 @@
+// Copyright (C) 2013  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 <hooks/callout_handle.h>
+#include <hooks/callout_manager.h>
+#include <hooks/library_handle.h>
+#include <hooks/server_hooks.h>
+
+#include <string>
+#include <utility>
+#include <vector>
+
+using namespace std;
+
+namespace isc {
+namespace hooks {
+
+// Constructor.
+CalloutHandle::CalloutHandle(const boost::shared_ptr<CalloutManager>& manager,
+                    const boost::shared_ptr<LibraryManagerCollection>& lmcoll)
+    : lm_collection_(lmcoll), arguments_(), context_collection_(),
+      manager_(manager), skip_(false) {
+
+    // Call the "context_create" hook.  We should be OK doing this - although
+    // the constructor has not finished running, all the member variables
+    // have been created.
+    manager_->callCallouts(ServerHooks::CONTEXT_CREATE, *this);
+}
+
+// Destructor
+CalloutHandle::~CalloutHandle() {
+
+    // Call the "context_destroy" hook.  We should be OK doing this - although
+    // the destructor is being called, all the member variables are still in
+    // existence.
+    manager_->callCallouts(ServerHooks::CONTEXT_DESTROY, *this);
+
+    // Explicitly clear the argument and context objects.  This should free up
+    // all memory that could have been allocated by libraries that were loaded.
+    arguments_.clear();
+    context_collection_.clear();
+
+    // Normal destruction of the remaining variables will include the
+    // destruction of lm_collection_, an action that decrements the reference
+    // count on the library manager collection (which holds the libraries that
+    // could have allocated memory in the argument and context members.)  When
+    // that goes to zero, the libraries will be unloaded: at that point nothing
+    // in the hooks framework will be pointing to memory in the libraries'
+    // address space.
+    //
+    // It is possible that some other data structure in the server (the program
+    // using the hooks library) still references the address space and attempts
+    // to access it causing a segmentation fault. That issue is outside the
+    // scope of this framework and is not addressed by it.
+}
+
+// Return the name of all argument items.
+
+vector<string>
+CalloutHandle::getArgumentNames() const {
+
+    vector<string> names;
+    for (ElementCollection::const_iterator i = arguments_.begin();
+         i != arguments_.end(); ++i) {
+        names.push_back(i->first);
+    }
+
+    return (names);
+}
+
+// Return the library handle allowing the callout to access the CalloutManager
+// registration/deregistration functions.
+
+LibraryHandle&
+CalloutHandle::getLibraryHandle() const {
+    return (manager_->getLibraryHandle());
+}
+
+// Return the context for the currently pointed-to library.  This version is
+// used by the "setContext()" method and creates a context for the current
+// library if it does not exist.
+
+CalloutHandle::ElementCollection&
+CalloutHandle::getContextForLibrary() {
+    int libindex = manager_->getLibraryIndex();
+
+    // Access a reference to the element collection for the given index,
+    // creating a new element collection if necessary, and return it.
+    return (context_collection_[libindex]);
+}
+
+// The "const" version of the above, used by the "getContext()" method.  If
+// the context for the current library doesn't exist, throw an exception.
+
+const CalloutHandle::ElementCollection&
+CalloutHandle::getContextForLibrary() const {
+    int libindex = manager_->getLibraryIndex();
+
+    ContextCollection::const_iterator libcontext =
+        context_collection_.find(libindex);
+    if (libcontext == context_collection_.end()) {
+        isc_throw(NoSuchCalloutContext, "unable to find callout context "
+                  "associated with the current library index (" << libindex <<
+                  ")");
+    }
+
+    // Return a reference to the context's element collection.
+    return (libcontext->second);
+}
+
+// Return the name of all items in the context associated with the current]
+// library.
+
+vector<string>
+CalloutHandle::getContextNames() const {
+
+    vector<string> names;
+
+    const ElementCollection& elements = getContextForLibrary();
+    for (ElementCollection::const_iterator i = elements.begin();
+         i != elements.end(); ++i) {
+        names.push_back(i->first);
+    }
+
+    return (names);
+}
+
+// Return name of current hook (the hook to which the current callout is
+// attached) or the empty string if not called within the context of a
+// callout.
+
+string
+CalloutHandle::getHookName() const {
+    // Get the current hook index.
+    int index = manager_->getHookIndex();
+
+    // ... and look up the hook.
+    string hook = "";
+    try {
+        hook = ServerHooks::getServerHooks().getName(index);
+    } catch (const NoSuchHook&) {
+        // Hook index is invalid, so this methods probably called from outside
+        // a callout being executed via a call to CalloutManager::callCallouts.
+        // In this case, the empty string is returned.
+    }
+
+    return (hook);
+}
+
+} // namespace util
+} // namespace isc
diff --git a/src/lib/hooks/callout_handle.h b/src/lib/hooks/callout_handle.h
new file mode 100644
index 0000000..eb57fd4
--- /dev/null
+++ b/src/lib/hooks/callout_handle.h
@@ -0,0 +1,383 @@
+// Copyright (C) 2013  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 CALLOUT_HANDLE_H
+#define CALLOUT_HANDLE_H
+
+#include <exceptions/exceptions.h>
+#include <hooks/library_handle.h>
+
+#include <boost/any.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <map>
+#include <string>
+#include <vector>
+
+namespace isc {
+namespace hooks {
+
+/// @brief No such argument
+///
+/// Thrown if an attempt is made access an argument that does not exist.
+
+class NoSuchArgument : public Exception {
+public:
+    NoSuchArgument(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
+/// @brief No such callout context item
+///
+/// Thrown if an attempt is made to get an item of data from this callout's
+/// context and either the context or an item in the context with that name
+/// does not exist.
+
+class NoSuchCalloutContext : public Exception {
+public:
+    NoSuchCalloutContext(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
+// Forward declaration of the library handle and related collection classes.
+
+class CalloutManager;
+class LibraryHandle;
+class LibraryManagerCollection;
+
+/// @brief Per-packet callout handle
+///
+/// An object of this class is associated with every packet (or request)
+/// processed by the server.  It forms the principle means of passing data
+/// between the server and the user-library callouts.
+///
+/// The class allows access to the following information:
+///
+/// - Arguments.  When the callouts associated with a hook are called, they
+///   are passed information by the server (and can return information to it)
+///   through name/value pairs.  Each of these pairs is an argument and the
+///   information is accessed through the {get,set}Argument() methods.
+///
+/// - Per-packet context.  Each packet has a context associated with it, this
+///   context being  on a per-library basis.  In other words, As a packet passes
+///   through the callouts associated with a given library, the callouts can
+///   associate and retrieve information with the packet.  The per-library
+///   nature of the context means that the callouts within a given library can
+///   pass packet-specific information between one another, but they cannot pass
+///   information to callous within another library.  Typically such context
+///   is created in the "context_create" callout and destroyed in the
+///   "context_destroy" callout.  The information is accessed through the
+///   {get,set}Context() methods.
+///
+/// - Per-library handle (LibraryHandle). The library handle allows the
+///   callout to dynamically register and deregister callouts. In the latter
+///   case, only functions registered by functions in the same library as the
+///   callout doing the deregistration can be removed: callouts registered by
+///   other libraries cannot be modified.
+
+class CalloutHandle {
+public:
+
+    /// Typedef to allow abbreviation of iterator specification in methods.
+    /// The std::string is the argument name and the "boost::any" is the
+    /// corresponding value associated with it.
+    typedef std::map<std::string, boost::any> ElementCollection;
+
+    /// Typedef to allow abbreviations in specifications when accessing
+    /// context.  The ElementCollection is the name/value collection for
+    /// a particular context.  The "int" corresponds to the index of an
+    /// associated library - there is a 1:1 correspondence between libraries
+    /// and a name.value collection.
+    ///
+    /// The collection of contexts is stored in a map, as not every library
+    /// will require creation of a context associated with each packet.  In
+    /// addition, the structure is more flexible in that the size does not
+    /// need to be set when the CalloutHandle is constructed.
+    typedef std::map<int, ElementCollection> ContextCollection;
+
+    /// @brief Constructor
+    ///
+    /// Creates the object and calls the callouts on the "context_create"
+    /// hook.
+    ///
+    /// Of the two arguments passed, only the pointer to the callout manager is
+    /// actively used.  The second argument, the pointer to the library manager
+    /// collection, is used for lifetime control: after use, the callout handle
+    /// may contain pointers to memory allocated by the loaded libraries.  The
+    /// used of a shared pointer to the collection of library managers means
+    /// that the libraries that could have allocated memory in a callout handle
+    /// will not be unloaded until all such handles have been destroyed.  This
+    /// issue is discussed in more detail in the documentation for
+    /// isc::hooks::LibraryManager.
+    ///
+    /// @param manager Pointer to the callout manager object.
+    /// @param lmcoll Pointer to the library manager collection.  This has a
+    ///        null default for testing purposes.
+    CalloutHandle(const boost::shared_ptr<CalloutManager>& manager,
+                  const boost::shared_ptr<LibraryManagerCollection>& lmcoll =
+                        boost::shared_ptr<LibraryManagerCollection>());
+
+    /// @brief Destructor
+    ///
+    /// Calls the context_destroy callback to release any per-packet context.
+    /// It also clears stored data to avoid problems during member destruction.
+    ~CalloutHandle();
+
+    /// @brief Set argument
+    ///
+    /// Sets the value of an argument.  The argument is created if it does not
+    /// already exist.
+    ///
+    /// @param name Name of the argument.
+    /// @param value Value to set.  That can be of any data type.
+    template <typename T>
+    void setArgument(const std::string& name, T value) {
+        arguments_[name] = value;
+    }
+
+    /// @brief Get argument
+    ///
+    /// Gets the value of an argument.
+    ///
+    /// @param name Name of the element in the argument list to get.
+    /// @param value [out] Value to set.  The type of "value" is important:
+    ///        it must match the type of the value set.
+    ///
+    /// @throw NoSuchArgument No argument with the given name is present.
+    /// @throw boost::bad_any_cast An argument with the given name is present,
+    ///        but the data type of the value is not the same as the type of
+    ///        the variable provided to receive the value.
+    template <typename T>
+    void getArgument(const std::string& name, T& value) const {
+        ElementCollection::const_iterator element_ptr = arguments_.find(name);
+        if (element_ptr == arguments_.end()) {
+            isc_throw(NoSuchArgument, "unable to find argument with name " <<
+                      name);
+        }
+
+        value = boost::any_cast<T>(element_ptr->second);
+    }
+
+    /// @brief Get argument names
+    ///
+    /// Returns a vector holding the names of arguments in the argument
+    /// vector.
+    ///
+    /// @return Vector of strings reflecting argument names.
+    std::vector<std::string> getArgumentNames() const;
+
+    /// @brief Delete argument
+    ///
+    /// Deletes an argument of the given name.  If an argument of that name
+    /// does not exist, the method is a no-op.
+    ///
+    /// N.B. If the element is a raw pointer, the pointed-to data is NOT deleted
+    /// by this method.
+    ///
+    /// @param name Name of the element in the argument list to set.
+    void deleteArgument(const std::string& name) {
+        static_cast<void>(arguments_.erase(name));
+    }
+
+    /// @brief Delete all arguments
+    ///
+    /// Deletes all arguments associated with this context.
+    ///
+    /// N.B. If any elements are raw pointers, the pointed-to data is NOT
+    /// deleted by this method.
+    void deleteAllArguments() {
+        arguments_.clear();
+    }
+
+    /// @brief Set skip flag
+    ///
+    /// Sets the "skip" variable in the callout handle.  This variable is
+    /// interrogated by the server to see if the remaining callouts associated
+    /// with the current hook should be bypassed.
+    ///
+    /// @param skip New value of the "skip" flag.
+    void setSkip(bool skip) {
+        skip_ = skip;
+    }
+
+    /// @brief Get skip flag
+    ///
+    /// Gets the current value of the "skip" flag.
+    ///
+    /// @return Current value of the skip flag.
+    bool getSkip() const {
+        return (skip_);
+    }
+
+    /// @brief Access current library handle
+    ///
+    /// Returns a reference to the current library handle.  This function is
+    /// only available when called by a callout (which in turn is called
+    /// through the "callCallouts" method), as it is only then that the current
+    /// library index is valid.  A callout uses the library handle to
+    /// dynamically register or deregister callouts.
+    ///
+    /// @return Reference to the library handle.
+    ///
+    /// @throw InvalidIndex thrown if this method is called when the current
+    ///        library index is invalid (typically if it is called outside of
+    ///        the active callout).
+    LibraryHandle& getLibraryHandle() const;
+
+    /// @brief Set context
+    ///
+    /// Sets an element in the context associated with the current library.  If
+    /// an element of the name is already present, it is replaced.
+    ///
+    /// @param name Name of the element in the context to set.
+    /// @param value Value to set.
+    template <typename T>
+    void setContext(const std::string& name, T value) {
+        getContextForLibrary()[name] = value;
+    }
+
+    /// @brief Get context
+    ///
+    /// Gets an element from the context associated with the current library.
+    ///
+    /// @param name Name of the element in the context to get.
+    /// @param value [out] Value to set.  The type of "value" is important:
+    ///        it must match the type of the value set.
+    ///
+    /// @throw NoSuchCalloutContext Thrown if no context element with the name
+    ///        "name" is present.
+    /// @throw boost::bad_any_cast Thrown if the context element is present
+    ///        but the type of the data is not the same as the type of the
+    ///        variable provided to receive its value.
+    template <typename T>
+    void getContext(const std::string& name, T& value) const {
+        const ElementCollection& lib_context = getContextForLibrary();
+
+        ElementCollection::const_iterator element_ptr = lib_context.find(name);
+        if (element_ptr == lib_context.end()) {
+            isc_throw(NoSuchCalloutContext, "unable to find callout context "
+                      "item " << name << " in the context associated with "
+                      "current library");
+        }
+
+        value = boost::any_cast<T>(element_ptr->second);
+    }
+
+    /// @brief Get context names
+    ///
+    /// Returns a vector holding the names of items in the context associated
+    /// with the current library.
+    ///
+    /// @return Vector of strings reflecting the names of items in the callout
+    ///         context associated with the current library.
+    std::vector<std::string> getContextNames() const;
+
+    /// @brief Delete context element
+    ///
+    /// Deletes an item of the given name from the context associated with the
+    /// current library.  If an item  of that name does not exist, the method is
+    /// a no-op.
+    ///
+    /// N.B. If the element is a raw pointer, the pointed-to data is NOT deleted
+    /// by this.
+    ///
+    /// @param name Name of the context item to delete.
+    void deleteContext(const std::string& name) {
+        static_cast<void>(getContextForLibrary().erase(name));
+    }
+
+    /// @brief Delete all context items
+    ///
+    /// Deletes all items from the context associated with the current library.
+    ///
+    /// N.B. If any elements are raw pointers, the pointed-to data is NOT
+    /// deleted by this.
+    void deleteAllContext() {
+        getContextForLibrary().clear();
+    }
+
+    /// @brief Get hook name
+    ///
+    /// Get the name of the hook to which the current callout is attached.
+    /// This can be the null string if the CalloutHandle is being accessed
+    /// outside of the CalloutManager's "callCallouts" method.
+    ///
+    /// @return Name of the current hook or the empty string if none.
+    std::string getHookName() const;
+
+private:
+    /// @brief Check index
+    ///
+    /// Gets the current library index, throwing an exception if it is not set
+    /// or is invalid for the current library collection.
+    ///
+    /// @return Current library index, valid for this library collection.
+    ///
+    /// @throw InvalidIndex current library index is not valid for the library
+    ///        handle collection.
+    int getLibraryIndex() const;
+
+    /// @brief Return reference to context for current library
+    ///
+    /// Called by all context-setting functions, this returns a reference to
+    /// the callout context for the current library, creating a context if it
+    /// does not exist.
+    ///
+    /// @return Reference to the collection of name/value pairs associated
+    ///         with the current library.
+    ///
+    /// @throw InvalidIndex current library index is not valid for the library
+    ///        handle collection.
+    ElementCollection& getContextForLibrary();
+
+    /// @brief Return reference to context for current library (const version)
+    ///
+    /// Called by all context-accessing functions, this a reference to the
+    /// callout context for the current library.  An exception is thrown if
+    /// it does not exist.
+    ///
+    /// @return Reference to the collection of name/value pairs associated
+    ///         with the current library.
+    ///
+    /// @throw NoSuchCalloutContext Thrown if there is no ElementCollection
+    ///        associated with the current library.
+    const ElementCollection& getContextForLibrary() const;
+
+    // Member variables
+
+    /// Pointer to the collection of libraries for which this handle has been
+    /// created.
+    boost::shared_ptr<LibraryManagerCollection> lm_collection_;
+
+    /// Collection of arguments passed to the callouts
+    ElementCollection arguments_;
+
+    /// Context collection - there is one entry per library context.
+    ContextCollection context_collection_;
+
+    /// Callout manager.
+    boost::shared_ptr<CalloutManager> manager_;
+
+    /// "Skip" flag, indicating if the caller should bypass remaining callouts.
+    bool skip_;
+};
+
+/// A shared pointer to a CalloutHandle object.
+typedef boost::shared_ptr<CalloutHandle> CalloutHandlePtr;
+
+} // namespace hooks
+} // namespace isc
+
+
+#endif // CALLOUT_HANDLE_H
diff --git a/src/lib/hooks/callout_manager.cc b/src/lib/hooks/callout_manager.cc
new file mode 100644
index 0000000..5a2126a
--- /dev/null
+++ b/src/lib/hooks/callout_manager.cc
@@ -0,0 +1,257 @@
+// Copyright (C) 2013  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 <hooks/callout_handle.h>
+#include <hooks/callout_manager.h>
+#include <hooks/hooks_log.h>
+#include <hooks/pointer_converter.h>
+
+#include <boost/static_assert.hpp>
+
+#include <algorithm>
+#include <climits>
+#include <functional>
+#include <utility>
+
+using namespace std;
+
+namespace isc {
+namespace hooks {
+
+// Constructor
+CalloutManager::CalloutManager(int num_libraries)
+    : current_hook_(-1), current_library_(-1),
+      hook_vector_(ServerHooks::getServerHooks().getCount()),
+      library_handle_(this), pre_library_handle_(this, 0),
+      post_library_handle_(this, INT_MAX), num_libraries_(num_libraries)
+{
+    if (num_libraries < 0) {
+        isc_throw(isc::BadValue, "number of libraries passed to the "
+                  "CalloutManager must be >= 0");
+    }
+}
+
+// Check that the index of a library is valid.  It can range from 1 - n
+// (n is the number of libraries), 0 (pre-user library callouts), or INT_MAX
+// (post-user library callouts).  It can also be -1 to indicate an invalid
+// value.
+
+void
+CalloutManager::checkLibraryIndex(int library_index) const {
+    if (((library_index >= -1) && (library_index <= num_libraries_)) ||
+        (library_index == INT_MAX)) {
+        return;
+    }
+
+    isc_throw(NoSuchLibrary, "library index " << library_index <<
+              " is not valid for the number of loaded libraries (" <<
+              num_libraries_ << ")");
+}
+
+// Register a callout for the current library.
+
+void
+CalloutManager::registerCallout(const std::string& name, CalloutPtr callout) {
+    // Note the registration.
+    LOG_DEBUG(hooks_logger, HOOKS_DBG_CALLS, HOOKS_CALLOUT_REGISTRATION)
+        .arg(current_library_).arg(name);
+
+    // Sanity check that the current library index is set to a valid value.
+    checkLibraryIndex(current_library_);
+
+    // Get the index associated with this hook (validating the name in the
+    // process).
+    int hook_index = ServerHooks::getServerHooks().getIndex(name);
+
+    // Iterate through the callout vector for the hook from start to end,
+    // looking for the first entry where the library index is greater than
+    // the present index.
+    for (CalloutVector::iterator i = hook_vector_[hook_index].begin();
+         i != hook_vector_[hook_index].end(); ++i) {
+        if (i->first > current_library_) {
+            // Found an element whose library index number is greater than the
+            // current index, so insert the new element ahead of this one.
+            hook_vector_[hook_index].insert(i, make_pair(current_library_,
+                                                         callout));
+            return;
+        }
+    }
+
+    // Reached the end of the vector, so there is no element in the (possibly
+    // empty) set of callouts with a library index greater than the current
+    // library index.  Inset the callout at the end of the list.
+    hook_vector_[hook_index].push_back(make_pair(current_library_, callout));
+}
+
+// Check if callouts are present for a given hook index.
+
+bool
+CalloutManager::calloutsPresent(int hook_index) const {
+    // Validate the hook index.
+    if ((hook_index < 0) || (hook_index >= hook_vector_.size())) {
+        isc_throw(NoSuchHook, "hook index " << hook_index <<
+                  " is not valid for the list of registered hooks");
+    }
+
+    // Valid, so are there any callouts associated with that hook?
+    return (!hook_vector_[hook_index].empty());
+}
+
+// Call all the callouts for a given hook.
+
+void
+CalloutManager::callCallouts(int hook_index, CalloutHandle& callout_handle) {
+
+    // Only initialize and iterate if there are callouts present.  This check
+    // also catches the case of an invalid index.
+    if (calloutsPresent(hook_index)) {
+
+        // Clear the "skip" flag so we don't carry state from a previous call.
+        callout_handle.setSkip(false);
+
+        // Set the current hook index.  This is used should a callout wish to
+        // determine to what hook it is attached.
+        current_hook_ = hook_index;
+
+        // Duplicate the callout vector for this hook and work through that.
+        // This step is needed because we allow dynamic registration and
+        // deregistration of callouts.  If a callout attached to a hook modified
+        // the list of callouts on that hook, the underlying CalloutVector would
+        // change and potentially affect the iteration through that vector.
+        CalloutVector callouts(hook_vector_[hook_index]);
+
+        // Call all the callouts.
+        for (CalloutVector::const_iterator i = callouts.begin();
+             i != callouts.end(); ++i) {
+            // In case the callout tries to register or deregister a callout,
+            // set the current library index to the index associated with the
+            // library that registered the callout being called.
+            current_library_ = i->first;
+
+            // Call the callout
+            try {
+                int status = (*i->second)(callout_handle);
+                if (status == 0) {
+                    LOG_DEBUG(hooks_logger, HOOKS_DBG_EXTENDED_CALLS,
+                              HOOKS_CALLOUT_CALLED).arg(current_library_)
+                        .arg(ServerHooks::getServerHooks()
+                            .getName(current_hook_))
+                        .arg(PointerConverter(i->second).dlsymPtr());
+                } else {
+                    LOG_ERROR(hooks_logger, HOOKS_CALLOUT_ERROR)
+                        .arg(current_library_)
+                        .arg(ServerHooks::getServerHooks()
+                            .getName(current_hook_))
+                        .arg(PointerConverter(i->second).dlsymPtr());
+                }
+            } catch (...) {
+                // Any exception, not just ones based on isc::Exception
+                LOG_ERROR(hooks_logger, HOOKS_CALLOUT_EXCEPTION)
+                    .arg(current_library_)
+                    .arg(ServerHooks::getServerHooks().getName(current_hook_))
+                    .arg(PointerConverter(i->second).dlsymPtr());
+            }
+
+        }
+
+        // Reset the current hook and library indexs to an invalid value to
+        // catch any programming errors.
+        current_hook_ = -1;
+        current_library_ = -1;
+    }
+}
+
+// Deregister a callout registered by the current library on a particular hook.
+
+bool
+CalloutManager::deregisterCallout(const std::string& name, CalloutPtr callout) {
+    // Sanity check that the current library index is set to a valid value.
+    checkLibraryIndex(current_library_);
+
+    // Get the index associated with this hook (validating the name in the
+    // process).
+    int hook_index = ServerHooks::getServerHooks().getIndex(name);
+
+    /// Construct a CalloutEntry matching the current library and the callout
+    /// we want to remove.
+    CalloutEntry target(current_library_, callout);
+
+    /// To decide if any entries were removed, we'll record the initial size
+    /// of the callout vector for the hook, and compare it with the size after
+    /// the removal.
+    size_t initial_size = hook_vector_[hook_index].size();
+
+    // The next bit is standard STL (see "Item 33" in "Effective STL" by
+    // Scott Meyers).
+    //
+    // remove_if reorders the hook vector so that all items not matching
+    // the predicate are at the start of the vector and returns a pointer
+    // to the next element. (In this case, the predicate is that the item
+    // is equal to the value of the passed callout.)  The erase() call
+    // removes everything from that element to the end of the vector, i.e.
+    // all the matching elements.
+    hook_vector_[hook_index].erase(remove_if(hook_vector_[hook_index].begin(),
+                                             hook_vector_[hook_index].end(),
+                                             bind1st(equal_to<CalloutEntry>(),
+                                                     target)),
+                                   hook_vector_[hook_index].end());
+
+    // Return an indication of whether anything was removed.
+    bool removed = initial_size != hook_vector_[hook_index].size();
+    if (removed) {
+        LOG_DEBUG(hooks_logger, HOOKS_DBG_EXTENDED_CALLS,
+                  HOOKS_CALLOUT_DEREGISTERED).arg(current_library_).arg(name);
+    }
+
+    return (removed);
+}
+
+// Deregister all callouts on a given hook.
+
+bool
+CalloutManager::deregisterAllCallouts(const std::string& name) {
+
+    // Get the index associated with this hook (validating the name in the
+    // process).
+    int hook_index = ServerHooks::getServerHooks().getIndex(name);
+
+    /// Construct a CalloutEntry matching the current library (the callout
+    /// pointer is NULL as we are not checking that).
+    CalloutEntry target(current_library_, NULL);
+
+    /// To decide if any entries were removed, we'll record the initial size
+    /// of the callout vector for the hook, and compare it with the size after
+    /// the removal.
+    size_t initial_size = hook_vector_[hook_index].size();
+
+    // Remove all callouts matching this library.
+    hook_vector_[hook_index].erase(remove_if(hook_vector_[hook_index].begin(),
+                                             hook_vector_[hook_index].end(),
+                                             bind1st(CalloutLibraryEqual(),
+                                                     target)),
+                                   hook_vector_[hook_index].end());
+
+    // Return an indication of whether anything was removed.
+    bool removed = initial_size != hook_vector_[hook_index].size();
+    if (removed) {
+        LOG_DEBUG(hooks_logger, HOOKS_DBG_EXTENDED_CALLS,
+                  HOOKS_ALL_CALLOUTS_DEREGISTERED).arg(current_library_)
+                                                .arg(name);
+    }
+
+    return (removed);
+}
+
+} // namespace util
+} // namespace isc
diff --git a/src/lib/hooks/callout_manager.h b/src/lib/hooks/callout_manager.h
new file mode 100644
index 0000000..e1b9e57
--- /dev/null
+++ b/src/lib/hooks/callout_manager.h
@@ -0,0 +1,377 @@
+// Copyright (C) 2013  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 CALLOUT_MANAGER_H
+#define CALLOUT_MANAGER_H
+
+#include <exceptions/exceptions.h>
+#include <hooks/library_handle.h>
+#include <hooks/server_hooks.h>
+
+#include <boost/shared_ptr.hpp>
+
+#include <climits>
+#include <map>
+#include <string>
+
+namespace isc {
+namespace hooks {
+
+/// @brief No such library
+///
+/// Thrown if an attempt is made to set the current library index to a value
+/// that is invalid for the number of loaded libraries.
+class NoSuchLibrary : public Exception {
+public:
+    NoSuchLibrary(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
+/// @brief Callout Manager
+///
+/// This class manages the registration, deregistration and execution of the
+/// library callouts.  It is part of the hooks framework used by the BIND 10
+/// server, and is not for use by user-written code in a hooks library.
+///
+/// In operation, the class needs to know two items of data:
+///
+/// - The list of server hooks, which is used in two ways.  Firstly, when a
+///   callout registers or deregisters a hook, it does so by name: the
+///   @ref isc::util::ServerHooks object supplies the names of registered
+///   hooks.  Secondly, when the callouts associated with a hook are called by
+///   the server, the server supplies the index of the relevant hook: this is
+///   validated by reference to the list of hook.
+///
+/// - The number of loaded libraries.  Each callout registered by a user
+///   library is associated with that library, the callout manager storing both
+///   a pointer to the callout and the index of the library in the list of
+///   loaded libraries.  Callouts are allowed to dynamically register and
+///   deregister callouts in the same library (including themselves): they
+///   cannot affect callouts registered by another library.  When calling a
+///   callout, the callout manager maintains the idea of a "current library
+///   index": if the callout calls one of the callout registration functions
+///   (indirectly via the @ref LibraryHandle object), the registration
+///   functions use the "current library index" in their processing.
+///
+/// These two items of data are supplied when an object of this class is
+/// constructed.  The latter (number of libraries) can be updated after the
+/// class is constructed.  (Such an update is used during library loading where
+/// the CalloutManager has to be constructed before the libraries are loaded,
+/// but one of the libraries subsequently fails to load.)
+///
+/// The library index is important because it determines in what order callouts
+/// on a particular hook are called.  For each hook, the CalloutManager
+/// maintains a vector of callouts ordered by library index.  When a callout
+/// is added to the list, it is added at the end of the callouts associated
+/// with the current library.  To clarify this further, suppose that three
+/// libraries are loaded, A (assigned an index 1), B (assigned an index 2) and
+/// C (assigned an index 3).  Suppose A registers two callouts on a given hook,
+/// A1 and A2 (in that order) B registers B1 and B2 (in that order) and C
+/// registers C1 and C2 (in that order).  Internally, the callouts are stored
+/// in the order A1, A2, B1, B2, C1, and C2: this is also the order in which
+/// the are called.  If B now registers another callout (B3), it is added to
+/// the vector after the list of callouts associated with B: the new order is
+/// therefore A1, A2, B1, B2, B3, C1 and C2.
+///
+/// Indexes range between 1 and n (where n is the number of the libraries
+/// loaded) and are assigned to libraries based on the order the libraries
+/// presented to the hooks framework for loading (something that occurs in the
+/// isc::util::HooksManager) class.  However, two other indexes are recognised,
+/// 0 and INT_MAX.  These are used when the server itself registers callouts -
+/// the server is able to register callouts that get called before any
+/// user-library callouts, and ones that get called after user-library callouts.
+/// In other words, assuming the callouts on a hook are A1, A2, B1, B2, B3, C2,
+/// C2 as before, and that the server registers S1 (to run before the
+/// user-registered callouts) and S2 (to run after them), the callouts are
+/// stored (and executed) in the order S1, A1, A2, B1, B2, B3, C2, C2, S2.  In
+/// summary, the recognised index values are:
+///
+/// - < 0: invalid.
+/// - 0: used for server-registered callouts that are called before
+///   user-registered callouts.
+/// - 1 - n: callouts from user libraries.
+/// - INT_MAX: used for server-registered callouts called after
+///   user-registered callouts.
+///
+/// Note that the callout functions do not access the CalloutManager: instead,
+/// they use a LibraryHandle object.  This contains an internal pointer to
+/// the CalloutManager, but provides a restricted interface.  In that way,
+/// callouts are unable to affect callouts supplied by other libraries.
+
+class CalloutManager {
+private:
+
+    // Private typedefs
+
+    /// Element in the vector of callouts.  The elements in the pair are the
+    /// index of the library from which this callout was registered, and a#
+    /// pointer to the callout itself.
+    typedef std::pair<int, CalloutPtr> CalloutEntry;
+
+    /// An element in the hook vector. Each element is a vector of callouts
+    /// associated with a given hook.
+    typedef std::vector<CalloutEntry> CalloutVector;
+
+public:
+
+    /// @brief Constructor
+    ///
+    /// Initializes member variables, in particular sizing the hook vector
+    /// (the vector of callout vectors) to the appropriate size.
+    ///
+    /// @param num_libraries Number of loaded libraries.
+    ///
+    /// @throw isc::BadValue if the number of libraries is less than 0,
+    CalloutManager(int num_libraries = 0);
+
+    /// @brief Register a callout on a hook for the current library
+    ///
+    /// Registers a callout function for the current library with a given hook
+    /// (the index of the "current library" being given by the current_library_
+    /// member).  The callout is added to the end of the callouts for this
+    /// library that are associated with that hook.
+    ///
+    /// @param name Name of the hook to which the callout is added.
+    /// @param callout Pointer to the callout function to be registered.
+    ///
+    /// @throw NoSuchHook The hook name is unrecognised.
+    /// @throw Unexpected The hook name is valid but an internal data structure
+    ///        is of the wrong size.
+    void registerCallout(const std::string& name, CalloutPtr callout);
+
+    /// @brief De-Register a callout on a hook for the current library
+    ///
+    /// Searches through the functions registered by the the current library
+    /// (the index of the "current library" being given by the current_library_
+    /// member) with the named hook and removes all entries matching the
+    /// callout.
+    ///
+    /// @param name Name of the hook from which the callout is removed.
+    /// @param callout Pointer to the callout function to be removed.
+    ///
+    /// @return true if a one or more callouts were deregistered.
+    ///
+    /// @throw NoSuchHook The hook name is unrecognised.
+    /// @throw Unexpected The hook name is valid but an internal data structure
+    ///        is of the wrong size.
+    bool deregisterCallout(const std::string& name, CalloutPtr callout);
+
+    /// @brief Removes all callouts on a hook for the current library
+    ///
+    /// Removes all callouts associated with a given hook that were registered
+    /// by the current library (the index of the "current library" being given
+    /// by the current_library_ member).
+    ///
+    /// @param name Name of the hook from which the callouts are removed.
+    ///
+    /// @return true if one or more callouts were deregistered.
+    ///
+    /// @throw NoSuchHook Thrown if the hook name is unrecognised.
+    bool deregisterAllCallouts(const std::string& name);
+
+    /// @brief Checks if callouts are present on a hook
+    ///
+    /// Checks all loaded libraries and returns true if at least one callout
+    /// has been registered by any of them for the given hook.
+    ///
+    /// @param hook_index Hook index for which callouts are checked.
+    ///
+    /// @return true if callouts are present, false if not.
+    ///
+    /// @throw NoSuchHook Given index does not correspond to a valid hook.
+    bool calloutsPresent(int hook_index) const;
+
+    /// @brief Calls the callouts for a given hook
+    ///
+    /// Iterates through the libray handles and calls the callouts associated
+    /// with the given hook index.
+    ///
+    /// @note This method invalidates the current library index set with
+    ///       setLibraryIndex().
+    ///
+    /// @param hook_index Index of the hook to call.
+    /// @param callout_handle Reference to the CalloutHandle object for the
+    ///        current object being processed.
+    void callCallouts(int hook_index, CalloutHandle& callout_handle);
+
+    /// @brief Get current hook index
+    ///
+    /// Made available during callCallouts, this is the index of the hook
+    /// on which callouts are being called.
+    int getHookIndex() const {
+        return (current_hook_);
+    }
+
+    /// @brief Get number of libraries
+    ///
+    /// Returns the number of libraries that this CalloutManager is expected
+    /// to serve.  This is the number passed to its constructor.
+    ///
+    /// @return Number of libraries served by this CalloutManager.
+    int getNumLibraries() const {
+        return (num_libraries_);
+    }
+
+    /// @brief Get current library index
+    ///
+    /// Returns the index of the "current" library.  This the index associated
+    /// with the currently executing callout when callCallouts is executing.
+    /// When callCallouts() is not executing (as is the case when the load()
+    /// function in a user-library is called during the library load process),
+    /// the index can be set by setLibraryIndex().
+    ///
+    /// @note The value set by this method is lost after a call to
+    ///       callCallouts.
+    ///
+    /// @return Current library index.
+    int getLibraryIndex() const {
+        return (current_library_);
+    }
+
+    /// @brief Set current library index
+    ///
+    /// Sets the current library index.  This has the following valid values:
+    ///
+    /// - -1: invalidate current index.
+    /// - 0: pre-user library callout.
+    /// - 1 - numlib: user-library callout (where "numlib" is the number of
+    ///   libraries loaded in the system, this figure being passed to this
+    ///   object at construction time).
+    /// - INT_MAX: post-user library callout.
+    ///
+    /// @param library_index New library index.
+    ///
+    /// @throw NoSuchLibrary if the index is not valid.
+    void setLibraryIndex(int library_index) {
+        checkLibraryIndex(library_index);
+        current_library_ = library_index;
+    }
+
+    /// @defgroup calloutManagerLibraryHandles Callout manager library handles
+    ///
+    /// The CalloutManager offers three library handles:
+    ///
+    /// - a "standard" one, used to register and deregister callouts for
+    ///   the library index that is marked as current in the CalloutManager.
+    ///   When a callout is called, it is passed this one.
+    /// - a pre-library callout handle, used by the server to register
+    //    callouts to run prior to user-library callouts.
+    /// - a post-library callout handle, used by the server to register
+    ///   callouts to run after the user-library callouts.
+    //@{
+
+    /// @brief Return library handle
+    ///
+    /// The library handle is available to the user callout via the callout
+    /// handle object.  It provides a cut-down view of the CalloutManager,
+    /// allowing the callout to register and deregister callouts in the
+    /// library of which it is part, whilst denying access to anything that
+    /// may affect other libraries.
+    ///
+    /// @return Reference to library handle for this manager
+    LibraryHandle& getLibraryHandle() {
+        return (library_handle_);
+    }
+
+    /// @brief Return pre-user callouts library handle
+    ///
+    /// The LibraryHandle to affect callouts that will run before the
+    /// user-library callouts.
+    ///
+    /// @return Reference to pre-user library handle for this manager
+    LibraryHandle& getPreLibraryHandle() {
+        return (pre_library_handle_);
+    }
+
+    /// @brief Return post-user callouts library handle
+    ///
+    /// The LibraryHandle to affect callouts that will run before the
+    /// user-library callouts.
+    ///
+    /// @return Reference to post-user library handle for this manager
+    LibraryHandle& getPostLibraryHandle() {
+        return (post_library_handle_);
+    }
+
+    //@}
+
+private:
+    /// @brief Check library index
+    ///
+    /// Ensures that the current library index is valid.  This is called by
+    /// the hook registration functions.
+    ///
+    /// @param library_index Value to check for validity as a library index.
+    ///        Valid values are 0 - numlib+1 and -1: see @ref setLibraryIndex
+    ///        for the meaning of the various values.
+    ///
+    /// @throw NoSuchLibrary Library index is not valid.
+    void checkLibraryIndex(int library_index) const;
+
+    /// @brief Compare two callout entries for library equality
+    ///
+    /// This is used in callout removal code when all callouts on a hook for a
+    /// given library are being removed.  It checks whether two callout entries
+    /// have the same library index.
+    ///
+    /// @param ent1 First callout entry to check
+    /// @param ent2 Second callout entry to check
+    ///
+    /// @return bool true if the library entries are the same
+    class CalloutLibraryEqual :
+        public std::binary_function<CalloutEntry, CalloutEntry, bool> {
+    public:
+        bool operator()(const CalloutEntry& ent1,
+                        const CalloutEntry& ent2) const {
+            return (ent1.first == ent2.first);
+        }
+    };
+
+    /// Current hook.  When a call is made to callCallouts, this holds the
+    /// index of the current hook.  It is set to an invalid value (-1)
+    /// otherwise.
+    int current_hook_;
+
+    /// Current library index.  When a call is made to any of the callout
+    /// registration methods, this variable indicates the index of the user
+    /// library that should be associated with the call.
+    int current_library_;
+
+    /// Vector of callout vectors.  There is one entry in this outer vector for
+    /// each hook. Each element is itself a vector, with one entry for each
+    /// callout registered for that hook.
+    std::vector<CalloutVector> hook_vector_;
+
+    /// LibraryHandle object user by the callout to access the callout
+    /// registration methods on this CalloutManager object.  The object is set
+    /// such that the index of the library associated with any operation is
+    /// whatever is currently set in the CalloutManager.
+    LibraryHandle library_handle_;
+
+    /// LibraryHandle for callouts to be registered as being called before
+    /// the user-registered callouts.
+    LibraryHandle pre_library_handle_;
+
+    /// LibraryHandle for callouts to be registered as being called after
+    /// the user-registered callouts.
+    LibraryHandle post_library_handle_;
+
+    /// Number of libraries.
+    int num_libraries_;
+};
+
+} // namespace util
+} // namespace isc
+
+#endif // CALLOUT_MANAGER_H
diff --git a/src/lib/hooks/hooks.h b/src/lib/hooks/hooks.h
new file mode 100644
index 0000000..a059d79
--- /dev/null
+++ b/src/lib/hooks/hooks.h
@@ -0,0 +1,38 @@
+// Copyright (C) 2013  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 HOOKS_H
+#define HOOKS_H
+
+#include <hooks/callout_handle.h>
+#include <hooks/library_handle.h>
+
+namespace {
+
+// Version 1 of the hooks framework.
+const int BIND10_HOOKS_VERSION = 1;
+
+// Names of the framework functions.
+const char* const LOAD_FUNCTION_NAME = "load";
+const char* const UNLOAD_FUNCTION_NAME = "unload";
+const char* const VERSION_FUNCTION_NAME = "version";
+
+// Typedefs for pointers to the framework functions.
+typedef int (*version_function_ptr)();
+typedef int (*load_function_ptr)(isc::hooks::LibraryHandle&);
+typedef int (*unload_function_ptr)();
+
+} // Anonymous namespace
+
+#endif  // HOOKS_H
diff --git a/src/lib/hooks/hooks_component_developer.dox b/src/lib/hooks/hooks_component_developer.dox
new file mode 100644
index 0000000..edf59a8
--- /dev/null
+++ b/src/lib/hooks/hooks_component_developer.dox
@@ -0,0 +1,483 @@
+// Copyright (C) 2013  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.
+
+/**
+ at page hooksComponentDeveloperGuide Guide to Hooks for the BIND 10 Component Developer
+
+ at section hooksComponentIntroduction Introduction
+
+The hooks framework is a BIND 10 system that simplifies the way that
+users can write code to modify the behavior of BIND 10.  Instead of
+altering the BIND 10 source code, they write functions that are compiled
+and linked into a shared library.  The library is specified in the BIND 10
+configuration database and run time, BIND 10 dynamically loads the library
+into its address space.  At various points in the processing, the component
+"calls out" to functions in the library, passing to them the data is it
+currently working on.  They can examine and modify the data as required.
+
+This guide is aimed at BIND 10 developers who want to write or modify a
+BIND 10 component to use hooks.  It shows how the component should be written
+to load a shared library at run-time and how to call functions in it.
+
+For information about writing a hooks library containing functions called by BIND 10
+during its execution, see the document @ref hooksDevelopersGuide.
+
+ at subsection hooksComponentTerminology Terminology
+
+In the remainder of this guide, the following terminology is used:
+
+- Component - a BIND 10 process, e.g. the authoritative DNS server or the
+DHCPv4 server.
+
+- Hook/Hook Point - used interchageably, this is a point in the code at
+which a call to user-written functions is made. Each hook has a name and
+each hook can have any number (including 0) of user-written functions
+attached to it.
+
+- Callout - a user-written function called by the component at a hook
+point. This is so-named because the component "calls out" to the library
+to execute a user-written function.
+
+- User code/user library - non-BIND 10 code that is compiled into a
+shared library and loaded by BIND 10 into its address space.  Multiple
+user libraries can be loaded at the same time, each containing callouts for
+the same hooks.  The hooks framework calls these libraries one after the
+other. (See the document @ref hooksDevelopersGuide for more details.)
+
+ at subsection hooksComponentLanguages Languages
+
+The core of BIND 10 is written in C++ with some parts in Python.  While it is
+the intention to provide the hooks framework for all languages, the initial
+version is for C++.  All examples in this guide are in that language.
+
+ at section hooksComponentBasicIdeas Basic Ideas
+
+From the point of view of the component author, the basic ideas of the hooks
+framework are quite simple:
+
+- The location of hook points in the code need to be determined.
+
+- Name the hook points and register them.
+
+- At each hook point, the component needs to complete the following steps to
+  execute callouts registered by the user-library:
+  -# copy data into the object used to pass information to the callout.
+  -# call the callout.
+  -# copy data back from the object used to exchange information.
+  -# take action based on information returned.
+
+Of course, to set up the system the libraries need to be loaded in the first
+place.  The component also needs to:
+
+- Define the configuration item that specifies the user libraries for this
+component.
+
+- Handle configuration changes and load/unload the user libraries.
+
+The following sections will describe these tasks in more detail.
+
+ at section hooksComponentDefinition Determing the Hook Points
+
+Before any other action takes place, the location of the hook points
+in the code need to be determined.  This of course depends on the
+component but as a general guideline, hook locations should be chosen
+where a callout is able to obtain useful information from BIND 10 and/or
+affect processing.  Typically this means at the start or end of a major
+step in the processing of a request, at a point where either useful
+information can be passed to a callout and/or the callout can affect
+the processing of the component. The latter is achieved in either or both
+of the following eays:
+
+- Setting the "skip" flag.  This is a boolean flag that the callout can set
+  and is a quick way of passing information back to the component.  It is used
+  to indicate that the component should skip the processing step associated with
+  the hook.  The exact action is up to the component, but is likely to be one
+  of skipping the processing step (probably because the callout has
+  done its own processing for the action) or dropping the current packet
+  and starting on a new request.
+
+- Modifying data passed to it.  The component should be prepared to continue
+  processing with the data returned by the callout.  It is up to the component
+  author whether the data is validated before being used, but doing so will
+  have performance implications.
+
+ at section hooksComponentRegistration Naming and Registering the Hooks Points
+
+Once the location of the hook point has been determined, it should be
+given a name.  This name should be unique amongst all hook points and is
+subject to certain restrictions (see below).
+
+Before the callouts at any hook point are called and any user libraries
+loaded - so typically during component initialization - the component must
+register the names of all the hooks.  The registration is done using
+the static method isc::hooks::HooksManager::registerHook():
+
+ at code
+
+#include <hooks/hooks_manager.h>
+    :
+    int example_index = HooksManager::registerHook("lease_allocate");
+ at endcode
+
+The name of the hook is passed as the sole argument to the registerHook()
+method.  The value returned is the index of that hook point and should
+be retained - it is needed to call the callouts attached to that hook.
+
+Note that a hook only needs to be registered once.  There is no mechanism for
+unregistering a hook and there is no need to do so.
+
+ at subsection hooksComponentAutomaticRegistration Automatic Registration of Hooks
+
+In some components, it may be convenient to set up a single initialization
+function that registers all hooks.  For others, it may be more convenient
+for each module within the component to perform its own initialization.
+Since the isc::hooks::HooksManager object is a singleton and is created when first
+accessed, a useful trick is to automatically register the hooks when
+the module is loaded.
+
+This technique involves declaring an object outside of any execution
+unit in the module.  When the module is loaded, the object's constructor
+is run.  By placing the hook registration calls in the constructor,
+the hooks in the module are defined at load time, before any function in
+the module is run.  The code for such an initialization sequence would
+be similar to:
+
+ at code
+#include <hooks/hooks_manager.h>
+
+namespace {
+
+// Declare structure to perform initialization and store the hook indexes.
+//
+struct MyHooks {
+    int pkt_rcvd;   // Index of "packet received" hook
+    int pkt_sent;   // Index of "packet sent" hook
+
+    // Constructor
+    MyHooks() {
+        pkt_rcvd = HooksManager::registerHook("pkt_rcvd");
+        pkt_sent = HooksManager::registerHook("pkt_sent");
+    }
+};
+
+// Declare a "MyHooks" object.  As this is outside any function or method, it
+// will be instantiated (and the constructor run) when the module is loaded.
+// As a result, the hook indexes will be defined before any method in this
+// module is called.
+MyHooks my_hooks;
+
+} // Anonymous namespace
+
+void Someclass::someFunction() {
+    :
+    // Check if any callouts are defined on the pkt_rcvd hook.
+    if (HooksManager::calloutPresent(my_hooks.pkt_rcvd)) {
+          :
+    }
+    :
+}
+ at endcode
+
+ at subsection hooksComponentHookNames Hook Names
+
+Hook names are strings and in principle, any string can be used as the
+name of a hook, even one containing spaces and non-printable characters.
+However, the following guidelines should be observed:
+
+- The names <b>context_create</b> and <b>context_destroy</b> are reserved to
+the hooks system and are automatically registered: an attempt to register
+one of these will lead to a isc::hooks::DuplicateHook exception being thrown.
+
+- The hook name should be a valid "C" function name.  If a user gives a
+callout the same name as one of the hooks, the hooks framework will
+automatically load that callout and attach it to the hook: the user does not
+have to explicitly register it.
+
+- The hook name should not conflict with the name of a function in any of
+the system libraries (e.g. naming a hook "sqrt" could lead to the
+square-root function in the system's maths library being attached to the hook
+as a callout).
+
+- Although hook names can be in any case (including mixed case), the BIND 10
+convention is that they are lower-case.
+
+ at section hooksComponentCallingCallouts Calling Callouts on a Hook
+
+ at subsection hooksComponentArgument The Callout Handle
+
+Before describing how to call user code at a hook point, we must first consider
+how to pass data to it.
+
+Each user callout has the signature:
+ at code
+int callout_name(isc::hooks::CalloutHandle& handle);
+ at endcode
+
+The isc::hooks::CalloutHandle object is the object used to pass data to
+and from the callout.  This holds the data as a set of name/value pairs,
+each pair being considered an argument to the callout.  If there are
+multiple callouts attached to a hook, the CalloutHandle is passed to
+each in turn. Should a callout modify an argument, the updated data is
+passed subsequent callouts (each of which could also modify it) before
+being returned to the component.
+
+Two methods are provided to get and set the arguments passed to
+the callout called (naturally enough) getArgument and SetArgument.
+Their usage is illustrated by the following code snippets.
+
+ at code
+    int count = 10;
+    boost::shared_ptr<Pkt4> pktptr = ... // Set to appropriate value
+
+    // Assume that "handle_ptr" has been created and is a pointer to a
+    // CalloutHandle.
+    handle_ptr->setArgument("data_count", count);
+    handle_ptr->setArgument("inpacket", pktptr);
+
+    // Call the hook code.  lease_assigned_index is the value returned from
+    // HooksManager::registerHook() when the hook was registered.
+    HooksManager::callCallouts(lease_assigned_index, *handle_ptr);
+
+    // Retrieve the modified values
+    handle_ptr->getArgument("data_count", count);
+    handle_ptr->getArgument("inpacket", pktptr);
+ at endcode
+
+As can be seen "getArgument" is used to retrieve data from the
+CalloutHandle, and "setArgument" used to put data into it.  If a callout
+wishes to alter data and pass it back to the component, it should retrieve
+the data with getArgument, modify it, and call setArgument to send
+it back.
+
+There are a couple points to be aware of:
+
+- The data type of the variable in the call to getArgument must
+match the data type of the variable passed to the corresponding
+setArgument <B>exactly</B>: using what would normally be considered
+to be a "compatible" type is not enough.  For example, if the callout
+passed an argument back to the component as an "int" and the component
+attempted to retrieve it as a "long", an exception would be thrown even
+though any value that can be stored in an "int" will fit into a "long".
+This restriction also applies the "const" attribute but only as applied to
+data pointed to by pointers, e.g. if an argument is defined as a "char*",
+an exception will be thrown if an attempt is made to retrieve it into
+a variable of type "const char*".  (However, if an argument is set as a
+"const int", it can be retrieved into an "int".)  The documentation of
+a hook point should detail the exact data type of each argument.
+
+- If a pointer to an object is passed to a callout (either a "raw"
+pointer, or a boost smart pointer (as in the example above), and the
+underlying object is altered through that pointer, the change will be
+reflected in the component even if the callout makes no call to setArgument.
+This can be avoided by passing a pointer to a "const" object.
+
+ at subsection hooksComponentSkipFlag The Skip Flag
+
+Although information is passed back to the component from callouts through
+CalloutHandle arguments, a common action for callouts is to inform the component
+that its flow of control should be altered.  For example:
+
+- In the DHCP servers, there is a hook at the point at which a lease is
+  about to be assigned.  Callouts attached to this hooks may handle the
+  lease assignment in special cases, in which case they set the skip flag
+  to indicate that the server should not perform lease assignment in this
+  case.
+- A server may define a hook just after a packet is received.  A callout
+  attached to the hook might inspect the source address and compare it
+  against a blacklist.  If the address is on the list, the callout could set
+  the skip flag to indicate to the server that the packet should be dropped.
+
+For ease of processing, the CalloutHandle contains
+two methods, isc::hooks::CalloutHandle::getSkip() and
+isc::hooks::CalloutHandle::setSkip().  It is only meaningful for the
+component to use the "get" method.  The skip flag is cleared by the hooks
+framework when the component requests that callouts be executed, so any
+value set by the component is lost.  Callouts can both inspect the flag (it
+might have been set by callouts earlier in the callout list for the hook)
+and set it.  Note that the setting of the flag by a callout does not
+prevent callouts later in the list from being called: the skip flag is
+just a boolean flag - the only significance comes from its interpretation
+by the component.
+
+An example of use could be:
+ at code
+// Set up arguments for DHCP lease assignment.
+handle->setArgument("query", query);
+handle->setArgument("response", response);
+HooksManager::callCallouts(lease_hook_index, *handle_ptr);
+if (! handle_ptr->getSkip()) {
+    // Skip flag not set, do the address allocation
+    :
+}
+ at endcode
+
+
+ at subsection hooksComponentGettingHandle Getting the Callout Handle
+
+The CalloutHandle object is linked to the loaded libraries
+for lifetime reasons (described below).  Components
+should retrieve a isc::hooks::CalloutHandle using
+isc::hooks::HooksManager::createCalloutHandle():
+ at code
+    CalloutHandlePtr handle_ptr = HooksManager::createCalloutHandle();
+ at endcode
+(isc::hooks::CalloutHandlePtr is a typedef for a Boost shared pointer to a
+CalloutHandle.)  The CalloutHandle so retrieved may be used for as
+long as the libraries are loaded.
+
+The handle is deleted by resetting the pointer:
+ at code
+    handle_ptr.reset();
+ at endcode
+... or by letting the handle pointer go out of scope.  The actual deletion
+occurs when the CallHandle's reference count goes to zero. (The
+current version of the hooks framework does not maintain any other
+pointer to the returned CalloutHandle, so it gets destroyed when the
+shared pointer to it is cleared or destroyed.  However, this may change
+in a future version.)
+
+ at subsection hooksComponentCallingCallout Calling the Callout
+
+Calling the callout is a simple matter of executing the
+isc::hooks::HooksManager::callCallouts() method for the hook index in
+question.  For example, with the hook index pkt_sent defined as above,
+the hook can be executed by:
+ at code
+    HooksManager::callCallouts(pkt_sent, *handle_ptr);
+ at endcode
+... where "*handle_ptr" is a reference (note: not a pointer) to the
+isc::hooks::CalloutHandle object holding the arguments.  No status code
+is returned.  If a component needs to get data returned (other than that
+provided by the "skip" flag), it should define an argument through which
+the callout can do so.
+
+ at subsubsection hooksComponentConditionalCallout Conditionally Calling Hook Callouts
+
+Most hooks in a component will not have callouts attached to them. To
+avoid the overhead of setting up arguments in the CalloutHandle, a
+component can check for callouts before doing that processing using
+isc::hooks::HooksManager::calloutsPresent().  Taking the index of a
+hook as its sole argument, the function returns true if there are any
+callouts attached to the hook and false otherwise.
+
+With this check, the code in the component for calling a hook would look
+something like:
+ at code
+if (HooksManager::calloutsPresent(lease_hook_index)) {
+    // Set up arguments for lease assignment
+    handle->setArgument("query", query);
+    handle->setArgument("response", response);
+    HooksManager::callCallouts(lease_hook_index, *handle);
+    if (! handle->getSkip()) {
+        // Skip flag not set, do the address allocation
+        :
+    }
+}
+ at endcode
+
+ at section hooksComponentLoadLibraries Loading the User Libraries
+
+Once hooks are defined, all the hooks code described above will
+work, even if no libraries are loaded (and even if the library
+loading method is not called).  The CalloutHandle returned by
+isc::hooks::HooksManager::createCalloutHandle() will be valid,
+isc::hooks::HooksManager::calloutsPresent() will return false for every
+index, and isc::hooks::HooksManager::callCallouts() will be a no-op.
+
+However, if user libraries are specified in the BIND 10 configuration,
+the component should load them.  (Note the term "libraries": the hooks
+framework allows multiple user libraries to be loaded.) This should take
+place after the component's configuration has been read, and is achieved
+by the isc::hooks::HooksManager::loadLibraries() method.  The method is
+passed a vector of strings, each giving the full file specification of
+a user library:
+ at code
+    std::vector<std::string> libraries = ... // Get array of libraries
+    bool success = HooksManager::loadLibraries(libraries);
+ at endcode
+loadLibraries() returns a boolean status which is true if all libraries
+loaded successfully or false if one or more failed to load.  Appropriate
+error messages will have been logged in the latter case, the status
+being more to allow the developer to decide whether the execution
+should proceed in such circumstances.
+
+If loadLibraries() is called a second or subsequent time (as a result
+of a reconfiguration), all existing libraries are unloaded and a new
+set loaded.  Libraries can be explicitly unloaded either by calling
+isc::hooks::HooksManager::unloadLibraries() or by calling
+loadLibraries() with an empty vector as an argument.
+
+ at subsection hooksComponentUnloadIssues Unload and Reload Issues
+
+Unloading a shared library works by unmapping the part of the process's
+virtual address space in which the library lies.  This may lead to
+problems if there are still references to that address space elsewhere
+in the process.
+
+In many operating systems, heap storage allowed by a shared library will
+lie in the virtual address allocated to the library.  This has implications
+in the hooks framework because:
+
+- Argument information stored in a CalloutHandle by a callout in a library
+may lie in the library's address space.
+- Data modified in objects passed as arguments may lie in the address
+space.  For example, it is common for a DHCP callout to add "options"
+to a packet: the memory allocated for those options will most likely
+lie in library address space.
+
+The problem really arises because of the extensive use by BIND 10 of boost
+smart pointers.  When the pointer is destroyed, the pointed-to memory is
+deallocated.  If the pointer points to address space that is unmapped because
+a library has been unloaded, the deletion causes a segmentation fault.
+
+The hooks framework addresses the issue for CalloutHandles by keeping in
+that object a shared pointer to the object controlling library unloading.
+Although a library can be unloaded at any time, it is only when all
+CalloutHandles that could possibly reference address space in the library
+have been deleted that the library will actually be unloaded and the
+address space unmapped.
+
+The hooks framework cannot solve the second issue as the objects in
+question are under control of the component.  It is up to the component
+developer to ensure that all such objects have been destroyed before
+libraries are reloaded.  In extreme cases this may mean the component
+suspending all processing of incoming requests until all currently
+executing requests have completed and data object destroyed, reloading
+the libraries, then resuming processing.
+
+ at section hooksComponentCallouts Component-Defined Callouts
+
+Previous sections have discussed callout registration by user libraries.
+It is possible for a component to register its own functions (i.e. within
+its own address space) as hook callouts.  These functions are called
+in eactly the same way as user callouts, being passed their arguments
+though a CalloutHandle object.  (Guidelines for writing callouts can be
+found in @ref hooksDevelopersGuide.)
+
+A component can associate with a hook callouts that run either before
+user-registered callouts or after them.  Registration is done via a
+isc::hooks::LibraryHandle object, a reference to one being obtained
+through the methods isc::hooks::HooksManager::preCalloutLibraryHandle()
+(for a handle to register callouts to run before the user library
+callouts) or isc::hooks::HooksManager::postCalloutLibraryHandle() (for
+a handle to register callouts to run after the user callouts).  Use of
+the LibraryHandle to register and deregister callouts is described in
+ at ref hooksLibraryHandle.
+
+Finally, it should be noted that callouts registered in this way only
+remain registered until the next call to isc::hooks::loadLibraries().
+It is up to the component to re-register the callouts after this
+method has been called.
+
+*/
diff --git a/src/lib/hooks/hooks_log.cc b/src/lib/hooks/hooks_log.cc
new file mode 100644
index 0000000..360394c
--- /dev/null
+++ b/src/lib/hooks/hooks_log.cc
@@ -0,0 +1,26 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+/// Defines the logger used by the NSAS
+
+#include "hooks/hooks_log.h"
+
+namespace isc {
+namespace hooks {
+
+isc::log::Logger hooks_logger("hooks");
+
+} // namespace hooks
+} // namespace isc
+
diff --git a/src/lib/hooks/hooks_log.h b/src/lib/hooks/hooks_log.h
new file mode 100644
index 0000000..92d429a
--- /dev/null
+++ b/src/lib/hooks/hooks_log.h
@@ -0,0 +1,50 @@
+// Copyright (C) 2013  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 HOOKS_LOG_H
+#define HOOKS_LOG_H
+
+#include <log/macros.h>
+#include <hooks/hooks_messages.h>
+
+namespace isc {
+namespace hooks {
+
+/// @brief Hooks debug Logging levels
+///
+/// Defines the levels used to output debug messages in the Hooks framework.
+/// Note that higher numbers equate to more verbose (and detailed) output.
+
+// The first level traces normal operations,
+const int HOOKS_DBG_TRACE = DBGLVL_TRACE_BASIC;
+
+// The next level traces each call to hook code.
+const int HOOKS_DBG_CALLS = DBGLVL_TRACE_BASIC_DATA;
+
+// Additional information on the calls.  Report each call to a callout (even
+// if there are multiple callouts on a hook) and each status return.
+const int HOOKS_DBG_EXTENDED_CALLS = DBGLVL_TRACE_DETAIL_DATA;
+
+
+/// @brief Hooks Logger
+///
+/// Define the logger used to log messages.  We could define it in multiple
+/// modules, but defining in a single module and linking to it saves time and
+/// space.
+extern isc::log::Logger hooks_logger;
+
+} // namespace hooks
+} // namespace isc
+
+#endif // HOOKS_LOG_H
diff --git a/src/lib/hooks/hooks_manager.cc b/src/lib/hooks/hooks_manager.cc
new file mode 100644
index 0000000..117db61
--- /dev/null
+++ b/src/lib/hooks/hooks_manager.cc
@@ -0,0 +1,200 @@
+// Copyright (C) 2013  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 <hooks/callout_handle.h>
+#include <hooks/callout_manager.h>
+#include <hooks/callout_manager.h>
+#include <hooks/library_handle.h>
+#include <hooks/library_manager_collection.h>
+#include <hooks/hooks_manager.h>
+#include <hooks/server_hooks.h>
+
+#include <boost/shared_ptr.hpp>
+
+#include <string>
+#include <vector>
+
+using namespace std;
+
+namespace isc {
+namespace hooks {
+
+// Constructor
+
+HooksManager::HooksManager() {
+}
+
+// Return reference to singleton hooks manager.
+
+HooksManager&
+HooksManager::getHooksManager() {
+    static HooksManager manager;
+    return (manager);
+}
+
+// Are callouts present?
+
+bool
+HooksManager::calloutsPresentInternal(int index) {
+    conditionallyInitialize();
+    return (callout_manager_->calloutsPresent(index));
+}
+
+bool
+HooksManager::calloutsPresent(int index) {
+    return (getHooksManager().calloutsPresentInternal(index));
+}
+
+// Call the callouts
+
+void
+HooksManager::callCalloutsInternal(int index, CalloutHandle& handle) {
+    conditionallyInitialize();
+    return (callout_manager_->callCallouts(index, handle));
+}
+
+void
+HooksManager::callCallouts(int index, CalloutHandle& handle) {
+    return (getHooksManager().callCalloutsInternal(index, handle));
+}
+
+// Load the libraries.  This will delete the previously-loaded libraries
+// (if present) and load new ones.
+
+bool
+HooksManager::loadLibrariesInternal(const std::vector<std::string>& libraries) {
+    // Unload current set of libraries (if any are loaded).
+    unloadLibrariesInternal();
+
+    // Create the library manager and load the libraries.
+    lm_collection_.reset(new LibraryManagerCollection(libraries));
+    bool status = lm_collection_->loadLibraries();
+
+    if (status) {
+        // ... and obtain the callout manager for them if successful.
+        callout_manager_ = lm_collection_->getCalloutManager();
+    } else {
+        // Unable to load libraries, reset to state before this function was
+        // called.
+        lm_collection_.reset();
+        callout_manager_.reset();
+    }
+
+    return (status);
+}
+
+bool
+HooksManager::loadLibraries(const std::vector<std::string>& libraries) {
+    return (getHooksManager().loadLibrariesInternal(libraries));
+}
+
+// Unload the libraries.  This just deletes all internal objects which will
+// cause the libraries to be unloaded.
+
+void
+HooksManager::unloadLibrariesInternal() {
+    // The order of deletion does not matter here, as each library manager
+    // holds its own pointer to the callout manager.  However, we may as
+    // well delete the library managers first: if there are no other references
+    // to the callout manager, the second statement will delete it, which may
+    // ease debugging.
+    lm_collection_.reset();
+    callout_manager_.reset();
+}
+
+void HooksManager::unloadLibraries() {
+    getHooksManager().unloadLibrariesInternal();
+}
+
+// Create a callout handle
+
+boost::shared_ptr<CalloutHandle>
+HooksManager::createCalloutHandleInternal() {
+    conditionallyInitialize();
+    return (boost::shared_ptr<CalloutHandle>(
+            new CalloutHandle(callout_manager_, lm_collection_)));
+}
+
+boost::shared_ptr<CalloutHandle>
+HooksManager::createCalloutHandle() {
+    return (getHooksManager().createCalloutHandleInternal());
+}
+
+// Get the list of the names of loaded libraries.
+
+std::vector<std::string>
+HooksManager::getLibraryNamesInternal() const {
+    return (lm_collection_ ? lm_collection_->getLibraryNames()
+                           : std::vector<std::string>());
+}
+
+std::vector<std::string>
+HooksManager::getLibraryNames() {
+    return (getHooksManager().getLibraryNamesInternal());
+}
+
+// Perform conditional initialization if nothing is loaded.
+
+void
+HooksManager::performConditionalInitialization() {
+
+    // Nothing present, so create the collection with any empty set of
+    // libraries, and get the CalloutManager.
+    vector<string> libraries;
+    lm_collection_.reset(new LibraryManagerCollection(libraries));
+    lm_collection_->loadLibraries();
+
+    callout_manager_ = lm_collection_->getCalloutManager();
+}
+
+// Shell around ServerHooks::registerHook()
+
+int
+HooksManager::registerHook(const std::string& name) {
+    return (ServerHooks::getServerHooks().registerHook(name));
+}
+
+// Return pre- and post- library handles.
+
+isc::hooks::LibraryHandle&
+HooksManager::preCalloutsLibraryHandleInternal() {
+    conditionallyInitialize();
+    return (callout_manager_->getPreLibraryHandle());
+}
+
+isc::hooks::LibraryHandle&
+HooksManager::preCalloutsLibraryHandle() {
+    return (getHooksManager().preCalloutsLibraryHandleInternal());
+}
+
+isc::hooks::LibraryHandle&
+HooksManager::postCalloutsLibraryHandleInternal() {
+    conditionallyInitialize();
+    return (callout_manager_->getPostLibraryHandle());
+}
+
+isc::hooks::LibraryHandle&
+HooksManager::postCalloutsLibraryHandle() {
+    return (getHooksManager().postCalloutsLibraryHandleInternal());
+}
+
+// Validate libraries
+
+std::vector<std::string>
+HooksManager::validateLibraries(const std::vector<std::string>& libraries) {
+    return (LibraryManagerCollection::validateLibraries(libraries));
+}
+
+} // namespace util
+} // namespace isc
diff --git a/src/lib/hooks/hooks_manager.h b/src/lib/hooks/hooks_manager.h
new file mode 100644
index 0000000..3abb68c
--- /dev/null
+++ b/src/lib/hooks/hooks_manager.h
@@ -0,0 +1,323 @@
+// Copyright (C) 2013  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 HOOKS_MANAGER_H
+#define HOOKS_MANAGER_H
+
+#include <hooks/server_hooks.h>
+
+#include <boost/noncopyable.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <string>
+#include <vector>
+
+namespace isc {
+namespace hooks {
+
+// Forward declarations
+class CalloutHandle;
+class CalloutManager;
+class LibraryHandle;
+class LibraryManagerCollection;
+
+/// @brief Hooks Manager
+///
+/// This is the overall manager of the hooks framework and is the main class
+/// used by a BIND 10 module when handling hooks.  It is responsible for the
+/// loading and unloading of user libraries, and for calling the callouts on
+/// each hook point.
+///
+/// The class is a singleton, the single instance of the object being accessed
+/// through the static getHooksManager() method.
+
+class HooksManager : boost::noncopyable {
+public:
+    /// @brief Get singleton hooks manager
+    ///
+    /// @return Reference to the singleton hooks manager.
+    static HooksManager& getHooksManager();
+
+    /// @brief Load and reload libraries
+    ///
+    /// Loads the list of libraries into the server address space.  For each
+    /// library, the "standard" functions (ones with the same names as the
+    /// hook points) are configured and the libraries' "load" function
+    /// called.
+    ///
+    /// If libraries are already loaded, they are unloaded and the new
+    /// libraries loaded.
+    ///
+    /// If any library fails to load, an error message will be logged.  The
+    /// remaining libraries will be loaded if possible.
+    ///
+    /// @param libraries List of libraries to be loaded.  The order is
+    ///        important, as it determines the order that callouts on the same
+    ///        hook will be called.
+    ///
+    /// @return true if all libraries loaded without a problem, false if one or
+    ///        more libraries failed to load.  In the latter case, message will
+    ///        be logged that give the reason.
+    static bool loadLibraries(const std::vector<std::string>& libraries);
+
+    /// @brief Unload libraries
+    ///
+    /// Unloads the loaded libraries and leaves the hooks subsystem in the
+    /// state it was after construction but before loadLibraries() is called.
+    ///
+    /// @note: This method should be used with caution - see the notes for
+    ///        the class LibraryManager for pitfalls.  In general, a server
+    ///        should not call this method: library unloading will automatically
+    ///        take place when new libraries are loaded, and when appropriate
+    ///        objects are destroyed.
+    ///
+    /// @return true if all libraries unloaded successfully, false on an error.
+    ///         In the latter case, an error message will have been output.
+    static void unloadLibraries();
+
+    /// @brief Reload libraries
+    ///
+    /// Reloads the current libraries.  This causes all the libraries' "unload"
+    /// functions to run, the libraries to be closed, reopened, and all the
+    /// "load" functions run.
+    static void reloadLibraries();
+
+    /// @brief Are callouts present?
+    ///
+    /// Checks loaded libraries and returns true if at lease one callout
+    /// has been registered by them for the given hook.
+    ///
+    /// @param index Hooks index for which callouts are checked.
+    ///
+    /// @return true if callouts are present, false if not.
+    /// @throw NoSuchHook Given index does not correspond to a valid hook.
+    static bool calloutsPresent(int index);
+
+    /// @brief Calls the callouts for a given hook
+    ///
+    /// Iterates through the libray handles and calls the callouts associated
+    /// with the given hook index.
+    ///
+    /// @note This method invalidates the current library index set with
+    ///       setLibraryIndex().
+    ///
+    /// @param index Index of the hook to call.
+    /// @param handle Reference to the CalloutHandle object for the current
+    ///        object being processed.
+    static void callCallouts(int index, CalloutHandle& handle);
+
+    /// @brief Return pre-callouts library handle
+    ///
+    /// Returns a library handle that can be used by the server to register
+    /// callouts on a hook that are called _before_ any callouts belonging
+    /// to a library.
+    ///
+    /// @note Both the reference returned and the callouts registered with
+    ///       this handle only remain valid until the next loadLibraries() or
+    ///       unloadLibraries() call.  If the callouts are to remain registered
+    ///       after this time, a new handle will need to be obtained and the
+    ///       callouts re-registered.
+    ///
+    /// @return Reference to library handle associated with pre-library callout
+    ///         registration.
+    static LibraryHandle& preCalloutsLibraryHandle();
+
+    /// @brief Return post-callouts library handle
+    ///
+    /// Returns a library handle that can be used by the server to register
+    /// callouts on a hook that are called _after any callouts belonging
+    /// to a library.
+    ///
+    /// @note Both the reference returned and the callouts registered with
+    ///       this handle only remain valid until the next loadLibraries() or
+    ///       unloadLibraries() call.  If the callouts are to remain registered
+    ///       after this time, a new handle will need to be obtained and the
+    ///       callouts re-registered.
+    ///
+    /// @return Reference to library handle associated with post-library callout
+    ///         registration.
+    static LibraryHandle& postCalloutsLibraryHandle();
+
+    /// @brief Return callout handle
+    ///
+    /// Returns a callout handle to be associated with a request passed round
+    /// the system.
+    ///
+    /// @note This handle is valid only after a loadLibraries() call and then
+    ///       only up to the next loadLibraries() call.
+    ///
+    /// @return Shared pointer to a CalloutHandle object.
+    static boost::shared_ptr<CalloutHandle> createCalloutHandle();
+
+    /// @brief Register Hook
+    ///
+    /// This is just a convenience shell around the ServerHooks::registerHook()
+    /// method.  It - along with the definitions of the two hook indexes for
+    /// the context_create and context_destroy methods - means that server
+    /// authors only need to deal with HooksManager and CalloutHandle, and not
+    /// include any other hooks framework classes.
+    ///
+    /// @param name Name of the hook
+    ///
+    /// @return Index of the hook, to be used in subsequent hook-related calls.
+    ///         This will be greater than or equal to zero (so allowing a
+    ///         negative value to indicate an invalid index).
+    ///
+    /// @throws DuplicateHook A hook with the same name has already been
+    ///         registered.
+    static int registerHook(const std::string& name);
+
+    /// @brief Return list of loaded libraries
+    ///
+    /// Returns the names of the loaded libraries.
+    ///
+    /// @return List of loaded library names.
+    static std::vector<std::string> getLibraryNames();
+
+    /// @brief Validate library list
+    ///
+    /// For each library passed to it, checks that the library can be opened
+    /// and that the "version" function is present and gives the right answer.
+    /// Each library is closed afterwards.
+    ///
+    /// This is used during the configuration parsing - when the list of hooks
+    /// libraries is changed, each of the new libraries is checked before the
+    /// change is committed.
+    ///
+    /// @param List of libraries to be validated.
+    ///
+    /// @return An empty vector if all libraries validated.  Otherwise it
+    ///         holds the names of the libraries that failed validation.
+    static std::vector<std::string> validateLibraries(
+                       const std::vector<std::string>& libraries);
+
+    /// Index numbers for pre-defined hooks.
+    static const int CONTEXT_CREATE = ServerHooks::CONTEXT_CREATE;
+    static const int CONTEXT_DESTROY = ServerHooks::CONTEXT_DESTROY;
+
+private:
+
+    /// @brief Constructor
+    ///
+    /// This is private as the object is a singleton and can only be addessed
+    /// through the getHooksManager() static method.
+    HooksManager();
+
+    //@{
+    /// The following methods correspond to similarly-named static methods,
+    /// but actually do the work on the singleton instance of the HooksManager.
+    /// See the descriptions of the static methods for more details.
+
+    /// @brief Validate library list
+    ///
+    /// @param List of libraries to be validated.
+    ///
+    /// @return An empty string if all libraries validated.  Otherwise it is
+    ///         the name of the first library that failed validation.  The
+    ///         configuration code can return this to bindctl as an indication
+    ///         of the problem.
+    std::string validateLibrariesInternal(
+                       const std::vector<std::string>& libraries) const;
+
+    /// @brief Load and reload libraries
+    ///
+    /// @param libraries List of libraries to be loaded.  The order is
+    ///        important, as it determines the order that callouts on the same
+    ///        hook will be called.
+    ///
+    /// @return true if all libraries loaded without a problem, false if one or
+    ///        more libraries failed to load.  In the latter case, message will
+    ///        be logged that give the reason.
+    bool loadLibrariesInternal(const std::vector<std::string>& libraries);
+
+    /// @brief Unload libraries
+    void unloadLibrariesInternal();
+
+    /// @brief Are callouts present?
+    ///
+    /// @param index Hooks index for which callouts are checked.
+    ///
+    /// @return true if callouts are present, false if not.
+    /// @throw NoSuchHook Given index does not correspond to a valid hook.
+    bool calloutsPresentInternal(int index);
+
+    /// @brief Calls the callouts for a given hook
+    ///
+    /// @param index Index of the hook to call.
+    /// @param handle Reference to the CalloutHandle object for the current
+    ///        object being processed.
+    void callCalloutsInternal(int index, CalloutHandle& handle);
+
+    /// @brief Return callout handle
+    ///
+    /// @return Shared pointer to a CalloutHandle object.
+    boost::shared_ptr<CalloutHandle> createCalloutHandleInternal();
+
+    /// @brief Return pre-callouts library handle
+    ///
+    /// @return Reference to library handle associated with pre-library callout
+    ///         registration.
+    LibraryHandle& preCalloutsLibraryHandleInternal();
+
+    /// @brief Return post-callouts library handle
+    ///
+    /// @return Reference to library handle associated with post-library callout
+    ///         registration.
+    LibraryHandle& postCalloutsLibraryHandleInternal();
+
+    /// @brief Return list of loaded libraries
+    ///
+    /// @return List of loaded library names.
+    std::vector<std::string> getLibraryNamesInternal() const;
+
+    //@}
+
+    /// @brief Initialization to No Libraries
+    ///
+    /// Initializes the hooks manager with an "empty set" of libraries.  This
+    /// method is called if conditionallyInitialize() determines that such
+    /// initialization is needed.
+    void performConditionalInitialization();
+
+    /// @brief Conditional initialization of the  hooks manager
+    ///
+    /// loadLibraries() performs the initialization of the HooksManager,
+    /// setting up the internal structures and loading libraries.  However,
+    /// in some cases, server authors may not do that.  This method is called
+    /// whenever any hooks execution function is invoked (checking callouts,
+    /// calling callouts or returning a callout handle).  If the HooksManager
+    /// is unitialised, it will initialize it with an "empty set" of libraries.
+    ///
+    /// For speed, the test of whether initialization is required is done
+    /// in-line here.  The actual initialization is performed in
+    /// performConditionalInitialization().
+    void conditionallyInitialize() {
+        if (!lm_collection_) {
+            performConditionalInitialization();
+        }
+    }
+
+    // Members
+
+    /// Set of library managers.
+    boost::shared_ptr<LibraryManagerCollection> lm_collection_;
+
+    /// Callout manager for the set of library managers.
+    boost::shared_ptr<CalloutManager> callout_manager_;
+};
+
+} // namespace util
+} // namespace hooks
+
+#endif // HOOKS_MANAGER_H
diff --git a/src/lib/hooks/hooks_messages.mes b/src/lib/hooks/hooks_messages.mes
new file mode 100644
index 0000000..68b6e3c
--- /dev/null
+++ b/src/lib/hooks/hooks_messages.mes
@@ -0,0 +1,162 @@
+# Copyright (C) 2013  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.
+
+$NAMESPACE isc::hooks
+
+% HOOKS_ALL_CALLOUTS_DEREGISTERED hook library at index %1 removed all callouts on hook %2
+A debug message issued when all callouts on the specified hook registered
+by the library with the given index were removed.  This is similar to
+the HOOKS_CALLOUTS_REMOVED message (and the two are likely to be seen
+together), but is issued at a lower-level in the hook framework.
+
+% HOOKS_CALLOUTS_REMOVED callouts removed from hook %1 for library %2
+This is a debug message issued during library unloading.  It notes that
+one of more callouts registered by that library have been removed from
+the specified hook.  This is similar to the HOOKS_DEREGISTER_ALL_CALLOUTS
+message (and the two are likely to be seen together), but is issued at a
+higher-level in the hook framework.
+
+% HOOKS_CALLOUT_CALLED hooks library with index %1 has called a callout on hook %2 that has address %3
+Only output at a high debugging level, this message indicates that
+a callout on the named hook registered by the library with the given
+index (in the list of loaded libraries) has been called and returned a
+success state.  The address of the callout is given in the message
+
+% HOOKS_CALLOUT_DEREGISTERED hook library at index %1 deregistered a callout on hook %2
+A debug message issued when all instances of a particular callouts on
+the hook identified in the message that were registered by the library
+with the given index have been removed.
+
+% HOOKS_CALLOUT_ERROR error returned by callout on hook %1 registered by library with index %2 (callout address %3)
+If a callout returns an error status when called, this error message
+is issued.  It identifies the hook to which the callout is attached, the
+index of the library (in the list of loaded libraries) that registered
+it and the address of the callout.  The error is otherwise ignored.
+
+% HOOKS_CALLOUT_EXCEPTION exception thrown by callout on hook %1 registered by library with index %2 (callout address %3)
+If a callout throws an exception when called, this error message is
+issued.  It identifies the hook to which the callout is attached, the
+index of the library (in the list of loaded libraries) that registered
+it and the address of the callout.  The error is otherwise ignored.
+
+% HOOKS_CALLOUT_REGISTRATION hooks library with index %1 registering callout for hook '%2'
+This is a debug message, output when a library (whose index in the list
+of libraries (being) loaded is given) registers a callout.
+
+% HOOKS_CLOSE_ERROR failed to close hook library %1: %2
+BIND 10 has failed to close the named hook library for the stated reason.
+Although this is an error, this should not affect the running system
+other than as a loss of resources.  If this error persists, you should
+restart BIND 10.
+
+% HOOKS_HOOK_LIST_RESET the list of hooks has been reset
+This is a message indicating that the list of hooks has been reset.
+While this is usual when running the BIND 10 test suite, it should not be
+seen when running BIND 10 in a producion environment.  If this appears,
+please report a bug through the usual channels.
+
+% HOOKS_INCORRECT_VERSION hook library %1 is at version %2, require version %3
+BIND 10 has detected that the named hook library has been built against
+a version of BIND 10 that is incompatible with the version of BIND 10
+running on your system.  It has not loaded the library.
+
+This is most likely due to the installation of a new version of BIND 10
+without rebuilding the hook library.  A rebuild and re-install of the
+library should fix the problem in most cases.
+
+% HOOKS_LIBRARY_LOADED hooks library %1 successfully loaded
+This information message is issued when a user-supplied hooks library
+has been successfully loaded.
+
+% HOOKS_LIBRARY_LOADING loading hooks library %1
+This is a debug message output just before the specified library is loaded.
+If the action is successfully, it will be followed by the
+HOOKS_LIBRARY_LOADED informational message.
+
+% HOOKS_LIBRARY_UNLOADED hooks library %1 successfully unloaded
+This information message is issued when a user-supplied hooks library
+has been successfully unloaded.
+
+% HOOKS_LIBRARY_UNLOADING unloading library %1
+This is a debug message called when the specified library is
+being unloaded.  If all is successful, it will be followed by the
+HOOKS_LIBRARY_UNLOADED informational message.
+
+% HOOKS_LIBRARY_VERSION hooks library %1 reports its version as %2
+A debug  message issued when the version check on the hooks library
+has succeeded.
+
+% HOOKS_LOAD_ERROR 'load' function in hook library %1 returned error %2
+A "load" function was found in the library named in the message and
+was called.  The function returned a non-zero status (also given in
+the message) which was interpreted as an error.  The library has been
+unloaded and no callouts from it will be installed.
+
+% HOOKS_LOAD_EXCEPTION 'load' function in hook library %1 threw an exception
+A "load" function was found in the library named in the message and
+was called.  The function threw an exception (an error indication)
+during execution, which is an error condition.  The library has been
+unloaded and no callouts from it will be installed.
+
+% HOOKS_LOAD_SUCCESS 'load' function in hook library %1 returned success
+This is a debug message issued when the "load" function has been found
+in a hook library and has been successfully called.
+
+% HOOKS_NO_LOAD no 'load' function found in hook library %1
+This is a debug message saying that the specified library was loaded
+but no function called "load" was found in it.  Providing the library
+contained some "standard" functions (i.e. functions with the names of
+the hooks for the given server), this is not an issue.
+
+% HOOKS_NO_UNLOAD no 'unload' function found in hook library %1
+This is a debug message issued when the library is being unloaded.
+It merely states that the library did not contain an "unload" function.
+
+% HOOKS_NO_VERSION no 'version' function found in hook library %1
+The shared library named in the message was found and successfully loaded,
+but BIND 10 did not find a function named "version" in it.  This function
+is required and should return the version of BIND 10 against which the
+library was built.  The value is used to check that the library was built
+against a compatible version of BIND 10.  The library has not been loaded.
+
+% HOOKS_OPEN_ERROR failed to open hook library %1: %2
+BIND 10 failed to open the specified hook library for the stated
+reason. The library has not been loaded.  BIND 10 will continue to
+function, but without the services offered by the library.
+
+% HOOKS_STD_CALLOUT_REGISTERED hooks library %1 registered standard callout for hook %2 at address %3
+This is a debug message, output when the library loading function has
+located a standard callout (a callout with the same name as a hook point)
+and registered it.  The address of the callout is indicated.
+
+% HOOKS_UNLOAD_ERROR 'unload' function in hook library %1 returned error %2
+During the unloading of a library, an "unload" function was found.
+It was called, but returned an error (non-zero) status, resulting in
+the issuing of this message.  The unload process continued after this
+message and the library has been unloaded.
+
+% HOOKS_UNLOAD_EXCEPTION 'unload' function in hook library %1 threw an exception
+During the unloading of a library, an "unload" function was found.  It was
+called, but in the process generated an exception (an error indication).
+The unload process continued after this message and the library has
+been unloaded.
+
+% HOOKS_UNLOAD_SUCCESS 'unload' function in hook library %1 returned success
+This is a debug message issued when an "unload" function has been found
+in a hook library during the unload process, called, and returned success.
+
+% HOOKS_VERSION_EXCEPTION 'version' function in hook library %1 threw an exception
+This error message is issued if the version() function in the specified
+hooks library was called and generated an exception.  The library is
+considered unusable and will not be loaded.
diff --git a/src/lib/hooks/library_handle.cc b/src/lib/hooks/library_handle.cc
new file mode 100644
index 0000000..7f43116
--- /dev/null
+++ b/src/lib/hooks/library_handle.cc
@@ -0,0 +1,76 @@
+// Copyright (C) 2013  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 <hooks/callout_manager.h>
+#include <hooks/library_handle.h>
+
+namespace isc {
+namespace hooks {
+
+// Callout manipulation - all deferred to the CalloutManager.
+
+void
+LibraryHandle::registerCallout(const std::string& name, CalloutPtr callout) {
+    // Reset library index if required, saving the current value.
+    int saved_index = callout_manager_->getLibraryIndex();
+    if (index_ >= 0) {
+        callout_manager_->setLibraryIndex(index_);
+    }
+
+    // Register the callout.
+    callout_manager_->registerCallout(name, callout);
+
+    // Restore the library index if required.  We know that the saved index
+    // is valid for the number of libraries (or is -1, which is an internal
+    // state indicating there is no current library index) as we obtained it
+    // from the callout manager.
+    if (index_ >= 0) {
+        callout_manager_->setLibraryIndex(saved_index);
+    }
+}
+
+bool
+LibraryHandle::deregisterCallout(const std::string& name, CalloutPtr callout) {
+    int saved_index = callout_manager_->getLibraryIndex();
+    if (index_ >= 0) {
+        callout_manager_->setLibraryIndex(index_);
+    }
+
+    bool status = callout_manager_->deregisterCallout(name, callout);
+
+    if (index_ >= 0) {
+        callout_manager_->setLibraryIndex(saved_index);
+    }
+
+    return (status);
+}
+
+bool
+LibraryHandle::deregisterAllCallouts(const std::string& name) {
+    int saved_index = callout_manager_->getLibraryIndex();
+    if (index_ >= 0) {
+        callout_manager_->setLibraryIndex(index_);
+    }
+
+    bool status = callout_manager_->deregisterAllCallouts(name);
+
+    if (index_ >= 0) {
+        callout_manager_->setLibraryIndex(saved_index);
+    }
+
+    return (status);
+}
+
+} // namespace util
+} // namespace isc
diff --git a/src/lib/hooks/library_handle.h b/src/lib/hooks/library_handle.h
new file mode 100644
index 0000000..4fe47cd
--- /dev/null
+++ b/src/lib/hooks/library_handle.h
@@ -0,0 +1,149 @@
+// Copyright (C) 2013  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 LIBRARY_HANDLE_H
+#define LIBRARY_HANDLE_H
+
+#include <string>
+
+namespace isc {
+namespace hooks {
+
+// Forward declarations
+class CalloutHandle;
+class CalloutManager;
+
+/// Typedef for a callout pointer.  (Callouts must have "C" linkage.)
+extern "C" {
+    typedef int (*CalloutPtr)(CalloutHandle&);
+};
+
+/// @brief Library handle
+///
+/// This class is accessed by the user library when registering callouts,
+/// either by the library's load() function, or by one of the callouts
+/// themselves.
+///
+/// It is really little more than a shell around the CalloutManager.  By
+/// presenting this object to the user-library callouts, callouts can manage
+/// the callout list for their own library, but cannot affect the callouts
+/// registered by other libraries.
+///
+/// (This restriction is achieved by the CalloutManager maintaining the concept
+/// of the "current library".  When a callout is registered - either by the
+/// library's load() function, or by a callout in the library - the registration
+/// information includes the library active at the time.  When that callout is
+/// called, the CalloutManager uses that information to set the "current
+/// library": the registration functions only operator on data whose
+/// associated library is equal to the "current library".)
+
+class LibraryHandle {
+public:
+
+    /// @brief Constructor
+    ///
+    /// @param manager Back pointer to the containing CalloutManager.
+    ///        This pointer is used to access appropriate methods in that
+    ///        object.  Note that the "raw" pointer is safe - the only
+    ///        instance of the LibraryHandle in the system is as a member of
+    ///        the CalloutManager to which it points.
+    ///
+    /// @param index Index of the library to which the LibraryHandle applies.
+    ///        If negative, the library index as set in the CalloutManager is
+    ///        used.  Otherwise the current library index is saved, this value
+    ///        is set as the current index, the registration call is made, and
+    ///        the CalloutManager's library index is reset.  Note: although -1
+    ///        is a valid argument value for
+    ///        isc::hooks::CalloutManager::setLibraryIndex(), in this class is
+    ///        is used as a sentinel to indicate that the library index in
+    ///        isc::util::CalloutManager should not be set or reset.
+    LibraryHandle(CalloutManager* manager, int index = -1)
+        : callout_manager_(manager), index_(index)
+    {}
+
+    /// @brief Register a callout on a hook
+    ///
+    /// Registers a callout function with a given hook.  The callout is added
+    /// to the end of the callouts for the current library that are associated
+    /// with that hook.
+    ///
+    /// @param name Name of the hook to which the callout is added.
+    /// @param callout Pointer to the callout function to be registered.
+    ///
+    /// @throw NoSuchHook The hook name is unrecognised.
+    /// @throw Unexpected The hook name is valid but an internal data structure
+    ///        is of the wrong size.
+    void registerCallout(const std::string& name, CalloutPtr callout);
+
+    /// @brief De-Register a callout on a hook
+    ///
+    /// Searches through the functions registered by the current library with
+    /// the named hook and removes all entries matching the callout.  It does
+    /// not affect callouts registered by other libraries.
+    ///
+    /// @param name Name of the hook from which the callout is removed.
+    /// @param callout Pointer to the callout function to be removed.
+    ///
+    /// @return true if a one or more callouts were deregistered.
+    ///
+    /// @throw NoSuchHook The hook name is unrecognised.
+    /// @throw Unexpected The hook name is valid but an internal data structure
+    ///        is of the wrong size.
+    bool deregisterCallout(const std::string& name, CalloutPtr callout);
+
+    /// @brief Removes all callouts on a hook
+    ///
+    /// Removes all callouts associated with a given hook that were registered.
+    /// by the current library.  It does not affect callouts that were
+    /// registered by other libraries.
+    ///
+    /// @param name Name of the hook from which the callouts are removed.
+    ///
+    /// @return true if one or more callouts were deregistered.
+    ///
+    /// @throw NoSuchHook Thrown if the hook name is unrecognised.
+    bool deregisterAllCallouts(const std::string& name);
+
+private:
+    /// @brief Copy constructor
+    ///
+    /// Private (with no implementation) as it makes no sense to copy an object
+    /// of this type.  All code receives a reference to an existing handle which
+    /// is tied to a particular CalloutManager.  Creating a copy of that handle
+    /// runs the risk of a "dangling pointer" to the original handle's callout
+    /// manager.
+    ///
+    /// @param Unused - should be the object to copy.
+    LibraryHandle(const LibraryHandle&);
+
+    /// @brief Assignment operator
+    ///
+    /// Declared private like the copy constructor for the same reasons. It too
+    /// has no implementation.
+    ///
+    /// @param Unused - should be the object to copy.
+    LibraryHandle& operator=(const LibraryHandle&);
+
+    /// Back pointer to the collection object for the library
+    CalloutManager* callout_manager_;
+
+    /// Library index to which this handle applies.  -1 indicates that it
+    /// applies to whatever index is current in the CalloutManager.
+    int index_;
+};
+
+} // namespace util
+} // namespace isc
+
+#endif // LIBRARY_HANDLE_H
diff --git a/src/lib/hooks/library_manager.cc b/src/lib/hooks/library_manager.cc
new file mode 100644
index 0000000..70c76ba
--- /dev/null
+++ b/src/lib/hooks/library_manager.cc
@@ -0,0 +1,353 @@
+// Copyright (C) 2013  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 <hooks/hooks.h>
+#include <hooks/hooks_log.h>
+#include <hooks/callout_manager.h>
+#include <hooks/library_handle.h>
+#include <hooks/library_manager.h>
+#include <hooks/pointer_converter.h>
+#include <hooks/server_hooks.h>
+
+#include <string>
+#include <vector>
+
+#include <dlfcn.h>
+
+using namespace std;
+
+namespace isc {
+namespace hooks {
+
+
+// Constructor (used by external agency)
+LibraryManager::LibraryManager(const std::string& name, int index,
+                               const boost::shared_ptr<CalloutManager>& manager)
+        : dl_handle_(NULL), index_(index), manager_(manager),
+          library_name_(name)
+{
+    if (!manager) {
+        isc_throw(NoCalloutManager, "must specify a CalloutManager when "
+                  "instantiating a LibraryManager object");
+    }
+}
+
+// Constructor (used by "validate" for library validation).  Note that this
+// sets "manager_" to not point to anything, which means that methods such as
+// registerStandardCallout() will fail, probably with a segmentation fault.
+// There are no checks for this condition in those methods: this constructor
+// is declared "private", so can only be executed by a method in this class.
+// The only method to do so is "validateLibrary", which takes care not to call
+// methods requiring a non-NULL manager.
+LibraryManager::LibraryManager(const std::string& name)
+        : dl_handle_(NULL), index_(-1), manager_(), library_name_(name)
+{}
+
+// Destructor.  
+LibraryManager::~LibraryManager() {
+    if (manager_) {
+        // LibraryManager instantiated to load a library, so ensure that
+        // it is unloaded before exiting.
+        static_cast<void>(unloadLibrary());
+    } else {
+        // LibraryManager instantiated to validate a library, so just ensure
+        // that it is closed before exiting.
+        static_cast<void>(closeLibrary());
+    }
+}
+
+// Open the library
+
+bool
+LibraryManager::openLibrary() {
+
+    // Open the library.  We'll resolve names now, so that if there are any
+    // issues we don't bugcheck in the middle of apparently unrelated code.
+    dl_handle_ = dlopen(library_name_.c_str(), RTLD_NOW | RTLD_LOCAL);
+    if (dl_handle_ == NULL) {
+        LOG_ERROR(hooks_logger, HOOKS_OPEN_ERROR).arg(library_name_)
+                  .arg(dlerror());
+    }
+
+    return (dl_handle_ != NULL);
+}
+
+// Close the library if not already open
+
+bool
+LibraryManager::closeLibrary() {
+
+    // Close the library if it is open. (If not, this is a no-op.)
+    int status = 0;
+    if (dl_handle_ != NULL) {
+        status = dlclose(dl_handle_);
+        dl_handle_ = NULL;
+        if (status != 0) {
+            LOG_ERROR(hooks_logger, HOOKS_CLOSE_ERROR).arg(library_name_)
+                      .arg(dlerror());
+        }
+    }
+
+    return (status == 0);
+}
+
+// Check the version of the library
+
+bool
+LibraryManager::checkVersion() const {
+
+    // Get the pointer to the "version" function.
+    PointerConverter pc(dlsym(dl_handle_, VERSION_FUNCTION_NAME));
+    if (pc.versionPtr() != NULL) {
+        int version = BIND10_HOOKS_VERSION - 1; // This is an invalid value
+        try {
+            version = (*pc.versionPtr())();
+        } catch (...) {
+            LOG_ERROR(hooks_logger, HOOKS_VERSION_EXCEPTION).arg(library_name_);
+            return (false);
+        }
+
+        if (version == BIND10_HOOKS_VERSION) {
+            // All OK, version checks out
+            LOG_DEBUG(hooks_logger, HOOKS_DBG_CALLS, HOOKS_LIBRARY_VERSION)
+                      .arg(library_name_).arg(version);
+            return (true);
+
+        } else {
+            LOG_ERROR(hooks_logger, HOOKS_INCORRECT_VERSION).arg(library_name_)
+                      .arg(version).arg(BIND10_HOOKS_VERSION);
+        }
+    } else {
+        LOG_ERROR(hooks_logger, HOOKS_NO_VERSION).arg(library_name_);
+    }
+
+    return (false);
+}
+
+// Register the standard callouts
+
+void
+LibraryManager::registerStandardCallouts() {
+    // Set the library index for doing the registration.  This is picked up
+    // when the library handle is created.
+    manager_->setLibraryIndex(index_);
+
+    // Iterate through the list of known hooks
+    vector<string> hook_names = ServerHooks::getServerHooks().getHookNames();
+    for (int i = 0; i < hook_names.size(); ++i) {
+
+        // Look up the symbol
+        void* dlsym_ptr = dlsym(dl_handle_, hook_names[i].c_str());
+        PointerConverter pc(dlsym_ptr);
+        if (pc.calloutPtr() != NULL) {
+            // Found a symbol, so register it.
+            manager_->getLibraryHandle().registerCallout(hook_names[i],
+                                                         pc.calloutPtr());
+            LOG_DEBUG(hooks_logger, HOOKS_DBG_CALLS,
+                      HOOKS_STD_CALLOUT_REGISTERED).arg(library_name_)
+                      .arg(hook_names[i]).arg(dlsym_ptr);
+
+        }
+    }
+}
+
+// Run the "load" function if present.
+
+bool
+LibraryManager::runLoad() {
+
+    // Get the pointer to the "load" function.
+    PointerConverter pc(dlsym(dl_handle_, LOAD_FUNCTION_NAME));
+    if (pc.loadPtr() != NULL) {
+
+        // Call the load() function with the library handle.  We need to set
+        // the CalloutManager's index appropriately.  We'll invalidate it
+        // afterwards.
+
+        int status = -1;
+        try {
+            manager_->setLibraryIndex(index_);
+            status = (*pc.loadPtr())(manager_->getLibraryHandle());
+        } catch (...) {
+            LOG_ERROR(hooks_logger, HOOKS_LOAD_EXCEPTION).arg(library_name_);
+            return (false);
+        }
+
+        if (status != 0) {
+            LOG_ERROR(hooks_logger, HOOKS_LOAD_ERROR).arg(library_name_)
+                      .arg(status);
+            return (false);
+        } else {
+        LOG_DEBUG(hooks_logger, HOOKS_DBG_TRACE, HOOKS_LOAD_SUCCESS)
+            .arg(library_name_);
+        }
+
+    } else {
+        LOG_DEBUG(hooks_logger, HOOKS_DBG_TRACE, HOOKS_NO_LOAD)
+            .arg(library_name_);
+    }
+
+    return (true);
+}
+
+
+// Run the "unload" function if present.
+
+bool
+LibraryManager::runUnload() {
+
+    // Get the pointer to the "load" function.
+    PointerConverter pc(dlsym(dl_handle_, UNLOAD_FUNCTION_NAME));
+    if (pc.unloadPtr() != NULL) {
+
+        // Call the load() function with the library handle.  We need to set
+        // the CalloutManager's index appropriately.  We'll invalidate it
+        // afterwards.
+        int status = -1;
+        try {
+            status = (*pc.unloadPtr())();
+        } catch (...) {
+            // Exception generated.  Note a warning as the unload will occur
+            // anyway.
+            LOG_WARN(hooks_logger, HOOKS_UNLOAD_EXCEPTION).arg(library_name_);
+            return (false);
+        }
+
+        if (status != 0) {
+            LOG_ERROR(hooks_logger, HOOKS_UNLOAD_ERROR).arg(library_name_)
+                      .arg(status);
+            return (false);
+        } else {
+        LOG_DEBUG(hooks_logger, HOOKS_DBG_TRACE, HOOKS_UNLOAD_SUCCESS)
+            .arg(library_name_);
+        }
+    } else {
+        LOG_DEBUG(hooks_logger, HOOKS_DBG_TRACE, HOOKS_NO_UNLOAD)
+            .arg(library_name_);
+    }
+
+    return (true);
+}
+
+// The main library loading function.
+
+bool
+LibraryManager::loadLibrary() {
+    LOG_DEBUG(hooks_logger, HOOKS_DBG_TRACE, HOOKS_LIBRARY_LOADING)
+        .arg(library_name_);
+
+    // In the following, if a method such as openLibrary() fails, it will
+    // have issued an error message so there is no need to issue another one
+    // here.
+
+    // Open the library (which is a check that it exists and is accessible).
+    if (openLibrary()) {
+
+        // Library opened OK, see if a version function is present and if so,
+        // check what value it returns.
+        if (checkVersion()) {
+
+            // Version OK, so now register the standard callouts and call the
+            // library's load() function if present.
+            registerStandardCallouts();
+            if (runLoad()) {
+
+                // Success - the library has been successfully loaded.
+                LOG_INFO(hooks_logger, HOOKS_LIBRARY_LOADED).arg(library_name_);
+                return (true);
+
+            } else {
+
+                // The load function failed, so back out.  We can't just close
+                // the library as (a) we need to call the library's "unload"
+                // function (if present) in case "load" allocated resources that
+                // need to be freed and (b) we need to remove any callouts that
+                // have been installed.
+                static_cast<void>(unloadLibrary());
+            }
+        }
+
+        // Either the version check or call to load() failed, so close the
+        // library and free up resources.  Ignore the status return here - we
+        // already know there's an error and will have output a message.
+        static_cast<void>(closeLibrary());
+    }
+
+    return (false);
+}
+
+// The library unloading function.  Call the unload() function (if present),
+// remove callouts from the callout manager, then close the library.  This is
+// only run if the library is still loaded and is a no-op if the library is
+// not open.
+
+bool
+LibraryManager::unloadLibrary() {
+    bool result = true;
+    if (dl_handle_ != NULL) {
+        LOG_DEBUG(hooks_logger, HOOKS_DBG_TRACE, HOOKS_LIBRARY_UNLOADING)
+            .arg(library_name_);
+
+        // Call the unload() function if present.  Note that this is done first
+        // - operations take place in the reverse order to which they were done
+        // when the library was loaded.
+        result = runUnload();
+
+        // Regardless of status, remove all callouts associated with this
+        // library on all hooks.
+        vector<string> hooks = ServerHooks::getServerHooks().getHookNames();
+        manager_->setLibraryIndex(index_);
+        for (int i = 0; i < hooks.size(); ++i) {
+            bool removed = manager_->deregisterAllCallouts(hooks[i]);
+            if (removed) {
+                LOG_DEBUG(hooks_logger, HOOKS_DBG_CALLS, HOOKS_CALLOUTS_REMOVED)
+                    .arg(hooks[i]).arg(library_name_);
+            }
+        }
+
+        // ... and close the library.
+        result = closeLibrary() && result;
+        if (result) {
+
+            // Issue the informational message only if the library was unloaded
+            // with no problems.  If there was an issue, an error message would
+            // have been issued.
+            LOG_INFO(hooks_logger, HOOKS_LIBRARY_UNLOADED).arg(library_name_);
+        }
+    }
+    return (result);
+}
+
+// Validate the library.  We must be able to open it, and the version function
+// must both exist and return the right number.  Note that this is a static
+// method.
+
+bool
+LibraryManager::validateLibrary(const std::string& name) {
+    // Instantiate a library manager for the validation.  We use the private
+    // constructor as we don't supply a CalloutManager.
+    LibraryManager manager(name);
+
+    // Try to open it and, if we succeed, check the version.
+    bool validated = manager.openLibrary() && manager.checkVersion();
+
+    // Regardless of whether the version checked out, close the library. (This
+    // is a no-op if the library failed to open.)
+    static_cast<void>(manager.closeLibrary());
+
+    return (validated);
+}
+
+} // namespace hooks
+} // namespace isc
diff --git a/src/lib/hooks/library_manager.h b/src/lib/hooks/library_manager.h
new file mode 100644
index 0000000..dc7c5eb
--- /dev/null
+++ b/src/lib/hooks/library_manager.h
@@ -0,0 +1,231 @@
+// Copyright (C) 2013  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 LIBRARY_MANAGER_H
+#define LIBRARY_MANAGER_H
+
+#include <exceptions/exceptions.h>
+
+#include <boost/shared_ptr.hpp>
+
+#include <string>
+
+namespace isc {
+namespace hooks {
+
+/// @brief No Callout Manager
+///
+/// Thrown if a library manager is instantiated by an external agency without
+/// specifying a CalloutManager object.
+class NoCalloutManager : public Exception {
+public:
+    NoCalloutManager(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
+class CalloutManager;
+class LibraryHandle;
+class LibraryManager;
+
+/// @brief Library manager
+///
+/// This class handles the loading and unloading of a specific library.  It also
+/// provides a static method for checking that a library is valid (this is used
+/// in configuration parsing).
+///
+/// On loading, it opens the library using dlopen and checks the version (set
+/// with the "version" method.  If all is OK, it iterates through the list of
+/// known hooks and locates their symbols, registering each callout as it does
+/// so.  Finally it locates the "load" function (if present) and calls it.
+///
+/// On unload, it calls the "unload" method if present, clears the callouts on
+/// all hooks, and closes the library.
+///
+/// @note Caution needs to be exercised when using the unload method. During
+///       normal use, data will pass between the server and the library.  In
+///       this process, the library may allocate memory and pass it back to the
+///       server.  This could happen by the server setting arguments or context
+///       in the CalloutHandle object, or by the library modifying the content
+///       of pointed-to data. If the library is unloaded, this memory may lie
+///       in the virtual address space deleted in that process. (The word "may"
+///       is used, as this could be operating-system specific.) Should this
+///       happen, any reference to the memory will cause a segmentation fault.
+///       This can occur in a quite obscure place, for example in the middle of
+///       a destructor of an STL class when it is deleting memory allocated
+///       when the data structure was extended by a function in the library.
+///
+/// @note The only safe way to run the "unload" function is to ensure that all
+///       possible references to it are removed first.  This means that all
+///       CalloutHandles must be destroyed, as must any data items that were
+///       passed to the callouts.  In practice, it could mean that a server
+///       suspends processing of new requests until all existing ones have
+///       been serviced and all packet/context structures destroyed before
+///       reloading the libraries.
+///
+/// When validating a library, only the fact that the library can be opened and
+/// version() exists and returns the correct number is checked.  The library
+/// is closed after the validation.
+
+class LibraryManager {
+public:
+    /// @brief Constructor
+    ///
+    /// This constructor is used by external agencies (i.e. the
+    /// LibraryManagerCollection) when instantiating a LibraryManager.  It
+    /// stores the library name - the actual actual loading is done in
+    /// loadLibrary().
+    ///
+    /// @param name Name of the library to load.  This should be an absolute
+    ///        path name.
+    /// @param index Index of this library
+    /// @param manager CalloutManager object
+    ///
+    /// @throw NoCalloutManager Thrown if the manager argument is NULL.
+    LibraryManager(const std::string& name, int index,
+                   const boost::shared_ptr<CalloutManager>& manager);
+
+    /// @brief Destructor
+    ///
+    /// If the library is open, closes it.  This is principally a safety
+    /// feature to ensure closure in the case of an exception destroying this
+    /// object.  However, see the caveat in the class header about when it is
+    /// safe to unload libraries.
+    ~LibraryManager();
+
+    /// @brief Validate library
+    ///
+    /// A static method that is used to validate a library.  Validation checks
+    /// that the library can be opened, that "version" exists, and that it
+    /// returns the right number.
+    ///
+    /// @param name Name of the library to validate
+    ///
+    /// @return true if the library validated, false if not.  If the library
+    /// fails to validate, the reason for the failure is logged.
+    static bool validateLibrary(const std::string& name);
+
+    /// @brief Loads a library
+    ///
+    /// Open the library and check the version.  If all is OK, load all standard
+    /// symbols then call "load" if present.
+    ///
+    /// @return true if the library loaded successfully, false otherwise. In the
+    ///         latter case, the library will be unloaded if possible.
+    bool loadLibrary();
+
+    /// @brief Unloads a library
+    ///
+    /// Calls the libraries "unload" function if present, the closes the
+    /// library.
+    ///
+    /// However, see the caveat in the class header about when it is safe to
+    /// unload libraries.
+    ///
+    /// @return true if the library unloaded successfully, false if an error
+    ///         occurred in the process (most likely the unload() function
+    ///         (if present) returned an error).  Even if an error did occur,
+    ///         the library is closed if possible.
+    bool unloadLibrary();
+
+    /// @brief Return library name
+    ///
+    /// @return Name of this library
+    std::string getName() const {
+        return (library_name_);
+    }
+
+protected:
+    // The following methods are protected as they are accessed in testing.
+
+    /// @brief Open library
+    ///
+    /// Opens the library associated with this LibraryManager.  A message is
+    /// logged on an error.
+    ///
+    /// @return true if the library opened successfully, false otherwise.
+    bool openLibrary();
+
+    /// @brief Close library
+    ///
+    /// Closes the library associated with this LibraryManager.  A message is
+    /// logged on an error.
+    ///
+    /// @return true if the library closed successfully, false otherwise. "true"
+    ///         is also returned if the library were already closed when this
+    ///         method was called.
+    bool closeLibrary();
+
+    /// @brief Check library version
+    ///
+    /// With the library open, accesses the "version()" function and, if
+    /// present, checks the returned value against the hooks version symbol
+    /// for the currently running BIND 10.  The "version()" function is
+    /// mandatory and must be present (and return the correct value) for the
+    /// library to load.
+    ///
+    /// If there is no version() function, or if there is a mismatch in
+    /// version number, a message logged.
+    ///
+    /// @return bool true if the check succeeded
+    bool checkVersion() const;
+
+    /// @brief Register standard callouts
+    ///
+    /// Loops through the list of hook names and searches the library for
+    /// functions with those names.  Any that are found are registered as
+    /// callouts for that hook.
+    void registerStandardCallouts();
+
+    /// @brief Run the load function if present
+    ///
+    /// Searches for the "load" framework function and, if present, runs it.
+    ///
+    /// @return bool true if not found or found and run successfully,
+    ///         false on an error.  In this case, an error message will
+    ///         have been output.
+    bool runLoad();
+
+    /// @brief Run the unload function if present
+    ///
+    /// Searches for the "unload" framework function and, if present, runs it.
+    ///
+    /// @return bool true if not found or found and run successfully,
+    ///         false on an error.  In this case, an error message will
+    ///         have been output.
+    bool runUnload();
+
+private:
+    /// @brief Validating constructor
+    ///
+    /// Constructor used when the LibraryManager is instantiated to validate
+    /// a library (i.e. by the "validateLibrary" static method).
+    ///
+    /// @param name Name of the library to load.  This should be an absolute
+    ///        path name.
+    LibraryManager(const std::string& name);
+
+    // Member variables
+
+    void*       dl_handle_;     ///< Handle returned by dlopen
+    int         index_;         ///< Index associated with this library
+    boost::shared_ptr<CalloutManager> manager_;
+                                ///< Callout manager for registration
+    std::string library_name_;  ///< Name of the library
+
+};
+
+} // namespace hooks
+} // namespace isc
+
+#endif  // LIBRARY_MANAGER_H
diff --git a/src/lib/hooks/library_manager_collection.cc b/src/lib/hooks/library_manager_collection.cc
new file mode 100644
index 0000000..b9122e2
--- /dev/null
+++ b/src/lib/hooks/library_manager_collection.cc
@@ -0,0 +1,129 @@
+// Copyright (C) 2013  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 <hooks/callout_manager.h>
+#include <hooks/library_manager.h>
+#include <hooks/library_manager_collection.h>
+
+namespace isc {
+namespace hooks {
+
+// Return callout manager for the loaded libraries.  This call is only valid
+// after one has been created for the loaded libraries (which includes the
+// case of no loaded libraries).
+//
+// Note that there is no real connection between the callout manager and the
+// libraries, other than it knows the number of libraries so can do sanity
+// checks on values passed to it.  However, this may change in the future,
+// so the hooks framework is written such that a callout manager is used only
+// with the LibraryManagerCollection that created it.  It is also the reason
+// why each LibraryManager contains a pointer to this CalloutManager.
+
+boost::shared_ptr<CalloutManager>
+LibraryManagerCollection::getCalloutManager() const {
+
+    // Only return a pointer if we have a CalloutManager created.
+    if (! callout_manager_) {
+        isc_throw(LoadLibrariesNotCalled, "must load hooks libraries before "
+                  "attempting to retrieve a CalloutManager for them");
+    }
+
+    return (callout_manager_);
+}
+
+// Load a set of libraries
+
+bool
+LibraryManagerCollection::loadLibraries() {
+
+    // Unload libraries if any are loaded.
+    static_cast<void>(unloadLibraries());
+
+    // Create the callout manager.  A pointer to this is maintained by
+    // each library.  Note that the callout manager does not hold any memory
+    // allocated by a library: although a library registers a callout (and so
+    // causes the creation of an entry in the CalloutManager's callout list),
+    // that creation is done by the CalloutManager itself.  The CalloutManager
+    // is created within the server.
+    //
+    // The upshot of this is that it is therefore safe for the CalloutManager
+    // to be deleted after all associated libraries are deleted, hence this
+    // link (LibraryManager -> CalloutManager) is safe.
+    callout_manager_.reset(new CalloutManager(library_names_.size()));
+
+    // Now iterate through the libraries are load them one by one.  We'll
+    for (int i = 0; i < library_names_.size(); ++i) {
+        // Create a pointer to the new library manager.  The index of this
+        // library is determined by the number of library managers currently
+        // loaded: note that the library indexes run from 1 to (number of loaded
+        // libraries).
+        boost::shared_ptr<LibraryManager> manager(
+                new LibraryManager(library_names_[i], lib_managers_.size() + 1,
+                                   callout_manager_));
+
+        // Load the library.  On success, add it to the list of loaded
+        // libraries.  On failure, unload all currently loaded libraries,
+        // leaving the object in the state it was in before loadLibraries was
+        // called.
+        if (manager->loadLibrary()) {
+            lib_managers_.push_back(manager);
+        } else {
+            static_cast<void>(unloadLibraries());
+            return (false);
+        }
+    }
+
+    return (true);
+}
+
+// Unload the libraries.
+
+void
+LibraryManagerCollection::unloadLibraries() {
+
+    // Delete the library managers in the reverse order to which they were
+    // created, then clear the library manager vector.
+    for (int i = lib_managers_.size() - 1; i >= 0; --i) {
+        lib_managers_[i].reset();
+    }
+    lib_managers_.clear();
+
+    // Get rid of the callout manager. (The other member, the list of library
+    // names, was cleared when the libraries were loaded.)
+    callout_manager_.reset();
+}
+
+// Return number of loaded libraries.
+int
+LibraryManagerCollection::getLoadedLibraryCount() const {
+    return (lib_managers_.size());
+}
+
+// Validate the libraries.
+std::vector<std::string>
+LibraryManagerCollection::validateLibraries(
+                          const std::vector<std::string>& libraries) {
+
+    std::vector<std::string> failures;
+    for (int i = 0; i < libraries.size(); ++i) {
+        if (!LibraryManager::validateLibrary(libraries[i])) {
+            failures.push_back(libraries[i]);
+        }
+    }
+
+    return (failures);
+}
+
+} // namespace hooks
+} // namespace isc
diff --git a/src/lib/hooks/library_manager_collection.h b/src/lib/hooks/library_manager_collection.h
new file mode 100644
index 0000000..0a255ba
--- /dev/null
+++ b/src/lib/hooks/library_manager_collection.h
@@ -0,0 +1,170 @@
+// Copyright (C) 2013  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 LIBRARY_MANAGER_COLLECTION_H
+#define LIBRARY_MANAGER_COLLECTION_H
+
+#include <exceptions/exceptions.h>
+
+#include <boost/shared_ptr.hpp>
+
+#include <vector>
+
+namespace isc {
+namespace hooks {
+
+/// @brief LoadLibraries not called
+///
+/// Thrown if an attempt is made get a CalloutManager before the libraries
+/// have been loaded.
+class LoadLibrariesNotCalled : public Exception {
+public:
+    LoadLibrariesNotCalled(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
+
+// Forward declarations
+class CalloutManager;
+class LibraryManager;
+
+/// @brief Library manager collection
+///
+/// The LibraryManagerCollection class, as the name implies, is responsible for
+/// managing the collection of LibraryManager objects that describe the loaded
+/// libraries.  As such, it converts a single operation (e.g load libraries)
+/// into multiple operations, one per library.  However, the class does more
+/// than that - it provides a single object with which to manage lifetimes.
+///
+/// As described in the LibraryManager documentation, a CalloutHandle may end
+/// up with pointers to memory within the address space of a loaded library.
+/// If the library is unloaded before this address space is deleted, the
+/// deletion of the CalloutHandle may attempt to free memory into the newly-
+/// unmapped address space and cause a segmentation fault.
+///
+/// To prevent this, each CalloutHandle maintains a shared pointer to the
+/// LibraryManagerCollection current when it was created.  In addition, the
+/// containing HooksManager object also maintains a shared pointer to it.  A
+/// a LibraryManagerCollection is never explicitly deleted: when a new set
+/// of libraries is loaded, the HooksManager clears its pointer to the
+/// collection.  The LibraryManagerCollection is only destroyed when all
+/// CallHandle objects referencing it are destroyed.
+///
+/// Note that this does not completely solve the problem - a hook function may
+/// have modified a packet being processed by the server and that packet may
+/// hold a pointer to memory in the library's virtual address space. To avoid
+/// a segmentation fault, that packet needs to free the memory before the
+/// LibraryManagerCollection is destroyed and this places demands on the server
+/// code.  However, the link with the CalloutHandle does at least mean that
+/// authors of server code do not need to be so careful about when they destroy
+/// CalloutHandles.
+///
+/// The collection object also provides a utility function to validate a set
+/// of libraries.  The function checks that each library exists, can be opened,
+/// that the "version" function exists and return the right number.
+
+class LibraryManagerCollection {
+public:
+    /// @brief Constructor
+    ///
+    /// @param libraries List of libraries that this collection will manage.
+    ///        The order of the libraries is important.
+    LibraryManagerCollection(const std::vector<std::string>& libraries)
+        : library_names_(libraries)
+    {}
+
+    /// @brief Destructor
+    ///
+    /// Unloads all loaded libraries.
+    ~LibraryManagerCollection() {
+        static_cast<void>(unloadLibraries());
+    }
+
+    /// @brief Load libraries
+    ///
+    /// Loads the libraries.  This creates the LibraryManager associated with
+    /// each library and calls its loadLibrary() method.  If a library fails
+    /// to load, the loading is abandoned and all libraries loaded so far
+    /// are unloaded.
+    ///
+    /// @return true if all libraries loaded, false if one or more failed t
+    ////        load.
+    bool loadLibraries();
+
+    /// @brief Get callout manager
+    ///
+    /// Returns a callout manager that can be used with this set of loaded
+    /// libraries (even if the number of loaded libraries is zero).  This
+    /// method may only be caslled after loadLibraries() has been called.
+    ///
+    /// @return Pointer to a callout manager for this set of libraries.
+    ///
+    /// @throw LoadLibrariesNotCalled Thrown if this method is called between
+    ///        construction and the time loadLibraries() is called.
+    boost::shared_ptr<CalloutManager> getCalloutManager() const;
+
+    /// @brief Get library names
+    ///
+    /// Returns the list of library names.  If called before loadLibraries(),
+    /// the list is the list of names to be loaded; if called afterwards, it
+    /// is the list of libraries that have been loaded.
+    std::vector<std::string> getLibraryNames() const {
+        return (library_names_);
+    }
+
+    /// @brief Get number of loaded libraries
+    ///
+    /// Mainly for testing, this returns the number of libraries that are
+    /// loaded.
+    ///
+    /// @return Number of libraries that are loaded.
+    int getLoadedLibraryCount() const;
+
+    /// @brief Validate libraries
+    ///
+    /// Utility function to validate libraries.  It checks that the libraries
+    /// exist, can be opened, that a "version" function is present in them, and
+    /// that it returns the right number.  All errors are logged.
+    ///
+    /// @param libraries List of libraries to validate
+    ///
+    /// @return Vector of libraries that faled to validate, or an empty vector
+    ///         if all validated.
+    static std::vector<std::string>
+    validateLibraries(const std::vector<std::string>& libraries);
+
+protected:
+    /// @brief Unload libraries
+    ///
+    /// Unloads and closes all loaded libraries.  They are unloaded in the
+    /// reverse order to the order in which they were loaded.
+    void unloadLibraries();
+
+private:
+
+    /// Vector of library names
+    std::vector<std::string>                        library_names_;
+
+    /// Vector of library managers
+    std::vector<boost::shared_ptr<LibraryManager> > lib_managers_;
+
+    /// Callout manager to be associated with the libraries
+    boost::shared_ptr<CalloutManager>               callout_manager_;
+};
+
+} // namespace hooks
+} // namespace isc
+
+
+#endif // LIBRARY_MANAGER_COLLECTION_H
diff --git a/src/lib/hooks/pointer_converter.h b/src/lib/hooks/pointer_converter.h
new file mode 100644
index 0000000..1fe15ac
--- /dev/null
+++ b/src/lib/hooks/pointer_converter.h
@@ -0,0 +1,121 @@
+// Copyright (C) 2013  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 POINTER_CONVERTER_H
+#define POINTER_CONVERTER_H
+
+#include <hooks/hooks.h>
+
+namespace isc {
+namespace hooks {
+
+/// @brief Local class for conversion of void pointers to function pointers
+///
+/// Converting between void* and function pointers in C++ is fraught with
+/// difficulty and pitfalls, e.g. see
+/// https://groups.google.com/forum/?hl=en&fromgroups#!topic/comp.lang.c++/37o0l8rtEE0
+///
+/// The method given in that article - convert using a union is used here.  A
+/// union is declared (and zeroed) and the appropriate member extracted when
+/// needed.
+
+class PointerConverter {
+public:
+    /// @brief Constructor
+    ///
+    /// Zeroes the union and stores the void* pointer we wish to convert (the
+    /// one returned by dlsym).
+    ///
+    /// @param dlsym_ptr void* pointer returned by call to dlsym()
+    PointerConverter(void* dlsym_ptr) {
+        memset(&pointers_, 0, sizeof(pointers_));
+        pointers_.dlsym_ptr = dlsym_ptr;
+    }
+
+    /// @brief Constructor
+    ///
+    /// Zeroes the union and stores the CalloutPtr pointer we wish to convert.
+    /// This constructor is used in debug messages; output of a pointer to
+    /// an object (including to a function) is, on some compilers, printed as
+    /// "1".
+    ///
+    /// @param callout_ptr Pointer to callout function
+    PointerConverter(CalloutPtr callout_ptr) {
+        memset(&pointers_, 0, sizeof(pointers_));
+        pointers_.callout_ptr = callout_ptr;
+    }
+
+    /// @name Pointer accessor functions
+    ///
+    /// It is up to the caller to ensure that the correct member is called so
+    /// that the correct type of pointer is returned.
+    ///
+    ///@{
+
+    /// @brief Return pointer returned by dlsym call
+    ///
+    /// @return void* pointer returned by the call to dlsym().  This can be
+    ///         used in statements that print the hexadecimal value of the
+    ///         symbol.
+    void* dlsymPtr() const {
+        return (pointers_.dlsym_ptr);
+    }
+
+    /// @brief Return pointer to callout function
+    ///
+    /// @return Pointer to the callout function
+    CalloutPtr calloutPtr() const {
+        return (pointers_.callout_ptr);
+    }
+
+    /// @brief Return pointer to load function
+    ///
+    /// @return Pointer to the load function
+    load_function_ptr loadPtr() const {
+        return (pointers_.load_ptr);
+    }
+
+    /// @brief Return pointer to unload function
+    ///
+    /// @return Pointer to the unload function
+    unload_function_ptr unloadPtr() const {
+        return (pointers_.unload_ptr);
+    }
+
+    /// @brief Return pointer to version function
+    ///
+    /// @return Pointer to the version function
+    version_function_ptr versionPtr() const {
+        return (pointers_.version_ptr);
+    }
+
+    ///@}
+
+private:
+
+    /// @brief Union linking void* and pointers to functions.
+    union {
+        void*                   dlsym_ptr;      // void* returned by dlsym
+        CalloutPtr              callout_ptr;    // Pointer to callout
+        load_function_ptr       load_ptr;       // Pointer to load function
+        unload_function_ptr     unload_ptr;     // Pointer to unload function
+        version_function_ptr    version_ptr;    // Pointer to version function
+    } pointers_;
+};
+
+} // namespace hooks
+} // namespace isc
+
+
+#endif // POINTER_CONVERTER_H
diff --git a/src/lib/hooks/server_hooks.cc b/src/lib/hooks/server_hooks.cc
new file mode 100644
index 0000000..3057d25
--- /dev/null
+++ b/src/lib/hooks/server_hooks.cc
@@ -0,0 +1,159 @@
+// Copyright (C) 2013  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 <hooks/hooks_log.h>
+#include <hooks/server_hooks.h>
+
+#include <utility>
+#include <vector>
+
+using namespace std;
+using namespace isc;
+
+namespace isc {
+namespace hooks {
+
+// Constructor - register the pre-defined hooks and check that the indexes
+// assigned to them are as expected.
+//
+// Note that there are no logging messages here or in registerHooks().  The
+// recommended way to initialize hook names is to use static initialization.
+// Here, a static object is declared in a file outside of any function or
+// method.  As a result, it is instantiated and its constructor run before the
+// program starts.  By putting calls to ServerHooks::registerHook() in there,
+// hooks names are already registered when the program runs.  However, at that
+// point, the logging system is not initialized, so messages are unable to
+// be output.
+
+ServerHooks::ServerHooks() {
+    initialize();
+}
+
+// Register a hook.  The index assigned to the hook is the current number
+// of entries in the collection, so ensuring that hook indexes are unique
+// and non-negative.
+
+int
+ServerHooks::registerHook(const string& name) {
+
+    // Determine index for the new element and insert.
+    int index = hooks_.size();
+    pair<HookCollection::iterator, bool> result =
+        hooks_.insert(make_pair(name, index));
+
+    if (!result.second) {
+        // New element was not inserted because an element with the same name
+        // already existed.
+        isc_throw(DuplicateHook, "hook with name " << name <<
+                  " is already registered");
+    }
+
+    // Element was inserted, so add to the inverse hooks collection.
+    inverse_hooks_[index] = name;
+
+    // ... and return numeric index.
+    return (index);
+}
+
+// Set ServerHooks object to initial state.
+
+void
+ServerHooks::initialize() {
+
+    // Clear out the name->index and index->name maps.
+    hooks_.clear();
+    inverse_hooks_.clear();
+
+    // Register the pre-defined hooks.
+    int create = registerHook("context_create");
+    int destroy = registerHook("context_destroy");
+
+    // Check registration went as expected.
+    if ((create != CONTEXT_CREATE) || (destroy != CONTEXT_DESTROY)) {
+        isc_throw(Unexpected, "pre-defined hook indexes are not as expected. "
+                  "context_create: expected = " << CONTEXT_CREATE <<
+                  ", actual = " << create <<
+                  ". context_destroy: expected = " << CONTEXT_DESTROY <<
+                  ", actual = " << destroy);
+    }
+}
+
+// Reset ServerHooks object to initial state.
+
+void
+ServerHooks::reset() {
+
+    // Clear all hooks then initialize the pre-defined ones.
+    initialize();
+
+    // Log a warning - although this is done during testing, it should never be
+    // seen in a production system.
+    LOG_WARN(hooks_logger, HOOKS_HOOK_LIST_RESET);
+}
+
+// Find the name associated with a hook index.
+
+std::string
+ServerHooks::getName(int index) const {
+
+    // Get iterator to matching element.
+    InverseHookCollection::const_iterator i = inverse_hooks_.find(index);
+    if (i == inverse_hooks_.end()) {
+        isc_throw(NoSuchHook, "hook index " << index << " is not recognised");
+    }
+
+    return (i->second);
+}
+
+// Find the index associated with a hook name.
+
+int
+ServerHooks::getIndex(const string& name) const {
+
+    // Get iterator to matching element.
+    HookCollection::const_iterator i = hooks_.find(name);
+    if (i == hooks_.end()) {
+        isc_throw(NoSuchHook, "hook name " << name << " is not recognised");
+    }
+
+    return (i->second);
+}
+
+// Return vector of hook names.  The names are not sorted - it is up to the
+// caller to perform sorting if required.
+
+vector<string>
+ServerHooks::getHookNames() const {
+
+    vector<string> names;
+    HookCollection::const_iterator i;
+    for (i = hooks_.begin(); i != hooks_.end(); ++i) {
+        names.push_back(i->first);
+    }
+
+    return (names);
+}
+
+// Return global ServerHooks object
+
+ServerHooks&
+ServerHooks::getServerHooks() {
+    static ServerHooks hooks;
+    return (hooks);
+}
+
+
+} // namespace util
+} // namespace isc
diff --git a/src/lib/hooks/server_hooks.h b/src/lib/hooks/server_hooks.h
new file mode 100644
index 0000000..c4a7ae8
--- /dev/null
+++ b/src/lib/hooks/server_hooks.h
@@ -0,0 +1,183 @@
+// Copyright (C) 2013  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 SERVER_HOOKS_H
+#define SERVER_HOOKS_H
+
+#include <exceptions/exceptions.h>
+
+#include <boost/noncopyable.hpp>
+
+#include <map>
+#include <string>
+#include <vector>
+
+namespace isc {
+namespace hooks {
+
+/// @brief Duplicate hook
+///
+/// Thrown if an attempt is made to register a hook with the same name as a
+/// previously-registered hook.
+class DuplicateHook : public Exception {
+public:
+    DuplicateHook(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
+/// @brief Invalid hook
+///
+/// Thrown if an attempt is made to get the index for an invalid hook.
+class NoSuchHook : public Exception {
+public:
+    NoSuchHook(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
+
+/// @brief Server hook collection
+///
+/// This class is used by the server-side code to register hooks - points in the
+/// server processing at which libraries can register functions (callouts) that
+/// the server will call.  These functions can modify data and so affect the
+/// processing of the server.
+///
+/// The ServerHooks class is little more than a wrapper around the std::map
+/// class.  It stores a hook, assigning to it a unique index number.  This
+/// number is then used by the server code to identify the hook being called.
+/// (Although it would be feasible to use a name as an index, using an integer
+/// will speed up the time taken to locate the callouts, which may make a
+/// difference in a frequently-executed piece of code.)
+///
+/// ServerHooks is a singleton object and is only accessible by the static
+/// method getServerHooks().
+
+class ServerHooks : public boost::noncopyable {
+public:
+
+    /// Index numbers for pre-defined hooks.
+    static const int CONTEXT_CREATE = 0;
+    static const int CONTEXT_DESTROY = 1;
+
+    /// @brief Reset to Initial State
+    ///
+    /// Resets the collection of hooks to the initial state, with just the
+    /// context_create and context_destroy hooks set.  This used during
+    /// testing to reset the global ServerHooks object; it should never be
+    /// used in production.
+    ///
+    /// @throws isc::Unexpected if the registration of the pre-defined hooks
+    ///         fails in some way.
+    void reset();
+
+    /// @brief Register a hook
+    ///
+    /// Registers a hook and returns the hook index.
+    ///
+    /// @param name Name of the hook
+    ///
+    /// @return Index of the hook, to be used in subsequent hook-related calls.
+    ///         This will be greater than or equal to zero (so allowing a
+    ///         negative value to indicate an invalid index).
+    ///
+    /// @throws DuplicateHook A hook with the same name has already been
+    ///         registered.
+    int registerHook(const std::string& name);
+
+    /// @brief Get hook name
+    ///
+    /// Returns the name of a hook given the index.  This is most likely to be
+    /// used in log messages.
+    ///
+    /// @param index Index of the hoold
+    ///
+    /// @return Name of the hook.
+    ///
+    /// @throw NoSuchHook if the hook index is invalid.
+    std::string getName(int index) const;
+
+    /// @brief Get hook index
+    ///
+    /// Returns the index of a hook.
+    ///
+    /// @param name Name of the hook
+    ///
+    /// @return Index of the hook, to be used in subsequent calls.
+    ///
+    /// @throw NoSuchHook if the hook name is unknown to the caller.
+    int getIndex(const std::string& name) const;
+
+    /// @brief Return number of hooks
+    ///
+    /// Returns the total number of hooks registered.
+    ///
+    /// @return Number of hooks registered.
+    int getCount() const {
+        return (hooks_.size());
+    }
+
+    /// @brief Get hook names
+    ///
+    /// Return list of hooks registered in the object.
+    ///
+    /// @return Vector of strings holding hook names.
+    std::vector<std::string> getHookNames() const;
+
+    /// @brief Return ServerHooks object
+    ///
+    /// Returns the global ServerHooks object.
+    ///
+    /// @return Reference to the global ServerHooks object.
+    static ServerHooks& getServerHooks();
+
+private:
+    /// @brief Constructor
+    ///
+    /// This pre-registers two hooks, context_create and context_destroy, which
+    /// are called by the server before processing a packet and after processing
+    /// for the packet has completed.  They allow the server code to allocate
+    /// and destroy per-packet context.
+    ///
+    /// The constructor is declared private to enforce the singleton nature of
+    /// the object.  A reference to the singleton is obtainable through the
+    /// getServerHooks() static method.
+    ///
+    /// @throws isc::Unexpected if the registration of the pre-defined hooks
+    ///         fails in some way.
+    ServerHooks();
+
+    /// @brief Initialize hooks
+    ///
+    /// Sets the collection of hooks to the initial state, with just the
+    /// context_create and context_destroy hooks set.  This is used during
+    /// construction.
+    ///
+    /// @throws isc::Unexpected if the registration of the pre-defined hooks
+    ///         fails in some way.
+    void initialize();
+
+    /// Useful typedefs.
+    typedef std::map<std::string, int> HookCollection;
+    typedef std::map<int, std::string> InverseHookCollection;
+
+    /// Two maps, one for name->index, the other for index->name.  (This is
+    /// simpler than using a multi-indexed container.)
+    HookCollection  hooks_;                 ///< Hook name/index collection
+    InverseHookCollection inverse_hooks_;   ///< Hook index/name collection
+};
+
+} // namespace util
+} // namespace isc
+
+#endif  // SERVER_HOOKS_H
diff --git a/src/lib/hooks/tests/Makefile.am b/src/lib/hooks/tests/Makefile.am
new file mode 100644
index 0000000..37fe238
--- /dev/null
+++ b/src/lib/hooks/tests/Makefile.am
@@ -0,0 +1,104 @@
+SUBDIRS = .
+
+AM_CPPFLAGS  = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES) $(MULTITHREADING_FLAG)
+
+# Some versions of GCC warn about some versions of Boost regarding
+# missing initializer for members in its posix_time.
+# https://svn.boost.org/trac/boost/ticket/3477
+# But older GCC compilers don't have the flag.     
+AM_CXXFLAGS  = $(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
+
+if USE_CLANGPP
+# see ../Makefile.am
+AM_CXXFLAGS += -Wno-unused-parameter
+endif
+
+# Files to clean include the file created by testing.
+CLEANFILES = *.gcno *.gcda $(builddir)/marker_file.dat
+
+TESTS_ENVIRONMENT = \
+	$(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
+
+TESTS =
+if HAVE_GTEST
+# Build shared libraries for testing.
+lib_LTLIBRARIES = libnvl.la libivl.la libfxl.la libbcl.la liblcl.la liblecl.la \
+                  libucl.la libfcl.la
+
+# No version function
+libnvl_la_SOURCES  = no_version_library.cc
+libnvl_la_CXXFLAGS = $(AM_CXXFLAGS)
+libnvl_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
+
+# Incorrect version function
+libivl_la_SOURCES  = incorrect_version_library.cc
+libivl_la_CXXFLAGS = $(AM_CXXFLAGS)
+libivl_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
+
+# All framework functions throw an exception
+libfxl_la_SOURCES = framework_exception_library.cc
+libfxl_la_CXXFLAGS = $(AM_CXXFLAGS)
+libfxl_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
+
+# The basic callout library - contains standard callouts
+libbcl_la_SOURCES  = basic_callout_library.cc
+libbcl_la_CXXFLAGS = $(AM_CXXFLAGS)
+libbcl_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
+
+# The load callout library - contains a load function
+liblcl_la_SOURCES  = load_callout_library.cc
+liblcl_la_CXXFLAGS = $(AM_CXXFLAGS)
+liblcl_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
+
+# The load error callout library - contains a load function that returns
+# an error.
+liblecl_la_SOURCES  = load_error_callout_library.cc
+liblecl_la_CXXFLAGS = $(AM_CXXFLAGS)
+liblecl_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
+
+# The unload callout library - contains an unload function that
+# creates a marker file.
+libucl_la_SOURCES  = unload_callout_library.cc
+libucl_la_CXXFLAGS = $(AM_CXXFLAGS)
+libucl_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
+
+# The full callout library - contains all three framework functions.
+libfcl_la_SOURCES  = full_callout_library.cc
+libfcl_la_CXXFLAGS = $(AM_CXXFLAGS)
+libfcl_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
+
+TESTS += run_unittests
+run_unittests_SOURCES  = run_unittests.cc
+run_unittests_SOURCES += callout_handle_unittest.cc
+run_unittests_SOURCES += callout_manager_unittest.cc
+run_unittests_SOURCES += common_test_class.h
+run_unittests_SOURCES += handles_unittest.cc
+run_unittests_SOURCES += hooks_manager_unittest.cc
+run_unittests_SOURCES += library_manager_collection_unittest.cc
+run_unittests_SOURCES += library_manager_unittest.cc
+run_unittests_SOURCES += server_hooks_unittest.cc
+
+nodist_run_unittests_SOURCES  = marker_file.h
+nodist_run_unittests_SOURCES += test_libraries.h
+
+run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+run_unittests_LDFLAGS  = $(AM_LDFLAGS)  $(GTEST_LDFLAGS)
+if USE_STATIC_LINK
+# If specified, only link unit tests static - the test libraries must be
+# build as shared libraries.
+run_unittests_LDFLAGS  += -static
+endif
+
+run_unittests_LDADD    = $(AM_LDADD)    $(GTEST_LDADD)
+
+run_unittests_LDADD += $(top_builddir)/src/lib/hooks/libb10-hooks.la
+run_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
+run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
+endif
+
+noinst_PROGRAMS = $(TESTS)
+
+EXTRA_DIST = marker_file.h.in test_libraries.h.in
diff --git a/src/lib/hooks/tests/basic_callout_library.cc b/src/lib/hooks/tests/basic_callout_library.cc
new file mode 100644
index 0000000..253de80
--- /dev/null
+++ b/src/lib/hooks/tests/basic_callout_library.cc
@@ -0,0 +1,115 @@
+// Copyright (C) 2013  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.
+
+/// @file
+/// @brief Basic callout library
+///
+/// This is source of a test library for various test (LibraryManager and
+/// HooksManager).  The characteristics of the library produced from this
+/// file are:
+///
+/// - Only the "version" framework function is supplied.
+///
+/// - A context_create callout is supplied.
+///
+/// - Three "standard" callouts are supplied corresponding to the hooks
+///   "hookpt_one", "hookpt_two", "hookpt_three".  All do some trivial calculations
+///   on the arguments supplied to it and the context variables, returning
+///   intermediate results through the "result" argument. The result of
+///   executing all four callouts in order is:
+///
+///   @f[ (10 + data_1) * data_2 - data_3 @f]
+///
+///   ...where data_1, data_2 and data_3 are the values passed in arguments of
+///   the same name to the three callouts (data_1 passed to hookpt_one, data_2 to
+///   hookpt_two etc.) and the result is returned in the argument "result".
+
+#include <hooks/hooks.h>
+#include <fstream>
+
+using namespace isc::hooks;
+using namespace std;
+
+extern "C" {
+
+// Callouts.  All return their result through the "result" argument.
+
+int
+context_create(CalloutHandle& handle) {
+    handle.setContext("result", static_cast<int>(10));
+    handle.setArgument("result", static_cast<int>(10));
+    return (0);
+}
+
+// First callout adds the passed "data_1" argument to the initialized context
+// value of 10. (Note that the value set by context_create is accessed through
+// context and not the argument, so checking that context is correctly passed
+// between callouts in the same library.)
+
+int
+hookpt_one(CalloutHandle& handle) {
+    int data;
+    handle.getArgument("data_1", data);
+
+    int result;
+    handle.getArgument("result", result);
+
+    result += data;
+    handle.setArgument("result", result);
+
+    return (0);
+}
+
+// Second callout multiplies the current context value by the "data_2"
+// argument.
+
+int
+hookpt_two(CalloutHandle& handle) {
+    int data;
+    handle.getArgument("data_2", data);
+
+    int result;
+    handle.getArgument("result", result);
+
+    result *= data;
+    handle.setArgument("result", result);
+
+    return (0);
+}
+
+// Final callout subtracts the result in "data_3".
+
+int
+hookpt_three(CalloutHandle& handle) {
+    int data;
+    handle.getArgument("data_3", data);
+
+    int result;
+    handle.getArgument("result", result);
+
+    result -= data;
+    handle.setArgument("result", result);
+
+    return (0);
+}
+
+// Framework functions.  Only version() is supplied here.
+
+int
+version() {
+    return (BIND10_HOOKS_VERSION);
+}
+
+};
+
diff --git a/src/lib/hooks/tests/callout_handle_unittest.cc b/src/lib/hooks/tests/callout_handle_unittest.cc
new file mode 100644
index 0000000..b24a4cf
--- /dev/null
+++ b/src/lib/hooks/tests/callout_handle_unittest.cc
@@ -0,0 +1,329 @@
+// Copyright (C) 2013  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 <hooks/callout_handle.h>
+#include <hooks/callout_manager.h>
+#include <hooks/library_handle.h>
+#include <hooks/server_hooks.h>
+
+#include <boost/shared_ptr.hpp>
+
+#include <gtest/gtest.h>
+
+using namespace isc::hooks;
+using namespace std;
+
+namespace {
+
+/// @file
+/// @brief Holds the CalloutHandle argument tests
+///
+/// Additional testing of the CalloutHandle - together with the interaction
+/// of the LibraryHandle - is done in the handles_unittests set of tests.
+
+class CalloutHandleTest : public ::testing::Test {
+public:
+
+    /// @brief Constructor
+    ///
+    /// Sets up a callout manager to be referenced by the CalloutHandle in
+    /// these tests. (The "4" for the number of libraries in the
+    /// CalloutManager is arbitrary - it is not used in these tests.)
+    CalloutHandleTest() : manager_(new CalloutManager(4))
+    {}
+
+    /// Obtain hook manager
+    boost::shared_ptr<CalloutManager>& getCalloutManager() {
+        return (manager_);
+    }
+
+private:
+    /// Callout manager accessed by this CalloutHandle.
+    boost::shared_ptr<CalloutManager> manager_;
+};
+
+// *** Argument Tests ***
+//
+// The first set of tests check that the CalloutHandle can store and retrieve
+// arguments.  These are very similar to the LibraryHandle context tests.
+
+// Test that we can store multiple values of the same type and that they
+// are distinct.
+
+TEST_F(CalloutHandleTest, ArgumentDistinctSimpleType) {
+    CalloutHandle handle(getCalloutManager());
+
+    // Store and retrieve an int (random value).
+    int a = 42;
+    handle.setArgument("integer1", a);
+    EXPECT_EQ(42, a);
+
+    int b = 0;
+    handle.getArgument("integer1", b);
+    EXPECT_EQ(42, b);
+
+    // Add another integer (another random value).
+    int c = 142;
+    handle.setArgument("integer2", c);
+    EXPECT_EQ(142, c);
+
+    int d = 0;
+    handle.getArgument("integer2", d);
+    EXPECT_EQ(142, d);
+
+    // Add a short (random value).
+    short e = -81;
+    handle.setArgument("short", e);
+    EXPECT_EQ(-81, e);
+
+    short f = 0;
+    handle.getArgument("short", f);
+    EXPECT_EQ(-81, f);
+}
+
+// Test that trying to get an unknown argument throws an exception.
+
+TEST_F(CalloutHandleTest, ArgumentUnknownName) {
+    CalloutHandle handle(getCalloutManager());
+
+    // Set an integer
+    int a = 42;
+    handle.setArgument("integer1", a);
+    EXPECT_EQ(42, a);
+
+    // Check we can retrieve it
+    int b = 0;
+    handle.getArgument("integer1", b);
+    EXPECT_EQ(42, b);
+
+    // Check that getting an unknown name throws an exception.
+    int c = 0;
+    EXPECT_THROW(handle.getArgument("unknown", c), NoSuchArgument);
+}
+
+// Test that trying to get an argument with an incorrect type throws an
+// exception.
+
+TEST_F(CalloutHandleTest, ArgumentIncorrectType) {
+    CalloutHandle handle(getCalloutManager());
+
+    // Set an integer
+    int a = 42;
+    handle.setArgument("integer1", a);
+    EXPECT_EQ(42, a);
+
+    // Check we can retrieve it
+    long b = 0;
+    EXPECT_THROW(handle.getArgument("integer1", b), boost::bad_any_cast);
+}
+
+// Now try with some very complex types.  The types cannot be defined within
+// the function and they should contain a copy constructor.  For this reason,
+// a simple "struct" is used.
+
+struct Alpha {
+    int a;
+    int b;
+    Alpha(int first = 0, int second = 0) : a(first), b(second) {}
+};
+
+struct Beta {
+    int c;
+    int d;
+    Beta(int first = 0, int second = 0) : c(first), d(second) {}
+};
+
+TEST_F(CalloutHandleTest, ComplexTypes) {
+    CalloutHandle handle(getCalloutManager());
+
+    // Declare two variables of different (complex) types. (Note as to the
+    // variable names: aleph and beth are the first two letters of the Hebrew
+    // alphabet.)
+    Alpha aleph(1, 2);
+    EXPECT_EQ(1, aleph.a);
+    EXPECT_EQ(2, aleph.b);
+    handle.setArgument("aleph", aleph);
+
+    Beta beth(11, 22);
+    EXPECT_EQ(11, beth.c);
+    EXPECT_EQ(22, beth.d);
+    handle.setArgument("beth", beth);
+
+    // Ensure we can extract the data correctly.
+    Alpha aleph2;
+    EXPECT_EQ(0, aleph2.a);
+    EXPECT_EQ(0, aleph2.b);
+    handle.getArgument("aleph", aleph2);
+    EXPECT_EQ(1, aleph2.a);
+    EXPECT_EQ(2, aleph2.b);
+
+    Beta beth2;
+    EXPECT_EQ(0, beth2.c);
+    EXPECT_EQ(0, beth2.d);
+    handle.getArgument("beth", beth2);
+    EXPECT_EQ(11, beth2.c);
+    EXPECT_EQ(22, beth2.d);
+
+    // Ensure that complex types also thrown an exception if we attempt to
+    // get a context element of the wrong type.
+    EXPECT_THROW(handle.getArgument("aleph", beth), boost::bad_any_cast);
+}
+
+// Check that the context can store pointers. And also check that it respects
+// that a "pointer to X" is not the same as a "pointer to const X".
+
+TEST_F(CalloutHandleTest, PointerTypes) {
+    CalloutHandle handle(getCalloutManager());
+
+    // Declare a couple of variables, const and non-const.
+    Alpha aleph(5, 10);
+    const Beta beth(15, 20);
+
+    Alpha* pa = ℵ
+    const Beta* pcb = ℶ
+
+    // Check pointers can be set and retrieved OK.
+    handle.setArgument("non_const_pointer", pa);
+    handle.setArgument("const_pointer", pcb);
+
+    Alpha* pa2 = 0;
+    handle.getArgument("non_const_pointer", pa2);
+    EXPECT_TRUE(pa == pa2);
+
+    const Beta* pcb2 = 0;
+    handle.getArgument("const_pointer", pcb2);
+    EXPECT_TRUE(pcb == pcb2);
+
+    // Check that the "const" is protected in the context.
+    const Alpha* pca3;
+    EXPECT_THROW(handle.getArgument("non_const_pointer", pca3),
+                 boost::bad_any_cast);
+
+    Beta* pb3;
+    EXPECT_THROW(handle.getArgument("const_pointer", pb3),
+                 boost::bad_any_cast);
+}
+
+// Check that we can get the names of the arguments.
+
+TEST_F(CalloutHandleTest, ContextItemNames) {
+    CalloutHandle handle(getCalloutManager());
+
+    vector<string> expected_names;
+
+    expected_names.push_back("faith");
+    handle.setArgument("faith", 42);
+    expected_names.push_back("hope");
+    handle.setArgument("hope", 43);
+    expected_names.push_back("charity");
+    handle.setArgument("charity", 44);
+
+    // Get the names and check against the expected names.  We'll sort
+    // both arrays to simplify the checking.
+    vector<string> actual_names = handle.getArgumentNames();
+
+    sort(actual_names.begin(), actual_names.end());
+    sort(expected_names.begin(), expected_names.end());
+    EXPECT_TRUE(expected_names == actual_names);
+}
+
+// Test that we can delete an argument.
+
+TEST_F(CalloutHandleTest, DeleteArgument) {
+    CalloutHandle handle(getCalloutManager());
+
+    int one = 1;
+    int two = 2;
+    int three = 3;
+    int four = 4;
+    int value;      // Return value
+
+    handle.setArgument("one", one);
+    handle.setArgument("two", two);
+    handle.setArgument("three", three);
+    handle.setArgument("four", four);
+
+    // Delete "one".
+    handle.getArgument("one", value);
+    EXPECT_EQ(1, value);
+    handle.deleteArgument("one");
+
+    EXPECT_THROW(handle.getArgument("one", value), NoSuchArgument);
+    handle.getArgument("two", value);
+    EXPECT_EQ(2, value);
+    handle.getArgument("three", value);
+    EXPECT_EQ(3, value);
+    handle.getArgument("four", value);
+    EXPECT_EQ(4, value);
+
+    // Delete "three".
+    handle.getArgument("three", value);
+    EXPECT_EQ(3, value);
+    handle.deleteArgument("three");
+
+    EXPECT_THROW(handle.getArgument("one", value), NoSuchArgument);
+    handle.getArgument("two", value);
+    EXPECT_EQ(2, value);
+    EXPECT_THROW(handle.getArgument("three", value), NoSuchArgument);
+    handle.getArgument("four", value);
+    EXPECT_EQ(4, value);
+}
+
+// Test that we can delete all arguments.
+
+TEST_F(CalloutHandleTest, DeleteAllArguments) {
+    CalloutHandle handle(getCalloutManager());
+
+    int one = 1;
+    int two = 2;
+    int three = 3;
+    int four = 4;
+    int value;      // Return value
+
+    // Set the arguments.  The previous test verifies that this works.
+    handle.setArgument("one", one);
+    handle.setArgument("two", two);
+    handle.setArgument("three", three);
+    handle.setArgument("four", four);
+
+    // Delete all arguments...
+    handle.deleteAllArguments();
+
+    // ... and check that none are left.
+    EXPECT_THROW(handle.getArgument("one", value), NoSuchArgument);
+    EXPECT_THROW(handle.getArgument("two", value), NoSuchArgument);
+    EXPECT_THROW(handle.getArgument("three", value), NoSuchArgument);
+    EXPECT_THROW(handle.getArgument("four", value), NoSuchArgument);
+}
+
+// Test the "skip" flag.
+
+TEST_F(CalloutHandleTest, SkipFlag) {
+    CalloutHandle handle(getCalloutManager());
+
+    // Should be false on construction.
+    EXPECT_FALSE(handle.getSkip());
+
+    handle.setSkip(true);
+    EXPECT_TRUE(handle.getSkip());
+
+    handle.setSkip(false);
+    EXPECT_FALSE(handle.getSkip());
+}
+
+// Further tests of the "skip" flag and tests of getting the name of the
+// hook to which the current callout is attached is in the "handles_unittest"
+// module.
+
+} // Anonymous namespace
diff --git a/src/lib/hooks/tests/callout_manager_unittest.cc b/src/lib/hooks/tests/callout_manager_unittest.cc
new file mode 100644
index 0000000..c3f3b1d
--- /dev/null
+++ b/src/lib/hooks/tests/callout_manager_unittest.cc
@@ -0,0 +1,882 @@
+// Copyright (C) 2013  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 <hooks/callout_handle.h>
+#include <hooks/callout_manager.h>
+#include <hooks/library_handle.h>
+#include <hooks/server_hooks.h>
+
+#include <boost/scoped_ptr.hpp>
+#include <gtest/gtest.h>
+
+#include <algorithm>
+#include <climits>
+#include <string>
+#include <vector>
+
+/// @file
+/// @brief CalloutManager and LibraryHandle tests
+///
+/// These set of tests check the CalloutManager and LibraryHandle.  They are
+/// together in the same file because the LibraryHandle is little more than a
+/// restricted interface to the CalloutManager, and a lot of the support
+/// structure for the tests is common.
+
+using namespace isc;
+using namespace isc::hooks;
+using namespace std;
+
+namespace {
+
+class CalloutManagerTest : public ::testing::Test {
+public:
+    /// @brief Constructor
+    ///
+    /// Sets up a collection of three LibraryHandle objects to use in the test.
+    CalloutManagerTest() {
+
+        // Set up the server hooks.  There is sone singleton for all tests,
+        // so reset it and explicitly set up the hooks for the test.
+        ServerHooks& hooks = ServerHooks::getServerHooks();
+        hooks.reset();
+        alpha_index_ = hooks.registerHook("alpha");
+        beta_index_ = hooks.registerHook("beta");
+        gamma_index_ = hooks.registerHook("gamma");
+        delta_index_ = hooks.registerHook("delta");
+
+        // Set up the callout manager with these hooks.  Assume a maximum of
+        // four libraries.
+        callout_manager_.reset(new CalloutManager(10));
+
+        // Set up the callout handle.
+        callout_handle_.reset(new CalloutHandle(callout_manager_));
+
+        // Initialize the static variable.
+        callout_value_ = 0;
+    }
+
+    /// @brief Return the callout handle
+    CalloutHandle& getCalloutHandle() {
+        return (*callout_handle_);
+    }
+
+    /// @brief Return the callout manager
+    boost::shared_ptr<CalloutManager> getCalloutManager() {
+        return (callout_manager_);
+    }
+
+    /// Static variable used for accumulating information
+    static int callout_value_;
+
+    /// Hook indexes.  These are somewhat ubiquitous, so are made public for
+    /// ease of reference instead of being accessible by a function.
+    int alpha_index_;
+    int beta_index_;
+    int gamma_index_;
+    int delta_index_;
+
+private:
+    /// Callout handle used in calls
+    boost::shared_ptr<CalloutHandle> callout_handle_;
+
+    /// Callout manager used for the test
+    boost::shared_ptr<CalloutManager> callout_manager_;
+};
+
+// Definition of the static variable.
+int CalloutManagerTest::callout_value_ = 0;
+
+// Callout definitions
+//
+// The callouts defined here are structured in such a way that it is possible
+// to determine the order in which they are called and whether they are called
+// at all. The method used is simple - after a sequence of callouts, the digits
+// in the value, reading left to right, determines the order of the callouts
+// called.  For example, callout one followed by two followed by three followed
+// by two followed by one results in a value of 12321.
+//
+// Functions return a zero to indicate success.
+
+extern "C" {
+int callout_general(int number) {
+    CalloutManagerTest::callout_value_ =
+        10 * CalloutManagerTest::callout_value_ + number;
+    return (0);
+}
+
+int callout_one(CalloutHandle&) {
+    return (callout_general(1));
+}
+
+int callout_two(CalloutHandle&) {
+    return (callout_general(2));
+}
+
+int callout_three(CalloutHandle&) {
+    return (callout_general(3));
+}
+
+int callout_four(CalloutHandle&) {
+    return (callout_general(4));
+}
+
+int callout_five(CalloutHandle&) {
+    return (callout_general(5));
+}
+
+int callout_six(CalloutHandle&) {
+    return (callout_general(6));
+}
+
+int callout_seven(CalloutHandle&) {
+    return (callout_general(7));
+}
+
+// The next functions are duplicates of some of the above, but return an error.
+
+int callout_one_error(CalloutHandle& handle) {
+    (void) callout_one(handle);
+    return (1);
+}
+
+int callout_two_error(CalloutHandle& handle) {
+    (void) callout_two(handle);
+    return (1);
+}
+
+int callout_three_error(CalloutHandle& handle) {
+    (void) callout_three(handle);
+    return (1);
+}
+
+int callout_four_error(CalloutHandle& handle) {
+    (void) callout_four(handle);
+    return (1);
+}
+
+};  // extern "C"
+
+// *** Callout Tests ***
+//
+// The next set of tests check that callouts can be called.
+
+// Constructor - check that we trap bad parameters.
+
+TEST_F(CalloutManagerTest, BadConstructorParameters) {
+    boost::scoped_ptr<CalloutManager> cm;
+
+    // Invalid number of libraries
+    EXPECT_THROW(cm.reset(new CalloutManager(-1)), BadValue);
+}
+
+// Check the number of libraries is reported successfully.
+
+TEST_F(CalloutManagerTest, NumberOfLibraries) {
+    boost::scoped_ptr<CalloutManager> cm;
+
+    // Check two valid values of number of libraries to ensure that the
+    // GetNumLibraries() returns the value set.
+    EXPECT_NO_THROW(cm.reset(new CalloutManager()));
+    EXPECT_EQ(0, cm->getNumLibraries());
+
+    EXPECT_NO_THROW(cm.reset(new CalloutManager(0)));
+    EXPECT_EQ(0, cm->getNumLibraries());
+
+    EXPECT_NO_THROW(cm.reset(new CalloutManager(4)));
+    EXPECT_EQ(4, cm->getNumLibraries());
+
+    EXPECT_NO_THROW(cm.reset(new CalloutManager(42)));
+    EXPECT_EQ(42, cm->getNumLibraries());
+}
+
+// Check that we can only set the current library index to the correct values.
+
+TEST_F(CalloutManagerTest, CheckLibraryIndex) {
+    // Check valid indexes.  As the callout manager is sized for 10 libraries,
+    // we expect:
+    //
+    // -1 to be valid as it is the standard "invalid" value.
+    // 0 to be valid for the pre-user library callouts
+    // 1-10 to be valid for the user-library callouts
+    // INT_MAX to be valid for the post-user library callouts
+    //
+    // All other values to be invalid.
+    for (int i = -1; i < 11; ++i) {
+        EXPECT_NO_THROW(getCalloutManager()->setLibraryIndex(i));
+        EXPECT_EQ(i, getCalloutManager()->getLibraryIndex());
+    }
+    EXPECT_NO_THROW(getCalloutManager()->setLibraryIndex(INT_MAX));
+    EXPECT_EQ(INT_MAX, getCalloutManager()->getLibraryIndex());
+
+    // Check invalid ones
+    EXPECT_THROW(getCalloutManager()->setLibraryIndex(-2), NoSuchLibrary);
+    EXPECT_THROW(getCalloutManager()->setLibraryIndex(11), NoSuchLibrary);
+}
+
+// Check that we can only register callouts on valid hook names.
+
+TEST_F(CalloutManagerTest, ValidHookNames) {
+    getCalloutManager()->setLibraryIndex(0);
+    EXPECT_NO_THROW(getCalloutManager()->registerCallout("alpha", callout_one));
+    EXPECT_THROW(getCalloutManager()->registerCallout("unknown", callout_one),
+                                                      NoSuchHook);
+}
+
+
+// Check we can register callouts appropriately.
+
+TEST_F(CalloutManagerTest, RegisterCallout) {
+    // Ensure that no callouts are attached to any of the hooks.
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
+
+    // Set up so that hooks "alpha" and "beta" have callouts attached from a
+    // different libraries.
+    getCalloutManager()->setLibraryIndex(0);
+    getCalloutManager()->registerCallout("alpha", callout_one);
+    getCalloutManager()->setLibraryIndex(1);
+    getCalloutManager()->registerCallout("beta", callout_two);
+
+    // Check all is as expected.
+    EXPECT_TRUE(getCalloutManager()->calloutsPresent(alpha_index_));
+    EXPECT_TRUE(getCalloutManager()->calloutsPresent(beta_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
+
+    // Check that calling the callouts returns as expected. (This is also a
+    // test of the callCallouts method.)
+    callout_value_ = 0;
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(1, callout_value_);
+
+    callout_value_ = 0;
+    getCalloutManager()->callCallouts(beta_index_, getCalloutHandle());
+    EXPECT_EQ(2, callout_value_);
+
+    // Register some more callouts from different libraries on hook "alpha".
+    getCalloutManager()->setLibraryIndex(2);
+    getCalloutManager()->registerCallout("alpha", callout_three);
+    getCalloutManager()->registerCallout("alpha", callout_four);
+    getCalloutManager()->setLibraryIndex(3);
+    getCalloutManager()->registerCallout("alpha", callout_five);
+
+    // Check it is as expected.
+    callout_value_ = 0;
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(1345, callout_value_);
+
+    // ... and check the additional callouts were not registered on the "beta"
+    // hook.
+    callout_value_ = 0;
+    getCalloutManager()->callCallouts(beta_index_, getCalloutHandle());
+    EXPECT_EQ(2, callout_value_);
+
+    // Add another callout to hook "alpha" from library index 2 - this should
+    // appear at the end of the callout list for that library.
+    getCalloutManager()->setLibraryIndex(2);
+    getCalloutManager()->registerCallout("alpha", callout_six);
+    callout_value_ = 0;
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(13465, callout_value_);
+
+    // Add a callout from library index 1 - this should appear between the
+    // callouts from library index 0 and linrary index 2.
+    getCalloutManager()->setLibraryIndex(1);
+    getCalloutManager()->registerCallout("alpha", callout_seven);
+    callout_value_ = 0;
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(173465, callout_value_);
+}
+
+// Check the "calloutsPresent()" method.
+
+TEST_F(CalloutManagerTest, CalloutsPresent) {
+    // Ensure that no callouts are attached to any of the hooks.
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
+
+    // Set up so that hooks "alpha", "beta" and "delta" have callouts attached
+    // to them, and callout  "gamma" does not. (In the statements below, the
+    // exact callouts attached to a hook are not relevant - only the fact
+    // that some callouts are).  Chose the libraries for which the callouts
+    // are registered randomly.
+    getCalloutManager()->setLibraryIndex(0);
+    getCalloutManager()->registerCallout("alpha", callout_one);
+
+    getCalloutManager()->setLibraryIndex(1);
+    getCalloutManager()->registerCallout("alpha", callout_two);
+    getCalloutManager()->registerCallout("beta", callout_two);
+
+    getCalloutManager()->setLibraryIndex(3);
+    getCalloutManager()->registerCallout("alpha", callout_three);
+    getCalloutManager()->registerCallout("delta", callout_four);
+
+    // Check all is as expected.
+    EXPECT_TRUE(getCalloutManager()->calloutsPresent(alpha_index_));
+    EXPECT_TRUE(getCalloutManager()->calloutsPresent(beta_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
+    EXPECT_TRUE(getCalloutManager()->calloutsPresent(delta_index_));
+
+    // Check we fail on an invalid hook index.
+    EXPECT_THROW(getCalloutManager()->calloutsPresent(42), NoSuchHook);
+    EXPECT_THROW(getCalloutManager()->calloutsPresent(-1), NoSuchHook);
+}
+
+// Test that calling a hook with no callouts on it returns success.
+
+TEST_F(CalloutManagerTest, CallNoCallouts) {
+    // Ensure that no callouts are attached to any of the hooks.
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
+
+    // Call the callouts on an arbitrary hook and ensure that nothing happens.
+    callout_value_ = 475;
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(475, callout_value_); // Unchanged
+}
+
+// Test that the callouts are called in the correct order (i.e. the callouts
+// from the first library in the order they were registered, then the callouts
+// from the second library in the order they were registered etc.)
+
+TEST_F(CalloutManagerTest, CallCalloutsSuccess) {
+    // Ensure that no callouts are attached to any of the hooks.
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
+
+    // Each library contributes one callout on hook "alpha".
+    callout_value_ = 0;
+    getCalloutManager()->setLibraryIndex(1);
+    getCalloutManager()->registerCallout("alpha", callout_one);
+    getCalloutManager()->setLibraryIndex(1);
+    getCalloutManager()->registerCallout("alpha", callout_two);
+    getCalloutManager()->setLibraryIndex(2);
+    getCalloutManager()->registerCallout("alpha", callout_three);
+    getCalloutManager()->setLibraryIndex(3);
+    getCalloutManager()->registerCallout("alpha", callout_four);
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(1234, callout_value_);
+
+    // Do a random selection of callouts on hook "beta".
+    callout_value_ = 0;
+    getCalloutManager()->setLibraryIndex(0);
+    getCalloutManager()->registerCallout("beta", callout_one);
+    getCalloutManager()->registerCallout("beta", callout_three);
+    getCalloutManager()->setLibraryIndex(1);
+    getCalloutManager()->registerCallout("beta", callout_two);
+    getCalloutManager()->setLibraryIndex(3);
+    getCalloutManager()->registerCallout("beta", callout_four);
+    getCalloutManager()->callCallouts(beta_index_, getCalloutHandle());
+    EXPECT_EQ(1324, callout_value_);
+
+    // Ensure that calling the callouts on a hook with no callouts works.
+    callout_value_ = 0;
+    getCalloutManager()->callCallouts(gamma_index_, getCalloutHandle());
+    EXPECT_EQ(0, callout_value_);
+}
+
+// Test that the callouts are called in order, but that callouts occurring
+// after a callout that returns an error are not called.
+//
+// (Note: in this test, the callouts that return an error set the value of
+// callout_value_ before they return the error code.)
+
+TEST_F(CalloutManagerTest, CallCalloutsError) {
+    // Ensure that no callouts are attached to any of the hooks.
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
+
+    // Each library contributing one callout on hook "alpha". The first callout
+    // returns an error (after adding its value to the result).
+    callout_value_ = 0;
+    getCalloutManager()->setLibraryIndex(0);
+    getCalloutManager()->registerCallout("alpha", callout_one_error);
+    getCalloutManager()->setLibraryIndex(1);
+    getCalloutManager()->registerCallout("alpha", callout_two);
+    getCalloutManager()->setLibraryIndex(2);
+    getCalloutManager()->registerCallout("alpha", callout_three);
+    getCalloutManager()->setLibraryIndex(3);
+    getCalloutManager()->registerCallout("alpha", callout_four);
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(1234, callout_value_);
+
+    // Each library contributing multiple callouts on hook "beta". The last
+    // callout on the first library returns an error.
+    callout_value_ = 0;
+    getCalloutManager()->setLibraryIndex(0);
+    getCalloutManager()->registerCallout("beta", callout_one);
+    getCalloutManager()->registerCallout("beta", callout_one_error);
+    getCalloutManager()->setLibraryIndex(1);
+    getCalloutManager()->registerCallout("beta", callout_two);
+    getCalloutManager()->registerCallout("beta", callout_two);
+    getCalloutManager()->registerCallout("beta", callout_three);
+    getCalloutManager()->registerCallout("beta", callout_three);
+    getCalloutManager()->setLibraryIndex(3);
+    getCalloutManager()->registerCallout("beta", callout_four);
+    getCalloutManager()->registerCallout("beta", callout_four);
+    getCalloutManager()->callCallouts(beta_index_, getCalloutHandle());
+    EXPECT_EQ(11223344, callout_value_);
+
+    // A callout in a random position in the callout list returns an error.
+    callout_value_ = 0;
+    getCalloutManager()->setLibraryIndex(0);
+    getCalloutManager()->registerCallout("gamma", callout_one);
+    getCalloutManager()->registerCallout("gamma", callout_one);
+    getCalloutManager()->setLibraryIndex(1);
+    getCalloutManager()->registerCallout("gamma", callout_two);
+    getCalloutManager()->registerCallout("gamma", callout_two);
+    getCalloutManager()->setLibraryIndex(3);
+    getCalloutManager()->registerCallout("gamma", callout_four_error);
+    getCalloutManager()->registerCallout("gamma", callout_four);
+    getCalloutManager()->callCallouts(gamma_index_, getCalloutHandle());
+    EXPECT_EQ(112244, callout_value_);
+
+    // The last callout on a hook returns an error.
+    callout_value_ = 0;
+    getCalloutManager()->setLibraryIndex(0);
+    getCalloutManager()->registerCallout("delta", callout_one);
+    getCalloutManager()->registerCallout("delta", callout_one);
+    getCalloutManager()->setLibraryIndex(1);
+    getCalloutManager()->registerCallout("delta", callout_two);
+    getCalloutManager()->registerCallout("delta", callout_two);
+    getCalloutManager()->setLibraryIndex(2);
+    getCalloutManager()->registerCallout("delta", callout_three);
+    getCalloutManager()->registerCallout("delta", callout_three);
+    getCalloutManager()->setLibraryIndex(3);
+    getCalloutManager()->registerCallout("delta", callout_four);
+    getCalloutManager()->registerCallout("delta", callout_four_error);
+    getCalloutManager()->callCallouts(delta_index_, getCalloutHandle());
+    EXPECT_EQ(11223344, callout_value_);
+}
+
+// Now test that we can deregister a single callout on a hook.
+
+TEST_F(CalloutManagerTest, DeregisterSingleCallout) {
+    // Ensure that no callouts are attached to any of the hooks.
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
+
+    // Add a callout to hook "alpha" and check it is added correctly.
+    callout_value_ = 0;
+    getCalloutManager()->setLibraryIndex(0);
+    getCalloutManager()->registerCallout("alpha", callout_two);
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(2, callout_value_);
+
+    // Remove it and check that the no callouts are present.  We have to reset
+    // the current library index here as it was invalidated by the call
+    // to callCallouts().
+    getCalloutManager()->setLibraryIndex(0);
+    EXPECT_TRUE(getCalloutManager()->calloutsPresent(alpha_index_));
+    EXPECT_TRUE(getCalloutManager()->deregisterCallout("alpha", callout_two));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
+}
+
+// Now test that we can deregister a single callout on a hook that has multiple
+// callouts from the same library.
+
+TEST_F(CalloutManagerTest, DeregisterSingleCalloutSameLibrary) {
+    // Ensure that no callouts are attached to any of the hooks.
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
+
+    // Add multiple callouts to hook "alpha".
+    callout_value_ = 0;
+    getCalloutManager()->setLibraryIndex(0);
+    getCalloutManager()->registerCallout("alpha", callout_one);
+    getCalloutManager()->registerCallout("alpha", callout_two);
+    getCalloutManager()->registerCallout("alpha", callout_three);
+    getCalloutManager()->registerCallout("alpha", callout_four);
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(1234, callout_value_);
+
+    // Remove the callout_two callout.  We have to reset the current library
+    // index here as it was invalidated by the call to callCallouts().
+    getCalloutManager()->setLibraryIndex(0);
+    EXPECT_TRUE(getCalloutManager()->deregisterCallout("alpha", callout_two));
+    callout_value_ = 0;
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(134, callout_value_);
+
+    // Try removing it again.
+    getCalloutManager()->setLibraryIndex(0);
+    EXPECT_FALSE(getCalloutManager()->deregisterCallout("alpha", callout_two));
+    callout_value_ = 0;
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(134, callout_value_);
+
+}
+
+// Check we can deregister multiple callouts from the same library.
+
+TEST_F(CalloutManagerTest, DeregisterMultipleCalloutsSameLibrary) {
+    // Ensure that no callouts are attached to any of the hooks.
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
+
+    // Each library contributes one callout on hook "alpha".
+    callout_value_ = 0;
+    getCalloutManager()->setLibraryIndex(0);
+    getCalloutManager()->registerCallout("alpha", callout_one);
+    getCalloutManager()->registerCallout("alpha", callout_two);
+    getCalloutManager()->registerCallout("alpha", callout_one);
+    getCalloutManager()->registerCallout("alpha", callout_two);
+    getCalloutManager()->registerCallout("alpha", callout_three);
+    getCalloutManager()->registerCallout("alpha", callout_four);
+    getCalloutManager()->registerCallout("alpha", callout_three);
+    getCalloutManager()->registerCallout("alpha", callout_four);
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(12123434, callout_value_);
+
+    // Remove the callout_two callouts.  We have to reset the current library
+    // index here as it was invalidated by the call to callCallouts().
+    getCalloutManager()->setLibraryIndex(0);
+    EXPECT_TRUE(getCalloutManager()->deregisterCallout("alpha", callout_two));
+    callout_value_ = 0;
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(113434, callout_value_);
+
+    // Try removing multiple callouts that includes one at the end of the
+    // list of callouts.
+    getCalloutManager()->setLibraryIndex(0);
+    EXPECT_TRUE(getCalloutManager()->deregisterCallout("alpha", callout_four));
+    callout_value_ = 0;
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(1133, callout_value_);
+
+    // ... and from the start.
+    getCalloutManager()->setLibraryIndex(0);
+    EXPECT_TRUE(getCalloutManager()->deregisterCallout("alpha", callout_one));
+    callout_value_ = 0;
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(33, callout_value_);
+
+    // ... and the remaining callouts.
+    getCalloutManager()->setLibraryIndex(0);
+    EXPECT_TRUE(getCalloutManager()->deregisterCallout("alpha", callout_three));
+    callout_value_ = 0;
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(0, callout_value_);
+
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
+}
+
+// Check we can deregister multiple callouts from multiple libraries.
+
+TEST_F(CalloutManagerTest, DeregisterMultipleCalloutsMultipleLibraries) {
+    // Ensure that no callouts are attached to any of the hooks.
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
+
+    // Each library contributes two callouts to hook "alpha".
+    callout_value_ = 0;
+    getCalloutManager()->setLibraryIndex(0);
+    getCalloutManager()->registerCallout("alpha", callout_one);
+    getCalloutManager()->registerCallout("alpha", callout_two);
+    getCalloutManager()->setLibraryIndex(1);
+    getCalloutManager()->registerCallout("alpha", callout_three);
+    getCalloutManager()->registerCallout("alpha", callout_four);
+    getCalloutManager()->setLibraryIndex(2);
+    getCalloutManager()->registerCallout("alpha", callout_five);
+    getCalloutManager()->registerCallout("alpha", callout_two);
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(123452, callout_value_);
+
+    // Remove the callout_two callout from library 0.  It should not affect
+    // the second callout_two callout registered by library 2.
+    getCalloutManager()->setLibraryIndex(0);
+    EXPECT_TRUE(getCalloutManager()->deregisterCallout("alpha", callout_two));
+    callout_value_ = 0;
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(13452, callout_value_);
+}
+
+// Check we can deregister all callouts from a single library.
+
+TEST_F(CalloutManagerTest, DeregisterAllCallouts) {
+    // Ensure that no callouts are attached to hook one.
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
+
+    // Each library contributes two callouts to hook "alpha".
+    callout_value_ = 0;
+    getCalloutManager()->setLibraryIndex(0);
+    getCalloutManager()->registerCallout("alpha", callout_one);
+    getCalloutManager()->registerCallout("alpha", callout_two);
+    getCalloutManager()->setLibraryIndex(1);
+    getCalloutManager()->registerCallout("alpha", callout_three);
+    getCalloutManager()->registerCallout("alpha", callout_four);
+    getCalloutManager()->setLibraryIndex(2);
+    getCalloutManager()->registerCallout("alpha", callout_five);
+    getCalloutManager()->registerCallout("alpha", callout_six);
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(123456, callout_value_);
+
+    // Remove all callouts from library index 1.
+    getCalloutManager()->setLibraryIndex(1);
+    EXPECT_TRUE(getCalloutManager()->deregisterAllCallouts("alpha"));
+    callout_value_ = 0;
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(1256, callout_value_);
+
+    // Remove all callouts from library index 2.
+    getCalloutManager()->setLibraryIndex(2);
+    EXPECT_TRUE(getCalloutManager()->deregisterAllCallouts("alpha"));
+    callout_value_ = 0;
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(12, callout_value_);
+}
+
+// Check that we can register/deregister callouts on different libraries
+// and different hooks, and that the callout instances are regarded as
+// unique and do not affect one another.
+
+TEST_F(CalloutManagerTest, MultipleCalloutsLibrariesHooks) {
+    // Ensure that no callouts are attached to any of the hooks.
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
+
+    // Register callouts on the alpha hook.
+    callout_value_ = 0;
+    getCalloutManager()->setLibraryIndex(0);
+    getCalloutManager()->registerCallout("alpha", callout_one);
+    getCalloutManager()->registerCallout("alpha", callout_two);
+    getCalloutManager()->setLibraryIndex(1);
+    getCalloutManager()->registerCallout("alpha", callout_three);
+    getCalloutManager()->registerCallout("alpha", callout_four);
+    getCalloutManager()->setLibraryIndex(2);
+    getCalloutManager()->registerCallout("alpha", callout_five);
+    getCalloutManager()->registerCallout("alpha", callout_two);
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(123452, callout_value_);
+
+    // Register the same callouts on the beta hook, and check that those
+    // on the alpha hook are not affected.
+    callout_value_ = 0;
+    getCalloutManager()->setLibraryIndex(0);
+    getCalloutManager()->registerCallout("beta", callout_five);
+    getCalloutManager()->registerCallout("beta", callout_one);
+    getCalloutManager()->setLibraryIndex(2);
+    getCalloutManager()->registerCallout("beta", callout_four);
+    getCalloutManager()->registerCallout("beta", callout_three);
+    getCalloutManager()->callCallouts(beta_index_, getCalloutHandle());
+    EXPECT_EQ(5143, callout_value_);
+
+    // Check that the order of callouts on the alpha hook has not been affected.
+    callout_value_ = 0;
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(123452, callout_value_);
+
+    // Remove callout four from beta and check that alpha is not affected.
+    getCalloutManager()->setLibraryIndex(2);
+    EXPECT_TRUE(getCalloutManager()->deregisterCallout("beta", callout_four));
+
+    callout_value_ = 0;
+    getCalloutManager()->callCallouts(beta_index_, getCalloutHandle());
+    EXPECT_EQ(513, callout_value_);
+
+    callout_value_ = 0;
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(123452, callout_value_);
+}
+
+// Library handle tests.  As by inspection the LibraryHandle can be seen to be
+// little more than shell around CalloutManager, only a basic set of tests
+// is done concerning registration and deregistration of functions.
+//
+// More extensive tests (i.e. checking that when a callout is called it can
+// only register and deregister callouts within its library) require that
+// the CalloutHandle object pass the appropriate LibraryHandle to the
+// callout.  These tests are done in the handles_unittest tests.
+
+TEST_F(CalloutManagerTest, LibraryHandleRegistration) {
+    // Ensure that no callouts are attached to any of the hooks.
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
+
+    // Set up so that hooks "alpha" and "beta" have callouts attached from a
+    // different libraries.
+    getCalloutManager()->setLibraryIndex(0);
+    getCalloutManager()->getLibraryHandle().registerCallout("alpha",
+                                                            callout_one);
+    getCalloutManager()->getLibraryHandle().registerCallout("alpha",
+                                                            callout_two);
+    getCalloutManager()->setLibraryIndex(1);
+    getCalloutManager()->getLibraryHandle().registerCallout("alpha",
+                                                            callout_three);
+    getCalloutManager()->getLibraryHandle().registerCallout("alpha",
+                                                            callout_four);
+
+    // Check all is as expected.
+    EXPECT_TRUE(getCalloutManager()->calloutsPresent(alpha_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
+
+    // Check that calling the callouts returns as expected. (This is also a
+    // test of the callCallouts method.)
+    callout_value_ = 0;
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(1234, callout_value_);
+
+    // Deregister a callout on library index 0 (after we check we can't
+    // deregister it through library index 1).
+    getCalloutManager()->setLibraryIndex(1);
+    EXPECT_FALSE(getCalloutManager()->deregisterCallout("alpha", callout_two));
+    callout_value_ = 0;
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(1234, callout_value_);
+
+    getCalloutManager()->setLibraryIndex(0);
+    EXPECT_TRUE(getCalloutManager()->deregisterCallout("alpha", callout_two));
+    callout_value_ = 0;
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(134, callout_value_);
+
+    // Deregister all callouts on library index 1.
+    getCalloutManager()->setLibraryIndex(1);
+    EXPECT_TRUE(getCalloutManager()->deregisterAllCallouts("alpha"));
+    callout_value_ = 0;
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(1, callout_value_);
+}
+
+// A repeat of the test above, but using the alternate constructor for the
+// LibraryHandle.
+TEST_F(CalloutManagerTest, LibraryHandleAlternateConstructor) {
+    // Ensure that no callouts are attached to any of the hooks.
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
+
+    // Set up so that hooks "alpha" and "beta" have callouts attached from a
+    // different libraries.
+    LibraryHandle lh0(getCalloutManager().get(), 0);
+    lh0.registerCallout("alpha", callout_one);
+    lh0.registerCallout("alpha", callout_two);
+
+    LibraryHandle lh1(getCalloutManager().get(), 1);
+    lh1.registerCallout("alpha", callout_three);
+    lh1.registerCallout("alpha", callout_four);
+
+    // Check all is as expected.
+    EXPECT_TRUE(getCalloutManager()->calloutsPresent(alpha_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
+
+    // Check that calling the callouts returns as expected. (This is also a
+    // test of the callCallouts method.)
+    callout_value_ = 0;
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(1234, callout_value_);
+
+    // Deregister a callout on library index 0 (after we check we can't
+    // deregister it through library index 1).
+    EXPECT_FALSE(lh1.deregisterCallout("alpha", callout_two));
+    callout_value_ = 0;
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(1234, callout_value_);
+
+    EXPECT_TRUE(lh0.deregisterCallout("alpha", callout_two));
+    callout_value_ = 0;
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(134, callout_value_);
+
+    // Deregister all callouts on library index 1.
+    EXPECT_TRUE(lh1.deregisterAllCallouts("alpha"));
+    callout_value_ = 0;
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(1, callout_value_);
+}
+
+// Check that the pre- and post- user callout library handles work
+// appropriately with no user libraries.
+
+TEST_F(CalloutManagerTest, LibraryHandlePrePostNoLibraries) {
+    // Create a local callout manager and callout handle to reflect no libraries
+    // being loaded.
+    boost::shared_ptr<CalloutManager> manager(new CalloutManager(0));
+    CalloutHandle handle(manager);
+
+    // Ensure that no callouts are attached to any of the hooks.
+    EXPECT_FALSE(manager->calloutsPresent(alpha_index_));
+
+    // Setup the pre-and post callouts.
+    manager->getPostLibraryHandle().registerCallout("alpha", callout_four);
+    manager->getPreLibraryHandle().registerCallout("alpha", callout_one);
+    // Check all is as expected.
+    EXPECT_TRUE(manager->calloutsPresent(alpha_index_));
+    EXPECT_FALSE(manager->calloutsPresent(beta_index_));
+    EXPECT_FALSE(manager->calloutsPresent(gamma_index_));
+    EXPECT_FALSE(manager->calloutsPresent(delta_index_));
+
+    // Check that calling the callouts returns as expected.
+    callout_value_ = 0;
+    manager->callCallouts(alpha_index_, handle);
+    EXPECT_EQ(14, callout_value_);
+
+    // Deregister the pre- library callout.
+    EXPECT_TRUE(manager->getPreLibraryHandle().deregisterAllCallouts("alpha"));
+    callout_value_ = 0;
+    manager->callCallouts(alpha_index_, handle);
+    EXPECT_EQ(4, callout_value_);
+}
+
+// Repeat the tests with one user library.
+
+TEST_F(CalloutManagerTest, LibraryHandlePrePostUserLibrary) {
+
+    // Setup the pre-, library and post callouts.
+    getCalloutManager()->getPostLibraryHandle().registerCallout("alpha",
+                                                                callout_four);
+    getCalloutManager()->getPreLibraryHandle().registerCallout("alpha",
+                                                                callout_one);
+
+    // ... and set up a callout in between, on library number 2.
+    LibraryHandle lh1(getCalloutManager().get(), 2);
+    lh1.registerCallout("alpha", callout_five);
+
+    // Check all is as expected.
+    EXPECT_TRUE(getCalloutManager()->calloutsPresent(alpha_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
+
+    // Check that calling the callouts returns as expected.
+    callout_value_ = 0;
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(154, callout_value_);
+}
+
+// The setting of the hook index is checked in the handles_unittest
+// set of tests, as access restrictions mean it is not easily tested
+// on its own.
+
+} // Anonymous namespace
diff --git a/src/lib/hooks/tests/common_test_class.h b/src/lib/hooks/tests/common_test_class.h
new file mode 100644
index 0000000..803e25c
--- /dev/null
+++ b/src/lib/hooks/tests/common_test_class.h
@@ -0,0 +1,142 @@
+// Copyright (C) 2013  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 COMMON_HOOKS_TEST_CLASS_H
+#define COMMON_HOOKS_TEST_CLASS_H
+
+#include <hooks/callout_handle.h>
+#include <hooks/callout_manager.h>
+#include <hooks/server_hooks.h>
+#include <hooks/tests/marker_file.h>
+
+#include <boost/shared_ptr.hpp>
+#include <gtest/gtest.h>
+
+/// @brief Common hooks test class
+///
+/// This class is a shared parent of the test fixture class in the tests of the
+/// higher-level hooks classes (LibraryManager, LibraryManagerCollection and
+/// HooksManager).  It
+///
+/// - sets the the ServerHooks object with three hooks and stores their
+///   indexes.
+/// - executes the callouts (which are assumed to perform a calculation)
+///   and checks the results.
+
+class HooksCommonTestClass {
+public:
+    /// @brief Constructor
+    HooksCommonTestClass() {
+
+        // Set up the server hooks.  ServerHooks is a singleton, so we reset it
+        // between each test.
+        isc::hooks::ServerHooks& hooks =
+            isc::hooks::ServerHooks::getServerHooks();
+        hooks.reset();
+        hookpt_one_index_ = hooks.registerHook("hookpt_one");
+        hookpt_two_index_ = hooks.registerHook("hookpt_two");
+        hookpt_three_index_ = hooks.registerHook("hookpt_three");
+    }
+
+    /// @brief Call callouts test
+    ///
+    /// All of the loaded libraries for which callouts are called register four
+    /// callouts: a context_create callout and three callouts that are attached
+    /// to hooks hookpt_one, hookpt_two and hookpt_three.  These four callouts,
+    /// executed in sequence, perform a series of calculations. Data is passed
+    /// between callouts in the argument list, in a variable named "result".
+    ///
+    /// context_create initializes the calculation by setting a seed
+    /// value, called r0 here.  This value is dependent on the library being
+    /// loaded.  Prior to that, the argument "result" is initialized to -1,
+    /// the purpose being to avoid exceptions when running this test with no
+    /// libraries loaded.
+    ///
+    /// Callout hookpt_one is passed a value d1 and performs a simple arithmetic
+    /// operation on it and r0 yielding a result r1.  Hence we can say that
+    /// @f[ r1 = hookpt_one(r0, d1) @f]
+    ///
+    /// Callout hookpt_two is passed a value d2 and peforms another simple
+    /// arithmetic operation on it and d2, yielding r2, i.e.
+    /// @f[ r2 = hookpt_two(d1, d2) @f]
+    ///
+    /// hookpt_three does a similar operation giving
+    /// @f[ r3 = hookpt_three(r2, d3) @f].
+    ///
+    /// The details of the operations hookpt_one, hookpt_two and hookpt_three
+    /// depend on the library, so the results obtained not only depend on
+    /// the data, but also on the library loaded.  This method is passed both
+    /// data and expected results.  It executes the three callouts in sequence,
+    /// checking the intermediate and final results.  Only if the expected
+    /// library has been loaded correctly and the callouts in it registered
+    /// correctly will be the results be as expected.
+    ///
+    /// It is assumed that callout_manager_ has been set up appropriately.
+    ///
+    /// @note The CalloutHandle used in the calls is declared locally here.
+    ///       The advantage of this (apart from scope reduction) is that on
+    ///       exit, it is destroyed.  This removes any references to memory
+    ///       allocated by loaded libraries while they are still loaded.
+    ///
+    /// @param manager CalloutManager to use for the test
+    /// @param r0...r3, d1..d3 Data (dN) and expected results (rN) - both
+    ///        intermediate and final.  The arguments are ordered so that they
+    ///        appear in the argument list in the order they are used.
+    void executeCallCallouts(
+            const boost::shared_ptr<isc::hooks::CalloutManager>& manager,
+            int r0, int d1, int r1, int d2, int r2, int d3, int r3) {
+        static const char* COMMON_TEXT = " callout returned the wong value";
+        static const char* RESULT = "result";
+
+        int result;
+
+        // Set up a callout handle for the calls.
+        isc::hooks::CalloutHandle handle(manager);
+
+        // Initialize the argument RESULT.  This simplifies testing by
+        // eliminating the generation of an exception when we try the unload
+        // test.  In that case, RESULT is unchanged.
+        handle.setArgument(RESULT, -1);
+
+        // Seed the calculation.
+        manager->callCallouts(isc::hooks::ServerHooks::CONTEXT_CREATE, handle);
+        handle.getArgument(RESULT, result);
+        EXPECT_EQ(r0, result) << "context_create" << COMMON_TEXT;
+
+        // Perform the first calculation.
+        handle.setArgument("data_1", d1);
+        manager->callCallouts(hookpt_one_index_, handle);
+        handle.getArgument(RESULT, result);
+        EXPECT_EQ(r1, result) << "hookpt_one" << COMMON_TEXT;
+
+        // ... the second ...
+        handle.setArgument("data_2", d2);
+        manager->callCallouts(hookpt_two_index_, handle);
+        handle.getArgument(RESULT, result);
+        EXPECT_EQ(r2, result) << "hookpt_two" << COMMON_TEXT;
+
+        // ... and the third.
+        handle.setArgument("data_3", d3);
+        manager->callCallouts(hookpt_three_index_, handle);
+        handle.getArgument(RESULT, result);
+        EXPECT_EQ(r3, result) << "hookpt_three" << COMMON_TEXT;
+    }
+
+    /// Hook indexes.  These are are made public for ease of reference.
+    int hookpt_one_index_;
+    int hookpt_two_index_;
+    int hookpt_three_index_;
+};
+
+#endif // COMMON_HOOKS_TEST_CLASS_H
diff --git a/src/lib/hooks/tests/framework_exception_library.cc b/src/lib/hooks/tests/framework_exception_library.cc
new file mode 100644
index 0000000..e90fd36
--- /dev/null
+++ b/src/lib/hooks/tests/framework_exception_library.cc
@@ -0,0 +1,47 @@
+// Copyright (C) 2013  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.
+
+/// @file
+/// @brief Framework exception library
+///
+/// This is source of a test library for various test (LibraryManager and
+/// HooksManager).  The characteristics of the library produced from this
+/// file are:
+///
+/// - All three framework functions are supplied (version(), load() and
+///   unload()) and all generate an exception.
+
+#include <hooks/hooks.h>
+
+#include <exception>
+
+extern "C" {
+
+int
+version() {
+    throw std::exception();
+}
+
+int
+load(isc::hooks::LibraryHandle& handle) {
+    throw std::exception();
+}
+
+int
+unload() {
+    throw std::exception();
+}
+
+};
+
diff --git a/src/lib/hooks/tests/full_callout_library.cc b/src/lib/hooks/tests/full_callout_library.cc
new file mode 100644
index 0000000..33d5660
--- /dev/null
+++ b/src/lib/hooks/tests/full_callout_library.cc
@@ -0,0 +1,137 @@
+// Copyright (C) 2013  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.
+
+/// @file
+/// @brief Full callout library
+///
+/// This is source of a test library for various test (LibraryManager and
+/// HooksManager).  The characteristics of the library produced from this
+/// file are:
+///
+/// The characteristics of this library are:
+///
+/// - All three framework functions are supplied (version(), load() and
+///   unload()), with unload() creating a marker file.  The test code checks
+///   for the presence of this file, so verifying that unload() has been run.
+///
+/// - One standard and two non-standard callouts are supplied, with the latter
+///   being registered by the load() function.
+///
+///   All callouts do trivial calculations, the result of all being called in
+///   sequence being
+///
+///   @f[ ((7 * data_1) - data_2) * data_3 @f]
+///
+///   ...where data_1, data_2 and data_3 are the values passed in arguments of
+///   the same name to the three callouts (data_1 passed to hookpt_one, data_2 to
+///   hookpt_two etc.) and the result is returned in the argument "result".
+
+#include <hooks/hooks.h>
+#include <hooks/tests/marker_file.h>
+
+#include <fstream>
+
+using namespace isc::hooks;
+
+extern "C" {
+
+// Callouts
+
+int
+context_create(CalloutHandle& handle) {
+    handle.setContext("result", static_cast<int>(7));
+    handle.setArgument("result", static_cast<int>(7));
+    return (0);
+}
+
+// First callout adds the passed "data_1" argument to the initialized context
+// value of 7. (Note that the value set by context_create is accessed through
+// context and not the argument, so checking that context is correctly passed
+// between callouts in the same library.)
+
+int
+hookpt_one(CalloutHandle& handle) {
+    int data;
+    handle.getArgument("data_1", data);
+
+    int result;
+    handle.getArgument("result", result);
+
+    result *= data;
+    handle.setArgument("result", result);
+
+    return (0);
+}
+
+// Second callout subtracts the passed value of data_2 from the current
+// running total.
+
+static int
+hook_nonstandard_two(CalloutHandle& handle) {
+    int data;
+    handle.getArgument("data_2", data);
+
+    int result;
+    handle.getArgument("result", result);
+
+    result -= data;
+    handle.setArgument("result", result);
+
+    return (0);
+}
+
+// Final callout multplies the current running total by data_3.
+
+static int
+hook_nonstandard_three(CalloutHandle& handle) {
+    int data;
+    handle.getArgument("data_3", data);
+
+    int result;
+    handle.getArgument("result", result);
+
+    result *= data;
+    handle.setArgument("result", result);
+
+    return (0);
+}
+
+// Framework functions
+
+int
+version() {
+    return (BIND10_HOOKS_VERSION);
+}
+
+int
+load(LibraryHandle& handle) {
+    // Register the non-standard functions
+    handle.registerCallout("hookpt_two", hook_nonstandard_two);
+    handle.registerCallout("hookpt_three", hook_nonstandard_three);
+
+    return (0);
+}
+
+int
+unload() {
+    // Create the marker file.
+    std::fstream marker;
+    marker.open(MARKER_FILE, std::fstream::out);
+    marker.close();
+
+    return (0);
+}
+
+};
+
diff --git a/src/lib/hooks/tests/handles_unittest.cc b/src/lib/hooks/tests/handles_unittest.cc
new file mode 100644
index 0000000..e5364bc
--- /dev/null
+++ b/src/lib/hooks/tests/handles_unittest.cc
@@ -0,0 +1,956 @@
+// Copyright (C) 2013  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 <hooks/callout_handle.h>
+#include <hooks/callout_manager.h>
+#include <hooks/library_handle.h>
+#include <hooks/server_hooks.h>
+
+#include <boost/lexical_cast.hpp>
+#include <boost/scoped_ptr.hpp>
+#include <gtest/gtest.h>
+
+#include <algorithm>
+#include <string>
+
+/// @file
+/// CalloutHandle/LibraryHandle interaction tests
+///
+/// This file holds unit tests checking the interaction between the
+/// CalloutHandle/LibraryHandle and CalloutManager classes.  In particular,
+/// they check that:
+///
+/// - A CalloutHandle's context is shared between callouts from the same
+///   library, but there is a separate context for each library.
+///
+/// - The various methods manipulating the items in the CalloutHandle's context
+///   work correctly.
+///
+/// - An active callout can only modify the registration of callouts registered
+///   by its own library.
+
+using namespace isc::hooks;
+using namespace std;
+
+namespace {
+
+class HandlesTest : public ::testing::Test {
+public:
+    /// @brief Constructor
+    ///
+    /// Sets up the various elements used in each test.
+    HandlesTest() {
+        // Set up four hooks, although through gamma
+        ServerHooks& hooks = ServerHooks::getServerHooks();
+        hooks.reset();
+        alpha_index_ = hooks.registerHook("alpha");
+        beta_index_ = hooks.registerHook("beta");
+        gamma_index_ = hooks.registerHook("gamma");
+        delta_index_ = hooks.registerHook("delta");
+
+        // Set up for three libraries.
+        manager_.reset(new CalloutManager(3));
+
+        // Initialize remaining variables.
+        common_string_ = "";
+    }
+
+    /// @brief Return callout manager
+    boost::shared_ptr<CalloutManager> getCalloutManager() {
+        return (manager_);
+    }
+
+    /// Hook indexes - these are frequently accessed, so are accessed directly.
+    int alpha_index_;
+    int beta_index_;
+    int gamma_index_;
+    int delta_index_;
+
+    /// String accessible by all callouts whatever the library
+    static std::string common_string_;
+
+private:
+    /// Callout manager.  Declared static so that the callout functions can
+    /// access it.
+    boost::shared_ptr<CalloutManager> manager_;
+};
+
+/// Define the common string
+std::string HandlesTest::common_string_;
+
+
+// The next set of functions define the callouts used by the tests.  They
+// manipulate the data in such a way that callouts called - and the order in
+// which they were called - can be determined.  The functions also check that
+// the "callout context" data areas are separate.
+//
+// Three libraries are assumed, and each supplies four callouts.  All callouts
+// manipulate two context elements the CalloutHandle, the elements being called
+// "string" and "int" (which describe the type of data manipulated).
+//
+// For the string item, each callout shifts data to the left and inserts its own
+// data.  The data is a string of the form "nmc", where "n" is the number of
+// the library, "m" is the callout number and "y" is the indication of what
+// callout handle was passed as an argument ("1" or "2": "0" is used when no
+// identification has been set in the callout handle).
+//
+// For simplicity, and to cut down the number of functions actually written,
+// the callout indicator ("1" or "2") ) used in the in the CalloutHandle
+// functions is passed via a CalloutArgument.  The argument is named "string":
+// use of a name the same as that of one of the context elements serves as a
+// check that the argument name space and argument context space are separate.
+//
+// For integer data, the value starts at zero and an increment is added on each
+// call.  This increment is equal to:
+//
+// 100 * library number + 10 * callout number + callout handle
+//
+// Although this gives less information than the string value, the reasons for
+// using it are:
+//
+// - It is a separate item in the context, so checks that the context can
+//   handle multiple items.
+// - It provides an item that can be deleted by the context deletion
+//   methods.
+
+
+// Values set in the CalloutHandle context.  There are three libraries, so
+// there are three contexts for the callout, one for each library.
+
+std::string& resultCalloutString(int index) {
+    static std::string result_callout_string[3];
+    return (result_callout_string[index]);
+}
+
+int& resultCalloutInt(int index) {
+    static int result_callout_int[3];
+    return (result_callout_int[index]);
+}
+
+// A simple function to zero the results.
+
+static void zero_results() {
+    for (int i = 0; i < 3; ++i) {
+        resultCalloutString(i) = "";
+        resultCalloutInt(i) = 0;
+    }
+}
+
+
+// Library callouts.
+
+// Common code for setting the callout context values.
+
+int
+execute(CalloutHandle& callout_handle, int library_num, int callout_num) {
+
+    // Obtain the callout handle number
+    int handle_num = 0;
+    try  {
+        callout_handle.getArgument("handle_num", handle_num);
+    } catch (const NoSuchArgument&) {
+        // handle_num argument not set: this is the case in the tests where
+        // the context_create hook check is tested.
+        handle_num = 0;
+    }
+
+    // Create the basic data to be appended to the context value.
+    int idata = 100 * library_num + 10 * callout_num + handle_num;
+    string sdata = boost::lexical_cast<string>(idata);
+
+    // Get the context data. As before, this will not exist for the first
+    // callout called. (In real life, the library should create it when the
+    // "context_create" hook gets called before any packet processing takes
+    // place.)
+    int int_value = 0;
+    try {
+        callout_handle.getContext("int", int_value);
+    } catch (const NoSuchCalloutContext&) {
+        int_value = 0;
+    }
+
+    string string_value = "";
+    try {
+        callout_handle.getContext("string", string_value);
+    } catch (const NoSuchCalloutContext&) {
+        string_value = "";
+    }
+
+    // Update the values and set them back in the callout context.
+    int_value += idata;
+    callout_handle.setContext("int", int_value);
+
+    string_value += sdata;
+    callout_handle.setContext("string", string_value);
+
+    return (0);
+}
+
+// The following functions are the actual callouts - the name is of the
+// form "callout_<library number>_<callout number>"
+
+int
+callout11(CalloutHandle& callout_handle) {
+    return (execute(callout_handle, 1, 1));
+}
+
+int
+callout12(CalloutHandle& callout_handle) {
+    return (execute(callout_handle, 1, 2));
+}
+
+int
+callout13(CalloutHandle& callout_handle) {
+    return (execute(callout_handle, 1, 3));
+}
+
+int
+callout21(CalloutHandle& callout_handle) {
+    return (execute(callout_handle, 2, 1));
+}
+
+int
+callout22(CalloutHandle& callout_handle) {
+    return (execute(callout_handle, 2, 2));
+}
+
+int
+callout23(CalloutHandle& callout_handle) {
+    return (execute(callout_handle, 2, 3));
+}
+
+int
+callout31(CalloutHandle& callout_handle) {
+    return (execute(callout_handle, 3, 1));
+}
+
+int
+callout32(CalloutHandle& callout_handle) {
+    return (execute(callout_handle, 3, 2));
+}
+
+int
+callout33(CalloutHandle& callout_handle) {
+    return (execute(callout_handle, 3, 3));
+}
+
+// Common callout code for the fourth hook (which makes the data available for
+// checking).  It copies the library and callout context data to the global
+// variables.
+
+int printExecute(CalloutHandle& callout_handle, int library_num) {
+    callout_handle.getContext("string", resultCalloutString(library_num - 1));
+    callout_handle.getContext("int", resultCalloutInt(library_num - 1));
+
+    return (0);
+}
+
+// These are the actual callouts.
+
+int
+print1(CalloutHandle& callout_handle) {
+    return (printExecute(callout_handle, 1));
+}
+
+int
+print2(CalloutHandle& callout_handle) {
+    return (printExecute(callout_handle, 2));
+}
+
+int
+print3(CalloutHandle& callout_handle) {
+    return (printExecute(callout_handle, 3));
+}
+
+// This test checks the many-faced nature of the context for the CalloutContext.
+
+TEST_F(HandlesTest, ContextAccessCheck) {
+    // Register callouts for the different libraries.
+    CalloutHandle handle(getCalloutManager());
+
+    // Library 0.
+    getCalloutManager()->setLibraryIndex(0);
+    getCalloutManager()->registerCallout("alpha", callout11);
+    getCalloutManager()->registerCallout("beta", callout12);
+    getCalloutManager()->registerCallout("gamma", callout13);
+    getCalloutManager()->registerCallout("delta", print1);
+
+    getCalloutManager()->setLibraryIndex(1);
+    getCalloutManager()->registerCallout("alpha", callout21);
+    getCalloutManager()->registerCallout("beta", callout22);
+    getCalloutManager()->registerCallout("gamma", callout23);
+    getCalloutManager()->registerCallout("delta", print2);
+
+    getCalloutManager()->setLibraryIndex(2);
+    getCalloutManager()->registerCallout("alpha", callout31);
+    getCalloutManager()->registerCallout("beta", callout32);
+    getCalloutManager()->registerCallout("gamma", callout33);
+    getCalloutManager()->registerCallout("delta", print3);
+
+    // Create the callout handles and distinguish them by setting the
+    // "handle_num" argument.
+    CalloutHandle callout_handle_1(getCalloutManager());
+    callout_handle_1.setArgument("handle_num", static_cast<int>(1));
+
+    CalloutHandle callout_handle_2(getCalloutManager());
+    callout_handle_2.setArgument("handle_num", static_cast<int>(2));
+
+    // Now call the callouts attached to the first three hooks.  Each hook is
+    // called twice (once for each callout handle) before the next hook is
+    // called.
+    getCalloutManager()->callCallouts(alpha_index_, callout_handle_1);
+    getCalloutManager()->callCallouts(alpha_index_, callout_handle_2);
+    getCalloutManager()->callCallouts(beta_index_, callout_handle_1);
+    getCalloutManager()->callCallouts(beta_index_, callout_handle_2);
+    getCalloutManager()->callCallouts(gamma_index_, callout_handle_1);
+    getCalloutManager()->callCallouts(gamma_index_, callout_handle_2);
+
+    // Get the results for each callout (the callout on hook "delta" copies
+    // the context values into a location the test can access).  Explicitly
+    // zero the variables before getting the results so we are certain that
+    // the values are the results of the callouts.
+
+    zero_results();
+
+    // To explain the expected callout context results.
+    //
+    // Each callout handle maintains a separate context for each library.  When
+    // the first call to callCallouts() is made, "111" gets appended to
+    // the context for library 1 maintained by the first callout handle, "211"
+    // gets appended to the context maintained for library 2, and "311" to
+    // the context maintained for library 3.  In each case, the first digit
+    // corresponds to the library number, the second to the callout number and
+    // the third to the "handle_num" of the callout handle. For the first call
+    // to callCallouts, handle 1 is used, so the last digit is always 1.
+    //
+    // The next call to callCallouts() calls the same callouts but for the
+    // second callout handle.  It also maintains three contexts (one for
+    // each library) and they will get "112", "212", "312" appended to
+    // them. The explanation for the digits is the same as before, except that
+    // in this case, the callout handle is number 2, so the third digit is
+    // always 2.  These additions don't affect the contexts maintained by
+    // callout handle 1.
+    //
+    // The process is then repeated for hooks "beta" and "gamma" which, for
+    // callout handle 1, append "121", "221" and "321" for hook "beta" and
+    // "311", "321" and "331" for hook "gamma".
+    //
+    // The expected integer values can be found by summing up the values
+    // corresponding to the elements of the strings.
+
+    // At this point, we have only called the "print" function for callout
+    // handle "1", so the following results are checking the context values
+    // maintained in that callout handle.
+
+    getCalloutManager()->callCallouts(delta_index_, callout_handle_1);
+    EXPECT_EQ("111121131", resultCalloutString(0));
+    EXPECT_EQ("211221231", resultCalloutString(1));
+    EXPECT_EQ("311321331", resultCalloutString(2));
+
+    EXPECT_EQ((111 + 121 + 131), resultCalloutInt(0));
+    EXPECT_EQ((211 + 221 + 231), resultCalloutInt(1));
+    EXPECT_EQ((311 + 321 + 331), resultCalloutInt(2));
+
+    // Repeat the checks for callout 2.
+
+    zero_results();
+    getCalloutManager()->callCallouts(delta_index_, callout_handle_2);
+
+    EXPECT_EQ((112 + 122 + 132), resultCalloutInt(0));
+    EXPECT_EQ((212 + 222 + 232), resultCalloutInt(1));
+    EXPECT_EQ((312 + 322 + 332), resultCalloutInt(2));
+
+    EXPECT_EQ("112122132", resultCalloutString(0));
+    EXPECT_EQ("212222232", resultCalloutString(1));
+    EXPECT_EQ("312322332", resultCalloutString(2));
+}
+
+// Now repeat the test, but add a deletion callout to the list.  The "beta"
+// hook of library 2 will have an additional callout to delete the "int"
+// element: the same hook for library 3 will delete both elements.  In
+// addition, the names of context elements for the libraries at this point
+// will be printed.
+
+// List of context item names.
+
+vector<string>&
+getItemNames(int index) {
+    static vector<string> context_items[3];
+    return (context_items[index]);
+}
+
+// Context item deletion functions.
+
+int
+deleteIntContextItem(CalloutHandle& handle) {
+    handle.deleteContext("int");
+    return (0);
+}
+
+int
+deleteAllContextItems(CalloutHandle& handle) {
+    handle.deleteAllContext();
+    return (0);
+}
+
+// Generic print function - copy names in sorted order.
+
+int
+printContextNamesExecute(CalloutHandle& handle, int library_num) {
+    const int index = library_num - 1;
+    getItemNames(index) = handle.getContextNames();
+    sort(getItemNames(index).begin(), getItemNames(index).end());
+    return (0);
+}
+
+int
+printContextNames1(CalloutHandle& handle) {
+    return (printContextNamesExecute(handle, 1));
+}
+
+int
+printContextNames2(CalloutHandle& handle) {
+    return (printContextNamesExecute(handle, 2));
+}
+
+int
+printContextNames3(CalloutHandle& handle) {
+    return (printContextNamesExecute(handle, 3));
+}
+
+// Perform the test including deletion of context items.
+
+TEST_F(HandlesTest, ContextDeletionCheck) {
+
+    getCalloutManager()->setLibraryIndex(0);
+    getCalloutManager()->registerCallout("alpha", callout11);
+    getCalloutManager()->registerCallout("beta", callout12);
+    getCalloutManager()->registerCallout("beta", printContextNames1);
+    getCalloutManager()->registerCallout("gamma", callout13);
+    getCalloutManager()->registerCallout("delta", print1);
+
+    getCalloutManager()->setLibraryIndex(1);
+    getCalloutManager()->registerCallout("alpha", callout21);
+    getCalloutManager()->registerCallout("beta", callout22);
+    getCalloutManager()->registerCallout("beta", deleteIntContextItem);
+    getCalloutManager()->registerCallout("beta", printContextNames2);
+    getCalloutManager()->registerCallout("gamma", callout23);
+    getCalloutManager()->registerCallout("delta", print2);
+
+    getCalloutManager()->setLibraryIndex(2);
+    getCalloutManager()->registerCallout("alpha", callout31);
+    getCalloutManager()->registerCallout("beta", callout32);
+    getCalloutManager()->registerCallout("beta", deleteAllContextItems);
+    getCalloutManager()->registerCallout("beta", printContextNames3);
+    getCalloutManager()->registerCallout("gamma", callout33);
+    getCalloutManager()->registerCallout("delta", print3);
+
+    // Create the callout handles and distinguish them by setting the "long"
+    // argument.
+    CalloutHandle callout_handle_1(getCalloutManager());
+    callout_handle_1.setArgument("handle_num", static_cast<int>(1));
+
+    CalloutHandle callout_handle_2(getCalloutManager());
+    callout_handle_2.setArgument("handle_num", static_cast<int>(2));
+
+    // Now call the callouts attached to the first three hooks.  Each hook is
+    // called twice (once for each callout handle) before the next hook is
+    // called.
+    getCalloutManager()->callCallouts(alpha_index_, callout_handle_1);
+    getCalloutManager()->callCallouts(alpha_index_, callout_handle_2);
+    getCalloutManager()->callCallouts(beta_index_, callout_handle_1);
+    getCalloutManager()->callCallouts(beta_index_, callout_handle_2);
+    getCalloutManager()->callCallouts(gamma_index_, callout_handle_1);
+    getCalloutManager()->callCallouts(gamma_index_, callout_handle_2);
+
+    // Get the results for each callout.  Explicitly zero the variables before
+    // getting the results so we are certain that the values are the results
+    // of the callouts.
+
+    zero_results();
+    getCalloutManager()->callCallouts(delta_index_, callout_handle_1);
+
+    // The logic by which the expected results are arrived at is described
+    // in the ContextAccessCheck test.  The results here are different
+    // because context items have been modified along the way.
+
+    EXPECT_EQ((111 + 121 + 131), resultCalloutInt(0));
+    EXPECT_EQ((            231), resultCalloutInt(1));
+    EXPECT_EQ((            331), resultCalloutInt(2));
+
+    EXPECT_EQ("111121131", resultCalloutString(0));
+    EXPECT_EQ("211221231", resultCalloutString(1));
+    EXPECT_EQ(      "331", resultCalloutString(2));
+
+    // Repeat the checks for callout handle 2.
+
+    zero_results();
+    getCalloutManager()->callCallouts(delta_index_, callout_handle_2);
+
+    EXPECT_EQ((112 + 122 + 132), resultCalloutInt(0));
+    EXPECT_EQ((            232), resultCalloutInt(1));
+    EXPECT_EQ((            332), resultCalloutInt(2));
+
+    EXPECT_EQ("112122132", resultCalloutString(0));
+    EXPECT_EQ("212222232", resultCalloutString(1));
+    EXPECT_EQ(      "332", resultCalloutString(2));
+
+    // ... and check what the names of the context items are after the callouts
+    // for hook "beta".  We know they are in sorted order.
+
+    EXPECT_EQ(2, getItemNames(0).size());
+    EXPECT_EQ(string("int"),    getItemNames(0)[0]);
+    EXPECT_EQ(string("string"), getItemNames(0)[1]);
+
+    EXPECT_EQ(1, getItemNames(1).size());
+    EXPECT_EQ(string("string"), getItemNames(1)[0]);
+
+    EXPECT_EQ(0, getItemNames(2).size());
+}
+
+// Tests that the CalloutHandle's constructor and destructor call the
+// context_create and context_destroy callbacks (if registered).  For
+// simplicity, we'll use the same callout functions as used above.
+
+TEST_F(HandlesTest, ConstructionDestructionCallouts) {
+
+    // Register context callouts.
+    getCalloutManager()->setLibraryIndex(0);
+    getCalloutManager()->registerCallout("context_create", callout11);
+    getCalloutManager()->registerCallout("context_create", print1);
+    getCalloutManager()->registerCallout("context_destroy", callout12);
+    getCalloutManager()->registerCallout("context_destroy", print1);
+
+    // Create the CalloutHandle and check that the constructor callout
+    // has run.
+    zero_results();
+    boost::scoped_ptr<CalloutHandle>
+        callout_handle(new CalloutHandle(getCalloutManager()));
+    EXPECT_EQ("110", resultCalloutString(0));
+    EXPECT_EQ(110, resultCalloutInt(0));
+
+    // Check that the destructor callout runs.  Note that the "print1" callout
+    // didn't destroy the library context - it only copied it to where it
+    // could be examined.  As a result, the destructor callout appends its
+    // elements to the constructor's values and the result is printed.
+    zero_results();
+    callout_handle.reset();
+
+    EXPECT_EQ("110120", resultCalloutString(0));
+    EXPECT_EQ((110 + 120), resultCalloutInt(0));
+}
+
+// Dynamic callout registration and deregistration.
+// The following are the dynamic registration/deregistration callouts.
+
+
+// Add callout_78_alpha - adds a callout to hook alpha that appends "78x"
+// (where "x" is the callout handle) to the current output.
+
+int
+callout78(CalloutHandle& callout_handle) {
+    return (execute(callout_handle, 7, 8));
+}
+
+int
+add_callout78_alpha(CalloutHandle& callout_handle) {
+    callout_handle.getLibraryHandle().registerCallout("alpha", callout78);
+    return (0);
+}
+
+int
+delete_callout78_alpha(CalloutHandle& callout_handle) {
+    static_cast<void>(
+        callout_handle.getLibraryHandle().deregisterCallout("alpha",
+                                                            callout78));
+    return (0);
+}
+
+// Check that a callout can register another callout on a different hook.
+
+TEST_F(HandlesTest, DynamicRegistrationAnotherHook) {
+    // Register callouts for the different libraries.
+    CalloutHandle handle(getCalloutManager());
+
+    // Set up callouts on "alpha".
+    getCalloutManager()->setLibraryIndex(0);
+    getCalloutManager()->registerCallout("alpha", callout11);
+    getCalloutManager()->registerCallout("delta", print1);
+    getCalloutManager()->setLibraryIndex(1);
+    getCalloutManager()->registerCallout("alpha", callout21);
+    getCalloutManager()->registerCallout("delta", print2);
+    getCalloutManager()->setLibraryIndex(2);
+    getCalloutManager()->registerCallout("alpha", callout31);
+    getCalloutManager()->registerCallout("delta", print3);
+
+    // ... and on "beta", set up the function to add a hook to alpha (but only
+    // for library 1).
+    getCalloutManager()->setLibraryIndex(1);
+    getCalloutManager()->registerCallout("beta", add_callout78_alpha);
+
+    // See what we get for calling the callouts on alpha first.
+    CalloutHandle callout_handle_1(getCalloutManager());
+    callout_handle_1.setArgument("handle_num", static_cast<int>(1));
+    getCalloutManager()->callCallouts(alpha_index_, callout_handle_1);
+
+    zero_results();
+    getCalloutManager()->callCallouts(delta_index_, callout_handle_1);
+    EXPECT_EQ("111", resultCalloutString(0));
+    EXPECT_EQ("211", resultCalloutString(1));
+    EXPECT_EQ("311", resultCalloutString(2));
+
+    // All as expected, now call the callouts on beta.  This should add a
+    // callout to the list of callouts for alpha, which we should see when
+    // we run the test again.
+    getCalloutManager()->callCallouts(beta_index_, callout_handle_1);
+
+    // Use a new callout handle so as to get fresh callout context.
+    CalloutHandle callout_handle_2(getCalloutManager());
+    callout_handle_2.setArgument("handle_num", static_cast<int>(2));
+    getCalloutManager()->callCallouts(alpha_index_, callout_handle_2);
+
+    zero_results();
+    getCalloutManager()->callCallouts(delta_index_, callout_handle_2);
+    EXPECT_EQ("112", resultCalloutString(0));
+    EXPECT_EQ("212782", resultCalloutString(1));
+    EXPECT_EQ("312", resultCalloutString(2));
+}
+
+// Check that a callout can register another callout on the same hook.
+// Note that the registration only applies to a subsequent invocation of
+// callCallouts, not to the current one. In other words, if
+//
+// * the callout list for a library is "A then B then C"
+// * when callCallouts is executed "B" adds "D" to that list,
+//
+// ... the current execution of callCallouts only executes A, B and C.  A
+// subsequent invocation will execute A, B, C then D.
+
+TEST_F(HandlesTest, DynamicRegistrationSameHook) {
+    // Register callouts for the different libraries.
+    CalloutHandle handle(getCalloutManager());
+
+    // Set up callouts on "alpha".
+    getCalloutManager()->setLibraryIndex(0);
+    getCalloutManager()->registerCallout("alpha", callout11);
+    getCalloutManager()->registerCallout("alpha", add_callout78_alpha);
+    getCalloutManager()->registerCallout("delta", print1);
+
+    // See what we get for calling the callouts on alpha first.
+    CalloutHandle callout_handle_1(getCalloutManager());
+    callout_handle_1.setArgument("handle_num", static_cast<int>(1));
+    getCalloutManager()->callCallouts(alpha_index_, callout_handle_1);
+    zero_results();
+    getCalloutManager()->callCallouts(delta_index_, callout_handle_1);
+    EXPECT_EQ("111", resultCalloutString(0));
+
+    // Run it again - we should have added something to this hook.
+    CalloutHandle callout_handle_2(getCalloutManager());
+    callout_handle_2.setArgument("handle_num", static_cast<int>(2));
+    getCalloutManager()->callCallouts(alpha_index_, callout_handle_2);
+    zero_results();
+    getCalloutManager()->callCallouts(delta_index_, callout_handle_2);
+    EXPECT_EQ("112782", resultCalloutString(0));
+
+    // And a third time...
+    CalloutHandle callout_handle_3(getCalloutManager());
+    callout_handle_3.setArgument("handle_num", static_cast<int>(3));
+    getCalloutManager()->callCallouts(alpha_index_, callout_handle_3);
+    zero_results();
+    getCalloutManager()->callCallouts(delta_index_, callout_handle_3);
+    EXPECT_EQ("113783783", resultCalloutString(0));
+}
+
+// Deregistration of a callout from a different hook
+
+TEST_F(HandlesTest, DynamicDeregistrationDifferentHook) {
+    // Register callouts for the different libraries.
+    CalloutHandle handle(getCalloutManager());
+
+    // Set up callouts on "alpha".
+    getCalloutManager()->setLibraryIndex(0);
+    getCalloutManager()->registerCallout("alpha", callout11);
+    getCalloutManager()->registerCallout("alpha", callout78);
+    getCalloutManager()->registerCallout("alpha", callout11);
+    getCalloutManager()->registerCallout("delta", print1);
+
+    getCalloutManager()->registerCallout("beta", delete_callout78_alpha);
+
+    // Call the callouts on alpha
+    CalloutHandle callout_handle_1(getCalloutManager());
+    callout_handle_1.setArgument("handle_num", static_cast<int>(1));
+    getCalloutManager()->callCallouts(alpha_index_, callout_handle_1);
+
+    zero_results();
+    getCalloutManager()->callCallouts(delta_index_, callout_handle_1);
+    EXPECT_EQ("111781111", resultCalloutString(0));
+
+    // Run the callouts on hook beta to remove the callout on alpha.
+    getCalloutManager()->callCallouts(beta_index_, callout_handle_1);
+
+    // The run of the callouts should have altered the callout list on the
+    // first library for hook alpha, so call again to make sure.
+    CalloutHandle callout_handle_2(getCalloutManager());
+    callout_handle_2.setArgument("handle_num", static_cast<int>(2));
+    getCalloutManager()->callCallouts(alpha_index_, callout_handle_2);
+
+    zero_results();
+    getCalloutManager()->callCallouts(delta_index_, callout_handle_2);
+    EXPECT_EQ("112112", resultCalloutString(0));
+}
+
+// Deregistration of a callout from the same hook
+
+TEST_F(HandlesTest, DynamicDeregistrationSameHook) {
+    // Register callouts for the different libraries.
+    CalloutHandle handle(getCalloutManager());
+
+    // Set up callouts on "alpha".
+    getCalloutManager()->setLibraryIndex(0);
+    getCalloutManager()->registerCallout("alpha", callout11);
+    getCalloutManager()->registerCallout("alpha", delete_callout78_alpha);
+    getCalloutManager()->registerCallout("alpha", callout78);
+    getCalloutManager()->registerCallout("delta", print1);
+    getCalloutManager()->setLibraryIndex(1);
+    getCalloutManager()->registerCallout("alpha", callout21);
+    getCalloutManager()->registerCallout("alpha", callout78);
+    getCalloutManager()->registerCallout("delta", print2);
+
+    // Call the callouts on alpha
+    CalloutHandle callout_handle_1(getCalloutManager());
+    callout_handle_1.setArgument("handle_num", static_cast<int>(1));
+    getCalloutManager()->callCallouts(alpha_index_, callout_handle_1);
+
+    zero_results();
+    getCalloutManager()->callCallouts(delta_index_, callout_handle_1);
+    EXPECT_EQ("111781", resultCalloutString(0));
+    EXPECT_EQ("211781", resultCalloutString(1));
+
+    // The run of the callouts should have altered the callout list on the
+    // first library for hook alpha, so call again to make sure.
+    CalloutHandle callout_handle_2(getCalloutManager());
+    callout_handle_2.setArgument("handle_num", static_cast<int>(2));
+    getCalloutManager()->callCallouts(alpha_index_, callout_handle_2);
+
+    zero_results();
+    getCalloutManager()->callCallouts(delta_index_, callout_handle_2);
+    EXPECT_EQ("112", resultCalloutString(0));
+    EXPECT_EQ("212782", resultCalloutString(1));
+}
+
+// Testing the operation of the "skip" flag.  Callouts print the value
+// they see in the flag and either leave it unchanged, set it or clear it.
+
+int
+calloutPrintSkip(CalloutHandle& handle) {
+    static const std::string YES("Y");
+    static const std::string NO("N");
+
+    HandlesTest::common_string_ = HandlesTest::common_string_ +
+        (handle.getSkip() ? YES : NO);
+    return (0);
+}
+
+int
+calloutSetSkip(CalloutHandle& handle) {
+    static_cast<void>(calloutPrintSkip(handle));
+    handle.setSkip(true);
+    return (0);
+}
+
+int
+calloutClearSkip(CalloutHandle& handle) {
+    static_cast<void>(calloutPrintSkip(handle));
+    handle.setSkip(false);
+    return (0);
+}
+
+// Do a series of tests, returning with the skip flag set "true".
+
+TEST_F(HandlesTest, ReturnSkipSet) {
+    getCalloutManager()->setLibraryIndex(0);
+    getCalloutManager()->registerCallout("alpha", calloutPrintSkip);
+    getCalloutManager()->registerCallout("alpha", calloutSetSkip);
+    getCalloutManager()->registerCallout("alpha", calloutSetSkip);
+    getCalloutManager()->registerCallout("alpha", calloutClearSkip);
+
+    getCalloutManager()->setLibraryIndex(1);
+    getCalloutManager()->registerCallout("alpha", calloutPrintSkip);
+    getCalloutManager()->registerCallout("alpha", calloutSetSkip);
+    getCalloutManager()->registerCallout("alpha", calloutSetSkip);
+    getCalloutManager()->registerCallout("alpha", calloutClearSkip);
+    getCalloutManager()->registerCallout("alpha", calloutClearSkip);
+
+    getCalloutManager()->setLibraryIndex(2);
+    getCalloutManager()->registerCallout("alpha", calloutPrintSkip);
+    getCalloutManager()->registerCallout("alpha", calloutSetSkip);
+    getCalloutManager()->registerCallout("alpha", calloutClearSkip);
+    getCalloutManager()->registerCallout("alpha", calloutSetSkip);
+
+    CalloutHandle callout_handle(getCalloutManager());
+    getCalloutManager()->callCallouts(alpha_index_, callout_handle);
+
+    // Check result.  For each of visual checking, the expected string is
+    // divided into sections corresponding to the blocks of callouts above.
+    EXPECT_EQ(std::string("NNYY" "NNYYN" "NNYN"), common_string_);
+
+    // ... and check that the skip flag on exit from callCallouts is set.
+    EXPECT_TRUE(callout_handle.getSkip());
+}
+
+// Repeat the test, returning with the skip flag clear.
+TEST_F(HandlesTest, ReturnSkipClear) {
+    getCalloutManager()->setLibraryIndex(0);
+    getCalloutManager()->registerCallout("alpha", calloutSetSkip);
+    getCalloutManager()->registerCallout("alpha", calloutSetSkip);
+    getCalloutManager()->registerCallout("alpha", calloutClearSkip);
+
+    getCalloutManager()->setLibraryIndex(1);
+    getCalloutManager()->registerCallout("alpha", calloutPrintSkip);
+    getCalloutManager()->registerCallout("alpha", calloutSetSkip);
+    getCalloutManager()->registerCallout("alpha", calloutClearSkip);
+    getCalloutManager()->registerCallout("alpha", calloutSetSkip);
+    getCalloutManager()->registerCallout("alpha", calloutClearSkip);
+    getCalloutManager()->registerCallout("alpha", calloutClearSkip);
+
+    getCalloutManager()->setLibraryIndex(2);
+    getCalloutManager()->registerCallout("alpha", calloutClearSkip);
+    getCalloutManager()->registerCallout("alpha", calloutPrintSkip);
+    getCalloutManager()->registerCallout("alpha", calloutSetSkip);
+    getCalloutManager()->registerCallout("alpha", calloutClearSkip);
+
+    CalloutHandle callout_handle(getCalloutManager());
+    getCalloutManager()->callCallouts(alpha_index_, callout_handle);
+
+    // Check result.  For each of visual checking, the expected string is
+    // divided into sections corresponding to the blocks of callouts above.
+    EXPECT_EQ(std::string("NYY" "NNYNYN" "NNNY"), common_string_);
+
+    // ... and check that the skip flag on exit from callCallouts is set.
+    EXPECT_FALSE(callout_handle.getSkip());
+}
+
+// The next set of callouts do a similar thing to the above "skip" tests,
+// but alter the value of a string argument.  This is for testing that the
+// a callout is able to change an argument and return it to the caller.
+
+const char* MODIFIED_ARG = "modified_arg";
+
+int
+calloutSetArgumentCommon(CalloutHandle& handle, const char* what) {
+    std::string modified_arg = "";
+
+    handle.getArgument(MODIFIED_ARG, modified_arg);
+    modified_arg = modified_arg + std::string(what);
+    handle.setArgument(MODIFIED_ARG, modified_arg);
+    return (0);
+}
+
+int
+calloutSetArgumentYes(CalloutHandle& handle) {
+    return (calloutSetArgumentCommon(handle, "Y"));
+}
+
+int
+calloutSetArgumentNo(CalloutHandle& handle) {
+    return (calloutSetArgumentCommon(handle, "N"));
+}
+
+// ... and a callout to just copy the argument to the "common_string_" variable
+// but otherwise not alter it.
+
+int
+calloutPrintArgument(CalloutHandle& handle) {
+    handle.getArgument(MODIFIED_ARG, HandlesTest::common_string_);
+    return (0);
+}
+
+TEST_F(HandlesTest, CheckModifiedArgument) {
+    getCalloutManager()->setLibraryIndex(0);
+    getCalloutManager()->registerCallout("alpha", calloutSetArgumentYes);
+    getCalloutManager()->registerCallout("alpha", calloutSetArgumentNo);
+    getCalloutManager()->registerCallout("alpha", calloutSetArgumentNo);
+
+    getCalloutManager()->setLibraryIndex(1);
+    getCalloutManager()->registerCallout("alpha", calloutSetArgumentYes);
+    getCalloutManager()->registerCallout("alpha", calloutSetArgumentYes);
+    getCalloutManager()->registerCallout("alpha", calloutPrintArgument);
+    getCalloutManager()->registerCallout("alpha", calloutSetArgumentNo);
+    getCalloutManager()->registerCallout("alpha", calloutSetArgumentNo);
+
+    getCalloutManager()->setLibraryIndex(2);
+    getCalloutManager()->registerCallout("alpha", calloutSetArgumentYes);
+    getCalloutManager()->registerCallout("alpha", calloutSetArgumentNo);
+    getCalloutManager()->registerCallout("alpha", calloutSetArgumentYes);
+
+    // Create the argument with an initial empty string value.  Then call the
+    // sequence of callouts above.
+    CalloutHandle callout_handle(getCalloutManager());
+    std::string modified_arg = "";
+    callout_handle.setArgument(MODIFIED_ARG, modified_arg);
+    getCalloutManager()->callCallouts(alpha_index_, callout_handle);
+
+    // Check the intermediate and results.  For visual checking, the expected
+    // string is divided into sections corresponding to the blocks of callouts
+    // above.
+    EXPECT_EQ(std::string("YNN" "YY"), common_string_);
+
+    callout_handle.getArgument(MODIFIED_ARG, modified_arg);
+    EXPECT_EQ(std::string("YNN" "YYNN" "YNY"), modified_arg);
+}
+
+// Test that the CalloutHandle provides the name of the hook to which the
+// callout is attached.
+
+int
+callout_hook_name(CalloutHandle& callout_handle) {
+    HandlesTest::common_string_ = callout_handle.getHookName();
+    return (0);
+}
+
+int
+callout_hook_dummy(CalloutHandle&) {
+    return (0);
+}
+
+TEST_F(HandlesTest, HookName) {
+    getCalloutManager()->setLibraryIndex(0);
+    getCalloutManager()->registerCallout("alpha", callout_hook_name);
+    getCalloutManager()->registerCallout("beta", callout_hook_name);
+
+    // Call alpha and beta callouts and check the hook to which they belong.
+    CalloutHandle callout_handle(getCalloutManager());
+
+    EXPECT_EQ(std::string(""), HandlesTest::common_string_);
+
+    getCalloutManager()->callCallouts(alpha_index_, callout_handle);
+    EXPECT_EQ(std::string("alpha"), HandlesTest::common_string_);
+
+    getCalloutManager()->callCallouts(beta_index_, callout_handle);
+    EXPECT_EQ(std::string("beta"), HandlesTest::common_string_);
+
+    // Make sure that the callout accesses the name even if it is not the
+    // only callout in the list.
+    getCalloutManager()->setLibraryIndex(1);
+    getCalloutManager()->registerCallout("gamma", callout_hook_dummy);
+    getCalloutManager()->registerCallout("gamma", callout_hook_name);
+    getCalloutManager()->registerCallout("gamma", callout_hook_dummy);
+
+    EXPECT_EQ(std::string("beta"), HandlesTest::common_string_);
+    getCalloutManager()->callCallouts(gamma_index_, callout_handle);
+    EXPECT_EQ(std::string("gamma"), HandlesTest::common_string_);
+}
+
+} // Anonymous namespace
+
diff --git a/src/lib/hooks/tests/hooks_manager_unittest.cc b/src/lib/hooks/tests/hooks_manager_unittest.cc
new file mode 100644
index 0000000..136eeae
--- /dev/null
+++ b/src/lib/hooks/tests/hooks_manager_unittest.cc
@@ -0,0 +1,524 @@
+// Copyright (C) 2013  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 <hooks/callout_handle.h>
+#include <hooks/hooks_manager.h>
+#include <hooks/server_hooks.h>
+
+#include <hooks/tests/common_test_class.h>
+#include <hooks/tests/test_libraries.h>
+
+#include <boost/shared_ptr.hpp>
+#include <gtest/gtest.h>
+
+#include <algorithm>
+#include <string>
+
+
+using namespace isc;
+using namespace isc::hooks;
+using namespace std;
+
+namespace {
+
+/// @brief Hooks manager collection test class
+
+class HooksManagerTest : public ::testing::Test,
+                         public HooksCommonTestClass {
+public:
+    /// @brief Constructor
+    ///
+    /// Reset the hooks manager.  The hooks manager is a singleton, so needs
+    /// to be reset for each test.
+    HooksManagerTest() {
+        HooksManager::unloadLibraries();
+    }
+
+    /// @brief Destructor
+    ///
+    /// Unload all libraries.
+    ~HooksManagerTest() {
+        HooksManager::unloadLibraries();
+    }
+
+
+    /// @brief Call callouts test
+    ///
+    /// See the header for HooksCommonTestClass::execute for details.
+    ///
+    /// @param r0...r3, d1..d3 Values and intermediate values expected.  They
+    ///        are ordered so that the variables appear in the argument list in
+    ///        the order they are used.
+    void executeCallCallouts(int r0, int d1, int r1, int d2, int r2, int d3,
+                             int r3) {
+        static const char* COMMON_TEXT = " callout returned the wong value";
+        static const char* RESULT = "result";
+
+        // Get a CalloutHandle for the calculation.
+        CalloutHandlePtr handle = HooksManager::createCalloutHandle();
+
+        // Initialize the argument RESULT.  This simplifies testing by
+        // eliminating the generation of an exception when we try the unload
+        // test.  In that case, RESULT is unchanged.
+        int result = -1;
+        handle->setArgument(RESULT, result);
+
+        // Seed the calculation.
+        HooksManager::callCallouts(isc::hooks::ServerHooks::CONTEXT_CREATE,
+                                   *handle);
+        handle->getArgument(RESULT, result);
+        EXPECT_EQ(r0, result) << "context_create" << COMMON_TEXT;
+
+        // Perform the first calculation.
+        handle->setArgument("data_1", d1);
+        HooksManager::callCallouts(hookpt_one_index_, *handle);
+        handle->getArgument(RESULT, result);
+        EXPECT_EQ(r1, result) << "hookpt_one" << COMMON_TEXT;
+
+        // ... the second ...
+        handle->setArgument("data_2", d2);
+        HooksManager::callCallouts(hookpt_two_index_, *handle);
+        handle->getArgument(RESULT, result);
+        EXPECT_EQ(r2, result) << "hookpt_two" << COMMON_TEXT;
+
+        // ... and the third.
+        handle->setArgument("data_3", d3);
+        HooksManager::callCallouts(hookpt_three_index_, *handle);
+        handle->getArgument(RESULT, result);
+        EXPECT_EQ(r3, result) << "hookpt_three" << COMMON_TEXT;
+    }
+
+};
+
+// This is effectively the same test as for LibraryManager, but using the
+// HooksManager object.
+
+TEST_F(HooksManagerTest, LoadLibraries) {
+
+    // Set up the list of libraries to be loaded.
+    std::vector<std::string> library_names;
+    library_names.push_back(std::string(FULL_CALLOUT_LIBRARY));
+    library_names.push_back(std::string(BASIC_CALLOUT_LIBRARY));
+
+    // Load the libraries.
+    EXPECT_TRUE(HooksManager::loadLibraries(library_names));
+
+    // Execute the callouts.  The first library implements the calculation.
+    //
+    // r3 = (7 * d1 - d2) * d3
+    //
+    // The last-loaded library implements the calculation
+    //
+    // r3 = (10 + d1) * d2 - d3
+    //
+    // Putting the processing for each library together in the appropriate
+    // order, we get:
+    //
+    // r3 = ((10 * d1 + d1) - d2) * d2 * d3 - d3
+    {
+        SCOPED_TRACE("Calculation with libraries loaded");
+        executeCallCallouts(10, 3, 33, 2, 62, 3, 183);
+    }
+
+    // Try unloading the libraries.
+    EXPECT_NO_THROW(HooksManager::unloadLibraries());
+
+    // Re-execute the calculation - callouts can be called but as nothing
+    // happens, the result should always be -1.
+    {
+        SCOPED_TRACE("Calculation with libraries not loaded");
+        executeCallCallouts(-1, 3, -1, 22, -1, 83, -1);
+    }
+}
+
+// This is effectively the same test as above, but with a library generating
+// an error when loaded. It is expected that the failing library will not be
+// loaded, but others will be.
+
+TEST_F(HooksManagerTest, LoadLibrariesWithError) {
+
+    // Set up the list of libraries to be loaded.
+    std::vector<std::string> library_names;
+    library_names.push_back(std::string(FULL_CALLOUT_LIBRARY));
+    library_names.push_back(std::string(INCORRECT_VERSION_LIBRARY));
+    library_names.push_back(std::string(BASIC_CALLOUT_LIBRARY));
+
+    // Load the libraries.  We expect a failure return because one of the
+    // libraries fails to load.
+    EXPECT_FALSE(HooksManager::loadLibraries(library_names));
+}
+
+// Test that we can unload a set of libraries while we have a CalloutHandle
+// created on them in existence, and can delete the handle afterwards.
+
+TEST_F(HooksManagerTest, CalloutHandleUnloadLibrary) {
+
+    // Set up the list of libraries to be loaded.
+    std::vector<std::string> library_names;
+    library_names.push_back(std::string(FULL_CALLOUT_LIBRARY));
+
+    // Load the libraries.
+    EXPECT_TRUE(HooksManager::loadLibraries(library_names));
+
+    // Execute the callouts.  Thiis library implements:
+    //
+    // r3 = (7 * d1 - d2) * d3
+    {
+        SCOPED_TRACE("Calculation with full callout library loaded");
+        executeCallCallouts(7, 4, 28, 8, 20, 2, 40);
+    }
+
+    // Get an outstanding callout handle on this library.
+    CalloutHandlePtr handle = HooksManager::createCalloutHandle();
+
+    // Execute once of the callouts again to ensure that the handle contains
+    // memory allocated by the library.
+    HooksManager::callCallouts(ServerHooks::CONTEXT_CREATE, *handle);
+
+    // Unload the libraries.
+    HooksManager::unloadLibraries();
+
+    // Deleting the callout handle should not cause a segmentation fault.
+    handle.reset();
+}
+
+// Test that we can load a new set of libraries while we have a CalloutHandle
+// created on them in existence, and can delete the handle afterwards.
+
+TEST_F(HooksManagerTest, CalloutHandleLoadLibrary) {
+
+    // Set up the list of libraries to be loaded.
+    std::vector<std::string> library_names;
+    library_names.push_back(std::string(FULL_CALLOUT_LIBRARY));
+
+    // Load the libraries.
+    EXPECT_TRUE(HooksManager::loadLibraries(library_names));
+
+    // Execute the callouts.  Thiis library implements:
+    //
+    // r3 = (7 * d1 - d2) * d3
+    {
+        SCOPED_TRACE("Calculation with full callout library loaded");
+        executeCallCallouts(7, 4, 28, 8, 20, 2, 40);
+    }
+
+    // Get an outstanding callout handle on this library and execute one of
+    // the callouts again to ensure that the handle contains memory allocated
+    // by the library.
+    CalloutHandlePtr handle = HooksManager::createCalloutHandle();
+    HooksManager::callCallouts(ServerHooks::CONTEXT_CREATE, *handle);
+
+    // Load a new library that implements the calculation
+    //
+    // r3 = (10 + d1) * d2 - d3
+    std::vector<std::string> new_library_names;
+    new_library_names.push_back(std::string(BASIC_CALLOUT_LIBRARY));
+
+    // Load the libraries.
+    EXPECT_TRUE(HooksManager::loadLibraries(new_library_names));
+
+    // Execute the calculation.  Note that we still have the CalloutHandle
+    // for the old library: however, this should not affect the new calculation.
+    {
+        SCOPED_TRACE("Calculation with basic callout library loaded");
+        executeCallCallouts(10, 7, 17, 3, 51, 16, 35);
+    }
+
+    // Deleting the old callout handle should not cause a segmentation fault.
+    handle.reset();
+}
+
+// This is effectively the same test as the LoadLibraries test.
+
+TEST_F(HooksManagerTest, ReloadSameLibraries) {
+
+    // Set up the list of libraries to be loaded.
+    std::vector<std::string> library_names;
+    library_names.push_back(std::string(FULL_CALLOUT_LIBRARY));
+    library_names.push_back(std::string(BASIC_CALLOUT_LIBRARY));
+
+    // Load the libraries.
+    EXPECT_TRUE(HooksManager::loadLibraries(library_names));
+
+    // Execute the callouts.  See the LoadLibraries test for an explanation of
+    // the calculation.
+    {
+        SCOPED_TRACE("Calculation with libraries loaded");
+        executeCallCallouts(10, 3, 33, 2, 62, 3, 183);
+    }
+
+    // Try reloading the libraries and re-execute the calculation - we should
+    // get the same results.
+    EXPECT_NO_THROW(HooksManager::loadLibraries(library_names));
+    {
+        SCOPED_TRACE("Calculation with libraries reloaded");
+        executeCallCallouts(10, 3, 33, 2, 62, 3, 183);
+    }
+}
+
+TEST_F(HooksManagerTest, ReloadLibrariesReverseOrder) {
+
+    // Set up the list of libraries to be loaded and load them.
+    std::vector<std::string> library_names;
+    library_names.push_back(std::string(FULL_CALLOUT_LIBRARY));
+    library_names.push_back(std::string(BASIC_CALLOUT_LIBRARY));
+    EXPECT_TRUE(HooksManager::loadLibraries(library_names));
+
+    // Execute the callouts.  The first library implements the calculation.
+    //
+    // r3 = (7 * d1 - d2) * d3
+    //
+    // The last-loaded library implements the calculation
+    //
+    // r3 = (10 + d1) * d2 - d3
+    //
+    // Putting the processing for each library together in the given order
+    // gives.
+    //
+    // r3 = ((10 * d1 + d1) - d2) * d2 * d3 - d3
+    {
+        SCOPED_TRACE("Calculation with libraries loaded");
+        executeCallCallouts(10, 3, 33, 2, 62, 3, 183);
+    }
+
+    // Reload the libraries in the reverse order.
+    std::reverse(library_names.begin(), library_names.end());
+    EXPECT_TRUE(HooksManager::loadLibraries(library_names));
+
+    // The calculation in the reverse order gives:
+    //
+    // r3 = ((((7 + d1) * d1) * d2 - d2) - d3) * d3
+    {
+        SCOPED_TRACE("Calculation with libraries loaded in reverse order");
+        executeCallCallouts(7, 3, 30, 3, 87, 7, 560);
+    }
+}
+
+// Local callouts for the test of server-registered callouts.
+
+namespace {
+
+    int
+testPreCallout(CalloutHandle& handle) {
+    handle.setArgument("result", static_cast<int>(1027));
+    return (0);
+}
+
+int
+testPostCallout(CalloutHandle& handle) {
+    int result;
+    handle.getArgument("result", result);
+    result *= 2;
+    handle.setArgument("result", result);
+    return (0);
+}
+
+}
+
+// The next test registers the pre and post- callouts above for hook hookpt_two,
+// and checks they are called.
+
+TEST_F(HooksManagerTest, PrePostCalloutTest) {
+
+    // Load a single library.
+    std::vector<std::string> library_names;
+    library_names.push_back(std::string(FULL_CALLOUT_LIBRARY));
+    EXPECT_TRUE(HooksManager::loadLibraries(library_names));
+
+    // Load the pre- and post- callouts.
+    HooksManager::preCalloutsLibraryHandle().registerCallout("hookpt_two",
+                                                             testPreCallout);
+    HooksManager::postCalloutsLibraryHandle().registerCallout("hookpt_two",
+                                                              testPostCallout);
+
+    // Execute the callouts.  hookpt_two implements the calculation:
+    //
+    //  "result - data_2"
+    //
+    // With the pre- and post- callouts above, the result expected is
+    //
+    // (1027 - data_2) * 2
+    CalloutHandlePtr handle = HooksManager::createCalloutHandle();
+    handle->setArgument("result", static_cast<int>(0));
+    handle->setArgument("data_2", static_cast<int>(15));
+
+    HooksManager::callCallouts(hookpt_two_index_, *handle);
+
+    int result = 0;
+    handle->getArgument("result", result);
+    EXPECT_EQ(2024, result);
+
+    // ... and check that the pre- and post- callout functions don't survive a
+    // reload.
+    EXPECT_TRUE(HooksManager::loadLibraries(library_names));
+    handle = HooksManager::createCalloutHandle();
+
+    handle->setArgument("result", static_cast<int>(0));
+    handle->setArgument("data_2", static_cast<int>(15));
+
+    HooksManager::callCallouts(hookpt_two_index_, *handle);
+
+    result = 0;
+    handle->getArgument("result", result);
+    EXPECT_EQ(-15, result);
+}
+
+// Check that everything works even with no libraries loaded.  First that
+// calloutsPresent() always returns false.
+
+TEST_F(HooksManagerTest, NoLibrariesCalloutsPresent) {
+    // No callouts should be present on any hooks.
+    EXPECT_FALSE(HooksManager::calloutsPresent(hookpt_one_index_));
+    EXPECT_FALSE(HooksManager::calloutsPresent(hookpt_two_index_));
+    EXPECT_FALSE(HooksManager::calloutsPresent(hookpt_three_index_));
+}
+
+TEST_F(HooksManagerTest, NoLibrariesCallCallouts) {
+    executeCallCallouts(-1, 3, -1, 22, -1, 83, -1);
+}
+
+// Test the encapsulation of the ServerHooks::registerHook() method.
+
+TEST_F(HooksManagerTest, RegisterHooks) {
+    ServerHooks::getServerHooks().reset();
+    EXPECT_EQ(2, ServerHooks::getServerHooks().getCount());
+
+    // Check that the hook indexes are as expected. (Use temporary variables
+    // as it appears that Google test can't access the constants.)
+    int sh_cc = ServerHooks::CONTEXT_CREATE;
+    int hm_cc = HooksManager::CONTEXT_CREATE;
+    EXPECT_EQ(sh_cc, hm_cc);
+
+    int sh_cd = ServerHooks::CONTEXT_DESTROY;
+    int hm_cd = HooksManager::CONTEXT_DESTROY;
+    EXPECT_EQ(sh_cd, hm_cd);
+
+    // Register a few hooks and check we have the indexes as expected.
+    EXPECT_EQ(2, HooksManager::registerHook(string("alpha")));
+    EXPECT_EQ(3, HooksManager::registerHook(string("beta")));
+    EXPECT_EQ(4, HooksManager::registerHook(string("gamma")));
+    EXPECT_THROW(static_cast<void>(HooksManager::registerHook(string("alpha"))),
+                 DuplicateHook);
+
+    // ... an check the hooks are as we expect.
+    EXPECT_EQ(5, ServerHooks::getServerHooks().getCount());
+    vector<string> names = ServerHooks::getServerHooks().getHookNames();
+    sort(names.begin(), names.end());
+
+    EXPECT_EQ(string("alpha"), names[0]);
+    EXPECT_EQ(string("beta"), names[1]);
+    EXPECT_EQ(string("context_create"), names[2]);
+    EXPECT_EQ(string("context_destroy"), names[3]);
+    EXPECT_EQ(string("gamma"), names[4]);
+}
+
+// Check that we can get the names of the libraries.
+
+TEST_F(HooksManagerTest, LibraryNames) {
+
+    // Set up the list of libraries to be loaded.
+    std::vector<std::string> library_names;
+    library_names.push_back(std::string(FULL_CALLOUT_LIBRARY));
+    library_names.push_back(std::string(BASIC_CALLOUT_LIBRARY));
+
+    // Check the names before the libraries are loaded.
+    std::vector<std::string> loaded_names = HooksManager::getLibraryNames();
+    EXPECT_TRUE(loaded_names.empty());
+
+    // Load the libraries and check the names again.
+    EXPECT_TRUE(HooksManager::loadLibraries(library_names));
+    loaded_names = HooksManager::getLibraryNames();
+    EXPECT_TRUE(library_names == loaded_names);
+
+    // Unload the libraries and check again.
+    EXPECT_NO_THROW(HooksManager::unloadLibraries());
+    loaded_names = HooksManager::getLibraryNames();
+    EXPECT_TRUE(loaded_names.empty());
+}
+
+// Test the library validation function.
+
+TEST_F(HooksManagerTest, validateLibraries) {
+    // Vector of libraries that failed validation
+    std::vector<std::string> failed;
+
+    // Test different vectors of libraries.
+
+    // No libraries should return a success.
+    std::vector<std::string> libraries;
+
+    failed = HooksManager::validateLibraries(libraries);
+    EXPECT_TRUE(failed.empty());
+
+    // Single valid library should validate.
+    libraries.clear();
+    libraries.push_back(BASIC_CALLOUT_LIBRARY);
+
+    failed = HooksManager::validateLibraries(libraries);
+    EXPECT_TRUE(failed.empty());
+
+    // Multiple valid libraries should succeed.
+    libraries.clear();
+    libraries.push_back(BASIC_CALLOUT_LIBRARY);
+    libraries.push_back(FULL_CALLOUT_LIBRARY);
+    libraries.push_back(UNLOAD_CALLOUT_LIBRARY);
+
+    failed = HooksManager::validateLibraries(libraries);
+    EXPECT_TRUE(failed.empty());
+
+    // Single invalid library should fail.
+    libraries.clear();
+    libraries.push_back(NOT_PRESENT_LIBRARY);
+
+    failed = HooksManager::validateLibraries(libraries);
+    EXPECT_TRUE(failed == libraries);
+
+    // Multiple invalid libraries should fail.
+    libraries.clear();
+    libraries.push_back(INCORRECT_VERSION_LIBRARY);
+    libraries.push_back(NO_VERSION_LIBRARY);
+    libraries.push_back(FRAMEWORK_EXCEPTION_LIBRARY);
+
+    failed = HooksManager::validateLibraries(libraries);
+    EXPECT_TRUE(failed == libraries);
+
+    // Combination of valid and invalid (first one valid) should fail.
+    libraries.clear();
+    libraries.push_back(FULL_CALLOUT_LIBRARY);
+    libraries.push_back(INCORRECT_VERSION_LIBRARY);
+    libraries.push_back(NO_VERSION_LIBRARY);
+
+    std::vector<std::string> expected_failures;
+    expected_failures.push_back(INCORRECT_VERSION_LIBRARY);
+    expected_failures.push_back(NO_VERSION_LIBRARY);
+
+    failed = HooksManager::validateLibraries(libraries);
+    EXPECT_TRUE(failed == expected_failures);
+
+    // Combination of valid and invalid (first one invalid) should fail.
+    libraries.clear();
+    libraries.push_back(NO_VERSION_LIBRARY);
+    libraries.push_back(FULL_CALLOUT_LIBRARY);
+    libraries.push_back(INCORRECT_VERSION_LIBRARY);
+
+    expected_failures.clear();
+    expected_failures.push_back(NO_VERSION_LIBRARY);
+    expected_failures.push_back(INCORRECT_VERSION_LIBRARY);
+
+    failed = HooksManager::validateLibraries(libraries);
+    EXPECT_TRUE(failed == expected_failures);
+}
+
+
+} // Anonymous namespace
diff --git a/src/lib/hooks/tests/incorrect_version_library.cc b/src/lib/hooks/tests/incorrect_version_library.cc
new file mode 100644
index 0000000..bb6eedf
--- /dev/null
+++ b/src/lib/hooks/tests/incorrect_version_library.cc
@@ -0,0 +1,33 @@
+// Copyright (C) 2013  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.
+
+/// @file
+/// @brief Incorrect version function test
+///
+/// This is source of a test library for various test (LibraryManager and
+/// HooksManager).  The characteristics of the library produced from this
+/// file are:
+///
+/// - It contains the version() framework function only, which returns an
+///   incorrect version number.
+
+#include <hooks/hooks.h>
+
+extern "C" {
+
+int version() {
+    return (BIND10_HOOKS_VERSION + 1);
+}
+
+};
diff --git a/src/lib/hooks/tests/library_manager_collection_unittest.cc b/src/lib/hooks/tests/library_manager_collection_unittest.cc
new file mode 100644
index 0000000..7fdbb7d
--- /dev/null
+++ b/src/lib/hooks/tests/library_manager_collection_unittest.cc
@@ -0,0 +1,251 @@
+// Copyright (C) 2013  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 <hooks/callout_handle.h>
+#include <hooks/callout_manager.h>
+#include <hooks/library_manager.h>
+#include <hooks/library_manager_collection.h>
+
+#include <hooks/tests/common_test_class.h>
+#include <hooks/tests/test_libraries.h>
+
+#include <boost/shared_ptr.hpp>
+#include <gtest/gtest.h>
+
+#include <algorithm>
+#include <string>
+
+
+using namespace isc;
+using namespace isc::hooks;
+using namespace std;
+
+namespace {
+
+/// @brief Library manager collection test class
+
+class LibraryManagerCollectionTest : public ::testing::Test,
+                                     public HooksCommonTestClass {
+};
+
+/// @brief Public library manager collection class
+///
+/// This is an instance of the LibraryManagerCollection class but with the
+/// protected methods made public for test purposes.
+
+class PublicLibraryManagerCollection
+                : public isc::hooks::LibraryManagerCollection {
+public:
+    /// @brief Constructor
+    ///
+    /// @param List of libraries that this collection will manage.  The order
+    ///        of the libraries is important.
+    PublicLibraryManagerCollection(const std::vector<std::string>& libraries)
+        : LibraryManagerCollection(libraries)
+    {}
+
+    /// Public methods that call protected methods on the superclass.
+    using LibraryManagerCollection::unloadLibraries;
+};
+
+
+// This is effectively the same test as for LibraryManager, but using the
+// LibraryManagerCollection object.
+
+TEST_F(LibraryManagerCollectionTest, LoadLibraries) {
+
+    // Set up the list of libraries to be loaded.
+    std::vector<std::string> library_names;
+    library_names.push_back(std::string(FULL_CALLOUT_LIBRARY));
+    library_names.push_back(std::string(BASIC_CALLOUT_LIBRARY));
+
+    // Set up the library manager collection and get the callout manager we'll
+    // be using.
+    PublicLibraryManagerCollection lm_collection(library_names);
+
+    // Load the libraries.
+    EXPECT_TRUE(lm_collection.loadLibraries());
+    EXPECT_EQ(2, lm_collection.getLoadedLibraryCount());
+
+    // Execute the callouts.  The first library implements the calculation.
+    //
+    // r3 = (7 * d1 - d2) * d3
+    //
+    // The last-loaded library implements the calculation
+    //
+    // r3 = (10 + d1) * d2 - d3
+    //
+    // Putting the processing for each library together in the appropriate
+    // order, we get:
+    //
+    // r3 = ((10 * d1 + d1) - d2) * d2 * d3 - d3
+    boost::shared_ptr<CalloutManager> manager =
+                                      lm_collection.getCalloutManager();
+    {
+        SCOPED_TRACE("Doing calculation with libraries loaded");
+        executeCallCallouts(manager, 10, 3, 33, 2, 62, 3, 183);
+    }
+
+    // Try unloading the libraries.
+    EXPECT_NO_THROW(lm_collection.unloadLibraries());
+    EXPECT_EQ(0, lm_collection.getLoadedLibraryCount());
+
+    // Re-execute the calculation - callouts can be called but as nothing
+    // happens, the result should always be -1.
+    {
+        SCOPED_TRACE("Doing calculation with libraries not loaded");
+        executeCallCallouts(manager, -1, 3, -1, 22, -1, 83, -1);
+    }
+}
+
+// This is effectively the same test as above, but with a library generating
+// an error when loaded. It is expected that no libraries will be loaded.
+
+TEST_F(LibraryManagerCollectionTest, LoadLibrariesWithError) {
+
+    // Set up the list of libraries to be loaded.
+    std::vector<std::string> library_names;
+    library_names.push_back(std::string(FULL_CALLOUT_LIBRARY));
+    library_names.push_back(std::string(INCORRECT_VERSION_LIBRARY));
+    library_names.push_back(std::string(BASIC_CALLOUT_LIBRARY));
+
+    // Set up the library manager collection and get the callout manager we'll
+    // be using.
+    PublicLibraryManagerCollection lm_collection(library_names);
+
+    // Load the libraries.  We expect a failure status to be returned as
+    // one of the libraries failed to load.
+    EXPECT_FALSE(lm_collection.loadLibraries());
+
+    // Expect no libraries were loaded.
+    EXPECT_EQ(0, lm_collection.getLoadedLibraryCount());
+}
+
+// Check that everything works even with no libraries loaded.
+
+TEST_F(LibraryManagerCollectionTest, NoLibrariesLoaded) {
+    // Set up the list of libraries to be loaded.
+    std::vector<std::string> library_names;
+
+    // Set up the library manager collection and get the callout manager we'll
+    // be using.
+    LibraryManagerCollection lm_collection(library_names);
+    EXPECT_TRUE(lm_collection.loadLibraries());
+    EXPECT_EQ(0, lm_collection.getLoadedLibraryCount());
+    boost::shared_ptr<CalloutManager> manager =
+                                      lm_collection.getCalloutManager();
+
+    // Eecute the calculation - callouts can be called but as nothing
+    // happens, the result should always be -1.
+    executeCallCallouts(manager, -1, 3, -1, 22, -1, 83, -1);
+}
+
+// Check that we can get the names of the libraries.
+
+TEST_F(LibraryManagerCollectionTest, LibraryNames) {
+
+    // Set up the list of libraries to be loaded.
+    std::vector<std::string> library_names;
+    library_names.push_back(std::string(FULL_CALLOUT_LIBRARY));
+    library_names.push_back(std::string(BASIC_CALLOUT_LIBRARY));
+
+    // Set up the library manager collection and get the callout manager we'll
+    // be using.
+    PublicLibraryManagerCollection lm_collection(library_names);
+
+    // Check the names before the libraries are loaded.
+    std::vector<std::string> collection_names = lm_collection.getLibraryNames();
+    EXPECT_TRUE(library_names == collection_names);
+
+    // Load the libraries and check the names again.
+    EXPECT_TRUE(lm_collection.loadLibraries());
+    EXPECT_EQ(2, lm_collection.getLoadedLibraryCount());
+    collection_names = lm_collection.getLibraryNames();
+    EXPECT_TRUE(library_names == collection_names);
+}
+
+// Test the library validation function.
+
+TEST_F(LibraryManagerCollectionTest, validateLibraries) {
+    // Vector of libraries that failed validation
+    std::vector<std::string> failed;
+
+    // Test different vectors of libraries.
+
+    // No libraries should return a success.
+    std::vector<std::string> libraries;
+
+    failed = LibraryManagerCollection::validateLibraries(libraries);
+    EXPECT_TRUE(failed.empty());
+
+    // Single valid library should validate.
+    libraries.clear();
+    libraries.push_back(BASIC_CALLOUT_LIBRARY);
+
+    failed = LibraryManagerCollection::validateLibraries(libraries);
+    EXPECT_TRUE(failed.empty());
+
+    // Multiple valid libraries should succeed.
+    libraries.clear();
+    libraries.push_back(BASIC_CALLOUT_LIBRARY);
+    libraries.push_back(FULL_CALLOUT_LIBRARY);
+    libraries.push_back(UNLOAD_CALLOUT_LIBRARY);
+
+    failed = LibraryManagerCollection::validateLibraries(libraries);
+    EXPECT_TRUE(failed.empty());
+
+    // Single invalid library should fail.
+    libraries.clear();
+    libraries.push_back(NOT_PRESENT_LIBRARY);
+
+    failed = LibraryManagerCollection::validateLibraries(libraries);
+    EXPECT_TRUE(failed == libraries);
+
+    // Multiple invalid libraries should fail.
+    libraries.clear();
+    libraries.push_back(INCORRECT_VERSION_LIBRARY);
+    libraries.push_back(NO_VERSION_LIBRARY);
+    libraries.push_back(FRAMEWORK_EXCEPTION_LIBRARY);
+
+    failed = LibraryManagerCollection::validateLibraries(libraries);
+    EXPECT_TRUE(failed == libraries);
+
+    // Combination of valid and invalid (first one valid) should fail.
+    libraries.clear();
+    libraries.push_back(FULL_CALLOUT_LIBRARY);
+    libraries.push_back(INCORRECT_VERSION_LIBRARY);
+    libraries.push_back(NO_VERSION_LIBRARY);
+
+    std::vector<std::string> expected_failures;
+    expected_failures.push_back(INCORRECT_VERSION_LIBRARY);
+    expected_failures.push_back(NO_VERSION_LIBRARY);
+
+    failed = LibraryManagerCollection::validateLibraries(libraries);
+    EXPECT_TRUE(failed == expected_failures);
+
+    // Combination of valid and invalid (first one invalid) should fail.
+    libraries.clear();
+    libraries.push_back(NO_VERSION_LIBRARY);
+    libraries.push_back(FULL_CALLOUT_LIBRARY);
+    libraries.push_back(INCORRECT_VERSION_LIBRARY);
+
+    expected_failures.clear();
+    expected_failures.push_back(NO_VERSION_LIBRARY);
+    expected_failures.push_back(INCORRECT_VERSION_LIBRARY);
+
+    failed = LibraryManagerCollection::validateLibraries(libraries);
+    EXPECT_TRUE(failed == expected_failures);
+}
+
+} // Anonymous namespace
diff --git a/src/lib/hooks/tests/library_manager_unittest.cc b/src/lib/hooks/tests/library_manager_unittest.cc
new file mode 100644
index 0000000..9336946
--- /dev/null
+++ b/src/lib/hooks/tests/library_manager_unittest.cc
@@ -0,0 +1,569 @@
+// Copyright (C) 2013  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 <hooks/callout_handle.h>
+#include <hooks/callout_manager.h>
+#include <hooks/library_manager.h>
+#include <hooks/server_hooks.h>
+
+#include <hooks/tests/common_test_class.h>
+#include <hooks/tests/marker_file.h>
+#include <hooks/tests/test_libraries.h>
+
+#include <gtest/gtest.h>
+
+#include <algorithm>
+#include <fstream>
+#include <string>
+
+#include <unistd.h>
+
+
+using namespace isc;
+using namespace isc::hooks;
+using namespace std;
+
+namespace {
+
+/// @brief Library manager test class
+
+class LibraryManagerTest : public ::testing::Test,
+                           public HooksCommonTestClass {
+public:
+    /// @brief Constructor
+    ///
+    /// Initializes the CalloutManager object used in the tests.  It sets it
+    /// up with the hooks initialized in the HooksCommonTestClass object and
+    /// with four libraries.
+    LibraryManagerTest() {
+        callout_manager_.reset(new CalloutManager(4));
+
+        // Ensure the marker file is not present at the start of a test.
+        static_cast<void>(unlink(MARKER_FILE));
+    }
+
+    /// @brief Destructor
+    ///
+    /// Ensures a marker file is removed after each test.
+    ~LibraryManagerTest() {
+        static_cast<void>(unlink(MARKER_FILE));
+    }
+
+    /// @brief Marker file present
+    ///
+    /// Convenience function to check whether a marker file is present.  It
+    /// does this by opening the file.
+    ///
+    /// @return true if the marker file is present.
+    bool markerFilePresent() const {
+
+        // Try to open it.
+        std::fstream marker;
+        marker.open(MARKER_FILE, std::fstream::in);
+
+        // Check if it is open and close it if so.
+        bool exists = marker.is_open();
+        if (exists) {
+            marker.close();
+        }
+
+        return (exists);
+    }
+
+    /// @brief Call callouts test
+    ///
+    /// A wrapper around the method of the same name in the HooksCommonTestClass
+    /// object, this passes this class's CalloutManager to that method.
+    ///
+    /// @param r0...r3, d1..d3 Values and intermediate values expected.  They
+    ///        are ordered so that the variables appear in the argument list in
+    ///        the order they are used.  See HooksCommonTestClass::execute for
+    ///        a full description. (rN is used to indicate an expected result,
+    ///        dN is data to be passed to the calculation.)
+    void executeCallCallouts(int r0, int d1, int r1, int d2, int r2, int d3,
+                             int r3) {
+        HooksCommonTestClass::executeCallCallouts(callout_manager_, r0, d1,
+                                                  r1, d2, r2, d3, r3);
+    }
+
+    /// Callout manager used for the test.
+    boost::shared_ptr<CalloutManager> callout_manager_;
+};
+
+/// @brief Library manager class
+///
+/// This is an instance of the LibraryManager class but with the protected
+/// methods made public for test purposes.
+
+class PublicLibraryManager : public isc::hooks::LibraryManager {
+public:
+    /// @brief Constructor
+    ///
+    /// Stores the library name.  The actual loading is done in loadLibrary().
+    ///
+    /// @param name Name of the library to load.  This should be an absolute
+    ///        path name.
+    /// @param index Index of this library.  For all these tests, it will be
+    ///        zero, as we are only using one library.
+    /// @param manager CalloutManager object
+    PublicLibraryManager(const std::string& name, int index,
+                         const boost::shared_ptr<CalloutManager>& manager)
+        : LibraryManager(name, index, manager)
+    {}
+
+    /// Public methods that call protected methods on the superclass.
+    using LibraryManager::openLibrary;
+    using LibraryManager::closeLibrary;
+    using LibraryManager::checkVersion;
+    using LibraryManager::registerStandardCallouts;
+    using LibraryManager::runLoad;
+    using LibraryManager::runUnload;
+};
+
+
+// Check that openLibrary() reports an error when it can't find the specified
+// library.
+
+TEST_F(LibraryManagerTest, NoLibrary) {
+    PublicLibraryManager lib_manager(std::string(NOT_PRESENT_LIBRARY),
+                                     0, callout_manager_);
+    EXPECT_FALSE(lib_manager.openLibrary());
+}
+
+// Check that the openLibrary() and closeLibrary() methods work.
+
+TEST_F(LibraryManagerTest, OpenClose) {
+    PublicLibraryManager lib_manager(std::string(BASIC_CALLOUT_LIBRARY),
+                                     0, callout_manager_);
+
+    // Open and close the library
+    EXPECT_TRUE(lib_manager.openLibrary());
+    EXPECT_TRUE(lib_manager.closeLibrary());
+
+    // Check that a second close on an already closed library does not report
+    // an error.
+    EXPECT_TRUE(lib_manager.closeLibrary());
+}
+
+// Check that the code handles the case of a library with no version function.
+
+TEST_F(LibraryManagerTest, NoVersion) {
+    PublicLibraryManager lib_manager(std::string(NO_VERSION_LIBRARY),
+                                     0, callout_manager_);
+    // Open should succeed.
+    EXPECT_TRUE(lib_manager.openLibrary());
+
+    // Version check should fail.
+    EXPECT_FALSE(lib_manager.checkVersion());
+
+    // Tidy up.
+    EXPECT_TRUE(lib_manager.closeLibrary());
+}
+
+// Check that the code handles the case of a library with a version function
+// that returns an incorrect version number.
+
+TEST_F(LibraryManagerTest, WrongVersion) {
+    PublicLibraryManager lib_manager(std::string(INCORRECT_VERSION_LIBRARY),
+                                     0, callout_manager_);
+    // Open should succeed.
+    EXPECT_TRUE(lib_manager.openLibrary());
+
+    // Version check should fail.
+    EXPECT_FALSE(lib_manager.checkVersion());
+
+    // Tidy up.
+    EXPECT_TRUE(lib_manager.closeLibrary());
+}
+
+// Check that the code handles the case of a library where the version function
+// throws an exception.
+
+TEST_F(LibraryManagerTest, VersionException) {
+    PublicLibraryManager lib_manager(std::string(FRAMEWORK_EXCEPTION_LIBRARY),
+                                     0, callout_manager_);
+    // Open should succeed.
+    EXPECT_TRUE(lib_manager.openLibrary());
+
+    // Version check should fail.
+    EXPECT_FALSE(lib_manager.checkVersion());
+
+    // Tidy up.
+    EXPECT_TRUE(lib_manager.closeLibrary());
+}
+
+// Tests that checkVersion() function succeeds in the case of a library with a
+// version function that returns the correct version number.
+
+TEST_F(LibraryManagerTest, CorrectVersionReturned) {
+    PublicLibraryManager lib_manager(std::string(BASIC_CALLOUT_LIBRARY),
+                                     0, callout_manager_);
+    // Open should succeed.
+    EXPECT_TRUE(lib_manager.openLibrary());
+
+    // Version check should succeed.
+    EXPECT_TRUE(lib_manager.checkVersion());
+
+    // Tidy up.
+    EXPECT_TRUE(lib_manager.closeLibrary());
+}
+
+// Checks the registration of standard callouts.
+
+TEST_F(LibraryManagerTest, RegisterStandardCallouts) {
+
+    // Load the only library, specifying the index of 0 as it's the only
+    // library.  This should load all callouts.
+    PublicLibraryManager lib_manager(std::string(BASIC_CALLOUT_LIBRARY),
+                               0, callout_manager_);
+    EXPECT_TRUE(lib_manager.openLibrary());
+
+    // Check the version of the library.
+    EXPECT_TRUE(lib_manager.checkVersion());
+
+    // Load the standard callouts
+    EXPECT_NO_THROW(lib_manager.registerStandardCallouts());
+
+    // Now execute the callouts in the order expected.  The library performs
+    // the calculation:
+    //
+    // r3 = (10 + d1) * d2 - d3
+    executeCallCallouts(10, 5, 15, 7, 105, 17, 88);
+
+    // Tidy up
+    EXPECT_TRUE(lib_manager.closeLibrary());
+}
+
+// Test that the "load" function is called correctly.
+
+TEST_F(LibraryManagerTest, CheckLoadCalled) {
+
+    // Load the only library, specifying the index of 0 as it's the only
+    // library.  This should load all callouts.
+    PublicLibraryManager lib_manager(std::string(LOAD_CALLOUT_LIBRARY),
+                                     0, callout_manager_);
+    EXPECT_TRUE(lib_manager.openLibrary());
+
+    // Check the version of the library.
+    EXPECT_TRUE(lib_manager.checkVersion());
+
+    // Load the standard callouts
+    EXPECT_NO_THROW(lib_manager.registerStandardCallouts());
+
+    // Check that only context_create and hookpt_one have callouts registered.
+    EXPECT_TRUE(callout_manager_->calloutsPresent(
+                ServerHooks::CONTEXT_CREATE));
+    EXPECT_TRUE(callout_manager_->calloutsPresent(hookpt_one_index_));
+    EXPECT_FALSE(callout_manager_->calloutsPresent(hookpt_two_index_));
+    EXPECT_FALSE(callout_manager_->calloutsPresent(hookpt_three_index_));
+    EXPECT_FALSE(callout_manager_->calloutsPresent(
+                 ServerHooks::CONTEXT_DESTROY));
+
+    // Call the runLoad() method to run the load() function.
+    EXPECT_TRUE(lib_manager.runLoad());
+    EXPECT_TRUE(callout_manager_->calloutsPresent(
+                ServerHooks::CONTEXT_CREATE));
+    EXPECT_TRUE(callout_manager_->calloutsPresent(hookpt_one_index_));
+    EXPECT_TRUE(callout_manager_->calloutsPresent(hookpt_two_index_));
+    EXPECT_TRUE(callout_manager_->calloutsPresent(hookpt_three_index_));
+    EXPECT_FALSE(callout_manager_->calloutsPresent(
+                 ServerHooks::CONTEXT_DESTROY));
+
+    // Now execute the callouts in the order expected.  The library performs
+    // the calculation:
+    //
+    // r3 = (5 * d1 + d2) * d3
+    executeCallCallouts(5, 5, 25, 7, 32, 10, 320);
+
+    // Tidy up
+    EXPECT_TRUE(lib_manager.closeLibrary());
+}
+
+// Check handling of a "load" function that throws an exception
+
+TEST_F(LibraryManagerTest, CheckLoadException) {
+
+    // Load the only library, specifying the index of 0 as it's the only
+    // library.  This should load all callouts.
+    PublicLibraryManager lib_manager(std::string(FRAMEWORK_EXCEPTION_LIBRARY),
+                                     0, callout_manager_);
+    EXPECT_TRUE(lib_manager.openLibrary());
+
+    // Running the load function should fail.
+    EXPECT_FALSE(lib_manager.runLoad());
+
+    // Tidy up
+    EXPECT_TRUE(lib_manager.closeLibrary());
+}
+
+// Check handling of a "load" function that returns an error.
+
+TEST_F(LibraryManagerTest, CheckLoadError) {
+
+    // Load the only library, specifying the index of 0 as it's the only
+    // library.  This should load all callouts.
+    PublicLibraryManager lib_manager(std::string(LOAD_ERROR_CALLOUT_LIBRARY),
+                                     0, callout_manager_);
+    EXPECT_TRUE(lib_manager.openLibrary());
+
+    // Check that we catch a load error
+    EXPECT_FALSE(lib_manager.runLoad());
+
+    // Tidy up
+    EXPECT_TRUE(lib_manager.closeLibrary());
+}
+
+// No unload function
+
+TEST_F(LibraryManagerTest, CheckNoUnload) {
+
+    // Load the only library, specifying the index of 0 as it's the only
+    // library.  This should load all callouts.
+    PublicLibraryManager lib_manager(std::string(BASIC_CALLOUT_LIBRARY),
+                                     0, callout_manager_);
+    EXPECT_TRUE(lib_manager.openLibrary());
+
+    // Check that no unload function returns true.
+    EXPECT_TRUE(lib_manager.runUnload());
+
+    // Tidy up
+    EXPECT_TRUE(lib_manager.closeLibrary());
+}
+
+// Unload function returns an error
+
+TEST_F(LibraryManagerTest, CheckUnloadError) {
+
+    // Load the only library, specifying the index of 0 as it's the only
+    // library.  This should load all callouts.
+    PublicLibraryManager lib_manager(std::string(LOAD_ERROR_CALLOUT_LIBRARY),
+                                     0, callout_manager_);
+    EXPECT_TRUE(lib_manager.openLibrary());
+
+    // Check that unload function returning an error returns false.
+    EXPECT_FALSE(lib_manager.runUnload());
+
+    // Tidy up
+    EXPECT_TRUE(lib_manager.closeLibrary());
+}
+
+// Unload function throws an exception.
+
+TEST_F(LibraryManagerTest, CheckUnloadException) {
+
+    // Load the only library, specifying the index of 0 as it's the only
+    // library.  This should load all callouts.
+    PublicLibraryManager lib_manager(std::string(FRAMEWORK_EXCEPTION_LIBRARY),
+                                     0, callout_manager_);
+    EXPECT_TRUE(lib_manager.openLibrary());
+
+    // Check that we detect that the unload function throws an exception.
+    EXPECT_FALSE(lib_manager.runUnload());
+
+    // Tidy up
+    EXPECT_TRUE(lib_manager.closeLibrary());
+}
+
+// Check that the case of the library's unload() function returning a
+// success is handled correcty.
+
+TEST_F(LibraryManagerTest, CheckUnload) {
+
+    // Load the only library, specifying the index of 0 as it's the only
+    // library.  This should load all callouts.
+    PublicLibraryManager lib_manager(std::string(UNLOAD_CALLOUT_LIBRARY),
+                                     0, callout_manager_);
+    EXPECT_TRUE(lib_manager.openLibrary());
+
+
+    // Check that the marker file is not present (at least that the file
+    // open fails).
+    EXPECT_FALSE(markerFilePresent());
+
+    // Check that unload function runs and returns a success
+    EXPECT_TRUE(lib_manager.runUnload());
+
+    // Check that the marker file was created.
+    EXPECT_TRUE(markerFilePresent());
+
+    // Tidy up
+    EXPECT_TRUE(lib_manager.closeLibrary());
+}
+
+// Test the operation of unloadLibrary().  We load a library with a set
+// of callouts then unload it.  We need to check that the callouts have been
+// removed.  We'll also check that the library's unload() function was called
+// as well.
+
+TEST_F(LibraryManagerTest, LibUnload) {
+
+    // Load the only library, specifying the index of 0 as it's the only
+    // library.  This should load all callouts.
+    PublicLibraryManager lib_manager(std::string(FULL_CALLOUT_LIBRARY),
+                               0, callout_manager_);
+    EXPECT_TRUE(lib_manager.openLibrary());
+
+    // Check the version of the library.
+    EXPECT_TRUE(lib_manager.checkVersion());
+
+    // No callouts should be registered at the moment.
+    EXPECT_FALSE(callout_manager_->calloutsPresent(hookpt_one_index_));
+    EXPECT_FALSE(callout_manager_->calloutsPresent(hookpt_two_index_));
+    EXPECT_FALSE(callout_manager_->calloutsPresent(hookpt_three_index_));
+
+    // Load the single standard callout and check it is registered correctly.
+    EXPECT_NO_THROW(lib_manager.registerStandardCallouts());
+    EXPECT_TRUE(callout_manager_->calloutsPresent(hookpt_one_index_));
+    EXPECT_FALSE(callout_manager_->calloutsPresent(hookpt_two_index_));
+    EXPECT_FALSE(callout_manager_->calloutsPresent(hookpt_three_index_));
+
+    // Call the load function to load the other callouts.
+    EXPECT_TRUE(lib_manager.runLoad());
+    EXPECT_TRUE(callout_manager_->calloutsPresent(hookpt_one_index_));
+    EXPECT_TRUE(callout_manager_->calloutsPresent(hookpt_two_index_));
+    EXPECT_TRUE(callout_manager_->calloutsPresent(hookpt_three_index_));
+
+    // Unload the library and check that the callouts have been removed from
+    // the CalloutManager.
+    lib_manager.unloadLibrary();
+    EXPECT_FALSE(callout_manager_->calloutsPresent(hookpt_one_index_));
+    EXPECT_FALSE(callout_manager_->calloutsPresent(hookpt_two_index_));
+    EXPECT_FALSE(callout_manager_->calloutsPresent(hookpt_three_index_));
+}
+
+// Now come the loadLibrary() tests that make use of all the methods tested
+// above.  These tests are really to make sure that the methods have been
+// tied together correctly.
+
+// First test the basic error cases - no library, no version function, version
+// function returning an error.
+
+TEST_F(LibraryManagerTest, LoadLibraryNoLibrary) {
+    LibraryManager lib_manager(std::string(NOT_PRESENT_LIBRARY), 0,
+                                           callout_manager_);
+    EXPECT_FALSE(lib_manager.loadLibrary());
+}
+
+// Check that the code handles the case of a library with no version function.
+
+TEST_F(LibraryManagerTest, LoadLibraryNoVersion) {
+    LibraryManager lib_manager(std::string(NO_VERSION_LIBRARY), 0,
+                                           callout_manager_);
+    EXPECT_FALSE(lib_manager.loadLibrary());
+}
+
+// Check that the code handles the case of a library with a version function
+// that returns an incorrect version number.
+
+TEST_F(LibraryManagerTest, LoadLibraryWrongVersion) {
+    LibraryManager lib_manager(std::string(INCORRECT_VERSION_LIBRARY), 0,
+                                           callout_manager_);
+    EXPECT_FALSE(lib_manager.loadLibrary());
+}
+
+// Check that the full loadLibrary call works.
+
+TEST_F(LibraryManagerTest, LoadLibrary) {
+    LibraryManager lib_manager(std::string(FULL_CALLOUT_LIBRARY), 0,
+                                           callout_manager_);
+    EXPECT_TRUE(lib_manager.loadLibrary());
+
+    // Now execute the callouts in the order expected.  The library performs
+    // the calculation:
+    //
+    // r3 = (7 * d1 - d2) * d3
+    executeCallCallouts(7, 5, 35, 9, 26, 3, 78);
+
+    EXPECT_TRUE(lib_manager.unloadLibrary());
+
+    // Check that the callouts have been removed from the callout manager.
+    EXPECT_FALSE(callout_manager_->calloutsPresent(hookpt_one_index_));
+    EXPECT_FALSE(callout_manager_->calloutsPresent(hookpt_two_index_));
+    EXPECT_FALSE(callout_manager_->calloutsPresent(hookpt_three_index_));
+}
+
+// Now test for multiple libraries.  We'll load the full callout library
+// first, then load some of the libraries with missing framework functions.
+// This will check that when searching for framework functions, only the
+// specified library is checked, not other loaded libraries. We will
+// load a second library with suitable callouts and check that the callouts
+// are added correctly. Finally, we'll unload one of the libraries and
+// check that only the callouts belonging to that library were removed.
+
+TEST_F(LibraryManagerTest, LoadMultipleLibraries) {
+    // Load a library with all framework functions.
+    LibraryManager lib_manager_1(std::string(FULL_CALLOUT_LIBRARY), 0,
+                                 callout_manager_);
+    EXPECT_TRUE(lib_manager_1.loadLibrary());
+
+    // Attempt to load a library with no version() function.  We should detect
+    // this and not end up calling the function from the already loaded
+    // library.
+    LibraryManager lib_manager_2(std::string(NO_VERSION_LIBRARY), 1,
+                                 callout_manager_);
+    EXPECT_FALSE(lib_manager_2.loadLibrary());
+
+    // Attempt to load the library with an incorrect version.  This should
+    // be detected.
+    LibraryManager lib_manager_3(std::string(INCORRECT_VERSION_LIBRARY), 1,
+                                 callout_manager_);
+    EXPECT_FALSE(lib_manager_3.loadLibrary());
+
+    // Load the basic callout library.  This only has standard callouts so,
+    // if the first library's load() function gets called, some callouts
+    // will be registered twice and lead to incorrect results.
+    LibraryManager lib_manager_4(std::string(BASIC_CALLOUT_LIBRARY), 1,
+                                 callout_manager_);
+    EXPECT_TRUE(lib_manager_4.loadLibrary());
+
+    // Execute the callouts.  The first library implements the calculation.
+    //
+    // r3 = (7 * d1 - d2) * d3
+    //
+    // The last-loaded library implements the calculation
+    //
+    // r3 = (10 + d1) * d2 - d3
+    //
+    // Putting the processing for each library together in the appropriate
+    // order, we get:
+    //
+    // r3 = ((10 * d1 + d1) - d2) * d2 * d3 - d3
+    executeCallCallouts(10, 3, 33, 2, 62, 3, 183);
+
+    // All done, so unload the first library.
+    EXPECT_TRUE(lib_manager_1.unloadLibrary());
+
+    // Now execute the callouts again and check that the results are as
+    // expected for the new calculation.
+    executeCallCallouts(10, 5, 15, 7, 105, 17, 88);
+
+    // ... and tidy up.
+    EXPECT_TRUE(lib_manager_4.unloadLibrary());
+}
+
+// Check that libraries can be validated.
+
+TEST_F(LibraryManagerTest, validateLibraries) {
+    EXPECT_TRUE(LibraryManager::validateLibrary(BASIC_CALLOUT_LIBRARY));
+    EXPECT_TRUE(LibraryManager::validateLibrary(FULL_CALLOUT_LIBRARY));
+    EXPECT_FALSE(LibraryManager::validateLibrary(FRAMEWORK_EXCEPTION_LIBRARY));
+    EXPECT_FALSE(LibraryManager::validateLibrary(INCORRECT_VERSION_LIBRARY));
+    EXPECT_TRUE(LibraryManager::validateLibrary(LOAD_CALLOUT_LIBRARY));
+    EXPECT_TRUE(LibraryManager::validateLibrary(LOAD_ERROR_CALLOUT_LIBRARY));
+    EXPECT_FALSE(LibraryManager::validateLibrary(NOT_PRESENT_LIBRARY));
+    EXPECT_FALSE(LibraryManager::validateLibrary(NO_VERSION_LIBRARY));
+    EXPECT_TRUE(LibraryManager::validateLibrary(UNLOAD_CALLOUT_LIBRARY));
+}
+
+} // Anonymous namespace
diff --git a/src/lib/hooks/tests/load_callout_library.cc b/src/lib/hooks/tests/load_callout_library.cc
new file mode 100644
index 0000000..ae9f470
--- /dev/null
+++ b/src/lib/hooks/tests/load_callout_library.cc
@@ -0,0 +1,119 @@
+// Copyright (C) 2013  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.
+
+/// @file
+/// @brief Basic library with load() function
+///
+/// This is source of a test library for various test (LibraryManager and
+/// HooksManager).  The characteristics of the library produced from this
+/// file are:
+///
+/// - The "version" and "load" framework functions are supplied.  One "standard"
+///   callout is supplied ("hookpt_one") and two non-standard ones which are
+///   registered during the call to "load" on the hooks "hookpt_two" and
+///   "hookpt_three".
+///
+///   All callouts do trivial calculations, the result of all being called in
+///   sequence being
+///
+///   @f[ ((5 * data_1) + data_2) * data_3 @f]
+///
+///   ...where data_1, data_2 and data_3 are the values passed in arguments of
+///   the same name to the three callouts (data_1 passed to hookpt_one, data_2 to
+///   hookpt_two etc.) and the result is returned in the argument "result".
+
+#include <hooks/hooks.h>
+
+using namespace isc::hooks;
+
+extern "C" {
+
+// Callouts
+
+int
+context_create(CalloutHandle& handle) {
+    handle.setContext("result", static_cast<int>(5));
+    handle.setArgument("result", static_cast<int>(5));
+    return (0);
+}
+
+// First callout adds the passed "data_1" argument to the initialized context
+// value of 5. (Note that the value set by context_create is accessed through
+// context and not the argument, so checking that context is correctly passed
+// between callouts in the same library.)
+
+int
+hookpt_one(CalloutHandle& handle) {
+    int data;
+    handle.getArgument("data_1", data);
+
+    int result;
+    handle.getContext("result", result);
+
+    result *= data;
+    handle.setArgument("result", result);
+
+    return (0);
+}
+
+// Second callout multiplies the current context value by the "data_2"
+// argument.
+
+static int
+hook_nonstandard_two(CalloutHandle& handle) {
+    int data;
+    handle.getArgument("data_2", data);
+
+    int result;
+    handle.getArgument("result", result);
+
+    result += data;
+    handle.setArgument("result", result);
+
+    return (0);
+}
+
+// Final callout adds "data_3" to the result.
+
+static int
+hook_nonstandard_three(CalloutHandle& handle) {
+    int data;
+    handle.getArgument("data_3", data);
+
+    int result;
+    handle.getArgument("result", result);
+
+    result *= data;
+    handle.setArgument("result", result);
+
+    return (0);
+}
+
+// Framework functions
+
+int
+version() {
+    return (BIND10_HOOKS_VERSION);
+}
+
+int load(LibraryHandle& handle) {
+    // Register the non-standard functions
+    handle.registerCallout("hookpt_two", hook_nonstandard_two);
+    handle.registerCallout("hookpt_three", hook_nonstandard_three);
+
+    return (0);
+}
+
+};
+
diff --git a/src/lib/hooks/tests/load_error_callout_library.cc b/src/lib/hooks/tests/load_error_callout_library.cc
new file mode 100644
index 0000000..b861d7f
--- /dev/null
+++ b/src/lib/hooks/tests/load_error_callout_library.cc
@@ -0,0 +1,49 @@
+// Copyright (C) 2013  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.
+
+/// @file
+/// @brief Error load library
+///
+/// This is source of a test library for various test (LibraryManager and
+/// HooksManager).  The characteristics of the library produced from this
+/// file are:
+///
+/// - All framework functions are supplied.  "version" returns the correct
+///   value, but "load" and unload return an error.
+
+#include <hooks/hooks.h>
+
+using namespace isc::hooks;
+
+extern "C" {
+
+// Framework functions
+
+int
+version() {
+    return (BIND10_HOOKS_VERSION);
+}
+
+int
+load(LibraryHandle&) {
+    return (1);
+}
+
+int
+unload() {
+    return (1);
+}
+
+};
+
diff --git a/src/lib/hooks/tests/marker_file.h.in b/src/lib/hooks/tests/marker_file.h.in
new file mode 100644
index 0000000..e032cdd
--- /dev/null
+++ b/src/lib/hooks/tests/marker_file.h.in
@@ -0,0 +1,27 @@
+// Copyright (C) 2013  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 MARKER_FILE_H
+#define MARKER_FILE_H
+
+/// @file
+/// Define a marker file that is used in tests to prove that an "unload"
+/// function has been called.
+
+namespace {
+const char* MARKER_FILE = "@abs_builddir@/marker_file.dat";
+}
+
+#endif // MARKER_FILE_H
+
diff --git a/src/lib/hooks/tests/no_version_library.cc b/src/lib/hooks/tests/no_version_library.cc
new file mode 100644
index 0000000..f5b5b9c
--- /dev/null
+++ b/src/lib/hooks/tests/no_version_library.cc
@@ -0,0 +1,30 @@
+// Copyright (C) 2013  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.
+
+/// @file
+/// @brief No version function library
+///
+/// This is source of a test library for various test (LibraryManager and
+/// HooksManager).  The characteristics of the library produced from this
+/// file are:
+///
+/// - No version() function is present.
+
+extern "C" {
+
+int no_version() {
+    return (0);
+}
+
+};
diff --git a/src/lib/hooks/tests/run_unittests.cc b/src/lib/hooks/tests/run_unittests.cc
new file mode 100644
index 0000000..f68a58d
--- /dev/null
+++ b/src/lib/hooks/tests/run_unittests.cc
@@ -0,0 +1,25 @@
+// Copyright (C) 2009  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <log/logger_support.h>
+#include <util/unittests/run_all.h>
+
+#include <gtest/gtest.h>
+
+int
+main(int argc, char* argv[]) {
+    ::testing::InitGoogleTest(&argc, argv);
+    isc::log::initLogger();
+    return (isc::util::unittests::run_all());
+}
diff --git a/src/lib/hooks/tests/server_hooks_unittest.cc b/src/lib/hooks/tests/server_hooks_unittest.cc
new file mode 100644
index 0000000..ca9b6f0
--- /dev/null
+++ b/src/lib/hooks/tests/server_hooks_unittest.cc
@@ -0,0 +1,178 @@
+// Copyright (C) 2013  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 <hooks/server_hooks.h>
+
+#include <gtest/gtest.h>
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+using namespace isc;
+using namespace isc::hooks;
+using namespace std;
+
+namespace {
+
+// Checks the registration of hooks and the interrogation methods.  As the
+// constructor registers two hooks, this is also a test of the constructor.
+
+TEST(ServerHooksTest, RegisterHooks) {
+    ServerHooks& hooks = ServerHooks::getServerHooks();
+    hooks.reset();
+
+    // There should be two hooks already registered, with indexes 0 and 1.
+    EXPECT_EQ(2, hooks.getCount());
+    EXPECT_EQ(0, hooks.getIndex("context_create"));
+    EXPECT_EQ(1, hooks.getIndex("context_destroy"));
+
+    // Check that the constants are as expected. (The intermediate variables
+    // are used because of problems with g++ 4.6.1/Ubuntu 11.10 when resolving
+    // the value of the ServerHooks constants when they appeared within the
+    // gtest macro.)
+    const int create_value = ServerHooks::CONTEXT_CREATE;
+    const int destroy_value = ServerHooks::CONTEXT_DESTROY;
+    EXPECT_EQ(0, create_value);
+    EXPECT_EQ(1, destroy_value);
+
+    // Register another couple of hooks.  The test on returned index is based
+    // on knowledge that the hook indexes are assigned in ascending order.
+    int alpha = hooks.registerHook("alpha");
+    EXPECT_EQ(2, alpha);
+    EXPECT_EQ(2, hooks.getIndex("alpha"));
+
+    int beta = hooks.registerHook("beta");
+    EXPECT_EQ(3, beta);
+    EXPECT_EQ(3, hooks.getIndex("beta"));
+
+    // Should be four hooks now
+    EXPECT_EQ(4, hooks.getCount());
+}
+
+// Check that duplicate names cannot be registered.
+
+TEST(ServerHooksTest, DuplicateHooks) {
+    ServerHooks& hooks = ServerHooks::getServerHooks();
+    hooks.reset();
+
+    // Ensure we can't duplicate one of the existing names.
+    EXPECT_THROW(hooks.registerHook("context_create"), DuplicateHook);
+
+    // Check we can't duplicate a newly registered hook.
+    int gamma = hooks.registerHook("gamma");
+    EXPECT_EQ(2, gamma);
+    EXPECT_THROW(hooks.registerHook("gamma"), DuplicateHook);
+}
+
+// Checks that we can get the name of the hooks.
+
+TEST(ServerHooksTest, GetHookNames) {
+    ServerHooks& hooks = ServerHooks::getServerHooks();
+    hooks.reset();
+    vector<string> expected_names;
+
+    // Add names into the hooks object and to the set of expected names.
+    expected_names.push_back("alpha");
+    expected_names.push_back("beta");
+    expected_names.push_back("gamma");
+    expected_names.push_back("delta");
+    for (int i = 0; i < expected_names.size(); ++i) {
+        hooks.registerHook(expected_names[i].c_str());
+    };
+
+    // Update the expected names to include the pre-defined hook names.
+    expected_names.push_back("context_create");
+    expected_names.push_back("context_destroy");
+
+    // Get the actual hook names
+    vector<string> actual_names = hooks.getHookNames();
+
+    // For comparison, sort the names into alphabetical order and do a straight
+    // vector comparison.
+    sort(expected_names.begin(), expected_names.end());
+    sort(actual_names.begin(), actual_names.end());
+
+    EXPECT_TRUE(expected_names == actual_names);
+}
+
+// Test the inverse hooks functionality (i.e. given an index, get the name).
+
+TEST(ServerHooksTest, GetHookIndexes) {
+    ServerHooks& hooks = ServerHooks::getServerHooks();
+    hooks.reset();
+
+    int alpha = hooks.registerHook("alpha");
+    int beta = hooks.registerHook("beta");
+    int gamma = hooks.registerHook("gamma");
+
+    EXPECT_EQ(std::string("context_create"),
+              hooks.getName(ServerHooks::CONTEXT_CREATE));
+    EXPECT_EQ(std::string("context_destroy"),
+              hooks.getName(ServerHooks::CONTEXT_DESTROY));
+    EXPECT_EQ(std::string("alpha"), hooks.getName(alpha));
+    EXPECT_EQ(std::string("beta"), hooks.getName(beta));
+    EXPECT_EQ(std::string("gamma"), hooks.getName(gamma));
+
+    // Check for an invalid index
+    EXPECT_THROW(hooks.getName(-1), NoSuchHook);
+    EXPECT_THROW(hooks.getName(42), NoSuchHook);
+}
+
+// Test the reset functionality.
+
+TEST(ServerHooksTest, Reset) {
+    ServerHooks& hooks = ServerHooks::getServerHooks();
+    hooks.reset();
+
+    int alpha = hooks.registerHook("alpha");
+    int beta = hooks.registerHook("beta");
+    int gamma = hooks.registerHook("gamma");
+
+    // Check the counts before and after a reset.
+    EXPECT_EQ(5, hooks.getCount());
+    hooks.reset();
+    EXPECT_EQ(2, hooks.getCount());
+
+    // ... and check that the hooks are as expected.
+    EXPECT_EQ(0, hooks.getIndex("context_create"));
+    EXPECT_EQ(1, hooks.getIndex("context_destroy"));
+}
+
+// Check that getting an unknown name throws an exception.
+
+TEST(ServerHooksTest, UnknownHookName) {
+    ServerHooks& hooks = ServerHooks::getServerHooks();
+    hooks.reset();
+
+    EXPECT_THROW(static_cast<void>(hooks.getIndex("unknown")), NoSuchHook);
+}
+
+// Check that the count of hooks is correct.
+
+TEST(ServerHooksTest, HookCount) {
+    ServerHooks& hooks = ServerHooks::getServerHooks();
+    hooks.reset();
+
+    // Insert the names into the hooks object
+    hooks.registerHook("alpha");
+    hooks.registerHook("beta");
+    hooks.registerHook("gamma");
+    hooks.registerHook("delta");
+
+    // Should be two more hooks that the number we have registered.
+    EXPECT_EQ(6, hooks.getCount());
+}
+
+} // Anonymous namespace
diff --git a/src/lib/hooks/tests/test_libraries.h.in b/src/lib/hooks/tests/test_libraries.h.in
new file mode 100644
index 0000000..bb6a24a
--- /dev/null
+++ b/src/lib/hooks/tests/test_libraries.h.in
@@ -0,0 +1,79 @@
+// Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef TEST_LIBRARIES_H
+#define TEST_LIBRARIES_H
+
+#include <config.h>
+
+namespace {
+
+
+// Take care of differences in DLL naming between operating systems.
+
+#ifdef OS_OSX
+#define DLL_SUFFIX ".dylib"
+
+#else
+#define DLL_SUFFIX ".so"
+
+#endif
+
+
+// Names of the libraries used in these tests.  These libraries are built using
+// libtool, so we need to look in the hidden ".libs" directory to locate the
+// .so file.  Note that we access the .so file - libtool creates this as a
+// like to the real shared library.
+
+// Basic library with context_create and three "standard" callouts.
+static const char* BASIC_CALLOUT_LIBRARY = "@abs_builddir@/.libs/libbcl"
+                                           DLL_SUFFIX;
+
+// Library with context_create and three "standard" callouts, as well as
+// load() and unload() functions.
+static const char* FULL_CALLOUT_LIBRARY = "@abs_builddir@/.libs/libfcl"
+                                          DLL_SUFFIX;
+
+// Library where the all framework functions throw an exception
+static const char* FRAMEWORK_EXCEPTION_LIBRARY = "@abs_builddir@/.libs/libfxl"
+                                                 DLL_SUFFIX;
+
+// Library where the version() function returns an incorrect result.
+static const char* INCORRECT_VERSION_LIBRARY = "@abs_builddir@/.libs/libivl"
+                                               DLL_SUFFIX;
+
+// Library where some of the callout registration is done with the load()
+// function.
+static const char* LOAD_CALLOUT_LIBRARY = "@abs_builddir@/.libs/liblcl"
+                                          DLL_SUFFIX;
+
+// Library where the load() function returns an error.
+static const char* LOAD_ERROR_CALLOUT_LIBRARY =
+    "@abs_builddir@/.libs/liblecl" DLL_SUFFIX;
+
+// Name of a library which is not present.
+static const char* NOT_PRESENT_LIBRARY = "@abs_builddir@/.libs/libnothere"
+                                         DLL_SUFFIX;
+
+// Library that does not include a version function.
+static const char* NO_VERSION_LIBRARY = "@abs_builddir@/.libs/libnvl"
+                                        DLL_SUFFIX;
+
+// Library where there is an unload() function.
+static const char* UNLOAD_CALLOUT_LIBRARY = "@abs_builddir@/.libs/libucl"
+                                            DLL_SUFFIX;
+} // anonymous namespace
+
+
+#endif // TEST_LIBRARIES_H
diff --git a/src/lib/hooks/tests/unload_callout_library.cc b/src/lib/hooks/tests/unload_callout_library.cc
new file mode 100644
index 0000000..9baa830
--- /dev/null
+++ b/src/lib/hooks/tests/unload_callout_library.cc
@@ -0,0 +1,52 @@
+// Copyright (C) 2013  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.
+
+/// @file
+/// @brief Basic unload library
+///
+/// This is source of a test library for various test (LibraryManager and
+/// HooksManager).  The characteristics of the library produced from this
+/// file are:
+///
+/// - The "version" and "unload" framework functions are supplied. "version"
+///   returns a valid value and "unload" creates a marker file and returns
+///   success.
+
+#include <hooks/hooks.h>
+#include <hooks/tests/marker_file.h>
+
+#include <fstream>
+
+using namespace isc::hooks;
+
+extern "C" {
+
+// Framework functions
+
+int
+version() {
+    return (BIND10_HOOKS_VERSION);
+}
+
+int
+unload() {
+    // Create the marker file.
+    std::fstream marker;
+    marker.open(MARKER_FILE, std::fstream::out);
+    marker.close();
+
+    return (0);
+}
+
+};
diff --git a/src/lib/python/isc/config/ccsession.py b/src/lib/python/isc/config/ccsession.py
index 206a0ee..4dd73b7 100644
--- a/src/lib/python/isc/config/ccsession.py
+++ b/src/lib/python/isc/config/ccsession.py
@@ -220,6 +220,9 @@ class ModuleCCSession(ConfigData):
         self._remote_module_configs = {}
         self._remote_module_callbacks = {}
 
+        self._notification_callbacks = {}
+        self._last_notif_id = 0
+
         if handle_logging_config:
             self.add_remote_config(path_search('logging.spec', bind10_config.PLUGIN_PATHS),
                                    default_logconfig_handler)
@@ -294,8 +297,27 @@ class ModuleCCSession(ConfigData):
            configuration update. Calls the corresponding handler
            functions if present. Responds on the channel if the
            handler returns a message."""
-        # should we default to an answer? success-by-default? unhandled error?
-        if msg is not None and not CC_PAYLOAD_RESULT in msg:
+        if msg is None:
+            return
+        if CC_PAYLOAD_NOTIFICATION in msg:
+            group_s = env[CC_HEADER_GROUP].split('/', 1)
+            # What to do with these bogus inputs? We just ignore them for now.
+            if len(group_s) != 2:
+                return
+            [prefix, group] = group_s
+            if prefix + '/' != CC_GROUP_NOTIFICATION_PREFIX:
+                return
+            # Now, get the callbacks and call one by one
+            callbacks = self._notification_callbacks.get(group, {})
+            event = msg[CC_PAYLOAD_NOTIFICATION][0]
+            params = None
+            if len(msg[CC_PAYLOAD_NOTIFICATION]) > 1:
+                params = msg[CC_PAYLOAD_NOTIFICATION][1]
+            for key in sorted(callbacks.keys()):
+                callbacks[key](event, params)
+        elif not CC_PAYLOAD_RESULT in msg:
+            # should we default to an answer? success-by-default? unhandled
+            # error?
             answer = None
             try:
                 module_name = env[CC_HEADER_GROUP]
@@ -575,6 +597,64 @@ class ModuleCCSession(ConfigData):
                                     to=CC_TO_WILDCARD,
                                     want_answer=False)
 
+    def subscribe_notification(self, notification_group, callback):
+        """
+        Subscribe to receive notifications in given notification group. When a
+        notification comes to the group, the callback is called with two
+        parameters, the name of the event (the value of `event_name` parameter
+        passed to `notify`) and the parameters of the event (the value
+        of `params` passed to `notify`).
+
+        This is a fast operation (there may be communication with the message
+        queue daemon, but it does not wait for any remote process).
+
+        The callback may get called multiple times (once for each notification).
+        It is possible to subscribe multiple callbacks for the same notification,
+        by multiple calls of this method, and they will be called in the order
+        of registration when the notification comes.
+
+        Throws:
+        - CCSessionError: for low-level communication errors.
+        Params:
+        - notification_group (string): Notification group to subscribe to.
+          Notification with the same value of the same parameter of `notify`
+          will be received.
+        - callback (callable): The callback to be called whenever the
+          notification comes.
+
+          The callback should not raise exceptions, such exceptions are
+          likely to propagate through the loop and terminate program.
+        Returns: Opaque id of the subscription. It can be used to cancel
+          the subscription by unsubscribe_notification.
+        """
+        self._last_notif_id += 1
+        my_id = self._last_notif_id
+        if notification_group in self._notification_callbacks:
+            self._notification_callbacks[notification_group][my_id] = callback
+        else:
+            self._session.group_subscribe(CC_GROUP_NOTIFICATION_PREFIX +
+                                          notification_group)
+            self._notification_callbacks[notification_group] = \
+                { my_id: callback }
+        return (notification_group, my_id)
+
+    def unsubscribe_notification(self, nid):
+        """
+        Remove previous subscription for notifications. Pass the id returned
+        from subscribe_notification.
+
+        Throws:
+        - CCSessionError: for low-level communication errors.
+        - KeyError: The id does not correspond to valid subscription.
+        """
+        (group, cid) = nid
+        del self._notification_callbacks[group][cid]
+        if not self._notification_callbacks[group]:
+            # Removed the last one
+            self._session.group_unsubscribe(CC_GROUP_NOTIFICATION_PREFIX +
+                                            group)
+            del self._notification_callbacks[group]
+
 class UIModuleCCSession(MultiConfigData):
     """This class is used in a configuration user interface. It contains
        specific functions for getting, displaying, and sending
diff --git a/src/lib/python/isc/config/tests/ccsession_test.py b/src/lib/python/isc/config/tests/ccsession_test.py
index 3585337..9c33ef6 100644
--- a/src/lib/python/isc/config/tests/ccsession_test.py
+++ b/src/lib/python/isc/config/tests/ccsession_test.py
@@ -376,6 +376,77 @@ class TestModuleCCSession(unittest.TestCase):
             ],
             fake_session.message_queue)
 
+    def test_notify_receive(self):
+        """
+        Test we can subscribe to notifications, receive them, unsubscribe, etc.
+        """
+        fake_session = FakeModuleCCSession()
+        mccs = self.create_session("spec1.spec", None, None, fake_session)
+        fake_session.group_sendmsg({"notification": ["event", {
+            "param": True
+        }]}, "notifications/group")
+        # Not subscribed to notifications -> not subscribed to
+        # 'notifications/group' -> message not eaten yet
+        mccs.check_command()
+        self.assertEqual(fake_session.message_queue, [['notifications/group',
+                         None, {'notification': ['event', {'param': True}]},
+                         False]])
+        # Place to log called notifications
+        notifications = []
+        def notified(tag, event, params):
+            notifications.append((tag, event, params))
+        # Subscribe to the notifications. Twice.
+        id1 = mccs.subscribe_notification('group',
+                                          lambda event, params:
+                                              notified("first", event, params))
+        id2 = mccs.subscribe_notification('group',
+                                          lambda event, params:
+                                              notified("second", event,
+                                              params))
+        # Now the message gets eaten because we are subscribed, and both
+        # callbacks are called.
+        mccs.check_command()
+        self.assertEqual(fake_session.message_queue, [])
+        self.assertEqual(notifications, [
+            ("first", "event", {'param': True}),
+            ("second", "event", {'param': True})
+        ])
+        del notifications[:]
+        # If a notification for different group comes, it is left untouched.
+        fake_session.group_sendmsg({"notification": ["event", {
+            "param": True
+        }]}, "notifications/other")
+        mccs.check_command()
+        self.assertEqual(notifications, [])
+        self.assertEqual(fake_session.message_queue, [['notifications/other',
+                         None, {'notification': ['event', {'param': True}]},
+                         False]])
+        del fake_session.message_queue[:]
+        # Unsubscribe one of the notifications and see that only the other
+        # is triggered.
+        mccs.unsubscribe_notification(id2)
+        fake_session.group_sendmsg({"notification": ["event", {
+            "param": True
+        }]}, "notifications/group")
+        mccs.check_command()
+        self.assertEqual(fake_session.message_queue, [])
+        self.assertEqual(notifications, [
+            ("first", "event", {'param': True})
+        ])
+        del notifications[:]
+        # If we try to unsubscribe again, it complains.
+        self.assertRaises(KeyError, mccs.unsubscribe_notification, id2)
+        # Unsubscribe the other one too. From now on, it doesn't eat the
+        # messages again.
+        mccs.unsubscribe_notification(id1)
+        fake_session.group_sendmsg({"notification": ["event", {
+            "param": True
+        }]}, "notifications/group")
+        mccs.check_command()
+        self.assertEqual(fake_session.message_queue, [['notifications/group',
+                         None, {'notification': ['event', {'param': True}]},
+                         False]])
+
     def my_config_handler_ok(self, new_config):
         return isc.config.ccsession.create_answer(0)
 
diff --git a/src/lib/python/isc/memmgr/Makefile.am b/src/lib/python/isc/memmgr/Makefile.am
index efb4742..f00dba6 100644
--- a/src/lib/python/isc/memmgr/Makefile.am
+++ b/src/lib/python/isc/memmgr/Makefile.am
@@ -1,6 +1,6 @@
 SUBDIRS = . tests
 
-python_PYTHON = __init__.py datasrc_info.py
+python_PYTHON = __init__.py builder.py datasrc_info.py
 
 pythondir = $(pyexecdir)/isc/memmgr
 
diff --git a/src/lib/python/isc/memmgr/builder.py b/src/lib/python/isc/memmgr/builder.py
new file mode 100644
index 0000000..5b4eed9
--- /dev/null
+++ b/src/lib/python/isc/memmgr/builder.py
@@ -0,0 +1,99 @@
+# Copyright (C) 2013  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+class MemorySegmentBuilder:
+    """The builder runs in a different thread in the memory manager. It
+    waits for commands from the memory manager, and then executes them
+    in the given order sequentially.
+    """
+
+    def __init__(self, sock, cv, command_queue, response_queue):
+        """ The constructor takes the following arguments:
+
+            sock: A socket using which this builder object notifies the
+                  main thread that it has a response waiting for it.
+
+            cv: A condition variable object that is used by the main
+                thread to tell this builder object that new commands are
+                available to it. Note that this is also used for
+                synchronizing access to the queues, so code that uses
+                MemorySegmentBuilder must use this condition variable's
+                lock object to synchronize its access to the queues.
+
+            command_queue: A list of commands sent by the main thread to
+                           this object. Commands should be executed
+                           sequentially in the given order by this
+                           object.
+
+            response_queue: A list of responses sent by this object to
+                            the main thread. The format of this is
+                            currently not strictly defined. Future
+                            tickets will be able to define it based on
+                            how it's used.
+        """
+
+        self._sock = sock
+        self._cv = cv
+        self._command_queue = command_queue
+        self._response_queue = response_queue
+        self._shutdown = False
+
+    def run(self):
+        """ This is the method invoked when the builder thread is
+            started.  In this thread, be careful when modifying
+            variables passed-by-reference in the constructor. If they
+            are reassigned, they will not refer to the main thread's
+            objects any longer. Any use of command_queue and
+            response_queue must be synchronized by acquiring the lock in
+            the condition variable. This method must normally terminate
+            only when the 'shutdown' command is sent to it.
+        """
+
+        # Acquire the condition variable while running the loop.
+        with self._cv:
+            while not self._shutdown:
+                while len(self._command_queue) == 0:
+                    self._cv.wait()
+                # Move the queue content to a local queue. Be careful of
+                # not making assignments to reference variables.
+                local_command_queue = self._command_queue[:]
+                del self._command_queue[:]
+
+                # Run commands passed in the command queue sequentially
+                # in the given order.  For now, it only supports the
+                # "shutdown" command, which just exits the thread.
+                for command in local_command_queue:
+                    if command == 'shutdown':
+                        self._shutdown = True
+                        # When the shutdown command is received, we do
+                        # not process any further commands.
+                        break
+                    else:
+                        # A bad command was received. Raising an
+                        # exception is not useful in this case as we are
+                        # likely running in a different thread from the
+                        # main thread which would need to be
+                        # notified. Instead return this in the response
+                        # queue.
+                        self._response_queue.append(('bad_command',))
+                        self._shutdown = True
+                        break
+
+                # Notify (any main thread) on the socket about a
+                # response. Otherwise, the main thread may wait in its
+                # loop without knowing there was a problem.
+                if len(self._response_queue) > 0:
+                    while self._sock.send(b'x') != 1:
+                        continue
diff --git a/src/lib/python/isc/memmgr/tests/Makefile.am b/src/lib/python/isc/memmgr/tests/Makefile.am
index 1f78d9f..7a85083 100644
--- a/src/lib/python/isc/memmgr/tests/Makefile.am
+++ b/src/lib/python/isc/memmgr/tests/Makefile.am
@@ -1,5 +1,5 @@
 PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
-PYTESTS = datasrc_info_tests.py
+PYTESTS = builder_tests.py datasrc_info_tests.py
 EXTRA_DIST = $(PYTESTS)
 
 # If necessary (rare cases), explicitly specify paths to dynamic libraries
diff --git a/src/lib/python/isc/memmgr/tests/builder_tests.py b/src/lib/python/isc/memmgr/tests/builder_tests.py
new file mode 100644
index 0000000..328fd74
--- /dev/null
+++ b/src/lib/python/isc/memmgr/tests/builder_tests.py
@@ -0,0 +1,121 @@
+# Copyright (C) 2013  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import unittest
+import socket
+import select
+import threading
+
+import isc.log
+from isc.memmgr.builder import *
+
+class TestMemorySegmentBuilder(unittest.TestCase):
+    def _create_builder_thread(self):
+        (self._master_sock, self._builder_sock) = \
+            socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM)
+
+        self._builder_command_queue = []
+        self._builder_response_queue = []
+
+        self._builder_cv = threading.Condition()
+
+        self._builder = MemorySegmentBuilder(self._builder_sock,
+                                             self._builder_cv,
+                                             self._builder_command_queue,
+                                             self._builder_response_queue)
+        self._builder_thread = threading.Thread(target=self._builder.run)
+
+    def setUp(self):
+        self._create_builder_thread()
+
+    def tearDown(self):
+        # It's the tests' responsibility to stop and join the builder
+        # thread if they start it.
+        self.assertFalse(self._builder_thread.isAlive())
+
+        self._master_sock.close()
+        self._builder_sock.close()
+
+    def test_bad_command(self):
+        """Tests what happens when a bad command is passed to the
+        MemorySegmentBuilder.
+        """
+
+        self._builder_thread.start()
+
+        # Now that the builder thread is running, send it a bad
+        # command. The thread should exit its main loop and be joinable.
+        with self._builder_cv:
+            self._builder_command_queue.append('bad_command')
+            self._builder_cv.notify_all()
+
+        # Wait 5 seconds to receive a notification on the socket from
+        # the builder.
+        (reads, _, _) = select.select([self._master_sock], [], [], 5)
+        self.assertTrue(self._master_sock in reads)
+
+        # Reading 1 byte should not block us here, especially as the
+        # socket is ready to read. It's a hack, but this is just a
+        # testcase.
+        got = self._master_sock.recv(1)
+        self.assertEqual(got, b'x')
+
+        # Wait 5 seconds at most for the main loop of the builder to
+        # exit.
+        self._builder_thread.join(5)
+        self.assertFalse(self._builder_thread.isAlive())
+
+        # The command queue must be cleared, and the response queue must
+        # contain a response that a bad command was sent. The thread is
+        # no longer running, so we can use the queues without a lock.
+        self.assertEqual(len(self._builder_command_queue), 0)
+        self.assertEqual(len(self._builder_response_queue), 1)
+
+        response = self._builder_response_queue[0]
+        self.assertTrue(isinstance(response, tuple))
+        self.assertTupleEqual(response, ('bad_command',))
+        del self._builder_response_queue[:]
+
+    def test_shutdown(self):
+        """Tests that shutdown command exits the MemorySegmentBuilder
+        loop.
+        """
+
+        self._builder_thread.start()
+
+        # Now that the builder thread is running, send it the shutdown
+        # command. The thread should exit its main loop and be joinable.
+        with self._builder_cv:
+            self._builder_command_queue.append('shutdown')
+            # Commands after 'shutdown' must be ignored.
+            self._builder_command_queue.append('bad_command_1')
+            self._builder_command_queue.append('bad_command_2')
+            self._builder_cv.notify_all()
+
+        # Wait 5 seconds at most for the main loop of the builder to
+        # exit.
+        self._builder_thread.join(5)
+        self.assertFalse(self._builder_thread.isAlive())
+
+        # The command queue must be cleared, and the response queue must
+        # be untouched (we don't use it in this test). The thread is no
+        # longer running, so we can use the queues without a lock.
+        self.assertEqual(len(self._builder_command_queue), 0)
+        self.assertEqual(len(self._builder_response_queue), 0)
+
+if __name__ == "__main__":
+    isc.log.init("bind10-test")
+    isc.log.resetUnitTestRootLogger()
+    unittest.main()
diff --git a/src/lib/python/isc/notify/notify_out.py b/src/lib/python/isc/notify/notify_out.py
index a030a51..39c6708 100644
--- a/src/lib/python/isc/notify/notify_out.py
+++ b/src/lib/python/isc/notify/notify_out.py
@@ -509,10 +509,14 @@ class NotifyOut:
             sock.sendto(render.get_data(), 0, addrinfo)
             # count notifying by IPv4 or IPv6 for statistics
             if zone_notify_info.get_socket().family == socket.AF_INET:
-                self._counters.inc('zones', zone_notify_info.zone_name,
+                self._counters.inc('zones',
+                                   zone_notify_info.zone_class,
+                                   zone_notify_info.zone_name,
                                   'notifyoutv4')
             elif zone_notify_info.get_socket().family == socket.AF_INET6:
-                self._counters.inc('zones', zone_notify_info.zone_name,
+                self._counters.inc('zones',
+                                   zone_notify_info.zone_class,
+                                   zone_notify_info.zone_name,
                                   'notifyoutv6')
             logger.info(NOTIFY_OUT_SENDING_NOTIFY, AddressFormatter(addrinfo))
         except (socket.error, addr.InvalidAddress) as err:
diff --git a/src/lib/python/isc/notify/tests/notify_out_test.py b/src/lib/python/isc/notify/tests/notify_out_test.py
index e2b8d27..7097f01 100644
--- a/src/lib/python/isc/notify/tests/notify_out_test.py
+++ b/src/lib/python/isc/notify/tests/notify_out_test.py
@@ -304,10 +304,10 @@ class TestNotifyOut(unittest.TestCase):
 
         self.assertRaises(isc.cc.data.DataNotFoundError,
                           self._notify._counters.get,
-                          'zones', 'example.net.', 'notifyoutv4')
+                          'zones', 'IN', 'example.net.', 'notifyoutv4')
         self.assertRaises(isc.cc.data.DataNotFoundError,
                           self._notify._counters.get,
-                          'zones', 'example.net.', 'notifyoutv6')
+                          'zones', 'IN', 'example.net.', 'notifyoutv6')
 
         example_com_info.prepare_notify_out()
         ret = self._notify._send_notify_message_udp(example_com_info,
@@ -315,38 +315,38 @@ class TestNotifyOut(unittest.TestCase):
         self.assertTrue(ret)
         self.assertEqual(socket.AF_INET, example_com_info.sock_family)
         self.assertEqual(self._notify._counters.get(
-                'zones', 'example.net.', 'notifyoutv4'), 1)
+                'zones', 'IN', 'example.net.', 'notifyoutv4'), 1)
         self.assertEqual(self._notify._counters.get(
-                'zones', 'example.net.', 'notifyoutv6'), 0)
+                'zones', 'IN', 'example.net.', 'notifyoutv6'), 0)
 
     def test_send_notify_message_udp_ipv6(self):
         example_com_info = self._notify._notify_infos[('example.net.', 'IN')]
 
         self.assertRaises(isc.cc.data.DataNotFoundError,
                           self._notify._counters.get,
-                          'zones', 'example.net.', 'notifyoutv4')
+                          'zones', 'IN', 'example.net.', 'notifyoutv4')
         self.assertRaises(isc.cc.data.DataNotFoundError,
                           self._notify._counters.get,
-                          'zones', 'example.net.', 'notifyoutv6')
+                          'zones', 'IN', 'example.net.', 'notifyoutv6')
 
         ret = self._notify._send_notify_message_udp(example_com_info,
                                                     ('2001:db8::53', 53))
         self.assertTrue(ret)
         self.assertEqual(socket.AF_INET6, example_com_info.sock_family)
         self.assertEqual(self._notify._counters.get(
-                'zones', 'example.net.', 'notifyoutv4'), 0)
+                'zones', 'IN', 'example.net.', 'notifyoutv4'), 0)
         self.assertEqual(self._notify._counters.get(
-                'zones', 'example.net.', 'notifyoutv6'), 1)
+                'zones', 'IN', 'example.net.', 'notifyoutv6'), 1)
 
     def test_send_notify_message_with_bogus_address(self):
         example_com_info = self._notify._notify_infos[('example.net.', 'IN')]
 
         self.assertRaises(isc.cc.data.DataNotFoundError,
                           self._notify._counters.get,
-                          'zones', 'example.net.', 'notifyoutv4')
+                          'zones', 'IN', 'example.net.', 'notifyoutv4')
         self.assertRaises(isc.cc.data.DataNotFoundError,
                           self._notify._counters.get,
-                          'zones', 'example.net.', 'notifyoutv6')
+                          'zones', 'IN', 'example.net.', 'notifyoutv6')
 
         # As long as the underlying data source validates RDATA this shouldn't
         # happen, but right now it's not actually the case.  Even if the
@@ -358,10 +358,10 @@ class TestNotifyOut(unittest.TestCase):
 
         self.assertRaises(isc.cc.data.DataNotFoundError,
                           self._notify._counters.get,
-                          'zones', 'example.net.', 'notifyoutv4')
+                          'zones', 'IN', 'example.net.', 'notifyoutv4')
         self.assertRaises(isc.cc.data.DataNotFoundError,
                           self._notify._counters.get,
-                          'zones', 'example.net.', 'notifyoutv4')
+                          'zones', 'IN', 'example.net.', 'notifyoutv4')
 
     def test_zone_notify_handler(self):
         sent_addrs = []
diff --git a/src/lib/python/isc/server_common/.gitignore b/src/lib/python/isc/server_common/.gitignore
new file mode 100644
index 0000000..63239b7
--- /dev/null
+++ b/src/lib/python/isc/server_common/.gitignore
@@ -0,0 +1 @@
+/bind10_server.py
diff --git a/src/lib/python/isc/server_common/bind10_server.py.in b/src/lib/python/isc/server_common/bind10_server.py.in
index 2bac145..5bbd341 100644
--- a/src/lib/python/isc/server_common/bind10_server.py.in
+++ b/src/lib/python/isc/server_common/bind10_server.py.in
@@ -52,8 +52,14 @@ class BIND10Server:
                      initialization.  This is called after the module CC
                      session has started, and can be used for registering
                      interest on remote modules, etc.  If it raises an
-                     exception, the server will be immediatelly stopped.
+                     exception, the server will be immediately stopped.
                      Parameter: None, Return: None
+      _shutdown_module: can be optionally defined for module-specific
+                        finalization. This is called right before the
+                        module CC session is stopped. If it raises an
+                        exception, the server will be immediately
+                        stopped.
+                        Parameter: None, Return: None
 
     """
     # Will be set to True when the server should stop and shut down.
@@ -72,6 +78,11 @@ class BIND10Server:
     # Basically constant, but allow tests to override it.
     _select_fn = select.select
 
+    def __init__(self):
+        self._read_callbacks = {}
+        self._write_callbacks = {}
+        self._error_callbacks = {}
+
     @property
     def shutdown(self):
         return self.__shutdown
@@ -141,7 +152,13 @@ class BIND10Server:
         cc_fileno = self._mod_cc.get_socket().fileno()
         while not self.__shutdown:
             try:
-                (reads, _, _) = self._select_fn([cc_fileno], [], [])
+                read_fds = list(self._read_callbacks.keys())
+                read_fds.append(cc_fileno)
+                write_fds = list(self._write_callbacks.keys())
+                error_fds = list(self._error_callbacks.keys())
+
+                (reads, writes, errors) = \
+                    self._select_fn(read_fds, write_fds, error_fds)
             except select.error as ex:
                 # ignore intterruption by signal; regard other select errors
                 # fatal.
@@ -149,12 +166,28 @@ class BIND10Server:
                     continue
                 else:
                     raise
-            for fileno in reads:
-                if fileno == cc_fileno:
-                    # this shouldn't raise an exception (if it does, we'll
-                    # propagate it)
-                    self._mod_cc.check_command(True)
 
+            for fileno in reads:
+                if fileno in self._read_callbacks:
+                    for callback in self._read_callbacks[fileno]:
+                        callback()
+
+            for fileno in writes:
+                if fileno in self._write_callbacks:
+                    for callback in self._write_callbacks[fileno]:
+                        callback()
+
+            for fileno in errors:
+                if fileno in self._error_callbacks:
+                    for callback in self._error_callbacks[fileno]:
+                        callback()
+
+            if cc_fileno in reads:
+                # this shouldn't raise an exception (if it does, we'll
+                # propagate it)
+                self._mod_cc.check_command(True)
+
+        self._shutdown_module()
         self._mod_cc.send_stopping()
 
     def _command_handler(self, cmd, args):
@@ -173,9 +206,38 @@ class BIND10Server:
         return isc.config.create_answer(1, "Unknown command: " + str(cmd))
 
     def _setup_module(self):
-        """The default implementation of the module specific initilization"""
+        """The default implementation of the module specific initialization"""
         pass
 
+    def _shutdown_module(self):
+        """The default implementation of the module specific finalization"""
+        pass
+
+    def watch_fileno(self, fileno, rcallback=None, wcallback=None, \
+                         xcallback=None):
+        """Register the fileno for the internal select() call.
+
+        *callback's are callable objects which would be called when
+        read, write, error events occur on the specified fileno.
+        """
+        if rcallback is not None:
+            if fileno in self._read_callbacks:
+                self._read_callbacks[fileno].append(rcallback)
+            else:
+                self._read_callbacks[fileno] = [rcallback]
+
+        if wcallback is not None:
+            if fileno in self._write_callbacks:
+                self._write_callbacks[fileno].append(wcallback)
+            else:
+                self._write_callbacks[fileno] = [wcallback]
+
+        if xcallback is not None:
+            if fileno in self._error_callbacks:
+                self._error_callbacks[fileno].append(xcallback)
+            else:
+                self._error_callbacks[fileno] = [xcallback]
+
     def run(self, module_name):
         """Start the server and let it run until it's told to stop.
 
diff --git a/src/lib/python/isc/server_common/tests/bind10_server_test.py b/src/lib/python/isc/server_common/tests/bind10_server_test.py
index a447269..f93eed6 100755
--- a/src/lib/python/isc/server_common/tests/bind10_server_test.py
+++ b/src/lib/python/isc/server_common/tests/bind10_server_test.py
@@ -62,6 +62,7 @@ class MyCCSession(MockModuleCCSession, isc.config.ConfigData):
 
 class MockServer(BIND10Server):
     def __init__(self):
+        BIND10Server.__init__(self)
         self._select_fn = self.select_wrapper
 
     def _setup_ccsession(self):
@@ -90,6 +91,9 @@ class MockServer(BIND10Server):
 class TestBIND10Server(unittest.TestCase):
     def setUp(self):
         self.__server = MockServer()
+        self.__reads = 0
+        self.__writes = 0
+        self.__errors = 0
 
     def test_init(self):
         """Check initial conditions"""
@@ -162,6 +166,16 @@ class TestBIND10Server(unittest.TestCase):
         self.assertTrue(self.__server.shutdown)
         self.assertEqual((0, None), isc.config.parse_answer(answer))
 
+    def test_run_with_shutdown_module(self):
+        """Check run() with module specific shutdown method."""
+        self.shutdown_called = False
+        def check_called():
+            self.shutdown_called = True
+        self.__server.__shutdown = True
+        self.__server._shutdown_module = check_called
+        self.assertEqual(0, self.__server.run('test'))
+        self.assertTrue(self.shutdown_called)
+
     def test_other_command(self):
         self.__server._mod_command_handler = self.__server.mod_command_handler
         answer = self.__server._command_handler('other command', None)
@@ -246,6 +260,34 @@ class TestBIND10Server(unittest.TestCase):
         # others will notice it due to connection reset.
         self.assertFalse(self.__server.mod_ccsession.stopped)
 
+    def my_read_callback(self):
+        self.__reads += 1
+
+    def my_write_callback(self):
+        self.__writes += 1
+
+    def my_error_callback(self):
+        self.__errors += 1
+
+    def test_watch_fileno(self):
+        """Test watching for fileno."""
+        self.select_params = []
+        self.__server._select_fn = \
+            lambda r, w, e: self.select_wrapper(r, w, e,
+                                                ret=([10, 20, 42, TEST_FILENO], [], [30]))
+        self.__server._setup_ccsession()
+
+        self.__server.watch_fileno(10, rcallback=self.my_read_callback)
+        self.__server.watch_fileno(20, rcallback=self.my_read_callback, \
+                                       wcallback=self.my_write_callback)
+        self.__server.watch_fileno(30, xcallback=self.my_error_callback)
+
+        self.__server._run_internal()
+        self.assertEqual([([10, 20, TEST_FILENO], [20], [30])], self.select_params)
+        self.assertEqual(2, self.__reads)
+        self.assertEqual(0, self.__writes)
+        self.assertEqual(1, self.__errors)
+
 if __name__== "__main__":
     isc.log.init("bind10_server_test")
     isc.log.resetUnitTestRootLogger()
diff --git a/src/lib/python/isc/statistics/counters.py b/src/lib/python/isc/statistics/counters.py
index 279c14b..0c2c827 100644
--- a/src/lib/python/isc/statistics/counters.py
+++ b/src/lib/python/isc/statistics/counters.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2012  Internet Systems Consortium.
+# Copyright (C) 2012-2013  Internet Systems Consortium.
 #
 # Permission to use, copy, modify, and distribute this software for any
 # purpose with or without fee is hereby granted, provided that the above
@@ -81,19 +81,47 @@ def _add_counter(element, spec, identifier):
         return isc.cc.data.find(element, identifier)
     except isc.cc.data.DataNotFoundError:
         pass
-    # check whether spec and identifier are correct
-    isc.config.find_spec_part(spec, identifier)
-    # examine spec of the top-level item first
-    spec_ = isc.config.find_spec_part(spec, identifier.split('/')[0])
-    if spec_['item_type'] == 'named_set' and \
-            spec_['named_set_item_spec']['item_type'] ==  'map':
-        map_spec = spec_['named_set_item_spec']['map_item_spec']
-        for name in isc.config.spec_name_list(map_spec):
-            spec_ = isc.config.find_spec_part(map_spec, name)
-            id_str = '%s/%s/%s' % \
-                tuple(identifier.split('/')[0:2] + [name])
-            isc.cc.data.set(element, id_str, spec_['item_default'])
-    else:
+
+    # Note: If there is a named_set type item in the statistics spec
+    # and if there are map type items under it, all of items under the
+    # map type item need to be added. For example, we're assuming that
+    # this method is now adding a counter whose identifier is like
+    # dir1/dir2/dir3/counter1. If both of dir1 and dir2 are named_set
+    # types, and if dir3 is a map type, and if counter1, counter2, and
+    # counter3 are defined as items under dir3 by the statistics spec,
+    # this method would add other two counters:
+    #
+    #   dir1/dir2/dir3/counter2
+    #   dir1/dir2/dir3/counter3
+    #
+    # Otherwise this method just adds the only counter
+    # dir1/dir2/dir3/counter1.
+
+    # examine spec from the top-level item and know whether
+    # has_named_set, and check whether spec and identifier are correct
+    pidr = ''
+    has_named_set = False
+    for idr in identifier.split('/'):
+        if len(pidr) > 0:
+            idr = pidr + '/' + idr
+        spec_ = isc.config.find_spec_part(spec, idr)
+        if isc.config.spec_part_is_named_set(spec_):
+            has_named_set = True
+            break
+        pidr = idr
+    # add all elements in map type if has_named_set
+    has_map = False
+    if has_named_set:
+        p_idr = identifier.rsplit('/', 1)[0]
+        p_spec = isc.config.find_spec_part(spec, p_idr)
+        if isc.config.spec_part_is_map(p_spec):
+            has_map = True
+            for name in isc.config.spec_name_list(p_spec['map_item_spec']):
+                idr_ = p_idr + '/' + name
+                spc_ = isc.config.find_spec_part(spec, idr_)
+                isc.cc.data.set(element, idr_, spc_['item_default'])
+    # otherwise add a specific element
+    if not has_map:
         spec_ = isc.config.find_spec_part(spec, identifier)
         isc.cc.data.set(element, identifier, spec_['item_default'])
     return isc.cc.data.find(element, identifier)
@@ -161,30 +189,38 @@ class _Statistics():
         "item_title": "Zone names",
         "item_description": "Zone names",
         "named_set_item_spec": {
-          "item_name": "zonename",
-          "item_type": "map",
+          "item_name": "classname",
+          "item_type": "named_set",
           "item_optional": False,
           "item_default": {},
-          "item_title": "Zone name",
-          "item_description": "Zone name",
-          "map_item_spec": [
-            {
-              "item_name": "notifyoutv4",
-              "item_type": "integer",
-              "item_optional": False,
-              "item_default": 0,
-              "item_title": "IPv4 notifies",
-              "item_description": "Number of IPv4 notifies per zone name sent out"
-            },
-            {
-              "item_name": "notifyoutv6",
-              "item_type": "integer",
-              "item_optional": False,
-              "item_default": 0,
-              "item_title": "IPv6 notifies",
-              "item_description": "Number of IPv6 notifies per zone name sent out"
-            }
-          ]
+          "item_title": "RR class name",
+          "item_description": "RR class name",
+          "named_set_item_spec": {
+            "item_name": "zonename",
+            "item_type": "map",
+            "item_optional": False,
+            "item_default": {},
+            "item_title": "Zone name",
+            "item_description": "Zone name",
+            "map_item_spec": [
+              {
+                "item_name": "notifyoutv4",
+                "item_type": "integer",
+                "item_optional": False,
+                "item_default": 0,
+                "item_title": "IPv4 notifies",
+                "item_description": "Number of IPv4 notifies per zone name sent out"
+              },
+              {
+                "item_name": "notifyoutv6",
+                "item_type": "integer",
+                "item_optional": False,
+                "item_default": 0,
+                "item_title": "IPv6 notifies",
+                "item_description": "Number of IPv6 notifies per zone name sent out"
+              }
+            ]
+          }
         }
       }
     ]
@@ -205,20 +241,20 @@ class Counters():
     per-zone counters, a list of counters which can be handled in the
     class are like the following:
 
-        zones/example.com./notifyoutv4
-        zones/example.com./notifyoutv6
-        zones/example.com./xfrrej
-        zones/example.com./xfrreqdone
-        zones/example.com./soaoutv4
-        zones/example.com./soaoutv6
-        zones/example.com./axfrreqv4
-        zones/example.com./axfrreqv6
-        zones/example.com./ixfrreqv4
-        zones/example.com./ixfrreqv6
-        zones/example.com./xfrsuccess
-        zones/example.com./xfrfail
-        zones/example.com./last_ixfr_duration
-        zones/example.com./last_axfr_duration
+        zones/IN/example.com./notifyoutv4
+        zones/IN/example.com./notifyoutv6
+        zones/IN/example.com./xfrrej
+        zones/IN/example.com./xfrreqdone
+        zones/IN/example.com./soaoutv4
+        zones/IN/example.com./soaoutv6
+        zones/IN/example.com./axfrreqv4
+        zones/IN/example.com./axfrreqv6
+        zones/IN/example.com./ixfrreqv4
+        zones/IN/example.com./ixfrreqv6
+        zones/IN/example.com./xfrsuccess
+        zones/IN/example.com./xfrfail
+        zones/IN/example.com./last_ixfr_duration
+        zones/IN/example.com./last_axfr_duration
         ixfr_running
         axfr_running
         socket/unixdomain/open
@@ -275,8 +311,9 @@ class Counters():
                 isc.config.spec_name_list(self._statistics._spec):
             self._zones_item_list = isc.config.spec_name_list(
                 isc.config.find_spec_part(
-                    self._statistics._spec, self._perzone_prefix)\
-                    ['named_set_item_spec']['map_item_spec'])
+                    self._statistics._spec,
+                    '%s/%s/%s' % (self._perzone_prefix,
+                                  '_CLASS_', self._entire_server)))
 
     def clear_all(self):
         """clears all statistics data"""
@@ -387,15 +424,14 @@ class Counters():
         # Start calculation for '_SERVER_' counts
         zones_spec = isc.config.find_spec_part(self._statistics._spec,
                                                self._perzone_prefix)
-        zones_attrs = zones_spec['item_default'][self._entire_server]
         zones_data = {}
-        for attr in zones_attrs:
-            id_str = '%s/%s' % (self._entire_server, attr)
-            sum_ = 0
-            for name in zones:
-                if attr in zones[name]:
-                    sum_ += zones[name][attr]
-            _set_counter(zones_data, zones_spec, id_str, sum_)
+        for cls in zones.keys():
+            for zone in zones[cls].keys():
+                for (attr, val) in zones[cls][zone].items():
+                    id_str1 = '%s/%s/%s' % (cls, zone, attr)
+                    id_str2 = '%s/%s/%s' % (cls, self._entire_server, attr)
+                    _set_counter(zones_data, zones_spec, id_str1, val)
+                    _inc_counter(zones_data, zones_spec, id_str2, val)
         # insert entire-server counts
         statistics_data[self._perzone_prefix] = dict(
             statistics_data[self._perzone_prefix],
diff --git a/src/lib/python/isc/statistics/tests/counters_test.py b/src/lib/python/isc/statistics/tests/counters_test.py
index 5567dda..718c9da 100644
--- a/src/lib/python/isc/statistics/tests/counters_test.py
+++ b/src/lib/python/isc/statistics/tests/counters_test.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2012  Internet Systems Consortium.
+# Copyright (C) 2012-2013  Internet Systems Consortium.
 #
 # Permission to use, copy, modify, and distribute this software for any
 # purpose with or without fee is hereby granted, provided that the above
@@ -23,6 +23,7 @@ import imp
 import isc.config
 
 TEST_ZONE_NAME_STR = "example.com."
+TEST_ZONE_CLASS_STR = "IN"
 TESTDATA_SRCDIR = os.getenv("TESTDATASRCDIR")
 
 from isc.statistics import counters
@@ -196,7 +197,8 @@ class BaseTestCounters():
     def test_perzone_counters(self):
         # for per-zone counters
         for name in self.counters._zones_item_list:
-            args = (self._perzone_prefix, TEST_ZONE_NAME_STR, name)
+            args = (self._perzone_prefix, TEST_ZONE_CLASS_STR,
+                    TEST_ZONE_NAME_STR, name)
             if name.find('last_') == 0 and name.endswith('_duration'):
                 self.counters.start_timer(*args)
                 self.counters.stop_timer(*args)
@@ -204,7 +206,8 @@ class BaseTestCounters():
                 sec = self.counters.get(*args)
                 for zone_str in (self._entire_server, TEST_ZONE_NAME_STR):
                     isc.cc.data.set(self._statistics_data,
-                                    '%s/%s/%s' % (args[0], zone_str, name), sec)
+                                    '%s/%s/%s/%s' % (args[0], args[1],
+                                                     zone_str, name), sec)
                 # twice exec stopper, then second is not changed
                 self.counters.stop_timer(*args)
                 self.assertEqual(self.counters.get(*args), sec)
@@ -220,7 +223,8 @@ class BaseTestCounters():
                 self.assertEqual(self.counters.get(*args), 2)
                 for zone_str in (self._entire_server, TEST_ZONE_NAME_STR):
                     isc.cc.data.set(self._statistics_data,
-                                    '%s/%s/%s' % (args[0], zone_str, name), 2)
+                                    '%s/%s/%s/%s' % (args[0], args[1],
+                                                     zone_str, name), 2)
         self.check_get_statistics()
 
     def test_xfrrunning_counters(self):
@@ -277,7 +281,8 @@ class BaseTestCounters():
     def test_perzone_zero_counters(self):
         # setting all counters to zero
         for name in self.counters._zones_item_list:
-            args = (self._perzone_prefix, TEST_ZONE_NAME_STR, name)
+            args = (self._perzone_prefix, TEST_ZONE_CLASS_STR,
+                    TEST_ZONE_NAME_STR, name)
             if name.find('last_') == 0 and name.endswith('_duration'):
                 zero = 0.0
             else:
@@ -286,7 +291,8 @@ class BaseTestCounters():
             self.counters._incdec(*args, step=zero)
             for zone_str in (self._entire_server, TEST_ZONE_NAME_STR):
                 isc.cc.data.set(self._statistics_data,
-                                '%s/%s/%s' % (args[0], zone_str, name), zero)
+                                '%s/%s/%s/%s' % (args[0], args[1],
+                                                 zone_str, name), zero)
         self.check_get_statistics()
 
     def test_undefined_item(self):
@@ -352,15 +358,19 @@ class DummyNotifyOut(BaseDummyModule):
 
     def inc_counters(self):
         """increments counters"""
-        self.counters.inc('zones', TEST_ZONE_NAME_STR, 'notifyoutv4')
-        self.counters.inc('zones', TEST_ZONE_NAME_STR, 'notifyoutv6')
+        self.counters.inc('zones', TEST_ZONE_CLASS_STR,
+                          TEST_ZONE_NAME_STR, 'notifyoutv4')
+        self.counters.inc('zones', TEST_ZONE_CLASS_STR,
+                          TEST_ZONE_NAME_STR, 'notifyoutv6')
 
 class DummyXfroutSession(BaseDummyModule):
     """A dummy class equivalent to XfroutSession in b10-xfrout"""
     def inc_counters(self):
         """increments counters"""
-        self.counters.inc('zones', TEST_ZONE_NAME_STR, 'xfrreqdone')
-        self.counters.inc('zones', TEST_ZONE_NAME_STR, 'xfrrej')
+        self.counters.inc('zones', TEST_ZONE_CLASS_STR,
+                          TEST_ZONE_NAME_STR, 'xfrreqdone')
+        self.counters.inc('zones', TEST_ZONE_CLASS_STR,
+                          TEST_ZONE_NAME_STR, 'xfrrej')
         self.counters.inc('axfr_running')
         self.counters.inc('ixfr_running')
         self.counters.dec('axfr_running')
@@ -399,8 +409,10 @@ class TestDummyNotifyOut(unittest.TestCase):
 
     def test_counters(self):
         self.assertEqual(
-            {'zones': {'_SERVER_': {'notifyoutv4': 1, 'notifyoutv6': 1},
-                       TEST_ZONE_NAME_STR: {'notifyoutv4': 1, 'notifyoutv6': 1}}},
+            {'zones': {TEST_ZONE_CLASS_STR: { '_SERVER_':
+                           {'notifyoutv4': 1, 'notifyoutv6': 1},
+                                              TEST_ZONE_NAME_STR:
+                           {'notifyoutv4': 1, 'notifyoutv6': 1}}}},
             self.notifier.get_counters())
 
 class TestDummyXfroutServer(unittest.TestCase):
@@ -418,13 +430,14 @@ class TestDummyXfroutServer(unittest.TestCase):
         self.assertEqual(
             {'axfr_running': 0, 'ixfr_running': 0,
              'socket': {'unixdomain': {'open': 1, 'close': 1}},
-             'zones': {'_SERVER_': {'notifyoutv4': 1,
+             'zones': {TEST_ZONE_CLASS_STR: {
+                        '_SERVER_': {'notifyoutv4': 1,
                                     'notifyoutv6': 1,
                                     'xfrrej': 1, 'xfrreqdone': 1},
-                       TEST_ZONE_NAME_STR: {'notifyoutv4': 1,
+                        TEST_ZONE_NAME_STR: {'notifyoutv4': 1,
                                         'notifyoutv6': 1,
                                         'xfrrej': 1,
-                                        'xfrreqdone': 1}}},
+                                        'xfrreqdone': 1}}}},
             self.xfrout_server.get_counters())
 
 if __name__== "__main__":
diff --git a/src/lib/python/isc/statistics/tests/testdata/test_spec2.spec b/src/lib/python/isc/statistics/tests/testdata/test_spec2.spec
index 11b8706..422fc0a 100644
--- a/src/lib/python/isc/statistics/tests/testdata/test_spec2.spec
+++ b/src/lib/python/isc/statistics/tests/testdata/test_spec2.spec
@@ -9,56 +9,66 @@
         "item_type": "named_set",
         "item_optional": false,
         "item_default": {
-          "_SERVER_" : {
-            "notifyoutv4" : 0,
-            "notifyoutv6" : 0,
-            "xfrrej" : 0,
-            "xfrreqdone" : 0
+          "IN" : {
+            "_SERVER_" : {
+              "notifyoutv4" : 0,
+              "notifyoutv6" : 0,
+              "xfrrej" : 0,
+              "xfrreqdone" : 0
+            }
           }
         },
         "item_title": "Zone names",
         "item_description": "Zone names for Xfrout statistics",
         "named_set_item_spec": {
-          "item_name": "zonename",
-          "item_type": "map",
+          "item_name": "classname",
+          "item_type": "named_set",
           "item_optional": false,
           "item_default": {},
-          "item_title": "Zone name",
-          "item_description": "Zone name for Xfrout statistics",
-          "map_item_spec": [
-            {
-              "item_name": "notifyoutv4",
-              "item_type": "integer",
-              "item_optional": false,
-              "item_default": 0,
-              "item_title": "IPv4 notifies",
-              "item_description": "Number of IPv4 notifies per zone name sent out from Xfrout"
-            },
-            {
-              "item_name": "notifyoutv6",
-              "item_type": "integer",
-              "item_optional": false,
-              "item_default": 0,
-              "item_title": "IPv6 notifies",
-              "item_description": "Number of IPv6 notifies per zone name sent out from Xfrout"
-            },
-            {
-              "item_name": "xfrrej",
-              "item_type": "integer",
-              "item_optional": false,
-              "item_default": 0,
-              "item_title": "XFR rejected requests",
-              "item_description": "Number of XFR requests per zone name rejected by Xfrout"
-            },
-            {
-              "item_name": "xfrreqdone",
-              "item_type": "integer",
-              "item_optional": false,
-              "item_default": 0,
-              "item_title": "Requested zone transfers",
-              "item_description": "Number of requested zone transfers completed per zone name"
-            }
-          ]
+          "item_title": "RR class name",
+          "item_description": "RR class name for Xfrout statistics",
+          "named_set_item_spec": {
+            "item_name": "zonename",
+            "item_type": "map",
+            "item_optional": false,
+            "item_default": {},
+            "item_title": "Zone name",
+            "item_description": "Zone name for Xfrout statistics",
+            "map_item_spec": [
+              {
+                "item_name": "notifyoutv4",
+                "item_type": "integer",
+                "item_optional": false,
+                "item_default": 0,
+                "item_title": "IPv4 notifies",
+                "item_description": "Number of IPv4 notifies per zone name sent out from Xfrout"
+              },
+              {
+                "item_name": "notifyoutv6",
+                "item_type": "integer",
+                "item_optional": false,
+                "item_default": 0,
+                "item_title": "IPv6 notifies",
+                "item_description": "Number of IPv6 notifies per zone name sent out from Xfrout"
+              },
+              {
+                "item_name": "xfrrej",
+                "item_type": "integer",
+                "item_optional": false,
+                "item_default": 0,
+                "item_title": "XFR rejected requests",
+                "item_description": "Number of XFR requests per zone name rejected by Xfrout"
+              },
+              {
+                "item_name": "xfrreqdone",
+                "item_type": "integer",
+                "item_optional": false,
+                "item_default": 0,
+                "item_title": "Requested zone transfers",
+                "item_description": "Number of requested zone transfers completed per zone name"
+              }
+            ]
+          }
         }
       },
       {
diff --git a/src/lib/python/isc/statistics/tests/testdata/test_spec3.spec b/src/lib/python/isc/statistics/tests/testdata/test_spec3.spec
index 6c06f69..f620cad 100644
--- a/src/lib/python/isc/statistics/tests/testdata/test_spec3.spec
+++ b/src/lib/python/isc/statistics/tests/testdata/test_spec3.spec
@@ -10,110 +10,120 @@
         "item_type": "named_set",
         "item_optional": false,
         "item_default": {
-          "_SERVER_" : {
-	    "soaoutv4": 0,
-	    "soaoutv6": 0,
-	    "axfrreqv4": 0,
-	    "axfrreqv6": 0,
-	    "ixfrreqv4": 0,
-	    "ixfrreqv6": 0,
-	    "xfrsuccess": 0,
-	    "xfrfail": 0,
-	    "last_ixfr_duration": 0.0,
-	    "last_axfr_duration": 0.0
+          "IN" : {
+            "_SERVER_" : {
+              "soaoutv4": 0,
+              "soaoutv6": 0,
+              "axfrreqv4": 0,
+              "axfrreqv6": 0,
+              "ixfrreqv4": 0,
+              "ixfrreqv6": 0,
+              "xfrsuccess": 0,
+              "xfrfail": 0,
+              "last_ixfr_duration": 0.0,
+              "last_axfr_duration": 0.0
+            }
           }
         },
         "item_title": "Zone names",
         "item_description": "Zone names for Xfrout statistics",
         "named_set_item_spec": {
-          "item_name": "zonename",
-          "item_type": "map",
+          "item_name": "classname",
+          "item_type": "named_set",
           "item_optional": false,
           "item_default": {},
-          "item_title": "Zone name",
-          "item_description": "Zone name for Xfrout statistics",
-          "map_item_spec": [
-            {
-              "item_name": "soaoutv4",
-              "item_type": "integer",
-              "item_optional": false,
-              "item_default": 0,
-              "item_title": "SOAOutv4",
-              "item_description": "Number of IPv4 SOA queries sent from Xfrin"
-            },
-            {
-              "item_name": "soaoutv6",
-              "item_type": "integer",
-              "item_optional": false,
-              "item_default": 0,
-              "item_title": "SOAOutv6",
-              "item_description": "Number of IPv6 SOA queries sent from Xfrin"
-            },
-            {
-              "item_name": "axfrreqv4",
-              "item_type": "integer",
-              "item_optional": false,
-              "item_default": 0,
-              "item_title": "AXFRReqv4",
-              "item_description": "Number of IPv4 AXFR requests sent from Xfrin"
-            },
-            {
-              "item_name": "axfrreqv6",
-              "item_type": "integer",
-              "item_optional": false,
-              "item_default": 0,
-              "item_title": "AXFRReqv6",
-              "item_description": "Number of IPv6 AXFR requests sent from Xfrin"
-            },
-            {
-              "item_name": "ixfrreqv4",
-              "item_type": "integer",
-              "item_optional": false,
-              "item_default": 0,
-              "item_title": "IXFRReqv4",
-              "item_description": "Number of IPv4 IXFR requests sent from Xfrin"
-            },
-            {
-              "item_name": "ixfrreqv6",
-              "item_type": "integer",
-              "item_optional": false,
-              "item_default": 0,
-              "item_title": "IXFRReqv6",
-              "item_description": "Number of IPv6 IXFR requests sent from Xfrin"
-            },
-            {
-              "item_name": "xfrsuccess",
-              "item_type": "integer",
-              "item_optional": false,
-              "item_default": 0,
-              "item_title": "XfrSuccess",
-              "item_description": "Number of zone transfer requests succeeded"
-            },
-            {
-              "item_name": "xfrfail",
-              "item_type": "integer",
-              "item_optional": false,
-              "item_default": 0,
-              "item_title": "XfrFail",
-              "item_description": "Number of zone transfer requests failed"
-            },
-            {
-              "item_name": "last_ixfr_duration",
-              "item_type": "real",
-              "item_optional": false,
-              "item_default": 0.0,
-              "item_title": "Last IXFR duration",
-              "item_description": "Duration of the last IXFR. 0.0 means no successful IXFR done."
-            },
-            {
-              "item_name": "last_axfr_duration",
-              "item_type": "real",
-              "item_optional": false,
-              "item_default": 0.0,
-              "item_title": "Last AXFR duration",
-              "item_description": "Duration of the last AXFR. 0.0 means no successful AXFR done."
-            }
-          ]
+          "item_title": "RR class name",
+          "item_description": "RR class name for Xfrout statistics",
+          "named_set_item_spec": {
+            "item_name": "zonename",
+            "item_type": "map",
+            "item_optional": false,
+            "item_default": {},
+            "item_title": "Zone name",
+            "item_description": "Zone name for Xfrout statistics",
+            "map_item_spec": [
+              {
+                "item_name": "soaoutv4",
+                "item_type": "integer",
+                "item_optional": false,
+                "item_default": 0,
+                "item_title": "SOAOutv4",
+                "item_description": "Number of IPv4 SOA queries sent from Xfrin"
+              },
+              {
+                "item_name": "soaoutv6",
+                "item_type": "integer",
+                "item_optional": false,
+                "item_default": 0,
+                "item_title": "SOAOutv6",
+                "item_description": "Number of IPv6 SOA queries sent from Xfrin"
+              },
+              {
+                "item_name": "axfrreqv4",
+                "item_type": "integer",
+                "item_optional": false,
+                "item_default": 0,
+                "item_title": "AXFRReqv4",
+                "item_description": "Number of IPv4 AXFR requests sent from Xfrin"
+              },
+              {
+                "item_name": "axfrreqv6",
+                "item_type": "integer",
+                "item_optional": false,
+                "item_default": 0,
+                "item_title": "AXFRReqv6",
+                "item_description": "Number of IPv6 AXFR requests sent from Xfrin"
+              },
+              {
+                "item_name": "ixfrreqv4",
+                "item_type": "integer",
+                "item_optional": false,
+                "item_default": 0,
+                "item_title": "IXFRReqv4",
+                "item_description": "Number of IPv4 IXFR requests sent from Xfrin"
+              },
+              {
+                "item_name": "ixfrreqv6",
+                "item_type": "integer",
+                "item_optional": false,
+                "item_default": 0,
+                "item_title": "IXFRReqv6",
+                "item_description": "Number of IPv6 IXFR requests sent from Xfrin"
+              },
+              {
+                "item_name": "xfrsuccess",
+                "item_type": "integer",
+                "item_optional": false,
+                "item_default": 0,
+                "item_title": "XfrSuccess",
+                "item_description": "Number of zone transfer requests succeeded"
+              },
+              {
+                "item_name": "xfrfail",
+                "item_type": "integer",
+                "item_optional": false,
+                "item_default": 0,
+                "item_title": "XfrFail",
+                "item_description": "Number of zone transfer requests failed"
+              },
+              {
+                "item_name": "last_ixfr_duration",
+                "item_type": "real",
+                "item_optional": false,
+                "item_default": 0.0,
+                "item_title": "Last IXFR duration",
+                "item_description": "Duration of the last IXFR. 0.0 means no successful IXFR done."
+              },
+              {
+                "item_name": "last_axfr_duration",
+                "item_type": "real",
+                "item_optional": false,
+                "item_default": 0.0,
+                "item_title": "Last AXFR duration",
+                "item_description": "Duration of the last AXFR. 0.0 means no successful AXFR done."
+              }
+            ]
+          }
         }
       },
       {
diff --git a/src/lib/util/Makefile.am b/src/lib/util/Makefile.am
index 6d07ac8..ff5ef40 100644
--- a/src/lib/util/Makefile.am
+++ b/src/lib/util/Makefile.am
@@ -37,10 +37,6 @@ libb10_util_la_SOURCES += encode/base32hex_from_binary.h
 libb10_util_la_SOURCES += encode/base_n.cc encode/hex.h
 libb10_util_la_SOURCES += encode/binary_from_base32hex.h
 libb10_util_la_SOURCES += encode/binary_from_base16.h
-libb10_util_la_SOURCES += hooks/callout_manager.h hooks/callout_manager.cc
-libb10_util_la_SOURCES += hooks/callout_handle.h hooks/callout_handle.cc
-libb10_util_la_SOURCES += hooks/library_handle.h hooks/library_handle.cc
-libb10_util_la_SOURCES += hooks/server_hooks.h hooks/server_hooks.cc
 libb10_util_la_SOURCES += random/qid_gen.h random/qid_gen.cc
 libb10_util_la_SOURCES += random/random_number_generator.h
 
diff --git a/src/lib/util/hooks/callout_handle.cc b/src/lib/util/hooks/callout_handle.cc
deleted file mode 100644
index 8052823..0000000
--- a/src/lib/util/hooks/callout_handle.cc
+++ /dev/null
@@ -1,121 +0,0 @@
-// Copyright (C) 2013  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/hooks/callout_handle.h>
-#include <util/hooks/callout_manager.h>
-#include <util/hooks/library_handle.h>
-#include <util/hooks/server_hooks.h>
-
-#include <string>
-#include <utility>
-#include <vector>
-
-using namespace std;
-using namespace isc::util;
-
-namespace isc {
-namespace util {
-
-// Constructor.
-CalloutHandle::CalloutHandle(const boost::shared_ptr<CalloutManager>& manager)
-    : arguments_(), context_collection_(), manager_(manager), skip_(false) {
-
-    // Call the "context_create" hook.  We should be OK doing this - although
-    // the constructor has not finished running, all the member variables
-    // have been created.
-    manager_->callCallouts(ServerHooks::CONTEXT_CREATE, *this);
-}
-
-// Destructor
-CalloutHandle::~CalloutHandle() {
-
-    // Call the "context_destroy" hook.  We should be OK doing this - although
-    // the destructor is being called, all the member variables are still in
-    // existence.
-    manager_->callCallouts(ServerHooks::CONTEXT_DESTROY, *this);
-}
-
-// Return the name of all argument items.
-
-vector<string>
-CalloutHandle::getArgumentNames() const {
-
-    vector<string> names;
-    for (ElementCollection::const_iterator i = arguments_.begin();
-         i != arguments_.end(); ++i) {
-        names.push_back(i->first);
-    }
-
-    return (names);
-}
-
-// Return the library handle allowing the callout to access the CalloutManager
-// registration/deregistration functions.
-
-LibraryHandle&
-CalloutHandle::getLibraryHandle() const {
-    return (manager_->getLibraryHandle());
-}
-
-// Return the context for the currently pointed-to library.  This version is
-// used by the "setContext()" method and creates a context for the current
-// library if it does not exist.
-
-CalloutHandle::ElementCollection&
-CalloutHandle::getContextForLibrary() {
-    int libindex = manager_->getLibraryIndex();
-
-    // Access a reference to the element collection for the given index,
-    // creating a new element collection if necessary, and return it.
-    return (context_collection_[libindex]);
-}
-
-// The "const" version of the above, used by the "getContext()" method.  If
-// the context for the current library doesn't exist, throw an exception.
-
-const CalloutHandle::ElementCollection&
-CalloutHandle::getContextForLibrary() const {
-    int libindex = manager_->getLibraryIndex();
-
-    ContextCollection::const_iterator libcontext =
-        context_collection_.find(libindex);
-    if (libcontext == context_collection_.end()) {
-        isc_throw(NoSuchCalloutContext, "unable to find callout context "
-                  "associated with the current library index (" << libindex <<
-                  ")");
-    }
-
-    // Return a reference to the context's element collection.
-    return (libcontext->second);
-}
-
-// Return the name of all items in the context associated with the current]
-// library.
-
-vector<string>
-CalloutHandle::getContextNames() const {
-
-    vector<string> names;
-
-    const ElementCollection& elements = getContextForLibrary();
-    for (ElementCollection::const_iterator i = elements.begin();
-         i != elements.end(); ++i) {
-        names.push_back(i->first);
-    }
-
-    return (names);
-}
-
-} // namespace util
-} // namespace isc
diff --git a/src/lib/util/hooks/callout_handle.h b/src/lib/util/hooks/callout_handle.h
deleted file mode 100644
index 0033505..0000000
--- a/src/lib/util/hooks/callout_handle.h
+++ /dev/null
@@ -1,353 +0,0 @@
-// Copyright (C) 2013  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 CALLOUT_HANDLE_H
-#define CALLOUT_HANDLE_H
-
-#include <exceptions/exceptions.h>
-#include <util/hooks/library_handle.h>
-
-#include <boost/any.hpp>
-#include <boost/shared_ptr.hpp>
-
-#include <map>
-#include <string>
-#include <vector>
-
-namespace isc {
-namespace util {
-
-/// @brief No such argument
-///
-/// Thrown if an attempt is made access an argument that does not exist.
-
-class NoSuchArgument : public Exception {
-public:
-    NoSuchArgument(const char* file, size_t line, const char* what) :
-        isc::Exception(file, line, what) {}
-};
-
-/// @brief No such callout context item
-///
-/// Thrown if an attempt is made to get an item of data from this callout's
-/// context and either the context or an item in the context with that name
-/// does not exist.
-
-class NoSuchCalloutContext : public Exception {
-public:
-    NoSuchCalloutContext(const char* file, size_t line, const char* what) :
-        isc::Exception(file, line, what) {}
-};
-
-// Forward declaration of the library handle and related collection classes.
-
-class CalloutManager;
-class LibraryHandle;
-
-/// @brief Per-packet callout handle
-///
-/// An object of this class is associated with every packet (or request)
-/// processed by the server.  It forms the principle means of passing data
-/// between the server and the user-library callouts.
-///
-/// The class allows access to the following information:
-///
-/// - Arguments.  When the callouts associated with a hook are called, they
-///   are passed information by the server (and can return information to it)
-///   through name/value pairs.  Each of these pairs is an argument and the
-///   information is accessed through the {get,set}Argument() methods.
-///
-/// - Per-packet context.  Each packet has a context associated with it, this
-///   context being  on a per-library basis.  In other words, As a packet passes
-///   through the callouts associated with a given library, the callouts can
-///   associate and retrieve information with the packet.  The per-library
-///   nature of the context means that the callouts within a given library can
-///   pass packet-specific information between one another, but they cannot pass
-///   information to callous within another library.  Typically such context
-///   is created in the "context_create" callout and destroyed in the
-///   "context_destroy" callout.  The information is accessed through the
-///   {get,set}Context() methods.
-///
-/// - Per-library handle.  Allows the callout to dynamically register and
-///   deregister callouts. (In the latter case, only functions registered by
-///   functions in the same library as the callout doing the deregistration
-///   can be removed: callouts registered by other libraries cannot be
-///   modified.)
-
-class CalloutHandle {
-public:
-
-    /// Typedef to allow abbreviation of iterator specification in methods.
-    /// The std::string is the argument name and the "boost::any" is the
-    /// corresponding value associated with it.
-    typedef std::map<std::string, boost::any> ElementCollection;
-
-    /// Typedef to allow abbreviations in specifications when accessing
-    /// context.  The ElementCollection is the name/value collection for
-    /// a particular context.  The "int" corresponds to the index of an
-    /// associated library - there is a 1:1 correspondence between libraries
-    /// and a name.value collection.
-    ///
-    /// The collection of contexts is stored in a map, as not every library
-    /// will require creation of a context associated with each packet.  In
-    /// addition, the structure is more flexible in that the size does not
-    /// need to be set when the CalloutHandle is constructed.
-    typedef std::map<int, ElementCollection> ContextCollection;
-
-
-    /// @brief Constructor
-    ///
-    /// Creates the object and calls the callouts on the "context_create"
-    /// hook.
-    ///
-    /// @param manager Pointer to the callout manager object.
-    CalloutHandle(const boost::shared_ptr<CalloutManager>& manager);
-
-    /// @brief Destructor
-    ///
-    /// Calls the context_destroy callback to release any per-packet context.
-    ~CalloutHandle();
-
-    /// @brief Set argument
-    ///
-    /// Sets the value of an argument.  The argument is created if it does not
-    /// already exist.
-    ///
-    /// @param name Name of the argument.
-    /// @param value Value to set.  That can be of any data type.
-    template <typename T>
-    void setArgument(const std::string& name, T value) {
-        arguments_[name] = value;
-    }
-
-    /// @brief Get argument
-    ///
-    /// Gets the value of an argument.
-    ///
-    /// @param name Name of the element in the argument list to get.
-    /// @param value [out] Value to set.  The type of "value" is important:
-    ///        it must match the type of the value set.
-    ///
-    /// @throw NoSuchArgument No argument with the given name is present.
-    /// @throw boost::bad_any_cast An argument with the given name is present,
-    ///        but the data type of the value is not the same as the type of
-    ///        the variable provided to receive the value.
-    template <typename T>
-    void getArgument(const std::string& name, T& value) const {
-        ElementCollection::const_iterator element_ptr = arguments_.find(name);
-        if (element_ptr == arguments_.end()) {
-            isc_throw(NoSuchArgument, "unable to find argument with name " <<
-                      name);
-        }
-
-        value = boost::any_cast<T>(element_ptr->second);
-    }
-    
-    /// @brief Get argument names
-    ///
-    /// Returns a vector holding the names of arguments in the argument
-    /// vector.
-    ///
-    /// @return Vector of strings reflecting argument names.
-    std::vector<std::string> getArgumentNames() const;
-
-    /// @brief Delete argument
-    ///
-    /// Deletes an argument of the given name.  If an argument of that name
-    /// does not exist, the method is a no-op.
-    ///
-    /// N.B. If the element is a raw pointer, the pointed-to data is NOT deleted
-    /// by this method.
-    ///
-    /// @param name Name of the element in the argument list to set.
-    void deleteArgument(const std::string& name) {
-        static_cast<void>(arguments_.erase(name));
-    }
-
-    /// @brief Delete all arguments
-    ///
-    /// Deletes all arguments associated with this context.
-    ///
-    /// N.B. If any elements are raw pointers, the pointed-to data is NOT
-    /// deleted by this method.
-    void deleteAllArguments() {
-        arguments_.clear();
-    }
-
-    /// @brief Set skip flag
-    ///
-    /// Sets the "skip" variable in the callout handle.  This variable is
-    /// interrogated by the server to see if the remaining callouts associated
-    /// with the current hook should be bypassed.
-    ///
-    /// @param skip New value of the "skip" flag.
-    void setSkip(bool skip) {
-        skip_ = skip;
-    }
-
-    /// @brief Get skip flag
-    ///
-    /// Gets the current value of the "skip" flag.
-    ///
-    /// @return Current value of the skip flag.
-    bool getSkip() const {
-        return (skip_);
-    }
-
-    /// @brief Access current library handle
-    ///
-    /// Returns a reference to the current library handle.  This function is
-    /// only available when called by a callout (which in turn is called
-    /// through the "callCallouts" method), as it is only then that the current
-    /// library index is valid.  A callout uses the library handle to
-    /// dynamically register or deregister callouts.
-    ///
-    /// @return Reference to the library handle.
-    ///
-    /// @throw InvalidIndex thrown if this method is called when the current
-    ///        library index is invalid (typically if it is called outside of
-    ///        the active callout).
-    LibraryHandle& getLibraryHandle() const;
-
-    /// @brief Set context
-    ///
-    /// Sets an element in the context associated with the current library.  If
-    /// an element of the name is already present, it is replaced.
-    ///
-    /// @param name Name of the element in the context to set.
-    /// @param value Value to set.
-    template <typename T>
-    void setContext(const std::string& name, T value) {
-        getContextForLibrary()[name] = value;
-    }
-
-    /// @brief Get context
-    ///
-    /// Gets an element from the context associated with the current library.
-    ///
-    /// @param name Name of the element in the context to get.
-    /// @param value [out] Value to set.  The type of "value" is important:
-    ///        it must match the type of the value set.
-    ///
-    /// @throw NoSuchCalloutContext Thrown if no context element with the name
-    ///        "name" is present.
-    /// @throw boost::bad_any_cast Thrown if the context element is present
-    ///        but the type of the data is not the same as the type of the
-    ///        variable provided to receive its value.
-    template <typename T>
-    void getContext(const std::string& name, T& value) const {
-        const ElementCollection& lib_context = getContextForLibrary();
-
-        ElementCollection::const_iterator element_ptr = lib_context.find(name);
-        if (element_ptr == lib_context.end()) {
-            isc_throw(NoSuchCalloutContext, "unable to find callout context "
-                      "item " << name << " in the context associated with "
-                      "current library");
-        }
-
-        value = boost::any_cast<T>(element_ptr->second);
-    }
-    
-    /// @brief Get context names
-    ///
-    /// Returns a vector holding the names of items in the context associated
-    /// with the current library.
-    ///
-    /// @return Vector of strings reflecting the names of items in the callout
-    ///         context associated with the current library.
-    std::vector<std::string> getContextNames() const;
-
-    /// @brief Delete context element
-    ///
-    /// Deletes an item of the given name from the context associated with the
-    /// current library.  If an item  of that name does not exist, the method is
-    /// a no-op.
-    ///
-    /// N.B. If the element is a raw pointer, the pointed-to data is NOT deleted
-    /// by this.
-    ///
-    /// @param name Name of the context item to delete.
-    void deleteContext(const std::string& name) {
-        static_cast<void>(getContextForLibrary().erase(name));
-    }
-
-    /// @brief Delete all context items
-    ///
-    /// Deletes all items from the context associated with the current library.
-    ///
-    /// N.B. If any elements are raw pointers, the pointed-to data is NOT
-    /// deleted by this.
-    void deleteAllContext() {
-        getContextForLibrary().clear();
-    }
-
-
-private:
-    /// @brief Check index
-    ///
-    /// Gets the current library index, throwing an exception if it is not set
-    /// or is invalid for the current library collection.
-    ///
-    /// @return Current library index, valid for this library collection.
-    ///
-    /// @throw InvalidIndex current library index is not valid for the library
-    ///        handle collection.
-    int getLibraryIndex() const;
-
-    /// @brief Return reference to context for current library
-    ///
-    /// Called by all context-setting functions, this returns a reference to
-    /// the callout context for the current library, creating a context if it
-    /// does not exist.
-    ///
-    /// @return Reference to the collection of name/value pairs associated
-    ///         with the current library.
-    ///
-    /// @throw InvalidIndex current library index is not valid for the library
-    ///        handle collection.
-    ElementCollection& getContextForLibrary();
-
-    /// @brief Return reference to context for current library (const version)
-    ///
-    /// Called by all context-accessing functions, this a reference to the
-    /// callout context for the current library.  An exception is thrown if
-    /// it does not exist.
-    ///
-    /// @return Reference to the collection of name/value pairs associated
-    ///         with the current library.
-    ///
-    /// @throw NoSuchCalloutContext Thrown if there is no ElementCollection
-    ///        associated with the current library.
-    const ElementCollection& getContextForLibrary() const;
-
-    // Member variables
-
-    /// Collection of arguments passed to the callouts
-    ElementCollection arguments_;
-
-    /// Context collection - there is one entry per library context.
-    ContextCollection context_collection_;
-
-    /// Callout manager.
-    boost::shared_ptr<CalloutManager> manager_;
-
-    /// "Skip" flag, indicating if the caller should bypass remaining callouts.
-    bool skip_;
-};
-
-} // namespace util
-} // namespace isc
-
-
-#endif // CALLOUT_HANDLE_H
diff --git a/src/lib/util/hooks/callout_manager.cc b/src/lib/util/hooks/callout_manager.cc
deleted file mode 100644
index af20619..0000000
--- a/src/lib/util/hooks/callout_manager.cc
+++ /dev/null
@@ -1,182 +0,0 @@
-// Copyright (C) 2013  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/hooks/callout_handle.h>
-#include <util/hooks/callout_manager.h>
-
-#include <boost/static_assert.hpp>
-
-#include <algorithm>
-#include <functional>
-#include <utility>
-
-using namespace std;
-using namespace isc::util;
-
-namespace isc {
-namespace util {
-
-// Register a callout for the current library.
-
-void
-CalloutManager::registerCallout(const std::string& name, CalloutPtr callout) {
-    // Sanity check that the current library index is set to a valid value.
-    checkLibraryIndex(current_library_);
-
-    // Get the index associated with this hook (validating the name in the
-    // process).
-    int hook_index = hooks_->getIndex(name);
-
-    // Iterate through the callout vector for the hook from start to end,
-    // looking for the first entry where the library index is greater than
-    // the present index.
-    for (CalloutVector::iterator i = hook_vector_[hook_index].begin();
-         i != hook_vector_[hook_index].end(); ++i) {
-        if (i->first > current_library_) {
-            // Found an element whose library index number is greater than the
-            // current index, so insert the new element ahead of this one.
-            hook_vector_[hook_index].insert(i, make_pair(current_library_,
-                                                         callout));
-            return;
-        }
-    }
-
-    // Reached the end of the vector, so there is no element in the (possibly
-    // empty) set of callouts with a library index greater than the current
-    // library index.  Inset the callout at the end of the list.
-    hook_vector_[hook_index].push_back(make_pair(current_library_, callout));
-}
-
-// Check if callouts are present for a given hook index.
-
-bool
-CalloutManager::calloutsPresent(int hook_index) const {
-    // Validate the hook index.
-    if ((hook_index < 0) || (hook_index >= hook_vector_.size())) {
-        isc_throw(NoSuchHook, "hook index " << hook_index <<
-                  " is not valid for the list of registered hooks");
-    }
-
-    // Valid, so are there any callouts associated with that hook?
-    return (!hook_vector_[hook_index].empty());
-}
-
-// Call all the callouts for a given hook.
-
-void
-CalloutManager::callCallouts(int hook_index, CalloutHandle& callout_handle) {
-
-    // Only initialize and iterate if there are callouts present.  This check
-    // also catches the case of an invalid index.
-    if (calloutsPresent(hook_index)) {
-
-        // Clear the "skip" flag so we don't carry state from a previous call.
-        callout_handle.setSkip(false);
-
-        // Duplicate the callout vector for this hook and work through that.
-        // This step is needed because we allow dynamic registration and
-        // deregistration of callouts.  If a callout attached to a hook modified
-        // the list of callouts on that hook, the underlying CalloutVector would
-        // change and potentially affect the iteration through that vector.
-        CalloutVector callouts(hook_vector_[hook_index]);
-
-        // Call all the callouts.
-        for (CalloutVector::const_iterator i = callouts.begin();
-             i != callouts.end(); ++i) {
-            // In case the callout tries to register or deregister a callout,
-            // set the current library index to the index associated with the
-            // library that registered the callout being called.
-            current_library_ = i->first;
-
-            // Call the callout
-            // @todo Log the return status if non-zero
-            static_cast<void>((*i->second)(callout_handle));
-        }
-
-        // Reset the current library index to an invalid value to catch any
-        // programming errors.
-        current_library_ = -1;
-    }
-}
-
-// Deregister a callout registered by the current library on a particular hook.
-
-bool
-CalloutManager::deregisterCallout(const std::string& name, CalloutPtr callout) {
-    // Sanity check that the current library index is set to a valid value.
-    checkLibraryIndex(current_library_);
-
-    // Get the index associated with this hook (validating the name in the
-    // process).
-    int hook_index = hooks_->getIndex(name);
-
-    /// Construct a CalloutEntry matching the current library and the callout
-    /// we want to remove.
-    CalloutEntry target(current_library_, callout);
-
-    /// To decide if any entries were removed, we'll record the initial size
-    /// of the callout vector for the hook, and compare it with the size after
-    /// the removal.
-    size_t initial_size = hook_vector_[hook_index].size();
-
-    // The next bit is standard STL (see "Item 33" in "Effective STL" by
-    // Scott Meyers).
-    //
-    // remove_if reorders the hook vector so that all items not matching
-    // the predicate are at the start of the vector and returns a pointer
-    // to the next element. (In this case, the predicate is that the item
-    // is equal to the value of the passed callout.)  The erase() call
-    // removes everything from that element to the end of the vector, i.e.
-    // all the matching elements.
-    hook_vector_[hook_index].erase(remove_if(hook_vector_[hook_index].begin(),
-                                             hook_vector_[hook_index].end(),
-                                             bind1st(equal_to<CalloutEntry>(),
-                                                     target)),
-                                   hook_vector_[hook_index].end());
-
-    // Return an indication of whether anything was removed.
-    return (initial_size != hook_vector_[hook_index].size());
-}
-
-// Deregister all callouts on a given hook.
-
-bool
-CalloutManager::deregisterAllCallouts(const std::string& name) {
-
-    // Get the index associated with this hook (validating the name in the
-    // process).
-    int hook_index = hooks_->getIndex(name);
-
-    /// Construct a CalloutEntry matching the current library (the callout
-    /// pointer is NULL as we are not checking that).
-    CalloutEntry target(current_library_, NULL);
-
-    /// To decide if any entries were removed, we'll record the initial size
-    /// of the callout vector for the hook, and compare it with the size after
-    /// the removal.
-    size_t initial_size = hook_vector_[hook_index].size();
-
-    // Remove all callouts matching this library.
-    hook_vector_[hook_index].erase(remove_if(hook_vector_[hook_index].begin(),
-                                             hook_vector_[hook_index].end(),
-                                             bind1st(CalloutLibraryEqual(),
-                                                     target)),
-                                   hook_vector_[hook_index].end());
-
-    // Return an indication of whether anything was removed.
-    return (initial_size != hook_vector_[hook_index].size());
-}
-
-} // namespace util
-} // namespace isc
diff --git a/src/lib/util/hooks/callout_manager.h b/src/lib/util/hooks/callout_manager.h
deleted file mode 100644
index 7a22433..0000000
--- a/src/lib/util/hooks/callout_manager.h
+++ /dev/null
@@ -1,301 +0,0 @@
-// Copyright (C) 2013  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 CALLOUT_MANAGER_H
-#define CALLOUT_MANAGER_H
-
-#include <exceptions/exceptions.h>
-#include <util/hooks/library_handle.h>
-#include <util/hooks/server_hooks.h>
-
-#include <boost/shared_ptr.hpp>
-
-#include <map>
-#include <string>
-
-namespace isc {
-namespace util {
-
-/// @brief No such library
-///
-/// Thrown if an attempt is made to set the current library index to a value
-/// that is invalid for the number of loaded libraries.
-class NoSuchLibrary : public Exception {
-public:
-    NoSuchLibrary(const char* file, size_t line, const char* what) :
-        isc::Exception(file, line, what) {}
-};
-
-
-/// @brief Callout Manager
-///
-/// This class manages the registration, deregistration and execution of the
-/// library callouts.
-///
-/// In operation, the class needs to know two items of data:
-///
-/// - The list of server hooks, which is used in two ways.  Firstly, when a
-///   callout registers or deregisters a hook, it does so by name: the
-///   @ref isc::util::ServerHooks object supplies the names of registered
-///   hooks.  Secondly, when the callouts associated with a hook are called by
-///   the server, the server supplies the index of the relevant hook: this is
-///   validated by reference to the list of hook.
-///
-/// - The number of loaded libraries.  Each callout registered by a user
-///   library is associated with that library, the callout manager storing both
-///   a pointer to the callout and the index of the library in the list of
-///   loaded libraries.  Callouts are allowed to dynamically register and
-///   deregister callouts in the same library (including themselves): they
-///   cannot affect callouts registered by another library.  When calling a
-///   callout, the callout manager maintains the idea of a "current library
-///   index": if the callout calls one of the callout registration functions 
-///   (indirectly via the @ref LibraryHandle object), the registration
-///   functions use the "current library index" in their processing.
-///
-/// These two items of data are supplied when an object of this class is
-/// constructed.
-///
-/// Note that the callout function do not access the library manager: instead,
-/// they use a LibraryHandle object.  This contains an internal pointer to
-/// the CalloutManager, but provides a restricted interface.  In that way,
-/// callouts are unable to affect callouts supplied by other libraries.
-
-class CalloutManager {
-private:
-
-    // Private typedefs
-
-    /// Element in the vector of callouts.  The elements in the pair are the
-    /// index of the library from which this callout was registered, and a#
-    /// pointer to the callout itself.
-    typedef std::pair<int, CalloutPtr> CalloutEntry;
-
-    /// An element in the hook vector. Each element is a vector of callouts
-    /// associated with a given hook.
-    typedef std::vector<CalloutEntry> CalloutVector;
-
-public:
-
-    /// @brief Constructor
-    ///
-    /// Initializes member variables, in particular sizing the hook vector
-    /// (the vector of callout vectors) to the appropriate size.
-    ///
-    /// @param hooks Collection of known hook names.
-    /// @param num_libraries Number of loaded libraries.
-    ///
-    /// @throw isc::BadValue if the number of libraries is less than or equal
-    ///        to 0, or if the pointer to the server hooks object is empty.
-    CalloutManager(const boost::shared_ptr<ServerHooks>& hooks,
-                   int num_libraries)
-        : current_library_(-1), hooks_(hooks), hook_vector_(),
-          library_handle_(this), num_libraries_(num_libraries)
-    {
-        if (!hooks) {
-            isc_throw(isc::BadValue, "must pass a pointer to a valid server "
-                      "hooks object to the CalloutManager");
-        } else if (num_libraries <= 0) {
-            isc_throw(isc::BadValue, "number of libraries passed to the "
-                      "CalloutManager must be >= 0");
-        }
-
-        // Parameters OK, do operations that depend on them.
-        hook_vector_.resize(hooks_->getCount());
-    }
-
-    /// @brief Register a callout on a hook for the current library
-    ///
-    /// Registers a callout function for the current library with a given hook
-    /// (the index of the "current library" being given by the current_library_
-    /// member).  The callout is added to the end of the callouts for this
-    /// library that are associated with that hook.
-    ///
-    /// @param name Name of the hook to which the callout is added.
-    /// @param callout Pointer to the callout function to be registered.
-    ///
-    /// @throw NoSuchHook The hook name is unrecognised.
-    /// @throw Unexpected The hook name is valid but an internal data structure
-    ///        is of the wrong size.
-    void registerCallout(const std::string& name, CalloutPtr callout);
-
-    /// @brief De-Register a callout on a hook for the current library
-    ///
-    /// Searches through the functions registered by the the current library
-    /// (the index of the "current library" being given by the current_library_
-    /// member) with the named hook and removes all entries matching the
-    /// callout.
-    ///
-    /// @param name Name of the hook from which the callout is removed.
-    /// @param callout Pointer to the callout function to be removed.
-    ///
-    /// @return true if a one or more callouts were deregistered.
-    ///
-    /// @throw NoSuchHook The hook name is unrecognised.
-    /// @throw Unexpected The hook name is valid but an internal data structure
-    ///        is of the wrong size.
-    bool deregisterCallout(const std::string& name, CalloutPtr callout);
-
-    /// @brief Removes all callouts on a hook for the current library
-    ///
-    /// Removes all callouts associated with a given hook that were registered
-    /// by the current library (the index of the "current library" being given
-    /// by the current_library_ member).
-    ///
-    /// @param name Name of the hook from which the callouts are removed.
-    ///
-    /// @return true if one or more callouts were deregistered.
-    ///
-    /// @throw NoSuchHook Thrown if the hook name is unrecognised.
-    bool deregisterAllCallouts(const std::string& name);
-
-    /// @brief Checks if callouts are present on a hook
-    ///
-    /// Checks all loaded libraries and returns true if at least one callout
-    /// has been registered by any of them for the given hook.
-    ///
-    /// @param hook_index Hook index for which callouts are checked.
-    ///
-    /// @return true if callouts are present, false if not.
-    ///
-    /// @throw NoSuchHook Given index does not correspond to a valid hook.
-    bool calloutsPresent(int hook_index) const;
-
-    /// @brief Calls the callouts for a given hook
-    ///
-    /// Iterates through the libray handles and calls the callouts associated
-    /// with the given hook index.
-    ///
-    /// @note This method invalidates the current library index set with
-    ///       setLibraryIndex().
-    ///
-    /// @param hook_index Index of the hook to call.
-    /// @param callout_handle Reference to the CalloutHandle object for the
-    ///        current object being processed.
-    void callCallouts(int hook_index, CalloutHandle& callout_handle);
-
-    /// @brief Get number of libraries
-    ///
-    /// Returns the number of libraries that this CalloutManager is expected
-    /// to serve.  This is the number passed to its constructor.
-    ///
-    /// @return Number of libraries server by this CalloutManager.
-    int getNumLibraries() const {
-        return (num_libraries_);
-    }
-
-    /// @brief Get current library index
-    ///
-    /// Returns the index of the "current" library.  This the index associated
-    /// with the currently executing callout when callCallouts is executing.
-    /// When callCallouts() is not executing (as is the case when the load()
-    /// function in a user-library is called during the library load process),
-    /// the index can be set by setLibraryIndex().
-    ///
-    /// @note The value set by this method is lost after a call to
-    ///       callCallouts.
-    ///
-    /// @return Current library index.
-    int getLibraryIndex() const {
-        return (current_library_);
-    }
-
-    /// @brief Set current library index
-    ///
-    /// Sets the current library index.  This must be in the range 0 to
-    /// (numlib - 1), where "numlib" is the number of libraries loaded in the
-    /// system, this figure being passed to this object at construction time.
-    ///
-    /// @param library_index New library index.
-    ///
-    /// @throw NoSuchLibrary if the index is not valid.
-    void setLibraryIndex(int library_index) {
-        checkLibraryIndex(library_index);
-        current_library_ = library_index;
-    }
-
-    /// @brief Return library handle
-    ///
-    /// The library handle is available to the user callout via the callout
-    /// handle object.  It provides a cut-down view of the CalloutManager,
-    /// allowing the callout to register and deregister callouts in the
-    /// library of which it is part, whilst denying access to anything that
-    /// may affect other libraries.
-    ///
-    /// @return Reference to callout handle for this manager
-    LibraryHandle& getLibraryHandle() {
-        return (library_handle_);
-    }
-
-private:
-    /// @brief Check library index
-    ///
-    /// Ensures that the current library index is valid.  This is called by
-    /// the hook registration functions.
-    ///
-    /// @param library_index Value to check for validity as a library index.
-    ///
-    /// @throw NoSuchLibrary Library index is not valid.
-    void checkLibraryIndex(int library_index) const {
-        if ((library_index < 0) || (library_index >= num_libraries_)) {
-            isc_throw(NoSuchLibrary, "library index " << library_index <<
-                      " is not valid for the number of loaded libraries (" <<
-                      num_libraries_ << ")");
-        }
-    }
-
-    /// @brief Compare two callout entries for library equality
-    ///
-    /// This is used in callout removal code when all callouts on a hook for a
-    /// given library are being removed.  It checks whether two callout entries
-    /// have the same library index.
-    ///
-    /// @param ent1 First callout entry to check
-    /// @param ent2 Second callout entry to check
-    ///
-    /// @return bool true if the library entries are the same
-    class CalloutLibraryEqual :
-        public std::binary_function<CalloutEntry, CalloutEntry, bool> {
-    public:
-        bool operator()(const CalloutEntry& ent1,
-                        const CalloutEntry& ent2) const {
-            return (ent1.first == ent2.first);
-        }
-    };
-
-    /// Current library index.  When a call is made to any of the callout
-    /// registration methods, this variable indicates the index of the user
-    /// library that should be associated with the call.
-    int current_library_;
-
-    /// List of server hooks.
-    boost::shared_ptr<ServerHooks>  hooks_;
-
-    /// Vector of callout vectors.  There is one entry in this outer vector for
-    /// each hook. Each element is itself a vector, with one entry for each
-    /// callout registered for that hook.
-    std::vector<CalloutVector>  hook_vector_;
-
-    /// LibraryHandle object user by the callout to access the callout
-    /// registration methods on this CalloutManager object.
-    LibraryHandle library_handle_;
-
-    /// Number of libraries.
-    int num_libraries_;
-
-};
-
-} // namespace util
-} // namespace isc
-
-#endif // CALLOUT_MANAGER_H
diff --git a/src/lib/util/hooks/library_handle.cc b/src/lib/util/hooks/library_handle.cc
deleted file mode 100644
index 0a65e54..0000000
--- a/src/lib/util/hooks/library_handle.cc
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright (C) 2013  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/hooks/callout_manager.h>
-#include <util/hooks/library_handle.h>
-
-namespace isc {
-namespace util {
-
-// Callout manipulation - all deferred to the CalloutManager.
-
-void
-LibraryHandle::registerCallout(const std::string& name, CalloutPtr callout) {
-    callout_manager_->registerCallout(name, callout);
-}
-
-bool
-LibraryHandle::deregisterCallout(const std::string& name, CalloutPtr callout) {
-    return (callout_manager_->deregisterCallout(name, callout));
-}
-
-bool
-LibraryHandle::deregisterAllCallouts(const std::string& name) {
-    return (callout_manager_->deregisterAllCallouts(name));
-}
-
-} // namespace util
-} // namespace isc
diff --git a/src/lib/util/hooks/library_handle.h b/src/lib/util/hooks/library_handle.h
deleted file mode 100644
index 43a78a1..0000000
--- a/src/lib/util/hooks/library_handle.h
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright (C) 2013  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 LIBRARY_HANDLE_H
-#define LIBRARY_HANDLE_H
-
-#include <string>
-
-namespace isc {
-namespace util {
-
-// Forward declarations
-class CalloutHandle;
-class CalloutManager;
-
-/// Typedef for a callout pointer.  (Callouts must have "C" linkage.)
-extern "C" {
-    typedef int (*CalloutPtr)(CalloutHandle&);
-};
-
-/// @brief Library handle
-///
-/// This class is accessed by the user library when registering callouts,
-/// either by the library's load() function, or by one of the callouts
-/// themselves.
-///
-/// It is really little more than a shell around the CalloutManager.  By
-/// presenting this object to the user-library callouts, callouts can manage
-/// the callout list for their own library, but cannot affect the callouts
-/// registered by other libraries.
-///
-/// (This restriction is achieved by the CalloutManager maintaining the concept
-/// of the "current library".  When a callout is registered - either by the
-/// library's load() function, or by a callout in the library - the registration
-/// information includes the library active at the time.  When that callout is
-/// called, the CalloutManager uses that information to set the "current
-/// library": the registration functions only operator on data whose
-/// associated library is equal to the "current library".)
-
-class LibraryHandle {
-public:
-
-    /// @brief Constructor
-    ///
-    /// @param manager Back pointer to the containing CalloutManager.
-    ///        This pointer is used to access appropriate methods in that
-    ///        object.  Note that the "raw" pointer is safe - the only
-    ///        instance of the LibraryHandle in the system is as a member of
-    ///        the CalloutManager to which it points.
-    LibraryHandle(CalloutManager* manager) : callout_manager_(manager)
-    {}
-
-    /// @brief Register a callout on a hook
-    ///
-    /// Registers a callout function with a given hook.  The callout is added
-    /// to the end of the callouts for the current library that are associated
-    /// with that hook.
-    ///
-    /// @param name Name of the hook to which the callout is added.
-    /// @param callout Pointer to the callout function to be registered.
-    ///
-    /// @throw NoSuchHook The hook name is unrecognised.
-    /// @throw Unexpected The hook name is valid but an internal data structure
-    ///        is of the wrong size.
-    void registerCallout(const std::string& name, CalloutPtr callout);
-
-    /// @brief De-Register a callout on a hook
-    ///
-    /// Searches through the functions registered by the current library with
-    /// the named hook and removes all entries matching the callout.  It does
-    /// not affect callouts registered by other libraries.
-    ///
-    /// @param name Name of the hook from which the callout is removed.
-    /// @param callout Pointer to the callout function to be removed.
-    ///
-    /// @return true if a one or more callouts were deregistered.
-    ///
-    /// @throw NoSuchHook The hook name is unrecognised.
-    /// @throw Unexpected The hook name is valid but an internal data structure
-    ///        is of the wrong size.
-    bool deregisterCallout(const std::string& name, CalloutPtr callout);
-
-    /// @brief Removes all callouts on a hook
-    ///
-    /// Removes all callouts associated with a given hook that were registered.
-    /// by the current library.  It does not affect callouts that were
-    /// registered by other libraries.
-    ///
-    /// @param name Name of the hook from which the callouts are removed.
-    ///
-    /// @return true if one or more callouts were deregistered.
-    ///
-    /// @throw NoSuchHook Thrown if the hook name is unrecognised.
-    bool deregisterAllCallouts(const std::string& name);
-
-private:
-    /// Back pointer to the collection object for the library
-    CalloutManager* callout_manager_;
-};
-
-} // namespace util
-} // namespace isc
-
-#endif // LIBRARY_HANDLE_H
diff --git a/src/lib/util/hooks/server_hooks.cc b/src/lib/util/hooks/server_hooks.cc
deleted file mode 100644
index 478d94c..0000000
--- a/src/lib/util/hooks/server_hooks.cc
+++ /dev/null
@@ -1,125 +0,0 @@
-// Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <exceptions/exceptions.h>
-#include <util/hooks/server_hooks.h>
-
-#include <utility>
-#include <vector>
-
-using namespace std;
-using namespace isc;
-
-namespace isc {
-namespace util {
-
-// Constructor - register the pre-defined hooks and check that the indexes
-// assigned to them are as expected.
-
-ServerHooks::ServerHooks() {
-    int create = registerHook("context_create");
-    int destroy = registerHook("context_destroy");
-
-    if ((create != CONTEXT_CREATE) || (destroy != CONTEXT_DESTROY)) {
-        isc_throw(Unexpected, "pre-defined hook indexes are not as expected. "
-                  "context_create: expected = " << CONTEXT_CREATE <<
-                  ", actual = " << create <<
-                  ". context_destroy: expected = " << CONTEXT_DESTROY <<
-                  ", actual = " << destroy);
-    }
-}
-
-// Register a hook.  The index assigned to the hook is the current number
-// of entries in the collection, so ensuring that hook indexes are unique
-// and non-negative.
-
-int
-ServerHooks::registerHook(const string& name) {
-
-    // Determine index for the new element and insert.
-    int index = hooks_.size();
-    pair<HookCollection::iterator, bool> result =
-        hooks_.insert(make_pair(name, index));
-
-    if (!result.second) {
-        // New element was not inserted because an element with the same name
-        // already existed.
-        isc_throw(DuplicateHook, "hook with name " << name <<
-                  " is already registered");
-    }
-
-    // New element inserted, return numeric index.
-    return (index);
-}
-
-// Find the index associated with a hook name.
-
-int
-ServerHooks::getIndex(const string& name) const {
-
-    // Get iterator to matching element.
-    HookCollection::const_iterator i = hooks_.find(name);
-    if (i == hooks_.end()) {
-        isc_throw(NoSuchHook, "hook name " << name << " is not recognised");
-    }
-
-    return (i->second);
-}
-
-// Return vector of hook names.  The names are not sorted - it is up to the
-// caller to perform sorting if required.
-
-vector<string>
-ServerHooks::getHookNames() const {
-
-    vector<string> names;
-    HookCollection::const_iterator i;
-    for (i = hooks_.begin(); i != hooks_.end(); ++i) {
-        names.push_back(i->first);
-    }
-
-    return (names);
-}
-
-// Hook registration function methods
-
-// Access the hook registration function vector itself
-
-std::vector<HookRegistrationFunction::RegistrationFunctionPtr>&
-HookRegistrationFunction::getFunctionVector() {
-    static std::vector<RegistrationFunctionPtr> reg_functions;
-    return (reg_functions);
-}
-
-// Constructor - add a registration function to the function vector
-
-HookRegistrationFunction::HookRegistrationFunction(
-    HookRegistrationFunction::RegistrationFunctionPtr reg_func) {
-    getFunctionVector().push_back(reg_func);
-}
-
-// Execute all registered registration functions
-
-void
-HookRegistrationFunction::execute(ServerHooks& hooks) {
-    std::vector<RegistrationFunctionPtr>& reg_functions = getFunctionVector();
-    for (int i = 0; i < reg_functions.size(); ++i) {
-        (*reg_functions[i])(hooks);
-    }
-}
-
-
-
-} // namespace util
-} // namespace isc
diff --git a/src/lib/util/hooks/server_hooks.h b/src/lib/util/hooks/server_hooks.h
deleted file mode 100644
index ce41fa0..0000000
--- a/src/lib/util/hooks/server_hooks.h
+++ /dev/null
@@ -1,207 +0,0 @@
-// Copyright (C) 2013  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 SERVER_HOOKS_H
-#define SERVER_HOOKS_H
-
-#include <exceptions/exceptions.h>
-
-#include <map>
-#include <string>
-#include <vector>
-
-namespace isc {
-namespace util {
-
-/// @brief Duplicate hook
-///
-/// Thrown if an attempt is made to register a hook with the same name as a
-/// previously-registered hook.
-class DuplicateHook : public Exception {
-public:
-    DuplicateHook(const char* file, size_t line, const char* what) :
-        isc::Exception(file, line, what) {}
-};
-
-/// @brief Invalid hook
-///
-/// Thrown if an attempt is made to get the index for an invalid hook.
-class NoSuchHook : public Exception {
-public:
-    NoSuchHook(const char* file, size_t line, const char* what) :
-        isc::Exception(file, line, what) {}
-};
-
-
-/// @brief Server hook collection
-///
-/// This class is used by the server-side code to register hooks - points in the
-/// server processing at which libraries can register functions (callouts) that
-/// the server will call.  These functions can modify data and so affect the
-/// processing of the server.
-///
-/// The ServerHooks class is little more than a wrapper around the std::map
-/// class.  It stores a hook, assigning to it a unique index number.  This
-/// number is then used by the server code to identify the hook being called.
-/// (Although it would be feasible to use a name as an index, using an integer
-/// will speed up the time taken to locate the callouts, which may make a
-/// difference in a frequently-executed piece of code.)
-
-class ServerHooks {
-public:
-
-    /// Index numbers for pre-defined hooks.
-    static const int CONTEXT_CREATE = 0;
-    static const int CONTEXT_DESTROY = 1;
-
-    /// @brief Constructor
-    ///
-    /// This pre-registers two hooks, context_create and context_destroy, which
-    /// are called by the server before processing a packet and after processing
-    /// for the packet has completed.  They allow the server code to allocate
-    /// and destroy per-packet context.
-    ///
-    /// @throws isc::Unexpected if the registration of the pre-defined hooks
-    ///         fails in some way.
-    ServerHooks();
-
-    /// @brief Register a hook
-    ///
-    /// Registers a hook and returns the hook index.
-    ///
-    /// @param name Name of the hook
-    ///
-    /// @return Index of the hook, to be used in subsequent hook-related calls.
-    ///         This will be greater than or equal to zero (so allowing a
-    ///         negative value to indicate an invalid index).
-    ///
-    /// @throws DuplicateHook A hook with the same name has already been
-    ///         registered.
-    int registerHook(const std::string& name);
-
-    /// @brief Get hook index
-    ///
-    /// Returns the index of a hook.
-    ///
-    /// @param name Name of the hook
-    ///
-    /// @return Index of the hook, to be used in subsequent calls.
-    ///
-    /// @throw NoSuchHook if the hook name is unknown to the caller.
-    int getIndex(const std::string& name) const;
-
-    /// @brief Return number of hooks
-    ///
-    /// Returns the total number of hooks registered.
-    ///
-    /// @return Number of hooks registered.
-    int getCount() const {
-        return (hooks_.size());
-    }
-
-    /// @brief Get hook names
-    ///
-    /// Return list of hooks registered in the object.
-    ///
-    /// @return Vector of strings holding hook names.
-    std::vector<std::string> getHookNames() const;
-
-private:
-    typedef std::map<std::string, int> HookCollection;
-
-    HookCollection  hooks_;     ///< Hook name/index collection
-};
-
-
-/// @brief Hooks Registration
-///
-/// All hooks must be registered before libraries are loaded and callouts
-/// assigned to them.  One way of doing this is to have a global list of hooks:
-/// the addition of any hook anywhere would require updating the list. This
-/// is possible and, if desired, the author of a server can do it.
-///
-/// An alternative is the option provided here, where each component of BIND 10
-/// registers the hooks they are using.  To do this, the component should
-/// create a hook registration function of the form:
-///
-/// @code
-/// static int hook1_num = -1;  // Initialize number for hook 1
-/// static int hook2_num = -1;  // Initialize number for hook 2
-///
-/// void myModuleRegisterHooks(ServerHooks& hooks) {
-///     hook1_num = hooks.registerHook("hook1");
-///     hook2_num = hooks.registerHook("hook2");
-/// }
-/// @endcode
-///
-/// ... which registers the hooks and stores the associated hook index. To
-/// avoid the need to add an explicit call to each of the hook registration
-/// functions to the server initialization code, the component should declare
-/// an object of this class in the same file as the registration function,
-/// but outside of any function.  The declaration should include the name
-/// of the registration function, i.e.
-///
-/// @code
-/// HookRegistrationFunction f(myModuleRegisterHooks);
-/// @code
-///
-/// The constructor of this object will run prior to main() getting called and
-/// will add the registration function to a list of such functions.  The server
-/// then calls the static class method "execute()" to run all the declared
-/// registration functions.
-
-class HookRegistrationFunction {
-public:
-    /// @brief Pointer to a hook registration function
-    typedef void (*RegistrationFunctionPtr)(ServerHooks&);
-
-    /// @brief Constructor
-    ///
-    /// For variables declared outside functions or methods, the constructors
-    /// are run after the program is loaded and before main() is called. This
-    /// constructor adds the passed pointer to a vector of such pointers.
-    HookRegistrationFunction(RegistrationFunctionPtr reg_func);
-
-    /// @brief Access registration function vector
-    ///
-    /// One of the problems with functions run prior to starting main() is the
-    /// "static initialization fiasco".  This occurs because the order in which
-    /// objects outside functions are constructed is not defined.  So if this
-    /// constructor were to depend on a vector declared externally, we would
-    /// not be able to guarantee that the vector had been initialised properly
-    /// before we used it.
-    ///
-    /// To get round this situation, the vector is declared statically within
-    /// a static function.  The first time the function is called, the vector
-    /// is initialized before it is used.
-    ///
-    /// This function returns a reference to the vector used to hold the
-    /// pointers.
-    ///
-    /// @return Reference to the (static) list of registration functions
-    static std::vector<RegistrationFunctionPtr>& getFunctionVector();
-
-    /// @brief Execute registration functions
-    ///
-    /// Called by the server initialization code, this function executes all
-    /// registered hook registration functions.
-    ///
-    /// @param hooks ServerHooks object to which hook information will be added.
-    static void execute(ServerHooks& hooks);
-};
-
-} // namespace util
-} // namespace isc
-
-#endif  // SERVER_HOOKS_H
diff --git a/src/lib/util/tests/Makefile.am b/src/lib/util/tests/Makefile.am
index ceb8b95..ab85fa2 100644
--- a/src/lib/util/tests/Makefile.am
+++ b/src/lib/util/tests/Makefile.am
@@ -25,13 +25,10 @@ run_unittests_SOURCES  = run_unittests.cc
 run_unittests_SOURCES += base32hex_unittest.cc
 run_unittests_SOURCES += base64_unittest.cc
 run_unittests_SOURCES += buffer_unittest.cc
-run_unittests_SOURCES += callout_handle_unittest.cc
-run_unittests_SOURCES += callout_manager_unittest.cc
 run_unittests_SOURCES += fd_share_tests.cc
 run_unittests_SOURCES += fd_tests.cc
 run_unittests_SOURCES += filename_unittest.cc
 run_unittests_SOURCES += hex_unittest.cc
-run_unittests_SOURCES += handles_unittest.cc
 run_unittests_SOURCES += io_utilities_unittest.cc
 run_unittests_SOURCES += lru_list_unittest.cc
 run_unittests_SOURCES += memory_segment_local_unittest.cc
@@ -42,7 +39,6 @@ run_unittests_SOURCES += memory_segment_common_unittest.h
 run_unittests_SOURCES += memory_segment_common_unittest.cc
 run_unittests_SOURCES += qid_gen_unittest.cc
 run_unittests_SOURCES += random_number_generator_unittest.cc
-run_unittests_SOURCES += server_hooks_unittest.cc
 run_unittests_SOURCES += sha1_unittest.cc
 run_unittests_SOURCES += socketsession_unittest.cc
 run_unittests_SOURCES += strutil_unittest.cc
diff --git a/src/lib/util/tests/callout_handle_unittest.cc b/src/lib/util/tests/callout_handle_unittest.cc
deleted file mode 100644
index 76e18d8..0000000
--- a/src/lib/util/tests/callout_handle_unittest.cc
+++ /dev/null
@@ -1,329 +0,0 @@
-// Copyright (C) 2013  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/hooks/callout_handle.h>
-#include <util/hooks/callout_manager.h>
-#include <util/hooks/library_handle.h>
-#include <util/hooks/server_hooks.h>
-
-#include <boost/shared_ptr.hpp>
-
-#include <gtest/gtest.h>
-
-using namespace isc::util;
-using namespace std;
-
-namespace {
-
-/// @file
-/// @brief Holds the CalloutHandle argument tests
-///
-/// Additional testing of the CalloutHandle - together with the interaction
-/// of the LibraryHandle - is done in the handles_unittests set of tests.
-
-class CalloutHandleTest : public ::testing::Test {
-public:
-
-    /// @brief Constructor
-    ///
-    /// Sets up a callout manager to be referenced by the CalloutHandle in
-    /// these tests. (The "4" for the number of libraries in the
-    /// CalloutManager is arbitrary - it is not used in these tests.)
-    CalloutHandleTest()
-        : hooks_(new ServerHooks()), manager_(new CalloutManager(hooks_, 4))
-    {}
-
-    /// Obtain hook manager
-    boost::shared_ptr<CalloutManager>& getCalloutManager() {
-        return (manager_);
-    }
-
-private:
-    /// List of server hooks
-    boost::shared_ptr<ServerHooks> hooks_;
-
-    /// Callout manager accessed by this CalloutHandle.
-    boost::shared_ptr<CalloutManager> manager_;
-};
-
-// *** Argument Tests ***
-//
-// The first set of tests check that the CalloutHandle can store and retrieve
-// arguments.  These are very similar to the LibraryHandle context tests.
-
-// Test that we can store multiple values of the same type and that they
-// are distinct.
-
-TEST_F(CalloutHandleTest, ArgumentDistinctSimpleType) {
-    CalloutHandle handle(getCalloutManager());
-
-    // Store and retrieve an int (random value).
-    int a = 42;
-    handle.setArgument("integer1", a);
-    EXPECT_EQ(42, a);
-
-    int b = 0;
-    handle.getArgument("integer1", b);
-    EXPECT_EQ(42, b);
-
-    // Add another integer (another random value).
-    int c = 142;
-    handle.setArgument("integer2", c);
-    EXPECT_EQ(142, c);
-
-    int d = 0;
-    handle.getArgument("integer2", d);
-    EXPECT_EQ(142, d);
-
-    // Add a short (random value).
-    short e = -81; 
-    handle.setArgument("short", e);
-    EXPECT_EQ(-81, e);
-
-    short f = 0;
-    handle.getArgument("short", f);
-    EXPECT_EQ(-81, f);
-}
-
-// Test that trying to get an unknown argument throws an exception.
-
-TEST_F(CalloutHandleTest, ArgumentUnknownName) {
-    CalloutHandle handle(getCalloutManager());
-
-    // Set an integer
-    int a = 42;
-    handle.setArgument("integer1", a);
-    EXPECT_EQ(42, a);
-
-    // Check we can retrieve it
-    int b = 0;
-    handle.getArgument("integer1", b);
-    EXPECT_EQ(42, b);
-
-    // Check that getting an unknown name throws an exception.
-    int c = 0;
-    EXPECT_THROW(handle.getArgument("unknown", c), NoSuchArgument);
-}
-
-// Test that trying to get an argument with an incorrect type throws an
-// exception.
-
-TEST_F(CalloutHandleTest, ArgumentIncorrectType) {
-    CalloutHandle handle(getCalloutManager());
-
-    // Set an integer
-    int a = 42;
-    handle.setArgument("integer1", a);
-    EXPECT_EQ(42, a);
-
-    // Check we can retrieve it
-    long b = 0;
-    EXPECT_THROW(handle.getArgument("integer1", b), boost::bad_any_cast);
-}
-
-// Now try with some very complex types.  The types cannot be defined within
-// the function and they should contain a copy constructor.  For this reason,
-// a simple "struct" is used.
-
-struct Alpha {
-    int a;
-    int b;
-    Alpha(int first = 0, int second = 0) : a(first), b(second) {}
-};
-
-struct Beta {
-    int c;
-    int d;
-    Beta(int first = 0, int second = 0) : c(first), d(second) {}
-};
-
-TEST_F(CalloutHandleTest, ComplexTypes) {
-    CalloutHandle handle(getCalloutManager());
-
-    // Declare two variables of different (complex) types. (Note as to the
-    // variable names: aleph and beth are the first two letters of the Hebrew
-    // alphabet.)
-    Alpha aleph(1, 2);
-    EXPECT_EQ(1, aleph.a);
-    EXPECT_EQ(2, aleph.b);
-    handle.setArgument("aleph", aleph);
-
-    Beta beth(11, 22);
-    EXPECT_EQ(11, beth.c);
-    EXPECT_EQ(22, beth.d);
-    handle.setArgument("beth", beth);
-
-    // Ensure we can extract the data correctly.
-    Alpha aleph2;
-    EXPECT_EQ(0, aleph2.a);
-    EXPECT_EQ(0, aleph2.b);
-    handle.getArgument("aleph", aleph2);
-    EXPECT_EQ(1, aleph2.a);
-    EXPECT_EQ(2, aleph2.b);
-
-    Beta beth2;
-    EXPECT_EQ(0, beth2.c);
-    EXPECT_EQ(0, beth2.d);
-    handle.getArgument("beth", beth2);
-    EXPECT_EQ(11, beth2.c);
-    EXPECT_EQ(22, beth2.d);
-
-    // Ensure that complex types also thrown an exception if we attempt to
-    // get a context element of the wrong type.
-    EXPECT_THROW(handle.getArgument("aleph", beth), boost::bad_any_cast);
-}
-
-// Check that the context can store pointers. And also check that it respects
-// that a "pointer to X" is not the same as a "pointer to const X".
-
-TEST_F(CalloutHandleTest, PointerTypes) {
-    CalloutHandle handle(getCalloutManager());
-
-    // Declare a couple of variables, const and non-const.
-    Alpha aleph(5, 10);
-    const Beta beth(15, 20);
-
-    Alpha* pa = ℵ
-    const Beta* pcb = ℶ
-
-    // Check pointers can be set and retrieved OK.
-    handle.setArgument("non_const_pointer", pa);
-    handle.setArgument("const_pointer", pcb);
-
-    Alpha* pa2 = 0;
-    handle.getArgument("non_const_pointer", pa2);
-    EXPECT_TRUE(pa == pa2);
-
-    const Beta* pcb2 = 0;
-    handle.getArgument("const_pointer", pcb2);
-    EXPECT_TRUE(pcb == pcb2);
-
-    // Check that the "const" is protected in the context.
-    const Alpha* pca3;
-    EXPECT_THROW(handle.getArgument("non_const_pointer", pca3),
-                 boost::bad_any_cast);
-
-    Beta* pb3;
-    EXPECT_THROW(handle.getArgument("const_pointer", pb3),
-                 boost::bad_any_cast);
-}
-
-// Check that we can get the names of the arguments.
-
-TEST_F(CalloutHandleTest, ContextItemNames) {
-    CalloutHandle handle(getCalloutManager());
-
-    vector<string> expected_names;
-
-    expected_names.push_back("faith");
-    handle.setArgument("faith", 42);
-    expected_names.push_back("hope");
-    handle.setArgument("hope", 43);
-    expected_names.push_back("charity");
-    handle.setArgument("charity", 44);
-
-    // Get the names and check against the expected names.  We'll sort
-    // both arrays to simplify the checking.
-    vector<string> actual_names = handle.getArgumentNames();
-
-    sort(actual_names.begin(), actual_names.end());
-    sort(expected_names.begin(), expected_names.end());
-    EXPECT_TRUE(expected_names == actual_names);
-}
-
-// Test that we can delete an argument.
-
-TEST_F(CalloutHandleTest, DeleteArgument) {
-    CalloutHandle handle(getCalloutManager());
-
-    int one = 1;
-    int two = 2;
-    int three = 3;
-    int four = 4;
-    int value;      // Return value
-
-    handle.setArgument("one", one);
-    handle.setArgument("two", two);
-    handle.setArgument("three", three);
-    handle.setArgument("four", four);
-
-    // Delete "one".
-    handle.getArgument("one", value);
-    EXPECT_EQ(1, value);
-    handle.deleteArgument("one");
-
-    EXPECT_THROW(handle.getArgument("one", value), NoSuchArgument);
-    handle.getArgument("two", value);
-    EXPECT_EQ(2, value);
-    handle.getArgument("three", value);
-    EXPECT_EQ(3, value);
-    handle.getArgument("four", value);
-    EXPECT_EQ(4, value);
-
-    // Delete "three".
-    handle.getArgument("three", value);
-    EXPECT_EQ(3, value);
-    handle.deleteArgument("three");
-
-    EXPECT_THROW(handle.getArgument("one", value), NoSuchArgument);
-    handle.getArgument("two", value);
-    EXPECT_EQ(2, value);
-    EXPECT_THROW(handle.getArgument("three", value), NoSuchArgument);
-    handle.getArgument("four", value);
-    EXPECT_EQ(4, value);
-}
-
-// Test that we can delete all arguments.
-
-TEST_F(CalloutHandleTest, DeleteAllArguments) {
-    CalloutHandle handle(getCalloutManager());
-
-    int one = 1;
-    int two = 2;
-    int three = 3;
-    int four = 4;
-    int value;      // Return value
-
-    // Set the arguments.  The previous test verifies that this works.
-    handle.setArgument("one", one);
-    handle.setArgument("two", two);
-    handle.setArgument("three", three);
-    handle.setArgument("four", four);
-
-    // Delete all arguments...
-    handle.deleteAllArguments();
-
-    // ... and check that none are left.
-    EXPECT_THROW(handle.getArgument("one", value), NoSuchArgument);
-    EXPECT_THROW(handle.getArgument("two", value), NoSuchArgument);
-    EXPECT_THROW(handle.getArgument("three", value), NoSuchArgument);
-    EXPECT_THROW(handle.getArgument("four", value), NoSuchArgument);
-}
-
-// Test the "skip" flag.
-
-TEST_F(CalloutHandleTest, SkipFlag) {
-    CalloutHandle handle(getCalloutManager());
-
-    // Should be false on construction.
-    EXPECT_FALSE(handle.getSkip());
-
-    handle.setSkip(true);
-    EXPECT_TRUE(handle.getSkip());
-
-    handle.setSkip(false);
-    EXPECT_FALSE(handle.getSkip());
-}
-
-} // Anonymous namespace
diff --git a/src/lib/util/tests/callout_manager_unittest.cc b/src/lib/util/tests/callout_manager_unittest.cc
deleted file mode 100644
index ca9b8ad..0000000
--- a/src/lib/util/tests/callout_manager_unittest.cc
+++ /dev/null
@@ -1,765 +0,0 @@
-// Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <exceptions/exceptions.h>
-#include <util/hooks/callout_handle.h>
-#include <util/hooks/callout_manager.h>
-#include <util/hooks/library_handle.h>
-#include <util/hooks/server_hooks.h>
-
-#include <boost/scoped_ptr.hpp>
-#include <gtest/gtest.h>
-
-#include <algorithm>
-#include <string>
-#include <vector>
-
-/// @file
-/// @brief CalloutManager and LibraryHandle tests
-///
-/// These set of tests check the CalloutManager and LibraryHandle.  They are
-/// together in the same file because the LibraryHandle is little more than a
-/// restricted interface to the CalloutManager, and a lot of the support
-/// structure for the tests is common.
-
-using namespace isc;
-using namespace isc::util;
-using namespace std;
-
-namespace {
-
-class CalloutManagerTest : public ::testing::Test {
-public:
-    /// @brief Constructor
-    ///
-    /// Sets up a collection of three LibraryHandle objects to use in the test.
-    CalloutManagerTest() : hooks_(new ServerHooks()) {
-
-        // Set up the server hooks
-        alpha_index_ = hooks_->registerHook("alpha");
-        beta_index_ = hooks_->registerHook("beta");
-        gamma_index_ = hooks_->registerHook("gamma");
-        delta_index_ = hooks_->registerHook("delta");
-
-        // Set up the callout manager with these hooks.  Assume a maximum of
-        // four libraries.
-        callout_manager_.reset(new CalloutManager(hooks_, 10));
-
-        // Set up the callout handle.
-        callout_handle_.reset(new CalloutHandle(callout_manager_));
-
-        // Initialize the static variable.
-        callout_value_ = 0;
-    }
-
-    /// @brief Return the callout handle
-    CalloutHandle& getCalloutHandle() {
-        return (*callout_handle_);
-    }
-
-    /// @brief Return the callout manager
-    boost::shared_ptr<CalloutManager> getCalloutManager() {
-        return (callout_manager_);
-    }
-
-    boost::shared_ptr<ServerHooks> getServerHooks() {
-        return (hooks_);
-    }
-
-    /// Static variable used for accumulating information
-    static int callout_value_;
-
-    /// Hook indexes.  These are somewhat ubiquitous, so are made public for
-    /// ease of reference instead of being accessible by a function.
-    int alpha_index_;
-    int beta_index_;
-    int gamma_index_;
-    int delta_index_;
-
-private:
-    /// Callout handle used in calls
-    boost::shared_ptr<CalloutHandle> callout_handle_;
-
-    /// Callout manager used for the test
-    boost::shared_ptr<CalloutManager> callout_manager_;
-
-    /// Server hooks
-    boost::shared_ptr<ServerHooks> hooks_;
-};
-
-// Definition of the static variable.
-int CalloutManagerTest::callout_value_ = 0;
-
-// Callout definitions
-//
-// The callouts defined here are structured in such a way that it is possible
-// to determine the order in which they are called and whether they are called
-// at all. The method used is simple - after a sequence of callouts, the digits
-// in the value, reading left to right, determines the order of the callouts
-// called.  For example, callout one followed by two followed by three followed
-// by two followed by one results in a value of 12321.
-//
-// Functions return a zero to indicate success.
-
-extern "C" {
-int callout_general(int number) {
-    CalloutManagerTest::callout_value_ =
-        10 * CalloutManagerTest::callout_value_ + number;
-    return (0);
-}
-
-int callout_one(CalloutHandle&) {
-    return (callout_general(1));
-}
-
-int callout_two(CalloutHandle&) {
-    return (callout_general(2));
-}
-
-int callout_three(CalloutHandle&) {
-    return (callout_general(3));
-}
-
-int callout_four(CalloutHandle&) {
-    return (callout_general(4));
-}
-
-int callout_five(CalloutHandle&) {
-    return (callout_general(5));
-}
-
-int callout_six(CalloutHandle&) {
-    return (callout_general(6));
-}
-
-int callout_seven(CalloutHandle&) {
-    return (callout_general(7));
-}
-
-// The next functions are duplicates of some of the above, but return an error.
-
-int callout_one_error(CalloutHandle& handle) {
-    (void) callout_one(handle);
-    return (1);
-}
-
-int callout_two_error(CalloutHandle& handle) {
-    (void) callout_two(handle);
-    return (1);
-}
-
-int callout_three_error(CalloutHandle& handle) {
-    (void) callout_three(handle);
-    return (1);
-}
-
-int callout_four_error(CalloutHandle& handle) {
-    (void) callout_four(handle);
-    return (1);
-}
-
-};  // extern "C"
-
-// *** Callout Tests ***
-//
-// The next set of tests check that callouts can be called.
-
-// Constructor - check that we trap bad parameters.
-
-TEST_F(CalloutManagerTest, BadConstructorParameters) {
-    boost::scoped_ptr<CalloutManager> cm;
-
-    // Invalid number of libraries
-    EXPECT_THROW(cm.reset(new CalloutManager(getServerHooks(), 0)), BadValue);
-    EXPECT_THROW(cm.reset(new CalloutManager(getServerHooks(), -1)), BadValue);
-
-    // Invalid server hooks pointer.
-    boost::shared_ptr<ServerHooks> sh;
-    EXPECT_THROW(cm.reset(new CalloutManager(sh, 4)), BadValue);
-}
-
-// Check the number of libraries is reported successfully.
-
-TEST_F(CalloutManagerTest, GetNumLibraries) {
-    boost::scoped_ptr<CalloutManager> cm;
-
-    // Check two valid values of number of libraries to ensure that the
-    // GetNumLibraries() returns the value set.
-    EXPECT_NO_THROW(cm.reset(new CalloutManager(getServerHooks(), 4)));
-    EXPECT_EQ(4, cm->getNumLibraries());
-
-    EXPECT_NO_THROW(cm.reset(new CalloutManager(getServerHooks(), 42)));
-    EXPECT_EQ(42, cm->getNumLibraries());
-}
-
-// Check that we can only set the current library index to the correct values.
-
-TEST_F(CalloutManagerTest, CheckLibraryIndex) {
-    // Check valid indexes
-    for (int i = 0; i < 4; ++i) {
-        EXPECT_NO_THROW(getCalloutManager()->setLibraryIndex(i));
-    }
-
-    // Check invalid ones
-    EXPECT_THROW(getCalloutManager()->setLibraryIndex(-1), NoSuchLibrary);
-    EXPECT_THROW(getCalloutManager()->setLibraryIndex(15), NoSuchLibrary);
-}
-
-// Check that we can only register callouts on valid hook names.
-
-TEST_F(CalloutManagerTest, ValidHookNames) {
-    getCalloutManager()->setLibraryIndex(0);
-    EXPECT_NO_THROW(getCalloutManager()->registerCallout("alpha", callout_one));
-    EXPECT_THROW(getCalloutManager()->registerCallout("unknown", callout_one),
-                                                      NoSuchHook);
-}
-
-
-// Check we can register callouts appropriately.
-
-TEST_F(CalloutManagerTest, RegisterCallout) {
-    // Ensure that no callouts are attached to any of the hooks.
-    EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
-    EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
-
-    // Set up so that hooks "alpha" and "beta" have callouts attached from a
-    // different libraries.
-    getCalloutManager()->setLibraryIndex(0);
-    getCalloutManager()->registerCallout("alpha", callout_one);
-    getCalloutManager()->setLibraryIndex(1);
-    getCalloutManager()->registerCallout("beta", callout_two);
-
-    // Check all is as expected.
-    EXPECT_TRUE(getCalloutManager()->calloutsPresent(alpha_index_));
-    EXPECT_TRUE(getCalloutManager()->calloutsPresent(beta_index_));
-    EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
-    EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
-                 
-    // Check that calling the callouts returns as expected. (This is also a
-    // test of the callCallouts method.)
-    callout_value_ = 0;
-    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
-    EXPECT_EQ(1, callout_value_);
-
-    callout_value_ = 0;
-    getCalloutManager()->callCallouts(beta_index_, getCalloutHandle());
-    EXPECT_EQ(2, callout_value_);
-
-    // Register some more callouts from different libraries on hook "alpha".
-    getCalloutManager()->setLibraryIndex(2);
-    getCalloutManager()->registerCallout("alpha", callout_three);
-    getCalloutManager()->registerCallout("alpha", callout_four);
-    getCalloutManager()->setLibraryIndex(3);
-    getCalloutManager()->registerCallout("alpha", callout_five);
-
-    // Check it is as expected.
-    callout_value_ = 0;
-    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
-    EXPECT_EQ(1345, callout_value_);
-
-    // ... and check the additional callouts were not registered on the "beta"
-    // hook.
-    callout_value_ = 0;
-    getCalloutManager()->callCallouts(beta_index_, getCalloutHandle());
-    EXPECT_EQ(2, callout_value_);
-
-    // Add another callout to hook "alpha" from library index 2 - this should
-    // appear at the end of the callout list for that library.
-    getCalloutManager()->setLibraryIndex(2);
-    getCalloutManager()->registerCallout("alpha", callout_six);
-    callout_value_ = 0;
-    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
-    EXPECT_EQ(13465, callout_value_);
-
-    // Add a callout from library index 1 - this should appear between the
-    // callouts from library index 0 and linrary index 2.
-    getCalloutManager()->setLibraryIndex(1);
-    getCalloutManager()->registerCallout("alpha", callout_seven);
-    callout_value_ = 0;
-    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
-    EXPECT_EQ(173465, callout_value_);
-}
-
-// Check the "calloutsPresent()" method.
-
-TEST_F(CalloutManagerTest, CalloutsPresent) {
-    // Ensure that no callouts are attached to any of the hooks.
-    EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
-    EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
-    EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
-    EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
-                 
-    // Set up so that hooks "alpha", "beta" and "delta" have callouts attached
-    // to them, and callout  "gamma" does not. (In the statements below, the
-    // exact callouts attached to a hook are not relevant - only the fact
-    // that some callouts are).  Chose the libraries for which the callouts
-    // are registered randomly.
-    getCalloutManager()->setLibraryIndex(0);
-    getCalloutManager()->registerCallout("alpha", callout_one);
-
-    getCalloutManager()->setLibraryIndex(1);
-    getCalloutManager()->registerCallout("alpha", callout_two);
-    getCalloutManager()->registerCallout("beta", callout_two);
-
-    getCalloutManager()->setLibraryIndex(3);
-    getCalloutManager()->registerCallout("alpha", callout_three);
-    getCalloutManager()->registerCallout("delta", callout_four);
-
-    // Check all is as expected.
-    EXPECT_TRUE(getCalloutManager()->calloutsPresent(alpha_index_));
-    EXPECT_TRUE(getCalloutManager()->calloutsPresent(beta_index_));
-    EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
-    EXPECT_TRUE(getCalloutManager()->calloutsPresent(delta_index_));
-
-    // Check we fail on an invalid hook index.
-    EXPECT_THROW(getCalloutManager()->calloutsPresent(42), NoSuchHook);
-    EXPECT_THROW(getCalloutManager()->calloutsPresent(-1), NoSuchHook);
-}
-
-// Test that calling a hook with no callouts on it returns success.
-
-TEST_F(CalloutManagerTest, CallNoCallouts) {
-    // Ensure that no callouts are attached to any of the hooks.
-    EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
-    EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
-    EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
-    EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
-                 
-    // Call the callouts on an arbitrary hook and ensure that nothing happens.
-    callout_value_ = 475;
-    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
-    EXPECT_EQ(475, callout_value_); // Unchanged
-}
-
-// Test that the callouts are called in the correct order (i.e. the callouts
-// from the first library in the order they were registered, then the callouts
-// from the second library in the order they were registered etc.)
-
-TEST_F(CalloutManagerTest, CallCalloutsSuccess) {
-    // Ensure that no callouts are attached to any of the hooks.
-    EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
-    EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
-    EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
-    EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
-                 
-    // Each library contributes one callout on hook "alpha".
-    callout_value_ = 0;
-    getCalloutManager()->setLibraryIndex(1);
-    getCalloutManager()->registerCallout("alpha", callout_one);
-    getCalloutManager()->setLibraryIndex(1);
-    getCalloutManager()->registerCallout("alpha", callout_two);
-    getCalloutManager()->setLibraryIndex(2);
-    getCalloutManager()->registerCallout("alpha", callout_three);
-    getCalloutManager()->setLibraryIndex(3);
-    getCalloutManager()->registerCallout("alpha", callout_four);
-    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
-    EXPECT_EQ(1234, callout_value_);
-
-    // Do a random selection of callouts on hook "beta".
-    callout_value_ = 0;
-    getCalloutManager()->setLibraryIndex(0);
-    getCalloutManager()->registerCallout("beta", callout_one);
-    getCalloutManager()->registerCallout("beta", callout_three);
-    getCalloutManager()->setLibraryIndex(1);
-    getCalloutManager()->registerCallout("beta", callout_two);
-    getCalloutManager()->setLibraryIndex(3);
-    getCalloutManager()->registerCallout("beta", callout_four);
-    getCalloutManager()->callCallouts(beta_index_, getCalloutHandle());
-    EXPECT_EQ(1324, callout_value_);
-
-    // Ensure that calling the callouts on a hook with no callouts works.
-    callout_value_ = 0;
-    getCalloutManager()->callCallouts(gamma_index_, getCalloutHandle());
-    EXPECT_EQ(0, callout_value_);
-}
-
-// Test that the callouts are called in order, but that callouts occurring
-// after a callout that returns an error are not called.
-//
-// (Note: in this test, the callouts that return an error set the value of
-// callout_value_ before they return the error code.)
-
-TEST_F(CalloutManagerTest, CallCalloutsError) {
-    // Ensure that no callouts are attached to any of the hooks.
-    EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
-    EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
-    EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
-    EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
-                 
-    // Each library contributing one callout on hook "alpha". The first callout
-    // returns an error (after adding its value to the result).
-    callout_value_ = 0;
-    getCalloutManager()->setLibraryIndex(0);
-    getCalloutManager()->registerCallout("alpha", callout_one_error);
-    getCalloutManager()->setLibraryIndex(1);
-    getCalloutManager()->registerCallout("alpha", callout_two);
-    getCalloutManager()->setLibraryIndex(2);
-    getCalloutManager()->registerCallout("alpha", callout_three);
-    getCalloutManager()->setLibraryIndex(3);
-    getCalloutManager()->registerCallout("alpha", callout_four);
-    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
-    EXPECT_EQ(1234, callout_value_);
-
-    // Each library contributing multiple callouts on hook "beta". The last
-    // callout on the first library returns an error.
-    callout_value_ = 0;
-    getCalloutManager()->setLibraryIndex(0);
-    getCalloutManager()->registerCallout("beta", callout_one);
-    getCalloutManager()->registerCallout("beta", callout_one_error);
-    getCalloutManager()->setLibraryIndex(1);
-    getCalloutManager()->registerCallout("beta", callout_two);
-    getCalloutManager()->registerCallout("beta", callout_two);
-    getCalloutManager()->registerCallout("beta", callout_three);
-    getCalloutManager()->registerCallout("beta", callout_three);
-    getCalloutManager()->setLibraryIndex(3);
-    getCalloutManager()->registerCallout("beta", callout_four);
-    getCalloutManager()->registerCallout("beta", callout_four);
-    getCalloutManager()->callCallouts(beta_index_, getCalloutHandle());
-    EXPECT_EQ(11223344, callout_value_);
-
-    // A callout in a random position in the callout list returns an error.
-    callout_value_ = 0;
-    getCalloutManager()->setLibraryIndex(0);
-    getCalloutManager()->registerCallout("gamma", callout_one);
-    getCalloutManager()->registerCallout("gamma", callout_one);
-    getCalloutManager()->setLibraryIndex(1);
-    getCalloutManager()->registerCallout("gamma", callout_two);
-    getCalloutManager()->registerCallout("gamma", callout_two);
-    getCalloutManager()->setLibraryIndex(3);
-    getCalloutManager()->registerCallout("gamma", callout_four_error);
-    getCalloutManager()->registerCallout("gamma", callout_four);
-    getCalloutManager()->callCallouts(gamma_index_, getCalloutHandle());
-    EXPECT_EQ(112244, callout_value_);
-
-    // The last callout on a hook returns an error.
-    callout_value_ = 0;
-    getCalloutManager()->setLibraryIndex(0);
-    getCalloutManager()->registerCallout("delta", callout_one);
-    getCalloutManager()->registerCallout("delta", callout_one);
-    getCalloutManager()->setLibraryIndex(1);
-    getCalloutManager()->registerCallout("delta", callout_two);
-    getCalloutManager()->registerCallout("delta", callout_two);
-    getCalloutManager()->setLibraryIndex(2);
-    getCalloutManager()->registerCallout("delta", callout_three);
-    getCalloutManager()->registerCallout("delta", callout_three);
-    getCalloutManager()->setLibraryIndex(3);
-    getCalloutManager()->registerCallout("delta", callout_four);
-    getCalloutManager()->registerCallout("delta", callout_four_error);
-    getCalloutManager()->callCallouts(delta_index_, getCalloutHandle());
-    EXPECT_EQ(11223344, callout_value_);
-}
-
-// Now test that we can deregister a single callout on a hook.
-
-TEST_F(CalloutManagerTest, DeregisterSingleCallout) {
-    // Ensure that no callouts are attached to any of the hooks.
-    EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
-    EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
-    EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
-    EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
-                 
-    // Add a callout to hook "alpha" and check it is added correctly.
-    callout_value_ = 0;
-    getCalloutManager()->setLibraryIndex(0);
-    getCalloutManager()->registerCallout("alpha", callout_two);
-    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
-    EXPECT_EQ(2, callout_value_);
-
-    // Remove it and check that the no callouts are present.  We have to reset
-    // the current library index here as it was invalidated by the call
-    // to callCallouts().
-    getCalloutManager()->setLibraryIndex(0);
-    EXPECT_TRUE(getCalloutManager()->calloutsPresent(alpha_index_));
-    EXPECT_TRUE(getCalloutManager()->deregisterCallout("alpha", callout_two));
-    EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
-}
-
-// Now test that we can deregister a single callout on a hook that has multiple
-// callouts from the same library.
-
-TEST_F(CalloutManagerTest, DeregisterSingleCalloutSameLibrary) {
-    // Ensure that no callouts are attached to any of the hooks.
-    EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
-    EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
-    EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
-    EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
-                 
-    // Add multiple callouts to hook "alpha".
-    callout_value_ = 0;
-    getCalloutManager()->setLibraryIndex(0);
-    getCalloutManager()->registerCallout("alpha", callout_one);
-    getCalloutManager()->registerCallout("alpha", callout_two);
-    getCalloutManager()->registerCallout("alpha", callout_three);
-    getCalloutManager()->registerCallout("alpha", callout_four);
-    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
-    EXPECT_EQ(1234, callout_value_);
-
-    // Remove the callout_two callout.  We have to reset the current library
-    // index here as it was invalidated by the call to callCallouts().
-    getCalloutManager()->setLibraryIndex(0);
-    EXPECT_TRUE(getCalloutManager()->deregisterCallout("alpha", callout_two));
-    callout_value_ = 0;
-    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
-    EXPECT_EQ(134, callout_value_);
-
-    // Try removing it again.
-    getCalloutManager()->setLibraryIndex(0);
-    EXPECT_FALSE(getCalloutManager()->deregisterCallout("alpha", callout_two));
-    callout_value_ = 0;
-    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
-    EXPECT_EQ(134, callout_value_);
-
-}
-
-// Check we can deregister multiple callouts from the same library.
-
-TEST_F(CalloutManagerTest, DeregisterMultipleCalloutsSameLibrary) {
-    // Ensure that no callouts are attached to any of the hooks.
-    EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
-    EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
-    EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
-    EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
-                 
-    // Each library contributes one callout on hook "alpha".
-    callout_value_ = 0;
-    getCalloutManager()->setLibraryIndex(0);
-    getCalloutManager()->registerCallout("alpha", callout_one);
-    getCalloutManager()->registerCallout("alpha", callout_two);
-    getCalloutManager()->registerCallout("alpha", callout_one);
-    getCalloutManager()->registerCallout("alpha", callout_two);
-    getCalloutManager()->registerCallout("alpha", callout_three);
-    getCalloutManager()->registerCallout("alpha", callout_four);
-    getCalloutManager()->registerCallout("alpha", callout_three);
-    getCalloutManager()->registerCallout("alpha", callout_four);
-    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
-    EXPECT_EQ(12123434, callout_value_);
-
-    // Remove the callout_two callouts.  We have to reset the current library
-    // index here as it was invalidated by the call to callCallouts().
-    getCalloutManager()->setLibraryIndex(0);
-    EXPECT_TRUE(getCalloutManager()->deregisterCallout("alpha", callout_two));
-    callout_value_ = 0;
-    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
-    EXPECT_EQ(113434, callout_value_);
-
-    // Try removing multiple callouts that includes one at the end of the
-    // list of callouts.
-    getCalloutManager()->setLibraryIndex(0);
-    EXPECT_TRUE(getCalloutManager()->deregisterCallout("alpha", callout_four));
-    callout_value_ = 0;
-    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
-    EXPECT_EQ(1133, callout_value_);
-
-    // ... and from the start.
-    getCalloutManager()->setLibraryIndex(0);
-    EXPECT_TRUE(getCalloutManager()->deregisterCallout("alpha", callout_one));
-    callout_value_ = 0;
-    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
-    EXPECT_EQ(33, callout_value_);
-
-    // ... and the remaining callouts.
-    getCalloutManager()->setLibraryIndex(0);
-    EXPECT_TRUE(getCalloutManager()->deregisterCallout("alpha", callout_three));
-    callout_value_ = 0;
-    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
-    EXPECT_EQ(0, callout_value_);
-
-    EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
-}
-
-// Check we can deregister multiple callouts from multiple libraries.
-
-TEST_F(CalloutManagerTest, DeregisterMultipleCalloutsMultipleLibraries) {
-    // Ensure that no callouts are attached to any of the hooks.
-    EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
-    EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
-    EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
-    EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
-                 
-    // Each library contributes two callouts to hook "alpha".
-    callout_value_ = 0;
-    getCalloutManager()->setLibraryIndex(0);
-    getCalloutManager()->registerCallout("alpha", callout_one);
-    getCalloutManager()->registerCallout("alpha", callout_two);
-    getCalloutManager()->setLibraryIndex(1);
-    getCalloutManager()->registerCallout("alpha", callout_three);
-    getCalloutManager()->registerCallout("alpha", callout_four);
-    getCalloutManager()->setLibraryIndex(2);
-    getCalloutManager()->registerCallout("alpha", callout_five);
-    getCalloutManager()->registerCallout("alpha", callout_two);
-    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
-    EXPECT_EQ(123452, callout_value_);
-
-    // Remove the callout_two callout from library 0.  It should not affect
-    // the second callout_two callout registered by library 2.
-    getCalloutManager()->setLibraryIndex(0);
-    EXPECT_TRUE(getCalloutManager()->deregisterCallout("alpha", callout_two));
-    callout_value_ = 0;
-    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
-    EXPECT_EQ(13452, callout_value_);
-}
-
-// Check we can deregister all callouts from a single library.
-
-TEST_F(CalloutManagerTest, DeregisterAllCallouts) {
-    // Ensure that no callouts are attached to hook one.
-    EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
-                 
-    // Each library contributes two callouts to hook "alpha".
-    callout_value_ = 0;
-    getCalloutManager()->setLibraryIndex(0);
-    getCalloutManager()->registerCallout("alpha", callout_one);
-    getCalloutManager()->registerCallout("alpha", callout_two);
-    getCalloutManager()->setLibraryIndex(1);
-    getCalloutManager()->registerCallout("alpha", callout_three);
-    getCalloutManager()->registerCallout("alpha", callout_four);
-    getCalloutManager()->setLibraryIndex(2);
-    getCalloutManager()->registerCallout("alpha", callout_five);
-    getCalloutManager()->registerCallout("alpha", callout_six);
-    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
-    EXPECT_EQ(123456, callout_value_);
-
-    // Remove all callouts from library index 1.
-    getCalloutManager()->setLibraryIndex(1);
-    EXPECT_TRUE(getCalloutManager()->deregisterAllCallouts("alpha"));
-    callout_value_ = 0;
-    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
-    EXPECT_EQ(1256, callout_value_);
-
-    // Remove all callouts from library index 2.
-    getCalloutManager()->setLibraryIndex(2);
-    EXPECT_TRUE(getCalloutManager()->deregisterAllCallouts("alpha"));
-    callout_value_ = 0;
-    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
-    EXPECT_EQ(12, callout_value_);
-}
-
-// Check that we can register/deregister callouts on different libraries
-// and different hooks, and that the callout instances are regarded as
-// unique and do not affect one another.
-
-TEST_F(CalloutManagerTest, MultipleCalloutsLibrariesHooks) {
-    // Ensure that no callouts are attached to any of the hooks.
-    EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
-    EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
-    EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
-    EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
-                 
-    // Register callouts on the alpha hook.
-    callout_value_ = 0;
-    getCalloutManager()->setLibraryIndex(0);
-    getCalloutManager()->registerCallout("alpha", callout_one);
-    getCalloutManager()->registerCallout("alpha", callout_two);
-    getCalloutManager()->setLibraryIndex(1);
-    getCalloutManager()->registerCallout("alpha", callout_three);
-    getCalloutManager()->registerCallout("alpha", callout_four);
-    getCalloutManager()->setLibraryIndex(2);
-    getCalloutManager()->registerCallout("alpha", callout_five);
-    getCalloutManager()->registerCallout("alpha", callout_two);
-    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
-    EXPECT_EQ(123452, callout_value_);
-
-    // Register the same callouts on the beta hook, and check that those
-    // on the alpha hook are not affected.
-    callout_value_ = 0;
-    getCalloutManager()->setLibraryIndex(0);
-    getCalloutManager()->registerCallout("beta", callout_five);
-    getCalloutManager()->registerCallout("beta", callout_one);
-    getCalloutManager()->setLibraryIndex(2);
-    getCalloutManager()->registerCallout("beta", callout_four);
-    getCalloutManager()->registerCallout("beta", callout_three);
-    getCalloutManager()->callCallouts(beta_index_, getCalloutHandle());
-    EXPECT_EQ(5143, callout_value_);
-
-    // Check that the order of callouts on the alpha hook has not been affected.
-    callout_value_ = 0;
-    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
-    EXPECT_EQ(123452, callout_value_);
-
-    // Remove callout four from beta and check that alpha is not affected.
-    getCalloutManager()->setLibraryIndex(2);
-    EXPECT_TRUE(getCalloutManager()->deregisterCallout("beta", callout_four));
-
-    callout_value_ = 0;
-    getCalloutManager()->callCallouts(beta_index_, getCalloutHandle());
-    EXPECT_EQ(513, callout_value_);
-
-    callout_value_ = 0;
-    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
-    EXPECT_EQ(123452, callout_value_);
-}
-
-// Library handle tests.  As by inspection the LibraryHandle can be seen to be
-// little more than shell around CalloutManager, only a basic set of tests
-// is done concerning registration and deregistration of functions.
-//
-// More extensive tests (i.e. checking that when a callout is called it can
-// only register and deregister callouts within its library) require that
-// the CalloutHandle object pass the appropriate LibraryHandle to the
-// callout.  These tests are done in the handles_unittest tests.
-
-TEST_F(CalloutManagerTest, LibraryHandleRegistration) {
-    // Ensure that no callouts are attached to any of the hooks.
-    EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
-
-    // Set up so that hooks "alpha" and "beta" have callouts attached from a
-    // different libraries.
-    getCalloutManager()->setLibraryIndex(0);
-    getCalloutManager()->getLibraryHandle().registerCallout("alpha",
-                                                            callout_one);
-    getCalloutManager()->getLibraryHandle().registerCallout("alpha",
-                                                            callout_two);
-    getCalloutManager()->setLibraryIndex(1);
-    getCalloutManager()->getLibraryHandle().registerCallout("alpha",
-                                                            callout_three);
-    getCalloutManager()->getLibraryHandle().registerCallout("alpha",
-                                                            callout_four);
-
-    // Check all is as expected.
-    EXPECT_TRUE(getCalloutManager()->calloutsPresent(alpha_index_));
-    EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
-    EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
-    EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
-                 
-    // Check that calling the callouts returns as expected. (This is also a
-    // test of the callCallouts method.)
-    callout_value_ = 0;
-    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
-    EXPECT_EQ(1234, callout_value_);
-
-    // Deregister a callout on library index 0 (after we check we can't
-    // deregister it through library index 1).
-    getCalloutManager()->setLibraryIndex(1);
-    EXPECT_FALSE(getCalloutManager()->deregisterCallout("alpha", callout_two));
-    callout_value_ = 0;
-    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
-    EXPECT_EQ(1234, callout_value_);
-
-    getCalloutManager()->setLibraryIndex(0);
-    EXPECT_TRUE(getCalloutManager()->deregisterCallout("alpha", callout_two));
-    callout_value_ = 0;
-    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
-    EXPECT_EQ(134, callout_value_);
-
-    // Deregister all callouts on library index 1.
-    getCalloutManager()->setLibraryIndex(1);
-    EXPECT_TRUE(getCalloutManager()->deregisterAllCallouts("alpha"));
-    callout_value_ = 0;
-    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
-    EXPECT_EQ(1, callout_value_);
-}
-
-
-
-} // Anonymous namespace
diff --git a/src/lib/util/tests/handles_unittest.cc b/src/lib/util/tests/handles_unittest.cc
deleted file mode 100644
index 5e5b237..0000000
--- a/src/lib/util/tests/handles_unittest.cc
+++ /dev/null
@@ -1,917 +0,0 @@
-// Copyright (C) 2013  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/hooks/callout_handle.h>
-#include <util/hooks/callout_manager.h>
-#include <util/hooks/library_handle.h>
-#include <util/hooks/server_hooks.h>
-
-#include <boost/lexical_cast.hpp>
-#include <boost/scoped_ptr.hpp>
-#include <gtest/gtest.h>
-
-#include <algorithm>
-#include <string>
-
-/// @file
-/// CalloutHandle/LibraryHandle interaction tests
-///
-/// This file holds unit tests checking the interaction between the
-/// CalloutHandle/LibraryHandle and CalloutManager classes.  In particular,
-/// they check that:
-///
-/// - A CalloutHandle's context is shared between callouts from the same
-///   library, but there is a separate context for each library.
-///
-/// - The various methods manipulating the items in the CalloutHandle's context
-///   work correctly.
-///
-/// - An active callout can only modify the registration of callouts registered
-///   by its own library.
-
-using namespace isc::util;
-using namespace std;
-
-namespace {
-
-class HandlesTest : public ::testing::Test {
-public:
-    /// @brief Constructor
-    ///
-    /// Sets up the various elements used in each test.
-    HandlesTest() : hooks_(new ServerHooks()), manager_()
-    {
-        // Set up four hooks, although through gamma
-        alpha_index_ = hooks_->registerHook("alpha");
-        beta_index_ = hooks_->registerHook("beta");
-        gamma_index_ = hooks_->registerHook("gamma");
-        delta_index_ = hooks_->registerHook("delta");
-
-        // Set up for three libraries.
-        manager_.reset(new CalloutManager(hooks_, 3));
-
-        // Initialize remaining variables.
-        common_string_ = "";
-    }
-
-    /// @brief Return callout manager
-    boost::shared_ptr<CalloutManager> getCalloutManager() {
-        return (manager_);
-    }
-
-    /// Hook indexes - these are frequently accessed, so are accessed directly.
-    int alpha_index_;
-    int beta_index_;
-    int gamma_index_;
-    int delta_index_;
-
-    /// String accessible by all callouts whatever the library
-    static std::string common_string_;
-
-private:
-    /// Server hooks 
-    boost::shared_ptr<ServerHooks> hooks_;
-
-    /// Callout manager.  Declared static so that the callout functions can
-    /// access it.
-    boost::shared_ptr<CalloutManager> manager_;
-};
-
-/// Define the common string
-std::string HandlesTest::common_string_;
-
-
-// The next set of functions define the callouts used by the tests.  They
-// manipulate the data in such a way that callouts called - and the order in
-// which they were called - can be determined.  The functions also check that
-// the "callout context" data areas are separate.
-//
-// Three libraries are assumed, and each supplies four callouts.  All callouts
-// manipulate two context elements the CalloutHandle, the elements being called
-// "string" and "int" (which describe the type of data manipulated).
-//
-// For the string item, each callout shifts data to the left and inserts its own
-// data.  The data is a string of the form "nmc", where "n" is the number of
-// the library, "m" is the callout number and "y" is the indication of what
-// callout handle was passed as an argument ("1" or "2": "0" is used when no
-// identification has been set in the callout handle).
-//
-// For simplicity, and to cut down the number of functions actually written,
-// the callout indicator ("1" or "2") ) used in the in the CalloutHandle
-// functions is passed via a CalloutArgument.  The argument is named "string":
-// use of a name the same as that of one of the context elements serves as a
-// check that the argument name space and argument context space are separate.
-//
-// For integer data, the value starts at zero and an increment is added on each
-// call.  This increment is equal to:
-//
-// 100 * library number + 10 * callout number + callout handle
-//
-// Although this gives less information than the string value, the reasons for
-// using it are:
-//
-// - It is a separate item in the context, so checks that the context can
-//   handle multiple items.
-// - It provides an item that can be deleted by the context deletion
-//   methods.
-
-
-// Values set in the CalloutHandle context.  There are three libraries, so
-// there are three contexts for the callout, one for each library.
-
-std::string& resultCalloutString(int index) {
-    static std::string result_callout_string[3];
-    return (result_callout_string[index]);
-}
-
-int& resultCalloutInt(int index) {
-    static int result_callout_int[3];
-    return (result_callout_int[index]);
-}
-
-// A simple function to zero the results.
-
-static void zero_results() {
-    for (int i = 0; i < 3; ++i) {
-        resultCalloutString(i) = "";
-        resultCalloutInt(i) = 0;
-    }
-}
-
-
-// Library callouts.
-
-// Common code for setting the callout context values.
-
-int
-execute(CalloutHandle& callout_handle, int library_num, int callout_num) {
-
-    // Obtain the callout handle number
-    int handle_num = 0;
-    try  {
-        callout_handle.getArgument("handle_num", handle_num);
-    } catch (const NoSuchArgument&) {
-        // handle_num argument not set: this is the case in the tests where
-        // the context_create hook check is tested.
-        handle_num = 0;
-    }
-
-    // Create the basic data to be appended to the context value.
-    int idata = 100 * library_num + 10 * callout_num + handle_num;
-    string sdata = boost::lexical_cast<string>(idata);
-
-    // Get the context data. As before, this will not exist for the first
-    // callout called. (In real life, the library should create it when the
-    // "context_create" hook gets called before any packet processing takes
-    // place.)
-    int int_value = 0;
-    try {
-        callout_handle.getContext("int", int_value);
-    } catch (const NoSuchCalloutContext&) {
-        int_value = 0;
-    }
-
-    string string_value = "";
-    try {
-        callout_handle.getContext("string", string_value);
-    } catch (const NoSuchCalloutContext&) {
-        string_value = "";
-    }
-
-    // Update the values and set them back in the callout context.
-    int_value += idata;
-    callout_handle.setContext("int", int_value);
-
-    string_value += sdata;
-    callout_handle.setContext("string", string_value);
-
-    return (0);
-}
-
-// The following functions are the actual callouts - the name is of the
-// form "callout_<library number>_<callout number>"
-
-int
-callout11(CalloutHandle& callout_handle) {
-    return (execute(callout_handle, 1, 1));
-}
-
-int
-callout12(CalloutHandle& callout_handle) {
-    return (execute(callout_handle, 1, 2));
-}
-
-int
-callout13(CalloutHandle& callout_handle) {
-    return (execute(callout_handle, 1, 3));
-}
-
-int
-callout21(CalloutHandle& callout_handle) {
-    return (execute(callout_handle, 2, 1));
-}
-
-int
-callout22(CalloutHandle& callout_handle) {
-    return (execute(callout_handle, 2, 2));
-}
-
-int
-callout23(CalloutHandle& callout_handle) {
-    return (execute(callout_handle, 2, 3));
-}
-
-int
-callout31(CalloutHandle& callout_handle) {
-    return (execute(callout_handle, 3, 1));
-}
-
-int
-callout32(CalloutHandle& callout_handle) {
-    return (execute(callout_handle, 3, 2));
-}
-
-int
-callout33(CalloutHandle& callout_handle) {
-    return (execute(callout_handle, 3, 3));
-}
-
-// Common callout code for the fourth hook (which makes the data available for
-// checking).  It copies the library and callout context data to the global
-// variables.
-
-int printExecute(CalloutHandle& callout_handle, int library_num) {
-    callout_handle.getContext("string", resultCalloutString(library_num - 1));
-    callout_handle.getContext("int", resultCalloutInt(library_num - 1));
-
-    return (0);
-}
-
-// These are the actual callouts.
-
-int
-print1(CalloutHandle& callout_handle) {
-    return (printExecute(callout_handle, 1));
-}
-
-int
-print2(CalloutHandle& callout_handle) {
-    return (printExecute(callout_handle, 2));
-}
-
-int
-print3(CalloutHandle& callout_handle) {
-    return (printExecute(callout_handle, 3));
-}
-
-// This test checks the many-faced nature of the context for the CalloutContext.
-
-TEST_F(HandlesTest, ContextAccessCheck) {
-    // Register callouts for the different libraries.
-    CalloutHandle handle(getCalloutManager());
-
-    // Library 0.
-    getCalloutManager()->setLibraryIndex(0);
-    getCalloutManager()->registerCallout("alpha", callout11);
-    getCalloutManager()->registerCallout("beta", callout12);
-    getCalloutManager()->registerCallout("gamma", callout13);
-    getCalloutManager()->registerCallout("delta", print1);
-
-    getCalloutManager()->setLibraryIndex(1);
-    getCalloutManager()->registerCallout("alpha", callout21);
-    getCalloutManager()->registerCallout("beta", callout22);
-    getCalloutManager()->registerCallout("gamma", callout23);
-    getCalloutManager()->registerCallout("delta", print2);
-
-    getCalloutManager()->setLibraryIndex(2);
-    getCalloutManager()->registerCallout("alpha", callout31);
-    getCalloutManager()->registerCallout("beta", callout32);
-    getCalloutManager()->registerCallout("gamma", callout33);
-    getCalloutManager()->registerCallout("delta", print3);
-
-    // Create the callout handles and distinguish them by setting the
-    // "handle_num" argument.
-    CalloutHandle callout_handle_1(getCalloutManager());
-    callout_handle_1.setArgument("handle_num", static_cast<int>(1)); 
-
-    CalloutHandle callout_handle_2(getCalloutManager());
-    callout_handle_2.setArgument("handle_num", static_cast<int>(2)); 
-
-    // Now call the callouts attached to the first three hooks.  Each hook is
-    // called twice (once for each callout handle) before the next hook is
-    // called.
-    getCalloutManager()->callCallouts(alpha_index_, callout_handle_1);
-    getCalloutManager()->callCallouts(alpha_index_, callout_handle_2);
-    getCalloutManager()->callCallouts(beta_index_, callout_handle_1);
-    getCalloutManager()->callCallouts(beta_index_, callout_handle_2);
-    getCalloutManager()->callCallouts(gamma_index_, callout_handle_1);
-    getCalloutManager()->callCallouts(gamma_index_, callout_handle_2);
-
-    // Get the results for each callout (the callout on hook "delta" copies
-    // the context values into a location the test can access).  Explicitly
-    // zero the variables before getting the results so we are certain that
-    // the values are the results of the callouts.
-
-    zero_results();
-
-    // To explain the expected callout context results.
-    //
-    // Each callout handle maintains a separate context for each library.  When
-    // the first call to callCallouts() is made, "111" gets appended to
-    // the context for library 1 maintained by the first callout handle, "211"
-    // gets appended to the context maintained for library 2, and "311" to
-    // the context maintained for library 3.  In each case, the first digit
-    // corresponds to the library number, the second to the callout number and
-    // the third to the "handle_num" of the callout handle. For the first call
-    // to callCallouts, handle 1 is used, so the last digit is always 1.
-    //
-    // The next call to callCallouts() calls the same callouts but for the
-    // second callout handle.  It also maintains three contexts (one for
-    // each library) and they will get "112", "212", "312" appended to
-    // them. The explanation for the digits is the same as before, except that
-    // in this case, the callout handle is number 2, so the third digit is
-    // always 2.  These additions don't affect the contexts maintained by
-    // callout handle 1.
-    //
-    // The process is then repeated for hooks "beta" and "gamma" which, for
-    // callout handle 1, append "121", "221" and "321" for hook "beta" and
-    // "311", "321" and "331" for hook "gamma".
-    //
-    // The expected integer values can be found by summing up the values
-    // corresponding to the elements of the strings.
-
-    // At this point, we have only called the "print" function for callout
-    // handle "1", so the following results are checking the context values
-    // maintained in that callout handle.
-
-    getCalloutManager()->callCallouts(delta_index_, callout_handle_1);
-    EXPECT_EQ("111121131", resultCalloutString(0));
-    EXPECT_EQ("211221231", resultCalloutString(1));
-    EXPECT_EQ("311321331", resultCalloutString(2));
-
-    EXPECT_EQ((111 + 121 + 131), resultCalloutInt(0));
-    EXPECT_EQ((211 + 221 + 231), resultCalloutInt(1));
-    EXPECT_EQ((311 + 321 + 331), resultCalloutInt(2));
-
-    // Repeat the checks for callout 2.
-
-    zero_results();
-    getCalloutManager()->callCallouts(delta_index_, callout_handle_2);
-
-    EXPECT_EQ((112 + 122 + 132), resultCalloutInt(0));
-    EXPECT_EQ((212 + 222 + 232), resultCalloutInt(1));
-    EXPECT_EQ((312 + 322 + 332), resultCalloutInt(2));
-
-    EXPECT_EQ("112122132", resultCalloutString(0));
-    EXPECT_EQ("212222232", resultCalloutString(1));
-    EXPECT_EQ("312322332", resultCalloutString(2));
-}
-
-// Now repeat the test, but add a deletion callout to the list.  The "beta"
-// hook of library 2 will have an additional callout to delete the "int"
-// element: the same hook for library 3 will delete both elements.  In
-// addition, the names of context elements for the libraries at this point
-// will be printed.
-
-// List of context item names.
-
-vector<string>&
-getItemNames(int index) {
-    static vector<string> context_items[3];
-    return (context_items[index]);
-}
-
-// Context item deletion functions.
-
-int
-deleteIntContextItem(CalloutHandle& handle) {
-    handle.deleteContext("int");
-    return (0);
-}
-
-int
-deleteAllContextItems(CalloutHandle& handle) {
-    handle.deleteAllContext();
-    return (0);
-}
-
-// Generic print function - copy names in sorted order.
-
-int
-printContextNamesExecute(CalloutHandle& handle, int library_num) {
-    const int index = library_num - 1;
-    getItemNames(index) = handle.getContextNames();
-    sort(getItemNames(index).begin(), getItemNames(index).end());
-    return (0);
-}
-
-int
-printContextNames1(CalloutHandle& handle) {
-    return (printContextNamesExecute(handle, 1));
-}
-
-int
-printContextNames2(CalloutHandle& handle) {
-    return (printContextNamesExecute(handle, 2));
-}
-
-int
-printContextNames3(CalloutHandle& handle) {
-    return (printContextNamesExecute(handle, 3));
-}
-
-// Perform the test including deletion of context items.
-
-TEST_F(HandlesTest, ContextDeletionCheck) {
-
-    getCalloutManager()->setLibraryIndex(0);
-    getCalloutManager()->registerCallout("alpha", callout11);
-    getCalloutManager()->registerCallout("beta", callout12);
-    getCalloutManager()->registerCallout("beta", printContextNames1);
-    getCalloutManager()->registerCallout("gamma", callout13);
-    getCalloutManager()->registerCallout("delta", print1);
-
-    getCalloutManager()->setLibraryIndex(1);
-    getCalloutManager()->registerCallout("alpha", callout21);
-    getCalloutManager()->registerCallout("beta", callout22);
-    getCalloutManager()->registerCallout("beta", deleteIntContextItem);
-    getCalloutManager()->registerCallout("beta", printContextNames2);
-    getCalloutManager()->registerCallout("gamma", callout23);
-    getCalloutManager()->registerCallout("delta", print2);
-
-    getCalloutManager()->setLibraryIndex(2);
-    getCalloutManager()->registerCallout("alpha", callout31);
-    getCalloutManager()->registerCallout("beta", callout32);
-    getCalloutManager()->registerCallout("beta", deleteAllContextItems);
-    getCalloutManager()->registerCallout("beta", printContextNames3);
-    getCalloutManager()->registerCallout("gamma", callout33);
-    getCalloutManager()->registerCallout("delta", print3);
-
-    // Create the callout handles and distinguish them by setting the "long"
-    // argument.
-    CalloutHandle callout_handle_1(getCalloutManager());
-    callout_handle_1.setArgument("handle_num", static_cast<int>(1));
-
-    CalloutHandle callout_handle_2(getCalloutManager());
-    callout_handle_2.setArgument("handle_num", static_cast<int>(2));
-
-    // Now call the callouts attached to the first three hooks.  Each hook is
-    // called twice (once for each callout handle) before the next hook is
-    // called.
-    getCalloutManager()->callCallouts(alpha_index_, callout_handle_1);
-    getCalloutManager()->callCallouts(alpha_index_, callout_handle_2);
-    getCalloutManager()->callCallouts(beta_index_, callout_handle_1);
-    getCalloutManager()->callCallouts(beta_index_, callout_handle_2);
-    getCalloutManager()->callCallouts(gamma_index_, callout_handle_1);
-    getCalloutManager()->callCallouts(gamma_index_, callout_handle_2);
-
-    // Get the results for each callout.  Explicitly zero the variables before
-    // getting the results so we are certain that the values are the results
-    // of the callouts.
-
-    zero_results();
-    getCalloutManager()->callCallouts(delta_index_, callout_handle_1);
-
-    // The logic by which the expected results are arrived at is described
-    // in the ContextAccessCheck test.  The results here are different
-    // because context items have been modified along the way.
-
-    EXPECT_EQ((111 + 121 + 131), resultCalloutInt(0));
-    EXPECT_EQ((            231), resultCalloutInt(1));
-    EXPECT_EQ((            331), resultCalloutInt(2));
-
-    EXPECT_EQ("111121131", resultCalloutString(0));
-    EXPECT_EQ("211221231", resultCalloutString(1));
-    EXPECT_EQ(      "331", resultCalloutString(2));
-
-    // Repeat the checks for callout handle 2.
-
-    zero_results();
-    getCalloutManager()->callCallouts(delta_index_, callout_handle_2);
-
-    EXPECT_EQ((112 + 122 + 132), resultCalloutInt(0));
-    EXPECT_EQ((            232), resultCalloutInt(1));
-    EXPECT_EQ((            332), resultCalloutInt(2));
-
-    EXPECT_EQ("112122132", resultCalloutString(0));
-    EXPECT_EQ("212222232", resultCalloutString(1));
-    EXPECT_EQ(      "332", resultCalloutString(2));
-
-    // ... and check what the names of the context items are after the callouts
-    // for hook "beta".  We know they are in sorted order.
-
-    EXPECT_EQ(2, getItemNames(0).size());
-    EXPECT_EQ(string("int"),    getItemNames(0)[0]);
-    EXPECT_EQ(string("string"), getItemNames(0)[1]);
-
-    EXPECT_EQ(1, getItemNames(1).size());
-    EXPECT_EQ(string("string"), getItemNames(1)[0]);
-
-    EXPECT_EQ(0, getItemNames(2).size());
-}
-
-// Tests that the CalloutHandle's constructor and destructor call the
-// context_create and context_destroy callbacks (if registered).  For
-// simplicity, we'll use the same callout functions as used above.
-
-TEST_F(HandlesTest, ConstructionDestructionCallouts) {
-
-    // Register context callouts.
-    getCalloutManager()->setLibraryIndex(0);
-    getCalloutManager()->registerCallout("context_create", callout11);
-    getCalloutManager()->registerCallout("context_create", print1);
-    getCalloutManager()->registerCallout("context_destroy", callout12);
-    getCalloutManager()->registerCallout("context_destroy", print1);
-
-    // Create the CalloutHandle and check that the constructor callout
-    // has run.
-    zero_results();
-    boost::scoped_ptr<CalloutHandle>
-        callout_handle(new CalloutHandle(getCalloutManager()));
-    EXPECT_EQ("110", resultCalloutString(0));
-    EXPECT_EQ(110, resultCalloutInt(0));
-
-    // Check that the destructor callout runs.  Note that the "print1" callout
-    // didn't destroy the library context - it only copied it to where it
-    // could be examined.  As a result, the destructor callout appends its
-    // elements to the constructor's values and the result is printed.
-    zero_results();
-    callout_handle.reset();
-
-    EXPECT_EQ("110120", resultCalloutString(0));
-    EXPECT_EQ((110 + 120), resultCalloutInt(0));
-}
-
-// Dynamic callout registration and deregistration.
-// The following are the dynamic registration/deregistration callouts.
-
-
-// Add callout_78_alpha - adds a callout to hook alpha that appends "78x"
-// (where "x" is the callout handle) to the current output.
-
-int
-callout78(CalloutHandle& callout_handle) {
-    return (execute(callout_handle, 7, 8));
-}
-
-int
-add_callout78_alpha(CalloutHandle& callout_handle) {
-    callout_handle.getLibraryHandle().registerCallout("alpha", callout78);
-    return (0);
-}
-
-int
-delete_callout78_alpha(CalloutHandle& callout_handle) {
-    static_cast<void>(
-        callout_handle.getLibraryHandle().deregisterCallout("alpha",
-                                                            callout78));
-    return (0);
-}
-
-// Check that a callout can register another callout on a different hook.
-
-TEST_F(HandlesTest, DynamicRegistrationAnotherHook) {
-    // Register callouts for the different libraries.
-    CalloutHandle handle(getCalloutManager());
-
-    // Set up callouts on "alpha".
-    getCalloutManager()->setLibraryIndex(0);
-    getCalloutManager()->registerCallout("alpha", callout11);
-    getCalloutManager()->registerCallout("delta", print1);
-    getCalloutManager()->setLibraryIndex(1);
-    getCalloutManager()->registerCallout("alpha", callout21);
-    getCalloutManager()->registerCallout("delta", print2);
-    getCalloutManager()->setLibraryIndex(2);
-    getCalloutManager()->registerCallout("alpha", callout31);
-    getCalloutManager()->registerCallout("delta", print3);
-
-    // ... and on "beta", set up the function to add a hook to alpha (but only
-    // for library 1).
-    getCalloutManager()->setLibraryIndex(1);
-    getCalloutManager()->registerCallout("beta", add_callout78_alpha);
-
-    // See what we get for calling the callouts on alpha first.
-    CalloutHandle callout_handle_1(getCalloutManager());
-    callout_handle_1.setArgument("handle_num", static_cast<int>(1)); 
-    getCalloutManager()->callCallouts(alpha_index_, callout_handle_1);
-
-    zero_results();
-    getCalloutManager()->callCallouts(delta_index_, callout_handle_1);
-    EXPECT_EQ("111", resultCalloutString(0));
-    EXPECT_EQ("211", resultCalloutString(1));
-    EXPECT_EQ("311", resultCalloutString(2));
-
-    // All as expected, now call the callouts on beta.  This should add a
-    // callout to the list of callouts for alpha, which we should see when
-    // we run the test again.
-    getCalloutManager()->callCallouts(beta_index_, callout_handle_1);
-
-    // Use a new callout handle so as to get fresh callout context.
-    CalloutHandle callout_handle_2(getCalloutManager());
-    callout_handle_2.setArgument("handle_num", static_cast<int>(2)); 
-    getCalloutManager()->callCallouts(alpha_index_, callout_handle_2);
-
-    zero_results();
-    getCalloutManager()->callCallouts(delta_index_, callout_handle_2);
-    EXPECT_EQ("112", resultCalloutString(0));
-    EXPECT_EQ("212782", resultCalloutString(1));
-    EXPECT_EQ("312", resultCalloutString(2));
-}
-
-// Check that a callout can register another callout on the same hook.
-// Note that the registration only applies to a subsequent invocation of
-// callCallouts, not to the current one. In other words, if
-//
-// * the callout list for a library is "A then B then C"
-// * when callCallouts is executed "B" adds "D" to that list,
-//
-// ... the current execution of callCallouts only executes A, B and C.  A
-// subsequent invocation will execute A, B, C then D.
-
-TEST_F(HandlesTest, DynamicRegistrationSameHook) {
-    // Register callouts for the different libraries.
-    CalloutHandle handle(getCalloutManager());
-
-    // Set up callouts on "alpha".
-    getCalloutManager()->setLibraryIndex(0);
-    getCalloutManager()->registerCallout("alpha", callout11);
-    getCalloutManager()->registerCallout("alpha", add_callout78_alpha);
-    getCalloutManager()->registerCallout("delta", print1);
-
-    // See what we get for calling the callouts on alpha first.
-    CalloutHandle callout_handle_1(getCalloutManager());
-    callout_handle_1.setArgument("handle_num", static_cast<int>(1)); 
-    getCalloutManager()->callCallouts(alpha_index_, callout_handle_1);
-    zero_results();
-    getCalloutManager()->callCallouts(delta_index_, callout_handle_1);
-    EXPECT_EQ("111", resultCalloutString(0));
-
-    // Run it again - we should have added something to this hook.
-    CalloutHandle callout_handle_2(getCalloutManager());
-    callout_handle_2.setArgument("handle_num", static_cast<int>(2)); 
-    getCalloutManager()->callCallouts(alpha_index_, callout_handle_2);
-    zero_results();
-    getCalloutManager()->callCallouts(delta_index_, callout_handle_2);
-    EXPECT_EQ("112782", resultCalloutString(0));
-
-    // And a third time...
-    CalloutHandle callout_handle_3(getCalloutManager());
-    callout_handle_3.setArgument("handle_num", static_cast<int>(3)); 
-    getCalloutManager()->callCallouts(alpha_index_, callout_handle_3);
-    zero_results();
-    getCalloutManager()->callCallouts(delta_index_, callout_handle_3);
-    EXPECT_EQ("113783783", resultCalloutString(0));
-}
-
-// Deregistration of a callout from a different hook
-
-TEST_F(HandlesTest, DynamicDeregistrationDifferentHook) {
-    // Register callouts for the different libraries.
-    CalloutHandle handle(getCalloutManager());
-
-    // Set up callouts on "alpha".
-    getCalloutManager()->setLibraryIndex(0);
-    getCalloutManager()->registerCallout("alpha", callout11);
-    getCalloutManager()->registerCallout("alpha", callout78);
-    getCalloutManager()->registerCallout("alpha", callout11);
-    getCalloutManager()->registerCallout("delta", print1);
-
-    getCalloutManager()->registerCallout("beta", delete_callout78_alpha);
-
-    // Call the callouts on alpha
-    CalloutHandle callout_handle_1(getCalloutManager());
-    callout_handle_1.setArgument("handle_num", static_cast<int>(1)); 
-    getCalloutManager()->callCallouts(alpha_index_, callout_handle_1);
-
-    zero_results();
-    getCalloutManager()->callCallouts(delta_index_, callout_handle_1);
-    EXPECT_EQ("111781111", resultCalloutString(0));
-
-    // Run the callouts on hook beta to remove the callout on alpha.
-    getCalloutManager()->callCallouts(beta_index_, callout_handle_1);
-
-    // The run of the callouts should have altered the callout list on the
-    // first library for hook alpha, so call again to make sure.
-    CalloutHandle callout_handle_2(getCalloutManager());
-    callout_handle_2.setArgument("handle_num", static_cast<int>(2)); 
-    getCalloutManager()->callCallouts(alpha_index_, callout_handle_2);
-
-    zero_results();
-    getCalloutManager()->callCallouts(delta_index_, callout_handle_2);
-    EXPECT_EQ("112112", resultCalloutString(0));
-}
-
-// Deregistration of a callout from the same hook
-
-TEST_F(HandlesTest, DynamicDeregistrationSameHook) {
-    // Register callouts for the different libraries.
-    CalloutHandle handle(getCalloutManager());
-
-    // Set up callouts on "alpha".
-    getCalloutManager()->setLibraryIndex(0);
-    getCalloutManager()->registerCallout("alpha", callout11);
-    getCalloutManager()->registerCallout("alpha", delete_callout78_alpha);
-    getCalloutManager()->registerCallout("alpha", callout78);
-    getCalloutManager()->registerCallout("delta", print1);
-    getCalloutManager()->setLibraryIndex(1);
-    getCalloutManager()->registerCallout("alpha", callout21);
-    getCalloutManager()->registerCallout("alpha", callout78);
-    getCalloutManager()->registerCallout("delta", print2);
-
-    // Call the callouts on alpha
-    CalloutHandle callout_handle_1(getCalloutManager());
-    callout_handle_1.setArgument("handle_num", static_cast<int>(1)); 
-    getCalloutManager()->callCallouts(alpha_index_, callout_handle_1);
-
-    zero_results();
-    getCalloutManager()->callCallouts(delta_index_, callout_handle_1);
-    EXPECT_EQ("111781", resultCalloutString(0));
-    EXPECT_EQ("211781", resultCalloutString(1));
-
-    // The run of the callouts should have altered the callout list on the
-    // first library for hook alpha, so call again to make sure.
-    CalloutHandle callout_handle_2(getCalloutManager());
-    callout_handle_2.setArgument("handle_num", static_cast<int>(2)); 
-    getCalloutManager()->callCallouts(alpha_index_, callout_handle_2);
-
-    zero_results();
-    getCalloutManager()->callCallouts(delta_index_, callout_handle_2);
-    EXPECT_EQ("112", resultCalloutString(0));
-    EXPECT_EQ("212782", resultCalloutString(1));
-}
-
-// Testing the operation of the "skip" flag.  Callouts print the value
-// they see in the flag and either leave it unchanged, set it or clear it.
-
-int
-calloutPrintSkip(CalloutHandle& handle) {
-    static const std::string YES("Y");
-    static const std::string NO("N");
-
-    HandlesTest::common_string_ = HandlesTest::common_string_ +
-        (handle.getSkip() ? YES : NO);
-    return (0);
-}
-
-int
-calloutSetSkip(CalloutHandle& handle) {
-    static_cast<void>(calloutPrintSkip(handle));
-    handle.setSkip(true);
-    return (0);
-}
-
-int
-calloutClearSkip(CalloutHandle& handle) {
-    static_cast<void>(calloutPrintSkip(handle));
-    handle.setSkip(false);
-    return (0);
-}
-
-// Do a series of tests, returning with the skip flag set "true".
-
-TEST_F(HandlesTest, ReturnSkipSet) {
-    getCalloutManager()->setLibraryIndex(0);
-    getCalloutManager()->registerCallout("alpha", calloutPrintSkip);
-    getCalloutManager()->registerCallout("alpha", calloutSetSkip);
-    getCalloutManager()->registerCallout("alpha", calloutSetSkip);
-    getCalloutManager()->registerCallout("alpha", calloutClearSkip);
-
-    getCalloutManager()->setLibraryIndex(1);
-    getCalloutManager()->registerCallout("alpha", calloutPrintSkip);
-    getCalloutManager()->registerCallout("alpha", calloutSetSkip);
-    getCalloutManager()->registerCallout("alpha", calloutSetSkip);
-    getCalloutManager()->registerCallout("alpha", calloutClearSkip);
-    getCalloutManager()->registerCallout("alpha", calloutClearSkip);
-
-    getCalloutManager()->setLibraryIndex(2);
-    getCalloutManager()->registerCallout("alpha", calloutPrintSkip);
-    getCalloutManager()->registerCallout("alpha", calloutSetSkip);
-    getCalloutManager()->registerCallout("alpha", calloutClearSkip);
-    getCalloutManager()->registerCallout("alpha", calloutSetSkip);
-
-    CalloutHandle callout_handle(getCalloutManager());
-    getCalloutManager()->callCallouts(alpha_index_, callout_handle);
-
-    // Check result.  For each of visual checking, the expected string is
-    // divided into sections corresponding to the blocks of callouts above.
-    EXPECT_EQ(std::string("NNYY" "NNYYN" "NNYN"), common_string_);
-
-    // ... and check that the skip flag on exit from callCallouts is set.
-    EXPECT_TRUE(callout_handle.getSkip());
-}
-
-// Repeat the test, returning with the skip flag clear.
-TEST_F(HandlesTest, ReturnSkipClear) {
-    getCalloutManager()->setLibraryIndex(0);
-    getCalloutManager()->registerCallout("alpha", calloutSetSkip);
-    getCalloutManager()->registerCallout("alpha", calloutSetSkip);
-    getCalloutManager()->registerCallout("alpha", calloutClearSkip);
-
-    getCalloutManager()->setLibraryIndex(1);
-    getCalloutManager()->registerCallout("alpha", calloutPrintSkip);
-    getCalloutManager()->registerCallout("alpha", calloutSetSkip);
-    getCalloutManager()->registerCallout("alpha", calloutClearSkip);
-    getCalloutManager()->registerCallout("alpha", calloutSetSkip);
-    getCalloutManager()->registerCallout("alpha", calloutClearSkip);
-    getCalloutManager()->registerCallout("alpha", calloutClearSkip);
-
-    getCalloutManager()->setLibraryIndex(2);
-    getCalloutManager()->registerCallout("alpha", calloutClearSkip);
-    getCalloutManager()->registerCallout("alpha", calloutPrintSkip);
-    getCalloutManager()->registerCallout("alpha", calloutSetSkip);
-    getCalloutManager()->registerCallout("alpha", calloutClearSkip);
-
-    CalloutHandle callout_handle(getCalloutManager());
-    getCalloutManager()->callCallouts(alpha_index_, callout_handle);
-
-    // Check result.  For each of visual checking, the expected string is
-    // divided into sections corresponding to the blocks of callouts above.
-    EXPECT_EQ(std::string("NYY" "NNYNYN" "NNNY"), common_string_);
-
-    // ... and check that the skip flag on exit from callCallouts is set.
-    EXPECT_FALSE(callout_handle.getSkip());
-}
-
-// The next set of callouts do a similar thing to the above "skip" tests,
-// but alter the value of a string argument.  This is for testing that the
-// a callout is able to change an argument and return it to the caller.
-
-const char* MODIFIED_ARG = "modified_arg";
-
-int
-calloutSetArgumentCommon(CalloutHandle& handle, const char* what) {
-    std::string modified_arg = "";
-
-    handle.getArgument(MODIFIED_ARG, modified_arg);
-    modified_arg = modified_arg + std::string(what);
-    handle.setArgument(MODIFIED_ARG, modified_arg);
-    return (0);
-}
-
-int
-calloutSetArgumentYes(CalloutHandle& handle) {
-    return (calloutSetArgumentCommon(handle, "Y"));
-}
-
-int
-calloutSetArgumentNo(CalloutHandle& handle) {
-    return (calloutSetArgumentCommon(handle, "N"));
-}
-
-// ... and a callout to just copy the argument to the "common_string_" variable
-// but otherwise not alter it.
-
-int
-calloutPrintArgument(CalloutHandle& handle) {
-    handle.getArgument(MODIFIED_ARG, HandlesTest::common_string_);
-    return (0);
-}
-
-TEST_F(HandlesTest, CheckModifiedArgument) {
-    getCalloutManager()->setLibraryIndex(0);
-    getCalloutManager()->registerCallout("alpha", calloutSetArgumentYes);
-    getCalloutManager()->registerCallout("alpha", calloutSetArgumentNo);
-    getCalloutManager()->registerCallout("alpha", calloutSetArgumentNo);
-
-    getCalloutManager()->setLibraryIndex(1);
-    getCalloutManager()->registerCallout("alpha", calloutSetArgumentYes);
-    getCalloutManager()->registerCallout("alpha", calloutSetArgumentYes);
-    getCalloutManager()->registerCallout("alpha", calloutPrintArgument);
-    getCalloutManager()->registerCallout("alpha", calloutSetArgumentNo);
-    getCalloutManager()->registerCallout("alpha", calloutSetArgumentNo);
-
-    getCalloutManager()->setLibraryIndex(2);
-    getCalloutManager()->registerCallout("alpha", calloutSetArgumentYes);
-    getCalloutManager()->registerCallout("alpha", calloutSetArgumentNo);
-    getCalloutManager()->registerCallout("alpha", calloutSetArgumentYes);
-
-    // Create the argument with an initial empty string value.  Then call the
-    // sequence of callouts above.
-    CalloutHandle callout_handle(getCalloutManager());
-    std::string modified_arg = "";
-    callout_handle.setArgument(MODIFIED_ARG, modified_arg);
-    getCalloutManager()->callCallouts(alpha_index_, callout_handle);
-
-    // Check the intermediate and results.  For visual checking, the expected
-    // string is divided into sections corresponding to the blocks of callouts
-    // above.
-    EXPECT_EQ(std::string("YNN" "YY"), common_string_);
-
-    callout_handle.getArgument(MODIFIED_ARG, modified_arg);
-    EXPECT_EQ(std::string("YNN" "YYNN" "YNY"), modified_arg);
-}
-
-
-} // Anonymous namespace
-
diff --git a/src/lib/util/tests/memory_segment_mapped_unittest.cc b/src/lib/util/tests/memory_segment_mapped_unittest.cc
index c22b59e..8ae6fea 100644
--- a/src/lib/util/tests/memory_segment_mapped_unittest.cc
+++ b/src/lib/util/tests/memory_segment_mapped_unittest.cc
@@ -237,6 +237,15 @@ TEST_F(MemorySegmentMappedTest, allocate) {
 }
 
 TEST_F(MemorySegmentMappedTest, badAllocate) {
+    // If the test is run as the root user, the following allocate()
+    // call will result in a successful MemorySegmentGrown exception,
+    // instead of an abort (due to insufficient permissions during
+    // reopen).
+    if (getuid() == 0) {
+        std::cerr << "Skipping test as it's run as the root user" << std::endl;
+        return;
+    }
+
     // Make the mapped file non-writable; managed_mapped_file::grow() will
     // fail, resulting in abort.
     const int ret = chmod(mapped_file, 0444);
diff --git a/src/lib/util/tests/server_hooks_unittest.cc b/src/lib/util/tests/server_hooks_unittest.cc
deleted file mode 100644
index f029ad2..0000000
--- a/src/lib/util/tests/server_hooks_unittest.cc
+++ /dev/null
@@ -1,242 +0,0 @@
-// Copyright (C) 2013  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/hooks/server_hooks.h>
-
-#include <gtest/gtest.h>
-
-#include <algorithm>
-#include <string>
-#include <vector>
-
-using namespace isc;
-using namespace isc::util;
-using namespace std;
-
-namespace {
-
-// Checks the registration of hooks and the interrogation methods.  As the
-// constructor registers two hooks, this is also a test of the constructor.
-
-TEST(ServerHooksTest, RegisterHooks) {
-    ServerHooks hooks;
-
-    // There should be two hooks already registered, with indexes 0 and 1.
-    EXPECT_EQ(2, hooks.getCount());
-    EXPECT_EQ(0, hooks.getIndex("context_create"));
-    EXPECT_EQ(1, hooks.getIndex("context_destroy"));
-
-    // Check that the constants are as expected. (The intermediate variables
-    // are used because of problems with g++ 4.6.1/Ubuntu 11.10 when resolving
-    // the value of the ServerHooks constants when they appeared within the
-    // gtest macro.)
-    const int create_value = ServerHooks::CONTEXT_CREATE;
-    const int destroy_value = ServerHooks::CONTEXT_DESTROY;
-    EXPECT_EQ(0, create_value);
-    EXPECT_EQ(1, destroy_value);
-
-    // Register another couple of hooks.  The test on returned index is based
-    // on knowledge that the hook indexes are assigned in ascending order.
-    int alpha = hooks.registerHook("alpha");
-    EXPECT_EQ(2, alpha);
-    EXPECT_EQ(2, hooks.getIndex("alpha"));
-
-    int beta = hooks.registerHook("beta");
-    EXPECT_EQ(3, beta);
-    EXPECT_EQ(3, hooks.getIndex("beta"));
-
-    // Should be four hooks now
-    EXPECT_EQ(4, hooks.getCount());
-}
-
-// Check that duplicate names cannot be registered.
-
-TEST(ServerHooksTest, DuplicateHooks) {
-    ServerHooks hooks;
-
-    // Ensure we can't duplicate one of the existing names.
-    EXPECT_THROW(hooks.registerHook("context_create"), DuplicateHook);
-
-    // Check we can't duplicate a newly registered hook.
-    int gamma = hooks.registerHook("gamma");
-    EXPECT_EQ(2, gamma);
-    EXPECT_THROW(hooks.registerHook("gamma"), DuplicateHook);
-}
-
-// Checks that we can get the name of the hooks.
-
-TEST(ServerHooksTest, GetHookNames) {
-    vector<string> expected_names;
-    ServerHooks hooks;
-
-    // Add names into the hooks object and to the set of expected names.
-    expected_names.push_back("alpha");
-    expected_names.push_back("beta");
-    expected_names.push_back("gamma");
-    expected_names.push_back("delta");
-    for (int i = 0; i < expected_names.size(); ++i) {
-        hooks.registerHook(expected_names[i].c_str());
-    };
-
-    // Update the expected names to include the pre-defined hook names.
-    expected_names.push_back("context_create");
-    expected_names.push_back("context_destroy");
-
-    // Get the actual hook names
-    vector<string> actual_names = hooks.getHookNames();
-
-    // For comparison, sort the names into alphabetical order and do a straight
-    // vector comparison.
-    sort(expected_names.begin(), expected_names.end());
-    sort(actual_names.begin(), actual_names.end());
-
-    EXPECT_TRUE(expected_names == actual_names);
-}
-
-// Check that getting an unknown name throws an exception.
-
-TEST(ServerHooksTest, UnknownHookName) {
-    ServerHooks hooks;
-
-    EXPECT_THROW(static_cast<void>(hooks.getIndex("unknown")), NoSuchHook);
-}
-
-// Check that the count of hooks is correct.
-
-TEST(ServerHooksTest, HookCount) {
-    ServerHooks hooks;
-
-    // Insert the names into the hooks object
-    hooks.registerHook("alpha");
-    hooks.registerHook("beta");
-    hooks.registerHook("gamma");
-    hooks.registerHook("delta");
-
-    // Should be two more hooks that the number we have registered.
-    EXPECT_EQ(6, hooks.getCount());
-}
-
-// HookRegistrationFunction tests
-
-// Declare some hook registration functions.
-
-int alpha = 0;
-int beta = 0;
-int gamma = 0;
-int delta = 0;
-
-void registerAlphaBeta(ServerHooks& hooks) {
-    alpha = hooks.registerHook("alpha");
-    beta = hooks.registerHook("beta");
-}
-
-void registerGammaDelta(ServerHooks& hooks) {
-    gamma = hooks.registerHook("gamma");
-    delta = hooks.registerHook("delta");
-}
-
-// Add them to the registration vector.  This addition should happen before
-// any tests are run, so we should start off with two functions in the
-// registration vector.
-
-HookRegistrationFunction f1(registerAlphaBeta);
-HookRegistrationFunction f2(registerGammaDelta);
-
-// This is not registered statically: it is used in the latter part of the
-// test.
-
-int epsilon = 0;
-void registerEpsilon(ServerHooks& hooks) {
-    epsilon = hooks.registerHook("epsilon");
-}
-
-// Test that the registration functions were defined and can be executed.
-
-TEST(HookRegistrationFunction, Registration) {
-
-    // The first part of the tests checks the static registration.  As there
-    // is only one list of registration functions, we have to do this first
-    // as the static registration is done outside our control, before the
-    // tests are loaded.
-
-    // Ensure that the hook numbers are initialized.
-    EXPECT_EQ(0, alpha);
-    EXPECT_EQ(0, beta);
-    EXPECT_EQ(0, gamma);
-    EXPECT_EQ(0, delta);
-
-    // Should have two hook registration functions registered.
-    EXPECT_EQ(2, HookRegistrationFunction::getFunctionVector().size());
-
-    // Execute the functions and check that four new hooks were defined (two
-    // from each function).
-    ServerHooks hooks;
-    EXPECT_EQ(2, hooks.getCount());
-    HookRegistrationFunction::execute(hooks);
-    EXPECT_EQ(6, hooks.getCount());
-
-    // Check the hook names are as expected.
-    vector<string> names = hooks.getHookNames();
-    ASSERT_EQ(6, names.size());
-    sort(names.begin(), names.end());
-    EXPECT_EQ(string("alpha"), names[0]);
-    EXPECT_EQ(string("beta"), names[1]);
-    EXPECT_EQ(string("context_create"), names[2]);
-    EXPECT_EQ(string("context_destroy"), names[3]);
-    EXPECT_EQ(string("delta"), names[4]);
-    EXPECT_EQ(string("gamma"), names[5]);
-
-    // Check that numbers in the range 2-5 inclusive were assigned as the
-    // hook indexes (0 and 1 being reserved for context_create and
-    // context_destroy).
-    vector<int> indexes;
-    indexes.push_back(alpha);
-    indexes.push_back(beta);
-    indexes.push_back(gamma);
-    indexes.push_back(delta);
-    sort(indexes.begin(), indexes.end());
-    EXPECT_EQ(2, indexes[0]);
-    EXPECT_EQ(3, indexes[1]);
-    EXPECT_EQ(4, indexes[2]);
-    EXPECT_EQ(5, indexes[3]);
-
-    // One last check.  We'll test that the constructor of does indeed
-    // add a function to the function vector and that the static initialization
-    // was not somehow by chance.
-    HookRegistrationFunction::getFunctionVector().clear();
-    EXPECT_TRUE(HookRegistrationFunction::getFunctionVector().empty());
-    epsilon = 0;
-
-    // Register a single registration function.
-    HookRegistrationFunction f3(registerEpsilon);
-    EXPECT_EQ(1, HookRegistrationFunction::getFunctionVector().size());
-
-    // Execute it...
-    ServerHooks hooks2;
-    EXPECT_EQ(0, epsilon);
-    EXPECT_EQ(2, hooks2.getCount());
-    HookRegistrationFunction::execute(hooks2);
-
-    // There should be three hooks, with the new one assigned an index of 2.
-    names = hooks2.getHookNames();
-    ASSERT_EQ(3, names.size());
-    sort(names.begin(), names.end());
-    EXPECT_EQ(string("context_create"), names[0]);
-    EXPECT_EQ(string("context_destroy"), names[1]);
-    EXPECT_EQ(string("epsilon"), names[2]);
-
-    EXPECT_EQ(2, epsilon);
-}
-
-} // Anonymous namespace
diff --git a/tests/lettuce/configurations/xfrin/retransfer_slave.conf.orig b/tests/lettuce/configurations/xfrin/retransfer_slave.conf.orig
index 2e6b17f..aff8218 100644
--- a/tests/lettuce/configurations/xfrin/retransfer_slave.conf.orig
+++ b/tests/lettuce/configurations/xfrin/retransfer_slave.conf.orig
@@ -8,7 +8,6 @@
         } ]
     },
     "Auth": {
-        "database_file": "data/test_nonexistent_db.sqlite3",
         "listen_on": [ {
             "address": "::1",
             "port": 47806
diff --git a/tests/lettuce/configurations/xfrin/retransfer_slave_diffs.conf b/tests/lettuce/configurations/xfrin/retransfer_slave_diffs.conf
index 8f5e8fb..24bfa2a 100644
--- a/tests/lettuce/configurations/xfrin/retransfer_slave_diffs.conf
+++ b/tests/lettuce/configurations/xfrin/retransfer_slave_diffs.conf
@@ -8,7 +8,6 @@
         } ]
     },
     "Auth": {
-        "database_file": "data/xfrin-before-diffs.sqlite3",
         "listen_on": [ {
             "address": "::1",
             "port": 47806
diff --git a/tests/lettuce/configurations/xfrin/retransfer_slave_notify.conf.orig b/tests/lettuce/configurations/xfrin/retransfer_slave_notify.conf.orig
index 0d048c1..c4ba1ef 100644
--- a/tests/lettuce/configurations/xfrin/retransfer_slave_notify.conf.orig
+++ b/tests/lettuce/configurations/xfrin/retransfer_slave_notify.conf.orig
@@ -8,7 +8,6 @@
         } ]
     },
     "Auth": {
-        "database_file": "data/xfrin-notify.sqlite3",
         "listen_on": [ {
             "address": "::1",
             "port": 47806
diff --git a/tests/lettuce/configurations/xfrin/retransfer_slave_notify_v4.conf b/tests/lettuce/configurations/xfrin/retransfer_slave_notify_v4.conf
index 8e55878..b99f3f7 100644
--- a/tests/lettuce/configurations/xfrin/retransfer_slave_notify_v4.conf
+++ b/tests/lettuce/configurations/xfrin/retransfer_slave_notify_v4.conf
@@ -8,7 +8,6 @@
         } ]
     },
     "Auth": {
-        "database_file": "data/xfrin-notify.sqlite3",
         "listen_on": [ {
             "address": "127.0.0.1",
             "port": 47806
diff --git a/tests/lettuce/features/terrain/terrain.py b/tests/lettuce/features/terrain/terrain.py
index d82a6e1..d0ba4fe 100644
--- a/tests/lettuce/features/terrain/terrain.py
+++ b/tests/lettuce/features/terrain/terrain.py
@@ -135,7 +135,7 @@ class RunningProcess:
         """
         stderr_write = open(self.stderr_filename, "w")
         stdout_write = open(self.stdout_filename, "w")
-        self.process = subprocess.Popen(args, 1, None, subprocess.PIPE,
+        self.process = subprocess.Popen(args, 0, None, subprocess.PIPE,
                                         stdout_write, stderr_write)
         # open them again, this time for reading
         self.stderr = open(self.stderr_filename, "r")
diff --git a/tests/lettuce/features/xfrin_notify_handling.feature b/tests/lettuce/features/xfrin_notify_handling.feature
index 375a8a9..4f9d44e 100644
--- a/tests/lettuce/features/xfrin_notify_handling.feature
+++ b/tests/lettuce/features/xfrin_notify_handling.feature
@@ -75,7 +75,7 @@ Feature: Xfrin incoming notify handling
     last bindctl output should not contain "error"
 
     When I query statistics zones of bind10 module Xfrout with cmdctl port 47804
-    The statistics counters are 0 in category .Xfrout.zones except for the following items
+    The statistics counters are 0 in category .Xfrout.zones.IN except for the following items
       | item_name                | item_value |
       | _SERVER_.notifyoutv6     |          1 |
       | _SERVER_.xfrreqdone      |          1 |
@@ -100,7 +100,7 @@ Feature: Xfrin incoming notify handling
     last bindctl output should not contain "error"
 
     When I query statistics zones of bind10 module Xfrin with cmdctl
-    The statistics counters are 0 in category .Xfrin.zones except for the following items
+    The statistics counters are 0 in category .Xfrin.zones.IN except for the following items
       | item_name                       | item_value | min_value |
       | _SERVER_.soaoutv6               |          1 |           |
       | _SERVER_.axfrreqv6              |          1 |           |
@@ -177,7 +177,7 @@ Feature: Xfrin incoming notify handling
     last bindctl output should not contain "error"
 
     When I query statistics zones of bind10 module Xfrout with cmdctl port 47804
-    The statistics counters are 0 in category .Xfrout.zones except for the following items
+    The statistics counters are 0 in category .Xfrout.zones.IN except for the following items
       | item_name                | item_value |
       | _SERVER_.notifyoutv4     |          1 |
       | _SERVER_.xfrreqdone      |          1 |
@@ -202,7 +202,7 @@ Feature: Xfrin incoming notify handling
     last bindctl output should not contain "error"
 
     When I query statistics zones of bind10 module Xfrin with cmdctl
-    The statistics counters are 0 in category .Xfrin.zones except for the following items
+    The statistics counters are 0 in category .Xfrin.zones.IN except for the following items
       | item_name                       | item_value | min_value |
       | _SERVER_.soaoutv4               |          1 |           |
       | _SERVER_.axfrreqv4              |          1 |           |
@@ -282,7 +282,7 @@ Feature: Xfrin incoming notify handling
     last bindctl output should not contain "error"
 
     When I query statistics zones of bind10 module Xfrout with cmdctl port 47804
-    The statistics counters are 0 in category .Xfrout.zones except for the following items
+    The statistics counters are 0 in category .Xfrout.zones.IN except for the following items
       | item_name                | item_value | min_value | max_value |
       | _SERVER_.notifyoutv6     |          1 |           |           |
       | _SERVER_.xfrrej          |            |         1 |         3 |
@@ -310,7 +310,7 @@ Feature: Xfrin incoming notify handling
     last bindctl output should not contain "error"
 
     When I query statistics zones of bind10 module Xfrin with cmdctl
-    The statistics counters are 0 in category .Xfrin.zones except for the following items
+    The statistics counters are 0 in category .Xfrin.zones.IN except for the following items
       | item_name              | item_value |
       | _SERVER_.soaoutv6      |          1 |
       | _SERVER_.axfrreqv6     |          1 |
@@ -388,7 +388,7 @@ Feature: Xfrin incoming notify handling
     last bindctl output should not contain "error"
 
     When I query statistics zones of bind10 module Xfrout with cmdctl port 47804
-    The statistics counters are 0 in category .Xfrout.zones except for the following items
+    The statistics counters are 0 in category .Xfrout.zones.IN except for the following items
       | item_name                | item_value | min_value | max_value |
       | _SERVER_.notifyoutv4     |          1 |           |           |
       | _SERVER_.xfrrej          |            |         1 |         3 |
@@ -416,7 +416,7 @@ Feature: Xfrin incoming notify handling
     last bindctl output should not contain "error"
 
     When I query statistics zones of bind10 module Xfrin with cmdctl
-    The statistics counters are 0 in category .Xfrin.zones except for the following items
+    The statistics counters are 0 in category .Xfrin.zones.IN except for the following items
       | item_name              | item_value |
       | _SERVER_.soaoutv4      |          1 |
       | _SERVER_.axfrreqv4     |          1 |
@@ -454,7 +454,7 @@ Feature: Xfrin incoming notify handling
     last bindctl output should not contain "error"
 
     When I query statistics zones of bind10 module Xfrout with cmdctl port 47804
-    The statistics counters are 0 in category .Xfrout.zones except for the following items
+    The statistics counters are 0 in category .Xfrout.zones.IN except for the following items
       | item_name                | min_value | max_value |
       | _SERVER_.notifyoutv6     |         1 |	       5 |
       | example.org..notifyoutv6 |         1 |	       5 |



More information about the bind10-changes mailing list