BIND 10 trac547, updated. f5fbca52ef6cdc84c9ac574bedfd5c2af0251263 Merge branch 'trac547' of ssh://bind10.isc.org/var/bind10/git/bind10 into trac547

BIND 10 source code commits bind10-changes at lists.isc.org
Wed Feb 16 02:16:00 UTC 2011


The branch, trac547 has been updated
       via  f5fbca52ef6cdc84c9ac574bedfd5c2af0251263 (commit)
       via  b5bed27df009939459ea1609d90ab9d530c68eaf (commit)
       via  b905a971e121bb003579a1b274c7bd2ec76c199e (commit)
       via  c99bc5c53604084c70d52365afd4a11258c8c505 (commit)
       via  4c0ca5ad7d0df86146fc9f467b3dac29415db33b (commit)
       via  b93a0c234eb66e98ab7b94cac1411335d70d12e3 (commit)
       via  4f992e158c48eba97d4cfd497a1abba12336e38d (commit)
       via  496a0353cded1f60342e7713c28fecdffcecbbbe (commit)
       via  6c11d9c51b88e2a6d238eac951d1d0317978f636 (commit)
       via  cdd18357620129b8533e9619b30b37ecbc91ede0 (commit)
       via  8aa3b2246ae095bbe7f855fd11656ae3bdb98986 (commit)
       via  adba8b7c9e699cf557646e942012567f8e5156c9 (commit)
       via  541478374f183c2742a7f07e6558836a4dd09570 (commit)
       via  17f237478961005707d649a661cc72a4a0d612d4 (commit)
       via  12c23a667c1e9a4b92d1cd8fc07136faf4c41a8a (commit)
       via  dd61821a6c910d90c4a46024e303965048dde0aa (commit)
       via  0e6a18e12f61cc482e07078776234f32605312e5 (commit)
       via  756e56a8c683906b1b50bba5675c996fb1295820 (commit)
       via  0cc21b601f0ef1e907deaf3dbf02813ec2e0f493 (commit)
       via  afc4be09c37272fdadecbe8496b2579ef4cf3e42 (commit)
       via  5e357877317a7a70e9a66643a4f846c32f5dc2ae (commit)
       via  db9ebc20e4f4b401307d6237796e0252bfaa1c59 (commit)
       via  b9e1f48df9f398d7641f61317a7d969690af1ff2 (commit)
       via  d8989e0a05ae7a453a6768f2ff4019d41ad84aab (commit)
       via  a9ac4ee37388d93c0adceffa424599b5affc5c3a (commit)
       via  1708a52ea3956e0e7027991f6cfeb6a1b9415b94 (commit)
       via  47e53343554aa6b4323ab05799a6315fb1e42538 (commit)
       via  dfb68c22293f3e84aae8e81865525dd043f26e0d (commit)
       via  72267db6056eca0fbc9ea9b656ffb42475e7480b (commit)
       via  a151c4a62009d73ab3f05607ccd12889de4b9718 (commit)
       via  0ae5d2d73aad78ee24e641870cec1ab06f65e3f6 (commit)
       via  411b0f674614000d1f4606e0c225d078c1981628 (commit)
       via  696cec5b0e99ebf65c3ea634abbc6114f1b92d8c (commit)
       via  2c54e9c5f490b876eb59200cfcf9cc4623250bda (commit)
       via  c9eb48c9a8c49d99a31ce752b11ac507f3346356 (commit)
       via  8697c90c350f8ec3c3c3677de0761230fedc8ef4 (commit)
       via  9cf25c82920546461f63a87359a9ef1aa00bfc78 (commit)
       via  e8cbf87a4e734023f9a79d131dbe1fde067fe0c3 (commit)
       via  17a76d75e8ee9e88417058b220b01761c1fd370a (commit)
       via  af0e5cd93bebb27cb5c4457f7759d12c8bf953a6 (commit)
       via  2d2093b3eda1d0d09ae852f2dc6feefc4886b71f (commit)
       via  49e2c1ae796e46fca4f5c395a92387638bb828c6 (commit)
       via  a02e4f3441285f10958b72e2337aa1b6e290fb56 (commit)
       via  16bbee505681b46ef60febb8067765704afdeb24 (commit)
       via  53f4afe16013ef6f97556d3b49d1bc56443dc08c (commit)
       via  180015eada7cdc67f8756a7425f9023d884b19df (commit)
       via  f9a11dcba2da26e3a27a4be00461aea07aa60d85 (commit)
       via  a43d8d0ef4cd44d5e0ab9fa9fc746e6d1ff353e0 (commit)
       via  495087240c7b5736999e3edca697eb8509068ca3 (commit)
       via  f364f8feb190aa09fd0a65b35bdf05a3c7fe0932 (commit)
       via  1b4b8193675f0ebc9b63480900257fa21e5cfa72 (commit)
       via  214eb805737eed8035490dc8ada790bca5c14fb6 (commit)
       via  073d75e7ab64ff107198181cbc9ef4482e83b681 (commit)
       via  4fd542a1c095efee2afb61c440784cd3984803cc (commit)
       via  5400e06818b54c363c6006395ea51567caa651b3 (commit)
       via  42e9021df5556117697f9228645a4ff3eee246ae (commit)
       via  746980b33112b63147744796663389e5e1c3144c (commit)
       via  47f69905aac05aa6f619cb143a81f206b396975a (commit)
       via  ca368d008315b135fa2ad3ff366aa5ae15eb4d12 (commit)
       via  c5dfa657c2431f3daea2dcc49741f96383f0d18d (commit)
       via  1d018cd8a62fdf430f5322db023badc030cee6ee (commit)
       via  0b1f8cb6dfa5481cb67e43ee8c2f24fac97ec95d (commit)
       via  0c760d178eecf3162e848074e3fe631d690cc77a (commit)
       via  c393ec92641b6699822ebacd1c3f4c68496f3316 (commit)
       via  e7251d0c587ee8a794e590fdcdd4fa07f6a19c1c (commit)
       via  26ab69e18bd0417086315c20192b8b93093bf6e3 (commit)
       via  a61a83e551549808ddc80fa99ad453932d001ff0 (commit)
       via  81c35ef7e5e01da333654443045de3dd2663482c (commit)
       via  f167a263da41987a76b76e8b71acb9620a0c89c5 (commit)
       via  c334da7773375a87b5e7154daabf132f945f154a (commit)
       via  4d29fda48f7304d2c5bfbc246326850ca5970b0f (commit)
       via  1ed67ffd6fc90c04a79056766b2658c01d30c548 (commit)
       via  cd6beb6ccd33f33efbb6ac078a53fa9e0de7f3d0 (commit)
       via  0c303cb20840f80038f98f023ebd6903a452efdf (commit)
       via  f436c5c20d296c17f8afcea1e6ba6242a9d09929 (commit)
       via  dcfa4914665900dc475ac1dbe98336d8daab1c61 (commit)
       via  389cf02a183ea2223d1c44f68fc2f764ca5d69ea (commit)
       via  b832c3c975bcae66717ce28e392a189887b6f08f (commit)
       via  f1885e6e7b4d2688be602b189eece51e4e649f42 (commit)
       via  9bfbbabf77cb41babe425c9006c050edcd3cda7b (commit)
       via  32ca390dad48d4d5c8bea98248bdfc62894f87ed (commit)
       via  3b18410e04cda0411a9e5699005a452d144793d7 (commit)
       via  81d20bfeeab719136909382cc2d9bc1aba5f8070 (commit)
       via  b6d39e3da5f916c5c79ea49d7aebeca64a2c03b6 (commit)
       via  0c97558ecf0c9c35d9ac86e214a0cbd1c10aaec7 (commit)
       via  8cce1250c4b1a5303b515a75278103118cee83e0 (commit)
       via  3715569dde8e4770696ba2552fb545d2cff81b87 (commit)
       via  c336287b3e5f3262b4f85ec45e7b38b893c0b2b8 (commit)
       via  bc1b548a3828f5cd722496dd8eb5c09fc1f95187 (commit)
       via  f998d613872373c2fbf7088088d7464e79cc10c3 (commit)
       via  c5f4af2f749808f7e8b96dca09819f69bb094031 (commit)
       via  8ddc37ac3b6229f03a1fc122ea3c1c8906cbd75b (commit)
       via  cac61e91646c344359a5b767d2f2042d05cc1d74 (commit)
       via  fb4ca10d60279cc2d7da663b912dcb189849d0da (commit)
       via  59dd3fb61b265455b544407fcd8976a04f152af3 (commit)
       via  65ef7a150b4562f2e80222afd7fb010cbd55ec43 (commit)
       via  026a4104701791649673a74591007e1d50167cbe (commit)
       via  c1dd1aeae9aa2cb06b372d2002fa15d57aa7c552 (commit)
       via  a8746fb17e10db78330cf69b2850515020f5cd56 (commit)
       via  42dbc3cabef1db3f4c9ce99bc0830ce34794a704 (commit)
       via  e7c61f1bb5cf375a1f005bb7d77080d2fcd8fb9e (commit)
       via  ac8359174eaeb1df18bddcb0a57807d49ff8ed53 (commit)
       via  c94995ad3292958582d1ad491fdfbaa76e43cad8 (commit)
       via  fca9cd2ad55dfdff80ed3106c4cb40bf4d508d26 (commit)
       via  42beef26ba126bab9740af0a809617fc3c1906c6 (commit)
       via  3f791c7c437212e55981f044ef722a759f58a184 (commit)
       via  e64c9d4107c1d012b68239ecf4d828467f7eb6f7 (commit)
       via  ba8595ee4dbcc87deb5aca78359dd7a42e4a6f45 (commit)
       via  0162b6441a567a0a687a726f651e23bb754bfd71 (commit)
       via  74266da0141650d6ca337a66f266ba675ad2d357 (commit)
       via  bd58459c94e2074aecb39b02fa47a2b79e48cfb6 (commit)
       via  238f276c9b5887fcf72e86011e610466ce163f5b (commit)
       via  87c347e04505f141d624eb7be497181a37e346d0 (commit)
       via  3a899ef4c5c11d4bdfd9f50ac4aaa1b276060a65 (commit)
       via  a99da0d489fbecc59b6d75a85e71e92ec7d173e3 (commit)
       via  a0e0f22323abff0332ac4dff60300874a3e2f1a0 (commit)
       via  4205ee154f209d4493e2b7176b53c79d5dbaf193 (commit)
       via  6e5884cfb1610b47339be5f2578e8e77840d3095 (commit)
       via  4e8c15eae9ffb0d159f5d9f138455aa0213163fe (commit)
       via  83d5d9a0bc2599bd9a643c2c6294f382638486b6 (commit)
       via  e75ba8ce2f5b2f9814741d0ea633f3542b458a74 (commit)
       via  f62f4f06cf94b4d979e5b036a41c06b4f53e5759 (commit)
       via  39158d307dd4b4a74f89bb755d22d1edd3a04d80 (commit)
       via  777f07c955102aaa875ed2567d788b4103f849e1 (commit)
       via  2aff13f019888ae254733f2c16d9a8b995a48318 (commit)
       via  0050a51bfb904132db32a2d4729064d8a35a5332 (commit)
       via  77f677784ed005ff2cf6e5387d837897e3e3bdc4 (commit)
       via  886b10843e5e8fe2fd7b04684a590affe1b812ab (commit)
       via  07e20270989ec37e95feb3a01fefeb5b80465bef (commit)
       via  a7bf808a220742d18fb1868241f1665f745ebdbc (commit)
       via  8eafc24514f6d6cc1a8b71bef7358cb9bdff5a8c (commit)
       via  38e8504574d599e1e6d139ebcfe5f905de678e2d (commit)
       via  61e391f3bb0af427263c2374d613af3fd10a5fe4 (commit)
       via  6c3a274305a7823a4060769ab21d55c5975615d6 (commit)
       via  203152faad9ccda93c9f799ef48c47d22da1d3bf (commit)
       via  b6b9f884f331a4f8726a3cd6a0f3eed7870b7cf8 (commit)
       via  5efcee702cfe268615733f30988abca319089531 (commit)
       via  5a7d082452848daa9dcb0b023f0829d457d8bcc0 (commit)
      from  293a8a684262ffcb187a9c7ed2649eee2e9d0e30 (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 f5fbca52ef6cdc84c9ac574bedfd5c2af0251263
Merge: b5bed27df009939459ea1609d90ab9d530c68eaf 293a8a684262ffcb187a9c7ed2649eee2e9d0e30
Author: Naoki Kambe <kambe at jprs.co.jp>
Date:   Wed Feb 16 11:15:46 2011 +0900

    Merge branch 'trac547' of ssh://bind10.isc.org/var/bind10/git/bind10 into trac547

commit b5bed27df009939459ea1609d90ab9d530c68eaf
Author: Naoki Kambe <kambe at jprs.co.jp>
Date:   Thu Feb 3 20:40:51 2011 +0900

    [trac547] necessary more changes for adding "b10-stats-httpd" module
    [trac547] added "shutdown" command handling to "b10-stats-httpd" module

commit b905a971e121bb003579a1b274c7bd2ec76c199e
Author: Naoki Kambe <kambe at jprs.co.jp>
Date:   Thu Feb 3 20:40:51 2011 +0900

    [trac547] change configure.ac for module "b10-stats-httpd"

commit c99bc5c53604084c70d52365afd4a11258c8c505
Author: Naoki Kambe <kambe at jprs.co.jp>
Date:   Thu Feb 3 20:40:51 2011 +0900

    [trac547] add mocked-up module "b10-stats-httpd" for HTTP/XML interface

commit 4c0ca5ad7d0df86146fc9f467b3dac29415db33b
Author: Naoki Kambe <kambe at jprs.co.jp>
Date:   Thu Feb 3 20:40:51 2011 +0900

    [trac547] add mocked-up module "b10-stats-httpd" for HTTP/XML interface

commit b93a0c234eb66e98ab7b94cac1411335d70d12e3
Author: Naoki Kambe <kambe at jprs.co.jp>
Date:   Thu Feb 3 20:40:51 2011 +0900

    [trac547] removed unnecessary module "b10-stats_stub" including related files

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

Summary of changes:
 ChangeLog                                          |   26 +
 Makefile.am                                        |    1 +
 configure.ac                                       |    7 +-
 doc/Doxyfile                                       |    2 +-
 .../internal => ext/coroutine}/coroutine.h         |    0 
 src/bin/auth/query.cc                              |   59 +-
 src/bin/auth/tests/query_unittest.cc               |  175 +++-
 src/bin/bind10/bind10.py.in                        |   42 +-
 src/bin/msgq/tests/msgq_test.py                    |    2 +-
 src/bin/resolver/Makefile.am                       |    1 -
 src/bin/resolver/resolver.cc                       |    1 -
 src/bin/resolver/resolver.h                        |   11 +-
 src/bin/resolver/response_classifier.cc            |  259 -----
 src/bin/resolver/response_classifier.h             |  138 ---
 src/bin/resolver/response_scrubber.h               |    2 +-
 src/bin/resolver/tests/Makefile.am                 |    6 +-
 .../resolver/tests/response_classifier_unittest.cc |  494 --------
 .../resolver/tests/response_scrubber_unittest.cc   |    4 +-
 src/lib/Makefile.am                                |    2 +-
 src/lib/asiolink/Makefile.am                       |   28 +-
 src/lib/asiolink/asiolink.cc                       |  756 ------------
 src/lib/asiolink/asiolink.h                        |  644 +----------
 src/lib/asiolink/dns_answer.h                      |   73 ++
 src/lib/asiolink/dns_lookup.h                      |   81 ++
 src/lib/asiolink/dns_server.h                      |  152 +++
 src/lib/asiolink/dns_service.cc                    |  192 ++++
 src/lib/asiolink/dns_service.h                     |  106 ++
 src/lib/asiolink/internal/Makefile.am              |    1 -
 src/lib/asiolink/internal/tcpdns.h                 |  224 ----
 src/lib/asiolink/internal/tests/Makefile.am        |   37 -
 src/lib/asiolink/internal/tests/run_unittests.cc   |   21 -
 src/lib/asiolink/internal/tests/udpdns_unittest.cc |  145 ---
 src/lib/asiolink/internal/udpdns.h                 |  253 ----
 src/lib/asiolink/interval_timer.cc                 |  136 +++
 src/lib/asiolink/interval_timer.h                  |  133 +++
 src/lib/asiolink/{ioaddress.cc => io_address.cc}   |    0 
 src/lib/asiolink/{ioaddress.h => io_address.h}     |    0 
 src/lib/asiolink/io_endpoint.cc                    |   45 +
 src/lib/asiolink/io_endpoint.h                     |  122 ++
 src/lib/asiolink/io_message.h                      |  103 ++
 src/lib/asiolink/io_service.cc                     |   97 ++
 src/lib/asiolink/io_service.h                      |   77 ++
 src/lib/asiolink/io_socket.cc                      |   67 ++
 src/lib/asiolink/{iosocket.h => io_socket.h}       |    0 
 src/lib/asiolink/ioendpoint.cc                     |   43 -
 src/lib/asiolink/ioendpoint.h                      |  122 --
 src/lib/asiolink/iomessage.h                       |  103 --
 src/lib/asiolink/iosocket.cc                       |   67 --
 src/lib/asiolink/recursive_query.cc                |  457 ++++++++
 src/lib/asiolink/recursive_query.h                 |  113 ++
 src/lib/asiolink/simple_callback.h                 |   71 ++
 src/lib/asiolink/tcp_endpoint.h                    |   98 ++
 src/lib/asiolink/tcp_server.cc                     |  194 ++++
 src/lib/asiolink/tcp_server.h                      |  119 ++
 src/lib/asiolink/tcp_socket.h                      |   52 +
 src/lib/asiolink/tcpdns.cc                         |  194 ----
 src/lib/asiolink/tests/Makefile.am                 |   12 +-
 src/lib/asiolink/tests/asiolink_unittest.cc        | 1207 --------------------
 src/lib/asiolink/tests/interval_timer_unittest.cc  |  292 +++++
 src/lib/asiolink/tests/io_service_unittest.cc      |  115 ++
 src/lib/asiolink/tests/ioaddress_unittest.cc       |   57 +
 src/lib/asiolink/tests/ioendpoint_unittest.cc      |   67 ++
 src/lib/asiolink/tests/iosocket_unittest.cc        |   29 +
 src/lib/asiolink/tests/recursive_query_unittest.cc |  791 +++++++++++++
 src/lib/asiolink/tests/udp_query_unittest.cc       |  145 +++
 src/lib/asiolink/udp_endpoint.h                    |   89 ++
 src/lib/asiolink/udp_query.cc                      |  189 +++
 src/lib/asiolink/udp_query.h                       |   88 ++
 src/lib/asiolink/udp_server.cc                     |  284 +++++
 src/lib/asiolink/udp_server.h                      |  102 ++
 src/lib/asiolink/udp_socket.h                      |   48 +
 src/lib/asiolink/udpdns.cc                         |  432 -------
 src/lib/cache/Makefile.am                          |   33 +
 src/lib/cache/TODO                                 |   12 +
 src/lib/cache/cache_entry_key.cc                   |   44 +
 src/lib/cache/cache_entry_key.h                    |   56 +
 src/lib/cache/local_zone_data.cc                   |   58 +
 src/lib/cache/local_zone_data.h                    |   66 ++
 src/lib/cache/message_cache.cc                     |   97 ++
 src/lib/cache/message_cache.h                      |   95 ++
 src/lib/cache/message_entry.cc                     |  251 ++++
 src/lib/cache/message_entry.h                      |  183 +++
 src/lib/cache/resolver_cache.cc                    |  249 ++++
 src/lib/cache/resolver_cache.h                     |  331 ++++++
 src/lib/cache/rrset_cache.cc                       |  107 ++
 src/lib/cache/rrset_cache.h                        |  108 ++
 src/lib/cache/rrset_copy.cc                        |   40 +
 src/lib/cache/rrset_copy.h                         |   44 +
 src/lib/cache/rrset_entry.cc                       |   68 ++
 src/lib/cache/rrset_entry.h                        |  137 +++
 src/lib/cache/tests/Makefile.am                    |   57 +
 src/lib/cache/tests/cache_test_messagefromfile.h   |   40 +
 src/lib/cache/tests/cache_test_sectioncount.h      |   45 +
 src/lib/cache/tests/local_zone_data_unittest.cc    |   65 ++
 src/lib/cache/tests/message_cache_unittest.cc      |   98 ++
 src/lib/cache/tests/message_entry_unittest.cc      |  237 ++++
 src/lib/cache/tests/resolver_cache_unittest.cc     |  138 +++
 src/lib/cache/tests/rrset_cache_unittest.cc        |   84 ++
 src/lib/cache/tests/rrset_entry_unittest.cc        |  106 ++
 src/lib/cache/tests/run_unittests.cc               |   29 +
 .../tests/testdata/message_fromWire1               |    0 
 src/lib/cache/tests/testdata/message_fromWire2     |   22 +
 src/lib/cache/tests/testdata/message_fromWire3     |   76 ++
 src/lib/cache/tests/testdata/message_fromWire4     |   80 ++
 src/lib/cache/tests/testdata/message_fromWire5     |   36 +
 src/lib/cache/tests/testdata/message_fromWire6     |   40 +
 src/lib/datasrc/memory_datasrc.cc                  |  116 ++-
 src/lib/datasrc/rbtree.h                           |   92 ++-
 src/lib/datasrc/tests/memory_datasrc_unittest.cc   |  225 +++--
 src/lib/datasrc/tests/rbtree_unittest.cc           |   44 +-
 src/lib/dns/message.cc                             |   29 +
 src/lib/dns/message.h                              |   12 +
 src/lib/dns/rrset.h                                |   10 +-
 src/lib/dns/tests/message_unittest.cc              |  126 ++
 src/lib/log/tests/Makefile.am                      |    2 +
 src/lib/nsas/zone_entry.h                          |    3 +
 src/lib/resolve/Makefile.am                        |    3 +-
 src/lib/resolve/resolve.cc                         |   58 +
 src/lib/resolve/resolve.h                          |   40 +
 src/lib/resolve/response_classifier.cc             |  274 +++++
 src/lib/resolve/response_classifier.h              |  156 +++
 src/lib/resolve/tests/Makefile.am                  |    3 +
 src/lib/resolve/tests/resolve_unittest.cc          |  172 +++
 .../resolve/tests/response_classifier_unittest.cc  |  537 +++++++++
 124 files changed, 10005 insertions(+), 5334 deletions(-)
 rename {src/lib/asiolink/internal => ext/coroutine}/coroutine.h (100%)
 delete mode 100644 src/bin/resolver/response_classifier.cc
 delete mode 100644 src/bin/resolver/response_classifier.h
 delete mode 100644 src/bin/resolver/tests/response_classifier_unittest.cc
 delete mode 100644 src/lib/asiolink/asiolink.cc
 create mode 100644 src/lib/asiolink/dns_answer.h
 create mode 100644 src/lib/asiolink/dns_lookup.h
 create mode 100644 src/lib/asiolink/dns_server.h
 create mode 100644 src/lib/asiolink/dns_service.cc
 create mode 100644 src/lib/asiolink/dns_service.h
 delete mode 100644 src/lib/asiolink/internal/Makefile.am
 delete mode 100644 src/lib/asiolink/internal/tcpdns.h
 delete mode 100644 src/lib/asiolink/internal/tests/Makefile.am
 delete mode 100644 src/lib/asiolink/internal/tests/run_unittests.cc
 delete mode 100644 src/lib/asiolink/internal/tests/udpdns_unittest.cc
 delete mode 100644 src/lib/asiolink/internal/udpdns.h
 create mode 100644 src/lib/asiolink/interval_timer.cc
 create mode 100644 src/lib/asiolink/interval_timer.h
 rename src/lib/asiolink/{ioaddress.cc => io_address.cc} (100%)
 rename src/lib/asiolink/{ioaddress.h => io_address.h} (100%)
 create mode 100644 src/lib/asiolink/io_endpoint.cc
 create mode 100644 src/lib/asiolink/io_endpoint.h
 create mode 100644 src/lib/asiolink/io_message.h
 create mode 100644 src/lib/asiolink/io_service.cc
 create mode 100644 src/lib/asiolink/io_service.h
 create mode 100644 src/lib/asiolink/io_socket.cc
 rename src/lib/asiolink/{iosocket.h => io_socket.h} (100%)
 delete mode 100644 src/lib/asiolink/ioendpoint.cc
 delete mode 100644 src/lib/asiolink/ioendpoint.h
 delete mode 100644 src/lib/asiolink/iomessage.h
 delete mode 100644 src/lib/asiolink/iosocket.cc
 create mode 100644 src/lib/asiolink/recursive_query.cc
 create mode 100644 src/lib/asiolink/recursive_query.h
 create mode 100644 src/lib/asiolink/simple_callback.h
 create mode 100644 src/lib/asiolink/tcp_endpoint.h
 create mode 100644 src/lib/asiolink/tcp_server.cc
 create mode 100644 src/lib/asiolink/tcp_server.h
 create mode 100644 src/lib/asiolink/tcp_socket.h
 delete mode 100644 src/lib/asiolink/tcpdns.cc
 delete mode 100644 src/lib/asiolink/tests/asiolink_unittest.cc
 create mode 100644 src/lib/asiolink/tests/interval_timer_unittest.cc
 create mode 100644 src/lib/asiolink/tests/io_service_unittest.cc
 create mode 100644 src/lib/asiolink/tests/ioaddress_unittest.cc
 create mode 100644 src/lib/asiolink/tests/ioendpoint_unittest.cc
 create mode 100644 src/lib/asiolink/tests/iosocket_unittest.cc
 create mode 100644 src/lib/asiolink/tests/recursive_query_unittest.cc
 create mode 100644 src/lib/asiolink/tests/udp_query_unittest.cc
 create mode 100644 src/lib/asiolink/udp_endpoint.h
 create mode 100644 src/lib/asiolink/udp_query.cc
 create mode 100644 src/lib/asiolink/udp_query.h
 create mode 100644 src/lib/asiolink/udp_server.cc
 create mode 100644 src/lib/asiolink/udp_server.h
 create mode 100644 src/lib/asiolink/udp_socket.h
 delete mode 100644 src/lib/asiolink/udpdns.cc
 create mode 100644 src/lib/cache/Makefile.am
 create mode 100644 src/lib/cache/TODO
 create mode 100644 src/lib/cache/cache_entry_key.cc
 create mode 100644 src/lib/cache/cache_entry_key.h
 create mode 100644 src/lib/cache/local_zone_data.cc
 create mode 100644 src/lib/cache/local_zone_data.h
 create mode 100644 src/lib/cache/message_cache.cc
 create mode 100644 src/lib/cache/message_cache.h
 create mode 100644 src/lib/cache/message_entry.cc
 create mode 100644 src/lib/cache/message_entry.h
 create mode 100644 src/lib/cache/resolver_cache.cc
 create mode 100644 src/lib/cache/resolver_cache.h
 create mode 100644 src/lib/cache/rrset_cache.cc
 create mode 100644 src/lib/cache/rrset_cache.h
 create mode 100644 src/lib/cache/rrset_copy.cc
 create mode 100644 src/lib/cache/rrset_copy.h
 create mode 100644 src/lib/cache/rrset_entry.cc
 create mode 100644 src/lib/cache/rrset_entry.h
 create mode 100644 src/lib/cache/tests/Makefile.am
 create mode 100644 src/lib/cache/tests/cache_test_messagefromfile.h
 create mode 100644 src/lib/cache/tests/cache_test_sectioncount.h
 create mode 100644 src/lib/cache/tests/local_zone_data_unittest.cc
 create mode 100644 src/lib/cache/tests/message_cache_unittest.cc
 create mode 100644 src/lib/cache/tests/message_entry_unittest.cc
 create mode 100644 src/lib/cache/tests/resolver_cache_unittest.cc
 create mode 100644 src/lib/cache/tests/rrset_cache_unittest.cc
 create mode 100644 src/lib/cache/tests/rrset_entry_unittest.cc
 create mode 100644 src/lib/cache/tests/run_unittests.cc
 copy src/lib/{dns => cache}/tests/testdata/message_fromWire1 (100%)
 create mode 100644 src/lib/cache/tests/testdata/message_fromWire2
 create mode 100644 src/lib/cache/tests/testdata/message_fromWire3
 create mode 100644 src/lib/cache/tests/testdata/message_fromWire4
 create mode 100644 src/lib/cache/tests/testdata/message_fromWire5
 create mode 100644 src/lib/cache/tests/testdata/message_fromWire6
 create mode 100644 src/lib/resolve/resolve.cc
 create mode 100644 src/lib/resolve/response_classifier.cc
 create mode 100644 src/lib/resolve/response_classifier.h
 create mode 100644 src/lib/resolve/tests/resolve_unittest.cc
 create mode 100644 src/lib/resolve/tests/response_classifier_unittest.cc

-----------------------------------------------------------------------
diff --git a/ChangeLog b/ChangeLog
index ae4e72a..9cf9b3b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,29 @@
+  169.  [func]      zhang likun, jelte
+	Added a basic implementation for a resolver cache (though not
+	used yet).
+	(Trac #449, git 8aa3b2246ae095bbe7f855fd11656ae3bdb98986)
+
+  168.  [bug]       vorner
+	Boss no longer has the -f argument, which was undocumented and stayed as
+	a relict of previous versions, currently causing only strange behaviour.
+	(Trac #572, git 17f237478961005707d649a661cc72a4a0d612d4)
+
+  167.  [bug]           naokikambe
+	Fixed failure of termination of msgq_test.py with python3 coverage(3.3.1)
+	(Trac #573, git 0e6a18e12f61cc482e07078776234f32605312e5)
+
+  166.  [func]      jelte
+	The resolver now sends back a SERVFAIL when there is a client
+	timeout (timeout_client config setting), but it will not stop
+	resolving (until there is a lookup timeout or a result).
+	(Trac #497 and #489, git af0e5cd93bebb27cb5c4457f7759d12c8bf953a6)
+
+  165.  [func]      jelte
+	The resolver now handles CNAMEs, it will follow them, and include
+	them in the answer. The maximum length of CNAME chains that is
+	supported is 16.
+	(Trac #497, git af0e5cd93bebb27cb5c4457f7759d12c8bf953a6)
+
   164.  [bug]           y-aharen
 	IntervalTimer: Modified the interface to accept interval in
 	milliseconds. It shortens the time of the tests of IntervalTimer.
diff --git a/Makefile.am b/Makefile.am
index 93a7498..68a41d6 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -282,3 +282,4 @@ EXTRA_DIST += ext/asio/asio/is_write_buffered.hpp
 EXTRA_DIST += ext/asio/asio/buffered_read_stream_fwd.hpp
 EXTRA_DIST += ext/asio/asio/socket_acceptor_service.hpp
 EXTRA_DIST += ext/asio/asio.hpp
+EXTRA_DIST += ext/coroutine/coroutine.h
diff --git a/configure.ac b/configure.ac
index 3907864..6453022 100644
--- a/configure.ac
+++ b/configure.ac
@@ -573,6 +573,9 @@ AC_SUBST(MULTITHREADING_FLAG)
 #
 CPPFLAGS="$CPPFLAGS -I\$(top_srcdir)/ext/asio"
 #
+# Use our 'coroutine' header from ext
+CPPFLAGS="$CPPFLAGS -I\$(top_srcdir)/ext/coroutine"
+#
 # Disable threads: Currently we don't use them.
 CPPFLAGS="$CPPFLAGS -DASIO_DISABLE_THREADS=1"
 #
@@ -666,8 +669,6 @@ AC_CONFIG_FILES([Makefile
                  src/lib/Makefile
                  src/lib/asiolink/Makefile
                  src/lib/asiolink/tests/Makefile
-                 src/lib/asiolink/internal/Makefile
-                 src/lib/asiolink/internal/tests/Makefile
                  src/lib/bench/Makefile
                  src/lib/bench/example/Makefile
                  src/lib/bench/tests/Makefile
@@ -711,6 +712,8 @@ AC_CONFIG_FILES([Makefile
                  src/lib/testutils/testdata/Makefile
                  src/lib/nsas/Makefile
                  src/lib/nsas/tests/Makefile
+                 src/lib/cache/Makefile
+                 src/lib/cache/tests/Makefile
                ])
 AC_OUTPUT([doc/version.ent
            src/bin/cfgmgr/b10-cfgmgr.py
diff --git a/doc/Doxyfile b/doc/Doxyfile
index 6ab2001..34ec3d8 100644
--- a/doc/Doxyfile
+++ b/doc/Doxyfile
@@ -568,7 +568,7 @@ WARN_LOGFILE           =
 # directories like "/usr/src/myproject". Separate the files or directories
 # with spaces.
 
-INPUT                  = ../src/lib/cc ../src/lib/config ../src/lib/dns ../src/lib/exceptions ../src/lib/datasrc ../src/bin/auth ../src/bin/resolver ../src/lib/bench ../src/lib/log ../src/lib/asiolink/ ../src/lib/nsas ../src/lib/testutils
+INPUT                  = ../src/lib/cc ../src/lib/config ../src/lib/dns ../src/lib/exceptions ../src/lib/datasrc ../src/bin/auth ../src/bin/resolver ../src/lib/bench ../src/lib/log ../src/lib/asiolink/ ../src/lib/nsas ../src/lib/testutils ../src/lib/cache
 
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
diff --git a/ext/coroutine/coroutine.h b/ext/coroutine/coroutine.h
new file mode 100644
index 0000000..985888b
--- /dev/null
+++ b/ext/coroutine/coroutine.h
@@ -0,0 +1,133 @@
+//
+// coroutine.h
+// ~~~~~~~~~~~
+//
+// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef COROUTINE_HPP
+#define COROUTINE_HPP
+
+
+// \brief Coroutine object
+//
+// A coroutine object maintains the state of a re-enterable routine.  It
+// is assignable and copy-constructable, and can be used as a base class
+// for a class that uses it, or as a data member.  The copy overhead is
+// a single int.
+//
+// A reenterable function contains a CORO_REENTER (coroutine)  { ... }
+// block.  Whenever an asychrnonous operation is initiated within the
+// routine, the function is provided as the handler object.  (The simplest
+// way to do this is to have the reenterable function be the operator()
+// member for the coroutine object itself.)   For example:
+// 
+//     CORO_YIELD socket->async_read_some(buffer, *this);
+//
+// The CORO_YIELD keyword updates the current status of the coroutine to
+// indicate the line number currently being executed.  The
+// async_read_some() call is initiated, with a copy of the updated
+// corotutine as its handler object, and the current coroutine exits.  When
+// the async_read_some() call finishes, the copied coroutine will be
+// called, and will resume processing exactly where the original one left
+// off--right after asynchronous call.  This allows asynchronous I/O
+// routines to be written with a logical flow, step following step, rather
+// than as a linked chain of separate handler functions.
+//
+// When necessary, a coroutine can fork itself using the CORO_FORK keyword.
+// This updates the status of the coroutine and makes a copy.  The copy can
+// then be called directly or posted to the ASIO service queue so that both
+// coroutines will continue forward, one "parent" and one "child".  The
+// is_parent() and is_child() methods indicate which is which.
+//
+// The CORO_REENTER, CORO_YIELD and CORO_FORK keywords are implemented
+// via preprocessor macros.  The CORO_REENTER block is actually a large,
+// complex switch statement.  Because of this, inline variable declaration
+// is impossible within CORO_REENTER unless it is done in a subsidiary
+// scope--and if it is, that scope cannot contain CORO_YIELD or CORO_FORK
+// keywords.
+//
+// Because coroutines are frequently copied, it is best to minimize copy
+// overhead by limiting the size of data members in derived classes.
+//
+// It should be noted that when a coroutine falls out of scope its memory
+// is reclaimed, even though it may be scheduled to resume when an
+// asynchronous operation completes.  Any shared_ptr<> objects declared in
+// the coroutine may be destroyed if their reference count drops to zero,
+// in which case the coroutine will have serious problems once it resumes.
+// One solution so this is to have the space that will be used by a
+// coroutine pre-allocated and stored on a free list; a new coroutine can
+// fetch the block of space off a free list, place a shared pointer to it
+// on an "in use" list, and carry on.  The reference in the "in use" list
+// would prevent the data from being destroyed.
+class coroutine
+{
+public:
+  coroutine() : value_(0) {}
+  virtual ~coroutine() {}
+  bool is_child() const { return value_ < 0; }
+  bool is_parent() const { return !is_child(); }
+  bool is_complete() const { return value_ == -1; }
+  int get_value() const { return value_; }
+private:
+  friend class coroutine_ref;
+  int value_;
+};
+
+class coroutine_ref
+{
+public:
+  coroutine_ref(coroutine& c) : value_(c.value_), modified_(false) {}
+  coroutine_ref(coroutine* c) : value_(c->value_), modified_(false) {}
+  ~coroutine_ref() { if (!modified_) value_ = -1; }
+  operator int() const { return value_; }
+  int& operator=(int v) { modified_ = true; return value_ = v; }
+private:
+  void operator=(const coroutine_ref&);
+  int& value_;
+  bool modified_;
+};
+
+#define CORO_REENTER(c) \
+  switch (coroutine_ref _coro_value = c) \
+    case -1: if (_coro_value) \
+    { \
+      goto terminate_coroutine; \
+      terminate_coroutine: \
+      _coro_value = -1; \
+      goto bail_out_of_coroutine; \
+      bail_out_of_coroutine: \
+      break; \
+    } \
+    else case 0:
+
+#define CORO_YIELD \
+  for (_coro_value = __LINE__;;) \
+    if (_coro_value == 0) \
+    { \
+      case __LINE__: ; \
+      break; \
+    } \
+    else \
+      switch (_coro_value ? 0 : 1) \
+        for (;;) \
+          case -1: if (_coro_value) \
+            goto terminate_coroutine; \
+          else for (;;) \
+            case 1: if (_coro_value) \
+              goto bail_out_of_coroutine; \
+            else case 0:
+
+#define CORO_FORK \
+  for (_coro_value = -__LINE__;; _coro_value = __LINE__) \
+    if (_coro_value == __LINE__) \
+    { \
+      case -__LINE__: ; \
+      break; \
+    } \
+    else
+#endif // COROUTINE_HPP
+
diff --git a/src/bin/auth/query.cc b/src/bin/auth/query.cc
index e85c31a..e936c97 100644
--- a/src/bin/auth/query.cc
+++ b/src/bin/auth/query.cc
@@ -141,13 +141,56 @@ Query::process() const {
 
     // Found a zone which is the nearest ancestor to QNAME, set the AA bit
     response_.setHeaderFlag(Message::HEADERFLAG_AA);
+    response_.setRcode(Rcode::NOERROR());
     while (keep_doing) {
         keep_doing = false;
         std::auto_ptr<RRsetList> target(qtype_is_any ? new RRsetList : NULL);
-        Zone::FindResult db_result =
-            result.zone->find(qname_, qtype_, target.get());
+        const Zone::FindResult db_result(result.zone->find(qname_, qtype_,
+            target.get()));
 
         switch (db_result.code) {
+            case Zone::DNAME: {
+                // First, put the dname into the answer
+                response_.addRRset(Message::SECTION_ANSWER,
+                    boost::const_pointer_cast<RRset>(db_result.rrset));
+                /*
+                 * Empty DNAME should never get in, as it is impossible to
+                 * create one in master file.
+                 *
+                 * FIXME: Other way to prevent this should be done
+                 */
+                assert(db_result.rrset->getRdataCount() > 0);
+                // Get the data of DNAME
+                const rdata::generic::DNAME& dname(
+                    dynamic_cast<const rdata::generic::DNAME&>(
+                    db_result.rrset->getRdataIterator()->getCurrent()));
+                // The yet unmatched prefix dname
+                const Name prefix(qname_.split(0, qname_.getLabelCount() -
+                    db_result.rrset->getName().getLabelCount()));
+                // If we put it together, will it be too long?
+                // (The prefix contains trailing ., which will be removed
+                if (prefix.getLength() - Name::ROOT_NAME().getLength() +
+                    dname.getDname().getLength() > Name::MAX_WIRE) {
+                    /*
+                     * In case the synthesized name is too long, section 4.1
+                     * of RFC 2672 mandates we return YXDOMAIN.
+                     */
+                    response_.setRcode(Rcode::YXDOMAIN());
+                    return;
+                }
+                // The new CNAME we are creating (it will be unsigned even
+                // with DNSSEC, the DNAME is signed and it can be validated
+                // by that)
+                RRsetPtr cname(new RRset(qname_, db_result.rrset->getClass(),
+                    RRType::CNAME(), db_result.rrset->getTTL()));
+                // Construct the new target by replacing the end
+                cname->addRdata(rdata::generic::CNAME(qname_.split(0,
+                    qname_.getLabelCount() -
+                    db_result.rrset->getName().getLabelCount()).
+                    concatenate(dname.getDname())));
+                response_.addRRset(Message::SECTION_ANSWER, cname);
+                break;
+            }
             case Zone::CNAME:
                 /*
                  * We don't do chaining yet. Therefore handling a CNAME is
@@ -155,10 +198,13 @@ Query::process() const {
                  * what we expected. It means no exceptions in ANY or NS
                  * on the origin (though CNAME in origin is probably
                  * forbidden anyway).
+                 *
+                 * So, just put it there.
                  */
-                // No break; here, fall trough.
+                response_.addRRset(Message::SECTION_ANSWER,
+                    boost::const_pointer_cast<RRset>(db_result.rrset));
+                break;
             case Zone::SUCCESS:
-                response_.setRcode(Rcode::NOERROR());
                 if (qtype_is_any) {
                     // If quety type is ANY, insert all RRs under the domain
                     // into answer section.
@@ -184,7 +230,6 @@ Query::process() const {
                 break;
             case Zone::DELEGATION:
                 response_.setHeaderFlag(Message::HEADERFLAG_AA, false);
-                response_.setRcode(Rcode::NOERROR());
                 response_.addRRset(Message::SECTION_AUTHORITY,
                     boost::const_pointer_cast<RRset>(db_result.rrset));
                 getAdditional(*result.zone, *db_result.rrset);
@@ -196,12 +241,8 @@ Query::process() const {
                 break;
             case Zone::NXRRSET:
                 // Just empty answer with SOA in authority section
-                response_.setRcode(Rcode::NOERROR());
                 putSOA(*result.zone);
                 break;
-            case Zone::DNAME:
-                // TODO : replace qname, continue lookup
-                break;
         }
     }
 }
diff --git a/src/bin/auth/tests/query_unittest.cc b/src/bin/auth/tests/query_unittest.cc
index 3bf4142..2d3cf03 100644
--- a/src/bin/auth/tests/query_unittest.cc
+++ b/src/bin/auth/tests/query_unittest.cc
@@ -75,6 +75,17 @@ const char* const cname_nxdom_txt =
 // CNAME Leading out of zone
 const char* const cname_out_txt =
     "cnameout.example.com. 3600 IN CNAME www.example.org.\n";
+// The DNAME to do tests against
+const char* const dname_txt =
+    "dname.example.com. 3600 IN DNAME "
+    "somethinglong.dnametarget.example.com.\n";
+// Some data at the dname node (allowed by RFC 2672)
+const char* const dname_a_txt =
+    "dname.example.com. 3600 IN A 192.0.2.5\n";
+// This is not inside the zone, this is created at runtime
+const char* const synthetized_cname_txt =
+    "www.dname.example.com. 3600 IN CNAME "
+    "www.somethinglong.dnametarget.example.com.\n";
 // The rest of data won't be referenced from the test cases.
 const char* const other_zone_rrs =
     "cnamemailer.example.com. 3600 IN CNAME www.example.com.\n"
@@ -88,13 +99,16 @@ const char* const other_zone_rrs =
 // behavior.
 // For simplicity, most names are assumed to be "in zone"; there's only
 // one zone cut at the point of name "delegation.example.com".
-// It doesn't handle empty non terminal nodes (if we need to test such cases
-// find() should have specialized code for it).
+// Another special name is "dname.example.com".  Query names under this name
+// will result in DNAME.
+// This mock zone doesn't handle empty non terminal nodes (if we need to test
+// such cases find() should have specialized code for it).
 class MockZone : public Zone {
 public:
     MockZone() :
         origin_(Name("example.com")),
         delegation_name_("delegation.example.com"),
+        dname_name_("dname.example.com"),
         has_SOA_(true),
         has_apex_NS_(true),
         rrclass_(RRClass::IN())
@@ -102,7 +116,8 @@ public:
         stringstream zone_stream;
         zone_stream << soa_txt << zone_ns_txt << ns_addrs_txt <<
             delegation_txt << mx_txt << www_a_txt << cname_txt <<
-            cname_nxdom_txt << cname_out_txt << other_zone_rrs;
+            cname_nxdom_txt << cname_out_txt << dname_txt << dname_a_txt <<
+            other_zone_rrs;
 
         masterLoad(zone_stream, origin_, rrclass_,
                    boost::bind(&MockZone::loadRRset, this, _1));
@@ -131,14 +146,20 @@ private:
         if (rrset->getName() == delegation_name_ &&
             rrset->getType() == RRType::NS()) {
             delegation_rrset_ = rrset;
+        } else if (rrset->getName() == dname_name_ &&
+            rrset->getType() == RRType::DNAME()) {
+            dname_rrset_ = rrset;
         }
     }
 
     const Name origin_;
+    // Names where we delegate somewhere else
     const Name delegation_name_;
+    const Name dname_name_;
     bool has_SOA_;
     bool has_apex_NS_;
     ConstRRsetPtr delegation_rrset_;
+    ConstRRsetPtr dname_rrset_;
     const RRClass rrclass_;
 };
 
@@ -160,6 +181,10 @@ MockZone::find(const Name& name, const RRType& type,
          name.compare(delegation_name_).getRelation() ==
          NameComparisonResult::SUBDOMAIN)) {
         return (FindResult(DELEGATION, delegation_rrset_));
+    // And under DNAME
+    } else if (name.compare(dname_name_).getRelation() ==
+        NameComparisonResult::SUBDOMAIN) {
+        return (FindResult(DNAME, dname_rrset_));
     }
 
     // normal cases.  names are searched for only per exact-match basis
@@ -176,8 +201,7 @@ MockZone::find(const Name& name, const RRType& type,
         // If not found but we have a target, fill it with all RRsets here
         if (!found_domain->second.empty() && target != NULL) {
             for (found_rrset = found_domain->second.begin();
-                 found_rrset != found_domain->second.end(); found_rrset++)
-            {
+                 found_rrset != found_domain->second.end(); found_rrset++) {
                 // Insert RRs under the domain name into target
                 target->addRRset(
                     boost::const_pointer_cast<RRset>(found_rrset->second));
@@ -443,8 +467,8 @@ TEST_F(QueryTest, CNAME) {
     Query(memory_datasrc, Name("cname.example.com"), RRType::A(),
         response).process();
 
-    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
-        cname_txt, zone_ns_txt, ns_addrs_txt);
+    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 0, 0,
+        cname_txt, NULL, NULL);
 }
 
 TEST_F(QueryTest, explicitCNAME) {
@@ -465,8 +489,8 @@ TEST_F(QueryTest, CNAME_NX_RRSET) {
     Query(memory_datasrc, Name("cname.example.com"), RRType::TXT(),
         response).process();
 
-    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
-        cname_txt, zone_ns_txt, ns_addrs_txt);
+    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 0, 0,
+        cname_txt, NULL, NULL);
 }
 
 TEST_F(QueryTest, explicitCNAME_NX_RRSET) {
@@ -488,8 +512,8 @@ TEST_F(QueryTest, CNAME_NX_DOMAIN) {
     Query(memory_datasrc, Name("cnamenxdom.example.com"), RRType::A(),
         response).process();
 
-    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
-        cname_nxdom_txt, zone_ns_txt, ns_addrs_txt);
+    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 0, 0,
+        cname_nxdom_txt, NULL, NULL);
 }
 
 TEST_F(QueryTest, explicitCNAME_NX_DOMAIN) {
@@ -513,8 +537,8 @@ TEST_F(QueryTest, CNAME_OUT) {
     Query(memory_datasrc, Name("cnameout.example.com"), RRType::A(),
         response).process();
 
-    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
-        cname_out_txt, zone_ns_txt, ns_addrs_txt);
+    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 0, 0,
+        cname_out_txt, NULL, NULL);
 }
 
 TEST_F(QueryTest, explicitCNAME_OUT) {
@@ -526,4 +550,129 @@ TEST_F(QueryTest, explicitCNAME_OUT) {
         cname_out_txt, zone_ns_txt, ns_addrs_txt);
 }
 
+/*
+ * Test a query under a domain with DNAME. We should get a synthetized CNAME
+ * as well as the DNAME.
+ *
+ * TODO: Once we have CNAME chaining, check it works with synthetized CNAMEs
+ * as well. This includes tests pointing inside the zone, outside the zone,
+ * pointing to NXRRSET and NXDOMAIN cases (similarly as with CNAME).
+ */
+TEST_F(QueryTest, DNAME) {
+    Query(memory_datasrc, Name("www.dname.example.com"), RRType::A(),
+        response).process();
+
+    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 0, 0,
+        (string(dname_txt) + synthetized_cname_txt).c_str(),
+        NULL, NULL);
+}
+
+/*
+ * Ask an ANY query below a DNAME. Should return the DNAME and synthetized
+ * CNAME.
+ *
+ * ANY is handled specially sometimes. We check it is not the case with
+ * DNAME.
+ */
+TEST_F(QueryTest, DNAME_ANY) {
+    Query(memory_datasrc, Name("www.dname.example.com"), RRType::ANY(),
+        response).process();
+
+    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 0, 0,
+        (string(dname_txt) + synthetized_cname_txt).c_str(), NULL, NULL);
+}
+
+// Test when we ask for DNAME explicitly, it does no synthetizing.
+TEST_F(QueryTest, explicitDNAME) {
+    Query(memory_datasrc, Name("dname.example.com"), RRType::DNAME(),
+        response).process();
+
+    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
+        dname_txt, zone_ns_txt, ns_addrs_txt);
+}
+
+/*
+ * Request a RRset at the domain with DNAME. It should not synthetize
+ * the CNAME, it should return the RRset.
+ */
+TEST_F(QueryTest, DNAME_A) {
+    Query(memory_datasrc, Name("dname.example.com"), RRType::A(),
+        response).process();
+
+    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
+        dname_a_txt, zone_ns_txt, ns_addrs_txt);
+}
+
+/*
+ * Request a RRset at the domain with DNAME that is not there (NXRRSET).
+ * It should not synthetize the CNAME.
+ */
+TEST_F(QueryTest, DNAME_NX_RRSET) {
+    EXPECT_NO_THROW(Query(memory_datasrc, Name("dname.example.com"),
+        RRType::TXT(), response).process());
+
+    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 1, 0,
+        NULL, soa_txt, NULL, mock_zone->getOrigin());
+}
+
+/*
+ * Constructing the CNAME will result in a name that is too long. This,
+ * however, should not throw (and crash the server), but respond with
+ * YXDOMAIN.
+ */
+TEST_F(QueryTest, LongDNAME) {
+    // A name that is as long as it can be
+    Name longname(
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
+        "dname.example.com.");
+    EXPECT_NO_THROW(Query(memory_datasrc, longname, RRType::A(),
+        response).process());
+
+    responseCheck(response, Rcode::YXDOMAIN(), AA_FLAG, 1, 0, 0,
+        dname_txt, NULL, NULL);
+}
+
+/*
+ * Constructing the CNAME will result in a name of maximal length.
+ * This tests that we don't reject valid one by some kind of off by
+ * one mistake.
+ */
+TEST_F(QueryTest, MaxLenDNAME) {
+    Name longname(
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
+        "dname.example.com.");
+    EXPECT_NO_THROW(Query(memory_datasrc, longname, RRType::A(),
+        response).process());
+
+    // Check the answer is OK
+    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 0, 0,
+        NULL, NULL, NULL);
+
+    // Check that the CNAME has the maximal length.
+    bool ok(false);
+    for (RRsetIterator i(response.beginSection(Message::SECTION_ANSWER));
+        i != response.endSection(Message::SECTION_ANSWER); ++ i) {
+        if ((*i)->getType() == RRType::CNAME()) {
+            ok = true;
+            RdataIteratorPtr ci((*i)->getRdataIterator());
+            ASSERT_FALSE(ci->isLast()) << "The CNAME is empty";
+            /*
+             * Does anybody have a clue why, if the Name::MAX_WIRE is put
+             * directly inside ASSERT_EQ, it fails to link and complains
+             * it is unresolved external?
+             */
+            const size_t max_len(Name::MAX_WIRE);
+            ASSERT_EQ(max_len, dynamic_cast<const rdata::generic::CNAME&>(
+                ci->getCurrent()).getCname().getLength());
+        }
+    }
+    EXPECT_TRUE(ok) << "The synthetized CNAME not found";
+}
+
 }
diff --git a/src/bin/bind10/bind10.py.in b/src/bin/bind10/bind10.py.in
index 8b80b50..ddecd27 100644
--- a/src/bin/bind10/bind10.py.in
+++ b/src/bin/bind10/bind10.py.in
@@ -195,8 +195,7 @@ class BoB:
     """Boss of BIND class."""
     
     def __init__(self, msgq_socket_file=None, dns_port=5300, address=None,
-                 forward=None, nocache=False, verbose=False, setuid=None,
-                 username=None):
+                 nocache=False, verbose=False, setuid=None, username=None):
         """
             Initialize the Boss of BIND. This is a singleton (only one can run).
         
@@ -206,11 +205,6 @@ class BoB:
         """
         self.address = address
         self.dns_port = dns_port
-        self.forward = forward
-        if forward:
-            self.resolver = True
-        else:
-            self.resolver = False
         self.cc_session = None
         self.ccs = None
         self.cfg_start_auth = True
@@ -422,26 +416,19 @@ class BoB:
         """
             Start the Authoritative server
         """
-        # XXX: this must be read from the configuration manager in the future
-        if self.resolver:
-            dns_prog = 'b10-resolver'
-        else:
-            dns_prog = 'b10-auth'
-        dnsargs = [dns_prog]
-        if not self.resolver:
-            # The resolver uses configuration manager for these
-            dnsargs += ['-p', str(self.dns_port)]
-            if self.address:
-                dnsargs += ['-a', str(self.address)]
-            if self.nocache:
-                dnsargs += ['-n']
+        authargs = ['b10-auth']
+        authargs += ['-p', str(self.dns_port)]
+        if self.address:
+            authargs += ['-a', str(self.address)]
+        if self.nocache:
+            authargs += ['-n']
         if self.uid:
-            dnsargs += ['-u', str(self.uid)]
+            authargs += ['-u', str(self.uid)]
         if self.verbose:
-            dnsargs += ['-v']
+            authargs += ['-v']
 
         # ... and start
-        self.start_process("b10-auth", dnsargs, c_channel_env,
+        self.start_process("b10-auth", authargs, c_channel_env,
             self.dns_port, self.address)
 
     def start_resolver(self, c_channel_env):
@@ -744,8 +731,6 @@ def check_addr(option, opt_str, value, parser):
     try:
         if opt_str in ['-a', '--address']:
             parser.values.address = isc.net.parse.addr_parse(value)
-        elif opt_str in ['-f', '--forward']:
-            parser.values.forward = isc.net.parse.addr_parse(value)
         else:
             raise OptionValueError("Unknown option " + opt_str)
     except ValueError:
@@ -766,9 +751,6 @@ def main():
     parser.add_option("-a", "--address", dest="address", type="string",
                       action="callback", callback=check_addr, default=None,
                       help="address the DNS server will use (default: listen on all addresses)")
-    parser.add_option("-f", "--forward", dest="forward", type="string",
-                      action="callback", callback=check_addr, default=None,
-                      help="nameserver to which DNS queries should be forwarded")
     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")
@@ -838,8 +820,8 @@ def main():
 
     # Go bob!
     boss_of_bind = BoB(options.msgq_socket_file, options.dns_port,
-                       options.address, options.forward, options.nocache,
-                       options.verbose, setuid, username)
+                       options.address, options.nocache, options.verbose,
+                       setuid, username)
     startup_result = boss_of_bind.startup()
     if startup_result:
         sys.stderr.write("[bind10] Error on startup: %s\n" % startup_result)
diff --git a/src/bin/msgq/tests/msgq_test.py b/src/bin/msgq/tests/msgq_test.py
index efae151..59fcf41 100644
--- a/src/bin/msgq/tests/msgq_test.py
+++ b/src/bin/msgq/tests/msgq_test.py
@@ -132,7 +132,7 @@ class SendNonblock(unittest.TestCase):
             task()
             # If we got here, then everything worked well and in time
             # In that case, we terminate successfully
-            sys.exit()
+            sys.exit(0)	# needs exit code
         else:
             (pid, status) = os.waitpid(task_pid, 0)
             self.assertEqual(0, status,
diff --git a/src/bin/resolver/Makefile.am b/src/bin/resolver/Makefile.am
index 0200675..9e86ddd 100644
--- a/src/bin/resolver/Makefile.am
+++ b/src/bin/resolver/Makefile.am
@@ -37,7 +37,6 @@ spec_config.h: spec_config.h.pre
 BUILT_SOURCES = spec_config.h 
 pkglibexec_PROGRAMS = b10-resolver
 b10_resolver_SOURCES = resolver.cc resolver.h
-b10_resolver_SOURCES += response_classifier.cc response_classifier.h
 b10_resolver_SOURCES += response_scrubber.cc response_scrubber.h
 b10_resolver_SOURCES += $(top_builddir)/src/bin/auth/change_user.h
 b10_resolver_SOURCES += $(top_builddir)/src/bin/auth/common.h
diff --git a/src/bin/resolver/resolver.cc b/src/bin/resolver/resolver.cc
index 9e9bbd4..dfd84a6 100644
--- a/src/bin/resolver/resolver.cc
+++ b/src/bin/resolver/resolver.cc
@@ -21,7 +21,6 @@
 #include <cassert>
 
 #include <asiolink/asiolink.h>
-#include <asiolink/ioaddress.h>
 
 #include <boost/foreach.hpp>
 #include <boost/lexical_cast.hpp>
diff --git a/src/bin/resolver/resolver.h b/src/bin/resolver/resolver.h
index 3a5219f..2ae8079 100644
--- a/src/bin/resolver/resolver.h
+++ b/src/bin/resolver/resolver.h
@@ -65,7 +65,10 @@ public:
     /// send the reply.
     ///
     /// \param io_message The raw message received
-    /// \param message Pointer to the \c Message object
+    /// \param query_message Pointer to the query Message object we
+    /// received from the client
+    /// \param answer_message Pointer to the anwer Message object we
+    /// shall return to the client
     /// \param buffer Pointer to an \c OutputBuffer for the resposne
     /// \param server Pointer to the \c DNSServer
     void processMessage(const asiolink::IOMessage& io_message,
@@ -146,7 +149,11 @@ public:
      * \short Set options related to timeouts.
      *
      * This sets the time of timeout and number of retries.
-     * \param timeout The time in milliseconds. The value -1 disables timeouts.
+     * \param query_timeout The timeout we use for queries we send
+     * \param client_timeout The timeout at which point we send back a
+     * SERVFAIL (while continuing to resolve the query)
+     * \param lookup_timeout The timeout at which point we give up and
+     * stop.
      * \param retries The number of retries (0 means try the first time only,
      *     do not retry).
      */
diff --git a/src/bin/resolver/response_classifier.cc b/src/bin/resolver/response_classifier.cc
deleted file mode 100644
index 2d21407..0000000
--- a/src/bin/resolver/response_classifier.cc
+++ /dev/null
@@ -1,259 +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.
-
-// $Id$
-
-#include <cstddef>
-#include <vector>
-
-#include <resolver/response_classifier.h>
-#include <dns/name.h>
-#include <dns/opcode.h>
-#include <dns/rcode.h>
-#include <dns/rrset.h>
-
-using namespace isc::dns;
-using namespace std;
-
-// Classify the response in the "message" object.
-
-ResponseClassifier::Category ResponseClassifier::classify(
-    const Question& question, const MessagePtr& message, bool tcignore)
-{
-    // Check header bits
-    if (!message->getHeaderFlag(Message::HEADERFLAG_QR)) {
-        return (NOTRESPONSE);   // Query-response bit not set in the response
-    }
-
-    // We only recognise responses to queries here
-    if (message->getOpcode() != Opcode::QUERY()) {
-        return (OPCODE);
-    }
-
-    // Apparently have a response.  There must be a single question in it...
-    const vector<QuestionPtr> msgquestion(message->beginQuestion(),
-            message->endQuestion());
-    if (msgquestion.size() != 1) {
-        return (NOTONEQUEST); // Not one question in response question section
-    }
-
-    // ... and the question should be equal to the question given.
-    // XXX: This means that "question" may not be the question sent by the
-    // client.  In the case of a CNAME response, the qname of subsequent
-    // questions needs to be altered.
-    if (question != *(msgquestion[0])) {
-        return (MISMATQUEST);
-    }
-
-    // Check for Rcode-related errors.
-    const Rcode& rcode = message->getRcode();
-    if (rcode != Rcode::NOERROR()) {
-        if (rcode == Rcode::NXDOMAIN()) {
-
-            // No such domain.  According to RFC2308, the domain referred to by
-            // the QNAME does not exist, although there may be a CNAME in the
-            // answer section and there may be an SOA and/or NS RRs in the
-            // authority section (ignoring any DNSSEC RRs for now).
-            //
-            // Note the "may".  There may not be anything.  Also, note that if
-            // there is a CNAME in the answer section, the authoritative server
-            // has verified that the name given in the CNAME's RDATA field does
-            // not exist. And that if a CNAME is returned in the answer, then
-            // the QNAME of the RRs in the authority section will refer to the
-            // authority for the CNAME's RDATA and not to the original question.
-            //
-            // Without doing further classification, it is sufficient to say
-            // that if an NXDOMAIN is received, there was no translation of the
-            // QNAME available.
-            return (NXDOMAIN);  // Received NXDOMAIN from parent.
-
-        } else {
-
-            // Not NXDOMAIN but not NOERROR either.  Must be an RCODE-related
-            // error.
-            return (RCODE);
-        }
-    }
-
-    // All seems OK and we can start looking at the content.  However, one
-    // more header check remains - was the response truncated?  If so, we'll
-    // probably want to re-query over TCP.  However, in some circumstances we
-    // might want to go with what we have.  So give the caller the option of
-    // ignoring the TC bit.
-    if (message->getHeaderFlag(Message::HEADERFLAG_TC) && (!tcignore)) {
-        return (TRUNCATED);
-    }
-
-    // By the time we get here, we're assured that the packet format is correct.
-    // We now need to decide as to whether it is an answer, a CNAME, or a
-    // referral.  For this, we need to inspect the contents of the answer
-    // and authority sections.
-    const vector<RRsetPtr> answer(
-            message->beginSection(Message::SECTION_ANSWER),
-            message->endSection(Message::SECTION_ANSWER)
-            );
-    const vector<RRsetPtr> authority(
-            message->beginSection(Message::SECTION_AUTHORITY),
-            message->endSection(Message::SECTION_AUTHORITY)
-            );
-
-    // If there is nothing in the answer section, it is a referral - unless
-    // there is nothing in the authority section
-    if (answer.empty()) {
-        if (authority.empty()) {
-            return (EMPTY);
-        } else {
-            return (REFERRAL);
-        }
-    }
-
-    // Look at two cases - one RRset in the answer and multiple RRsets in
-    // the answer.
-    if (answer.size() == 1) {
-
-        // Does the name and class of the answer match that of the question?
-        if ((answer[0]->getName() == question.getName()) &&
-            (answer[0]->getClass() == question.getClass())) {
-
-            // It does.  How about the type of the response?  The response
-            // is an answer if the type matches that of the question, or if the
-            // question was for type ANY.  It is a CNAME reply if the answer
-            // type is CNAME.  And it is an error for anything else.
-            if ((answer[0]->getType() == question.getType()) ||
-                (question.getType() == RRType::ANY())) {
-                return (ANSWER);
-            } else if (answer[0]->getType() == RRType::CNAME()) {
-                return (CNAME);
-            } else {
-                return (INVTYPE);
-            }
-        }
-        else {
-
-            // Either the name and/or class of the reply don't match that of
-            // the question.
-            return (INVNAMCLASS);
-        }
-    }
-
-    // There are multiple RRsets in the answer. They should all have the same
-    // QCLASS, else there is some error in the response.
-    for (int i = 1; i < answer.size(); ++i) {
-        if (answer[0]->getClass() != answer[i]->getClass()) {
-            return (MULTICLASS);
-        }
-    }
-
-    // If the request type was ANY and they all have the same QNAME, we have
-    // an answer.  But if they don't have the same QNAME, we must have an error;
-    // the only way we could get different QNAMES in an answer is if one were a
-    // CNAME - in which case there should no other record types at that QNAME.
-    if (question.getType() == RRType::ANY()) {
-        bool all_same = true;
-        for (int i = 1; (i < answer.size()) && all_same; ++i) {
-            all_same = (answer[0]->getName() == answer[i]->getName());
-        }
-        if (all_same) {
-            return (ANSWER);
-        } else {
-            return (EXTRADATA);
-        }
-    }
-
-    // Multiple RRs in the answer, and not all the same QNAME.  This
-    // is either an answer, a CNAME (in either case, there could be multiple
-    // CNAMEs in the chain) or an error.
-    //
-    // So we need to follow the CNAME chain to resolve this.  For this to work:
-    //
-    // a) There must be one RR that matches the name, class and type of
-    //    the question, and this is a CNAME.
-    // b) The CNAME chain is followed until the end of the chain does not
-    //    exist (answer is a CNAME) or it is not of type CNAME (ANSWER).
-    //
-    // In the latter case, if there are additional RRs, it must be an error.
-
-    vector<RRsetPtr> ansrrset(answer);
-    vector<int> present(ansrrset.size(), 1);
-    return cnameChase(question.getName(), question.getType(), ansrrset, present,
-        ansrrset.size());
-}
-
-// Search the CNAME chain.
-ResponseClassifier::Category ResponseClassifier::cnameChase(
-    const Name& qname, const RRType& qtype, vector<RRsetPtr>& ansrrset,
-    vector<int>& present, size_t size)
-{
-    // Search through the vector of RRset pointers until we find one with the
-    // right QNAME.
-    for (int i = 0; i < ansrrset.size(); ++i) {
-        if (present[i]) {
-
-            // This entry has not been logically removed, so look at it.
-            if (ansrrset[i]->getName() == qname) {
-
-                // QNAME match.  If this RRset is a CNAME, remove it from
-                // further consideration.  If nothing is left, the end of the
-                // chain is a CNAME so this is a CNAME.  Otherwise replace
-                // the name with the RDATA of the CNAME and call ourself
-                // recursively.
-                if (ansrrset[i]->getType() == RRType::CNAME()) {
-
-                    // Don't consider it in the next iteration (although we
-                    // can still access it for now).
-                    present[i] = 0;
-                    --size;
-                    if (size == 0) {
-                        return (CNAME);
-                    }
-                    else {
-                        if (ansrrset[i]->getRdataCount() != 1) {
-
-                            // Multiple RDATA for a CNAME?  This is invalid.
-
-                            return (NOTSINGLE);
-                        }
-                        RdataIteratorPtr it = ansrrset[i]->getRdataIterator();
-                        Name newname(it->getCurrent().toText());
-
-                        return cnameChase(newname, qtype, ansrrset, present,
-                            size);
-                    }
-
-                } else {
-
-                    // We've got here because the element is not a CNAME.  If
-                    // this is the last element and the type is the one we are
-                    // after, we've found the answer, or it is an error.  If
-                    // there is more than one RRset left in the list we are
-                    // searching, we have extra data in the answer.
-                    if (ansrrset[i]->getType() == qtype) {
-                        if (size == 1) {
-                            return (ANSWERCNAME);
-                        } else {
-                            return (EXTRADATA);
-                        }
-                    }
-                    return (INVTYPE);
-                }
-            }
-        }
-    }
-
-    // We get here if we've dropped off the end of the list without finding the
-    // QNAME we are looking for.  This means that the CNAME chain has ended
-    // but there are additional RRsets in the data.
-
-    return (EXTRADATA);
-}
diff --git a/src/bin/resolver/response_classifier.h b/src/bin/resolver/response_classifier.h
deleted file mode 100644
index 394d60f..0000000
--- a/src/bin/resolver/response_classifier.h
+++ /dev/null
@@ -1,138 +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.
-
-// $Id$
-
-#ifndef __RESPONSE_CLASSIFIER_H
-#define __RESPONSE_CLASSIFIER_H
-
-#include <cstddef>
-
-#include <dns/question.h>
-#include <dns/message.h>
-#include <dns/question.h>
-
-/// \brief Classify Server Response
-///
-/// This class is used in the recursive server.  It is passed an answer received
-/// from an upstream server and categorises it.
-///
-/// TODO: It is unlikely that the code can be used in this form.  Some adaption
-/// of it will be required to put it in the server.
-///
-/// TODO: The code here does not take into account any EDNS0 fields.
-
-class ResponseClassifier {
-public:
-
-    /// \brief Category of Answer
-    ///
-    /// In the valid answers, not the distinction between REFERRAL and CNAME.
-    /// A REFERRAL answer means that the answer section of the message is
-    /// empty, but there is something in the authority section.  A CNAME means
-    /// that the answer section contains one or more CNAMES in a chain that
-    /// do not end with a non-CNAME RRset.
-    enum Category {
-
-        // Codes indicating that a message is valid.
-
-        ANSWER,             ///< Response contains the answer
-        ANSWERCNAME,        ///< Response was a CNAME chain ending in an answer
-        CNAME,              ///< Response was a CNAME
-        NXDOMAIN,           ///< Response was an NXDOMAIN
-        REFERRAL,           ///< Response contains a referral
-
-        // Codes indicating that a message is invalid.  Note that the error()
-        // method relies on these appearing after the "message valid" codes.
-
-        EMPTY,              ///< No answer or authority sections
-        EXTRADATA,          ///< Answer section contains more RRsets than needed
-        INVNAMCLASS,        ///< Invalid name or class in answer
-        INVTYPE,            ///< Name/class of answer correct, type is wrong
-        MISMATQUEST,        ///< Response question section != question
-        MULTICLASS,         ///< Multiple classes in multi-RR answer
-        NOTONEQUEST,        ///< Not one question in response question section
-        NOTRESPONSE,        ///< Response has the Query/Response bit clear
-        NOTSINGLE,          ///< CNAME has multiple RDATA elements.
-        OPCODE,             ///< Opcode field does not indicate a query
-        RCODE,              ///< RCODE indicated an error
-        TRUNCATED           ///< Response was truncated
-    };
-
-    /// \brief Check Error
-    ///
-    /// An inline routine to quickly classify whether the return category is
-    /// an error or not.  This makes use of internal knowledge of the order of
-    /// codes in the Category enum.
-    ///
-    /// \param code Return category from classify()
-    ///
-    /// \return true if the category is an error, false if not.
-    static bool error(Category code) {
-        return (code > REFERRAL);
-    }
-
-    /// \brief Classify
-    ///
-    /// Classify the response in the "message" object.
-    ///
-    /// \param question Question that was sent to the server
-    /// \param message Pointer to the associated response from the server.
-    /// \param tcignore If set, the TC bit in a response packet is
-    /// ignored.  Otherwise the error code TRUNCATED will be returned.  The
-    /// only time this is likely to be used is in development where we are not
-    /// going to fail over to TCP and will want to use what is returned, even
-    /// if some of the response was lost.
-    static Category classify(const isc::dns::Question& question,
-            const isc::dns::MessagePtr& message, bool tcignore = false);
-
-private:
-    /// \brief Follow CNAMEs
-    ///
-    /// Given a QNAME and an answer section that contains CNAMEs, assume that
-    /// they form a CNAME chain and search through them.  Possible outcomes
-    /// are:
-    ///
-    /// a) All CNAMES and they form a chain.  The result is a referral.
-    /// b) All but one are CNAMES and they form a chain.  The other is pointed
-    ///    to by the last element of the chain and is the correct QTYPE.  The
-    ///    result is an answer.
-    /// c) Having followed the CNAME chain as far as we can, there is one
-    ///    remaining RRset that is of the wrong type, or there are multiple
-    ///    RRsets remaining.  return the EXTRADATA code.
-    ///
-    /// \param qname Question name we are searching for
-    /// \param qtype Question type we are search for.  (This is assumed not
-    /// to be "ANY".)
-    /// \param ansrrset Vector of RRsetPtr pointing to the RRsets we are
-    /// considering.
-    /// \param present Array of "int" the same size of ansrrset, with each
-    /// element set to "1" to allow the corresponding element of ansrrset to
-    /// be checked, and "0" to skip it.  This might be premature optimisation,
-    /// but the algorithm would otherwise involve duplicating the RRset
-    /// vector then removing elements from random positions one by one.  As
-    /// each removal involves the destruction of an "xxxPtr" element (which
-    /// presently is implemented by boost::shared_ptr), the overhad of memory
-    /// management seemed high.  This solution imposes some additional loop
-    /// cycles, but that should be minimal compared with the overhead of the
-    /// memory management.
-    /// \param size Number of elements to check.  See description of \c present
-    /// for details.
-    static Category cnameChase(const isc::dns::Name& qname,
-        const isc::dns::RRType& qtype,
-        std::vector<isc::dns::RRsetPtr>& ansrrset, std::vector<int>& present,
-        size_t size);
-};
-
-#endif // __RESPONSE_CLASSIFIER_H
diff --git a/src/bin/resolver/response_scrubber.h b/src/bin/resolver/response_scrubber.h
index 971de64..c3fce57 100644
--- a/src/bin/resolver/response_scrubber.h
+++ b/src/bin/resolver/response_scrubber.h
@@ -243,7 +243,7 @@
 /// scrubbed.
 
 #include <config.h>
-#include <asiolink/ioendpoint.h>
+#include <asiolink/io_endpoint.h>
 #include <dns/message.h>
 #include <dns/name.h>
 
diff --git a/src/bin/resolver/tests/Makefile.am b/src/bin/resolver/tests/Makefile.am
index 8bebff0..a03439c 100644
--- a/src/bin/resolver/tests/Makefile.am
+++ b/src/bin/resolver/tests/Makefile.am
@@ -19,11 +19,9 @@ TESTS += run_unittests
 run_unittests_SOURCES = $(top_srcdir)/src/lib/dns/tests/unittest_util.h
 run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
 run_unittests_SOURCES += ../resolver.h ../resolver.cc
-run_unittests_SOURCES += ../response_classifier.h ../response_classifier.cc
 run_unittests_SOURCES += ../response_scrubber.h ../response_scrubber.cc
 run_unittests_SOURCES += resolver_unittest.cc
 run_unittests_SOURCES += resolver_config_unittest.cc
-run_unittests_SOURCES += response_classifier_unittest.cc
 run_unittests_SOURCES += response_scrubber_unittest.cc
 run_unittests_SOURCES += run_unittests.cc
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
@@ -31,8 +29,8 @@ 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/datasrc/libdatasrc.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/config/libcfgclient.la
 run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la
diff --git a/src/bin/resolver/tests/response_classifier_unittest.cc b/src/bin/resolver/tests/response_classifier_unittest.cc
deleted file mode 100644
index ee439b1..0000000
--- a/src/bin/resolver/tests/response_classifier_unittest.cc
+++ /dev/null
@@ -1,494 +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.
-
-#include <iostream>
-#include <gtest/gtest.h>
-
-#include <dns/tests/unittest_util.h>
-
-#include <resolver/response_classifier.h>
-
-#include <dns/name.h>
-#include <dns/opcode.h>
-#include <dns/question.h>
-#include <dns/rdata.h>
-#include <dns/rdataclass.h>
-#include <dns/rcode.h>
-#include <dns/rrclass.h>
-#include <dns/rrset.h>
-#include <dns/rrtype.h>
-#include <dns/rrttl.h>
-
-using namespace std;
-using namespace isc::dns;
-using namespace rdata;
-using namespace isc::dns::rdata::generic;
-using namespace isc::dns::rdata::in;
-
-namespace {
-class ResponseClassifierTest : public ::testing::Test {
-public:
-    /// \brief Constructor
-    ///
-    /// The naming convention is:
-    ///
-    /// <category>_<class>_<type>_<name>
-    ///
-    /// <category> is "qu" (question), "rrs" (rrset),
-    /// <qclass> is self-explanatory
-    /// <qtype> is self-explanatory
-    /// <name> is the first part of the domain name (all expected to be in
-    /// example.com)
-    ///
-    /// Message variables
-    ///
-    /// msg_<qtype>  Where <qtype> is the type of query.  These are only used
-    /// in the early tests where simple messages are required.
-
-    ResponseClassifierTest() :
-        msg_a(new Message(Message::RENDER)),
-        msg_any(new Message(Message::RENDER)),
-        qu_ch_a_www(Name("www.example.com"), RRClass::CH(), RRType::A()),
-        qu_in_any_www(Name("www.example.com"), RRClass::IN(), RRType::ANY()),
-        qu_in_a_www2(Name("www2.example.com"), RRClass::IN(), RRType::A()),
-        qu_in_a_www(Name("www.example.com"), RRClass::IN(), RRType::A()),
-        qu_in_cname_www1(Name("www1.example.com"), RRClass::IN(), RRType::A()),
-        qu_in_ns_(Name("example.com"), RRClass::IN(), RRType::NS()),
-        qu_in_txt_www(Name("www.example.com"), RRClass::IN(), RRType::TXT()),
-        rrs_hs_txt_www(new RRset(Name("www.example.com"), RRClass::HS(),
-            RRType::TXT(), RRTTL(300))),
-        rrs_in_a_mail(new RRset(Name("mail.example.com"), RRClass::IN(),
-            RRType::A(), RRTTL(300))),
-        rrs_in_a_www(new RRset(Name("www.example.com"), RRClass::IN(),
-            RRType::A(), RRTTL(300))),
-        rrs_in_cname_www1(new RRset(Name("www1.example.com"), RRClass::IN(),
-            RRType::CNAME(), RRTTL(300))),
-        rrs_in_cname_www2(new RRset(Name("www2.example.com"), RRClass::IN(),
-            RRType::CNAME(), RRTTL(300))),
-        rrs_in_ns_(new RRset(Name("example.com"), RRClass::IN(),
-            RRType::NS(), RRTTL(300))),
-        rrs_in_txt_www(new RRset(Name("www.example.com"), RRClass::IN(),
-            RRType::TXT(), RRTTL(300)))
-    {
-        // Set up the message to indicate a successful response to the question
-        // "www.example.com A", but don't add in any response sections.
-        msg_a->setHeaderFlag(Message::HEADERFLAG_QR);
-        msg_a->setOpcode(Opcode::QUERY());
-        msg_a->setRcode(Rcode::NOERROR());
-        msg_a->addQuestion(qu_in_a_www);
-
-        // ditto for the query "www.example.com ANY"
-        msg_any->setHeaderFlag(Message::HEADERFLAG_QR);
-        msg_any->setOpcode(Opcode::QUERY());
-        msg_any->setRcode(Rcode::NOERROR());
-        msg_any->addQuestion(qu_in_any_www);
-
-        // The next set of assignments set up the following zone records
-        //
-        // example.com           NS     ns0.isc.org
-        //                       NS     ns0.example.org
-        //
-        // www.example.com       A      1.2.3.4
-        //                       TXT    "An example text string"
-        //
-        // mail.example.com      A      4.5.6.7
-        //
-        // www1.example.com      CNAME  www.example.com
-        //
-        // www2.example.com      CNAME  www1.example.com
-
-        // Set up an imaginary NS RRset for an authority section
-        rrs_in_ns_->addRdata(ConstRdataPtr(new NS(Name("ns0.isc.org"))));
-        rrs_in_ns_->addRdata(ConstRdataPtr(new NS(Name("ns0.example.org"))));
-
-        // Set up the records for the www host
-        rrs_in_a_www->addRdata(ConstRdataPtr(new A("1.2.3.4")));
-        rrs_in_txt_www->addRdata(ConstRdataPtr(
-            new TXT("An example text string")));
-
-        // ... for the mail host
-        rrs_in_a_mail->addRdata(ConstRdataPtr(new A("5.6.7.8")));
-
-        // ... the CNAME records
-        rrs_in_cname_www1->addRdata(ConstRdataPtr(
-            new CNAME("www.example.com")));
-        rrs_in_cname_www2->addRdata(ConstRdataPtr(
-            new CNAME("www1.example.com")));
-    }
-
-    MessagePtr  msg_a;              // Pointer to message in RENDER state
-    MessagePtr  msg_any;            // Pointer to message in RENDER state
-    Question    qu_ch_a_www;        // www.example.com CH A
-    Question    qu_in_any_www;      // www.example.com IN ANY
-    Question    qu_in_a_www2;       // www.example.com IN ANY
-    Question    qu_in_a_www;        // www.example.com IN A
-    Question    qu_in_cname_www1;   // www1.example.com IN CNAME
-    Question    qu_in_ns_;          // example.com IN NS
-    Question    qu_in_txt_www;      // www.example.com IN TXT
-    RRsetPtr    rrs_hs_txt_www;     // www.example.com HS TXT
-    RRsetPtr    rrs_in_a_mail;      // mail.example.com IN A
-    RRsetPtr    rrs_in_a_www;       // www.example.com IN A
-    RRsetPtr    rrs_in_cname_www1;  // www1.example.com IN CNAME
-    RRsetPtr    rrs_in_cname_www2;  // www2.example.com IN CNAME
-    RRsetPtr    rrs_in_ns_;         // example.com IN NS
-    RRsetPtr    rrs_in_txt_www;     // www.example.com IN TXT
-};
-
-// Test that the error() function categorises the codes correctly.
-
-TEST_F(ResponseClassifierTest, StatusCodes) {
-    EXPECT_FALSE(ResponseClassifier::error(ResponseClassifier::ANSWER));
-    EXPECT_FALSE(ResponseClassifier::error(ResponseClassifier::ANSWERCNAME));
-    EXPECT_FALSE(ResponseClassifier::error(ResponseClassifier::CNAME));
-    EXPECT_FALSE(ResponseClassifier::error(ResponseClassifier::NXDOMAIN));
-    EXPECT_FALSE(ResponseClassifier::error(ResponseClassifier::REFERRAL));
-
-    EXPECT_TRUE(ResponseClassifier::error(ResponseClassifier::EMPTY));
-    EXPECT_TRUE(ResponseClassifier::error(ResponseClassifier::EXTRADATA));
-    EXPECT_TRUE(ResponseClassifier::error(ResponseClassifier::INVNAMCLASS));
-    EXPECT_TRUE(ResponseClassifier::error(ResponseClassifier::INVTYPE));
-    EXPECT_TRUE(ResponseClassifier::error(ResponseClassifier::MISMATQUEST));
-    EXPECT_TRUE(ResponseClassifier::error(ResponseClassifier::MULTICLASS));
-    EXPECT_TRUE(ResponseClassifier::error(ResponseClassifier::NOTONEQUEST));
-    EXPECT_TRUE(ResponseClassifier::error(ResponseClassifier::NOTRESPONSE));
-    EXPECT_TRUE(ResponseClassifier::error(ResponseClassifier::NOTSINGLE));
-    EXPECT_TRUE(ResponseClassifier::error(ResponseClassifier::OPCODE));
-    EXPECT_TRUE(ResponseClassifier::error(ResponseClassifier::RCODE));
-    EXPECT_TRUE(ResponseClassifier::error(ResponseClassifier::TRUNCATED));
-}
-
-// Test that the system will reject a message which is a query.
-
-TEST_F(ResponseClassifierTest, Query) {
-
-    // Set up message to indicate a query (QR flag = 0, one question).  By
-    // default the opcode will be 0 (query)
-    msg_a->setHeaderFlag(Message::HEADERFLAG_QR, false);
-
-    // Should be rejected as it is a query, not a response
-    EXPECT_EQ(ResponseClassifier::NOTRESPONSE,
-        ResponseClassifier::classify(qu_in_a_www, msg_a));
-}
-
-// Check that we get an OPCODE error on all but QUERY opcodes.
-
-TEST_F(ResponseClassifierTest, Opcode) {
-
-    uint8_t query = static_cast<uint8_t>(Opcode::QUERY().getCode());
-
-    for (uint8_t i = 0; i < (1 << 4); ++i) {
-        msg_a->setOpcode(Opcode(i));
-        if (i == query) {
-            EXPECT_NE(ResponseClassifier::OPCODE,
-                ResponseClassifier::classify(qu_in_a_www, msg_a));
-        } else {
-            EXPECT_EQ(ResponseClassifier::OPCODE,
-                ResponseClassifier::classify(qu_in_a_www, msg_a));
-        }
-    }
-}
-
-// Test that the system will reject a response with anything other than one
-// question.
-
-TEST_F(ResponseClassifierTest, MultipleQuestions) {
-
-    // Create a message object for this test that has no question section.
-    MessagePtr message(new Message(Message::RENDER));
-    message->setHeaderFlag(Message::HEADERFLAG_QR);
-    message->setOpcode(Opcode::QUERY());
-    message->setRcode(Rcode::NOERROR());
-
-    // Zero questions
-    EXPECT_EQ(ResponseClassifier::NOTONEQUEST,
-        ResponseClassifier::classify(qu_in_a_www, message));
-
-    // One question
-    message->addQuestion(qu_in_a_www);
-    EXPECT_NE(ResponseClassifier::NOTONEQUEST,
-        ResponseClassifier::classify(qu_in_a_www, message));
-
-    // Two questions
-    message->addQuestion(qu_in_ns_);
-    EXPECT_EQ(ResponseClassifier::NOTONEQUEST,
-        ResponseClassifier::classify(qu_in_a_www, message));
-
-    // And finish the check with three questions
-    message->addQuestion(qu_in_txt_www);
-    EXPECT_EQ(ResponseClassifier::NOTONEQUEST,
-        ResponseClassifier::classify(qu_in_a_www, message));
-}
-
-// Test that the question in the question section in the message response
-// is equal to the question supplied.
-
-TEST_F(ResponseClassifierTest, SameQuestion) {
-
-    EXPECT_EQ(ResponseClassifier::MISMATQUEST,
-        ResponseClassifier::classify(qu_in_ns_, msg_a));
-    EXPECT_NE(ResponseClassifier::MISMATQUEST,
-        ResponseClassifier::classify(qu_in_a_www, msg_a));
-}
-
-// Should get an NXDOMAIN response only on an NXDOMAIN RCODE.
-
-TEST_F(ResponseClassifierTest, NXDOMAIN) {
-
-    uint16_t nxdomain = static_cast<uint16_t>(Rcode::NXDOMAIN().getCode());
-
-    for (uint8_t i = 0; i < (1 << 4); ++i) {
-        msg_a->setRcode(Rcode(i));
-        if (i == nxdomain) {
-            EXPECT_EQ(ResponseClassifier::NXDOMAIN,
-                ResponseClassifier::classify(qu_in_a_www, msg_a));
-        } else {
-            EXPECT_NE(ResponseClassifier::NXDOMAIN,
-                ResponseClassifier::classify(qu_in_a_www, msg_a));
-        }
-    }
-}
-
-// Check that we get an RCODE error on all but NXDOMAIN and NOERROR responses.
-
-TEST_F(ResponseClassifierTest, RCODE) {
-
-    uint16_t nxdomain = static_cast<uint16_t>(Rcode::NXDOMAIN().getCode());
-    uint16_t noerror = static_cast<uint16_t>(Rcode::NOERROR().getCode());
-
-    for (uint8_t i = 0; i < (1 << 4); ++i) {
-        msg_a->setRcode(Rcode(i));
-        if ((i == nxdomain) || (i == noerror)) {
-            EXPECT_NE(ResponseClassifier::RCODE,
-                ResponseClassifier::classify(qu_in_a_www, msg_a));
-        } else {
-            EXPECT_EQ(ResponseClassifier::RCODE,
-                ResponseClassifier::classify(qu_in_a_www, msg_a));
-        }
-    }
-}
-
-// Test that the code will detect a truncated message.  Even if nothing else
-// is wrong, we'll want to retry the query if we receive a truncated code.
-// However, we give the option to the user of the code aws to whether they
-// want to take into account the truncated bit.
-
-TEST_F(ResponseClassifierTest, Truncated) {
-
-    // Don't expect the truncated code whatever option we ask for if the TC
-    // bit is not set.
-    msg_a->setHeaderFlag(Message::HEADERFLAG_TC, false);
-    EXPECT_NE(ResponseClassifier::TRUNCATED,
-        ResponseClassifier::classify(qu_in_a_www, msg_a, true));
-    EXPECT_NE(ResponseClassifier::TRUNCATED,
-        ResponseClassifier::classify(qu_in_a_www, msg_a, false));
-
-    // Expect the truncated code if the TC bit is set, only if we don't ignore
-    // it.
-    msg_a->setHeaderFlag(Message::HEADERFLAG_TC, true);
-    EXPECT_NE(ResponseClassifier::TRUNCATED,
-        ResponseClassifier::classify(qu_in_a_www, msg_a, true));
-    EXPECT_EQ(ResponseClassifier::TRUNCATED,
-        ResponseClassifier::classify(qu_in_a_www, msg_a, false));
-}
-
-// Check for an empty packet (i.e. no error, but with the answer and additional
-// sections empty).
-
-TEST_F(ResponseClassifierTest, Empty) {
-
-    EXPECT_EQ(ResponseClassifier::EMPTY,
-        ResponseClassifier::classify(qu_in_a_www, msg_a));
-}
-
-// Anything where we have an empty answer section but something in the
-// authority section is a referral (if the status is NOERROR).
-
-TEST_F(ResponseClassifierTest, EmptyAnswerReferral) {
-
-    msg_a->addRRset(Message::SECTION_AUTHORITY, rrs_in_ns_);
-    EXPECT_EQ(ResponseClassifier::REFERRAL,
-        ResponseClassifier::classify(qu_in_a_www, msg_a));
-
-}
-
-// Check the case where we have a simple answer in the answer section.  This
-// occurs when the QNAME/QTYPE/QCLASS matches one of the RRsets in the
-// answer section - expect when the QTYPE is ANY, in which case the match
-// must be on the QNAME/QCLASS alone.
-
-TEST_F(ResponseClassifierTest, SingleAnswer) {
-
-    // Check a question that matches the answer
-    msg_a->addRRset(Message::SECTION_ANSWER, rrs_in_a_www);
-    EXPECT_EQ(ResponseClassifier::ANSWER,
-        ResponseClassifier::classify(qu_in_a_www, msg_a));
-
-    // Check an ANY question that matches the answer
-    msg_any->addRRset(Message::SECTION_ANSWER, rrs_in_a_www);
-    EXPECT_EQ(ResponseClassifier::ANSWER,
-        ResponseClassifier::classify(qu_in_any_www, msg_any));
-
-    // Check a CNAME response that matches the QNAME.
-    MessagePtr message_a(new Message(Message::RENDER));
-    message_a->setHeaderFlag(Message::HEADERFLAG_QR);
-    message_a->setOpcode(Opcode::QUERY());
-    message_a->setRcode(Rcode::NOERROR());
-    message_a->addQuestion(qu_in_cname_www1);
-    message_a->addRRset(Message::SECTION_ANSWER, rrs_in_cname_www1);
-    EXPECT_EQ(ResponseClassifier::CNAME,
-        ResponseClassifier::classify(qu_in_cname_www1, message_a));
-
-    // Check if the answer QNAME does not match the question
-    // Q: www.example.com  IN A
-    // A: mail.example.com IN A
-    MessagePtr message_b(new Message(Message::RENDER));
-    message_b->setHeaderFlag(Message::HEADERFLAG_QR);
-    message_b->setOpcode(Opcode::QUERY());
-    message_b->setRcode(Rcode::NOERROR());
-    message_b->addQuestion(qu_in_a_www);
-    message_b->addRRset(Message::SECTION_ANSWER, rrs_in_a_mail);
-    EXPECT_EQ(ResponseClassifier::INVNAMCLASS,
-        ResponseClassifier::classify(qu_in_a_www, message_b));
-
-    // Check if the answer class does not match the question
-    // Q: www.example.com CH A
-    // A: www.example.com IN A
-    MessagePtr message_c(new Message(Message::RENDER));
-    message_c->setHeaderFlag(Message::HEADERFLAG_QR);
-    message_c->setOpcode(Opcode::QUERY());
-    message_c->setRcode(Rcode::NOERROR());
-    message_c->addQuestion(qu_ch_a_www);
-    message_c->addRRset(Message::SECTION_ANSWER, rrs_in_a_www);
-    EXPECT_EQ(ResponseClassifier::INVNAMCLASS,
-        ResponseClassifier::classify(qu_ch_a_www, message_c));
-
-    // Check if the answer type does not match the question
-    // Q: www.example.com IN A
-    // A: www.example.com IN TXT
-    MessagePtr message_d(new Message(Message::RENDER));
-    message_d->setHeaderFlag(Message::HEADERFLAG_QR);
-    message_d->setOpcode(Opcode::QUERY());
-    message_d->setRcode(Rcode::NOERROR());
-    message_d->addQuestion(qu_in_a_www);
-    message_d->addRRset(Message::SECTION_ANSWER, rrs_in_txt_www);
-    EXPECT_EQ(ResponseClassifier::INVTYPE,
-        ResponseClassifier::classify(qu_in_a_www, message_d));
-}
-
-// Check what happens if we have multiple RRsets in the answer.
-
-TEST_F(ResponseClassifierTest, MultipleAnswerRRsets) {
-
-    // All the same QNAME but different types is only valid on an ANY query.
-    MessagePtr message_a(new Message(Message::RENDER));
-    message_a->setHeaderFlag(Message::HEADERFLAG_QR);
-    message_a->setOpcode(Opcode::QUERY());
-    message_a->setRcode(Rcode::NOERROR());
-    message_a->addQuestion(qu_in_any_www);
-    message_a->addRRset(Message::SECTION_ANSWER, rrs_in_a_www);
-    message_a->addRRset(Message::SECTION_ANSWER, rrs_in_txt_www);
-    EXPECT_EQ(ResponseClassifier::ANSWER,
-        ResponseClassifier::classify(qu_in_any_www, message_a));
-
-    // On another type of query, it results in an EXTRADATA error
-    MessagePtr message_b(new Message(Message::RENDER));
-    message_b->setHeaderFlag(Message::HEADERFLAG_QR);
-    message_b->setOpcode(Opcode::QUERY());
-    message_b->setRcode(Rcode::NOERROR());
-    message_b->addQuestion(qu_in_a_www);
-    message_b->addRRset(Message::SECTION_ANSWER, rrs_in_a_www);
-    message_b->addRRset(Message::SECTION_ANSWER, rrs_in_txt_www);
-    EXPECT_EQ(ResponseClassifier::EXTRADATA,
-        ResponseClassifier::classify(qu_in_a_www, message_b));
-
-    // Same QNAME on an ANY query is not valid with mixed classes
-    MessagePtr message_c(new Message(Message::RENDER));
-    message_c->setHeaderFlag(Message::HEADERFLAG_QR);
-    message_c->setOpcode(Opcode::QUERY());
-    message_c->setRcode(Rcode::NOERROR());
-    message_c->addQuestion(qu_in_any_www);
-    message_c->addRRset(Message::SECTION_ANSWER, rrs_in_a_www);
-    message_c->addRRset(Message::SECTION_ANSWER, rrs_hs_txt_www);
-    EXPECT_EQ(ResponseClassifier::MULTICLASS,
-        ResponseClassifier::classify(qu_in_any_www, message_c));
-
-    // Mixed QNAME is not valid unless QNAME requested is a CNAME.
-    MessagePtr message_d(new Message(Message::RENDER));
-    message_d->setHeaderFlag(Message::HEADERFLAG_QR);
-    message_d->setOpcode(Opcode::QUERY());
-    message_d->setRcode(Rcode::NOERROR());
-    message_d->addQuestion(qu_in_a_www);
-    message_d->addRRset(Message::SECTION_ANSWER, rrs_in_a_www);
-    message_d->addRRset(Message::SECTION_ANSWER, rrs_in_a_mail);
-    EXPECT_EQ(ResponseClassifier::EXTRADATA,
-        ResponseClassifier::classify(qu_in_a_www, message_d));
-
-    // Mixed QNAME is not valid when the query is an ANY.
-    MessagePtr message_e(new Message(Message::RENDER));
-    message_e->setHeaderFlag(Message::HEADERFLAG_QR);
-    message_e->setOpcode(Opcode::QUERY());
-    message_e->setRcode(Rcode::NOERROR());
-    message_e->addQuestion(qu_in_any_www);
-    message_e->addRRset(Message::SECTION_ANSWER, rrs_in_a_www);
-    message_e->addRRset(Message::SECTION_ANSWER, rrs_in_txt_www);
-    message_e->addRRset(Message::SECTION_ANSWER, rrs_in_a_mail);
-    EXPECT_EQ(ResponseClassifier::EXTRADATA,
-        ResponseClassifier::classify(qu_in_any_www, message_e));
-}
-
-// CNAME chain is CNAME if it terminates in a CNAME, answer if it
-// does not, and error if there are RRs left over.
-TEST_F(ResponseClassifierTest, CNAMEChain) {
-
-    // Answer contains a single CNAME
-    MessagePtr message_a(new Message(Message::RENDER));
-    message_a->setHeaderFlag(Message::HEADERFLAG_QR);
-    message_a->setOpcode(Opcode::QUERY());
-    message_a->setRcode(Rcode::NOERROR());
-    message_a->addQuestion(qu_in_a_www2);
-    message_a->addRRset(Message::SECTION_ANSWER, rrs_in_cname_www2);
-    EXPECT_EQ(ResponseClassifier::CNAME,
-        ResponseClassifier::classify(qu_in_a_www2, message_a));
-
-    // Add a CNAME for www1, and it should still return a CNAME
-    message_a->addRRset(Message::SECTION_ANSWER, rrs_in_cname_www1);
-    EXPECT_EQ(ResponseClassifier::CNAME,
-        ResponseClassifier::classify(qu_in_a_www2, message_a));
-
-    // Add the A record for www and it should be an answer
-    message_a->addRRset(Message::SECTION_ANSWER, rrs_in_a_www);
-    EXPECT_EQ(ResponseClassifier::ANSWERCNAME,
-        ResponseClassifier::classify(qu_in_a_www2, message_a));
-
-    // Adding an unrelated TXT record should result in EXTRADATA
-    message_a->addRRset(Message::SECTION_ANSWER, rrs_in_txt_www);
-    EXPECT_EQ(ResponseClassifier::EXTRADATA,
-        ResponseClassifier::classify(qu_in_a_www2, message_a));
-
-    // Recreate the chain, but this time end with a TXT RR and not the A
-    // record.  This should return INVTYPE.
-    MessagePtr message_b(new Message(Message::RENDER));
-    message_b->setHeaderFlag(Message::HEADERFLAG_QR);
-    message_b->setOpcode(Opcode::QUERY());
-    message_b->setRcode(Rcode::NOERROR());
-    message_b->addQuestion(qu_in_a_www2);
-    message_b->addRRset(Message::SECTION_ANSWER, rrs_in_cname_www2);
-    message_b->addRRset(Message::SECTION_ANSWER, rrs_in_cname_www1);
-    message_b->addRRset(Message::SECTION_ANSWER, rrs_in_txt_www);
-
-    EXPECT_EQ(ResponseClassifier::INVTYPE,
-        ResponseClassifier::classify(qu_in_a_www2, message_b));
-}
-
-} // Anonymous namespace
diff --git a/src/bin/resolver/tests/response_scrubber_unittest.cc b/src/bin/resolver/tests/response_scrubber_unittest.cc
index e561eea..1dc6639 100644
--- a/src/bin/resolver/tests/response_scrubber_unittest.cc
+++ b/src/bin/resolver/tests/response_scrubber_unittest.cc
@@ -21,8 +21,8 @@
 
 #include <config.h>
 
-#include <asiolink/ioendpoint.h>
-#include <asiolink/ioaddress.h>
+#include <asiolink/io_endpoint.h>
+#include <asiolink/io_address.h>
 #include <netinet/in.h>
 
 #include <dns/name.h>
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
index b7b80bf..8ab93ba 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 asiolink testutils nsas
+          resolve asiolink testutils nsas cache
diff --git a/src/lib/asiolink/Makefile.am b/src/lib/asiolink/Makefile.am
index 99b3c66..868fde5 100644
--- a/src/lib/asiolink/Makefile.am
+++ b/src/lib/asiolink/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = . tests internal
+SUBDIRS = . tests
 
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
 AM_CPPFLAGS += $(BOOST_INCLUDES)
@@ -12,14 +12,24 @@ CLEANFILES = *.gcno *.gcda
 # have some code fragments that would hit gcc's unused-parameter warning,
 # which would make the build fail with -Werror (our default setting).
 lib_LTLIBRARIES = libasiolink.la
-libasiolink_la_SOURCES = asiolink.cc asiolink.h
-libasiolink_la_SOURCES += iosocket.cc iosocket.h
-libasiolink_la_SOURCES += iomessage.h
-libasiolink_la_SOURCES += ioaddress.cc ioaddress.h
-libasiolink_la_SOURCES += ioendpoint.cc ioendpoint.h
-libasiolink_la_SOURCES += udpdns.cc internal/udpdns.h
-libasiolink_la_SOURCES += tcpdns.cc internal/tcpdns.h
-libasiolink_la_SOURCES += internal/coroutine.h
+libasiolink_la_SOURCES = asiolink.h
+libasiolink_la_SOURCES += io_service.cc io_service.h
+libasiolink_la_SOURCES += dns_service.cc dns_service.h
+libasiolink_la_SOURCES += dns_server.h
+libasiolink_la_SOURCES += dns_lookup.h
+libasiolink_la_SOURCES += dns_answer.h
+libasiolink_la_SOURCES += simple_callback.h
+libasiolink_la_SOURCES += interval_timer.h interval_timer.cc
+libasiolink_la_SOURCES += recursive_query.h recursive_query.cc
+libasiolink_la_SOURCES += io_socket.cc io_socket.h
+libasiolink_la_SOURCES += io_message.h
+libasiolink_la_SOURCES += io_address.cc io_address.h
+libasiolink_la_SOURCES += io_endpoint.cc io_endpoint.h
+libasiolink_la_SOURCES += udp_endpoint.h udp_socket.h
+libasiolink_la_SOURCES += udp_server.h udp_server.cc
+libasiolink_la_SOURCES += udp_query.h udp_query.cc
+libasiolink_la_SOURCES += tcp_endpoint.h tcp_socket.h
+libasiolink_la_SOURCES += tcp_server.h tcp_server.cc
 # Note: the ordering matters: -Wno-... must follow -Wextra (defined in
 # B10_CXXFLAGS)
 libasiolink_la_CXXFLAGS = $(AM_CXXFLAGS)
diff --git a/src/lib/asiolink/asiolink.cc b/src/lib/asiolink/asiolink.cc
deleted file mode 100644
index 6fe7666..0000000
--- a/src/lib/asiolink/asiolink.cc
+++ /dev/null
@@ -1,756 +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.
-
-#include <config.h>
-
-#include <cstdlib> // For rand(), temporary until better forwarding is done
-
-#include <unistd.h>             // for some IPC/network system calls
-#include <sys/socket.h>
-#include <netinet/in.h>
-
-#include <vector>
-#include <asio.hpp>
-#include <boost/lexical_cast.hpp>
-#include <boost/bind.hpp>
-#include <boost/date_time/posix_time/posix_time_types.hpp>
-
-#include <boost/shared_ptr.hpp>
-
-#include <dns/buffer.h>
-#include <dns/message.h>
-#include <dns/rcode.h>
-#include <dns/opcode.h>
-
-#include <asiolink/asiolink.h>
-#include <asiolink/internal/tcpdns.h>
-#include <asiolink/internal/udpdns.h>
-
-#include <resolve/resolve.h>
-
-#include <log/dummylog.h>
-
-using namespace asio;
-using asio::ip::udp;
-using asio::ip::tcp;
-
-using namespace std;
-using namespace isc::dns;
-using isc::log::dlog;
-using namespace boost;
-
-// Is this something we can use in libdns++?
-namespace {
-    class SectionInserter {
-    public:
-        SectionInserter(MessagePtr message, const Message::Section sect) :
-            message_(message), section_(sect)
-        {}
-        void operator()(const RRsetPtr rrset) {
-            message_->addRRset(section_, rrset, true);
-        }
-        MessagePtr message_;
-        const Message::Section section_;
-    };
-
-
-    /// \brief Copies the parts relevant for a DNS answer to the
-    /// target message
-    ///
-    /// This adds all the RRsets in the answer, authority and
-    /// additional sections to the target, as well as the response
-    /// code
-    void copyAnswerMessage(const Message& source, MessagePtr target) {
-        target->setRcode(source.getRcode());
-
-        for_each(source.beginSection(Message::SECTION_ANSWER),
-                 source.endSection(Message::SECTION_ANSWER),
-                 SectionInserter(target, Message::SECTION_ANSWER));
-        for_each(source.beginSection(Message::SECTION_AUTHORITY),
-                 source.endSection(Message::SECTION_AUTHORITY),
-                 SectionInserter(target, Message::SECTION_AUTHORITY));
-        for_each(source.beginSection(Message::SECTION_ADDITIONAL),
-                 source.endSection(Message::SECTION_ADDITIONAL),
-                 SectionInserter(target, Message::SECTION_ADDITIONAL));
-    }
-}
-
-namespace asiolink {
-
-typedef pair<string, uint16_t> addr_t;
-
-class IOServiceImpl {
-private:
-    IOServiceImpl(const IOService& source);
-    IOServiceImpl& operator=(const IOService& source);
-public:
-    /// \brief The constructor
-    IOServiceImpl() :
-        io_service_(),
-        work_(io_service_)
-    {};
-    /// \brief The destructor.
-    ~IOServiceImpl() {};
-    //@}
-
-    /// \brief Start the underlying event loop.
-    ///
-    /// This method does not return control to the caller until
-    /// the \c stop() method is called via some handler.
-    void run() { io_service_.run(); };
-
-    /// \brief Run the underlying event loop for a single event.
-    ///
-    /// This method return control to the caller as soon as the
-    /// first handler has completed.  (If no handlers are ready when
-    /// it is run, it will block until one is.)
-    void run_one() { io_service_.run_one();} ;
-
-    /// \brief Stop the underlying event loop.
-    ///
-    /// This will return the control to the caller of the \c run() method.
-    void stop() { io_service_.stop();} ;
-
-    /// \brief Return the native \c io_service object used in this wrapper.
-    ///
-    /// This is a short term work around to support other BIND 10 modules
-    /// that share the same \c io_service with the authoritative server.
-    /// It will eventually be removed once the wrapper interface is
-    /// generalized.
-    asio::io_service& get_io_service() { return io_service_; };
-private:
-    asio::io_service io_service_;
-    asio::io_service::work work_;
-};
-
-IOService::IOService() {
-    io_impl_ = new IOServiceImpl();
-}
-
-IOService::~IOService() {
-    delete io_impl_;
-}
-
-void
-IOService::run() {
-    io_impl_->run();
-}
-
-void
-IOService::run_one() {
-    io_impl_->run_one();
-}
-
-void
-IOService::stop() {
-    io_impl_->stop();
-}
-
-asio::io_service&
-IOService::get_io_service() {
-    return (io_impl_->get_io_service());
-}
-
-class DNSServiceImpl {
-public:
-    DNSServiceImpl(IOService& io_service, const char& port,
-                  const ip::address* v4addr, const ip::address* v6addr,
-                  SimpleCallback* checkin, DNSLookup* lookup,
-                  DNSAnswer* answer);
-
-    IOService& io_service_;
-
-    typedef boost::shared_ptr<UDPServer> UDPServerPtr;
-    typedef boost::shared_ptr<TCPServer> TCPServerPtr;
-    typedef boost::shared_ptr<DNSServer> DNSServerPtr;
-    vector<DNSServerPtr> servers_;
-    SimpleCallback *checkin_;
-    DNSLookup *lookup_;
-    DNSAnswer *answer_;
-
-    void addServer(uint16_t port, const ip::address& address) {
-        try {
-            dlog(std::string("Initialize TCP server at ") + address.to_string() + ":" + boost::lexical_cast<string>(port));
-            TCPServerPtr tcpServer(new TCPServer(io_service_.get_io_service(),
-                address, port, checkin_, lookup_, answer_));
-            (*tcpServer)();
-            servers_.push_back(tcpServer);
-            dlog(std::string("Initialize UDP server at ") + address.to_string() + ":" + boost::lexical_cast<string>(port));
-            UDPServerPtr udpServer(new UDPServer(io_service_.get_io_service(),
-                address, port, checkin_, lookup_, answer_));
-            (*udpServer)();
-            servers_.push_back(udpServer);
-        }
-        catch (const asio::system_error& err) {
-            // We need to catch and convert any ASIO level exceptions.
-            // This can happen for unavailable address, binding a privilege port
-            // without the privilege, etc.
-            isc_throw(IOError, "Failed to initialize network servers: " <<
-                      err.what());
-        }
-    }
-    void addServer(const char& port, const ip::address& address) {
-        uint16_t portnum;
-        try {
-            // XXX: SunStudio with stlport4 doesn't reject some invalid
-            // representation such as "-1" by lexical_cast<uint16_t>, so
-            // we convert it into a signed integer of a larger size and perform
-            // range check ourselves.
-            const int32_t portnum32 = boost::lexical_cast<int32_t>(&port);
-            if (portnum32 < 0 || portnum32 > 65535) {
-                isc_throw(IOError, "Invalid port number '" << &port);
-            }
-            portnum = portnum32;
-        } catch (const boost::bad_lexical_cast& ex) {
-            isc_throw(IOError, "Invalid port number '" << &port << "': " <<
-                      ex.what());
-        }
-        addServer(portnum, address);
-    }
-};
-
-DNSServiceImpl::DNSServiceImpl(IOService& io_service,
-                               const char& port,
-                               const ip::address* const v4addr,
-                               const ip::address* const v6addr,
-                               SimpleCallback* checkin,
-                               DNSLookup* lookup,
-                               DNSAnswer* answer) :
-    io_service_(io_service),
-    checkin_(checkin),
-    lookup_(lookup),
-    answer_(answer)
-{
-
-    if (v4addr) {
-        addServer(port, *v4addr);
-    }
-    if (v6addr) {
-        addServer(port, *v6addr);
-    }
-}
-
-DNSService::DNSService(IOService& io_service,
-                       const char& port, const char& address,
-                       SimpleCallback* checkin,
-                       DNSLookup* lookup,
-                       DNSAnswer* answer) :
-    impl_(new DNSServiceImpl(io_service, port, NULL, NULL, checkin, lookup,
-        answer)), io_service_(io_service)
-{
-    addServer(port, &address);
-}
-
-DNSService::DNSService(IOService& io_service,
-                       const char& port,
-                       const bool use_ipv4, const bool use_ipv6,
-                       SimpleCallback* checkin,
-                       DNSLookup* lookup,
-                       DNSAnswer* answer) :
-    impl_(NULL), io_service_(io_service)
-{
-    const ip::address v4addr_any = ip::address(ip::address_v4::any());
-    const ip::address* const v4addrp = use_ipv4 ? &v4addr_any : NULL; 
-    const ip::address v6addr_any = ip::address(ip::address_v6::any());
-    const ip::address* const v6addrp = use_ipv6 ? &v6addr_any : NULL;
-    impl_ = new DNSServiceImpl(io_service, port, v4addrp, v6addrp, checkin, lookup, answer);
-}
-
-DNSService::DNSService(IOService& io_service, SimpleCallback* checkin,
-    DNSLookup* lookup, DNSAnswer *answer) :
-    impl_(new DNSServiceImpl(io_service, *"0", NULL, NULL, checkin, lookup,
-        answer)), io_service_(io_service)
-{
-}
-
-DNSService::~DNSService() {
-    delete impl_;
-}
-
-namespace {
-
-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 {
-
-ip::address
-convertAddr(const string& address) {
-    error_code err;
-    ip::address addr = ip::address::from_string(address, err);
-    if (err) {
-        isc_throw(IOError, "Invalid IP address '" << &address << "': "
-            << err.message());
-    }
-    return (addr);
-}
-
-}
-
-void
-DNSService::addServer(const char& port, const string& address) {
-    impl_->addServer(port, convertAddr(address));
-}
-
-void
-DNSService::addServer(uint16_t port, const string& address) {
-    impl_->addServer(port, convertAddr(address));
-}
-
-void
-DNSService::clearServers() {
-    // FIXME: This does not work, it does not close the socket.
-    // How is it done?
-    impl_->servers_.clear();
-}
-
-namespace {
-
-/*
- * 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 UDPQuery::Callback {
-private:
-    // The io service to handle async calls
-    asio::io_service& 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
-    shared_ptr<AddressVector> upstream_;
-
-    // root servers...just copied over to the zone_servers_
-    shared_ptr<AddressVector> upstream_root_;
-
-    // Buffer to store the result.
-    OutputBufferPtr buffer_;
-
-    // Server to notify when we succeed or fail
-    //shared_ptr<DNSServer> server_;
-    isc::resolve::ResolverInterface::CallbackPtr resolvercallback_;
-
-    /*
-     * TODO Do something more clever with timeouts. In the long term, some
-     *     computation of average RTT, increase with each retry, etc.
-     */
-    // Timeout information
-    int query_timeout_;
-    unsigned retries_;
-
-    // normal query state
-
-    // Not using NSAS at this moment, so we keep a list
-    // of 'current' zone servers
-    vector<addr_t> zone_servers_;
-
-    // Update the question that will be sent to the server
-    void setQuestion(const Question& new_question) {
-        question_ = new_question;
-    }
-
-    deadline_timer client_timer;
-    deadline_timer lookup_timer;
-
-    size_t queries_out_;
-
-    // If we timed out ourselves (lookup timeout), stop issuing queries
-    bool done_;
-
-    // (re)send the query to the server.
-    void send() {
-        const int uc = upstream_->size();
-        const int zs = zone_servers_.size();
-        buffer_->clear();
-        if (uc > 0) {
-            int serverIndex = rand() % uc;
-            dlog("Sending upstream query (" + question_.toText() +
-                ") to " + upstream_->at(serverIndex).first);
-            UDPQuery query(io_, question_,
-                upstream_->at(serverIndex).first,
-                upstream_->at(serverIndex).second, buffer_, this,
-                query_timeout_);
-            ++queries_out_;
-            io_.post(query);
-        } else if (zs > 0) {
-            int serverIndex = rand() % zs;
-            dlog("Sending query to zone server (" + question_.toText() +
-                ") to " + zone_servers_.at(serverIndex).first);
-            UDPQuery query(io_, question_,
-                zone_servers_.at(serverIndex).first,
-                zone_servers_.at(serverIndex).second, buffer_, this,
-                query_timeout_);
-            ++queries_out_;
-            io_.post(query);
-        } else {
-            dlog("Error, no upstream servers to send to.");
-        }
-    }
-    
-    // This function is called by operator() if there is an actual
-    // answer from a server and we are in recursive mode
-    // depending on the contents, we go on recursing or return
-    //
-    // Note that the footprint may change as this function may
-    // need to append data to the answer we are building later.
-    //
-    // returns true if we are done
-    // returns false if we are not done
-    bool handleRecursiveAnswer(const Message& incoming) {
-        if (incoming.getRRCount(Message::SECTION_ANSWER) > 0) {
-            dlog("Got final result, copying answer.");
-            copyAnswerMessage(incoming, answer_message_);
-            return true;
-        } else {
-            dlog("Got delegation, continuing");
-            // ok we need to do some more processing.
-            // the ns list should contain all nameservers
-            // while the additional may contain addresses for
-            // them.
-            // this needs to tie into NSAS of course
-            // for this very first mockup, hope there is an
-            // address in additional and just use that
-
-            // send query to the addresses in the delegation
-            bool found_ns_address = false;
-            zone_servers_.clear();
-
-            for (RRsetIterator rrsi = incoming.beginSection(Message::SECTION_ADDITIONAL);
-                 rrsi != incoming.endSection(Message::SECTION_ADDITIONAL) && !found_ns_address;
-                 rrsi++) {
-                ConstRRsetPtr rrs = *rrsi;
-                if (rrs->getType() == RRType::A()) {
-                    // found address
-                    RdataIteratorPtr rdi = rrs->getRdataIterator();
-                    // just use the first for now
-                    if (!rdi->isLast()) {
-                        std::string addr_str = rdi->getCurrent().toText();
-                        dlog("[XX] first address found: " + addr_str);
-                        // now we have one address, simply
-                        // resend that exact same query
-                        // to that address and yield, when it
-                        // returns, loop again.
-                        
-                        // should use NSAS
-                        zone_servers_.push_back(addr_t(addr_str, 53));
-                        found_ns_address = true;
-                    }
-                }
-            }
-            if (found_ns_address) {
-                // next resolver round
-                send();
-                return false;
-            } else {
-                dlog("[XX] no ready-made addresses in additional. need nsas.");
-                // this will result in answering with the delegation. oh well
-                copyAnswerMessage(incoming, answer_message_);
-                return true;
-            }
-        }
-    }
-    
-
-public:
-    RunningQuery(asio::io_service& io, const Question &question,
-        MessagePtr answer_message, shared_ptr<AddressVector> upstream,
-        shared_ptr<AddressVector> upstream_root,
-        OutputBufferPtr buffer,
-        isc::resolve::ResolverInterface::CallbackPtr cb,
-        int query_timeout, int client_timeout, int lookup_timeout,
-        unsigned retries) :
-        io_(io),
-        question_(question),
-        answer_message_(answer_message),
-        upstream_(upstream),
-        upstream_root_(upstream_root),
-        buffer_(buffer),
-        resolvercallback_(cb),
-        query_timeout_(query_timeout),
-        retries_(retries),
-        client_timer(io),
-        lookup_timer(io),
-        queries_out_(0),
-        done_(false)
-    {
-        // Setup the timer to stop trying (lookup_timeout)
-        if (lookup_timeout >= 0) {
-            lookup_timer.expires_from_now(
-                boost::posix_time::milliseconds(lookup_timeout));
-            lookup_timer.async_wait(boost::bind(&RunningQuery::stop, this, false));
-        }
-        
-        // Setup the timer to send an answer (client_timeout)
-        if (client_timeout >= 0) {
-            client_timer.expires_from_now(
-                boost::posix_time::milliseconds(client_timeout));
-            client_timer.async_wait(boost::bind(&RunningQuery::clientTimeout, this));
-        }
-        
-        // should use NSAS for root servers
-        // Adding root servers if not a forwarder
-        if (upstream_->empty()) {
-            if (upstream_root_->empty()) { //if no root ips given, use this
-                zone_servers_.push_back(addr_t("192.5.5.241", 53));
-            }
-            else
-            {
-              //copy the list
-              dlog("Size is " + 
-                    boost::lexical_cast<string>(upstream_root_->size()) + 
-                    "\n");
-              //Use BOOST_FOREACH here? Is it faster?
-              for(AddressVector::iterator it = upstream_root_->begin();
-                   it < upstream_root_->end(); it++) {
-                zone_servers_.push_back(addr_t(it->first,it->second));
-                dlog("Put " + zone_servers_.back().first + "into root list\n");
-              }
-            }
-        }
-
-        send();
-    }
-
-    virtual void clientTimeout() {
-        // right now, just stop (should make SERVFAIL and send that
-        // back, but not stop)
-        stop(false);
-    }
-
-    virtual void stop(bool resume) {
-        // if we cancel our timers, we will still get an event for
-        // that, so we cannot delete ourselves just yet (those events
-        // would be bound to a deleted object)
-        // cancel them one by one, both cancels should get us back
-        // here again.
-        // same goes if we have an outstanding query (can't delete
-        // until that one comes back to us)
-        done_ = true;
-        if (resume) {
-            resolvercallback_->success(answer_message_);
-        } else {
-            resolvercallback_->failure();
-        }
-        if (lookup_timer.cancel() != 0) {
-            return;
-        }
-        if (client_timer.cancel() != 0) {
-            return;
-        }
-        if (queries_out_ > 0) {
-            return;
-        }
-        delete this;
-    }
-
-    // This function is used as callback from DNSQuery.
-    virtual void operator()(UDPQuery::Result result) {
-        // XXX is this the place for TCP retry?
-        --queries_out_;
-        if (!done_ && result != UDPQuery::TIME_OUT) {
-            // we got an answer
-            Message incoming(Message::PARSE);
-            InputBuffer ibuf(buffer_->getData(), buffer_->getLength());
-            incoming.fromWire(ibuf);
-
-            if (upstream_->size() == 0 &&
-                incoming.getRcode() == Rcode::NOERROR()) {
-                done_ = handleRecursiveAnswer(incoming);
-            } else {
-                copyAnswerMessage(incoming, answer_message_);
-                done_ = true;
-            }
-            
-            if (done_) {
-                stop(true);
-            }
-        } else if (!done_ && retries_--) {
-            // We timed out, but we have some retries, so send again
-            dlog("Timeout, resending query");
-            send();
-        } else {
-            // out of retries, give up for now
-            stop(false);
-        }
-    }
-};
-
-}
-
-void
-RecursiveQuery::resolve(const isc::dns::QuestionPtr& question,
-    const isc::resolve::ResolverInterface::CallbackPtr callback)
-{
-    asio::io_service& io = dns_service_.get_io_service();
-
-    MessagePtr answer_message(new Message(Message::RENDER));
-    OutputBufferPtr buffer(new OutputBuffer(0));
-    
-    // It will delete itself when it is done
-    new RunningQuery(io, *question, answer_message, upstream_,
-                     upstream_root_, buffer, callback, query_timeout_,
-                     client_timeout_, lookup_timeout_, retries_);
-}
-
-void
-RecursiveQuery::resolve(const Question& question,
-                        MessagePtr answer_message,
-                        OutputBufferPtr buffer,
-                        DNSServer* server)
-{
-    // XXX: eventually we will need to be able to determine whether
-    // the message should be sent via TCP or UDP, or sent initially via
-    // UDP and then fall back to TCP on failure, but for the moment
-    // we're only going to handle UDP.
-    asio::io_service& io = dns_service_.get_io_service();
-
-    isc::resolve::ResolverInterface::CallbackPtr crs(
-        new isc::resolve::ResolverCallbackServer(server));
-    
-    // It will delete itself when it is done
-    new RunningQuery(io, question, answer_message, upstream_, upstream_root_,
-                         buffer, crs, query_timeout_, client_timeout_,
-                         lookup_timeout_, retries_);
-}
-
-class IntervalTimerImpl {
-private:
-    // prohibit copy
-    IntervalTimerImpl(const IntervalTimerImpl& source);
-    IntervalTimerImpl& operator=(const IntervalTimerImpl& source);
-public:
-    IntervalTimerImpl(IOService& io_service);
-    ~IntervalTimerImpl();
-    void setup(const IntervalTimer::Callback& cbfunc, const long interval);
-    void callback(const asio::error_code& error);
-    void cancel() {
-        timer_.cancel();
-        interval_ = 0;
-    }
-    long getInterval() const { return (interval_); }
-private:
-    // a function to update timer_ when it expires
-    void update();
-    // a function to call back when timer_ expires
-    IntervalTimer::Callback cbfunc_;
-    // interval in milliseconds
-    long interval_;
-    // asio timer
-    asio::deadline_timer timer_;
-};
-
-IntervalTimerImpl::IntervalTimerImpl(IOService& io_service) :
-    interval_(0), timer_(io_service.get_io_service())
-{}
-
-IntervalTimerImpl::~IntervalTimerImpl()
-{}
-
-void
-IntervalTimerImpl::setup(const IntervalTimer::Callback& cbfunc,
-                         const long interval)
-{
-    // Interval should not be less than or equal to 0.
-    if (interval <= 0) {
-        isc_throw(isc::BadValue, "Interval should not be less than or "
-                                 "equal to 0");
-    }
-    // Call back function should not be empty.
-    if (cbfunc.empty()) {
-        isc_throw(isc::InvalidParameter, "Callback function is empty");
-    }
-    cbfunc_ = cbfunc;
-    interval_ = interval;
-    // Set initial expire time.
-    // At this point the timer is not running yet and will not expire.
-    // After calling IOService::run(), the timer will expire.
-    update();
-    return;
-}
-
-void
-IntervalTimerImpl::update() {
-    if (interval_ == 0) {
-        // timer has been canceled.  Do nothing.
-        return;
-    }
-    try {
-        // Update expire time to (current time + interval_).
-        timer_.expires_from_now(boost::posix_time::millisec(interval_));
-    } catch (const asio::system_error& e) {
-        isc_throw(isc::Unexpected, "Failed to update timer");
-    }
-    // Reset timer.
-    timer_.async_wait(boost::bind(&IntervalTimerImpl::callback, this, _1));
-}
-
-void
-IntervalTimerImpl::callback(const asio::error_code& cancelled) {
-    // Do not call cbfunc_ in case the timer was cancelled.
-    // The timer will be canelled in the destructor of asio::deadline_timer.
-    if (!cancelled) {
-        cbfunc_();
-        // Set next expire time.
-        update();
-    }
-}
-
-IntervalTimer::IntervalTimer(IOService& io_service) {
-    impl_ = new IntervalTimerImpl(io_service);
-}
-
-IntervalTimer::~IntervalTimer() {
-    delete impl_;
-}
-
-void
-IntervalTimer::setup(const Callback& cbfunc, const long interval) {
-    return (impl_->setup(cbfunc, interval));
-}
-
-void
-IntervalTimer::cancel() {
-    impl_->cancel();
-}
-
-long
-IntervalTimer::getInterval() const {
-    return (impl_->getInterval());
-}
-
-}
diff --git a/src/lib/asiolink/asiolink.h b/src/lib/asiolink/asiolink.h
index 3b3fa93..43a1244 100644
--- a/src/lib/asiolink/asiolink.h
+++ b/src/lib/asiolink/asiolink.h
@@ -18,34 +18,20 @@
 // IMPORTANT NOTE: only very few ASIO headers files can be included in
 // this file.  In particular, asio.hpp should never be included here.
 // See the description of the namespace below.
-#include <unistd.h>             // for some network system calls
-#include <asio/ip/address.hpp>
-#include <boost/shared_ptr.hpp>
-#include <boost/function.hpp>
 
-#include <functional>
-#include <string>
-#include <vector>
-#include <utility>
+#include <asiolink/io_service.h>
+#include <asiolink/dns_service.h>
+#include <asiolink/dns_server.h>
+#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 <dns/buffer.h>
-#include <dns/message.h>
-#include <dns/question.h>
-#include <dns/rcode.h>
-
-#include <exceptions/exceptions.h>
-
-#include <asiolink/ioaddress.h>
-#include <asiolink/ioendpoint.h>
-#include <asiolink/iomessage.h>
-#include <asiolink/iosocket.h>
-
-#include <resolve/resolver_interface.h>
-
-namespace asio {
-// forward declaration for IOService::get_io_service() below
-class io_service;
-}
+#include <asiolink/io_address.h>
+#include <asiolink/io_endpoint.h>
+#include <asiolink/io_message.h>
+#include <asiolink/io_socket.h>
 
 /// \namespace asiolink
 /// \brief A wrapper interface for the ASIO library.
@@ -98,9 +84,6 @@ class io_service;
 /// http://think-async.com/Asio/asio-1.3.1/doc/asio/reference/asio_handler_allocate.html
 
 namespace asiolink {
-class DNSServiceImpl;
-struct IOServiceImpl;
-struct IntervalTimerImpl;
 
 
 /// \brief An exception that is thrown if an error occurs within the IO
@@ -112,609 +95,6 @@ public:
         isc::Exception(file, line, what) {}
 };
 
-/// \brief Forward declarations for classes used below
-class SimpleCallback;
-class DNSLookup;
-class DNSAnswer;
-
-/// \brief The \c IOService class is a wrapper for the ASIO \c io_service
-/// class.
-///
-class IOService {
-    ///
-    /// \name Constructors and Destructor
-    ///
-    /// Note: The copy constructor and the assignment operator are
-    /// intentionally defined as private, making this class non-copyable.
-    //@{
-private:
-    IOService(const IOService& source);
-    IOService& operator=(const IOService& source);
-public:
-    /// \brief The constructor
-    IOService();
-    /// \brief The destructor.
-    ~IOService();
-    //@}
-
-    /// \brief Start the underlying event loop.
-    ///
-    /// This method does not return control to the caller until
-    /// the \c stop() method is called via some handler.
-    void run();
-
-    /// \brief Run the underlying event loop for a single event.
-    ///
-    /// This method return control to the caller as soon as the
-    /// first handler has completed.  (If no handlers are ready when
-    /// it is run, it will block until one is.)
-    void run_one();
-
-    /// \brief Stop the underlying event loop.
-    ///
-    /// This will return the control to the caller of the \c run() method.
-    void stop();
-
-    /// \brief Return the native \c io_service object used in this wrapper.
-    ///
-    /// This is a short term work around to support other BIND 10 modules
-    /// that share the same \c io_service with the authoritative server.
-    /// It will eventually be removed once the wrapper interface is
-    /// generalized.
-    asio::io_service& get_io_service();
-
-private:
-    IOServiceImpl* io_impl_;
-};
-
-///
-/// 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
-    ///
-    /// Note: The copy constructor and the assignment operator are
-    /// intentionally defined as private, making this class non-copyable.
-    //@{
-private:
-    DNSService(const DNSService& source);
-    DNSService& operator=(const DNSService& source);
-
-public:
-    /// \brief The constructor with a specific IP address and port on which
-    /// the services listen on.
-    ///
-    /// \param io_service The IOService to work with
-    /// \param port the port to listen on
-    /// \param address the IP address to listen on
-    /// \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)
-    DNSService(IOService& io_service, const char& port,
-               const char& address, SimpleCallback* checkin,
-               DNSLookup* lookup, DNSAnswer* answer);
-    /// \brief The constructor with a specific port on which the services
-    /// listen on.
-    ///
-    /// It effectively listens on "any" IPv4 and/or IPv6 addresses.
-    /// IPv4/IPv6 services will be available if and only if \c use_ipv4
-    /// or \c use_ipv6 is \c true, respectively.
-    ///
-    /// \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 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)
-    DNSService(IOService& io_service, const char& port,
-               const bool use_ipv4, const bool use_ipv6,
-               SimpleCallback* checkin, DNSLookup* lookup,
-               DNSAnswer* answer);
-    /// \brief The constructor without any servers.
-    ///
-    /// Use addServer() to add some servers.
-    DNSService(IOService& io_service, SimpleCallback* checkin,
-               DNSLookup* lookup, DNSAnswer* answer);
-    /// \brief The destructor.
-    ~DNSService();
-    //@}
-
-    /// \brief Add another server to the service
-    void addServer(uint16_t port, const std::string &address);
-    void addServer(const char &port, const std::string &address);
-    /// \brief Remove all servers from the service
-    void clearServers();
-
-    /// \brief Return the native \c io_service object used in this wrapper.
-    ///
-    /// This is a short term work around to support other BIND 10 modules
-    /// that share the same \c io_service with the authoritative server.
-    /// It will eventually be removed once the wrapper interface is
-    /// generalized.
-    asio::io_service& get_io_service() { return io_service_.get_io_service(); }
-private:
-    DNSServiceImpl* impl_;
-    IOService& io_service_;
-};
-
-/// \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.
-/// Those functions are described in more detail below under
-/// \c SimpleCallback, \c DNSLookup, and \c DNSAnswer.
-///
-/// Notes to developers:
-/// When constructed, this class (and its derived classes) will have its
-/// "self_" member set to point to "this".  Objects of this class (as
-/// instantiated through a base class) are sometimes passed by
-/// reference (as this superclass); calls to methods in the base
-/// class are then rerouted via this pointer to methods in the derived
-/// class.  This allows code from outside asiolink, with no specific
-/// knowledge of \c TCPServer or \c UDPServer, to access their methods.
-///
-/// This class is both assignable and copy-constructable.  Its subclasses
-/// use the "stackless coroutine" pattern, meaning that it will copy itself
-/// 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 
-/// that derived classes be kept small to reduce copy overhead.
-class DNSServer {
-protected: 
-    ///
-    /// \name Constructors and destructors
-    ///
-    /// This is intentionally defined as \c protected, as this base class
-    /// should never be instantiated except as part of a derived class.
-    //@{
-    DNSServer() : self_(this) {}
-public:
-    /// \brief The destructor
-    virtual ~DNSServer() {}
-    //@}
-
-    ///
-    /// \name Class methods
-    ///
-    /// These methods all make their calls indirectly via the "self_"
-    /// pointer, ensuring that the functions ultimately invoked will be
-    /// 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(),
-                            size_t length = 0)
-    {
-        (*self_)(ec, length);
-    }
-
-    /// \brief Resume processing of the server coroutine after an 
-    /// asynchronous call (e.g., to the DNS Lookup provider) has completed.
-    ///
-    /// \param done If true, this signals the system there is an answer
-    ///             to return.
-    virtual void resume(const bool done) { self_->resume(done); }
-
-    /// \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()); }
-
-    /// \brief Returns the current value of the 'coroutine' object
-    ///
-    /// This is a temporary method, intended to be used for debugging
-    /// purposes during development and removed later.  It allows
-    /// callers from outside the coroutine object to retrieve information
-    /// about its current state.
-    ///
-    /// \return The value of the 'coroutine' object
-    virtual int value() { return (self_->value()); }
-
-    /// \brief Returns a pointer to a clone of this DNSServer object.
-    ///
-    /// When a \c DNSServer object is copied or assigned, the result will
-    /// normally be another \c DNSServer object containing a copy
-    /// of the original "self_" pointer.  Calling clone() guarantees
-    /// that the underlying object is also correctly copied.
-    ///
-    /// \return A deep copy of this DNSServer object
-    virtual DNSServer* clone() { return (self_->clone()); }
-    //@}
-
-protected:
-    /// \brief Lookup handler object.
-    ///
-    /// This is a protected class; it can only be instantiated
-    /// from within a derived class of \c DNSServer.
-    ///
-    /// A server object that has received a query creates an instance
-    /// of this class and scheudles it on the ASIO service queue
-    /// using asio::io_service::post().  When the handler executes, it
-    /// calls the asyncLookup() method in the server object to start a
-    /// DNS lookup.  When the lookup is complete, the server object is
-    /// scheduled to resume, again using io_service::post().
-    ///
-    /// Note that the calling object is copied into the handler object,
-    /// not referenced.  This is because, once the calling object yields
-    /// control to the handler, it falls out of scope and may disappear
-    template <typename T>
-    class AsyncLookup {
-    public:
-        AsyncLookup(T& caller) : caller_(caller) {}
-        void operator()() { caller_.asyncLookup(); }
-    private:
-        T caller_;
-    };
-
-    /// \brief Carries out a DNS lookup.
-    ///
-    /// This function calls the \c DNSLookup object specified by the
-    /// DNS server when the \c IOService was created, passing along
-    /// the details of the query and a pointer back to the current
-    /// server object.  It is called asynchronously via the AsyncLookup
-    /// handler class.
-    virtual void asyncLookup() { self_->asyncLookup(); }
-
-private:
-    DNSServer* self_;
-};
-
-/// \brief The \c DNSLookup class is an abstract base class for a DNS
-/// Lookup provider function.
-///
-/// Specific derived class implementations are hidden within the
-/// implementation.  Instances of the derived classes can be called
-/// as functions via the operator() interface.  Pointers to these
-/// instances can then be provided to the \c IOService class
-/// via its constructor.
-///
-/// A DNS Lookup provider function obtains the data needed to answer
-/// a DNS query (e.g., from authoritative data source, cache, or upstream
-/// query).  After it has run, the OutputBuffer object passed to it
-/// should contain the answer to the query, in an internal representation.
-class DNSLookup {
-    ///
-    /// \name Constructors and Destructor
-    ///
-    /// Note: The copy constructor and the assignment operator are
-    /// intentionally defined as private, making this class non-copyable.
-    //@{
-private:
-    DNSLookup(const DNSLookup& source);
-    DNSLookup& operator=(const DNSLookup& source);
-protected:
-    /// \brief The default constructor.
-    ///
-    /// This is intentionally defined as \c protected as this base class
-    /// should never be instantiated (except as part of a derived class).
-    DNSLookup() : self_(this) {}
-public:
-    /// \brief The destructor
-    virtual ~DNSLookup() {}
-    //@}
-    /// \brief The function operator
-    ///
-    /// This makes its call indirectly via the "self" pointer, ensuring
-    /// that the function ultimately invoked will be the one in the derived
-    /// class.
-    ///
-    /// \param io_message The event message to handle
-    /// \param message The DNS MessagePtr that needs handling
-    /// \param buffer The final answer is put here
-    /// \param DNSServer DNSServer object to use
-    virtual void operator()(const IOMessage& io_message,
-                            isc::dns::MessagePtr message,
-                            isc::dns::MessagePtr answer_message,
-                            isc::dns::OutputBufferPtr buffer,
-                            DNSServer* server) const
-    {
-        (*self_)(io_message, message, answer_message, buffer, server);
-    }
-private:
-    DNSLookup* self_;
-};
-
-/// \brief The \c DNSAnswer class is an abstract base class for a DNS
-/// Answer provider function.
-///
-/// Specific derived class implementations are hidden within the
-/// implementation.  Instances of the derived classes can be called
-/// as functions via the operator() interface.  Pointers to these
-/// instances can then be provided to the \c IOService class
-/// via its constructor.
-///
-/// A DNS Answer provider function takes answer data that has been obtained
-/// from a DNS Lookup provider functon and readies it to be sent to the
-/// client.  After it has run, the OutputBuffer object passed to it should
-/// contain the answer to the query rendered into wire format.
-class DNSAnswer {
-    ///
-    /// \name Constructors and Destructor
-    ///
-    /// Note: The copy constructor and the assignment operator are
-    /// intentionally defined as private, making this class non-copyable.
-    //@{
-private:
-    DNSAnswer(const DNSAnswer& source);
-    DNSAnswer& operator=(const DNSAnswer& source);
-protected:
-    /// \brief The default constructor.
-    ///
-    /// This is intentionally defined as \c protected as this base class
-    /// should never be instantiated (except as part of a derived class).
-    DNSAnswer() {}
-public:
-    /// \brief The destructor
-    virtual ~DNSAnswer() {}
-    //@}
-    /// \brief The function operator
-    ///
-    /// This makes its call indirectly via the "self" pointer, ensuring
-    /// that the function ultimately invoked will be the one in the derived
-    /// class.
-    ///
-    /// \param io_message The event message to handle
-    /// \param message The DNS MessagePtr that needs handling
-    /// \param buffer The result is put here
-    virtual void operator()(const IOMessage& io_message,
-                            isc::dns::MessagePtr message,
-                            isc::dns::MessagePtr answer_message,
-                            isc::dns::OutputBufferPtr buffer) const = 0;
-};
-
-/// \brief The \c SimpleCallback class is an abstract base class for a
-/// simple callback function with the signature:
-///
-/// void simpleCallback(const IOMessage& io_message) const;
-///
-/// Specific derived class implementations are hidden within the
-/// implementation.  Instances of the derived classes can be called
-/// as functions via the operator() interface.  Pointers to these
-/// instances can then be provided to the \c IOService class
-/// via its constructor.
-///
-/// The \c SimpleCallback is expected to be used for basic, generic
-/// tasks such as checking for configuration changes.  It may also be
-/// used for testing purposes.
-class SimpleCallback {
-    ///
-    /// \name Constructors and Destructor
-    ///
-    /// Note: The copy constructor and the assignment operator are
-    /// intentionally defined as private, making this class non-copyable.
-    //@{
-private:
-    SimpleCallback(const SimpleCallback& source);
-    SimpleCallback& operator=(const SimpleCallback& source);
-protected:
-    /// \brief The default constructor.
-    ///
-    /// This is intentionally defined as \c protected as this base class
-    /// should never be instantiated (except as part of a derived class).
-    SimpleCallback() : self_(this) {}
-public:
-    /// \brief The destructor
-    virtual ~SimpleCallback() {}
-    /// \brief The function operator
-    //@}
-    ///
-    /// This makes its call indirectly via the "self" pointer, ensuring
-    /// that the function ultimately invoked will be the one in the derived
-    /// class.
-    ///
-    /// \param io_message The event message to handle
-    virtual void operator()(const IOMessage& io_message) const {
-        (*self_)(io_message);
-    }
-private:
-    SimpleCallback* self_;
-};
-
-/// \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 timeout How long to timeout the query, in ms
-    ///     -1 means never timeout (but do not use that).
-    ///     TODO: This should be computed somehow dynamically in future
-    /// \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_;
-};
-
-/// \brief The \c IntervalTimer class is a wrapper for the ASIO
-/// \c asio::deadline_timer class.
-///
-/// This class is implemented to use \c asio::deadline_timer as interval
-/// timer.
-///
-/// \c setup() sets a timer to expire on (now + interval) and a call back
-/// function.
-///
-/// \c IntervalTimerImpl::callback() is called by the timer when it expires.
-///
-/// 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.
-///
-/// Note: Destruction of an instance of this class while call back is pending
-/// causes throwing an exception from \c IOService.
-///
-/// Sample code:
-/// \code
-///  void function_to_call_back() {
-///      // this function will be called periodically
-///  }
-///  int interval_in_milliseconds = 1000;
-///  IOService io_service;
-///
-///  IntervalTimer intervalTimer(io_service);
-///  intervalTimer.setup(function_to_call_back, interval_in_milliseconds);
-///  io_service.run();
-/// \endcode
-class IntervalTimer {
-public:
-    /// \name The type of timer callback function
-    typedef boost::function<void()> Callback;
-
-    ///
-    /// \name Constructors and Destructor
-    ///
-    /// Note: The copy constructor and the assignment operator are
-    /// intentionally defined as private, making this class non-copyable.
-    //@{
-private:
-    IntervalTimer(const IntervalTimer& source);
-    IntervalTimer& operator=(const IntervalTimer& source);
-public:
-    /// \brief The constructor with \c IOService.
-    ///
-    /// This constructor may throw a standard exception if
-    /// memory allocation fails inside the method.
-    /// This constructor may also throw \c asio::system_error.
-    ///
-    /// \param io_service A reference to an instance of IOService
-    IntervalTimer(IOService& io_service);
-
-    /// \brief The destructor.
-    ///
-    /// This destructor never throws an exception.
-    ///
-    /// On the destruction of this class the timer will be canceled
-    /// inside \c asio::deadline_timer.
-    ~IntervalTimer();
-    //@}
-
-    /// \brief Register timer callback function and interval.
-    ///
-    /// This function sets callback function and interval in milliseconds.
-    /// Timer will actually start after calling \c IOService::run().
-    ///
-    /// \param cbfunc A reference to a function \c void(void) to call back
-    /// when the timer is expired (should not be an empty functor)
-    /// \param interval Interval in milliseconds (greater than 0)
-    ///
-    /// Note: IntervalTimer will not pass \c asio::error_code to
-    /// call back function. In case the timer is cancelled, the function
-    /// will not be called.
-    ///
-    /// \throw isc::InvalidParameter cbfunc is empty
-    /// \throw isc::BadValue interval is less than or equal to 0
-    /// \throw isc::Unexpected ASIO library error
-    void setup(const Callback& cbfunc, const long interval);
-
-    /// Cancel the timer.
-    ///
-    /// If the timer has been set up, this method cancels any asynchronous
-    /// events waiting on the timer and stops the timer itself.
-    /// If the timer has already been canceled, this method effectively does
-    /// nothing.
-    ///
-    /// This method never throws an exception.
-    void cancel();
-
-    /// Return the timer interval.
-    ///
-    /// This method returns the timer interval in milliseconds if it's running;
-    /// if the timer has been canceled it returns 0.
-    ///
-    /// This method never throws an exception.
-    long getInterval() const;
-
-private:
-    IntervalTimerImpl* impl_;
-};
 
 }      // asiolink
 #endif // __ASIOLINK_H
diff --git a/src/lib/asiolink/dns_answer.h b/src/lib/asiolink/dns_answer.h
new file mode 100644
index 0000000..84e1f6f
--- /dev/null
+++ b/src/lib/asiolink/dns_answer.h
@@ -0,0 +1,73 @@
+// 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_DNS_ANSWER_H
+#define __ASIOLINK_DNS_ANSWER_H 1
+
+#include <asiolink/io_message.h>
+
+namespace asiolink {
+
+/// \brief The \c DNSAnswer class is an abstract base class for a DNS
+/// Answer provider function.
+///
+/// Specific derived class implementations are hidden within the
+/// implementation.  Instances of the derived classes can be called
+/// as functions via the operator() interface.  Pointers to these
+/// instances can then be provided to the \c IOService class
+/// via its constructor.
+///
+/// A DNS Answer provider function takes answer data that has been obtained
+/// from a DNS Lookup provider functon and readies it to be sent to the
+/// client.  After it has run, the OutputBuffer object passed to it should
+/// contain the answer to the query rendered into wire format.
+class DNSAnswer {
+    ///
+    /// \name Constructors and Destructor
+    ///
+    /// Note: The copy constructor and the assignment operator are
+    /// intentionally defined as private, making this class non-copyable.
+    //@{
+private:
+    DNSAnswer(const DNSAnswer& source);
+    DNSAnswer& operator=(const DNSAnswer& source);
+protected:
+    /// \brief The default constructor.
+    ///
+    /// This is intentionally defined as \c protected as this base class
+    /// should never be instantiated (except as part of a derived class).
+    DNSAnswer() {}
+public:
+    /// \brief The destructor
+    virtual ~DNSAnswer() {}
+    //@}
+    /// \brief The function operator
+    ///
+    /// This makes its call indirectly via the "self" pointer, ensuring
+    /// that the function ultimately invoked will be the one in the derived
+    /// class.
+    ///
+    /// \param io_message The event message to handle
+    /// \param query_message The DNS MessagePtr of the original query
+    /// \param answer_message The DNS MessagePtr of the answer we are
+    /// building
+    /// \param buffer Intermediate data results are put here
+    virtual void operator()(const IOMessage& io_message,
+                            isc::dns::MessagePtr query_message,
+                            isc::dns::MessagePtr answer_message,
+                            isc::dns::OutputBufferPtr buffer) const = 0;
+};
+
+}      // namespace asiolink
+#endif // __ASIOLINK_DNS_ANSWER_H
diff --git a/src/lib/asiolink/dns_lookup.h b/src/lib/asiolink/dns_lookup.h
new file mode 100644
index 0000000..0788853
--- /dev/null
+++ b/src/lib/asiolink/dns_lookup.h
@@ -0,0 +1,81 @@
+// 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_DNS_LOOKUP_H
+#define __ASIOLINK_DNS_LOOKUP_H 1
+
+#include <asiolink/io_message.h>
+#include <asiolink/dns_server.h>
+#include <dns/buffer.h>
+#include <dns/message.h>
+
+namespace asiolink {
+
+/// \brief The \c DNSLookup class is an abstract base class for a DNS
+/// Lookup provider function.
+///
+/// Specific derived class implementations are hidden within the
+/// implementation.  Instances of the derived classes can be called
+/// as functions via the operator() interface.  Pointers to these
+/// instances can then be provided to the \c IOService class
+/// via its constructor.
+///
+/// A DNS Lookup provider function obtains the data needed to answer
+/// a DNS query (e.g., from authoritative data source, cache, or upstream
+/// query).  After it has run, the OutputBuffer object passed to it
+/// should contain the answer to the query, in an internal representation.
+class DNSLookup {
+    ///
+    /// \name Constructors and Destructor
+    ///
+    /// Note: The copy constructor and the assignment operator are
+    /// intentionally defined as private, making this class non-copyable.
+    //@{
+private:
+    DNSLookup(const DNSLookup& source);
+    DNSLookup& operator=(const DNSLookup& source);
+protected:
+    /// \brief The default constructor.
+    ///
+    /// This is intentionally defined as \c protected as this base class
+    /// should never be instantiated (except as part of a derived class).
+    DNSLookup() : self_(this) {}
+public:
+    /// \brief The destructor
+    virtual ~DNSLookup() {}
+    //@}
+    /// \brief The function operator
+    ///
+    /// This makes its call indirectly via the "self" pointer, ensuring
+    /// that the function ultimately invoked will be the one in the derived
+    /// class.
+    ///
+    /// \param io_message The event message to handle
+    /// \param message The DNS MessagePtr that needs handling
+    /// \param buffer The final answer is put here
+    /// \param DNSServer DNSServer object to use
+    virtual void operator()(const IOMessage& io_message,
+                            isc::dns::MessagePtr message,
+                            isc::dns::MessagePtr answer_message,
+                            isc::dns::OutputBufferPtr buffer,
+                            DNSServer* server) const
+    {
+        (*self_)(io_message, message, answer_message, buffer, server);
+    }
+private:
+    DNSLookup* self_;
+};
+
+}      // namespace asiolink
+#endif // __ASIOLINK_DNS_LOOKUP_H
diff --git a/src/lib/asiolink/dns_server.h b/src/lib/asiolink/dns_server.h
new file mode 100644
index 0000000..6545275
--- /dev/null
+++ b/src/lib/asiolink/dns_server.h
@@ -0,0 +1,152 @@
+// 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_DNS_SERVER_H
+#define __ASIOLINK_DNS_SERVER_H 1
+
+#include <asiolink/io_message.h>
+
+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.
+/// Those functions are described in more detail below under
+/// \c SimpleCallback, \c DNSLookup, and \c DNSAnswer.
+///
+/// Notes to developers:
+/// When constructed, this class (and its derived classes) will have its
+/// "self_" member set to point to "this".  Objects of this class (as
+/// instantiated through a base class) are sometimes passed by
+/// reference (as this superclass); calls to methods in the base
+/// class are then rerouted via this pointer to methods in the derived
+/// class.  This allows code from outside asiolink, with no specific
+/// knowledge of \c TCPServer or \c UDPServer, to access their methods.
+///
+/// This class is both assignable and copy-constructable.  Its subclasses
+/// use the "stackless coroutine" pattern, meaning that it will copy itself
+/// 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 
+/// that derived classes be kept small to reduce copy overhead.
+class DNSServer {
+protected: 
+    ///
+    /// \name Constructors and destructors
+    ///
+    /// This is intentionally defined as \c protected, as this base class
+    /// should never be instantiated except as part of a derived class.
+    //@{
+    DNSServer() : self_(this) {}
+public:
+    /// \brief The destructor
+    virtual ~DNSServer() {}
+    //@}
+
+    ///
+    /// \name Class methods
+    ///
+    /// These methods all make their calls indirectly via the "self_"
+    /// pointer, ensuring that the functions ultimately invoked will be
+    /// 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(),
+                            size_t length = 0)
+    {
+        (*self_)(ec, length);
+    }
+
+    /// \brief Resume processing of the server coroutine after an 
+    /// asynchronous call (e.g., to the DNS Lookup provider) has completed.
+    ///
+    /// \param done If true, this signals the system there is an answer
+    ///             to return.
+    virtual void resume(const bool done) { self_->resume(done); }
+
+    /// \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()); }
+
+    /// \brief Returns the current value of the 'coroutine' object
+    ///
+    /// This is a temporary method, intended to be used for debugging
+    /// purposes during development and removed later.  It allows
+    /// callers from outside the coroutine object to retrieve information
+    /// about its current state.
+    ///
+    /// \return The value of the 'coroutine' object
+    virtual int value() { return (self_->value()); }
+
+    /// \brief Returns a pointer to a clone of this DNSServer object.
+    ///
+    /// When a \c DNSServer object is copied or assigned, the result will
+    /// normally be another \c DNSServer object containing a copy
+    /// of the original "self_" pointer.  Calling clone() guarantees
+    /// that the underlying object is also correctly copied.
+    ///
+    /// \return A deep copy of this DNSServer object
+    virtual DNSServer* clone() { return (self_->clone()); }
+    //@}
+
+protected:
+    /// \brief Lookup handler object.
+    ///
+    /// This is a protected class; it can only be instantiated
+    /// from within a derived class of \c DNSServer.
+    ///
+    /// A server object that has received a query creates an instance
+    /// of this class and scheudles it on the ASIO service queue
+    /// using asio::io_service::post().  When the handler executes, it
+    /// calls the asyncLookup() method in the server object to start a
+    /// DNS lookup.  When the lookup is complete, the server object is
+    /// scheduled to resume, again using io_service::post().
+    ///
+    /// Note that the calling object is copied into the handler object,
+    /// not referenced.  This is because, once the calling object yields
+    /// control to the handler, it falls out of scope and may disappear
+    template <typename T>
+    class AsyncLookup {
+    public:
+        AsyncLookup(T& caller) : caller_(caller) {}
+        void operator()() { caller_.asyncLookup(); }
+    private:
+        T caller_;
+    };
+
+    /// \brief Carries out a DNS lookup.
+    ///
+    /// This function calls the \c DNSLookup object specified by the
+    /// DNS server when the \c IOService was created, passing along
+    /// the details of the query and a pointer back to the current
+    /// server object.  It is called asynchronously via the AsyncLookup
+    /// handler class.
+    virtual void asyncLookup() { self_->asyncLookup(); }
+
+private:
+    DNSServer* self_;
+};
+
+
+}      // asiolink
+#endif // __ASIOLINK_DNS_SERVER_H
diff --git a/src/lib/asiolink/dns_service.cc b/src/lib/asiolink/dns_service.cc
new file mode 100644
index 0000000..98ca032
--- /dev/null
+++ b/src/lib/asiolink/dns_service.cc
@@ -0,0 +1,192 @@
+// 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>
+
+// unistd is needed for asio.hpp with SunStudio
+#include <unistd.h>
+
+#include <asio.hpp>
+
+#include <asiolink/io_service.h>
+#include <asiolink/tcp_server.h>
+#include <asiolink/udp_server.h>
+
+#include <log/dummylog.h>
+
+#include <boost/lexical_cast.hpp>
+
+using isc::log::dlog;
+
+namespace asiolink {
+
+class SimpleCallback;
+class DNSLookup;
+class DNSAnswer;
+
+namespace {
+
+asio::ip::address
+convertAddr(const std::string& address) {
+    asio::error_code err;
+    asio::ip::address addr = asio::ip::address::from_string(address, err);
+    if (err) {
+        isc_throw(IOError, "Invalid IP address '" << &address << "': "
+            << err.message());
+    }
+    return (addr);
+}
+
+}
+
+
+class DNSServiceImpl {
+public:
+    DNSServiceImpl(IOService& io_service, const char& port,
+                  const asio::ip::address* v4addr,
+                  const asio::ip::address* v6addr,
+                  SimpleCallback* checkin, DNSLookup* lookup,
+                  DNSAnswer* answer);
+
+    IOService& io_service_;
+
+    typedef boost::shared_ptr<UDPServer> UDPServerPtr;
+    typedef boost::shared_ptr<TCPServer> TCPServerPtr;
+    typedef boost::shared_ptr<DNSServer> DNSServerPtr;
+    std::vector<DNSServerPtr> servers_;
+    SimpleCallback *checkin_;
+    DNSLookup *lookup_;
+    DNSAnswer *answer_;
+
+    void addServer(uint16_t port, const asio::ip::address& address) {
+        try {
+            dlog(std::string("Initialize TCP server at ") + address.to_string() + ":" + boost::lexical_cast<std::string>(port));
+            TCPServerPtr tcpServer(new TCPServer(io_service_.get_io_service(),
+                address, port, checkin_, lookup_, answer_));
+            (*tcpServer)();
+            servers_.push_back(tcpServer);
+            dlog(std::string("Initialize UDP server at ") + address.to_string() + ":" + boost::lexical_cast<std::string>(port));
+            UDPServerPtr udpServer(new UDPServer(io_service_.get_io_service(),
+                address, port, checkin_, lookup_, answer_));
+            (*udpServer)();
+            servers_.push_back(udpServer);
+        }
+        catch (const asio::system_error& err) {
+            // We need to catch and convert any ASIO level exceptions.
+            // This can happen for unavailable address, binding a privilege port
+            // without the privilege, etc.
+            isc_throw(IOError, "Failed to initialize network servers: " <<
+                      err.what());
+        }
+    }
+    void addServer(const char& port, const asio::ip::address& address) {
+        uint16_t portnum;
+        try {
+            // XXX: SunStudio with stlport4 doesn't reject some invalid
+            // representation such as "-1" by lexical_cast<uint16_t>, so
+            // we convert it into a signed integer of a larger size and perform
+            // range check ourselves.
+            const int32_t portnum32 = boost::lexical_cast<int32_t>(&port);
+            if (portnum32 < 0 || portnum32 > 65535) {
+                isc_throw(IOError, "Invalid port number '" << &port);
+            }
+            portnum = portnum32;
+        } catch (const boost::bad_lexical_cast& ex) {
+            isc_throw(IOError, "Invalid port number '" << &port << "': " <<
+                      ex.what());
+        }
+        addServer(portnum, address);
+    }
+};
+
+DNSServiceImpl::DNSServiceImpl(IOService& io_service,
+                               const char& port,
+                               const asio::ip::address* const v4addr,
+                               const asio::ip::address* const v6addr,
+                               SimpleCallback* checkin,
+                               DNSLookup* lookup,
+                               DNSAnswer* answer) :
+    io_service_(io_service),
+    checkin_(checkin),
+    lookup_(lookup),
+    answer_(answer)
+{
+
+    if (v4addr) {
+        addServer(port, *v4addr);
+    }
+    if (v6addr) {
+        addServer(port, *v6addr);
+    }
+}
+
+DNSService::DNSService(IOService& io_service,
+                       const char& port, const char& address,
+                       SimpleCallback* checkin,
+                       DNSLookup* lookup,
+                       DNSAnswer* answer) :
+    impl_(new DNSServiceImpl(io_service, port, NULL, NULL, checkin, lookup,
+        answer)), io_service_(io_service)
+{
+    addServer(port, &address);
+}
+
+DNSService::DNSService(IOService& io_service,
+                       const char& port,
+                       const bool use_ipv4, const bool use_ipv6,
+                       SimpleCallback* checkin,
+                       DNSLookup* lookup,
+                       DNSAnswer* answer) :
+    impl_(NULL), io_service_(io_service)
+{
+    const asio::ip::address v4addr_any =
+        asio::ip::address(asio::ip::address_v4::any());
+    const asio::ip::address* const v4addrp = use_ipv4 ? &v4addr_any : NULL; 
+    const asio::ip::address v6addr_any =
+        asio::ip::address(asio::ip::address_v6::any());
+    const asio::ip::address* const v6addrp = use_ipv6 ? &v6addr_any : NULL;
+    impl_ = new DNSServiceImpl(io_service, port, v4addrp, v6addrp, checkin, lookup, answer);
+}
+
+DNSService::DNSService(IOService& io_service, SimpleCallback* checkin,
+    DNSLookup* lookup, DNSAnswer *answer) :
+    impl_(new DNSServiceImpl(io_service, *"0", NULL, NULL, checkin, lookup,
+        answer)), io_service_(io_service)
+{
+}
+
+DNSService::~DNSService() {
+    delete impl_;
+}
+
+void
+DNSService::addServer(const char& port, const std::string& address) {
+    impl_->addServer(port, convertAddr(address));
+}
+
+void
+DNSService::addServer(uint16_t port, const std::string& address) {
+    impl_->addServer(port, convertAddr(address));
+}
+
+void
+DNSService::clearServers() {
+    // FIXME: This does not work, it does not close the socket.
+    // How is it done?
+    impl_->servers_.clear();
+}
+
+
+
+} // namespace asiolink
diff --git a/src/lib/asiolink/dns_service.h b/src/lib/asiolink/dns_service.h
new file mode 100644
index 0000000..6b14345
--- /dev/null
+++ b/src/lib/asiolink/dns_service.h
@@ -0,0 +1,106 @@
+// 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_DNS_SERVICE_H
+#define __ASIOLINK_DNS_SERVICE_H 1
+
+#include <resolve/resolver_interface.h>
+
+#include <asiolink/io_service.h>
+
+namespace asiolink {
+
+class SimpleCallback;
+class DNSLookup;
+class DNSAnswer;
+class DNSServiceImpl;
+
+///
+/// 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
+    ///
+    /// Note: The copy constructor and the assignment operator are
+    /// intentionally defined as private, making this class non-copyable.
+    //@{
+private:
+    DNSService(const DNSService& source);
+    DNSService& operator=(const DNSService& source);
+
+public:
+    /// \brief The constructor with a specific IP address and port on which
+    /// the services listen on.
+    ///
+    /// \param io_service The IOService to work with
+    /// \param port the port to listen on
+    /// \param address the IP address to listen on
+    /// \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)
+    DNSService(IOService& io_service, const char& port,
+               const char& address, SimpleCallback* checkin,
+               DNSLookup* lookup, DNSAnswer* answer);
+    /// \brief The constructor with a specific port on which the services
+    /// listen on.
+    ///
+    /// It effectively listens on "any" IPv4 and/or IPv6 addresses.
+    /// IPv4/IPv6 services will be available if and only if \c use_ipv4
+    /// or \c use_ipv6 is \c true, respectively.
+    ///
+    /// \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 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)
+    DNSService(IOService& io_service, const char& port,
+               const bool use_ipv4, const bool use_ipv6,
+               SimpleCallback* checkin, DNSLookup* lookup,
+               DNSAnswer* answer);
+    /// \brief The constructor without any servers.
+    ///
+    /// Use addServer() to add some servers.
+    DNSService(IOService& io_service, SimpleCallback* checkin,
+               DNSLookup* lookup, DNSAnswer* answer);
+    /// \brief The destructor.
+    ~DNSService();
+    //@}
+
+    /// \brief Add another server to the service
+    void addServer(uint16_t port, const std::string &address);
+    void addServer(const char &port, const std::string &address);
+    /// \brief Remove all servers from the service
+    void clearServers();
+
+    /// \brief Return the native \c io_service object used in this wrapper.
+    ///
+    /// This is a short term work around to support other BIND 10 modules
+    /// that share the same \c io_service with the authoritative server.
+    /// It will eventually be removed once the wrapper interface is
+    /// generalized.
+    asio::io_service& get_io_service() { return io_service_.get_io_service(); }
+private:
+    DNSServiceImpl* impl_;
+    IOService& io_service_;
+};
+
+}      // namespace asiolink
+#endif // __ASIOLINK_DNS_SERVICE_H
diff --git a/src/lib/asiolink/internal/Makefile.am b/src/lib/asiolink/internal/Makefile.am
deleted file mode 100644
index 3c6155b..0000000
--- a/src/lib/asiolink/internal/Makefile.am
+++ /dev/null
@@ -1 +0,0 @@
-SUBDIRS = tests
diff --git a/src/lib/asiolink/internal/coroutine.h b/src/lib/asiolink/internal/coroutine.h
deleted file mode 100644
index 985888b..0000000
--- a/src/lib/asiolink/internal/coroutine.h
+++ /dev/null
@@ -1,133 +0,0 @@
-//
-// coroutine.h
-// ~~~~~~~~~~~
-//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
-//
-// Distributed under the Boost Software License, Version 1.0. (See accompanying
-// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-//
-
-#ifndef COROUTINE_HPP
-#define COROUTINE_HPP
-
-
-// \brief Coroutine object
-//
-// A coroutine object maintains the state of a re-enterable routine.  It
-// is assignable and copy-constructable, and can be used as a base class
-// for a class that uses it, or as a data member.  The copy overhead is
-// a single int.
-//
-// A reenterable function contains a CORO_REENTER (coroutine)  { ... }
-// block.  Whenever an asychrnonous operation is initiated within the
-// routine, the function is provided as the handler object.  (The simplest
-// way to do this is to have the reenterable function be the operator()
-// member for the coroutine object itself.)   For example:
-// 
-//     CORO_YIELD socket->async_read_some(buffer, *this);
-//
-// The CORO_YIELD keyword updates the current status of the coroutine to
-// indicate the line number currently being executed.  The
-// async_read_some() call is initiated, with a copy of the updated
-// corotutine as its handler object, and the current coroutine exits.  When
-// the async_read_some() call finishes, the copied coroutine will be
-// called, and will resume processing exactly where the original one left
-// off--right after asynchronous call.  This allows asynchronous I/O
-// routines to be written with a logical flow, step following step, rather
-// than as a linked chain of separate handler functions.
-//
-// When necessary, a coroutine can fork itself using the CORO_FORK keyword.
-// This updates the status of the coroutine and makes a copy.  The copy can
-// then be called directly or posted to the ASIO service queue so that both
-// coroutines will continue forward, one "parent" and one "child".  The
-// is_parent() and is_child() methods indicate which is which.
-//
-// The CORO_REENTER, CORO_YIELD and CORO_FORK keywords are implemented
-// via preprocessor macros.  The CORO_REENTER block is actually a large,
-// complex switch statement.  Because of this, inline variable declaration
-// is impossible within CORO_REENTER unless it is done in a subsidiary
-// scope--and if it is, that scope cannot contain CORO_YIELD or CORO_FORK
-// keywords.
-//
-// Because coroutines are frequently copied, it is best to minimize copy
-// overhead by limiting the size of data members in derived classes.
-//
-// It should be noted that when a coroutine falls out of scope its memory
-// is reclaimed, even though it may be scheduled to resume when an
-// asynchronous operation completes.  Any shared_ptr<> objects declared in
-// the coroutine may be destroyed if their reference count drops to zero,
-// in which case the coroutine will have serious problems once it resumes.
-// One solution so this is to have the space that will be used by a
-// coroutine pre-allocated and stored on a free list; a new coroutine can
-// fetch the block of space off a free list, place a shared pointer to it
-// on an "in use" list, and carry on.  The reference in the "in use" list
-// would prevent the data from being destroyed.
-class coroutine
-{
-public:
-  coroutine() : value_(0) {}
-  virtual ~coroutine() {}
-  bool is_child() const { return value_ < 0; }
-  bool is_parent() const { return !is_child(); }
-  bool is_complete() const { return value_ == -1; }
-  int get_value() const { return value_; }
-private:
-  friend class coroutine_ref;
-  int value_;
-};
-
-class coroutine_ref
-{
-public:
-  coroutine_ref(coroutine& c) : value_(c.value_), modified_(false) {}
-  coroutine_ref(coroutine* c) : value_(c->value_), modified_(false) {}
-  ~coroutine_ref() { if (!modified_) value_ = -1; }
-  operator int() const { return value_; }
-  int& operator=(int v) { modified_ = true; return value_ = v; }
-private:
-  void operator=(const coroutine_ref&);
-  int& value_;
-  bool modified_;
-};
-
-#define CORO_REENTER(c) \
-  switch (coroutine_ref _coro_value = c) \
-    case -1: if (_coro_value) \
-    { \
-      goto terminate_coroutine; \
-      terminate_coroutine: \
-      _coro_value = -1; \
-      goto bail_out_of_coroutine; \
-      bail_out_of_coroutine: \
-      break; \
-    } \
-    else case 0:
-
-#define CORO_YIELD \
-  for (_coro_value = __LINE__;;) \
-    if (_coro_value == 0) \
-    { \
-      case __LINE__: ; \
-      break; \
-    } \
-    else \
-      switch (_coro_value ? 0 : 1) \
-        for (;;) \
-          case -1: if (_coro_value) \
-            goto terminate_coroutine; \
-          else for (;;) \
-            case 1: if (_coro_value) \
-              goto bail_out_of_coroutine; \
-            else case 0:
-
-#define CORO_FORK \
-  for (_coro_value = -__LINE__;; _coro_value = __LINE__) \
-    if (_coro_value == __LINE__) \
-    { \
-      case -__LINE__: ; \
-      break; \
-    } \
-    else
-#endif // COROUTINE_HPP
-
diff --git a/src/lib/asiolink/internal/tcpdns.h b/src/lib/asiolink/internal/tcpdns.h
deleted file mode 100644
index a97ed17..0000000
--- a/src/lib/asiolink/internal/tcpdns.h
+++ /dev/null
@@ -1,224 +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 __TCPDNS_H
-#define __TCPDNS_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 <asiolink/asiolink.h>
-#include <asiolink/internal/coroutine.h>
-
-// This file contains TCP-specific implementations of generic classes 
-// defined in asiolink.h.  It is *not* intended to be part of the public
-// API.
-
-namespace asiolink {
-/// \brief The \c TCPEndpoint class is a concrete derived class of
-/// \c IOEndpoint that represents an endpoint of a TCP connection.
-///
-/// 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.
-class TCPEndpoint : public IOEndpoint {
-public:
-    ///
-    /// \name Constructors and Destructor
-    ///
-    //@{
-    /// \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)),
-        asio_endpoint_(*asio_endpoint_placeholder_)
-    {}
-
-    /// \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_(NULL), asio_endpoint_(asio_endpoint)
-    {}
-
-    /// \brief The destructor.
-    ~TCPEndpoint() { delete asio_endpoint_placeholder_; }
-    //@}
-
-    IOAddress getAddress() const {
-        return (asio_endpoint_.address());
-    }
-
-    uint16_t getPort() const {
-        return (asio_endpoint_.port());
-    }
-
-    short getProtocol() const {
-        return (asio_endpoint_.protocol().protocol());
-    }
-
-    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 {
-        return (asio_endpoint_);
-    }
-
-private:
-    const asio::ip::tcp::endpoint* asio_endpoint_placeholder_;
-    const asio::ip::tcp::endpoint& asio_endpoint_;
-};
-
-/// \brief The \c TCPSocket class is a concrete derived class of
-/// \c IOSocket that represents a TCP socket.
-///
-/// 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 IOSocket.
-/// This design may be changed when we generalize the wrapper interface.
-class TCPSocket : public IOSocket {
-private:
-    TCPSocket(const TCPSocket& source);
-    TCPSocket& operator=(const TCPSocket& source);
-public:
-    /// \brief Constructor from an ASIO TCP socket.
-    ///
-    /// \param socket The ASIO representation of the TCP socket.
-    TCPSocket(asio::ip::tcp::socket& socket) : socket_(socket) {}
-
-    int getNative() const { return (socket_.native()); }
-    int getProtocol() const { return (IPPROTO_TCP); }
-
-private:
-    asio::ip::tcp::socket& socket_;
-};
-
-/// \brief A TCP-specific \c DNSServer object.
-///
-/// This class inherits from both \c DNSServer and from \c coroutine,
-/// defined in coroutine.h. 
-class TCPServer : public virtual DNSServer, public virtual coroutine {
-public:
-    explicit TCPServer(asio::io_service& io_service,
-                       const asio::ip::address& addr, const uint16_t port, 
-                       const SimpleCallback* checkin = NULL,
-                       const DNSLookup* lookup = NULL,
-                       const DNSAnswer* answer = NULL);
-
-    void operator()(asio::error_code ec = asio::error_code(),
-                    size_t length = 0);
-    void asyncLookup();
-    void resume(const bool done);
-    bool hasAnswer() { return (done_); }
-    int value() { return (get_value()); }
-
-    DNSServer* clone() {
-        TCPServer* s = new TCPServer(*this);
-        return (s);
-    }
-
-private:
-    enum { MAX_LENGTH = 65535 };
-    static const size_t TCP_MESSAGE_LENGTHSIZE = 2;
-
-    // The ASIO service object
-    asio::io_service& io_;
-
-    // Class member variables which are dynamic, and changes to which
-    // need to accessible from both sides of a coroutine fork or from
-    // outside of the coroutine (i.e., from an asynchronous I/O call),
-    // should be declared here as pointers and allocated in the
-    // constructor or in the coroutine.  This allows state information
-    // to persist when an individual copy of the coroutine falls out
-    // scope while waiting for an event, *so long as* there is another
-    // object that is referencing the same data.  As a side-benefit, using
-    // pointers also reduces copy overhead for coroutine objects.
-    //
-    // Note: Currently these objects are allocated by "new" in the
-    // constructor, or in the function operator while processing a query.
-    // Repeated allocations from the heap for every incoming query is
-    // clearly a performance issue; this must be optimized in the future.
-    // The plan is to have a structure pre-allocate several "server state"
-    // objects which can be pulled off a free list and placed on an in-use
-    // list whenever a query comes in.  This will serve the dual purpose
-    // of improving performance and guaranteeing that state information
-    // will *not* be destroyed when any one instance of the coroutine
-    // falls out of scope while waiting for an event.
-    //
-    // An ASIO acceptor object to handle new connections.  Created in
-    // the constructor.
-    boost::shared_ptr<asio::ip::tcp::acceptor> acceptor_;
-
-    // Socket used to for listen for queries.  Created in the
-    // constructor and stored in a shared_ptr because socket objects
-    // are not copyable.
-    boost::shared_ptr<asio::ip::tcp::socket> socket_;
-
-    // The buffer into which the response is written
-    boost::shared_ptr<isc::dns::OutputBuffer> respbuf_;
-
-    // \c IOMessage and \c Message objects to be passed to the
-    // DNS lookup and answer providers
-    boost::shared_ptr<asiolink::IOMessage> io_message_;
-    isc::dns::MessagePtr query_message_;
-    isc::dns::MessagePtr answer_message_;
-
-    // The buffer into which the query packet is written
-    boost::shared_array<char>data_;
-
-    // State information that is entirely internal to a given instance
-    // of the coroutine can be declared here.
-    size_t bytes_;
-    bool done_;
-
-    // Callback functions provided by the caller
-    const SimpleCallback* checkin_callback_;
-    const DNSLookup* lookup_callback_;
-    const DNSAnswer* answer_callback_;
-
-    boost::shared_ptr<IOEndpoint> peer_;
-    boost::shared_ptr<IOSocket> iosock_;
-};
-
-}
-
-#endif // __TCPDNS_H
-
-// Local Variables: 
-// mode: c++
-// End: 
diff --git a/src/lib/asiolink/internal/tests/Makefile.am b/src/lib/asiolink/internal/tests/Makefile.am
deleted file mode 100644
index 449cab7..0000000
--- a/src/lib/asiolink/internal/tests/Makefile.am
+++ /dev/null
@@ -1,37 +0,0 @@
-AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
-AM_CPPFLAGS += $(BOOST_INCLUDES)
-
-AM_CXXFLAGS = $(B10_CXXFLAGS)
-
-if USE_STATIC_LINK
-AM_LDFLAGS = -static
-endif
-
-CLEANFILES = *.gcno *.gcda
-
-TESTS =
-if HAVE_GTEST
-TESTS += run_unittests
-run_unittests_SOURCES = udpdns_unittest.cc
-run_unittests_SOURCES += run_unittests.cc
-run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
-run_unittests_LDADD = $(GTEST_LDADD)
-run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
-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/log/liblog.la
-# Note: the ordering matters: -Wno-... must follow -Wextra (defined in
-# B10_CXXFLAGS)
-run_unittests_CXXFLAGS = $(AM_CXXFLAGS)
-if USE_GXX
-run_unittests_CXXFLAGS += -Wno-unused-parameter
-endif
-if USE_CLANGPP
-# We need to disable -Werror for any test that uses internal definitions of
-# ASIO when using clang++
-run_unittests_CXXFLAGS += -Wno-error
-endif
-endif
-
-noinst_PROGRAMS = $(TESTS)
diff --git a/src/lib/asiolink/internal/tests/run_unittests.cc b/src/lib/asiolink/internal/tests/run_unittests.cc
deleted file mode 100644
index 5f1195d..0000000
--- a/src/lib/asiolink/internal/tests/run_unittests.cc
+++ /dev/null
@@ -1,21 +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.
-
-#include <gtest/gtest.h>
-
-int
-main(int argc, char* argv[]) {
-    ::testing::InitGoogleTest(&argc, argv);
-    return (RUN_ALL_TESTS());
-}
diff --git a/src/lib/asiolink/internal/tests/udpdns_unittest.cc b/src/lib/asiolink/internal/tests/udpdns_unittest.cc
deleted file mode 100644
index 099071c..0000000
--- a/src/lib/asiolink/internal/tests/udpdns_unittest.cc
+++ /dev/null
@@ -1,145 +0,0 @@
-// Copyright (C) 2010  CZ.NIC
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <gtest/gtest.h>
-#include <asio.hpp>
-#include <boost/bind.hpp>
-#include <cstdlib>
-
-#include <dns/question.h>
-
-#include <asiolink/internal/udpdns.h>
-
-using namespace asio;
-using namespace isc::dns;
-using asio::ip::udp;
-
-namespace {
-
-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";
-
-// Test fixture for the asiolink::UDPQuery.
-class UDPQueryTest : public ::testing::Test,
-    public asiolink::UDPQuery::Callback
-{
-    public:
-        // Expected result of the callback
-        asiolink::UDPQuery::Result expected_;
-        // Did the callback run already?
-        bool run_;
-        // We use an io_service to run the query
-        io_service service_;
-        // Something to ask
-        Question question_;
-        // Buffer where the UDPQuery will store response
-        OutputBufferPtr buffer_;
-        // The query we are testing
-        asiolink::UDPQuery query_;
-
-        UDPQueryTest() :
-            run_(false),
-            question_(Name("example.net"), RRClass::IN(), RRType::A()),
-            buffer_(new OutputBuffer(512)),
-            query_(service_, question_, asiolink::IOAddress(TEST_HOST),
-                TEST_PORT, buffer_, this, 100)
-        { }
-
-        // This is the callback's (), so it can be called.
-        void operator()(asiolink::UDPQuery::Result result) {
-            // We check the query returns the correct result
-            EXPECT_EQ(expected_, result);
-            // Check it is called only once
-            EXPECT_FALSE(run_);
-            // And mark the callback was called
-            run_ = true;
-        }
-        // A response handler, pretending to be remote DNS server
-        void respond(udp::endpoint* remote, udp::socket* socket) {
-            // Some data came, just send something back.
-            socket->send_to(asio::buffer(TEST_DATA, sizeof TEST_DATA),
-                *remote);
-            socket->close();
-        }
-};
-
-/*
- * 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(UDPQueryTest, stop) {
-    expected_ = asiolink::UDPQuery::STOPPED;
-    // Post the query
-    service_.post(query_);
-    // Post query_.stop() (yes, the boost::bind thing is just
-    // query_.stop()).
-    service_.post(boost::bind(&asiolink::UDPQuery::stop, query_,
-        asiolink::UDPQuery::STOPPED));
-    // Run both of them
-    service_.run();
-    EXPECT_TRUE(run_);
-}
-
-/*
- * 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).
- */
-TEST_F(UDPQueryTest, prematureStop) {
-    expected_ = asiolink::UDPQuery::STOPPED;
-    // Stop before it is started
-    query_.stop();
-    service_.post(query_);
-    service_.run();
-    EXPECT_TRUE(run_);
-}
-
-/*
- * Test that it will timeout when no answer will arrive.
- */
-TEST_F(UDPQueryTest, timeout) {
-    expected_ = asiolink::UDPQuery::TIME_OUT;
-    service_.post(query_);
-    service_.run();
-    EXPECT_TRUE(run_);
-}
-
-/*
- * 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 loopback address.
- */
-TEST_F(UDPQueryTest, receive) {
-    expected_ = asiolink::UDPQuery::SUCCESS;
-    udp::socket socket(service_, udp::v4());
-    socket.set_option(socket_base::reuse_address(true));
-    socket.bind(udp::endpoint(TEST_HOST, TEST_PORT));
-    char inbuff[512];
-    udp::endpoint remote;
-    socket.async_receive_from(asio::buffer(inbuff, 512), remote, boost::bind(
-        &UDPQueryTest::respond, this, &remote, &socket));
-    service_.post(query_);
-    service_.run();
-    EXPECT_TRUE(run_);
-    ASSERT_EQ(sizeof TEST_DATA, buffer_->getLength());
-    EXPECT_EQ(0, memcmp(TEST_DATA, buffer_->getData(), sizeof TEST_DATA));
-}
-
-}
diff --git a/src/lib/asiolink/internal/udpdns.h b/src/lib/asiolink/internal/udpdns.h
deleted file mode 100644
index 5b67ca9..0000000
--- a/src/lib/asiolink/internal/udpdns.h
+++ /dev/null
@@ -1,253 +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 __UDPDNS_H
-#define __UDPDNS_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 UDP-specific implementations of generic classes 
-// defined in asiolink.h.  It is *not* intended to be part of the public
-// API.
-
-namespace asiolink {
-/// \brief The \c UDPEndpoint class is a concrete derived class of
-/// \c IOEndpoint that represents an endpoint of a UDP packet.
-///
-/// Other notes about \c TCPEndpoint applies to this class, too.
-class UDPEndpoint : public IOEndpoint {
-public:
-    ///
-    /// \name Constructors and Destructor.
-    ///
-    //@{
-    /// \brief Constructor from a pair of address and port.
-    ///
-    /// \param address The IP address of the endpoint.
-    /// \param port The UDP port number of the endpoint.
-    UDPEndpoint(const IOAddress& address, const unsigned short port) :
-        asio_endpoint_placeholder_(
-            new asio::ip::udp::endpoint(asio::ip::address::from_string(address.toText()),
-                              port)),
-        asio_endpoint_(*asio_endpoint_placeholder_)
-    {}
-
-    /// \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 UDP endpoint.
-    UDPEndpoint(const asio::ip::udp::endpoint& asio_endpoint) :
-        asio_endpoint_placeholder_(NULL), asio_endpoint_(asio_endpoint)
-    {}
-
-    /// \brief The destructor.
-    ~UDPEndpoint() { delete asio_endpoint_placeholder_; }
-    //@}
-
-    inline IOAddress getAddress() const {
-        return (asio_endpoint_.address());
-    }
-
-    inline uint16_t getPort() const {
-        return (asio_endpoint_.port());
-    }
-
-    inline short getProtocol() const {
-        return (asio_endpoint_.protocol().protocol());
-    }
-
-    inline 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
-    inline const asio::ip::udp::endpoint& getASIOEndpoint() const {
-        return (asio_endpoint_);
-    }
-
-private:
-    const asio::ip::udp::endpoint* asio_endpoint_placeholder_;
-    const asio::ip::udp::endpoint& asio_endpoint_;
-};
-
-/// \brief The \c UDPSocket class is a concrete derived class of
-/// \c IOSocket that represents a UDP socket.
-///
-/// Other notes about \c TCPSocket applies to this class, too.
-class UDPSocket : public IOSocket {
-private:
-    UDPSocket(const UDPSocket& source);
-    UDPSocket& operator=(const UDPSocket& source);
-public:
-    /// \brief Constructor from an ASIO UDP socket.
-    ///
-    /// \param socket The ASIO representation of the UDP socket.
-    UDPSocket(asio::ip::udp::socket& socket) : socket_(socket) {}
-
-    virtual int getNative() const { return (socket_.native()); }
-    virtual int getProtocol() const { return (IPPROTO_UDP); }
-
-private:
-    asio::ip::udp::socket& socket_;
-};
-
-//
-// Asynchronous UDP server coroutine
-//
-///
-/// \brief This class implements the coroutine to handle UDP
-///        DNS query event. As such, it is both a \c DNSServer and
-///        a \c coroutine
-///
-class UDPServer : public virtual DNSServer, public virtual coroutine {
-public:
-    /// \brief Constructor
-    /// \param io_service the asio::io_service to work with
-    /// \param addr the IP address to listen for queries on
-    /// \param port the port to listen for queries on
-    /// \param checkin the callbackprovider for non-DNS events
-    /// \param lookup the callbackprovider for DNS lookup events
-    /// \param answer the callbackprovider for DNS answer events
-    explicit UDPServer(asio::io_service& io_service,
-                       const asio::ip::address& addr, const uint16_t port,
-                       SimpleCallback* checkin = NULL,
-                       DNSLookup* lookup = NULL,
-                       DNSAnswer* answer = NULL);
-
-    /// \brief The function operator
-    void operator()(asio::error_code ec = asio::error_code(),
-                    size_t length = 0);
-
-    /// \brief Calls the lookup callback
-    void asyncLookup();
-
-    /// \brief Resume operation
-    ///
-    /// \param done Set this to true if the lookup action is done and
-    ///        we have an answer
-    void resume(const bool done);
-
-    /// \brief Check if we have an answer
-    ///
-    /// \return true if we have an answer
-    bool hasAnswer();
-
-    /// \brief Returns the coroutine state value
-    ///
-    /// \return the coroutine state value
-    int value() { return (get_value()); }
-
-    /// \brief Clones the object
-    ///
-    /// \return a newly allocated copy of this object
-    DNSServer* clone() {
-        UDPServer* s = new UDPServer(*this);
-        return (s);
-    }
-
-private:
-    enum { MAX_LENGTH = 4096 };
-
-    /**
-     * \brief Internal state and data.
-     *
-     * We use the pimple design pattern, but not because we need to hide
-     * internal data. This class and whole header is for private use anyway.
-     * It turned out that UDPServer is copied a lot, because it is a coroutine.
-     * This way the overhead of copying is lower, we copy only one shared
-     * pointer instead of about 10 of them.
-     */
-    class Data;
-    boost::shared_ptr<Data> data_;
-};
-
-//
-// Asynchronous UDP coroutine for upstream queries
-//
-class UDPQuery : public coroutine {
-public:
-    // TODO Maybe this should be more generic than just for UDPQuery?
-    ///
-    /// \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 UDPQuery.
-    class Callback {
-    public:
-        virtual ~Callback() {}
-
-        /// This will be called when the UDPQuery 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.
-    ///
-    explicit UDPQuery(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);
-    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 PrivateData;
-    boost::shared_ptr<PrivateData> data_;
-};
-}
-
-
-#endif // __UDPDNS_H
-
-// Local Variables: 
-// mode: c++
-// End: 
diff --git a/src/lib/asiolink/interval_timer.cc b/src/lib/asiolink/interval_timer.cc
new file mode 100644
index 0000000..6b0cd09
--- /dev/null
+++ b/src/lib/asiolink/interval_timer.cc
@@ -0,0 +1,136 @@
+// 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>
+
+// unistd is needed for asio.hpp with SunStudio
+#include <unistd.h>
+
+#include <asio.hpp>
+
+#include <exceptions/exceptions.h>
+
+#include <asiolink/interval_timer.h>
+#include <asiolink/io_service.h>
+
+#include <boost/bind.hpp>
+
+namespace asiolink {
+
+class IntervalTimerImpl {
+private:
+    // prohibit copy
+    IntervalTimerImpl(const IntervalTimerImpl& source);
+    IntervalTimerImpl& operator=(const IntervalTimerImpl& source);
+public:
+    IntervalTimerImpl(IOService& io_service);
+    ~IntervalTimerImpl();
+    void setup(const IntervalTimer::Callback& cbfunc, const long interval);
+    void callback(const asio::error_code& error);
+    void cancel() {
+        timer_.cancel();
+        interval_ = 0;
+    }
+    long getInterval() const { return (interval_); }
+private:
+    // a function to update timer_ when it expires
+    void update();
+    // a function to call back when timer_ expires
+    IntervalTimer::Callback cbfunc_;
+    // interval in milliseconds
+    long interval_;
+    // asio timer
+    asio::deadline_timer timer_;
+};
+
+IntervalTimerImpl::IntervalTimerImpl(IOService& io_service) :
+    interval_(0), timer_(io_service.get_io_service())
+{}
+
+IntervalTimerImpl::~IntervalTimerImpl()
+{}
+
+void
+IntervalTimerImpl::setup(const IntervalTimer::Callback& cbfunc,
+                         const long interval)
+{
+    // Interval should not be less than or equal to 0.
+    if (interval <= 0) {
+        isc_throw(isc::BadValue, "Interval should not be less than or "
+                                 "equal to 0");
+    }
+    // Call back function should not be empty.
+    if (cbfunc.empty()) {
+        isc_throw(isc::InvalidParameter, "Callback function is empty");
+    }
+    cbfunc_ = cbfunc;
+    interval_ = interval;
+    // Set initial expire time.
+    // At this point the timer is not running yet and will not expire.
+    // After calling IOService::run(), the timer will expire.
+    update();
+    return;
+}
+
+void
+IntervalTimerImpl::update() {
+    if (interval_ == 0) {
+        // timer has been canceled.  Do nothing.
+        return;
+    }
+    try {
+        // Update expire time to (current time + interval_).
+        timer_.expires_from_now(boost::posix_time::millisec(interval_));
+    } catch (const asio::system_error& e) {
+        isc_throw(isc::Unexpected, "Failed to update timer");
+    }
+    // Reset timer.
+    timer_.async_wait(boost::bind(&IntervalTimerImpl::callback, this, _1));
+}
+
+void
+IntervalTimerImpl::callback(const asio::error_code& cancelled) {
+    // Do not call cbfunc_ in case the timer was cancelled.
+    // The timer will be canelled in the destructor of asio::deadline_timer.
+    if (!cancelled) {
+        cbfunc_();
+        // Set next expire time.
+        update();
+    }
+}
+
+IntervalTimer::IntervalTimer(IOService& io_service) {
+    impl_ = new IntervalTimerImpl(io_service);
+}
+
+IntervalTimer::~IntervalTimer() {
+    delete impl_;
+}
+
+void
+IntervalTimer::setup(const Callback& cbfunc, const long interval) {
+    return (impl_->setup(cbfunc, interval));
+}
+
+void
+IntervalTimer::cancel() {
+    impl_->cancel();
+}
+
+long
+IntervalTimer::getInterval() const {
+    return (impl_->getInterval());
+}
+
+}
diff --git a/src/lib/asiolink/interval_timer.h b/src/lib/asiolink/interval_timer.h
new file mode 100644
index 0000000..d805cd7
--- /dev/null
+++ b/src/lib/asiolink/interval_timer.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 __ASIOLINK_INTERVAL_TIMER_H
+#define __ASIOLINK_INTERVAL_TIMER_H 1
+
+#include <boost/function.hpp>
+
+#include <asiolink/io_service.h>
+
+namespace asiolink {
+
+struct IntervalTimerImpl;
+
+/// \brief The \c IntervalTimer class is a wrapper for the ASIO
+/// \c asio::deadline_timer class.
+///
+/// This class is implemented to use \c asio::deadline_timer as interval
+/// timer.
+///
+/// \c setup() sets a timer to expire on (now + interval) and a call back
+/// function.
+///
+/// \c IntervalTimerImpl::callback() is called by the timer when it expires.
+///
+/// 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.
+///
+/// Note: Destruction of an instance of this class while call back is pending
+/// causes throwing an exception from \c IOService.
+///
+/// Sample code:
+/// \code
+///  void function_to_call_back() {
+///      // this function will be called periodically
+///  }
+///  int interval_in_milliseconds = 1000;
+///  IOService io_service;
+///
+///  IntervalTimer intervalTimer(io_service);
+///  intervalTimer.setup(function_to_call_back, interval_in_milliseconds);
+///  io_service.run();
+/// \endcode
+class IntervalTimer {
+public:
+    /// \name The type of timer callback function
+    typedef boost::function<void()> Callback;
+
+    ///
+    /// \name Constructors and Destructor
+    ///
+    /// Note: The copy constructor and the assignment operator are
+    /// intentionally defined as private, making this class non-copyable.
+    //@{
+private:
+    IntervalTimer(const IntervalTimer& source);
+    IntervalTimer& operator=(const IntervalTimer& source);
+public:
+    /// \brief The constructor with \c IOService.
+    ///
+    /// This constructor may throw a standard exception if
+    /// memory allocation fails inside the method.
+    /// This constructor may also throw \c asio::system_error.
+    ///
+    /// \param io_service A reference to an instance of IOService
+    IntervalTimer(IOService& io_service);
+
+    /// \brief The destructor.
+    ///
+    /// This destructor never throws an exception.
+    ///
+    /// On the destruction of this class the timer will be canceled
+    /// inside \c asio::deadline_timer.
+    ~IntervalTimer();
+    //@}
+
+    /// \brief Register timer callback function and interval.
+    ///
+    /// This function sets callback function and interval in milliseconds.
+    /// Timer will actually start after calling \c IOService::run().
+    ///
+    /// \param cbfunc A reference to a function \c void(void) to call back
+    /// when the timer is expired (should not be an empty functor)
+    /// \param interval Interval in milliseconds (greater than 0)
+    ///
+    /// Note: IntervalTimer will not pass \c asio::error_code to
+    /// call back function. In case the timer is cancelled, the function
+    /// will not be called.
+    ///
+    /// \throw isc::InvalidParameter cbfunc is empty
+    /// \throw isc::BadValue interval is less than or equal to 0
+    /// \throw isc::Unexpected ASIO library error
+    void setup(const Callback& cbfunc, const long interval);
+
+    /// Cancel the timer.
+    ///
+    /// If the timer has been set up, this method cancels any asynchronous
+    /// events waiting on the timer and stops the timer itself.
+    /// If the timer has already been canceled, this method effectively does
+    /// nothing.
+    ///
+    /// This method never throws an exception.
+    void cancel();
+
+    /// Return the timer interval.
+    ///
+    /// This method returns the timer interval in milliseconds if it's running;
+    /// if the timer has been canceled it returns 0.
+    ///
+    /// This method never throws an exception.
+    long getInterval() const;
+
+private:
+    IntervalTimerImpl* impl_;
+};
+
+}      // namespace asiolink
+#endif // __ASIOLINK_INTERVAL_TIMER_H
diff --git a/src/lib/asiolink/io_address.cc b/src/lib/asiolink/io_address.cc
new file mode 100644
index 0000000..990524a
--- /dev/null
+++ b/src/lib/asiolink/io_address.cc
@@ -0,0 +1,62 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+
+#include <unistd.h>             // for some IPC/network system calls
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <asio.hpp>
+
+#include <asiolink/asiolink.h>
+
+using namespace asio;
+using asio::ip::udp;
+using asio::ip::tcp;
+
+using namespace std;
+
+namespace asiolink {
+
+// XXX: we cannot simply construct the address in the initialization list,
+// because we'd like to throw our own exception on failure.
+IOAddress::IOAddress(const string& address_str) {
+    error_code err;
+    asio_address_ = ip::address::from_string(address_str, err);
+    if (err) {
+        isc_throw(IOError, "Failed to convert string to address '"
+                  << address_str << "': " << err.message());
+    }
+}
+
+IOAddress::IOAddress(const ip::address& asio_address) :
+    asio_address_(asio_address)
+{}
+
+string
+IOAddress::toText() const {
+    return (asio_address_.to_string());
+}
+
+short
+IOAddress::getFamily() const {
+    if (asio_address_.is_v4()) {
+        return (AF_INET);
+    } else {
+        return (AF_INET6);
+    }
+}
+
+}
diff --git a/src/lib/asiolink/io_address.h b/src/lib/asiolink/io_address.h
new file mode 100644
index 0000000..98e6fe8
--- /dev/null
+++ b/src/lib/asiolink/io_address.h
@@ -0,0 +1,127 @@
+// 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 __IOADDRESS_H
+#define __IOADDRESS_H 1
+
+// IMPORTANT NOTE: only very few ASIO headers files can be included in
+// this file.  In particular, asio.hpp should never be included here.
+// See the description of the namespace below.
+#include <unistd.h>             // for some network system calls
+#include <asio/ip/address.hpp>
+
+#include <functional>
+#include <string>
+
+#include <exceptions/exceptions.h>
+
+namespace asiolink {
+
+/// \brief The \c IOAddress class represents an IP addresses (version
+/// agnostic)
+///
+/// This class is a wrapper for the ASIO \c ip::address class.
+class IOAddress {
+public:
+    ///
+    /// \name Constructors and Destructor
+    ///
+    /// This class is copyable.  We use default versions of copy constructor
+    /// and the assignment operator.
+    /// We use the default destructor.
+    //@{
+    /// \brief Constructor from string.
+    ///
+    /// This constructor converts a textual representation of IPv4 and IPv6
+    /// addresses into an IOAddress object.
+    /// If \c address_str is not a valid representation of any type of
+    /// address, an exception of class \c IOError will be thrown.
+    /// This constructor allocates memory for the object, and if that fails
+    /// a corresponding standard exception will be thrown.
+    ///
+    /// \param address_str Textual representation of address.
+    IOAddress(const std::string& address_str);
+
+    /// \brief Constructor from an ASIO \c ip::address object.
+    ///
+    /// This constructor is intended to be used within the wrapper
+    /// implementation; user applications of the wrapper API won't use it.
+    ///
+    /// This constructor never throws an exception.
+    ///
+    /// \param asio_address The ASIO \c ip::address to be converted.
+    IOAddress(const asio::ip::address& asio_adress);
+    //@}
+
+    /// \brief Convert the address to a string.
+    ///
+    /// This method is basically expected to be exception free, but
+    /// generating the string will involve resource allocation,
+    /// and if it fails the corresponding standard exception will be thrown.
+    ///
+    /// \return A string representation of the address.
+    std::string toText() const;
+
+    /// \brief Returns the address family
+    ///
+    /// \return AF_INET for IPv4 or AF_INET6 for IPv6.
+    short getFamily() const;
+
+    /// \brief Compare addresses for equality
+    ///
+    /// \param other Address to compare against.
+    ///
+    /// \return true if addresses are equal, false if not.
+    bool equals(const IOAddress& other) const {
+        return (asio_address_ == other.asio_address_);
+    }
+
+    /// \brief Compare addresses for equality
+    ///
+    /// \param other Address to compare against.
+    ///
+    /// \return true if addresses are equal, false if not.
+    bool operator==(const IOAddress& other) const {
+        return equals(other);
+    }
+
+    // \brief Compare addresses for inequality
+    ///
+    /// \param other Address to compare against.
+    ///
+    /// \return false if addresses are equal, true if not.
+    bool nequals(const IOAddress& other) const {
+        return (!equals(other));
+    }
+
+    // \brief Compare addresses for inequality
+    ///
+    /// \param other Address to compare against.
+    ///
+    /// \return false if addresses are equal, true if not.
+    bool operator!=(const IOAddress& other) const {
+        return (nequals(other));
+    }
+
+
+private:
+    asio::ip::address asio_address_;
+};
+
+}      // asiolink
+#endif // __IOADDRESS_H
+
+// Local Variables: 
+// mode: c++
+// End: 
diff --git a/src/lib/asiolink/io_endpoint.cc b/src/lib/asiolink/io_endpoint.cc
new file mode 100644
index 0000000..86e0607
--- /dev/null
+++ b/src/lib/asiolink/io_endpoint.cc
@@ -0,0 +1,45 @@
+// 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 <unistd.h>             // for some IPC/network system calls
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <asio.hpp>
+
+#include <asiolink/asiolink.h>
+#include <asiolink/tcp_endpoint.h>
+#include <asiolink/udp_endpoint.h>
+
+using namespace std;
+
+namespace asiolink {
+
+const IOEndpoint*
+IOEndpoint::create(const int protocol, const IOAddress& address,
+                   const unsigned short port)
+{
+    if (protocol == IPPROTO_UDP) {
+        return (new UDPEndpoint(address, port));
+    } else if (protocol == IPPROTO_TCP) {
+        return (new TCPEndpoint(address, port));
+    }
+    isc_throw(IOError,
+              "IOEndpoint creation attempt for unsupported protocol: " <<
+              protocol);
+}
+
+}
diff --git a/src/lib/asiolink/io_endpoint.h b/src/lib/asiolink/io_endpoint.h
new file mode 100644
index 0000000..37f9087
--- /dev/null
+++ b/src/lib/asiolink/io_endpoint.h
@@ -0,0 +1,122 @@
+// 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 __IOENDPOINT_H
+#define __IOENDPOINT_H 1
+
+// IMPORTANT NOTE: only very few ASIO headers files can be included in
+// this file.  In particular, asio.hpp should never be included here.
+// See the description of the namespace below.
+#include <unistd.h>             // for some network system calls
+
+#include <functional>
+#include <string>
+
+#include <exceptions/exceptions.h>
+#include <asiolink/io_address.h>
+
+namespace asiolink {
+
+/// \brief The \c IOEndpoint class is an abstract base class to represent
+/// a communication endpoint.
+///
+/// This class is a wrapper for the ASIO endpoint classes such as
+/// \c ip::tcp::endpoint and \c ip::udp::endpoint.
+///
+/// Derived class implementations are completely hidden within the
+/// implementation.  User applications only get access to concrete
+/// \c IOEndpoint objects via the abstract interfaces.
+class IOEndpoint {
+    ///
+    /// \name Constructors and Destructor
+    ///
+    /// Note: The copy constructor and the assignment operator are
+    /// intentionally defined as private, making this class non-copyable.
+    //@{
+private:
+    IOEndpoint(const IOEndpoint& source);
+    IOEndpoint& operator=(const IOEndpoint& source);
+protected:
+    /// \brief The default constructor.
+    ///
+    /// This is intentionally defined as \c protected as this base class
+    /// should never be instantiated (except as part of a derived class).
+    IOEndpoint() {}
+public:
+    /// The destructor.
+    virtual ~IOEndpoint() {}
+    //@}
+
+    /// \brief Returns the address of the endpoint.
+    ///
+    /// This method returns an IOAddress object corresponding to \c this
+    /// endpoint.
+    ///
+    /// Note that the return value is a real object, not a reference or
+    /// a pointer.
+    ///
+    /// This is aligned with the interface of the ASIO counterpart:
+    /// the \c address() method of \c ip::xxx::endpoint classes returns
+    /// an \c ip::address object.
+    ///
+    /// This also means handling the address of an endpoint using this method
+    /// can be expensive.  If the address information is necessary in a
+    /// performance sensitive context and there's a more efficient interface
+    /// for that purpose, it's probably better to avoid using this method.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \return A copy of \c IOAddress object corresponding to the endpoint.
+    virtual IOAddress getAddress() const = 0;
+
+    /// \brief Returns the port of the endpoint.
+    virtual uint16_t getPort() const = 0;
+
+    /// \brief Returns the protocol number of the endpoint (TCP, UDP...)
+    virtual short getProtocol() const = 0;
+
+    /// \brief Returns the address family of the endpoint.
+    virtual short getFamily() const = 0;
+
+    /// \brief A polymorphic factory of endpoint from address and port.
+    ///
+    /// This method creates a new instance of (a derived class of)
+    /// \c IOEndpoint object that identifies the pair of given address
+    /// and port.
+    /// The appropriate derived class is chosen based on the specified
+    /// transport protocol.  If the \c protocol doesn't specify a protocol
+    /// supported in this implementation, an exception of class \c IOError
+    /// will be thrown.
+    ///
+    /// Memory for the created object will be dynamically allocated.  It's
+    /// the caller's responsibility to \c delete it later.
+    /// If resource allocation for the new object fails, a corresponding
+    /// standard exception will be thrown.
+    ///
+    /// \param protocol The transport protocol used for the endpoint.
+    /// Currently, only \c IPPROTO_UDP and \c IPPROTO_TCP can be specified.
+    /// \param address The (IP) address of the endpoint.
+    /// \param port The transport port number of the endpoint
+    /// \return A pointer to a newly created \c IOEndpoint object.
+    static const IOEndpoint* create(const int protocol,
+                                    const IOAddress& address,
+                                    const unsigned short port);
+};
+
+}      // asiolink
+#endif // __IOENDPOINT_H
+
+// Local Variables: 
+// mode: c++
+// End: 
diff --git a/src/lib/asiolink/io_message.h b/src/lib/asiolink/io_message.h
new file mode 100644
index 0000000..7d7d56c
--- /dev/null
+++ b/src/lib/asiolink/io_message.h
@@ -0,0 +1,103 @@
+// 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 __IOMESSAGE_H
+#define __IOMESSAGE_H 1
+
+// IMPORTANT NOTE: only very few ASIO headers files can be included in
+// this file.  In particular, asio.hpp should never be included here.
+// See the description of the namespace below.
+#include <unistd.h>             // for some network system calls
+
+#include <functional>
+#include <string>
+
+#include <exceptions/exceptions.h>
+
+#include <asiolink/io_endpoint.h>
+#include <asiolink/io_socket.h>
+
+namespace asiolink {
+
+/// \brief The \c IOMessage class encapsulates an incoming message received
+/// on a socket.
+///
+/// An \c IOMessage object represents a tuple of a chunk of data
+/// (a UDP packet or some segment of TCP stream), the socket over which the
+/// data is passed, the information about the other end point of the
+/// communication, and perhaps more.
+///
+/// The current design and interfaces of this class is tentative.
+/// It only provides a minimal level of support that is necessary for
+/// the current implementation of the authoritative server.
+/// A future version of this class will definitely support more.
+class IOMessage {
+    ///
+    /// \name Constructors and Destructor
+    ///
+    /// Note: The copy constructor and the assignment operator are
+    /// intentionally defined as private, making this class non-copyable.
+    //@{
+private:
+    IOMessage(const IOMessage& source);
+    IOMessage& operator=(const IOMessage& source);
+public:
+    /// \brief Constructor from message data
+    ///
+    /// This constructor needs to handle the ASIO \c ip::address class,
+    /// and is intended to be used within this wrapper implementation.
+    /// Once the \c IOMessage object is created, the application can
+    /// get access to the information via the wrapper interface such as
+    /// \c getRemoteAddress().
+    ///
+    /// This constructor never throws an exception.
+    ///
+    /// \param data A pointer to the message data.
+    /// \param data_size The size of the message data in bytes.
+    /// \param io_socket The socket over which the data is given.
+    /// \param remote_endpoint The other endpoint of the socket, that is,
+    /// the sender of the message.
+    IOMessage(const void* data, const size_t data_size,
+              const IOSocket& io_socket, const IOEndpoint& remote_endpoint) :
+        data_(data), data_size_(data_size), io_socket_(io_socket),
+        remote_endpoint_(remote_endpoint)
+    {}
+    //@}
+
+    /// \brief Returns a pointer to the received data.
+    const void* getData() const { return (data_); }
+
+    /// \brief Returns the size of the received data in bytes.
+    size_t getDataSize() const { return (data_size_); }
+
+    /// \brief Returns the socket on which the message arrives.
+    const IOSocket& getSocket() const { return (io_socket_); }
+
+    /// \brief Returns the endpoint that sends the message.
+    const IOEndpoint& getRemoteEndpoint() const { return (remote_endpoint_); }
+
+private:
+    const void* data_;
+    const size_t data_size_;
+    const IOSocket& io_socket_;
+    const IOEndpoint& remote_endpoint_;
+};
+
+
+}      // asiolink
+#endif // __IOMESSAGE_H
+
+// Local Variables: 
+// mode: c++
+// End: 
diff --git a/src/lib/asiolink/io_service.cc b/src/lib/asiolink/io_service.cc
new file mode 100644
index 0000000..8d96da9
--- /dev/null
+++ b/src/lib/asiolink/io_service.cc
@@ -0,0 +1,97 @@
+// 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>
+
+// unistd is needed for asio.hpp with SunStudio
+#include <unistd.h>
+
+#include <asio.hpp>
+
+#include <asiolink/io_service.h>
+
+namespace asiolink {
+
+class IOServiceImpl {
+private:
+    IOServiceImpl(const IOService& source);
+    IOServiceImpl& operator=(const IOService& source);
+public:
+    /// \brief The constructor
+    IOServiceImpl() :
+        io_service_(),
+        work_(io_service_)
+    {};
+    /// \brief The destructor.
+    ~IOServiceImpl() {};
+    //@}
+
+    /// \brief Start the underlying event loop.
+    ///
+    /// This method does not return control to the caller until
+    /// the \c stop() method is called via some handler.
+    void run() { io_service_.run(); };
+
+    /// \brief Run the underlying event loop for a single event.
+    ///
+    /// This method return control to the caller as soon as the
+    /// first handler has completed.  (If no handlers are ready when
+    /// it is run, it will block until one is.)
+    void run_one() { io_service_.run_one();} ;
+
+    /// \brief Stop the underlying event loop.
+    ///
+    /// This will return the control to the caller of the \c run() method.
+    void stop() { io_service_.stop();} ;
+
+    /// \brief Return the native \c io_service object used in this wrapper.
+    ///
+    /// This is a short term work around to support other BIND 10 modules
+    /// that share the same \c io_service with the authoritative server.
+    /// It will eventually be removed once the wrapper interface is
+    /// generalized.
+    asio::io_service& get_io_service() { return io_service_; };
+private:
+    asio::io_service io_service_;
+    asio::io_service::work work_;
+};
+
+IOService::IOService() {
+    io_impl_ = new IOServiceImpl();
+}
+
+IOService::~IOService() {
+    delete io_impl_;
+}
+
+void
+IOService::run() {
+    io_impl_->run();
+}
+
+void
+IOService::run_one() {
+    io_impl_->run_one();
+}
+
+void
+IOService::stop() {
+    io_impl_->stop();
+}
+
+asio::io_service&
+IOService::get_io_service() {
+    return (io_impl_->get_io_service());
+}
+
+} // namepsace asiolink
diff --git a/src/lib/asiolink/io_service.h b/src/lib/asiolink/io_service.h
new file mode 100644
index 0000000..66558b7
--- /dev/null
+++ b/src/lib/asiolink/io_service.h
@@ -0,0 +1,77 @@
+// 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_IO_SERVICE_H
+#define __ASIOLINK_IO_SERVICE_H 1
+
+namespace asio {
+    class io_service;
+}
+
+namespace asiolink {
+
+struct IOServiceImpl;
+
+/// \brief The \c IOService class is a wrapper for the ASIO \c io_service
+/// class.
+///
+class IOService {
+    ///
+    /// \name Constructors and Destructor
+    ///
+    /// Note: The copy constructor and the assignment operator are
+    /// intentionally defined as private, making this class non-copyable.
+    //@{
+private:
+    IOService(const IOService& source);
+    IOService& operator=(const IOService& source);
+public:
+    /// \brief The constructor
+    IOService();
+    /// \brief The destructor.
+    ~IOService();
+    //@}
+
+    /// \brief Start the underlying event loop.
+    ///
+    /// This method does not return control to the caller until
+    /// the \c stop() method is called via some handler.
+    void run();
+
+    /// \brief Run the underlying event loop for a single event.
+    ///
+    /// This method return control to the caller as soon as the
+    /// first handler has completed.  (If no handlers are ready when
+    /// it is run, it will block until one is.)
+    void run_one();
+
+    /// \brief Stop the underlying event loop.
+    ///
+    /// This will return the control to the caller of the \c run() method.
+    void stop();
+
+    /// \brief Return the native \c io_service object used in this wrapper.
+    ///
+    /// This is a short term work around to support other BIND 10 modules
+    /// that share the same \c io_service with the authoritative server.
+    /// It will eventually be removed once the wrapper interface is
+    /// generalized.
+    asio::io_service& get_io_service();
+
+private:
+    IOServiceImpl* io_impl_;
+};
+
+}      // namespace asiolink
+#endif // __ASIOLINK_IO_SERVICE_H
diff --git a/src/lib/asiolink/io_socket.cc b/src/lib/asiolink/io_socket.cc
new file mode 100644
index 0000000..fb325e9
--- /dev/null
+++ b/src/lib/asiolink/io_socket.cc
@@ -0,0 +1,67 @@
+// Copyright (C) 2010  CZ NIC
+// Copyed from other version of auth/asiolink.cc which is:
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include "io_socket.h"
+
+#include <asio.hpp>
+
+using namespace asio;
+
+namespace asiolink {
+
+/// \brief The \c DummySocket class is a concrete derived class of
+/// \c IOSocket that is not associated with any real socket.
+///
+/// This main purpose of this class is tests, where it may be desirable to
+/// instantiate an \c IOSocket object without involving system resource
+/// allocation such as real network sockets.
+class DummySocket : public IOSocket {
+private:
+    DummySocket(const DummySocket& source);
+    DummySocket& operator=(const DummySocket& source);
+public:
+    /// \brief Constructor from the protocol number.
+    ///
+    /// The protocol must validly identify a standard network protocol.
+    /// For example, to specify TCP \c protocol must be \c IPPROTO_TCP.
+    ///
+    /// \param protocol The network protocol number for the socket.
+    DummySocket(const int protocol) : protocol_(protocol) {}
+
+    /// \brief A dummy derived method of \c IOSocket::getNative().
+    ///
+    /// This version of method always returns -1 as the object is not
+    /// associated with a real (native) socket.
+    virtual int getNative() const { return (-1); }
+
+    virtual int getProtocol() const { return (protocol_); }
+private:
+    const int protocol_;
+};
+
+IOSocket&
+IOSocket::getDummyUDPSocket() {
+    static DummySocket socket(IPPROTO_UDP);
+    return (socket);
+}
+
+IOSocket&
+IOSocket::getDummyTCPSocket() {
+    static DummySocket socket(IPPROTO_TCP);
+    return (socket);
+}
+
+}
diff --git a/src/lib/asiolink/io_socket.h b/src/lib/asiolink/io_socket.h
new file mode 100644
index 0000000..df37d71
--- /dev/null
+++ b/src/lib/asiolink/io_socket.h
@@ -0,0 +1,127 @@
+// 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 __IOSOCKET_H
+#define __IOSOCKET_H 1
+
+// IMPORTANT NOTE: only very few ASIO headers files can be included in
+// this file.  In particular, asio.hpp should never be included here.
+// See the description of the namespace below.
+#include <unistd.h>             // for some network system calls
+
+#include <functional>
+#include <string>
+
+#include <exceptions/exceptions.h>
+
+namespace asiolink {
+
+/// \brief The \c IOSocket class is an abstract base class to represent
+/// various types of network sockets.
+///
+/// This class is a wrapper for the ASIO socket classes such as
+/// \c ip::tcp::socket and \c ip::udp::socket.
+///
+/// Derived class implementations are completely hidden within the
+/// implementation.  User applications only get access to concrete
+/// \c IOSocket objects via the abstract interfaces.
+///
+/// We may revisit this decision when we generalize the wrapper and more
+/// modules use it.  Also, at that point we may define a separate (visible)
+/// derived class for testing purposes rather than providing factory methods
+/// (i.e., getDummy variants below).
+class IOSocket {
+    ///
+    /// \name Constructors and Destructor
+    ///
+    /// Note: The copy constructor and the assignment operator are
+    /// intentionally defined as private, making this class non-copyable.
+    //@{
+private:
+    IOSocket(const IOSocket& source);
+    IOSocket& operator=(const IOSocket& source);
+protected:
+    /// \brief The default constructor.
+    ///
+    /// This is intentionally defined as \c protected as this base class
+    /// should never be instantiated (except as part of a derived class).
+    IOSocket() {}
+public:
+    /// The destructor.
+    virtual ~IOSocket() {}
+    //@}
+
+    /// \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 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.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \return The native representation of the socket.  This is the socket
+    /// file descriptor for UNIX-like systems.
+    virtual int getNative() const = 0;
+
+    /// \brief Return the transport protocol of the socket.
+    ///
+    /// Currently, it returns \c IPPROTO_UDP for UDP sockets, and
+    /// \c IPPROTO_TCP for TCP sockets.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \return IPPROTO_UDP for UDP sockets
+    /// \return IPPROTO_TCP for TCP sockets
+    virtual int getProtocol() const = 0;
+
+    /// \brief Return a non-usable "dummy" UDP socket for testing.
+    ///
+    /// This is a class method that returns a "mock" of UDP socket.
+    /// This is not associated with any actual socket, and its only
+    /// responsibility is to return \c IPPROTO_UDP from \c getProtocol().
+    /// The only feasible usage of this socket is for testing so that
+    /// the test code can prepare some "UDP data" even without opening any
+    /// actual socket.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \return A reference to an \c IOSocket object whose \c getProtocol()
+    /// returns \c IPPROTO_UDP.
+    static IOSocket& getDummyUDPSocket();
+
+    /// \brief Return a non-usable "dummy" TCP socket for testing.
+    ///
+    /// See \c getDummyUDPSocket().  This method is its TCP version.
+    ///
+    /// \return A reference to an \c IOSocket object whose \c getProtocol()
+    /// returns \c IPPROTO_TCP.
+    static IOSocket& getDummyTCPSocket();
+};
+
+}      // asiolink
+#endif // __IOSOCKET_H
+
+// Local Variables: 
+// mode: c++
+// End: 
diff --git a/src/lib/asiolink/ioaddress.cc b/src/lib/asiolink/ioaddress.cc
deleted file mode 100644
index 990524a..0000000
--- a/src/lib/asiolink/ioaddress.cc
+++ /dev/null
@@ -1,62 +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.
-
-#include <config.h>
-
-#include <unistd.h>             // for some IPC/network system calls
-#include <sys/socket.h>
-#include <netinet/in.h>
-
-#include <asio.hpp>
-
-#include <asiolink/asiolink.h>
-
-using namespace asio;
-using asio::ip::udp;
-using asio::ip::tcp;
-
-using namespace std;
-
-namespace asiolink {
-
-// XXX: we cannot simply construct the address in the initialization list,
-// because we'd like to throw our own exception on failure.
-IOAddress::IOAddress(const string& address_str) {
-    error_code err;
-    asio_address_ = ip::address::from_string(address_str, err);
-    if (err) {
-        isc_throw(IOError, "Failed to convert string to address '"
-                  << address_str << "': " << err.message());
-    }
-}
-
-IOAddress::IOAddress(const ip::address& asio_address) :
-    asio_address_(asio_address)
-{}
-
-string
-IOAddress::toText() const {
-    return (asio_address_.to_string());
-}
-
-short
-IOAddress::getFamily() const {
-    if (asio_address_.is_v4()) {
-        return (AF_INET);
-    } else {
-        return (AF_INET6);
-    }
-}
-
-}
diff --git a/src/lib/asiolink/ioaddress.h b/src/lib/asiolink/ioaddress.h
deleted file mode 100644
index 98e6fe8..0000000
--- a/src/lib/asiolink/ioaddress.h
+++ /dev/null
@@ -1,127 +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 __IOADDRESS_H
-#define __IOADDRESS_H 1
-
-// IMPORTANT NOTE: only very few ASIO headers files can be included in
-// this file.  In particular, asio.hpp should never be included here.
-// See the description of the namespace below.
-#include <unistd.h>             // for some network system calls
-#include <asio/ip/address.hpp>
-
-#include <functional>
-#include <string>
-
-#include <exceptions/exceptions.h>
-
-namespace asiolink {
-
-/// \brief The \c IOAddress class represents an IP addresses (version
-/// agnostic)
-///
-/// This class is a wrapper for the ASIO \c ip::address class.
-class IOAddress {
-public:
-    ///
-    /// \name Constructors and Destructor
-    ///
-    /// This class is copyable.  We use default versions of copy constructor
-    /// and the assignment operator.
-    /// We use the default destructor.
-    //@{
-    /// \brief Constructor from string.
-    ///
-    /// This constructor converts a textual representation of IPv4 and IPv6
-    /// addresses into an IOAddress object.
-    /// If \c address_str is not a valid representation of any type of
-    /// address, an exception of class \c IOError will be thrown.
-    /// This constructor allocates memory for the object, and if that fails
-    /// a corresponding standard exception will be thrown.
-    ///
-    /// \param address_str Textual representation of address.
-    IOAddress(const std::string& address_str);
-
-    /// \brief Constructor from an ASIO \c ip::address object.
-    ///
-    /// This constructor is intended to be used within the wrapper
-    /// implementation; user applications of the wrapper API won't use it.
-    ///
-    /// This constructor never throws an exception.
-    ///
-    /// \param asio_address The ASIO \c ip::address to be converted.
-    IOAddress(const asio::ip::address& asio_adress);
-    //@}
-
-    /// \brief Convert the address to a string.
-    ///
-    /// This method is basically expected to be exception free, but
-    /// generating the string will involve resource allocation,
-    /// and if it fails the corresponding standard exception will be thrown.
-    ///
-    /// \return A string representation of the address.
-    std::string toText() const;
-
-    /// \brief Returns the address family
-    ///
-    /// \return AF_INET for IPv4 or AF_INET6 for IPv6.
-    short getFamily() const;
-
-    /// \brief Compare addresses for equality
-    ///
-    /// \param other Address to compare against.
-    ///
-    /// \return true if addresses are equal, false if not.
-    bool equals(const IOAddress& other) const {
-        return (asio_address_ == other.asio_address_);
-    }
-
-    /// \brief Compare addresses for equality
-    ///
-    /// \param other Address to compare against.
-    ///
-    /// \return true if addresses are equal, false if not.
-    bool operator==(const IOAddress& other) const {
-        return equals(other);
-    }
-
-    // \brief Compare addresses for inequality
-    ///
-    /// \param other Address to compare against.
-    ///
-    /// \return false if addresses are equal, true if not.
-    bool nequals(const IOAddress& other) const {
-        return (!equals(other));
-    }
-
-    // \brief Compare addresses for inequality
-    ///
-    /// \param other Address to compare against.
-    ///
-    /// \return false if addresses are equal, true if not.
-    bool operator!=(const IOAddress& other) const {
-        return (nequals(other));
-    }
-
-
-private:
-    asio::ip::address asio_address_;
-};
-
-}      // asiolink
-#endif // __IOADDRESS_H
-
-// Local Variables: 
-// mode: c++
-// End: 
diff --git a/src/lib/asiolink/ioendpoint.cc b/src/lib/asiolink/ioendpoint.cc
deleted file mode 100644
index 2807f8d..0000000
--- a/src/lib/asiolink/ioendpoint.cc
+++ /dev/null
@@ -1,43 +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.
-
-#include <config.h>
-
-#include <unistd.h>             // for some IPC/network system calls
-#include <sys/socket.h>
-#include <netinet/in.h>
-
-#include <asiolink/asiolink.h>
-#include <internal/tcpdns.h>
-#include <internal/udpdns.h>
-
-using namespace std;
-
-namespace asiolink {
-
-const IOEndpoint*
-IOEndpoint::create(const int protocol, const IOAddress& address,
-                   const unsigned short port)
-{
-    if (protocol == IPPROTO_UDP) {
-        return (new UDPEndpoint(address, port));
-    } else if (protocol == IPPROTO_TCP) {
-        return (new TCPEndpoint(address, port));
-    }
-    isc_throw(IOError,
-              "IOEndpoint creation attempt for unsupported protocol: " <<
-              protocol);
-}
-
-}
diff --git a/src/lib/asiolink/ioendpoint.h b/src/lib/asiolink/ioendpoint.h
deleted file mode 100644
index 82190a3..0000000
--- a/src/lib/asiolink/ioendpoint.h
+++ /dev/null
@@ -1,122 +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 __IOENDPOINT_H
-#define __IOENDPOINT_H 1
-
-// IMPORTANT NOTE: only very few ASIO headers files can be included in
-// this file.  In particular, asio.hpp should never be included here.
-// See the description of the namespace below.
-#include <unistd.h>             // for some network system calls
-
-#include <functional>
-#include <string>
-
-#include <exceptions/exceptions.h>
-#include <asiolink/ioaddress.h>
-
-namespace asiolink {
-
-/// \brief The \c IOEndpoint class is an abstract base class to represent
-/// a communication endpoint.
-///
-/// This class is a wrapper for the ASIO endpoint classes such as
-/// \c ip::tcp::endpoint and \c ip::udp::endpoint.
-///
-/// Derived class implementations are completely hidden within the
-/// implementation.  User applications only get access to concrete
-/// \c IOEndpoint objects via the abstract interfaces.
-class IOEndpoint {
-    ///
-    /// \name Constructors and Destructor
-    ///
-    /// Note: The copy constructor and the assignment operator are
-    /// intentionally defined as private, making this class non-copyable.
-    //@{
-private:
-    IOEndpoint(const IOEndpoint& source);
-    IOEndpoint& operator=(const IOEndpoint& source);
-protected:
-    /// \brief The default constructor.
-    ///
-    /// This is intentionally defined as \c protected as this base class
-    /// should never be instantiated (except as part of a derived class).
-    IOEndpoint() {}
-public:
-    /// The destructor.
-    virtual ~IOEndpoint() {}
-    //@}
-
-    /// \brief Returns the address of the endpoint.
-    ///
-    /// This method returns an IOAddress object corresponding to \c this
-    /// endpoint.
-    ///
-    /// Note that the return value is a real object, not a reference or
-    /// a pointer.
-    ///
-    /// This is aligned with the interface of the ASIO counterpart:
-    /// the \c address() method of \c ip::xxx::endpoint classes returns
-    /// an \c ip::address object.
-    ///
-    /// This also means handling the address of an endpoint using this method
-    /// can be expensive.  If the address information is necessary in a
-    /// performance sensitive context and there's a more efficient interface
-    /// for that purpose, it's probably better to avoid using this method.
-    ///
-    /// This method never throws an exception.
-    ///
-    /// \return A copy of \c IOAddress object corresponding to the endpoint.
-    virtual IOAddress getAddress() const = 0;
-
-    /// \brief Returns the port of the endpoint.
-    virtual uint16_t getPort() const = 0;
-
-    /// \brief Returns the protocol number of the endpoint (TCP, UDP...)
-    virtual short getProtocol() const = 0;
-
-    /// \brief Returns the address family of the endpoint.
-    virtual short getFamily() const = 0;
-
-    /// \brief A polymorphic factory of endpoint from address and port.
-    ///
-    /// This method creates a new instance of (a derived class of)
-    /// \c IOEndpoint object that identifies the pair of given address
-    /// and port.
-    /// The appropriate derived class is chosen based on the specified
-    /// transport protocol.  If the \c protocol doesn't specify a protocol
-    /// supported in this implementation, an exception of class \c IOError
-    /// will be thrown.
-    ///
-    /// Memory for the created object will be dynamically allocated.  It's
-    /// the caller's responsibility to \c delete it later.
-    /// If resource allocation for the new object fails, a corresponding
-    /// standard exception will be thrown.
-    ///
-    /// \param protocol The transport protocol used for the endpoint.
-    /// Currently, only \c IPPROTO_UDP and \c IPPROTO_TCP can be specified.
-    /// \param address The (IP) address of the endpoint.
-    /// \param port The transport port number of the endpoint
-    /// \return A pointer to a newly created \c IOEndpoint object.
-    static const IOEndpoint* create(const int protocol,
-                                    const IOAddress& address,
-                                    const unsigned short port);
-};
-
-}      // asiolink
-#endif // __IOENDPOINT_H
-
-// Local Variables: 
-// mode: c++
-// End: 
diff --git a/src/lib/asiolink/iomessage.h b/src/lib/asiolink/iomessage.h
deleted file mode 100644
index 27baa8a..0000000
--- a/src/lib/asiolink/iomessage.h
+++ /dev/null
@@ -1,103 +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 __IOMESSAGE_H
-#define __IOMESSAGE_H 1
-
-// IMPORTANT NOTE: only very few ASIO headers files can be included in
-// this file.  In particular, asio.hpp should never be included here.
-// See the description of the namespace below.
-#include <unistd.h>             // for some network system calls
-
-#include <functional>
-#include <string>
-
-#include <exceptions/exceptions.h>
-
-#include <asiolink/ioendpoint.h>
-#include <asiolink/iosocket.h>
-
-namespace asiolink {
-
-/// \brief The \c IOMessage class encapsulates an incoming message received
-/// on a socket.
-///
-/// An \c IOMessage object represents a tuple of a chunk of data
-/// (a UDP packet or some segment of TCP stream), the socket over which the
-/// data is passed, the information about the other end point of the
-/// communication, and perhaps more.
-///
-/// The current design and interfaces of this class is tentative.
-/// It only provides a minimal level of support that is necessary for
-/// the current implementation of the authoritative server.
-/// A future version of this class will definitely support more.
-class IOMessage {
-    ///
-    /// \name Constructors and Destructor
-    ///
-    /// Note: The copy constructor and the assignment operator are
-    /// intentionally defined as private, making this class non-copyable.
-    //@{
-private:
-    IOMessage(const IOMessage& source);
-    IOMessage& operator=(const IOMessage& source);
-public:
-    /// \brief Constructor from message data
-    ///
-    /// This constructor needs to handle the ASIO \c ip::address class,
-    /// and is intended to be used within this wrapper implementation.
-    /// Once the \c IOMessage object is created, the application can
-    /// get access to the information via the wrapper interface such as
-    /// \c getRemoteAddress().
-    ///
-    /// This constructor never throws an exception.
-    ///
-    /// \param data A pointer to the message data.
-    /// \param data_size The size of the message data in bytes.
-    /// \param io_socket The socket over which the data is given.
-    /// \param remote_endpoint The other endpoint of the socket, that is,
-    /// the sender of the message.
-    IOMessage(const void* data, const size_t data_size,
-              const IOSocket& io_socket, const IOEndpoint& remote_endpoint) :
-        data_(data), data_size_(data_size), io_socket_(io_socket),
-        remote_endpoint_(remote_endpoint)
-    {}
-    //@}
-
-    /// \brief Returns a pointer to the received data.
-    const void* getData() const { return (data_); }
-
-    /// \brief Returns the size of the received data in bytes.
-    size_t getDataSize() const { return (data_size_); }
-
-    /// \brief Returns the socket on which the message arrives.
-    const IOSocket& getSocket() const { return (io_socket_); }
-
-    /// \brief Returns the endpoint that sends the message.
-    const IOEndpoint& getRemoteEndpoint() const { return (remote_endpoint_); }
-
-private:
-    const void* data_;
-    const size_t data_size_;
-    const IOSocket& io_socket_;
-    const IOEndpoint& remote_endpoint_;
-};
-
-
-}      // asiolink
-#endif // __IOMESSAGE_H
-
-// Local Variables: 
-// mode: c++
-// End: 
diff --git a/src/lib/asiolink/iosocket.cc b/src/lib/asiolink/iosocket.cc
deleted file mode 100644
index a3967d4..0000000
--- a/src/lib/asiolink/iosocket.cc
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright (C) 2010  CZ NIC
-// Copyed from other version of auth/asiolink.cc which is:
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include "iosocket.h"
-
-#include <asio.hpp>
-
-using namespace asio;
-
-namespace asiolink {
-
-/// \brief The \c DummySocket class is a concrete derived class of
-/// \c IOSocket that is not associated with any real socket.
-///
-/// This main purpose of this class is tests, where it may be desirable to
-/// instantiate an \c IOSocket object without involving system resource
-/// allocation such as real network sockets.
-class DummySocket : public IOSocket {
-private:
-    DummySocket(const DummySocket& source);
-    DummySocket& operator=(const DummySocket& source);
-public:
-    /// \brief Constructor from the protocol number.
-    ///
-    /// The protocol must validly identify a standard network protocol.
-    /// For example, to specify TCP \c protocol must be \c IPPROTO_TCP.
-    ///
-    /// \param protocol The network protocol number for the socket.
-    DummySocket(const int protocol) : protocol_(protocol) {}
-
-    /// \brief A dummy derived method of \c IOSocket::getNative().
-    ///
-    /// This version of method always returns -1 as the object is not
-    /// associated with a real (native) socket.
-    virtual int getNative() const { return (-1); }
-
-    virtual int getProtocol() const { return (protocol_); }
-private:
-    const int protocol_;
-};
-
-IOSocket&
-IOSocket::getDummyUDPSocket() {
-    static DummySocket socket(IPPROTO_UDP);
-    return (socket);
-}
-
-IOSocket&
-IOSocket::getDummyTCPSocket() {
-    static DummySocket socket(IPPROTO_TCP);
-    return (socket);
-}
-
-}
diff --git a/src/lib/asiolink/iosocket.h b/src/lib/asiolink/iosocket.h
deleted file mode 100644
index df37d71..0000000
--- a/src/lib/asiolink/iosocket.h
+++ /dev/null
@@ -1,127 +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 __IOSOCKET_H
-#define __IOSOCKET_H 1
-
-// IMPORTANT NOTE: only very few ASIO headers files can be included in
-// this file.  In particular, asio.hpp should never be included here.
-// See the description of the namespace below.
-#include <unistd.h>             // for some network system calls
-
-#include <functional>
-#include <string>
-
-#include <exceptions/exceptions.h>
-
-namespace asiolink {
-
-/// \brief The \c IOSocket class is an abstract base class to represent
-/// various types of network sockets.
-///
-/// This class is a wrapper for the ASIO socket classes such as
-/// \c ip::tcp::socket and \c ip::udp::socket.
-///
-/// Derived class implementations are completely hidden within the
-/// implementation.  User applications only get access to concrete
-/// \c IOSocket objects via the abstract interfaces.
-///
-/// We may revisit this decision when we generalize the wrapper and more
-/// modules use it.  Also, at that point we may define a separate (visible)
-/// derived class for testing purposes rather than providing factory methods
-/// (i.e., getDummy variants below).
-class IOSocket {
-    ///
-    /// \name Constructors and Destructor
-    ///
-    /// Note: The copy constructor and the assignment operator are
-    /// intentionally defined as private, making this class non-copyable.
-    //@{
-private:
-    IOSocket(const IOSocket& source);
-    IOSocket& operator=(const IOSocket& source);
-protected:
-    /// \brief The default constructor.
-    ///
-    /// This is intentionally defined as \c protected as this base class
-    /// should never be instantiated (except as part of a derived class).
-    IOSocket() {}
-public:
-    /// The destructor.
-    virtual ~IOSocket() {}
-    //@}
-
-    /// \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 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.
-    ///
-    /// This method never throws an exception.
-    ///
-    /// \return The native representation of the socket.  This is the socket
-    /// file descriptor for UNIX-like systems.
-    virtual int getNative() const = 0;
-
-    /// \brief Return the transport protocol of the socket.
-    ///
-    /// Currently, it returns \c IPPROTO_UDP for UDP sockets, and
-    /// \c IPPROTO_TCP for TCP sockets.
-    ///
-    /// This method never throws an exception.
-    ///
-    /// \return IPPROTO_UDP for UDP sockets
-    /// \return IPPROTO_TCP for TCP sockets
-    virtual int getProtocol() const = 0;
-
-    /// \brief Return a non-usable "dummy" UDP socket for testing.
-    ///
-    /// This is a class method that returns a "mock" of UDP socket.
-    /// This is not associated with any actual socket, and its only
-    /// responsibility is to return \c IPPROTO_UDP from \c getProtocol().
-    /// The only feasible usage of this socket is for testing so that
-    /// the test code can prepare some "UDP data" even without opening any
-    /// actual socket.
-    ///
-    /// This method never throws an exception.
-    ///
-    /// \return A reference to an \c IOSocket object whose \c getProtocol()
-    /// returns \c IPPROTO_UDP.
-    static IOSocket& getDummyUDPSocket();
-
-    /// \brief Return a non-usable "dummy" TCP socket for testing.
-    ///
-    /// See \c getDummyUDPSocket().  This method is its TCP version.
-    ///
-    /// \return A reference to an \c IOSocket object whose \c getProtocol()
-    /// returns \c IPPROTO_TCP.
-    static IOSocket& getDummyTCPSocket();
-};
-
-}      // asiolink
-#endif // __IOSOCKET_H
-
-// Local Variables: 
-// mode: c++
-// End: 
diff --git a/src/lib/asiolink/recursive_query.cc b/src/lib/asiolink/recursive_query.cc
new file mode 100644
index 0000000..9f814b0
--- /dev/null
+++ b/src/lib/asiolink/recursive_query.cc
@@ -0,0 +1,457 @@
+// 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 <stdlib.h>
+
+// unistd is needed for asio.hpp with SunStudio
+#include <unistd.h>
+
+#include <asio.hpp>
+
+#include <asiolink/recursive_query.h>
+#include <asiolink/dns_service.h>
+#include <asiolink/udp_query.h>
+
+#include <log/dummylog.h>
+
+#include <boost/lexical_cast.hpp>
+#include <boost/bind.hpp>
+
+#include <dns/question.h>
+#include <dns/message.h>
+
+#include <resolve/resolve.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 UDPQuery::Callback {
+private:
+    // The io service to handle async calls
+    asio::io_service& 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_;
+
+    // (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);
+            UDPQuery query(io_, question_,
+                upstream_->at(serverIndex).first,
+                upstream_->at(serverIndex).second, buffer_, this,
+                query_timeout_);
+            ++queries_out_;
+            io_.post(query);
+        } else if (zs > 0) {
+            int serverIndex = rand() % zs;
+            dlog("Sending query to zone server (" + question_.toText() +
+                ") to " + zone_servers_.at(serverIndex).first);
+            UDPQuery query(io_, question_,
+                zone_servers_.at(serverIndex).first,
+                zone_servers_.at(serverIndex).second, buffer_, this,
+                query_timeout_);
+            ++queries_out_;
+            io_.post(query);
+        } else {
+            dlog("Error, no upstream servers to send to.");
+        }
+    }
+    
+    // This function is called by operator() if there is an actual
+    // answer from a server and we are in recursive mode
+    // depending on the contents, we go on recursing or return
+    //
+    // Note that the footprint may change as this function may
+    // need to append data to the answer we are building later.
+    //
+    // returns true if we are done (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;
+
+        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());
+            send();
+            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;
+                    }
+                }
+            }
+            if (found_ns_address) {
+                // next resolver round
+                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(asio::io_service& 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) :
+        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),
+        lookup_timer(io),
+        queries_out_(0),
+        done_(false),
+        answer_sent_(false)
+    {
+        // Setup the timer to stop trying (lookup_timeout)
+        if (lookup_timeout >= 0) {
+            lookup_timer.expires_from_now(
+                boost::posix_time::milliseconds(lookup_timeout));
+            lookup_timer.async_wait(boost::bind(&RunningQuery::stop, this, false));
+        }
+        
+        // Setup the timer to send an answer (client_timeout)
+        if (client_timeout >= 0) {
+            client_timer.expires_from_now(
+                boost::posix_time::milliseconds(client_timeout));
+            client_timer.async_wait(boost::bind(&RunningQuery::clientTimeout, this));
+        }
+        
+        // should use NSAS for root servers
+        // Adding root servers if not a forwarder
+        if (upstream_->empty()) {
+            setZoneServersToRoot();
+        }
+
+        send();
+    }
+
+    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());
+        resolvercallback_->success(answer_message_);
+        answer_sent_ = true;
+    }
+
+    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_) {
+            resolvercallback_->success(answer_message_);
+        } else {
+            resolvercallback_->failure();
+        }
+        if (lookup_timer.cancel() != 0) {
+            return;
+        }
+        if (client_timer.cancel() != 0) {
+            return;
+        }
+        if (queries_out_ > 0) {
+            return;
+        }
+        delete this;
+    }
+
+    // This function is used as callback from DNSQuery.
+    virtual void operator()(UDPQuery::Result result) {
+        // XXX is this the place for TCP retry?
+        --queries_out_;
+        if (!done_ && result != UDPQuery::TIME_OUT) {
+            // we got an answer
+            Message incoming(Message::PARSE);
+            InputBuffer ibuf(buffer_->getData(), buffer_->getLength());
+            incoming.fromWire(ibuf);
+
+            if (upstream_->size() == 0 &&
+                incoming.getRcode() == Rcode::NOERROR()) {
+                done_ = handleRecursiveAnswer(incoming);
+            } else {
+                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)
+{
+    asio::io_service& io = dns_service_.get_io_service();
+
+    MessagePtr answer_message(new Message(Message::RENDER));
+    OutputBufferPtr buffer(new OutputBuffer(0));
+    
+    // It will delete itself when it is done
+    new RunningQuery(io, *question, answer_message, upstream_,
+                     upstream_root_, buffer, callback, query_timeout_,
+                     client_timeout_, lookup_timeout_, retries_);
+}
+
+void
+RecursiveQuery::resolve(const Question& question,
+                        MessagePtr answer_message,
+                        OutputBufferPtr buffer,
+                        DNSServer* server)
+{
+    // XXX: eventually we will need to be able to determine whether
+    // the message should be sent via TCP or UDP, or sent initially via
+    // UDP and then fall back to TCP on failure, but for the moment
+    // we're only going to handle UDP.
+    asio::io_service& io = dns_service_.get_io_service();
+
+    isc::resolve::ResolverInterface::CallbackPtr crs(
+        new isc::resolve::ResolverCallbackServer(server));
+    
+    // It will delete itself when it is done
+    new RunningQuery(io, question, answer_message, upstream_, upstream_root_,
+                         buffer, crs, query_timeout_, client_timeout_,
+                         lookup_timeout_, retries_);
+}
+
+
+
+} // namespace asiolink
diff --git a/src/lib/asiolink/recursive_query.h b/src/lib/asiolink/recursive_query.h
new file mode 100644
index 0000000..0660c09
--- /dev/null
+++ b/src/lib/asiolink/recursive_query.h
@@ -0,0 +1,113 @@
+// 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>
+
+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_;
+};
+
+}      // namespace asiolink
+#endif // __ASIOLINK_RECURSIVE_QUERY_H
diff --git a/src/lib/asiolink/simple_callback.h b/src/lib/asiolink/simple_callback.h
new file mode 100644
index 0000000..ab5deaf
--- /dev/null
+++ b/src/lib/asiolink/simple_callback.h
@@ -0,0 +1,71 @@
+// 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_SIMPLE_CALLBACK_H
+#define __ASIOLINK_SIMPLE_CALLBACK_H 1
+
+#include <asiolink/io_message.h>
+
+namespace asiolink {
+
+/// \brief The \c SimpleCallback class is an abstract base class for a
+/// simple callback function with the signature:
+///
+/// void simpleCallback(const IOMessage& io_message) const;
+///
+/// Specific derived class implementations are hidden within the
+/// implementation.  Instances of the derived classes can be called
+/// as functions via the operator() interface.  Pointers to these
+/// instances can then be provided to the \c IOService class
+/// via its constructor.
+///
+/// The \c SimpleCallback is expected to be used for basic, generic
+/// tasks such as checking for configuration changes.  It may also be
+/// used for testing purposes.
+class SimpleCallback {
+    ///
+    /// \name Constructors and Destructor
+    ///
+    /// Note: The copy constructor and the assignment operator are
+    /// intentionally defined as private, making this class non-copyable.
+    //@{
+private:
+    SimpleCallback(const SimpleCallback& source);
+    SimpleCallback& operator=(const SimpleCallback& source);
+protected:
+    /// \brief The default constructor.
+    ///
+    /// This is intentionally defined as \c protected as this base class
+    /// should never be instantiated (except as part of a derived class).
+    SimpleCallback() : self_(this) {}
+public:
+    /// \brief The destructor
+    virtual ~SimpleCallback() {}
+    /// \brief The function operator
+    //@}
+    ///
+    /// This makes its call indirectly via the "self" pointer, ensuring
+    /// that the function ultimately invoked will be the one in the derived
+    /// class.
+    ///
+    /// \param io_message The event message to handle
+    virtual void operator()(const IOMessage& io_message) const {
+        (*self_)(io_message);
+    }
+private:
+    SimpleCallback* self_;
+};
+
+}      // namespace asiolink
+#endif // __ASIOLINK_SIMPLE_CALLBACK_H
diff --git a/src/lib/asiolink/tcp_endpoint.h b/src/lib/asiolink/tcp_endpoint.h
new file mode 100644
index 0000000..8f6270f
--- /dev/null
+++ b/src/lib/asiolink/tcp_endpoint.h
@@ -0,0 +1,98 @@
+// 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 __TCP_ENDPOINT_H
+#define __TCP_ENDPOINT_H 1
+
+#ifndef ASIO_HPP
+#error "asio.hpp must be included before including this, see asiolink.h as to why"
+#endif
+
+#include <asiolink/io_endpoint.h>
+
+namespace asiolink {
+
+/// \brief The \c TCPEndpoint class is a concrete derived class of
+/// \c IOEndpoint that represents an endpoint of a TCP connection.
+///
+/// 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.
+class TCPEndpoint : public IOEndpoint {
+public:
+    ///
+    /// \name Constructors and Destructor
+    ///
+    //@{
+    /// \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)),
+        asio_endpoint_(*asio_endpoint_placeholder_)
+    {}
+
+    /// \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_(NULL), asio_endpoint_(asio_endpoint)
+    {}
+
+    /// \brief The destructor.
+    ~TCPEndpoint() { delete asio_endpoint_placeholder_; }
+    //@}
+
+    IOAddress getAddress() const {
+        return (asio_endpoint_.address());
+    }
+
+    uint16_t getPort() const {
+        return (asio_endpoint_.port());
+    }
+
+    short getProtocol() const {
+        return (asio_endpoint_.protocol().protocol());
+    }
+
+    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 {
+        return (asio_endpoint_);
+    }
+
+private:
+    const asio::ip::tcp::endpoint* asio_endpoint_placeholder_;
+    const asio::ip::tcp::endpoint& asio_endpoint_;
+};
+
+}      // namespace asiolink
+#endif // __TCP_ENDPOINT_H
diff --git a/src/lib/asiolink/tcp_server.cc b/src/lib/asiolink/tcp_server.cc
new file mode 100644
index 0000000..2bdfc60
--- /dev/null
+++ b/src/lib/asiolink/tcp_server.cc
@@ -0,0 +1,194 @@
+// 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 <boost/shared_array.hpp>
+
+// unistd is needed for asio.hpp with SunStudio
+#include <unistd.h>
+
+#include <asio.hpp>
+
+#include <log/dummylog.h>
+
+#include <asiolink/tcp_endpoint.h>
+#include <asiolink/tcp_socket.h>
+
+#include <asiolink/tcp_server.h>
+
+
+using namespace asio;
+using asio::ip::udp;
+using asio::ip::tcp;
+
+using namespace std;
+using namespace isc::dns;
+
+namespace asiolink {
+
+/// The following functions implement the \c TCPServer class.
+///
+/// The constructor
+TCPServer::TCPServer(io_service& io_service,
+                     const ip::address& addr, const uint16_t port, 
+                     const SimpleCallback* checkin,
+                     const DNSLookup* lookup,
+                     const DNSAnswer* answer) :
+    io_(io_service), done_(false),
+    checkin_callback_(checkin), lookup_callback_(lookup),
+    answer_callback_(answer)
+{
+    tcp::endpoint endpoint(addr, port);
+    acceptor_.reset(new tcp::acceptor(io_service));
+    acceptor_->open(endpoint.protocol());
+    // Set v6-only (we use a separate instantiation for v4,
+    // otherwise asio will bind to both v4 and v6
+    if (addr.is_v6()) {
+        acceptor_->set_option(ip::v6_only(true));
+    }
+    acceptor_->set_option(tcp::acceptor::reuse_address(true));
+    acceptor_->bind(endpoint);
+    acceptor_->listen();
+}
+
+void
+TCPServer::operator()(error_code ec, size_t length) {
+    /// Because the coroutine reeentry block is implemented as
+    /// a switch statement, inline variable declarations are not
+    /// permitted.  Certain variables used below can be declared here.
+    boost::array<const_buffer,2> bufs;
+    OutputBuffer lenbuf(TCP_MESSAGE_LENGTHSIZE);
+
+    CORO_REENTER (this) {
+        do {
+            /// 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,
+            /// try again
+            do {
+                CORO_YIELD acceptor_->async_accept(*socket_, *this);
+            } while (!ec);
+
+            /// Fork the coroutine by creating a copy of this one and
+            /// scheduling it on the ASIO service queue.  The parent
+            /// will continue listening for DNS connections while the
+            /// handles the one that has just arrived.
+            CORO_FORK io_.post(TCPServer(*this));
+        } while (is_parent());
+
+        /// Instantiate the data buffer that will be used by the
+        /// asynchronous read call.
+        data_.reset(new char[MAX_LENGTH]);
+
+        /// Read the message, in two parts.  First, the message length:
+        CORO_YIELD async_read(*socket_, asio::buffer(data_.get(),
+                              TCP_MESSAGE_LENGTHSIZE), *this);
+        if (ec) {
+            CORO_YIELD return;
+        }
+
+        /// Now read the message itself. (This is done in a different scope
+        /// to allow inline variable declarations.)
+        CORO_YIELD {
+            InputBuffer dnsbuffer((const void *) data_.get(), length);
+            uint16_t msglen = dnsbuffer.readUint16();
+            async_read(*socket_, asio::buffer(data_.get(), msglen), *this);
+        }
+
+        if (ec) {
+            CORO_YIELD return;
+        }
+
+        // Create an \c IOMessage object to store the query.
+        //
+        // (XXX: It would be good to write a factory function
+        // that would quickly generate an IOMessage object without
+        // all these calls to "new".)
+        peer_.reset(new TCPEndpoint(socket_->remote_endpoint()));
+        iosock_.reset(new TCPSocket(*socket_));
+        io_message_.reset(new IOMessage(data_.get(), length, *iosock_, *peer_));
+        bytes_ = length;
+
+        // Perform any necessary operations prior to processing the incoming
+        // packet (e.g., checking for queued configuration messages).
+        //
+        // (XXX: it may be a performance issue to have this called for
+        // every single incoming packet; we may wish to throttle it somehow
+        // in the future.)
+        if (checkin_callback_ != NULL) {
+            (*checkin_callback_)(*io_message_);
+        }
+
+        // If we don't have a DNS Lookup provider, there's no point in
+        // continuing; we exit the coroutine permanently.
+        if (lookup_callback_ == NULL) {
+            CORO_YIELD return;
+        }
+
+        // Reset or instantiate objects that will be needed by the
+        // DNS lookup and the write call.
+        respbuf_.reset(new OutputBuffer(0));
+        query_message_.reset(new Message(Message::PARSE));
+        answer_message_.reset(new Message(Message::RENDER));
+
+        // Schedule a DNS lookup, and yield.  When the lookup is
+        // finished, the coroutine will resume immediately after
+        // this point.
+        CORO_YIELD io_.post(AsyncLookup<TCPServer>(*this));
+
+        // The 'done_' flag indicates whether we have an answer
+        // to send back.  If not, exit the coroutine permanently.
+        if (!done_) {
+            CORO_YIELD return;
+        }
+
+        // Call the DNS answer provider to render the answer into
+        // wire format
+        (*answer_callback_)(*io_message_, query_message_,
+                            answer_message_, respbuf_);
+
+        // Set up the response, beginning with two length bytes.
+        lenbuf.writeUint16(respbuf_->getLength());
+        bufs[0] = buffer(lenbuf.getData(), lenbuf.getLength());
+        bufs[1] = buffer(respbuf_->getData(), respbuf_->getLength());
+
+        // Begin an asynchronous send, and then yield.  When the
+        // send completes, we will resume immediately after this point
+        // (though we have nothing further to do, so the coroutine
+        // will simply exit at that time).
+        CORO_YIELD async_write(*socket_, bufs, *this);
+    }
+}
+
+/// Call the DNS lookup provider.  (Expected to be called by the
+/// AsyncLookup<TCPServer> handler.)
+void
+TCPServer::asyncLookup() {
+    (*lookup_callback_)(*io_message_, query_message_,
+                        answer_message_, respbuf_, this);
+}
+
+/// Post this coroutine on the ASIO service queue so that it will
+/// resume processing where it left off.  The 'done' parameter indicates
+/// whether there is an answer to return to the client.
+void
+TCPServer::resume(const bool done) {
+    done_ = done;
+    io_.post(*this);
+}
+
+} // namespace asiolink
+
diff --git a/src/lib/asiolink/tcp_server.h b/src/lib/asiolink/tcp_server.h
new file mode 100644
index 0000000..9b985ce
--- /dev/null
+++ b/src/lib/asiolink/tcp_server.h
@@ -0,0 +1,119 @@
+// 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 __TCP_SERVER_H
+#define __TCP_SERVER_H 1
+
+#ifndef ASIO_HPP
+#error "asio.hpp must be included before including this, see asiolink.h as to why"
+#endif
+
+#include <boost/shared_array.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <asiolink/asiolink.h>
+#include <coroutine.h>
+
+
+namespace asiolink {
+
+/// \brief A TCP-specific \c DNSServer object.
+///
+/// This class inherits from both \c DNSServer and from \c coroutine,
+/// defined in coroutine.h. 
+class TCPServer : public virtual DNSServer, public virtual coroutine {
+public:
+    explicit TCPServer(asio::io_service& io_service,
+                       const asio::ip::address& addr, const uint16_t port, 
+                       const SimpleCallback* checkin = NULL,
+                       const DNSLookup* lookup = NULL,
+                       const DNSAnswer* answer = NULL);
+
+    void operator()(asio::error_code ec = asio::error_code(),
+                    size_t length = 0);
+    void asyncLookup();
+    void resume(const bool done);
+    bool hasAnswer() { return (done_); }
+    int value() { return (get_value()); }
+
+    DNSServer* clone() {
+        TCPServer* s = new TCPServer(*this);
+        return (s);
+    }
+
+private:
+    enum { MAX_LENGTH = 65535 };
+    static const size_t TCP_MESSAGE_LENGTHSIZE = 2;
+
+    // The ASIO service object
+    asio::io_service& io_;
+
+    // Class member variables which are dynamic, and changes to which
+    // need to accessible from both sides of a coroutine fork or from
+    // outside of the coroutine (i.e., from an asynchronous I/O call),
+    // should be declared here as pointers and allocated in the
+    // constructor or in the coroutine.  This allows state information
+    // to persist when an individual copy of the coroutine falls out
+    // scope while waiting for an event, *so long as* there is another
+    // object that is referencing the same data.  As a side-benefit, using
+    // pointers also reduces copy overhead for coroutine objects.
+    //
+    // Note: Currently these objects are allocated by "new" in the
+    // constructor, or in the function operator while processing a query.
+    // Repeated allocations from the heap for every incoming query is
+    // clearly a performance issue; this must be optimized in the future.
+    // The plan is to have a structure pre-allocate several "server state"
+    // objects which can be pulled off a free list and placed on an in-use
+    // list whenever a query comes in.  This will serve the dual purpose
+    // of improving performance and guaranteeing that state information
+    // will *not* be destroyed when any one instance of the coroutine
+    // falls out of scope while waiting for an event.
+    //
+    // An ASIO acceptor object to handle new connections.  Created in
+    // the constructor.
+    boost::shared_ptr<asio::ip::tcp::acceptor> acceptor_;
+
+    // Socket used to for listen for queries.  Created in the
+    // constructor and stored in a shared_ptr because socket objects
+    // are not copyable.
+    boost::shared_ptr<asio::ip::tcp::socket> socket_;
+
+    // The buffer into which the response is written
+    boost::shared_ptr<isc::dns::OutputBuffer> respbuf_;
+
+    // \c IOMessage and \c Message objects to be passed to the
+    // DNS lookup and answer providers
+    boost::shared_ptr<asiolink::IOMessage> io_message_;
+    isc::dns::MessagePtr query_message_;
+    isc::dns::MessagePtr answer_message_;
+
+    // The buffer into which the query packet is written
+    boost::shared_array<char>data_;
+
+    // State information that is entirely internal to a given instance
+    // of the coroutine can be declared here.
+    size_t bytes_;
+    bool done_;
+
+    // Callback functions provided by the caller
+    const SimpleCallback* checkin_callback_;
+    const DNSLookup* lookup_callback_;
+    const DNSAnswer* answer_callback_;
+
+    boost::shared_ptr<IOEndpoint> peer_;
+    boost::shared_ptr<IOSocket> iosock_;
+};
+
+}      // namespace asiolink
+#endif // __TCP_SERVER_H
diff --git a/src/lib/asiolink/tcp_socket.h b/src/lib/asiolink/tcp_socket.h
new file mode 100644
index 0000000..03713a5
--- /dev/null
+++ b/src/lib/asiolink/tcp_socket.h
@@ -0,0 +1,52 @@
+// 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 __TCP_SOCKET_H
+#define __TCP_SOCKET_H 1
+
+#ifndef ASIO_HPP
+#error "asio.hpp must be included before including this, see asiolink.h as to why"
+#endif
+
+#include <asiolink/io_socket.h>
+
+namespace asiolink {
+
+/// \brief The \c TCPSocket class is a concrete derived class of
+/// \c IOSocket that represents a TCP socket.
+///
+/// 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 IOSocket.
+/// This design may be changed when we generalize the wrapper interface.
+class TCPSocket : public IOSocket {
+private:
+    TCPSocket(const TCPSocket& source);
+    TCPSocket& operator=(const TCPSocket& source);
+public:
+    /// \brief Constructor from an ASIO TCP socket.
+    ///
+    /// \param socket The ASIO representation of the TCP socket.
+    TCPSocket(asio::ip::tcp::socket& socket) : socket_(socket) {}
+
+    int getNative() const { return (socket_.native()); }
+    int getProtocol() const { return (IPPROTO_TCP); }
+
+private:
+    asio::ip::tcp::socket& socket_;
+};
+
+
+}      // namespace asiolink
+#endif // __TCP_SOCKET_H
diff --git a/src/lib/asiolink/tcpdns.cc b/src/lib/asiolink/tcpdns.cc
deleted file mode 100644
index c00b87a..0000000
--- a/src/lib/asiolink/tcpdns.cc
+++ /dev/null
@@ -1,194 +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.
-
-#include <config.h>
-
-#include <unistd.h>             // for some IPC/network system calls
-#include <sys/socket.h>
-#include <netinet/in.h>
-
-#include <asio.hpp>
-#include <asio/ip/address.hpp>
-
-#include <boost/array.hpp>
-#include <boost/shared_ptr.hpp>
-
-#include <dns/buffer.h>
-#include <dns/message.h>
-
-#include <asiolink.h>
-#include <internal/coroutine.h>
-#include <internal/tcpdns.h>
-
-using namespace asio;
-using asio::ip::udp;
-using asio::ip::tcp;
-
-using namespace std;
-using namespace isc::dns;
-
-namespace asiolink {
-/// The following functions implement the \c UDPServer class.
-///
-/// The constructor
-TCPServer::TCPServer(io_service& io_service,
-                     const ip::address& addr, const uint16_t port, 
-                     const SimpleCallback* checkin,
-                     const DNSLookup* lookup,
-                     const DNSAnswer* answer) :
-    io_(io_service), done_(false),
-    checkin_callback_(checkin), lookup_callback_(lookup),
-    answer_callback_(answer)
-{
-    tcp::endpoint endpoint(addr, port);
-    acceptor_.reset(new tcp::acceptor(io_service));
-    acceptor_->open(endpoint.protocol());
-    // Set v6-only (we use a separate instantiation for v4,
-    // otherwise asio will bind to both v4 and v6
-    if (addr.is_v6()) {
-        acceptor_->set_option(ip::v6_only(true));
-    }
-    acceptor_->set_option(tcp::acceptor::reuse_address(true));
-    acceptor_->bind(endpoint);
-    acceptor_->listen();
-}
-
-void
-TCPServer::operator()(error_code ec, size_t length) {
-    /// Because the coroutine reeentry block is implemented as
-    /// a switch statement, inline variable declarations are not
-    /// permitted.  Certain variables used below can be declared here.
-    boost::array<const_buffer,2> bufs;
-    OutputBuffer lenbuf(TCP_MESSAGE_LENGTHSIZE);
-
-    CORO_REENTER (this) {
-        do {
-            /// 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,
-            /// try again
-            do {
-                CORO_YIELD acceptor_->async_accept(*socket_, *this);
-            } while (!ec);
-
-            /// Fork the coroutine by creating a copy of this one and
-            /// scheduling it on the ASIO service queue.  The parent
-            /// will continue listening for DNS connections while the
-            /// handles the one that has just arrived.
-            CORO_FORK io_.post(TCPServer(*this));
-        } while (is_parent());
-
-        /// Instantiate the data buffer that will be used by the
-        /// asynchronous read call.
-        data_.reset(new char[MAX_LENGTH]);
-
-        /// Read the message, in two parts.  First, the message length:
-        CORO_YIELD async_read(*socket_, asio::buffer(data_.get(),
-                              TCP_MESSAGE_LENGTHSIZE), *this);
-        if (ec) {
-            CORO_YIELD return;
-        }
-
-        /// Now read the message itself. (This is done in a different scope
-        /// to allow inline variable declarations.)
-        CORO_YIELD {
-            InputBuffer dnsbuffer((const void *) data_.get(), length);
-            uint16_t msglen = dnsbuffer.readUint16();
-            async_read(*socket_, asio::buffer(data_.get(), msglen), *this);
-        }
-
-        if (ec) {
-            CORO_YIELD return;
-        }
-
-        // Create an \c IOMessage object to store the query.
-        //
-        // (XXX: It would be good to write a factory function
-        // that would quickly generate an IOMessage object without
-        // all these calls to "new".)
-        peer_.reset(new TCPEndpoint(socket_->remote_endpoint()));
-        iosock_.reset(new TCPSocket(*socket_));
-        io_message_.reset(new IOMessage(data_.get(), length, *iosock_, *peer_));
-        bytes_ = length;
-
-        // Perform any necessary operations prior to processing the incoming
-        // packet (e.g., checking for queued configuration messages).
-        //
-        // (XXX: it may be a performance issue to have this called for
-        // every single incoming packet; we may wish to throttle it somehow
-        // in the future.)
-        if (checkin_callback_ != NULL) {
-            (*checkin_callback_)(*io_message_);
-        }
-
-        // If we don't have a DNS Lookup provider, there's no point in
-        // continuing; we exit the coroutine permanently.
-        if (lookup_callback_ == NULL) {
-            CORO_YIELD return;
-        }
-
-        // Reset or instantiate objects that will be needed by the
-        // DNS lookup and the write call.
-        respbuf_.reset(new OutputBuffer(0));
-        query_message_.reset(new Message(Message::PARSE));
-        answer_message_.reset(new Message(Message::RENDER));
-
-        // Schedule a DNS lookup, and yield.  When the lookup is
-        // finished, the coroutine will resume immediately after
-        // this point.
-        CORO_YIELD io_.post(AsyncLookup<TCPServer>(*this));
-
-        // The 'done_' flag indicates whether we have an answer
-        // to send back.  If not, exit the coroutine permanently.
-        if (!done_) {
-            CORO_YIELD return;
-        }
-
-        // Call the DNS answer provider to render the answer into
-        // wire format
-        (*answer_callback_)(*io_message_, query_message_,
-                            answer_message_, respbuf_);
-
-        // Set up the response, beginning with two length bytes.
-        lenbuf.writeUint16(respbuf_->getLength());
-        bufs[0] = buffer(lenbuf.getData(), lenbuf.getLength());
-        bufs[1] = buffer(respbuf_->getData(), respbuf_->getLength());
-
-        // Begin an asynchronous send, and then yield.  When the
-        // send completes, we will resume immediately after this point
-        // (though we have nothing further to do, so the coroutine
-        // will simply exit at that time).
-        CORO_YIELD async_write(*socket_, bufs, *this);
-    }
-}
-
-/// Call the DNS lookup provider.  (Expected to be called by the
-/// AsyncLookup<TCPServer> handler.)
-void
-TCPServer::asyncLookup() {
-    (*lookup_callback_)(*io_message_, query_message_,
-                        answer_message_, respbuf_, this);
-}
-
-/// Post this coroutine on the ASIO service queue so that it will
-/// resume processing where it left off.  The 'done' parameter indicates
-/// whether there is an answer to return to the client.
-void
-TCPServer::resume(const bool done) {
-    done_ = done;
-    io_.post(*this);
-}
-
-}
diff --git a/src/lib/asiolink/tests/Makefile.am b/src/lib/asiolink/tests/Makefile.am
index e301fb2..7fe268e 100644
--- a/src/lib/asiolink/tests/Makefile.am
+++ b/src/lib/asiolink/tests/Makefile.am
@@ -17,7 +17,13 @@ if HAVE_GTEST
 TESTS += run_unittests
 run_unittests_SOURCES = $(top_srcdir)/src/lib/dns/tests/unittest_util.h
 run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
-run_unittests_SOURCES += asiolink_unittest.cc
+run_unittests_SOURCES += udp_query_unittest.cc
+run_unittests_SOURCES += ioaddress_unittest.cc
+run_unittests_SOURCES += ioendpoint_unittest.cc
+run_unittests_SOURCES += iosocket_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 += run_unittests.cc
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) $(LOG4CXX_LDFLAGS)
@@ -33,6 +39,10 @@ run_unittests_CXXFLAGS = $(AM_CXXFLAGS)
 if USE_GXX
 run_unittests_CXXFLAGS += -Wno-unused-parameter
 endif
+if USE_CLANGPP
+# Same for clang++, but we need to turn off -Werror completely.
+run_unittests_CXXFLAGS += -Wno-error
+endif
 endif
 
 noinst_PROGRAMS = $(TESTS)
diff --git a/src/lib/asiolink/tests/asiolink_unittest.cc b/src/lib/asiolink/tests/asiolink_unittest.cc
deleted file mode 100644
index 2044d50..0000000
--- a/src/lib/asiolink/tests/asiolink_unittest.cc
+++ /dev/null
@@ -1,1207 +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.
-
-#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/asiolink.h>
-#include <asiolink/iosocket.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};
-// TODO: Consider this margin
-const boost::posix_time::time_duration TIMER_MARGIN_MSEC =
-    boost::posix_time::milliseconds(50);
-
-TEST(IOAddressTest, fromText) {
-    IOAddress io_address_v4("192.0.2.1");
-    EXPECT_EQ("192.0.2.1", io_address_v4.toText());
-
-    IOAddress io_address_v6("2001:db8::1234");
-    EXPECT_EQ("2001:db8::1234", io_address_v6.toText());
-
-    // bogus IPv4 address-like input
-    EXPECT_THROW(IOAddress("192.0.2.2.1"), IOError);
-
-    // bogus IPv4 address-like input: out-of-range octet
-    EXPECT_THROW(IOAddress("192.0.2.300"), IOError);
-
-    // bogus IPv6 address-like input
-    EXPECT_THROW(IOAddress("2001:db8:::1234"), IOError);
-
-    // bogus IPv6 address-like input
-    EXPECT_THROW(IOAddress("2001:db8::efgh"), IOError);
-}
-
-TEST(IOAddressTest, Equality) {
-    EXPECT_TRUE(IOAddress("192.0.2.1") == IOAddress("192.0.2.1"));
-    EXPECT_FALSE(IOAddress("192.0.2.1") != IOAddress("192.0.2.1"));
-
-    EXPECT_TRUE(IOAddress("192.0.2.1") != IOAddress("192.0.2.2"));
-    EXPECT_FALSE(IOAddress("192.0.2.1") == IOAddress("192.0.2.2"));
-
-    EXPECT_TRUE(IOAddress("2001:db8::12") == IOAddress("2001:0DB8:0:0::0012"));
-    EXPECT_FALSE(IOAddress("2001:db8::12") != IOAddress("2001:0DB8:0:0::0012"));
-
-    EXPECT_TRUE(IOAddress("2001:db8::1234") != IOAddress("2001:db8::1235"));
-    EXPECT_FALSE(IOAddress("2001:db8::1234") == IOAddress("2001:db8::1235"));
-
-    EXPECT_TRUE(IOAddress("2001:db8::1234") != IOAddress("192.0.2.3"));
-    EXPECT_FALSE(IOAddress("2001:db8::1234") == IOAddress("192.0.2.3"));
-}
-
-TEST(IOEndpointTest, createUDPv4) {
-    const IOEndpoint* ep;
-    ep = IOEndpoint::create(IPPROTO_UDP, IOAddress("192.0.2.1"), 5300);
-    EXPECT_EQ("192.0.2.1", ep->getAddress().toText());
-    EXPECT_EQ(5300, ep->getPort());
-    EXPECT_EQ(AF_INET, ep->getFamily());
-    EXPECT_EQ(AF_INET, ep->getAddress().getFamily());
-    EXPECT_EQ(IPPROTO_UDP, ep->getProtocol());
-}
-
-TEST(IOEndpointTest, createTCPv4) {
-    const IOEndpoint* ep;
-    ep = IOEndpoint::create(IPPROTO_TCP, IOAddress("192.0.2.1"), 5301);
-    EXPECT_EQ("192.0.2.1", ep->getAddress().toText());
-    EXPECT_EQ(5301, ep->getPort());
-    EXPECT_EQ(AF_INET, ep->getFamily());
-    EXPECT_EQ(AF_INET, ep->getAddress().getFamily());
-    EXPECT_EQ(IPPROTO_TCP, ep->getProtocol());
-}
-
-TEST(IOEndpointTest, createUDPv6) {
-    const IOEndpoint* ep;
-    ep = IOEndpoint::create(IPPROTO_UDP, IOAddress("2001:db8::1234"), 5302);
-    EXPECT_EQ("2001:db8::1234", ep->getAddress().toText());
-    EXPECT_EQ(5302, ep->getPort());
-    EXPECT_EQ(AF_INET6, ep->getFamily());
-    EXPECT_EQ(AF_INET6, ep->getAddress().getFamily());
-    EXPECT_EQ(IPPROTO_UDP, ep->getProtocol());
-}
-
-TEST(IOEndpointTest, createTCPv6) {
-    const IOEndpoint* ep;
-    ep = IOEndpoint::create(IPPROTO_TCP, IOAddress("2001:db8::1234"), 5303);
-    EXPECT_EQ("2001:db8::1234", ep->getAddress().toText());
-    EXPECT_EQ(5303, ep->getPort());
-    EXPECT_EQ(AF_INET6, ep->getFamily());
-    EXPECT_EQ(AF_INET6, ep->getAddress().getFamily());
-    EXPECT_EQ(IPPROTO_TCP, ep->getProtocol());
-}
-
-TEST(IOEndpointTest, createIPProto) {
-    EXPECT_THROW(IOEndpoint::create(IPPROTO_IP, IOAddress("192.0.2.1"),
-                                    5300)->getAddress().toText(),
-                 IOError);
-}
-
-TEST(IOSocketTest, dummySockets) {
-    EXPECT_EQ(IPPROTO_UDP, IOSocket::getDummyUDPSocket().getProtocol());
-    EXPECT_EQ(IPPROTO_TCP, IOSocket::getDummyTCPSocket().getProtocol());
-    EXPECT_EQ(-1, IOSocket::getDummyUDPSocket().getNative());
-    EXPECT_EQ(-1, IOSocket::getDummyTCPSocket().getNative());
-}
-
-TEST(IOServiceTest, badPort) {
-    IOService io_service;
-    EXPECT_THROW(DNSService(io_service, *"65536", true, false, NULL, NULL, NULL), IOError);
-    EXPECT_THROW(DNSService(io_service, *"5300.0", true, false, NULL, NULL, NULL), IOError);
-    EXPECT_THROW(DNSService(io_service, *"-1", true, false, NULL, NULL, NULL), IOError);
-    EXPECT_THROW(DNSService(io_service, *"domain", true, false, NULL, NULL, NULL), IOError);
-}
-
-TEST(IOServiceTest, badAddress) {
-    IOService io_service;
-    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"192.0.2.1.1", NULL, NULL, NULL), IOError);
-    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"2001:db8:::1", NULL, NULL, NULL), IOError);
-    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"localhost", NULL, NULL, NULL), IOError);
-}
-
-TEST(IOServiceTest, unavailableAddress) {
-    IOService io_service;
-    // These addresses should generally be unavailable as a valid local
-    // address, although there's no guarantee in theory.
-    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"192.0.2.0", NULL, NULL, NULL), IOError);
-
-    // Some OSes would simply reject binding attempt for an AF_INET6 socket
-    // to an IPv4-mapped IPv6 address.  Even if those that allow it, since
-    // the corresponding IPv4 address is the same as the one used in the
-    // AF_INET socket case above, it should at least show the same result
-    // as the previous one.
-    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"::ffff:192.0.2.0", NULL, NULL, NULL), IOError);
-}
-
-TEST(IOServiceTest, duplicateBind_v6) {
-    // In each sub test case, second attempt should fail due to duplicate bind
-    IOService io_service;
-
-    // IPv6, "any" address
-    DNSService* dns_service = new DNSService(io_service, *TEST_SERVER_PORT, false, true, NULL, NULL, NULL);
-    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, false, true, NULL, NULL, NULL), IOError);
-    delete dns_service;
-
-}
-
-TEST(IOServiceTest, duplicateBind_v6_address) {
-    // In each sub test case, second attempt should fail due to duplicate bind
-    IOService io_service;
-
-    // IPv6, specific address
-    DNSService* dns_service = new DNSService(io_service, *TEST_SERVER_PORT, *TEST_IPV6_ADDR, NULL, NULL, NULL);
-    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *TEST_IPV6_ADDR, NULL, NULL, NULL), IOError);
-    delete dns_service;
-
-}
-
-TEST(IOServiceTest, duplicateBind_v4) {
-    // In each sub test case, second attempt should fail due to duplicate bind
-    IOService io_service;
-
-    // IPv4, "any" address
-    DNSService* dns_service = new DNSService(io_service, *TEST_SERVER_PORT, true, false, NULL, NULL, NULL);
-    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, true, false, NULL, NULL, NULL), IOError);
-    delete dns_service;
-
-}
-
-TEST(IOServiceTest, duplicateBind_v4_address) {
-    // In each sub test case, second attempt should fail due to duplicate bind
-    IOService io_service;
-
-    // IPv4, specific address
-    DNSService* dns_service = new DNSService(io_service, *TEST_SERVER_PORT, *TEST_IPV4_ADDR, NULL, NULL, NULL);
-    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *TEST_IPV4_ADDR, NULL, NULL, NULL), IOError);
-    delete dns_service;
-}
-
-// Disabled because IPv4-mapped addresses don't seem to be working with
-// the IOService constructor
-TEST(IOServiceTest, DISABLED_IPv4MappedDuplicateBind) {
-    IOService io_service;
-    // Duplicate bind on IPv4-mapped IPv6 address
-    DNSService* dns_service = new DNSService(io_service, *TEST_SERVER_PORT, *"127.0.0.1", NULL, NULL, NULL);
-    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"::ffff:127.0.0.1", NULL, NULL, NULL), IOError);
-    delete dns_service;
-
-    // XXX:
-    // Currently, this throws an "invalid argument" exception.  I have
-    // not been able to get IPv4-mapped addresses to work.
-    dns_service = new DNSService(io_service, *TEST_SERVER_PORT, *"::ffff:127.0.0.1", NULL, NULL, NULL);
-    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"127.0.0.1", NULL, NULL, NULL), IOError);
-    delete dns_service;
-}
-
-// 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 ASIOLinkTest use actual network services and may
-// involve undesirable side effects such as blocking.
-class ASIOLinkTest : public ::testing::Test {
-protected:
-    ASIOLinkTest();
-    ~ASIOLinkTest() {
-        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),
-            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_;
-    };
-
-private:
-    class ASIOCallBack : public SimpleCallback {
-    public:
-        ASIOCallBack(ASIOLinkTest* test_obj) : test_obj_(test_obj) {}
-        void operator()(const IOMessage& io_message) const {
-            test_obj_->callBack(io_message);
-        }
-    private:
-        ASIOLinkTest* 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_;
-};
-
-ASIOLinkTest::ASIOLinkTest() :
-    dns_service_(NULL), callback_(NULL), sock_(-1), res_(NULL)
-{
-    io_service_ = new IOService();
-    setDNSService(true, true);
-}
-
-TEST_F(ASIOLinkTest, v6UDPSend) {
-    doTest(AF_INET6, IPPROTO_UDP);
-}
-
-TEST_F(ASIOLinkTest, v6TCPSend) {
-    doTest(AF_INET6, IPPROTO_TCP);
-}
-
-TEST_F(ASIOLinkTest, v4UDPSend) {
-    doTest(AF_INET, IPPROTO_UDP);
-}
-
-TEST_F(ASIOLinkTest, v4TCPSend) {
-    doTest(AF_INET, IPPROTO_TCP);
-}
-
-TEST_F(ASIOLinkTest, 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(ASIOLinkTest, v6TCPSendSpecific) {
-    setDNSService(*TEST_IPV6_ADDR);
-    doTest(AF_INET6, IPPROTO_TCP);
-
-    EXPECT_THROW(sendTCP(AF_INET), IOError);
-}
-
-TEST_F(ASIOLinkTest, v4UDPSendSpecific) {
-    setDNSService(*TEST_IPV4_ADDR);
-    doTest(AF_INET, IPPROTO_UDP);
-}
-
-TEST_F(ASIOLinkTest, v4TCPSendSpecific) {
-    setDNSService(*TEST_IPV4_ADDR);
-    doTest(AF_INET, IPPROTO_TCP);
-
-    EXPECT_THROW(sendTCP(AF_INET6), IOError);
-}
-
-TEST_F(ASIOLinkTest, v6AddServer) {
-    setDNSService();
-    dns_service_->addServer(*TEST_SERVER_PORT, TEST_IPV6_ADDR);
-    doTest(AF_INET6, IPPROTO_TCP);
-
-    EXPECT_THROW(sendTCP(AF_INET), IOError);
-}
-
-TEST_F(ASIOLinkTest, v4AddServer) {
-    setDNSService();
-    dns_service_->addServer(*TEST_SERVER_PORT, TEST_IPV4_ADDR);
-    doTest(AF_INET, IPPROTO_TCP);
-
-    EXPECT_THROW(sendTCP(AF_INET6), IOError);
-}
-
-TEST_F(ASIOLinkTest, DISABLED_clearServers) {
-    // FIXME: Enable when clearServers actually close the sockets
-    //    See #388
-    setDNSService();
-    dns_service_->clearServers();
-
-    EXPECT_THROW(sendTCP(AF_INET), IOError);
-    EXPECT_THROW(sendTCP(AF_INET6), IOError);
-}
-
-TEST_F(ASIOLinkTest, 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(ASIOLinkTest, 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(ASIOLinkTest, 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(ASIOLinkTest, recursiveSetupV6) {
-    setDNSService(false, true);
-    uint16_t port = boost::lexical_cast<uint16_t>(TEST_CLIENT_PORT);
-    EXPECT_NO_THROW(RecursiveQuery(*dns_service_,
-                                   singleAddress(TEST_IPV6_ADDR, port),
-                                   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(ASIOLinkTest, 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 ASIOLinkTest::recvUDP()
-            recv_options = MSG_DONTWAIT;
-        } else {
-            isc_throw(IOError, "set RCVTIMEO failed: " << strerror(errno));
-        }
-    }
-    return recv_options;
-}
-
-// try to read from the socket max time
-// *num is incremented for every succesfull read
-// returns true if it can read max times, false otherwise
-bool tryRead(int sock_, int recv_options, size_t max, int* num) {
-    size_t i = 0;
-    do {
-        char inbuff[512];
-        if (recv(sock_, inbuff, sizeof(inbuff), recv_options) < 0) {
-            return false;
-        } else {
-            ++i;
-            ++*num;
-        }
-    } while (i < max);
-    return true;
-}
-
-
-// Test it tries the correct amount of times before giving up
-TEST_F(ASIOLinkTest, forwardQueryTimeout) {
-    // Prepare the service (we do not use the common setup, we do not answer
-    setDNSService();
-
-    // Prepare the socket
-    sock_ = createTestSocket();
-
-    // Prepare the server
-    bool done(true);
-    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(ASIOLinkTest, forwardClientTimeout) {
-    // Prepare the service (we do not use the common setup, we do not answer
-    setDNSService();
-
-    sock_ = createTestSocket();
-
-    // Prepare the server
-    bool done(true);
-    MockServerStop server(*io_service_, &done);
-
-    MessagePtr answer(new Message(Message::RENDER));
-
-    // Do the answer
-    const uint16_t port = boost::lexical_cast<uint16_t>(TEST_CLIENT_PORT);
-    // Set it up to retry twice before client timeout fires
-    // Since the lookup timer has not fired, it should retry
-    // a third time
-    RecursiveQuery query(*dns_service_,
-                         singleAddress(TEST_IPV4_ADDR, port),
-                         singleAddress(TEST_IPV4_ADDR, port),
-                         50, 120, 1000, 3);
-    Question question(Name("example.net"), RRClass::IN(), RRType::A());
-    OutputBufferPtr buffer(new OutputBuffer(0));
-    query.resolve(question, answer, buffer, &server);
-
-    // Run the test
-    io_service_->run();
-
-    // we know it'll fail, so make it a shorter timeout
-    int recv_options = setSocketTimeout(sock_, 1, 0);
-
-    // Try to read 5 times, should stop after 3 reads
-    int num = 0;
-    bool read_success = tryRead(sock_, recv_options, 5, &num);
-
-    // The query should fail (for resolver it should send back servfail,
-    // but currently, and perhaps for forwarder in general, the effect
-    // will be the same as on a lookup timeout, i.e. no answer is sent
-    // back)
-    EXPECT_FALSE(done);
-    EXPECT_EQ(3, num);
-    EXPECT_FALSE(read_success);
-}
-
-// If we set lookup timeout to lower than querytimeout*retries, we should
-// fail before the full amount of retries
-TEST_F(ASIOLinkTest, forwardLookupTimeout) {
-    // Prepare the service (we do not use the common setup, we do not answer
-    setDNSService();
-
-    // Prepare the socket
-    sock_ = createTestSocket();
-
-    // Prepare the server
-    bool done(true);
-    MockServerStop server(*io_service_, &done);
-
-    MessagePtr answer(new Message(Message::RENDER));
-
-    // Do the answer
-    const uint16_t port = boost::lexical_cast<uint16_t>(TEST_CLIENT_PORT);
-    // Set up the test so that it will retry 5 times, but the lookup
-    // timeout will fire after only 3 normal timeouts
-    RecursiveQuery query(*dns_service_,
-                         singleAddress(TEST_IPV4_ADDR, port),
-                         singleAddress(TEST_IPV4_ADDR, port),
-                         50, 4000, 120, 5);
-    Question question(Name("example.net"), RRClass::IN(), RRType::A());
-    OutputBufferPtr buffer(new OutputBuffer(0));
-    query.resolve(question, answer, buffer, &server);
-
-    // Run the test
-    io_service_->run();
-
-    int recv_options = setSocketTimeout(sock_, 1, 0);
-
-    // Try to read 5 times, should stop after 3 reads
-    int num = 0;
-    bool read_success = tryRead(sock_, recv_options, 5, &num);
-
-    // The query should fail
-    EXPECT_FALSE(done);
-    EXPECT_EQ(3, num);
-    EXPECT_FALSE(read_success);
-}
-
-// as mentioned above, we need a more better framework for this,
-// in addition to that, this sends out queries into the world
-// (which we should catch somehow and fake replies for)
-// for the skeleton code, it shouldn't be too much of a problem
-// Ok so even we don't all have access to the DNS world right now,
-// so disabling these tests too.
-TEST_F(ASIOLinkTest, DISABLED_recursiveSendOk) {
-    setDNSService(true, false);
-    bool done;
-    
-    MockServerStop server(*io_service_, &done);
-    vector<pair<string, uint16_t> > empty_vector;
-    RecursiveQuery rq(*dns_service_, empty_vector, empty_vector, 10000, 0);
-
-    Question q(Name("www.isc.org"), RRClass::IN(), RRType::A());
-    OutputBufferPtr buffer(new OutputBuffer(0));
-    MessagePtr answer(new Message(Message::RENDER));
-    rq.resolve(q, answer, buffer, &server);
-    io_service_->run();
-
-    // Check that the answer we got matches the one we wanted
-    EXPECT_EQ(Rcode::NOERROR(), answer->getRcode());
-    ASSERT_EQ(1, answer->getRRCount(Message::SECTION_ANSWER));
-    RRsetPtr a = *answer->beginSection(Message::SECTION_ANSWER);
-    EXPECT_EQ(q.getName(), a->getName());
-    EXPECT_EQ(q.getType(), a->getType());
-    EXPECT_EQ(q.getClass(), a->getClass());
-    EXPECT_EQ(1, a->getRdataCount());
-}
-
-// see comments at previous test
-TEST_F(ASIOLinkTest, DISABLED_recursiveSendNXDOMAIN) {
-    setDNSService(true, false);
-    bool done;
-    
-    MockServerStop server(*io_service_, &done);
-    vector<pair<string, uint16_t> > empty_vector;
-    RecursiveQuery rq(*dns_service_, empty_vector, empty_vector, 10000, 0);
-
-    Question q(Name("wwwdoesnotexist.isc.org"), RRClass::IN(), RRType::A());
-    OutputBufferPtr buffer(new OutputBuffer(0));
-    MessagePtr answer(new Message(Message::RENDER));
-    rq.resolve(q, answer, buffer, &server);
-    io_service_->run();
-
-    // Check that the answer we got matches the one we wanted
-    EXPECT_EQ(Rcode::NXDOMAIN(), answer->getRcode());
-    EXPECT_EQ(0, answer->getRRCount(Message::SECTION_ANSWER));
-}
-
-
-
-// This fixture is for testing IntervalTimer. Some callback functors are 
-// registered as callback function of the timer to test if they are called
-// or not.
-class IntervalTimerTest : public ::testing::Test {
-protected:
-    IntervalTimerTest() : io_service_() {}
-    ~IntervalTimerTest() {}
-    class TimerCallBack : public std::unary_function<void, void> {
-    public:
-        TimerCallBack(IntervalTimerTest* test_obj) : test_obj_(test_obj) {}
-        void operator()() const {
-            test_obj_->timer_called_ = true;
-            test_obj_->io_service_.stop();
-            return;
-        }
-    private:
-        IntervalTimerTest* test_obj_;
-    };
-    class TimerCallBackCounter : public std::unary_function<void, void> {
-    public:
-        TimerCallBackCounter(IntervalTimerTest* test_obj) : test_obj_(test_obj) {
-            counter_ = 0;
-        }
-        void operator()() {
-            ++counter_;
-            return;
-        }
-        int counter_;
-    private:
-        IntervalTimerTest* test_obj_;
-    };
-    class TimerCallBackCancelDeleter : public std::unary_function<void, void> {
-    public:
-        TimerCallBackCancelDeleter(IntervalTimerTest* test_obj,
-                                   IntervalTimer* timer,
-                                   TimerCallBackCounter& counter)
-            : test_obj_(test_obj), timer_(timer), counter_(counter), count_(0)
-        {}
-        void operator()() {
-            ++count_;
-            if (count_ == 1) {
-                // First time of call back.
-                // Store the value of counter_.counter_.
-                prev_counter_ = counter_.counter_;
-                delete timer_;
-            } else if (count_ == 2) {
-                // Second time of call back.
-                // Stop io_service to stop all timers.
-                test_obj_->io_service_.stop();
-                // Compare the value of counter_.counter_ with stored one.
-                // If TimerCallBackCounter was not called (expected behavior),
-                // they are same.
-                if (counter_.counter_ == prev_counter_) {
-                    test_obj_->timer_cancel_success_ = true;
-                }
-            }
-            return;
-        }
-    private:
-        IntervalTimerTest* test_obj_;
-        IntervalTimer* timer_;
-        TimerCallBackCounter& counter_;
-        int count_;
-        int prev_counter_;
-    };
-    class TimerCallBackCanceller {
-    public:
-        TimerCallBackCanceller(unsigned int& counter, IntervalTimer& itimer) :
-            counter_(counter), itimer_(itimer)
-        {}
-        void operator()() {
-            ++counter_;
-            itimer_.cancel();
-        }
-    private:
-        unsigned int& counter_;
-        IntervalTimer& itimer_;
-    };
-    class TimerCallBackOverwriter : public std::unary_function<void, void> {
-    public:
-        TimerCallBackOverwriter(IntervalTimerTest* test_obj,
-                                IntervalTimer& timer)
-            : test_obj_(test_obj), timer_(timer), count_(0)
-        {}
-        void operator()() {
-            ++count_;
-            if (count_ == 1) {
-                // First time of call back.
-                // Call setup() to update callback function to TimerCallBack.
-                test_obj_->timer_called_ = false;
-                timer_.setup(TimerCallBack(test_obj_), 100);
-            } else if (count_ == 2) {
-                // Second time of call back.
-                // If it reaches here, re-setup() is failed (unexpected).
-                // We should stop here.
-                test_obj_->io_service_.stop();
-            }
-            return;
-        }
-    private:
-        IntervalTimerTest* test_obj_;
-        IntervalTimer& timer_;
-        int count_;
-    };
-protected:
-    IOService io_service_;
-    bool timer_called_;
-    bool timer_cancel_success_;
-};
-
-TEST_F(IntervalTimerTest, invalidArgumentToIntervalTimer) {
-    // Create asio_link::IntervalTimer and setup.
-    IntervalTimer itimer(io_service_);
-    // expect throw if call back function is empty
-    EXPECT_THROW(itimer.setup(IntervalTimer::Callback(), 1),
-                 isc::InvalidParameter);
-    // expect throw if interval is not greater than 0
-    EXPECT_THROW(itimer.setup(TimerCallBack(this), 0), isc::BadValue);
-    EXPECT_THROW(itimer.setup(TimerCallBack(this), -1), isc::BadValue);
-}
-
-TEST_F(IntervalTimerTest, startIntervalTimer) {
-    // Create asio_link::IntervalTimer and setup.
-    // Then run IOService and test if the callback function is called.
-    IntervalTimer itimer(io_service_);
-    timer_called_ = false;
-    // store start time
-    boost::posix_time::ptime start;
-    start = boost::posix_time::microsec_clock::universal_time();
-    // setup timer
-    itimer.setup(TimerCallBack(this), 100);
-    EXPECT_EQ(100, itimer.getInterval());
-    io_service_.run();
-    // reaches here after timer expired
-    // delta: difference between elapsed time and 100 milliseconds.
-    boost::posix_time::time_duration delta =
-        (boost::posix_time::microsec_clock::universal_time() - start)
-         - boost::posix_time::millisec(100);
-    if (delta.is_negative()) {
-        delta.invert_sign();
-    }
-    // expect TimerCallBack is called; timer_called_ is true
-    EXPECT_TRUE(timer_called_);
-    // expect interval is 100 milliseconds +/- TIMER_MARGIN_MSEC.
-    EXPECT_TRUE(delta < TIMER_MARGIN_MSEC);
-}
-
-TEST_F(IntervalTimerTest, destructIntervalTimer) {
-    // This code isn't exception safe, but we'd rather keep the code
-    // simpler and more readable as this is only for tests and if it throws
-    // the program would immediately terminate anyway.
-
-    // The call back function will not be called after the timer is
-    // destroyed.
-    //
-    // There are two timers:
-    //  itimer_counter (A)
-    //   (Calls TimerCallBackCounter)
-    //     - increments internal counter in callback function
-    //  itimer_canceller (B)
-    //   (Calls TimerCallBackCancelDeleter)
-    //     - first time of callback, it stores the counter value of
-    //       callback_canceller and destroys itimer_counter
-    //     - second time of callback, it compares the counter value of
-    //       callback_canceller with stored value
-    //       if they are same the timer was not called; expected result
-    //       if they are different the timer was called after destroyed
-    //
-    //     0  100  200  300  400  500  600 (ms)
-    // (A) i--------+----x
-    //                   ^
-    //                   |destroy itimer_counter
-    // (B) i-------------+--------------s
-    //                                  ^stop io_service
-    //                                   and check if itimer_counter have been
-    //                                   stopped
-
-    // itimer_counter will be deleted in TimerCallBackCancelDeleter
-    IntervalTimer* itimer_counter = new IntervalTimer(io_service_);
-    IntervalTimer itimer_canceller(io_service_);
-    timer_cancel_success_ = false;
-    TimerCallBackCounter callback_canceller(this);
-    itimer_counter->setup(callback_canceller, 200);
-    itimer_canceller.setup(
-        TimerCallBackCancelDeleter(this, itimer_counter, callback_canceller),
-        300);
-    io_service_.run();
-    EXPECT_TRUE(timer_cancel_success_);
-}
-
-TEST_F(IntervalTimerTest, cancel) {
-    // Similar to destructIntervalTimer test, but the first timer explicitly
-    // cancels itself on first callback.
-    IntervalTimer itimer_counter(io_service_);
-    IntervalTimer itimer_watcher(io_service_);
-    unsigned int counter = 0;
-    itimer_counter.setup(TimerCallBackCanceller(counter, itimer_counter), 100);
-    itimer_watcher.setup(TimerCallBack(this), 200);
-    io_service_.run();
-    EXPECT_EQ(1, counter);
-    EXPECT_EQ(0, itimer_counter.getInterval());
-
-    // canceling an already canceled timer shouldn't cause any surprise.
-    EXPECT_NO_THROW(itimer_counter.cancel());
-}
-
-TEST_F(IntervalTimerTest, overwriteIntervalTimer) {
-    // Calling setup() multiple times updates call back function and interval.
-    //
-    // There are two timers:
-    //  itimer (A)
-    //   (Calls TimerCallBackCounter / TimerCallBack)
-    //     - increments internal counter in callback function
-    //       (TimerCallBackCounter)
-    //       interval: 300 milliseconds
-    //     - io_service_.stop() (TimerCallBack)
-    //       interval: 100 milliseconds
-    //  itimer_overwriter (B)
-    //   (Calls TimerCallBackOverwriter)
-    //     - first time of callback, it calls setup() to change call back
-    //       function to TimerCallBack and interval of itimer to 100
-    //       milliseconds
-    //       after 300 + 100 milliseconds from the beginning of this test,
-    //       TimerCallBack() will be called and io_service_ stops.
-    //     - second time of callback, it means the test fails.
-    //
-    //     0  100  200  300  400  500  600  700  800 (ms)
-    // (A) i-------------+----C----s
-    //                        ^    ^stop io_service
-    //                        |change call back function
-    // (B) i------------------+-------------------S
-    //                                            ^(stop io_service on fail)
-    //
-
-    IntervalTimer itimer(io_service_);
-    IntervalTimer itimer_overwriter(io_service_);
-    // store start time
-    boost::posix_time::ptime start;
-    start = boost::posix_time::microsec_clock::universal_time();
-    itimer.setup(TimerCallBackCounter(this), 300);
-    itimer_overwriter.setup(TimerCallBackOverwriter(this, itimer), 400);
-    io_service_.run();
-    // reaches here after timer expired
-    // if interval is updated, it takes
-    //   400 milliseconds for TimerCallBackOverwriter
-    //   + 100 milliseconds for TimerCallBack (stop)
-    //   = 500 milliseconds.
-    // otherwise (test fails), it takes
-    //   400 milliseconds for TimerCallBackOverwriter
-    //   + 400 milliseconds for TimerCallBackOverwriter (stop)
-    //   = 800 milliseconds.
-    // delta: difference between elapsed time and 400 + 100 milliseconds
-    boost::posix_time::time_duration delta =
-        (boost::posix_time::microsec_clock::universal_time() - start)
-         - boost::posix_time::millisec(400 + 100);
-    if (delta.is_negative()) {
-        delta.invert_sign();
-    }
-    // expect callback function is updated: TimerCallBack is called
-    EXPECT_TRUE(timer_called_);
-    // expect interval is updated
-    EXPECT_TRUE(delta < TIMER_MARGIN_MSEC);
-}
-
-}
diff --git a/src/lib/asiolink/tests/interval_timer_unittest.cc b/src/lib/asiolink/tests/interval_timer_unittest.cc
new file mode 100644
index 0000000..9a7b0f4
--- /dev/null
+++ b/src/lib/asiolink/tests/interval_timer_unittest.cc
@@ -0,0 +1,292 @@
+// 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 <gtest/gtest.h>
+
+#include <asiolink/asiolink.h>
+
+#include <boost/date_time/posix_time/posix_time_types.hpp>
+
+namespace {
+// TODO: Consider this margin
+const boost::posix_time::time_duration TIMER_MARGIN_MSEC =
+    boost::posix_time::milliseconds(50);
+}
+
+using namespace asiolink;
+
+// This fixture is for testing IntervalTimer. Some callback functors are 
+// registered as callback function of the timer to test if they are called
+// or not.
+class IntervalTimerTest : public ::testing::Test {
+protected:
+    IntervalTimerTest() : io_service_() {}
+    ~IntervalTimerTest() {}
+    class TimerCallBack : public std::unary_function<void, void> {
+    public:
+        TimerCallBack(IntervalTimerTest* test_obj) : test_obj_(test_obj) {}
+        void operator()() const {
+            test_obj_->timer_called_ = true;
+            test_obj_->io_service_.stop();
+            return;
+        }
+    private:
+        IntervalTimerTest* test_obj_;
+    };
+    class TimerCallBackCounter : public std::unary_function<void, void> {
+    public:
+        TimerCallBackCounter(IntervalTimerTest* test_obj) : test_obj_(test_obj) {
+            counter_ = 0;
+        }
+        void operator()() {
+            ++counter_;
+            return;
+        }
+        int counter_;
+    private:
+        IntervalTimerTest* test_obj_;
+    };
+    class TimerCallBackCancelDeleter : public std::unary_function<void, void> {
+    public:
+        TimerCallBackCancelDeleter(IntervalTimerTest* test_obj,
+                                   IntervalTimer* timer,
+                                   TimerCallBackCounter& counter)
+            : test_obj_(test_obj), timer_(timer), counter_(counter), count_(0)
+        {}
+        void operator()() {
+            ++count_;
+            if (count_ == 1) {
+                // First time of call back.
+                // Store the value of counter_.counter_.
+                prev_counter_ = counter_.counter_;
+                delete timer_;
+            } else if (count_ == 2) {
+                // Second time of call back.
+                // Stop io_service to stop all timers.
+                test_obj_->io_service_.stop();
+                // Compare the value of counter_.counter_ with stored one.
+                // If TimerCallBackCounter was not called (expected behavior),
+                // they are same.
+                if (counter_.counter_ == prev_counter_) {
+                    test_obj_->timer_cancel_success_ = true;
+                }
+            }
+            return;
+        }
+    private:
+        IntervalTimerTest* test_obj_;
+        IntervalTimer* timer_;
+        TimerCallBackCounter& counter_;
+        int count_;
+        int prev_counter_;
+    };
+    class TimerCallBackCanceller {
+    public:
+        TimerCallBackCanceller(unsigned int& counter, IntervalTimer& itimer) :
+            counter_(counter), itimer_(itimer)
+        {}
+        void operator()() {
+            ++counter_;
+            itimer_.cancel();
+        }
+    private:
+        unsigned int& counter_;
+        IntervalTimer& itimer_;
+    };
+    class TimerCallBackOverwriter : public std::unary_function<void, void> {
+    public:
+        TimerCallBackOverwriter(IntervalTimerTest* test_obj,
+                                IntervalTimer& timer)
+            : test_obj_(test_obj), timer_(timer), count_(0)
+        {}
+        void operator()() {
+            ++count_;
+            if (count_ == 1) {
+                // First time of call back.
+                // Call setup() to update callback function to TimerCallBack.
+                test_obj_->timer_called_ = false;
+                timer_.setup(TimerCallBack(test_obj_), 100);
+            } else if (count_ == 2) {
+                // Second time of call back.
+                // If it reaches here, re-setup() is failed (unexpected).
+                // We should stop here.
+                test_obj_->io_service_.stop();
+            }
+            return;
+        }
+    private:
+        IntervalTimerTest* test_obj_;
+        IntervalTimer& timer_;
+        int count_;
+    };
+protected:
+    IOService io_service_;
+    bool timer_called_;
+    bool timer_cancel_success_;
+};
+
+TEST_F(IntervalTimerTest, invalidArgumentToIntervalTimer) {
+    // Create asio_link::IntervalTimer and setup.
+    IntervalTimer itimer(io_service_);
+    // expect throw if call back function is empty
+    EXPECT_THROW(itimer.setup(IntervalTimer::Callback(), 1),
+                 isc::InvalidParameter);
+    // expect throw if interval is not greater than 0
+    EXPECT_THROW(itimer.setup(TimerCallBack(this), 0), isc::BadValue);
+    EXPECT_THROW(itimer.setup(TimerCallBack(this), -1), isc::BadValue);
+}
+
+TEST_F(IntervalTimerTest, startIntervalTimer) {
+    // Create asio_link::IntervalTimer and setup.
+    // Then run IOService and test if the callback function is called.
+    IntervalTimer itimer(io_service_);
+    timer_called_ = false;
+    // store start time
+    boost::posix_time::ptime start;
+    start = boost::posix_time::microsec_clock::universal_time();
+    // setup timer
+    itimer.setup(TimerCallBack(this), 100);
+    EXPECT_EQ(100, itimer.getInterval());
+    io_service_.run();
+    // reaches here after timer expired
+    // delta: difference between elapsed time and 100 milliseconds.
+    boost::posix_time::time_duration delta =
+        (boost::posix_time::microsec_clock::universal_time() - start)
+         - boost::posix_time::millisec(100);
+    if (delta.is_negative()) {
+        delta.invert_sign();
+    }
+    // expect TimerCallBack is called; timer_called_ is true
+    EXPECT_TRUE(timer_called_);
+    // expect interval is 100 milliseconds +/- TIMER_MARGIN_MSEC.
+    EXPECT_TRUE(delta < TIMER_MARGIN_MSEC);
+}
+
+TEST_F(IntervalTimerTest, destructIntervalTimer) {
+    // This code isn't exception safe, but we'd rather keep the code
+    // simpler and more readable as this is only for tests and if it throws
+    // the program would immediately terminate anyway.
+
+    // The call back function will not be called after the timer is
+    // destroyed.
+    //
+    // There are two timers:
+    //  itimer_counter (A)
+    //   (Calls TimerCallBackCounter)
+    //     - increments internal counter in callback function
+    //  itimer_canceller (B)
+    //   (Calls TimerCallBackCancelDeleter)
+    //     - first time of callback, it stores the counter value of
+    //       callback_canceller and destroys itimer_counter
+    //     - second time of callback, it compares the counter value of
+    //       callback_canceller with stored value
+    //       if they are same the timer was not called; expected result
+    //       if they are different the timer was called after destroyed
+    //
+    //     0  100  200  300  400  500  600 (ms)
+    // (A) i--------+----x
+    //                   ^
+    //                   |destroy itimer_counter
+    // (B) i-------------+--------------s
+    //                                  ^stop io_service
+    //                                   and check if itimer_counter have been
+    //                                   stopped
+
+    // itimer_counter will be deleted in TimerCallBackCancelDeleter
+    IntervalTimer* itimer_counter = new IntervalTimer(io_service_);
+    IntervalTimer itimer_canceller(io_service_);
+    timer_cancel_success_ = false;
+    TimerCallBackCounter callback_canceller(this);
+    itimer_counter->setup(callback_canceller, 200);
+    itimer_canceller.setup(
+        TimerCallBackCancelDeleter(this, itimer_counter, callback_canceller),
+        300);
+    io_service_.run();
+    EXPECT_TRUE(timer_cancel_success_);
+}
+
+TEST_F(IntervalTimerTest, cancel) {
+    // Similar to destructIntervalTimer test, but the first timer explicitly
+    // cancels itself on first callback.
+    IntervalTimer itimer_counter(io_service_);
+    IntervalTimer itimer_watcher(io_service_);
+    unsigned int counter = 0;
+    itimer_counter.setup(TimerCallBackCanceller(counter, itimer_counter), 100);
+    itimer_watcher.setup(TimerCallBack(this), 200);
+    io_service_.run();
+    EXPECT_EQ(1, counter);
+    EXPECT_EQ(0, itimer_counter.getInterval());
+
+    // canceling an already canceled timer shouldn't cause any surprise.
+    EXPECT_NO_THROW(itimer_counter.cancel());
+}
+
+TEST_F(IntervalTimerTest, overwriteIntervalTimer) {
+    // Calling setup() multiple times updates call back function and interval.
+    //
+    // There are two timers:
+    //  itimer (A)
+    //   (Calls TimerCallBackCounter / TimerCallBack)
+    //     - increments internal counter in callback function
+    //       (TimerCallBackCounter)
+    //       interval: 300 milliseconds
+    //     - io_service_.stop() (TimerCallBack)
+    //       interval: 100 milliseconds
+    //  itimer_overwriter (B)
+    //   (Calls TimerCallBackOverwriter)
+    //     - first time of callback, it calls setup() to change call back
+    //       function to TimerCallBack and interval of itimer to 100
+    //       milliseconds
+    //       after 300 + 100 milliseconds from the beginning of this test,
+    //       TimerCallBack() will be called and io_service_ stops.
+    //     - second time of callback, it means the test fails.
+    //
+    //     0  100  200  300  400  500  600  700  800 (ms)
+    // (A) i-------------+----C----s
+    //                        ^    ^stop io_service
+    //                        |change call back function
+    // (B) i------------------+-------------------S
+    //                                            ^(stop io_service on fail)
+    //
+
+    IntervalTimer itimer(io_service_);
+    IntervalTimer itimer_overwriter(io_service_);
+    // store start time
+    boost::posix_time::ptime start;
+    start = boost::posix_time::microsec_clock::universal_time();
+    itimer.setup(TimerCallBackCounter(this), 300);
+    itimer_overwriter.setup(TimerCallBackOverwriter(this, itimer), 400);
+    io_service_.run();
+    // reaches here after timer expired
+    // if interval is updated, it takes
+    //   400 milliseconds for TimerCallBackOverwriter
+    //   + 100 milliseconds for TimerCallBack (stop)
+    //   = 500 milliseconds.
+    // otherwise (test fails), it takes
+    //   400 milliseconds for TimerCallBackOverwriter
+    //   + 400 milliseconds for TimerCallBackOverwriter (stop)
+    //   = 800 milliseconds.
+    // delta: difference between elapsed time and 400 + 100 milliseconds
+    boost::posix_time::time_duration delta =
+        (boost::posix_time::microsec_clock::universal_time() - start)
+         - boost::posix_time::millisec(400 + 100);
+    if (delta.is_negative()) {
+        delta.invert_sign();
+    }
+    // expect callback function is updated: TimerCallBack is called
+    EXPECT_TRUE(timer_called_);
+    // expect interval is updated
+    EXPECT_TRUE(delta < TIMER_MARGIN_MSEC);
+}
diff --git a/src/lib/asiolink/tests/io_service_unittest.cc b/src/lib/asiolink/tests/io_service_unittest.cc
new file mode 100644
index 0000000..49aa67e
--- /dev/null
+++ b/src/lib/asiolink/tests/io_service_unittest.cc
@@ -0,0 +1,115 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+#include <gtest/gtest.h>
+
+#include <asiolink/asiolink.h>
+
+using namespace asiolink;
+
+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";
+
+TEST(IOServiceTest, badPort) {
+    IOService io_service;
+    EXPECT_THROW(DNSService(io_service, *"65536", true, false, NULL, NULL, NULL), IOError);
+    EXPECT_THROW(DNSService(io_service, *"5300.0", true, false, NULL, NULL, NULL), IOError);
+    EXPECT_THROW(DNSService(io_service, *"-1", true, false, NULL, NULL, NULL), IOError);
+    EXPECT_THROW(DNSService(io_service, *"domain", true, false, NULL, NULL, NULL), IOError);
+}
+
+TEST(IOServiceTest, badAddress) {
+    IOService io_service;
+    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"192.0.2.1.1", NULL, NULL, NULL), IOError);
+    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"2001:db8:::1", NULL, NULL, NULL), IOError);
+    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"localhost", NULL, NULL, NULL), IOError);
+}
+
+TEST(IOServiceTest, unavailableAddress) {
+    IOService io_service;
+    // These addresses should generally be unavailable as a valid local
+    // address, although there's no guarantee in theory.
+    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"192.0.2.0", NULL, NULL, NULL), IOError);
+
+    // Some OSes would simply reject binding attempt for an AF_INET6 socket
+    // to an IPv4-mapped IPv6 address.  Even if those that allow it, since
+    // the corresponding IPv4 address is the same as the one used in the
+    // AF_INET socket case above, it should at least show the same result
+    // as the previous one.
+    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"::ffff:192.0.2.0", NULL, NULL, NULL), IOError);
+}
+
+TEST(IOServiceTest, duplicateBind_v6) {
+    // In each sub test case, second attempt should fail due to duplicate bind
+    IOService io_service;
+
+    // IPv6, "any" address
+    DNSService* dns_service = new DNSService(io_service, *TEST_SERVER_PORT, false, true, NULL, NULL, NULL);
+    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, false, true, NULL, NULL, NULL), IOError);
+    delete dns_service;
+
+}
+
+TEST(IOServiceTest, duplicateBind_v6_address) {
+    // In each sub test case, second attempt should fail due to duplicate bind
+    IOService io_service;
+
+    // IPv6, specific address
+    DNSService* dns_service = new DNSService(io_service, *TEST_SERVER_PORT, *TEST_IPV6_ADDR, NULL, NULL, NULL);
+    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *TEST_IPV6_ADDR, NULL, NULL, NULL), IOError);
+    delete dns_service;
+
+}
+
+TEST(IOServiceTest, duplicateBind_v4) {
+    // In each sub test case, second attempt should fail due to duplicate bind
+    IOService io_service;
+
+    // IPv4, "any" address
+    DNSService* dns_service = new DNSService(io_service, *TEST_SERVER_PORT, true, false, NULL, NULL, NULL);
+    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, true, false, NULL, NULL, NULL), IOError);
+    delete dns_service;
+
+}
+
+TEST(IOServiceTest, duplicateBind_v4_address) {
+    // In each sub test case, second attempt should fail due to duplicate bind
+    IOService io_service;
+
+    // IPv4, specific address
+    DNSService* dns_service = new DNSService(io_service, *TEST_SERVER_PORT, *TEST_IPV4_ADDR, NULL, NULL, NULL);
+    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *TEST_IPV4_ADDR, NULL, NULL, NULL), IOError);
+    delete dns_service;
+}
+
+// Disabled because IPv4-mapped addresses don't seem to be working with
+// the IOService constructor
+TEST(IOServiceTest, DISABLED_IPv4MappedDuplicateBind) {
+    IOService io_service;
+    // Duplicate bind on IPv4-mapped IPv6 address
+    DNSService* dns_service = new DNSService(io_service, *TEST_SERVER_PORT, *"127.0.0.1", NULL, NULL, NULL);
+    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"::ffff:127.0.0.1", NULL, NULL, NULL), IOError);
+    delete dns_service;
+
+    // XXX:
+    // Currently, this throws an "invalid argument" exception.  I have
+    // not been able to get IPv4-mapped addresses to work.
+    dns_service = new DNSService(io_service, *TEST_SERVER_PORT, *"::ffff:127.0.0.1", NULL, NULL, NULL);
+    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"127.0.0.1", NULL, NULL, NULL), IOError);
+    delete dns_service;
+}
+
diff --git a/src/lib/asiolink/tests/ioaddress_unittest.cc b/src/lib/asiolink/tests/ioaddress_unittest.cc
new file mode 100644
index 0000000..57c9496
--- /dev/null
+++ b/src/lib/asiolink/tests/ioaddress_unittest.cc
@@ -0,0 +1,57 @@
+// 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 <gtest/gtest.h>
+
+#include <asiolink/asiolink.h>
+
+using namespace asiolink;
+
+TEST(IOAddressTest, fromText) {
+    IOAddress io_address_v4("192.0.2.1");
+    EXPECT_EQ("192.0.2.1", io_address_v4.toText());
+
+    IOAddress io_address_v6("2001:db8::1234");
+    EXPECT_EQ("2001:db8::1234", io_address_v6.toText());
+
+    // bogus IPv4 address-like input
+    EXPECT_THROW(IOAddress("192.0.2.2.1"), IOError);
+
+    // bogus IPv4 address-like input: out-of-range octet
+    EXPECT_THROW(IOAddress("192.0.2.300"), IOError);
+
+    // bogus IPv6 address-like input
+    EXPECT_THROW(IOAddress("2001:db8:::1234"), IOError);
+
+    // bogus IPv6 address-like input
+    EXPECT_THROW(IOAddress("2001:db8::efgh"), IOError);
+}
+
+TEST(IOAddressTest, Equality) {
+    EXPECT_TRUE(IOAddress("192.0.2.1") == IOAddress("192.0.2.1"));
+    EXPECT_FALSE(IOAddress("192.0.2.1") != IOAddress("192.0.2.1"));
+
+    EXPECT_TRUE(IOAddress("192.0.2.1") != IOAddress("192.0.2.2"));
+    EXPECT_FALSE(IOAddress("192.0.2.1") == IOAddress("192.0.2.2"));
+
+    EXPECT_TRUE(IOAddress("2001:db8::12") == IOAddress("2001:0DB8:0:0::0012"));
+    EXPECT_FALSE(IOAddress("2001:db8::12") != IOAddress("2001:0DB8:0:0::0012"));
+
+    EXPECT_TRUE(IOAddress("2001:db8::1234") != IOAddress("2001:db8::1235"));
+    EXPECT_FALSE(IOAddress("2001:db8::1234") == IOAddress("2001:db8::1235"));
+
+    EXPECT_TRUE(IOAddress("2001:db8::1234") != IOAddress("192.0.2.3"));
+    EXPECT_FALSE(IOAddress("2001:db8::1234") == IOAddress("192.0.2.3"));
+}
diff --git a/src/lib/asiolink/tests/ioendpoint_unittest.cc b/src/lib/asiolink/tests/ioendpoint_unittest.cc
new file mode 100644
index 0000000..a5b5cd2
--- /dev/null
+++ b/src/lib/asiolink/tests/ioendpoint_unittest.cc
@@ -0,0 +1,67 @@
+// 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 <gtest/gtest.h>
+
+#include <asiolink/asiolink.h>
+
+using namespace asiolink;
+
+TEST(IOEndpointTest, createUDPv4) {
+    const IOEndpoint* ep;
+    ep = IOEndpoint::create(IPPROTO_UDP, IOAddress("192.0.2.1"), 5300);
+    EXPECT_EQ("192.0.2.1", ep->getAddress().toText());
+    EXPECT_EQ(5300, ep->getPort());
+    EXPECT_EQ(AF_INET, ep->getFamily());
+    EXPECT_EQ(AF_INET, ep->getAddress().getFamily());
+    EXPECT_EQ(IPPROTO_UDP, ep->getProtocol());
+}
+
+TEST(IOEndpointTest, createTCPv4) {
+    const IOEndpoint* ep;
+    ep = IOEndpoint::create(IPPROTO_TCP, IOAddress("192.0.2.1"), 5301);
+    EXPECT_EQ("192.0.2.1", ep->getAddress().toText());
+    EXPECT_EQ(5301, ep->getPort());
+    EXPECT_EQ(AF_INET, ep->getFamily());
+    EXPECT_EQ(AF_INET, ep->getAddress().getFamily());
+    EXPECT_EQ(IPPROTO_TCP, ep->getProtocol());
+}
+
+TEST(IOEndpointTest, createUDPv6) {
+    const IOEndpoint* ep;
+    ep = IOEndpoint::create(IPPROTO_UDP, IOAddress("2001:db8::1234"), 5302);
+    EXPECT_EQ("2001:db8::1234", ep->getAddress().toText());
+    EXPECT_EQ(5302, ep->getPort());
+    EXPECT_EQ(AF_INET6, ep->getFamily());
+    EXPECT_EQ(AF_INET6, ep->getAddress().getFamily());
+    EXPECT_EQ(IPPROTO_UDP, ep->getProtocol());
+}
+
+TEST(IOEndpointTest, createTCPv6) {
+    const IOEndpoint* ep;
+    ep = IOEndpoint::create(IPPROTO_TCP, IOAddress("2001:db8::1234"), 5303);
+    EXPECT_EQ("2001:db8::1234", ep->getAddress().toText());
+    EXPECT_EQ(5303, ep->getPort());
+    EXPECT_EQ(AF_INET6, ep->getFamily());
+    EXPECT_EQ(AF_INET6, ep->getAddress().getFamily());
+    EXPECT_EQ(IPPROTO_TCP, ep->getProtocol());
+}
+
+TEST(IOEndpointTest, createIPProto) {
+    EXPECT_THROW(IOEndpoint::create(IPPROTO_IP, IOAddress("192.0.2.1"),
+                                    5300)->getAddress().toText(),
+                 IOError);
+}
+
diff --git a/src/lib/asiolink/tests/iosocket_unittest.cc b/src/lib/asiolink/tests/iosocket_unittest.cc
new file mode 100644
index 0000000..06ae861
--- /dev/null
+++ b/src/lib/asiolink/tests/iosocket_unittest.cc
@@ -0,0 +1,29 @@
+// 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 <gtest/gtest.h>
+
+#include <asiolink/asiolink.h>
+
+using namespace asiolink;
+
+TEST(IOSocketTest, dummySockets) {
+    EXPECT_EQ(IPPROTO_UDP, IOSocket::getDummyUDPSocket().getProtocol());
+    EXPECT_EQ(IPPROTO_TCP, IOSocket::getDummyTCPSocket().getProtocol());
+    EXPECT_EQ(-1, IOSocket::getDummyUDPSocket().getNative());
+    EXPECT_EQ(-1, IOSocket::getDummyTCPSocket().getNative());
+}
+
+
diff --git a/src/lib/asiolink/tests/recursive_query_unittest.cc b/src/lib/asiolink/tests/recursive_query_unittest.cc
new file mode 100644
index 0000000..ad4e5b4
--- /dev/null
+++ b/src/lib/asiolink/tests/recursive_query_unittest.cc
@@ -0,0 +1,791 @@
+// 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/asiolink.h>
+#include <asiolink/io_socket.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),
+            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), 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, DISABLED_clearServers) {
+    // FIXME: Enable when clearServers actually close the sockets
+    //    See #388
+    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),
+                         50, 120, 1000, 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),
+                         50, 4000, 120, 5);
+    Question question(Name("example.net"), RRClass::IN(), RRType::A());
+    OutputBufferPtr buffer(new OutputBuffer(0));
+    query.resolve(question, answer, buffer, &server);
+
+    // Run the test
+    io_service_->run();
+
+    int recv_options = setSocketTimeout(sock_, 1, 0);
+
+    // Try to read 5 times, should stop after 3 reads
+    int num = 0;
+    bool read_success = tryRead(sock_, recv_options, 5, &num);
+
+    // The query should fail
+    EXPECT_FALSE(done);
+    EXPECT_EQ(3, num);
+    EXPECT_FALSE(read_success);
+}
+
+// as mentioned above, we need a more better framework for this,
+// in addition to that, this sends out queries into the world
+// (which we should catch somehow and fake replies for)
+// for the skeleton code, it shouldn't be too much of a problem
+// Ok so even we don't all have access to the DNS world right now,
+// so disabling these tests too.
+TEST_F(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/udp_query_unittest.cc b/src/lib/asiolink/tests/udp_query_unittest.cc
new file mode 100644
index 0000000..9eb1aba
--- /dev/null
+++ b/src/lib/asiolink/tests/udp_query_unittest.cc
@@ -0,0 +1,145 @@
+// 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 <gtest/gtest.h>
+#include <asio.hpp>
+#include <boost/bind.hpp>
+#include <cstdlib>
+
+#include <dns/question.h>
+
+#include <asiolink/udp_query.h>
+
+using namespace asio;
+using namespace isc::dns;
+using asio::ip::udp;
+
+namespace {
+
+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";
+
+// Test fixture for the asiolink::UDPQuery.
+class UDPQueryTest : public ::testing::Test,
+    public asiolink::UDPQuery::Callback
+{
+    public:
+        // Expected result of the callback
+        asiolink::UDPQuery::Result expected_;
+        // Did the callback run already?
+        bool run_;
+        // We use an io_service to run the query
+        io_service service_;
+        // Something to ask
+        Question question_;
+        // Buffer where the UDPQuery will store response
+        OutputBufferPtr buffer_;
+        // The query we are testing
+        asiolink::UDPQuery query_;
+
+        UDPQueryTest() :
+            run_(false),
+            question_(Name("example.net"), RRClass::IN(), RRType::A()),
+            buffer_(new OutputBuffer(512)),
+            query_(service_, question_, asiolink::IOAddress(TEST_HOST),
+                TEST_PORT, buffer_, this, 100)
+        { }
+
+        // This is the callback's (), so it can be called.
+        void operator()(asiolink::UDPQuery::Result result) {
+            // We check the query returns the correct result
+            EXPECT_EQ(expected_, result);
+            // Check it is called only once
+            EXPECT_FALSE(run_);
+            // And mark the callback was called
+            run_ = true;
+        }
+        // A response handler, pretending to be remote DNS server
+        void respond(udp::endpoint* remote, udp::socket* socket) {
+            // Some data came, just send something back.
+            socket->send_to(asio::buffer(TEST_DATA, sizeof TEST_DATA),
+                *remote);
+            socket->close();
+        }
+};
+
+/*
+ * 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(UDPQueryTest, stop) {
+    expected_ = asiolink::UDPQuery::STOPPED;
+    // Post the query
+    service_.post(query_);
+    // Post query_.stop() (yes, the boost::bind thing is just
+    // query_.stop()).
+    service_.post(boost::bind(&asiolink::UDPQuery::stop, query_,
+        asiolink::UDPQuery::STOPPED));
+    // Run both of them
+    service_.run();
+    EXPECT_TRUE(run_);
+}
+
+/*
+ * 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).
+ */
+TEST_F(UDPQueryTest, prematureStop) {
+    expected_ = asiolink::UDPQuery::STOPPED;
+    // Stop before it is started
+    query_.stop();
+    service_.post(query_);
+    service_.run();
+    EXPECT_TRUE(run_);
+}
+
+/*
+ * Test that it will timeout when no answer will arrive.
+ */
+TEST_F(UDPQueryTest, timeout) {
+    expected_ = asiolink::UDPQuery::TIME_OUT;
+    service_.post(query_);
+    service_.run();
+    EXPECT_TRUE(run_);
+}
+
+/*
+ * 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 loopback address.
+ */
+TEST_F(UDPQueryTest, receive) {
+    expected_ = asiolink::UDPQuery::SUCCESS;
+    udp::socket socket(service_, udp::v4());
+    socket.set_option(socket_base::reuse_address(true));
+    socket.bind(udp::endpoint(TEST_HOST, TEST_PORT));
+    char inbuff[512];
+    udp::endpoint remote;
+    socket.async_receive_from(asio::buffer(inbuff, 512), remote, boost::bind(
+        &UDPQueryTest::respond, this, &remote, &socket));
+    service_.post(query_);
+    service_.run();
+    EXPECT_TRUE(run_);
+    ASSERT_EQ(sizeof TEST_DATA, buffer_->getLength());
+    EXPECT_EQ(0, memcmp(TEST_DATA, buffer_->getData(), sizeof TEST_DATA));
+}
+
+}
diff --git a/src/lib/asiolink/udp_endpoint.h b/src/lib/asiolink/udp_endpoint.h
new file mode 100644
index 0000000..27541e0
--- /dev/null
+++ b/src/lib/asiolink/udp_endpoint.h
@@ -0,0 +1,89 @@
+// 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 __UDP_ENDPOINT_H
+#define __UDP_ENDPOINT_H 1
+
+#ifndef ASIO_HPP
+#error "asio.hpp must be included before including this, see asiolink.h as to why"
+#endif
+
+#include <asiolink/io_endpoint.h>
+
+namespace asiolink {
+
+/// \brief The \c UDPEndpoint class is a concrete derived class of
+/// \c IOEndpoint that represents an endpoint of a UDP packet.
+///
+/// Other notes about \c TCPEndpoint applies to this class, too.
+class UDPEndpoint : public IOEndpoint {
+public:
+    ///
+    /// \name Constructors and Destructor.
+    ///
+    //@{
+    /// \brief Constructor from a pair of address and port.
+    ///
+    /// \param address The IP address of the endpoint.
+    /// \param port The UDP port number of the endpoint.
+    UDPEndpoint(const IOAddress& address, const unsigned short port) :
+        asio_endpoint_placeholder_(
+            new asio::ip::udp::endpoint(asio::ip::address::from_string(address.toText()),
+                              port)),
+        asio_endpoint_(*asio_endpoint_placeholder_)
+    {}
+
+    /// \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 UDP endpoint.
+    UDPEndpoint(const asio::ip::udp::endpoint& asio_endpoint) :
+        asio_endpoint_placeholder_(NULL), asio_endpoint_(asio_endpoint)
+    {}
+
+    /// \brief The destructor.
+    ~UDPEndpoint() { delete asio_endpoint_placeholder_; }
+    //@}
+
+    inline IOAddress getAddress() const {
+        return (asio_endpoint_.address());
+    }
+
+    inline uint16_t getPort() const {
+        return (asio_endpoint_.port());
+    }
+
+    inline short getProtocol() const {
+        return (asio_endpoint_.protocol().protocol());
+    }
+
+    inline 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
+    inline const asio::ip::udp::endpoint& getASIOEndpoint() const {
+        return (asio_endpoint_);
+    }
+
+private:
+    const asio::ip::udp::endpoint* asio_endpoint_placeholder_;
+    const asio::ip::udp::endpoint& asio_endpoint_;
+};
+
+}      // namespace asiolink
+#endif // __UDP_ENDPOINT_H
diff --git a/src/lib/asiolink/udp_query.cc b/src/lib/asiolink/udp_query.cc
new file mode 100644
index 0000000..a793814
--- /dev/null
+++ b/src/lib/asiolink/udp_query.cc
@@ -0,0 +1,189 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+
+// unistd is needed for asio.hpp with SunStudio
+#include <unistd.h>
+
+#include <asio.hpp>
+
+#include <boost/bind.hpp>
+#include <boost/shared_array.hpp>
+
+#include <dns/messagerenderer.h>
+#include <dns/opcode.h>
+#include <dns/rcode.h>
+
+#include <log/dummylog.h>
+
+#include <asio.hpp>
+
+#include <asiolink.h>
+
+#include <coroutine.h>
+#include <asiolink/udp_endpoint.h>
+
+#include <asiolink/udp_query.h>
+
+using namespace asio;
+using asio::ip::udp;
+using asio::ip::tcp;
+using isc::log::dlog;
+
+using namespace std;
+using namespace isc::dns;
+
+namespace asiolink {
+
+
+// Private UDPQuery data (see internal/udpdns.h for reasons)
+struct UDPQuery::PrivateData {
+    // Socket we send query to and expect reply from there
+    udp::socket socket;
+    // Where was the query sent
+    udp::endpoint remote;
+    // What we ask the server
+    Question question;
+    // We will store the answer here
+    OutputBufferPtr buffer;
+    OutputBufferPtr msgbuf;
+    // Temporary buffer for answer
+    boost::shared_array<char> data;
+    // This will be called when the data arrive or timeouts
+    Callback* callback;
+    // Did we already stop operating (data arrived, we timed out, someone
+    // called stop). This can be so when we are cleaning up/there are
+    // still pointers to us.
+    bool stopped;
+    // Timer to measure timeouts.
+    deadline_timer timer;
+    // How many milliseconds are we willing to wait for answer?
+    int timeout;
+
+    PrivateData(io_service& service,
+        const udp::socket::protocol_type& protocol, const Question &q,
+        OutputBufferPtr b, Callback *c) :
+        socket(service, protocol),
+        question(q),
+        buffer(b),
+        msgbuf(new OutputBuffer(512)),
+        callback(c),
+        stopped(false),
+        timer(service)
+    { }
+};
+
+/// The following functions implement the \c UDPQuery class.
+///
+/// The constructor
+UDPQuery::UDPQuery(io_service& io_service,
+                   const Question& q, const IOAddress& addr, uint16_t port,
+                   OutputBufferPtr buffer, Callback *callback, int timeout) :
+    data_(new PrivateData(io_service,
+        addr.getFamily() == AF_INET ? udp::v4() : udp::v6(), q, buffer,
+        callback))
+{
+    data_->remote = UDPEndpoint(addr, port).getASIOEndpoint();
+    data_->timeout = timeout;
+}
+
+/// The function operator is implemented with the "stackless coroutine"
+/// pattern; see internal/coroutine.h for details.
+void
+UDPQuery::operator()(error_code ec, size_t length) {
+    if (ec || data_->stopped) {
+        return;
+    }
+
+    CORO_REENTER (this) {
+        /// Generate the upstream query and render it to wire format
+        /// This is done in a different scope to allow inline variable
+        /// declarations.
+        {
+            Message msg(Message::RENDER);
+            
+            // XXX: replace with boost::random or some other suitable PRNG
+            msg.setQid(0);
+            msg.setOpcode(Opcode::QUERY());
+            msg.setRcode(Rcode::NOERROR());
+            msg.setHeaderFlag(Message::HEADERFLAG_RD);
+            msg.addQuestion(data_->question);
+            MessageRenderer renderer(*data_->msgbuf);
+            msg.toWire(renderer);
+            dlog("Sending " + msg.toText() + " to " +
+                data_->remote.address().to_string());
+        }
+
+
+        // If we timeout, we stop, which will shutdown everything and
+        // cancel all other attempts to run inside the coroutine
+        if (data_->timeout != -1) {
+            data_->timer.expires_from_now(boost::posix_time::milliseconds(
+                data_->timeout));
+            data_->timer.async_wait(boost::bind(&UDPQuery::stop, *this,
+                TIME_OUT));
+        }
+
+        // Begin an asynchronous send, and then yield.  When the
+        // send completes, we will resume immediately after this point.
+        CORO_YIELD data_->socket.async_send_to(buffer(data_->msgbuf->getData(),
+            data_->msgbuf->getLength()), data_->remote, *this);
+
+        /// Allocate space for the response.  (XXX: This should be
+        /// optimized by maintaining a free list of pre-allocated blocks)
+        data_->data.reset(new char[MAX_LENGTH]);
+
+        /// Begin an asynchronous receive, and yield.  When the receive
+        /// completes, we will resume immediately after this point.
+        CORO_YIELD data_->socket.async_receive_from(buffer(data_->data.get(),
+            MAX_LENGTH), data_->remote, *this);
+        // The message is not rendered yet, so we can't print it easilly
+        dlog("Received response from " + data_->remote.address().to_string());
+
+        /// Copy the answer into the response buffer.  (XXX: If the
+        /// OutputBuffer object were made to meet the requirements of
+        /// a MutableBufferSequence, then it could be written to directly
+        /// by async_recieve_from() and this additional copy step would
+        /// be unnecessary.)
+        data_->buffer->writeData(data_->data.get(), length);
+
+        /// We are done
+        stop(SUCCESS);
+    }
+}
+
+void
+UDPQuery::stop(Result result) {
+    if (!data_->stopped) {
+        switch (result) {
+            case TIME_OUT:
+                dlog("Query timed out");
+                break;
+            case STOPPED:
+                dlog("Query stopped");
+                break;
+            default:;
+        }
+        data_->stopped = true;
+        data_->socket.cancel();
+        data_->socket.close();
+        data_->timer.cancel();
+        if (data_->callback) {
+            (*data_->callback)(result);
+        }
+    }
+}
+
+} // namespace asiolink
diff --git a/src/lib/asiolink/udp_query.h b/src/lib/asiolink/udp_query.h
new file mode 100644
index 0000000..3ed44ad
--- /dev/null
+++ b/src/lib/asiolink/udp_query.h
@@ -0,0 +1,88 @@
+// 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 __UDP_QUERY_H
+#define __UDP_QUERY_H 1
+
+#ifndef ASIO_HPP
+#error "asio.hpp must be included before including this, see asiolink.h as to why"
+#endif
+
+#include <dns/buffer.h>
+
+#include <asiolink/io_address.h>
+#include <coroutine.h>
+
+namespace asiolink {
+
+//
+// Asynchronous UDP coroutine for upstream queries
+//
+class UDPQuery : public coroutine {
+public:
+    // TODO Maybe this should be more generic than just for UDPQuery?
+    ///
+    /// \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 UDPQuery.
+    class Callback {
+    public:
+        virtual ~Callback() {}
+
+        /// This will be called when the UDPQuery 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.
+    ///
+    explicit UDPQuery(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);
+    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 PrivateData;
+    boost::shared_ptr<PrivateData> data_;
+};
+
+}      // namespace asiolink
+#endif // __UDP_QUERY_H
diff --git a/src/lib/asiolink/udp_server.cc b/src/lib/asiolink/udp_server.cc
new file mode 100644
index 0000000..9a18d76
--- /dev/null
+++ b/src/lib/asiolink/udp_server.cc
@@ -0,0 +1,284 @@
+// 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 <boost/shared_array.hpp>
+
+// unistd is needed for asio.hpp with SunStudio
+#include <unistd.h>
+
+#include <asio.hpp>
+
+#include <log/dummylog.h>
+
+#include <asiolink/udp_endpoint.h>
+#include <asiolink/udp_socket.h>
+
+#include <asiolink/udp_server.h>
+
+using namespace asio;
+using asio::ip::udp;
+using asio::ip::tcp;
+using isc::log::dlog;
+
+using namespace std;
+using namespace isc::dns;
+
+namespace asiolink {
+
+/*
+ * Some of the member variables here are shared_ptrs and some are
+ * auto_ptrs. There will be one instance of Data for the lifetime
+ * of packet. The variables that are state only for a single packet
+ * use auto_ptr, as it is more lightweight. In the case of shared
+ * configuration (eg. the callbacks, socket), we use shared_ptrs.
+ */
+struct UDPServer::Data {
+    /*
+     * Constructor from parameters passed to UDPServer constructor.
+     * This instance will not be used to retrieve and answer the actual
+     * query, it will only hold parameters until we wait for the
+     * first packet. But we do initialize the socket in here.
+     */
+    Data(io_service& io_service, const ip::address& addr, const uint16_t port,
+        SimpleCallback* checkin, DNSLookup* lookup, DNSAnswer* answer) :
+        io_(io_service), done_(false), checkin_callback_(checkin),
+        lookup_callback_(lookup), answer_callback_(answer)
+    {
+        // We must use different instantiations for v4 and v6;
+        // otherwise ASIO will bind to both
+        udp proto = addr.is_v4() ? udp::v4() : udp::v6();
+        socket_.reset(new udp::socket(io_service, proto));
+        socket_->set_option(socket_base::reuse_address(true));
+        if (addr.is_v6()) {
+            socket_->set_option(asio::ip::v6_only(true));
+        }
+        socket_->bind(udp::endpoint(addr, port));
+    }
+
+    /*
+     * Copy constructor. Default one would probably do, but it is unnecessary
+     * to copy many of the member variables every time we fork to handle
+     * another packet.
+     *
+     * We also allocate data for receiving the packet here.
+     */
+    Data(const Data& other) :
+        io_(other.io_), socket_(other.socket_), done_(false),
+        checkin_callback_(other.checkin_callback_),
+        lookup_callback_(other.lookup_callback_),
+        answer_callback_(other.answer_callback_)
+    {
+        // Instantiate the data buffer and endpoint that will
+        // be used by the asynchronous receive call.
+        data_.reset(new char[MAX_LENGTH]);
+        sender_.reset(new udp::endpoint());
+    }
+
+    // The ASIO service object
+    asio::io_service& io_;
+
+    // Class member variables which are dynamic, and changes to which
+    // need to accessible from both sides of a coroutine fork or from
+    // outside of the coroutine (i.e., from an asynchronous I/O call),
+    // should be declared here as pointers and allocated in the
+    // constructor or in the coroutine.  This allows state information
+    // to persist when an individual copy of the coroutine falls out
+    // scope while waiting for an event, *so long as* there is another
+    // object that is referencing the same data.  As a side-benefit, using
+    // pointers also reduces copy overhead for coroutine objects.
+    //
+    // Note: Currently these objects are allocated by "new" in the
+    // constructor, or in the function operator while processing a query.
+    // Repeated allocations from the heap for every incoming query is
+    // clearly a performance issue; this must be optimized in the future.
+    // The plan is to have a structure pre-allocate several "Data"
+    // objects which can be pulled off a free list and placed on an in-use
+    // list whenever a query comes in.  This will serve the dual purpose
+    // of improving performance and guaranteeing that state information
+    // will *not* be destroyed when any one instance of the coroutine
+    // falls out of scope while waiting for an event.
+    //
+    // Socket used to for listen for queries.  Created in the
+    // constructor and stored in a shared_ptr because socket objects
+    // are not copyable.
+    boost::shared_ptr<asio::ip::udp::socket> socket_;
+
+    // The ASIO-internal endpoint object representing the client
+    std::auto_ptr<asio::ip::udp::endpoint> sender_;
+
+    // \c IOMessage and \c Message objects to be passed to the
+    // DNS lookup and answer providers
+    std::auto_ptr<asiolink::IOMessage> io_message_;
+
+    // The original query as sent by the client
+    isc::dns::MessagePtr query_message_;
+
+    // The response message we are building
+    isc::dns::MessagePtr answer_message_;
+
+    // The buffer into which the response is written
+    isc::dns::OutputBufferPtr respbuf_;
+
+    // The buffer into which the query packet is written
+    boost::shared_array<char> data_;
+
+    // State information that is entirely internal to a given instance
+    // of the coroutine can be declared here.
+    size_t bytes_;
+    bool done_;
+
+    // Callback functions provided by the caller
+    const SimpleCallback* checkin_callback_;
+    const DNSLookup* lookup_callback_;
+    const DNSAnswer* answer_callback_;
+
+    std::auto_ptr<IOEndpoint> peer_;
+    std::auto_ptr<IOSocket> iosock_;
+};
+
+/// The following functions implement the \c UDPServer class.
+///
+/// The constructor. It just creates new internal state object
+/// and lets it handle the initialization.
+UDPServer::UDPServer(io_service& io_service, const ip::address& addr,
+    const uint16_t port, SimpleCallback* checkin, DNSLookup* lookup,
+    DNSAnswer* answer) :
+    data_(new Data(io_service, addr, port, checkin, lookup, answer))
+{ }
+
+/// The function operator is implemented with the "stackless coroutine"
+/// pattern; see internal/coroutine.h for details.
+void
+UDPServer::operator()(error_code ec, size_t length) {
+    /// Because the coroutine reeentry block is implemented as
+    /// a switch statement, inline variable declarations are not
+    /// permitted.  Certain variables used below can be declared here.
+
+    CORO_REENTER (this) {
+        do {
+            /*
+             * This is preparation for receiving a packet. We get a new
+             * state object for the lifetime of the next packet to come.
+             * It allocates the buffers to receive data into.
+             */
+            data_.reset(new Data(*data_));
+
+            do {
+                // Begin an asynchronous receive, then yield.
+                // When the receive event is posted, the coroutine
+                // will resume immediately after this point.
+                CORO_YIELD data_->socket_->async_receive_from(
+                    buffer(data_->data_.get(), MAX_LENGTH), *data_->sender_,
+                    *this);
+            } while (ec || length == 0);
+
+            data_->bytes_ = length;
+
+            /*
+             * We fork the coroutine now. One (the child) will keep
+             * the current state and handle the packet, then die and
+             * drop ownership of the state. The other (parent) will just
+             * go into the loop again and replace the current state with
+             * a new one for a new object.
+             *
+             * Actually, both of the coroutines will be a copy of this
+             * one, but that's just internal implementation detail.
+             */
+            CORO_FORK data_->io_.post(UDPServer(*this));
+        } while (is_parent());
+
+        // Create an \c IOMessage object to store the query.
+        //
+        // (XXX: It would be good to write a factory function
+        // that would quickly generate an IOMessage object without
+        // all these calls to "new".)
+        data_->peer_.reset(new UDPEndpoint(*data_->sender_));
+        data_->iosock_.reset(new UDPSocket(*data_->socket_));
+        data_->io_message_.reset(new IOMessage(data_->data_.get(),
+            data_->bytes_, *data_->iosock_, *data_->peer_));
+
+        // Perform any necessary operations prior to processing an incoming
+        // query (e.g., checking for queued configuration messages).
+        //
+        // (XXX: it may be a performance issue to check in for every single
+        // incoming query; we may wish to throttle this in the future.)
+        if (data_->checkin_callback_ != NULL) {
+            (*data_->checkin_callback_)(*data_->io_message_);
+        }
+
+        // If we don't have a DNS Lookup provider, there's no point in
+        // continuing; we exit the coroutine permanently.
+        if (data_->lookup_callback_ == NULL) {
+            CORO_YIELD return;
+        }
+
+        // Instantiate objects that will be needed by the
+        // asynchronous DNS lookup and/or by the send call.
+        data_->respbuf_.reset(new OutputBuffer(0));
+        data_->query_message_.reset(new Message(Message::PARSE));
+        data_->answer_message_.reset(new Message(Message::RENDER));
+
+        // Schedule a DNS lookup, and yield.  When the lookup is
+        // finished, the coroutine will resume immediately after
+        // this point.
+        CORO_YIELD 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_) {
+            CORO_YIELD return;
+        }
+
+        // Call the DNS answer provider to render the answer into
+        // wire format
+        (*data_->answer_callback_)(*data_->io_message_, data_->query_message_,
+            data_->answer_message_, data_->respbuf_);
+
+        // Begin an asynchronous send, and then yield.  When the
+        // send completes, we will resume immediately after this point
+        // (though we have nothing further to do, so the coroutine
+        // will simply exit at that time).
+        CORO_YIELD data_->socket_->async_send_to(
+            buffer(data_->respbuf_->getData(), data_->respbuf_->getLength()),
+            *data_->sender_, *this);
+    }
+}
+
+/// Call the DNS lookup provider.  (Expected to be called by the
+/// AsyncLookup<UDPServer> handler.)
+void
+UDPServer::asyncLookup() {
+    (*data_->lookup_callback_)(*data_->io_message_,
+        data_->query_message_, data_->answer_message_, data_->respbuf_, this);
+}
+
+/// Post this coroutine on the ASIO service queue so that it will
+/// resume processing where it left off.  The 'done' parameter indicates
+/// whether there is an answer to return to the client.
+void
+UDPServer::resume(const bool done) {
+    data_->done_ = done;
+    data_->io_.post(*this);
+}
+
+bool
+UDPServer::hasAnswer() {
+    return (data_->done_);
+}
+
+} // namespace asiolink
diff --git a/src/lib/asiolink/udp_server.h b/src/lib/asiolink/udp_server.h
new file mode 100644
index 0000000..16a03dd
--- /dev/null
+++ b/src/lib/asiolink/udp_server.h
@@ -0,0 +1,102 @@
+// 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 __UDP_SERVER_H
+#define __UDP_SERVER_H 1
+
+#ifndef ASIO_HPP
+#error "asio.hpp must be included before including this, see asiolink.h as to why"
+#endif
+
+#include <asiolink/dns_server.h>
+#include <asiolink/simple_callback.h>
+#include <asiolink/dns_lookup.h>
+#include <asiolink/dns_answer.h>
+
+#include <coroutine.h>
+
+namespace asiolink {
+
+//
+// Asynchronous UDP server coroutine
+//
+///
+/// \brief This class implements the coroutine to handle UDP
+///        DNS query event. As such, it is both a \c DNSServer and
+///        a \c coroutine
+///
+class UDPServer : public virtual DNSServer, public virtual coroutine {
+public:
+    /// \brief Constructor
+    /// \param io_service the asio::io_service to work with
+    /// \param addr the IP address to listen for queries on
+    /// \param port the port to listen for queries on
+    /// \param checkin the callbackprovider for non-DNS events
+    /// \param lookup the callbackprovider for DNS lookup events
+    /// \param answer the callbackprovider for DNS answer events
+    explicit UDPServer(asio::io_service& io_service,
+                       const asio::ip::address& addr, const uint16_t port,
+                       SimpleCallback* checkin = NULL,
+                       DNSLookup* lookup = NULL,
+                       DNSAnswer* answer = NULL);
+
+    /// \brief The function operator
+    void operator()(asio::error_code ec = asio::error_code(),
+                    size_t length = 0);
+
+    /// \brief Calls the lookup callback
+    void asyncLookup();
+
+    /// \brief Resume operation
+    ///
+    /// \param done Set this to true if the lookup action is done and
+    ///        we have an answer
+    void resume(const bool done);
+
+    /// \brief Check if we have an answer
+    ///
+    /// \return true if we have an answer
+    bool hasAnswer();
+
+    /// \brief Returns the coroutine state value
+    ///
+    /// \return the coroutine state value
+    int value() { return (get_value()); }
+
+    /// \brief Clones the object
+    ///
+    /// \return a newly allocated copy of this object
+    DNSServer* clone() {
+        UDPServer* s = new UDPServer(*this);
+        return (s);
+    }
+
+private:
+    enum { MAX_LENGTH = 4096 };
+
+    /**
+     * \brief Internal state and data.
+     *
+     * We use the pimple design pattern, but not because we need to hide
+     * internal data. This class and whole header is for private use anyway.
+     * It turned out that UDPServer is copied a lot, because it is a coroutine.
+     * This way the overhead of copying is lower, we copy only one shared
+     * pointer instead of about 10 of them.
+     */
+    class Data;
+    boost::shared_ptr<Data> data_;
+};
+
+}      // namespace asiolink
+#endif // __UDP_SERVER_H
diff --git a/src/lib/asiolink/udp_socket.h b/src/lib/asiolink/udp_socket.h
new file mode 100644
index 0000000..5c641ff
--- /dev/null
+++ b/src/lib/asiolink/udp_socket.h
@@ -0,0 +1,48 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __UDP_SOCKET_H
+#define __UDP_SOCKET_H 1
+
+#ifndef ASIO_HPP
+#error "asio.hpp must be included before including this, see asiolink.h as to why"
+#endif
+
+#include <asiolink/io_socket.h>
+
+namespace asiolink {
+
+/// \brief The \c UDPSocket class is a concrete derived class of
+/// \c IOSocket that represents a UDP socket.
+///
+/// Other notes about \c TCPSocket applies to this class, too.
+class UDPSocket : public IOSocket {
+private:
+    UDPSocket(const UDPSocket& source);
+    UDPSocket& operator=(const UDPSocket& source);
+public:
+    /// \brief Constructor from an ASIO UDP socket.
+    ///
+    /// \param socket The ASIO representation of the UDP socket.
+    UDPSocket(asio::ip::udp::socket& socket) : socket_(socket) {}
+
+    virtual int getNative() const { return (socket_.native()); }
+    virtual int getProtocol() const { return (IPPROTO_UDP); }
+
+private:
+    asio::ip::udp::socket& socket_;
+};
+
+}      // namespace asiolink
+#endif // __UDP_SOCKET_H
diff --git a/src/lib/asiolink/udpdns.cc b/src/lib/asiolink/udpdns.cc
deleted file mode 100644
index 83f7b3c..0000000
--- a/src/lib/asiolink/udpdns.cc
+++ /dev/null
@@ -1,432 +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.
-
-#include <config.h>
-
-#include <unistd.h>             // for some IPC/network system calls
-#include <sys/socket.h>
-#include <netinet/in.h>
-
-#include <boost/bind.hpp>
-
-#include <asio.hpp>
-#include <asio/deadline_timer.hpp>
-
-#include <memory>
-#include <boost/shared_ptr.hpp>
-#include <boost/date_time/posix_time/posix_time_types.hpp>
-
-#include <dns/buffer.h>
-#include <dns/message.h>
-#include <dns/messagerenderer.h>
-#include <log/dummylog.h>
-#include <dns/opcode.h>
-#include <dns/rcode.h>
-
-#include <asiolink.h>
-#include <internal/coroutine.h>
-#include <internal/udpdns.h>
-
-using namespace asio;
-using asio::ip::udp;
-using asio::ip::tcp;
-using isc::log::dlog;
-
-using namespace std;
-using namespace isc::dns;
-
-namespace asiolink {
-
-/*
- * Some of the member variables here are shared_ptrs and some are
- * auto_ptrs. There will be one instance of Data for the lifetime
- * of packet. The variables that are state only for a single packet
- * use auto_ptr, as it is more lightweight. In the case of shared
- * configuration (eg. the callbacks, socket), we use shared_ptrs.
- */
-struct UDPServer::Data {
-    /*
-     * Constructor from parameters passed to UDPServer constructor.
-     * This instance will not be used to retrieve and answer the actual
-     * query, it will only hold parameters until we wait for the
-     * first packet. But we do initialize the socket in here.
-     */
-    Data(io_service& io_service, const ip::address& addr, const uint16_t port,
-        SimpleCallback* checkin, DNSLookup* lookup, DNSAnswer* answer) :
-        io_(io_service), done_(false), checkin_callback_(checkin),
-        lookup_callback_(lookup), answer_callback_(answer)
-    {
-        // We must use different instantiations for v4 and v6;
-        // otherwise ASIO will bind to both
-        udp proto = addr.is_v4() ? udp::v4() : udp::v6();
-        socket_.reset(new udp::socket(io_service, proto));
-        socket_->set_option(socket_base::reuse_address(true));
-        if (addr.is_v6()) {
-            socket_->set_option(asio::ip::v6_only(true));
-        }
-        socket_->bind(udp::endpoint(addr, port));
-    }
-
-    /*
-     * Copy constructor. Default one would probably do, but it is unnecessary
-     * to copy many of the member variables every time we fork to handle
-     * another packet.
-     *
-     * We also allocate data for receiving the packet here.
-     */
-    Data(const Data& other) :
-        io_(other.io_), socket_(other.socket_), done_(false),
-        checkin_callback_(other.checkin_callback_),
-        lookup_callback_(other.lookup_callback_),
-        answer_callback_(other.answer_callback_)
-    {
-        // Instantiate the data buffer and endpoint that will
-        // be used by the asynchronous receive call.
-        data_.reset(new char[MAX_LENGTH]);
-        sender_.reset(new udp::endpoint());
-    }
-
-    // The ASIO service object
-    asio::io_service& io_;
-
-    // Class member variables which are dynamic, and changes to which
-    // need to accessible from both sides of a coroutine fork or from
-    // outside of the coroutine (i.e., from an asynchronous I/O call),
-    // should be declared here as pointers and allocated in the
-    // constructor or in the coroutine.  This allows state information
-    // to persist when an individual copy of the coroutine falls out
-    // scope while waiting for an event, *so long as* there is another
-    // object that is referencing the same data.  As a side-benefit, using
-    // pointers also reduces copy overhead for coroutine objects.
-    //
-    // Note: Currently these objects are allocated by "new" in the
-    // constructor, or in the function operator while processing a query.
-    // Repeated allocations from the heap for every incoming query is
-    // clearly a performance issue; this must be optimized in the future.
-    // The plan is to have a structure pre-allocate several "Data"
-    // objects which can be pulled off a free list and placed on an in-use
-    // list whenever a query comes in.  This will serve the dual purpose
-    // of improving performance and guaranteeing that state information
-    // will *not* be destroyed when any one instance of the coroutine
-    // falls out of scope while waiting for an event.
-    //
-    // Socket used to for listen for queries.  Created in the
-    // constructor and stored in a shared_ptr because socket objects
-    // are not copyable.
-    boost::shared_ptr<asio::ip::udp::socket> socket_;
-
-    // The ASIO-internal endpoint object representing the client
-    std::auto_ptr<asio::ip::udp::endpoint> sender_;
-
-    // \c IOMessage and \c Message objects to be passed to the
-    // DNS lookup and answer providers
-    std::auto_ptr<asiolink::IOMessage> io_message_;
-
-    // The original query as sent by the client
-    isc::dns::MessagePtr query_message_;
-
-    // The response message we are building
-    isc::dns::MessagePtr answer_message_;
-
-    // The buffer into which the response is written
-    isc::dns::OutputBufferPtr respbuf_;
-
-    // The buffer into which the query packet is written
-    boost::shared_array<char> data_;
-
-    // State information that is entirely internal to a given instance
-    // of the coroutine can be declared here.
-    size_t bytes_;
-    bool done_;
-
-    // Callback functions provided by the caller
-    const SimpleCallback* checkin_callback_;
-    const DNSLookup* lookup_callback_;
-    const DNSAnswer* answer_callback_;
-
-    std::auto_ptr<IOEndpoint> peer_;
-    std::auto_ptr<IOSocket> iosock_;
-};
-
-/// The following functions implement the \c UDPServer class.
-///
-/// The constructor. It just creates new internal state object
-/// and lets it handle the initialization.
-UDPServer::UDPServer(io_service& io_service, const ip::address& addr,
-    const uint16_t port, SimpleCallback* checkin, DNSLookup* lookup,
-    DNSAnswer* answer) :
-    data_(new Data(io_service, addr, port, checkin, lookup, answer))
-{ }
-
-/// The function operator is implemented with the "stackless coroutine"
-/// pattern; see internal/coroutine.h for details.
-void
-UDPServer::operator()(error_code ec, size_t length) {
-    /// Because the coroutine reeentry block is implemented as
-    /// a switch statement, inline variable declarations are not
-    /// permitted.  Certain variables used below can be declared here.
-
-    CORO_REENTER (this) {
-        do {
-            /*
-             * This is preparation for receiving a packet. We get a new
-             * state object for the lifetime of the next packet to come.
-             * It allocates the buffers to receive data into.
-             */
-            data_.reset(new Data(*data_));
-
-            do {
-                // Begin an asynchronous receive, then yield.
-                // When the receive event is posted, the coroutine
-                // will resume immediately after this point.
-                CORO_YIELD data_->socket_->async_receive_from(
-                    buffer(data_->data_.get(), MAX_LENGTH), *data_->sender_,
-                    *this);
-            } while (ec || length == 0);
-
-            data_->bytes_ = length;
-
-            /*
-             * We fork the coroutine now. One (the child) will keep
-             * the current state and handle the packet, then die and
-             * drop ownership of the state. The other (parent) will just
-             * go into the loop again and replace the current state with
-             * a new one for a new object.
-             *
-             * Actually, both of the coroutines will be a copy of this
-             * one, but that's just internal implementation detail.
-             */
-            CORO_FORK data_->io_.post(UDPServer(*this));
-        } while (is_parent());
-
-        // Create an \c IOMessage object to store the query.
-        //
-        // (XXX: It would be good to write a factory function
-        // that would quickly generate an IOMessage object without
-        // all these calls to "new".)
-        data_->peer_.reset(new UDPEndpoint(*data_->sender_));
-        data_->iosock_.reset(new UDPSocket(*data_->socket_));
-        data_->io_message_.reset(new IOMessage(data_->data_.get(),
-            data_->bytes_, *data_->iosock_, *data_->peer_));
-
-        // Perform any necessary operations prior to processing an incoming
-        // query (e.g., checking for queued configuration messages).
-        //
-        // (XXX: it may be a performance issue to check in for every single
-        // incoming query; we may wish to throttle this in the future.)
-        if (data_->checkin_callback_ != NULL) {
-            (*data_->checkin_callback_)(*data_->io_message_);
-        }
-
-        // If we don't have a DNS Lookup provider, there's no point in
-        // continuing; we exit the coroutine permanently.
-        if (data_->lookup_callback_ == NULL) {
-            CORO_YIELD return;
-        }
-
-        // Instantiate objects that will be needed by the
-        // asynchronous DNS lookup and/or by the send call.
-        data_->respbuf_.reset(new OutputBuffer(0));
-        data_->query_message_.reset(new Message(Message::PARSE));
-        data_->answer_message_.reset(new Message(Message::RENDER));
-
-        // Schedule a DNS lookup, and yield.  When the lookup is
-        // finished, the coroutine will resume immediately after
-        // this point.
-        CORO_YIELD 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_) {
-            CORO_YIELD return;
-        }
-
-        // Call the DNS answer provider to render the answer into
-        // wire format
-        (*data_->answer_callback_)(*data_->io_message_, data_->query_message_,
-            data_->answer_message_, data_->respbuf_);
-
-        // Begin an asynchronous send, and then yield.  When the
-        // send completes, we will resume immediately after this point
-        // (though we have nothing further to do, so the coroutine
-        // will simply exit at that time).
-        CORO_YIELD data_->socket_->async_send_to(
-            buffer(data_->respbuf_->getData(), data_->respbuf_->getLength()),
-            *data_->sender_, *this);
-    }
-}
-
-/// Call the DNS lookup provider.  (Expected to be called by the
-/// AsyncLookup<UDPServer> handler.)
-void
-UDPServer::asyncLookup() {
-    (*data_->lookup_callback_)(*data_->io_message_,
-        data_->query_message_, data_->answer_message_, data_->respbuf_, this);
-}
-
-/// Post this coroutine on the ASIO service queue so that it will
-/// resume processing where it left off.  The 'done' parameter indicates
-/// whether there is an answer to return to the client.
-void
-UDPServer::resume(const bool done) {
-    data_->done_ = done;
-    data_->io_.post(*this);
-}
-
-bool
-UDPServer::hasAnswer() {
-    return (data_->done_);
-}
-
-// Private UDPQuery data (see internal/udpdns.h for reasons)
-struct UDPQuery::PrivateData {
-    // Socket we send query to and expect reply from there
-    udp::socket socket;
-    // Where was the query sent
-    udp::endpoint remote;
-    // What we ask the server
-    Question question;
-    // We will store the answer here
-    OutputBufferPtr buffer;
-    OutputBufferPtr msgbuf;
-    // Temporary buffer for answer
-    boost::shared_array<char> data;
-    // This will be called when the data arrive or timeouts
-    Callback* callback;
-    // Did we already stop operating (data arrived, we timed out, someone
-    // called stop). This can be so when we are cleaning up/there are
-    // still pointers to us.
-    bool stopped;
-    // Timer to measure timeouts.
-    deadline_timer timer;
-    // How many milliseconds are we willing to wait for answer?
-    int timeout;
-
-    PrivateData(io_service& service,
-        const udp::socket::protocol_type& protocol, const Question &q,
-        OutputBufferPtr b, Callback *c) :
-        socket(service, protocol),
-        question(q),
-        buffer(b),
-        msgbuf(new OutputBuffer(512)),
-        callback(c),
-        stopped(false),
-        timer(service)
-    { }
-};
-
-/// The following functions implement the \c UDPQuery class.
-///
-/// The constructor
-UDPQuery::UDPQuery(io_service& io_service,
-                   const Question& q, const IOAddress& addr, uint16_t port,
-                   OutputBufferPtr buffer, Callback *callback, int timeout) :
-    data_(new PrivateData(io_service,
-        addr.getFamily() == AF_INET ? udp::v4() : udp::v6(), q, buffer,
-        callback))
-{
-    data_->remote = UDPEndpoint(addr, port).getASIOEndpoint();
-    data_->timeout = timeout;
-}
-
-/// The function operator is implemented with the "stackless coroutine"
-/// pattern; see internal/coroutine.h for details.
-void
-UDPQuery::operator()(error_code ec, size_t length) {
-    if (ec || data_->stopped) {
-        return;
-    }
-
-    CORO_REENTER (this) {
-        /// Generate the upstream query and render it to wire format
-        /// This is done in a different scope to allow inline variable
-        /// declarations.
-        {
-            Message msg(Message::RENDER);
-            
-            // XXX: replace with boost::random or some other suitable PRNG
-            msg.setQid(0);
-            msg.setOpcode(Opcode::QUERY());
-            msg.setRcode(Rcode::NOERROR());
-            msg.setHeaderFlag(Message::HEADERFLAG_RD);
-            msg.addQuestion(data_->question);
-            MessageRenderer renderer(*data_->msgbuf);
-            msg.toWire(renderer);
-            dlog("Sending " + msg.toText() + " to " +
-                data_->remote.address().to_string());
-        }
-
-
-        // If we timeout, we stop, which will shutdown everything and
-        // cancel all other attempts to run inside the coroutine
-        if (data_->timeout != -1) {
-            data_->timer.expires_from_now(boost::posix_time::milliseconds(
-                data_->timeout));
-            data_->timer.async_wait(boost::bind(&UDPQuery::stop, *this,
-                TIME_OUT));
-        }
-
-        // Begin an asynchronous send, and then yield.  When the
-        // send completes, we will resume immediately after this point.
-        CORO_YIELD data_->socket.async_send_to(buffer(data_->msgbuf->getData(),
-            data_->msgbuf->getLength()), data_->remote, *this);
-
-        /// Allocate space for the response.  (XXX: This should be
-        /// optimized by maintaining a free list of pre-allocated blocks)
-        data_->data.reset(new char[MAX_LENGTH]);
-
-        /// Begin an asynchronous receive, and yield.  When the receive
-        /// completes, we will resume immediately after this point.
-        CORO_YIELD data_->socket.async_receive_from(buffer(data_->data.get(),
-            MAX_LENGTH), data_->remote, *this);
-        // The message is not rendered yet, so we can't print it easilly
-        dlog("Received response from " + data_->remote.address().to_string());
-
-        /// Copy the answer into the response buffer.  (XXX: If the
-        /// OutputBuffer object were made to meet the requirements of
-        /// a MutableBufferSequence, then it could be written to directly
-        /// by async_recieve_from() and this additional copy step would
-        /// be unnecessary.)
-        data_->buffer->writeData(data_->data.get(), length);
-
-        /// We are done
-        stop(SUCCESS);
-    }
-}
-
-void
-UDPQuery::stop(Result result) {
-    if (!data_->stopped) {
-        switch (result) {
-            case TIME_OUT:
-                dlog("Query timed out");
-                break;
-            case STOPPED:
-                dlog("Query stopped");
-                break;
-            default:;
-        }
-        data_->stopped = true;
-        data_->socket.cancel();
-        data_->socket.close();
-        data_->timer.cancel();
-        if (data_->callback) {
-            (*data_->callback)(result);
-        }
-    }
-}
-
-}
diff --git a/src/lib/cache/Makefile.am b/src/lib/cache/Makefile.am
new file mode 100644
index 0000000..264aca6
--- /dev/null
+++ b/src/lib/cache/Makefile.am
@@ -0,0 +1,33 @@
+SUBDIRS = . tests
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES) $(MULTITHREADING_FLAG)
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/nsas -I$(top_builddir)/src/lib/nsas
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/cache -I$(top_builddir)/src/lib/cache
+AM_CPPFLAGS += $(SQLITE_CFLAGS)
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+# Some versions of GCC warn about some versions of Boost regarding
+# missing initializer for members in its posix_time.
+# https://svn.boost.org/trac/boost/ticket/3477
+# But older GCC compilers don't have the flag.
+AM_CXXFLAGS += $(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
+
+if USE_CLANGPP
+# clang++ complains about unused function parameters in some boost header
+# files.
+AM_CXXFLAGS += -Wno-unused-parameter
+endif
+
+lib_LTLIBRARIES = libcache.la
+libcache_la_SOURCES  = resolver_cache.h resolver_cache.cc
+libcache_la_SOURCES  += message_cache.h message_cache.cc
+libcache_la_SOURCES  += message_entry.h message_entry.cc
+libcache_la_SOURCES  += rrset_cache.h rrset_cache.cc
+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
+
+CLEANFILES = *.gcno *.gcda
diff --git a/src/lib/cache/TODO b/src/lib/cache/TODO
new file mode 100644
index 0000000..0743ee4
--- /dev/null
+++ b/src/lib/cache/TODO
@@ -0,0 +1,12 @@
+* Revisit the algorithm used by getRRsetTrustLevel() in message_entry.cc.
+* Implement dump/load/resize interfaces of rrset/message/recursor cache.
+* Once LRU hash table is implemented, it should be used by message/rrset cache.
+* Once the hash/lrulist related files in /lib/nsas is moved to seperated
+  folder, the code of recursor cache has to be updated.
+* Set proper AD flags once DNSSEC is supported by the cache.
+* When the message or rrset entry has expired, it should be removed
+  from the cache, or just moved to the head of LRU list, so that it
+  can removed first.
+* Make resolver cache be smart to refetch the messages that are about
+  to expire.
+
diff --git a/src/lib/cache/cache_entry_key.cc b/src/lib/cache/cache_entry_key.cc
new file mode 100644
index 0000000..35917a0
--- /dev/null
+++ b/src/lib/cache/cache_entry_key.cc
@@ -0,0 +1,44 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <sstream>
+#include "cache_entry_key.h"
+
+using namespace std;
+
+namespace isc {
+namespace cache {
+const std::string
+genCacheEntryName(const isc::dns::Name& name, const isc::dns::RRType& type) {
+    std::string keystr = name.toText();
+    ostringstream stream;
+    stream << type.getCode();
+    keystr += stream.str();
+    return (keystr);
+}
+
+const std::string
+genCacheEntryName(const std::string& namestr, const uint16_t type) {
+    std::string keystr = namestr;
+    ostringstream stream;
+    stream << type;
+    keystr += stream.str();
+    return (keystr);
+}
+
+} // namespace cache
+} // namespace isc
+
diff --git a/src/lib/cache/cache_entry_key.h b/src/lib/cache/cache_entry_key.h
new file mode 100644
index 0000000..002f958
--- /dev/null
+++ b/src/lib/cache/cache_entry_key.h
@@ -0,0 +1,56 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#ifndef __CACHE_ENTRY_KEY_H
+#define __CACHE_ENTRY_KEY_H
+
+#include <string>
+#include <dns/name.h>
+#include <dns/rrtype.h>
+
+namespace isc {
+namespace cache {
+
+/// \brief Entry Name Generation Functions
+///
+/// Generate the name for message/rrset entries.
+///
+/// Concatenates the string representation of the Name and the
+/// string representation of the type number.
+///
+/// Note: the returned name is a text string, not wire format.
+/// eg. if name is 'example.com.', type is 'A', the return
+/// value is 'example.com.1'
+///
+/// \param name The Name to create a text entry for
+/// \param type The RRType to create a text entry for
+/// \return return the entry name.
+const std::string
+genCacheEntryName(const isc::dns::Name& name, const isc::dns::RRType& type);
+
+///
+/// \overload
+///
+/// \param namestr A string representation of a DNS Name
+/// \param type The value of a DNS RRType
+const std::string
+genCacheEntryName(const std::string& namestr, const uint16_t type);
+
+} // namespace cache
+} // namespace isc
+
+#endif // __CACHE_ENTRY_KEY_H
+
diff --git a/src/lib/cache/local_zone_data.cc b/src/lib/cache/local_zone_data.cc
new file mode 100644
index 0000000..2dcc113
--- /dev/null
+++ b/src/lib/cache/local_zone_data.cc
@@ -0,0 +1,58 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <dns/rrset.h>
+#include "local_zone_data.h"
+#include "cache_entry_key.h"
+#include "rrset_copy.h"
+
+using namespace std;
+using namespace isc::dns;
+
+namespace isc {
+namespace cache {
+
+typedef pair<std::string, RRsetPtr> RRsetMapPair;
+typedef map<std::string, RRsetPtr>::iterator RRsetMapIterator;
+
+isc::dns::RRsetPtr
+LocalZoneData::lookup(const isc::dns::Name& name,
+                      const isc::dns::RRType& type)
+{
+    string key = genCacheEntryName(name, type);
+    RRsetMapIterator iter = rrsets_map_.find(key);
+    if (iter == rrsets_map_.end()) {
+        return (RRsetPtr());
+    } else {
+        return (iter->second);
+    }
+}
+
+void
+LocalZoneData::update(const isc::dns::RRset& rrset) {
+    //TODO Do we really need to recreate the rrset again?
+    string key = genCacheEntryName(rrset.getName(), rrset.getType());
+    RRset* rrset_copy = new RRset(rrset.getName(), rrset.getClass(),
+                                  rrset.getType(), rrset.getTTL());
+
+    rrsetCopy(rrset, *rrset_copy);
+    RRsetPtr rrset_ptr(rrset_copy);
+    rrsets_map_[key] = rrset_ptr;
+}
+
+} // namespace cache
+} // namespace isc
+
diff --git a/src/lib/cache/local_zone_data.h b/src/lib/cache/local_zone_data.h
new file mode 100644
index 0000000..bcf5a94
--- /dev/null
+++ b/src/lib/cache/local_zone_data.h
@@ -0,0 +1,66 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#ifndef _LOCAL_ZONE_DATA
+#define _LOCAL_ZONE_DATA
+
+#include <map>
+#include <string>
+#include <boost/shared_ptr.hpp>
+#include <dns/rrset.h>
+
+namespace isc {
+namespace cache {
+
+/// \brief Local Zone Data
+/// The object of LocalZoneData represents the data of one
+/// local zone. It provides the interface for lookup the rrsets
+/// in the zone.
+class LocalZoneData {
+public:
+    LocalZoneData(uint16_t rrset_class) : class_(rrset_class)
+    {}
+
+    /// \brief Look up one rrset.
+    ///
+    /// \param qname The query name to look up
+    /// \param qtype The query type to look up
+    /// \return return the shared_ptr of rrset if it is
+    /// found in the local zone, or else, return NULL.
+    isc::dns::RRsetPtr lookup(const isc::dns::Name& qname,
+                              const isc::dns::RRType& qtype);
+
+    /// \brief Update the rrset in the local zone.
+    ///
+    /// If the rrset doesn't exist, it will be added.
+    /// Otherwise, the existed one will be overwritten.
+    ///
+    /// \param rrset The rrset to update
+    void update(const isc::dns::RRset& rrset);
+
+private:
+    std::map<std::string, isc::dns::RRsetPtr> rrsets_map_; // RRsets of the zone
+    uint16_t class_; // The class of the zone
+};
+
+typedef boost::shared_ptr<LocalZoneData> LocalZoneDataPtr;
+typedef boost::shared_ptr<const LocalZoneData> ConstLocalZoneDataPtr;
+
+} // namespace cache
+} // namespace isc
+
+#endif // _LOCAL_ZONE_DATA
+
diff --git a/src/lib/cache/message_cache.cc b/src/lib/cache/message_cache.cc
new file mode 100644
index 0000000..6582199
--- /dev/null
+++ b/src/lib/cache/message_cache.cc
@@ -0,0 +1,97 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <config.h>
+
+#include <nsas/nsas_entry_compare.h>
+#include <nsas/hash_table.h>
+#include <nsas/hash_deleter.h>
+#include "message_cache.h"
+#include "cache_entry_key.h"
+
+using namespace isc::nsas;
+using namespace isc::dns;
+using namespace std;
+
+namespace isc {
+namespace cache {
+
+MessageCache::MessageCache(boost::shared_ptr<RRsetCache> rrset_cache,
+    uint32_t cache_size, uint16_t message_class):
+    message_class_(message_class),
+    rrset_cache_(rrset_cache),
+    message_table_(new NsasEntryCompare<MessageEntry>, cache_size),
+    message_lru_((3 * cache_size),
+                  new HashDeleter<MessageEntry>(message_table_))
+{
+}
+
+bool
+MessageCache::lookup(const isc::dns::Name& qname,
+                     const isc::dns::RRType& qtype,
+                     isc::dns::Message& response)
+{
+    std::string entry_name = genCacheEntryName(qname, qtype);
+    HashKey entry_key = HashKey(entry_name, RRClass(message_class_));
+    MessageEntryPtr msg_entry = message_table_.get(entry_key);
+    if(msg_entry) {
+        message_lru_.touch(msg_entry);
+        return (msg_entry->genMessage(time(NULL), response));
+    }
+
+    return (false);
+}
+
+bool
+MessageCache::update(const Message& msg) {
+    QuestionIterator iter = msg.beginQuestion();
+    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.
+    // We have find the existed message entry, since we need to delete it
+    // from lru list too.
+    // TODO, but there should be a better way, since we here have to remove and
+    // add the message entry, maybe there is one way to touch it once.
+    MessageEntryPtr old_msg_entry = message_table_.get(entry_key);
+    if (old_msg_entry) {
+        message_lru_.remove(old_msg_entry);
+    }
+
+    MessageEntryPtr msg_entry(new MessageEntry(msg, rrset_cache_));
+    message_lru_.add(msg_entry);
+    return (message_table_.add(msg_entry, entry_key, true));
+}
+
+void
+MessageCache::dump(const std::string&) {
+    //TODO
+}
+
+void
+MessageCache::load(const std::string&) {
+    //TODO
+}
+
+bool
+MessageCache::resize(uint32_t) {
+    //TODO
+    return (true);
+}
+
+} // namespace cache
+} // namespace isc
+
diff --git a/src/lib/cache/message_cache.h b/src/lib/cache/message_cache.h
new file mode 100644
index 0000000..0131b30
--- /dev/null
+++ b/src/lib/cache/message_cache.h
@@ -0,0 +1,95 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#ifndef __MESSAGE_CACHE_H
+#define __MESSAGE_CACHE_H
+
+#include <string>
+#include <boost/shared_ptr.hpp>
+#include <dns/message.h>
+#include "message_entry.h"
+#include <nsas/hash_table.h>
+#include <nsas/lru_list.h>
+
+namespace isc {
+namespace cache {
+
+class RRsetCache;
+
+/// \brief Message Cache
+/// The object of MessageCache represents the cache for class-specific
+/// messages.
+///
+class MessageCache {
+// Noncopyable
+private:
+    MessageCache(const MessageCache& source);
+    MessageCache& operator=(const MessageCache& source);
+public:
+    /// \param cache_size The size of message cache.
+    MessageCache(boost::shared_ptr<RRsetCache> rrset_cache_,
+                 uint32_t cache_size, uint16_t message_class);
+
+    /// \brief Look up message in cache.
+    /// \param message generated response message if the message entry
+    ///        can be found.
+    ///
+    /// \return return true if the message can be found in cache, or else,
+    /// return false.
+    //TODO Maybe some user just want to get the message_entry.
+    bool lookup(const isc::dns::Name& qname,
+                const isc::dns::RRType& qtype,
+                isc::dns::Message& message);
+
+    /// \brief Update the message in the cache with the new one.
+    /// If the message doesn't exist in the cache, it will be added
+    /// directly.
+    bool update(const isc::dns::Message& msg);
+
+    /// \brief Dump the message cache to specified file.
+    /// \todo It should can be dumped to one configured database.
+    void dump(const std::string& file_name);
+
+    /// \brief Load the cache from one file.
+    /// \todo It should can be loaded from one configured database.
+    void load(const std::string& file_name);
+
+    /// \brief Resize the size of message cache in runtime.
+    bool resize(uint32_t size);
+
+protected:
+    /// \brief Get the hash key for the message entry in the cache.
+    /// \param name query name of the message.
+    /// \param type query type of the message.
+    /// \return return the hash key.
+    HashKey getEntryHashKey(const isc::dns::Name& name,
+                            const isc::dns::RRType& type) const;
+
+    // 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_;
+    isc::nsas::HashTable<MessageEntry> message_table_;
+    isc::nsas::LruList<MessageEntry> message_lru_;
+};
+
+typedef boost::shared_ptr<MessageCache> MessageCachePtr;
+
+} // namespace cache
+} // namespace isc
+
+#endif // __MESSAGE_CACHE_H
+
diff --git a/src/lib/cache/message_entry.cc b/src/lib/cache/message_entry.cc
new file mode 100644
index 0000000..fe21ce9
--- /dev/null
+++ b/src/lib/cache/message_entry.cc
@@ -0,0 +1,251 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <config.h>
+
+#include <limits>
+#include <dns/message.h>
+#include <nsas/nsas_entry.h>
+#include "message_entry.h"
+#include "rrset_cache.h"
+
+using namespace isc::dns;
+using namespace std;
+
+namespace isc {
+namespace cache {
+
+static uint32_t MAX_UINT32 = numeric_limits<uint32_t>::max();
+
+MessageEntry::MessageEntry(const isc::dns::Message& msg,
+                           boost::shared_ptr<RRsetCache> rrset_cache):
+    rrset_cache_(rrset_cache),
+    headerflag_aa_(false),
+    headerflag_tc_(false)
+{
+    initMessageEntry(msg);
+    entry_name_ = genCacheEntryName(query_name_, query_type_);
+    hash_key_ptr_ = new HashKey(entry_name_, RRClass(query_class_));
+}
+
+bool
+MessageEntry::getRRsetEntries(vector<RRsetEntryPtr>& rrset_entry_vec,
+                              const time_t time_now)
+{
+    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_,
+                                                        rrsets_[index].type_);
+        if (time_now < rrset_entry->getExpireTime()) {
+            rrset_entry_vec.push_back(rrset_entry);
+        } else {
+            return (false);
+        }
+    }
+
+    return (true);
+}
+
+void
+MessageEntry::addRRset(isc::dns::Message& message,
+                       const vector<RRsetEntryPtr>& rrset_entry_vec,
+                       const isc::dns::Message::Section& section,
+                       bool dnssec_need)
+{
+    uint16_t start_index = 0;
+    uint16_t end_index = answer_count_;
+    assert(section != Message::SECTION_QUESTION);
+
+    if (section == Message::SECTION_AUTHORITY) {
+        start_index = answer_count_;
+        end_index = answer_count_ + authority_count_;
+    } else if (section == Message::SECTION_ADDITIONAL) {
+        start_index = answer_count_ + authority_count_;
+        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);
+    }
+}
+
+bool
+MessageEntry::genMessage(const time_t& time_now,
+                         isc::dns::Message& msg)
+{
+    if (time_now >= expire_time_) {
+        // The message entry has expired.
+        return (false);
+    } else {
+        // Before do any generation, we should check if some rrset
+        // has expired, if it is, return false.
+        vector<RRsetEntryPtr> rrset_entry_vec;
+        if (false == getRRsetEntries(rrset_entry_vec, time_now)) {
+            return (false);
+        }
+
+        // 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_);
+        msg.setHeaderFlag(Message::HEADERFLAG_TC, headerflag_tc_);
+
+        bool dnssec_need = msg.getEDNS().get();
+        addRRset(msg, rrset_entry_vec, Message::SECTION_ANSWER, dnssec_need);
+        addRRset(msg, rrset_entry_vec, Message::SECTION_AUTHORITY, dnssec_need);
+        addRRset(msg, rrset_entry_vec, Message::SECTION_ADDITIONAL, dnssec_need);
+
+        return (true);
+    }
+}
+
+RRsetTrustLevel
+MessageEntry::getRRsetTrustLevel(const Message& message,
+    const isc::dns::RRsetPtr& rrset,
+    const isc::dns::Message::Section& section)
+{
+    bool aa = message.getHeaderFlag(Message::HEADERFLAG_AA);
+    switch(section) {
+        case Message::SECTION_ANSWER: {
+            if (aa) {
+                RRsetIterator rrset_iter = message.beginSection(section);
+
+                // Make sure we are inspecting the right RRset
+                while((*rrset_iter)->getName() != rrset->getName() &&
+                      (*rrset_iter)->getType() != rrset->getType() &&
+                      rrset_iter != message.endSection(section)) {
+                    ++rrset_iter;
+                }
+                assert(rrset_iter != message.endSection(section));
+                
+                // According RFC2181 section 5.4.1, only the record
+                // describing that ailas is necessarily authoritative.
+                // If there is one or more CNAME records in answer section.
+                // CNAME records is assumed as the first rrset.
+                if ((*rrset_iter)->getType() == RRType::CNAME()) {
+                    // TODO: real equals for RRsets?
+                    if ((*rrset_iter).get() == rrset.get()) {
+                        return (RRSET_TRUST_ANSWER_AA);
+                    } else {
+                        return (RRSET_TRUST_ANSWER_NONAA);
+                    }
+                }
+
+                // Here, if the first rrset is DNAME, then assume the
+                // second rrset is synchronized CNAME record, except
+                // these two records, any other records in answer section
+                // should be treated as non-authoritative.
+                // TODO, this part logic should be revisited later,
+                // since it's not mentioned by RFC2181.
+                if ((*rrset_iter)->getType() == RRType::DNAME()) {
+                    // TODO: real equals for RRsets?
+                    if ((*rrset_iter).get() == rrset.get() ||
+                        ((++rrset_iter) != message.endSection(section) &&
+                                     (*rrset_iter).get() == rrset.get())) {
+                        return (RRSET_TRUST_ANSWER_AA);
+                    } else {
+                        return (RRSET_TRUST_ANSWER_NONAA);
+                    }
+                }
+
+                return (RRSET_TRUST_ANSWER_AA);
+
+            } else {
+                return (RRSET_TRUST_ANSWER_NONAA);
+            }
+            break;
+        }
+
+        case Message::SECTION_AUTHORITY: {
+            if (aa) {
+                return (RRSET_TRUST_AUTHORITY_AA);
+            } else {
+                return (RRSET_TRUST_AUTHORITY_NONAA);
+            }
+            break;
+        }
+
+        case Message::SECTION_ADDITIONAL: {
+            if (aa) {
+                return (RRSET_TRUST_ADDITIONAL_AA);
+            } else {
+                return (RRSET_TRUST_ADDITIONAL_NONAA);
+            }
+            break;
+        }
+
+        default:
+            return (RRSET_TRUST_DEFAULT);
+    }
+}
+
+void
+MessageEntry::parseSection(const isc::dns::Message& msg,
+                         const Message::Section& section,
+                         uint32_t& smaller_ttl,
+                         uint16_t& rrset_count)
+{
+    RRsetIterator iter;
+    int count = 0;
+    for (iter = msg.beginSection(section);
+         iter != msg.endSection(section);
+         ++iter) {
+        // Add the rrset entry to rrset_cache or update the existed
+        // rrset entry if the new one is more authoritative.
+        //TODO set proper rrset trust level.
+        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()));
+
+        uint32_t rrset_ttl = rrset_entry->getTTL();
+        if (smaller_ttl > rrset_ttl) {
+            smaller_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);
+    headerflag_tc_ = msg.getHeaderFlag(Message::HEADERFLAG_TC);
+
+    // We only cache the first question in question section.
+    // TODO, do we need to support muptiple questions?
+    query_count_ = 1;
+    QuestionIterator iter = msg.beginQuestion();
+    query_name_ = (*iter)->getName().toText();
+    query_type_ = (*iter)->getType().getCode();
+    query_class_ = (*iter)->getClass().getCode();
+
+    uint32_t min_ttl = MAX_UINT32;
+    parseSection(msg, Message::SECTION_ANSWER, min_ttl, answer_count_);
+    parseSection(msg, Message::SECTION_AUTHORITY, min_ttl, authority_count_);
+    parseSection(msg, Message::SECTION_ADDITIONAL, min_ttl, additional_count_);
+
+    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
new file mode 100644
index 0000000..0ed00f0
--- /dev/null
+++ b/src/lib/cache/message_entry.h
@@ -0,0 +1,183 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#ifndef __MESSAGE_ENTRY_H
+#define __MESSAGE_ENTRY_H
+
+#include <vector>
+#include <dns/message.h>
+#include <dns/rrset.h>
+#include <nsas/nsas_entry.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
+///
+/// The object of MessageEntry represents one response message
+/// answered to the resolver client.
+class MessageEntry : public NsasEntry<MessageEntry> {
+// Noncopyable
+private:
+    MessageEntry(const MessageEntry& source);
+    MessageEntry& operator=(const MessageEntry& source);
+public:
+
+    /// \brief Initialize the message entry object with one dns
+    ///        message.
+    /// \param message The message used to initialize MessageEntry.
+    /// \param rrset_cache the pointer of RRsetCache. When one message
+    ///        entry is created, rrset cache needs to be updated,
+    ///        since some new rrset entries may be inserted into
+    ///        rrset cache, or the existed rrset entries need
+    ///        to be updated.
+    MessageEntry(const isc::dns::Message& message,
+                 boost::shared_ptr<RRsetCache> rrset_cache);
+
+    /// \brief generate one dns message according
+    ///        the rrsets information of the message.
+    ///
+    /// \param time_now set the ttl of each rrset in the message
+    ///        as "expire_time - time_now" (expire_time is the
+    ///        expiration time of the rrset).
+    /// \param response generated dns message.
+    /// \return return true if the response message can be generated
+    ///         from the cached information, or else, return false.
+    bool genMessage(const time_t& time_now, isc::dns::Message& response);
+
+    /// \brief Get the hash key of the message entry.
+    ///
+    /// \return return hash key
+    virtual HashKey hashKey() const {
+        return (*hash_key_ptr_);
+    }
+
+protected:
+    /// \brief Initialize the message entry with dns message.
+    ///
+    /// \param message The Message to initialize the entry with
+    void initMessageEntry(const isc::dns::Message& message);
+
+    /// \brief These two functions should be static functions
+    ///        placed in cc file. Put them here just for easy unit
+    ///        tests.
+    //@{
+    /// \brief Parse the rrsets in specified section.
+    ///
+    /// \param msg The message to parse the RRsets from
+    /// \param section The Section to parse the RRsets from
+    /// \param smaller_ttl Get the smallest ttl of rrsets in
+    ///        specified section, if it's smaller than the given value.
+    /// \param rrset_count the rrset count of the section.
+    ///        (TODO for Message, getRRsetCount() should be one
+    ///        interface provided by Message.)
+    void parseSection(const isc::dns::Message& msg,
+                      const isc::dns::Message::Section& section,
+                      uint32_t& smaller_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
+    ///        with higher trust level.
+    ///
+    /// \param message Message that the rrset belongs to
+    /// \param rrset specified rrset which needs to get its
+    ///        trust worthiness
+    /// \param section Section of the rrset
+    /// \return return rrset trust level.
+    RRsetTrustLevel getRRsetTrustLevel(const isc::dns::Message& message,
+        const isc::dns::RRsetPtr& rrset,
+        const isc::dns::Message::Section& section);
+
+    /// \brief Add rrset to one section of message.
+    ///
+    /// \param message The message to add rrsets to.
+    /// \param rrset_entry_vec vector for rrset entries in
+    ///        different sections.
+    /// \param section The section to add to
+    /// \param dnssec_need need dnssec records or not.
+    void addRRset(isc::dns::Message& message,
+                  const std::vector<RRsetEntryPtr>& rrset_entry_vec,
+                  const isc::dns::Message::Section& section,
+                  bool dnssec_need);
+
+    /// \brief Get the all the rrset entries for the message entry.
+    ///
+    /// \param rrset_entry_vec vector to add unexpired rrset entries to
+    /// \param time_now the time of now. Used to compare with rrset
+    ///        entry's expire time.
+    /// \return return false if any rrset entry has expired, true
+    ///         otherwise.
+    bool getRRsetEntries(std::vector<RRsetEntryPtr>& rrset_entry_vec,
+                         const time_t time_now);
+    //@}
+protected:
+    /// \note Make the variable be protected for easy test.
+    time_t expire_time_;  // Expiration time of the message.
+
+private:
+    std::string entry_name_; // The name for this entry(name + type)
+    HashKey* hash_key_ptr_;  // the key for messag entry in hash table.
+
+    std::vector<RRsetRef> rrsets_;
+    boost::shared_ptr<RRsetCache> rrset_cache_;
+
+    std::string query_name_; // query name of the message.
+    uint16_t query_class_; // query class of the message.
+    uint16_t query_type_; // query type of message.
+
+    uint16_t query_count_; // query count in query section.
+    uint16_t answer_count_; // rrset count in answer section.
+    uint16_t authority_count_; // rrset count in authority section.
+    uint16_t additional_count_; // rrset count in addition section.
+
+    //TODO, there should be a better way to cache these header flags
+    bool headerflag_aa_; // Whether AA bit is set.
+    bool headerflag_tc_; // Whether TC bit is set.
+};
+
+typedef boost::shared_ptr<MessageEntry> MessageEntryPtr;
+
+} // namespace cache
+} // namespace isc
+
+#endif // __MESSAGE_ENTRY_H
+
diff --git a/src/lib/cache/resolver_cache.cc b/src/lib/cache/resolver_cache.cc
new file mode 100644
index 0000000..aa681cc
--- /dev/null
+++ b/src/lib/cache/resolver_cache.cc
@@ -0,0 +1,249 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <config.h>
+
+#include "resolver_cache.h"
+#include "dns/message.h"
+#include "rrset_cache.h"
+#include <string>
+#include <algorithm>
+
+using namespace isc::dns;
+using namespace std;
+
+namespace isc {
+namespace cache {
+
+ResolverClassCache::ResolverClassCache(const RRClass& cache_class) :
+    cache_class_(cache_class)
+{
+    local_zone_data_ = LocalZoneDataPtr(new LocalZoneData(cache_class_.getCode()));
+    rrsets_cache_ = RRsetCachePtr(new RRsetCache(RRSET_CACHE_DEFAULT_SIZE,
+                                                 cache_class_.getCode()));
+    messages_cache_ = MessageCachePtr(new MessageCache(rrsets_cache_,
+                                      MESSAGE_CACHE_DEFAULT_SIZE,
+                                      cache_class_.getCode()));
+}
+
+ResolverClassCache::ResolverClassCache(CacheSizeInfo cache_info) :
+    cache_class_(cache_info.cclass)
+{
+    uint16_t klass = cache_class_.getCode();
+    // TODO We should find one way to load local zone data.
+    local_zone_data_ = LocalZoneDataPtr(new LocalZoneData(klass));
+    rrsets_cache_ = RRsetCachePtr(new
+                        RRsetCache(cache_info.rrset_cache_size, klass));
+    messages_cache_ = MessageCachePtr(new MessageCache(rrsets_cache_,
+                                      cache_info.message_cache_size,
+                                      klass));
+}
+
+const RRClass&
+ResolverClassCache::getClass() const {
+    return cache_class_;
+}
+
+bool
+ResolverClassCache::lookup(const isc::dns::Name& qname,
+                      const isc::dns::RRType& qtype,
+                      isc::dns::Message& response) const
+{
+    // message response should has question section already.
+    if (response.beginQuestion() == response.endQuestion()) {
+        isc_throw(MessageNoQuestionSection, "Message has no question section");
+    }
+
+    // First, query in local zone, if the rrset(qname, qtype, qclass) can be
+    // found in local zone, generated reply message with only the rrset in
+    // answer section.
+    RRsetPtr rrset_ptr = local_zone_data_->lookup(qname, qtype);
+    if (rrset_ptr) {
+        response.addRRset(Message::SECTION_ANSWER, rrset_ptr);
+        return (true);
+    }
+
+    // Search in class-specific message cache.
+    return (messages_cache_->lookup(qname, qtype, response));
+}
+
+isc::dns::RRsetPtr
+ResolverClassCache::lookup(const isc::dns::Name& qname,
+               const isc::dns::RRType& qtype) const
+{
+    // Algorithm:
+    // 1. Search in local zone data first,
+    // 2. Then do search in rrsets_cache_.
+    RRsetPtr rrset_ptr = local_zone_data_->lookup(qname, qtype);
+    if (rrset_ptr) {
+        return (rrset_ptr);
+    } else {
+        RRsetEntryPtr rrset_entry = rrsets_cache_->lookup(qname, qtype);
+        if (rrset_entry) {
+            return (rrset_entry->getRRset());
+        } else {
+            return (RRsetPtr());
+        }
+    }
+}
+
+bool
+ResolverClassCache::update(const isc::dns::Message& msg) {
+    return (messages_cache_->update(msg));
+}
+
+bool
+ResolverClassCache::updateRRsetCache(const isc::dns::ConstRRsetPtr rrset_ptr,
+                                RRsetCachePtr rrset_cache_ptr)
+{
+    RRsetTrustLevel level;
+    if (rrset_ptr->getType() == RRType::A() || 
+        rrset_ptr->getType() == RRType::AAAA()) {
+        level = RRSET_TRUST_PRIM_GLUE;
+    } else {
+        level = RRSET_TRUST_PRIM_ZONE_NONGLUE;
+    }
+
+    rrset_cache_ptr->update((*rrset_ptr.get()), level);
+    return (true);
+}
+
+bool
+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_);
+    return (true);
+}
+
+
+ResolverCache::ResolverCache()
+{
+    class_caches_.push_back(new ResolverClassCache(RRClass::IN()));
+}
+
+ResolverCache::ResolverCache(std::vector<CacheSizeInfo> caches_info)
+{
+    for (int i = 0; i < caches_info.size(); ++i) {
+        class_caches_.push_back(new ResolverClassCache(caches_info[i]));
+    }
+}
+
+ResolverCache::~ResolverCache()
+{
+    for (int i = 0; i < class_caches_.size(); ++i) {
+        delete class_caches_[i];
+    }
+}
+
+bool
+ResolverCache::lookup(const isc::dns::Name& qname,
+                      const isc::dns::RRType& qtype,
+                      const isc::dns::RRClass& qclass,
+                      isc::dns::Message& response) const
+{
+    ResolverClassCache* cc = getClassCache(qclass);
+    if (cc) {
+        return (cc->lookup(qname, qtype, response));
+    } else {
+        return (false);
+    }
+}
+
+isc::dns::RRsetPtr
+ResolverCache::lookup(const isc::dns::Name& qname,
+               const isc::dns::RRType& qtype,
+               const isc::dns::RRClass& qclass) const
+{
+    ResolverClassCache* cc = getClassCache(qclass);
+    if (cc) {
+        return (cc->lookup(qname, qtype));
+    } else {
+        return (RRsetPtr());
+    }
+}
+
+isc::dns::RRsetPtr
+ResolverCache::lookupClosestRRset(const isc::dns::Name& qname,
+                                  const isc::dns::RRType& qtype,
+                                  const isc::dns::RRClass& qclass) const
+{
+    ResolverClassCache* cc = getClassCache(qclass);
+    if (cc) {
+        unsigned int count = qname.getLabelCount();
+        unsigned int level = 0;
+        while(level < count) {
+            Name close_name = qname.split(level);
+            RRsetPtr rrset_ptr = cc->lookup(close_name, qtype);
+            if (rrset_ptr) {
+                return (rrset_ptr);
+            } else {
+                ++level;
+            }
+        }
+    }
+
+    return (RRsetPtr());
+}
+
+bool
+ResolverCache::update(const isc::dns::Message& msg) {
+    
+    QuestionIterator iter = msg.beginQuestion();
+    ResolverClassCache* cc = getClassCache((*iter)->getClass());
+    if (cc) {
+        return (cc->update(msg));
+    } else {
+        return (false);
+    }
+}
+
+bool
+ResolverCache::update(const isc::dns::ConstRRsetPtr rrset_ptr) {
+    ResolverClassCache* cc = getClassCache(rrset_ptr->getClass());
+    if (cc) {
+        return (cc->update(rrset_ptr));
+    } else {
+        return (false);
+    }
+}
+
+void
+ResolverCache::dump(const std::string&) {
+    //TODO
+}
+
+void
+ResolverCache::load(const std::string&) {
+    //TODO
+}
+
+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 NULL;
+}
+
+
+
+
+} // namespace cache
+} // namespace isc
+
diff --git a/src/lib/cache/resolver_cache.h b/src/lib/cache/resolver_cache.h
new file mode 100644
index 0000000..7a0fdab
--- /dev/null
+++ b/src/lib/cache/resolver_cache.h
@@ -0,0 +1,331 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#ifndef __RESOLVER_CACHE_H
+#define __RESOLVER_CACHE_H
+
+#include <map>
+#include <string>
+#include <boost/shared_ptr.hpp>
+#include <dns/rrclass.h>
+#include <dns/message.h>
+#include <exceptions/exceptions.h>
+#include "message_cache.h"
+#include "rrset_cache.h"
+#include "local_zone_data.h"
+
+namespace isc {
+namespace cache {
+class RRsetCache;
+
+//TODO a better proper default cache size
+#define MESSAGE_CACHE_DEFAULT_SIZE 10000
+#define RRSET_CACHE_DEFAULT_SIZE   20000
+
+/// \brief Cache Size Information.
+///
+/// Used to initialize the size of class-specific rrset/message cache.
+struct CacheSizeInfo
+{
+public:
+    /// \brief Constructor
+    ///
+    /// \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, 
+                  uint32_t msg_cache_size,
+                  uint32_t rst_cache_size):
+                    cclass(cls),
+                    message_cache_size(msg_cache_size),
+                    rrset_cache_size(rst_cache_size)
+    {}
+
+    isc::dns::RRClass cclass; // class of the cache.
+    uint32_t message_cache_size; // the size for message cache.
+    uint32_t rrset_cache_size; // The size for rrset cache.
+};
+
+/// \brief  Message has no question section.
+///
+/// Thrown if the given message has no question section when looking up
+/// the message in cache.
+class MessageNoQuestionSection : public isc::Exception {
+public:
+    MessageNoQuestionSection(const char*file, size_t line, const char*what) :
+        isc::Exception(file, line, what)
+    {}
+};
+
+/// \brief Class-specific Resolver Cache.
+///
+/// The object of ResolverCache represents the cache of the resolver. It may hold
+/// a list of message/rrset cache which are in different class.
+///
+/// \note Public interaction with the cache should be through ResolverCache,
+/// not directly with this one. (TODO: make this private/hidden/local to the .cc?)
+class ResolverClassCache {
+public:
+    /// \brief Default Constructor.
+    ///
+    /// Only support for class "IN", and message cache size is
+    /// MESSAGE_CACHE_DEFAULT_SIZE, rrset cache size is
+    /// RRSET_CACHE_DEFAULT_SIZE
+    ResolverClassCache(const isc::dns::RRClass& cache_class);
+
+    /// \brief Construct Function.
+    /// \param caches_size cache size information for each
+    ///        messages/rrsets of different classes.
+    ResolverClassCache(CacheSizeInfo cache_info);
+
+    /// \name Lookup Interfaces
+    //@{
+    /// \brief Look up message in cache.
+    ///
+    /// \param qname The query name to look up
+    /// \param qtype The query type to look up
+    /// \param response the query message (must be in RENDER mode)
+    ///        which has question section already (exception
+    ///        MessageNoQeustionSection will be thrown if it has
+    ///        no question section). If the message can be found
+    ///        in cache, rrsets for the message will be added to
+    ///        different sections(answer, authority, additional).
+    /// \return return true if the message can be found, or else,
+    ///         return false.
+    bool lookup(const isc::dns::Name& qname,
+                const isc::dns::RRType& qtype,
+                isc::dns::Message& response) const;
+
+    /// \brief Look up rrset in cache.
+    ///
+    /// \param qname The query name to look up
+    /// \param qtype The query type to look up
+    ///
+    /// \return return the shared_ptr of rrset if it can be found,
+    ///         or else, return NULL. When looking up, local zone
+    ///         data will be searched first, if not found, then
+    ///         search in rrset cache.
+    ///
+    /// \overload
+    ///
+    isc::dns::RRsetPtr lookup(const isc::dns::Name& qname,
+                              const isc::dns::RRType& qtype) const;
+
+    /// \brief Update the message in the cache with the new one.
+    ///
+    /// \param msg The message to update
+    ///
+    /// \return return true if the message is updated successfully,
+    ///         or else, return false.
+    ///
+    /// \note the function doesn't do any message validation check,
+    ///       the user should make sure the message is valid, and of
+    ///       the right class
+    bool update(const isc::dns::Message& msg);
+
+    /// \brief Update the rrset in the cache with the new one.
+    ///
+    /// local zone data and rrset cache will be updated together.
+    /// If the rrset doesn't exist in both of them, then the rrset
+    /// will be added into both of them.
+    ///
+    /// \param rrset_ptr The RRset to update
+    ///
+    /// \return return false, if the class of the parameter rrset is
+    ///        allowed to be cached.
+    ///
+    /// \overload
+    ///
+    /// \note The class of the RRset must have been checked. It is not
+    /// here.
+    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.
+    ///
+    /// \param rrset_ptr The rrset to update with
+    /// \param rrset_cache_ptr the rrset cache to update
+    ///
+    /// \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,
+                          RRsetCachePtr rrset_cache_ptr);
+
+    /// \brief Class this cache is for.
+    const isc::dns::RRClass cache_class_;
+
+    /// \brief map of message caches for configured classes(each message
+    /// cache is class-specific)
+    MessageCachePtr messages_cache_;
+
+    /// \name rrset caches
+    //@{
+    /// \brief Local Zone data cache
+    /// 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_;
+    //@}
+};
+
+class ResolverCache {
+public:
+    /// \brief Default Constructor.
+    ///
+    /// Right now, only support for class "IN", and message cache size is
+    /// MESSAGE_CACHE_DEFAULT_SIZE, rrset cache size is
+    /// RRSET_CACHE_DEFAULT_SIZE
+    ResolverCache();
+
+    /// \brief Construct Function.
+    /// \param caches_size cache size information for each
+    ///        messages/rrsets of different classes.
+    ResolverCache(std::vector<CacheSizeInfo> caches_size);
+
+    /// \brief Destructor
+    ~ResolverCache();
+
+    /// \name Lookup Interfaces
+    //@{
+    /// \brief Look up message in cache.
+    ///
+    /// \param qname The query name to look up
+    /// \param qtype The query type to look up
+    /// \param qclass The query class to look up
+    /// \param response the query message (must be in RENDER mode)
+    ///        which has question section already (exception
+    ///        MessageNoQeustionSection will be thrown if it has
+    ///        no question section). If the message can be found
+    ///        in cache, rrsets for the message will be added to
+    ///        different sections(answer, authority, additional).
+    /// \return return true if the message can be found, or else,
+    ///         return false.
+    bool lookup(const isc::dns::Name& qname,
+                const isc::dns::RRType& qtype,
+                const isc::dns::RRClass& qclass,
+                isc::dns::Message& response) const;
+
+    /// \brief Look up rrset in cache.
+    ///
+    /// \param qname The query name to look up
+    /// \param qtype The query type to look up
+    /// \param qclass The query class to look up
+    ///
+    /// \return return the shared_ptr of rrset if it can be found,
+    ///         or else, return NULL. When looking up, local zone
+    ///         data will be searched first, if not found, then
+    ///         search in rrset cache.
+    ///
+    /// \overload
+    ///
+    isc::dns::RRsetPtr lookup(const isc::dns::Name& qname,
+                              const isc::dns::RRType& qtype,
+                              const isc::dns::RRClass& qclass) const;
+
+    /// \brief Look up closest rrset in cache.
+    ///
+    /// \param qname The query name to look up
+    /// \param qtype The query type to look up
+    /// \param qclass The query class to look up
+    ///
+    /// \return return the shared_ptr of rrset if it can be found in
+    ///         cache, or else return NULL.
+    ///
+    /// Currently the implementation is: search exact rrset
+    /// label by lable, If the rrset can't be found, remove the last
+    /// label, then search again. The efficiency may be very low when
+    /// the name of rrset is very long but it's closest rrset's name
+    /// is very short.
+    /// If a good perfermance is needed when looking up the closest rrset,
+    /// rrset cache structure(HashTable) should be redesigned. By using
+    /// HashTable, it can only garantee the performance for looking
+    /// up exact rrset.
+    /// So here there is another question, which rrset looking up interface
+    /// is used frequently? Exact or closest looking up.
+    isc::dns::RRsetPtr lookupClosestRRset(const isc::dns::Name& qname,
+                              const isc::dns::RRType& qtype,
+                              const isc::dns::RRClass& qclass) const;
+    //@}
+
+    /// \brief Update the message in the cache with the new one.
+    ///
+    /// \param msg The message to update
+    ///
+    /// \return return true if the message is updated successfully,
+    ///         or else, return false.
+    ///
+    /// \note the function doesn't do any message validation check,
+    ///       the user should make sure the message is valid.
+    bool update(const isc::dns::Message& msg);
+
+    /// \brief Update the rrset in the cache with the new one.
+    ///
+    /// local zone data and rrset cache will be updated together.
+    /// If the rrset doesn't exist in both of them, then the rrset
+    /// will be added into both of them.
+    ///
+    /// \param rrset_ptr The RRset to update
+    ///
+    /// \return return false, if the class of the parameter rrset is
+    ///        allowed to be cached.
+    ///
+    /// \overload
+    ///
+    bool update(const isc::dns::ConstRRsetPtr rrset_ptr);
+
+    /// \name Cache Serialization
+    //@{
+    /// \brief Dump the cache content to one file.
+    ///
+    /// \param file_name file to write to
+    ///
+    /// \todo It should can be dumped to one configured database.
+    void dump(const std::string& file_name);
+
+    /// \brief Load the cache from one file.
+    ///
+    /// \param file to load from
+    ///
+    /// \todo It should can be loaded from one configured database.
+    void load(const std::string& file_name);
+    //@}
+
+private:
+    /// \brief Returns the class-specific subcache
+    ///
+    /// \param cache_class the class to get the subcache for
+    /// \return The subcache, or NULL if there is no cache for this class
+    ResolverClassCache* getClassCache(const isc::dns::RRClass& cache_class) const;
+
+    /// The class-specific caches.
+    /// TODO: I think we can optimize for IN, and always have that
+    /// one directly available, use the vector for the rest?
+    std::vector<ResolverClassCache*> class_caches_;
+};
+
+} // namespace cache
+} // namespace isc
+
+#endif // __RESOLVER_CACHE_H
+
diff --git a/src/lib/cache/rrset_cache.cc b/src/lib/cache/rrset_cache.cc
new file mode 100644
index 0000000..02a6a45
--- /dev/null
+++ b/src/lib/cache/rrset_cache.cc
@@ -0,0 +1,107 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <config.h>
+
+#include <string>
+#include "rrset_cache.h"
+#include <nsas/nsas_entry_compare.h>
+#include <nsas/hash_table.h>
+#include <nsas/hash_deleter.h>
+
+using namespace isc::nsas;
+using namespace isc::dns;
+using namespace std;
+
+namespace isc {
+namespace cache {
+
+RRsetCache::RRsetCache(uint32_t cache_size,
+                       uint16_t rrset_class):
+    class_(rrset_class),
+    rrset_table_(new NsasEntryCompare<RRsetEntry>, cache_size),
+    rrset_lru_((3 * cache_size),
+                  new HashDeleter<RRsetEntry>(rrset_table_))
+{
+}
+
+RRsetEntryPtr
+RRsetCache::lookup(const isc::dns::Name& qname,
+                   const isc::dns::RRType& qtype)
+{
+    const string entry_name = genCacheEntryName(qname, qtype);
+    RRsetEntryPtr entry_ptr = rrset_table_.get(HashKey(entry_name, RRClass(class_)));
+
+    //If the rrset entry has expired, return NULL.
+    if(entry_ptr && (time(NULL) > entry_ptr->getExpireTime())) {
+        return (RRsetEntryPtr());
+    }
+    return (entry_ptr);
+}
+
+RRsetEntryPtr
+RRsetCache::update(const isc::dns::RRset& rrset, const RRsetTrustLevel& level) {
+    // TODO: If the RRset is an NS, we should update the NSAS as well
+    
+    // lookup first
+    RRsetEntryPtr entry_ptr = lookup(rrset.getName(), rrset.getType());
+    if(!entry_ptr) {
+        // rrset entry doesn't exist, create one rrset entry for the rrset
+        // and add it directly.
+        entry_ptr.reset(new RRsetEntry(rrset, level));
+        // Replace the expired rrset entry if it exists.
+        rrset_table_.add(entry_ptr, entry_ptr->hashKey(), true);
+        //TODO , lru list touch.
+        return (entry_ptr);
+    } else {
+        // there is one rrset entry in the cache, need to check whether
+        // the new rrset is more authoritative.
+        if (entry_ptr->getTrustLevel() > level) {
+            // existed rrset entry is more authoritative, do nothing,
+            // just return it.
+            //TODO, lru list touch
+            return (entry_ptr);
+        } else {
+            HashKey key = entry_ptr->hashKey();
+            rrset_table_.remove(key);
+            entry_ptr.reset(new RRsetEntry(rrset, level));
+            //TODO, lru list touch.
+            // Replace the expired rrset entry if it exists.
+            rrset_table_.add(entry_ptr, key, true);
+            return (entry_ptr);
+        }
+    }
+}
+
+void
+RRsetCache::dump(const std::string&) {
+    //TODO
+}
+
+void
+RRsetCache::load(const std::string&) {
+    //TODO
+}
+
+bool
+RRsetCache::resize(uint32_t) {
+    //TODO
+    return (true);
+}
+
+} // namespace cache
+} // namespace isc
+
diff --git a/src/lib/cache/rrset_cache.h b/src/lib/cache/rrset_cache.h
new file mode 100644
index 0000000..d082b3c
--- /dev/null
+++ b/src/lib/cache/rrset_cache.h
@@ -0,0 +1,108 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#ifndef __RRSET_CACHE_H
+#define __RRSET_CACHE_H
+
+#include <rrset_entry.h>
+#include <nsas/hash_table.h>
+#include <nsas/lru_list.h>
+
+using namespace isc::nsas;
+
+namespace isc {
+namespace cache {
+
+class RRsetEntry;
+
+/// \brief RRset Cache
+/// The object of RRsetCache represented the cache for class-specific
+/// RRsets.
+class RRsetCache{
+    ///
+    /// \name Constructors and Destructor
+    ///
+    /// Note: The copy constructor and the assignment operator are intentionally
+    /// defined as private to make it uncopyable
+    //@{
+private:
+    RRsetCache(const RRsetCache&);
+    RRsetCache& operator=(const RRsetCache&);
+public:
+    /// \brief Constructor
+    ///
+    /// \param cache_size the size of rrset cache.
+    /// \param rrset_class the class of rrset cache.
+    RRsetCache(uint32_t cache_size, uint16_t rrset_class);
+    ~RRsetCache() {}
+    //@}
+
+    /// \brief Look up rrset in cache.
+    ///
+    /// \param qname The query name to look up
+    /// \param qtype The query type 
+    /// \return return the shared_ptr of rrset entry if it can be
+    /// found in the cache, or else, return NULL.
+    RRsetEntryPtr lookup(const isc::dns::Name& qname,
+                         const isc::dns::RRType& qtype);
+
+    /// \brief Update RRset Cache
+    /// Update the rrset entry in the cache with the new one.
+    /// If the rrset has expired or doesn't exist in the cache,
+    /// it will be added directly. It may be ingored if the new
+    /// rrset is not more authoritative than the old rrset in cache.
+    ///
+    /// \param rrset The new rrset used to update cache.
+    /// \param level trustworthiness of the rrset.
+    /// \return return the rrset entry in the cache, it may be the
+    /// new added rrset entry or existed one if it is not replaced.
+    RRsetEntryPtr update(const isc::dns::RRset& rrset,
+                         const RRsetTrustLevel& level);
+
+    /// \brief Dump the rrset cache to specified file.
+    ///
+    /// \param file_name The file to write to
+    ///
+    /// \todo It should can be dumped to one configured database.
+    void dump(const std::string& file_name);
+
+    /// \brief Load the cache from one file.
+    ///
+    /// \param file_name The file to read from
+    ///
+    /// \todo It should can be loaded from one configured database.
+    void load(const std::string& file_name);
+
+    /// \brief Resize the size of rrset cache in runtime.
+    ///
+    /// \param The size to resize to
+    /// \return true
+    bool resize(uint32_t size);
+
+private:
+    uint16_t class_; // The class of the rrset cache.
+    isc::nsas::HashTable<RRsetEntry> rrset_table_;
+    isc::nsas::LruList<RRsetEntry> rrset_lru_;
+};
+
+typedef boost::shared_ptr<RRsetCache> RRsetCachePtr;
+typedef boost::shared_ptr<const RRsetCache> ConstRRsetCachePtr;
+
+} // namespace cache
+} // namespace isc
+
+#endif // __RRSET_CACHE_H
+
diff --git a/src/lib/cache/rrset_copy.cc b/src/lib/cache/rrset_copy.cc
new file mode 100644
index 0000000..85ba153
--- /dev/null
+++ b/src/lib/cache/rrset_copy.cc
@@ -0,0 +1,40 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include "rrset_copy.h"
+
+using namespace isc::dns;
+
+namespace isc {
+namespace cache {
+
+void
+rrsetCopy(const isc::dns::RRset& src, isc::dns::RRset& dst) {
+    RdataIteratorPtr rdata_itor = src.getRdataIterator();
+    rdata_itor->first();
+    while(!rdata_itor->isLast()){
+        dst.addRdata(rdata_itor->getCurrent());
+        rdata_itor->next();
+    }
+
+    RRsetPtr rrsig = src.getRRsig();
+    if (rrsig != NULL){
+        dst.addRRsig(rrsig);
+    }
+}
+
+} // namespace cache
+} // namespace isc
diff --git a/src/lib/cache/rrset_copy.h b/src/lib/cache/rrset_copy.h
new file mode 100644
index 0000000..f6bee55
--- /dev/null
+++ b/src/lib/cache/rrset_copy.h
@@ -0,0 +1,44 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#ifndef __RRSET_COPY_
+#define __RRSET_COPY_
+
+#include <dns/rrset.h>
+
+namespace isc {
+namespace cache {
+
+/// \brief RRset Copy Function
+///
+/// Adds all Rdatas and the RRsig in the source RRset to the target
+/// RRset
+///
+/// \param src RRset to copy from
+/// \param dst RRset to copy to
+///
+/// \note RRset class doesn't provide the interface for
+///       doing RRset copy. But in cache's code, sometime
+///       we have to do the copy.
+
+void
+rrsetCopy(const isc::dns::RRset& src, isc::dns::RRset& dst);
+
+} // namespace cache
+} // namespace isc
+
+#endif // __RRSET_COPY_
+
diff --git a/src/lib/cache/rrset_entry.cc b/src/lib/cache/rrset_entry.cc
new file mode 100644
index 0000000..9407e97
--- /dev/null
+++ b/src/lib/cache/rrset_entry.cc
@@ -0,0 +1,68 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <config.h>
+
+#include <dns/message.h>
+#include <nsas/nsas_entry.h>
+#include <nsas/fetchable.h>
+#include "rrset_entry.h"
+#include "rrset_copy.h"
+
+using namespace isc::dns;
+
+namespace isc {
+namespace cache {
+
+RRsetEntry::RRsetEntry(const isc::dns::RRset& rrset, const RRsetTrustLevel& level):
+    entry_name_(genCacheEntryName(rrset.getName(), rrset.getType())),
+    expire_time_(time(NULL) + rrset.getTTL().getValue()),
+    trust_level_(level),
+    rrset_(new RRset(rrset.getName(), rrset.getClass(), rrset.getType(), rrset.getTTL())),
+    hash_key_(HashKey(entry_name_, rrset_->getClass()))
+{
+    rrsetCopy(rrset, *(rrset_.get()));
+}
+
+isc::dns::RRsetPtr
+RRsetEntry::getRRset() {
+    updateTTL();
+    return (rrset_);
+}
+
+time_t
+RRsetEntry::getExpireTime() const {
+    return (expire_time_);
+}
+
+void
+RRsetEntry::updateTTL(){
+    uint32_t oldTTL = rrset_->getTTL().getValue();
+    if(oldTTL == 0) {
+        return;
+    }
+
+    uint32_t now = time(NULL);
+    uint32_t newTTL = now < expire_time_ ? (expire_time_ - now) : 0;
+
+    RRTTL ttl(newTTL);
+    rrset_->setTTL(ttl);
+}
+
+} // namespace cache
+} // namespace isc
+
+
diff --git a/src/lib/cache/rrset_entry.h b/src/lib/cache/rrset_entry.h
new file mode 100644
index 0000000..f0149a4
--- /dev/null
+++ b/src/lib/cache/rrset_entry.h
@@ -0,0 +1,137 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#ifndef __RRSET_ENTRY_H
+#define __RRSET_ENTRY_H
+
+#include <dns/rrset.h>
+#include <dns/message.h>
+#include <dns/rrttl.h>
+#include <nsas/nsas_entry.h>
+#include <nsas/fetchable.h>
+#include "cache_entry_key.h"
+
+using namespace isc::nsas;
+
+namespace isc {
+namespace cache {
+
+/// \enum RRset Trustworthiness
+/// For detail of RRset trustworthiness, please refer to
+/// RFC2181 section5.4.1.
+/// Bigger value is more trustworthy.
+enum RRsetTrustLevel {
+    /// Default trust for RRset.
+    RRSET_TRUST_DEFAULT = 0,
+    /// Additional information from non-authoritative answer.
+    RRSET_TRUST_ADDITIONAL_NONAA,
+    /// Data from the authority section of a non-authoritative answer
+    RRSET_TRUST_AUTHORITY_NONAA,
+    /// Additional information from an authoritative answer.
+    RRSET_TRUST_ADDITIONAL_AA,
+    /// Non-authoritative data from the answer section of authoritative
+    /// answers
+    RRSET_TRUST_NONAUTH_ANSWER_AA,
+    /// Data from the answer section of a non-authoritative answer.
+    RRSET_TRUST_ANSWER_NONAA,
+    /// Glue from a primary zone, or glue from a zone transfer.
+    RRSET_TRUST_PRIM_GLUE,
+    /// Data from the authority section of an authoritative answer.
+    RRSET_TRUST_AUTHORITY_AA,
+    /// Authoritative data included in the answer section of
+    /// an authoritative reply.
+    RRSET_TRUST_ANSWER_AA,
+    /// Data from a primary zone file, other than glue data.
+    RRSET_TRUST_PRIM_ZONE_NONGLUE
+};
+
+/// \brief RRset Entry
+/// The object of RRsetEntry represents one cached RRset.
+/// Each RRset entry may be refered using shared_ptr by several message
+/// entries.
+class RRsetEntry : public NsasEntry<RRsetEntry>
+{
+    ///
+    /// \name Constructors and Destructor
+    ///
+    /// Note: The copy constructor and the assignment operator are intentionally
+    /// defined as private to make it uncopyable
+    //@{
+private:
+    RRsetEntry(const RRsetEntry&);
+    RRsetEntry& operator=(const RRsetEntry&);
+public:
+    /// \brief Constructor
+    /// \param rrset The RRset used to initialize the RRset entry.
+    /// \param level trustworthiness of the RRset.
+    RRsetEntry(const isc::dns::RRset& rrset, const RRsetTrustLevel& level);
+
+    /// The destructor.
+    ~RRsetEntry() {}
+    //@}
+
+    /// \brief Return a pointer to a generated RRset
+    ///
+    /// \return Pointer to the generated RRset
+    isc::dns::RRsetPtr getRRset();
+
+    /// \brief Get the expiration time of the RRset.
+    ///
+    /// \return The expiration time of the RRset
+    ///
+    /// \todo RRsig expiration processing
+    time_t getExpireTime() const;
+
+    /// \brief Get the ttl of the RRset.
+    ///
+    /// \return The TTL of the RRset
+    uint32_t getTTL() {
+        updateTTL();
+        return (rrset_->getTTL().getValue());
+    }
+
+    /// \brief Get the hash key
+    ///
+    /// \return return hash key
+    HashKey hashKey() const {
+        return (hash_key_);
+    }
+
+    /// \brief get RRset trustworthiness
+    ///
+    /// \return return the trust level
+    RRsetTrustLevel getTrustLevel() const {
+        return (trust_level_);
+    }
+private:
+    /// \brief Update TTL according to expiration time
+    void updateTTL();
+
+private:
+    std::string entry_name_; // The entry name for this rrset entry.
+    time_t expire_time_;     // Expiration time of rrset.
+    RRsetTrustLevel trust_level_; // RRset trustworthiness.
+    boost::shared_ptr<isc::dns::RRset> rrset_;
+    HashKey hash_key_;       // RRsetEntry hash key
+};
+
+typedef boost::shared_ptr<RRsetEntry> RRsetEntryPtr;
+
+} // namespace cache
+} // namespace isc
+
+#endif // __RRSET_ENTRY_H
+
diff --git a/src/lib/cache/tests/Makefile.am b/src/lib/cache/tests/Makefile.am
new file mode 100644
index 0000000..9bc3e3a
--- /dev/null
+++ b/src/lib/cache/tests/Makefile.am
@@ -0,0 +1,57 @@
+SUBDIRS = .
+
+AM_CPPFLAGS  = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES) $(MULTITHREADING_FLAG)
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/cache -I$(top_builddir)/src/lib/cache
+AM_CPPFLAGS += -DTEST_DATA_SRCDIR=\"$(srcdir)/testdata\"
+AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/lib/cache/tests/testdata\"
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+
+AM_LDFLAGS = $(PTHREAD_LDFLAGS)
+if USE_STATIC_LINK
+AM_LDFLAGS += -static
+endif
+
+# Some versions of GCC warn about some versions of Boost regarding
+# missing initializer for members in its posix_time.
+# https://svn.boost.org/trac/boost/ticket/3477
+# But older GCC compilers don't have the flag.
+AM_CXXFLAGS += $(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
+
+if USE_CLANGPP
+# see ../Makefile.am
+AM_CXXFLAGS += -Wno-unused-parameter
+endif
+
+CLEANFILES = *.gcno *.gcda
+
+TESTS =
+if HAVE_GTEST
+TESTS += run_unittests
+run_unittests_SOURCES  = run_unittests.cc
+run_unittests_SOURCES  += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
+run_unittests_SOURCES  += rrset_entry_unittest.cc
+run_unittests_SOURCES  += rrset_cache_unittest.cc
+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_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+run_unittests_LDADD = $(GTEST_LDADD)
+
+# NOTE: we may have to clean up this hack later (see the note in configure.ac)
+if NEED_LIBBOOST_THREAD
+run_unittests_LDADD += -lboost_thread
+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/exceptions/libexceptions.la
+endif
+
+noinst_PROGRAMS = $(TESTS)
diff --git a/src/lib/cache/tests/cache_test_messagefromfile.h b/src/lib/cache/tests/cache_test_messagefromfile.h
new file mode 100644
index 0000000..820d822
--- /dev/null
+++ b/src/lib/cache/tests/cache_test_messagefromfile.h
@@ -0,0 +1,40 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+#include <vector>
+#include <dns/tests/unittest_util.h>
+#include <dns/buffer.h>
+#include <dns/message.h>
+
+using namespace isc;
+using namespace isc::dns;
+
+namespace {
+
+/// \brief Reads a Message from a data file
+///
+/// \param message Message to put the read data in
+/// \param datafile The file to read from
+void
+messageFromFile(Message& message, const char* datafile) {
+    std::vector<unsigned char> data;
+    UnitTestUtil::readWireData(datafile, data);
+
+    InputBuffer buffer(&data[0], data.size());
+    message.fromWire(buffer);
+}
+
+}   // namespace
+
diff --git a/src/lib/cache/tests/cache_test_sectioncount.h b/src/lib/cache/tests/cache_test_sectioncount.h
new file mode 100644
index 0000000..9583805
--- /dev/null
+++ b/src/lib/cache/tests/cache_test_sectioncount.h
@@ -0,0 +1,45 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+#include <vector>
+#include <dns/tests/unittest_util.h>
+#include <dns/buffer.h>
+#include <dns/message.h>
+
+using namespace isc;
+using namespace isc::dns;
+
+namespace {
+
+/// \brief Counts the number of rrsets in the given section
+///
+/// \param msg The message to count in
+/// \param section The section to count
+///
+/// \return The number of RRsets in the given section
+int
+sectionRRsetCount(Message& msg, Message::Section section) {
+    int count = 0;
+    for (RRsetIterator rrset_iter = msg.beginSection(section);
+         rrset_iter != msg.endSection(section); 
+         ++rrset_iter) {
+        ++count;
+    }
+
+    return count;
+}
+
+}   // namespace
+
diff --git a/src/lib/cache/tests/local_zone_data_unittest.cc b/src/lib/cache/tests/local_zone_data_unittest.cc
new file mode 100644
index 0000000..28de4be
--- /dev/null
+++ b/src/lib/cache/tests/local_zone_data_unittest.cc
@@ -0,0 +1,65 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+#include <config.h>
+#include <string>
+#include <gtest/gtest.h>
+#include <cache/local_zone_data.h>
+#include <dns/rrset.h>
+#include <dns/rrttl.h>
+#include "cache_test_messagefromfile.h"
+
+using namespace isc::cache;
+using namespace isc::dns;
+using namespace std;
+
+namespace {
+
+class LocalZoneDataTest: public testing::Test {
+protected:
+    LocalZoneDataTest(): local_zone_data(1) 
+    {
+    }
+
+    LocalZoneData local_zone_data;
+};
+
+TEST_F(LocalZoneDataTest, updateAndLookup) {
+    Message msg(Message::PARSE);
+    messageFromFile(msg, "message_fromWire3");
+    RRsetIterator rrset_iter = msg.beginSection(Message::SECTION_AUTHORITY);
+    Name name = (*rrset_iter)->getName();
+    RRType type = (*rrset_iter)->getType();
+
+    EXPECT_FALSE(local_zone_data.lookup(name, type));
+    local_zone_data.update((*(*rrset_iter).get()));
+    EXPECT_TRUE(local_zone_data.lookup(name, type));
+
+    // Test whether the old one is replaced
+    uint32_t ttl = (*rrset_iter)->getTTL().getValue();
+    // Make sure it is not zero
+    ASSERT_NE(ttl / 2, ttl);
+    
+    RRsetPtr rrset_ptr = local_zone_data.lookup(name, type);
+    EXPECT_EQ(ttl, rrset_ptr->getTTL().getValue());
+
+    (*rrset_iter)->setTTL(RRTTL(ttl/2));
+
+    local_zone_data.update((*(*rrset_iter).get()));
+    rrset_ptr = local_zone_data.lookup(name, type);
+    EXPECT_EQ(ttl/2, rrset_ptr->getTTL().getValue());
+}
+
+}
diff --git a/src/lib/cache/tests/message_cache_unittest.cc b/src/lib/cache/tests/message_cache_unittest.cc
new file mode 100644
index 0000000..f984312
--- /dev/null
+++ b/src/lib/cache/tests/message_cache_unittest.cc
@@ -0,0 +1,98 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+#include <config.h>
+#include <string>
+#include <gtest/gtest.h>
+#include <dns/tests/unittest_util.h>
+#include <dns/buffer.h>
+#include "../message_cache.h"
+#include "../rrset_cache.h"
+#include "../resolver_cache.h"
+#include "cache_test_messagefromfile.h"
+
+using namespace isc::cache;
+using namespace isc;
+using namespace isc::dns;
+using namespace std;
+
+namespace {
+
+/// \brief Derived from base class to make it easy to test
+/// 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)
+    {}
+
+    uint16_t messages_count() {
+        return message_lru_.size();
+    }
+};
+
+class MessageCacheTest: public testing::Test {
+public:
+    MessageCacheTest(): message_parse(Message::PARSE),
+                        message_render(Message::RENDER)
+    {
+        uint16_t class_ = RRClass::IN().getCode();
+        rrset_cache_.reset(new RRsetCache(RRSET_CACHE_DEFAULT_SIZE, class_));
+        message_cache_.reset(new DerivedMessageCache(rrset_cache_, 
+                                          MESSAGE_CACHE_DEFAULT_SIZE, class_ ));
+    }
+
+protected:
+    boost::shared_ptr<DerivedMessageCache> message_cache_;
+    RRsetCachePtr rrset_cache_;
+    Message message_parse;
+    Message message_render;
+};
+
+TEST_F(MessageCacheTest, testLookup) {
+    messageFromFile(message_parse, "message_fromWire1");
+    EXPECT_TRUE(message_cache_->update(message_parse));
+    Name qname("test.example.com.");
+    EXPECT_TRUE(message_cache_->lookup(qname, RRType::A(), message_render));
+    EXPECT_EQ(message_cache_->messages_count(), 1);
+
+    Message message_net(Message::PARSE);
+    messageFromFile(message_net, "message_fromWire2");
+    EXPECT_TRUE(message_cache_->update(message_net));
+    EXPECT_EQ(message_cache_->messages_count(), 2);
+
+    Name qname1("test.example.net.");
+    EXPECT_TRUE(message_cache_->lookup(qname1, RRType::A(), message_render));
+}
+
+TEST_F(MessageCacheTest, testUpdate) {
+    messageFromFile(message_parse, "message_fromWire4");
+    EXPECT_TRUE(message_cache_->update(message_parse));
+
+    Name qname("example.com.");
+    EXPECT_TRUE(message_cache_->lookup(qname, RRType::SOA(), message_render));
+    EXPECT_FALSE(message_render.getHeaderFlag(Message::HEADERFLAG_AA));
+
+    Message new_msg(Message::PARSE);
+    messageFromFile(new_msg, "message_fromWire3");
+    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));
+}
+
+}   // namespace
+
diff --git a/src/lib/cache/tests/message_entry_unittest.cc b/src/lib/cache/tests/message_entry_unittest.cc
new file mode 100644
index 0000000..f0fc777
--- /dev/null
+++ b/src/lib/cache/tests/message_entry_unittest.cc
@@ -0,0 +1,237 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+#include <config.h>
+#include <string>
+#include <gtest/gtest.h>
+#include <dns/tests/unittest_util.h>
+#include <dns/message.h>
+#include <dns/buffer.h>
+#include "../message_entry.h"
+#include "../rrset_cache.h"
+#include "../resolver_cache.h"
+#include "cache_test_messagefromfile.h"
+#include "cache_test_sectioncount.h"
+
+using namespace isc::cache;
+using namespace isc;
+using namespace isc::dns;
+using namespace std;
+
+static uint32_t MAX_UINT32 = numeric_limits<uint32_t>::max();    
+
+namespace {
+
+/// \brief Derived from base class to make it easy to test
+/// its internals.
+class DerivedMessageEntry: public MessageEntry {
+public:
+    DerivedMessageEntry(const isc::dns::Message& message,
+                        boost::shared_ptr<RRsetCache> rrset_cache_):
+             MessageEntry(message, rrset_cache_)
+    {}
+
+    /// \brief Wrap the protected function so that it can be tested.   
+    void parseSectionForTest(const Message& msg,
+                           const Message::Section& section,
+                           uint32_t& smaller_ttl, 
+                           uint16_t& rrset_count)
+    {
+        parseSection(msg, section, smaller_ttl, rrset_count);
+    }
+
+    RRsetTrustLevel getRRsetTrustLevelForTest(const Message& message,
+                                              const RRsetPtr rrset,
+                                              const Message::Section& section) 
+    {
+        return getRRsetTrustLevel(message, rrset, section);
+    }
+
+    bool getRRsetEntriesForTest(vector<RRsetEntryPtr> vec, time_t now) {
+        return getRRsetEntries(vec, now);
+    }
+
+    time_t getExpireTime() {
+        return expire_time_;
+    }
+
+};
+
+class MessageEntryTest: public testing::Test {
+public:
+    MessageEntryTest(): class_(1),
+                        message_parse(Message::PARSE),
+                        message_render(Message::RENDER)
+    {
+        
+        rrset_cache_.reset(new RRsetCache(RRSET_CACHE_DEFAULT_SIZE, class_));
+    }
+
+protected:
+    uint16_t class_;
+    RRsetCachePtr rrset_cache_;
+    Message message_parse;
+    Message message_render;
+};
+
+TEST_F(MessageEntryTest, testParseRRset) {
+    messageFromFile(message_parse, "message_fromWire3");
+    DerivedMessageEntry message_entry(message_parse, rrset_cache_);
+    uint32_t ttl = MAX_UINT32;
+    uint16_t rrset_count = 0;
+    message_entry.parseSectionForTest(message_parse, Message::SECTION_ANSWER, ttl, rrset_count);
+    EXPECT_EQ(ttl, 21600);
+    EXPECT_EQ(rrset_count, 1);
+
+    ttl = MAX_UINT32;
+    message_entry.parseSectionForTest(message_parse, Message::SECTION_AUTHORITY, ttl, rrset_count);
+    EXPECT_EQ(ttl, 21600);
+    EXPECT_EQ(rrset_count, 1);
+
+    ttl = MAX_UINT32;
+    message_entry.parseSectionForTest(message_parse, Message::SECTION_ADDITIONAL, ttl, rrset_count);
+    EXPECT_EQ(ttl, 10800);
+    EXPECT_EQ(rrset_count, 5);
+}
+
+TEST_F(MessageEntryTest, testGetRRsetTrustLevel_AA) {
+    messageFromFile(message_parse, "message_fromWire3");
+    DerivedMessageEntry message_entry(message_parse, rrset_cache_);
+    
+
+    RRsetIterator rrset_iter = message_parse.beginSection(Message::SECTION_ANSWER);
+    RRsetTrustLevel level = message_entry.getRRsetTrustLevelForTest(message_parse,
+                                                                    *rrset_iter,
+                                                                    Message::SECTION_ANSWER);
+    EXPECT_EQ(level, RRSET_TRUST_ANSWER_AA);
+
+    rrset_iter = message_parse.beginSection(Message::SECTION_AUTHORITY);
+    level = message_entry.getRRsetTrustLevelForTest(message_parse,
+                                                    *rrset_iter,
+                                                    Message::SECTION_AUTHORITY);
+    EXPECT_EQ(level, RRSET_TRUST_AUTHORITY_AA);
+
+    rrset_iter = message_parse.beginSection(Message::SECTION_ADDITIONAL);
+    level = message_entry.getRRsetTrustLevelForTest(message_parse,
+                                                    *rrset_iter,
+                                                    Message::SECTION_ADDITIONAL);
+    EXPECT_EQ(level, RRSET_TRUST_ADDITIONAL_AA);
+}
+
+TEST_F(MessageEntryTest, testGetRRsetTrustLevel_NONAA) {
+    messageFromFile(message_parse, "message_fromWire4");
+    DerivedMessageEntry message_entry(message_parse, rrset_cache_);
+    RRsetIterator rrset_iter = message_parse.beginSection(Message::SECTION_ANSWER);
+    RRsetTrustLevel level = message_entry.getRRsetTrustLevelForTest(message_parse,
+                                                                    *rrset_iter,
+                                                                    Message::SECTION_ANSWER);
+    EXPECT_EQ(level, RRSET_TRUST_ANSWER_NONAA);
+
+    rrset_iter = message_parse.beginSection(Message::SECTION_AUTHORITY);
+    level = message_entry.getRRsetTrustLevelForTest(message_parse,
+                                                    *rrset_iter,
+                                                    Message::SECTION_AUTHORITY);
+    EXPECT_EQ(level, RRSET_TRUST_AUTHORITY_NONAA);
+
+    rrset_iter = message_parse.beginSection(Message::SECTION_ADDITIONAL);
+    level = message_entry.getRRsetTrustLevelForTest(message_parse,
+                                                    *rrset_iter,
+                                                    Message::SECTION_ADDITIONAL);
+    EXPECT_EQ(level, RRSET_TRUST_ADDITIONAL_NONAA);
+}
+
+TEST_F(MessageEntryTest, testGetRRsetTrustLevel_CNAME) {
+    messageFromFile(message_parse, "message_fromWire5");
+    DerivedMessageEntry message_entry(message_parse, rrset_cache_);
+    RRsetIterator rrset_iter = message_parse.beginSection(Message::SECTION_ANSWER);
+    RRsetTrustLevel level = message_entry.getRRsetTrustLevelForTest(message_parse,
+                                                                    *rrset_iter,
+                                                                    Message::SECTION_ANSWER);
+    EXPECT_EQ(level, RRSET_TRUST_ANSWER_AA);
+
+    ++rrset_iter; // Get the rrset after the first cname rrset.
+    level = message_entry.getRRsetTrustLevelForTest(message_parse,
+                                                    *rrset_iter,
+                                                    Message::SECTION_ANSWER);
+    EXPECT_EQ(level, RRSET_TRUST_ANSWER_AA);
+}
+
+TEST_F(MessageEntryTest, testGetRRsetTrustLevel_DNAME) {
+    messageFromFile(message_parse, "message_fromWire6");
+    DerivedMessageEntry message_entry(message_parse, rrset_cache_);
+    RRsetIterator rrset_iter = message_parse.beginSection(Message::SECTION_ANSWER);
+    RRsetTrustLevel level = message_entry.getRRsetTrustLevelForTest(message_parse,
+                                                                    *rrset_iter,
+                                                                    Message::SECTION_ANSWER);
+    EXPECT_EQ(level, RRSET_TRUST_ANSWER_AA);
+
+    ++rrset_iter; // Get the rrset after the first dname rrset.
+    level = message_entry.getRRsetTrustLevelForTest(message_parse,
+                                                    *rrset_iter,
+                                                    Message::SECTION_ANSWER);
+    EXPECT_EQ(level, RRSET_TRUST_ANSWER_AA);
+
+    ++rrset_iter; // Get the second cname rrset
+    level = message_entry.getRRsetTrustLevelForTest(message_parse,
+                                                    *rrset_iter,
+                                                    Message::SECTION_ANSWER);
+    EXPECT_EQ(level, RRSET_TRUST_ANSWER_AA);
+}
+
+// We only test the expire_time of the message entry.
+// The test for genMessage() will make sure whether InitMessageEntry()
+// is right
+TEST_F(MessageEntryTest, testInitMessageEntry) {
+    messageFromFile(message_parse, "message_fromWire3");
+    DerivedMessageEntry message_entry(message_parse, rrset_cache_);
+    time_t expire_time = message_entry.getExpireTime();
+    // 1 second should be enough to do the compare
+    EXPECT_TRUE((time(NULL) + 10801) > expire_time);
+}
+
+TEST_F(MessageEntryTest, testGetRRsetEntries) {
+    messageFromFile(message_parse, "message_fromWire3");
+    DerivedMessageEntry message_entry(message_parse, rrset_cache_);
+    vector<RRsetEntryPtr> vec;
+    
+    // the time is bigger than the smallest expire time of 
+    // the rrset in message.
+    time_t expire_time = time(NULL) + 10802;
+    EXPECT_FALSE(message_entry.getRRsetEntriesForTest(vec, expire_time));
+}
+
+TEST_F(MessageEntryTest, testGenMessage) {
+    messageFromFile(message_parse, "message_fromWire3");
+    DerivedMessageEntry message_entry(message_parse, rrset_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_TC));
+    EXPECT_EQ(1, sectionRRsetCount(msg, Message::SECTION_ANSWER)); 
+    EXPECT_EQ(1, sectionRRsetCount(msg, Message::SECTION_AUTHORITY)); 
+    EXPECT_EQ(5, sectionRRsetCount(msg, Message::SECTION_ADDITIONAL)); 
+
+    // Check the rrset in answer section.
+    EXPECT_EQ(1, msg.getRRCount(Message::SECTION_ANSWER));
+    EXPECT_EQ(5, msg.getRRCount(Message::SECTION_AUTHORITY));
+    EXPECT_EQ(7, msg.getRRCount(Message::SECTION_ADDITIONAL));
+}
+
+}   // namespace
diff --git a/src/lib/cache/tests/resolver_cache_unittest.cc b/src/lib/cache/tests/resolver_cache_unittest.cc
new file mode 100644
index 0000000..a3cf728
--- /dev/null
+++ b/src/lib/cache/tests/resolver_cache_unittest.cc
@@ -0,0 +1,138 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+#include <config.h>
+#include <string>
+#include <gtest/gtest.h>
+#include <dns/rrset.h>
+#include "resolver_cache.h"
+#include "cache_test_messagefromfile.h"
+
+using namespace isc::cache;
+using namespace isc::dns;
+using namespace std;
+
+namespace {
+
+class ResolverCacheTest: public testing::Test {
+public:
+    ResolverCacheTest() {
+        vector<CacheSizeInfo> vec;
+        CacheSizeInfo class_in(RRClass::IN(), 100, 200);
+        CacheSizeInfo class_ch(RRClass::CH(), 100, 200);
+        vec.push_back(class_in);
+        vec.push_back(class_ch);
+        cache = new ResolverCache(vec);
+    }
+
+    ~ResolverCacheTest() {
+        delete cache;
+    }
+
+    ResolverCache* cache;
+};
+
+TEST_F(ResolverCacheTest, testUpdateMessage) {
+    Message msg(Message::PARSE);
+    messageFromFile(msg, "message_fromWire3");
+    cache->update(msg);
+
+    Name qname("example.com.");
+
+    msg.makeResponse();
+    EXPECT_TRUE(cache->lookup(qname, RRType::SOA(), RRClass::IN(), msg));
+    EXPECT_TRUE(msg.getHeaderFlag(Message::HEADERFLAG_AA));
+
+    // Test whether the old message can be updated
+    Message new_msg(Message::PARSE);
+    messageFromFile(new_msg, "message_fromWire4");
+    cache->update(new_msg);
+
+    new_msg.makeResponse();
+    EXPECT_TRUE(cache->lookup(qname, RRType::SOA(), RRClass::IN(), new_msg));
+    EXPECT_FALSE(new_msg.getHeaderFlag(Message::HEADERFLAG_AA));
+}
+#if 0
+TEST_F(ResolverCacheTest, testUpdateRRset) {
+    Message msg(Message::PARSE);
+    messageFromFile(msg, "message_fromWire3");
+    cache->update(msg);
+
+    Name qname("example.com.");
+
+    msg.makeResponse();
+    EXPECT_TRUE(cache->lookup(qname, RRType::SOA(), RRClass::IN(), msg));
+
+    Message except_msg(Message::RENDER);
+    EXPECT_THROW(cache->lookup(qname, RRType::SOA(), RRClass::IN(), except_msg), 
+                 MessageNoQuestionSection);
+
+    // Get one rrset in the message, then use it to 
+    // update rrset cache-> Test whether the local zone
+    // data is updated.
+    RRsetIterator iter = msg.beginSection(Message::SECTION_AUTHORITY);
+    RRsetPtr rrset_ptr = *iter;
+    cache->update(rrset_ptr);
+
+    Message new_msg(Message::RENDER);
+    Question question(qname, klass, RRType::NS());
+    new_msg.addQuestion(question);
+    EXPECT_TRUE(cache->lookup(qname, RRType::NS(), RRClass::IN(), new_msg));
+    EXPECT_EQ(0, sectionRRsetCount(new_msg, Message::SECTION_AUTHORITY));
+    EXPECT_EQ(0, sectionRRsetCount(new_msg, Message::SECTION_ADDITIONAL));
+}
+
+TEST_F(ResolverCacheTest, testLookupUnsupportedClass) {
+    Message msg(Message::PARSE);
+    messageFromFile(msg, "message_fromWire3");
+    cache->update(msg);
+
+    Name qname("example.com.");
+
+    msg.makeResponse();
+    EXPECT_FALSE(cache->lookup(qname, RRType::SOA(), RRClass::CH(), msg));
+    EXPECT_FALSE(cache->lookup(qname, RRType::SOA(), RRClass::CH()));
+}
+
+TEST_F(ResolverCacheTest, testLookupClosestRRset) {
+    Message msg(Message::PARSE);
+    messageFromFile(msg, "message_fromWire3");
+    cache->update(msg);
+
+    Name qname("www.test.example.com.");
+
+    RRsetPtr rrset_ptr = cache->lookupClosestRRset(qname, RRType::NS(),
+                                                  RRClass::IN());
+    EXPECT_TRUE(rrset_ptr);
+    EXPECT_EQ(rrset_ptr->getName(), Name("example.com."));
+
+    rrset_ptr = cache->lookupClosestRRset(Name("example.com."),
+                                         RRType::NS(), RRClass::IN());
+    EXPECT_TRUE(rrset_ptr);
+    EXPECT_EQ(rrset_ptr->getName(), Name("example.com."));
+
+    rrset_ptr = cache->lookupClosestRRset(Name("com."),
+                                         RRType::NS(), RRClass::IN());
+    EXPECT_FALSE(rrset_ptr);
+}
+
+TEST_F(ResolverCacheTest, testHasClass) {
+    EXPECT_TRUE(cache->getClassCache(RRClass::IN()));
+    EXPECT_TRUE(cache->getClassCache(RRClass::CH()));
+    EXPECT_FALSE(cache->getClassCache(RRClass::ANY()));
+}
+#endif
+
+}
diff --git a/src/lib/cache/tests/rrset_cache_unittest.cc b/src/lib/cache/tests/rrset_cache_unittest.cc
new file mode 100644
index 0000000..1263406
--- /dev/null
+++ b/src/lib/cache/tests/rrset_cache_unittest.cc
@@ -0,0 +1,84 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+#include <config.h>
+#include <string>
+#include <gtest/gtest.h>
+#include <cache/resolver_cache.h>
+#include <cache/cache_entry_key.h>
+#include <cache/rrset_entry.h>
+#include <cache/rrset_cache.h>
+#include <dns/name.h>
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+#include <dns/rrttl.h>
+#include <dns/rrset.h>
+
+using namespace isc::cache;
+using namespace isc::dns;
+using namespace std;
+
+namespace {
+
+class RRsetCacheTest : public testing::Test {
+protected:
+    RRsetCacheTest():
+        cache(RRSET_CACHE_DEFAULT_SIZE, RRClass::IN().getCode()),
+        name("example.com"),
+        rrset1(name, RRClass::IN(), RRType::A(), RRTTL(20)),
+        rrset2(name, RRClass::IN(), RRType::A(), RRTTL(10)),
+        rrset_entry1(rrset1, RRSET_TRUST_ADDITIONAL_AA),
+        rrset_entry2(rrset2, RRSET_TRUST_PRIM_ZONE_NONGLUE)
+    {
+    }
+
+    RRsetCache cache;
+    Name name;
+    RRset rrset1;
+    RRset rrset2;
+    RRsetEntry rrset_entry1;
+    RRsetEntry rrset_entry2;
+};
+
+TEST_F(RRsetCacheTest, lookup) {
+    const RRType& type = RRType::A();
+    EXPECT_TRUE(cache.lookup(name, type) == NULL);
+
+    cache.update(rrset1, rrset_entry1.getTrustLevel());
+    RRsetEntryPtr rrset_entry_ptr = cache.lookup(name, type);
+    EXPECT_EQ(rrset_entry_ptr->getTrustLevel(), rrset_entry1.getTrustLevel());
+    EXPECT_EQ(rrset_entry_ptr->getRRset()->getName(), rrset_entry1.getRRset()->getName());
+    EXPECT_EQ(rrset_entry_ptr->getRRset()->getType(), rrset_entry1.getRRset()->getType());
+    EXPECT_EQ(rrset_entry_ptr->getRRset()->getClass(), rrset_entry1.getRRset()->getClass());
+}
+
+TEST_F(RRsetCacheTest, update) {
+    const RRType& type = RRType::A();
+
+    cache.update(rrset1, rrset_entry1.getTrustLevel());
+    RRsetEntryPtr rrset_entry_ptr = cache.lookup(name, type);
+    EXPECT_EQ(rrset_entry_ptr->getTrustLevel(), rrset_entry1.getTrustLevel());
+
+    cache.update(rrset2, rrset_entry2.getTrustLevel());
+    rrset_entry_ptr = cache.lookup(name, type);
+    // The trust level should be updated
+    EXPECT_EQ(rrset_entry_ptr->getTrustLevel(), rrset_entry2.getTrustLevel());
+
+    cache.update(rrset1, rrset_entry1.getTrustLevel());
+    // The trust level should not be updated
+    EXPECT_EQ(rrset_entry_ptr->getTrustLevel(), rrset_entry2.getTrustLevel());
+}
+
+}
diff --git a/src/lib/cache/tests/rrset_entry_unittest.cc b/src/lib/cache/tests/rrset_entry_unittest.cc
new file mode 100644
index 0000000..ce6238f
--- /dev/null
+++ b/src/lib/cache/tests/rrset_entry_unittest.cc
@@ -0,0 +1,106 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+#include <config.h>
+#include <string>
+#include <gtest/gtest.h>
+#include <cache/cache_entry_key.h>
+#include <cache/rrset_entry.h>
+#include <dns/name.h>
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+#include <dns/rrttl.h>
+#include <dns/rrset.h>
+
+using namespace isc::cache;
+using namespace isc::dns;
+using namespace std;
+
+namespace {
+class GenCacheKeyTest: public testing::Test {
+};
+
+TEST_F(GenCacheKeyTest, genCacheEntryKey1) {
+    string name = "example.com.";
+    uint16_t type = 12;
+    string name_type = "example.com.12";
+
+    EXPECT_EQ(name_type, genCacheEntryName(name, type));
+}
+
+TEST_F(GenCacheKeyTest, genCacheEntryKey2) {
+    Name name("example.com");
+    RRType type(1234);
+    string keystr = "example.com.1234";
+    EXPECT_EQ(keystr, genCacheEntryName(name, type));
+}
+
+class DerivedRRsetEntry: public RRsetEntry {
+public:
+
+    void updateTTLForTest() {
+        
+    }
+};
+
+#define TEST_TTL 100
+class RRsetEntryTest : public ::testing::Test {
+protected:
+    RRsetEntryTest():
+        name("test.example.com"),
+        rrset(name, RRClass::IN(), RRType::A(), RRTTL(TEST_TTL)),
+        trust_level(RRSET_TRUST_ADDITIONAL_AA),
+        rrset_entry(rrset, trust_level)
+    {
+    }
+    Name name;
+    RRset rrset;
+    RRsetTrustLevel trust_level;
+    RRsetEntry rrset_entry;
+};
+
+TEST_F(RRsetEntryTest, constructor) {
+    EXPECT_EQ(trust_level, rrset_entry.getTrustLevel());
+    EXPECT_EQ(rrset.getName(), rrset_entry.getRRset()->getName());
+    EXPECT_EQ(rrset.getClass(), rrset_entry.getRRset()->getClass());
+    EXPECT_EQ(rrset.getType(), rrset_entry.getRRset()->getType());
+    EXPECT_EQ(rrset.getRdataCount(), rrset_entry.getRRset()->getRdataCount());
+}
+
+TEST_F(RRsetEntryTest, updateTTL) {
+    uint32_t ttl = rrset_entry.getTTL();
+    sleep(1);
+    // The TTL should be decreased
+    EXPECT_TRUE(rrset_entry.getTTL() < ttl);
+}
+
+TEST_F(RRsetEntryTest, TTLExpire) {
+    RRset exp_rrset(name, RRClass::IN(), RRType::A(), RRTTL(1));
+    RRsetEntry rrset_entry(exp_rrset, RRSET_TRUST_ANSWER_AA);
+    sleep(1);
+    uint32_t ttl = rrset_entry.getTTL();
+    EXPECT_LT(ttl, 1);
+    sleep(1);
+    ttl = rrset_entry.getTTL();
+    EXPECT_LT(ttl, 1);
+}
+
+TEST_F(RRsetEntryTest, getExpireTime){
+    uint32_t exp_time = time(NULL) + TEST_TTL;
+    EXPECT_EQ(exp_time, rrset_entry.getExpireTime());
+}
+
+}   // namespace
+
diff --git a/src/lib/cache/tests/run_unittests.cc b/src/lib/cache/tests/run_unittests.cc
new file mode 100644
index 0000000..b34b3dd
--- /dev/null
+++ b/src/lib/cache/tests/run_unittests.cc
@@ -0,0 +1,29 @@
+// Copyright (C) 2009  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id: run_unittests.cc 3020 2010-09-26 03:47:26Z jinmei $
+#include <config.h>
+
+#include <gtest/gtest.h>
+
+#include <dns/tests/unittest_util.h>
+
+int
+main(int argc, char* argv[]) {
+    ::testing::InitGoogleTest(&argc, argv);
+    isc::UnitTestUtil::addDataPath(TEST_DATA_SRCDIR);
+    isc::UnitTestUtil::addDataPath(TEST_DATA_BUILDDIR);
+
+    return (RUN_ALL_TESTS());
+}
diff --git a/src/lib/cache/tests/testdata/message_fromWire1 b/src/lib/cache/tests/testdata/message_fromWire1
new file mode 100644
index 0000000..5b76e3f
--- /dev/null
+++ b/src/lib/cache/tests/testdata/message_fromWire1
@@ -0,0 +1,22 @@
+#
+# A simple DNS response message
+# ID = 0x1035
+# QR=1 (response), Opcode=0, AA=1, RD=1 (other fields are 0)
+# QDCOUNT=1, ANCOUNT=2, other COUNTS=0
+# Question: test.example.com. IN A
+# Answer:
+#  test.example.com. 3600 IN A 192.0.2.1
+#  test.example.com. 7200 IN A 192.0.2.2
+#
+1035 8500
+0001 0002 0000 0000
+#(4) t  e  s  t (7) e  x  a  m  p  l  e (3) c  o  m  .
+ 04 74 65 73 74 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
+0001 0001
+# same name, fully compressed
+c0 0c
+# TTL=3600, A, IN, RDLENGTH=4, RDATA
+0001 0001 00000e10 0004 c0 00 02 01
+# mostly same, with the slight difference in RDATA and TTL
+c0 0c
+0001 0001 00001c20 0004 c0 00 02 02
diff --git a/src/lib/cache/tests/testdata/message_fromWire2 b/src/lib/cache/tests/testdata/message_fromWire2
new file mode 100644
index 0000000..c8fddbd
--- /dev/null
+++ b/src/lib/cache/tests/testdata/message_fromWire2
@@ -0,0 +1,22 @@
+#
+# A simple DNS response message
+# ID = 0x1035
+# QR=1 (response), Opcode=0, AA=1, RD=1 (other fields are 0)
+# QDCOUNT=1, ANCOUNT=2, other COUNTS=0
+# Question: test.example.net. IN A
+# Answer:
+#  test.example.net. 3600 IN A 192.0.2.1
+#  test.example.net. 7200 IN A 192.0.2.2
+#
+1035 8500
+0001 0002 0000 0000
+#(4) t  e  s  t (7) e  x  a  m  p  l  e (3) n  e  t  .
+ 04 74 65 73 74 07 65 78 61 6d 70 6c 65 03 6e 65 74 00
+0001 0001
+# same name, fully compressed
+c0 0c
+# TTL=3600, A, IN, RDLENGTH=4, RDATA
+0001 0001 00000e10 0004 c0 00 02 01
+# mostly same, with the slight difference in RDATA and TTL
+c0 0c
+0001 0001 00001c20 0004 c0 00 02 02
diff --git a/src/lib/cache/tests/testdata/message_fromWire3 b/src/lib/cache/tests/testdata/message_fromWire3
new file mode 100644
index 0000000..f7b3a4a
--- /dev/null
+++ b/src/lib/cache/tests/testdata/message_fromWire3
@@ -0,0 +1,76 @@
+#
+# A simple DNS response message
+# ID = 0x0513
+# QR=1 (response), Opcode=0, AA=1, RD=1 (other fields are 0)
+# QDCOUNT=1, ANCOUNT=1, AUTHORITY COUNT=5, ADDITIONAL COUNT=7
+# Question: example.com. IN SOA
+# ANSWER:
+#   ;; QUESTION SECTION:
+#   ;example.com.                   IN      SOA
+
+#   ;; ANSWER SECTION:
+#   example.com.            21600   IN      SOA     a.dns.example.com. root.example.com. 2009070811 7200 3600 2419200 21600
+
+#   ;; AUTHORITY SECTION:
+#   example.com.            21600   IN      NS      b.dns.example.com.
+#   example.com.            21600   IN      NS      c.dns.example.com.
+#   example.com.            21600   IN      NS      a.dns.example.com.
+#   example.com.            21600   IN      NS      e.dns.example.com.
+#   example.com.            21600   IN      NS      d.dns.example.com.
+
+#    ;; ADDITIONAL SECTION:
+#    a.dns.example.com.      21600   IN      A       1.1.1.1
+#    a.dns.example.com.      21600   IN      A       2.2.2.2
+#    b.dns.example.com.      21600   IN      A       3.3.3.3
+#    c.dns.example.com.      10800   IN      A       4.4.4.4
+#    d.dns.example.com.      43200   IN      A       5.5.5.5
+#    e.dns.example.com.      21600   IN      A       7.7.7.7
+#    e.dns.example.com.      21600   IN      A       6.6.6.6
+
+0513 8500
+0001 0001 0005 0007
+#(7) e x  a  m  p  l  e (3) c  o  m  .
+ 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
+0006 0001
+# same name, fully compressed
+c0 0c
+# SOA IN  TTL=6h   RDLENGTH=35 rdata
+0006 0001 00005460 0023 01 61 03 64 6e 73 c0 0c 04 72 6f 6f 74 c0 0c 77 bf fc db 00 00 1c 20 00 00 0e 10 00 24 ea 00 00 00 54 60
+#Authority section begin
+c0 0c
+# NS IN   TTL=6h   RDLENGTH=4  b.dns.example.com.
+0002 0001 00005460 0004 01 62 c0 2b
+# NS IN   TTL=6h  c.dns.example.com.
+c0 0c
+0002 0001 00005460 00 04 01 63 c0 2b
+# NS IN a.dns.example.com.
+c0 0c
+0002 0001 00005460 00 02 c0 29
+# NS IN e.dns.example.com.
+c0 0c
+0002 0001 00005460 0004 01 65 c0 2b
+# NS IN d.dns.example.com.
+c0 0c
+0002 0001 00005460 0004 01 64 c0 2b
+# additional section begin
+# a.dns.example.com. A
+c0 29
+0001 0001 00005460 0004 01 01 01 01
+# a.dns.example.com. A
+c0 29
+0001 0001 00005460 0004 02 02 02 02
+#b.dns.example.com.  A
+c0 58
+0001 0001 00002A30 0004 03 03 03 03
+#c.dns.example.com.  A
+c0 68
+0001 0001 00005460 0004 04 04 04 04
+# d.dns.example.com. A
+c0 96
+0001 0001 0000A8C0 0004 05 05 05 05
+# e.dns.example.com. A
+c0 86
+0001 0001 00005460 0004 07 07 07 07
+# e.dns.example.com. A
+c0 86
+0001 0001 00005460 0004 06 06 06 06
diff --git a/src/lib/cache/tests/testdata/message_fromWire4 b/src/lib/cache/tests/testdata/message_fromWire4
new file mode 100644
index 0000000..251abd5
--- /dev/null
+++ b/src/lib/cache/tests/testdata/message_fromWire4
@@ -0,0 +1,80 @@
+# Note: This message is same with message_fromWire3, except
+#       AA bit is not set. There should be a better way to
+#       avoid the duplicated file by clear the AA bit flags
+#       after reading the message from message_fromWire4.
+# 
+# A simple DNS response message
+# ID = 0x0513
+# QR=1 (response), Opcode=0, RD=1 (other fields are 0)
+# QDCOUNT=1, ANCOUNT=1, AUTHORITY COUNT=5, ADDITIONAL COUNT=7
+# Question: example.com. IN SOA
+# ANSWER:
+#   ;; QUESTION SECTION:
+#   ;example.com.                   IN      SOA
+
+#   ;; ANSWER SECTION:
+#   example.com.            21600   IN      SOA     a.dns.example.com. root.example.com. 2009070811 7200 3600 2419200 21600
+
+#   ;; AUTHORITY SECTION:
+#   example.com.            21600   IN      NS      b.dns.example.com.
+#   example.com.            21600   IN      NS      c.dns.example.com.
+#   example.com.            21600   IN      NS      a.dns.example.com.
+#   example.com.            21600   IN      NS      e.dns.example.com.
+#   example.com.            21600   IN      NS      d.dns.example.com.
+
+#    ;; ADDITIONAL SECTION:
+#    a.dns.example.com.      21600   IN      A       1.1.1.1
+#    a.dns.example.com.      21600   IN      A       2.2.2.2
+#    b.dns.example.com.      21600   IN      A       3.3.3.3
+#    c.dns.example.com.      10800   IN      A       4.4.4.4
+#    d.dns.example.com.      43200   IN      A       5.5.5.5
+#    e.dns.example.com.      21600   IN      A       7.7.7.7
+#    e.dns.example.com.      21600   IN      A       6.6.6.6
+
+0513 8100
+0001 0001 0005 0007
+#(7) e x  a  m  p  l  e (3) c  o  m  .
+ 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
+0006 0001
+# same name, fully compressed
+c0 0c
+# SOA IN  TTL=6h   RDLENGTH=35 rdata
+0006 0001 00005460 0023 01 61 03 64 6e 73 c0 0c 04 72 6f 6f 74 c0 0c 77 bf fc db 00 00 1c 20 00 00 0e 10 00 24 ea 00 00 00 54 60
+#Authority section begin
+c0 0c
+# NS IN   TTL=6h   RDLENGTH=4  b.dns.example.com.
+0002 0001 00005460 0004 01 62 c0 2b
+# NS IN   TTL=6h  c.dns.example.com.
+c0 0c
+0002 0001 00005460 00 04 01 63 c0 2b
+# NS IN a.dns.example.com.
+c0 0c
+0002 0001 00005460 00 02 c0 29
+# NS IN e.dns.example.com.
+c0 0c
+0002 0001 00005460 0004 01 65 c0 2b
+# NS IN d.dns.example.com.
+c0 0c
+0002 0001 00005460 0004 01 64 c0 2b
+# additional section begin
+# a.dns.example.com. A
+c0 29
+0001 0001 00005460 0004 01 01 01 01
+# a.dns.example.com. A
+c0 29
+0001 0001 00005460 0004 02 02 02 02
+#b.dns.example.com.  A
+c0 58
+0001 0001 00002A30 0004 03 03 03 03
+#c.dns.example.com.  A
+c0 68
+0001 0001 00005460 0004 04 04 04 04
+# d.dns.example.com. A
+c0 96
+0001 0001 0000A8C0 0004 05 05 05 05
+# e.dns.example.com. A
+c0 86
+0001 0001 00005460 0004 07 07 07 07
+# e.dns.example.com. A
+c0 86
+0001 0001 00005460 0004 06 06 06 06
diff --git a/src/lib/cache/tests/testdata/message_fromWire5 b/src/lib/cache/tests/testdata/message_fromWire5
new file mode 100644
index 0000000..965f250
--- /dev/null
+++ b/src/lib/cache/tests/testdata/message_fromWire5
@@ -0,0 +1,36 @@
+#
+# A simple DNS response message
+# ID = 0x07b2
+# QR=1 (response), Opcode=0, AA=1, RD=1 (other fields are 0)
+# QDCOUNT=1, ANCOUNT=2, other COUNTS=0
+# Question: a.example.net. IN A
+# Answer:
+#    ANSWER SECTION:
+#    a.example.com.          21600   IN      CNAME   cname.example.com.
+#    cname.example.com.      21600   IN      A       1.1.1.1
+#
+#    AUTHORITY SECTION:
+#    example.com.            21600   IN      NS      a.dns.example.com.
+#
+#    ADDITIONAL SECTION:
+#    a.dns.example.com.      21600   IN      A       1.1.1.1
+#
+07b2 8500
+0001 0002 0001 0001
+#(1) a (7)  e  x  a  m  p  l  e  (3) c o  m  .
+ 01 61 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
+# A  IN
+0001 0001
+#
+c0 0c
+#CNAME IN  TTL     RDATA_LEN
+0005 0001 00005460 0008 05 63 6e 61 6d 65 c0 0e
+#
+c0 2b
+0001 0001 00005460 0004 01 01 01 01
+#
+c0 0e
+0002 0001 00005460 0008 01 61 03 64 6e 73 c0 0e
+#
+c0 4f
+0001 0001 00005460 0004 01 01 01 01
diff --git a/src/lib/cache/tests/testdata/message_fromWire6 b/src/lib/cache/tests/testdata/message_fromWire6
new file mode 100644
index 0000000..23684ba
--- /dev/null
+++ b/src/lib/cache/tests/testdata/message_fromWire6
@@ -0,0 +1,40 @@
+#
+# A simple DNS response message
+# ID = 0x005e
+# QR=1 (response), Opcode=0, AA=1, RD=1 (other fields are 0)
+# QDCOUNT=1, ANCOUNT=2, other COUNTS=0
+# Question: a.d.example.net. IN A
+# Answer:
+#    ;; ANSWER SECTION:
+#    d.example.com.          21600   IN      DNAME   dname.example.com.
+#    a.d.example.com.        21600   IN      CNAME   a.dname.example.com.
+#    a.dname.example.com.    21600   IN      A       1.1.1.1
+#
+#    ;; AUTHORITY SECTION:
+#    example.com.            21600   IN      NS      a.dns.example.com.
+#
+#    ;; ADDITIONAL SECTION:
+#    a.dns.example.com.      21600   IN      A       1.1.1.1
+#
+#
+005e 8500
+0001 0003 0001 0001
+#(1)a (1) b (7) e  x  a  m  p  l  e (3) c  o  m  .
+ 01 61 01 64 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
+# A  IN
+0001 0001
+#
+c0 0e
+0027 0001 00005460 0013 05 64 6e 61 6d 65 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
+# 
+c0 0c
+0005 0001 00005460 0004 01 61 c0 2d
+#
+c0 4c
+0001 0001 00005460 0004 01 01 01 01
+#
+c0 33
+0002 0001 00005460 0008 01 61 03 64 6e 73 c0 33
+#
+c0 6c
+0001 0001 00005460 0004 01 01 01 01
diff --git a/src/lib/datasrc/memory_datasrc.cc b/src/lib/datasrc/memory_datasrc.cc
index 0691926..fdef888 100644
--- a/src/lib/datasrc/memory_datasrc.cc
+++ b/src/lib/datasrc/memory_datasrc.cc
@@ -36,7 +36,7 @@ struct MemoryZone::MemoryZoneImpl {
     // Constructor
     MemoryZoneImpl(const RRClass& zone_class, const Name& origin) :
         zone_class_(zone_class), origin_(origin), origin_data_(NULL),
-        domains_(true) 
+        domains_(true)
     {
         // We create the node for origin (it needs to exist anyway in future)
         domains_.insert(origin, &origin_data_);
@@ -62,6 +62,7 @@ struct MemoryZone::MemoryZoneImpl {
     // The tree stores domains
     typedef RBTree<Domain> DomainTree;
     typedef RBNode<Domain> DomainNode;
+    static const DomainNode::Flags DOMAINFLAG_WILD = DomainNode::FLAG_USER1;
 
     // Information about the zone
     RRClass zone_class_;
@@ -72,6 +73,47 @@ struct MemoryZone::MemoryZoneImpl {
     // The actual zone data
     DomainTree domains_;
 
+    // Add the necessary magic for any wildcard contained in 'name'
+    // (including itself) to be found in the zone.
+    //
+    // In order for wildcard matching to work correctly in find(),
+    // we must ensure that a node for the wildcarding level exists in the
+    // backend RBTree.
+    // E.g. if the wildcard name is "*.sub.example." then we must ensure
+    // that "sub.example." exists and is marked as a wildcard level.
+    // Note: the "wildcarding level" is for the parent name of the wildcard
+    // name (such as "sub.example.").
+    //
+    // We also perform the same trick for empty wild card names possibly
+    // contained in 'name' (e.g., '*.foo.example' in 'bar.*.foo.example').
+    void addWildcards(DomainTree& domains, const Name& name) {
+        Name wname(name);
+        const unsigned int labels(wname.getLabelCount());
+        const unsigned int origin_labels(origin_.getLabelCount());
+        for (unsigned int l = labels;
+             l > origin_labels;
+             --l, wname = wname.split(1)) {
+            if (wname.isWildcard()) {
+                // Ensure a separate level exists for the wildcard name.
+                // Note: for 'name' itself we do this later anyway, but the
+                // overhead should be marginal because wildcard names should
+                // be rare.
+                DomainNode* node;
+                DomainTree::Result result(domains.insert(wname.split(1),
+                                                         &node));
+                assert(result == DomainTree::SUCCESS ||
+                       result == DomainTree::ALREADYEXISTS);
+
+                // Ensure a separate level exists for the "wildcarding" name,
+                // and mark the node as "wild".
+                result = domains.insert(wname, &node);
+                assert(result == DomainTree::SUCCESS ||
+                       result == DomainTree::ALREADYEXISTS);
+                node->setFlag(DOMAINFLAG_WILD);
+            }
+        }
+    }
+
     /*
      * Does some checks in context of the data that are already in the zone.
      * Currently checks for forbidden combinations of RRsets in the same
@@ -114,13 +156,10 @@ struct MemoryZone::MemoryZoneImpl {
         }
     }
 
-    /*
-     * Implementation of longer methods. We put them here, because the
-     * access is without the impl_-> and it will get inlined anyway.
-     */
-    // Implementation of MemoryZone::add
-    result::Result add(const ConstRRsetPtr& rrset, DomainTree* domains) {
-        // Sanitize input
+    // Validate rrset before adding it to the zone.  If something is wrong
+    // it throws an exception.  It doesn't modify the zone, and provides
+    // the strong exception guarantee.
+    void addValidation(const ConstRRsetPtr rrset) {
         if (!rrset) {
             isc_throw(NullRRset, "The rrset provided is NULL");
         }
@@ -137,26 +176,55 @@ struct MemoryZone::MemoryZoneImpl {
                       << rrset->getName());
         }
 
-        Name name(rrset->getName());
-        NameComparisonResult compare(origin_.compare(name));
+        NameComparisonResult compare(origin_.compare(rrset->getName()));
         if (compare.getRelation() != NameComparisonResult::SUPERDOMAIN &&
             compare.getRelation() != NameComparisonResult::EQUAL)
         {
-            isc_throw(OutOfZone, "The name " << name <<
+            isc_throw(OutOfZone, "The name " << rrset->getName() <<
                 " is not contained in zone " << origin_);
         }
+
+        // Some RR types do not really work well with a wildcard.
+        // Even though the protocol specifically doesn't completely ban such
+        // usage, we refuse to load a zone containing such RR in order to
+        // keep the lookup logic simpler and more predictable.
+        // See RFC4592 and (for DNAME) draft-ietf-dnsext-rfc2672bis-dname
+        // for more technical background.  Note also that BIND 9 refuses
+        // NS at a wildcard, so in that sense we simply provide compatible
+        // behavior.
+        if (rrset->getName().isWildcard()) {
+            if (rrset->getType() == RRType::NS()) {
+                isc_throw(AddError, "Invalid NS owner name (wildcard): " <<
+                          rrset->getName());
+            }
+            if (rrset->getType() == RRType::DNAME()) {
+                isc_throw(AddError, "Invalid DNAME owner name (wildcard): " <<
+                          rrset->getName());
+            }
+        }
+    }
+
+    /*
+     * Implementation of longer methods. We put them here, because the
+     * access is without the impl_-> and it will get inlined anyway.
+     */
+    // Implementation of MemoryZone::add
+    result::Result add(const ConstRRsetPtr& rrset, DomainTree* domains) {
+        // Sanitize input
+        addValidation(rrset);
+
+        // Add wildcards possibly contained in the owner name to the domain
+        // tree.
+        // Note: this can throw an exception, breaking strong exception
+        // guarantee.  (see also the note for contextCheck() below).
+        addWildcards(*domains, rrset->getName());
+
         // Get the node
         DomainNode* node;
-        switch (domains->insert(name, &node)) {
-            // Just check it returns reasonable results
-            case DomainTree::SUCCESS:
-            case DomainTree::ALREADYEXISTS:
-                break;
-            // Something odd got out
-            default:
-                assert(0);
-        }
-        assert(node != NULL);
+        DomainTree::Result result = domains->insert(rrset->getName(), &node);
+        // Just check it returns reasonable results
+        assert((result == DomainTree::SUCCESS ||
+                result == DomainTree::ALREADYEXISTS) && node!= NULL);
 
         // Now get the domain
         DomainPtr domain;
@@ -183,10 +251,10 @@ struct MemoryZone::MemoryZoneImpl {
             // indicating the need for callback in find().
             if (rrset->getType() == RRType::NS() &&
                 rrset->getName() != origin_) {
-                node->enableCallback();
+                node->setFlag(DomainNode::FLAG_CALLBACK);
             // If it is DNAME, we have a callback as well here
             } else if (rrset->getType() == RRType::DNAME()) {
-                node->enableCallback();
+                node->setFlag(DomainNode::FLAG_CALLBACK);
             }
 
             return (result::SUCCESS);
@@ -349,7 +417,7 @@ struct MemoryZone::MemoryZoneImpl {
 
         // If the node callback is enabled, this may be a zone cut.  If it
         // has a NS RR, we should return a delegation, but not in the apex.
-        if (node->isCallbackEnabled() && node != origin_data_) {
+        if (node->getFlag(DomainNode::FLAG_CALLBACK) && node != origin_data_) {
             found = node->getData()->find(RRType::NS());
             if (found != node->getData()->end()) {
                 return (FindResult(DELEGATION, found->second));
diff --git a/src/lib/datasrc/rbtree.h b/src/lib/datasrc/rbtree.h
index 8ceb7e0..bd04066 100644
--- a/src/lib/datasrc/rbtree.h
+++ b/src/lib/datasrc/rbtree.h
@@ -23,6 +23,8 @@
 ///     issue, the design and interface are not fixed, and RBTree isn't ready
 ///     to be used as a base data structure by other modules.
 
+#include <exceptions/exceptions.h>
+
 #include <dns/name.h>
 #include <boost/utility.hpp>
 #include <boost/shared_ptr.hpp>
@@ -110,6 +112,29 @@ public:
     /// \brief Alias for shared pointer to the data.
     typedef boost::shared_ptr<T> NodeDataPtr;
 
+    /// Node flags.
+    ///
+    /// Each flag value defines a non default property for a specific node.
+    /// These are defined as bitmask type values for the convenience of
+    /// internal implementation, but applications are expected to use
+    /// each flag separately via the enum definitions.
+    ///
+    /// All (settable) flags are off by default; they must be explicitly
+    /// set to on by the \c setFlag() method.
+    enum Flags {
+        FLAG_CALLBACK = 1, ///< Callback enabled. See \ref callback
+        FLAG_USER1 = 0x80000000U ///< Application specific flag
+    };
+private:
+    // Some flag values are expected to be used for internal purposes
+    // (e.g., representing the node color) in future versions, so we
+    // limit the settable flags via the \c setFlag() method to those
+    // explicitly defined in \c Flags.  This constant represents all
+    // such flags.
+    static const uint32_t SETTABLE_FLAGS = (FLAG_CALLBACK | FLAG_USER1);
+
+public:
+
     /// \brief Destructor
     ///
     /// It might seem strange that constructors are private and destructor
@@ -153,6 +178,52 @@ public:
     void setData(const NodeDataPtr& data) { data_ = data; }
     //@}
 
+    /// \name Node flag manipulation methods
+    //@{
+    /// Get the status of a node flag.
+    ///
+    /// This method returns whether the given node flag is set (enabled)
+    /// on the node.  The \c flag parameter is expected to be one of the
+    /// defined \c Flags constants.  For simplicity, the method interface
+    /// does not prohibit passing an undefined flag or combined flags, but
+    /// the return value in such a case will be meaningless for the caller
+    /// (an application would have to use an ugly cast for such an unintended
+    /// form of call, which will hopefully avoid accidental misuse).
+    ///
+    /// \exception None
+    /// \param flag The flag to be tested.
+    /// \return \c true if the \c flag is set; \c false otherwise.
+    bool getFlag(Flags flag) const {
+        return ((flags_ & flag) != 0);
+    }
+
+    /// Set or clear a node flag.
+    ///
+    /// This method changes the status of the specified node flag to either
+    /// "on" (enabled) or "off" (disabled).  The new status is specified by
+    /// the \c on parameter.
+    /// Like the \c getFlag() method, \c flag is expected to be one of the
+    /// defined \c Flags constants.  If an undefined or unsettable flag is
+    /// specified, \c isc::InvalidParameter exception will be thrown.
+    ///
+    /// \exception isc::InvalidParameter Unsettable flag is specified
+    /// \exception None otherwise
+    /// \param flag The node flag to be changed.
+    /// \on If \c true, set the flag to on; otherwise set it to off.
+    void setFlag(Flags flag, bool on = true) {
+        if ((flag & ~SETTABLE_FLAGS) != 0) {
+            isc_throw(isc::InvalidParameter,
+                      "Unsettable RBTree flag is being set");
+        }
+        if (on) {
+            flags_ |= flag;
+        } else {
+            flags_ &= ~flag;
+        }
+    }
+    //@}
+
+private:
     /// \name Callback related methods
     ///
     /// See the description of \c RBTree<T>::find() about callbacks.
@@ -160,16 +231,8 @@ public:
     /// These methods never throw an exception.
     //@{
     /// Return if callback is enabled at the node.
-    bool isCallbackEnabled() const { return (callback_required_); }
-
-    /// Enable callback at the node.
-    void enableCallback() { callback_required_ = true; }
-
-    /// Disable callback at the node.
-    void disableCallback() { callback_required_ = false; }
     //@}
 
-
 private:
     /// \brief Define rbnode color
     enum RBNodeColor {BLACK, RED};
@@ -224,7 +287,7 @@ private:
     /// RBTree::find().
     ///
     /// \todo It might be needed to put it into more general attributes field.
-    bool callback_required_;
+    uint32_t flags_;
 };
 
 
@@ -238,7 +301,7 @@ RBNode<T>::RBNode() :
     // dummy name, the value doesn't matter:
     name_(isc::dns::Name::ROOT_NAME()),
     down_(this),
-    callback_required_(false)
+    flags_(0)
 {
 }
 
@@ -250,7 +313,7 @@ RBNode<T>::RBNode(const isc::dns::Name& name) :
     color_(RED),
     name_(name),
     down_(NULL_NODE()),
-    callback_required_(false)
+    flags_(0)
 {
 }
 
@@ -650,7 +713,7 @@ public:
     ///
     /// This version of \c find() calls the callback whenever traversing (on
     /// the way from root down the tree) a marked node on the way down through
-    /// the domain namespace (see RBNode::enableCallback and related
+    /// the domain namespace (see \c RBNode::enableCallback and related
     /// functions).
     ///
     /// If you return true from the callback, the search is stopped and a
@@ -944,7 +1007,8 @@ RBTree<T>::find(const isc::dns::Name& target_name,
                 if (needsReturnEmptyNode_ || !node->isEmpty()) {
                     ret = PARTIALMATCH;
                     *target = node;
-                    if (callback != NULL && node->callback_required_) {
+                    if (callback != NULL &&
+                        node->getFlag(RBNode<T>::FLAG_CALLBACK)) {
                         if ((callback)(*node, callback_arg)) {
                             break;
                         }
@@ -1096,7 +1160,7 @@ RBTree<T>::nodeFission(RBNode<T>& node, const isc::dns::Name& base_name) {
     // consistent behavior (i.e., a weak form of strong exception guarantee)
     // even if code after the call to this function throws an exception.
     std::swap(node.data_, down_node->data_);
-    std::swap(node.callback_required_, down_node->callback_required_);
+    std::swap(node.flags_, down_node->flags_);
     down_node->down_ = node.down_;
     node.down_ = down_node.get();
     // root node of sub tree, the initial color is BLACK
diff --git a/src/lib/datasrc/tests/memory_datasrc_unittest.cc b/src/lib/datasrc/tests/memory_datasrc_unittest.cc
index 65b3c95..15b06b4 100644
--- a/src/lib/datasrc/tests/memory_datasrc_unittest.cc
+++ b/src/lib/datasrc/tests/memory_datasrc_unittest.cc
@@ -13,8 +13,14 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include <sstream>
+#include <vector>
+
+#include <boost/bind.hpp>
+
 #include <exceptions/exceptions.h>
 
+#include <dns/masterload.h>
 #include <dns/name.h>
 #include <dns/rdata.h>
 #include <dns/rdataclass.h>
@@ -27,6 +33,7 @@
 
 #include <gtest/gtest.h>
 
+using namespace std;
 using namespace isc::dns;
 using namespace isc::dns::rdata;
 using namespace isc::datasrc;
@@ -140,62 +147,86 @@ TEST_F(MemoryDataSrcTest, getZoneCount) {
     EXPECT_EQ(2, memory_datasrc.getZoneCount());
 }
 
+// A helper callback of masterLoad() used in MemoryZoneTest.
+void
+setRRset(RRsetPtr rrset, vector<RRsetPtr*>::iterator& it) {
+    *(*it) = rrset;
+    ++it;
+}
+
 /// \brief Test fixture for the MemoryZone class
 class MemoryZoneTest : public ::testing::Test {
+    // A straightforward pair of textual RR(set) and a RRsetPtr variable
+    // to store the RRset.  Used to build test data below.
+    struct RRsetData {
+        const char* const text; // textual representation of an RRset
+        RRsetPtr* rrset;
+    };
 public:
     MemoryZoneTest() :
         class_(RRClass::IN()),
         origin_("example.org"),
-        ns_name_("ns.example.org"),
-        cname_name_("cname.example.org"),
-        dname_name_("dname.example.org"),
-        child_ns_name_("child.example.org"),
-        child_glue_name_("ns.child.example.org"),
-        grandchild_ns_name_("grand.child.example.org"),
-        grandchild_glue_name_("ns.grand.child.example.org"),
-        child_dname_name_("dname.child.example.org"),
-        zone_(class_, origin_),
-        rr_out_(new RRset(Name("example.com"), class_, RRType::A(),
-            RRTTL(300))),
-        rr_ns_(new RRset(origin_, class_, RRType::NS(), RRTTL(300))),
-        rr_ns_a_(new RRset(ns_name_, class_, RRType::A(), RRTTL(300))),
-        rr_ns_aaaa_(new RRset(ns_name_, class_, RRType::AAAA(), RRTTL(300))),
-        rr_a_(new RRset(origin_, class_, RRType::A(), RRTTL(300))),
-        rr_cname_(new RRset(cname_name_, class_, RRType::CNAME(), RRTTL(300))),
-        rr_cname_a_(new RRset(cname_name_, class_, RRType::A(), RRTTL(300))),
-        rr_dname_(new RRset(dname_name_, class_, RRType::DNAME(), RRTTL(300))),
-        rr_dname_a_(new RRset(dname_name_, class_, RRType::A(),
-            RRTTL(300))),
-        rr_dname_ns_(new RRset(dname_name_, class_, RRType::NS(), RRTTL(300))),
-        rr_dname_apex_(new RRset(origin_, class_, RRType::DNAME(),
-            RRTTL(300))),
-        rr_child_ns_(new RRset(child_ns_name_, class_, RRType::NS(),
-                               RRTTL(300))),
-        rr_child_glue_(new RRset(child_glue_name_, class_, RRType::A(),
-                              RRTTL(300))),
-        rr_grandchild_ns_(new RRset(grandchild_ns_name_, class_, RRType::NS(),
-                                    RRTTL(300))),
-        rr_grandchild_glue_(new RRset(grandchild_glue_name_, class_,
-                                      RRType::AAAA(), RRTTL(300))),
-        rr_child_dname_(new RRset(child_dname_name_, class_, RRType::DNAME(),
-            RRTTL(300)))
+        zone_(class_, origin_)
     {
+        // Build test RRsets.  Below, we construct an RRset for
+        // each textual RR(s) of zone_data, and assign it to the corresponding
+        // rr_xxx.
+        const RRsetData zone_data[] = {
+            {"example.org. 300 IN NS ns.example.org.", &rr_ns_},
+            {"example.org. 300 IN A 192.0.2.1", &rr_a_},
+            {"ns.example.org. 300 IN A 192.0.2.2", &rr_ns_a_},
+            {"ns.example.org. 300 IN AAAA 2001:db8::2", &rr_ns_aaaa_},
+            {"cname.example.org. 300 IN CNAME canonical.example.org",
+             &rr_cname_},
+            {"cname.example.org. 300 IN A 192.0.2.3", &rr_cname_a_},
+            {"dname.example.org. 300 IN DNAME target.example.org.",
+             &rr_dname_},
+            {"dname.example.org. 300 IN A 192.0.2.39", &rr_dname_a_},
+            {"dname.example.org. 300 IN NS ns.dname.example.org.",
+             &rr_dname_ns_},
+            {"example.org. 300 IN DNAME example.com.", &rr_dname_apex_},
+            {"child.example.org. 300 IN NS ns.child.example.org.",
+             &rr_child_ns_},
+            {"ns.child.example.org. 300 IN A 192.0.2.153",
+             &rr_child_glue_},
+            {"grand.child.example.org. 300 IN NS ns.grand.child.example.org.",
+             &rr_grandchild_ns_},
+            {"ns.grand.child.example.org. 300 IN AAAA 2001:db8::253",
+             &rr_grandchild_glue_},
+            {"dname.child.example.org. 300 IN DNAME example.com.",
+             &rr_child_dname_},
+            {"example.com. 300 IN A 192.0.2.10", &rr_out_},
+            {"*.wild.example.org. 300 IN A 192.0.2.1", &rr_wild_},
+            {"wild.*.foo.example.org. 300 IN A 192.0.2.1", &rr_emptywild_},
+            {"wild.*.foo.*.bar.example.org. 300 IN A 192.0.2.1",
+             &rr_nested_emptywild_},
+            {"*.nswild.example.org. 300 IN NS nswild.example.", &rr_nswild_},
+            {"*.dnamewild.example.org. 300 IN DNAME dnamewild.example.",
+             &rr_dnamewild_},
+            {NULL, NULL}
+        };
+
+        stringstream zone_data_stream;
+        vector<RRsetPtr*> rrsets;
+        for (unsigned int i = 0; zone_data[i].text != NULL; ++i) {
+            zone_data_stream << zone_data[i].text << "\n";
+            rrsets.push_back(zone_data[i].rrset);
+        }
+
+        vector<RRsetPtr*>::iterator it = rrsets.begin();
+        masterLoad(zone_data_stream, Name::ROOT_NAME(), class_,
+                   boost::bind(setRRset, _1, it));
     }
     // Some data to test with
     const RRClass class_;
-    const Name origin_, ns_name_, cname_name_, dname_name_, child_ns_name_,
-        child_glue_name_, grandchild_ns_name_, grandchild_glue_name_,
-        child_dname_name_;
+    const Name origin_;
     // The zone to torture by tests
     MemoryZone zone_;
 
     /*
      * Some RRsets to put inside the zone.
-     * They are empty, but the MemoryZone does not have a reason to look
-     * inside anyway. We will check it finds them and does not change
-     * the pointer.
      */
-    ConstRRsetPtr
+    RRsetPtr
         // Out of zone RRset
         rr_out_,
         // NS of example.org
@@ -207,16 +238,20 @@ public:
         // A of example.org
         rr_a_;
     RRsetPtr rr_cname_;         // CNAME in example.org (RDATA will be added)
-    ConstRRsetPtr rr_cname_a_; // for mixed CNAME + A case
+    RRsetPtr rr_cname_a_; // for mixed CNAME + A case
     RRsetPtr rr_dname_;         // DNAME in example.org (RDATA will be added)
-    ConstRRsetPtr rr_dname_a_; // for mixed DNAME + A case
-    ConstRRsetPtr rr_dname_ns_; // for mixed DNAME + NS case
-    ConstRRsetPtr rr_dname_apex_; // for mixed DNAME + NS case in the apex
-    ConstRRsetPtr rr_child_ns_; // NS of a child domain (for delegation)
-    ConstRRsetPtr rr_child_glue_; // glue RR of the child domain
-    ConstRRsetPtr rr_grandchild_ns_; // NS below a zone cut (unusual)
-    ConstRRsetPtr rr_grandchild_glue_; // glue RR below a deeper zone cut
-    ConstRRsetPtr rr_child_dname_; // A DNAME under NS
+    RRsetPtr rr_dname_a_; // for mixed DNAME + A case
+    RRsetPtr rr_dname_ns_; // for mixed DNAME + NS case
+    RRsetPtr rr_dname_apex_; // for mixed DNAME + NS case in the apex
+    RRsetPtr rr_child_ns_; // NS of a child domain (for delegation)
+    RRsetPtr rr_child_glue_; // glue RR of the child domain
+    RRsetPtr rr_grandchild_ns_; // NS below a zone cut (unusual)
+    RRsetPtr rr_grandchild_glue_; // glue RR below a deeper zone cut
+    RRsetPtr rr_child_dname_; // A DNAME under NS
+    RRsetPtr rr_wild_;
+    RRsetPtr rr_emptywild_;
+    RRsetPtr rr_nested_emptywild_;
+    RRsetPtr rr_nswild_, rr_dnamewild_;
 
     /**
      * \brief Test one find query to the zone.
@@ -292,33 +327,30 @@ TEST_F(MemoryZoneTest, add) {
 }
 
 TEST_F(MemoryZoneTest, addMultipleCNAMEs) {
-    rr_cname_->addRdata(generic::CNAME("canonical1.example.org."));
     rr_cname_->addRdata(generic::CNAME("canonical2.example.org."));
     EXPECT_THROW(zone_.add(rr_cname_), MemoryZone::AddError);
 }
 
 TEST_F(MemoryZoneTest, addCNAMEThenOther) {
-    rr_cname_->addRdata(generic::CNAME("canonical.example.org."));
     EXPECT_EQ(SUCCESS, zone_.add(rr_cname_));
     EXPECT_THROW(zone_.add(rr_cname_a_), MemoryZone::AddError);
 }
 
 TEST_F(MemoryZoneTest, addOtherThenCNAME) {
-    rr_cname_->addRdata(generic::CNAME("canonical.example.org."));
     EXPECT_EQ(SUCCESS, zone_.add(rr_cname_a_));
     EXPECT_THROW(zone_.add(rr_cname_), MemoryZone::AddError);
 }
 
 TEST_F(MemoryZoneTest, findCNAME) {
     // install CNAME RR
-    rr_cname_->addRdata(generic::CNAME("canonical.example.org."));
     EXPECT_EQ(SUCCESS, zone_.add(rr_cname_));
 
     // Find A RR of the same.  Should match the CNAME
-    findTest(cname_name_, RRType::NS(), Zone::CNAME, true, rr_cname_);
+    findTest(rr_cname_->getName(), RRType::NS(), Zone::CNAME, true, rr_cname_);
 
     // Find the CNAME itself.  Should result in normal SUCCESS
-    findTest(cname_name_, RRType::CNAME(), Zone::SUCCESS, true, rr_cname_);
+    findTest(rr_cname_->getName(), RRType::CNAME(), Zone::SUCCESS, true,
+             rr_cname_);
 }
 
 TEST_F(MemoryZoneTest, findCNAMEUnderZoneCut) {
@@ -339,8 +371,7 @@ TEST_F(MemoryZoneTest, findCNAMEUnderZoneCut) {
 // Having a CNAME there is disallowed too, but it is tested by
 // addOtherThenCNAME and addCNAMEThenOther.
 TEST_F(MemoryZoneTest, addMultipleDNAMEs) {
-    rr_dname_->addRdata(generic::DNAME("dname1.example.org."));
-    rr_dname_->addRdata(generic::DNAME("dname2.example.org."));
+    rr_dname_->addRdata(generic::DNAME("target2.example.org."));
     EXPECT_THROW(zone_.add(rr_dname_), MemoryZone::AddError);
 }
 
@@ -366,7 +397,8 @@ TEST_F(MemoryZoneTest, DNAMEAndNSAtApex) {
     // The NS should be possible to be found, below should be DNAME, not
     // delegation
     findTest(origin_, RRType::NS(), Zone::SUCCESS, true, rr_ns_);
-    findTest(child_ns_name_, RRType::A(), Zone::DNAME, true, rr_dname_apex_);
+    findTest(rr_child_ns_->getName(), RRType::A(), Zone::DNAME, true,
+             rr_dname_apex_);
 }
 
 TEST_F(MemoryZoneTest, NSAndDNAMEAtApex) {
@@ -379,7 +411,6 @@ TEST_F(MemoryZoneTest, NSAndDNAMEAtApex) {
 
 // Search under a DNAME record. It should return the DNAME
 TEST_F(MemoryZoneTest, findBelowDNAME) {
-    rr_dname_->addRdata(generic::DNAME("target.example.org."));
     EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_dname_)));
     findTest(Name("below.dname.example.org"), RRType::A(), Zone::DNAME, true,
         rr_dname_);
@@ -388,13 +419,13 @@ TEST_F(MemoryZoneTest, findBelowDNAME) {
 // Search at the domain with DNAME. It should act as DNAME isn't there, DNAME
 // influences only the data below (see RFC 2672, section 3)
 TEST_F(MemoryZoneTest, findAtDNAME) {
-    rr_dname_->addRdata(generic::DNAME("target.example.org."));
     EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_dname_)));
     EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_dname_a_)));
 
-    findTest(dname_name_, RRType::A(), Zone::SUCCESS, true, rr_dname_a_);
-    findTest(dname_name_, RRType::DNAME(), Zone::SUCCESS, true, rr_dname_);
-    findTest(dname_name_, RRType::TXT(), Zone::NXRRSET, true);
+    const Name dname_name(rr_dname_->getName());
+    findTest(dname_name, RRType::A(), Zone::SUCCESS, true, rr_dname_a_);
+    findTest(dname_name, RRType::DNAME(), Zone::SUCCESS, true, rr_dname_);
+    findTest(dname_name, RRType::TXT(), Zone::NXRRSET, true);
 }
 
 // Try searching something that is both under NS and DNAME, without and with
@@ -458,7 +489,7 @@ TEST_F(MemoryZoneTest, findAny) {
     EXPECT_EQ(0, out_rrsets.size());
 
     RRsetList glue_child_rrsets;
-    findTest(child_glue_name_, RRType::ANY(), Zone::SUCCESS, true,
+    findTest(rr_child_glue_->getName(), RRType::ANY(), Zone::SUCCESS, true,
                 ConstRRsetPtr(), &glue_child_rrsets);
     EXPECT_EQ(rr_child_glue_, glue_child_rrsets.findRRset(RRType::A(),
                                                      RRClass::IN()));
@@ -472,13 +503,13 @@ TEST_F(MemoryZoneTest, findAny) {
 
     // zone cut
     RRsetList child_rrsets;
-    findTest(child_ns_name_, RRType::ANY(), Zone::DELEGATION, true,
+    findTest(rr_child_ns_->getName(), RRType::ANY(), Zone::DELEGATION, true,
              rr_child_ns_, &child_rrsets);
     EXPECT_EQ(0, child_rrsets.size());
 
     // glue for this zone cut
     RRsetList new_glue_child_rrsets;
-    findTest(child_glue_name_, RRType::ANY(), Zone::DELEGATION, true,
+    findTest(rr_child_glue_->getName(), RRType::ANY(), Zone::DELEGATION, true,
                 rr_child_ns_, &new_glue_child_rrsets);
     EXPECT_EQ(0, new_glue_child_rrsets.size());
 }
@@ -495,16 +526,16 @@ TEST_F(MemoryZoneTest, glue) {
     EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_grandchild_glue_)));
 
     // by default glue is hidden due to the zone cut
-    findTest(child_glue_name_, RRType::A(), Zone::DELEGATION, true,
+    findTest(rr_child_glue_->getName(), RRType::A(), Zone::DELEGATION, true,
              rr_child_ns_);
 
 
     // If we do it in the "glue OK" mode, we should find the exact match.
-    findTest(child_glue_name_, RRType::A(), Zone::SUCCESS, true,
+    findTest(rr_child_glue_->getName(), RRType::A(), Zone::SUCCESS, true,
              rr_child_glue_, NULL, NULL, Zone::FIND_GLUE_OK);
 
     // glue OK + NXRRSET case
-    findTest(child_glue_name_, RRType::AAAA(), Zone::NXRRSET, true,
+    findTest(rr_child_glue_->getName(), RRType::AAAA(), Zone::NXRRSET, true,
              ConstRRsetPtr(), NULL, NULL, Zone::FIND_GLUE_OK);
 
     // glue OK + NXDOMAIN case
@@ -517,7 +548,8 @@ TEST_F(MemoryZoneTest, glue) {
     // (This case cannot be tested yet)
 
     // nested cut case.  The glue should be found.
-    findTest(grandchild_glue_name_, RRType::AAAA(), Zone::SUCCESS,
+    findTest(rr_grandchild_glue_->getName(), RRType::AAAA(),
+             Zone::SUCCESS,
              true, rr_grandchild_glue_, NULL, NULL, Zone::FIND_GLUE_OK);
 
     // A non-existent name in nested cut.  This should result in delegation
@@ -544,11 +576,11 @@ TEST_F(MemoryZoneTest, find) {
 
     // These two should be successful
     findTest(origin_, RRType::NS(), Zone::SUCCESS, true, rr_ns_);
-    findTest(ns_name_, RRType::A(), Zone::SUCCESS, true, rr_ns_a_);
+    findTest(rr_ns_a_->getName(), RRType::A(), Zone::SUCCESS, true, rr_ns_a_);
 
     // These domain exist but don't have the provided RRType
     findTest(origin_, RRType::AAAA(), Zone::NXRRSET);
-    findTest(ns_name_, RRType::NS(), Zone::NXRRSET);
+    findTest(rr_ns_a_->getName(), RRType::NS(), Zone::NXRRSET);
 
     // These domains don't exist (and one is out of the zone)
     findTest(Name("nothere.example.org"), RRType::A(), Zone::NXDOMAIN);
@@ -616,14 +648,59 @@ TEST_F(MemoryZoneTest, load) {
     findTest(Name("a.root-servers.net."), RRType::A(), Zone::SUCCESS, false,
         ConstRRsetPtr(), NULL, &rootzone);
     // But this should no longer be here
-    findTest(ns_name_, RRType::AAAA(), Zone::NXDOMAIN, true, ConstRRsetPtr(),
-        NULL, &rootzone);
+    findTest(rr_ns_a_->getName(), RRType::AAAA(), Zone::NXDOMAIN, true,
+             ConstRRsetPtr(), NULL, &rootzone);
 
     // Try loading zone that is wrong in a different way
     EXPECT_THROW(zone_.load(TEST_DATA_DIR "/duplicate_rrset.zone"),
         MasterLoadError);
 }
 
+// Note: once #507 is merged, findTest() would succeed whether or not
+// we load the wildcard correctly, so the test will become meaningless.
+// The plan is to clean them up when we complete #551 (then the effect of
+// load will be indirectly tested via find() tests).
+TEST_F(MemoryZoneTest, loadWildcard) {
+    /*
+     *            example.org.
+     *                 |
+     *                wild (not *.wild, should have wild mark)
+     *                 |
+     *                 *
+     */
+    EXPECT_EQ(SUCCESS, zone_.add(rr_wild_));
+    findTest(Name("wild.example.org"), RRType::A(), Zone::NXRRSET);
+}
+
+// same note as loadWildcard applies.
+TEST_F(MemoryZoneTest, loadEmptyWildcard) {
+    /*
+     *            example.org.
+     *                foo
+     *                 *
+     *               wild
+     */
+    EXPECT_EQ(SUCCESS, zone_.add(rr_emptywild_));
+    findTest(Name("*.foo.example.org"), RRType::A(), Zone::NXRRSET);
+    findTest(Name("foo.example.org"), RRType::A(), Zone::NXRRSET);
+}
+
+// same note as loadWildcard applies.
+TEST_F(MemoryZoneTest, loadNestedEmptyWildcard) {
+    EXPECT_EQ(SUCCESS, zone_.add(rr_nested_emptywild_));
+    findTest(Name("*.foo.*.bar.example.org"), RRType::A(), Zone::NXRRSET);
+    findTest(Name("foo.*.bar.example.org"), RRType::A(), Zone::NXRRSET);
+    findTest(Name("*.bar.example.org"), RRType::A(), Zone::NXRRSET);
+    findTest(Name("bar.example.org"), RRType::A(), Zone::NXRRSET);
+}
+
+TEST_F(MemoryZoneTest, loadBadWildcard) {
+    // We reject loading the zone if it contains a wildcard name for
+    // NS or DNAME.
+    EXPECT_THROW(zone_.add(rr_nswild_), MemoryZone::AddError);
+    EXPECT_THROW(zone_.add(rr_dnamewild_), MemoryZone::AddError);
+}
+
 TEST_F(MemoryZoneTest, swap) {
     // build one zone with some data
     MemoryZone zone1(class_, origin_);
diff --git a/src/lib/datasrc/tests/rbtree_unittest.cc b/src/lib/datasrc/tests/rbtree_unittest.cc
index 2abe14d..82eed63 100644
--- a/src/lib/datasrc/tests/rbtree_unittest.cc
+++ b/src/lib/datasrc/tests/rbtree_unittest.cc
@@ -187,6 +187,30 @@ TEST_F(RBTreeTest, findError) {
                  BadValue);
 }
 
+TEST_F(RBTreeTest, flags) {
+    EXPECT_EQ(RBTree<int>::SUCCESS, rbtree.insert(Name("flags.example"),
+                                                  &rbtnode));
+
+    // by default, flags are all off
+    EXPECT_FALSE(rbtnode->getFlag(RBNode<int>::FLAG_CALLBACK));
+
+    // set operation, by default it enables the flag
+    rbtnode->setFlag(RBNode<int>::FLAG_CALLBACK);
+    EXPECT_TRUE(rbtnode->getFlag(RBNode<int>::FLAG_CALLBACK));
+
+    // try disable the flag explicitly
+    rbtnode->setFlag(RBNode<int>::FLAG_CALLBACK, false);
+    EXPECT_FALSE(rbtnode->getFlag(RBNode<int>::FLAG_CALLBACK));
+
+    // try enable the flag explicitly
+    rbtnode->setFlag(RBNode<int>::FLAG_CALLBACK, true);
+    EXPECT_TRUE(rbtnode->getFlag(RBNode<int>::FLAG_CALLBACK));
+
+    // setting an unknown flag will trigger an exception
+    EXPECT_THROW(rbtnode->setFlag(static_cast<RBNode<int>::Flags>(2), true),
+                 isc::InvalidParameter);
+}
+
 bool
 testCallback(const RBNode<int>&, bool* callack_checker) {
     *callack_checker = true;
@@ -198,16 +222,16 @@ TEST_F(RBTreeTest, callback) {
     EXPECT_EQ(RBTree<int>::SUCCESS, rbtree.insert(Name("callback.example"),
                                                   &rbtnode));
     rbtnode->setData(RBNode<int>::NodeDataPtr(new int(1)));
-    EXPECT_FALSE(rbtnode->isCallbackEnabled());
+    EXPECT_FALSE(rbtnode->getFlag(RBNode<int>::FLAG_CALLBACK));
 
     // enable/re-disable callback
-    rbtnode->enableCallback();
-    EXPECT_TRUE(rbtnode->isCallbackEnabled());
-    rbtnode->disableCallback();
-    EXPECT_FALSE(rbtnode->isCallbackEnabled());
+    rbtnode->setFlag(RBNode<int>::FLAG_CALLBACK);
+    EXPECT_TRUE(rbtnode->getFlag(RBNode<int>::FLAG_CALLBACK));
+    rbtnode->setFlag(RBNode<int>::FLAG_CALLBACK, false);
+    EXPECT_FALSE(rbtnode->getFlag(RBNode<int>::FLAG_CALLBACK));
 
     // enable again for subsequent tests
-    rbtnode->enableCallback();
+    rbtnode->setFlag(RBNode<int>::FLAG_CALLBACK);
     // add more levels below and above the callback node for partial match.
     RBNode<int>* subrbtnode;
     EXPECT_EQ(RBTree<int>::SUCCESS, rbtree.insert(Name("sub.callback.example"),
@@ -221,9 +245,9 @@ TEST_F(RBTreeTest, callback) {
     // it.
     EXPECT_EQ(RBTree<int>::EXACTMATCH, rbtree.find(Name("callback.example"),
                                                    &rbtnode));
-    EXPECT_TRUE(rbtnode->isCallbackEnabled());
-    EXPECT_FALSE(subrbtnode->isCallbackEnabled());
-    EXPECT_FALSE(parentrbtnode->isCallbackEnabled());
+    EXPECT_TRUE(rbtnode->getFlag(RBNode<int>::FLAG_CALLBACK));
+    EXPECT_FALSE(subrbtnode->getFlag(RBNode<int>::FLAG_CALLBACK));
+    EXPECT_FALSE(parentrbtnode->getFlag(RBNode<int>::FLAG_CALLBACK));
 
     // check if the callback is called from find()
     RBTreeNodeChain<int> node_path1;
@@ -236,7 +260,7 @@ TEST_F(RBTreeTest, callback) {
     // enable callback at the parent node, but it doesn't have data so
     // the callback shouldn't be called.
     RBTreeNodeChain<int> node_path2;
-    parentrbtnode->enableCallback();
+    parentrbtnode->setFlag(RBNode<int>::FLAG_CALLBACK);
     callback_called = false;
     EXPECT_EQ(RBTree<int>::EXACTMATCH,
               rbtree.find(Name("callback.example"), &crbtnode, node_path2,
diff --git a/src/lib/dns/message.cc b/src/lib/dns/message.cc
index e9ab1b9..c966116 100644
--- a/src/lib/dns/message.cc
+++ b/src/lib/dns/message.cc
@@ -338,6 +338,14 @@ Message::removeRRset(const Section section, RRsetIterator& iterator) {
     return (removed);
 }
 
+void
+Message::clearSection(const Section section) {
+    if (section >= MessageImpl::NUM_SECTIONS) {
+        isc_throw(OutOfRange, "Invalid message section: " << section);
+    }
+    impl_->rrsets_[section].clear();
+    impl_->counts_[section] = 0;
+}
 
 void
 Message::addQuestion(const QuestionPtr question) {
@@ -769,6 +777,27 @@ Message::clear(Mode mode) {
 }
 
 void
+Message::appendSection(const Section section, const Message& source) {
+    if (section >= MessageImpl::NUM_SECTIONS) {
+        isc_throw(OutOfRange, "Invalid message section: " << section);
+    }
+
+    if (section == SECTION_QUESTION) {
+        for (QuestionIterator qi = source.beginQuestion();
+             qi != source.endQuestion();
+             ++qi) {
+            addQuestion(*qi);
+        }
+    } else {
+        for (RRsetIterator rrsi = source.beginSection(section);
+             rrsi != source.endSection(section);
+             ++rrsi) {
+            addRRset(section, *rrsi);
+        }
+    }
+}
+
+void
 Message::makeResponse() {
     if (impl_->mode_ != Message::PARSE) {
         isc_throw(InvalidMessageOperation,
diff --git a/src/lib/dns/message.h b/src/lib/dns/message.h
index de82085..11167d2 100644
--- a/src/lib/dns/message.h
+++ b/src/lib/dns/message.h
@@ -483,6 +483,11 @@ public:
     /// found in the specified section.
     bool removeRRset(const Section section, RRsetIterator& iterator);
 
+    /// \brief Remove all RRSets from the given Section
+    ///
+    /// \param section Section to remove all rrsets from
+    void clearSection(const Section section);
+
     // The following methods are not currently implemented.
     //void removeQuestion(QuestionPtr question);
     // notyet:
@@ -493,6 +498,13 @@ public:
     /// specified mode.
     void clear(Mode mode);
 
+    /// \brief Adds all rrsets from the source the given section in the
+    /// source message to the same section of this message
+    ///
+    /// \param section the section to append
+    /// \param target The source Message
+    void appendSection(const Section section, const Message& source);
+
     /// \brief Prepare for making a response from a request.
     ///
     /// This will clear the DNS header except those fields that should be kept
diff --git a/src/lib/dns/rrset.h b/src/lib/dns/rrset.h
index 4fc6cdc..acb4224 100644
--- a/src/lib/dns/rrset.h
+++ b/src/lib/dns/rrset.h
@@ -229,8 +229,8 @@ public:
 
     /// \brief Updates the owner name of the \c RRset.
     ///
-    /// \param name A reference to a \c RRTTL class object to be copied as the
-    /// new TTL.
+    /// \param name A reference to a \c Name class object to be copied as the
+    /// new name.
     virtual void setName(const Name& name) = 0;
 
     /// \brief Updates the TTL of the \c RRset.
@@ -588,8 +588,8 @@ public:
     /// internal copy of the \c name involves resource allocation and it
     /// fails.
     ///
-    /// \param name A reference to a \c RRTTL class object to be copied as the
-    /// new TTL.
+    /// \param name A reference to a \c Name class object to be copied as the
+    /// new name.
     virtual void setName(const Name& name);
 
     /// \brief Updates the TTL of the \c RRset.
@@ -720,7 +720,7 @@ public:
     void removeRRsig() { rrsig_ = RRsetPtr(); }
 
     /// \brief Return a pointer to this RRset's RRSIG RRset
-    RRsetPtr getRRsig() { return (rrsig_); }
+    RRsetPtr getRRsig() const { return (rrsig_); }
 private:
     RRsetPtr rrsig_;
 };
diff --git a/src/lib/dns/tests/message_unittest.cc b/src/lib/dns/tests/message_unittest.cc
index ee1375a..92adbc9 100644
--- a/src/lib/dns/tests/message_unittest.cc
+++ b/src/lib/dns/tests/message_unittest.cc
@@ -297,6 +297,75 @@ TEST_F(MessageTest, removeRRset) {
     EXPECT_EQ(2, message_render.getRRCount(Message::SECTION_ANSWER));
 }
 
+TEST_F(MessageTest, clearQuestionSection) {
+    QuestionPtr q(new Question(Name("www.example.com"), RRClass::IN(),
+                               RRType::A()));
+    message_render.addQuestion(q);
+    ASSERT_EQ(1, message_render.getRRCount(Message::SECTION_QUESTION));
+
+    message_render.clearSection(Message::SECTION_QUESTION);
+    EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_QUESTION));
+}
+
+
+TEST_F(MessageTest, clearAnswerSection) {
+    // Add two RRsets, check they are present, clear the section,
+    // check if they are gone.
+    message_render.addRRset(Message::SECTION_ANSWER, rrset_a);
+    message_render.addRRset(Message::SECTION_ANSWER, rrset_aaaa);
+    ASSERT_TRUE(message_render.hasRRset(Message::SECTION_ANSWER, test_name,
+        RRClass::IN(), RRType::A()));
+    ASSERT_TRUE(message_render.hasRRset(Message::SECTION_ANSWER, test_name,
+        RRClass::IN(), RRType::AAAA()));
+    ASSERT_EQ(3, message_render.getRRCount(Message::SECTION_ANSWER));
+
+    message_render.clearSection(Message::SECTION_ANSWER);
+    EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ANSWER, test_name,
+        RRClass::IN(), RRType::A()));
+    EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ANSWER, test_name,
+        RRClass::IN(), RRType::AAAA()));
+    EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_ANSWER));
+}
+
+TEST_F(MessageTest, clearAuthoritySection) {
+    // Add two RRsets, check they are present, clear the section,
+    // check if they are gone.
+    message_render.addRRset(Message::SECTION_AUTHORITY, rrset_a);
+    message_render.addRRset(Message::SECTION_AUTHORITY, rrset_aaaa);
+    ASSERT_TRUE(message_render.hasRRset(Message::SECTION_AUTHORITY, test_name,
+        RRClass::IN(), RRType::A()));
+    ASSERT_TRUE(message_render.hasRRset(Message::SECTION_AUTHORITY, test_name,
+        RRClass::IN(), RRType::AAAA()));
+    ASSERT_EQ(3, message_render.getRRCount(Message::SECTION_AUTHORITY));
+
+    message_render.clearSection(Message::SECTION_AUTHORITY);
+    EXPECT_FALSE(message_render.hasRRset(Message::SECTION_AUTHORITY, test_name,
+        RRClass::IN(), RRType::A()));
+    EXPECT_FALSE(message_render.hasRRset(Message::SECTION_AUTHORITY, test_name,
+        RRClass::IN(), RRType::AAAA()));
+    EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_AUTHORITY));
+}
+
+TEST_F(MessageTest, clearAdditionalSection) {
+    // Add two RRsets, check they are present, clear the section,
+    // check if they are gone.
+    message_render.addRRset(Message::SECTION_ADDITIONAL, rrset_a);
+    message_render.addRRset(Message::SECTION_ADDITIONAL, rrset_aaaa);
+    ASSERT_TRUE(message_render.hasRRset(Message::SECTION_ADDITIONAL, test_name,
+        RRClass::IN(), RRType::A()));
+    ASSERT_TRUE(message_render.hasRRset(Message::SECTION_ADDITIONAL, test_name,
+        RRClass::IN(), RRType::AAAA()));
+    ASSERT_EQ(3, message_render.getRRCount(Message::SECTION_ADDITIONAL));
+
+    message_render.clearSection(Message::SECTION_ADDITIONAL);
+    EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ADDITIONAL, test_name,
+        RRClass::IN(), RRType::A()));
+    EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ADDITIONAL, test_name,
+        RRClass::IN(), RRType::AAAA()));
+    EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_ADDITIONAL));
+}
+
+
 TEST_F(MessageTest, badBeginSection) {
     // valid cases are tested via other tests
     EXPECT_THROW(message_render.beginSection(Message::SECTION_QUESTION),
@@ -311,6 +380,63 @@ TEST_F(MessageTest, badEndSection) {
     EXPECT_THROW(message_render.endSection(bogus_section), OutOfRange);
 }
 
+TEST_F(MessageTest, appendSection) {
+    Message target(Message::RENDER);
+
+    // Section check
+    EXPECT_THROW(target.appendSection(bogus_section, message_render),
+                 OutOfRange);
+
+    // Make sure nothing is copied if there is nothing to copy
+    target.appendSection(Message::SECTION_QUESTION, message_render);
+    EXPECT_EQ(0, target.getRRCount(Message::SECTION_QUESTION));
+    target.appendSection(Message::SECTION_ANSWER, message_render);
+    EXPECT_EQ(0, target.getRRCount(Message::SECTION_ANSWER));
+    target.appendSection(Message::SECTION_AUTHORITY, message_render);
+    EXPECT_EQ(0, target.getRRCount(Message::SECTION_AUTHORITY));
+    target.appendSection(Message::SECTION_ADDITIONAL, message_render);
+    EXPECT_EQ(0, target.getRRCount(Message::SECTION_ADDITIONAL));
+
+    // Now add some data, copy again, and see if it got added
+    message_render.addQuestion(Question(Name("test.example.com"),
+                                        RRClass::IN(), RRType::A()));
+    message_render.addRRset(Message::SECTION_ANSWER, rrset_a);
+    message_render.addRRset(Message::SECTION_AUTHORITY, rrset_a);
+    message_render.addRRset(Message::SECTION_ADDITIONAL, rrset_a);
+    message_render.addRRset(Message::SECTION_ADDITIONAL, rrset_aaaa);
+
+    target.appendSection(Message::SECTION_QUESTION, message_render);
+    EXPECT_EQ(1, target.getRRCount(Message::SECTION_QUESTION));
+
+    target.appendSection(Message::SECTION_ANSWER, message_render);
+    EXPECT_EQ(2, target.getRRCount(Message::SECTION_ANSWER));
+    EXPECT_TRUE(target.hasRRset(Message::SECTION_ANSWER, test_name,
+        RRClass::IN(), RRType::A()));
+
+    target.appendSection(Message::SECTION_AUTHORITY, message_render);
+    EXPECT_EQ(2, target.getRRCount(Message::SECTION_AUTHORITY));
+    EXPECT_TRUE(target.hasRRset(Message::SECTION_AUTHORITY, test_name,
+        RRClass::IN(), RRType::A()));
+
+    target.appendSection(Message::SECTION_ADDITIONAL, message_render);
+    EXPECT_EQ(3, target.getRRCount(Message::SECTION_ADDITIONAL));
+    EXPECT_TRUE(target.hasRRset(Message::SECTION_ADDITIONAL, test_name,
+        RRClass::IN(), RRType::A()));
+    EXPECT_TRUE(target.hasRRset(Message::SECTION_ADDITIONAL, test_name,
+        RRClass::IN(), RRType::AAAA()));
+
+    // One more test, test to see if the section gets added, not replaced
+    Message source2(Message::RENDER);
+    source2.addRRset(Message::SECTION_ANSWER, rrset_aaaa);
+    target.appendSection(Message::SECTION_ANSWER, source2);
+    EXPECT_EQ(3, target.getRRCount(Message::SECTION_ANSWER));
+    EXPECT_TRUE(target.hasRRset(Message::SECTION_ANSWER, test_name,
+        RRClass::IN(), RRType::A()));
+    EXPECT_TRUE(target.hasRRset(Message::SECTION_ANSWER, test_name,
+        RRClass::IN(), RRType::AAAA()));
+    
+}
+
 TEST_F(MessageTest, fromWire) {
     factoryFromFile(message_parse, "message_fromWire1");
     EXPECT_EQ(0x1035, message_parse.getQid());
diff --git a/src/lib/log/tests/Makefile.am b/src/lib/log/tests/Makefile.am
index 01973c9..e3a023a 100644
--- a/src/lib/log/tests/Makefile.am
+++ b/src/lib/log/tests/Makefile.am
@@ -11,6 +11,8 @@ endif
 
 CLEANFILES = *.gcno *.gcda
 
+EXTRA_DIST = localdef.mes
+
 TESTS =
 if HAVE_GTEST
 TESTS += run_unittests
diff --git a/src/lib/nsas/zone_entry.h b/src/lib/nsas/zone_entry.h
index c105f70..c819692 100644
--- a/src/lib/nsas/zone_entry.h
+++ b/src/lib/nsas/zone_entry.h
@@ -74,6 +74,9 @@ public:
      * \param name Name of the zone
      * \param class_code Class of this zone (zones of different classes have
      *     different objects.
+     * \param nameserver_table Hashtable of NameServerEntry objects for
+     *     this zone
+     * \param namesever_lru LRU for the nameserver entries
      * \todo Move to cc file, include the lookup (if NSAS uses resolver for
      *     everything)
      */
diff --git a/src/lib/resolve/Makefile.am b/src/lib/resolve/Makefile.am
index 8edb594..c9f1326 100644
--- a/src/lib/resolve/Makefile.am
+++ b/src/lib/resolve/Makefile.am
@@ -10,8 +10,9 @@ AM_CXXFLAGS = $(B10_CXXFLAGS)
 CLEANFILES = *.gcno *.gcda
 
 lib_LTLIBRARIES = libresolve.la
-libresolve_la_SOURCES = resolve.h
+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_LIBADD = $(top_builddir)/src/lib/dns/libdns++.la
 libresolve_la_LIBADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
diff --git a/src/lib/resolve/resolve.cc b/src/lib/resolve/resolve.cc
new file mode 100644
index 0000000..312c89d
--- /dev/null
+++ b/src/lib/resolve/resolve.cc
@@ -0,0 +1,58 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <resolve/resolve.h>
+
+using namespace isc::dns;
+
+namespace {
+    class SectionInserter {
+    public:
+        SectionInserter(MessagePtr message, const Message::Section sect) :
+            message_(message), section_(sect)
+        {}
+        void operator()(const RRsetPtr rrset) {
+            message_->addRRset(section_, rrset, true);
+        }
+        MessagePtr message_;
+        const Message::Section section_;
+    };
+}
+
+namespace isc {
+namespace resolve {
+
+void
+makeErrorMessage(MessagePtr answer_message,
+                 const Rcode& error_code)
+{
+    answer_message->clearSection(Message::SECTION_ANSWER);
+    answer_message->clearSection(Message::SECTION_AUTHORITY);
+    answer_message->clearSection(Message::SECTION_ADDITIONAL);
+
+    answer_message->setRcode(error_code);
+}
+
+void copyResponseMessage(const Message& source, MessagePtr target) {
+    target->setRcode(source.getRcode());
+
+    target->appendSection(Message::SECTION_ANSWER, source);
+    target->appendSection(Message::SECTION_AUTHORITY, source);
+    target->appendSection(Message::SECTION_ADDITIONAL, source);
+}
+
+
+} // namespace resolve
+} // namespace isc
+
diff --git a/src/lib/resolve/resolve.h b/src/lib/resolve/resolve.h
index 63c9b58..b08c30c 100644
--- a/src/lib/resolve/resolve.h
+++ b/src/lib/resolve/resolve.h
@@ -15,6 +15,46 @@
 #ifndef _ISC_RESOLVE_H
 #define _ISC_RESOLVE_H 1
 
+/// This file includes all other libresolve headers, and provides
+/// several helper functions used in resolving.
+
 #include <resolve/resolver_interface.h>
 #include <resolve/resolver_callback.h>
+#include <resolve/response_classifier.h>
+
+#include <dns/rcode.h>
+
+namespace isc {
+namespace resolve {
+
+/// \brief Create an error response
+///
+/// Clears the answer, authority, and additional section of the
+/// given MessagePtr and sets the given error code
+///
+/// Notes: Assuming you have already done initial preparations
+/// on the given answer message (copy the opcode, qid and question
+/// section), you can simply use this to create an error response.
+///
+/// \param answer_message The message to clear and place the error in
+/// \param question The question to add to the
+/// \param error_code The error Rcode
+void makeErrorMessage(isc::dns::MessagePtr answer_message,
+                      const isc::dns::Rcode& error_code);
+
+/// \brief Copies the parts relevant for a DNS response to the
+/// target message
+///
+/// This adds all the RRsets in the answer, authority and
+/// additional sections to the target, as well as the response
+/// code
+/// \param source The Message to copy the data from
+/// \param target The Message to copy the data to
+void copyResponseMessage(const isc::dns::Message& source,
+                         isc::dns::MessagePtr target);
+
+
+} // namespace resolve
+} // namespace isc
+
 #endif // ISC_RESOLVE_H_
diff --git a/src/lib/resolve/response_classifier.cc b/src/lib/resolve/response_classifier.cc
new file mode 100644
index 0000000..45e9cbc
--- /dev/null
+++ b/src/lib/resolve/response_classifier.cc
@@ -0,0 +1,274 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <cstddef>
+#include <vector>
+
+#include <resolve/response_classifier.h>
+#include <dns/name.h>
+#include <dns/opcode.h>
+#include <dns/rcode.h>
+#include <dns/rrset.h>
+
+using namespace isc::dns;
+using namespace std;
+
+namespace isc {
+namespace resolve {
+
+// Classify the response in the "message" object.
+
+ResponseClassifier::Category ResponseClassifier::classify(
+    const Question& question, const Message& message, 
+    Name& cname_target, unsigned int& cname_count, bool tcignore
+    )
+{
+    // Check header bits
+    if (!message.getHeaderFlag(Message::HEADERFLAG_QR)) {
+        return (NOTRESPONSE);   // Query-response bit not set in the response
+    }
+
+    // We only recognise responses to queries here
+    if (message.getOpcode() != Opcode::QUERY()) {
+        return (OPCODE);
+    }
+
+    // Apparently have a response.  There must be a single question in it...
+    const vector<QuestionPtr> msgquestion(message.beginQuestion(),
+            message.endQuestion());
+    if (msgquestion.size() != 1) {
+        return (NOTONEQUEST); // Not one question in response question section
+    }
+
+    // ... and the question should be equal to the question given.
+    // XXX: This means that "question" may not be the question sent by the
+    // client.  In the case of a CNAME response, the qname of subsequent
+    // questions needs to be altered.
+    if (question != *(msgquestion[0])) {
+        return (MISMATQUEST);
+    }
+
+    // Check for Rcode-related errors.
+    const Rcode& rcode = message.getRcode();
+    if (rcode != Rcode::NOERROR()) {
+        if (rcode == Rcode::NXDOMAIN()) {
+
+            // No such domain.  According to RFC2308, the domain referred to by
+            // the QNAME does not exist, although there may be a CNAME in the
+            // answer section and there may be an SOA and/or NS RRs in the
+            // authority section (ignoring any DNSSEC RRs for now).
+            //
+            // Note the "may".  There may not be anything.  Also, note that if
+            // there is a CNAME in the answer section, the authoritative server
+            // has verified that the name given in the CNAME's RDATA field does
+            // not exist. And that if a CNAME is returned in the answer, then
+            // the QNAME of the RRs in the authority section will refer to the
+            // authority for the CNAME's RDATA and not to the original question.
+            //
+            // Without doing further classification, it is sufficient to say
+            // that if an NXDOMAIN is received, there was no translation of the
+            // QNAME available.
+            return (NXDOMAIN);  // Received NXDOMAIN from parent.
+
+        } else {
+
+            // Not NXDOMAIN but not NOERROR either.  Must be an RCODE-related
+            // error.
+            return (RCODE);
+        }
+    }
+
+    // All seems OK and we can start looking at the content.  However, one
+    // more header check remains - was the response truncated?  If so, we'll
+    // probably want to re-query over TCP.  However, in some circumstances we
+    // might want to go with what we have.  So give the caller the option of
+    // ignoring the TC bit.
+    if (message.getHeaderFlag(Message::HEADERFLAG_TC) && (!tcignore)) {
+        return (TRUNCATED);
+    }
+
+    // By the time we get here, we're assured that the packet format is correct.
+    // We now need to decide as to whether it is an answer, a CNAME, or a
+    // referral.  For this, we need to inspect the contents of the answer
+    // and authority sections.
+    const vector<RRsetPtr> answer(
+            message.beginSection(Message::SECTION_ANSWER),
+            message.endSection(Message::SECTION_ANSWER)
+            );
+    const vector<RRsetPtr> authority(
+            message.beginSection(Message::SECTION_AUTHORITY),
+            message.endSection(Message::SECTION_AUTHORITY)
+            );
+
+    // If there is nothing in the answer section, it is a referral - unless
+    // there is nothing in the authority section
+    if (answer.empty()) {
+        if (authority.empty()) {
+            return (EMPTY);
+        } else {
+            return (REFERRAL);
+        }
+    }
+
+    // Look at two cases - one RRset in the answer and multiple RRsets in
+    // the answer.
+    if (answer.size() == 1) {
+
+        // Does the name and class of the answer match that of the question?
+        if ((answer[0]->getName() == question.getName()) &&
+            (answer[0]->getClass() == question.getClass())) {
+
+            // It does.  How about the type of the response?  The response
+            // is an answer if the type matches that of the question, or if the
+            // question was for type ANY.  It is a CNAME reply if the answer
+            // type is CNAME.  And it is an error for anything else.
+            if ((answer[0]->getType() == question.getType()) ||
+                (question.getType() == RRType::ANY())) {
+                return (ANSWER);
+            } else if (answer[0]->getType() == RRType::CNAME()) {
+                RdataIteratorPtr it = answer[0]->getRdataIterator();
+                cname_target = Name(it->getCurrent().toText());
+                ++cname_count;
+                return (CNAME);
+            } else {
+                return (INVTYPE);
+            }
+        }
+        else {
+
+            // Either the name and/or class of the reply don't match that of
+            // the question.
+            return (INVNAMCLASS);
+        }
+    }
+
+    // There are multiple RRsets in the answer. They should all have the same
+    // QCLASS, else there is some error in the response.
+    for (int i = 1; i < answer.size(); ++i) {
+        if (answer[0]->getClass() != answer[i]->getClass()) {
+            return (MULTICLASS);
+        }
+    }
+
+    // If the request type was ANY and they all have the same QNAME, we have
+    // an answer.  But if they don't have the same QNAME, we must have an error;
+    // the only way we could get different QNAMES in an answer is if one were a
+    // CNAME - in which case there should no other record types at that QNAME.
+    if (question.getType() == RRType::ANY()) {
+        bool all_same = true;
+        for (int i = 1; (i < answer.size()) && all_same; ++i) {
+            all_same = (answer[0]->getName() == answer[i]->getName());
+        }
+        if (all_same) {
+            return (ANSWER);
+        } else {
+            return (EXTRADATA);
+        }
+    }
+
+    // Multiple RRs in the answer, and not all the same QNAME.  This
+    // is either an answer, a CNAME (in either case, there could be multiple
+    // CNAMEs in the chain) or an error.
+    //
+    // So we need to follow the CNAME chain to resolve this.  For this to work:
+    //
+    // a) There must be one RR that matches the name, class and type of
+    //    the question, and this is a CNAME.
+    // b) The CNAME chain is followed until the end of the chain does not
+    //    exist (answer is a CNAME) or it is not of type CNAME (ANSWER).
+    //
+    // In the latter case, if there are additional RRs, it must be an error.
+
+    vector<RRsetPtr> ansrrset(answer);
+    vector<int> present(ansrrset.size(), 1);
+    return cnameChase(question.getName(), question.getType(),
+        cname_target, cname_count,
+        ansrrset, present, ansrrset.size());
+}
+
+// Search the CNAME chain.
+ResponseClassifier::Category ResponseClassifier::cnameChase(
+    const Name& qname, const RRType& qtype,
+    Name& cname_target, unsigned int& cname_count,
+    vector<RRsetPtr>& ansrrset, vector<int>& present, size_t size)
+{
+    // Search through the vector of RRset pointers until we find one with the
+    // right QNAME.
+    for (int i = 0; i < ansrrset.size(); ++i) {
+        if (present[i]) {
+
+            // This entry has not been logically removed, so look at it.
+            if (ansrrset[i]->getName() == qname) {
+
+                // QNAME match.  If this RRset is a CNAME, remove it from
+                // further consideration.  If nothing is left, the end of the
+                // chain is a CNAME so this is a CNAME.  Otherwise replace
+                // the name with the RDATA of the CNAME and call ourself
+                // recursively.
+                if (ansrrset[i]->getType() == RRType::CNAME()) {
+
+                    // Don't consider it in the next iteration (although we
+                    // can still access it for now).
+                    present[i] = 0;
+                    --size;
+                    if (size == 0) {
+                        RdataIteratorPtr it = ansrrset[i]->getRdataIterator();
+                        cname_target = Name(it->getCurrent().toText());
+                        return (CNAME);
+                    } else {
+                        if (ansrrset[i]->getRdataCount() != 1) {
+
+                            // Multiple RDATA for a CNAME?  This is invalid.
+
+                            return (NOTSINGLE);
+                        }
+                        RdataIteratorPtr it = ansrrset[i]->getRdataIterator();
+                        Name newname(it->getCurrent().toText());
+
+                        // Increase CNAME count, and continue
+                        return cnameChase(newname, qtype, cname_target,
+                            ++cname_count, ansrrset, present, size);
+                    }
+
+                } else {
+
+                    // We've got here because the element is not a CNAME.  If
+                    // this is the last element and the type is the one we are
+                    // after, we've found the answer, or it is an error.  If
+                    // there is more than one RRset left in the list we are
+                    // searching, we have extra data in the answer.
+                    if (ansrrset[i]->getType() == qtype) {
+                        if (size == 1) {
+                            return (ANSWERCNAME);
+                        } else {
+                            return (EXTRADATA);
+                        }
+                    }
+                    return (INVTYPE);
+                }
+            }
+        }
+    }
+
+    // We get here if we've dropped off the end of the list without finding the
+    // QNAME we are looking for.  This means that the CNAME chain has ended
+    // but there are additional RRsets in the data.
+
+    return (EXTRADATA);
+}
+
+} // namespace resolve
+} // namespace isc
diff --git a/src/lib/resolve/response_classifier.h b/src/lib/resolve/response_classifier.h
new file mode 100644
index 0000000..bee0628
--- /dev/null
+++ b/src/lib/resolve/response_classifier.h
@@ -0,0 +1,156 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#ifndef __RESPONSE_CLASSIFIER_H
+#define __RESPONSE_CLASSIFIER_H
+
+#include <cstddef>
+
+#include <dns/question.h>
+#include <dns/message.h>
+#include <dns/question.h>
+
+#define RESOLVER_MAX_CNAME_CHAIN    16
+
+namespace isc {
+namespace resolve {
+
+/// \brief Classify Server Response
+///
+/// This class is used in the recursive server.  It is passed an answer received
+/// from an upstream server and categorises it.
+///
+/// TODO: The code here does not take into account any EDNS0 fields.
+
+class ResponseClassifier {
+public:
+
+    /// \brief Category of Answer
+    ///
+    /// In the valid answers, not the distinction between REFERRAL and CNAME.
+    /// A REFERRAL answer means that the answer section of the message is
+    /// empty, but there is something in the authority section.  A CNAME means
+    /// that the answer section contains one or more CNAMES in a chain that
+    /// do not end with a non-CNAME RRset.
+    enum Category {
+
+        // Codes indicating that a message is valid.
+
+        ANSWER,             ///< Response contains the answer
+        ANSWERCNAME,        ///< Response was a CNAME chain ending in an answer
+        CNAME,              ///< Response was a CNAME
+        NXDOMAIN,           ///< Response was an NXDOMAIN
+        REFERRAL,           ///< Response contains a referral
+
+        // Codes indicating that a message is invalid.  Note that the error()
+        // method relies on these appearing after the "message valid" codes.
+
+        EMPTY,              ///< No answer or authority sections
+        EXTRADATA,          ///< Answer section contains more RRsets than needed
+        INVNAMCLASS,        ///< Invalid name or class in answer
+        INVTYPE,            ///< Name/class of answer correct, type is wrong
+        MISMATQUEST,        ///< Response question section != question
+        MULTICLASS,         ///< Multiple classes in multi-RR answer
+        NOTONEQUEST,        ///< Not one question in response question section
+        NOTRESPONSE,        ///< Response has the Query/Response bit clear
+        NOTSINGLE,          ///< CNAME has multiple RDATA elements.
+        OPCODE,             ///< Opcode field does not indicate a query
+        RCODE,              ///< RCODE indicated an error
+        TRUNCATED           ///< Response was truncated
+    };
+
+    /// \brief Check Error
+    ///
+    /// An inline routine to quickly classify whether the return category is
+    /// an error or not.  This makes use of internal knowledge of the order of
+    /// codes in the Category enum.
+    ///
+    /// \param code Return category from classify()
+    ///
+    /// \return true if the category is an error, false if not.
+    static bool error(Category code) {
+        return (code > REFERRAL);
+    }
+
+    /// \brief Classify
+    ///
+    /// Classify the response in the "message" object.
+    ///
+    /// \param question Question that was sent to the server
+    /// \param message Pointer to the associated response from the server.
+    /// \param cname_target If the message contains an (unfinished) CNAME
+    /// chain, this Name will be replaced by the target of the last CNAME
+    /// in the chain
+    /// \param cname_count This unsigned int will be incremented with
+    /// the number of CNAMEs followed
+    /// \param tcignore If set, the TC bit in a response packet is
+    /// ignored.  Otherwise the error code TRUNCATED will be returned.  The
+    /// only time this is likely to be used is in development where we are not
+    /// going to fail over to TCP and will want to use what is returned, even
+    /// if some of the response was lost.
+    static Category classify(const isc::dns::Question& question,
+            const isc::dns::Message& message, 
+            isc::dns::Name& cname_target, unsigned int& cname_count,
+            bool tcignore = false);
+
+private:
+    /// \brief Follow CNAMEs
+    ///
+    /// Given a QNAME and an answer section that contains CNAMEs, assume that
+    /// they form a CNAME chain and search through them.  Possible outcomes
+    /// are:
+    ///
+    /// a) All CNAMES and they form a chain.  The result is a referral.
+    /// b) All but one are CNAMES and they form a chain.  The other is pointed
+    ///    to by the last element of the chain and is the correct QTYPE.  The
+    ///    result is an answer.
+    /// c) Having followed the CNAME chain as far as we can, there is one
+    ///    remaining RRset that is of the wrong type, or there are multiple
+    ///    RRsets remaining.  return the EXTRADATA code.
+    ///
+    /// \param qname Question name we are searching for
+    /// \param qtype Question type we are search for.  (This is assumed not
+    /// to be "ANY".)
+    /// \param ansrrset Vector of RRsetPtr pointing to the RRsets we are
+    /// considering.
+    /// \param present Array of "int" the same size of ansrrset, with each
+    /// element set to "1" to allow the corresponding element of ansrrset to
+    /// be checked, and "0" to skip it.  This might be premature optimisation,
+    /// but the algorithm would otherwise involve duplicating the RRset
+    /// vector then removing elements from random positions one by one.  As
+    /// each removal involves the destruction of an "xxxPtr" element (which
+    /// presently is implemented by boost::shared_ptr), the overhad of memory
+    /// management seemed high.  This solution imposes some additional loop
+    /// cycles, but that should be minimal compared with the overhead of the
+    /// memory management.
+    /// \param cname_target If the message contains an (unfinished) CNAME
+    /// chain, this Name will be replaced by the target of the last CNAME
+    /// in the chain
+    /// \param cname_count This unsigned int will be incremented with
+    /// the number of CNAMEs followed
+    /// \param size Number of elements to check.  See description of \c present
+    /// for details.
+    static Category cnameChase(const isc::dns::Name& qname,
+        const isc::dns::RRType& qtype,
+        isc::dns::Name& cname_target, unsigned int& cname_count,
+        std::vector<isc::dns::RRsetPtr>& ansrrset, std::vector<int>& present,
+        size_t size);
+};
+
+#endif // __RESPONSE_CLASSIFIER_H
+
+} // namespace resolve
+} // namespace isc
diff --git a/src/lib/resolve/tests/Makefile.am b/src/lib/resolve/tests/Makefile.am
index e064cc0..3b5f41a 100644
--- a/src/lib/resolve/tests/Makefile.am
+++ b/src/lib/resolve/tests/Makefile.am
@@ -14,10 +14,13 @@ 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 += resolve_unittest.cc
 run_unittests_SOURCES += resolver_callback_unittest.cc
+run_unittests_SOURCES += response_classifier_unittest.cc
 
 run_unittests_LDADD = $(GTEST_LDADD)
 run_unittests_LDADD +=  $(top_builddir)/src/lib/resolve/libresolve.la
+run_unittests_LDADD +=  $(top_builddir)/src/lib/dns/libdns++.la
 
 endif
 
diff --git a/src/lib/resolve/tests/resolve_unittest.cc b/src/lib/resolve/tests/resolve_unittest.cc
new file mode 100644
index 0000000..fd68f16
--- /dev/null
+++ b/src/lib/resolve/tests/resolve_unittest.cc
@@ -0,0 +1,172 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <iostream>
+#include <gtest/gtest.h>
+
+#include <dns/message.h>
+#include <dns/question.h>
+#include <dns/opcode.h>
+#include <dns/rcode.h>
+#include <dns/rrttl.h>
+#include <dns/rdata.h>
+#include <resolve/resolve.h>
+
+using namespace isc::dns;
+
+namespace {
+
+class ResolveHelperFunctionsTest : public ::testing::Test {
+public:
+    ResolveHelperFunctionsTest() :
+        message_a_(new Message(Message::RENDER)),
+        message_b_(new Message(Message::RENDER)),
+        question_(new Question(Name("www.example.com"), RRClass::IN(), RRType::A()))
+    {
+        createMessageA();
+        createMessageB();
+    };
+
+    void createMessageA() {
+        message_a_->setOpcode(Opcode::QUERY());
+        message_a_->setRcode(Rcode::NOERROR());
+        message_a_->addQuestion(question_);
+    }
+
+    void createMessageB() {
+        message_b_->setOpcode(Opcode::QUERY());
+        message_b_->setRcode(Rcode::NOERROR());
+        message_b_->addQuestion(question_);
+
+        // We could reuse the same rrset in the different sections,
+        // but to be sure, we create separate ones
+        RRsetPtr answer_rrset(new RRset(Name("www.example.com"),
+                                        RRClass::IN(), RRType::TXT(),
+                                        RRTTL(3600)));
+        answer_rrset->addRdata(rdata::createRdata(RRType::TXT(),
+                                                  RRClass::IN(),
+                                                  "Answer"));
+        message_b_->addRRset(Message::SECTION_ANSWER, answer_rrset);
+    
+        RRsetPtr auth_rrset(new RRset(Name("www.example.com"),
+                                        RRClass::IN(), RRType::TXT(),
+                                        RRTTL(3600)));
+        auth_rrset->addRdata(rdata::createRdata(RRType::TXT(),
+                                                  RRClass::IN(),
+                                                  "Authority"));
+        auth_rrset->addRdata(rdata::createRdata(RRType::TXT(),
+                                                  RRClass::IN(),
+                                                  "Rdata"));
+        message_b_->addRRset(Message::SECTION_AUTHORITY, auth_rrset);
+    
+        RRsetPtr add_rrset(new RRset(Name("www.example.com"),
+                                     RRClass::IN(), RRType::TXT(),
+                                     RRTTL(3600)));
+        add_rrset->addRdata(rdata::createRdata(RRType::TXT(),
+                                               RRClass::IN(),
+                                               "Additional"));
+        add_rrset->addRdata(rdata::createRdata(RRType::TXT(),
+                                               RRClass::IN(),
+                                               "Rdata"));
+        add_rrset->addRdata(rdata::createRdata(RRType::TXT(),
+                                               RRClass::IN(),
+                                               "fields."));
+        message_b_->addRRset(Message::SECTION_ADDITIONAL, add_rrset);
+    };
+
+    MessagePtr message_a_;
+    MessagePtr message_b_;
+    QuestionPtr question_;
+};
+
+TEST_F(ResolveHelperFunctionsTest, makeErrorMessageEmptyMessage) {
+    ASSERT_EQ(Rcode::NOERROR(), message_a_->getRcode());
+    ASSERT_EQ(1, message_a_->getRRCount(Message::SECTION_QUESTION));
+    ASSERT_EQ(0, message_a_->getRRCount(Message::SECTION_ANSWER));
+    ASSERT_EQ(0, message_a_->getRRCount(Message::SECTION_AUTHORITY));
+    ASSERT_EQ(0, message_a_->getRRCount(Message::SECTION_ADDITIONAL));
+
+    isc::resolve::makeErrorMessage(message_a_, Rcode::SERVFAIL());
+    EXPECT_EQ(Rcode::SERVFAIL(), message_a_->getRcode());
+    EXPECT_EQ(1, message_a_->getRRCount(Message::SECTION_QUESTION));
+    EXPECT_EQ(0, message_a_->getRRCount(Message::SECTION_ANSWER));
+    EXPECT_EQ(0, message_a_->getRRCount(Message::SECTION_AUTHORITY));
+    EXPECT_EQ(0, message_a_->getRRCount(Message::SECTION_ADDITIONAL));
+}
+
+TEST_F(ResolveHelperFunctionsTest, makeErrorMessageNonEmptyMessage) {
+
+    ASSERT_EQ(Rcode::NOERROR(), message_b_->getRcode());
+    ASSERT_EQ(1, message_b_->getRRCount(Message::SECTION_QUESTION));
+    ASSERT_EQ(1, message_b_->getRRCount(Message::SECTION_ANSWER));
+    ASSERT_EQ(2, message_b_->getRRCount(Message::SECTION_AUTHORITY));
+    ASSERT_EQ(3, message_b_->getRRCount(Message::SECTION_ADDITIONAL));
+
+    isc::resolve::makeErrorMessage(message_b_, Rcode::FORMERR());
+    EXPECT_EQ(Rcode::FORMERR(), message_b_->getRcode());
+    EXPECT_EQ(1, message_b_->getRRCount(Message::SECTION_QUESTION));
+    EXPECT_EQ(0, message_b_->getRRCount(Message::SECTION_ANSWER));
+    EXPECT_EQ(0, message_b_->getRRCount(Message::SECTION_AUTHORITY));
+    EXPECT_EQ(0, message_b_->getRRCount(Message::SECTION_ADDITIONAL));
+}
+
+void
+compareSections(const MessagePtr message_a, const MessagePtr message_b,
+                Message::Section section)
+{
+    RRsetIterator rrs_a = message_a->beginSection(section);
+    RRsetIterator rrs_b = message_b->beginSection(section);
+    while (rrs_a != message_a->endSection(section) &&
+           rrs_b != message_b->endSection(section)
+          ) {
+        EXPECT_EQ(*rrs_a, *rrs_b);
+        ++rrs_a;
+        ++rrs_b;
+    }
+    // can't use EXPECT_EQ here, no eqHelper for endsection comparison
+    EXPECT_TRUE(rrs_a == message_a->endSection(section));
+    EXPECT_TRUE(rrs_b == message_b->endSection(section));
+}
+
+TEST_F(ResolveHelperFunctionsTest, copyAnswerMessage) {
+    message_b_->setRcode(Rcode::NXDOMAIN());
+    
+    ASSERT_NE(message_b_->getRcode(), message_a_->getRcode());
+    ASSERT_NE(message_b_->getRRCount(Message::SECTION_ANSWER),
+              message_a_->getRRCount(Message::SECTION_ANSWER));
+    ASSERT_NE(message_b_->getRRCount(Message::SECTION_AUTHORITY),
+              message_a_->getRRCount(Message::SECTION_AUTHORITY));
+    ASSERT_NE(message_b_->getRRCount(Message::SECTION_ADDITIONAL),
+              message_a_->getRRCount(Message::SECTION_ADDITIONAL));
+    ASSERT_EQ(0, message_a_->getRRCount(Message::SECTION_ANSWER));
+    ASSERT_EQ(0, message_a_->getRRCount(Message::SECTION_AUTHORITY));
+    ASSERT_EQ(0, message_a_->getRRCount(Message::SECTION_ADDITIONAL));
+
+    isc::resolve::copyResponseMessage(*message_b_, message_a_);
+
+    EXPECT_EQ(message_b_->getRcode(), message_a_->getRcode());
+    ASSERT_EQ(message_b_->getRRCount(Message::SECTION_ANSWER),
+              message_a_->getRRCount(Message::SECTION_ANSWER));
+    ASSERT_EQ(message_b_->getRRCount(Message::SECTION_AUTHORITY),
+              message_a_->getRRCount(Message::SECTION_AUTHORITY));
+    ASSERT_EQ(message_b_->getRRCount(Message::SECTION_ADDITIONAL),
+              message_a_->getRRCount(Message::SECTION_ADDITIONAL));
+
+    
+    compareSections(message_a_, message_b_, Message::SECTION_ANSWER);
+    compareSections(message_a_, message_b_, Message::SECTION_AUTHORITY);
+    compareSections(message_a_, message_b_, Message::SECTION_ADDITIONAL);
+}
+
+} // Anonymous namespace
diff --git a/src/lib/resolve/tests/response_classifier_unittest.cc b/src/lib/resolve/tests/response_classifier_unittest.cc
new file mode 100644
index 0000000..b37ded7
--- /dev/null
+++ b/src/lib/resolve/tests/response_classifier_unittest.cc
@@ -0,0 +1,537 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <iostream>
+#include <gtest/gtest.h>
+
+#include <dns/tests/unittest_util.h>
+
+#include <resolve/response_classifier.h>
+
+#include <dns/name.h>
+#include <dns/opcode.h>
+#include <dns/question.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/rcode.h>
+#include <dns/rrclass.h>
+#include <dns/rrset.h>
+#include <dns/rrtype.h>
+#include <dns/rrttl.h>
+
+using namespace std;
+using namespace isc::dns;
+using namespace rdata;
+using namespace isc::dns::rdata::generic;
+using namespace isc::dns::rdata::in;
+using namespace isc::resolve;
+
+
+namespace {
+class ResponseClassifierTest : public ::testing::Test {
+public:
+    /// \brief Constructor
+    ///
+    /// The naming convention is:
+    ///
+    /// <category>_<class>_<type>_<name>
+    ///
+    /// <category> is "qu" (question), "rrs" (rrset),
+    /// <qclass> is self-explanatory
+    /// <qtype> is self-explanatory
+    /// <name> is the first part of the domain name (all expected to be in
+    /// example.com)
+    ///
+    /// Message variables
+    ///
+    /// msg_<qtype>  Where <qtype> is the type of query.  These are only used
+    /// in the early tests where simple messages are required.
+
+    ResponseClassifierTest() :
+        msg_a(Message::RENDER),
+        msg_any(Message::RENDER),
+        qu_ch_a_www(Name("www.example.com"), RRClass::CH(), RRType::A()),
+        qu_in_any_www(Name("www.example.com"), RRClass::IN(), RRType::ANY()),
+        qu_in_a_www2(Name("www2.example.com"), RRClass::IN(), RRType::A()),
+        qu_in_a_www(Name("www.example.com"), RRClass::IN(), RRType::A()),
+        qu_in_cname_www1(Name("www1.example.com"), RRClass::IN(), RRType::A()),
+        qu_in_ns_(Name("example.com"), RRClass::IN(), RRType::NS()),
+        qu_in_txt_www(Name("www.example.com"), RRClass::IN(), RRType::TXT()),
+        rrs_hs_txt_www(new RRset(Name("www.example.com"), RRClass::HS(),
+            RRType::TXT(), RRTTL(300))),
+        rrs_in_a_mail(new RRset(Name("mail.example.com"), RRClass::IN(),
+            RRType::A(), RRTTL(300))),
+        rrs_in_a_www(new RRset(Name("www.example.com"), RRClass::IN(),
+            RRType::A(), RRTTL(300))),
+        rrs_in_cname_www1(new RRset(Name("www1.example.com"), RRClass::IN(),
+            RRType::CNAME(), RRTTL(300))),
+        rrs_in_cname_www2(new RRset(Name("www2.example.com"), RRClass::IN(),
+            RRType::CNAME(), RRTTL(300))),
+        rrs_in_ns_(new RRset(Name("example.com"), RRClass::IN(),
+            RRType::NS(), RRTTL(300))),
+        rrs_in_txt_www(new RRset(Name("www.example.com"), RRClass::IN(),
+            RRType::TXT(), RRTTL(300))),
+        cname_target("."),
+        cname_count(0)
+    {
+        // Set up the message to indicate a successful response to the question
+        // "www.example.com A", but don't add in any response sections.
+        msg_a.setHeaderFlag(Message::HEADERFLAG_QR);
+        msg_a.setOpcode(Opcode::QUERY());
+        msg_a.setRcode(Rcode::NOERROR());
+        msg_a.addQuestion(qu_in_a_www);
+
+        // ditto for the query "www.example.com ANY"
+        msg_any.setHeaderFlag(Message::HEADERFLAG_QR);
+        msg_any.setOpcode(Opcode::QUERY());
+        msg_any.setRcode(Rcode::NOERROR());
+        msg_any.addQuestion(qu_in_any_www);
+
+        // The next set of assignments set up the following zone records
+        //
+        // example.com           NS     ns0.isc.org
+        //                       NS     ns0.example.org
+        //
+        // www.example.com       A      1.2.3.4
+        //                       TXT    "An example text string"
+        //
+        // mail.example.com      A      4.5.6.7
+        //
+        // www1.example.com      CNAME  www.example.com
+        //
+        // www2.example.com      CNAME  www1.example.com
+
+        // Set up an imaginary NS RRset for an authority section
+        rrs_in_ns_->addRdata(ConstRdataPtr(new NS(Name("ns0.isc.org"))));
+        rrs_in_ns_->addRdata(ConstRdataPtr(new NS(Name("ns0.example.org"))));
+
+        // Set up the records for the www host
+        rrs_in_a_www->addRdata(ConstRdataPtr(new A("1.2.3.4")));
+        rrs_in_txt_www->addRdata(ConstRdataPtr(
+            new TXT("An example text string")));
+
+        // ... for the mail host
+        rrs_in_a_mail->addRdata(ConstRdataPtr(new A("5.6.7.8")));
+
+        // ... the CNAME records
+        rrs_in_cname_www1->addRdata(ConstRdataPtr(
+            new CNAME("www.example.com")));
+        rrs_in_cname_www2->addRdata(ConstRdataPtr(
+            new CNAME("www1.example.com")));
+    }
+
+    Message     msg_a;              // Pointer to message in RENDER state
+    Message     msg_any;            // Pointer to message in RENDER state
+    Question    qu_ch_a_www;        // www.example.com CH A
+    Question    qu_in_any_www;      // www.example.com IN ANY
+    Question    qu_in_a_www2;       // www.example.com IN ANY
+    Question    qu_in_a_www;        // www.example.com IN A
+    Question    qu_in_cname_www1;   // www1.example.com IN CNAME
+    Question    qu_in_ns_;          // example.com IN NS
+    Question    qu_in_txt_www;      // www.example.com IN TXT
+    RRsetPtr    rrs_hs_txt_www;     // www.example.com HS TXT
+    RRsetPtr    rrs_in_a_mail;      // mail.example.com IN A
+    RRsetPtr    rrs_in_a_www;       // www.example.com IN A
+    RRsetPtr    rrs_in_cname_www1;  // www1.example.com IN CNAME
+    RRsetPtr    rrs_in_cname_www2;  // www2.example.com IN CNAME
+    RRsetPtr    rrs_in_ns_;         // example.com IN NS
+    RRsetPtr    rrs_in_txt_www;     // www.example.com IN TXT
+    Name        cname_target;       // Used in response classifier to
+                                    // store the target of a possible
+                                    // CNAME chain
+    unsigned int cname_count;       // Used to count cnames in a chain
+};
+
+// Test that the error() function categorises the codes correctly.
+
+TEST_F(ResponseClassifierTest, StatusCodes) {
+    EXPECT_FALSE(ResponseClassifier::error(ResponseClassifier::ANSWER));
+    EXPECT_FALSE(ResponseClassifier::error(ResponseClassifier::ANSWERCNAME));
+    EXPECT_FALSE(ResponseClassifier::error(ResponseClassifier::CNAME));
+    EXPECT_FALSE(ResponseClassifier::error(ResponseClassifier::NXDOMAIN));
+    EXPECT_FALSE(ResponseClassifier::error(ResponseClassifier::REFERRAL));
+
+    EXPECT_TRUE(ResponseClassifier::error(ResponseClassifier::EMPTY));
+    EXPECT_TRUE(ResponseClassifier::error(ResponseClassifier::EXTRADATA));
+    EXPECT_TRUE(ResponseClassifier::error(ResponseClassifier::INVNAMCLASS));
+    EXPECT_TRUE(ResponseClassifier::error(ResponseClassifier::INVTYPE));
+    EXPECT_TRUE(ResponseClassifier::error(ResponseClassifier::MISMATQUEST));
+    EXPECT_TRUE(ResponseClassifier::error(ResponseClassifier::MULTICLASS));
+    EXPECT_TRUE(ResponseClassifier::error(ResponseClassifier::NOTONEQUEST));
+    EXPECT_TRUE(ResponseClassifier::error(ResponseClassifier::NOTRESPONSE));
+    EXPECT_TRUE(ResponseClassifier::error(ResponseClassifier::NOTSINGLE));
+    EXPECT_TRUE(ResponseClassifier::error(ResponseClassifier::OPCODE));
+    EXPECT_TRUE(ResponseClassifier::error(ResponseClassifier::RCODE));
+    EXPECT_TRUE(ResponseClassifier::error(ResponseClassifier::TRUNCATED));
+}
+
+// Test that the system will reject a message which is a query.
+
+TEST_F(ResponseClassifierTest, Query) {
+
+    // Set up message to indicate a query (QR flag = 0, one question).  By
+    // default the opcode will be 0 (query)
+    msg_a.setHeaderFlag(Message::HEADERFLAG_QR, false);
+
+    // Should be rejected as it is a query, not a response
+    EXPECT_EQ(ResponseClassifier::NOTRESPONSE,
+        ResponseClassifier::classify(qu_in_a_www, msg_a,
+                                     cname_target, cname_count));
+}
+
+// Check that we get an OPCODE error on all but QUERY opcodes.
+
+TEST_F(ResponseClassifierTest, Opcode) {
+
+    uint8_t query = static_cast<uint8_t>(Opcode::QUERY().getCode());
+
+    for (uint8_t i = 0; i < (1 << 4); ++i) {
+        msg_a.setOpcode(Opcode(i));
+        if (i == query) {
+            EXPECT_NE(ResponseClassifier::OPCODE,
+                ResponseClassifier::classify(qu_in_a_www, msg_a,
+                                             cname_target, cname_count));
+        } else {
+            EXPECT_EQ(ResponseClassifier::OPCODE,
+                ResponseClassifier::classify(qu_in_a_www, msg_a,
+                                             cname_target, cname_count));
+        }
+    }
+}
+
+// Test that the system will reject a response with anything other than one
+// question.
+
+TEST_F(ResponseClassifierTest, MultipleQuestions) {
+
+    // Create a message object for this test that has no question section.
+    Message message(Message::RENDER);
+    message.setHeaderFlag(Message::HEADERFLAG_QR);
+    message.setOpcode(Opcode::QUERY());
+    message.setRcode(Rcode::NOERROR());
+
+    // Zero questions
+    EXPECT_EQ(ResponseClassifier::NOTONEQUEST,
+        ResponseClassifier::classify(qu_in_a_www, message,
+                                     cname_target, cname_count));
+
+    // One question
+    message.addQuestion(qu_in_a_www);
+    EXPECT_NE(ResponseClassifier::NOTONEQUEST,
+        ResponseClassifier::classify(qu_in_a_www, message,
+                                     cname_target, cname_count));
+
+    // Two questions
+    message.addQuestion(qu_in_ns_);
+    EXPECT_EQ(ResponseClassifier::NOTONEQUEST,
+        ResponseClassifier::classify(qu_in_a_www, message,
+                                     cname_target, cname_count));
+
+    // And finish the check with three questions
+    message.addQuestion(qu_in_txt_www);
+    EXPECT_EQ(ResponseClassifier::NOTONEQUEST,
+        ResponseClassifier::classify(qu_in_a_www, message,
+                                     cname_target, cname_count));
+}
+
+// Test that the question in the question section in the message response
+// is equal to the question supplied.
+
+TEST_F(ResponseClassifierTest, SameQuestion) {
+
+    EXPECT_EQ(ResponseClassifier::MISMATQUEST,
+        ResponseClassifier::classify(qu_in_ns_, msg_a,
+                                     cname_target, cname_count));
+    EXPECT_NE(ResponseClassifier::MISMATQUEST,
+        ResponseClassifier::classify(qu_in_a_www, msg_a,
+                                     cname_target, cname_count));
+}
+
+// Should get an NXDOMAIN response only on an NXDOMAIN RCODE.
+
+TEST_F(ResponseClassifierTest, NXDOMAIN) {
+
+    uint16_t nxdomain = static_cast<uint16_t>(Rcode::NXDOMAIN().getCode());
+
+    for (uint8_t i = 0; i < (1 << 4); ++i) {
+        msg_a.setRcode(Rcode(i));
+        if (i == nxdomain) {
+            EXPECT_EQ(ResponseClassifier::NXDOMAIN,
+                ResponseClassifier::classify(qu_in_a_www, msg_a,
+                                             cname_target, cname_count));
+        } else {
+            EXPECT_NE(ResponseClassifier::NXDOMAIN,
+                ResponseClassifier::classify(qu_in_a_www, msg_a,
+                                             cname_target, cname_count));
+        }
+    }
+}
+
+// Check that we get an RCODE error on all but NXDOMAIN and NOERROR responses.
+
+TEST_F(ResponseClassifierTest, RCODE) {
+
+    uint16_t nxdomain = static_cast<uint16_t>(Rcode::NXDOMAIN().getCode());
+    uint16_t noerror = static_cast<uint16_t>(Rcode::NOERROR().getCode());
+
+    for (uint8_t i = 0; i < (1 << 4); ++i) {
+        msg_a.setRcode(Rcode(i));
+        if ((i == nxdomain) || (i == noerror)) {
+            EXPECT_NE(ResponseClassifier::RCODE,
+                ResponseClassifier::classify(qu_in_a_www, msg_a,
+                                             cname_target, cname_count));
+        } else {
+            EXPECT_EQ(ResponseClassifier::RCODE,
+                ResponseClassifier::classify(qu_in_a_www, msg_a,
+                                             cname_target, cname_count));
+        }
+    }
+}
+
+// Test that the code will detect a truncated message.  Even if nothing else
+// is wrong, we'll want to retry the query if we receive a truncated code.
+// However, we give the option to the user of the code aws to whether they
+// want to take into account the truncated bit.
+
+TEST_F(ResponseClassifierTest, Truncated) {
+
+    // Don't expect the truncated code whatever option we ask for if the TC
+    // bit is not set.
+    msg_a.setHeaderFlag(Message::HEADERFLAG_TC, false);
+    EXPECT_NE(ResponseClassifier::TRUNCATED,
+        ResponseClassifier::classify(qu_in_a_www, msg_a, cname_target,
+                                     cname_count, true));
+    EXPECT_NE(ResponseClassifier::TRUNCATED,
+        ResponseClassifier::classify(qu_in_a_www, msg_a, cname_target,
+                                     cname_count, false));
+
+    // Expect the truncated code if the TC bit is set, only if we don't ignore
+    // it.
+    msg_a.setHeaderFlag(Message::HEADERFLAG_TC, true);
+    EXPECT_NE(ResponseClassifier::TRUNCATED,
+        ResponseClassifier::classify(qu_in_a_www, msg_a, cname_target,
+                                     cname_count, true));
+    EXPECT_EQ(ResponseClassifier::TRUNCATED,
+        ResponseClassifier::classify(qu_in_a_www, msg_a, cname_target,
+                                     cname_count, false));
+}
+
+// Check for an empty packet (i.e. no error, but with the answer and additional
+// sections empty).
+
+TEST_F(ResponseClassifierTest, Empty) {
+
+    EXPECT_EQ(ResponseClassifier::EMPTY,
+        ResponseClassifier::classify(qu_in_a_www, msg_a, cname_target,
+                                     cname_count));
+}
+
+// Anything where we have an empty answer section but something in the
+// authority section is a referral (if the status is NOERROR).
+
+TEST_F(ResponseClassifierTest, EmptyAnswerReferral) {
+
+    msg_a.addRRset(Message::SECTION_AUTHORITY, rrs_in_ns_);
+    EXPECT_EQ(ResponseClassifier::REFERRAL,
+        ResponseClassifier::classify(qu_in_a_www, msg_a, 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
+// must be on the QNAME/QCLASS alone.
+
+TEST_F(ResponseClassifierTest, SingleAnswer) {
+
+    // Check a question that matches the answer
+    msg_a.addRRset(Message::SECTION_ANSWER, rrs_in_a_www);
+    EXPECT_EQ(ResponseClassifier::ANSWER,
+        ResponseClassifier::classify(qu_in_a_www, msg_a, cname_target,
+                                     cname_count));
+
+    // Check an ANY question that matches the answer
+    msg_any.addRRset(Message::SECTION_ANSWER, rrs_in_a_www);
+    EXPECT_EQ(ResponseClassifier::ANSWER,
+        ResponseClassifier::classify(qu_in_any_www, msg_any, cname_target,
+                                     cname_count));
+
+    // Check a CNAME response that matches the QNAME.
+    Message message_a(Message::RENDER);
+    message_a.setHeaderFlag(Message::HEADERFLAG_QR);
+    message_a.setOpcode(Opcode::QUERY());
+    message_a.setRcode(Rcode::NOERROR());
+    message_a.addQuestion(qu_in_cname_www1);
+    message_a.addRRset(Message::SECTION_ANSWER, rrs_in_cname_www1);
+    EXPECT_EQ(ResponseClassifier::CNAME,
+        ResponseClassifier::classify(qu_in_cname_www1, message_a,
+                                     cname_target, cname_count));
+
+    // Check if the answer QNAME does not match the question
+    // Q: www.example.com  IN A
+    // A: mail.example.com IN A
+    Message message_b(Message::RENDER);
+    message_b.setHeaderFlag(Message::HEADERFLAG_QR);
+    message_b.setOpcode(Opcode::QUERY());
+    message_b.setRcode(Rcode::NOERROR());
+    message_b.addQuestion(qu_in_a_www);
+    message_b.addRRset(Message::SECTION_ANSWER, rrs_in_a_mail);
+    EXPECT_EQ(ResponseClassifier::INVNAMCLASS,
+        ResponseClassifier::classify(qu_in_a_www, message_b,
+                                     cname_target, cname_count));
+
+    // Check if the answer class does not match the question
+    // Q: www.example.com CH A
+    // A: www.example.com IN A
+    Message message_c(Message::RENDER);
+    message_c.setHeaderFlag(Message::HEADERFLAG_QR);
+    message_c.setOpcode(Opcode::QUERY());
+    message_c.setRcode(Rcode::NOERROR());
+    message_c.addQuestion(qu_ch_a_www);
+    message_c.addRRset(Message::SECTION_ANSWER, rrs_in_a_www);
+    EXPECT_EQ(ResponseClassifier::INVNAMCLASS,
+        ResponseClassifier::classify(qu_ch_a_www, message_c,
+                                     cname_target, cname_count));
+
+    // Check if the answer type does not match the question
+    // Q: www.example.com IN A
+    // A: www.example.com IN TXT
+    Message message_d(Message::RENDER);
+    message_d.setHeaderFlag(Message::HEADERFLAG_QR);
+    message_d.setOpcode(Opcode::QUERY());
+    message_d.setRcode(Rcode::NOERROR());
+    message_d.addQuestion(qu_in_a_www);
+    message_d.addRRset(Message::SECTION_ANSWER, rrs_in_txt_www);
+    EXPECT_EQ(ResponseClassifier::INVTYPE,
+        ResponseClassifier::classify(qu_in_a_www, message_d,
+                                     cname_target, cname_count));
+}
+
+// Check what happens if we have multiple RRsets in the answer.
+
+TEST_F(ResponseClassifierTest, MultipleAnswerRRsets) {
+
+    // All the same QNAME but different types is only valid on an ANY query.
+    Message message_a(Message::RENDER);
+    message_a.setHeaderFlag(Message::HEADERFLAG_QR);
+    message_a.setOpcode(Opcode::QUERY());
+    message_a.setRcode(Rcode::NOERROR());
+    message_a.addQuestion(qu_in_any_www);
+    message_a.addRRset(Message::SECTION_ANSWER, rrs_in_a_www);
+    message_a.addRRset(Message::SECTION_ANSWER, rrs_in_txt_www);
+    EXPECT_EQ(ResponseClassifier::ANSWER,
+        ResponseClassifier::classify(qu_in_any_www, message_a,
+                                     cname_target, cname_count));
+
+    // On another type of query, it results in an EXTRADATA error
+    Message message_b(Message::RENDER);
+    message_b.setHeaderFlag(Message::HEADERFLAG_QR);
+    message_b.setOpcode(Opcode::QUERY());
+    message_b.setRcode(Rcode::NOERROR());
+    message_b.addQuestion(qu_in_a_www);
+    message_b.addRRset(Message::SECTION_ANSWER, rrs_in_a_www);
+    message_b.addRRset(Message::SECTION_ANSWER, rrs_in_txt_www);
+    EXPECT_EQ(ResponseClassifier::EXTRADATA,
+        ResponseClassifier::classify(qu_in_a_www, message_b,
+                                     cname_target, cname_count));
+
+    // Same QNAME on an ANY query is not valid with mixed classes
+    Message message_c(Message::RENDER);
+    message_c.setHeaderFlag(Message::HEADERFLAG_QR);
+    message_c.setOpcode(Opcode::QUERY());
+    message_c.setRcode(Rcode::NOERROR());
+    message_c.addQuestion(qu_in_any_www);
+    message_c.addRRset(Message::SECTION_ANSWER, rrs_in_a_www);
+    message_c.addRRset(Message::SECTION_ANSWER, rrs_hs_txt_www);
+    EXPECT_EQ(ResponseClassifier::MULTICLASS,
+        ResponseClassifier::classify(qu_in_any_www, message_c,
+                                     cname_target, cname_count));
+
+    // Mixed QNAME is not valid unless QNAME requested is a CNAME.
+    Message message_d(Message::RENDER);
+    message_d.setHeaderFlag(Message::HEADERFLAG_QR);
+    message_d.setOpcode(Opcode::QUERY());
+    message_d.setRcode(Rcode::NOERROR());
+    message_d.addQuestion(qu_in_a_www);
+    message_d.addRRset(Message::SECTION_ANSWER, rrs_in_a_www);
+    message_d.addRRset(Message::SECTION_ANSWER, rrs_in_a_mail);
+    EXPECT_EQ(ResponseClassifier::EXTRADATA,
+        ResponseClassifier::classify(qu_in_a_www, message_d,
+                                     cname_target, cname_count));
+
+    // Mixed QNAME is not valid when the query is an ANY.
+    Message message_e(Message::RENDER);
+    message_e.setHeaderFlag(Message::HEADERFLAG_QR);
+    message_e.setOpcode(Opcode::QUERY());
+    message_e.setRcode(Rcode::NOERROR());
+    message_e.addQuestion(qu_in_any_www);
+    message_e.addRRset(Message::SECTION_ANSWER, rrs_in_a_www);
+    message_e.addRRset(Message::SECTION_ANSWER, rrs_in_txt_www);
+    message_e.addRRset(Message::SECTION_ANSWER, rrs_in_a_mail);
+    EXPECT_EQ(ResponseClassifier::EXTRADATA,
+        ResponseClassifier::classify(qu_in_any_www, message_e,
+                                     cname_target, cname_count));
+}
+
+// CNAME chain is CNAME if it terminates in a CNAME, answer if it
+// does not, and error if there are RRs left over.
+TEST_F(ResponseClassifierTest, CNAMEChain) {
+
+    // Answer contains a single CNAME
+    Message message_a(Message::RENDER);
+    message_a.setHeaderFlag(Message::HEADERFLAG_QR);
+    message_a.setOpcode(Opcode::QUERY());
+    message_a.setRcode(Rcode::NOERROR());
+    message_a.addQuestion(qu_in_a_www2);
+    message_a.addRRset(Message::SECTION_ANSWER, rrs_in_cname_www2);
+    EXPECT_EQ(ResponseClassifier::CNAME,
+        ResponseClassifier::classify(qu_in_a_www2, message_a,
+                                     cname_target, cname_count));
+
+    // Add a CNAME for www1, and it should still return a CNAME
+    message_a.addRRset(Message::SECTION_ANSWER, rrs_in_cname_www1);
+    EXPECT_EQ(ResponseClassifier::CNAME,
+        ResponseClassifier::classify(qu_in_a_www2, message_a,
+                                     cname_target, cname_count));
+
+    // Add the A record for www and it should be an answer
+    message_a.addRRset(Message::SECTION_ANSWER, rrs_in_a_www);
+    EXPECT_EQ(ResponseClassifier::ANSWERCNAME,
+        ResponseClassifier::classify(qu_in_a_www2, message_a,
+                                     cname_target, cname_count));
+
+    // Adding an unrelated TXT record should result in EXTRADATA
+    message_a.addRRset(Message::SECTION_ANSWER, rrs_in_txt_www);
+    EXPECT_EQ(ResponseClassifier::EXTRADATA,
+        ResponseClassifier::classify(qu_in_a_www2, message_a,
+                                     cname_target, cname_count));
+
+    // Recreate the chain, but this time end with a TXT RR and not the A
+    // record.  This should return INVTYPE.
+    Message message_b(Message::RENDER);
+    message_b.setHeaderFlag(Message::HEADERFLAG_QR);
+    message_b.setOpcode(Opcode::QUERY());
+    message_b.setRcode(Rcode::NOERROR());
+    message_b.addQuestion(qu_in_a_www2);
+    message_b.addRRset(Message::SECTION_ANSWER, rrs_in_cname_www2);
+    message_b.addRRset(Message::SECTION_ANSWER, rrs_in_cname_www1);
+    message_b.addRRset(Message::SECTION_ANSWER, rrs_in_txt_www);
+
+    EXPECT_EQ(ResponseClassifier::INVTYPE,
+        ResponseClassifier::classify(qu_in_a_www2, message_b,
+                                     cname_target, cname_count));
+}
+
+} // Anonymous namespace




More information about the bind10-changes mailing list