BIND 10 trac643, updated. aba4c4067da0dc63c97c6356dc3137651755ffce Merge branch 'master' into trac643

BIND 10 source code commits bind10-changes at lists.isc.org
Tue Mar 15 11:46:56 UTC 2011


The branch, trac643 has been updated
       via  aba4c4067da0dc63c97c6356dc3137651755ffce (commit)
       via  e79153fe56e7d514bd2ddf70450058e863c72d51 (commit)
       via  5514dd78f2d61a222f3069fc94723ca33fb3200b (commit)
       via  cd620bf3e4315d693582f25538b3bb71941a42e5 (commit)
       via  662e99ef050d98e86614c4443326568a0b5be437 (commit)
       via  3b05e142949e5bee23b809625cb524e7e5ea66f8 (commit)
       via  df2502bc7f9600f03dc410f01b1e6e060ea427ff (commit)
       via  729bbdeb813e99a5f8323f29593f2aaadc95ce3f (commit)
       via  b923cbf809b74f03d3f13a147ac098dc376b45a4 (commit)
       via  f096d86833c3ec49b349d1dc0df91e84a8121891 (commit)
       via  7a3dc628e96dd7b5201a8a1d3851992ea8d325ce (commit)
       via  79e6083d6c946d9734337bd4ebb7f3a984fd6d05 (commit)
       via  1e67f2cbd82e555241a366d5b93a7a6ec52c7921 (commit)
       via  14d7b2bc8d2d3cbfbaf78d1d9a0492c4705aedd0 (commit)
       via  8af77178bac36566bc64b0df7fe22a2b4494ea42 (commit)
       via  ce374384070155e16216b2624bd89f184993df0d (commit)
       via  ba9ed9d67285288e425e9fe9ebddf99bcda69d8a (commit)
       via  0ec520e6070de64a0462e3239e1be48aa8635ffd (commit)
       via  36208de9b48fdf61061554819f72a72258589f84 (commit)
       via  1d88daaa24e8b1ab27f28be876f40a144241e93b (commit)
       via  4049550671160e2bbc06ca7e65996d0b7e243971 (commit)
       via  728f0af26952a8e684be96c73567defbe81052ab (commit)
       via  9ea1252c6694bd933049e0629cae3d2d4a1cd47e (commit)
       via  f8fb852bc6aef292555063590c361f01cf29e5ca (commit)
       via  a92158cc1d674e0dce9e75acbb40a1a9bd807f0e (commit)
       via  3a4e135e22cb4e986ca4c1902d34172ccc9aeae1 (commit)
       via  fdfe3422ffc3d7f6eb44114d02a0a3759d4dee7c (commit)
       via  721a53160c15e8218f6798309befe940b9597ba0 (commit)
       via  677c3d840612c58d1cd011adb678e464a904f959 (commit)
       via  67bfb6c60061a86dd0420df77d5cb85af935c634 (commit)
       via  ec8977f28f7824c8c0b07180eda806be22110199 (commit)
       via  2e697f95732f976f3ba51a671a0c23d2057f3162 (commit)
       via  e2c01932a3a51d44a76cdfe7c97bcc14bab8a1cc (commit)
       via  19a98f3b7abb7ec47957f9143628174f61e3f590 (commit)
       via  76022a7e9f3ff339f0f9f10049aa85e5784d72c5 (commit)
       via  c952ddadcd1c0339097009c7cc9015bf33fb81aa (commit)
       via  170485efd4eb6cb6ba6af78d62be71b2354befd8 (commit)
       via  5874ea28a5e9a3af38fc15c53bcf9d0690680e6c (commit)
       via  4fd1b4c0f03f01b2fb085dae50455f6746b435c9 (commit)
       via  691a4a2f4e82d52d313e67ddd9b1e5480226fefe (commit)
       via  52247fe2ac1e071d95b3333d3c0cea73a0429399 (commit)
       via  454739d105be01d7b9711038ca68271d0f4fd02c (commit)
       via  065327bbba5925b882af5230e5d312750e619a91 (commit)
       via  a72483929eb91cf20d41ca5c40bd6ba8b6f31e60 (commit)
       via  11889ab357ea40764683e4be03bcc27a4ab6bb04 (commit)
       via  9efbe64fe3ff22bb5fba46de409ae058f199c8a7 (commit)
       via  7e385db1cb3baae0bcc5461c17a07062816cc5a6 (commit)
       via  3487cf43d093e5f3abc120fb416a73d8a0e6a878 (commit)
       via  527718a5b0447981ab410c5379bb1a68ef4d9fa9 (commit)
       via  fef3266de36bdf6b6f349d3176ce37b6e29f0a7e (commit)
       via  db1c1a4eec4249d96c385c69a4fe4bc8c49b1b10 (commit)
       via  dcaab4f4a892d1ab0e9e2c246282066891b2ffcb (commit)
       via  385a9f81dd2d30a370b2f45a6ff0ec7d09dbea79 (commit)
       via  420ec42bd913fb83da37b26b75faae49c7957c46 (commit)
       via  1ecb969fc160370724527c3e2e1812463f2398ca (commit)
       via  4c05048ba059b79efeab53498737abe94d37ee07 (commit)
       via  d6f579083977bb3a94ed1bf2ffcaa31da878e0bf (commit)
       via  cba9add1f69d78f1ace066e76136bcf6f0a0d3d9 (commit)
       via  f7d2e62980d949a74bcaf03f6079cb847d6f0f0f (commit)
       via  0cd6c3812b7e7250be1381cf87140794c9fa3c42 (commit)
       via  bf0b8426e2d05c1fc30a2b29929531c430f8bcda (commit)
       via  a84eadd788ec9544c79b26a47f5ef34f6b34c359 (commit)
       via  d39787aa4305f91cfca004088c6f5ca2a3867622 (commit)
       via  02d45b17f160bd3662ee765147debe770c6d3faa (commit)
       via  fc26c7396d98fa84a8f057cc409303f911792365 (commit)
       via  ba92da6356b8d3a5b164102af9d8d22259efccc9 (commit)
       via  7c38ee8fabb2f93c83495e45c58a37712da25e67 (commit)
       via  b1bc4d63d568d15f6b21a3c25a2848bea2553ff7 (commit)
       via  a0d91794e105108425429a6ab9eed196171955eb (commit)
       via  db57bdd2f3f73b7d4048fec614f0a3db73c8489a (commit)
       via  08d197753711038ef73a50ebc4458df5a454f648 (commit)
       via  cf2b8ace9b9c295e46fdd5373bd70a13e7e3fbee (commit)
       via  cbc69359f3d9841a217ee55f0e30937a86cb0097 (commit)
       via  87d58087b71b509d9798bd700b9ba968712e142d (commit)
       via  827babb0d6caade8ca685e5f3e779c756f9ebe1a (commit)
       via  290bbf2d7c93c202b22f1f4e2638ae01038bc983 (commit)
       via  45b76ba773367f8a490bbf19f0d74c02293c387b (commit)
       via  86d0ba9023b4f4a23cf203d3c9bf7c2f3668315e (commit)
       via  1ac4ea1a6996dd39d15d4f6cbdf0a766e8d8d569 (commit)
       via  f009a637580c569a49a74b87a384a242c5a5f30a (commit)
       via  58da05cd5f8709c8f4e1824c5479a0e3cd2b4371 (commit)
       via  bf8ba76aed284a9f1907b3edde1f546c9a4c0a3c (commit)
       via  416fcf7eb6c7c0dc249bcc838d75504f070750a1 (commit)
       via  64978870d1b6843e94268160f5e9c7b7dfbeb755 (commit)
       via  b4149e03fd8f722fe1f0fe567b34d530da005109 (commit)
       via  a447b8e583551311e487816e11a171fdb0e8951a (commit)
       via  e0863720a874d75923ea66adcfbf5b2948efb10a (commit)
       via  03de120c1148775977a8cad2b0697e934e2a823a (commit)
       via  241571157deb66f6766918b783379409e294e2f5 (commit)
       via  65a77d8fde64d464c75917a1ab9b6b3f02640ca6 (commit)
       via  9287cc8f31d6ed03cdcd95f6ca59b58ed6d695b6 (commit)
       via  34eaa7c5e831bb170f0fe8792e40df88f66ed8b9 (commit)
       via  9dcb53261abe3c16324155fbc4c3436100b9e2ee (commit)
       via  0eefd8be74290fd9e72e3d25356c64b24c844816 (commit)
       via  2be9594e5c6cac0e01a75a06d8df31d8cef98380 (commit)
       via  41ef6df597d8831d080d4dfc7c582f06e7f92511 (commit)
       via  17f4eab177a8d459024a5fcf2b43e6826c129891 (commit)
       via  de2d7124bd8a6bf3f2c0a6e661dc5b6b178765cc (commit)
       via  1af89c43e91b613bf3d8ece42ec5d93c19a5b225 (commit)
       via  933b35dcb9cec5113853c93994d69cac44a97f4b (commit)
       via  e1b0deba4ea46e097e15f40b9825f7abffa00596 (commit)
       via  71bcc9a5c413276bd90276c24ee28d59478a138e (commit)
       via  6ac000df85625f5921e8895a1aafff5e4be3ba9c (commit)
       via  7fef6a88c5ab2cedc5f2fa0003ae1beb7a380b9c (commit)
       via  b0c4e1599b68046bbd72455ce90433285b226ffe (commit)
       via  56f0f77fe500b6cb41cda2a950b4349f0778561e (commit)
       via  e49c4b5a038981cea80bf81613efa2c538c8b3e0 (commit)
       via  5222b51a047d8f2352bc9f92fd022baf1681ed81 (commit)
       via  ccd3fea75828ec4e0e2b20d8bba1d7b103164141 (commit)
       via  a663b567f5a57c19c033de049219e4a5eb6d936b (commit)
       via  4cf5938f86b3c1a54beac417f6723505183ddf11 (commit)
       via  e33f9a6d1009753b2f02b43e43da1014b0df1964 (commit)
       via  977a6a742c821ed49fc7d977f917b47f23563bdb (commit)
       via  07c01a7a90476fd6b90659bf809e7135ce26cff2 (commit)
       via  a1d7f70e7c660e847fb388c6f17d6709c93fe8e2 (commit)
       via  96e55aef179874427ea28641316dd145fbd98175 (commit)
       via  ccdce3bab3f04bade1e92e66e3929ffd0db97f0b (commit)
       via  137a6934a14cf0c5b5c065e910b8b364beb0973f (commit)
       via  79155a51eb422813900332ad1582ed5964d70f5c (commit)
       via  b23ef1cf58b0754e23e550362e7a979d5f8624ef (commit)
       via  bdc63200a7e2adcde2b0ce44aaeb8477fb059d17 (commit)
       via  2495b96e3a3adeaaca091d7bf2533c1a58971ee5 (commit)
       via  eb6441aca464980d00e3ff827cbf4195c5a7afc5 (commit)
       via  5e2e3fa2759475313d69d3f9cf1cd339f3db8bc7 (commit)
       via  bf57a576ff80578d47471569a48f0fbfd1fb6e2e (commit)
       via  5ff855e14dca375c24404eebc4573b77dba915d3 (commit)
       via  117fa08fc7f8b7462190b75f99d7604c49f3b7c6 (commit)
       via  058f40e75eb59aea1d3e18ffcdfdacc8386aafa3 (commit)
       via  0b42ead35810e82a97561d71309283950ca9d5a3 (commit)
       via  b9d341ecd382b44d72888899e3559d7d41baa203 (commit)
       via  e0d5584bdb3ca05b078693787c35d8f8f9228040 (commit)
       via  4596c143eadfb9d67c5395bb6fa367470976d0cb (commit)
       via  f3bd834c8e9334004994c33d0798ed6b61a7a99b (commit)
       via  860a2965d04f80fd0e90d3c5100332a8bb2ae9d8 (commit)
       via  c4f4d32eed0f4c26e4968336179a1e749e925aa7 (commit)
       via  b2d210a1ed486dc8c1083deb6f1b0bf2802e8d57 (commit)
       via  3407184a20c367a0de02b41befb4db16971c9589 (commit)
       via  6c1f3af003f02eee8952da8463d05732e667c986 (commit)
       via  d686e3ada2141094413e756d601d2e727fb6f760 (commit)
       via  d0dcd91d39a438d5a18e0726251e1a93212143ca (commit)
       via  b32ea06f28de94a8ec779385d5e86374e81c023d (commit)
       via  26582526e20e56ea4547a447cfa93e122af0f0e1 (commit)
       via  6934390c92855d4006b727e1c8c1c89688afeb5f (commit)
       via  333edc6244dafdab42258b84a46237c6dcf936a0 (commit)
       via  8af2ccd45c7f070d2aa168763815fe8a82808e79 (commit)
       via  19f9f10fa5594bfe311688823fba5dd149e76a59 (commit)
       via  5851a6cc19356b49fcc2500f5d40e7861874561e (commit)
       via  8be2bf19228473f5d54f5015e893b44791a5c0c2 (commit)
       via  edfcbcaccbf4b8098f5cfc1bd1943fe4542b3306 (commit)
       via  7396247046ccc046d711265100aa8d46be057626 (commit)
       via  9c3f27137967b5f92c430baab6ab442258d9d692 (commit)
       via  56df4f9ab31b300b83c26fbeb1a7cf7951f8338e (commit)
       via  2298392e3ce9b5a470f4c0e3b2a22ba571f9b8ab (commit)
       via  309d02a51ee22ff4c0226784ecd7bb4394d19542 (commit)
       via  ddd7b7c3dfdebae68e3742e175e4931f0a5f6c5e (commit)
       via  a2609d0762b9dfdf2750a1bc44ea33525c634f5b (commit)
       via  de130e6d9b6dec3586126f660123a905cc9a284a (commit)
       via  53b5297513ee2320556dead019f268e79b49f77a (commit)
       via  a9211d7973042503353e61f0e16ea092c0f621bf (commit)
       via  48211526895ce56de771e65a7e6d23892c639660 (commit)
       via  1faf02bda80053ddb2f815466d0372a2eeb6c08c (commit)
       via  9e782794969ab034eb92dca6a8d5ee8f518ccc95 (commit)
       via  ff55e63140852f4c7cf8a0404dde36442c45ff4a (commit)
       via  b723719cdad0da4db9b2099596c09f407dbdb135 (commit)
       via  1f682b8172e368d7609aa2f066b778989a7fdf7e (commit)
       via  87308eeddf767dea581b817d1d2ab2aaa4a99dd3 (commit)
       via  c5ce94c579d2d022cc7a39f826599c12c193dc20 (commit)
       via  6b5705bb7f6fac495f8b3e050ce9089997416ccb (commit)
       via  e9cd09d4a41cfb46af3a89e57f7d3184c602dc06 (commit)
       via  8c136625d1e2556c7c8280917a1f18370794ce76 (commit)
       via  af0eb33e4d57c842f692b653d783e028250264dc (commit)
       via  6ed6d55c1e9908ce49d4ba2584c9d647c23908ba (commit)
       via  ca06de9c9e5a017361041a7ce0db4bd37c27e0a7 (commit)
       via  0e52d28e09894ae1cab993e9cf61a49d00f9be64 (commit)
       via  f9efa6910cdf152f850a76c039f597f516f0c2ce (commit)
       via  c0cec0792078fbdd5a6cc5cb19c4361a960d95cf (commit)
       via  6ba7a9cf0d9bcaa52ab17652ef1e6e3ad78094a0 (commit)
       via  f664bc1482edb6da965169317f04f2d7b6458fbd (commit)
       via  e241028cc0993fb87306bd097a45f99a5ea3a50d (commit)
       via  e1db14abce5e64985340e4ebb9eb4e7bee56763e (commit)
       via  6483bb374e7ac88e3b736f08bf11e292605aaeef (commit)
       via  a3d81c1479c88a8ba26941e351fce1b0b1e3a2ca (commit)
       via  895e8df77e0d3f8957f3b518052ff874f67bb8b8 (commit)
       via  cb13c22e8b2464d903d2254dec4d46c1103cf5ea (commit)
       via  68c33a7725a2e41dab53b4d74e2aedec79090380 (commit)
       via  860be372d776c05c9535790257812dbde1b9f74f (commit)
       via  b73ea473019bceeab7aab78ddc5d1c4a7710a8bd (commit)
       via  70d71681b83dd3e0cb125e3f3e176d04e7461aae (commit)
       via  21d48af75c1e756de9acea4c45dc35634c0475ac (commit)
       via  9ae710aa95a75d63e2a37ca7d79297f8ef308e6c (commit)
       via  cac094e7e14a483bbf84394adf55a86d70097188 (commit)
       via  5017cbe7d18c752b80e8e8289b9ab0315f5915d0 (commit)
       via  2e967b7775e023f77f082f49457342fff2c7be33 (commit)
       via  77027490d53b644e157fce2df59c5dbd496d1e1a (commit)
       via  be701769ab3ba884c550d22455ba09af3b725a07 (commit)
       via  f4b20fc54b74d03279245942900806a5e7b57d39 (commit)
       via  38f2d6e49c7f693c55e5d27b3b247a167895826c (commit)
       via  120a7ea6efb5ba35008ed9b3502846f4b8fb2ed8 (commit)
       via  682436e844799b2194eccc2ffdfb3fa3da527cea (commit)
       via  28c720f2b0319ee8b2ee21cea1105e411a31360c (commit)
       via  d82bd9a601e95a301e268c21a8ddcfea560d38dc (commit)
       via  3cb71c8c163525c612460eff4292adb997f8a797 (commit)
       via  2a0f21d3415558e8be812e74e554e11c6cbd6270 (commit)
       via  b13c2fd090114e2c0cfe164bae10cde145f509b4 (commit)
       via  19603be4d345c061cb2345187cfed58a785bf03a (commit)
       via  cc7a7fb930a5300aae369665ddc882c8bbf73cb7 (commit)
       via  7c419681601df9c3a453f0e46756dd751344b1a8 (commit)
       via  f0dd8824dc9bb06420b960f2842902ca2d63fba3 (commit)
       via  af01e66fde8c0055319c47d775b0af1408a3e18a (commit)
       via  d4af3712f3987069dffe6ed30919ce0b7bf84699 (commit)
       via  ea0d42e325de86353e17b29c26257333f0fe016e (commit)
       via  094d2aa6c73201892be2362d6d43fdeb34a2650f (commit)
       via  66174ced68123b63ec6d23a5f4ecd17302fd2e8b (commit)
       via  ef6da9e4e8d00024a6c7565eecc6216331ddfee1 (commit)
       via  dd1809bf5b3c9b6ac372523f5fc96a1baaf7d0a5 (commit)
       via  70c1df71874b71443129d7205297ae5ed630a83d (commit)
       via  00e1d85817e9304e1836bbf2f2e7eb194e4617c4 (commit)
       via  5f59f72eeb28488a1a4e4c336b33fccc2215ba06 (commit)
       via  e321d8b344152b558cb42856deb5e8798f153d1b (commit)
       via  74d131f92fbcd1fe21335a743dde205063b63d00 (commit)
       via  8a18de79026a2620f82662d308c4f87e9a54faa3 (commit)
      from  963acb307871f945870c61699cffee72de1c7b65 (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 aba4c4067da0dc63c97c6356dc3137651755ffce
Merge: 963acb307871f945870c61699cffee72de1c7b65 e79153fe56e7d514bd2ddf70450058e863c72d51
Author: zhanglikun <zhanglikun at cnnic.cn>
Date:   Tue Mar 15 19:46:45 2011 +0800

    Merge branch 'master' into trac643

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

Summary of changes:
 ChangeLog                                          |   80 ++-
 Makefile.am                                        |    7 +-
 README                                             |    2 -
 configure.ac                                       |   17 +-
 doc/guide/bind10-guide.xml                         |   69 +-
 ext/asio/asio/detail/epoll_reactor.hpp             |    2 +-
 ext/asio/asio/detail/kqueue_reactor.hpp            |    2 +-
 ext/asio/asio/detail/null_thread.hpp               |    2 +-
 src/bin/auth/auth.spec.pre.in                      |   10 +-
 src/bin/auth/b10-auth.8                            |   62 +--
 src/bin/auth/b10-auth.xml                          |   11 +-
 src/bin/auth/main.cc                               |    8 +-
 src/bin/bind10/bind10.8                            |   37 +-
 src/bin/bind10/bind10.py.in                        |  112 +++-
 src/bin/bind10/bind10.xml                          |   30 +
 src/bin/bind10/tests/bind10_test.py                |  416 ----------
 src/bin/bind10/tests/bind10_test.py.in             |  515 ++++++++++++
 src/bin/bindctl/Makefile.am                        |    9 +-
 src/bin/bindctl/bindcmd.py                         |   35 +-
 src/bin/bindctl/bindctl-source.py.in               |  135 ---
 src/bin/bindctl/bindctl.1                          |   15 +-
 src/bin/bindctl/bindctl.xml                        |   21 +-
 src/bin/bindctl/bindctl_main.py.in                 |  138 ++++
 src/bin/bindctl/tests/Makefile.am                  |    2 +-
 src/bin/bindctl/tests/bindctl_test.py              |  126 +++-
 src/bin/cfgmgr/b10-cfgmgr.py.in                    |   33 +-
 src/bin/cfgmgr/b10-cfgmgr.xml                      |   45 +-
 src/bin/cfgmgr/tests/b10-cfgmgr_test.py.in         |   66 ++-
 src/bin/resolver/Makefile.am                       |    1 +
 src/bin/resolver/b10-resolver.8                    |    2 +-
 src/bin/resolver/b10-resolver.xml                  |    5 +-
 src/bin/resolver/main.cc                           |   54 ++-
 src/bin/resolver/resolver.cc                       |   28 +-
 src/bin/resolver/resolver.h                        |   21 +
 src/bin/resolver/resolver.spec.pre.in              |    6 +-
 src/bin/resolver/response_scrubber.h               |    4 +-
 src/bin/resolver/tests/Makefile.am                 |    1 +
 src/bin/xfrout/xfrout.py.in                        |    6 +-
 src/lib/Makefile.am                                |    4 +-
 src/lib/asiolink/Makefile.am                       |   27 +-
 src/lib/asiolink/asiodef.cc                        |   37 +
 src/lib/asiolink/asiodef.h                         |   21 +
 src/lib/asiolink/asiodef.msg                       |   56 ++
 src/lib/asiolink/asiolink.h                        |    5 -
 src/lib/asiolink/asiolink_utilities.h              |   61 ++
 src/lib/asiolink/dns_lookup.h                      |    4 +-
 src/lib/asiolink/dns_server.h                      |   10 +-
 src/lib/asiolink/dns_service.h                     |    6 +-
 src/lib/asiolink/dummy_io_cb.h                     |    8 +
 src/lib/asiolink/internal/iofetch.h                |  125 ---
 src/lib/asiolink/interval_timer.h                  |    2 +-
 src/lib/asiolink/io_address.h                      |    6 +-
 src/lib/asiolink/io_asio_socket.h                  |  214 ++++--
 src/lib/asiolink/io_endpoint.cc                    |    1 +
 src/lib/asiolink/io_endpoint.h                     |    4 -
 src/lib/asiolink/io_fetch.cc                       |  260 +++++--
 src/lib/asiolink/io_fetch.h                        |  147 ++---
 src/lib/asiolink/io_message.h                      |    4 -
 src/lib/asiolink/qid_gen.cc                        |   54 ++
 src/lib/asiolink/qid_gen.h                         |   85 ++
 src/lib/asiolink/recursive_query.cc                |  543 ------------
 src/lib/asiolink/recursive_query.h                 |  117 ---
 src/lib/asiolink/tcp_endpoint.h                    |   61 +-
 src/lib/asiolink/tcp_server.cc                     |   25 +-
 src/lib/asiolink/tcp_socket.h                      |  301 +++++--
 src/lib/asiolink/tests/Makefile.am                 |   11 +-
 .../asiolink/tests/asiolink_utilities_unittest.cc  |   74 ++
 src/lib/asiolink/tests/io_fetch_unittest.cc        |  601 ++++++++++++---
 src/lib/asiolink/tests/qid_gen_unittest.cc         |   59 ++
 src/lib/asiolink/tests/recursive_query_unittest.cc |  796 ------------------
 src/lib/asiolink/tests/run_unittests.cc            |    6 +-
 src/lib/asiolink/tests/tcp_endpoint_unittest.cc    |   55 ++
 src/lib/asiolink/tests/tcp_socket_unittest.cc      |  515 ++++++++++++
 src/lib/asiolink/tests/udp_socket_unittest.cc      |   88 ++-
 src/lib/asiolink/udp_endpoint.h                    |   11 +
 src/lib/asiolink/udp_server.cc                     |   11 +-
 src/lib/asiolink/udp_socket.h                      |  184 +++--
 src/lib/cache/Makefile.am                          |    1 +
 src/lib/cache/TODO                                 |    4 +
 src/lib/cache/message_cache.cc                     |   24 +-
 src/lib/cache/message_cache.h                      |   16 +-
 src/lib/cache/message_entry.cc                     |   89 ++-
 src/lib/cache/message_entry.h                      |   61 +-
 src/lib/cache/message_utility.cc                   |   80 ++
 src/lib/cache/message_utility.h                    |   66 ++
 src/lib/cache/resolver_cache.cc                    |   27 +-
 src/lib/cache/resolver_cache.h                     |   23 +-
 src/lib/cache/tests/Makefile.am                    |   14 +-
 src/lib/cache/tests/message_cache_unittest.cc      |   14 +-
 src/lib/cache/tests/message_entry_unittest.cc      |   64 +-
 src/lib/cache/tests/negative_cache_unittest.cc     |  242 ++++++
 src/lib/cache/tests/resolver_cache_unittest.cc     |    2 +-
 src/lib/cache/tests/rrset_cache_unittest.cc        |    2 +-
 .../tests/testdata/message_cname_referral.wire     |   56 ++
 .../tests/testdata/message_example_com_soa.wire    |   57 ++
 src/lib/cache/tests/testdata/message_fromWire9     |    4 +-
 .../cache/tests/testdata/message_large_ttl.wire    |   31 +
 .../tests/testdata/message_nodata_with_soa.wire    |   32 +
 .../tests/testdata/message_nxdomain_cname.wire     |   36 +
 .../tests/testdata/message_nxdomain_large_ttl.wire |   25 +
 .../tests/testdata/message_nxdomain_no_soa.wire    |   26 +
 .../tests/testdata/message_nxdomain_with_soa.wire  |   55 ++
 src/lib/cache/tests/testdata/message_referral.wire |   36 +
 src/lib/cc/data.h                                  |   12 +-
 src/lib/cc/session.h                               |    2 +-
 src/lib/config/module_spec.h                       |    4 +
 src/lib/datasrc/data_source.cc                     |   13 +
 src/lib/datasrc/memory_datasrc.h                   |    2 +-
 src/lib/datasrc/rbtree.h                           |   23 +-
 src/lib/datasrc/tests/Makefile.am                  |    1 +
 src/lib/datasrc/tests/datasrc_unittest.cc          |  168 +++--
 src/lib/datasrc/tests/rbtree_unittest.cc           |   52 +-
 src/lib/datasrc/tests/test_datasrc.cc              |   15 +-
 src/lib/datasrc/zonetable.h                        |    2 +-
 src/lib/dns/edns.h                                 |    4 +-
 src/lib/dns/masterload.h                           |    6 +-
 src/lib/dns/message.h                              |    8 +-
 src/lib/dns/question.h                             |   10 +-
 src/lib/dns/rrset.h                                |    2 -
 src/lib/dns/rrttl.h                                |    6 +-
 src/lib/log/Makefile.am                            |    5 +
 src/lib/log/README                                 |  376 +++++++++
 src/lib/log/compiler/Makefile.am                   |    4 +-
 src/lib/log/documentation.txt                      |  434 ----------
 src/lib/log/dummylog.h                             |    3 +-
 src/lib/log/filename.h                             |    2 +-
 src/lib/log/message_dictionary.h                   |    2 +-
 src/lib/log/xdebuglevel.h                          |    4 +-
 src/lib/nsas/address_entry.h                       |    4 +-
 src/lib/nsas/asiolink.h                            |   37 -
 src/lib/nsas/hash.h                                |    2 +-
 src/lib/nsas/hash_table.h                          |    2 +-
 src/lib/nsas/lru_list.h                            |    2 +-
 src/lib/nsas/nameserver_address.cc                 |    4 +-
 src/lib/nsas/nameserver_address.h                  |    4 +-
 src/lib/nsas/nameserver_address_store.cc           |   13 +
 src/lib/nsas/nameserver_address_store.h            |    9 +-
 src/lib/nsas/nameserver_entry.cc                   |    9 +-
 src/lib/nsas/nameserver_entry.h                    |    2 +-
 src/lib/nsas/tests/Makefile.am                     |    1 +
 src/lib/nsas/tests/address_entry_unittest.cc       |    4 +-
 src/lib/nsas/tests/nameserver_address_unittest.cc  |    2 +-
 src/lib/nsas/tests/nameserver_entry_unittest.cc    |    4 +-
 src/lib/nsas/tests/zone_entry_unittest.cc          |   32 +-
 src/lib/nsas/zone_entry.cc                         |   21 +-
 src/lib/nsas/zone_entry.h                          |    9 +
 src/lib/python/isc/Makefile.am                     |    2 +-
 src/lib/python/isc/config/cfgmgr.py                |   66 +-
 src/lib/python/isc/config/tests/cfgmgr_test.py     |   40 +-
 src/lib/python/isc/testutils/Makefile.am           |    1 +
 src/lib/python/isc/testutils/README                |    3 +
 src/lib/python/isc/testutils/__init__.py           |   17 +
 src/lib/python/isc/testutils/parse_args.py         |   30 +
 src/lib/resolve/Makefile.am                        |   11 +
 src/lib/resolve/recursive_query.cc                 |  757 +++++++++++++++++
 src/lib/resolve/recursive_query.h                  |  133 +++
 src/lib/resolve/response_classifier.cc             |   10 +-
 src/lib/resolve/response_classifier.h              |    1 +
 src/lib/resolve/tests/Makefile.am                  |    7 +
 src/lib/resolve/tests/recursive_query_unittest.cc  |  861 ++++++++++++++++++++
 .../resolve/tests/recursive_query_unittest_2.cc    |  655 +++++++++++++++
 .../resolve/tests/response_classifier_unittest.cc  |   17 +
 src/lib/testutils/Makefile.am                      |    1 +
 tests/Makefile.am                                  |    1 +
 tests/system/Makefile.am                           |   16 +
 tests/system/README                                |   63 ++
 tests/system/bindctl/clean.sh                      |   20 +
 .../system/bindctl/nsx1/b10-config.db.template.in  |   10 +
 tests/system/bindctl/nsx1/example-normalized.db    |    3 +
 tests/system/bindctl/nsx1/root.db                  |   25 +
 tests/system/bindctl/setup.sh                      |   26 +
 tests/system/bindctl/tests.sh                      |  106 +++
 tests/system/cleanall.sh                           |   33 +
 tests/system/common/default_user.csv               |    1 +
 tests/system/conf.sh.in                            |   57 ++
 tests/system/glue/auth.good                        |   15 +
 tests/system/glue/clean.sh                         |   23 +
 tests/system/glue/example.good                     |   19 +
 tests/system/glue/noglue.good                      |   14 +
 tests/system/glue/nsx1/b10-config.db.in            |    9 +
 tests/system/glue/nsx1/com.db                      |   31 +
 tests/system/glue/nsx1/net.db                      |   32 +
 tests/system/glue/nsx1/root-servers.nil.db         |   26 +
 tests/system/glue/nsx1/root.db                     |   55 ++
 tests/system/glue/setup.sh.in                      |   25 +
 tests/system/glue/test.good                        |   19 +
 tests/system/glue/tests.sh                         |   66 ++
 tests/system/ifconfig.sh                           |  226 +++++
 tests/system/run.sh                                |  125 +++
 tests/system/runall.sh                             |   44 +
 tests/system/start.pl                              |  226 +++++
 tests/system/stop.pl                               |  188 +++++
 192 files changed, 9633 insertions(+), 3625 deletions(-)
 delete mode 100644 src/bin/bind10/tests/bind10_test.py
 create mode 100644 src/bin/bind10/tests/bind10_test.py.in
 delete mode 100644 src/bin/bindctl/bindctl-source.py.in
 create mode 100755 src/bin/bindctl/bindctl_main.py.in
 create mode 100644 src/lib/asiolink/asiodef.cc
 create mode 100644 src/lib/asiolink/asiodef.h
 create mode 100644 src/lib/asiolink/asiodef.msg
 create mode 100644 src/lib/asiolink/asiolink_utilities.h
 delete mode 100644 src/lib/asiolink/internal/iofetch.h
 create mode 100644 src/lib/asiolink/qid_gen.cc
 create mode 100644 src/lib/asiolink/qid_gen.h
 delete mode 100644 src/lib/asiolink/recursive_query.cc
 delete mode 100644 src/lib/asiolink/recursive_query.h
 create mode 100644 src/lib/asiolink/tests/asiolink_utilities_unittest.cc
 create mode 100644 src/lib/asiolink/tests/qid_gen_unittest.cc
 delete mode 100644 src/lib/asiolink/tests/recursive_query_unittest.cc
 create mode 100644 src/lib/asiolink/tests/tcp_endpoint_unittest.cc
 create mode 100644 src/lib/asiolink/tests/tcp_socket_unittest.cc
 create mode 100644 src/lib/cache/message_utility.cc
 create mode 100644 src/lib/cache/message_utility.h
 create mode 100644 src/lib/cache/tests/negative_cache_unittest.cc
 create mode 100644 src/lib/cache/tests/testdata/message_cname_referral.wire
 create mode 100644 src/lib/cache/tests/testdata/message_example_com_soa.wire
 create mode 100644 src/lib/cache/tests/testdata/message_large_ttl.wire
 create mode 100644 src/lib/cache/tests/testdata/message_nodata_with_soa.wire
 create mode 100644 src/lib/cache/tests/testdata/message_nxdomain_cname.wire
 create mode 100644 src/lib/cache/tests/testdata/message_nxdomain_large_ttl.wire
 create mode 100644 src/lib/cache/tests/testdata/message_nxdomain_no_soa.wire
 create mode 100644 src/lib/cache/tests/testdata/message_nxdomain_with_soa.wire
 create mode 100644 src/lib/cache/tests/testdata/message_referral.wire
 create mode 100644 src/lib/log/README
 delete mode 100644 src/lib/log/documentation.txt
 create mode 100644 src/lib/python/isc/testutils/Makefile.am
 create mode 100644 src/lib/python/isc/testutils/README
 create mode 100644 src/lib/python/isc/testutils/__init__.py
 create mode 100644 src/lib/python/isc/testutils/parse_args.py
 create mode 100644 src/lib/resolve/recursive_query.cc
 create mode 100644 src/lib/resolve/recursive_query.h
 create mode 100644 src/lib/resolve/tests/recursive_query_unittest.cc
 create mode 100644 src/lib/resolve/tests/recursive_query_unittest_2.cc
 create mode 100644 tests/Makefile.am
 create mode 100644 tests/system/Makefile.am
 create mode 100644 tests/system/README
 create mode 100755 tests/system/bindctl/clean.sh
 create mode 100644 tests/system/bindctl/nsx1/b10-config.db.template.in
 create mode 100644 tests/system/bindctl/nsx1/example-normalized.db
 create mode 100644 tests/system/bindctl/nsx1/root.db
 create mode 100755 tests/system/bindctl/setup.sh
 create mode 100755 tests/system/bindctl/tests.sh
 create mode 100755 tests/system/cleanall.sh
 create mode 100644 tests/system/common/default_user.csv
 create mode 100755 tests/system/conf.sh.in
 create mode 100644 tests/system/glue/auth.good
 create mode 100755 tests/system/glue/clean.sh
 create mode 100644 tests/system/glue/example.good
 create mode 100644 tests/system/glue/noglue.good
 create mode 100644 tests/system/glue/nsx1/b10-config.db.in
 create mode 100644 tests/system/glue/nsx1/com.db
 create mode 100644 tests/system/glue/nsx1/net.db
 create mode 100644 tests/system/glue/nsx1/root-servers.nil.db
 create mode 100644 tests/system/glue/nsx1/root.db
 create mode 100755 tests/system/glue/setup.sh.in
 create mode 100644 tests/system/glue/test.good
 create mode 100755 tests/system/glue/tests.sh
 create mode 100755 tests/system/ifconfig.sh
 create mode 100755 tests/system/run.sh
 create mode 100755 tests/system/runall.sh
 create mode 100755 tests/system/start.pl
 create mode 100755 tests/system/stop.pl

-----------------------------------------------------------------------
diff --git a/ChangeLog b/ChangeLog
index 540ea76..c7ff9ac 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,8 +1,84 @@
-  189.  [bug]		zhang likun
+  202.  [func]    vorner
+	It is possible to specify a different directory where we look for
+	configuration files (by -p) and different configuration file to use (-c).
+	Also, it is possible to specify the port on which cmdctl should listen
+	(--cmdctl-port).
+	(Trac #615, git 5514dd78f2d61a222f3069fc94723ca33fb3200b)
+
+  201.  [bug]           jerry
+	src/bin/bindctl: bindctl doesn't show traceback on shutdown.
+	(Trac #588, git 662e99ef050d98e86614c4443326568a0b5be437)
+
+  200.  [bug]           Jelte
+	Fixed a bug where incoming TCP connections were not closed.
+	(Trac #589, git 1d88daaa24e8b1ab27f28be876f40a144241e93b)
+
+  199.  [func]           ocean
+	Cache negative responses (NXDOMAIN/NODATA) from authoritative
+	server for recursive resolver.
+	(Trac #493, git f8fb852bc6aef292555063590c361f01cf29e5ca)
+
+  198.	[bug]		jinmei
+	b10-auth, src/lib/datasrc: fixed a bug where hot spot cache failed
+	to reuse cached SOA for negative responses.  Due to this bug
+	b10-auth returned SERVFAIL when it was expected to return a
+	negative response immediately after a specific SOA query for
+	the zone.
+	(Trac #626, git 721a53160c15e8218f6798309befe940b9597ba0)
+
+  197.  [bug]		zhang likun
 	Remove expired message and rrset entries when looking up them
 	in cache, touch or remove the rrset entry in cache properly
 	when doing lookup or update.
-	(Trac #661 git )
+	(Trac #661, git 9efbe64fe3ff22bb5fba46de409ae058f199c8a7)
+
+  196.	[bug]		jinmei
+	b10-auth, src/lib/datasrc: the backend of the in-memory data
+	source could not handle the root name.  As a result b10-auth could
+	not work as a root server when using the in-memory data source.
+	(Trac #683, git 420ec42bd913fb83da37b26b75faae49c7957c46)
+
+  195.  [func]      stephen
+	Resolver will now re-try a query over TCP if a response to a UDP
+	query has the TC bit set.
+	(Trac #499, git 4c05048ba059b79efeab53498737abe94d37ee07)
+
+  194.  [bug]       vorner
+	Solved a 100% CPU usage problem after switching addresses in b10-auth
+	(and possibly, but unconfirmed, in b10-resolver). It was caused by
+	repeated reads/accepts on closed socket (the bug was in the code for a
+	long time, recent changes made it show).
+	(Trac #657, git e0863720a874d75923ea66adcfbf5b2948efb10a)
+
+  193.	[func]*		jreed
+	Listen on the IPv6 (::) and IPv4 (0.0.0.0) wildcard addresses
+	for b10-auth. This returns to previous behavior prior to
+	change #184. Document the listen_on configuration in manual.
+	(Trac #649, git 65a77d8fde64d464c75917a1ab9b6b3f02640ca6)
+
+  192.	[func]*		jreed
+	Listen on standard domain port 53 for b10-auth and
+	b10-resolver.
+	(Trac #617, #618, git 137a6934a14cf0c5b5c065e910b8b364beb0973f)
+
+  191.	[func]		jinmei
+	Imported system test framework of BIND 9.  It can be run by
+	'make systest' at the top source directory.  Notes: currently it
+	doesn't work when built in a separate tree.  It also requires
+	perl, an inherited dependency from the original framework.
+	Also, mainly for the purpose of tests, a new option "--pid-file"
+	was added to BoB, with which the boss process will dump its PID
+	to the specified file.
+	(Trac #606, git 6ac000df85625f5921e8895a1aafff5e4be3ba9c)
+
+  190.	[func]		jelte
+	Resolver now sets random qids on outgoing queries using
+	the boost::mt19937 prng.
+	(Trac #583, git 5222b51a047d8f2352bc9f92fd022baf1681ed81)
+
+  189.	[bug]		jreed
+	Do not install the log message compiler.
+	(Trac #634, git eb6441aca464980d00e3ff827cbf4195c5a7afc5)
 
   188.  [bug]		zhang likun
 	Make the rrset trust level ranking algorithm used by
diff --git a/Makefile.am b/Makefile.am
index 9a28f20..e31a1a5 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = doc src
+SUBDIRS = doc src tests
 USE_LCOV=@USE_LCOV@
 LCOV=@LCOV@
 GENHTML=@GENHTML@
@@ -77,6 +77,11 @@ cppcheck:
 		--template '{file}:{line}: check_fail: {message} ({severity},{id})' \
 		src
 
+# system tests
+systest:
+	cd tests/system; \
+	sh $(abs_srcdir)/tests/system/runall.sh
+
 #### include external sources in the distributed tarball:
 EXTRA_DIST = ext/asio/README
 EXTRA_DIST += ext/asio/asio/local/stream_protocol.hpp
diff --git a/README b/README
index 5bc3154..b10d12e 100644
--- a/README
+++ b/README
@@ -164,8 +164,6 @@ source tree:
 (Which will use the modules and configurations also from the source
 tree.)
 
-The server will listen on port 5300 for DNS requests.
-
 CONFIGURATION
 
 Commands can be given through the bindctl tool.
diff --git a/configure.ac b/configure.ac
index 0f34b0d..b93fdc7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -583,6 +583,12 @@ if test "X$ac_cv_have_devpoll" = "Xyes" -a "X$GXX" = "Xyes"; then
 	CPPFLAGS="$CPPFLAGS -DASIO_DISABLE_DEV_POLL=1"
 fi
 
+#
+# Perl is optional; it is used only by some of the system test scripts.
+#
+AC_PATH_PROGS(PERL, perl5 perl)
+AC_SUBST(PERL)
+
 AC_ARG_ENABLE(man, [AC_HELP_STRING([--enable-man],
   [regenerate man pages [default=no]])], enable_man=yes, enable_man=no)
 
@@ -657,6 +663,7 @@ AC_CONFIG_FILES([Makefile
                  src/lib/python/isc/net/tests/Makefile
                  src/lib/python/isc/notify/Makefile
                  src/lib/python/isc/notify/tests/Makefile
+                 src/lib/python/isc/testutils/Makefile
                  src/lib/config/Makefile
                  src/lib/config/tests/Makefile
                  src/lib/config/tests/testdata/Makefile
@@ -683,6 +690,8 @@ AC_CONFIG_FILES([Makefile
                  src/lib/cache/tests/Makefile
                  src/lib/server_common/Makefile
                  src/lib/server_common/tests/Makefile
+                 tests/Makefile
+                 tests/system/Makefile
                ])
 AC_OUTPUT([doc/version.ent
            src/bin/cfgmgr/b10-cfgmgr.py
@@ -712,9 +721,10 @@ AC_OUTPUT([doc/version.ent
            src/bin/stats/tests/stats_test
            src/bin/bind10/bind10.py
            src/bin/bind10/tests/bind10_test
+           src/bin/bind10/tests/bind10_test.py
            src/bin/bind10/run_bind10.sh
            src/bin/bindctl/run_bindctl.sh
-           src/bin/bindctl/bindctl-source.py
+           src/bin/bindctl/bindctl_main.py
            src/bin/bindctl/tests/bindctl_test
            src/bin/loadzone/run_loadzone.sh
            src/bin/loadzone/tests/correct/correct_test.sh
@@ -739,6 +749,10 @@ AC_OUTPUT([doc/version.ent
            src/lib/cc/session_config.h.pre
            src/lib/cc/tests/session_unittests_config.h
            src/lib/log/tests/run_time_init_test.sh
+           tests/system/conf.sh
+           tests/system/glue/setup.sh
+           tests/system/glue/nsx1/b10-config.db
+           tests/system/bindctl/nsx1/b10-config.db.template
           ], [
            chmod +x src/bin/cmdctl/run_b10-cmdctl.sh
            chmod +x src/bin/xfrin/run_b10-xfrin.sh
@@ -763,6 +777,7 @@ AC_OUTPUT([doc/version.ent
            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
+           chmod +x tests/system/conf.sh
           ])
 AC_OUTPUT
 
diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml
index 0935c81..c020f11 100644
--- a/doc/guide/bind10-guide.xml
+++ b/doc/guide/bind10-guide.xml
@@ -336,14 +336,6 @@ var/
         </simpara>
       </note>
 
-      <note>
-        <simpara>
-          The development prototype of the b10-auth server listens on
-          0.0.0.0 (all interfaces) port 5300. (This is not the standard
-          domain service port.)
-        </simpara>
-      </note>
-
       <para>
         To quickly get started with BIND 10, follow these steps.
       </para>
@@ -397,7 +389,7 @@ var/
         <listitem>
 
          <para>Test it; for example:
-            <screen>$ <userinput>dig @127.0.0.1 -p 5300 -c CH -t TXT authors.bind</userinput></screen>
+            <screen>$ <userinput>dig @127.0.0.1 -c CH -t TXT authors.bind</userinput></screen>
          </para>
         </listitem>
 
@@ -1044,11 +1036,6 @@ TODO
       process.
     </para>
 
-    <note><simpara>
-      This development prototype release listens on all interfaces
-      and the non-standard port 5300.
-    </simpara></note>
-
     <section>
       <title>Server Configurations</title>
 
@@ -1212,10 +1199,9 @@ TODO
     <title>Incoming Zone Transfers</title>
 
     <para>
-      The <command>b10-xfrin</command> process is started by
-      <command>bind10</command>.
-      It can be manually triggered to request an AXFR zone
-      transfer. When received, it is stored in the BIND 10
+      Incoming zones are transferred using the <command>b10-xfrin</command>
+      process which is started by <command>bind10</command>.
+      When received, the zone is stored in the BIND 10
       data store, and its records can be served by
       <command>b10-auth</command>.
       In combination with <command>b10-zonemgr</command> (for
@@ -1226,8 +1212,22 @@ TODO
     <note><simpara>
      The current development release of BIND 10 only supports
      AXFR. (IXFR is not supported.) 
+
+<!-- TODO: sqlite3 data source only? -->
+
     </simpara></note>
 
+<!-- TODO:
+
+how to tell bind10 you are a secondary?
+
+when will it first attempt to check for new zone? (using REFRESH?)
+what if zonemgr is not running?
+
+what if a NOTIFY is sent?
+
+-->
+
     <para>
        To manually trigger a zone transfer to retrieve a remote zone,
        you may use the <command>bindctl</command> utility.
@@ -1236,6 +1236,9 @@ TODO
        <screen>> <userinput>Xfrin retransfer zone_name="<option>foo.example.org</option>" master=<option>192.0.2.99</option></userinput></screen>
     </para>
 
+<!-- TODO: can that retransfer be used to identify a new zone? -->
+<!-- TODO: what if doesn't exist at that master IP? -->
+
   </chapter>
 
   <chapter id="xfrout">
@@ -1342,28 +1345,34 @@ what is XfroutClient xfr_client??
 
 <!-- TODO: later the above will have some defaults -->
 
-    <para>
-      To enable forwarding, the upstream address and port must be
-      configured to forward queries to, such as:
+    <section>
+      <title>Forwarding</title>
 
-      <screen>
+      <para>
+
+        To enable forwarding, 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>
+        (Replace <replaceable>192.168.1.1</replaceable> to point to your
+        full resolver.)
+      </para>
 
-    <para>
-      Normal iterative name service can be re-enabled by clearing the
-      forwarding address(es); for example:
+      <para>
+        Normal iterative name service can be re-enabled by clearing the
+        forwarding address(es); for example:
 
-      <screen>
+        <screen>
 > <userinput>config set Resolver/forward_addresses []</userinput>
 > <userinput>config commit</userinput>
 </screen>
-    </para>
+      </para>
+
+    </section>
 
 <!-- TODO: later try this
 
diff --git a/ext/asio/asio/detail/epoll_reactor.hpp b/ext/asio/asio/detail/epoll_reactor.hpp
index 6367398..0a1eac0 100644
--- a/ext/asio/asio/detail/epoll_reactor.hpp
+++ b/ext/asio/asio/detail/epoll_reactor.hpp
@@ -207,7 +207,7 @@ public:
   // Cancel all operations associated with the given descriptor. The
   // handlers associated with the descriptor will be invoked with the
   // operation_aborted error.
-  void cancel_ops(socket_type descriptor, per_descriptor_data& descriptor_data)
+  void cancel_ops(socket_type, per_descriptor_data& descriptor_data)
   {
     mutex::scoped_lock descriptor_lock(descriptor_data->mutex_);
 
diff --git a/ext/asio/asio/detail/kqueue_reactor.hpp b/ext/asio/asio/detail/kqueue_reactor.hpp
index 1e118b3..bfa004d 100644
--- a/ext/asio/asio/detail/kqueue_reactor.hpp
+++ b/ext/asio/asio/detail/kqueue_reactor.hpp
@@ -205,7 +205,7 @@ public:
   // Cancel all operations associated with the given descriptor. The
   // handlers associated with the descriptor will be invoked with the
   // operation_aborted error.
-  void cancel_ops(socket_type descriptor, per_descriptor_data& descriptor_data)
+  void cancel_ops(socket_type , per_descriptor_data& descriptor_data)
   {
     mutex::scoped_lock descriptor_lock(descriptor_data->mutex_);
 
diff --git a/ext/asio/asio/detail/null_thread.hpp b/ext/asio/asio/detail/null_thread.hpp
index d96883f..ce3d470 100644
--- a/ext/asio/asio/detail/null_thread.hpp
+++ b/ext/asio/asio/detail/null_thread.hpp
@@ -40,7 +40,7 @@ class null_thread
 public:
   // Constructor.
   template <typename Function>
-  null_thread(Function f)
+  null_thread(Function )
   {
     asio::system_error e(
         asio::error::operation_not_supported, "thread");
diff --git a/src/bin/auth/auth.spec.pre.in b/src/bin/auth/auth.spec.pre.in
index e95dabd..d88ffb5 100644
--- a/src/bin/auth/auth.spec.pre.in
+++ b/src/bin/auth/auth.spec.pre.in
@@ -63,12 +63,12 @@
         "item_optional": false,
         "item_default": [
           {
-            "address": "::1",
-            "port": 5300
+            "address": "::",
+            "port": 53
           },
           {
-            "address": "127.0.0.1",
-            "port": 5300
+            "address": "0.0.0.0",
+            "port": 53
           }
         ],
         "list_item_spec": {
@@ -87,7 +87,7 @@
               "item_name": "port",
               "item_type": "integer",
               "item_optional": false,
-              "item_default": 5300
+              "item_default": 53
             }
           ]
         }
diff --git a/src/bin/auth/b10-auth.8 b/src/bin/auth/b10-auth.8
index bae8e4a..0356683 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: January 19, 2011
+.\"      Date: March 8, 2011
 .\"    Manual: BIND10
 .\"    Source: BIND10
 .\"  Language: English
 .\"
-.TH "B10\-AUTH" "8" "January 19, 2011" "BIND10" "BIND10"
+.TH "B10\-AUTH" "8" "March 8, 2011" "BIND10" "BIND10"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
@@ -22,7 +22,7 @@
 b10-auth \- Authoritative DNS server
 .SH "SYNOPSIS"
 .HP \w'\fBb10\-auth\fR\ 'u
-\fBb10\-auth\fR [\fB\-4\fR] [\fB\-6\fR] [\fB\-a\ \fR\fB\fIaddress\fR\fR] [\fB\-n\fR] [\fB\-p\ \fR\fB\fInumber\fR\fR] [\fB\-u\ \fR\fB\fIusername\fR\fR] [\fB\-v\fR]
+\fBb10\-auth\fR [\fB\-n\fR] [\fB\-u\ \fR\fB\fIusername\fR\fR] [\fB\-v\fR]
 .SH "DESCRIPTION"
 .PP
 The
@@ -42,55 +42,11 @@ It receives its configurations from
 .PP
 The arguments are as follows:
 .PP
-\fB\-4\fR
-.RS 4
-Enables IPv4 only mode\&. This switch may not be used with
-\fB\-6\fR
-nor
-\fB\-a\fR\&. By default, it listens on both IPv4 and IPv6 (if capable)\&.
-.RE
-.PP
-\fB\-6\fR
-.RS 4
-Enables IPv6 only mode\&. This switch may not be used with
-\fB\-4\fR
-nor
-\fB\-a\fR\&. By default, it listens on both IPv4 and IPv6 (if capable)\&.
-.RE
-.PP
-\fB\-a \fR\fB\fIaddress\fR\fR
-.RS 4
-The IPv4 or IPv6 address to listen on\&. This switch may not be used with
-\fB\-4\fR
-nor
-\fB\-6\fR\&. The default is to listen on all addresses\&. (This is a short term workaround\&. This argument may change\&.)
-.RE
-.PP
 \fB\-n\fR
 .RS 4
 Do not cache answers in memory\&. The default is to use the cache for faster responses\&. The cache keeps the most recent 30,000 answers (positive and negative) in memory for 30 seconds (instead of querying the data source, such as SQLite3 database, each time)\&.
 .RE
 .PP
-\fB\-p \fR\fB\fInumber\fR\fR
-.RS 4
-The port number it listens on\&. The default is 5300\&.
-.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
-The Y1 prototype runs on all interfaces and on this nonstandard port\&.
-.sp .5v
-.RE
-.RE
-.PP
 \fB\-u \fR\fB\fIusername\fR\fR
 .RS 4
 The user name of the
@@ -114,6 +70,18 @@ defines the path to the SQLite3 zone file when using the sqlite datasource\&. Th
 /usr/local/var/bind10\-devel/zone\&.sqlite3\&.
 .PP
 
+\fIlisten_on\fR
+is a list of addresses and ports for
+\fBb10\-auth\fR
+to listen on\&. The list items are the
+\fIaddress\fR
+string and
+\fIport\fR
+number\&. By default,
+\fBb10\-auth\fR
+listens on port 53 on the IPv6 (::) and IPv4 (0\&.0\&.0\&.0) wildcard addresses\&.
+.PP
+
 \fIdatasources\fR
 configures data sources\&. The list items include:
 \fItype\fR
diff --git a/src/bin/auth/b10-auth.xml b/src/bin/auth/b10-auth.xml
index c9e935a..2b53394 100644
--- a/src/bin/auth/b10-auth.xml
+++ b/src/bin/auth/b10-auth.xml
@@ -20,7 +20,7 @@
 <refentry>
 
   <refentryinfo>
-    <date>January 19, 2011</date>
+    <date>March 8, 2011</date>
   </refentryinfo>
 
   <refmeta>
@@ -132,6 +132,15 @@
     </para>
 
     <para>
+      <varname>listen_on</varname> is a list of addresses and ports for
+      <command>b10-auth</command> to listen on.
+      The list items are the <varname>address</varname> string
+      and <varname>port</varname> number.
+      By default, <command>b10-auth</command> listens on port 53
+      on the IPv6 (::) and IPv4 (0.0.0.0) wildcard addresses.
+    </para>
+
+    <para>
       <varname>datasources</varname> configures data sources.
       The list items include:
       <varname>type</varname> to optionally choose the data source type
diff --git a/src/bin/auth/main.cc b/src/bin/auth/main.cc
index 275ae7d..0701b94 100644
--- a/src/bin/auth/main.cc
+++ b/src/bin/auth/main.cc
@@ -122,7 +122,13 @@ main(int argc, char* argv[]) {
     ModuleCCSession* config_session = NULL;
     string xfrout_socket_path;
     if (getenv("B10_FROM_BUILD") != NULL) {
-        xfrout_socket_path = string(getenv("B10_FROM_BUILD")) + "/auth_xfrout_conn";
+        if (getenv("B10_FROM_SOURCE_LOCALSTATEDIR")) {
+            xfrout_socket_path = string("B10_FROM_SOURCE_LOCALSTATEDIR") +
+                "/auth_xfrout_conn";
+        } else {
+            xfrout_socket_path = string(getenv("B10_FROM_BUILD")) +
+                "/auth_xfrout_conn";
+        }
     } else {
         xfrout_socket_path = UNIX_SOCKET_FILE;
     }
diff --git a/src/bin/bind10/bind10.8 b/src/bin/bind10/bind10.8
index f625abe..a75136b 100644
--- a/src/bin/bind10/bind10.8
+++ b/src/bin/bind10/bind10.8
@@ -2,12 +2,12 @@
 .\"     Title: bind10
 .\"    Author: [see the "AUTHORS" section]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: July 29, 2010
+.\"      Date: February 22, 2011
 .\"    Manual: BIND10
 .\"    Source: BIND10
 .\"  Language: English
 .\"
-.TH "BIND10" "8" "July 29, 2010" "BIND10" "BIND10"
+.TH "BIND10" "8" "February 22, 2011" "BIND10" "BIND10"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
@@ -22,7 +22,7 @@
 bind10 \- BIND 10 boss process
 .SH "SYNOPSIS"
 .HP \w'\fBbind10\fR\ 'u
-\fBbind10\fR [\fB\-a\ \fR\fB\fIaddress\fR\fR] [\fB\-m\ \fR\fB\fIfile\fR\fR] [\fB\-n\fR] [\fB\-p\ \fR\fB\fInumber\fR\fR] [\fB\-u\ \fR\fB\fIuser\fR\fR] [\fB\-v\fR] [\fB\-\-address\ \fR\fB\fIaddress\fR\fR] [\fB\-\-msgq\-socket\-file\ \fR\fB\fIfile\fR\fR] [\fB\-\-no\-cache\fR] [\fB\-\-port\ \fR\fB\fInumber\fR\fR] [\fB\-\-user\ \fR\fB\fIuser\fR\fR] [\fB\-\-pretty\-name\ \fR\fB\fIname\fR\fR] [\fB\-\-verbose\fR]
+\fBbind10\fR [\fB\-m\ \fR\fB\fIfile\fR\fR] [\fB\-n\fR] [\fB\-u\ \fR\fB\fIuser\fR\fR] [\fB\-v\fR] [\fB\-\-msgq\-socket\-file\ \fR\fB\fIfile\fR\fR] [\fB\-\-no\-cache\fR] [\fB\-\-user\ \fR\fB\fIuser\fR\fR] [\fB\-\-pretty\-name\ \fR\fB\fIname\fR\fR] [\fB\-\-verbose\fR]
 .SH "DESCRIPTION"
 .PP
 The
@@ -32,13 +32,6 @@ daemon starts up other BIND 10 required daemons\&. It handles restarting of exit
 .PP
 The arguments are as follows:
 .PP
-\fB\-a\fR \fIaddress\fR, \fB\-\-address\fR \fIaddress\fR
-.RS 4
-The IPv4 or IPv6 address for the
-\fBb10-auth\fR(8)
-daemon to listen on\&. The default is to listen on all addresses\&. (This is a short term workaround\&. This argument may change\&.)
-.RE
-.PP
 \fB\-m\fR \fIfile\fR, \fB\-\-msgq\-socket\-file\fR \fIfile\fR
 .RS 4
 The UNIX domain socket file for the
@@ -54,28 +47,6 @@ Disables the hot\-spot caching used by the
 daemon\&.
 .RE
 .PP
-\fB\-p\fR \fInumber\fR, \fB\-\-port\fR \fInumber\fR
-.RS 4
-The port number for the
-\fBb10-auth\fR(8)
-daemon to listen on\&. The default is 5300\&.
-.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 prototype release uses a non\-default port for domain service\&.
-.sp .5v
-.RE
-.RE
-.PP
 \fB\-u\fR \fIuser\fR, \fB\-\-user\fR \fIname\fR
 .RS 4
 The username for
@@ -125,5 +96,5 @@ The
 daemon was initially designed by Shane Kerr of ISC\&.
 .SH "COPYRIGHT"
 .br
-Copyright \(co 2010 Internet Systems Consortium, Inc. ("ISC")
+Copyright \(co 2011 Internet Systems Consortium, Inc. ("ISC")
 .br
diff --git a/src/bin/bind10/bind10.py.in b/src/bin/bind10/bind10.py.in
index 83acf1f..9cdf6f0 100755
--- a/src/bin/bind10/bind10.py.in
+++ b/src/bin/bind10/bind10.py.in
@@ -194,14 +194,21 @@ class CChannelConnectError(Exception): pass
 class BoB:
     """Boss of BIND class."""
     
-    def __init__(self, msgq_socket_file=None, nocache=False, verbose=False,
-    setuid=None, username=None):
+    def __init__(self, msgq_socket_file=None, data_path=None,
+    config_filename=None, nocache=False, verbose=False, setuid=None,
+    username=None, cmdctl_port=None):
         """
             Initialize the Boss of BIND. This is a singleton (only one can run).
         
             The msgq_socket_file specifies the UNIX domain socket file that the
             msgq process listens on.  If verbose is True, then the boss reports
             what it is doing.
+
+            Data path and config filename are passed trough to config manager
+            (if provided) and specify the config file to be used.
+
+            The cmdctl_port is passed to cmdctl and specify on which port it
+            should listen.
         """
         self.cc_session = None
         self.ccs = None
@@ -219,6 +226,9 @@ class BoB:
         self.uid = setuid
         self.username = username
         self.verbose = verbose
+        self.data_path = data_path
+        self.config_filename = config_filename
+        self.cmdctl_port = cmdctl_port
 
     def config_handler(self, new_config):
         # If this is initial update, don't do anything now, leave it to startup
@@ -390,7 +400,12 @@ class BoB:
             Starts the configuration manager process
         """
         self.log_starting("b10-cfgmgr")
-        bind_cfgd = ProcessInfo("b10-cfgmgr", ["b10-cfgmgr"],
+        args = ["b10-cfgmgr"]
+        if self.data_path is not None:
+            args.append("--data-path=" + self.data_path)
+        if self.config_filename is not None:
+            args.append("--config-filename=" + self.config_filename)
+        bind_cfgd = ProcessInfo("b10-cfgmgr", args,
                                 c_channel_env, uid=self.uid,
                                 username=self.username)
         self.processes[bind_cfgd.pid] = bind_cfgd
@@ -500,8 +515,13 @@ class BoB:
         self.start_simple("b10-stats", c_channel_env)
 
     def start_cmdctl(self, c_channel_env):
-        # XXX: we hardcode port 8080
-        self.start_simple("b10-cmdctl", c_channel_env, 8080)
+        """
+            Starts the command control process
+        """
+        args = ["b10-cmdctl"]
+        if self.cmdctl_port is not None:
+            args.append("--port=" + str(self.cmdctl_port))
+        self.start_process("b10-cmdctl", args, c_channel_env, self.cmdctl_port)
 
     def start_all_processes(self):
         """
@@ -785,14 +805,12 @@ def process_rename(option, opt_str, value, parser):
     """Function that renames the process if it is requested by a option."""
     isc.util.process.rename(value)
 
-def main():
-    global options
-    global boss_of_bind
-    # Enforce line buffering on stdout, even when not a TTY
-    sys.stdout = io.TextIOWrapper(sys.stdout.detach(), line_buffering=True)
-
-    # Parse any command-line options.
-    parser = OptionParser(version=VERSION)
+def parse_args(args=sys.argv[1:], Parser=OptionParser):
+    """
+    Function for parsing command line arguments. Returns the
+    options object from OptionParser.
+    """
+    parser = Parser(version=VERSION)
     parser.add_option("-m", "--msgq-socket-file", dest="msgq_socket_file",
                       type="string", default=None,
                       help="UNIX domain socket file the b10-msgq daemon will use")
@@ -805,11 +823,70 @@ def main():
     parser.add_option("--pretty-name", type="string", action="callback",
                       callback=process_rename,
                       help="Set the process name (displayed in ps, top, ...)")
-    (options, args) = parser.parse_args()
+    parser.add_option("-c", "--config-file", action="store",
+                      dest="config_file", default=None,
+                      help="Configuration database filename")
+    parser.add_option("-p", "--data-path", dest="data_path",
+                      help="Directory to search for configuration files",
+                      default=None)
+    parser.add_option("--cmdctl-port", dest="cmdctl_port", type="int",
+                      default=None, help="Port of command control")
+    parser.add_option("--pid-file", dest="pid_file", type="string",
+                      default=None,
+                      help="file to dump the PID of the BIND 10 process")
+
+    (options, args) = parser.parse_args(args)
+
+    if options.cmdctl_port is not None:
+        try:
+            isc.net.parse.port_parse(options.cmdctl_port)
+        except ValueError as e:
+            parser.error(e)
+
     if args:
         parser.print_help()
         sys.exit(1)
 
+    return options
+
+def dump_pid(pid_file):
+    """
+    Dump the PID of the current process to the specified file.  If the given
+    file is None this function does nothing.  If the file already exists,
+    the existing content will be removed.  If a system error happens in
+    creating or writing to the file, the corresponding exception will be
+    propagated to the caller.
+    """
+    if pid_file is None:
+        return
+    f = open(pid_file, "w")
+    f.write('%d\n' % os.getpid())
+    f.close()
+
+def unlink_pid_file(pid_file):
+    """
+    Remove the given file, which is basically expected to be the PID file
+    created by dump_pid().  The specified may or may not exist; if it
+    doesn't this function does nothing.  Other system level errors in removing
+    the file will be propagated as the corresponding exception.
+    """
+    if pid_file is None:
+        return
+    try:
+        os.unlink(pid_file)
+    except OSError as error:
+        if error.errno is not errno.ENOENT:
+            raise
+
+
+def main():
+    global options
+    global boss_of_bind
+    # Enforce line buffering on stdout, even when not a TTY
+    sys.stdout = io.TextIOWrapper(sys.stdout.detach(), line_buffering=True)
+
+    options = parse_args()
+
     # Check user ID.
     setuid = None
     username = None
@@ -858,13 +935,15 @@ def main():
     signal.signal(signal.SIGPIPE, signal.SIG_IGN)
 
     # Go bob!
-    boss_of_bind = BoB(options.msgq_socket_file, options.nocache,
-                       options.verbose, setuid, username)
+    boss_of_bind = BoB(options.msgq_socket_file, options.data_path,
+                       options.config_file, options.nocache, options.verbose,
+                       setuid, username, options.cmdctl_port)
     startup_result = boss_of_bind.startup()
     if startup_result:
         sys.stderr.write("[bind10] Error on startup: %s\n" % startup_result)
         sys.exit(1)
     sys.stdout.write("[bind10] BIND 10 started\n")
+    dump_pid(options.pid_file)
 
     # send "bind10.boot_time" to b10-stats
     time.sleep(1) # wait a second
@@ -918,6 +997,7 @@ def main():
     signal.signal(signal.SIGCHLD, signal.SIG_DFL)
     boss_of_bind.shutdown()
     sys.stdout.write("[bind10] BIND 10 exiting\n");
+    unlink_pid_file(options.pid_file)
     sys.exit(0)
 
 if __name__ == "__main__":
diff --git a/src/bin/bind10/bind10.xml b/src/bin/bind10/bind10.xml
index f3964a6..6331503 100644
--- a/src/bin/bind10/bind10.xml
+++ b/src/bin/bind10/bind10.xml
@@ -48,6 +48,8 @@
       <arg><option>-n</option></arg>
       <arg><option>-u <replaceable>user</replaceable></option></arg>
       <arg><option>-v</option></arg>
+      <arg><option>-c<replaceable>config-filename</replaceable></option></arg>
+      <arg><option>-p<replaceable>data_path</replaceable></option></arg>
       <arg><option>--msgq-socket-file <replaceable>file</replaceable></option></arg>
       <arg><option>--no-cache</option></arg>
       <arg><option>--user <replaceable>user</replaceable></option></arg>
@@ -80,6 +82,31 @@
     <para>The arguments are as follows:</para>
 
     <variablelist>
+      <varlistentry>
+        <term>
+          <option>-c</option><replaceable>config-filename</replaceable>,
+          <option>--config-file</option> <replaceable>config-filename</replaceable>
+        </term>
+        <listitem>
+          <para>The configuration filename to use. Can be either absolute or
+          relative to data path. In case it is absolute, value of data path is
+          not considered.</para>
+          <para>Defaults to b10-config.db.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>
+          <option>-p</option><replaceable>data-path</replaceable>,
+          <option>--data-path</option> <replaceable>data-path</replaceable>
+        </term>
+        <listitem>
+          <para>The path where BIND 10 programs look for various data files.
+          Currently only b10-cfgmgr uses it to locate the configuration file,
+          but the usage might be extended for other programs and other types
+          of files.</para>
+        </listitem>
+      </varlistentry>
 
       <varlistentry>
         <term><option>-m</option> <replaceable>file</replaceable>,
@@ -145,6 +172,9 @@ The default is the basename of ARG 0.
   </refsect1>
 
 <!--
+TODO: configuration section
+-->
+<!--
   <refsect1>
     <title>FILES</title>
     <para><filename></filename>
diff --git a/src/bin/bind10/tests/bind10_test.py b/src/bin/bind10/tests/bind10_test.py
deleted file mode 100644
index f3fe949..0000000
--- a/src/bin/bind10/tests/bind10_test.py
+++ /dev/null
@@ -1,416 +0,0 @@
-from bind10 import ProcessInfo, BoB
-
-# XXX: environment tests are currently disabled, due to the preprocessor
-#      setup that we have now complicating the environment
-
-import unittest
-import sys
-import os
-import signal
-import socket
-from isc.net.addr import IPAddr
-
-class TestProcessInfo(unittest.TestCase):
-    def setUp(self):
-        # redirect stdout to a pipe so we can check that our
-        # process spawning is doing the right thing with stdout
-        self.old_stdout = os.dup(sys.stdout.fileno())
-        self.pipes = os.pipe()
-        os.dup2(self.pipes[1], sys.stdout.fileno())
-        os.close(self.pipes[1])
-        # note that we use dup2() to restore the original stdout
-        # to the main program ASAP in each test... this prevents
-        # hangs reading from the child process (as the pipe is only
-        # open in the child), and also insures nice pretty output
-
-    def tearDown(self):
-        # clean up our stdout munging
-        os.dup2(self.old_stdout, sys.stdout.fileno())
-        os.close(self.pipes[0])
-
-    def test_init(self):
-        pi = ProcessInfo('Test Process', [ '/bin/echo', 'foo' ])
-        os.dup2(self.old_stdout, sys.stdout.fileno())
-        self.assertEqual(pi.name, 'Test Process')
-        self.assertEqual(pi.args, [ '/bin/echo', 'foo' ])
-#        self.assertEqual(pi.env, { 'PATH': os.environ['PATH'],
-#                                   'PYTHON_EXEC': os.environ['PYTHON_EXEC'] })
-        self.assertEqual(pi.dev_null_stdout, False)
-        self.assertEqual(os.read(self.pipes[0], 100), b"foo\n")
-        self.assertNotEqual(pi.process, None)
-        self.assertTrue(type(pi.pid) is int)
-
-#    def test_setting_env(self):
-#        pi = ProcessInfo('Test Process', [ '/bin/true' ], env={'FOO': 'BAR'})
-#        os.dup2(self.old_stdout, sys.stdout.fileno())
-#        self.assertEqual(pi.env, { 'PATH': os.environ['PATH'],
-#                                   'PYTHON_EXEC': os.environ['PYTHON_EXEC'],
-#                                   'FOO': 'BAR' })
-
-    def test_setting_null_stdout(self):
-        pi = ProcessInfo('Test Process', [ '/bin/echo', 'foo' ], 
-                         dev_null_stdout=True)
-        os.dup2(self.old_stdout, sys.stdout.fileno())
-        self.assertEqual(pi.dev_null_stdout, True)
-        self.assertEqual(os.read(self.pipes[0], 100), b"")
-
-    def test_respawn(self):
-        pi = ProcessInfo('Test Process', [ '/bin/echo', 'foo' ])
-        # wait for old process to work...
-        self.assertEqual(os.read(self.pipes[0], 100), b"foo\n")
-        # respawn it
-        old_pid = pi.pid
-        pi.respawn()
-        os.dup2(self.old_stdout, sys.stdout.fileno())
-        # make sure the new one started properly
-        self.assertEqual(pi.name, 'Test Process')
-        self.assertEqual(pi.args, [ '/bin/echo', 'foo' ])
-#        self.assertEqual(pi.env, { 'PATH': os.environ['PATH'],
-#                                   'PYTHON_EXEC': os.environ['PYTHON_EXEC'] })
-        self.assertEqual(pi.dev_null_stdout, False)
-        self.assertEqual(os.read(self.pipes[0], 100), b"foo\n")
-        self.assertNotEqual(pi.process, None)
-        self.assertTrue(type(pi.pid) is int)
-        self.assertNotEqual(pi.pid, old_pid)
-
-class TestBoB(unittest.TestCase):
-    def test_init(self):
-        bob = BoB()
-        self.assertEqual(bob.verbose, False)
-        self.assertEqual(bob.msgq_socket_file, None)
-        self.assertEqual(bob.cc_session, None)
-        self.assertEqual(bob.ccs, None)
-        self.assertEqual(bob.processes, {})
-        self.assertEqual(bob.dead_processes, {})
-        self.assertEqual(bob.runnable, False)
-        self.assertEqual(bob.uid, None)
-        self.assertEqual(bob.username, None)
-        self.assertEqual(bob.nocache, False)
-        self.assertEqual(bob.cfg_start_auth, True)
-        self.assertEqual(bob.cfg_start_resolver, False)
-
-    def test_init_alternate_socket(self):
-        bob = BoB("alt_socket_file")
-        self.assertEqual(bob.verbose, False)
-        self.assertEqual(bob.msgq_socket_file, "alt_socket_file")
-        self.assertEqual(bob.cc_session, None)
-        self.assertEqual(bob.ccs, None)
-        self.assertEqual(bob.processes, {})
-        self.assertEqual(bob.dead_processes, {})
-        self.assertEqual(bob.runnable, False)
-        self.assertEqual(bob.uid, None)
-        self.assertEqual(bob.username, None)
-        self.assertEqual(bob.nocache, False)
-        self.assertEqual(bob.cfg_start_auth, True)
-        self.assertEqual(bob.cfg_start_resolver, False)
-
-# Class for testing the BoB start/stop components routines.
-#
-# Although testing that external processes start is outside the scope
-# of the unit test, by overriding the process start methods we can check
-# that the right processes are started depending on the configuration
-# options.
-class StartStopCheckBob(BoB):
-    def __init__(self):
-        BoB.__init__(self)
-
-# Set flags as to which of the overridden methods has been run.
-        self.msgq = False
-        self.cfgmgr = False
-        self.ccsession = False
-        self.auth = False
-        self.resolver = False
-        self.xfrout = False
-        self.xfrin = False
-        self.zonemgr = False
-        self.stats = False
-        self.cmdctl = False
-        self.c_channel_env = {}
-
-    def read_bind10_config(self):
-        # Configuration options are set directly
-        pass
-
-    def start_msgq(self, c_channel_env):
-        self.msgq = True
-
-    def start_cfgmgr(self, c_channel_env):
-        self.cfgmgr = True
-
-    def start_ccsession(self, c_channel_env):
-        self.ccsession = True
-
-    def start_auth(self, c_channel_env):
-        self.auth = True
-
-    def start_resolver(self, c_channel_env):
-        self.resolver = True
-
-    def start_xfrout(self, c_channel_env):
-        self.xfrout = True
-
-    def start_xfrin(self, c_channel_env):
-        self.xfrin = True
-
-    def start_zonemgr(self, c_channel_env):
-        self.zonemgr = True
-
-    def start_stats(self, c_channel_env):
-        self.stats = True
-
-    def start_cmdctl(self, c_channel_env):
-        self.cmdctl = True
-
-    # We don't really use all of these stop_ methods. But it might turn out
-    # someone would add some stop_ method to BoB and we want that one overriden
-    # in case he forgets to update the tests.
-    def stop_msgq(self):
-        self.msgq = False
-
-    def stop_cfgmgr(self):
-        self.cfgmgr = False
-
-    def stop_ccsession(self):
-        self.ccsession = False
-
-    def stop_auth(self):
-        self.auth = False
-
-    def stop_resolver(self):
-        self.resolver = False
-
-    def stop_xfrout(self):
-        self.xfrout = False
-
-    def stop_xfrin(self):
-        self.xfrin = False
-
-    def stop_zonemgr(self):
-        self.zonemgr = False
-
-    def stop_stats(self):
-        self.stats = False
-
-    def stop_cmdctl(self):
-        self.cmdctl = False
-
-class TestStartStopProcessesBob(unittest.TestCase):
-    """
-    Check that the start_all_processes method starts the right combination
-    of processes and that the right processes are started and stopped
-    according to changes in configuration.
-    """
-    def check_started(self, bob, core, auth, resolver):
-        """
-        Check that the right sets of services are started. The ones that
-        should be running are specified by the core, auth and resolver parameters
-        (they are groups of processes, eg. auth means b10-auth, -xfrout, -xfrin
-        and -zonemgr).
-        """
-        self.assertEqual(bob.msgq, core)
-        self.assertEqual(bob.cfgmgr, core)
-        self.assertEqual(bob.ccsession, core)
-        self.assertEqual(bob.auth, auth)
-        self.assertEqual(bob.resolver, resolver)
-        self.assertEqual(bob.xfrout, auth)
-        self.assertEqual(bob.xfrin, auth)
-        self.assertEqual(bob.zonemgr, auth)
-        self.assertEqual(bob.stats, core)
-        self.assertEqual(bob.cmdctl, core)
-
-    def check_preconditions(self, bob):
-        self.check_started(bob, False, False, False)
-
-    def check_started_none(self, bob):
-        """
-        Check that the situation is according to configuration where no servers
-        should be started. Some processes still need to be running.
-        """
-        self.check_started(bob, True, False, False)
-
-    def check_started_both(self, bob):
-        """
-        Check the situation is according to configuration where both servers
-        (auth and resolver) are enabled.
-        """
-        self.check_started(bob, True, True, True)
-
-    def check_started_auth(self, bob):
-        """
-        Check the set of processes needed to run auth only is started.
-        """
-        self.check_started(bob, True, True, False)
-
-    def check_started_resolver(self, bob):
-        """
-        Check the set of processes needed to run resolver only is started.
-        """
-        self.check_started(bob, True, False, True)
-
-    # Checks the processes started when starting neither auth nor resolver
-    # is specified.
-    def test_start_none(self):
-        # Create BoB and ensure correct initialization
-        bob = StartStopCheckBob()
-        self.check_preconditions(bob)
-
-        # Start processes and check what was started
-        bob.cfg_start_auth = False
-        bob.cfg_start_resolver = False
-
-        bob.start_all_processes()
-        self.check_started_none(bob)
-
-    # Checks the processes started when starting only the auth process
-    def test_start_auth(self):
-        # Create BoB and ensure correct initialization
-        bob = StartStopCheckBob()
-        self.check_preconditions(bob)
-
-        # Start processes and check what was started
-        bob.cfg_start_auth = True
-        bob.cfg_start_resolver = False
-
-        bob.start_all_processes()
-
-        self.check_started_auth(bob)
-
-    # Checks the processes started when starting only the resolver process
-    def test_start_resolver(self):
-        # Create BoB and ensure correct initialization
-        bob = StartStopCheckBob()
-        self.check_preconditions(bob)
-
-        # Start processes and check what was started
-        bob.cfg_start_auth = False
-        bob.cfg_start_resolver = True
-
-        bob.start_all_processes()
-
-        self.check_started_resolver(bob)
-
-    # Checks the processes started when starting both auth and resolver process
-    def test_start_both(self):
-        # Create BoB and ensure correct initialization
-        bob = StartStopCheckBob()
-        self.check_preconditions(bob)
-
-        # Start processes and check what was started
-        bob.cfg_start_auth = True
-        bob.cfg_start_resolver = True
-
-        bob.start_all_processes()
-
-        self.check_started_both(bob)
-
-    def test_config_start(self):
-        """
-        Test that the configuration starts and stops processes according
-        to configuration changes.
-        """
-
-        # Create BoB and ensure correct initialization
-        bob = StartStopCheckBob()
-        self.check_preconditions(bob)
-
-        # Start processes (nothing much should be started, as in
-        # test_start_none)
-        bob.cfg_start_auth = False
-        bob.cfg_start_resolver = False
-
-        bob.start_all_processes()
-        bob.runnable = True
-        self.check_started_none(bob)
-
-        # Enable both at once
-        bob.config_handler({'start_auth': True, 'start_resolver': True})
-        self.check_started_both(bob)
-
-        # Not touched by empty change
-        bob.config_handler({})
-        self.check_started_both(bob)
-
-        # Not touched by change to the same configuration
-        bob.config_handler({'start_auth': True, 'start_resolver': True})
-        self.check_started_both(bob)
-
-        # Turn them both off again
-        bob.config_handler({'start_auth': False, 'start_resolver': False})
-        self.check_started_none(bob)
-
-        # Not touched by empty change
-        bob.config_handler({})
-        self.check_started_none(bob)
-
-        # Not touched by change to the same configuration
-        bob.config_handler({'start_auth': False, 'start_resolver': False})
-        self.check_started_none(bob)
-
-        # Start and stop auth separately
-        bob.config_handler({'start_auth': True})
-        self.check_started_auth(bob)
-
-        bob.config_handler({'start_auth': False})
-        self.check_started_none(bob)
-
-        # Start and stop resolver separately
-        bob.config_handler({'start_resolver': True})
-        self.check_started_resolver(bob)
-
-        bob.config_handler({'start_resolver': False})
-        self.check_started_none(bob)
-
-        # Alternate
-        bob.config_handler({'start_auth': True})
-        self.check_started_auth(bob)
-
-        bob.config_handler({'start_auth': False, 'start_resolver': True})
-        self.check_started_resolver(bob)
-
-        bob.config_handler({'start_auth': True, 'start_resolver': False})
-        self.check_started_auth(bob)
-
-    def test_config_start_once(self):
-        """
-        Tests that a process is started only once.
-        """
-        # Create BoB and ensure correct initialization
-        bob = StartStopCheckBob()
-        self.check_preconditions(bob)
-
-        # Start processes (both)
-        bob.cfg_start_auth = True
-        bob.cfg_start_resolver = True
-
-        bob.start_all_processes()
-        bob.runnable = True
-        self.check_started_both(bob)
-
-        bob.start_auth = lambda: self.fail("Started auth again")
-        bob.start_xfrout = lambda: self.fail("Started xfrout again")
-        bob.start_xfrin = lambda: self.fail("Started xfrin again")
-        bob.start_zonemgr = lambda: self.fail("Started zonemgr again")
-        bob.start_resolver = lambda: self.fail("Started resolver again")
-
-        # Send again we want to start them. Should not do it, as they are.
-        bob.config_handler({'start_auth': True})
-        bob.config_handler({'start_resolver': True})
-
-    def test_config_not_started_early(self):
-        """
-        Test that processes are not started by the config handler before
-        startup.
-        """
-        bob = StartStopCheckBob()
-        self.check_preconditions(bob)
-
-        bob.start_auth = lambda: self.fail("Started auth again")
-        bob.start_xfrout = lambda: self.fail("Started xfrout again")
-        bob.start_xfrin = lambda: self.fail("Started xfrin again")
-        bob.start_zonemgr = lambda: self.fail("Started zonemgr again")
-        bob.start_resolver = lambda: self.fail("Started resolver again")
-
-        bob.config_handler({'start_auth': True, 'start_resolver': True})
-
-if __name__ == '__main__':
-    unittest.main()
diff --git a/src/bin/bind10/tests/bind10_test.py.in b/src/bin/bind10/tests/bind10_test.py.in
new file mode 100644
index 0000000..a53df1a
--- /dev/null
+++ b/src/bin/bind10/tests/bind10_test.py.in
@@ -0,0 +1,515 @@
+from bind10 import ProcessInfo, BoB, parse_args, dump_pid, unlink_pid_file
+
+# XXX: environment tests are currently disabled, due to the preprocessor
+#      setup that we have now complicating the environment
+
+import unittest
+import sys
+import os
+import signal
+import socket
+from isc.net.addr import IPAddr
+from isc.testutils.parse_args import TestOptParser, OptsError
+
+class TestProcessInfo(unittest.TestCase):
+    def setUp(self):
+        # redirect stdout to a pipe so we can check that our
+        # process spawning is doing the right thing with stdout
+        self.old_stdout = os.dup(sys.stdout.fileno())
+        self.pipes = os.pipe()
+        os.dup2(self.pipes[1], sys.stdout.fileno())
+        os.close(self.pipes[1])
+        # note that we use dup2() to restore the original stdout
+        # to the main program ASAP in each test... this prevents
+        # hangs reading from the child process (as the pipe is only
+        # open in the child), and also insures nice pretty output
+
+    def tearDown(self):
+        # clean up our stdout munging
+        os.dup2(self.old_stdout, sys.stdout.fileno())
+        os.close(self.pipes[0])
+
+    def test_init(self):
+        pi = ProcessInfo('Test Process', [ '/bin/echo', 'foo' ])
+        os.dup2(self.old_stdout, sys.stdout.fileno())
+        self.assertEqual(pi.name, 'Test Process')
+        self.assertEqual(pi.args, [ '/bin/echo', 'foo' ])
+#        self.assertEqual(pi.env, { 'PATH': os.environ['PATH'],
+#                                   'PYTHON_EXEC': os.environ['PYTHON_EXEC'] })
+        self.assertEqual(pi.dev_null_stdout, False)
+        self.assertEqual(os.read(self.pipes[0], 100), b"foo\n")
+        self.assertNotEqual(pi.process, None)
+        self.assertTrue(type(pi.pid) is int)
+
+#    def test_setting_env(self):
+#        pi = ProcessInfo('Test Process', [ '/bin/true' ], env={'FOO': 'BAR'})
+#        os.dup2(self.old_stdout, sys.stdout.fileno())
+#        self.assertEqual(pi.env, { 'PATH': os.environ['PATH'],
+#                                   'PYTHON_EXEC': os.environ['PYTHON_EXEC'],
+#                                   'FOO': 'BAR' })
+
+    def test_setting_null_stdout(self):
+        pi = ProcessInfo('Test Process', [ '/bin/echo', 'foo' ],
+                         dev_null_stdout=True)
+        os.dup2(self.old_stdout, sys.stdout.fileno())
+        self.assertEqual(pi.dev_null_stdout, True)
+        self.assertEqual(os.read(self.pipes[0], 100), b"")
+
+    def test_respawn(self):
+        pi = ProcessInfo('Test Process', [ '/bin/echo', 'foo' ])
+        # wait for old process to work...
+        self.assertEqual(os.read(self.pipes[0], 100), b"foo\n")
+        # respawn it
+        old_pid = pi.pid
+        pi.respawn()
+        os.dup2(self.old_stdout, sys.stdout.fileno())
+        # make sure the new one started properly
+        self.assertEqual(pi.name, 'Test Process')
+        self.assertEqual(pi.args, [ '/bin/echo', 'foo' ])
+#        self.assertEqual(pi.env, { 'PATH': os.environ['PATH'],
+#                                   'PYTHON_EXEC': os.environ['PYTHON_EXEC'] })
+        self.assertEqual(pi.dev_null_stdout, False)
+        self.assertEqual(os.read(self.pipes[0], 100), b"foo\n")
+        self.assertNotEqual(pi.process, None)
+        self.assertTrue(type(pi.pid) is int)
+        self.assertNotEqual(pi.pid, old_pid)
+
+class TestBoB(unittest.TestCase):
+    def test_init(self):
+        bob = BoB()
+        self.assertEqual(bob.verbose, False)
+        self.assertEqual(bob.msgq_socket_file, None)
+        self.assertEqual(bob.cc_session, None)
+        self.assertEqual(bob.ccs, None)
+        self.assertEqual(bob.processes, {})
+        self.assertEqual(bob.dead_processes, {})
+        self.assertEqual(bob.runnable, False)
+        self.assertEqual(bob.uid, None)
+        self.assertEqual(bob.username, None)
+        self.assertEqual(bob.nocache, False)
+        self.assertEqual(bob.cfg_start_auth, True)
+        self.assertEqual(bob.cfg_start_resolver, False)
+
+    def test_init_alternate_socket(self):
+        bob = BoB("alt_socket_file")
+        self.assertEqual(bob.verbose, False)
+        self.assertEqual(bob.msgq_socket_file, "alt_socket_file")
+        self.assertEqual(bob.cc_session, None)
+        self.assertEqual(bob.ccs, None)
+        self.assertEqual(bob.processes, {})
+        self.assertEqual(bob.dead_processes, {})
+        self.assertEqual(bob.runnable, False)
+        self.assertEqual(bob.uid, None)
+        self.assertEqual(bob.username, None)
+        self.assertEqual(bob.nocache, False)
+        self.assertEqual(bob.cfg_start_auth, True)
+        self.assertEqual(bob.cfg_start_resolver, False)
+
+# Class for testing the BoB start/stop components routines.
+#
+# Although testing that external processes start is outside the scope
+# of the unit test, by overriding the process start methods we can check
+# that the right processes are started depending on the configuration
+# options.
+class StartStopCheckBob(BoB):
+    def __init__(self):
+        BoB.__init__(self)
+
+# Set flags as to which of the overridden methods has been run.
+        self.msgq = False
+        self.cfgmgr = False
+        self.ccsession = False
+        self.auth = False
+        self.resolver = False
+        self.xfrout = False
+        self.xfrin = False
+        self.zonemgr = False
+        self.stats = False
+        self.cmdctl = False
+        self.c_channel_env = {}
+
+    def read_bind10_config(self):
+        # Configuration options are set directly
+        pass
+
+    def start_msgq(self, c_channel_env):
+        self.msgq = True
+
+    def start_cfgmgr(self, c_channel_env):
+        self.cfgmgr = True
+
+    def start_ccsession(self, c_channel_env):
+        self.ccsession = True
+
+    def start_auth(self, c_channel_env):
+        self.auth = True
+
+    def start_resolver(self, c_channel_env):
+        self.resolver = True
+
+    def start_xfrout(self, c_channel_env):
+        self.xfrout = True
+
+    def start_xfrin(self, c_channel_env):
+        self.xfrin = True
+
+    def start_zonemgr(self, c_channel_env):
+        self.zonemgr = True
+
+    def start_stats(self, c_channel_env):
+        self.stats = True
+
+    def start_cmdctl(self, c_channel_env):
+        self.cmdctl = True
+
+    # We don't really use all of these stop_ methods. But it might turn out
+    # someone would add some stop_ method to BoB and we want that one overriden
+    # in case he forgets to update the tests.
+    def stop_msgq(self):
+        self.msgq = False
+
+    def stop_cfgmgr(self):
+        self.cfgmgr = False
+
+    def stop_ccsession(self):
+        self.ccsession = False
+
+    def stop_auth(self):
+        self.auth = False
+
+    def stop_resolver(self):
+        self.resolver = False
+
+    def stop_xfrout(self):
+        self.xfrout = False
+
+    def stop_xfrin(self):
+        self.xfrin = False
+
+    def stop_zonemgr(self):
+        self.zonemgr = False
+
+    def stop_stats(self):
+        self.stats = False
+
+    def stop_cmdctl(self):
+        self.cmdctl = False
+
+class TestStartStopProcessesBob(unittest.TestCase):
+    """
+    Check that the start_all_processes method starts the right combination
+    of processes and that the right processes are started and stopped
+    according to changes in configuration.
+    """
+    def check_started(self, bob, core, auth, resolver):
+        """
+        Check that the right sets of services are started. The ones that
+        should be running are specified by the core, auth and resolver parameters
+        (they are groups of processes, eg. auth means b10-auth, -xfrout, -xfrin
+        and -zonemgr).
+        """
+        self.assertEqual(bob.msgq, core)
+        self.assertEqual(bob.cfgmgr, core)
+        self.assertEqual(bob.ccsession, core)
+        self.assertEqual(bob.auth, auth)
+        self.assertEqual(bob.resolver, resolver)
+        self.assertEqual(bob.xfrout, auth)
+        self.assertEqual(bob.xfrin, auth)
+        self.assertEqual(bob.zonemgr, auth)
+        self.assertEqual(bob.stats, core)
+        self.assertEqual(bob.cmdctl, core)
+
+    def check_preconditions(self, bob):
+        self.check_started(bob, False, False, False)
+
+    def check_started_none(self, bob):
+        """
+        Check that the situation is according to configuration where no servers
+        should be started. Some processes still need to be running.
+        """
+        self.check_started(bob, True, False, False)
+
+    def check_started_both(self, bob):
+        """
+        Check the situation is according to configuration where both servers
+        (auth and resolver) are enabled.
+        """
+        self.check_started(bob, True, True, True)
+
+    def check_started_auth(self, bob):
+        """
+        Check the set of processes needed to run auth only is started.
+        """
+        self.check_started(bob, True, True, False)
+
+    def check_started_resolver(self, bob):
+        """
+        Check the set of processes needed to run resolver only is started.
+        """
+        self.check_started(bob, True, False, True)
+
+    # Checks the processes started when starting neither auth nor resolver
+    # is specified.
+    def test_start_none(self):
+        # Create BoB and ensure correct initialization
+        bob = StartStopCheckBob()
+        self.check_preconditions(bob)
+
+        # Start processes and check what was started
+        bob.cfg_start_auth = False
+        bob.cfg_start_resolver = False
+
+        bob.start_all_processes()
+        self.check_started_none(bob)
+
+    # Checks the processes started when starting only the auth process
+    def test_start_auth(self):
+        # Create BoB and ensure correct initialization
+        bob = StartStopCheckBob()
+        self.check_preconditions(bob)
+
+        # Start processes and check what was started
+        bob.cfg_start_auth = True
+        bob.cfg_start_resolver = False
+
+        bob.start_all_processes()
+
+        self.check_started_auth(bob)
+
+    # Checks the processes started when starting only the resolver process
+    def test_start_resolver(self):
+        # Create BoB and ensure correct initialization
+        bob = StartStopCheckBob()
+        self.check_preconditions(bob)
+
+        # Start processes and check what was started
+        bob.cfg_start_auth = False
+        bob.cfg_start_resolver = True
+
+        bob.start_all_processes()
+
+        self.check_started_resolver(bob)
+
+    # Checks the processes started when starting both auth and resolver process
+    def test_start_both(self):
+        # Create BoB and ensure correct initialization
+        bob = StartStopCheckBob()
+        self.check_preconditions(bob)
+
+        # Start processes and check what was started
+        bob.cfg_start_auth = True
+        bob.cfg_start_resolver = True
+
+        bob.start_all_processes()
+
+        self.check_started_both(bob)
+
+    def test_config_start(self):
+        """
+        Test that the configuration starts and stops processes according
+        to configuration changes.
+        """
+
+        # Create BoB and ensure correct initialization
+        bob = StartStopCheckBob()
+        self.check_preconditions(bob)
+
+        # Start processes (nothing much should be started, as in
+        # test_start_none)
+        bob.cfg_start_auth = False
+        bob.cfg_start_resolver = False
+
+        bob.start_all_processes()
+        bob.runnable = True
+        self.check_started_none(bob)
+
+        # Enable both at once
+        bob.config_handler({'start_auth': True, 'start_resolver': True})
+        self.check_started_both(bob)
+
+        # Not touched by empty change
+        bob.config_handler({})
+        self.check_started_both(bob)
+
+        # Not touched by change to the same configuration
+        bob.config_handler({'start_auth': True, 'start_resolver': True})
+        self.check_started_both(bob)
+
+        # Turn them both off again
+        bob.config_handler({'start_auth': False, 'start_resolver': False})
+        self.check_started_none(bob)
+
+        # Not touched by empty change
+        bob.config_handler({})
+        self.check_started_none(bob)
+
+        # Not touched by change to the same configuration
+        bob.config_handler({'start_auth': False, 'start_resolver': False})
+        self.check_started_none(bob)
+
+        # Start and stop auth separately
+        bob.config_handler({'start_auth': True})
+        self.check_started_auth(bob)
+
+        bob.config_handler({'start_auth': False})
+        self.check_started_none(bob)
+
+        # Start and stop resolver separately
+        bob.config_handler({'start_resolver': True})
+        self.check_started_resolver(bob)
+
+        bob.config_handler({'start_resolver': False})
+        self.check_started_none(bob)
+
+        # Alternate
+        bob.config_handler({'start_auth': True})
+        self.check_started_auth(bob)
+
+        bob.config_handler({'start_auth': False, 'start_resolver': True})
+        self.check_started_resolver(bob)
+
+        bob.config_handler({'start_auth': True, 'start_resolver': False})
+        self.check_started_auth(bob)
+
+    def test_config_start_once(self):
+        """
+        Tests that a process is started only once.
+        """
+        # Create BoB and ensure correct initialization
+        bob = StartStopCheckBob()
+        self.check_preconditions(bob)
+
+        # Start processes (both)
+        bob.cfg_start_auth = True
+        bob.cfg_start_resolver = True
+
+        bob.start_all_processes()
+        bob.runnable = True
+        self.check_started_both(bob)
+
+        bob.start_auth = lambda: self.fail("Started auth again")
+        bob.start_xfrout = lambda: self.fail("Started xfrout again")
+        bob.start_xfrin = lambda: self.fail("Started xfrin again")
+        bob.start_zonemgr = lambda: self.fail("Started zonemgr again")
+        bob.start_resolver = lambda: self.fail("Started resolver again")
+
+        # Send again we want to start them. Should not do it, as they are.
+        bob.config_handler({'start_auth': True})
+        bob.config_handler({'start_resolver': True})
+
+    def test_config_not_started_early(self):
+        """
+        Test that processes are not started by the config handler before
+        startup.
+        """
+        bob = StartStopCheckBob()
+        self.check_preconditions(bob)
+
+        bob.start_auth = lambda: self.fail("Started auth again")
+        bob.start_xfrout = lambda: self.fail("Started xfrout again")
+        bob.start_xfrin = lambda: self.fail("Started xfrin again")
+        bob.start_zonemgr = lambda: self.fail("Started zonemgr again")
+        bob.start_resolver = lambda: self.fail("Started resolver again")
+
+        bob.config_handler({'start_auth': True, 'start_resolver': True})
+
+class TestParseArgs(unittest.TestCase):
+    """
+    This tests parsing of arguments of the bind10 master process.
+    """
+    #TODO: Write tests for the original parsing, bad options, etc.
+    def test_no_opts(self):
+        """
+        Test correct default values when no options are passed.
+        """
+        options = parse_args([], TestOptParser)
+        self.assertEqual(None, options.data_path)
+        self.assertEqual(None, options.config_file)
+        self.assertEqual(None, options.cmdctl_port)
+
+    def test_data_path(self):
+        """
+        Test it can parse the data path.
+        """
+        self.assertRaises(OptsError, parse_args, ['-p'], TestOptParser)
+        self.assertRaises(OptsError, parse_args, ['--data-path'],
+                          TestOptParser)
+        options = parse_args(['-p', '/data/path'], TestOptParser)
+        self.assertEqual('/data/path', options.data_path)
+        options = parse_args(['--data-path=/data/path'], TestOptParser)
+        self.assertEqual('/data/path', options.data_path)
+
+    def test_config_filename(self):
+        """
+        Test it can parse the config switch.
+        """
+        self.assertRaises(OptsError, parse_args, ['-c'], TestOptParser)
+        self.assertRaises(OptsError, parse_args, ['--config-file'],
+                          TestOptParser)
+        options = parse_args(['-c', 'config-file'], TestOptParser)
+        self.assertEqual('config-file', options.config_file)
+        options = parse_args(['--config-file=config-file'], TestOptParser)
+        self.assertEqual('config-file', options.config_file)
+
+    def test_cmdctl_port(self):
+        """
+        Test it can parse the command control port.
+        """
+        self.assertRaises(OptsError, parse_args, ['--cmdctl-port=abc'],
+                                                TestOptParser)
+        self.assertRaises(OptsError, parse_args, ['--cmdctl-port=100000000'],
+                                                TestOptParser)
+        self.assertRaises(OptsError, parse_args, ['--cmdctl-port'],
+                          TestOptParser)
+        options = parse_args(['--cmdctl-port=1234'], TestOptParser)
+        self.assertEqual(1234, options.cmdctl_port)
+
+class TestPIDFile(unittest.TestCase):
+    def setUp(self):
+        self.pid_file = '@builddir@' + os.sep + 'bind10.pid'
+        if os.path.exists(self.pid_file):
+            os.unlink(self.pid_file)
+
+    def tearDown(self):
+        if os.path.exists(self.pid_file):
+            os.unlink(self.pid_file)
+
+    def check_pid_file(self):
+        # dump PID to the file, and confirm the content is correct
+        dump_pid(self.pid_file)
+        my_pid = os.getpid()
+        self.assertEqual(my_pid, int(open(self.pid_file, "r").read()))
+
+    def test_dump_pid(self):
+        self.check_pid_file()
+
+        # make sure any existing content will be removed
+        open(self.pid_file, "w").write('dummy data\n')
+        self.check_pid_file()
+
+    def test_unlink_pid_file_notexist(self):
+        dummy_data = 'dummy_data\n'
+        open(self.pid_file, "w").write(dummy_data)
+        unlink_pid_file("no_such_pid_file")
+        # the file specified for unlink_pid_file doesn't exist,
+        # and the original content of the file should be intact.
+        self.assertEqual(dummy_data, open(self.pid_file, "r").read())
+
+    def test_dump_pid_with_none(self):
+        # Check the behavior of dump_pid() and unlink_pid_file() with None.
+        # This should be no-op.
+        dump_pid(None)
+        self.assertFalse(os.path.exists(self.pid_file))
+
+        dummy_data = 'dummy_data\n'
+        open(self.pid_file, "w").write(dummy_data)
+        unlink_pid_file(None)
+        self.assertEqual(dummy_data, open(self.pid_file, "r").read())
+
+    def test_dump_pid_failure(self):
+        # the attempt to open file will fail, which should result in exception.
+        self.assertRaises(IOError, dump_pid,
+                          'nonexistent_dir' + os.sep + 'bind10.pid')
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/src/bin/bindctl/Makefile.am b/src/bin/bindctl/Makefile.am
index e95af78..2f412ec 100644
--- a/src/bin/bindctl/Makefile.am
+++ b/src/bin/bindctl/Makefile.am
@@ -5,12 +5,13 @@ man_MANS = bindctl.1
 
 EXTRA_DIST = $(man_MANS) bindctl.xml
 
-python_PYTHON = __init__.py bindcmd.py cmdparse.py exception.py moduleinfo.py mycollections.py
+python_PYTHON = __init__.py bindcmd.py cmdparse.py exception.py moduleinfo.py \
+		mycollections.py
 pythondir = $(pyexecdir)/bindctl
 
 bindctldir = $(pkgdatadir)
 
-CLEANFILES = bindctl
+CLEANFILES = bindctl bindctl_main.pyc
 
 if ENABLE_MAN
 
@@ -19,8 +20,8 @@ bindctl.1: bindctl.xml
 
 endif
 
-bindctl: bindctl-source.py
+bindctl: bindctl_main.py
 	$(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" \
 	       -e "s|@@SYSCONFDIR@@|@sysconfdir@|" \
-	       -e "s|@@LIBEXECDIR@@|$(pkglibexecdir)|" bindctl-source.py >$@
+	       -e "s|@@LIBEXECDIR@@|$(pkglibexecdir)|" bindctl_main.py >$@
 	chmod a+x $@
diff --git a/src/bin/bindctl/bindcmd.py b/src/bin/bindctl/bindcmd.py
index 683dda9..8973aa5 100644
--- a/src/bin/bindctl/bindcmd.py
+++ b/src/bin/bindctl/bindcmd.py
@@ -87,7 +87,8 @@ class ValidatedHTTPSConnection(http.client.HTTPSConnection):
 class BindCmdInterpreter(Cmd):
     """simple bindctl example."""    
 
-    def __init__(self, server_port = 'localhost:8080', pem_file = None):
+    def __init__(self, server_port='localhost:8080', pem_file=None,
+                 csv_file_dir=None):
         Cmd.__init__(self)
         self.location = ""
         self.prompt_end = '> '
@@ -103,7 +104,12 @@ class BindCmdInterpreter(Cmd):
                                              ca_certs=pem_file)
         self.session_id = self._get_session_id()
         self.config_data = None
-        
+        if csv_file_dir is not None:
+            self.csv_file_dir = csv_file_dir
+        else:
+            self.csv_file_dir = pwd.getpwnam(getpass.getuser()).pw_dir + \
+                os.sep + '.bind10' + os.sep
+
     def _get_session_id(self):
         '''Generate one session id for the connection. '''
         rand = os.urandom(16)
@@ -117,14 +123,19 @@ class BindCmdInterpreter(Cmd):
         '''Parse commands from user and send them to cmdctl. '''
         try:
             if not self.login_to_cmdctl():
-                return 
+                return
 
             self.cmdloop()
+            print('\nExit from bindctl')
         except FailToLogin as err:
             # error already printed when this was raised, ignoring
             pass
         except KeyboardInterrupt:
             print('\nExit from bindctl')
+        except socket.error as err:
+            print('Failed to send request, the connection is closed')
+        except http.client.CannotSendRequest:
+            print('Can not send request, the connection is busy')
 
     def _get_saved_user_info(self, dir, file_name):
         ''' Read all the available username and password pairs saved in 
@@ -175,9 +186,7 @@ class BindCmdInterpreter(Cmd):
         time, username and password saved in 'default_user.csv' will be
         used first.
         '''
-        csv_file_dir = pwd.getpwnam(getpass.getuser()).pw_dir
-        csv_file_dir += os.sep + '.bind10' + os.sep
-        users = self._get_saved_user_info(csv_file_dir, CSV_FILE_NAME)
+        users = self._get_saved_user_info(self.csv_file_dir, CSV_FILE_NAME)
         for row in users:
             param = {'username': row[0], 'password' : row[1]}
             try:
@@ -188,8 +197,10 @@ class BindCmdInterpreter(Cmd):
                 raise FailToLogin()
 
             if response.status == http.client.OK:
-                print(data + ' login as ' + row[0] )
-                return True 
+                # Is interactive?
+                if sys.stdin.isatty():
+                    print(data + ' login as ' + row[0])
+                return True
 
         count = 0
         print("[TEMP MESSAGE]: username :root  password :bind10")
@@ -211,7 +222,8 @@ class BindCmdInterpreter(Cmd):
                 raise FailToLogin()
 
             if response.status == http.client.OK:
-                self._save_user_info(username, passwd, csv_file_dir, CSV_FILE_NAME)
+                self._save_user_info(username, passwd, self.csv_file_dir,
+                                     CSV_FILE_NAME)
                 return True
 
     def _update_commands(self):
@@ -268,8 +280,9 @@ class BindCmdInterpreter(Cmd):
         self._update_commands()
 
     def precmd(self, line):
-        self._update_all_modules_info()
-        return line 
+        if line != 'EOF':
+            self._update_all_modules_info()
+        return line
 
     def postcmd(self, stop, line):
         '''Update the prompt after every command, but only if we
diff --git a/src/bin/bindctl/bindctl-source.py.in b/src/bin/bindctl/bindctl-source.py.in
deleted file mode 100644
index 080c3bc..0000000
--- a/src/bin/bindctl/bindctl-source.py.in
+++ /dev/null
@@ -1,135 +0,0 @@
-#!@PYTHON@
-
-# Copyright (C) 2009  Internet Systems Consortium.
-#
-# Permission to use, copy, modify, and distribute this software for any
-# purpose with or without fee is hereby granted, provided that the above
-# copyright notice and this permission notice appear in all copies.
-#
-# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
-# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
-# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
-# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
-# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
-# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
-# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-"""This is the main calling class for the bindctl configuration and
-   command tool. It sets up a command interpreter and runs that."""
-
-import sys; sys.path.append ('@@PYTHONPATH@@')
-
-from bindctl.moduleinfo import *
-from bindctl.bindcmd import *
-import pprint
-from optparse import OptionParser, OptionValueError
-import isc.util.process
-
-isc.util.process.rename()
-
-# This is the version that gets displayed to the user.
-# The VERSION string consists of the module name, the module version
-# number, and the overall BIND 10 version number (set in configure.ac).
-VERSION = "bindctl 20110217 (BIND 10 @PACKAGE_VERSION@)"
-
-DEFAULT_IDENTIFIER_DESC = "The identifier specifies the config item. Child elements are separated with the '/' character. List indices can be specified with '[i]', where i is an integer specifying the index, starting with 0. Examples: 'Boss/start_auth', 'Recurse/listen_on[0]/address'. If no identifier is given, shows the item at the current location."
-
-def prepare_config_commands(tool):
-    '''Prepare fixed commands for local configuration editing'''
-    module = ModuleInfo(name = CONFIG_MODULE_NAME, desc = "Configuration commands.")
-    cmd = CommandInfo(name = "show", desc = "Show configuration.")
-    param = ParamInfo(name = "argument", type = "string", optional=True, desc = "If you specify the argument 'all' (before the identifier), recursively show all child elements for the given identifier.")
-    cmd.add_param(param)
-    param = ParamInfo(name = "identifier", type = "string", optional=True, desc = DEFAULT_IDENTIFIER_DESC)
-    cmd.add_param(param)
-    module.add_command(cmd)
-
-    cmd = CommandInfo(name = "show_json", desc = "Show full configuration in JSON format.")
-    param = ParamInfo(name = "identifier", type = "string", optional=True, desc = DEFAULT_IDENTIFIER_DESC)
-    cmd.add_param(param)
-    module.add_command(cmd)
-
-    cmd = CommandInfo(name = "add", desc = "Add an entry to configuration list. If no value is given, a default value is added.")
-    param = ParamInfo(name = "identifier", type = "string", optional=True, desc = DEFAULT_IDENTIFIER_DESC)
-    cmd.add_param(param)
-    param = ParamInfo(name = "value", type = "string", optional=True, desc = "Specifies a value to add to the list. It must be in correct JSON format and complete.")
-    cmd.add_param(param)
-    module.add_command(cmd)
-
-    cmd = CommandInfo(name = "remove", desc = "Remove entry from configuration list.")
-    param = ParamInfo(name = "identifier", type = "string", optional=True, desc = DEFAULT_IDENTIFIER_DESC)
-    cmd.add_param(param)
-    param = ParamInfo(name = "value", type = "string", optional=True, desc = "Specifies a value to remove from the list. It must be in correct JSON format and complete.")
-    cmd.add_param(param)
-    module.add_command(cmd)
-
-    cmd = CommandInfo(name = "set", desc = "Set a configuration value.")
-    param = ParamInfo(name = "identifier", type = "string", optional=True, desc = DEFAULT_IDENTIFIER_DESC)
-    cmd.add_param(param)
-    param = ParamInfo(name = "value", type = "string", optional=False, desc = "Specifies a value to set. It must be in correct JSON format and complete.")
-    cmd.add_param(param)
-    module.add_command(cmd)
-
-    cmd = CommandInfo(name = "unset", desc = "Unset a configuration value (i.e. revert to the default, if any).")
-    param = ParamInfo(name = "identifier", type = "string", optional=False, desc = DEFAULT_IDENTIFIER_DESC)
-    cmd.add_param(param)
-    module.add_command(cmd)
-
-    cmd = CommandInfo(name = "diff", desc = "Show all local changes that have not been committed.")
-    module.add_command(cmd)
-
-    cmd = CommandInfo(name = "revert", desc = "Revert all local changes.")
-    module.add_command(cmd)
-
-    cmd = CommandInfo(name = "commit", desc = "Commit all local changes.")
-    module.add_command(cmd)
-
-    cmd = CommandInfo(name = "go", desc = "Go to a specific configuration part.")
-    param = ParamInfo(name = "identifier", type="string", optional=False, desc = DEFAULT_IDENTIFIER_DESC)
-    cmd.add_param(param)
-    module.add_command(cmd)
-
-    tool.add_module_info(module)
-
-def check_port(option, opt_str, value, parser):
-    if (value < 0) or (value > 65535):
-        raise OptionValueError('%s requires a port number (0-65535)' % opt_str)
-    parser.values.port = value
-
-def check_addr(option, opt_str, value, parser):
-    ipstr = value
-    ip_family = socket.AF_INET
-    if (ipstr.find(':') != -1):
-        ip_family = socket.AF_INET6
-
-    try:
-        socket.inet_pton(ip_family, ipstr)
-    except:
-        raise OptionValueError("%s invalid ip address" % ipstr)
-
-    parser.values.addr = value
-
-def set_bindctl_options(parser):
-    parser.add_option('-p', '--port', dest = 'port', type = 'int',
-                      action = 'callback', callback=check_port,
-                      default = '8080', help = 'port for cmdctl of bind10')
-
-    parser.add_option('-a', '--address', dest = 'addr', type = 'string',
-                      action = 'callback', callback=check_addr,
-                      default = '127.0.0.1', help = 'IP address for cmdctl of bind10')
-
-    parser.add_option('-c', '--certificate-chain', dest = 'cert_chain', 
-                      type = 'string', action = 'store',
-                      help = 'PEM formatted server certificate validation chain file')
-
-if __name__ == '__main__':
-    parser = OptionParser(version = VERSION)
-    set_bindctl_options(parser)
-    (options, args) = parser.parse_args()
-    server_addr = options.addr + ':' + str(options.port)
-    tool = BindCmdInterpreter(server_addr, pem_file=options.cert_chain)
-    prepare_config_commands(tool)
-    tool.run()
-
-
diff --git a/src/bin/bindctl/bindctl.1 b/src/bin/bindctl/bindctl.1
index e86eca2..97700d6 100644
--- a/src/bin/bindctl/bindctl.1
+++ b/src/bin/bindctl/bindctl.1
@@ -22,7 +22,7 @@
 bindctl \- control and configure BIND 10
 .SH "SYNOPSIS"
 .HP \w'\fBbindctl\fR\ 'u
-\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]
+\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\-\-csv\-file\-dir\fR\fB\fIfile\fR\fR] [\fB\-\-port\ \fR\fB\fInumber\fR\fR] [\fB\-\-version\fR]
 .SH "DESCRIPTION"
 .PP
 The
@@ -52,6 +52,11 @@ daemon\&. The default is 127\&.0\&.0\&.1\&.
 The PEM formatted server certificate validation chain file\&.
 .RE
 .PP
+\fB\-\-csv\-file\-dir\fR\fIfile\fR
+.RS 4
+The directory name in which the user/password CSV file is stored (see AUTHENTICATION)\&. By default this option doesn\'t have any value, in which case the "\&.bind10" directory under the user\'s home directory will be used\&.
+.RE
+.PP
 \fB\-h\fR, \fB\-\-help\fR
 .RS 4
 Display command usage\&.
@@ -85,10 +90,10 @@ 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\&.
+The tool will authenticate using a username and password\&. On the first successful login, it will save the details to a comma\-separated\-value (CSV) file which will be used for later uses of
+\fBbindctl\fR\&. The file name is
+default_user\&.csv
+located under the directory specified by the \-\-csv\-file\-dir option\&.
 .SH "USAGE"
 .PP
 The
diff --git a/src/bin/bindctl/bindctl.xml b/src/bin/bindctl/bindctl.xml
index 98d65f9..eff1de2 100644
--- a/src/bin/bindctl/bindctl.xml
+++ b/src/bin/bindctl/bindctl.xml
@@ -51,6 +51,7 @@
       <arg><option>--address <replaceable>address</replaceable></option></arg>
       <arg><option>--help</option></arg>
       <arg><option>--certificate-chain <replaceable>file</replaceable></option></arg>
+      <arg><option>--csv-file-dir<replaceable>file</replaceable></option></arg>
       <arg><option>--port <replaceable>number</replaceable></option></arg>
       <arg><option>--version</option></arg>
     </cmdsynopsis>
@@ -110,6 +111,22 @@
       </varlistentry>
 
       <varlistentry>
+        <term>
+	  <option>--csv-file-dir</option><replaceable>file</replaceable>
+	</term>
+
+        <listitem>
+          <para>
+	    The directory name in which the user/password CSV file
+            is stored (see AUTHENTICATION).
+	    By default this option doesn't have any value,
+	    in which case the ".bind10" directory under the user's
+            home directory will be used.
+          </para>
+         </listitem>
+      </varlistentry>
+
+      <varlistentry>
         <term><option>-h</option>,
           <option>--help</option></term>
         <listitem><para>
@@ -148,8 +165,10 @@
     <para>
       The tool will authenticate using a username and password.
       On the first successful login, it will save the details to
-      <filename>~/.bind10/default_user.csv</filename>
+      a comma-separated-value (CSV) file
       which will be used for later uses of <command>bindctl</command>.
+      The file name is <filename>default_user.csv</filename>
+      located under the directory specified by the --csv-file-dir option.
     </para>
 
 <!-- TODO: mention HTTPS? -->
diff --git a/src/bin/bindctl/bindctl_main.py.in b/src/bin/bindctl/bindctl_main.py.in
new file mode 100755
index 0000000..01307e9
--- /dev/null
+++ b/src/bin/bindctl/bindctl_main.py.in
@@ -0,0 +1,138 @@
+#!@PYTHON@
+
+# Copyright (C) 2009  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""This is the main calling class for the bindctl configuration and
+   command tool. It sets up a command interpreter and runs that."""
+
+import sys; sys.path.append ('@@PYTHONPATH@@')
+
+from bindctl.moduleinfo import *
+from bindctl.bindcmd import *
+import pprint
+from optparse import OptionParser, OptionValueError
+import isc.util.process
+
+isc.util.process.rename()
+
+# This is the version that gets displayed to the user.
+# The VERSION string consists of the module name, the module version
+# number, and the overall BIND 10 version number (set in configure.ac).
+VERSION = "bindctl 20110217 (BIND 10 @PACKAGE_VERSION@)"
+
+DEFAULT_IDENTIFIER_DESC = "The identifier specifies the config item. Child elements are separated with the '/' character. List indices can be specified with '[i]', where i is an integer specifying the index, starting with 0. Examples: 'Boss/start_auth', 'Recurse/listen_on[0]/address'. If no identifier is given, shows the item at the current location."
+
+def prepare_config_commands(tool):
+    '''Prepare fixed commands for local configuration editing'''
+    module = ModuleInfo(name = CONFIG_MODULE_NAME, desc = "Configuration commands.")
+    cmd = CommandInfo(name = "show", desc = "Show configuration.")
+    param = ParamInfo(name = "argument", type = "string", optional=True, desc = "If you specify the argument 'all' (before the identifier), recursively show all child elements for the given identifier.")
+    cmd.add_param(param)
+    param = ParamInfo(name = "identifier", type = "string", optional=True, desc = DEFAULT_IDENTIFIER_DESC)
+    cmd.add_param(param)
+    module.add_command(cmd)
+
+    cmd = CommandInfo(name = "show_json", desc = "Show full configuration in JSON format.")
+    param = ParamInfo(name = "identifier", type = "string", optional=True, desc = DEFAULT_IDENTIFIER_DESC)
+    cmd.add_param(param)
+    module.add_command(cmd)
+
+    cmd = CommandInfo(name = "add", desc = "Add an entry to configuration list. If no value is given, a default value is added.")
+    param = ParamInfo(name = "identifier", type = "string", optional=True, desc = DEFAULT_IDENTIFIER_DESC)
+    cmd.add_param(param)
+    param = ParamInfo(name = "value", type = "string", optional=True, desc = "Specifies a value to add to the list. It must be in correct JSON format and complete.")
+    cmd.add_param(param)
+    module.add_command(cmd)
+
+    cmd = CommandInfo(name = "remove", desc = "Remove entry from configuration list.")
+    param = ParamInfo(name = "identifier", type = "string", optional=True, desc = DEFAULT_IDENTIFIER_DESC)
+    cmd.add_param(param)
+    param = ParamInfo(name = "value", type = "string", optional=True, desc = "Specifies a value to remove from the list. It must be in correct JSON format and complete.")
+    cmd.add_param(param)
+    module.add_command(cmd)
+
+    cmd = CommandInfo(name = "set", desc = "Set a configuration value.")
+    param = ParamInfo(name = "identifier", type = "string", optional=True, desc = DEFAULT_IDENTIFIER_DESC)
+    cmd.add_param(param)
+    param = ParamInfo(name = "value", type = "string", optional=False, desc = "Specifies a value to set. It must be in correct JSON format and complete.")
+    cmd.add_param(param)
+    module.add_command(cmd)
+
+    cmd = CommandInfo(name = "unset", desc = "Unset a configuration value (i.e. revert to the default, if any).")
+    param = ParamInfo(name = "identifier", type = "string", optional=False, desc = DEFAULT_IDENTIFIER_DESC)
+    cmd.add_param(param)
+    module.add_command(cmd)
+
+    cmd = CommandInfo(name = "diff", desc = "Show all local changes that have not been committed.")
+    module.add_command(cmd)
+
+    cmd = CommandInfo(name = "revert", desc = "Revert all local changes.")
+    module.add_command(cmd)
+
+    cmd = CommandInfo(name = "commit", desc = "Commit all local changes.")
+    module.add_command(cmd)
+
+    cmd = CommandInfo(name = "go", desc = "Go to a specific configuration part.")
+    param = ParamInfo(name = "identifier", type="string", optional=False, desc = DEFAULT_IDENTIFIER_DESC)
+    cmd.add_param(param)
+    module.add_command(cmd)
+
+    tool.add_module_info(module)
+
+def check_port(option, opt_str, value, parser):
+    if (value < 0) or (value > 65535):
+        raise OptionValueError('%s requires a port number (0-65535)' % opt_str)
+    parser.values.port = value
+
+def check_addr(option, opt_str, value, parser):
+    ipstr = value
+    ip_family = socket.AF_INET
+    if (ipstr.find(':') != -1):
+        ip_family = socket.AF_INET6
+
+    try:
+        socket.inet_pton(ip_family, ipstr)
+    except:
+        raise OptionValueError("%s invalid ip address" % ipstr)
+
+    parser.values.addr = value
+
+def set_bindctl_options(parser):
+    parser.add_option('-p', '--port', dest='port', type='int',
+                      action='callback', callback=check_port,
+                      default='8080', help='port for cmdctl of bind10')
+
+    parser.add_option('-a', '--address', dest='addr', type='string',
+                      action='callback', callback=check_addr,
+                      default='127.0.0.1', help='IP address for cmdctl of bind10')
+
+    parser.add_option('-c', '--certificate-chain', dest='cert_chain',
+                      type='string', action='store',
+                      help='PEM formatted server certificate validation chain file')
+
+    parser.add_option('--csv-file-dir', dest='csv_file_dir', type='string',
+                      default=None, action='store',
+                      help='Directory to store the password CSV file')
+
+if __name__ == '__main__':
+    parser = OptionParser(version = VERSION)
+    set_bindctl_options(parser)
+    (options, args) = parser.parse_args()
+    server_addr = options.addr + ':' + str(options.port)
+    tool = BindCmdInterpreter(server_addr, pem_file=options.cert_chain,
+                              csv_file_dir=options.csv_file_dir)
+    prepare_config_commands(tool)
+    tool.run()
diff --git a/src/bin/bindctl/tests/Makefile.am b/src/bin/bindctl/tests/Makefile.am
index 8a7a623..d2bb90f 100644
--- a/src/bin/bindctl/tests/Makefile.am
+++ b/src/bin/bindctl/tests/Makefile.am
@@ -11,6 +11,6 @@ if ENABLE_PYTHON_COVERAGE
 endif
 	for pytest in $(PYTESTS) ; do \
 	echo Running test: $$pytest ; \
-	env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_srcdir)/src/bin \
+	env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/bin/bindctl:$(abs_top_srcdir)/src/bin  \
 	$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
 	done
diff --git a/src/bin/bindctl/tests/bindctl_test.py b/src/bin/bindctl/tests/bindctl_test.py
index 490dd7a..0635b32 100644
--- a/src/bin/bindctl/tests/bindctl_test.py
+++ b/src/bin/bindctl/tests/bindctl_test.py
@@ -17,8 +17,17 @@
 import unittest
 import isc.cc.data
 import os
+import io
+import sys
+import socket
+import http.client
+import pwd
+import getpass
+from optparse import OptionParser
 from isc.config.config_data import ConfigData, MultiConfigData
 from isc.config.module_spec import ModuleSpec
+from isc.testutils.parse_args import TestOptParser, OptsError
+from bindctl_main import set_bindctl_options
 from bindctl import cmdparse
 from bindctl import bindcmd
 from bindctl.moduleinfo import *
@@ -271,7 +280,33 @@ class FakeCCSession(MultiConfigData):
                  ]
                }
         self.set_specification(ModuleSpec(spec))
-    
+
+
+# fake socket
+class FakeSocket():
+    def __init__(self):
+        self.run = True
+
+    def connect(self, to):
+        if not self.run:
+            raise socket.error
+
+    def close(self):
+        self.run = False
+
+    def send(self, data):
+        if not self.run:
+            raise socket.error
+        return len(data)
+
+    def makefile(self, type):
+        return self
+
+    def sendall(self, data):
+        if not self.run:
+            raise socket.error
+        return len(data)
+
 
 class TestConfigCommands(unittest.TestCase):
     def setUp(self):
@@ -279,7 +314,47 @@ class TestConfigCommands(unittest.TestCase):
         mod_info = ModuleInfo(name = "foo")
         self.tool.add_module_info(mod_info)
         self.tool.config_data = FakeCCSession()
-        
+        self.stdout_backup = sys.stdout
+
+    def test_precmd(self):
+        def update_all_modules_info():
+            raise socket.error
+        def precmd(line):
+            self.tool.precmd(line)
+        self.tool._update_all_modules_info = update_all_modules_info
+        # If line is equals to 'EOF', _update_all_modules_info() shouldn't be called
+        precmd('EOF')
+        self.assertRaises(socket.error, precmd, 'continue')
+
+    def test_run(self):
+        def login_to_cmdctl():
+            return True
+        def cmd_loop():
+            self.tool._send_message("/module_spec", None)
+
+        self.tool.login_to_cmdctl = login_to_cmdctl
+        # rewrite cmdloop() to avoid interactive mode
+        self.tool.cmdloop = cmd_loop
+
+        self.tool.conn.sock = FakeSocket()
+        self.tool.conn.sock.close()
+
+        # validate log message for socket.err
+        socket_err_output = io.StringIO()
+        sys.stdout = socket_err_output
+        self.assertRaises(None, self.tool.run())
+        self.assertEqual("Failed to send request, the connection is closed\n",
+                         socket_err_output.getvalue())
+        socket_err_output.close()
+
+        # validate log message for http.client.CannotSendRequest
+        cannot_send_output = io.StringIO()
+        sys.stdout = cannot_send_output
+        self.assertRaises(None, self.tool.run())
+        self.assertEqual("Can not send request, the connection is busy\n",
+                         cannot_send_output.getvalue())
+        cannot_send_output.close()
+
     def test_apply_cfg_command_int(self):
         self.tool.location = '/'
 
@@ -328,12 +403,12 @@ class TestConfigCommands(unittest.TestCase):
         # this should raise a TypeError
         cmd = cmdparse.BindCmdParse("config set identifier=\"foo/a_list\" value=\"a\"")
         self.assertRaises(isc.cc.data.DataTypeError, self.tool.apply_config_cmd, cmd)
-        
+
         cmd = cmdparse.BindCmdParse("config set identifier=\"foo/a_list\" value=[1]")
         self.assertRaises(isc.cc.data.DataTypeError, self.tool.apply_config_cmd, cmd)
 
-
-    
+    def tearDown(self):
+        sys.stdout = self.stdout_backup
 
 class FakeBindCmdInterpreter(bindcmd.BindCmdInterpreter):
     def __init__(self):
@@ -349,16 +424,51 @@ class TestBindCmdInterpreter(unittest.TestCase):
         writer.writerow(['name2'])
         csvfile.close()
 
+    def test_csv_file_dir(self):
+        # Checking default value
+        if "HOME" in os.environ:
+            home_dir = os.environ["HOME"]
+        else:
+            home_dir = pwd.getpwnam(getpass.getuser()).pw_dir
+        self.assertEqual(home_dir + os.sep + '.bind10' + os.sep,
+                         bindcmd.BindCmdInterpreter().csv_file_dir)
+
+        new_csv_dir = '/something/different/'
+        custom_cmd = bindcmd.BindCmdInterpreter(csv_file_dir=new_csv_dir)
+        self.assertEqual(new_csv_dir, custom_cmd.csv_file_dir)
+
     def test_get_saved_user_info(self):
-        cmd = FakeBindCmdInterpreter()
-        users = cmd._get_saved_user_info('/notexist', 'cvs_file.cvs')
+        old_stdout = sys.stdout
+        sys.stdout = open(os.devnull, 'w')
+        cmd = bindcmd.BindCmdInterpreter()
+        users = cmd._get_saved_user_info('/notexist', 'csv_file.csv')
         self.assertEqual([], users)
-        
+
         csvfilename = 'csv_file.csv'
         self._create_invalid_csv_file(csvfilename)
         users = cmd._get_saved_user_info('./', csvfilename)
         self.assertEqual([], users)
         os.remove(csvfilename)
+        sys.stdout = old_stdout
+
+
+class TestCommandLineOptions(unittest.TestCase):
+    def setUp(self):
+        self.parser = TestOptParser()
+        set_bindctl_options(self.parser)
+
+    def test_csv_file_dir(self):
+        # by default the option is "undefined"
+        (options, _) = self.parser.parse_args([])
+        self.assertEqual(None, options.csv_file_dir)
+
+        # specify the option, valid case.
+        (options, _) = self.parser.parse_args(['--csv-file-dir', 'some_dir'])
+        self.assertEqual('some_dir', options.csv_file_dir)
+
+        # missing option arg; should trigger parser error.
+        self.assertRaises(OptsError, self.parser.parse_args,
+                          ['--csv-file-dir'])
 
 if __name__== "__main__":
     unittest.main()
diff --git a/src/bin/cfgmgr/b10-cfgmgr.py.in b/src/bin/cfgmgr/b10-cfgmgr.py.in
index 659426d..5355582 100755
--- a/src/bin/cfgmgr/b10-cfgmgr.py.in
+++ b/src/bin/cfgmgr/b10-cfgmgr.py.in
@@ -22,29 +22,54 @@ from isc.cc import SessionError
 import isc.util.process
 import signal
 import os
+from optparse import OptionParser
 
 isc.util.process.rename()
 
 # If B10_FROM_SOURCE is set in the environment, we use data files
-# from a directory relative to that, otherwise we use the ones
-# installed on the system
+# from a directory relative to the value of that variable, or, if defined,
+# relative to the value of B10_FROM_SOURCE_LOCALSTATEDIR.  Otherwise
+# we use the ones installed on the system.
+# B10_FROM_SOURCE_LOCALSTATEDIR is specifically intended to be used for
+# tests where we want to use variuos types of configuration within the test
+# environment.  (We may want to make it even more generic so that the path is
+# passed from the boss process)
 if "B10_FROM_SOURCE" in os.environ:
-    DATA_PATH = os.environ["B10_FROM_SOURCE"]
+    if "B10_FROM_SOURCE_LOCALSTATEDIR" in os.environ:
+        DATA_PATH = os.environ["B10_FROM_SOURCE_LOCALSTATEDIR"]
+    else:
+        DATA_PATH = os.environ["B10_FROM_SOURCE"]
 else:
     PREFIX = "@prefix@"
     DATA_PATH = "@localstatedir@/@PACKAGE@".replace("${prefix}", PREFIX)
+DEFAULT_CONFIG_FILE = "b10-config.db"
 
 cm = None
 
+def parse_options(args=sys.argv[1:], Parser=OptionParser):
+    parser = Parser()
+    parser.add_option("-p", "--data-path", dest="data_path",
+                      help="Directory to search for configuration files " +
+                      "(default=" + DATA_PATH + ")", default=DATA_PATH)
+    parser.add_option("-c", "--config-filename", dest="config_file",
+                      help="Configuration database filename " +
+                      "(default=" + DEFAULT_CONFIG_FILE + ")",
+                      default=DEFAULT_CONFIG_FILE)
+    (options, args) = parser.parse_args(args)
+    if args:
+        parser.error("No non-option arguments allowed")
+    return options
+
 def signal_handler(signal, frame):
     global cm
     if cm:
         cm.running = False
 
 def main():
+    options = parse_options()
     global cm
     try:
-        cm = ConfigManager(DATA_PATH)
+        cm = ConfigManager(options.data_path, options.config_file)
         signal.signal(signal.SIGINT, signal_handler)
         signal.signal(signal.SIGTERM, signal_handler)
         cm.read_config()
diff --git a/src/bin/cfgmgr/b10-cfgmgr.xml b/src/bin/cfgmgr/b10-cfgmgr.xml
index 9505eee..08a6a97 100644
--- a/src/bin/cfgmgr/b10-cfgmgr.xml
+++ b/src/bin/cfgmgr/b10-cfgmgr.xml
@@ -41,16 +41,13 @@
     </copyright>
   </docinfo>
 
-<!--
   <refsynopsisdiv>
     <cmdsynopsis>
-      <command></command>
-      <arg><option></option></arg>
-      <arg choice="opt"></arg>
-      <arg choice="opt"></arg>
+      <command>b10-cfgmgr</command>
+      <arg><option>-c<replaceable>config-filename</replaceable></option></arg>
+      <arg><option>-p<replaceable>data_path</replaceable></option></arg>
     </cmdsynopsis>
   </refsynopsisdiv>
--->
 
   <refsect1>
     <title>DESCRIPTION</title>
@@ -93,24 +90,38 @@
     </para>
   </refsect1>
 
-<!--
   <refsect1>
     <title>ARGUMENTS</title>
-    <para>
-      <orderedlist numeration="loweralpha">
+
+    <para>The arguments are as follows:</para>
+
+    <variablelist>
+      <varlistentry>
+        <term>
+          <option>-c</option><replaceable>config-filename</replaceable>,
+          <option>--config-filename</option> <replaceable>config-filename</replaceable>
+        </term>
         <listitem>
-          <para>
-          </para>
+          <para>The configuration database filename to use. Can be either
+          absolute or relative to data path.</para>
+          <para>Defaults to b10-config.db</para>
         </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>
+          <option>-p</option><replaceable>data-path</replaceable>,
+          <option>--data-path</option> <replaceable>data-path</replaceable>
+        </term>
         <listitem>
-          <para>
-          </para>
+          <para>The path where BIND 10 looks for files. The
+          configuration file is looked for here, if it is relative. If it is
+          absolute, the path is ignored.</para>
         </listitem>
-      </orderedlist>
-    </para>
-
+      </varlistentry>
+    </variablelist>
   </refsect1>
--->
+
   <refsect1>
     <title>FILES</title>
 <!-- TODO: fix path -->
diff --git a/src/bin/cfgmgr/tests/b10-cfgmgr_test.py.in b/src/bin/cfgmgr/tests/b10-cfgmgr_test.py.in
index 8847b18..037a106 100644
--- a/src/bin/cfgmgr/tests/b10-cfgmgr_test.py.in
+++ b/src/bin/cfgmgr/tests/b10-cfgmgr_test.py.in
@@ -20,9 +20,10 @@
 import unittest
 import os
 import sys
+from isc.testutils.parse_args import OptsError, TestOptParser
 
 class MyConfigManager:
-    def __init__(self, path):
+    def __init__(self, path, filename):
         self._path = path
         self.read_config_called = False
         self.notify_boss_called = False
@@ -88,6 +89,69 @@ class TestConfigManagerStartup(unittest.TestCase):
 
         sys.modules.pop("b10-cfgmgr")
 
+class TestParseArgs(unittest.TestCase):
+    """
+    Test for the parsing of command line arguments. We provide a different
+    array to parse instead.
+    """
+
+    def test_defaults(self):
+        """
+        Test the default values when no options are provided.
+        """
+        # Pass it empty array, not our arguments
+        b = __import__("b10-cfgmgr")
+        parsed = b.parse_options([], TestOptParser)
+        self.assertEqual(b.DATA_PATH, parsed.data_path)
+        self.assertEqual(b.DEFAULT_CONFIG_FILE, parsed.config_file)
+
+    def test_wrong_args(self):
+        """
+        Test it fails when we pass invalid option.
+        """
+        b = __import__("b10-cfgmgr")
+        self.assertRaises(OptsError, b.parse_options, ['--wrong-option'],
+                          TestOptParser)
+
+    def test_not_arg(self):
+        """
+        Test it fails when there's an argument that's not option
+        (eg. without -- at the beginning).
+        """
+        b = __import__("b10-cfgmgr")
+        self.assertRaises(OptsError, b.parse_options, ['not-option'],
+                          TestOptParser)
+
+    def test_datapath(self):
+        """
+        Test overwriting the data path.
+        """
+        b = __import__("b10-cfgmgr")
+        parsed = b.parse_options(['--data-path=/path'], TestOptParser)
+        self.assertEqual('/path', parsed.data_path)
+        self.assertEqual(b.DEFAULT_CONFIG_FILE, parsed.config_file)
+        parsed = b.parse_options(['-p', '/path'], TestOptParser)
+        self.assertEqual('/path', parsed.data_path)
+        self.assertEqual(b.DEFAULT_CONFIG_FILE, parsed.config_file)
+        self.assertRaises(OptsError, b.parse_options, ['-p'], TestOptParser)
+        self.assertRaises(OptsError, b.parse_options, ['--data-path'],
+                          TestOptParser)
+
+    def test_db_filename(self):
+        """
+        Test setting the configuration database file.
+        """
+        b = __import__("b10-cfgmgr")
+        parsed = b.parse_options(['--config-filename=filename'],
+                                 TestOptParser)
+        self.assertEqual(b.DATA_PATH, parsed.data_path)
+        self.assertEqual("filename", parsed.config_file)
+        parsed = b.parse_options(['-c', 'filename'], TestOptParser)
+        self.assertEqual(b.DATA_PATH, parsed.data_path)
+        self.assertEqual("filename", parsed.config_file)
+        self.assertRaises(OptsError, b.parse_options, ['-c'], TestOptParser)
+        self.assertRaises(OptsError, b.parse_options, ['--config-filename'],
+                          TestOptParser)
 
 if __name__ == '__main__':
     unittest.main()
diff --git a/src/bin/resolver/Makefile.am b/src/bin/resolver/Makefile.am
index 36b8551..54e15bd 100644
--- a/src/bin/resolver/Makefile.am
+++ b/src/bin/resolver/Makefile.am
@@ -51,6 +51,7 @@ b10_resolver_LDADD += $(top_builddir)/src/lib/log/liblog.la
 b10_resolver_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
 b10_resolver_LDADD += $(top_builddir)/src/lib/cache/libcache.la
 b10_resolver_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
+b10_resolver_LDADD += $(top_builddir)/src/lib/resolve/libresolve.la
 b10_resolver_LDADD += $(top_builddir)/src/bin/auth/change_user.o
 b10_resolver_LDFLAGS = -pthread
 
diff --git a/src/bin/resolver/b10-resolver.8 b/src/bin/resolver/b10-resolver.8
index 3125e32..849092c 100644
--- a/src/bin/resolver/b10-resolver.8
+++ b/src/bin/resolver/b10-resolver.8
@@ -74,7 +74,7 @@ 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\&.
+number\&. The defaults are address ::1 port 53 and address 127\&.0\&.0\&.1 port 53\&.
 .PP
 
 \fIretries\fR
diff --git a/src/bin/resolver/b10-resolver.xml b/src/bin/resolver/b10-resolver.xml
index 0d395a7..bdf4f8a 100644
--- a/src/bin/resolver/b10-resolver.xml
+++ b/src/bin/resolver/b10-resolver.xml
@@ -141,8 +141,9 @@ once that is merged you can for instance do 'config add Resolver/forward_address
       <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.
+      The defaults are address ::1 port 53 and
+      address 127.0.0.1 port 53.
+<!-- TODO: but defaults are not used, Trac #518 -->
     </para>
 
     <para>
diff --git a/src/bin/resolver/main.cc b/src/bin/resolver/main.cc
index 03f9ab7..bbb7d13 100644
--- a/src/bin/resolver/main.cc
+++ b/src/bin/resolver/main.cc
@@ -45,6 +45,9 @@
 #include <resolver/spec_config.h>
 #include <resolver/resolver.h>
 
+#include <cache/resolver_cache.h>
+#include <nsas/nameserver_address_store.h>
+
 #include <log/dummylog.h>
 
 using namespace std;
@@ -56,11 +59,10 @@ using namespace asiolink;
 
 namespace {
 
-// Default port current 5300 for testing purposes
 static const string PROGRAM = "Resolver";
 
 IOService io_service;
-static Resolver *resolver;
+static boost::shared_ptr<Resolver> resolver;
 
 ConstElementPtr
 my_config_handler(ConstElementPtr new_config) {
@@ -136,15 +138,58 @@ main(int argc, char* argv[]) {
             specfile = string(RESOLVER_SPECFILE_LOCATION);
         }
 
-        resolver = new Resolver();
+        resolver = boost::shared_ptr<Resolver>(new Resolver());
         dlog("Server created.");
 
         SimpleCallback* checkin = resolver->getCheckinProvider();
         DNSLookup* lookup = resolver->getDNSLookupProvider();
         DNSAnswer* answer = resolver->getDNSAnswerProvider();
 
+        isc::nsas::NameserverAddressStore nsas(resolver);
+        resolver->setNameserverAddressStore(nsas);
+
+        isc::cache::ResolverCache cache;
+        resolver->setCache(cache);
+        
+        // TODO priming query, remove root from direct
+        // Fake a priming query result here (TODO2 how to flag non-expiry?)
+        // propagation to runningquery. And check for forwarder mode?
+        isc::dns::QuestionPtr root_question(new isc::dns::Question(
+                                            isc::dns::Name("."),
+                                            isc::dns::RRClass::IN(),
+                                            isc::dns::RRType::NS()));
+        isc::dns::RRsetPtr root_ns_rrset(new isc::dns::RRset(isc::dns::Name("."), 
+                                         isc::dns::RRClass::IN(),
+                                         isc::dns::RRType::NS(),
+                                         isc::dns::RRTTL(8888)));
+        root_ns_rrset->addRdata(isc::dns::rdata::createRdata(isc::dns::RRType::NS(),
+                                                             isc::dns::RRClass::IN(),
+                                                             "l.root-servers.net."));
+        isc::dns::RRsetPtr root_a_rrset(new isc::dns::RRset(isc::dns::Name("l.root-servers.net"), 
+                                        isc::dns::RRClass::IN(),
+                                        isc::dns::RRType::A(),
+                                        isc::dns::RRTTL(8888)));
+        root_a_rrset->addRdata(isc::dns::rdata::createRdata(isc::dns::RRType::A(),
+                                                             isc::dns::RRClass::IN(),
+                                                             "199.7.83.42"));
+        isc::dns::RRsetPtr root_aaaa_rrset(new isc::dns::RRset(isc::dns::Name("l.root-servers.net"), 
+                                        isc::dns::RRClass::IN(),
+                                        isc::dns::RRType::AAAA(),
+                                        isc::dns::RRTTL(8888)));
+        root_aaaa_rrset->addRdata(isc::dns::rdata::createRdata(isc::dns::RRType::AAAA(),
+                                                             isc::dns::RRClass::IN(),
+                                                             "2001:500:3::42"));
+        isc::dns::MessagePtr priming_result(new isc::dns::Message(isc::dns::Message::RENDER));
+        priming_result->addQuestion(root_question);
+        priming_result->addRRset(isc::dns::Message::SECTION_ANSWER, root_ns_rrset);
+        priming_result->addRRset(isc::dns::Message::SECTION_ADDITIONAL, root_a_rrset);
+        priming_result->addRRset(isc::dns::Message::SECTION_ADDITIONAL, root_aaaa_rrset);
+        cache.update(*priming_result);
+        cache.update(root_ns_rrset);
+        cache.update(root_a_rrset);
+        cache.update(root_aaaa_rrset);
+        
         DNSService dns_service(io_service, checkin, lookup, answer);
-
         resolver->setDNSService(dns_service);
         dlog("IOService created.");
 
@@ -173,7 +218,6 @@ main(int argc, char* argv[]) {
 
     delete config_session;
     delete cc_session;
-    delete resolver;
 
     return (ret);
 }
diff --git a/src/bin/resolver/resolver.cc b/src/bin/resolver/resolver.cc
index 84df9d2..7656e2b 100644
--- a/src/bin/resolver/resolver.cc
+++ b/src/bin/resolver/resolver.cc
@@ -41,6 +41,8 @@
 #include <dns/messagerenderer.h>
 #include <server_common/portconfig.h>
 
+#include <resolve/recursive_query.h>
+
 #include <log/dummylog.h>
 
 #include <resolver/resolver.h>
@@ -74,10 +76,15 @@ public:
         queryShutdown();
     }
 
-    void querySetup(DNSService& dnss) {
+    void querySetup(DNSService& dnss,
+                    isc::nsas::NameserverAddressStore& nsas,
+                    isc::cache::ResolverCache& cache)
+    {
         assert(!rec_query_); // queryShutdown must be called first
         dlog("Query setup");
-        rec_query_ = new RecursiveQuery(dnss, upstream_,
+        rec_query_ = new RecursiveQuery(dnss, 
+                                        nsas, cache,
+                                        upstream_,
                                         upstream_root_,
                                         query_timeout_,
                                         client_timeout_,
@@ -129,7 +136,7 @@ public:
             }
         }
     }
-
+    
     void resolve(const isc::dns::QuestionPtr& question,
         const isc::resolve::ResolverInterface::CallbackPtr& callback);
 
@@ -342,6 +349,19 @@ Resolver::setDNSService(asiolink::DNSService& dnss) {
 }
 
 void
+Resolver::setNameserverAddressStore(isc::nsas::NameserverAddressStore& nsas)
+{
+    nsas_ = &nsas;
+}
+
+void
+Resolver::setCache(isc::cache::ResolverCache& cache)
+{
+    cache_ = &cache;
+}
+
+
+void
 Resolver::setConfigSession(ModuleCCSession* config_session) {
     impl_->config_session_ = config_session;
 }
@@ -544,7 +564,7 @@ Resolver::updateConfig(ConstElementPtr config) {
 
         if (need_query_restart) {
             impl_->queryShutdown();
-            impl_->querySetup(*dnss_);
+            impl_->querySetup(*dnss_, *nsas_, *cache_);
         }
         return (isc::config::createAnswer());
     } catch (const isc::Exception& error) {
diff --git a/src/bin/resolver/resolver.h b/src/bin/resolver/resolver.h
index 2ae8079..d851656 100644
--- a/src/bin/resolver/resolver.h
+++ b/src/bin/resolver/resolver.h
@@ -24,6 +24,9 @@
 
 #include <asiolink/asiolink.h>
 
+#include <nsas/nameserver_address_store.h>
+#include <cache/resolver_cache.h>
+
 #include <resolve/resolver_interface.h>
 
 class ResolverImpl;
@@ -86,10 +89,26 @@ public:
 
     /// \brief Assign an ASIO IO Service queue to this Resolver object
     void setDNSService(asiolink::DNSService& dnss);
+    
+    /// \brief Assign a NameserverAddressStore to this Resolver object
+    void setNameserverAddressStore(isc::nsas::NameserverAddressStore &nsas);
+    
+    /// \brief Assign a cache to this Resolver object
+    void setCache(isc::cache::ResolverCache& cache);
 
     /// \brief Return this object's ASIO IO Service queue
     asiolink::DNSService& getDNSService() const { return (*dnss_); }
 
+    /// \brief Returns this object's NSAS
+    isc::nsas::NameserverAddressStore& getNameserverAddressStore() const {
+        return *nsas_;
+    };
+
+    /// \brief Returns this object's ResolverCache
+    isc::cache::ResolverCache& getResolverCache() const {
+        return *cache_;
+    };
+    
     /// \brief Return pointer to the DNS Lookup callback function
     asiolink::DNSLookup* getDNSLookupProvider() { return (dns_lookup_); }
 
@@ -208,6 +227,8 @@ private:
     asiolink::SimpleCallback* checkin_;
     asiolink::DNSLookup* dns_lookup_;
     asiolink::DNSAnswer* dns_answer_;
+    isc::nsas::NameserverAddressStore* nsas_;
+    isc::cache::ResolverCache* cache_;
 };
 
 #endif // __RESOLVER_H
diff --git a/src/bin/resolver/resolver.spec.pre.in b/src/bin/resolver/resolver.spec.pre.in
index bc598b0..9df1e75 100644
--- a/src/bin/resolver/resolver.spec.pre.in
+++ b/src/bin/resolver/resolver.spec.pre.in
@@ -86,11 +86,11 @@
         "item_default": [
           {
             "address": "::1",
-            "port": 5300
+            "port": 53
           },
           {
             "address": "127.0.0.1",
-            "port": 5300
+            "port": 53
           }
         ],
         "list_item_spec": {
@@ -109,7 +109,7 @@
               "item_name": "port",
               "item_type": "integer",
               "item_optional": false,
-              "item_default": 5300
+              "item_default": 53
             }
           ]
         }
diff --git a/src/bin/resolver/response_scrubber.h b/src/bin/resolver/response_scrubber.h
index c3fce57..680aa5a 100644
--- a/src/bin/resolver/response_scrubber.h
+++ b/src/bin/resolver/response_scrubber.h
@@ -177,7 +177,7 @@
 /// Qu: www.sub.example.com\n
 /// Zo: example.com
 ///
-/// An: <nothing>
+/// An: (nothing)
 ///
 /// Au(1): sub.example.com NS ns0.sub.example.com\n
 /// Au(2): sub.example.com NS ns1.example.net
@@ -312,7 +312,7 @@ public:
     /// 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
+    /// \param names 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.
diff --git a/src/bin/resolver/tests/Makefile.am b/src/bin/resolver/tests/Makefile.am
index eb7e3e1..b85c223 100644
--- a/src/bin/resolver/tests/Makefile.am
+++ b/src/bin/resolver/tests/Makefile.am
@@ -40,6 +40,7 @@ run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
 run_unittests_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
 run_unittests_LDADD += $(top_builddir)/src/lib/cache/libcache.la
 run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
+run_unittests_LDADD += $(top_builddir)/src/lib/resolve/libresolve.la
 
 # Note the ordering matters: -Wno-... must follow -Wextra (defined in
 # B10_CXXFLAGS
diff --git a/src/bin/xfrout/xfrout.py.in b/src/bin/xfrout/xfrout.py.in
index fd1288d..f420d4b 100755
--- a/src/bin/xfrout/xfrout.py.in
+++ b/src/bin/xfrout/xfrout.py.in
@@ -50,7 +50,11 @@ isc.util.process.rename()
 if "B10_FROM_BUILD" in os.environ:
     SPECFILE_PATH = os.environ["B10_FROM_BUILD"] + "/src/bin/xfrout"
     AUTH_SPECFILE_PATH = os.environ["B10_FROM_BUILD"] + "/src/bin/auth"
-    UNIX_SOCKET_FILE= os.environ["B10_FROM_BUILD"] + "/auth_xfrout_conn"
+    if "B10_FROM_SOURCE_LOCALSTATEDIR" in os.environ:
+        UNIX_SOCKET_FILE = os.environ["B10_FROM_SOURCE_LOCALSTATEDIR"] + \
+            "/auth_xfrout_conn"
+    else:
+        UNIX_SOCKET_FILE = os.environ["B10_FROM_BUILD"] + "/auth_xfrout_conn"
 else:
     PREFIX = "@prefix@"
     DATAROOTDIR = "@datarootdir@"
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
index 27d9b8b..8525b8d 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 \
-          resolve nsas cache asiolink testutils server_common
+SUBDIRS = exceptions dns cc config python xfr bench log asiolink \
+          nsas cache resolve testutils datasrc server_common
diff --git a/src/lib/asiolink/Makefile.am b/src/lib/asiolink/Makefile.am
index b3968f0..2fda728 100644
--- a/src/lib/asiolink/Makefile.am
+++ b/src/lib/asiolink/Makefile.am
@@ -13,40 +13,39 @@ CLEANFILES = *.gcno *.gcda
 # which would make the build fail with -Werror (our default setting).
 lib_LTLIBRARIES = libasiolink.la
 libasiolink_la_SOURCES  = asiolink.h
+libasiolink_la_SOURCES += asiolink_utilities.h
+libasiolink_la_SOURCES += asiodef.cc asiodef.h
 libasiolink_la_SOURCES += dns_answer.h
 libasiolink_la_SOURCES += dns_lookup.h
 libasiolink_la_SOURCES += dns_server.h
-libasiolink_la_SOURCES += dns_service.h dns_service.cc
+libasiolink_la_SOURCES += dns_service.cc dns_service.h
 libasiolink_la_SOURCES += dummy_io_cb.h
-libasiolink_la_SOURCES += interval_timer.h interval_timer.cc
-libasiolink_la_SOURCES += io_address.h io_address.cc
+libasiolink_la_SOURCES += interval_timer.cc interval_timer.h
+libasiolink_la_SOURCES += io_address.cc io_address.h
 libasiolink_la_SOURCES += io_asio_socket.h
-libasiolink_la_SOURCES += io_endpoint.h io_endpoint.cc
+libasiolink_la_SOURCES += io_endpoint.cc io_endpoint.h
 libasiolink_la_SOURCES += io_error.h
-libasiolink_la_SOURCES += io_fetch.h io_fetch.cc
+libasiolink_la_SOURCES += io_fetch.cc io_fetch.h
 libasiolink_la_SOURCES += io_message.h
+libasiolink_la_SOURCES += qid_gen.cc qid_gen.h
 libasiolink_la_SOURCES += io_service.h io_service.cc
 libasiolink_la_SOURCES += io_socket.h io_socket.cc
-libasiolink_la_SOURCES += recursive_query.h recursive_query.cc
 libasiolink_la_SOURCES += simple_callback.h
 libasiolink_la_SOURCES += tcp_endpoint.h
-libasiolink_la_SOURCES += tcp_server.h tcp_server.cc
+libasiolink_la_SOURCES += tcp_server.cc tcp_server.h
 libasiolink_la_SOURCES += tcp_socket.h
 libasiolink_la_SOURCES += udp_endpoint.h
-libasiolink_la_SOURCES += udp_server.h udp_server.cc
+libasiolink_la_SOURCES += udp_server.cc udp_server.h
 libasiolink_la_SOURCES += udp_socket.h
+
+EXTRA_DIST = asiodef.msg
+
 # Note: the ordering matters: -Wno-... must follow -Wextra (defined in
 # B10_CXXFLAGS)
 libasiolink_la_CXXFLAGS = $(AM_CXXFLAGS)
-if USE_GXX
-libasiolink_la_CXXFLAGS += -Wno-unused-parameter
-endif
 if USE_CLANGPP
 # Same for clang++, but we need to turn off -Werror completely.
 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
-libasiolink_la_LIBADD += $(top_builddir)/src/lib/cache/libcache.la
-libasiolink_la_LIBADD += $(top_builddir)/src/lib/nsas/libnsas.la
diff --git a/src/lib/asiolink/asiodef.cc b/src/lib/asiolink/asiodef.cc
new file mode 100644
index 0000000..94c71b5
--- /dev/null
+++ b/src/lib/asiolink/asiodef.cc
@@ -0,0 +1,37 @@
+// File created from asiodef.msg on Mon Feb 28 17:15:30 2011
+
+#include <cstddef>
+#include <log/message_types.h>
+#include <log/message_initializer.h>
+
+namespace asiolink {
+
+extern const isc::log::MessageID ASIO_FETCHCOMP = "FETCHCOMP";
+extern const isc::log::MessageID ASIO_FETCHSTOP = "FETCHSTOP";
+extern const isc::log::MessageID ASIO_OPENSOCK = "OPENSOCK";
+extern const isc::log::MessageID ASIO_RECVSOCK = "RECVSOCK";
+extern const isc::log::MessageID ASIO_RECVTMO = "RECVTMO";
+extern const isc::log::MessageID ASIO_SENDSOCK = "SENDSOCK";
+extern const isc::log::MessageID ASIO_UNKORIGIN = "UNKORIGIN";
+extern const isc::log::MessageID ASIO_UNKRESULT = "UNKRESULT";
+
+} // namespace asiolink
+
+namespace {
+
+const char* values[] = {
+    "FETCHCOMP", "upstream fetch to %s(%d) has now completed",
+    "FETCHSTOP", "upstream fetch to %s(%d) has been stopped",
+    "OPENSOCK", "error %d opening %s socket to %s(%d)",
+    "RECVSOCK", "error %d reading %s data from %s(%d)",
+    "RECVTMO", "receive timeout while waiting for data from %s(%d)",
+    "SENDSOCK", "error %d sending data using %s to %s(%d)",
+    "UNKORIGIN", "unknown origin for ASIO error code %d (protocol: %s, address %s)",
+    "UNKRESULT", "unknown result (%d) when IOFetch::stop() was executed for I/O to %s(%d)",
+    NULL
+};
+
+const isc::log::MessageInitializer initializer(values);
+
+} // Anonymous namespace
+
diff --git a/src/lib/asiolink/asiodef.h b/src/lib/asiolink/asiodef.h
new file mode 100644
index 0000000..ba77817
--- /dev/null
+++ b/src/lib/asiolink/asiodef.h
@@ -0,0 +1,21 @@
+// File created from asiodef.msg on Mon Feb 28 17:15:30 2011
+
+#ifndef __ASIODEF_H
+#define __ASIODEF_H
+
+#include <log/message_types.h>
+
+namespace asiolink {
+
+extern const isc::log::MessageID ASIO_FETCHCOMP;
+extern const isc::log::MessageID ASIO_FETCHSTOP;
+extern const isc::log::MessageID ASIO_OPENSOCK;
+extern const isc::log::MessageID ASIO_RECVSOCK;
+extern const isc::log::MessageID ASIO_RECVTMO;
+extern const isc::log::MessageID ASIO_SENDSOCK;
+extern const isc::log::MessageID ASIO_UNKORIGIN;
+extern const isc::log::MessageID ASIO_UNKRESULT;
+
+} // namespace asiolink
+
+#endif // __ASIODEF_H
diff --git a/src/lib/asiolink/asiodef.msg b/src/lib/asiolink/asiodef.msg
new file mode 100644
index 0000000..2fcadd1
--- /dev/null
+++ b/src/lib/asiolink/asiodef.msg
@@ -0,0 +1,56 @@
+# 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.
+
+$PREFIX ASIO_
+$NAMESPACE asiolink
+
+FETCHCOMP   upstream fetch to %s(%d) has now completed
++ A debug message, this records the the upstream fetch (a query made by the
++ resolver on behalf of its client) to the specified address has completed.
+
+FETCHSTOP   upstream fetch to %s(%d) has been stopped
++ An external component has requested the halting of an upstream fetch.  This
++ is an allowed operation, and the message should only appear if debug is
++ enabled.
+
+OPENSOCK    error %d opening %s socket to %s(%d)
++ The asynchronous I/O code encountered an error when trying to open a socket
++ of the specified protocol in order to send a message to the target address.
++ The the number of the system error that cause the problem is given in the
++ message.
+
+RECVSOCK    error %d reading %s data from %s(%d)
++ The asynchronous I/O code encountered an error when trying read data from
++ the specified address on the given protocol.  The the number of the system
++ error that cause the problem is given in the message.
+
+SENDSOCK    error %d sending data using %s to %s(%d)
++ The asynchronous I/O code encountered an error when trying send data to
++ the specified address on the given protocol.  The the number of the system
++ error that cause the problem is given in the message.
+
+RECVTMO     receive timeout while waiting for data from %s(%d)
++ An upstream fetch from the specified address timed out.  This may happen for
++ any number of reasons and is most probably a problem at the remote server
++ or a problem on the network.  The message will only appear if debug is
++ enabled.
+
+UNKORIGIN  unknown origin for ASIO error code %d (protocol: %s, address %s)
++ This message should not appear and indicates an internal error if it does.
++ Please enter a bug report.
+
+UNKRESULT  unknown result (%d) when IOFetch::stop() was executed for I/O to %s(%d)
++ The termination method of the resolver's upstream fetch class was called with
++ an unknown result code (which is given in the message).  This message should
++ not appear and may indicate an internal error.  Please enter a bug report.
diff --git a/src/lib/asiolink/asiolink.h b/src/lib/asiolink/asiolink.h
index 03951ae..6e8fe84 100644
--- a/src/lib/asiolink/asiolink.h
+++ b/src/lib/asiolink/asiolink.h
@@ -25,7 +25,6 @@
 #include <asiolink/dns_lookup.h>
 #include <asiolink/dns_answer.h>
 #include <asiolink/simple_callback.h>
-#include <asiolink/recursive_query.h>
 #include <asiolink/interval_timer.h>
 
 #include <asiolink/io_address.h>
@@ -85,7 +84,3 @@
 /// http://think-async.com/Asio/asio-1.3.1/doc/asio/reference/asio_handler_allocate.html
 
 #endif // __ASIOLINK_H
-
-// Local Variables: 
-// mode: c++
-// End: 
diff --git a/src/lib/asiolink/asiolink_utilities.h b/src/lib/asiolink/asiolink_utilities.h
new file mode 100644
index 0000000..659e6a0
--- /dev/null
+++ b/src/lib/asiolink/asiolink_utilities.h
@@ -0,0 +1,61 @@
+// 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 __ASIOLINK_UTILITIES_H
+#define __ASIOLINK_UTILITIES_H
+
+#include <cstddef>
+
+namespace asiolink {
+
+/// \brief Read Unsigned 16-Bit Integer from Buffer
+///
+/// This is essentially a copy of the isc::dns::InputBuffer::readUint16.  It
+/// should really be moved into a separate library.
+///
+/// \param buffer Data buffer at least two bytes long of which the first two
+///        bytes are assumed to represent a 16-bit integer in network-byte
+///        order.
+///
+/// \return Value of 16-bit integer
+inline uint16_t
+readUint16(const void* buffer) {
+    const uint8_t* byte_buffer = static_cast<const uint8_t*>(buffer);
+
+    uint16_t result = (static_cast<uint16_t>(byte_buffer[0])) << 8;
+    result |= static_cast<uint16_t>(byte_buffer[1]);
+
+    return (result);
+}
+
+/// \brief Write Unisgned 16-Bit Integer to Buffer
+///
+/// This is essentially a copy of isc::dns::OutputBuffer::writeUint16.  It
+/// should really be moved into a separate library.
+///
+/// \param value 16-bit value to convert
+/// \param buffer Data buffer at least two bytes long into which the 16-bit
+///        value is written in network-byte order.
+
+inline void
+writeUint16(uint16_t value, void* buffer) {
+    uint8_t* byte_buffer = static_cast<uint8_t*>(buffer);
+
+    byte_buffer[0] = static_cast<uint8_t>((value & 0xff00U) >> 8);
+    byte_buffer[1] = static_cast<uint8_t>(value & 0x00ffU);
+}
+
+} // namespace asiolink
+
+#endif // __ASIOLINK_UTILITIES_H
diff --git a/src/lib/asiolink/dns_lookup.h b/src/lib/asiolink/dns_lookup.h
index 0788853..a79976f 100644
--- a/src/lib/asiolink/dns_lookup.h
+++ b/src/lib/asiolink/dns_lookup.h
@@ -63,8 +63,10 @@ public:
     ///
     /// \param io_message The event message to handle
     /// \param message The DNS MessagePtr that needs handling
+    /// \param answer_message The final answer will be constructed in
+    ///                       this MessagePtr
     /// \param buffer The final answer is put here
-    /// \param DNSServer DNSServer object to use
+    /// \param server DNSServer object to use
     virtual void operator()(const IOMessage& io_message,
                             isc::dns::MessagePtr message,
                             isc::dns::MessagePtr answer_message,
diff --git a/src/lib/asiolink/dns_server.h b/src/lib/asiolink/dns_server.h
index 352ea8e..f15f808 100644
--- a/src/lib/asiolink/dns_server.h
+++ b/src/lib/asiolink/dns_server.h
@@ -21,7 +21,7 @@ namespace asiolink {
 
 /// \brief The \c DNSServer class is a wrapper (and base class) for
 /// classes which provide DNS server functionality.
-/// 
+///
 /// The classes derived from this one, \c TCPServer and \c UDPServer,
 /// act as the interface layer between clients sending queries, and
 /// functions defined elsewhere that provide answers to those queries.
@@ -42,10 +42,10 @@ namespace asiolink {
 /// when "forking", and that instances will be posted as ASIO handler
 /// objects, which are always copied.
 ///
-/// Because these objects are frequently copied, it is recommended 
+/// Because these objects are frequently copied, it is recommended
 /// that derived classes be kept small to reduce copy overhead.
 class DNSServer {
-protected: 
+protected:
     ///
     /// \name Constructors and destructors
     ///
@@ -66,7 +66,7 @@ public:
     /// the ones in the derived class.  This makes it possible to pass
     /// instances of derived classes as references to this base class
     /// without losing access to derived class data.
-    /// 
+    ///
     //@{
     /// \brief The funtion operator
     virtual void operator()(asio::error_code ec = asio::error_code(),
@@ -87,7 +87,7 @@ public:
 
     /// \brief Indicate whether the server is able to send an answer
     /// to a query.
-    /// 
+    ///
     /// This is presently used only for testing purposes.
     virtual bool hasAnswer() { return (self_->hasAnswer()); }
 
diff --git a/src/lib/asiolink/dns_service.h b/src/lib/asiolink/dns_service.h
index 84aa5fb..9a3fb4c 100644
--- a/src/lib/asiolink/dns_service.h
+++ b/src/lib/asiolink/dns_service.h
@@ -26,13 +26,13 @@ class DNSLookup;
 class DNSAnswer;
 class DNSServiceImpl;
 
+/// \brief Handle DNS Queries
 ///
 /// DNSService is the service that handles DNS queries and answers with
 /// a given IOService. This class is mainly intended to hold all the
 /// logic that is shared between the authoritative and the recursive
 /// server implementations. As such, it handles asio, including config
 /// updates (through the 'Checkinprovider'), and listening sockets.
-/// 
 class DNSService {
     ///
     /// \name Constructors and Destructor
@@ -66,8 +66,8 @@ public:
     ///
     /// \param io_service The IOService to work with
     /// \param port the port to listen on
-    /// \param ipv4 If true, listen on ipv4 'any'
-    /// \param ipv6 If true, listen on ipv6 'any'
+    /// \param use_ipv4 If true, listen on ipv4 'any'
+    /// \param use_ipv6 If true, listen on ipv6 'any'
     /// \param checkin Provider for cc-channel events (see \c SimpleCallback)
     /// \param lookup The lookup provider (see \c DNSLookup)
     /// \param answer The answer provider (see \c DNSAnswer)
diff --git a/src/lib/asiolink/dummy_io_cb.h b/src/lib/asiolink/dummy_io_cb.h
index bde656c..0006b95 100644
--- a/src/lib/asiolink/dummy_io_cb.h
+++ b/src/lib/asiolink/dummy_io_cb.h
@@ -39,6 +39,14 @@ public:
     /// \brief Asynchronous I/O callback method
     ///
     /// \param error Unused
+    void operator()(asio::error_code)
+    {
+        // TODO: log an error if this method ever gets called.
+    }
+
+    /// \brief Asynchronous I/O callback method
+    ///
+    /// \param error Unused
     /// \param length Unused
     void operator()(asio::error_code, size_t)
     {
diff --git a/src/lib/asiolink/internal/iofetch.h b/src/lib/asiolink/internal/iofetch.h
deleted file mode 100644
index d066c92..0000000
--- a/src/lib/asiolink/internal/iofetch.h
+++ /dev/null
@@ -1,125 +0,0 @@
-// 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.
-
-#ifndef __IOFETCH_H
-#define __IOFETCH_H 1
-
-#include <config.h>
-
-#include <asio.hpp>
-#include <boost/shared_array.hpp>
-#include <boost/shared_ptr.hpp>
-
-#include <dns/buffer.h>
-#include <dns/message.h>
-#include <dns/messagerenderer.h>
-
-#include <asiolink/asiolink.h>
-#include <asiolink/internal/coroutine.h>
-
-// This file contains TCP/UDP-specific implementations of generic classes 
-// defined in asiolink.h.  It is *not* intended to be part of the public
-// API.
-
-namespace asiolink {
-//
-// Asynchronous UDP/TCP coroutine for upstream fetches
-//
-//class IOFetch : public coroutine, public UdpFetch, public TcpFetch {
-class IOFetch : public coroutine {
-public:
-    // TODO Maybe this should be more generic than just for IOFetch?
-    ///
-    /// \brief Result of the query
-    ///
-    /// This is related only to contacting the remote server. If the answer
-    ///indicates error, it is still counted as SUCCESS here, if it comes back.
-    ///
-    enum Result {
-        SUCCESS,
-        TIME_OUT,
-        STOPPED
-    };
-    /// Abstract callback for the IOFetch.
-    class Callback {
-    public:
-        virtual ~Callback() {}
-
-        /// This will be called when the IOFetch is completed
-        virtual void operator()(Result result) = 0;
-    };
-    ///
-    /// \brief Constructor.
-    ///
-    /// It creates the query.
-    /// @param callback will be called when we terminate. It is your task to
-    ///        delete it if allocated on heap.
-    ///@param timeout in ms.
-    ///
-    IOFetch(asio::io_service& io_service,
-                      const isc::dns::Question& q,
-                      const IOAddress& addr, uint16_t port,
-                      isc::dns::OutputBufferPtr buffer,
-                      Callback* callback, int timeout = -1, 
-                      int protocol = IPPROTO_UDP);
-    void operator()(asio::error_code ec = asio::error_code(),
-                    size_t length = 0);
-    /// Terminate the query.
-    void stop(Result reason = STOPPED);
-private:
-    enum { MAX_LENGTH = 4096 };
-
-    ///
-    /// \short Private data
-    ///
-    /// They are not private because of stability of the
-    /// interface (this is private class anyway), but because this class
-    /// will be copyed often (it is used as a coroutine and passed as callback
-    /// to many async_*() functions) and we want keep the same data. Some of
-    /// the data is not copyable too.
-    ///
-    //struct IOFetchProtocol;
-    //boost::shared_ptr<IOFetchProtocol> data_;
-    //struct UdpData;
-    //struct TcpData;
-    boost::shared_ptr<UdpFetch> data_;
-    boost::shared_ptr<TcpFetch> tcp_data_;
-};
-class UdpFetch : public IOFetch {
-    public:
-        struct UdpData;
-        explicit UdpFetch(asio::io_service& io_service,
-                          const isc::dns::Question& q,
-                          const IOAddress& addr,
-                          uint16_t port,
-                          isc::dns::OutputBufferPtr buffer,
-                          IOFetch::Callback *callback,
-                          int timeout);
-};
-class TcpFetch : public IOFetch {
-    public:
-        struct TcpData;
-        explicit TcpFetch(io_service& io_service, const Question& q,
-                 const IOAddress& addr, uint16_t port,
-                 OutputBufferPtr buffer, Callback *callback, int timeout);
-};
-
-}
-
-
-#endif // __IOFETCH_H
-
-// Local Variables: 
-// mode: c++
-// End: 
diff --git a/src/lib/asiolink/interval_timer.h b/src/lib/asiolink/interval_timer.h
index d805cd7..6c43327 100644
--- a/src/lib/asiolink/interval_timer.h
+++ b/src/lib/asiolink/interval_timer.h
@@ -37,7 +37,7 @@ struct IntervalTimerImpl;
 /// The function calls the call back function set by \c setup() and updates
 /// the timer to expire in (now + interval) milliseconds.
 /// The type of call back function is \c void(void).
-/// 
+///
 /// The call back function will not be called if the instance of this class is
 /// destroyed before the timer is expired.
 ///
diff --git a/src/lib/asiolink/io_address.h b/src/lib/asiolink/io_address.h
index 0d2787f..53c1a7a 100644
--- a/src/lib/asiolink/io_address.h
+++ b/src/lib/asiolink/io_address.h
@@ -61,7 +61,7 @@ public:
     /// This constructor never throws an exception.
     ///
     /// \param asio_address The ASIO \c ip::address to be converted.
-    IOAddress(const asio::ip::address& asio_adress);
+    IOAddress(const asio::ip::address& asio_address);
     //@}
 
     /// \brief Convert the address to a string.
@@ -121,7 +121,3 @@ private:
 
 }      // asiolink
 #endif // __IO_ADDRESS_H
-
-// Local Variables: 
-// mode: c++
-// End: 
diff --git a/src/lib/asiolink/io_asio_socket.h b/src/lib/asiolink/io_asio_socket.h
index eae9b32..ac793a6 100644
--- a/src/lib/asiolink/io_asio_socket.h
+++ b/src/lib/asiolink/io_asio_socket.h
@@ -26,6 +26,8 @@
 #include <exceptions/exceptions.h>
 #include <coroutine.h>
 
+#include <dns/buffer.h>
+
 #include <asiolink/io_error.h>
 #include <asiolink/io_socket.h>
 
@@ -41,7 +43,24 @@ public:
         IOError(file, line, what) {}
 };
 
+/// \brief Error setting socket options
+///
+/// Thrown if attempt to change socket options fails.
+class SocketSetError : public IOError {
+public:
+    SocketSetError(const char* file, size_t line, const char* what) :
+        IOError(file, line, what) {}
+};
 
+/// \brief Buffer overflow
+///
+/// Thrown if an attempt is made to receive into an area beyond the end of
+/// the receive data buffer.
+class BufferOverflow : public IOError {
+public:
+    BufferOverflow(const char* file, size_t line, const char* what) :
+        IOError(file, line, what) {}
+};
 
 /// Forward declaration of an IOEndpoint
 class IOEndpoint;
@@ -91,24 +110,23 @@ public:
 
     /// \brief Return the "native" representation of the socket.
     ///
-    /// In practice, this is the file descriptor of the socket for
-    /// UNIX-like systems so the current implementation simply uses
-    /// \c int as the type of the return value.
-    /// We may have to need revisit this decision later.
+    /// In practice, this is the file descriptor of the socket for UNIX-like
+    /// systems so the current implementation simply uses \c int as the type of
+    /// the return value. We may have to need revisit this decision later.
     ///
-    /// In general, the application should avoid using this method;
-    /// it essentially discloses an implementation specific "handle" that
-    /// can change the internal state of the socket (consider the
-    /// application closes it, for example).
-    /// But we sometimes need to perform very low-level operations that
-    /// requires the native representation.  Passing the file descriptor
-    /// to a different process is one example.
-    /// This method is provided as a necessary evil for such limited purposes.
+    /// In general, the application should avoid using this method; it
+    /// essentially discloses an implementation specific "handle" that can
+    /// change the internal state of the socket (consider what would happen if
+    /// the application closes it, for example).  But we sometimes need to
+    /// perform very low-level operations that requires the native
+    /// representation.  Passing the file descriptor to a different process is
+    /// one example.  This method is provided as a necessary evil for such
+    /// limited purposes.
     ///
     /// This method never throws an exception.
     ///
     /// \return The native representation of the socket.  This is the socket
-    /// file descriptor for UNIX-like systems.
+    ///         file descriptor for UNIX-like systems.
     virtual int getNative() const = 0;
 
     /// \brief Return the transport protocol of the socket.
@@ -118,36 +136,50 @@ public:
     ///
     /// This method never throws an exception.
     ///
-    /// \return IPPROTO_UDP for UDP sockets
-    /// \return IPPROTO_TCP for TCP sockets
+    /// \return \c IPPROTO_UDP for UDP sockets, \c IPPROTO_TCP for TCP sockets
     virtual int getProtocol() const = 0;
 
-    /// \brief Open AsioSocket
+    /// \brief Is Open() synchronous?
     ///
-    /// Opens the socket for asynchronous I/O.  On a UDP socket, this is merely
-    /// an "open()" on the underlying socket (so completes immediately), but on
-    /// a TCP socket it also connects to the remote end (which is done as an
-    /// asynchronous operation).
+    /// On a TCP socket, an "open" operation is a call to the socket's "open()"
+    /// method followed by a connection to the remote system: it is an
+    /// asynchronous operation.  On a UDP socket, it is just a call to "open()"
+    /// and completes synchronously.
     ///
     /// For TCP, signalling of the completion of the operation is done by
     /// by calling the callback function in the normal way.  This could be done
     /// for UDP (by posting en event on the event queue); however, that will
-    /// incur additional overhead in the most common case.  Instead, the return
-    /// value indicates whether the operation was asynchronous or not. If yes,
-    /// (i.e. TCP) the callback has been posted to the event queue: if no (UDP),
-    /// no callback has been posted (in which case it is up to the caller as to
-    /// whether they want to manually post the callback themself.)
+    /// incur additional overhead in the most common case.  So we give the
+    /// caller the choice for calling this open() method synchronously or
+    /// asynchronously.
+    ///
+    /// Owing to the way that the stackless coroutines are implemented, we need
+    /// to know _before_ executing the "open" function whether or not it is
+    /// asynchronous.  So this method is called to provide that information.
+    ///
+    /// (The reason there is a need to know is because the call to open() passes
+    /// in the state of the coroutine at the time the call is made.  On an
+    /// asynchronous I/O, we need to set the state to point to the statement
+    /// after the call to open() _before_ we pass the corouine to the open()
+    /// call.  Unfortunately, the macros that set the state of the coroutine
+    /// also yield control - which we don't want to do if the open is
+    /// synchronous.  Hence we need to know before we make the call to open()
+    /// whether that call will complete asynchronously.)
+    virtual bool isOpenSynchronous() const = 0;
+
+    /// \brief Open AsioSocket
+    ///
+    /// Opens the socket for asynchronous I/O.  The open will complete
+    /// synchronously on UCP or asynchronously on TCP (in which case a callback
+    /// will be queued).
     ///
     /// \param endpoint Pointer to the endpoint object.  This is ignored for
-    /// a UDP socket (the target is specified in the send call), but should
-    /// be of type TCPEndpoint for a TCP connection.
+    ///        a UDP socket (the target is specified in the send call), but
+    ///        should be of type TCPEndpoint for a TCP connection.
     /// \param callback I/O Completion callback, called when the operation has
-    /// completed, but only if the operation was asynchronous.
-    ///
-    /// \return true if an asynchronous operation was started and the caller
-    /// should yield and wait for completion, false if the operation was
-    /// completed synchronously and no callback was queued.
-    virtual bool open(const IOEndpoint* endpoint, C& callback) = 0;
+    ///        completed, but only if the operation was asynchronous. (It is
+    ///        ignored on a UDP socket.)
+    virtual void open(const IOEndpoint* endpoint, C& callback) = 0;
 
     /// \brief Send Asynchronously
     ///
@@ -160,44 +192,85 @@ public:
     /// \param endpoint Target of the send
     /// \param callback Callback object.
     virtual void asyncSend(const void* data, size_t length,
-        const IOEndpoint* endpoint, C& callback) = 0;
+                           const IOEndpoint* endpoint, C& callback) = 0;
 
     /// \brief Receive Asynchronously
     ///
-    /// This correstponds to async_receive_from() for UDP sockets and
+    /// This corresponds to async_receive_from() for UDP sockets and
     /// async_receive() for TCP.  In both cases, an endpoint argument is
     /// supplied to receive the source of the communication.  For TCP it will
     /// be filled in with details of the connection.
     ///
     /// \param data Buffer to receive incoming message
     /// \param length Length of the data buffer
-    /// \param cumulative Amount of data that should already be in the buffer.
+    /// \param offset Offset into buffer where data is to be put.  Although the
+    ///        offset could be implied by adjusting "data" and "length"
+    ///        appropriately, using this argument allows data to be specified as
+    ///        "const void*" - the overhead of converting it to a pointer to a
+    ///        set of bytes is hidden away here.
     /// \param endpoint Source of the communication
     /// \param callback Callback object
-    virtual void asyncReceive(void* data, size_t length, size_t cumulative,
-        IOEndpoint* endpoint, C& callback) = 0;
-
-    /// \brief Checks if the data received is complete.
-    ///
-    /// This applies to TCP receives, where the data is a byte stream and a
-    /// receive is not guaranteed to receive the entire message.  DNS messages
-    /// over TCP are prefixed by a two-byte count field.  This method takes the
-    /// amount received so far and the amount received in this I/O and checks
-    /// if the message is complete, returning the appropriate indication.  As
-    /// a side-effect, it also updates the amount received.
-    ///
-    /// For a UDP receive, all the data is received in one I/O, so this is
-    /// effectively a no-op (although it does update the amount received).
-    ///
-    /// \param data Data buffer containing data to date
-    /// \param length Amount of data received in last asynchronous I/O
-    /// \param cumulative On input, amount of data received before the last
-    /// I/O.  On output, the total amount of data received to date.
+    virtual void asyncReceive(void* data, size_t length, size_t offset,
+                              IOEndpoint* endpoint, C& callback) = 0;
+
+    /// \brief Processes received data
+    ///
+    /// In the IOFetch code, data is received into a staging buffer before being
+    /// copied into the target buffer.  (This is because (a) we don't know how
+    /// much data we will be receiving, so don't know how to size the output
+    /// buffer and (b) TCP data is preceded by a two-byte count field that needs
+    /// to be discarded before being returned to the user.)
+    ///
+    /// An additional consideration is that TCP data is not received in one
+    /// I/O - it may take a number of I/Os - each receiving any non-zero number
+    /// of bytes - to read the entire message.
+    ///
+    /// So the IOFetch code has to loop until it determines that all the data
+    /// has been read.  This is where this method comes in.  It has several
+    /// functions:
+    ///
+    /// - It checks if the received data is complete.
+    /// - If data is not complete, decides if the next set of data is to go into
+    ///   the start of the staging buffer or at some offset into it.  (This
+    ///   simplifies the case we could have in a TCP receive where the two-byte
+    ///   count field is received in one-byte chunks: we put off interpreting
+    ///   the count until we have all of it.  The alternative - copying the
+    ///   data to the output buffer and interpreting the count from there -
+    ///   would require moving the data in the output buffer by two bytes before
+    ///   returning it to the caller.)
+    /// - Copies data from the staging buffer into the output buffer.
+    ///
+    /// This functionality mainly applies to TCP receives.  For UDP, all the
+    /// data is received in one I/O, so this just copies the data into the
+    /// output buffer.
+    ///
+    /// \param staging Pointer to the start of the staging buffer.
+    /// \param length Amount of data in the staging buffer.
+    /// \param cumulative Amount of data received before the staging buffer is
+    ///        processed (this includes the TCP count field if appropriate).
+    ///        The value should be set to zero before the receive loop is
+    ///        entered, and it will be updated by this method as required.
+    /// \param offset Offset into the staging buffer where the next read should
+    ///        put the received data.  It should be set to zero before the first
+    ///        call and may be updated by this method.
+    /// \param expected Expected amount of data to be received.  This is
+    ///        really the TCP count field and is set to that value when enough
+    ///        of a TCP message is received.  It should be initialized to -1
+    ///        before the first read is executed.
+    /// \param outbuff Output buffer.  Data in the staging buffer may be copied
+    ///        to this output buffer in the call.
     ///
     /// \return true if the receive is complete, false if another receive is
-    /// needed.
-    virtual bool receiveComplete(void* data, size_t length,
-        size_t& cumulative) = 0;
+    ///         needed.  This is always true for UDP, but for TCP involves
+    ///         checking the amount of data received so far against the amount
+    ///         expected (as indicated by the two-byte count field).  If this
+    ///         method returns false, another read should be queued and data
+    ///         should be read into the staging buffer at offset given by the
+    ///         "offset" parameter.
+    virtual bool processReceivedData(const void* staging, size_t length,
+                                     size_t& cumulative, size_t& offset,
+                                     size_t& expected,
+                                     isc::dns::OutputBufferPtr& outbuff) = 0;
 
     /// \brief Cancel I/O On AsioSocket
     virtual void cancel() = 0;
@@ -244,6 +317,13 @@ public:
     virtual int getProtocol() const { return (protocol_); }
 
 
+    /// \brief Is socket opening synchronous?
+    ///
+    /// \return true - it is for this class.
+    bool isOpenSynchronous() const {
+        return true;
+    }
+
     /// \brief Open AsioSocket
     ///
     /// A call that is a no-op on UDP sockets, this opens a connection to the
@@ -273,21 +353,31 @@ public:
     ///
     /// \param data Unused
     /// \param length Unused
-    /// \param cumulative Unused
+    /// \param offset Unused
     /// \param endpoint Unused
     /// \param callback Unused
-    virtual void asyncReceive(void* data, size_t, size_t, IOEndpoint*, C&) { } 
+    virtual void asyncReceive(void* data, size_t, size_t, IOEndpoint*, C&) {
+    }
+
     /// \brief Checks if the data received is complete.
     ///
-    /// \param data Unused
+    /// \param staging Unused
     /// \param length Unused
     /// \param cumulative Unused
+    /// \param offset Unused.
+    /// \param expected Unused.
+    /// \param outbuff Unused.
     ///
     /// \return Always true
-    virtual bool receiveComplete(void*, size_t, size_t&) {
+    virtual bool receiveComplete(const void* staging, size_t length,
+                                 size_t& cumulative, size_t& offset,
+                                 size_t& expected,
+                                 isc::dns::OutputBufferPtr& outbuff)
+    {
         return (true);
     }
 
+
     /// \brief Cancel I/O On AsioSocket
     ///
     /// Must be supplied as it is abstract in the base class.
diff --git a/src/lib/asiolink/io_endpoint.cc b/src/lib/asiolink/io_endpoint.cc
index bf79f61..97e9c91 100644
--- a/src/lib/asiolink/io_endpoint.cc
+++ b/src/lib/asiolink/io_endpoint.cc
@@ -22,6 +22,7 @@
 
 #include <asiolink/io_address.h>
 #include <asiolink/io_error.h>
+#include <asiolink/io_endpoint.h>
 #include <asiolink/tcp_endpoint.h>
 #include <asiolink/udp_endpoint.h>
 
diff --git a/src/lib/asiolink/io_endpoint.h b/src/lib/asiolink/io_endpoint.h
index 62b9e47..2ec4083 100644
--- a/src/lib/asiolink/io_endpoint.h
+++ b/src/lib/asiolink/io_endpoint.h
@@ -116,7 +116,3 @@ public:
 
 }      // asiolink
 #endif // __IO_ENDPOINT_H
-
-// Local Variables: 
-// mode: c++
-// End: 
diff --git a/src/lib/asiolink/io_fetch.cc b/src/lib/asiolink/io_fetch.cc
index d1f722c..3ff44c0 100644
--- a/src/lib/asiolink/io_fetch.cc
+++ b/src/lib/asiolink/io_fetch.cc
@@ -19,15 +19,30 @@
 #include <netinet/in.h>
 
 #include <boost/bind.hpp>
+#include <boost/scoped_ptr.hpp>
+#include <boost/date_time/posix_time/posix_time_types.hpp>
 
 #include <dns/message.h>
 #include <dns/messagerenderer.h>
 #include <dns/opcode.h>
 #include <dns/rcode.h>
-#include <log/dummylog.h>
+#include <log/logger.h>
+
+#include <asiolink/qid_gen.h>
 
 #include <asio.hpp>
+#include <asio/deadline_timer.hpp>
+
+#include <asiolink/asiodef.h>
+#include <asiolink/io_address.h>
+#include <asiolink/io_asio_socket.h>
+#include <asiolink/io_endpoint.h>
 #include <asiolink/io_fetch.h>
+#include <asiolink/io_service.h>
+#include <asiolink/tcp_endpoint.h>
+#include <asiolink/tcp_socket.h>
+#include <asiolink/udp_endpoint.h>
+#include <asiolink/udp_socket.h>
 
 using namespace asio;
 using namespace isc::dns;
@@ -36,23 +51,125 @@ using namespace std;
 
 namespace asiolink {
 
+/// Use the ASIO logger
+
+isc::log::Logger logger("asiolink");
+
+/// \brief IOFetch Data
+///
+/// The data for IOFetch is held in a separate struct pointed to by a shared_ptr
+/// object.  This is because the IOFetch object will be copied often (it is used
+/// as a coroutine and passed as callback to many async_*() functions) and we
+/// want keep the same data).  Organising the data in this way keeps copying to
+/// a minimum.
+struct IOFetchData {
+
+    // The first two members are shared pointers to a base class because what is
+    // actually instantiated depends on whether the fetch is over UDP or TCP,
+    // which is not known until construction of the IOFetch.  Use of a shared
+    // pointer here is merely to ensure deletion when the data object is deleted.
+    boost::scoped_ptr<IOAsioSocket<IOFetch> > socket;
+                                            ///< Socket to use for I/O
+    boost::scoped_ptr<IOEndpoint> remote;   ///< Where the fetch was sent
+    isc::dns::Question          question;   ///< Question to be asked
+    isc::dns::OutputBufferPtr   msgbuf;     ///< Wire buffer for question
+    isc::dns::OutputBufferPtr   received;   ///< Received data put here
+    IOFetch::Callback*          callback;   ///< Called on I/O Completion
+    asio::deadline_timer        timer;      ///< Timer to measure timeouts
+    IOFetch::Protocol           protocol;   ///< Protocol being used
+    size_t                      cumulative; ///< Cumulative received amount
+    size_t                      expected;   ///< Expected amount of data
+    size_t                      offset;     ///< Offset to receive data
+    bool                        stopped;    ///< Have we stopped running?
+    int                         timeout;    ///< Timeout in ms
+
+    // In case we need to log an error, the origin of the last asynchronous
+    // I/O is recorded.  To save time and simplify the code, this is recorded
+    // as the ID of the error message that would be generated if the I/O failed.
+    // This means that we must make sure that all possible "origins" take the
+    // same arguments in their message in the same order.
+    isc::log::MessageID         origin;     ///< Origin of last asynchronous I/O
+    uint8_t                     staging[IOFetch::STAGING_LENGTH];
+                                            ///< Temporary array for received data
+
+    /// \brief Constructor
+    ///
+    /// Just fills in the data members of the IOFetchData structure
+    ///
+    /// \param proto Either IOFetch::TCP or IOFetch::UDP.
+    /// \param service I/O Service object to handle the asynchronous
+    ///        operations.
+    /// \param query DNS question to send to the upstream server.
+    /// \param address IP address of upstream server
+    /// \param port Port to use for the query
+    /// \param buff Output buffer into which the response (in wire format)
+    ///        is written (if a response is received).
+    /// \param cb Callback object containing the callback to be called
+    ///        when we terminate.  The caller is responsible for managing this
+    ///        object and deleting it if necessary.
+    /// \param wait Timeout for the fetch (in ms).
+    ///
+    /// TODO: May need to alter constructor (see comment 4 in Trac ticket #554)
+    IOFetchData(IOFetch::Protocol proto, IOService& service,
+        const isc::dns::Question& query, const IOAddress& address,
+        uint16_t port, isc::dns::OutputBufferPtr& buff, IOFetch::Callback* cb,
+        int wait)
+        :
+        socket((proto == IOFetch::UDP) ?
+            static_cast<IOAsioSocket<IOFetch>*>(
+                new UDPSocket<IOFetch>(service)) :
+            static_cast<IOAsioSocket<IOFetch>*>(
+                new TCPSocket<IOFetch>(service))
+            ),
+        remote((proto == IOFetch::UDP) ?
+            static_cast<IOEndpoint*>(new UDPEndpoint(address, port)) :
+            static_cast<IOEndpoint*>(new TCPEndpoint(address, port))
+            ),
+        question(query),
+        msgbuf(new isc::dns::OutputBuffer(512)),
+        received(buff),
+
+        callback(cb),
+        timer(service.get_io_service()),
+        protocol(proto),
+        cumulative(0),
+        expected(0),
+        offset(0),
+        stopped(false),
+        timeout(wait),
+        origin(ASIO_UNKORIGIN),
+        staging()
+    {}
+};
+
 /// IOFetch Constructor - just initialize the private data
 
-IOFetch::IOFetch(int protocol, IOService& service,
+IOFetch::IOFetch(Protocol protocol, IOService& service,
     const isc::dns::Question& question, const IOAddress& address, uint16_t port,
-    isc::dns::OutputBufferPtr& buff, Callback* cb, int wait)
+    OutputBufferPtr& buff, Callback* cb, int wait)
     :
-    data_(new IOFetch::IOFetchData(protocol, service, question, address,
+    data_(new IOFetchData(protocol, service, question, address,
         port, buff, cb, wait))
 {
 }
 
+// Return protocol in use.
+
+IOFetch::Protocol
+IOFetch::getProtocol() const {
+    return (data_->protocol);
+}
+
 /// The function operator is implemented with the "stackless coroutine"
 /// pattern; see internal/coroutine.h for details.
 
 void
-IOFetch::operator()(error_code ec, size_t length) {
-    if (ec || data_->stopped) {
+IOFetch::operator()(asio::error_code ec, size_t length) {
+
+    if (data_->stopped) {
+        return;
+    } else if (ec) {
+        logIOFailure(ec);
         return;
     }
 
@@ -63,26 +180,17 @@ IOFetch::operator()(error_code ec, size_t length) {
         /// declarations.
         {
             Message msg(Message::RENDER);
-            
-            // TODO: replace with boost::random or some other suitable PRNG
-            msg.setQid(0);
+            msg.setQid(QidGenerator::getInstance().generateQid());
             msg.setOpcode(Opcode::QUERY());
             msg.setRcode(Rcode::NOERROR());
             msg.setHeaderFlag(Message::HEADERFLAG_RD);
             msg.addQuestion(data_->question);
             MessageRenderer renderer(*data_->msgbuf);
             msg.toWire(renderer);
-
-            // As this is a new fetch, clear the amount of data received
-            data_->cumulative = 0;
-
-            dlog("Sending " + msg.toText() + " to " +
-                data_->remote->getAddress().toText());
         }
 
-
-        // If we timeout, we stop, which will shutdown everything and
-        // cancel all other attempts to run inside the coroutine
+        // If we timeout, we stop, which will can cancel outstanding I/Os and
+        // shutdown everything.
         if (data_->timeout != -1) {
             data_->timer.expires_from_now(boost::posix_time::milliseconds(
                 data_->timeout));
@@ -91,13 +199,17 @@ IOFetch::operator()(error_code ec, size_t length) {
         }
 
         // Open a connection to the target system.  For speed, if the operation
-        // was completed synchronously (i.e. UDP operation) we bypass the yield.
-        if (data_->socket->open(data_->remote.get(), *this)) {
-            CORO_YIELD;
+        // is synchronous (i.e. UDP operation) we bypass the yield.
+        data_->origin = ASIO_OPENSOCK;
+        if (data_->socket->isOpenSynchronous()) {
+            data_->socket->open(data_->remote.get(), *this);
+        } else {
+            CORO_YIELD data_->socket->open(data_->remote.get(), *this);
         }
 
-        // Begin an asynchronous send, and then yield.  When the send completes
-        // send completes, we will resume immediately after this point.
+        // Begin an asynchronous send, and then yield.  When the send completes,
+        // we will resume immediately after this point.
+        data_->origin = ASIO_SENDSOCK;
         CORO_YIELD data_->socket->asyncSend(data_->msgbuf->getData(),
             data_->msgbuf->getLength(), data_->remote.get(), *this);
 
@@ -108,24 +220,33 @@ IOFetch::operator()(error_code ec, size_t length) {
         // we need to yield ... and we *really* don't want to set up another
         // coroutine within that method.)  So after each receive (and yield),
         // we check if the operation is complete and if not, loop to read again.
+        //
+        // Another concession to TCP is that the amount of is contained in the
+        // first two bytes.  This leads to two problems:
+        //
+        // a) We don't want those bytes in the return buffer.
+        // b) They may not both arrive in the first I/O.
+        //
+        // So... we need to loop until we have at least two bytes, then store
+        // the expected amount of data.  Then we need to loop until we have
+        // received all the data before copying it back to the user's buffer.
+        // And we want to minimise the amount of copying...
+
+        data_->origin = ASIO_RECVSOCK;
+        data_->cumulative = 0;          // No data yet received
+        data_->offset = 0;              // First data into start of buffer
         do {
-            CORO_YIELD data_->socket->asyncReceive(data_->data.get(),
-                static_cast<size_t>(MAX_LENGTH), data_->cumulative,
-                data_->remote.get(), *this);
-        } while (!data_->socket->receiveComplete(data_->data.get(), length,
-            data_->cumulative));
-
-        // The message is not rendered yet, so we can't print it easily
-        dlog("Received response from " + data_->remote->getAddress().toText());
-
-        /// Copy the answer into the response buffer.  (TODO: If the
-        /// OutputBuffer object were made to meet the requirements of
-        /// a MutableBufferSequence, then it could be written to directly
-        /// by async_receive_from() and this additional copy step would
-        /// be unnecessary.)
-        data_->buffer->writeData(data_->data.get(), length);
-
-        // Finished with this socket, so close it.
+            CORO_YIELD data_->socket->asyncReceive(data_->staging,
+                                                   static_cast<size_t>(STAGING_LENGTH),
+                                                   data_->offset,
+                                                   data_->remote.get(), *this);
+        } while (!data_->socket->processReceivedData(data_->staging, length,
+                                                     data_->cumulative, data_->offset,
+                                                     data_->expected, data_->received));
+
+        // Finished with this socket, so close it.  This will not generate an
+        // I/O error, but reset the origin to unknown in case we change this.
+        data_->origin = ASIO_UNKORIGIN;
         data_->socket->close();
 
         /// We are done
@@ -137,9 +258,8 @@ IOFetch::operator()(error_code ec, size_t length) {
 // query finishes or when the timer times out.  Either way, it sets the
 // "stopped_" flag and cancels anything that is in progress.
 //
-// As the function may be entered multiple times as things wind down, the
-// stopped_ flag checks if stop() has already been called.  If it has,
-// subsequent calls are no-ops.
+// As the function may be entered multiple times as things wind down, it checks
+// if the stopped_ flag is already set.  If it is, the call is a no-op.
 
 void
 IOFetch::stop(Result result) {
@@ -156,20 +276,46 @@ IOFetch::stop(Result result) {
         // variable should be done inside a mutex (and the stopped_ variable
         // declared as "volatile").
         //
+        // The numeric arguments indicate the debug level, with the lower
+        // numbers indicating the most important information.  The relative
+        // values are somewhat arbitrary.
+        //
+        // Although Logger::debug checks the debug flag internally, doing it
+        // below before calling Logger::debug avoids the overhead of a string
+        // conversion in the common case when debug is not enabled.
+        //
         // TODO: Update testing of stopped_ if threads are used.
         data_->stopped = true;
-
         switch (result) {
             case TIME_OUT:
-                dlog("Query timed out");
+                if (logger.isDebugEnabled(1)) {
+                    logger.debug(20, ASIO_RECVTMO,
+                                 data_->remote->getAddress().toText().c_str(),
+                                 static_cast<int>(data_->remote->getPort()));
+                }
+                break;
+
+            case SUCCESS:
+                if (logger.isDebugEnabled(50)) {
+                    logger.debug(30, ASIO_FETCHCOMP,
+                                 data_->remote->getAddress().toText().c_str(),
+                                 static_cast<int>(data_->remote->getPort()));
+                }
                 break;
 
             case STOPPED:
-                dlog("Query stopped");
+                // Fetch has been stopped for some other reason.  This is
+                // allowed but as it is unusual it is logged, but with a lower
+                // debug level than a timeout (which is totally normal).
+                logger.debug(1, ASIO_FETCHSTOP,
+                             data_->remote->getAddress().toText().c_str(),
+                             static_cast<int>(data_->remote->getPort()));
                 break;
 
             default:
-                ;
+                logger.error(ASIO_UNKRESULT, static_cast<int>(result),
+                             data_->remote->getAddress().toText().c_str(),
+                             static_cast<int>(data_->remote->getPort()));
         }
 
         // Stop requested, cancel and I/O's on the socket and shut it down,
@@ -183,10 +329,26 @@ IOFetch::stop(Result result) {
         if (data_->callback) {
             (*(data_->callback))(result);
         }
+    }
+}
 
-        // Mark that stop() has now been called.
+// Log an error - called on I/O failure
 
-    }
+void IOFetch::logIOFailure(asio::error_code ec) {
+
+    // Should only get here with a known error code.
+    assert((data_->origin == ASIO_OPENSOCK) ||
+           (data_->origin == ASIO_SENDSOCK) ||
+           (data_->origin == ASIO_RECVSOCK) ||
+           (data_->origin == ASIO_UNKORIGIN));
+
+    static const char* PROTOCOL[2] = {"TCP", "UDP"};
+    logger.error(data_->origin,
+                 ec.value(),
+                 ((data_->remote->getProtocol() == IPPROTO_TCP) ?
+                     PROTOCOL[0] : PROTOCOL[1]),
+                 data_->remote->getAddress().toText().c_str(),
+                 static_cast<int>(data_->remote->getPort()));
 }
 
 } // namespace asiolink
diff --git a/src/lib/asiolink/io_fetch.h b/src/lib/asiolink/io_fetch.h
index 8158c6c..0723777 100644
--- a/src/lib/asiolink/io_fetch.h
+++ b/src/lib/asiolink/io_fetch.h
@@ -17,31 +17,23 @@
 
 #include <config.h>
 
-#include <netinet/in.h>
-#include <sys/socket.h>
-#include <unistd.h>             // for some IPC/network system calls
-
 #include <boost/shared_array.hpp>
 #include <boost/shared_ptr.hpp>
 #include <boost/date_time/posix_time/posix_time_types.hpp>
-#include <asio/deadline_timer.hpp>
 
 #include <coroutine.h>
 
+#include <asio/error_code.hpp>
+
 #include <dns/buffer.h>
 #include <dns/question.h>
 
-#include <asiolink/io_asio_socket.h>
-#include <asiolink/io_endpoint.h>
-#include <asiolink/io_service.h>
-#include <asiolink/tcp_socket.h>
-#include <asiolink/tcp_endpoint.h>
-#include <asiolink/udp_socket.h>
-#include <asiolink/udp_endpoint.h>
-
-
 namespace asiolink {
 
+// Forward declarations
+class IOAddress;
+class IOFetchData;
+class IOService;
 
 /// \brief Upstream Fetch Processing
 ///
@@ -51,6 +43,23 @@ namespace asiolink {
 
 class IOFetch : public coroutine {
 public:
+    /// \brief Protocol to use on the fetch
+    enum Protocol {
+        UDP = 0,
+        TCP = 1
+    };
+
+    /// \brief Origin of Asynchronous I/O Call
+    ///
+    /// Indicates what initiated an asynchronous I/O call and used in deciding
+    /// what error message to output if the I/O fails.
+    enum Origin {
+        NONE = 0,           ///< No asynchronous call outstanding
+        OPEN = 1,
+        SEND = 2,
+        RECEIVE = 3,
+        CLOSE = 4
+    };
 
     /// \brief Result of Upstream Fetch
     ///
@@ -59,9 +68,9 @@ public:
     /// even if the contents of the packet indicate that some error occurred.
     enum Result {
         SUCCESS = 0,        ///< Success, fetch completed
-        TIME_OUT,           ///< Failure, fetch timed out
-        STOPPED,            ///< Control code, fetch has been stopped
-        NOTSET              ///< For testing, indicates value not set
+        TIME_OUT = 1,       ///< Failure, fetch timed out
+        STOPPED = 2,        ///< Control code, fetch has been stopped
+        NOTSET = 3          ///< For testing, indicates value not set
     };
 
     // The next enum is a "trick" to allow constants to be defined in a class
@@ -69,7 +78,7 @@ public:
 
     /// \brief Integer Constants
     enum {
-        MAX_LENGTH = 4096   ///< Maximum size of receive buffer
+        STAGING_LENGTH = 8192   ///< Size of staging buffer
     };
 
     /// \brief I/O Fetch Callback
@@ -95,82 +104,12 @@ public:
         virtual ~Callback()
         {}
 
-        /// \brief Callback method called when the fetch completes
-        ///
-        /// \brief result Result of the fetch
-        virtual void operator()(Result result) = 0;
-    };
-
-    /// \brief IOFetch Data
-    ///
-    /// The data for IOFetch is held in a separate struct pointed to by a
-    /// shared_ptr object.  This is because the IOFetch object will be copied
-    /// often (it is used as a coroutine and passed as callback to many
-    /// async_*() functions) and we want keep the same data).  Organising the
-    /// data in this way keeps copying to a minimum.
-    struct IOFetchData {
-
-        // The next two members are shared pointers to a base class because what
-        // is actually instantiated depends on whether the fetch is over UDP or
-        // TCP, which is not known until construction of the IOFetch.  Use of
-        // a shared pointer here is merely to ensure deletion when the data
-        // object is deleted.
-        boost::shared_ptr<IOAsioSocket<IOFetch> > socket;
-                                                ///< Socket to use for I/O
-        boost::shared_ptr<IOEndpoint> remote;   ///< Where the fetch was sent
-        isc::dns::Question          question;   ///< Question to be asked
-        isc::dns::OutputBufferPtr   msgbuf;     ///< Wire buffer for question
-        isc::dns::OutputBufferPtr   buffer;     ///< Received data held here
-        boost::shared_array<char>   data;       ///< Temporary array for data
-        IOFetch::Callback*          callback;   ///< Called on I/O Completion
-        size_t                      cumulative; ///< Cumulative received amount
-        bool                        stopped;    ///< Have we stopped running?
-        asio::deadline_timer        timer;      ///< Timer to measure timeouts
-        int                         timeout;    ///< Timeout in ms
-
-        /// \brief Constructor
-        ///
-        /// Just fills in the data members of the IOFetchData structure
+        /// \brief Callback method
         ///
-        /// \param protocol either IPPROTO_UDP or IPPROTO_TCP
-        /// \param service I/O Service object to handle the asynchronous
-        ///     operations.
-        /// \param query DNS question to send to the upstream server.
-        /// \param address IP address of upstream server
-        /// \param port Port to use for the query
-        /// \param buff Output buffer into which the response (in wire format)
-        ///     is written (if a response is received).
-        /// \param cb Callback object containing the callback to be called
-        ///     when we terminate.  The caller is responsible for managing this
-        ///     object and deleting it if necessary.
-        /// \param wait Timeout for the fetch (in ms).
+        /// This is the method called when the fetch completes.
         ///
-        /// TODO: May need to alter constructor (see comment 4 in Trac ticket #554)
-        IOFetchData(int protocol, IOService& service,
-            const isc::dns::Question& query, const IOAddress& address,
-            uint16_t port, isc::dns::OutputBufferPtr& buff, Callback* cb,
-            int wait)
-            :
-            socket((protocol == IPPROTO_UDP) ?
-                static_cast<IOAsioSocket<IOFetch>*>(
-                    new UDPSocket<IOFetch>(service)) :
-                static_cast<IOAsioSocket<IOFetch>*>(
-                    new TCPSocket<IOFetch>(service))
-                ),
-            remote((protocol == IPPROTO_UDP) ?
-                static_cast<IOEndpoint*>(new UDPEndpoint(address, port)) :
-                static_cast<IOEndpoint*>(new TCPEndpoint(address, port))
-                ),
-            question(query),
-            msgbuf(new isc::dns::OutputBuffer(512)),
-            buffer(buff),
-            data(new char[IOFetch::MAX_LENGTH]),
-            callback(cb),
-            cumulative(0),
-            stopped(false),
-            timer(service.get_io_service()),
-            timeout(wait)
-        {}
+        /// \param result Result of the fetch
+        virtual void operator()(Result result) = 0;
     };
 
     /// \brief Constructor.
@@ -179,7 +118,7 @@ public:
     ///
     /// TODO: Need to randomise the source port
     ///
-    /// \param protocol Fetch protocol, either IPPROTO_UDP or IPPROTO_TCP
+    /// \param protocol Fetch protocol, either IOFetch::TCP or IOFetch::UDP
     /// \param service I/O Service object to handle the asynchronous
     ///     operations.
     /// \param question DNS question to send to the upstream server.
@@ -193,11 +132,16 @@ public:
     /// (default = 53)
     /// \param wait Timeout for the fetch (in ms).  The default value of
     ///     -1 indicates no timeout.
-    IOFetch(int protocol, IOService& service,
+    IOFetch(Protocol protocol, IOService& service,
         const isc::dns::Question& question, const IOAddress& address,
         uint16_t port, isc::dns::OutputBufferPtr& buff, Callback* cb,
         int wait = -1);
-    
+
+    /// \brief Return Current Protocol
+    ///
+    /// \return Protocol associated with this IOFetch object.
+    Protocol getProtocol() const;
+
     /// \brief Coroutine entry point
     ///
     /// The operator() method is the method in which the coroutine code enters
@@ -205,8 +149,7 @@ public:
     ///
     /// \param ec Error code, the result of the last asynchronous I/O operation.
     /// \param length Amount of data received on the last asynchronous read
-    void operator()(asio::error_code ec = asio::error_code(),
-        size_t length = 0);
+    void operator()(asio::error_code ec = asio::error_code(), size_t length = 0);
 
     /// \brief Terminate query
     ///
@@ -217,6 +160,16 @@ public:
     void stop(Result reason = STOPPED);
 
 private:
+    /// \brief Log I/O Failure
+    ///
+    /// Records an I/O failure to the log file
+    ///
+    /// \param ec ASIO error code
+    void logIOFailure(asio::error_code ec);
+
+    // Member variables.  All data is in a structure pointed to by a shared
+    // pointer.  The IOFetch object is copied a number of times during its
+    // life, and only requiring a pointer to be copied reduces overhead.
     boost::shared_ptr<IOFetchData>  data_;   ///< Private data
 
 };
diff --git a/src/lib/asiolink/io_message.h b/src/lib/asiolink/io_message.h
index 532f449..e857bd9 100644
--- a/src/lib/asiolink/io_message.h
+++ b/src/lib/asiolink/io_message.h
@@ -98,7 +98,3 @@ private:
 
 }      // asiolink
 #endif // __IO_MESSAGE_H
-
-// Local Variables: 
-// mode: c++
-// End: 
diff --git a/src/lib/asiolink/qid_gen.cc b/src/lib/asiolink/qid_gen.cc
new file mode 100644
index 0000000..4063b39
--- /dev/null
+++ b/src/lib/asiolink/qid_gen.cc
@@ -0,0 +1,54 @@
+// 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.
+
+// qid_gen defines a generator for query id's
+//
+// We probably want to merge this with the weighted random in the nsas
+// (and other parts where we need randomness, perhaps another thing
+// for a general libutil?)
+
+#include <asiolink/qid_gen.h>
+
+#include <sys/time.h>
+
+namespace {
+    asiolink::QidGenerator qid_generator_instance;
+}
+
+namespace asiolink {
+
+QidGenerator&
+QidGenerator::getInstance() {
+    return (qid_generator_instance);
+}
+
+QidGenerator::QidGenerator() : dist_(0, 65535),
+                               vgen_(generator_, dist_)
+{
+    seed();
+}
+
+void
+QidGenerator::seed() {
+    struct timeval tv;
+    gettimeofday(&tv, 0);
+    generator_.seed((tv.tv_sec * 1000000) + tv.tv_usec);
+}
+
+isc::dns::qid_t
+QidGenerator::generateQid() {
+    return (vgen_());
+}
+
+} // namespace asiolink
diff --git a/src/lib/asiolink/qid_gen.h b/src/lib/asiolink/qid_gen.h
new file mode 100644
index 0000000..a5caa17
--- /dev/null
+++ b/src/lib/asiolink/qid_gen.h
@@ -0,0 +1,85 @@
+// 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.
+
+// qid_gen defines a generator for query id's
+//
+// We probably want to merge this with the weighted random in the nsas
+// (and other parts where we need randomness, perhaps another thing
+// for a general libutil?)
+
+#ifndef __QID_GEN_H
+#define __QID_GEN_H
+
+#include <dns/message.h>
+#include <boost/random/mersenne_twister.hpp>
+#include <boost/random/uniform_int.hpp>
+#include <boost/random/variate_generator.hpp>
+
+
+namespace asiolink {
+
+/// This class generates Qids for outgoing queries
+///
+/// It is implemented as a singleton; the public way to access it
+/// is to call getInstance()->generateQid().
+///
+/// It automatically seeds it with the current time when it is first
+/// used.
+class QidGenerator {
+public:
+    /// \brief Returns the singleton instance of the QidGenerator
+    ///
+    /// Returns a reference to the singleton instance of the generator
+    static QidGenerator& getInstance();
+
+    /// \brief Default constructor
+    ///
+    /// It is recommended that getInstance is used rather than creating
+    /// separate instances of this class.
+    ///
+    /// The constructor automatically seeds the generator with the
+    /// current time.
+    QidGenerator();
+
+    /// Generate a Qid
+    ///
+    /// \return A random Qid
+    isc::dns::qid_t generateQid();
+
+    /// \brief Seeds the QidGenerator (based on the current time)
+    ///
+    /// This is automatically called by the constructor
+    void seed();
+
+private:
+    // "Mersenne Twister: A 623-dimensionally equidistributed
+    // uniform pseudo-random number generator", Makoto Matsumoto and
+    // Takuji Nishimura, ACM Transactions on Modeling and Computer
+    // Simulation: Special Issue on Uniform Random Number Generation,
+    // Vol. 8, No. 1, January 1998, pp. 3-30.
+    //
+    // mt19937 is an implementation of one of the pseudo random
+    // generators described in this paper.
+    boost::mt19937 generator_;
+
+    // For qid's we want a uniform distribution
+    boost::uniform_int<> dist_;
+
+    boost::variate_generator<boost::mt19937&, boost::uniform_int<> > vgen_;
+};
+
+
+} // namespace asiolink
+
+#endif // __QID_GEN_H
diff --git a/src/lib/asiolink/recursive_query.cc b/src/lib/asiolink/recursive_query.cc
deleted file mode 100644
index 0bdf24e..0000000
--- a/src/lib/asiolink/recursive_query.cc
+++ /dev/null
@@ -1,543 +0,0 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <netinet/in.h>
-#include <stdlib.h>
-#include <sys/socket.h>
-#include <unistd.h>             // for some IPC/network system calls
-
-#include <boost/lexical_cast.hpp>
-#include <boost/bind.hpp>
-
-#include <config.h>
-
-#include <log/dummylog.h>
-
-#include <dns/question.h>
-#include <dns/message.h>
-#include <dns/opcode.h>
-
-#include <resolve/resolve.h>
-#include <cache/resolver_cache.h>
-
-#include <asio.hpp>
-#include <asiolink/dns_service.h>
-#include <asiolink/io_fetch.h>
-#include <asiolink/io_service.h>
-#include <asiolink/recursive_query.h>
-
-using isc::log::dlog;
-using namespace isc::dns;
-
-namespace asiolink {
-
-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 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)),
-    upstream_root_(new AddressVector(upstream_root)),
-    query_timeout_(query_timeout), client_timeout_(client_timeout),
-    lookup_timeout_(lookup_timeout), retries_(retries)
-{}
-
-namespace {
-
-typedef std::pair<std::string, uint16_t> addr_t;
-
-/*
- * This is a query in progress. When a new query is made, this one holds
- * the context information about it, like how many times we are allowed
- * to retry on failure, what to do when we succeed, etc.
- *
- * Used by RecursiveQuery::sendQuery.
- */
-class RunningQuery : public IOFetch::Callback {
-private:
-    // The io service to handle async calls
-    IOService& io_;
-
-    // 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
-    boost::shared_ptr<AddressVector> upstream_;
-
-    // root servers...just copied over to the zone_servers_
-    boost::shared_ptr<AddressVector> upstream_root_;
-
-    // Buffer to store the result.
-    OutputBufferPtr buffer_;
-
-    // Server to notify when we succeed or fail
-    //shared_ptr<DNSServer> server_;
-    isc::resolve::ResolverInterface::CallbackPtr resolvercallback_;
-
-    // To prevent both unreasonably long cname chains and cname loops,
-    // we simply keep a counter of the number of CNAMEs we have
-    // followed so far (and error if it exceeds RESOLVER_MAX_CNAME_CHAIN
-    // from lib/resolve/response_classifier.h)
-    unsigned cname_count_;
-
-    /*
-     * TODO Do something more clever with timeouts. In the long term, some
-     *     computation of average RTT, increase with each retry, etc.
-     */
-    // Timeout information
-    int query_timeout_;
-    unsigned retries_;
-
-    // normal query state
-
-    // Not using NSAS at this moment, so we keep a list
-    // of 'current' zone servers
-    std::vector<addr_t> zone_servers_;
-
-    // Update the question that will be sent to the server
-    void setQuestion(const Question& new_question) {
-        question_ = new_question;
-    }
-
-    // TODO: replace by our wrapper
-    asio::deadline_timer client_timer;
-    asio::deadline_timer lookup_timer;
-
-    size_t queries_out_;
-    
-    // If we timed out ourselves (lookup timeout), stop issuing queries
-    bool done_;
-
-    // If we have a client timeout, we send back an answer, but don't
-    // stop. We use this variable to make sure we don't send another
-    // answer if we do find one later (or if we have a lookup_timeout)
-    bool answer_sent_;
-
-    // Reference to our cache
-    isc::cache::ResolverCache& cache_;
-
-    // perform a single lookup; first we check the cache to see
-    // if we have a response for our query stored already. if
-    // so, call handlerecursiveresponse(), if not, we call send()
-    void doLookup() {
-        dlog("doLookup: try cache");
-        Message cached_message(Message::RENDER);
-        isc::resolve::initResponseMessage(question_, cached_message);
-        if (cache_.lookup(question_.getName(), question_.getType(),
-                          question_.getClass(), cached_message)) {
-            dlog("Message found in cache, returning that");
-            handleRecursiveAnswer(cached_message);
-        } else {
-            send();
-        }
-        
-    }
-
-    // (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() +
-                ") to " + upstream_->at(serverIndex).first);
-            IOFetch query(IPPROTO_UDP, io_, question_,
-                upstream_->at(serverIndex).first,
-                upstream_->at(serverIndex).second, buffer_, this,
-                query_timeout_);
-            ++queries_out_;
-            io_.get_io_service().post(query);
-        } else if (zs > 0) {
-            int serverIndex = rand() % zs;
-            dlog("Sending query to zone server (" + question_.toText() +
-                ") to " + zone_servers_.at(serverIndex).first);
-            IOFetch query(IPPROTO_UDP, io_, question_,
-                zone_servers_.at(serverIndex).first,
-                zone_servers_.at(serverIndex).second, buffer_, this,
-                query_timeout_);
-            ++queries_out_;
-            io_.get_io_service().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 (either we have an answer or an
-    //              error message)
-    // returns false if we are not done
-    bool handleRecursiveAnswer(const Message& incoming) {
-        dlog("Handle response");
-        // In case we get a CNAME, we store the target
-        // here (classify() will set it when it walks through
-        // the cname chain to verify it).
-        Name cname_target(question_.getName());
-        
-        isc::resolve::ResponseClassifier::Category category =
-            isc::resolve::ResponseClassifier::classify(
-                question_, incoming, cname_target, cname_count_, true);
-
-        bool found_ns_address = false;
-            
-        // If the packet is OK, store it in the cache
-        if (!isc::resolve::ResponseClassifier::error(category)) {
-            cache_.update(incoming);
-        }
-
-        switch (category) {
-        case isc::resolve::ResponseClassifier::ANSWER:
-        case isc::resolve::ResponseClassifier::ANSWERCNAME:
-            // Done. copy and return.
-            isc::resolve::copyResponseMessage(incoming, answer_message_);
-            return true;
-            break;
-        case isc::resolve::ResponseClassifier::CNAME:
-            dlog("Response is CNAME!");
-            // (unfinished) CNAME. We set our question_ to the CNAME
-            // target, then start over at the beginning (for now, that
-            // is, we reset our 'current servers' to the root servers).
-            if (cname_count_ >= RESOLVER_MAX_CNAME_CHAIN) {
-                // just give up
-                dlog("CNAME chain too long");
-                isc::resolve::makeErrorMessage(answer_message_,
-                                               Rcode::SERVFAIL());
-                return true;
-            }
-
-            answer_message_->appendSection(Message::SECTION_ANSWER,
-                                           incoming);
-            setZoneServersToRoot();
-
-            question_ = Question(cname_target, question_.getClass(),
-                                 question_.getType());
-
-            dlog("Following CNAME chain to " + question_.toText());
-            doLookup();
-            return false;
-            break;
-        case isc::resolve::ResponseClassifier::NXDOMAIN:
-            // NXDOMAIN, just copy and return.
-            isc::resolve::copyResponseMessage(incoming, answer_message_);
-            return true;
-            break;
-        case isc::resolve::ResponseClassifier::REFERRAL:
-            // Referral. For now we just take the first glue address
-            // we find and continue with that
-            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.
-                        
-                        // TODO should use NSAS
-                        zone_servers_.push_back(addr_t(addr_str, 53));
-                        found_ns_address = true;
-                        break;
-                    }
-                }
-            }
-            if (found_ns_address) {
-                // next resolver round
-                // we do NOT use doLookup() here, but send() (i.e. we
-                // skip the cache), since if we had the final answer
-                // instead of a delegation cached, we would have been
-                // there by now.
-                send();
-                return false;
-            } else {
-                dlog("[XX] no ready-made addresses in additional. need nsas.");
-                // TODO this will result in answering with the delegation. oh well
-                isc::resolve::copyResponseMessage(incoming, answer_message_);
-                return true;
-            }
-            break;
-        case isc::resolve::ResponseClassifier::EMPTY:
-        case isc::resolve::ResponseClassifier::EXTRADATA:
-        case isc::resolve::ResponseClassifier::INVNAMCLASS:
-        case isc::resolve::ResponseClassifier::INVTYPE:
-        case isc::resolve::ResponseClassifier::MISMATQUEST:
-        case isc::resolve::ResponseClassifier::MULTICLASS:
-        case isc::resolve::ResponseClassifier::NOTONEQUEST:
-        case isc::resolve::ResponseClassifier::NOTRESPONSE:
-        case isc::resolve::ResponseClassifier::NOTSINGLE:
-        case isc::resolve::ResponseClassifier::OPCODE:
-        case isc::resolve::ResponseClassifier::RCODE:
-        case isc::resolve::ResponseClassifier::TRUNCATED:
-            // Should we try a different server rather than SERVFAIL?
-            isc::resolve::makeErrorMessage(answer_message_,
-                                           Rcode::SERVFAIL());
-            return true;
-            break;
-        }
-        // should not be reached. assert here?
-        dlog("[FATAL] unreachable code");
-        return true;
-    }
-    
-public:
-    RunningQuery(IOService& io,
-        const Question &question,
-        MessagePtr answer_message,
-        boost::shared_ptr<AddressVector> upstream,
-        boost::shared_ptr<AddressVector> upstream_root,
-        OutputBufferPtr buffer,
-        isc::resolve::ResolverInterface::CallbackPtr cb,
-        int query_timeout, int client_timeout, int lookup_timeout,
-        unsigned retries,
-        isc::cache::ResolverCache& cache) :
-        io_(io),
-        question_(question),
-        answer_message_(answer_message),
-        upstream_(upstream),
-        upstream_root_(upstream_root),
-        buffer_(buffer),
-        resolvercallback_(cb),
-        cname_count_(0),
-        query_timeout_(query_timeout),
-        retries_(retries),
-        client_timer(io.get_io_service()),
-        lookup_timer(io.get_io_service()),
-        queries_out_(0),
-        done_(false),
-        answer_sent_(false),
-        cache_(cache)
-    {
-        // 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()) {
-            setZoneServersToRoot();
-        }
-
-        doLookup();
-    }
-
-    void setZoneServersToRoot() {
-        zone_servers_.clear();
-        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<std::string>(upstream_root_->size()) + 
-                "\n");
-            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");
-            }
-        }
-    }
-    virtual void clientTimeout() {
-        // Return a SERVFAIL, but do not stop until
-        // we have an answer or timeout ourselves
-        isc::resolve::makeErrorMessage(answer_message_,
-                                       Rcode::SERVFAIL());
-        if (!answer_sent_) {
-            answer_sent_ = true;
-            resolvercallback_->success(answer_message_);
-        }
-    }
-
-    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 && !answer_sent_) {
-            answer_sent_ = true;
-
-            // There are two types of messages we could store in the
-            // cache;
-            // 1. answers to our fetches from authoritative servers,
-            //    exactly as we receive them, and
-            // 2. answers to queries we received from clients, which
-            //    have received additional processing (following CNAME
-            //    chains, for instance)
-            //
-            // Doing only the first would mean we would have to re-do
-            // processing when we get data from our cache, and doing
-            // only the second would miss out on the side-effect of
-            // having nameserver data in our cache.
-            //
-            // So right now we do both. Since the cache (currently)
-            // stores Messages on their question section only, this
-            // does mean that we overwrite the messages we stored in
-            // the previous iteration if we are following a delegation.
-            cache_.update(*answer_message_);
-
-            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()(IOFetch::Result result) {
-        // XXX is this the place for TCP retry?
-        --queries_out_;
-        if (!done_ && result != IOFetch::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 {
-                isc::resolve::copyResponseMessage(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 {
-            // out of retries, give up for now
-            stop(false);
-        }
-    }
-};
-
-}
-
-void
-RecursiveQuery::resolve(const QuestionPtr& question,
-    const isc::resolve::ResolverInterface::CallbackPtr callback)
-{
-    IOService& io = dns_service_.getIOService();
-
-    MessagePtr answer_message(new Message(Message::RENDER));
-    isc::resolve::initResponseMessage(*question, *answer_message);
-
-    OutputBufferPtr buffer(new OutputBuffer(0));
-
-    dlog("Try out cache first (direct call to resolve)");
-    // First try to see if we have something cached in the messagecache
-    if (cache_.lookup(question->getName(), question->getType(),
-                      question->getClass(), *answer_message)) {
-        dlog("Message found in cache, returning that");
-        // TODO: err, should cache set rcode as well?
-        answer_message->setRcode(Rcode::NOERROR());
-        callback->success(answer_message);
-    } else {
-        dlog("Message not found in cache, starting recursive query");
-        // 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_,
-                         cache_);
-    }
-}
-
-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.
-    IOService& io = dns_service_.getIOService();
-
-    isc::resolve::ResolverInterface::CallbackPtr crs(
-        new isc::resolve::ResolverCallbackServer(server));
-
-    // TODO: general 'prepareinitialanswer'
-    answer_message->setOpcode(isc::dns::Opcode::QUERY());
-    answer_message->addQuestion(question);
-    
-    // First try to see if we have something cached in the messagecache
-    dlog("Try out cache first (started by incoming event)");
-    if (cache_.lookup(question.getName(), question.getType(),
-                      question.getClass(), *answer_message)) {
-        dlog("Message found in cache, returning that");
-        // TODO: err, should cache set rcode as well?
-        answer_message->setRcode(Rcode::NOERROR());
-        crs->success(answer_message);
-    } else {
-        dlog("Message not found in cache, starting recursive query");
-        // It will delete itself when it is done
-        new RunningQuery(io, question, answer_message, upstream_, upstream_root_,
-                             buffer, crs, query_timeout_, client_timeout_,
-                             lookup_timeout_, retries_, cache_);
-    }
-}
-
-
-
-} // namespace asiolink
diff --git a/src/lib/asiolink/recursive_query.h b/src/lib/asiolink/recursive_query.h
deleted file mode 100644
index 6ef0069..0000000
--- a/src/lib/asiolink/recursive_query.h
+++ /dev/null
@@ -1,117 +0,0 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#ifndef __ASIOLINK_RECURSIVE_QUERY_H
-#define __ASIOLINK_RECURSIVE_QUERY_H 1
-
-#include <asiolink/dns_service.h>
-#include <asiolink/dns_server.h>
-#include <dns/buffer.h>
-#include <cache/resolver_cache.h>
-
-namespace asiolink {
-/// \brief The \c RecursiveQuery class provides a layer of abstraction around
-/// the ASIO code that carries out an upstream query.
-///
-/// This design is very preliminary; currently it is only capable of
-/// handling simple forward requests to a single resolver.
-class RecursiveQuery {
-    ///
-    /// \name Constructors
-    ///
-    //@{
-public:
-    /// \brief Constructor
-    ///
-    /// This is currently the only way to construct \c RecursiveQuery
-    /// 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 query_timeout Timeout value for queries we sent, in ms
-    /// \param client_timeout Timeout value for when we send back an
-    ///        error, in ms
-    /// \param lookup_timeout Timeout value for when we give up, in ms
-    /// \param retries how many times we try again (0 means just send and
-    ///     and return if it returs).
-    RecursiveQuery(DNSService& dns_service,
-                   const std::vector<std::pair<std::string, uint16_t> >&
-                   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 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 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 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 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_;
-    boost::shared_ptr<std::vector<std::pair<std::string, uint16_t> > >
-        upstream_root_;
-    int query_timeout_;
-    int client_timeout_;
-    int lookup_timeout_;
-    unsigned retries_;
-    // Cache. TODO: I think we want this initialized in Resolver class,
-    // not here
-    isc::cache::ResolverCache cache_;
-};
-
-}      // namespace asiolink
-#endif // __ASIOLINK_RECURSIVE_QUERY_H
diff --git a/src/lib/asiolink/tcp_endpoint.h b/src/lib/asiolink/tcp_endpoint.h
index 8f6270f..158ca4a 100644
--- a/src/lib/asiolink/tcp_endpoint.h
+++ b/src/lib/asiolink/tcp_endpoint.h
@@ -24,32 +24,33 @@
 namespace asiolink {
 
 /// \brief The \c TCPEndpoint class is a concrete derived class of
-/// \c IOEndpoint that represents an endpoint of a TCP connection.
+/// \c IOEndpoint that represents an endpoint of a TCP packet.
 ///
-/// In the current implementation, an object of this class is always
-/// instantiated within the wrapper routines.  Applications are expected to
-/// get access to the object via the abstract base class, \c IOEndpoint.
-/// This design may be changed when we generalize the wrapper interface.
-///
-/// Note: this implementation is optimized for the case where this object
-/// is created from an ASIO endpoint object in a receiving code path
-/// by avoiding to make a copy of the base endpoint.  For TCP it may not be
-/// a big deal, but when we receive UDP packets at a high rate, the copy
-/// overhead might be significant.
+/// Other notes about \c TCPEndpoint applies to this class, too.
 class TCPEndpoint : public IOEndpoint {
 public:
     ///
-    /// \name Constructors and Destructor
+    /// \name Constructors and Destructor.
     ///
     //@{
+
+    /// \brief Default Constructor
+    ///
+    /// Creates an internal endpoint.  This is expected to be set by some
+    /// external call.
+    TCPEndpoint() :
+        asio_endpoint_placeholder_(new asio::ip::tcp::endpoint()),
+        asio_endpoint_(*asio_endpoint_placeholder_)
+    {}
+
     /// \brief Constructor from a pair of address and port.
     ///
     /// \param address The IP address of the endpoint.
     /// \param port The TCP port number of the endpoint.
     TCPEndpoint(const IOAddress& address, const unsigned short port) :
         asio_endpoint_placeholder_(
-            new asio::ip::tcp::endpoint(
-                asio::ip::address::from_string(address.toText()), port)),
+            new asio::ip::tcp::endpoint(asio::ip::address::from_string(address.toText()),
+                              port)),
         asio_endpoint_(*asio_endpoint_placeholder_)
     {}
 
@@ -59,39 +60,53 @@ public:
     /// corresponding ASIO class, \c tcp::endpoint.
     ///
     /// \param asio_endpoint The ASIO representation of the TCP endpoint.
-    TCPEndpoint(const asio::ip::tcp::endpoint& asio_endpoint) :
+    TCPEndpoint(asio::ip::tcp::endpoint& asio_endpoint) :
         asio_endpoint_placeholder_(NULL), asio_endpoint_(asio_endpoint)
     {}
 
+    /// \brief Constructor from an ASIO TCP endpoint.
+    ///
+    /// This constructor is designed to be an efficient wrapper for the
+    /// corresponding ASIO class, \c tcp::endpoint.
+    ///
+    /// \param asio_endpoint The ASIO representation of the TCP endpoint.
+    TCPEndpoint(const asio::ip::tcp::endpoint& asio_endpoint) :
+        asio_endpoint_placeholder_(new asio::ip::tcp::endpoint(asio_endpoint)),
+        asio_endpoint_(*asio_endpoint_placeholder_)
+    {}
+
     /// \brief The destructor.
-    ~TCPEndpoint() { delete asio_endpoint_placeholder_; }
+    virtual ~TCPEndpoint() { delete asio_endpoint_placeholder_; }
     //@}
 
-    IOAddress getAddress() const {
+    virtual IOAddress getAddress() const {
         return (asio_endpoint_.address());
     }
 
-    uint16_t getPort() const {
+    virtual uint16_t getPort() const {
         return (asio_endpoint_.port());
     }
 
-    short getProtocol() const {
+    virtual short getProtocol() const {
         return (asio_endpoint_.protocol().protocol());
     }
 
-    short getFamily() const {
+    virtual short getFamily() const {
         return (asio_endpoint_.protocol().family());
     }
 
     // This is not part of the exosed IOEndpoint API but allows
     // direct access to the ASIO implementation of the endpoint
-    const asio::ip::tcp::endpoint& getASIOEndpoint() const {
+    inline const asio::ip::tcp::endpoint& getASIOEndpoint() const {
+        return (asio_endpoint_);
+    }
+    inline asio::ip::tcp::endpoint& getASIOEndpoint() {
         return (asio_endpoint_);
     }
 
 private:
-    const asio::ip::tcp::endpoint* asio_endpoint_placeholder_;
-    const asio::ip::tcp::endpoint& asio_endpoint_;
+    asio::ip::tcp::endpoint* asio_endpoint_placeholder_;
+    asio::ip::tcp::endpoint& asio_endpoint_;
 };
 
 }      // namespace asiolink
diff --git a/src/lib/asiolink/tcp_server.cc b/src/lib/asiolink/tcp_server.cc
index df19b00..0bc56d8 100644
--- a/src/lib/asiolink/tcp_server.cc
+++ b/src/lib/asiolink/tcp_server.cc
@@ -17,6 +17,7 @@
 #include <netinet/in.h>
 #include <sys/socket.h>
 #include <unistd.h>             // for some IPC/network system calls
+#include <errno.h>
 
 #include <boost/shared_array.hpp>
 
@@ -83,11 +84,21 @@ TCPServer::operator()(error_code ec, size_t length) {
             /// Create a socket to listen for connections
             socket_.reset(new tcp::socket(acceptor_->get_io_service()));
 
-            /// Wait for new connections. In the event of error,
+            /// Wait for new connections. In the event of non-fatal error,
             /// try again
             do {
                 CORO_YIELD acceptor_->async_accept(*socket_, *this);
-            } while (!ec);
+                // Abort on fatal errors
+                // TODO: Log error?
+                if (ec) {
+                    using namespace asio::error;
+                    if (ec.value() != would_block && ec.value() != try_again &&
+                        ec.value() != connection_aborted &&
+                        ec.value() != interrupted) {
+                        return;
+                    }
+                }
+            } while (ec);
 
             /// Fork the coroutine by creating a copy of this one and
             /// scheduling it on the ASIO service queue.  The parent
@@ -104,6 +115,7 @@ TCPServer::operator()(error_code ec, size_t length) {
         CORO_YIELD async_read(*socket_, asio::buffer(data_.get(),
                               TCP_MESSAGE_LENGTHSIZE), *this);
         if (ec) {
+            socket_->close();
             CORO_YIELD return;
         }
 
@@ -116,6 +128,7 @@ TCPServer::operator()(error_code ec, size_t length) {
         }
 
         if (ec) {
+            socket_->close();
             CORO_YIELD return;
         }
 
@@ -149,6 +162,7 @@ TCPServer::operator()(error_code ec, size_t length) {
         // If we don't have a DNS Lookup provider, there's no point in
         // continuing; we exit the coroutine permanently.
         if (lookup_callback_ == NULL) {
+            socket_->close();
             CORO_YIELD return;
         }
 
@@ -166,6 +180,9 @@ TCPServer::operator()(error_code ec, size_t length) {
         // The 'done_' flag indicates whether we have an answer
         // to send back.  If not, exit the coroutine permanently.
         if (!done_) {
+            // TODO: should we keep the connection open for a short time
+            // to see if new requests come in?
+            socket_->close();
             CORO_YIELD return;
         }
 
@@ -184,6 +201,10 @@ TCPServer::operator()(error_code ec, size_t length) {
         // (though we have nothing further to do, so the coroutine
         // will simply exit at that time).
         CORO_YIELD async_write(*socket_, bufs, *this);
+
+        // TODO: should we keep the connection open for a short time
+        // to see if new requests come in?
+        socket_->close();
     }
 }
 
diff --git a/src/lib/asiolink/tcp_socket.h b/src/lib/asiolink/tcp_socket.h
index 5a85aaa..e6e0863 100644
--- a/src/lib/asiolink/tcp_socket.h
+++ b/src/lib/asiolink/tcp_socket.h
@@ -24,11 +24,18 @@
 #include <sys/socket.h>
 #include <unistd.h>             // for some IPC/network system calls
 
-#include <iostream>
+#include <algorithm>
+#include <cassert>
 #include <cstddef>
 
+#include <boost/bind.hpp>
+#include <boost/numeric/conversion/cast.hpp>
+
 #include <config.h>
 
+#include <dns/buffer.h>
+
+#include <asiolink/asiolink_utilities.h>
 #include <asiolink/io_asio_socket.h>
 #include <asiolink/io_endpoint.h>
 #include <asiolink/io_service.h>
@@ -36,6 +43,15 @@
 
 namespace asiolink {
 
+/// \brief Buffer Too Large
+///
+/// Thrown on an attempt to send a buffer > 64k
+class BufferTooLarge : public IOError {
+public:
+    BufferTooLarge(const char* file, size_t line, const char* what) :
+        IOError(file, line, what) {}
+};
+
 /// \brief The \c TCPSocket class is a concrete derived class of \c IOAsioSocket
 /// that represents a TCP socket.
 ///
@@ -48,18 +64,18 @@ private:
     TCPSocket& operator=(const TCPSocket&);
 
 public:
-    
+
     /// \brief Constructor from an ASIO TCP socket.
     ///
-    /// \param socket The ASIO representation of the TCP socket.  It
-    /// is assumed that the caller will open and close the socket, so
-    /// these operations are a no-op for that socket.
+    /// \param socket The ASIO representation of the TCP socket.  It is assumed
+    ///        that the caller will open and close the socket, so these
+    ///        operations are a no-op for that socket.
     TCPSocket(asio::ip::tcp::socket& socket);
 
     /// \brief Constructor
     ///
     /// Used when the TCPSocket is being asked to manage its own internal
-    /// socket.  It is assumed that open() and close() will not be used.
+    /// socket.  In this case, the open() and close() methods are used.
     ///
     /// \param service I/O Service object used to manage the socket.
     TCPSocket(IOService& service);
@@ -67,68 +83,79 @@ public:
     /// \brief Destructor
     virtual ~TCPSocket();
 
-    virtual int getNative() const { return (socket_.native()); }
-    virtual int getProtocol() const { return (IPPROTO_TCP); }
+    /// \brief Return file descriptor of underlying socket
+    virtual int getNative() const {
+        return (socket_.native());
+    }
 
-    /// \brief Open Socket
+    /// \brief Return protocol of socket
+    virtual int getProtocol() const {
+        return (IPPROTO_TCP);
+    }
+
+    /// \brief Is "open()" synchronous?
     ///
-    /// Opens the TCP socket.  In the model for transport-layer agnostic I/O,
-    /// an "open" operation includes a connection to the remote end (which
-    /// may take time).  This does not happen for TCP, so the method returns
-    /// "false" to indicate that the operation completed synchronously.
+    /// Indicates that the opening of a TCP socket is asynchronous.
+    virtual bool isOpenSynchronous() const {
+        return (false);
+    }
+
+    /// \brief Open Socket
     ///
-    /// \param endpoint Endpoint to which the socket will connect to.
-    /// \param callback Unused.
+    /// Opens the TCP socket.  This is an asynchronous operation, completion of
+    /// which will be signalled via a call to the callback function.
     ///
-    /// \return false to indicate that the "operation" completed synchronously.
-    virtual bool open(const IOEndpoint* endpoint, C&);
+    /// \param endpoint Endpoint to which the socket will connect.
+    /// \param callback Callback object.
+    virtual void open(const IOEndpoint* endpoint, C& callback);
 
     /// \brief Send Asynchronously
     ///
-    /// This corresponds to async_send_to() for TCP sockets and async_send()
-    /// for TCP.  In both cases an endpoint argument is supplied indicating the
-    /// target of the send - this is ignored for TCP.
+    /// Calls the underlying socket's async_send() method to send a packet of
+    /// data asynchronously to the remote endpoint.  The callback will be called
+    /// on completion.
     ///
     /// \param data Data to send
     /// \param length Length of data to send
-    /// \param endpoint Target of the send
+    /// \param endpoint Target of the send. (Unused for a TCP socket because
+    ///        that was determined when the connection was opened.)
     /// \param callback Callback object.
     virtual void asyncSend(const void* data, size_t length,
-        const IOEndpoint* endpoint, C& callback);
+                           const IOEndpoint* endpoint, C& callback);
 
     /// \brief Receive Asynchronously
     ///
-    /// This correstponds to async_receive_from() for TCP sockets and
-    /// async_receive() for TCP.  In both cases, an endpoint argument is
-    /// supplied to receive the source of the communication.  For TCP it will
-    /// be filled in with details of the connection.
+    /// Calls the underlying socket's async_receive() method to read a packet
+    /// of data from a remote endpoint.  Arrival of the data is signalled via a
+    /// call to the callback function.
     ///
     /// \param data Buffer to receive incoming message
     /// \param length Length of the data buffer
-    /// \param cumulative Amount of data that should already be in the buffer.
-    /// (This is ignored - every UPD receive fills the buffer from the start.)
+    /// \param offset Offset into buffer where data is to be put
     /// \param endpoint Source of the communication
     /// \param callback Callback object
-    virtual void asyncReceive(void* data, size_t length, size_t cumulative,
-        IOEndpoint* endpoint, C& callback);
+    virtual void asyncReceive(void* data, size_t length, size_t offset,
+                              IOEndpoint* endpoint, C& callback);
 
-    /// \brief Checks if the data received is complete.
+    /// \brief Process received data packet
     ///
-    /// As all the data is received in one I/O, so this is, this is effectively
-    /// a no-op (although it does update the amount of data received).
+    /// See the description of IOAsioSocket::receiveComplete for a complete
+    /// description of this method.
     ///
-    /// \param data Data buffer containing data to date.  (This is ignored
-    /// for TCP receives.)
-    /// \param length Amount of data received in last asynchronous I/O
-    /// \param cumulative On input, amount of data received before the last
-    /// I/O.  On output, the total amount of data received to date.
+    /// \param staging Pointer to the start of the staging buffer.
+    /// \param length Amount of data in the staging buffer.
+    /// \param cumulative Amount of data received before the staging buffer is
+    ///        processed.
+    /// \param offset Unused.
+    /// \param expected unused.
+    /// \param outbuff Output buffer.  Data in the staging buffer is be copied
+    ///        to this output buffer in the call.
     ///
-    /// \return true if the receive is complete, false if another receive is
-    /// needed.
-    virtual bool receiveComplete(void*, size_t length, size_t& cumulative) {
-        cumulative = length;
-        return (true);
-    }
+    /// \return Always true
+    virtual bool processReceivedData(const void* staging, size_t length,
+                                     size_t& cumulative, size_t& offset,
+                                     size_t& expected,
+                                     isc::dns::OutputBufferPtr& outbuff);
 
     /// \brief Cancel I/O On Socket
     virtual void cancel();
@@ -144,13 +171,28 @@ private:
     asio::ip::tcp::socket*      socket_ptr_;    ///< Pointer to own socket
     asio::ip::tcp::socket&      socket_;        ///< Socket
     bool                        isopen_;        ///< true when socket is open
+
+    // TODO: Remove temporary buffer
+    // The current implementation copies the buffer passed to asyncSend() into
+    // a temporary buffer and precedes it with a two-byte count field.  As
+    // ASIO should really be just about sending and receiving data, the TCP
+    // code should not do this.  If the protocol using this requires a two-byte
+    // count, it should add it before calling this code.  (This may be best
+    // achieved by altering isc::dns::buffer to have pairs of methods:
+    // getLength()/getTCPLength(), getData()/getTCPData(), with the getTCPXxx()
+    // methods taking into account a two-byte count field.)
+    //
+    // The option of sending the data in two operations, the count followed by
+    // the data was discounted as that would lead to two callbacks which would
+    // cause problems with the stackless coroutine code.
+    isc::dns::OutputBufferPtr   send_buffer_;   ///< Send buffer
 };
 
 // Constructor - caller manages socket
 
 template <typename C>
 TCPSocket<C>::TCPSocket(asio::ip::tcp::socket& socket) :
-    socket_ptr_(NULL), socket_(socket), isopen_(true)
+    socket_ptr_(NULL), socket_(socket), isopen_(true), send_buffer_()
 {
 }
 
@@ -171,16 +213,14 @@ TCPSocket<C>::~TCPSocket()
     delete socket_ptr_;
 }
 
-// Open the socket.  Throws an error on failure
-// TODO: Make the open more resilient
+// Open the socket.
 
-template <typename C> bool
-TCPSocket<C>::open(const IOEndpoint* endpoint, C&) {
+template <typename C> void
+TCPSocket<C>::open(const IOEndpoint* endpoint, C& callback) {
 
     // Ignore opens on already-open socket.  Don't throw a failure because
     // of uncertainties as to what precedes whan when using asynchronous I/O.
     // At also allows us a treat a passed-in socket as a self-managed socket.
-
     if (!isopen_) {
         if (endpoint->getFamily() == AF_INET) {
             socket_.open(asio::ip::tcp::v4());
@@ -190,10 +230,25 @@ TCPSocket<C>::open(const IOEndpoint* endpoint, C&) {
         }
         isopen_ = true;
 
-        // TODO: Complete TCPSocket::open()
+        // Set options on the socket:
 
+        // Reuse address - allow the socket to bind to a port even if the port
+        // is in the TIMED_WAIT state.
+        socket_.set_option(asio::socket_base::reuse_address(true));
     }
-    return (false);
+
+    // Upconvert to a TCPEndpoint.  We need to do this because although
+    // IOEndpoint is the base class of UDPEndpoint and TCPEndpoint, it does not
+    // contain a method for getting at the underlying endpoint type - that is in
+    /// the derived class and the two classes differ on return type.
+    assert(endpoint->getProtocol() == IPPROTO_TCP);
+    const TCPEndpoint* tcp_endpoint =
+        static_cast<const TCPEndpoint*>(endpoint);
+
+    // Connect to the remote endpoint.  On success, the handler will be
+    // called (with one argument - the length argument will default to
+    // zero).
+    socket_.async_connect(tcp_endpoint->getASIOEndpoint(), callback);
 }
 
 // Send a message.  Should never do this if the socket is not open, so throw
@@ -201,24 +256,29 @@ TCPSocket<C>::open(const IOEndpoint* endpoint, C&) {
 
 template <typename C> void
 TCPSocket<C>::asyncSend(const void* data, size_t length,
-    const IOEndpoint* endpoint, C& callback)
+    const IOEndpoint*, C& callback)
 {
     if (isopen_) {
 
-        // Upconvert to a TCPEndpoint.  We need to do this because although
-        // IOEndpoint is the base class of TCPEndpoint and TCPEndpoint, it
-        // doing cont contain a method for getting at the underlying endpoint
-        // type - those are in the derived class and the two classes differ on
-        // return type.
-
-        assert(endpoint->getProtocol() == IPPROTO_TCP);
-        const TCPEndpoint* tcp_endpoint =
-            static_cast<const TCPEndpoint*>(endpoint);
-        std::cerr << "TCPSocket::asyncSend(): sending to " <<
-            tcp_endpoint->getAddress().toText() <<
-            ", port " << tcp_endpoint->getPort() << "\n";
-
-        // TODO: Complete TCPSocket::asyncSend()
+        // Need to copy the data into a temporary buffer and precede it with
+        // a two-byte count field.
+        // TODO: arrange for the buffer passed to be preceded by the count
+        try {
+            // Ensure it fits into 16 bits
+            uint16_t count = boost::numeric_cast<uint16_t>(length);
+
+            // Copy data into a buffer preceded by the count field.
+            send_buffer_.reset(new isc::dns::OutputBuffer(length + 2));
+            send_buffer_->writeUint16(count);
+            send_buffer_->writeData(data, length);
+
+            // ... and send it
+            socket_.async_send(asio::buffer(send_buffer_->getData(),
+                               send_buffer_->getLength()), callback);
+        } catch (boost::numeric::bad_numeric_cast& e) {
+            isc_throw(BufferTooLarge,
+                      "attempt to send buffer larger than 64kB");
+        }
 
     } else {
         isc_throw(SocketNotOpen,
@@ -226,26 +286,40 @@ TCPSocket<C>::asyncSend(const void* data, size_t length,
     }
 }
 
-// Receive a message. Note that the "cumulative" argument is ignored - every TCP
-// receive is put into the buffer beginning at the start - there is no concept
-// receiving a subsequent part of a message.  Same critera as before concerning
-// the need for the socket to be open.
-
+// Receive a message. Note that the "offset" argument is used as an index
+// into the buffer in order to decide where to put the data.  It is up to the
+// caller to initialize the data to zero
 template <typename C> void
-TCPSocket<C>::asyncReceive(void* data, size_t length, size_t,
+TCPSocket<C>::asyncReceive(void* data, size_t length, size_t offset,
     IOEndpoint* endpoint, C& callback)
 {
     if (isopen_) {
-
-        // Upconvert the endpoint again.
+        // Upconvert to a TCPEndpoint.  We need to do this because although
+        // IOEndpoint is the base class of UDPEndpoint and TCPEndpoint, it
+        // does not contain a method for getting at the underlying endpoint
+        // type - that is in the derived class and the two classes differ on
+        // return type.
         assert(endpoint->getProtocol() == IPPROTO_TCP);
-        const TCPEndpoint* tcp_endpoint =
-            static_cast<const TCPEndpoint*>(endpoint);
-        std::cerr << "TCPSocket::asyncReceive(): receiving from " <<
-            tcp_endpoint->getAddress().toText() <<
-            ", port " << tcp_endpoint->getPort() << "\n";
+        TCPEndpoint* tcp_endpoint = static_cast<TCPEndpoint*>(endpoint);
+
+        // Write the endpoint details from the communications link.  Ideally
+        // we should make IOEndpoint assignable, but this runs in to all sorts
+        // of problems concerning the management of the underlying Boost
+        // endpoint (e.g. if it is not self-managed, is the copied one
+        // self-managed?) The most pragmatic solution is to let Boost take care
+        // of everything and copy details of the underlying endpoint.
+        tcp_endpoint->getASIOEndpoint() = socket_.remote_endpoint();
+
+        // Ensure we can write into the buffer and if so, set the pointer to
+        // where the data will be written.
+        if (offset >= length) {
+            isc_throw(BufferOverflow, "attempt to read into area beyond end of "
+                                      "TCP receive buffer");
+        }
+        void* buffer_start = static_cast<void*>(static_cast<uint8_t*>(data) + offset);
 
-        // TODO: Complete TCPSocket::asyncReceive()
+        // ... and kick off the read.
+        socket_.async_receive(asio::buffer(buffer_start, length - offset), callback);
 
     } else {
         isc_throw(SocketNotOpen,
@@ -253,7 +327,72 @@ TCPSocket<C>::asyncReceive(void* data, size_t length, size_t,
     }
 }
 
+// Is the receive complete?
+
+template <typename C> bool
+TCPSocket<C>::processReceivedData(const void* staging, size_t length,
+                                  size_t& cumulative, size_t& offset,
+                                  size_t& expected,
+                                  isc::dns::OutputBufferPtr& outbuff)
+{
+    // Point to the data in the staging buffer and note how much there is.
+    const uint8_t* data = static_cast<const uint8_t*>(staging);
+    size_t data_length = length;
+
+    // Is the number is "expected" valid?  It won't be unless we have received
+    // at least two bytes of data in total for this set of receives.
+    if (cumulative < 2) {
+
+        // "expected" is not valid.  Did this read give us enough data to
+        // work it out?
+        cumulative += length;
+        if (cumulative < 2) {
+
+            // Nope, still not valid.  This must have been the first packet and
+            // was only one byte long.  Tell the fetch code to read the next
+            // packet into the staging buffer beyond the data that is already
+            // there so that the next time we are called we have a complete
+            // TCP count.
+            offset = cumulative;
+            return (false);
+        }
+
+        // Have enough data to interpret the packet count, so do so now.
+        expected = readUint16(data);
+
+        // We have two bytes less of data to process.  Point to the start of the
+        // data and adjust the packet size.  Note that at this point,
+        // "cumulative" is the true amount of data in the staging buffer, not
+        // "length".
+        data += 2;
+        data_length = cumulative - 2;
+    } else {
+
+        // Update total amount of data received.
+        cumulative += length;
+    }
+
+    // Regardless of anything else, the next read goes into the start of the
+    // staging buffer.
+    offset = 0;
+
+    // Work out how much data we still have to put in the output buffer. (This
+    // could be zero if we have just interpreted the TCP count and that was
+    // set to zero.)
+    if (expected >= outbuff->getLength()) {
+
+        // Still need data in the output packet.  Copy what we can from the
+        // staging buffer to the output buffer.
+        size_t copy_amount = std::min(expected - outbuff->getLength(), data_length);
+        outbuff->writeData(data, copy_amount);
+    }
+
+    // We can now say if we have all the data.
+    return (expected == outbuff->getLength());
+}
+
 // Cancel I/O on the socket.  No-op if the socket is not open.
+
 template <typename C> void
 TCPSocket<C>::cancel() {
     if (isopen_) {
diff --git a/src/lib/asiolink/tests/Makefile.am b/src/lib/asiolink/tests/Makefile.am
index b4f0a87..000e6f2 100644
--- a/src/lib/asiolink/tests/Makefile.am
+++ b/src/lib/asiolink/tests/Makefile.am
@@ -18,26 +18,27 @@ TESTS += run_unittests
 run_unittests_SOURCES  = run_unittests.cc
 run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.h
 run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
+run_unittests_SOURCES += asiolink_utilities_unittest.cc
 run_unittests_SOURCES += io_address_unittest.cc
 run_unittests_SOURCES += io_endpoint_unittest.cc
 run_unittests_SOURCES += io_fetch_unittest.cc
 run_unittests_SOURCES += io_socket_unittest.cc
 run_unittests_SOURCES += io_service_unittest.cc
 run_unittests_SOURCES += interval_timer_unittest.cc
-run_unittests_SOURCES += recursive_query_unittest.cc
+run_unittests_SOURCES += tcp_endpoint_unittest.cc
+run_unittests_SOURCES += tcp_socket_unittest.cc
 run_unittests_SOURCES += udp_endpoint_unittest.cc
 run_unittests_SOURCES += udp_socket_unittest.cc
+run_unittests_SOURCES += qid_gen_unittest.cc
 
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 
 run_unittests_LDADD  = $(GTEST_LDADD)
 run_unittests_LDADD += $(SQLITE_LIBS)
-run_unittests_LDADD +=  $(top_builddir)/src/lib/dns/libdns++.la
-run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
 run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
 run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
-run_unittests_LDADD += $(top_builddir)/src/lib/cache/libcache.la
-run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
+run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 
 run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
 
diff --git a/src/lib/asiolink/tests/asiolink_utilities_unittest.cc b/src/lib/asiolink/tests/asiolink_utilities_unittest.cc
new file mode 100644
index 0000000..51f565f
--- /dev/null
+++ b/src/lib/asiolink/tests/asiolink_utilities_unittest.cc
@@ -0,0 +1,74 @@
+// 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 Test of asiolink utilties
+///
+/// Tests the fuctionality of the asiolink utilities code by comparing them
+/// with the equivalent methods in isc::dns::[Input/Output]Buffer.
+
+#include <cstddef>
+
+#include <gtest/gtest.h>
+
+#include <dns/buffer.h>
+#include <asiolink/asiolink_utilities.h>
+
+using namespace asiolink;
+using namespace isc::dns;
+
+TEST(asioutil, readUint16) {
+
+    // Reference buffer
+    uint8_t data[2];
+    isc::dns::InputBuffer buffer(data, sizeof(data));
+
+    // Avoid possible compiler warnings by only setting uint8_t variables to
+    // uint8_t values.
+    uint8_t i8 = 0;
+    uint8_t j8 = 0;
+    for (int i = 0; i < (2 << 8); ++i, ++i8) {
+        for (int j = 0; j < (2 << 8); ++j, ++j8) {
+            data[0] = i8;
+            data[1] = j8;
+            buffer.setPosition(0);
+            EXPECT_EQ(buffer.readUint16(), readUint16(data));
+        }
+    }
+}
+
+
+TEST(asioutil, writeUint16) {
+
+    // Reference buffer
+    isc::dns::OutputBuffer buffer(2);
+    uint8_t test[2];
+
+    // Avoid possible compiler warnings by only setting uint16_t variables to
+    // uint16_t values.
+    uint16_t i16 = 0;
+    for (uint32_t i = 0; i < (2 << 16); ++i, ++i16) {
+
+        // Write the reference data
+        buffer.clear();
+        buffer.writeUint16(i16);
+
+        // ... and the test data
+        writeUint16(i16, test);
+
+        // ... and compare
+        const uint8_t* ref = static_cast<const uint8_t*>(buffer.getData());
+        EXPECT_EQ(ref[0], test[0]);
+        EXPECT_EQ(ref[1], test[1]);
+    }
+}
diff --git a/src/lib/asiolink/tests/io_fetch_unittest.cc b/src/lib/asiolink/tests/io_fetch_unittest.cc
index 9b74ee0..901df45 100644
--- a/src/lib/asiolink/tests/io_fetch_unittest.cc
+++ b/src/lib/asiolink/tests/io_fetch_unittest.cc
@@ -12,13 +12,17 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-#include <gtest/gtest.h>
-#include <boost/bind.hpp>
+#include <algorithm>
 #include <cstdlib>
 #include <string>
+#include <iostream>
+#include <iomanip>
+#include <iterator>
 #include <vector>
 
-#include <string.h>
+#include <gtest/gtest.h>
+#include <boost/bind.hpp>
+#include <boost/date_time/posix_time/posix_time_types.hpp>
 
 #include <asio.hpp>
 
@@ -30,19 +34,27 @@
 #include <dns/name.h>
 #include <dns/rcode.h>
 
+#include <asiolink/asiolink_utilities.h>
+#include <asiolink/io_address.h>
+#include <asiolink/io_endpoint.h>
 #include <asiolink/io_fetch.h>
 #include <asiolink/io_service.h>
 
 using namespace asio;
 using namespace isc::dns;
-using asio::ip::udp;
+using namespace asio::ip;
+using namespace std;
 
 namespace asiolink {
 
 const asio::ip::address TEST_HOST(asio::ip::address::from_string("127.0.0.1"));
 const uint16_t TEST_PORT(5301);
-// FIXME Shouldn't we send something that is real message?
-const char TEST_DATA[] = "TEST DATA";
+const int SEND_INTERVAL = 250;      // Interval in ms between TCP sends
+const size_t MAX_SIZE = 64 * 1024;  // Should be able to take 64kB
+
+// The tests are complex, so debug output has been left in (although disabled).
+// Set this to true to enable it.
+const bool DEBUG = false;
 
 /// \brief Test fixture for the asiolink::IOFetch.
 class IOFetchTest : public virtual ::testing::Test, public virtual IOFetch::Callback
@@ -52,13 +64,26 @@ public:
     IOFetch::Result expected_;      ///< Expected result of the callback
     bool            run_;           ///< Did the callback run already?
     Question        question_;      ///< What to ask
-    OutputBufferPtr buff_;          ///< Buffer to hold result
+    OutputBufferPtr result_buff_;   ///< Buffer to hold result of fetch
+    OutputBufferPtr msgbuf_;        ///< Buffer corresponding to known question
     IOFetch         udp_fetch_;     ///< For UDP query test
-    //IOFetch         tcp_fetch_;     ///< For TCP query test
-
-    // The next member is the buffer iin which the "server" (implemented by the
-    // response handler method) receives the question sent by the fetch object.
-    std::vector<char>      server_buff_;  ///< Server buffer
+    IOFetch         tcp_fetch_;     ///< For TCP query test
+    IOFetch::Protocol protocol_;    ///< Protocol being tested
+    size_t          cumulative_;    ///< Cumulative data received by "server".
+    deadline_timer  timer_;         ///< Timer to measure timeouts
+
+    // The next member is the buffer in which the "server" (implemented by the
+    // response handler methods in this class) receives the question sent by the
+    // fetch object.
+    uint8_t         receive_buffer_[MAX_SIZE]; ///< Server receive buffer
+    vector<uint8_t> send_buffer_;           ///< Server send buffer
+    uint16_t        send_cumulative_;       ///< Data sent so far
+
+    // Other data.
+    string          return_data_;           ///< Data returned by server
+    string          test_data_;             ///< Large string - here for convenience
+    bool            debug_;                 ///< true to enable debug output
+    size_t          tcp_send_size_;         ///< Max size of TCP send
 
     /// \brief Constructor
     IOFetchTest() :
@@ -66,126 +91,518 @@ public:
         expected_(IOFetch::NOTSET),
         run_(false),
         question_(Name("example.net"), RRClass::IN(), RRType::A()),
-        buff_(new OutputBuffer(512)),
-        udp_fetch_(IPPROTO_UDP, service_, question_, IOAddress(TEST_HOST),
-                   TEST_PORT, buff_, this, 100),
-        server_buff_(512)
-        // tcp_fetch_(service_, question_, IOAddress(TEST_HOST), TEST_PORT,
-        //    buff_, this, 100, IPPROTO_UDP)
-        { }
+        result_buff_(new OutputBuffer(512)),
+        msgbuf_(new OutputBuffer(512)),
+        udp_fetch_(IOFetch::UDP, service_, question_, IOAddress(TEST_HOST),
+            TEST_PORT, result_buff_, this, 100),
+        tcp_fetch_(IOFetch::TCP, service_, question_, IOAddress(TEST_HOST),
+            TEST_PORT, result_buff_, this, (16 * SEND_INTERVAL)),
+                                        // Timeout interval chosen to ensure no timeout
+        protocol_(IOFetch::TCP),        // for initialization - will be changed
+        cumulative_(0),
+        timer_(service_.get_io_service()),
+        receive_buffer_(),
+        send_buffer_(),
+        send_cumulative_(0),
+        return_data_(""),
+        test_data_(""),
+        debug_(DEBUG),
+        tcp_send_size_(0)
+    {
+        // Construct the data buffer for question we expect to receive.
+        Message msg(Message::RENDER);
+        msg.setQid(0);
+        msg.setOpcode(Opcode::QUERY());
+        msg.setRcode(Rcode::NOERROR());
+        msg.setHeaderFlag(Message::HEADERFLAG_RD);
+        msg.addQuestion(question_);
+        MessageRenderer renderer(*msgbuf_);
+        msg.toWire(renderer);
+
+        // Initialize the test data to be returned: tests will return a
+        // substring of this data. (It's convenient to have this as a member of
+        // the class.)
+        //
+        // We could initialize the data with a single character, but as an added
+        // check we'll make ssre that it has some structure.
+
+        test_data_.clear();
+        test_data_.reserve(MAX_SIZE);
+        while (test_data_.size() < MAX_SIZE) {
+            test_data_ += "A message to be returned to the client that has "
+                          "some sort of structure.";
+        }
+    }
+
+    /// \brief UDP Response handler (the "remote UDP DNS server")
+    ///
+    /// When IOFetch is sending data, this response handler emulates the remote
+    /// DNS server.  It checks that the data sent by the IOFetch object is what
+    /// was expected to have been sent, then sends back a known buffer of data.
+    ///
+    /// \param remote Endpoint to which to send the answer
+    /// \param socket Socket to use to send the answer
+    /// \param ec ASIO error code, completion code of asynchronous I/O issued
+    ///        by the "server" to receive data.
+    /// \param length Amount of data received.
+    void udpReceiveHandler(udp::endpoint* remote, udp::socket* socket,
+                    error_code ec = error_code(), size_t length = 0) {
+        if (debug_) {
+            cout << "udpReceiveHandler(): error = " << ec.value() <<
+                    ", length = " << length << endl;
+        }
+
+        // The QID in the incoming data is random so set it to 0 for the
+        // data comparison check. (It is set to 0 in the buffer containing
+        // the expected data.)
+        receive_buffer_[0] = receive_buffer_[1] = 0;
+
+        // Check that length of the received data and the expected data are
+        // identical, then check that the data is identical as well.
+        EXPECT_EQ(msgbuf_->getLength(), length);
+        EXPECT_TRUE(equal(receive_buffer_, (receive_buffer_ + length - 1),
+        static_cast<const uint8_t*>(msgbuf_->getData())));
+
+        // Return a message back to the IOFetch object.
+        socket->send_to(asio::buffer(return_data_.c_str(), return_data_.size()),
+                                     *remote);
+        if (debug_) {
+            cout << "udpReceiveHandler(): returned " << return_data_.size() <<
+                    " bytes to the client" << endl;
+        }
+    }
+
+    /// \brief Completion Handler for accepting TCP data
+    ///
+    /// Called when the remote system connects to the "server".  It issues
+    /// an asynchronous read on the socket to read data.
+    ///
+    /// \param socket Socket on which data will be received
+    /// \param ec Boost error code, value should be zero.
+    void tcpAcceptHandler(tcp::socket* socket, error_code ec = error_code())
+    {
+        if (debug_) {
+            cout << "tcpAcceptHandler(): error = " << ec.value() << endl;
+        }
+
+        // Expect that the accept completed without a problem.
+        EXPECT_EQ(0, ec.value());
+
+        // Work out the maximum size of data we can send over it when we
+        // respond, then subtract 1kB or so for safety.
+        tcp::socket::send_buffer_size send_size;
+        socket->get_option(send_size);
+        if (send_size.value() < (2 * 1024)) {
+            FAIL() << "TCP send size is less than 2kB";
+        } else {
+            tcp_send_size_ = send_size.value() - 1024;
+            if (debug_) {
+                cout << "tcpacceptHandler(): will use send size = " << tcp_send_size_ << endl;
+            }
+        }
+
+        // Initiate a read on the socket.
+        cumulative_ = 0;
+        socket->async_receive(asio::buffer(receive_buffer_, sizeof(receive_buffer_)),
+            boost::bind(&IOFetchTest::tcpReceiveHandler, this, socket, _1, _2));
+    }
+
+    /// \brief Completion handler for receiving TCP data
+    ///
+    /// When IOFetch is sending data, this response handler emulates the remote
+    /// DNS server.  It that all the data sent by the IOFetch object has been
+    /// received, issuing another read if not.  If the data is complete, it is
+    /// compared to what is expected and a reply sent back to the IOFetch.
+    ///
+    /// \param socket Socket to use to send the answer
+    /// \param ec ASIO error code, completion code of asynchronous I/O issued
+    ///        by the "server" to receive data.
+    /// \param length Amount of data received.
+    void tcpReceiveHandler(tcp::socket* socket, error_code ec = error_code(),
+                           size_t length = 0)
+    {
+        if (debug_) {
+            cout << "tcpReceiveHandler(): error = " << ec.value() <<
+                    ", length = " << length << endl;
+        }
+        // Expect that the receive completed without a problem.
+        EXPECT_EQ(0, ec.value());
+
+        // If we haven't received all the data, issue another read.
+        cumulative_ += length;
+        bool complete = false;
+        if (cumulative_ > 2) {
+            uint16_t dns_length = readUint16(receive_buffer_);
+            complete = ((dns_length + 2) == cumulative_);
+        }
+
+        if (!complete) {
+            socket->async_receive(asio::buffer((receive_buffer_ + cumulative_),
+                (sizeof(receive_buffer_) - cumulative_)),
+                boost::bind(&IOFetchTest::tcpReceiveHandler, this, socket, _1, _2));
+            return;
+        }
+
+        // Check that length of the DNS message received is that expected, then
+        // compare buffers, zeroing the QID in the received buffer to match
+        // that set in our expected question.  Note that due to the length
+        // field the QID in the received buffer is in the third and fourth
+        // bytes.
+        EXPECT_EQ(msgbuf_->getLength() + 2, cumulative_);
+        receive_buffer_[2] = receive_buffer_[3] = 0;
+        EXPECT_TRUE(equal((receive_buffer_ + 2), (receive_buffer_ + cumulative_ - 2),
+            static_cast<const uint8_t*>(msgbuf_->getData())));
+
+        // ... and return a message back.  This has to be preceded by a two-byte
+        // count field.
+        send_buffer_.clear();
+        send_buffer_.push_back(0);
+        send_buffer_.push_back(0);
+        writeUint16(return_data_.size(), &send_buffer_[0]);
+        copy(return_data_.begin(), return_data_.end(), back_inserter(send_buffer_));
+
+        // Send the data.  This is done in multiple writes with a delay between
+        // each to check that the reassembly of TCP packets from fragments works.
+        send_cumulative_ = 0;
+        tcpSendData(socket);
+    }
+
+    /// \brief Sent Data Over TCP
+    ///
+    /// Send the TCP data back to the IOFetch object.  The data is sent in
+    /// three chunks - two of 16 bytes and the remainder, with a 250ms gap
+    /// between each. (Amounts of data smaller than one 32 bytes are sent in
+    /// one or two packets.)
+    ///
+    /// \param socket Socket over which send should take place
+    void tcpSendData(tcp::socket* socket) {
+        if (debug_) {
+            cout << "tcpSendData()" << endl;
+        }
+
+        // Decide what to send based on the cumulative count.  At most we'll do
+        // two chunks of 16 bytes (with a 250ms gap between) and then the
+        // remainder.
+        uint8_t* send_ptr = &send_buffer_[send_cumulative_];
+                                    // Pointer to data to send
+        size_t amount = 16;         // Amount of data to send
+        if (send_cumulative_ < (2 * amount)) {
+            
+            // First or second time through, send at most 16 bytes
+            amount = min(amount, (send_buffer_.size() - send_cumulative_));
+
+        } else {
+
+            // For all subsequent times, send the remainder, maximised to
+            // whatever we have chosen for the maximum send size.
+            amount = min(tcp_send_size_,
+                        (send_buffer_.size() - send_cumulative_));
+        }
+        if (debug_) {
+            cout << "tcpSendData(): sending " << amount << " bytes" << endl;
+        }
+
+
+        // ... and send it.  The amount sent is also passed as the first
+        // argument of the send callback, as a check.
+        socket->async_send(asio::buffer(send_ptr, amount),
+                           boost::bind(&IOFetchTest::tcpSendHandler, this,
+                                       amount, socket, _1, _2));
+    }
+
+    /// \brief Completion Handler for Sending TCP data
+    ///
+    /// Called when the asynchronous send of data back to the IOFetch object
+    /// by the TCP "server" in this class has completed.  (This send has to
+    /// be asynchronous because control needs to return to the caller in order
+    /// for the IOService "run()" method to be called to run the handlers.)
+    ///
+    /// If not all the data has been sent, a short delay is instigated (during
+    /// which control returns to the IOService).  This should force the queued
+    /// data to actually be sent and the IOFetch receive handler to be triggered.
+    /// In this way, the ability of IOFetch to handle fragmented TCP packets
+    /// should be checked.
+    ///
+    /// \param expected Number of bytes that were expected to have been sent.
+    /// \param socket Socket over which the send took place.  Only used to
+    ///        pass back to the send method.
+    /// \param ec Boost error code, value should be zero.
+    /// \param length Number of bytes sent.
+    void tcpSendHandler(size_t expected, tcp::socket* socket,
+                        error_code ec = error_code(), size_t length = 0)
+    {
+        if (debug_) {
+            cout << "tcpSendHandler(): error = " << ec.value() <<
+                    ", length = " << length << endl;
+        }
+
+        EXPECT_EQ(0, ec.value());       // Expect no error
+        EXPECT_EQ(expected, length);    // And that amount sent is as expected
+
+        // Do we need to send more?
+        send_cumulative_ += length;
+        if (send_cumulative_ < send_buffer_.size()) {
+
+            // Yes - set up a timer:  the callback handler for the timer is
+            // tcpSendData, which will then send the next chunk.  We pass the
+            // socket over which data should be sent as an argument to that
+            // function.
+            timer_.expires_from_now(boost::posix_time::milliseconds(SEND_INTERVAL));
+            timer_.async_wait(boost::bind(&IOFetchTest::tcpSendData, this,
+                                          socket));
+        }
+    }
 
     /// \brief Fetch completion callback
     ///
     /// This is the callback's operator() method which is called when the fetch
-    /// is complete.  Check that the data received is the wire format of the
-    /// question, then send back an arbitrary response.
+    /// is complete.  It checks that the data received is the wire format of the
+    /// data sent back by the server.
+    ///
+    /// \param result Result indicated by the callback
     void operator()(IOFetch::Result result) {
+        if (debug_) {
+            cout << "operator()(): result = " << result << endl;
+        }
+
         EXPECT_EQ(expected_, result);   // Check correct result returned
         EXPECT_FALSE(run_);             // Check it is run only once
         run_ = true;                    // Note success
-        service_.stop();                // ... and exit run loop
-    }
 
-    /// \brief Response handler, pretending to be remote DNS server
-    ///
-    /// This checks that the data sent is what we expected to receive, and
-    /// sends back a test answer.
-    void respond(udp::endpoint* remote, udp::socket* socket,
-            asio::error_code ec = asio::error_code(), size_t length = 0) {
+        // If the expected result for SUCCESS, then this should have been called
+        // when one of the "servers" in this class has sent back return_data_.
+        // Check the data is as expected/
+        if (expected_ == IOFetch::SUCCESS) {
+            EXPECT_EQ(return_data_.size(), result_buff_->getLength());
 
-        // Construct the data buffer for question we expect to receive.
-        OutputBuffer msgbuf(512);
-        Message msg(Message::RENDER);
-        msg.setQid(0);
-        msg.setOpcode(Opcode::QUERY());
-        msg.setRcode(Rcode::NOERROR());
-        msg.setHeaderFlag(Message::HEADERFLAG_RD);
-        msg.addQuestion(question_);
-        MessageRenderer renderer(msgbuf);
-        msg.toWire(renderer);
+            const uint8_t* start = static_cast<const uint8_t*>(result_buff_->getData());
+            EXPECT_TRUE(equal(return_data_.begin(), return_data_.end(), start));
+        }
 
-        // The QID in the incoming data is random so set it to 0 for the
-        // data comparison check. (It was set to 0 when the buffer containing
-        // the expected data was constructed above.)
-        server_buff_[0] = 0;
-        server_buff_[1] = 0;
+        // ... and cause the run loop to exit.
+        service_.stop();
+    }
 
-        // Check that lengths are identical.
-        EXPECT_EQ(msgbuf.getLength(), length);
-        EXPECT_TRUE(memcmp(msgbuf.getData(), &server_buff_[0], length) == 0);
+    // The next set of methods are the tests themselves.  A number of the TCP
+    // and UDP tests are very similar.
 
-        // ... and return a message back.
-        socket->send_to(asio::buffer(TEST_DATA, sizeof TEST_DATA), *remote);
+    /// \brief Check for stop()
+    ///
+    /// Test that when we run the query and stop it after it was run, it returns
+    /// "stopped" correctly. (That is why stop() is posted to the service_ as
+    /// well instead of calling it.)
+    ///
+    /// \param protocol Test protocol
+    /// \param fetch Fetch object being tested
+    void stopTest(IOFetch::Protocol protocol, IOFetch& fetch) {
+        protocol_ = protocol;
+        expected_ = IOFetch::STOPPED;
+
+        // Post the query
+        service_.get_io_service().post(fetch);
+
+        // Post query_.stop() (yes, the boost::bind thing is just
+        // query_.stop()).
+        service_.get_io_service().post(
+            boost::bind(&IOFetch::stop, fetch, IOFetch::STOPPED));
+
+        // Run both of them.  run() returns when everything in the I/O service
+        // queue has completed.
+        service_.run();
+        EXPECT_TRUE(run_);
     }
-};
 
+    /// \brief Premature stop test
+    ///
+    /// Test that when we queue the query to service_ and call stop() before it
+    /// gets executed, it acts sanely as well (eg. has the same result as
+    /// running stop() after - calls the callback).
+    ///
+    /// \param protocol Test protocol
+    /// \param fetch Fetch object being tested
+    void prematureStopTest(IOFetch::Protocol protocol, IOFetch& fetch) {
+        protocol_ = protocol;
+        expected_ = IOFetch::STOPPED;
+
+        // Stop before it is started
+        fetch.stop();
+        service_.get_io_service().post(fetch);
+
+        service_.run();
+        EXPECT_TRUE(run_);
+    }
 
-/// Test that when we run the query and stop it after it was run,
-/// it returns "stopped" correctly.
-///
-/// That is why stop() is posted to the service_ as well instead
-/// of calling it.
-TEST_F(IOFetchTest, UdpStop) {
-    expected_ = IOFetch::STOPPED;
+    /// \brief Timeout test
+    ///
+    /// Test that fetch times out when no answer arrives.
+    ///
+    /// \param protocol Test protocol
+    /// \param fetch Fetch object being tested
+    void timeoutTest(IOFetch::Protocol protocol, IOFetch& fetch) {
+        protocol_ = protocol;
+        expected_ = IOFetch::TIME_OUT;
+
+        service_.get_io_service().post(fetch);
+        service_.run();
+        EXPECT_TRUE(run_);
+    }
 
-    // Post the query
-    service_.get_io_service().post(udp_fetch_);
+    /// \brief Send/Receive Test
+    ///
+    /// Send a query to the server then receives a response.
+    ///
+    /// \param Test data to return to client
+    void tcpSendReturnTest(const std::string& return_data) {
+        if (debug_) {
+            cout << "tcpSendReturnTest(): data size = " << return_data.size() << endl;
+        }
+        return_data_ = return_data;
+        protocol_ = IOFetch::TCP;
+        expected_ = IOFetch::SUCCESS;
+
+        // Socket into which the connection will be accepted.
+        tcp::socket socket(service_.get_io_service());
+
+        // Acceptor object - called when the connection is made, the handler
+        // will initiate a read on the socket.
+        tcp::acceptor acceptor(service_.get_io_service(),
+                               tcp::endpoint(tcp::v4(), TEST_PORT));
+        acceptor.async_accept(socket,
+            boost::bind(&IOFetchTest::tcpAcceptHandler, this, &socket, _1));
+
+        // Post the TCP fetch object to send the query and receive the response.
+        service_.get_io_service().post(tcp_fetch_);
+
+        // ... and execute all the callbacks.  This exits when the fetch
+        // completes.
+        service_.run();
+        EXPECT_TRUE(run_);  // Make sure the callback did execute
+
+        // Tidy up
+        socket.close();
+    }
+};
 
-    // Post query_.stop() (yes, the boost::bind thing is just
-    // query_.stop()).
-    service_.get_io_service().post(
-       boost::bind(&IOFetch::stop, udp_fetch_, IOFetch::STOPPED));
+// Check the protocol
+TEST_F(IOFetchTest, Protocol) {
+    EXPECT_EQ(IOFetch::UDP, udp_fetch_.getProtocol());
+    EXPECT_EQ(IOFetch::TCP, tcp_fetch_.getProtocol());
+}
 
-    // Run both of them.  run() returns when everything in the I/O service
-    // queue has completed.
-    service_.run();
-    EXPECT_TRUE(run_);
+// UDP Stop test - see IOFetchTest::stopTest() header.
+TEST_F(IOFetchTest, UdpStop) {
+    stopTest(IOFetch::UDP, udp_fetch_);
 }
 
-// Test that when we queue the query to service_ and call stop() before it gets
-// executed, it acts sanely as well (eg. has the same result as running stop()
-// after - calls the callback).
+// UDP premature stop test - see IOFetchTest::prematureStopTest() header.
 TEST_F(IOFetchTest, UdpPrematureStop) {
-    expected_ = IOFetch::STOPPED;
-
-    // Stop before it is started
-    udp_fetch_.stop();
-    service_.get_io_service().post(udp_fetch_);
-
-    service_.run();
-    EXPECT_TRUE(run_);
+    prematureStopTest(IOFetch::UDP, udp_fetch_);
 }
 
-// Test that it will timeout when no answer arrives.
+// UDP premature stop test - see IOFetchTest::timeoutTest() header.
 TEST_F(IOFetchTest, UdpTimeout) {
-    expected_ = IOFetch::TIME_OUT;
-
-    service_.get_io_service().post(udp_fetch_);
-    service_.run();
-    EXPECT_TRUE(run_);
+    timeoutTest(IOFetch::UDP, udp_fetch_);
 }
 
-// Test that it will succeed when we fake an answer and stores the same data we
-// send.  This is done through a real socket on the loopback address.
-TEST_F(IOFetchTest, UdpReceive) {
+// UDP SendReceive test.  Set up a UDP server then ports a UDP fetch object.
+// This will send question_ to the server and receive the answer back from it.
+TEST_F(IOFetchTest, UdpSendReceive) {
+    protocol_ = IOFetch::UDP;
     expected_ = IOFetch::SUCCESS;
 
+    // Set up the server.
     udp::socket socket(service_.get_io_service(), udp::v4());
     socket.set_option(socket_base::reuse_address(true));
     socket.bind(udp::endpoint(TEST_HOST, TEST_PORT));
+    return_data_ = "Message returned to the client";
 
     udp::endpoint remote;
-    socket.async_receive_from(asio::buffer(server_buff_),
+    socket.async_receive_from(asio::buffer(receive_buffer_, sizeof(receive_buffer_)),
         remote,
-        boost::bind(&IOFetchTest::respond, this, &remote, &socket, _1, _2));
+        boost::bind(&IOFetchTest::udpReceiveHandler, this, &remote, &socket,
+                    _1, _2));
     service_.get_io_service().post(udp_fetch_);
+    if (debug_) {
+        cout << "udpSendReceive: async_receive_from posted, waiting for callback" <<
+                endl;
+    }
     service_.run();
 
     socket.close();
 
-    EXPECT_TRUE(run_);
-    ASSERT_EQ(sizeof TEST_DATA, buff_->getLength());
-    EXPECT_EQ(0, memcmp(TEST_DATA, buff_->getData(), sizeof TEST_DATA));
+    EXPECT_TRUE(run_);;
+}
+
+// Do the same tests for TCP transport
+
+TEST_F(IOFetchTest, TcpStop) {
+    stopTest(IOFetch::TCP, tcp_fetch_);
+}
+
+TEST_F(IOFetchTest, TcpPrematureStop) {
+    prematureStopTest(IOFetch::TCP, tcp_fetch_);
+}
+
+TEST_F(IOFetchTest, TcpTimeout) {
+    timeoutTest(IOFetch::TCP, tcp_fetch_);
+}
+
+// Test with values at or near 0, then at or near the chunk size (16 and 32
+// bytes, the sizes of the first two packets) then up to 65535.  These are done
+// in separate tests because in practice a new IOFetch is created for each
+// query/response exchange and we don't want to confuse matters in the test
+// by running the test with an IOFetch that has already done one exchange.
+
+TEST_F(IOFetchTest, TcpSendReceive0) {
+    tcpSendReturnTest(test_data_.substr(0, 0));
+}
+
+TEST_F(IOFetchTest, TcpSendReceive1) {
+    tcpSendReturnTest(test_data_.substr(0, 1));
+}
+
+TEST_F(IOFetchTest, TcpSendReceive15) {
+    tcpSendReturnTest(test_data_.substr(0, 15));
+}
+
+TEST_F(IOFetchTest, TcpSendReceive16) {
+    tcpSendReturnTest(test_data_.substr(0, 16));
+}
+
+TEST_F(IOFetchTest, TcpSendReceive17) {
+    tcpSendReturnTest(test_data_.substr(0, 17));
+}
+
+TEST_F(IOFetchTest, TcpSendReceive31) {
+    tcpSendReturnTest(test_data_.substr(0, 31));
+}
+
+TEST_F(IOFetchTest, TcpSendReceive32) {
+    tcpSendReturnTest(test_data_.substr(0, 32));
+}
+
+TEST_F(IOFetchTest, TcpSendReceive33) {
+    tcpSendReturnTest(test_data_.substr(0, 33));
+}
+
+TEST_F(IOFetchTest, TcpSendReceive4096) {
+    tcpSendReturnTest(test_data_.substr(0, 4096));
+}
+
+TEST_F(IOFetchTest, TcpSendReceive8192) {
+    tcpSendReturnTest(test_data_.substr(0, 8192));
+}
+
+TEST_F(IOFetchTest, TcpSendReceive16384) {
+    tcpSendReturnTest(test_data_.substr(0, 16384));
+}
+
+TEST_F(IOFetchTest, TcpSendReceive32768) {
+    tcpSendReturnTest(test_data_.substr(0, 32768));
+}
+
+TEST_F(IOFetchTest, TcpSendReceive65535) {
+    tcpSendReturnTest(test_data_.substr(0, 65535));
 }
 
 } // namespace asiolink
diff --git a/src/lib/asiolink/tests/qid_gen_unittest.cc b/src/lib/asiolink/tests/qid_gen_unittest.cc
new file mode 100644
index 0000000..3ad8a03
--- /dev/null
+++ b/src/lib/asiolink/tests/qid_gen_unittest.cc
@@ -0,0 +1,59 @@
+// 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.
+
+// 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 Test of QidGenerator
+///
+
+#include <gtest/gtest.h>
+
+#include <asiolink/qid_gen.h>
+#include <dns/message.h>
+
+// Tests the operation of the Qid generator
+
+// Check that getInstance returns a singleton
+TEST(QidGenerator, singleton) {
+    asiolink::QidGenerator& g1 = asiolink::QidGenerator::getInstance();
+    asiolink::QidGenerator& g2 = asiolink::QidGenerator::getInstance();
+
+    EXPECT_TRUE(&g1 == &g2);
+}
+
+TEST(QidGenerator, generate) {
+    // We'll assume that boost's generator is 'good enough', and won't
+    // do full statistical checking here. Let's just call it the xkcd
+    // test (http://xkcd.com/221/), and check if three consecutive
+    // generates are not all the same.
+    isc::dns::qid_t one, two, three;
+    asiolink::QidGenerator& gen = asiolink::QidGenerator::getInstance();
+    one = gen.generateQid();
+    two = gen.generateQid();
+    three = gen.generateQid();
+    ASSERT_FALSE((one == two) && (one == three));
+}
diff --git a/src/lib/asiolink/tests/recursive_query_unittest.cc b/src/lib/asiolink/tests/recursive_query_unittest.cc
deleted file mode 100644
index f4fc2ac..0000000
--- a/src/lib/asiolink/tests/recursive_query_unittest.cc
+++ /dev/null
@@ -1,796 +0,0 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <config.h>
-
-#include <sys/socket.h>
-#include <sys/time.h>
-
-#include <string.h>
-
-#include <boost/lexical_cast.hpp>
-#include <boost/bind.hpp>
-#include <boost/date_time/posix_time/posix_time_types.hpp>
-
-#include <gtest/gtest.h>
-
-#include <exceptions/exceptions.h>
-
-#include <dns/tests/unittest_util.h>
-#include <dns/rcode.h>
-
-#include <dns/buffer.h>
-#include <dns/message.h>
-
-// IMPORTANT: We shouldn't directly use ASIO definitions in this test.
-// In particular, we must not include asio.hpp in this file.
-// The asiolink module is primarily intended to be a wrapper that hide the
-// details of the underlying implementations.  We need to test the wrapper
-// level behaviors.  In addition, some compilers reject to compile this file
-// if we include asio.hpp unless we specify a special compiler option.
-// If we need to test something at the level of underlying ASIO and need
-// their definition, that test should go to asiolink/internal/tests.
-#include <asiolink/recursive_query.h>
-#include <asiolink/io_socket.h>
-#include <asiolink/io_service.h>
-#include <asiolink/io_message.h>
-#include <asiolink/io_error.h>
-#include <asiolink/dns_lookup.h>
-#include <asiolink/simple_callback.h>
-
-using isc::UnitTestUtil;
-using namespace std;
-using namespace asiolink;
-using namespace isc::dns;
-
-namespace {
-const char* const TEST_SERVER_PORT = "53535";
-const char* const TEST_CLIENT_PORT = "53536";
-const char* const TEST_IPV6_ADDR = "::1";
-const char* const TEST_IPV4_ADDR = "127.0.0.1";
-// This data is intended to be valid as a DNS/TCP-like message: the first
-// two octets encode the length of the rest of the data.  This is crucial
-// for the tests below.
-const uint8_t test_data[] = {0, 4, 1, 2, 3, 4};
-
-// This function returns an addrinfo structure for use by tests, using
-// different addresses and ports depending on whether we're testing
-// IPv4 or v6, TCP or UDP, and client or server operation.
-struct addrinfo*
-resolveAddress(const int family, const int protocol, const bool client) {
-    const char* const addr = (family == AF_INET6) ?
-        TEST_IPV6_ADDR : TEST_IPV4_ADDR;
-    const char* const port = client ? TEST_CLIENT_PORT : TEST_SERVER_PORT;
-
-    struct addrinfo hints;
-    memset(&hints, 0, sizeof(hints));
-    hints.ai_family = family;
-    hints.ai_socktype = (protocol == IPPROTO_UDP) ? SOCK_DGRAM : SOCK_STREAM;
-    hints.ai_protocol = protocol;
-    hints.ai_flags = AI_NUMERICSERV;
-
-    struct addrinfo* res;
-    const int error = getaddrinfo(addr, port, &hints, &res);
-    if (error != 0) {
-        isc_throw(IOError, "getaddrinfo failed: " << gai_strerror(error));
-    }
-
-    return (res);
-}
-
-// This fixture is a framework for various types of network operations
-// using the ASIO interfaces.  Each test case creates an IOService object,
-// opens a local "client" socket for testing, sends data via the local socket
-// to the service that would run in the IOService object.
-// A mock callback function (an ASIOCallBack object) is registered with the
-// IOService object, so the test code should be able to examine the data
-// received on the server side.  It then checks the received data matches
-// expected parameters.
-// If initialization parameters of the IOService should be modified, the test
-// case can do it using the setDNSService() method.
-// Note: the set of tests in RecursiveQueryTest use actual network services and may
-// involve undesirable side effects such as blocking.
-class RecursiveQueryTest : public ::testing::Test {
-protected:
-    RecursiveQueryTest();
-    ~RecursiveQueryTest() {
-        if (res_ != NULL) {
-            freeaddrinfo(res_);
-        }
-        if (sock_ != -1) {
-            close(sock_);
-        }
-        delete dns_service_;
-        delete callback_;
-        delete io_service_;
-    }
-
-    // Send a test UDP packet to a mock server
-    void sendUDP(const int family) {
-        res_ = resolveAddress(family, IPPROTO_UDP, false);
-
-        sock_ = socket(res_->ai_family, res_->ai_socktype, res_->ai_protocol);
-        if (sock_ < 0) {
-            isc_throw(IOError, "failed to open test socket");
-        }
-        const int cc = sendto(sock_, test_data, sizeof(test_data), 0,
-                              res_->ai_addr, res_->ai_addrlen);
-        if (cc != sizeof(test_data)) {
-            isc_throw(IOError, "unexpected sendto result: " << cc);
-        }
-        io_service_->run();
-    }
-
-    // Send a test TCP packet to a mock server
-    void sendTCP(const int family) {
-        res_ = resolveAddress(family, IPPROTO_TCP, false);
-
-        sock_ = socket(res_->ai_family, res_->ai_socktype, res_->ai_protocol);
-        if (sock_ < 0) {
-            isc_throw(IOError, "failed to open test socket");
-        }
-        if (connect(sock_, res_->ai_addr, res_->ai_addrlen) < 0) {
-            isc_throw(IOError, "failed to connect to the test server");
-        }
-        const int cc = send(sock_, test_data, sizeof(test_data), 0);
-        if (cc != sizeof(test_data)) {
-            isc_throw(IOError, "unexpected send result: " << cc);
-        }
-        io_service_->run();
-    }
-
-    // Receive a UDP packet from a mock server; used for testing
-    // recursive lookup.  The caller must place a RecursiveQuery 
-    // on the IO Service queue before running this routine.
-    void recvUDP(const int family, void* buffer, size_t& size) {
-        res_ = resolveAddress(family, IPPROTO_UDP, true);
-
-        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, "bind failed: " << strerror(errno));
-        }
-
-        // The IO service queue should have a RecursiveQuery object scheduled
-        // to run at this point.  This call will cause it to begin an
-        // async send, then return.
-        io_service_->run_one();
-
-        // ... and this one will block until the send has completed
-        io_service_->run_one();
-
-        // Now we attempt to recv() whatever was sent.
-        // XXX: there's no guarantee the receiving socket can immediately get
-        // the packet.  Normally we can perform blocking recv to wait for it,
-        // but in theory it's even possible that the packet is lost.
-        // In order to prevent the test from hanging in such a worst case
-        // we add an ad hoc timeout.
-        const struct timeval timeo = { 10, 0 };
-        int recv_options = 0;
-        if (setsockopt(sock_, SOL_SOCKET, SO_RCVTIMEO, &timeo,
-                       sizeof(timeo))) {
-            if (errno == ENOPROTOOPT) {
-                // Workaround for Solaris: it doesn't accept SO_RCVTIMEO
-                // with the error of ENOPROTOOPT.  Since this is a workaround
-                // for rare error cases anyway, we simply switch to the
-                // "don't wait" mode.  If we still find an error in recv()
-                // can happen often we'll consider a more complete solution.
-                recv_options = MSG_DONTWAIT;
-            } else {
-                isc_throw(IOError, "set RCVTIMEO failed: " << strerror(errno));
-            }
-        }
-        const int ret = recv(sock_, buffer, size, recv_options);
-        if (ret < 0) {
-            isc_throw(IOError, "recvfrom failed: " << strerror(errno));
-        }
-        
-        // Pass the message size back via the size parameter
-        size = ret;
-    }
-
-
-    // Set up an IO Service queue using the specified address
-    void setDNSService(const char& address) {
-        delete dns_service_;
-        dns_service_ = NULL;
-        delete io_service_;
-        io_service_ = new IOService();
-        callback_ = new ASIOCallBack(this);
-        dns_service_ = new DNSService(*io_service_, *TEST_SERVER_PORT, address, callback_, NULL, NULL);
-    }
-
-    // Set up an IO Service queue using the "any" address, on IPv4 if
-    // 'use_ipv4' is true and on IPv6 if 'use_ipv6' is true.
-    void setDNSService(const bool use_ipv4, const bool use_ipv6) {
-        delete dns_service_;
-        dns_service_ = NULL;
-        delete io_service_;
-        io_service_ = new IOService();
-        callback_ = new ASIOCallBack(this);
-        dns_service_ = new DNSService(*io_service_, *TEST_SERVER_PORT, use_ipv4, use_ipv6, callback_,
-                                      NULL, NULL);
-    }
-
-    // Set up empty DNS Service
-    // Set up an IO Service queue without any addresses
-    void setDNSService() {
-        delete dns_service_;
-        dns_service_ = NULL;
-        delete io_service_;
-        io_service_ = new IOService();
-        callback_ = new ASIOCallBack(this);
-        dns_service_ = new DNSService(*io_service_, callback_, NULL, NULL);
-    }
-
-    // Run a simple server test, on either IPv4 or IPv6, and over either
-    // UDP or TCP.  Calls the sendUDP() or sendTCP() methods, which will
-    // start the IO Service queue.  The UDPServer or TCPServer that was
-    // created by setIOService() will receive the test packet and issue a
-    // callback, which enables us to check that the data it received
-    // matches what we sent.
-    void doTest(const int family, const int protocol) {
-        if (protocol == IPPROTO_UDP) {
-            sendUDP(family);
-        } else {
-            sendTCP(family);
-        }
-
-        // There doesn't seem to be an effective test for the validity of
-        // 'native'.
-        // One thing we are sure is it must be different from our local socket.
-        EXPECT_NE(sock_, callback_native_);
-        EXPECT_EQ(protocol, callback_protocol_);
-        EXPECT_EQ(family == AF_INET6 ? TEST_IPV6_ADDR : TEST_IPV4_ADDR,
-                  callback_address_);
-
-        const uint8_t* expected_data =
-            protocol == IPPROTO_UDP ? test_data : test_data + 2;
-        const size_t expected_datasize =
-            protocol == IPPROTO_UDP ? sizeof(test_data) :
-            sizeof(test_data) - 2;
-        EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, &callback_data_[0],
-                            callback_data_.size(),
-                            expected_data, expected_datasize);
-    }
-
-protected:
-    // This is a nonfunctional mockup of a DNSServer object.  Its purpose
-    // is to resume after a recursive query or other asynchronous call
-    // has completed.
-    class MockServer : public DNSServer {
-    public:
-        explicit MockServer(IOService& io_service,
-                            SimpleCallback* checkin = NULL,
-                            DNSLookup* lookup = NULL,
-                            DNSAnswer* answer = NULL) :
-            io_(io_service),
-            done_(false),
-            message_(new Message(Message::PARSE)),
-            answer_message_(new Message(Message::RENDER)),
-            respbuf_(new OutputBuffer(0)),
-            checkin_(checkin), lookup_(lookup), answer_(answer)
-        {}
-
-        void operator()(asio::error_code ec = asio::error_code(),
-                        size_t length = 0)
-        {}
-
-        void resume(const bool) {
-          // should never be called in our tests
-        }
-
-        DNSServer* clone() {
-            MockServer* s = new MockServer(*this);
-            return (s);
-        }
-
-        inline void asyncLookup() {
-            if (lookup_) {
-                (*lookup_)(*io_message_, message_, answer_message_,
-                           respbuf_, this);
-            }
-        }
-
-    protected:
-        IOService& io_;
-        bool done_;
-
-    private:
-        // Currently unused; these will be used for testing
-        // 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
-        const SimpleCallback* checkin_;
-        const DNSLookup* lookup_;
-        const DNSAnswer* answer_;
-    };
-
-    // This version of mock server just stops the io_service when it is resumed
-    class MockServerStop : public MockServer {
-        public:
-            explicit MockServerStop(IOService& io_service, bool* done) :
-                MockServer(io_service),
-                done_(done)
-            {}
-
-            void resume(const bool done) {
-                *done_ = done;
-                io_.stop();
-            }
-
-            DNSServer* clone() {
-                return (new MockServerStop(*this));
-            }
-        private:
-            bool* done_;
-    };
-
-    // This version of mock server just stops the io_service when it is resumed
-    // the second time. (Used in the clientTimeout test, where resume
-    // is called initially with the error answer, and later when the
-    // lookup times out, it is called without an answer to send back)
-    class MockServerStop2 : public MockServer {
-        public:
-            explicit MockServerStop2(IOService& io_service,
-                                     bool* done1, bool* done2) :
-                MockServer(io_service),
-                done1_(done1),
-                done2_(done2),
-                stopped_once_(false)
-            {}
-
-            void resume(const bool done) {
-                if (stopped_once_) {
-                    *done2_ = done;
-                    io_.stop();
-                } else {
-                    *done1_ = done;
-                    stopped_once_ = true;
-                }
-            }
-
-            DNSServer* clone() {
-                return (new MockServerStop2(*this));
-            }
-        private:
-            bool* done1_;
-            bool* done2_;
-            bool stopped_once_;
-    };
-
-private:
-    class ASIOCallBack : public SimpleCallback {
-    public:
-        ASIOCallBack(RecursiveQueryTest* test_obj) : test_obj_(test_obj) {}
-        void operator()(const IOMessage& io_message) const {
-            test_obj_->callBack(io_message);
-        }
-    private:
-        RecursiveQueryTest* test_obj_;
-    };
-    void callBack(const IOMessage& io_message) {
-        callback_protocol_ = io_message.getSocket().getProtocol();
-        callback_native_ = io_message.getSocket().getNative();
-        callback_address_ =
-            io_message.getRemoteEndpoint().getAddress().toText();
-        callback_data_.assign(
-            static_cast<const uint8_t*>(io_message.getData()),
-            static_cast<const uint8_t*>(io_message.getData()) +
-            io_message.getDataSize());
-        io_service_->stop();
-    }
-protected:
-    // We use a pointer for io_service_, because for some tests we
-    // need to recreate a new one within one onstance of this class
-    IOService* io_service_;
-    DNSService* dns_service_;
-    ASIOCallBack* callback_;
-    int callback_protocol_;
-    int callback_native_;
-    string callback_address_;
-    vector<uint8_t> callback_data_;
-    int sock_;
-    struct addrinfo* res_;
-};
-
-RecursiveQueryTest::RecursiveQueryTest() :
-    dns_service_(NULL), callback_(NULL), callback_protocol_(0),
-    callback_native_(-1), sock_(-1), res_(NULL)
-{
-    io_service_ = new IOService();
-    setDNSService(true, true);
-}
-
-TEST_F(RecursiveQueryTest, v6UDPSend) {
-    doTest(AF_INET6, IPPROTO_UDP);
-}
-
-TEST_F(RecursiveQueryTest, v6TCPSend) {
-    doTest(AF_INET6, IPPROTO_TCP);
-}
-
-TEST_F(RecursiveQueryTest, v4UDPSend) {
-    doTest(AF_INET, IPPROTO_UDP);
-}
-
-TEST_F(RecursiveQueryTest, v4TCPSend) {
-    doTest(AF_INET, IPPROTO_TCP);
-}
-
-TEST_F(RecursiveQueryTest, v6UDPSendSpecific) {
-    // Explicitly set a specific address to be bound to the socket.
-    // The subsequent test does not directly ensures the underlying socket
-    // is bound to the expected address, but the success of the tests should
-    // reasonably suggest it works as intended.
-    // Specifying an address also implicitly means the service runs in a
-    // single address-family mode.  In tests using TCP we can confirm that
-    // by trying to make a connection and seeing a failure.  In UDP, it'd be
-    // more complicated because we need to use a connected socket and catch
-    // an error on a subsequent read operation.  We could do it, but for
-    // simplicity we only tests the easier cases for now.
-
-    setDNSService(*TEST_IPV6_ADDR);
-    doTest(AF_INET6, IPPROTO_UDP);
-}
-
-TEST_F(RecursiveQueryTest, v6TCPSendSpecific) {
-    setDNSService(*TEST_IPV6_ADDR);
-    doTest(AF_INET6, IPPROTO_TCP);
-
-    EXPECT_THROW(sendTCP(AF_INET), IOError);
-}
-
-TEST_F(RecursiveQueryTest, v4UDPSendSpecific) {
-    setDNSService(*TEST_IPV4_ADDR);
-    doTest(AF_INET, IPPROTO_UDP);
-}
-
-TEST_F(RecursiveQueryTest, v4TCPSendSpecific) {
-    setDNSService(*TEST_IPV4_ADDR);
-    doTest(AF_INET, IPPROTO_TCP);
-
-    EXPECT_THROW(sendTCP(AF_INET6), IOError);
-}
-
-TEST_F(RecursiveQueryTest, v6AddServer) {
-    setDNSService();
-    dns_service_->addServer(*TEST_SERVER_PORT, TEST_IPV6_ADDR);
-    doTest(AF_INET6, IPPROTO_TCP);
-
-    EXPECT_THROW(sendTCP(AF_INET), IOError);
-}
-
-TEST_F(RecursiveQueryTest, v4AddServer) {
-    setDNSService();
-    dns_service_->addServer(*TEST_SERVER_PORT, TEST_IPV4_ADDR);
-    doTest(AF_INET, IPPROTO_TCP);
-
-    EXPECT_THROW(sendTCP(AF_INET6), IOError);
-}
-
-TEST_F(RecursiveQueryTest, clearServers) {
-    setDNSService();
-    dns_service_->clearServers();
-
-    EXPECT_THROW(sendTCP(AF_INET), IOError);
-    EXPECT_THROW(sendTCP(AF_INET6), IOError);
-}
-
-TEST_F(RecursiveQueryTest, v6TCPOnly) {
-    // Open only IPv6 TCP socket.  A subsequent attempt of establishing an
-    // IPv4/TCP connection should fail.  See above for why we only test this
-    // for TCP.
-    setDNSService(false, true);
-    EXPECT_THROW(sendTCP(AF_INET), IOError);
-}
-
-TEST_F(RecursiveQueryTest, v4TCPOnly) {
-    setDNSService(true, false);
-    EXPECT_THROW(sendTCP(AF_INET6), IOError);
-}
-
-vector<pair<string, uint16_t> >
-singleAddress(const string &address, uint16_t port) {
-    vector<pair<string, uint16_t> > result;
-    result.push_back(pair<string, uint16_t>(address, port));
-    return (result);
-}
-
-TEST_F(RecursiveQueryTest, recursiveSetupV4) {
-    setDNSService(true, false);
-    uint16_t port = boost::lexical_cast<uint16_t>(TEST_CLIENT_PORT);
-    EXPECT_NO_THROW(RecursiveQuery(*dns_service_,
-                                   singleAddress(TEST_IPV4_ADDR, port),
-                                   singleAddress(TEST_IPV4_ADDR, port)));
-}
-
-TEST_F(RecursiveQueryTest, 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),
-                                   singleAddress(TEST_IPV6_ADDR,port)));
-}
-
-// XXX:
-// This is very inadequate unit testing.  It should be generalized into
-// 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(RecursiveQueryTest, forwarderSend) {
-    setDNSService(true, false);
-
-    // Note: We use the test prot plus one to ensure we aren't binding
-    // to the same port as the actual server
-    uint16_t port = boost::lexical_cast<uint16_t>(TEST_CLIENT_PORT);
-
-    MockServer server(*io_service_);
-    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));
-    MessagePtr answer(new Message(Message::RENDER));
-    rq.resolve(q, answer, buffer, &server);
-
-    char data[4096];
-    size_t size = sizeof(data);
-    ASSERT_NO_THROW(recvUDP(AF_INET, data, size));
-
-    Message m(Message::PARSE);
-    InputBuffer ibuf(data, size);
-
-    // Make sure we can parse the message that was sent
-    EXPECT_NO_THROW(m.parseHeader(ibuf));
-    EXPECT_NO_THROW(m.fromWire(ibuf));
-
-    // Check that the question sent matches the one we wanted
-    QuestionPtr q2 = *m.beginQuestion();
-    EXPECT_EQ(q.getName(), q2->getName());
-    EXPECT_EQ(q.getType(), q2->getType());
-    EXPECT_EQ(q.getClass(), q2->getClass());
-}
-
-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 RecursiveQueryTest::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(RecursiveQueryTest, 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);
-    MockServerStop server(*io_service_, &done);
-
-    // Do the answer
-    const uint16_t port = boost::lexical_cast<uint16_t>(TEST_CLIENT_PORT);
-    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));
-    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()).
-    int recv_options = setSocketTimeout(sock_, 10, 0);
-    int num = 0;
-    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(RecursiveQueryTest, forwardClientTimeout) {
-    // Prepare the service (we do not use the common setup, we do not answer
-    setDNSService();
-
-    sock_ = createTestSocket();
-
-    // Prepare the server
-    bool done1(true);
-    bool done2(true);
-    MockServerStop2 server(*io_service_, &done1, &done2);
-
-    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
-    // four times
-    RecursiveQuery query(*dns_service_,
-                         singleAddress(TEST_IPV4_ADDR, port),
-                         singleAddress(TEST_IPV4_ADDR, port),
-                         200, 480, 4000, 4);
-    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
-    int num = 0;
-    bool read_success = tryRead(sock_, recv_options, 5, &num);
-
-    // The query should fail, but we should have kept on trying
-    EXPECT_TRUE(done1);
-    EXPECT_FALSE(done2);
-    EXPECT_EQ(5, num);
-    EXPECT_TRUE(read_success);
-}
-
-// If we set lookup timeout to lower than querytimeout*retries, we should
-// fail before the full amount of retries
-TEST_F(RecursiveQueryTest, 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),
-                         200, 4000, 480, 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(RecursiveQueryTest, 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(RecursiveQueryTest, 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));
-}
-
-}
diff --git a/src/lib/asiolink/tests/run_unittests.cc b/src/lib/asiolink/tests/run_unittests.cc
index b481784..c285f9e 100644
--- a/src/lib/asiolink/tests/run_unittests.cc
+++ b/src/lib/asiolink/tests/run_unittests.cc
@@ -14,13 +14,15 @@
 
 #include <gtest/gtest.h>
 
+#include <log/root_logger_name.h>
 #include <dns/tests/unittest_util.h>
 
 int
 main(int argc, char* argv[])
 {
-    ::testing::InitGoogleTest(&argc, argv);
-    isc::UnitTestUtil::addDataPath(TEST_DATA_DIR);
+    ::testing::InitGoogleTest(&argc, argv);         // Initialize Google test
+    isc::log::setRootLoggerName("unittest");        // Set a root logger name
+    isc::UnitTestUtil::addDataPath(TEST_DATA_DIR);  // Add location of test data
 
     return (RUN_ALL_TESTS());
 }
diff --git a/src/lib/asiolink/tests/tcp_endpoint_unittest.cc b/src/lib/asiolink/tests/tcp_endpoint_unittest.cc
new file mode 100644
index 0000000..3787e1c
--- /dev/null
+++ b/src/lib/asiolink/tests/tcp_endpoint_unittest.cc
@@ -0,0 +1,55 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include <asio.hpp>
+#include <asiolink/io_address.h>
+#include <asiolink/tcp_endpoint.h>
+
+using namespace asiolink;
+using namespace std;
+
+// This test checks that the endpoint can manage its own internal
+// asio::ip::tcp::endpoint object.
+
+TEST(TCPEndpointTest, v4Address) {
+    const string test_address("192.0.2.1");
+    const unsigned short test_port = 5301;
+
+    IOAddress address(test_address);
+    TCPEndpoint endpoint(address, test_port);
+
+    EXPECT_TRUE(address == endpoint.getAddress());
+    EXPECT_EQ(test_port, endpoint.getPort());
+    EXPECT_EQ(IPPROTO_TCP, endpoint.getProtocol());
+    EXPECT_EQ(AF_INET, endpoint.getFamily());
+}
+
+TEST(TCPEndpointTest, v6Address) {
+    const string test_address("2001:db8::1235");
+    const unsigned short test_port = 5302;
+
+    IOAddress address(test_address);
+    TCPEndpoint endpoint(address, test_port);
+
+    EXPECT_TRUE(address == endpoint.getAddress());
+    EXPECT_EQ(test_port, endpoint.getPort());
+    EXPECT_EQ(IPPROTO_TCP, endpoint.getProtocol());
+    EXPECT_EQ(AF_INET6, endpoint.getFamily());
+}
diff --git a/src/lib/asiolink/tests/tcp_socket_unittest.cc b/src/lib/asiolink/tests/tcp_socket_unittest.cc
new file mode 100644
index 0000000..f0a45ee
--- /dev/null
+++ b/src/lib/asiolink/tests/tcp_socket_unittest.cc
@@ -0,0 +1,515 @@
+// 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 Test of TCPSocket
+///
+/// Tests the fuctionality of a TCPSocket by working through an open-send-
+/// receive-close sequence and checking that the asynchronous notifications
+/// work.
+
+#include <string>
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <algorithm>
+#include <cstdlib>
+#include <cstddef>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <boost/bind.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <dns/buffer.h>
+
+#include <asio.hpp>
+
+#include <asiolink/asiolink_utilities.h>
+#include <asiolink/io_service.h>
+#include <asiolink/tcp_endpoint.h>
+#include <asiolink/tcp_socket.h>
+
+using namespace asio;
+using namespace asio::ip;
+using namespace asiolink;
+using namespace isc::dns;
+using namespace std;
+
+namespace {
+
+const char SERVER_ADDRESS[] = "127.0.0.1";
+const unsigned short SERVER_PORT = 5303;
+
+// TODO: Shouldn't we send something that is real message?
+const char OUTBOUND_DATA[] = "Data sent from client to server";
+const char INBOUND_DATA[] = "Returned data from server to client";
+}
+
+/// An instance of this object is passed to the asynchronous I/O functions
+/// and the operator() method is called when when an asynchronous I/O completes.
+/// The arguments to the completion callback are stored for later retrieval.
+class TCPCallback {
+public:
+    /// \brief Operations the server is doing
+    enum Operation {
+        ACCEPT = 0,     ///< accept() was issued
+        OPEN = 1,       /// Client connected to server
+        READ = 2,       ///< Asynchronous read completed
+        WRITE = 3,      ///< Asynchronous write completed
+        NONE = 4        ///< "Not set" state
+    };
+
+    /// \brief Minimim size of buffers
+    enum {
+        MIN_SIZE = (64 * 1024 + 2)          ///< 64kB + two bytes for a count
+    };
+
+    struct PrivateData {
+        PrivateData() :
+            error_code_(), length_(0), cumulative_(0), expected_(0), offset_(0),
+            name_(""), queued_(NONE), called_(NONE)
+        {}
+
+        asio::error_code    error_code_;    ///< Completion error code
+        size_t              length_;        ///< Bytes transferred in this I/O
+        size_t              cumulative_;    ///< Cumulative bytes transferred
+        size_t              expected_;      ///< Expected amount of data
+        size_t              offset_;        ///< Where to put data in buffer
+        std::string         name_;          ///< Which of the objects this is
+        Operation           queued_;        ///< Queued operation
+        Operation           called_;        ///< Which callback called
+        uint8_t             data_[MIN_SIZE];  ///< Receive buffer
+
+    };
+
+    /// \brief Constructor
+    ///
+    /// Constructs the object.  It also creates the data member pointed to by
+    /// a shared pointer.  When used as a callback object, this is copied as it
+    /// is passed into the asynchronous function.  This means that there are two
+    /// objects and inspecting the one we passed in does not tell us anything.
+    ///
+    /// Therefore we use a boost::shared_ptr.  When the object is copied, the
+    /// shared pointer is copied, which leaves both objects pointing to the same
+    /// data.
+    ///
+    /// \param which Which of the two callback objects this is
+    TCPCallback(std::string which) : ptr_(new PrivateData())
+    {
+        ptr_->name_ = which;
+    }
+
+    /// \brief Destructor
+    ///
+    /// No code needed, destroying the shared pointer destroys the private data.
+    virtual ~TCPCallback()
+    {}
+
+    /// \brief Client Callback Function
+    ///
+    /// Called when an asynchronous operation is completed by the client, this
+    /// stores the origin of the operation in the client_called_ data member.
+    ///
+    /// \param ec I/O completion error code passed to callback function.
+    /// \param length Number of bytes transferred
+    void operator()(asio::error_code ec = asio::error_code(),
+                            size_t length = 0)
+    {
+        setCode(ec.value());
+        ptr_->called_ = ptr_->queued_;
+        ptr_->length_ = length;
+    }
+
+    /// \brief Get I/O completion error code
+    int getCode() {
+        return (ptr_->error_code_.value());
+    }
+
+    /// \brief Set I/O completion code
+    ///
+    /// \param code New value of completion code
+    void setCode(int code) {
+        ptr_->error_code_ = asio::error_code(code, asio::error_code().category());
+    }
+
+    /// \brief Get number of bytes transferred in I/O
+    size_t& length() {
+        return (ptr_->length_);
+    }
+
+    /// \brief Get cumulative number of bytes transferred in I/O
+    size_t& cumulative() {
+        return (ptr_->cumulative_);
+    }
+
+    /// \brief Get expected amount of data
+    size_t& expected() {
+        return (ptr_->expected_);
+    }
+
+    /// \brief Get offset intodData
+    size_t& offset() {
+        return (ptr_->offset_);
+    }
+
+    /// \brief Get data member
+    uint8_t* data() {
+        return (ptr_->data_);
+    }
+
+    /// \brief Get flag to say what was queued
+    Operation& queued() {
+        return (ptr_->queued_);
+    }
+
+    /// \brief Get flag to say when callback was called
+    Operation& called() {
+        return (ptr_->called_);
+    }
+
+    /// \brief Return instance of callback name
+    std::string& name() {
+        return (ptr_->name_);
+    }
+
+private:
+    boost::shared_ptr<PrivateData>  ptr_;   ///< Pointer to private data
+};
+
+
+// Read Server Data
+//
+// Called in the part of the test that has the client send a message to the
+// server, this loops until all the data has been read (synchronously) by the
+// server.
+//
+// "All the data read" means that the server has received a message that is
+// preceded by a two-byte count field and that the total amount of data received
+// from the remote end is equal to the value in the count field plus two bytes
+// for the count field itself.
+//
+// \param socket Socket on which the server is reading data
+// \param server_cb Structure in which server data is held.
+void
+serverRead(tcp::socket& socket, TCPCallback& server_cb) {
+
+    // As we may need to read multiple times, keep a count of the cumulative
+    // amount of data read and do successive reads into the appropriate part
+    // of the buffer.
+    //
+    // Note that there are no checks for buffer overflow - this is a test
+    // program and we have sized the buffer to be large enough for the test.
+    server_cb.cumulative() = 0;
+
+    bool complete = false;
+    while (!complete) {
+
+        // Read block of data and update cumulative amount of data received.
+        server_cb.length() = socket.receive(
+            asio::buffer(server_cb.data() + server_cb.cumulative(),
+                TCPCallback::MIN_SIZE - server_cb.cumulative()));
+        server_cb.cumulative() += server_cb.length();
+
+        // If we have read at least two bytes, we can work out how much we
+        // should be reading.
+        if (server_cb.cumulative() >= 2) {
+           server_cb.expected() = readUint16(server_cb.data());
+            if ((server_cb.expected() + 2) == server_cb.cumulative()) {
+
+                // Amount of data read from socket equals the size of the
+                // message (as indicated in the first two bytes of the message)
+                // plus the size of the count field.  Therefore we have received
+                // all the data.
+                complete = true;
+            }
+        }
+    }
+}
+
+// Receive complete method should return true only if the count in the first
+// two bytes is equal to the size of the rest if the buffer.
+
+TEST(TCPSocket, processReceivedData) {
+    const uint16_t PACKET_SIZE = 16382;     // Amount of "real" data in the buffer
+
+    IOService               service;        // Used to instantiate socket
+    TCPSocket<TCPCallback>  test(service);  // Socket under test
+    uint8_t                 inbuff[PACKET_SIZE + 2];   // Buffer to check
+    OutputBufferPtr         outbuff(new OutputBuffer(16));
+                                            // Where data is put
+    size_t                  expected;       // Expected amount of data
+    size_t                  offset;         // Where to put next data
+    size_t                  cumulative;     // Cumulative data received
+
+    // Set some dummy values in the buffer to check
+    for (size_t i = 0; i < sizeof(inbuff); ++i) {
+        inbuff[i] = i % 256;
+    }
+
+    // Check that the method will handle various receive sizes.
+    writeUint16(PACKET_SIZE, inbuff);
+
+    cumulative = 0;
+    offset = 0;
+    expected = 0;
+    outbuff->clear();
+    bool complete = test.processReceivedData(inbuff, 1, cumulative, offset,
+                                             expected, outbuff);
+    EXPECT_FALSE(complete);
+    EXPECT_EQ(1, cumulative);
+    EXPECT_EQ(1, offset);
+    EXPECT_EQ(0, expected);
+    EXPECT_EQ(0, outbuff->getLength());
+
+    // Now pretend that we've received one more byte.
+    complete = test.processReceivedData(inbuff, 1, cumulative, offset, expected,
+                                        outbuff);
+    EXPECT_FALSE(complete);
+    EXPECT_EQ(2, cumulative);
+    EXPECT_EQ(0, offset);
+    EXPECT_EQ(PACKET_SIZE, expected);
+    EXPECT_EQ(0, outbuff->getLength());
+
+    // Add another two bytes.  However, this time note that we have to offset
+    // in the input buffer because it is expected that the next chunk of data
+    // from the connection will be read into the start of the buffer.
+    complete = test.processReceivedData(inbuff + cumulative, 2, cumulative,
+                                        offset, expected, outbuff);
+    EXPECT_FALSE(complete);
+    EXPECT_EQ(4, cumulative);
+    EXPECT_EQ(0, offset);
+    EXPECT_EQ(PACKET_SIZE, expected);
+    EXPECT_EQ(2, outbuff->getLength());
+
+    const uint8_t* dataptr = static_cast<const uint8_t*>(outbuff->getData());
+    EXPECT_TRUE(equal(inbuff + 2, inbuff + cumulative, dataptr));
+
+    // And add the remaining data.  Remember that "inbuff" is "PACKET_SIZE + 2"
+    // long.
+    complete = test.processReceivedData(inbuff + cumulative,
+                                        PACKET_SIZE + 2 - cumulative,
+                                        cumulative, offset, expected, outbuff);
+    EXPECT_TRUE(complete);
+    EXPECT_EQ(PACKET_SIZE + 2, cumulative);
+    EXPECT_EQ(0, offset);
+    EXPECT_EQ(PACKET_SIZE, expected);
+    EXPECT_EQ(PACKET_SIZE, outbuff->getLength());
+    dataptr = static_cast<const uint8_t*>(outbuff->getData());
+    EXPECT_TRUE(equal(inbuff + 2, inbuff + cumulative, dataptr));
+}
+
+// TODO: Need to add a test to check the cancel() method
+
+// Tests the operation of a TCPSocket by opening it, sending an asynchronous
+// message to a server, receiving an asynchronous message from the server and
+// closing.
+TEST(TCPSocket, SequenceTest) {
+
+    // Common objects.
+    IOService   service;                    // Service object for async control
+
+    // The client - the TCPSocket being tested
+    TCPSocket<TCPCallback>  client(service);// Socket under test
+    TCPCallback client_cb("Client");        // Async I/O callback function
+    TCPEndpoint client_remote_endpoint;     // Where client receives message from
+    OutputBufferPtr client_buffer(new OutputBuffer(128));
+                                            // Received data is put here
+
+    // The server - with which the client communicates.
+    IOAddress   server_address(SERVER_ADDRESS);
+                                            // Address of target server
+    TCPCallback server_cb("Server");        // Server callback
+    TCPEndpoint server_endpoint(server_address, SERVER_PORT);
+                                            // Endpoint describing server
+    TCPEndpoint server_remote_endpoint;     // Address where server received message from
+    tcp::socket server_socket(service.get_io_service());
+                                            // Socket used for server
+
+    // Step 1.  Create the connection between the client and the server.  Set
+    // up the server to accept incoming connections and have the client open
+    // a channel to it.
+
+    // Set up server - open socket and queue an accept.
+    server_cb.queued() = TCPCallback::ACCEPT;
+    server_cb.called() = TCPCallback::NONE;
+    server_cb.setCode(42);  // Some error
+    tcp::acceptor acceptor(service.get_io_service(),
+                            tcp::endpoint(tcp::v4(), SERVER_PORT));
+    acceptor.set_option(tcp::acceptor::reuse_address(true));
+    acceptor.async_accept(server_socket, server_cb);
+
+    // Set up client - connect to the server.
+    client_cb.queued() = TCPCallback::OPEN;
+    client_cb.called() = TCPCallback::NONE;
+    client_cb.setCode(43);  // Some error
+    EXPECT_FALSE(client.isOpenSynchronous());
+    client.open(&server_endpoint, client_cb);
+
+    // Run the open and the accept callback and check that they ran.
+    service.run_one();
+    service.run_one();
+
+    EXPECT_EQ(TCPCallback::ACCEPT, server_cb.called());
+    EXPECT_EQ(0, server_cb.getCode());
+
+    EXPECT_EQ(TCPCallback::OPEN, client_cb.called());
+    EXPECT_EQ(0, client_cb.getCode());
+
+    // Step 2.  Get the client to write to the server asynchronously.  The
+    // server will loop reading the data synchronously.
+
+    // Write asynchronously to the server.
+    client_cb.called() = TCPCallback::NONE;
+    client_cb.queued() = TCPCallback::WRITE;
+    client_cb.setCode(143);  // Arbitrary number
+    client_cb.length() = 0;
+    client.asyncSend(OUTBOUND_DATA, sizeof(OUTBOUND_DATA), &server_endpoint, client_cb);
+
+    // Wait for the client callback to complete. (Must do this first on
+    // Solaris: if we do the synchronous read first, the test hangs.)
+    service.run_one();
+
+    // Synchronously read the data from the server.;
+    serverRead(server_socket, server_cb);
+
+    // Check the client state
+    EXPECT_EQ(TCPCallback::WRITE, client_cb.called());
+    EXPECT_EQ(0, client_cb.getCode());
+    EXPECT_EQ(sizeof(OUTBOUND_DATA) + 2, client_cb.length());
+
+    // ... and check what the server received.
+    EXPECT_EQ(sizeof(OUTBOUND_DATA) + 2, server_cb.cumulative());
+    EXPECT_TRUE(equal(OUTBOUND_DATA,
+                (OUTBOUND_DATA + (sizeof(OUTBOUND_DATA) - 1)),
+                (server_cb.data() + 2)));
+
+    // Step 3.  Get the server to write all the data asynchronously and have the
+    // client loop (asynchronously) reading the data.  Note that we copy the
+    // data into the server's internal buffer in order to precede it with a two-
+    // byte count field.
+
+    // Have the server write asynchronously to the client.
+    server_cb.called() = TCPCallback::NONE;
+    server_cb.queued() = TCPCallback::WRITE;
+    server_cb.length() = 0;
+    server_cb.cumulative() = 0;
+
+    writeUint16(sizeof(INBOUND_DATA), server_cb.data());
+    copy(INBOUND_DATA, (INBOUND_DATA + sizeof(INBOUND_DATA) - 1),
+        (server_cb.data() + 2));
+    server_socket.async_send(asio::buffer(server_cb.data(),
+                                          (sizeof(INBOUND_DATA) + 2)),
+                             server_cb);
+
+    // Have the client read asynchronously.
+    client_cb.called() = TCPCallback::NONE;
+    client_cb.queued() = TCPCallback::READ;
+    client_cb.length() = 0;
+    client_cb.cumulative() = 0;
+    client_cb.expected() = 0;
+    client_cb.offset() = 0;
+
+    client.asyncReceive(client_cb.data(), TCPCallback::MIN_SIZE,
+                        client_cb.offset(), &client_remote_endpoint,
+                        client_cb);
+
+    // Run the callbacks. Several options are possible depending on how ASIO
+    // is implemented and whether the message gets fragmented:
+    //
+    // 1) The send handler may complete immediately, regardess of whether the
+    // data has been read by the client.  (This is the most likely.)
+    // 2) The send handler may only run after all the data has been read by
+    // the client. (This could happen if the client's TCP buffers were too
+    // small so the data was not transferred to the "remote" system until the
+    // remote buffer has been emptied one or more times.)
+    // 3) The client handler may be run a number of times to handle the message
+    // fragments and the server handler may run between calls of the client
+    // handler.
+    //
+    // So loop, running one handler at a time until we are certain that all the
+    // handlers have run.
+
+    bool server_complete = false;
+    bool client_complete = false;
+    while (!server_complete || !client_complete) {
+        service.run_one();
+
+        // Has the server run?
+        if (!server_complete) {
+            if (server_cb.called() == server_cb.queued()) {
+
+                // Yes.  Check that the send completed successfully and that
+                // all the data that was expected to have been sent was in fact
+                // sent.
+                EXPECT_EQ(0, server_cb.getCode());
+                EXPECT_EQ((sizeof(INBOUND_DATA) + 2), server_cb.length());
+                server_complete = true;
+                continue;
+            }
+        }
+
+        if (!client_complete) {
+
+            // Client callback must have run.  Check that it ran OK.
+            EXPECT_EQ(TCPCallback::READ, client_cb.called());
+            EXPECT_EQ(0, client_cb.getCode());
+
+            // Check if we need to queue another read, copying the data into
+            // the output buffer as we do so.
+            client_complete = client.processReceivedData(client_cb.data(),
+                                                         client_cb.length(),
+                                                         client_cb.cumulative(),
+                                                         client_cb.offset(),
+                                                         client_cb.expected(),
+                                                         client_buffer);
+
+            // If the data is not complete, queue another read.
+            if (! client_complete) {
+                client_cb.called() = TCPCallback::NONE;
+                client_cb.queued() = TCPCallback::READ;
+                client_cb.length() = 0;
+                client.asyncReceive(client_cb.data(), TCPCallback::MIN_SIZE ,
+                                    client_cb.offset(), &client_remote_endpoint,
+                                    client_cb);
+            }
+        }
+    }
+
+    // Both the send and the receive have completed.  Check that the received
+    // is what was sent.
+
+    // Check the client state
+    EXPECT_EQ(TCPCallback::READ, client_cb.called());
+    EXPECT_EQ(0, client_cb.getCode());
+    EXPECT_EQ(sizeof(INBOUND_DATA) + 2, client_cb.cumulative());
+    EXPECT_EQ(sizeof(INBOUND_DATA), client_buffer->getLength());
+
+    // ... and check what the server sent.
+    EXPECT_EQ(TCPCallback::WRITE, server_cb.called());
+    EXPECT_EQ(0, server_cb.getCode());
+    EXPECT_EQ(sizeof(INBOUND_DATA) + 2, server_cb.length());
+
+    // ... and that what was sent is what was received.
+    const uint8_t* received = static_cast<const uint8_t*>(client_buffer->getData());
+    EXPECT_TRUE(equal(INBOUND_DATA, (INBOUND_DATA + (sizeof(INBOUND_DATA) - 1)),
+                      received));
+
+    // Close client and server.
+    EXPECT_NO_THROW(client.close());
+    EXPECT_NO_THROW(server_socket.close());
+}
diff --git a/src/lib/asiolink/tests/udp_socket_unittest.cc b/src/lib/asiolink/tests/udp_socket_unittest.cc
index bb79b88..8563d22 100644
--- a/src/lib/asiolink/tests/udp_socket_unittest.cc
+++ b/src/lib/asiolink/tests/udp_socket_unittest.cc
@@ -12,21 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// 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 Test of UDPSocket
 ///
 /// Tests the fuctionality of a UDPSocket by working through an open-send-
@@ -50,14 +35,18 @@
 #include <boost/bind.hpp>
 #include <boost/shared_ptr.hpp>
 
+#include <dns/buffer.h>
+
 #include <asio.hpp>
 
+#include <asiolink/asiolink_utilities.h>
 #include <asiolink/io_service.h>
 #include <asiolink/udp_endpoint.h>
 #include <asiolink/udp_socket.h>
 
 using namespace asio;
 using namespace asiolink;
+using namespace isc::dns;
 using namespace std;
 
 namespace {
@@ -177,6 +166,49 @@ private:
     boost::shared_ptr<PrivateData>  ptr_;   ///< Pointer to private data
 };
 
+// Receive complete method should return true regardless of what is in the first
+// two bytes of a buffer.
+
+TEST(UDPSocket, processReceivedData) {
+    IOService               service;        // Used to instantiate socket
+    UDPSocket<UDPCallback>  test(service);  // Socket under test
+    uint8_t                 inbuff[32];     // Buffer to check
+    OutputBufferPtr         outbuff(new OutputBuffer(16));
+                                            // Where data is put
+    size_t                  expected;       // Expected amount of data
+    size_t                  offset;         // Where to put next data
+    size_t                  cumulative;     // Cumulative data received
+
+    // Set some dummy values in the buffer to check
+    for (uint8_t i = 0; i < sizeof(inbuff); ++i) {
+        inbuff[i] = i;
+    }
+
+    // Expect that the value is true whatever number is written in the first
+    // two bytes of the buffer.
+    uint16_t count = 0;
+    for (uint32_t i = 0; i < (2 << 16); ++i, ++count) {
+        writeUint16(count, inbuff);
+
+        // Set some random values
+        cumulative = 5;
+        offset = 10;
+        expected = 15;
+        outbuff->clear();
+
+        bool completed = test.processReceivedData(inbuff, sizeof(inbuff),
+                                                  cumulative, offset, expected,
+                                                  outbuff);
+        EXPECT_TRUE(completed);
+        EXPECT_EQ(sizeof(inbuff), cumulative);
+        EXPECT_EQ(0, offset);
+        EXPECT_EQ(sizeof(inbuff), expected);
+
+        const uint8_t* dataptr = static_cast<const uint8_t*>(outbuff->getData());
+        EXPECT_TRUE(equal(inbuff, inbuff + sizeof(inbuff) - 1, dataptr));
+    }
+}
+
 // TODO: Need to add a test to check the cancel() method
 
 // Tests the operation of a UDPSocket by opening it, sending an asynchronous
@@ -199,6 +231,10 @@ TEST(UDPSocket, SequenceTest) {
     UDPCallback client_cb("Client");        // Async I/O callback function
     UDPEndpoint client_remote_endpoint;     // Where client receives message from
     size_t      client_cumulative = 0;      // Cumulative data received
+    size_t      client_offset = 0;          // Offset into buffer where data is put
+    size_t      client_expected = 0;        // Expected amount of data
+    OutputBufferPtr client_buffer(new OutputBuffer(16));
+                                            // Where data is put
 
     // The server - with which the client communicates.  For convenience, we
     // use the same io_service, and use the endpoint object created for
@@ -208,11 +244,12 @@ TEST(UDPSocket, SequenceTest) {
     server.set_option(socket_base::reuse_address(true));
 
     // Assertion to ensure that the server buffer is large enough
-    char data[UDPSocket<UDPCallback>::MAX_SIZE];
+    char data[UDPSocket<UDPCallback>::MIN_SIZE];
     ASSERT_GT(sizeof(data), sizeof(OUTBOUND_DATA));
 
     // Open the client socket - the operation should be synchronous
-    EXPECT_FALSE(client.open(&server_endpoint, client_cb));
+    EXPECT_TRUE(client.isOpenSynchronous());
+    client.open(&server_endpoint, client_cb);
 
     // Issue read on the server.  Completion callback should not have run.
     server_cb.setCalled(false);
@@ -257,7 +294,7 @@ TEST(UDPSocket, SequenceTest) {
     server.async_send_to(buffer(INBOUND_DATA, sizeof(INBOUND_DATA)),
         server_remote_endpoint.getASIOEndpoint(), server_cb);
 
-    // Expect the two callbacks to run
+    // Expect two callbacks to run.
     service.run_one();
     service.run_one();
 
@@ -276,10 +313,19 @@ TEST(UDPSocket, SequenceTest) {
     EXPECT_TRUE(server_address == client_remote_endpoint.getAddress());
     EXPECT_EQ(SERVER_PORT, client_remote_endpoint.getPort());
 
-    // Finally, check that the receive received a complete buffer's worth of data.
-    EXPECT_TRUE(client.receiveComplete(&data[0], client_cb.getLength(),
-        client_cumulative));
+    // Check that the receive received a complete buffer's worth of data.
+    EXPECT_TRUE(client.processReceivedData(&data[0], client_cb.getLength(),
+                                           client_cumulative, client_offset,
+                                           client_expected, client_buffer));
+
     EXPECT_EQ(client_cb.getLength(), client_cumulative);
+    EXPECT_EQ(0, client_offset);
+    EXPECT_EQ(client_cb.getLength(), client_expected);
+    EXPECT_EQ(client_cb.getLength(), client_buffer->getLength());
+
+    // ...and check that the data was copied to the output client buffer.
+    const char* client_char_data = static_cast<const char*>(client_buffer->getData());
+    EXPECT_TRUE(equal(&data[0], &data[client_cb.getLength() - 1], client_char_data));
 
     // Close client and server.
     EXPECT_NO_THROW(client.close());
diff --git a/src/lib/asiolink/udp_endpoint.h b/src/lib/asiolink/udp_endpoint.h
index 0958af6..99dc27f 100644
--- a/src/lib/asiolink/udp_endpoint.h
+++ b/src/lib/asiolink/udp_endpoint.h
@@ -64,6 +64,17 @@ public:
         asio_endpoint_placeholder_(NULL), asio_endpoint_(asio_endpoint)
     {}
 
+    /// \brief Constructor from an ASIO UDP endpoint.
+    ///
+    /// This constructor is designed to be an efficient wrapper for the
+    /// corresponding ASIO class, \c udp::endpoint.
+    ///
+    /// \param asio_endpoint The ASIO representation of the TCP endpoint.
+    UDPEndpoint(const asio::ip::udp::endpoint& asio_endpoint) :
+        asio_endpoint_placeholder_(new asio::ip::udp::endpoint(asio_endpoint)),
+        asio_endpoint_(*asio_endpoint_placeholder_)
+    {}
+
     /// \brief The destructor.
     virtual ~UDPEndpoint() { delete asio_endpoint_placeholder_; }
     //@}
diff --git a/src/lib/asiolink/udp_server.cc b/src/lib/asiolink/udp_server.cc
index 98a47c4..063926e 100644
--- a/src/lib/asiolink/udp_server.cc
+++ b/src/lib/asiolink/udp_server.cc
@@ -15,6 +15,7 @@
 #include <netinet/in.h>
 #include <sys/socket.h>
 #include <unistd.h>             // for some IPC/network system calls
+#include <errno.h>
 
 #include <boost/shared_array.hpp>
 
@@ -195,6 +196,14 @@ UDPServer::operator()(error_code ec, size_t length) {
                 CORO_YIELD data_->socket_->async_receive_from(
                     buffer(data_->data_.get(), MAX_LENGTH), *data_->sender_,
                     *this);
+                // Abort on fatal errors
+                if (ec) {
+                    using namespace asio::error;
+                    if (ec.value() != would_block && ec.value() != try_again &&
+                        ec.value() != interrupted) {
+                        return;
+                    }
+                }
             } while (ec || length == 0);
 
             data_->bytes_ = length;
@@ -257,8 +266,6 @@ UDPServer::operator()(error_code ec, size_t length) {
         // this point.
         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 (!data_->done_) {
diff --git a/src/lib/asiolink/udp_socket.h b/src/lib/asiolink/udp_socket.h
index bb94ad5..35fc7b1 100644
--- a/src/lib/asiolink/udp_socket.h
+++ b/src/lib/asiolink/udp_socket.h
@@ -28,7 +28,6 @@
 
 #include <config.h>
 
-
 #include <asiolink/io_asio_socket.h>
 #include <asiolink/io_endpoint.h>
 #include <asiolink/io_service.h>
@@ -49,20 +48,20 @@ private:
 
 public:
     enum {
-        MAX_SIZE = 4096         // Send and receive size
+        MIN_SIZE = 4096         // Minimum send and receive size
     };
-    
+
     /// \brief Constructor from an ASIO UDP socket.
     ///
-    /// \param socket The ASIO representation of the UDP socket.  It
-    /// is assumed that the caller will open and close the socket, so
-    /// these operations are a no-op for that socket.
+    /// \param socket The ASIO representation of the UDP socket.  It is assumed
+    ///        that the caller will open and close the socket, so these
+    ///        operations are a no-op for that socket.
     UDPSocket(asio::ip::udp::socket& socket);
 
     /// \brief Constructor
     ///
     /// Used when the UDPSocket is being asked to manage its own internal
-    /// socket.  It is assumed that open() and close() will not be used.
+    /// socket.  In this case, the open() and close() methods are used.
     ///
     /// \param service I/O Service object used to manage the socket.
     UDPSocket(IOService& service);
@@ -70,68 +69,79 @@ public:
     /// \brief Destructor
     virtual ~UDPSocket();
 
-    virtual int getNative() const { return (socket_.native()); }
-    virtual int getProtocol() const { return (IPPROTO_UDP); }
+    /// \brief Return file descriptor of underlying socket
+    virtual int getNative() const {
+        return (socket_.native());
+    }
 
-    /// \brief Open Socket
+    /// \brief Return protocol of socket
+    virtual int getProtocol() const {
+        return (IPPROTO_UDP);
+    }
+
+    /// \brief Is "open()" synchronous?
     ///
-    /// Opens the UDP socket.  In the model for transport-layer agnostic I/O,
-    /// an "open" operation includes a connection to the remote end (which
-    /// may take time).  This does not happen for UDP, so the method returns
-    /// "false" to indicate that the operation completed synchronously.
+    /// Indicates that the opening of a UDP socket is synchronous.
+    virtual bool isOpenSynchronous() const {
+        return true;
+    }
+
+    /// \brief Open Socket
     ///
-    /// \param endpoint Endpoint to which the socket will connect to.
-    /// \param callback Unused.
+    /// Opens the UDP socket.  This is a synchronous operation.
     ///
-    /// \return false to indicate that the "operation" completed synchronously.
-    virtual bool open(const IOEndpoint* endpoint, C&);
+    /// \param endpoint Endpoint to which the socket will send data.  This is
+    ///        used to determine the address family trhat should be used for the
+    ///        underlying socket.
+    /// \param callback Unused as the operation is synchronous.
+    virtual void open(const IOEndpoint* endpoint, C& callback);
 
     /// \brief Send Asynchronously
     ///
-    /// This corresponds to async_send_to() for UDP sockets and async_send()
-    /// for TCP.  In both cases an endpoint argument is supplied indicating the
-    /// target of the send - this is ignored for TCP.
+    /// Calls the underlying socket's async_send_to() method to send a packet of
+    /// data asynchronously to the remote endpoint.  The callback will be called
+    /// on completion.
     ///
     /// \param data Data to send
     /// \param length Length of data to send
     /// \param endpoint Target of the send
     /// \param callback Callback object.
     virtual void asyncSend(const void* data, size_t length,
-        const IOEndpoint* endpoint, C& callback);
+                           const IOEndpoint* endpoint, C& callback);
 
     /// \brief Receive Asynchronously
     ///
-    /// This correstponds to async_receive_from() for UDP sockets and
-    /// async_receive() for TCP.  In both cases, an endpoint argument is
-    /// supplied to receive the source of the communication.  For TCP it will
-    /// be filled in with details of the connection.
+    /// Calls the underlying socket's async_receive_from() method to read a
+    /// packet of data from a remote endpoint.  Arrival of the data is signalled
+    /// via a call to the callback function.
     ///
     /// \param data Buffer to receive incoming message
     /// \param length Length of the data buffer
-    /// \param cumulative Amount of data that should already be in the buffer.
-    /// (This is ignored - every UPD receive fills the buffer from the start.)
+    /// \param offset Offset into buffer where data is to be put
     /// \param endpoint Source of the communication
     /// \param callback Callback object
-    virtual void asyncReceive(void* data, size_t length, size_t cumulative,
-        IOEndpoint* endpoint, C& callback);
+    virtual void asyncReceive(void* data, size_t length, size_t offset,
+                              IOEndpoint* endpoint, C& callback);
 
-    /// \brief Checks if the data received is complete.
+    /// \brief Process received data
     ///
-    /// As all the data is received in one I/O, so this is, this is effectively
-    /// a no-op (although it does update the amount of data received).
+    /// See the description of IOAsioSocket::receiveComplete for a complete
+    /// description of this method.
     ///
-    /// \param data Data buffer containing data to date.  (This is ignored
-    /// for UDP receives.)
-    /// \param length Amount of data received in last asynchronous I/O
-    /// \param cumulative On input, amount of data received before the last
-    /// I/O.  On output, the total amount of data received to date.
+    /// \param staging Pointer to the start of the staging buffer.
+    /// \param length Amount of data in the staging buffer.
+    /// \param cumulative Amount of data received before the staging buffer is
+    ///        processed.
+    /// \param offset Unused.
+    /// \param expected unused.
+    /// \param outbuff Output buffer.  Data in the staging buffer is be copied
+    ///        to this output buffer in the call.
     ///
-    /// \return true if the receive is complete, false if another receive is
-    /// needed.
-    virtual bool receiveComplete(void*, size_t length, size_t& cumulative) {
-        cumulative = length;
-        return (true);
-    }
+    /// \return Always true
+    virtual bool processReceivedData(const void* staging, size_t length,
+                                     size_t& cumulative, size_t& offset,
+                                     size_t& expected,
+                                     isc::dns::OutputBufferPtr& outbuff);
 
     /// \brief Cancel I/O On Socket
     virtual void cancel();
@@ -174,16 +184,16 @@ UDPSocket<C>::~UDPSocket()
     delete socket_ptr_;
 }
 
-// Open the socket.  Throws an error on failure
-// TODO: Make the open more resilient
+// Open the socket.
 
-template <typename C> bool
+template <typename C> void
 UDPSocket<C>::open(const IOEndpoint* endpoint, C&) {
 
-    // Ignore opens on already-open socket.  Don't throw a failure because
-    // of uncertainties as to what precedes whan when using asynchronous I/O.
-    // At also allows us a treat a passed-in socket as a self-managed socket.
-
+    // Ignore opens on already-open socket.  (Don't throw a failure because
+    // of uncertainties as to what precedes whan when using asynchronous I/O.)
+    // It also allows us a treat a passed-in socket in exactly the same way as
+    // a self-managed socket (in that we can call the open() and close() methods
+    // of this class).
     if (!isopen_) {
         if (endpoint->getFamily() == AF_INET) {
             socket_.open(asio::ip::udp::v4());
@@ -193,14 +203,21 @@ UDPSocket<C>::open(const IOEndpoint* endpoint, C&) {
         }
         isopen_ = true;
 
-        // Ensure it can send and receive 4K buffers.
-        socket_.set_option(asio::socket_base::send_buffer_size(MAX_SIZE));
-        socket_.set_option(asio::socket_base::receive_buffer_size(MAX_SIZE));
-    ;
-        // Allow reuse of an existing port/address
-        socket_.set_option(asio::socket_base::reuse_address(true));
+        // Ensure it can send and receive at least 4K buffers.
+        asio::ip::udp::socket::send_buffer_size snd_size;
+        socket_.get_option(snd_size);
+        if (snd_size.value() < MIN_SIZE) {
+            snd_size = MIN_SIZE;
+            socket_.set_option(snd_size);
+        }
+
+        asio::ip::udp::socket::receive_buffer_size rcv_size;
+        socket_.get_option(rcv_size);
+        if (rcv_size.value() < MIN_SIZE) {
+            rcv_size = MIN_SIZE;
+            socket_.set_option(rcv_size);
+        }
     }
-    return (false);
 }
 
 // Send a message.  Should never do this if the socket is not open, so throw
@@ -208,19 +225,20 @@ UDPSocket<C>::open(const IOEndpoint* endpoint, C&) {
 
 template <typename C> void
 UDPSocket<C>::asyncSend(const void* data, size_t length,
-    const IOEndpoint* endpoint, C& callback)
+                        const IOEndpoint* endpoint, C& callback)
 {
     if (isopen_) {
 
         // Upconvert to a UDPEndpoint.  We need to do this because although
         // IOEndpoint is the base class of UDPEndpoint and TCPEndpoint, it
-        // doing cont contain a method for getting at the underlying endpoint
-        // type - those are in the derived class and the two classes differ on
+        // does not contain a method for getting at the underlying endpoint
+        // type - that is in the derived class and the two classes differ on
         // return type.
-
         assert(endpoint->getProtocol() == IPPROTO_UDP);
         const UDPEndpoint* udp_endpoint =
             static_cast<const UDPEndpoint*>(endpoint);
+
+        // ... and send the message.
         socket_.async_send_to(asio::buffer(data, length),
             udp_endpoint->getASIOEndpoint(), callback);
     } else {
@@ -229,14 +247,12 @@ UDPSocket<C>::asyncSend(const void* data, size_t length,
     }
 }
 
-// Receive a message. Note that the "cumulative" argument is ignored - every UDP
-// receive is put into the buffer beginning at the start - there is no concept
-// receiving a subsequent part of a message.  Same critera as before concerning
-// the need for the socket to be open.
+// Receive a message.   Should never do this if the socket is not open, so throw
+// an exception if this is the case.
 
 template <typename C> void
-UDPSocket<C>::asyncReceive(void* data, size_t length, size_t,
-    IOEndpoint* endpoint, C& callback)
+UDPSocket<C>::asyncReceive(void* data, size_t length, size_t offset,
+                           IOEndpoint* endpoint, C& callback)
 {
     if (isopen_) {
 
@@ -244,7 +260,15 @@ UDPSocket<C>::asyncReceive(void* data, size_t length, size_t,
         assert(endpoint->getProtocol() == IPPROTO_UDP);
         UDPEndpoint* udp_endpoint = static_cast<UDPEndpoint*>(endpoint);
 
-        socket_.async_receive_from(asio::buffer(data, length),
+        // Ensure we can write into the buffer
+        if (offset >= length) {
+            isc_throw(BufferOverflow, "attempt to read into area beyond end of "
+                                      "UDP receive buffer");
+        }
+        void* buffer_start = static_cast<void*>(static_cast<uint8_t*>(data) + offset);
+
+        // Issue the read
+        socket_.async_receive_from(asio::buffer(buffer_start, length - offset),
             udp_endpoint->getASIOEndpoint(), callback);
     } else {
         isc_throw(SocketNotOpen,
@@ -252,7 +276,29 @@ UDPSocket<C>::asyncReceive(void* data, size_t length, size_t,
     }
 }
 
+// Receive complete.  Just copy the data across to the output buffer and
+// update arguments as appropriate.
+
+template <typename C> bool
+UDPSocket<C>::processReceivedData(const void* staging, size_t length,
+                                  size_t& cumulative, size_t& offset,
+                                  size_t& expected,
+                                  isc::dns::OutputBufferPtr& outbuff)
+{
+    // Set return values to what we should expect.
+    cumulative = length;
+    expected = length;
+    offset = 0;
+
+    // Copy data across
+    outbuff->writeData(staging, length);
+
+    // ... and mark that we have everything.
+    return (true);
+}
+
 // Cancel I/O on the socket.  No-op if the socket is not open.
+
 template <typename C> void
 UDPSocket<C>::cancel() {
     if (isopen_) {
diff --git a/src/lib/cache/Makefile.am b/src/lib/cache/Makefile.am
index 264aca6..107fc9a 100644
--- a/src/lib/cache/Makefile.am
+++ b/src/lib/cache/Makefile.am
@@ -29,5 +29,6 @@ libcache_la_SOURCES  += rrset_entry.h rrset_entry.cc
 libcache_la_SOURCES  += cache_entry_key.h cache_entry_key.cc
 libcache_la_SOURCES  += rrset_copy.h rrset_copy.cc
 libcache_la_SOURCES  += local_zone_data.h local_zone_data.cc
+libcache_la_SOURCES  += message_utility.h message_utility.cc
 
 CLEANFILES = *.gcno *.gcda
diff --git a/src/lib/cache/TODO b/src/lib/cache/TODO
index a7d2458..aa7e3b0 100644
--- a/src/lib/cache/TODO
+++ b/src/lib/cache/TODO
@@ -11,4 +11,8 @@
   to expire.
 * When the rrset beging updated is an NS rrset, NSAS should be updated
   together.
+* Share the NXDOMAIN info between different type queries. current implementation
+  can only cache for the type that user quired, for example, if user query A 
+  record of a.example. and the server replied with NXDOMAIN, this should be
+  cached for all the types queries of a.example.
 
diff --git a/src/lib/cache/message_cache.cc b/src/lib/cache/message_cache.cc
index e832f52..0464f87 100644
--- a/src/lib/cache/message_cache.cc
+++ b/src/lib/cache/message_cache.cc
@@ -18,19 +18,23 @@
 #include <nsas/hash_table.h>
 #include <nsas/hash_deleter.h>
 #include "message_cache.h"
+#include "message_utility.h"
 #include "cache_entry_key.h"
 
+namespace isc {
+namespace cache {
+
 using namespace isc::nsas;
 using namespace isc::dns;
 using namespace std;
+using namespace MessageUtility;
 
-namespace isc {
-namespace cache {
-
-MessageCache::MessageCache(boost::shared_ptr<RRsetCache> rrset_cache,
-    uint32_t cache_size, uint16_t message_class):
+MessageCache::MessageCache(const RRsetCachePtr& rrset_cache,
+                           uint32_t cache_size, uint16_t message_class,
+                           const RRsetCachePtr& negative_soa_cache):
     message_class_(message_class),
     rrset_cache_(rrset_cache),
+    negative_soa_cache_(negative_soa_cache),
     message_table_(new NsasEntryCompare<MessageEntry>, cache_size),
     message_lru_((3 * cache_size),
                   new HashDeleter<MessageEntry>(message_table_))
@@ -68,8 +72,13 @@ MessageCache::lookup(const isc::dns::Name& qname,
 
 bool
 MessageCache::update(const Message& msg) {
+    if (!canMessageBeCached(msg)){
+        return (false);
+    }
+
     QuestionIterator iter = msg.beginQuestion();
-    std::string entry_name = genCacheEntryName((*iter)->getName(), (*iter)->getType());
+    std::string entry_name = genCacheEntryName((*iter)->getName(),
+                                               (*iter)->getType());
     HashKey entry_key = HashKey(entry_name, RRClass(message_class_));
 
     // The simplest way to update is removing the old message entry directly.
@@ -82,7 +91,8 @@ MessageCache::update(const Message& msg) {
         message_lru_.remove(old_msg_entry);
     }
 
-    MessageEntryPtr msg_entry(new MessageEntry(msg, rrset_cache_));
+    MessageEntryPtr msg_entry(new MessageEntry(msg, rrset_cache_,
+                                               negative_soa_cache_));
     message_lru_.add(msg_entry);
     return (message_table_.add(msg_entry, entry_key, true));
 }
diff --git a/src/lib/cache/message_cache.h b/src/lib/cache/message_cache.h
index 50b2b8a..790ed83 100644
--- a/src/lib/cache/message_cache.h
+++ b/src/lib/cache/message_cache.h
@@ -21,12 +21,11 @@
 #include "message_entry.h"
 #include <nsas/hash_table.h>
 #include <nsas/lru_list.h>
+#include "rrset_cache.h"
 
 namespace isc {
 namespace cache {
 
-class RRsetCache;
-
 /// \brief Message Cache
 /// The object of MessageCache represents the cache for class-specific
 /// messages.
@@ -37,9 +36,15 @@ private:
     MessageCache(const MessageCache& source);
     MessageCache& operator=(const MessageCache& source);
 public:
+    /// \param rrset_cache The cache that stores the RRsets that the
+    ///        message entry will points to
     /// \param cache_size The size of message cache.
-    MessageCache(boost::shared_ptr<RRsetCache> rrset_cache_,
-                 uint32_t cache_size, uint16_t message_class);
+    /// \param message_class The class of the message cache
+    /// \param negative_soa_cache The cache that stores the SOA record
+    ///        that comes from negative response message
+    MessageCache(const RRsetCachePtr& rrset_cache,
+                 uint32_t cache_size, uint16_t message_class,
+                 const RRsetCachePtr& negative_soa_cache);
 
     /// \brief Destructor function
     virtual ~MessageCache();
@@ -84,7 +89,8 @@ protected:
     // Make these variants be protected for easy unittest.
 protected:
     uint16_t message_class_; // The class of the message cache.
-    boost::shared_ptr<RRsetCache> rrset_cache_;
+    RRsetCachePtr rrset_cache_;
+    RRsetCachePtr negative_soa_cache_;
     isc::nsas::HashTable<MessageEntry> message_table_;
     isc::nsas::LruList<MessageEntry> message_lru_;
 };
diff --git a/src/lib/cache/message_entry.cc b/src/lib/cache/message_entry.cc
index 6396167..de4ea89 100644
--- a/src/lib/cache/message_entry.cc
+++ b/src/lib/cache/message_entry.cc
@@ -18,6 +18,7 @@
 #include <dns/message.h>
 #include <nsas/nsas_entry.h>
 #include "message_entry.h"
+#include "message_utility.h"
 #include "rrset_cache.h"
 
 using namespace isc::dns;
@@ -56,9 +57,27 @@ namespace cache {
 
 static uint32_t MAX_UINT32 = numeric_limits<uint32_t>::max();
 
+// As with caching positive responses it is sensible for a resolver to
+// limit for how long it will cache a negative response as the protocol
+// supports caching for up to 68 years.  Such a limit should not be
+// greater than that applied to positive answers and preferably be
+// tunable.  Values of one to three hours have been found to work well
+// and would make sensible a default.  Values exceeding one day have
+// been found to be problematic. (sec 5, RFC2308)
+// The default value is 3 hourse (10800 seconds)
+// TODO:Give an option to let user configure
+static uint32_t MAX_NEGATIVE_CACHE_TTL = 10800;
+
+// Sets the maximum time for which the server will cache ordinary (positive) answers. The
+// default is one week (7 days = 604800 seconds)
+// TODO:Give an option to let user configure
+static uint32_t MAX_NORMAL_CACHE_TTL = 604800;
+
 MessageEntry::MessageEntry(const isc::dns::Message& msg,
-                           boost::shared_ptr<RRsetCache> rrset_cache):
+                           const RRsetCachePtr& rrset_cache,
+                           const RRsetCachePtr& negative_soa_cache):
     rrset_cache_(rrset_cache),
+    negative_soa_cache_(negative_soa_cache),
     headerflag_aa_(false),
     headerflag_tc_(false)
 {
@@ -74,7 +93,8 @@ MessageEntry::getRRsetEntries(vector<RRsetEntryPtr>& rrset_entry_vec,
     uint16_t entry_count = answer_count_ + authority_count_ + additional_count_;
     rrset_entry_vec.reserve(rrset_entry_vec.size() + entry_count);
     for (int index = 0; index < entry_count; ++index) {
-        RRsetEntryPtr rrset_entry = rrset_cache_->lookup(rrsets_[index].name_,
+        RRsetCache* rrset_cache = rrsets_[index].cache_;
+        RRsetEntryPtr rrset_entry = rrset_cache->lookup(rrsets_[index].name_,
                                                         rrsets_[index].type_);
         if (rrset_entry && time_now < rrset_entry->getExpireTime()) {
             rrset_entry_vec.push_back(rrset_entry);
@@ -104,8 +124,9 @@ MessageEntry::addRRset(isc::dns::Message& message,
         end_index = start_index + additional_count_;
     }
 
-    for(uint16_t index = start_index; index < end_index; ++index) {
-        message.addRRset(section, rrset_entry_vec[index]->getRRset(), dnssec_need);
+    for (uint16_t index = start_index; index < end_index; ++index) {
+        message.addRRset(section, rrset_entry_vec[index]->getRRset(),
+                         dnssec_need);
     }
 }
 
@@ -127,7 +148,9 @@ MessageEntry::genMessage(const time_t& time_now,
         // Begin message generation. We don't need to add question
         // section, since it has been included in the message.
         // Set cached header flags.
-        msg.setHeaderFlag(Message::HEADERFLAG_AA, headerflag_aa_);
+        // The AA flag bit should be cleared because this is a response from
+        // resolver cache
+        msg.setHeaderFlag(Message::HEADERFLAG_AA, false);
         msg.setHeaderFlag(Message::HEADERFLAG_TC, headerflag_tc_);
 
         bool dnssec_need = msg.getEDNS().get();
@@ -233,7 +256,8 @@ MessageEntry::parseSection(const isc::dns::Message& msg,
         RRsetPtr rrset_ptr = *iter;
         RRsetTrustLevel level = getRRsetTrustLevel(msg, rrset_ptr, section);
         RRsetEntryPtr rrset_entry = rrset_cache_->update(*rrset_ptr, level);
-        rrsets_.push_back(RRsetRef(rrset_ptr->getName(), rrset_ptr->getType()));
+        rrsets_.push_back(RRsetRef(rrset_ptr->getName(), rrset_ptr->getType(),
+                          rrset_cache_.get()));
 
         uint32_t rrset_ttl = rrset_entry->getTTL();
         if (smaller_ttl > rrset_ttl) {
@@ -247,6 +271,37 @@ MessageEntry::parseSection(const isc::dns::Message& msg,
 }
 
 void
+MessageEntry::parseNegativeResponseAuthoritySection(const isc::dns::Message& msg,
+        uint32_t& min_ttl,
+        uint16_t& rrset_count)
+{
+    uint16_t count = 0;
+    for (RRsetIterator iter = msg.beginSection(Message::SECTION_AUTHORITY);
+            iter != msg.endSection(Message::SECTION_AUTHORITY);
+            ++iter) {
+        RRsetPtr rrset_ptr = *iter;
+        RRsetTrustLevel level = getRRsetTrustLevel(msg, rrset_ptr,
+                                                   Message::SECTION_AUTHORITY);
+        boost::shared_ptr<RRsetCache> rrset_cache_ptr = rrset_cache_;
+        if (rrset_ptr->getType() == RRType::SOA()) {
+            rrset_cache_ptr = negative_soa_cache_;
+        }
+
+        RRsetEntryPtr rrset_entry = rrset_cache_ptr->update(*rrset_ptr, level);
+        rrsets_.push_back(RRsetRef(rrset_ptr->getName(),
+                                   rrset_ptr->getType(),
+                                   rrset_cache_ptr.get()));
+        uint32_t rrset_ttl = rrset_entry->getTTL();
+        if (min_ttl > rrset_ttl) {
+            min_ttl = rrset_ttl;
+        }
+        ++count;
+    }
+
+    rrset_count = count;
+}
+
+void
 MessageEntry::initMessageEntry(const isc::dns::Message& msg) {
     //TODO better way to cache the header flags?
     headerflag_aa_ = msg.getHeaderFlag(Message::HEADERFLAG_AA);
@@ -261,14 +316,30 @@ MessageEntry::initMessageEntry(const isc::dns::Message& msg) {
     query_class_ = (*iter)->getClass().getCode();
 
     uint32_t min_ttl = MAX_UINT32;
+
+    bool isNegativeResponse = MessageUtility::isNegativeResponse(msg);
+
     parseSection(msg, Message::SECTION_ANSWER, min_ttl, answer_count_);
-    parseSection(msg, Message::SECTION_AUTHORITY, min_ttl, authority_count_);
+    if (!isNegativeResponse) {
+        parseSection(msg, Message::SECTION_AUTHORITY, min_ttl, authority_count_);
+    } else {
+        parseNegativeResponseAuthoritySection(msg, min_ttl, authority_count_);
+    }
     parseSection(msg, Message::SECTION_ADDITIONAL, min_ttl, additional_count_);
 
+    // Limit the ttl to a prset max-value
+    if (!isNegativeResponse) {
+        if (min_ttl > MAX_NORMAL_CACHE_TTL) {
+            min_ttl = MAX_NORMAL_CACHE_TTL;
+        }
+    } else {
+        if (min_ttl > MAX_NEGATIVE_CACHE_TTL) {
+            min_ttl = MAX_NEGATIVE_CACHE_TTL;
+        }
+    }
+
     expire_time_ = time(NULL) + min_ttl;
 }
 
 } // namespace cache
 } // namespace isc
-
-
diff --git a/src/lib/cache/message_entry.h b/src/lib/cache/message_entry.h
index 51bf5cc..6775ff6 100644
--- a/src/lib/cache/message_entry.h
+++ b/src/lib/cache/message_entry.h
@@ -19,33 +19,15 @@
 #include <dns/message.h>
 #include <dns/rrset.h>
 #include <nsas/nsas_entry.h>
+#include "rrset_cache.h"
 #include "rrset_entry.h"
 
-
 using namespace isc::nsas;
 
 namespace isc {
 namespace cache {
 
 class RRsetEntry;
-class RRsetCache;
-
-/// \brief Information to refer an RRset.
-///
-/// There is no class information here, since the rrsets are cached in
-/// the class-specific rrset cache.
-struct RRsetRef{
-    /// \brief Constructor
-    ///
-    /// \param name The Name for the RRset
-    /// \param type the RRType for the RRrset
-    RRsetRef(const isc::dns::Name& name, const isc::dns::RRType& type):
-            name_(name), type_(type)
-    {}
-
-    isc::dns::Name name_; // Name of rrset.
-    isc::dns::RRType type_; // Type of rrset.
-};
 
 /// \brief Message Entry
 ///
@@ -56,6 +38,27 @@ class MessageEntry : public NsasEntry<MessageEntry> {
 private:
     MessageEntry(const MessageEntry& source);
     MessageEntry& operator=(const MessageEntry& source);
+
+    /// \brief Information to refer an RRset.
+    ///
+    /// There is no class information here, since the rrsets are cached in
+    /// the class-specific rrset cache.
+    struct RRsetRef{
+        /// \brief Constructor
+        ///
+        /// \param name The Name for the RRset
+        /// \param type The RRType for the RRrset
+        /// \param cache Which cache the RRset is stored in
+        RRsetRef(const isc::dns::Name& name, const isc::dns::RRType& type,
+                RRsetCache* cache):
+                name_(name), type_(type), cache_(cache)
+        {}
+
+        isc::dns::Name name_; // Name of rrset.
+        isc::dns::RRType type_; // Type of rrset.
+        RRsetCache* cache_; //Which cache the RRset is stored
+    };
+
 public:
 
     /// \brief Initialize the message entry object with one dns
@@ -66,8 +69,12 @@ public:
     ///        since some new rrset entries may be inserted into
     ///        rrset cache, or the existed rrset entries need
     ///        to be updated.
+    /// \param negative_soa_cache the pointer of RRsetCache. This
+    ///        cache is used only for storing SOA rrset from negative
+    ///        response (NXDOMAIN or NOERROR_NODATA)
     MessageEntry(const isc::dns::Message& message,
-                 boost::shared_ptr<RRsetCache> rrset_cache);
+                 const RRsetCachePtr& rrset_cache,
+                 const RRsetCachePtr& negative_soa_cache);
 
     ~MessageEntry() { delete hash_key_ptr_; };
 
@@ -117,6 +124,16 @@ protected:
                       uint32_t& smaller_ttl,
                       uint16_t& rrset_count);
 
+    /// \brief Parse the RRsets in the authority section of
+    ///        negative response. The SOA RRset need to be located and
+    ///        stored in a seperate cache
+    /// \param msg The message to parse the RRsets from
+    /// \param min_ttl Get the minimum ttl of rrset in the authority section
+    /// \param rrset_count the rrset count of the authority section
+    void parseNegativeResponseAuthoritySection(const isc::dns::Message& msg,
+            uint32_t& min_ttl,
+            uint16_t& rrset_count);
+
     /// \brief Get RRset Trustworthiness
     ///        The algorithm refers to RFC2181 section 5.4.1
     ///        Only the rrset can be updated by the rrsets
@@ -161,7 +178,9 @@ private:
     HashKey* hash_key_ptr_;  // the key for messag entry in hash table.
 
     std::vector<RRsetRef> rrsets_;
-    boost::shared_ptr<RRsetCache> rrset_cache_;
+    RRsetCachePtr rrset_cache_; //Normal rrset cache
+    // SOA rrset from negative response
+    RRsetCachePtr negative_soa_cache_;
 
     std::string query_name_; // query name of the message.
     uint16_t query_class_; // query class of the message.
diff --git a/src/lib/cache/message_utility.cc b/src/lib/cache/message_utility.cc
new file mode 100644
index 0000000..53a3352
--- /dev/null
+++ b/src/lib/cache/message_utility.cc
@@ -0,0 +1,80 @@
+// 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 "message_utility.h"
+#include <dns/rcode.h>
+
+using namespace isc::dns;
+
+namespace isc {
+namespace cache {
+namespace MessageUtility{
+
+bool
+hasTheRecordInAuthoritySection(const isc::dns::Message& msg,
+                               const isc::dns::RRType& type)
+{
+    // isc::dns::Message provide one function hasRRset() should be used to
+    // determine whether the given section has an RRset matching the given
+    // name and type, but currently it is not const-qualified and cannot be
+    // used here
+    // TODO: use hasRRset() function when it is const qualified
+    for (RRsetIterator iter = msg.beginSection(Message::SECTION_AUTHORITY);
+            iter != msg.endSection(Message::SECTION_AUTHORITY);
+            ++iter) {
+        RRsetPtr rrset_ptr = *iter;
+        if (rrset_ptr->getType() == type) {
+            return (true);
+        }
+    }
+    return (false);
+}
+
+bool
+isNegativeResponse(const isc::dns::Message& msg) {
+    if (msg.getRcode() == Rcode::NXDOMAIN()) {
+        return (true);
+    } else if (msg.getRcode() == Rcode::NOERROR()) {
+        // no data in the answer section
+        if (msg.getRRCount(Message::SECTION_ANSWER) == 0) {
+            // NODATA type 1/ type 2 (ref sec2.2 of RFC2308)
+            if (hasTheRecordInAuthoritySection(msg, RRType::SOA())) {
+                return (true);
+            } else if (!hasTheRecordInAuthoritySection(msg, RRType::NS())) {
+                // NODATA type 3 (sec2.2 of RFC2308)
+                return (true);
+            }
+        }
+    }
+
+    return (false);
+}
+
+bool
+canMessageBeCached(const isc::dns::Message& msg) {
+    // If the message is a negative response, but no SOA record is found in
+    // the authority section, the message cannot be cached
+    if (isNegativeResponse(msg) &&
+        !hasTheRecordInAuthoritySection(msg, RRType::SOA())){
+        return (false);
+    }
+
+    return (true);
+}
+
+} // namespace MessageUtility
+} // namespace cache
+} // namespace isc
diff --git a/src/lib/cache/message_utility.h b/src/lib/cache/message_utility.h
new file mode 100644
index 0000000..a77af07
--- /dev/null
+++ b/src/lib/cache/message_utility.h
@@ -0,0 +1,66 @@
+// 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_UTILITY_H
+#define __MESSAGE_UTILITY_H
+
+#include <dns/message.h>
+
+namespace isc {
+namespace cache {
+
+/// \brief Some utility functions to extract info from message
+///
+/// We need to check the message before cache it, for example, if no SOA
+/// record is found in the Authority section of NXDOMAIN response, the
+/// message cannot be cached
+namespace MessageUtility{
+
+/// \brief Check whether there is some type of record in
+///        Authority section
+///
+/// \param msg The response message to be checked
+/// \param type The RR type that need to check
+bool hasTheRecordInAuthoritySection(const isc::dns::Message& msg,
+                                    const isc::dns::RRType& type);
+
+/// \brief Check whetehr the message is a negative response
+///        (NXDOMAIN or NOERROR_NODATA)
+///
+/// \param msg The response message
+bool isNegativeResponse(const isc::dns::Message& msg);
+
+/// \brief Check whether the message can be cached
+///        Negative responses without SOA records SHOULD NOT be cached as there
+///        is no way to prevent the negative responses looping forever between a
+///        pair of servers even with a short TTL.
+///        Despite the DNS forming a tree of servers, with various mis-
+///        configurations it is possible to form a loop in the query graph, e.g.
+///        two servers listing each other as forwarders, various lame server
+///        configurations.  Without a TTL count down a cache negative response
+///        when received by the next server would have its TTL reset.  This
+///        negative indication could then live forever circulating between the
+///        servers involved. (Sec 5, RFC2308)
+///
+/// \param msg The response message
+bool canMessageBeCached(const isc::dns::Message& msg);
+
+} // namespace MessageUtility
+} // namespace cache
+} // namespace isc
+
+
+#endif//__MESSAGE_UTILITY_H
diff --git a/src/lib/cache/resolver_cache.cc b/src/lib/cache/resolver_cache.cc
index 0734da8..261db3c 100644
--- a/src/lib/cache/resolver_cache.cc
+++ b/src/lib/cache/resolver_cache.cc
@@ -32,12 +32,17 @@ ResolverClassCache::ResolverClassCache(const RRClass& cache_class) :
     local_zone_data_ = LocalZoneDataPtr(new LocalZoneData(cache_class_.getCode()));
     rrsets_cache_ = RRsetCachePtr(new RRsetCache(RRSET_CACHE_DEFAULT_SIZE,
                                                  cache_class_.getCode()));
+    // SOA rrset cache from negative response
+    negative_soa_cache_ = RRsetCachePtr(new RRsetCache(NEGATIVE_RRSET_CACHE_DEFAULT_SIZE,
+                                                       cache_class_.getCode()));
+
     messages_cache_ = MessageCachePtr(new MessageCache(rrsets_cache_,
                                       MESSAGE_CACHE_DEFAULT_SIZE,
-                                      cache_class_.getCode()));
+                                      cache_class_.getCode(),
+                                      negative_soa_cache_));
 }
 
-ResolverClassCache::ResolverClassCache(CacheSizeInfo cache_info) :
+ResolverClassCache::ResolverClassCache(const CacheSizeInfo& cache_info) :
     cache_class_(cache_info.cclass)
 {
     uint16_t klass = cache_class_.getCode();
@@ -45,14 +50,18 @@ ResolverClassCache::ResolverClassCache(CacheSizeInfo cache_info) :
     local_zone_data_ = LocalZoneDataPtr(new LocalZoneData(klass));
     rrsets_cache_ = RRsetCachePtr(new
                         RRsetCache(cache_info.rrset_cache_size, klass));
+    // SOA rrset cache from negative response
+    negative_soa_cache_ = RRsetCachePtr(new RRsetCache(cache_info.rrset_cache_size,
+                                                       klass));
+
     messages_cache_ = MessageCachePtr(new MessageCache(rrsets_cache_,
                                       cache_info.message_cache_size,
-                                      klass));
+                                      klass, negative_soa_cache_));
 }
 
 const RRClass&
 ResolverClassCache::getClass() const {
-    return cache_class_;
+    return (cache_class_);
 }
 
 bool
@@ -104,7 +113,7 @@ ResolverClassCache::update(const isc::dns::Message& msg) {
 }
 
 bool
-ResolverClassCache::updateRRsetCache(const isc::dns::ConstRRsetPtr rrset_ptr,
+ResolverClassCache::updateRRsetCache(const isc::dns::ConstRRsetPtr& rrset_ptr,
                                 RRsetCachePtr rrset_cache_ptr)
 {
     RRsetTrustLevel level;
@@ -120,7 +129,7 @@ ResolverClassCache::updateRRsetCache(const isc::dns::ConstRRsetPtr rrset_ptr,
 }
 
 bool
-ResolverClassCache::update(const isc::dns::ConstRRsetPtr rrset_ptr) {
+ResolverClassCache::update(const isc::dns::ConstRRsetPtr& rrset_ptr) {
     // First update local zone, then update rrset cache.
     local_zone_data_->update((*rrset_ptr.get()));
     updateRRsetCache(rrset_ptr, rrsets_cache_);
@@ -209,7 +218,7 @@ ResolverCache::update(const isc::dns::Message& msg) {
 }
 
 bool
-ResolverCache::update(const isc::dns::ConstRRsetPtr rrset_ptr) {
+ResolverCache::update(const isc::dns::ConstRRsetPtr& rrset_ptr) {
     ResolverClassCache* cc = getClassCache(rrset_ptr->getClass());
     if (cc) {
         return (cc->update(rrset_ptr));
@@ -232,10 +241,10 @@ ResolverClassCache*
 ResolverCache::getClassCache(const isc::dns::RRClass& cache_class) const {
     for (int i = 0; i < class_caches_.size(); ++i) {
         if (class_caches_[i]->getClass() == cache_class) {
-            return class_caches_[i];
+            return (class_caches_[i]);
         }
     }
-    return NULL;
+    return (NULL);
 }
 
 } // namespace cache
diff --git a/src/lib/cache/resolver_cache.h b/src/lib/cache/resolver_cache.h
index a8149e4..49818b5 100644
--- a/src/lib/cache/resolver_cache.h
+++ b/src/lib/cache/resolver_cache.h
@@ -32,6 +32,7 @@ class RRsetCache;
 //TODO a better proper default cache size
 #define MESSAGE_CACHE_DEFAULT_SIZE 10000
 #define RRSET_CACHE_DEFAULT_SIZE   20000
+#define NEGATIVE_RRSET_CACHE_DEFAULT_SIZE   10000
 
 /// \brief Cache Size Information.
 ///
@@ -44,7 +45,7 @@ public:
     /// \param cls The RRClass code
     /// \param msg_cache_size The size for the message cache
     /// \param rst_cache_size The size for the RRset cache
-    CacheSizeInfo(const isc::dns::RRClass& cls, 
+    CacheSizeInfo(const isc::dns::RRClass& cls,
                   uint32_t msg_cache_size,
                   uint32_t rst_cache_size):
                     cclass(cls),
@@ -87,7 +88,7 @@ public:
     /// \brief Construct Function.
     /// \param caches_size cache size information for each
     ///        messages/rrsets of different classes.
-    ResolverClassCache(CacheSizeInfo cache_info);
+    ResolverClassCache(const CacheSizeInfo& cache_info);
 
     /// \name Lookup Interfaces
     //@{
@@ -132,6 +133,11 @@ public:
     /// \note the function doesn't do any message validation check,
     ///       the user should make sure the message is valid, and of
     ///       the right class
+    /// TODO: Share the NXDOMAIN info between different type queries
+    ///       current implementation can only cache for the type that
+    ///       user quired, for example, if user query A record of
+    ///       a.example. and the server replied with NXDOMAIN, this
+    ///       should be cached for all the types queries of a.example.
     bool update(const isc::dns::Message& msg);
 
     /// \brief Update the rrset in the cache with the new one.
@@ -149,13 +155,13 @@ public:
     ///
     /// \note The class of the RRset must have been checked. It is not
     /// here.
-    bool update(const isc::dns::ConstRRsetPtr rrset_ptr);
+    bool update(const isc::dns::ConstRRsetPtr& rrset_ptr);
 
     /// \brief Get the RRClass this cache is for
     ///
     /// \return The RRClass of this cache
     const isc::dns::RRClass& getClass() const;
-    
+
 private:
     /// \brief Update rrset cache.
     ///
@@ -165,7 +171,7 @@ private:
     /// \return return true if the rrset is updated in the rrset cache,
     ///         or else return false if failed.
     /// \param rrset_cache_ptr The rrset cache need to be updated.
-    bool updateRRsetCache(const isc::dns::ConstRRsetPtr rrset_ptr,
+    bool updateRRsetCache(const isc::dns::ConstRRsetPtr& rrset_ptr,
                           RRsetCachePtr rrset_cache_ptr);
 
     /// \brief Class this cache is for.
@@ -181,10 +187,13 @@ private:
     /// Cache for rrsets in local zones, rrsets
     /// in it never expire.
     LocalZoneDataPtr local_zone_data_;
+    //@}
 
     /// \brief cache the rrsets parsed from the received message.
     RRsetCachePtr rrsets_cache_;
-    //@}
+
+    /// \brief cache the SOA rrset parsed from the negative response message.
+    RRsetCachePtr negative_soa_cache_;
 };
 
 class ResolverCache {
@@ -289,7 +298,7 @@ public:
     ///
     /// \overload
     ///
-    bool update(const isc::dns::ConstRRsetPtr rrset_ptr);
+    bool update(const isc::dns::ConstRRsetPtr& rrset_ptr);
 
     /// \name Cache Serialization
     //@{
diff --git a/src/lib/cache/tests/Makefile.am b/src/lib/cache/tests/Makefile.am
index 6f05f74..4763f55 100644
--- a/src/lib/cache/tests/Makefile.am
+++ b/src/lib/cache/tests/Makefile.am
@@ -38,6 +38,7 @@ run_unittests_SOURCES  += message_cache_unittest.cc
 run_unittests_SOURCES  += message_entry_unittest.cc
 run_unittests_SOURCES  += local_zone_data_unittest.cc
 run_unittests_SOURCES  += resolver_cache_unittest.cc
+run_unittests_SOURCES  += negative_cache_unittest.cc
 run_unittests_SOURCES  += cache_test_messagefromfile.h
 run_unittests_SOURCES  += cache_test_sectioncount.h
 
@@ -53,12 +54,15 @@ endif
 run_unittests_LDADD += $(top_builddir)/src/lib/cache/libcache.la
 run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
 run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
+run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
 run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 endif
 
 noinst_PROGRAMS = $(TESTS)
 
-EXTRA_DIST = testdata/message_fromWire1
+EXTRA_DIST = testdata/message_cname_referral.wire
+EXTRA_DIST += testdata/message_example_com_soa.wire
+EXTRA_DIST += testdata/message_fromWire1
 EXTRA_DIST += testdata/message_fromWire2
 EXTRA_DIST += testdata/message_fromWire3
 EXTRA_DIST += testdata/message_fromWire4
@@ -66,3 +70,11 @@ EXTRA_DIST += testdata/message_fromWire5
 EXTRA_DIST += testdata/message_fromWire6
 EXTRA_DIST += testdata/message_fromWire7
 EXTRA_DIST += testdata/message_fromWire8
+EXTRA_DIST += testdata/message_fromWire9
+EXTRA_DIST += testdata/message_large_ttl.wire
+EXTRA_DIST += testdata/message_nodata_with_soa.wire
+EXTRA_DIST += testdata/message_nxdomain_cname.wire
+EXTRA_DIST += testdata/message_nxdomain_large_ttl.wire
+EXTRA_DIST += testdata/message_nxdomain_no_soa.wire
+EXTRA_DIST += testdata/message_nxdomain_with_soa.wire
+EXTRA_DIST += testdata/message_referral.wire
diff --git a/src/lib/cache/tests/message_cache_unittest.cc b/src/lib/cache/tests/message_cache_unittest.cc
index 187216e..fc62e21 100644
--- a/src/lib/cache/tests/message_cache_unittest.cc
+++ b/src/lib/cache/tests/message_cache_unittest.cc
@@ -33,9 +33,10 @@ namespace {
 /// its internals.
 class DerivedMessageCache: public MessageCache {
 public:
-    DerivedMessageCache(boost::shared_ptr<RRsetCache> rrset_cache_,
-                        uint32_t cache_size, uint16_t message_class):
-        MessageCache(rrset_cache_, cache_size, message_class)
+    DerivedMessageCache(const RRsetCachePtr& rrset_cache,
+                        uint32_t cache_size, uint16_t message_class,
+                        const RRsetCachePtr& negative_soa_cache):
+        MessageCache(rrset_cache, cache_size, message_class, negative_soa_cache)
     {}
 
     uint16_t messages_count() {
@@ -70,13 +71,16 @@ public:
     {
         uint16_t class_ = RRClass::IN().getCode();
         rrset_cache_.reset(new DerivedRRsetCache(RRSET_CACHE_DEFAULT_SIZE, class_));
+        negative_soa_cache_.reset(new RRsetCache(NEGATIVE_RRSET_CACHE_DEFAULT_SIZE, class_));
         // Set the message cache size to 1, make it easy for unittest.
-        message_cache_.reset(new DerivedMessageCache(rrset_cache_, 1, class_ ));
+        message_cache_.reset(new DerivedMessageCache(rrset_cache_, 1, class_,
+                                                     negative_soa_cache_));
     }
 
 protected:
     boost::shared_ptr<DerivedMessageCache> message_cache_;
     boost::shared_ptr<DerivedRRsetCache> rrset_cache_;
+    RRsetCachePtr negative_soa_cache_;
     Message message_parse;
     Message message_render;
 };
@@ -134,7 +138,7 @@ TEST_F(MessageCacheTest, testUpdate) {
     EXPECT_TRUE(message_cache_->update(new_msg));
     Message new_msg_render(Message::RENDER);
     EXPECT_TRUE(message_cache_->lookup(qname, RRType::SOA(), new_msg_render));
-    EXPECT_TRUE(new_msg_render.getHeaderFlag(Message::HEADERFLAG_AA));
+    EXPECT_FALSE(new_msg_render.getHeaderFlag(Message::HEADERFLAG_AA));
 }
 
 TEST_F(MessageCacheTest, testCacheLruBehavior) {
diff --git a/src/lib/cache/tests/message_entry_unittest.cc b/src/lib/cache/tests/message_entry_unittest.cc
index a96c441..2ca33ec 100644
--- a/src/lib/cache/tests/message_entry_unittest.cc
+++ b/src/lib/cache/tests/message_entry_unittest.cc
@@ -1,5 +1,3 @@
-// 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.
@@ -38,14 +36,15 @@ namespace {
 class DerivedMessageEntry: public MessageEntry {
 public:
     DerivedMessageEntry(const isc::dns::Message& message,
-                        boost::shared_ptr<RRsetCache> rrset_cache_):
-             MessageEntry(message, rrset_cache_)
+                        const RRsetCachePtr& rrset_cache_,
+                        const RRsetCachePtr& negative_soa_cache_):
+             MessageEntry(message, rrset_cache_, negative_soa_cache_)
     {}
 
-    /// \brief Wrap the protected function so that it can be tested.   
+    /// \brief Wrap the protected function so that it can be tested.
     void parseSectionForTest(const Message& msg,
                            const Message::Section& section,
-                           uint32_t& smaller_ttl, 
+                           uint32_t& smaller_ttl,
                            uint16_t& rrset_count)
     {
         parseSection(msg, section, smaller_ttl, rrset_count);
@@ -75,18 +74,20 @@ public:
                         message_render(Message::RENDER)
     {
         rrset_cache_.reset(new RRsetCache(RRSET_CACHE_DEFAULT_SIZE, class_));
+        negative_soa_cache_.reset(new RRsetCache(NEGATIVE_RRSET_CACHE_DEFAULT_SIZE, class_));
     }
 
 protected:
     uint16_t class_;
     RRsetCachePtr rrset_cache_;
+    RRsetCachePtr negative_soa_cache_;
     Message message_parse;
     Message message_render;
 };
 
 TEST_F(MessageEntryTest, testParseRRset) {
     messageFromFile(message_parse, "message_fromWire3");
-    DerivedMessageEntry message_entry(message_parse, rrset_cache_);
+    DerivedMessageEntry message_entry(message_parse, rrset_cache_, negative_soa_cache_);
     uint32_t ttl = MAX_UINT32;
     uint16_t rrset_count = 0;
     message_entry.parseSectionForTest(message_parse, Message::SECTION_ANSWER, ttl, rrset_count);
@@ -106,7 +107,7 @@ TEST_F(MessageEntryTest, testParseRRset) {
 
 TEST_F(MessageEntryTest, testGetRRsetTrustLevel_AA) {
     messageFromFile(message_parse, "message_fromWire3");
-    DerivedMessageEntry message_entry(message_parse, rrset_cache_);
+    DerivedMessageEntry message_entry(message_parse, rrset_cache_, negative_soa_cache_);
 
     RRsetIterator rrset_iter = message_parse.beginSection(Message::SECTION_ANSWER);
     RRsetTrustLevel level = message_entry.getRRsetTrustLevelForTest(message_parse,
@@ -129,7 +130,7 @@ TEST_F(MessageEntryTest, testGetRRsetTrustLevel_AA) {
 
 TEST_F(MessageEntryTest, testGetRRsetTrustLevel_NONAA) {
     messageFromFile(message_parse, "message_fromWire4");
-    DerivedMessageEntry message_entry(message_parse, rrset_cache_);
+    DerivedMessageEntry message_entry(message_parse, rrset_cache_, negative_soa_cache_);
     RRsetIterator rrset_iter = message_parse.beginSection(Message::SECTION_ANSWER);
     RRsetTrustLevel level = message_entry.getRRsetTrustLevelForTest(message_parse,
                                                                     *rrset_iter,
@@ -151,7 +152,7 @@ TEST_F(MessageEntryTest, testGetRRsetTrustLevel_NONAA) {
 
 TEST_F(MessageEntryTest, testGetRRsetTrustLevel_CNAME) {
     messageFromFile(message_parse, "message_fromWire5");
-    DerivedMessageEntry message_entry(message_parse, rrset_cache_);
+    DerivedMessageEntry message_entry(message_parse, rrset_cache_, negative_soa_cache_);
     RRsetIterator rrset_iter = message_parse.beginSection(Message::SECTION_ANSWER);
     RRsetTrustLevel level = message_entry.getRRsetTrustLevelForTest(message_parse,
                                                                     *rrset_iter,
@@ -167,7 +168,7 @@ TEST_F(MessageEntryTest, testGetRRsetTrustLevel_CNAME) {
 
 TEST_F(MessageEntryTest, testGetRRsetTrustLevel_CNAME_and_DNAME) {
     messageFromFile(message_parse, "message_fromWire7");
-    DerivedMessageEntry message_entry(message_parse, rrset_cache_);
+    DerivedMessageEntry message_entry(message_parse, rrset_cache_, negative_soa_cache_);
     RRsetIterator rrset_iter = message_parse.beginSection(Message::SECTION_ANSWER);
     RRsetTrustLevel level = message_entry.getRRsetTrustLevelForTest(message_parse,
                                                                     *rrset_iter,
@@ -186,7 +187,7 @@ TEST_F(MessageEntryTest, testGetRRsetTrustLevel_CNAME_and_DNAME) {
 
 TEST_F(MessageEntryTest, testGetRRsetTrustLevel_DNAME_and_CNAME) {
     messageFromFile(message_parse, "message_fromWire8");
-    DerivedMessageEntry message_entry(message_parse, rrset_cache_);
+    DerivedMessageEntry message_entry(message_parse, rrset_cache_, negative_soa_cache_);
     RRsetIterator rrset_iter = message_parse.beginSection(Message::SECTION_ANSWER);
     RRsetTrustLevel level = message_entry.getRRsetTrustLevelForTest(message_parse,
                                                                     *rrset_iter,
@@ -214,7 +215,7 @@ TEST_F(MessageEntryTest, testGetRRsetTrustLevel_DNAME_and_CNAME) {
 
 TEST_F(MessageEntryTest, testGetRRsetTrustLevel_DNAME) {
     messageFromFile(message_parse, "message_fromWire6");
-    DerivedMessageEntry message_entry(message_parse, rrset_cache_);
+    DerivedMessageEntry message_entry(message_parse, rrset_cache_, negative_soa_cache_);
     RRsetIterator rrset_iter = message_parse.beginSection(Message::SECTION_ANSWER);
     RRsetTrustLevel level = message_entry.getRRsetTrustLevelForTest(message_parse,
                                                                     *rrset_iter,
@@ -239,7 +240,7 @@ TEST_F(MessageEntryTest, testGetRRsetTrustLevel_DNAME) {
 // is right
 TEST_F(MessageEntryTest, testInitMessageEntry) {
     messageFromFile(message_parse, "message_fromWire3");
-    DerivedMessageEntry message_entry(message_parse, rrset_cache_);
+    DerivedMessageEntry message_entry(message_parse, rrset_cache_, negative_soa_cache_);
     time_t expire_time = message_entry.getExpireTime();
     // 1 second should be enough to do the compare
     EXPECT_TRUE((time(NULL) + 10801) > expire_time);
@@ -247,7 +248,7 @@ TEST_F(MessageEntryTest, testInitMessageEntry) {
 
 TEST_F(MessageEntryTest, testGetRRsetEntries) {
     messageFromFile(message_parse, "message_fromWire3");
-    DerivedMessageEntry message_entry(message_parse, rrset_cache_);
+    DerivedMessageEntry message_entry(message_parse, rrset_cache_, negative_soa_cache_);
     vector<RRsetEntryPtr> vec;
 
     // the time is bigger than the smallest expire time of
@@ -258,15 +259,14 @@ TEST_F(MessageEntryTest, testGetRRsetEntries) {
 
 TEST_F(MessageEntryTest, testGenMessage) {
     messageFromFile(message_parse, "message_fromWire3");
-    DerivedMessageEntry message_entry(message_parse, rrset_cache_);
+    DerivedMessageEntry message_entry(message_parse, rrset_cache_, negative_soa_cache_);
     time_t expire_time = message_entry.getExpireTime();
 
     Message msg(Message::RENDER);
     EXPECT_FALSE(message_entry.genMessage(expire_time + 2, msg));
     message_entry.genMessage(time(NULL), msg);
     // Check whether the generated message is same with cached one.
-
-    EXPECT_TRUE(msg.getHeaderFlag(Message::HEADERFLAG_AA));
+    EXPECT_FALSE(msg.getHeaderFlag(Message::HEADERFLAG_AA));
     EXPECT_FALSE(msg.getHeaderFlag(Message::HEADERFLAG_TC));
     EXPECT_EQ(1, sectionRRsetCount(msg, Message::SECTION_ANSWER));
     EXPECT_EQ(1, sectionRRsetCount(msg, Message::SECTION_AUTHORITY));
@@ -278,4 +278,32 @@ TEST_F(MessageEntryTest, testGenMessage) {
     EXPECT_EQ(7, msg.getRRCount(Message::SECTION_ADDITIONAL));
 }
 
+TEST_F(MessageEntryTest, testMaxTTL) {
+    messageFromFile(message_parse, "message_large_ttl.wire");
+
+    // The ttl of rrset from Answer and Authority sections are both 604801 seconds
+    RRsetIterator iter = message_parse.beginSection(Message::SECTION_ANSWER);
+    EXPECT_EQ(604801, (*iter)->getTTL().getValue());
+    iter = message_parse.beginSection(Message::SECTION_AUTHORITY);
+    EXPECT_EQ(604801, (*iter)->getTTL().getValue());
+
+    DerivedMessageEntry message_entry(message_parse, rrset_cache_, negative_soa_cache_);
+
+    // The ttl is limited to 604800 seconds (7days)
+    EXPECT_EQ(time(NULL) + 604800, message_entry.getExpireTime());
+}
+
+TEST_F(MessageEntryTest, testMaxNegativeTTL) {
+    messageFromFile(message_parse, "message_nxdomain_large_ttl.wire");
+
+    // The ttl of rrset Authority sections are 10801 seconds
+    RRsetIterator iter = message_parse.beginSection(Message::SECTION_AUTHORITY);
+    EXPECT_EQ(10801, (*iter)->getTTL().getValue());
+
+    DerivedMessageEntry message_entry(message_parse, rrset_cache_, negative_soa_cache_);
+
+    // The ttl is limited to 10800 seconds (3 hours)
+    EXPECT_EQ(time(NULL) + 10800, message_entry.getExpireTime());
+}
+
 }   // namespace
diff --git a/src/lib/cache/tests/negative_cache_unittest.cc b/src/lib/cache/tests/negative_cache_unittest.cc
new file mode 100644
index 0000000..56d777d
--- /dev/null
+++ b/src/lib/cache/tests/negative_cache_unittest.cc
@@ -0,0 +1,242 @@
+// 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 <config.h>
+#include <string>
+#include <gtest/gtest.h>
+#include <dns/rrset.h>
+#include <dns/rcode.h>
+#include "resolver_cache.h"
+#include "cache_test_messagefromfile.h"
+
+using namespace isc::cache;
+using namespace isc::dns;
+using namespace std;
+
+namespace {
+
+class NegativeCacheTest: public testing::Test{
+public:
+    NegativeCacheTest() {
+        vector<CacheSizeInfo> vec;
+        CacheSizeInfo class_in(RRClass::IN(), 100, 200);
+        vec.push_back(class_in);
+        cache = new ResolverCache(vec);
+    }
+
+    ~NegativeCacheTest() {
+        delete cache;
+    }
+
+    ResolverCache *cache;
+};
+
+TEST_F(NegativeCacheTest, testNXDOMAIN){
+    // NXDOMAIN response for nonexist.example.com
+    Message msg_nxdomain(Message::PARSE);
+    messageFromFile(msg_nxdomain, "message_nxdomain_with_soa.wire");
+    cache->update(msg_nxdomain);
+
+    msg_nxdomain.makeResponse();
+
+    Name non_exist_qname("nonexist.example.com.");
+    EXPECT_TRUE(cache->lookup(non_exist_qname, RRType::A(), RRClass::IN(), msg_nxdomain));
+
+    RRsetIterator iter = msg_nxdomain.beginSection(Message::SECTION_AUTHORITY);
+    RRsetPtr rrset_ptr = *iter;
+
+    // The TTL should equal to the TTL of SOA record
+    const RRTTL& nxdomain_ttl1 = rrset_ptr->getTTL();
+    EXPECT_EQ(nxdomain_ttl1.getValue(), 86400);
+
+    // SOA response for example.com
+    Message msg_example_com_soa(Message::PARSE);
+    messageFromFile(msg_example_com_soa, "message_example_com_soa.wire");
+    cache->update(msg_example_com_soa);
+
+    msg_example_com_soa.makeResponse();
+    Name soa_qname("example.com.");
+    EXPECT_TRUE(cache->lookup(soa_qname, RRType::SOA(), RRClass::IN(), msg_example_com_soa));
+
+    iter = msg_example_com_soa.beginSection(Message::SECTION_ANSWER);
+    rrset_ptr = *iter;
+
+    // The TTL should equal to the TTL of SOA record in answer section
+    const RRTTL& soa_ttl = rrset_ptr->getTTL();
+    EXPECT_EQ(soa_ttl.getValue(), 172800);
+
+    sleep(1);
+
+    // Query nonexist.example.com again
+    Message msg_nxdomain2(Message::PARSE);
+    messageFromFile(msg_nxdomain2, "message_nxdomain_with_soa.wire");
+    msg_nxdomain2.makeResponse();
+
+    EXPECT_TRUE(cache->lookup(non_exist_qname, RRType::A(), RRClass::IN(), msg_nxdomain2));
+    iter = msg_nxdomain2.beginSection(Message::SECTION_AUTHORITY);
+    rrset_ptr = *iter;
+
+    // The TTL should equal to the TTL of negative response SOA record
+    const RRTTL& nxdomain_ttl2 = rrset_ptr->getTTL();
+    EXPECT_TRUE(86398 <= nxdomain_ttl2.getValue() && nxdomain_ttl2.getValue() <= 86399);
+    // No RRset in ANSWER section
+    EXPECT_TRUE(msg_nxdomain2.getRRCount(Message::SECTION_ANSWER) == 0);
+    // Check that only one SOA record exist in AUTHORITY section
+    EXPECT_TRUE(msg_nxdomain2.getRRCount(Message::SECTION_AUTHORITY) == 1);
+    iter = msg_nxdomain2.beginSection(Message::SECTION_AUTHORITY);
+    rrset_ptr = *iter;
+    EXPECT_TRUE(rrset_ptr->getType() == RRType::SOA());
+
+    // Check the normal SOA cache again
+    Message msg_example_com_soa2(Message::PARSE);
+    messageFromFile(msg_example_com_soa2, "message_example_com_soa.wire");
+    msg_example_com_soa2.makeResponse();
+    EXPECT_TRUE(cache->lookup(soa_qname, RRType::SOA(), RRClass::IN(), msg_example_com_soa2));
+
+    iter = msg_example_com_soa2.beginSection(Message::SECTION_ANSWER);
+    rrset_ptr = *iter;
+    const RRTTL& soa_ttl2 = rrset_ptr->getTTL();
+    // The TTL should equal to the TTL of SOA record in answer section
+    EXPECT_TRUE(172798 <= soa_ttl2.getValue() && soa_ttl2.getValue() <= 172799);
+}
+
+TEST_F(NegativeCacheTest, testNXDOMAINWithoutSOA){
+    // NXDOMAIN response for nonexist.example.com
+    Message msg_nxdomain(Message::PARSE);
+    messageFromFile(msg_nxdomain, "message_nxdomain_no_soa.wire");
+    cache->update(msg_nxdomain);
+
+    msg_nxdomain.makeResponse();
+
+    Name non_exist_qname("nonexist.example.com.");
+    // The message should not be cached
+    EXPECT_FALSE(cache->lookup(non_exist_qname, RRType::A(), RRClass::IN(), msg_nxdomain));
+}
+
+TEST_F(NegativeCacheTest, testNXDOMAINCname){
+    // a.example.org points to b.example.org
+    // b.example.org points to c.example.org
+    // c.example.org does not exist
+    Message msg_nxdomain_cname(Message::PARSE);
+    messageFromFile(msg_nxdomain_cname, "message_nxdomain_cname.wire");
+    cache->update(msg_nxdomain_cname);
+
+    msg_nxdomain_cname.makeResponse();
+
+    Name a_example_org("a.example.org.");
+    // The message should be cached
+    EXPECT_TRUE(cache->lookup(a_example_org, RRType::A(), RRClass::IN(), msg_nxdomain_cname));
+
+    EXPECT_EQ(msg_nxdomain_cname.getRcode().getCode(), Rcode::NXDOMAIN().getCode());
+
+    // It should include 2 CNAME records in Answer section
+    EXPECT_TRUE(msg_nxdomain_cname.getRRCount(Message::SECTION_ANSWER) == 2);
+    RRsetIterator iter = msg_nxdomain_cname.beginSection(Message::SECTION_ANSWER);
+    EXPECT_TRUE((*iter)->getType() == RRType::CNAME());
+    ++iter;
+    EXPECT_TRUE((*iter)->getType() == RRType::CNAME());
+
+    // It should include 1 SOA record in Authority section
+    EXPECT_TRUE(msg_nxdomain_cname.getRRCount(Message::SECTION_AUTHORITY) == 1);
+    iter = msg_nxdomain_cname.beginSection(Message::SECTION_AUTHORITY);
+    EXPECT_TRUE((*iter)->getType() == RRType::SOA());
+
+    const RRTTL& soa_ttl = (*iter)->getTTL();
+    EXPECT_EQ(soa_ttl.getValue(), 600);
+}
+
+TEST_F(NegativeCacheTest, testNoerrorNodata){
+    // NODATA/NOERROR response for MX type query of example.com
+    Message msg_nodata(Message::PARSE);
+    messageFromFile(msg_nodata, "message_nodata_with_soa.wire");
+    cache->update(msg_nodata);
+
+    msg_nodata.makeResponse();
+
+    Name example_dot_com("example.com.");
+    EXPECT_TRUE(cache->lookup(example_dot_com, RRType::MX(), RRClass::IN(), msg_nodata));
+
+    RRsetIterator iter = msg_nodata.beginSection(Message::SECTION_AUTHORITY);
+    RRsetPtr rrset_ptr = *iter;
+
+    // The TTL should equal to the TTL of SOA record
+    const RRTTL& nodata_ttl1 = rrset_ptr->getTTL();
+    EXPECT_EQ(nodata_ttl1.getValue(), 86400);
+
+
+    // Normal SOA response for example.com
+    Message msg_example_com_soa(Message::PARSE);
+    messageFromFile(msg_example_com_soa, "message_example_com_soa.wire");
+    cache->update(msg_example_com_soa);
+
+    msg_example_com_soa.makeResponse();
+    Name soa_qname("example.com.");
+    EXPECT_TRUE(cache->lookup(soa_qname, RRType::SOA(), RRClass::IN(), msg_example_com_soa));
+
+    iter = msg_example_com_soa.beginSection(Message::SECTION_ANSWER);
+    rrset_ptr = *iter;
+
+    // The TTL should equal to the TTL of SOA record in answer section
+    const RRTTL& soa_ttl = rrset_ptr->getTTL();
+    EXPECT_EQ(soa_ttl.getValue(), 172800);
+
+    // Query MX record of example.com again
+    Message msg_nodata2(Message::PARSE);
+    messageFromFile(msg_nodata2, "message_nodata_with_soa.wire");
+    msg_nodata2.makeResponse();
+
+    sleep(1);
+
+    EXPECT_TRUE(cache->lookup(example_dot_com, RRType::MX(), RRClass::IN(), msg_nodata2));
+
+    // No answer
+    EXPECT_EQ(msg_nodata2.getRRCount(Message::SECTION_ANSWER), 0);
+    // One SOA record in authority section
+    EXPECT_EQ(msg_nodata2.getRRCount(Message::SECTION_AUTHORITY), 1);
+
+    iter = msg_nodata2.beginSection(Message::SECTION_AUTHORITY);
+    rrset_ptr = *iter;
+
+    // The TTL should equal to the TTL of negative response SOA record and counted down
+    const RRTTL& nodata_ttl2 = rrset_ptr->getTTL();
+    EXPECT_TRUE(86398 <= nodata_ttl2.getValue() && nodata_ttl2.getValue() <= 86399);
+}
+
+TEST_F(NegativeCacheTest, testReferralResponse){
+    // CNAME exist, but it points to out of zone data, so the server give some reference data
+    Message msg_cname_referral(Message::PARSE);
+    messageFromFile(msg_cname_referral, "message_cname_referral.wire");
+    cache->update(msg_cname_referral);
+
+    msg_cname_referral.makeResponse();
+
+    Name x_example_org("x.example.org.");
+    EXPECT_TRUE(cache->lookup(x_example_org, RRType::A(), RRClass::IN(), msg_cname_referral));
+
+    // The Rcode should be NOERROR
+    EXPECT_EQ(msg_cname_referral.getRcode().getCode(), Rcode::NOERROR().getCode());
+
+    // One CNAME record in Answer section
+    EXPECT_EQ(msg_cname_referral.getRRCount(Message::SECTION_ANSWER), 1);
+    RRsetIterator iter = msg_cname_referral.beginSection(Message::SECTION_ANSWER);
+    EXPECT_EQ((*iter)->getType(), RRType::CNAME());
+
+    // 13 NS records in Authority section
+    EXPECT_EQ(msg_cname_referral.getRRCount(Message::SECTION_AUTHORITY), 13);
+    iter = msg_cname_referral.beginSection(Message::SECTION_AUTHORITY);
+    EXPECT_EQ((*iter)->getType(), RRType::NS());
+}
+
+}
diff --git a/src/lib/cache/tests/resolver_cache_unittest.cc b/src/lib/cache/tests/resolver_cache_unittest.cc
index 20dcec9..7b686f5 100644
--- a/src/lib/cache/tests/resolver_cache_unittest.cc
+++ b/src/lib/cache/tests/resolver_cache_unittest.cc
@@ -53,7 +53,7 @@ TEST_F(ResolverCacheTest, testUpdateMessage) {
 
     msg.makeResponse();
     EXPECT_TRUE(cache->lookup(qname, RRType::SOA(), RRClass::IN(), msg));
-    EXPECT_TRUE(msg.getHeaderFlag(Message::HEADERFLAG_AA));
+    EXPECT_FALSE(msg.getHeaderFlag(Message::HEADERFLAG_AA));
 
     // Test whether the old message can be updated
     Message new_msg(Message::PARSE);
diff --git a/src/lib/cache/tests/rrset_cache_unittest.cc b/src/lib/cache/tests/rrset_cache_unittest.cc
index 90b24df..b61f5c4 100644
--- a/src/lib/cache/tests/rrset_cache_unittest.cc
+++ b/src/lib/cache/tests/rrset_cache_unittest.cc
@@ -74,7 +74,7 @@ TEST_F(RRsetCacheTest, lookup) {
     // Check whether the expired rrset entry will be removed automatically
     // when looking up.
     Name name_test("test.example.com.");
-    updateRRsetCache(cache_, name_test, 0);
+    updateRRsetCache(cache_, name_test, 0); // Add a rrset with TTL 0 to cache.
     EXPECT_FALSE(cache_.lookup(name_test, RRType::A()));
 }
 
diff --git a/src/lib/cache/tests/testdata/message_cname_referral.wire b/src/lib/cache/tests/testdata/message_cname_referral.wire
new file mode 100644
index 0000000..7c6b285
--- /dev/null
+++ b/src/lib/cache/tests/testdata/message_cname_referral.wire
@@ -0,0 +1,56 @@
+#
+# Request A record for x.example.org, the CNAME record exist for x.example.org
+# it poinst to x.example.net, but the server has no idea whether x.example.net exist
+# so it give some NS records for reference
+#
+# Transaction ID: 0xaf71
+# Flags: 0x8480 (Standard query response, No error)
+af71 8480
+# Questions: 1
+# Answer RRs: 1
+# Authority RRs: 13
+# Additional RRs: 0
+00 01 00 01 00 0d 00 00
+##
+## query
+##
+# x.example.org: type A, class IN
+##
+## Answer
+##
+# x.example.org: type CNAME, class IN, cname x.example.net
+# TTL: 360s
+01 78 07 65 78 61 6d 70 6c 65 03 6f 72 67 00 00 01 00 01
+c0 0c 00 05 00 01 00 00 0e 10 00 0f 01 78 07 65 78
+61 6d 70 6c 65 03 6e 65 74 00
+##
+## Authority
+##
+# TTL:518400
+# <Root>: type NS, class IN, ns G.ROOT-SERVERS.net
+# <Root>: type NS, class IN, ns E.ROOT-SERVERS.net
+# <Root>: type NS, class IN, ns J.ROOT-SERVERS.net
+# <Root>: type NS, class IN, ns L.ROOT-SERVERS.net
+# <Root>: type NS, class IN, ns H.ROOT-SERVERS.net
+# <Root>: type NS, class IN, ns I.ROOT-SERVERS.net
+# <Root>: type NS, class IN, ns K.ROOT-SERVERS.net
+# <Root>: type NS, class IN, ns M.ROOT-SERVERS.net
+# <Root>: type NS, class IN, ns F.ROOT-SERVERS.net
+# <Root>: type NS, class IN, ns B.ROOT-SERVERS.net
+# <Root>: type NS, class IN, ns C.ROOT-SERVERS.net
+# <Root>: type NS, class IN, ns D.ROOT-SERVERS.net
+# <Root>: type NS, class IN, ns A.ROOT-SERVERS.net
+00 00 02 00 01 00
+07 e9 00 00 11 01 47 0c 52 4f 4f 54 2d 53 45 52
+56 45 52 53 c0 35 00 00 02 00 01 00 07 e9 00 00
+04 01 45 c0 47 00 00 02 00 01 00 07 e9 00 00 04
+01 4a c0 47 00 00 02 00 01 00 07 e9 00 00 04 01
+4c c0 47 00 00 02 00 01 00 07 e9 00 00 04 01 48
+c0 47 00 00 02 00 01 00 07 e9 00 00 04 01 49 c0
+47 00 00 02 00 01 00 07 e9 00 00 04 01 4b c0 47
+00 00 02 00 01 00 07 e9 00 00 04 01 4d c0 47 00
+00 02 00 01 00 07 e9 00 00 04 01 46 c0 47 00 00
+02 00 01 00 07 e9 00 00 04 01 42 c0 47 00 00 02
+00 01 00 07 e9 00 00 04 01 43 c0 47 00 00 02 00
+01 00 07 e9 00 00 04 01 44 c0 47 00 00 02 00 01
+00 07 e9 00 00 04 01 41 c0 47
diff --git a/src/lib/cache/tests/testdata/message_example_com_soa.wire b/src/lib/cache/tests/testdata/message_example_com_soa.wire
new file mode 100644
index 0000000..6d70ed7
--- /dev/null
+++ b/src/lib/cache/tests/testdata/message_example_com_soa.wire
@@ -0,0 +1,57 @@
+#
+# SOA request response for example.com 
+#
+# Transaction ID: 0x7f36
+# Flags: 0x8400 (Standard query response, No error)
+7f 36 84 00
+# Questions: 1
+00 01
+# Answer RRs: 1
+00 01
+# Authority RRs: 2
+00 02
+# Additional RRs: 0
+00 00
+##
+## Query
+##
+# Name: example.com
+07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
+# Type: SOA (Start of zone of authority)
+00 06
+# Class: IN (0x0001)
+00 01
+##
+## Answers
+##
+# Name: example.com
+c0 0c
+# Type: SOA (Start of zone of authority)
+00 06
+# Class: IN (0x0001)
+00 01
+# Time to live: 2 days (172800s)
+00 02 a3 00
+# Data length: 49
+00 31
+# Primary name server: dns1.icann.org
+04 64 6e 73 31 05 69 63 61 6e 6e 03 6f 72 67 00
+# Responsible authority's mailbox: hostmaster.icann.org
+0a 68 6f 73 74 6d 61 73 74 65 72 c0 2e
+# Serial number: 2010072301
+77 cf 44 ed
+# Refresh interval: 2 hours
+00 00 1c 20
+# Retry interval: 1 hour
+00 00 0e 10
+# Expiration limit: 14 days
+00 12 75 00
+# Minimum TTL: 1 day
+00 01 51 80
+##
+## Authoritative nameservers
+##
+# example.com: type NS, class IN, ns a.iana-servers.net
+c0 0c 00 02 00 01 00 02 a3 00 00 14 01 61 0c 69 61 6e 61 2d 73 65 72 76 65 72 73 03 6e 65 74 00
+# example.com: type NS, class IN, ns b.iana-servers.net
+c0 0c 00 02 00 01 00 02 a3 00 00 04 01 62 c0 68
diff --git a/src/lib/cache/tests/testdata/message_fromWire9 b/src/lib/cache/tests/testdata/message_fromWire9
index d155e8c..e2dbd06 100644
--- a/src/lib/cache/tests/testdata/message_fromWire9
+++ b/src/lib/cache/tests/testdata/message_fromWire9
@@ -1,4 +1,6 @@
 #
+# The TTL for a record in answer section is 0, so it
+# will expire immediately after being cached.
 #
 # A simple DNS response message
 # ID = 0x1035
@@ -6,7 +8,7 @@
 # QDCOUNT=1, ANCOUNT=2, other COUNTS=0
 # Question: test.example.org. IN A
 # Answer:
-#  test.example.org. 3600 IN A 192.0.2.1
+#  test.example.org. 0000 IN A 192.0.2.1
 #  test.example.org. 7200 IN A 192.0.2.2
 #
 1035 8500
diff --git a/src/lib/cache/tests/testdata/message_large_ttl.wire b/src/lib/cache/tests/testdata/message_large_ttl.wire
new file mode 100644
index 0000000..6e152ef
--- /dev/null
+++ b/src/lib/cache/tests/testdata/message_large_ttl.wire
@@ -0,0 +1,31 @@
+#
+# A response that the TTL is quite large(> 7days)
+#
+##
+## header
+##
+# Transaction ID: 0x0d1f
+# Flags: 0x8580 (Standard query response, No error)
+0d1f 8580
+# Questions: 1
+# Answer RRs: 1
+# Authority RRs: 3
+# Additional RRs: 3
+00 01 00 01 00 01 00 00
+##
+## Query
+##
+# test.example.org: type A, class IN
+04 74 65 73 74 07 65 78 61 6d 70 6c 65 03 6f 72 67 00 00 01 00 01
+##
+## Answer
+##
+# test.example.org: type A, class IN, addr 127.0.0.1
+# TTL: 7 days, 1 second (604801 seconds)
+c0 0c 00 01 00 01 00 09 3a 81 00 04 7f 00 00 01
+##
+## Authority
+##
+# example.org: type NS, class IN, ns ns1.example.org
+# TTL: 7 days, 1 second (604801 seconds)
+c0 11 00 02 00 01 00 09 3a 81 00 06 03 6e 73 31 c0 11
diff --git a/src/lib/cache/tests/testdata/message_nodata_with_soa.wire b/src/lib/cache/tests/testdata/message_nodata_with_soa.wire
new file mode 100644
index 0000000..67a4adc
--- /dev/null
+++ b/src/lib/cache/tests/testdata/message_nodata_with_soa.wire
@@ -0,0 +1,32 @@
+#
+# NOERROR/NODATA response with SOA record
+#
+##
+## header
+##
+#Transaction ID: 0x0284
+#Flags: 0x8500 (Standard query response, No error)
+0284 8500
+#Question:1
+00 01
+#Answer RRs:0
+00 00
+#Authority RRs:1
+00 01
+#Additional RRs:0
+00 00
+##
+## Queries
+##
+# example.com: type MX, class IN
+07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 00 0f 00 01
+##
+## Authoritative nameservers
+##
+# example.com: type SOA, class IN, mname dns1.icann.org
+# TTL:86400
+c0 0c 00
+06 00 01 00 01 51 80 00 31 04 64 6e 73 31 05 69
+63 61 6e 6e 03 6f 72 67 00 0a 68 6f 73 74 6d 61
+73 74 65 72 c0 2e 77 cf 44 ed 00 00 1c 20 00 00
+0e 10 00 12 75 00 00 01 51 80
diff --git a/src/lib/cache/tests/testdata/message_nxdomain_cname.wire b/src/lib/cache/tests/testdata/message_nxdomain_cname.wire
new file mode 100644
index 0000000..1ae3d76
--- /dev/null
+++ b/src/lib/cache/tests/testdata/message_nxdomain_cname.wire
@@ -0,0 +1,36 @@
+#
+# NXDOMAIN response
+# The cname type of a.example.org exist, it points to b.example.org
+# b.example.org points to c.example.org
+# but c.example.org does not exist
+#
+##
+## header
+##
+# Transaction ID: 0xc2aa
+# Flags: 0x8583 (Standard query response, No such name)
+c2aa 8583
+# Questions: 1
+# Answer RRs: 2
+# Authority RRs: 1
+# dditional RRs: 0
+00 01 00 02 00 01 00 00
+##
+## Queries
+##
+# a.example.org: type A, class IN
+01 61 07 65 78 61 6d 70 6c 65 03 6f 72 67 00 00 01 00 01
+##
+## Answers
+##
+# a.example.org: type CNAME, class IN, cname b.example.org
+c0 0c 00 05 00 01 00 00 0e 10 00 04 01 62 c0 0e
+# b.example.org: type CNAME, class IN, cname c.example.org
+c0 2b 00 05 00 01 00 00 0e 10 00 04 01 63 c0 0e
+##
+## Authority
+##
+# example.org: type SOA, class IN, mname ns1.example.org
+c0 0e 00 06 00 01 00 00 02 58 00 22 03 6e 73 31 c0
+0e 05 61 64 6d 69 6e c0 0e 00 00 04 d2 00 00 0e
+10 00 00 07 08 00 24 ea 00 00 00 02 58
diff --git a/src/lib/cache/tests/testdata/message_nxdomain_large_ttl.wire b/src/lib/cache/tests/testdata/message_nxdomain_large_ttl.wire
new file mode 100644
index 0000000..142d8cf
--- /dev/null
+++ b/src/lib/cache/tests/testdata/message_nxdomain_large_ttl.wire
@@ -0,0 +1,25 @@
+#
+# Negative response (NXDOMAIN) with large TTL (3hours + 1second)
+#
+##
+## Header
+##
+# Transaction ID: 0xb1fe
+# Flags: 0x8583 (Standard query response, No such name)
+b1fe 8583
+# Questions: 1
+# Authority RRs: 1
+00 01 00 00 00 01 00 00
+##
+## Query
+##
+# c.example.org: type A, class IN
+01 63 07 65 78 61 6d 70 6c 65 03 6f 72 67 00 00 01 00 01
+##
+## Authority
+##
+# example.org: type SOA, class IN, mname ns1.example.org
+# TTL: 3 Hourse, 1 second (10801seconds)
+c0 0e 00 06 00 01 00 00 2a 31 00 22 03 6e 73 31 c0
+0e 05 61 64 6d 69 6e c0 0e 00 00 04 d2 00 00 0e
+10 00 00 07 08 00 24 ea 00 00 00 2a 31
diff --git a/src/lib/cache/tests/testdata/message_nxdomain_no_soa.wire b/src/lib/cache/tests/testdata/message_nxdomain_no_soa.wire
new file mode 100644
index 0000000..b138cc2
--- /dev/null
+++ b/src/lib/cache/tests/testdata/message_nxdomain_no_soa.wire
@@ -0,0 +1,26 @@
+#
+# NXDOMAIN response with SOA record
+#
+##
+## Header
+##
+# ID = 0x3da0
+# QR = 1 (response), Opcode = 0, AA = 1, RCODE=3 (NXDOMAIN)
+3da0 8403
+# Question : 1
+00 01
+# Answer : 0
+00 00
+# Authority : 0
+00 00
+# Additional : 0
+00 00
+##
+## Query
+##
+#(4) n  o  n  e  x  i  s  t (7) e  x  a  m  p  l  e (3) c  o  m (0)
+  08 6e 6f 6e 65 78 69 73 74 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
+# Type:A
+00 01
+# class: IN
+00 01
diff --git a/src/lib/cache/tests/testdata/message_nxdomain_with_soa.wire b/src/lib/cache/tests/testdata/message_nxdomain_with_soa.wire
new file mode 100644
index 0000000..04fd66f
--- /dev/null
+++ b/src/lib/cache/tests/testdata/message_nxdomain_with_soa.wire
@@ -0,0 +1,55 @@
+#
+# NXDOMAIN response with SOA record
+#
+##
+## Header
+##
+# ID = 0x3da0
+# QR = 1 (response), Opcode = 0, AA = 1, RCODE=3 (NXDOMAIN)
+3da0 8403
+# Question : 1
+00 01
+# Answer : 0
+00 00
+# Authority : 1
+00 01
+# Additional : 0
+00 00
+##
+## Query
+##
+#(4) n  o  n  e  x  i  s  t (7) e  x  a  m  p  l  e (3) c  o  m (0)
+  08 6e 6f 6e 65 78 69 73 74 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
+# Type:A
+00 01
+# class: IN
+00 01
+##
+## Authority
+## 
+# name: example.com
+c0 15
+# Type:SOA
+00 06
+# Class: IN
+00 01
+# TTL: 86400
+00 01 51 80
+# Data Length: 49
+00 31
+# Name Server:
+#(4) d  n  s  1 (5) i   c a  n  n (3) o  r  g (0)
+  04 64 6e 73 31 05 69 63 61 6e 6e 03 6f 72 67 00
+# MX: 
+# (10) h  o   s  t  m  a  s  t  e  r .icann.org.
+  0a   68 6f 73 74 6d 61 73 74 65 72 c0 37
+# Serial Number:2010072301
+77 cf 44 ed
+# Refresh Interval:2 hours
+00 00 1c 20
+# Retry Interval: 1 hour
+00 00 0e 10
+# Expiration: 14 days
+00 12 75 00
+# Minimum TTL 1 day
+00 01 51 80
diff --git a/src/lib/cache/tests/testdata/message_referral.wire b/src/lib/cache/tests/testdata/message_referral.wire
new file mode 100644
index 0000000..bb897ca
--- /dev/null
+++ b/src/lib/cache/tests/testdata/message_referral.wire
@@ -0,0 +1,36 @@
+#
+# Query x.example.net to nameservr of example.org
+# It will just give some referral info
+#
+#
+# Transaction ID: 0x8b61
+# Flags: 0x8080 (Standard query response, No error)
+8b61 8080
+# Questions: 1
+# Authority RRs: 13
+00 01 00 00 00 0d 00 00
+##
+## Query
+##
+# x.example.net: type A, class IN
+01 78 07 65 78 61 6d 70 6c 65 03 6e 65 74 00 00 01 00 01
+##
+## Authority
+##
+# <Root>: type NS, class IN, ns B.ROOT-SERVERS.net
+# <Root>: type NS, class IN, ns M.ROOT-SERVERS.net
+# ...
+# <Root>: type NS, class IN, ns H.ROOT-SERVERS.net
+00 00 02 00 01 00 07 e9 00 00 11 01 42 0c 52 4f 4f
+54 2d 53 45 52 56 45 52 53 c0 16 00 00 02 00 01
+00 07 e9 00 00 04 01 4d c0 2c 00 00 02 00 01 00
+07 e9 00 00 04 01 44 c0 2c 00 00 02 00 01 00 07
+e9 00 00 04 01 4c c0 2c 00 00 02 00 01 00 07 e9
+00 00 04 01 4b c0 2c 00 00 02 00 01 00 07 e9 00
+00 04 01 43 c0 2c 00 00 02 00 01 00 07 e9 00 00
+04 01 41 c0 2c 00 00 02 00 01 00 07 e9 00 00 04
+01 49 c0 2c 00 00 02 00 01 00 07 e9 00 00 04 01
+45 c0 2c 00 00 02 00 01 00 07 e9 00 00 04 01 46
+c0 2c 00 00 02 00 01 00 07 e9 00 00 04 01 4a c0
+2c 00 00 02 00 01 00 07 e9 00 00 04 01 47 c0 2c
+00 00 02 00 01 00 07 e9 00 00 04 01 48 c0 2c
diff --git a/src/lib/cc/data.h b/src/lib/cc/data.h
index 4f121d5..0a363f4 100644
--- a/src/lib/cc/data.h
+++ b/src/lib/cc/data.h
@@ -222,6 +222,7 @@ public:
 
     /// Sets the ElementPtr at the given key
     /// \param name The key of the Element to set
+    /// \param element The ElementPtr to set at the given key.
     virtual void set(const std::string& name, ConstElementPtr element);
 
     /// Remove the ElementPtr at the given key
@@ -315,10 +316,11 @@ public:
     /// Creates an Element from the given input stream, where we keep
     /// track of the location in the stream for error reporting.
     ///
-    /// \param in The string to parse the element from
+    /// \param in The string to parse the element from.
+    /// \param file The input file name.
     /// \param line A reference to the int where the function keeps
     /// track of the current line.
-    /// \param line A reference to the int where the function keeps
+    /// \param pos A reference to the int where the function keeps
     /// track of the current position within the current line.
     /// \return An ElementPtr that contains the element(s) specified
     /// in the given input stream.
@@ -548,18 +550,18 @@ void merge(ElementPtr element, ConstElementPtr other);
 ///
 /// \brief Insert the Element as a string into stream.
 ///
-/// This method converts the \c ElemetPtr into a string with
+/// This method converts the \c ElementPtr into a string with
 /// \c Element::str() and inserts it into the
 /// output stream \c out.
 ///
 /// This function overloads the global operator<< to behave as described in
 /// ostream::operator<< but applied to \c ElementPtr objects.
 ///
-/// \param os A \c std::ostream object on which the insertion operation is
+/// \param out A \c std::ostream object on which the insertion operation is
 /// performed.
 /// \param e The \c ElementPtr object to insert.
 /// \return A reference to the same \c std::ostream object referenced by
-/// parameter \c os after the insertion operation.
+/// parameter \c out after the insertion operation.
 std::ostream& operator<<(std::ostream& out, const Element& e);
 
 bool operator==(const Element& a, const Element& b);
diff --git a/src/lib/cc/session.h b/src/lib/cc/session.h
index 6d8696d..e91dd76 100644
--- a/src/lib/cc/session.h
+++ b/src/lib/cc/session.h
@@ -99,7 +99,7 @@ namespace isc {
             /// \brief Sets the default timeout for blocking reads
             ///        in this session to the given number of milliseconds
             /// \param milliseconds the timeout for blocking reads in
-            ///        milliseconds, if this is set to 0, reads will block
+            ///        milliseconds; if this is set to 0, reads will block
             ///        forever.
             virtual void setTimeout(size_t milliseconds) = 0;
 
diff --git a/src/lib/config/module_spec.h b/src/lib/config/module_spec.h
index 0e06a62..ab6e273 100644
--- a/src/lib/config/module_spec.h
+++ b/src/lib/config/module_spec.h
@@ -53,6 +53,8 @@ namespace isc { namespace config {
         /// Create a \c ModuleSpec instance with the given data as
         /// the specification
         /// \param e The Element containing the data specification
+        /// \param check If false, the module specification in the file
+        /// is not checked to be of the correct form.
         explicit ModuleSpec(isc::data::ConstElementPtr e,
                             const bool check = true)
             throw(ModuleSpecError);
@@ -86,6 +88,8 @@ namespace isc { namespace config {
         // configuration specification
         /// Validates the given configuration data for this specification.
         /// \param data The base \c Element of the data to check
+        /// \param full If true, all non-optional configuration parameters
+        /// must be specified.
         /// \return true if the data conforms to the specification,
         /// false otherwise.
         bool validateConfig(isc::data::ConstElementPtr data,
diff --git a/src/lib/datasrc/data_source.cc b/src/lib/datasrc/data_source.cc
index 829d1fc..0ee656f 100644
--- a/src/lib/datasrc/data_source.cc
+++ b/src/lib/datasrc/data_source.cc
@@ -189,6 +189,19 @@ checkCache(QueryTask& task, RRsetList& target) {
                 rrsets.addRRset(rrset);
                 target.append(rrsets);
             }
+
+            // Reset the referral flag and treat CNAME as "not found".
+            // This emulates the behavior of the sqlite3 data source.
+            // XXX: this is not ideal in that the responsibility for handling
+            // operation specific cases is spread over various classes at
+            // different abstraction levels.  For longer terms we should
+            // revisit the whole datasource/query design, and clarify this
+            // point better.
+            flags &= ~DataSrc::REFERRAL;
+            if ((flags & DataSrc::CNAME_FOUND) != 0) {
+                flags &= ~DataSrc::CNAME_FOUND;
+                flags |= DataSrc::TYPE_NOT_FOUND;
+            }
             task.flags = flags;
             return (true);
         }
diff --git a/src/lib/datasrc/memory_datasrc.h b/src/lib/datasrc/memory_datasrc.h
index 5a2c74e..99bb4e8 100644
--- a/src/lib/datasrc/memory_datasrc.h
+++ b/src/lib/datasrc/memory_datasrc.h
@@ -289,7 +289,7 @@ public:
     ///   - \c result::PARTIALMATCH: A zone whose origin is a
     //    super domain of \c name is found (but there is no exact match)
     ///   - \c result::NOTFOUND: For all other cases.
-    /// - \c zone: A <Boost> shared pointer to the found \c Zone object if one
+    /// - \c zone: A "Boost" shared pointer to the found \c Zone object if one
     //  is found; otherwise \c NULL.
     ///
     /// This method never throws an exception.
diff --git a/src/lib/datasrc/rbtree.h b/src/lib/datasrc/rbtree.h
index bd04066..03a6967 100644
--- a/src/lib/datasrc/rbtree.h
+++ b/src/lib/datasrc/rbtree.h
@@ -533,12 +533,9 @@ private:
 
 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;
+    // Since each node in rbtree stores at least one label, it's also equal
+    // to the possible maximum level.
+    const static int RBT_MAX_LEVEL = isc::dns::Name::MAX_LABELS;
 
     int node_count_;
     const RBNode<T>* nodes_[RBT_MAX_LEVEL];
@@ -999,8 +996,15 @@ RBTree<T>::find(const isc::dns::Name& target_name,
             const int common_label_count =
                 node_path.last_comparison_.getCommonLabels();
             // If the common label count is 1, there is no common label between
-            // the two names, except the trailing "dot".
-            if (common_label_count == 1) {
+            // the two names, except the trailing "dot".  In this case the two
+            // sequences of labels have essentially no hierarchical
+            // relationship in terms of matching, so we should continue the
+            // binary search.  One important exception is when the node
+            // represents the root name ("."), in which case the comparison
+            // result must indeed be considered subdomain matching. (We use
+            // getLength() to check if the name is root, which is an equivalent
+            // but cheaper way).
+            if (common_label_count == 1 && node->name_.getLength() != 1) {
                 node = (node_path.last_comparison_.getOrder() < 0) ?
                     node->left_ : node->right_;
             } else if (relation == isc::dns::NameComparisonResult::SUBDOMAIN) {
@@ -1093,7 +1097,8 @@ RBTree<T>::insert(const isc::dns::Name& target_name, RBNode<T>** new_node) {
             return (ALREADYEXISTS);
         } else {
             const int common_label_count = compare_result.getCommonLabels();
-            if (common_label_count == 1) {
+            // Note: see find() for the check of getLength().
+            if (common_label_count == 1 && current->name_.getLength() != 1) {
                 parent = current;
                 order = compare_result.getOrder();
                 current = order < 0 ? current->left_ : current->right_;
diff --git a/src/lib/datasrc/tests/Makefile.am b/src/lib/datasrc/tests/Makefile.am
index 3c36e2d..f09b4b7 100644
--- a/src/lib/datasrc/tests/Makefile.am
+++ b/src/lib/datasrc/tests/Makefile.am
@@ -31,6 +31,7 @@ run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
 run_unittests_LDADD = $(GTEST_LDADD)
 run_unittests_LDADD += $(SQLITE_LIBS)
+run_unittests_LDADD += $(top_builddir)/src/lib/testutils/libtestutils.la
 run_unittests_LDADD += $(top_builddir)/src/lib/datasrc/libdatasrc.la
 run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
 run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la
diff --git a/src/lib/datasrc/tests/datasrc_unittest.cc b/src/lib/datasrc/tests/datasrc_unittest.cc
index bff7949..ab5cd85 100644
--- a/src/lib/datasrc/tests/datasrc_unittest.cc
+++ b/src/lib/datasrc/tests/datasrc_unittest.cc
@@ -38,6 +38,7 @@
 #include <datasrc/sqlite3_datasrc.h>
 #include <datasrc/static_datasrc.h>
 
+#include <testutils/dnsmessage_test.h>
 #include <dns/tests/unittest_util.h>
 #include <datasrc/tests/test_datasrc.h>
 
@@ -47,6 +48,7 @@ using namespace isc::dns;
 using namespace isc::dns::rdata;
 using namespace isc::datasrc;
 using namespace isc::data;
+using namespace isc::testutils;
 
 namespace {
 ConstElementPtr SQLITE_DBFILE_EXAMPLE = Element::fromJSON(
@@ -54,7 +56,9 @@ ConstElementPtr SQLITE_DBFILE_EXAMPLE = Element::fromJSON(
 
 class DataSrcTest : public ::testing::Test {
 protected:
-    DataSrcTest() : obuffer(0), renderer(obuffer), msg(Message::PARSE) {
+    DataSrcTest() : obuffer(0), renderer(obuffer), msg(Message::PARSE),
+                    opcodeval(Opcode::QUERY().getCode()), qid(0)
+    {
         DataSrcPtr sql3_source = DataSrcPtr(new Sqlite3DataSrc); 
         sql3_source->init(SQLITE_DBFILE_EXAMPLE);
         DataSrcPtr test_source = DataSrcPtr(new TestDataSrc);
@@ -73,6 +77,8 @@ protected:
     OutputBuffer obuffer;
     MessageRenderer renderer;
     Message msg;
+    const uint16_t opcodeval;
+    qid_t qid;
 };
 
 void
@@ -91,29 +97,16 @@ DataSrcTest::createAndProcessQuery(const Name& qname, const RRClass& qclass,
     msg.setOpcode(Opcode::QUERY());
     msg.addQuestion(Question(qname, qclass, qtype));
     msg.setHeaderFlag(Message::HEADERFLAG_RD);
+    qid = msg.getQid();
     performQuery(meta_source, cache, msg);
 }
 
 void
-headerCheck(const Message& message, const Rcode& rcode, const bool qrflag,
-            const bool aaflag, const bool rdflag, const unsigned int ancount,
-            const unsigned int nscount, const unsigned int arcount)
-{
-    EXPECT_EQ(rcode, message.getRcode());
-    EXPECT_EQ(qrflag, message.getHeaderFlag(Message::HEADERFLAG_QR));
-    EXPECT_EQ(aaflag, message.getHeaderFlag(Message::HEADERFLAG_AA));
-    EXPECT_EQ(rdflag, message.getHeaderFlag(Message::HEADERFLAG_RD));
-
-    EXPECT_EQ(ancount, message.getRRCount(Message::SECTION_ANSWER));
-    EXPECT_EQ(nscount, message.getRRCount(Message::SECTION_AUTHORITY));
-    EXPECT_EQ(arcount, message.getRRCount(Message::SECTION_ADDITIONAL));
-}
-
-void
 DataSrcTest::QueryCommon(const RRClass& qclass) {
     createAndProcessQuery(Name("www.example.com"), qclass, RRType::A());
 
-    headerCheck(msg, Rcode::NOERROR(), true, true, true, 2, 4, 6);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 2, 4, 6);
 
     RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
     RRsetPtr rrset = *rit;
@@ -163,12 +156,8 @@ TEST_F(DataSrcTest, Query) {
 // should be the same as "NxZone".
 TEST_F(DataSrcTest, QueryClassMismatch) {
     createAndProcessQuery(Name("www.example.com"), RRClass::CH(), RRType::A());
-    headerCheck(msg, Rcode::REFUSED(), true, false, true, 0, 0, 0);
-
-    EXPECT_EQ(Rcode::REFUSED(), msg.getRcode());
-    EXPECT_TRUE(msg.getHeaderFlag(Message::HEADERFLAG_QR));
-    EXPECT_FALSE(msg.getHeaderFlag(Message::HEADERFLAG_AA));
-    EXPECT_TRUE(msg.getHeaderFlag(Message::HEADERFLAG_RD));
+    headerCheck(msg, qid, Rcode::REFUSED(), opcodeval, QR_FLAG | RD_FLAG,
+                1, 0, 0, 0);
 }
 
 // Query class of any should match the first data source.
@@ -179,7 +168,8 @@ TEST_F(DataSrcTest, QueryClassAny) {
 TEST_F(DataSrcTest, NSQuery) {
     createAndProcessQuery(Name("example.com"), RRClass::IN(),
                           RRType::NS());
-    headerCheck(msg, Rcode::NOERROR(), true, true, true, 4, 0, 6);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 4, 0, 6);
 
     RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
     RRsetPtr rrset = *rit;
@@ -201,7 +191,8 @@ TEST_F(DataSrcTest, NSQuery) {
 TEST_F(DataSrcTest, DuplicateQuery) {
     createAndProcessQuery(Name("example.com"), RRClass::IN(),
                           RRType::NS());
-    headerCheck(msg, Rcode::NOERROR(), true, true, true, 4, 0, 6);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 4, 0, 6);
 
     RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
     RRsetPtr rrset = *rit;
@@ -221,7 +212,8 @@ TEST_F(DataSrcTest, DuplicateQuery) {
     msg.clear(Message::PARSE);
     createAndProcessQuery(Name("example.com"), RRClass::IN(),
                           RRType::NS());
-    headerCheck(msg, Rcode::NOERROR(), true, true, true, 4, 0, 6);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 4, 0, 6);
 
     rit = msg.beginSection(Message::SECTION_ANSWER);
     rrset = *rit;
@@ -242,7 +234,8 @@ TEST_F(DataSrcTest, DuplicateQuery) {
 TEST_F(DataSrcTest, DNSKEYQuery) {
     createAndProcessQuery(Name("example.com"), RRClass::IN(),
                           RRType::DNSKEY());
-    headerCheck(msg, Rcode::NOERROR(), true, true, true, 4, 4, 6);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 4, 4, 6);
 
     RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
     RRsetPtr rrset = *rit;
@@ -257,7 +250,8 @@ TEST_F(DataSrcTest, DNSKEYQuery) {
 TEST_F(DataSrcTest, DNSKEYDuplicateQuery) {
     createAndProcessQuery(Name("example.com"), RRClass::IN(),
                           RRType::DNSKEY());
-    headerCheck(msg, Rcode::NOERROR(), true, true, true, 4, 4, 6);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 4, 4, 6);
 
     RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
     RRsetPtr rrset = *rit;
@@ -279,7 +273,8 @@ TEST_F(DataSrcTest, NxRRset) {
     createAndProcessQuery(Name("example.com"), RRClass::IN(),
                           RRType::PTR());
 
-    headerCheck(msg, Rcode::NOERROR(), true, true, true, 0, 4, 0);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 0, 4, 0);
 
     RRsetIterator rit = msg.beginSection(Message::SECTION_AUTHORITY);
     RRsetPtr rrset = *rit;
@@ -291,7 +286,8 @@ TEST_F(DataSrcTest, Nxdomain) {
     createAndProcessQuery(Name("glork.example.com"), RRClass::IN(),
                           RRType::A());
 
-    headerCheck(msg, Rcode::NXDOMAIN(), true, true, true, 0, 6, 0);
+    headerCheck(msg, qid, Rcode::NXDOMAIN(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 0, 6, 0);
 
     RRsetIterator rit = msg.beginSection(Message::SECTION_AUTHORITY);
     RRsetPtr rrset = *rit;
@@ -301,11 +297,46 @@ TEST_F(DataSrcTest, Nxdomain) {
     // XXX: check for other authority section answers
 }
 
+TEST_F(DataSrcTest, NxdomainAfterSOAQuery) {
+    // There was a bug where once SOA RR is stored in the hot spot cache
+    // subsequent negative search fails due to "missing SOA".  This test
+    // checks that situation.
+
+    // First, run the scenario with disabling the cache.
+    cache.setEnabled(false);
+    createAndProcessQuery(Name("example.com"), RRClass::IN(),
+                          RRType::SOA());
+    msg.clear(Message::PARSE);
+    createAndProcessQuery(Name("notexistent.example.com"), RRClass::IN(),
+                          RRType::A());
+    {
+        SCOPED_TRACE("NXDOMAIN after SOA, without hot spot cache");
+        headerCheck(msg, qid, Rcode::NXDOMAIN(), opcodeval,
+                    QR_FLAG | AA_FLAG | RD_FLAG, 1, 0, 6, 0);
+    }
+
+    // Then enable the cache and perform the same queries.  This should
+    // produce the same result.
+    cache.setEnabled(true);
+    msg.clear(Message::PARSE);
+    createAndProcessQuery(Name("example.com"), RRClass::IN(),
+                          RRType::SOA());
+    msg.clear(Message::PARSE);
+    createAndProcessQuery(Name("notexistent.example.com"), RRClass::IN(),
+                        RRType::A());
+    {
+        SCOPED_TRACE("NXDOMAIN after SOA, without hot spot cache");
+        headerCheck(msg, qid, Rcode::NXDOMAIN(), opcodeval,
+                    QR_FLAG | AA_FLAG | RD_FLAG, 1, 0, 6, 0);
+    }
+}
+
 TEST_F(DataSrcTest, NxZone) {
     createAndProcessQuery(Name("spork.example"), RRClass::IN(),
                           RRType::A());
 
-    headerCheck(msg, Rcode::REFUSED(), true, false, true, 0, 0, 0);
+    headerCheck(msg, qid, Rcode::REFUSED(), opcodeval,
+                QR_FLAG | RD_FLAG, 1, 0, 0, 0);
 
     EXPECT_EQ(Rcode::REFUSED(), msg.getRcode());
     EXPECT_TRUE(msg.getHeaderFlag(Message::HEADERFLAG_QR));
@@ -317,7 +348,8 @@ TEST_F(DataSrcTest, Wildcard) {
     createAndProcessQuery(Name("www.wild.example.com"), RRClass::IN(),
                           RRType::A());
 
-    headerCheck(msg, Rcode::NOERROR(), true, true, true, 2, 6, 6);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 2, 6, 6);
 
     RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
     RRsetPtr rrset = *rit;
@@ -369,7 +401,8 @@ TEST_F(DataSrcTest, WildcardNodata) {
     // returns NOERROR
     createAndProcessQuery(Name("www.wild.example.com"), RRClass::IN(),
                           RRType::AAAA());
-    headerCheck(msg, Rcode::NOERROR(), true, true, true, 0, 2, 0);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 0, 2, 0);
 }
 
 TEST_F(DataSrcTest, DISABLED_WildcardAgainstMultiLabel) {
@@ -377,7 +410,8 @@ TEST_F(DataSrcTest, DISABLED_WildcardAgainstMultiLabel) {
     // a single label), and it should result in NXDOMAIN.
     createAndProcessQuery(Name("www.xxx.wild.example.com"), RRClass::IN(),
                           RRType::A());
-    headerCheck(msg, Rcode::NXDOMAIN(), true, true, true, 0, 1, 0);
+    headerCheck(msg, qid, Rcode::NXDOMAIN(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 0, 1, 0);
 }
 
 TEST_F(DataSrcTest, WildcardCname) {
@@ -386,7 +420,8 @@ TEST_F(DataSrcTest, WildcardCname) {
     createAndProcessQuery(Name("www.wild2.example.com"), RRClass::IN(),
                           RRType::A());
 
-    headerCheck(msg, Rcode::NOERROR(), true, true, true, 4, 6, 6);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 4, 6, 6);
 
     RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
     RRsetPtr rrset = *rit;
@@ -450,7 +485,8 @@ TEST_F(DataSrcTest, WildcardCnameNodata) {
     // data of this type.
     createAndProcessQuery(Name("www.wild2.example.com"), RRClass::IN(),
                           RRType::AAAA());
-    headerCheck(msg, Rcode::NOERROR(), true, true, true, 2, 4, 0);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 2, 4, 0);
 
     RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
     RRsetPtr rrset = *rit;
@@ -481,7 +517,8 @@ TEST_F(DataSrcTest, WildcardCnameNxdomain) {
     // A wildcard containing a CNAME whose target does not exist
     createAndProcessQuery(Name("www.wild3.example.com"), RRClass::IN(),
                           RRType::A());
-    headerCheck(msg, Rcode::NOERROR(), true, true, true, 2, 6, 0);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 2, 6, 0);
 
     RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
     RRsetPtr rrset = *rit;
@@ -518,7 +555,8 @@ TEST_F(DataSrcTest, AuthDelegation) {
     createAndProcessQuery(Name("www.sql1.example.com"), RRClass::IN(),
                           RRType::A());
 
-    headerCheck(msg, Rcode::NOERROR(), true, true, true, 2, 4, 6);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 2, 4, 6);
 
     RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
     RRsetPtr rrset = *rit;
@@ -562,7 +600,8 @@ TEST_F(DataSrcTest, Dname) {
     createAndProcessQuery(Name("www.dname.example.com"), RRClass::IN(),
                           RRType::A());
 
-    headerCheck(msg, Rcode::NOERROR(), true, true, true, 5, 4, 6);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 5, 4, 6);
 
     RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
     RRsetPtr rrset = *rit;
@@ -610,14 +649,16 @@ TEST_F(DataSrcTest, DnameExact) {
     // confuse delegation processing.
     createAndProcessQuery(Name("dname2.foo.example.org"), RRClass::IN(),
                           RRType::A());
-    headerCheck(msg, Rcode::NOERROR(), true, true, true, 0, 1, 0);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 0, 1, 0);
 }
 
 TEST_F(DataSrcTest, Cname) {
     createAndProcessQuery(Name("foo.example.com"), RRClass::IN(),
                           RRType::A());
 
-    headerCheck(msg, Rcode::NOERROR(), true, true, true, 2, 0, 0);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 2, 0, 0);
 
     RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
     RRsetPtr rrset = *rit;
@@ -635,7 +676,8 @@ TEST_F(DataSrcTest, CnameInt) {
     createAndProcessQuery(Name("cname-int.example.com"), RRClass::IN(),
                           RRType::A());
 
-    headerCheck(msg, Rcode::NOERROR(), true, true, true, 4, 4, 6);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 4, 4, 6);
 
     RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
     RRsetPtr rrset = *rit;
@@ -661,7 +703,8 @@ TEST_F(DataSrcTest, CnameExt) {
     createAndProcessQuery(Name("cname-ext.example.com"), RRClass::IN(),
                           RRType::A());
 
-    headerCheck(msg, Rcode::NOERROR(), true, true, true, 4, 4, 6);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 4, 4, 6);
 
     RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
     RRsetPtr rrset = *rit;
@@ -685,7 +728,8 @@ TEST_F(DataSrcTest, Delegation) {
     createAndProcessQuery(Name("www.subzone.example.com"), RRClass::IN(),
                           RRType::A());
 
-    headerCheck(msg, Rcode::NOERROR(), true, false, true, 0, 5, 2);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | RD_FLAG, 1, 0, 5, 2);
 
     RRsetIterator rit = msg.beginSection(Message::SECTION_AUTHORITY);
     RRsetPtr rrset = *rit;
@@ -714,7 +758,8 @@ TEST_F(DataSrcTest, NSDelegation) {
     createAndProcessQuery(Name("subzone.example.com"), RRClass::IN(),
                           RRType::NS());
 
-    headerCheck(msg, Rcode::NOERROR(), true, false, true, 0, 5, 2);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | RD_FLAG, 1, 0, 5, 2);
 
     RRsetIterator rit = msg.beginSection(Message::SECTION_AUTHORITY);
     RRsetPtr rrset = *rit;
@@ -750,7 +795,8 @@ TEST_F(DataSrcTest, NSECZonecut) {
     createAndProcessQuery(Name("subzone.example.com"), RRClass::IN(),
                           RRType::NSEC());
 
-    headerCheck(msg, Rcode::NOERROR(), true, true, true, 2, 4, 6);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 2, 4, 6);
 
     RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
     RRsetPtr rrset = *rit;
@@ -778,7 +824,8 @@ TEST_F(DataSrcTest, DNAMEZonecut) {
     createAndProcessQuery(Name("subzone.example.com"), RRClass::IN(),
                           RRType::DNAME());
 
-    headerCheck(msg, Rcode::NOERROR(), true, false, true, 0, 5, 2);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | RD_FLAG, 1, 0, 5, 2);
     RRsetIterator rit = msg.beginSection(Message::SECTION_AUTHORITY);
     RRsetPtr rrset = *rit;
     EXPECT_EQ(Name("subzone.example.com."), rrset->getName());
@@ -806,7 +853,8 @@ TEST_F(DataSrcTest, DS) {
     createAndProcessQuery(Name("subzone.example.com"), RRClass::IN(),
                           RRType::DS());
 
-    headerCheck(msg, Rcode::NOERROR(), true, true, true, 3, 4, 6);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 3, 4, 6);
 
     RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
     RRsetPtr rrset = *rit;
@@ -847,7 +895,8 @@ TEST_F(DataSrcTest, NSECZonecutOfNonsecureZone) {
     createAndProcessQuery(Name("sub.example.org"), RRClass::IN(),
                           RRType::NSEC());
 
-    headerCheck(msg, Rcode::NOERROR(), true, false, true, 0, 1, 1);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | RD_FLAG, 1, 0, 1, 1);
 
     RRsetIterator rit = msg.beginSection(Message::SECTION_AUTHORITY);
     ConstRRsetPtr rrset = *rit;
@@ -879,7 +928,8 @@ TEST_F(DataSrcTest, NSECZonecutOfNonsecureZone) {
 TEST_F(DataSrcTest, RootDSQuery1) {
     EXPECT_NO_THROW(createAndProcessQuery(Name("."), RRClass::IN(),
                                           RRType::DS()));
-    headerCheck(msg, Rcode::REFUSED(), true, false, true, 0, 0, 0);
+    headerCheck(msg, qid, Rcode::REFUSED(), opcodeval,
+                QR_FLAG | RD_FLAG, 1, 0, 0, 0);
 }
 
 // The same, but when we have the root zone
@@ -898,7 +948,8 @@ TEST_F(DataSrcTest, RootDSQuery2) {
     // Make the query
     EXPECT_NO_THROW(performQuery(*sql3_source, cache, msg));
 
-    headerCheck(msg, Rcode::NOERROR(), true, true, true, 0, 1, 0);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 0, 1, 0);
 }
 
 TEST_F(DataSrcTest, DSQueryFromCache) {
@@ -916,7 +967,8 @@ TEST_F(DataSrcTest, DSQueryFromCache) {
 
     // returning refused is probably a bad behavior, but it's a different
     // issue -- see Trac Ticket #306.
-    headerCheck(msg, Rcode::REFUSED(), true, false, true, 0, 0, 0);
+    headerCheck(msg, qid, Rcode::REFUSED(), opcodeval,
+                QR_FLAG | RD_FLAG, 1, 0, 0, 0);
 }
 
 // Non-existent name in the "static" data source.  The purpose of this test
@@ -925,7 +977,8 @@ TEST_F(DataSrcTest, DSQueryFromCache) {
 TEST_F(DataSrcTest, StaticNxDomain) {
     createAndProcessQuery(Name("www.version.bind"), RRClass::CH(),
                           RRType::TXT());
-    headerCheck(msg, Rcode::NXDOMAIN(), true, true, true, 0, 1, 0);
+    headerCheck(msg, qid, Rcode::NXDOMAIN(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 0, 1, 0);
     RRsetIterator rit = msg.beginSection(Message::SECTION_AUTHORITY);
     RRsetPtr rrset = *rit;
     EXPECT_EQ(Name("version.bind"), rrset->getName());
@@ -973,6 +1026,15 @@ TEST_F(DataSrcTest, noSOAZone) {
                  DataSourceError);
 }
 
+TEST_F(DataSrcTest, apexCNAMEZone) {
+    // The query name doesn't exist in the best matching zone,
+    // and there's a CNAME at the apex (which is bogus), so query handling
+    // will fail due to missing SOA.
+    EXPECT_THROW(createAndProcessQuery(Name("notexist.apexcname.example"),
+                                       RRClass::IN(), RRType::A()),
+                 DataSourceError);
+}
+
 // currently fails
 TEST_F(DataSrcTest, DISABLED_synthesizedCnameTooLong) {
     // qname has the possible max length (255 octets).  it matches a DNAME,
diff --git a/src/lib/datasrc/tests/rbtree_unittest.cc b/src/lib/datasrc/tests/rbtree_unittest.cc
index 23b25f4..dd1b7fe 100644
--- a/src/lib/datasrc/tests/rbtree_unittest.cc
+++ b/src/lib/datasrc/tests/rbtree_unittest.cc
@@ -284,21 +284,21 @@ TEST_F(RBTreeTest, chainLevel) {
     EXPECT_EQ(1, chain.getLevelCount());
 
     /*
-     * Now creating a possibly deepest tree with MAX_LABELS - 1 levels.
+     * Now creating a possibly deepest tree with MAX_LABELS 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.
+     * Note that the root name (".") solely belongs to a single level,
+     * so the levels begin with 2.
      */
-    for (unsigned int i = 1; i < Name::MAX_LABELS; ++i) {
+    for (unsigned int i = 2; 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;
@@ -523,4 +523,44 @@ TEST_F(RBTreeTest, swap) {
     tree2.dumpTree(out);
     ASSERT_EQ(str1.str(), out.str());
 }
+
+// Matching in the "root zone" may be special (e.g. there's no parent,
+// any domain names should be considered a subdomain of it), so it makes
+// sense to test cases with the root zone explicitly.
+TEST_F(RBTreeTest, root) {
+    RBTree<int> root;
+    root.insert(Name::ROOT_NAME(), &rbtnode);
+    rbtnode->setData(RBNode<int>::NodeDataPtr(new int(1)));
+
+    EXPECT_EQ(RBTree<int>::EXACTMATCH,
+              root.find(Name::ROOT_NAME(), &crbtnode));
+    EXPECT_EQ(rbtnode, crbtnode);
+    EXPECT_EQ(RBTree<int>::PARTIALMATCH,
+              root.find(Name("example.com"), &crbtnode));
+    EXPECT_EQ(rbtnode, crbtnode);
+
+    // Insert a new name that better matches the query name.  find() should
+    // find the better one.
+    root.insert(Name("com"), &rbtnode);
+    rbtnode->setData(RBNode<int>::NodeDataPtr(new int(2)));
+    EXPECT_EQ(RBTree<int>::PARTIALMATCH,
+              root.find(Name("example.com"), &crbtnode));
+    EXPECT_EQ(rbtnode, crbtnode);
+
+    // Perform the same tests for the tree that allows matching against empty
+    // nodes.
+    RBTree<int> root_emptyok(true);
+    root_emptyok.insert(Name::ROOT_NAME(), &rbtnode);
+    EXPECT_EQ(RBTree<int>::EXACTMATCH,
+              root_emptyok.find(Name::ROOT_NAME(), &crbtnode));
+    EXPECT_EQ(rbtnode, crbtnode);
+    EXPECT_EQ(RBTree<int>::PARTIALMATCH,
+              root_emptyok.find(Name("example.com"), &crbtnode));
+    EXPECT_EQ(rbtnode, crbtnode);
+
+    root.insert(Name("com"), &rbtnode);
+    EXPECT_EQ(RBTree<int>::PARTIALMATCH,
+              root.find(Name("example.com"), &crbtnode));
+    EXPECT_EQ(rbtnode, crbtnode);
+}
 }
diff --git a/src/lib/datasrc/tests/test_datasrc.cc b/src/lib/datasrc/tests/test_datasrc.cc
index 8145539..9493e1a 100644
--- a/src/lib/datasrc/tests/test_datasrc.cc
+++ b/src/lib/datasrc/tests/test_datasrc.cc
@@ -273,6 +273,18 @@ const struct RRData nosoa_example_records[] = {
 };
 
 //
+// zone data for apexcname.example.
+//
+const struct RRData apexcname_example_records[] = {
+    {"apexcname.example", "CNAME", "canonical.apexcname.example"},
+    {"canonical.apexcname.example", "SOA",
+     "master.apexcname.example "
+     "admin.apexcname.example. 1234 3600 1800 2419200 7200"},
+    {NULL, NULL, NULL}
+};
+
+
+//
 // empty data set, for convenience.
 //
 const struct RRData empty_records[] = {
@@ -288,7 +300,8 @@ const struct ZoneData zone_data[] = {
     { "loop.example", "IN", loop_example_records, empty_records },
     { "nons.example", "IN", nons_example_records, empty_records },
     { "nons-dname.example", "IN", nonsdname_example_records, empty_records },
-    { "nosoa.example", "IN", nosoa_example_records, empty_records }
+    { "nosoa.example", "IN", nosoa_example_records, empty_records },
+    { "apexcname.example", "IN", nosoa_example_records, empty_records }
 };
 const size_t NUM_ZONES = sizeof(zone_data) / sizeof(zone_data[0]);
 
diff --git a/src/lib/datasrc/zonetable.h b/src/lib/datasrc/zonetable.h
index 0ca80b0..5b873d1 100644
--- a/src/lib/datasrc/zonetable.h
+++ b/src/lib/datasrc/zonetable.h
@@ -107,7 +107,7 @@ public:
     ///   - \c result::PARTIALMATCH: A zone whose origin is a
     ///    super domain of \c name is found (but there is no exact match)
     ///   - \c result::NOTFOUND: For all other cases.
-    /// - \c zone: A <Boost> shared pointer to the found \c Zone object if one
+    /// - \c zone: A "Boost" shared pointer to the found \c Zone object if one
     ///  is found; otherwise \c NULL.
     ///
     /// This method never throws an exception.
diff --git a/src/lib/dns/edns.h b/src/lib/dns/edns.h
index f9a5758..ca3db47 100644
--- a/src/lib/dns/edns.h
+++ b/src/lib/dns/edns.h
@@ -213,7 +213,7 @@ public:
     /// \param name The owner name of the OPT RR.  This must be the root name.
     /// \param rrclass The RR class of the OPT RR.
     /// \param rrtype This must specify the OPT RR type.
-    /// \param rrttl The TTL of the OPT RR.
+    /// \param ttl The TTL of the OPT RR.
     /// \param rdata The RDATA of the OPT RR.
     EDNS(const Name& name, const RRClass& rrclass, const RRType& rrtype,
          const RRTTL& ttl, const rdata::Rdata& rdata);
@@ -418,7 +418,7 @@ private:
 /// \param name The owner name of the OPT RR.  This must be the root name.
 /// \param rrclass The RR class of the OPT RR.
 /// \param rrtype This must specify the OPT RR type.
-/// \param rrttl The TTL of the OPT RR.
+/// \param ttl The TTL of the OPT RR.
 /// \param rdata The RDATA of the OPT RR.
 /// \param extended_rcode A placeholder to store the topmost 8 bits of the
 /// extended Rcode.
diff --git a/src/lib/dns/masterload.h b/src/lib/dns/masterload.h
index 76d6709..fe5c08f 100644
--- a/src/lib/dns/masterload.h
+++ b/src/lib/dns/masterload.h
@@ -110,7 +110,7 @@ typedef boost::function<void(RRsetPtr)> MasterLoadCallback;
 ///  but this is not even though it's valid per RFC1035:
 /// \code example.com. IN 3600 A 192.0.2.1
 /// \endcode
-/// - <TTL>, <RRCLASS>, and <RRTYPE> must be recognizable by the \c RRTTL,
+/// - "TTL", "RRCLASS", and "RRTYPE" must be recognizable by the \c RRTTL,
 ///   RRClass and RRType class implementations of this library.  In particular,
 ///   as of this writing TTL must be a decimal number (a convenient extension
 ///   such as "1H" instead of 3600 cannot be used).  Not all standard RR
@@ -213,7 +213,7 @@ typedef boost::function<void(RRsetPtr)> MasterLoadCallback;
 /// \param filename A path to a master zone file to be loaded.
 /// \param origin The origin name of the zone.
 /// \param zone_class The RR class of the zone.
-/// \param callbck A callback functor or function that is to be called
+/// \param callback A callback functor or function that is to be called
 /// for each RRset.
 void masterLoad(const char* const filename, const Name& origin,
                 const RRClass& zone_class, MasterLoadCallback callback);
@@ -231,7 +231,7 @@ void masterLoad(const char* const filename, const Name& origin,
 /// \param input An input stream object that is to emit zone's RRs.
 /// \param origin The origin name of the zone.
 /// \param zone_class The RR class of the zone.
-/// \param callbck A callback functor or function that is to be called for
+/// \param callback A callback functor or function that is to be called for
 /// each RRset.
 void masterLoad(std::istream& input, const Name& origin,
                 const RRClass& zone_class, MasterLoadCallback callback);
diff --git a/src/lib/dns/message.h b/src/lib/dns/message.h
index 11167d2..91a241a 100644
--- a/src/lib/dns/message.h
+++ b/src/lib/dns/message.h
@@ -141,7 +141,7 @@ typedef SectionIterator<RRsetPtr> RRsetIterator;
 /// - We may want to provide an "iterator" for all RRsets/RRs for convenience.
 ///   This will be for applications that do not care about performance much,
 ///   so the implementation can only be moderately efficient.
-/// - may want to provide a "find" method for a specified type
+/// - We may want to provide a "find" method for a specified type
 ///   of RR in the message.
 class Message {
 public:
@@ -155,8 +155,8 @@ public:
     ///
     /// Only the defined constants are valid where a header flag is required
     /// in this library (e.g., in \c Message::setHeaderFlag()).
-    /// Since these are enum constants, however, invalid value could be passed
-    /// via casting without an error at compilation time.
+    /// Since these are enum constants, however, an invalid value could be
+    /// passed via casting without an error at compilation time.
     /// It is generally the callee's responsibility to check and reject invalid
     /// values.
     /// Of course, applications shouldn't pass invalid values even if the
@@ -168,7 +168,7 @@ public:
     /// specified flag in the second 16 bits of the DNS Header section
     /// in order to make the internal implementation simpler.
     /// For example, \c HEADERFLAG_QR is defined to be 0x8000 as the QR
-    /// bit is the most significant bit of the 2nd 16 bits of the header.
+    /// bit is the most significant bit of the second 16 bits of the header.
     /// However, applications should not assume this coincidence and
     /// must solely use the enum representations.
     /// Any usage based on the assumption of the underlying values is invalid
diff --git a/src/lib/dns/question.h b/src/lib/dns/question.h
index 851de2c..520207b 100644
--- a/src/lib/dns/question.h
+++ b/src/lib/dns/question.h
@@ -54,13 +54,13 @@ typedef boost::shared_ptr<const Question> ConstQuestionPtr;
 /// class.
 /// This may look odd in that an "RRset" and "Question" are similar from the
 /// protocol point of view: Both are used as a semantics unit of DNS messages;
-/// both share the same set of components, name, RR type and RR class.
+/// both share the same set of components (name, RR type and RR class).
 ///
 /// In fact, BIND9 didn't introduce a separate data structure for Questions,
 /// and use the same \c "rdataset" structure for both RRsets and Questions.
 /// We could take the same approach, but chose to adopt the different design.
 /// One reason for that is because a Question and an RRset are still
-/// different, and a Question might not be cleanly defined if (e.g.) it were
+/// different, and a Question might not be cleanly defined, e.g., if it were
 /// a derived class of some "RRset-like" class.
 /// For example, we couldn't give a reasonable semantics for \c %getTTL() or
 /// \c %setTTL() methods for a Question, since it's not associated with the
@@ -74,14 +74,14 @@ typedef boost::shared_ptr<const Question> ConstQuestionPtr;
 ///
 /// On the other hand, we do not expect a strong need for customizing the
 /// \c Question class, unlike the RRset.
-/// Handling the Question section of a DNS message is relatively a
+/// Handling the "Question" section of a DNS message is relatively a
 /// simple work comparing to RRset-involved operations, so a unified
 /// straightforward implementation should suffice for any use cases
 /// including performance sensitive ones.
 ///
-/// We may, however, still want to have customized version of Question
+/// We may, however, still want to have a customized version of Question
 /// for, e.g, highly optimized behavior, and may revisit this design choice
-/// as we have more experiences with this implementation.
+/// as we have more experience with this implementation.
 ///
 /// One disadvantage of defining RRsets and Questions as unrelated classes
 /// is that we cannot handle them in a polymorphic way.
diff --git a/src/lib/dns/rrset.h b/src/lib/dns/rrset.h
index acb4224..926a58f 100644
--- a/src/lib/dns/rrset.h
+++ b/src/lib/dns/rrset.h
@@ -278,8 +278,6 @@ public:
     /// name when possible in the context of zone dump.  This is a future
     /// TODO item.
     ///
-    /// \param rrset A reference to a (derived class of) \c AbstractRRset object
-    /// whose content is to be converted.
     /// \return A string representation of the RRset.
     virtual std::string toText() const = 0;
 
diff --git a/src/lib/dns/rrttl.h b/src/lib/dns/rrttl.h
index c80030e..695306a 100644
--- a/src/lib/dns/rrttl.h
+++ b/src/lib/dns/rrttl.h
@@ -118,7 +118,8 @@ public:
     /// If resource allocation in rendering process fails, a corresponding
     /// standard exception will be thrown.
     ///
-    /// \param buffer An output buffer to store the wire data.
+    /// \param renderer DNS message rendering context that encapsulates the
+    /// output buffer in which the RRTTL is to be stored.
     void toWire(MessageRenderer& renderer) const;
     /// \brief Render the \c RRTTL in the wire format.
     ///
@@ -128,8 +129,7 @@ public:
     /// If resource allocation in rendering process fails, a corresponding
     /// standard exception will be thrown.
     ///
-    /// \param renderer DNS message rendering context that encapsulates the
-    /// output buffer in which the RRTTL is to be stored.
+    /// \param buffer An output buffer to store the wire data.
     void toWire(OutputBuffer& buffer) const;
     //@}
 
diff --git a/src/lib/log/Makefile.am b/src/lib/log/Makefile.am
index 5bcafa8..d941b01 100644
--- a/src/lib/log/Makefile.am
+++ b/src/lib/log/Makefile.am
@@ -23,6 +23,11 @@ liblog_la_SOURCES += message_types.h
 liblog_la_SOURCES += root_logger_name.cc root_logger_name.h
 liblog_la_SOURCES += strutil.h strutil.cc
 
+EXTRA_DIST  = README
+EXTRA_DIST += messagedef.mes
+EXTRA_DIST += logger_impl_log4cxx.cc logger_impl_log4cxx.h
+EXTRA_DIST += xdebuglevel.cc xdebuglevel.h
+
 # Note: the ordering matters: -Wno-... must follow -Wextra (defined in
 # B10_CXXFLAGS)
 liblog_la_CXXFLAGS = $(AM_CXXFLAGS)
diff --git a/src/lib/log/README b/src/lib/log/README
new file mode 100644
index 0000000..072649e
--- /dev/null
+++ b/src/lib/log/README
@@ -0,0 +1,376 @@
+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 now 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 initLogger (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_
+$NAMESPACE isc::log
+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
+  example 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, two directives are recognised:
+
+  * $PREFIX, which has one argument: the string used to prefix symbols.  If
+    absent, there is no prefix to the symbols. (Prefixes are explained below.)
+  * $NAMESPACE, which has one argument: the namespace in which the symbols are
+    created.  In the absence of a $NAMESPACE directive, symbols will be put
+    in the global namespace.
+
+* 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 is invoked by the command:
+
+    message [-h] [-v] <message-file>
+
+("-v" prints the version number and exits; "-h" prints brief help text.)
+The message compiler 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 <namespace> {
+   extern const isc::log::MessageID PREFIX_IDENTIFIER;
+      :
+   }
+
+The symbols define the keys in the global message dictionary.
+
+The namespace enclosing the symbols is set by the $NAMESPACE directive.
+
+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 definitions
+of the global symbols and code to insert the symbols and messages into the map.
+
+Symbols are defined to be equal to strings holding the identifier, e.g.
+
+   extern const isc::log::MessageID MSG_DUPLNS = "DUPLNS";
+
+(The implementation allows symbols to be compared.  However, use of strings
+should not be assumed - a future implementation may change this.)
+
+In addition, the file declares an array of identifiers/messages and an object
+to add them to the global dictionary:
+
+    namespace {
+    const char* values[] = {
+        identifier1, text1,
+        identifier2, text2,
+        :
+        NULL
+    };
+
+    const isc::log::MessageInitializer initializer(values);
+    }
+
+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");
+
+   This can be declared inside or outside an execution unit.
+
+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);
+
+   (The argument is required to support a possible future implementation of
+   logging.  Currently it has no effect.)
+
+3. The main program unit should include a call to isc::log::initLogger()
+   (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::DEBUG
+        isc::log::INFO
+        isc::log::WARN
+        isc::log::ERROR
+        isc::log::FATAL
+        isc::log::NONE
+
+   b) The debug log level is only interpreted when the severity is DEBUG and
+      is an integer ranging 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 any directives. (A single directive
+      of a particular type 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 drawing
+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 denial 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, server 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.
+
+
+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.
+
+
+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.)
+
+
+log4cxx Issues
+==============
+Some experimental code to utilise log4cxx as an underlying implementation
+is present in the source code directory although it is not currently used.
+The files are:
+
+   logger_impl_log4cxx.{cc,h}
+   xdebuglevel.{cc,h}
diff --git a/src/lib/log/compiler/Makefile.am b/src/lib/log/compiler/Makefile.am
index 2475036..9343793 100644
--- a/src/lib/log/compiler/Makefile.am
+++ b/src/lib/log/compiler/Makefile.am
@@ -10,11 +10,9 @@ if USE_STATIC_LINK
 AM_LDFLAGS = -static
 endif
 
-pkglibexecdir = $(libexecdir)/@PACKAGE@
-
 CLEANFILES = *.gcno *.gcda
 
-pkglibexec_PROGRAMS = message
+noinst_PROGRAMS = message
 message_SOURCES = message.cc
 message_LDADD  = $(top_builddir)/src/lib/log/liblog.la
 
diff --git a/src/lib/log/documentation.txt b/src/lib/log/documentation.txt
deleted file mode 100644
index 0501587..0000000
--- a/src/lib/log/documentation.txt
+++ /dev/null
@@ -1,434 +0,0 @@
-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_
-$NAMESPACE isc::log
-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, two directives are recognised:
-
-  * $PREFIX, which has one argument: the string used to prefix symbols.  If
-    absent, there is no prefix to the symbols. (Prefixes are explained below.)
-  * $NAMESPACE, which has one argument: the namespace in which the symbols are
-    created.  (Specifying the argument as a double colon - i.e. "$NAMESPACE
-    ::" puts the symbol definitions in the unnamed namespace.  And not
-    including a $NAMESPACE directive will result in the symbols note being
-    put in any namespace.
-
-* 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 is invoked by the command:
-
-    message [-h] [-v] <message-file>
-
-("-v" prints the version number and exits; "-h" prints brief help text.)
-The message compiler 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 <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 namespace enclosing the symbols is set by the $NAMESPACE directive.
-
-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).
-
-The header file also includes a couple of lines to ensure that the message
-text is included in the final program image.
-
-
-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");
-
-   This can be declared inside or outside an execution unit.
-
-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);
-
-   The argument is ignored for underlying implementations other than log4cxx.
-   See below for the use of this argument.
-
-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::DEBUG
-        isc::log::INFO
-        isc::log::WARN
-        isc::log::ERROR
-        isc::log::FATAL
-        isc::log::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 drawing
-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.
-
-
-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.
-
-
-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.)
-
-
-log4cxx Issues
-==============
-
-Second Argument in Logger Constructor
--------------------------------------
-As noted above, when using log4cxx as the underlying implementation, the
-argument to the logger's constructor should be set true if declaring the
-logger within a method and set false (or omitted) if declaring the logger
-external to an execution unit.
-
-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.
-
-Building with log4cxx
----------------------
-Owing to issues with versions of log4cxx on different systems, log4cxx was
-temporarily disabled.  To use log4cxx on your system:
-
-* Uncomment the log4cxx lines in configure.ac
-* In src/lib/log, replace the logger_impl.{cc,h} files with their log4cxx
-  equivalents, i.e.
-
-  cp logger_impl_log4cxx.h logger_impl.h
-  cp logger_impl_log4cxx.cc logger_impl.cc
-
-* In src/lib/log/Makefile.am, uncomment the lines:
-
-  # AM_CPPFLAGS += $(LOG4CXX_INCLUDES)
-
-  # liblog_la_SOURCES += xdebuglevel.cc xdebuglevel.h
-
-  # liblog_la_LDFLAGS = $(LOG4CXX_LDFLAGS)
-
-* In src/lib/log/test, re-enable testing of the log4cxx implementation
-  class, i.e.
-
-  cp logger_impl_log4cxx_unittest.cc logger_impl_unittest.cc
-
-  ... and uncomment the following lines in Makefile.am:
-
-  # run_unittests_SOURCES += logger_impl_unittest.cc
-
-  # run_unittests_SOURCES += xdebuglevel_unittest.cc
-
-Then rebuild the system from scratch.
diff --git a/src/lib/log/dummylog.h b/src/lib/log/dummylog.h
index f20c032..87da9ac 100644
--- a/src/lib/log/dummylog.h
+++ b/src/lib/log/dummylog.h
@@ -34,7 +34,7 @@ extern std::string dprefix;
  * \short Temporary interface to logging.
  *
  * This is a temporary function to do logging. It has wrong interface currently
- * and should be replaced by something else. It's main purpose now is to mark
+ * and should be replaced by something else. Its main purpose now is to mark
  * places where logging should happen. When it is removed, compiler will do
  * our work of finding the places.
  *
@@ -51,6 +51,7 @@ extern std::string dprefix;
  *
  * @param message The message to log. The real interface will probably have
  *     more parameters.
+ * \param error_flag TODO
  */
 void dlog(const std::string& message, bool error_flag=false);
 
diff --git a/src/lib/log/filename.h b/src/lib/log/filename.h
index da9e560..e3cda16 100644
--- a/src/lib/log/filename.h
+++ b/src/lib/log/filename.h
@@ -131,7 +131,7 @@ public:
     /// \param name Name to expand
     ///
     /// \return Name expanded with stored name
-    std::string useAsDefault(const std::string&) const;
+    std::string useAsDefault(const std::string& name) const;
 
 private:
     /// \brief Split Name into Components
diff --git a/src/lib/log/message_dictionary.h b/src/lib/log/message_dictionary.h
index 0caa3ea..23f76d7 100644
--- a/src/lib/log/message_dictionary.h
+++ b/src/lib/log/message_dictionary.h
@@ -116,7 +116,7 @@ public:
     /// 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
+    /// \param elements 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.
diff --git a/src/lib/log/xdebuglevel.h b/src/lib/log/xdebuglevel.h
index fce3de4..e580b77 100644
--- a/src/lib/log/xdebuglevel.h
+++ b/src/lib/log/xdebuglevel.h
@@ -132,7 +132,7 @@ public:
     /// \return Pointer to the desired logging level object.
     static LevelPtr toLevel(int val, const LevelPtr& defaultLevel);
 
-    /// \param Convert String to Logging Level
+    /// \brief 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
@@ -143,7 +143,7 @@ public:
     /// \return Pointer to the desired logging level object.
     static LevelPtr toLevelLS(const LogString& sArg);
 
-    /// \param Convert String to Logging Level
+    /// \brief 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.
diff --git a/src/lib/nsas/address_entry.h b/src/lib/nsas/address_entry.h
index 148d479..8698017 100644
--- a/src/lib/nsas/address_entry.h
+++ b/src/lib/nsas/address_entry.h
@@ -21,7 +21,7 @@
 /// convenience methods for accessing and updating the information.
 
 #include <stdint.h>
-#include "asiolink.h"
+#include <asiolink/io_address.h>
 
 namespace isc {
 namespace nsas {
@@ -39,7 +39,7 @@ public:
     {}
 
     /// \return Address object
-    asiolink::IOAddress getAddress() const {
+    const asiolink::IOAddress& getAddress() const {
         return address_;
     }
 
diff --git a/src/lib/nsas/asiolink.h b/src/lib/nsas/asiolink.h
index f5af192..d95868f 100644
--- a/src/lib/nsas/asiolink.h
+++ b/src/lib/nsas/asiolink.h
@@ -18,41 +18,4 @@
 #include <string>
 #include <sys/socket.h>
 
-namespace asiolink {
-
-/// \brief IO Address Dummy Class
-///
-/// As part of ther resolver, Evan has written the asiolink.h file, which
-/// encapsulates some of the boost::asio classes.  Until these are checked
-/// into trunk and merged with this branch, these dummy classes should fulfill
-/// their function.
-
-class IOAddress {
-public:
-    /// \param address_str String representing the address
-    IOAddress(const std::string& address_str) : address_(address_str)
-    {}
-
-    /// \param Just a virtual destructor
-    virtual ~ IOAddress() { }
-
-    /// \return Textual representation of the address
-    std::string toText() const
-    {return address_;}
-
-    /// \return Address family of the address
-    virtual short getFamily() const {
-        return ((address_.find(".") != std::string::npos) ? AF_INET : AF_INET6);
-    }
-
-    /// \return true if two addresses are equal
-    bool equal(const IOAddress& address) const
-    {return (toText() == address.toText());}
-
-private:
-    std::string     address_;       ///< Address represented
-};
-
-}   // namespace asiolink
-
 #endif // __ASIOLINK_H
diff --git a/src/lib/nsas/hash.h b/src/lib/nsas/hash.h
index 0290c26..85b82c3 100644
--- a/src/lib/nsas/hash.h
+++ b/src/lib/nsas/hash.h
@@ -59,7 +59,7 @@ public:
     /// sequence could lead to problems in checking results.
     Hash(uint32_t tablesize, uint32_t maxkeylen = 255, bool randomise = true);
 
-    /// \bool Virtual Destructor
+    /// \brief Virtual Destructor
     virtual ~Hash()
     {}
 
diff --git a/src/lib/nsas/hash_table.h b/src/lib/nsas/hash_table.h
index e46d687..c4a9913 100644
--- a/src/lib/nsas/hash_table.h
+++ b/src/lib/nsas/hash_table.h
@@ -126,7 +126,7 @@ public:
     ///
     /// Initialises the hash table.
     ///
-    /// \param CmpFn Compare function (or object) used to compare an object with
+    /// \param cmp Compare function (or object) used to compare an object with
     /// to get the name to be used as a key in the table.  The object should be
     /// created via a "new" as ownership passes to the hash table.  The hash
     /// table will take the responsibility of deleting it.
diff --git a/src/lib/nsas/lru_list.h b/src/lib/nsas/lru_list.h
index 1a20dbf..b057baf 100644
--- a/src/lib/nsas/lru_list.h
+++ b/src/lib/nsas/lru_list.h
@@ -140,7 +140,7 @@ public:
 
     /// \brief Set Maximum Size
     ///
-    /// \param new_size New maximum list size
+    /// \param max_size New maximum list size
     virtual void setMaxSize(uint32_t max_size) {
         max_size_ = max_size;
     }
diff --git a/src/lib/nsas/nameserver_address.cc b/src/lib/nsas/nameserver_address.cc
index b2ed55c..b76d7b8 100644
--- a/src/lib/nsas/nameserver_address.cc
+++ b/src/lib/nsas/nameserver_address.cc
@@ -23,7 +23,9 @@ namespace nsas {
 void
 NameserverAddress::updateRTT(uint32_t rtt) const {
     // We delegate it to the address entry inside the nameserver entry
-    ns_->updateAddressRTT(rtt, address_.getAddress(), family_);
+    if (ns_) {
+        ns_->updateAddressRTT(rtt, address_.getAddress(), family_);
+    }
 }
 
 } // namespace nsas
diff --git a/src/lib/nsas/nameserver_address.h b/src/lib/nsas/nameserver_address.h
index 7752deb..07b6d4a 100644
--- a/src/lib/nsas/nameserver_address.h
+++ b/src/lib/nsas/nameserver_address.h
@@ -60,10 +60,10 @@ public:
     /// pointed to NameserverEntry which contains the address as well as it's
     /// corresponding index. The user can update it's RTT with the index later.
     ///
-    /// \param namerserver A shared_ptr that points to a NameserverEntry object
+    /// \param nameserver A shared_ptr that points to a NameserverEntry object
     /// the shared_ptr can avoid the NameserverEntry object being dropped while the
     /// request is processing.
-    /// \param index The address's index in NameserverEntry's addresses vector
+    /// \param address The address's index in NameserverEntry's addresses vector
     /// \param family Address family, V4_ONLY or V6_ONLY
     NameserverAddress(const boost::shared_ptr<NameserverEntry>& nameserver,
         const AddressEntry& address, AddressFamily family):
diff --git a/src/lib/nsas/nameserver_address_store.cc b/src/lib/nsas/nameserver_address_store.cc
index 7bb0eee..4efe491 100644
--- a/src/lib/nsas/nameserver_address_store.cc
+++ b/src/lib/nsas/nameserver_address_store.cc
@@ -93,5 +93,18 @@ NameserverAddressStore::lookup(const string& zone, const RRClass& class_code,
     zone_obj.second->addCallback(callback, family);
 }
 
+void
+NameserverAddressStore::cancel(const string& zone,
+    const RRClass& class_code,
+    const boost::shared_ptr<AddressRequestCallback>& callback,
+    AddressFamily family)
+{
+    boost::shared_ptr<ZoneEntry> entry(zone_hash_->get(HashKey(zone,
+                                                               class_code)));
+    if (entry) {
+        entry->removeCallback(callback, family);
+    }
+}
+
 } // namespace nsas
 } // namespace isc
diff --git a/src/lib/nsas/nameserver_address_store.h b/src/lib/nsas/nameserver_address_store.h
index f183871..98eb2dd 100644
--- a/src/lib/nsas/nameserver_address_store.h
+++ b/src/lib/nsas/nameserver_address_store.h
@@ -60,7 +60,7 @@ public:
     /// tests) should it use to ask questions.
     /// \param zonehashsize Size of the zone hash table.  The default value of
     /// 1009 is the first prime number above 1000.
-    /// \param nshash size Size of the nameserver hash table.  The default
+    /// \param nshashsize Size of the nameserver hash table.  The default
     /// 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.
@@ -87,6 +87,13 @@ public:
         boost::shared_ptr<AddressRequestCallback> callback, AddressFamily
         family = ANY_OK);
 
+    /// \brief cancel the given lookup action
+    ///
+    /// \param callback Callback object that would be called
+    void cancel(const std::string& zone, const dns::RRClass& class_code,
+                const boost::shared_ptr<AddressRequestCallback>& callback,
+                AddressFamily family = ANY_OK);
+
     /// \brief Protected Members
     ///
     /// These members should be private.  However, with so few public methods
diff --git a/src/lib/nsas/nameserver_entry.cc b/src/lib/nsas/nameserver_entry.cc
index 9522e81..5c7873e 100644
--- a/src/lib/nsas/nameserver_entry.cc
+++ b/src/lib/nsas/nameserver_entry.cc
@@ -35,6 +35,8 @@
 #include <dns/question.h>
 #include <resolve/resolver_interface.h>
 
+#include <asiolink/io_address.h>
+
 #include "address_entry.h"
 #include "nameserver_address.h"
 #include "nameserver_entry.h"
@@ -140,7 +142,7 @@ NameserverEntry::setAddressRTT(const IOAddress& address, uint32_t rtt) {
     AddressFamily family(V4_ONLY);
     for (;;) {
         BOOST_FOREACH(AddressEntry& entry, addresses_[family]) {
-            if (entry.getAddress().equal(address)) {
+            if (entry.getAddress().equals(address)) {
                 entry.setRTT(rtt);
                 return;
             }
@@ -181,7 +183,7 @@ NameserverEntry::updateAddressRTT(uint32_t rtt,
 {
     Lock lock(mutex_);
     for (size_t i(0); i < addresses_[family].size(); ++ i) {
-        if (addresses_[family][i].getAddress().equal(address)) {
+        if (addresses_[family][i].getAddress().equals(address)) {
             updateAddressRTTAtIndex(rtt, i, family);
             return;
         }
@@ -226,8 +228,9 @@ class NameserverEntry::ResolverCallback :
                 response_message->getRcode() != isc::dns::Rcode::NOERROR() ||
                 response_message->getRRCount(isc::dns::Message::SECTION_ANSWER) == 0) {
                 failureInternal(lock);
+                return;
             }
-                
+            
             isc::dns::RRsetIterator rrsi =
                 response_message->beginSection(isc::dns::Message::SECTION_ANSWER);
             const isc::dns::RRsetPtr response = *rrsi;
diff --git a/src/lib/nsas/nameserver_entry.h b/src/lib/nsas/nameserver_entry.h
index f6c2e8c..77937d1 100644
--- a/src/lib/nsas/nameserver_entry.h
+++ b/src/lib/nsas/nameserver_entry.h
@@ -151,7 +151,7 @@ public:
     /// Updates the RTT for a particular address
     ///
     /// \param address Address to update
-    /// \param RTT New RTT for the address
+    /// \param rtt New RTT for the address
     void setAddressRTT(const asiolink::IOAddress& address, uint32_t rtt);
 
     /// \brief Update RTT of the address that corresponding to the index
diff --git a/src/lib/nsas/tests/Makefile.am b/src/lib/nsas/tests/Makefile.am
index 530b730..9d9e61c 100644
--- a/src/lib/nsas/tests/Makefile.am
+++ b/src/lib/nsas/tests/Makefile.am
@@ -54,6 +54,7 @@ endif
 
 run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
 run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
+run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
 run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 endif
 
diff --git a/src/lib/nsas/tests/address_entry_unittest.cc b/src/lib/nsas/tests/address_entry_unittest.cc
index 716068c..02fef51 100644
--- a/src/lib/nsas/tests/address_entry_unittest.cc
+++ b/src/lib/nsas/tests/address_entry_unittest.cc
@@ -24,12 +24,12 @@
 #include <stdint.h>
 
 
-#include "../asiolink.h"
+#include <asiolink/io_address.h>
 #include "../address_entry.h"
 
 static std::string V4A_TEXT("1.2.3.4");
 static std::string V4B_TEXT("5.6.7.8");
-static std::string V6A_TEXT("2001:dead:beef::0");
+static std::string V6A_TEXT("2001:dead:beef::");
 static std::string V6B_TEXT("1984:1985::1986:1987");
 
 using namespace asiolink;
diff --git a/src/lib/nsas/tests/nameserver_address_unittest.cc b/src/lib/nsas/tests/nameserver_address_unittest.cc
index 35a46f0..1f924b3 100644
--- a/src/lib/nsas/tests/nameserver_address_unittest.cc
+++ b/src/lib/nsas/tests/nameserver_address_unittest.cc
@@ -100,7 +100,7 @@ protected:
 
 // Test that the address is equal to the address in NameserverEntry
 TEST_F(NameserverAddressTest, Address) {
-    EXPECT_TRUE(ns_address_.getAddress().equal( ns_sample_.getAddressAtIndex(TEST_ADDRESS_INDEX)));
+    EXPECT_TRUE(ns_address_.getAddress().equals( ns_sample_.getAddressAtIndex(TEST_ADDRESS_INDEX)));
 
     boost::shared_ptr<NameserverEntry> empty_ne((NameserverEntry*)NULL);
     // It will throw an NullNameserverEntryPointer exception with the empty NameserverEntry shared pointer
diff --git a/src/lib/nsas/tests/nameserver_entry_unittest.cc b/src/lib/nsas/tests/nameserver_entry_unittest.cc
index 9e4cec7..398c568 100644
--- a/src/lib/nsas/tests/nameserver_entry_unittest.cc
+++ b/src/lib/nsas/tests/nameserver_entry_unittest.cc
@@ -153,7 +153,7 @@ TEST_F(NameserverEntryTest, SetRTT) {
     int matchcount = 0;
     for (NameserverEntry::AddressVectorIterator i = newvec.begin();
         i != newvec.end(); ++i) {
-        if (i->getAddress().equal(first_address)) {
+        if (i->getAddress().equals(first_address)) {
             ++matchcount;
             EXPECT_EQ(i->getAddressEntry().getRTT(), new_rtt);
         }
@@ -188,7 +188,7 @@ TEST_F(NameserverEntryTest, Unreachable) {
     int matchcount = 0;
     for (NameserverEntry::AddressVectorIterator i = newvec.begin();
         i != newvec.end(); ++i) {
-        if (i->getAddress().equal(first_address)) {
+        if (i->getAddress().equals(first_address)) {
             ++matchcount;
             EXPECT_TRUE(i->getAddressEntry().isUnreachable());
         }
diff --git a/src/lib/nsas/tests/zone_entry_unittest.cc b/src/lib/nsas/tests/zone_entry_unittest.cc
index 8a3c6f2..d10f12d 100644
--- a/src/lib/nsas/tests/zone_entry_unittest.cc
+++ b/src/lib/nsas/tests/zone_entry_unittest.cc
@@ -165,9 +165,9 @@ protected:
         EXPECT_EQ(failure_count, callback_->unreachable_count_);
         EXPECT_EQ(success_count, callback_->successes_.size());
         for (size_t i = 0; i < callback_->successes_.size(); ++ i) {
-            EXPECT_TRUE(IOAddress("192.0.2.1").equal(
+            EXPECT_TRUE(IOAddress("192.0.2.1").equals(
                 callback_->successes_[i].getAddress()) ||
-                IOAddress("2001:db8::1").equal(
+                IOAddress("2001:db8::1").equals(
                 callback_->successes_[i].getAddress()));
         }
     }
@@ -234,7 +234,7 @@ TEST_F(ZoneEntryTest, ChangedNS) {
     EXPECT_NO_THROW(resolver_->answer(1, ns_name_, RRType::A(),
         rdata::in::A("192.0.2.1")));
     ASSERT_EQ(1, callback_->successes_.size());
-    EXPECT_TRUE(IOAddress("192.0.2.1").equal(
+    EXPECT_TRUE(IOAddress("192.0.2.1").equals(
         callback_->successes_[0].getAddress()));
     EXPECT_NO_THROW(resolver_->answer(2, ns_name_, RRType::AAAA(),
         rdata::in::AAAA("2001:db8::1")));
@@ -257,7 +257,7 @@ TEST_F(ZoneEntryTest, ChangedNS) {
     EXPECT_NO_THROW(resolver_->answer(4, different_name, RRType::A(),
         rdata::in::A("192.0.2.2")));
     ASSERT_EQ(2, callback_->successes_.size());
-    EXPECT_TRUE(IOAddress("192.0.2.2").equal(
+    EXPECT_TRUE(IOAddress("192.0.2.2").equals(
         callback_->successes_[1].getAddress()));
 
     // And now, switch back, as it timed out again
@@ -270,7 +270,7 @@ TEST_F(ZoneEntryTest, ChangedNS) {
     EXPECT_EQ(7, resolver_->requests.size());
     EXPECT_EQ(Fetchable::READY, zone->getState());
     ASSERT_EQ(3, callback_->successes_.size());
-    EXPECT_TRUE(IOAddress("192.0.2.1").equal(
+    EXPECT_TRUE(IOAddress("192.0.2.1").equals(
         callback_->successes_[0].getAddress()));
 }
 
@@ -306,9 +306,9 @@ TEST_F(ZoneEntryTest, CallbacksAnswered) {
          rdata::in::A("192.0.2.1")));
     // Two are answered (ANY and V4)
     ASSERT_EQ(2, callback_->successes_.size());
-    EXPECT_TRUE(IOAddress("192.0.2.1").equal(
+    EXPECT_TRUE(IOAddress("192.0.2.1").equals(
         callback_->successes_[0].getAddress()));
-    EXPECT_TRUE(IOAddress("192.0.2.1").equal(
+    EXPECT_TRUE(IOAddress("192.0.2.1").equals(
         callback_->successes_[1].getAddress()));
     // None are rejected
     EXPECT_EQ(0, callback_->unreachable_count_);
@@ -318,7 +318,7 @@ TEST_F(ZoneEntryTest, CallbacksAnswered) {
     // This should answer the third callback
     EXPECT_EQ(0, callback_->unreachable_count_);
     ASSERT_EQ(3, callback_->successes_.size());
-    EXPECT_TRUE(IOAddress("2001:db8::1").equal(
+    EXPECT_TRUE(IOAddress("2001:db8::1").equals(
         callback_->successes_[2].getAddress()));
     // It should think it is ready
     EXPECT_EQ(Fetchable::READY, zone->getState());
@@ -326,7 +326,7 @@ TEST_F(ZoneEntryTest, CallbacksAnswered) {
     zone->addCallback(callback_, V4_ONLY);
     EXPECT_EQ(3, resolver_->requests.size());
     ASSERT_EQ(4, callback_->successes_.size());
-    EXPECT_TRUE(IOAddress("192.0.2.1").equal(
+    EXPECT_TRUE(IOAddress("192.0.2.1").equals(
         callback_->successes_[3].getAddress()));
     EXPECT_EQ(0, callback_->unreachable_count_);
 }
@@ -366,9 +366,9 @@ TEST_F(ZoneEntryTest, CallbacksAOnly) {
     EXPECT_NO_THROW(resolver_->answer(1, ns_name_, RRType::A(),
         rdata::in::A("192.0.2.1")));
     ASSERT_EQ(2, callback_->successes_.size());
-    EXPECT_TRUE(IOAddress("192.0.2.1").equal(
+    EXPECT_TRUE(IOAddress("192.0.2.1").equals(
         callback_->successes_[0].getAddress()));
-    EXPECT_TRUE(IOAddress("192.0.2.1").equal(
+    EXPECT_TRUE(IOAddress("192.0.2.1").equals(
         callback_->successes_[1].getAddress()));
     EXPECT_EQ(1, callback_->unreachable_count_);
     // Everything arriwed, so we are ready
@@ -377,7 +377,7 @@ TEST_F(ZoneEntryTest, CallbacksAOnly) {
     zone->addCallback(callback_, V4_ONLY);
     EXPECT_EQ(3, resolver_->requests.size());
     ASSERT_EQ(3, callback_->successes_.size());
-    EXPECT_TRUE(IOAddress("192.0.2.1").equal(
+    EXPECT_TRUE(IOAddress("192.0.2.1").equals(
         callback_->successes_[2].getAddress()));
     EXPECT_EQ(1, callback_->unreachable_count_);
 
@@ -439,9 +439,9 @@ TEST_F(ZoneEntryTest, CallbackTwoNS) {
     // The other callbacks should be answered now
     EXPECT_EQ(2, callback_->unreachable_count_);
     ASSERT_EQ(2, callback_->successes_.size());
-    EXPECT_TRUE(IOAddress("2001:db8::1").equal(
+    EXPECT_TRUE(IOAddress("2001:db8::1").equals(
         callback_->successes_[0].getAddress()));
-    EXPECT_TRUE(IOAddress("2001:db8::1").equal(
+    EXPECT_TRUE(IOAddress("2001:db8::1").equals(
         callback_->successes_[1].getAddress()));
 }
 
@@ -534,7 +534,7 @@ TEST_F(ZoneEntryTest, AddressTimeout) {
          rdata::in::A("192.0.2.1"), 0));
     // It answers, not rejects
     ASSERT_EQ(1, callback_->successes_.size());
-    EXPECT_TRUE(IOAddress("192.0.2.1").equal(
+    EXPECT_TRUE(IOAddress("192.0.2.1").equals(
         callback_->successes_[0].getAddress()));
     EXPECT_EQ(0, callback_->unreachable_count_);
     // As well with IPv6
@@ -551,7 +551,7 @@ TEST_F(ZoneEntryTest, AddressTimeout) {
          rdata::in::A("192.0.2.1"), 0));
     EXPECT_EQ(0, callback_->unreachable_count_);
     ASSERT_EQ(2, callback_->successes_.size());
-    EXPECT_TRUE(IOAddress("192.0.2.1").equal(
+    EXPECT_TRUE(IOAddress("192.0.2.1").equals(
         callback_->successes_[1].getAddress()));
 }
 
diff --git a/src/lib/nsas/zone_entry.cc b/src/lib/nsas/zone_entry.cc
index 77f3dad..b0ad322 100644
--- a/src/lib/nsas/zone_entry.cc
+++ b/src/lib/nsas/zone_entry.cc
@@ -122,7 +122,7 @@ class ZoneEntry::ResolverCallback :
                  * do), so we can just reuse them instead of looking them up in
                  * the table or creating them.
                  */
-                map<string, NameserverPtr> old;
+                std::map<string, NameserverPtr> old;
                 BOOST_FOREACH(const NameserverPtr& ptr, entry_->nameservers_) {
                     old[ptr->getName()] = ptr;
                 }
@@ -143,7 +143,7 @@ class ZoneEntry::ResolverCallback :
                         Name ns_name(dynamic_cast<const rdata::generic::NS&>(
                             iterator->getCurrent()).getNSName());
                         // Try to find it in the old ones
-                        map<string, NameserverPtr>::iterator old_ns(old.find(
+                        std::map<string, NameserverPtr>::iterator old_ns(old.find(
                             ns_name.toText()));
                         /*
                          * We didn't have this nameserver before. So we just
@@ -261,6 +261,23 @@ ZoneEntry::addCallback(CallbackPtr callback, AddressFamily family) {
     }
 }
 
+void
+ZoneEntry::removeCallback(const CallbackPtr& callback, AddressFamily family) {
+    Lock lock(mutex_);
+    std::vector<boost::shared_ptr<AddressRequestCallback> >::iterator i = 
+        callbacks_[family].begin();
+    for (; i != callbacks_[family].end(); ++i) {
+        if (*i == callback) {
+            callbacks_[family].erase(i);
+            // At this point, a callback should only be in the list
+            // once (enforced by RunningQuery doing only one at a time)
+            // If that changes, we need to revise this (can't delete
+            // elements from a list we're looping over)
+            return;
+        }
+    }
+}
+
 namespace {
 
 // This just moves items from one container to another
diff --git a/src/lib/nsas/zone_entry.h b/src/lib/nsas/zone_entry.h
index 28a42ea..a1f12bc 100644
--- a/src/lib/nsas/zone_entry.h
+++ b/src/lib/nsas/zone_entry.h
@@ -101,6 +101,15 @@ public:
     void addCallback(boost::shared_ptr<AddressRequestCallback>
         callback, AddressFamily family);
 
+    /**
+     * \short Remove a callback from the list
+     *
+     * \param callback The callback itself.
+     * \param family Which address family is acceptable as an answer?
+     */
+    void removeCallback(const boost::shared_ptr<AddressRequestCallback>&
+                        callback, AddressFamily family);
+
     /// \short Protected members, so they can be accessed by tests.
     //@{
 protected:
diff --git a/src/lib/python/isc/Makefile.am b/src/lib/python/isc/Makefile.am
index bda911b..7a54909 100644
--- a/src/lib/python/isc/Makefile.am
+++ b/src/lib/python/isc/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = datasrc cc config log net notify util 
+SUBDIRS = datasrc cc config log net notify util testutils
 
 python_PYTHON = __init__.py
 
diff --git a/src/lib/python/isc/config/cfgmgr.py b/src/lib/python/isc/config/cfgmgr.py
index e347f6a..8561378 100644
--- a/src/lib/python/isc/config/cfgmgr.py
+++ b/src/lib/python/isc/config/cfgmgr.py
@@ -44,25 +44,36 @@ class ConfigManagerData:
     """This class hold the actual configuration information, and
        reads it from and writes it to persistent storage"""
 
-    def __init__(self, data_path, file_name = "b10-config.db"):
+    def __init__(self, data_path, file_name):
         """Initialize the data for the configuration manager, and
            set the version and path for the data store. Initializing
            this does not yet read the database, a call to
-           read_from_file is needed for that."""
+           read_from_file is needed for that.
+
+           In case the file_name is absolute, data_path is ignored
+           and the directory where the file_name lives is used instead.
+           """
         self.data = {}
         self.data['version'] = config_data.BIND10_CONFIG_DATA_VERSION
-        self.data_path = data_path
-        self.db_filename = data_path + os.sep + file_name
+        if os.path.isabs(file_name):
+            self.db_filename = file_name
+            self.data_path = os.path.dirname(file_name)
+        else:
+            self.db_filename = data_path + os.sep + file_name
+            self.data_path = data_path
+
+    def read_from_file(data_path, file_name):
+        """Read the current configuration found in the file file_name.
+           If file_name is absolute, data_path is ignored. Otherwise
+           we look for the file_name in data_path directory.
 
-    def read_from_file(data_path, file_name = "b10-config.db"):
-        """Read the current configuration found in the file at
-           data_path. If the file does not exist, a
-           ConfigManagerDataEmpty exception is raised. If there is a
-           parse error, or if the data in the file has the wrong
-           version, a ConfigManagerDataReadError is raised. In the first
-           case, it is probably safe to log and ignore. In the case of
-           the second exception, the best way is probably to report the
-           error and stop loading the system."""
+           If the file does not exist, a ConfigManagerDataEmpty exception is
+           raised. If there is a parse error, or if the data in the file has
+           the wrong version, a ConfigManagerDataReadError is raised. In the
+           first case, it is probably safe to log and ignore. In the case of
+           the second exception, the best way is probably to report the error
+           and stop loading the system.
+           """
         config = ConfigManagerData(data_path, file_name)
         file = None
         try:
@@ -142,20 +153,24 @@ class ConfigManagerData:
 
 class ConfigManager:
     """Creates a configuration manager. The data_path is the path
-       to the directory containing the b10-config.db file.
+       to the directory containing the configuraton file,
+       database_filename points to the configuration file.
        If session is set, this will be used as the communication
        channel session. If not, a new session will be created.
        The ability to specify a custom session is for testing purposes
        and should not be needed for normal usage."""
-    def __init__(self, data_path, session = None):
+    def __init__(self, data_path, database_filename, session=None):
         """Initialize the configuration manager. The data_path string
            is the path to the directory where the configuration is
-           stored (in <data_path>/b10-config.db). Session is an optional
+           stored (in <data_path>/<database_filename> or in
+           <database_filename>, if it is absolute). The dabase_filename
+           is the config file to load. Session is an optional
            cc-channel session. If this is not given, a new one is
-           created"""
+           created."""
         self.data_path = data_path
+        self.database_filename = database_filename
         self.module_specs = {}
-        self.config = ConfigManagerData(data_path)
+        self.config = ConfigManagerData(data_path, database_filename)
         if session:
             self.cc = session
         else:
@@ -223,17 +238,18 @@ class ConfigManager:
         return commands
 
     def read_config(self):
-        """Read the current configuration from the b10-config.db file
-           at the path specificied at init()"""
+        """Read the current configuration from the file specificied at init()"""
         try:
-            self.config = ConfigManagerData.read_from_file(self.data_path)
+            self.config = ConfigManagerData.read_from_file(self.data_path,
+                                                           self.\
+                                                           database_filename)
         except ConfigManagerDataEmpty:
             # ok, just start with an empty config
-            self.config = ConfigManagerData(self.data_path)
+            self.config = ConfigManagerData(self.data_path,
+                                            self.database_filename)
         
     def write_config(self):
-        """Write the current configuration to the b10-config.db file
-           at the path specificied at init()"""
+        """Write the current configuration to the file specificied at init()"""
         self.config.write_to_file()
 
     def _handle_get_module_spec(self, cmd):
diff --git a/src/lib/python/isc/config/tests/cfgmgr_test.py b/src/lib/python/isc/config/tests/cfgmgr_test.py
index c992d0d..9534e14 100644
--- a/src/lib/python/isc/config/tests/cfgmgr_test.py
+++ b/src/lib/python/isc/config/tests/cfgmgr_test.py
@@ -27,9 +27,20 @@ class TestConfigManagerData(unittest.TestCase):
     def setUp(self):
         self.data_path = os.environ['CONFIG_TESTDATA_PATH']
         self.writable_data_path = os.environ['CONFIG_WR_TESTDATA_PATH']
-        self.config_manager_data = ConfigManagerData(self.writable_data_path)
+        self.config_manager_data = ConfigManagerData(self.writable_data_path,
+                                                     file_name="b10-config.db")
         self.assert_(self.config_manager_data)
 
+    def test_abs_file(self):
+        """
+        Test what happens if we give the config manager an absolute path.
+        It shouldn't append the data path to it.
+        """
+        abs_path = self.data_path + os.sep + "b10-config-imaginary.db"
+        data = ConfigManagerData(os.getcwd(), abs_path)
+        self.assertEqual(abs_path, data.db_filename)
+        self.assertEqual(self.data_path, data.data_path)
+
     def test_init(self):
         self.assertEqual(self.config_manager_data.data['version'],
                          config_data.BIND10_CONFIG_DATA_VERSION)
@@ -39,10 +50,10 @@ class TestConfigManagerData(unittest.TestCase):
                          self.writable_data_path + os.sep + "b10-config.db")
 
     def test_read_from_file(self):
-        ConfigManagerData.read_from_file(self.writable_data_path)
+        ConfigManagerData.read_from_file(self.writable_data_path, "b10-config.db")
         self.assertRaises(ConfigManagerDataEmpty,
                           ConfigManagerData.read_from_file,
-                          "doesnotexist")
+                          "doesnotexist", "b10-config.db")
         self.assertRaises(ConfigManagerDataReadError,
                           ConfigManagerData.read_from_file,
                           self.data_path, "b10-config-bad1.db")
@@ -68,8 +79,8 @@ class TestConfigManagerData(unittest.TestCase):
         # by equality of the .data element. If data_path or db_filename
         # are different, but the contents are the same, it's still
         # considered equal
-        cfd1 = ConfigManagerData(self.data_path)
-        cfd2 = ConfigManagerData(self.data_path)
+        cfd1 = ConfigManagerData(self.data_path, file_name="b10-config.db")
+        cfd2 = ConfigManagerData(self.data_path, file_name="b10-config.db")
         self.assertEqual(cfd1, cfd2)
         cfd2.data_path = "some/unknown/path"
         self.assertEqual(cfd1, cfd2)
@@ -85,10 +96,25 @@ class TestConfigManager(unittest.TestCase):
         self.data_path = os.environ['CONFIG_TESTDATA_PATH']
         self.writable_data_path = os.environ['CONFIG_WR_TESTDATA_PATH']
         self.fake_session = FakeModuleCCSession()
-        self.cm = ConfigManager(self.writable_data_path, self.fake_session)
+        self.cm = ConfigManager(self.writable_data_path,
+                                database_filename="b10-config.db",
+                                session=self.fake_session)
         self.name = "TestModule"
         self.spec = isc.config.module_spec_from_file(self.data_path + os.sep + "/spec2.spec")
-    
+
+    def test_paths(self):
+        """
+        Test data_path and database filename is passed trough to
+        underlying ConfigManagerData.
+        """
+        cm = ConfigManager("datapath", "filename", self.fake_session)
+        self.assertEqual("datapath" + os.sep + "filename",
+                         cm.config.db_filename)
+        # It should preserve it while reading
+        cm.read_config()
+        self.assertEqual("datapath" + os.sep + "filename",
+                         cm.config.db_filename)
+
     def test_init(self):
         self.assert_(self.cm.module_specs == {})
         self.assert_(self.cm.data_path == self.writable_data_path)
diff --git a/src/lib/python/isc/testutils/Makefile.am b/src/lib/python/isc/testutils/Makefile.am
new file mode 100644
index 0000000..8f00a9b
--- /dev/null
+++ b/src/lib/python/isc/testutils/Makefile.am
@@ -0,0 +1 @@
+EXTRA_DIST = __init__.py parse_args.py
diff --git a/src/lib/python/isc/testutils/README b/src/lib/python/isc/testutils/README
new file mode 100644
index 0000000..c0389a8
--- /dev/null
+++ b/src/lib/python/isc/testutils/README
@@ -0,0 +1,3 @@
+This contains some shared test code for other modules and python processes.
+That's why it doesn't have its own test subdirectory and why it isn't
+installed.
diff --git a/src/lib/python/isc/testutils/__init__.py b/src/lib/python/isc/testutils/__init__.py
new file mode 100644
index 0000000..afcccf4
--- /dev/null
+++ b/src/lib/python/isc/testutils/__init__.py
@@ -0,0 +1,17 @@
+# Copyright (C) 2011  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+# Nothing here, really, it's just to tell python this directory is in
+# module hierarchy
diff --git a/src/lib/python/isc/testutils/parse_args.py b/src/lib/python/isc/testutils/parse_args.py
new file mode 100644
index 0000000..5d79137
--- /dev/null
+++ b/src/lib/python/isc/testutils/parse_args.py
@@ -0,0 +1,30 @@
+# Copyright (C) 2011  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+from optparse import OptionParser
+
+class OptsError(Exception):
+    """To know when OptionParser would exit"""
+    pass
+
+class TestOptParser(OptionParser):
+    """
+    We define our own option parser to push into the parsing routine.
+    This one does not exit the whole application on error, it just raises
+    exception. It doesn't change anything else. The application uses the
+    stock one.
+    """
+    def error(self, message):
+        raise OptsError(message)
diff --git a/src/lib/resolve/Makefile.am b/src/lib/resolve/Makefile.am
index c9f1326..0b29da4 100644
--- a/src/lib/resolve/Makefile.am
+++ b/src/lib/resolve/Makefile.am
@@ -14,5 +14,16 @@ libresolve_la_SOURCES = resolve.h resolve.cc
 libresolve_la_SOURCES += resolver_interface.h
 libresolve_la_SOURCES += resolver_callback.h resolver_callback.cc
 libresolve_la_SOURCES += response_classifier.cc response_classifier.h
+libresolve_la_SOURCES += recursive_query.cc recursive_query.h
 libresolve_la_LIBADD = $(top_builddir)/src/lib/dns/libdns++.la
 libresolve_la_LIBADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+
+# Note: the ordering matters: -Wno-... must follow -Wextra (defined in
+# B10_CXXFLAGS)
+libresolve_la_CXXFLAGS = $(AM_CXXFLAGS)
+if USE_CLANGPP
+# For clang++, we need to turn off -Werror completely.
+libresolve_la_CXXFLAGS += -Wno-error
+endif
+libresolve_la_CPPFLAGS = $(AM_CPPFLAGS)
+
diff --git a/src/lib/resolve/recursive_query.cc b/src/lib/resolve/recursive_query.cc
new file mode 100644
index 0000000..eee98ca
--- /dev/null
+++ b/src/lib/resolve/recursive_query.cc
@@ -0,0 +1,757 @@
+// 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 <netinet/in.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <unistd.h>             // for some IPC/network system calls
+
+#include <boost/lexical_cast.hpp>
+#include <boost/bind.hpp>
+
+#include <config.h>
+
+#include <log/dummylog.h>
+
+#include <dns/question.h>
+#include <dns/message.h>
+#include <dns/opcode.h>
+
+#include <resolve/resolve.h>
+#include <cache/resolver_cache.h>
+#include <nsas/address_request_callback.h>
+#include <nsas/nameserver_address.h>
+
+#include <asio.hpp>
+#include <asiolink/dns_service.h>
+#include <asiolink/io_fetch.h>
+#include <asiolink/io_service.h>
+#include <resolve/recursive_query.h>
+
+using isc::log::dlog;
+using namespace isc::dns;
+
+namespace asiolink {
+
+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,
+    isc::nsas::NameserverAddressStore& nsas,
+    isc::cache::ResolverCache& cache,
+    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),
+    nsas_(nsas), cache_(cache),
+    upstream_(new AddressVector(upstream)),
+    upstream_root_(new AddressVector(upstream_root)),
+    test_server_("", 0),
+    query_timeout_(query_timeout), client_timeout_(client_timeout),
+    lookup_timeout_(lookup_timeout), retries_(retries)
+{
+}
+
+// Set the test server - only used for unit testing.
+
+void
+RecursiveQuery::setTestServer(const std::string& address, uint16_t port) {
+    dlog("Setting test server to " + address + "(" +
+            boost::lexical_cast<std::string>(port) + ")");
+    test_server_.first = address;
+    test_server_.second = port;
+}
+
+
+namespace {
+
+typedef std::pair<std::string, uint16_t> addr_t;
+
+/*
+ * This is a query in progress. When a new query is made, this one holds
+ * the context information about it, like how many times we are allowed
+ * to retry on failure, what to do when we succeed, etc.
+ *
+ * Used by RecursiveQuery::sendQuery.
+ */
+class RunningQuery : public IOFetch::Callback {
+
+class ResolverNSASCallback : public isc::nsas::AddressRequestCallback {
+public:
+    ResolverNSASCallback(RunningQuery* rq) : rq_(rq) {}
+    
+    void success(const isc::nsas::NameserverAddress& address) {
+        dlog("Found a nameserver, sending query to " + address.getAddress().toText());
+        rq_->nsasCallbackCalled();
+        rq_->sendTo(address);
+    }
+    
+    void unreachable() {
+        dlog("Nameservers unreachable");
+        // Drop query or send servfail?
+        rq_->nsasCallbackCalled();
+        rq_->makeSERVFAIL();
+        rq_->callCallback(true);
+        rq_->stop();
+    }
+
+private:
+    RunningQuery* rq_;
+};
+
+
+private:
+    // The io service to handle async calls
+    IOService& io_;
+
+    // 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
+    boost::shared_ptr<AddressVector> upstream_;
+
+    // Test server - only used for testing.  This takes precedence over all
+    // other servers if the port is non-zero.
+    std::pair<std::string, uint16_t> test_server_;
+
+    // Buffer to store the intermediate results.
+    OutputBufferPtr buffer_;
+
+    // The callback will be called when we have either decided we
+    // are done, or when we give up
+    isc::resolve::ResolverInterface::CallbackPtr resolvercallback_;
+
+    // Protocol used for the last query.  This is set to IOFetch::UDP when a
+    // new upstream query is initiated, and changed to IOFetch::TCP if a
+    // packet is returned with the TC bit set.  It is stored here to detect the
+    // case of a TCP packet being returned with the TC bit set.
+    IOFetch::Protocol protocol_;
+
+    // To prevent both unreasonably long cname chains and cname loops,
+    // we simply keep a counter of the number of CNAMEs we have
+    // followed so far (and error if it exceeds RESOLVER_MAX_CNAME_CHAIN
+    // from lib/resolve/response_classifier.h)
+    unsigned cname_count_;
+
+    /*
+     * TODO Do something more clever with timeouts. In the long term, some
+     *     computation of average RTT, increase with each retry, etc.
+     */
+    // Timeout information for outgoing queries
+    int query_timeout_;
+    unsigned retries_;
+
+    // normal query state
+
+    // Update the question that will be sent to the server
+    void setQuestion(const Question& new_question) {
+        question_ = new_question;
+    }
+
+    // TODO: replace by our wrapper
+    asio::deadline_timer client_timer;
+    asio::deadline_timer lookup_timer;
+
+    // If we timed out ourselves (lookup timeout), stop issuing queries
+    bool done_;
+
+    // If we have a client timeout, we call back with a failure message,
+    // but we do not stop yet. We use this variable to make sure we 
+    // don't call back a second time later
+    bool callback_called_;
+
+    // Reference to our NSAS
+    isc::nsas::NameserverAddressStore& nsas_;
+
+    // Reference to our cache
+    isc::cache::ResolverCache& cache_;
+    
+    // the 'current' zone we are in (i.e.) we start out at the root,
+    // and for each delegation this gets updated with the zone the
+    // delegation points to.
+    // TODO: make this a Name (it is a string right now because most
+    // of the call we use it in take a string, we need update those
+    // too).
+    std::string cur_zone_;
+    
+    // This is the handler we pass on to the NSAS; it is called when
+    // the NSAS has an address for us to query
+    boost::shared_ptr<ResolverNSASCallback> nsas_callback_;
+
+    // this is set to true if we have asked the nsas to give us
+    // an address and we are waiting for it to call us back.
+    // We use is to cancel the outstanding callback in case we
+    // have a lookup timeout and decide to give up
+    bool nsas_callback_out_;
+
+    // This is the nameserver we have an outstanding query to.
+    // It is used to update the RTT once the query returns
+    isc::nsas::NameserverAddress current_ns_address;
+
+    // The moment in time we sent a query to the nameserver above.
+    struct timeval current_ns_qsent_time;
+    
+    // RunningQuery deletes itself when it is done. In order for us
+    // to do this safely, we must make sure that there are no events
+    // that might call back to it. There are two types of events in
+    // this sense; the timers we set ourselves (lookup and client),
+    // and outstanding queries to nameservers. When each of these is
+    // started, we increase this value. When they fire, it is decreased
+    // again. We cannot delete ourselves until this value is back to 0.
+    //
+    // Note that the NSAS callback is *not* seen as an outstanding
+    // event; we can cancel the NSAS callback safely.
+    size_t outstanding_events_;
+
+    // perform a single lookup; first we check the cache to see
+    // if we have a response for our query stored already. if
+    // so, call handlerecursiveresponse(), if not, we call send()
+    void doLookup() {
+        dlog("doLookup: try cache");
+        Message cached_message(Message::RENDER);
+        isc::resolve::initResponseMessage(question_, cached_message);
+        if (cache_.lookup(question_.getName(), question_.getType(),
+                          question_.getClass(), cached_message)) {
+            dlog("Message found in cache, continuing with that");
+            // Should these be set by the cache too?
+            cached_message.setOpcode(Opcode::QUERY());
+            cached_message.setRcode(Rcode::NOERROR());
+            cached_message.setHeaderFlag(Message::HEADERFLAG_QR);
+            if (handleRecursiveAnswer(cached_message)) {
+                callCallback(true);
+                stop();
+            }
+        } else {
+            cur_zone_ = ".";
+            send();
+        }
+        
+    }
+
+    // Send the current question to the given nameserver address
+    void sendTo(const isc::nsas::NameserverAddress& address) {
+        // We need to keep track of the Address, so that we can update
+        // the RTT
+        current_ns_address = address;
+        gettimeofday(&current_ns_qsent_time, NULL);
+        ++outstanding_events_;
+        IOFetch query(protocol_, io_, question_,
+            current_ns_address.getAddress(),
+            53, buffer_, this,
+            query_timeout_);
+        io_.get_io_service().post(query);
+    }
+    
+    // 'general' send; if we are in forwarder mode, send a query to
+    // a random nameserver in our forwarders list. If we are in
+    // recursive mode, ask the NSAS to give us an address.
+    void send(IOFetch::Protocol protocol = IOFetch::UDP) {
+        // If are in forwarder mode, send it to a random
+        // forwarder. If not, ask the NSAS for an address
+        const int uc = upstream_->size();
+        protocol_ = protocol;   // Store protocol being used for this
+        if (test_server_.second != 0) {
+            dlog("Sending upstream query (" + question_.toText() +
+                 ") to test server at " + test_server_.first);
+            ++outstanding_events_;
+            IOFetch query(protocol, io_, question_,
+                test_server_.first,
+                test_server_.second, buffer_, this,
+                query_timeout_);
+            io_.get_io_service().post(query);
+        } else if (uc > 0) {
+            // TODO: use boost, or rand()-utility function we provide
+            int serverIndex = rand() % uc;
+            dlog("Sending upstream query (" + question_.toText() +
+                ") to " + upstream_->at(serverIndex).first);
+            ++outstanding_events_;
+            IOFetch query(protocol, io_, question_,
+                upstream_->at(serverIndex).first,
+                upstream_->at(serverIndex).second, buffer_, this,
+                query_timeout_);
+            io_.get_io_service().post(query);
+        } else {
+            // Ask the NSAS for an address for the current zone,
+            // the callback will call the actual sendTo()
+            dlog("Look up nameserver for " + cur_zone_ + " in NSAS");
+            // Can we have multiple calls to nsas_out? Let's assume not
+            // for now
+            assert(!nsas_callback_out_);
+            nsas_callback_out_ = true;
+            nsas_.lookup(cur_zone_, question_.getClass(), nsas_callback_);
+        }
+    }
+    
+    // Called by our NSAS callback handler so we know we do not have
+    // an outstanding NSAS call anymore.
+    void nsasCallbackCalled() {
+        nsas_callback_out_ = false;
+    }
+
+    // This function is called by operator() and lookup();
+    // We have an answer either from a nameserver or the cache, and
+    // we do not know yet if this is a final answer we can send back or
+    // that more recursive processing needs to be done.
+    // Depending on the content, we go on recursing or return
+    //
+    // This method also updates the cache, depending on the content
+    // of the message
+    //
+    // returns true if we are done (either we have an answer or an
+    //              error message)
+    // returns false if we are not done
+    bool handleRecursiveAnswer(const Message& incoming) {
+        dlog("Handle response");
+        // In case we get a CNAME, we store the target
+        // here (classify() will set it when it walks through
+        // the cname chain to verify it).
+        Name cname_target(question_.getName());
+        
+        isc::resolve::ResponseClassifier::Category category =
+            isc::resolve::ResponseClassifier::classify(
+                question_, incoming, cname_target, cname_count_);
+
+        bool found_ns_address = false;
+            
+        switch (category) {
+        case isc::resolve::ResponseClassifier::ANSWER:
+        case isc::resolve::ResponseClassifier::ANSWERCNAME:
+            // Done. copy and return.
+            dlog("Response is an answer");
+            isc::resolve::copyResponseMessage(incoming, answer_message_);
+            cache_.update(*answer_message_);
+            return true;
+            break;
+        case isc::resolve::ResponseClassifier::CNAME:
+            dlog("Response is CNAME!");
+            // (unfinished) CNAME. We set our question_ to the CNAME
+            // target, then start over at the beginning (for now, that
+            // is, we reset our 'current servers' to the root servers).
+            if (cname_count_ >= RESOLVER_MAX_CNAME_CHAIN) {
+                // just give up
+                dlog("CNAME chain too long");
+                makeSERVFAIL();
+                return true;
+            }
+
+            answer_message_->appendSection(Message::SECTION_ANSWER,
+                                           incoming);
+
+            question_ = Question(cname_target, question_.getClass(),
+                                 question_.getType());
+
+            dlog("Following CNAME chain to " + question_.toText());
+            doLookup();
+            return false;
+            break;
+        case isc::resolve::ResponseClassifier::NXDOMAIN:
+        case isc::resolve::ResponseClassifier::NXRRSET:
+            dlog("Response is NXDOMAIN or NXRRSET");
+            // NXDOMAIN, just copy and return.
+            dlog(incoming.toText());
+            isc::resolve::copyResponseMessage(incoming, answer_message_);
+            // no negcache yet
+            //cache_.update(*answer_message_);
+            return true;
+            break;
+        case isc::resolve::ResponseClassifier::REFERRAL:
+            dlog("Response is referral");
+            cache_.update(incoming);
+            // Referral. For now we just take the first glue address
+            // we find and continue with that
+
+            // auth section should have at least one RRset
+            // and one of them should be an NS (otherwise
+            // classifier should have error'd)
+            // TODO: should we check if it really is subzone?
+            for (RRsetIterator rrsi = incoming.beginSection(Message::SECTION_AUTHORITY);
+                 rrsi != incoming.endSection(Message::SECTION_AUTHORITY) && !found_ns_address;
+                 ++rrsi) {
+                ConstRRsetPtr rrs = *rrsi;
+                if (rrs->getType() == RRType::NS()) {
+                    // TODO: make cur_zone_ a Name instead of a string
+                    // (this requires a few API changes in related
+                    // libraries, so as not to need many conversions)
+                    cur_zone_ = rrs->getName().toText();
+                    dlog("Referred to zone " + cur_zone_);
+                    found_ns_address = true;
+                    break;
+                }
+            }
+
+            if (found_ns_address) {
+                // next resolver round
+                // we do NOT use doLookup() here, but send() (i.e. we
+                // skip the cache), since if we had the final answer
+                // instead of a delegation cached, we would have been
+                // there by now.
+                send();
+                return false;
+            } else {
+                dlog("No NS RRset in referral?");
+                // TODO this will result in answering with the delegation. oh well
+                isc::resolve::copyResponseMessage(incoming, answer_message_);
+                return true;
+            }
+            break;
+        case isc::resolve::ResponseClassifier::TRUNCATED:
+            // Truncated packet.  If the protocol we used for the last one is
+            // UDP, re-query using TCP.  Otherwise regard it as an error.
+            if (protocol_ == IOFetch::UDP) {
+                dlog("Response truncated, re-querying over TCP");
+                send(IOFetch::TCP);
+                return false;
+            }
+            // Was a TCP query so we have received a packet over TCP with the TC
+            // bit set: drop through to common error processing.
+            // TODO: Can we use what we have received instead of discarding it?
+
+        case isc::resolve::ResponseClassifier::EMPTY:
+        case isc::resolve::ResponseClassifier::EXTRADATA:
+        case isc::resolve::ResponseClassifier::INVNAMCLASS:
+        case isc::resolve::ResponseClassifier::INVTYPE:
+        case isc::resolve::ResponseClassifier::MISMATQUEST:
+        case isc::resolve::ResponseClassifier::MULTICLASS:
+        case isc::resolve::ResponseClassifier::NOTONEQUEST:
+        case isc::resolve::ResponseClassifier::NOTRESPONSE:
+        case isc::resolve::ResponseClassifier::NOTSINGLE:
+        case isc::resolve::ResponseClassifier::OPCODE:
+        case isc::resolve::ResponseClassifier::RCODE:
+            // Should we try a different server rather than SERVFAIL?
+            makeSERVFAIL();
+            return true;
+            break;
+        }
+
+        // Since we do not have a default in the switch above,
+        // the compiler should have errored on any missing case
+        // statements.
+        assert(false);
+        return true;
+    }
+    
+public:
+    RunningQuery(IOService& io,
+        const Question& question,
+        MessagePtr answer_message,
+        boost::shared_ptr<AddressVector> upstream,
+        std::pair<std::string, uint16_t>& test_server,
+        OutputBufferPtr buffer,
+        isc::resolve::ResolverInterface::CallbackPtr cb,
+        int query_timeout, int client_timeout, int lookup_timeout,
+        unsigned retries,
+        isc::nsas::NameserverAddressStore& nsas,
+        isc::cache::ResolverCache& cache) :
+        io_(io),
+        question_(question),
+        answer_message_(answer_message),
+        upstream_(upstream),
+        test_server_(test_server),
+        buffer_(buffer),
+        resolvercallback_(cb),
+        protocol_(IOFetch::UDP),
+        cname_count_(0),
+        query_timeout_(query_timeout),
+        retries_(retries),
+        client_timer(io.get_io_service()),
+        lookup_timer(io.get_io_service()),
+        done_(false),
+        callback_called_(false),
+        nsas_(nsas),
+        cache_(cache),
+        nsas_callback_(new ResolverNSASCallback(this)),
+        nsas_callback_out_(false),
+        outstanding_events_(0)
+    {
+        // Setup the timer to stop trying (lookup_timeout)
+        if (lookup_timeout >= 0) {
+            lookup_timer.expires_from_now(
+                boost::posix_time::milliseconds(lookup_timeout));
+            ++outstanding_events_;
+            lookup_timer.async_wait(boost::bind(&RunningQuery::lookupTimeout, this));
+        }
+        
+        // Setup the timer to send an answer (client_timeout)
+        if (client_timeout >= 0) {
+            client_timer.expires_from_now(
+                boost::posix_time::milliseconds(client_timeout));
+            ++outstanding_events_;
+            client_timer.async_wait(boost::bind(&RunningQuery::clientTimeout, this));
+        }
+        
+        doLookup();
+    }
+
+    // called if we have a lookup timeout; if our callback has
+    // not been called, call it now. Then stop.
+    void lookupTimeout() {
+        if (!callback_called_) {
+            makeSERVFAIL();
+            callCallback(true);
+        }
+        assert(outstanding_events_ > 0);
+        --outstanding_events_;
+        stop();
+    }
+    
+    // called if we have a client timeout; if our callback has
+    // not been called, call it now. But do not stop.
+    void clientTimeout() {
+        if (!callback_called_) {
+            makeSERVFAIL();
+            callCallback(true);
+        }
+        assert(outstanding_events_ > 0);
+        --outstanding_events_;
+        if (outstanding_events_ == 0) {
+            stop();
+        }
+    }
+
+    // If the callback has not been called yet, call it now
+    // If success is true, we call 'success' with our answer_message
+    // If it is false, we call failure()
+    void callCallback(bool success) {
+        if (!callback_called_) {
+            callback_called_ = true;
+
+            // There are two types of messages we could store in the
+            // cache;
+            // 1. answers to our fetches from authoritative servers,
+            //    exactly as we receive them, and
+            // 2. answers to queries we received from clients, which
+            //    have received additional processing (following CNAME
+            //    chains, for instance)
+            //
+            // Doing only the first would mean we would have to re-do
+            // processing when we get data from our cache, and doing
+            // only the second would miss out on the side-effect of
+            // having nameserver data in our cache.
+            //
+            // So right now we do both. Since the cache (currently)
+            // stores Messages on their question section only, this
+            // does mean that we overwrite the messages we stored in
+            // the previous iteration if we are following a delegation.
+            if (success) {
+                resolvercallback_->success(answer_message_);
+            } else {
+                resolvercallback_->failure();
+            }
+        }
+    }
+
+    // We are done. If there are no more outstanding events, we delete
+    // ourselves. If there are any, we do not.
+    void stop() {
+        done_ = true;
+        if (nsas_callback_out_) {
+            nsas_.cancel(cur_zone_, question_.getClass(), nsas_callback_);
+            nsas_callback_out_ = false;
+        }
+        client_timer.cancel();
+        lookup_timer.cancel();
+        if (outstanding_events_ > 0) {
+            return;
+        } else {
+            delete this;
+        }
+    }
+
+    // This function is used as callback from DNSQuery.
+    virtual void operator()(IOFetch::Result result) {
+        // XXX is this the place for TCP retry?
+        assert(outstanding_events_ > 0);
+        --outstanding_events_;
+        
+        if (!done_ && result != IOFetch::TIME_OUT) {
+            // we got an answer
+
+            // Update the NSAS with the time it took
+            struct timeval cur_time;
+            gettimeofday(&cur_time, NULL);
+            uint32_t rtt;
+            if (cur_time.tv_sec >= current_ns_qsent_time.tv_sec ||
+                cur_time.tv_usec > current_ns_qsent_time.tv_usec) {
+                rtt = 1000 * (cur_time.tv_sec - current_ns_qsent_time.tv_sec);
+                rtt += (cur_time.tv_usec - current_ns_qsent_time.tv_usec) / 1000;
+            } else {
+                rtt = 1;
+            }
+
+            dlog("RTT: " + boost::lexical_cast<std::string>(rtt));
+            current_ns_address.updateRTT(rtt);
+            
+            Message incoming(Message::PARSE);
+            InputBuffer ibuf(buffer_->getData(), buffer_->getLength());
+            incoming.fromWire(ibuf);
+
+            buffer_->clear();
+            if (recursive_mode() &&
+                incoming.getRcode() == Rcode::NOERROR()) {
+                done_ = handleRecursiveAnswer(incoming);
+            } else {
+                isc::resolve::copyResponseMessage(incoming, answer_message_);
+                done_ = true;
+            }
+            
+            if (done_) {
+                callCallback(true);
+                stop();
+            }
+        } else if (!done_ && retries_--) {
+            // Query timed out, but we have some retries, so send again
+            dlog("Timeout for " + question_.toText() + " to " + current_ns_address.getAddress().toText() + ", resending query");
+            if (recursive_mode()) {
+                current_ns_address.updateRTT(isc::nsas::AddressEntry::UNREACHABLE);
+            }
+            send();
+        } else {
+            // We are either already done, or out of retries
+            if (recursive_mode() && result == IOFetch::TIME_OUT) {
+                dlog("Timeout for " + question_.toText() + " to " + current_ns_address.getAddress().toText() + ", giving up");
+                current_ns_address.updateRTT(isc::nsas::AddressEntry::UNREACHABLE);
+            }
+            if (!callback_called_) {
+                makeSERVFAIL();
+                callCallback(true);
+            }
+            stop();
+        }
+    }
+    
+    // Clear the answer parts of answer_message, and set the rcode
+    // to servfail
+    void makeSERVFAIL() {
+        isc::resolve::makeErrorMessage(answer_message_, Rcode::SERVFAIL());
+    }
+    
+    // Returns true if we are in 'recursive' mode
+    // Returns false if we are in 'forwarding' mode
+    // (i.e. if we have anything in upstream_)
+    bool recursive_mode() const {
+        return upstream_->empty();
+    }
+};
+
+}
+
+void
+RecursiveQuery::resolve(const QuestionPtr& question,
+    const isc::resolve::ResolverInterface::CallbackPtr callback)
+{
+    IOService& io = dns_service_.getIOService();
+
+    MessagePtr answer_message(new Message(Message::RENDER));
+    isc::resolve::initResponseMessage(*question, *answer_message);
+
+    OutputBufferPtr buffer(new OutputBuffer(0));
+
+    dlog("Asked to resolve: " + question->toText());
+    
+    dlog("Try out cache first (direct call to resolve)");
+    // First try to see if we have something cached in the messagecache
+    if (cache_.lookup(question->getName(), question->getType(),
+                      question->getClass(), *answer_message) &&
+        answer_message->getRRCount(Message::SECTION_ANSWER) > 0) {
+        dlog("Message found in cache, returning that");
+        // TODO: err, should cache set rcode as well?
+        answer_message->setRcode(Rcode::NOERROR());
+        callback->success(answer_message);
+    } else {
+        // Perhaps we only have the one RRset?
+        // TODO: can we do this? should we check for specific types only?
+        RRsetPtr cached_rrset = cache_.lookup(question->getName(),
+                                              question->getType(),
+                                              question->getClass());
+        if (cached_rrset) {
+            dlog("Found single RRset in cache");
+            answer_message->addRRset(Message::SECTION_ANSWER,
+                                     cached_rrset);
+            answer_message->setRcode(Rcode::NOERROR());
+            callback->success(answer_message);
+        } else {
+            dlog("Message not found in cache, starting recursive query");
+            // It will delete itself when it is done
+            new RunningQuery(io, *question, answer_message, upstream_,
+                             test_server_, buffer, callback,
+                             query_timeout_, client_timeout_,
+                             lookup_timeout_, retries_, nsas_, cache_);
+        }
+    }
+}
+
+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.
+    IOService& io = dns_service_.getIOService();
+
+    isc::resolve::ResolverInterface::CallbackPtr crs(
+        new isc::resolve::ResolverCallbackServer(server));
+
+    // TODO: general 'prepareinitialanswer'
+    answer_message->setOpcode(isc::dns::Opcode::QUERY());
+    answer_message->addQuestion(question);
+    
+    dlog("Asked to resolve: " + question.toText());
+    
+    // First try to see if we have something cached in the messagecache
+    dlog("Try out cache first (started by incoming event)");
+    if (cache_.lookup(question.getName(), question.getType(),
+                      question.getClass(), *answer_message) &&
+        answer_message->getRRCount(Message::SECTION_ANSWER) > 0) {
+        dlog("Message found in cache, returning that");
+        // TODO: err, should cache set rcode as well?
+        answer_message->setRcode(Rcode::NOERROR());
+        crs->success(answer_message);
+    } else {
+        // Perhaps we only have the one RRset?
+        // TODO: can we do this? should we check for specific types only?
+        RRsetPtr cached_rrset = cache_.lookup(question.getName(),
+                                              question.getType(),
+                                              question.getClass());
+        if (cached_rrset) {
+            dlog("Found single RRset in cache");
+            answer_message->addRRset(Message::SECTION_ANSWER,
+                                     cached_rrset);
+            answer_message->setRcode(Rcode::NOERROR());
+            crs->success(answer_message);
+        } else {
+            dlog("Message not found in cache, starting recursive query");
+            // It will delete itself when it is done
+            new RunningQuery(io, question, answer_message, upstream_,
+                             test_server_, buffer, crs, query_timeout_,
+                             client_timeout_, lookup_timeout_, retries_,
+                             nsas_, cache_);
+        }
+    }
+}
+
+
+
+} // namespace asiolink
diff --git a/src/lib/resolve/recursive_query.h b/src/lib/resolve/recursive_query.h
new file mode 100644
index 0000000..1180d55
--- /dev/null
+++ b/src/lib/resolve/recursive_query.h
@@ -0,0 +1,133 @@
+// 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 __RECURSIVE_QUERY_H
+#define __RECURSIVE_QUERY_H 1
+
+#include <asiolink/dns_service.h>
+#include <asiolink/dns_server.h>
+#include <dns/buffer.h>
+#include <nsas/nameserver_address_store.h>
+#include <cache/resolver_cache.h>
+
+namespace asiolink {
+/// \brief The \c RecursiveQuery class provides a layer of abstraction around
+/// the ASIO code that carries out an upstream query.
+///
+/// This design is very preliminary; currently it is only capable of
+/// handling simple forward requests to a single resolver.
+class RecursiveQuery {
+    ///
+    /// \name Constructors
+    ///
+    //@{
+public:
+    /// \brief Constructor
+    ///
+    /// This is currently the only way to construct \c RecursiveQuery
+    /// 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 query_timeout Timeout value for queries we sent, in ms
+    /// \param client_timeout Timeout value for when we send back an
+    ///        error, in ms
+    /// \param lookup_timeout Timeout value for when we give up, in ms
+    /// \param retries how many times we try again (0 means just send and
+    ///     and return if it returs).
+    RecursiveQuery(DNSService& dns_service,
+                   isc::nsas::NameserverAddressStore& nsas,
+                   isc::cache::ResolverCache& cache,
+                   const std::vector<std::pair<std::string, uint16_t> >&
+                   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 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 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 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 resolve(const isc::dns::Question& question,
+                 isc::dns::MessagePtr answer_message,
+                 isc::dns::OutputBufferPtr buffer,
+                 DNSServer* server);
+
+    /// \brief Set Test Server
+    ///
+    /// This method is *only* for unit testing the class.  If set, it enables
+    /// recursive behaviour but, regardless of responses received, sends every
+    /// query to the test server.
+    ///
+    /// The test server is enabled by setting a non-zero port number.
+    ///
+    /// \param address IP address of the test server.
+    /// \param port Port number of the test server
+    void setTestServer(const std::string& address, uint16_t port);
+    
+private:
+    DNSService& dns_service_;
+    isc::nsas::NameserverAddressStore& nsas_;
+    isc::cache::ResolverCache& cache_;
+    boost::shared_ptr<std::vector<std::pair<std::string, uint16_t> > >
+        upstream_;
+    boost::shared_ptr<std::vector<std::pair<std::string, uint16_t> > >
+        upstream_root_;
+    std::pair<std::string, uint16_t> test_server_;
+    int query_timeout_;
+    int client_timeout_;
+    int lookup_timeout_;
+    unsigned retries_;
+};
+
+}      // namespace asiolink
+#endif // __RECURSIVE_QUERY_H
diff --git a/src/lib/resolve/response_classifier.cc b/src/lib/resolve/response_classifier.cc
index 45e9cbc..02808e4 100644
--- a/src/lib/resolve/response_classifier.cc
+++ b/src/lib/resolve/response_classifier.cc
@@ -114,13 +114,17 @@ ResponseClassifier::Category ResponseClassifier::classify(
             );
 
     // If there is nothing in the answer section, it is a referral - unless
-    // there is nothing in the authority section
+    // there is no NS in the authority section
     if (answer.empty()) {
         if (authority.empty()) {
             return (EMPTY);
-        } else {
-            return (REFERRAL);
         }
+        for (int i = 0; i < authority.size(); ++i) {
+            if (authority[i]->getType() == RRType::NS()) {
+                return (REFERRAL);
+            }
+        }
+        return (NXRRSET);
     }
 
     // Look at two cases - one RRset in the answer and multiple RRsets in
diff --git a/src/lib/resolve/response_classifier.h b/src/lib/resolve/response_classifier.h
index bee0628..3821560 100644
--- a/src/lib/resolve/response_classifier.h
+++ b/src/lib/resolve/response_classifier.h
@@ -53,6 +53,7 @@ public:
         ANSWERCNAME,        ///< Response was a CNAME chain ending in an answer
         CNAME,              ///< Response was a CNAME
         NXDOMAIN,           ///< Response was an NXDOMAIN
+        NXRRSET,            ///< Response was name exists, but type does not
         REFERRAL,           ///< Response contains a referral
 
         // Codes indicating that a message is invalid.  Note that the error()
diff --git a/src/lib/resolve/tests/Makefile.am b/src/lib/resolve/tests/Makefile.am
index e669384..da28f78 100644
--- a/src/lib/resolve/tests/Makefile.am
+++ b/src/lib/resolve/tests/Makefile.am
@@ -14,12 +14,19 @@ 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 += $(top_srcdir)/src/lib/dns/tests/unittest_util.h
+run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
 run_unittests_SOURCES += resolve_unittest.cc
 run_unittests_SOURCES += resolver_callback_unittest.cc
 run_unittests_SOURCES += response_classifier_unittest.cc
+run_unittests_SOURCES += recursive_query_unittest.cc
+run_unittests_SOURCES += recursive_query_unittest_2.cc
 
 run_unittests_LDADD = $(GTEST_LDADD)
 run_unittests_LDADD +=  $(top_builddir)/src/lib/exceptions/libexceptions.la
+run_unittests_LDADD +=  $(top_builddir)/src/lib/nsas/libnsas.la
+run_unittests_LDADD +=  $(top_builddir)/src/lib/cache/libcache.la
+run_unittests_LDADD +=  $(top_builddir)/src/lib/asiolink/libasiolink.la
 run_unittests_LDADD +=  $(top_builddir)/src/lib/resolve/libresolve.la
 run_unittests_LDADD +=  $(top_builddir)/src/lib/dns/libdns++.la
 
diff --git a/src/lib/resolve/tests/recursive_query_unittest.cc b/src/lib/resolve/tests/recursive_query_unittest.cc
new file mode 100644
index 0000000..ab1ffa3
--- /dev/null
+++ b/src/lib/resolve/tests/recursive_query_unittest.cc
@@ -0,0 +1,861 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include <string.h>
+
+#include <boost/lexical_cast.hpp>
+#include <boost/bind.hpp>
+#include <boost/date_time/posix_time/posix_time_types.hpp>
+
+#include <gtest/gtest.h>
+
+#include <exceptions/exceptions.h>
+
+#include <dns/tests/unittest_util.h>
+#include <dns/rcode.h>
+
+#include <dns/buffer.h>
+#include <dns/message.h>
+
+#include <nsas/nameserver_address_store.h>
+#include <cache/resolver_cache.h>
+
+// IMPORTANT: We shouldn't directly use ASIO definitions in this test.
+// In particular, we must not include asio.hpp in this file.
+// The asiolink module is primarily intended to be a wrapper that hide the
+// details of the underlying implementations.  We need to test the wrapper
+// level behaviors.  In addition, some compilers reject to compile this file
+// if we include asio.hpp unless we specify a special compiler option.
+// If we need to test something at the level of underlying ASIO and need
+// their definition, that test should go to asiolink/internal/tests.
+#include <resolve/recursive_query.h>
+#include <asiolink/io_socket.h>
+#include <asiolink/io_service.h>
+#include <asiolink/io_message.h>
+#include <asiolink/io_error.h>
+#include <asiolink/dns_lookup.h>
+#include <asiolink/simple_callback.h>
+
+using isc::UnitTestUtil;
+using namespace std;
+using namespace asiolink;
+using namespace isc::dns;
+
+namespace {
+const char* const TEST_SERVER_PORT = "53535";
+const char* const TEST_CLIENT_PORT = "53536";
+const char* const TEST_IPV6_ADDR = "::1";
+const char* const TEST_IPV4_ADDR = "127.0.0.1";
+// This data is intended to be valid as a DNS/TCP-like message: the first
+// two octets encode the length of the rest of the data.  This is crucial
+// for the tests below.
+const uint8_t test_data[] = {0, 4, 1, 2, 3, 4};
+
+// This function returns an addrinfo structure for use by tests, using
+// different addresses and ports depending on whether we're testing
+// IPv4 or v6, TCP or UDP, and client or server operation.
+struct addrinfo*
+resolveAddress(const int family, const int protocol, const bool client) {
+    const char* const addr = (family == AF_INET6) ?
+        TEST_IPV6_ADDR : TEST_IPV4_ADDR;
+    const char* const port = client ? TEST_CLIENT_PORT : TEST_SERVER_PORT;
+
+    struct addrinfo hints;
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_family = family;
+    hints.ai_socktype = (protocol == IPPROTO_UDP) ? SOCK_DGRAM : SOCK_STREAM;
+    hints.ai_protocol = protocol;
+    hints.ai_flags = AI_NUMERICSERV;
+
+    struct addrinfo* res;
+    const int error = getaddrinfo(addr, port, &hints, &res);
+    if (error != 0) {
+        isc_throw(IOError, "getaddrinfo failed: " << gai_strerror(error));
+    }
+
+    return (res);
+}
+
+// This fixture is a framework for various types of network operations
+// using the ASIO interfaces.  Each test case creates an IOService object,
+// opens a local "client" socket for testing, sends data via the local socket
+// to the service that would run in the IOService object.
+// A mock callback function (an ASIOCallBack object) is registered with the
+// IOService object, so the test code should be able to examine the data
+// received on the server side.  It then checks the received data matches
+// expected parameters.
+// If initialization parameters of the IOService should be modified, the test
+// case can do it using the setDNSService() method.
+// Note: the set of tests in RecursiveQueryTest use actual network services and may
+// involve undesirable side effects such as blocking.
+class RecursiveQueryTest : public ::testing::Test {
+protected:
+    RecursiveQueryTest();
+    ~RecursiveQueryTest() {
+        if (res_ != NULL) {
+            freeaddrinfo(res_);
+        }
+        if (sock_ != -1) {
+            close(sock_);
+        }
+        delete dns_service_;
+        delete callback_;
+        delete io_service_;
+    }
+
+    // Send a test UDP packet to a mock server
+    void sendUDP(const int family) {
+        res_ = resolveAddress(family, IPPROTO_UDP, false);
+
+        sock_ = socket(res_->ai_family, res_->ai_socktype, res_->ai_protocol);
+        if (sock_ < 0) {
+            isc_throw(IOError, "failed to open test socket");
+        }
+        const int cc = sendto(sock_, test_data, sizeof(test_data), 0,
+                              res_->ai_addr, res_->ai_addrlen);
+        if (cc != sizeof(test_data)) {
+            isc_throw(IOError, "unexpected sendto result: " << cc);
+        }
+        io_service_->run();
+    }
+
+    // Send a test TCP packet to a mock server
+    void sendTCP(const int family) {
+        res_ = resolveAddress(family, IPPROTO_TCP, false);
+
+        sock_ = socket(res_->ai_family, res_->ai_socktype, res_->ai_protocol);
+        if (sock_ < 0) {
+            isc_throw(IOError, "failed to open test socket");
+        }
+        if (connect(sock_, res_->ai_addr, res_->ai_addrlen) < 0) {
+            isc_throw(IOError, "failed to connect to the test server");
+        }
+        const int cc = send(sock_, test_data, sizeof(test_data), 0);
+        if (cc != sizeof(test_data)) {
+            isc_throw(IOError, "unexpected send result: " << cc);
+        }
+        io_service_->run();
+    }
+
+    // Receive a UDP packet from a mock server; used for testing
+    // recursive lookup.  The caller must place a RecursiveQuery 
+    // on the IO Service queue before running this routine.
+    void recvUDP(const int family, void* buffer, size_t& size) {
+        res_ = resolveAddress(family, IPPROTO_UDP, true);
+
+        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, "bind failed: " << strerror(errno));
+        }
+
+        // The IO service queue should have a RecursiveQuery object scheduled
+        // to run at this point.  This call will cause it to begin an
+        // async send, then return.
+        io_service_->run_one();
+
+        // ... and this one will block until the send has completed
+        io_service_->run_one();
+
+        // Now we attempt to recv() whatever was sent.
+        // XXX: there's no guarantee the receiving socket can immediately get
+        // the packet.  Normally we can perform blocking recv to wait for it,
+        // but in theory it's even possible that the packet is lost.
+        // In order to prevent the test from hanging in such a worst case
+        // we add an ad hoc timeout.
+        const struct timeval timeo = { 10, 0 };
+        int recv_options = 0;
+        if (setsockopt(sock_, SOL_SOCKET, SO_RCVTIMEO, &timeo,
+                       sizeof(timeo))) {
+            if (errno == ENOPROTOOPT) {
+                // Workaround for Solaris: it doesn't accept SO_RCVTIMEO
+                // with the error of ENOPROTOOPT.  Since this is a workaround
+                // for rare error cases anyway, we simply switch to the
+                // "don't wait" mode.  If we still find an error in recv()
+                // can happen often we'll consider a more complete solution.
+                recv_options = MSG_DONTWAIT;
+            } else {
+                isc_throw(IOError, "set RCVTIMEO failed: " << strerror(errno));
+            }
+        }
+        const int ret = recv(sock_, buffer, size, recv_options);
+        if (ret < 0) {
+            isc_throw(IOError, "recvfrom failed: " << strerror(errno));
+        }
+        
+        // Pass the message size back via the size parameter
+        size = ret;
+    }
+
+
+    // Set up an IO Service queue using the specified address
+    void setDNSService(const char& address) {
+        delete dns_service_;
+        dns_service_ = NULL;
+        delete io_service_;
+        io_service_ = new IOService();
+        callback_ = new ASIOCallBack(this);
+        dns_service_ = new DNSService(*io_service_, *TEST_SERVER_PORT, address, callback_, NULL, NULL);
+    }
+
+    // Set up an IO Service queue using the "any" address, on IPv4 if
+    // 'use_ipv4' is true and on IPv6 if 'use_ipv6' is true.
+    void setDNSService(const bool use_ipv4, const bool use_ipv6) {
+        delete dns_service_;
+        dns_service_ = NULL;
+        delete io_service_;
+        io_service_ = new IOService();
+        callback_ = new ASIOCallBack(this);
+        dns_service_ = new DNSService(*io_service_, *TEST_SERVER_PORT, use_ipv4, use_ipv6, callback_,
+                                      NULL, NULL);
+    }
+
+    // Set up empty DNS Service
+    // Set up an IO Service queue without any addresses
+    void setDNSService() {
+        delete dns_service_;
+        dns_service_ = NULL;
+        delete io_service_;
+        io_service_ = new IOService();
+        callback_ = new ASIOCallBack(this);
+        dns_service_ = new DNSService(*io_service_, callback_, NULL, NULL);
+    }
+
+    // Run a simple server test, on either IPv4 or IPv6, and over either
+    // UDP or TCP.  Calls the sendUDP() or sendTCP() methods, which will
+    // start the IO Service queue.  The UDPServer or TCPServer that was
+    // created by setIOService() will receive the test packet and issue a
+    // callback, which enables us to check that the data it received
+    // matches what we sent.
+    void doTest(const int family, const int protocol) {
+        if (protocol == IPPROTO_UDP) {
+            sendUDP(family);
+        } else {
+            sendTCP(family);
+        }
+
+        // There doesn't seem to be an effective test for the validity of
+        // 'native'.
+        // One thing we are sure is it must be different from our local socket.
+        EXPECT_NE(sock_, callback_native_);
+        EXPECT_EQ(protocol, callback_protocol_);
+        EXPECT_EQ(family == AF_INET6 ? TEST_IPV6_ADDR : TEST_IPV4_ADDR,
+                  callback_address_);
+
+        const uint8_t* expected_data =
+            protocol == IPPROTO_UDP ? test_data : test_data + 2;
+        const size_t expected_datasize =
+            protocol == IPPROTO_UDP ? sizeof(test_data) :
+            sizeof(test_data) - 2;
+        EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, &callback_data_[0],
+                            callback_data_.size(),
+                            expected_data, expected_datasize);
+    }
+
+protected:
+    // This is a nonfunctional mockup of a DNSServer object.  Its purpose
+    // is to resume after a recursive query or other asynchronous call
+    // has completed.
+    class MockServer : public DNSServer {
+    public:
+        explicit MockServer(IOService& io_service,
+                            SimpleCallback* checkin = NULL,
+                            DNSLookup* lookup = NULL,
+                            DNSAnswer* answer = NULL) :
+            io_(io_service),
+            done_(false),
+            message_(new Message(Message::PARSE)),
+            answer_message_(new Message(Message::RENDER)),
+            respbuf_(new OutputBuffer(0)),
+            checkin_(checkin), lookup_(lookup), answer_(answer)
+        {}
+
+        void operator()(asio::error_code ec = asio::error_code(),
+                        size_t length = 0)
+        {}
+
+        void resume(const bool) {
+          // should never be called in our tests
+        }
+
+        DNSServer* clone() {
+            MockServer* s = new MockServer(*this);
+            return (s);
+        }
+
+        inline void asyncLookup() {
+            if (lookup_) {
+                (*lookup_)(*io_message_, message_, answer_message_,
+                           respbuf_, this);
+            }
+        }
+
+    protected:
+        IOService& io_;
+        bool done_;
+
+    private:
+        // Currently unused; these will be used for testing
+        // 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
+        const SimpleCallback* checkin_;
+        const DNSLookup* lookup_;
+        const DNSAnswer* answer_;
+    };
+
+    // This version of mock server just stops the io_service when it is resumed
+    class MockServerStop : public MockServer {
+        public:
+            explicit MockServerStop(IOService& io_service, bool* done) :
+                MockServer(io_service),
+                done_(done)
+            {}
+
+            void resume(const bool done) {
+                *done_ = done;
+                io_.stop();
+            }
+
+            DNSServer* clone() {
+                return (new MockServerStop(*this));
+            }
+        private:
+            bool* done_;
+    };
+    
+    class MockResolver : public isc::resolve::ResolverInterface {
+        void resolve(const QuestionPtr& question,
+                     const ResolverInterface::CallbackPtr& callback) {
+        }
+    };
+
+    // This version of mock server just stops the io_service when it is resumed
+    // the second time. (Used in the clientTimeout test, where resume
+    // is called initially with the error answer, and later when the
+    // lookup times out, it is called without an answer to send back)
+    class MockServerStop2 : public MockServer {
+        public:
+            explicit MockServerStop2(IOService& io_service,
+                                     bool* done1, bool* done2) :
+                MockServer(io_service),
+                done1_(done1),
+                done2_(done2),
+                stopped_once_(false)
+            {}
+
+            void resume(const bool done) {
+                if (stopped_once_) {
+                    *done2_ = done;
+                    io_.stop();
+                } else {
+                    *done1_ = done;
+                    stopped_once_ = true;
+                }
+            }
+
+            DNSServer* clone() {
+                return (new MockServerStop2(*this));
+            }
+        private:
+            bool* done1_;
+            bool* done2_;
+            bool stopped_once_;
+    };
+
+private:
+    class ASIOCallBack : public SimpleCallback {
+    public:
+        ASIOCallBack(RecursiveQueryTest* test_obj) : test_obj_(test_obj) {}
+        void operator()(const IOMessage& io_message) const {
+            test_obj_->callBack(io_message);
+        }
+    private:
+        RecursiveQueryTest* test_obj_;
+    };
+    void callBack(const IOMessage& io_message) {
+        callback_protocol_ = io_message.getSocket().getProtocol();
+        callback_native_ = io_message.getSocket().getNative();
+        callback_address_ =
+            io_message.getRemoteEndpoint().getAddress().toText();
+        callback_data_.assign(
+            static_cast<const uint8_t*>(io_message.getData()),
+            static_cast<const uint8_t*>(io_message.getData()) +
+            io_message.getDataSize());
+        io_service_->stop();
+    }
+protected:
+    // We use a pointer for io_service_, because for some tests we
+    // need to recreate a new one within one onstance of this class
+    IOService* io_service_;
+    DNSService* dns_service_;
+    isc::nsas::NameserverAddressStore* nsas_;
+    isc::cache::ResolverCache cache_;
+    ASIOCallBack* callback_;
+    int callback_protocol_;
+    int callback_native_;
+    string callback_address_;
+    vector<uint8_t> callback_data_;
+    int sock_;
+    struct addrinfo* res_;
+};
+
+RecursiveQueryTest::RecursiveQueryTest() :
+    dns_service_(NULL), callback_(NULL), callback_protocol_(0),
+    callback_native_(-1), sock_(-1), res_(NULL)
+{
+    io_service_ = new IOService();
+    setDNSService(true, true);
+    boost::shared_ptr<MockResolver>mock_resolver(new MockResolver());
+    nsas_ = new isc::nsas::NameserverAddressStore(mock_resolver);
+}
+
+TEST_F(RecursiveQueryTest, v6UDPSend) {
+    doTest(AF_INET6, IPPROTO_UDP);
+}
+
+TEST_F(RecursiveQueryTest, v6TCPSend) {
+    doTest(AF_INET6, IPPROTO_TCP);
+}
+
+TEST_F(RecursiveQueryTest, v4UDPSend) {
+    doTest(AF_INET, IPPROTO_UDP);
+}
+
+TEST_F(RecursiveQueryTest, v4TCPSend) {
+    doTest(AF_INET, IPPROTO_TCP);
+}
+
+TEST_F(RecursiveQueryTest, v6UDPSendSpecific) {
+    // Explicitly set a specific address to be bound to the socket.
+    // The subsequent test does not directly ensures the underlying socket
+    // is bound to the expected address, but the success of the tests should
+    // reasonably suggest it works as intended.
+    // Specifying an address also implicitly means the service runs in a
+    // single address-family mode.  In tests using TCP we can confirm that
+    // by trying to make a connection and seeing a failure.  In UDP, it'd be
+    // more complicated because we need to use a connected socket and catch
+    // an error on a subsequent read operation.  We could do it, but for
+    // simplicity we only tests the easier cases for now.
+
+    setDNSService(*TEST_IPV6_ADDR);
+    doTest(AF_INET6, IPPROTO_UDP);
+}
+
+TEST_F(RecursiveQueryTest, v6TCPSendSpecific) {
+    setDNSService(*TEST_IPV6_ADDR);
+    doTest(AF_INET6, IPPROTO_TCP);
+
+    EXPECT_THROW(sendTCP(AF_INET), IOError);
+}
+
+TEST_F(RecursiveQueryTest, v4UDPSendSpecific) {
+    setDNSService(*TEST_IPV4_ADDR);
+    doTest(AF_INET, IPPROTO_UDP);
+}
+
+TEST_F(RecursiveQueryTest, v4TCPSendSpecific) {
+    setDNSService(*TEST_IPV4_ADDR);
+    doTest(AF_INET, IPPROTO_TCP);
+
+    EXPECT_THROW(sendTCP(AF_INET6), IOError);
+}
+
+TEST_F(RecursiveQueryTest, v6AddServer) {
+    setDNSService();
+    dns_service_->addServer(*TEST_SERVER_PORT, TEST_IPV6_ADDR);
+    doTest(AF_INET6, IPPROTO_TCP);
+
+    EXPECT_THROW(sendTCP(AF_INET), IOError);
+}
+
+TEST_F(RecursiveQueryTest, v4AddServer) {
+    setDNSService();
+    dns_service_->addServer(*TEST_SERVER_PORT, TEST_IPV4_ADDR);
+    doTest(AF_INET, IPPROTO_TCP);
+
+    EXPECT_THROW(sendTCP(AF_INET6), IOError);
+}
+
+TEST_F(RecursiveQueryTest, clearServers) {
+    setDNSService();
+    dns_service_->clearServers();
+
+    EXPECT_THROW(sendTCP(AF_INET), IOError);
+    EXPECT_THROW(sendTCP(AF_INET6), IOError);
+}
+
+TEST_F(RecursiveQueryTest, v6TCPOnly) {
+    // Open only IPv6 TCP socket.  A subsequent attempt of establishing an
+    // IPv4/TCP connection should fail.  See above for why we only test this
+    // for TCP.
+    setDNSService(false, true);
+    EXPECT_THROW(sendTCP(AF_INET), IOError);
+}
+
+TEST_F(RecursiveQueryTest, v4TCPOnly) {
+    setDNSService(true, false);
+    EXPECT_THROW(sendTCP(AF_INET6), IOError);
+}
+
+vector<pair<string, uint16_t> >
+singleAddress(const string &address, uint16_t port) {
+    vector<pair<string, uint16_t> > result;
+    result.push_back(pair<string, uint16_t>(address, port));
+    return (result);
+}
+
+TEST_F(RecursiveQueryTest, recursiveSetupV4) {
+    setDNSService(true, false);
+    uint16_t port = boost::lexical_cast<uint16_t>(TEST_CLIENT_PORT);
+    EXPECT_NO_THROW(RecursiveQuery(*dns_service_,
+                                   *nsas_, cache_,
+                                   singleAddress(TEST_IPV4_ADDR, port),
+                                   singleAddress(TEST_IPV4_ADDR, port)));
+}
+
+TEST_F(RecursiveQueryTest, recursiveSetupV6) {
+    setDNSService(false, true);
+    uint16_t port = boost::lexical_cast<uint16_t>(TEST_CLIENT_PORT);
+    EXPECT_NO_THROW(RecursiveQuery(*dns_service_,
+                                   *nsas_, cache_,
+                                   singleAddress(TEST_IPV6_ADDR, port),
+                                   singleAddress(TEST_IPV6_ADDR,port)));
+}
+
+// XXX:
+// This is very inadequate unit testing.  It should be generalized into
+// 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(RecursiveQueryTest, forwarderSend) {
+    setDNSService(true, false);
+
+    // Note: We use the test prot plus one to ensure we aren't binding
+    // to the same port as the actual server
+    uint16_t port = boost::lexical_cast<uint16_t>(TEST_CLIENT_PORT);
+
+    MockServer server(*io_service_);
+    RecursiveQuery rq(*dns_service_,
+                      *nsas_, cache_,
+                      singleAddress(TEST_IPV4_ADDR, port),
+                      singleAddress(TEST_IPV4_ADDR, port));
+
+    Question q(Name("example.com"), RRClass::IN(), RRType::TXT());
+    OutputBufferPtr buffer(new OutputBuffer(0));
+    MessagePtr answer(new Message(Message::RENDER));
+    rq.resolve(q, answer, buffer, &server);
+
+    char data[4096];
+    size_t size = sizeof(data);
+    ASSERT_NO_THROW(recvUDP(AF_INET, data, size));
+
+    Message m(Message::PARSE);
+    InputBuffer ibuf(data, size);
+
+    // Make sure we can parse the message that was sent
+    EXPECT_NO_THROW(m.parseHeader(ibuf));
+    EXPECT_NO_THROW(m.fromWire(ibuf));
+
+    // Check that the question sent matches the one we wanted
+    QuestionPtr q2 = *m.beginQuestion();
+    EXPECT_EQ(q.getName(), q2->getName());
+    EXPECT_EQ(q.getType(), q2->getType());
+    EXPECT_EQ(q.getClass(), q2->getClass());
+}
+
+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 RecursiveQueryTest::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(RecursiveQueryTest, 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);
+    MockServerStop server(*io_service_, &done);
+
+    // Do the answer
+    const uint16_t port = boost::lexical_cast<uint16_t>(TEST_CLIENT_PORT);
+    RecursiveQuery query(*dns_service_,
+                         *nsas_, cache_,
+                         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));
+    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()).
+    int recv_options = setSocketTimeout(sock_, 10, 0);
+    int num = 0;
+    bool read_success = tryRead(sock_, recv_options, 3, &num);
+
+    // The query should 'succeed' with an error response
+    EXPECT_TRUE(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(RecursiveQueryTest, forwardClientTimeout) {
+    // Prepare the service (we do not use the common setup, we do not answer
+    setDNSService();
+
+    sock_ = createTestSocket();
+
+    // Prepare the server
+    bool done1(true);
+    MockServerStop server(*io_service_, &done1);
+
+    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
+    // four times
+    RecursiveQuery query(*dns_service_,
+                         *nsas_, cache_,
+                         singleAddress(TEST_IPV4_ADDR, port),
+                         singleAddress(TEST_IPV4_ADDR, port),
+                         200, 480, 4000, 4);
+    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 4 times
+    int num = 0;
+    bool read_success = tryRead(sock_, recv_options, 4, &num);
+
+    // The query should fail
+    EXPECT_TRUE(done1);
+    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(RecursiveQueryTest, 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_,
+                         *nsas_, cache_,
+                         singleAddress(TEST_IPV4_ADDR, port),
+                         singleAddress(TEST_IPV4_ADDR, port),
+                         200, 4000, 480, 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 and respond with an error
+    EXPECT_TRUE(done);
+    EXPECT_EQ(3, num);
+    EXPECT_FALSE(read_success);
+}
+
+// Set everything very low and see if this doesn't cause weird
+// behaviour
+TEST_F(RecursiveQueryTest, lowtimeouts) {
+    // 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_,
+                         *nsas_, cache_,
+                         singleAddress(TEST_IPV4_ADDR, port),
+                         singleAddress(TEST_IPV4_ADDR, port),
+                         1, 1, 1, 1);
+    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 and respond with an error
+    EXPECT_TRUE(done);
+    EXPECT_EQ(1, 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(RecursiveQueryTest, DISABLED_recursiveSendOk) {
+    setDNSService(true, false);
+    bool done;
+    
+    MockServerStop server(*io_service_, &done);
+    vector<pair<string, uint16_t> > empty_vector;
+    RecursiveQuery rq(*dns_service_, *nsas_, cache_, 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(RecursiveQueryTest, DISABLED_recursiveSendNXDOMAIN) {
+    setDNSService(true, false);
+    bool done;
+    
+    MockServerStop server(*io_service_, &done);
+    vector<pair<string, uint16_t> > empty_vector;
+    RecursiveQuery rq(*dns_service_, *nsas_, cache_, 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));
+}
+
+// TODO: add tests that check whether the cache is updated on succesfull
+// responses, and not updated on failures.
+
+}
diff --git a/src/lib/resolve/tests/recursive_query_unittest_2.cc b/src/lib/resolve/tests/recursive_query_unittest_2.cc
new file mode 100644
index 0000000..4799324
--- /dev/null
+++ b/src/lib/resolve/tests/recursive_query_unittest_2.cc
@@ -0,0 +1,655 @@
+// 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 <cstdlib>
+#include <iomanip>
+#include <iostream>
+#include <string>
+
+#include <gtest/gtest.h>
+#include <boost/bind.hpp>
+
+
+#include <asio.hpp>
+
+#include <dns/buffer.h>
+#include <dns/question.h>
+#include <dns/message.h>
+#include <dns/messagerenderer.h>
+#include <dns/opcode.h>
+#include <dns/name.h>
+#include <dns/rcode.h>
+#include <dns/rrtype.h>
+#include <dns/rrset.h>
+#include <dns/rrttl.h>
+#include <dns/rdata.h>
+
+#include <asiolink/asiolink_utilities.h>
+#include <asiolink/dns_service.h>
+#include <asiolink/io_address.h>
+#include <asiolink/io_endpoint.h>
+#include <asiolink/io_fetch.h>
+#include <asiolink/io_service.h>
+#include <resolve/recursive_query.h>
+#include <resolve/resolver_interface.h>
+
+using namespace asio;
+using namespace asio::ip;
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+using namespace isc::resolve;
+using namespace std;
+
+/// RecursiveQuery Test - 2
+///
+/// The second part of the RecursiveQuery unit tests, this attempts to get the
+/// RecursiveQuery object to follow a set of referrals for "www.example.org" to
+/// and to invoke TCP fallback on one of the queries.  In particular, we expect
+/// that the test will do the following in an attempt to resolve
+/// www.example.org:
+///
+/// - Send question over UDP to "root" - get referral to "org".
+/// - Send question over UDP to "org" - get referral to "example.org" with TC bit set.
+/// - Send question over TCP to "org" - get referral to "example.org".
+/// - Send question over UDP to "example.org" - get response for www.example.org.
+///
+/// (The order of queries is set in this way in order to also test that after a
+/// failover to TCP, queries revert to UDP).
+///
+/// By using the "test_server_" element of RecursiveQuery, all queries are
+/// directed to one or other of the "servers" in the RecursiveQueryTest2 class,
+/// regardless of the glue returned in referrals.
+
+namespace asiolink {
+
+const std::string TEST_ADDRESS = "127.0.0.1";   ///< Servers are on this address
+const uint16_t TEST_PORT = 5301;                ///< ... and this port
+const size_t BUFFER_SIZE = 1024;                ///< For all buffers
+const char* WWW_EXAMPLE_ORG = "192.0.2.254";    ///< Address of www.example.org
+
+// As the test is fairly long and complex, debugging "print" statements have
+// been left in although they are disabled.  Set the following to "true" to
+// enable them.
+const bool DEBUG_PRINT = false;
+
+class MockResolver : public isc::resolve::ResolverInterface {
+    void resolve(const QuestionPtr& question,
+                 const ResolverInterface::CallbackPtr& callback) {
+    }
+};
+
+
+
+/// \brief Test fixture for the RecursiveQuery Test
+class RecursiveQueryTest2 : public virtual ::testing::Test
+{
+public:
+
+    /// \brief Status of query
+    ///
+    /// Set before the query and then by each "server" when responding.
+    enum QueryStatus {
+        NONE = 0,                   ///< Default
+        UDP_ROOT = 1,               ///< Query root server over UDP
+        UDP_ORG = 2,                ///< Query ORG server over UDP
+        TCP_ORG = 3,                ///< Query ORG server over TCP
+        UDP_EXAMPLE_ORG = 4,        ///< Query EXAMPLE.ORG server over UDP
+        COMPLETE = 5                ///< Query is complete
+    };
+
+    // Common stuff
+    bool            debug_;                     ///< Set true for debug print
+    IOService       service_;                   ///< Service to run everything
+    DNSService      dns_service_;               ///< Resolver is part of "server"
+    QuestionPtr     question_;                  ///< What to ask
+    QueryStatus     last_;                      ///< What was the last state
+    QueryStatus     expected_;                  ///< Expected next state
+    OutputBufferPtr question_buffer_;           ///< Question we expect to receive
+    isc::nsas::NameserverAddressStore* nsas_;
+    isc::cache::ResolverCache cache_;
+
+    // Data for TCP Server
+    size_t          tcp_cumulative_;            ///< Cumulative TCP data received
+    tcp::endpoint   tcp_endpoint_;              ///< Endpoint for TCP receives
+    size_t          tcp_length_;                ///< Expected length value
+    uint8_t         tcp_receive_buffer_[BUFFER_SIZE];   ///< Receive buffer for TCP I/O
+    OutputBufferPtr tcp_send_buffer_;           ///< Send buffer for TCP I/O
+    tcp::socket     tcp_socket_;                ///< Socket used by TCP server
+
+    /// Data for UDP
+    udp::endpoint   udp_remote_;                ///< Endpoint for UDP receives
+    size_t          udp_length_;                ///< Expected length value
+    uint8_t         udp_receive_buffer_[BUFFER_SIZE];   ///< Receive buffer for UDP I/O
+    OutputBufferPtr udp_send_buffer_;           ///< Send buffer for UDP I/O
+    udp::socket     udp_socket_;                ///< Socket used by UDP server
+
+    /// \brief Constructor
+    RecursiveQueryTest2() :
+        debug_(DEBUG_PRINT),
+        service_(),
+        dns_service_(service_, NULL, NULL, NULL),
+        question_(new Question(Name("www.example.org"), RRClass::IN(), RRType::A())),
+        last_(NONE),
+        expected_(NONE),
+        question_buffer_(new OutputBuffer(BUFFER_SIZE)),
+        tcp_cumulative_(0),
+        tcp_endpoint_(asio::ip::address::from_string(TEST_ADDRESS), TEST_PORT),
+        tcp_length_(0),
+        tcp_receive_buffer_(),
+        tcp_send_buffer_(new OutputBuffer(BUFFER_SIZE)),
+        tcp_socket_(service_.get_io_service()),
+        udp_remote_(),
+        udp_length_(0),
+        udp_receive_buffer_(),
+        udp_send_buffer_(new OutputBuffer(BUFFER_SIZE)),
+        udp_socket_(service_.get_io_service(), udp::v4())
+    {
+        boost::shared_ptr<MockResolver>mock_resolver(new MockResolver());
+        nsas_ = new isc::nsas::NameserverAddressStore(mock_resolver);
+    }
+
+    /// \brief Set Common Message Bits
+    ///
+    /// Sets up the common bits of a response message returned by the handlers.
+    ///
+    /// \param msg Message buffer in RENDER mode.
+    /// \param qid QIT to set the message to
+    void setCommonMessage(isc::dns::Message& msg, uint16_t qid = 0) {
+        msg.setQid(qid);
+        msg.setHeaderFlag(Message::HEADERFLAG_QR);
+        msg.setOpcode(Opcode::QUERY());
+        msg.setHeaderFlag(Message::HEADERFLAG_AA);
+        msg.setRcode(Rcode::NOERROR());
+        msg.addQuestion(*question_);
+    }
+
+    /// \brief Set Referral to "org"
+    ///
+    /// Sets up the passed-in message (expected to be in "RENDER" mode to
+    /// indicate a referral to fictitious .org nameservers.
+    ///
+    /// \param msg Message to update with referral information.
+    void setReferralOrg(isc::dns::Message& msg) {
+        if (debug_) {
+            cout << "setReferralOrg(): creating referral to .org nameservers" << endl;
+        }
+
+        // Do a referral to org.  We'll define all NS records as "in-zone"
+        // nameservers (and supply glue) to avoid the possibility of the
+        // resolver starting another recursive query to resolve the address of
+        // a nameserver.
+        RRsetPtr org_ns(new RRset(Name("org."), RRClass::IN(), RRType::NS(), RRTTL(300)));
+        org_ns->addRdata(createRdata(RRType::NS(), RRClass::IN(), "ns1.org."));
+        org_ns->addRdata(createRdata(RRType::NS(), RRClass::IN(), "ns2.org."));
+        msg.addRRset(Message::SECTION_AUTHORITY, org_ns);
+
+        RRsetPtr org_ns1(new RRset(Name("ns1.org."), RRClass::IN(), RRType::A(), RRTTL(300)));
+        org_ns1->addRdata(createRdata(RRType::A(), RRClass::IN(), "192.0.2.1"));
+        msg.addRRset(Message::SECTION_ADDITIONAL, org_ns1);
+
+        RRsetPtr org_ns2(new RRset(Name("ns2.org."), RRClass::IN(), RRType::A(), RRTTL(300)));
+        org_ns2->addRdata(createRdata(RRType::A(), RRClass::IN(), "192.0.2.2"));
+        msg.addRRset(Message::SECTION_ADDITIONAL, org_ns2);
+    }
+
+    /// \brief Set Referral to "example.org"
+    ///
+    /// Sets up the passed-in message (expected to be in "RENDER" mode to
+    /// indicate a referral to fictitious example.org nameservers.
+    ///
+    /// \param msg Message to update with referral information.
+    void setReferralExampleOrg(isc::dns::Message& msg) {
+        if (debug_) {
+            cout << "setReferralExampleOrg(): creating referral to example.org nameservers" << endl;
+        }
+
+        // Do a referral to example.org.  As before, we'll define all NS
+        // records as "in-zone" nameservers (and supply glue) to avoid the
+        // possibility of the resolver starting another recursive query to look
+        // up the address of the nameserver.
+        RRsetPtr example_org_ns(new RRset(Name("example.org."), RRClass::IN(), RRType::NS(), RRTTL(300)));
+        example_org_ns->addRdata(createRdata(RRType::NS(), RRClass::IN(), "ns1.example.org."));
+        example_org_ns->addRdata(createRdata(RRType::NS(), RRClass::IN(), "ns2.example.org."));
+        msg.addRRset(Message::SECTION_AUTHORITY, example_org_ns);
+
+        RRsetPtr example_org_ns1(new RRset(Name("ns1.example.org."), RRClass::IN(), RRType::A(), RRTTL(300)));
+        example_org_ns1->addRdata(createRdata(RRType::A(), RRClass::IN(), "192.0.2.11"));
+        msg.addRRset(Message::SECTION_ADDITIONAL, example_org_ns1);
+
+        RRsetPtr example_org_ns2(new RRset(Name("ns2.example.org."), RRClass::IN(), RRType::A(), RRTTL(300)));
+        example_org_ns2->addRdata(createRdata(RRType::A(), RRClass::IN(), "192.0.2.21"));
+        msg.addRRset(Message::SECTION_ADDITIONAL, example_org_ns2);
+    }
+
+    /// \brief Set Answer to "www.example.org"
+    ///
+    /// Sets up the passed-in message (expected to be in "RENDER" mode) to
+    /// indicate an authoritative answer to www.example.org.
+    ///
+    /// \param msg Message to update with referral information.
+    void setAnswerWwwExampleOrg(isc::dns::Message& msg) {
+        if (debug_) {
+            cout << "setAnswerWwwExampleOrg(): creating answer for www.example.org" << endl;
+        }
+
+        // Give a response for www.example.org.
+        RRsetPtr www_example_org_a(new RRset(Name("www.example.org."), RRClass::IN(), RRType::A(), RRTTL(300)));
+        www_example_org_a->addRdata(createRdata(RRType::A(), RRClass::IN(), WWW_EXAMPLE_ORG));
+        msg.addRRset(Message::SECTION_ANSWER, www_example_org_a);
+
+        // ... and add the Authority and Additional sections. (These are the
+        // same as in the referral to example.org from the .org nameserver.)
+        setReferralExampleOrg(msg);
+    }
+
+    /// \brief UDP Receive Handler
+    ///
+    /// This is invoked when a message is received over UDP from the
+    /// RecursiveQuery object under test.  It formats an answer and sends it
+    /// asynchronously, with the UdpSendHandler method being specified as the
+    /// completion handler.
+    ///
+    /// \param ec ASIO error code, completion code of asynchronous I/O issued
+    ///        by the "server" to receive data.
+    /// \param length Amount of data received.
+    void udpReceiveHandler(error_code ec = error_code(), size_t length = 0) {
+        if (debug_) {
+            cout << "udpReceiveHandler(): error = " << ec.value() <<
+                    ", length = " << length << ", last state = " << last_ <<
+                    ", expected state = " << expected_ << endl;
+        }
+
+        // Expected state should be one greater than the last state.
+        EXPECT_EQ(static_cast<int>(expected_), static_cast<int>(last_) + 1);
+        last_ = expected_;
+
+        // The QID in the incoming data is random so set it to 0 for the
+        // data comparison check. (It is set to 0 in the buffer containing
+        // the expected data.)
+        uint16_t qid = readUint16(udp_receive_buffer_);
+        udp_receive_buffer_[0] = udp_receive_buffer_[1] = 0;
+
+        // Check that question we received is what was expected.
+        checkReceivedPacket(udp_receive_buffer_, length);
+
+        // The message returned depends on what state we are in.  Set up
+        // common stuff first: bits not mentioned are set to 0.
+        Message msg(Message::RENDER);
+        setCommonMessage(msg, qid);
+
+        // Set up state-dependent bits:
+        switch (expected_) {
+        case UDP_ROOT:
+            // Return a referral to org.  We then expect to query the "org"
+            // nameservers over UDP next.
+            setReferralOrg(msg);
+            expected_ = UDP_ORG;
+            break;
+
+         case UDP_ORG:
+            // Return a referral to example.org.  We explicitly set the TC bit to
+            // force a repeat query to the .org nameservers over TCP.
+            setReferralExampleOrg(msg);
+            if (debug_) {
+                cout << "udpReceiveHandler(): setting TC bit" << endl;
+            }
+            msg.setHeaderFlag(Message::HEADERFLAG_TC);
+            expected_ = TCP_ORG;
+            break;
+
+         case UDP_EXAMPLE_ORG:
+            // Return the answer to the question.
+            setAnswerWwwExampleOrg(msg);
+            expected_ = COMPLETE;
+            break;
+
+         default:
+            FAIL() << "UdpReceiveHandler called with unknown state";
+        }
+
+        // Convert to wire format
+        udp_send_buffer_->clear();
+        MessageRenderer renderer(*udp_send_buffer_);
+        msg.toWire(renderer);
+
+        // Return a message back to the IOFetch object (after setting the
+        // expected length of data for the check in the send handler).
+        udp_length_ = udp_send_buffer_->getLength();
+        udp_socket_.async_send_to(asio::buffer(udp_send_buffer_->getData(),
+                                               udp_send_buffer_->getLength()),
+                                  udp_remote_,
+                                  boost::bind(&RecursiveQueryTest2::udpSendHandler,
+                                              this, _1, _2));
+    }
+
+    /// \brief UDP Send Handler
+    ///
+    /// Called when a send operation of the UDP server (i.e. a response
+    /// being sent to the RecursiveQuery) has completed, this re-issues
+    /// a read call.
+    ///
+    /// \param ec Completion error code of the send.
+    /// \param length Actual number of bytes sent.
+    void udpSendHandler(error_code ec = error_code(), size_t length = 0) {
+        if (debug_) {
+            cout << "udpSendHandler(): error = " << ec.value() <<
+                    ", length = " << length << endl;
+        }
+
+        // Check send was OK
+        EXPECT_EQ(0, ec.value());
+        EXPECT_EQ(udp_length_, length);
+
+        // Reissue the receive call to await the next message.
+        udp_socket_.async_receive_from(
+            asio::buffer(udp_receive_buffer_, sizeof(udp_receive_buffer_)),
+            udp_remote_,
+            boost::bind(&RecursiveQueryTest2::udpReceiveHandler, this, _1, _2));
+    }
+
+    /// \brief Completion Handler for Accepting TCP Data
+    ///
+    /// Called when the remote system connects to the "TCP server".  It issues
+    /// an asynchronous read on the socket to read data.
+    ///
+    /// \param socket Socket on which data will be received
+    /// \param ec Boost error code, value should be zero.
+    void tcpAcceptHandler(error_code ec = error_code(), size_t length = 0) {
+        if (debug_) {
+            cout << "tcpAcceptHandler(): error = " << ec.value() <<
+                    ", length = " << length << endl;
+        }
+
+        // Expect that the accept completed without a problem.
+        EXPECT_EQ(0, ec.value());
+
+        // Initiate a read on the socket, indicating that nothing has yet been
+        // received.
+        tcp_cumulative_ = 0;
+        tcp_socket_.async_receive(
+            asio::buffer(tcp_receive_buffer_, sizeof(tcp_receive_buffer_)),
+            boost::bind(&RecursiveQueryTest2::tcpReceiveHandler, this, _1, _2));
+    }
+
+    /// \brief Completion Handler for Receiving TCP Data
+    ///
+    /// Reads data from the RecursiveQuery object and loops, reissuing reads,
+    /// until all the message has been read.  It then returns an appropriate
+    /// response.
+    ///
+    /// \param socket Socket to use to send the answer
+    /// \param ec ASIO error code, completion code of asynchronous I/O issued
+    ///        by the "server" to receive data.
+    /// \param length Amount of data received.
+    void tcpReceiveHandler(error_code ec = error_code(), size_t length = 0) {
+        if (debug_) {
+            cout << "tcpReceiveHandler(): error = " << ec.value() <<
+                    ", length = " << length <<
+                    ", cumulative = " << tcp_cumulative_ << endl;
+        }
+
+        // Expect that the receive completed without a problem.
+        EXPECT_EQ(0, ec.value());
+
+        // Have we received all the data?  We know this by checking if the two-
+        // byte length count in the message is equal to the data received.
+        tcp_cumulative_ += length;
+        bool complete = false;
+        if (tcp_cumulative_ > 2) {
+            uint16_t dns_length = readUint16(tcp_receive_buffer_);
+            complete = ((dns_length + 2) == tcp_cumulative_);
+        }
+
+        if (!complete) {
+            if (debug_) {
+                cout << "tcpReceiveHandler(): read not complete, " <<
+                        "issuing another read" << endl;
+            }
+
+            // Not complete yet, issue another read.
+            tcp_socket_.async_receive(
+                asio::buffer(tcp_receive_buffer_ + tcp_cumulative_,
+                             sizeof(tcp_receive_buffer_) - tcp_cumulative_),
+                boost::bind(&RecursiveQueryTest2::tcpReceiveHandler, this, _1, _2));
+            return;
+        }
+
+        // Have received a TCP message.  Expected state should be one greater
+        // than the last state.
+        EXPECT_EQ(static_cast<int>(expected_), static_cast<int>(last_) + 1);
+        last_ = expected_;
+
+        // Check that question we received is what was expected.  Note that we
+        // have to ignore the two-byte header in order to parse the message.
+        checkReceivedPacket(tcp_receive_buffer_ + 2, length - 2);
+
+        // Return a message back.  This is a referral to example.org, which
+        // should result in another query over UDP.  Note the setting of the
+        // QID in the returned message with what was in the received message.
+        Message msg(Message::RENDER);
+        setCommonMessage(msg, readUint16(tcp_receive_buffer_));
+        setReferralExampleOrg(msg);
+
+        // Convert to wire format
+        // Use a temporary buffer for the dns wire data (we copy it
+        // to the 'real' buffer below)
+        OutputBuffer msg_buf(BUFFER_SIZE);
+        MessageRenderer renderer(msg_buf);
+        msg.toWire(renderer);
+
+        // Expected next state (when checked) is the UDP query to example.org.
+        // Also, take this opportunity to clear the accumulated read count in
+        // readiness for the next read. (If any - at present, there is only
+        // one read in the test, although extensions to this test suite could
+        // change that.)
+        expected_ = UDP_EXAMPLE_ORG;
+        tcp_cumulative_ = 0;
+
+        // Unless we go through a callback loop we cannot simply use
+        // async_send() multiple times, so we cannot send the size first
+        // followed by the actual data. We copy them to a new buffer
+        // first
+        tcp_send_buffer_->clear();
+        tcp_send_buffer_->writeUint16(msg_buf.getLength());
+        tcp_send_buffer_->writeData(msg_buf.getData(), msg_buf.getLength());
+        tcp_socket_.async_send(asio::buffer(tcp_send_buffer_->getData(),
+                                            tcp_send_buffer_->getLength()),
+                               boost::bind(&RecursiveQueryTest2::tcpSendHandler, this,
+                                           tcp_send_buffer_->getLength(), _1, _2));
+    }
+
+    /// \brief Completion Handler for Sending TCP data
+    ///
+    /// Called when the asynchronous send of data back to the RecursiveQuery
+    /// by the TCP "server" in this class has completed.  (This send has to
+    /// be asynchronous because control needs to return to the caller in order
+    /// for the IOService "run()" method to be called to run the handlers.)
+    ///
+    /// \param expected_length Number of bytes that were expected to have been sent.
+    /// \param ec Boost error code, value should be zero.
+    /// \param length Number of bytes sent.
+    void tcpSendHandler(size_t expected_length = 0, error_code ec = error_code(),
+                        size_t length = 0)
+    {
+        if (debug_) {
+            cout << "tcpSendHandler(): error = " << ec.value() <<
+                    ", length = " << length <<
+                    ", (expected length = " << expected_length << ")" << endl;
+        }
+        EXPECT_EQ(0, ec.value());       // Expect no error
+        EXPECT_EQ(expected_length, length);    // And that amount sent is as expected
+    }
+
+    /// \brief Check Received Packet
+    ///
+    /// Checks the packet received from the RecursiveQuery object to ensure
+    /// that the question is what is expected.
+    ///
+    /// \param data Start of data.  This is the start of the received buffer in
+    ///        the case of UDP data, and an offset into the buffer past the
+    ///        count field for TCP data.
+    /// \param length Length of data.
+    void checkReceivedPacket(uint8_t* data, size_t length) {
+
+        // Decode the received buffer.
+        InputBuffer buffer(data, length);
+        Message message(Message::PARSE);
+        message.fromWire(buffer);
+
+        // Check the packet.
+        EXPECT_FALSE(message.getHeaderFlag(Message::HEADERFLAG_QR));
+
+        Question question = **(message.beginQuestion());
+        EXPECT_TRUE(question == *question_);
+    }
+};
+
+/// \brief Resolver Callback Object
+///
+/// Holds the success and failure callback methods for the resolver
+class ResolverCallback : public isc::resolve::ResolverInterface::Callback {
+public:
+    /// \brief Constructor
+    ResolverCallback(IOService& service) :
+        service_(service), run_(false), status_(false), debug_(DEBUG_PRINT)
+    {}
+
+    /// \brief Destructor
+    virtual ~ResolverCallback()
+    {}
+
+    /// \brief Resolver Callback Success
+    ///
+    /// Called if the resolver detects that the call has succeeded.
+    ///
+    /// \param response Answer to the question.
+    virtual void success(const isc::dns::MessagePtr response) {
+        if (debug_) {
+            cout << "ResolverCallback::success(): answer received" << endl;
+        }
+
+        // There should be one RR each  in the question and answer sections, and
+        // two RRs in each of the the authority and additional sections.
+        EXPECT_EQ(1, response->getRRCount(Message::SECTION_QUESTION));
+        EXPECT_EQ(1, response->getRRCount(Message::SECTION_ANSWER));
+        EXPECT_EQ(2, response->getRRCount(Message::SECTION_AUTHORITY));
+        EXPECT_EQ(2, response->getRRCount(Message::SECTION_ADDITIONAL));
+
+        // Check the answer - that the RRset is there...
+        EXPECT_TRUE(response->hasRRset(Message::SECTION_ANSWER,
+                                       RRsetPtr(new RRset(Name("www.example.org."),
+                                                RRClass::IN(),
+                                                RRType::A(),
+                                                RRTTL(300)))));
+        const RRsetIterator rrset_i = response->beginSection(Message::SECTION_ANSWER);
+
+        // ... get iterator into the Rdata of this RRset and point to first
+        // element...
+        const RdataIteratorPtr rdata_i = (*rrset_i)->getRdataIterator();
+        rdata_i->first();
+
+        // ... and check it is what we expect.
+        EXPECT_EQ(string(WWW_EXAMPLE_ORG), rdata_i->getCurrent().toText());
+
+        // Flag completion
+        run_ = true;
+        status_ = true;
+
+        service_.stop();    // Cause run() to exit.
+    }
+
+    /// \brief Resolver Failure Completion
+    ///
+    /// Called if the resolver detects that the resolution has failed.
+    virtual void failure() {
+        if (debug_) {
+            cout << "ResolverCallback::success(): resolution failure" << endl;
+        }
+        FAIL() << "Resolver reported completion failure";
+
+        // Flag completion
+        run_ = true;
+        status_ = false;
+
+        service_.stop();    // Cause run() to exit.
+    }
+
+    /// \brief Return status of "run" flag
+    bool getRun() const {
+        return (run_);
+    }
+
+    /// \brief Return "status" flag
+    bool getStatus() const {
+        return (status_);
+    }
+
+private:
+    IOService&      service_;       ///< Service handling the run queue
+    bool            run_;           ///< Set true when completion handler run
+    bool            status_;        ///< Set true for success, false on error
+    bool            debug_;         ///< Debug flag
+};
+
+// Sets up the UDP and TCP "servers", then tries a resolution.
+
+TEST_F(RecursiveQueryTest2, Resolve) {
+
+    // Set up the UDP server and issue the first read.  The endpoint from which
+    // the query is sent is put in udp_endpoint_ when the read completes, which
+    // is referenced in the callback as the place to which the response is sent.
+    udp_socket_.set_option(socket_base::reuse_address(true));
+    udp_socket_.bind(udp::endpoint(address::from_string(TEST_ADDRESS), TEST_PORT));
+    udp_socket_.async_receive_from(asio::buffer(udp_receive_buffer_,
+                                                sizeof(udp_receive_buffer_)),
+                                   udp_remote_,
+                                   boost::bind(&RecursiveQueryTest2::udpReceiveHandler,
+                                               this, _1, _2));
+
+    // Set up the TCP server and issue the accept.  Acceptance will cause the
+    // read to be issued.
+    tcp::acceptor acceptor(service_.get_io_service(),
+                           tcp::endpoint(tcp::v4(), TEST_PORT));
+    acceptor.async_accept(tcp_socket_,
+                          boost::bind(&RecursiveQueryTest2::tcpAcceptHandler,
+                                      this, _1, 0));
+
+    // Set up the RecursiveQuery object.
+    std::vector<std::pair<std::string, uint16_t> > upstream;         // Empty
+    std::vector<std::pair<std::string, uint16_t> > upstream_root;    // Empty
+    RecursiveQuery query(dns_service_, *nsas_, cache_,
+                         upstream, upstream_root);
+    query.setTestServer(TEST_ADDRESS, TEST_PORT);
+
+    // Set up callback for the tor eceive notification that the query has
+    // completed.
+    isc::resolve::ResolverInterface::CallbackPtr
+        resolver_callback(new ResolverCallback(service_));
+
+    // Kick off the resolution process.  We expect the first question to go to
+    // "root".
+    expected_ = UDP_ROOT;
+    query.resolve(question_, resolver_callback);
+    service_.run();
+
+    // Check what ran. (We have to cast the callback to ResolverCallback as we
+    // lost the information on the derived class when we used a
+    // ResolverInterface::CallbackPtr to store a pointer to it.)
+    ResolverCallback* rc = static_cast<ResolverCallback*>(resolver_callback.get());
+    EXPECT_TRUE(rc->getRun());
+    EXPECT_TRUE(rc->getStatus());
+}
+
+} // namespace asiolink
diff --git a/src/lib/resolve/tests/response_classifier_unittest.cc b/src/lib/resolve/tests/response_classifier_unittest.cc
index b37ded7..23c8666 100644
--- a/src/lib/resolve/tests/response_classifier_unittest.cc
+++ b/src/lib/resolve/tests/response_classifier_unittest.cc
@@ -80,6 +80,8 @@ public:
             RRType::CNAME(), RRTTL(300))),
         rrs_in_ns_(new RRset(Name("example.com"), RRClass::IN(),
             RRType::NS(), RRTTL(300))),
+        rrs_in_soa_(new RRset(Name("example.com"), RRClass::IN(),
+            RRType::SOA(), RRTTL(300))),
         rrs_in_txt_www(new RRset(Name("www.example.com"), RRClass::IN(),
             RRType::TXT(), RRTTL(300))),
         cname_target("."),
@@ -115,6 +117,9 @@ public:
         // 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"))));
+        
+        // And an imaginary SOA
+        rrs_in_soa_->addRdata(ConstRdataPtr(new SOA(Name("ns0.example.org"), Name("root.example.org"), 1, 2, 3, 4, 5)));
 
         // Set up the records for the www host
         rrs_in_a_www->addRdata(ConstRdataPtr(new A("1.2.3.4")));
@@ -146,6 +151,7 @@ public:
     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_soa_;        // example.com IN SOA
     RRsetPtr    rrs_in_txt_www;     // www.example.com IN TXT
     Name        cname_target;       // Used in response classifier to
                                     // store the target of a possible
@@ -349,6 +355,17 @@ TEST_F(ResponseClassifierTest, EmptyAnswerReferral) {
 
 }
 
+// Test if we get a NOERROR answer that contains neither an actual
+// answer nor a delegation
+TEST_F(ResponseClassifierTest, NoErrorNoData) {
+
+    msg_a.addRRset(Message::SECTION_AUTHORITY, rrs_in_soa_);
+    EXPECT_EQ(ResponseClassifier::NXRRSET,
+        ResponseClassifier::classify(qu_in_a_www, msg_a, cname_target,
+                                     cname_count));
+
+}
+
 // 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
diff --git a/src/lib/testutils/Makefile.am b/src/lib/testutils/Makefile.am
index e5cb46b..ae5c6da 100644
--- a/src/lib/testutils/Makefile.am
+++ b/src/lib/testutils/Makefile.am
@@ -11,6 +11,7 @@ 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)
+libtestutils_la_LIBADD = $(top_builddir)/src/lib/asiolink/libasiolink.la
 endif
 
 EXTRA_DIST = portconfig.h
diff --git a/tests/Makefile.am b/tests/Makefile.am
new file mode 100644
index 0000000..d4008c0
--- /dev/null
+++ b/tests/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = system
diff --git a/tests/system/Makefile.am b/tests/system/Makefile.am
new file mode 100644
index 0000000..663258b
--- /dev/null
+++ b/tests/system/Makefile.am
@@ -0,0 +1,16 @@
+systest:
+	sh $(srcdir)/runall.sh
+
+distclean-local:
+	sh $(srcdir)/cleanall.sh
+
+# Most of the files under this directory (including test subdirectories)
+# must be listed in EXTRA_DIST.
+EXTRA_DIST = README cleanall.sh ifconfig.sh start.pl stop.pl run.sh runall.sh
+EXTRA_DIST += common/default_user.csv
+EXTRA_DIST += glue/auth.good glue/example.good glue/noglue.good glue/test.good
+EXTRA_DIST += glue/tests.sh glue/clean.sh
+EXTRA_DIST += glue/nsx1/com.db glue/nsx1/net.db glue/nsx1/root-servers.nil.db
+EXTRA_DIST += glue/nsx1/root.db
+EXTRA_DIST += bindctl/tests.sh bindctl/clean.sh bindctl/setup.sh
+EXTRA_DIST += bindctl/nsx1/root.db bindctl/nsx1/example-normalized.db
diff --git a/tests/system/README b/tests/system/README
new file mode 100644
index 0000000..a43d49e
--- /dev/null
+++ b/tests/system/README
@@ -0,0 +1,63 @@
+Copyright (C) 2004, 2010, 2011  Internet Systems Consortium, Inc. ("ISC")
+Copyright (C) 2000, 2001  Internet Software Consortium.
+See COPYRIGHT in the source root or http://isc.org/copyright.html for terms.
+
+This is a simple test environment for running BIND 10 system tests
+involving multiple name servers.  It was originally developed for BIND
+9, and has been ported to test BIND 10 implementations.  Ideally we
+should share the same framework for both versions, so some part of
+the original setup are kept, even though they are BIND 9 specific and
+not currently used.
+
+Also, these tests generally rely on BIND 9 programs, most commonly its
+dig, and will sometimes be its name server (named).  So, the test
+environment assumes that there's a source tree of BIND 9 where its
+programs are built, and that an environment variable "BIND9_TOP" is
+set to point to the top directory of the source tree.
+
+There are multiple test suites, each in a separate subdirectory and
+involving a different DNS setup.  They are:
+
+  bindctl/      Some basic management operations using the bindctl tool
+  glue/		Glue handling tests
+(the following tests are planned to be added soon)
+  dnssec/	DNSSEC tests
+  masterfile/	Master file parser
+  xfer/		Zone transfer tests
+
+Typically each test suite sets up 2-5 instances of BIND 10 (or BIND 9
+named) and then performs one or more tests against them.  Within the
+test suite subdirectory, each instance has a separate subdirectory
+containing its configuration data.  By convention, these
+subdirectories are named "nsx1", "nsx2", etc for BIND 10 ("x" means
+BIND 10), and "ns1", "ns2", etc. for BIND 9.
+
+The tests are completely self-contained and do not require access to
+the real DNS.  Generally, one of the test servers (ns[x]1) is set up
+as a root name server and is listed in the hints file of the others.
+
+To enable all servers to run on the same machine, they bind to
+separate virtual IP address on the loopback interface.  ns[x]1 runs on
+10.53.0.1, ns[x]2 on 10.53.0.2, etc.  Before running any tests, you
+must set up these addresses by running "ifconfig.sh up" as root.
+
+Mac OS X:
+If you wish to make the interfaces survive across reboots
+copy org.isc.bind.system and org.isc.bind.system to
+/Library/LaunchDaemons then run
+"launchctl load /Library/LaunchDaemons/org.isc.bind.system.plist" as
+root.
+
+The servers use port 53210 instead of the usual port 53, so they can be
+run without root privileges once the interfaces have been set up.
+
+The tests can be run individually like this:
+
+  sh run.sh xfer
+  sh run.sh glue
+  etc.
+
+To run all the tests, just type "make systest" either on this directory
+or on the top source directory.  Note: currently these tests cannot be
+run when built under a separate build directory.  Everything must be
+run within the original source tree.
diff --git a/tests/system/bindctl/clean.sh b/tests/system/bindctl/clean.sh
new file mode 100755
index 0000000..f691512
--- /dev/null
+++ b/tests/system/bindctl/clean.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+#
+# Copyright (C) 2004, 2007  Internet Systems Consortium, Inc. ("ISC")
+# Copyright (C) 2000, 2001  Internet Software Consortium.
+#
+# 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.
+
+rm -f */b10-config.db
+rm -f dig.out.* bindctl.out.*
+rm -f */msgq_socket */zone.sqlite3
diff --git a/tests/system/bindctl/nsx1/b10-config.db.template.in b/tests/system/bindctl/nsx1/b10-config.db.template.in
new file mode 100644
index 0000000..162329a
--- /dev/null
+++ b/tests/system/bindctl/nsx1/b10-config.db.template.in
@@ -0,0 +1,10 @@
+{"version": 2,
+ "Auth": {
+   "listen_on": [{"address": "10.53.0.1", "port": 53210}],
+   "database_file": "@abs_builddir@/zone.sqlite3",
+   "statistics-interval": 1
+ },
+ "Xfrout": {
+   "log_file": "@abs_builddir@/Xfrout.log"
+ }
+}
diff --git a/tests/system/bindctl/nsx1/example-normalized.db b/tests/system/bindctl/nsx1/example-normalized.db
new file mode 100644
index 0000000..7129522
--- /dev/null
+++ b/tests/system/bindctl/nsx1/example-normalized.db
@@ -0,0 +1,3 @@
+com.					      300 IN SOA	postmaster.example. ns.example.com. 2000042100 600 600 1200 600
+com.					      300 IN NS		ns.example.com.
+ns.example.com.				      300 IN A		192.0.2.2
diff --git a/tests/system/bindctl/nsx1/root.db b/tests/system/bindctl/nsx1/root.db
new file mode 100644
index 0000000..31293de
--- /dev/null
+++ b/tests/system/bindctl/nsx1/root.db
@@ -0,0 +1,25 @@
+; Copyright (C) 2004, 2007  Internet Systems Consortium, Inc. ("ISC")
+; Copyright (C) 2000, 2001  Internet Software Consortium.
+;
+; 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.
+
+$TTL 300
+. 			IN SOA	postmaster.example. a.root.servers.nil. (
+				2000042100   	; serial
+				600         	; refresh
+				600         	; retry
+				1200    	; expire
+				600       	; minimum
+				)
+.			NS	ns.example.com.
+ns.example.com.		A	192.0.2.1
diff --git a/tests/system/bindctl/setup.sh b/tests/system/bindctl/setup.sh
new file mode 100755
index 0000000..55afcc1
--- /dev/null
+++ b/tests/system/bindctl/setup.sh
@@ -0,0 +1,26 @@
+#!/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.
+
+SYSTEMTESTTOP=..
+. $SYSTEMTESTTOP/conf.sh
+
+SUBTEST_TOP=${TEST_TOP}/bindctl
+
+cp ${SUBTEST_TOP}/nsx1/b10-config.db.template ${SUBTEST_TOP}/nsx1/b10-config.db
+
+rm -f ${SUBTEST_TOP}/*/zone.sqlite3
+${B10_LOADZONE} -o . -d ${SUBTEST_TOP}/nsx1/zone.sqlite3 \
+	${SUBTEST_TOP}//nsx1/root.db
diff --git a/tests/system/bindctl/tests.sh b/tests/system/bindctl/tests.sh
new file mode 100755
index 0000000..354f349
--- /dev/null
+++ b/tests/system/bindctl/tests.sh
@@ -0,0 +1,106 @@
+#!/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.
+
+SYSTEMTESTTOP=..
+. $SYSTEMTESTTOP/conf.sh
+
+#
+# Do bindctl tests.
+#
+
+status=0
+n=0
+
+echo "I:Checking b10-auth is working by default ($n)"
+$DIG +norec @10.53.0.1 -p 53210 ns.example.com. A >dig.out.$n || status=1
+# perform a simple check on the output (digcomp would be too much for this)
+grep 192.0.2.1 dig.out.$n > /dev/null || status=1
+if [ $status != 0 ]; then echo "I:failed"; fi
+n=`expr $n + 1`
+
+echo "I:Checking BIND 10 statistics after a pose ($n)"
+# wait for 2sec to make sure b10-stats gets the latest statistics.
+# note that we set statistics-interval to 1.
+sleep 2
+echo 'Stats show
+' | $RUN_BINDCTL \
+	--csv-file-dir=$BINDCTL_CSV_DIR > bindctl.out.$n || status=1
+# the server should have received 1 UDP and 1 TCP queries (TCP query was
+# sent from the server startup script)
+grep "\"auth.queries.tcp\": 1," bindctl.out.$n > /dev/null || status=1
+grep "\"auth.queries.udp\": 1," bindctl.out.$n > /dev/null || status=1
+if [ $status != 0 ]; then echo "I:failed"; fi
+n=`expr $n + 1`
+
+echo "I:Stopping b10-auth and checking that ($n)"
+echo 'config set Boss/start_auth false
+config commit
+quit
+' | $RUN_BINDCTL \
+	--csv-file-dir=$BINDCTL_CSV_DIR 2>&1 > /dev/null || status=1
+# dig should exit with a failure code.
+$DIG +tcp +norec @10.53.0.1 -p 53210 ns.example.com. A && status=1
+if [ $status != 0 ]; then echo "I:failed"; fi
+n=`expr $n + 1`
+
+echo "I:Restarting b10-auth and checking that ($n)"
+echo 'config set Boss/start_auth true
+config commit
+quit
+' | $RUN_BINDCTL \
+	--csv-file-dir=$BINDCTL_CSV_DIR 2>&1 > /dev/null || status=1
+$DIG +norec @10.53.0.1 -p 53210 ns.example.com. A >dig.out.$n || status=1
+grep 192.0.2.1 dig.out.$n > /dev/null || status=1
+if [ $status != 0 ]; then echo "I:failed"; fi
+n=`expr $n + 1`
+
+echo "I:Rechecking BIND 10 statistics after a pose ($n)"
+sleep 2
+echo 'Stats show
+' | $RUN_BINDCTL \
+	--csv-file-dir=$BINDCTL_CSV_DIR > bindctl.out.$n || status=1
+# The statistics counters should have been reset while stop/start.
+grep "\"auth.queries.tcp\": 0," bindctl.out.$n > /dev/null || status=1
+grep "\"auth.queries.udp\": 1," bindctl.out.$n > /dev/null || status=1
+if [ $status != 0 ]; then echo "I:failed"; fi
+n=`expr $n + 1`
+
+echo "I:Changing the data source from sqlite3 to in-memory ($n)"
+DATASRC_SPEC='[{"type": "memory", "zones": [{"origin": "com","file":'
+DATASRC_SPEC="${DATASRC_SPEC} \"${TEST_TOP}/bindctl/nsx1/example-normalized.db\"}]}]"
+echo "config set Auth/datasources ${DATASRC_SPEC}
+config commit
+quit
+" | $RUN_BINDCTL \
+	--csv-file-dir=$BINDCTL_CSV_DIR > bindctl.out.$n || status=1
+$DIG +norec @10.53.0.1 -p 53210 ns.example.com. A >dig.out.$n || status=1
+grep 192.0.2.2 dig.out.$n > /dev/null || status=1
+if [ $status != 0 ]; then echo "I:failed"; fi
+n=`expr $n + 1`
+
+echo "I:Rechecking BIND 10 statistics after changing the datasource ($n)"
+sleep 2
+echo 'Stats show
+' | $RUN_BINDCTL \
+	--csv-file-dir=$BINDCTL_CSV_DIR > bindctl.out.$n || status=1
+# The statistics counters shouldn't be reset due to hot-swapping datasource.
+grep "\"auth.queries.tcp\": 0," bindctl.out.$n > /dev/null || status=1
+grep "\"auth.queries.udp\": 2," bindctl.out.$n > /dev/null || status=1
+if [ $status != 0 ]; then echo "I:failed"; fi
+n=`expr $n + 1`
+
+echo "I:exit status: $status"
+exit $status
diff --git a/tests/system/cleanall.sh b/tests/system/cleanall.sh
new file mode 100755
index 0000000..17c3d4a
--- /dev/null
+++ b/tests/system/cleanall.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+#
+# Copyright (C) 2004, 2007  Internet Systems Consortium, Inc. ("ISC")
+# Copyright (C) 2000, 2001  Internet Software Consortium.
+#
+# 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.
+
+#
+# Clean up after system tests.
+#
+
+find . -type f \( \
+    -name 'K*' -o -name '*~' -o -name '*.core' -o -name '*.log' \
+    -o -name '*.pid' -o -name '*.keyset' -o -name named.run \
+    -o -name bind10.run -o -name lwresd.run -o -name ans.run \) -print | \
+    xargs rm -f
+
+status=0
+
+for d in `find . -type d -maxdepth 1 -mindepth 1 -print`
+do
+   test ! -f $d/clean.sh || ( cd $d && sh clean.sh )
+done
diff --git a/tests/system/common/default_user.csv b/tests/system/common/default_user.csv
new file mode 100644
index 0000000..e13e194
--- /dev/null
+++ b/tests/system/common/default_user.csv
@@ -0,0 +1 @@
+root,bind10
diff --git a/tests/system/conf.sh.in b/tests/system/conf.sh.in
new file mode 100755
index 0000000..66aa3f5
--- /dev/null
+++ b/tests/system/conf.sh.in
@@ -0,0 +1,57 @@
+#!/bin/sh
+#
+# Copyright (C) 2004-2011  Internet Systems Consortium, Inc. ("ISC")
+# Copyright (C) 2000-2003  Internet Software Consortium.
+#
+# 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.
+
+#
+# Common configuration data for system tests, to be sourced into
+# other shell scripts.
+#
+
+# Prerequisite check
+if [ @srcdir@ != @builddir@ ]; then
+	echo "Currently systest doesn't work for a separate build tree."
+	echo "Rebuild BIND 10 on the source tree and run the tests."
+	exit 1
+fi
+
+if [ -z $BIND9_TOP ]; then
+	echo "systest assumes there's a compiled tree of BIND 9 which can be"
+	echo "accessed via the BIND9_TOP environment variable."
+	echo "Please make sure this assumption is met."
+	exit 1
+fi
+
+# Find the top of the source and test trees.
+TOP=@abs_top_srcdir@
+TEST_TOP=@abs_builddir@
+
+RUN_BIND10=$TOP/src/bin/bind10/run_bind10.sh
+RUN_BINDCTL=$TOP/src/bin/bindctl/run_bindctl.sh
+BINDCTL_CSV_DIR=@abs_srcdir@/common/
+B10_LOADZONE=$TOP/src/bin/loadzone/run_loadzone.sh
+BIND9_NAMED=$BIND9_TOP/bin/named/named
+DIG=$BIND9_TOP/bin/dig/dig
+# Test tools borrowed from BIND 9's system test (without change).
+TESTSOCK=$BIND9_TOP/bin/tests/system/testsock.pl
+DIGCOMP=$BIND9_TOP/bin/tests/system/digcomp.pl
+
+SUBDIRS="bindctl glue"
+#SUBDIRS="dnssec masterfile xfer"
+
+# PERL will be an empty string if no perl interpreter was found.
+PERL=@PERL@
+
+export RUN_BIND10 BIND9_NAMED DIG SUBDIRS PERL TESTSOCK
diff --git a/tests/system/glue/auth.good b/tests/system/glue/auth.good
new file mode 100644
index 0000000..2c619f6
--- /dev/null
+++ b/tests/system/glue/auth.good
@@ -0,0 +1,15 @@
+
+; <<>> DiG 9.0 <<>> +norec @10.53.0.1 -p 5300 foo.bar.example.org. a
+;; global options:  printcmd
+;; Got answer:
+;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 41239
+;; flags: qr ad; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1
+
+;; QUESTION SECTION:
+;foo.bar.example.org.			IN	A
+
+;; AUTHORITY SECTION:
+example.org.		172800	IN	NS	b.root-servers.nil.
+
+;; ADDITIONAL SECTION:
+b.root-servers.nil.	300	IN	A	10.53.0.2
diff --git a/tests/system/glue/clean.sh b/tests/system/glue/clean.sh
new file mode 100755
index 0000000..b2c1e02
--- /dev/null
+++ b/tests/system/glue/clean.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+#
+# Copyright (C) 2004, 2007  Internet Systems Consortium, Inc. ("ISC")
+# Copyright (C) 2000, 2001  Internet Software Consortium.
+#
+# 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.
+
+#
+# Clean up after glue tests.
+#
+
+rm -f dig.out.*
+rm -f */msgq_socket */zone.sqlite3
diff --git a/tests/system/glue/example.good b/tests/system/glue/example.good
new file mode 100644
index 0000000..3b7bbb8
--- /dev/null
+++ b/tests/system/glue/example.good
@@ -0,0 +1,19 @@
+
+; <<>> DiG 9.0 <<>> +norec @10.53.0.1 -p 5300 foo.bar.example. A
+;; global options:  printcmd
+;; Got answer:
+;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 58772
+;; flags: qr ad; QUERY: 1, ANSWER: 0, AUTHORITY: 6, ADDITIONAL: 7
+
+;; QUESTION SECTION:
+;foo.bar.example.			IN	A
+
+;; AUTHORITY SECTION:
+example.			172800	IN	NS	NS1.example.COM.
+example.			172800	IN	NS	NS.example.
+
+;; ADDITIONAL SECTION:
+NS.example.		172800	IN	A	192.0.2.1
+NS.example.		172800	IN	A	192.0.2.2
+NS1.example.COM.	172800	IN	A	192.0.2.101
+NS1.example.COM.		172800	IN	AAAA	2001:db8::1
diff --git a/tests/system/glue/noglue.good b/tests/system/glue/noglue.good
new file mode 100644
index 0000000..57a2211
--- /dev/null
+++ b/tests/system/glue/noglue.good
@@ -0,0 +1,14 @@
+
+; <<>> DiG 9.0 <<>> @10.53.0.1 -p 5300 example.net a
+;; global options:  printcmd
+;; Got answer:
+;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 29409
+;; flags: qr rd ad; QUERY: 1, ANSWER: 0, AUTHORITY: 2, ADDITIONAL: 0
+
+;; QUESTION SECTION:
+;example.net.			IN	A
+
+;; AUTHORITY SECTION:
+example.net.		300	IN	NS	ns2.example.info.
+example.net.		300	IN	NS	ns1.example.info.
+
diff --git a/tests/system/glue/nsx1/b10-config.db.in b/tests/system/glue/nsx1/b10-config.db.in
new file mode 100644
index 0000000..acd040c
--- /dev/null
+++ b/tests/system/glue/nsx1/b10-config.db.in
@@ -0,0 +1,9 @@
+{"version": 2,
+ "Auth": {
+   "listen_on": [{"address": "10.53.0.1", "port": 53210}],
+   "database_file": "@abs_builddir@/zone.sqlite3"
+ },
+ "Xfrout": {
+   "log_file": "@abs_builddir@/Xfrout.log"
+ }
+}
diff --git a/tests/system/glue/nsx1/com.db b/tests/system/glue/nsx1/com.db
new file mode 100644
index 0000000..c4b94e1
--- /dev/null
+++ b/tests/system/glue/nsx1/com.db
@@ -0,0 +1,31 @@
+; Copyright (C) 2004, 2007  Internet Systems Consortium, Inc. ("ISC")
+; Copyright (C) 2000, 2001  Internet Software Consortium.
+;
+; 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.
+
+$ORIGIN com.
+$TTL 300
+@ 			IN SOA	root.example.com. a.root.servers.nil. (
+				2000042100   	; serial
+				600         	; refresh
+				600         	; retry
+				1200    	; expire
+				600       	; minimum
+				)
+@			NS	a.root-servers.nil.
+
+example.com.			NS	ns1.example.com.
+example.com.			NS	ns2.example.com.
+ns1.example.com.		172800	IN	A	192.0.2.101
+ns1.example.com.		172800	IN	AAAA	2001:db8::1
+ns2.example.com.		172800	IN	A	192.0.2.102
diff --git a/tests/system/glue/nsx1/net.db b/tests/system/glue/nsx1/net.db
new file mode 100644
index 0000000..8b66521
--- /dev/null
+++ b/tests/system/glue/nsx1/net.db
@@ -0,0 +1,32 @@
+; Copyright (C) 2004, 2007  Internet Systems Consortium, Inc. ("ISC")
+; Copyright (C) 2000, 2001  Internet Software Consortium.
+;
+; 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.
+
+$ORIGIN net.
+$TTL 300
+@ 			IN SOA	root.example.net. a.root.servers.nil. (
+				2000042100   	; serial
+				600         	; refresh
+				600         	; retry
+				1200    	; expire
+				600       	; minimum
+				)
+@			NS	a.root-servers.nil.
+
+; Referral outside of server authority, but with glue records present.
+; Don't hand out the glue.
+example.net.			NS	ns1.example.info.
+example.net.			NS	ns2.example.info.
+ns1.example.info.	172800	IN	A	192.0.2.101
+ns2.example.info.	172800	IN	A	192.0.2.102
diff --git a/tests/system/glue/nsx1/root-servers.nil.db b/tests/system/glue/nsx1/root-servers.nil.db
new file mode 100644
index 0000000..45050a9
--- /dev/null
+++ b/tests/system/glue/nsx1/root-servers.nil.db
@@ -0,0 +1,26 @@
+; Copyright (C) 2004, 2007  Internet Systems Consortium, Inc. ("ISC")
+; Copyright (C) 2000, 2001  Internet Software Consortium.
+;
+; 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.
+
+$TTL 300
+@			IN SOA	ns hostmaster (
+				1
+				3600
+				1800
+				1814400
+				3600
+				)
+			NS	a
+a			A	10.53.0.1
+b			A	10.53.0.2
diff --git a/tests/system/glue/nsx1/root.db b/tests/system/glue/nsx1/root.db
new file mode 100644
index 0000000..e43f2d2
--- /dev/null
+++ b/tests/system/glue/nsx1/root.db
@@ -0,0 +1,55 @@
+; Copyright (C) 2004, 2007  Internet Systems Consortium, Inc. ("ISC")
+; Copyright (C) 2000, 2001  Internet Software Consortium.
+;
+; 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.
+
+$TTL 300
+. 			IN SOA	postmaster.example. a.root.servers.nil. (
+				2000042100   	; serial
+				600         	; refresh
+				600         	; retry
+				1200    	; expire
+				600       	; minimum
+				)
+.			NS	a.root-servers.nil.
+
+root-servers.nil.	NS	a.root-servers.nil.
+a.root-servers.nil.	A	10.53.0.1
+
+; Delegate some domains that contain name servers for the sample
+; ccTLDs below.
+com.			172800	IN	NS	a.root-servers.nil.
+
+;
+; A sample TLD
+;
+example.			172800	IN	NS	NS.example.
+example.			172800	IN	NS	NS1.example.COM.
+NS.example.			172800	IN	A	192.0.2.1
+NS.example.			172800	IN	A	192.0.2.2
+; this "glue" is below a zone cut for com.  BIND 9 still uses it for
+; the delegation to example.  BIND 10 (with sqlite3 data source) doesn't.
+NS1.example.COM.		172800	IN	A	192.0.2.3
+
+;
+;
+;
+test.				172800	IN	NS	ns.test.
+test.				172800	IN	NS	ns1.example.net.
+ns.test.			172800	IN	A	192.0.2.200
+ns1.example.net.		172800	IN	A	192.0.2.201
+
+;
+; A hypothetical ccTLD where we are authoritative for the NS glue.
+;
+example.org		172800  IN      NS      b.root-servers.nil.
diff --git a/tests/system/glue/setup.sh.in b/tests/system/glue/setup.sh.in
new file mode 100755
index 0000000..dc5b28a
--- /dev/null
+++ b/tests/system/glue/setup.sh.in
@@ -0,0 +1,25 @@
+#!/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.
+
+SYSTEMTESTTOP=..
+. $SYSTEMTESTTOP/conf.sh
+
+rm -f */zone.sqlite3
+${B10_LOADZONE} -o . -d @builddir@/nsx1/zone.sqlite3 @builddir@/nsx1/root.db
+${B10_LOADZONE} -o root-servers.nil -d @builddir@/nsx1/zone.sqlite3 \
+	@builddir@/nsx1/root-servers.nil.db
+${B10_LOADZONE} -o com -d @builddir@/nsx1/zone.sqlite3 @builddir@/nsx1/com.db
+${B10_LOADZONE} -o net -d @builddir@/nsx1/zone.sqlite3 @builddir@/nsx1/net.db
diff --git a/tests/system/glue/test.good b/tests/system/glue/test.good
new file mode 100644
index 0000000..b9b4719
--- /dev/null
+++ b/tests/system/glue/test.good
@@ -0,0 +1,19 @@
+
+; <<>> DiG 9.8.0 <<>> @127.0.0.1 -p 5300 foo.bar.test
+; (1 server found)
+;; global options: +cmd
+;; Got answer:
+;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 55069
+;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 2, ADDITIONAL: 2
+;; WARNING: recursion requested but not available
+
+;; QUESTION SECTION:
+;foo.bar.test.			IN	A
+
+;; AUTHORITY SECTION:
+test.			172800	IN	NS	ns.test.
+test.			172800	IN	NS	ns1.example.net.
+
+;; ADDITIONAL SECTION:
+ns.test.		172800	IN	A	192.0.2.200
+ns1.example.net.	172800	IN	A	192.0.2.201
diff --git a/tests/system/glue/tests.sh b/tests/system/glue/tests.sh
new file mode 100755
index 0000000..50b2330
--- /dev/null
+++ b/tests/system/glue/tests.sh
@@ -0,0 +1,66 @@
+#!/bin/sh
+#
+# Copyright (C) 2004, 2007  Internet Systems Consortium, Inc. ("ISC")
+# Copyright (C) 2000, 2001, 2003  Internet Software Consortium.
+#
+# 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.
+
+SYSTEMTESTTOP=..
+. $SYSTEMTESTTOP/conf.sh
+
+#
+# Do glue tests.
+#
+
+status=0
+n=0
+
+# This query should result in a delegation with two NS; one in the delegated
+# zone and one in a so called out-of-bailiwick zone for which the auth server
+# has authority, too.  For the former, the server should return glue in the
+# parent zone.  For the latter, BIND 9 and BIND 10 behave differently; BIND 9
+# uses "glue" in the parent zone (since this is the root zone everything can
+# be considered a valid glue).  BIND 10 (using sqlite3 data source) searches
+# the other zone and uses the authoritative data in that zone (which is
+# intentionally different from the glue in the root zone).
+echo "I:testing that a TLD referral gets a full glue set from the root zone ($n)"
+$DIG +norec @10.53.0.1 -p 53210 foo.bar.example. A >dig.out.$n || status=1
+$PERL $DIGCOMP example.good dig.out.$n || status=1
+n=`expr $n + 1`
+
+echo "I:testing that we find glue A RRs we are authoritative for ($n)"
+$DIG +norec @10.53.0.1 -p 53210 foo.bar.example.org. a >dig.out.$n || status=1
+$PERL $DIGCOMP auth.good dig.out.$n || status=1
+n=`expr $n + 1`
+
+# We cannot do this test for BIND 10 because b10-auth doesn't act as a
+# recursive (caching) server (by design)
+# echo "I:testing that we find glue A/AAAA RRs in the cache ($n)"
+# $DIG +norec @10.53.0.1 -p 53210 foo.bar.yy. a >dig.out.$n || status=1
+# $PERL $DIGCOMP yy.good dig.out.$n || status=1
+# n=`expr $n + 1`
+
+echo "I:testing that we don't find out-of-zone glue ($n)"
+$DIG +norec @10.53.0.1 -p 53210 example.net. a > dig.out.$n || status=1
+$PERL $DIGCOMP noglue.good dig.out.$n || status=1
+n=`expr $n + 1`
+
+# This test currently fails (additional section will be empty, which is
+# incorrect).  See Trac ticket #646.
+#echo "I:testing that we are finding partial out-of-zone glue ($n)"
+#$DIG +norec @10.53.0.1 -p 53210 foo.bar.test. a >dig.out.$n || status=1
+#$PERL $DIGCOMP test.good dig.out.$n || status=1
+#n=`expr $n + 1`
+
+echo "I:exit status: $status"
+exit $status
diff --git a/tests/system/ifconfig.sh b/tests/system/ifconfig.sh
new file mode 100755
index 0000000..c0c365a
--- /dev/null
+++ b/tests/system/ifconfig.sh
@@ -0,0 +1,226 @@
+#!/bin/sh
+#
+# Copyright (C) 2004, 2007-2010  Internet Systems Consortium, Inc. ("ISC")
+# Copyright (C) 2000-2003  Internet Software Consortium.
+#
+# 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.
+
+#
+# Set up interface aliases for bind9 system tests.
+#
+# IPv4: 10.53.0.{1..7}				RFC 1918
+# IPv6: fd92:7065:b8e:ffff::{1..7}		ULA
+#
+
+config_guess=""
+for f in ./config.guess ../../config.guess
+do
+	if test -f $f
+	then
+		config_guess=$f
+	fi
+done
+
+if test "X$config_guess" = "X"
+then
+	cat <<EOF >&2
+$0: must be run from the top level source directory or the
+bin/tests/system directory
+EOF
+	exit 1
+fi
+
+# If running on hp-ux, don't even try to run config.guess.
+# It will try to create a temporary file in the current directory,
+# which fails when running as root with the current directory
+# on a NFS mounted disk.
+
+case `uname -a` in
+  *HP-UX*) sys=hpux ;;
+  *) sys=`sh $config_guess` ;;
+esac
+
+case "$2" in
+[0-9]|[1-9][0-9]|[1-9][0-9][0-9]) base=$2;;
+*) base=""
+esac
+
+case "$3" in
+[0-9]|[1-9][0-9]|[1-9][0-9][0-9]) base6=$2;;
+*) base6=""
+esac
+
+case "$1" in
+
+    start|up)
+	for ns in 1 2 3 4 5 6 7
+	do
+		if test -n "$base"
+		then
+			int=`expr $ns + $base - 1`
+		else
+			int=$ns
+		fi
+		if test -n "$base6"
+		then
+			int6=`expr $ns + $base6 - 1`
+		else
+			int6=$ns
+		fi
+		case "$sys" in
+		    *-pc-solaris2.5.1)
+			ifconfig lo0:$int 10.53.0.$ns netmask 0xffffffff up
+			;;
+		    *-sun-solaris2.[6-7])
+			ifconfig lo0:$int 10.53.0.$ns netmask 0xffffffff up
+			;;
+		    *-*-solaris2.[8-9]|*-*-solaris2.1[0-9])
+			/sbin/ifconfig lo0:$int plumb
+			/sbin/ifconfig lo0:$int 10.53.0.$ns up
+			if test -n "$int6"
+			then
+				/sbin/ifconfig lo0:$int6 inet6 plumb
+				/sbin/ifconfig lo0:$int6 \
+					inet6 fd92:7065:b8e:ffff::$ns up
+			fi
+			;;
+		    *-*-linux*)
+			ifconfig lo:$int 10.53.0.$ns up netmask 255.255.255.0
+			ifconfig lo inet6 add fd92:7065:b8e:ffff::$ns/64
+		        ;;
+		    *-unknown-freebsd*)
+			ifconfig lo0 10.53.0.$ns alias netmask 0xffffffff
+			ifconfig lo0 inet6 fd92:7065:b8e:ffff::$ns alias
+			;;
+		    *-unknown-netbsd*)
+			ifconfig lo0 10.53.0.$ns alias netmask 255.255.255.0
+			ifconfig lo0 inet6 fd92:7065:b8e:ffff::$ns alias
+			;;
+		    *-unknown-openbsd*)
+			ifconfig lo0 10.53.0.$ns alias netmask 255.255.255.0
+			ifconfig lo0 inet6 fd92:7065:b8e:ffff::$ns alias
+			;;
+		    *-*-bsdi[3-5].*)
+			ifconfig lo0 add 10.53.0.$ns netmask 255.255.255.0
+			;;
+		    *-dec-osf[4-5].*)
+			ifconfig lo0 alias 10.53.0.$ns
+			;;
+		    *-sgi-irix6.*)
+			ifconfig lo0 alias 10.53.0.$ns
+			;;
+		    *-*-sysv5uw7*|*-*-sysv*UnixWare*|*-*-sysv*OpenUNIX*)
+			ifconfig lo0 10.53.0.$ns alias netmask 0xffffffff
+			;;
+		    *-ibm-aix4.*|*-ibm-aix5.*)
+			ifconfig lo0 alias 10.53.0.$ns
+			ifconfig lo0 inet6 alias -dad fd92:7065:b8e:ffff::$ns/64
+			;;
+		    hpux)
+			ifconfig lo0:$int 10.53.0.$ns netmask 255.255.255.0 up
+			ifconfig lo0:$int inet6 fd92:7065:b8e:ffff::$ns up
+		        ;;
+		    *-sco3.2v*)
+			ifconfig lo0 alias 10.53.0.$ns
+			;;
+		    *-darwin*)
+			ifconfig lo0 alias 10.53.0.$ns
+			ifconfig lo0 inet6 fd92:7065:b8e:ffff::$ns alias
+			;;
+	            *)
+			echo "Don't know how to set up interface.  Giving up."
+			exit 1
+		esac
+	done
+	;;
+
+    stop|down)
+	for ns in 7 6 5 4 3 2 1
+	do
+		if test -n "$base"
+		then
+			int=`expr $ns + $base - 1`
+		else
+			int=$ns
+		fi
+		case "$sys" in
+		    *-pc-solaris2.5.1)
+			ifconfig lo0:$int 0.0.0.0 down
+			;;
+		    *-sun-solaris2.[6-7])
+			ifconfig lo0:$int 10.53.0.$ns down
+			;;
+		    *-*-solaris2.[8-9]|*-*-solaris2.1[0-9])
+			ifconfig lo0:$int 10.53.0.$ns down
+			ifconfig lo0:$int 10.53.0.$ns unplumb
+			if test -n "$int6"
+			then
+				ifconfig lo0:$int6 inet6 down
+				ifconfig lo0:$int6 inet6 unplumb
+			fi
+			;;
+		    *-*-linux*)
+			ifconfig lo:$int 10.53.0.$ns down
+			ifconfig lo inet6 del fd92:7065:b8e:ffff::$ns/64
+		        ;;
+		    *-unknown-freebsd*)
+			ifconfig lo0 10.53.0.$ns delete
+			ifconfig lo0 inet6 fd92:7065:b8e:ffff::$ns delete
+			;;
+		    *-unknown-netbsd*)
+			ifconfig lo0 10.53.0.$ns delete
+			ifconfig lo0 inet6 fd92:7065:b8e:ffff::$ns delete
+			;;
+		    *-unknown-openbsd*)
+			ifconfig lo0 10.53.0.$ns delete
+			ifconfig lo0 inet6 fd92:7065:b8e:ffff::$ns delete
+			;;
+		    *-*-bsdi[3-5].*)
+			ifconfig lo0 remove 10.53.0.$ns
+			;;
+		    *-dec-osf[4-5].*)
+			ifconfig lo0 -alias 10.53.0.$ns
+			;;
+		    *-sgi-irix6.*)
+			ifconfig lo0 -alias 10.53.0.$ns
+			;;
+		    *-*-sysv5uw7*|*-*-sysv*UnixWare*|*-*-sysv*OpenUNIX*)
+			ifconfig lo0 -alias 10.53.0.$ns
+			;;
+		    *-ibm-aix4.*|*-ibm-aix5.*)
+			ifconfig lo0 delete 10.53.0.$ns
+			ifconfig lo0 delete inet6 fd92:7065:b8e:ffff::$ns/64
+			;;
+		    hpux)
+			ifconfig lo0:$int 0.0.0.0
+			ifconfig lo0:$int inet6 ::
+		        ;;
+		    *-sco3.2v*)
+			ifconfig lo0 -alias 10.53.0.$ns
+			;;
+		    *darwin*)
+			ifconfig lo0 -alias 10.53.0.$ns
+			ifconfig lo0 inet6 fd92:7065:b8e:ffff::$ns delete
+			;;
+	            *)
+			echo "Don't know how to destroy interface.  Giving up."
+			exit 1
+		esac
+	done
+
+	;;
+
+	*)
+		echo "Usage: $0 { up | down } [base]"
+		exit 1
+esac
diff --git a/tests/system/run.sh b/tests/system/run.sh
new file mode 100755
index 0000000..4f852f4
--- /dev/null
+++ b/tests/system/run.sh
@@ -0,0 +1,125 @@
+#!/bin/sh
+#
+# Copyright (C) 2004, 2007, 2010  Internet Systems Consortium, Inc. ("ISC")
+# Copyright (C) 2000, 2001  Internet Software Consortium.
+#
+# 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.
+
+#
+# Run a system test.
+#
+
+SYSTEMTESTTOP=.
+. $SYSTEMTESTTOP/conf.sh
+
+stopservers=true
+
+case $1 in
+   --keep) stopservers=false; shift ;;
+esac
+
+test $# -gt 0 || { echo "usage: $0 [--keep] test-directory" >&2; exit 1; }
+
+test=$1
+shift
+
+test -d $test || { echo "$0: $test: no such test" >&2; exit 1; }
+
+echo "S:$test:`date`" >&2
+echo "T:$test:1:A" >&2
+echo "A:System test $test" >&2
+
+if [ x$PERL = x ]
+then
+    echo "I:Perl not available.  Skipping test." >&2
+    echo "R:UNTESTED" >&2
+    echo "E:$test:`date`" >&2
+    exit 0;
+fi
+
+$PERL $TESTSOCK || {
+    echo "I:Network interface aliases not set up.  Skipping test." >&2;
+    echo "R:UNTESTED" >&2;
+    echo "E:$test:`date`" >&2;
+    exit 0;
+}
+
+
+# Check for test-specific prerequisites.
+test ! -f $test/prereq.sh || ( cd $test && sh prereq.sh "$@" )
+result=$?
+
+if [ $result -eq 0 ]; then
+    : prereqs ok
+else
+    echo "I:Prerequisites for $test missing, skipping test." >&2
+    [ $result -eq 255 ] && echo "R:SKIPPED" || echo "R:UNTESTED"
+    echo "E:$test:`date`" >&2
+    exit 0
+fi
+
+# Check for PKCS#11 support
+if
+    test ! -f $test/usepkcs11 || sh cleanpkcs11.sh
+then
+    : pkcs11 ok
+else
+    echo "I:Need PKCS#11 for $test, skipping test." >&2
+    echo "R:PKCS11ONLY" >&2
+    echo "E:$test:`date`" >&2
+    exit 0
+fi
+
+# Set up any dynamically generated test data
+if test -f $test/setup.sh
+then
+   ( cd $test && sh setup.sh "$@" )
+fi
+
+# Start name servers running
+$PERL start.pl $test || exit 1
+
+# Run the tests
+( cd $test ; sh tests.sh )
+
+status=$?
+
+if $stopservers
+then
+    :
+else
+    exit $status
+fi
+
+# Shutdown
+$PERL stop.pl $test
+
+status=`expr $status + $?`
+
+if [ $status != 0 ]; then
+	echo "R:FAIL"
+	# Don't clean up - we need the evidence.
+	find . -name core -exec chmod 0644 '{}' \;
+else
+	echo "R:PASS"
+
+	# Clean up.
+	if test -f $test/clean.sh
+	then
+	   ( cd $test && sh clean.sh "$@" )
+	fi
+fi
+
+echo "E:$test:`date`"
+
+exit $status
diff --git a/tests/system/runall.sh b/tests/system/runall.sh
new file mode 100755
index 0000000..5d0fe9b
--- /dev/null
+++ b/tests/system/runall.sh
@@ -0,0 +1,44 @@
+#!/bin/sh
+#
+# Copyright (C) 2004, 2007, 2010  Internet Systems Consortium, Inc. ("ISC")
+# Copyright (C) 2000, 2001  Internet Software Consortium.
+#
+# 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.
+
+#
+# Run all the system tests.
+#
+
+SYSTEMTESTTOP=.
+. $SYSTEMTESTTOP/conf.sh
+
+status=0
+
+for d in $SUBDIRS
+do
+	sh run.sh $d || status=1
+done
+
+$PERL $TESTSOCK || {
+    cat <<EOF >&2
+I:
+I:NOTE: Many of the tests were skipped because they require that
+I:      the IP addresses 10.53.0.1 through 10.53.0.7 are configured
+I:	as alias addresses on the loopback interface.  Please run
+I:	"tests/system/ifconfig.sh up" as root to configure them
+I:	and rerun the tests.
+EOF
+    exit 0;
+}
+
+exit $status
diff --git a/tests/system/start.pl b/tests/system/start.pl
new file mode 100755
index 0000000..56f00c4
--- /dev/null
+++ b/tests/system/start.pl
@@ -0,0 +1,226 @@
+#!/usr/bin/perl -w
+#
+# Copyright (C) 2004-2008, 2010  Internet Systems Consortium, Inc. ("ISC")
+# Copyright (C) 2001  Internet Software Consortium.
+#
+# 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.
+
+# Framework for starting test servers.
+# Based on the type of server specified, check for port availability, remove
+# temporary files, start the server, and verify that the server is running.
+# If a server is specified, start it. Otherwise, start all servers for test.
+
+use strict;
+use Cwd 'abs_path';
+use Getopt::Long;
+
+# Option handling
+#   --noclean test [server [options]]
+#
+#   --noclean - Do not cleanup files in server directory
+#   test - name of the test directory
+#   server - name of the server directory
+#   options - alternate options for the server
+
+my $usage = "usage: $0 [--noclean] test-directory [server-directory [server-options]]";
+my $noclean;
+GetOptions('noclean' => \$noclean);
+my $test = $ARGV[0];
+my $server = $ARGV[1];
+my $options = $ARGV[2];
+
+if (!$test) {
+	print "$usage\n";
+}
+if (!-d $test) {
+	print "No test directory: \"$test\"\n";
+}
+if ($server && !-d "$test/$server") {
+	print "No server directory: \"$test/$server\"\n";
+}
+
+# Global variables
+my $topdir = abs_path("$test/..");
+my $testdir = abs_path("$test");
+my $RUN_BIND10 = $ENV{'RUN_BIND10'};
+my $NAMED = $ENV{'NAMED'};
+my $LWRESD = $ENV{'LWRESD'};
+my $DIG = $ENV{'DIG'};
+my $PERL = $ENV{'PERL'};
+my $TESTSOCK = $ENV{'TESTSOCK'};
+
+# Start the server(s)
+
+if ($server) {
+	if ($server =~ /^ns/) {
+		&check_ports($server);
+	}
+	&start_server($server, $options);
+	if ($server =~ /^ns/) {
+		&verify_server($server);
+	}
+} else {
+	# Determine which servers need to be started for this test.
+	opendir DIR, $testdir;
+	my @files = sort readdir DIR;
+	closedir DIR;
+
+	my @ns = grep /^nsx?[0-9]*$/, @files;
+	my @lwresd = grep /^lwresd[0-9]*$/, @files;
+	my @ans = grep /^ans[0-9]*$/, @files;
+
+	# Start the servers we found.
+	&check_ports();
+	foreach my $s (@ns, @lwresd, @ans) {
+		&start_server($s);
+	}
+	foreach my $s (@ns) {
+		&verify_server($s);
+	}
+}
+
+# Subroutines
+
+sub check_ports {
+	my $server = shift;
+	my $options = "";
+
+	if ($server && $server =~ /(\d+)$/) {
+		$options = "-i $1";
+	}
+
+	my $tries = 0;
+	while (1) {
+		my $return = system("$PERL $TESTSOCK -p 53210 $options");
+		last if ($return == 0);
+		if (++$tries > 4) {
+			print "$0: could not bind to server addresses, still running?\n";
+			print "I:server sockets not available\n";
+			print "R:FAIL\n";
+			system("$PERL $topdir/stop.pl $testdir"); # Is this the correct behavior?
+			exit 1;
+		}
+		print "I:Couldn't bind to socket (yet)\n";
+		sleep 2;
+	}
+}
+
+sub start_server {
+	my $server = shift;
+	my $options = shift;
+
+	my $cleanup_files;
+	my $command;
+	my $pid_file;
+
+	if ($server =~ /^nsx/) {
+		$cleanup_files = "{bind10.run}";
+		$command = "B10_FROM_SOURCE_LOCALSTATEDIR=$testdir/$server/ ";
+		$command .= "$RUN_BIND10 ";
+		if ($options) {
+			$command .= "$options";
+		} else {
+			$command .= "--msgq-socket-file=$testdir/$server/msgq_socket ";
+			$command .= "--pid-file=$testdir/$server/bind10.pid ";
+			$command .= "-v";
+		}
+		$command .= " >bind10.run 2>&1 &";
+		$pid_file = "bind10.pid";
+	} elsif ($server =~ /^ns/) {
+		$cleanup_files = "{*.jnl,*.bk,*.st,named.run}";
+		$command = "$NAMED ";
+		if ($options) {
+			$command .= "$options";
+		} else {
+			$command .= "-m record,size,mctx ";
+			$command .= "-T clienttest ";
+			$command .= "-T nosoa "
+				if (-e "$testdir/$server/named.nosoa");
+			$command .= "-T noaa "
+				if (-e "$testdir/$server/named.noaa");
+			$command .= "-c named.conf -d 99 -g";
+		}
+		$command .= " >named.run 2>&1 &";
+		$pid_file = "named.pid";
+	} elsif ($server =~ /^lwresd/) {
+		$cleanup_files = "{lwresd.run}";
+		$command = "$LWRESD ";
+		if ($options) {
+			$command .= "$options";
+		} else {
+			$command .= "-m record,size,mctx ";
+			$command .= "-T clienttest ";
+			$command .= "-C resolv.conf -d 99 -g ";
+			$command .= "-i lwresd.pid -P 9210 -p 53210";
+		}
+		$command .= " >lwresd.run 2>&1 &";
+		$pid_file = "lwresd.pid";
+	} elsif ($server =~ /^ans/) {
+		$cleanup_files = "{ans.run}";
+		$command = "$PERL ./ans.pl ";
+		if ($options) {
+			$command .= "$options";
+		} else {
+			$command .= "";
+		}
+		$command .= " >ans.run 2>&1 &";
+		$pid_file = "ans.pid";
+	} else {
+		print "I:Unknown server type $server\n";
+		print "R:FAIL\n";
+		system "$PERL $topdir/stop.pl $testdir";
+		exit 1;
+	}
+
+	#               print "I:starting server $server\n";
+
+	chdir "$testdir/$server";
+
+	unless ($noclean) {
+		unlink glob $cleanup_files;
+	}
+
+	system "$command";
+
+	my $tries = 0;
+	while (!-f $pid_file) {
+		if (++$tries > 14) {
+			print "I:Couldn't start server $server\n";
+			print "R:FAIL\n";
+			system "$PERL $topdir/stop.pl $testdir";
+			exit 1;
+		}
+		sleep 1;
+	}
+}
+
+sub verify_server {
+	my $server = shift;
+	my $n = $server;
+	$n =~ s/^nsx?//;
+
+	my $tries = 0;
+	while (1) {
+		my $return = system("$DIG +tcp +noadd +nosea +nostat +noquest +nocomm +nocmd -p 53210 version.bind. chaos txt \@10.53.0.$n > dig.out");
+		last if ($return == 0);
+		print `grep ";" dig.out`;
+		if (++$tries >= 30) {
+			print "I:no response from $server\n";
+			print "R:FAIL\n";
+			system("$PERL $topdir/stop.pl $testdir");
+			exit 1;
+		}
+		sleep 2;
+	}
+	unlink "dig.out";
+}
diff --git a/tests/system/stop.pl b/tests/system/stop.pl
new file mode 100755
index 0000000..a803f52
--- /dev/null
+++ b/tests/system/stop.pl
@@ -0,0 +1,188 @@
+#!/usr/bin/perl -w
+#
+# Copyright (C) 2004-2007  Internet Systems Consortium, Inc. ("ISC")
+# Copyright (C) 2001  Internet Software Consortium.
+#
+# 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.
+
+# Framework for stopping test servers
+# Based on the type of server specified, signal the server to stop, wait
+# briefly for it to die, and then kill it if it is still alive.
+# If a server is specified, stop it. Otherwise, stop all servers for test.
+
+use strict;
+use Cwd 'abs_path';
+
+# Option handling
+#   [--use-rndc] test [server]
+#
+#   test - name of the test directory
+#   server - name of the server directory
+
+my $usage = "usage: $0 [--use-rndc] test-directory [server-directory]";
+my $use_rndc;
+
+while (@ARGV && $ARGV[0] =~ /^-/) {
+	my $opt = shift @ARGV;
+	if ($opt eq '--use-rndc') {
+		$use_rndc = 1;
+	} else {
+		die "$usage\n";
+	}
+}
+
+my $test = $ARGV[0];
+my $server = $ARGV[1];
+
+my $errors = 0;
+
+die "$usage\n" unless defined($test);
+die "No test directory: \"$test\"\n" unless (-d $test);
+die "No server directory: \"$server\"\n" if (defined($server) && !-d "$test/$server");
+
+# Global variables
+my $testdir = abs_path($test);
+my @servers;
+
+
+# Determine which servers need to be stopped.
+if (defined $server) {
+	@servers = ($server);
+} else {
+	local *DIR;
+	opendir DIR, $testdir or die "$testdir: $!\n";
+	my @files = sort readdir DIR;
+	closedir DIR;
+
+	my @ns = grep /^nsx?[0-9]*$/, @files;
+	my @lwresd = grep /^lwresd[0-9]*$/, @files;
+	my @ans = grep /^ans[0-9]*$/, @files;
+
+	push @servers, @ns, @lwresd, @ans;
+}
+
+
+# Stop the server(s), pass 1: rndc.
+if ($use_rndc) {
+	foreach my $server (grep /^ns/, @servers) {
+		stop_rndc($server);
+	}
+
+	wait_for_servers(30, grep /^ns/, @servers);
+}
+
+
+# Pass 2: SIGTERM
+foreach my $server (@servers) {
+	stop_signal($server, "TERM");
+}
+
+wait_for_servers(60, @servers);
+
+# Pass 3: SIGABRT
+foreach my $server (@servers) {
+	stop_signal($server, "ABRT");
+}
+
+exit($errors ? 1 : 0);
+
+# Subroutines
+
+# Return the full path to a given server's PID file.
+sub server_pid_file {
+	my($server) = @_;
+
+	my $pid_file;
+	if ($server =~ /^nsx/) {
+		$pid_file = "bind10.pid";
+	} elsif ($server =~ /^ns/) {
+		$pid_file = "named.pid";
+	} elsif ($server =~ /^lwresd/) {
+		$pid_file = "lwresd.pid";
+	} elsif ($server =~ /^ans/) {
+		$pid_file = "ans.pid";
+	} else {
+		print "I:Unknown server type $server\n";
+		exit 1;
+	}
+	$pid_file = "$testdir/$server/$pid_file";
+}
+
+# Read a PID.
+sub read_pid {
+	my($pid_file) = @_;
+
+	local *FH;
+	my $result = open FH, "< $pid_file";
+	if (!$result) {
+		print "I:$pid_file: $!\n";
+		unlink $pid_file;
+		return;
+	}
+
+	my $pid = <FH>;
+	chomp($pid);
+	return $pid;
+}
+
+# Stop a named process with rndc.
+sub stop_rndc {
+	my($server) = @_;
+
+	return unless ($server =~ /^ns(\d+)$/);
+	my $ip = "10.53.0.$1";
+
+	# Ugly, but should work.
+	system("$ENV{RNDC} -c $testdir/../common/rndc.conf -s $ip -p 9953 stop | sed 's/^/I:$server /'");
+	return;
+}
+
+# Stop a server by sending a signal to it.
+sub stop_signal {
+	my($server, $sig) = @_;
+
+	my $pid_file = server_pid_file($server);
+	return unless -f $pid_file;
+
+	my $pid = read_pid($pid_file);
+	return unless defined($pid);
+
+	if ($sig eq 'ABRT') {
+		print "I:$server didn't die when sent a SIGTERM\n";
+		$errors++;
+	}
+
+	my $result = kill $sig, $pid;
+	if (!$result) {
+		print "I:$server died before a SIG$sig was sent\n";
+		unlink $pid_file;
+		$errors++;
+	}
+
+	return;
+}
+
+sub wait_for_servers {
+	my($timeout, @servers) = @_;
+
+	my @pid_files = grep { defined($_) }
+	                map  { server_pid_file($_) } @servers;
+
+	while ($timeout > 0 && @pid_files > 0) {
+		@pid_files = grep { -f $_ } @pid_files;
+		sleep 1 if (@pid_files > 0);
+		$timeout--;
+	}
+
+	return;
+}




More information about the bind10-changes mailing list