BIND 10 trac452, updated. c9f6acc81e24c4b8f0eb351123dc7b43f64e0914 Merge branch 'master' into trac452

BIND 10 source code commits bind10-changes at lists.isc.org
Tue Feb 8 03:37:25 UTC 2011


The branch, trac452 has been updated
       via  c9f6acc81e24c4b8f0eb351123dc7b43f64e0914 (commit)
       via  29c6946c6a6a13a833b6037057debfa82ee19c22 (commit)
       via  6e31e88f3be72f3e774892b46e58dc00da565dac (commit)
       via  d2cb311448c6f9010ff2f614ca6ffbc0c0d668a8 (commit)
       via  731862b901f5530ac764db9605468d2a60ced17b (commit)
       via  65ff9720f00bccc88590d09aa8f6feae5356e081 (commit)
       via  1e5b246a41dbfee1fdeb672b07b4e73ee1b94353 (commit)
       via  66f1989566dd871d010b08506c2b3a130ad3ee13 (commit)
       via  c936d4d7d78f23bd06e5db397ac350d9e3deb1f3 (commit)
       via  89dd63f0d2b08ed9bb4e872ecd6b5987fa60523a (commit)
       via  5ff48bacf28bfc49dc6dc6351eb72f84a207f3ee (commit)
       via  32fcf336abbd3e989ab37108cc896c7f5d6984b2 (commit)
       via  94cb95b1d508541201fc064302ba836164d3cbe6 (commit)
       via  4d62e02fd9d410441b2f94f982e4a3b4b6e85ab4 (commit)
       via  7b1606cea7af15dc71f5ec1d70d958b00aa98af7 (commit)
       via  76fa3d127cf0118000f368195cde54b9b49063f1 (commit)
       via  b9296ca023cc9e76cda48a7eeebb0119166592c5 (commit)
       via  8cb9313e68bb0a7867e9c1dc6e51de1b3763fe95 (commit)
       via  75692d6d89837df09f1e2c74d0fd069553fa13d8 (commit)
       via  cc5136da5b04edb3cdc66e90f2f60f0f0291e8ff (commit)
       via  81ab8dbc34ed3b181170ecaf553266dfdfaf123f (commit)
       via  18706a6018483a20fff84781c5d2fced811c7a4f (commit)
       via  ab2c79a8815be54d7f3482ea5608d2611b7de433 (commit)
       via  ce77ca466280e9c63f5c47c001cd3e98381cd56a (commit)
       via  254f34910f282862727eaed7557657726887439d (commit)
       via  c73a307cb41eb3e8bb6681dd963f6953973330ca (commit)
       via  a9c024d3ea91431cb4a5cab0ee53aaf4b1f808c0 (commit)
       via  da171d46c0a6f14f40c3730fd3d6ba34d4bf9dff (commit)
       via  e29c8e8050f63c1a8ca1bcd0dfa0949c3cc94063 (commit)
       via  38bc36c4027fdcfbe74891f11f030014a34b46dc (commit)
       via  7a264c3e91a77fed53fd305d799db6b4f671108a (commit)
       via  430e0e89ec5ab05bdbbd43b5fba9c93bdcf8f6c9 (commit)
       via  1a52db4373a1c4169b0bbd9bc6a550e5a3e68b39 (commit)
       via  bbd103fff6a6a6e775a075c5132bc1cdd38a5acf (commit)
       via  ad83fe6b6c31a54e39116f12eb5c70d9fce8d9ea (commit)
       via  12aa5f7fd9f8ce0aa10b66cbd3608c363eb3390f (commit)
       via  873ab3f9db15055ec293533202bc73d908ab73c1 (commit)
       via  a929e898df74d43fc590e8df793edeb5f355e29d (commit)
       via  c11ea16f0ce2d7eac933dfc9e7b57e0e82972205 (commit)
       via  18964f9374531c8f131065dcd53aefd998e080ac (commit)
       via  8a4a87feea1dceee3b31a25b678d3e7ce45b2d5f (commit)
       via  1c325e8b3512372f5d69b366975621d06b5c217e (commit)
       via  58a43899ff533e725bb606a0359862dee416f7c7 (commit)
       via  f70d352db01e774ad9561499f2205de3e66e8dbb (commit)
       via  640640363d2a29cc0f4ab8151ca5c02dc9f7c361 (commit)
       via  0f31fd26c5525b7bd63acaea187f790c2fed8dc6 (commit)
       via  e4dd2ed94539a6e17070494878c9023e7a8472cb (commit)
       via  2ec914c9ed52fd0f22501addacb726ad193e1fed (commit)
       via  bf944144b1283d93a5138145cbdb576cea7a9fa0 (commit)
       via  d1971c14bddf6bcdbc031d1e3195b48ed4bffd62 (commit)
       via  cfc8341a5ff3f2cc7873689b2b787e9ed6dd1b02 (commit)
       via  08b215209a79db8420d565adaec7309413b01ea7 (commit)
       via  a0f8c9ee2bf78af6d95293a4d802e0205a4429c3 (commit)
       via  3372c331452e94681b82b708d11dd0f93c945153 (commit)
       via  311eb1ae0574c5f981937ae41614ff0fad034c9a (commit)
       via  7c42c6d38d63dd55462eb01037a55a40185920a3 (commit)
       via  6d9d7b76a3e55c6ca2a4c7af09674867a0e54c28 (commit)
       via  950ffa229928d24ba8ebd74c5945e4cf0facf539 (commit)
       via  06756779103175d716dbeee36aff59a116eef39d (commit)
       via  d351fe6ec30972595d70135cee24e6dd6d990d33 (commit)
       via  a697043708acddf2f9757615d173b514c98de9a4 (commit)
       via  00dab940cb413e50ac20df693d8a05053608aa40 (commit)
       via  b7c5f0703226ea1f68af4802e4b410cc42e98144 (commit)
       via  cf6de64ab83a1db3a71aa065c1d247462f236ea8 (commit)
       via  5f6cbcc31cc0f16af49dd433ac96da967d200d6c (commit)
       via  923977389a20aa0396dccec4867f3721fc95a4bd (commit)
       via  d8a7320161d314a5863183a063d0e00fdd1017bd (commit)
       via  b086fbca9ce75d8950cd51d0b9de3d49715565a7 (commit)
       via  eb4c7905227413fe7d4ee8a42d1e8dfc185d36f1 (commit)
       via  47dc887dfdf6d3501a73bad82f30f6148fc2ff8e (commit)
       via  2565b9fe9daf6ba63234b06b9f0eb5b8168257b4 (commit)
       via  c2b374e5e2d608ce0236b7f49b63bf70b57c761f (commit)
       via  2e93f9a6c23985a4e9968c13c3068c61f202ca4f (commit)
       via  6031b9096d5a9c73468afa386a3e311984125522 (commit)
       via  83904ae5b53bf4a5a5436902f4be88f818db984a (commit)
       via  f0a43d242f48103eec0703d7081522ec0f2225a9 (commit)
       via  a6f9d5cd07ea1b21641232312cc15adcce91f1ec (commit)
       via  8909387beb99136bc5ec22574515c84e8ef526da (commit)
       via  952124e0f4d2b64d8b3a47b52163edca7c3546ab (commit)
       via  73a679d7b03fc3c53fb4826a95ee099321fb4c4d (commit)
       via  af4b23610ff5cb55748737dff2b67c4f905b2480 (commit)
       via  2363b50bc04e0218e185561a4e198f21e8783905 (commit)
       via  4265faedd3e14e2f4afccd664df07a4e09e3ae7f (commit)
       via  4db21f814bf18d17475dad5e81b8bbb8e0dfa570 (commit)
       via  b4aed028611c1f229d44cd688053b5635f6c909d (commit)
       via  c5d416a01e152b936c0023fcc65d3d468250388b (commit)
       via  f0a09daf74c62309e9de65adf3b99daf967ca0ac (commit)
       via  9f172776a92413f7ba09fae87c98522c3d55fe63 (commit)
       via  a1e26d979de642bc8fc52d613f65c0dcd8fc8ee3 (commit)
       via  d6113a313ff848124a3ecd2e58b17bb04b5bcbee (commit)
       via  578ea7f4ba94dc0d8a3d39231dad2be118e125a2 (commit)
       via  210a3fa8f933d7141f0ca48e5088a89fcac611ef (commit)
       via  1dbc33e995c77185505e91387359f0f4ff7adadd (commit)
       via  661ee8d341d0a24b6cab13302b028a9efb40daa4 (commit)
       via  1dd7c3d6806b8694fa315f55c105884245e486ea (commit)
       via  ed3436f599952a3e070dbf19668e5bcb2e9dacac (commit)
       via  dd2f336165e9f974aaed88569b5e718493cd5757 (commit)
       via  5720ab60da4b443e9414852234d65acccd8e9e4e (commit)
       via  f4b2e3f8f7404dc3739a211d23022336f33609f8 (commit)
       via  95861564f241fa7f657eb7c34449c3095fba1930 (commit)
       via  ad011294186315051152f2f2bb9d9af09b8f338d (commit)
       via  8a175757cd9b46205b7118c3e7fad72883f130e5 (commit)
       via  94b0beeb606dedc3c8fc44856e0a6f2d3c9754b4 (commit)
       via  424bb3df5cd1e1cf8b64f76ff5f0118da05de9e6 (commit)
       via  5d03cd4b844bf1288032ee9a148aef5077a167f3 (commit)
       via  c8e84e56caeb809f91c7823ae1c4e9aa30e49c57 (commit)
       via  324b135b99582c63aa49f50d8c80e00d6e43e2f9 (commit)
       via  6fb3a7ca7f9f1b7018493a263b3063b0b0962b5e (commit)
       via  239c9694953dcd1dc1ffdd60b7205fca5fdd651c (commit)
       via  b211d93037f31731426dbf073098ebfa8ae16bc4 (commit)
       via  5823a2ff954e0965048c5d4cc33314d9bb2ae637 (commit)
       via  c30cd00cf1afde68776d7320cb283a8f9a911163 (commit)
       via  6f6f5892046cb552bf334d2e5f84cf575dde3da5 (commit)
       via  a2a812ede6d3df4a70d6309437c6715cb09a7670 (commit)
       via  749c26e2c6cdc98abf0f7d6dc693acf122e5d9f5 (commit)
       via  a5bd8c03942d981b01fb0be3f8ea54ebcd960945 (commit)
       via  6c8cd2a184118179db9e54eed8967cedbafecbab (commit)
       via  acc6a3ab370cd5c22ad37bb79f819578bb1e6e97 (commit)
       via  3e5f772632f1472a1125e0cd725adf6709635e8c (commit)
       via  a07e078b4feeb01949133fc88c9939254c38aa7c (commit)
       via  748885cd80a12aad0f02fc7b53943c2620d9eb22 (commit)
       via  c5e46db3463427504c4814d1ad712e3f09ea9c36 (commit)
       via  5609b0c697b2f3d4684ced27d35fcb91721a48e0 (commit)
       via  93697f58e4d912fa87bc7f9a591c1febc9e0d139 (commit)
       via  4b4813d5873856044ce28b91b1e14c8a0f84b59f (commit)
       via  89cbb6aafeaf5cbc51b50a0caa4542c7b0f43eb4 (commit)
       via  d85c19ad4f3a92897eba8f2868f5a661bbc9a177 (commit)
       via  7b84de4c0e11f4a070e038ca4f093486e55622af (commit)
       via  90ede89d895da7a088715ee315b12dfb843ee2fe (commit)
       via  18491370576e7438c7893f8551bbb8647001be9c (commit)
       via  b9b7ef39987bc2f0031bb7f46b9002665a62607a (commit)
       via  98d6a12c51d58d7712c6876dde8c548162cbcbdd (commit)
       via  ed69c4be96f940b86a45823fa2efd010f53a357c (commit)
       via  83033bbfd1298a45240e073c25bae9b0bb2f4e6f (commit)
       via  6e9d5c8b61fff0b1f2a1dfb9d685bbd6ce090e8a (commit)
       via  1133c5a571d5bbddaeb44c2313cce75ebd624eeb (commit)
       via  1243b751048365abdb5cec845a92674e0469b7b4 (commit)
       via  89f3a8b45f18f1bf4e1a2e6082a93513582b6d55 (commit)
       via  64ebfb19dd4c42b8ba81b258a1a408345c3b1caf (commit)
       via  9d0ec38ee3ef9e08778abd66c6946bdec5f2d25b (commit)
       via  fb981291afb0a4dc0fcbd16b9bb552e5fbb5c20b (commit)
       via  2f151e47e2579aed8c124385dacb3c971978ed45 (commit)
       via  bfa3f83286dd88d263e6c12c74ed3f2073210e03 (commit)
       via  a0d8f67619db8737abda16b3ef9b1b731ab1bfa1 (commit)
       via  6b118748704d34fbea4d80cbed3a1094abd22e11 (commit)
       via  51d35705f42ab9b20a315edbc5dd8a9541b0483c (commit)
       via  3e3bcbeb2f3ceae0863e9ad943f589ac4c8e0ec3 (commit)
       via  344dcf64d70d10d47d405b4e88e3cefca777aa64 (commit)
       via  a039907d688e2ae82af39369baa72a9c43805eb1 (commit)
       via  57ba31639cd4f5686b220adbba6e91f80d3626c0 (commit)
       via  fd9e8511f7759b0eecab6e0a2447dfbea2b2de1a (commit)
       via  94270478763c9c4000ca94afb94fb4762f56c3f9 (commit)
       via  bebfa15d5007c3ba8ce740403c8333ea6480b83e (commit)
       via  3c337419ad37fb4af2d14e7d470e7c9c35790b40 (commit)
       via  dd37e9539f78bb16e4a044f4df3a7a6c8b284856 (commit)
       via  88369b5ed0c0b023b1c985fa01964500a4c37a1f (commit)
       via  bc2a7f57b5a7dc127045309f7f2ff42badc4bb95 (commit)
       via  d6d7f1157668e98a1f849d53258b8f9c1dee1c78 (commit)
       via  b20ba8d51b2d395da64857e71151e0ef2f018b25 (commit)
       via  e5febac1248d25e3a026889ae2914fdb868361da (commit)
       via  e3e59cf1f240c7ab556ff8f827888f13d698e677 (commit)
       via  1916a92fba92220b3a7b2149a325fc7eefca2c72 (commit)
       via  30838487e181829182543e1a32e25ad313172b89 (commit)
       via  a8793f5ce8927d0a746c41636dc7ab414f8d21ff (commit)
       via  77c5ed7fa2654d2740477b6e9e15c23fb635032a (commit)
       via  1a11edc24cdbea4f90653cf0cd94344084b9f568 (commit)
       via  3ccbc9347eb6fc3ea9d3b3ac9e181a1e1b94e42d (commit)
       via  8f23ba4a72eed354852b023a4d41adfe4a62bb0c (commit)
       via  b3ec8dc3523bf2573cdc33a73eb21605d8a32305 (commit)
       via  36b738c03eaba27228285a9ee00a424b799b1f61 (commit)
       via  21e8607873dc52d860e3b82d00b0ab7a9eaa6deb (commit)
       via  61aedbfffa0a5edf8e9e7076b54fafb927ddf3c2 (commit)
       via  b39bfceed83508b4e0a9932f864d15da1787c3f9 (commit)
       via  93f50d1a63829198f9afc280dbd9e12057c434ed (commit)
       via  f9ceab26e3c573ccdabff7658b8221fbbe9a6122 (commit)
       via  692c6b15659b6af9b092dfb6263d152421cf05a7 (commit)
       via  107229aeffecbac4278fdbf6515513c391e8ad2d (commit)
       via  90c89f6d435109f6228f309ae1824e0b8c552416 (commit)
       via  273d03db595f480800e4d071da9997cebbe976e9 (commit)
       via  ce2fead07b25247d05b3175a30863a898b819db1 (commit)
       via  3e151d447774c4d1cc1cbf854f29a75aaa43d6ce (commit)
       via  928d2871d02f7f9c17b27b2c91ae4e1314a72d24 (commit)
       via  43adf3a50adde888fe01661f69069d4b8d690b18 (commit)
       via  6ed7ca08bb751d131db2b3d7837e0c02e4704145 (commit)
       via  1c6d27d61112ae3cfd1663d51863c58e37d5e675 (commit)
       via  cd2d518fbcb19a0119553f71593b7a9d908b90c3 (commit)
       via  ef811c6e76fe84505141ceb053af0b110a87c0b8 (commit)
       via  c14c4741b754a1eb226d3bdc3a7abbc4c5d727c0 (commit)
       via  0e10d2264eae5b3067d5dd5d2e5d1a70d2016adf (commit)
       via  ce648a9362d9e10dfcfbda87546ea91c8bca5c61 (commit)
       via  11da99d8c55b326acb8c7866ab9c7fe9c4800888 (commit)
       via  70ca4dedec19d177198de545f8a330c8d55d33bc (commit)
       via  62c72fcdf4617e4841e901408f1e7961255b8194 (commit)
       via  a81bd162356eea6812e6b8bdb64cd7d9eb495097 (commit)
       via  3752558cd9e659d55cdd5f261e45df6a793f4aeb (commit)
       via  eca0f29b4b1159730e6ab82964880a98bb4cf7d6 (commit)
       via  cb38a8f920ba899ec0e1c674cce8e4b2fa1599bc (commit)
       via  d37301004adbac7209e1b9ad1a2fe6cfa0ea9354 (commit)
       via  16be1dd56846ee964ffd527ec0291e13ce8d23a3 (commit)
       via  395bbc339365b2d512eed63a04179845bc36c308 (commit)
       via  7a8bfbdd24317e216fc10ae01d9a41fd791a8795 (commit)
       via  66ace85fab74d0c2d5760f1d01a40e440161ddb7 (commit)
       via  597bb698382ffdd04cdd2869ce5d2570dd5ac875 (commit)
       via  a26f28f2fec4d557a74c3582ed7230f87e7e9731 (commit)
       via  063c1cf69047425e8553efd7ae3f48c5c0fac072 (commit)
       via  e01dadd724cfe1274f45c6be4b5ba6f532df8c0e (commit)
       via  bd45814672cd96ddb708af3eead4461a3870c440 (commit)
       via  d0a9b15c2cc5a9f61b2a3f136d54192d6c4b212c (commit)
       via  53033d94889ce0cadaf20f743b2deb6b68a08af7 (commit)
       via  878a6a4b89e1efb2bab3d5d50d9e5144c5761d9b (commit)
       via  c4f0d38193a1a6719bd31af29a60b51e405678d5 (commit)
       via  5ed5d8034e14418db7f473d966cad68dc1629262 (commit)
       via  182a134e8cd02ee7c32171d876fd4c96c009ead8 (commit)
       via  2240f7d6ed56168afa2882b38e20d677548e3ab5 (commit)
       via  1b44409d9774464306529b4174f34a4172e392d1 (commit)
       via  006424d3ebd1a9b1d5b6f7daa1c98707265ec6fd (commit)
       via  a1210be107e45632b7e4fb47d74d627a5ae18901 (commit)
       via  ea084ffdffb800600ccd66a2afe84579e36749d3 (commit)
       via  64e656b8ca74d9198d5035dcc017d4e8347d089f (commit)
       via  ce5624a28b5eb026cd7d0bf42d1f0bf523013912 (commit)
       via  4fb7fbb237becd17fc4fb6c370bd6ee6d02a4eca (commit)
       via  47eb191bbb4990ca935d962ee31ab72bbc398194 (commit)
       via  5dd96968ae574f78725e8cafeaebbee35f1eba79 (commit)
       via  1de3a15628847340019d8f9a9f4d0ec7cce5b593 (commit)
       via  635c6e467ed5d9bfe731a726bec673a70aad0d0a (commit)
       via  29bfd26b74745ed3d0278bfbad06a1defadff657 (commit)
       via  c772a141d7963b2d327287c37a0720fa65a192ae (commit)
       via  ee96cc0479b7fbb8a90817d43b4a778e94c28fdc (commit)
       via  caf6ca1dc4f8e014f975d168af370cf7a35a5fbf (commit)
       via  6e4c7c07b7d6dc58213df0c67100ead14932c1ea (commit)
       via  e9c8b1402a1bd1834209c1f29aa121cc514388ce (commit)
       via  b4c068b2189668459f6f945d6cbe51e294af4e8d (commit)
       via  3be136324e68d5ebab0b0d3da88550a920662c98 (commit)
       via  435b7a283437aa13047cc69c1b9e28284a3de46c (commit)
       via  294251d62ece34da011ecf662df9dd0c1b141a53 (commit)
       via  34a083b7721a2b32177ff9b4a4f768f03435a326 (commit)
       via  f11adb59bb8be08e4ee47340ff5be153646afbcd (commit)
       via  1efc3bee0b08fe1abaabcee82f4391591b1be2a3 (commit)
       via  59270dc809d9013d502f7ab66788e900eab53a3b (commit)
       via  d2a4816b0595fa3b8b0a3b9b55228108cddfa82f (commit)
       via  848b1823fc4c893f7f398759e3b6b9dde35b05f9 (commit)
       via  7b4bd8aae373869067f7d8b5156925c87c37df8f (commit)
       via  cdf66cc9c391019d5d76c8685a719f38a06c5610 (commit)
       via  758460fa8ad9cd35841f5c44dd700d9f74f3d070 (commit)
       via  3666c6d78561034b5235d91547f94c554adc4b11 (commit)
       via  ebb344c1e2b8bb7eaa424ae4b03a46ac01787ff9 (commit)
       via  17684b0c3c8dae44b22c9f69945fbd4b24faa9bf (commit)
      from  24bb934f0ad2732c50730187376d96ddadefecbc (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 c9f6acc81e24c4b8f0eb351123dc7b43f64e0914
Merge: 24bb934f0ad2732c50730187376d96ddadefecbc 29c6946c6a6a13a833b6037057debfa82ee19c22
Author: Yoshitaka Aharen <aharen at jprs.co.jp>
Date:   Tue Feb 8 12:33:01 2011 +0900

    Merge branch 'master' into trac452

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

Summary of changes:
 ChangeLog                                          |   81 ++-
 Makefile.am                                        |    8 +-
 README                                             |    2 +-
 configure.ac                                       |   67 ++-
 doc/Doxyfile                                       |    2 +-
 doc/Makefile.am                                    |    3 +
 doc/guide/Makefile                                 |   13 -
 doc/guide/Makefile.am                              |   16 +
 doc/guide/bind10-guide.html                        |  152 ++++-
 doc/guide/bind10-guide.xml                         |  182 +++++-
 doc/version.ent.in                                 |    1 +
 src/bin/auth/auth_srv.cc                           |   12 +-
 src/bin/auth/auth_srv.h                            |    2 -
 src/bin/auth/b10-auth.8                            |  123 +++-
 src/bin/auth/b10-auth.xml                          |  113 +++-
 src/bin/auth/benchmarks/Makefile.am                |    1 +
 src/bin/auth/benchmarks/query_bench.cc             |    2 -
 src/bin/auth/change_user.cc                        |    2 -
 src/bin/auth/change_user.h                         |    2 -
 src/bin/auth/common.h                              |    2 -
 src/bin/auth/main.cc                               |    2 -
 src/bin/auth/query.cc                              |   41 +-
 src/bin/auth/statistics.cc                         |    2 -
 src/bin/auth/statistics.h                          |    2 -
 src/bin/auth/tests/Makefile.am                     |    1 +
 src/bin/auth/tests/auth_srv_unittest.cc            |    9 +-
 src/bin/auth/tests/change_user_unittest.cc         |    2 -
 src/bin/auth/tests/query_unittest.cc               |  727 +++++++++++---------
 src/bin/auth/tests/run_unittests.cc                |    2 -
 src/bin/auth/tests/statistics_unittest.cc          |    2 -
 src/bin/bind10/bind10.xml                          |    3 +-
 src/bin/bindctl/bindctl.1                          |   57 ++-
 src/bin/bindctl/bindctl.xml                        |   90 +++-
 src/bin/cfgmgr/b10-cfgmgr.py.in                    |    2 -
 src/bin/cfgmgr/b10-cfgmgr.xml                      |    1 -
 src/bin/cfgmgr/tests/b10-cfgmgr_test.py.in         |    2 -
 src/bin/cmdctl/b10-cmdctl.xml                      |    1 -
 src/bin/loadzone/b10-loadzone.xml                  |    1 -
 src/bin/loadzone/tests/correct/mix1.db             |    1 -
 src/bin/loadzone/tests/correct/ttlext.db           |    1 -
 src/bin/msgq/msgq.py.in                            |  120 +++-
 src/bin/msgq/msgq.xml                              |    1 -
 src/bin/msgq/tests/msgq_test.py                    |  129 ++++-
 src/bin/resolver/Makefile.am                       |    2 +
 src/bin/resolver/b10-resolver.8                    |   45 +-
 src/bin/resolver/b10-resolver.xml                  |   73 ++-
 src/bin/resolver/main.cc                           |    2 -
 src/bin/resolver/resolver.cc                       |  332 ++++++----
 src/bin/resolver/resolver.h                        |   69 ++-
 src/bin/resolver/resolver.spec.pre.in              |   40 +-
 src/bin/resolver/response_classifier.cc            |  259 +++++++
 src/bin/resolver/response_classifier.h             |  138 ++++
 src/bin/resolver/response_scrubber.cc              |  189 +++++
 src/bin/resolver/response_scrubber.h               |  422 ++++++++++++
 src/bin/resolver/tests/Makefile.am                 |   14 +-
 src/bin/resolver/tests/resolver_config_unittest.cc |   79 ++-
 src/bin/resolver/tests/resolver_unittest.cc        |   18 +-
 .../resolver/tests/response_classifier_unittest.cc |  494 +++++++++++++
 .../resolver/tests/response_scrubber_unittest.cc   |  542 +++++++++++++++
 src/bin/stats/b10-stats.xml                        |    4 +-
 src/bin/stats/stats.py.in                          |    1 -
 src/bin/stats/stats_stub.py.in                     |    1 -
 src/bin/stats/tests/b10-stats_stub_test.py         |    1 -
 src/bin/stats/tests/b10-stats_test.py              |    1 -
 src/bin/stats/tests/fake_time.py                   |    1 -
 src/bin/stats/tests/isc/cc/session.py              |    1 -
 src/bin/stats/tests/isc/config/ccsession.py        |    1 -
 src/bin/stats/tests/isc/util/process.py            |    2 -
 src/bin/stats/tests/isc/utils/process.py           |    2 -
 src/bin/usermgr/b10-cmdctl-usermgr.py.in           |    2 +-
 src/bin/usermgr/b10-cmdctl-usermgr.xml             |    1 -
 src/bin/xfrin/b10-xfrin.xml                        |    3 +-
 src/bin/xfrin/tests/xfrin_test.py                  |    2 -
 src/bin/xfrin/xfrin.py.in                          |   24 +-
 src/bin/xfrout/b10-xfrout.8                        |   20 +
 src/bin/xfrout/b10-xfrout.xml                      |   28 +-
 src/bin/zonemgr/b10-zonemgr.8                      |   24 +-
 src/bin/zonemgr/b10-zonemgr.xml                    |   35 +-
 src/bin/zonemgr/tests/zonemgr_test.py              |    3 +
 src/bin/zonemgr/zonemgr.py.in                      |    6 +-
 src/lib/Makefile.am                                |    4 +-
 src/lib/asiolink/Makefile.am                       |    1 +
 src/lib/asiolink/asiolink.cc                       |  313 ++++++++-
 src/lib/asiolink/asiolink.h                        |   75 ++-
 src/lib/asiolink/internal/tcpdns.h                 |    5 +-
 src/lib/asiolink/internal/udpdns.h                 |   70 +--
 src/lib/asiolink/ioaddress.cc                      |    2 -
 src/lib/asiolink/ioaddress.h                       |   43 ++-
 src/lib/asiolink/ioendpoint.cc                     |    2 -
 src/lib/asiolink/ioendpoint.h                      |    3 +-
 src/lib/asiolink/iomessage.h                       |    2 -
 src/lib/asiolink/iosocket.cc                       |    2 -
 src/lib/asiolink/iosocket.h                        |    2 -
 src/lib/asiolink/tcpdns.cc                         |   11 +-
 src/lib/asiolink/tests/Makefile.am                 |    2 +-
 src/lib/asiolink/tests/asiolink_unittest.cc        |  276 +++++++--
 src/lib/asiolink/tests/run_unittests.cc            |    2 -
 src/lib/asiolink/udpdns.cc                         |  225 +++++--
 src/lib/bench/benchmark.h                          |    2 -
 src/lib/bench/benchmark_util.cc                    |    2 -
 src/lib/bench/benchmark_util.h                     |    2 -
 src/lib/bench/example/search_bench.cc              |    2 -
 src/lib/bench/tests/benchmark_unittest.cc          |    2 -
 src/lib/bench/tests/loadquery_unittest.cc          |    2 -
 src/lib/bench/tests/run_unittests.cc               |    2 -
 src/lib/cc/data.cc                                 |    2 -
 src/lib/cc/data.h                                  |    2 -
 src/lib/cc/session.cc                              |    2 -
 src/lib/cc/session.h                               |    2 -
 src/lib/cc/tests/data_unittests.cc                 |    2 -
 src/lib/cc/tests/run_unittests.cc                  |    2 -
 src/lib/cc/tests/session_unittests.cc              |    2 -
 src/lib/config/ccsession.cc                        |    2 -
 src/lib/config/ccsession.h                         |    2 -
 src/lib/config/config_data.cc                      |    2 -
 src/lib/config/config_data.h                       |    2 -
 src/lib/config/tests/ccsession_unittests.cc        |    2 -
 src/lib/config/tests/config_data_unittests.cc      |    2 -
 src/lib/config/tests/fake_session.cc               |    2 -
 src/lib/config/tests/fake_session.h                |    2 -
 src/lib/config/tests/module_spec_unittests.cc      |    2 -
 src/lib/config/tests/run_unittests.cc              |    2 -
 src/lib/datasrc/cache.cc                           |    2 -
 src/lib/datasrc/cache.h                            |    2 -
 src/lib/datasrc/data_source.cc                     |    2 -
 src/lib/datasrc/data_source.h                      |    2 -
 src/lib/datasrc/memory_datasrc.cc                  |  209 +++++-
 src/lib/datasrc/memory_datasrc.h                   |   35 +-
 src/lib/datasrc/query.cc                           |    2 -
 src/lib/datasrc/query.h                            |    2 -
 src/lib/datasrc/rbtree.h                           |  486 +++++++++++---
 src/lib/datasrc/sqlite3_datasrc.cc                 |    2 -
 src/lib/datasrc/sqlite3_datasrc.h                  |    2 -
 src/lib/datasrc/static_datasrc.cc                  |    4 +-
 src/lib/datasrc/static_datasrc.h                   |    8 +-
 src/lib/datasrc/tests/cache_unittest.cc            |    2 -
 src/lib/datasrc/tests/datasrc_unittest.cc          |    2 -
 src/lib/datasrc/tests/memory_datasrc_unittest.cc   |  241 ++++++-
 src/lib/datasrc/tests/query_unittest.cc            |    2 -
 src/lib/datasrc/tests/rbtree_unittest.cc           |  171 ++++-
 src/lib/datasrc/tests/run_unittests.cc             |    2 -
 src/lib/datasrc/tests/sqlite3_unittest.cc          |    2 -
 src/lib/datasrc/tests/static_unittest.cc           |    6 +-
 src/lib/datasrc/tests/test_datasrc.cc              |    2 -
 src/lib/datasrc/tests/test_datasrc.h               |    2 -
 src/lib/datasrc/tests/testdata/mkbrokendb.c        |    2 -
 src/lib/datasrc/zone.h                             |   24 +-
 src/lib/dns/buffer.h                               |    2 -
 src/lib/dns/dnssectime.cc                          |    2 -
 src/lib/dns/dnssectime.h                           |    2 -
 src/lib/dns/edns.cc                                |    2 -
 src/lib/dns/edns.h                                 |    2 -
 src/lib/dns/exceptions.cc                          |    2 -
 src/lib/dns/exceptions.h                           |    2 -
 src/lib/dns/gen-rdatacode.py.in                    |    2 -
 src/lib/dns/message.cc                             |   32 +-
 src/lib/dns/message.h                              |   29 +-
 src/lib/dns/messagerenderer.cc                     |    2 -
 src/lib/dns/messagerenderer.h                      |    2 -
 src/lib/dns/name.cc                                |    2 -
 src/lib/dns/name.h                                 |    2 -
 src/lib/dns/opcode.cc                              |    2 -
 src/lib/dns/opcode.h                               |    2 -
 src/lib/dns/python/edns_python.cc                  |    2 -
 src/lib/dns/python/message_python.cc               |    2 -
 src/lib/dns/python/messagerenderer_python.cc       |    2 -
 src/lib/dns/python/name_python.cc                  |    2 -
 src/lib/dns/python/opcode_python.cc                |    2 -
 src/lib/dns/python/pydnspp.cc                      |    2 -
 src/lib/dns/python/pydnspp_common.cc               |    2 -
 src/lib/dns/python/pydnspp_common.h                |    2 -
 src/lib/dns/python/question_python.cc              |    2 -
 src/lib/dns/python/rcode_python.cc                 |    2 -
 src/lib/dns/python/rdata_python.cc                 |    2 -
 src/lib/dns/python/rrclass_python.cc               |    2 -
 src/lib/dns/python/rrset_python.cc                 |    2 -
 src/lib/dns/python/rrttl_python.cc                 |    2 -
 src/lib/dns/python/rrtype_python.cc                |    2 -
 src/lib/dns/python/tests/edns_python_test.py       |    2 -
 src/lib/dns/python/tests/testutil.py               |    2 -
 src/lib/dns/python/tests/tsigkey_python_test.py    |    2 -
 src/lib/dns/python/tsigkey_python.cc               |    2 -
 src/lib/dns/question.cc                            |    2 -
 src/lib/dns/question.h                             |   26 +-
 src/lib/dns/rcode.cc                               |    2 -
 src/lib/dns/rcode.h                                |    2 -
 src/lib/dns/rdata.cc                               |    2 -
 src/lib/dns/rdata.h                                |    2 -
 src/lib/dns/rdata/any_255/tsig_250.cc              |    2 -
 src/lib/dns/rdata/any_255/tsig_250.h               |    2 -
 src/lib/dns/rdata/ch_3/a_1.cc                      |    2 -
 src/lib/dns/rdata/ch_3/a_1.h                       |    2 -
 src/lib/dns/rdata/generic/cname_5.cc               |    2 -
 src/lib/dns/rdata/generic/cname_5.h                |    2 -
 src/lib/dns/rdata/generic/dname_39.cc              |    2 -
 src/lib/dns/rdata/generic/dname_39.h               |    2 -
 src/lib/dns/rdata/generic/dnskey_48.cc             |    2 -
 src/lib/dns/rdata/generic/dnskey_48.h              |    2 -
 src/lib/dns/rdata/generic/ds_43.cc                 |    2 -
 src/lib/dns/rdata/generic/ds_43.h                  |    2 -
 src/lib/dns/rdata/generic/mx_15.cc                 |    2 -
 src/lib/dns/rdata/generic/mx_15.h                  |    2 -
 src/lib/dns/rdata/generic/ns_2.cc                  |    2 -
 src/lib/dns/rdata/generic/ns_2.h                   |    2 -
 src/lib/dns/rdata/generic/nsec3_50.cc              |    2 -
 src/lib/dns/rdata/generic/nsec3_50.h               |    2 -
 src/lib/dns/rdata/generic/nsec3param_51.cc         |    2 -
 src/lib/dns/rdata/generic/nsec3param_51.h          |    2 -
 src/lib/dns/rdata/generic/nsec_47.cc               |    2 -
 src/lib/dns/rdata/generic/nsec_47.h                |    2 -
 src/lib/dns/rdata/generic/opt_41.cc                |    2 -
 src/lib/dns/rdata/generic/opt_41.h                 |    2 -
 src/lib/dns/rdata/generic/ptr_12.cc                |    2 -
 src/lib/dns/rdata/generic/ptr_12.h                 |    2 -
 src/lib/dns/rdata/generic/rrsig_46.cc              |    2 -
 src/lib/dns/rdata/generic/rrsig_46.h               |    2 -
 src/lib/dns/rdata/generic/soa_6.cc                 |    2 -
 src/lib/dns/rdata/generic/soa_6.h                  |    2 -
 src/lib/dns/rdata/generic/txt_16.cc                |    2 -
 src/lib/dns/rdata/generic/txt_16.h                 |    2 -
 src/lib/dns/rdata/hs_4/a_1.cc                      |    2 -
 src/lib/dns/rdata/hs_4/a_1.h                       |    2 -
 src/lib/dns/rdata/in_1/a_1.cc                      |    2 -
 src/lib/dns/rdata/in_1/a_1.h                       |    2 -
 src/lib/dns/rdata/in_1/aaaa_28.cc                  |    2 -
 src/lib/dns/rdata/in_1/aaaa_28.h                   |    2 -
 src/lib/dns/rdata/template.cc                      |    2 -
 src/lib/dns/rdata/template.h                       |    2 -
 src/lib/dns/rrclass-placeholder.h                  |    2 -
 src/lib/dns/rrclass.cc                             |    2 -
 src/lib/dns/rrparamregistry-placeholder.cc         |    2 -
 src/lib/dns/rrparamregistry.h                      |    2 -
 src/lib/dns/rrset.cc                               |    2 -
 src/lib/dns/rrset.h                                |    2 -
 src/lib/dns/rrsetlist.cc                           |    2 -
 src/lib/dns/rrsetlist.h                            |   23 +-
 src/lib/dns/rrttl.cc                               |    2 -
 src/lib/dns/rrttl.h                                |    2 -
 src/lib/dns/rrtype-placeholder.h                   |    2 -
 src/lib/dns/rrtype.cc                              |    2 -
 src/lib/dns/tests/base32hex_unittest.cc            |    2 -
 src/lib/dns/tests/base64_unittest.cc               |    2 -
 src/lib/dns/tests/buffer_unittest.cc               |    2 -
 src/lib/dns/tests/dnssectime_unittest.cc           |    2 -
 src/lib/dns/tests/edns_unittest.cc                 |    2 -
 src/lib/dns/tests/hex_unittest.cc                  |    2 -
 src/lib/dns/tests/message_unittest.cc              |   47 ++-
 src/lib/dns/tests/messagerenderer_unittest.cc      |    2 -
 src/lib/dns/tests/name_unittest.cc                 |    2 -
 src/lib/dns/tests/opcode_unittest.cc               |    2 -
 src/lib/dns/tests/question_unittest.cc             |   35 +-
 src/lib/dns/tests/rcode_unittest.cc                |    2 -
 src/lib/dns/tests/rdata_cname_unittest.cc          |    2 -
 src/lib/dns/tests/rdata_dname_unittest.cc          |    2 -
 src/lib/dns/tests/rdata_dnskey_unittest.cc         |    2 -
 src/lib/dns/tests/rdata_ds_unittest.cc             |    2 -
 src/lib/dns/tests/rdata_in_a_unittest.cc           |    2 -
 src/lib/dns/tests/rdata_in_aaaa_unittest.cc        |    2 -
 src/lib/dns/tests/rdata_mx_unittest.cc             |    2 -
 src/lib/dns/tests/rdata_ns_unittest.cc             |    2 -
 src/lib/dns/tests/rdata_nsec3_unittest.cc          |    2 -
 src/lib/dns/tests/rdata_nsec3param_unittest.cc     |    2 -
 src/lib/dns/tests/rdata_nsec_unittest.cc           |    2 -
 src/lib/dns/tests/rdata_opt_unittest.cc            |    2 -
 src/lib/dns/tests/rdata_ptr_unittest.cc            |    2 -
 src/lib/dns/tests/rdata_rrsig_unittest.cc          |    2 -
 src/lib/dns/tests/rdata_soa_unittest.cc            |    2 -
 src/lib/dns/tests/rdata_tsig_unittest.cc           |    2 -
 src/lib/dns/tests/rdata_txt_unittest.cc            |    2 -
 src/lib/dns/tests/rdata_unittest.cc                |    2 -
 src/lib/dns/tests/rdata_unittest.h                 |    2 -
 src/lib/dns/tests/rrclass_unittest.cc              |    2 -
 src/lib/dns/tests/rrparamregistry_unittest.cc      |    2 -
 src/lib/dns/tests/rrset_unittest.cc                |    2 -
 src/lib/dns/tests/rrsetlist_unittest.cc            |    2 -
 src/lib/dns/tests/rrttl_unittest.cc                |    2 -
 src/lib/dns/tests/rrtype_unittest.cc               |    2 -
 src/lib/dns/tests/run_unittests.cc                 |    2 -
 src/lib/dns/tests/sha1_unittest.cc                 |    2 -
 src/lib/dns/tests/unittest_util.cc                 |    2 -
 src/lib/dns/tests/unittest_util.h                  |    2 -
 src/lib/dns/tsigkey.cc                             |    2 -
 src/lib/dns/tsigkey.h                              |    2 -
 src/lib/dns/util/base32hex.h                       |    2 -
 src/lib/dns/util/base64.h                          |    2 -
 src/lib/dns/util/base_n.cc                         |    2 -
 src/lib/dns/util/hex.h                             |    2 -
 src/lib/exceptions/exceptions.cc                   |    2 -
 src/lib/exceptions/exceptions.h                    |    2 -
 src/lib/exceptions/tests/exceptions_unittest.cc    |    2 -
 src/lib/exceptions/tests/run_unittests.cc          |    2 -
 src/lib/log/Makefile.am                            |   43 ++-
 src/lib/log/compiler/Makefile.am                   |   20 +
 src/lib/log/compiler/message.cc                    |  450 ++++++++++++
 src/lib/log/dbglevels.h                            |   31 +
 src/lib/log/documentation.txt                      |  371 ++++++++++
 src/lib/log/filename.cc                            |  140 ++++
 src/lib/log/filename.h                             |  163 +++++
 src/lib/log/logger.cc                              |  307 +++++++++
 src/lib/log/logger.h                               |  327 +++++++++
 src/lib/log/logger_support.cc                      |  116 ++++
 src/lib/log/logger_support.h                       |   43 ++
 src/lib/log/message_dictionary.cc                  |  116 ++++
 src/lib/log/message_dictionary.h                   |  150 ++++
 src/lib/log/message_exception.cc                   |   28 +
 src/lib/log/message_exception.h                    |   90 +++
 src/lib/log/message_initializer.cc                 |   32 +
 src/lib/log/message_initializer.h                  |   63 ++
 src/lib/log/message_reader.cc                      |  184 +++++
 src/lib/log/message_reader.h                       |  175 +++++
 src/lib/log/message_types.h                        |   32 +
 src/lib/log/messagedef.cc                          |   27 +
 src/lib/log/messagedef.h                           |   24 +
 src/lib/log/messagedef.mes                         |   82 +++
 src/lib/log/root_logger_name.cc                    |   26 +
 src/lib/log/root_logger_name.h                     |   66 ++
 src/lib/log/strutil.cc                             |  138 ++++
 src/lib/log/strutil.h                              |  147 ++++
 src/lib/log/tests/Makefile.am                      |   45 ++
 src/lib/log/tests/filename_unittest.cc             |  181 +++++
 src/lib/log/tests/localdef.mes                     |   23 +
 src/lib/log/tests/logger_support_test.cc           |  109 +++
 src/lib/log/tests/logger_unittest.cc               |  395 +++++++++++
 src/lib/log/tests/message_dictionary_unittest.cc   |  173 +++++
 src/lib/log/tests/message_initializer_unittest.cc  |   72 ++
 .../log/tests/message_initializer_unittest_2.cc    |   41 ++
 src/lib/log/tests/message_reader_unittest.cc       |  228 ++++++
 src/lib/log/tests/root_logger_name_unittest.cc     |   52 ++
 src/lib/log/tests/run_time_init_test.sh.in         |   81 +++
 src/lib/log/tests/run_unittests.cc                 |   23 +
 src/lib/log/tests/strutil_unittest.cc              |  216 ++++++
 src/lib/log/tests/xdebuglevel_unittest.cc          |  205 ++++++
 src/lib/log/xdebuglevel.cc                         |  148 ++++
 src/lib/log/xdebuglevel.h                          |  164 +++++
 src/lib/nsas/Makefile.am                           |    1 -
 src/lib/nsas/address_entry.cc                      |    2 -
 src/lib/nsas/address_entry.h                       |    2 -
 src/lib/nsas/address_request_callback.h            |    2 -
 src/lib/nsas/asiolink.h                            |    2 -
 src/lib/nsas/fetchable.h                           |    2 -
 src/lib/nsas/hash.cc                               |    2 -
 src/lib/nsas/hash.h                                |    2 -
 src/lib/nsas/hash_deleter.h                        |    2 -
 src/lib/nsas/hash_key.cc                           |    2 -
 src/lib/nsas/hash_key.h                            |    2 -
 src/lib/nsas/hash_table.h                          |    2 -
 src/lib/nsas/lru_list.h                            |    2 -
 src/lib/nsas/nameserver_address.cc                 |    2 -
 src/lib/nsas/nameserver_address.h                  |    2 -
 src/lib/nsas/nameserver_address_store.cc           |   11 +-
 src/lib/nsas/nameserver_address_store.h            |    9 +-
 src/lib/nsas/nameserver_entry.cc                   |   28 +-
 src/lib/nsas/nameserver_entry.h                    |   10 +-
 src/lib/nsas/nsas_entry.h                          |    2 -
 src/lib/nsas/nsas_entry_compare.h                  |    2 -
 src/lib/nsas/nsas_types.h                          |    2 -
 src/lib/nsas/random_number_generator.h             |    2 -
 src/lib/nsas/resolver_interface.h                  |   80 ---
 src/lib/nsas/tests/address_entry_unittest.cc       |    1 -
 src/lib/nsas/tests/fetchable_unittest.cc           |    1 -
 src/lib/nsas/tests/hash_deleter_unittest.cc        |    1 -
 src/lib/nsas/tests/hash_key_unittest.cc            |    1 -
 src/lib/nsas/tests/hash_table_unittest.cc          |    1 -
 src/lib/nsas/tests/hash_unittest.cc                |    1 -
 src/lib/nsas/tests/lru_list_unittest.cc            |    1 -
 .../tests/nameserver_address_store_unittest.cc     |    3 +-
 src/lib/nsas/tests/nameserver_address_unittest.cc  |    7 +-
 src/lib/nsas/tests/nameserver_entry_unittest.cc    |   17 +-
 src/lib/nsas/tests/nsas_entry_compare_unittest.cc  |    1 -
 src/lib/nsas/tests/nsas_test.h                     |   33 +-
 .../nsas/tests/random_number_generator_unittest.cc |    1 -
 src/lib/nsas/tests/run_unittests.cc                |    1 -
 src/lib/nsas/tests/zone_entry_unittest.cc          |   10 +-
 src/lib/nsas/zone_entry.cc                         |   24 +-
 src/lib/nsas/zone_entry.h                          |   10 +-
 src/lib/python/isc/config/ccsession.py             |   12 +-
 src/lib/python/isc/config/tests/ccsession_test.py  |    2 -
 src/lib/python/isc/config/tests/cfgmgr_test.py     |    2 -
 .../python/isc/config/tests/config_data_test.py    |    2 -
 .../python/isc/config/tests/module_spec_test.py    |    2 -
 .../isc/config/tests/unittest_fakesession.py       |    2 -
 src/lib/python/isc/datasrc/master.py               |    2 -
 src/lib/python/isc/datasrc/sqlite3_ds.py           |    2 -
 src/lib/resolve/Makefile.am                        |   17 +
 src/lib/resolve/resolve.h                          |   20 +
 src/lib/resolve/resolver_callback.cc               |   36 +
 src/lib/resolve/resolver_callback.h                |   48 ++
 src/lib/resolve/resolver_interface.h               |   98 +++
 src/lib/resolve/tests/Makefile.am                  |   24 +
 .../resolve/tests/resolver_callback_unittest.cc    |   90 +++
 src/lib/resolve/tests/run_unittests.cc             |   24 +
 src/lib/testutils/Makefile.am                      |    1 +
 src/lib/testutils/dnsmessage_test.cc               |  115 +++
 src/lib/testutils/dnsmessage_test.h                |  304 ++++++++
 src/lib/testutils/mockups.h                        |    4 +-
 src/lib/testutils/srv_test.cc                      |   41 +-
 src/lib/testutils/srv_test.h                       |   11 +-
 src/lib/xfr/fd_share.cc                            |    2 -
 src/lib/xfr/fd_share.h                             |    2 -
 src/lib/xfr/fdshare_python.cc                      |    2 -
 src/lib/xfr/python_xfr.cc                          |    2 -
 src/lib/xfr/xfrout_client.cc                       |    2 -
 src/lib/xfr/xfrout_client.h                        |    2 -
 403 files changed, 12523 insertions(+), 1822 deletions(-)
 create mode 100644 doc/Makefile.am
 delete mode 100644 doc/guide/Makefile
 create mode 100644 doc/guide/Makefile.am
 create mode 100644 doc/version.ent.in
 mode change 100644 => 100755 src/bin/cfgmgr/b10-cfgmgr.py.in
 mode change 100644 => 100755 src/bin/msgq/msgq.py.in
 create mode 100644 src/bin/resolver/response_classifier.cc
 create mode 100644 src/bin/resolver/response_classifier.h
 create mode 100644 src/bin/resolver/response_scrubber.cc
 create mode 100644 src/bin/resolver/response_scrubber.h
 create mode 100644 src/bin/resolver/tests/response_classifier_unittest.cc
 create mode 100644 src/bin/resolver/tests/response_scrubber_unittest.cc
 mode change 100644 => 100755 src/bin/stats/stats.py.in
 mode change 100644 => 100755 src/bin/stats/stats_stub.py.in
 mode change 100644 => 100755 src/bin/xfrin/xfrin.py.in
 mode change 100644 => 100755 src/bin/zonemgr/zonemgr.py.in
 create mode 100644 src/lib/log/compiler/Makefile.am
 create mode 100644 src/lib/log/compiler/message.cc
 create mode 100644 src/lib/log/dbglevels.h
 create mode 100644 src/lib/log/documentation.txt
 create mode 100644 src/lib/log/filename.cc
 create mode 100644 src/lib/log/filename.h
 create mode 100644 src/lib/log/logger.cc
 create mode 100644 src/lib/log/logger.h
 create mode 100644 src/lib/log/logger_support.cc
 create mode 100644 src/lib/log/logger_support.h
 create mode 100644 src/lib/log/message_dictionary.cc
 create mode 100644 src/lib/log/message_dictionary.h
 create mode 100644 src/lib/log/message_exception.cc
 create mode 100644 src/lib/log/message_exception.h
 create mode 100644 src/lib/log/message_initializer.cc
 create mode 100644 src/lib/log/message_initializer.h
 create mode 100644 src/lib/log/message_reader.cc
 create mode 100644 src/lib/log/message_reader.h
 create mode 100644 src/lib/log/message_types.h
 create mode 100644 src/lib/log/messagedef.cc
 create mode 100644 src/lib/log/messagedef.h
 create mode 100644 src/lib/log/messagedef.mes
 create mode 100644 src/lib/log/root_logger_name.cc
 create mode 100644 src/lib/log/root_logger_name.h
 create mode 100644 src/lib/log/strutil.cc
 create mode 100644 src/lib/log/strutil.h
 create mode 100644 src/lib/log/tests/Makefile.am
 create mode 100644 src/lib/log/tests/filename_unittest.cc
 create mode 100644 src/lib/log/tests/localdef.mes
 create mode 100644 src/lib/log/tests/logger_support_test.cc
 create mode 100644 src/lib/log/tests/logger_unittest.cc
 create mode 100644 src/lib/log/tests/message_dictionary_unittest.cc
 create mode 100644 src/lib/log/tests/message_initializer_unittest.cc
 create mode 100644 src/lib/log/tests/message_initializer_unittest_2.cc
 create mode 100644 src/lib/log/tests/message_reader_unittest.cc
 create mode 100644 src/lib/log/tests/root_logger_name_unittest.cc
 create mode 100755 src/lib/log/tests/run_time_init_test.sh.in
 create mode 100644 src/lib/log/tests/run_unittests.cc
 create mode 100644 src/lib/log/tests/strutil_unittest.cc
 create mode 100644 src/lib/log/tests/xdebuglevel_unittest.cc
 create mode 100644 src/lib/log/xdebuglevel.cc
 create mode 100644 src/lib/log/xdebuglevel.h
 delete mode 100644 src/lib/nsas/resolver_interface.h
 create mode 100644 src/lib/resolve/Makefile.am
 create mode 100644 src/lib/resolve/resolve.h
 create mode 100644 src/lib/resolve/resolver_callback.cc
 create mode 100644 src/lib/resolve/resolver_callback.h
 create mode 100644 src/lib/resolve/resolver_interface.h
 create mode 100644 src/lib/resolve/tests/Makefile.am
 create mode 100644 src/lib/resolve/tests/resolver_callback_unittest.cc
 create mode 100644 src/lib/resolve/tests/run_unittests.cc
 create mode 100644 src/lib/testutils/dnsmessage_test.cc
 create mode 100644 src/lib/testutils/dnsmessage_test.h

-----------------------------------------------------------------------
diff --git a/ChangeLog b/ChangeLog
index 1e0f21f..29e62c9 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,74 @@
+  163.  [func]      vorner
+	The pimpl design pattern is used in UDPServer, with a shared pointer. This
+	makes it smaller to copy (which is done a lot as a sideeffect of being
+	coroutine) and speeds applications of this class (notably b10-auth) up by
+	around 10%.
+	(Trac #537, git 94cb95b1d508541201fc064302ba836164d3cbe6)
+
+  162.  [func]		stephen
+	Added C++ logging, allowing logging at different severities.
+	Code specifies the message to be logged via a symbol, and the
+	logging code picks up the message from an in-built dictionary.
+	The contents of the dictionary can be replaced at run-time by
+	locale-specific messages.  A message compiler program is provided
+	to create message header files and supply the default messages.
+	(Trac #438, git 7b1606cea7af15dc71f5ec1d70d958b00aa98af7)
+
+  161.  [func]		stephen
+	Added ResponseScrubber class to examine response from
+	a server and to remove out-of-bailiwick RRsets.  Also
+	does cross-section checks to ensure consistency.
+	(Trac #496, git b9296ca023cc9e76cda48a7eeebb0119166592c5)
+
+  160.  [func]		jelte
+  	Updated the resolver to take 3 different timeout values;
+	timeout_query for outstanding queries we sent while resolving
+	timeout_client for sending an answer back to the client
+	timeout_lookup for stopping the resolving
+	(currently 2 and 3 have the same final effect)
+	(Trac 489, git 578ea7f4ba94dc0d8a3d39231dad2be118e125a2)
+
+  159.	[func]		smann
+	The resolver now has a configurable set of root servers to start
+	resolving at (called root_addresses). By default these are not
+	(yet) filled in. If empty, a hardcoded address for f-root will be
+	used right now.
+	(Trac #483, git a07e078b4feeb01949133fc88c9939254c38aa7c)
+
+  158.	[func]		jelte
+	The Resolver module will now do (very limited) resolving, if not
+	set to forwarding mode (i.e. if the configuration option
+	forward_addresses is left empty). It only supports referrals that
+	contain glue addresses at this point, and does no other processing
+	of authoritative answers.
+	(Trac #484, git 7b84de4c0e11f4a070e038ca4f093486e55622af)
+
+  157.  [bug]       vorner
+	One frozen process no longer freezes the whole b10-msgq. It caused the
+	whole system to stop working.
+	(Trac #420, git 93697f58e4d912fa87bc7f9a591c1febc9e0d139)
+
+  156.	[func]		stephen
+	Added ResponseClassifier class to examine response from
+	a server and classify it into one of several categories.
+	(Trac #487, git 18491370576e7438c7893f8551bbb8647001be9c)
+
+bind10-devel-20110120 released on January 20, 2011
+
+  155.	[doc]		jreed
+	Miscellaneous documentation improvements for man pages and
+	the guide, including auth, resolver, stats, xfrout, and
+	zonemgr.  (git c14c4741b754a1eb226d3bdc3a7abbc4c5d727c0)
+
+  154.	[bug]		jinmei
+	b10-xfrin/b10-zonemgr: Fixed a bug where these programs didn't
+	receive command responses from CC sessions.  Eventually the
+	receive buffer became full, and many other components that rely
+	on CC channels would stall (as noted in #420 and #513).  This is
+	an urgent care fix due to the severity of the problem; we'll need
+	to revisit it for cleaner fix later.
+	(Trac #516, git 62c72fcdf4617e4841e901408f1e7961255b8194)
+
   153.	[bug]		jelte
 	b10-cfgmgr: Fixed a bug where configuration updates sometimes
 	lost previous settings in the configuration manager.
@@ -10,7 +81,7 @@
 	the value to 0.  Disabling statistics updates will also work as
 	a temporary workaround of a known issue that b10-auth can block in
 	sending statistics and stop responding to queries as a result.
-	(Trac #513, git 285c5ee)
+	(Trac #513, git 285c5ee3d5582ed6df02d1aa00387f92a74e3695)
 
   151.  [bug]		smann
 	lib/log/dummylog.h: 
@@ -42,7 +113,7 @@
   147.	[bug]		jinmei
 	python/isc/config: Fixed a bug that importing custom configuration
 	(in b10-config.db) of a remote module didn't work.
-	(Trac #478, git ea4a481)
+	(Trac #478, git ea4a481003d80caf2bff8d0187790efd526d72ca)
 
   146.	[func]		jelte
 	Command arguments were not validated internally against their
@@ -57,7 +128,8 @@
 	only feasible for class IN in memory data source.  To reload a
 	zone "example.com" via bindctl, execute the command as follows:
 	> Auth loadzone origin = example.com
-	(Trac #467)
+	(Trac #467 git 4f7e1f46da1046de527ab129a88f6aad3dba7562
+	from 1d7d3918661ba1c6a8b1e40d8fcbc5640a84df12)
 
   144.	[build]		jinmei
 	Introduced a workaround for clang++ build on FreeBSD (and probably
@@ -67,7 +139,8 @@
 	doesn't matter; the important part is the -L flag).  This
 	workaround is not automatically enabled as it's difficult to
 	detect the need for it dynamically, and must be enabled via the
-	variable by hand. (Trac #474, git cfde436)
+	variable by hand.
+	(Trac #474, git cfde436fbd7ddf3f49cbbd153999656e8ca2a298)
 
   143.	[build]		jinmei
 	Fixed build problems with clang++ in unit tests due to recent
diff --git a/Makefile.am b/Makefile.am
index e49b1f0..93a7498 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = src
+SUBDIRS = doc src
 USE_LCOV=@USE_LCOV@
 LCOV=@LCOV@
 GENHTML=@GENHTML@
@@ -282,9 +282,3 @@ EXTRA_DIST += ext/asio/asio/is_write_buffered.hpp
 EXTRA_DIST += ext/asio/asio/buffered_read_stream_fwd.hpp
 EXTRA_DIST += ext/asio/asio/socket_acceptor_service.hpp
 EXTRA_DIST += ext/asio/asio.hpp
-
-## include the guide in tarball, later will include the other parts there
-## but they cleanup.
-EXTRA_DIST += doc/guide/bind10-guide.css
-EXTRA_DIST += doc/guide/bind10-guide.html
-EXTRA_DIST += doc/guide/bind10-guide.xml
diff --git a/README b/README
index 04eb923..9b258bc 100644
--- a/README
+++ b/README
@@ -48,7 +48,7 @@ Simple build instructions:
   ./configure
   make
 
-If building from Subversion repository, run:
+If building from Git repository, run:
 
   autoreconf --install
 
diff --git a/configure.ac b/configure.ac
index 415985e..87d8473 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2,7 +2,7 @@
 # Process this file with autoconf to produce a configure script.
 
 AC_PREREQ([2.59])
-AC_INIT(bind10-devel, 20101201, bind10-dev at isc.org)
+AC_INIT(bind10-devel, 20110120, bind10-dev at isc.org)
 AC_CONFIG_SRCDIR(README)
 AM_INIT_AUTOMAKE
 AC_CONFIG_HEADERS([config.h])
@@ -363,6 +363,57 @@ if test "$lcov" != "no"; then
 fi
 AC_SUBST(USE_LCOV)
 
+# Configure log4cxx header and library path
+#
+# If explicitly specified, use it.
+
+AC_ARG_WITH([log4cxx],
+  AC_HELP_STRING([--with-log4cxx=PATH],
+    [specify directory where log4cxx is installed]),
+  [
+   log4cxx_include_path="${withval}/include";
+   log4cxx_library_path="${withval}/lib"
+  ])
+
+# This is an urgent fix to avoid regression due to log4cxx on some
+# platforms.  It should be cleaned up with a better fix.
+if test "X$with_log4cxx" != "Xno"; then
+
+# If not specified, try some common paths.  These default to
+# /usr/include and /usr/lib if not found
+
+if test -z "$with_log4cxx"; then
+	log4cxxdirs="/usr/local /usr/pkg /opt /opt/local"
+	for d in $log4cxxdirs
+	do
+		if test -d $d/include/log4cxx; then
+			log4cxx_include_path=$d/include
+			log4cxx_library_path=$d/lib
+			break
+		fi
+	done
+fi
+
+CPPFLAGS_SAVES="$CPPFLAGS"
+if test "${log4cxx_include_path}" ; then
+	LOG4CXX_INCLUDES="-I${log4cxx_include_path}"
+	CPPFLAGS="$CPPFLAGS $LOG4CXX_INCLUDES"
+fi
+AC_CHECK_HEADER([log4cxx/logger.h],, AC_MSG_ERROR([Missing log4cxx header files.]))
+CPPFLAGS="$CPPFLAGS_SAVES"
+AC_SUBST(LOG4CXX_INCLUDES)
+
+LOG4CXX_LDFLAGS="-llog4cxx";
+if test "${log4cxx_library_path}"; then
+    LOG4CXX_LDFLAGS="-L${log4cxx_library_path} -llog4cxx"
+fi
+AC_SUBST(LOG4CXX_LDFLAGS)
+
+# The following two lines are part of the urgent fix, and should be cleaned
+# up with a better fix.
+fi
+AM_CONDITIONAL(USE_LOG4CXX, test "X${with_log4cxx}" != "Xno")
+
 #
 # Configure Boost header path
 #
@@ -574,6 +625,8 @@ AC_ARG_ENABLE(install-configurations,
 AM_CONDITIONAL(INSTALL_CONFIGURATIONS, test x$install_configurations = xyes || test x$install_configurations = xtrue)
 
 AC_CONFIG_FILES([Makefile
+                 doc/Makefile
+                 doc/guide/Makefile
                  src/Makefile
                  src/bin/Makefile
                  src/bin/bind10/Makefile
@@ -650,12 +703,17 @@ AC_CONFIG_FILES([Makefile
                  src/lib/datasrc/tests/Makefile
                  src/lib/xfr/Makefile
                  src/lib/log/Makefile
+                 src/lib/log/compiler/Makefile
+                 src/lib/log/tests/Makefile
+                 src/lib/resolve/Makefile
+                 src/lib/resolve/tests/Makefile
                  src/lib/testutils/Makefile
                  src/lib/testutils/testdata/Makefile
                  src/lib/nsas/Makefile
                  src/lib/nsas/tests/Makefile
                ])
-AC_OUTPUT([src/bin/cfgmgr/b10-cfgmgr.py
+AC_OUTPUT([doc/version.ent
+           src/bin/cfgmgr/b10-cfgmgr.py
            src/bin/cfgmgr/tests/b10-cfgmgr_test.py
            src/bin/cmdctl/cmdctl.py
            src/bin/cmdctl/run_b10-cmdctl.sh
@@ -708,11 +766,11 @@ AC_OUTPUT([src/bin/cfgmgr/b10-cfgmgr.py
            src/lib/dns/tests/testdata/gen-wiredata.py
            src/lib/cc/session_config.h.pre
            src/lib/cc/tests/session_unittests_config.h
+           src/lib/log/tests/run_time_init_test.sh
           ], [
            chmod +x src/bin/cmdctl/run_b10-cmdctl.sh
            chmod +x src/bin/xfrin/run_b10-xfrin.sh
            chmod +x src/bin/xfrout/run_b10-xfrout.sh
-           chmod +x src/bin/resolver/run_b10-resolver.sh
            chmod +x src/bin/zonemgr/run_b10-zonemgr.sh
            chmod +x src/bin/stats/tests/stats_test
            chmod +x src/bin/stats/run_b10-stats.sh
@@ -732,6 +790,7 @@ AC_OUTPUT([src/bin/cfgmgr/b10-cfgmgr.py
            chmod +x src/bin/msgq/tests/msgq_test
            chmod +x src/lib/dns/gen-rdatacode.py
            chmod +x src/lib/dns/tests/testdata/gen-wiredata.py
+           chmod +x src/lib/log/tests/run_time_init_test.sh
           ])
 AC_OUTPUT
 
@@ -759,6 +818,8 @@ dnl includes too
                  ${PYTHON_LDFLAGS}
                  ${PYTHON_LIB}
   Boost:         ${BOOST_INCLUDES}
+  log4cxx:       ${LOG4CXX_INCLUDES}
+                 ${LOG4CXX_LDFLAGS}
   SQLite:        $SQLITE_CFLAGS
                  $SQLITE_LIBS
 
diff --git a/doc/Doxyfile b/doc/Doxyfile
index 0e1ab00..6ab2001 100644
--- a/doc/Doxyfile
+++ b/doc/Doxyfile
@@ -568,7 +568,7 @@ WARN_LOGFILE           =
 # directories like "/usr/src/myproject". Separate the files or directories
 # with spaces.
 
-INPUT                  = ../src/lib/cc ../src/lib/config ../src/lib/dns ../src/lib/exceptions ../src/lib/datasrc ../src/bin/auth ../src/lib/bench ../src/lib/log ../src/lib/asiolink/ ../src/lib/nsas
+INPUT                  = ../src/lib/cc ../src/lib/config ../src/lib/dns ../src/lib/exceptions ../src/lib/datasrc ../src/bin/auth ../src/bin/resolver ../src/lib/bench ../src/lib/log ../src/lib/asiolink/ ../src/lib/nsas ../src/lib/testutils
 
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
diff --git a/doc/Makefile.am b/doc/Makefile.am
new file mode 100644
index 0000000..31d0cfa
--- /dev/null
+++ b/doc/Makefile.am
@@ -0,0 +1,3 @@
+SUBDIRS = guide
+
+EXTRA_DIST = version.ent.in
diff --git a/doc/guide/Makefile b/doc/guide/Makefile
deleted file mode 100644
index 23882b2..0000000
--- a/doc/guide/Makefile
+++ /dev/null
@@ -1,13 +0,0 @@
-#
-# Quick and dirty makefile
-#
-
-bind10-guide.html: bind10-guide.xml
-	xsltproc --novalid --xinclude --nonet \
-		-o bind10-guide.html \
-		--stringparam html.stylesheet bind10-guide.css \
-		http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl \
-		bind10-guide.xml
-
-clean:
-	rm -f bind10-guide.html
diff --git a/doc/guide/Makefile.am b/doc/guide/Makefile.am
new file mode 100644
index 0000000..c790139
--- /dev/null
+++ b/doc/guide/Makefile.am
@@ -0,0 +1,16 @@
+EXTRA_DIST = bind10-guide.css
+EXTRA_DIST += bind10-guide.html
+EXTRA_DIST += bind10-guide.xml
+
+# This is not a "man" manual, but reuse this for now for docbook.
+if ENABLE_MAN
+
+bind10-guide.html: bind10-guide.xml
+	xsltproc --novalid --xinclude --nonet \
+		--path $(top_builddir)/doc \
+		-o $@ \
+		--stringparam html.stylesheet $(srcdir)/bind10-guide.css \
+		http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl \
+		$(srcdir)/bind10-guide.xml
+
+endif
diff --git a/doc/guide/bind10-guide.html b/doc/guide/bind10-guide.html
index b2c132e..98c7e46 100644
--- a/doc/guide/bind10-guide.html
+++ b/doc/guide/bind10-guide.html
@@ -1,22 +1,25 @@
-<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>BIND 10 Guide</title><link rel="stylesheet" href="bind10-guide.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.75.2"><meta name="description" content="This is the reference guide for BIND 10. The most up-to-date version of this document, along with other documents for BIND 10, can be found at ."></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="book" title="BIND 10 Guide"><div class="titlepage"><div><div><h1 class="title"><a name="id1168230298903"></a>BIND 10 Guide</h1></div><div><h2 class="subtitle">Administrator Reference for BIND 10</h2></div><div><p class="copyright">Copyright © 2010 Internet Systems Consortium, Inc.</p></div><div><div class="abstract" title="Abstract"><p class="title"><b>Abstract</b></p><p>This is the reference guide for BIND 10.</p><p>
-        The most up-to-date version of this document, along with other documents
-        for BIND 10, can be found at
-        <a class="ulink" href="http://bind10.isc.org/docs" target="_top">http://bind10.isc.org/docs</a>.
-      </p></div></div></div><hr></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="chapter"><a href="#intro">1. Introduction</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230299028">Supported Platforms</a></span></dt><dt><span class="section"><a href="#id1168230299056">Required Software</a></span></dt><dt><span class="section"><a href="#starting_stopping">Starting and Stopping the Server</a></span></dt><dt><span class="section"><a href="#managing_once_running">Managing BIND 10</a></span></dt></dl></dd><dt><span class="chapter"><a href="#installation">2. Installation</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230284542">Building Requirements</a></span></dt><dt><span class="section"><a href="#quickstart">Quick start</a></span></dt><dt><span class="section"><a href="#install">Installation from source</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230284728">Download Tar File</a></span></dt><dt><s
 pan class="section"><a href="#id1168230284748">Retrieve from Subversion</a></span></dt><dt><span class="section"><a href="#id1168230284809">Configure before the build</a></span></dt><dt><span class="section"><a href="#id1168230284906">Build</a></span></dt><dt><span class="section"><a href="#id1168230284921">Install</a></span></dt><dt><span class="section"><a href="#id1168230284946">Install Hierarchy</a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="#bind10">3. Starting BIND10 with <span class="command"><strong>bind10</strong></span></a></span></dt><dd><dl><dt><span class="section"><a href="#start">Starting BIND 10</a></span></dt></dl></dd><dt><span class="chapter"><a href="#msgq">4. Command channel</a></span></dt><dt><span class="chapter"><a href="#cfgmgr">5. Configuration manager</a></span></dt><dt><span class="chapter"><a href="#cmdctl">6. Remote control daemon</a></span></dt><dd><dl><dt><span class="section"><a href="#cmdctl.spec">Configuration specifi
 cation for b10-cmdctl</a></span></dt></dl></dd><dt><span class="chapter"><a href="#bindctl">7. Control and configure user interface</a></span></dt><dt><span class="chapter"><a href="#authserver">8. Authoritative Server</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230285515">Server Configurations</a></span></dt><dt><span class="section"><a href="#id1168230285580">Data Source Backends</a></span></dt><dt><span class="section"><a href="#id1168230285610">Loading Master Zones Files</a></span></dt></dl></dd><dt><span class="chapter"><a href="#xfrin">9. Incoming Zone Transfers</a></span></dt><dt><span class="chapter"><a href="#xfrout">10. Outbound Zone Transfers</a></span></dt><dt><span class="chapter"><a href="#zonemgr">11. Secondary Manager</a></span></dt></dl></div><div class="chapter" title="Chapter 1. Introduction"><div class="titlepage"><div><div><h2 class="title"><a name="intro"></a>Chapter 1. Introduction</h2></div></div></div><div class="toc"><p><b>Table
  of Contents</b></p><dl><dt><span class="section"><a href="#id1168230299028">Supported Platforms</a></span></dt><dt><span class="section"><a href="#id1168230299056">Required Software</a></span></dt><dt><span class="section"><a href="#starting_stopping">Starting and Stopping the Server</a></span></dt><dt><span class="section"><a href="#managing_once_running">Managing BIND 10</a></span></dt></dl></div><p>
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>BIND 10 Guide</title><link rel="stylesheet" href="./bind10-guide.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.75.2"><meta name="description" content="BIND 10 is a Domain Name System (DNS) suite managed by Internet Systems Consortium (ISC). It includes DNS libraries and modular components for controlling authoritative and recursive DNS servers. This is the reference guide for BIND 10 version 20110120. The most up-to-date version of this document, along with other documents for BIND 10, can be found at ."></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="book" title="BIND 10 Guide"><div class="titlepage"><div><div><h1 class="title"><a name="id1168230298903"></a>BIND 10 Guide</h1></div><div><h2 class="subtitle">Administrator Reference for BIND 10</h2></div><div><p class="releaseinfo">This is the referenc
 e guide for BIND 10 version
+        20110120.</p></div><div><p class="copyright">Copyright © 2010 Internet Systems Consortium, Inc.</p></div><div><div class="abstract" title="Abstract"><p class="title"><b>Abstract</b></p><p>BIND 10 is a Domain Name System (DNS) suite managed by
+	Internet Systems Consortium (ISC). It includes DNS libraries
+	and modular components for controlling authoritative and
+	recursive DNS servers.
+      </p><p>
+        This is the reference guide for BIND 10 version 20110120.
+	The most up-to-date version of this document, along with
+	other documents for BIND 10, can be found at <a class="ulink" href="http://bind10.isc.org/docs" target="_top">http://bind10.isc.org/docs</a>.  </p></div></div></div><hr></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="chapter"><a href="#intro">1. Introduction</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230299042">Supported Platforms</a></span></dt><dt><span class="section"><a href="#id1168230299068">Required Software</a></span></dt><dt><span class="section"><a href="#starting_stopping">Starting and Stopping the Server</a></span></dt><dt><span class="section"><a href="#managing_once_running">Managing BIND 10</a></span></dt></dl></dd><dt><span class="chapter"><a href="#installation">2. Installation</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230284843">Building Requirements</a></span></dt><dt><span class="section"><a href="#quickstart">Quick start</a></span></dt><dt><span class="section"><a href="#install">In
 stallation from source</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230285029">Download Tar File</a></span></dt><dt><span class="section"><a href="#id1168230285048">Retrieve from Git</a></span></dt><dt><span class="section"><a href="#id1168230285109">Configure before the build</a></span></dt><dt><span class="section"><a href="#id1168230285206">Build</a></span></dt><dt><span class="section"><a href="#id1168230285222">Install</a></span></dt><dt><span class="section"><a href="#id1168230285245">Install Hierarchy</a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="#bind10">3. Starting BIND10 with <span class="command"><strong>bind10</strong></span></a></span></dt><dd><dl><dt><span class="section"><a href="#start">Starting BIND 10</a></span></dt></dl></dd><dt><span class="chapter"><a href="#msgq">4. Command channel</a></span></dt><dt><span class="chapter"><a href="#cfgmgr">5. Configuration manager</a></span></dt><dt><span class="chapter"><a hr
 ef="#cmdctl">6. Remote control daemon</a></span></dt><dd><dl><dt><span class="section"><a href="#cmdctl.spec">Configuration specification for b10-cmdctl</a></span></dt></dl></dd><dt><span class="chapter"><a href="#bindctl">7. Control and configure user interface</a></span></dt><dt><span class="chapter"><a href="#authserver">8. Authoritative Server</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230285821">Server Configurations</a></span></dt><dt><span class="section"><a href="#id1168230285886">Data Source Backends</a></span></dt><dt><span class="section"><a href="#id1168230285917">Loading Master Zones Files</a></span></dt></dl></dd><dt><span class="chapter"><a href="#xfrin">9. Incoming Zone Transfers</a></span></dt><dt><span class="chapter"><a href="#xfrout">10. Outbound Zone Transfers</a></span></dt><dt><span class="chapter"><a href="#zonemgr">11. Secondary Manager</a></span></dt><dt><span class="chapter"><a href="#resolverserver">12. Recursive Name Server<
 /a></span></dt><dt><span class="chapter"><a href="#statistics">13. Statistics</a></span></dt></dl></div><div class="chapter" title="Chapter 1. Introduction"><div class="titlepage"><div><div><h2 class="title"><a name="intro"></a>Chapter 1. Introduction</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id1168230299042">Supported Platforms</a></span></dt><dt><span class="section"><a href="#id1168230299068">Required Software</a></span></dt><dt><span class="section"><a href="#starting_stopping">Starting and Stopping the Server</a></span></dt><dt><span class="section"><a href="#managing_once_running">Managing BIND 10</a></span></dt></dl></div><p>
       BIND is the popular implementation of a DNS server, developer
       interfaces, and DNS tools.
       BIND 10 is a rewrite of BIND 9.  BIND 10 is written in C++ and Python
       and provides a modular environment for serving and maintaining DNS.
     </p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
-        This guide covers the experimental prototype version of
-        BIND 10.
+        This guide covers the experimental prototype of
+        BIND 10 version 20110120.
       </p></div><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
-        BIND 10, at this time, does not provide a recursive
-        DNS server. It does provide a EDNS0- and DNSSEC-capable
-        authoritative DNS server.
-      </p></div><div class="section" title="Supported Platforms"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230299028"></a>Supported Platforms</h2></div></div></div><p>
+        BIND 10 provides a EDNS0- and DNSSEC-capable
+        authoritative DNS server and a forwarding DNS server.
+      </p></div><div class="section" title="Supported Platforms"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230299042"></a>Supported Platforms</h2></div></div></div><p>
   BIND 10 builds have been tested on Debian GNU/Linux 5,
-  Ubuntu 9.10, NetBSD 5, Solaris 10, FreeBSD 7, and CentOS
+  Ubuntu 9.10, NetBSD 5, Solaris 10, FreeBSD 7 and 8, and CentOS
   Linux 5.3.
 
   It has been tested on Sparc, i386, and amd64 hardware
@@ -24,7 +27,7 @@
 
         It is planned for BIND 10 to build, install and run on
         Windows and standard Unix-type platforms.
-      </p></div><div class="section" title="Required Software"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230299056"></a>Required Software</h2></div></div></div><p>
+      </p></div><div class="section" title="Required Software"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230299068"></a>Required Software</h2></div></div></div><p>
         BIND 10 requires Python 3.1.  Later versions may work, but Python
         3.1 is the minimum version which will work.
       </p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
@@ -73,6 +76,15 @@
               Command and control service.
               This process allows external control of the BIND 10 system.
             </li><li class="listitem">
+              <span class="command"><strong>b10-resolver</strong></span> —
+              Recursive name server.
+              This process handles incoming queries.
+
+            </li><li class="listitem">
+              <span class="command"><strong>b10-stats</strong></span> —
+              Statistics collection daemon.
+              This process collects and reports statistics data.
+            </li><li class="listitem">
               <span class="command"><strong>b10-xfrin</strong></span> —
               Incoming zone transfer service.
               This process is used to transfer a new copy
@@ -121,7 +133,7 @@
       and, of course, DNS. These include detailed developer
       documentation and code examples.
 
-    </p></div><div class="chapter" title="Chapter 2. Installation"><div class="titlepage"><div><div><h2 class="title"><a name="installation"></a>Chapter 2. Installation</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id1168230284542">Building Requirements</a></span></dt><dt><span class="section"><a href="#quickstart">Quick start</a></span></dt><dt><span class="section"><a href="#install">Installation from source</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230284728">Download Tar File</a></span></dt><dt><span class="section"><a href="#id1168230284748">Retrieve from Subversion</a></span></dt><dt><span class="section"><a href="#id1168230284809">Configure before the build</a></span></dt><dt><span class="section"><a href="#id1168230284906">Build</a></span></dt><dt><span class="section"><a href="#id1168230284921">Install</a></span></dt><dt><span class="section"><a href="#id1168230284946">Install Hie
 rarchy</a></span></dt></dl></dd></dl></div><div class="section" title="Building Requirements"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230284542"></a>Building Requirements</h2></div></div></div><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
+    </p></div><div class="chapter" title="Chapter 2. Installation"><div class="titlepage"><div><div><h2 class="title"><a name="installation"></a>Chapter 2. Installation</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id1168230284843">Building Requirements</a></span></dt><dt><span class="section"><a href="#quickstart">Quick start</a></span></dt><dt><span class="section"><a href="#install">Installation from source</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230285029">Download Tar File</a></span></dt><dt><span class="section"><a href="#id1168230285048">Retrieve from Git</a></span></dt><dt><span class="section"><a href="#id1168230285109">Configure before the build</a></span></dt><dt><span class="section"><a href="#id1168230285206">Build</a></span></dt><dt><span class="section"><a href="#id1168230285222">Install</a></span></dt><dt><span class="section"><a href="#id1168230285245">Install Hierarchy<
 /a></span></dt></dl></dd></dl></div><div class="section" title="Building Requirements"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230284843"></a>Building Requirements</h2></div></div></div><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
             Some operating systems have split their distribution packages into
             a run-time and a development package.  You will need to install
             the development package versions, which include header files and
@@ -178,35 +190,37 @@
           </li></ol></div></div><div class="section" title="Installation from source"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="install"></a>Installation from source</h2></div></div></div><p>
         BIND 10 is open source software written in C++ and Python.
         It is freely available in source code form from ISC via
-        the Subversion code revision control system or as a downloadable
+        the Git code revision control system or as a downloadable
         tar file. It may also be available in pre-compiled ready-to-use
         packages from operating system vendors.
-      </p><div class="section" title="Download Tar File"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230284728"></a>Download Tar File</h3></div></div></div><p>
+      </p><div class="section" title="Download Tar File"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230285029"></a>Download Tar File</h3></div></div></div><p>
           Downloading a release tar file is the recommended method to
           obtain the source code.
         </p><p>
           The BIND 10 releases are available as tar file downloads from
           <a class="ulink" href="ftp://ftp.isc.org/isc/bind10/" target="_top">ftp://ftp.isc.org/isc/bind10/</a>.
           Periodic development snapshots may also be available.
-        </p></div><div class="section" title="Retrieve from Subversion"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230284748"></a>Retrieve from Subversion</h3></div></div></div><p>
+        </p></div><div class="section" title="Retrieve from Git"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230285048"></a>Retrieve from Git</h3></div></div></div><p>
           Downloading this "bleeding edge" code is recommended only for
           developers or advanced users.  Using development code in a production
           environment is not recommended.
         </p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
-            When using source code retrieved via Subversion additional
+            When using source code retrieved via Git additional
             software will be required:  automake (v1.11 or newer),
             libtoolize, and autoconf (2.59 or newer).
             These may need to be installed.
           </p></div><p>
           The latest development code, including temporary experiments
           and un-reviewed code, is available via the BIND 10 code revision
-          control system. This is powered by Subversion and all the BIND 10
+          control system. This is powered by Git and all the BIND 10
           development is public.
-          The leading development is done in the <span class="quote">“<span class="quote">trunk</span>”</span>.
+          The leading development is done in the <span class="quote">“<span class="quote">master</span>”</span>.
         </p><p>
-          The code can be checked out from <code class="filename">svn://bind10.isc.org/svn/bind10</code>; for example to check out the trunk:
+          The code can be checked out from
+          <code class="filename">git://bind10.isc.org/bind10</code>;
+          for example:
 
-        </p><pre class="screen">$ <strong class="userinput"><code>svn co svn://bind10.isc.org/svn/bind10/trunk</code></strong></pre><p>
+        </p><pre class="screen">$ <strong class="userinput"><code>git clone git://bind10.isc.org/bind10</code></strong></pre><p>
         </p><p>
           When checking out the code from
           the code version control system, it doesn't include the
@@ -220,7 +234,7 @@
           <span class="command"><strong>autoheader</strong></span>,
           <span class="command"><strong>automake</strong></span>,
           and related commands.
-        </p></div><div class="section" title="Configure before the build"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230284809"></a>Configure before the build</h3></div></div></div><p>
+        </p></div><div class="section" title="Configure before the build"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230285109"></a>Configure before the build</h3></div></div></div><p>
           BIND 10 uses the GNU Build System to discover build environment
           details.
           To generate the makefiles using the defaults, simply run:
@@ -251,16 +265,16 @@
         </p><p>
           If the configure fails, it may be due to missing or old
           dependencies.
-        </p></div><div class="section" title="Build"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230284906"></a>Build</h3></div></div></div><p>
+        </p></div><div class="section" title="Build"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230285206"></a>Build</h3></div></div></div><p>
     After the configure step is complete, to build the executables
     from the C++ code and prepare the Python scripts, run:
 
           </p><pre class="screen">$ <strong class="userinput"><code>make</code></strong></pre><p>
-        </p></div><div class="section" title="Install"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230284921"></a>Install</h3></div></div></div><p>
+        </p></div><div class="section" title="Install"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230285222"></a>Install</h3></div></div></div><p>
           To install the BIND 10 executables, support files,
           and documentation, run:
           </p><pre class="screen">$ <strong class="userinput"><code>make install</code></strong></pre><p>
-        </p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>The install step may require superuser privileges.</p></div></div><div class="section" title="Install Hierarchy"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230284946"></a>Install Hierarchy</h3></div></div></div><p>
+        </p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>The install step may require superuser privileges.</p></div></div><div class="section" title="Install Hierarchy"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230285245"></a>Install Hierarchy</h3></div></div></div><p>
           The following is the layout of the complete BIND 10 installation:
           </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem">
                 <code class="filename">bin/</code> —
@@ -311,7 +325,9 @@
       about other modules.
       The <span class="command"><strong>bind10</strong></span> master process will also start up
       <span class="command"><strong>b10-cmdctl</strong></span> for admins to communicate with the
-      system, <span class="command"><strong>b10-auth</strong></span> for Authoritative DNS service,
+      system, <span class="command"><strong>b10-auth</strong></span> for authoritative DNS service or
+      <span class="command"><strong>b10-resolver</strong></span> for recursive name service,
+      <span class="command"><strong>b10-stats</strong></span> for statistics collection,
       <span class="command"><strong>b10-xfrin</strong></span> for inbound DNS zone transfers,
       <span class="command"><strong>b10-xfrout</strong></span> for outbound DNS zone transfers,
       and <span class="command"><strong>b10-zonemgr</strong></span> for secondary service.
@@ -473,7 +489,7 @@ shutdown
       the details and relays (over a <span class="command"><strong>b10-msgq</strong></span> command
       channel) the configuration on to the specified module.
     </p><p>
-    </p></div><div class="chapter" title="Chapter 8. Authoritative Server"><div class="titlepage"><div><div><h2 class="title"><a name="authserver"></a>Chapter 8. Authoritative Server</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id1168230285515">Server Configurations</a></span></dt><dt><span class="section"><a href="#id1168230285580">Data Source Backends</a></span></dt><dt><span class="section"><a href="#id1168230285610">Loading Master Zones Files</a></span></dt></dl></div><p>
+    </p></div><div class="chapter" title="Chapter 8. Authoritative Server"><div class="titlepage"><div><div><h2 class="title"><a name="authserver"></a>Chapter 8. Authoritative Server</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id1168230285821">Server Configurations</a></span></dt><dt><span class="section"><a href="#id1168230285886">Data Source Backends</a></span></dt><dt><span class="section"><a href="#id1168230285917">Loading Master Zones Files</a></span></dt></dl></div><p>
       The <span class="command"><strong>b10-auth</strong></span> is the authoritative DNS server.
       It supports EDNS0 and DNSSEC. It supports IPv6.
       Normally it is started by the <span class="command"><strong>bind10</strong></span> master
@@ -481,7 +497,7 @@ shutdown
     </p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
       This development prototype release listens on all interfaces
       and the non-standard port 5300.
-    </p></div><div class="section" title="Server Configurations"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230285515"></a>Server Configurations</h2></div></div></div><p>
+    </p></div><div class="section" title="Server Configurations"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230285821"></a>Server Configurations</h2></div></div></div><p>
         <span class="command"><strong>b10-auth</strong></span> is configured via the
         <span class="command"><strong>b10-cfgmgr</strong></span> configuration manager.
         The module name is <span class="quote">“<span class="quote">Auth</span>”</span>.
@@ -501,7 +517,7 @@ This may be a temporary setting until then.
         </p><div class="variablelist"><dl><dt><span class="term">shutdown</span></dt><dd>Stop the authoritative DNS server.
               </dd></dl></div><p>
 
-      </p></div><div class="section" title="Data Source Backends"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230285580"></a>Data Source Backends</h2></div></div></div><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
+      </p></div><div class="section" title="Data Source Backends"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230285886"></a>Data Source Backends</h2></div></div></div><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
         For the development prototype release, <span class="command"><strong>b10-auth</strong></span>
         only supports the SQLite3 data source backend.
         Upcoming versions will be able to use multiple different
@@ -514,7 +530,7 @@ This may be a temporary setting until then.
         The default is <code class="filename">/usr/local/var/</code>.)
   This data file location may be changed by defining the
   <span class="quote">“<span class="quote">database_file</span>”</span> configuration.
-      </p></div><div class="section" title="Loading Master Zones Files"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230285610"></a>Loading Master Zones Files</h2></div></div></div><p>
+      </p></div><div class="section" title="Loading Master Zones Files"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230285917"></a>Loading Master Zones Files</h2></div></div></div><p>
         RFC 1035 style DNS master zone files may imported
         into a BIND 10 data source by using the
         <span class="command"><strong>b10-loadzone</strong></span> utility.
@@ -590,4 +606,76 @@ This may be a temporary setting until then.
     </p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
      Access control (such as allowing notifies) is not yet provided.
      The primary/secondary service is not yet complete.
-    </p></div></div></div></body></html>
+    </p></div></div><div class="chapter" title="Chapter 12. Recursive Name Server"><div class="titlepage"><div><div><h2 class="title"><a name="resolverserver"></a>Chapter 12. Recursive Name Server</h2></div></div></div><p>
+      The <span class="command"><strong>b10-resolver</strong></span> process is started by
+      <span class="command"><strong>bind10</strong></span>.
+
+    </p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
+      The current version only provides a forwarding DNS server.
+      It does not cache and does not iterate to find answers.
+      It simply forwards the query on to another full resolver.
+    </p></div><p>
+      The main <span class="command"><strong>bind10</strong></span> process can be configured
+      to select to run either the authoritative or resolver.
+      By default, it starts the authoritative service.
+
+
+      You may change this using <span class="command"><strong>bindctl</strong></span>, for example:
+
+      </p><pre class="screen">
+> <strong class="userinput"><code>config set Boss/start_auth false</code></strong>
+> <strong class="userinput"><code>config set Boss/start_resolver true</code></strong>
+> <strong class="userinput"><code>config commit</code></strong>
+</pre><p>
+
+    </p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
+       In the current version, the master <span class="command"><strong>bind10</strong></span>
+       process must be stopped and restarted to start up the resolver.
+    </p></div><p>
+      Then the upstream address and port must be configured to
+      forward queries to, such as:
+
+      </p><pre class="screen">
+> <strong class="userinput"><code>config set Resolver/forward_addresses [{ "address": "<em class="replaceable"><code>192.168.1.1</code></em>", "port": 53 }]</code></strong>
+> <strong class="userinput"><code>config commit</code></strong>
+</pre><p>
+
+      (Replace <em class="replaceable"><code>192.168.1.1</code></em> to point to your
+      full resolver.)
+    </p><p>
+      The resolver also needs to be configured to listen on an address
+      and port:
+
+      </p><pre class="screen">
+> <strong class="userinput"><code>config set Resolver/listen_on [{ "address": "127.0.0.1", "port": 53 }]</code></strong>
+> <strong class="userinput"><code>config commit</code></strong>
+</pre><p>
+    </p></div><div class="chapter" title="Chapter 13. Statistics"><div class="titlepage"><div><div><h2 class="title"><a name="statistics"></a>Chapter 13. Statistics</h2></div></div></div><p>
+      The <span class="command"><strong>b10-stats</strong></span> process is started by
+      <span class="command"><strong>bind10</strong></span>.
+      It periodically collects statistics data from various modules
+      and aggregates it.
+
+    </p><p>
+
+       This stats daemon provides commands to identify if it is running,
+       show specified or all statistics data, set values, remove data,
+       and reset data.
+
+       For example, using <span class="command"><strong>bindctl</strong></span>:
+
+       </p><pre class="screen">
+> <strong class="userinput"><code>Stats show</code></strong>
+{
+    "auth.queries.tcp": 1749,
+    "auth.queries.udp": 867868,
+    "bind10.boot_time": "2011-01-20T16:59:03Z",
+    "report_time": "2011-01-20T17:04:06Z",
+    "stats.boot_time": "2011-01-20T16:59:05Z",
+    "stats.last_update_time": "2011-01-20T17:04:05Z",
+    "stats.lname": "4d3869d9_a at jreed.example.net",
+    "stats.start_time": "2011-01-20T16:59:05Z",
+    "stats.timestamp": 1295543046.823504
+}
+       </pre><p>
+    </p></div></div></body></html>
diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml
index fbf4cb4..3670c46 100644
--- a/doc/guide/bind10-guide.xml
+++ b/doc/guide/bind10-guide.xml
@@ -2,6 +2,8 @@
 <!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
 "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
 <!ENTITY mdash  "&#x2014;" >
+<!ENTITY % version SYSTEM "version.ent">
+%version;
 ]>
 <book>
   <?xml-stylesheet href="bind10-guide.css" type="text/css"?>
@@ -15,13 +17,20 @@
     </copyright>
 
     <abstract>
-      <para>This is the reference guide for BIND 10.</para>
-      <para>
-        The most up-to-date version of this document, along with other documents
-        for BIND 10, can be found at
-        <ulink url="http://bind10.isc.org/docs"/>.
+      <para>BIND 10 is a Domain Name System (DNS) suite managed by
+	Internet Systems Consortium (ISC). It includes DNS libraries
+	and modular components for controlling authoritative and
+	recursive DNS servers.
       </para>
-    </abstract>
+      <para>
+        This is the reference guide for BIND 10 version &__VERSION__;.
+	The most up-to-date version of this document, along with
+	other documents for BIND 10, can be found at <ulink
+	url="http://bind10.isc.org/docs"/>.  </para> </abstract>
+
+      <releaseinfo>This is the reference guide for BIND 10 version
+        &__VERSION__;.</releaseinfo>
+
   </bookinfo>
 
   <chapter id="intro">
@@ -35,8 +44,8 @@
 
     <note>
       <para>
-        This guide covers the experimental prototype version of
-        BIND 10.
+        This guide covers the experimental prototype of
+        BIND 10 version &__VERSION__;.
       </para>
     </note>
 
@@ -51,7 +60,7 @@
       <title>Supported Platforms</title>
       <para>
   BIND 10 builds have been tested on Debian GNU/Linux 5,
-  Ubuntu 9.10, NetBSD 5, Solaris 10, FreeBSD 7, and CentOS
+  Ubuntu 9.10, NetBSD 5, Solaris 10, FreeBSD 7 and 8, and CentOS
   Linux 5.3.
 
   It has been tested on Sparc, i386, and amd64 hardware
@@ -115,6 +124,7 @@
       <para>
 
         <itemizedlist>
+
           <listitem>
             <simpara>
               <command>b10-msgq</command> —
@@ -123,6 +133,7 @@
               BIND 10 processes.
             </simpara>
           </listitem>
+
           <listitem>
             <simpara>
               <command>b10-auth</command> —
@@ -130,6 +141,7 @@
               This process serves DNS requests.
             </simpara>
           </listitem>
+
           <listitem>
             <simpara>
               <command>b10-cfgmgr</command> —
@@ -137,6 +149,7 @@
               This process maintains all of the configuration for BIND 10.
             </simpara>
           </listitem>
+
           <listitem>
             <simpara>
               <command>b10-cmdctl</command> —
@@ -147,6 +160,23 @@
 
           <listitem>
             <simpara>
+              <command>b10-resolver</command> —
+              Recursive name server.
+              This process handles incoming queries.
+<!-- TODO: -->
+            </simpara>
+          </listitem>
+
+          <listitem>
+            <simpara>
+              <command>b10-stats</command> —
+              Statistics collection daemon.
+              This process collects and reports statistics data.
+            </simpara>
+          </listitem>
+
+          <listitem>
+            <simpara>
               <command>b10-xfrin</command> —
               Incoming zone transfer service.
               This process is used to transfer a new copy
@@ -393,7 +423,7 @@ var/
       <para>
         BIND 10 is open source software written in C++ and Python.
         It is freely available in source code form from ISC via
-        the Subversion code revision control system or as a downloadable
+        the Git code revision control system or as a downloadable
         tar file. It may also be available in pre-compiled ready-to-use
         packages from operating system vendors.
       </para>
@@ -414,7 +444,7 @@ var/
       </section>
 
       <section>
-        <title>Retrieve from Subversion</title>
+        <title>Retrieve from Git</title>
         <para>
           Downloading this "bleeding edge" code is recommended only for
           developers or advanced users.  Using development code in a production
@@ -423,7 +453,7 @@ var/
 
         <note>
           <para>
-            When using source code retrieved via Subversion additional
+            When using source code retrieved via Git additional
             software will be required:  automake (v1.11 or newer),
             libtoolize, and autoconf (2.59 or newer).
             These may need to be installed.
@@ -433,14 +463,16 @@ var/
         <para>
           The latest development code, including temporary experiments
           and un-reviewed code, is available via the BIND 10 code revision
-          control system. This is powered by Subversion and all the BIND 10
+          control system. This is powered by Git and all the BIND 10
           development is public.
-          The leading development is done in the <quote>trunk</quote>.
+          The leading development is done in the <quote>master</quote>.
         </para>
         <para>
-          The code can be checked out from <filename>svn://bind10.isc.org/svn/bind10</filename>; for example to check out the trunk:
+          The code can be checked out from
+          <filename>git://bind10.isc.org/bind10</filename>;
+          for example:
 
-        <screen>$ <userinput>svn co svn://bind10.isc.org/svn/bind10/trunk</userinput></screen>
+        <screen>$ <userinput>git clone git://bind10.isc.org/bind10</userinput></screen>
         </para>
 
         <para>
@@ -657,7 +689,9 @@ var/
       about other modules.
       The <command>bind10</command> master process will also start up
       <command>b10-cmdctl</command> for admins to communicate with the
-      system, <command>b10-auth</command> for Authoritative DNS service,
+      system, <command>b10-auth</command> for authoritative DNS service or
+      <command>b10-resolver</command> for recursive name service,
+      <command>b10-stats</command> for statistics collection,
       <command>b10-xfrin</command> for inbound DNS zone transfers,
       <command>b10-xfrout</command> for outbound DNS zone transfers,
       and <command>b10-zonemgr</command> for secondary service.
@@ -948,6 +982,8 @@ accounts_file
       <para>
         The control commands are:
 print_settings
+<!-- TODO: remove that -->
+
 shutdown
       </para>
 <!-- TODO -->
@@ -1261,6 +1297,118 @@ what is XfroutClient xfr_client??
 
   </chapter>
 
+  <chapter id="resolverserver">
+    <title>Recursive Name Server</title>
+
+    <para>
+      The <command>b10-resolver</command> process is started by
+      <command>bind10</command>.
+<!-- TODO
+      It provides a resolver so DNS clients can ask it to do recursion
+      and it will return answers.
+-->
+    </para>
+
+    <note><simpara>
+      The current version only provides a forwarding DNS server.
+      It does not cache and does not iterate to find answers.
+      It simply forwards the query on to another full resolver.
+    </simpara></note>
+
+    <para>
+      The main <command>bind10</command> process can be configured
+      to select to run either the authoritative or resolver.
+      By default, it starts the authoritative service.
+<!-- TODO: later both -->
+
+      You may change this using <command>bindctl</command>, for example:
+
+      <screen>
+> <userinput>config set Boss/start_auth false</userinput>
+> <userinput>config set Boss/start_resolver true</userinput>
+> <userinput>config commit</userinput>
+</screen>
+
+    </para>
+
+<!-- TODO: -->
+    <note><simpara>
+       In the current version, the master <command>bind10</command>
+       process must be stopped and restarted to start up the resolver.
+    </simpara></note>
+
+    <para>
+      Then the upstream address and port must be configured to
+      forward queries to, such as:
+
+      <screen>
+> <userinput>config set Resolver/forward_addresses [{ "address": "<replaceable>192.168.1.1</replaceable>", "port": 53 }]</userinput>
+> <userinput>config commit</userinput>
+</screen>
+
+      (Replace <replaceable>192.168.1.1</replaceable> to point to your
+      full resolver.)
+    </para>
+
+    <para>
+      The resolver also needs to be configured to listen on an address
+      and port:
+
+      <screen>
+> <userinput>config set Resolver/listen_on [{ "address": "127.0.0.1", "port": 53 }]</userinput>
+> <userinput>config commit</userinput>
+</screen>
+    </para>
+
+<!-- TODO: later the above will have some defaults -->
+
+<!-- TODO: later try this
+
+> config set Resolver/forward_addresses[0]/address "192.168.8.8"
+> config set Resolver/forward_addresses[0]/port 53
+then change those defaults with config set Resolver/forward_addresses[0]/address "1.2.3.4"
+> config set Resolver/forward_addresses[0]/address "1.2.3.4"
+-->
+
+  </chapter>
+
+  <chapter id="statistics">
+    <title>Statistics</title>
+
+    <para>
+      The <command>b10-stats</command> process is started by
+      <command>bind10</command>.
+      It periodically collects statistics data from various modules
+      and aggregates it.
+<!-- TODO -->
+    </para>
+
+    <para>
+
+       This stats daemon provides commands to identify if it is running,
+       show specified or all statistics data, set values, remove data,
+       and reset data.
+
+       For example, using <command>bindctl</command>:
+
+       <screen>
+> <userinput>Stats show</userinput>
+{
+    "auth.queries.tcp": 1749,
+    "auth.queries.udp": 867868,
+    "bind10.boot_time": "2011-01-20T16:59:03Z",
+    "report_time": "2011-01-20T17:04:06Z",
+    "stats.boot_time": "2011-01-20T16:59:05Z",
+    "stats.last_update_time": "2011-01-20T17:04:05Z",
+    "stats.lname": "4d3869d9_a at jreed.example.net",
+    "stats.start_time": "2011-01-20T16:59:05Z",
+    "stats.timestamp": 1295543046.823504
+}
+       </screen>
+    </para>
+
+  </chapter>
+
 <!-- TODO: how to help: run unit tests, join lists, review trac tickets -->
 
   <!-- <index>    <title>Index</title> </index> -->
diff --git a/doc/version.ent.in b/doc/version.ent.in
new file mode 100644
index 0000000..9a72f7e
--- /dev/null
+++ b/doc/version.ent.in
@@ -0,0 +1 @@
+<!ENTITY __VERSION__ "@PACKAGE_VERSION@">
diff --git a/src/bin/auth/auth_srv.cc b/src/bin/auth/auth_srv.cc
index d8ecea5..045fe7f 100644
--- a/src/bin/auth/auth_srv.cc
+++ b/src/bin/auth/auth_srv.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <config.h>
 
 #include <netinet/in.h>
@@ -161,9 +159,13 @@ AuthSrvImpl::~AuthSrvImpl() {
 class MessageLookup : public DNSLookup {
 public:
     MessageLookup(AuthSrv* srv) : server_(srv) {}
-    virtual void operator()(const IOMessage& io_message, MessagePtr message,
-                            OutputBufferPtr buffer, DNSServer* server) const
+    virtual void operator()(const IOMessage& io_message,
+                            MessagePtr message,
+                            MessagePtr answer_message,
+                            OutputBufferPtr buffer,
+                            DNSServer* server) const
     {
+        (void) answer_message;
         server_->processMessage(io_message, message, buffer, server);
     }
 private:
@@ -182,7 +184,7 @@ class MessageAnswer : public DNSAnswer {
 public:
     MessageAnswer(AuthSrv*) {}
     virtual void operator()(const IOMessage&, MessagePtr,
-                            OutputBufferPtr) const
+                            MessagePtr, OutputBufferPtr) const
     {}
 };
 
diff --git a/src/bin/auth/auth_srv.h b/src/bin/auth/auth_srv.h
index 43089b9..4772a02 100644
--- a/src/bin/auth/auth_srv.h
+++ b/src/bin/auth/auth_srv.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __AUTH_SRV_H
 #define __AUTH_SRV_H 1
 
diff --git a/src/bin/auth/b10-auth.8 b/src/bin/auth/b10-auth.8
index 144caf2..bae8e4a 100644
--- a/src/bin/auth/b10-auth.8
+++ b/src/bin/auth/b10-auth.8
@@ -2,12 +2,12 @@
 .\"     Title: b10-auth
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: July 29, 2010
+.\"      Date: January 19, 2011
 .\"    Manual: BIND10
 .\"    Source: BIND10
 .\"  Language: English
 .\"
-.TH "B10\-AUTH" "8" "July 29, 2010" "BIND10" "BIND10"
+.TH "B10\-AUTH" "8" "January 19, 2011" "BIND10" "BIND10"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
@@ -36,27 +36,8 @@ This daemon communicates with other BIND 10 components over a
 C\-Channel connection\&. If this connection is not established,
 \fBb10\-auth\fR
 will exit\&.
-.PP
-It also receives its configurations from
-\fBb10-cfgmgr\fR(8)\&. It will honor the
-\fIdatabase_file\fR
-configuration to point to the SQLite3 zone file\&.
-.if n \{\
-.sp
-.\}
-.RS 4
-.it 1 an-trap
-.nr an-no-space-flag 1
-.nr an-break-flag 1
-.br
-.ps +1
-\fBNote\fR
-.ps -1
-.br
-.PP
-This prototype version uses SQLite3 as its data source backend\&. Future versions will be configurable, supporting multiple data storage types\&.
-.sp .5v
-.RE
+It receives its configurations from
+\fBb10-cfgmgr\fR(8)\&.
 .SH "OPTIONS"
 .PP
 The arguments are as follows:
@@ -123,10 +104,102 @@ must be either a valid numeric user ID or a valid user name\&. By default the da
 .RS 4
 Enabled verbose mode\&. This enables diagnostic messages to STDERR\&.
 .RE
+.SH "CONFIGURATION AND COMMANDS"
+.PP
+The configurable settings are:
+.PP
+
+\fIdatabase_file\fR
+defines the path to the SQLite3 zone file when using the sqlite datasource\&. The default is
+/usr/local/var/bind10\-devel/zone\&.sqlite3\&.
+.PP
+
+\fIdatasources\fR
+configures data sources\&. The list items include:
+\fItype\fR
+to optionally choose the data source type (such as
+\(lqmemory\(rq);
+\fIclass\fR
+to optionally select the class (it defaults to
+\(lqIN\(rq); and
+\fIzones\fR
+to define the
+\fIfile\fR
+path name and the
+\fIorigin\fR
+(default domain)\&. By default, this is empty\&.
+.if n \{\
+.sp
+.\}
+.RS 4
+.it 1 an-trap
+.nr an-no-space-flag 1
+.nr an-break-flag 1
+.br
+.ps +1
+\fBNote\fR
+.ps -1
+.br
+.sp
+In this development version, currently this is only used for the memory data source\&. Only the IN class is supported at this time\&. By default, the memory data source is disabled\&. Also, currently the zone file must be canonical such as generated by \fBnamed\-compilezone \-D\fR\&.
+.sp .5v
+.RE
+.PP
+
+\fIstatistics\-interval\fR
+is the timer interval in seconds for
+\fBb10\-auth\fR
+to share its statistics information to
+\fBb10-stats\fR(8)\&. Statistics updates can be disabled by setting this to 0\&. The default is 60\&.
+.PP
+The configuration commands are:
+.PP
+
+\fBloadzone\fR
+tells
+\fBb10\-auth\fR
+to load or reload a zone file\&. The arguments include:
+\fIclass\fR
+which optionally defines the class (it defaults to
+\(lqIN\(rq);
+\fIorigin\fR
+is the domain name of the zone; and
+\fIdatasrc\fR
+optionally defines the type of datasource (it defaults to
+\(lqmemory\(rq)\&.
+.if n \{\
+.sp
+.\}
+.RS 4
+.it 1 an-trap
+.nr an-no-space-flag 1
+.nr an-break-flag 1
+.br
+.ps +1
+\fBNote\fR
+.ps -1
+.br
+.sp
+In this development version, currently this only supports the IN class and the memory data source\&.
+.sp .5v
+.RE
+.PP
+
+\fBsendstats\fR
+tells
+\fBb10\-auth\fR
+to send its statistics data to
+\fBb10-stats\fR(8)
+immediately\&.
+.PP
+
+\fBshutdown\fR
+exits
+\fBb10\-auth\fR\&. (Note that the BIND 10 boss process will restart this service\&.)
 .SH "FILES"
 .PP
 
-/usr/local/var/db/zone\&.sqlite3
+/usr/local/var/bind10\-devel/zone\&.sqlite3
 \(em Location for the SQLite3 zone database when
 \fIdatabase_file\fR
 configuration is not defined\&.
@@ -134,9 +207,9 @@ configuration is not defined\&.
 .PP
 
 \fBb10-cfgmgr\fR(8),
-\fBb10-cmdctl\fR(8),
 \fBb10-loadzone\fR(8),
 \fBb10-msgq\fR(8),
+\fBb10-stats\fR(8),
 \fBb10-zonemgr\fR(8),
 \fBbind10\fR(8),
 BIND 10 Guide\&.
diff --git a/src/bin/auth/b10-auth.xml b/src/bin/auth/b10-auth.xml
index 619fd08..b22d24d 100644
--- a/src/bin/auth/b10-auth.xml
+++ b/src/bin/auth/b10-auth.xml
@@ -2,7 +2,7 @@
                "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
 	       [<!ENTITY mdash "—">]>
 <!--
- - Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+ - Copyright (C) 2010-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
@@ -17,11 +17,10 @@
  - PERFORMANCE OF THIS SOFTWARE.
 -->
 
-<!-- $Id$ -->
 <refentry>
 
   <refentryinfo>
-    <date>July 29, 2010</date>
+    <date>January 19, 2011</date>
   </refentryinfo>
 
   <refmeta>
@@ -70,22 +69,13 @@
       C-Channel connection.  If this connection is not established,
       <command>b10-auth</command> will exit.
 <!-- TODO what if msgq connection closes later, will b10-auth exit? -->
-    </para>
-
-    <para>
-      It also receives its configurations from
+      It receives its configurations from
 <citerefentry><refentrytitle>b10-cfgmgr</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
-      It will honor the <emphasis>database_file</emphasis> configuration
-      to point to the SQLite3 zone file.
 
-<!-- TODO: data source -->
     </para>
 
-    <note><para>
-      This prototype version uses SQLite3 as its data source backend.
-      Future versions will be configurable, supporting multiple
-      data storage types.
-    </para></note>
+<!-- TODO: mention xfrin, xfrout, zonemgr ? -->
+
   </refsect1>
 
   <refsect1>
@@ -136,6 +126,7 @@
 	  and negative) in memory for 30 seconds (instead of querying
 	  the data source, such as SQLite3 database, each time).
         </para></listitem>
+<!-- TODO: this is SQLite3 only -->
       </varlistentry>
 
       <varlistentry>
@@ -143,7 +134,7 @@
         <listitem><para>
           The port number it listens on.
           The default is 5300.</para>
-	  <note><simpara>The Y1 prototype runs on all interfaces
+	  <note><simpara>This prototype runs on all interfaces
 	  and on this nonstandard port.</simpara></note>
         </listitem>
       </varlistentry>
@@ -175,14 +166,94 @@
   </refsect1>
 
   <refsect1>
+    <title>CONFIGURATION AND COMMANDS</title>
+    <para>
+      The configurable settings are:
+    </para>
+
+    <para>
+      <varname>database_file</varname> defines the path to the
+      SQLite3 zone file when using the sqlite datasource.
+      The default is
+      <filename>/usr/local/var/bind10-devel/zone.sqlite3</filename>.
+    </para>
+
+    <para>
+      <varname>datasources</varname> configures data sources.
+      The list items include:
+      <varname>type</varname> to optionally choose the data source type
+      (such as <quote>memory</quote>);
+      <varname>class</varname> to optionally select the class
+      (it defaults to <quote>IN</quote>);
+      and
+      <varname>zones</varname> to define the
+      <varname>file</varname> path name and the
+      <varname>origin</varname> (default domain).
+
+      By default, this is empty.
+
+      <note><simpara>
+        In this development version, currently this is only used for the
+        memory data source.
+        Only the IN class is supported at this time.
+        By default, the memory data source is disabled.
+        Also, currently the zone file must be canonical such as
+        generated by <command>named-compilezone -D</command>.
+      </simpara></note>
+    </para>
+
+    <para>
+      <varname>statistics-interval</varname> is the timer interval
+      in seconds for <command>b10-auth</command> to share its
+      statistics information to
+      <citerefentry><refentrytitle>b10-stats</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
+      Statistics updates can be disabled by setting this to 0.
+      The default is 60.
+    </para>
+
+<!-- TODO: formating -->
+    <para>
+      The configuration commands are:
+    </para>
+
+    <para>
+      <command>loadzone</command> tells <command>b10-auth</command>
+      to load or reload a zone file. The arguments include:
+      <varname>class</varname> which optionally defines the class
+      (it defaults to <quote>IN</quote>);
+      <varname>origin</varname> is the domain name of the zone;
+      and
+      <varname>datasrc</varname> optionally defines the type of datasource
+      (it defaults to <quote>memory</quote>).
+
+      <note><simpara>
+        In this development version, currently this only supports the
+        IN class and the memory data source.
+      </simpara></note>
+    </para>
+
+    <para>
+      <command>sendstats</command> tells <command>b10-auth</command>
+      to send its statistics data to
+      <citerefentry><refentrytitle>b10-stats</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+      immediately.
+    </para>
+
+    <para>
+      <command>shutdown</command> exits <command>b10-auth</command>.
+      (Note that the BIND 10 boss process will restart this service.)
+    </para>
+
+  </refsect1>
+
+  <refsect1>
     <title>FILES</title>
     <para>
-      <filename>/usr/local/var/db/zone.sqlite3</filename>
+      <filename>/usr/local/var/bind10-devel/zone.sqlite3</filename>
       — Location for the SQLite3 zone database
       when <emphasis>database_file</emphasis> configuration is not
       defined.
     </para>
-<!-- TODO: this is not correct yet. -->
   </refsect1>
 
   <refsect1>
@@ -192,15 +263,15 @@
         <refentrytitle>b10-cfgmgr</refentrytitle><manvolnum>8</manvolnum>
       </citerefentry>,
       <citerefentry>
-        <refentrytitle>b10-cmdctl</refentrytitle><manvolnum>8</manvolnum>
-      </citerefentry>,
-      <citerefentry>
         <refentrytitle>b10-loadzone</refentrytitle><manvolnum>8</manvolnum>
       </citerefentry>,
       <citerefentry>
         <refentrytitle>b10-msgq</refentrytitle><manvolnum>8</manvolnum>
       </citerefentry>,
       <citerefentry>
+        <refentrytitle>b10-stats</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
+      <citerefentry>
         <refentrytitle>b10-zonemgr</refentrytitle><manvolnum>8</manvolnum>
       </citerefentry>,
       <citerefentry>
diff --git a/src/bin/auth/benchmarks/Makefile.am b/src/bin/auth/benchmarks/Makefile.am
index 165bb4c..05ab754 100644
--- a/src/bin/auth/benchmarks/Makefile.am
+++ b/src/bin/auth/benchmarks/Makefile.am
@@ -20,5 +20,6 @@ query_bench_LDADD += $(top_builddir)/src/lib/datasrc/libdatasrc.la
 query_bench_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
 query_bench_LDADD += $(top_builddir)/src/lib/cc/libcc.la
 query_bench_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
+query_bench_LDADD += $(top_builddir)/src/lib/log/liblog.la
 query_bench_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
 query_bench_LDADD += $(SQLITE_LIBS)
diff --git a/src/bin/auth/benchmarks/query_bench.cc b/src/bin/auth/benchmarks/query_bench.cc
index f68eab2..7f643f3 100644
--- a/src/bin/auth/benchmarks/query_bench.cc
+++ b/src/bin/auth/benchmarks/query_bench.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <stdlib.h>
 
 #include <iostream>
diff --git a/src/bin/auth/change_user.cc b/src/bin/auth/change_user.cc
index 049715a..253b8fb 100644
--- a/src/bin/auth/change_user.cc
+++ b/src/bin/auth/change_user.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <errno.h>
 #include <string.h>
 #include <pwd.h>
diff --git a/src/bin/auth/change_user.h b/src/bin/auth/change_user.h
index 7a18926..e4fc5ee 100644
--- a/src/bin/auth/change_user.h
+++ b/src/bin/auth/change_user.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __CHANGE_USER_H
 #define __CHANGE_USER_H 1
 
diff --git a/src/bin/auth/common.h b/src/bin/auth/common.h
index 5ff2c7e..6af09fb 100644
--- a/src/bin/auth/common.h
+++ b/src/bin/auth/common.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __COMMON_H
 #define __COMMON_H 1
 
diff --git a/src/bin/auth/main.cc b/src/bin/auth/main.cc
index 30c8a48..10e1194 100644
--- a/src/bin/auth/main.cc
+++ b/src/bin/auth/main.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/select.h>
diff --git a/src/bin/auth/query.cc b/src/bin/auth/query.cc
index cb7e3e0..e85c31a 100644
--- a/src/bin/auth/query.cc
+++ b/src/bin/auth/query.cc
@@ -66,7 +66,8 @@ Query::findAddrs(const Zone& zone, const Name& qname,
 
     // Find A rrset
     if (qname_ != qname || qtype_ != RRType::A()) {
-        Zone::FindResult a_result = zone.find(qname, RRType::A(), options);
+        Zone::FindResult a_result = zone.find(qname, RRType::A(), NULL,
+                                              options);
         if (a_result.code == Zone::SUCCESS) {
             response_.addRRset(Message::SECTION_ADDITIONAL,
                     boost::const_pointer_cast<RRset>(a_result.rrset));
@@ -76,7 +77,7 @@ Query::findAddrs(const Zone& zone, const Name& qname,
     // Find AAAA rrset
     if (qname_ != qname || qtype_ != RRType::AAAA()) {
         Zone::FindResult aaaa_result =
-            zone.find(qname, RRType::AAAA(), options);
+            zone.find(qname, RRType::AAAA(), NULL, options);
         if (aaaa_result.code == Zone::SUCCESS) {
             response_.addRRset(Message::SECTION_ADDITIONAL,
                     boost::const_pointer_cast<RRset>(aaaa_result.rrset));
@@ -121,6 +122,8 @@ Query::getAuthAdditional(const Zone& zone) const {
 void
 Query::process() const {
     bool keep_doing = true;
+    const bool qtype_is_any = (qtype_ == RRType::ANY());
+
     response_.setHeaderFlag(Message::HEADERFLAG_AA, false);
     const MemoryDataSrc::FindResult result =
         memory_datasrc_.findZone(qname_);
@@ -140,20 +143,41 @@ Query::process() const {
     response_.setHeaderFlag(Message::HEADERFLAG_AA);
     while (keep_doing) {
         keep_doing = false;
-        Zone::FindResult db_result = result.zone->find(qname_, qtype_);
+        std::auto_ptr<RRsetList> target(qtype_is_any ? new RRsetList : NULL);
+        Zone::FindResult db_result =
+            result.zone->find(qname_, qtype_, target.get());
+
         switch (db_result.code) {
+            case Zone::CNAME:
+                /*
+                 * We don't do chaining yet. Therefore handling a CNAME is
+                 * mostly the same as handling SUCCESS, but we didn't get
+                 * what we expected. It means no exceptions in ANY or NS
+                 * on the origin (though CNAME in origin is probably
+                 * forbidden anyway).
+                 */
+                // No break; here, fall trough.
             case Zone::SUCCESS:
                 response_.setRcode(Rcode::NOERROR());
-                response_.addRRset(Message::SECTION_ANSWER,
-                    boost::const_pointer_cast<RRset>(db_result.rrset));
-                // Handle additional for answer section
-                getAdditional(*result.zone, *db_result.rrset);
+                if (qtype_is_any) {
+                    // If quety type is ANY, insert all RRs under the domain
+                    // into answer section.
+                    BOOST_FOREACH(RRsetPtr rrset, *target) {
+                        response_.addRRset(Message::SECTION_ANSWER, rrset);
+                    }
+                } else {
+                    response_.addRRset(Message::SECTION_ANSWER,
+                        boost::const_pointer_cast<RRset>(db_result.rrset));
+                    // Handle additional for answer section
+                    getAdditional(*result.zone, *db_result.rrset);
+                }
                 // If apex NS records haven't been provided in the answer
                 // section, insert apex NS records into the authority section
                 // and AAAA/A RRS of each of the NS RDATA into the additional
                 // section.
                 if (qname_ != result.zone->getOrigin() ||
-                    (qtype_ != RRType::NS() && qtype_ != RRType::ANY()))
+                    db_result.code != Zone::SUCCESS ||
+                    (qtype_ != RRType::NS() && !qtype_is_any))
                 {
                     getAuthAdditional(*result.zone);
                 }
@@ -175,7 +199,6 @@ Query::process() const {
                 response_.setRcode(Rcode::NOERROR());
                 putSOA(*result.zone);
                 break;
-            case Zone::CNAME:
             case Zone::DNAME:
                 // TODO : replace qname, continue lookup
                 break;
diff --git a/src/bin/auth/statistics.cc b/src/bin/auth/statistics.cc
index ed4a6c7..e68793c 100644
--- a/src/bin/auth/statistics.cc
+++ b/src/bin/auth/statistics.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <auth/statistics.h>
 
 #include <cc/data.h>
diff --git a/src/bin/auth/statistics.h b/src/bin/auth/statistics.h
index 7ce2e01..9e5240e 100644
--- a/src/bin/auth/statistics.h
+++ b/src/bin/auth/statistics.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __STATISTICS_H
 #define __STATISTICS_H 1
 
diff --git a/src/bin/auth/tests/Makefile.am b/src/bin/auth/tests/Makefile.am
index 7298498..a1114e4 100644
--- a/src/bin/auth/tests/Makefile.am
+++ b/src/bin/auth/tests/Makefile.am
@@ -44,6 +44,7 @@ run_unittests_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
 run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la
 run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 run_unittests_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
+run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
 endif
 
 noinst_PROGRAMS = $(TESTS)
diff --git a/src/bin/auth/tests/auth_srv_unittest.cc b/src/bin/auth/tests/auth_srv_unittest.cc
index 5ec9bde..576799c 100644
--- a/src/bin/auth/tests/auth_srv_unittest.cc
+++ b/src/bin/auth/tests/auth_srv_unittest.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <config.h>
 
 #include <vector>
@@ -34,6 +32,7 @@
 #include <auth/statistics.h>
 
 #include <dns/tests/unittest_util.h>
+#include <testutils/dnsmessage_test.h>
 #include <testutils/srv_test.h>
 
 using namespace std;
@@ -139,9 +138,10 @@ TEST_F(AuthSrvTest, builtInQueryViaDNSServer) {
     createRequestPacket(request_message, IPPROTO_UDP);
 
     (*server.getDNSLookupProvider())(*io_message, parse_message,
+                                     response_message,
                                      response_obuffer, &dnsserv);
     (*server.getDNSAnswerProvider())(*io_message, parse_message,
-                                     response_obuffer);
+                                     response_message, response_obuffer);
 
     createBuiltinVersionResponse(default_qid, response_data);
     EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
@@ -154,9 +154,10 @@ TEST_F(AuthSrvTest, builtInQueryViaDNSServer) {
 TEST_F(AuthSrvTest, iqueryViaDNSServer) {
     createDataFromFile("iquery_fromWire.wire");
     (*server.getDNSLookupProvider())(*io_message, parse_message,
+                                     response_message,
                                      response_obuffer, &dnsserv);
     (*server.getDNSAnswerProvider())(*io_message, parse_message,
-                                     response_obuffer);
+                                     response_message, response_obuffer);
 
     UnitTestUtil::readWireData("iquery_response_fromWire.wire",
                                response_data);
diff --git a/src/bin/auth/tests/change_user_unittest.cc b/src/bin/auth/tests/change_user_unittest.cc
index 299b521..33897b6 100644
--- a/src/bin/auth/tests/change_user_unittest.cc
+++ b/src/bin/auth/tests/change_user_unittest.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <stdlib.h>
 #include <unistd.h>             // for getuid
 
diff --git a/src/bin/auth/tests/query_unittest.cc b/src/bin/auth/tests/query_unittest.cc
index f1f3a56..3bf4142 100644
--- a/src/bin/auth/tests/query_unittest.cc
+++ b/src/bin/auth/tests/query_unittest.cc
@@ -12,8 +12,16 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include <sstream>
+#include <vector>
+#include <map>
+
+#include <boost/bind.hpp>
+
+#include <dns/masterload.h>
 #include <dns/message.h>
 #include <dns/name.h>
+#include <dns/opcode.h>
 #include <dns/rcode.h>
 #include <dns/rrttl.h>
 #include <dns/rrtype.h>
@@ -23,162 +31,172 @@
 
 #include <auth/query.h>
 
+#include <testutils/dnsmessage_test.h>
+
 #include <gtest/gtest.h>
 
+using namespace std;
 using namespace isc::dns;
+using namespace isc::dns::rdata;
 using namespace isc::datasrc;
 using namespace isc::auth;
+using namespace isc::testutils;
 
 namespace {
 
-RRsetPtr a_rrset = RRsetPtr(new RRset(Name("www.example.com"),
-                                      RRClass::IN(), RRType::A(),
-                                      RRTTL(3600)));
-RRsetPtr soa_rrset = RRsetPtr(new RRset(Name("example.com"),
-                                        RRClass::IN(), RRType::SOA(),
-                                        RRTTL(3600)));
-RRsetPtr ns_rrset(RRsetPtr(new RRset(Name("ns.example.com"),
-                                     RRClass::IN(), RRType::NS(),
-                                     RRTTL(3600))));
-RRsetPtr glue_a_rrset(RRsetPtr(new RRset(Name("glue.ns.example.com"),
-                                         RRClass::IN(), RRType::A(),
-                                         RRTTL(3600))));
-RRsetPtr glue_aaaa_rrset(RRsetPtr(new RRset(Name("glue.ns.example.com"),
-                                            RRClass::IN(), RRType::AAAA(),
-                                            RRTTL(3600))));
-RRsetPtr noglue_a_rrset(RRsetPtr(new RRset(Name("noglue.example.com"),
-                                           RRClass::IN(), RRType::A(),
-                                           RRTTL(3600))));
-RRsetPtr delegated_mx_a_rrset(RRsetPtr(new RRset(
-    Name("mx.delegation.example.com"), RRClass::IN(), RRType::A(),
-    RRTTL(3600))));
+// This is the content of the mock zone (see below).
+// It's a sequence of textual RRs that is supposed to be parsed by
+// dns::masterLoad().  Some of the RRs are also used as the expected
+// data in specific tests, in which case they are referenced via specific
+// local variables (such as soa_txt).
+const char* const soa_txt = "example.com. 3600 IN SOA . . 0 0 0 0 0\n";
+const char* const zone_ns_txt =
+    "example.com. 3600 IN NS glue.delegation.example.com.\n"
+    "example.com. 3600 IN NS noglue.example.com.\n"
+    "example.com. 3600 IN NS example.net.\n";
+const char* const ns_addrs_txt =
+    "glue.delegation.example.com. 3600 IN A 192.0.2.153\n"
+    "glue.delegation.example.com. 3600 IN AAAA 2001:db8::53\n"
+    "noglue.example.com. 3600 IN A 192.0.2.53\n";
+const char* const delegation_txt =
+    "delegation.example.com. 3600 IN NS glue.delegation.example.com.\n"
+    "delegation.example.com. 3600 IN NS noglue.example.com.\n"
+    "delegation.example.com. 3600 IN NS cname.example.com.\n"
+    "delegation.example.com. 3600 IN NS example.org.\n";
+const char* const mx_txt =
+    "mx.example.com. 3600 IN MX 10 www.example.com.\n"
+    "mx.example.com. 3600 IN MX 20 mailer.example.org.\n"
+    "mx.example.com. 3600 IN MX 30 mx.delegation.example.com.\n";
+const char* const www_a_txt = "www.example.com. 3600 IN A 192.0.2.80\n";
+const char* const cname_txt =
+    "cname.example.com. 3600 IN CNAME www.example.com.\n";
+const char* const cname_nxdom_txt =
+    "cnamenxdom.example.com. 3600 IN CNAME nxdomain.example.com.\n";
+// CNAME Leading out of zone
+const char* const cname_out_txt =
+    "cnameout.example.com. 3600 IN CNAME www.example.org.\n";
+// The rest of data won't be referenced from the test cases.
+const char* const other_zone_rrs =
+    "cnamemailer.example.com. 3600 IN CNAME www.example.com.\n"
+    "cnamemx.example.com. 3600 IN MX 10 cnamemailer.example.com.\n"
+    "mx.delegation.example.com. 3600 IN A 192.0.2.100\n";
 
 // This is a mock Zone class for testing.
-// It is a derived class of Zone, and simply hardcodes the results of find()
-// See the find() implementation if you want to know its content.
+// It is a derived class of Zone for the convenient of tests.
+// Its find() method emulates the common behavior of protocol compliant
+// zone classes, but simplifies some minor cases and also supports broken
+// behavior.
+// For simplicity, most names are assumed to be "in zone"; there's only
+// one zone cut at the point of name "delegation.example.com".
+// It doesn't handle empty non terminal nodes (if we need to test such cases
+// find() should have specialized code for it).
 class MockZone : public Zone {
 public:
-    MockZone(bool has_SOA = true, bool has_apex_NS = true) :
+    MockZone() :
         origin_(Name("example.com")),
-        has_SOA_(has_SOA),
-        has_apex_NS_(has_apex_NS),
-        delegation_rrset(RRsetPtr(new RRset(Name("delegation.example.com"),
-                                            RRClass::IN(), RRType::NS(),
-                                            RRTTL(3600)))),
-        cname_rrset(RRsetPtr(new RRset(Name("cname.example.com"),
-                                       RRClass::IN(), RRType::CNAME(),
-                                       RRTTL(3600)))),
-        auth_ns_rrset(RRsetPtr(new RRset(Name("example.com"),
-                                         RRClass::IN(), RRType::NS(),
-                                         RRTTL(3600)))),
-        mx_cname_rrset_(new RRset(Name("cnamemailer.example.com"),
-            RRClass::IN(), RRType::CNAME(), RRTTL(3600))),
-        mx_rrset_(new RRset(Name("mx.example.com"), RRClass::IN(),
-            RRType::MX(), RRTTL(3600)))
+        delegation_name_("delegation.example.com"),
+        has_SOA_(true),
+        has_apex_NS_(true),
+        rrclass_(RRClass::IN())
     {
-        delegation_rrset->addRdata(rdata::generic::NS(
-                          Name("glue.ns.example.com")));
-        delegation_rrset->addRdata(rdata::generic::NS(
-                          Name("noglue.example.com")));
-        delegation_rrset->addRdata(rdata::generic::NS(
-                          Name("cname.example.com")));
-        delegation_rrset->addRdata(rdata::generic::NS(
-                          Name("example.org")));
-        cname_rrset->addRdata(rdata::generic::CNAME(
-                          Name("www.example.com")));
-        auth_ns_rrset->addRdata(rdata::generic::NS(
-                          Name("glue.ns.example.com")));
-        auth_ns_rrset->addRdata(rdata::generic::NS(
-                          Name("noglue.example.com")));
-        auth_ns_rrset->addRdata(rdata::generic::NS(
-                          Name("example.net")));
-        mx_rrset_->addRdata(isc::dns::rdata::generic::MX(10,
-            Name("www.example.com")));
-        mx_rrset_->addRdata(isc::dns::rdata::generic::MX(20,
-            Name("mailer.example.org")));
-        mx_rrset_->addRdata(isc::dns::rdata::generic::MX(30,
-            Name("mx.delegation.example.com")));
-        mx_cname_rrset_->addRdata(rdata::generic::CNAME(
-            Name("mx.example.com")));
+        stringstream zone_stream;
+        zone_stream << soa_txt << zone_ns_txt << ns_addrs_txt <<
+            delegation_txt << mx_txt << www_a_txt << cname_txt <<
+            cname_nxdom_txt << cname_out_txt << other_zone_rrs;
+
+        masterLoad(zone_stream, origin_, rrclass_,
+                   boost::bind(&MockZone::loadRRset, this, _1));
     }
-    virtual const isc::dns::Name& getOrigin() const;
-    virtual const isc::dns::RRClass& getClass() const;
+    virtual const isc::dns::Name& getOrigin() const { return (origin_); }
+    virtual const isc::dns::RRClass& getClass() const { return (rrclass_); }
+    virtual FindResult find(const isc::dns::Name& name,
+                            const isc::dns::RRType& type,
+                            RRsetList* target = NULL,
+                            const FindOptions options = FIND_DEFAULT) const;
+
+    // If false is passed, it makes the zone broken as if it didn't have the
+    // SOA.
+    void setSOAFlag(bool on) { has_SOA_ = on; }
 
-    FindResult find(const isc::dns::Name& name,
-                    const isc::dns::RRType& type,
-                    const FindOptions options = FIND_DEFAULT) const;
+    // If false is passed, it makes the zone broken as if it didn't have
+    // the apex NS.
+    void setApexNSFlag(bool on) { has_apex_NS_ = on; }
 
 private:
-    Name origin_;
+    typedef map<RRType, ConstRRsetPtr> RRsetStore;
+    typedef map<Name, RRsetStore> Domains;
+    Domains domains_;
+    void loadRRset(ConstRRsetPtr rrset) {
+        domains_[rrset->getName()][rrset->getType()] = rrset;
+        if (rrset->getName() == delegation_name_ &&
+            rrset->getType() == RRType::NS()) {
+            delegation_rrset_ = rrset;
+        }
+    }
+
+    const Name origin_;
+    const Name delegation_name_;
     bool has_SOA_;
     bool has_apex_NS_;
-    RRsetPtr delegation_rrset;
-    RRsetPtr cname_rrset;
-    RRsetPtr auth_ns_rrset;
-    RRsetPtr mx_cname_rrset_;
-    RRsetPtr mx_rrset_;
+    ConstRRsetPtr delegation_rrset_;
+    const RRClass rrclass_;
 };
 
-const Name&
-MockZone::getOrigin() const {
-    return (origin_);
-}
-
-const RRClass&
-MockZone::getClass() const {
-    return (RRClass::IN());
-}
-
 Zone::FindResult
 MockZone::find(const Name& name, const RRType& type,
-               const FindOptions options) const
+               RRsetList* target, const FindOptions options) const
 {
-    // hardcode the find results
-    if (name == Name("www.example.com") && type == RRType::A()) {
-        return (FindResult(SUCCESS, a_rrset));
-    } else if (name == Name("www.example.com")) {
-        return (FindResult(NXRRSET, RRsetPtr()));
-    } else if (name == Name("glue.ns.example.com") && type == RRType::A() &&
-        (options & FIND_GLUE_OK) != 0) {
-        return (FindResult(SUCCESS, glue_a_rrset));
-    } else if (name == Name("noglue.example.com") && (type == RRType::A() ||
-        type == RRType::ANY())) {
-        return (FindResult(SUCCESS, noglue_a_rrset));
-    } else if (name == Name("glue.ns.example.com") && type == RRType::AAAA() &&
-        (options & FIND_GLUE_OK) != 0) {
-        return (FindResult(SUCCESS, glue_aaaa_rrset));
-    } else if (name == Name("example.com") && type == RRType::SOA() &&
-        has_SOA_)
-    {
-        return (FindResult(SUCCESS, soa_rrset));
-    } else if (name == Name("example.com") && type == RRType::NS() &&
-        has_apex_NS_)
-    {
-        return (FindResult(SUCCESS, auth_ns_rrset));
-    } else if (name == Name("mx.delegation.example.com") &&
-        type == RRType::A() && (options & FIND_GLUE_OK) != 0)
-    {
-        return (FindResult(SUCCESS, delegated_mx_a_rrset));
-    } else if (name == Name("delegation.example.com") ||
-        name.compare(Name("delegation.example.com")).getRelation() ==
-        NameComparisonResult::SUBDOMAIN)
-    {
-        return (FindResult(DELEGATION, delegation_rrset));
-    } else if (name == Name("ns.example.com")) {
-        return (FindResult(DELEGATION, ns_rrset));
-    } else if (name == Name("nxdomain.example.com")) {
+    // Emulating a broken zone: mandatory apex RRs are missing if specifically
+    // configured so (which are rare cases).
+    if (name == origin_ && type == RRType::SOA() && !has_SOA_) {
+        return (FindResult(NXDOMAIN, RRsetPtr()));
+    } else if (name == origin_ && type == RRType::NS() && !has_apex_NS_) {
         return (FindResult(NXDOMAIN, RRsetPtr()));
-    } else if (name == Name("nxrrset.example.com")) {
+    }
+
+    // Special case for names on or under a zone cut
+    if ((options & FIND_GLUE_OK) == 0 &&
+        (name == delegation_name_ ||
+         name.compare(delegation_name_).getRelation() ==
+         NameComparisonResult::SUBDOMAIN)) {
+        return (FindResult(DELEGATION, delegation_rrset_));
+    }
+
+    // normal cases.  names are searched for only per exact-match basis
+    // for simplicity.
+    const Domains::const_iterator found_domain = domains_.find(name);
+    if (found_domain != domains_.end()) {
+        // First, try exact match.
+        RRsetStore::const_iterator found_rrset =
+            found_domain->second.find(type);
+        if (found_rrset != found_domain->second.end()) {
+            return (FindResult(SUCCESS, found_rrset->second));
+        }
+
+        // If not found but we have a target, fill it with all RRsets here
+        if (!found_domain->second.empty() && target != NULL) {
+            for (found_rrset = found_domain->second.begin();
+                 found_rrset != found_domain->second.end(); found_rrset++)
+            {
+                // Insert RRs under the domain name into target
+                target->addRRset(
+                    boost::const_pointer_cast<RRset>(found_rrset->second));
+            }
+            return (FindResult(SUCCESS, found_domain->second.begin()->second));
+        }
+
+        // Otherwise, if this domain name has CNAME, return it.
+        found_rrset = found_domain->second.find(RRType::CNAME());
+        if (found_rrset != found_domain->second.end()) {
+            return (FindResult(CNAME, found_rrset->second));
+        }
+
+        // Otherwise it's NXRRSET case.
         return (FindResult(NXRRSET, RRsetPtr()));
-    } else if ((name == Name("cname.example.com"))) {
-        return (FindResult(CNAME, cname_rrset));
-    } else if (name == Name("cnamemailer.example.com")) {
-        return (FindResult(CNAME, mx_cname_rrset_));
-    } else if (name == Name("mx.example.com")) {
-        return (FindResult(SUCCESS, mx_rrset_));
-    } else {
-        return (FindResult(DNAME, RRsetPtr()));
     }
+
+    // query name isn't found in our domains.  returns NXDOMAIN.
+    return (FindResult(NXDOMAIN, RRsetPtr()));
 }
 
 class QueryTest : public ::testing::Test {
@@ -186,195 +204,174 @@ protected:
     QueryTest() :
         qname(Name("www.example.com")), qclass(RRClass::IN()),
         qtype(RRType::A()), response(Message::RENDER),
-        query(memory_datasrc, qname, qtype, response)
+        qid(response.getQid()), query_code(Opcode::QUERY().getCode())
     {
         response.setRcode(Rcode::NOERROR());
+        response.setOpcode(Opcode::QUERY());
+        // create and add a matching zone.
+        mock_zone = new MockZone();
+        memory_datasrc.addZone(ZonePtr(mock_zone));
     }
+    MockZone* mock_zone;
     MemoryDataSrc memory_datasrc;
     const Name qname;
     const RRClass qclass;
     const RRType qtype;
     Message response;
-    Query query;
+    const qid_t qid;
+    const uint16_t query_code;
 };
 
+// A wrapper to check resulting response message commonly used in
+// tests below.
+// check_origin needs to be specified only when the authority section has
+// an SOA RR.  The interface is not generic enough but should be okay
+// for our test cases in practice.
+void
+responseCheck(Message& response, const isc::dns::Rcode& rcode,
+              unsigned int flags, const unsigned int ancount,
+              const unsigned int nscount, const unsigned int arcount,
+              const char* const expected_answer,
+              const char* const expected_authority,
+              const char* const expected_additional,
+              const Name& check_origin = Name::ROOT_NAME())
+{
+    // In our test cases QID, Opcode, and QDCOUNT should be constant, so
+    // we don't bother the test cases specifying these values.
+    headerCheck(response, response.getQid(), rcode, Opcode::QUERY().getCode(),
+                flags, 0, ancount, nscount, arcount);
+    if (expected_answer != NULL) {
+        rrsetsCheck(expected_answer,
+                    response.beginSection(Message::SECTION_ANSWER),
+                    response.endSection(Message::SECTION_ANSWER),
+                    check_origin);
+    }
+    if (expected_authority != NULL) {
+        rrsetsCheck(expected_authority,
+                    response.beginSection(Message::SECTION_AUTHORITY),
+                    response.endSection(Message::SECTION_AUTHORITY),
+                    check_origin);
+    }
+    if (expected_additional != NULL) {
+        rrsetsCheck(expected_additional,
+                    response.beginSection(Message::SECTION_ADDITIONAL),
+                    response.endSection(Message::SECTION_ADDITIONAL));
+    }
+}
+
 TEST_F(QueryTest, noZone) {
     // There's no zone in the memory datasource.  So the response should have
     // REFUSED.
-    EXPECT_NO_THROW(query.process());
+    MemoryDataSrc empty_memory_datasrc;
+    Query nozone_query(empty_memory_datasrc, qname, qtype, response);
+    EXPECT_NO_THROW(nozone_query.process());
     EXPECT_EQ(Rcode::REFUSED(), response.getRcode());
 }
 
 TEST_F(QueryTest, exactMatch) {
-    // add a matching zone.
-    memory_datasrc.addZone(ZonePtr(new MockZone()));
+    Query query(memory_datasrc, qname, qtype, response);
     EXPECT_NO_THROW(query.process());
     // find match rrset
-    EXPECT_TRUE(response.getHeaderFlag(Message::HEADERFLAG_AA));
-    EXPECT_EQ(Rcode::NOERROR(), response.getRcode());
-    EXPECT_TRUE(response.hasRRset(Message::SECTION_ANSWER,
-                                  Name("www.example.com"), RRClass::IN(),
-                                  RRType::A()));
-    EXPECT_TRUE(response.hasRRset(Message::SECTION_AUTHORITY,
-                                  Name("example.com"), RRClass::IN(),
-                                  RRType::NS()));
-    EXPECT_TRUE(response.hasRRset(Message::SECTION_ADDITIONAL,
-                                  Name("glue.ns.example.com"),
-                                  RRClass::IN(), RRType::A()));
-    EXPECT_TRUE(response.hasRRset(Message::SECTION_ADDITIONAL,
-                                  Name("glue.ns.example.com"),
-                                  RRClass::IN(), RRType::AAAA()));
-    EXPECT_TRUE(response.hasRRset(Message::SECTION_ADDITIONAL,
-                                  Name("noglue.example.com"),
-                                  RRClass::IN(), RRType::A()));
+    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
+                  www_a_txt, zone_ns_txt, ns_addrs_txt);
 }
 
 TEST_F(QueryTest, exactAddrMatch) {
     // find match rrset, omit additional data which has already been provided
     // in the answer section from the additional.
-    memory_datasrc.addZone(ZonePtr(new MockZone()));
-    const Name noglue_name(Name("noglue.example.com"));
-    Query noglue_query(memory_datasrc, noglue_name, qtype, response);
-    EXPECT_NO_THROW(noglue_query.process());
-    EXPECT_TRUE(response.getHeaderFlag(Message::HEADERFLAG_AA));
-    EXPECT_EQ(Rcode::NOERROR(), response.getRcode());
-    EXPECT_TRUE(response.hasRRset(Message::SECTION_ANSWER,
-                                  Name("noglue.example.com"), RRClass::IN(),
-                                  RRType::A()));
-    EXPECT_TRUE(response.hasRRset(Message::SECTION_AUTHORITY,
-                                  Name("example.com"), RRClass::IN(),
-                                  RRType::NS()));
-    EXPECT_TRUE(response.hasRRset(Message::SECTION_ADDITIONAL,
-                                  Name("glue.ns.example.com"),
-                                  RRClass::IN(), RRType::A()));
-    EXPECT_TRUE(response.hasRRset(Message::SECTION_ADDITIONAL,
-                                  Name("glue.ns.example.com"),
-                                  RRClass::IN(), RRType::AAAA()));
-    EXPECT_FALSE(response.hasRRset(Message::SECTION_ADDITIONAL,
-                                  Name("noglue.example.com"),
-                                  RRClass::IN(), RRType::A()));
+    EXPECT_NO_THROW(Query(memory_datasrc, Name("noglue.example.com"), qtype,
+                          response).process());
+
+    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 2,
+                  "noglue.example.com. 3600 IN A 192.0.2.53\n", zone_ns_txt,
+                  "glue.delegation.example.com. 3600 IN A 192.0.2.153\n"
+                  "glue.delegation.example.com. 3600 IN AAAA 2001:db8::53\n");
 }
 
 TEST_F(QueryTest, apexNSMatch) {
     // find match rrset, omit authority data which has already been provided
     // in the answer section from the authority section.
-    memory_datasrc.addZone(ZonePtr(new MockZone()));
-    const Name apex_name(Name("example.com"));
-    Query apex_ns_query(memory_datasrc, apex_name, RRType::NS(), response);
-    EXPECT_NO_THROW(apex_ns_query.process());
-    EXPECT_TRUE(response.getHeaderFlag(Message::HEADERFLAG_AA));
-    EXPECT_EQ(Rcode::NOERROR(), response.getRcode());
-    EXPECT_TRUE(response.hasRRset(Message::SECTION_ANSWER,
-                                  Name("example.com"), RRClass::IN(),
-                                  RRType::NS()));
-    EXPECT_FALSE(response.hasRRset(Message::SECTION_AUTHORITY,
-                                  Name("example.com"), RRClass::IN(),
-                                  RRType::NS()));
-    EXPECT_TRUE(response.hasRRset(Message::SECTION_ADDITIONAL,
-                                  Name("glue.ns.example.com"),
-                                  RRClass::IN(), RRType::A()));
-    EXPECT_TRUE(response.hasRRset(Message::SECTION_ADDITIONAL,
-                                  Name("glue.ns.example.com"),
-                                  RRClass::IN(), RRType::AAAA()));
-    EXPECT_TRUE(response.hasRRset(Message::SECTION_ADDITIONAL,
-                                  Name("noglue.example.com"),
-                                  RRClass::IN(), RRType::A()));
+    EXPECT_NO_THROW(Query(memory_datasrc, Name("example.com"), RRType::NS(),
+                          response).process());
+
+    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 3, 0, 3,
+                  zone_ns_txt, NULL, ns_addrs_txt);
 }
 
+// test type any query logic
 TEST_F(QueryTest, exactAnyMatch) {
     // find match rrset, omit additional data which has already been provided
     // in the answer section from the additional.
-    memory_datasrc.addZone(ZonePtr(new MockZone()));
-    const Name noglue_name(Name("noglue.example.com"));
-    Query noglue_query(memory_datasrc, noglue_name, RRType::ANY(), response);
-    EXPECT_NO_THROW(noglue_query.process());
-    EXPECT_TRUE(response.getHeaderFlag(Message::HEADERFLAG_AA));
-    EXPECT_EQ(Rcode::NOERROR(), response.getRcode());
-    EXPECT_TRUE(response.hasRRset(Message::SECTION_ANSWER,
-                                  Name("noglue.example.com"), RRClass::IN(),
-                                  RRType::A()));
-    EXPECT_TRUE(response.hasRRset(Message::SECTION_AUTHORITY,
-                                  Name("example.com"), RRClass::IN(),
-                                  RRType::NS()));
-    EXPECT_TRUE(response.hasRRset(Message::SECTION_ADDITIONAL,
-                                  Name("glue.ns.example.com"),
-                                  RRClass::IN(), RRType::A()));
-    EXPECT_TRUE(response.hasRRset(Message::SECTION_ADDITIONAL,
-                                  Name("glue.ns.example.com"),
-                                  RRClass::IN(), RRType::AAAA()));
-    EXPECT_FALSE(response.hasRRset(Message::SECTION_ADDITIONAL,
-                                  Name("noglue.example.com"),
-                                  RRClass::IN(), RRType::A()));
+    EXPECT_NO_THROW(Query(memory_datasrc, Name("noglue.example.com"),
+                          RRType::ANY(), response).process());
+
+    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 2,
+                  "noglue.example.com. 3600 IN A 192.0.2.53\n",
+                  zone_ns_txt,
+                  "glue.delegation.example.com. 3600 IN A 192.0.2.153\n"
+                  "glue.delegation.example.com. 3600 IN AAAA 2001:db8::53\n");
+}
+
+TEST_F(QueryTest, apexAnyMatch) {
+    // find match rrset, omit additional data which has already been provided
+    // in the answer section from the additional.
+    EXPECT_NO_THROW(Query(memory_datasrc, Name("example.com"),
+                          RRType::ANY(), response).process());
+    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 4, 0, 0,
+                  "example.com. 3600 IN SOA . . 0 0 0 0 0\n"
+                  "example.com. 3600 IN NS glue.delegation.example.com.\n"
+                  "example.com. 3600 IN NS noglue.example.com.\n"
+                  "example.com. 3600 IN NS example.net.\n",
+                  NULL, NULL, mock_zone->getOrigin());
+}
+
+TEST_F(QueryTest, glueANYMatch) {
+    EXPECT_NO_THROW(Query(memory_datasrc, Name("delegation.example.com"),
+                          RRType::ANY(), response).process());
+    responseCheck(response, Rcode::NOERROR(), 0, 0, 4, 3,
+                  NULL, delegation_txt, ns_addrs_txt);
+}
+
+TEST_F(QueryTest, nodomainANY) {
+    EXPECT_NO_THROW(Query(memory_datasrc, Name("nxdomain.example.com"),
+                          RRType::ANY(), response).process());
+    responseCheck(response, Rcode::NXDOMAIN(), AA_FLAG, 0, 1, 0,
+                  NULL, soa_txt, NULL, mock_zone->getOrigin());
 }
 
 // This tests that when we need to look up Zone's apex NS records for
 // authoritative answer, and there is no apex NS records. It should
 // throw in that case.
 TEST_F(QueryTest, noApexNS) {
-    // Add a zone without apex NS records
-    memory_datasrc.addZone(ZonePtr(new MockZone(true, false)));
-    const Name noglue_name(Name("noglue.example.com"));
-    Query noglue_query(memory_datasrc, noglue_name, qtype, response);
-    EXPECT_THROW(noglue_query.process(), Query::NoApexNS);
-    // We don't look into the response, as it throwed
+    // Disable apex NS record
+    mock_zone->setApexNSFlag(false);
+
+    EXPECT_THROW(Query(memory_datasrc, Name("noglue.example.com"), qtype,
+                       response).process(), Query::NoApexNS);
+    // We don't look into the response, as it threw
 }
 
 TEST_F(QueryTest, delegation) {
-    // add a matching zone.
-    memory_datasrc.addZone(ZonePtr(new MockZone()));
-    const Name delegation_name(Name("delegation.example.com"));
-    Query delegation_query(memory_datasrc, delegation_name, qtype, response);
-    EXPECT_NO_THROW(delegation_query.process());
-    EXPECT_FALSE(response.getHeaderFlag(Message::HEADERFLAG_AA));
-    EXPECT_EQ(Rcode::NOERROR(), response.getRcode());
-    EXPECT_TRUE(response.hasRRset(Message::SECTION_AUTHORITY,
-                                  Name("delegation.example.com"),
-                                  RRClass::IN(), RRType::NS()));
-    // glue address records
-    EXPECT_TRUE(response.hasRRset(Message::SECTION_ADDITIONAL,
-                                  Name("glue.ns.example.com"),
-                                  RRClass::IN(), RRType::A()));
-    EXPECT_TRUE(response.hasRRset(Message::SECTION_ADDITIONAL,
-                                  Name("glue.ns.example.com"),
-                                  RRClass::IN(), RRType::AAAA()));
-    // noglue address records
-    EXPECT_TRUE(response.hasRRset(Message::SECTION_ADDITIONAL,
-                                  Name("noglue.example.com"),
-                                  RRClass::IN(), RRType::A()));
-    // NS name has a CNAME
-    EXPECT_FALSE(response.hasRRset(Message::SECTION_ADDITIONAL,
-                                  Name("www.example.com"),
-                                  RRClass::IN(), RRType::A()));
-    // NS name is out of zone
-    EXPECT_FALSE(response.hasRRset(Message::SECTION_ADDITIONAL,
-                                  Name("example.org"),
-                                  RRClass::IN(), RRType::A()));
+    EXPECT_NO_THROW(Query(memory_datasrc, Name("delegation.example.com"),
+                          qtype, response).process());
+
+    responseCheck(response, Rcode::NOERROR(), 0, 0, 4, 3,
+                  NULL, delegation_txt, ns_addrs_txt);
 }
 
 TEST_F(QueryTest, nxdomain) {
-    // add a matching zone.
-    memory_datasrc.addZone(ZonePtr(new MockZone()));
-    const Name nxdomain_name(Name("nxdomain.example.com"));
-    Query nxdomain_query(memory_datasrc, nxdomain_name, qtype, response);
-    EXPECT_NO_THROW(nxdomain_query.process());
-    EXPECT_EQ(Rcode::NXDOMAIN(), response.getRcode());
-    EXPECT_EQ(0, response.getRRCount(Message::SECTION_ANSWER));
-    EXPECT_EQ(0, response.getRRCount(Message::SECTION_ADDITIONAL));
-    EXPECT_TRUE(response.hasRRset(Message::SECTION_AUTHORITY,
-        Name("example.com"), RRClass::IN(), RRType::SOA()));
+    EXPECT_NO_THROW(Query(memory_datasrc, Name("nxdomain.example.com"), qtype,
+                          response).process());
+    responseCheck(response, Rcode::NXDOMAIN(), AA_FLAG, 0, 1, 0,
+                  NULL, soa_txt, NULL, mock_zone->getOrigin());
 }
 
 TEST_F(QueryTest, nxrrset) {
-    // add a matching zone.
-    memory_datasrc.addZone(ZonePtr(new MockZone()));
-    const Name nxrrset_name(Name("nxrrset.example.com"));
-    Query nxrrset_query(memory_datasrc, nxrrset_name, qtype, response);
-    EXPECT_NO_THROW(nxrrset_query.process());
-    EXPECT_EQ(Rcode::NOERROR(), response.getRcode());
-    EXPECT_EQ(0, response.getRRCount(Message::SECTION_ANSWER));
-    EXPECT_EQ(0, response.getRRCount(Message::SECTION_ADDITIONAL));
-    EXPECT_TRUE(response.hasRRset(Message::SECTION_AUTHORITY,
-        Name("example.com"), RRClass::IN(), RRType::SOA()));
+    EXPECT_NO_THROW(Query(memory_datasrc, Name("www.example.com"),
+                          RRType::TXT(), response).process());
+
+    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 1, 0,
+                  NULL, soa_txt, NULL, mock_zone->getOrigin());
 }
 
 /*
@@ -382,27 +379,23 @@ TEST_F(QueryTest, nxrrset) {
  * throw in that case.
  */
 TEST_F(QueryTest, noSOA) {
-    memory_datasrc.addZone(ZonePtr(new MockZone(false)));
+    // disable zone's SOA RR.
+    mock_zone->setSOAFlag(false);
 
     // The NX Domain
-    const Name nxdomain_name(Name("nxdomain.example.com"));
-    Query nxdomain_query(memory_datasrc, nxdomain_name, qtype, response);
-    EXPECT_THROW(nxdomain_query.process(), Query::NoSOA);
+    EXPECT_THROW(Query(memory_datasrc, Name("nxdomain.example.com"),
+                       qtype, response).process(), Query::NoSOA);
     // Of course, we don't look into the response, as it throwed
 
     // NXRRSET
-    const Name nxrrset_name(Name("nxrrset.example.com"));
-    Query nxrrset_query(memory_datasrc, nxrrset_name, qtype, response);
-    EXPECT_THROW(nxrrset_query.process(), Query::NoSOA);
+    EXPECT_THROW(Query(memory_datasrc, Name("nxrrset.example.com"),
+                       qtype, response).process(), Query::NoSOA);
 }
 
 TEST_F(QueryTest, noMatchZone) {
     // there's a zone in the memory datasource but it doesn't match the qname.
     // should result in REFUSED.
-    memory_datasrc.addZone(ZonePtr(new MockZone()));
-    const Name nomatch_name(Name("example.org"));
-    Query nomatch_query(memory_datasrc, nomatch_name, qtype, response);
-    nomatch_query.process();
+    Query(memory_datasrc, Name("example.org"), qtype, response).process();
     EXPECT_EQ(Rcode::REFUSED(), response.getRcode());
 }
 
@@ -413,76 +406,124 @@ TEST_F(QueryTest, noMatchZone) {
  * A record, other to unknown out of zone one.
  */
 TEST_F(QueryTest, MX) {
-    memory_datasrc.addZone(ZonePtr(new MockZone()));
-    Name qname("mx.example.com");
-    Query mx_query(memory_datasrc, qname, RRType::MX(), response);
-    EXPECT_NO_THROW(mx_query.process());
-    EXPECT_EQ(Rcode::NOERROR(), response.getRcode());
-    EXPECT_TRUE(response.hasRRset(Message::SECTION_ANSWER,
-        Name("mx.example.com"), RRClass::IN(), RRType::MX()));
-    EXPECT_TRUE(response.hasRRset(Message::SECTION_ADDITIONAL,
-        Name("www.example.com"), RRClass::IN(), RRType::A()));
-    // We want to skip the additional ones related to authoritative
-    RRsetPtr ns;
-    for (SectionIterator<RRsetPtr> ai(response.beginSection(
-        Message::SECTION_AUTHORITY)); ai != response.endSection(
-        Message::SECTION_AUTHORITY); ++ai)
-    {
-        if ((*ai)->getName() == Name("example.com") && (*ai)->getType() ==
-            RRType::NS())
-        {
-            ns = *ai;
-            break;
-        }
-    }
-    /*
-     * In fact, the MX RRset mentions three names, but we don't know anything
-     * about one of them and one is under a zone cut, so we should have just
-     * one RRset (A for www.example.com)
-     */
-    // We can't use getRRCount, as it counts RRs, not RRsets
-    unsigned additional_count(0);
-    for (SectionIterator<RRsetPtr> ai(response.beginSection(
-        Message::SECTION_ADDITIONAL)); ai != response.endSection(
-        Message::SECTION_ADDITIONAL); ++ai)
-    {
-        // Skip the ones for the NS record
-        if (ns) {
-            for (RdataIteratorPtr nsi(ns->getRdataIterator()); !nsi->isLast();
-                nsi->next())
-            {
-                if ((*ai)->getName() ==
-                    dynamic_cast<const isc::dns::rdata::generic::NS&>(
-                    nsi->getCurrent()).getNSName())
-                {
-                    goto NS_ADDITIONAL_DATA;
-                }
-            }
-        }
-        // It is not related to the NS, then it must be related to the MX
-        ++additional_count;
-        EXPECT_EQ(Name("www.example.com"), (*ai)->getName());
-        EXPECT_EQ(RRType::A(), (*ai)->getType());
-        NS_ADDITIONAL_DATA:;
-    }
-    EXPECT_EQ(1, additional_count);
+    Query(memory_datasrc, Name("mx.example.com"), RRType::MX(),
+          response).process();
+
+    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 3, 3, 4,
+                  mx_txt, NULL,
+                  (string(ns_addrs_txt) + string(www_a_txt)).c_str());
 }
 
 /*
- * Test when we ask for MX and encounter an alias (CNAME in this case).
+ * Test when we ask for MX whose exchange is an alias (CNAME in this case).
  *
- * This should not trigger the additional processing.
+ * This should not trigger the additional processing for the exchange.
  */
 TEST_F(QueryTest, MXAlias) {
-    memory_datasrc.addZone(ZonePtr(new MockZone()));
-    Name qname("cnamemailer.example.com");
-    Query mx_query(memory_datasrc, qname, RRType::MX(), response);
-    EXPECT_NO_THROW(mx_query.process());
-    EXPECT_EQ(Rcode::NOERROR(), response.getRcode());
-    // We should not have the IP address in additional section
-    // Currently, the section should be completely empty
-    EXPECT_TRUE(response.beginSection(Message::SECTION_ADDITIONAL) ==
-        response.endSection(Message::SECTION_ADDITIONAL));
+    Query(memory_datasrc, Name("cnamemx.example.com"), RRType::MX(),
+          response).process();
+
+    // there shouldn't be no additional RRs for the exchanges (we have 3
+    // RRs for the NS).  The normal MX case is tested separately so we don't
+    // bother to examine the answer (and authority) sections.
+    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
+                  NULL, NULL, ns_addrs_txt);
+}
+
+/*
+ * Tests encountering a cname.
+ *
+ * There are tests leading to successful answers, NXRRSET, NXDOMAIN and
+ * out of the zone.
+ *
+ * TODO: We currently don't do chaining, so only the CNAME itself should be
+ * returned.
+ */
+TEST_F(QueryTest, CNAME) {
+    Query(memory_datasrc, Name("cname.example.com"), RRType::A(),
+        response).process();
+
+    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
+        cname_txt, zone_ns_txt, ns_addrs_txt);
+}
+
+TEST_F(QueryTest, explicitCNAME) {
+    // same owner name as the CNAME test but explicitly query for CNAME RR.
+    // expect the same response as we don't provide a full chain yet.
+    Query(memory_datasrc, Name("cname.example.com"), RRType::CNAME(),
+        response).process();
+
+    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
+        cname_txt, zone_ns_txt, ns_addrs_txt);
+}
+
+TEST_F(QueryTest, CNAME_NX_RRSET) {
+    // Leads to www.example.com, it doesn't have TXT
+    // note: with chaining, what should be expected is not trivial:
+    // BIND 9 returns the CNAME in answer and SOA in authority, no additional.
+    // NSD returns the CNAME, NS in authority, A/AAAA for NS in additional.
+    Query(memory_datasrc, Name("cname.example.com"), RRType::TXT(),
+        response).process();
+
+    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
+        cname_txt, zone_ns_txt, ns_addrs_txt);
+}
+
+TEST_F(QueryTest, explicitCNAME_NX_RRSET) {
+    // same owner name as the NXRRSET test but explicitly query for CNAME RR.
+    Query(memory_datasrc, Name("cname.example.com"), RRType::CNAME(),
+        response).process();
+
+    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
+        cname_txt, zone_ns_txt, ns_addrs_txt);
+}
+
+TEST_F(QueryTest, CNAME_NX_DOMAIN) {
+    // Leads to nxdomain.example.com
+    // note: with chaining, what should be expected is not trivial:
+    // BIND 9 returns the CNAME in answer and SOA in authority, no additional,
+    // RCODE being NXDOMAIN.
+    // NSD returns the CNAME, NS in authority, A/AAAA for NS in additional,
+    // RCODE being NOERROR.
+    Query(memory_datasrc, Name("cnamenxdom.example.com"), RRType::A(),
+        response).process();
+
+    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
+        cname_nxdom_txt, zone_ns_txt, ns_addrs_txt);
+}
+
+TEST_F(QueryTest, explicitCNAME_NX_DOMAIN) {
+    // same owner name as the NXDOMAIN test but explicitly query for CNAME RR.
+    Query(memory_datasrc, Name("cnamenxdom.example.com"), RRType::CNAME(),
+        response).process();
+
+    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
+        cname_nxdom_txt, zone_ns_txt, ns_addrs_txt);
+}
+
+TEST_F(QueryTest, CNAME_OUT) {
+    /*
+     * This leads out of zone. This should have only the CNAME even
+     * when we do chaining.
+     *
+     * TODO: We should be able to have two zones in the mock data source.
+     * Then the same test should be done with .org included there and
+     * see what it does (depends on what we want to do)
+     */
+    Query(memory_datasrc, Name("cnameout.example.com"), RRType::A(),
+        response).process();
+
+    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
+        cname_out_txt, zone_ns_txt, ns_addrs_txt);
+}
+
+TEST_F(QueryTest, explicitCNAME_OUT) {
+    // same owner name as the OUT test but explicitly query for CNAME RR.
+    Query(memory_datasrc, Name("cnameout.example.com"), RRType::CNAME(),
+        response).process();
+
+    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
+        cname_out_txt, zone_ns_txt, ns_addrs_txt);
 }
 
 }
diff --git a/src/bin/auth/tests/run_unittests.cc b/src/bin/auth/tests/run_unittests.cc
index 4bc529c..6ae848d 100644
--- a/src/bin/auth/tests/run_unittests.cc
+++ b/src/bin/auth/tests/run_unittests.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <gtest/gtest.h>
 
 #include <dns/tests/unittest_util.h>
diff --git a/src/bin/auth/tests/statistics_unittest.cc b/src/bin/auth/tests/statistics_unittest.cc
index 1bb3cd2..062b70d 100644
--- a/src/bin/auth/tests/statistics_unittest.cc
+++ b/src/bin/auth/tests/statistics_unittest.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <config.h>
 
 #include <gtest/gtest.h>
diff --git a/src/bin/bind10/bind10.xml b/src/bin/bind10/bind10.xml
index 25a8804..dfc8acf 100644
--- a/src/bin/bind10/bind10.xml
+++ b/src/bin/bind10/bind10.xml
@@ -17,7 +17,6 @@
  - PERFORMANCE OF THIS SOFTWARE.
 -->
 
-<!-- $Id$ -->
 <refentry>
 
   <refentryinfo>
@@ -132,7 +131,7 @@
             daemon to listen on.
             The default is 5300.</para>
 <!-- TODO: -->
-	    <note><simpara>The Y1 prototype release uses a non-default
+	    <note><simpara>This prototype release uses a non-default
 	    port for domain service.</simpara></note>
          </listitem>
       </varlistentry>
diff --git a/src/bin/bindctl/bindctl.1 b/src/bin/bindctl/bindctl.1
index e97c213..e86eca2 100644
--- a/src/bin/bindctl/bindctl.1
+++ b/src/bin/bindctl/bindctl.1
@@ -2,12 +2,12 @@
 .\"     Title: bindctl
 .\"    Author: [see the "AUTHORS" section]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: March 18, 2010
+.\"      Date: December 23, 2010
 .\"    Manual: BIND10
 .\"    Source: BIND10
 .\"  Language: English
 .\"
-.TH "BINDCTL" "1" "March 18, 2010" "BIND10" "BIND10"
+.TH "BINDCTL" "1" "December 23, 2010" "BIND10" "BIND10"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
@@ -22,7 +22,7 @@
 bindctl \- control and configure BIND 10
 .SH "SYNOPSIS"
 .HP \w'\fBbindctl\fR\ 'u
-\fBbindctl\fR
+\fBbindctl\fR [\fB\-a\ \fR\fB\fIaddress\fR\fR] [\fB\-h\fR] [\fB\-c\ \fR\fB\fIfile\fR\fR] [\fB\-p\ \fR\fB\fInumber\fR\fR] [\fB\-\-address\ \fR\fB\fIaddress\fR\fR] [\fB\-\-help\fR] [\fB\-\-certificate\-chain\ \fR\fB\fIfile\fR\fR] [\fB\-\-port\ \fR\fB\fInumber\fR\fR] [\fB\-\-version\fR]
 .SH "DESCRIPTION"
 .PP
 The
@@ -32,15 +32,64 @@ via its interactive command interpreter\&.
 .PP
 
 \fBbindctl\fR
-communicates over the REST\-ful interface provided by
+communicates over a HTTPS REST\-ful interface provided by
 \fBb10-cmdctl\fR(8)\&. The
 \fBb10-cfgmgr\fR(8)
 daemon stores the configurations and defines the commands\&.
+.SH "ARGUMENTS"
+.PP
+The arguments are as follows:
+.PP
+\fB\-a\fR \fIaddress\fR, \fB\-\-address\fR \fIaddress\fR
+.RS 4
+The IPv4 or IPv6 address to use to connect to the running
+\fBb10-cmdctl\fR(8)
+daemon\&. The default is 127\&.0\&.0\&.1\&.
+.RE
+.PP
+\fB\-c\fR \fIfile\fR, \fB\-\-certificate\-chain\fR \fIfile\fR
+.RS 4
+The PEM formatted server certificate validation chain file\&.
+.RE
+.PP
+\fB\-h\fR, \fB\-\-help\fR
+.RS 4
+Display command usage\&.
+.RE
+.PP
+\fB\-p\fR \fInumber\fR, \fB\-\-port\fR \fInumber\fR
+.RS 4
+The port number to use to connect to the running
+\fBb10-cmdctl\fR(8)
+daemon\&. The default is 8080\&.
+.if n \{\
+.sp
+.\}
+.RS 4
+.it 1 an-trap
+.nr an-no-space-flag 1
+.nr an-break-flag 1
+.br
+.ps +1
+\fBNote\fR
+.ps -1
+.br
+This default port number may change\&.
+.sp .5v
+.RE
+.RE
+.PP
+\fB\-\-version\fR
+.RS 4
+Display the version number and exit\&.
+.RE
+.SH "AUTHENTICATION"
 .PP
 The tool will authenticate using a username and password\&. On the first successful login, it will save the details to
 ~/\&.bind10/default_user\&.csv
 which will be used for later uses of
 \fBbindctl\fR\&.
+.SH "USAGE"
 .PP
 The
 \fBbindctl\fR
diff --git a/src/bin/bindctl/bindctl.xml b/src/bin/bindctl/bindctl.xml
index c709de6..98d65f9 100644
--- a/src/bin/bindctl/bindctl.xml
+++ b/src/bin/bindctl/bindctl.xml
@@ -17,11 +17,10 @@
  - PERFORMANCE OF THIS SOFTWARE.
 -->
 
-<!-- $Id$ -->
 <refentry>
 
   <refentryinfo>
-    <date>March 18, 2010</date>
+    <date>December 23, 2010</date>
   </refentryinfo>
 
   <refmeta>
@@ -45,6 +44,15 @@
   <refsynopsisdiv>
     <cmdsynopsis>
       <command>bindctl</command>
+      <arg><option>-a <replaceable>address</replaceable></option></arg>
+      <arg><option>-h</option></arg>
+      <arg><option>-c <replaceable>file</replaceable></option></arg>
+      <arg><option>-p <replaceable>number</replaceable></option></arg>
+      <arg><option>--address <replaceable>address</replaceable></option></arg>
+      <arg><option>--help</option></arg>
+      <arg><option>--certificate-chain <replaceable>file</replaceable></option></arg>
+      <arg><option>--port <replaceable>number</replaceable></option></arg>
+      <arg><option>--version</option></arg>
     </cmdsynopsis>
   </refsynopsisdiv>
 
@@ -60,7 +68,7 @@
     </para>
 
     <para>
-      <command>bindctl</command> communicates over the REST-ful
+      <command>bindctl</command> communicates over a HTTPS REST-ful
       interface provided by
       <citerefentry><refentrytitle>b10-cmdctl</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
       The
@@ -68,6 +76,75 @@
       daemon stores the configurations and defines the commands.
     </para>
 
+  </refsect1>
+
+  <refsect1>
+    <title>ARGUMENTS</title>
+
+    <para>The arguments are as follows:</para>
+
+    <variablelist>
+
+      <varlistentry>
+        <term><option>-a</option> <replaceable>address</replaceable>, <option>--address</option> <replaceable>address</replaceable></term>
+
+        <listitem>
+          <para>The IPv4 or IPv6 address to use to connect to the running
+            <citerefentry><refentrytitle>b10-cmdctl</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+            daemon.
+            The default is 127.0.0.1.
+          </para>
+         </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-c</option> <replaceable>file</replaceable>,
+        <option>--certificate-chain</option> <replaceable>file</replaceable></term>
+
+        <listitem>
+          <para>The PEM formatted server certificate validation chain file.
+          </para>
+<!-- TODO: any default? -->
+<!-- TODO: any way to choose this for cmdctl? -->
+         </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-h</option>,
+          <option>--help</option></term>
+        <listitem><para>
+          Display command usage.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-p</option> <replaceable>number</replaceable>, <option>--port</option> <replaceable>number</replaceable></term>
+
+        <listitem>
+          <para>The port number to use to connect to the running
+            <citerefentry><refentrytitle>b10-cmdctl</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+            daemon.
+            The default is 8080.</para>
+<!-- TODO: -->
+            <note><simpara>This default port number may change.</simpara></note>
+         </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>
+          <option>--version</option></term>
+        <listitem><para>
+          Display the version number and exit.</para>
+        </listitem>
+      </varlistentry>
+
+    </variablelist>
+
+  </refsect1>
+
+  <refsect1>
+    <title>AUTHENTICATION</title>
+
     <para>
       The tool will authenticate using a username and password.
       On the first successful login, it will save the details to
@@ -75,6 +152,13 @@
       which will be used for later uses of <command>bindctl</command>.
     </para>
 
+<!-- TODO: mention HTTPS? -->
+
+  </refsect1>
+
+  <refsect1>
+    <title>USAGE</title>
+
     <para>
       The <command>bindctl</command> prompt shows
       <quote>> </quote>.
diff --git a/src/bin/cfgmgr/b10-cfgmgr.py.in b/src/bin/cfgmgr/b10-cfgmgr.py.in
old mode 100644
new mode 100755
index 73906b7..659426d
--- a/src/bin/cfgmgr/b10-cfgmgr.py.in
+++ b/src/bin/cfgmgr/b10-cfgmgr.py.in
@@ -15,8 +15,6 @@
 # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
-# $Id$
-
 import sys; sys.path.append ('@@PYTHONPATH@@')
 
 from isc.config.cfgmgr import ConfigManager, ConfigManagerDataReadError
diff --git a/src/bin/cfgmgr/b10-cfgmgr.xml b/src/bin/cfgmgr/b10-cfgmgr.xml
index 20224dc..9505eee 100644
--- a/src/bin/cfgmgr/b10-cfgmgr.xml
+++ b/src/bin/cfgmgr/b10-cfgmgr.xml
@@ -17,7 +17,6 @@
  - PERFORMANCE OF THIS SOFTWARE.
 -->
 
-<!-- $Id$ -->
 <refentry>
 
   <refentryinfo>
diff --git a/src/bin/cfgmgr/tests/b10-cfgmgr_test.py.in b/src/bin/cfgmgr/tests/b10-cfgmgr_test.py.in
index c2687ff..8847b18 100644
--- a/src/bin/cfgmgr/tests/b10-cfgmgr_test.py.in
+++ b/src/bin/cfgmgr/tests/b10-cfgmgr_test.py.in
@@ -13,8 +13,6 @@
 # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
-# $Id: cfgmgr_test.py 2126 2010-06-16 14:40:22Z jelte $
-
 #
 # Tests for the configuration manager run script
 #
diff --git a/src/bin/cmdctl/b10-cmdctl.xml b/src/bin/cmdctl/b10-cmdctl.xml
index c0ac36a..06953a4 100644
--- a/src/bin/cmdctl/b10-cmdctl.xml
+++ b/src/bin/cmdctl/b10-cmdctl.xml
@@ -17,7 +17,6 @@
  - PERFORMANCE OF THIS SOFTWARE.
 -->
 
-<!-- $Id$ -->
 <refentry>
 
   <refentryinfo>
diff --git a/src/bin/loadzone/b10-loadzone.xml b/src/bin/loadzone/b10-loadzone.xml
index 3bed19e..25e23a5 100644
--- a/src/bin/loadzone/b10-loadzone.xml
+++ b/src/bin/loadzone/b10-loadzone.xml
@@ -17,7 +17,6 @@
  - PERFORMANCE OF THIS SOFTWARE.
 -->
 
-<!-- $Id$ -->
 <refentry>
 
   <refentryinfo>
diff --git a/src/bin/loadzone/tests/correct/mix1.db b/src/bin/loadzone/tests/correct/mix1.db
index da562ff..a9d58a8 100644
--- a/src/bin/loadzone/tests/correct/mix1.db
+++ b/src/bin/loadzone/tests/correct/mix1.db
@@ -1,4 +1,3 @@
-; $Id: ttl1.db,v 1.6 2007/06/19 23:47:04 tbox Exp $
 $ORIGIN mix1.
 @			IN SOA	ns hostmaster (
 				1        ; serial
diff --git a/src/bin/loadzone/tests/correct/ttlext.db b/src/bin/loadzone/tests/correct/ttlext.db
index 8ad6103..f8b96ea 100644
--- a/src/bin/loadzone/tests/correct/ttlext.db
+++ b/src/bin/loadzone/tests/correct/ttlext.db
@@ -1,4 +1,3 @@
-; $Id: ttl1.db,v 1.6 2007/06/19 23:47:04 tbox Exp $
 $ORIGIN ttlext.
 @			IN SOA	ns hostmaster (
 				1        ; serial
diff --git a/src/bin/msgq/msgq.py.in b/src/bin/msgq/msgq.py.in
old mode 100644
new mode 100755
index fad5b1f..8a8362a
--- a/src/bin/msgq/msgq.py.in
+++ b/src/bin/msgq/msgq.py.in
@@ -127,6 +127,7 @@ class MsgQ:
         self.hostname = socket.gethostname()
         self.subs = SubscriptionManager()
         self.lnames = {}
+        self.sendbuffs = {}
 
     def setup_poller(self):
         """Set up the poll thing.  Internal function."""
@@ -135,12 +136,29 @@ class MsgQ:
         except AttributeError:
             self.kqueue = select.kqueue()
     
-    def add_kqueue_socket(self, socket):
-        event = select.kevent(socket.fileno(),
-                              select.KQ_FILTER_READ,
+    def add_kqueue_socket(self, socket, write_filter=False):
+        """Add a kquque filter for a socket.  By default the read
+        filter is used; if write_filter is set to True, the write
+        filter is used.  We use a boolean value instead of a specific
+        filter constant, because kqueue filter values do not seem to
+        be defined on some systems.  The use of boolean makes the
+        interface restrictive because there are other filters, but this
+        method is mostly only for our internal use, so it should be
+        acceptable at least for now."""
+        filter_type = select.KQ_FILTER_WRITE if write_filter else \
+            select.KQ_FILTER_READ
+        event = select.kevent(socket.fileno(), filter_type,
                               select.KQ_EV_ADD | select.KQ_EV_ENABLE)
         self.kqueue.control([event], 0)
 
+    def delete_kqueue_socket(self, socket, write_filter=False):
+        """Delete a kqueue filter for socket.  See add_kqueue_socket()
+        for the semantics and notes about write_filter."""
+        filter_type = select.KQ_FILTER_WRITE if write_filter else \
+            select.KQ_FILTER_READ
+        event = select.kevent(socket.fileno(), filter_type,
+                              select.KQ_EV_DELETE)
+        self.kqueue.control([event], 0)
 
     def setup_listener(self):
         """Set up the listener socket.  Internal function."""
@@ -187,6 +205,12 @@ class MsgQ:
         # TODO: When we have logging, we might want
         # to add a debug message here that a new connection
         # was made
+        self.register_socket(newsocket)
+
+    def register_socket(self, newsocket):
+        """
+        Internal function to insert a socket. Used by process_accept and some tests.
+        """
         self.sockets[newsocket.fileno()] = newsocket
         lname = self.newlname()
         self.lnames[lname] = newsocket
@@ -198,10 +222,10 @@ class MsgQ:
 
     def process_socket(self, fd):
         """Process a read on a socket."""
-        sock = self.sockets[fd]
-        if sock == None:
+        if not fd in self.sockets:
             sys.stderr.write("[b10-msgq] Got read on Strange Socket fd %d\n" % fd)
             return
+        sock = self.sockets[fd]
 #        sys.stderr.write("[b10-msgq] Got read on fd %d\n" %fd)
         self.process_packet(fd, sock)
 
@@ -213,7 +237,9 @@ class MsgQ:
         lname = [ k for k, v in self.lnames.items() if v == sock ][0]
         del self.lnames[lname]
         sock.close()
-        self.sockets[fd] = None
+        del self.sockets[fd]
+        if fd in self.sendbuffs:
+            del self.sendbuffs[fd]
         sys.stderr.write("[b10-msgq] Closing socket fd %d\n" % fd)
 
     def getbytes(self, fd, sock, length):
@@ -287,6 +313,9 @@ class MsgQ:
             self.process_command_unsubscribe(sock, routing, data)
         elif cmd == 'getlname':
             self.process_command_getlname(sock, routing, data)
+        elif cmd == 'ping':
+            # Command for testing purposes
+            self.process_command_ping(sock, routing, data)
         else:
             sys.stderr.write("[b10-msgq] Invalid command: %s\n" % cmd)
 
@@ -305,10 +334,67 @@ class MsgQ:
         return ret
 
     def sendmsg(self, sock, env, msg = None):
-        sock.send(self.preparemsg(env, msg))
+        self.send_prepared_msg(sock, self.preparemsg(env, msg))
+
+    def __send_data(self, sock, data):
+        try:
+            # We set the socket nonblocking, MSG_DONTWAIT doesn't exist
+            # on some OSes
+            sock.setblocking(0)
+            return sock.send(data)
+        except socket.error as e:
+            if e.errno == errno.EAGAIN or e.errno == errno.EWOULDBLOCK:
+                return 0
+            else:
+                raise e
+        finally:
+            # And set it back again
+            sock.setblocking(1)
 
     def send_prepared_msg(self, sock, msg):
-        sock.send(msg)
+        # Try to send the data, but only if there's nothing waiting
+        fileno = sock.fileno()
+        if fileno in self.sendbuffs:
+            amount_sent = 0
+        else:
+            amount_sent = self.__send_data(sock, msg)
+
+        # Still something to send
+        if amount_sent < len(msg):
+            now = time.clock()
+            # Append it to buffer (but check the data go away)
+            if fileno in self.sendbuffs:
+                (last_sent, buff) = self.sendbuffs[fileno]
+                if now - last_sent > 0.1:
+                    self.kill_socket(fileno, sock)
+                    return
+                buff += msg
+            else:
+                buff = msg[amount_sent:]
+                last_sent = now
+                if self.poller:
+                    self.poller.register(fileno, select.POLLIN |
+                        select.POLLOUT)
+                else:
+                    self.add_kqueue_socket(sock, True)
+            self.sendbuffs[fileno] = (last_sent, buff)
+
+    def __process_write(self, fileno):
+        # Try to send some data from the buffer
+        (_, msg) = self.sendbuffs[fileno]
+        sock = self.sockets[fileno]
+        amount_sent = self.__send_data(sock, msg)
+        # Keep the rest
+        msg = msg[amount_sent:]
+        if len(msg) == 0:
+            # If there's no more, stop requesting for write availability
+            if self.poller:
+                self.poller.register(fileno, select.POLLIN)
+            else:
+                self.delete_kqueue_socket(sock, True)
+            del self.sendbuffs[fileno]
+        else:
+            self.sendbuffs[fileno] = (time.clock(), msg)
 
     def newlname(self):
         """Generate a unique connection identifier for this socket.
@@ -317,6 +403,9 @@ class MsgQ:
         self.connection_counter += 1
         return "%x_%x@%s" % (time.time(), self.connection_counter, self.hostname)
 
+    def process_command_ping(self, sock, routing, data):
+        self.sendmsg(sock, { "type" : "pong" }, data)
+
     def process_command_getlname(self, sock, routing, data):
         lname = [ k for k, v in self.lnames.items() if v == sock ][0]
         self.sendmsg(sock, { "type" : "getlname" }, { "lname" : lname })
@@ -379,22 +468,29 @@ class MsgQ:
                 if fd == self.listen_socket.fileno():
                     self.process_accept()
                 else:
-                    self.process_socket(fd)
+                    if event & select.POLLOUT:
+                        self.__process_write(fd)
+                    if event & select.POLLIN:
+                        self.process_socket(fd)
 
     def run_kqueue(self):
         while True:
             events = self.kqueue.control(None, 10)
             if not events:
                 raise RuntimeError('serve: kqueue returned no events')
-            
+
             for event in events:
                 if event.ident == self.listen_socket.fileno():
                     self.process_accept()
                 else:
-                    if event.flags & select.KQ_FILTER_READ and event.data > 0:
+                    if event.filter == select.KQ_FILTER_WRITE:
+                        self.__process_write(event.ident)
+                    if event.filter == select.KQ_FILTER_READ and \
+                            event.data > 0:
                         self.process_socket(event.ident)
                     elif event.flags & select.KQ_EV_EOF:
-                        self.kill_socket(event.ident, self.sockets[event.ident])
+                        self.kill_socket(event.ident,
+                                         self.sockets[event.ident])
 
     def shutdown(self):
         """Stop the MsgQ master."""
diff --git a/src/bin/msgq/msgq.xml b/src/bin/msgq/msgq.xml
index dc60a01..fd9518d 100644
--- a/src/bin/msgq/msgq.xml
+++ b/src/bin/msgq/msgq.xml
@@ -17,7 +17,6 @@
  - PERFORMANCE OF THIS SOFTWARE.
 -->
 
-<!-- $Id$ -->
 <refentry>
 
   <refentryinfo>
diff --git a/src/bin/msgq/tests/msgq_test.py b/src/bin/msgq/tests/msgq_test.py
index 2c10ed2..efae151 100644
--- a/src/bin/msgq/tests/msgq_test.py
+++ b/src/bin/msgq/tests/msgq_test.py
@@ -3,10 +3,14 @@ from msgq import SubscriptionManager, MsgQ
 import unittest
 import os
 import socket
+import signal
+import sys
+import time
+import isc.cc
 
 #
-# Currently only the subscription part is implemented...  I'd have to mock
-# out a socket, which, while not impossible, is not trivial.
+# Currently only the subscription part and some sending is implemented...
+# I'd have to mock out a socket, which, while not impossible, is not trivial.
 #
 
 class TestSubscriptionManager(unittest.TestCase):
@@ -108,5 +112,126 @@ class TestSubscriptionManager(unittest.TestCase):
         msgq = MsgQ("/does/not/exist")
         self.assertRaises(socket.error, msgq.setup)
 
+class SendNonblock(unittest.TestCase):
+    """
+    Tests that the whole thing will not get blocked if someone does not read.
+    """
+
+    def terminate_check(self, task, timeout = 10):
+        """
+        Runs task in separate process (task is a function) and checks
+        it terminates sooner than timeout.
+        """
+        task_pid = os.fork()
+        if task_pid == 0:
+            # Kill the forked process after timeout by SIGALRM
+            signal.alarm(timeout)
+            # Run the task
+            # If an exception happens or we run out of time, we terminate
+            # with non-zero
+            task()
+            # If we got here, then everything worked well and in time
+            # In that case, we terminate successfully
+            sys.exit()
+        else:
+            (pid, status) = os.waitpid(task_pid, 0)
+            self.assertEqual(0, status,
+                "The task did not complete successfully in time")
+
+    def infinite_sender(self, sender):
+        """
+        Sends data until an exception happens. socket.error is caught,
+        as it means the socket got closed. Sender is called to actually
+        send the data.
+        """
+        msgq = MsgQ()
+        # We do only partial setup, so we don't create the listening socket
+        msgq.setup_poller()
+        (read, write) = socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM)
+        msgq.register_socket(write)
+        # Keep sending while it is not closed by the msgq
+        try:
+            while True:
+                sender(msgq, write)
+        except socket.error:
+            pass
+
+    def test_infinite_sendmsg(self):
+        """
+        Tries sending messages (and not reading them) until it either times
+        out (in blocking call, wrong) or closes it (correct).
+        """
+        data = "data"
+        for i in range(1, 10):
+            data += data
+        self.terminate_check(lambda: self.infinite_sender(
+            lambda msgq, socket: msgq.sendmsg(socket, {}, {"message" : data})))
+
+    def test_infinite_sendprepared(self):
+        """
+        Tries sending data (and not reading them) until it either times
+        out (in blocking call, wrong) or closes it (correct).
+        """
+        data = b"data"
+        for i in range(1, 10):
+            data += data
+        self.terminate_check(lambda: self.infinite_sender(
+            lambda msgq, socket: msgq.send_prepared_msg(socket, data)))
+
+    def send_many(self, data):
+        """
+        Tries that sending a command many times and getting an answer works.
+        """
+        msgq = MsgQ()
+        # msgq.run needs to compare with the listen_socket, so we provide
+        # a replacement
+        class DummySocket:
+            def fileno():
+                return -1
+        msgq.listen_socket = DummySocket
+        (queue, out) = socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM)
+        def run():
+            length = len(data)
+            queue_pid = os.fork()
+            if queue_pid == 0:
+                signal.alarm(30)
+                msgq.setup_poller()
+                msgq.register_socket(queue)
+                msgq.run()
+            else:
+                try:
+                    def killall(signum, frame):
+                        os.kill(queue_pid, signal.SIGTERM)
+                        sys.exit(1)
+                    signal.signal(signal.SIGALRM, killall)
+                    msg = msgq.preparemsg({"type" : "ping"}, data)
+                    now = time.clock()
+                    while time.clock() - now < 0.2:
+                        out.sendall(msg)
+                        # Check the answer
+                        (routing, received) = msgq.read_packet(out.fileno(),
+                            out)
+                        self.assertEqual({"type" : "pong"},
+                            isc.cc.message.from_wire(routing))
+                        self.assertEqual(data, received)
+                finally:
+                    os.kill(queue_pid, signal.SIGTERM)
+        self.terminate_check(run)
+
+    def test_small_sends(self):
+        """
+        Tests sending small data many times.
+        """
+        self.send_many(b"data")
+
+    def test_large_sends(self):
+        """
+        Tests sending large data many times.
+        """
+        data = b"data"
+        for i in range(1, 20):
+            data = data + data
+        self.send_many(data)
+
 if __name__ == '__main__':
     unittest.main()
diff --git a/src/bin/resolver/Makefile.am b/src/bin/resolver/Makefile.am
index eaecfe2..0200675 100644
--- a/src/bin/resolver/Makefile.am
+++ b/src/bin/resolver/Makefile.am
@@ -37,6 +37,8 @@ spec_config.h: spec_config.h.pre
 BUILT_SOURCES = spec_config.h 
 pkglibexec_PROGRAMS = b10-resolver
 b10_resolver_SOURCES = resolver.cc resolver.h
+b10_resolver_SOURCES += response_classifier.cc response_classifier.h
+b10_resolver_SOURCES += response_scrubber.cc response_scrubber.h
 b10_resolver_SOURCES += $(top_builddir)/src/bin/auth/change_user.h
 b10_resolver_SOURCES += $(top_builddir)/src/bin/auth/common.h
 b10_resolver_SOURCES += main.cc
diff --git a/src/bin/resolver/b10-resolver.8 b/src/bin/resolver/b10-resolver.8
index 4eb1dbf..493538b 100644
--- a/src/bin/resolver/b10-resolver.8
+++ b/src/bin/resolver/b10-resolver.8
@@ -2,12 +2,12 @@
 .\"     Title: b10-resolver
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: January 3, 2011
+.\"      Date: January 19, 2011
 .\"    Manual: BIND10
 .\"    Source: BIND10
 .\"  Language: English
 .\"
-.TH "B10\-RESOLVER" "8" "January 3, 2011" "BIND10" "BIND10"
+.TH "B10\-RESOLVER" "8" "January 19, 2011" "BIND10" "BIND10"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
@@ -38,7 +38,7 @@ C\-Channel connection\&. If this connection is not established,
 will exit\&.
 .PP
 It also receives its configurations from
-\fBb10-cfgmgr\fR(8)\&. Currently no configuration commands are defined\&.
+\fBb10-cfgmgr\fR(8)\&.
 .if n \{\
 .sp
 .\}
@@ -73,9 +73,40 @@ must be either a valid numeric user ID or a valid user name\&. By default the da
 .RS 4
 Enabled verbose mode\&. This enables diagnostic messages to STDERR\&.
 .RE
-.SH "FILES"
+.SH "CONFIGURATION AND COMMANDS"
 .PP
-None\&.
+The configurable settings are:
+.PP
+
+\fIforward_addresses\fR
+defines the list of addresses and ports that
+\fBb10\-resolver\fR
+should forward queries to\&. Defining this enables forwarding\&.
+.PP
+
+\fIlisten_on\fR
+is a list of addresses and ports for
+\fBb10\-resolver\fR
+to listen on\&. The list items are the
+\fIaddress\fR
+string and
+\fIport\fR
+number\&. The defaults are address ::1 port 5300 and address 127\&.0\&.0\&.1 port 5300\&.
+.PP
+
+\fIretries\fR
+is the number of times to retry (resend query) after a timeout\&. The default is 0 (do not retry)\&.
+.PP
+
+\fItimeout\fR
+is the number of milliseconds to wait for answer\&. If set to \-1, the timeout is disabled\&. The default is 2000\&.
+.PP
+The configuration command is:
+.PP
+
+\fBshutdown\fR
+exits
+\fBb10\-resolver\fR\&. (Note that the BIND 10 boss process will restart this service\&.)
 .SH "SEE ALSO"
 .PP
 
@@ -88,7 +119,9 @@ BIND 10 Guide\&.
 .PP
 The
 \fBb10\-resolver\fR
-daemon was first coded in September 2010\&.
+daemon was first coded in September 2010\&. The initial implementation only provided forwarding\&.
+
+
 .SH "COPYRIGHT"
 .br
 Copyright \(co 2010 Internet Systems Consortium, Inc. ("ISC")
diff --git a/src/bin/resolver/b10-resolver.xml b/src/bin/resolver/b10-resolver.xml
index 063da21..dc2b724 100644
--- a/src/bin/resolver/b10-resolver.xml
+++ b/src/bin/resolver/b10-resolver.xml
@@ -2,7 +2,7 @@
                "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
 	       [<!ENTITY mdash "—">]>
 <!--
- - Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+ - Copyright (C) 2010-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
@@ -17,11 +17,10 @@
  - PERFORMANCE OF THIS SOFTWARE.
 -->
 
-<!-- $Id$ -->
 <refentry>
 
   <refentryinfo>
-    <date>January 3, 2011</date>
+    <date>January 19, 2011</date>
   </refentryinfo>
 
   <refmeta>
@@ -68,7 +67,6 @@
     <para>
       It also receives its configurations from
 <citerefentry><refentrytitle>b10-cfgmgr</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
-      Currently no configuration commands are defined.
     </para>
 
     <note><para>
@@ -112,12 +110,71 @@
   </refsect1>
 
   <refsect1>
+    <title>CONFIGURATION AND COMMANDS</title>
+    <para>
+      The configurable settings are:
+    </para>
+
+    <para>
+      <varname>forward_addresses</varname> defines the list of addresses
+      and ports that <command>b10-resolver</command> should forward
+      queries to.
+      Defining this enables forwarding.
+<!-- TODO: list
+address
+	::1
+port
+	53
+-->
+    </para>
+
+<!-- trac386:
+
+once that is merged you can for instance do 'config add Resolver/forward_addresses { "port": 123 } and it will fill in the rest (in this case ::1 for the address)
+
+-->
+
+    <para>
+      <varname>listen_on</varname> is a list of addresses and ports for
+      <command>b10-resolver</command> to listen on.
+      The list items are the <varname>address</varname> string
+      and <varname>port</varname> number.
+      The defaults are address ::1 port 5300 and
+      address 127.0.0.1 port 5300.
+    </para>
+
+    <para>
+      <varname>retries</varname> is the number of times to retry
+      (resend query) after a timeout.
+      The default is 0 (do not retry).
+    </para>
+
+    <para>
+      <varname>timeout</varname> is the number of milliseconds to
+      wait for answer. If set to -1, the timeout is disabled.
+      The default is 2000.
+    </para>
+
+<!-- TODO: formating -->
+    <para>
+      The configuration command is:
+    </para>
+
+    <para>
+      <command>shutdown</command> exits <command>b10-resolver</command>.
+      (Note that the BIND 10 boss process will restart this service.)
+    </para>
+
+  </refsect1>
+
+<!--
+  <refsect1>
     <title>FILES</title>
     <para>
       None.
     </para>
-<!-- TODO: this is not correct yet. -->
   </refsect1>
+-->
 
   <refsect1>
     <title>SEE ALSO</title>
@@ -142,7 +199,11 @@
     <title>HISTORY</title>
     <para>
       The <command>b10-resolver</command> daemon was first coded in
-      September 2010.
+      September 2010. The initial implementation only provided
+      forwarding.
+<!-- TODO: document when caching was added -->
+<!-- TODO: document when iteration was added -->
+<!-- TODO: document when validation was added -->
     </para>
   </refsect1>
 </refentry><!--
diff --git a/src/bin/resolver/main.cc b/src/bin/resolver/main.cc
index 29d91f5..03f9ab7 100644
--- a/src/bin/resolver/main.cc
+++ b/src/bin/resolver/main.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/select.h>
diff --git a/src/bin/resolver/resolver.cc b/src/bin/resolver/resolver.cc
index 73990ec..9e9bbd4 100644
--- a/src/bin/resolver/resolver.cc
+++ b/src/bin/resolver/resolver.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <config.h>
 
 #include <netinet/in.h>
@@ -66,6 +64,10 @@ private:
 public:
     ResolverImpl() :
         config_session_(NULL),
+        query_timeout_(2000),
+        client_timeout_(4000),
+        lookup_timeout_(30000),
+        retries_(3),
         rec_query_(NULL)
     {}
 
@@ -76,7 +78,12 @@ public:
     void querySetup(DNSService& dnss) {
         assert(!rec_query_); // queryShutdown must be called first
         dlog("Query setup");
-        rec_query_ = new RecursiveQuery(dnss, upstream_, timeout_, retries_);
+        rec_query_ = new RecursiveQuery(dnss, upstream_,
+                                        upstream_root_,
+                                        query_timeout_,
+                                        client_timeout_,
+                                        lookup_timeout_,
+                                        retries_);
     }
 
     void queryShutdown() {
@@ -95,20 +102,40 @@ public:
     {
         upstream_ = upstream;
         if (dnss) {
-            if (upstream_.empty()) {
-                dlog("Asked to do full recursive, but not implemented yet. "
-                    "I'll do nothing.",true);
-            } else {
+            if (!upstream_.empty()) {
                 dlog("Setting forward addresses:");
                 BOOST_FOREACH(const addr_t& address, upstream) {
                     dlog(" " + address.first + ":" +
                         boost::lexical_cast<string>(address.second));
                 }
+            } else {
+                dlog("No forward addresses, running in recursive mode");
             }
         }
     }
 
-    void processNormalQuery(const Question& question, MessagePtr message,
+    void setRootAddresses(const vector<addr_t>& upstream_root,
+                          DNSService *dnss)
+    {
+        upstream_root_ = upstream_root;
+        if (dnss) {
+            if (!upstream_root_.empty()) {
+                dlog("Setting root addresses:");
+                BOOST_FOREACH(const addr_t& address, upstream_root) {
+                    dlog(" " + address.first + ":" +
+                        boost::lexical_cast<string>(address.second));
+                }
+            } else {
+                dlog("No root addresses");
+            }
+        }
+    }
+
+    void resolve(const isc::dns::QuestionPtr& question,
+        const isc::resolve::ResolverInterface::CallbackPtr& callback);
+
+    void processNormalQuery(const Question& question,
+                            MessagePtr answer_message,
                             OutputBufferPtr buffer,
                             DNSServer* server);
 
@@ -117,13 +144,20 @@ public:
 
     /// These members are public because Resolver accesses them directly.
     ModuleCCSession* config_session_;
+    /// Addresses of the root nameserver(s)
+    vector<addr_t> upstream_root_;
     /// Addresses of the forward nameserver
     vector<addr_t> upstream_;
     /// Addresses we listen on
     vector<addr_t> listen_;
 
-    /// Time in milliseconds, to timeout
-    int timeout_;
+    /// Timeout for outgoing queries in milliseconds
+    int query_timeout_;
+    /// Timeout for incoming client queries in milliseconds
+    int client_timeout_;
+    /// Timeout for lookup processing in milliseconds
+    int lookup_timeout_;
+    
     /// Number of retries after timeout
     unsigned retries_;
 
@@ -149,20 +183,6 @@ public:
     MessagePtr message_;
 };
 
-class SectionInserter {
-public:
-    SectionInserter(MessagePtr message, const Message::Section sect) :
-        message_(message), section_(sect)
-    {}
-    void operator()(const RRsetPtr rrset) {
-        //dlog("Adding RRSet to message section " +
-        //    boost::lexical_cast<string>(section_));
-        message_->addRRset(section_, rrset, true);
-    }
-    MessagePtr message_;
-    const Message::Section section_;
-};
-
 void
 makeErrorMessage(MessagePtr message, OutputBufferPtr buffer,
                  const Rcode& rcode)
@@ -210,10 +230,14 @@ public:
     MessageLookup(Resolver* srv) : server_(srv) {}
 
     // \brief Handle the DNS Lookup
-    virtual void operator()(const IOMessage& io_message, MessagePtr message,
-                            OutputBufferPtr buffer, DNSServer* server) const
+    virtual void operator()(const IOMessage& io_message,
+                            MessagePtr query_message,
+                            MessagePtr answer_message,
+                            OutputBufferPtr buffer,
+                            DNSServer* server) const
     {
-        server_->processMessage(io_message, message, buffer, server);
+        server_->processMessage(io_message, query_message,
+                                answer_message, buffer, server);
     }
 private:
     Resolver* server_;
@@ -226,76 +250,62 @@ private:
 class MessageAnswer : public DNSAnswer {
 public:
     virtual void operator()(const IOMessage& io_message,
-                            MessagePtr message,
+                            MessagePtr query_message,
+                            MessagePtr answer_message,
                             OutputBufferPtr buffer) const
     {
-        const qid_t qid = message->getQid();
-        const bool rd = message->getHeaderFlag(Message::HEADERFLAG_RD);
-        const bool cd = message->getHeaderFlag(Message::HEADERFLAG_CD);
-        const Opcode& opcode = message->getOpcode();
-        const Rcode& rcode = message->getRcode();
-        vector<QuestionPtr> questions;
-        questions.assign(message->beginQuestion(), message->endQuestion());
+        const qid_t qid = query_message->getQid();
+        const bool rd = query_message->getHeaderFlag(Message::HEADERFLAG_RD);
+        const bool cd = query_message->getHeaderFlag(Message::HEADERFLAG_CD);
+        const Opcode& opcode = query_message->getOpcode();
 
-        message->clear(Message::RENDER);
-        message->setQid(qid);
-        message->setOpcode(opcode);
-        message->setRcode(rcode);
+        // Fill in the final details of the answer message
+        answer_message->setQid(qid);
+        answer_message->setOpcode(opcode);
 
-        message->setHeaderFlag(Message::HEADERFLAG_QR);
-        message->setHeaderFlag(Message::HEADERFLAG_RA);
+        answer_message->setHeaderFlag(Message::HEADERFLAG_QR);
+        answer_message->setHeaderFlag(Message::HEADERFLAG_RA);
         if (rd) {
-            message->setHeaderFlag(Message::HEADERFLAG_RD);
+            answer_message->setHeaderFlag(Message::HEADERFLAG_RD);
         }
         if (cd) {
-            message->setHeaderFlag(Message::HEADERFLAG_CD);
-        }
-
-
-        // Copy the question section.
-        for_each(questions.begin(), questions.end(), QuestionInserter(message));
-
-        // If the buffer already has an answer in it, copy RRsets from
-        // that into the new message, then clear the buffer and render
-        // the new message into it.
-        if (buffer->getLength() != 0) {
-            try {
-                Message incoming(Message::PARSE);
-                InputBuffer ibuf(buffer->getData(), buffer->getLength());
-                incoming.fromWire(ibuf);
-                message->setRcode(incoming.getRcode());
-                for_each(incoming.beginSection(Message::SECTION_ANSWER),
-                         incoming.endSection(Message::SECTION_ANSWER),
-                         SectionInserter(message, Message::SECTION_ANSWER));
-                for_each(incoming.beginSection(Message::SECTION_AUTHORITY),
-                         incoming.endSection(Message::SECTION_AUTHORITY),
-                         SectionInserter(message, Message::SECTION_AUTHORITY));
-                for_each(incoming.beginSection(Message::SECTION_ADDITIONAL),
-                         incoming.endSection(Message::SECTION_ADDITIONAL),
-                         SectionInserter(message, Message::SECTION_ADDITIONAL));
-            } catch (const Exception& ex) {
-                // Incoming message couldn't be read, we just SERVFAIL
-                message->setRcode(Rcode::SERVFAIL());
-            }
+            answer_message->setHeaderFlag(Message::HEADERFLAG_CD);
         }
 
+        vector<QuestionPtr> questions;
+        questions.assign(query_message->beginQuestion(), query_message->endQuestion());
+        for_each(questions.begin(), questions.end(), QuestionInserter(answer_message));
+        
         // Now we can clear the buffer and render the new message into it
         buffer->clear();
         MessageRenderer renderer(*buffer);
 
+        ConstEDNSPtr edns(query_message->getEDNS());
+        const bool dnssec_ok = edns && edns->getDNSSECAwareness();
+        if (edns) {
+            EDNSPtr edns_response(new EDNS());
+            edns_response->setDNSSECAwareness(dnssec_ok);
+
+            // TODO: We should make our own edns bufsize length configurable
+            edns_response->setUDPSize(Message::DEFAULT_MAX_EDNS0_UDPSIZE);
+            answer_message->setEDNS(edns_response);
+        }
+        
         if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
-            ConstEDNSPtr edns(message->getEDNS());
-            renderer.setLengthLimit(edns ? edns->getUDPSize() :
-                Message::DEFAULT_MAX_UDPSIZE);
+            if (edns) {
+                renderer.setLengthLimit(edns->getUDPSize());
+            } else {
+                renderer.setLengthLimit(Message::DEFAULT_MAX_UDPSIZE);
+            }
         } else {
             renderer.setLengthLimit(65535);
         }
 
-        message->toWire(renderer);
+        answer_message->toWire(renderer);
 
         dlog(string("sending a response (") +
             boost::lexical_cast<string>(renderer.getLength()) + "bytes): \n" +
-            message->toText());
+            answer_message->toText());
     }
 };
 
@@ -326,7 +336,6 @@ Resolver::~Resolver() {
     delete checkin_;
     delete dns_lookup_;
     delete dns_answer_;
-    dlog("Deleting the Resolver",true);
 }
 
 void
@@ -345,18 +354,29 @@ Resolver::getConfigSession() const {
 }
 
 void
-Resolver::processMessage(const IOMessage& io_message, MessagePtr message,
-                        OutputBufferPtr buffer, DNSServer* server)
+Resolver::resolve(const isc::dns::QuestionPtr& question,
+    const isc::resolve::ResolverInterface::CallbackPtr& callback)
+{
+    impl_->resolve(question, callback);
+}
+
+
+void
+Resolver::processMessage(const IOMessage& io_message,
+                         MessagePtr query_message,
+                         MessagePtr answer_message,
+                         OutputBufferPtr buffer,
+                         DNSServer* server)
 {
     dlog("Got a DNS message");
     InputBuffer request_buffer(io_message.getData(), io_message.getDataSize());
     // First, check the header part.  If we fail even for the base header,
     // just drop the message.
     try {
-        message->parseHeader(request_buffer);
+        query_message->parseHeader(request_buffer);
 
         // Ignore all responses.
-        if (message->getHeaderFlag(Message::HEADERFLAG_QR)) {
+        if (query_message->getHeaderFlag(Message::HEADERFLAG_QR)) {
             dlog("Received unexpected response, ignoring");
             server->resume(false);
             return;
@@ -369,52 +389,53 @@ Resolver::processMessage(const IOMessage& io_message, MessagePtr message,
 
     // Parse the message.  On failure, return an appropriate error.
     try {
-        message->fromWire(request_buffer);
+        query_message->fromWire(request_buffer);
     } catch (const DNSProtocolError& error) {
         dlog(string("returning ") + error.getRcode().toText() + ": " + 
             error.what());
-        makeErrorMessage(message, buffer, error.getRcode());
+        makeErrorMessage(query_message, buffer, error.getRcode());
         server->resume(true);
         return;
     } catch (const Exception& ex) {
         dlog(string("returning SERVFAIL: ") + ex.what());
-        makeErrorMessage(message, buffer, Rcode::SERVFAIL());
+        makeErrorMessage(query_message, buffer, Rcode::SERVFAIL());
         server->resume(true);
         return;
     } // other exceptions will be handled at a higher layer.
 
-    dlog("received a message:\n" + message->toText());
+    dlog("received a message:\n" + query_message->toText());
 
     // Perform further protocol-level validation.
     bool sendAnswer = true;
-    if (message->getOpcode() == Opcode::NOTIFY()) {
-        makeErrorMessage(message, buffer, Rcode::NOTAUTH());
+    if (query_message->getOpcode() == Opcode::NOTIFY()) {
+        makeErrorMessage(query_message, buffer, Rcode::NOTAUTH());
         dlog("Notify arrived, but we are not authoritative");
-    } else if (message->getOpcode() != Opcode::QUERY()) {
-        dlog("Unsupported opcode (got: " + message->getOpcode().toText() +
+    } else if (query_message->getOpcode() != Opcode::QUERY()) {
+        dlog("Unsupported opcode (got: " + query_message->getOpcode().toText() +
             ", expected: " + Opcode::QUERY().toText());
-        makeErrorMessage(message, buffer, Rcode::NOTIMP());
-    } else if (message->getRRCount(Message::SECTION_QUESTION) != 1) {
+        makeErrorMessage(query_message, buffer, Rcode::NOTIMP());
+    } else if (query_message->getRRCount(Message::SECTION_QUESTION) != 1) {
         dlog("The query contained " +
-            boost::lexical_cast<string>(message->getRRCount(
+            boost::lexical_cast<string>(query_message->getRRCount(
             Message::SECTION_QUESTION) + " questions, exactly one expected"));
-        makeErrorMessage(message, buffer, Rcode::FORMERR());
+        makeErrorMessage(query_message, buffer, Rcode::FORMERR());
     } else {
-        ConstQuestionPtr question = *message->beginQuestion();
+        ConstQuestionPtr question = *query_message->beginQuestion();
         const RRType &qtype = question->getType();
         if (qtype == RRType::AXFR()) {
             if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
-                makeErrorMessage(message, buffer, Rcode::FORMERR());
+                makeErrorMessage(query_message, buffer, Rcode::FORMERR());
             } else {
-                makeErrorMessage(message, buffer, Rcode::NOTIMP());
+                makeErrorMessage(query_message, buffer, Rcode::NOTIMP());
             }
         } else if (qtype == RRType::IXFR()) {
-            makeErrorMessage(message, buffer, Rcode::NOTIMP());
+            makeErrorMessage(query_message, buffer, Rcode::NOTIMP());
         } else {
             // The RecursiveQuery object will post the "resume" event to the
             // DNSServer when an answer arrives, so we don't have to do it now.
             sendAnswer = false;
-            impl_->processNormalQuery(*question, message, buffer, server);
+            impl_->processNormalQuery(*question, answer_message,
+                                      buffer, server);
         }
     }
 
@@ -424,23 +445,20 @@ Resolver::processMessage(const IOMessage& io_message, MessagePtr message,
 }
 
 void
-ResolverImpl::processNormalQuery(const Question& question, MessagePtr message,
-                                 OutputBufferPtr buffer, DNSServer* server)
+ResolverImpl::resolve(const QuestionPtr& question,
+    const isc::resolve::ResolverInterface::CallbackPtr& callback)
+{
+    rec_query_->resolve(question, callback);
+}
+
+void
+ResolverImpl::processNormalQuery(const Question& question,
+                                 MessagePtr answer_message,
+                                 OutputBufferPtr buffer,
+                                 DNSServer* server)
 {
     dlog("Processing normal query");
-    ConstEDNSPtr edns(message->getEDNS());
-    const bool dnssec_ok = edns && edns->getDNSSECAwareness();
-
-    message->makeResponse();
-    message->setHeaderFlag(Message::HEADERFLAG_RA);
-    message->setRcode(Rcode::NOERROR());
-    if (edns) {
-        EDNSPtr edns_response(new EDNS());
-        edns_response->setDNSSECAwareness(dnssec_ok);
-        edns_response->setUDPSize(ResolverImpl::DEFAULT_LOCAL_UDPSIZE);
-        message->setEDNS(edns_response);
-    }
-    rec_query_->sendQuery(question, buffer, server);
+    rec_query_->resolve(question, answer_message, buffer, server);
 }
 
 namespace {
@@ -475,7 +493,7 @@ parseAddresses(ConstElementPtr addresses) {
             }
         } else if (addresses->getType() != Element::null) {
             isc_throw(TypeError,
-                "forward_addresses config element must be a list");
+                "root_addresses, forward_addresses, and listen_on config element must be a list");
         }
     }
     return (result);
@@ -489,21 +507,41 @@ Resolver::updateConfig(ConstElementPtr config) {
 
     try {
         // Parse forward_addresses
+        ConstElementPtr rootAddressesE(config->get("root_addresses"));
+        vector<addr_t> rootAddresses(parseAddresses(rootAddressesE));
         ConstElementPtr forwardAddressesE(config->get("forward_addresses"));
         vector<addr_t> forwardAddresses(parseAddresses(forwardAddressesE));
         ConstElementPtr listenAddressesE(config->get("listen_on"));
         vector<addr_t> listenAddresses(parseAddresses(listenAddressesE));
         bool set_timeouts(false);
-        int timeout = impl_->timeout_;
+        int qtimeout = impl_->query_timeout_;
+        int ctimeout = impl_->client_timeout_;
+        int ltimeout = impl_->lookup_timeout_;
         unsigned retries = impl_->retries_;
-        ConstElementPtr timeoutE(config->get("timeout")),
-            retriesE(config->get("retries"));
-        if (timeoutE) {
+        ConstElementPtr qtimeoutE(config->get("timeout_query")),
+                        ctimeoutE(config->get("timeout_client")),
+                        ltimeoutE(config->get("timeout_lookup")),
+                        retriesE(config->get("retries"));
+        if (qtimeoutE) {
             // It should be safe to just get it, the config manager should
             // check for us
-            timeout = timeoutE->intValue();
-            if (timeout < -1) {
-                isc_throw(BadValue, "Timeout too small");
+            qtimeout = qtimeoutE->intValue();
+            if (qtimeout < -1) {
+                isc_throw(BadValue, "Query timeout too small");
+            }
+            set_timeouts = true;
+        }
+        if (ctimeoutE) {
+            ctimeout = ctimeoutE->intValue();
+            if (ctimeout < -1) {
+                isc_throw(BadValue, "Client timeout too small");
+            }
+            set_timeouts = true;
+        }
+        if (ltimeoutE) {
+            ltimeout = ltimeoutE->intValue();
+            if (ltimeout < -1) {
+                isc_throw(BadValue, "Lookup timeout too small");
             }
             set_timeouts = true;
         }
@@ -526,8 +564,12 @@ Resolver::updateConfig(ConstElementPtr config) {
             setForwardAddresses(forwardAddresses);
             need_query_restart = true;
         }
+        if (rootAddressesE) {
+            setRootAddresses(rootAddresses);
+            need_query_restart = true;
+        }
         if (set_timeouts) {
-            setTimeouts(timeout, retries);
+            setTimeouts(qtimeout, ctimeout, ltimeout, retries);
             need_query_restart = true;
         }
 
@@ -548,6 +590,12 @@ Resolver::setForwardAddresses(const vector<addr_t>& addresses)
     impl_->setForwardAddresses(addresses, dnss_);
 }
 
+void
+Resolver::setRootAddresses(const vector<addr_t>& addresses)
+{
+    impl_->setRootAddresses(addresses, dnss_);
+}
+
 bool
 Resolver::isForwarding() const {
     return (!impl_->upstream_.empty());
@@ -558,6 +606,11 @@ Resolver::getForwardAddresses() const {
     return (impl_->upstream_);
 }
 
+vector<addr_t>
+Resolver::getRootAddresses() const {
+    return (impl_->upstream_root_);
+}
+
 namespace {
 
 void
@@ -603,15 +656,36 @@ Resolver::setListenAddresses(const vector<addr_t>& addresses) {
 }
 
 void
-Resolver::setTimeouts(int timeout, unsigned retries) {
-    dlog("Setting timeout to " + boost::lexical_cast<string>(timeout) +
-        " and retry count to " + boost::lexical_cast<string>(retries));
-    impl_->timeout_ = timeout;
+Resolver::setTimeouts(int query_timeout, int client_timeout,
+                      int lookup_timeout, unsigned retries) {
+    dlog("Setting query timeout to " + boost::lexical_cast<string>(query_timeout) +
+         ", client timeout to " + boost::lexical_cast<string>(client_timeout) +
+         ", lookup timeout to " + boost::lexical_cast<string>(lookup_timeout) +
+         " and retry count to " + boost::lexical_cast<string>(retries));
+    impl_->query_timeout_ = query_timeout;
+    impl_->client_timeout_ = client_timeout;
+    impl_->lookup_timeout_ = lookup_timeout;
     impl_->retries_ = retries;
 }
-pair<int, unsigned>
-Resolver::getTimeouts() const {
-    return (pair<int, unsigned>(impl_->timeout_, impl_->retries_));
+
+int
+Resolver::getQueryTimeout() const {
+    return impl_->query_timeout_;
+}
+
+int
+Resolver::getClientTimeout() const {
+    return impl_->client_timeout_;
+}
+
+int
+Resolver::getLookupTimeout() const {
+    return impl_->lookup_timeout_;
+}
+
+int
+Resolver::getRetries() const {
+    return impl_->retries_;
 }
 
 vector<addr_t>
diff --git a/src/bin/resolver/resolver.h b/src/bin/resolver/resolver.h
index 606b1e4..3a5219f 100644
--- a/src/bin/resolver/resolver.h
+++ b/src/bin/resolver/resolver.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __RESOLVER_H
 #define __RESOLVER_H 1
 
@@ -26,6 +24,8 @@
 
 #include <asiolink/asiolink.h>
 
+#include <resolve/resolver_interface.h>
+
 class ResolverImpl;
 
 /**
@@ -37,7 +37,7 @@ class ResolverImpl;
  * answer. It doesn't really know about chasing referrals and similar, it
  * simply plugs the parts that know into the network handling code.
  */
-class Resolver {
+class Resolver : public isc::resolve::ResolverInterface {
     ///
     /// \name Constructors, Assignment Operator and Destructor.
     ///
@@ -53,6 +53,10 @@ public:
     ~Resolver();
     //@}
 
+    virtual void resolve(
+        const isc::dns::QuestionPtr& question,
+        const isc::resolve::ResolverInterface::CallbackPtr& callback);
+
     /// \brief Process an incoming DNS message, then signal 'server' to resume 
     ///
     /// A DNS query (or other message) has been received by a \c DNSServer
@@ -65,7 +69,8 @@ public:
     /// \param buffer Pointer to an \c OutputBuffer for the resposne
     /// \param server Pointer to the \c DNSServer
     void processMessage(const asiolink::IOMessage& io_message,
-                        isc::dns::MessagePtr message,
+                        isc::dns::MessagePtr query_message,
+                        isc::dns::MessagePtr answer_message,
                         isc::dns::OutputBufferPtr buffer,
                         asiolink::DNSServer* server);
 
@@ -113,6 +118,24 @@ public:
     bool isForwarding() const;
 
     /**
+     * \brief Specify the list of root nameservers.
+     *
+     * Specify the list of addresses of root nameservers
+     *
+     * @param addresses The list of addresses to use (each one is the address
+     * and port pair).
+     */
+    void setRootAddresses(const std::vector<std::pair<std::string,
+                          uint16_t> >& addresses);
+
+    /**
+     * \short Get list of root addresses.
+     *
+     * \see setRootAddresses.
+     */
+    std::vector<std::pair<std::string, uint16_t> > getRootAddresses() const;
+
+    /**
      * Set and get the addresses we listen on.
      */
     void setListenAddresses(const std::vector<std::pair<std::string,
@@ -127,7 +150,10 @@ public:
      * \param retries The number of retries (0 means try the first time only,
      *     do not retry).
      */
-    void setTimeouts(int timeout = -1, unsigned retries = 0);
+    void setTimeouts(int query_timeout = 2000,
+                     int client_timeout = 4000,
+                     int lookup_timeout = 30000,
+                     unsigned retries = 3);
 
     /**
      * \short Get info about timeouts.
@@ -136,6 +162,39 @@ public:
      */
     std::pair<int, unsigned> getTimeouts() const;
 
+    /**
+     * \brief Get the timeout for outgoing queries
+     *
+     * \returns Timeout for outgoing queries
+     */
+    int getQueryTimeout() const;
+
+    /**
+     * \brief Get the timeout for incoming client queries
+     *
+     * After this timeout, a SERVFAIL shall be sent back
+     * (internal resolving on the query will continue, see
+     * \c getLookupTimeout())
+     * 
+     * \returns Timeout for outgoing queries
+     */
+    int getClientTimeout() const;
+
+    /**
+     * \brief Get the timeout for lookups
+     *
+     * After this timeout, internal processing shall stop
+     */
+    int getLookupTimeout() const;
+
+    /**
+     * \brief Get the number of retries for outgoing queries
+     *
+     * If a query times out (value of \c getQueryTimeout()), we
+     * will retry this number of times
+     */
+    int getRetries() const;
+
 private:
     ResolverImpl* impl_;
     asiolink::DNSService* dnss_;
diff --git a/src/bin/resolver/resolver.spec.pre.in b/src/bin/resolver/resolver.spec.pre.in
index 9e9f136..a249009 100644
--- a/src/bin/resolver/resolver.spec.pre.in
+++ b/src/bin/resolver/resolver.spec.pre.in
@@ -4,12 +4,24 @@
     "module_description": "Recursive service",
     "config_data": [
       {
-        "item_name": "timeout",
+        "item_name": "timeout_query",
         "item_type": "integer",
         "item_optional": False,
         "item_default": 2000
       },
       {
+        "item_name": "timeout_client",
+        "item_type": "integer",
+        "item_optional": False,
+        "item_default": 4000
+      },
+      {
+        "item_name": "timeout_lookup",
+        "item_type": "integer",
+        "item_optional": False,
+        "item_default": 30000
+      },
+      {
         "item_name": "retries",
         "item_type": "integer",
         "item_optional": False,
@@ -42,6 +54,32 @@
         }
       },
       {
+        "item_name": "root_addresses",
+        "item_type": "list",
+        "item_optional": True,
+        "item_default": [],
+        "list_item_spec" : {
+          "item_name": "address",
+          "item_type": "map",
+          "item_optional": False,
+          "item_default": {},
+          "map_item_spec": [
+            {
+              "item_name": "address",
+              "item_type": "string",
+              "item_optional": False,
+              "item_default": "::1"
+            },
+            {
+              "item_name": "port",
+              "item_type": "integer",
+              "item_optional": False,
+              "item_default": 53
+            }
+          ]
+        }
+      },
+      {
         "item_name": "listen_on",
         "item_type": "list",
         "item_optional": False,
diff --git a/src/bin/resolver/response_classifier.cc b/src/bin/resolver/response_classifier.cc
new file mode 100644
index 0000000..2d21407
--- /dev/null
+++ b/src/bin/resolver/response_classifier.cc
@@ -0,0 +1,259 @@
+// 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.
+
+// $Id$
+
+#include <cstddef>
+#include <vector>
+
+#include <resolver/response_classifier.h>
+#include <dns/name.h>
+#include <dns/opcode.h>
+#include <dns/rcode.h>
+#include <dns/rrset.h>
+
+using namespace isc::dns;
+using namespace std;
+
+// Classify the response in the "message" object.
+
+ResponseClassifier::Category ResponseClassifier::classify(
+    const Question& question, const MessagePtr& message, bool tcignore)
+{
+    // Check header bits
+    if (!message->getHeaderFlag(Message::HEADERFLAG_QR)) {
+        return (NOTRESPONSE);   // Query-response bit not set in the response
+    }
+
+    // We only recognise responses to queries here
+    if (message->getOpcode() != Opcode::QUERY()) {
+        return (OPCODE);
+    }
+
+    // Apparently have a response.  There must be a single question in it...
+    const vector<QuestionPtr> msgquestion(message->beginQuestion(),
+            message->endQuestion());
+    if (msgquestion.size() != 1) {
+        return (NOTONEQUEST); // Not one question in response question section
+    }
+
+    // ... and the question should be equal to the question given.
+    // XXX: This means that "question" may not be the question sent by the
+    // client.  In the case of a CNAME response, the qname of subsequent
+    // questions needs to be altered.
+    if (question != *(msgquestion[0])) {
+        return (MISMATQUEST);
+    }
+
+    // Check for Rcode-related errors.
+    const Rcode& rcode = message->getRcode();
+    if (rcode != Rcode::NOERROR()) {
+        if (rcode == Rcode::NXDOMAIN()) {
+
+            // No such domain.  According to RFC2308, the domain referred to by
+            // the QNAME does not exist, although there may be a CNAME in the
+            // answer section and there may be an SOA and/or NS RRs in the
+            // authority section (ignoring any DNSSEC RRs for now).
+            //
+            // Note the "may".  There may not be anything.  Also, note that if
+            // there is a CNAME in the answer section, the authoritative server
+            // has verified that the name given in the CNAME's RDATA field does
+            // not exist. And that if a CNAME is returned in the answer, then
+            // the QNAME of the RRs in the authority section will refer to the
+            // authority for the CNAME's RDATA and not to the original question.
+            //
+            // Without doing further classification, it is sufficient to say
+            // that if an NXDOMAIN is received, there was no translation of the
+            // QNAME available.
+            return (NXDOMAIN);  // Received NXDOMAIN from parent.
+
+        } else {
+
+            // Not NXDOMAIN but not NOERROR either.  Must be an RCODE-related
+            // error.
+            return (RCODE);
+        }
+    }
+
+    // All seems OK and we can start looking at the content.  However, one
+    // more header check remains - was the response truncated?  If so, we'll
+    // probably want to re-query over TCP.  However, in some circumstances we
+    // might want to go with what we have.  So give the caller the option of
+    // ignoring the TC bit.
+    if (message->getHeaderFlag(Message::HEADERFLAG_TC) && (!tcignore)) {
+        return (TRUNCATED);
+    }
+
+    // By the time we get here, we're assured that the packet format is correct.
+    // We now need to decide as to whether it is an answer, a CNAME, or a
+    // referral.  For this, we need to inspect the contents of the answer
+    // and authority sections.
+    const vector<RRsetPtr> answer(
+            message->beginSection(Message::SECTION_ANSWER),
+            message->endSection(Message::SECTION_ANSWER)
+            );
+    const vector<RRsetPtr> authority(
+            message->beginSection(Message::SECTION_AUTHORITY),
+            message->endSection(Message::SECTION_AUTHORITY)
+            );
+
+    // If there is nothing in the answer section, it is a referral - unless
+    // there is nothing in the authority section
+    if (answer.empty()) {
+        if (authority.empty()) {
+            return (EMPTY);
+        } else {
+            return (REFERRAL);
+        }
+    }
+
+    // Look at two cases - one RRset in the answer and multiple RRsets in
+    // the answer.
+    if (answer.size() == 1) {
+
+        // Does the name and class of the answer match that of the question?
+        if ((answer[0]->getName() == question.getName()) &&
+            (answer[0]->getClass() == question.getClass())) {
+
+            // It does.  How about the type of the response?  The response
+            // is an answer if the type matches that of the question, or if the
+            // question was for type ANY.  It is a CNAME reply if the answer
+            // type is CNAME.  And it is an error for anything else.
+            if ((answer[0]->getType() == question.getType()) ||
+                (question.getType() == RRType::ANY())) {
+                return (ANSWER);
+            } else if (answer[0]->getType() == RRType::CNAME()) {
+                return (CNAME);
+            } else {
+                return (INVTYPE);
+            }
+        }
+        else {
+
+            // Either the name and/or class of the reply don't match that of
+            // the question.
+            return (INVNAMCLASS);
+        }
+    }
+
+    // There are multiple RRsets in the answer. They should all have the same
+    // QCLASS, else there is some error in the response.
+    for (int i = 1; i < answer.size(); ++i) {
+        if (answer[0]->getClass() != answer[i]->getClass()) {
+            return (MULTICLASS);
+        }
+    }
+
+    // If the request type was ANY and they all have the same QNAME, we have
+    // an answer.  But if they don't have the same QNAME, we must have an error;
+    // the only way we could get different QNAMES in an answer is if one were a
+    // CNAME - in which case there should no other record types at that QNAME.
+    if (question.getType() == RRType::ANY()) {
+        bool all_same = true;
+        for (int i = 1; (i < answer.size()) && all_same; ++i) {
+            all_same = (answer[0]->getName() == answer[i]->getName());
+        }
+        if (all_same) {
+            return (ANSWER);
+        } else {
+            return (EXTRADATA);
+        }
+    }
+
+    // Multiple RRs in the answer, and not all the same QNAME.  This
+    // is either an answer, a CNAME (in either case, there could be multiple
+    // CNAMEs in the chain) or an error.
+    //
+    // So we need to follow the CNAME chain to resolve this.  For this to work:
+    //
+    // a) There must be one RR that matches the name, class and type of
+    //    the question, and this is a CNAME.
+    // b) The CNAME chain is followed until the end of the chain does not
+    //    exist (answer is a CNAME) or it is not of type CNAME (ANSWER).
+    //
+    // In the latter case, if there are additional RRs, it must be an error.
+
+    vector<RRsetPtr> ansrrset(answer);
+    vector<int> present(ansrrset.size(), 1);
+    return cnameChase(question.getName(), question.getType(), ansrrset, present,
+        ansrrset.size());
+}
+
+// Search the CNAME chain.
+ResponseClassifier::Category ResponseClassifier::cnameChase(
+    const Name& qname, const RRType& qtype, vector<RRsetPtr>& ansrrset,
+    vector<int>& present, size_t size)
+{
+    // Search through the vector of RRset pointers until we find one with the
+    // right QNAME.
+    for (int i = 0; i < ansrrset.size(); ++i) {
+        if (present[i]) {
+
+            // This entry has not been logically removed, so look at it.
+            if (ansrrset[i]->getName() == qname) {
+
+                // QNAME match.  If this RRset is a CNAME, remove it from
+                // further consideration.  If nothing is left, the end of the
+                // chain is a CNAME so this is a CNAME.  Otherwise replace
+                // the name with the RDATA of the CNAME and call ourself
+                // recursively.
+                if (ansrrset[i]->getType() == RRType::CNAME()) {
+
+                    // Don't consider it in the next iteration (although we
+                    // can still access it for now).
+                    present[i] = 0;
+                    --size;
+                    if (size == 0) {
+                        return (CNAME);
+                    }
+                    else {
+                        if (ansrrset[i]->getRdataCount() != 1) {
+
+                            // Multiple RDATA for a CNAME?  This is invalid.
+
+                            return (NOTSINGLE);
+                        }
+                        RdataIteratorPtr it = ansrrset[i]->getRdataIterator();
+                        Name newname(it->getCurrent().toText());
+
+                        return cnameChase(newname, qtype, ansrrset, present,
+                            size);
+                    }
+
+                } else {
+
+                    // We've got here because the element is not a CNAME.  If
+                    // this is the last element and the type is the one we are
+                    // after, we've found the answer, or it is an error.  If
+                    // there is more than one RRset left in the list we are
+                    // searching, we have extra data in the answer.
+                    if (ansrrset[i]->getType() == qtype) {
+                        if (size == 1) {
+                            return (ANSWERCNAME);
+                        } else {
+                            return (EXTRADATA);
+                        }
+                    }
+                    return (INVTYPE);
+                }
+            }
+        }
+    }
+
+    // We get here if we've dropped off the end of the list without finding the
+    // QNAME we are looking for.  This means that the CNAME chain has ended
+    // but there are additional RRsets in the data.
+
+    return (EXTRADATA);
+}
diff --git a/src/bin/resolver/response_classifier.h b/src/bin/resolver/response_classifier.h
new file mode 100644
index 0000000..394d60f
--- /dev/null
+++ b/src/bin/resolver/response_classifier.h
@@ -0,0 +1,138 @@
+// 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.
+
+// $Id$
+
+#ifndef __RESPONSE_CLASSIFIER_H
+#define __RESPONSE_CLASSIFIER_H
+
+#include <cstddef>
+
+#include <dns/question.h>
+#include <dns/message.h>
+#include <dns/question.h>
+
+/// \brief Classify Server Response
+///
+/// This class is used in the recursive server.  It is passed an answer received
+/// from an upstream server and categorises it.
+///
+/// TODO: It is unlikely that the code can be used in this form.  Some adaption
+/// of it will be required to put it in the server.
+///
+/// TODO: The code here does not take into account any EDNS0 fields.
+
+class ResponseClassifier {
+public:
+
+    /// \brief Category of Answer
+    ///
+    /// In the valid answers, not the distinction between REFERRAL and CNAME.
+    /// A REFERRAL answer means that the answer section of the message is
+    /// empty, but there is something in the authority section.  A CNAME means
+    /// that the answer section contains one or more CNAMES in a chain that
+    /// do not end with a non-CNAME RRset.
+    enum Category {
+
+        // Codes indicating that a message is valid.
+
+        ANSWER,             ///< Response contains the answer
+        ANSWERCNAME,        ///< Response was a CNAME chain ending in an answer
+        CNAME,              ///< Response was a CNAME
+        NXDOMAIN,           ///< Response was an NXDOMAIN
+        REFERRAL,           ///< Response contains a referral
+
+        // Codes indicating that a message is invalid.  Note that the error()
+        // method relies on these appearing after the "message valid" codes.
+
+        EMPTY,              ///< No answer or authority sections
+        EXTRADATA,          ///< Answer section contains more RRsets than needed
+        INVNAMCLASS,        ///< Invalid name or class in answer
+        INVTYPE,            ///< Name/class of answer correct, type is wrong
+        MISMATQUEST,        ///< Response question section != question
+        MULTICLASS,         ///< Multiple classes in multi-RR answer
+        NOTONEQUEST,        ///< Not one question in response question section
+        NOTRESPONSE,        ///< Response has the Query/Response bit clear
+        NOTSINGLE,          ///< CNAME has multiple RDATA elements.
+        OPCODE,             ///< Opcode field does not indicate a query
+        RCODE,              ///< RCODE indicated an error
+        TRUNCATED           ///< Response was truncated
+    };
+
+    /// \brief Check Error
+    ///
+    /// An inline routine to quickly classify whether the return category is
+    /// an error or not.  This makes use of internal knowledge of the order of
+    /// codes in the Category enum.
+    ///
+    /// \param code Return category from classify()
+    ///
+    /// \return true if the category is an error, false if not.
+    static bool error(Category code) {
+        return (code > REFERRAL);
+    }
+
+    /// \brief Classify
+    ///
+    /// Classify the response in the "message" object.
+    ///
+    /// \param question Question that was sent to the server
+    /// \param message Pointer to the associated response from the server.
+    /// \param tcignore If set, the TC bit in a response packet is
+    /// ignored.  Otherwise the error code TRUNCATED will be returned.  The
+    /// only time this is likely to be used is in development where we are not
+    /// going to fail over to TCP and will want to use what is returned, even
+    /// if some of the response was lost.
+    static Category classify(const isc::dns::Question& question,
+            const isc::dns::MessagePtr& message, bool tcignore = false);
+
+private:
+    /// \brief Follow CNAMEs
+    ///
+    /// Given a QNAME and an answer section that contains CNAMEs, assume that
+    /// they form a CNAME chain and search through them.  Possible outcomes
+    /// are:
+    ///
+    /// a) All CNAMES and they form a chain.  The result is a referral.
+    /// b) All but one are CNAMES and they form a chain.  The other is pointed
+    ///    to by the last element of the chain and is the correct QTYPE.  The
+    ///    result is an answer.
+    /// c) Having followed the CNAME chain as far as we can, there is one
+    ///    remaining RRset that is of the wrong type, or there are multiple
+    ///    RRsets remaining.  return the EXTRADATA code.
+    ///
+    /// \param qname Question name we are searching for
+    /// \param qtype Question type we are search for.  (This is assumed not
+    /// to be "ANY".)
+    /// \param ansrrset Vector of RRsetPtr pointing to the RRsets we are
+    /// considering.
+    /// \param present Array of "int" the same size of ansrrset, with each
+    /// element set to "1" to allow the corresponding element of ansrrset to
+    /// be checked, and "0" to skip it.  This might be premature optimisation,
+    /// but the algorithm would otherwise involve duplicating the RRset
+    /// vector then removing elements from random positions one by one.  As
+    /// each removal involves the destruction of an "xxxPtr" element (which
+    /// presently is implemented by boost::shared_ptr), the overhad of memory
+    /// management seemed high.  This solution imposes some additional loop
+    /// cycles, but that should be minimal compared with the overhead of the
+    /// memory management.
+    /// \param size Number of elements to check.  See description of \c present
+    /// for details.
+    static Category cnameChase(const isc::dns::Name& qname,
+        const isc::dns::RRType& qtype,
+        std::vector<isc::dns::RRsetPtr>& ansrrset, std::vector<int>& present,
+        size_t size);
+};
+
+#endif // __RESPONSE_CLASSIFIER_H
diff --git a/src/bin/resolver/response_scrubber.cc b/src/bin/resolver/response_scrubber.cc
new file mode 100644
index 0000000..060a8b1
--- /dev/null
+++ b/src/bin/resolver/response_scrubber.cc
@@ -0,0 +1,189 @@
+
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <iostream>
+#include <vector>
+#include <dns/message.h>
+#include <dns/rrset.h>
+#include <dns/name.h>
+#include "response_scrubber.h"
+
+using namespace isc::dns;
+using namespace std;
+
+// Compare addresses etc.
+
+ResponseScrubber::Category ResponseScrubber::addressCheck(
+    const asiolink::IOEndpoint& to, const asiolink::IOEndpoint& from)
+{
+    if (from.getProtocol() == to.getProtocol()) {
+        if (from.getAddress() == to.getAddress()) {
+            if (from.getPort() == to.getPort()) {
+                return (ResponseScrubber::SUCCESS);
+            } else {
+                return (ResponseScrubber::PORT);
+            }
+        } else {
+            return (ResponseScrubber::ADDRESS);
+        }
+    }
+    return (ResponseScrubber::PROTOCOL);
+}
+
+// Do a general scrubbing.  The QNAMES of RRsets in the specified section are
+// compared against the list of name given and if they are not equal and not in
+// the specified relationship (generally superdomain or subdomain) to at least
+// of of the given names, they are removed.
+
+unsigned int
+ResponseScrubber::scrubSection(Message& message,
+    const vector<const Name*>& names,
+    const NameComparisonResult::NameRelation connection, 
+    const Message::Section section)
+{
+    unsigned int count = 0;     // Count of RRsets removed
+    unsigned int kept = 0;      // Count of RRsets kept
+    bool removed = true;        // Set true if RRset removed in a pass
+
+    // Need to go through the section multiple times as when an RRset is
+    // removed, all iterators into the section are invalidated.  This condition
+    // is flagged by "remove" being set true when an RRset is removed.
+
+    while (removed) {
+        RRsetIterator i = message.beginSection(section);
+
+        // Skips the ones that have been checked (and retained) in a previous
+        // pass through the "while" loop.  (Although RRset removal invalidates
+        // iterators, it does not change the relative order of the retained
+        // RRsets in the section.)
+        for (int j = 0; j < kept; ++j) {
+            ++i;
+        }
+
+        // Start looking at the remaining entries in the section.
+        removed = false;
+        for (; (i != message.endSection(section)) && (!removed); ++i) {
+
+            // Loop through the list of names given and see if any are in the
+            // given relationship with the QNAME of this RRset
+            bool nomatch = true;
+            for (vector<const Name*>::const_iterator n = names.begin();
+                ((n != names.end()) && nomatch); ++n) {
+                NameComparisonResult result = (*i)->getName().compare(**n);
+                NameComparisonResult::NameRelation relationship =
+                    result.getRelation();
+                if ((relationship == NameComparisonResult::EQUAL) ||
+                   (relationship == connection)) {
+                    
+                    // RRset in the specified relationship, so a match has
+                    // been found
+                    nomatch = false;
+                }
+            }
+
+            // Remove the RRset if there was no match to one of the given names.
+            if (nomatch) {
+                message.removeRRset(section, i);
+                ++count;            // One more RRset removed
+                removed = true;     // Something was removed
+             } else {
+
+                // There was a match so this is one more entry we can skip next
+                // time.
+                ++kept;
+             }
+        }
+    }
+
+    return count;
+}
+
+// Perform the scrubbing of all sections of the message.
+
+unsigned int
+ResponseScrubber::scrubAllSections(Message& message, const Name& bailiwick) {
+
+    // Leave the question section alone.  Just go through the RRsets in the
+    // answer, authority and additional sections.
+    unsigned int count = 0;
+    const vector<const Name*> bailiwick_names(1, &bailiwick);
+    count += scrubSection(message, bailiwick_names,
+            NameComparisonResult::SUBDOMAIN, Message::SECTION_ANSWER);
+    count += scrubSection(message, bailiwick_names,
+            NameComparisonResult::SUBDOMAIN, Message::SECTION_AUTHORITY);
+    count += scrubSection(message, bailiwick_names,
+            NameComparisonResult::SUBDOMAIN, Message::SECTION_ADDITIONAL);
+
+    return count;
+}
+
+// Scrub across sections.
+
+unsigned int
+ResponseScrubber::scrubCrossSections(isc::dns::Message& message) {
+
+    // Get a list of the names in the answer section or, failing this, the
+    // question section.  Note that pointers to the names within "message" are
+    // stored; this is OK as the relevant sections in "message" will not change
+    // during the lifetime of this method (it only affects the authority
+    // section).
+    vector<const Name*> source;
+    if (message.getRRCount(Message::SECTION_ANSWER) != 0) {
+        for (RRsetIterator i = message.beginSection(Message::SECTION_ANSWER);
+            i != message.endSection(Message::SECTION_ANSWER); ++i) {
+            const Name& qname = (*i)->getName();
+            source.push_back(&qname);
+        }
+
+    } else {
+        for (QuestionIterator i = message.beginQuestion();
+            i != message.endQuestion(); ++i) {
+            const Name& qname = (*i)->getName();
+            source.push_back(&qname);
+        }
+    }
+
+    if (source.empty()) {
+        // TODO: Log the fact - should be at least a question present
+        return (0);
+    }
+
+    // Could be duplicates, especially in the answer section, so sort the
+    // names and remove them.
+    sort(source.begin(), source.end(), ResponseScrubber::compareNameLt);
+    vector<const Name*>::iterator endunique =
+        unique(source.begin(), source.end(), ResponseScrubber::compareNameEq);
+    source.erase(endunique, source.end());
+
+    // Now purge the authority section of RRsets that are not equal to or a
+    // superdomain of the names in the question/answer section.
+    return (scrubSection(message, source,
+        NameComparisonResult::SUPERDOMAIN, Message::SECTION_AUTHORITY));
+
+}
+
+// Scrub a message
+
+unsigned int
+ResponseScrubber::scrub(const isc::dns::MessagePtr& message,
+    const isc::dns::Name& bailiwick)
+{
+    unsigned int sections_removed = scrubAllSections(*message, bailiwick);
+    sections_removed += scrubCrossSections(*message);
+
+    return sections_removed;
+}
+
+
diff --git a/src/bin/resolver/response_scrubber.h b/src/bin/resolver/response_scrubber.h
new file mode 100644
index 0000000..971de64
--- /dev/null
+++ b/src/bin/resolver/response_scrubber.h
@@ -0,0 +1,422 @@
+// 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.
+
+// $Id$
+
+#ifndef __RESPONSE_SCRUBBER_H
+#define __RESPONSE_SCRUBBER_H
+
+/// \page DataScrubbing Data Scrubbing
+/// \section DataScrubbingIntro Introduction
+/// When a response is received from an authoritative server, it should be
+/// checked to ensure that the data contained in it is valid.  Signed data is
+/// not a problem - validating the signatures is a sufficient check.  But
+/// unsigned data in a response is more of a problem. (Note that even data from
+/// signed zones may be not be signed, e.g. delegations are not signed.) In
+/// particular, how do we know that the server from which the response was
+/// received was authoritive for the data it returned?
+///
+/// The part of the code that checks for this is the "Data Scrubbing" module.
+/// Although it includes the checking of IP addresses and ports, it is called
+/// "Scrubbing" because it "scrubs" the returned message and removes doubtful
+/// information.
+///
+/// \section DataScrubbingBasic Basic Checks
+/// The first part - how do we know that the response comes from the correct
+/// server - is relatively trivial, albeit not foolproof (which is why DNSSEC
+/// was developed).  The following are checked:
+///
+/// - The IP address from which the response was received is the same as the
+///   one to which the query was sent.
+/// - The port on which the response was received is the same as the one from
+///   which the query was sent.
+///
+/// (These tests need not not done for a TCP connection - if data is received
+/// over the TCP stream, it is assumed that it comes from the address and port
+/// to which a connection was made.)
+///
+/// - The protocol used to send the question is the same as the protocol on
+///   which an answer was received.
+///
+/// (Strictly speaking, if this check fails it is a programming error - the
+/// code should not mix up UPD and TCP messages.)
+///
+/// - The QID in the response message is the same as the QID in the query
+///   message sent.
+///
+/// If the conditions are met, then the data - in all three response sections -
+/// is scanned and out of bailiwick data is removed ("scrubbed").
+///
+/// \section DataScrubbingBailiwick Bailiwick
+/// Bailiwick means "district or jurisdiction of bailie or bailiff" (Concise
+/// Oxford Dictionary, 7th Edition).  It is not a term mentioned in any RFC
+/// (or at least, any RFC up to RFC 5997) but is widely used in DNS literature.
+/// In this context it is taken to mean the data for which a DNS server has
+/// authority.  So when we speak of the information being "in bailiwick", we
+/// mean that the the server is the ultimate source of authority for that data.
+///
+/// In practice, determining this from the response alone is difficult.  In
+/// particular, as a server may be authoritative for many zones, it could in
+/// theory be authoritative for any combination of RRsets that appear in a
+/// response.
+///
+/// For this reason, bailiwick is dependent on the query.  If, for example, a
+/// query for www.example.com is sent to the nameservers for example.com
+/// (because of a referral of from the com. servers), the bailiwick for the
+/// query is example.com.  This means that any information returned on domains
+/// other than example.com may not be authoritative.  More exactly, it may be
+/// authoritative (because the server is also authoritative for the zone
+/// concerned), but based on the information available (in this example, that
+/// the response originated from a nameserver for the zone example.com) it is
+/// not possible to be certain.
+///
+/// Ideally, out of bailiwick data should be excluded from further processing
+/// as it may be incorrect and corrupt the cache.  In practice, there are
+/// two cases to consider:
+///
+/// The first is when the data has a qname that is not example.com or a
+/// subdomain of it (e.g. xyz.com, www.example.net).  In this case the data can
+/// be retrieved by an independent query - no path from the root zone to the
+/// data goes through the current bailiwick, so there is no chance of ending up
+/// in a loop.  In this case, data that appears to be out of bailiwick can be
+/// dropped from the response.
+///
+/// The second case is when the QNAME of the data is a subdomain of the
+/// bailiwick.  Here the server may or may not be authoritative for the data.
+/// For example, if the name queried for were www.sub.example.com and the
+/// example.com nameservers supplied an answer:
+///
+/// - The answer could be authoritative - www.sub.example.com could be
+///   in the example.com zone.
+/// - The answer might not be authoritative - the zone sub.example.com may have
+///   been delegated, so the authoritative answer should come from
+///   sub.example.com's nameservers.
+/// - The answer might be authoritative even though zone sub.example.com has
+///   been delegated, because the nameserver for example.com is the same as
+///   that for sub.example.com.
+///
+/// Unlike the previous case, it is not possible to err on the side of caution
+/// and drop such data.  Any independent query for it will pass through the
+/// current bailiwick and the same question will be asked again.  For this
+/// reason, any data in the response that has a QNAME equal to a subdomain of
+/// the bailiwick has to be accepted.
+///
+/// In summary then, data in a response that has a QNAME equal to or a subdomain
+/// of the bailiwick is considered in-bailiwick.  Anything else is out of of
+/// bailiwick.
+///
+/// \subsection DataScrubbingCrossSection Cross-Section Scrubbing
+/// Even with the bailiwick checks above, there are some additional cleaning
+/// that can be done with the packet.  In particular:
+///
+/// - The QNAMEs of the RRsets in the authority section must be equal to or
+///   superdomains of a QNAME of an RRset in the answer.  Any that are not
+///   should be removed.
+/// - If there is no answer section, the QNAMES of RRsets in the authority
+///   section must be equal to or superdomains of the QNAME of the RRset in the
+///   question.
+///
+/// Although previous checks should have removed some inconsistencies, it
+/// will not trap obscure cases (e.g. bailiwick: "example.com", answer:
+/// "www.example.com", authority: sub.example.com).  These checks do just that.
+///
+/// (Note that not included here is QNAME of question not equal to or a
+/// superdomain of the answer; that check is made in the ResponseClassifier
+/// class.)
+///
+/// \section DataScrubbingExample Examples
+/// Some examples should make this clear: they all use the notation
+/// Qu = Question, Zo = Zone being queried, An = Answer, Au = Authority,
+/// Ad = Additional.
+///
+/// \subsection DataScrubbingEx1 Example 1: Simple Query
+/// Querying a nameserver for the zone "example.com" for www.example.com and
+/// receiving the answer "www.example.com A 1.2.3.4" with two nameservers quoted
+/// as authority and both their addresses in the additional section:
+///
+/// Qu: www.example.com\n
+/// Zo: example.com
+///
+/// An: www.example.com A 192.0.2.1
+///
+/// Au(1): example.com NS ns0.example.com\n
+/// Au(2): example.com NS ns1.example.net
+///
+/// Ad(1): ns0.example.com A 192.0.2.100\n
+/// Ad(2): ns1.example.net A 192.0.2.200
+///
+/// This answer could be returned by a properly configured server.  All resource
+/// records in the answer - with the exception of Ad(2) - are in bailiwick
+/// because the QNAME is equal to or a subdomain of the zone being queried.
+///
+/// It is permissible for Ad(2) to be returned by a properly configured server
+/// as a hint to resolvers.  However the example.com nameservers are not
+/// authoritative for addresses of domains in example.net; that record could
+/// be out of date or incorrect.  Indeed, it might even be a deliberate attempt
+/// at a spoof by getting us to cache an invalid address for ns1.example.net.
+/// The safest thing to do is to drop the A record and to get the address of
+/// ns1.example.net by querying for that name through the .net nameservers.
+///
+/// \subsection DataScrubbingEx2 Example 2: Multiple Zones on Same Nameserver
+/// Assume now that example.com and sub.example.com are hosted on the same
+/// nameserver and that from the .com zone the resolver has received a referral
+/// to example.com.  Suppose that the query is for www.sub.example.com and that
+/// the following response is received:
+///
+/// Qu: www.sub.example.com\n
+/// Zo: example.com
+///
+/// An: <nothing>
+///
+/// Au(1): sub.example.com NS ns0.sub.example.com\n
+/// Au(2): sub.example.com NS ns1.example.net
+///
+/// Ad(1): ns0.sub.example.com A 192.0.2.101\n
+/// Ad(2): ns1.example.net A 192.0.2.201
+///
+/// Although we asked the example.com nameservers for information, we got the
+/// nameservers for sub.example.com in the authority section.  This is valid
+/// because if BIND-10 hosts multiple zones, it will look up the data in the
+/// zone that most closely matches the query.
+///
+/// Using the criteria above, the data in the additional section can therefore
+/// be regarded as in bailiwick because sub.example.com is a subdomain of
+/// example.com.  As before though, the address for ns1.example.net in the
+/// additional section is not in bailiwick because ns1.example.net is now a
+/// subdomain of example.com.
+///
+/// \subsection DataScrubbingEx3 Example 3: Deliberate Spoof Attempt
+/// Qu: www.example.com\n
+/// Zo: example.com
+///
+/// An: www.example.com A 192.0.2.1
+///
+/// Au(1): com NS ns0.example.com\n
+/// Au(2): com NS ns1.example.net
+///
+/// Ad(1): ns0.example.com A 192.0.2.100\n
+/// Ad(2): ns1.example.net A 192.0.2.200
+///
+/// This is a deliberately invalid response.  The query is being sent to the
+/// nameservers for example.com (presumably because a referral to example.com
+/// was received from the com nameservers), but the response is an attempt
+/// to get the specified nameservers cached as the nameservers for com - for
+/// which example.com is not authoritative.
+///
+/// Note though that this response is only invalid because, due to the previous
+/// referral, the query was sent to the example.com nameservers.  Had the
+/// referral been to the com nameservers, it would be a valid response; the com
+/// zone could well be serving all the data for example.com.  Having said that,
+/// the A record for ns1.example.net would still be regarded as being out of
+/// bailiwick becase the nameserver is not authoritative for the .net zone.
+///
+/// \subsection DataScrubbingEx4 Example 4: Inconsistent Answer Section
+/// Qu: www.example.com\n
+/// Zo: example.com
+///
+/// An: www.example.com A 192.0.2.1
+///
+/// Au(1): alpha.example.com NS ns0.example.com\n
+/// Au(2): alpha.example.com NS ns1.example.net
+///
+/// Ad(1): ns0.example.com A 192.0.2.100\n
+/// Ad(2): ns1.example.net A 192.0.2.200
+///
+/// Here, everything in the answer and authority sections is in bailiwick for
+/// the example.com server. And although the zone example.com was queried, it
+/// is permissible for the authority section to contain nameservers with a
+/// qname that is a subdomain of example.com (e.g. see \ref DataScrubbingEx2).
+/// However, only servers with a qname that is equal to or a superdomain of
+/// the answer are authoritative for the answer.  So in this case, both
+/// Au(1) and Au(2) (as well as Ad(2), for reasons given earlier) will be
+/// scrubbed.
+
+#include <config.h>
+#include <asiolink/ioendpoint.h>
+#include <dns/message.h>
+#include <dns/name.h>
+
+/// \brief Response Data Scrubbing
+///
+/// This is the class that implements the data scrubbing.  Given a response
+/// message and some additional information, it checks the information using
+/// the rules given in \ref DataScrubbing and either rejects the packet or
+/// modifies it to remove non-conforming RRsets.
+///
+/// TODO: Examine the additional records and remove all cases where the
+/// QNAME does not match the RDATA of records in the authority section.
+
+class ResponseScrubber {
+public:
+
+    /// \brief Response Code for Address Check
+    enum Category {
+        SUCCESS = 0,            ///< Packet is OK
+
+        // Error categories
+
+        ADDRESS = 1,            ///< Mismatching IP address
+        PORT = 2,               ///< Mismatching port
+        PROTOCOL = 3            ///< Mismatching protocol
+    };
+
+    /// \brief Check IP Address
+    ///
+    /// Compares the address to which the query was sent, the port it was
+    /// sent from, and the protocol used for communication with the (address,
+    /// port, protocol) from which the response was received.
+    ///
+    /// \param to Endpoint representing the address to which the query was sent.
+    /// \param from Endpoint from which the response was received.
+    ///
+    /// \return SUCCESS if the two endpoints match, otherwise an error status
+    /// indicating what was incorrect.
+    static Category addressCheck(const asiolink::IOEndpoint& to,
+        const asiolink::IOEndpoint& from);
+
+    /// \brief Check QID
+    ///
+    /// Compares the QID in the sent message with the QID in the response.
+    ///
+    /// \param sent Message sent to the authoritative server
+    /// \param received Message received from the authoritative server
+    ///
+    /// \return true if the QIDs match, false otherwise.
+    static bool qidCheck(const isc::dns::Message& sent,
+        const isc::dns::Message& received) {
+        return (sent.getQid() == received.getQid());
+    }
+
+    /// \brief Generalised Scrub Message Section
+    ///
+    /// When scrubbing a message given the bailiwick of the server, RRsets are
+    /// retained in the message section if the QNAME is equal to or a subdomain
+    /// of the bailiwick.  However, when checking QNAME of RRsets in the
+    /// authority section against the QNAME of the question or answers, RRsets
+    /// are retained only if their QNAME is equal to or a superdomain of the
+    /// name in question.
+    ///
+    /// This method provides the generalised scrubbing whereby the RRsets in
+    /// a section are tested against a given name, and RRsets kept if their
+    /// QNAME is equal to or in the supplied relationship with the given name.
+    ///
+    /// \param section Section of the message to be scrubbed.
+    /// \param zone Names against which RRsets should be checked.  Note that
+    /// this is a vector of pointers to Name objects; they are assumed to
+    /// independently exist, and the caller retains ownership of them and is
+    /// assumed to destroy them when needed.
+    /// \param connection Relationship required for retention, i.e. the QNAME of
+    /// an RRset in the specified section must be equal to or a "connection"
+    /// (SUPERDOMAIN/SUBDOMAIN) of "name" for the RRset to be retained.
+    /// \param message Message to be scrubbed.
+    ///
+    /// \return Count of the number of RRsets removed from the section.
+    static unsigned int scrubSection(isc::dns::Message& message,
+        const std::vector<const isc::dns::Name*>& names,
+        const isc::dns::NameComparisonResult::NameRelation connection,
+        const isc::dns::Message::Section section);
+
+    /// \brief Scrub All Sections of a Message
+    ///
+    /// Scrubs each of the answer, authority and additional sections of the
+    /// message.
+    ///
+    /// No distinction is made between RRsets legitimately in the message (e.g.
+    /// glue for authorities that are not in bailiwick) and ones that could be
+    /// considered as attempts of spoofing (e.g. non-bailiwick RRsets in the
+    /// additional section that are not related to the query).
+    ///
+    /// The resultant packet returned to the caller may be invalid.  If so, it
+    /// is up to the caller to detect that.
+    ///
+    /// \param message Message to be scrubbed.
+    /// \param bailiwick Name of the zone whose authoritative servers were
+    /// queried.
+    ///
+    /// \return Count of the number of RRsets removed from the message.
+    static unsigned int scrubAllSections(isc::dns::Message& message,
+        const isc::dns::Name& bailiwick);
+
+    /// \brief Scrub Across Message Sections
+    ///
+    /// Does some cross-section comparisons and removes inconsistent RRs.  In
+    /// particular it:
+    ///
+    /// - If an answer is present, checks that the qname of the authority RRs
+    ///   are equal to or superdomain of the qname answer RRsets.  Any that are
+    ///   not are removed.
+    /// - If an answer is not present, checks that the authority RRs are
+    ///   equal to or superdomains of the question.  If not, the authority RRs
+    ///   are removed.
+    ///
+    /// Note that the scrubbing does not check:
+    ///
+    /// - that the question is in the bailiwick of the server; that check is
+    ///   assumed to have been done prior to the query being sent (else why
+    ///   was the query sent there in the first place?)
+    /// - that the qname of one of the RRsets in the answer (if present) is
+    ///   equal to the qname of the question (that check is done in the
+    ///   response classification code).
+    ///
+    /// \param message Message to be scrubbed.
+    ///
+    /// \return Count of the number of RRsets removed from the section.
+    static unsigned int scrubCrossSections(isc::dns::Message& message);
+    
+    /// \brief Main Scrubbing Entry Point
+    ///
+    /// The single entry point to the module to sanitise the message.  All
+    /// it does is call the various other scrubbing methods.
+    ///
+    /// \param message Pointer to the message to be scrubbed. (This is a
+    /// pointer - as opposed to a Message as in other methods in this class -
+    /// as the external code is expected to be mainly using message pointers
+    /// to access messages.)
+    /// \param bailiwick Name of the zone whose authoritative servers were
+    /// queried.
+    ///
+    /// \return Count of the number of RRsets removed from the message.
+    static unsigned int scrub(const isc::dns::MessagePtr& message,
+        const isc::dns::Name& bailiwick);
+
+    /// \brief Comparison Function for Sorting Name Pointers
+    ///
+    /// Utility method called to sorts pointers to names in lexical order.
+    ///
+    /// \param n1 Pointer to first Name object
+    /// \param n2 Pointer to second Name object
+    ///
+    /// \return true if n1 is less than n2, false otherwise.
+    static bool compareNameLt(const isc::dns::Name* n1,
+        const isc::dns::Name* n2)
+    {
+        return (*n1 < *n2);
+    }
+
+    /// \brief Function for Comparing Name Pointers
+    ///
+    /// Utility method called to sorts pointers to names in lexical order.
+    ///
+    /// \param n1 Pointer to first Name object
+    /// \param n2 Pointer to second Name object
+    ///
+    /// \return true if n1 is equal to n2, false otherwise.
+    static bool compareNameEq(const isc::dns::Name* n1,
+        const isc::dns::Name* n2)
+    {
+        return (*n1 == *n2);
+    }
+};
+
+#endif // __RESPONSE_SCRUBBER_H
diff --git a/src/bin/resolver/tests/Makefile.am b/src/bin/resolver/tests/Makefile.am
index 405a44f..8bebff0 100644
--- a/src/bin/resolver/tests/Makefile.am
+++ b/src/bin/resolver/tests/Makefile.am
@@ -4,7 +4,6 @@ AM_CPPFLAGS += -I$(top_builddir)/src/lib/cc
 AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(top_srcdir)/src/lib/testutils/testdata\"
 AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/lib/testutils/testdata\"
 AM_CPPFLAGS += $(BOOST_INCLUDES)
-AM_CPPFLAGS += $(BOOST_INCLUDES)
 
 AM_CXXFLAGS = $(B10_CXXFLAGS)
 
@@ -20,8 +19,12 @@ TESTS += run_unittests
 run_unittests_SOURCES = $(top_srcdir)/src/lib/dns/tests/unittest_util.h
 run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
 run_unittests_SOURCES += ../resolver.h ../resolver.cc
+run_unittests_SOURCES += ../response_classifier.h ../response_classifier.cc
+run_unittests_SOURCES += ../response_scrubber.h ../response_scrubber.cc
 run_unittests_SOURCES += resolver_unittest.cc
 run_unittests_SOURCES += resolver_config_unittest.cc
+run_unittests_SOURCES += response_classifier_unittest.cc
+run_unittests_SOURCES += response_scrubber_unittest.cc
 run_unittests_SOURCES += run_unittests.cc
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
@@ -36,6 +39,15 @@ run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la
 run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 run_unittests_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
 run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
+
+# Note the ordering matters: -Wno-... must follow -Wextra (defined in
+# B10_CXXFLAGS
+run_unittests_CXXFLAGS = $(AM_CXXFLAGS)
+if USE_GXX
+run_unittests_CXXFLAGS += -Wno-unused-parameter
+endif
 endif
 
+
+
 noinst_PROGRAMS = $(TESTS)
diff --git a/src/bin/resolver/tests/resolver_config_unittest.cc b/src/bin/resolver/tests/resolver_config_unittest.cc
index 9d5940c..916396a 100644
--- a/src/bin/resolver/tests/resolver_config_unittest.cc
+++ b/src/bin/resolver/tests/resolver_config_unittest.cc
@@ -96,6 +96,31 @@ TEST_F(ResolverConfig, forwardAddressConfig) {
     EXPECT_EQ(0, server.getForwardAddresses().size());
 }
 
+TEST_F(ResolverConfig, rootAddressConfig) {
+    // Try putting there some address
+    ElementPtr config(Element::fromJSON("{"
+        "\"root_addresses\": ["
+        "   {"
+        "       \"address\": \"192.0.2.1\","
+        "       \"port\": 53"
+        "   }"
+        "]"
+        "}"));
+    ConstElementPtr result(server.updateConfig(config));
+    EXPECT_EQ(result->toWire(), isc::config::createAnswer()->toWire());
+    ASSERT_EQ(1, server.getRootAddresses().size());
+    EXPECT_EQ("192.0.2.1", server.getRootAddresses()[0].first);
+    EXPECT_EQ(53, server.getRootAddresses()[0].second);
+
+    // And then remove all addresses
+    config = Element::fromJSON("{"
+        "\"root_addresses\": null"
+        "}");
+    result = server.updateConfig(config);
+    EXPECT_EQ(result->toWire(), isc::config::createAnswer()->toWire());
+    EXPECT_EQ(0, server.getRootAddresses().size());
+}
+
 void
 ResolverConfig::invalidTest(const string &JOSN) {
     ElementPtr config(Element::fromJSON(JOSN));
@@ -134,8 +159,8 @@ TEST_F(ResolverConfig, listenAddresses) {
 
     // Try putting there some addresses
     vector<pair<string, uint16_t> > addresses;
-    addresses.push_back(pair<string, uint16_t>("127.0.0.1", 5300));
-    addresses.push_back(pair<string, uint16_t>("::1", 5300));
+    addresses.push_back(pair<string, uint16_t>("127.0.0.1", 5321));
+    addresses.push_back(pair<string, uint16_t>("::1", 5321));
     server.setListenAddresses(addresses);
     EXPECT_EQ(2, server.getListenAddresses().size());
     EXPECT_EQ("::1", server.getListenAddresses()[1].first);
@@ -155,7 +180,7 @@ TEST_F(ResolverConfig, DISABLED_listenAddressConfig) {
         "\"listen_on\": ["
         "   {"
         "       \"address\": \"127.0.0.1\","
-        "       \"port\": 5300"
+        "       \"port\": 5321"
         "   }"
         "]"
         "}"));
@@ -163,7 +188,7 @@ TEST_F(ResolverConfig, DISABLED_listenAddressConfig) {
     EXPECT_EQ(result->toWire(), isc::config::createAnswer()->toWire());
     ASSERT_EQ(1, server.getListenAddresses().size());
     EXPECT_EQ("127.0.0.1", server.getListenAddresses()[0].first);
-    EXPECT_EQ(5300, server.getListenAddresses()[0].second);
+    EXPECT_EQ(5321, server.getListenAddresses()[0].second);
 
     // As this is example address, the machine should not have it on
     // any interface
@@ -174,7 +199,7 @@ TEST_F(ResolverConfig, DISABLED_listenAddressConfig) {
         "\"listen_on\": ["
         "   {"
         "       \"address\": \"192.0.2.0\","
-        "       \"port\": 5300"
+        "       \"port\": 5321"
         "   }"
         "]"
         "}");
@@ -182,7 +207,7 @@ TEST_F(ResolverConfig, DISABLED_listenAddressConfig) {
     EXPECT_FALSE(result->equals(*isc::config::createAnswer()));
     ASSERT_EQ(1, server.getListenAddresses().size());
     EXPECT_EQ("127.0.0.1", server.getListenAddresses()[0].first);
-    EXPECT_EQ(5300, server.getListenAddresses()[0].second);
+    EXPECT_EQ(5321, server.getListenAddresses()[0].second);
 }
 
 TEST_F(ResolverConfig, invalidListenAddresses) {
@@ -212,31 +237,51 @@ TEST_F(ResolverConfig, invalidListenAddresses) {
 
 // Just test it sets and gets the values correctly
 TEST_F(ResolverConfig, timeouts) {
-    server.setTimeouts(0, 1);
-    EXPECT_EQ(0, server.getTimeouts().first);
-    EXPECT_EQ(1, server.getTimeouts().second);
+    server.setTimeouts(0, 1, 2, 3);
+    EXPECT_EQ(0, server.getQueryTimeout());
+    EXPECT_EQ(1, server.getClientTimeout());
+    EXPECT_EQ(2, server.getLookupTimeout());
+    EXPECT_EQ(3, server.getRetries());
     server.setTimeouts();
-    EXPECT_EQ(-1, server.getTimeouts().first);
-    EXPECT_EQ(0, server.getTimeouts().second);
+    EXPECT_EQ(2000, server.getQueryTimeout());
+    EXPECT_EQ(4000, server.getClientTimeout());
+    EXPECT_EQ(30000, server.getLookupTimeout());
+    EXPECT_EQ(3, server.getRetries());
 }
 
 TEST_F(ResolverConfig, timeoutsConfig) {
     ElementPtr config = Element::fromJSON("{"
-            "\"timeout\": 1000,"
-            "\"retries\": 3"
+            "\"timeout_query\": 1000,"
+            "\"timeout_client\": 2000,"
+            "\"timeout_lookup\": 3000,"
+            "\"retries\": 4"
             "}");
     ConstElementPtr result(server.updateConfig(config));
     EXPECT_EQ(result->toWire(), isc::config::createAnswer()->toWire());
-    EXPECT_EQ(1000, server.getTimeouts().first);
-    EXPECT_EQ(3, server.getTimeouts().second);
+    EXPECT_EQ(1000, server.getQueryTimeout());
+    EXPECT_EQ(2000, server.getClientTimeout());
+    EXPECT_EQ(3000, server.getLookupTimeout());
+    EXPECT_EQ(4, server.getRetries());
 }
 
 TEST_F(ResolverConfig, invalidTimeoutsConfig) {
     invalidTest("{"
-        "\"timeout\": \"error\""
+        "\"timeout_query\": \"error\""
+        "}");
+    invalidTest("{"
+        "\"timeout_query\": -2"
+        "}");
+    invalidTest("{"
+        "\"timeout_client\": \"error\""
+        "}");
+    invalidTest("{"
+        "\"timeout_client\": -2"
+        "}");
+    invalidTest("{"
+        "\"timeout_lookup\": \"error\""
         "}");
     invalidTest("{"
-        "\"timeout\": -2"
+        "\"timeout_lookup\": -2"
         "}");
     invalidTest("{"
         "\"retries\": \"error\""
diff --git a/src/bin/resolver/tests/resolver_unittest.cc b/src/bin/resolver/tests/resolver_unittest.cc
index 240c9d7..a4f11f5 100644
--- a/src/bin/resolver/tests/resolver_unittest.cc
+++ b/src/bin/resolver/tests/resolver_unittest.cc
@@ -16,6 +16,7 @@
 
 #include <resolver/resolver.h>
 #include <dns/tests/unittest_util.h>
+#include <testutils/dnsmessage_test.h>
 #include <testutils/srv_test.h>
 
 using namespace isc::dns;
@@ -29,7 +30,10 @@ class ResolverTest : public SrvTestBase{
 protected:
     ResolverTest() : server(){}
     virtual void processMessage() {
-        server.processMessage(*io_message, parse_message, response_obuffer,
+        server.processMessage(*io_message,
+                              parse_message,
+                              response_message,
+                              response_obuffer,
                               &dnsserv);
     }
     Resolver server;
@@ -82,7 +86,11 @@ TEST_F(ResolverTest, AXFRFail) {
                                        RRType::AXFR());
     createRequestPacket(request_message, IPPROTO_TCP);
     // AXFR is not implemented and should always send NOTIMP.
-    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+    server.processMessage(*io_message,
+                          parse_message,
+                          response_message,
+                          response_obuffer,
+                          &dnsserv);
     EXPECT_TRUE(dnsserv.hasAnswer());
     headerCheck(*parse_message, default_qid, Rcode::NOTIMP(), opcode.getCode(),
                 QR_FLAG, 1, 0, 0, 0);
@@ -97,7 +105,11 @@ TEST_F(ResolverTest, notifyFail) {
     request_message.setQid(default_qid);
     request_message.setHeaderFlag(Message::HEADERFLAG_AA);
     createRequestPacket(request_message, IPPROTO_UDP);
-    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+    server.processMessage(*io_message,
+                          parse_message,
+                          response_message,
+                          response_obuffer,
+                          &dnsserv);
     EXPECT_TRUE(dnsserv.hasAnswer());
     headerCheck(*parse_message, default_qid, Rcode::NOTAUTH(),
                 Opcode::NOTIFY().getCode(), QR_FLAG, 0, 0, 0, 0);
diff --git a/src/bin/resolver/tests/response_classifier_unittest.cc b/src/bin/resolver/tests/response_classifier_unittest.cc
new file mode 100644
index 0000000..ee439b1
--- /dev/null
+++ b/src/bin/resolver/tests/response_classifier_unittest.cc
@@ -0,0 +1,494 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <iostream>
+#include <gtest/gtest.h>
+
+#include <dns/tests/unittest_util.h>
+
+#include <resolver/response_classifier.h>
+
+#include <dns/name.h>
+#include <dns/opcode.h>
+#include <dns/question.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/rcode.h>
+#include <dns/rrclass.h>
+#include <dns/rrset.h>
+#include <dns/rrtype.h>
+#include <dns/rrttl.h>
+
+using namespace std;
+using namespace isc::dns;
+using namespace rdata;
+using namespace isc::dns::rdata::generic;
+using namespace isc::dns::rdata::in;
+
+namespace {
+class ResponseClassifierTest : public ::testing::Test {
+public:
+    /// \brief Constructor
+    ///
+    /// The naming convention is:
+    ///
+    /// <category>_<class>_<type>_<name>
+    ///
+    /// <category> is "qu" (question), "rrs" (rrset),
+    /// <qclass> is self-explanatory
+    /// <qtype> is self-explanatory
+    /// <name> is the first part of the domain name (all expected to be in
+    /// example.com)
+    ///
+    /// Message variables
+    ///
+    /// msg_<qtype>  Where <qtype> is the type of query.  These are only used
+    /// in the early tests where simple messages are required.
+
+    ResponseClassifierTest() :
+        msg_a(new Message(Message::RENDER)),
+        msg_any(new Message(Message::RENDER)),
+        qu_ch_a_www(Name("www.example.com"), RRClass::CH(), RRType::A()),
+        qu_in_any_www(Name("www.example.com"), RRClass::IN(), RRType::ANY()),
+        qu_in_a_www2(Name("www2.example.com"), RRClass::IN(), RRType::A()),
+        qu_in_a_www(Name("www.example.com"), RRClass::IN(), RRType::A()),
+        qu_in_cname_www1(Name("www1.example.com"), RRClass::IN(), RRType::A()),
+        qu_in_ns_(Name("example.com"), RRClass::IN(), RRType::NS()),
+        qu_in_txt_www(Name("www.example.com"), RRClass::IN(), RRType::TXT()),
+        rrs_hs_txt_www(new RRset(Name("www.example.com"), RRClass::HS(),
+            RRType::TXT(), RRTTL(300))),
+        rrs_in_a_mail(new RRset(Name("mail.example.com"), RRClass::IN(),
+            RRType::A(), RRTTL(300))),
+        rrs_in_a_www(new RRset(Name("www.example.com"), RRClass::IN(),
+            RRType::A(), RRTTL(300))),
+        rrs_in_cname_www1(new RRset(Name("www1.example.com"), RRClass::IN(),
+            RRType::CNAME(), RRTTL(300))),
+        rrs_in_cname_www2(new RRset(Name("www2.example.com"), RRClass::IN(),
+            RRType::CNAME(), RRTTL(300))),
+        rrs_in_ns_(new RRset(Name("example.com"), RRClass::IN(),
+            RRType::NS(), RRTTL(300))),
+        rrs_in_txt_www(new RRset(Name("www.example.com"), RRClass::IN(),
+            RRType::TXT(), RRTTL(300)))
+    {
+        // Set up the message to indicate a successful response to the question
+        // "www.example.com A", but don't add in any response sections.
+        msg_a->setHeaderFlag(Message::HEADERFLAG_QR);
+        msg_a->setOpcode(Opcode::QUERY());
+        msg_a->setRcode(Rcode::NOERROR());
+        msg_a->addQuestion(qu_in_a_www);
+
+        // ditto for the query "www.example.com ANY"
+        msg_any->setHeaderFlag(Message::HEADERFLAG_QR);
+        msg_any->setOpcode(Opcode::QUERY());
+        msg_any->setRcode(Rcode::NOERROR());
+        msg_any->addQuestion(qu_in_any_www);
+
+        // The next set of assignments set up the following zone records
+        //
+        // example.com           NS     ns0.isc.org
+        //                       NS     ns0.example.org
+        //
+        // www.example.com       A      1.2.3.4
+        //                       TXT    "An example text string"
+        //
+        // mail.example.com      A      4.5.6.7
+        //
+        // www1.example.com      CNAME  www.example.com
+        //
+        // www2.example.com      CNAME  www1.example.com
+
+        // Set up an imaginary NS RRset for an authority section
+        rrs_in_ns_->addRdata(ConstRdataPtr(new NS(Name("ns0.isc.org"))));
+        rrs_in_ns_->addRdata(ConstRdataPtr(new NS(Name("ns0.example.org"))));
+
+        // Set up the records for the www host
+        rrs_in_a_www->addRdata(ConstRdataPtr(new A("1.2.3.4")));
+        rrs_in_txt_www->addRdata(ConstRdataPtr(
+            new TXT("An example text string")));
+
+        // ... for the mail host
+        rrs_in_a_mail->addRdata(ConstRdataPtr(new A("5.6.7.8")));
+
+        // ... the CNAME records
+        rrs_in_cname_www1->addRdata(ConstRdataPtr(
+            new CNAME("www.example.com")));
+        rrs_in_cname_www2->addRdata(ConstRdataPtr(
+            new CNAME("www1.example.com")));
+    }
+
+    MessagePtr  msg_a;              // Pointer to message in RENDER state
+    MessagePtr  msg_any;            // Pointer to message in RENDER state
+    Question    qu_ch_a_www;        // www.example.com CH A
+    Question    qu_in_any_www;      // www.example.com IN ANY
+    Question    qu_in_a_www2;       // www.example.com IN ANY
+    Question    qu_in_a_www;        // www.example.com IN A
+    Question    qu_in_cname_www1;   // www1.example.com IN CNAME
+    Question    qu_in_ns_;          // example.com IN NS
+    Question    qu_in_txt_www;      // www.example.com IN TXT
+    RRsetPtr    rrs_hs_txt_www;     // www.example.com HS TXT
+    RRsetPtr    rrs_in_a_mail;      // mail.example.com IN A
+    RRsetPtr    rrs_in_a_www;       // www.example.com IN A
+    RRsetPtr    rrs_in_cname_www1;  // www1.example.com IN CNAME
+    RRsetPtr    rrs_in_cname_www2;  // www2.example.com IN CNAME
+    RRsetPtr    rrs_in_ns_;         // example.com IN NS
+    RRsetPtr    rrs_in_txt_www;     // www.example.com IN TXT
+};
+
+// Test that the error() function categorises the codes correctly.
+
+TEST_F(ResponseClassifierTest, StatusCodes) {
+    EXPECT_FALSE(ResponseClassifier::error(ResponseClassifier::ANSWER));
+    EXPECT_FALSE(ResponseClassifier::error(ResponseClassifier::ANSWERCNAME));
+    EXPECT_FALSE(ResponseClassifier::error(ResponseClassifier::CNAME));
+    EXPECT_FALSE(ResponseClassifier::error(ResponseClassifier::NXDOMAIN));
+    EXPECT_FALSE(ResponseClassifier::error(ResponseClassifier::REFERRAL));
+
+    EXPECT_TRUE(ResponseClassifier::error(ResponseClassifier::EMPTY));
+    EXPECT_TRUE(ResponseClassifier::error(ResponseClassifier::EXTRADATA));
+    EXPECT_TRUE(ResponseClassifier::error(ResponseClassifier::INVNAMCLASS));
+    EXPECT_TRUE(ResponseClassifier::error(ResponseClassifier::INVTYPE));
+    EXPECT_TRUE(ResponseClassifier::error(ResponseClassifier::MISMATQUEST));
+    EXPECT_TRUE(ResponseClassifier::error(ResponseClassifier::MULTICLASS));
+    EXPECT_TRUE(ResponseClassifier::error(ResponseClassifier::NOTONEQUEST));
+    EXPECT_TRUE(ResponseClassifier::error(ResponseClassifier::NOTRESPONSE));
+    EXPECT_TRUE(ResponseClassifier::error(ResponseClassifier::NOTSINGLE));
+    EXPECT_TRUE(ResponseClassifier::error(ResponseClassifier::OPCODE));
+    EXPECT_TRUE(ResponseClassifier::error(ResponseClassifier::RCODE));
+    EXPECT_TRUE(ResponseClassifier::error(ResponseClassifier::TRUNCATED));
+}
+
+// Test that the system will reject a message which is a query.
+
+TEST_F(ResponseClassifierTest, Query) {
+
+    // Set up message to indicate a query (QR flag = 0, one question).  By
+    // default the opcode will be 0 (query)
+    msg_a->setHeaderFlag(Message::HEADERFLAG_QR, false);
+
+    // Should be rejected as it is a query, not a response
+    EXPECT_EQ(ResponseClassifier::NOTRESPONSE,
+        ResponseClassifier::classify(qu_in_a_www, msg_a));
+}
+
+// Check that we get an OPCODE error on all but QUERY opcodes.
+
+TEST_F(ResponseClassifierTest, Opcode) {
+
+    uint8_t query = static_cast<uint8_t>(Opcode::QUERY().getCode());
+
+    for (uint8_t i = 0; i < (1 << 4); ++i) {
+        msg_a->setOpcode(Opcode(i));
+        if (i == query) {
+            EXPECT_NE(ResponseClassifier::OPCODE,
+                ResponseClassifier::classify(qu_in_a_www, msg_a));
+        } else {
+            EXPECT_EQ(ResponseClassifier::OPCODE,
+                ResponseClassifier::classify(qu_in_a_www, msg_a));
+        }
+    }
+}
+
+// Test that the system will reject a response with anything other than one
+// question.
+
+TEST_F(ResponseClassifierTest, MultipleQuestions) {
+
+    // Create a message object for this test that has no question section.
+    MessagePtr message(new Message(Message::RENDER));
+    message->setHeaderFlag(Message::HEADERFLAG_QR);
+    message->setOpcode(Opcode::QUERY());
+    message->setRcode(Rcode::NOERROR());
+
+    // Zero questions
+    EXPECT_EQ(ResponseClassifier::NOTONEQUEST,
+        ResponseClassifier::classify(qu_in_a_www, message));
+
+    // One question
+    message->addQuestion(qu_in_a_www);
+    EXPECT_NE(ResponseClassifier::NOTONEQUEST,
+        ResponseClassifier::classify(qu_in_a_www, message));
+
+    // Two questions
+    message->addQuestion(qu_in_ns_);
+    EXPECT_EQ(ResponseClassifier::NOTONEQUEST,
+        ResponseClassifier::classify(qu_in_a_www, message));
+
+    // And finish the check with three questions
+    message->addQuestion(qu_in_txt_www);
+    EXPECT_EQ(ResponseClassifier::NOTONEQUEST,
+        ResponseClassifier::classify(qu_in_a_www, message));
+}
+
+// Test that the question in the question section in the message response
+// is equal to the question supplied.
+
+TEST_F(ResponseClassifierTest, SameQuestion) {
+
+    EXPECT_EQ(ResponseClassifier::MISMATQUEST,
+        ResponseClassifier::classify(qu_in_ns_, msg_a));
+    EXPECT_NE(ResponseClassifier::MISMATQUEST,
+        ResponseClassifier::classify(qu_in_a_www, msg_a));
+}
+
+// Should get an NXDOMAIN response only on an NXDOMAIN RCODE.
+
+TEST_F(ResponseClassifierTest, NXDOMAIN) {
+
+    uint16_t nxdomain = static_cast<uint16_t>(Rcode::NXDOMAIN().getCode());
+
+    for (uint8_t i = 0; i < (1 << 4); ++i) {
+        msg_a->setRcode(Rcode(i));
+        if (i == nxdomain) {
+            EXPECT_EQ(ResponseClassifier::NXDOMAIN,
+                ResponseClassifier::classify(qu_in_a_www, msg_a));
+        } else {
+            EXPECT_NE(ResponseClassifier::NXDOMAIN,
+                ResponseClassifier::classify(qu_in_a_www, msg_a));
+        }
+    }
+}
+
+// Check that we get an RCODE error on all but NXDOMAIN and NOERROR responses.
+
+TEST_F(ResponseClassifierTest, RCODE) {
+
+    uint16_t nxdomain = static_cast<uint16_t>(Rcode::NXDOMAIN().getCode());
+    uint16_t noerror = static_cast<uint16_t>(Rcode::NOERROR().getCode());
+
+    for (uint8_t i = 0; i < (1 << 4); ++i) {
+        msg_a->setRcode(Rcode(i));
+        if ((i == nxdomain) || (i == noerror)) {
+            EXPECT_NE(ResponseClassifier::RCODE,
+                ResponseClassifier::classify(qu_in_a_www, msg_a));
+        } else {
+            EXPECT_EQ(ResponseClassifier::RCODE,
+                ResponseClassifier::classify(qu_in_a_www, msg_a));
+        }
+    }
+}
+
+// Test that the code will detect a truncated message.  Even if nothing else
+// is wrong, we'll want to retry the query if we receive a truncated code.
+// However, we give the option to the user of the code aws to whether they
+// want to take into account the truncated bit.
+
+TEST_F(ResponseClassifierTest, Truncated) {
+
+    // Don't expect the truncated code whatever option we ask for if the TC
+    // bit is not set.
+    msg_a->setHeaderFlag(Message::HEADERFLAG_TC, false);
+    EXPECT_NE(ResponseClassifier::TRUNCATED,
+        ResponseClassifier::classify(qu_in_a_www, msg_a, true));
+    EXPECT_NE(ResponseClassifier::TRUNCATED,
+        ResponseClassifier::classify(qu_in_a_www, msg_a, false));
+
+    // Expect the truncated code if the TC bit is set, only if we don't ignore
+    // it.
+    msg_a->setHeaderFlag(Message::HEADERFLAG_TC, true);
+    EXPECT_NE(ResponseClassifier::TRUNCATED,
+        ResponseClassifier::classify(qu_in_a_www, msg_a, true));
+    EXPECT_EQ(ResponseClassifier::TRUNCATED,
+        ResponseClassifier::classify(qu_in_a_www, msg_a, false));
+}
+
+// Check for an empty packet (i.e. no error, but with the answer and additional
+// sections empty).
+
+TEST_F(ResponseClassifierTest, Empty) {
+
+    EXPECT_EQ(ResponseClassifier::EMPTY,
+        ResponseClassifier::classify(qu_in_a_www, msg_a));
+}
+
+// Anything where we have an empty answer section but something in the
+// authority section is a referral (if the status is NOERROR).
+
+TEST_F(ResponseClassifierTest, EmptyAnswerReferral) {
+
+    msg_a->addRRset(Message::SECTION_AUTHORITY, rrs_in_ns_);
+    EXPECT_EQ(ResponseClassifier::REFERRAL,
+        ResponseClassifier::classify(qu_in_a_www, msg_a));
+
+}
+
+// Check the case where we have a simple answer in the answer section.  This
+// occurs when the QNAME/QTYPE/QCLASS matches one of the RRsets in the
+// answer section - expect when the QTYPE is ANY, in which case the match
+// must be on the QNAME/QCLASS alone.
+
+TEST_F(ResponseClassifierTest, SingleAnswer) {
+
+    // Check a question that matches the answer
+    msg_a->addRRset(Message::SECTION_ANSWER, rrs_in_a_www);
+    EXPECT_EQ(ResponseClassifier::ANSWER,
+        ResponseClassifier::classify(qu_in_a_www, msg_a));
+
+    // Check an ANY question that matches the answer
+    msg_any->addRRset(Message::SECTION_ANSWER, rrs_in_a_www);
+    EXPECT_EQ(ResponseClassifier::ANSWER,
+        ResponseClassifier::classify(qu_in_any_www, msg_any));
+
+    // Check a CNAME response that matches the QNAME.
+    MessagePtr message_a(new Message(Message::RENDER));
+    message_a->setHeaderFlag(Message::HEADERFLAG_QR);
+    message_a->setOpcode(Opcode::QUERY());
+    message_a->setRcode(Rcode::NOERROR());
+    message_a->addQuestion(qu_in_cname_www1);
+    message_a->addRRset(Message::SECTION_ANSWER, rrs_in_cname_www1);
+    EXPECT_EQ(ResponseClassifier::CNAME,
+        ResponseClassifier::classify(qu_in_cname_www1, message_a));
+
+    // Check if the answer QNAME does not match the question
+    // Q: www.example.com  IN A
+    // A: mail.example.com IN A
+    MessagePtr message_b(new Message(Message::RENDER));
+    message_b->setHeaderFlag(Message::HEADERFLAG_QR);
+    message_b->setOpcode(Opcode::QUERY());
+    message_b->setRcode(Rcode::NOERROR());
+    message_b->addQuestion(qu_in_a_www);
+    message_b->addRRset(Message::SECTION_ANSWER, rrs_in_a_mail);
+    EXPECT_EQ(ResponseClassifier::INVNAMCLASS,
+        ResponseClassifier::classify(qu_in_a_www, message_b));
+
+    // Check if the answer class does not match the question
+    // Q: www.example.com CH A
+    // A: www.example.com IN A
+    MessagePtr message_c(new Message(Message::RENDER));
+    message_c->setHeaderFlag(Message::HEADERFLAG_QR);
+    message_c->setOpcode(Opcode::QUERY());
+    message_c->setRcode(Rcode::NOERROR());
+    message_c->addQuestion(qu_ch_a_www);
+    message_c->addRRset(Message::SECTION_ANSWER, rrs_in_a_www);
+    EXPECT_EQ(ResponseClassifier::INVNAMCLASS,
+        ResponseClassifier::classify(qu_ch_a_www, message_c));
+
+    // Check if the answer type does not match the question
+    // Q: www.example.com IN A
+    // A: www.example.com IN TXT
+    MessagePtr message_d(new Message(Message::RENDER));
+    message_d->setHeaderFlag(Message::HEADERFLAG_QR);
+    message_d->setOpcode(Opcode::QUERY());
+    message_d->setRcode(Rcode::NOERROR());
+    message_d->addQuestion(qu_in_a_www);
+    message_d->addRRset(Message::SECTION_ANSWER, rrs_in_txt_www);
+    EXPECT_EQ(ResponseClassifier::INVTYPE,
+        ResponseClassifier::classify(qu_in_a_www, message_d));
+}
+
+// Check what happens if we have multiple RRsets in the answer.
+
+TEST_F(ResponseClassifierTest, MultipleAnswerRRsets) {
+
+    // All the same QNAME but different types is only valid on an ANY query.
+    MessagePtr message_a(new Message(Message::RENDER));
+    message_a->setHeaderFlag(Message::HEADERFLAG_QR);
+    message_a->setOpcode(Opcode::QUERY());
+    message_a->setRcode(Rcode::NOERROR());
+    message_a->addQuestion(qu_in_any_www);
+    message_a->addRRset(Message::SECTION_ANSWER, rrs_in_a_www);
+    message_a->addRRset(Message::SECTION_ANSWER, rrs_in_txt_www);
+    EXPECT_EQ(ResponseClassifier::ANSWER,
+        ResponseClassifier::classify(qu_in_any_www, message_a));
+
+    // On another type of query, it results in an EXTRADATA error
+    MessagePtr message_b(new Message(Message::RENDER));
+    message_b->setHeaderFlag(Message::HEADERFLAG_QR);
+    message_b->setOpcode(Opcode::QUERY());
+    message_b->setRcode(Rcode::NOERROR());
+    message_b->addQuestion(qu_in_a_www);
+    message_b->addRRset(Message::SECTION_ANSWER, rrs_in_a_www);
+    message_b->addRRset(Message::SECTION_ANSWER, rrs_in_txt_www);
+    EXPECT_EQ(ResponseClassifier::EXTRADATA,
+        ResponseClassifier::classify(qu_in_a_www, message_b));
+
+    // Same QNAME on an ANY query is not valid with mixed classes
+    MessagePtr message_c(new Message(Message::RENDER));
+    message_c->setHeaderFlag(Message::HEADERFLAG_QR);
+    message_c->setOpcode(Opcode::QUERY());
+    message_c->setRcode(Rcode::NOERROR());
+    message_c->addQuestion(qu_in_any_www);
+    message_c->addRRset(Message::SECTION_ANSWER, rrs_in_a_www);
+    message_c->addRRset(Message::SECTION_ANSWER, rrs_hs_txt_www);
+    EXPECT_EQ(ResponseClassifier::MULTICLASS,
+        ResponseClassifier::classify(qu_in_any_www, message_c));
+
+    // Mixed QNAME is not valid unless QNAME requested is a CNAME.
+    MessagePtr message_d(new Message(Message::RENDER));
+    message_d->setHeaderFlag(Message::HEADERFLAG_QR);
+    message_d->setOpcode(Opcode::QUERY());
+    message_d->setRcode(Rcode::NOERROR());
+    message_d->addQuestion(qu_in_a_www);
+    message_d->addRRset(Message::SECTION_ANSWER, rrs_in_a_www);
+    message_d->addRRset(Message::SECTION_ANSWER, rrs_in_a_mail);
+    EXPECT_EQ(ResponseClassifier::EXTRADATA,
+        ResponseClassifier::classify(qu_in_a_www, message_d));
+
+    // Mixed QNAME is not valid when the query is an ANY.
+    MessagePtr message_e(new Message(Message::RENDER));
+    message_e->setHeaderFlag(Message::HEADERFLAG_QR);
+    message_e->setOpcode(Opcode::QUERY());
+    message_e->setRcode(Rcode::NOERROR());
+    message_e->addQuestion(qu_in_any_www);
+    message_e->addRRset(Message::SECTION_ANSWER, rrs_in_a_www);
+    message_e->addRRset(Message::SECTION_ANSWER, rrs_in_txt_www);
+    message_e->addRRset(Message::SECTION_ANSWER, rrs_in_a_mail);
+    EXPECT_EQ(ResponseClassifier::EXTRADATA,
+        ResponseClassifier::classify(qu_in_any_www, message_e));
+}
+
+// CNAME chain is CNAME if it terminates in a CNAME, answer if it
+// does not, and error if there are RRs left over.
+TEST_F(ResponseClassifierTest, CNAMEChain) {
+
+    // Answer contains a single CNAME
+    MessagePtr message_a(new Message(Message::RENDER));
+    message_a->setHeaderFlag(Message::HEADERFLAG_QR);
+    message_a->setOpcode(Opcode::QUERY());
+    message_a->setRcode(Rcode::NOERROR());
+    message_a->addQuestion(qu_in_a_www2);
+    message_a->addRRset(Message::SECTION_ANSWER, rrs_in_cname_www2);
+    EXPECT_EQ(ResponseClassifier::CNAME,
+        ResponseClassifier::classify(qu_in_a_www2, message_a));
+
+    // Add a CNAME for www1, and it should still return a CNAME
+    message_a->addRRset(Message::SECTION_ANSWER, rrs_in_cname_www1);
+    EXPECT_EQ(ResponseClassifier::CNAME,
+        ResponseClassifier::classify(qu_in_a_www2, message_a));
+
+    // Add the A record for www and it should be an answer
+    message_a->addRRset(Message::SECTION_ANSWER, rrs_in_a_www);
+    EXPECT_EQ(ResponseClassifier::ANSWERCNAME,
+        ResponseClassifier::classify(qu_in_a_www2, message_a));
+
+    // Adding an unrelated TXT record should result in EXTRADATA
+    message_a->addRRset(Message::SECTION_ANSWER, rrs_in_txt_www);
+    EXPECT_EQ(ResponseClassifier::EXTRADATA,
+        ResponseClassifier::classify(qu_in_a_www2, message_a));
+
+    // Recreate the chain, but this time end with a TXT RR and not the A
+    // record.  This should return INVTYPE.
+    MessagePtr message_b(new Message(Message::RENDER));
+    message_b->setHeaderFlag(Message::HEADERFLAG_QR);
+    message_b->setOpcode(Opcode::QUERY());
+    message_b->setRcode(Rcode::NOERROR());
+    message_b->addQuestion(qu_in_a_www2);
+    message_b->addRRset(Message::SECTION_ANSWER, rrs_in_cname_www2);
+    message_b->addRRset(Message::SECTION_ANSWER, rrs_in_cname_www1);
+    message_b->addRRset(Message::SECTION_ANSWER, rrs_in_txt_www);
+
+    EXPECT_EQ(ResponseClassifier::INVTYPE,
+        ResponseClassifier::classify(qu_in_a_www2, message_b));
+}
+
+} // Anonymous namespace
diff --git a/src/bin/resolver/tests/response_scrubber_unittest.cc b/src/bin/resolver/tests/response_scrubber_unittest.cc
new file mode 100644
index 0000000..e561eea
--- /dev/null
+++ b/src/bin/resolver/tests/response_scrubber_unittest.cc
@@ -0,0 +1,542 @@
+// 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.
+
+// $Id$
+
+#include <string>
+#include <iostream>
+
+#include <gtest/gtest.h>
+
+#include <config.h>
+
+#include <asiolink/ioendpoint.h>
+#include <asiolink/ioaddress.h>
+#include <netinet/in.h>
+
+#include <dns/name.h>
+#include <dns/opcode.h>
+#include <dns/question.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/rcode.h>
+#include <dns/rrclass.h>
+#include <dns/rrset.h>
+#include <dns/rrtype.h>
+#include <dns/rrttl.h>
+#include <resolver/response_scrubber.h>
+
+
+// Class for endpoint checks.  The family of the endpoint is set in the
+// constructor; the address family by the string provided for the address.
+
+namespace asiolink {
+
+class GenericEndpoint : public IOEndpoint {
+public:
+    GenericEndpoint(const std::string& address, uint16_t port, short protocol) :
+        address_(address), port_(port), protocol_(protocol)
+    {}
+    virtual ~GenericEndpoint()
+    {}
+
+    virtual IOAddress getAddress() const {
+        return address_;
+    }
+
+    virtual uint16_t getPort() const {
+        return port_;
+    }
+
+    virtual short getProtocol() const {
+        return protocol_;
+    }
+
+    virtual short getFamily() const {
+        return address_.getFamily();
+    }
+
+private:
+    IOAddress   address_;        // Address of endpoint
+    uint16_t    port_;          // Port number of endpoint
+    short       protocol_;      // Protocol of the endpoint
+    };
+}
+
+using namespace asio::ip;
+using namespace isc::dns;
+using namespace rdata;
+using namespace isc::dns::rdata::generic;
+using namespace isc::dns::rdata::in;
+using namespace asiolink;
+
+// Test class
+
+namespace {
+class ResponseScrubberTest : public ::testing::Test {
+public:
+    ResponseScrubberTest() :
+        bailiwick("example.com"),
+
+        qu_in_any_www(Name("www.example.com"), RRClass::IN(), RRType::ANY()),
+        qu_in_a_www(Name("www.example.com"), RRClass::IN(), RRType::A()),
+        qu_in_ns(Name("example.com"), RRClass::IN(), RRType::NS()),
+        qu_in_txt_www(Name("www.example.com"), RRClass::IN(), RRType::TXT()),
+        rrs_in_a_org(new RRset(Name("mail.example.org"), RRClass::IN(),
+            RRType::A(), RRTTL(300))),
+
+        rrs_in_a_net(new RRset(Name("mail.example.net"), RRClass::IN(),
+            RRType::A(), RRTTL(300))),
+        rrs_in_a_www(new RRset(Name("www.example.com"), RRClass::IN(),
+            RRType::A(), RRTTL(300))),
+        rrs_in_cname_www(new RRset(Name("www.example.com"), RRClass::IN(),
+            RRType::CNAME(), RRTTL(300))),
+        rrs_in_a_wwwnet(new RRset(Name("www.example.net"), RRClass::IN(),
+            RRType::A(), RRTTL(300))),
+        rrs_in_ns(new RRset(Name("example.com"), RRClass::IN(),
+            RRType::NS(), RRTTL(300))),
+        rrs_in_ns_com(new RRset(Name("com"), RRClass::IN(),
+            RRType::NS(), RRTTL(300))),
+        rrs_in_ns_net(new RRset(Name("example.net"), RRClass::IN(),
+            RRType::NS(), RRTTL(300))),
+        rrs_in_ns_sub(new RRset(Name("subdomain.example.com"), RRClass::IN(),
+            RRType::NS(), RRTTL(300))),
+        rrs_in_ns_sub2(new RRset(Name("subdomain2.example.com"), RRClass::IN(),
+            RRType::NS(), RRTTL(300))),
+        rrs_in_a_ns0(new RRset(Name("ns0.example.com"), RRClass::IN(),
+            RRType::A(), RRTTL(300))),
+        rrs_in_a_ns1(new RRset(Name("ns1.com"), RRClass::IN(),
+            RRType::A(), RRTTL(300))),
+        rrs_in_a_ns2(new RRset(Name("ns2.example.net"), RRClass::IN(),
+            RRType::A(), RRTTL(300))),
+        rrs_in_a_ns3(new RRset(Name("ns3.subdomain.example.com"), RRClass::IN(),
+            RRType::A(), RRTTL(300))),
+        rrs_in_txt_www(new RRset(Name("www.example.com"), RRClass::IN(),
+            RRType::TXT(), RRTTL(300)))
+    {}
+    Name        bailiwick;          // Bailiwick of the server queried
+    Question    qu_in_any_www;      // www.example.com IN ANY
+    Question    qu_in_a_www;        // www.example.com IN A
+    Question    qu_in_ns;           // example.com IN NS
+    Question    qu_in_txt_www;      // www.example.com IN TXT
+    RRsetPtr    rrs_in_a_org;       // mail.example.org IN A
+    RRsetPtr    rrs_in_a_net;       // mail.example.org IN A
+    RRsetPtr    rrs_in_a_www;       // www.example.com IN A
+    RRsetPtr    rrs_in_cname_www;   // www.example.com IN CNAME
+    RRsetPtr    rrs_in_a_wwwnet;    // www.example.net IN A
+    RRsetPtr    rrs_in_ns;          // example.com IN NS
+    RRsetPtr    rrs_in_ns_com;      // com IN NS
+    RRsetPtr    rrs_in_ns_net;      // example.net IN NS
+    RRsetPtr    rrs_in_ns_sub;      // subdomain.example.com IN NS
+    RRsetPtr    rrs_in_ns_sub2;     // subdomain2.example.com IN NS
+    RRsetPtr    rrs_in_a_ns0;       // ns0.example.com IN A
+    RRsetPtr    rrs_in_a_ns1;       // ns1.com IN A
+    RRsetPtr    rrs_in_a_ns2;       // ns2.example.net IN A
+    RRsetPtr    rrs_in_a_ns3;       // ns3.subdomain.example.net IN A
+    RRsetPtr    rrs_in_txt_www;     // www.example.com IN TXT
+};
+
+
+// Check that the IP addresses/ports/protocol for the packets sent and received
+// both match if both types are IP V4.
+
+TEST_F(ResponseScrubberTest, UDPv4) {
+
+    // Basic UDP Endpoint
+    GenericEndpoint udp_a("192.0.2.1", 12345, IPPROTO_UDP);
+
+    // Same address, port
+    GenericEndpoint udp_b("192.0.2.1", 12345, IPPROTO_UDP);
+    EXPECT_EQ(ResponseScrubber::SUCCESS,
+        ResponseScrubber::addressCheck(udp_a, udp_b));
+
+    // Different address, same port
+    GenericEndpoint udp_c("192.0.2.2", 12345, IPPROTO_UDP);
+    EXPECT_EQ(ResponseScrubber::ADDRESS,
+        ResponseScrubber::addressCheck(udp_a, udp_c));
+
+    // Same address, different port
+    GenericEndpoint udp_d("192.0.2.1", 12346, IPPROTO_UDP);
+    EXPECT_EQ(ResponseScrubber::PORT,
+        ResponseScrubber::addressCheck(udp_a, udp_d));
+
+    // Different address, different port
+    GenericEndpoint udp_e("192.0.2.3", 12347, IPPROTO_UDP);
+    EXPECT_EQ(ResponseScrubber::ADDRESS,
+        ResponseScrubber::addressCheck(udp_a, udp_e));
+
+}
+
+// Repeat the tests for TCP
+
+TEST_F(ResponseScrubberTest, TCPv4) {
+
+    // Basic TCP Endpoint
+    GenericEndpoint tcp_a("192.0.2.1", 12345, IPPROTO_TCP);
+
+    // Same address, port
+    GenericEndpoint tcp_b("192.0.2.1", 12345, IPPROTO_TCP);
+    EXPECT_EQ(ResponseScrubber::SUCCESS,
+        ResponseScrubber::addressCheck(tcp_a, tcp_b));
+
+    // Different address, same port
+    GenericEndpoint tcp_c("192.0.2.2", 12345, IPPROTO_TCP);
+    EXPECT_EQ(ResponseScrubber::ADDRESS,
+        ResponseScrubber::addressCheck(tcp_a, tcp_c));
+
+    // Same address, different port
+    GenericEndpoint tcp_d("192.0.2.1", 12346, IPPROTO_TCP);
+    EXPECT_EQ(ResponseScrubber::PORT,
+        ResponseScrubber::addressCheck(tcp_a, tcp_d));
+
+    // Different address, different port
+    GenericEndpoint tcp_e("192.0.2.3", 12347, IPPROTO_TCP);
+    EXPECT_EQ(ResponseScrubber::ADDRESS,
+        ResponseScrubber::addressCheck(tcp_a, tcp_e));
+
+}
+
+// Repeat the tests for UDP/IPv6
+
+TEST_F(ResponseScrubberTest, UDPv6) {
+
+    // Basic UDP Endpoint
+    GenericEndpoint  udp_a("2001:db8::1", 12345, IPPROTO_UDP);
+
+    // Same address and port
+    GenericEndpoint  udp_b("2001:db8::1", 12345, IPPROTO_UDP);
+    EXPECT_EQ(ResponseScrubber::SUCCESS,
+        ResponseScrubber::addressCheck(udp_a, udp_b));
+
+    // Different address, same port
+    GenericEndpoint  udp_c("2001:db8::3", 12345, IPPROTO_UDP);
+    EXPECT_EQ(ResponseScrubber::ADDRESS,
+        ResponseScrubber::addressCheck(udp_a, udp_c));
+
+    // Same address, different port
+    GenericEndpoint  udp_d("2001:db8::1", 12346, IPPROTO_UDP);
+    EXPECT_EQ(ResponseScrubber::PORT,
+        ResponseScrubber::addressCheck(udp_a, udp_d));
+
+    // Different address, different port
+    GenericEndpoint  udp_e("2001:db8::3", 12347, IPPROTO_UDP);
+    EXPECT_EQ(ResponseScrubber::ADDRESS,
+        ResponseScrubber::addressCheck(udp_a, udp_e));
+
+}
+
+// Same again for TCP/IPv6
+
+TEST_F(ResponseScrubberTest, TCPv6) {
+
+    // Basic TCP Endpoint
+    GenericEndpoint  tcp_a("2001:db8::1", 12345, IPPROTO_TCP);
+
+    // Same address and port
+    GenericEndpoint  tcp_b("2001:db8::1", 12345, IPPROTO_TCP);
+    EXPECT_EQ(ResponseScrubber::SUCCESS,
+        ResponseScrubber::addressCheck(tcp_a, tcp_b));
+
+    // Different address, same port
+    GenericEndpoint  tcp_c("2001:db8::3", 12345, IPPROTO_TCP);
+    EXPECT_EQ(ResponseScrubber::ADDRESS,
+        ResponseScrubber::addressCheck(tcp_a, tcp_c));
+
+    // Same address, different port
+    GenericEndpoint  tcp_d("2001:db8::1", 12346, IPPROTO_TCP);
+    EXPECT_EQ(ResponseScrubber::PORT,
+        ResponseScrubber::addressCheck(tcp_a, tcp_d));
+
+    // Different address, different port
+    GenericEndpoint  tcp_e("2001:db8::3", 12347, IPPROTO_TCP);
+    EXPECT_EQ(ResponseScrubber::ADDRESS,
+        ResponseScrubber::addressCheck(tcp_a, tcp_e));
+
+}
+
+// Ensure that mixed IPv4/6 addresses don't match.
+
+TEST_F(ResponseScrubberTest, v4v6) {
+
+    // UDP
+    GenericEndpoint  udp_a("2001:db8::1", 12345, IPPROTO_UDP);
+    GenericEndpoint  udp_b("192.0.2.1", 12345, IPPROTO_UDP);
+    EXPECT_EQ(ResponseScrubber::ADDRESS,
+        ResponseScrubber::addressCheck(udp_a, udp_b));
+
+    // TCP
+    GenericEndpoint  tcp_a("2001:db8::1", 12345, IPPROTO_TCP);
+    GenericEndpoint  tcp_b("192.0.2.1", 12345, IPPROTO_TCP);
+    EXPECT_EQ(ResponseScrubber::ADDRESS,
+        ResponseScrubber::addressCheck(udp_a, udp_b));
+}
+
+// Check mixed protocols are detected
+
+TEST_F(ResponseScrubberTest, Protocol) {
+    GenericEndpoint  udp_a("2001:db8::1", 12345, IPPROTO_UDP);
+    GenericEndpoint  tcp_a("2001:db8::1", 12345, IPPROTO_TCP);
+    EXPECT_EQ(ResponseScrubber::PROTOCOL,
+        ResponseScrubber::addressCheck(udp_a, tcp_a));
+}
+
+// Check that the QIDs check OK
+
+TEST_F(ResponseScrubberTest, Qid) {
+    Message a(Message::RENDER);
+    a.setQid(27);
+
+    Message b(Message::RENDER);
+    b.setQid(27);
+    EXPECT_TRUE(ResponseScrubber::qidCheck(a, b));
+
+    Message c(Message::RENDER);
+    c.setQid(28);
+    EXPECT_FALSE(ResponseScrubber::qidCheck(a, c));
+}
+
+// Check the scrubAllSections() method. As this operates by calling the
+// scrubSection() method (with a SUBDOMAIN argument), this is also a check of
+// the latter.
+
+TEST_F(ResponseScrubberTest, ScrubAllSectionsValid) {
+    Message valid(Message::RENDER);
+
+    // Valid message with nothing out of bailiwick
+    valid.addQuestion(qu_in_a_www);
+    valid.addRRset(Message::SECTION_ANSWER, rrs_in_a_www);
+    valid.addRRset(Message::SECTION_AUTHORITY, rrs_in_ns);
+    valid.addRRset(Message::SECTION_ADDITIONAL, rrs_in_a_ns0);
+
+    // Scrub the message and expect nothing to have been removed.
+    int removed = ResponseScrubber::scrubAllSections(valid, bailiwick);
+    EXPECT_EQ(0, removed);
+
+    // ... and check that this is the case
+    EXPECT_TRUE(valid.hasRRset(Message::SECTION_ANSWER, rrs_in_a_www));
+    EXPECT_TRUE(valid.hasRRset(Message::SECTION_AUTHORITY, rrs_in_ns));
+    EXPECT_TRUE(valid.hasRRset(Message::SECTION_ADDITIONAL, rrs_in_a_ns0));
+
+    // Add out-of-bailiwick glue to the additional section (pretend that the
+    // NS RRset contained an out-of-domain server.
+    valid.addRRset(Message::SECTION_ADDITIONAL, rrs_in_a_ns2);
+    EXPECT_TRUE(valid.hasRRset(Message::SECTION_ADDITIONAL, rrs_in_a_ns2));
+
+    // ... and check that it is removed when scrubbed
+    removed = ResponseScrubber::scrubAllSections(valid, bailiwick);
+    EXPECT_EQ(1, removed);
+    EXPECT_TRUE(valid.hasRRset(Message::SECTION_ANSWER, rrs_in_a_www));
+    EXPECT_TRUE(valid.hasRRset(Message::SECTION_AUTHORITY, rrs_in_ns));
+    EXPECT_TRUE(valid.hasRRset(Message::SECTION_ADDITIONAL, rrs_in_a_ns0));
+    EXPECT_FALSE(valid.hasRRset(Message::SECTION_ADDITIONAL, rrs_in_a_ns2));
+ }
+
+TEST_F(ResponseScrubberTest, ScrubAllSectionsInvalid) {
+    Message invalid(Message::RENDER);
+
+    // Invalid message, with various things in and out of bailiwick.
+
+    invalid.addQuestion(qu_in_a_www);
+
+    // Answer section
+    //
+    // rrs_in_a_www - "www.example.com A", in bailiwick
+    // rrs_in_txt_www - "www.example.com TXT", in bailiwick
+    // rrs_in_a_org - "mail.example.org A", out of bailiwick - the qname is
+    //     related to the bailiwick name by having a common ancestor at the root
+    // rrs_in_a_net - "mail.example.net A", out of bailiwick - the qname is
+    //     related to the bailiwick name by having a common ancestor at the root
+    invalid.addRRset(Message::SECTION_ANSWER, rrs_in_a_www);
+    invalid.addRRset(Message::SECTION_ANSWER, rrs_in_txt_www);
+    invalid.addRRset(Message::SECTION_ANSWER, rrs_in_a_org);
+    invalid.addRRset(Message::SECTION_ANSWER, rrs_in_a_net);
+
+    // Authority section
+    //
+    // rrs_in_ns - "example.com NS", in bailiwick (qname is bailiwick name)
+    // rrs_in_ns_com - "com NS", out of bailiwick as the qname is a superdomain
+    //     (direct ancestor) of the bailiwick name
+    // rrs_in_ns_net - "example.net NS", out of bailiwick - the qname is related
+    //     to the bailiwick name by having a common ancestor at the root
+    // rrs_in_ns_sub - "subdomain.example.com", in bailiwick as the qname is
+    //     a subdomain of the bailiwick name
+    invalid.addRRset(Message::SECTION_AUTHORITY, rrs_in_ns);
+    invalid.addRRset(Message::SECTION_AUTHORITY, rrs_in_ns_com);
+    invalid.addRRset(Message::SECTION_AUTHORITY, rrs_in_ns_net);
+    invalid.addRRset(Message::SECTION_AUTHORITY, rrs_in_ns_sub);
+
+    // Additional section
+    //
+    // rrs_in_a_ns0 - "ns0.example.com", in bailiwick because the qname is
+    //     a subdomain of the bailiwick name
+    // rrs_in_a_ns1 - "ns1.com", out of bailiwick because the qname is a
+    //     sibling to the bailiwick name
+    // rrs_in_a_ns2 - "ns2.example.net", out of bailiwick because qname is
+    //     related by having a common ancestor and the root.
+    // rrs_in_a_ns3 - "ns3.subdomain.example.com", in bailiwick because the
+    //     qname is a direct descendent of the bailiwick name.
+    invalid.addRRset(Message::SECTION_ADDITIONAL, rrs_in_a_ns0);
+    invalid.addRRset(Message::SECTION_ADDITIONAL, rrs_in_a_ns1);
+    invalid.addRRset(Message::SECTION_ADDITIONAL, rrs_in_a_ns2);
+    invalid.addRRset(Message::SECTION_ADDITIONAL, rrs_in_a_ns3);
+
+    // Scrub the message
+    int removed = ResponseScrubber::scrubAllSections(invalid, bailiwick);
+    EXPECT_EQ(6, removed);
+
+    // ... and check the sections.  Answer...
+    EXPECT_TRUE(invalid.hasRRset(Message::SECTION_ANSWER, rrs_in_a_www));
+    EXPECT_TRUE(invalid.hasRRset(Message::SECTION_ANSWER, rrs_in_txt_www));
+    EXPECT_FALSE(invalid.hasRRset(Message::SECTION_ANSWER, rrs_in_a_org));
+    EXPECT_FALSE(invalid.hasRRset(Message::SECTION_ANSWER, rrs_in_a_net));
+
+    // ... authority...
+    EXPECT_TRUE(invalid.hasRRset(Message::SECTION_AUTHORITY, rrs_in_ns));
+    EXPECT_FALSE(invalid.hasRRset(Message::SECTION_AUTHORITY, rrs_in_ns_com));
+    EXPECT_FALSE(invalid.hasRRset(Message::SECTION_AUTHORITY, rrs_in_ns_net));
+    EXPECT_TRUE(invalid.hasRRset(Message::SECTION_AUTHORITY, rrs_in_ns_sub));
+
+    // ... additional.
+    EXPECT_TRUE(invalid.hasRRset(Message::SECTION_ADDITIONAL, rrs_in_a_ns0));
+    EXPECT_FALSE(invalid.hasRRset(Message::SECTION_ADDITIONAL, rrs_in_a_ns1));
+    EXPECT_FALSE(invalid.hasRRset(Message::SECTION_ADDITIONAL, rrs_in_a_ns2));
+    EXPECT_TRUE(invalid.hasRRset(Message::SECTION_ADDITIONAL, rrs_in_a_ns3));
+}
+
+// An empty message
+
+TEST_F(ResponseScrubberTest, ScrubAllSectionsEmpty) {
+    Message empty(Message::RENDER);
+
+    EXPECT_EQ(0, empty.getRRCount(Message::SECTION_QUESTION));
+    EXPECT_EQ(0, empty.getRRCount(Message::SECTION_ANSWER));
+    EXPECT_EQ(0, empty.getRRCount(Message::SECTION_AUTHORITY));
+    EXPECT_EQ(0, empty.getRRCount(Message::SECTION_ADDITIONAL));
+
+    int removed = ResponseScrubber::scrubAllSections(empty, bailiwick);
+    EXPECT_EQ(0, removed);
+
+    EXPECT_EQ(0, empty.getRRCount(Message::SECTION_QUESTION));
+    EXPECT_EQ(0, empty.getRRCount(Message::SECTION_ANSWER));
+    EXPECT_EQ(0, empty.getRRCount(Message::SECTION_AUTHORITY));
+    EXPECT_EQ(0, empty.getRRCount(Message::SECTION_ADDITIONAL));
+
+}
+
+// Check the cross-section scrubbing (checks the general scrubSection()
+// method with a SUPERDOMAIN argument.)
+
+// Empty message (apart from question)
+
+TEST_F(ResponseScrubberTest, CrossSectionEmpty) {
+
+    Message message1(Message::RENDER);
+    message1.addQuestion(qu_in_a_www);
+    int removed = ResponseScrubber::scrubCrossSections(message1);
+    EXPECT_EQ(0, removed);
+}
+
+// Valid answer section
+
+TEST_F(ResponseScrubberTest, CrossSectionAnswer) {
+
+    // Valid message with nothing out of bailiwick, but the authority
+    // (subdomain.example.com) is not authoritative for the answer.
+    //
+    // TODO: Test the case where the additional section does not match
+    // with something in the authority section.
+    Message message1(Message::RENDER);
+    message1.addQuestion(qu_in_a_www);
+    message1.addRRset(Message::SECTION_ANSWER, rrs_in_a_www);
+    message1.addRRset(Message::SECTION_AUTHORITY, rrs_in_ns_sub);
+    message1.addRRset(Message::SECTION_ADDITIONAL, rrs_in_a_ns3);
+    int removed = ResponseScrubber::scrubCrossSections(message1);
+    EXPECT_EQ(1, removed);
+    EXPECT_TRUE(message1.hasRRset(Message::SECTION_ANSWER, rrs_in_a_www));
+    EXPECT_FALSE(message1.hasRRset(Message::SECTION_AUTHORITY, rrs_in_ns_sub));
+    EXPECT_TRUE(message1.hasRRset(Message::SECTION_ADDITIONAL, rrs_in_a_ns3));
+
+    // A repeat of the test, this time with a mixture of incorrect and correct
+    // authorities.
+    Message message2(Message::RENDER);
+    message2.addQuestion(qu_in_a_www);
+    message2.addRRset(Message::SECTION_ANSWER, rrs_in_a_www);
+    message2.addRRset(Message::SECTION_AUTHORITY, rrs_in_ns_sub);
+    message2.addRRset(Message::SECTION_AUTHORITY, rrs_in_ns);
+    message2.addRRset(Message::SECTION_AUTHORITY, rrs_in_ns_sub2);
+    message2.addRRset(Message::SECTION_ADDITIONAL, rrs_in_a_ns3);
+    removed = ResponseScrubber::scrubCrossSections(message2);
+    EXPECT_EQ(2, removed);
+    EXPECT_TRUE(message2.hasRRset(Message::SECTION_ANSWER, rrs_in_a_www));
+    EXPECT_FALSE(message2.hasRRset(Message::SECTION_AUTHORITY, rrs_in_ns_sub));
+    EXPECT_TRUE(message2.hasRRset(Message::SECTION_AUTHORITY, rrs_in_ns));
+    EXPECT_FALSE(message2.hasRRset(Message::SECTION_AUTHORITY, rrs_in_ns_sub2));
+    EXPECT_TRUE(message2.hasRRset(Message::SECTION_ADDITIONAL, rrs_in_a_ns3));
+}
+
+// Test the main "scrub" method.  This is a single to ensure that the
+// combination of methods
+
+TEST_F(ResponseScrubberTest, All) {
+    MessagePtr mptr(new Message(Message::RENDER));
+
+    // Question is "www.example.com IN A" sent to a nameserver with the
+    // bailiwick of "example.com".
+    mptr->addQuestion(qu_in_a_www);
+
+    // Answer section.
+
+    // "www.example.com IN CNAME www.example.net" - should be kept
+    mptr->addRRset(Message::SECTION_ANSWER, rrs_in_cname_www);
+
+    // "www.example.net IN A a.b.c.d" - should be removed, out of bailiwick
+    mptr->addRRset(Message::SECTION_ANSWER, rrs_in_a_wwwnet);
+
+    // Authority section.
+
+    // "example.net IN NS xxxx" - should be removed, out of bailiwick.
+    mptr->addRRset(Message::SECTION_AUTHORITY, rrs_in_ns_net);
+
+    // "example.com IN NS xxx" - kept
+    mptr->addRRset(Message::SECTION_AUTHORITY, rrs_in_ns);
+
+    // "com IN NS xxx" - removed, out of bailiwick
+    mptr->addRRset(Message::SECTION_AUTHORITY, rrs_in_ns_com);
+
+    // "subdomain.example.com IN NS xxx" - removed, not a superdomain of the
+    // answer.
+    mptr->addRRset(Message::SECTION_AUTHORITY, rrs_in_ns_sub);
+
+    // Additional section
+
+    // "ns2.example.net IN A a.b.c.d" - removed, out of bailiwick
+    mptr->addRRset(Message::SECTION_ADDITIONAL, rrs_in_a_ns2);
+
+    // "ns3.subdomain.example.com IN A a.b.c.d" - retained.
+    mptr->addRRset(Message::SECTION_ADDITIONAL, rrs_in_a_ns3);
+
+    unsigned int removed = ResponseScrubber::scrub(mptr, bailiwick);
+    EXPECT_EQ(5, removed);
+
+    EXPECT_TRUE(mptr->hasRRset(Message::SECTION_ANSWER, rrs_in_cname_www));
+    EXPECT_FALSE(mptr->hasRRset(Message::SECTION_ANSWER, rrs_in_a_wwwnet));
+    EXPECT_FALSE(mptr->hasRRset(Message::SECTION_AUTHORITY, rrs_in_ns_net));
+    EXPECT_TRUE(mptr->hasRRset(Message::SECTION_AUTHORITY, rrs_in_ns));
+    EXPECT_FALSE(mptr->hasRRset(Message::SECTION_AUTHORITY, rrs_in_ns_com));
+    EXPECT_FALSE(mptr->hasRRset(Message::SECTION_AUTHORITY, rrs_in_ns_sub));
+    EXPECT_FALSE(mptr->hasRRset(Message::SECTION_ADDITIONAL, rrs_in_a_ns2));
+    EXPECT_TRUE(mptr->hasRRset(Message::SECTION_ADDITIONAL, rrs_in_a_ns3));
+
+}
+} // Anonymous namespace
diff --git a/src/bin/stats/b10-stats.xml b/src/bin/stats/b10-stats.xml
index fb82ecd..f622439 100644
--- a/src/bin/stats/b10-stats.xml
+++ b/src/bin/stats/b10-stats.xml
@@ -17,7 +17,6 @@
  - PERFORMANCE OF THIS SOFTWARE.
 -->
 
-<!-- $Id$ -->
 <refentry>
 
   <refentryinfo>
@@ -90,7 +89,8 @@
     <para><filename>/usr/local/share/bind10-devel/stats.spec</filename>
       — This is a spec file for <command>b10-stats</command>. It
       contains definitions of statistics items of BIND 10 and commands
-      received vi bindctl.
+      received via
+      <refentrytitle>bindctl</refentrytitle><manvolnum>1</manvolnum>.
     </para>
   </refsect1>
 
diff --git a/src/bin/stats/stats.py.in b/src/bin/stats/stats.py.in
old mode 100644
new mode 100755
index 2d5ff36..15e2980
--- a/src/bin/stats/stats.py.in
+++ b/src/bin/stats/stats.py.in
@@ -15,7 +15,6 @@
 # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
-# $Id$
 __version__ = "$Revision$"
 
 import sys; sys.path.append ('@@PYTHONPATH@@')
diff --git a/src/bin/stats/stats_stub.py.in b/src/bin/stats/stats_stub.py.in
old mode 100644
new mode 100755
index fe63916..4a4a641
--- a/src/bin/stats/stats_stub.py.in
+++ b/src/bin/stats/stats_stub.py.in
@@ -15,7 +15,6 @@
 # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
-# $Id$
 __version__ = "$Revision$"
 
 import sys; sys.path.append ('@@PYTHONPATH@@')
diff --git a/src/bin/stats/tests/b10-stats_stub_test.py b/src/bin/stats/tests/b10-stats_stub_test.py
index 75c5fde..b8983c5 100644
--- a/src/bin/stats/tests/b10-stats_stub_test.py
+++ b/src/bin/stats/tests/b10-stats_stub_test.py
@@ -13,7 +13,6 @@
 # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
-# $Id$
 __version__ = "$Revision$"
 
 #
diff --git a/src/bin/stats/tests/b10-stats_test.py b/src/bin/stats/tests/b10-stats_test.py
index 873c24d..e4e1a1e 100644
--- a/src/bin/stats/tests/b10-stats_test.py
+++ b/src/bin/stats/tests/b10-stats_test.py
@@ -13,7 +13,6 @@
 # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
-# $Id$
 __version__ = "$Revision$"
 
 #
diff --git a/src/bin/stats/tests/fake_time.py b/src/bin/stats/tests/fake_time.py
index 964097f..65e0237 100644
--- a/src/bin/stats/tests/fake_time.py
+++ b/src/bin/stats/tests/fake_time.py
@@ -13,7 +13,6 @@
 # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
-# $Id$
 __version__ = "$Revision$"
 
 # This is a dummy time class against a Python standard time class.
diff --git a/src/bin/stats/tests/isc/cc/session.py b/src/bin/stats/tests/isc/cc/session.py
index ab9c296..4d12adc 100644
--- a/src/bin/stats/tests/isc/cc/session.py
+++ b/src/bin/stats/tests/isc/cc/session.py
@@ -13,7 +13,6 @@
 # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
-# $Id$
 # This module is a mock-up class of isc.cc.session
 
 __version__ = "$Revision$"
diff --git a/src/bin/stats/tests/isc/config/ccsession.py b/src/bin/stats/tests/isc/config/ccsession.py
index b563e3b..fc285a7 100644
--- a/src/bin/stats/tests/isc/config/ccsession.py
+++ b/src/bin/stats/tests/isc/config/ccsession.py
@@ -13,7 +13,6 @@
 # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
-# $Id$
 # This module is a mock-up class of isc.cc.session
 
 __version__ = "$Revision$"
diff --git a/src/bin/stats/tests/isc/util/process.py b/src/bin/stats/tests/isc/util/process.py
index 768528c..7274a48 100644
--- a/src/bin/stats/tests/isc/util/process.py
+++ b/src/bin/stats/tests/isc/util/process.py
@@ -13,8 +13,6 @@
 # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
-# $Id$
-
 # A dummy function of isc.util.process.rename()
 def rename(name=None):
     pass
diff --git a/src/bin/stats/tests/isc/utils/process.py b/src/bin/stats/tests/isc/utils/process.py
index 806c2ae..4c0cf8c 100644
--- a/src/bin/stats/tests/isc/utils/process.py
+++ b/src/bin/stats/tests/isc/utils/process.py
@@ -13,8 +13,6 @@
 # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
-# $Id$
-
 # A dummy function of isc.utils.process.rename()
 def rename(name=None):
     pass
diff --git a/src/bin/usermgr/b10-cmdctl-usermgr.py.in b/src/bin/usermgr/b10-cmdctl-usermgr.py.in
index 645c053..d62ad72 100644
--- a/src/bin/usermgr/b10-cmdctl-usermgr.py.in
+++ b/src/bin/usermgr/b10-cmdctl-usermgr.py.in
@@ -24,7 +24,7 @@ from hashlib import sha1
 import csv
 import getpass
 import getopt
-import sys
+import sys; sys.path.append ('@@PYTHONPATH@@')
 import isc.util.process
 
 isc.util.process.rename()
diff --git a/src/bin/usermgr/b10-cmdctl-usermgr.xml b/src/bin/usermgr/b10-cmdctl-usermgr.xml
index 7549fe1..529a8db 100644
--- a/src/bin/usermgr/b10-cmdctl-usermgr.xml
+++ b/src/bin/usermgr/b10-cmdctl-usermgr.xml
@@ -17,7 +17,6 @@
  - PERFORMANCE OF THIS SOFTWARE.
 -->
 
-<!-- $Id$ -->
 <refentry>
 
   <refentryinfo>
diff --git a/src/bin/xfrin/b10-xfrin.xml b/src/bin/xfrin/b10-xfrin.xml
index 05ae43c..fdfe1ef 100644
--- a/src/bin/xfrin/b10-xfrin.xml
+++ b/src/bin/xfrin/b10-xfrin.xml
@@ -17,7 +17,6 @@
  - PERFORMANCE OF THIS SOFTWARE.
 -->
 
-<!-- $Id$ -->
 <refentry>
 
   <refentryinfo>
@@ -64,7 +63,7 @@
     </para>
 
     <note><simpara>
-      The Y1 prototype release only supports AXFR. IXFR is not implemented.
+      This prototype release only supports AXFR. IXFR is not implemented.
     </simpara></note>
 
     <para>
diff --git a/src/bin/xfrin/tests/xfrin_test.py b/src/bin/xfrin/tests/xfrin_test.py
index 4a1ae01..04d04a6 100644
--- a/src/bin/xfrin/tests/xfrin_test.py
+++ b/src/bin/xfrin/tests/xfrin_test.py
@@ -13,8 +13,6 @@
 # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
-# $Id$
-
 import unittest
 import socket
 from xfrin import *
diff --git a/src/bin/xfrin/xfrin.py.in b/src/bin/xfrin/xfrin.py.in
old mode 100644
new mode 100755
index 1282a3b..fdbc990
--- a/src/bin/xfrin/xfrin.py.in
+++ b/src/bin/xfrin/xfrin.py.in
@@ -16,8 +16,6 @@
 # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
-# $Id$
-
 import sys; sys.path.append ('@@PYTHONPATH@@')
 import os
 import signal
@@ -521,8 +519,19 @@ class Xfrin:
             msg = create_command(notify_out.ZONE_NEW_DATA_READY_CMD, param)
             # catch the exception, in case msgq has been killed.
             try:
-                self._send_cc_session.group_sendmsg(msg, XFROUT_MODULE_NAME)
-                self._send_cc_session.group_sendmsg(msg, ZONE_MANAGER_MODULE_NAME)
+                seq = self._send_cc_session.group_sendmsg(msg,
+                                                          XFROUT_MODULE_NAME)
+                try:
+                    answer, env = self._send_cc_session.group_recvmsg(False,
+                                                                      seq)
+                except isc.cc.session.SessionTimeout:
+                    pass        # for now we just ignore the failure
+                seq = self._send_cc_session.group_sendmsg(msg, ZONE_MANAGER_MODULE_NAME)
+                try:
+                    answer, env = self._send_cc_session.group_recvmsg(False,
+                                                                      seq)
+                except isc.cc.session.SessionTimeout:
+                    pass        # for now we just ignore the failure
             except socket.error as err: 
                 log_error("Fail to send message to %s and %s, msgq may has been killed" 
                           % (XFROUT_MODULE_NAME, ZONE_MANAGER_MODULE_NAME))
@@ -530,7 +539,12 @@ class Xfrin:
             msg = create_command(ZONE_XFRIN_FAILED, param)
             # catch the exception, in case msgq has been killed.
             try:
-                self._send_cc_session.group_sendmsg(msg, ZONE_MANAGER_MODULE_NAME)
+                seq = self._send_cc_session.group_sendmsg(msg, ZONE_MANAGER_MODULE_NAME)
+                try:
+                    answer, env = self._send_cc_session.group_recvmsg(False,
+                                                                      seq)
+                except isc.cc.session.SessionTimeout:
+                    pass        # for now we just ignore the failure
             except socket.error as err:
                 log_error("Fail to send message to %s, msgq may has been killed" 
                           % ZONE_MANAGER_MODULE_NAME)
diff --git a/src/bin/xfrout/b10-xfrout.8 b/src/bin/xfrout/b10-xfrout.8
index 3f3de27..c8b4b07 100644
--- a/src/bin/xfrout/b10-xfrout.8
+++ b/src/bin/xfrout/b10-xfrout.8
@@ -69,6 +69,26 @@ The configurable settings are:
 
 \fItransfers_out\fR
 defines the maximum number of outgoing zone transfers that can run concurrently\&. The default is 10\&.
+.PP
+
+\fIlog_name\fR
+.PP
+
+\fIlog_file\fR
+The location of the log file if using a file channel\&. If undefined, then the file channel is closed\&. The default is
+/usr/local/var/bind10\-devel/log/Xfrout\&.log\&.
+.PP
+
+\fIlog_severity\fR
+The default is "debug"\&.
+.PP
+
+\fIlog_versions\fR
+The default is 5\&.
+.PP
+
+\fIlog_max_bytes\fR
+The default is 1048576\&.
 .if n \{\
 .sp
 .\}
diff --git a/src/bin/xfrout/b10-xfrout.xml b/src/bin/xfrout/b10-xfrout.xml
index 984fd9d..ad71fe2 100644
--- a/src/bin/xfrout/b10-xfrout.xml
+++ b/src/bin/xfrout/b10-xfrout.xml
@@ -17,7 +17,6 @@
  - PERFORMANCE OF THIS SOFTWARE.
 -->
 
-<!-- $Id$ -->
 <refentry>
 
   <refentryinfo>
@@ -98,6 +97,33 @@
       defines the maximum number of outgoing zone transfers
       that can run concurrently. The default is 10.
     </para>
+    <para>
+      <varname>log_name</varname>
+<!-- TODO -->
+    </para>
+    <para>
+      <varname>log_file</varname>
+<!-- TODO -->
+      The location of the log file if using a file channel.
+      If undefined, then the file channel is closed.
+      The default is
+      <filename>/usr/local/var/bind10-devel/log/Xfrout.log</filename>.
+    </para>
+    <para>
+      <varname>log_severity</varname>
+<!-- TODO -->
+      The default is "debug".
+    </para>
+    <para>
+      <varname>log_versions</varname>
+<!-- TODO -->
+      The default is 5.
+    </para>
+    <para>
+      <varname>log_max_bytes</varname>
+<!-- TODO -->
+      The default is 1048576.
+    </para>
 
 <!-- TODO: log configurations not documented yet in here. jreed
      has some but waiting on decisions ... -->
diff --git a/src/bin/zonemgr/b10-zonemgr.8 b/src/bin/zonemgr/b10-zonemgr.8
index a53887c..fbd0602 100644
--- a/src/bin/zonemgr/b10-zonemgr.8
+++ b/src/bin/zonemgr/b10-zonemgr.8
@@ -2,12 +2,12 @@
 .\"     Title: b10-zonemgr
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: September 8, 2010
+.\"      Date: October 18, 2010
 .\"    Manual: BIND10
 .\"    Source: BIND10
 .\"  Language: English
 .\"
-.TH "B10\-ZONEMGR" "8" "September 8, 2010" "BIND10" "BIND10"
+.TH "B10\-ZONEMGR" "8" "October 18, 2010" "BIND10" "BIND10"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
@@ -43,6 +43,26 @@ receives its configurations from
 \fBb10-cfgmgr\fR(8)\&.
 .SH "CONFIGURATION AND COMMANDS"
 .PP
+The configurable settings are:
+.PP
+
+\fIjitter_scope\fR
+defines the random jitter range subtracted from the refresh and retry timers to avoid many zones from refreshing at the same time\&. The refresh or retry time actually used is a random time between the defined refresh or retry time and it multiplied by the
+\fIjitter_scope\fR\&. This is re\-evaluated after each refresh or retry\&. This value is a real number and the maximum is 0\&.5 (half of the refresh or retry time)\&. The default is 0\&.25\&. Set to 0 to disable the jitter\&.
+.PP
+
+\fIlowerbound_refresh\fR
+defines the minimum SOA REFRESH time in seconds\&. The default is 10\&.
+.PP
+
+\fIlowerbound_retry\fR
+defines the minimum SOA RETRY time in seconds\&. The default is 5\&.
+.PP
+
+\fImax_transfer_timeout\fR
+defines the maximum amount of time in seconds for a transfer\&.
+The default is 14400 (4 hours)\&.
+.PP
 The configuration commands are:
 .PP
 
diff --git a/src/bin/zonemgr/b10-zonemgr.xml b/src/bin/zonemgr/b10-zonemgr.xml
index d0fe44b..4d796ee 100644
--- a/src/bin/zonemgr/b10-zonemgr.xml
+++ b/src/bin/zonemgr/b10-zonemgr.xml
@@ -17,11 +17,10 @@
  - PERFORMANCE OF THIS SOFTWARE.
 -->
 
-<!-- $Id$ -->
 <refentry>
 
   <refentryinfo>
-    <date>September 8, 2010</date>
+    <date>October 18, 2010</date>
   </refentryinfo>
 
   <refmeta>
@@ -90,11 +89,39 @@
 
   <refsect1>
     <title>CONFIGURATION AND COMMANDS</title>
-<!--
     <para>
       The configurable settings are:
     </para>
--->
+    <para>
+      <varname>jitter_scope</varname>
+      defines the random jitter range subtracted from the refresh
+      and retry timers to avoid many zones from refreshing at the
+      same time.
+      The refresh or retry time actually used is a random time
+      between the defined refresh or retry time and it multiplied
+      by the <varname>jitter_scope</varname>.
+      This is re-evaluated after each refresh or retry.
+      This value is a real number and the maximum is 0.5 (half of the
+      refresh or retry time).
+      The default is 0.25.
+      Set to 0 to disable the jitter.
+    </para>
+    <para>
+      <varname>lowerbound_refresh</varname>
+      defines the minimum SOA REFRESH time in seconds.
+      The default is 10.
+    </para>
+    <para>
+      <varname>lowerbound_retry</varname>
+      defines the minimum SOA RETRY time in seconds.
+      The default is 5.
+    </para>
+    <para>
+      <varname>max_transfer_timeout</varname>
+      defines the maximum amount of time in seconds for a transfer.
+<!-- TODO: what is the purpose of this? -->
+      The default is 14400 (4 hours).
+    </para>
 
 <!-- TODO: formating -->
     <para>
diff --git a/src/bin/zonemgr/tests/zonemgr_test.py b/src/bin/zonemgr/tests/zonemgr_test.py
index 88e8af4..70dc1b0 100644
--- a/src/bin/zonemgr/tests/zonemgr_test.py
+++ b/src/bin/zonemgr/tests/zonemgr_test.py
@@ -43,6 +43,9 @@ class MySession():
         if module_name not in ("Auth", "Xfrin"):
             raise ZonemgrTestException("module name not exist")
 
+    def group_recvmsg(self, nonblock, seq):
+        return None, None
+
 class MyZonemgrRefresh(ZonemgrRefresh):
     def __init__(self):
         class FakeConfig:
diff --git a/src/bin/zonemgr/zonemgr.py.in b/src/bin/zonemgr/zonemgr.py.in
old mode 100644
new mode 100755
index 952856e..f9659a3
--- a/src/bin/zonemgr/zonemgr.py.in
+++ b/src/bin/zonemgr/zonemgr.py.in
@@ -266,7 +266,11 @@ class ZonemgrRefresh:
         """Send command between modules."""
         msg = create_command(command_name, params)
         try:
-            self._cc.group_sendmsg(msg, module_name)
+            seq = self._cc.group_sendmsg(msg, module_name)
+            try:
+                answer, env = self._cc.group_recvmsg(False, seq)
+            except isc.cc.session.SessionTimeout:
+                pass        # for now we just ignore the failure
         except socket.error:
             sys.stderr.write("[b10-zonemgr] Failed to send to module %s, the session has been closed." % module_name)
 
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
index 866ca65..b7b80bf 100644
--- a/src/lib/Makefile.am
+++ b/src/lib/Makefile.am
@@ -1,2 +1,2 @@
-SUBDIRS = exceptions dns cc config datasrc python xfr bench log asiolink \
-    testutils nsas
+SUBDIRS = exceptions dns cc config datasrc python xfr bench log \
+          resolve asiolink testutils nsas
diff --git a/src/lib/asiolink/Makefile.am b/src/lib/asiolink/Makefile.am
index 26d9a68..99b3c66 100644
--- a/src/lib/asiolink/Makefile.am
+++ b/src/lib/asiolink/Makefile.am
@@ -32,3 +32,4 @@ libasiolink_la_CXXFLAGS += -Wno-error
 endif
 libasiolink_la_CPPFLAGS = $(AM_CPPFLAGS)
 libasiolink_la_LIBADD = $(top_builddir)/src/lib/log/liblog.la
+libasiolink_la_LIBADD += $(top_builddir)/src/lib/resolve/libresolve.la
diff --git a/src/lib/asiolink/asiolink.cc b/src/lib/asiolink/asiolink.cc
index 9102eba..6fe7666 100644
--- a/src/lib/asiolink/asiolink.cc
+++ b/src/lib/asiolink/asiolink.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <config.h>
 
 #include <cstdlib> // For rand(), temporary until better forwarding is done
@@ -32,11 +30,15 @@
 
 #include <dns/buffer.h>
 #include <dns/message.h>
+#include <dns/rcode.h>
+#include <dns/opcode.h>
 
 #include <asiolink/asiolink.h>
 #include <asiolink/internal/tcpdns.h>
 #include <asiolink/internal/udpdns.h>
 
+#include <resolve/resolve.h>
+
 #include <log/dummylog.h>
 
 using namespace asio;
@@ -48,8 +50,46 @@ using namespace isc::dns;
 using isc::log::dlog;
 using namespace boost;
 
+// Is this something we can use in libdns++?
+namespace {
+    class SectionInserter {
+    public:
+        SectionInserter(MessagePtr message, const Message::Section sect) :
+            message_(message), section_(sect)
+        {}
+        void operator()(const RRsetPtr rrset) {
+            message_->addRRset(section_, rrset, true);
+        }
+        MessagePtr message_;
+        const Message::Section section_;
+    };
+
+
+    /// \brief Copies the parts relevant for a DNS answer to the
+    /// target message
+    ///
+    /// This adds all the RRsets in the answer, authority and
+    /// additional sections to the target, as well as the response
+    /// code
+    void copyAnswerMessage(const Message& source, MessagePtr target) {
+        target->setRcode(source.getRcode());
+
+        for_each(source.beginSection(Message::SECTION_ANSWER),
+                 source.endSection(Message::SECTION_ANSWER),
+                 SectionInserter(target, Message::SECTION_ANSWER));
+        for_each(source.beginSection(Message::SECTION_AUTHORITY),
+                 source.endSection(Message::SECTION_AUTHORITY),
+                 SectionInserter(target, Message::SECTION_AUTHORITY));
+        for_each(source.beginSection(Message::SECTION_ADDITIONAL),
+                 source.endSection(Message::SECTION_ADDITIONAL),
+                 SectionInserter(target, Message::SECTION_ADDITIONAL));
+    }
+}
+
 namespace asiolink {
 
+typedef pair<string, uint16_t> addr_t;
+
 class IOServiceImpl {
 private:
     IOServiceImpl(const IOService& source);
@@ -244,10 +284,19 @@ typedef std::vector<std::pair<std::string, uint16_t> > AddressVector;
 
 }
 
+// Here we do not use the typedef above, as the SunStudio compiler
+// mishandles this in its name mangling, and wouldn't compile.
+// We can probably use a typedef, but need to move it to a central
+// location and use it consistently.
 RecursiveQuery::RecursiveQuery(DNSService& dns_service,
-    const AddressVector& upstream, int timeout, unsigned retries) :
+    const std::vector<std::pair<std::string, uint16_t> >& upstream,
+    const std::vector<std::pair<std::string, uint16_t> >& upstream_root,
+    int query_timeout, int client_timeout, int lookup_timeout,
+    unsigned retries) :
     dns_service_(dns_service), upstream_(new AddressVector(upstream)),
-    timeout_(timeout), retries_(retries)
+    upstream_root_(new AddressVector(upstream_root)),
+    query_timeout_(query_timeout), client_timeout_(client_timeout),
+    lookup_timeout_(lookup_timeout), retries_(retries)
 {}
 
 namespace {
@@ -298,25 +347,56 @@ private:
 
     // Info for (re)sending the query (the question and destination)
     Question question_;
+
+    // This is where we build and store our final answer
+    MessagePtr answer_message_;
+
+    // currently we use upstream as the current list of NS records
+    // we should differentiate between forwarding and resolving
     shared_ptr<AddressVector> upstream_;
 
+    // root servers...just copied over to the zone_servers_
+    shared_ptr<AddressVector> upstream_root_;
+
     // Buffer to store the result.
     OutputBufferPtr buffer_;
 
     // Server to notify when we succeed or fail
-    shared_ptr<DNSServer> server_;
+    //shared_ptr<DNSServer> server_;
+    isc::resolve::ResolverInterface::CallbackPtr resolvercallback_;
 
     /*
      * TODO Do something more clever with timeouts. In the long term, some
      *     computation of average RTT, increase with each retry, etc.
      */
     // Timeout information
-    int timeout_;
+    int query_timeout_;
     unsigned retries_;
 
+    // normal query state
+
+    // Not using NSAS at this moment, so we keep a list
+    // of 'current' zone servers
+    vector<addr_t> zone_servers_;
+
+    // Update the question that will be sent to the server
+    void setQuestion(const Question& new_question) {
+        question_ = new_question;
+    }
+
+    deadline_timer client_timer;
+    deadline_timer lookup_timer;
+
+    size_t queries_out_;
+
+    // If we timed out ourselves (lookup timeout), stop issuing queries
+    bool done_;
+
     // (re)send the query to the server.
     void send() {
         const int uc = upstream_->size();
+        const int zs = zone_servers_.size();
+        buffer_->clear();
         if (uc > 0) {
             int serverIndex = rand() % uc;
             dlog("Sending upstream query (" + question_.toText() +
@@ -324,37 +404,208 @@ private:
             UDPQuery query(io_, question_,
                 upstream_->at(serverIndex).first,
                 upstream_->at(serverIndex).second, buffer_, this,
-                timeout_);
+                query_timeout_);
+            ++queries_out_;
+            io_.post(query);
+        } else if (zs > 0) {
+            int serverIndex = rand() % zs;
+            dlog("Sending query to zone server (" + question_.toText() +
+                ") to " + zone_servers_.at(serverIndex).first);
+            UDPQuery query(io_, question_,
+                zone_servers_.at(serverIndex).first,
+                zone_servers_.at(serverIndex).second, buffer_, this,
+                query_timeout_);
+            ++queries_out_;
             io_.post(query);
         } else {
             dlog("Error, no upstream servers to send to.");
         }
     }
+    
+    // This function is called by operator() if there is an actual
+    // answer from a server and we are in recursive mode
+    // depending on the contents, we go on recursing or return
+    //
+    // Note that the footprint may change as this function may
+    // need to append data to the answer we are building later.
+    //
+    // returns true if we are done
+    // returns false if we are not done
+    bool handleRecursiveAnswer(const Message& incoming) {
+        if (incoming.getRRCount(Message::SECTION_ANSWER) > 0) {
+            dlog("Got final result, copying answer.");
+            copyAnswerMessage(incoming, answer_message_);
+            return true;
+        } else {
+            dlog("Got delegation, continuing");
+            // ok we need to do some more processing.
+            // the ns list should contain all nameservers
+            // while the additional may contain addresses for
+            // them.
+            // this needs to tie into NSAS of course
+            // for this very first mockup, hope there is an
+            // address in additional and just use that
+
+            // send query to the addresses in the delegation
+            bool found_ns_address = false;
+            zone_servers_.clear();
+
+            for (RRsetIterator rrsi = incoming.beginSection(Message::SECTION_ADDITIONAL);
+                 rrsi != incoming.endSection(Message::SECTION_ADDITIONAL) && !found_ns_address;
+                 rrsi++) {
+                ConstRRsetPtr rrs = *rrsi;
+                if (rrs->getType() == RRType::A()) {
+                    // found address
+                    RdataIteratorPtr rdi = rrs->getRdataIterator();
+                    // just use the first for now
+                    if (!rdi->isLast()) {
+                        std::string addr_str = rdi->getCurrent().toText();
+                        dlog("[XX] first address found: " + addr_str);
+                        // now we have one address, simply
+                        // resend that exact same query
+                        // to that address and yield, when it
+                        // returns, loop again.
+                        
+                        // should use NSAS
+                        zone_servers_.push_back(addr_t(addr_str, 53));
+                        found_ns_address = true;
+                    }
+                }
+            }
+            if (found_ns_address) {
+                // next resolver round
+                send();
+                return false;
+            } else {
+                dlog("[XX] no ready-made addresses in additional. need nsas.");
+                // this will result in answering with the delegation. oh well
+                copyAnswerMessage(incoming, answer_message_);
+                return true;
+            }
+        }
+    }
+    
+
 public:
     RunningQuery(asio::io_service& io, const Question &question,
-        shared_ptr<AddressVector> upstream,
-        OutputBufferPtr buffer, DNSServer* server, int timeout,
+        MessagePtr answer_message, shared_ptr<AddressVector> upstream,
+        shared_ptr<AddressVector> upstream_root,
+        OutputBufferPtr buffer,
+        isc::resolve::ResolverInterface::CallbackPtr cb,
+        int query_timeout, int client_timeout, int lookup_timeout,
         unsigned retries) :
         io_(io),
         question_(question),
+        answer_message_(answer_message),
         upstream_(upstream),
+        upstream_root_(upstream_root),
         buffer_(buffer),
-        server_(server->clone()),
-        timeout_(timeout),
-        retries_(retries)
+        resolvercallback_(cb),
+        query_timeout_(query_timeout),
+        retries_(retries),
+        client_timer(io),
+        lookup_timer(io),
+        queries_out_(0),
+        done_(false)
     {
+        // Setup the timer to stop trying (lookup_timeout)
+        if (lookup_timeout >= 0) {
+            lookup_timer.expires_from_now(
+                boost::posix_time::milliseconds(lookup_timeout));
+            lookup_timer.async_wait(boost::bind(&RunningQuery::stop, this, false));
+        }
+        
+        // Setup the timer to send an answer (client_timeout)
+        if (client_timeout >= 0) {
+            client_timer.expires_from_now(
+                boost::posix_time::milliseconds(client_timeout));
+            client_timer.async_wait(boost::bind(&RunningQuery::clientTimeout, this));
+        }
+        
+        // should use NSAS for root servers
+        // Adding root servers if not a forwarder
+        if (upstream_->empty()) {
+            if (upstream_root_->empty()) { //if no root ips given, use this
+                zone_servers_.push_back(addr_t("192.5.5.241", 53));
+            }
+            else
+            {
+              //copy the list
+              dlog("Size is " + 
+                    boost::lexical_cast<string>(upstream_root_->size()) + 
+                    "\n");
+              //Use BOOST_FOREACH here? Is it faster?
+              for(AddressVector::iterator it = upstream_root_->begin();
+                   it < upstream_root_->end(); it++) {
+                zone_servers_.push_back(addr_t(it->first,it->second));
+                dlog("Put " + zone_servers_.back().first + "into root list\n");
+              }
+            }
+        }
+
         send();
     }
 
+    virtual void clientTimeout() {
+        // right now, just stop (should make SERVFAIL and send that
+        // back, but not stop)
+        stop(false);
+    }
+
+    virtual void stop(bool resume) {
+        // if we cancel our timers, we will still get an event for
+        // that, so we cannot delete ourselves just yet (those events
+        // would be bound to a deleted object)
+        // cancel them one by one, both cancels should get us back
+        // here again.
+        // same goes if we have an outstanding query (can't delete
+        // until that one comes back to us)
+        done_ = true;
+        if (resume) {
+            resolvercallback_->success(answer_message_);
+        } else {
+            resolvercallback_->failure();
+        }
+        if (lookup_timer.cancel() != 0) {
+            return;
+        }
+        if (client_timer.cancel() != 0) {
+            return;
+        }
+        if (queries_out_ > 0) {
+            return;
+        }
+        delete this;
+    }
+
     // This function is used as callback from DNSQuery.
     virtual void operator()(UDPQuery::Result result) {
-        if (result == UDPQuery::TIME_OUT && retries_ --) {
-            dlog("Resending query");
+        // XXX is this the place for TCP retry?
+        --queries_out_;
+        if (!done_ && result != UDPQuery::TIME_OUT) {
+            // we got an answer
+            Message incoming(Message::PARSE);
+            InputBuffer ibuf(buffer_->getData(), buffer_->getLength());
+            incoming.fromWire(ibuf);
+
+            if (upstream_->size() == 0 &&
+                incoming.getRcode() == Rcode::NOERROR()) {
+                done_ = handleRecursiveAnswer(incoming);
+            } else {
+                copyAnswerMessage(incoming, answer_message_);
+                done_ = true;
+            }
+            
+            if (done_) {
+                stop(true);
+            }
+        } else if (!done_ && retries_--) {
             // We timed out, but we have some retries, so send again
+            dlog("Timeout, resending query");
             send();
         } else {
-            server_->resume(result == UDPQuery::SUCCESS);
-            delete this;
+            // out of retries, give up for now
+            stop(false);
         }
     }
 };
@@ -362,17 +613,39 @@ public:
 }
 
 void
-RecursiveQuery::sendQuery(const Question& question, OutputBufferPtr buffer,
-                          DNSServer* server)
+RecursiveQuery::resolve(const isc::dns::QuestionPtr& question,
+    const isc::resolve::ResolverInterface::CallbackPtr callback)
+{
+    asio::io_service& io = dns_service_.get_io_service();
+
+    MessagePtr answer_message(new Message(Message::RENDER));
+    OutputBufferPtr buffer(new OutputBuffer(0));
+    
+    // It will delete itself when it is done
+    new RunningQuery(io, *question, answer_message, upstream_,
+                     upstream_root_, buffer, callback, query_timeout_,
+                     client_timeout_, lookup_timeout_, retries_);
+}
+
+void
+RecursiveQuery::resolve(const Question& question,
+                        MessagePtr answer_message,
+                        OutputBufferPtr buffer,
+                        DNSServer* server)
 {
     // XXX: eventually we will need to be able to determine whether
     // the message should be sent via TCP or UDP, or sent initially via
     // UDP and then fall back to TCP on failure, but for the moment
     // we're only going to handle UDP.
     asio::io_service& io = dns_service_.get_io_service();
+
+    isc::resolve::ResolverInterface::CallbackPtr crs(
+        new isc::resolve::ResolverCallbackServer(server));
+    
     // It will delete itself when it is done
-    new RunningQuery(io, question, upstream_, buffer, server,
-         timeout_, retries_);
+    new RunningQuery(io, question, answer_message, upstream_, upstream_root_,
+                         buffer, crs, query_timeout_, client_timeout_,
+                         lookup_timeout_, retries_);
 }
 
 class IntervalTimerImpl {
diff --git a/src/lib/asiolink/asiolink.h b/src/lib/asiolink/asiolink.h
index 1178ceb..3b3fa93 100644
--- a/src/lib/asiolink/asiolink.h
+++ b/src/lib/asiolink/asiolink.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __ASIOLINK_H
 #define __ASIOLINK_H 1
 
@@ -33,6 +31,7 @@
 #include <dns/buffer.h>
 #include <dns/message.h>
 #include <dns/question.h>
+#include <dns/rcode.h>
 
 #include <exceptions/exceptions.h>
 
@@ -41,6 +40,8 @@
 #include <asiolink/iomessage.h>
 #include <asiolink/iosocket.h>
 
+#include <resolve/resolver_interface.h>
+
 namespace asio {
 // forward declaration for IOService::get_io_service() below
 class io_service;
@@ -101,6 +102,7 @@ class DNSServiceImpl;
 struct IOServiceImpl;
 struct IntervalTimerImpl;
 
+
 /// \brief An exception that is thrown if an error occurs within the IO
 /// module.  This is mainly intended to be a wrapper exception class for
 /// ASIO specific exceptions.
@@ -414,10 +416,11 @@ public:
     /// \param DNSServer DNSServer object to use
     virtual void operator()(const IOMessage& io_message,
                             isc::dns::MessagePtr message,
+                            isc::dns::MessagePtr answer_message,
                             isc::dns::OutputBufferPtr buffer,
                             DNSServer* server) const
     {
-        (*self_)(io_message, message, buffer, server);
+        (*self_)(io_message, message, answer_message, buffer, server);
     }
 private:
     DNSLookup* self_;
@@ -467,6 +470,7 @@ public:
     /// \param buffer The result is put here
     virtual void operator()(const IOMessage& io_message,
                             isc::dns::MessagePtr message,
+                            isc::dns::MessagePtr answer_message,
                             isc::dns::OutputBufferPtr buffer) const = 0;
 };
 
@@ -529,15 +533,19 @@ class RecursiveQuery {
     ///
     //@{
 public:
-    /// \brief Constructor for use when acting as a forwarder
+    /// \brief Constructor
     ///
     /// This is currently the only way to construct \c RecursiveQuery
-    /// object.  The addresses of the forward nameservers is specified,
-    /// and every upstream query will be sent to one random address.
+    /// object. If the addresses of the forward nameservers is specified,
+    /// and every upstream query will be sent to one random address, and
+    /// the result sent back directly. If not, it will do full resolving.
+    ///
     /// \param dns_service The DNS Service to perform the recursive
     ///        query on.
     /// \param upstream Addresses and ports of the upstream servers
     ///        to forward queries to.
+    /// \param upstream_root Addresses and ports of the root servers
+    ///        to use when resolving.
     /// \param timeout How long to timeout the query, in ms
     ///     -1 means never timeout (but do not use that).
     ///     TODO: This should be computed somehow dynamically in future
@@ -545,27 +553,60 @@ public:
     ///     and return if it returs).
     RecursiveQuery(DNSService& dns_service,
                    const std::vector<std::pair<std::string, uint16_t> >&
-                   upstream, int timeout = -1, unsigned retries = 0);
+                   upstream, 
+                   const std::vector<std::pair<std::string, uint16_t> >&
+                   upstream_root, 
+                   int query_timeout = 2000,
+                   int client_timeout = 4000,
+                   int lookup_timeout = 30000,
+                   unsigned retries = 3);
     //@}
 
-    /// \brief Initiates an upstream query in the \c RecursiveQuery object.
+    /// \brief Initiate resolving
+    /// 
+    /// When sendQuery() is called, a (set of) message(s) is sent
+    /// asynchronously. If upstream servers are set, one is chosen
+    /// and the response (if any) from that server will be returned.
+    ///
+    /// If not upstream is set, a root server is chosen from the
+    /// root_servers, and the RunningQuery shall do a full resolve
+    /// (i.e. if the answer is a delegation, it will be followed, etc.)
+    /// until there is an answer or an error.
     ///
-    /// When sendQuery() is called, a message is sent asynchronously to
-    /// the upstream name server.  When a reply arrives, 'server'
-    /// is placed on the ASIO service queue via io_service::post(), so
-    /// that the original \c DNSServer objct can resume processing.
+    /// When there is a response or an error and we give up, the given
+    /// CallbackPtr object shall be called (with either success() or
+    /// failure(). See ResolverInterface::Callback for more information.
     ///
     /// \param question The question being answered <qname/qclass/qtype>
-    /// \param buffer An output buffer into which the response can be copied
+    /// \param callback Callback object. See
+    ///        \c ResolverInterface::Callback for more information
+    void resolve(const isc::dns::QuestionPtr& question,
+                 const isc::resolve::ResolverInterface::CallbackPtr callback);
+
+
+    /// \brief Initiates resolving for the given question.
+    ///
+    /// This actually calls the previous sendQuery() with a default
+    /// callback object, which calls resume() on the given DNSServer
+    /// object.
+    ///
+    /// \param question The question being answered <qname/qclass/qtype>
+    /// \param answer_message An output Message into which the final response will be copied
+    /// \param buffer An output buffer into which the intermediate responses will be copied
     /// \param server A pointer to the \c DNSServer object handling the client
-    void sendQuery(const isc::dns::Question& question,
-                   isc::dns::OutputBufferPtr buffer,
-                   DNSServer* server);
+    void resolve(const isc::dns::Question& question,
+                 isc::dns::MessagePtr answer_message,
+                 isc::dns::OutputBufferPtr buffer,
+                 DNSServer* server);
 private:
     DNSService& dns_service_;
     boost::shared_ptr<std::vector<std::pair<std::string, uint16_t> > >
         upstream_;
-    int timeout_;
+    boost::shared_ptr<std::vector<std::pair<std::string, uint16_t> > >
+        upstream_root_;
+    int query_timeout_;
+    int client_timeout_;
+    int lookup_timeout_;
     unsigned retries_;
 };
 
diff --git a/src/lib/asiolink/internal/tcpdns.h b/src/lib/asiolink/internal/tcpdns.h
index da3869f..a97ed17 100644
--- a/src/lib/asiolink/internal/tcpdns.h
+++ b/src/lib/asiolink/internal/tcpdns.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __TCPDNS_H
 #define __TCPDNS_H 1
 
@@ -197,7 +195,8 @@ private:
     // \c IOMessage and \c Message objects to be passed to the
     // DNS lookup and answer providers
     boost::shared_ptr<asiolink::IOMessage> io_message_;
-    isc::dns::MessagePtr message_;
+    isc::dns::MessagePtr query_message_;
+    isc::dns::MessagePtr answer_message_;
 
     // The buffer into which the query packet is written
     boost::shared_array<char>data_;
diff --git a/src/lib/asiolink/internal/udpdns.h b/src/lib/asiolink/internal/udpdns.h
index c41dca2..5b67ca9 100644
--- a/src/lib/asiolink/internal/udpdns.h
+++ b/src/lib/asiolink/internal/udpdns.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __UDPDNS_H
 #define __UDPDNS_H 1
 
@@ -157,7 +155,7 @@ public:
     /// \brief Check if we have an answer
     ///
     /// \return true if we have an answer
-    bool hasAnswer() { return (done_); }
+    bool hasAnswer();
 
     /// \brief Returns the coroutine state value
     ///
@@ -175,61 +173,17 @@ public:
 private:
     enum { MAX_LENGTH = 4096 };
 
-    // The ASIO service object
-    asio::io_service& io_;
-
-    // Class member variables which are dynamic, and changes to which
-    // need to accessible from both sides of a coroutine fork or from
-    // outside of the coroutine (i.e., from an asynchronous I/O call),
-    // should be declared here as pointers and allocated in the
-    // constructor or in the coroutine.  This allows state information
-    // to persist when an individual copy of the coroutine falls out
-    // scope while waiting for an event, *so long as* there is another
-    // object that is referencing the same data.  As a side-benefit, using
-    // pointers also reduces copy overhead for coroutine objects.
-    //
-    // Note: Currently these objects are allocated by "new" in the
-    // constructor, or in the function operator while processing a query.
-    // Repeated allocations from the heap for every incoming query is
-    // clearly a performance issue; this must be optimized in the future.
-    // The plan is to have a structure pre-allocate several "server state"
-    // objects which can be pulled off a free list and placed on an in-use
-    // list whenever a query comes in.  This will serve the dual purpose
-    // of improving performance and guaranteeing that state information
-    // will *not* be destroyed when any one instance of the coroutine
-    // falls out of scope while waiting for an event.
-    //
-    // Socket used to for listen for queries.  Created in the
-    // constructor and stored in a shared_ptr because socket objects
-    // are not copyable.
-    boost::shared_ptr<asio::ip::udp::socket> socket_;
-
-    // The ASIO-enternal endpoint object representing the client
-    boost::shared_ptr<asio::ip::udp::endpoint> sender_;
-
-    // \c IOMessage and \c Message objects to be passed to the
-    // DNS lookup and answer providers
-    boost::shared_ptr<asiolink::IOMessage> io_message_;
-    isc::dns::MessagePtr message_;
-
-    // The buffer into which the response is written
-    isc::dns::OutputBufferPtr respbuf_;
-    
-    // The buffer into which the query packet is written
-    boost::shared_array<char> data_;
-
-    // State information that is entirely internal to a given instance
-    // of the coroutine can be declared here.
-    size_t bytes_;
-    bool done_;
-
-    // Callback functions provided by the caller
-    const SimpleCallback* checkin_callback_;
-    const DNSLookup* lookup_callback_;
-    const DNSAnswer* answer_callback_;
-
-    boost::shared_ptr<IOEndpoint> peer_;
-    boost::shared_ptr<IOSocket> iosock_;
+    /**
+     * \brief Internal state and data.
+     *
+     * We use the pimple design pattern, but not because we need to hide
+     * internal data. This class and whole header is for private use anyway.
+     * It turned out that UDPServer is copied a lot, because it is a coroutine.
+     * This way the overhead of copying is lower, we copy only one shared
+     * pointer instead of about 10 of them.
+     */
+    class Data;
+    boost::shared_ptr<Data> data_;
 };
 
 //
diff --git a/src/lib/asiolink/ioaddress.cc b/src/lib/asiolink/ioaddress.cc
index 8c53c43..990524a 100644
--- a/src/lib/asiolink/ioaddress.cc
+++ b/src/lib/asiolink/ioaddress.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <config.h>
 
 #include <unistd.h>             // for some IPC/network system calls
diff --git a/src/lib/asiolink/ioaddress.h b/src/lib/asiolink/ioaddress.h
index 884e3cf..98e6fe8 100644
--- a/src/lib/asiolink/ioaddress.h
+++ b/src/lib/asiolink/ioaddress.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __IOADDRESS_H
 #define __IOADDRESS_H 1
 
@@ -75,9 +73,48 @@ public:
     /// \return A string representation of the address.
     std::string toText() const;
 
-    /// \brief Returns the address family.
+    /// \brief Returns the address family
+    ///
+    /// \return AF_INET for IPv4 or AF_INET6 for IPv6.
     short getFamily() const;
 
+    /// \brief Compare addresses for equality
+    ///
+    /// \param other Address to compare against.
+    ///
+    /// \return true if addresses are equal, false if not.
+    bool equals(const IOAddress& other) const {
+        return (asio_address_ == other.asio_address_);
+    }
+
+    /// \brief Compare addresses for equality
+    ///
+    /// \param other Address to compare against.
+    ///
+    /// \return true if addresses are equal, false if not.
+    bool operator==(const IOAddress& other) const {
+        return equals(other);
+    }
+
+    // \brief Compare addresses for inequality
+    ///
+    /// \param other Address to compare against.
+    ///
+    /// \return false if addresses are equal, true if not.
+    bool nequals(const IOAddress& other) const {
+        return (!equals(other));
+    }
+
+    // \brief Compare addresses for inequality
+    ///
+    /// \param other Address to compare against.
+    ///
+    /// \return false if addresses are equal, true if not.
+    bool operator!=(const IOAddress& other) const {
+        return (nequals(other));
+    }
+
+
 private:
     asio::ip::address asio_address_;
 };
diff --git a/src/lib/asiolink/ioendpoint.cc b/src/lib/asiolink/ioendpoint.cc
index 968ab22..2807f8d 100644
--- a/src/lib/asiolink/ioendpoint.cc
+++ b/src/lib/asiolink/ioendpoint.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <config.h>
 
 #include <unistd.h>             // for some IPC/network system calls
diff --git a/src/lib/asiolink/ioendpoint.h b/src/lib/asiolink/ioendpoint.h
index 36aceaa..82190a3 100644
--- a/src/lib/asiolink/ioendpoint.h
+++ b/src/lib/asiolink/ioendpoint.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __IOENDPOINT_H
 #define __IOENDPOINT_H 1
 
@@ -26,6 +24,7 @@
 #include <string>
 
 #include <exceptions/exceptions.h>
+#include <asiolink/ioaddress.h>
 
 namespace asiolink {
 
diff --git a/src/lib/asiolink/iomessage.h b/src/lib/asiolink/iomessage.h
index f3e376b..27baa8a 100644
--- a/src/lib/asiolink/iomessage.h
+++ b/src/lib/asiolink/iomessage.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __IOMESSAGE_H
 #define __IOMESSAGE_H 1
 
diff --git a/src/lib/asiolink/iosocket.cc b/src/lib/asiolink/iosocket.cc
index dea859d..a3967d4 100644
--- a/src/lib/asiolink/iosocket.cc
+++ b/src/lib/asiolink/iosocket.cc
@@ -14,8 +14,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include "iosocket.h"
 
 #include <asio.hpp>
diff --git a/src/lib/asiolink/iosocket.h b/src/lib/asiolink/iosocket.h
index d58429d..df37d71 100644
--- a/src/lib/asiolink/iosocket.h
+++ b/src/lib/asiolink/iosocket.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __IOSOCKET_H
 #define __IOSOCKET_H 1
 
diff --git a/src/lib/asiolink/tcpdns.cc b/src/lib/asiolink/tcpdns.cc
index c998aa2..c00b87a 100644
--- a/src/lib/asiolink/tcpdns.cc
+++ b/src/lib/asiolink/tcpdns.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <config.h>
 
 #include <unistd.h>             // for some IPC/network system calls
@@ -144,7 +142,8 @@ TCPServer::operator()(error_code ec, size_t length) {
         // Reset or instantiate objects that will be needed by the
         // DNS lookup and the write call.
         respbuf_.reset(new OutputBuffer(0));
-        message_.reset(new Message(Message::PARSE));
+        query_message_.reset(new Message(Message::PARSE));
+        answer_message_.reset(new Message(Message::RENDER));
 
         // Schedule a DNS lookup, and yield.  When the lookup is
         // finished, the coroutine will resume immediately after
@@ -159,7 +158,8 @@ TCPServer::operator()(error_code ec, size_t length) {
 
         // Call the DNS answer provider to render the answer into
         // wire format
-        (*answer_callback_)(*io_message_, message_, respbuf_);
+        (*answer_callback_)(*io_message_, query_message_,
+                            answer_message_, respbuf_);
 
         // Set up the response, beginning with two length bytes.
         lenbuf.writeUint16(respbuf_->getLength());
@@ -178,7 +178,8 @@ TCPServer::operator()(error_code ec, size_t length) {
 /// AsyncLookup<TCPServer> handler.)
 void
 TCPServer::asyncLookup() {
-    (*lookup_callback_)(*io_message_, message_, respbuf_, this);
+    (*lookup_callback_)(*io_message_, query_message_,
+                        answer_message_, respbuf_, this);
 }
 
 /// Post this coroutine on the ASIO service queue so that it will
diff --git a/src/lib/asiolink/tests/Makefile.am b/src/lib/asiolink/tests/Makefile.am
index 3c6cd3e..e301fb2 100644
--- a/src/lib/asiolink/tests/Makefile.am
+++ b/src/lib/asiolink/tests/Makefile.am
@@ -20,7 +20,7 @@ run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
 run_unittests_SOURCES += asiolink_unittest.cc
 run_unittests_SOURCES += run_unittests.cc
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) $(LOG4CXX_LDFLAGS)
 run_unittests_LDADD = $(GTEST_LDADD)
 run_unittests_LDADD += $(SQLITE_LIBS)
 run_unittests_LDADD +=  $(top_builddir)/src/lib/dns/libdns++.la
diff --git a/src/lib/asiolink/tests/asiolink_unittest.cc b/src/lib/asiolink/tests/asiolink_unittest.cc
index 44ccee8..2044d50 100644
--- a/src/lib/asiolink/tests/asiolink_unittest.cc
+++ b/src/lib/asiolink/tests/asiolink_unittest.cc
@@ -12,9 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
-
 #include <config.h>
 
 #include <sys/socket.h>
@@ -31,6 +28,7 @@
 #include <exceptions/exceptions.h>
 
 #include <dns/tests/unittest_util.h>
+#include <dns/rcode.h>
 
 #include <dns/buffer.h>
 #include <dns/message.h>
@@ -84,6 +82,23 @@ TEST(IOAddressTest, fromText) {
     EXPECT_THROW(IOAddress("2001:db8::efgh"), IOError);
 }
 
+TEST(IOAddressTest, Equality) {
+    EXPECT_TRUE(IOAddress("192.0.2.1") == IOAddress("192.0.2.1"));
+    EXPECT_FALSE(IOAddress("192.0.2.1") != IOAddress("192.0.2.1"));
+
+    EXPECT_TRUE(IOAddress("192.0.2.1") != IOAddress("192.0.2.2"));
+    EXPECT_FALSE(IOAddress("192.0.2.1") == IOAddress("192.0.2.2"));
+
+    EXPECT_TRUE(IOAddress("2001:db8::12") == IOAddress("2001:0DB8:0:0::0012"));
+    EXPECT_FALSE(IOAddress("2001:db8::12") != IOAddress("2001:0DB8:0:0::0012"));
+
+    EXPECT_TRUE(IOAddress("2001:db8::1234") != IOAddress("2001:db8::1235"));
+    EXPECT_FALSE(IOAddress("2001:db8::1234") == IOAddress("2001:db8::1235"));
+
+    EXPECT_TRUE(IOAddress("2001:db8::1234") != IOAddress("192.0.2.3"));
+    EXPECT_FALSE(IOAddress("2001:db8::1234") == IOAddress("192.0.2.3"));
+}
+
 TEST(IOEndpointTest, createUDPv4) {
     const IOEndpoint* ep;
     ep = IOEndpoint::create(IPPROTO_UDP, IOAddress("192.0.2.1"), 5300);
@@ -156,14 +171,14 @@ TEST(IOServiceTest, unavailableAddress) {
     IOService io_service;
     // These addresses should generally be unavailable as a valid local
     // address, although there's no guarantee in theory.
-    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"255.255.0.0", NULL, NULL, NULL), IOError);
+    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"192.0.2.0", NULL, NULL, NULL), IOError);
 
     // Some OSes would simply reject binding attempt for an AF_INET6 socket
     // to an IPv4-mapped IPv6 address.  Even if those that allow it, since
     // the corresponding IPv4 address is the same as the one used in the
     // AF_INET socket case above, it should at least show the same result
     // as the previous one.
-    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"::ffff:255.255.0.0", NULL, NULL, NULL), IOError);
+    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"::ffff:192.0.2.0", NULL, NULL, NULL), IOError);
 }
 
 TEST(IOServiceTest, duplicateBind_v6) {
@@ -442,6 +457,7 @@ protected:
                             DNSAnswer* answer = NULL) :
             io_(io_service),
             message_(new Message(Message::PARSE)),
+            answer_message_(new Message(Message::RENDER)),
             respbuf_(new OutputBuffer(0)),
             checkin_(checkin), lookup_(lookup), answer_(answer)
         {}
@@ -450,7 +466,8 @@ protected:
                         size_t length = 0)
         {}
 
-        void resume(const bool) { // in our test this shouldn't be called
+        void resume(const bool) {
+          // should never be called in our tests
         }
 
         DNSServer* clone() {
@@ -460,7 +477,8 @@ protected:
 
         inline void asyncLookup() {
             if (lookup_) {
-                (*lookup_)(*io_message_, message_, respbuf_, this);
+                (*lookup_)(*io_message_, message_, answer_message_,
+                           respbuf_, this);
             }
         }
 
@@ -473,6 +491,7 @@ protected:
         // asynchronous lookup calls via the asyncLookup() method
         boost::shared_ptr<asiolink::IOMessage> io_message_;
         isc::dns::MessagePtr message_;
+        isc::dns::MessagePtr answer_message_;
         isc::dns::OutputBufferPtr respbuf_;
 
         // Callback functions provided by the caller
@@ -643,15 +662,17 @@ singleAddress(const string &address, uint16_t port) {
 TEST_F(ASIOLinkTest, recursiveSetupV4) {
     setDNSService(true, false);
     uint16_t port = boost::lexical_cast<uint16_t>(TEST_CLIENT_PORT);
-    EXPECT_NO_THROW(RecursiveQuery(*dns_service_, singleAddress(TEST_IPV6_ADDR,
-        port)));
+    EXPECT_NO_THROW(RecursiveQuery(*dns_service_,
+                                   singleAddress(TEST_IPV4_ADDR, port),
+                                   singleAddress(TEST_IPV4_ADDR, port)));
 }
 
 TEST_F(ASIOLinkTest, recursiveSetupV6) {
     setDNSService(false, true);
     uint16_t port = boost::lexical_cast<uint16_t>(TEST_CLIENT_PORT);
-    EXPECT_NO_THROW(RecursiveQuery(*dns_service_, singleAddress(TEST_IPV6_ADDR,
-        port)));
+    EXPECT_NO_THROW(RecursiveQuery(*dns_service_,
+                                   singleAddress(TEST_IPV6_ADDR, port),
+                                   singleAddress(TEST_IPV6_ADDR,port)));
 }
 
 // XXX:
@@ -659,7 +680,7 @@ TEST_F(ASIOLinkTest, recursiveSetupV6) {
 // a routine that can do this with variable address family, address, and
 // port, and with the various callbacks defined in such a way as to ensure
 // full code coverage including error cases.
-TEST_F(ASIOLinkTest, recursiveSend) {
+TEST_F(ASIOLinkTest, forwarderSend) {
     setDNSService(true, false);
 
     // Note: We use the test prot plus one to ensure we aren't binding
@@ -667,11 +688,14 @@ TEST_F(ASIOLinkTest, recursiveSend) {
     uint16_t port = boost::lexical_cast<uint16_t>(TEST_CLIENT_PORT);
 
     MockServer server(*io_service_);
-    RecursiveQuery rq(*dns_service_, singleAddress(TEST_IPV4_ADDR, port));
+    RecursiveQuery rq(*dns_service_,
+                      singleAddress(TEST_IPV4_ADDR, port),
+                      singleAddress(TEST_IPV4_ADDR, port));
 
     Question q(Name("example.com"), RRClass::IN(), RRType::TXT());
     OutputBufferPtr buffer(new OutputBuffer(0));
-    rq.sendQuery(q, buffer, &server);
+    MessagePtr answer(new Message(Message::RENDER));
+    rq.resolve(q, answer, buffer, &server);
 
     char data[4096];
     size_t size = sizeof(data);
@@ -691,20 +715,59 @@ TEST_F(ASIOLinkTest, recursiveSend) {
     EXPECT_EQ(q.getClass(), q2->getClass());
 }
 
-// Test it tries the correct amount of times before giving up
-TEST_F(ASIOLinkTest, recursiveTimeout) {
-    // Prepare the service (we do not use the common setup, we do not answer
-    setDNSService();
-
-    // Prepare the socket
-    res_ = resolveAddress(AF_INET, IPPROTO_UDP, true);
-    sock_ = socket(res_->ai_family, res_->ai_socktype, res_->ai_protocol);
+int
+createTestSocket()
+{
+    struct addrinfo* res_ = resolveAddress(AF_INET, IPPROTO_UDP, true);
+    int sock_ = socket(res_->ai_family, res_->ai_socktype, res_->ai_protocol);
     if (sock_ < 0) {
         isc_throw(IOError, "failed to open test socket");
     }
     if (bind(sock_, res_->ai_addr, res_->ai_addrlen) < 0) {
         isc_throw(IOError, "failed to bind test socket");
     }
+    return sock_;
+}
+
+int
+setSocketTimeout(int sock_, size_t tv_sec, size_t tv_usec) {
+    const struct timeval timeo = { tv_sec, tv_usec };
+    int recv_options = 0;
+    if (setsockopt(sock_, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo))) {
+        if (errno == ENOPROTOOPT) { // see ASIOLinkTest::recvUDP()
+            recv_options = MSG_DONTWAIT;
+        } else {
+            isc_throw(IOError, "set RCVTIMEO failed: " << strerror(errno));
+        }
+    }
+    return recv_options;
+}
+
+// try to read from the socket max time
+// *num is incremented for every succesfull read
+// returns true if it can read max times, false otherwise
+bool tryRead(int sock_, int recv_options, size_t max, int* num) {
+    size_t i = 0;
+    do {
+        char inbuff[512];
+        if (recv(sock_, inbuff, sizeof(inbuff), recv_options) < 0) {
+            return false;
+        } else {
+            ++i;
+            ++*num;
+        }
+    } while (i < max);
+    return true;
+}
+
+
+// Test it tries the correct amount of times before giving up
+TEST_F(ASIOLinkTest, forwardQueryTimeout) {
+    // Prepare the service (we do not use the common setup, we do not answer
+    setDNSService();
+
+    // Prepare the socket
+    sock_ = createTestSocket();
 
     // Prepare the server
     bool done(true);
@@ -712,40 +775,171 @@ TEST_F(ASIOLinkTest, recursiveTimeout) {
 
     // Do the answer
     const uint16_t port = boost::lexical_cast<uint16_t>(TEST_CLIENT_PORT);
-    RecursiveQuery query(*dns_service_, singleAddress(TEST_IPV4_ADDR, port),
-        10, 2);
+    RecursiveQuery query(*dns_service_,
+                         singleAddress(TEST_IPV4_ADDR, port),
+                         singleAddress(TEST_IPV4_ADDR, port),
+                         10, 4000, 3000, 2);
     Question question(Name("example.net"), RRClass::IN(), RRType::A());
     OutputBufferPtr buffer(new OutputBuffer(0));
-    query.sendQuery(question, buffer, &server);
+    MessagePtr answer(new Message(Message::RENDER));
+    query.resolve(question, answer, buffer, &server);
 
     // Run the test
     io_service_->run();
 
     // Read up to 3 packets.  Use some ad hoc timeout to prevent an infinite
     // block (see also recvUDP()).
-    const struct timeval timeo = { 10, 0 };
-    int recv_options = 0;
-    if (setsockopt(sock_, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo))) {
-        if (errno == ENOPROTOOPT) { // see ASIOLinkTest::recvUDP()
-            recv_options = MSG_DONTWAIT;
-        } else {
-            isc_throw(IOError, "set RCVTIMEO failed: " << strerror(errno));
-        }
-    }
+    int recv_options = setSocketTimeout(sock_, 10, 0);
     int num = 0;
-    do {
-        char inbuff[512];
-        if (recv(sock_, inbuff, sizeof(inbuff), recv_options) < 0) {
-            num = -1;
-            break;
-        }
-    } while (++num < 3);
+    bool read_success = tryRead(sock_, recv_options, 3, &num);
 
     // The query should fail
     EXPECT_FALSE(done);
     EXPECT_EQ(3, num);
+    EXPECT_TRUE(read_success);
 }
 
+// If we set client timeout to lower than querytimeout, we should
+// get a failure answer, but still see retries
+// (no actual answer is given here yet)
+TEST_F(ASIOLinkTest, forwardClientTimeout) {
+    // Prepare the service (we do not use the common setup, we do not answer
+    setDNSService();
+
+    sock_ = createTestSocket();
+
+    // Prepare the server
+    bool done(true);
+    MockServerStop server(*io_service_, &done);
+
+    MessagePtr answer(new Message(Message::RENDER));
+
+    // Do the answer
+    const uint16_t port = boost::lexical_cast<uint16_t>(TEST_CLIENT_PORT);
+    // Set it up to retry twice before client timeout fires
+    // Since the lookup timer has not fired, it should retry
+    // a third time
+    RecursiveQuery query(*dns_service_,
+                         singleAddress(TEST_IPV4_ADDR, port),
+                         singleAddress(TEST_IPV4_ADDR, port),
+                         50, 120, 1000, 3);
+    Question question(Name("example.net"), RRClass::IN(), RRType::A());
+    OutputBufferPtr buffer(new OutputBuffer(0));
+    query.resolve(question, answer, buffer, &server);
+
+    // Run the test
+    io_service_->run();
+
+    // we know it'll fail, so make it a shorter timeout
+    int recv_options = setSocketTimeout(sock_, 1, 0);
+
+    // Try to read 5 times, should stop after 3 reads
+    int num = 0;
+    bool read_success = tryRead(sock_, recv_options, 5, &num);
+
+    // The query should fail (for resolver it should send back servfail,
+    // but currently, and perhaps for forwarder in general, the effect
+    // will be the same as on a lookup timeout, i.e. no answer is sent
+    // back)
+    EXPECT_FALSE(done);
+    EXPECT_EQ(3, num);
+    EXPECT_FALSE(read_success);
+}
+
+// If we set lookup timeout to lower than querytimeout*retries, we should
+// fail before the full amount of retries
+TEST_F(ASIOLinkTest, forwardLookupTimeout) {
+    // Prepare the service (we do not use the common setup, we do not answer
+    setDNSService();
+
+    // Prepare the socket
+    sock_ = createTestSocket();
+
+    // Prepare the server
+    bool done(true);
+    MockServerStop server(*io_service_, &done);
+
+    MessagePtr answer(new Message(Message::RENDER));
+
+    // Do the answer
+    const uint16_t port = boost::lexical_cast<uint16_t>(TEST_CLIENT_PORT);
+    // Set up the test so that it will retry 5 times, but the lookup
+    // timeout will fire after only 3 normal timeouts
+    RecursiveQuery query(*dns_service_,
+                         singleAddress(TEST_IPV4_ADDR, port),
+                         singleAddress(TEST_IPV4_ADDR, port),
+                         50, 4000, 120, 5);
+    Question question(Name("example.net"), RRClass::IN(), RRType::A());
+    OutputBufferPtr buffer(new OutputBuffer(0));
+    query.resolve(question, answer, buffer, &server);
+
+    // Run the test
+    io_service_->run();
+
+    int recv_options = setSocketTimeout(sock_, 1, 0);
+
+    // Try to read 5 times, should stop after 3 reads
+    int num = 0;
+    bool read_success = tryRead(sock_, recv_options, 5, &num);
+
+    // The query should fail
+    EXPECT_FALSE(done);
+    EXPECT_EQ(3, num);
+    EXPECT_FALSE(read_success);
+}
+
+// as mentioned above, we need a more better framework for this,
+// in addition to that, this sends out queries into the world
+// (which we should catch somehow and fake replies for)
+// for the skeleton code, it shouldn't be too much of a problem
+// Ok so even we don't all have access to the DNS world right now,
+// so disabling these tests too.
+TEST_F(ASIOLinkTest, DISABLED_recursiveSendOk) {
+    setDNSService(true, false);
+    bool done;
+    
+    MockServerStop server(*io_service_, &done);
+    vector<pair<string, uint16_t> > empty_vector;
+    RecursiveQuery rq(*dns_service_, empty_vector, empty_vector, 10000, 0);
+
+    Question q(Name("www.isc.org"), RRClass::IN(), RRType::A());
+    OutputBufferPtr buffer(new OutputBuffer(0));
+    MessagePtr answer(new Message(Message::RENDER));
+    rq.resolve(q, answer, buffer, &server);
+    io_service_->run();
+
+    // Check that the answer we got matches the one we wanted
+    EXPECT_EQ(Rcode::NOERROR(), answer->getRcode());
+    ASSERT_EQ(1, answer->getRRCount(Message::SECTION_ANSWER));
+    RRsetPtr a = *answer->beginSection(Message::SECTION_ANSWER);
+    EXPECT_EQ(q.getName(), a->getName());
+    EXPECT_EQ(q.getType(), a->getType());
+    EXPECT_EQ(q.getClass(), a->getClass());
+    EXPECT_EQ(1, a->getRdataCount());
+}
+
+// see comments at previous test
+TEST_F(ASIOLinkTest, DISABLED_recursiveSendNXDOMAIN) {
+    setDNSService(true, false);
+    bool done;
+    
+    MockServerStop server(*io_service_, &done);
+    vector<pair<string, uint16_t> > empty_vector;
+    RecursiveQuery rq(*dns_service_, empty_vector, empty_vector, 10000, 0);
+
+    Question q(Name("wwwdoesnotexist.isc.org"), RRClass::IN(), RRType::A());
+    OutputBufferPtr buffer(new OutputBuffer(0));
+    MessagePtr answer(new Message(Message::RENDER));
+    rq.resolve(q, answer, buffer, &server);
+    io_service_->run();
+
+    // Check that the answer we got matches the one we wanted
+    EXPECT_EQ(Rcode::NXDOMAIN(), answer->getRcode());
+    EXPECT_EQ(0, answer->getRRCount(Message::SECTION_ANSWER));
+}
+
+
+
 // This fixture is for testing IntervalTimer. Some callback functors are 
 // registered as callback function of the timer to test if they are called
 // or not.
diff --git a/src/lib/asiolink/tests/run_unittests.cc b/src/lib/asiolink/tests/run_unittests.cc
index 68f55f6..b481784 100644
--- a/src/lib/asiolink/tests/run_unittests.cc
+++ b/src/lib/asiolink/tests/run_unittests.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <gtest/gtest.h>
 
 #include <dns/tests/unittest_util.h>
diff --git a/src/lib/asiolink/udpdns.cc b/src/lib/asiolink/udpdns.cc
index 7d9fd99..83f7b3c 100644
--- a/src/lib/asiolink/udpdns.cc
+++ b/src/lib/asiolink/udpdns.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <config.h>
 
 #include <unistd.h>             // for some IPC/network system calls
@@ -25,6 +23,7 @@
 #include <asio.hpp>
 #include <asio/deadline_timer.hpp>
 
+#include <memory>
 #include <boost/shared_ptr.hpp>
 #include <boost/date_time/posix_time/posix_time_types.hpp>
 
@@ -48,29 +47,127 @@ using namespace std;
 using namespace isc::dns;
 
 namespace asiolink {
+
+/*
+ * Some of the member variables here are shared_ptrs and some are
+ * auto_ptrs. There will be one instance of Data for the lifetime
+ * of packet. The variables that are state only for a single packet
+ * use auto_ptr, as it is more lightweight. In the case of shared
+ * configuration (eg. the callbacks, socket), we use shared_ptrs.
+ */
+struct UDPServer::Data {
+    /*
+     * Constructor from parameters passed to UDPServer constructor.
+     * This instance will not be used to retrieve and answer the actual
+     * query, it will only hold parameters until we wait for the
+     * first packet. But we do initialize the socket in here.
+     */
+    Data(io_service& io_service, const ip::address& addr, const uint16_t port,
+        SimpleCallback* checkin, DNSLookup* lookup, DNSAnswer* answer) :
+        io_(io_service), done_(false), checkin_callback_(checkin),
+        lookup_callback_(lookup), answer_callback_(answer)
+    {
+        // We must use different instantiations for v4 and v6;
+        // otherwise ASIO will bind to both
+        udp proto = addr.is_v4() ? udp::v4() : udp::v6();
+        socket_.reset(new udp::socket(io_service, proto));
+        socket_->set_option(socket_base::reuse_address(true));
+        if (addr.is_v6()) {
+            socket_->set_option(asio::ip::v6_only(true));
+        }
+        socket_->bind(udp::endpoint(addr, port));
+    }
+
+    /*
+     * Copy constructor. Default one would probably do, but it is unnecessary
+     * to copy many of the member variables every time we fork to handle
+     * another packet.
+     *
+     * We also allocate data for receiving the packet here.
+     */
+    Data(const Data& other) :
+        io_(other.io_), socket_(other.socket_), done_(false),
+        checkin_callback_(other.checkin_callback_),
+        lookup_callback_(other.lookup_callback_),
+        answer_callback_(other.answer_callback_)
+    {
+        // Instantiate the data buffer and endpoint that will
+        // be used by the asynchronous receive call.
+        data_.reset(new char[MAX_LENGTH]);
+        sender_.reset(new udp::endpoint());
+    }
+
+    // The ASIO service object
+    asio::io_service& io_;
+
+    // Class member variables which are dynamic, and changes to which
+    // need to accessible from both sides of a coroutine fork or from
+    // outside of the coroutine (i.e., from an asynchronous I/O call),
+    // should be declared here as pointers and allocated in the
+    // constructor or in the coroutine.  This allows state information
+    // to persist when an individual copy of the coroutine falls out
+    // scope while waiting for an event, *so long as* there is another
+    // object that is referencing the same data.  As a side-benefit, using
+    // pointers also reduces copy overhead for coroutine objects.
+    //
+    // Note: Currently these objects are allocated by "new" in the
+    // constructor, or in the function operator while processing a query.
+    // Repeated allocations from the heap for every incoming query is
+    // clearly a performance issue; this must be optimized in the future.
+    // The plan is to have a structure pre-allocate several "Data"
+    // objects which can be pulled off a free list and placed on an in-use
+    // list whenever a query comes in.  This will serve the dual purpose
+    // of improving performance and guaranteeing that state information
+    // will *not* be destroyed when any one instance of the coroutine
+    // falls out of scope while waiting for an event.
+    //
+    // Socket used to for listen for queries.  Created in the
+    // constructor and stored in a shared_ptr because socket objects
+    // are not copyable.
+    boost::shared_ptr<asio::ip::udp::socket> socket_;
+
+    // The ASIO-internal endpoint object representing the client
+    std::auto_ptr<asio::ip::udp::endpoint> sender_;
+
+    // \c IOMessage and \c Message objects to be passed to the
+    // DNS lookup and answer providers
+    std::auto_ptr<asiolink::IOMessage> io_message_;
+
+    // The original query as sent by the client
+    isc::dns::MessagePtr query_message_;
+
+    // The response message we are building
+    isc::dns::MessagePtr answer_message_;
+
+    // The buffer into which the response is written
+    isc::dns::OutputBufferPtr respbuf_;
+
+    // The buffer into which the query packet is written
+    boost::shared_array<char> data_;
+
+    // State information that is entirely internal to a given instance
+    // of the coroutine can be declared here.
+    size_t bytes_;
+    bool done_;
+
+    // Callback functions provided by the caller
+    const SimpleCallback* checkin_callback_;
+    const DNSLookup* lookup_callback_;
+    const DNSAnswer* answer_callback_;
+
+    std::auto_ptr<IOEndpoint> peer_;
+    std::auto_ptr<IOSocket> iosock_;
+};
+
 /// The following functions implement the \c UDPServer class.
 ///
-/// The constructor
-UDPServer::UDPServer(io_service& io_service,
-                     const ip::address& addr, const uint16_t port,
-                     SimpleCallback* checkin,
-                     DNSLookup* lookup,
-                     DNSAnswer* answer) :
-    io_(io_service), done_(false),
-    checkin_callback_(checkin),
-    lookup_callback_(lookup),
-    answer_callback_(answer)
-{
-    // We must use different instantiations for v4 and v6;
-    // otherwise ASIO will bind to both
-    udp proto = addr.is_v4() ? udp::v4() : udp::v6();
-    socket_.reset(new udp::socket(io_service, proto));
-    socket_->set_option(socket_base::reuse_address(true));
-    if (addr.is_v6()) {
-        socket_->set_option(asio::ip::v6_only(true));
-    }
-    socket_->bind(udp::endpoint(addr, port));
-}
+/// The constructor. It just creates new internal state object
+/// and lets it handle the initialization.
+UDPServer::UDPServer(io_service& io_service, const ip::address& addr,
+    const uint16_t port, SimpleCallback* checkin, DNSLookup* lookup,
+    DNSAnswer* answer) :
+    data_(new Data(io_service, addr, port, checkin, lookup, answer))
+{ }
 
 /// The function operator is implemented with the "stackless coroutine"
 /// pattern; see internal/coroutine.h for details.
@@ -82,27 +179,35 @@ UDPServer::operator()(error_code ec, size_t length) {
 
     CORO_REENTER (this) {
         do {
-            // Instantiate the data buffer and endpoint that will
-            // be used by the asynchronous receive call.
-            data_.reset(new char[MAX_LENGTH]);
-            sender_.reset(new udp::endpoint());
+            /*
+             * This is preparation for receiving a packet. We get a new
+             * state object for the lifetime of the next packet to come.
+             * It allocates the buffers to receive data into.
+             */
+            data_.reset(new Data(*data_));
 
             do {
                 // Begin an asynchronous receive, then yield.
                 // When the receive event is posted, the coroutine
                 // will resume immediately after this point.
-                CORO_YIELD socket_->async_receive_from(buffer(data_.get(),
-                                                              MAX_LENGTH),
-                                                  *sender_, *this);
+                CORO_YIELD data_->socket_->async_receive_from(
+                    buffer(data_->data_.get(), MAX_LENGTH), *data_->sender_,
+                    *this);
             } while (ec || length == 0);
 
-            bytes_ = length;
-
-            /// Fork the coroutine by creating a copy of this one and
-            /// scheduling it on the ASIO service queue.  The parent
-            /// will continue listening for DNS packets while the child
-            /// processes the one that has just arrived.
-            CORO_FORK io_.post(UDPServer(*this));
+            data_->bytes_ = length;
+
+            /*
+             * We fork the coroutine now. One (the child) will keep
+             * the current state and handle the packet, then die and
+             * drop ownership of the state. The other (parent) will just
+             * go into the loop again and replace the current state with
+             * a new one for a new object.
+             *
+             * Actually, both of the coroutines will be a copy of this
+             * one, but that's just internal implementation detail.
+             */
+            CORO_FORK data_->io_.post(UDPServer(*this));
         } while (is_parent());
 
         // Create an \c IOMessage object to store the query.
@@ -110,52 +215,57 @@ UDPServer::operator()(error_code ec, size_t length) {
         // (XXX: It would be good to write a factory function
         // that would quickly generate an IOMessage object without
         // all these calls to "new".)
-        peer_.reset(new UDPEndpoint(*sender_));
-        iosock_.reset(new UDPSocket(*socket_));
-        io_message_.reset(new IOMessage(data_.get(), bytes_, *iosock_, *peer_));
+        data_->peer_.reset(new UDPEndpoint(*data_->sender_));
+        data_->iosock_.reset(new UDPSocket(*data_->socket_));
+        data_->io_message_.reset(new IOMessage(data_->data_.get(),
+            data_->bytes_, *data_->iosock_, *data_->peer_));
 
         // Perform any necessary operations prior to processing an incoming
         // query (e.g., checking for queued configuration messages).
         //
         // (XXX: it may be a performance issue to check in for every single
         // incoming query; we may wish to throttle this in the future.)
-        if (checkin_callback_ != NULL) {
-            (*checkin_callback_)(*io_message_);
+        if (data_->checkin_callback_ != NULL) {
+            (*data_->checkin_callback_)(*data_->io_message_);
         }
 
         // If we don't have a DNS Lookup provider, there's no point in
         // continuing; we exit the coroutine permanently.
-        if (lookup_callback_ == NULL) {
+        if (data_->lookup_callback_ == NULL) {
             CORO_YIELD return;
         }
 
         // Instantiate objects that will be needed by the
         // asynchronous DNS lookup and/or by the send call.
-        respbuf_.reset(new OutputBuffer(0));
-        message_.reset(new Message(Message::PARSE));
+        data_->respbuf_.reset(new OutputBuffer(0));
+        data_->query_message_.reset(new Message(Message::PARSE));
+        data_->answer_message_.reset(new Message(Message::RENDER));
 
         // Schedule a DNS lookup, and yield.  When the lookup is
         // finished, the coroutine will resume immediately after
         // this point.
-        CORO_YIELD io_.post(AsyncLookup<UDPServer>(*this));
+        CORO_YIELD data_->io_.post(AsyncLookup<UDPServer>(*this));
+
+        dlog("[XX] got an answer");
 
         // The 'done_' flag indicates whether we have an answer
         // to send back.  If not, exit the coroutine permanently.
-        if (!done_) {
+        if (!data_->done_) {
             CORO_YIELD return;
         }
 
         // Call the DNS answer provider to render the answer into
         // wire format
-        (*answer_callback_)(*io_message_, message_, respbuf_);
+        (*data_->answer_callback_)(*data_->io_message_, data_->query_message_,
+            data_->answer_message_, data_->respbuf_);
 
         // Begin an asynchronous send, and then yield.  When the
         // send completes, we will resume immediately after this point
         // (though we have nothing further to do, so the coroutine
         // will simply exit at that time).
-        CORO_YIELD socket_->async_send_to(buffer(respbuf_->getData(),
-                                                 respbuf_->getLength()),
-                                     *sender_, *this);
+        CORO_YIELD data_->socket_->async_send_to(
+            buffer(data_->respbuf_->getData(), data_->respbuf_->getLength()),
+            *data_->sender_, *this);
     }
 }
 
@@ -163,7 +273,8 @@ UDPServer::operator()(error_code ec, size_t length) {
 /// AsyncLookup<UDPServer> handler.)
 void
 UDPServer::asyncLookup() {
-    (*lookup_callback_)(*io_message_, message_, respbuf_, this);
+    (*data_->lookup_callback_)(*data_->io_message_,
+        data_->query_message_, data_->answer_message_, data_->respbuf_, this);
 }
 
 /// Post this coroutine on the ASIO service queue so that it will
@@ -171,8 +282,13 @@ UDPServer::asyncLookup() {
 /// whether there is an answer to return to the client.
 void
 UDPServer::resume(const bool done) {
-    done_ = done;
-    io_.post(*this);
+    data_->done_ = done;
+    data_->io_.post(*this);
+}
+
+bool
+UDPServer::hasAnswer() {
+    return (data_->done_);
 }
 
 // Private UDPQuery data (see internal/udpdns.h for reasons)
@@ -253,6 +369,7 @@ UDPQuery::operator()(error_code ec, size_t length) {
                 data_->remote.address().to_string());
         }
 
+
         // If we timeout, we stop, which will shutdown everything and
         // cancel all other attempts to run inside the coroutine
         if (data_->timeout != -1) {
diff --git a/src/lib/bench/benchmark.h b/src/lib/bench/benchmark.h
index 03d126d..5eab1b7 100644
--- a/src/lib/bench/benchmark.h
+++ b/src/lib/bench/benchmark.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __BENCHMARK_H
 #define __BENCHMARK_H 1
 
diff --git a/src/lib/bench/benchmark_util.cc b/src/lib/bench/benchmark_util.cc
index cdae6fe..ca2ca1b 100644
--- a/src/lib/bench/benchmark_util.cc
+++ b/src/lib/bench/benchmark_util.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <fstream>
 #include <iostream>
 #include <string>
diff --git a/src/lib/bench/benchmark_util.h b/src/lib/bench/benchmark_util.h
index 30d3a8f..3a373f2 100644
--- a/src/lib/bench/benchmark_util.h
+++ b/src/lib/bench/benchmark_util.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __BENCHMARK_UTIL_H
 #define __BENCHMARK_UTIL_H 1
 
diff --git a/src/lib/bench/example/search_bench.cc b/src/lib/bench/example/search_bench.cc
index d830d2c..851d815 100644
--- a/src/lib/bench/example/search_bench.cc
+++ b/src/lib/bench/example/search_bench.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <unistd.h>             // for getpid
 
 #include <cassert>
diff --git a/src/lib/bench/tests/benchmark_unittest.cc b/src/lib/bench/tests/benchmark_unittest.cc
index ffd39b8..f16b47d 100644
--- a/src/lib/bench/tests/benchmark_unittest.cc
+++ b/src/lib/bench/tests/benchmark_unittest.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <unistd.h>             // for usleep
 
 #include <bench/benchmark.h>
diff --git a/src/lib/bench/tests/loadquery_unittest.cc b/src/lib/bench/tests/loadquery_unittest.cc
index e7d6cf3..3ac352a 100644
--- a/src/lib/bench/tests/loadquery_unittest.cc
+++ b/src/lib/bench/tests/loadquery_unittest.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <algorithm>
 #include <utility>
 #include <vector>
diff --git a/src/lib/bench/tests/run_unittests.cc b/src/lib/bench/tests/run_unittests.cc
index ecc077f..85d4548 100644
--- a/src/lib/bench/tests/run_unittests.cc
+++ b/src/lib/bench/tests/run_unittests.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <gtest/gtest.h>
 
 int
diff --git a/src/lib/cc/data.cc b/src/lib/cc/data.cc
index 39cdfc0..6f7d4a2 100644
--- a/src/lib/cc/data.cc
+++ b/src/lib/cc/data.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <config.h>
 
 #include <cc/data.h>
diff --git a/src/lib/cc/data.h b/src/lib/cc/data.h
index 1acfcad..4f121d5 100644
--- a/src/lib/cc/data.h
+++ b/src/lib/cc/data.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef _ISC_DATA_H
 #define _ISC_DATA_H 1
 
diff --git a/src/lib/cc/session.cc b/src/lib/cc/session.cc
index 7ee90a2..e911a86 100644
--- a/src/lib/cc/session.cc
+++ b/src/lib/cc/session.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <config.h>
 #include <cc/session_config.h>
 
diff --git a/src/lib/cc/session.h b/src/lib/cc/session.h
index 37080cf..6d8696d 100644
--- a/src/lib/cc/session.h
+++ b/src/lib/cc/session.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef _ISC_SESSION_H
 #define _ISC_SESSION_H 1
 
diff --git a/src/lib/cc/tests/data_unittests.cc b/src/lib/cc/tests/data_unittests.cc
index cc3e121..2536682 100644
--- a/src/lib/cc/tests/data_unittests.cc
+++ b/src/lib/cc/tests/data_unittests.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <gtest/gtest.h>
 #include <boost/foreach.hpp>
 #include <boost/assign/std/vector.hpp>
diff --git a/src/lib/cc/tests/run_unittests.cc b/src/lib/cc/tests/run_unittests.cc
index 9aee35f..0908071 100644
--- a/src/lib/cc/tests/run_unittests.cc
+++ b/src/lib/cc/tests/run_unittests.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <gtest/gtest.h>
 
 int
diff --git a/src/lib/cc/tests/session_unittests.cc b/src/lib/cc/tests/session_unittests.cc
index fbb18e0..d61cbd3 100644
--- a/src/lib/cc/tests/session_unittests.cc
+++ b/src/lib/cc/tests/session_unittests.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id: data_unittests.cc 1899 2010-05-21 12:03:59Z jelte $
-
 #include <config.h>
 
 // for some IPC/network system calls in asio/detail/pipe_select_interrupter.hpp 
diff --git a/src/lib/config/ccsession.cc b/src/lib/config/ccsession.cc
index 09a1c9c..7049565 100644
--- a/src/lib/config/ccsession.cc
+++ b/src/lib/config/ccsession.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 // 
 // todo: generalize this and make it into a specific API for all modules
 //       to use (i.e. connect to cc, send config and commands, get config,
diff --git a/src/lib/config/ccsession.h b/src/lib/config/ccsession.h
index b0477a3..d8d1eeb 100644
--- a/src/lib/config/ccsession.h
+++ b/src/lib/config/ccsession.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __CCSESSION_H
 #define __CCSESSION_H 1
 
diff --git a/src/lib/config/config_data.cc b/src/lib/config/config_data.cc
index 01acd99..1fa37c1 100644
--- a/src/lib/config/config_data.cc
+++ b/src/lib/config/config_data.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <config/config_data.h>
 
 #include <boost/foreach.hpp>
diff --git a/src/lib/config/config_data.h b/src/lib/config/config_data.h
index 423f0dd..29a5b5f 100644
--- a/src/lib/config/config_data.h
+++ b/src/lib/config/config_data.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __CONFIG_DATA_H
 #define __CONFIG_DATA_H 1
 
diff --git a/src/lib/config/tests/ccsession_unittests.cc b/src/lib/config/tests/ccsession_unittests.cc
index 00d09d6..4ed3ce2 100644
--- a/src/lib/config/tests/ccsession_unittests.cc
+++ b/src/lib/config/tests/ccsession_unittests.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id: module_spec_unittests.cc 1321 2010-03-11 10:17:03Z jelte $
-
 #include <config.h>
 
 #include <gtest/gtest.h>
diff --git a/src/lib/config/tests/config_data_unittests.cc b/src/lib/config/tests/config_data_unittests.cc
index d5d4d53..974812d 100644
--- a/src/lib/config/tests/config_data_unittests.cc
+++ b/src/lib/config/tests/config_data_unittests.cc
@@ -13,8 +13,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <gtest/gtest.h>
 
 #include <config/tests/data_def_unittests_config.h>
diff --git a/src/lib/config/tests/fake_session.cc b/src/lib/config/tests/fake_session.cc
index ad04390..29744c7 100644
--- a/src/lib/config/tests/fake_session.cc
+++ b/src/lib/config/tests/fake_session.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id: session.cc 1250 2010-03-09 22:52:15Z jinmei $
-
 #include <config.h>
 
 #include <stdint.h>
diff --git a/src/lib/config/tests/fake_session.h b/src/lib/config/tests/fake_session.h
index 2150ef5..ac8e291 100644
--- a/src/lib/config/tests/fake_session.h
+++ b/src/lib/config/tests/fake_session.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id: session.h 1250 2010-03-09 22:52:15Z jinmei $
-
 #ifndef _ISC_FAKESESSION_H
 #define _ISC_FAKESESSION_H 1
 
diff --git a/src/lib/config/tests/module_spec_unittests.cc b/src/lib/config/tests/module_spec_unittests.cc
index 7a55fbc..8490aa0 100644
--- a/src/lib/config/tests/module_spec_unittests.cc
+++ b/src/lib/config/tests/module_spec_unittests.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <gtest/gtest.h>
 
 #include <config/module_spec.h>
diff --git a/src/lib/config/tests/run_unittests.cc b/src/lib/config/tests/run_unittests.cc
index 9aee35f..0908071 100644
--- a/src/lib/config/tests/run_unittests.cc
+++ b/src/lib/config/tests/run_unittests.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <gtest/gtest.h>
 
 int
diff --git a/src/lib/datasrc/cache.cc b/src/lib/datasrc/cache.cc
index 64bffb3..6fff754 100644
--- a/src/lib/datasrc/cache.cc
+++ b/src/lib/datasrc/cache.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <stdint.h>
 
 #include <map>
diff --git a/src/lib/datasrc/cache.h b/src/lib/datasrc/cache.h
index 1a7d9fe..054912c 100644
--- a/src/lib/datasrc/cache.h
+++ b/src/lib/datasrc/cache.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __CACHE_H
 #define __CACHE_H
 
diff --git a/src/lib/datasrc/data_source.cc b/src/lib/datasrc/data_source.cc
index 980319f..8b2b47e 100644
--- a/src/lib/datasrc/data_source.cc
+++ b/src/lib/datasrc/data_source.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <config.h>
 
 #include <cassert>
diff --git a/src/lib/datasrc/data_source.h b/src/lib/datasrc/data_source.h
index 411ea3d..ff695da 100644
--- a/src/lib/datasrc/data_source.h
+++ b/src/lib/datasrc/data_source.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __DATA_SOURCE_H
 #define __DATA_SOURCE_H
 
diff --git a/src/lib/datasrc/memory_datasrc.cc b/src/lib/datasrc/memory_datasrc.cc
index 6fee000..31c5a95 100644
--- a/src/lib/datasrc/memory_datasrc.cc
+++ b/src/lib/datasrc/memory_datasrc.cc
@@ -19,6 +19,7 @@
 
 #include <dns/name.h>
 #include <dns/rrclass.h>
+#include <dns/rrsetlist.h>
 #include <dns/masterload.h>
 
 #include <datasrc/memory_datasrc.h>
@@ -34,13 +35,13 @@ namespace datasrc {
 struct MemoryZone::MemoryZoneImpl {
     // Constructor
     MemoryZoneImpl(const RRClass& zone_class, const Name& origin) :
-        zone_class_(zone_class), origin_(origin)
-    {}
-
-    // Information about the zone
-    RRClass zone_class_;
-    Name origin_;
-    string file_name_;
+        zone_class_(zone_class), origin_(origin), origin_data_(NULL)
+    {
+        // We create the node for origin (it needs to exist anyway in future)
+        domains_.insert(origin, &origin_data_);
+        DomainPtr origin_domain(new Domain);
+        origin_data_->setData(origin_domain);
+    }
 
     // Some type aliases
     /*
@@ -60,10 +61,59 @@ struct MemoryZone::MemoryZoneImpl {
     // The tree stores domains
     typedef RBTree<Domain> DomainTree;
     typedef RBNode<Domain> DomainNode;
+
+    // Information about the zone
+    RRClass zone_class_;
+    Name origin_;
+    DomainNode* origin_data_;
+    string file_name_;
+
     // The actual zone data
     DomainTree domains_;
 
     /*
+     * Does some checks in context of the data that are already in the zone.
+     * Currently checks for forbidden combinations of RRsets in the same
+     * domain (CNAME+anything, DNAME+NS).
+     *
+     * If such condition is found, it throws AddError.
+     */
+    void contextCheck(const ConstRRsetPtr& rrset,
+                      const DomainPtr& domain) const {
+        // Ensure CNAME and other type of RR don't coexist for the same
+        // owner name.
+        if (rrset->getType() == RRType::CNAME()) {
+            // XXX: this check will become incorrect when we support DNSSEC
+            // (depending on how we support DNSSEC).  We should revisit it
+            // at that point.
+            if (!domain->empty()) {
+                isc_throw(AddError, "CNAME can't be added with other data for "
+                          << rrset->getName());
+            }
+        } else if (domain->find(RRType::CNAME()) != domain->end()) {
+            isc_throw(AddError, "CNAME and " << rrset->getType() <<
+                      " can't coexist for " << rrset->getName());
+        }
+
+        /*
+         * Similar with DNAME, but it must not coexist only with NS and only in
+         * non-apex domains.
+         * RFC 2672 section 3 mentions that it is implied from it and RFC 2181
+         */
+        if (rrset->getName() != origin_ &&
+            // Adding DNAME, NS already there
+            ((rrset->getType() == RRType::DNAME() &&
+            domain->find(RRType::NS()) != domain->end()) ||
+            // Adding NS, DNAME already there
+            (rrset->getType() == RRType::NS() &&
+            domain->find(RRType::DNAME()) != domain->end())))
+        {
+            isc_throw(AddError, "DNAME can't coexist with NS in non-apex "
+                "domain " << rrset->getName());
+        }
+    }
+
+    /*
      * Implementation of longer methods. We put them here, because the
      * access is without the impl_-> and it will get inlined anyway.
      */
@@ -73,6 +123,19 @@ struct MemoryZone::MemoryZoneImpl {
         if (!rrset) {
             isc_throw(NullRRset, "The rrset provided is NULL");
         }
+        // Check for singleton RRs. It should probably handled at a different
+        // in future.
+        if ((rrset->getType() == RRType::CNAME() ||
+            rrset->getType() == RRType::DNAME()) &&
+            rrset->getRdataCount() > 1)
+        {
+            // XXX: this is not only for CNAME or DNAME. We should generalize
+            // this code for all other "singleton RR types" (such as SOA) in a
+            // separate task.
+            isc_throw(AddError, "multiple RRs of singleton type for "
+                      << rrset->getName());
+        }
+
         Name name(rrset->getName());
         NameComparisonResult compare(origin_.compare(name));
         if (compare.getRelation() != NameComparisonResult::SUPERDOMAIN &&
@@ -92,7 +155,7 @@ struct MemoryZone::MemoryZoneImpl {
             default:
                 assert(0);
         }
-        assert(node);
+        assert(node != NULL);
 
         // Now get the domain
         DomainPtr domain;
@@ -104,16 +167,25 @@ struct MemoryZone::MemoryZoneImpl {
             domain = node->getData();
         }
 
+        // Checks related to the surrounding data.
+        // Note: when the check fails and the exception is thrown, it may
+        // break strong exception guarantee.  At the moment we prefer
+        // code simplicity and don't bother to introduce complicated
+        // recovery code.
+        contextCheck(rrset, domain);
+
         // Try inserting the rrset there
         if (domain->insert(DomainPair(rrset->getType(), rrset)).second) {
             // Ok, we just put it in
 
             // If this RRset creates a zone cut at this node, mark the node
             // indicating the need for callback in find().
-            // TBD: handle DNAME, too
             if (rrset->getType() == RRType::NS() &&
                 rrset->getName() != origin_) {
                 node->enableCallback();
+            // If it is DNAME, we have a callback as well here
+            } else if (rrset->getType() == RRType::DNAME()) {
+                node->enableCallback();
             }
 
             return (result::SUCCESS);
@@ -145,31 +217,56 @@ struct MemoryZone::MemoryZoneImpl {
     /// It will be passed to \c zonecutCallback() and record a possible
     /// zone cut node and related RRset (normally NS or DNAME).
     struct FindState {
-        FindState(FindOptions options) : zonecut_node_(NULL),
-                                         options_(options)
+        FindState(FindOptions options) :
+            zonecut_node_(NULL),
+            dname_node_(NULL),
+            options_(options)
         {}
         const DomainNode* zonecut_node_;
+        const DomainNode* dname_node_;
         ConstRRsetPtr rrset_;
         const FindOptions options_;
     };
 
-    // A callback called from possible zone cut nodes.  This will be passed
-    // from the \c find() method to \c RBTree::find().
-    static bool zonecutCallback(const DomainNode& node, FindState* state) {
-        // We perform callback check only for the highest zone cut in the
-        // rare case of nested zone cuts.
-        if (state->zonecut_node_ != NULL) {
-            return (false);
+    // A callback called from possible zone cut nodes and nodes with DNAME.
+    // This will be passed from the \c find() method to \c RBTree::find().
+    static bool cutCallback(const DomainNode& node, FindState* state) {
+        // We need to look for DNAME first, there's allowed case where
+        // DNAME and NS coexist in the apex. DNAME is the one to notice,
+        // the NS is authoritative, not delegation (corner case explicitly
+        // allowed by section 3 of 2672)
+        const Domain::const_iterator foundDNAME(node.getData()->find(
+            RRType::DNAME()));
+        if (foundDNAME != node.getData()->end()) {
+            state->dname_node_ = &node;
+            state->rrset_ = foundDNAME->second;
+            // No more processing below the DNAME (RFC 2672, section 3
+            // forbids anything to exist below it, so there's no need
+            // to actually search for it). This is strictly speaking
+            // a different way than described in 4.1 of that RFC,
+            // but because of the assumption in section 3, it has the
+            // same behaviour.
+            return (true);
         }
 
-        const Domain::const_iterator found(node.getData()->find(RRType::NS()));
-        if (found != node.getData()->end()) {
-            // BIND 9 checks if this node is not the origin.  But it cannot
-            // be the origin because we don't enable the callback at the
-            // origin node (see MemoryZoneImpl::add()).  Or should we do a
-            // double check for it?
+        // Look for NS
+        const Domain::const_iterator foundNS(node.getData()->find(
+            RRType::NS()));
+        if (foundNS != node.getData()->end()) {
+            // We perform callback check only for the highest zone cut in the
+            // rare case of nested zone cuts.
+            if (state->zonecut_node_ != NULL) {
+                return (false);
+            }
+
+            // BIND 9 checks if this node is not the origin.  That's probably
+            // because it can support multiple versions for dynamic updates
+            // and IXFR, and it's possible that the callback is called at
+            // the apex and the DNAME doesn't exist for a particular version.
+            // It cannot happen for us (at least for now), so we don't do
+            // that check.
             state->zonecut_node_ = &node;
-            state->rrset_ = found->second;
+            state->rrset_ = foundNS->second;
 
             // Unless glue is allowed the search stops here, so we return
             // false; otherwise return true to continue the search.
@@ -185,15 +282,40 @@ struct MemoryZone::MemoryZoneImpl {
         return (false);
     }
 
+
     // Implementation of MemoryZone::find
     FindResult find(const Name& name, RRType type,
-                    const FindOptions options) const
+                    RRsetList* target, const FindOptions options) const
     {
         // Get the node
         DomainNode* node(NULL);
         FindState state(options);
-        switch (domains_.find(name, &node, zonecutCallback, &state)) {
+        RBTreeNodeChain<Domain> node_path;
+        switch (domains_.find(name, &node, node_path, cutCallback, &state)) {
             case DomainTree::PARTIALMATCH:
+                /*
+                 * In fact, we could use a single variable instead of
+                 * dname_node_ and zonecut_node_. But then we would need
+                 * to distinquish these two cases by something else and
+                 * it seemed little more confusing to me when I wrote it.
+                 *
+                 * Usually at most one of them will be something else than
+                 * NULL (it might happen both are NULL, in which case we
+                 * consider it NOT FOUND). There's one corner case when
+                 * both might be something else than NULL and it is in case
+                 * there's a DNAME under a zone cut and we search in
+                 * glue OK mode ‒ in that case we don't stop on the domain
+                 * with NS and ignore it for the answer, but it gets set
+                 * anyway. Then we find the DNAME and we need to act by it,
+                 * therefore we first check for DNAME and then for NS. In
+                 * all other cases it doesn't matter, as at least one of them
+                 * is NULL.
+                 */
+                if (state.dname_node_ != NULL) {
+                    // We were traversing a DNAME node (and wanted to go
+                    // lower below it), so return the DNAME
+                    return (FindResult(DNAME, state.rrset_));
+                }
                 if (state.zonecut_node_ != NULL) {
                     return (FindResult(DELEGATION, state.rrset_));
                 }
@@ -214,26 +336,39 @@ struct MemoryZone::MemoryZoneImpl {
         Domain::const_iterator found;
 
         // If the node callback is enabled, this may be a zone cut.  If it
-        // has a NS RR, we should return a delegation.
-        if (node->isCallbackEnabled()) {
+        // has a NS RR, we should return a delegation, but not in the apex.
+        if (node->isCallbackEnabled() && node != origin_data_) {
             found = node->getData()->find(RRType::NS());
             if (found != node->getData()->end()) {
                 return (FindResult(DELEGATION, found->second));
             }
         }
 
+        // handle type any query
+        if (target != NULL && !node->getData()->empty()) {
+            // Empty domain will be handled as NXRRSET by normal processing
+            for (found = node->getData()->begin();
+                 found != node->getData()->end(); found++)
+            {
+                target->addRRset(
+                    boost::const_pointer_cast<RRset>(found->second));
+            }
+            return (FindResult(SUCCESS, ConstRRsetPtr()));
+        }
+
         found = node->getData()->find(type);
         if (found != node->getData()->end()) {
             // Good, it is here
             return (FindResult(SUCCESS, found->second));
         } else {
-            /*
-             * TODO Look for CNAME and DNAME (it should be OK to do so when
-             * the value is not found, as CNAME/DNAME domain should be
-             * empty otherwise.)
-             */
-            return (FindResult(NXRRSET, ConstRRsetPtr()));
+            // Next, try CNAME.
+            found = node->getData()->find(RRType::CNAME());
+            if (found != node->getData()->end()) {
+                return (FindResult(CNAME, found->second));
+            }
         }
+        // No exact match or CNAME.  Return NXRRSET.
+        return (FindResult(NXRRSET, ConstRRsetPtr()));
     }
 };
 
@@ -258,9 +393,9 @@ MemoryZone::getClass() const {
 
 Zone::FindResult
 MemoryZone::find(const Name& name, const RRType& type,
-                 const FindOptions options) const
+                 RRsetList* target, const FindOptions options) const
 {
-    return (impl_->find(name, type, options));
+    return (impl_->find(name, type, target, options));
 }
 
 result::Result
diff --git a/src/lib/datasrc/memory_datasrc.h b/src/lib/datasrc/memory_datasrc.h
index 63d25e7..5a2c74e 100644
--- a/src/lib/datasrc/memory_datasrc.h
+++ b/src/lib/datasrc/memory_datasrc.h
@@ -22,6 +22,7 @@
 namespace isc {
 namespace dns {
 class Name;
+class RRsetList;
 };
 
 namespace datasrc {
@@ -55,26 +56,35 @@ public:
 
     /// \brief Returns the origin of the zone.
     virtual const isc::dns::Name& getOrigin() const;
+
     /// \brief Returns the class of the zone.
     virtual const isc::dns::RRClass& getClass() const;
+
     /// \brief Looks up an RRset in the zone.
     ///
     /// See documentation in \c Zone.
     ///
-    /// It returns NULL pointer in case of NXDOMAIN and NXRRSET
+    /// It returns NULL pointer in case of NXDOMAIN and NXRRSET,
+    /// and also SUCCESS if target is not NULL(TYPE_ANY query).
     /// (the base class documentation does not seem to require that).
     virtual FindResult find(const isc::dns::Name& name,
                             const isc::dns::RRType& type,
+                            isc::dns::RRsetList* target = NULL,
                             const FindOptions options = FIND_DEFAULT) const;
 
     /// \brief Inserts an rrset into the zone.
     ///
     /// It puts another RRset into the zone.
     ///
-    /// It throws NullRRset or OutOfZone if the provided rrset is invalid. It
-    /// might throw standard allocation exceptions, in which case this function
-    /// does not guarantee strong exception safety (it is currently not needed,
-    /// if it is needed in future, it should be implemented).
+    /// Except for NullRRset and OutOfZone, this method does not guarantee
+    /// strong exception safety (it is currently not needed, if it is needed
+    /// in future, it should be implemented).
+    ///
+    /// \throw NullRRset \c rrset is a NULL pointer.
+    /// \throw OutOfZone The owner name of \c rrset is outside of the
+    /// origin of the zone.
+    /// \throw AddError Other general errors.
+    /// \throw Others This method might throw standard allocation exceptions.
     ///
     /// \param rrset The set to add.
     /// \return SUCCESS or EXIST (if an rrset for given name and type already
@@ -100,6 +110,21 @@ public:
         { }
     };
 
+    /// \brief General failure exception for \c add().
+    ///
+    /// This is thrown against general error cases in adding an RRset
+    /// to the zone.
+    ///
+    /// Note: this exception would cover cases for \c OutOfZone or
+    /// \c NullRRset.  We'll need to clarify and unify the granularity
+    /// of exceptions eventually.  For now, exceptions are added as
+    /// developers see the need for it.
+    struct AddError : public InvalidParameter {
+        AddError(const char* file, size_t line, const char* what) :
+            InvalidParameter(file, line, what)
+        { }
+    };
+
     /// Return the master file name of the zone
     ///
     /// This method returns the name of the zone's master file to be loaded.
diff --git a/src/lib/datasrc/query.cc b/src/lib/datasrc/query.cc
index 4d8d025..d3de5c7 100644
--- a/src/lib/datasrc/query.cc
+++ b/src/lib/datasrc/query.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <dns/buffer.h>
 #include <dns/name.h>
 #include <dns/rrset.h>
diff --git a/src/lib/datasrc/query.h b/src/lib/datasrc/query.h
index 57b0bca..43b62cc 100644
--- a/src/lib/datasrc/query.h
+++ b/src/lib/datasrc/query.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __QUERY_H
 #define __QUERY_H
 
diff --git a/src/lib/datasrc/rbtree.h b/src/lib/datasrc/rbtree.h
index 59f6b37..cf413e8 100644
--- a/src/lib/datasrc/rbtree.h
+++ b/src/lib/datasrc/rbtree.h
@@ -26,9 +26,10 @@
 #include <dns/name.h>
 #include <boost/utility.hpp>
 #include <boost/shared_ptr.hpp>
-#include <exception>
+#include <exceptions/exceptions.h>
 #include <ostream>
 #include <algorithm>
+#include <cassert>
 
 namespace isc {
 namespace datasrc {
@@ -54,6 +55,8 @@ operator-(const isc::dns::Name& super_name, const isc::dns::Name& sub_name) {
 }
 }
 
+/// Forward declare RBTree class here is convinent for following friend
+/// class declare inside RBNode and RBTreeNodeChain
 template <typename T>
 class RBTree;
 
@@ -141,6 +144,7 @@ public:
     /// non-terminal domains, but it is possible (yet probably meaningless)
     /// empty nodes anywhere.
     bool isEmpty() const { return (data_.get() == NULL); }
+
     //@}
 
     /// \name Setter functions.
@@ -175,6 +179,23 @@ private:
         return (&null_node);
     }
 
+    /// \brief return the next node which is bigger than current node
+    /// in the same subtree
+    ///
+    /// The next successor for this node is the next bigger node in terms of
+    /// the DNSSEC order relation within the same single subtree.
+    /// Note that it may NOT be the next bigger node in the entire RBTree;
+    ///  RBTree is a tree in tree, and the real next node may reside in
+    /// an upper or lower subtree of the subtree where this node belongs.
+    /// For example, if this node has a sub domain, the real next node is
+    /// the smallest node in the sub domain tree.
+    ///
+    /// If this node is the biggest node within the subtree, this method
+    /// returns \c NULL_NODE().
+    ///
+    /// This method never throws an exception.
+    const RBNode<T>* successor() const;
+
     /// \name Data to maintain the rbtree structure.
     //@{
     RBNode<T>*  parent_;
@@ -238,6 +259,170 @@ template <typename T>
 RBNode<T>::~RBNode() {
 }
 
+template <typename T>
+const RBNode<T>*
+RBNode<T>::successor() const {
+    const RBNode<T>* current = this;
+    // If it has right node, the successor is the left-most node of the right
+    // subtree.
+    if (right_ != NULL_NODE()) {
+        current = right_;
+        while (current->left_ != NULL_NODE()) {
+            current = current->left_;
+        }
+        return (current);
+    }
+
+
+    // Otherwise go up until we find the first left branch on our path to
+    // root.  If found, the parent of the branch is the successor.
+    // Otherwise, we return the null node
+    const RBNode<T>* parent = current->parent_;
+    while (parent != NULL_NODE() && current == parent->right_) {
+        current = parent;
+        parent = parent->parent_;
+    }
+    return (parent);
+}
+
+
+/// \brief RBTreeNodeChain is used to keep track of the sequence of
+/// nodes to reach any given node from the root of RBTree.
+///
+/// Currently, RBNode does not have "up" pointers in them (i.e., back pointers
+/// from the root of one level of tree of trees to the node in the parent
+/// tree whose down pointer points to that root node) for memory usage
+/// reasons, so there is no other way to find the path back to the root from
+/// any given RBNode.
+///
+/// \note This design may change in future versions.  In particular, it's
+/// quite likely we want to have that pointer if we want to optimize name
+/// compression by exploiting the structure of the zone.  If and when that
+/// happens we should also revisit the need for the chaining.
+///
+/// RBTreeNodeChain is constructed and manipulated only inside the \c RBTree
+/// class.
+/// \c RBTree uses it as an inner data structure to iterate over the whole
+/// RBTree.
+/// This is the reason why manipulation methods such as \c push() and \c pop()
+/// are private (and not shown in the doxygen document).
+template <typename T>
+class RBTreeNodeChain {
+    /// RBTreeNodeChain is initialized by RBTree, only RBTree has
+    /// knowledge to manipuate it.
+    friend class RBTree<T>;
+public:
+    /// \name Constructors and Assignment Operator.
+    ///
+    /// \note The copy constructor and the assignment operator are
+    /// intentionally defined as private, making this class non copyable.
+    /// This may have to be changed in a future version with newer need.
+    /// For now we explicitly disable copy to avoid accidental copy happens
+    /// unintentionally.
+    //{@
+    /// The default constructor.
+    ///
+    /// \exception None
+    RBTreeNodeChain() : node_count_(0) {}
+
+private:
+    RBTreeNodeChain(const RBTreeNodeChain<T>&);
+    RBTreeNodeChain<T>& operator=(const RBTreeNodeChain<T>&);
+    //@}
+
+public:
+    /// \brief Return the number of levels stored in the chain.
+    ///
+    /// It's equal to the number of nodes in the chain; for an empty
+    /// chain, 0 will be returned.
+    ///
+    /// \exception None
+    unsigned int getLevelCount() const { return (node_count_); }
+
+    /// \brief return the absolute name for the node which this
+    /// \c RBTreeNodeChain currently refers to.
+    ///
+    /// The chain must not be empty.
+    ///
+    /// \exception isc::BadValue the chain is empty.
+    /// \exception std::bad_alloc memory allocation for the new name fails.
+    isc::dns::Name getAbsoluteName() const {
+        if (isEmpty()) {
+            isc_throw(isc::BadValue,
+                      "RBTreeNodeChain::getAbsoluteName is called on an empty "
+                      "chain");
+        }
+
+        const RBNode<T>* top_node = top();
+        isc::dns::Name absolute_name = top_node->getName();
+        int node_count = node_count_ - 1;
+        while (node_count > 0) {
+            top_node = nodes_[node_count - 1];
+            absolute_name = absolute_name.concatenate(top_node->getName());
+            --node_count;
+        }
+        return (absolute_name);
+    }
+
+private:
+    // the following private functions check invariants about the internal
+    // state using assert() instead of exception.  The state of a chain
+    // can only be modified operations within this file, so if any of the
+    // assumptions fails it means an internal bug.
+
+    /// \brief return whther node chain has node in it.
+    ///
+    /// \exception None
+    bool isEmpty() const { return (node_count_ == 0); }
+
+    /// \brief return the top node for the node chain
+    ///
+    /// RBTreeNodeChain store all the nodes along top node to
+    /// root node of RBTree
+    ///
+    /// \exception None
+    const RBNode<T>* top() const {
+        assert(!isEmpty());
+        return (nodes_[node_count_ - 1]);
+    }
+
+    /// \brief pop the top node from the node chain
+    ///
+    /// After pop, up/super node of original top node will be
+    /// the top node
+    ///
+    /// \exception None
+    void pop() {
+        assert(!isEmpty());
+        --node_count_;
+    }
+
+    /// \brief add the node into the node chain
+    ///
+    /// If the node chain isn't empty, the node should be
+    /// the sub domain of the original top node in node chain
+    /// otherwise the node should be the root node of RBTree.
+    ///
+    /// \exception None
+    void push(const RBNode<T>* node) {
+        assert(node_count_ < RBT_MAX_LEVEL);
+        nodes_[node_count_++] = node;
+    }
+
+private:
+    // The max label count for one domain name is Name::MAX_LABELS (128).
+    // Since each node in rbtree stores at least one label, and the root
+    // name always shares the same level with some others (which means
+    // all top level nodes except the one for the root name contain at least
+    // two labels), the possible maximum level is MAX_LABELS - 1.
+    // It's also the possible maximum nodes stored in a chain.
+    const static int RBT_MAX_LEVEL = isc::dns::Name::MAX_LABELS - 1;
+
+    const RBNode<T>* nodes_[RBT_MAX_LEVEL];
+    int node_count_;
+};
+
+
 // note: the following class description is documented using multiline comments
 // because the verbatim diagram contain a backslash, which could be interpreted
 // as escape of newline in singleline comment.
@@ -256,6 +441,17 @@ RBNode<T>::~RBNode() {
  *  - Decreases the memory footprint, as it doesn't store the suffix labels
  *      multiple times.
  *
+ * Depending on different usage, rbtree will support different search policies.
+ * Whether to return an empty node to end user is one policy among them.
+ * The default policy is to NOT return an empty node to end user;
+ * to change the behavior, specify \c true for the constructor parameter
+ * \c returnEmptyNode.
+ * \note The search policy only affects the \c find() behavior of RBTree.
+ * When inserting one name into RBTree, if the node with the name already
+ * exists in the RBTree and it's an empty node which doesn't have any data,
+ * the \c insert() method will still return \c ALREADYEXISTS regardless of
+ * the search policy.
+ *
  * \anchor diagram
  *
  * with the following names:
@@ -269,7 +465,7 @@ RBNode<T>::~RBNode() {
  *  - p.w.y.d.e.f
  *  - q.w.y.d.e.f
  *
- * the tree will looks like:
+ * the tree will look like:
  *  \verbatim
                                 b
                               /   \
@@ -285,16 +481,10 @@ RBNode<T>::~RBNode() {
                                     / \
                                    o   q
    \endverbatim
- *  \note open problems:
- *  - current \c find() function only returns non-empty nodes, so there is no
- *    difference between find a non existent name and a name corresponding to
- *    an empty non-terminal nodes, but in DNS query logic, they are different
  *  \todo
  *  - add remove interface
  *  - add iterator to iterate over the whole \c RBTree.  This may be necessary,
  *    for example, to support AXFR.
- *  - since \c RBNode only has down pointer without up pointer, the node path
- *    during finding should be recorded for later use
  */
 template <typename T>
 class RBTree : public boost::noncopyable {
@@ -316,7 +506,7 @@ public:
     /// The constructor.
     ///
     /// It never throws an exception.
-    explicit RBTree();
+    explicit RBTree(bool returnEmptyNode = false);
 
     /// \b Note: RBTree is not intended to be inherited so the destructor
     /// is not virtual
@@ -329,22 +519,25 @@ public:
     ///
     /// \anchor find
     ///
-    /// These methods search the RBTree for a node whose name is a longest
+    /// These methods search the RBTree for a node whose name is longest
     /// against name. The found node, if any, is returned via the node pointer.
     ///
     /// By default, nodes that don't have data (see RBNode::isEmpty) are
     /// ignored and the result can be NOTFOUND even if there's a node whose
-    /// name mathes. The plan is to introduce a "no data OK" mode for this
-    /// method, that would match any node of the tree regardless of wheather
-    /// the node has any data or not.
+    /// name matches.  If the \c RBTree is constructed with its
+    /// \c returnEmptyNode parameter being \c true, an empty node will also
+    /// be match candidates.
     ///
-    /// The case with "no data OK" mode is not as easy as it seems. For example
-    /// in the diagram shown in the class description, the name y.d.e.f is
-    /// logically contained in the tree as part of the node w.y.  It cannot be
-    /// identified simply by checking whether existing nodes (such as
-    /// d.e.f or w.y) has data.
+    /// \note Even when \c returnEmptyNode is \c true, not all empty nodes
+    /// in terms of the DNS protocol may necessarily be found by this method.
+    /// For example, in the \ref diagram shown in the class description,
+    /// the name y.d.e.f is logically contained in the tree as part of the
+    /// node w.y, but the \c find() variants cannot find the former for
+    /// the search key of y.d.e.f, no matter how the \c RBTree is constructed.
+    /// The caller of this method must use a different way to identify the
+    /// hidden match when necessary.
     ///
-    /// These methods involves operations on names that can throw an exception.
+    /// These methods involve operations on names that can throw an exception.
     /// If that happens the exception will be propagated to the caller.
     /// The callback function should generally not throw an exception, but
     /// if it throws, the exception will be propagated to the caller.
@@ -365,13 +558,39 @@ public:
     ///    of it. In that case, node parameter is left intact.
     //@{
 
-    /// \brief Find with callback.
+    /// \brief Simple find.
     ///
-    /// \anchor callback
+    /// Acts as described in the \ref find section.
+    Result find(const isc::dns::Name& name, RBNode<T>** node) const {
+        RBTreeNodeChain<T> node_path;
+        return (find<void*>(name, node, node_path, NULL, NULL));
+    }
+
+    /// \brief Simple find returning immutable node.
     ///
-    /// This version of find calls the callback whenever traversing (on the
-    /// way from root down the tree) a marked node on the way down through the
-    /// domain namespace (see RBNode::enableCallback and related functions).
+    /// Acts as described in the \ref find section, but returns immutable node
+    /// pointer.
+    Result find(const isc::dns::Name& name, const RBNode<T>** node) const {
+        RBTreeNodeChain<T> node_path;
+        RBNode<T> *target_node = NULL;
+        Result ret = (find<void*>(name, &target_node, node_path, NULL, NULL));
+        if (ret != NOTFOUND) {
+            *node = target_node;
+        }
+        return (ret);
+    }
+
+    /// \brief Find with callback and node chain.
+    ///
+    /// This version of \c find() is specifically designed for the backend
+    /// of the \c MemoryZone class, and implements all necessary features
+    /// for that purpose.  Other applications shouldn't need these additional
+    /// features, and should normally use the simpler versions.
+    ///
+    /// This version of \c find() calls the callback whenever traversing (on
+    /// the way from root down the tree) a marked node on the way down through
+    /// the domain namespace (see RBNode::enableCallback and related
+    /// functions).
     ///
     /// If you return true from the callback, the search is stopped and a
     /// PARTIALMATCH is returned with the given node. Note that this node
@@ -384,9 +603,32 @@ public:
     /// The callbacks are not general functors for the same reason - we don't
     /// expect it to be needed.
     ///
+    /// Another special feature of this version is the ability to provide
+    /// a node chain containing a path to the found node.  The chain will be
+    /// returned via the \c node_path parameter.
+    /// The passed parameter must be empty.
+    /// On success, it will contain all the ancestor nodes from the found
+    /// node towards the root.
+    /// For example, if we look for o.w.y.d.e.f in the example \ref diagram,
+    /// \c node_path will contain w.y and d.e.f; the \c top() node of the
+    /// chain will be o, w.f and d.e.f will be stored below it.
+    ///
+    /// This feature can be used to get the absolute name for a node;
+    /// to do so, we need to travel upside from the node toward the root,
+    /// concatenating all ancestor names.  With the current implementation
+    /// it's not possible without a node chain, because there is a no pointer
+    /// from the root of a subtree to the parent subtree (this may change
+    /// in a future version).  A node chain can also be used to find the next
+    /// node of a given node in the entire RBTree; the \c nextNode() method
+    /// takes a node chain as a parameter.
+    ///
+    /// \exception isc::BadValue node_path is not empty.
+    ///
     /// \param name Target to be found
     /// \param node On success (either \c EXACTMATCH or \c PARTIALMATCH)
     ///     it will store a pointer to the matching node
+    /// \param node_path It will store all the ancestor nodes in the RBTree
+    ///        from the found node to the root.  The found node is stored.
     /// \param callback If non \c NULL, a call back function to be called
     ///     at marked nodes (see above).
     /// \param callback_arg A caller supplied argument to be passed to
@@ -395,33 +637,57 @@ public:
     /// \return As described above, but in case of callback returning true,
     ///     it returns immediately with the current node.
     template <typename CBARG>
-    Result find(const isc::dns::Name& name, RBNode<T>** node,
+    Result find(const isc::dns::Name& name,
+                RBNode<T>** node,
+                RBTreeNodeChain<T>& node_path,
                 bool (*callback)(const RBNode<T>&, CBARG),
                 CBARG callback_arg) const;
 
-    /// \brief Find with callback returning immutable node.
+    /// \brief Simple find returning immutable node.
     ///
-    /// It has the same behaviour as the find with \ref callback version.
+    /// Acts as described in the \ref find section, but returns immutable
+    /// node pointer.
     template <typename CBARG>
-    Result find(const isc::dns::Name& name, const RBNode<T>** node,
+    Result find(const isc::dns::Name& name,
+                const RBNode<T>** node,
+                RBTreeNodeChain<T>& node_path,
                 bool (*callback)(const RBNode<T>&, CBARG),
-                CBARG callback_arg) const;
-
-    /// \brief Simple find.
-    ///
-    /// Acts as described in the \ref find section.
-    Result find(const isc::dns::Name& name, RBNode<T>** node) const {
-        return (find<void*>(name, node, NULL, NULL));
+                CBARG callback_arg) const
+    {
+        RBNode<T>* target_node = NULL;
+        Result ret = find(name, &target_node, node_path, callback,
+                          callback_arg);
+        if (ret != NOTFOUND) {
+            *node = target_node;
+        }
+        return (ret);
     }
+    //@}
 
-    /// \brieg Simple find returning immutable node.
+    /// \brief return the next bigger node in DNSSEC order from a given node
+    /// chain.
     ///
-    /// Acts as described in the \ref find section, but returns immutable node
-    /// pointer.
-    Result find(const isc::dns::Name& name, const RBNode<T>** node) const {
-        return (find<void*>(name, node, NULL, NULL));
-    }
-    //@}
+    /// This method identifies the next bigger node of the node currently
+    /// referenced in \c node_path and returns it.
+    /// This method also updates the passed \c node_path so that it will store
+    /// the path for the returned next node.
+    /// It will be convenient when we want to iterate over the all nodes
+    /// of \c RBTree; we can do this by calling this method repeatedly
+    /// starting from the root node.
+    ///
+    /// \note \c nextNode() will iterate over all the nodes in RBTree including
+    /// empty nodes. If empty node isn't desired, it's easy to add logic to
+    /// check return node and keep invoking \c nextNode() until the non-empty
+    /// node is retrieved.
+    ///
+    /// \exception isc::BadValue node_path is empty.
+    ///
+    /// \param node_path A node chain that stores all the nodes along the path
+    /// from root to node.
+    ///
+    /// \return An \c RBNode that is next bigger than \c node; if \c node is
+    /// the largest, \c NULL will be returned.
+    const RBNode<T>* nextNode(RBTreeNodeChain<T>& node_path) const;
 
     /// \brief Get the total number of nodes in the tree
     ///
@@ -498,23 +764,11 @@ private:
     //@{
     /// \brief delete tree whose root is equal to node
     void deleteHelper(RBNode<T> *node);
-    /// \brief find the node with name
-    ///
-    /// Internal searching function.
-    ///
-    /// \param name What should be found.
-    /// \param up It will point to the node whose down pointer points
-    ///     to the tree containing node. If we looked for o.w.y.d.e.f in the
-    ///     \ref diagram, the up would point to the w.y node.
-    ///     This parameter is not used currently, but it will be soon.
-    /// \param node The found node.
-    template <typename CBARG>
-    Result findHelper(const isc::dns::Name& name, const RBNode<T>** up,
-                      RBNode<T>** node,
-                      bool (*callback)(const RBNode<T>&, CBARG),
-                      CBARG callback_arg) const;
+
+    /// \brief Print the information of given RBNode.
     void dumpTreeHelper(std::ostream& os, const RBNode<T>* node,
                         unsigned int depth) const;
+
     /// \brief Indentation helper function for dumpTree
     static void indent(std::ostream& os, unsigned int depth);
 
@@ -525,17 +779,21 @@ private:
     void nodeFission(RBNode<T>& node, const isc::dns::Name& sub_name);
     //@}
 
-    RBNode<T>*  root_;
     RBNode<T>*  NULLNODE;
+    RBNode<T>*  root_;
     /// the node count of current tree
     unsigned int node_count_;
+    /// search policy for rbtree
+    const bool needsReturnEmptyNode_;
 };
 
 template <typename T>
-RBTree<T>::RBTree() {
-    NULLNODE = RBNode<T>::NULL_NODE();
-    root_ = NULLNODE;
-    node_count_ = 0;
+RBTree<T>::RBTree(bool returnEmptyNode) :
+    NULLNODE(RBNode<T>::NULL_NODE()),
+    root_(NULLNODE),
+    node_count_(0),
+    needsReturnEmptyNode_(returnEmptyNode)
+{
 }
 
 template <typename T>
@@ -545,18 +803,19 @@ RBTree<T>::~RBTree() {
 }
 
 template <typename T>
-void RBTree<T> ::deleteHelper(RBNode<T> *root) {
+void
+RBTree<T>::deleteHelper(RBNode<T>* root) {
     if (root == NULLNODE) {
         return;
     }
 
-    RBNode<T> *node = root;
+    RBNode<T>* node = root;
     while (root->left_ != NULLNODE || root->right_ != NULLNODE) {
         while (node->left_ != NULLNODE || node->right_ != NULLNODE) {
             node = (node->left_ != NULLNODE) ? node->left_ : node->right_;
         }
 
-        RBNode<T> *parent = node->parent_;
+        RBNode<T>* parent = node->parent_;
         if (parent->left_ == node) {
             parent->left_ = NULLNODE;
         } else {
@@ -574,45 +833,23 @@ void RBTree<T> ::deleteHelper(RBNode<T> *root) {
     --node_count_;
 }
 
-template <typename T> template <typename CBARG>
+template <typename T>
+template <typename CBARG>
 typename RBTree<T>::Result
-RBTree<T>::find(const isc::dns::Name& name, RBNode<T>** node,
+RBTree<T>::find(const isc::dns::Name& target_name,
+                RBNode<T>** target,
+                RBTreeNodeChain<T>& node_path,
                 bool (*callback)(const RBNode<T>&, CBARG),
                 CBARG callback_arg) const
 {
-    const RBNode<T>* up_node = NULLNODE;
-    return (findHelper(name, &up_node, node, callback, callback_arg));
-}
+    using namespace helper;
 
-template <typename T> template <typename CBARG>
-typename RBTree<T>::Result
-RBTree<T>::find(const isc::dns::Name& name, const RBNode<T>** node,
-                bool (*callback)(const RBNode<T>&, CBARG),
-                CBARG callback_arg) const
-{
-    const RBNode<T>* up_node;
-    RBNode<T>* target_node;
-    const typename RBTree<T>::Result ret =
-        findHelper(name, &up_node, &target_node, callback, callback_arg);
-    if (ret != NOTFOUND) {
-        *node = target_node;
+    if (!node_path.isEmpty()) {
+        isc_throw(isc::BadValue, "RBTree::find is given a non empty chain");
     }
-    return (ret);
-}
-
-template <typename T> template <typename CBARG>
-typename RBTree<T>::Result
-RBTree<T>::findHelper(const isc::dns::Name& target_name,
-                      const RBNode<T>** up_node,
-                      RBNode<T>** target,
-                      bool (*callback)(const RBNode<T>&, CBARG),
-                      CBARG callback_arg) const
-{
-    using namespace helper;
 
     RBNode<T>* node = root_;
-    typename RBTree<T>::Result ret = NOTFOUND;
-    *up_node = NULLNODE;
+    Result ret = NOTFOUND;
     isc::dns::Name name = target_name;
 
     while (node != NULLNODE) {
@@ -621,7 +858,8 @@ RBTree<T>::findHelper(const isc::dns::Name& target_name,
         const isc::dns::NameComparisonResult::NameRelation relation =
             compare_result.getRelation();
         if (relation == isc::dns::NameComparisonResult::EQUAL) {
-            if (!node->isEmpty()) {
+            if (needsReturnEmptyNode_ || !node->isEmpty()) {
+                node_path.push(node);
                 *target = node;
                 ret = EXACTMATCH;
             }
@@ -634,8 +872,8 @@ RBTree<T>::findHelper(const isc::dns::Name& target_name,
                 node = (compare_result.getOrder() < 0) ?
                     node->left_ : node->right_;
             } else if (relation == isc::dns::NameComparisonResult::SUBDOMAIN) {
-                if (!node->isEmpty()) {
-                    ret = RBTree<T>::PARTIALMATCH;
+                if (needsReturnEmptyNode_ || !node->isEmpty()) {
+                    ret = PARTIALMATCH;
                     *target = node;
                     if (callback != NULL && node->callback_required_) {
                         if ((callback)(*node, callback_arg)) {
@@ -643,7 +881,7 @@ RBTree<T>::findHelper(const isc::dns::Name& target_name,
                         }
                     }
                 }
-                *up_node = node;
+                node_path.push(node);
                 name = name - node->name_;
                 node = node->down_;
             } else {
@@ -656,6 +894,51 @@ RBTree<T>::findHelper(const isc::dns::Name& target_name,
 }
 
 template <typename T>
+const RBNode<T>*
+RBTree<T>::nextNode(RBTreeNodeChain<T>& node_path) const {
+    if (node_path.isEmpty()) {
+        isc_throw(isc::BadValue, "RBTree::nextNode is given an empty chain");
+    }
+
+    const RBNode<T>* node = node_path.top();
+    // if node has sub domain, the next domain is the smallest
+    // domain in sub domain tree
+    if (node->down_ != NULLNODE) {
+        const RBNode<T>* left_most = node->down_;
+        while (left_most->left_ != NULLNODE) {
+            left_most = left_most->left_;
+        }
+        node_path.push(left_most);
+        return (left_most);
+    }
+
+    // node_path go to up level
+    node_path.pop();
+    // otherwise found the successor node in current level
+    const RBNode<T>* successor = node->successor();
+    if (successor != NULLNODE) {
+        node_path.push(successor);
+        return (successor);
+    }
+
+    // if no successor found move to up level, the next successor
+    // is the successor of up node in the up level tree, if
+    // up node doesn't have successor we gonna keep moving to up
+    // level
+    while (!node_path.isEmpty()) {
+        const RBNode<T>* up_node_successor = node_path.top()->successor();
+        node_path.pop();
+        if (up_node_successor != NULLNODE) {
+            node_path.push(up_node_successor);
+            return (up_node_successor);
+        }
+    }
+
+    return (NULL);
+}
+
+
+template <typename T>
 typename RBTree<T>::Result
 RBTree<T>::insert(const isc::dns::Name& target_name, RBNode<T>** new_node) {
     using namespace helper;
@@ -730,6 +1013,7 @@ RBTree<T>::insert(const isc::dns::Name& target_name, RBNode<T>** new_node) {
     return (SUCCESS);
 }
 
+
 template <typename T>
 void
 RBTree<T>::nodeFission(RBNode<T>& node, const isc::dns::Name& base_name) {
@@ -752,6 +1036,7 @@ RBTree<T>::nodeFission(RBNode<T>& node, const isc::dns::Name& base_name) {
     down_node.release();
 }
 
+
 template <typename T>
 void
 RBTree<T>::insertRebalance(RBNode<T>** root, RBNode<T>* node) {
@@ -847,6 +1132,7 @@ RBTree<T>::rightRotate(RBNode<T>** root, RBNode<T>* node) {
     return (node);
 }
 
+
 template <typename T>
 void
 RBTree<T>::dumpTree(std::ostream& os, unsigned int depth) const {
diff --git a/src/lib/datasrc/sqlite3_datasrc.cc b/src/lib/datasrc/sqlite3_datasrc.cc
index 08e7482..ab910ba 100644
--- a/src/lib/datasrc/sqlite3_datasrc.cc
+++ b/src/lib/datasrc/sqlite3_datasrc.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <string>
 #include <sstream>
 
diff --git a/src/lib/datasrc/sqlite3_datasrc.h b/src/lib/datasrc/sqlite3_datasrc.h
index 0261630..d4abef7 100644
--- a/src/lib/datasrc/sqlite3_datasrc.h
+++ b/src/lib/datasrc/sqlite3_datasrc.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __DATA_SOURCE_SQLITE3_H
 #define __DATA_SOURCE_SQLITE3_H
 
diff --git a/src/lib/datasrc/static_datasrc.cc b/src/lib/datasrc/static_datasrc.cc
index 79d6899..025078a 100644
--- a/src/lib/datasrc/static_datasrc.cc
+++ b/src/lib/datasrc/static_datasrc.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <config.h>
 
 #include <cassert>
@@ -72,6 +70,7 @@ StaticDataSrcImpl::StaticDataSrcImpl() :
                                  RRType::TXT(), RRTTL(0)));
     authors->addRdata(generic::TXT("Chen Zhengzhang")); // Jerry
     authors->addRdata(generic::TXT("Evan Hunt"));
+    authors->addRdata(generic::TXT("Haidong Wang")); // Ocean
     authors->addRdata(generic::TXT("Han Feng"));
     authors->addRdata(generic::TXT("Jelte Jansen"));
     authors->addRdata(generic::TXT("Jeremy C. Reed")); 
@@ -84,6 +83,7 @@ StaticDataSrcImpl::StaticDataSrcImpl() :
     authors->addRdata(generic::TXT("Shane Kerr"));
     authors->addRdata(generic::TXT("Shen Tingting"));
     authors->addRdata(generic::TXT("Stephen Morris"));
+    authors->addRdata(generic::TXT("Yoshitaka Aharen"));
     authors->addRdata(generic::TXT("Zhang Likun"));
 
     authors_ns = RRsetPtr(new RRset(authors_name, RRClass::CH(),
diff --git a/src/lib/datasrc/static_datasrc.h b/src/lib/datasrc/static_datasrc.h
index 0cd14c1..4d212fe 100644
--- a/src/lib/datasrc/static_datasrc.h
+++ b/src/lib/datasrc/static_datasrc.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 //
 // Sample Datasource implementation; this datasource only returns
 // static content for the queries
@@ -76,9 +74,9 @@ public:
                             isc::dns::Name& target,
                             const isc::dns::Name* zonename) const;
 
-   Result findCoveringNSEC3(const isc::dns::Name& zonename,
-                            std::string& hash,
-                            isc::dns::RRsetList& target) const;
+    Result findCoveringNSEC3(const isc::dns::Name& zonename,
+                             std::string& hash,
+                             isc::dns::RRsetList& target) const;
 
     Result init();
     Result init(isc::data::ConstElementPtr config);
diff --git a/src/lib/datasrc/tests/cache_unittest.cc b/src/lib/datasrc/tests/cache_unittest.cc
index d7192f8..96beae0 100644
--- a/src/lib/datasrc/tests/cache_unittest.cc
+++ b/src/lib/datasrc/tests/cache_unittest.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <stdexcept>
 
 #include <dns/name.h>
diff --git a/src/lib/datasrc/tests/datasrc_unittest.cc b/src/lib/datasrc/tests/datasrc_unittest.cc
index 8718c12..bff7949 100644
--- a/src/lib/datasrc/tests/datasrc_unittest.cc
+++ b/src/lib/datasrc/tests/datasrc_unittest.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <stdint.h>
 
 #include <iostream>
diff --git a/src/lib/datasrc/tests/memory_datasrc_unittest.cc b/src/lib/datasrc/tests/memory_datasrc_unittest.cc
index 9862f29..be71e7f 100644
--- a/src/lib/datasrc/tests/memory_datasrc_unittest.cc
+++ b/src/lib/datasrc/tests/memory_datasrc_unittest.cc
@@ -1,4 +1,5 @@
 // Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011  CZ NIC
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -15,7 +16,10 @@
 #include <exceptions/exceptions.h>
 
 #include <dns/name.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
 #include <dns/rrclass.h>
+#include <dns/rrsetlist.h>
 #include <dns/rrttl.h>
 #include <dns/masterload.h>
 
@@ -24,6 +28,7 @@
 #include <gtest/gtest.h>
 
 using namespace isc::dns;
+using namespace isc::dns::rdata;
 using namespace isc::datasrc;
 
 namespace {
@@ -142,10 +147,13 @@ public:
         class_(RRClass::IN()),
         origin_("example.org"),
         ns_name_("ns.example.org"),
+        cname_name_("cname.example.org"),
+        dname_name_("dname.example.org"),
         child_ns_name_("child.example.org"),
         child_glue_name_("ns.child.example.org"),
         grandchild_ns_name_("grand.child.example.org"),
         grandchild_glue_name_("ns.grand.child.example.org"),
+        child_dname_name_("dname.child.example.org"),
         zone_(class_, origin_),
         rr_out_(new RRset(Name("example.com"), class_, RRType::A(),
             RRTTL(300))),
@@ -153,6 +161,14 @@ public:
         rr_ns_a_(new RRset(ns_name_, class_, RRType::A(), RRTTL(300))),
         rr_ns_aaaa_(new RRset(ns_name_, class_, RRType::AAAA(), RRTTL(300))),
         rr_a_(new RRset(origin_, class_, RRType::A(), RRTTL(300))),
+        rr_cname_(new RRset(cname_name_, class_, RRType::CNAME(), RRTTL(300))),
+        rr_cname_a_(new RRset(cname_name_, class_, RRType::A(), RRTTL(300))),
+        rr_dname_(new RRset(dname_name_, class_, RRType::DNAME(), RRTTL(300))),
+        rr_dname_a_(new RRset(dname_name_, class_, RRType::A(),
+            RRTTL(300))),
+        rr_dname_ns_(new RRset(dname_name_, class_, RRType::NS(), RRTTL(300))),
+        rr_dname_apex_(new RRset(origin_, class_, RRType::DNAME(),
+            RRTTL(300))),
         rr_child_ns_(new RRset(child_ns_name_, class_, RRType::NS(),
                                RRTTL(300))),
         rr_child_glue_(new RRset(child_glue_name_, class_, RRType::A(),
@@ -160,13 +176,16 @@ public:
         rr_grandchild_ns_(new RRset(grandchild_ns_name_, class_, RRType::NS(),
                                     RRTTL(300))),
         rr_grandchild_glue_(new RRset(grandchild_glue_name_, class_,
-                                      RRType::AAAA(), RRTTL(300)))
+                                      RRType::AAAA(), RRTTL(300))),
+        rr_child_dname_(new RRset(child_dname_name_, class_, RRType::DNAME(),
+            RRTTL(300)))
     {
     }
     // Some data to test with
     const RRClass class_;
-    const Name origin_, ns_name_, child_ns_name_, child_glue_name_,
-        grandchild_ns_name_, grandchild_glue_name_;
+    const Name origin_, ns_name_, cname_name_, dname_name_, child_ns_name_,
+        child_glue_name_, grandchild_ns_name_, grandchild_glue_name_,
+        child_dname_name_;
     // The zone to torture by tests
     MemoryZone zone_;
 
@@ -187,10 +206,17 @@ public:
         rr_ns_aaaa_,
         // A of example.org
         rr_a_;
+    RRsetPtr rr_cname_;         // CNAME in example.org (RDATA will be added)
+    ConstRRsetPtr rr_cname_a_; // for mixed CNAME + A case
+    RRsetPtr rr_dname_;         // DNAME in example.org (RDATA will be added)
+    ConstRRsetPtr rr_dname_a_; // for mixed DNAME + A case
+    ConstRRsetPtr rr_dname_ns_; // for mixed DNAME + NS case
+    ConstRRsetPtr rr_dname_apex_; // for mixed DNAME + NS case in the apex
     ConstRRsetPtr rr_child_ns_; // NS of a child domain (for delegation)
     ConstRRsetPtr rr_child_glue_; // glue RR of the child domain
     ConstRRsetPtr rr_grandchild_ns_; // NS below a zone cut (unusual)
     ConstRRsetPtr rr_grandchild_glue_; // glue RR below a deeper zone cut
+    ConstRRsetPtr rr_child_dname_; // A DNAME under NS
 
     /**
      * \brief Test one find query to the zone.
@@ -211,7 +237,8 @@ public:
     void findTest(const Name& name, const RRType& rrtype, Zone::Result result,
                   bool check_answer = true,
                   const ConstRRsetPtr& answer = ConstRRsetPtr(),
-                  MemoryZone *zone = NULL,
+                  RRsetList* target = NULL,
+                  MemoryZone* zone = NULL,
                   Zone::FindOptions options = Zone::FIND_DEFAULT)
     {
         if (!zone) {
@@ -220,7 +247,7 @@ public:
         // The whole block is inside, because we need to check the result and
         // we can't assign to FindResult
         EXPECT_NO_THROW({
-                Zone::FindResult find_result(zone->find(name, rrtype,
+                Zone::FindResult find_result(zone->find(name, rrtype, target,
                                                         options));
                 // Check it returns correct answers
                 EXPECT_EQ(result, find_result.code);
@@ -264,6 +291,125 @@ TEST_F(MemoryZoneTest, add) {
     EXPECT_NO_THROW(EXPECT_EQ(EXIST, zone_.add(rr_ns_a_)));
 }
 
+TEST_F(MemoryZoneTest, addMultipleCNAMEs) {
+    rr_cname_->addRdata(generic::CNAME("canonical1.example.org."));
+    rr_cname_->addRdata(generic::CNAME("canonical2.example.org."));
+    EXPECT_THROW(zone_.add(rr_cname_), MemoryZone::AddError);
+}
+
+TEST_F(MemoryZoneTest, addCNAMEThenOther) {
+    rr_cname_->addRdata(generic::CNAME("canonical.example.org."));
+    EXPECT_EQ(SUCCESS, zone_.add(rr_cname_));
+    EXPECT_THROW(zone_.add(rr_cname_a_), MemoryZone::AddError);
+}
+
+TEST_F(MemoryZoneTest, addOtherThenCNAME) {
+    rr_cname_->addRdata(generic::CNAME("canonical.example.org."));
+    EXPECT_EQ(SUCCESS, zone_.add(rr_cname_a_));
+    EXPECT_THROW(zone_.add(rr_cname_), MemoryZone::AddError);
+}
+
+TEST_F(MemoryZoneTest, findCNAME) {
+    // install CNAME RR
+    rr_cname_->addRdata(generic::CNAME("canonical.example.org."));
+    EXPECT_EQ(SUCCESS, zone_.add(rr_cname_));
+
+    // Find A RR of the same.  Should match the CNAME
+    findTest(cname_name_, RRType::NS(), Zone::CNAME, true, rr_cname_);
+
+    // Find the CNAME itself.  Should result in normal SUCCESS
+    findTest(cname_name_, RRType::CNAME(), Zone::SUCCESS, true, rr_cname_);
+}
+
+TEST_F(MemoryZoneTest, findCNAMEUnderZoneCut) {
+    // There's nothing special when we find a CNAME under a zone cut
+    // (with FIND_GLUE_OK).  The behavior is different from BIND 9,
+    // so we test this case explicitly.
+    EXPECT_EQ(SUCCESS, zone_.add(rr_child_ns_));
+    RRsetPtr rr_cname_under_cut_(new RRset(Name("cname.child.example.org"),
+                                           class_, RRType::CNAME(),
+                                           RRTTL(300)));
+    EXPECT_EQ(SUCCESS, zone_.add(rr_cname_under_cut_));
+    findTest(Name("cname.child.example.org"), RRType::AAAA(),
+             Zone::CNAME, true, rr_cname_under_cut_, NULL, NULL,
+             Zone::FIND_GLUE_OK);
+}
+
+// Two DNAMEs at single domain are disallowed by RFC 2672, section 3)
+// Having a CNAME there is disallowed too, but it is tested by
+// addOtherThenCNAME and addCNAMEThenOther.
+TEST_F(MemoryZoneTest, addMultipleDNAMEs) {
+    rr_dname_->addRdata(generic::DNAME("dname1.example.org."));
+    rr_dname_->addRdata(generic::DNAME("dname2.example.org."));
+    EXPECT_THROW(zone_.add(rr_dname_), MemoryZone::AddError);
+}
+
+/*
+ * These two tests ensure that we can't have DNAME and NS at the same
+ * node with the exception of the apex of zone (forbidden by RFC 2672)
+ */
+TEST_F(MemoryZoneTest, addDNAMEThenNS) {
+    EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_dname_)));
+    EXPECT_THROW(zone_.add(rr_dname_ns_), MemoryZone::AddError);
+}
+
+TEST_F(MemoryZoneTest, addNSThenDNAME) {
+    EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_dname_ns_)));
+    EXPECT_THROW(zone_.add(rr_dname_), MemoryZone::AddError);
+}
+
+// It is allowed to have NS and DNAME at apex
+TEST_F(MemoryZoneTest, DNAMEAndNSAtApex) {
+    EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_dname_apex_)));
+    EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_ns_)));
+
+    // The NS should be possible to be found, below should be DNAME, not
+    // delegation
+    findTest(origin_, RRType::NS(), Zone::SUCCESS, true, rr_ns_);
+    findTest(child_ns_name_, RRType::A(), Zone::DNAME, true, rr_dname_apex_);
+}
+
+TEST_F(MemoryZoneTest, NSAndDNAMEAtApex) {
+    EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_ns_)));
+    EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_dname_apex_)));
+}
+
+// TODO: Test (and implement) adding data under DNAME. That is forbidden by
+// 2672 as well.
+
+// Search under a DNAME record. It should return the DNAME
+TEST_F(MemoryZoneTest, findBelowDNAME) {
+    rr_dname_->addRdata(generic::DNAME("target.example.org."));
+    EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_dname_)));
+    findTest(Name("below.dname.example.org"), RRType::A(), Zone::DNAME, true,
+        rr_dname_);
+}
+
+// Search at the domain with DNAME. It should act as DNAME isn't there, DNAME
+// influences only the data below (see RFC 2672, section 3)
+TEST_F(MemoryZoneTest, findAtDNAME) {
+    rr_dname_->addRdata(generic::DNAME("target.example.org."));
+    EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_dname_)));
+    EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_dname_a_)));
+
+    findTest(dname_name_, RRType::A(), Zone::SUCCESS, true, rr_dname_a_);
+    findTest(dname_name_, RRType::DNAME(), Zone::SUCCESS, true, rr_dname_);
+    findTest(dname_name_, RRType::TXT(), Zone::NXRRSET, true);
+}
+
+// Try searching something that is both under NS and DNAME, without and with
+// GLUE_OK mode (it should stop at the NS and DNAME respectively).
+TEST_F(MemoryZoneTest, DNAMEUnderNS) {
+    zone_.add(rr_child_ns_);
+    zone_.add(rr_child_dname_);
+
+    Name lowName("below.dname.child.example.org.");
+
+    findTest(lowName, RRType::A(), Zone::DELEGATION, true, rr_child_ns_);
+    findTest(lowName, RRType::A(), Zone::DNAME, true, rr_child_dname_, NULL,
+        NULL, Zone::FIND_GLUE_OK);
+}
+
 // Test adding child zones and zone cut handling
 TEST_F(MemoryZoneTest, delegationNS) {
     // add in-zone data
@@ -292,6 +438,51 @@ TEST_F(MemoryZoneTest, delegationNS) {
              Zone::DELEGATION, true, rr_child_ns_); // note: !rr_grandchild_ns_
 }
 
+TEST_F(MemoryZoneTest, findAny) {
+    EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_a_)));
+    EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_ns_)));
+    EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_child_glue_)));
+
+    // origin
+    RRsetList origin_rrsets;
+    findTest(origin_, RRType::ANY(), Zone::SUCCESS, true,
+             ConstRRsetPtr(), &origin_rrsets);
+    EXPECT_EQ(2, origin_rrsets.size());
+    EXPECT_EQ(rr_a_, origin_rrsets.findRRset(RRType::A(), RRClass::IN()));
+    EXPECT_EQ(rr_ns_, origin_rrsets.findRRset(RRType::NS(), RRClass::IN()));
+
+    // out zone name
+    RRsetList out_rrsets;
+    findTest(Name("example.com"), RRType::ANY(), Zone::NXDOMAIN, true,
+             ConstRRsetPtr(), &out_rrsets);
+    EXPECT_EQ(0, out_rrsets.size());
+
+    RRsetList glue_child_rrsets;
+    findTest(child_glue_name_, RRType::ANY(), Zone::SUCCESS, true,
+                ConstRRsetPtr(), &glue_child_rrsets);
+    EXPECT_EQ(rr_child_glue_, glue_child_rrsets.findRRset(RRType::A(),
+                                                     RRClass::IN()));
+    EXPECT_EQ(1, glue_child_rrsets.size());
+
+    // TODO: test NXRRSET case after rbtree non-terminal logic has
+    // been implemented
+
+    // add zone cut
+    EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_child_ns_)));
+
+    // zone cut
+    RRsetList child_rrsets;
+    findTest(child_ns_name_, RRType::ANY(), Zone::DELEGATION, true,
+             rr_child_ns_, &child_rrsets);
+    EXPECT_EQ(0, child_rrsets.size());
+
+    // glue for this zone cut
+    RRsetList new_glue_child_rrsets;
+    findTest(child_glue_name_, RRType::ANY(), Zone::DELEGATION, true,
+                rr_child_ns_, &new_glue_child_rrsets);
+    EXPECT_EQ(0, new_glue_child_rrsets.size());
+}
+
 TEST_F(MemoryZoneTest, glue) {
     // install zone data:
     // a zone cut
@@ -310,15 +501,15 @@ TEST_F(MemoryZoneTest, glue) {
 
     // If we do it in the "glue OK" mode, we should find the exact match.
     findTest(child_glue_name_, RRType::A(), Zone::SUCCESS, true,
-             rr_child_glue_, NULL, Zone::FIND_GLUE_OK);
+             rr_child_glue_, NULL, NULL, Zone::FIND_GLUE_OK);
 
     // glue OK + NXRRSET case
     findTest(child_glue_name_, RRType::AAAA(), Zone::NXRRSET, true,
-             ConstRRsetPtr(), NULL, Zone::FIND_GLUE_OK);
+             ConstRRsetPtr(), NULL, NULL, Zone::FIND_GLUE_OK);
 
     // glue OK + NXDOMAIN case
     findTest(Name("www.child.example.org"), RRType::A(), Zone::DELEGATION,
-             true, rr_child_ns_, NULL, Zone::FIND_GLUE_OK);
+             true, rr_child_ns_, NULL, NULL, Zone::FIND_GLUE_OK);
 
     // TODO:
     // glue name would match a wildcard under a zone cut: wildcard match
@@ -327,24 +518,13 @@ TEST_F(MemoryZoneTest, glue) {
 
     // nested cut case.  The glue should be found.
     findTest(grandchild_glue_name_, RRType::AAAA(), Zone::SUCCESS,
-             true, rr_grandchild_glue_, NULL, Zone::FIND_GLUE_OK);    
+             true, rr_grandchild_glue_, NULL, NULL, Zone::FIND_GLUE_OK);
 
     // A non-existent name in nested cut.  This should result in delegation
     // at the highest zone cut.
     findTest(Name("www.grand.child.example.org"), RRType::TXT(),
-             Zone::DELEGATION, true, rr_child_ns_, NULL, Zone::FIND_GLUE_OK);
-}
-
-// Test adding DNAMEs and resulting delegation handling
-// Listing ideas only for now
-TEST_F(MemoryZoneTest, delegationDNAME) {
-    // apex DNAME: allowed by spec.  No DNAME delegation at the apex;
-    // descendants are subject to delegation.
-
-    // Other cases of NS and DNAME mixture are prohibited.
-    // BIND 9 doesn't reject such cases at load time, however.
-
-    // DNAME and ordinary types (allowed by spec)
+             Zone::DELEGATION, true, rr_child_ns_, NULL, NULL,
+             Zone::FIND_GLUE_OK);
 }
 
 /**
@@ -391,14 +571,14 @@ TEST_F(MemoryZoneTest, load) {
 
     // Now see there are some rrsets (we don't look inside, though)
     findTest(Name("."), RRType::SOA(), Zone::SUCCESS, false, ConstRRsetPtr(),
-        &rootzone);
+        NULL, &rootzone);
     findTest(Name("."), RRType::NS(), Zone::SUCCESS, false, ConstRRsetPtr(),
-        &rootzone);
+        NULL, &rootzone);
     findTest(Name("a.root-servers.net."), RRType::A(), Zone::SUCCESS, false,
-        ConstRRsetPtr(), &rootzone);
+        ConstRRsetPtr(), NULL, &rootzone);
     // But this should no longer be here
     findTest(ns_name_, RRType::AAAA(), Zone::NXDOMAIN, true, ConstRRsetPtr(),
-        &rootzone);
+        NULL, &rootzone);
 
     // Try loading zone that is wrong in a different way
     EXPECT_THROW(zone_.load(TEST_DATA_DIR "/duplicate_rrset.zone"),
@@ -427,13 +607,13 @@ TEST_F(MemoryZoneTest, swap) {
     EXPECT_EQ(RRClass::IN(), zone2.getClass());
     // make sure the zone data is swapped, too
     findTest(origin_, RRType::NS(), Zone::NXDOMAIN, false, ConstRRsetPtr(),
-             &zone1);
+             NULL, &zone1);
     findTest(other_origin, RRType::TXT(), Zone::SUCCESS, false,
-             ConstRRsetPtr(), &zone1);
+             ConstRRsetPtr(), NULL, &zone1);
     findTest(origin_, RRType::NS(), Zone::SUCCESS, false, ConstRRsetPtr(),
-             &zone2);
+             NULL, &zone2);
     findTest(other_origin, RRType::TXT(), Zone::NXDOMAIN, false,
-             ConstRRsetPtr(), &zone2);
+             ConstRRsetPtr(), NULL, &zone2);
 }
 
 TEST_F(MemoryZoneTest, getFileName) {
@@ -459,4 +639,5 @@ TEST_F(MemoryZoneTest, getFileName) {
     EXPECT_EQ(TEST_DATA_DIR "/root.zone", zone_.getFileName());
     EXPECT_TRUE(rootzone.getFileName().empty());
 }
+
 }
diff --git a/src/lib/datasrc/tests/query_unittest.cc b/src/lib/datasrc/tests/query_unittest.cc
index a5758ad..fa7216b 100644
--- a/src/lib/datasrc/tests/query_unittest.cc
+++ b/src/lib/datasrc/tests/query_unittest.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <gtest/gtest.h>
 
 #include <dns/buffer.h>
diff --git a/src/lib/datasrc/tests/rbtree_unittest.cc b/src/lib/datasrc/tests/rbtree_unittest.cc
index 9a96b72..24d94a3 100644
--- a/src/lib/datasrc/tests/rbtree_unittest.cc
+++ b/src/lib/datasrc/tests/rbtree_unittest.cc
@@ -15,6 +15,8 @@
 
 #include <gtest/gtest.h>
 
+#include <exceptions/exceptions.h>
+
 #include <dns/name.h>
 #include <dns/rrclass.h>
 #include <dns/rrset.h>
@@ -26,10 +28,15 @@
 #include <dns/tests/unittest_util.h>
 
 using namespace std;
+using namespace isc;
 using namespace isc::dns;
 using isc::UnitTestUtil;
 using namespace isc::datasrc;
 
+// XXX: some compilers cannot find class static constants used in
+// EXPECT_xxx macros, for which we need an explicit empty definition.
+const size_t Name::MAX_LABELS;
+
 /* The initial structure of rbtree
  *
  *             b
@@ -50,31 +57,23 @@ using namespace isc::datasrc;
 namespace {
 class RBTreeTest : public::testing::Test {
 protected:
-    RBTreeTest() : rbtree() {
-        rbtree.insert(Name("c"), &rbtnode);
-        rbtnode->setData(RBNode<int>::NodeDataPtr(new int(1)));
-        rbtree.insert(Name("b"), &rbtnode);
-        rbtnode->setData(RBNode<int>::NodeDataPtr(new int(2)));
-        rbtree.insert(Name("a"), &rbtnode);
-        rbtnode->setData(RBNode<int>::NodeDataPtr(new int(3)));
-        rbtree.insert(Name("x.d.e.f"), &rbtnode);
-        rbtnode->setData(RBNode<int>::NodeDataPtr(new int(4)));
-        rbtree.insert(Name("z.d.e.f"), &rbtnode);
-        rbtnode->setData(RBNode<int>::NodeDataPtr(new int(5)));
-        rbtree.insert(Name("g.h"), &rbtnode);
-        rbtnode->setData(RBNode<int>::NodeDataPtr(new int(6)));
-        rbtree.insert(Name("i.g.h"), &rbtnode);
-        rbtnode->setData(RBNode<int>::NodeDataPtr(new int(7)));
-        rbtree.insert(Name("o.w.y.d.e.f"), &rbtnode);
-        rbtnode->setData(RBNode<int>::NodeDataPtr(new int(8)));
-        rbtree.insert(Name("j.z.d.e.f"), &rbtnode);
-        rbtnode->setData(RBNode<int>::NodeDataPtr(new int(9)));
-        rbtree.insert(Name("p.w.y.d.e.f"), &rbtnode);
-        rbtnode->setData(RBNode<int>::NodeDataPtr(new int(10)));
-        rbtree.insert(Name("q.w.y.d.e.f"), &rbtnode);
-        rbtnode->setData(RBNode<int>::NodeDataPtr(new int(11)));
+    RBTreeTest() : rbtree_expose_empty_node(true) {
+        const char* const domain_names[] = {
+            "c", "b", "a", "x.d.e.f", "z.d.e.f", "g.h", "i.g.h", "o.w.y.d.e.f",
+            "j.z.d.e.f", "p.w.y.d.e.f", "q.w.y.d.e.f"};
+        int name_count = sizeof(domain_names) / sizeof(domain_names[0]);
+        for (int i = 0; i < name_count; ++i) {
+            rbtree.insert(Name(domain_names[i]), &rbtnode);
+            rbtnode->setData(RBNode<int>::NodeDataPtr(new int(i + 1)));
+
+            rbtree_expose_empty_node.insert(Name(domain_names[i]), &rbtnode);
+            rbtnode->setData(RBNode<int>::NodeDataPtr(new int(i + 1)));
+
+        }
     }
+
     RBTree<int> rbtree;
+    RBTree<int> rbtree_expose_empty_node;
     RBNode<int>* rbtnode;
     const RBNode<int>* crbtnode;
 };
@@ -90,10 +89,12 @@ TEST_F(RBTreeTest, setGetData) {
 }
 
 TEST_F(RBTreeTest, insertNames) {
-    EXPECT_EQ(RBTree<int>::ALREADYEXISTS, rbtree.insert(Name("d.e.f"), &rbtnode));
+    EXPECT_EQ(RBTree<int>::ALREADYEXISTS, rbtree.insert(Name("d.e.f"),
+                                                        &rbtnode));
     EXPECT_EQ(Name("d.e.f"), rbtnode->getName());
     EXPECT_EQ(13, rbtree.getNodeCount());
 
+    //insert not exist node
     EXPECT_EQ(RBTree<int>::SUCCESS, rbtree.insert(Name("."), &rbtnode));
     EXPECT_EQ(Name("."), rbtnode->getName());
     EXPECT_EQ(14, rbtree.getNodeCount());
@@ -160,15 +161,33 @@ TEST_F(RBTreeTest, findName) {
     EXPECT_EQ(RBTree<int>::NOTFOUND, rbtree.find(Name("x"), &crbtnode));
     EXPECT_EQ(RBTree<int>::NOTFOUND, rbtree.find(Name("m.n"), &crbtnode));
 
+    // if we expose empty node, we can get the empty node created during insert
+    EXPECT_EQ(RBTree<int>::EXACTMATCH,
+              rbtree_expose_empty_node.find(Name("d.e.f"), &crbtnode));
+    EXPECT_EQ(RBTree<int>::EXACTMATCH,
+              rbtree_expose_empty_node.find(Name("w.y.d.e.f"), &crbtnode));
+
     // partial match
     EXPECT_EQ(RBTree<int>::PARTIALMATCH, rbtree.find(Name("m.b"), &crbtnode));
     EXPECT_EQ(Name("b"), crbtnode->getName());
+    EXPECT_EQ(RBTree<int>::PARTIALMATCH,
+              rbtree_expose_empty_node.find(Name("m.d.e.f"), &crbtnode));
 
     // find rbtnode
     EXPECT_EQ(RBTree<int>::EXACTMATCH, rbtree.find(Name("q.w.y.d.e.f"), &rbtnode));
     EXPECT_EQ(Name("q"), rbtnode->getName());
 }
 
+TEST_F(RBTreeTest, findError) {
+    // For the version that takes a node chain, the chain must be empty.
+    RBTreeNodeChain<int> chain;
+    EXPECT_EQ(RBTree<int>::EXACTMATCH, rbtree.find<void*>(Name("a"), &crbtnode,
+                                                          chain, NULL, NULL));
+    // trying to reuse the same chain.  it should result in an exception.
+    EXPECT_THROW(rbtree.find<void*>(Name("a"), &crbtnode, chain, NULL, NULL),
+                 BadValue);
+}
+
 bool
 testCallback(const RBNode<int>&, bool* callack_checker) {
     *callack_checker = true;
@@ -208,22 +227,121 @@ TEST_F(RBTreeTest, callback) {
     EXPECT_FALSE(parentrbtnode->isCallbackEnabled());
 
     // check if the callback is called from find()
+    RBTreeNodeChain<int> node_path1;
     bool callback_called = false;
     EXPECT_EQ(RBTree<int>::EXACTMATCH,
-              rbtree.find(Name("sub.callback.example"), &crbtnode,
+              rbtree.find(Name("sub.callback.example"), &crbtnode, node_path1,
                           testCallback, &callback_called));
     EXPECT_TRUE(callback_called);
 
     // enable callback at the parent node, but it doesn't have data so
     // the callback shouldn't be called.
+    RBTreeNodeChain<int> node_path2;
     parentrbtnode->enableCallback();
     callback_called = false;
     EXPECT_EQ(RBTree<int>::EXACTMATCH,
-              rbtree.find(Name("callback.example"), &crbtnode,
+              rbtree.find(Name("callback.example"), &crbtnode, node_path2,
                           testCallback, &callback_called));
     EXPECT_FALSE(callback_called);
 }
 
+TEST_F(RBTreeTest, chainLevel) {
+    RBTreeNodeChain<int> chain;
+
+    // by default there should be no level in the chain.
+    EXPECT_EQ(0, chain.getLevelCount());
+
+    // insert one node to the tree and find it.  there should be exactly
+    // one level in the chain.
+    RBTree<int> tree(true);
+    Name node_name(Name::ROOT_NAME());
+    EXPECT_EQ(RBTree<int>::SUCCESS, tree.insert(node_name, &rbtnode));
+    EXPECT_EQ(RBTree<int>::EXACTMATCH,
+              tree.find<void*>(node_name, &crbtnode, chain, NULL, NULL));
+    EXPECT_EQ(1, chain.getLevelCount());
+
+    /*
+     * Now creating a possibly deepest tree with MAX_LABELS - 1 levels.
+     * it should look like:
+     *            a
+     *           /|
+     *         (.)a
+     *            |
+     *            a
+     *            : (MAX_LABELS - 1) "a"'s
+     *
+     * then confirm that find() for the deepest name succeeds without any
+     * disruption, and the resulting chain has the expected level.
+     * Note that "a." and the root name (".") belong to the same level.
+     * So the possible maximum level is MAX_LABELS - 1, not MAX_LABELS.
+     */
+    for (unsigned int i = 1; i < Name::MAX_LABELS; ++i) {
+        node_name = Name("a.").concatenate(node_name);
+        EXPECT_EQ(RBTree<int>::SUCCESS, tree.insert(node_name, &rbtnode));
+        RBTreeNodeChain<int> found_chain;
+        EXPECT_EQ(RBTree<int>::EXACTMATCH,
+                  tree.find<void*>(node_name, &crbtnode, found_chain,
+                                   NULL, NULL));
+        EXPECT_EQ(i, found_chain.getLevelCount());
+    }
+
+    // Confirm the last inserted name has the possible maximum length with
+    // maximum label count.  This confirms the rbtree and chain level cannot
+    // be larger.
+    EXPECT_EQ(Name::MAX_LABELS, node_name.getLabelCount());
+    EXPECT_THROW(node_name.concatenate(Name("a.")), TooLongName);
+}
+
+TEST_F(RBTreeTest, getAbsoluteNameError) {
+    // an empty chain isn't allowed.
+    RBTreeNodeChain<int> chain;
+    EXPECT_THROW(chain.getAbsoluteName(), BadValue);
+}
+
+/*
+ *the domain order should be:
+ * a, b, c, d.e.f, x.d.e.f, w.y.d.e.f, o.w.y.d.e.f, p.w.y.d.e.f, q.w.y.d.e.f,
+ * z.d.e.f, j.z.d.e.f, g.h, i.g.h
+ *             b
+ *           /   \
+ *          a    d.e.f
+ *              /  |   \
+ *             c   |    g.h
+ *                 |     |
+ *                w.y    i
+ *              /  |  \
+ *             x   |   z
+ *                 |   |
+ *                 p   j
+ *               /   \
+ *              o     q
+ */
+TEST_F(RBTreeTest, nextNode) {
+    const char* const names[] = {
+        "a", "b", "c", "d.e.f", "x.d.e.f", "w.y.d.e.f", "o.w.y.d.e.f",
+        "p.w.y.d.e.f", "q.w.y.d.e.f", "z.d.e.f", "j.z.d.e.f", "g.h", "i.g.h"};
+    const int name_count = sizeof(names) / sizeof(names[0]);
+    RBTreeNodeChain<int> node_path;
+    const RBNode<int>* node = NULL;
+    EXPECT_EQ(RBTree<int>::EXACTMATCH,
+              rbtree.find<void*>(Name(names[0]), &node, node_path, NULL,
+                                 NULL));
+    for (int i = 0; i < name_count; ++i) {
+        EXPECT_NE(static_cast<void*>(NULL), node);
+        EXPECT_EQ(Name(names[i]), node_path.getAbsoluteName());
+        node = rbtree.nextNode(node_path);
+    }
+
+    // We should have reached the end of the tree.
+    EXPECT_EQ(static_cast<void*>(NULL), node);
+}
+
+TEST_F(RBTreeTest, nextNodeError) {
+    // Empty chain for nextNode() is invalid.
+    RBTreeNodeChain<int> chain;
+    EXPECT_THROW(rbtree.nextNode(chain), BadValue);
+}
+
 TEST_F(RBTreeTest, dumpTree) {
     std::ostringstream str;
     std::ostringstream str2;
@@ -260,5 +378,4 @@ TEST_F(RBTreeTest, swap) {
     tree2.dumpTree(out);
     ASSERT_EQ(str1.str(), out.str());
 }
-
 }
diff --git a/src/lib/datasrc/tests/run_unittests.cc b/src/lib/datasrc/tests/run_unittests.cc
index 20d5673..a35a646 100644
--- a/src/lib/datasrc/tests/run_unittests.cc
+++ b/src/lib/datasrc/tests/run_unittests.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <gtest/gtest.h>
 
 #include <dns/tests/unittest_util.h>
diff --git a/src/lib/datasrc/tests/sqlite3_unittest.cc b/src/lib/datasrc/tests/sqlite3_unittest.cc
index 653b417..ce9413d 100644
--- a/src/lib/datasrc/tests/sqlite3_unittest.cc
+++ b/src/lib/datasrc/tests/sqlite3_unittest.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <stdint.h>
 
 #include <algorithm>
diff --git a/src/lib/datasrc/tests/static_unittest.cc b/src/lib/datasrc/tests/static_unittest.cc
index 025a603..a11e889 100644
--- a/src/lib/datasrc/tests/static_unittest.cc
+++ b/src/lib/datasrc/tests/static_unittest.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <stdint.h>
 #include <string>
 #include <vector>
@@ -53,9 +51,10 @@ protected:
         // (defined as PACKAGE_STRING in config.h)
         version_data.push_back(PACKAGE_STRING);
 
-        // XXX: in addition, the order the following items matter.
+        // NOTE: in addition, the order of the following items matter.
         authors_data.push_back("Chen Zhengzhang");
         authors_data.push_back("Evan Hunt");
+        authors_data.push_back("Haidong Wang");
         authors_data.push_back("Han Feng");
         authors_data.push_back("Jelte Jansen");
         authors_data.push_back("Jeremy C. Reed"); 
@@ -68,6 +67,7 @@ protected:
         authors_data.push_back("Shane Kerr");
         authors_data.push_back("Shen Tingting");
         authors_data.push_back("Stephen Morris");
+        authors_data.push_back("Yoshitaka Aharen");
         authors_data.push_back("Zhang Likun");
 
         version_ns_data.push_back("version.bind.");
diff --git a/src/lib/datasrc/tests/test_datasrc.cc b/src/lib/datasrc/tests/test_datasrc.cc
index 6ed0266..8145539 100644
--- a/src/lib/datasrc/tests/test_datasrc.cc
+++ b/src/lib/datasrc/tests/test_datasrc.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <config.h>
 
 #include <cassert>
diff --git a/src/lib/datasrc/tests/test_datasrc.h b/src/lib/datasrc/tests/test_datasrc.h
index 8335ca1..d0d67fc 100644
--- a/src/lib/datasrc/tests/test_datasrc.h
+++ b/src/lib/datasrc/tests/test_datasrc.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __TEST_DATA_SOURCE_H
 #define __TEST_DATA_SOURCE_H
 
diff --git a/src/lib/datasrc/tests/testdata/mkbrokendb.c b/src/lib/datasrc/tests/testdata/mkbrokendb.c
index aff4cd4..58f055c 100644
--- a/src/lib/datasrc/tests/testdata/mkbrokendb.c
+++ b/src/lib/datasrc/tests/testdata/mkbrokendb.c
@@ -14,8 +14,6 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id$ */
-
 /*
  * This file is provided for reference purpose only.  A broken DB file, named
  * "brokendb.sqlite3" in this directory was created using this program.
diff --git a/src/lib/datasrc/zone.h b/src/lib/datasrc/zone.h
index 3e72af1..f70274f 100644
--- a/src/lib/datasrc/zone.h
+++ b/src/lib/datasrc/zone.h
@@ -17,6 +17,7 @@
 #define __ZONE_H 1
 
 #include <datasrc/result.h>
+#include <dns/rrsetlist.h>
 
 namespace isc {
 namespace datasrc {
@@ -155,10 +156,19 @@ public:
     /// - If there is a matching name but no RRset of the search type, it
     ///   returns the code of \c NXRRSET, and, if DNSSEC is required,
     ///   the NSEC RRset for that name.
-    /// - If there is a matching name with CNAME, it returns the code of
-    ///   \c CNAME and that CNAME RR.
+    /// - If there is a CNAME RR of the searched name but there is no
+    ///   RR of the searched type of the name (so this type is different from
+    ///   CNAME), it returns the code of \c CNAME and that CNAME RR.
+    ///   Note that if the searched RR type is CNAME, it is considered
+    ///   a successful match, and the code of \c SUCCESS will be returned.
     /// - If the search name matches a delegation point of DNAME, it returns
     ///   the code of \c DNAME and that DNAME RR.
+    /// - If the target isn't NULL, all RRsets under the domain are inserted
+    ///   there and SUCCESS (or NXDOMAIN, in case of empty domain) is returned
+    ///   instead of normall processing. This is intended to handle ANY query.
+    ///   \note: this behavior is controversial as we discussed in
+    ///   https://lists.isc.org/pipermail/bind10-dev/2011-January/001918.html
+    ///   We should revisit the interface before we heavily rely on it.
     ///
     /// The \c options parameter specifies customized behavior of the search.
     /// Their semantics is as follows:
@@ -174,19 +184,19 @@ public:
     /// A derived version of this method may involve internal resource
     /// allocation, especially for constructing the resulting RRset, and may
     /// throw an exception if it fails.
+    /// It throws DuplicateRRset exception if there are duplicate rrsets under
+    /// the same domain.
     /// It should not throw other types of exceptions.
     ///
-    /// Note: It's quite likely that we'll need to specify search options.
-    /// For example, we should be able to specify whether to allow returning
-    /// glue records at or under a zone cut.  We leave this interface open
-    /// at this moment.
-    ///
     /// \param name The domain name to be searched for.
     /// \param type The RR type to be searched for.
+    /// \param target If target is not NULL, insert all RRs under the domain
+    /// into it.
     /// \param options The search options.
     /// \return A \c FindResult object enclosing the search result (see above).
     virtual FindResult find(const isc::dns::Name& name,
                             const isc::dns::RRType& type,
+                            isc::dns::RRsetList* target = NULL,
                             const FindOptions options
                             = FIND_DEFAULT) const = 0;
     //@}
diff --git a/src/lib/dns/buffer.h b/src/lib/dns/buffer.h
index fe24488..e100876 100644
--- a/src/lib/dns/buffer.h
+++ b/src/lib/dns/buffer.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __BUFFER_H
 #define __BUFFER_H 1
 
diff --git a/src/lib/dns/dnssectime.cc b/src/lib/dns/dnssectime.cc
index 856130a..04643e2 100644
--- a/src/lib/dns/dnssectime.cc
+++ b/src/lib/dns/dnssectime.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <string>
 #include <iomanip>
 #include <iostream>
diff --git a/src/lib/dns/dnssectime.h b/src/lib/dns/dnssectime.h
index 6999640..5069650 100644
--- a/src/lib/dns/dnssectime.h
+++ b/src/lib/dns/dnssectime.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __DNSSECTIME_H
 #define __DNSSECTIME_H 1
 
diff --git a/src/lib/dns/edns.cc b/src/lib/dns/edns.cc
index e70bb47..6e25624 100644
--- a/src/lib/dns/edns.cc
+++ b/src/lib/dns/edns.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <config.h>
 
 #include <stdint.h>
diff --git a/src/lib/dns/edns.h b/src/lib/dns/edns.h
index 9ba9663..f9a5758 100644
--- a/src/lib/dns/edns.h
+++ b/src/lib/dns/edns.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __EDNS_H
 #define __EDNS_H 1
 
diff --git a/src/lib/dns/exceptions.cc b/src/lib/dns/exceptions.cc
index 61bbaa4..eb823b9 100644
--- a/src/lib/dns/exceptions.cc
+++ b/src/lib/dns/exceptions.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <dns/exceptions.h>
 #include <dns/rcode.h>
 
diff --git a/src/lib/dns/exceptions.h b/src/lib/dns/exceptions.h
index c540d05..bd696a5 100644
--- a/src/lib/dns/exceptions.h
+++ b/src/lib/dns/exceptions.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 // XXX: we have another exceptions.h, so we need to use a prefix "DNS_" in
 // the include guard.  More preferably, we should define a consistent naming
 // style for the header guide (e.g. module-name_file-name_H) throughout the
diff --git a/src/lib/dns/gen-rdatacode.py.in b/src/lib/dns/gen-rdatacode.py.in
index 6dc80b5..04e5c6a 100755
--- a/src/lib/dns/gen-rdatacode.py.in
+++ b/src/lib/dns/gen-rdatacode.py.in
@@ -14,8 +14,6 @@
 # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 # PERFORMANCE OF THIS SOFTWARE.
 
-# $Id$
-
 """\
 This is a supplemental script to (half) auto-generate DNS Rdata related
 classes and constants.
diff --git a/src/lib/dns/message.cc b/src/lib/dns/message.cc
index 0f5b1eb..e9ab1b9 100644
--- a/src/lib/dns/message.cc
+++ b/src/lib/dns/message.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <stdint.h>
 
 #include <algorithm>
@@ -311,6 +309,36 @@ Message::hasRRset(const Section section, const Name& name,
     return (false);
 }
 
+bool
+Message::hasRRset(const Section section, const RRsetPtr& rrset) {
+    return (hasRRset(section, rrset->getName(), rrset->getClass(), rrset->getType()));
+}
+
+bool
+Message::removeRRset(const Section section, RRsetIterator& iterator) {
+    if (section >= MessageImpl::NUM_SECTIONS) {
+        isc_throw(OutOfRange, "Invalid message section: " << section);
+    }
+
+    bool removed = false;
+    for (vector<RRsetPtr>::iterator i = impl_->rrsets_[section].begin();
+            i != impl_->rrsets_[section].end(); ++i) {
+        if (((*i)->getName() == (*iterator)->getName()) &&
+            ((*i)->getClass() == (*iterator)->getClass()) &&
+            ((*i)->getType() == (*iterator)->getType())) {
+
+            // Found the matching RRset so remove it & ignore rest
+            impl_->counts_[section] -= (*iterator)->getRdataCount();
+            impl_->rrsets_[section].erase(i);
+            removed = true;
+            break;
+        }
+    }
+
+    return (removed);
+}
+
+
 void
 Message::addQuestion(const QuestionPtr question) {
     if (impl_->mode_ != Message::RENDER) {
diff --git a/src/lib/dns/message.h b/src/lib/dns/message.h
index 3fa2c7b..de82085 100644
--- a/src/lib/dns/message.h
+++ b/src/lib/dns/message.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __MESSAGE_H
 #define __MESSAGE_H 1
 
@@ -462,9 +460,31 @@ public:
     bool hasRRset(const Section section, const Name& name,
                   const RRClass& rrclass, const RRType& rrtype);
 
+    /// \brief Determine whether the given section already has an RRset
+    /// matching the one pointed to by the argumet
+    ///
+    /// \c section must be a valid constant of the \c Section type;
+    /// otherwise, an exception of class \c OutOfRange will be thrown.
+    bool hasRRset(const Section section, const RRsetPtr& rrset);
+
+    /// \brief Remove RRSet from Message
+    ///
+    /// Removes the RRset identified by the section iterator from the message.
+    /// Note: if,.for some reason, the RRset is duplicated in the section, only
+    /// one occurrence is removed.
+    ///
+    /// If the operation is successful, all iterators into the section are
+    /// invalidated.
+    ///
+    /// \param section Section to which the iterator belongs
+    /// \param iterator Iterator pointing to the element to be removed
+    ///
+    /// \return true if the element was removed, false if the iterator was not
+    /// found in the specified section.
+    bool removeRRset(const Section section, RRsetIterator& iterator);
+
     // The following methods are not currently implemented.
     //void removeQuestion(QuestionPtr question);
-    //void removeRRset(const Section section, RRsetPtr rrset);
     // notyet:
     //void addRR(const Section section, const RR& rr);
     //void removeRR(const Section section, const RR& rr);
@@ -511,6 +531,9 @@ public:
     ///
     /// With EDNS the maximum size can be increased per message.
     static const uint16_t DEFAULT_MAX_UDPSIZE = 512;
+
+    /// \brief The default maximum size of UDP DNS messages we can handle
+    static const uint16_t DEFAULT_MAX_EDNS0_UDPSIZE = 4096;
     //@}
 
 private:
diff --git a/src/lib/dns/messagerenderer.cc b/src/lib/dns/messagerenderer.cc
index 1ff4c25..212411a 100644
--- a/src/lib/dns/messagerenderer.cc
+++ b/src/lib/dns/messagerenderer.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <cctype>
 #include <cassert>
 #include <set>
diff --git a/src/lib/dns/messagerenderer.h b/src/lib/dns/messagerenderer.h
index c2ba9b0..9a7f149 100644
--- a/src/lib/dns/messagerenderer.h
+++ b/src/lib/dns/messagerenderer.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __MESSAGERENDERER_H
 #define __MESSAGERENDERER_H 1
 
diff --git a/src/lib/dns/name.cc b/src/lib/dns/name.cc
index 725d607..8786bcf 100644
--- a/src/lib/dns/name.cc
+++ b/src/lib/dns/name.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <cctype>
 #include <cassert>
 #include <iterator>
diff --git a/src/lib/dns/name.h b/src/lib/dns/name.h
index 84ae246..dc1b5b3 100644
--- a/src/lib/dns/name.h
+++ b/src/lib/dns/name.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __NAME_H
 #define __NAME_H 1
 
diff --git a/src/lib/dns/opcode.cc b/src/lib/dns/opcode.cc
index 8141123..570dd81 100644
--- a/src/lib/dns/opcode.cc
+++ b/src/lib/dns/opcode.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <string>
 #include <ostream>
 
diff --git a/src/lib/dns/opcode.h b/src/lib/dns/opcode.h
index c5499c1..dd88062 100644
--- a/src/lib/dns/opcode.h
+++ b/src/lib/dns/opcode.h
@@ -14,8 +14,6 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id$ */
-
 #include <stdint.h>
 
 #include <ostream>
diff --git a/src/lib/dns/python/edns_python.cc b/src/lib/dns/python/edns_python.cc
index dac2fb8..e54dba0 100644
--- a/src/lib/dns/python/edns_python.cc
+++ b/src/lib/dns/python/edns_python.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <cassert>
 
 #include <dns/edns.h>
diff --git a/src/lib/dns/python/message_python.cc b/src/lib/dns/python/message_python.cc
index 13b2644..e19547e 100644
--- a/src/lib/dns/python/message_python.cc
+++ b/src/lib/dns/python/message_python.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <exceptions/exceptions.h>
 #include <dns/message.h>
 using namespace isc::dns;
diff --git a/src/lib/dns/python/messagerenderer_python.cc b/src/lib/dns/python/messagerenderer_python.cc
index 3d154bd..91ab0c5 100644
--- a/src/lib/dns/python/messagerenderer_python.cc
+++ b/src/lib/dns/python/messagerenderer_python.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <dns/messagerenderer.h>
 
 // For each class, we need a struct, a helper functions (init, destroy,
diff --git a/src/lib/dns/python/name_python.cc b/src/lib/dns/python/name_python.cc
index 47ea76f..3d7a196 100644
--- a/src/lib/dns/python/name_python.cc
+++ b/src/lib/dns/python/name_python.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 //
 // Declaration of the custom exceptions
 // Initialization and addition of these go in the module init at the
diff --git a/src/lib/dns/python/opcode_python.cc b/src/lib/dns/python/opcode_python.cc
index 3363dd1..0e2a30b 100644
--- a/src/lib/dns/python/opcode_python.cc
+++ b/src/lib/dns/python/opcode_python.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <dns/opcode.h>
 
 using namespace isc::dns;
diff --git a/src/lib/dns/python/pydnspp.cc b/src/lib/dns/python/pydnspp.cc
index 0c4dce5..7b41598 100644
--- a/src/lib/dns/python/pydnspp.cc
+++ b/src/lib/dns/python/pydnspp.cc
@@ -23,8 +23,6 @@
 //
 // And of course care has to be taken that all identifiers be unique
 
-// $Id$
-
 #define PY_SSIZE_T_CLEAN
 #include <Python.h>
 #include <structmember.h>
diff --git a/src/lib/dns/python/pydnspp_common.cc b/src/lib/dns/python/pydnspp_common.cc
index 1b10d6f..6c26367 100644
--- a/src/lib/dns/python/pydnspp_common.cc
+++ b/src/lib/dns/python/pydnspp_common.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <Python.h>
 #include <pydnspp_common.h>
 
diff --git a/src/lib/dns/python/pydnspp_common.h b/src/lib/dns/python/pydnspp_common.h
index 750e344..32e2b78 100644
--- a/src/lib/dns/python/pydnspp_common.h
+++ b/src/lib/dns/python/pydnspp_common.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __LIBDNS_PYTHON_COMMON_H
 #define __LIBDNS_PYTHON_COMMON_H 1
 
diff --git a/src/lib/dns/python/question_python.cc b/src/lib/dns/python/question_python.cc
index 378fc5d..a039284 100644
--- a/src/lib/dns/python/question_python.cc
+++ b/src/lib/dns/python/question_python.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id: question_python.cc 1711 2010-04-14 15:14:53Z jelte $
-
 #include <dns/question.h>
 using namespace isc::dns;
 
diff --git a/src/lib/dns/python/rcode_python.cc b/src/lib/dns/python/rcode_python.cc
index 6b2b948..fce8eef 100644
--- a/src/lib/dns/python/rcode_python.cc
+++ b/src/lib/dns/python/rcode_python.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <dns/rcode.h>
 
 using namespace isc::dns;
diff --git a/src/lib/dns/python/rdata_python.cc b/src/lib/dns/python/rdata_python.cc
index 3c9c45c..8579b7e 100644
--- a/src/lib/dns/python/rdata_python.cc
+++ b/src/lib/dns/python/rdata_python.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <dns/rdata.h>
 using namespace isc::dns;
 using namespace isc::dns::rdata;
diff --git a/src/lib/dns/python/rrclass_python.cc b/src/lib/dns/python/rrclass_python.cc
index 66f93f2..3cfed4c 100644
--- a/src/lib/dns/python/rrclass_python.cc
+++ b/src/lib/dns/python/rrclass_python.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <dns/rrclass.h>
 using namespace isc::dns;
 
diff --git a/src/lib/dns/python/rrset_python.cc b/src/lib/dns/python/rrset_python.cc
index 838e792..2292784 100644
--- a/src/lib/dns/python/rrset_python.cc
+++ b/src/lib/dns/python/rrset_python.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <dns/rrset.h>
 
 //
diff --git a/src/lib/dns/python/rrttl_python.cc b/src/lib/dns/python/rrttl_python.cc
index 6885ea5..e7391d0 100644
--- a/src/lib/dns/python/rrttl_python.cc
+++ b/src/lib/dns/python/rrttl_python.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <vector>
 
 #include <dns/rrttl.h>
diff --git a/src/lib/dns/python/rrtype_python.cc b/src/lib/dns/python/rrtype_python.cc
index 1c41a59..012f251 100644
--- a/src/lib/dns/python/rrtype_python.cc
+++ b/src/lib/dns/python/rrtype_python.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <vector>
 
 #include <dns/rrtype.h>
diff --git a/src/lib/dns/python/tests/edns_python_test.py b/src/lib/dns/python/tests/edns_python_test.py
index a68342c..3f03c90 100644
--- a/src/lib/dns/python/tests/edns_python_test.py
+++ b/src/lib/dns/python/tests/edns_python_test.py
@@ -13,8 +13,6 @@
 # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
-# $Id$
-
 import unittest
 import os
 from pydnspp import *
diff --git a/src/lib/dns/python/tests/testutil.py b/src/lib/dns/python/tests/testutil.py
index 38e9762..679f827 100644
--- a/src/lib/dns/python/tests/testutil.py
+++ b/src/lib/dns/python/tests/testutil.py
@@ -13,8 +13,6 @@
 # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
-# $Id$
-
 #
 # helper functions for tests taken from C++ unittests
 #
diff --git a/src/lib/dns/python/tests/tsigkey_python_test.py b/src/lib/dns/python/tests/tsigkey_python_test.py
index 90b919d..06e6868 100644
--- a/src/lib/dns/python/tests/tsigkey_python_test.py
+++ b/src/lib/dns/python/tests/tsigkey_python_test.py
@@ -13,8 +13,6 @@
 # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
-# $Id$
-
 import unittest
 from pydnspp import *
 
diff --git a/src/lib/dns/python/tsigkey_python.cc b/src/lib/dns/python/tsigkey_python.cc
index 5e58b34..aa87909 100644
--- a/src/lib/dns/python/tsigkey_python.cc
+++ b/src/lib/dns/python/tsigkey_python.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <new>
 
 #include <dns/tsigkey.h>
diff --git a/src/lib/dns/question.cc b/src/lib/dns/question.cc
index 54a4262..3e14c78 100644
--- a/src/lib/dns/question.cc
+++ b/src/lib/dns/question.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <iostream>
 #include <string>
 
diff --git a/src/lib/dns/question.h b/src/lib/dns/question.h
index 3d72173..851de2c 100644
--- a/src/lib/dns/question.h
+++ b/src/lib/dns/question.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __QUESTION_H
 #define __QUESTION_H 1
 
@@ -230,10 +228,10 @@ public:
     //@}
 
     ///
-    /// \name Comparison Operator
+    /// \name Comparison Operators
     ///
     //@{
-    /// A comparison operator is needed for this class so it can
+    /// A "less than" operator is needed for this class so it can
     /// function as an index to std::map.
     bool operator <(const Question& rhs) const {
         return (rrclass_ < rhs.rrclass_ ||
@@ -241,6 +239,26 @@ public:
                  (rrtype_ < rhs.rrtype_ ||
                   (rrtype_ == rhs.rrtype_ && (name_ < rhs.name_)))));
     }
+
+    /// Equality operator.  Primarily used to compare the question section in
+    /// a response to that in the query.
+    ///
+    /// \param rhs Question to compare against
+    /// \return true if name, class and type are equal, false otherwise
+    bool operator==(const Question& rhs) const {
+        return ((rrclass_ == rhs.rrclass_) && (rrtype_ == rhs.rrtype_) &&
+                (name_ == rhs.name_));
+    }
+
+    /// Inequality operator.  Primarily used to compare the question section in
+    /// a response to that in the query.
+    ///
+    /// \param rhs Question to compare against
+    /// \return true if one or more of the name, class and type do not match,
+    /// false otherwise.
+    bool operator!=(const Question& rhs) const {
+        return (!operator==(rhs));
+    }
     //@}
 
 private:
diff --git a/src/lib/dns/rcode.cc b/src/lib/dns/rcode.cc
index c02ab6a..ee41b1e 100644
--- a/src/lib/dns/rcode.cc
+++ b/src/lib/dns/rcode.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <string>
 #include <sstream>
 #include <ostream>
diff --git a/src/lib/dns/rcode.h b/src/lib/dns/rcode.h
index 8b88b1b..0c63285 100644
--- a/src/lib/dns/rcode.h
+++ b/src/lib/dns/rcode.h
@@ -14,8 +14,6 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id$ */
-
 #include <stdint.h>
 
 #include <ostream>
diff --git a/src/lib/dns/rdata.cc b/src/lib/dns/rdata.cc
index 7f66fe3..17fee25 100644
--- a/src/lib/dns/rdata.cc
+++ b/src/lib/dns/rdata.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <algorithm>
 #include <cctype>
 #include <string>
diff --git a/src/lib/dns/rdata.h b/src/lib/dns/rdata.h
index 3f4aaf9..8eaedd3 100644
--- a/src/lib/dns/rdata.h
+++ b/src/lib/dns/rdata.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __RDATA_H
 #define __RDATA_H 1
 
diff --git a/src/lib/dns/rdata/any_255/tsig_250.cc b/src/lib/dns/rdata/any_255/tsig_250.cc
index ebb2667..e025ce4 100644
--- a/src/lib/dns/rdata/any_255/tsig_250.cc
+++ b/src/lib/dns/rdata/any_255/tsig_250.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <string>
 #include <sstream>
 #include <vector>
diff --git a/src/lib/dns/rdata/any_255/tsig_250.h b/src/lib/dns/rdata/any_255/tsig_250.h
index 5887695..ff24925 100644
--- a/src/lib/dns/rdata/any_255/tsig_250.h
+++ b/src/lib/dns/rdata/any_255/tsig_250.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 // BEGIN_HEADER_GUARD
 
 #include <stdint.h>
diff --git a/src/lib/dns/rdata/ch_3/a_1.cc b/src/lib/dns/rdata/ch_3/a_1.cc
index dda84fd..376cbdd 100644
--- a/src/lib/dns/rdata/ch_3/a_1.cc
+++ b/src/lib/dns/rdata/ch_3/a_1.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <string>
 
 #include <exceptions/exceptions.h>
diff --git a/src/lib/dns/rdata/ch_3/a_1.h b/src/lib/dns/rdata/ch_3/a_1.h
index 9e827bb..6d75952 100644
--- a/src/lib/dns/rdata/ch_3/a_1.h
+++ b/src/lib/dns/rdata/ch_3/a_1.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 // BEGIN_HEADER_GUARD
 
 #include <string>
diff --git a/src/lib/dns/rdata/generic/cname_5.cc b/src/lib/dns/rdata/generic/cname_5.cc
index d4d57c9..c7f97d0 100644
--- a/src/lib/dns/rdata/generic/cname_5.cc
+++ b/src/lib/dns/rdata/generic/cname_5.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <config.h>
 
 #include <string>
diff --git a/src/lib/dns/rdata/generic/cname_5.h b/src/lib/dns/rdata/generic/cname_5.h
index 9630cea..92dffad 100644
--- a/src/lib/dns/rdata/generic/cname_5.h
+++ b/src/lib/dns/rdata/generic/cname_5.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 // BEGIN_HEADER_GUARD
 
 #include <string>
diff --git a/src/lib/dns/rdata/generic/dname_39.cc b/src/lib/dns/rdata/generic/dname_39.cc
index 01e4d35..b88720e 100644
--- a/src/lib/dns/rdata/generic/dname_39.cc
+++ b/src/lib/dns/rdata/generic/dname_39.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <config.h>
 
 #include <string>
diff --git a/src/lib/dns/rdata/generic/dname_39.h b/src/lib/dns/rdata/generic/dname_39.h
index 7a12abd..5e12167 100644
--- a/src/lib/dns/rdata/generic/dname_39.h
+++ b/src/lib/dns/rdata/generic/dname_39.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 // BEGIN_HEADER_GUARD
 
 #include <string>
diff --git a/src/lib/dns/rdata/generic/dnskey_48.cc b/src/lib/dns/rdata/generic/dnskey_48.cc
index 5ec9b7d..16a748e 100644
--- a/src/lib/dns/rdata/generic/dnskey_48.cc
+++ b/src/lib/dns/rdata/generic/dnskey_48.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <iostream>
 #include <string>
 #include <sstream>
diff --git a/src/lib/dns/rdata/generic/dnskey_48.h b/src/lib/dns/rdata/generic/dnskey_48.h
index e676227..14fad9f 100644
--- a/src/lib/dns/rdata/generic/dnskey_48.h
+++ b/src/lib/dns/rdata/generic/dnskey_48.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <stdint.h>
 
 #include <string>
diff --git a/src/lib/dns/rdata/generic/ds_43.cc b/src/lib/dns/rdata/generic/ds_43.cc
index 8b69b46..cca19ae 100644
--- a/src/lib/dns/rdata/generic/ds_43.cc
+++ b/src/lib/dns/rdata/generic/ds_43.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <iostream>
 #include <string>
 #include <sstream>
diff --git a/src/lib/dns/rdata/generic/ds_43.h b/src/lib/dns/rdata/generic/ds_43.h
index e88c339..03b19a0 100644
--- a/src/lib/dns/rdata/generic/ds_43.h
+++ b/src/lib/dns/rdata/generic/ds_43.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <stdint.h>
 
 #include <string>
diff --git a/src/lib/dns/rdata/generic/mx_15.cc b/src/lib/dns/rdata/generic/mx_15.cc
index 1b87853..0ae2251 100644
--- a/src/lib/dns/rdata/generic/mx_15.cc
+++ b/src/lib/dns/rdata/generic/mx_15.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <config.h>
 
 #include <string>
diff --git a/src/lib/dns/rdata/generic/mx_15.h b/src/lib/dns/rdata/generic/mx_15.h
index 50c8c98..1381f18 100644
--- a/src/lib/dns/rdata/generic/mx_15.h
+++ b/src/lib/dns/rdata/generic/mx_15.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 // BEGIN_HEADER_GUARD
 
 #include <stdint.h>
diff --git a/src/lib/dns/rdata/generic/ns_2.cc b/src/lib/dns/rdata/generic/ns_2.cc
index 97d224b..0e56911 100644
--- a/src/lib/dns/rdata/generic/ns_2.cc
+++ b/src/lib/dns/rdata/generic/ns_2.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <config.h>
 
 #include <string>
diff --git a/src/lib/dns/rdata/generic/ns_2.h b/src/lib/dns/rdata/generic/ns_2.h
index 4e7d0b7..fa44f22 100644
--- a/src/lib/dns/rdata/generic/ns_2.h
+++ b/src/lib/dns/rdata/generic/ns_2.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id: rdata.h 545 2010-01-27 00:33:28Z jinmei $
-
 // BEGIN_HEADER_GUARD
 
 #include <string>
diff --git a/src/lib/dns/rdata/generic/nsec3_50.cc b/src/lib/dns/rdata/generic/nsec3_50.cc
index 369f80e..c20fda2 100644
--- a/src/lib/dns/rdata/generic/nsec3_50.cc
+++ b/src/lib/dns/rdata/generic/nsec3_50.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <iostream>
 #include <iomanip>
 #include <string>
diff --git a/src/lib/dns/rdata/generic/nsec3_50.h b/src/lib/dns/rdata/generic/nsec3_50.h
index eeec8bb..5532071 100644
--- a/src/lib/dns/rdata/generic/nsec3_50.h
+++ b/src/lib/dns/rdata/generic/nsec3_50.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id: nsec_47.h 991 2010-02-26 08:53:26Z jinmei $
-
 #include <stdint.h>
 
 #include <string>
diff --git a/src/lib/dns/rdata/generic/nsec3param_51.cc b/src/lib/dns/rdata/generic/nsec3param_51.cc
index ad38fca..639feed 100644
--- a/src/lib/dns/rdata/generic/nsec3param_51.cc
+++ b/src/lib/dns/rdata/generic/nsec3param_51.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <iostream>
 #include <string>
 #include <sstream>
diff --git a/src/lib/dns/rdata/generic/nsec3param_51.h b/src/lib/dns/rdata/generic/nsec3param_51.h
index cad236f..130c759 100644
--- a/src/lib/dns/rdata/generic/nsec3param_51.h
+++ b/src/lib/dns/rdata/generic/nsec3param_51.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <stdint.h>
 
 #include <string>
diff --git a/src/lib/dns/rdata/generic/nsec_47.cc b/src/lib/dns/rdata/generic/nsec_47.cc
index 0d66e82..0859edd 100644
--- a/src/lib/dns/rdata/generic/nsec_47.cc
+++ b/src/lib/dns/rdata/generic/nsec_47.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <iostream>
 #include <string>
 #include <sstream>
diff --git a/src/lib/dns/rdata/generic/nsec_47.h b/src/lib/dns/rdata/generic/nsec_47.h
index 0140bca..b86a25b 100644
--- a/src/lib/dns/rdata/generic/nsec_47.h
+++ b/src/lib/dns/rdata/generic/nsec_47.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <stdint.h>
 
 #include <string>
diff --git a/src/lib/dns/rdata/generic/opt_41.cc b/src/lib/dns/rdata/generic/opt_41.cc
index 33a6e4c..1aae810 100644
--- a/src/lib/dns/rdata/generic/opt_41.cc
+++ b/src/lib/dns/rdata/generic/opt_41.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <config.h>
 
 #include <string>
diff --git a/src/lib/dns/rdata/generic/opt_41.h b/src/lib/dns/rdata/generic/opt_41.h
index d62d870..0cb7043 100644
--- a/src/lib/dns/rdata/generic/opt_41.h
+++ b/src/lib/dns/rdata/generic/opt_41.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 // BEGIN_HEADER_GUARD
 
 #include <string>
diff --git a/src/lib/dns/rdata/generic/ptr_12.cc b/src/lib/dns/rdata/generic/ptr_12.cc
index 5d1abf4..dc656b8 100644
--- a/src/lib/dns/rdata/generic/ptr_12.cc
+++ b/src/lib/dns/rdata/generic/ptr_12.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <config.h>
 
 #include <string>
diff --git a/src/lib/dns/rdata/generic/ptr_12.h b/src/lib/dns/rdata/generic/ptr_12.h
index 8fc42db..5d2d048 100644
--- a/src/lib/dns/rdata/generic/ptr_12.h
+++ b/src/lib/dns/rdata/generic/ptr_12.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 // BEGIN_HEADER_GUARD
 
 #include <string>
diff --git a/src/lib/dns/rdata/generic/rrsig_46.cc b/src/lib/dns/rdata/generic/rrsig_46.cc
index 17f32e8..6e6c5fb 100644
--- a/src/lib/dns/rdata/generic/rrsig_46.cc
+++ b/src/lib/dns/rdata/generic/rrsig_46.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <string>
 #include <iomanip>
 #include <iostream>
diff --git a/src/lib/dns/rdata/generic/rrsig_46.h b/src/lib/dns/rdata/generic/rrsig_46.h
index 3cc254a..19acc40 100644
--- a/src/lib/dns/rdata/generic/rrsig_46.h
+++ b/src/lib/dns/rdata/generic/rrsig_46.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <stdint.h>
 
 #include <string>
diff --git a/src/lib/dns/rdata/generic/soa_6.cc b/src/lib/dns/rdata/generic/soa_6.cc
index 5519030..c4b87c6 100644
--- a/src/lib/dns/rdata/generic/soa_6.cc
+++ b/src/lib/dns/rdata/generic/soa_6.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <config.h>
 
 #include <string>
diff --git a/src/lib/dns/rdata/generic/soa_6.h b/src/lib/dns/rdata/generic/soa_6.h
index fc847ff..3f6185e 100644
--- a/src/lib/dns/rdata/generic/soa_6.h
+++ b/src/lib/dns/rdata/generic/soa_6.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 // BEGIN_HEADER_GUARD
 
 #include <string>
diff --git a/src/lib/dns/rdata/generic/txt_16.cc b/src/lib/dns/rdata/generic/txt_16.cc
index 69ecb20..0e20f4e 100644
--- a/src/lib/dns/rdata/generic/txt_16.cc
+++ b/src/lib/dns/rdata/generic/txt_16.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <stdint.h>
 #include <string.h>
 
diff --git a/src/lib/dns/rdata/generic/txt_16.h b/src/lib/dns/rdata/generic/txt_16.h
index 33a1ee2..b4c791f 100644
--- a/src/lib/dns/rdata/generic/txt_16.h
+++ b/src/lib/dns/rdata/generic/txt_16.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 // BEGIN_HEADER_GUARD
 
 #include <stdint.h>
diff --git a/src/lib/dns/rdata/hs_4/a_1.cc b/src/lib/dns/rdata/hs_4/a_1.cc
index dda84fd..376cbdd 100644
--- a/src/lib/dns/rdata/hs_4/a_1.cc
+++ b/src/lib/dns/rdata/hs_4/a_1.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <string>
 
 #include <exceptions/exceptions.h>
diff --git a/src/lib/dns/rdata/hs_4/a_1.h b/src/lib/dns/rdata/hs_4/a_1.h
index 9e827bb..6d75952 100644
--- a/src/lib/dns/rdata/hs_4/a_1.h
+++ b/src/lib/dns/rdata/hs_4/a_1.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 // BEGIN_HEADER_GUARD
 
 #include <string>
diff --git a/src/lib/dns/rdata/in_1/a_1.cc b/src/lib/dns/rdata/in_1/a_1.cc
index f9d3599..ddd03f8 100644
--- a/src/lib/dns/rdata/in_1/a_1.cc
+++ b/src/lib/dns/rdata/in_1/a_1.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id: rdata.cc 545 2010-01-27 00:33:28Z jinmei $
-
 #include <stdint.h>
 #include <string.h>
 
diff --git a/src/lib/dns/rdata/in_1/a_1.h b/src/lib/dns/rdata/in_1/a_1.h
index d684267..6e98f0d 100644
--- a/src/lib/dns/rdata/in_1/a_1.h
+++ b/src/lib/dns/rdata/in_1/a_1.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id: rdata.h 545 2010-01-27 00:33:28Z jinmei $
-
 // BEGIN_HEADER_GUARD
 
 #include <string>
diff --git a/src/lib/dns/rdata/in_1/aaaa_28.cc b/src/lib/dns/rdata/in_1/aaaa_28.cc
index f9bf54b..45c4682 100644
--- a/src/lib/dns/rdata/in_1/aaaa_28.cc
+++ b/src/lib/dns/rdata/in_1/aaaa_28.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <stdint.h>
 #include <string.h>
 
diff --git a/src/lib/dns/rdata/in_1/aaaa_28.h b/src/lib/dns/rdata/in_1/aaaa_28.h
index cf4f450..3093017 100644
--- a/src/lib/dns/rdata/in_1/aaaa_28.h
+++ b/src/lib/dns/rdata/in_1/aaaa_28.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 // BEGIN_HEADER_GUARD
 
 #include <stdint.h>
diff --git a/src/lib/dns/rdata/template.cc b/src/lib/dns/rdata/template.cc
index f7eb08b..0e0bf46 100644
--- a/src/lib/dns/rdata/template.cc
+++ b/src/lib/dns/rdata/template.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <string>
 
 #include <dns/buffer.h>
diff --git a/src/lib/dns/rdata/template.h b/src/lib/dns/rdata/template.h
index 9541af2..e85a839 100644
--- a/src/lib/dns/rdata/template.h
+++ b/src/lib/dns/rdata/template.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 // BEGIN_HEADER_GUARD
 
 #include <string>
diff --git a/src/lib/dns/rrclass-placeholder.h b/src/lib/dns/rrclass-placeholder.h
index 9d0c201..ce9a141 100644
--- a/src/lib/dns/rrclass-placeholder.h
+++ b/src/lib/dns/rrclass-placeholder.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id: rrclass.h 530 2010-01-26 22:15:42Z jinmei $
-
 #ifndef __RRCLASS_H
 #define __RRCLASS_H 1
 
diff --git a/src/lib/dns/rrclass.cc b/src/lib/dns/rrclass.cc
index 27e82cf..04ff59c 100644
--- a/src/lib/dns/rrclass.cc
+++ b/src/lib/dns/rrclass.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <stdint.h>
 
 #include <string>
diff --git a/src/lib/dns/rrparamregistry-placeholder.cc b/src/lib/dns/rrparamregistry-placeholder.cc
index 37a676e..19363a3 100644
--- a/src/lib/dns/rrparamregistry-placeholder.cc
+++ b/src/lib/dns/rrparamregistry-placeholder.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <cassert>
 #include <algorithm>
 #include <cctype>
diff --git a/src/lib/dns/rrparamregistry.h b/src/lib/dns/rrparamregistry.h
index 408738a..a856423 100644
--- a/src/lib/dns/rrparamregistry.h
+++ b/src/lib/dns/rrparamregistry.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __RRPARAMREGISTRY_H
 #define __RRPARAMREGISTRY_H 1
 
diff --git a/src/lib/dns/rrset.cc b/src/lib/dns/rrset.cc
index fbdffc0..b931bec 100644
--- a/src/lib/dns/rrset.cc
+++ b/src/lib/dns/rrset.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <algorithm>
 #include <string>
 #include <vector>
diff --git a/src/lib/dns/rrset.h b/src/lib/dns/rrset.h
index 2f76945..4fc6cdc 100644
--- a/src/lib/dns/rrset.h
+++ b/src/lib/dns/rrset.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __RRSET_H
 #define __RRSET_H 1
 
diff --git a/src/lib/dns/rrsetlist.cc b/src/lib/dns/rrsetlist.cc
index 8c2637c..fcdcfbb 100644
--- a/src/lib/dns/rrsetlist.cc
+++ b/src/lib/dns/rrsetlist.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <vector>
 
 #include <boost/foreach.hpp>
diff --git a/src/lib/dns/rrsetlist.h b/src/lib/dns/rrsetlist.h
index a07f0fc..0e05b5b 100644
--- a/src/lib/dns/rrsetlist.h
+++ b/src/lib/dns/rrsetlist.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __RRSETLIST_H
 #define __RRSETLIST_H 1
 
@@ -75,6 +73,27 @@ private:
     T it_;
 };
 
+/// A set of RRsets.
+///
+/// \note Do not use this class unless you really understand what
+/// you're doing and you're 100% sure that this class is the best choice
+/// for your purpose.
+///
+/// Counter intuitively, this class is not a "list" of RRsets but a
+/// "set" of them; it doesn't allow multiple RRsets of the same RR
+/// type and RR class to be added at the same time.  And, for that
+/// reason, adding an RRset is more expensive than you'd expect.  The
+/// class name is confusing, but was named so as a result of
+/// compromise: "RRsetset" would look awkward; RRsets would be
+/// confusing (with RRset).
+///
+/// In any case, if you want a list like container of RRsets, your best choice
+/// would be \c std::vector<RRset> or \c std::list<RRset>, not this class.
+/// In fact, in many cases \c RRsetList will be a suboptimal choice.
+/// This class is defined publicly as part of libdns++ for a historical
+/// reason and is actually quite specific to a particular need for libdatasrc.
+/// If you are tempted to use it, think twice to assess if this class
+/// is really what you want.  Again, in many cases the answer will be no.
 class RRsetList {
 private:
     RRsetList(const RRsetList& source);
diff --git a/src/lib/dns/rrttl.cc b/src/lib/dns/rrttl.cc
index 2e57f61..78bb355 100644
--- a/src/lib/dns/rrttl.cc
+++ b/src/lib/dns/rrttl.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <stdint.h>
 
 #include <sstream>
diff --git a/src/lib/dns/rrttl.h b/src/lib/dns/rrttl.h
index a3c07ea..c80030e 100644
--- a/src/lib/dns/rrttl.h
+++ b/src/lib/dns/rrttl.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __RRTTL_H
 #define __RRTTL_H 1
 
diff --git a/src/lib/dns/rrtype-placeholder.h b/src/lib/dns/rrtype-placeholder.h
index 165891f..76cb29d 100644
--- a/src/lib/dns/rrtype-placeholder.h
+++ b/src/lib/dns/rrtype-placeholder.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id: rrtype.h 534 2010-01-26 23:08:23Z jinmei $
-
 #ifndef __RRTYPE_H
 #define __RRTYPE_H 1
 
diff --git a/src/lib/dns/rrtype.cc b/src/lib/dns/rrtype.cc
index 659674d..44377f5 100644
--- a/src/lib/dns/rrtype.cc
+++ b/src/lib/dns/rrtype.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <stdint.h>
 
 #include <string>
diff --git a/src/lib/dns/tests/base32hex_unittest.cc b/src/lib/dns/tests/base32hex_unittest.cc
index b106533..253d310 100644
--- a/src/lib/dns/tests/base32hex_unittest.cc
+++ b/src/lib/dns/tests/base32hex_unittest.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <stdint.h>
 
 #include <cctype>
diff --git a/src/lib/dns/tests/base64_unittest.cc b/src/lib/dns/tests/base64_unittest.cc
index d3d5491..7333793 100644
--- a/src/lib/dns/tests/base64_unittest.cc
+++ b/src/lib/dns/tests/base64_unittest.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <string>
 #include <utility>
 #include <vector>
diff --git a/src/lib/dns/tests/buffer_unittest.cc b/src/lib/dns/tests/buffer_unittest.cc
index 1459ac9..2ac9fc5 100644
--- a/src/lib/dns/tests/buffer_unittest.cc
+++ b/src/lib/dns/tests/buffer_unittest.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <exceptions/exceptions.h>
 
 #include <dns/buffer.h>
diff --git a/src/lib/dns/tests/dnssectime_unittest.cc b/src/lib/dns/tests/dnssectime_unittest.cc
index 68b4f85..2479a29 100644
--- a/src/lib/dns/tests/dnssectime_unittest.cc
+++ b/src/lib/dns/tests/dnssectime_unittest.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <string>
 
 #include <time.h>
diff --git a/src/lib/dns/tests/edns_unittest.cc b/src/lib/dns/tests/edns_unittest.cc
index 2663e24..fb9a7c2 100644
--- a/src/lib/dns/tests/edns_unittest.cc
+++ b/src/lib/dns/tests/edns_unittest.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <sstream>
 
 #include <exceptions/exceptions.h>
diff --git a/src/lib/dns/tests/hex_unittest.cc b/src/lib/dns/tests/hex_unittest.cc
index 3237940..6f82b17 100644
--- a/src/lib/dns/tests/hex_unittest.cc
+++ b/src/lib/dns/tests/hex_unittest.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id: rrtype_unittest.cc 476 2010-01-19 00:29:28Z jinmei $
-
 #include <stdint.h>
 
 #include <vector>
diff --git a/src/lib/dns/tests/message_unittest.cc b/src/lib/dns/tests/message_unittest.cc
index d577d66..ee1375a 100644
--- a/src/lib/dns/tests/message_unittest.cc
+++ b/src/lib/dns/tests/message_unittest.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <exceptions/exceptions.h>
 
 #include <dns/buffer.h>
@@ -252,6 +250,51 @@ TEST_F(MessageTest, hasRRset) {
     EXPECT_THROW(message_render.hasRRset(bogus_section, test_name,
                                          RRClass::IN(), RRType::A()),
                  OutOfRange);
+
+    // Repeat the checks having created an RRset of the appropriate type.
+
+    RRsetPtr rrs1(new RRset(test_name, RRClass::IN(), RRType::A(), RRTTL(60)));
+    EXPECT_TRUE(message_render.hasRRset(Message::SECTION_ANSWER, rrs1));
+    EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ADDITIONAL, rrs1));
+
+    RRsetPtr rrs2(new RRset(Name("nomatch.example"), RRClass::IN(), RRType::A(),
+        RRTTL(5)));
+    EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ANSWER, rrs2));
+
+    RRsetPtr rrs3(new RRset(test_name, RRClass::CH(), RRType::A(), RRTTL(60)));
+    EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ANSWER, rrs3));
+
+    RRsetPtr rrs4(new RRset(test_name, RRClass::IN(), RRType::AAAA(), RRTTL(5)));
+    EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ANSWER, rrs4));
+
+    RRsetPtr rrs5(new RRset(test_name, RRClass::IN(), RRType::AAAA(), RRTTL(5)));
+    EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ANSWER, rrs4));
+
+    EXPECT_THROW(message_render.hasRRset(bogus_section, rrs1), OutOfRange);
+}
+
+TEST_F(MessageTest, removeRRset) {
+    message_render.addRRset(Message::SECTION_ANSWER, rrset_a);
+    message_render.addRRset(Message::SECTION_ANSWER, rrset_aaaa);
+    EXPECT_TRUE(message_render.hasRRset(Message::SECTION_ANSWER, test_name,
+        RRClass::IN(), RRType::A()));
+    EXPECT_TRUE(message_render.hasRRset(Message::SECTION_ANSWER, test_name,
+        RRClass::IN(), RRType::AAAA()));
+    EXPECT_EQ(3, message_render.getRRCount(Message::SECTION_ANSWER));
+
+    // Locate the AAAA RRset and remove it; this has one RR in it.
+    RRsetIterator i = message_render.beginSection(Message::SECTION_ANSWER);
+    if ((*i)->getType() == RRType::A()) {
+        ++i;
+    }
+    EXPECT_EQ(RRType::AAAA(), (*i)->getType());
+    message_render.removeRRset(Message::SECTION_ANSWER, i);
+
+    EXPECT_TRUE(message_render.hasRRset(Message::SECTION_ANSWER, test_name,
+        RRClass::IN(), RRType::A()));
+    EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ANSWER, test_name,
+        RRClass::IN(), RRType::AAAA()));
+    EXPECT_EQ(2, message_render.getRRCount(Message::SECTION_ANSWER));
 }
 
 TEST_F(MessageTest, badBeginSection) {
diff --git a/src/lib/dns/tests/messagerenderer_unittest.cc b/src/lib/dns/tests/messagerenderer_unittest.cc
index 056304a..c3d3edb 100644
--- a/src/lib/dns/tests/messagerenderer_unittest.cc
+++ b/src/lib/dns/tests/messagerenderer_unittest.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <vector>
 
 #include <dns/buffer.h>
diff --git a/src/lib/dns/tests/name_unittest.cc b/src/lib/dns/tests/name_unittest.cc
index 3972f64..b79fd74 100644
--- a/src/lib/dns/tests/name_unittest.cc
+++ b/src/lib/dns/tests/name_unittest.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <vector>
 #include <string>
 #include <sstream>
diff --git a/src/lib/dns/tests/opcode_unittest.cc b/src/lib/dns/tests/opcode_unittest.cc
index 1cf8af1..a7db654 100644
--- a/src/lib/dns/tests/opcode_unittest.cc
+++ b/src/lib/dns/tests/opcode_unittest.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <vector>
 #include <sstream>
 
diff --git a/src/lib/dns/tests/question_unittest.cc b/src/lib/dns/tests/question_unittest.cc
index d2fb1f7..59a4815 100644
--- a/src/lib/dns/tests/question_unittest.cc
+++ b/src/lib/dns/tests/question_unittest.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <vector>
 #include <sstream>
 
@@ -141,6 +139,39 @@ TEST_F(QuestionTest, comparison) {
     EXPECT_FALSE(Question(a, ch, ns) < Question(a, ch, ns));
     EXPECT_FALSE(Question(b, in, ns) < Question(b, in, ns));
     EXPECT_FALSE(Question(b, in, aaaa) < Question(b, in, aaaa));
+
+    // Identical questions are equal
+
+    EXPECT_TRUE(Question(a, in, ns) == Question(a, in, ns));
+    EXPECT_FALSE(Question(a, in, ns) != Question(a, in, ns));
+
+    // Components differing by one component are unequal...
+
+    EXPECT_FALSE(Question(b, in, ns) == Question(a, in, ns));
+    EXPECT_TRUE(Question(b, in, ns) != Question(a, in, ns));
+
+    EXPECT_FALSE(Question(a, ch, ns) == Question(a, in, ns));
+    EXPECT_TRUE(Question(a, ch, ns) != Question(a, in, ns));
+
+    EXPECT_FALSE(Question(a, in, aaaa) == Question(a, in, ns));
+    EXPECT_TRUE(Question(a, in, aaaa) != Question(a, in, ns));
+
+    // ... as are those differing by two components
+
+    EXPECT_FALSE(Question(b, ch, ns) == Question(a, in, ns));
+    EXPECT_TRUE(Question(b, ch, ns) != Question(a, in, ns));
+
+    EXPECT_FALSE(Question(b, in, aaaa) == Question(a, in, ns));
+    EXPECT_TRUE(Question(b, in, aaaa) != Question(a, in, ns));
+
+    EXPECT_FALSE(Question(a, ch, aaaa) == Question(a, in, ns));
+    EXPECT_TRUE(Question(a, ch, aaaa) != Question(a, in, ns));
+
+    // ... and question differing by all three
+
+    EXPECT_FALSE(Question(b, ch, aaaa) == Question(a, in, ns));
+    EXPECT_TRUE(Question(b, ch, aaaa) != Question(a, in, ns));
+
 }
 
 }
diff --git a/src/lib/dns/tests/rcode_unittest.cc b/src/lib/dns/tests/rcode_unittest.cc
index 4ccb7b0..b8d7c73 100644
--- a/src/lib/dns/tests/rcode_unittest.cc
+++ b/src/lib/dns/tests/rcode_unittest.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <vector>
 #include <sstream>
 
diff --git a/src/lib/dns/tests/rdata_cname_unittest.cc b/src/lib/dns/tests/rdata_cname_unittest.cc
index 2e46cd7..e3137a7 100644
--- a/src/lib/dns/tests/rdata_cname_unittest.cc
+++ b/src/lib/dns/tests/rdata_cname_unittest.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <dns/buffer.h>
 #include <dns/exceptions.h>
 #include <dns/messagerenderer.h>
diff --git a/src/lib/dns/tests/rdata_dname_unittest.cc b/src/lib/dns/tests/rdata_dname_unittest.cc
index be2990f..c2384b6 100644
--- a/src/lib/dns/tests/rdata_dname_unittest.cc
+++ b/src/lib/dns/tests/rdata_dname_unittest.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <dns/buffer.h>
 #include <dns/exceptions.h>
 #include <dns/messagerenderer.h>
diff --git a/src/lib/dns/tests/rdata_dnskey_unittest.cc b/src/lib/dns/tests/rdata_dnskey_unittest.cc
index 38d51b9..e26bf57 100644
--- a/src/lib/dns/tests/rdata_dnskey_unittest.cc
+++ b/src/lib/dns/tests/rdata_dnskey_unittest.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <string>
 
 #include <exceptions/exceptions.h>
diff --git a/src/lib/dns/tests/rdata_ds_unittest.cc b/src/lib/dns/tests/rdata_ds_unittest.cc
index 87e05f1..d7e3f88 100644
--- a/src/lib/dns/tests/rdata_ds_unittest.cc
+++ b/src/lib/dns/tests/rdata_ds_unittest.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <string>
 
 #include <dns/buffer.h>
diff --git a/src/lib/dns/tests/rdata_in_a_unittest.cc b/src/lib/dns/tests/rdata_in_a_unittest.cc
index 8c0388a..7302881 100644
--- a/src/lib/dns/tests/rdata_in_a_unittest.cc
+++ b/src/lib/dns/tests/rdata_in_a_unittest.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <dns/buffer.h>
 #include <dns/exceptions.h>
 #include <dns/messagerenderer.h>
diff --git a/src/lib/dns/tests/rdata_in_aaaa_unittest.cc b/src/lib/dns/tests/rdata_in_aaaa_unittest.cc
index c1b46cf..c1953d6 100644
--- a/src/lib/dns/tests/rdata_in_aaaa_unittest.cc
+++ b/src/lib/dns/tests/rdata_in_aaaa_unittest.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <dns/buffer.h>
 #include <dns/exceptions.h>
 #include <dns/messagerenderer.h>
diff --git a/src/lib/dns/tests/rdata_mx_unittest.cc b/src/lib/dns/tests/rdata_mx_unittest.cc
index e385361..4491f86 100644
--- a/src/lib/dns/tests/rdata_mx_unittest.cc
+++ b/src/lib/dns/tests/rdata_mx_unittest.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <dns/buffer.h>
 #include <dns/messagerenderer.h>
 #include <dns/rdata.h>
diff --git a/src/lib/dns/tests/rdata_ns_unittest.cc b/src/lib/dns/tests/rdata_ns_unittest.cc
index 8aee40a..6d4a69e 100644
--- a/src/lib/dns/tests/rdata_ns_unittest.cc
+++ b/src/lib/dns/tests/rdata_ns_unittest.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <dns/buffer.h>
 #include <dns/exceptions.h>
 #include <dns/messagerenderer.h>
diff --git a/src/lib/dns/tests/rdata_nsec3_unittest.cc b/src/lib/dns/tests/rdata_nsec3_unittest.cc
index 3636640..6b3a0b5 100644
--- a/src/lib/dns/tests/rdata_nsec3_unittest.cc
+++ b/src/lib/dns/tests/rdata_nsec3_unittest.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <string>
 
 #include <exceptions/exceptions.h>
diff --git a/src/lib/dns/tests/rdata_nsec3param_unittest.cc b/src/lib/dns/tests/rdata_nsec3param_unittest.cc
index 777d6a3..53e9126 100644
--- a/src/lib/dns/tests/rdata_nsec3param_unittest.cc
+++ b/src/lib/dns/tests/rdata_nsec3param_unittest.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <string>
 
 #include <exceptions/exceptions.h>
diff --git a/src/lib/dns/tests/rdata_nsec_unittest.cc b/src/lib/dns/tests/rdata_nsec_unittest.cc
index 63e80ff..f9ad027 100644
--- a/src/lib/dns/tests/rdata_nsec_unittest.cc
+++ b/src/lib/dns/tests/rdata_nsec_unittest.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <string>
 
 #include <dns/buffer.h>
diff --git a/src/lib/dns/tests/rdata_opt_unittest.cc b/src/lib/dns/tests/rdata_opt_unittest.cc
index d957ba8..be92b91 100644
--- a/src/lib/dns/tests/rdata_opt_unittest.cc
+++ b/src/lib/dns/tests/rdata_opt_unittest.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <dns/buffer.h>
 #include <dns/messagerenderer.h>
 #include <dns/rdata.h>
diff --git a/src/lib/dns/tests/rdata_ptr_unittest.cc b/src/lib/dns/tests/rdata_ptr_unittest.cc
index cb96b7f..da13dcb 100644
--- a/src/lib/dns/tests/rdata_ptr_unittest.cc
+++ b/src/lib/dns/tests/rdata_ptr_unittest.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <dns/buffer.h>
 #include <dns/exceptions.h>
 #include <dns/messagerenderer.h>
diff --git a/src/lib/dns/tests/rdata_rrsig_unittest.cc b/src/lib/dns/tests/rdata_rrsig_unittest.cc
index d691a88..04d9469 100644
--- a/src/lib/dns/tests/rdata_rrsig_unittest.cc
+++ b/src/lib/dns/tests/rdata_rrsig_unittest.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <exceptions/exceptions.h>
 
 #include <dns/buffer.h>
diff --git a/src/lib/dns/tests/rdata_soa_unittest.cc b/src/lib/dns/tests/rdata_soa_unittest.cc
index 999ecd1..6858a0b 100644
--- a/src/lib/dns/tests/rdata_soa_unittest.cc
+++ b/src/lib/dns/tests/rdata_soa_unittest.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <dns/buffer.h>
 #include <dns/messagerenderer.h>
 #include <dns/rdata.h>
diff --git a/src/lib/dns/tests/rdata_tsig_unittest.cc b/src/lib/dns/tests/rdata_tsig_unittest.cc
index 7a8f695..5c9a14f 100644
--- a/src/lib/dns/tests/rdata_tsig_unittest.cc
+++ b/src/lib/dns/tests/rdata_tsig_unittest.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <string>
 
 #include <exceptions/exceptions.h>
diff --git a/src/lib/dns/tests/rdata_txt_unittest.cc b/src/lib/dns/tests/rdata_txt_unittest.cc
index 3271d88..54993e1 100644
--- a/src/lib/dns/tests/rdata_txt_unittest.cc
+++ b/src/lib/dns/tests/rdata_txt_unittest.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <dns/buffer.h>
 #include <dns/exceptions.h>
 #include <dns/messagerenderer.h>
diff --git a/src/lib/dns/tests/rdata_unittest.cc b/src/lib/dns/tests/rdata_unittest.cc
index 81dfe10..5ce4c03 100644
--- a/src/lib/dns/tests/rdata_unittest.cc
+++ b/src/lib/dns/tests/rdata_unittest.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <vector>
 #include <string>
 #include <sstream>
diff --git a/src/lib/dns/tests/rdata_unittest.h b/src/lib/dns/tests/rdata_unittest.h
index 40b1eb2..748c8d3 100644
--- a/src/lib/dns/tests/rdata_unittest.h
+++ b/src/lib/dns/tests/rdata_unittest.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __RDATA_UNITTEST_H
 #define __RDATA_UNITTEST_H 1
 
diff --git a/src/lib/dns/tests/rrclass_unittest.cc b/src/lib/dns/tests/rrclass_unittest.cc
index 871909a..4eeb1e0 100644
--- a/src/lib/dns/tests/rrclass_unittest.cc
+++ b/src/lib/dns/tests/rrclass_unittest.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <gtest/gtest.h>
 
 #include <dns/buffer.h>
diff --git a/src/lib/dns/tests/rrparamregistry_unittest.cc b/src/lib/dns/tests/rrparamregistry_unittest.cc
index ac57ae7..a75eed5 100644
--- a/src/lib/dns/tests/rrparamregistry_unittest.cc
+++ b/src/lib/dns/tests/rrparamregistry_unittest.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <string>
 #include <sstream>
 
diff --git a/src/lib/dns/tests/rrset_unittest.cc b/src/lib/dns/tests/rrset_unittest.cc
index 1149480..c704cc8 100644
--- a/src/lib/dns/tests/rrset_unittest.cc
+++ b/src/lib/dns/tests/rrset_unittest.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <stdexcept>
 
 #include <dns/buffer.h>
diff --git a/src/lib/dns/tests/rrsetlist_unittest.cc b/src/lib/dns/tests/rrsetlist_unittest.cc
index 90d4782..080f888 100644
--- a/src/lib/dns/tests/rrsetlist_unittest.cc
+++ b/src/lib/dns/tests/rrsetlist_unittest.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <vector>
 #include <boost/foreach.hpp>
 
diff --git a/src/lib/dns/tests/rrttl_unittest.cc b/src/lib/dns/tests/rrttl_unittest.cc
index c359ce3..b8f5ac2 100644
--- a/src/lib/dns/tests/rrttl_unittest.cc
+++ b/src/lib/dns/tests/rrttl_unittest.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <gtest/gtest.h>
 
 #include <dns/buffer.h>
diff --git a/src/lib/dns/tests/rrtype_unittest.cc b/src/lib/dns/tests/rrtype_unittest.cc
index 25aa1d7..6da7381 100644
--- a/src/lib/dns/tests/rrtype_unittest.cc
+++ b/src/lib/dns/tests/rrtype_unittest.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <gtest/gtest.h>
 
 #include <dns/buffer.h>
diff --git a/src/lib/dns/tests/run_unittests.cc b/src/lib/dns/tests/run_unittests.cc
index 1a6f8ee..3cdc61d 100644
--- a/src/lib/dns/tests/run_unittests.cc
+++ b/src/lib/dns/tests/run_unittests.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <gtest/gtest.h>
 
 #include <dns/tests/unittest_util.h>
diff --git a/src/lib/dns/tests/sha1_unittest.cc b/src/lib/dns/tests/sha1_unittest.cc
index 658be0d..79bc37d 100644
--- a/src/lib/dns/tests/sha1_unittest.cc
+++ b/src/lib/dns/tests/sha1_unittest.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <stdint.h>
 #include <string>
 
diff --git a/src/lib/dns/tests/unittest_util.cc b/src/lib/dns/tests/unittest_util.cc
index 14ace94..c9c0982 100644
--- a/src/lib/dns/tests/unittest_util.cc
+++ b/src/lib/dns/tests/unittest_util.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <config.h>
 
 #include <iostream>
diff --git a/src/lib/dns/tests/unittest_util.h b/src/lib/dns/tests/unittest_util.h
index 899f28d..f85a921 100644
--- a/src/lib/dns/tests/unittest_util.h
+++ b/src/lib/dns/tests/unittest_util.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __UNITTEST_UTIL_H
 #define __UNITTEST_UTIL_H 1
 
diff --git a/src/lib/dns/tsigkey.cc b/src/lib/dns/tsigkey.cc
index e324c29..057191d 100644
--- a/src/lib/dns/tsigkey.cc
+++ b/src/lib/dns/tsigkey.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <map>
 #include <utility>
 #include <vector>
diff --git a/src/lib/dns/tsigkey.h b/src/lib/dns/tsigkey.h
index c277b19..e56fa88 100644
--- a/src/lib/dns/tsigkey.h
+++ b/src/lib/dns/tsigkey.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __TSIGKEY_H
 #define __TSIGKEY_H 1
 
diff --git a/src/lib/dns/util/base32hex.h b/src/lib/dns/util/base32hex.h
index 95df331..cba172e 100644
--- a/src/lib/dns/util/base32hex.h
+++ b/src/lib/dns/util/base32hex.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __BASE32HEX_H
 #define __BASE32HEX_H 1
 
diff --git a/src/lib/dns/util/base64.h b/src/lib/dns/util/base64.h
index ab2326e..46e10a6 100644
--- a/src/lib/dns/util/base64.h
+++ b/src/lib/dns/util/base64.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __BASE64_H
 #define __BASE64_H 1
 
diff --git a/src/lib/dns/util/base_n.cc b/src/lib/dns/util/base_n.cc
index 2f799c3..9d0c777 100644
--- a/src/lib/dns/util/base_n.cc
+++ b/src/lib/dns/util/base_n.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <stdint.h>
 #include <cassert>
 #include <iterator>
diff --git a/src/lib/dns/util/hex.h b/src/lib/dns/util/hex.h
index d3b3acc..e2626bf 100644
--- a/src/lib/dns/util/hex.h
+++ b/src/lib/dns/util/hex.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __HEX_H
 #define __HEX_H 1
 
diff --git a/src/lib/exceptions/exceptions.cc b/src/lib/exceptions/exceptions.cc
index 10ddad6..2a374da 100644
--- a/src/lib/exceptions/exceptions.cc
+++ b/src/lib/exceptions/exceptions.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <string>
 
 #include <exceptions/exceptions.h>
diff --git a/src/lib/exceptions/exceptions.h b/src/lib/exceptions/exceptions.h
index 4d7a9e9..a42037b 100644
--- a/src/lib/exceptions/exceptions.h
+++ b/src/lib/exceptions/exceptions.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __EXCEPTIONS_H
 #define __EXCEPTIONS_H 1
 
diff --git a/src/lib/exceptions/tests/exceptions_unittest.cc b/src/lib/exceptions/tests/exceptions_unittest.cc
index 7d5990c..44cbc17 100644
--- a/src/lib/exceptions/tests/exceptions_unittest.cc
+++ b/src/lib/exceptions/tests/exceptions_unittest.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <stdexcept>
 #include <string>
 
diff --git a/src/lib/exceptions/tests/run_unittests.cc b/src/lib/exceptions/tests/run_unittests.cc
index 863ac68..0908071 100644
--- a/src/lib/exceptions/tests/run_unittests.cc
+++ b/src/lib/exceptions/tests/run_unittests.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id: run_unittests.cc 476 2010-01-19 00:29:28Z jinmei $
-
 #include <gtest/gtest.h>
 
 int
diff --git a/src/lib/log/Makefile.am b/src/lib/log/Makefile.am
index 4afb7be..900f11b 100644
--- a/src/lib/log/Makefile.am
+++ b/src/lib/log/Makefile.am
@@ -1,4 +1,43 @@
-AM_CXXFLAGS = $(B10_CXXFLAGS)
+if USE_LOG4CXX
+SUBDIRS = . compiler tests
+endif
+
+AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CPPFLAGS += $(LOG4CXX_INCLUDES)
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/log -I$(top_builddir)/src/lib/log
+
+CLEANFILES = *.gcno *.gcda
 
 lib_LTLIBRARIES = liblog.la
-liblog_la_SOURCES = dummylog.cc dummylog.h
+liblog_la_SOURCES  =
+liblog_la_SOURCES += dbglevels.h
+liblog_la_SOURCES += dummylog.h dummylog.cc
+if USE_LOG4CXX
+liblog_la_SOURCES += filename.h filename.cc
+liblog_la_SOURCES += logger.cc logger.h
+liblog_la_SOURCES += logger_support.cc logger_support.h
+liblog_la_SOURCES += messagedef.cc messagedef.h
+liblog_la_SOURCES += message_dictionary.cc message_dictionary.h
+liblog_la_SOURCES += message_exception.h message_exception.cc
+liblog_la_SOURCES += message_initializer.cc message_initializer.h
+liblog_la_SOURCES += message_reader.cc message_reader.h
+liblog_la_SOURCES += message_types.h
+liblog_la_SOURCES += root_logger_name.cc root_logger_name.h
+liblog_la_SOURCES += strutil.h strutil.cc
+liblog_la_SOURCES += xdebuglevel.cc xdebuglevel.h
+
+liblog_la_LDFLAGS = $(LOG4CXX_LDFLAGS)
+endif
+
+# Note: the ordering matters: -Wno-... must follow -Wextra (defined in
+# B10_CXXFLAGS)
+liblog_la_CXXFLAGS = $(AM_CXXFLAGS)
+if USE_GXX
+liblog_la_CXXFLAGS += -Wno-unused-parameter
+endif
+if USE_CLANGPP
+# Same for clang++, but we need to turn off -Werror completely.
+liblog_la_CXXFLAGS += -Wno-error
+endif
+liblog_la_CPPFLAGS = $(AM_CPPFLAGS)
diff --git a/src/lib/log/compiler/Makefile.am b/src/lib/log/compiler/Makefile.am
new file mode 100644
index 0000000..2475036
--- /dev/null
+++ b/src/lib/log/compiler/Makefile.am
@@ -0,0 +1,20 @@
+SUBDIRS = .
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/log -I$(top_builddir)/src/lib/log
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+
+CLEANFILES = *.gcno *.gcda
+
+pkglibexec_PROGRAMS = message
+message_SOURCES = message.cc
+message_LDADD  = $(top_builddir)/src/lib/log/liblog.la
+
diff --git a/src/lib/log/compiler/message.cc b/src/lib/log/compiler/message.cc
new file mode 100644
index 0000000..50e33a6
--- /dev/null
+++ b/src/lib/log/compiler/message.cc
@@ -0,0 +1,450 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <cctype>
+#include <fstream>
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <errno.h>
+#include <getopt.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <log/filename.h>
+#include <log/message_dictionary.h>
+#include <log/message_exception.h>
+#include <log/message_reader.h>
+#include <log/messagedef.h>
+#include <log/strutil.h>
+
+#include <log/logger.h>
+
+using namespace std;
+using namespace isc::log;
+
+static const char* VERSION = "1.0-0";
+
+/// \brief Message Compiler
+///
+/// \b Overview<BR>
+/// This is the program that takes as input a message file and produces:
+///
+/// \li A .h file containing message definition
+/// \li A .cc file containing code that adds the messages to the program's
+/// message disctionary at start-up time.
+///
+/// Alternatively, the program can produce a .py file that contains the
+/// message definitions.
+///
+
+/// \b Invocation<BR>
+/// The program is invoked with the command:
+///
+/// <tt>message [-p] \<message-file\></tt>
+///
+/// It reads the message file and writes out two files of the same name but with
+/// extensions of .h and .cc.
+///
+/// If \c -p is specified, the C++ files are not written; instead a Python file
+/// of the same name (but with the file extension .py) is written.
+
+
+/// \brief Print Version
+///
+/// Prints the program's version number.
+
+static void version() {
+    cout << VERSION << "\n";
+}
+
+/// \brief Print Usage
+///
+/// Prints program usage to stdout.
+
+static void usage() {
+    cout <<
+        "Usage: message [-h] [-p] [-v] <message-file>\n" <<
+        "\n" <<
+        "-h       Print this message and exit\n" <<
+        "-p       Output a Python module holding the message definitions.\n" <<
+        "         By default a C++ header file and implementation file are\n" <<
+
+
+        "         written.\n" <<
+        "-v       Print the program version and exit\n" <<
+        "\n" <<
+        "<message-file> is the name of the input message file.\n";
+}
+
+
+/// \brief Create Time
+///
+/// Returns the current time as a suitably-formatted string.
+///
+/// \return Current time
+
+static string currentTime() {
+
+    // Get the current time.
+    time_t curtime;
+    time(&curtime);
+
+    // Format it
+    char buffer[32];
+    ctime_r(&curtime, buffer);
+
+    // Convert to string and strip out the trailing newline
+    string current_time = buffer;
+    return isc::strutil::trim(current_time);
+}
+
+
+
+
+/// \brief Create Header Sentinel
+///
+/// Given the name of a file, create an #ifdef sentinel name.  The name is
+/// __<name>_<ext>, where <name> is the name of the file, and <ext> is the
+/// extension less the leading period.  The sentinel will be upper-case.
+///
+/// \param file Filename object representing the file.
+///
+/// \return Sentinel name
+
+static string sentinel(Filename& file) {
+
+    string name = file.name();
+    string ext = file.extension();
+    string sentinel_text = "__" + name + "_" + ext.substr(1);
+    isc::strutil::uppercase(sentinel_text);
+    return sentinel_text;
+}
+
+
+/// \brief Quote String
+///
+/// Inserts an escape character (a backslash) prior to any double quote
+/// characters.  This is used to handle the fact that the input file does not
+/// contain quotes, yet the string will be included in a C++ literal string.
+
+string quoteString(const string& instring) {
+
+    // Create the output string and reserve the space needed to hold the input
+    // string. (Most input strings will not contain quotes, so this single
+    // reservation should be all that is needed.) 
+    string outstring;
+    outstring.reserve(instring.size());
+
+    // Iterate through the input string, preceding quotes with a slash.
+    for (size_t i = 0; i < instring.size(); ++i) {
+        if (instring[i] == '"') {
+            outstring += '\\';
+        }
+        outstring += instring[i];
+    }
+
+    return outstring;
+}
+
+
+/// \brief Sorted Identifiers
+///
+/// Given a dictionary, return a vector holding the message IDs in sorted
+/// order.
+///
+/// \param dictionary Dictionary to examine
+///
+/// \return Sorted list of message IDs
+
+vector<MessageID> sortedIdentifiers(MessageDictionary* dictionary) {
+    vector<MessageID> ident;
+
+    for (MessageDictionary::const_iterator i = dictionary->begin();
+         i != dictionary->end(); ++i) {
+        ident.push_back(i->first);
+    }
+    sort(ident.begin(), ident.end());
+
+    return ident;
+}
+
+
+/// \brief Write Header File
+///
+/// Writes the C++ header file containing the symbol definitions.
+///
+/// \param file Name of the message file.  The header file is written to a
+/// file of the same name but with a .h suffix.
+/// \param prefix Prefix string to use in symbols
+/// \param dictionary Dictionary holding the message definitions.
+
+void writeHeaderFile(const string& file, const string& prefix,
+    MessageDictionary* dictionary)
+{
+    Filename message_file(file);
+    Filename header_file(message_file.useAsDefault(".h"));
+
+    // Text to use as the sentinels.
+    string sentinel_text = sentinel(header_file);
+
+    // Open the output file for writing
+    ofstream hfile(header_file.fullName().c_str());
+
+    try {
+        if (hfile.fail()) {
+            throw MessageException(MSG_OPENOUT, header_file.fullName(),
+                strerror(errno));
+        }
+
+        // Write the header preamble.  If there is an error, we'll pick it up
+        // after the last write.
+
+        hfile <<
+            "// File created from " << message_file.fullName() << " on " <<
+                currentTime() << "\n" <<
+             "\n" <<
+             "#ifndef " << sentinel_text << "\n" <<
+             "#define "  << sentinel_text << "\n" <<
+             "\n" <<
+             "#include <log/message_types.h>\n" <<
+             "\n" <<
+             "namespace {\n" <<
+             "\n";
+
+        vector<MessageID> idents = sortedIdentifiers(dictionary);
+        for (vector<MessageID>::const_iterator j = idents.begin();
+            j != idents.end(); ++j) {
+            hfile << "isc::log::MessageID " << prefix << *j <<
+                " = \"" << *j << "\";\n";
+        }
+
+        // ... and finally the postamble
+        hfile <<
+            "\n" <<
+            "} // Anonymous namespace\n" <<
+            "\n" <<
+            "#endif // " << sentinel_text << "\n";
+
+        // Report errors (if any) and exit
+        if (hfile.fail()) {
+            throw MessageException(MSG_WRITERR, header_file.fullName(),
+                strerror(errno));
+        }
+
+        hfile.close();
+    }
+    catch (MessageException&) {
+        hfile.close();
+        throw;
+    }
+}
+
+
+/// \brief Convert Non Alpha-Numeric Characters to Underscores
+///
+/// Simple function for use in a call to transform
+
+char replaceNonAlphaNum(char c) {
+    return (isalnum(c) ? c : '_');
+}
+
+
+/// \brief Write Program File
+///
+/// Writes the C++ source code file.  This defines an external objects whose
+/// constructor is run at initialization time.  The constructor adds the message
+/// definitions to the main global dictionary.
+
+void writeProgramFile(const string& file, MessageDictionary* dictionary)
+{
+    Filename message_file(file);
+    Filename program_file(message_file.useAsDefault(".cc"));
+
+    // Open the output file for writing
+    ofstream ccfile(program_file.fullName().c_str());
+    try {
+        if (ccfile.fail()) {
+            throw MessageException(MSG_OPENOUT, program_file.fullName(),
+                strerror(errno));
+        }
+
+        // Write the preamble.  If there is an error, we'll pick it up after
+        // the last write.
+
+        ccfile <<
+            "// File created from " << message_file.fullName() << " on " <<
+                currentTime() << "\n" <<
+             "\n" <<
+             "#include <cstddef>\n" <<
+             "#include <log/message_initializer.h>\n" <<
+             "\n" <<
+             "using namespace isc::log;\n" <<
+             "\n" <<
+             "namespace {\n" <<
+             "\n" <<
+             "const char* values[] = {\n";
+
+        // Output the identifiers and the associated text.
+        vector<MessageID> idents = sortedIdentifiers(dictionary);
+        for (vector<MessageID>::const_iterator i = idents.begin();
+            i != idents.end(); ++i) {
+                ccfile << "    \"" << *i << "\", \"" <<
+                    quoteString(dictionary->getText(*i)) << "\",\n";
+        }
+
+        // ... and the postamble
+        ccfile <<
+            "    NULL\n" <<
+            "};\n" <<
+            "\n" <<
+            "} // Anonymous namespace\n" <<
+            "\n";
+
+        // Now construct a unique name.  We don't put the message initializer as
+        // a static variable or in an anonymous namespace lest the C++
+        // compiler's optimizer decides it can optimise it away.
+        string unique_name = program_file.name() + program_file.extension() +
+            "_" + currentTime();
+        transform(unique_name.begin(), unique_name.end(), unique_name.begin(),
+            replaceNonAlphaNum);
+
+        // ... and write the initialization code
+        ccfile <<
+            "MessageInitializer " << unique_name << "(values);\n";
+
+        // Report errors (if any) and exit
+        if (ccfile.fail()) {
+            throw MessageException(MSG_WRITERR, program_file.fullName(),
+                strerror(errno));
+        }
+
+        ccfile.close();
+    }
+    catch (MessageException&) {
+        ccfile.close();
+        throw;
+    }
+}
+
+
+/// \brief Warn of Duplicate Entries
+///
+/// If the input file contained duplicate message IDs, only the first will be
+/// processed.  However, we should warn about it.
+///
+/// \param reader Message Reader used to read the file
+
+static void warnDuplicates(MessageReader& reader) {
+
+    // Get the duplicates (the overflow) and, if present, sort them into some
+    // order and remove those which occur more than once (which mean that they
+    // occur more than twice in the input file).
+    MessageReader::MessageIDCollection duplicates = reader.getNotAdded();
+    if (duplicates.size() > 0) {
+        cout << "Warning: the following duplicate IDs were found:\n";
+
+        sort(duplicates.begin(), duplicates.end());
+        MessageReader::MessageIDCollection::iterator new_end =
+            unique(duplicates.begin(), duplicates.end());
+        for (MessageReader::MessageIDCollection::iterator i = duplicates.begin();
+            i != new_end; ++i) {
+            cout << "    " << *i << "\n";
+        }
+    }
+}
+
+
+/// \brief Main Program
+///
+/// Parses the options then dispatches to the appropriate function.  See the
+/// main file header for the invocation.
+
+int main(int argc, char** argv) {
+    
+    const struct option loptions[] = {          // Long options
+        {"help",    no_argument, NULL, 'h'},
+        {"version", no_argument, NULL, 'v'},
+        {NULL,      0,           NULL, 0  }
+    };
+    const char* soptions = "hv";               // Short options
+
+    optind = 1;             // Ensure we start a new scan
+    int  opt;               // Value of the option
+
+    while ((opt = getopt_long(argc, argv, soptions, loptions, NULL)) != -1) {
+        switch (opt) {
+            case 'h':
+                usage();
+                return 0;
+
+            case 'v':
+                version();
+                return 0;
+
+            default:
+                // A message will have already been output about the error.
+                return 1;
+        }
+    }
+
+    // Do we have the message file?
+    if (optind < (argc - 1)) {
+        cout << "Error: excess arguments in command line\n";
+        usage();
+        return 1;
+    } else if (optind >= argc) {
+        cout << "Error: missing message file\n";
+        usage();
+        return 1;
+    }
+    string message_file = argv[optind];
+
+    try {
+        // Have identified the file, so process it.  First create a local
+        // dictionary into which the data will be put.
+        MessageDictionary dictionary;
+
+        // Read the data into it.
+        MessageReader reader(&dictionary);
+        reader.readFile(message_file);
+
+        // Now write the header file.
+        writeHeaderFile(message_file, reader.getPrefix(), &dictionary);
+
+        // ... and the message text file.
+        writeProgramFile(message_file, &dictionary);
+
+        // Finally, warn of any duplicates encountered.
+        warnDuplicates(reader);
+    }
+    catch (MessageException& e) {
+        // Create an error message from the ID and the text
+        MessageDictionary* global = MessageDictionary::globalDictionary();
+        string text = e.id() + ", " + global->getText(e.id());
+
+        // Format with arguments
+        text = isc::strutil::format(text, e.arguments());
+        cerr << text << "\n";
+
+        return 1;
+    }
+
+    return 0;
+
+}
diff --git a/src/lib/log/dbglevels.h b/src/lib/log/dbglevels.h
new file mode 100644
index 0000000..35c6878
--- /dev/null
+++ b/src/lib/log/dbglevels.h
@@ -0,0 +1,31 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#ifndef __DBGLEVELS_H
+#define __DBGLEVELS_H
+
+/// \brief Defines Debug Levels
+///
+/// Defines the maximum and minimum debug levels and the number of levels.
+/// These are defined using #define as they are referenced in the construction
+/// of variables declared outside execution units.  (In this way we avoid the
+/// "static initialization fiasco" problem.)
+
+#define MIN_DEBUG_LEVEL (0)
+#define MAX_DEBUG_LEVEL (99)
+#define NUM_DEBUG_LEVEL (MAX_DEBUG_LEVEL - MIN_DEBUG_LEVEL + 1)
+
+#endif // __DBGLEVELS_H
diff --git a/src/lib/log/documentation.txt b/src/lib/log/documentation.txt
new file mode 100644
index 0000000..1439ad5
--- /dev/null
+++ b/src/lib/log/documentation.txt
@@ -0,0 +1,371 @@
+This directory holds the first release of the logging system.
+
+Basic Ideas
+===========
+The BIND-10 logging system merges two ideas:
+
+* A hierarchical logging system similar to that used in Java (i.e. log4j)
+* Separation of message definitions and text
+
+
+Hierarchical Logging System
+===========================
+When a program writes a message to the logging system, it does so using an
+instance of the Logger class.  As well as performing the write of the message,
+the logger identifies the source of the message: different sources can write
+to different destinations and can log different severities of messages.  For
+example, the "cache" logger could write messages of DEBUG severity or above
+to a file while all other components write messages of "INFO" severity or above
+to the Syslog file.
+
+The loggers are hierarchical in that each logger is the child of another logger.
+The top of the hierarchy is the root logger, which does not have a parent.  The
+point of the hierarchy is that unless a logger is explicitly assigned an
+attribute (such as severity of message being logger), it picks it up from the
+parent.  (In BIND-10, there is the root logger (named after the program) and
+every other logger is a child of that.)  So in the example above, the
+INFO/Syslog attributes could be associated with the root logger while the
+DEBUG/file attributes are associated with the "cache" logger.
+
+
+Separation of Messages Definitions And Text
+===========================================
+The reason for this is to allow the message text to be overridden by versions
+in a local language.  To do this, each message is identified by an identifier
+e.g. "OPENIN".  Within the program, this is the symbol passed to the logging
+system.  The logger system uses the symbol as an index into a dictionary to
+retrieve the message associated with it (e.g. "unable to open %s for input").
+substitutes any message parameters (in this example, the string that is an
+invalid filename) and logs it to the destination.
+
+In the BIND-10 system, a set of default messages are linked into the program.
+At run-time. each program reads a message file, updating the stored definitions;
+this updated text is logged.  However, to aid support, the message identifier
+so in the example above, the message finally logged would be something like:
+
+    OPENIN, unable to open a.txt for input
+
+
+Using The System
+================
+The steps in using the system are:
+
+1. Create a message file.  This defines messages by an identification - a
+   mnemonic for the message, typically 6-12 characters long - and a message.
+   The file is described in more detail below.
+
+   Ideally the file should have a file type of ".msg".
+
+2. Run it through the message compiler to produce the .h and .cc files.  It
+   is intended that this step be included in the build process.  However, for
+   not run the compiler (found in the "compiler" subdirectory) manually.  The
+   only argument is the name of the message file: it will produce as output
+   two files, having the same name as the input file but with file types of
+   ".h" and ".cc".
+
+   The compiler is built in the "compiler" subdirectory of the "src/lib/log"
+   directory.
+
+3. Include the .h file in your source code to define message symbols, and
+   make sure that the .cc file is compiled and linked into your program -
+   static initialization will add the symbols to the global dictionary.
+
+4. Declare loggers in your code and use them to log messages.  This is described
+   in more detail below.
+
+5. To set the debug level and run-time message file, call runTimeInit (declared
+   in logger_support.h) in the main program unit.  This is a temporary solution
+   for Year 2, and will be replaced at a later date, the information coming from
+   the configuration database.
+
+
+Message Files
+=============
+
+File Contents and Format
+------------------------
+A message file is a file containing message definitions.  Typically there will
+be one message file for each component that declares message symbols.  An
+example file could be:
+
+-- BEGIN --
+
+# Example message file
+# $ID:$
+
+$PREFIX TEST_
+TEST1       message %s is much too large
++ This message is a test for the general message code
+
+UNKNOWN     unknown message
++ Issued when the message is unknown.
+
+-- END --
+
+Points to note:
+* Leading and trailing space are trimmed from the line.  Although the above
+  exampl,e has every line starting at column 1, the lines could be indented if
+  desired.
+
+* Blank lines are ignored.
+
+* Lines starting with "#" are comments are are ignored.  Comments must be on
+  a line by themselves - inline comments will be interpreted as part of the
+  text of the line.
+
+* Lines starting $ are directives.  At present, the only directive recognised
+  is $PREFIX, which has one argument: the string used to prefix symbols.  If
+  there is no facility directive, there is no prefix to the symbols. (Prefixes
+  are explained below.)
+
+* Lines starting + indicate an explanation for the preceding message.  These
+  are intended to be processed by a separate program and used to generate an
+  error messages manual.  However they are treated like comments by the message
+  compiler.  As with comments, these must be on a line by themselves; if inline,
+  the text (including the leading "+") will be interpreted as part of the line.
+
+* Message lines.  These comprise a symbol name and a message, which may
+  include zero or more printf-style tokens.  Symbol names will be upper-cased
+  by the compiler.
+
+
+Message Compiler
+----------------
+The message compiler is a program built in the src/log/compiler directory.
+It processes the message file to produce two files:
+
+1) A C++ header file (called <message-file-name>.h) that holds lines of
+the form:
+
+   namespace {
+   isc::log::MessageID PREFIX_IDENTIFIER = "IDENTIFIER";
+      :
+   }
+
+The symbols define the keys in the global message dictionary.  At present
+they are defined as std::strings, but a future implementation could redefine
+them as numeric values.
+
+The "PREFIX_" part of the symbol name is the string defined in the $PREFIX
+the argument to the directive.  So "$PREFIX MSG_" would prefix the identifer
+ABC with "MSG_" to give the symbol MSG_ABC.  Similarly "$PREFIX E" would
+prefix it with "E" to give the symbol EABC.  If no $PREFIX is given, no
+prefix appears (so the symbol in this example would be ABC).
+
+
+2) A C++ source file (called <message-file-name>.cc) that holds the code to
+insert the symbols and messages into the map.
+
+This file declares an array of identifiers/messages in the form:
+
+    namespace {
+    const char* values[] = {
+        identifier1, text1,
+        identifier2, text2,
+        :
+        NULL
+    };
+    }
+
+(A more complex structure to group identifiers and their messages could be
+imposed, but as the array is generated by code and will be read by code,
+it is not needed.)
+
+It then declares an object that will add information to the global dictionary:
+
+    MessageInitializer <message-file-name>_<time>(values);
+
+(Declaring the object as "static" or in the anonymous namespace runs the risk
+of it being optimised away when the module is compiled with optimisation.
+But giving it a standard name would cause a clash when multiple files are
+used, hence an attempt at disambiguation.)
+
+The constructor of the MessageInitializer object retrieves the singleton
+global Dictionary object (created using standard methods to avoid the
+"static initialization fiasco") and adds each identifier and text to it.
+A check is made as each is added; if the identifier already exists, it is
+added to "overflow" vector; the vector is printed to the main logging output
+when logging is finally enabled (to indicate a programming error).
+
+
+Using the Logging
+=================
+To use the current version of the logging:
+
+1. Build message header file and source file as describe above.
+
+2. In the main module of the program, declare an instance of the
+   RootLoggerName class to define the name of the program's root logger, e.g.
+
+       #include <log/root_logger_name.h>
+
+       isc::log::RootLoggerName("b10-auth");
+
+   It should be declared outside an execution unit to allow other statically-
+   declared loggers to pick it up.
+
+2. In the code that needs to do logging, declare a logger with a given name,
+   e.g.
+
+       #include <log/logger.h>
+            :
+       isc::log::Logger logger("myname");   // "myname" can be anything
+
+   The above example assumes declaration outside a function.  If declaring
+   non-statically within a function, declare it as:
+
+       isc::log::Logger logger("myname", true);
+
+   This is due to an apparent bug in the underlying log4cxx, where the deletion
+   of a statically-declared object at program termination can cause a segment
+   fault. (The destruction of internal memory structures can sometimes happen
+   out of order.)  By default the Logger class creates the structures in its
+   constructor but does not delete them in the destruction.  The default
+   behavious works because instead of reclaiming memory at program run-down,
+   the operating system reclaims it when the process is deleted.
+
+   Setting the second argument "true" causes the Logger's destructor to delete
+   the log4cxx structures.  This does not cause a problem if the program is
+   not terminating.  So use the second form when declaring an automatic
+   instance of isc::log::Logger on the stack.
+
+3. The main program unit should include a call to isc::log::runTimeInit()
+   (defined in logger_support.h) to set the logging severity, debug log level,
+   and external message file.
+
+   a) The logging severity is one of the enum defined in logger.h, i.e.
+
+        isc::log::Logger::DEBUG
+        isc::log::Logger::INFO
+        isc::log::Logger::WARN
+        isc::log::Logger::ERROR
+        isc::log::Logger::FATAL
+        isc::log::Logger::NONE
+
+   b) The debug log level is only interpreted when the severity is DEBUG and
+      is an integer raning from 0 to 99.  0 should be used for the highest-level
+      debug messages and 99 for the lowest-level (and typically more verbose)
+      messages.
+
+   c) Name of an external message file.  This is the same as a standard message
+      file, although it should not include the $PREFIX directive. (A single
+      $PREFIX directive will be ignored; multiple directives will cause the
+      read of the file to fail with an error.)  If a message is replaced, the
+      message should include the same printf-format directives in the same order
+      as the original message.
+
+4. Issue logging calls using methods on logger, e.g.
+
+       logger.error(DPS_NSTIMEOUT, "isc.org");
+
+   (where, in the example above we might have defined the symbol in the message
+   file with something along the lines of:
+
+       $PREFIX DPS_
+           :
+       NSTIMEOUT  queries to all nameservers for %s have timed out
+
+   At present, the only logging is to the console.
+
+
+Severity Guidelines
+===================
+When using logging, the question arises, what severity should a message be
+logged at?  The following is a suggestion - as always, the decision must be
+made in the context of which the message is logged.
+
+FATAL
+-----
+The program has encountered an error that is so severe that it cannot
+continue (or there is no point in continuing).  When a fatal error has been
+logged, the program will usually exit immediately (via a call to abort()) or
+shortly afterwards, after dumping some diagnostic information.
+
+ERROR
+-----
+Something has happened such that the program can continue but the results
+for the current (or future) operations cannot be guaranteed to be correct,
+or the results will be correct but the service is impaired.  For example,
+the program started but attempts to open one or more network interfaces failed.
+
+WARN
+----
+An unusual event  happened.  Although the program will continue working
+normally, the event was sufficiently out of the ordinary to warrant drawings
+attention to it.  For example, at program start-up a zone was loaded that
+contained no resource records,
+
+INFO
+----
+A normal but significant event has occurred that should be recorded,
+e.g. the program has started or is just about to terminate, a new zone has
+been created, etc.
+
+DEBUG
+-----
+This severity is only enabled on for debugging purposes.  A debug level is
+associated with debug messages, level 0 (the default) being for high-level
+messages and level 99 (the maximum) for the lowest level.  How the messages
+are distributed between the levels is up to the developer.  So if debugging
+the NSAS (for example), a level 0 message might record the creation of a new
+zone, a level 10 recording a timeout when trying to get a nameserver address,
+but a level 50 would record every query for an address. (And we might add
+level 51 to record every update of the RTT.)
+
+Note that like severities, levels are cumulative; so if level 25 is set as the
+debug level, all debug levels from 0 to 25 will be output.  In fact, it is
+probably easier to visualise the debug levels as part of the severity system:
+
+    FATAL                High
+    ERROR
+    WARN
+    INFO
+    DEBUG level 0
+    DEBUG level 1
+       :
+    DEBUG level 99       Low
+
+When a particular severity is set, it - and all severities and/or debug
+levels above it - will be logged.
+
+Logging Sources v Logging Severities
+------------------------------------
+When logging events, make a distinction between events related to the server
+and events related to DNS messages received.  Caution needs to be exercised
+with the latter as, if the logging is enabled in the normal course of events,
+such logging could be a denoial of service vector. For example, suppose that
+the main authoritiative service logger were to log both zone loading and
+unloading as INFO and a warning message if it received an invalid packet. An
+attacker could make the INFO messages unusable by flooding the server with
+malformed packets.
+
+There are two approaches to get round this:
+
+a) Make the logging of packet-dependent events a DEBUG-severity message.
+DEBUG is not enabled by default, so these events will not be recorded unless
+DEBUG is specifically chosen.
+
+b) Record system-related and packet-related messages via different loggers
+(e.g.  in the example given, sever events could be logged using the logger
+"auth" and packet-related events at that level logged using the logger
+"pkt-auth".)
+As the loggers are independent and the severity levels independent, fine-tuning
+of what and what is not recorded can be achieved.
+
+
+Outstanding Issues
+==================
+* Ability to configure system according to configuration database.
+* Update the build procedure to create .cc and .h files from the .msg file
+  during the build process. (Requires that the message compiler is built first.)
+
+
+Notes
+=====
+The message compiler is written in C++ (instead of Python) because it
+contains a component that reads the message file.  This component is used
+in both the message compiler and the server; in the server it is used when
+the server starts up (or when triggered by a command) to read in a message
+file to overwrite the internal dictionary.  Writing it in C++ means there
+is only one piece of code that does this functionality.
+
diff --git a/src/lib/log/filename.cc b/src/lib/log/filename.cc
new file mode 100644
index 0000000..949ed9f
--- /dev/null
+++ b/src/lib/log/filename.cc
@@ -0,0 +1,140 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <iostream>
+#include <algorithm>
+#include <string>
+
+#include <ctype.h>
+
+#include <log/filename.h>
+#include <log/strutil.h>
+
+using namespace std;
+
+
+namespace isc {
+namespace log {
+
+// Split string into components.  Any backslashes are assumed to have
+// been replaced by forward slashes.
+
+void
+Filename::split(const string& full_name, string& directory,
+    string& name, string& extension) const
+{
+    directory = name = extension = "";
+    bool dir_present = false;
+    if (!full_name.empty()) {
+
+        // Find the directory.
+        size_t last_slash = full_name.find_last_of('/');
+        if (last_slash != string::npos) {
+
+            // Found the last slash, so extract directory component and
+            // set where the scan for the last_dot should terminate.
+            directory = full_name.substr(0, last_slash + 1);
+            if (last_slash == full_name.size()) {
+
+                // The entire string was a directory, so exit not and don't
+                // do any more searching.
+                return;
+            }
+
+            // Found a directory so note the fact.
+            dir_present = true;
+        }
+
+        // Now search backwards for the last ".".
+        size_t last_dot = full_name.find_last_of('.');
+        if ((last_dot == string::npos) ||
+            (dir_present && (last_dot < last_slash))) {
+
+            // Last "." either not found or it occurs to the left of the last
+            // slash if a directory was present (so it is part of a directory
+            // name).  In this case, the remainder of the string after the slash
+            // is the name part.
+            name = full_name.substr(last_slash + 1);
+            return;
+        }
+
+        // Did find a valid dot, so it and everything to the right is the
+        // extension...
+        extension = full_name.substr(last_dot);
+
+        // ... and the name of the file is everything in between.
+        if ((last_dot - last_slash) > 1) {
+            name = full_name.substr(last_slash + 1, last_dot - last_slash - 1);
+        }
+    }
+
+}
+
+// Expand the stored filename with the default.
+
+string
+Filename::expandWithDefault(const string& defname) const {
+
+    string def_directory("");
+    string def_name("");
+    string def_extension("");
+
+    // Normalize the input string.
+    string copy_defname = isc::strutil::trim(defname);
+#ifdef WIN32
+    isc::strutil::normalizeSlash(copy_defname);
+#endif
+
+    // Split into the components
+    split(copy_defname, def_directory, def_name, def_extension);
+
+    // Now construct the result.
+    string retstring =
+        (directory_.empty() ? def_directory : directory_) +
+        (name_.empty() ? def_name : name_) +
+        (extension_.empty() ? def_extension : extension_);
+    return retstring;
+}
+
+// Use the stored name as default for a given name
+
+string
+Filename::useAsDefault(const string& name) const {
+
+    string name_directory("");
+    string name_name("");
+    string name_extension("");
+
+    // Normalize the input string.
+    string copy_name = isc::strutil::trim(name);
+#ifdef WIN32
+    isc::strutil::normalizeSlash(copy_name);
+#endif
+
+    // Split into the components
+    split(copy_name, name_directory, name_name, name_extension);
+
+    // Now construct the result.
+    string retstring =
+        (name_directory.empty() ? directory_ : name_directory) +
+        (name_name.empty() ? name_ : name_name) +
+        (name_extension.empty() ? extension_ : name_extension);
+    return retstring;
+}
+
+
+} // namespace log
+} // namespace isc
diff --git a/src/lib/log/filename.h b/src/lib/log/filename.h
new file mode 100644
index 0000000..29a9cc8
--- /dev/null
+++ b/src/lib/log/filename.h
@@ -0,0 +1,163 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#ifndef __FILENAME_H
+#define __FILENAME_H
+
+#include <string>
+
+#include <strutil.h>
+
+namespace isc {
+namespace log {
+
+/// \brief Class to Manipulate Filenames
+///
+/// This is a utility class to manipulate filenames.  It repeats some of the
+/// features found in the Boost filename class, but is self-contained so avoids
+/// the need to link in the Boost library.
+///
+/// A Unix-style filename comprises three parts:
+///
+/// Directory - everything up to and including the last "/".  If there is no
+/// "/" in the string, there is no directory component.  Note that the
+/// requirement of a trailing slash eliminates the ambiguity of whether a
+/// component is a directory or not, e.g. in /alpha/beta", "beta" could be the
+/// name of a directory or is could be a file.  The interpretation here is that
+/// "beta" is the name of a file (although that file could be a directory).
+///
+/// Note: Under Windows, the drive letter is considered to be part of the
+/// directory specification.  Unless this class becomes more widely-used on
+/// Windows, there is no point in adding redundant code.
+///
+/// Name - everthing from the character after the last "/" up to but not
+/// including the last ".".
+///
+/// Extension - everthing from the right-most "." (after the right-most "/") to
+/// the end of the string.  If there is no "." after the last "/", there is
+/// no file extension.
+///
+/// (Note that on Windows, this function will replace all "\" characters
+/// with "/" characters on input strings.)
+///
+/// This class provides functions for extracting the components and for
+/// substituting components.
+
+
+class Filename {
+public:
+
+    /// \brief Constructor
+    Filename(const std::string& name) :
+        full_name_(""), directory_(""), name_(""), extension_("")
+    {
+        setName(name);
+    }
+
+    /// \brief Sets Stored Filename
+    ///
+    /// \param name New name to replaced currently stored name
+    void setName(const std::string& name) {
+        full_name_ = isc::strutil::trim(name);
+#ifdef WIN32
+        isc::strutil::normalizeSlash(full_name_);
+#endif
+        split(full_name_, directory_, name_, extension_);
+    }
+
+    /// \return Stored Filename
+    std::string fullName() const {
+        return full_name_;
+    }
+
+    /// \return Directory of Given File Name
+    std::string directory() const {
+        return directory_;
+    }
+
+    /// \return Name of Given File Name
+    std::string name() const {
+        return name_;
+    }
+
+    /// \return Extension of Given File Name
+    std::string extension() const {
+        return extension_;
+    }
+
+    /// \brief Expand Name with Default
+    ///
+    /// A default file specified is supplied and used to fill in any missing
+    /// fields.  For example, if the name stored is "/a/b" and the supplied
+    /// name is "c.d", the result is "/a/b.d": the only field missing from the
+    /// stored name is the extension, which is supplied by the default.
+    /// Another example would be to store "a.b" and to supply a default of
+    /// "/c/d/" - the result is "/c/d/a.b".  (Note that if the supplied default
+    /// was "/c/d", the result would be "/c/a.b", even if "/c/d" were actually
+    /// a directory.)
+    ///
+    /// \param defname Default name
+    ///
+    /// \return Name expanded with defname.
+    std::string expandWithDefault(const std::string& defname) const;
+
+    /// \brief Use as Default and Substitute into String
+    ///
+    /// Does essentially the inverse of expand(); that filled in the stored
+    /// name with a default and returned the result.  This treats the stored
+    /// name as the default and uses it to fill in a given name.  In essence,
+    /// the code:
+    /// \code
+    ///       Filename f("/a/b");
+    ///       result = f.expandWithdefault("c.d");
+    /// \endcode
+    /// gives as a result "/a/b.d".  This is the same as:
+    /// \code
+    ///       Filename f("c.d");
+    ///       result = f.useAsDefault("/a/b");
+    /// \endcode
+    ///
+    /// \param name Name to expand
+    ///
+    /// \return Name expanded with stored name
+    std::string useAsDefault(const std::string&) const;
+
+private:
+    /// \brief Split Name into Components
+    ///
+    /// Splits the file name into the directory, name and extension parts.
+    /// The name is assumed to have had back slashes replaced by forward
+    /// slashes (if appropriate).
+    ///
+    /// \param full_name Name to split
+    /// \param directory Returned directory part
+    /// \param name Returned name part
+    /// \param extension Returned extension part
+    void split(const std::string& full_name, std::string& directory,
+       std::string& name, std::string& extension) const;
+
+    // Members
+
+    std::string full_name_;     ///< Given name
+    std::string directory_;     ///< Directory part
+    std::string name_;          ///< Name part
+    std::string extension_;     ///< Extension part
+};
+
+} // namespace log
+} // namespace isc
+
+#endif // __FILENAME_H
diff --git a/src/lib/log/logger.cc b/src/lib/log/logger.cc
new file mode 100644
index 0000000..494e7bc
--- /dev/null
+++ b/src/lib/log/logger.cc
@@ -0,0 +1,307 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE
+
+// $Id$
+
+#include <iostream>
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#include <log4cxx/appender.h>
+#include <log4cxx/basicconfigurator.h>
+#include <log4cxx/patternlayout.h>
+#include <log4cxx/consoleappender.h>
+
+#include <log/root_logger_name.h>
+#include <log/logger.h>
+#include <log/message_dictionary.h>
+#include <log/message_types.h>
+#include <log/strutil.h>
+#include <log/xdebuglevel.h>
+
+using namespace std;
+
+namespace isc {
+namespace log {
+
+// Static initializations
+
+bool Logger::init_ = false;
+
+// Destructor.  Delete log4cxx stuff if "don't delete" is clear.
+
+Logger::~Logger() {
+    if (exit_delete_) {
+        delete loggerptr_;
+    }
+}
+
+// Initialize logger - create a logger as a child of the root logger.  With
+// log4cxx this is assured by naming the logger <parent>.<child>.
+
+void
+Logger::initLogger() {
+
+    // Initialize basic logging if not already done.  This is a one-off for
+    // all loggers.
+    if (!init_) {
+
+        // TEMPORARY
+        // Add a suitable console logger to the log4cxx root logger.  (This
+        // is the logger at the root of the log4cxx tree, not the BIND-10 root
+        // logger, which is one level down.)  The chosen format is:
+        //
+        // YYYY-MM-DD hh:mm:ss.sss [logger] SEVERITY: text
+        //
+        // As noted, this is a temporary hack: it is done here to ensure that
+        // a suitable output and output pattern is set.  Future versions of the
+        // software will set this based on configuration data.
+
+        log4cxx::LayoutPtr layout(
+            new log4cxx::PatternLayout(
+                "%d{yyyy-MM-DD HH:mm:ss.SSS} %-5p [%c] %m\n"));
+        log4cxx::AppenderPtr console(
+            new log4cxx::ConsoleAppender(layout));
+        log4cxx::LoggerPtr sys_root_logger = log4cxx::Logger::getRootLogger();
+        sys_root_logger->addAppender(console);
+        
+        // Set the default logging to INFO
+        sys_root_logger->setLevel(log4cxx::Level::getInfo());
+
+        // All static stuff initialized
+        init_ = true;
+    }
+
+    // Initialize this logger.  Name this as to whether the BIND-10 root logger
+    // name has been set.  (If not, this mucks up the hierarchy :-( ).
+    string root_name = RootLoggerName::getName();
+    if (root_name.empty() || (name_ == root_name)) {
+        loggerptr_ = new log4cxx::LoggerPtr(log4cxx::Logger::getLogger(name_));
+    }
+    else {
+        loggerptr_ = new log4cxx::LoggerPtr(
+            log4cxx::Logger::getLogger(root_name + "." + name_)
+        );
+    }
+}
+
+
+// Set the severity for logging.  There is a 1:1 mapping between the logging
+// severity and the log4cxx logging levels, apart from DEBUG.
+//
+// In log4cxx, each of the logging levels (DEBUG, INFO, WARN etc.) has a numeric
+// value.  The level is set to one of these and any numeric level equal to or
+// above it that is reported.  For example INFO has a value of 20000 and ERROR
+// a value of 40000. So if a message of WARN severity (= 30000) is logged, it is
+// not logged when the logger's severity level is ERROR (as 30000 !>= 40000).
+// It is reported if the logger's severity level is set to WARN (as 30000 >=
+/// 30000) or INFO (30000 >= 20000).
+//
+// This gives a simple system for handling different debug levels.  The debug
+// level is a number between 0 and 99, with 0 being least verbose and 99 the
+// most.  To implement this seamlessly, when DEBUG is set, the numeric value
+// of the logging level is actually set to (DEBUG - debug-level).  Similarly
+// messages of level "n" are logged at a logging level of (DEBUG - n).  Thus if
+// the logging level is set to DEBUG and the debug level set to 25, the actual
+// level set is 10000 - 25 = 99975.
+//
+// Attempting to log a debug message of level 26 is an attempt to log a message
+// of level 10000 - 26 = 9974.  As 9974 !>= 9975, it is not logged.  A
+// message of level 25 is, because 9975 >= 9975.
+//
+// The extended set of logging levels is implemented by the XDebugLevel class.
+
+void
+Logger::setSeverity(Severity severity, int dbglevel) {
+    switch (severity) {
+        case NONE:
+            getLogger()->setLevel(log4cxx::Level::getOff());
+            break;
+
+        case FATAL:
+            getLogger()->setLevel(log4cxx::Level::getFatal());
+            break;
+
+        case ERROR:
+            getLogger()->setLevel(log4cxx::Level::getError());
+            break;
+
+        case WARN:
+            getLogger()->setLevel(log4cxx::Level::getWarn());
+            break;
+
+        case INFO:
+            getLogger()->setLevel(log4cxx::Level::getInfo());
+            break;
+
+        case DEBUG:
+            getLogger()->setLevel(
+                log4cxx::XDebugLevel::getExtendedDebug(dbglevel));
+            break;
+
+        // Will get here for DEFAULT or any other value.  This disables the
+        // logger's own severity and it defaults to the severity of the parent
+        // logger.
+        default:
+            getLogger()->setLevel(0);
+    }
+}
+
+// Convert between numeric log4cxx logging level and BIND-10 logging severity.
+
+Logger::Severity
+Logger::convertLevel(int value) const {
+
+    // The order is optimised.  This is only likely to be called when testing
+    // for writing debug messages, so the check for DEBUG_INT is first.
+    if (value <= log4cxx::Level::DEBUG_INT) {
+        return (DEBUG);
+    } else if (value <= log4cxx::Level::INFO_INT) {
+        return (INFO);
+    } else if (value <= log4cxx::Level::WARN_INT) {
+        return (WARN);
+    } else if (value <= log4cxx::Level::ERROR_INT) {
+        return (ERROR);
+    } else if (value <= log4cxx::Level::FATAL_INT) {
+        return (FATAL);
+    } else {
+        return (NONE);
+    }
+}
+
+
+// Return the logging severity associated with this logger.
+
+Logger::Severity
+Logger::getSeverityCommon(const log4cxx::LoggerPtr& ptrlogger,
+    bool check_parent) const {
+
+    log4cxx::LevelPtr level = ptrlogger->getLevel();
+    if (level == log4cxx::LevelPtr()) {
+
+        // Null level returned, logging should be that of the parent.
+
+        if (check_parent) {
+            log4cxx::LoggerPtr parent = ptrlogger->getParent();
+            if (parent == log4cxx::LoggerPtr()) {
+
+                // No parent, so reached the end of the chain.  Return INFO
+                // severity.
+                return (INFO);
+            }
+            else {
+                return getSeverityCommon(parent, check_parent);
+            }
+        }
+        else {
+            return (DEFAULT);
+        }
+    } else {
+        return convertLevel(level->toInt());
+    }
+}
+
+
+// Get the debug level.  This returns 0 unless the severity is DEBUG.
+
+int
+Logger::getDebugLevel() {
+
+    log4cxx::LevelPtr level = getLogger()->getLevel();
+    if (level == log4cxx::LevelPtr()) {
+
+        // Null pointer returned, logging should be that of the parent.
+        return (0);
+        
+    } else {
+        int severity = level->toInt();
+        if (severity <= log4cxx::Level::DEBUG_INT) {
+            return (log4cxx::Level::DEBUG_INT - severity);
+        }
+        else {
+            return (0);
+        }
+    }
+}
+
+// Log an error message:
+// Common code.  Owing to the use of variable arguments, this must be inline
+// (hence the definition of the macro).  Also note that it expects that the
+// message buffer "message" is declared in the compilation unit.
+
+#define MESSAGE_SIZE (256)
+
+#define FORMAT_MESSAGE(message) \
+    { \
+    MessageDictionary* global = MessageDictionary::globalDictionary(); \
+    string format = global->getText(ident); \
+    va_list ap; \
+    va_start(ap, ident); \
+    vsnprintf(message, sizeof(message), format.c_str(), ap); \
+    message[sizeof(message) - 1] = '\0'; \
+    va_end(ap); \
+    }
+    
+
+// Output methods
+
+void
+Logger::debug(int dbglevel, isc::log::MessageID ident, ...) {
+    if (isDebugEnabled(dbglevel)) {
+        char message[MESSAGE_SIZE];
+        FORMAT_MESSAGE(message);
+        LOG4CXX_DEBUG(getLogger(), ident << ", " << message);
+    }
+}
+
+void
+Logger::info(isc::log::MessageID ident, ...) {
+    if (isInfoEnabled()) {
+        char message[MESSAGE_SIZE];
+        FORMAT_MESSAGE(message);
+        LOG4CXX_INFO(getLogger(), ident << ", " << message);
+    }
+}
+
+void
+Logger::warn(isc::log::MessageID ident, ...) {
+    if (isWarnEnabled()) {
+        char message[MESSAGE_SIZE];
+        FORMAT_MESSAGE(message);
+        LOG4CXX_WARN(getLogger(), ident << ", " << message);
+    }
+}
+
+void
+Logger::error(isc::log::MessageID ident, ...) {
+    if (isErrorEnabled()) {
+        char message[MESSAGE_SIZE];
+        FORMAT_MESSAGE(message);
+        LOG4CXX_ERROR(getLogger(), ident << ", " << message);
+    }
+}
+
+void
+Logger::fatal(isc::log::MessageID ident, ...) {
+    if (isFatalEnabled()) {
+        char message[MESSAGE_SIZE];
+        FORMAT_MESSAGE(message);
+        LOG4CXX_FATAL(getLogger(), ident << ", " << message);
+    }
+}
+
+
+} // namespace log
+} // namespace isc
diff --git a/src/lib/log/logger.h b/src/lib/log/logger.h
new file mode 100644
index 0000000..f26f6d1
--- /dev/null
+++ b/src/lib/log/logger.h
@@ -0,0 +1,327 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#ifndef __LOGGER_H
+#define __LOGGER_H
+
+#include <cstdlib>
+#include <string>
+#include <boost/lexical_cast.hpp>
+#include <log4cxx/logger.h>
+
+#include <log/dbglevels.h>
+#include <log/message_types.h>
+
+namespace isc {
+namespace log {
+
+class Logger {
+public:
+
+    /// \brief Severity Levels
+    typedef enum {
+        DEFAULT,    // Default to logging level of parent
+        DEBUG,
+        INFO,
+        WARN,
+        ERROR,
+        FATAL,
+        NONE        // Disable logging
+    } Severity;
+
+    /// \brief Constructor
+    ///
+    /// Creates/attaches to a logger of a specific name.
+    ///
+    /// \param name Name of the logger.  If the name is that of the root name,
+    /// this creates an instance of the root logger; otherwise it creates a
+    /// child of the root logger.
+    ///
+    /// \param exit_delete This argument is present to get round a bug in
+    /// log4cxx.  If a log4cxx logger is declared outside an execution unit, it
+    /// is not deleted until the program runs down.  At that point all such
+    /// objects - including internal log4cxx objects - are deleted.  However,
+    /// there seems to be a bug in log4cxx where the way that such objects are
+    /// destroyed causes a MutexException to be thrown (this is described in
+    /// https://issues.apache.org/jira/browse/LOGCXX-322).  As this only occurs
+    /// during program rundown, the issue is not serious - it just looks bad to
+    /// have the program crash instead of shut down cleanly.\n
+    /// \n
+    /// The original implementation of the isc::log::Logger had as a member a
+    /// log4cxx logger (actually a LoggerPtr).  If the isc:: Logger was declared
+    /// statically, when it was destroyed at the end of the program the internal
+    /// LoggerPtr was destroyed, which triggered the problem.  The problem did
+    /// not occur if the isc::log::Logger was created on the stack.  To get
+    /// round this, the internal LoggerPtr is now created dynamically.  The
+    /// exit_delete argument controls its destruction: if true, it is destroyed
+    /// in the ISC Logger destructor.  If false, it is not.\n
+    /// \n
+    /// When creating an isc::log::Logger on the stack, the argument should be
+    /// false (the default); when the Logger is destroyed, all the internal
+    /// log4cxx objects are destroyed.  As only the logger (and not the internal
+    /// log4cxx data structures are being destroyed), all is well.  However,
+    /// when creating the logger statically, the argument should be false.  This
+    /// means that the log4cxx objects are not destroyed at program rundown;
+    /// instead memory is reclaimed and files are closed when the process is
+    /// destroyed, something that does not trigger the bug.
+    Logger(const std::string& name, bool exit_delete = false) :
+        loggerptr_(), name_(name), exit_delete_(exit_delete)
+    {}
+
+
+    /// \brief Destructor
+    virtual ~Logger();
+
+
+    /// \brief Configure Options
+    ///
+    /// TEMPORARY: Pass in the command-line options to set the logging severity
+    /// for the root logger.  Future versions of the logger will get this
+    /// information from the configuration database.
+    ///
+    /// \param severity Severity level to log
+    /// \param dbglevel If the severity is DEBUG, this is the debug level.
+    /// This can be in the range 1 to 100 and controls the verbosity.  A value
+    /// outside these limits is silently coerced to the nearest boundary.
+    /// \param local_file If provided, the name of a message file to read in and
+    /// supersede one or more of the current messages.
+    static void runTimeInit(Severity severity = INFO, int dbglevel = 1,
+        const char* local_file = NULL);
+
+
+    /// \brief Get Name of Logger
+    ///
+    /// \return The full name of the logger (including the root name)
+    virtual std::string getName() {
+        return getLogger()->getName();
+    }
+
+
+    /// \brief Set Severity Level for Logger
+    ///
+    /// Sets the level at which this logger will log messages.  If none is set,
+    /// the level is inherited from the parent.
+    ///
+    /// \param severity Severity level to log
+    /// \param dbglevel If the severity is DEBUG, this is the debug level.
+    /// This can be in the range 1 to 100 and controls the verbosity.  A value
+    /// outside these limits is silently coerced to the nearest boundary.
+    virtual void setSeverity(Severity severity, int dbglevel = 1);
+
+
+    /// \brief Get Severity Level for Logger
+    ///
+    /// \return The current logging level of this logger.  In most cases though,
+    /// the effective logging level is what is required.
+    virtual Severity getSeverity() {
+        return getSeverityCommon(getLogger(), false);
+    }
+
+    /// \brief Get Effective Severity Level for Logger
+    ///
+    /// \return The effective severity level of the logger.  This is the same
+    /// as getSeverity() if the logger has a severity level set, but otherwise
+    /// is the severity of the parent.
+    virtual Severity getEffectiveSeverity() {
+        return getSeverityCommon(getLogger(), true);
+    }
+
+
+    /// \brief Return DEBUG Level
+    ///
+    /// \return Current setting of debug level.  This is returned regardless of
+    /// whether the 
+    virtual int getDebugLevel();
+
+
+    /// \brief Returns if Debug Message Should Be Output
+    ///
+    /// \param dbglevel Level for which debugging is checked.  Debugging is
+    /// enabled only if the logger has DEBUG enabled and if the dbglevel
+    /// checked is less than or equal to the debug level set for the logger.
+    virtual bool
+    isDebugEnabled(int dbglevel = MIN_DEBUG_LEVEL) {
+        return (getLogger()->getEffectiveLevel()->toInt() <=
+            (log4cxx::Level::DEBUG_INT - dbglevel));
+    }
+
+
+    /// \brief Is INFO Enabled?
+    virtual bool isInfoEnabled() {
+        return (getLogger()->isInfoEnabled());
+    }
+
+
+    /// \brief Is WARNING Enabled?
+    virtual bool isWarnEnabled() {
+        return (getLogger()->isWarnEnabled());
+    }
+
+
+    /// \brief Is ERROR Enabled?
+    virtual bool isErrorEnabled() {
+        return (getLogger()->isErrorEnabled());
+    }
+
+
+    /// \brief Is FATAL Enabled?
+    virtual bool isFatalEnabled() {
+        return (getLogger()->isFatalEnabled());
+    }
+
+
+    /// \brief Output Debug Message
+    ///
+    /// \param dbglevel Debug level, ranging between 0 and 99.  Higher numbers
+    /// are used for more verbose output.
+    /// \param ident Message identification.
+    /// \param ... Optional arguments for the message.
+    void debug(int dbglevel, MessageID ident, ...);
+
+
+    /// \brief Output Informational Message
+    ///
+    /// \param ident Message identification.
+    /// \param ... Optional arguments for the message.
+    void info(MessageID ident, ...);
+
+
+    /// \brief Output Warning Message
+    ///
+    /// \param ident Message identification.
+    /// \param ... Optional arguments for the message.
+    void warn(MessageID ident, ...);
+
+
+    /// \brief Output Error Message
+    ///
+    /// \param ident Message identification.
+    /// \param ... Optional arguments for the message.
+    void error(MessageID ident, ...);
+
+
+    /// \brief Output Fatal Message
+    ///
+    /// \param ident Message identification.
+    /// \param ... Optional arguments for the message.
+    void fatal(MessageID ident, ...);
+
+
+protected:
+
+    /// \brief Equality
+    ///
+    /// Check if two instances of this logger refer to the same stream.
+    /// (This method is principally for testing.)
+    ///
+    /// \return true if the logger objects are instances of the same logger.
+    bool operator==(const Logger& other) const {
+        return (*loggerptr_ == *other.loggerptr_);
+    }
+
+
+    /// \brief Logger Initialized
+    ///
+    /// Check that the logger has been properly initialized.  (This method
+    /// is principally for testing.)
+    ///
+    /// \return true if this logger object has been initialized.
+    bool isInitialized() const {
+        return (loggerptr_ != NULL);
+    }
+
+
+    /// \brief Get Severity Level for Logger
+    ///
+    /// This is common code for getSeverity() and getEffectiveSeverity() -
+    /// it returns the severity of the logger; if not set (and the check_parent)
+    /// flag is set, it searches up the parent-child tree until a severity
+    /// level is found and uses that.
+    ///
+    /// \param ptrlogger Pointer to the log4cxx logger to check.
+    /// \param check_parent true to search up the tree, false to return the
+    /// current level.
+    ///
+    /// \return The effective severity level of the logger.  This is the same
+    /// as getSeverity() if the logger has a severity level set, but otherwise
+    /// is the severity of the parent.
+    Logger::Severity getSeverityCommon(const log4cxx::LoggerPtr& ptrlogger,
+        bool check_parent) const;
+
+
+    /// \brief Convert Between BIND-10 and log4cxx Logging Levels
+    ///
+    /// Converts between the numeric value of the log4cxx logging level
+    /// and the BIND-10 severity level.
+    ///
+    /// \param value log4cxx numeric logging level
+    ///
+    /// \return BIND-10 logging severity
+    Severity convertLevel(int value) const;
+
+
+    /// \brief Initialize log4cxx Logger
+    ///
+    /// Creates the log4cxx logger used internally.  A function is provided for
+    /// this so that the creation does not take place when this Logger object
+    /// is created but when it is used.  As the latter occurs in executable
+    /// code but the former can occur during initialization, this order
+    /// guarantees that anything that is statically initialized has completed
+    /// its initialization by the time the logger is used.
+    void initLogger();
+
+
+    /// \brief Return log4cxx Logger
+    ///
+    /// Returns the log4cxx logger, initializing it if not already initialized.
+    ///
+    /// \return Loggerptr object
+    log4cxx::LoggerPtr& getLogger() {
+        if (loggerptr_ == NULL) {
+            initLogger();
+        }
+        return *loggerptr_;
+    }
+
+
+    /// \brief Read Local Message File
+    ///
+    /// Reads a local message file into the global dictionary, replacing any
+    /// definitions there.  Any messages found in the local file that do not
+    /// replace ones in the global dictionary are listed.
+    ///
+    /// \param file Local message file to be read.
+    static void readLocalMessageFile(const char* file);
+
+private:
+    // Note that loggerptr_ is a pointer to a LoggerPtr, which is itself a
+    // pointer to the underlying log4cxx logger.  This is due to the problems
+    // with memory deletion on program exit, explained in the comments for
+    // the "exit_delete" parameter in this class's constructor.
+
+    log4cxx::LoggerPtr*  loggerptr_;    ///< Pointer to the underlying logger
+    std::string          name_;         ///< Name of this logger]
+    bool                 exit_delete_;  ///< Delete loggerptr_ on exit?
+
+    // NOTE - THIS IS A PLACE HOLDER
+    static bool         init_;      ///< Set true when initialized
+};
+
+} // namespace log
+} // namespace isc
+
+
+#endif // __LOGGER_H
diff --git a/src/lib/log/logger_support.cc b/src/lib/log/logger_support.cc
new file mode 100644
index 0000000..c9ba858
--- /dev/null
+++ b/src/lib/log/logger_support.cc
@@ -0,0 +1,116 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE
+
+// $Id$
+
+
+
+/// \brief Temporary Logger Support
+///
+/// Performs run-time initialization of the logger system.  In particular, it
+/// is passed information from the command line and:
+///
+/// a) Sets the severity of the messages being logged (and debug level if
+/// appropriate).
+/// b) Reads in the local message file is one has been supplied.
+///
+/// These functions will be replaced once the code has bneen written to obtain
+/// the logging parameters from the configuration database.
+
+#include <vector>
+
+#include <log/logger.h>
+#include <log/logger_support.h>
+#include <log/messagedef.h>
+#include <log/message_dictionary.h>
+#include <log/message_exception.h>
+#include <log/message_reader.h>
+#include <log/message_types.h>
+#include <log/root_logger_name.h>
+
+namespace isc {
+namespace log {
+
+using namespace std;
+
+// Declare a logger for the logging subsystem
+Logger logger("log");
+
+
+/// \brief Reads Local Message File
+///
+/// Reads the local message file into the global dictionary, overwriting
+/// existing messages.  If the file contained any message IDs not in the
+/// dictionary, they are listed in a warning message.
+///
+/// \param file Name of the local message file
+static void
+readLocalMessageFile(const char* file) {
+    
+    MessageDictionary* dictionary = MessageDictionary::globalDictionary();
+    MessageReader reader(dictionary);
+    try {
+        reader.readFile(file, MessageReader::REPLACE);
+
+        // File successfully read, list the duplicates
+        MessageReader::MessageIDCollection unknown = reader.getNotAdded();
+        for (MessageReader::MessageIDCollection::const_iterator
+            i = unknown.begin(); i != unknown.end(); ++i) {
+                logger.warn(MSG_IDNOTFND, (*i).c_str());
+        }
+    }
+    catch (MessageException& e) {
+        MessageID ident = e.id();
+        vector<MessageID> args = e.arguments();
+        switch (args.size()) {
+        case 0:
+            logger.error(ident);
+            break;
+
+        case 1:
+            logger.error(ident, args[0].c_str());
+            break;
+
+        default:    // 2 or more (2 should be the maximum)
+            logger.error(ident, args[0].c_str(), args[1].c_str());
+        }
+    }
+}
+
+/// Logger Run-Time Initialization
+
+void
+runTimeInit(Logger::Severity severity, int dbglevel, const char* file) {
+
+    // Create the application root logger.  This is the logger that has the
+    // name of the application (and is one level down from the log4cxx root
+    // logger).  All other loggers created in this application will be its
+    // child.
+    //
+    // The main purpose of the application root logger is to provide the root
+    // name in output message for all other loggers.
+    Logger logger(RootLoggerName::getName());
+
+    // Set the severity associated with it.  If no other logger has a severity,
+    // this will be the default.
+    logger.setSeverity(severity, dbglevel);
+
+    // Replace any messages with local ones (if given)
+    if (file) {
+        readLocalMessageFile(file);
+    }
+}
+
+} // namespace log
+} // namespace isc
diff --git a/src/lib/log/logger_support.h b/src/lib/log/logger_support.h
new file mode 100644
index 0000000..85f838f
--- /dev/null
+++ b/src/lib/log/logger_support.h
@@ -0,0 +1,43 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#ifndef __LOGGER_SUPPORT_H
+#define __LOGGER_SUPPORT_H
+
+#include <log/logger.h>
+
+namespace isc {
+namespace log {
+
+/// \brief Run-Time Initialization
+///
+/// This code will be used until the logger is fully integrated into the BIND-10
+/// configuration database.  It performs run-time initialization of th logger,
+/// in particular supplying run-time choices to it:
+///
+/// * The severity (and if applicable, debug level) at which to log
+/// * Name of a local message file, containing localisation of message text.
+///
+/// \param severity Severity at which to log
+/// \param dbglevel Debug severiy (ignored if "severity" is not "DEBUG")
+/// \param file Name of the local message file.
+void runTimeInit(Logger::Severity severity, int dbglevel, const char* file);
+
+} // namespace log
+} // namespace isc
+
+
+#endif // __LOGGER_SUPPORT_H
diff --git a/src/lib/log/message_dictionary.cc b/src/lib/log/message_dictionary.cc
new file mode 100644
index 0000000..05c79f8
--- /dev/null
+++ b/src/lib/log/message_dictionary.cc
@@ -0,0 +1,116 @@
+// 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.
+
+// $Id$
+
+#include <cstddef>
+#include <log/message_dictionary.h>
+#include <log/message_types.h>
+
+using namespace std;
+
+namespace isc {
+namespace log {
+
+// (Virtual) Destructor
+
+MessageDictionary::~MessageDictionary() {
+}
+
+// Add message and note if ID already exists
+
+bool
+MessageDictionary::add(const MessageID& ident, const std::string& text) {
+    map<MessageID, string>::iterator i = dictionary_.find(ident);
+    bool not_found = (i == dictionary_.end());
+    if (not_found) {
+
+        // Message not already in the dictionary, so add it.
+        dictionary_[ident] = text;
+    }
+    
+    return (not_found);
+}
+
+// Add message and note if ID does not already exist
+
+bool
+MessageDictionary::replace(const MessageID& ident, const std::string& text) {
+    map<MessageID, string>::iterator i = dictionary_.find(ident);
+    bool found = (i != dictionary_.end());
+    if (found) {
+
+        // Exists, so replace it.
+        dictionary_[ident] = text;
+    }
+    
+    return (found);
+}
+
+// Load a set of messages
+
+vector<MessageID>
+MessageDictionary::load(const char* messages[]) {
+    vector<MessageID> duplicates;
+    int i = 0;
+    while (messages[i]) {
+
+        // ID present, so note it and point to text.
+        MessageID ident(messages[i++]);
+        if (messages[i]) {
+
+            // Text not null, note it and point to next ident. 
+            string text(messages[i++]);
+
+            // Add ID and text to message dictionary, noting if the ID was
+            // already present.
+            bool added = add(ident, text);
+            if (!added) {
+                duplicates.push_back(ident);
+            }
+        }
+    }
+    return duplicates;
+}
+
+// Return message text or blank string
+
+string
+MessageDictionary::getText(const MessageID& ident) const {
+    map<MessageID, string>::const_iterator i = dictionary_.find(ident);
+    if (i == dictionary_.end()) {
+        return string("");
+    }
+    else {
+        return i->second;
+    }
+}
+
+// Return global dictionary
+
+MessageDictionary*
+MessageDictionary::globalDictionary() {
+    static MessageDictionary* global = NULL;
+
+    if (global == NULL) {
+        global = new MessageDictionary();
+    }
+    return global;
+}
+
+
+
+
+} // namspace log
+} // namespace isc
diff --git a/src/lib/log/message_dictionary.h b/src/lib/log/message_dictionary.h
new file mode 100644
index 0000000..0b2e704
--- /dev/null
+++ b/src/lib/log/message_dictionary.h
@@ -0,0 +1,150 @@
+// 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.
+
+// $Id$
+
+#ifndef __MESSAGE_DICTIONARY_H
+#define __MESSAGE_DICTIONARY_H
+
+#include <cstddef>
+#include <string>
+#include <map>
+#include <vector>
+
+#include <log/message_types.h>
+
+namespace isc {
+namespace log {
+
+/// \brief Message Dictionary
+///
+/// The message dictionary is a wrapper around a std::map object, and allows
+/// message text to be retrieved given the string identification.
+///
+/// Adding text occurs in two modes:
+///
+/// Through the "Add" method, ID/text mappings are added to the dictionary
+/// unless the ID already exists.  This is designed for use during program
+/// initialization, where a local message may supplant a compiled-in message.
+///
+/// Through the "Replace" method, ID/text mappings are added to the dictionary
+/// only if the ID already exists.  This is for use when a message file is
+/// supplied to replace messages provided with the program.
+///
+/// Although the class can be used stand-alone, it does supply a static method
+/// to return a particular instance - the "global" dictionary.
+
+class MessageDictionary {
+public:
+
+    // Default constructor and assignment operator are OK for this class
+
+    /// \brief Virtual Destructor
+    virtual ~MessageDictionary();
+
+    /// \brief Add Message
+    ///
+    /// Adds a message to the dictionary.  If the ID already exists, the ID is
+    /// added to the overflow vector.
+    ///
+    /// \param ident Identification of the message to add
+    /// \param text Message text
+    ///
+    /// \return true if the message was added to the dictionary, false if the
+    /// message existed and it was not added.
+    virtual bool add(const MessageID& ident, const std::string& text);
+
+
+    /// \brief Replace Message
+    ///
+    /// Replaces a message in the dictionary.  If the ID does not exist, it is
+    /// added to the overflow vector.
+    ///
+    /// \param ident Identification of the message to replace
+    /// \param text Message text
+    ///
+    /// \return true if the message was added to the dictionary, false if the
+    /// message did not exist and it was not added.
+    virtual bool replace(const MessageID& ident, const std::string& text);
+
+
+    /// \brief Load Dictionary
+    ///
+    /// Designed to be used during the initialization of programs, this
+    /// accepts a set of (ID, text) pairs as a one-dimensional array of
+    /// const char* and adds them to the dictionary.  The messages are added
+    /// using "Add".
+    ///
+    /// \param data null-terminated array of const char* alternating ID and
+    /// message text.  This should be an odd number of elements long, the last
+    /// elemnent being NULL.  If it is an even number of elements long, the
+    /// last ID is ignored.
+    ///
+    /// \return Vector of message IDs that were not loaded because an ID of the
+    /// same name already existing in the dictionary.  This vector may be
+    /// empty.
+    virtual std::vector<MessageID> load(const char* elements[]);
+
+
+    /// \brief Get Message Text
+    ///
+    /// Given an ID, retrieve associated message text.
+    ///
+    /// \param ident Message identification
+    ///
+    /// \return Text associated with message or empty string if the ID is not
+    /// recognised.  (Note: this precludes an ID being associated with an empty
+    /// string.)
+    virtual std::string getText(const MessageID& ident) const;
+
+
+    /// \brief Number of Items in Dictionary
+    ///
+    /// \return Number of items in the dictionary
+    virtual size_t size() const {
+        return dictionary_.size();
+    }
+
+
+    // Allow access to the internal map structure, but don't allow alteration.
+    typedef std::map<MessageID, std::string>::const_iterator const_iterator;
+
+
+    /// \brief Return begin() iterator of internal map
+    const_iterator begin() const {
+        return dictionary_.begin();
+    }
+
+
+    /// \brief Return end() iterator of internal map
+    const_iterator end() const {
+        return dictionary_.end();
+    }
+
+
+    /// \brief Return Global Dictionary
+    ///
+    /// Returns a pointer to the singleton global dictionary.
+    ///
+    /// \return Pointer to global dictionary.
+    static MessageDictionary* globalDictionary();
+
+private:
+    std::map<MessageID, std::string>  dictionary_;
+};
+
+} // namespace log
+} // namespace isc
+
+#endif // __MESSAGE_DICTIONARY_H
diff --git a/src/lib/log/message_exception.cc b/src/lib/log/message_exception.cc
new file mode 100644
index 0000000..562c381
--- /dev/null
+++ b/src/lib/log/message_exception.cc
@@ -0,0 +1,28 @@
+// 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.
+
+// $Id$
+
+/// \brief Body of Virtual Destructor
+
+#include <log/message_exception.h>
+
+namespace isc {
+namespace log {
+
+MessageException::~MessageException() throw() {
+}
+
+} // namespace log
+} // namespace isc
diff --git a/src/lib/log/message_exception.h b/src/lib/log/message_exception.h
new file mode 100644
index 0000000..537392d
--- /dev/null
+++ b/src/lib/log/message_exception.h
@@ -0,0 +1,90 @@
+// 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.
+
+// $Id$
+
+#ifndef __MESSAGE_EXCEPTION_H
+#define __MESSAGE_EXCEPTION_H
+
+#include <stdexcept>
+#include <string>
+#include <vector>
+
+#include <log/message_types.h>
+
+namespace isc {
+namespace log {
+
+/// \brief Message Exception
+///
+/// Used in the message reader, this simple exception class allows a message
+/// code and its arguments to be encapsulated in an exception and thrown
+/// up the stack.
+
+class MessageException : public std::exception {
+public:
+
+    /// \brief Constructor
+    ///
+    /// \param id Message identification
+    MessageException(MessageID id) : id_(id)
+    {}
+
+    /// \brief Constructor
+    ///
+    /// \param id Message identification
+    /// \param arg1 First message argument
+    MessageException(MessageID id, const std::string& arg1) : id_(id)
+    {
+        args_.push_back(arg1);
+    }
+
+    /// \brief Constructor
+    ///
+    /// \param id Message identification
+    /// \param arg1 First message argument
+    /// \param arg2 Second message argument
+    MessageException(MessageID id, const std::string& arg1,
+        const std::string& arg2) : id_(id)
+    {
+        args_.push_back(arg1);
+        args_.push_back(arg2);
+    }
+
+    /// \brief Destructor
+    virtual ~MessageException() throw();
+
+    /// \brief Return Message ID
+    ///
+    /// \return Message identification
+    MessageID id() const {
+        return id_;
+    }
+
+    /// \brief Return Arguments
+    ///
+    /// \return Exception Arguments
+    std::vector<std::string> arguments() const {
+        return args_;
+    }
+
+private:
+    MessageID                   id_;        // Exception ID
+    std::vector<std::string>    args_;      // Exception arguments
+};
+
+} // namespace log
+} // namespace isc
+
+#endif // __MESSAGE_EXCEPTION_H
diff --git a/src/lib/log/message_initializer.cc b/src/lib/log/message_initializer.cc
new file mode 100644
index 0000000..914ed17
--- /dev/null
+++ b/src/lib/log/message_initializer.cc
@@ -0,0 +1,32 @@
+// 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.
+
+// $Id$
+
+#include <log/message_dictionary.h>
+#include <log/message_initializer.h>
+
+namespace isc {
+namespace log {
+
+// Constructor.  Just retrieve the global dictionary and load the IDs and
+// associated text into it.
+
+MessageInitializer::MessageInitializer(const char* values[]) {
+    MessageDictionary* global = MessageDictionary::globalDictionary();
+    global->load(values);
+}
+
+} // namespace log
+} // namespace isc
diff --git a/src/lib/log/message_initializer.h b/src/lib/log/message_initializer.h
new file mode 100644
index 0000000..a776a02
--- /dev/null
+++ b/src/lib/log/message_initializer.h
@@ -0,0 +1,63 @@
+// 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.
+
+// $Id$
+
+#ifndef __MESSAGEINITIALIZER_H
+#define __MESSAGEINITIALIZER_H
+
+#include <log/message_dictionary.h>
+
+namespace isc {
+namespace log {
+
+/// \brief Initialize Message Dictionary
+///
+/// This is a helper class to add a set of message IDs and associated text to
+/// the global dictionary.
+///
+/// It should be declared outside an execution unit and initialized with a
+/// an array of values, alternating identifier, associated text and ending with
+/// a NULL, e.g.
+///
+///     static const char* values[] = {
+///         "IDENT1", "message for ident 1",
+///         "IDENT2", "message for ident 2",
+///             :
+///         NULL
+///     };
+///     MessageDictionaryHelper xyz(values);
+///
+/// This will automatically add the message ID/text pairs to the global
+/// dictionary during initialization - all that is required is that the module
+/// containing the definition is included into the final executable.
+///
+/// Messages are added via the MessageDictionary::add() method, so any
+/// duplicates are stored in the the global dictionary's overflow vector whence
+/// they can be retrieved at run-time.
+
+class MessageInitializer {
+public:
+
+    /// \brief Constructor
+    ///
+    /// The only method in the class, this adds the array of values to the
+    /// global dictionary.
+    MessageInitializer(const char* values[]);
+};
+
+} // namespace log
+} // namespace isc
+
+#endif // __MESSAGEINITIALIZER_H
diff --git a/src/lib/log/message_reader.cc b/src/lib/log/message_reader.cc
new file mode 100644
index 0000000..203b836
--- /dev/null
+++ b/src/lib/log/message_reader.cc
@@ -0,0 +1,184 @@
+// 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.
+
+// $Id$
+
+#include <errno.h>
+#include <string.h>
+
+#include <iostream>
+#include <fstream>
+
+#include <log/message_exception.h>
+#include <log/messagedef.h>
+#include <log/message_reader.h>
+#include <log/strutil.h>
+
+using namespace std;
+
+namespace isc {
+namespace log {
+
+// Virtual destructor.
+MessageReader::~MessageReader() {
+}
+
+
+// Read the file.
+
+void
+MessageReader::readFile(const string& file, MessageReader::Mode mode) {
+
+    // Ensure the non-added collection is empty: this object might be
+    // being reused.
+    not_added_.clear();
+
+    // Open the file
+    ifstream infile(file.c_str());
+    if (infile.fail()) {
+        throw MessageException(MSG_OPENIN, file, strerror(errno));
+    }
+
+    // Loop round reading it.
+    string line;
+    getline(infile, line);
+    while (infile.good()) {
+        processLine(line, mode);
+        getline(infile, line);
+    }
+
+    // Why did the loop terminate?
+    if (!infile.eof()) {
+        throw MessageException(MSG_READERR, file, strerror(errno));
+    }
+    infile.close();
+}
+
+// Parse a line of the file
+
+void
+MessageReader::processLine(const string& line, MessageReader::Mode mode) {
+
+    // Get rid of leading and trailing spaces
+    string text = isc::strutil::trim(line);
+
+    if (text.empty()) {
+        ;                           // Ignore blank lines
+
+    } else if ((text[0] == '#') || (text[0] == '+')) {
+        ;                           // Ignore comments or descriptions
+
+    } else if (text[0] == '$') {
+        parseDirective(text);       // Process directives
+
+    } else {
+        parseMessage(text, mode);   // Process other lines
+
+    }
+}
+
+// Process directive
+
+void
+MessageReader::parseDirective(const std::string& text) {
+
+    static string valid = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
+
+    // Regardless of what happens, all prefixes will be uppercase (as will
+    // all symbols).
+    string line = text;
+    isc::strutil::uppercase(line);
+    vector<string> tokens = isc::strutil::tokens(line);
+
+    // Only $PREFIX is recognised so far, so we'll handle it here.
+    if (tokens[0] != string("$PREFIX")) {
+        throw MessageException(MSG_UNRECDIR, tokens[0]);
+
+    } else if (tokens.size() < 2) {
+        throw MessageException(MSG_PRFNOARG);
+
+    } else if (tokens.size() > 2) {
+        throw MessageException(MSG_PRFEXTRARG);
+
+    }
+
+    // Token is potentially valid providing it only contains alphabetic
+    // and numeric characters (and underscores) and does not start with a
+    // digit.
+    
+    if ((tokens[1].find_first_not_of(valid) != string::npos) ||
+        (std::isdigit(tokens[1][0]))) {
+
+        // Invalid character in string or it starts with a digit.
+        throw MessageException(MSG_PRFINVARG, tokens[1]);
+    }
+
+    // All OK - unless the prefix has already been set.
+
+    if (prefix_.size() != 0) {
+        throw MessageException(MSG_DUPLPRFX);
+    }
+
+    // Prefix has not been set, so set it and return success.
+
+    prefix_ = tokens[1];
+}
+
+// Process message.  By the time this method is called, the line has been
+// stripped of leading and trailing spaces, and we believe that it is a line
+// defining a message.  The first token on the line is convered to uppercase
+// and becomes the message ID; the rest of the line is the message text.
+
+void
+MessageReader::parseMessage(const std::string& text, MessageReader::Mode mode) {
+
+    static string delimiters("\t\n ");   // Delimiters
+
+    // Look for the first delimiter.
+    size_t first_delim = text.find_first_of(delimiters);
+    if (first_delim == string::npos) {
+
+        // Just a single token in the line - this is not valid
+        throw MessageException(MSG_ONETOKEN, text);
+    }
+
+    // Extract the first token into the message ID
+    MessageID ident = text.substr(0, first_delim);
+
+    // Locate the start of the message text
+    size_t first_text = text.find_first_not_of(delimiters, first_delim);
+    if (first_text == string::npos) {
+
+        // ?? This happens if there are trailing delimiters, which should not
+        // occur as we have stripped trailing spaces off the line.  Just treat
+        // this as a single-token error for simplicity's sake.
+        throw MessageException(MSG_ONETOKEN, text);
+    }
+
+    // Add the result to the dictionary and to the non-added list if the add to
+    // the dictionary fails.
+    bool added;
+    if (mode == ADD) {
+        added = dictionary_->add(ident, text.substr(first_text));
+    }
+    else {
+        added = dictionary_->replace(ident, text.substr(first_text));
+    }
+    if (!added) {
+        not_added_.push_back(ident);
+    }
+}
+
+} // namespace log
+} // namespace isc
diff --git a/src/lib/log/message_reader.h b/src/lib/log/message_reader.h
new file mode 100644
index 0000000..84ffce9
--- /dev/null
+++ b/src/lib/log/message_reader.h
@@ -0,0 +1,175 @@
+// 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.
+
+// $Id$
+
+#ifndef __MESSAGE_READER_H
+#define __MESSAGE_READER_H
+
+#include <iostream>
+#include <map>
+#include <string>
+#include <vector>
+
+#include <log/message_dictionary.h>
+#include <log/message_types.h>
+
+namespace isc {
+namespace log {
+
+/// \brief Read Message File
+///
+/// Reads a message file and creates a map of identifier against the text of the
+/// message.  This map can be retrieved for subsequent processing.
+
+class MessageReader {
+public:
+
+    /// \brief Read Mode
+    ///
+    /// If ADD, messages are added to the dictionary if the ID does not exist
+    /// there.  If it does, the ID is added to the dictionary's overflow
+    /// vector.
+    ///
+    /// If REPLACE, the dictionary is only modified if the message ID already
+    /// exists in it.  New message IDs are added to the overflow vector.
+    typedef enum {
+        ADD,
+        REPLACE
+    } Mode;
+
+    /// \brief Visible collection types
+    typedef std::vector<MessageID>   MessageIDCollection;
+
+    /// \brief Constructor
+    ///
+    /// Default constructor.  All work is done in the main readFile code (so
+    /// that a status return can be returned instead of needing to throw an
+    /// exception).
+    ///
+    /// \param dictionary Dictionary to which messages read read from the file
+    /// are added.  (This should be a local dictionary when the class is used in
+    /// the message compiler, and the global dictionary when used in a server.
+    /// The ownership of the dictionary object is not transferred - the caller
+    /// is responsible for managing the lifetime of the dictionary.
+    MessageReader(MessageDictionary* dictionary = NULL) :
+        dictionary_(dictionary)
+    {}
+
+
+    /// \brief Virtual Destructor
+    virtual ~MessageReader();
+
+
+    /// \brief Get Dictionary
+    ///
+    /// Returns the pointer to the dictionary object.  Note that ownership is
+    /// not transferred - the caller should not delete it.
+    ///
+    /// \return Pointer to current dictionary object
+    MessageDictionary* getDictionary() const {
+        return dictionary_;
+    }
+
+
+    /// \brief Set Dictionary
+    ///
+    /// Sets the current dictionary object.
+    ///
+    /// \param dictionary New dictionary object. The ownership of the dictionary
+    /// object is not transferred - the caller is responsible for managing the
+    /// lifetime of the dictionary.
+    void setDictionary(MessageDictionary* dictionary) {
+        dictionary_ = dictionary;
+    }
+
+
+    /// \brief Read File
+    ///
+    /// This is the main method of the class and reads in the file, parses it,
+    /// and stores the result in the message dictionary.
+    ///
+    /// \param file Name of the message file.
+    /// \param mode Addition mode.  See the description of the "Mode" enum.
+    virtual void readFile(const std::string& file, Mode mode = ADD);
+
+
+    /// \brief Process Line
+    ///
+    /// Parses a text line and adds it to the message map.  Although this is
+    /// for use in readFile, it can also be used to add individual messages
+    /// to the message map.
+    ///
+    /// \param line Line of text to process
+    /// \param mode If a message line, how to add the message to the dictionary.
+    virtual void processLine(const std::string& line, Mode mode = ADD);
+
+
+    /// \brief Get Prefix
+    ///
+    /// \return Argument to the $PREFIX directive (if present)
+    virtual std::string getPrefix() const {
+        return prefix_;
+    }
+
+
+    /// \brief Clear Prefix
+    ///
+    /// Clears the current prefix.
+    virtual void clearPrefix() {
+        prefix_ = "";
+    }
+
+
+    /// \brief Get Not-Added List
+    ///
+    /// Returns the list of IDs that were not added during the last
+    /// read of the file.
+    ///
+    /// \return Collection of messages not added
+    MessageIDCollection getNotAdded() const {
+        return not_added_;
+    }
+
+private:
+
+    /// \brief Handle a Message Definition
+    ///
+    /// Passed a line that should contain a message, this processes that line
+    /// and adds it to the dictionary according to the mode setting.
+    ///
+    /// \param line Line of text
+    /// \param ADD or REPLACE depending on how the reader is operating.  (See
+    /// the description of the Mode typedef for details.)
+    void parseMessage(const std::string& line, Mode mode);
+
+
+    /// \brief Handle Directive
+    ///
+    /// Passed a line starting with a "$", this handles the processing of
+    /// directives.
+    ///
+    /// \param line Line of text that starts with "$",
+    void parseDirective(const std::string& line);
+
+    /// Attributes
+    MessageDictionary*  dictionary_;    ///< Dictionary to add messages to
+    MessageIDCollection not_added_;     ///< List of IDs not added
+    std::string         prefix_;        ///< Input of $PREFIX statement
+};
+
+} // namespace log
+} // namespace isc
+
+#endif // __MESSAGE_READER_H
diff --git a/src/lib/log/message_types.h b/src/lib/log/message_types.h
new file mode 100644
index 0000000..b101401
--- /dev/null
+++ b/src/lib/log/message_types.h
@@ -0,0 +1,32 @@
+// 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.
+
+// $Id$
+
+#ifndef __MESSAGE_TYPES_H
+#define __MESSAGE_TYPES_H
+
+#include <string>
+
+namespace isc {
+namespace log {
+
+typedef std::string MessageID;
+
+} // namespace log
+} // namespace isc
+
+
+
+#endif // __MESSAGE_TYPES_H
diff --git a/src/lib/log/messagedef.cc b/src/lib/log/messagedef.cc
new file mode 100644
index 0000000..7dfa4f6
--- /dev/null
+++ b/src/lib/log/messagedef.cc
@@ -0,0 +1,27 @@
+// File created from messagedef.mes on Mon Jan 17 15:25:32 2011
+
+#include <cstddef>
+#include <log/message_initializer.h>
+
+using namespace isc::log;
+
+namespace {
+
+const char* values[] = {
+    "DUPLPRFX", "duplicate $PREFIX directive found",
+    "IDNOTFND", "could not replace message for '%s': no such message identification",
+    "ONETOKEN", "a line containing a message ID ('%s') and nothing else was found",
+    "OPENIN", "unable to open message file %s for input: %s",
+    "OPENOUT", "unable to open %s for output: %s",
+    "PRFEXTRARG", "$PREFIX directive has too many arguments",
+    "PRFINVARG", "$PREFIX directive has an invalid argument ('%s')",
+    "PRFNOARG", "no arguments were given to the $PREFIX directive",
+    "READERR", "error reading from %s: %s",
+    "UNRECDIR", "unrecognised directive '%s'",
+    "WRITERR", "error writing to %s: %s",
+    NULL
+};
+
+} // Anonymous namespace
+
+MessageInitializer messagedef_cc_Mon_Jan_17_15_25_32_2011(values);
diff --git a/src/lib/log/messagedef.h b/src/lib/log/messagedef.h
new file mode 100644
index 0000000..ae0a99d
--- /dev/null
+++ b/src/lib/log/messagedef.h
@@ -0,0 +1,24 @@
+// File created from messagedef.mes on Mon Jan 17 15:25:32 2011
+
+#ifndef __MESSAGEDEF_H
+#define __MESSAGEDEF_H
+
+#include <log/message_types.h>
+
+namespace {
+
+isc::log::MessageID MSG_DUPLPRFX = "DUPLPRFX";
+isc::log::MessageID MSG_IDNOTFND = "IDNOTFND";
+isc::log::MessageID MSG_ONETOKEN = "ONETOKEN";
+isc::log::MessageID MSG_OPENIN = "OPENIN";
+isc::log::MessageID MSG_OPENOUT = "OPENOUT";
+isc::log::MessageID MSG_PRFEXTRARG = "PRFEXTRARG";
+isc::log::MessageID MSG_PRFINVARG = "PRFINVARG";
+isc::log::MessageID MSG_PRFNOARG = "PRFNOARG";
+isc::log::MessageID MSG_READERR = "READERR";
+isc::log::MessageID MSG_UNRECDIR = "UNRECDIR";
+isc::log::MessageID MSG_WRITERR = "WRITERR";
+
+} // Anonymous namespace
+
+#endif // __MESSAGEDEF_H
diff --git a/src/lib/log/messagedef.mes b/src/lib/log/messagedef.mes
new file mode 100644
index 0000000..1535fc6
--- /dev/null
+++ b/src/lib/log/messagedef.mes
@@ -0,0 +1,82 @@
+# 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.
+
+# $Id$
+
+$PREFIX MSG_
+
+# \brief Message Utility Message File
+#
+# This is the source of the set of messages generated by the message and logging
+# components.  The associated .h and .cc files are created by hand from this
+# file though and are not built during the build process; this is to avoid the
+# chicken-and-egg situation where we need the files to build the message
+# compiler, yet we need the compiler to build the files.
+
+DUPLPRFX    duplicate $PREFIX directive found
++ When reading a message file, more than one $PREFIX directive was found.  In
++ this version of the code, such a condition is regarded as an error and the
++ read will be abandonded.
+
+IDNOTFND    could not replace message for '%s': no such message identification
++ During start-up a local message file was read.  A line with the listed
++ message identification was found in the file, but the identification is not
++ one contained in the compiled-in message dictionary.  Either the message
++ identification has been mis-spelled in the file, or the local file was used
++ for an earlier version of the software and the message with that
++ identification has been removed.
++
++ This message may appear a number of times in the file, once for every such
++ unknown mnessage identification.
+
+ONETOKEN    a line containing a message ID ('%s') and nothing else was found
++ Message definitions comprise lines starting with a message identification (a
++ symbolic name for the message) and followed by the text of the message.  This
++ error is generated when a line is found in the message file that contains just
++ the message identification.
+
+OPENIN      unable to open message file %s for input: %s
++ The program was not able to open the specified input message file for the
++ reason given.
+
+OPENOUT     unable to open %s for output: %s
++ The program was not able to open the specified output file for the reason
++ given.
+
+PRFEXTRARG  $PREFIX directive has too many arguments
++ The $PREFIX directive takes a single argument, a prefix to be added to the
++ symbol names when a C++ .h file is created.  This error is generated when the
++ compiler finds a $PREFIX directive with more than one argument.
+
+PRFINVARG   $PREFIX directive has an invalid argument ('%s')
++ The $PREFIX argument is used in a symbol name in a C++ header file.  As such,
++ it must adhere to restrictions on C++ symbol names (e.g. may only contain
++ alphanumeric characters or underscores, and may nor start with a digit).  A
++ $PREFIX directive was found with an argument (given in the message) that
++ violates those restictions.
+
+PRFNOARG    no arguments were given to the $PREFIX directive
++ The $PREFIX directive takes a single argument, a prefix to be added to the
++ symbol names when a C++ .h file is created.  This error is generated when the
++ compiler finds a $PREFIX directive with noa rguments.
+
+READERR     error reading from %s: %s
++ The specified error was encountered reading from the named input file.
+
+UNRECDIR    unrecognised directive '%s'
++ A line starting with a dollar symbol was found, but the first word on the line
++ (shown in the message) was not a recognised message compiler directive.
+
+WRITERR     error writing to %s: %s
++ The specified error was encountered writing to the named output file.
diff --git a/src/lib/log/root_logger_name.cc b/src/lib/log/root_logger_name.cc
new file mode 100644
index 0000000..9378857
--- /dev/null
+++ b/src/lib/log/root_logger_name.cc
@@ -0,0 +1,26 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <string>
+#include <root_logger_name.h>
+
+namespace isc {
+namespace log {
+
+std::string RootLoggerName::name_("");
+
+}
+}
diff --git a/src/lib/log/root_logger_name.h b/src/lib/log/root_logger_name.h
new file mode 100644
index 0000000..80691d1
--- /dev/null
+++ b/src/lib/log/root_logger_name.h
@@ -0,0 +1,66 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#ifndef __ROOT_LOGGER_NAME_H
+#define __ROOT_LOGGER_NAME_H
+
+#include <string>
+
+/// \brief Define Name of Root Logger
+///
+/// In the log4cxx system, the root logger is ".".  The definition for the
+/// BIND-10 system is that the root logger of a program has the name of the
+/// program.  This (trivial) class stores the name of the program in a
+/// location accessible to the logger classes.
+
+namespace isc {
+namespace log {
+
+class RootLoggerName {
+public:
+
+    /// \brief Constructor
+    ///
+    /// Sets the root logger name.  Although the name is static, setting the
+    /// name in the constructor allows static initialization of the name by
+    /// declaring an external instance of the class in the main execution unit.
+    RootLoggerName(const std::string& name) {
+        setName(name);
+    } 
+
+    /// \brief Set Root Logger Name
+    ///
+    /// \param name Name of the root logger.  This should be the program
+    /// name.
+    static void setName(const std::string& name) {
+        name_ = name;
+    }
+
+    /// \brief Get Root Logger Name
+    ///
+    /// \return Name of the root logger.
+    static std::string getName() {
+        return name_;
+    }
+    
+private:
+    static std::string name_;      ///< Name of the root logger
+};
+
+}
+}
+
+#endif // __ROOT_LOGGER_NAME_H
diff --git a/src/lib/log/strutil.cc b/src/lib/log/strutil.cc
new file mode 100644
index 0000000..4b96601
--- /dev/null
+++ b/src/lib/log/strutil.cc
@@ -0,0 +1,138 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <numeric>
+#include <iostream>
+
+#include <string.h>
+#include <strutil.h>
+
+using namespace std;
+
+namespace isc {
+namespace strutil {
+
+// Normalize slashes
+
+void
+normalizeSlash(std::string& name) {
+    if (!name.empty()) {
+        size_t pos = 0;
+        while ((pos = name.find('\\', pos)) != std::string::npos) {
+            name[pos] = '/';
+        }
+    }
+}
+
+// Trim String
+
+string
+trim(const string& instring) {
+    static const char* blanks = " \t\n";
+
+    string retstring = "";
+    if (!instring.empty()) {
+
+        // Search for first non-blank character in the string
+        size_t first = instring.find_first_not_of(blanks);
+        if (first != string::npos) {
+
+            // String not all blanks, so look for last character
+            size_t last = instring.find_last_not_of(blanks);
+
+            // Extract the trimmed substring
+            retstring = instring.substr(first, (last - first + 1));
+        }
+    }
+
+    return retstring;
+}
+
+// Tokenise string.  As noted in the header, this is locally written to avoid
+// another dependency on a Boost library.
+
+vector<string>
+tokens(const std::string text, const std::string& delim) {
+    vector<string> result;
+
+    // Search for the first non-delimiter character
+    size_t start = text.find_first_not_of(delim);
+    while (start != string::npos) {
+
+        // Non-delimiter found, look for next delimiter
+        size_t end = text.find_first_of(delim, start);
+        if (end != string::npos) {
+
+            // Delimiter found, so extract string & search for start of next
+            // non-delimiter segment.
+            result.push_back(text.substr(start, (end - start)));
+            start = text.find_first_not_of(delim, end);
+
+        } else {
+
+            // End of string found, extract rest of string and flag to exit
+            result.push_back(text.substr(start));
+            start = string::npos;
+        }
+    }
+
+    return result;
+}
+
+// Local function to pass to accumulate() for summing up string lengths.
+
+namespace {
+
+size_t
+lengthSum(string::size_type curlen, const string& cur_string) {
+    return (curlen + cur_string.size());
+}
+
+}
+
+// Provide printf-style formatting.
+
+std::string
+format(const std::string& format, const std::vector<std::string>& args) {
+
+    static const string flag = "%s";
+
+    // Initialize return string.  To speed things up, we'll reserve an
+    // appropriate amount of space - current string size, plus length of all
+    // the argument strings, less two characters for each argument (the %s in
+    // the format string is being replaced).
+    string result;
+    size_t length = accumulate(args.begin(), args.end(), format.size(),
+        lengthSum) - (args.size() * flag.size());
+    result.reserve(length);
+
+    // Iterate through replacing all tokens
+    result = format;
+    size_t tokenpos = 0;    // Position of last token replaced
+    int i = 0;              // Index into argument array
+
+    while ((i < args.size()) && (tokenpos != string::npos)) {
+        tokenpos = result.find(flag, tokenpos);
+        if (tokenpos != string::npos) {
+            result.replace(tokenpos, flag.size(), args[i++]);
+        }
+    }
+
+    return result;
+}
+
+} // namespace log
+} // namespace isc
diff --git a/src/lib/log/strutil.h b/src/lib/log/strutil.h
new file mode 100644
index 0000000..cb0b793
--- /dev/null
+++ b/src/lib/log/strutil.h
@@ -0,0 +1,147 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#ifndef __STRUTIL_H
+#define __STRUTIL_H
+
+#include <algorithm>
+#include <cctype>
+#include <string>
+#include <vector>
+
+namespace isc {
+namespace strutil {
+
+/// \brief A Set of C++ Utilities for Manipulating Strings
+
+/// \brief Normalize Backslash
+///
+/// Only relevant to Windows, this replaces all "\" in a string with "/" and
+/// returns the result.  On other systems it is a no-op.  Note that Windows does
+/// recognise file names with the "\" replaced by "/" (at least in system calls,
+/// if not the command line).
+///
+/// \param name Name to be substituted
+void normalizeSlash(std::string& name);
+
+
+/// \brief Trim Leading and Trailing Spaces
+///
+/// Returns a copy of the input string but with any leading or trailing spaces
+/// or tabs removed.
+///
+/// \param instring Input string to modify
+///
+/// \return String with leading and trailing spaces removed
+std::string trim(const std::string& instring);
+
+
+/// \brief Split String into Tokens
+///
+/// Splits a string into tokens (the tokens being delimited by one or more of
+/// the delimiter characters) and returns the tokens in a vector array. Note
+/// that adjacent delimiters are considered to be a single delimiter.
+///
+/// Special cases are:
+/// -# The empty string is considered to be zero tokens.
+/// -# A string comprising nothing but delimiters is considered to be zero
+///    tokens.
+///
+/// The reasoning behind this is that the string can be thought of as having
+/// invisible leading and trailing delimiter characters.  Therefore both cases
+/// reduce to a set of contiguous delimiters, which are considered a single
+/// delimiter (so getting rid of the string).
+///
+/// We could use Boost for this, but this (simple) function eliminates one
+/// dependency in the code.
+///
+/// \param text String to be split.  Passed by value as the internal copy is
+/// altered during the processing.
+/// \param delim Delimiter characters
+///
+/// \return Vector of tokens.
+std::vector<std::string> tokens(const std::string text,
+        const std::string& delim = std::string(" \t\n"));
+
+
+/// \brief Uppercase Character
+///
+/// Used in uppercase() to pass as an argument to std::transform().  The
+/// function std::toupper() can't be used as it takes an "int" as its argument;
+/// this confuses the template expansion mechanism because defererencing a
+/// string::iterator returns a char.
+///
+/// \param chr Character to be upper-cased.
+///
+/// \return Uppercase version of the argument
+inline char toUpper(char chr) {
+    return static_cast<char>(std::toupper(static_cast<int>(chr)));
+}
+
+
+/// \brief Uppercase String
+///
+/// A convenience function to uppercase a string.
+///
+/// \param text String to be upper-cased.
+inline void uppercase(std::string& text) {
+    std::transform(text.begin(), text.end(), text.begin(),
+        isc::strutil::toUpper);
+}
+
+/// \brief Lowercase Character
+///
+/// Used in lowercase() to pass as an argument to std::transform().  The
+/// function std::tolower() can't be used as it takes an "int" as its argument;
+/// this confuses the template expansion mechanism because defererencing a
+/// string::iterator returns a char.
+///
+/// \param chr Character to be lower-cased.
+///
+/// \return Lowercase version of the argument
+inline char toLower(char chr) {
+    return static_cast<char>(std::tolower(static_cast<int>(chr)));
+}
+
+/// \brief Lowercase String
+///
+/// A convenience function to lowercase a string
+///
+/// \param text String to be lower-cased.
+inline void lowercase(std::string& text) {
+    std::transform(text.begin(), text.end(), text.begin(),
+        isc::strutil::toLower);
+}
+
+
+/// \brief Apply Formatting
+///
+/// Given a printf-style format string containing only "%s" place holders
+/// (others are ignored) and a vector of strings, this produces a single string
+/// with the placeholders replaced.
+///
+/// \param format Format string
+/// \param args Vector of argument strings
+///
+/// \return Resultant string
+std::string format(const std::string& format,
+    const std::vector<std::string>& args);
+
+
+} // namespace strutil
+} // namespace isc
+
+#endif // __STRUTIL_H
diff --git a/src/lib/log/tests/Makefile.am b/src/lib/log/tests/Makefile.am
new file mode 100644
index 0000000..01973c9
--- /dev/null
+++ b/src/lib/log/tests/Makefile.am
@@ -0,0 +1,45 @@
+SUBDIRS = .
+
+AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/log -I$(top_builddir)/src/lib/log
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+CLEANFILES = *.gcno *.gcda
+
+TESTS =
+if HAVE_GTEST
+TESTS += run_unittests
+run_unittests_SOURCES  = root_logger_name_unittest.cc
+run_unittests_SOURCES += filename_unittest.cc
+run_unittests_SOURCES += logger_unittest.cc
+run_unittests_SOURCES += message_dictionary_unittest.cc
+run_unittests_SOURCES += message_reader_unittest.cc
+run_unittests_SOURCES += message_initializer_unittest.cc
+run_unittests_SOURCES += message_initializer_unittest_2.cc
+run_unittests_SOURCES += strutil_unittest.cc
+run_unittests_SOURCES += xdebuglevel_unittest.cc
+run_unittests_SOURCES += run_unittests.cc
+run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+run_unittests_LDADD  = $(GTEST_LDADD)
+run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
+run_unittests_LDADD += -llog4cxx
+endif
+
+TESTS += logger_support_test
+logger_support_test_SOURCES = logger_support_test.cc
+logger_support_test_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+logger_support_test_LDFLAGS = $(AM_LDFLAGS)
+logger_support_test_LDADD  = $(top_builddir)/src/lib/log/liblog.la
+
+noinst_PROGRAMS = $(TESTS)
+
+# Additional test using the shell
+PYTESTS = run_time_init_test.sh
+check-local:
+	$(SHELL) $(abs_builddir)/run_time_init_test.sh
diff --git a/src/lib/log/tests/filename_unittest.cc b/src/lib/log/tests/filename_unittest.cc
new file mode 100644
index 0000000..c33be9f
--- /dev/null
+++ b/src/lib/log/tests/filename_unittest.cc
@@ -0,0 +1,181 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id: base64_unittest.cc 2549 2010-07-20 19:09:37Z jinmei $
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include <log/filename.h>
+
+using namespace isc;
+using namespace isc::log;
+using namespace std;
+
+class FilenameTest : public ::testing::Test {
+protected:
+    FilenameTest()
+    {
+    }
+};
+
+
+// Check that the name can be changed
+
+TEST_F(FilenameTest, SetName) {
+    Filename fname("/a/b/c.d");
+    EXPECT_EQ("/a/b/c.d", fname.fullName());
+
+    fname.setName("test.txt");
+    EXPECT_EQ("test.txt", fname.fullName());
+}
+
+
+// Check that the components are split correctly.  This is a check of the
+// private member split() method.
+
+TEST_F(FilenameTest, Components) {
+
+    // Complete name
+    Filename fname("/alpha/beta/gamma.delta");
+    EXPECT_EQ("/alpha/beta/", fname.directory());
+    EXPECT_EQ("gamma", fname.name());
+    EXPECT_EQ(".delta", fname.extension());
+
+    // Directory only
+    fname.setName("/gamma/delta/");
+    EXPECT_EQ("/gamma/delta/", fname.directory());
+    EXPECT_EQ("", fname.name());
+    EXPECT_EQ("", fname.extension());
+
+    // Filename only
+    fname.setName("epsilon");
+    EXPECT_EQ("", fname.directory());
+    EXPECT_EQ("epsilon", fname.name());
+    EXPECT_EQ("", fname.extension());
+
+    // Extension only
+    fname.setName(".zeta");
+    EXPECT_EQ("", fname.directory());
+    EXPECT_EQ("", fname.name());
+    EXPECT_EQ(".zeta", fname.extension());
+
+    // Missing directory
+    fname.setName("eta.theta");
+    EXPECT_EQ("", fname.directory());
+    EXPECT_EQ("eta", fname.name());
+    EXPECT_EQ(".theta", fname.extension());
+
+    // Missing filename
+    fname.setName("/iota/.kappa");
+    EXPECT_EQ("/iota/", fname.directory());
+    EXPECT_EQ("", fname.name());
+    EXPECT_EQ(".kappa", fname.extension());
+
+    // Missing extension
+    fname.setName("lambda/mu/nu");
+    EXPECT_EQ("lambda/mu/", fname.directory());
+    EXPECT_EQ("nu", fname.name());
+    EXPECT_EQ("", fname.extension());
+
+    // Check that the decomposition can occur in the presence of leading and
+    // trailing spaces
+    fname.setName("  lambda/mu/nu\t  ");
+    EXPECT_EQ("lambda/mu/", fname.directory());
+    EXPECT_EQ("nu", fname.name());
+    EXPECT_EQ("", fname.extension());
+
+    // Empty string
+    fname.setName("");
+    EXPECT_EQ("", fname.directory());
+    EXPECT_EQ("", fname.name());
+    EXPECT_EQ("", fname.extension());
+
+    // ... and just spaces
+    fname.setName("  ");
+    EXPECT_EQ("", fname.directory());
+    EXPECT_EQ("", fname.name());
+    EXPECT_EQ("", fname.extension());
+
+    // Check corner cases - where separators are present, but strings are
+    // absent.
+    fname.setName("/");
+    EXPECT_EQ("/", fname.directory());
+    EXPECT_EQ("", fname.name());
+    EXPECT_EQ("", fname.extension());
+
+    fname.setName(".");
+    EXPECT_EQ("", fname.directory());
+    EXPECT_EQ("", fname.name());
+    EXPECT_EQ(".", fname.extension());
+
+    fname.setName("/.");
+    EXPECT_EQ("/", fname.directory());
+    EXPECT_EQ("", fname.name());
+    EXPECT_EQ(".", fname.extension());
+
+    // Note that the space is a valid filename here; only leading and trailing
+    // spaces should be trimmed.
+    fname.setName("/ .");
+    EXPECT_EQ("/", fname.directory());
+    EXPECT_EQ(" ", fname.name());
+    EXPECT_EQ(".", fname.extension());
+
+    fname.setName(" / . ");
+    EXPECT_EQ("/", fname.directory());
+    EXPECT_EQ(" ", fname.name());
+    EXPECT_EQ(".", fname.extension());
+}
+
+// Check that the expansion with a default works.
+
+TEST_F(FilenameTest, ExpandWithDefault) {
+    Filename fname("a.b");
+
+    // These tests also check that the trimming of the default component is
+    // done properly.
+    EXPECT_EQ("/c/d/a.b", fname.expandWithDefault(" /c/d/  "));
+    EXPECT_EQ("/c/d/a.b", fname.expandWithDefault("/c/d/e.f"));
+    EXPECT_EQ("a.b", fname.expandWithDefault("e.f"));
+
+    fname.setName("/a/b/c");
+    EXPECT_EQ("/a/b/c.d", fname.expandWithDefault(".d"));
+    EXPECT_EQ("/a/b/c.d", fname.expandWithDefault("x.d"));
+    EXPECT_EQ("/a/b/c.d", fname.expandWithDefault("/s/t/u.d"));
+    EXPECT_EQ("/a/b/c", fname.expandWithDefault("/s/t/u"));
+
+    fname.setName(".h");
+    EXPECT_EQ("/a/b/c.h", fname.expandWithDefault("/a/b/c.msg"));
+}
+
+// Check that we can use this as a default in expanding a filename
+
+TEST_F(FilenameTest, UseAsDefault) {
+
+    Filename fname("a.b");
+
+    // These tests also check that the trimming of the default component is
+    // done properly.
+    EXPECT_EQ("/c/d/a.b", fname.useAsDefault(" /c/d/  "));
+    EXPECT_EQ("/c/d/e.f", fname.useAsDefault("/c/d/e.f"));
+    EXPECT_EQ("e.f", fname.useAsDefault("e.f"));
+
+    fname.setName("/a/b/c");
+    EXPECT_EQ("/a/b/c.d", fname.useAsDefault(".d"));
+    EXPECT_EQ("/a/b/x.d", fname.useAsDefault("x.d"));
+    EXPECT_EQ("/s/t/u.d", fname.useAsDefault("/s/t/u.d"));
+    EXPECT_EQ("/s/t/u", fname.useAsDefault("/s/t/u"));
+    EXPECT_EQ("/a/b/c", fname.useAsDefault(""));
+}
diff --git a/src/lib/log/tests/localdef.mes b/src/lib/log/tests/localdef.mes
new file mode 100644
index 0000000..98e197d
--- /dev/null
+++ b/src/lib/log/tests/localdef.mes
@@ -0,0 +1,23 @@
+# 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.
+
+# \brief Local Definitions
+#
+# Holds local definitions of some of the messages produced by the program
+# logger_support_test, and is used as input to check that run-time message
+# replacement works.
+
+NOTHERE     this message is not in the global dictionary
+READERR     replacement read error, parameters: '%s' and '%s'
+UNRECDIR    replacement unrecognised directive message, parameter is '%s'
diff --git a/src/lib/log/tests/logger_support_test.cc b/src/lib/log/tests/logger_support_test.cc
new file mode 100644
index 0000000..acca4f6
--- /dev/null
+++ b/src/lib/log/tests/logger_support_test.cc
@@ -0,0 +1,109 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id: $
+
+/// \brief Example Program
+///
+/// Simple example program showing how to use the logger.
+
+#include <unistd.h>
+#include <string.h>
+
+#include <iostream>
+
+#include <log/logger.h>
+#include <log/logger_support.h>
+#include <log/root_logger_name.h>
+
+// Include a set of message definitions.
+#include <log/messagedef.h>
+
+using namespace isc::log;
+
+// Declare root logger and a logger to use an example.
+//RootLoggerName root_name("testing");
+
+RootLoggerName root("alpha");
+Logger logger_ex("example");
+Logger logger_dlm("dlm");
+
+// The program is invoked:
+//
+// logger_support_test [-s severity] [-d level ] [local_file]
+//
+// "severity" is one of "debug", "info", "warn", "error", "fatal"
+// "level" is the debug level, a number between 0 and 99
+// "local_file" is the name of a local file.
+//
+// The program sets the attributes on the root logger.  Looking
+// at the output determines whether the program worked.e root logger.  Looking
+// at the output determines whether the 
+
+int main(int argc, char** argv) {
+
+    Logger::Severity    severity = Logger::INFO;
+    int                 dbglevel = -1;
+    const char*         localfile = NULL;
+    int                 option;
+
+    // Parse options
+    while ((option = getopt(argc, argv, "s:d:")) != -1) {
+        switch (option) {
+            case 's':
+                if (strcmp(optarg, "debug") == 0) {
+                    severity = Logger::DEBUG;
+                } else if (strcmp(optarg, "info") == 0) {
+                    severity = Logger::INFO;
+                } else if (strcmp(optarg, "warn") == 0) {
+                    severity = Logger::WARN;
+                } else if (strcmp(optarg, "error") == 0) {
+                    severity = Logger::ERROR;
+                } else if (strcmp(optarg, "fatal") == 0) {
+                    severity = Logger::FATAL;
+                } else {
+                    std::cout << "Unrecognised severity option: " <<
+                        optarg << "\n";
+                    exit(1);
+                }
+                break;
+
+            case 'd':
+                dbglevel = atoi(optarg);
+                break;
+
+            default:
+                std::cout << "Unrecognised option: " <<
+                    static_cast<char>(option) << "\n";
+        }
+    }
+
+    if (optind < argc) {
+        localfile = argv[optind];
+    }
+
+    // Update the logging parameters
+    runTimeInit(severity, dbglevel, localfile);
+
+    // Log a few messages
+    logger_ex.fatal(MSG_WRITERR, "test1", "42");
+    logger_ex.error(MSG_UNRECDIR, "false");
+    logger_dlm.warn(MSG_READERR, "a.txt", "dummy test");
+    logger_dlm.info(MSG_OPENIN, "example.msg", "dummy test");
+    logger_ex.debug(0, MSG_UNRECDIR, "[abc]");
+    logger_ex.debug(24, MSG_UNRECDIR, "[24]");
+    logger_ex.debug(25, MSG_UNRECDIR, "[25]");
+    logger_ex.debug(26, MSG_UNRECDIR, "[26]");
+    return 0;
+}
diff --git a/src/lib/log/tests/logger_unittest.cc b/src/lib/log/tests/logger_unittest.cc
new file mode 100644
index 0000000..e15ec42
--- /dev/null
+++ b/src/lib/log/tests/logger_unittest.cc
@@ -0,0 +1,395 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id: $
+
+#include <iostream>
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include <log/root_logger_name.h>
+#include <log/logger.h>
+#include <log/messagedef.h>
+
+using namespace isc;
+using namespace isc::log;
+using namespace std;
+
+namespace isc {
+namespace log {
+
+/// \brief Test Logger
+///
+/// This logger is a subclass of the logger class under test, but makes
+/// protected methods public (for testing)
+
+class TestLogger : public Logger {
+public:
+    /// \brief constructor
+    TestLogger(const string& name) : Logger(name, true)
+    {}
+
+    /// \brief Logger Equality
+    bool operator==(const TestLogger& other) {
+        return Logger::operator==(other);
+    }
+
+    /// \brief Logger is Null
+    bool isInitialized() const {
+        return Logger::isInitialized();
+    }
+
+    /// \brief Conversion Between log4cxx Number and BIND-10 Severity
+    Severity convertLevel(int value) {
+        return Logger::convertLevel(value);
+    }
+};
+
+} // namespace log
+} // namespace isc
+
+
+class LoggerTest : public ::testing::Test {
+protected:
+    LoggerTest()
+    {
+    }
+};
+
+
+// Checks that the logger is named correctly.
+
+TEST_F(LoggerTest, Name) {
+
+    // Create a logger
+    RootLoggerName::setName("test1");
+    Logger logger("alpha");
+
+    // ... and check the name
+    EXPECT_EQ(string("test1.alpha"), logger.getName());
+}
+
+// This test attempts to get two instances of a logger with the same name
+// and checks that they are in fact the same logger.
+
+TEST_F(LoggerTest, GetLogger) {
+
+    // Set the root logger name (not strictly needed, but this will be the
+    // case in the program(.
+    RootLoggerName::setName("test2");
+
+    const string name1 = "alpha";
+    const string name2 = "beta";
+
+    // Instantiate two loggers that should be the same
+    TestLogger logger1(name1);
+    TestLogger logger2(name1);
+
+    // And check they are null at this point.
+    EXPECT_FALSE(logger1.isInitialized());
+    EXPECT_FALSE(logger2.isInitialized());
+
+    // Do some random operation
+    EXPECT_TRUE(logger1.isFatalEnabled());
+    EXPECT_TRUE(logger2.isFatalEnabled());
+
+    // And check they initialized and equal
+    EXPECT_TRUE(logger1.isInitialized());
+    EXPECT_TRUE(logger2.isInitialized());
+    EXPECT_TRUE(logger1 == logger2);
+
+    // Instantiate another logger with another name and check that it
+    // is different to the previously instantiated ones.
+    TestLogger logger3(name2);
+    EXPECT_FALSE(logger3.isInitialized());
+    EXPECT_TRUE(logger3.isFatalEnabled());
+    EXPECT_TRUE(logger3.isInitialized());
+    EXPECT_FALSE(logger1 == logger3);
+}
+
+// Test the number to severity conversion function
+
+TEST_F(LoggerTest, ConvertLevel) {
+
+    // Create a logger
+    RootLoggerName::setName("test3");
+    TestLogger logger("alpha");
+
+    // Basic 1:1
+    EXPECT_EQ(Logger::DEBUG, logger.convertLevel(log4cxx::Level::DEBUG_INT));
+    EXPECT_EQ(Logger::INFO, logger.convertLevel(log4cxx::Level::INFO_INT));
+    EXPECT_EQ(Logger::WARN, logger.convertLevel(log4cxx::Level::WARN_INT));
+    EXPECT_EQ(Logger::WARN, logger.convertLevel(log4cxx::Level::WARN_INT));
+    EXPECT_EQ(Logger::ERROR, logger.convertLevel(log4cxx::Level::ERROR_INT));
+    EXPECT_EQ(Logger::FATAL, logger.convertLevel(log4cxx::Level::FATAL_INT));
+    EXPECT_EQ(Logger::FATAL, logger.convertLevel(log4cxx::Level::FATAL_INT));
+    EXPECT_EQ(Logger::NONE, logger.convertLevel(log4cxx::Level::OFF_INT));
+
+    // Now some debug levels
+    EXPECT_EQ(Logger::DEBUG,
+        logger.convertLevel(log4cxx::Level::DEBUG_INT - 1));
+    EXPECT_EQ(Logger::DEBUG,
+        logger.convertLevel(log4cxx::Level::DEBUG_INT - MAX_DEBUG_LEVEL));
+    EXPECT_EQ(Logger::DEBUG,
+        logger.convertLevel(log4cxx::Level::DEBUG_INT - 2 * MAX_DEBUG_LEVEL));
+}
+
+// Check that the logger levels are get set properly.
+
+TEST_F(LoggerTest, Severity) {
+
+    // Create a logger
+    RootLoggerName::setName("test3");
+    TestLogger logger("alpha");
+
+    // Now check the levels
+    logger.setSeverity(Logger::NONE);
+    EXPECT_EQ(Logger::NONE, logger.getSeverity());
+
+    logger.setSeverity(Logger::FATAL);
+    EXPECT_EQ(Logger::FATAL, logger.getSeverity());
+
+    logger.setSeverity(Logger::ERROR);
+    EXPECT_EQ(Logger::ERROR, logger.getSeverity());
+
+    logger.setSeverity(Logger::WARN);
+    EXPECT_EQ(Logger::WARN, logger.getSeverity());
+
+    logger.setSeverity(Logger::INFO);
+    EXPECT_EQ(Logger::INFO, logger.getSeverity());
+
+    logger.setSeverity(Logger::DEBUG);
+    EXPECT_EQ(Logger::DEBUG, logger.getSeverity());
+
+    logger.setSeverity(Logger::DEFAULT);
+    EXPECT_EQ(Logger::DEFAULT, logger.getSeverity());
+}
+
+// Check that the debug level is set correctly.
+
+TEST_F(LoggerTest, DebugLevels) {
+
+    // Create a logger
+    RootLoggerName::setName("test4");
+    TestLogger logger("alpha");
+
+    // Debug level should be 0 if not at debug severity
+    logger.setSeverity(Logger::NONE, 20);
+    EXPECT_EQ(0, logger.getDebugLevel());
+
+    logger.setSeverity(Logger::INFO, 42);
+    EXPECT_EQ(0, logger.getDebugLevel());
+
+    // Should be the value set if the severity is set to DEBUG though.
+    logger.setSeverity(Logger::DEBUG, 32);
+    EXPECT_EQ(32, logger.getDebugLevel());
+
+    logger.setSeverity(Logger::DEBUG, 97);
+    EXPECT_EQ(97, logger.getDebugLevel());
+
+    // Try the limits
+    logger.setSeverity(Logger::DEBUG, -1);
+    EXPECT_EQ(0, logger.getDebugLevel());
+
+    logger.setSeverity(Logger::DEBUG, 0);
+    EXPECT_EQ(0, logger.getDebugLevel());
+
+    logger.setSeverity(Logger::DEBUG, 1);
+    EXPECT_EQ(1, logger.getDebugLevel());
+
+    logger.setSeverity(Logger::DEBUG, 98);
+    EXPECT_EQ(98, logger.getDebugLevel());
+
+    logger.setSeverity(Logger::DEBUG, 99);
+    EXPECT_EQ(99, logger.getDebugLevel());
+
+    logger.setSeverity(Logger::DEBUG, 100);
+    EXPECT_EQ(99, logger.getDebugLevel());
+}
+
+// Check that changing the parent and child severity does not affect the
+// other.
+
+TEST_F(LoggerTest, SeverityInheritance) {
+
+    // Create to loggers.  We cheat here as we know that the underlying
+    // implementation (in this case log4cxx) will set a parent-child
+    // relationship if the loggers are named <parent> and <parent>.<child>.
+
+    RootLoggerName::setName("test5");
+    TestLogger parent("alpha");
+    TestLogger child("alpha.beta");
+
+    // By default, newly created loggers should have a level of DEFAULT
+    // (i.e. default to parent)
+    EXPECT_EQ(Logger::DEFAULT, parent.getSeverity());
+    EXPECT_EQ(Logger::DEFAULT, child.getSeverity());
+
+    // Set the severity of the child to something other than the default -
+    // check it changes and that of the parent does not.
+    child.setSeverity(Logger::INFO);
+    EXPECT_EQ(Logger::DEFAULT, parent.getSeverity());
+    EXPECT_EQ(Logger::INFO, child.getSeverity());
+
+    // Reset the child severity and set that of the parent
+    child.setSeverity(Logger::DEFAULT);
+    EXPECT_EQ(Logger::DEFAULT, parent.getSeverity());
+    EXPECT_EQ(Logger::DEFAULT, child.getSeverity());
+    parent.setSeverity(Logger::WARN);
+    EXPECT_EQ(Logger::WARN, parent.getSeverity());
+    EXPECT_EQ(Logger::DEFAULT, child.getSeverity());
+}
+
+// Check that severity is inherited.
+
+TEST_F(LoggerTest, EffectiveSeverityInheritance) {
+
+    // Create to loggers.  We cheat here as we know that the underlying
+    // implementation (in this case log4cxx) will set a parent-child
+    // relationship if the loggers are named <parent> and <parent>.<child>.
+
+    RootLoggerName::setName("test6");
+    Logger parent("test6");
+    Logger child("test6.beta");
+
+    // By default, newly created loggers should have a level of DEFAULT
+    // (i.e. default to parent) and the root should have a default severity
+    // of INFO.  However, the latter is only enforced when created by the
+    // RootLogger class, so explicitly set it for the parent for now.
+    parent.setSeverity(Logger::INFO);
+    EXPECT_EQ(Logger::INFO, parent.getEffectiveSeverity());
+
+    EXPECT_EQ(Logger::DEFAULT, child.getSeverity());
+    EXPECT_EQ(Logger::INFO, child.getEffectiveSeverity());
+
+    // Set the severity of the child to something other than the default -
+    // check it changes and that of the parent does not.
+    child.setSeverity(Logger::FATAL);
+    EXPECT_EQ(Logger::INFO, parent.getEffectiveSeverity());
+    EXPECT_EQ(Logger::FATAL, child.getEffectiveSeverity());
+
+    // Reset the child severity and check again.
+    child.setSeverity(Logger::DEFAULT);
+    EXPECT_EQ(Logger::INFO, parent.getEffectiveSeverity());
+    EXPECT_EQ(Logger::INFO, child.getEffectiveSeverity());
+
+    // Change the parwnt's severity and check it is reflects in the child.
+    parent.setSeverity(Logger::WARN);
+    EXPECT_EQ(Logger::WARN, parent.getEffectiveSeverity());
+    EXPECT_EQ(Logger::WARN, child.getEffectiveSeverity());
+}
+
+// Test the isXxxxEnabled methods.
+
+TEST_F(LoggerTest, IsXxxEnabled) {
+
+    RootLoggerName::setName("test7");
+    Logger logger("test7");
+
+    logger.setSeverity(Logger::INFO);
+    EXPECT_FALSE(logger.isDebugEnabled());
+    EXPECT_TRUE(logger.isInfoEnabled());
+    EXPECT_TRUE(logger.isWarnEnabled());
+    EXPECT_TRUE(logger.isErrorEnabled());
+    EXPECT_TRUE(logger.isFatalEnabled());
+
+    logger.setSeverity(Logger::WARN);
+    EXPECT_FALSE(logger.isDebugEnabled());
+    EXPECT_FALSE(logger.isInfoEnabled());
+    EXPECT_TRUE(logger.isWarnEnabled());
+    EXPECT_TRUE(logger.isErrorEnabled());
+    EXPECT_TRUE(logger.isFatalEnabled());
+
+    logger.setSeverity(Logger::ERROR);
+    EXPECT_FALSE(logger.isDebugEnabled());
+    EXPECT_FALSE(logger.isInfoEnabled());
+    EXPECT_FALSE(logger.isWarnEnabled());
+    EXPECT_TRUE(logger.isErrorEnabled());
+    EXPECT_TRUE(logger.isFatalEnabled());
+
+    logger.setSeverity(Logger::FATAL);
+    EXPECT_FALSE(logger.isDebugEnabled());
+    EXPECT_FALSE(logger.isInfoEnabled());
+    EXPECT_FALSE(logger.isWarnEnabled());
+    EXPECT_FALSE(logger.isErrorEnabled());
+    EXPECT_TRUE(logger.isFatalEnabled());
+
+    // Check various debug levels
+
+    logger.setSeverity(Logger::DEBUG);
+    EXPECT_TRUE(logger.isDebugEnabled());
+    EXPECT_TRUE(logger.isInfoEnabled());
+    EXPECT_TRUE(logger.isWarnEnabled());
+    EXPECT_TRUE(logger.isErrorEnabled());
+    EXPECT_TRUE(logger.isFatalEnabled());
+
+    logger.setSeverity(Logger::DEBUG, 45);
+    EXPECT_TRUE(logger.isDebugEnabled());
+    EXPECT_TRUE(logger.isInfoEnabled());
+    EXPECT_TRUE(logger.isWarnEnabled());
+    EXPECT_TRUE(logger.isErrorEnabled());
+    EXPECT_TRUE(logger.isFatalEnabled());
+
+    // Create a child logger with no severity set, and check that it reflects
+    // the severity of the parent logger.
+
+    Logger child("test7.child");
+    logger.setSeverity(Logger::FATAL);
+    EXPECT_FALSE(child.isDebugEnabled());
+    EXPECT_FALSE(child.isInfoEnabled());
+    EXPECT_FALSE(child.isWarnEnabled());
+    EXPECT_FALSE(child.isErrorEnabled());
+    EXPECT_TRUE(child.isFatalEnabled());
+
+    logger.setSeverity(Logger::INFO);
+    EXPECT_FALSE(child.isDebugEnabled());
+    EXPECT_TRUE(child.isInfoEnabled());
+    EXPECT_TRUE(child.isWarnEnabled());
+    EXPECT_TRUE(child.isErrorEnabled());
+    EXPECT_TRUE(child.isFatalEnabled());
+}
+
+// Within the Debug level there are 100 debug levels.  Test that we know
+// when to issue a debug message.
+
+TEST_F(LoggerTest, IsDebugEnabledLevel) {
+
+    RootLoggerName::setName("test8");
+    Logger logger("test8");
+
+    int MID_LEVEL = (MIN_DEBUG_LEVEL + MAX_DEBUG_LEVEL) / 2;
+
+    logger.setSeverity(Logger::DEBUG);
+    EXPECT_TRUE(logger.isDebugEnabled(MIN_DEBUG_LEVEL));
+    EXPECT_FALSE(logger.isDebugEnabled(MID_LEVEL));
+    EXPECT_FALSE(logger.isDebugEnabled(MAX_DEBUG_LEVEL));
+
+    logger.setSeverity(Logger::DEBUG, MIN_DEBUG_LEVEL);
+    EXPECT_TRUE(logger.isDebugEnabled(MIN_DEBUG_LEVEL));
+    EXPECT_FALSE(logger.isDebugEnabled(MID_LEVEL));
+    EXPECT_FALSE(logger.isDebugEnabled(MAX_DEBUG_LEVEL));
+
+    logger.setSeverity(Logger::DEBUG, MID_LEVEL);
+    EXPECT_TRUE(logger.isDebugEnabled(MIN_DEBUG_LEVEL));
+    EXPECT_TRUE(logger.isDebugEnabled(MID_LEVEL - 1));
+    EXPECT_TRUE(logger.isDebugEnabled(MID_LEVEL));
+    EXPECT_FALSE(logger.isDebugEnabled(MID_LEVEL + 1));
+    EXPECT_FALSE(logger.isDebugEnabled(MAX_DEBUG_LEVEL));
+
+    logger.setSeverity(Logger::DEBUG, MAX_DEBUG_LEVEL);
+    EXPECT_TRUE(logger.isDebugEnabled(MIN_DEBUG_LEVEL));
+    EXPECT_TRUE(logger.isDebugEnabled(MID_LEVEL));
+    EXPECT_TRUE(logger.isDebugEnabled(MAX_DEBUG_LEVEL));
+}
diff --git a/src/lib/log/tests/message_dictionary_unittest.cc b/src/lib/log/tests/message_dictionary_unittest.cc
new file mode 100644
index 0000000..78aa851
--- /dev/null
+++ b/src/lib/log/tests/message_dictionary_unittest.cc
@@ -0,0 +1,173 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id: base64_unittest.cc 2549 2010-07-20 19:09:37Z jinmei $
+
+#include <cstddef>
+#include <string>
+#include <gtest/gtest.h>
+#include <log/message_dictionary.h>
+#include <log/message_types.h>
+
+using namespace isc;
+using namespace isc::log;
+using namespace std;
+
+class MessageDictionaryTest : public ::testing::Test {
+protected:
+    MessageDictionaryTest() : 
+        alpha_id("ALPHA"), alpha_text("This is alpha"),
+        beta_id("BETA"), beta_text("This is beta"),
+        gamma_id("GAMMA"), gamma_text("This is gamma")
+    {
+    }
+
+    MessageID alpha_id;
+    std::string alpha_text;
+    MessageID beta_id;
+    std::string beta_text;
+    MessageID gamma_id;
+    std::string gamma_text;
+
+};
+
+
+// Check that the global dictionary is a singleton.
+
+TEST_F(MessageDictionaryTest, GlobalTest) {
+    MessageDictionary* global = MessageDictionary::globalDictionary();
+    EXPECT_FALSE(NULL == global);
+
+    MessageDictionary* global2 = MessageDictionary::globalDictionary();
+    EXPECT_EQ(global2, global);
+}
+
+// Check that adding messages works
+
+TEST_F(MessageDictionaryTest, Add) {
+    MessageDictionary dictionary;
+    EXPECT_EQ(0, dictionary.size());
+
+    // Add a few messages and check that we can look them up and that there is
+    // nothing in the overflow vector.
+    EXPECT_TRUE(dictionary.add(alpha_id, alpha_text));
+    EXPECT_TRUE(dictionary.add(beta_id, beta_text));
+    EXPECT_EQ(2, dictionary.size());
+
+    EXPECT_EQ(alpha_text, dictionary.getText(alpha_id));
+    EXPECT_EQ(beta_text, dictionary.getText(beta_id));
+    EXPECT_EQ(string(""), dictionary.getText(gamma_id));
+
+    // Try adding a duplicate with different text.  It should not replace the
+    // current text and the ID should be in the overflow section.
+    EXPECT_FALSE(dictionary.add(alpha_id, gamma_text));
+    EXPECT_EQ(2, dictionary.size());
+}
+
+// Check that replacing messages works.
+
+TEST_F(MessageDictionaryTest, Replace) {
+    MessageDictionary dictionary;
+    EXPECT_EQ(0, dictionary.size());
+
+    // Try to replace a non-existent message
+    EXPECT_FALSE(dictionary.replace(alpha_id, alpha_text));
+    EXPECT_EQ(0, dictionary.size());
+
+    // Add a couple of messages.
+    EXPECT_TRUE(dictionary.add(alpha_id, alpha_text));
+    EXPECT_TRUE(dictionary.add(beta_id, beta_text));
+    EXPECT_EQ(2, dictionary.size());
+
+    // Replace an existing message
+    EXPECT_TRUE(dictionary.replace(alpha_id, gamma_text));
+    EXPECT_EQ(2, dictionary.size());
+    EXPECT_EQ(gamma_text, dictionary.getText(alpha_id));
+
+    // ... and replace non-existent message (but now the dictionary has some
+    // items in it).
+    EXPECT_FALSE(dictionary.replace(gamma_id, alpha_text));
+    EXPECT_EQ(2, dictionary.size());
+    EXPECT_EQ(string(""), dictionary.getText(gamma_id));
+}
+
+// Load test
+
+TEST_F(MessageDictionaryTest, LoadTest) {
+    static const char* data1[] = {
+        "ALPHA", "This is alpha",
+        "BETA", "This is beta",
+        "GAMMA", "This is gamma",
+        NULL
+    };
+
+    static const char* data2[] = {
+        "DELTA", "This is delta",
+        "EPSILON", "This is epsilon",
+        "ETA", NULL
+    };
+
+    MessageDictionary dictionary1;
+    EXPECT_EQ(0, dictionary1.size());
+
+    // Load a dictionary1.
+    vector<MessageID> duplicates = dictionary1.load(data1);
+    EXPECT_EQ(3, dictionary1.size());
+    EXPECT_EQ(string(data1[1]), dictionary1.getText(data1[0]));
+    EXPECT_EQ(string(data1[3]), dictionary1.getText(data1[2]));
+    EXPECT_EQ(string(data1[5]), dictionary1.getText(data1[4]));
+    EXPECT_EQ(0, duplicates.size());
+
+    // Attempt an overwrite
+    duplicates = dictionary1.load(data1);
+    EXPECT_EQ(3, dictionary1.size());
+    EXPECT_EQ(3, duplicates.size());
+
+    // Try a new dictionary but with an incorrect number of elements
+    MessageDictionary dictionary2;
+    EXPECT_EQ(0, dictionary2.size());
+
+    duplicates = dictionary2.load(data2);
+    EXPECT_EQ(2, dictionary2.size());
+    EXPECT_EQ(string(data2[1]), dictionary2.getText(data2[0]));
+    EXPECT_EQ(string(data2[3]), dictionary2.getText(data2[2]));
+    EXPECT_EQ(string(""), dictionary2.getText(data2[4]));
+    EXPECT_EQ(0, duplicates.size());
+}
+
+// Check for some non-existent items
+
+TEST_F(MessageDictionaryTest, Lookups) {
+    static const char* data[] = {
+        "ALPHA", "This is alpha",
+        "BETA", "This is beta",
+        "GAMMA", "This is gamma",
+        NULL
+    };
+
+    MessageDictionary dictionary;
+    vector<MessageID> duplicates = dictionary.load(data);
+    EXPECT_EQ(3, dictionary.size());
+    EXPECT_EQ(0, duplicates.size());
+
+    // Valid lookups
+    EXPECT_EQ(string("This is alpha"), dictionary.getText("ALPHA"));
+    EXPECT_EQ(string("This is beta"), dictionary.getText("BETA"));
+    EXPECT_EQ(string("This is gamma"), dictionary.getText("GAMMA"));
+
+    // ... and invalid ones
+    EXPECT_EQ(string(""), dictionary.getText("XYZZY"));
+    EXPECT_EQ(string(""), dictionary.getText(""));
+    EXPECT_EQ(string(""), dictionary.getText("\n\n\n"));
+}
diff --git a/src/lib/log/tests/message_initializer_unittest.cc b/src/lib/log/tests/message_initializer_unittest.cc
new file mode 100644
index 0000000..6a1019f
--- /dev/null
+++ b/src/lib/log/tests/message_initializer_unittest.cc
@@ -0,0 +1,72 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id: base64_unittest.cc 2549 2010-07-20 19:09:37Z jinmei $
+
+#include <cstddef>
+#include <string>
+#include <gtest/gtest.h>
+#include <log/message_dictionary.h>
+#include <log/message_initializer.h>
+
+using namespace isc;
+using namespace isc::log;
+using namespace std;
+
+// Declare a set of messages to go into the global dictionary.
+
+namespace {
+const char* values1[] = {
+    "GLOBAL1", "global message one",
+    "GLOBAL2", "global message two",
+    NULL
+};
+
+const char* values2[] = {
+    "GLOBAL3", "global message three",
+    "GLOBAL4", "global message four",
+    NULL
+};
+
+}
+
+// Statically initialize the global dictionary with those messages.  Three sets
+// are used to check that the declaration of separate initializer objects really// does combine the messages. (The third set is declared in the separately-
+// compiled file message_identifier_initializer_unittest_2.cc.)
+
+MessageInitializer init_message_initializer_unittest_1(values1);
+MessageInitializer init_message_initializer_unittest_2(values2);
+
+
+class MessageInitializerTest : public ::testing::Test {
+protected:
+    MessageInitializerTest()
+    {
+    }
+};
+
+
+// Check that the global dictionary is initialized with the specified
+// messages.
+
+TEST_F(MessageInitializerTest, MessageTest) {
+    MessageDictionary* global = MessageDictionary::globalDictionary();
+
+    EXPECT_EQ(string("global message one"), global->getText("GLOBAL1"));
+    EXPECT_EQ(string("global message two"), global->getText("GLOBAL2"));
+    EXPECT_EQ(string("global message three"), global->getText("GLOBAL3"));
+    EXPECT_EQ(string("global message four"), global->getText("GLOBAL4"));
+    EXPECT_EQ(string("global message five"), global->getText("GLOBAL5"));
+    EXPECT_EQ(string("global message six"), global->getText("GLOBAL6"));
+}
diff --git a/src/lib/log/tests/message_initializer_unittest_2.cc b/src/lib/log/tests/message_initializer_unittest_2.cc
new file mode 100644
index 0000000..c005033
--- /dev/null
+++ b/src/lib/log/tests/message_initializer_unittest_2.cc
@@ -0,0 +1,41 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id: base64_unittest.cc 2549 2010-07-20 19:09:37Z jinmei $
+
+// The sole purpose of this file is to provide a set of message definitions
+// in a separate compilation unit from the one in which their presence is
+// checked.  This tests that merely declaring the MessageInitializer object
+// is enough to include the definitions in the global dictionary.
+
+#include <log/message_initializer.h>
+
+using namespace isc::log;
+
+// Declare a set of messages to go into the global dictionary.
+
+namespace {
+
+const char* values3[] = {
+    "GLOBAL5", "global message five",
+    "GLOBAL6", "global message six",
+    NULL
+};
+
+}
+
+// Statically initialize the global dictionary with those messages.
+// Three sets are used to check that the declaration of separate
+// initializer objects really does combine the messages.
+MessageInitializer init_message_initializer_unittest_3(values3);
diff --git a/src/lib/log/tests/message_reader_unittest.cc b/src/lib/log/tests/message_reader_unittest.cc
new file mode 100644
index 0000000..2891805
--- /dev/null
+++ b/src/lib/log/tests/message_reader_unittest.cc
@@ -0,0 +1,228 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id: base64_unittest.cc 2549 2010-07-20 19:09:37Z jinmei $
+
+#include <algorithm>
+#include <string>
+#include <gtest/gtest.h>
+
+#include <log/messagedef.h>
+#include <log/message_dictionary.h>
+#include <log/message_exception.h>
+#include <log/message_reader.h>
+
+using namespace isc;
+using namespace isc::log;
+using namespace std;
+
+class MessageReaderTest : public ::testing::Test {
+protected:
+    MessageReaderTest() : dictionary_(), reader_()
+    {
+        dictionary_ = new MessageDictionary();
+        reader_.setDictionary(dictionary_);
+    }
+
+    ~MessageReaderTest() {
+        delete dictionary_;
+    }
+
+    MessageDictionary*  dictionary_;    // Dictionary to add messages to
+    MessageReader       reader_;        // Default reader object
+};
+
+
+// Check the get/set dictionary calls (using a local reader and dictionary).
+
+TEST_F(MessageReaderTest, GetSetDictionary) {
+    MessageReader reader;
+    EXPECT_TRUE(reader.getDictionary() == NULL);
+
+    MessageDictionary dictionary;
+    reader.setDictionary(&dictionary);
+    EXPECT_EQ(&dictionary, reader.getDictionary());
+}
+
+// Check for parsing blank lines and comments.  These should not add to the
+// dictionary and each parse should return success.
+
+TEST_F(MessageReaderTest, BlanksAndComments) {
+
+    // Ensure that the dictionary is empty.
+    EXPECT_EQ(0, dictionary_->size());
+
+    // Add a number of blank lines and comments and check that (a) they are
+    // parsed successfully ...
+    EXPECT_NO_THROW(reader_.processLine(""));
+    EXPECT_NO_THROW(reader_.processLine(" "));
+    EXPECT_NO_THROW(reader_.processLine(" \n "));
+    EXPECT_NO_THROW(reader_.processLine("# This is a comment"));
+    EXPECT_NO_THROW(reader_.processLine("\t\t # Another comment"));
+    EXPECT_NO_THROW(reader_.processLine("  + A description line"));
+    EXPECT_NO_THROW(reader_.processLine("#+ A comment"));
+    EXPECT_NO_THROW(reader_.processLine("  +# A description line"));
+
+    // ... and (b) nothing gets added to either the map or the not-added section.
+    EXPECT_EQ(0, dictionary_->size());
+    vector<MessageID> not_added = reader_.getNotAdded();
+    EXPECT_EQ(0, not_added.size());
+}
+
+
+// Local test to check that processLine generates the right exception.
+
+void
+processLineException(MessageReader& reader, const char* what,
+    MessageID& expected) {
+
+    try {
+        reader.processLine(what);
+        FAIL() << "MessageReader::processLine() should throw an exception " <<
+            " with message ID " << expected << " for '" << what << "'\n";
+    } catch (MessageException& e) {
+        EXPECT_EQ(expected, e.id());
+    } catch (...) {
+        FAIL() << "Unknown exception thrown by MessageReader::processLine()\n";
+    }
+}
+
+// Check that it can parse a prefix
+
+TEST_F(MessageReaderTest, Prefix) {
+
+    // Check that no prefix is present
+    EXPECT_EQ(string(""), reader_.getPrefix());
+
+    // Check that a prefix directive with no argument generates an error.
+    processLineException(reader_, "$PREFIX", MSG_PRFNOARG);
+
+    // Check a prefix with multiple arguments is invalid
+    processLineException(reader_, "$prefix A B", MSG_PRFEXTRARG);
+
+    // Prefixes should be alphanumeric (with underscores) and not start
+    // with a number.
+    processLineException(reader_, "$prefix ab[cd", MSG_PRFINVARG);
+    processLineException(reader_, "$prefix 123", MSG_PRFINVARG);
+    processLineException(reader_, "$prefix 1ABC", MSG_PRFINVARG);
+
+    // A valid prefix should be accepted
+    EXPECT_NO_THROW(reader_.processLine("$PREFIX   dlm__"));
+    EXPECT_EQ(string("DLM__"), reader_.getPrefix());
+
+    // And check that the parser fails on invalid prefixes...
+    processLineException(reader_, "$prefix 1ABC", MSG_PRFINVARG);
+
+    // ... and rejects another valid one
+    processLineException(reader_, "$PREFIX ABC", MSG_DUPLPRFX);
+
+    // Check that we can clear the prefix as well
+    reader_.clearPrefix();
+    EXPECT_EQ(string(""), reader_.getPrefix());
+}
+
+// Check that it can parse a line
+
+TEST_F(MessageReaderTest, ValidMessageAddDefault) {
+
+    // Add a couple of valid messages
+    reader_.processLine("GLOBAL1\t\tthis is message global one\n");
+    reader_.processLine("GLOBAL2 this is message global two");
+
+    // ... and check them
+    EXPECT_EQ(string("this is message global one"),
+        dictionary_->getText("GLOBAL1"));
+    EXPECT_EQ(string("this is message global two"),
+        dictionary_->getText("GLOBAL2"));
+    EXPECT_EQ(2, dictionary_->size());
+
+    // ... and ensure no messages were not added
+    vector<MessageID> not_added = reader_.getNotAdded();
+    EXPECT_EQ(0, not_added.size());
+}
+
+TEST_F(MessageReaderTest, ValidMessageAdd) {
+
+    // Add a couple of valid messages
+    reader_.processLine("GLOBAL1\t\tthis is message global one\n",
+        MessageReader::ADD);
+    reader_.processLine("GLOBAL2 this is message global two",
+        MessageReader::ADD);
+
+    // ... and check them
+    EXPECT_EQ(string("this is message global one"),
+        dictionary_->getText("GLOBAL1"));
+    EXPECT_EQ(string("this is message global two"),
+        dictionary_->getText("GLOBAL2"));
+    EXPECT_EQ(2, dictionary_->size());
+
+    // ... and ensure no messages were not added
+    vector<MessageID> not_added = reader_.getNotAdded();
+    EXPECT_EQ(0, not_added.size());
+}
+
+TEST_F(MessageReaderTest, ValidMessageReplace) {
+
+    dictionary_->add("GLOBAL1", "original global1 message");
+    dictionary_->add("GLOBAL2", "original global2 message");
+
+    // Replace a couple of valid messages
+    reader_.processLine("GLOBAL1\t\tthis is message global one\n",
+        MessageReader::REPLACE);
+    reader_.processLine("GLOBAL2 this is message global two",
+        MessageReader::REPLACE);
+
+    // ... and check them
+    EXPECT_EQ(string("this is message global one"),
+        dictionary_->getText("GLOBAL1"));
+    EXPECT_EQ(string("this is message global two"),
+        dictionary_->getText("GLOBAL2"));
+    EXPECT_EQ(2, dictionary_->size());
+
+    // ... and ensure no messages were not added
+    vector<MessageID> not_added = reader_.getNotAdded();
+    EXPECT_EQ(0, not_added.size());
+}
+
+// Do checks on overflows, although this essentially duplicates the checks
+// in MessageDictionary.
+
+TEST_F(MessageReaderTest, Overflows) {
+
+    // Add a couple of valid messages
+    reader_.processLine("GLOBAL1\t\tthis is message global one\n");
+    reader_.processLine("GLOBAL2 this is message global two");
+
+    // Add a duplicate in ADD mode.
+    reader_.processLine("GLOBAL1\t\tthis is a replacement for global one");
+
+    // Replace a non-existent one in REPLACE mode
+    reader_.processLine("LOCAL\t\tthis is a new message",
+        MessageReader::REPLACE);
+
+    // Check what is in the dictionary.
+    EXPECT_EQ(string("this is message global one"),
+        dictionary_->getText("GLOBAL1"));
+    EXPECT_EQ(string("this is message global two"),
+        dictionary_->getText("GLOBAL2"));
+    EXPECT_EQ(2, dictionary_->size());
+
+    // ... and ensure no overflows
+    vector<MessageID> not_added = reader_.getNotAdded();
+    ASSERT_EQ(2, not_added.size());
+
+    sort(not_added.begin(), not_added.end());
+    EXPECT_EQ(string("GLOBAL1"), not_added[0]);
+    EXPECT_EQ(string("LOCAL"), not_added[1]);
+}
diff --git a/src/lib/log/tests/root_logger_name_unittest.cc b/src/lib/log/tests/root_logger_name_unittest.cc
new file mode 100644
index 0000000..6994dc6
--- /dev/null
+++ b/src/lib/log/tests/root_logger_name_unittest.cc
@@ -0,0 +1,52 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id: base64_unittest.cc 2549 2010-07-20 19:09:37Z jinmei $
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include <log/root_logger_name.h>
+
+using namespace isc;
+using namespace isc::log;
+
+class RootLoggerNameTest : public ::testing::Test {
+protected:
+    RootLoggerNameTest()
+    {
+    }
+};
+
+// Check of the (only) functionality of the class.
+
+TEST_F(RootLoggerNameTest, SetGet) {
+    const std::string name1 = "test1";
+    const std::string name2 = "test2";
+
+    // Check that Set/Get works
+    RootLoggerName::setName(name1);
+    EXPECT_EQ(name1, RootLoggerName::getName());
+
+    // We could not test that the root logger name is initialised
+    // correctly (as there is one instance of it and we don't know
+    // when this test will be run) so to check that setName() actually
+    // does change the name, run the test again with a different name.
+    //
+    // (There was always the outside chance that the root logger name
+    // was initialised with name1 and that setName() has no effect.)
+    RootLoggerName::setName(name2);
+    EXPECT_EQ(name2, RootLoggerName::getName());
+}
diff --git a/src/lib/log/tests/run_time_init_test.sh.in b/src/lib/log/tests/run_time_init_test.sh.in
new file mode 100755
index 0000000..be52ded
--- /dev/null
+++ b/src/lib/log/tests/run_time_init_test.sh.in
@@ -0,0 +1,81 @@
+#!/bin/sh
+# 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.
+
+failcount=0
+localmes=@abs_srcdir@/localdef.mes
+tempfile=@abs_builddir@/run_time_init_test_tempfile_$$
+
+passfail() {
+    if [ $1 -eq 0 ]; then
+        echo "pass"
+    else
+        echo "FAIL"
+    fi
+    failcount=`expr $failcount + $1`
+}
+    
+echo -n "1. runInitTest default parameters: "
+cat > $tempfile << .
+FATAL [alpha.example] WRITERR, error writing to test1: 42
+ERROR [alpha.example] UNRECDIR, unrecognised directive 'false'
+WARN  [alpha.dlm] READERR, error reading from a.txt: dummy test
+INFO  [alpha.dlm] OPENIN, unable to open message file example.msg for input: dummy test
+.
+./logger_support_test | cut -d' ' -f3- | diff $tempfile -
+passfail $?
+
+echo -n "2. Severity filter: "
+cat > $tempfile << .
+FATAL [alpha.example] WRITERR, error writing to test1: 42
+ERROR [alpha.example] UNRECDIR, unrecognised directive 'false'
+.
+./logger_support_test -s error | cut -d' ' -f3- | diff $tempfile -
+passfail $?
+
+echo -n "3. Debug level: "
+cat > $tempfile << .
+FATAL [alpha.example] WRITERR, error writing to test1: 42
+ERROR [alpha.example] UNRECDIR, unrecognised directive 'false'
+WARN  [alpha.dlm] READERR, error reading from a.txt: dummy test
+INFO  [alpha.dlm] OPENIN, unable to open message file example.msg for input: dummy test
+DEBUG [alpha.example] UNRECDIR, unrecognised directive '[abc]'
+DEBUG [alpha.example] UNRECDIR, unrecognised directive '[24]'
+DEBUG [alpha.example] UNRECDIR, unrecognised directive '[25]'
+.
+./logger_support_test -s debug -d 25 | cut -d' ' -f3- | diff $tempfile -
+passfail $?
+
+echo -n "4. Local message replacement: "
+cat > $tempfile << .
+WARN  [alpha.log] IDNOTFND, could not replace message for 'NOTHERE': no such message identification
+FATAL [alpha.example] WRITERR, error writing to test1: 42
+ERROR [alpha.example] UNRECDIR, replacement unrecognised directive message, parameter is 'false'
+WARN  [alpha.dlm] READERR, replacement read error, parameters: 'a.txt' and 'dummy test'
+INFO  [alpha.dlm] OPENIN, unable to open message file example.msg for input: dummy test
+.
+./logger_support_test $localmes | cut -d' ' -f3- | diff $tempfile -
+passfail $?
+
+rm -f $tempfile
+
+if [ $failcount -eq 0 ]; then
+    echo "PASS: run_time_init_test"
+elif [ $failcount -eq 1 ]; then
+    echo "FAIL: run_time_init_test - 1 test failed"
+else
+    echo "FAIL: run_time_init_test - $failcount tests failed"
+fi
+
+exit $failcount
diff --git a/src/lib/log/tests/run_unittests.cc b/src/lib/log/tests/run_unittests.cc
new file mode 100644
index 0000000..b91ce24
--- /dev/null
+++ b/src/lib/log/tests/run_unittests.cc
@@ -0,0 +1,23 @@
+// 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.
+
+// $Id: run_unittests.cc 3020 2010-09-26 03:47:26Z jinmei $
+
+#include <gtest/gtest.h>
+
+int
+main(int argc, char* argv[]) {
+    ::testing::InitGoogleTest(&argc, argv);
+    return (RUN_ALL_TESTS());
+}
diff --git a/src/lib/log/tests/strutil_unittest.cc b/src/lib/log/tests/strutil_unittest.cc
new file mode 100644
index 0000000..6e657f3
--- /dev/null
+++ b/src/lib/log/tests/strutil_unittest.cc
@@ -0,0 +1,216 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id: base64_unittest.cc 2549 2010-07-20 19:09:37Z jinmei $
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include <log/strutil.h>
+
+using namespace isc;
+using namespace std;
+
+class StringUtilTest : public ::testing::Test {
+protected:
+    StringUtilTest()
+    {
+    }
+};
+
+
+// Check for slash replacement
+
+TEST_F(StringUtilTest, Slash) {
+
+    string instring = "";
+    isc::strutil::normalizeSlash(instring);
+    EXPECT_EQ("", instring);
+
+    instring = "C:\\A\\B\\C.D";
+    isc::strutil::normalizeSlash(instring);
+    EXPECT_EQ("C:/A/B/C.D", instring);
+
+    instring = "// \\ //";
+    isc::strutil::normalizeSlash(instring);
+    EXPECT_EQ("// / //", instring);
+}
+
+// Check that leading and trailing space trimming works
+
+TEST_F(StringUtilTest, Trim) {
+
+    // Empty and full string.
+    EXPECT_EQ("", isc::strutil::trim(""));
+    EXPECT_EQ("abcxyz", isc::strutil::trim("abcxyz"));
+
+    // Trim right-most blanks
+    EXPECT_EQ("ABC", isc::strutil::trim("ABC   "));
+    EXPECT_EQ("ABC", isc::strutil::trim("ABC\t\t  \n\t"));
+
+    // Left-most blank trimming
+    EXPECT_EQ("XYZ", isc::strutil::trim("  XYZ"));
+    EXPECT_EQ("XYZ", isc::strutil::trim("\t\t  \tXYZ"));
+
+    // Right and left, with embedded spaces
+    EXPECT_EQ("MN \t OP", isc::strutil::trim("\t\tMN \t OP \t"));
+}
+
+// Check tokenization.  Note that ASSERT_EQ is used to check the size of the
+// returned vector; if not as expected, the following references may be invalid
+// so should not be used.
+
+TEST_F(StringUtilTest, Tokens) {
+    vector<string>  result;
+
+    // Default delimiters
+
+    // Degenerate cases
+    result = isc::strutil::tokens("");          // Empty string
+    EXPECT_EQ(0, result.size());
+
+    result = isc::strutil::tokens(" \n ");      // String is all delimiters
+    EXPECT_EQ(0, result.size());
+
+    result = isc::strutil::tokens("abc");       // String has no delimiters
+    ASSERT_EQ(1, result.size());
+    EXPECT_EQ(string("abc"), result[0]);
+
+    // String containing leading and/or trailing delimiters, no embedded ones.
+    result = isc::strutil::tokens("\txyz");     // One leading delimiter
+    ASSERT_EQ(1, result.size());
+    EXPECT_EQ(string("xyz"), result[0]);
+
+    result = isc::strutil::tokens("\t \nxyz");  // Multiple leading delimiters
+    ASSERT_EQ(1, result.size());
+    EXPECT_EQ(string("xyz"), result[0]);
+
+    result = isc::strutil::tokens("xyz\n");     // One trailing delimiter
+    ASSERT_EQ(1, result.size());
+    EXPECT_EQ(string("xyz"), result[0]);
+
+    result = isc::strutil::tokens("xyz  \t");   // Multiple trailing
+    ASSERT_EQ(1, result.size());
+    EXPECT_EQ(string("xyz"), result[0]);
+
+    result = isc::strutil::tokens("\t xyz \n"); // Leading and trailing
+    ASSERT_EQ(1, result.size());
+    EXPECT_EQ(string("xyz"), result[0]);
+
+    // Embedded delimiters
+    result = isc::strutil::tokens("abc\ndef");  // 2 tokens, one separator
+    ASSERT_EQ(2, result.size());
+    EXPECT_EQ(string("abc"), result[0]);
+    EXPECT_EQ(string("def"), result[1]);
+
+    result = isc::strutil::tokens("abc\t\t\ndef");  // 2 tokens, 3 separators
+    ASSERT_EQ(2, result.size());
+    EXPECT_EQ(string("abc"), result[0]);
+    EXPECT_EQ(string("def"), result[1]);
+
+    result = isc::strutil::tokens("abc\n  \tdef\t\tghi");
+    ASSERT_EQ(3, result.size());                // Multiple tokens, many delims
+    EXPECT_EQ(string("abc"), result[0]);
+    EXPECT_EQ(string("def"), result[1]);
+    EXPECT_EQ(string("ghi"), result[2]);
+
+    // Embedded and non-embedded delimiters
+
+    result = isc::strutil::tokens("\t\t  \nabc\n  \tdef\t\tghi   \n\n");
+    ASSERT_EQ(3, result.size());                // Multiple tokens, many delims
+    EXPECT_EQ(string("abc"), result[0]);
+    EXPECT_EQ(string("def"), result[1]);
+    EXPECT_EQ(string("ghi"), result[2]);
+
+    // Non-default delimiter
+    result = isc::strutil::tokens("alpha/beta/ /gamma//delta/epsilon/", "/");
+    ASSERT_EQ(6, result.size());
+    EXPECT_EQ(string("alpha"), result[0]);
+    EXPECT_EQ(string("beta"), result[1]);
+    EXPECT_EQ(string(" "), result[2]);
+    EXPECT_EQ(string("gamma"), result[3]);
+    EXPECT_EQ(string("delta"), result[4]);
+    EXPECT_EQ(string("epsilon"), result[5]);
+
+    // Non-default delimiters (plural)
+    result = isc::strutil::tokens("+*--alpha*beta+ -gamma**delta+epsilon-+**",
+        "*+-");
+    ASSERT_EQ(6, result.size());
+    EXPECT_EQ(string("alpha"), result[0]);
+    EXPECT_EQ(string("beta"), result[1]);
+    EXPECT_EQ(string(" "), result[2]);
+    EXPECT_EQ(string("gamma"), result[3]);
+    EXPECT_EQ(string("delta"), result[4]);
+    EXPECT_EQ(string("epsilon"), result[5]);
+}
+
+// Changing case
+
+TEST_F(StringUtilTest, ChangeCase) {
+    string mixed("abcDEFghiJKLmno123[]{=+--+]}");
+    string upper("ABCDEFGHIJKLMNO123[]{=+--+]}");
+    string lower("abcdefghijklmno123[]{=+--+]}");
+
+    string test = mixed;
+    isc::strutil::lowercase(test);
+    EXPECT_EQ(lower, test);
+
+    test = mixed;
+    isc::strutil::uppercase(test);
+    EXPECT_EQ(upper, test);
+}
+
+// Formatting
+
+TEST_F(StringUtilTest, Formatting) {
+
+    vector<string> args;
+    args.push_back("arg1");
+    args.push_back("arg2");
+    args.push_back("arg3");
+
+    string format1 = "This is a string with no tokens";
+    EXPECT_EQ(format1, isc::strutil::format(format1, args));
+
+    string format2 = "";    // Empty string
+    EXPECT_EQ(format2, isc::strutil::format(format2, args));
+
+    string format3 = "   ";    // Empty string
+    EXPECT_EQ(format3, isc::strutil::format(format3, args));
+
+    string format4 = "String with %d non-string tokens %lf";
+    EXPECT_EQ(format4, isc::strutil::format(format4, args));
+
+    string format5 = "String with %s correct %s number of tokens %s";
+    string result5 = "String with arg1 correct arg2 number of tokens arg3";
+    EXPECT_EQ(result5, isc::strutil::format(format5, args));
+
+    string format6 = "String with %s too %s few tokens";
+    string result6 = "String with arg1 too arg2 few tokens";
+    EXPECT_EQ(result6, isc::strutil::format(format6, args));
+
+    string format7 = "String with %s too %s many %s tokens %s !";
+    string result7 = "String with arg1 too arg2 many arg3 tokens %s !";
+    EXPECT_EQ(result7, isc::strutil::format(format7, args));
+
+    string format8 = "String with embedded%s%s%stokens";
+    string result8 = "String with embeddedarg1arg2arg3tokens";
+    EXPECT_EQ(result8, isc::strutil::format(format8, args));
+
+    // Handle an empty vector
+    args.clear();
+    string format9 = "%s %s";
+    EXPECT_EQ(format9, isc::strutil::format(format9, args));
+}
diff --git a/src/lib/log/tests/xdebuglevel_unittest.cc b/src/lib/log/tests/xdebuglevel_unittest.cc
new file mode 100644
index 0000000..2cb0952
--- /dev/null
+++ b/src/lib/log/tests/xdebuglevel_unittest.cc
@@ -0,0 +1,205 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id: $
+
+#include <iostream>
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include <log4cxx/level.h>
+#include <log/xdebuglevel.h>
+#include <log/dbglevels.h>
+
+/// \brief XDebugLevel (Debug Extension to Level Class)
+///
+/// The class is an extension of the log4cxx Level class; this set of tests
+/// only test the extensions, they do not test the underlying Level class
+/// itself.
+
+using namespace log4cxx;
+
+class XDebugLevelTest : public ::testing::Test {
+protected:
+    XDebugLevelTest()
+    {
+    }
+};
+
+// Check a basic assertion about the numeric values of the debug levels
+
+TEST_F(XDebugLevelTest, NumericValues) {
+    EXPECT_EQ(XDebugLevel::XDEBUG_MIN_LEVEL_INT, Level::DEBUG_INT);
+    EXPECT_EQ(XDebugLevel::XDEBUG_MAX_LEVEL_INT,
+        Level::DEBUG_INT - MAX_DEBUG_LEVEL);
+
+    // ... and check that assumptions used below - that the debug levels
+    // range from 0 to 99 - are valid.
+    EXPECT_EQ(0, MIN_DEBUG_LEVEL);
+    EXPECT_EQ(99, MAX_DEBUG_LEVEL);
+}
+
+
+// Checks that the main function for generating logging level objects from
+// debug levels is working.
+
+TEST_F(XDebugLevelTest, GetExtendedDebug) {
+
+    // Get a debug level of 0.  This should be the same as the main DEBUG
+    // level.
+    LevelPtr debug0 = XDebugLevel::getExtendedDebug(0);
+    EXPECT_EQ(std::string("DEBUG"), debug0->toString());
+    EXPECT_EQ(Level::DEBUG_INT, debug0->toInt());
+    EXPECT_TRUE(*Level::getDebug() == *debug0);
+
+    // Get an arbitrary debug level in the allowed range.
+    LevelPtr debug32 = XDebugLevel::getExtendedDebug(32);
+    EXPECT_EQ(std::string("DEBUG32"), debug32->toString());
+    EXPECT_TRUE((XDebugLevel::XDEBUG_MIN_LEVEL_INT - 32) == debug32->toInt());
+
+    // Check that a value outside the range gives the nearest level.
+    LevelPtr debug_more = XDebugLevel::getExtendedDebug(MAX_DEBUG_LEVEL + 1);
+    EXPECT_TRUE(*XDebugLevel::getExtendedDebug(MAX_DEBUG_LEVEL) == *debug_more);
+
+    LevelPtr debug_less = XDebugLevel::getExtendedDebug(MIN_DEBUG_LEVEL - 1);
+    EXPECT_TRUE(*XDebugLevel::getExtendedDebug(MIN_DEBUG_LEVEL) == *debug_less);
+}
+
+
+// Creation of a level from an int - should return the default debug level
+// if outside the range.
+
+TEST_F(XDebugLevelTest, FromIntOneArg) {
+
+    // Check that a valid debug level is as expected
+    LevelPtr debug42 = XDebugLevel::toLevel(
+        XDebugLevel::XDEBUG_MIN_LEVEL_INT - 42);
+    EXPECT_TRUE(*XDebugLevel::getExtendedDebug(42) == *debug42);
+
+    // ... and that an invalid one returns an object of type debug.
+    LevelPtr debug_invalid = XDebugLevel::toLevel(Level::getInfo()->toInt());
+    EXPECT_TRUE(*Level::getDebug() == *debug_invalid);
+}
+
+
+// Creation of a level from an int - should return the default level
+// if outside the range.
+
+TEST_F(XDebugLevelTest, FromIntTwoArg) {
+
+    // Check that a valid debug level is as expected
+    LevelPtr debug42 = XDebugLevel::toLevel(
+        (XDebugLevel::XDEBUG_MIN_LEVEL_INT - 42), Level::getFatal());
+    EXPECT_TRUE(*XDebugLevel::getExtendedDebug(42) == *debug42);
+
+    // ... and that an invalid one returns an object of type debug.
+    LevelPtr debug_invalid = XDebugLevel::toLevel(
+        Level::getInfo()->toInt(), Level::getFatal());
+    EXPECT_TRUE(*Level::getFatal() == *debug_invalid);
+}
+
+
+// Creation of a level from a string - should return the default debug level
+// if outside the range.
+
+TEST_F(XDebugLevelTest, FromStringOneArg) {
+
+    // Check that a valid debug levels are as expected
+    LevelPtr debug85 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBUG85"));
+    EXPECT_TRUE(*XDebugLevel::getExtendedDebug(85) == *debug85);
+
+    LevelPtr debug92 = XDebugLevel::toLevelLS(LOG4CXX_STR("debug92"));
+    EXPECT_TRUE(*XDebugLevel::getExtendedDebug(92) == *debug92);
+
+    LevelPtr debug27 = XDebugLevel::toLevelLS(LOG4CXX_STR("Debug27"));
+    EXPECT_TRUE(*XDebugLevel::getExtendedDebug(27) == *debug27);
+
+    LevelPtr debug0 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBUG"));
+    EXPECT_TRUE(*XDebugLevel::getExtendedDebug(0) == *debug0);
+
+    // ... and that an invalid one returns an object of type debug (which is
+    // the equivalent of a debug level 0 object).
+    LevelPtr debug_invalid1 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBU"));
+    EXPECT_TRUE(*XDebugLevel::getExtendedDebug(0) == *debug_invalid1);
+
+    LevelPtr debug_invalid2 = XDebugLevel::toLevelLS(LOG4CXX_STR("EBU"));
+    EXPECT_TRUE(*XDebugLevel::getExtendedDebug(0) == *debug_invalid2);
+
+    LevelPtr debug_invalid3 = XDebugLevel::toLevelLS(LOG4CXX_STR(""));
+    EXPECT_TRUE(*XDebugLevel::getExtendedDebug(0) == *debug_invalid3);
+
+    LevelPtr debug_invalid4 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBUGTEN"));
+    EXPECT_TRUE(*XDebugLevel::getExtendedDebug(0) == *debug_invalid4);
+
+    LevelPtr debug_invalid5 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBUG105"));
+    EXPECT_TRUE(*XDebugLevel::getExtendedDebug(MAX_DEBUG_LEVEL) ==
+        *debug_invalid5);
+
+    LevelPtr debug_invalid6 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBUG-7"));
+    EXPECT_TRUE(*XDebugLevel::getExtendedDebug(MIN_DEBUG_LEVEL) ==
+        *debug_invalid6);
+}
+
+
+// Creation of a level from a string - should return the default level
+// if outside the range.
+
+TEST_F(XDebugLevelTest, FromStringTwoArg) {
+
+    // Check that a valid debug levels are as expected
+    LevelPtr debug85 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBUG85"),
+            Level::getFatal());
+    EXPECT_TRUE(*XDebugLevel::getExtendedDebug(85) == *debug85);
+
+    LevelPtr debug92 = XDebugLevel::toLevelLS(LOG4CXX_STR("debug92"),
+            Level::getFatal());
+    EXPECT_TRUE(*XDebugLevel::getExtendedDebug(92) == *debug92);
+
+    LevelPtr debug27 = XDebugLevel::toLevelLS(LOG4CXX_STR("Debug27"),
+            Level::getFatal());
+    EXPECT_TRUE(*XDebugLevel::getExtendedDebug(27) == *debug27);
+
+    LevelPtr debug0 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBUG"),
+            Level::getFatal());
+    EXPECT_TRUE(*XDebugLevel::getExtendedDebug(0) == *debug0);
+
+    // ... and that an invalid one returns an object of type debug (which is
+    // the equivalent of a debug level 0 object).
+    LevelPtr debug_invalid1 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBU"),
+            Level::getFatal());
+    EXPECT_TRUE(*Level::getFatal() == *debug_invalid1);
+
+    LevelPtr debug_invalid2 = XDebugLevel::toLevelLS(LOG4CXX_STR("EBU"),
+            Level::getFatal());
+    EXPECT_TRUE(*Level::getFatal() == *debug_invalid2);
+
+    LevelPtr debug_invalid3 = XDebugLevel::toLevelLS(LOG4CXX_STR(""),
+            Level::getFatal());
+    EXPECT_TRUE(*Level::getFatal() == *debug_invalid3);
+
+    LevelPtr debug_invalid4 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBUGTEN"),
+            Level::getFatal());
+    EXPECT_TRUE(*Level::getFatal() == *debug_invalid4);
+
+    LevelPtr debug_invalid5 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBUG105"),
+            Level::getFatal());
+    EXPECT_TRUE(*XDebugLevel::getExtendedDebug(MAX_DEBUG_LEVEL) ==
+        *debug_invalid5);
+
+    LevelPtr debug_invalid6 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBUG-7"),
+            Level::getFatal());
+    EXPECT_TRUE(*XDebugLevel::getExtendedDebug(MIN_DEBUG_LEVEL) ==
+        *debug_invalid6);
+}
diff --git a/src/lib/log/xdebuglevel.cc b/src/lib/log/xdebuglevel.cc
new file mode 100644
index 0000000..7dddcff
--- /dev/null
+++ b/src/lib/log/xdebuglevel.cc
@@ -0,0 +1,148 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+ 
+#include <cassert>
+#include <algorithm>
+#include <syslog.h>
+#include <string.h>
+#include <boost/lexical_cast.hpp>
+
+#include <xdebuglevel.h>
+#include <dbglevels.h>
+#include <log4cxx/helpers/stringhelper.h>
+
+using namespace log4cxx;
+using namespace log4cxx::helpers;
+
+// Storage for the logging level objects corresponding to each debug level
+
+bool XDebugLevel::dbglevels_unset_ = true;
+LevelPtr XDebugLevel::dbglevels_[NUM_DEBUG_LEVEL];
+
+// Register the class
+
+IMPLEMENT_LOG4CXX_LEVEL(XDebugLevel)
+
+
+// Create Extended Debug Level Objects
+
+LevelPtr
+XDebugLevel::getExtendedDebug(int level) {
+
+    // Initialize the logging levels corresponding to the possible range of
+    // debug if we have not already done so
+    if (dbglevels_unset_) {
+
+        // Asserting that the minimum debug level is zero - so corresponds
+        // to DEBUG_INT - means that the lowest level is set to main DEBUG
+        // level.  This means that the existing logging level object can be
+        // used.
+        assert(MIN_DEBUG_LEVEL == 0);
+        dbglevels_[0] = Level::getDebug();
+
+        // Create the logging level objects for the rest of the debug levels.
+        // They are given names of the form DEBUG<debug level> (e.g. DEBUG42).
+        // They will all correspond to a syslog level of DEBUG.
+        for (int i = 1; i < NUM_DEBUG_LEVEL; ++i) {
+            std::string name = std::string("DEBUG") +
+                boost::lexical_cast<std::string>(i);
+            dbglevels_[i] = new XDebugLevel(
+                (XDebugLevel::XDEBUG_MIN_LEVEL_INT - i),
+                LOG4CXX_STR(name.c_str()), LOG_DEBUG);
+        }
+        dbglevels_unset_ = false;
+    }
+
+    // Now get the logging level object asked for.  Coerce the debug level to
+    // lie in the acceptable range.
+    int actual = std::max(MIN_DEBUG_LEVEL, std::min(MAX_DEBUG_LEVEL, level));
+
+    // ... and return a pointer to the appropriate logging level object
+    return dbglevels_[actual - MIN_DEBUG_LEVEL];
+}
+
+// Convert an integer (an absolute logging level number, not a debug level) to a
+// logging level object.  If it lies outside the valid range, an object
+// corresponding to the minimum debug value is returned.
+
+LevelPtr
+XDebugLevel::toLevel(int val) {
+    return toLevel(val, getExtendedDebug(MIN_DEBUG_LEVEL));
+}
+
+LevelPtr
+XDebugLevel::toLevel(int val, const LevelPtr& defaultLevel) {
+
+    // Note the reversal of the notion of MIN and MAX - see the header file for
+    // details.
+    if ((val >= XDEBUG_MAX_LEVEL_INT) && (val <= XDEBUG_MIN_LEVEL_INT)) {
+        return getExtendedDebug(XDEBUG_MIN_LEVEL_INT - val);
+    }
+    else {
+        return defaultLevel;
+    }
+}
+
+// Convert string passed to a logging level or return default level.
+
+LevelPtr
+XDebugLevel::toLevelLS(const LogString& sArg) {
+    return toLevelLS(sArg, getExtendedDebug(0));
+}
+
+LevelPtr
+XDebugLevel::toLevelLS(const LogString& sArg, const LevelPtr& defaultLevel) {
+    std::string name = sArg;        // Get to known type
+    size_t length = name.size();    // Length of the string
+
+    if (length < 5) {
+
+        // String can't possibly start DEBUG so we don't know what it is.
+        return defaultLevel;
+    }
+    else {
+        if (strncasecmp(name.c_str(), "DEBUG", 5) == 0) {
+
+            // String starts "DEBUG" (or "debug" or any case mixture).  The
+            // rest of the string -if any - should be a number.
+            if (length == 5) {
+
+                // It is plain "DEBUG".  Take this as level 0.
+                return getExtendedDebug(0);
+            }
+            else {
+
+                // Try converting the remainder to an integer.  The "5" is
+                // the length of the string "DEBUG".  Note that if the number
+                // is outside the rangeof debug levels, it is coerced to the
+                // nearest limit.  Thus a level of DEBUG509 will end up as
+                // if DEBUG99 has been specified.
+                try {
+                    int level = boost::lexical_cast<int>(name.substr(5));
+                    return getExtendedDebug(level);
+                }
+                catch (boost::bad_lexical_cast&) {
+                    return defaultLevel;
+                }
+            }
+        }
+        else {
+
+            // Unknown string - return default.
+            return defaultLevel;
+        }
+    }
+}
diff --git a/src/lib/log/xdebuglevel.h b/src/lib/log/xdebuglevel.h
new file mode 100644
index 0000000..4d28ead
--- /dev/null
+++ b/src/lib/log/xdebuglevel.h
@@ -0,0 +1,164 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#ifndef __XDEBUGLEVEL_H
+#define __XDEBUGLEVEL_H
+
+#include <syslog.h>
+#include <log4cxx/level.h>
+
+#include <dbglevels.h>
+
+namespace log4cxx {
+
+/// \brief Debug Extension to Level Class
+///
+/// Based on the example given in the log4cxx distribution, this extends the
+/// log4cxx Level class to allow 100 debug levels.
+///
+/// First some terminology, as the use of the term "level" gets confusing.  The
+/// code and comments here use the term "level" in two contexts:
+///
+/// Logging level: The category of messages to log.  By default log4cxx defines
+/// the following logging levels: OFF, FATAL, ERROR, WARNING, INFO, DEBUG,
+/// TRACE, ALL.  Within the context of BIND-10, OFF, TRACE and ALL are not used
+/// and the idea of DEBUG has been extended, as will be seen below.
+///
+/// Debug level: This is a number that ranges from 0 to 99 and is used by the
+/// application to control the detail of debug output.  A value of 0 gives the
+/// highest-level debug output; a value of 99 gives the most verbose and most
+/// detailed. Debug messages (or whatever debug level) are only ever output
+/// when the logging level is set to DEBUG.
+///
+///
+/// With log4cxx, the various logging levels have a numeric value associated
+/// with them, such that FATAL > ERROR > WARNING etc.  This suggests that the
+/// idea of debug levels can be incorporated into the existing logging level
+/// scheme by assigning them appropriate numeric values, i.e.
+///
+/// WARNING > INFO > DEBUG(0) > DEBUG(2) > ... > DEBUG(99)
+///
+/// Setting a numeric level of DEBUG enables the basic messages; setting lower
+/// numeric levels will enable progressively more messages.  The lowest debug
+/// level (0) is chosen such that setting the general DEBUG logging level will
+/// automatically select that debug level.
+///
+/// This sub-class is needed because the log4cxx::Level class does not allow
+/// the setting of the numeric value of the current level to something other
+/// than the values enumerated in the class.  It creates a set of log4cxx
+/// logging levels to correspond to the various debug levels.  These levels have
+/// names in the range DEBUG1 to DEBUG99 (the existing Level DEBUG is used for
+/// a debug level of 0), although they are not used in BIND-10: instead the
+/// BIND-10 Logger class treats the logging levels and debug levels separately
+/// and combines them to choose the underlying log4cxx logging level.
+
+
+/// \brief Debug-Extended Level
+
+class XDebugLevel : public Level {
+    DECLARE_LOG4CXX_LEVEL(XDebugLevel)
+
+    /// Array of pointers to logging level objects, one for each debug level.
+    /// The pointer corresponding to a debug level of 0 points to the DEBUG
+    /// logging level object.
+    static LevelPtr dbglevels_[NUM_DEBUG_LEVEL];
+    static bool     dbglevels_unset_;
+
+public:
+
+    // Minimum and maximum debug levels.  Note that XDEBUG_MIN_LEVEL_INT is the
+    // number corresponding to the minimum debug level - and is actually larger
+    // that XDEBUG_MAX_LEVEL_INT, the number corresponding to the maximum debug
+    // level.
+    enum {
+        XDEBUG_MIN_LEVEL_INT = Level::DEBUG_INT - MIN_DEBUG_LEVEL,
+        XDEBUG_MAX_LEVEL_INT = Level::DEBUG_INT - MAX_DEBUG_LEVEL
+    };
+
+    /// \brief Constructor
+    ///
+    /// \param level Numeric value of the logging level.
+    /// \param name Name given to this logging level.
+    /// \param syslogEquivalent The category to be used by syslog when it logs
+    /// an event associated with the specified logging level.
+    XDebugLevel(int level, const LogString& name, int syslogEquivalent) :
+        Level(level, name, syslogEquivalent)
+    {}
+
+    /// \brief Create Logging Level Object
+    ///
+    /// Creates a logging level object corresponding to one of the debug levels.
+    ///
+    /// \param dbglevel The debug level, which ranges from MIN_DEBUG_LEVEL to
+    /// MAX_DEBUG_LEVEL. It is coerced to that range if it lies outside it.
+    ///
+    /// \return Pointer to the desired logging level object.
+    static LevelPtr getExtendedDebug(int dbglevel);
+
+    /// \brief Convert Integer to a Logging Level
+    ///
+    /// Returns a logging level object corresponding to the given value (which
+    /// is an absolute value of a logging level - it is not a debug level).
+    /// If the number is invalid, an object of logging level DEBUG (the
+    /// minimum debug logging level) is returned.
+    ///
+    /// \param val Number to convert to a logging level.  This is an absolute
+    /// logging level number, not a debug level.
+    ///
+    /// \return Pointer to the desired logging level object.
+    static LevelPtr toLevel(int val);
+
+    /// \brief Convert Integer to a Level
+    ///
+    /// Returns a logging level object corresponding to the given value (which
+    /// is an absolute value of a logging level - it is not a debug level).
+    /// If the number is invalid, the given default is returned.
+    ///
+    /// \param val Number to convert to a logging level.  This is an absolute
+    /// logging level number, not a debug level.
+    /// \param defaultLevel Logging level to return if value is not recognised.
+    ///
+    /// \return Pointer to the desired logging level object.
+    static LevelPtr toLevel(int val, const LevelPtr& defaultLevel);
+
+    /// \param Convert String to Logging Level
+    ///
+    /// Returns a logging level object corresponding to the given name.  If the
+    /// name is invalid, an object of logging level DEBUG (the minimum debug
+    /// logging level) is returned.
+    ///
+    /// \param sArg Name of the logging level.
+    ///
+    /// \return Pointer to the desired logging level object.
+    static LevelPtr toLevelLS(const LogString& sArg);
+
+    /// \param Convert String to Logging Level
+    ///
+    /// Returns a logging level object corresponding to the given name.  If the
+    /// name is invalid, the given default is returned.
+    ///
+    /// \param sArg name of the level.
+    /// \param defaultLevel Logging level to return if name doesn't exist.
+    ///
+    /// \return Pointer to the desired logging level object.
+    static LevelPtr toLevelLS(const LogString& sArg,
+        const LevelPtr& defaultLevel);
+};
+
+} // namespace log4cxx
+
+
+#endif // __XDEBUGLEVEL_H
diff --git a/src/lib/nsas/Makefile.am b/src/lib/nsas/Makefile.am
index 02f4132..a88bd22 100644
--- a/src/lib/nsas/Makefile.am
+++ b/src/lib/nsas/Makefile.am
@@ -35,7 +35,6 @@ libnsas_la_SOURCES += nsas_entry.h nsas_types.h
 libnsas_la_SOURCES += zone_entry.cc zone_entry.h
 libnsas_la_SOURCES += fetchable.h
 libnsas_la_SOURCES += address_request_callback.h
-libnsas_la_SOURCES += resolver_interface.h
 libnsas_la_SOURCES += random_number_generator.h
 
 CLEANFILES = *.gcno *.gcda
diff --git a/src/lib/nsas/address_entry.cc b/src/lib/nsas/address_entry.cc
index 04b1747..24b0dd9 100644
--- a/src/lib/nsas/address_entry.cc
+++ b/src/lib/nsas/address_entry.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 /// \file address_entry.cc
 ///
 /// This file exists to define the single constant \c AddressEntry::UNREACHABLE,
diff --git a/src/lib/nsas/address_entry.h b/src/lib/nsas/address_entry.h
index 293c46e..148d479 100644
--- a/src/lib/nsas/address_entry.h
+++ b/src/lib/nsas/address_entry.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __ADDRESS_ENTRY_H
 #define __ADDRESS_ENTRY_H
 
diff --git a/src/lib/nsas/address_request_callback.h b/src/lib/nsas/address_request_callback.h
index a24a8ee..ad0630e 100644
--- a/src/lib/nsas/address_request_callback.h
+++ b/src/lib/nsas/address_request_callback.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __ADDRESS_REQUEST_CALLBACK_H
 #define __ADDRESS_REQUEST_CALLBACK_H
 
diff --git a/src/lib/nsas/asiolink.h b/src/lib/nsas/asiolink.h
index 3f1e7dc..b99ddb3 100644
--- a/src/lib/nsas/asiolink.h
+++ b/src/lib/nsas/asiolink.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __ASIOLINK_H
 #define __ASIOLINK_H
 
diff --git a/src/lib/nsas/fetchable.h b/src/lib/nsas/fetchable.h
index fffff20..e828611 100644
--- a/src/lib/nsas/fetchable.h
+++ b/src/lib/nsas/fetchable.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $id$
-
 #ifndef __FETCHABLE_H
 #define __FETCHABLE_H
 
diff --git a/src/lib/nsas/hash.cc b/src/lib/nsas/hash.cc
index 33dfc7c..dbd8eec 100644
--- a/src/lib/nsas/hash.cc
+++ b/src/lib/nsas/hash.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 /*! \file
  * Some parts of this code were copied from BIND-9, which in turn was derived
  * from universal hash function libraries of Rice University.
diff --git a/src/lib/nsas/hash.h b/src/lib/nsas/hash.h
index 6913dfc..0290c26 100644
--- a/src/lib/nsas/hash.h
+++ b/src/lib/nsas/hash.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __HASH_H
 #define __HASH_H
 
diff --git a/src/lib/nsas/hash_deleter.h b/src/lib/nsas/hash_deleter.h
index b112707..29a32d7 100644
--- a/src/lib/nsas/hash_deleter.h
+++ b/src/lib/nsas/hash_deleter.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __HASH_DELETER_H
 #define __HASH_DELETER_H
 
diff --git a/src/lib/nsas/hash_key.cc b/src/lib/nsas/hash_key.cc
index 813dd12..bf4676b 100644
--- a/src/lib/nsas/hash_key.cc
+++ b/src/lib/nsas/hash_key.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <cstring>
 
 #include <config.h>
diff --git a/src/lib/nsas/hash_key.h b/src/lib/nsas/hash_key.h
index a8e5d1d..c89b327 100644
--- a/src/lib/nsas/hash_key.h
+++ b/src/lib/nsas/hash_key.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __HASH_KEY_H
 #define __HASH_KEY_H
 
diff --git a/src/lib/nsas/hash_table.h b/src/lib/nsas/hash_table.h
index f415140..3c34ee9 100644
--- a/src/lib/nsas/hash_table.h
+++ b/src/lib/nsas/hash_table.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __HASH_TABLE_H
 #define __HASH_TABLE_H
 
diff --git a/src/lib/nsas/lru_list.h b/src/lib/nsas/lru_list.h
index 53875bf..a3e0974 100644
--- a/src/lib/nsas/lru_list.h
+++ b/src/lib/nsas/lru_list.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __LRU_LIST_H
 #define __LRU_LIST_H
 
diff --git a/src/lib/nsas/nameserver_address.cc b/src/lib/nsas/nameserver_address.cc
index 9f5f1fa..b2ed55c 100644
--- a/src/lib/nsas/nameserver_address.cc
+++ b/src/lib/nsas/nameserver_address.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $id$
-
 #include <config.h>
 
 #include "nameserver_address.h"
diff --git a/src/lib/nsas/nameserver_address.h b/src/lib/nsas/nameserver_address.h
index d609844..7752deb 100644
--- a/src/lib/nsas/nameserver_address.h
+++ b/src/lib/nsas/nameserver_address.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __NAMESERVER_ADDRESS_H
 #define __NAMESERVER_ADDRESS_H
 
diff --git a/src/lib/nsas/nameserver_address_store.cc b/src/lib/nsas/nameserver_address_store.cc
index b7482f5..0ba9c8e 100644
--- a/src/lib/nsas/nameserver_address_store.cc
+++ b/src/lib/nsas/nameserver_address_store.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <config.h>
 
 // Workaround for a problem with boost and sunstudio 5.10
@@ -58,8 +56,8 @@ namespace nsas {
 // hash table, on the assumption that three elements is the longest linear
 // search we want to do when looking up names in the hash table.
 NameserverAddressStore::NameserverAddressStore(
-    boost::shared_ptr<ResolverInterface> resolver, uint32_t zonehashsize,
-    uint32_t nshashsize) :
+    boost::shared_ptr<isc::resolve::ResolverInterface> resolver,
+    uint32_t zonehashsize, uint32_t nshashsize) :
     zone_hash_(new HashTable<ZoneEntry>(new NsasEntryCompare<ZoneEntry>,
         zonehashsize)),
     nameserver_hash_(new HashTable<NameserverEntry>(
@@ -80,8 +78,9 @@ namespace {
  * called at all to create the object, just call the function.
  */
 boost::shared_ptr<ZoneEntry>
-newZone(const boost::shared_ptr<ResolverInterface>* resolver, const string* zone,
-    const RRClass* class_code,
+newZone(
+    const boost::shared_ptr<isc::resolve::ResolverInterface>* resolver,
+    const string* zone, const RRClass* class_code,
     const boost::shared_ptr<HashTable<NameserverEntry> >* ns_hash,
     const boost::shared_ptr<LruList<NameserverEntry> >* ns_lru)
 {
diff --git a/src/lib/nsas/nameserver_address_store.h b/src/lib/nsas/nameserver_address_store.h
index 4fce3a4..f183871 100644
--- a/src/lib/nsas/nameserver_address_store.h
+++ b/src/lib/nsas/nameserver_address_store.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __NAMESERVER_ADDRESS_STORE_H
 #define __NAMESERVER_ADDRESS_STORE_H
 
@@ -22,6 +20,8 @@
 
 #include <boost/shared_ptr.hpp>
 
+#include <resolve/resolver_interface.h>
+
 #include "nsas_types.h"
 
 namespace isc {
@@ -64,7 +64,8 @@ public:
     /// value of 3001 is the first prime number over 3000, and by implication,
     /// there is an assumption that there will be more nameservers than zones
     /// in the store.
-    NameserverAddressStore(boost::shared_ptr<ResolverInterface> resolver,
+    NameserverAddressStore(
+        boost::shared_ptr<isc::resolve::ResolverInterface> resolver,
         uint32_t zonehashsize = 1009, uint32_t nshashsize = 3001);
 
     /// \brief Destructor
@@ -107,7 +108,7 @@ protected:
     boost::shared_ptr<LruList<NameserverEntry> > nameserver_lru_;
     // The resolver we use
 private:
-    boost::shared_ptr<ResolverInterface> resolver_;
+    boost::shared_ptr<isc::resolve::ResolverInterface> resolver_;
     //}@
 };
 
diff --git a/src/lib/nsas/nameserver_entry.cc b/src/lib/nsas/nameserver_entry.cc
index 5289623..53f4233 100644
--- a/src/lib/nsas/nameserver_entry.cc
+++ b/src/lib/nsas/nameserver_entry.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <config.h>
 
 #include <algorithm>
@@ -32,12 +30,14 @@
 #include <dns/name.h>
 #include <dns/rrclass.h>
 #include <dns/rrttl.h>
+#include <dns/rcode.h>
+#include <dns/opcode.h>
 #include <dns/question.h>
+#include <resolve/resolver_interface.h>
 
 #include "address_entry.h"
 #include "nameserver_address.h"
 #include "nameserver_entry.h"
-#include "resolver_interface.h"
 
 using namespace asiolink;
 using namespace isc::nsas;
@@ -201,7 +201,8 @@ NameserverEntry::setAddressUnreachable(const IOAddress& address) {
  * fed back trough this. It holds a shared pointer to the entry so it is not
  * destroyed too soon.
  */
-class NameserverEntry::ResolverCallback : public ResolverInterface::Callback {
+class NameserverEntry::ResolverCallback :
+        public isc::resolve::ResolverInterface::Callback {
     public:
         ResolverCallback(boost::shared_ptr<NameserverEntry> entry,
             AddressFamily family, const RRType& type) :
@@ -215,11 +216,22 @@ class NameserverEntry::ResolverCallback : public ResolverInterface::Callback {
          * This extracts the addresses out from the response and puts them
          * inside the entry. It tries to reuse the address entries from before (if there were any), to keep their RTTs.
          */
-        virtual void success(const boost::shared_ptr<AbstractRRset>& response) {
+        virtual void success(MessagePtr response_message) {
             time_t now = time(NULL);
 
             Lock lock(entry_->mutex_);
 
+            // TODO: find the correct RRset, not simply the first
+            if (!response_message ||
+                response_message->getRcode() != isc::dns::Rcode::NOERROR() ||
+                response_message->getRRCount(isc::dns::Message::SECTION_ANSWER) == 0) {
+                failureInternal(lock);
+            }
+                
+            isc::dns::RRsetIterator rrsi =
+                response_message->beginSection(isc::dns::Message::SECTION_ANSWER);
+            const isc::dns::RRsetPtr response = *rrsi;
+            
             vector<AddressEntry> entries;
 
             if (response->getType() != type_ ||
@@ -365,7 +377,8 @@ class NameserverEntry::ResolverCallback : public ResolverInterface::Callback {
 };
 
 void
-NameserverEntry::askIP(boost::shared_ptr<ResolverInterface> resolver,
+NameserverEntry::askIP(
+    boost::shared_ptr<isc::resolve::ResolverInterface> resolver,
     const RRType& type, AddressFamily family)
 {
     QuestionPtr question(new Question(Name(getName()), RRClass(getClass()),
@@ -376,7 +389,8 @@ NameserverEntry::askIP(boost::shared_ptr<ResolverInterface> resolver,
 }
 
 void
-NameserverEntry::askIP(boost::shared_ptr<ResolverInterface> resolver,
+NameserverEntry::askIP(
+    boost::shared_ptr<isc::resolve::ResolverInterface> resolver,
     boost::shared_ptr<Callback> callback, AddressFamily family)
 {
     Lock lock(mutex_);
diff --git a/src/lib/nsas/nameserver_entry.h b/src/lib/nsas/nameserver_entry.h
index f3ee1ca..9a8e542 100644
--- a/src/lib/nsas/nameserver_entry.h
+++ b/src/lib/nsas/nameserver_entry.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __NAMESERVER_ENTRY_H
 #define __NAMESERVER_ENTRY_H
 
@@ -38,13 +36,14 @@
 #include <dns/rrset.h>
 #include <dns/rrtype.h>
 
+#include <resolve/resolver_interface.h>
+
 #include "address_entry.h"
 #include "asiolink.h"
 #include "nsas_types.h"
 #include "hash_key.h"
 #include "lru_list.h"
 #include "fetchable.h"
-#include "resolver_interface.h"
 #include "nsas_entry.h"
 #include "nameserver_address.h"
 
@@ -86,7 +85,6 @@ public:
 };
 
 class ZoneEntry;
-class ResolverInterface;
 
 /// \brief Nameserver Entry
 ///
@@ -249,7 +247,7 @@ public:
      *     even when there are addresses, if there are no addresses for this
      *     family.
      */
-    void askIP(boost::shared_ptr<ResolverInterface> resolver,
+    void askIP(boost::shared_ptr<isc::resolve::ResolverInterface> resolver,
         boost::shared_ptr<Callback> callback, AddressFamily family);
     //@}
 
@@ -281,7 +279,7 @@ private:
     /// \short Private version that does the actual asking of one address type
     ///
     /// Call unlocked.
-    void askIP(boost::shared_ptr<ResolverInterface> resolver,
+    void askIP(boost::shared_ptr<isc::resolve::ResolverInterface> resolver,
         const isc::dns::RRType&, AddressFamily);
 };
 
diff --git a/src/lib/nsas/nsas_entry.h b/src/lib/nsas/nsas_entry.h
index 57a2384..f739e8d 100644
--- a/src/lib/nsas/nsas_entry.h
+++ b/src/lib/nsas/nsas_entry.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __NSAS_ENTRY_H
 #define __NSAS_ENTRY_H
 
diff --git a/src/lib/nsas/nsas_entry_compare.h b/src/lib/nsas/nsas_entry_compare.h
index fdad262..9e9ba7d 100644
--- a/src/lib/nsas/nsas_entry_compare.h
+++ b/src/lib/nsas/nsas_entry_compare.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __NSAS_ENTRY_COMPARE_H
 #define __NSAS_ENTRY_COMPARE_H
 
diff --git a/src/lib/nsas/nsas_types.h b/src/lib/nsas/nsas_types.h
index c4d8355..940cc3e 100644
--- a/src/lib/nsas/nsas_types.h
+++ b/src/lib/nsas/nsas_types.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __NSAS_TYPES_H
 #define __NSAS_TYPES_H
 
diff --git a/src/lib/nsas/random_number_generator.h b/src/lib/nsas/random_number_generator.h
index 0837a96..e80ebcb 100644
--- a/src/lib/nsas/random_number_generator.h
+++ b/src/lib/nsas/random_number_generator.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __NSAS_RANDOM_NUMBER_GENERATOR_H
 #define __NSAS_RANDOM_NUMBER_GENERATOR_H
 
diff --git a/src/lib/nsas/resolver_interface.h b/src/lib/nsas/resolver_interface.h
deleted file mode 100644
index 303b8e4..0000000
--- a/src/lib/nsas/resolver_interface.h
+++ /dev/null
@@ -1,80 +0,0 @@
-// Copyright (C) 2010  CZ NIC
-//
-// 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.
-
-// $id$
-
-#ifndef __RESOLVER_INTERFACE_H
-#define __RESOLVER_INTERFACE_H
-
-#include <dns/message.h>
-#include <dns/rrset.h>
-
-/**
- * \file resolver_interface.h
- * \short Temporary interface to resolver.
- *
- * This file contains a dummy interface for the resolver, which does not yet
- * exist. When the resolver appears, this file should either wrap its
- * interface or, better, be removed completely.
- */
-
-namespace isc {
-namespace nsas {
-
-/**
- * \short Abstract interface to the resolver.
- *
- * Abstract interface to the resolver. The NameserverAddressStore uses this
- * to ask for addresses. It is here because resolver does not yet exist.
- *
- * It is abstract to allow tests pass dummy resolvers.
- */
-class ResolverInterface {
-    public:
-        /// \short An abstract callback when data from resolver are ready.
-        class Callback {
-            public:
-                /// \short Some data arrived.
-                virtual void success(
-                    const boost::shared_ptr<isc::dns::AbstractRRset>&
-                    response) = 0;
-                /**
-                 * \short No data available.
-                 *
-                 * \todo Pass some reason.
-                 */
-                virtual void failure() = 0;
-                /// \short Virtual destructor, so descendants are cleaned up
-                virtual ~ Callback() {};
-        };
-        typedef boost::shared_ptr<Callback> CallbackPtr;
-        /**
-         * \short Ask a question.
-         *
-         * Asks the resolver a question. Once the answer is ready
-         * the callback is called.
-         *
-         * \param question What to ask. The resolver will decide who.
-         * \param callback What should happen when the answer is ready.
-         */
-        virtual void resolve(const isc::dns::QuestionPtr& question,
-            const CallbackPtr& callback) = 0;
-        /// \short Virtual destructor, so descendants are properly cleaned up
-        virtual ~ ResolverInterface() {}
-};
-
-} // namespace nsas
-} // namespace isc
-
-#endif //__RESOLVER_INTERFACE_H
diff --git a/src/lib/nsas/tests/address_entry_unittest.cc b/src/lib/nsas/tests/address_entry_unittest.cc
index 62ae383..716068c 100644
--- a/src/lib/nsas/tests/address_entry_unittest.cc
+++ b/src/lib/nsas/tests/address_entry_unittest.cc
@@ -12,7 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
 #include <config.h>
 
 #include <limits.h>
diff --git a/src/lib/nsas/tests/fetchable_unittest.cc b/src/lib/nsas/tests/fetchable_unittest.cc
index 90bfa7b..f94cd16 100644
--- a/src/lib/nsas/tests/fetchable_unittest.cc
+++ b/src/lib/nsas/tests/fetchable_unittest.cc
@@ -12,7 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $id$
 #include <config.h>
 
 #include "../fetchable.h"
diff --git a/src/lib/nsas/tests/hash_deleter_unittest.cc b/src/lib/nsas/tests/hash_deleter_unittest.cc
index 7f7373c..97fecbe 100644
--- a/src/lib/nsas/tests/hash_deleter_unittest.cc
+++ b/src/lib/nsas/tests/hash_deleter_unittest.cc
@@ -12,7 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
 #include <config.h>
 
 #include <algorithm>
diff --git a/src/lib/nsas/tests/hash_key_unittest.cc b/src/lib/nsas/tests/hash_key_unittest.cc
index 884db42..efa5fb2 100644
--- a/src/lib/nsas/tests/hash_key_unittest.cc
+++ b/src/lib/nsas/tests/hash_key_unittest.cc
@@ -12,7 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
 #include <config.h>
 
 #include <algorithm>
diff --git a/src/lib/nsas/tests/hash_table_unittest.cc b/src/lib/nsas/tests/hash_table_unittest.cc
index c92adc4..7ba25b5 100644
--- a/src/lib/nsas/tests/hash_table_unittest.cc
+++ b/src/lib/nsas/tests/hash_table_unittest.cc
@@ -12,7 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
 #include <config.h>
 
 #include <gtest/gtest.h>
diff --git a/src/lib/nsas/tests/hash_unittest.cc b/src/lib/nsas/tests/hash_unittest.cc
index a2d3ade..251e4b1 100644
--- a/src/lib/nsas/tests/hash_unittest.cc
+++ b/src/lib/nsas/tests/hash_unittest.cc
@@ -12,7 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
 #include <config.h>
 
 #include <algorithm>
diff --git a/src/lib/nsas/tests/lru_list_unittest.cc b/src/lib/nsas/tests/lru_list_unittest.cc
index 0a062a2..0161f2b 100644
--- a/src/lib/nsas/tests/lru_list_unittest.cc
+++ b/src/lib/nsas/tests/lru_list_unittest.cc
@@ -12,7 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
 #include <config.h>
 
 #include <iostream>
diff --git a/src/lib/nsas/tests/nameserver_address_store_unittest.cc b/src/lib/nsas/tests/nameserver_address_store_unittest.cc
index fde55d2..95b46a8 100644
--- a/src/lib/nsas/tests/nameserver_address_store_unittest.cc
+++ b/src/lib/nsas/tests/nameserver_address_store_unittest.cc
@@ -12,7 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
 #include <config.h>
 
 /// \brief Test Deleter Objects
@@ -87,7 +86,7 @@ public:
      * if it is asked for by the resolver.
      */
     void lookupAndAnswer(const string& name, const RRClass& class_code,
-        boost::shared_ptr<AbstractRRset> authority,
+        RRsetPtr authority,
         boost::shared_ptr<AddressRequestCallback> callback)
     {
         size_t size(resolver_->requests.size());
diff --git a/src/lib/nsas/tests/nameserver_address_unittest.cc b/src/lib/nsas/tests/nameserver_address_unittest.cc
index 516d785..35a46f0 100644
--- a/src/lib/nsas/tests/nameserver_address_unittest.cc
+++ b/src/lib/nsas/tests/nameserver_address_unittest.cc
@@ -12,7 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
 #include <config.h>
 
 #include <gtest/gtest.h>
@@ -40,7 +39,7 @@ class NameserverEntrySample {
 public:
     NameserverEntrySample():
         name_("example.org"),
-        rrv4_(new BasicRRset(name_, RRClass::IN(), RRType::A(), RRTTL(1200)))
+        rrv4_(new RRset(name_, RRClass::IN(), RRType::A(), RRTTL(1200)))
     {
         // Add some sample A records
         rrv4_->addRdata(ConstRdataPtr(new RdataTest<A>("1.2.3.4")));
@@ -51,7 +50,7 @@ public:
         boost::shared_ptr<TestResolver> resolver(new TestResolver);
         ns_->askIP(resolver, boost::shared_ptr<Callback>(new Callback), ANY_OK);
         resolver->asksIPs(name_, 0, 1);
-        resolver->requests[0].second->success(rrv4_);
+        resolver->requests[0].second->success(createResponseMessage(rrv4_));
     }
 
     // Return the sample NameserverEntry
@@ -74,7 +73,7 @@ public:
 
 private:
     Name name_;                             ///< Name of the sample
-    boost::shared_ptr<BasicRRset> rrv4_;           ///< Standard RRSet - IN, A, lowercase name
+    RRsetPtr rrv4_;           ///< Standard RRSet - IN, A, lowercase name
     boost::shared_ptr<NameserverEntry> ns_; ///< Shared_ptr that points to a NameserverEntry object
 
     class Callback : public NameserverEntry::Callback {
diff --git a/src/lib/nsas/tests/nameserver_entry_unittest.cc b/src/lib/nsas/tests/nameserver_entry_unittest.cc
index 6e1213e..9e4cec7 100644
--- a/src/lib/nsas/tests/nameserver_entry_unittest.cc
+++ b/src/lib/nsas/tests/nameserver_entry_unittest.cc
@@ -12,7 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
 #include <config.h>
 
 #include <iostream>
@@ -70,10 +69,10 @@ private:
      *     as a failure.
      */
     void fillSet(boost::shared_ptr<TestResolver> resolver, size_t index,
-        boost::shared_ptr<BasicRRset> set)
+        RRsetPtr set)
     {
         if (set) {
-            resolver->requests[index].second->success(set);
+            resolver->requests[index].second->success(createResponseMessage(set));
         } else {
             resolver->requests[index].second->failure();
         }
@@ -81,7 +80,7 @@ private:
 protected:
     /// Fills the nameserver entry with data trough ask IP
     void fillNSEntry(boost::shared_ptr<NameserverEntry> entry,
-        boost::shared_ptr<BasicRRset> rrv4, boost::shared_ptr<BasicRRset> rrv6)
+        RRsetPtr rrv4, RRsetPtr rrv6)
     {
         // Prepare data to run askIP
         boost::shared_ptr<TestResolver> resolver(new TestResolver);
@@ -213,13 +212,13 @@ TEST_F(NameserverEntryTest, ExpirationTime) {
     // Test where there is a single TTL
     boost::shared_ptr<NameserverEntry> alpha(new NameserverEntry(EXAMPLE_CO_UK,
         RRClass::IN()));
-    fillNSEntry(alpha, rrv4_, boost::shared_ptr<BasicRRset>());
+    fillNSEntry(alpha, rrv4_, RRsetPtr());
     expiration = alpha->getExpiration();
     EXPECT_EQ(expiration, curtime + rrv4_->getTTL().getValue());
 
     boost::shared_ptr<NameserverEntry> beta(new NameserverEntry(EXAMPLE_CO_UK,
         RRClass::IN()));
-    fillNSEntry(beta, boost::shared_ptr<BasicRRset>(), rrv6_);
+    fillNSEntry(beta, RRsetPtr(), rrv6_);
     expiration = beta->getExpiration();
     EXPECT_EQ(expiration, curtime + rrv6_->getTTL().getValue());
 
@@ -238,7 +237,7 @@ TEST_F(NameserverEntryTest, ExpirationTime) {
 
     boost::shared_ptr<NameserverEntry> delta(new NameserverEntry(EXAMPLE_CO_UK,
         RRClass::IN()));
-    fillNSEntry(delta, rrv4_, boost::shared_ptr<BasicRRset>());
+    fillNSEntry(delta, rrv4_, RRsetPtr());
     EXPECT_GT(delta->getExpiration(), rrv4_->getTTL().getValue());
 }
 
@@ -348,9 +347,9 @@ TEST_F(NameserverEntryTest, DirectAnswer) {
     resolver->addPresetAnswer(Question(Name(EXAMPLE_CO_UK), RRClass::IN(),
         RRType::AAAA()), rrv6_);
     resolver->addPresetAnswer(Question(Name(EXAMPLE_NET), RRClass::IN(),
-        RRType::A()), boost::shared_ptr<AbstractRRset>());
+        RRType::A()), RRsetPtr());
     resolver->addPresetAnswer(Question(Name(EXAMPLE_NET), RRClass::IN(),
-        RRType::AAAA()), boost::shared_ptr<AbstractRRset>());
+        RRType::AAAA()), RRsetPtr());
 
     // A successfull test first
     entry->askIP(resolver, callback, ANY_OK);
diff --git a/src/lib/nsas/tests/nsas_entry_compare_unittest.cc b/src/lib/nsas/tests/nsas_entry_compare_unittest.cc
index e95411f..33e41b8 100644
--- a/src/lib/nsas/tests/nsas_entry_compare_unittest.cc
+++ b/src/lib/nsas/tests/nsas_entry_compare_unittest.cc
@@ -12,7 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
 #include <config.h>
 
 #include <algorithm>
diff --git a/src/lib/nsas/tests/nsas_test.h b/src/lib/nsas/tests/nsas_test.h
index 7fc548f..b4446a4 100644
--- a/src/lib/nsas/tests/nsas_test.h
+++ b/src/lib/nsas/tests/nsas_test.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __NSAS_TEST_H
 #define __NSAS_TEST_H
 
@@ -27,18 +25,33 @@
 
 #include <config.h>
 
+#include <dns/message.h>
 #include <dns/buffer.h>
 #include <dns/rdata.h>
 #include <dns/rrtype.h>
 #include <dns/rrttl.h>
+#include <dns/opcode.h>
+#include <dns/rcode.h>
 #include <dns/messagerenderer.h>
 #include <dns/rdataclass.h>
+#include <resolve/resolver_interface.h>
 #include "../nsas_entry.h"
-#include "../resolver_interface.h"
 
 using namespace isc::dns::rdata;
 using namespace isc::dns;
 
+namespace {
+    MessagePtr
+    createResponseMessage(RRsetPtr answer_rrset)
+    {
+        MessagePtr response(new Message(Message::RENDER));
+        response->setOpcode(Opcode::QUERY());
+        response->setRcode(Rcode::NOERROR());
+        response->addRRset(Message::SECTION_ANSWER, answer_rrset);
+        return response;
+    }
+}
+
 namespace isc {
 namespace dns {
 
@@ -219,13 +232,13 @@ using namespace std;
  * This pretends to be a resolver. It stores the queries and
  * they can be answered.
  */
-class TestResolver : public isc::nsas::ResolverInterface {
+class TestResolver : public isc::resolve::ResolverInterface {
     private:
         bool checkIndex(size_t index) {
             return (requests.size() > index);
         }
 
-        typedef std::map<isc::dns::Question, boost::shared_ptr<AbstractRRset> >
+        typedef std::map<isc::dns::Question, RRsetPtr >
             PresetAnswers;
         PresetAnswers answers_;
     public:
@@ -237,7 +250,7 @@ class TestResolver : public isc::nsas::ResolverInterface {
                 requests.push_back(Request(q, c));
             } else {
                 if (it->second) {
-                    c->success(it->second);
+                    c->success(createResponseMessage(it->second));
                 } else {
                     c->failure();
                 }
@@ -250,7 +263,7 @@ class TestResolver : public isc::nsas::ResolverInterface {
          * it goes to requests and you can answer later.
          */
         void addPresetAnswer(const isc::dns::Question& question,
-            boost::shared_ptr<AbstractRRset> answer)
+            RRsetPtr answer)
         {
             answers_[question] = answer;
         }
@@ -310,11 +323,11 @@ class TestResolver : public isc::nsas::ResolverInterface {
             RRsetPtr set(new RRset(name, RRClass::IN(),
                 type, RRTTL(TTL)));
             set->addRdata(rdata);
-            requests[index].second->success(set);
+            requests[index].second->success(createResponseMessage(set));
         }
 
         void provideNS(size_t index,
-            boost::shared_ptr<AbstractRRset> nameservers)
+            RRsetPtr nameservers)
         {
             if (index >= requests.size()) {
                 throw NoSuchRequest();
@@ -324,7 +337,7 @@ class TestResolver : public isc::nsas::ResolverInterface {
             {
                 throw DifferentRequest();
             }
-            requests[index].second->success(nameservers);
+            requests[index].second->success(createResponseMessage(nameservers));
         }
 };
 
diff --git a/src/lib/nsas/tests/random_number_generator_unittest.cc b/src/lib/nsas/tests/random_number_generator_unittest.cc
index 6bb5773..c306b09 100644
--- a/src/lib/nsas/tests/random_number_generator_unittest.cc
+++ b/src/lib/nsas/tests/random_number_generator_unittest.cc
@@ -12,7 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
 #include <config.h>
 
 #include <gtest/gtest.h>
diff --git a/src/lib/nsas/tests/run_unittests.cc b/src/lib/nsas/tests/run_unittests.cc
index 52f3e13..d1277ad 100644
--- a/src/lib/nsas/tests/run_unittests.cc
+++ b/src/lib/nsas/tests/run_unittests.cc
@@ -12,7 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id: run_unittests.cc 3020 2010-09-26 03:47:26Z jinmei $
 #include <config.h>
 
 #include <gtest/gtest.h>
diff --git a/src/lib/nsas/tests/zone_entry_unittest.cc b/src/lib/nsas/tests/zone_entry_unittest.cc
index 0fe5f34..8a3c6f2 100644
--- a/src/lib/nsas/tests/zone_entry_unittest.cc
+++ b/src/lib/nsas/tests/zone_entry_unittest.cc
@@ -12,7 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
 #include <config.h>
 
 #include <gtest/gtest.h>
@@ -43,7 +42,8 @@ namespace {
 /// \brief Inherited version with access into its internals for tests
 class InheritedZoneEntry : public ZoneEntry {
     public:
-        InheritedZoneEntry(boost::shared_ptr<ResolverInterface> resolver,
+        InheritedZoneEntry(
+            boost::shared_ptr<isc::resolve::ResolverInterface> resolver,
             const std::string& name, const RRClass& class_code,
             boost::shared_ptr<HashTable<NameserverEntry> > nameserver_table,
             boost::shared_ptr<LruList<NameserverEntry> > nameserver_lru) :
@@ -460,7 +460,7 @@ TEST_F(ZoneEntryTest, DirectAnswer) {
 
     // One unsuccessfull attempt, nameservers fail
     resolver_->addPresetAnswer(Question(Name(EXAMPLE_CO_UK), RRClass::IN(),
-        RRType::NS()), boost::shared_ptr<AbstractRRset>());
+        RRType::NS()), RRsetPtr());
     zone->addCallback(callback_, ANY_OK);
     EXPECT_EQ(0, callback_->successes_.size());
     EXPECT_EQ(1, callback_->unreachable_count_);
@@ -494,9 +494,9 @@ TEST_F(ZoneEntryTest, DirectAnswer) {
     callback_->successes_.clear();
     // Now, pretend we do not have IP addresses
     resolver_->addPresetAnswer(Question(ns_name, RRClass::IN(), RRType::A()),
-        boost::shared_ptr<AbstractRRset>());
+        RRsetPtr());
     resolver_->addPresetAnswer(Question(ns_name, RRClass::IN(),
-        RRType::AAAA()), boost::shared_ptr<AbstractRRset>());
+        RRType::AAAA()), RRsetPtr());
     // Get another zone and ask it again. It should fail.
     // Clean the table first, though, so it does not find the old nameserver
     nameserver_table_->remove(HashKey(ns_name.toText(), RRClass::IN()));
diff --git a/src/lib/nsas/zone_entry.cc b/src/lib/nsas/zone_entry.cc
index 718dc22..395b06c 100644
--- a/src/lib/nsas/zone_entry.cc
+++ b/src/lib/nsas/zone_entry.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $id$
-
 #include <config.h>
 
 #include "zone_entry.h"
@@ -24,6 +22,7 @@
 #include <boost/foreach.hpp>
 #include <boost/bind.hpp>
 #include <dns/rrttl.h>
+#include <dns/rcode.h>
 #include <dns/rdataclass.h>
 
 using namespace std;
@@ -34,7 +33,8 @@ using namespace dns;
 
 namespace nsas {
 
-ZoneEntry::ZoneEntry(boost::shared_ptr<ResolverInterface> resolver,
+ZoneEntry::ZoneEntry(
+    boost::shared_ptr<isc::resolve::ResolverInterface> resolver,
     const std::string& name, const isc::dns::RRClass& class_code,
     boost::shared_ptr<HashTable<NameserverEntry> > nameserver_table,
     boost::shared_ptr<LruList<NameserverEntry> > nameserver_lru) :
@@ -75,7 +75,8 @@ newNs(const std::string* name, const RRClass* class_code) {
  * code. It manipulates directly ZoneEntry's data members, locks it and like
  * that. Mostly eliminates C++ bad design of missing lambda functions.
  */
-class ZoneEntry::ResolverCallback : public ResolverInterface::Callback {
+class ZoneEntry::ResolverCallback :
+        public isc::resolve::ResolverInterface::Callback {
     public:
         /// \short Constructor. Pass "this" zone entry
         ResolverCallback(boost::shared_ptr<ZoneEntry> entry) :
@@ -92,8 +93,21 @@ class ZoneEntry::ResolverCallback : public ResolverInterface::Callback {
          * examining them and seeing if some addresses are already there
          * and to ask for the rest of them.
          */
-        virtual void success(const boost::shared_ptr<AbstractRRset>& answer) {
+        virtual void success(MessagePtr response_message) {
             Lock lock(entry_->mutex_);
+
+            // TODO: find the correct RRset, not simply the first
+            if (!response_message ||
+                response_message->getRcode() != isc::dns::Rcode::NOERROR() ||
+                response_message->getRRCount(isc::dns::Message::SECTION_ANSWER) == 0) {
+                // todo: define this
+                failureInternal(300);
+            }
+
+            isc::dns::RRsetIterator rrsi =
+                response_message->beginSection(isc::dns::Message::SECTION_ANSWER);
+            const isc::dns::RRsetPtr answer = *rrsi;
+
             RdataIteratorPtr iterator(answer->getRdataIterator());
             // If there are no data
             if (iterator->isLast()) {
diff --git a/src/lib/nsas/zone_entry.h b/src/lib/nsas/zone_entry.h
index 758f2c9..c105f70 100644
--- a/src/lib/nsas/zone_entry.h
+++ b/src/lib/nsas/zone_entry.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __ZONE_ENTRY_H
 #define __ZONE_ENTRY_H
 
@@ -38,11 +36,12 @@
 
 #include <dns/rrset.h>
 
+#include <resolve/resolver_interface.h>
+
 #include "hash_key.h"
 #include "nsas_entry.h"
 #include "asiolink.h"
 #include "fetchable.h"
-#include "resolver_interface.h"
 #include "nsas_types.h"
 #include "random_number_generator.h"
 
@@ -78,7 +77,8 @@ public:
      * \todo Move to cc file, include the lookup (if NSAS uses resolver for
      *     everything)
      */
-    ZoneEntry(boost::shared_ptr<ResolverInterface> resolver,
+    ZoneEntry(
+        boost::shared_ptr<isc::resolve::ResolverInterface> resolver,
         const std::string& name, const isc::dns::RRClass& class_code,
         boost::shared_ptr<HashTable<NameserverEntry> > nameserver_table,
         boost::shared_ptr<LruList<NameserverEntry> > nameserver_lru);
@@ -153,7 +153,7 @@ private:
     void process(AddressFamily family,
         const boost::shared_ptr<NameserverEntry>& nameserver);
     // Resolver we use
-    boost::shared_ptr<ResolverInterface> resolver_;
+    boost::shared_ptr<isc::resolve::ResolverInterface> resolver_;
     // We store the nameserver table and lru, so we can look up when there's
     // update
     boost::shared_ptr<HashTable<NameserverEntry> > nameserver_table_;
diff --git a/src/lib/python/isc/config/ccsession.py b/src/lib/python/isc/config/ccsession.py
index 38e9349..e7fdb86 100644
--- a/src/lib/python/isc/config/ccsession.py
+++ b/src/lib/python/isc/config/ccsession.py
@@ -24,17 +24,17 @@
 # made there as well
 """Classes and functions for handling configuration and commands
 
-   This module provides the ModuleCCSession and UICCSession classes,
-   as well as a set of utility functions to create and parse messages
-   related to commands and configuration
+   This module provides the ModuleCCSession and UIModuleCCSession
+   classes, as well as a set of utility functions to create and parse
+   messages related to commands and configuration
 
    Modules should use the ModuleCCSession class to connect to the
    configuration manager, and receive updates and commands from
    other modules.
 
-   Configuration user interfaces should use the UICCSession to connect
-   to b10-cmdctl, and receive and send configuration and commands
-   through that to the configuration manager.
+   Configuration user interfaces should use the UIModuleCCSession
+   to connect to b10-cmdctl, and receive and send configuration and
+   commands through that to the configuration manager.
 """
 
 from isc.cc import Session
diff --git a/src/lib/python/isc/config/tests/ccsession_test.py b/src/lib/python/isc/config/tests/ccsession_test.py
index e4de960..2ae37f5 100644
--- a/src/lib/python/isc/config/tests/ccsession_test.py
+++ b/src/lib/python/isc/config/tests/ccsession_test.py
@@ -13,8 +13,6 @@
 # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
-# $Id$
-
 #
 # Tests for the ConfigData and MultiConfigData classes
 #
diff --git a/src/lib/python/isc/config/tests/cfgmgr_test.py b/src/lib/python/isc/config/tests/cfgmgr_test.py
index 7ab151b..c992d0d 100644
--- a/src/lib/python/isc/config/tests/cfgmgr_test.py
+++ b/src/lib/python/isc/config/tests/cfgmgr_test.py
@@ -13,8 +13,6 @@
 # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
-# $Id$
-
 #
 # Tests for the configuration manager module
 #
diff --git a/src/lib/python/isc/config/tests/config_data_test.py b/src/lib/python/isc/config/tests/config_data_test.py
index b61a259..4710b00 100644
--- a/src/lib/python/isc/config/tests/config_data_test.py
+++ b/src/lib/python/isc/config/tests/config_data_test.py
@@ -13,8 +13,6 @@
 # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
-# $Id$
-
 #
 # Tests for the ConfigData and MultiConfigData classes
 #
diff --git a/src/lib/python/isc/config/tests/module_spec_test.py b/src/lib/python/isc/config/tests/module_spec_test.py
index 3d493f2..a4dcdec 100644
--- a/src/lib/python/isc/config/tests/module_spec_test.py
+++ b/src/lib/python/isc/config/tests/module_spec_test.py
@@ -13,8 +13,6 @@
 # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
-# $Id$
-
 #
 # Tests for the module_spec module
 #
diff --git a/src/lib/python/isc/config/tests/unittest_fakesession.py b/src/lib/python/isc/config/tests/unittest_fakesession.py
index 8bd2607..e31b436 100644
--- a/src/lib/python/isc/config/tests/unittest_fakesession.py
+++ b/src/lib/python/isc/config/tests/unittest_fakesession.py
@@ -13,8 +13,6 @@
 # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
-# $Id$
-
 import isc
 
 class WouldBlockForever(Exception):
diff --git a/src/lib/python/isc/datasrc/master.py b/src/lib/python/isc/datasrc/master.py
index faa2ae5..bba0805 100644
--- a/src/lib/python/isc/datasrc/master.py
+++ b/src/lib/python/isc/datasrc/master.py
@@ -13,8 +13,6 @@
 # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
-# $Id$
-
 import sys, re, string
 import time
 import os
diff --git a/src/lib/python/isc/datasrc/sqlite3_ds.py b/src/lib/python/isc/datasrc/sqlite3_ds.py
index 2caa209..77b0828 100644
--- a/src/lib/python/isc/datasrc/sqlite3_ds.py
+++ b/src/lib/python/isc/datasrc/sqlite3_ds.py
@@ -13,8 +13,6 @@
 # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
-# $Id$
-
 import sqlite3, re, random
 import isc
 
diff --git a/src/lib/resolve/Makefile.am b/src/lib/resolve/Makefile.am
new file mode 100644
index 0000000..8edb594
--- /dev/null
+++ b/src/lib/resolve/Makefile.am
@@ -0,0 +1,17 @@
+SUBDIRS = . tests
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CPPFLAGS += $(SQLITE_CFLAGS)
+
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+CLEANFILES = *.gcno *.gcda
+
+lib_LTLIBRARIES = libresolve.la
+libresolve_la_SOURCES = resolve.h
+libresolve_la_SOURCES += resolver_interface.h
+libresolve_la_SOURCES += resolver_callback.h resolver_callback.cc
+libresolve_la_LIBADD = $(top_builddir)/src/lib/dns/libdns++.la
+libresolve_la_LIBADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
diff --git a/src/lib/resolve/resolve.h b/src/lib/resolve/resolve.h
new file mode 100644
index 0000000..63c9b58
--- /dev/null
+++ b/src/lib/resolve/resolve.h
@@ -0,0 +1,20 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef _ISC_RESOLVE_H
+#define _ISC_RESOLVE_H 1
+
+#include <resolve/resolver_interface.h>
+#include <resolve/resolver_callback.h>
+#endif // ISC_RESOLVE_H_
diff --git a/src/lib/resolve/resolver_callback.cc b/src/lib/resolve/resolver_callback.cc
new file mode 100644
index 0000000..c0db55e
--- /dev/null
+++ b/src/lib/resolve/resolver_callback.cc
@@ -0,0 +1,36 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <resolve/resolver_callback.h>
+
+namespace isc {
+namespace resolve {
+
+void
+ResolverCallbackServer::success(const isc::dns::MessagePtr response)
+{
+    // ignore our response here
+    (void)response;
+    
+    server_->resume(true);
+}
+
+void
+ResolverCallbackServer::failure()
+{
+    server_->resume(false);
+}
+
+} // namespace resolve
+} // namespace isc
diff --git a/src/lib/resolve/resolver_callback.h b/src/lib/resolve/resolver_callback.h
new file mode 100644
index 0000000..f69d8a7
--- /dev/null
+++ b/src/lib/resolve/resolver_callback.h
@@ -0,0 +1,48 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef _ISC_RESOLVER_CALLBACK_H
+#define _ISC_RESOLVER_CALLBACK_H 1
+
+#include <asiolink/asiolink.h>
+#include <dns/message.h>
+
+namespace isc {
+namespace resolve {
+
+/// \short Standard Callback for sendQuery for DNSServer instances
+///
+/// This is a standard ResolverInterface::Callback implementation
+/// that is used by Resolver; when RunningQuery finishes and has either
+/// some data or an error, DNSServer::resume() will be called.
+///
+/// This class will ignore the response MessagePtr in the callback,
+/// as the server itself should also have a reference.
+class ResolverCallbackServer : public ResolverInterface::Callback {
+public:
+    ResolverCallbackServer(asiolink::DNSServer* server) :
+        server_(server->clone()) {}
+    ~ResolverCallbackServer() { delete server_; };
+    
+    void success(const isc::dns::MessagePtr response);
+    void failure();
+
+private:
+    asiolink::DNSServer* server_;
+};
+
+} //namespace resolve
+} //namespace isc
+
+#endif // ISC_RESOLVER_CALLBACK_H_
diff --git a/src/lib/resolve/resolver_interface.h b/src/lib/resolve/resolver_interface.h
new file mode 100644
index 0000000..e08bb64
--- /dev/null
+++ b/src/lib/resolve/resolver_interface.h
@@ -0,0 +1,98 @@
+// Copyright (C) 2010  CZ NIC
+//
+// 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 __RESOLVER_INTERFACE_H
+#define __RESOLVER_INTERFACE_H
+
+#include <dns/message.h>
+
+///
+/// \file resolver_interface.h
+/// \short Interface to resolver.
+///
+/// This file contains an interface for the resolver. By subclassing
+/// this abstract interface, other parts of the system can ask the
+/// resolver to do some resolving too.
+///
+/// This is done by creating a subclass of ResolverInterface::Callback,
+/// which defines what to do with the result, and then calling resolve()
+/// on the ResolverInterface implementation.
+///
+/// One default Callback subclass is provided right now, in
+/// resolver_callback.[h|cc], which calls resumse() on a given DNSServer
+///
+
+namespace isc {
+namespace resolve {
+
+///
+/// \short Abstract interface to the resolver.
+///
+/// Abstract interface to the resolver. The NameserverAddressStore uses this
+/// to ask for addresses. It is here because resolver does not yet exist.
+///
+/// It is abstract to allow tests pass dummy resolvers.
+///
+class ResolverInterface {
+    public:
+        /// \short An abstract callback for when the resolver is done.
+        ///
+        /// You can pass an instance of a subclass of this (as a
+        /// CallbackPtr) to RecursiveQuery::sendQuery(), and when it
+        /// is done, it will either call success() if there is an
+        /// answer MessagePtr, or failure(), if the resolver was not
+        /// able to find anything.
+        ///
+        /// Note that a result Message does not necessarily contain
+        /// the actual answer (it could be a noerror/nodata response).
+        class Callback {
+            public:
+                /// \short Some data arrived.
+                virtual void success(const isc::dns::MessagePtr response) = 0;
+                
+                ///
+                ///\short No data available.
+                ///
+                ///\todo Provide error reason (result of the
+                ///      classification call, for instance? We'd also
+                ///      need some way to say 'everything times out')
+                ///
+                virtual void failure() = 0;
+
+                /// \short Virtual destructor, so descendants are cleaned up
+                virtual ~Callback() {};
+        };
+
+        typedef boost::shared_ptr<Callback> CallbackPtr;
+
+        ///
+        ///\short Ask a question.
+        ///
+        /// Asks the resolver a question. Once the answer is ready
+        /// the callback is called.
+        ///
+        /// \param question What to ask. The resolver will decide who.
+        /// \param callback What should happen when the answer is ready.
+        ///
+        virtual void resolve(const isc::dns::QuestionPtr& question,
+            const CallbackPtr& callback) = 0;
+
+        /// \short Virtual destructor, so descendants are properly cleaned up
+        virtual ~ ResolverInterface() {}
+};
+
+} // namespace nsas
+} // namespace isc
+
+#endif //__RESOLVER_INTERFACE_H
diff --git a/src/lib/resolve/tests/Makefile.am b/src/lib/resolve/tests/Makefile.am
new file mode 100644
index 0000000..e064cc0
--- /dev/null
+++ b/src/lib/resolve/tests/Makefile.am
@@ -0,0 +1,24 @@
+AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+CLEANFILES = *.gcno *.gcda
+
+TESTS =
+if HAVE_GTEST
+TESTS += run_unittests
+run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+run_unittests_SOURCES = run_unittests.cc
+run_unittests_SOURCES += resolver_callback_unittest.cc
+
+run_unittests_LDADD = $(GTEST_LDADD)
+run_unittests_LDADD +=  $(top_builddir)/src/lib/resolve/libresolve.la
+
+endif
+
+noinst_PROGRAMS = $(TESTS)
diff --git a/src/lib/resolve/tests/resolver_callback_unittest.cc b/src/lib/resolve/tests/resolver_callback_unittest.cc
new file mode 100644
index 0000000..6370e22
--- /dev/null
+++ b/src/lib/resolve/tests/resolver_callback_unittest.cc
@@ -0,0 +1,90 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <gtest/gtest.h>
+#include <resolve/resolver_callback.h>
+#include <asiolink/asiolink.h>
+
+using namespace isc::resolve;
+
+// Dummy subclass for DNSServer*
+// We want to check if resume is called
+// Since the server will get cloned(), we want the clones to share
+// our bools for whether resume got called and with what value
+class DummyServer : public asiolink::DNSServer {
+public:
+    DummyServer(DummyServer* orig) {
+        resume_called_ = orig->getResumeCalled();
+        resume_value_ = orig->getResumeValue();
+    }
+    DummyServer(bool* resume_called, bool* resume_value) :
+        resume_called_(resume_called), resume_value_(resume_value)
+    {}
+    
+    bool* getResumeCalled() { return resume_called_; }
+    bool* getResumeValue() { return resume_value_; }
+    
+    DNSServer* clone() {
+        DummyServer* n = new DummyServer(this);
+        return n;
+    }
+
+    void resume(bool value) {
+        *resume_called_ = true;
+        *resume_value_ = value;
+    }
+
+private:
+    bool* resume_called_;
+    bool* resume_value_;
+};
+
+class ResolverCallbackServerTest : public ::testing::Test {
+public:
+    ResolverCallbackServerTest() : resume_called_(false),
+                                   resume_value_(false) {
+        server_ = new DummyServer(&resume_called_, &resume_value_);
+        callback_ = new ResolverCallbackServer(server_);
+    };
+
+    ~ResolverCallbackServerTest() {
+        delete callback_;
+        delete server_;
+    }
+
+    DummyServer* getServer() { return server_; }
+    ResolverCallbackServer* getCallback() { return callback_; }
+    bool getResumeCalled() { return resume_called_; }
+    bool getResumeValue() { return resume_value_; }
+
+private:
+    DummyServer* server_;
+    ResolverCallbackServer* callback_;
+    bool resume_called_;
+    bool resume_value_;
+};
+
+TEST_F(ResolverCallbackServerTest, testSuccess) {
+    EXPECT_FALSE(getResumeCalled());
+    getCallback()->success(isc::dns::MessagePtr());
+    EXPECT_TRUE(getResumeCalled());
+    EXPECT_TRUE(getResumeValue());
+}
+
+TEST_F(ResolverCallbackServerTest, testFailure) {
+    EXPECT_FALSE(getResumeCalled());
+    getCallback()->failure();
+    EXPECT_TRUE(getResumeCalled());
+    EXPECT_FALSE(getResumeValue());
+}
diff --git a/src/lib/resolve/tests/run_unittests.cc b/src/lib/resolve/tests/run_unittests.cc
new file mode 100644
index 0000000..f80e167
--- /dev/null
+++ b/src/lib/resolve/tests/run_unittests.cc
@@ -0,0 +1,24 @@
+// 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 <gtest/gtest.h>
+
+#include <dns/tests/unittest_util.h>
+
+int
+main(int argc, char* argv[]) {
+    ::testing::InitGoogleTest(&argc, argv);
+
+    return (RUN_ALL_TESTS());
+}
diff --git a/src/lib/testutils/Makefile.am b/src/lib/testutils/Makefile.am
index b1962ab..d0467ea 100644
--- a/src/lib/testutils/Makefile.am
+++ b/src/lib/testutils/Makefile.am
@@ -8,6 +8,7 @@ if HAVE_GTEST
 lib_LTLIBRARIES = libtestutils.la
 
 libtestutils_la_SOURCES = srv_test.h srv_test.cc
+libtestutils_la_SOURCES += dnsmessage_test.h dnsmessage_test.cc
 libtestutils_la_SOURCES += mockups.h
 libtestutils_la_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 endif
diff --git a/src/lib/testutils/dnsmessage_test.cc b/src/lib/testutils/dnsmessage_test.cc
new file mode 100644
index 0000000..af354d5
--- /dev/null
+++ b/src/lib/testutils/dnsmessage_test.cc
@@ -0,0 +1,115 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <dns/message.h>
+#include <dns/opcode.h>
+#include <dns/rdata.h>
+#include <dns/rcode.h>
+#include <dns/rrset.h>
+#include <dns/rrttl.h>
+
+#include <gtest/gtest.h>
+
+#include <testutils/dnsmessage_test.h>
+
+using namespace isc::dns;
+
+namespace isc {
+namespace testutils {
+const unsigned int QR_FLAG = 0x1;
+const unsigned int AA_FLAG = 0x2;
+const unsigned int TC_FLAG = 0x4;
+const unsigned int RD_FLAG = 0x8;
+const unsigned int RA_FLAG = 0x10;
+const unsigned int AD_FLAG = 0x20;
+const unsigned int CD_FLAG = 0x40;
+
+void
+headerCheck(const Message& message, const qid_t qid, const Rcode& rcode,
+            const uint16_t opcodeval, const unsigned int flags,
+            const unsigned int qdcount,
+            const unsigned int ancount, const unsigned int nscount,
+            const unsigned int arcount)
+{
+    EXPECT_EQ(qid, message.getQid());
+    EXPECT_EQ(rcode, message.getRcode());
+    EXPECT_EQ(opcodeval, message.getOpcode().getCode());
+    EXPECT_EQ((flags & QR_FLAG) != 0,
+              message.getHeaderFlag(Message::HEADERFLAG_QR));
+    EXPECT_EQ((flags & AA_FLAG) != 0,
+              message.getHeaderFlag(Message::HEADERFLAG_AA));
+    EXPECT_EQ((flags & TC_FLAG) != 0,
+              message.getHeaderFlag(Message::HEADERFLAG_TC));
+    EXPECT_EQ((flags & RA_FLAG) != 0,
+              message.getHeaderFlag(Message::HEADERFLAG_RA));
+    EXPECT_EQ((flags & RD_FLAG) != 0,
+              message.getHeaderFlag(Message::HEADERFLAG_RD));
+    EXPECT_EQ((flags & AD_FLAG) != 0,
+              message.getHeaderFlag(Message::HEADERFLAG_AD));
+    EXPECT_EQ((flags & CD_FLAG) != 0,
+              message.getHeaderFlag(Message::HEADERFLAG_CD));
+
+    EXPECT_EQ(qdcount, message.getRRCount(Message::SECTION_QUESTION));
+    EXPECT_EQ(ancount, message.getRRCount(Message::SECTION_ANSWER));
+    EXPECT_EQ(nscount, message.getRRCount(Message::SECTION_AUTHORITY));
+    EXPECT_EQ(arcount, message.getRRCount(Message::SECTION_ADDITIONAL));
+}
+
+namespace {
+::testing::AssertionResult
+matchRdata(const char*, const char*,
+           const rdata::Rdata& expected, const rdata::Rdata& actual)
+{
+    if (expected.compare(actual) != 0) {
+        ::testing::Message msg;
+        msg << "Two RDATAs are expected to be equal but not:\n"
+            << "  Actual: " << actual.toText() << "\n"
+            << "Expected: " << expected.toText();
+        return (::testing::AssertionFailure(msg));
+    }
+    return (::testing::AssertionSuccess());
+}
+}
+
+void
+rrsetCheck(isc::dns::ConstRRsetPtr expected_rrset,
+           isc::dns::ConstRRsetPtr actual_rrset)
+{
+    EXPECT_EQ(expected_rrset->getName(), actual_rrset->getName());
+    EXPECT_EQ(expected_rrset->getClass(), actual_rrset->getClass());
+    EXPECT_EQ(expected_rrset->getType(), actual_rrset->getType());
+    EXPECT_EQ(expected_rrset->getTTL(), actual_rrset->getTTL());
+
+    isc::dns::RdataIteratorPtr rdata_it = actual_rrset->getRdataIterator();
+    isc::dns::RdataIteratorPtr expected_rdata_it =
+        expected_rrset->getRdataIterator();
+    while (!expected_rdata_it->isLast()) {
+        EXPECT_FALSE(rdata_it->isLast());
+        if (rdata_it->isLast()) {
+            // buggy case, should stop here
+            break;
+        }
+
+        EXPECT_PRED_FORMAT2(matchRdata, expected_rdata_it->getCurrent(),
+                            rdata_it->getCurrent());
+
+        expected_rdata_it->next();
+        rdata_it->next();
+    }
+
+    // Make sure we have examined all sets of rrset RDATA
+    EXPECT_TRUE(rdata_it->isLast());
+}
+} // end of namespace testutils
+} // end of namespace isc
diff --git a/src/lib/testutils/dnsmessage_test.h b/src/lib/testutils/dnsmessage_test.h
new file mode 100644
index 0000000..a8b7284
--- /dev/null
+++ b/src/lib/testutils/dnsmessage_test.h
@@ -0,0 +1,304 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <algorithm>
+#include <functional>
+#include <iosfwd>
+#include <string>
+#include <vector>
+
+#include <dns/message.h>
+#include <dns/name.h>
+#include <dns/masterload.h>
+#include <dns/rrclass.h>
+#include <dns/rrset.h>
+
+#include <gtest/gtest.h>
+
+namespace isc {
+namespace testutils {
+///
+/// \name Header flags
+///
+/// These are flags to indicate whether the corresponding flag bit of the
+/// DNS header is to be set in the test cases using \c headerCheck().
+/// (The flag values is irrelevant to their wire-format values).
+/// The meaning of the flags should be obvious from the variable names.
+//@{
+extern const unsigned int QR_FLAG;
+extern const unsigned int AA_FLAG;
+extern const unsigned int TC_FLAG;
+extern const unsigned int RD_FLAG;
+extern const unsigned int RA_FLAG;
+extern const unsigned int AD_FLAG;
+extern const unsigned int CD_FLAG;
+//@}
+
+/// Set of unit tests to examine a DNS message header.
+///
+/// This function takes a dns::Message object and performs various tests
+/// to confirm if the header fields of the message have the given specified
+/// value.  The \c message parameter is the Message object to be tested,
+/// and the remaining parameters specify the expected values of the fields.
+///
+/// If all fields have the expected values the test will be considered
+/// successful.  Otherwise, some of the tests will indicate a failure, which
+/// will make the test case that calls this function fail.
+///
+/// The meaning of the parameters should be obvious, but here are some notes
+/// that may not be so trivial:
+/// - \c opcode is an integer, not an \c dns::Opcode object.  This is because
+///   we can easily iterate over all possible OPCODEs in a test.
+/// - \c flags is a bitmask so that we can specify a set of header flags
+///   via a single parameter.  For example, when we expect the message has
+///   QR and AA flags are on and others are off, we'd set this parameter to
+///   <code>(QR_FLAG | AA_FLAG)</code>.
+///
+/// \param message The DNS message to be tested.
+/// \param qid The expected QID
+/// \param rcode The expected RCODE
+/// \param opcodeval The code value of the expected OPCODE
+/// \param flags Bit flags specifying header flags that are expected to be set
+/// \param qdcount The expected value of QDCOUNT
+/// \param ancount The expected value of ANCOUNT
+/// \param nscount The expected value of NSCOUNT
+/// \param arcount The expected value of ARCOUNT
+void
+headerCheck(const isc::dns::Message& message, const isc::dns::qid_t qid,
+            const isc::dns::Rcode& rcode,
+            const uint16_t opcodeval, const unsigned int flags,
+            const unsigned int qdcount,
+            const unsigned int ancount, const unsigned int nscount,
+            const unsigned int arcount);
+
+/// Set of unit tests to check equality of two RRsets
+///
+/// This function takes two RRset objects and performs detailed tests to
+/// check if these two are "equal", where equal means:
+/// - The owner name, RR class, RR type and TTL are all equal.  Names are
+///   compared in case-insensitive manner.
+/// - The number of RRs (more accurately RDATAs) is the same.
+/// - RDATAs are equal as a sequence.  That is, the first RDATA of
+///   \c expected_rrset is equal to the first RDATA of \c actual_rrset,
+///   the second RDATA of \c expected_rrset is equal to the second RDATA
+///   of \c actual_rrset, and so on.  Two RDATAs are equal iff they have
+///   the same DNSSEC sorting order as defined in RFC4034.
+///
+/// Some of the tests will fail if any of the above isn't met.
+///
+/// \note In future we may want to allow more flexible matching for RDATAs.
+/// For example, we may want to allow comparison as "sets", i.e., comparing
+/// RDATAs regardless of the ordering; we may also want to support suppressing
+/// duplicate RDATA.  For now, it's caller's responsibility to match the
+/// ordering (and any duplicates) between the expected and actual sets.
+/// Even if and when we support the flexible behavior, this "strict mode"
+/// will still be useful.
+///
+/// \param expected_rrset The expected RRset
+/// \param actual_rrset The RRset to be tested
+void rrsetCheck(isc::dns::ConstRRsetPtr expected_rrset,
+                isc::dns::ConstRRsetPtr actual_rrset);
+
+/// The definitions in this name space are not supposed to be used publicly,
+/// but are given here because they are used in templated functions.
+namespace detail {
+// Helper matching class used in rrsetsCheck()
+struct RRsetMatch : public std::unary_function<isc::dns::ConstRRsetPtr, bool> {
+    RRsetMatch(isc::dns::ConstRRsetPtr target) : target_(target) {}
+    bool operator()(isc::dns::ConstRRsetPtr rrset) const {
+        return (rrset->getType() == target_->getType() &&
+                rrset->getClass() == target_->getClass() &&
+                rrset->getName() == target_->getName());
+    }
+    const isc::dns::ConstRRsetPtr target_;
+};
+
+// Helper callback functor for masterLoad() used in rrsetsCheck (stream
+// version)
+class RRsetInserter {
+public:
+    RRsetInserter(std::vector<isc::dns::ConstRRsetPtr>& rrsets) :
+        rrsets_(rrsets)
+    {}
+    void operator()(isc::dns::ConstRRsetPtr rrset) const {
+        rrsets_.push_back(rrset);
+    }
+private:
+    std::vector<isc::dns::ConstRRsetPtr>& rrsets_;
+};
+}
+
+/// Set of unit tests to check if two sets of RRsets are identical.
+///
+/// This templated function takes two sets of sequences, each defined by
+/// two input iterators pointing to \c ConstRRsetPtr (begin and end).
+/// This function compares these two sets of RRsets as "sets", and considers
+/// they are equal when:
+/// - The number of RRsets are the same.
+/// - For any RRset in one set, there is an equivalent RRset in the other set,
+///   and vice versa, where the equivalence of two RRsets is tested using
+///   \c rrsetCheck().
+///
+/// Note that the sets of RRsets are compared as "sets", i.e, they don't have
+/// to be listed in the same order.
+///
+/// The entire tests will pass if the two sets are identical.  Otherwise
+/// some of the tests will indicate a failure.
+///
+/// \note
+/// - There is one known restriction: each set of RRsets must not have more
+///   than one RRsets for the same name, RR type and RR class.  If this
+///   condition isn't met, some of the tests will fail either against an
+///   explicit duplication check or as a result of counter mismatch.
+/// - This function uses linear searches on the expected and actual sequences,
+///   and won't be scalable for large input.  For the purpose of testing it
+///   should be acceptable, but be aware of the size of test data.
+///
+/// \param expected_begin The beginning of the expected set of RRsets
+/// \param expected_end The end of the expected set of RRsets
+/// \param actual_begin The beginning of the set of RRsets to be tested
+/// \param actual_end The end of the set of RRsets to be tested
+template<typename EXPECTED_ITERATOR, typename ACTUAL_ITERATOR>
+void
+rrsetsCheck(EXPECTED_ITERATOR expected_begin, EXPECTED_ITERATOR expected_end,
+            ACTUAL_ITERATOR actual_begin, ACTUAL_ITERATOR actual_end)
+{
+    std::vector<isc::dns::ConstRRsetPtr> checked_rrsets; // for duplicate check
+    unsigned int rrset_matched = 0;
+    ACTUAL_ITERATOR it;
+    for (it = actual_begin; it != actual_end; ++it) {
+        // Make sure there's no duplicate RRset in actual (using a naive
+        // search).  Since the actual set is guaranteed to be unique, we can
+        // detect it if the expected data has a duplicate by the match/size
+        // checks at the end of the function.
+        // Note: we cannot use EXPECT_EQ for iterators
+        EXPECT_TRUE(checked_rrsets.end() ==
+                    std::find_if(checked_rrsets.begin(), checked_rrsets.end(),
+                                 detail::RRsetMatch(*it)));
+        checked_rrsets.push_back(*it);
+
+        EXPECTED_ITERATOR found_rrset_it =
+            std::find_if(expected_begin, expected_end,
+                         detail::RRsetMatch(*it));
+        if (found_rrset_it != expected_end) {
+            rrsetCheck(*found_rrset_it, *it);
+            ++rrset_matched;
+        }
+    }
+
+    // make sure all expected RRsets are in actual sets
+    EXPECT_EQ(std::distance(expected_begin, expected_end), rrset_matched);
+    // make sure rrsets only contains expected RRsets
+    EXPECT_EQ(std::distance(expected_begin, expected_end),
+              std::distance(actual_begin, actual_end));
+}
+
+/// Set of unit tests to check if two sets of RRsets are identical using
+/// streamed expected data.
+///
+/// This templated function takes a standard input stream that produces
+/// a sequence of textural RRs and compares the entire set of RRsets
+/// with the range of RRsets specified by two input iterators.
+///
+/// This function is actually a convenient wrapper for the other version
+/// of function; it internally builds a standard vector of RRsets
+/// from the input stream and uses iterators of the vector as the expected
+/// input iterators for the backend function.
+/// Expected data in the form of input stream would be useful for testing
+/// as it can be easily hardcoded in test cases using string streams or
+/// given from a data source file.
+///
+/// One common use case of this function is to test whether a particular
+/// section of a DNS message contains an expected set of RRsets.
+/// For example, when \c message is an \c dns::Message object, the following
+/// test code will check if the additional section of \c message contains
+/// the hardcoded two RRsets (2 A RRs and 1 AAAA RR) and only contains these
+/// RRsets:
+/// \code std::stringstream expected;
+/// expected << "foo.example.com. 3600 IN A 192.0.2.1\n"
+///          << "foo.example.com. 3600 IN A 192.0.2.2\n"
+///          << "foo.example.com. 7200 IN AAAA 2001:db8::1\n"
+/// rrsetsCheck(expected, message.beginSection(Message::SECTION_ADDITIONAL),
+///                       message.endSection(Message::SECTION_ADDITIONAL));
+/// \endcode
+///
+/// The input stream is parsed using the \c dns::masterLoad() function,
+/// and notes and restrictions of that function apply.
+/// This is also the reason why this function takes \c origin and \c rrclass
+/// parameters.  The default values of these parameters should just work
+/// in many cases for usual tests, but due to a validity check on the SOA RR
+/// in \c dns::masterLoad(), if the input stream contains an SOA RR, the
+/// \c origin parameter will have to be set to the owner name of the SOA
+/// explicitly.  Likewise, all RRsets must have the same RR class.
+/// (We may have to modify \c dns::masterLoad() so that it can
+/// have an option to be more generous about these points if it turns out
+/// to be too restrictive).
+///
+/// \param expected_stream An input stream object that is to emit expected set
+/// of RRsets
+/// \param actual_begin The beginning of the set of RRsets to be tested
+/// \param actual_end The end of the set of RRsets to be tested
+/// \param origin A domain name that is a super domain of the owner name
+/// of all RRsets contained in the stream.
+/// \param rrclass The RR class of the RRsets contained in the stream.
+template<typename ACTUAL_ITERATOR>
+void
+rrsetsCheck(std::istream& expected_stream,
+            ACTUAL_ITERATOR actual_begin, ACTUAL_ITERATOR actual_end,
+            const isc::dns::Name& origin = isc::dns::Name::ROOT_NAME(),
+            const isc::dns::RRClass& rrclass = isc::dns::RRClass::IN())
+{
+    std::vector<isc::dns::ConstRRsetPtr> expected;
+    isc::dns::masterLoad(expected_stream, origin, rrclass,
+                         detail::RRsetInserter(expected));
+    rrsetsCheck(expected.begin(), expected.end(), actual_begin, actual_end);
+}
+
+/// Set of unit tests to check if two sets of RRsets are identical using
+/// expected data as string.
+///
+/// This function is a wrapper for the input stream version:
+/// \c rrsetsCheck(std::istream&, ACTUAL_ITERATOR, ACTUAL_ITERATOR, const isc::dns::Name&, const isc::dns::RRClass&)(),
+/// and takes a string object instead of a stream.
+/// While the stream version is more generic, this version would be more
+/// convenient for tests using hardcoded expected data.  Using this version,
+/// the example test case shown for the stream version would look as follows:
+/// \code
+/// rrsetsCheck("foo.example.com. 3600 IN A 192.0.2.1\n"
+///             "foo.example.com. 3600 IN A 192.0.2.2\n"
+///             "foo.example.com. 7200 IN AAAA 2001:db8::1\n",
+///             message.beginSection(Message::SECTION_ADDITIONAL),
+///             message.endSection(Message::SECTION_ADDITIONAL));
+/// \endcode
+///
+/// The semantics of parameters is the same as that of the stream version
+/// except that \c expected is a string of expected sets of RRsets.
+template<typename ACTUAL_ITERATOR>
+void
+rrsetsCheck(const std::string& expected,
+            ACTUAL_ITERATOR actual_begin, ACTUAL_ITERATOR actual_end,
+            const isc::dns::Name& origin = isc::dns::Name::ROOT_NAME(),
+            const isc::dns::RRClass& rrclass = isc::dns::RRClass::IN())
+{
+    std::stringstream expected_stream(expected);
+    rrsetsCheck(expected_stream, actual_begin, actual_end, origin,
+                rrclass);
+}
+
+} // end of namespace testutils
+} // end of namespace isc
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/testutils/mockups.h b/src/lib/testutils/mockups.h
index 0c64fc2..4bec83d 100644
--- a/src/lib/testutils/mockups.h
+++ b/src/lib/testutils/mockups.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <config.h>
 
 #include <cc/data.h>
@@ -21,6 +19,8 @@
 
 #include <xfr/xfrout_client.h>
 
+#include <asiolink/asiolink.h>
+
 // A minimal mock configuration session.  Most the methods are
 // stubbed out, except for a very basic group_sendmsg() and
 // group_recvmsg().  hasQueuedMessages() always returns false.
diff --git a/src/lib/testutils/srv_test.cc b/src/lib/testutils/srv_test.cc
index 1deb8f0..c0d6e0f 100644
--- a/src/lib/testutils/srv_test.cc
+++ b/src/lib/testutils/srv_test.cc
@@ -21,6 +21,7 @@
 
 #include <dns/tests/unittest_util.h>
 
+#include <testutils/dnsmessage_test.h>
 #include <testutils/srv_test.h>
 
 using namespace isc::dns;
@@ -30,16 +31,9 @@ namespace isc {
 namespace testutils {
 const char* const DEFAULT_REMOTE_ADDRESS = "192.0.2.1";
 
-const unsigned int QR_FLAG = 0x1;
-const unsigned int AA_FLAG = 0x2;
-const unsigned int TC_FLAG = 0x4;
-const unsigned int RD_FLAG = 0x8;
-const unsigned int RA_FLAG = 0x10;
-const unsigned int AD_FLAG = 0x20;
-const unsigned int CD_FLAG = 0x40;
-
 SrvTestBase::SrvTestBase() : request_message(Message::RENDER),
                              parse_message(new Message(Message::PARSE)),
+                             response_message(new Message(Message::RENDER)),
                              default_qid(0x1035),
                              opcode(Opcode(Opcode::QUERY())),
                              qname("www.example.com"),
@@ -232,37 +226,6 @@ SrvTestBase::axfrOverUDP() {
     headerCheck(*parse_message, default_qid, isc::dns::Rcode::FORMERR(),
                 opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
 }
-
-void
-headerCheck(const Message& message, const qid_t qid, const Rcode& rcode,
-            const uint16_t opcodeval, const unsigned int flags,
-            const unsigned int qdcount,
-            const unsigned int ancount, const unsigned int nscount,
-            const unsigned int arcount)
-{
-    EXPECT_EQ(qid, message.getQid());
-    EXPECT_EQ(rcode, message.getRcode());
-    EXPECT_EQ(opcodeval, message.getOpcode().getCode());
-    EXPECT_EQ((flags & QR_FLAG) != 0,
-              message.getHeaderFlag(Message::HEADERFLAG_QR));
-    EXPECT_EQ((flags & AA_FLAG) != 0,
-              message.getHeaderFlag(Message::HEADERFLAG_AA));
-    EXPECT_EQ((flags & TC_FLAG) != 0,
-              message.getHeaderFlag(Message::HEADERFLAG_TC));
-    EXPECT_EQ((flags & RA_FLAG) != 0,
-              message.getHeaderFlag(Message::HEADERFLAG_RA));
-    EXPECT_EQ((flags & RD_FLAG) != 0,
-              message.getHeaderFlag(Message::HEADERFLAG_RD));
-    EXPECT_EQ((flags & AD_FLAG) != 0,
-              message.getHeaderFlag(Message::HEADERFLAG_AD));
-    EXPECT_EQ((flags & CD_FLAG) != 0,
-              message.getHeaderFlag(Message::HEADERFLAG_CD));
-
-    EXPECT_EQ(qdcount, message.getRRCount(Message::SECTION_QUESTION));
-    EXPECT_EQ(ancount, message.getRRCount(Message::SECTION_ANSWER));
-    EXPECT_EQ(nscount, message.getRRCount(Message::SECTION_AUTHORITY));
-    EXPECT_EQ(arcount, message.getRRCount(Message::SECTION_ADDITIONAL));
-}
 } // end of namespace testutils
 } // end of namespace isc
 
diff --git a/src/lib/testutils/srv_test.h b/src/lib/testutils/srv_test.h
index be8b95a..7361a76 100644
--- a/src/lib/testutils/srv_test.h
+++ b/src/lib/testutils/srv_test.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-#include <gtest/gtest.h>
-
 #include <dns/buffer.h>
 #include <dns/name.h>
 #include <dns/message.h>
@@ -46,14 +44,6 @@ extern const unsigned int RA_FLAG;
 extern const unsigned int AD_FLAG;
 extern const unsigned int CD_FLAG;
 
-void
-headerCheck(const isc::dns::Message& message, const isc::dns::qid_t qid,
-            const isc::dns::Rcode& rcode,
-            const uint16_t opcodeval, const unsigned int flags,
-            const unsigned int qdcount,
-            const unsigned int ancount, const unsigned int nscount,
-            const unsigned int arcount);
-
 // The base class for Auth and Recurse test case
 class SrvTestBase : public ::testing::Test {
 protected:
@@ -100,6 +90,7 @@ protected:
     MockServer dnsserv;
     isc::dns::Message request_message;
     isc::dns::MessagePtr parse_message;
+    isc::dns::MessagePtr response_message;
     const isc::dns::qid_t default_qid;
     const isc::dns::Opcode opcode;
     const isc::dns::Name qname;
diff --git a/src/lib/xfr/fd_share.cc b/src/lib/xfr/fd_share.cc
index e27027c..4e1f093 100644
--- a/src/lib/xfr/fd_share.cc
+++ b/src/lib/xfr/fd_share.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <cstring>
 #include <cstdlib>
 
diff --git a/src/lib/xfr/fd_share.h b/src/lib/xfr/fd_share.h
index 7e439b4..4ee5fd5 100644
--- a/src/lib/xfr/fd_share.h
+++ b/src/lib/xfr/fd_share.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef FD_SHARE_H_
 #define FD_SHARE_H_
 
diff --git a/src/lib/xfr/fdshare_python.cc b/src/lib/xfr/fdshare_python.cc
index 0615d88..82b1b6e 100644
--- a/src/lib/xfr/fdshare_python.cc
+++ b/src/lib/xfr/fdshare_python.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #define PY_SSIZE_T_CLEAN
 #include <Python.h>
 #include <structmember.h>
diff --git a/src/lib/xfr/python_xfr.cc b/src/lib/xfr/python_xfr.cc
index 8ecfb58..52848ad 100644
--- a/src/lib/xfr/python_xfr.cc
+++ b/src/lib/xfr/python_xfr.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <boost/python.hpp>
 #include <boost/python/class.hpp>
 #include <boost/python/module.hpp>
diff --git a/src/lib/xfr/xfrout_client.cc b/src/lib/xfr/xfrout_client.cc
index 1dd9c60..e9e736b 100644
--- a/src/lib/xfr/xfrout_client.cc
+++ b/src/lib/xfr/xfrout_client.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <cstdlib>
 #include <cstring>
 #include <iostream>
diff --git a/src/lib/xfr/xfrout_client.h b/src/lib/xfr/xfrout_client.h
index 6539009..bad963c 100644
--- a/src/lib/xfr/xfrout_client.h
+++ b/src/lib/xfr/xfrout_client.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef _XFROUT_CLIENT_H
 #define _XFROUT_CLIENT_H
 




More information about the bind10-changes mailing list