BIND 10 trac388, updated. ba727c17d3517232a3c40fa3a30c6924a30ed7dc [trac388] fix the conflict during merge with master

BIND 10 source code commits bind10-changes at lists.isc.org
Fri Feb 25 08:26:08 UTC 2011


The branch, trac388 has been updated
       via  ba727c17d3517232a3c40fa3a30c6924a30ed7dc (commit)
       via  8f6657a13b4e6e140e3c5a52201fd4c998ee9560 (commit)
       via  a42df8e3040bc9cfa52aca8d45d09ef016810862 (commit)
       via  5ac67ede03892a5eacf42ce3ace1e4e376164c9f (commit)
       via  4bdd50ce23081a45afbcb653a6b75a1b408c4e0b (commit)
       via  a232b085dfef35b196013423e25927cd6e230276 (commit)
       via  d3f8f8fce7957a2227dbc38d2faf58ca33f85fd1 (commit)
       via  6e0b7398570e219d417f991681f6d6428fb12a67 (commit)
       via  e8460092b6addf9ce69dddb49d80d91037178ba7 (commit)
       via  add0f697aec63c9af1df661951bda2ae007c98bf (commit)
       via  1a5be7480941be67947095ab12297d157f8fb572 (commit)
       via  71d27e416b025bc77edd70f9e885fd804d043efd (commit)
       via  89ad5194ebbbc808c7e5f317da47550e99efa9ea (commit)
       via  9a75a9c73b0bb361f1952e92eb4b5a62694be4d6 (commit)
       via  6fb5dd6bf0e9fcb0d00eb3f659b5b9c45340bb37 (commit)
       via  13204f9c20a03dcc041cd22fff22fb28af569eaf (commit)
       via  7c9c7811d3d8642ae675073e0cf9e8b83df49c4c (commit)
       via  586df2b37d5f26ac1998d12c86f280b11bf077b7 (commit)
       via  0ac0b4602fa30852b0d86cc3c0b4730deb1a58fe (commit)
       via  54f4650b7de4d275b29bc1d70b2cba98d59d305a (commit)
       via  bb708b6586815ae9ac14d99b0d4bfd714f315273 (commit)
       via  d3120390ae945b88432c48aa82daf90ef67f6715 (commit)
       via  27025d831a4d06672458004ec70b5c2cef73904d (commit)
       via  34be7c60c8cd01a3693cdc35346ea8bed77a4c88 (commit)
       via  008a3bf75b8aef0b813c5109fe8749f32ca6c7a9 (commit)
       via  cce105f44bf716ed738a1465e5412468d796aa13 (commit)
       via  57b77aa7c09f568a9adf7f9ad475cc80f278fedc (commit)
       via  e5961a4ce06bb430469c457856f5392ca147d857 (commit)
       via  14a51327ed408ed5a957147720f7ad3e3db6a95d (commit)
       via  60f301c235b9d83b7fde6f06abac18d0a1168260 (commit)
       via  4a8925ec51c064a456795ba17042bdf30ff2b8ab (commit)
       via  0227b4b17e2bdbbf5472c145ce14f1fb08aa7791 (commit)
       via  795a77a4651542f80ba8906d3f02b4987d414f79 (commit)
       via  41a2bfb4045de0547186a8dca60c30314a3ae28c (commit)
       via  8b41f77f0099ddc7ca7d34d39ad8c39bb1a8363c (commit)
       via  34d182ec09a5674e87470772cce024d49043cbd9 (commit)
       via  f9e5f363f3abf3c08d65ff14421491b44ccbe8af (commit)
       via  e4ee8c985e9adf5f734f0f693b79fe459ef848db (commit)
       via  9739cbce2eaffc7e80640db58a8513295cf684de (commit)
       via  955a15527bec6ba0231f0bc377539a169fa34165 (commit)
       via  da6a33e379d7c8ad56087b9302d7dc5e6a6ed08d (commit)
       via  d0181f27a73dc4b77bc9c6756cb6583226dafacc (commit)
       via  cf131c0c98c346b2f075b76c2fc92a658eb58f1f (commit)
       via  dee286dad1ba1b47d89659572fc2ce8fd1a94af6 (commit)
       via  d22c96099aacc094be00c1fe51433d4b39b9656f (commit)
       via  fcb8add55e0b6d5be9104e59e4f47a6d4d4ae9c5 (commit)
       via  ecbfb7cf929d62a018dd4cdc7a841add3d5a35ae (commit)
       via  6689f461849841d7c5a724471107f758535c213e (commit)
       via  004e382616150f8a2362e94d3458b59bb2710182 (commit)
       via  6dad3efd9d023633d973d20b9102f2f8b10f3d17 (commit)
       via  1355eed8148ae3199e5c047c004bc8ad839ad5f8 (commit)
       via  5dc6a761125d48557a13a6354a6c373607fb2714 (commit)
       via  76a5b79fe8b601f5c9aeafd68394628ac9552f16 (commit)
       via  64dad23b34538632b6b2724c5d6c17b295baf9f5 (commit)
       via  3ebbda21d35170b26d7db65686583617c8f0cc26 (commit)
       via  e7a46851671cd942d39e74d0456435401dc15881 (commit)
       via  ba870bd64170a8533f1bbabcd6df36edf31674c5 (commit)
       via  1b7e11468e670385d56d05b97afac96c4da05b9a (commit)
       via  f3e39622ac840f6322cbc76277b2104ba4e8aee4 (commit)
       via  f9ed88b3ab1f080f2e96219a70c7f6ba9026d547 (commit)
       via  04934bb05de6689d170ff4d52c2518301df0f960 (commit)
       via  09ece8cdd41c0f025e8b897b4883885d88d4ba5d (commit)
       via  296d6b7b9b3805b5bc71ece7e3f947604c641ffb (commit)
       via  14b12e16f91cca0cd7c9053727832111538ecc85 (commit)
       via  0e2743485d50f8987b9d1fe959b215e49039a965 (commit)
       via  e62c547688f1e4820fa8ba200149ea16960354b9 (commit)
       via  319debfb957641f311102739a15059f8453c54ce (commit)
       via  80a4c6e866a3f40462b26260c6de1ef05a334e53 (commit)
       via  67ff28d5017ac0cb38b591de079809bfe765ddd1 (commit)
       via  7e90e3729b54ca18425c3b5a5223230aa335efff (commit)
       via  bd8392ffd8873367f243121cf1e33eb7981b4f4d (commit)
       via  f8f81b7f1512760cb72592a79d2f49999f11be2c (commit)
       via  d6f67da406651c7cc013ce28c513c23ee43e2efc (commit)
       via  e6e3ba735b384ebe4ee1ccacb39af2100bdd5f19 (commit)
       via  301cadd12c9f4aec34835ffde2abdc808ab28936 (commit)
       via  fa007383d4b4d71c5a4176c4bc6e11154d662d8d (commit)
       via  3a9752bd2611ce206fe0526257876ba99a640e6b (commit)
       via  b6b79e9a9f444e2ab6b1a53fc1597c77cbcefd61 (commit)
       via  d58cbd26658afd381cb715d0ca976d11006a0445 (commit)
       via  a37dace36f7322764bb01cc790c3f36d524c0456 (commit)
       via  9136696612968df28a925c910b54217ffe84d206 (commit)
       via  236adec49e83cc6f5ce85c07766fe5f553e00812 (commit)
       via  c9a06623e3e6041342046d52f502ce5e478ef29c (commit)
       via  85b6fa72d68d019149b8c751d495e34bbd4246a8 (commit)
       via  a2a0bf66d71c5b5adba4a1e0dca48496bfee0ce1 (commit)
       via  004816211d6b0f438713c1a4e167be8ac25a356f (commit)
       via  e5fb3bc1ed5f3c0aec6eb40a16c63f3d0fc6a7b2 (commit)
       via  c78659bd80e45ddb44b3cafb4937aa7b4e5f1b14 (commit)
       via  05f49a93714de9a09ad1c0a9d6e2a7365e890232 (commit)
       via  3c06d0d6c0d002138ab3edbd9a6833a40de3f911 (commit)
       via  64c233aa9b565df653928f79e987ff0ffacf68af (commit)
       via  f8e2229b901ea731ac1fd7e7bac0799b855cd88a (commit)
       via  ed89ba2b9f20a5aad5f213191f9b34981aa6a0b9 (commit)
       via  8ad3c81393273b291cbe8e97f9ab7c260d8ca94f (commit)
       via  5cfa9db0c1eea204a2f6161c4929da4d896afb41 (commit)
       via  cab140484a93d9fabc4c3a108ce4770e22b08dc4 (commit)
       via  cb2cf2013b616b055904e301b3d5ff4c49475525 (commit)
       via  cbc9f118a77f69878f4cebf383db39d984201bae (commit)
       via  b6bce27baf454101b3755d46877ac84c5eef20f2 (commit)
       via  3d61dceb1a9093ab110920fb436c3b2061f15857 (commit)
       via  6bbb42b7fa668d7a8fcc3cf3809365179705a3a2 (commit)
       via  cad6c0a62b3c5cace703d9863d64dbdb1e047046 (commit)
       via  949f642c90ab8ad7f0f02c35e383b9eb9e65792a (commit)
       via  f59af8201a12342a2779440a4c7afed1b51fef7f (commit)
       via  9cf88a2d14abccc7b3ad6ffd8d299dfa464bee1c (commit)
       via  4864018bdb5a6f3f9ea9d7a9cc608521655233dd (commit)
       via  ad418dc7853679f1d79c280af5993b82c43dc51a (commit)
       via  7ddfd9eca150efa2fed15114034e5297db765a53 (commit)
       via  e01aeb058be1d8bd7715ab603c097230cd18df5a (commit)
       via  841627853b192480a8e663870ad3a1cb39bb724e (commit)
       via  bbb0031f3a987b3314e1b3e8db1c7ca155f9f480 (commit)
       via  26aae7d612bf0f01b22a7c4784fffb909d148970 (commit)
       via  5266058997a5746c04db5a721ac60630bbbc2abd (commit)
       via  a30321812264615d0f64271ce6ba5e841f7977a5 (commit)
       via  93b246f4689918c081d9ed889cea9140b75adc1f (commit)
       via  569afbc3c08776e04f1b755dce2ff5aa5c385647 (commit)
       via  38778d44793426906ef7d0d25a70be4a36188765 (commit)
       via  ad91697c609b3ad79d8f2799a18ba63548a228a2 (commit)
       via  725aa66bb4eea86f29f5f851b4c188b864881e81 (commit)
       via  74e4f864d436d960d5feea540e5573cb21dbe39c (commit)
       via  bc3d1d70a2a074f3c3360ed75ea87a7fe81a4da6 (commit)
       via  709abc271e802e1a27b6f74fe54f7f5da5a7161e (commit)
       via  086b420bfba5b31f13e2828ca1be842a8faad4aa (commit)
       via  f33c6b79fa27d93bcf9be45aa6844e1c26f35a97 (commit)
       via  ef67acec41e9b83d4aacff8de12e6a3e37628227 (commit)
       via  184c6c7068ec90f2a85a859bfa56d779a4294382 (commit)
       via  7c7c776676860ba64571154faef440093799e996 (commit)
       via  db105a36beb8999996f9e4853a2cceadb074775f (commit)
       via  0061de28095c4ef166de784c12366adabc8a6636 (commit)
       via  f64a1962849e0773c33f95b541daf18a95911265 (commit)
       via  25530c6ca2483cf92aab7f240c7fa70bd3c5b3a8 (commit)
       via  33f0b8420f858e389d2fb0f27ddde1fa77287dc7 (commit)
       via  e8edbcf573f453fd1f72a3b899eaad9d195cc37c (commit)
       via  c31de72c76258d7444acc1f1ae1f30024b772af7 (commit)
       via  14824b3a8199caabbf76a5c7b933715a0f0c4fec (commit)
       via  cf32a344e6ed4eb7b5f2fcfb5c4d91a2d972d7d3 (commit)
       via  ff385d569e9a9b13c3c174b7d55f1916c75ce1df (commit)
       via  7099d4df871fb6186a64157c13b92b2d8b56de0e (commit)
       via  1b662b8dcab972fd07a7869b6b9108a8d128796f (commit)
       via  330e485343c4f3054146046dac4b98c937c35269 (commit)
       via  9851a189c6456b6ce5054cc4c9eea10a6866ce0f (commit)
       via  359efa4b2178e6929e9226fdf1bce782fce0c002 (commit)
       via  88effdc1b00be8636d40ef84f5556de60013347f (commit)
      from  c3d81863bc201dc1fab9690565158b8f09d61061 (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 ba727c17d3517232a3c40fa3a30c6924a30ed7dc
Merge: c3d81863bc201dc1fab9690565158b8f09d61061 8f6657a13b4e6e140e3c5a52201fd4c998ee9560
Author: hanfeng <ben.han.cn at gmail.com>
Date:   Fri Feb 25 16:21:00 2011 +0800

    [trac388] fix the conflict during merge with master

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

Summary of changes:
 ChangeLog                                          |   59 ++++-
 README                                             |   12 +-
 configure.ac                                       |  180 +++++-------
 doc/guide/bind10-guide.html                        |   82 +++---
 doc/guide/bind10-guide.xml                         |   51 ++--
 src/bin/auth/Makefile.am                           |    3 +-
 src/bin/auth/auth.spec.pre.in                      |   71 +++---
 src/bin/auth/benchmarks/Makefile.am                |    1 +
 src/bin/auth/tests/Makefile.am                     |    1 +
 src/bin/auth/tests/query_unittest.cc               |    2 +-
 src/bin/bind10/Makefile.am                         |    3 +-
 src/bin/bind10/bind10.8                            |    2 +-
 src/bin/bind10/bind10.py.in                        |   98 ++++++-
 src/bin/bind10/bind10.xml                          |    6 +-
 src/bin/bind10/tests/bind10_test.py                |  283 +++++++++++++-----
 src/bin/bindctl/Makefile.am                        |    2 +-
 src/bin/bindctl/bindcmd.py                         |  243 ++++++++++------
 src/bin/bindctl/bindctl-source.py.in               |   66 +++--
 src/bin/bindctl/cmdparse.py                        |   56 ++++-
 src/bin/bindctl/moduleinfo.py                      |   68 ++++-
 src/bin/bindctl/tests/Makefile.am                  |    2 +-
 src/bin/bindctl/tests/bindctl_test.py              |   96 ++++++-
 src/bin/bindctl/tests/cmdparse_test.py             |   88 ++++++
 src/bin/cfgmgr/Makefile.am                         |    1 -
 src/bin/cmdctl/Makefile.am                         |    3 +-
 src/bin/msgq/Makefile.am                           |    1 -
 src/bin/msgq/msgq.py.in                            |    2 +-
 src/bin/resolver/Makefile.am                       |    4 +-
 src/bin/resolver/b10-resolver.8                    |   49 ++--
 src/bin/resolver/b10-resolver.xml                  |   53 +++-
 src/bin/resolver/resolver.cc                       |   21 +-
 src/bin/resolver/tests/Makefile.am                 |    2 +
 src/bin/stats/Makefile.am                          |    3 +-
 src/bin/usermgr/Makefile.am                        |    3 +-
 src/bin/xfrin/Makefile.am                          |    3 +-
 src/bin/xfrout/Makefile.am                         |    3 +-
 src/bin/xfrout/tests/xfrout_test.py                |   23 ++
 src/bin/xfrout/xfrout.py.in                        |    3 +
 src/bin/zonemgr/Makefile.am                        |    2 +-
 src/lib/Makefile.am                                |    2 +-
 src/lib/asiolink/Makefile.am                       |   35 ++-
 src/lib/asiolink/README                            |   81 +++++-
 src/lib/asiolink/asiolink.h                        |   15 +-
 src/lib/asiolink/dns_service.cc                    |   12 +-
 src/lib/asiolink/dns_service.h                     |    6 +
 src/lib/asiolink/dummy_io_cb.h                     |   51 ++++
 .../asiolink/{udp_query.h => internal/iofetch.h}   |   77 ++++--
 src/lib/asiolink/interval_timer.cc                 |   10 +-
 src/lib/asiolink/io_address.cc                     |    5 +-
 src/lib/asiolink/io_address.h                      |    6 +-
 src/lib/asiolink/io_asio_socket.h                  |  309 ++++++++++++++++++++
 src/lib/asiolink/io_endpoint.cc                    |    3 +-
 src/lib/asiolink/io_endpoint.h                     |    6 +-
 .../{log/message_types.h => asiolink/io_error.h}   |   30 +-
 src/lib/asiolink/io_fetch.cc                       |  193 ++++++++++++
 src/lib/asiolink/io_fetch.h                        |  226 ++++++++++++++
 src/lib/asiolink/io_message.h                      |    7 +-
 src/lib/asiolink/io_service.cc                     |    9 +-
 src/lib/asiolink/io_socket.h                       |   11 +-
 src/lib/asiolink/recursive_query.cc                |  161 ++++++++---
 src/lib/asiolink/recursive_query.h                 |    4 +
 src/lib/asiolink/tcp_server.cc                     |   21 +-
 src/lib/asiolink/tcp_socket.h                      |  257 +++++++++++++++-
 src/lib/asiolink/tests/Makefile.am                 |   24 +-
 src/lib/asiolink/tests/interval_timer_unittest.cc  |    1 +
 ...oaddress_unittest.cc => io_address_unittest.cc} |    8 +-
 ...ndpoint_unittest.cc => io_endpoint_unittest.cc} |    3 +-
 src/lib/asiolink/tests/io_fetch_unittest.cc        |  188 ++++++++++++
 src/lib/asiolink/tests/io_service_unittest.cc      |    1 +
 ...{iosocket_unittest.cc => io_socket_unittest.cc} |    5 +-
 src/lib/asiolink/tests/recursive_query_unittest.cc |   11 +-
 src/lib/asiolink/tests/udp_endpoint_unittest.cc    |   55 ++++
 src/lib/asiolink/tests/udp_query_unittest.cc       |  145 ---------
 src/lib/asiolink/tests/udp_socket_unittest.cc      |  287 ++++++++++++++++++
 src/lib/asiolink/udp_endpoint.h                    |   29 ++-
 src/lib/asiolink/udp_query.cc                      |  189 ------------
 src/lib/asiolink/udp_server.cc                     |   26 ++-
 src/lib/asiolink/udp_socket.h                      |  250 +++++++++++++++-
 src/lib/cache/cache_entry_key.cc                   |    2 -
 src/lib/cache/cache_entry_key.h                    |    2 -
 src/lib/cache/local_zone_data.cc                   |    2 -
 src/lib/cache/local_zone_data.h                    |    2 -
 src/lib/cache/message_cache.cc                     |    2 -
 src/lib/cache/message_cache.h                      |    2 -
 src/lib/cache/message_entry.cc                     |    2 -
 src/lib/cache/message_entry.h                      |    2 -
 src/lib/cache/resolver_cache.cc                    |    9 +-
 src/lib/cache/resolver_cache.h                     |   31 +-
 src/lib/cache/rrset_cache.cc                       |    2 -
 src/lib/cache/rrset_cache.h                        |    4 +-
 src/lib/cache/rrset_copy.cc                        |    2 -
 src/lib/cache/rrset_copy.h                         |    2 -
 src/lib/cache/rrset_entry.cc                       |    2 -
 src/lib/cache/rrset_entry.h                        |    2 -
 src/lib/cache/tests/cache_test_messagefromfile.h   |    1 -
 src/lib/cache/tests/cache_test_sectioncount.h      |    1 -
 src/lib/cache/tests/local_zone_data_unittest.cc    |    1 -
 src/lib/cache/tests/message_cache_unittest.cc      |    1 -
 src/lib/cache/tests/message_entry_unittest.cc      |    1 -
 src/lib/cache/tests/resolver_cache_unittest.cc     |   22 +-
 src/lib/cache/tests/rrset_cache_unittest.cc        |    1 -
 src/lib/cache/tests/rrset_entry_unittest.cc        |    1 -
 src/lib/cache/tests/run_unittests.cc               |    1 -
 src/lib/config/tests/testdata/spec22.spec          |    4 +-
 src/lib/datasrc/data_source.cc                     |    2 +-
 src/lib/datasrc/memory_datasrc.cc                  |    2 +-
 src/lib/dns/dnssectime.cc                          |  153 ++++++++--
 src/lib/dns/dnssectime.h                           |   98 ++++++-
 src/lib/dns/python/messagerenderer_python.cc       |   45 +++-
 .../python/tests/messagerenderer_python_test.py    |   28 ++-
 src/lib/dns/rdata/generic/rrsig_46.cc              |   11 +-
 src/lib/dns/tests/dnssectime_unittest.cc           |  147 ++++++++--
 src/lib/dns/tests/rdata_mx_unittest.cc             |    5 +-
 src/lib/dns/tests/testdata/Makefile.am             |    3 +-
 .../{rdata_mx_toWire1 => rdata_mx_toWire2}         |    4 +-
 src/lib/log/Makefile.am                            |    1 -
 src/lib/nsas/Makefile.am                           |    1 +
 src/lib/nsas/hash_table.h                          |   24 +--
 src/lib/nsas/locks.h                               |  116 ++++++++
 src/lib/nsas/lru_list.h                            |   24 +--
 src/lib/nsas/nameserver_address_store.cc           |   15 +-
 src/lib/nsas/nameserver_entry.cc                   |    2 +-
 src/lib/nsas/nameserver_entry.h                    |   15 +-
 src/lib/nsas/tests/nsas_test.h                     |    1 +
 src/lib/nsas/zone_entry.cc                         |    4 +-
 src/lib/nsas/zone_entry.h                          |   16 +-
 src/lib/python/isc/cc/data.py                      |   10 +-
 src/lib/python/isc/config/ccsession.py             |   22 ++-
 src/lib/python/isc/config/config_data.py           |  181 ++++++++----
 .../python/isc/config/tests/config_data_test.py    |   76 ++++--
 src/lib/python/isc/notify/tests/notify_out_test.py |  265 ++++++++++--------
 src/lib/resolve/resolve.cc                         |   20 ++
 src/lib/resolve/resolve.h                          |   36 +++
 src/lib/resolve/tests/Makefile.am                  |    1 +
 src/lib/resolve/tests/resolve_unittest.cc          |   46 +++-
 135 files changed, 4397 insertions(+), 1491 deletions(-)
 mode change 100644 => 100755 src/bin/bind10/bind10.py.in
 create mode 100644 src/bin/bindctl/tests/cmdparse_test.py
 create mode 100644 src/lib/asiolink/dummy_io_cb.h
 rename src/lib/asiolink/{udp_query.h => internal/iofetch.h} (52%)
 create mode 100644 src/lib/asiolink/io_asio_socket.h
 copy src/lib/{log/message_types.h => asiolink/io_error.h} (62%)
 create mode 100644 src/lib/asiolink/io_fetch.cc
 create mode 100644 src/lib/asiolink/io_fetch.h
 rename src/lib/asiolink/tests/{ioaddress_unittest.cc => io_address_unittest.cc} (90%)
 rename src/lib/asiolink/tests/{ioendpoint_unittest.cc => io_endpoint_unittest.cc} (97%)
 create mode 100644 src/lib/asiolink/tests/io_fetch_unittest.cc
 rename src/lib/asiolink/tests/{iosocket_unittest.cc => io_socket_unittest.cc} (93%)
 create mode 100644 src/lib/asiolink/tests/udp_endpoint_unittest.cc
 delete mode 100644 src/lib/asiolink/tests/udp_query_unittest.cc
 create mode 100644 src/lib/asiolink/tests/udp_socket_unittest.cc
 delete mode 100644 src/lib/asiolink/udp_query.cc
 copy src/lib/dns/tests/testdata/{rdata_mx_toWire1 => rdata_mx_toWire2} (76%)
 create mode 100644 src/lib/nsas/locks.h

-----------------------------------------------------------------------
diff --git a/ChangeLog b/ChangeLog
index 2ba8522..016caee 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,15 +1,70 @@
-  172.  [func]      feng
+  181.  [func]      feng
 	Add stop interface into dns server, so we can stop each running
 	server individually. With it, user can reconfigure her running server
 	with different ip address or port.
 	(Trac #388, git TBD)
 
+  180.  [build]     jreed
+  Fix custom DESTDIR for make install.
+  (Trac $629, git 5ac67ede03892a5eacf42ce3ace1e4e376164c9f)
+
+bind10-devel-20110224 released on February 24, 2011
+
+  179.  [func]      vorner
+	It is possible to start and stop resolver and authoritative
+	server without restart of the whole system. Change of the
+	configuration (Boss/start_auth and Boss/start_resolver) is
+	enough.
+	(Trac #565, git 0ac0b4602fa30852b0d86cc3c0b4730deb1a58fe)
+
+  178.  [func]      jelte
+	Resolver now makes (limited) use of the cache
+	(Trac #491, git 8b41f77f0099ddc7ca7d34d39ad8c39bb1a8363c)
+
+  177.  [func]      stephen
+	The upstream fetch code in asiolink is now protocol agnostic to
+	allow for the addition of fallback to TCP if a fetch response
+	indicates truncation.
+	(Trac #554, git 9739cbce2eaffc7e80640db58a8513295cf684de)
+
+  176.  [func]      zhang likun
+	src/lib/cache: Rename one interface: from lookupClosestRRset()
+	to lookupDeepestNS(), and remove one parameter of it.
+	(Trac #492, git ecbfb7cf929d62a018dd4cdc7a841add3d5a35ae)
+
+  175.	[bug]		jerry
+	src/bin/xfrout: Xfrout use the case-sensitive mode to compress
+	names in an AXFR massage.
+	(Trac #253, git 004e382616150f8a2362e94d3458b59bb2710182)
+
+  174.	[bug]*		jinmei
+	src/lib/dns: revised dnssectime functions so that they don't rely
+	on the time_t type (whose size varies on different systems, which
+	can lead to subtle bugs like some form of "year 2038 problem").
+	Also handled 32-bit wrap around issues more explicitly, with more
+	detailed tests.  The function API has been changed, but the effect
+	should be minimal because these functions are mostly private.
+	(Trac #61, git 09ece8cdd41c0f025e8b897b4883885d88d4ba5d)
+
+  173.	[bug]		jerry
+	python/isc/notify: A notify_out test fails without network
+	connectivity, encapsulate the socket behavior using a mock
+	socket class to fix it.
+	(Trac #346, git 319debfb957641f311102739a15059f8453c54ce)
+
+  172.  [func]      jelte
+	Improved the bindctl cli in various ways, mainly concerning
+	list and map item addressing, the correct display of actual values,
+	and internal help.
+	(Trac #384, git e5fb3bc1ed5f3c0aec6eb40a16c63f3d0fc6a7b2)
+>>>>>>> master
+
   171.  [func]      feng, jerry, jinmei, vorner
 	b10-auth, src/lib/datasrc: in memory data source now works as a
 	complete data source for authoritative DNS servers and b10-auth
 	uses it.  It still misses major features, however, including
 	DNSSEC support and zone transfer.
-	(Last trac #552, but many more,
+	(Last trac #553, but many more,
 	git 6f031a09a248e7684723c000f3e8cc981dcdb349)
 
   170.	[bug]		jinmei
diff --git a/README b/README
index 9b258bc..5bc3154 100644
--- a/README
+++ b/README
@@ -14,12 +14,12 @@ five year plan are described here:
 	https://bind10.isc.org/wiki/Year2Milestones
 
 This release includes the bind10 master process, b10-msgq message
-bus, b10-auth authoritative DNS server (with SQLite3 backend),
-b10-resolver forwarding DNS server, b10-cmdctl remote control daemon,
-b10-cfgmgr configuration manager, b10-xfrin AXFR inbound service,
-b10-xfrout outgoing AXFR service, b10-zonemgr secondary manager,
-b10-stats statistics collection and reporting daemon, and a new
-libdns++ library for C++ with a python wrapper.
+bus, b10-auth authoritative DNS server (with SQLite3 and in-memory
+backends), b10-resolver forwarding DNS server, b10-cmdctl remote
+control daemon, b10-cfgmgr configuration manager, b10-xfrin AXFR
+inbound service, b10-xfrout outgoing AXFR service, b10-zonemgr
+secondary manager, b10-stats statistics collection and reporting
+daemon, and a new libdns++ library for C++ with a python wrapper.
 
 Documentation is included and also available via the BIND 10
 website at http://bind10.isc.org/
diff --git a/configure.ac b/configure.ac
index 139166b..d6b9376 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2,7 +2,7 @@
 # Process this file with autoconf to produce a configure script.
 
 AC_PREREQ([2.59])
-AC_INIT(bind10-devel, 20110120, bind10-dev at isc.org)
+AC_INIT(bind10-devel, 20110224, bind10-dev at isc.org)
 AC_CONFIG_SRCDIR(README)
 AM_INIT_AUTOMAKE
 AC_CONFIG_HEADERS([config.h])
@@ -66,6 +66,11 @@ if test $enable_shared = no; then
 	AC_MSG_ERROR([BIND 10 requires shared libraries to be built])
 fi
 
+AC_ARG_ENABLE(boost-threads,
+AC_HELP_STRING([--enable-boost-threads],
+  [use boost threads. Currently this only means using its locks instead of dummy locks, in the cache and NSAS]),
+  use_boost_threads=$enableval, use_boost_threads=no)
+
 # allow configuring without setproctitle.
 AC_ARG_ENABLE(setproctitle-check,
 AC_HELP_STRING([--disable-setproctitle-check],
@@ -193,8 +198,9 @@ if test "$setproctitle_check" = "yes" ; then
         AC_MSG_RESULT(ok)
     else
         AC_MSG_RESULT(missing)
-        AC_MSG_ERROR([Missing setproctitle module. Either install it or provide --disable-setproctitle-check.
-In that case we will continue, but naming of python processes will not work.])
+        AC_MSG_WARN([Missing setproctitle python module.
+Use --disable-setproctitle-check to skip this check.
+In this case we will continue, but naming of python processes will not work.])
     fi
 fi
 
@@ -363,57 +369,6 @@ if test "$lcov" != "no"; then
 fi
 AC_SUBST(USE_LCOV)
 
-# Configure log4cxx header and library path
-#
-# If explicitly specified, use it.
-
-AC_ARG_WITH([log4cxx],
-  AC_HELP_STRING([--with-log4cxx=PATH],
-    [specify directory where log4cxx is installed]),
-  [
-   log4cxx_include_path="${withval}/include";
-   log4cxx_library_path="${withval}/lib"
-  ])
-
-# This is an urgent fix to avoid regression due to log4cxx on some
-# platforms.  It should be cleaned up with a better fix.
-if test "X$with_log4cxx" != "Xno"; then
-
-# If not specified, try some common paths.  These default to
-# /usr/include and /usr/lib if not found
-
-if test -z "$with_log4cxx"; then
-	log4cxxdirs="/usr/local /usr/pkg /opt /opt/local"
-	for d in $log4cxxdirs
-	do
-		if test -d $d/include/log4cxx; then
-			log4cxx_include_path=$d/include
-			log4cxx_library_path=$d/lib
-			break
-		fi
-	done
-fi
-
-CPPFLAGS_SAVES="$CPPFLAGS"
-if test "${log4cxx_include_path}" ; then
-	LOG4CXX_INCLUDES="-I${log4cxx_include_path}"
-	CPPFLAGS="$CPPFLAGS $LOG4CXX_INCLUDES"
-fi
-AC_CHECK_HEADER([log4cxx/logger.h],, AC_MSG_ERROR([Missing log4cxx header files.]))
-CPPFLAGS="$CPPFLAGS_SAVES"
-AC_SUBST(LOG4CXX_INCLUDES)
-
-LOG4CXX_LDFLAGS="-llog4cxx";
-if test "${log4cxx_library_path}"; then
-    LOG4CXX_LDFLAGS="-L${log4cxx_library_path} -llog4cxx"
-fi
-AC_SUBST(LOG4CXX_LDFLAGS)
-
-# The following two lines are part of the urgent fix, and should be cleaned
-# up with a better fix.
-fi
-AM_CONDITIONAL(USE_LOG4CXX, test "X${with_log4cxx}" != "Xno")
-
 #
 # Configure Boost header path
 #
@@ -443,62 +398,69 @@ AC_CHECK_HEADERS([boost/shared_ptr.hpp boost/foreach.hpp boost/interprocess/sync
 CPPFLAGS="$CPPFLAGS_SAVES"
 AC_SUBST(BOOST_INCLUDES)
 
-# Using boost::mutex can result in requiring libboost_thread with older
-# versions of Boost.  We'd like to avoid relying on a compiled Boost library
-# whenever possible, so we need to check for it step by step.
-#
-# NOTE: another fix of this problem is to simply require newer versions of
-# boost.  If we choose that solution we should simplify the following tricky
-# checks accordingly and all Makefile.am's that refer to NEED_LIBBOOST_THREAD.
-AC_MSG_CHECKING(for boost::mutex)
-CPPFLAGS_SAVES="$CPPFLAGS"
-LIBS_SAVES="$LIBS"
-CPPFLAGS="$BOOST_INCLUDES $CPPFLAGS $MULTITHREADING_FLAG"
-need_libboost_thread=0
-need_sunpro_workaround=0
-AC_TRY_LINK([
-#include <boost/thread.hpp>
-],[
-boost::mutex m;
-],
-	[ AC_MSG_RESULT(yes (without libboost_thread)) ],
-
-    # there is one specific problem with SunStudio 5.10
-    # where including boost/thread causes a compilation failure
-    # There is a workaround in boost but it checks the version not being 5.10
-    # This will probably be fixed in the future, in which case this
-    # is only a temporary workaround
-    [ AC_TRY_LINK([
-#if defined(__SUNPRO_CC) && __SUNPRO_CC == 0x5100
-#undef __SUNPRO_CC
-#define __SUNPRO_CC 0x5090
-#endif
-#include <boost/thread.hpp>
-],[
-boost::mutex m;
-],
-    [ AC_MSG_RESULT(yes (with SUNOS workaround))
-      need_sunpro_workaround=1 ],
-    	[ LIBS=" $LIBS -lboost_thread"
-	  AC_TRY_LINK([
-#include <boost/thread.hpp>
-],[
-boost::mutex m;
-],
-		  [ AC_MSG_RESULT(yes (with libboost_thread))
-		    need_libboost_thread=1 ],
-		  [ AC_MSG_RESULT(no)
-		    AC_MSG_ERROR([boost::mutex cannot be linked in this build environment.
-Perhaps you are using an older version of Boost that requires libboost_thread for the mutex support, which does not appear to be available.
-You may want to check the availability of the library or to upgrade Boost.])
-   		  ])])])
-CPPFLAGS="$CPPFLAGS_SAVES"
-LIBS="$LIBS_SAVES"
-AM_CONDITIONAL(NEED_LIBBOOST_THREAD, test $need_libboost_thread = 1)
-if test $need_sunpro_workaround = 1; then
-    AC_DEFINE([NEED_SUNPRO_WORKAROUND], [], [Need boost sunstudio workaround])
+
+if test "${use_boost_threads}" = "yes" ; then
+    AC_DEFINE([USE_BOOST_THREADS], [], [Use boost threads])
+
+    # Using boost::mutex can result in requiring libboost_thread with older
+    # versions of Boost.  We'd like to avoid relying on a compiled Boost library
+    # whenever possible, so we need to check for it step by step.
+    #
+    # NOTE: another fix of this problem is to simply require newer versions of
+    # boost.  If we choose that solution we should simplify the following tricky
+    # checks accordingly and all Makefile.am's that refer to NEED_LIBBOOST_THREAD.
+    AC_MSG_CHECKING(for boost::mutex)
+    CPPFLAGS_SAVES="$CPPFLAGS"
+    LIBS_SAVES="$LIBS"
+    CPPFLAGS="$BOOST_INCLUDES $CPPFLAGS $MULTITHREADING_FLAG"
+    need_libboost_thread=0
+    need_sunpro_workaround=0
+    AC_TRY_LINK([
+    #include <boost/thread.hpp>
+    ],[
+    boost::mutex m;
+    ],
+        [ AC_MSG_RESULT(yes (without libboost_thread)) ],
+        # there is one specific problem with SunStudio 5.10
+        # where including boost/thread causes a compilation failure
+        # There is a workaround in boost but it checks the version not being 5.10
+        # This will probably be fixed in the future, in which case this
+        # is only a temporary workaround
+        [ AC_TRY_LINK([
+    #if defined(__SUNPRO_CC) && __SUNPRO_CC == 0x5100
+    #undef __SUNPRO_CC
+    #define __SUNPRO_CC 0x5090
+    #endif
+    #include <boost/thread.hpp>
+    ],[
+    boost::mutex m;
+    ],
+        [ AC_MSG_RESULT(yes (with SUNOS workaround))
+          need_sunpro_workaround=1 ],
+            [ LIBS=" $LIBS -lboost_thread"
+          AC_TRY_LINK([
+    #include <boost/thread.hpp>
+    ],[
+    boost::mutex m;
+    ],
+              [ AC_MSG_RESULT(yes (with libboost_thread))
+                need_libboost_thread=1 ],
+              [ AC_MSG_RESULT(no)
+                AC_MSG_ERROR([boost::mutex cannot be linked in this build environment.
+    Perhaps you are using an older version of Boost that requires libboost_thread for the mutex support, which does not appear to be available.
+    You may want to check the availability of the library or to upgrade Boost.])
+              ])])])
+    CPPFLAGS="$CPPFLAGS_SAVES"
+    LIBS="$LIBS_SAVES"
+    AM_CONDITIONAL(NEED_LIBBOOST_THREAD, test $need_libboost_thread = 1)
+    if test $need_sunpro_workaround = 1; then
+        AC_DEFINE([NEED_SUNPRO_WORKAROUND], [], [Need boost sunstudio workaround])
+    fi
+else
+    AM_CONDITIONAL(NEED_LIBBOOST_THREAD, test "${use_boost_threads}" = "yes")
 fi
 
+
 #
 # Check availability of gtest, which will be used for unit tests.
 #
@@ -821,8 +783,6 @@ dnl includes too
                  ${PYTHON_LDFLAGS}
                  ${PYTHON_LIB}
   Boost:         ${BOOST_INCLUDES}
-  log4cxx:       ${LOG4CXX_INCLUDES}
-                 ${LOG4CXX_LDFLAGS}
   SQLite:        $SQLITE_CFLAGS
                  $SQLITE_LIBS
 
diff --git a/doc/guide/bind10-guide.html b/doc/guide/bind10-guide.html
index 98c7e46..fe6bd93 100644
--- a/doc/guide/bind10-guide.html
+++ b/doc/guide/bind10-guide.html
@@ -1,23 +1,24 @@
-<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>BIND 10 Guide</title><link rel="stylesheet" href="./bind10-guide.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.75.2"><meta name="description" content="BIND 10 is a Domain Name System (DNS) suite managed by Internet Systems Consortium (ISC). It includes DNS libraries and modular components for controlling authoritative and recursive DNS servers. This is the reference guide for BIND 10 version 20110120. The most up-to-date version of this document, along with other documents for BIND 10, can be found at ."></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="book" title="BIND 10 Guide"><div class="titlepage"><div><div><h1 class="title"><a name="id1168230298903"></a>BIND 10 Guide</h1></div><div><h2 class="subtitle">Administrator Reference for BIND 10</h2></div><div><p class="releaseinfo">This is the referenc
 e guide for BIND 10 version
-        20110120.</p></div><div><p class="copyright">Copyright © 2010 Internet Systems Consortium, Inc.</p></div><div><div class="abstract" title="Abstract"><p class="title"><b>Abstract</b></p><p>BIND 10 is a Domain Name System (DNS) suite managed by
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>BIND 10 Guide</title><link rel="stylesheet" href="./bind10-guide.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.75.2"><meta name="description" content="BIND 10 is a Domain Name System (DNS) suite managed by Internet Systems Consortium (ISC). It includes DNS libraries and modular components for controlling authoritative and recursive DNS servers. This is the reference guide for BIND 10 version 20110224. The most up-to-date version of this document, along with other documents for BIND 10, can be found at ."></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="book" title="BIND 10 Guide"><div class="titlepage"><div><div><h1 class="title"><a name="id1168230298903"></a>BIND 10 Guide</h1></div><div><h2 class="subtitle">Administrator Reference for BIND 10</h2></div><div><p class="releaseinfo">This is the referenc
 e guide for BIND 10 version
+        20110224.</p></div><div><p class="copyright">Copyright © 2010 Internet Systems Consortium, Inc.</p></div><div><div class="abstract" title="Abstract"><p class="title"><b>Abstract</b></p><p>BIND 10 is a Domain Name System (DNS) suite managed by
 	Internet Systems Consortium (ISC). It includes DNS libraries
 	and modular components for controlling authoritative and
 	recursive DNS servers.
       </p><p>
-        This is the reference guide for BIND 10 version 20110120.
+        This is the reference guide for BIND 10 version 20110224.
 	The most up-to-date version of this document, along with
-	other documents for BIND 10, can be found at <a class="ulink" href="http://bind10.isc.org/docs" target="_top">http://bind10.isc.org/docs</a>.  </p></div></div></div><hr></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="chapter"><a href="#intro">1. Introduction</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230299042">Supported Platforms</a></span></dt><dt><span class="section"><a href="#id1168230299068">Required Software</a></span></dt><dt><span class="section"><a href="#starting_stopping">Starting and Stopping the Server</a></span></dt><dt><span class="section"><a href="#managing_once_running">Managing BIND 10</a></span></dt></dl></dd><dt><span class="chapter"><a href="#installation">2. Installation</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230284843">Building Requirements</a></span></dt><dt><span class="section"><a href="#quickstart">Quick start</a></span></dt><dt><span class="section"><a href="#install">In
 stallation from source</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230285029">Download Tar File</a></span></dt><dt><span class="section"><a href="#id1168230285048">Retrieve from Git</a></span></dt><dt><span class="section"><a href="#id1168230285109">Configure before the build</a></span></dt><dt><span class="section"><a href="#id1168230285206">Build</a></span></dt><dt><span class="section"><a href="#id1168230285222">Install</a></span></dt><dt><span class="section"><a href="#id1168230285245">Install Hierarchy</a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="#bind10">3. Starting BIND10 with <span class="command"><strong>bind10</strong></span></a></span></dt><dd><dl><dt><span class="section"><a href="#start">Starting BIND 10</a></span></dt></dl></dd><dt><span class="chapter"><a href="#msgq">4. Command channel</a></span></dt><dt><span class="chapter"><a href="#cfgmgr">5. Configuration manager</a></span></dt><dt><span class="chapter"><a hr
 ef="#cmdctl">6. Remote control daemon</a></span></dt><dd><dl><dt><span class="section"><a href="#cmdctl.spec">Configuration specification for b10-cmdctl</a></span></dt></dl></dd><dt><span class="chapter"><a href="#bindctl">7. Control and configure user interface</a></span></dt><dt><span class="chapter"><a href="#authserver">8. Authoritative Server</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230285821">Server Configurations</a></span></dt><dt><span class="section"><a href="#id1168230285886">Data Source Backends</a></span></dt><dt><span class="section"><a href="#id1168230285917">Loading Master Zones Files</a></span></dt></dl></dd><dt><span class="chapter"><a href="#xfrin">9. Incoming Zone Transfers</a></span></dt><dt><span class="chapter"><a href="#xfrout">10. Outbound Zone Transfers</a></span></dt><dt><span class="chapter"><a href="#zonemgr">11. Secondary Manager</a></span></dt><dt><span class="chapter"><a href="#resolverserver">12. Recursive Name Server<
 /a></span></dt><dt><span class="chapter"><a href="#statistics">13. Statistics</a></span></dt></dl></div><div class="chapter" title="Chapter 1. Introduction"><div class="titlepage"><div><div><h2 class="title"><a name="intro"></a>Chapter 1. Introduction</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id1168230299042">Supported Platforms</a></span></dt><dt><span class="section"><a href="#id1168230299068">Required Software</a></span></dt><dt><span class="section"><a href="#starting_stopping">Starting and Stopping the Server</a></span></dt><dt><span class="section"><a href="#managing_once_running">Managing BIND 10</a></span></dt></dl></div><p>
+	other documents for BIND 10, can be found at <a class="ulink" href="http://bind10.isc.org/docs" target="_top">http://bind10.isc.org/docs</a>.  </p></div></div></div><hr></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="chapter"><a href="#intro">1. Introduction</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230299038">Supported Platforms</a></span></dt><dt><span class="section"><a href="#id1168230299065">Required Software</a></span></dt><dt><span class="section"><a href="#starting_stopping">Starting and Stopping the Server</a></span></dt><dt><span class="section"><a href="#managing_once_running">Managing BIND 10</a></span></dt></dl></dd><dt><span class="chapter"><a href="#installation">2. Installation</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230284842">Building Requirements</a></span></dt><dt><span class="section"><a href="#quickstart">Quick start</a></span></dt><dt><span class="section"><a href="#install">In
 stallation from source</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230285028">Download Tar File</a></span></dt><dt><span class="section"><a href="#id1168230285047">Retrieve from Git</a></span></dt><dt><span class="section"><a href="#id1168230285108">Configure before the build</a></span></dt><dt><span class="section"><a href="#id1168230285205">Build</a></span></dt><dt><span class="section"><a href="#id1168230285221">Install</a></span></dt><dt><span class="section"><a href="#id1168230285244">Install Hierarchy</a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="#bind10">3. Starting BIND10 with <span class="command"><strong>bind10</strong></span></a></span></dt><dd><dl><dt><span class="section"><a href="#start">Starting BIND 10</a></span></dt></dl></dd><dt><span class="chapter"><a href="#msgq">4. Command channel</a></span></dt><dt><span class="chapter"><a href="#cfgmgr">5. Configuration manager</a></span></dt><dt><span class="chapter"><a hr
 ef="#cmdctl">6. Remote control daemon</a></span></dt><dd><dl><dt><span class="section"><a href="#cmdctl.spec">Configuration specification for b10-cmdctl</a></span></dt></dl></dd><dt><span class="chapter"><a href="#bindctl">7. Control and configure user interface</a></span></dt><dt><span class="chapter"><a href="#authserver">8. Authoritative Server</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230285822">Server Configurations</a></span></dt><dt><span class="section"><a href="#id1168230285888">Data Source Backends</a></span></dt><dt><span class="section"><a href="#id1168230285918">Loading Master Zones Files</a></span></dt></dl></dd><dt><span class="chapter"><a href="#xfrin">9. Incoming Zone Transfers</a></span></dt><dt><span class="chapter"><a href="#xfrout">10. Outbound Zone Transfers</a></span></dt><dt><span class="chapter"><a href="#zonemgr">11. Secondary Manager</a></span></dt><dt><span class="chapter"><a href="#resolverserver">12. Recursive Name Server<
 /a></span></dt><dt><span class="chapter"><a href="#statistics">13. Statistics</a></span></dt></dl></div><div class="chapter" title="Chapter 1. Introduction"><div class="titlepage"><div><div><h2 class="title"><a name="intro"></a>Chapter 1. Introduction</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id1168230299038">Supported Platforms</a></span></dt><dt><span class="section"><a href="#id1168230299065">Required Software</a></span></dt><dt><span class="section"><a href="#starting_stopping">Starting and Stopping the Server</a></span></dt><dt><span class="section"><a href="#managing_once_running">Managing BIND 10</a></span></dt></dl></div><p>
       BIND is the popular implementation of a DNS server, developer
       interfaces, and DNS tools.
       BIND 10 is a rewrite of BIND 9.  BIND 10 is written in C++ and Python
       and provides a modular environment for serving and maintaining DNS.
     </p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
         This guide covers the experimental prototype of
-        BIND 10 version 20110120.
+        BIND 10 version 20110224.
       </p></div><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
         BIND 10 provides a EDNS0- and DNSSEC-capable
-        authoritative DNS server and a forwarding DNS server.
-      </p></div><div class="section" title="Supported Platforms"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230299042"></a>Supported Platforms</h2></div></div></div><p>
+        authoritative DNS server and a caching recursive name server
+        which also provides forwarding.
+      </p></div><div class="section" title="Supported Platforms"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230299038"></a>Supported Platforms</h2></div></div></div><p>
   BIND 10 builds have been tested on Debian GNU/Linux 5,
   Ubuntu 9.10, NetBSD 5, Solaris 10, FreeBSD 7 and 8, and CentOS
   Linux 5.3.
@@ -27,13 +28,11 @@
 
         It is planned for BIND 10 to build, install and run on
         Windows and standard Unix-type platforms.
-      </p></div><div class="section" title="Required Software"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230299068"></a>Required Software</h2></div></div></div><p>
+      </p></div><div class="section" title="Required Software"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230299065"></a>Required Software</h2></div></div></div><p>
         BIND 10 requires Python 3.1.  Later versions may work, but Python
         3.1 is the minimum version which will work.
       </p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
-	For this development prototype release, the only supported
-	data source backend is SQLite3. The authoritative server
-	requires SQLite 3.3.9 or newer.
+	The authoritative server requires SQLite 3.3.9 or newer.
 	The <span class="command"><strong>b10-xfrin</strong></span>, <span class="command"><strong>b10-xfrout</strong></span>,
 	and <span class="command"><strong>b10-zonemgr</strong></span> modules require the
 	libpython3 library and the Python _sqlite3.so module.
@@ -133,7 +132,7 @@
       and, of course, DNS. These include detailed developer
       documentation and code examples.
 
-    </p></div><div class="chapter" title="Chapter 2. Installation"><div class="titlepage"><div><div><h2 class="title"><a name="installation"></a>Chapter 2. Installation</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id1168230284843">Building Requirements</a></span></dt><dt><span class="section"><a href="#quickstart">Quick start</a></span></dt><dt><span class="section"><a href="#install">Installation from source</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230285029">Download Tar File</a></span></dt><dt><span class="section"><a href="#id1168230285048">Retrieve from Git</a></span></dt><dt><span class="section"><a href="#id1168230285109">Configure before the build</a></span></dt><dt><span class="section"><a href="#id1168230285206">Build</a></span></dt><dt><span class="section"><a href="#id1168230285222">Install</a></span></dt><dt><span class="section"><a href="#id1168230285245">Install Hierarchy<
 /a></span></dt></dl></dd></dl></div><div class="section" title="Building Requirements"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230284843"></a>Building Requirements</h2></div></div></div><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
+    </p></div><div class="chapter" title="Chapter 2. Installation"><div class="titlepage"><div><div><h2 class="title"><a name="installation"></a>Chapter 2. Installation</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id1168230284842">Building Requirements</a></span></dt><dt><span class="section"><a href="#quickstart">Quick start</a></span></dt><dt><span class="section"><a href="#install">Installation from source</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230285028">Download Tar File</a></span></dt><dt><span class="section"><a href="#id1168230285047">Retrieve from Git</a></span></dt><dt><span class="section"><a href="#id1168230285108">Configure before the build</a></span></dt><dt><span class="section"><a href="#id1168230285205">Build</a></span></dt><dt><span class="section"><a href="#id1168230285221">Install</a></span></dt><dt><span class="section"><a href="#id1168230285244">Install Hierarchy<
 /a></span></dt></dl></dd></dl></div><div class="section" title="Building Requirements"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230284842"></a>Building Requirements</h2></div></div></div><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
             Some operating systems have split their distribution packages into
             a run-time and a development package.  You will need to install
             the development package versions, which include header files and
@@ -193,14 +192,14 @@
         the Git code revision control system or as a downloadable
         tar file. It may also be available in pre-compiled ready-to-use
         packages from operating system vendors.
-      </p><div class="section" title="Download Tar File"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230285029"></a>Download Tar File</h3></div></div></div><p>
+      </p><div class="section" title="Download Tar File"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230285028"></a>Download Tar File</h3></div></div></div><p>
           Downloading a release tar file is the recommended method to
           obtain the source code.
         </p><p>
           The BIND 10 releases are available as tar file downloads from
           <a class="ulink" href="ftp://ftp.isc.org/isc/bind10/" target="_top">ftp://ftp.isc.org/isc/bind10/</a>.
           Periodic development snapshots may also be available.
-        </p></div><div class="section" title="Retrieve from Git"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230285048"></a>Retrieve from Git</h3></div></div></div><p>
+        </p></div><div class="section" title="Retrieve from Git"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230285047"></a>Retrieve from Git</h3></div></div></div><p>
           Downloading this "bleeding edge" code is recommended only for
           developers or advanced users.  Using development code in a production
           environment is not recommended.
@@ -234,7 +233,7 @@
           <span class="command"><strong>autoheader</strong></span>,
           <span class="command"><strong>automake</strong></span>,
           and related commands.
-        </p></div><div class="section" title="Configure before the build"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230285109"></a>Configure before the build</h3></div></div></div><p>
+        </p></div><div class="section" title="Configure before the build"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230285108"></a>Configure before the build</h3></div></div></div><p>
           BIND 10 uses the GNU Build System to discover build environment
           details.
           To generate the makefiles using the defaults, simply run:
@@ -265,16 +264,16 @@
         </p><p>
           If the configure fails, it may be due to missing or old
           dependencies.
-        </p></div><div class="section" title="Build"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230285206"></a>Build</h3></div></div></div><p>
+        </p></div><div class="section" title="Build"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230285205"></a>Build</h3></div></div></div><p>
     After the configure step is complete, to build the executables
     from the C++ code and prepare the Python scripts, run:
 
           </p><pre class="screen">$ <strong class="userinput"><code>make</code></strong></pre><p>
-        </p></div><div class="section" title="Install"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230285222"></a>Install</h3></div></div></div><p>
+        </p></div><div class="section" title="Install"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230285221"></a>Install</h3></div></div></div><p>
           To install the BIND 10 executables, support files,
           and documentation, run:
           </p><pre class="screen">$ <strong class="userinput"><code>make install</code></strong></pre><p>
-        </p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>The install step may require superuser privileges.</p></div></div><div class="section" title="Install Hierarchy"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230285245"></a>Install Hierarchy</h3></div></div></div><p>
+        </p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>The install step may require superuser privileges.</p></div></div><div class="section" title="Install Hierarchy"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230285244"></a>Install Hierarchy</h3></div></div></div><p>
           The following is the layout of the complete BIND 10 installation:
           </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem">
                 <code class="filename">bin/</code> —
@@ -465,6 +464,8 @@ accounts_file
       </p><p>
         The control commands are:
 print_settings
+
+
 shutdown
       </p></div></div><div class="chapter" title="Chapter 7. Control and configure user interface"><div class="titlepage"><div><div><h2 class="title"><a name="bindctl"></a>Chapter 7. Control and configure user interface</h2></div></div></div><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
       For this development prototype release, <span class="command"><strong>bindctl</strong></span>
@@ -489,7 +490,7 @@ shutdown
       the details and relays (over a <span class="command"><strong>b10-msgq</strong></span> command
       channel) the configuration on to the specified module.
     </p><p>
-    </p></div><div class="chapter" title="Chapter 8. Authoritative Server"><div class="titlepage"><div><div><h2 class="title"><a name="authserver"></a>Chapter 8. Authoritative Server</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id1168230285821">Server Configurations</a></span></dt><dt><span class="section"><a href="#id1168230285886">Data Source Backends</a></span></dt><dt><span class="section"><a href="#id1168230285917">Loading Master Zones Files</a></span></dt></dl></div><p>
+    </p></div><div class="chapter" title="Chapter 8. Authoritative Server"><div class="titlepage"><div><div><h2 class="title"><a name="authserver"></a>Chapter 8. Authoritative Server</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id1168230285822">Server Configurations</a></span></dt><dt><span class="section"><a href="#id1168230285888">Data Source Backends</a></span></dt><dt><span class="section"><a href="#id1168230285918">Loading Master Zones Files</a></span></dt></dl></div><p>
       The <span class="command"><strong>b10-auth</strong></span> is the authoritative DNS server.
       It supports EDNS0 and DNSSEC. It supports IPv6.
       Normally it is started by the <span class="command"><strong>bind10</strong></span> master
@@ -497,7 +498,7 @@ shutdown
     </p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
       This development prototype release listens on all interfaces
       and the non-standard port 5300.
-    </p></div><div class="section" title="Server Configurations"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230285821"></a>Server Configurations</h2></div></div></div><p>
+    </p></div><div class="section" title="Server Configurations"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230285822"></a>Server Configurations</h2></div></div></div><p>
         <span class="command"><strong>b10-auth</strong></span> is configured via the
         <span class="command"><strong>b10-cfgmgr</strong></span> configuration manager.
         The module name is <span class="quote">“<span class="quote">Auth</span>”</span>.
@@ -517,11 +518,12 @@ This may be a temporary setting until then.
         </p><div class="variablelist"><dl><dt><span class="term">shutdown</span></dt><dd>Stop the authoritative DNS server.
               </dd></dl></div><p>
 
-      </p></div><div class="section" title="Data Source Backends"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230285886"></a>Data Source Backends</h2></div></div></div><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
+      </p></div><div class="section" title="Data Source Backends"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230285888"></a>Data Source Backends</h2></div></div></div><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
         For the development prototype release, <span class="command"><strong>b10-auth</strong></span>
-        only supports the SQLite3 data source backend.
+        supports a SQLite3 data source backend and in-memory data source
+        backend.
         Upcoming versions will be able to use multiple different
-        data sources, such as MySQL, Berkeley DB, or in-memory DB.
+        data sources, such as MySQL and Berkeley DB.
       </p></div><p>
         By default, the SQLite3 backend uses the data file located at
         <code class="filename">/usr/local/var/bind10-devel/zone.sqlite3</code>.
@@ -530,7 +532,7 @@ This may be a temporary setting until then.
         The default is <code class="filename">/usr/local/var/</code>.)
   This data file location may be changed by defining the
   <span class="quote">“<span class="quote">database_file</span>”</span> configuration.
-      </p></div><div class="section" title="Loading Master Zones Files"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230285917"></a>Loading Master Zones Files</h2></div></div></div><p>
+      </p></div><div class="section" title="Loading Master Zones Files"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230285918"></a>Loading Master Zones Files</h2></div></div></div><p>
         RFC 1035 style DNS master zone files may imported
         into a BIND 10 data source by using the
         <span class="command"><strong>b10-loadzone</strong></span> utility.
@@ -610,11 +612,7 @@ This may be a temporary setting until then.
       The <span class="command"><strong>b10-resolver</strong></span> process is started by
       <span class="command"><strong>bind10</strong></span>.
 
-    </p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
-      The current version only provides a forwarding DNS server.
-      It does not cache and does not iterate to find answers.
-      It simply forwards the query on to another full resolver.
-    </p></div><p>
+    </p><p>
       The main <span class="command"><strong>bind10</strong></span> process can be configured
       to select to run either the authoritative or resolver.
       By default, it starts the authoritative service.
@@ -628,12 +626,20 @@ This may be a temporary setting until then.
 > <strong class="userinput"><code>config commit</code></strong>
 </pre><p>
 
-    </p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
-       In the current version, the master <span class="command"><strong>bind10</strong></span>
-       process must be stopped and restarted to start up the resolver.
-    </p></div><p>
-      Then the upstream address and port must be configured to
-      forward queries to, such as:
+    </p><p>
+       The master <span class="command"><strong>bind10</strong></span> will stop and start
+       the desired services.
+    </p><p>
+      The resolver also needs to be configured to listen on an address
+      and port:
+
+      </p><pre class="screen">
+> <strong class="userinput"><code>config set Resolver/listen_on [{ "address": "127.0.0.1", "port": 53 }]</code></strong>
+> <strong class="userinput"><code>config commit</code></strong>
+</pre><p>
+    </p><p>
+      To enable forwarding, the upstream address and port must be
+      configured to forward queries to, such as:
 
       </p><pre class="screen">
 > <strong class="userinput"><code>config set Resolver/forward_addresses [{ "address": "<em class="replaceable"><code>192.168.1.1</code></em>", "port": 53 }]</code></strong>
@@ -643,11 +649,11 @@ This may be a temporary setting until then.
       (Replace <em class="replaceable"><code>192.168.1.1</code></em> to point to your
       full resolver.)
     </p><p>
-      The resolver also needs to be configured to listen on an address
-      and port:
+      Normal iterative name service can be re-enabled by clearing the
+      forwarding address(es); for example:
 
       </p><pre class="screen">
-> <strong class="userinput"><code>config set Resolver/listen_on [{ "address": "127.0.0.1", "port": 53 }]</code></strong>
+> <strong class="userinput"><code>config set Resolver/forward_addresses []</code></strong>
 > <strong class="userinput"><code>config commit</code></strong>
 </pre><p>
     </p></div><div class="chapter" title="Chapter 13. Statistics"><div class="titlepage"><div><div><h2 class="title"><a name="statistics"></a>Chapter 13. Statistics</h2></div></div></div><p>
diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml
index 3670c46..0935c81 100644
--- a/doc/guide/bind10-guide.xml
+++ b/doc/guide/bind10-guide.xml
@@ -52,7 +52,8 @@
     <note>
       <para>
         BIND 10 provides a EDNS0- and DNSSEC-capable
-        authoritative DNS server and a forwarding DNS server.
+        authoritative DNS server and a caching recursive name server
+        which also provides forwarding.
       </para>
     </note>
 
@@ -79,9 +80,7 @@
       </para>
 
       <note><para>
-	For this development prototype release, the only supported
-	data source backend is SQLite3. The authoritative server
-	requires SQLite 3.3.9 or newer.
+	The authoritative server requires SQLite 3.3.9 or newer.
 	The <command>b10-xfrin</command>, <command>b10-xfrout</command>,
 	and <command>b10-zonemgr</command> modules require the
 	libpython3 library and the Python _sqlite3.so module.
@@ -1108,9 +1107,10 @@ This may be a temporary setting until then.
 
       <note><para>
         For the development prototype release, <command>b10-auth</command>
-        only supports the SQLite3 data source backend.
+        supports a SQLite3 data source backend and in-memory data source
+        backend.
         Upcoming versions will be able to use multiple different
-        data sources, such as MySQL, Berkeley DB, or in-memory DB.
+        data sources, such as MySQL and Berkeley DB.
       </para></note>
 
 
@@ -1309,12 +1309,6 @@ what is XfroutClient xfr_client??
 -->
     </para>
 
-    <note><simpara>
-      The current version only provides a forwarding DNS server.
-      It does not cache and does not iterate to find answers.
-      It simply forwards the query on to another full resolver.
-    </simpara></note>
-
     <para>
       The main <command>bind10</command> process can be configured
       to select to run either the authoritative or resolver.
@@ -1331,15 +1325,26 @@ what is XfroutClient xfr_client??
 
     </para>
 
-<!-- TODO: -->
-    <note><simpara>
-       In the current version, the master <command>bind10</command>
-       process must be stopped and restarted to start up the resolver.
-    </simpara></note>
+    <para>
+       The master <command>bind10</command> will stop and start
+       the desired services.
+    </para>
 
     <para>
-      Then the upstream address and port must be configured to
-      forward queries to, such as:
+      The resolver also needs to be configured to listen on an address
+      and port:
+
+      <screen>
+> <userinput>config set Resolver/listen_on [{ "address": "127.0.0.1", "port": 53 }]</userinput>
+> <userinput>config commit</userinput>
+</screen>
+    </para>
+
+<!-- TODO: later the above will have some defaults -->
+
+    <para>
+      To enable forwarding, the upstream address and port must be
+      configured to forward queries to, such as:
 
       <screen>
 > <userinput>config set Resolver/forward_addresses [{ "address": "<replaceable>192.168.1.1</replaceable>", "port": 53 }]</userinput>
@@ -1351,17 +1356,15 @@ what is XfroutClient xfr_client??
     </para>
 
     <para>
-      The resolver also needs to be configured to listen on an address
-      and port:
+      Normal iterative name service can be re-enabled by clearing the
+      forwarding address(es); for example:
 
       <screen>
-> <userinput>config set Resolver/listen_on [{ "address": "127.0.0.1", "port": 53 }]</userinput>
+> <userinput>config set Resolver/forward_addresses []</userinput>
 > <userinput>config commit</userinput>
 </screen>
     </para>
 
-<!-- TODO: later the above will have some defaults -->
-
 <!-- TODO: later try this
 
 > config set Resolver/forward_addresses[0]/address "192.168.8.8"
diff --git a/src/bin/auth/Makefile.am b/src/bin/auth/Makefile.am
index e9097f2..bb8f1bb 100644
--- a/src/bin/auth/Makefile.am
+++ b/src/bin/auth/Makefile.am
@@ -50,11 +50,12 @@ b10_auth_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
 b10_auth_LDADD += $(top_builddir)/src/lib/cc/libcc.la
 b10_auth_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 b10_auth_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
+b10_auth_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
 b10_auth_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
 b10_auth_LDADD += $(SQLITE_LIBS)
 
 # TODO: config.h.in is wrong because doesn't honor pkgdatadir
 # and can't use @datadir@ because doesn't expand default ${prefix}
-b10_authdir = $(DESTDIR)$(pkgdatadir)
+b10_authdir = $(pkgdatadir)
 b10_auth_DATA = auth.spec
 
diff --git a/src/bin/auth/auth.spec.pre.in b/src/bin/auth/auth.spec.pre.in
index 8a77455..7cb571c 100644
--- a/src/bin/auth/auth.spec.pre.in
+++ b/src/bin/auth/auth.spec.pre.in
@@ -12,45 +12,44 @@
         "item_type": "list",
         "item_optional": true,
         "item_default": [],
-	"list_item_spec": {
-          "item_name": "list_element",
+        "list_item_spec":
+        { "item_name": "list_element",
           "item_type": "map",
           "item_optional": false,
           "item_default": {},
-	  "map_item_spec": [
-	    { "item_name": "type",
-	      "item_type": "string",
-	      "item_optional": false,
-	      "item_default": ""
-	    },
-	    { "item_name": "class",
-	      "item_type": "string",
-	      "item_optional": false,
-	      "item_default": "IN"
-	    },
-	    { "item_name": "zones",
-	      "item_type": "list",
-	      "item_optional": false,
-	      "item_default": [],
-	      "list_item_spec": {
-	        "item_name": "list_element",
-	        "item_type": "map",
-	        "item_optional": true,
-	        "map_item_spec": [
-		  { "item_name": "origin",
-		    "item_type": "string",
-		    "item_optional": false,
-		    "item_default": ""
-		  },
-		  { "item_name": "file",
-		    "item_type": "string",
-		    "item_optional": false,
-		    "item_default": ""
-		  }
-		]
-	      }
-	    }
-	  ]
+          "map_item_spec": [
+          { "item_name": "type",
+            "item_type": "string",
+            "item_optional": false,
+            "item_default": ""
+          },
+          { "item_name": "class",
+            "item_type": "string",
+            "item_optional": false,
+            "item_default": "IN"
+          },
+          { "item_name": "zones",
+            "item_type": "list",
+            "item_optional": false,
+            "item_default": [],
+            "list_item_spec":
+            { "item_name": "list_element",
+              "item_type": "map",
+              "item_optional": true,
+              "item_default": { "origin": "", "file": "" },
+              "map_item_spec": [
+              { "item_name": "origin",
+                "item_type": "string",
+                "item_optional": false,
+                "item_default": ""
+              },
+              { "item_name": "file",
+                "item_type": "string",
+                "item_optional": false,
+                "item_default": ""
+              }]
+            }
+          }]
         }
       },
       { "item_name": "statistics-interval",
diff --git a/src/bin/auth/benchmarks/Makefile.am b/src/bin/auth/benchmarks/Makefile.am
index 05ab754..653d502 100644
--- a/src/bin/auth/benchmarks/Makefile.am
+++ b/src/bin/auth/benchmarks/Makefile.am
@@ -21,5 +21,6 @@ query_bench_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
 query_bench_LDADD += $(top_builddir)/src/lib/cc/libcc.la
 query_bench_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
 query_bench_LDADD += $(top_builddir)/src/lib/log/liblog.la
+query_bench_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
 query_bench_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
 query_bench_LDADD += $(SQLITE_LIBS)
diff --git a/src/bin/auth/tests/Makefile.am b/src/bin/auth/tests/Makefile.am
index a1114e4..def99b0 100644
--- a/src/bin/auth/tests/Makefile.am
+++ b/src/bin/auth/tests/Makefile.am
@@ -45,6 +45,7 @@ run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la
 run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 run_unittests_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
 run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
+run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
 endif
 
 noinst_PROGRAMS = $(TESTS)
diff --git a/src/bin/auth/tests/query_unittest.cc b/src/bin/auth/tests/query_unittest.cc
index 2d3cf03..05dd748 100644
--- a/src/bin/auth/tests/query_unittest.cc
+++ b/src/bin/auth/tests/query_unittest.cc
@@ -201,7 +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));
diff --git a/src/bin/bind10/Makefile.am b/src/bin/bind10/Makefile.am
index 1445b95..254875f 100644
--- a/src/bin/bind10/Makefile.am
+++ b/src/bin/bind10/Makefile.am
@@ -5,7 +5,7 @@ CLEANFILES = bind10 bind10.pyc
 
 pkglibexecdir = $(libexecdir)/@PACKAGE@
 
-bind10dir = $(DESTDIR)$(pkgdatadir)
+bind10dir = $(pkgdatadir)
 bind10_DATA = bob.spec
 EXTRA_DIST = bob.spec
 
@@ -19,7 +19,6 @@ bind10.8: bind10.xml
 
 endif
 
-# TODO: does this need $$(DESTDIR) also?
 # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
 bind10: bind10.py
 	$(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" \
diff --git a/src/bin/bind10/bind10.8 b/src/bin/bind10/bind10.8
index a3ac653..f625abe 100644
--- a/src/bin/bind10/bind10.8
+++ b/src/bin/bind10/bind10.8
@@ -71,7 +71,7 @@ daemon to listen on\&. The default is 5300\&.
 \fBNote\fR
 .ps -1
 .br
-The Y1 prototype release uses a non\-default port for domain service\&.
+This prototype release uses a non\-default port for domain service\&.
 .sp .5v
 .RE
 .RE
diff --git a/src/bin/bind10/bind10.py.in b/src/bin/bind10/bind10.py.in
old mode 100644
new mode 100755
index 48d5b38..5a4ba4a
--- a/src/bin/bind10/bind10.py.in
+++ b/src/bin/bind10/bind10.py.in
@@ -72,7 +72,7 @@ isc.util.process.rename(sys.argv[0])
 # This is the version that gets displayed to the user.
 # The VERSION string consists of the module name, the module version
 # number, and the overall BIND 10 version number (set in configure.ac).
-VERSION = "bind10 20101129 (BIND 10 @PACKAGE_VERSION@)"
+VERSION = "bind10 20110223 (BIND 10 @PACKAGE_VERSION@)"
 
 # This is for bind10.boottime of stats module
 _BASETIME = time.gmtime()
@@ -209,23 +209,68 @@ class BoB:
         self.ccs = None
         self.cfg_start_auth = True
         self.cfg_start_resolver = False
+        self.started_auth_family = False
+        self.started_resolver_family = False
         self.curproc = None
         self.dead_processes = {}
         self.msgq_socket_file = msgq_socket_file
         self.nocache = nocache
         self.processes = {}
+        self.expected_shutdowns = {}
         self.runnable = False
         self.uid = setuid
         self.username = username
         self.verbose = verbose
 
     def config_handler(self, new_config):
+        # If this is initial update, don't do anything now, leave it to startup
+        if not self.runnable:
+            return
+        # Now we declare few functions used only internally here. Besides the
+        # benefit of not polluting the name space, they are closures, so we
+        # don't need to pass some variables
+        def start_stop(name, started, start, stop):
+            if not'start_' + name in new_config:
+                return
+            if new_config['start_' + name]:
+                if not started:
+                    if self.uid is not None:
+                        sys.stderr.write("[bind10] Starting " + name + " as " +
+                            "a user, not root. This might fail.\n")
+                    start()
+            else:
+                stop()
+        # These four functions are passed to start_stop (smells like functional
+        # programming little bit)
+        def resolver_on():
+            self.start_resolver(self.c_channel_env)
+            self.started_resolver_family = True
+        def resolver_off():
+            self.stop_resolver()
+            self.started_resolver_family = False
+        def auth_on():
+            self.start_auth(self.c_channel_env)
+            self.start_xfrout(self.c_channel_env)
+            self.start_xfrin(self.c_channel_env)
+            self.start_zonemgr(self.c_channel_env)
+            self.started_auth_family = True
+        def auth_off():
+            self.stop_zonemgr()
+            self.stop_xfrin()
+            self.stop_xfrout()
+            self.stop_auth()
+            self.started_auth_family = False
+
+        # The real code of the config handler function follows here
         if self.verbose:
             sys.stdout.write("[bind10] Handling new configuration: " +
                 str(new_config) + "\n")
+        start_stop('resolver', self.started_resolver_family, resolver_on,
+            resolver_off)
+        start_stop('auth', self.started_auth_family, auth_on, auth_off)
+
         answer = isc.config.ccsession.create_answer(0)
         return answer
-        # TODO
 
     def command_handler(self, command, args):
         if self.verbose:
@@ -314,8 +359,8 @@ class BoB:
             sys.stdout.write("\n")
 
     # The next few methods start the individual processes of BIND-10.  They
-    # are called via start_all_process().  If any fail, an exception is raised
-    # which is caught by the caller of start_all_processes(); this kills
+    # are called via start_all_processes().  If any fail, an exception is
+    # raised which is caught by the caller of start_all_processes(); this kills
     # processes started up to that point before terminating the program.
 
     def start_msgq(self, c_channel_env):
@@ -464,11 +509,12 @@ class BoB:
         # XXX: we hardcode port 8080
         self.start_simple("b10-cmdctl", c_channel_env, 8080)
 
-    def start_all_processes(self, c_channel_env):
+    def start_all_processes(self):
         """
             Starts up all the processes.  Any exception generated during the
             starting of the processes is handled by the caller.
         """
+        c_channel_env = self.c_channel_env
         self.start_msgq(c_channel_env)
         self.start_cfgmgr(c_channel_env)
         self.start_ccsession(c_channel_env)
@@ -485,6 +531,7 @@ class BoB:
         # ... and resolver (if selected):
         if self.cfg_start_resolver:
             self.start_resolver(c_channel_env)
+            self.started_resolver_family = True
 
         # Everything after the main components can run as non-root.
         # TODO: this is only temporary - once the privileged socket creator is
@@ -498,6 +545,7 @@ class BoB:
             self.start_xfrout(c_channel_env)
             self.start_xfrin(c_channel_env)
             self.start_zonemgr(c_channel_env)
+            self.started_auth_family = True
 
         # ... and finally start the remaining processes
         self.start_stats(c_channel_env)
@@ -528,7 +576,8 @@ class BoB:
         # Start all processes.  If any one fails to start, kill all started
         # processes and exit with an error indication.
         try:
-            self.start_all_processes(c_channel_env)
+            self.c_channel_env = c_channel_env
+            self.start_all_processes()
         except Exception as e:
             self.kill_started_processes()
             return "Unable to start " + self.curproc + ": " + str(e)
@@ -550,10 +599,35 @@ class BoB:
         self.cc_session.group_sendmsg(cmd, "Zonemgr", "Zonemgr")
         self.cc_session.group_sendmsg(cmd, "Stats", "Stats")
 
-    def stop_process(self, process):
-        """Stop the given process, friendly-like."""
-        # XXX nothing yet
-        pass
+    def stop_process(self, process, recipient):
+        """
+        Stop the given process, friendly-like. The process is the name it has
+        (in logs, etc), the recipient is the address on msgq.
+        """
+        if self.verbose:
+            sys.stdout.write("[bind10] Asking %s to terminate\n" % process)
+        # TODO: Some timeout to solve processes that don't want to die would
+        # help. We can even store it in the dict, it is used only as a set
+        self.expected_shutdowns[process] = 1
+        # Ask the process to die willingly
+        self.cc_session.group_sendmsg({'command': ['shutdown']}, recipient,
+            recipient)
+
+    # Series of stop_process wrappers
+    def stop_resolver(self):
+        self.stop_process('b10-resolver', 'Resolver')
+
+    def stop_auth(self):
+        self.stop_process('b10-auth', 'Auth')
+
+    def stop_xfrout(self):
+        self.stop_process('b10-xfrout', 'Xfrout')
+
+    def stop_xfrin(self):
+        self.stop_process('b10-xfrin', 'Xfrin')
+
+    def stop_zonemgr(self):
+        self.stop_process('b10-zonemgr', 'Zonemgr')
 
     def shutdown(self):
         """Stop the BoB instance."""
@@ -659,6 +733,10 @@ class BoB:
         still_dead = {}
         now = time.time()
         for proc_info in self.dead_processes.values():
+            if proc_info.name in self.expected_shutdowns:
+                # We don't restart, we wanted it to die
+                del self.expected_shutdowns[proc_info.name]
+                continue
             restart_time = proc_info.restart_schedule.get_restart_time(now)
             if restart_time > now:
                 if (next_restart is None) or (next_restart > restart_time):
diff --git a/src/bin/bind10/bind10.xml b/src/bin/bind10/bind10.xml
index dfc8acf..dd8daf9 100644
--- a/src/bin/bind10/bind10.xml
+++ b/src/bin/bind10/bind10.xml
@@ -155,7 +155,11 @@
           <para>The name this process should have in tools like
           <command>ps</command> or <command>top</command>. This
           is handy if you have multiple versions/installations
-          of <command>bind10</command>.</para>
+          of <command>bind10</command>.
+<!-- TODO: only supported with setproctitle feature
+The default is the basename of ARG 0.
+-->
+</para>
         </listitem>
       </varlistentry>
 
diff --git a/src/bin/bind10/tests/bind10_test.py b/src/bin/bind10/tests/bind10_test.py
index 5b38913..ffa06bc 100644
--- a/src/bin/bind10/tests/bind10_test.py
+++ b/src/bin/bind10/tests/bind10_test.py
@@ -142,13 +142,13 @@ class TestBoB(unittest.TestCase):
         self.assertEqual(bob.cfg_start_auth, True)
         self.assertEqual(bob.cfg_start_resolver, False)
 
-# Class for testing the Bob.start_all_processes() method call.
+# Class for testing the BoB start/stop components routines.
 #
 # Although testing that external processes start is outside the scope
 # of the unit test, by overriding the process start methods we can check
 # that the right processes are started depending on the configuration
 # options.
-class StartAllProcessesBob(BoB):
+class StartStopCheckBob(BoB):
     def __init__(self):
         BoB.__init__(self)
 
@@ -163,6 +163,7 @@ class StartAllProcessesBob(BoB):
         self.zonemgr = False
         self.stats = False
         self.cmdctl = False
+        self.c_channel_env = {}
 
     def read_bind10_config(self):
         # Configuration options are set directly
@@ -198,118 +199,256 @@ class StartAllProcessesBob(BoB):
     def start_cmdctl(self, c_channel_env):
         self.cmdctl = True
 
-# Check that the start_all_processes method starts the right combination
-# of processes.
-class TestStartAllProcessesBob(unittest.TestCase):
+    # We don't really use all of these stop_ methods. But it might turn out
+    # someone would add some stop_ method to BoB and we want that one overriden
+    # in case he forgets to update the tests.
+    def stop_msgq(self):
+        self.msgq = False
+
+    def stop_cfgmgr(self):
+        self.cfgmgr = False
+
+    def stop_ccsession(self):
+        self.ccsession = False
+
+    def stop_auth(self):
+        self.auth = False
+
+    def stop_resolver(self):
+        self.resolver = False
+
+    def stop_xfrout(self):
+        self.xfrout = False
+
+    def stop_xfrin(self):
+        self.xfrin = False
+
+    def stop_zonemgr(self):
+        self.zonemgr = False
+
+    def stop_stats(self):
+        self.stats = False
+
+    def stop_cmdctl(self):
+        self.cmdctl = False
+
+class TestStartStopProcessesBob(unittest.TestCase):
+    """
+    Check that the start_all_processes method starts the right combination
+    of processes and that the right processes are started and stopped
+    according to changes in configuration.
+    """
+    def check_started(self, bob, core, auth, resolver):
+        """
+        Check that the right sets of services are started. The ones that
+        should be running are specified by the core, auth and resolver parameters
+        (they are groups of processes, eg. auth means b10-auth, -xfrout, -xfrin
+        and -zonemgr).
+        """
+        self.assertEqual(bob.msgq, core)
+        self.assertEqual(bob.cfgmgr, core)
+        self.assertEqual(bob.ccsession, core)
+        self.assertEqual(bob.auth, auth)
+        self.assertEqual(bob.resolver, resolver)
+        self.assertEqual(bob.xfrout, auth)
+        self.assertEqual(bob.xfrin, auth)
+        self.assertEqual(bob.zonemgr, auth)
+        self.assertEqual(bob.stats, core)
+        self.assertEqual(bob.cmdctl, core)
+
     def check_preconditions(self, bob):
-        self.assertEqual(bob.msgq, False)
-        self.assertEqual(bob.cfgmgr, False)
-        self.assertEqual(bob.ccsession, False)
-        self.assertEqual(bob.auth, False)
-        self.assertEqual(bob.resolver, False)
-        self.assertEqual(bob.xfrout, False)
-        self.assertEqual(bob.xfrin, False)
-        self.assertEqual(bob.zonemgr, False)
-        self.assertEqual(bob.stats, False)
-        self.assertEqual(bob.cmdctl, False)
+        self.check_started(bob, False, False, False)
+
+    def check_started_none(self, bob):
+        """
+        Check that the situation is according to configuration where no servers
+        should be started. Some processes still need to be running.
+        """
+        self.check_started(bob, True, False, False)
+
+    def check_started_both(self, bob):
+        """
+        Check the situation is according to configuration where both servers
+        (auth and resolver) are enabled.
+        """
+        self.check_started(bob, True, True, True)
+
+    def check_started_auth(self, bob):
+        """
+        Check the set of processes needed to run auth only is started.
+        """
+        self.check_started(bob, True, True, False)
+
+    def check_started_resolver(self, bob):
+        """
+        Check the set of processes needed to run resolver only is started.
+        """
+        self.check_started(bob, True, False, True)
 
     # Checks the processes started when starting neither auth nor resolver
     # is specified.
     def test_start_none(self):
-        # Created Bob and ensure initialization correct
-        bob = StartAllProcessesBob()
+        # Create BoB and ensure correct initialization
+        bob = StartStopCheckBob()
         self.check_preconditions(bob)
 
         # Start processes and check what was started
-        c_channel_env = {}
         bob.cfg_start_auth = False
         bob.cfg_start_resolver = False
 
-        bob.start_all_processes(c_channel_env)
-
-        self.assertEqual(bob.msgq, True)
-        self.assertEqual(bob.cfgmgr, True)
-        self.assertEqual(bob.ccsession, True)
-        self.assertEqual(bob.auth, False)
-        self.assertEqual(bob.resolver, False)
-        self.assertEqual(bob.xfrout, False)
-        self.assertEqual(bob.xfrin, False)
-        self.assertEqual(bob.zonemgr, False)
-        self.assertEqual(bob.stats, True)
-        self.assertEqual(bob.cmdctl, True)
+        bob.start_all_processes()
+        self.check_started_none(bob)
 
     # Checks the processes started when starting only the auth process
     def test_start_auth(self):
-        # Created Bob and ensure initialization correct
-        bob = StartAllProcessesBob()
+        # Create BoB and ensure correct initialization
+        bob = StartStopCheckBob()
         self.check_preconditions(bob)
 
         # Start processes and check what was started
-        c_channel_env = {}
         bob.cfg_start_auth = True
         bob.cfg_start_resolver = False
 
-        bob.start_all_processes(c_channel_env)
+        bob.start_all_processes()
 
-        self.assertEqual(bob.msgq, True)
-        self.assertEqual(bob.cfgmgr, True)
-        self.assertEqual(bob.ccsession, True)
-        self.assertEqual(bob.auth, True)
-        self.assertEqual(bob.resolver, False)
-        self.assertEqual(bob.xfrout, True)
-        self.assertEqual(bob.xfrin, True)
-        self.assertEqual(bob.zonemgr, True)
-        self.assertEqual(bob.stats, True)
-        self.assertEqual(bob.cmdctl, True)
+        self.check_started_auth(bob)
 
     # Checks the processes started when starting only the resolver process
     def test_start_resolver(self):
-        # Created Bob and ensure initialization correct
-        bob = StartAllProcessesBob()
+        # Create BoB and ensure correct initialization
+        bob = StartStopCheckBob()
         self.check_preconditions(bob)
 
         # Start processes and check what was started
-        c_channel_env = {}
         bob.cfg_start_auth = False
         bob.cfg_start_resolver = True
 
-        bob.start_all_processes(c_channel_env)
+        bob.start_all_processes()
 
-        self.assertEqual(bob.msgq, True)
-        self.assertEqual(bob.cfgmgr, True)
-        self.assertEqual(bob.ccsession, True)
-        self.assertEqual(bob.auth, False)
-        self.assertEqual(bob.resolver, True)
-        self.assertEqual(bob.xfrout, False)
-        self.assertEqual(bob.xfrin, False)
-        self.assertEqual(bob.zonemgr, False)
-        self.assertEqual(bob.stats, True)
-        self.assertEqual(bob.cmdctl, True)
+        self.check_started_resolver(bob)
 
     # Checks the processes started when starting both auth and resolver process
     def test_start_both(self):
-        # Created Bob and ensure initialization correct
-        bob = StartAllProcessesBob()
+        # Create BoB and ensure correct initialization
+        bob = StartStopCheckBob()
         self.check_preconditions(bob)
 
         # Start processes and check what was started
-        c_channel_env = {}
         bob.cfg_start_auth = True
         bob.cfg_start_resolver = True
 
-        bob.start_all_processes(c_channel_env)
+        bob.start_all_processes()
+
+        self.check_started_both(bob)
+
+    def test_config_start(self):
+        """
+        Test that the configuration starts and stops processes according
+        to configuration changes.
+        """
+
+        # Create BoB and ensure correct initialization
+        bob = StartStopCheckBob()
+        self.check_preconditions(bob)
+
+        # Start processes (nothing much should be started, as in
+        # test_start_none)
+        bob.cfg_start_auth = False
+        bob.cfg_start_resolver = False
+
+        bob.start_all_processes()
+        bob.runnable = True
+        self.check_started_none(bob)
+
+        # Enable both at once
+        bob.config_handler({'start_auth': True, 'start_resolver': True})
+        self.check_started_both(bob)
+
+        # Not touched by empty change
+        bob.config_handler({})
+        self.check_started_both(bob)
+
+        # Not touched by change to the same configuration
+        bob.config_handler({'start_auth': True, 'start_resolver': True})
+        self.check_started_both(bob)
+
+        # Turn them both off again
+        bob.config_handler({'start_auth': False, 'start_resolver': False})
+        self.check_started_none(bob)
+
+        # Not touched by empty change
+        bob.config_handler({})
+        self.check_started_none(bob)
+
+        # Not touched by change to the same configuration
+        bob.config_handler({'start_auth': False, 'start_resolver': False})
+        self.check_started_none(bob)
+
+        # Start and stop auth separately
+        bob.config_handler({'start_auth': True})
+        self.check_started_auth(bob)
+
+        bob.config_handler({'start_auth': False})
+        self.check_started_none(bob)
+
+        # Start and stop resolver separately
+        bob.config_handler({'start_resolver': True})
+        self.check_started_resolver(bob)
+
+        bob.config_handler({'start_resolver': False})
+        self.check_started_none(bob)
+
+        # Alternate
+        bob.config_handler({'start_auth': True})
+        self.check_started_auth(bob)
+
+        bob.config_handler({'start_auth': False, 'start_resolver': True})
+        self.check_started_resolver(bob)
+
+        bob.config_handler({'start_auth': True, 'start_resolver': False})
+        self.check_started_auth(bob)
+
+    def test_config_start_once(self):
+        """
+        Tests that a process is started only once.
+        """
+        # Create BoB and ensure correct initialization
+        bob = StartStopCheckBob()
+        self.check_preconditions(bob)
+
+        # Start processes (both)
+        bob.cfg_start_auth = True
+        bob.cfg_start_resolver = True
+
+        bob.start_all_processes()
+        bob.runnable = True
+        self.check_started_both(bob)
+
+        bob.start_auth = lambda: self.fail("Started auth again")
+        bob.start_xfrout = lambda: self.fail("Started xfrout again")
+        bob.start_xfrin = lambda: self.fail("Started xfrin again")
+        bob.start_zonemgr = lambda: self.fail("Started zonemgr again")
+        bob.start_resolver = lambda: self.fail("Started resolver again")
+
+        # Send again we want to start them. Should not do it, as they are.
+        bob.config_handler({'start_auth': True})
+        bob.config_handler({'start_resolver': True})
+
+    def test_config_not_started_early(self):
+        """
+        Test that processes are not started by the config handler before
+        startup.
+        """
+        bob = StartStopCheckBob()
+        self.check_preconditions(bob)
 
-        self.assertEqual(bob.msgq, True)
-        self.assertEqual(bob.cfgmgr, True)
-        self.assertEqual(bob.ccsession, True)
-        self.assertEqual(bob.auth, True)
-        self.assertEqual(bob.resolver, True)
-        self.assertEqual(bob.xfrout, True)
-        self.assertEqual(bob.xfrin, True)
-        self.assertEqual(bob.zonemgr, True)
-        self.assertEqual(bob.stats, True)
-        self.assertEqual(bob.cmdctl, True)
+        bob.start_auth = lambda: self.fail("Started auth again")
+        bob.start_xfrout = lambda: self.fail("Started xfrout again")
+        bob.start_xfrin = lambda: self.fail("Started xfrin again")
+        bob.start_zonemgr = lambda: self.fail("Started zonemgr again")
+        bob.start_resolver = lambda: self.fail("Started resolver again")
 
+        bob.config_handler({'start_auth': True, 'start_resolver': True})
 
 if __name__ == '__main__':
     unittest.main()
diff --git a/src/bin/bindctl/Makefile.am b/src/bin/bindctl/Makefile.am
index da9b63d..e95af78 100644
--- a/src/bin/bindctl/Makefile.am
+++ b/src/bin/bindctl/Makefile.am
@@ -8,7 +8,7 @@ EXTRA_DIST = $(man_MANS) bindctl.xml
 python_PYTHON = __init__.py bindcmd.py cmdparse.py exception.py moduleinfo.py mycollections.py
 pythondir = $(pyexecdir)/bindctl
 
-bindctldir = $(DESTDIR)$(pkgdatadir)
+bindctldir = $(pkgdatadir)
 
 CLEANFILES = bindctl
 
diff --git a/src/bin/bindctl/bindcmd.py b/src/bin/bindctl/bindcmd.py
index fb6a892..683dda9 100644
--- a/src/bin/bindctl/bindcmd.py
+++ b/src/bin/bindctl/bindcmd.py
@@ -51,7 +51,6 @@ except ImportError:
     my_readline = sys.stdin.readline
 
 CSV_FILE_NAME = 'default_user.csv'
-FAIL_TO_CONNECT_WITH_CMDCTL = "Fail to connect with b10-cmdctl module, is it running?"
 CONFIG_MODULE_NAME = 'config'
 CONST_BINDCTL_HELP = """
 usage: <module name> <command name> [param1 = value1 [, param2 = value2]]
@@ -92,10 +91,13 @@ class BindCmdInterpreter(Cmd):
         Cmd.__init__(self)
         self.location = ""
         self.prompt_end = '> '
-        self.prompt = self.prompt_end
+        if sys.stdin.isatty():
+            self.prompt = self.prompt_end
+        else:
+            self.prompt = ""
         self.ruler = '-'
         self.modules = OrderedDict()
-        self.add_module_info(ModuleInfo("help", desc = "Get help for bindctl"))
+        self.add_module_info(ModuleInfo("help", desc = "Get help for bindctl."))
         self.server_port = server_port
         self.conn = ValidatedHTTPSConnection(self.server_port,
                                              ca_certs=pem_file)
@@ -119,8 +121,8 @@ class BindCmdInterpreter(Cmd):
 
             self.cmdloop()
         except FailToLogin as err:
-            print(err)
-            print(FAIL_TO_CONNECT_WITH_CMDCTL)
+            # error already printed when this was raised, ignoring
+            pass
         except KeyboardInterrupt:
             print('\nExit from bindctl')
 
@@ -270,8 +272,10 @@ class BindCmdInterpreter(Cmd):
         return line 
 
     def postcmd(self, stop, line):
-        '''Update the prompt after every command'''
-        self.prompt = self.location + self.prompt_end
+        '''Update the prompt after every command, but only if we
+           have a tty as output'''
+        if sys.stdin.isatty():
+            self.prompt = self.location + self.prompt_end
         return stop
 
     def _prepare_module_commands(self, module_spec):
@@ -375,7 +379,14 @@ class BindCmdInterpreter(Cmd):
         if cmd.command == "help" or ("help" in cmd.params.keys()):
             self._handle_help(cmd)
         elif cmd.module == CONFIG_MODULE_NAME:
-            self.apply_config_cmd(cmd)
+            try:
+                self.apply_config_cmd(cmd)
+            except isc.cc.data.DataTypeError as dte:
+                print("Error: " + str(dte))
+            except isc.cc.data.DataNotFoundError as dnfe:
+                print("Error: " + str(dnfe))
+            except KeyError as ke:
+                print("Error: missing " + str(ke))
         else:
             self.apply_cmd(cmd)
 
@@ -396,9 +407,24 @@ class BindCmdInterpreter(Cmd):
 
     def do_help(self, name):
         print(CONST_BINDCTL_HELP)
-        for k in self.modules.keys():
-            print("\t", self.modules[k])
-                
+        for k in self.modules.values():
+            n = k.get_name()
+            if len(n) >= CONST_BINDCTL_HELP_INDENT_WIDTH:
+                print("    %s" % n)
+                print(textwrap.fill(k.get_desc(),
+                      initial_indent="            ",
+                      subsequent_indent="    " +
+                      " " * CONST_BINDCTL_HELP_INDENT_WIDTH,
+                      width=70))
+            else:
+                print(textwrap.fill("%s%s%s" %
+                    (k.get_name(),
+                     " "*(CONST_BINDCTL_HELP_INDENT_WIDTH - len(k.get_name())),
+                     k.get_desc()),
+                    initial_indent="    ",
+                    subsequent_indent="    " +
+                    " " * CONST_BINDCTL_HELP_INDENT_WIDTH,
+                    width=70))
     
     def onecmd(self, line):
         if line == 'EOF' or line.lower() == "quit":
@@ -411,7 +437,19 @@ class BindCmdInterpreter(Cmd):
         Cmd.onecmd(self, line)
 
     def remove_prefix(self, list, prefix):
-        return [(val[len(prefix):]) for val in list]
+        """Removes the prefix already entered, and all elements from the
+           list that don't match it"""
+        if prefix.startswith('/'):
+            prefix = prefix[1:]
+
+        new_list = []
+        for val in list:
+            if val.startswith(prefix):
+                new_val = val[len(prefix):]
+                if new_val.startswith("/"):
+                    new_val = new_val[1:]
+                new_list.append(new_val)
+        return new_list
 
     def complete(self, text, state):
         if 0 == state:
@@ -502,8 +540,7 @@ class BindCmdInterpreter(Cmd):
             self._validate_cmd(cmd)
             self._handle_cmd(cmd)
         except (IOError, http.client.HTTPException) as err:
-            print('Error!', err)
-            print(FAIL_TO_CONNECT_WITH_CMDCTL)
+            print('Error: ', err)
         except BindCtlException as err:
             print("Error! ", err)
             self._print_correct_usage(err)
@@ -541,87 +578,115 @@ class BindCmdInterpreter(Cmd):
            Raises a KeyError if the command was not complete
         '''
         identifier = self.location
-        try:
-            if 'identifier' in cmd.params:
-                if not identifier.endswith("/"):
-                    identifier += "/"
-                if cmd.params['identifier'].startswith("/"):
-                    identifier = cmd.params['identifier']
-                else:
-                    identifier += cmd.params['identifier']
-
-                # Check if the module is known; for unknown modules
-                # we currently deny setting preferences, as we have
-                # no way yet to determine if they are ok.
-                module_name = identifier.split('/')[1]
-                if self.config_data is None or \
-                   not self.config_data.have_specification(module_name):
-                    print("Error: Module '" + module_name + "' unknown or not running")
-                    return
+        if 'identifier' in cmd.params:
+            if not identifier.endswith("/"):
+                identifier += "/"
+            if cmd.params['identifier'].startswith("/"):
+                identifier = cmd.params['identifier']
+            else:
+                if cmd.params['identifier'].startswith('['):
+                    identifier = identifier[:-1]
+                identifier += cmd.params['identifier']
+
+            # Check if the module is known; for unknown modules
+            # we currently deny setting preferences, as we have
+            # no way yet to determine if they are ok.
+            module_name = identifier.split('/')[1]
+            if module_name != "" and (self.config_data is None or \
+               not self.config_data.have_specification(module_name)):
+                print("Error: Module '" + module_name + "' unknown or not running")
+                return
 
-            if cmd.command == "show":
-                values = self.config_data.get_value_maps(identifier)
-                for value_map in values:
-                    line = value_map['name']
-                    if value_map['type'] in [ 'module', 'map', 'list' ]:
-                        line += "/"
-                    else:
-                        line += ":\t" + json.dumps(value_map['value'])
-                    line += "\t" + value_map['type']
-                    line += "\t"
-                    if value_map['default']:
-                        line += "(default)"
-                    if value_map['modified']:
-                        line += "(modified)"
-                    print(line)
-            elif cmd.command == "add":
-                self.config_data.add_value(identifier, cmd.params['value'])
-            elif cmd.command == "remove":
-                if 'value' in cmd.params:
-                    self.config_data.remove_value(identifier, cmd.params['value'])
+        if cmd.command == "show":
+            # check if we have the 'all' argument
+            show_all = False
+            if 'argument' in cmd.params:
+                if cmd.params['argument'] == 'all':
+                    show_all = True
+                elif 'identifier' not in cmd.params:
+                    # no 'all', no identifier, assume this is the
+                    #identifier
+                    identifier += cmd.params['argument']
                 else:
-                    self.config_data.remove_value(identifier, None)
-            elif cmd.command == "set":
-                if 'identifier' not in cmd.params:
-                    print("Error: missing identifier or value")
+                    print("Error: unknown argument " + cmd.params['argument'] + ", or multiple identifiers given")
+                    return
+            values = self.config_data.get_value_maps(identifier, show_all)
+            for value_map in values:
+                line = value_map['name']
+                if value_map['type'] in [ 'module', 'map' ]:
+                    line += "/"
+                elif value_map['type'] == 'list' \
+                     and value_map['value'] != []:
+                    # do not print content of non-empty lists if
+                    # we have more data to show
+                    line += "/"
                 else:
-                    parsed_value = None
-                    try:
-                        parsed_value = json.loads(cmd.params['value'])
-                    except Exception as exc:
-                        # ok could be an unquoted string, interpret as such
-                        parsed_value = cmd.params['value']
-                    self.config_data.set_value(identifier, parsed_value)
-            elif cmd.command == "unset":
-                self.config_data.unset(identifier)
-            elif cmd.command == "revert":
-                self.config_data.clear_local_changes()
-            elif cmd.command == "commit":
-                self.config_data.commit()
-            elif cmd.command == "diff":
-                print(self.config_data.get_local_changes());
-            elif cmd.command == "go":
-                self.go(identifier)
-        except isc.cc.data.DataTypeError as dte:
-            print("Error: " + str(dte))
-        except isc.cc.data.DataNotFoundError as dnfe:
-            print("Error: " + identifier + " not found")
-        except KeyError as ke:
-            print("Error: missing " + str(ke))
-            raise ke
+                    line += "\t" + json.dumps(value_map['value'])
+                line += "\t" + value_map['type']
+                line += "\t"
+                if value_map['default']:
+                    line += "(default)"
+                if value_map['modified']:
+                    line += "(modified)"
+                print(line)
+        elif cmd.command == "show_json":
+            if identifier == "":
+                print("Need at least the module to show the configuration in JSON format")
+            else:
+                data, default = self.config_data.get_value(identifier)
+                print(json.dumps(data))
+        elif cmd.command == "add":
+            if 'value' in cmd.params:
+                self.config_data.add_value(identifier, cmd.params['value'])
+            else:
+                self.config_data.add_value(identifier)
+        elif cmd.command == "remove":
+            if 'value' in cmd.params:
+                self.config_data.remove_value(identifier, cmd.params['value'])
+            else:
+                self.config_data.remove_value(identifier, None)
+        elif cmd.command == "set":
+            if 'identifier' not in cmd.params:
+                print("Error: missing identifier or value")
+            else:
+                parsed_value = None
+                try:
+                    parsed_value = json.loads(cmd.params['value'])
+                except Exception as exc:
+                    # ok could be an unquoted string, interpret as such
+                    parsed_value = cmd.params['value']
+                self.config_data.set_value(identifier, parsed_value)
+        elif cmd.command == "unset":
+            self.config_data.unset(identifier)
+        elif cmd.command == "revert":
+            self.config_data.clear_local_changes()
+        elif cmd.command == "commit":
+            self.config_data.commit()
+        elif cmd.command == "diff":
+            print(self.config_data.get_local_changes());
+        elif cmd.command == "go":
+            self.go(identifier)
 
     def go(self, identifier):
         '''Handles the config go command, change the 'current' location
-           within the configuration tree'''
-        # this is just to see if it exists
-        self.config_data.get_value(identifier)
-        # some sanitizing
-        identifier = identifier.replace("//", "/")
-        if not identifier.startswith("/"):
-            identifier = "/" + identifier
-        if identifier.endswith("/"):
-            identifier = identifier[:-1]
-        self.location = identifier
+           within the configuration tree. '..' will be interpreted as
+           'up one level'.'''
+        id_parts = isc.cc.data.split_identifier(identifier)
+
+        new_location = ""
+        for id_part in id_parts:
+            if (id_part == ".."):
+                # go 'up' one level
+                new_location, a, b = new_location.rpartition("/")
+            else:
+                new_location += "/" + id_part
+        # check if exists, if not, revert and error
+        v,d = self.config_data.get_value(new_location)
+        if v is None:
+            print("Error: " + identifier + " not found")
+            return
+
+        self.location = new_location
 
     def apply_cmd(self, cmd):
         '''Handles a general module command'''
diff --git a/src/bin/bindctl/bindctl-source.py.in b/src/bin/bindctl/bindctl-source.py.in
index 83059d2..080c3bc 100644
--- a/src/bin/bindctl/bindctl-source.py.in
+++ b/src/bin/bindctl/bindctl-source.py.in
@@ -31,53 +31,62 @@ isc.util.process.rename()
 # This is the version that gets displayed to the user.
 # The VERSION string consists of the module name, the module version
 # number, and the overall BIND 10 version number (set in configure.ac).
-VERSION = "bindctl 20101201 (BIND 10 @PACKAGE_VERSION@)"
+VERSION = "bindctl 20110217 (BIND 10 @PACKAGE_VERSION@)"
+
+DEFAULT_IDENTIFIER_DESC = "The identifier specifies the config item. Child elements are separated with the '/' character. List indices can be specified with '[i]', where i is an integer specifying the index, starting with 0. Examples: 'Boss/start_auth', 'Recurse/listen_on[0]/address'. If no identifier is given, shows the item at the current location."
 
 def prepare_config_commands(tool):
     '''Prepare fixed commands for local configuration editing'''
-    module = ModuleInfo(name = CONFIG_MODULE_NAME, desc = "Configuration commands")
-    cmd = CommandInfo(name = "show", desc = "Show configuration")
-    param = ParamInfo(name = "identifier", type = "string", optional=True)
+    module = ModuleInfo(name = CONFIG_MODULE_NAME, desc = "Configuration commands.")
+    cmd = CommandInfo(name = "show", desc = "Show configuration.")
+    param = ParamInfo(name = "argument", type = "string", optional=True, desc = "If you specify the argument 'all' (before the identifier), recursively show all child elements for the given identifier.")
+    cmd.add_param(param)
+    param = ParamInfo(name = "identifier", type = "string", optional=True, desc = DEFAULT_IDENTIFIER_DESC)
+    cmd.add_param(param)
+    module.add_command(cmd)
+
+    cmd = CommandInfo(name = "show_json", desc = "Show full configuration in JSON format.")
+    param = ParamInfo(name = "identifier", type = "string", optional=True, desc = DEFAULT_IDENTIFIER_DESC)
     cmd.add_param(param)
     module.add_command(cmd)
 
-    cmd = CommandInfo(name = "add", desc = "Add entry to configuration list")
-    param = ParamInfo(name = "identifier", type = "string", optional=True)
+    cmd = CommandInfo(name = "add", desc = "Add an entry to configuration list. If no value is given, a default value is added.")
+    param = ParamInfo(name = "identifier", type = "string", optional=True, desc = DEFAULT_IDENTIFIER_DESC)
     cmd.add_param(param)
-    param = ParamInfo(name = "value", type = "string", optional=False)
+    param = ParamInfo(name = "value", type = "string", optional=True, desc = "Specifies a value to add to the list. It must be in correct JSON format and complete.")
     cmd.add_param(param)
     module.add_command(cmd)
 
-    cmd = CommandInfo(name = "remove", desc = "Remove entry from configuration list")
-    param = ParamInfo(name = "identifier", type = "string", optional=True)
+    cmd = CommandInfo(name = "remove", desc = "Remove entry from configuration list.")
+    param = ParamInfo(name = "identifier", type = "string", optional=True, desc = DEFAULT_IDENTIFIER_DESC)
     cmd.add_param(param)
-    param = ParamInfo(name = "value", type = "string", optional=True)
+    param = ParamInfo(name = "value", type = "string", optional=True, desc = "Specifies a value to remove from the list. It must be in correct JSON format and complete.")
     cmd.add_param(param)
     module.add_command(cmd)
 
-    cmd = CommandInfo(name = "set", desc = "Set a configuration value")
-    param = ParamInfo(name = "identifier", type = "string", optional=True)
+    cmd = CommandInfo(name = "set", desc = "Set a configuration value.")
+    param = ParamInfo(name = "identifier", type = "string", optional=True, desc = DEFAULT_IDENTIFIER_DESC)
     cmd.add_param(param)
-    param = ParamInfo(name = "value", type = "string", optional=False)
+    param = ParamInfo(name = "value", type = "string", optional=False, desc = "Specifies a value to set. It must be in correct JSON format and complete.")
     cmd.add_param(param)
     module.add_command(cmd)
 
-    cmd = CommandInfo(name = "unset", desc = "Unset a configuration value")
-    param = ParamInfo(name = "identifier", type = "string", optional=False)
+    cmd = CommandInfo(name = "unset", desc = "Unset a configuration value (i.e. revert to the default, if any).")
+    param = ParamInfo(name = "identifier", type = "string", optional=False, desc = DEFAULT_IDENTIFIER_DESC)
     cmd.add_param(param)
     module.add_command(cmd)
 
-    cmd = CommandInfo(name = "diff", desc = "Show all local changes")
+    cmd = CommandInfo(name = "diff", desc = "Show all local changes that have not been committed.")
     module.add_command(cmd)
 
-    cmd = CommandInfo(name = "revert", desc = "Revert all local changes")
+    cmd = CommandInfo(name = "revert", desc = "Revert all local changes.")
     module.add_command(cmd)
 
-    cmd = CommandInfo(name = "commit", desc = "Commit all local changes")
+    cmd = CommandInfo(name = "commit", desc = "Commit all local changes.")
     module.add_command(cmd)
 
-    cmd = CommandInfo(name = "go", desc = "Go to a specific configuration part")
-    param = ParamInfo(name = "identifier", type="string", optional=False)
+    cmd = CommandInfo(name = "go", desc = "Go to a specific configuration part.")
+    param = ParamInfo(name = "identifier", type="string", optional=False, desc = DEFAULT_IDENTIFIER_DESC)
     cmd.add_param(param)
     module.add_command(cmd)
 
@@ -115,15 +124,12 @@ def set_bindctl_options(parser):
                       help = 'PEM formatted server certificate validation chain file')
 
 if __name__ == '__main__':
-    try:
-        parser = OptionParser(version = VERSION)
-        set_bindctl_options(parser)
-        (options, args) = parser.parse_args()
-        server_addr = options.addr + ':' + str(options.port)
-        tool = BindCmdInterpreter(server_addr, pem_file=options.cert_chain)
-        prepare_config_commands(tool)
-        tool.run()
-    except Exception as e:
-        print(e, "\nFailed to connect with b10-cmdctl module, is it running?")
+    parser = OptionParser(version = VERSION)
+    set_bindctl_options(parser)
+    (options, args) = parser.parse_args()
+    server_addr = options.addr + ':' + str(options.port)
+    tool = BindCmdInterpreter(server_addr, pem_file=options.cert_chain)
+    prepare_config_commands(tool)
+    tool.run()
 
 
diff --git a/src/bin/bindctl/cmdparse.py b/src/bin/bindctl/cmdparse.py
index ab891d7..c624cba 100644
--- a/src/bin/bindctl/cmdparse.py
+++ b/src/bin/bindctl/cmdparse.py
@@ -33,6 +33,7 @@ param_value_str  = "(?P<param_value>[^\'\" ][^, ]+)"
 param_value_with_quota_str  = "[\"\'](?P<param_value>.+?)(?<!\\\)[\"\']"
 next_params_str = "(?P<blank>\s*)(?P<comma>,?)(?P<next_params>.*)$"
 
+
 PARAM_WITH_QUOTA_PATTERN = re.compile(param_name_str + 
                                       param_value_with_quota_str + 
                                       next_params_str)
@@ -40,8 +41,58 @@ PARAM_PATTERN = re.compile(param_name_str + param_value_str + next_params_str)
 # Used for module and command name
 NAME_PATTERN = re.compile("^\s*(?P<name>[\w]+)(?P<blank>\s*)(?P<others>.*)$")
 
+# this removes all whitespace in the given string, except when
+# between " quotes
+_remove_unquoted_whitespace = \
+    lambda text:'"'.join( it if i%2 else ''.join(it.split())
+        for i,it in enumerate(text.split('"'))  )
+
+
+def _remove_list_and_map_whitespace(text):
+    """Returns a string where the whitespace between matching [ and ]
+       is removed, unless quoted"""
+    # regular expression aren't really the right tool, since we may have
+    # nested structures
+    result = []
+    start_pos = 0
+    pos = 0
+    list_count = 0
+    map_count = 0
+    cur_start_list_pos = None
+    cur_start_map_pos = None
+    for i in text:
+        if i == '[' and map_count == 0:
+            if list_count == 0:
+                result.append(text[start_pos:pos + 1])
+                cur_start_list_pos = pos + 1
+            list_count = list_count + 1
+        elif i == ']' and map_count == 0:
+            if list_count > 0:
+                list_count = list_count - 1
+                if list_count == 0:
+                    result.append(_remove_unquoted_whitespace(text[cur_start_list_pos:pos + 1]))
+                    start_pos = pos + 1
+        if i == '{' and list_count == 0:
+            if map_count == 0:
+                result.append(text[start_pos:pos + 1])
+                cur_start_map_pos = pos + 1
+            map_count = map_count + 1
+        elif i == '}' and list_count == 0:
+            if map_count > 0:
+                map_count = map_count - 1
+                if map_count == 0:
+                    result.append(_remove_unquoted_whitespace(text[cur_start_map_pos:pos + 1]))
+                    start_pos = pos + 1
+        
+
+        pos = pos + 1
+    if start_pos <= len(text):
+        result.append(text[start_pos:len(text)])
+    return "".join(result)
+    
+    
 class BindCmdParse:
-    """ This class will parse the command line usr input into three part
+    """ This class will parse the command line user input into three parts:
     module name, command, parameters
     the first two parts are strings and parameter is one hash, 
     parameters part is optional
@@ -86,9 +137,12 @@ class BindCmdParse:
 
             self._parse_params(param_str)
 
+    def _remove_list_whitespace(self, text):
+        return ""
 
     def _parse_params(self, param_text):
         """convert a=b,c=d into one hash """
+        param_text = _remove_list_and_map_whitespace(param_text)
         
         # Check parameter name "help"
         param = NAME_PATTERN.match(param_text)
diff --git a/src/bin/bindctl/moduleinfo.py b/src/bin/bindctl/moduleinfo.py
index 015ef16..6e41dce 100644
--- a/src/bin/bindctl/moduleinfo.py
+++ b/src/bin/bindctl/moduleinfo.py
@@ -16,6 +16,8 @@
 """This module holds classes representing modules, commands and
    parameters for use in bindctl"""
 
+import textwrap
+
 try:
     from collections import OrderedDict
 except ImportError:
@@ -30,6 +32,9 @@ MODULE_NODE_NAME = 'module'
 COMMAND_NODE_NAME = 'command'
 PARAM_NODE_NAME = 'param'
 
+# this is used to align the descriptions in help output
+CONST_BINDCTL_HELP_INDENT_WIDTH=12
+
 
 class ParamInfo:
     """One parameter of one command.
@@ -52,6 +57,12 @@ class ParamInfo:
     def __str__(self):        
         return str("\t%s <type: %s> \t(%s)" % (self.name, self.type, self.desc))
 
+    def get_name(self):
+        return "%s <type: %s>" % (self.name, self.type)
+
+    def get_desc(self):
+        return self.desc
+
 class CommandInfo:
     """One command which is provided by one bind10 module, it has zero
        or more parameters
@@ -63,13 +74,18 @@ class CommandInfo:
         self.params = OrderedDict()        
         # Set default parameter "help"
         self.add_param(ParamInfo("help", 
-                                  desc = "Get help for command",
+                                  desc = "Get help for command.",
                                   optional = True))
                 
     def __str__(self):
         return str("%s \t(%s)" % (self.name, self.desc))
-        
 
+    def get_name(self):
+        return self.name
+
+    def get_desc(self):
+        return self.desc;
+    
     def add_param(self, paraminfo):
         """Add a ParamInfo object to this CommandInfo"""
         self.params[paraminfo.name] = paraminfo
@@ -144,22 +160,30 @@ class CommandInfo:
         del params["help"]
 
         if len(params) == 0:
-            print("\tNo parameters for the command")
+            print("No parameters for the command")
             return
         
-        print("\n\tMandatory parameters:")
+        print("\nMandatory parameters:")
         mandatory_infos = []
         for info in params.values():            
             if not info.is_optional:
-                print("\t", info)
+                print("    %s" % info.get_name())
+                print(textwrap.fill(info.get_desc(),
+                      initial_indent="        ",
+                      subsequent_indent="        ",
+                      width=70))
                 mandatory_infos.append(info)
 
         optional_infos = [info for info in params.values() 
                           if info not in mandatory_infos]
         if len(optional_infos) > 0:
-            print("\n\tOptional parameters:")      
+            print("\nOptional parameters:")      
             for info in optional_infos:
-                    print("\t", info)
+                print("    %s" % info.get_name())
+                print(textwrap.fill(info.get_desc(),
+                      initial_indent="        ",
+                      subsequent_indent="        ",
+                      width=70))
 
 
 class ModuleInfo:
@@ -172,11 +196,17 @@ class ModuleInfo:
         self.desc = desc
         self.commands = OrderedDict()         
         self.add_command(CommandInfo(name = "help", 
-                                     desc = "Get help for module"))
+                                     desc = "Get help for module."))
         
     def __str__(self):
         return str("%s \t%s" % (self.name, self.desc))
-        
+
+    def get_name(self):
+        return self.name
+
+    def get_desc(self):
+        return self.desc
+
     def add_command(self, command_info):
         """Add a CommandInfo to this ModuleInfo."""
         self.commands[command_info.name] = command_info
@@ -201,8 +231,24 @@ class ModuleInfo:
     def module_help(self):
         """Prints the help info for this module to stdout"""
         print("Module ", self, "\nAvailable commands:")
-        for k in self.commands.keys():
-            print("\t", self.commands[k])
+        for k in self.commands.values():
+            n = k.get_name()
+            if len(n) >= CONST_BINDCTL_HELP_INDENT_WIDTH:
+                print("    %s" % n)
+                print(textwrap.fill(k.get_desc(),
+                      initial_indent="            ",
+                      subsequent_indent="    " +
+                      " " * CONST_BINDCTL_HELP_INDENT_WIDTH,
+                      width=70))
+            else:
+                print(textwrap.fill("%s%s%s" %
+                    (k.get_name(),
+                     " "*(CONST_BINDCTL_HELP_INDENT_WIDTH - len(k.get_name())),
+                     k.get_desc()),
+                    initial_indent="    ",
+                    subsequent_indent="    " +
+                    " " * CONST_BINDCTL_HELP_INDENT_WIDTH,
+                    width=70))
             
     def command_help(self, command):
         """Prints the help info for the command with the given name.
diff --git a/src/bin/bindctl/tests/Makefile.am b/src/bin/bindctl/tests/Makefile.am
index 5f93644..8a7a623 100644
--- a/src/bin/bindctl/tests/Makefile.am
+++ b/src/bin/bindctl/tests/Makefile.am
@@ -1,5 +1,5 @@
 PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
-PYTESTS = bindctl_test.py
+PYTESTS = bindctl_test.py cmdparse_test.py
 EXTRA_DIST = $(PYTESTS)
 
 # test using command-line arguments, so use check-local target instead of TESTS
diff --git a/src/bin/bindctl/tests/bindctl_test.py b/src/bin/bindctl/tests/bindctl_test.py
index 653c908..490dd7a 100644
--- a/src/bin/bindctl/tests/bindctl_test.py
+++ b/src/bin/bindctl/tests/bindctl_test.py
@@ -17,6 +17,8 @@
 import unittest
 import isc.cc.data
 import os
+from isc.config.config_data import ConfigData, MultiConfigData
+from isc.config.module_spec import ModuleSpec
 from bindctl import cmdparse
 from bindctl import bindcmd
 from bindctl.moduleinfo import *
@@ -238,11 +240,101 @@ class TestNameSequence(unittest.TestCase):
             assert self.random_names[i] == module_names[i+1]
             i = i + 1
 
-    def test_apply_cfg_command(self):
+# tine class to fake a UIModuleCCSession, but only the config data
+# parts for the next set of tests
+class FakeCCSession(MultiConfigData):
+    def __init__(self):
+        self._local_changes = {}
+        self._current_config = {}
+        self._specifications = {}
+        self.add_foo_spec()
+
+    def add_foo_spec(self):
+        spec = { "module_name": "foo",
+                 "config_data": [
+                 { "item_name": "an_int",
+                   "item_type": "integer",
+                   "item_optional": False,
+                   "item_default": 1
+                 },
+                 { "item_name": "a_list",
+                   "item_type": "list",
+                   "item_optional": False,
+                   "item_default": [],
+                   "list_item_spec":
+                   { "item_name": "a_string",
+                     "item_type": "string",
+                     "item_optional": False,
+                     "item_default": "bar"
+                   }
+                 }
+                 ]
+               }
+        self.set_specification(ModuleSpec(spec))
+    
+
+class TestConfigCommands(unittest.TestCase):
+    def setUp(self):
+        self.tool = bindcmd.BindCmdInterpreter()
+        mod_info = ModuleInfo(name = "foo")
+        self.tool.add_module_info(mod_info)
+        self.tool.config_data = FakeCCSession()
+        
+    def test_apply_cfg_command_int(self):
         self.tool.location = '/'
-        cmd = cmdparse.BindCmdParse("config set identifier=\"foo/bar\" value=\"5\"")
+
+        self.assertEqual((1, MultiConfigData.DEFAULT),
+                         self.tool.config_data.get_value("/foo/an_int"))
+
+        cmd = cmdparse.BindCmdParse("config set identifier=\"foo/an_int\" value=\"5\"")
         self.tool.apply_config_cmd(cmd)
+        self.assertEqual((5, MultiConfigData.LOCAL),
+                         self.tool.config_data.get_value("/foo/an_int"))
+
+        # this should raise a NotFoundError
+        cmd = cmdparse.BindCmdParse("config set identifier=\"foo/bar\" value=\"[]\"")
+        self.assertRaises(isc.cc.data.DataNotFoundError, self.tool.apply_config_cmd, cmd)
+
+        # this should raise a TypeError
+        cmd = cmdparse.BindCmdParse("config set identifier=\"foo/an_int\" value=\"[]\"")
+        self.assertRaises(isc.cc.data.DataTypeError, self.tool.apply_config_cmd, cmd)
+
+    # this is a very specific one for use with a set of list tests
+    # to try out the flexibility of the parser (only in the next test)
+    def clt(self, full_cmd_string, item_value):
+        cmd = cmdparse.BindCmdParse(full_cmd_string)
+        self.tool.apply_config_cmd(cmd)
+        self.assertEqual(([item_value], MultiConfigData.LOCAL),
+                         self.tool.config_data.get_value("/foo/a_list"))
+
+    def test_apply_cfg_command_list(self):
+        self.tool.location = '/'
+
+        self.assertEqual(([], MultiConfigData.DEFAULT),
+                         self.tool.config_data.get_value("/foo/a_list"))
+
+        self.clt("config set identifier=\"foo/a_list\" value=[\"a\"]", "a")
+        self.clt("config set identifier=\"foo/a_list\" value =[\"b\"]", "b")
+        self.clt("config set identifier=\"foo/a_list\" value= [\"c\"]", "c")
+        self.clt("config set identifier=\"foo/a_list\" value = [\"d\"]", "d")
+        self.clt("config set identifier =\"foo/a_list\" value=[\"e\"]", "e")
+        self.clt("config set identifier= \"foo/a_list\" value=[\"f\"]", "f")
+        self.clt("config set identifier = \"foo/a_list\" value=[\"g\"]", "g")
+        self.clt("config set identifier = \"foo/a_list\" value = [\"h\"]", "h")
+        self.clt("config set identifier = \"foo/a_list\" value=[\"i\" ]", "i")
+        self.clt("config set identifier = \"foo/a_list\" value=[ \"j\"]", "j")
+        self.clt("config set identifier = \"foo/a_list\" value=[ \"k\" ]", "k")
+
+        # this should raise a TypeError
+        cmd = cmdparse.BindCmdParse("config set identifier=\"foo/a_list\" value=\"a\"")
+        self.assertRaises(isc.cc.data.DataTypeError, self.tool.apply_config_cmd, cmd)
+        
+        cmd = cmdparse.BindCmdParse("config set identifier=\"foo/a_list\" value=[1]")
+        self.assertRaises(isc.cc.data.DataTypeError, self.tool.apply_config_cmd, cmd)
+
+
     
+
 class FakeBindCmdInterpreter(bindcmd.BindCmdInterpreter):
     def __init__(self):
         pass
diff --git a/src/bin/bindctl/tests/cmdparse_test.py b/src/bin/bindctl/tests/cmdparse_test.py
new file mode 100644
index 0000000..9150ed3
--- /dev/null
+++ b/src/bin/bindctl/tests/cmdparse_test.py
@@ -0,0 +1,88 @@
+# Copyright (C) 2009  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+
+import unittest
+from bindctl import cmdparse
+
+class TestCmdParse(unittest.TestCase):
+
+    def test_remove_unquoted_whitespace(self):
+        self.assertEqual(cmdparse._remove_unquoted_whitespace("a"), "a")
+        self.assertEqual(cmdparse._remove_unquoted_whitespace(" a"), "a")
+        self.assertEqual(cmdparse._remove_unquoted_whitespace("a "), "a")
+        self.assertEqual(cmdparse._remove_unquoted_whitespace(" a "), "a")
+        self.assertNotEqual(cmdparse._remove_unquoted_whitespace("a"), "a ")
+        self.assertNotEqual(cmdparse._remove_unquoted_whitespace(" a"), " a")
+        self.assertNotEqual(cmdparse._remove_unquoted_whitespace("a "), "a ")
+        self.assertNotEqual(cmdparse._remove_unquoted_whitespace(" a "), " a ")
+        self.assertNotEqual(cmdparse._remove_unquoted_whitespace(" a "), "b")
+
+        self.assertEqual(cmdparse._remove_unquoted_whitespace("\"abc\""), "\"abc\"")
+        self.assertEqual(cmdparse._remove_unquoted_whitespace(" \"abc\""), "\"abc\"")
+        self.assertEqual(cmdparse._remove_unquoted_whitespace("\"abc\" "), "\"abc\"")
+        self.assertEqual(cmdparse._remove_unquoted_whitespace(" \"abc\" "), "\"abc\"")
+        
+        self.assertEqual(cmdparse._remove_unquoted_whitespace("\" abc\""), "\" abc\"")
+        self.assertEqual(cmdparse._remove_unquoted_whitespace(" \"a bc\""), "\"a bc\"")
+        self.assertEqual(cmdparse._remove_unquoted_whitespace("\"ab c\" "), "\"ab c\"")
+        self.assertEqual(cmdparse._remove_unquoted_whitespace(" \"abc \" "), "\"abc \"")
+        self.assertEqual(cmdparse._remove_unquoted_whitespace(" \" a b c \" "), "\" a b c \"")
+        
+        self.assertEqual(cmdparse._remove_unquoted_whitespace("a\" abc\"a"), "a\" abc\"a")
+        self.assertEqual(cmdparse._remove_unquoted_whitespace("a \"a bc\"a"), "a\"a bc\"a")
+        self.assertEqual(cmdparse._remove_unquoted_whitespace("a\"ab c\" a"), "a\"ab c\"a")
+        self.assertEqual(cmdparse._remove_unquoted_whitespace("a \"abc \" a"), "a\"abc \"a")
+        self.assertEqual(cmdparse._remove_unquoted_whitespace("a \" a b c \" a"), "a\" a b c \"a")
+
+    # short-hand function to make the set of tests more readable
+    def rws(self, a, b):
+        self.assertEqual(cmdparse._remove_list_and_map_whitespace(a), b)
+
+    def test_remove_list_whitespace(self):
+        self.rws("a", "a")
+        self.rws(" a ", " a ")
+        self.rws(" [a] ", " [a] ")
+        self.rws(" [ a] ", " [a] ")
+        self.rws(" [ a ] ", " [a] ")
+        self.rws(" [ a b c ] ", " [abc] ")
+        self.rws(" [ a \"b c\" ] ", " [a\"b c\"] ")
+        self.rws("a [ a \"b c\" ] a", "a [a\"b c\"] a")
+        self.rws("a] [ a \"b c\" ] a", "a] [a\"b c\"] a")
+        self.rws(" [ a [b c] ] ", " [a[bc]] ")
+        self.rws(" [ a b][ c d ] ", " [ab][cd] ")
+        self.rws(" [ a b] [ c d ] ", " [ab] [cd] ")
+        
+        self.rws("a", "a")
+        self.rws(" a ", " a ")
+        self.rws(" {a} ", " {a} ")
+        self.rws(" { a} ", " {a} ")
+        self.rws(" { a } ", " {a} ")
+        self.rws(" { a b c } ", " {abc} ")
+        self.rws(" { a \"b c\" } ", " {a\"b c\"} ")
+        self.rws("a { a \"b c\" } a", "a {a\"b c\"} a")
+        self.rws("a} { a \"b c\" } a", "a} {a\"b c\"} a")
+        self.rws(" { a {b c} } ", " {a{bc}} ")
+        self.rws(" { a b}{ c d } ", " {ab}{cd} ")
+        self.rws(" { a b} { c d } ", " {ab} {cd} ")
+
+        self.rws(" [ a b]{ c d } ", " [ab]{cd} ")
+        self.rws(" [ a b{ c d }] ", " [ab{cd}] ")
+        self.rws(" [ a b{ \"c d\" }] ", " [ab{\"c d\"}] ")
+        
+
+if __name__== "__main__":
+    unittest.main()
+    
diff --git a/src/bin/cfgmgr/Makefile.am b/src/bin/cfgmgr/Makefile.am
index e092070..a41448b 100644
--- a/src/bin/cfgmgr/Makefile.am
+++ b/src/bin/cfgmgr/Makefile.am
@@ -19,7 +19,6 @@ b10-cfgmgr.8: b10-cfgmgr.xml
 
 endif
 
-# TODO: does this need $$(DESTDIR) also?
 # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
 b10-cfgmgr: b10-cfgmgr.py
 	$(SED) "s|@@PYTHONPATH@@|@pyexecdir@|" b10-cfgmgr.py >$@
diff --git a/src/bin/cmdctl/Makefile.am b/src/bin/cmdctl/Makefile.am
index 76572ab..04cf5e2 100644
--- a/src/bin/cmdctl/Makefile.am
+++ b/src/bin/cmdctl/Makefile.am
@@ -4,7 +4,7 @@ pkglibexecdir = $(libexecdir)/@PACKAGE@
 
 pkglibexec_SCRIPTS = b10-cmdctl
 
-b10_cmdctldir = $(DESTDIR)$(pkgdatadir)
+b10_cmdctldir = $(pkgdatadir)
 
 # NOTE: this will overwrite on install
 # So these generic copies are placed in share/bind10 instead of to etc
@@ -33,7 +33,6 @@ endif
 cmdctl.spec: cmdctl.spec.pre
 	$(SED) -e "s|@@SYSCONFDIR@@|$(sysconfdir)|" cmdctl.spec.pre >$@
 
-# TODO: does this need $$(DESTDIR) also?
 # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
 b10-cmdctl: cmdctl.py
 	$(SED) "s|@@PYTHONPATH@@|@pyexecdir@|" cmdctl.py >$@
diff --git a/src/bin/msgq/Makefile.am b/src/bin/msgq/Makefile.am
index 9ed4717..61d4f23 100644
--- a/src/bin/msgq/Makefile.am
+++ b/src/bin/msgq/Makefile.am
@@ -16,7 +16,6 @@ b10-msgq.8: msgq.xml
 
 endif
 
-# TODO: does this need $$(DESTDIR) also?
 # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
 b10-msgq: msgq.py
 	$(SED) "s|@@PYTHONPATH@@|@pyexecdir@|" msgq.py >$@
diff --git a/src/bin/msgq/msgq.py.in b/src/bin/msgq/msgq.py.in
index 8a8362a..06fe840 100755
--- a/src/bin/msgq/msgq.py.in
+++ b/src/bin/msgq/msgq.py.in
@@ -40,7 +40,7 @@ isc.util.process.rename()
 # This is the version that gets displayed to the user.
 # The VERSION string consists of the module name, the module version
 # number, and the overall BIND 10 version number (set in configure.ac).
-VERSION = "b10-msgq 20100818 (BIND 10 @PACKAGE_VERSION@)"
+VERSION = "b10-msgq 20110127 (BIND 10 @PACKAGE_VERSION@)"
 
 class MsgQReceiveError(Exception): pass
 
diff --git a/src/bin/resolver/Makefile.am b/src/bin/resolver/Makefile.am
index 9e86ddd..608e1a9 100644
--- a/src/bin/resolver/Makefile.am
+++ b/src/bin/resolver/Makefile.am
@@ -48,11 +48,13 @@ b10_resolver_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 b10_resolver_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
 b10_resolver_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
 b10_resolver_LDADD += $(top_builddir)/src/lib/log/liblog.la
+b10_resolver_LDADD += $(top_builddir)/src/lib/cache/libcache.la
+b10_resolver_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
 b10_resolver_LDADD += $(top_builddir)/src/bin/auth/change_user.o
 b10_resolver_LDFLAGS = -pthread
 
 # TODO: config.h.in is wrong because doesn't honor pkgdatadir
 # and can't use @datadir@ because doesn't expand default ${prefix}
-b10_resolverdir = $(DESTDIR)$(pkgdatadir)
+b10_resolverdir = $(pkgdatadir)
 b10_resolver_DATA = resolver.spec
 
diff --git a/src/bin/resolver/b10-resolver.8 b/src/bin/resolver/b10-resolver.8
index 493538b..3125e32 100644
--- a/src/bin/resolver/b10-resolver.8
+++ b/src/bin/resolver/b10-resolver.8
@@ -2,12 +2,12 @@
 .\"     Title: b10-resolver
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: January 19, 2011
+.\"      Date: February 17, 2011
 .\"    Manual: BIND10
 .\"    Source: BIND10
 .\"  Language: English
 .\"
-.TH "B10\-RESOLVER" "8" "January 19, 2011" "BIND10" "BIND10"
+.TH "B10\-RESOLVER" "8" "February 17, 2011" "BIND10" "BIND10"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
@@ -39,23 +39,6 @@ will exit\&.
 .PP
 It also receives its configurations from
 \fBb10-cfgmgr\fR(8)\&.
-.if n \{\
-.sp
-.\}
-.RS 4
-.it 1 an-trap
-.nr an-no-space-flag 1
-.nr an-break-flag 1
-.br
-.ps +1
-\fBNote\fR
-.ps -1
-.br
-.PP
-This prototype version only supports forwarding\&. Future versions will introduce full recursion, cache, lookup of local authoritative data (as in
-\fBb10\-auth\fR), and DNSSEC validation\&.
-.sp .5v
-.RE
 .SH "OPTIONS"
 .PP
 The arguments are as follows:
@@ -95,11 +78,30 @@ number\&. The defaults are address ::1 port 5300 and address 127\&.0\&.0\&.1 por
 .PP
 
 \fIretries\fR
-is the number of times to retry (resend query) after a timeout\&. The default is 0 (do not retry)\&.
+is the number of times to retry (resend query) after a query timeout (\fItimeout_query\fR)\&. The default is 3\&.
+.PP
+
+\fIroot_addresses\fR
+is a list of addresses and ports for
+\fBb10\-resolver\fR
+to use directly as root servers to start resolving\&. The list items are the
+\fIaddress\fR
+string and
+\fIport\fR
+number\&. If empty, a hardcoded address for F\-root (192\&.5\&.5\&.241) is used\&.
+.PP
+
+\fItimeout_client\fR
+is the number of milliseconds to wait before timing out the incoming client query\&. If set to \-1, this timeout is disabled\&. The default is 4000\&. After this timeout, a SERVFAIL is sent back to the client asking the question\&. (The lookup may continue after the timeout, but a later answer is not returned for the now\-past query\&.)
 .PP
 
-\fItimeout\fR
-is the number of milliseconds to wait for answer\&. If set to \-1, the timeout is disabled\&. The default is 2000\&.
+\fItimeout_lookup\fR
+is the number of milliseconds before it stops trying the query\&. If set to \-1, this timeout is disabled\&. The default is 30000\&.
+.PP
+
+
+\fItimeout_query\fR
+is the number of milliseconds to wait before it retries a query\&. If set to \-1, this timeout is disabled\&. The default is 2000\&.
 .PP
 The configuration command is:
 .PP
@@ -119,8 +121,7 @@ BIND 10 Guide\&.
 .PP
 The
 \fBb10\-resolver\fR
-daemon was first coded in September 2010\&. The initial implementation only provided forwarding\&.
-
+daemon was first coded in September 2010\&. The initial implementation only provided forwarding\&. Iteration was introduced in January 2011\&.
 
 .SH "COPYRIGHT"
 .br
diff --git a/src/bin/resolver/b10-resolver.xml b/src/bin/resolver/b10-resolver.xml
index dc2b724..0d395a7 100644
--- a/src/bin/resolver/b10-resolver.xml
+++ b/src/bin/resolver/b10-resolver.xml
@@ -20,7 +20,7 @@
 <refentry>
 
   <refentryinfo>
-    <date>January 19, 2011</date>
+    <date>February 17, 2011</date>
   </refentryinfo>
 
   <refmeta>
@@ -69,11 +69,13 @@
 <citerefentry><refentrytitle>b10-cfgmgr</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
     </para>
 
+<!--
     <note><para>
-      This prototype version only supports forwarding.  Future versions
-      will introduce full recursion, cache, lookup of local authoritative
-      data (as in <command>b10-auth</command>), and DNSSEC validation.
+      Future versions will introduce lookup of local authoritative
+      data (as in <command>b10-auth</command>) and DNSSEC validation.
     </para></note>
+-->
+
   </refsect1>
 
   <refsect1>
@@ -128,7 +130,7 @@ port
 -->
     </para>
 
-<!-- trac386:
+<!-- trac384:
 
 once that is merged you can for instance do 'config add Resolver/forward_addresses { "port": 123 } and it will fill in the rest (in this case ::1 for the address)
 
@@ -145,13 +147,43 @@ once that is merged you can for instance do 'config add Resolver/forward_address
 
     <para>
       <varname>retries</varname> is the number of times to retry
-      (resend query) after a timeout.
-      The default is 0 (do not retry).
+      (resend query) after a query timeout
+      (<varname>timeout_query</varname>).
+      The default is 3.
+    </para>
+
+    <para>
+      <varname>root_addresses</varname> is a list of addresses and ports
+      for <command>b10-resolver</command> to use directly as
+      root servers to start resolving.
+      The list items are the <varname>address</varname> string
+      and <varname>port</varname> number.
+      If empty, a hardcoded address for F-root (192.5.5.241) is used.
+    </para>
+
+    <para>
+      <varname>timeout_client</varname> is the number of milliseconds
+      to wait before timing out the incoming client query.
+      If set to -1, this timeout is disabled.
+      The default is 4000.
+      After this timeout, a SERVFAIL is sent back to the client asking
+      the question.
+      (The lookup may continue after the timeout, but a later answer
+      is not returned for the now-past query.)
+    </para>
+
+    <para>
+      <varname>timeout_lookup</varname> is the number of milliseconds
+      before it stops trying the query.
+      If set to -1, this timeout is disabled.
+      The default is 30000.
     </para>
 
     <para>
-      <varname>timeout</varname> is the number of milliseconds to
-      wait for answer. If set to -1, the timeout is disabled.
+<!-- previous timeout was renamed to timeout_query -->
+      <varname>timeout_query</varname> is the number of milliseconds to
+      wait before it retries a query.
+      If set to -1, this timeout is disabled.
       The default is 2000.
     </para>
 
@@ -200,9 +232,8 @@ once that is merged you can for instance do 'config add Resolver/forward_address
     <para>
       The <command>b10-resolver</command> daemon was first coded in
       September 2010. The initial implementation only provided
-      forwarding.
+      forwarding. Iteration was introduced in January 2011.
 <!-- TODO: document when caching was added -->
-<!-- TODO: document when iteration was added -->
 <!-- TODO: document when validation was added -->
     </para>
   </refsect1>
diff --git a/src/bin/resolver/resolver.cc b/src/bin/resolver/resolver.cc
index 5cde3b4..e7474e9 100644
--- a/src/bin/resolver/resolver.cc
+++ b/src/bin/resolver/resolver.cc
@@ -182,6 +182,8 @@ public:
     MessagePtr message_;
 };
 
+
+// TODO: REMOVE, USE isc::resolve::MakeErrorMessage?
 void
 makeErrorMessage(MessagePtr message, OutputBufferPtr buffer,
                  const Rcode& rcode)
@@ -256,25 +258,16 @@ public:
         const qid_t qid = query_message->getQid();
         const bool rd = query_message->getHeaderFlag(Message::HEADERFLAG_RD);
         const bool cd = query_message->getHeaderFlag(Message::HEADERFLAG_CD);
-        const Opcode& opcode = query_message->getOpcode();
-
-        // Fill in the final details of the answer message
+        
+        // The opcode and question section should have already been set,
+        // fill in the final details of the answer message
         answer_message->setQid(qid);
-        answer_message->setOpcode(opcode);
 
         answer_message->setHeaderFlag(Message::HEADERFLAG_QR);
         answer_message->setHeaderFlag(Message::HEADERFLAG_RA);
-        if (rd) {
-            answer_message->setHeaderFlag(Message::HEADERFLAG_RD);
-        }
-        if (cd) {
-            answer_message->setHeaderFlag(Message::HEADERFLAG_CD);
-        }
+        answer_message->setHeaderFlag(Message::HEADERFLAG_RD, rd);
+        answer_message->setHeaderFlag(Message::HEADERFLAG_CD, cd);
 
-        vector<QuestionPtr> questions;
-        questions.assign(query_message->beginQuestion(), query_message->endQuestion());
-        for_each(questions.begin(), questions.end(), QuestionInserter(answer_message));
-        
         // Now we can clear the buffer and render the new message into it
         buffer->clear();
         MessageRenderer renderer(*buffer);
diff --git a/src/bin/resolver/tests/Makefile.am b/src/bin/resolver/tests/Makefile.am
index a03439c..3dc6b3b 100644
--- a/src/bin/resolver/tests/Makefile.am
+++ b/src/bin/resolver/tests/Makefile.am
@@ -37,6 +37,8 @@ run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la
 run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 run_unittests_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
 run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
+run_unittests_LDADD += $(top_builddir)/src/lib/cache/libcache.la
+run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
 
 # Note the ordering matters: -Wno-... must follow -Wextra (defined in
 # B10_CXXFLAGS
diff --git a/src/bin/stats/Makefile.am b/src/bin/stats/Makefile.am
index b267479..b173813 100644
--- a/src/bin/stats/Makefile.am
+++ b/src/bin/stats/Makefile.am
@@ -5,7 +5,7 @@ pkglibexecdir = $(libexecdir)/@PACKAGE@
 pkglibexec_SCRIPTS = b10-stats
 noinst_SCRIPTS = b10-stats_stub
 
-b10_statsdir = $(DESTDIR)$(pkgdatadir)
+b10_statsdir = $(pkgdatadir)
 b10_stats_DATA = stats.spec
 
 CLEANFILES = stats.spec b10-stats stats.pyc stats.pyo b10-stats_stub stats_stub.pyc stats_stub.pyo
@@ -23,7 +23,6 @@ endif
 stats.spec: stats.spec.pre
 	$(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" stats.spec.pre >$@
 
-# TODO: does this need $$(DESTDIR) also?
 # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
 b10-stats: stats.py
 	$(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" \
diff --git a/src/bin/usermgr/Makefile.am b/src/bin/usermgr/Makefile.am
index f830aad..7ebb1cd 100644
--- a/src/bin/usermgr/Makefile.am
+++ b/src/bin/usermgr/Makefile.am
@@ -1,6 +1,6 @@
 sbin_SCRIPTS = b10-cmdctl-usermgr
 
-b10_cmdctl_usermgrdir = $(DESTDIR)$(pkgdatadir)
+b10_cmdctl_usermgrdir = $(pkgdatadir)
 
 CLEANFILES=	b10-cmdctl-usermgr
 
@@ -14,7 +14,6 @@ b10-cmdctl-usermgr.8: b10-cmdctl-usermgr.xml
 
 endif
 
-# TODO: does this need $$(DESTDIR) also?
 # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
 b10-cmdctl-usermgr: b10-cmdctl-usermgr.py
 	$(SED) "s|@@PYTHONPATH@@|@pyexecdir@|" b10-cmdctl-usermgr.py >$@
diff --git a/src/bin/xfrin/Makefile.am b/src/bin/xfrin/Makefile.am
index 7ed41e2..ee8505e 100644
--- a/src/bin/xfrin/Makefile.am
+++ b/src/bin/xfrin/Makefile.am
@@ -4,7 +4,7 @@ pkglibexecdir = $(libexecdir)/@PACKAGE@
 
 pkglibexec_SCRIPTS = b10-xfrin
 
-b10_xfrindir = $(DESTDIR)$(pkgdatadir)
+b10_xfrindir = $(pkgdatadir)
 b10_xfrin_DATA = xfrin.spec
 
 CLEANFILES = b10-xfrin xfrin.pyc 
@@ -20,7 +20,6 @@ b10-xfrin.8: b10-xfrin.xml
 
 endif
 
-# TODO: does this need $$(DESTDIR) also?
 # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
 b10-xfrin: xfrin.py
 	$(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" \
diff --git a/src/bin/xfrout/Makefile.am b/src/bin/xfrout/Makefile.am
index 4955b7a..d4f021e 100644
--- a/src/bin/xfrout/Makefile.am
+++ b/src/bin/xfrout/Makefile.am
@@ -4,7 +4,7 @@ pkglibexecdir = $(libexecdir)/@PACKAGE@
 
 pkglibexec_SCRIPTS = b10-xfrout
 
-b10_xfroutdir = $(DESTDIR)$(pkgdatadir)
+b10_xfroutdir = $(pkgdatadir)
 b10_xfrout_DATA = xfrout.spec
 
 CLEANFILES=	b10-xfrout xfrout.pyc xfrout.spec
@@ -23,7 +23,6 @@ endif
 xfrout.spec: xfrout.spec.pre
 	$(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" xfrout.spec.pre >$@
 
-# TODO: does this need $$(DESTDIR) also?
 # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
 b10-xfrout: xfrout.py
 	$(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" \
diff --git a/src/bin/xfrout/tests/xfrout_test.py b/src/bin/xfrout/tests/xfrout_test.py
index 2fb4463..55a2e52 100644
--- a/src/bin/xfrout/tests/xfrout_test.py
+++ b/src/bin/xfrout/tests/xfrout_test.py
@@ -121,6 +121,29 @@ class TestXfroutSession(unittest.TestCase):
         get_msg = self.sock.read_msg()
         self.assertEqual(get_msg.get_rcode().to_text(), "NXDOMAIN")
 
+    def test_send_message(self):
+        msg = self.getmsg()
+        msg.make_response()
+        # soa record data with different cases
+        soa_record = (4, 3, 'Example.com.', 'com.Example.', 3600, 'SOA', None, 'master.Example.com. admin.exAmple.com. 1234 3600 1800 2419200 7200')
+        rrset_soa = self.xfrsess._create_rrset_from_db_record(soa_record)
+        msg.add_rrset(Message.SECTION_ANSWER, rrset_soa)
+        self.xfrsess._send_message(self.sock, msg)
+        send_out_data = self.sock.readsent()[2:]
+
+        # CASE_INSENSITIVE compression mode
+        render = MessageRenderer();
+        render.set_length_limit(XFROUT_MAX_MESSAGE_SIZE)
+        msg.to_wire(render)
+        self.assertNotEqual(render.get_data(), send_out_data)
+
+        # CASE_SENSITIVE compression mode
+        render.clear()
+        render.set_compress_mode(MessageRenderer.CASE_SENSITIVE)
+        render.set_length_limit(XFROUT_MAX_MESSAGE_SIZE)
+        msg.to_wire(render)
+        self.assertEqual(render.get_data(), send_out_data)
+
     def test_clear_message(self):
         msg = self.getmsg()
         qid = msg.get_qid()
diff --git a/src/bin/xfrout/xfrout.py.in b/src/bin/xfrout/xfrout.py.in
index eb96b94..a819640 100755
--- a/src/bin/xfrout/xfrout.py.in
+++ b/src/bin/xfrout/xfrout.py.in
@@ -170,6 +170,9 @@ class XfroutSession(BaseRequestHandler):
 
     def _send_message(self, sock_fd, msg):
         render = MessageRenderer()
+        # As defined in RFC5936 section3.4, perform case-preserving name
+        # compression for AXFR message.
+        render.set_compress_mode(MessageRenderer.CASE_SENSITIVE)
         render.set_length_limit(XFROUT_MAX_MESSAGE_SIZE)
         msg.to_wire(render)
         header_len = struct.pack('H', socket.htons(render.get_length()))
diff --git a/src/bin/zonemgr/Makefile.am b/src/bin/zonemgr/Makefile.am
index dd3e67a..410279a 100644
--- a/src/bin/zonemgr/Makefile.am
+++ b/src/bin/zonemgr/Makefile.am
@@ -4,7 +4,7 @@ pkglibexecdir = $(libexecdir)/@PACKAGE@
 
 pkglibexec_SCRIPTS = b10-zonemgr
 
-b10_zonemgrdir = $(DESTDIR)$(pkgdatadir)
+b10_zonemgrdir = $(pkgdatadir)
 b10_zonemgr_DATA = zonemgr.spec
 
 CLEANFILES = b10-zonemgr zonemgr.pyc zonemgr.spec
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
index 8ab93ba..d5486a0 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 cache
+          resolve nsas cache asiolink testutils
diff --git a/src/lib/asiolink/Makefile.am b/src/lib/asiolink/Makefile.am
index 868fde5..b3968f0 100644
--- a/src/lib/asiolink/Makefile.am
+++ b/src/lib/asiolink/Makefile.am
@@ -12,24 +12,29 @@ 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.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  = asiolink.h
 libasiolink_la_SOURCES += dns_answer.h
-libasiolink_la_SOURCES += simple_callback.h
+libasiolink_la_SOURCES += dns_lookup.h
+libasiolink_la_SOURCES += dns_server.h
+libasiolink_la_SOURCES += dns_service.h dns_service.cc
+libasiolink_la_SOURCES += dummy_io_cb.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_address.h io_address.cc
+libasiolink_la_SOURCES += io_asio_socket.h
+libasiolink_la_SOURCES += io_endpoint.h io_endpoint.cc
+libasiolink_la_SOURCES += io_error.h
+libasiolink_la_SOURCES += io_fetch.h io_fetch.cc
 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 += io_service.h io_service.cc
+libasiolink_la_SOURCES += io_socket.h io_socket.cc
+libasiolink_la_SOURCES += recursive_query.h recursive_query.cc
+libasiolink_la_SOURCES += simple_callback.h
+libasiolink_la_SOURCES += tcp_endpoint.h
 libasiolink_la_SOURCES += tcp_server.h tcp_server.cc
+libasiolink_la_SOURCES += tcp_socket.h
+libasiolink_la_SOURCES += udp_endpoint.h
+libasiolink_la_SOURCES += udp_server.h udp_server.cc
+libasiolink_la_SOURCES += udp_socket.h
 # Note: the ordering matters: -Wno-... must follow -Wextra (defined in
 # B10_CXXFLAGS)
 libasiolink_la_CXXFLAGS = $(AM_CXXFLAGS)
@@ -43,3 +48,5 @@ endif
 libasiolink_la_CPPFLAGS = $(AM_CPPFLAGS)
 libasiolink_la_LIBADD = $(top_builddir)/src/lib/log/liblog.la
 libasiolink_la_LIBADD += $(top_builddir)/src/lib/resolve/libresolve.la
+libasiolink_la_LIBADD += $(top_builddir)/src/lib/cache/libcache.la
+libasiolink_la_LIBADD += $(top_builddir)/src/lib/nsas/libnsas.la
diff --git a/src/lib/asiolink/README b/src/lib/asiolink/README
index b0f6a7d..6bd1a73 100644
--- a/src/lib/asiolink/README
+++ b/src/lib/asiolink/README
@@ -33,7 +33,7 @@ This is intended to simplify development a bit, since it allows the
 routines to be written in a straightfowrard step-step-step fashion rather
 than as a complex chain of separate handler functions.
 
-Coroutine objects (i.e., UDPServer, TCPServer and UDPQuery) are objects
+Coroutine objects (i.e., UDPServer, TCPServer and IOFetch) are objects
 with reenterable operator() members.  When an instance of one of these
 classes is called as a function, it resumes at the position where it left
 off.  Thus, a UDPServer can issue an asynchronous I/O call and specify
@@ -101,3 +101,82 @@ when the answer has arrived.  In simplified form, the DNSQuery routine is:
 Currently, DNSQuery is only implemented for UDP queries.  In future work
 it will be necessary to write code to fall back to TCP when circumstances
 require it.
+
+
+Upstream Fetches
+================
+Upstream fetches (queries by the resolver on behalf of a client) are made
+using a slightly-modified version of the pattern described above.
+
+Sockets
+-------
+First, it will be useful to understand the class hierarchy used in the
+fetch logic:
+
+        IOSocket
+           |
+      IOAsioSocket
+           |
+     +-----+-----+                
+     |           |
+UDPSocket    TCPSocket
+
+IOSocket is a wrapper class for a socket and is used by the authoritative
+server code.  It is an abstract base class, providing little more that the ability to hold the socket and to return the protocol in use.
+
+Built on this is IOAsioSocket, which adds the open, close, asyncSend and
+asyncReceive methods.  This is a template class, which takes as template
+argument the class of the object that will be used as the callback when the
+asynchronous operation completes. This object can be of any type, but must
+include an operator() method with the signature:
+
+   operator()(asio::error_code ec, size_t length)
+
+... the two arguments being the status of the completed I/O operation and
+the number of bytes transferred. (In the case of the open method, the second
+argument will be zero.)
+
+Finally, the TCPSocket and UDPSocket classes provide the body of the
+asynchronous operations.
+
+Fetch Sequence
+--------------
+The fetch is implemented by the IOFetch class, which takes as argument the
+protocol to use.  The sequence is:
+
+  REENTER:
+    render the question into a wire-format query packet
+    open()                           // Open socket and optionally connect
+    if (! synchronous) {
+        YIELD;
+    }
+    YIELD asyncSend(query)           // Send query 
+    do {
+        YIELD asyncReceive(response) // Read response
+    } while (! complete(response))
+    close()                          // Drop connection and close socket
+    server->resume
+
+The open() method opens a socket for use.  On TCP, it also makes a
+connection to the remote end.  So under UDP the operation will complete
+immediately, but under TCP it could take a long time.  One solution would be
+for the open operation to post an event to the I/O queue; then both cases
+could be regarded as being equivalent, with the completion being signalled
+by the posting of the completion event.  However UDP is the most common case
+and that would involve extra overhead.  So the open() returns a status
+indicating whether the operation completed asynchronously.  If it did, the
+code yields back to the coroutine; if not the yield is bypassed.
+
+The asynchronous send is straightforward, invoking the underlying ASIO
+function.  (Note that the address/port is supplied to both the open() and
+asyncSend() methods - it is used by the TCPSocket in open() and by the
+UDPSocket in asyncSend().)
+
+The asyncReceive() method issues an asynchronous read and waits for completion.
+The fetch object keeps track of the amount of data received so far and when
+the receive completes it calls a method on the socket to determine if the
+entire message has been received.  (This will always be the case for UDP.  On
+TCP though, the message is preceded by a count field as several reads may be
+required to read all the data.)  The fetch loops until all the data is read.
+
+Finally, the socket is closed and the server called to resume operation.
diff --git a/src/lib/asiolink/asiolink.h b/src/lib/asiolink/asiolink.h
index 43a1244..03951ae 100644
--- a/src/lib/asiolink/asiolink.h
+++ b/src/lib/asiolink/asiolink.h
@@ -32,6 +32,7 @@
 #include <asiolink/io_endpoint.h>
 #include <asiolink/io_message.h>
 #include <asiolink/io_socket.h>
+#include <asiolink/io_error.h>
 
 /// \namespace asiolink
 /// \brief A wrapper interface for the ASIO library.
@@ -83,20 +84,6 @@
 /// the placeholder of callback handlers:
 /// http://think-async.com/Asio/asio-1.3.1/doc/asio/reference/asio_handler_allocate.html
 
-namespace asiolink {
-
-
-/// \brief An exception that is thrown if an error occurs within the IO
-/// module.  This is mainly intended to be a wrapper exception class for
-/// ASIO specific exceptions.
-class IOError : public isc::Exception {
-public:
-    IOError(const char* file, size_t line, const char* what) :
-        isc::Exception(file, line, what) {}
-};
-
-
-}      // asiolink
 #endif // __ASIOLINK_H
 
 // Local Variables: 
diff --git a/src/lib/asiolink/dns_service.cc b/src/lib/asiolink/dns_service.cc
index fee2a32..f17bb44 100644
--- a/src/lib/asiolink/dns_service.cc
+++ b/src/lib/asiolink/dns_service.cc
@@ -12,13 +12,19 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <unistd.h>             // for some IPC/network system calls
+
+#include <boost/lexical_cast.hpp>
+
 #include <config.h>
 
-// unistd is needed for asio.hpp with SunStudio
-#include <unistd.h>
+#include <log/dummylog.h>
 
 #include <asio.hpp>
-
+#include <asiolink/dns_service.h>
+#include <asiolink/io_service.h>
 #include <asiolink/io_service.h>
 #include <asiolink/tcp_server.h>
 #include <asiolink/udp_server.h>
diff --git a/src/lib/asiolink/dns_service.h b/src/lib/asiolink/dns_service.h
index 6b14345..84aa5fb 100644
--- a/src/lib/asiolink/dns_service.h
+++ b/src/lib/asiolink/dns_service.h
@@ -97,6 +97,12 @@ public:
     /// It will eventually be removed once the wrapper interface is
     /// generalized.
     asio::io_service& get_io_service() { return io_service_.get_io_service(); }
+
+    /// \brief Return the IO Service Object
+    ///
+    /// \return IOService object for this DNS service.
+    asiolink::IOService& getIOService() { return (io_service_);}
+
 private:
     DNSServiceImpl* impl_;
     IOService& io_service_;
diff --git a/src/lib/asiolink/dummy_io_cb.h b/src/lib/asiolink/dummy_io_cb.h
new file mode 100644
index 0000000..bde656c
--- /dev/null
+++ b/src/lib/asiolink/dummy_io_cb.h
@@ -0,0 +1,51 @@
+// 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 __DUMMY_IO_CB_H
+#define __DUMMY_IO_CB_H
+
+#include <iostream>
+
+#include <asio/error.hpp>
+#include <asio/error_code.hpp>
+
+namespace asiolink {
+
+/// \brief Asynchronous I/O Completion Callback
+///
+/// The two socket classes (UDPSocket and TCPSocket) require that the I/O
+/// completion callback function have an operator() method with the appropriate
+/// signature.  The classes are templates, any class with that method and
+/// signature can be passed as the callback object - there is no need for a
+/// base class defining the interface.  However, some users of the socket
+/// classes do not use the asynchronous I/O operations, yet have to supply a
+/// template parameter.  This is the reason for this class - it is the dummy
+/// template parameter.
+
+class DummyIOCallback {
+public:
+
+    /// \brief Asynchronous I/O callback method
+    ///
+    /// \param error Unused
+    /// \param length Unused
+    void operator()(asio::error_code, size_t)
+    {
+        // TODO: log an error if this method ever gets called.
+    }
+};
+
+} // namespace asiolink
+
+#endif // __DUMMY_IO_CB_H
diff --git a/src/lib/asiolink/internal/iofetch.h b/src/lib/asiolink/internal/iofetch.h
new file mode 100644
index 0000000..d066c92
--- /dev/null
+++ b/src/lib/asiolink/internal/iofetch.h
@@ -0,0 +1,125 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __IOFETCH_H
+#define __IOFETCH_H 1
+
+#include <config.h>
+
+#include <asio.hpp>
+#include <boost/shared_array.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <dns/buffer.h>
+#include <dns/message.h>
+#include <dns/messagerenderer.h>
+
+#include <asiolink/asiolink.h>
+#include <asiolink/internal/coroutine.h>
+
+// This file contains TCP/UDP-specific implementations of generic classes 
+// defined in asiolink.h.  It is *not* intended to be part of the public
+// API.
+
+namespace asiolink {
+//
+// Asynchronous UDP/TCP coroutine for upstream fetches
+//
+//class IOFetch : public coroutine, public UdpFetch, public TcpFetch {
+class IOFetch : public coroutine {
+public:
+    // TODO Maybe this should be more generic than just for IOFetch?
+    ///
+    /// \brief Result of the query
+    ///
+    /// This is related only to contacting the remote server. If the answer
+    ///indicates error, it is still counted as SUCCESS here, if it comes back.
+    ///
+    enum Result {
+        SUCCESS,
+        TIME_OUT,
+        STOPPED
+    };
+    /// Abstract callback for the IOFetch.
+    class Callback {
+    public:
+        virtual ~Callback() {}
+
+        /// This will be called when the IOFetch is completed
+        virtual void operator()(Result result) = 0;
+    };
+    ///
+    /// \brief Constructor.
+    ///
+    /// It creates the query.
+    /// @param callback will be called when we terminate. It is your task to
+    ///        delete it if allocated on heap.
+    ///@param timeout in ms.
+    ///
+    IOFetch(asio::io_service& io_service,
+                      const isc::dns::Question& q,
+                      const IOAddress& addr, uint16_t port,
+                      isc::dns::OutputBufferPtr buffer,
+                      Callback* callback, int timeout = -1, 
+                      int protocol = IPPROTO_UDP);
+    void operator()(asio::error_code ec = asio::error_code(),
+                    size_t length = 0);
+    /// Terminate the query.
+    void stop(Result reason = STOPPED);
+private:
+    enum { MAX_LENGTH = 4096 };
+
+    ///
+    /// \short Private data
+    ///
+    /// They are not private because of stability of the
+    /// interface (this is private class anyway), but because this class
+    /// will be copyed often (it is used as a coroutine and passed as callback
+    /// to many async_*() functions) and we want keep the same data. Some of
+    /// the data is not copyable too.
+    ///
+    //struct IOFetchProtocol;
+    //boost::shared_ptr<IOFetchProtocol> data_;
+    //struct UdpData;
+    //struct TcpData;
+    boost::shared_ptr<UdpFetch> data_;
+    boost::shared_ptr<TcpFetch> tcp_data_;
+};
+class UdpFetch : public IOFetch {
+    public:
+        struct UdpData;
+        explicit UdpFetch(asio::io_service& io_service,
+                          const isc::dns::Question& q,
+                          const IOAddress& addr,
+                          uint16_t port,
+                          isc::dns::OutputBufferPtr buffer,
+                          IOFetch::Callback *callback,
+                          int timeout);
+};
+class TcpFetch : public IOFetch {
+    public:
+        struct TcpData;
+        explicit TcpFetch(io_service& io_service, const Question& q,
+                 const IOAddress& addr, uint16_t port,
+                 OutputBufferPtr buffer, Callback *callback, int timeout);
+};
+
+}
+
+
+#endif // __IOFETCH_H
+
+// Local Variables: 
+// mode: c++
+// End: 
diff --git a/src/lib/asiolink/interval_timer.cc b/src/lib/asiolink/interval_timer.cc
index 6b0cd09..8efb102 100644
--- a/src/lib/asiolink/interval_timer.cc
+++ b/src/lib/asiolink/interval_timer.cc
@@ -14,18 +14,18 @@
 
 #include <config.h>
 
-// unistd is needed for asio.hpp with SunStudio
-#include <unistd.h>
+#include <unistd.h>             // for some IPC/network system calls
+#include <sys/socket.h>
+#include <netinet/in.h>
 
-#include <asio.hpp>
+#include <boost/bind.hpp>
 
 #include <exceptions/exceptions.h>
 
+#include <asio.hpp>
 #include <asiolink/interval_timer.h>
 #include <asiolink/io_service.h>
 
-#include <boost/bind.hpp>
-
 namespace asiolink {
 
 class IntervalTimerImpl {
diff --git a/src/lib/asiolink/io_address.cc b/src/lib/asiolink/io_address.cc
index 990524a..70e8374 100644
--- a/src/lib/asiolink/io_address.cc
+++ b/src/lib/asiolink/io_address.cc
@@ -20,7 +20,10 @@
 
 #include <asio.hpp>
 
-#include <asiolink/asiolink.h>
+#include <exceptions/exceptions.h>
+#include <asiolink/io_address.h>
+#include <asiolink/io_error.h>
+
 
 using namespace asio;
 using asio::ip::udp;
diff --git a/src/lib/asiolink/io_address.h b/src/lib/asiolink/io_address.h
index 98e6fe8..0d2787f 100644
--- a/src/lib/asiolink/io_address.h
+++ b/src/lib/asiolink/io_address.h
@@ -12,8 +12,8 @@
 // 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
+#ifndef __IO_ADDRESS_H
+#define __IO_ADDRESS_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.
@@ -120,7 +120,7 @@ private:
 };
 
 }      // asiolink
-#endif // __IOADDRESS_H
+#endif // __IO_ADDRESS_H
 
 // Local Variables: 
 // mode: c++
diff --git a/src/lib/asiolink/io_asio_socket.h b/src/lib/asiolink/io_asio_socket.h
new file mode 100644
index 0000000..eae9b32
--- /dev/null
+++ b/src/lib/asiolink/io_asio_socket.h
@@ -0,0 +1,309 @@
+// 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 __IO_ASIO_SOCKET_H
+#define __IO_ASIO_SOCKET_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 <coroutine.h>
+
+#include <asiolink/io_error.h>
+#include <asiolink/io_socket.h>
+
+
+namespace asiolink {
+
+/// \brief Socket not open
+///
+/// Thrown on an attempt to do read/write to a socket that is not open.
+class SocketNotOpen : public IOError {
+public:
+    SocketNotOpen(const char* file, size_t line, const char* what) :
+        IOError(file, line, what) {}
+};
+
+
+
+/// Forward declaration of an IOEndpoint
+class IOEndpoint;
+
+
+/// \brief I/O Socket with asynchronous operations
+///
+/// This class is a wrapper for the ASIO socket classes such as
+/// \c ip::tcp::socket and \c ip::udp::socket.
+///
+/// This is the basic IOSocket with additional operations - open, send, receive
+/// and close.  Depending on how the asiolink code develops, it may be a
+/// temporary class: its main use is to add the template parameter needed for
+/// the derived classes UDPSocket and TCPSocket but without changing the
+/// signature of the more basic IOSocket class.
+///
+/// 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).
+///
+/// TODO: Check if IOAsioSocket class is still needed
+///
+/// \param C Template parameter identifying type of the callback object.
+
+template <typename C>
+class IOAsioSocket : public IOSocket {
+    ///
+    /// \name Constructors and Destructor
+    ///
+    /// Note: The copy constructor and the assignment operator are
+    /// intentionally defined as private, making this class non-copyable.
+    //@{
+private:
+    IOAsioSocket(const IOAsioSocket<C>& source);
+    IOAsioSocket& operator=(const IOAsioSocket<C>& 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).
+    IOAsioSocket() {}
+public:
+    /// The destructor.
+    virtual ~IOAsioSocket() {}
+    //@}
+
+    /// \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 Open AsioSocket
+    ///
+    /// Opens the socket for asynchronous I/O.  On a UDP socket, this is merely
+    /// an "open()" on the underlying socket (so completes immediately), but on
+    /// a TCP socket it also connects to the remote end (which is done as an
+    /// asynchronous operation).
+    ///
+    /// For TCP, signalling of the completion of the operation is done by
+    /// by calling the callback function in the normal way.  This could be done
+    /// for UDP (by posting en event on the event queue); however, that will
+    /// incur additional overhead in the most common case.  Instead, the return
+    /// value indicates whether the operation was asynchronous or not. If yes,
+    /// (i.e. TCP) the callback has been posted to the event queue: if no (UDP),
+    /// no callback has been posted (in which case it is up to the caller as to
+    /// whether they want to manually post the callback themself.)
+    ///
+    /// \param endpoint Pointer to the endpoint object.  This is ignored for
+    /// a UDP socket (the target is specified in the send call), but should
+    /// be of type TCPEndpoint for a TCP connection.
+    /// \param callback I/O Completion callback, called when the operation has
+    /// completed, but only if the operation was asynchronous.
+    ///
+    /// \return true if an asynchronous operation was started and the caller
+    /// should yield and wait for completion, false if the operation was
+    /// completed synchronously and no callback was queued.
+    virtual bool open(const IOEndpoint* endpoint, C& callback) = 0;
+
+    /// \brief Send Asynchronously
+    ///
+    /// This corresponds to async_send_to() for UDP sockets and async_send()
+    /// for TCP.  In both cases an endpoint argument is supplied indicating the
+    /// target of the send - this is ignored for TCP.
+    ///
+    /// \param data Data to send
+    /// \param length Length of data to send
+    /// \param endpoint Target of the send
+    /// \param callback Callback object.
+    virtual void asyncSend(const void* data, size_t length,
+        const IOEndpoint* endpoint, C& callback) = 0;
+
+    /// \brief Receive Asynchronously
+    ///
+    /// This correstponds to async_receive_from() for UDP sockets and
+    /// async_receive() for TCP.  In both cases, an endpoint argument is
+    /// supplied to receive the source of the communication.  For TCP it will
+    /// be filled in with details of the connection.
+    ///
+    /// \param data Buffer to receive incoming message
+    /// \param length Length of the data buffer
+    /// \param cumulative Amount of data that should already be in the buffer.
+    /// \param endpoint Source of the communication
+    /// \param callback Callback object
+    virtual void asyncReceive(void* data, size_t length, size_t cumulative,
+        IOEndpoint* endpoint, C& callback) = 0;
+
+    /// \brief Checks if the data received is complete.
+    ///
+    /// This applies to TCP receives, where the data is a byte stream and a
+    /// receive is not guaranteed to receive the entire message.  DNS messages
+    /// over TCP are prefixed by a two-byte count field.  This method takes the
+    /// amount received so far and the amount received in this I/O and checks
+    /// if the message is complete, returning the appropriate indication.  As
+    /// a side-effect, it also updates the amount received.
+    ///
+    /// For a UDP receive, all the data is received in one I/O, so this is
+    /// effectively a no-op (although it does update the amount received).
+    ///
+    /// \param data Data buffer containing data to date
+    /// \param length Amount of data received in last asynchronous I/O
+    /// \param cumulative On input, amount of data received before the last
+    /// I/O.  On output, the total amount of data received to date.
+    ///
+    /// \return true if the receive is complete, false if another receive is
+    /// needed.
+    virtual bool receiveComplete(void* data, size_t length,
+        size_t& cumulative) = 0;
+
+    /// \brief Cancel I/O On AsioSocket
+    virtual void cancel() = 0;
+
+    /// \brief Close socket
+    virtual void close() = 0;
+};
+
+
+#include "io_socket.h"
+
+/// \brief The \c DummyAsioSocket class is a concrete derived class of
+/// \c IOAsioSocket 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 IOAsioSocket object without involving system resource
+/// allocation such as real network sockets.
+///
+/// \param C Template parameter identifying type of the callback object.
+
+template <typename C>
+class DummyAsioSocket : public IOAsioSocket<C> {
+private:
+    DummyAsioSocket(const DummyAsioSocket<C>& source);
+    DummyAsioSocket& operator=(const DummyAsioSocket<C>& 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.
+    DummyAsioSocket(const int protocol) : protocol_(protocol) {}
+
+    /// \brief A dummy derived method of \c IOAsioSocket::getNative().
+    ///
+    /// \return Always returns -1 as the object is not associated with a real
+    /// (native) socket.
+    virtual int getNative() const { return (-1); }
+
+    /// \brief A dummy derived method of \c IOAsioSocket::getProtocol().
+    ///
+    /// \return Protocol socket was created with
+    virtual int getProtocol() const { return (protocol_); }
+
+
+    /// \brief Open AsioSocket
+    ///
+    /// A call that is a no-op on UDP sockets, this opens a connection to the
+    /// system identified by the given endpoint.
+    ///
+    /// \param endpoint Unused
+    /// \param callback Unused.
+    ///false indicating that the operation completed synchronously.
+    virtual bool open(const IOEndpoint*, C&) {
+        return (false);
+    }
+
+    /// \brief Send Asynchronously
+    ///
+    /// Must be supplied as it is abstract in the base class.
+    ///
+    /// \param data Unused
+    /// \param length Unused
+    /// \param endpoint Unused
+    /// \param callback Unused
+    virtual void asyncSend(const void*, size_t, const IOEndpoint*, C&) {
+    }
+
+    /// \brief Receive Asynchronously
+    ///
+    /// Must be supplied as it is abstract in the base class.
+    ///
+    /// \param data Unused
+    /// \param length Unused
+    /// \param cumulative Unused
+    /// \param endpoint Unused
+    /// \param callback Unused
+    virtual void asyncReceive(void* data, size_t, size_t, IOEndpoint*, C&) { } 
+    /// \brief Checks if the data received is complete.
+    ///
+    /// \param data Unused
+    /// \param length Unused
+    /// \param cumulative Unused
+    ///
+    /// \return Always true
+    virtual bool receiveComplete(void*, size_t, size_t&) {
+        return (true);
+    }
+
+    /// \brief Cancel I/O On AsioSocket
+    ///
+    /// Must be supplied as it is abstract in the base class.
+    virtual void cancel() {
+    }
+
+    /// \brief Close socket
+    ///
+    /// Must be supplied as it is abstract in the base class.
+    virtual void close() {
+    }
+
+private:
+    const int protocol_;
+};
+
+} // namespace asiolink
+
+#endif // __IO_ASIO_SOCKET_H
diff --git a/src/lib/asiolink/io_endpoint.cc b/src/lib/asiolink/io_endpoint.cc
index 86e0607..bf79f61 100644
--- a/src/lib/asiolink/io_endpoint.cc
+++ b/src/lib/asiolink/io_endpoint.cc
@@ -20,7 +20,8 @@
 
 #include <asio.hpp>
 
-#include <asiolink/asiolink.h>
+#include <asiolink/io_address.h>
+#include <asiolink/io_error.h>
 #include <asiolink/tcp_endpoint.h>
 #include <asiolink/udp_endpoint.h>
 
diff --git a/src/lib/asiolink/io_endpoint.h b/src/lib/asiolink/io_endpoint.h
index 37f9087..62b9e47 100644
--- a/src/lib/asiolink/io_endpoint.h
+++ b/src/lib/asiolink/io_endpoint.h
@@ -12,8 +12,8 @@
 // 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
+#ifndef __IO_ENDPOINT_H
+#define __IO_ENDPOINT_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.
@@ -115,7 +115,7 @@ public:
 };
 
 }      // asiolink
-#endif // __IOENDPOINT_H
+#endif // __IO_ENDPOINT_H
 
 // Local Variables: 
 // mode: c++
diff --git a/src/lib/asiolink/io_error.h b/src/lib/asiolink/io_error.h
new file mode 100644
index 0000000..2869e0b
--- /dev/null
+++ b/src/lib/asiolink/io_error.h
@@ -0,0 +1,35 @@
+// 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 __IO_ERROR_H
+#define __IO_ERROR_H
+
+#include <exceptions/exceptions.h>
+
+namespace asiolink {
+
+/// \brief An exception that is thrown if an error occurs within the IO
+/// module.  This is mainly intended to be a wrapper exception class for
+/// ASIO specific exceptions.
+class IOError : public isc::Exception {
+public:
+    IOError(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
+
+}      // asiolink
+
+#endif // __IO_ERROR_H
diff --git a/src/lib/asiolink/io_fetch.cc b/src/lib/asiolink/io_fetch.cc
new file mode 100644
index 0000000..d1f722c
--- /dev/null
+++ b/src/lib/asiolink/io_fetch.cc
@@ -0,0 +1,193 @@
+// 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 <boost/bind.hpp>
+
+#include <dns/message.h>
+#include <dns/messagerenderer.h>
+#include <dns/opcode.h>
+#include <dns/rcode.h>
+#include <log/dummylog.h>
+
+#include <asio.hpp>
+#include <asiolink/io_fetch.h>
+
+using namespace asio;
+using namespace isc::dns;
+using namespace isc::log;
+using namespace std;
+
+namespace asiolink {
+
+/// IOFetch Constructor - just initialize the private data
+
+IOFetch::IOFetch(int protocol, IOService& service,
+    const isc::dns::Question& question, const IOAddress& address, uint16_t port,
+    isc::dns::OutputBufferPtr& buff, Callback* cb, int wait)
+    :
+    data_(new IOFetch::IOFetchData(protocol, service, question, address,
+        port, buff, cb, wait))
+{
+}
+
+/// The function operator is implemented with the "stackless coroutine"
+/// pattern; see internal/coroutine.h for details.
+
+void
+IOFetch::operator()(error_code ec, size_t length) {
+    if (ec || data_->stopped) {
+        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);
+            
+            // TODO: 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);
+
+            // As this is a new fetch, clear the amount of data received
+            data_->cumulative = 0;
+
+            dlog("Sending " + msg.toText() + " to " +
+                data_->remote->getAddress().toText());
+        }
+
+
+        // If we timeout, we stop, which will shutdown everything and
+        // cancel all other attempts to run inside the coroutine
+        if (data_->timeout != -1) {
+            data_->timer.expires_from_now(boost::posix_time::milliseconds(
+                data_->timeout));
+            data_->timer.async_wait(boost::bind(&IOFetch::stop, *this,
+                TIME_OUT));
+        }
+
+        // Open a connection to the target system.  For speed, if the operation
+        // was completed synchronously (i.e. UDP operation) we bypass the yield.
+        if (data_->socket->open(data_->remote.get(), *this)) {
+            CORO_YIELD;
+        }
+
+        // Begin an asynchronous send, and then yield.  When the send completes
+        // send completes, we will resume immediately after this point.
+        CORO_YIELD data_->socket->asyncSend(data_->msgbuf->getData(),
+            data_->msgbuf->getLength(), data_->remote.get(), *this);
+
+        // Now receive the response.  Since TCP may not receive the entire
+        // message in one operation, we need to loop until we have received
+        // it. (This can't be done within the asyncReceive() method because
+        // each I/O operation will be done asynchronously and between each one
+        // we need to yield ... and we *really* don't want to set up another
+        // coroutine within that method.)  So after each receive (and yield),
+        // we check if the operation is complete and if not, loop to read again.
+        do {
+            CORO_YIELD data_->socket->asyncReceive(data_->data.get(),
+                static_cast<size_t>(MAX_LENGTH), data_->cumulative,
+                data_->remote.get(), *this);
+        } while (!data_->socket->receiveComplete(data_->data.get(), length,
+            data_->cumulative));
+
+        // The message is not rendered yet, so we can't print it easily
+        dlog("Received response from " + data_->remote->getAddress().toText());
+
+        /// Copy the answer into the response buffer.  (TODO: If the
+        /// OutputBuffer object were made to meet the requirements of
+        /// a MutableBufferSequence, then it could be written to directly
+        /// by async_receive_from() and this additional copy step would
+        /// be unnecessary.)
+        data_->buffer->writeData(data_->data.get(), length);
+
+        // Finished with this socket, so close it.
+        data_->socket->close();
+
+        /// We are done
+        stop(SUCCESS);
+    }
+}
+
+// Function that stops the coroutine sequence.  It is called either when the
+// query finishes or when the timer times out.  Either way, it sets the
+// "stopped_" flag and cancels anything that is in progress.
+//
+// As the function may be entered multiple times as things wind down, the
+// stopped_ flag checks if stop() has already been called.  If it has,
+// subsequent calls are no-ops.
+
+void
+IOFetch::stop(Result result) {
+
+    if (!data_->stopped) {
+
+        // Mark the fetch as stopped to prevent other completion callbacks
+        // (invoked because of the calls to cancel()) from executing the
+        // cancel calls again.
+        //
+        // In a single threaded environment, the callbacks won't be invoked
+        // until this one completes. In a multi-threaded environment, they may
+        // well be, in which case the testing (and setting) of the stopped_
+        // variable should be done inside a mutex (and the stopped_ variable
+        // declared as "volatile").
+        //
+        // TODO: Update testing of stopped_ if threads are used.
+        data_->stopped = true;
+
+        switch (result) {
+            case TIME_OUT:
+                dlog("Query timed out");
+                break;
+
+            case STOPPED:
+                dlog("Query stopped");
+                break;
+
+            default:
+                ;
+        }
+
+        // Stop requested, cancel and I/O's on the socket and shut it down,
+        // and cancel the timer.
+        data_->socket->cancel();
+        data_->socket->close();
+
+        data_->timer.cancel();
+
+        // Execute the I/O completion callback (if present).
+        if (data_->callback) {
+            (*(data_->callback))(result);
+        }
+
+        // Mark that stop() has now been called.
+
+    }
+}
+
+} // namespace asiolink
+
diff --git a/src/lib/asiolink/io_fetch.h b/src/lib/asiolink/io_fetch.h
new file mode 100644
index 0000000..8158c6c
--- /dev/null
+++ b/src/lib/asiolink/io_fetch.h
@@ -0,0 +1,226 @@
+// 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 __IO_FETCH_H
+#define __IO_FETCH_H 1
+
+#include <config.h>
+
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <unistd.h>             // for some IPC/network system calls
+
+#include <boost/shared_array.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/date_time/posix_time/posix_time_types.hpp>
+#include <asio/deadline_timer.hpp>
+
+#include <coroutine.h>
+
+#include <dns/buffer.h>
+#include <dns/question.h>
+
+#include <asiolink/io_asio_socket.h>
+#include <asiolink/io_endpoint.h>
+#include <asiolink/io_service.h>
+#include <asiolink/tcp_socket.h>
+#include <asiolink/tcp_endpoint.h>
+#include <asiolink/udp_socket.h>
+#include <asiolink/udp_endpoint.h>
+
+
+namespace asiolink {
+
+
+/// \brief Upstream Fetch Processing
+///
+/// IOFetch is the class used to send upstream fetches and to handle responses.
+///
+/// \param E Endpoint type to use.
+
+class IOFetch : public coroutine {
+public:
+
+    /// \brief Result of Upstream Fetch
+    ///
+    /// Note that this applies to the status of I/Os in the fetch - a fetch
+    /// that resulted in a packet being received from the server is a SUCCESS,
+    /// even if the contents of the packet indicate that some error occurred.
+    enum Result {
+        SUCCESS = 0,        ///< Success, fetch completed
+        TIME_OUT,           ///< Failure, fetch timed out
+        STOPPED,            ///< Control code, fetch has been stopped
+        NOTSET              ///< For testing, indicates value not set
+    };
+
+    // The next enum is a "trick" to allow constants to be defined in a class
+    // declaration.
+
+    /// \brief Integer Constants
+    enum {
+        MAX_LENGTH = 4096   ///< Maximum size of receive buffer
+    };
+
+    /// \brief I/O Fetch Callback
+    ///
+    /// Class of callback object for when the fetch itself has completed - an
+    /// object of this class is passed to the IOFetch constructor and its
+    /// operator() method called when the fetch completes.
+    ///
+    /// Note the difference between the two operator() methods:
+    /// - IOFetch::operator() callback is called when an asynchronous I/O has
+    ///   completed.
+    /// - IOFetch::Callback::operator() is called when an upstream fetch - which
+    ///   may have involved several asynchronous I/O operations - has completed.
+    ///
+    /// This is an abstract class.
+    class Callback {
+    public:
+        /// \brief Default Constructor
+        Callback()
+        {}
+
+        /// \brief Virtual Destructor
+        virtual ~Callback()
+        {}
+
+        /// \brief Callback method called when the fetch completes
+        ///
+        /// \brief result Result of the fetch
+        virtual void operator()(Result result) = 0;
+    };
+
+    /// \brief IOFetch Data
+    ///
+    /// The data for IOFetch is held in a separate struct pointed to by a
+    /// shared_ptr object.  This is because the IOFetch object will be copied
+    /// often (it is used as a coroutine and passed as callback to many
+    /// async_*() functions) and we want keep the same data).  Organising the
+    /// data in this way keeps copying to a minimum.
+    struct IOFetchData {
+
+        // The next two members are shared pointers to a base class because what
+        // is actually instantiated depends on whether the fetch is over UDP or
+        // TCP, which is not known until construction of the IOFetch.  Use of
+        // a shared pointer here is merely to ensure deletion when the data
+        // object is deleted.
+        boost::shared_ptr<IOAsioSocket<IOFetch> > socket;
+                                                ///< Socket to use for I/O
+        boost::shared_ptr<IOEndpoint> remote;   ///< Where the fetch was sent
+        isc::dns::Question          question;   ///< Question to be asked
+        isc::dns::OutputBufferPtr   msgbuf;     ///< Wire buffer for question
+        isc::dns::OutputBufferPtr   buffer;     ///< Received data held here
+        boost::shared_array<char>   data;       ///< Temporary array for data
+        IOFetch::Callback*          callback;   ///< Called on I/O Completion
+        size_t                      cumulative; ///< Cumulative received amount
+        bool                        stopped;    ///< Have we stopped running?
+        asio::deadline_timer        timer;      ///< Timer to measure timeouts
+        int                         timeout;    ///< Timeout in ms
+
+        /// \brief Constructor
+        ///
+        /// Just fills in the data members of the IOFetchData structure
+        ///
+        /// \param protocol either IPPROTO_UDP or IPPROTO_TCP
+        /// \param service I/O Service object to handle the asynchronous
+        ///     operations.
+        /// \param query DNS question to send to the upstream server.
+        /// \param address IP address of upstream server
+        /// \param port Port to use for the query
+        /// \param buff Output buffer into which the response (in wire format)
+        ///     is written (if a response is received).
+        /// \param cb Callback object containing the callback to be called
+        ///     when we terminate.  The caller is responsible for managing this
+        ///     object and deleting it if necessary.
+        /// \param wait Timeout for the fetch (in ms).
+        ///
+        /// TODO: May need to alter constructor (see comment 4 in Trac ticket #554)
+        IOFetchData(int protocol, IOService& service,
+            const isc::dns::Question& query, const IOAddress& address,
+            uint16_t port, isc::dns::OutputBufferPtr& buff, Callback* cb,
+            int wait)
+            :
+            socket((protocol == IPPROTO_UDP) ?
+                static_cast<IOAsioSocket<IOFetch>*>(
+                    new UDPSocket<IOFetch>(service)) :
+                static_cast<IOAsioSocket<IOFetch>*>(
+                    new TCPSocket<IOFetch>(service))
+                ),
+            remote((protocol == IPPROTO_UDP) ?
+                static_cast<IOEndpoint*>(new UDPEndpoint(address, port)) :
+                static_cast<IOEndpoint*>(new TCPEndpoint(address, port))
+                ),
+            question(query),
+            msgbuf(new isc::dns::OutputBuffer(512)),
+            buffer(buff),
+            data(new char[IOFetch::MAX_LENGTH]),
+            callback(cb),
+            cumulative(0),
+            stopped(false),
+            timer(service.get_io_service()),
+            timeout(wait)
+        {}
+    };
+
+    /// \brief Constructor.
+    ///
+    /// Creates the object that will handle the upstream fetch.
+    ///
+    /// TODO: Need to randomise the source port
+    ///
+    /// \param protocol Fetch protocol, either IPPROTO_UDP or IPPROTO_TCP
+    /// \param service I/O Service object to handle the asynchronous
+    ///     operations.
+    /// \param question DNS question to send to the upstream server.
+    /// \param buff Output buffer into which the response (in wire format)
+    ///     is written (if a response is received).
+    /// \param cb Callback object containing the callback to be called
+    ///     when we terminate.  The caller is responsible for managing this
+    ///     object and deleting it if necessary.
+    /// \param address IP address of upstream server
+    /// \param port Port to which to connect on the upstream server
+    /// (default = 53)
+    /// \param wait Timeout for the fetch (in ms).  The default value of
+    ///     -1 indicates no timeout.
+    IOFetch(int protocol, IOService& service,
+        const isc::dns::Question& question, const IOAddress& address,
+        uint16_t port, isc::dns::OutputBufferPtr& buff, Callback* cb,
+        int wait = -1);
+    
+    /// \brief Coroutine entry point
+    ///
+    /// The operator() method is the method in which the coroutine code enters
+    /// this object when an operation has been completed.
+    ///
+    /// \param ec Error code, the result of the last asynchronous I/O operation.
+    /// \param length Amount of data received on the last asynchronous read
+    void operator()(asio::error_code ec = asio::error_code(),
+        size_t length = 0);
+
+    /// \brief Terminate query
+    ///
+    /// This method can be called at any point.  It terminates the current
+    /// query with the specified reason.
+    ///
+    /// \param reason Reason for terminating the query
+    void stop(Result reason = STOPPED);
+
+private:
+    boost::shared_ptr<IOFetchData>  data_;   ///< Private data
+
+};
+
+} // namespace asiolink
+
+#endif // __IO_FETCH_H
diff --git a/src/lib/asiolink/io_message.h b/src/lib/asiolink/io_message.h
index 7d7d56c..532f449 100644
--- a/src/lib/asiolink/io_message.h
+++ b/src/lib/asiolink/io_message.h
@@ -12,8 +12,8 @@
 // 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
+#ifndef __IO_MESSAGE_H
+#define __IO_MESSAGE_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.
@@ -46,6 +46,7 @@ class IOMessage {
     ///
     /// \name Constructors and Destructor
     ///
+
     /// Note: The copy constructor and the assignment operator are
     /// intentionally defined as private, making this class non-copyable.
     //@{
@@ -96,7 +97,7 @@ private:
 
 
 }      // asiolink
-#endif // __IOMESSAGE_H
+#endif // __IO_MESSAGE_H
 
 // Local Variables: 
 // mode: c++
diff --git a/src/lib/asiolink/io_service.cc b/src/lib/asiolink/io_service.cc
index 8d96da9..55fc4b3 100644
--- a/src/lib/asiolink/io_service.cc
+++ b/src/lib/asiolink/io_service.cc
@@ -11,13 +11,14 @@
 // LOSS OF USE, DATA OR PROFITS, WHETHER 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 <netinet/in.h>
+#include <sys/socket.h>
+#include <unistd.h>             // for some IPC/network system calls
 
-#include <asio.hpp>
+#include <config.h>
 
+#include <asio.hpp>
 #include <asiolink/io_service.h>
 
 namespace asiolink {
diff --git a/src/lib/asiolink/io_socket.h b/src/lib/asiolink/io_socket.h
index df37d71..bebc8b6 100644
--- a/src/lib/asiolink/io_socket.h
+++ b/src/lib/asiolink/io_socket.h
@@ -12,8 +12,8 @@
 // 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
+#ifndef __IO_SOCKET_H
+#define __IO_SOCKET_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.
@@ -119,9 +119,6 @@ public:
     static IOSocket& getDummyTCPSocket();
 };
 
-}      // asiolink
-#endif // __IOSOCKET_H
+} // namespace asiolink
 
-// Local Variables: 
-// mode: c++
-// End: 
+#endif // __IO_SOCKET_H
diff --git a/src/lib/asiolink/recursive_query.cc b/src/lib/asiolink/recursive_query.cc
index 231be67..0bdf24e 100644
--- a/src/lib/asiolink/recursive_query.cc
+++ b/src/lib/asiolink/recursive_query.cc
@@ -12,28 +12,30 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-#include <config.h>
-
+#include <netinet/in.h>
 #include <stdlib.h>
+#include <sys/socket.h>
+#include <unistd.h>             // for some IPC/network system calls
 
-// unistd is needed for asio.hpp with SunStudio
-#include <unistd.h>
-
-#include <asio.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/bind.hpp>
 
-#include <asiolink/recursive_query.h>
-#include <asiolink/dns_service.h>
-#include <asiolink/udp_query.h>
+#include <config.h>
 
 #include <log/dummylog.h>
 
-#include <boost/lexical_cast.hpp>
-#include <boost/bind.hpp>
-
 #include <dns/question.h>
 #include <dns/message.h>
+#include <dns/opcode.h>
 
 #include <resolve/resolve.h>
+#include <cache/resolver_cache.h>
+
+#include <asio.hpp>
+#include <asiolink/dns_service.h>
+#include <asiolink/io_fetch.h>
+#include <asiolink/io_service.h>
+#include <asiolink/recursive_query.h>
 
 using isc::log::dlog;
 using namespace isc::dns;
@@ -68,10 +70,10 @@ typedef std::pair<std::string, uint16_t> addr_t;
  *
  * Used by RecursiveQuery::sendQuery.
  */
-class RunningQuery : public UDPQuery::Callback {
+class RunningQuery : public IOFetch::Callback {
 private:
     // The io service to handle async calls
-    asio::io_service& io_;
+    IOService& io_;
 
     // Info for (re)sending the query (the question and destination)
     Question question_;
@@ -123,7 +125,7 @@ private:
     asio::deadline_timer lookup_timer;
 
     size_t queries_out_;
-
+    
     // If we timed out ourselves (lookup timeout), stop issuing queries
     bool done_;
 
@@ -132,6 +134,26 @@ private:
     // answer if we do find one later (or if we have a lookup_timeout)
     bool answer_sent_;
 
+    // Reference to our cache
+    isc::cache::ResolverCache& cache_;
+
+    // perform a single lookup; first we check the cache to see
+    // if we have a response for our query stored already. if
+    // so, call handlerecursiveresponse(), if not, we call send()
+    void doLookup() {
+        dlog("doLookup: try cache");
+        Message cached_message(Message::RENDER);
+        isc::resolve::initResponseMessage(question_, cached_message);
+        if (cache_.lookup(question_.getName(), question_.getType(),
+                          question_.getClass(), cached_message)) {
+            dlog("Message found in cache, returning that");
+            handleRecursiveAnswer(cached_message);
+        } else {
+            send();
+        }
+        
+    }
+
     // (re)send the query to the server.
     void send() {
         const int uc = upstream_->size();
@@ -141,22 +163,22 @@ private:
             int serverIndex = rand() % uc;
             dlog("Sending upstream query (" + question_.toText() +
                 ") to " + upstream_->at(serverIndex).first);
-            UDPQuery query(io_, question_,
+            IOFetch query(IPPROTO_UDP, io_, question_,
                 upstream_->at(serverIndex).first,
                 upstream_->at(serverIndex).second, buffer_, this,
                 query_timeout_);
             ++queries_out_;
-            io_.post(query);
+            io_.get_io_service().post(query);
         } else if (zs > 0) {
             int serverIndex = rand() % zs;
             dlog("Sending query to zone server (" + question_.toText() +
                 ") to " + zone_servers_.at(serverIndex).first);
-            UDPQuery query(io_, question_,
+            IOFetch query(IPPROTO_UDP, io_, question_,
                 zone_servers_.at(serverIndex).first,
                 zone_servers_.at(serverIndex).second, buffer_, this,
                 query_timeout_);
             ++queries_out_;
-            io_.post(query);
+            io_.get_io_service().post(query);
         } else {
             dlog("Error, no upstream servers to send to.");
         }
@@ -184,6 +206,11 @@ private:
                 question_, incoming, cname_target, cname_count_, true);
 
         bool found_ns_address = false;
+            
+        // If the packet is OK, store it in the cache
+        if (!isc::resolve::ResponseClassifier::error(category)) {
+            cache_.update(incoming);
+        }
 
         switch (category) {
         case isc::resolve::ResponseClassifier::ANSWER:
@@ -213,7 +240,7 @@ private:
                                  question_.getType());
 
             dlog("Following CNAME chain to " + question_.toText());
-            send();
+            doLookup();
             return false;
             break;
         case isc::resolve::ResponseClassifier::NXDOMAIN:
@@ -245,11 +272,16 @@ private:
                         // TODO should use NSAS
                         zone_servers_.push_back(addr_t(addr_str, 53));
                         found_ns_address = true;
+                        break;
                     }
                 }
             }
             if (found_ns_address) {
                 // next resolver round
+                // we do NOT use doLookup() here, but send() (i.e. we
+                // skip the cache), since if we had the final answer
+                // instead of a delegation cached, we would have been
+                // there by now.
                 send();
                 return false;
             } else {
@@ -283,7 +315,7 @@ private:
     }
     
 public:
-    RunningQuery(asio::io_service& io,
+    RunningQuery(IOService& io,
         const Question &question,
         MessagePtr answer_message,
         boost::shared_ptr<AddressVector> upstream,
@@ -291,7 +323,8 @@ public:
         OutputBufferPtr buffer,
         isc::resolve::ResolverInterface::CallbackPtr cb,
         int query_timeout, int client_timeout, int lookup_timeout,
-        unsigned retries) :
+        unsigned retries,
+        isc::cache::ResolverCache& cache) :
         io_(io),
         question_(question),
         answer_message_(answer_message),
@@ -302,11 +335,12 @@ public:
         cname_count_(0),
         query_timeout_(query_timeout),
         retries_(retries),
-        client_timer(io),
-        lookup_timer(io),
+        client_timer(io.get_io_service()),
+        lookup_timer(io.get_io_service()),
         queries_out_(0),
         done_(false),
-        answer_sent_(false)
+        answer_sent_(false),
+        cache_(cache)
     {
         // Setup the timer to stop trying (lookup_timeout)
         if (lookup_timeout >= 0) {
@@ -328,7 +362,7 @@ public:
             setZoneServersToRoot();
         }
 
-        send();
+        doLookup();
     }
 
     void setZoneServersToRoot() {
@@ -369,6 +403,26 @@ public:
         done_ = true;
         if (resume && !answer_sent_) {
             answer_sent_ = true;
+
+            // There are two types of messages we could store in the
+            // cache;
+            // 1. answers to our fetches from authoritative servers,
+            //    exactly as we receive them, and
+            // 2. answers to queries we received from clients, which
+            //    have received additional processing (following CNAME
+            //    chains, for instance)
+            //
+            // Doing only the first would mean we would have to re-do
+            // processing when we get data from our cache, and doing
+            // only the second would miss out on the side-effect of
+            // having nameserver data in our cache.
+            //
+            // So right now we do both. Since the cache (currently)
+            // stores Messages on their question section only, this
+            // does mean that we overwrite the messages we stored in
+            // the previous iteration if we are following a delegation.
+            cache_.update(*answer_message_);
+
             resolvercallback_->success(answer_message_);
         } else {
             resolvercallback_->failure();
@@ -386,10 +440,10 @@ public:
     }
 
     // This function is used as callback from DNSQuery.
-    virtual void operator()(UDPQuery::Result result) {
+    virtual void operator()(IOFetch::Result result) {
         // XXX is this the place for TCP retry?
         --queries_out_;
-        if (!done_ && result != UDPQuery::TIME_OUT) {
+        if (!done_ && result != IOFetch::TIME_OUT) {
             // we got an answer
             Message incoming(Message::PARSE);
             InputBuffer ibuf(buffer_->getData(), buffer_->getLength());
@@ -423,15 +477,29 @@ void
 RecursiveQuery::resolve(const QuestionPtr& question,
     const isc::resolve::ResolverInterface::CallbackPtr callback)
 {
-    asio::io_service& io = dns_service_.get_io_service();
+    IOService& io = dns_service_.getIOService();
 
     MessagePtr answer_message(new Message(Message::RENDER));
+    isc::resolve::initResponseMessage(*question, *answer_message);
+
     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_);
+
+    dlog("Try out cache first (direct call to resolve)");
+    // First try to see if we have something cached in the messagecache
+    if (cache_.lookup(question->getName(), question->getType(),
+                      question->getClass(), *answer_message)) {
+        dlog("Message found in cache, returning that");
+        // TODO: err, should cache set rcode as well?
+        answer_message->setRcode(Rcode::NOERROR());
+        callback->success(answer_message);
+    } else {
+        dlog("Message not found in cache, starting recursive query");
+        // It will delete itself when it is done
+        new RunningQuery(io, *question, answer_message, upstream_,
+                         upstream_root_, buffer, callback, query_timeout_,
+                         client_timeout_, lookup_timeout_, retries_,
+                         cache_);
+    }
 }
 
 void
@@ -444,15 +512,30 @@ RecursiveQuery::resolve(const Question& question,
     // 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();
+    IOService& io = dns_service_.getIOService();
 
     isc::resolve::ResolverInterface::CallbackPtr crs(
         new isc::resolve::ResolverCallbackServer(server));
+
+    // TODO: general 'prepareinitialanswer'
+    answer_message->setOpcode(isc::dns::Opcode::QUERY());
+    answer_message->addQuestion(question);
     
-    // 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_);
+    // First try to see if we have something cached in the messagecache
+    dlog("Try out cache first (started by incoming event)");
+    if (cache_.lookup(question.getName(), question.getType(),
+                      question.getClass(), *answer_message)) {
+        dlog("Message found in cache, returning that");
+        // TODO: err, should cache set rcode as well?
+        answer_message->setRcode(Rcode::NOERROR());
+        crs->success(answer_message);
+    } else {
+        dlog("Message not found in cache, starting recursive query");
+        // It will delete itself when it is done
+        new RunningQuery(io, question, answer_message, upstream_, upstream_root_,
+                             buffer, crs, query_timeout_, client_timeout_,
+                             lookup_timeout_, retries_, cache_);
+    }
 }
 
 
diff --git a/src/lib/asiolink/recursive_query.h b/src/lib/asiolink/recursive_query.h
index 0660c09..6ef0069 100644
--- a/src/lib/asiolink/recursive_query.h
+++ b/src/lib/asiolink/recursive_query.h
@@ -18,6 +18,7 @@
 #include <asiolink/dns_service.h>
 #include <asiolink/dns_server.h>
 #include <dns/buffer.h>
+#include <cache/resolver_cache.h>
 
 namespace asiolink {
 /// \brief The \c RecursiveQuery class provides a layer of abstraction around
@@ -107,6 +108,9 @@ private:
     int client_timeout_;
     int lookup_timeout_;
     unsigned retries_;
+    // Cache. TODO: I think we want this initialized in Resolver class,
+    // not here
+    isc::cache::ResolverCache cache_;
 };
 
 }      // namespace asiolink
diff --git a/src/lib/asiolink/tcp_server.cc b/src/lib/asiolink/tcp_server.cc
index 8fb8c84..e77828c 100644
--- a/src/lib/asiolink/tcp_server.cc
+++ b/src/lib/asiolink/tcp_server.cc
@@ -14,18 +14,18 @@
 
 #include <config.h>
 
-#include <boost/shared_array.hpp>
-
-// unistd is needed for asio.hpp with SunStudio
-#include <unistd.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <unistd.h>             // for some IPC/network system calls
 
-#include <asio.hpp>
+#include <boost/shared_array.hpp>
 
 #include <log/dummylog.h>
 
+#include <asio.hpp>
+#include <asiolink/dummy_io_cb.h>
 #include <asiolink/tcp_endpoint.h>
 #include <asiolink/tcp_socket.h>
-
 #include <asiolink/tcp_server.h>
 
 
@@ -125,7 +125,14 @@ TCPServer::operator()(error_code ec, size_t length) {
         // 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_));
+
+        // The TCP socket class has been extended with asynchronous functions
+        // and takes as a template parameter a completion callback class.  As
+        // TCPServer does not use these extended functions (only those defined
+        // in the IOSocket base class) - but needs a TCPSocket to get hold of
+        // the underlying Boost TCP socket - DummyIOCallback is used.  This
+        // provides the appropriate operator() but is otherwise functionless.
+        iosock_.reset(new TCPSocket<DummyIOCallback>(*socket_));
         io_message_.reset(new IOMessage(data_.get(), length, *iosock_, *peer_));
         bytes_ = length;
 
diff --git a/src/lib/asiolink/tcp_socket.h b/src/lib/asiolink/tcp_socket.h
index 03713a5..5a85aaa 100644
--- a/src/lib/asiolink/tcp_socket.h
+++ b/src/lib/asiolink/tcp_socket.h
@@ -19,34 +19,259 @@
 #error "asio.hpp must be included before including this, see asiolink.h as to why"
 #endif
 
-#include <asiolink/io_socket.h>
+#include <log/dummylog.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <unistd.h>             // for some IPC/network system calls
+
+#include <iostream>
+#include <cstddef>
+
+#include <config.h>
+
+#include <asiolink/io_asio_socket.h>
+#include <asiolink/io_endpoint.h>
+#include <asiolink/io_service.h>
+#include <asiolink/tcp_endpoint.h>
 
 namespace asiolink {
 
-/// \brief The \c TCPSocket class is a concrete derived class of
-/// \c IOSocket that represents a TCP socket.
+/// \brief The \c TCPSocket class is a concrete derived class of \c IOAsioSocket
+/// 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 {
+/// \param C Callback type
+template <typename C>
+class TCPSocket : public IOAsioSocket<C> {
 private:
-    TCPSocket(const TCPSocket& source);
-    TCPSocket& operator=(const TCPSocket& source);
+    /// \brief Class is non-copyable
+    TCPSocket(const TCPSocket&);
+    TCPSocket& operator=(const TCPSocket&);
+
 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) {}
+    /// \param socket The ASIO representation of the TCP socket.  It
+    /// is assumed that the caller will open and close the socket, so
+    /// these operations are a no-op for that socket.
+    TCPSocket(asio::ip::tcp::socket& socket);
+
+    /// \brief Constructor
+    ///
+    /// Used when the TCPSocket is being asked to manage its own internal
+    /// socket.  It is assumed that open() and close() will not be used.
+    ///
+    /// \param service I/O Service object used to manage the socket.
+    TCPSocket(IOService& service);
+
+    /// \brief Destructor
+    virtual ~TCPSocket();
+
+    virtual int getNative() const { return (socket_.native()); }
+    virtual int getProtocol() const { return (IPPROTO_TCP); }
+
+    /// \brief Open Socket
+    ///
+    /// Opens the TCP socket.  In the model for transport-layer agnostic I/O,
+    /// an "open" operation includes a connection to the remote end (which
+    /// may take time).  This does not happen for TCP, so the method returns
+    /// "false" to indicate that the operation completed synchronously.
+    ///
+    /// \param endpoint Endpoint to which the socket will connect to.
+    /// \param callback Unused.
+    ///
+    /// \return false to indicate that the "operation" completed synchronously.
+    virtual bool open(const IOEndpoint* endpoint, C&);
+
+    /// \brief Send Asynchronously
+    ///
+    /// This corresponds to async_send_to() for TCP sockets and async_send()
+    /// for TCP.  In both cases an endpoint argument is supplied indicating the
+    /// target of the send - this is ignored for TCP.
+    ///
+    /// \param data Data to send
+    /// \param length Length of data to send
+    /// \param endpoint Target of the send
+    /// \param callback Callback object.
+    virtual void asyncSend(const void* data, size_t length,
+        const IOEndpoint* endpoint, C& callback);
+
+    /// \brief Receive Asynchronously
+    ///
+    /// This correstponds to async_receive_from() for TCP sockets and
+    /// async_receive() for TCP.  In both cases, an endpoint argument is
+    /// supplied to receive the source of the communication.  For TCP it will
+    /// be filled in with details of the connection.
+    ///
+    /// \param data Buffer to receive incoming message
+    /// \param length Length of the data buffer
+    /// \param cumulative Amount of data that should already be in the buffer.
+    /// (This is ignored - every UPD receive fills the buffer from the start.)
+    /// \param endpoint Source of the communication
+    /// \param callback Callback object
+    virtual void asyncReceive(void* data, size_t length, size_t cumulative,
+        IOEndpoint* endpoint, C& callback);
+
+    /// \brief Checks if the data received is complete.
+    ///
+    /// As all the data is received in one I/O, so this is, this is effectively
+    /// a no-op (although it does update the amount of data received).
+    ///
+    /// \param data Data buffer containing data to date.  (This is ignored
+    /// for TCP receives.)
+    /// \param length Amount of data received in last asynchronous I/O
+    /// \param cumulative On input, amount of data received before the last
+    /// I/O.  On output, the total amount of data received to date.
+    ///
+    /// \return true if the receive is complete, false if another receive is
+    /// needed.
+    virtual bool receiveComplete(void*, size_t length, size_t& cumulative) {
+        cumulative = length;
+        return (true);
+    }
+
+    /// \brief Cancel I/O On Socket
+    virtual void cancel();
+
+    /// \brief Close socket
+    virtual void close();
 
-    int getNative() const { return (socket_.native()); }
-    int getProtocol() const { return (IPPROTO_TCP); }
 
 private:
-    asio::ip::tcp::socket& socket_;
+    // Two variables to hold the socket - a socket and a pointer to it.  This
+    // handles the case where a socket is passed to the TCPSocket on
+    // construction, or where it is asked to manage its own socket.
+    asio::ip::tcp::socket*      socket_ptr_;    ///< Pointer to own socket
+    asio::ip::tcp::socket&      socket_;        ///< Socket
+    bool                        isopen_;        ///< true when socket is open
 };
 
+// Constructor - caller manages socket
+
+template <typename C>
+TCPSocket<C>::TCPSocket(asio::ip::tcp::socket& socket) :
+    socket_ptr_(NULL), socket_(socket), isopen_(true)
+{
+}
+
+// Constructor - create socket on the fly
+
+template <typename C>
+TCPSocket<C>::TCPSocket(IOService& service) :
+    socket_ptr_(new asio::ip::tcp::socket(service.get_io_service())),
+    socket_(*socket_ptr_), isopen_(false)
+{
+}
+
+// Destructor.  Only delete the socket if we are managing it.
+
+template <typename C>
+TCPSocket<C>::~TCPSocket()
+{
+    delete socket_ptr_;
+}
+
+// Open the socket.  Throws an error on failure
+// TODO: Make the open more resilient
+
+template <typename C> bool
+TCPSocket<C>::open(const IOEndpoint* endpoint, C&) {
+
+    // Ignore opens on already-open socket.  Don't throw a failure because
+    // of uncertainties as to what precedes whan when using asynchronous I/O.
+    // At also allows us a treat a passed-in socket as a self-managed socket.
+
+    if (!isopen_) {
+        if (endpoint->getFamily() == AF_INET) {
+            socket_.open(asio::ip::tcp::v4());
+        }
+        else {
+            socket_.open(asio::ip::tcp::v6());
+        }
+        isopen_ = true;
+
+        // TODO: Complete TCPSocket::open()
+
+    }
+    return (false);
+}
+
+// Send a message.  Should never do this if the socket is not open, so throw
+// an exception if this is the case.
+
+template <typename C> void
+TCPSocket<C>::asyncSend(const void* data, size_t length,
+    const IOEndpoint* endpoint, C& callback)
+{
+    if (isopen_) {
+
+        // Upconvert to a TCPEndpoint.  We need to do this because although
+        // IOEndpoint is the base class of TCPEndpoint and TCPEndpoint, it
+        // doing cont contain a method for getting at the underlying endpoint
+        // type - those are in the derived class and the two classes differ on
+        // return type.
+
+        assert(endpoint->getProtocol() == IPPROTO_TCP);
+        const TCPEndpoint* tcp_endpoint =
+            static_cast<const TCPEndpoint*>(endpoint);
+        std::cerr << "TCPSocket::asyncSend(): sending to " <<
+            tcp_endpoint->getAddress().toText() <<
+            ", port " << tcp_endpoint->getPort() << "\n";
+
+        // TODO: Complete TCPSocket::asyncSend()
+
+    } else {
+        isc_throw(SocketNotOpen,
+            "attempt to send on a TCP socket that is not open");
+    }
+}
+
+// Receive a message. Note that the "cumulative" argument is ignored - every TCP
+// receive is put into the buffer beginning at the start - there is no concept
+// receiving a subsequent part of a message.  Same critera as before concerning
+// the need for the socket to be open.
+
+template <typename C> void
+TCPSocket<C>::asyncReceive(void* data, size_t length, size_t,
+    IOEndpoint* endpoint, C& callback)
+{
+    if (isopen_) {
+
+        // Upconvert the endpoint again.
+        assert(endpoint->getProtocol() == IPPROTO_TCP);
+        const TCPEndpoint* tcp_endpoint =
+            static_cast<const TCPEndpoint*>(endpoint);
+        std::cerr << "TCPSocket::asyncReceive(): receiving from " <<
+            tcp_endpoint->getAddress().toText() <<
+            ", port " << tcp_endpoint->getPort() << "\n";
+
+        // TODO: Complete TCPSocket::asyncReceive()
+
+    } else {
+        isc_throw(SocketNotOpen,
+            "attempt to receive from a TCP socket that is not open");
+    }
+}
+
+// Cancel I/O on the socket.  No-op if the socket is not open.
+template <typename C> void
+TCPSocket<C>::cancel() {
+    if (isopen_) {
+        socket_.cancel();
+    }
+}
+
+// Close the socket down.  Can only do this if the socket is open and we are
+// managing it ourself.
+
+template <typename C> void
+TCPSocket<C>::close() {
+    if (isopen_ && socket_ptr_) {
+        socket_.close();
+        isopen_ = false;
+    }
+}
+
+} // namespace asiolink
 
-}      // namespace asiolink
 #endif // __TCP_SOCKET_H
diff --git a/src/lib/asiolink/tests/Makefile.am b/src/lib/asiolink/tests/Makefile.am
index 7fe268e..b4f0a87 100644
--- a/src/lib/asiolink/tests/Makefile.am
+++ b/src/lib/asiolink/tests/Makefile.am
@@ -15,24 +15,32 @@ CLEANFILES = *.gcno *.gcda
 TESTS =
 if HAVE_GTEST
 TESTS += run_unittests
-run_unittests_SOURCES = $(top_srcdir)/src/lib/dns/tests/unittest_util.h
+run_unittests_SOURCES  = run_unittests.cc
+run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.h
 run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
-run_unittests_SOURCES += 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_address_unittest.cc
+run_unittests_SOURCES += io_endpoint_unittest.cc
+run_unittests_SOURCES += io_fetch_unittest.cc
+run_unittests_SOURCES += io_socket_unittest.cc
 run_unittests_SOURCES += io_service_unittest.cc
 run_unittests_SOURCES += interval_timer_unittest.cc
 run_unittests_SOURCES += recursive_query_unittest.cc
-run_unittests_SOURCES += run_unittests.cc
+run_unittests_SOURCES += udp_endpoint_unittest.cc
+run_unittests_SOURCES += udp_socket_unittest.cc
+
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) $(LOG4CXX_LDFLAGS)
-run_unittests_LDADD = $(GTEST_LDADD)
+
+run_unittests_LDADD  = $(GTEST_LDADD)
 run_unittests_LDADD += $(SQLITE_LIBS)
 run_unittests_LDADD +=  $(top_builddir)/src/lib/dns/libdns++.la
 run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
 run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
+run_unittests_LDADD += $(top_builddir)/src/lib/cache/libcache.la
+run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
+
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+
 # Note: the ordering matters: -Wno-... must follow -Wextra (defined in
 # B10_CXXFLAGS)
 run_unittests_CXXFLAGS = $(AM_CXXFLAGS)
diff --git a/src/lib/asiolink/tests/interval_timer_unittest.cc b/src/lib/asiolink/tests/interval_timer_unittest.cc
index 9a7b0f4..a6793bf 100644
--- a/src/lib/asiolink/tests/interval_timer_unittest.cc
+++ b/src/lib/asiolink/tests/interval_timer_unittest.cc
@@ -15,6 +15,7 @@
 #include <config.h>
 #include <gtest/gtest.h>
 
+#include <asio.hpp>
 #include <asiolink/asiolink.h>
 
 #include <boost/date_time/posix_time/posix_time_types.hpp>
diff --git a/src/lib/asiolink/tests/io_address_unittest.cc b/src/lib/asiolink/tests/io_address_unittest.cc
new file mode 100644
index 0000000..894f143
--- /dev/null
+++ b/src/lib/asiolink/tests/io_address_unittest.cc
@@ -0,0 +1,63 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+#include <gtest/gtest.h>
+
+#include <asiolink/io_error.h>
+#include <asiolink/io_address.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"));
+}
+
+TEST(IOAddressTest, Family) {
+    EXPECT_EQ(AF_INET, IOAddress("192.0.2.1").getFamily());
+    EXPECT_EQ(AF_INET6, IOAddress("2001:0DB8:0:0::0012").getFamily());
+}
diff --git a/src/lib/asiolink/tests/io_endpoint_unittest.cc b/src/lib/asiolink/tests/io_endpoint_unittest.cc
new file mode 100644
index 0000000..534850a
--- /dev/null
+++ b/src/lib/asiolink/tests/io_endpoint_unittest.cc
@@ -0,0 +1,68 @@
+// 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/io_endpoint.h>
+#include <asiolink/io_error.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/io_fetch_unittest.cc b/src/lib/asiolink/tests/io_fetch_unittest.cc
new file mode 100644
index 0000000..57f61b2
--- /dev/null
+++ b/src/lib/asiolink/tests/io_fetch_unittest.cc
@@ -0,0 +1,188 @@
+// 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 <boost/bind.hpp>
+#include <cstdlib>
+#include <string>
+
+#include <string.h>
+
+#include <asio.hpp>
+
+#include <dns/buffer.h>
+#include <dns/question.h>
+#include <dns/message.h>
+#include <dns/messagerenderer.h>
+#include <dns/opcode.h>
+#include <dns/name.h>
+#include <dns/rcode.h>
+
+#include <asiolink/io_fetch.h>
+#include <asiolink/io_service.h>
+
+using namespace asio;
+using namespace isc::dns;
+using asio::ip::udp;
+
+namespace asiolink {
+
+const asio::ip::address TEST_HOST(asio::ip::address::from_string("127.0.0.1"));
+const uint16_t TEST_PORT(5301);
+// FIXME Shouldn't we send something that is real message?
+const char TEST_DATA[] = "TEST DATA";
+
+/// \brief Test fixture for the asiolink::IOFetch.
+class IOFetchTest : public virtual ::testing::Test, public virtual IOFetch::Callback
+{
+public:
+    IOService       service_;       ///< Service to run the query
+    IOFetch::Result expected_;      ///< Expected result of the callback
+    bool            run_;           ///< Did the callback run already?
+    Question        question_;      ///< What to ask
+    OutputBufferPtr buff_;          ///< Buffer to hold result
+    IOFetch         udp_fetch_;     ///< For UDP query test
+    //IOFetch         tcp_fetch_;     ///< For TCP query test
+
+    // The next member is the buffer iin which the "server" (implemented by the
+    // response handler method) receives the question sent by the fetch object.
+    char            server_buff_[512];  ///< Server buffer
+
+    /// \brief Constructor
+    IOFetchTest() :
+        service_(),
+        expected_(IOFetch::NOTSET),
+        run_(false),
+        question_(Name("example.net"), RRClass::IN(), RRType::A()),
+        buff_(new OutputBuffer(512)),
+        udp_fetch_(IPPROTO_UDP, service_, question_, IOAddress(TEST_HOST),
+            TEST_PORT, buff_, this, 100)
+        // tcp_fetch_(service_, question_, IOAddress(TEST_HOST), TEST_PORT,
+        //    buff_, this, 100, IPPROTO_UDP)
+        { }
+
+    /// \brief Fetch completion callback
+    ///
+    /// This is the callback's operator() method which is called when the fetch
+    /// is complete.  Check that the data received is the wire format of the
+    /// question, then send back an arbitrary response.
+    void operator()(IOFetch::Result result) {
+        EXPECT_EQ(expected_, result);   // Check correct result returned
+        EXPECT_FALSE(run_);             // Check it is run only once
+        run_ = true;                    // Note success
+        service_.stop();                // ... and exit run loop
+    }
+
+    /// \brief Response handler, pretending to be remote DNS server
+    ///
+    /// This checks that the data sent is what we expected to receive, and
+    /// sends back a test answer.
+    void respond(udp::endpoint* remote, udp::socket* socket,
+            asio::error_code ec = asio::error_code(), size_t length = 0) {
+
+        // Construct the data buffer for question we expect to receive.
+        OutputBuffer msgbuf(512);
+        Message msg(Message::RENDER);
+        msg.setQid(0);
+        msg.setOpcode(Opcode::QUERY());
+        msg.setRcode(Rcode::NOERROR());
+        msg.setHeaderFlag(Message::HEADERFLAG_RD);
+        msg.addQuestion(question_);
+        MessageRenderer renderer(msgbuf);
+        msg.toWire(renderer);
+
+        // The QID in the incoming data is random so set it to 0 for the
+        // data comparison check. (It was set to 0 when the buffer containing
+        // the expected data was constructed above.)
+        server_buff_[0] = server_buff_[1] = 0;
+
+        // Check that lengths are identical.
+        EXPECT_EQ(msgbuf.getLength(), length);
+        EXPECT_TRUE(memcmp(msgbuf.getData(), server_buff_, length) == 0);
+
+        // ... and return a message back.
+        socket->send_to(asio::buffer(TEST_DATA, sizeof TEST_DATA), *remote);
+    }
+};
+
+
+/// Test that when we run the query and stop it after it was run,
+/// it returns "stopped" correctly.
+///
+/// That is why stop() is posted to the service_ as well instead
+/// of calling it.
+TEST_F(IOFetchTest, UdpStop) {
+    expected_ = IOFetch::STOPPED;
+
+    // Post the query
+    service_.get_io_service().post(udp_fetch_);
+
+    // Post query_.stop() (yes, the boost::bind thing is just
+    // query_.stop()).
+    service_.get_io_service().post(
+       boost::bind(&IOFetch::stop, udp_fetch_, IOFetch::STOPPED));
+
+    // Run both of them.  run() returns when everything in the I/O service
+    // queue has completed.
+    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(IOFetchTest, UdpPrematureStop) {
+    expected_ = IOFetch::STOPPED;
+
+    // Stop before it is started
+    udp_fetch_.stop();
+    service_.get_io_service().post(udp_fetch_);
+
+    service_.run();
+    EXPECT_TRUE(run_);
+}
+
+// Test that it will timeout when no answer arrives.
+TEST_F(IOFetchTest, UdpTimeout) {
+    expected_ = IOFetch::TIME_OUT;
+
+    service_.get_io_service().post(udp_fetch_);
+    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 the loopback address.
+TEST_F(IOFetchTest, UdpReceive) {
+    expected_ = IOFetch::SUCCESS;
+
+    udp::socket socket(service_.get_io_service(), udp::v4());
+    socket.set_option(socket_base::reuse_address(true));
+    socket.bind(udp::endpoint(TEST_HOST, TEST_PORT));
+
+    udp::endpoint remote;
+    socket.async_receive_from(asio::buffer(server_buff_, sizeof(server_buff_)),
+        remote,
+        boost::bind(&IOFetchTest::respond, this, &remote, &socket, _1, _2));
+    service_.get_io_service().post(udp_fetch_);
+    service_.run();
+
+    socket.close();
+
+    EXPECT_TRUE(run_);
+    ASSERT_EQ(sizeof TEST_DATA, buff_->getLength());
+    EXPECT_EQ(0, memcmp(TEST_DATA, buff_->getData(), sizeof TEST_DATA));
+}
+
+} // namespace asiolink
diff --git a/src/lib/asiolink/tests/io_service_unittest.cc b/src/lib/asiolink/tests/io_service_unittest.cc
index 49aa67e..28924d4 100644
--- a/src/lib/asiolink/tests/io_service_unittest.cc
+++ b/src/lib/asiolink/tests/io_service_unittest.cc
@@ -15,6 +15,7 @@
 #include <config.h>
 #include <gtest/gtest.h>
 
+#include <asio.hpp>
 #include <asiolink/asiolink.h>
 
 using namespace asiolink;
diff --git a/src/lib/asiolink/tests/io_socket_unittest.cc b/src/lib/asiolink/tests/io_socket_unittest.cc
new file mode 100644
index 0000000..6538550
--- /dev/null
+++ b/src/lib/asiolink/tests/io_socket_unittest.cc
@@ -0,0 +1,32 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+#include <gtest/gtest.h>
+
+#include <netinet/in.h>
+
+#include <asio.hpp>
+#include <asiolink/io_socket.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/ioaddress_unittest.cc b/src/lib/asiolink/tests/ioaddress_unittest.cc
deleted file mode 100644
index 57c9496..0000000
--- a/src/lib/asiolink/tests/ioaddress_unittest.cc
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <config.h>
-#include <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
deleted file mode 100644
index a5b5cd2..0000000
--- a/src/lib/asiolink/tests/ioendpoint_unittest.cc
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <config.h>
-#include <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
deleted file mode 100644
index 06ae861..0000000
--- a/src/lib/asiolink/tests/iosocket_unittest.cc
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <config.h>
-#include <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
index 0fd7e62..9f23000 100644
--- a/src/lib/asiolink/tests/recursive_query_unittest.cc
+++ b/src/lib/asiolink/tests/recursive_query_unittest.cc
@@ -41,8 +41,13 @@
 // 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/recursive_query.h>
 #include <asiolink/io_socket.h>
+#include <asiolink/io_service.h>
+#include <asiolink/io_message.h>
+#include <asiolink/io_error.h>
+#include <asiolink/dns_lookup.h>
+#include <asiolink/simple_callback.h>
 
 using isc::UnitTestUtil;
 using namespace std;
@@ -672,7 +677,7 @@ TEST_F(RecursiveQueryTest, forwardClientTimeout) {
     RecursiveQuery query(*dns_service_,
                          singleAddress(TEST_IPV4_ADDR, port),
                          singleAddress(TEST_IPV4_ADDR, port),
-                         50, 120, 1000, 4);
+                         200, 480, 4000, 4);
     Question question(Name("example.net"), RRClass::IN(), RRType::A());
     OutputBufferPtr buffer(new OutputBuffer(0));
     query.resolve(question, answer, buffer, &server);
@@ -716,7 +721,7 @@ TEST_F(RecursiveQueryTest, forwardLookupTimeout) {
     RecursiveQuery query(*dns_service_,
                          singleAddress(TEST_IPV4_ADDR, port),
                          singleAddress(TEST_IPV4_ADDR, port),
-                         50, 4000, 120, 5);
+                         200, 4000, 480, 5);
     Question question(Name("example.net"), RRClass::IN(), RRType::A());
     OutputBufferPtr buffer(new OutputBuffer(0));
     query.resolve(question, answer, buffer, &server);
diff --git a/src/lib/asiolink/tests/udp_endpoint_unittest.cc b/src/lib/asiolink/tests/udp_endpoint_unittest.cc
new file mode 100644
index 0000000..18135ec
--- /dev/null
+++ b/src/lib/asiolink/tests/udp_endpoint_unittest.cc
@@ -0,0 +1,55 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include <asio.hpp>
+#include <asiolink/io_address.h>
+#include <asiolink/udp_endpoint.h>
+
+using namespace asiolink;
+using namespace std;
+
+// This test checks that the endpoint can manage its own internal
+// asio::ip::udp::endpoint object.
+
+TEST(UDPEndpointTest, v4Address) {
+    const string test_address("192.0.2.1");
+    const unsigned short test_port = 5301;
+
+    IOAddress address(test_address);
+    UDPEndpoint endpoint(address, test_port);
+
+    EXPECT_TRUE(address == endpoint.getAddress());
+    EXPECT_EQ(test_port, endpoint.getPort());
+    EXPECT_EQ(IPPROTO_UDP, endpoint.getProtocol());
+    EXPECT_EQ(AF_INET, endpoint.getFamily());
+}
+
+TEST(UDPEndpointTest, v6Address) {
+    const string test_address("2001:db8::1235");
+    const unsigned short test_port = 5302;
+
+    IOAddress address(test_address);
+    UDPEndpoint endpoint(address, test_port);
+
+    EXPECT_TRUE(address == endpoint.getAddress());
+    EXPECT_EQ(test_port, endpoint.getPort());
+    EXPECT_EQ(IPPROTO_UDP, endpoint.getProtocol());
+    EXPECT_EQ(AF_INET6, endpoint.getFamily());
+}
diff --git a/src/lib/asiolink/tests/udp_query_unittest.cc b/src/lib/asiolink/tests/udp_query_unittest.cc
deleted file mode 100644
index 9eb1aba..0000000
--- a/src/lib/asiolink/tests/udp_query_unittest.cc
+++ /dev/null
@@ -1,145 +0,0 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <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/tests/udp_socket_unittest.cc b/src/lib/asiolink/tests/udp_socket_unittest.cc
new file mode 100644
index 0000000..3033857
--- /dev/null
+++ b/src/lib/asiolink/tests/udp_socket_unittest.cc
@@ -0,0 +1,287 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+
+/// \brief Test of UDPSocket
+///
+/// Tests the fuctionality of a UDPSocket by working through an open-send-
+/// receive-close sequence and checking that the asynchronous notifications
+/// work.
+
+#include <string>
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <algorithm>
+#include <cstdlib>
+#include <cstddef>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <boost/bind.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <asio.hpp>
+
+#include <asiolink/io_service.h>
+#include <asiolink/udp_endpoint.h>
+#include <asiolink/udp_socket.h>
+
+using namespace asio;
+using namespace asiolink;
+using namespace std;
+
+namespace {
+
+const char SERVER_ADDRESS[] = "127.0.0.1";
+const unsigned short SERVER_PORT = 5301;
+
+// TODO: Shouldn't we send something that is real message?
+const char OUTBOUND_DATA[] = "Data sent from client to server";
+const char INBOUND_DATA[] = "Returned data from server to client";
+}
+
+///
+/// An instance of this object is passed to the asynchronous I/O functions
+/// and the operator() method is called when when an asynchronous I/O
+/// completes.  The arguments to the completion callback are stored for later
+/// retrieval.
+class UDPCallback {
+public:
+
+    struct PrivateData {
+        PrivateData() :
+            error_code_(), length_(0), called_(false), name_("")
+        {}
+
+        asio::error_code    error_code_;    ///< Completion error code
+        size_t              length_;        ///< Number of bytes transferred
+        bool                called_;        ///< Set true when callback called
+        std::string         name_;          ///< Which of the objects this is
+    };
+
+    /// \brief Constructor
+    ///
+    /// Constructs the object.  It also creates the data member pointed to by
+    /// a shared pointer.  When used as a callback object, this is copied as it
+    /// is passed into the asynchronous function.  This means that there are two
+    /// objects and inspecting the one we passed in does not tell us anything.
+    ///
+    /// Therefore we use a boost::shared_ptr.  When the object is copied, the
+    /// shared pointer is copied, which leaves both objects pointing to the same
+    /// data.
+    ///
+    /// \param which Which of the two callback objects this is
+    UDPCallback(std::string which) : ptr_(new PrivateData())
+    {
+        setName(which);
+    }
+
+    /// \brief Destructor
+    ///
+    /// No code needed, destroying the shared pointer destroys the private data.
+    virtual ~UDPCallback()
+    {}
+
+    /// \brief Callback Function
+    ///
+    /// Called when an asynchronous I/O completes, this stores the
+    /// completion error code and the number of bytes transferred.
+    ///
+    /// \param ec I/O completion error code passed to callback function.
+    /// \param length Number of bytes transferred
+    virtual void operator()(asio::error_code ec, size_t length = 0) {
+        ptr_->error_code_ = ec;
+        setLength(length);
+        setCalled(true);
+    }
+
+    /// \brief Get I/O completion error code
+    int getCode() {
+        return (ptr_->error_code_.value());
+    }
+
+    /// \brief Set I/O completion code
+    ///
+    /// \param code New value of completion code
+    void setCode(int code) {
+        ptr_->error_code_ = asio::error_code(code, asio::error_code().category());
+    }
+
+    /// \brief Get number of bytes transferred in I/O
+    size_t getLength() {
+        return (ptr_->length_);
+    }
+
+    /// \brief Set number of bytes transferred in I/O
+    ///
+    /// \param length New value of length parameter
+    void setLength(size_t length) {
+        ptr_->length_ = length;
+    }
+
+    /// \brief Get flag to say when callback was called
+    bool getCalled() {
+        return (ptr_->called_);
+    }
+
+    /// \brief Set flag to say when callback was called
+    ///
+    /// \param called New value of called parameter
+    void setCalled(bool called) {
+        ptr_->called_ = called;
+    }
+
+    /// \brief Return instance of callback name
+    std::string getName() {
+        return (ptr_->name_);
+    }
+
+    /// \brief Set callback name
+    ///
+    /// \param name New value of the callback name
+    void setName(const std::string& name) {
+        ptr_->name_ = name;
+    }
+
+private:
+    boost::shared_ptr<PrivateData>  ptr_;   ///< Pointer to private data
+};
+
+// TODO: Need to add a test to check the cancel() method
+
+// Tests the operation of a UDPSocket by opening it, sending an asynchronous
+// message to a server, receiving an asynchronous message from the server and
+// closing.
+TEST(UDPSocket, SequenceTest) {
+
+    // Common objects.
+    IOService   service;                    // Service object for async control
+
+    // Server
+    IOAddress   server_address(SERVER_ADDRESS); // Address of target server
+    UDPCallback server_cb("Server");        // Server callback
+    UDPEndpoint server_endpoint(            // Endpoint describing server
+        server_address, SERVER_PORT);
+    UDPEndpoint server_remote_endpoint;     // Address where server received message from
+
+    // The client - the UDPSocket being tested
+    UDPSocket<UDPCallback>  client(service);// Socket under test
+    UDPCallback client_cb("Client");        // Async I/O callback function
+    UDPEndpoint client_remote_endpoint;     // Where client receives message from
+    size_t      client_cumulative = 0;      // Cumulative data received
+
+    // The server - with which the client communicates.  For convenience, we
+    // use the same io_service, and use the endpoint object created for
+    // the client to send to as the endpoint object in the constructor.
+    asio::ip::udp::socket server(service.get_io_service(),
+        server_endpoint.getASIOEndpoint());
+    server.set_option(socket_base::reuse_address(true));
+
+    // Assertion to ensure that the server buffer is large enough
+    char data[UDPSocket<UDPCallback>::MAX_SIZE];
+    ASSERT_GT(sizeof(data), sizeof(OUTBOUND_DATA));
+
+    // Open the client socket - the operation should be synchronous
+    EXPECT_FALSE(client.open(&server_endpoint, client_cb));
+
+    // Issue read on the server.  Completion callback should not have run.
+    server_cb.setCalled(false);
+    server_cb.setCode(42); // Answer to Life, the Universe and Everything!
+    server.async_receive_from(buffer(data, sizeof(data)),
+        server_remote_endpoint.getASIOEndpoint(), server_cb);
+    EXPECT_FALSE(server_cb.getCalled());
+
+    // Write something to the server using the client - the callback should not
+    // be called until we call the io_service.run() method.
+    client_cb.setCalled(false);
+    client_cb.setCode(7);  // Arbitrary number
+    client.asyncSend(OUTBOUND_DATA, sizeof(OUTBOUND_DATA), &server_endpoint, client_cb);
+    EXPECT_FALSE(client_cb.getCalled());
+
+    // Execute the two callbacks.
+    service.run_one();
+    service.run_one();
+
+    EXPECT_TRUE(client_cb.getCalled());
+    EXPECT_EQ(0, client_cb.getCode());
+    EXPECT_EQ(sizeof(OUTBOUND_DATA), client_cb.getLength());
+
+    EXPECT_TRUE(server_cb.getCalled());
+    EXPECT_EQ(0, server_cb.getCode());
+    EXPECT_EQ(sizeof(OUTBOUND_DATA), server_cb.getLength());
+
+    EXPECT_TRUE(equal(&data[0], &data[server_cb.getLength() - 1], OUTBOUND_DATA));
+
+    // Now return data from the server to the client.  Issue the read on the
+    // client.
+    client_cb.setLength(12345);             // Arbitrary number
+    client_cb.setCalled(false);
+    client_cb.setCode(32);                  // Arbitrary number
+    client.asyncReceive(data, sizeof(data), client_cumulative,
+        &client_remote_endpoint, client_cb);
+
+    // Issue the write on the server side to the source of the data it received.
+    server_cb.setLength(22345);             // Arbitrary number
+    server_cb.setCalled(false);
+    server_cb.setCode(232);                 // Arbitrary number
+    server.async_send_to(buffer(INBOUND_DATA, sizeof(INBOUND_DATA)),
+        server_remote_endpoint.getASIOEndpoint(), server_cb);
+
+    // Expect the two callbacks to run
+    service.run_one();
+    service.run_one();
+
+    EXPECT_TRUE(client_cb.getCalled());
+    EXPECT_EQ(0, client_cb.getCode());
+    EXPECT_EQ(sizeof(INBOUND_DATA), client_cb.getLength());
+
+    EXPECT_TRUE(server_cb.getCalled());
+    EXPECT_EQ(0, server_cb.getCode());
+    EXPECT_EQ(sizeof(INBOUND_DATA), server_cb.getLength());
+
+    EXPECT_TRUE(equal(&data[0], &data[server_cb.getLength() - 1], INBOUND_DATA));
+
+    // Check that the address/port received by the client corresponds to the
+    // address and port the server is listening on.
+    EXPECT_TRUE(server_address == client_remote_endpoint.getAddress());
+    EXPECT_EQ(SERVER_PORT, client_remote_endpoint.getPort());
+
+    // Finally, check that the receive received a complete buffer's worth of data.
+    EXPECT_TRUE(client.receiveComplete(&data[0], client_cb.getLength(),
+        client_cumulative));
+    EXPECT_EQ(client_cb.getLength(), client_cumulative);
+
+    // Close client and server.
+    EXPECT_NO_THROW(client.close());
+    EXPECT_NO_THROW(server.close());
+}
diff --git a/src/lib/asiolink/udp_endpoint.h b/src/lib/asiolink/udp_endpoint.h
index 27541e0..0958af6 100644
--- a/src/lib/asiolink/udp_endpoint.h
+++ b/src/lib/asiolink/udp_endpoint.h
@@ -33,6 +33,16 @@ public:
     /// \name Constructors and Destructor.
     ///
     //@{
+
+    /// \brief Default Constructor
+    ///
+    /// Creates an internal endpoint.  This is expected to be set by some
+    /// external call.
+    UDPEndpoint() :
+        asio_endpoint_placeholder_(new asio::ip::udp::endpoint()),
+        asio_endpoint_(*asio_endpoint_placeholder_)
+    {}
+
     /// \brief Constructor from a pair of address and port.
     ///
     /// \param address The IP address of the endpoint.
@@ -50,27 +60,27 @@ public:
     /// corresponding ASIO class, \c udp::endpoint.
     ///
     /// \param asio_endpoint The ASIO representation of the UDP endpoint.
-    UDPEndpoint(const asio::ip::udp::endpoint& asio_endpoint) :
+    UDPEndpoint(asio::ip::udp::endpoint& asio_endpoint) :
         asio_endpoint_placeholder_(NULL), asio_endpoint_(asio_endpoint)
     {}
 
     /// \brief The destructor.
-    ~UDPEndpoint() { delete asio_endpoint_placeholder_; }
+    virtual ~UDPEndpoint() { delete asio_endpoint_placeholder_; }
     //@}
 
-    inline IOAddress getAddress() const {
+    virtual IOAddress getAddress() const {
         return (asio_endpoint_.address());
     }
 
-    inline uint16_t getPort() const {
+    virtual uint16_t getPort() const {
         return (asio_endpoint_.port());
     }
 
-    inline short getProtocol() const {
+    virtual short getProtocol() const {
         return (asio_endpoint_.protocol().protocol());
     }
 
-    inline short getFamily() const {
+    virtual short getFamily() const {
         return (asio_endpoint_.protocol().family());
     }
 
@@ -79,10 +89,13 @@ public:
     inline const asio::ip::udp::endpoint& getASIOEndpoint() const {
         return (asio_endpoint_);
     }
+    inline asio::ip::udp::endpoint& getASIOEndpoint() {
+        return (asio_endpoint_);
+    }
 
 private:
-    const asio::ip::udp::endpoint* asio_endpoint_placeholder_;
-    const asio::ip::udp::endpoint& asio_endpoint_;
+    asio::ip::udp::endpoint* asio_endpoint_placeholder_;
+    asio::ip::udp::endpoint& asio_endpoint_;
 };
 
 }      // namespace asiolink
diff --git a/src/lib/asiolink/udp_query.cc b/src/lib/asiolink/udp_query.cc
deleted file mode 100644
index a793814..0000000
--- a/src/lib/asiolink/udp_query.cc
+++ /dev/null
@@ -1,189 +0,0 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <config.h>
-
-// 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
deleted file mode 100644
index 3ed44ad..0000000
--- a/src/lib/asiolink/udp_query.h
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#ifndef __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
index daea014..841bdc6 100644
--- a/src/lib/asiolink/udp_server.cc
+++ b/src/lib/asiolink/udp_server.cc
@@ -12,25 +12,26 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-#include <config.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <unistd.h>             // for some IPC/network system calls
 
 #include <boost/shared_array.hpp>
 
-// unistd is needed for asio.hpp with SunStudio
-#include <unistd.h>
-
-#include <asio.hpp>
+#include <config.h>
 
 #include <log/dummylog.h>
 
+#include <asio.hpp>
+#include <asiolink/dummy_io_cb.h>
 #include <asiolink/udp_endpoint.h>
+#include <asiolink/udp_server.h>
 #include <asiolink/udp_socket.h>
 
-#include <asiolink/udp_server.h>
+#include <dns/opcode.h>
 
 using namespace asio;
 using asio::ip::udp;
-using asio::ip::tcp;
 using isc::log::dlog;
 
 using namespace std;
@@ -217,7 +218,16 @@ UDPServer::operator()(error_code ec, size_t length) {
         // 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_));
+
+        // The UDP socket class has been extended with asynchronous functions
+        // and takes as a template parameter a completion callback class.  As
+        // UDPServer does not use these extended functions (only those defined
+        // in the IOSocket base class) - but needs a UDPSocket to get hold of
+        // the underlying Boost UDP socket - DummyIOCallback is used.  This
+        // provides the appropriate operator() but is otherwise functionless.
+        data_->iosock_.reset(
+            new UDPSocket<DummyIOCallback>(*data_->socket_));
+
         data_->io_message_.reset(new IOMessage(data_->data_.get(),
             data_->bytes_, *data_->iosock_, *data_->peer_));
 
diff --git a/src/lib/asiolink/udp_socket.h b/src/lib/asiolink/udp_socket.h
index 5c641ff..bb94ad5 100644
--- a/src/lib/asiolink/udp_socket.h
+++ b/src/lib/asiolink/udp_socket.h
@@ -19,30 +19,258 @@
 #error "asio.hpp must be included before including this, see asiolink.h as to why"
 #endif
 
-#include <asiolink/io_socket.h>
+#include <log/dummylog.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <unistd.h>             // for some IPC/network system calls
+
+#include <cstddef>
+
+#include <config.h>
+
+
+#include <asiolink/io_asio_socket.h>
+#include <asiolink/io_endpoint.h>
+#include <asiolink/io_service.h>
+#include <asiolink/udp_endpoint.h>
 
 namespace asiolink {
 
-/// \brief The \c UDPSocket class is a concrete derived class of
-/// \c IOSocket that represents a UDP socket.
+/// \brief The \c UDPSocket class is a concrete derived class of \c IOAsioSocket
+/// that represents a UDP socket.
 ///
-/// Other notes about \c TCPSocket applies to this class, too.
-class UDPSocket : public IOSocket {
+/// \param C Callback type
+template <typename C>
+class UDPSocket : public IOAsioSocket<C> {
 private:
-    UDPSocket(const UDPSocket& source);
-    UDPSocket& operator=(const UDPSocket& source);
+    /// \brief Class is non-copyable
+    UDPSocket(const UDPSocket&);
+    UDPSocket& operator=(const UDPSocket&);
+
 public:
+    enum {
+        MAX_SIZE = 4096         // Send and receive size
+    };
+    
     /// \brief Constructor from an ASIO UDP socket.
     ///
-    /// \param socket The ASIO representation of the UDP socket.
-    UDPSocket(asio::ip::udp::socket& socket) : socket_(socket) {}
+    /// \param socket The ASIO representation of the UDP socket.  It
+    /// is assumed that the caller will open and close the socket, so
+    /// these operations are a no-op for that socket.
+    UDPSocket(asio::ip::udp::socket& socket);
+
+    /// \brief Constructor
+    ///
+    /// Used when the UDPSocket is being asked to manage its own internal
+    /// socket.  It is assumed that open() and close() will not be used.
+    ///
+    /// \param service I/O Service object used to manage the socket.
+    UDPSocket(IOService& service);
+
+    /// \brief Destructor
+    virtual ~UDPSocket();
 
     virtual int getNative() const { return (socket_.native()); }
     virtual int getProtocol() const { return (IPPROTO_UDP); }
 
+    /// \brief Open Socket
+    ///
+    /// Opens the UDP socket.  In the model for transport-layer agnostic I/O,
+    /// an "open" operation includes a connection to the remote end (which
+    /// may take time).  This does not happen for UDP, so the method returns
+    /// "false" to indicate that the operation completed synchronously.
+    ///
+    /// \param endpoint Endpoint to which the socket will connect to.
+    /// \param callback Unused.
+    ///
+    /// \return false to indicate that the "operation" completed synchronously.
+    virtual bool open(const IOEndpoint* endpoint, C&);
+
+    /// \brief Send Asynchronously
+    ///
+    /// This corresponds to async_send_to() for UDP sockets and async_send()
+    /// for TCP.  In both cases an endpoint argument is supplied indicating the
+    /// target of the send - this is ignored for TCP.
+    ///
+    /// \param data Data to send
+    /// \param length Length of data to send
+    /// \param endpoint Target of the send
+    /// \param callback Callback object.
+    virtual void asyncSend(const void* data, size_t length,
+        const IOEndpoint* endpoint, C& callback);
+
+    /// \brief Receive Asynchronously
+    ///
+    /// This correstponds to async_receive_from() for UDP sockets and
+    /// async_receive() for TCP.  In both cases, an endpoint argument is
+    /// supplied to receive the source of the communication.  For TCP it will
+    /// be filled in with details of the connection.
+    ///
+    /// \param data Buffer to receive incoming message
+    /// \param length Length of the data buffer
+    /// \param cumulative Amount of data that should already be in the buffer.
+    /// (This is ignored - every UPD receive fills the buffer from the start.)
+    /// \param endpoint Source of the communication
+    /// \param callback Callback object
+    virtual void asyncReceive(void* data, size_t length, size_t cumulative,
+        IOEndpoint* endpoint, C& callback);
+
+    /// \brief Checks if the data received is complete.
+    ///
+    /// As all the data is received in one I/O, so this is, this is effectively
+    /// a no-op (although it does update the amount of data received).
+    ///
+    /// \param data Data buffer containing data to date.  (This is ignored
+    /// for UDP receives.)
+    /// \param length Amount of data received in last asynchronous I/O
+    /// \param cumulative On input, amount of data received before the last
+    /// I/O.  On output, the total amount of data received to date.
+    ///
+    /// \return true if the receive is complete, false if another receive is
+    /// needed.
+    virtual bool receiveComplete(void*, size_t length, size_t& cumulative) {
+        cumulative = length;
+        return (true);
+    }
+
+    /// \brief Cancel I/O On Socket
+    virtual void cancel();
+
+    /// \brief Close socket
+    virtual void close();
+
+
 private:
-    asio::ip::udp::socket& socket_;
+    // Two variables to hold the socket - a socket and a pointer to it.  This
+    // handles the case where a socket is passed to the UDPSocket on
+    // construction, or where it is asked to manage its own socket.
+    asio::ip::udp::socket*      socket_ptr_;    ///< Pointer to own socket
+    asio::ip::udp::socket&      socket_;        ///< Socket
+    bool                        isopen_;        ///< true when socket is open
 };
 
-}      // namespace asiolink
+// Constructor - caller manages socket
+
+template <typename C>
+UDPSocket<C>::UDPSocket(asio::ip::udp::socket& socket) :
+    socket_ptr_(NULL), socket_(socket), isopen_(true)
+{
+}
+
+// Constructor - create socket on the fly
+
+template <typename C>
+UDPSocket<C>::UDPSocket(IOService& service) :
+    socket_ptr_(new asio::ip::udp::socket(service.get_io_service())),
+    socket_(*socket_ptr_), isopen_(false)
+{
+}
+
+// Destructor.  Only delete the socket if we are managing it.
+
+template <typename C>
+UDPSocket<C>::~UDPSocket()
+{
+    delete socket_ptr_;
+}
+
+// Open the socket.  Throws an error on failure
+// TODO: Make the open more resilient
+
+template <typename C> bool
+UDPSocket<C>::open(const IOEndpoint* endpoint, C&) {
+
+    // Ignore opens on already-open socket.  Don't throw a failure because
+    // of uncertainties as to what precedes whan when using asynchronous I/O.
+    // At also allows us a treat a passed-in socket as a self-managed socket.
+
+    if (!isopen_) {
+        if (endpoint->getFamily() == AF_INET) {
+            socket_.open(asio::ip::udp::v4());
+        }
+        else {
+            socket_.open(asio::ip::udp::v6());
+        }
+        isopen_ = true;
+
+        // Ensure it can send and receive 4K buffers.
+        socket_.set_option(asio::socket_base::send_buffer_size(MAX_SIZE));
+        socket_.set_option(asio::socket_base::receive_buffer_size(MAX_SIZE));
+    ;
+        // Allow reuse of an existing port/address
+        socket_.set_option(asio::socket_base::reuse_address(true));
+    }
+    return (false);
+}
+
+// Send a message.  Should never do this if the socket is not open, so throw
+// an exception if this is the case.
+
+template <typename C> void
+UDPSocket<C>::asyncSend(const void* data, size_t length,
+    const IOEndpoint* endpoint, C& callback)
+{
+    if (isopen_) {
+
+        // Upconvert to a UDPEndpoint.  We need to do this because although
+        // IOEndpoint is the base class of UDPEndpoint and TCPEndpoint, it
+        // doing cont contain a method for getting at the underlying endpoint
+        // type - those are in the derived class and the two classes differ on
+        // return type.
+
+        assert(endpoint->getProtocol() == IPPROTO_UDP);
+        const UDPEndpoint* udp_endpoint =
+            static_cast<const UDPEndpoint*>(endpoint);
+        socket_.async_send_to(asio::buffer(data, length),
+            udp_endpoint->getASIOEndpoint(), callback);
+    } else {
+        isc_throw(SocketNotOpen,
+            "attempt to send on a UDP socket that is not open");
+    }
+}
+
+// Receive a message. Note that the "cumulative" argument is ignored - every UDP
+// receive is put into the buffer beginning at the start - there is no concept
+// receiving a subsequent part of a message.  Same critera as before concerning
+// the need for the socket to be open.
+
+template <typename C> void
+UDPSocket<C>::asyncReceive(void* data, size_t length, size_t,
+    IOEndpoint* endpoint, C& callback)
+{
+    if (isopen_) {
+
+        // Upconvert the endpoint again.
+        assert(endpoint->getProtocol() == IPPROTO_UDP);
+        UDPEndpoint* udp_endpoint = static_cast<UDPEndpoint*>(endpoint);
+
+        socket_.async_receive_from(asio::buffer(data, length),
+            udp_endpoint->getASIOEndpoint(), callback);
+    } else {
+        isc_throw(SocketNotOpen,
+            "attempt to receive from a UDP socket that is not open");
+    }
+}
+
+// Cancel I/O on the socket.  No-op if the socket is not open.
+template <typename C> void
+UDPSocket<C>::cancel() {
+    if (isopen_) {
+        socket_.cancel();
+    }
+}
+
+// Close the socket down.  Can only do this if the socket is open and we are
+// managing it ourself.
+
+template <typename C> void
+UDPSocket<C>::close() {
+    if (isopen_ && socket_ptr_) {
+        socket_.close();
+        isopen_ = false;
+    }
+}
+
+} // namespace asiolink
+
 #endif // __UDP_SOCKET_H
diff --git a/src/lib/cache/cache_entry_key.cc b/src/lib/cache/cache_entry_key.cc
index 35917a0..85c03a0 100644
--- a/src/lib/cache/cache_entry_key.cc
+++ b/src/lib/cache/cache_entry_key.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <sstream>
 #include "cache_entry_key.h"
 
diff --git a/src/lib/cache/cache_entry_key.h b/src/lib/cache/cache_entry_key.h
index 002f958..674deb0 100644
--- a/src/lib/cache/cache_entry_key.h
+++ b/src/lib/cache/cache_entry_key.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __CACHE_ENTRY_KEY_H
 #define __CACHE_ENTRY_KEY_H
 
diff --git a/src/lib/cache/local_zone_data.cc b/src/lib/cache/local_zone_data.cc
index 2dcc113..61ce35a 100644
--- a/src/lib/cache/local_zone_data.cc
+++ b/src/lib/cache/local_zone_data.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <dns/rrset.h>
 #include "local_zone_data.h"
 #include "cache_entry_key.h"
diff --git a/src/lib/cache/local_zone_data.h b/src/lib/cache/local_zone_data.h
index bcf5a94..3015847 100644
--- a/src/lib/cache/local_zone_data.h
+++ b/src/lib/cache/local_zone_data.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef _LOCAL_ZONE_DATA
 #define _LOCAL_ZONE_DATA
 
diff --git a/src/lib/cache/message_cache.cc b/src/lib/cache/message_cache.cc
index 6582199..70e7c67 100644
--- a/src/lib/cache/message_cache.cc
+++ b/src/lib/cache/message_cache.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <config.h>
 
 #include <nsas/nsas_entry_compare.h>
diff --git a/src/lib/cache/message_cache.h b/src/lib/cache/message_cache.h
index 0131b30..3a684c8 100644
--- a/src/lib/cache/message_cache.h
+++ b/src/lib/cache/message_cache.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __MESSAGE_CACHE_H
 #define __MESSAGE_CACHE_H
 
diff --git a/src/lib/cache/message_entry.cc b/src/lib/cache/message_entry.cc
index 2fdb07c..d4de11f 100644
--- a/src/lib/cache/message_entry.cc
+++ b/src/lib/cache/message_entry.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <config.h>
 
 #include <limits>
diff --git a/src/lib/cache/message_entry.h b/src/lib/cache/message_entry.h
index 20b2472..682c171 100644
--- a/src/lib/cache/message_entry.h
+++ b/src/lib/cache/message_entry.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __MESSAGE_ENTRY_H
 #define __MESSAGE_ENTRY_H
 
diff --git a/src/lib/cache/resolver_cache.cc b/src/lib/cache/resolver_cache.cc
index 7ebdb4f..0734da8 100644
--- a/src/lib/cache/resolver_cache.cc
+++ b/src/lib/cache/resolver_cache.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <config.h>
 
 #include "resolver_cache.h"
@@ -177,10 +175,10 @@ ResolverCache::lookup(const isc::dns::Name& qname,
 }
 
 isc::dns::RRsetPtr
-ResolverCache::lookupClosestRRset(const isc::dns::Name& qname,
-                                  const isc::dns::RRType& qtype,
-                                  const isc::dns::RRClass& qclass) const
+ResolverCache::lookupDeepestNS(const isc::dns::Name& qname,
+                               const isc::dns::RRClass& qclass) const
 {
+    isc::dns::RRType qtype = RRType::NS();
     ResolverClassCache* cc = getClassCache(qclass);
     if (cc) {
         unsigned int count = qname.getLabelCount();
@@ -201,7 +199,6 @@ ResolverCache::lookupClosestRRset(const isc::dns::Name& qname,
 
 bool
 ResolverCache::update(const isc::dns::Message& msg) {
-    
     QuestionIterator iter = msg.beginQuestion();
     ResolverClassCache* cc = getClassCache((*iter)->getClass());
     if (cc) {
diff --git a/src/lib/cache/resolver_cache.h b/src/lib/cache/resolver_cache.h
index 7a0fdab..a8149e4 100644
--- a/src/lib/cache/resolver_cache.h
+++ b/src/lib/cache/resolver_cache.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __RESOLVER_CACHE_H
 #define __RESOLVER_CACHE_H
 
@@ -243,28 +241,27 @@ public:
                               const isc::dns::RRType& qtype,
                               const isc::dns::RRClass& qclass) const;
 
-    /// \brief Look up closest rrset in cache.
+    /// \brief Look up closest enclosing NS 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.
+    /// \return return the shared_ptr of closest enclosing ns 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
+    /// Currently the implementation is: search exact ns rrset
+    /// label by lable, If the ns 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.
+    /// the name is very long but it's closest rrset's name is very short.
+    ///
+    /// If a good perfermance is needed when looking up the closest
+    /// enclosing ns 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,
+    /// is used frequently? Exact or closest enclosing ns looking up.
+    isc::dns::RRsetPtr lookupDeepestNS(const isc::dns::Name& qname,
                               const isc::dns::RRClass& qclass) const;
     //@}
 
diff --git a/src/lib/cache/rrset_cache.cc b/src/lib/cache/rrset_cache.cc
index 86ee867..0a2957c 100644
--- a/src/lib/cache/rrset_cache.cc
+++ b/src/lib/cache/rrset_cache.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <config.h>
 
 #include <string>
diff --git a/src/lib/cache/rrset_cache.h b/src/lib/cache/rrset_cache.h
index d082b3c..15084c9 100644
--- a/src/lib/cache/rrset_cache.h
+++ b/src/lib/cache/rrset_cache.h
@@ -12,12 +12,10 @@
 // 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 <cache/rrset_entry.h>
 #include <nsas/hash_table.h>
 #include <nsas/lru_list.h>
 
diff --git a/src/lib/cache/rrset_copy.cc b/src/lib/cache/rrset_copy.cc
index 85ba153..05b139a 100644
--- a/src/lib/cache/rrset_copy.cc
+++ b/src/lib/cache/rrset_copy.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include "rrset_copy.h"
 
 using namespace isc::dns;
diff --git a/src/lib/cache/rrset_copy.h b/src/lib/cache/rrset_copy.h
index f6bee55..b6af8d6 100644
--- a/src/lib/cache/rrset_copy.h
+++ b/src/lib/cache/rrset_copy.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __RRSET_COPY_
 #define __RRSET_COPY_
 
diff --git a/src/lib/cache/rrset_entry.cc b/src/lib/cache/rrset_entry.cc
index 9407e97..c829956 100644
--- a/src/lib/cache/rrset_entry.cc
+++ b/src/lib/cache/rrset_entry.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <config.h>
 
 #include <dns/message.h>
diff --git a/src/lib/cache/rrset_entry.h b/src/lib/cache/rrset_entry.h
index f0149a4..5fa8f2c 100644
--- a/src/lib/cache/rrset_entry.h
+++ b/src/lib/cache/rrset_entry.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __RRSET_ENTRY_H
 #define __RRSET_ENTRY_H
 
diff --git a/src/lib/cache/tests/cache_test_messagefromfile.h b/src/lib/cache/tests/cache_test_messagefromfile.h
index 820d822..62e237c 100644
--- a/src/lib/cache/tests/cache_test_messagefromfile.h
+++ b/src/lib/cache/tests/cache_test_messagefromfile.h
@@ -12,7 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
 #include <vector>
 #include <dns/tests/unittest_util.h>
 #include <dns/buffer.h>
diff --git a/src/lib/cache/tests/cache_test_sectioncount.h b/src/lib/cache/tests/cache_test_sectioncount.h
index a385133..537ca81 100644
--- a/src/lib/cache/tests/cache_test_sectioncount.h
+++ b/src/lib/cache/tests/cache_test_sectioncount.h
@@ -12,7 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
 #include <vector>
 #include <dns/tests/unittest_util.h>
 #include <dns/buffer.h>
diff --git a/src/lib/cache/tests/local_zone_data_unittest.cc b/src/lib/cache/tests/local_zone_data_unittest.cc
index 28de4be..6877eae 100644
--- a/src/lib/cache/tests/local_zone_data_unittest.cc
+++ b/src/lib/cache/tests/local_zone_data_unittest.cc
@@ -12,7 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
 #include <config.h>
 #include <string>
 #include <gtest/gtest.h>
diff --git a/src/lib/cache/tests/message_cache_unittest.cc b/src/lib/cache/tests/message_cache_unittest.cc
index f984312..e7184bd 100644
--- a/src/lib/cache/tests/message_cache_unittest.cc
+++ b/src/lib/cache/tests/message_cache_unittest.cc
@@ -12,7 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
 #include <config.h>
 #include <string>
 #include <gtest/gtest.h>
diff --git a/src/lib/cache/tests/message_entry_unittest.cc b/src/lib/cache/tests/message_entry_unittest.cc
index f0fc777..3b2711a 100644
--- a/src/lib/cache/tests/message_entry_unittest.cc
+++ b/src/lib/cache/tests/message_entry_unittest.cc
@@ -12,7 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
 #include <config.h>
 #include <string>
 #include <gtest/gtest.h>
diff --git a/src/lib/cache/tests/resolver_cache_unittest.cc b/src/lib/cache/tests/resolver_cache_unittest.cc
index a3cf728..20dcec9 100644
--- a/src/lib/cache/tests/resolver_cache_unittest.cc
+++ b/src/lib/cache/tests/resolver_cache_unittest.cc
@@ -12,13 +12,13 @@
 // 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"
+#include "cache_test_sectioncount.h"
 
 using namespace isc::cache;
 using namespace isc::dns;
@@ -64,7 +64,7 @@ TEST_F(ResolverCacheTest, testUpdateMessage) {
     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");
@@ -87,7 +87,7 @@ TEST_F(ResolverCacheTest, testUpdateRRset) {
     cache->update(rrset_ptr);
 
     Message new_msg(Message::RENDER);
-    Question question(qname, klass, RRType::NS());
+    Question question(qname, RRClass::IN(), 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));
@@ -113,26 +113,16 @@ TEST_F(ResolverCacheTest, testLookupClosestRRset) {
 
     Name qname("www.test.example.com.");
 
-    RRsetPtr rrset_ptr = cache->lookupClosestRRset(qname, RRType::NS(),
-                                                  RRClass::IN());
+    RRsetPtr rrset_ptr = cache->lookupDeepestNS(qname, 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());
+    rrset_ptr = cache->lookupDeepestNS(Name("example.com."), RRClass::IN());
     EXPECT_TRUE(rrset_ptr);
     EXPECT_EQ(rrset_ptr->getName(), Name("example.com."));
 
-    rrset_ptr = cache->lookupClosestRRset(Name("com."),
-                                         RRType::NS(), RRClass::IN());
+    rrset_ptr = cache->lookupDeepestNS(Name("com."), 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
index 1263406..afb7eaa 100644
--- a/src/lib/cache/tests/rrset_cache_unittest.cc
+++ b/src/lib/cache/tests/rrset_cache_unittest.cc
@@ -12,7 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
 #include <config.h>
 #include <string>
 #include <gtest/gtest.h>
diff --git a/src/lib/cache/tests/rrset_entry_unittest.cc b/src/lib/cache/tests/rrset_entry_unittest.cc
index 29c05b5..c7c3c6e 100644
--- a/src/lib/cache/tests/rrset_entry_unittest.cc
+++ b/src/lib/cache/tests/rrset_entry_unittest.cc
@@ -12,7 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
 #include <config.h>
 #include <string>
 #include <gtest/gtest.h>
diff --git a/src/lib/cache/tests/run_unittests.cc b/src/lib/cache/tests/run_unittests.cc
index b34b3dd..2c86581 100644
--- a/src/lib/cache/tests/run_unittests.cc
+++ b/src/lib/cache/tests/run_unittests.cc
@@ -12,7 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id: run_unittests.cc 3020 2010-09-26 03:47:26Z jinmei $
 #include <config.h>
 
 #include <gtest/gtest.h>
diff --git a/src/lib/config/tests/testdata/spec22.spec b/src/lib/config/tests/testdata/spec22.spec
index be6d51f..cccd77b 100644
--- a/src/lib/config/tests/testdata/spec22.spec
+++ b/src/lib/config/tests/testdata/spec22.spec
@@ -1,6 +1,6 @@
 {
   "module_spec": {
-    "module_name": "Spec2",
+    "module_name": "Spec22",
     "config_data": [
       { "item_name": "value1",
         "item_type": "integer",
@@ -81,7 +81,7 @@
       { "item_name": "value9",
         "item_type": "map",
         "item_optional": false,
-        "item_default": {},
+        "item_default": { "v91": "def", "v92": {} },
         "map_item_spec": [
           { "item_name": "v91",
             "item_type": "string",
diff --git a/src/lib/datasrc/data_source.cc b/src/lib/datasrc/data_source.cc
index 8b2b47e..829d1fc 100644
--- a/src/lib/datasrc/data_source.cc
+++ b/src/lib/datasrc/data_source.cc
@@ -1157,7 +1157,7 @@ MetaDataSrc::addDataSrc(ConstDataSrcPtr data_src) {
 void
 MetaDataSrc::removeDataSrc(ConstDataSrcPtr data_src) {
     std::vector<ConstDataSrcPtr>::iterator it, itr;
-    for (it = data_sources.begin(); it != data_sources.end(); it++) {
+    for (it = data_sources.begin(); it != data_sources.end(); ++it) {
         if (*it == data_src) {
             itr = it;
         }
diff --git a/src/lib/datasrc/memory_datasrc.cc b/src/lib/datasrc/memory_datasrc.cc
index bbc5166..bf3c8ca 100644
--- a/src/lib/datasrc/memory_datasrc.cc
+++ b/src/lib/datasrc/memory_datasrc.cc
@@ -515,7 +515,7 @@ struct MemoryZone::MemoryZoneImpl {
         if (target != NULL && !node->getData()->empty()) {
             // Empty domain will be handled as NXRRSET by normal processing
             for (found = node->getData()->begin();
-                 found != node->getData()->end(); found++)
+                 found != node->getData()->end(); ++found)
             {
                 target->addRRset(
                     boost::const_pointer_cast<RRset>(prepareRRset(name,
diff --git a/src/lib/dns/dnssectime.cc b/src/lib/dns/dnssectime.cc
index 04643e2..c889178 100644
--- a/src/lib/dns/dnssectime.cc
+++ b/src/lib/dns/dnssectime.cc
@@ -12,6 +12,10 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include <stdint.h>
+
+#include <sys/time.h>
+
 #include <string>
 #include <iomanip>
 #include <iostream>
@@ -26,30 +30,121 @@
 
 using namespace std;
 
+namespace {
+int days[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+
+inline bool
+isLeap(const int y) {
+    return ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0);
+}
+
+unsigned int
+yearSecs(const int year) {
+    return ((isLeap(year) ? 366 : 365 ) * 86400);
+}
+
+unsigned int
+monthSecs(const int month, const int year) {
+    return ((days[month] + ((month == 1 && isLeap(year)) ? 1 : 0 )) * 86400);
+}
+}
+
 namespace isc {
 namespace dns {
 
 string
-timeToText(const time_t timeval) {
-    struct tm* const t = gmtime(&timeval);
-
-    // gmtime() will keep most values within range, but it can
-    // produce a five-digit year; check for this.
-    if ((t->tm_year + 1900) > 9999) {
-        isc_throw(InvalidTime, "Time value out of range: year > 9999");
+timeToText64(uint64_t value) {
+    struct tm tm;
+    unsigned int secs;
+
+    // We cannot rely on gmtime() because time_t may not be of 64 bit
+    // integer.  The following conversion logic is borrowed from BIND 9.
+    tm.tm_year = 70;
+    while ((secs = yearSecs(tm.tm_year + 1900)) <= value) {
+        value -= secs;
+        ++tm.tm_year;
+        if (tm.tm_year + 1900 > 9999) {
+            isc_throw(InvalidTime,
+                      "Time value out of range (year > 9999): " <<
+                      tm.tm_year + 1900);
+        }
+    }
+    tm.tm_mon = 0;
+    while ((secs = monthSecs(tm.tm_mon, tm.tm_year + 1900)) <= value) {
+        value -= secs;
+        tm.tm_mon++;
     }
+    tm.tm_mday = 1;
+    while (86400 <= value) {
+        value -= 86400;
+        ++tm.tm_mday;
+    }
+    tm.tm_hour = 0;
+    while (3600 <= value) {
+        value -= 3600;
+        ++tm.tm_hour;
+    }
+    tm.tm_min = 0;
+    while (60 <= value) {
+        value -= 60;
+        ++tm.tm_min;
+    }
+    tm.tm_sec = value;    // now t < 60, so this substitution is safe.
 
     ostringstream oss;
     oss << setfill('0')
-        << setw(4) << t->tm_year + 1900
-        << setw(2) << t->tm_mon + 1
-        << setw(2) << t->tm_mday 
-        << setw(2) << t->tm_hour
-        << setw(2) << t->tm_min
-        << setw(2) << t->tm_sec;
+        << setw(4) << tm.tm_year + 1900
+        << setw(2) << tm.tm_mon + 1
+        << setw(2) << tm.tm_mday
+        << setw(2) << tm.tm_hour
+        << setw(2) << tm.tm_min
+        << setw(2) << tm.tm_sec;
     return (oss.str());
 }
 
+// timeToText32() below uses the current system time.  To test it with
+// unusual current time values we introduce the following function pointer;
+// when it's non NULL, we call it to get the (normally faked) current time.
+// Otherwise we use the standard gettimeofday(2).  This hook is specifically
+// intended for testing purposes, so, even if it's visible outside of this
+// library, it's not even declared in a header file.
+namespace dnssectime {
+namespace detail {
+int64_t (*gettimeFunction)() = NULL;
+}
+}
+
+namespace {
+int64_t
+gettimeofdayWrapper() {
+    using namespace dnssectime::detail;
+    if (gettimeFunction != NULL) {
+        return (gettimeFunction());
+    }
+
+    struct timeval now;
+    gettimeofday(&now, NULL);
+
+    return (static_cast<int64_t>(now.tv_sec));
+}
+}
+
+string
+timeToText32(const uint32_t value) {
+    // We first adjust the time to the closest epoch based on the current time.
+    // Note that the following variables must be signed in order to handle
+    // time until year 2038 correctly.
+    const int64_t start = gettimeofdayWrapper() - 0x7fffffff;
+    int64_t base = 0;
+    int64_t t;
+    while ((t = (base + value)) < start) {
+        base += 0x100000000LL;
+    }
+
+    // Then convert it to text.
+    return (timeToText64(t));
+}
+
 namespace {
 const size_t DATE_LEN = 14;      // YYYYMMDDHHmmSS
 
@@ -62,27 +157,20 @@ checkRange(const int min, const int max, const int value,
     }
     isc_throw(InvalidTime, "Invalid " << valname << "value: " << value);
 }
-
-int days[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
-
-inline bool
-isLeap(const int y) {
-    return ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0);
-}
 }
 
-time_t
-timeFromText(const string& time_txt) {
-    // first try reading YYYYMMDDHHmmSS format
-    int year, month, day, hour, minute, second;
-
+uint64_t
+timeFromText64(const string& time_txt) {
+    // Confirm the source only consists digits.  sscanf() allows some
+    // minor exceptions.
     for (int i = 0; i < time_txt.length(); ++i) {
         if (!isdigit(time_txt.at(i))) {
-            isc_throw(InvalidTime,
-                      "Couldn't convert non-numeric time value: " << time_txt); 
+            isc_throw(InvalidTime, "Couldn't convert non-numeric time value: "
+                      << time_txt);
         }
     }
 
+    int year, month, day, hour, minute, second;
     if (time_txt.length() != DATE_LEN ||
         sscanf(time_txt.c_str(), "%4d%2d%2d%2d%2d%2d",
                &year, &month, &day, &hour, &minute, &second) != 6)
@@ -98,9 +186,9 @@ timeFromText(const string& time_txt) {
     checkRange(0, 59, minute, "minute");
     checkRange(0, 60, second, "second"); // 60 == leap second.
 
-    time_t timeval = second + (60 * minute) + (3600 * hour) +
+    uint64_t timeval = second + (60 * minute) + (3600 * hour) +
         ((day - 1) * 86400);
-    for (int m = 0; m < (month - 1); m++) {
+    for (int m = 0; m < (month - 1); ++m) {
             timeval += days[m] * 86400;
     }
     if (isLeap(year) && month > 2) {
@@ -112,5 +200,12 @@ timeFromText(const string& time_txt) {
 
     return (timeval);
 }
+
+uint32_t
+timeFromText32(const string& time_txt) {
+    // The implicit conversion from uint64_t to uint32_t should just work here,
+    // because we only need to drop higher 32 bits.
+    return (timeFromText64(time_txt));
+}
 }
 }
diff --git a/src/lib/dns/dnssectime.h b/src/lib/dns/dnssectime.h
index 5069650..baf866f 100644
--- a/src/lib/dns/dnssectime.h
+++ b/src/lib/dns/dnssectime.h
@@ -17,7 +17,6 @@
 
 #include <sys/types.h>
 #include <stdint.h>
-#include <time.h>
 
 #include <exceptions/exceptions.h>
 
@@ -40,11 +39,102 @@ public:
         isc::Exception(file, line, what) {}
 };
 
-time_t
-timeFromText(const std::string& time_txt);
+///
+/// \name DNSSEC time conversion functions.
+///
+/// These functions convert between times represented in seconds (in integer)
+/// since epoch and those in the textual form used in the RRSIG records.
+/// For integers we provide both 32-bit and 64-bit versions.
+/// The RRSIG expiration and inception fields are both 32-bit unsigned
+/// integers, so 32-bit versions would be more useful for protocol operations.
+/// However, with 32-bit integers we need to take into account wrap-around
+/// points and compare values using the serial number arithmetic as specified
+/// in RFC4034, which would be more error prone.  We therefore provide 64-bit
+/// versions, too.
+///
+/// The timezone is always UTC for these functions.
+//@{
+/// Convert textual DNSSEC time to integer, 64-bit version.
+///
+/// The textual form must only consist of digits and be in the form of
+/// YYYYMMDDHHmmSS, where:
+/// - YYYY must be between 1970 and 9999
+/// - MM must be between 01 and 12
+/// - DD must be between 01 and 31 and must be a valid day for the month
+///   represented in 'MM'.  For example, if MM is 04, DD cannot be 31.
+///   DD can be 29 when MM is 02 only when YYYY is a leap year.
+/// - HH must be between 00 and 23
+/// - mm must be between 00 and 59
+/// - SS must be between 00 and 60
+///
+/// For all fields the range includes the begin and end values.  Note that
+/// 60 is allowed for 'SS', intending a leap second, although in real operation
+/// it's unlikely to be specified.
+///
+/// If the given text is valid, this function converts it to an unsigned
+/// 64-bit number of seconds since epoch (1 January 1970 00:00:00) and returns
+/// the converted value.  64 bits are sufficient to represent all possible
+/// values for the valid format uniquely, so there is no overflow.
+///
+/// \note RFC4034 also defines the textual form of an unsigned decimal integer
+/// for the corresponding time in seconds.  This function doesn't support
+/// this form, and if given it throws an exception of class \c InvalidTime.
+///
+/// \exception InvalidTime The given textual representation is invalid.
+///
+/// \param time_txt Textual time in the form of YYYYMMDDHHmmSS
+/// \return Seconds since epoch corresponding to \c time_txt
+uint64_t
+timeFromText64(const std::string& time_txt);
 
+/// Convert textual DNSSEC time to integer, 32-bit version.
+///
+/// This version is the same as \c timeFromText64() except that the return
+/// value is wrapped around to an unsigned 32-bit integer, simply dropping
+/// the upper 32 bits.
+uint32_t
+timeFromText32(const std::string& time_txt);
+
+/// Convert integral DNSSEC time to textual form, 64-bit version.
+///
+/// This function takes an integer that would be seconds since epoch and
+/// converts it in the form of YYYYMMDDHHmmSS.  For example, if \c value is
+/// 0, it returns "19700101000000".  If the value corresponds to a point
+/// of time on and after year 10,000, which cannot be represented in the
+/// YYYY... form, an exception of class \c InvalidTime will be thrown.
+///
+/// \exception InvalidTime The given time specifies on or after year 10,000.
+/// \exception Other A standard exception, if resource allocation for the
+/// returned text fails.
+///
+/// \param value Seconds since epoch to be converted.
+/// \return Textual representation of \c value in the form of YYYYMMDDHHmmSS.
 std::string
-timeToText(const time_t timeval);
+timeToText64(uint64_t value);
+
+/// Convert integral DNSSEC time to textual form, 32-bit version.
+///
+/// This version is the same as \c timeToText64(), but the time value
+/// is expected to be the lower 32 bits of the full 64-bit value.
+/// These two will be different on and after a certain point of time
+/// in year 2106, so this function internally resolves the ambiguity
+/// using the current system time at the time of function call;
+/// it first identifies the range of [N*2^32 - 2^31, N*2^32 + 2^31)
+/// that contains the current time, and interprets \c value in the context
+/// of that range.  It then applies the same process as \c timeToText64().
+///
+/// There is one important exception in this processing, however.
+/// Until 19 Jan 2038 03:14:08 (2^31 seconds since epoch), this range
+/// would contain time before epoch.  In order to ensure the returned
+/// value is also a valid input to \c timeFromText, this function uses
+/// a special range [0, 2^32) until that time.  As a result, all upper
+/// half of the 32-bit values are treated as a future time.  For example,
+/// 2^32-1 (the highest value in 32-bit unsigned integers) will be converted
+/// to "21060207062815", instead of "19691231235959".
+std::string
+timeToText32(const uint32_t value);
+
+//@}
 }
 }
 
diff --git a/src/lib/dns/python/messagerenderer_python.cc b/src/lib/dns/python/messagerenderer_python.cc
index 91ab0c5..a00d8d4 100644
--- a/src/lib/dns/python/messagerenderer_python.cc
+++ b/src/lib/dns/python/messagerenderer_python.cc
@@ -37,9 +37,10 @@ static PyObject* MessageRenderer_getData(s_MessageRenderer* self);
 static PyObject* MessageRenderer_getLength(s_MessageRenderer* self);
 static PyObject* MessageRenderer_isTruncated(s_MessageRenderer* self);
 static PyObject* MessageRenderer_getLengthLimit(s_MessageRenderer* self);
-// TODO: set/get compressmode
+static PyObject* MessageRenderer_getCompressMode(s_MessageRenderer* self);
 static PyObject* MessageRenderer_setTruncated(s_MessageRenderer* self);
 static PyObject* MessageRenderer_setLengthLimit(s_MessageRenderer* self, PyObject* args);
+static PyObject* MessageRenderer_setCompressMode(s_MessageRenderer* self, PyObject* args);
 static PyObject* MessageRenderer_clear(s_MessageRenderer* self);
 
 static PyMethodDef MessageRenderer_methods[] = {
@@ -51,10 +52,14 @@ static PyMethodDef MessageRenderer_methods[] = {
       "Returns True if the data is truncated" },
     { "get_length_limit", reinterpret_cast<PyCFunction>(MessageRenderer_getLengthLimit), METH_NOARGS,
       "Returns the length limit of the data" },
+    { "get_compress_mode", reinterpret_cast<PyCFunction>(MessageRenderer_getCompressMode), METH_NOARGS,
+      "Returns the current compression mode" },
     { "set_truncated", reinterpret_cast<PyCFunction>(MessageRenderer_setTruncated), METH_NOARGS,
       "Sets truncated to true" },
     { "set_length_limit", reinterpret_cast<PyCFunction>(MessageRenderer_setLengthLimit), METH_VARARGS,
       "Sets the length limit of the data to the given number" },
+    { "set_compress_mode", reinterpret_cast<PyCFunction>(MessageRenderer_setCompressMode), METH_VARARGS,
+      "Sets the compression mode of the MessageRenderer" },
     { "clear", reinterpret_cast<PyCFunction>(MessageRenderer_clear),
       METH_NOARGS,
       "Clear the internal buffer and other internal resources." },
@@ -159,6 +164,11 @@ MessageRenderer_getLengthLimit(s_MessageRenderer* self) {
 }
 
 static PyObject*
+MessageRenderer_getCompressMode(s_MessageRenderer* self) {
+    return (Py_BuildValue("I", self->messagerenderer->getCompressMode()));
+}
+
+static PyObject*
 MessageRenderer_setTruncated(s_MessageRenderer* self) {
     self->messagerenderer->setTruncated();
     Py_RETURN_NONE;
@@ -177,6 +187,31 @@ MessageRenderer_setLengthLimit(s_MessageRenderer* self,
 }
 
 static PyObject*
+MessageRenderer_setCompressMode(s_MessageRenderer* self,
+                               PyObject* args)
+{
+    unsigned int mode;
+    if (!PyArg_ParseTuple(args, "I", &mode)) {
+        return (NULL);
+    }
+
+    if (mode == MessageRenderer::CASE_INSENSITIVE) {
+        self->messagerenderer->setCompressMode(MessageRenderer::CASE_INSENSITIVE);
+        // If we return NULL it is seen as an error, so use this for
+        // None returns, it also applies to CASE_SENSITIVE.
+        Py_RETURN_NONE;
+    } else if (mode == MessageRenderer::CASE_SENSITIVE) {
+        self->messagerenderer->setCompressMode(MessageRenderer::CASE_SENSITIVE);
+        Py_RETURN_NONE;
+    } else {
+        PyErr_SetString(PyExc_TypeError,
+                        "MessageRenderer compress mode must be MessageRenderer.CASE_INSENSITIVE"
+                        "or MessageRenderer.CASE_SENSITIVE");
+        return (NULL);
+    }
+}
+
+static PyObject*
 MessageRenderer_clear(s_MessageRenderer* self) {
     self->messagerenderer->clear();
     Py_RETURN_NONE;
@@ -203,6 +238,14 @@ initModulePart_MessageRenderer(PyObject* mod) {
         return (false);
     }
     Py_INCREF(&messagerenderer_type);
+
+    // Class variables
+    // These are added to the tp_dict of the type object
+    addClassVariable(messagerenderer_type, "CASE_INSENSITIVE",
+                     Py_BuildValue("I", MessageRenderer::CASE_INSENSITIVE));
+    addClassVariable(messagerenderer_type, "CASE_SENSITIVE",
+                     Py_BuildValue("I", MessageRenderer::CASE_SENSITIVE));
+
     PyModule_AddObject(mod, "MessageRenderer",
                        reinterpret_cast<PyObject*>(&messagerenderer_type));
     
diff --git a/src/lib/dns/python/tests/messagerenderer_python_test.py b/src/lib/dns/python/tests/messagerenderer_python_test.py
index 62e2d51..544ad23 100644
--- a/src/lib/dns/python/tests/messagerenderer_python_test.py
+++ b/src/lib/dns/python/tests/messagerenderer_python_test.py
@@ -28,7 +28,7 @@ class MessageRendererTest(unittest.TestCase):
         c = RRClass("IN")
         t = RRType("A")
         ttl = RRTTL("3600")
-        
+
         message = Message(Message.RENDER)
         message.set_qid(123)
         message.set_opcode(Opcode.QUERY())
@@ -56,14 +56,14 @@ class MessageRendererTest(unittest.TestCase):
         self.message1.to_wire(self.renderer1)
         self.message2.to_wire(self.renderer2)
         self.message2.to_wire(self.renderer3)
-        
-    
+
+
     def test_messagerenderer_get_data(self):
         data1 = b'\x00{\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x07example\x03com\x00\x00\x01\x00\x01'
         self.assertEqual(data1, self.renderer1.get_data())
         data2 = b'\x00{\x84\x00\x00\x01\x00\x00\x00\x02\x00\x00\x07example\x03com\x00\x00\x01\x00\x01\xc0\x0c\x00\x01\x00\x01\x00\x00\x0e\x10\x00\x04\xc0\x00\x02b\xc0\x0c\x00\x01\x00\x01\x00\x00\x0e\x10\x00\x04\xc0\x00\x02c'
         self.assertEqual(data2, self.renderer2.get_data())
-        
+
     def test_messagerenderer_get_length(self):
         self.assertEqual(29, self.renderer1.get_length())
         self.assertEqual(61, self.renderer2.get_length())
@@ -79,6 +79,14 @@ class MessageRendererTest(unittest.TestCase):
         self.assertEqual(512, self.renderer2.get_length_limit())
         self.assertEqual(50, self.renderer3.get_length_limit())
 
+    def test_messagerenderer_get_compress_mode(self):
+        self.assertEqual(MessageRenderer.CASE_INSENSITIVE,
+                         self.renderer1.get_compress_mode())
+        self.assertEqual(MessageRenderer.CASE_INSENSITIVE,
+                         self.renderer2.get_compress_mode())
+        self.assertEqual(MessageRenderer.CASE_INSENSITIVE,
+                         self.renderer3.get_compress_mode())
+
     def test_messagerenderer_set_truncated(self):
         self.assertFalse(self.renderer1.is_truncated())
         self.renderer1.set_truncated()
@@ -91,5 +99,17 @@ class MessageRendererTest(unittest.TestCase):
         self.assertEqual(1024, renderer.get_length_limit())
         self.assertRaises(TypeError, renderer.set_length_limit, "wrong")
 
+    def test_messagerenderer_set_compress_mode(self):
+        renderer = MessageRenderer()
+        self.assertEqual(MessageRenderer.CASE_INSENSITIVE,
+                         renderer.get_compress_mode())
+        renderer.set_compress_mode(MessageRenderer.CASE_SENSITIVE)
+        self.assertEqual(MessageRenderer.CASE_SENSITIVE,
+                         renderer.get_compress_mode())
+        renderer.set_compress_mode(MessageRenderer.CASE_INSENSITIVE)
+        self.assertEqual(MessageRenderer.CASE_INSENSITIVE,
+                         renderer.get_compress_mode())
+        self.assertRaises(TypeError, renderer.set_compress_mode, "wrong")
+
 if __name__ == '__main__':
     unittest.main()
diff --git a/src/lib/dns/rdata/generic/rrsig_46.cc b/src/lib/dns/rdata/generic/rrsig_46.cc
index 6e6c5fb..c9d1e52 100644
--- a/src/lib/dns/rdata/generic/rrsig_46.cc
+++ b/src/lib/dns/rdata/generic/rrsig_46.cc
@@ -93,8 +93,8 @@ RRSIG::RRSIG(const string& rrsig_str) :
         isc_throw(InvalidRdataText, "RRSIG labels out of range");
     }
 
-    uint32_t timeexpire = timeFromText(expire_txt);
-    uint32_t timeinception = timeFromText(inception_txt);
+    const uint32_t timeexpire = timeFromText32(expire_txt);
+    const uint32_t timeinception = timeFromText32(inception_txt);
 
     vector<uint8_t> signature;
     decodeBase64(signaturebuf.str(), signature);
@@ -157,15 +157,12 @@ RRSIG::~RRSIG() {
 
 string
 RRSIG::toText() const {
-    string expire = timeToText(impl_->timeexpire_);
-    string inception = timeToText(impl_->timeinception_);
-
     return (impl_->covered_.toText() +
             " " + boost::lexical_cast<string>(static_cast<int>(impl_->algorithm_))
             + " " + boost::lexical_cast<string>(static_cast<int>(impl_->labels_))
             + " " + boost::lexical_cast<string>(impl_->originalttl_)
-            + " " + expire
-            + " " + inception
+            + " " + timeToText32(impl_->timeexpire_)
+            + " " + timeToText32(impl_->timeinception_)
             + " " + boost::lexical_cast<string>(impl_->tag_)
             + " " + impl_->signer_.toText()
             + " " + encodeBase64(impl_->signature_));
diff --git a/src/lib/dns/tests/dnssectime_unittest.cc b/src/lib/dns/tests/dnssectime_unittest.cc
index 2479a29..b2708cc 100644
--- a/src/lib/dns/tests/dnssectime_unittest.cc
+++ b/src/lib/dns/tests/dnssectime_unittest.cc
@@ -23,48 +23,141 @@
 using namespace std;
 using namespace isc::dns;
 
+// See dnssectime.cc
+namespace isc {
+namespace dns {
+namespace dnssectime {
+namespace detail {
+extern int64_t (*gettimeFunction)();
+}
+}
+}
+}
+
 namespace {
 
-TEST(DNSSECTimeTest, fromText) {
+class DNSSECTimeTest : public ::testing::Test {
+protected:
+    ~DNSSECTimeTest() {
+        dnssectime::detail::gettimeFunction = NULL;
+    }
+};
+
+TEST_F(DNSSECTimeTest, fromText) {
+    // In most cases (in practice) the 32-bit and 64-bit versions should
+    // behave identically, so we'll mainly test the 32-bit version, which
+    // will be more commonly used in actual code (because many of the wire
+    // format time field are 32-bit).  The subtle cases where these two
+    // return different values will be tested at the end of this test case.
+
     // These are bogus and should be rejected
-    EXPECT_THROW(timeFromText("2011 101120000"), InvalidTime);
-    EXPECT_THROW(timeFromText("201101011200-0"), InvalidTime);
+    EXPECT_THROW(timeFromText32("2011 101120000"), InvalidTime);
+    EXPECT_THROW(timeFromText32("201101011200-0"), InvalidTime);
 
-    // Short length
-    EXPECT_THROW(timeFromText("20100223"), InvalidTime);
+    // Short length (or "decimal integer" version of representation;
+    // it's valid per RFC4034, but is not supported in this implementation)
+    EXPECT_THROW(timeFromText32("20100223"), InvalidTime);
 
     // Leap year checks
-    EXPECT_THROW(timeFromText("20110229120000"), InvalidTime);
-    EXPECT_THROW(timeFromText("21000229120000"), InvalidTime);
-    EXPECT_NO_THROW(timeFromText("20000229120000"));
-    EXPECT_NO_THROW(timeFromText("20120229120000"));
+    EXPECT_THROW(timeFromText32("20110229120000"), InvalidTime);
+    EXPECT_THROW(timeFromText32("21000229120000"), InvalidTime);
+    EXPECT_NO_THROW(timeFromText32("20000229120000"));
+    EXPECT_NO_THROW(timeFromText32("20120229120000"));
 
     // unusual case: this implementation allows SS=60 for "leap seconds"
-    EXPECT_NO_THROW(timeFromText("20110101120060"));
+    EXPECT_NO_THROW(timeFromText32("20110101120060"));
 
     // Out of range parameters
-    EXPECT_THROW(timeFromText("19100223214617"), InvalidTime); // YY<1970
-    EXPECT_THROW(timeFromText("20110001120000"), InvalidTime); // MM=00
-    EXPECT_THROW(timeFromText("20111301120000"), InvalidTime); // MM=13
-    EXPECT_THROW(timeFromText("20110100120000"), InvalidTime); // DD=00
-    EXPECT_THROW(timeFromText("20110132120000"), InvalidTime); // DD=32
-    EXPECT_THROW(timeFromText("20110431120000"), InvalidTime); // 'Apr31'
-    EXPECT_THROW(timeFromText("20110101250000"), InvalidTime); // HH=25
-    EXPECT_THROW(timeFromText("20110101126000"), InvalidTime); // mm=60
-    EXPECT_THROW(timeFromText("20110101120061"), InvalidTime); // SS=61
+    EXPECT_THROW(timeFromText32("19100223214617"), InvalidTime); // YY<1970
+    EXPECT_THROW(timeFromText32("20110001120000"), InvalidTime); // MM=00
+    EXPECT_THROW(timeFromText32("20111301120000"), InvalidTime); // MM=13
+    EXPECT_THROW(timeFromText32("20110100120000"), InvalidTime); // DD=00
+    EXPECT_THROW(timeFromText32("20110132120000"), InvalidTime); // DD=32
+    EXPECT_THROW(timeFromText32("20110431120000"), InvalidTime); // 'Apr31'
+    EXPECT_THROW(timeFromText32("20110101250000"), InvalidTime); // HH=25
+    EXPECT_THROW(timeFromText32("20110101126000"), InvalidTime); // mm=60
+    EXPECT_THROW(timeFromText32("20110101120061"), InvalidTime); // SS=61
+
+    // Feb 7, 06:28:15 UTC 2106 is the possible maximum time that can be
+    // represented as an unsigned 32bit integer without overflow.
+    EXPECT_EQ(4294967295LU, timeFromText32("21060207062815"));
+
+    // After that, timeFromText32() should start returning the second count
+    // modulo 2^32.
+    EXPECT_EQ(0, timeFromText32("21060207062816"));
+    EXPECT_EQ(10, timeFromText32("21060207062826"));
+
+    // On the other hand, the 64-bit version should return monotonically
+    // increasing counters.
+    EXPECT_EQ(4294967296LL, timeFromText64("21060207062816"));
+    EXPECT_EQ(4294967306LL, timeFromText64("21060207062826"));
 }
 
-TEST(DNSSECTimeTest, toText) {
-    EXPECT_EQ("19700101000000", timeToText(0));
-    EXPECT_EQ("20100311233000", timeToText(1268350200));
+// This helper templated function tells timeToText32 a faked current time.
+// The template parameter is that faked time in the form of int64_t seconds
+// since epoch.
+template <int64_t NOW>
+int64_t
+testGetTime() {
+    return (NOW);
 }
 
-TEST(DNSSECTimeTest, overflow) {
+// Seconds since epoch for the year 10K eve.  Commonly used in some tests
+// below.
+const uint64_t YEAR10K_EVE = 253402300799LL;
+
+TEST_F(DNSSECTimeTest, toText) {
+    // Check a basic case with the default (normal) gettimeFunction
+    // based on the "real current time".
+    // Note: this will fail after year 2078, but at that point we won't use
+    // this program anyway:-)
+    EXPECT_EQ("20100311233000", timeToText32(1268350200));
+
+    // Set the current time to: Feb 18 09:04:14 UTC 2012 (an arbitrary choice
+    // in the range of the first half of uint32 since epoch).
+    dnssectime::detail::gettimeFunction = testGetTime<1329555854LL>;
+
+    // Test the "year 2038" problem.
+    // Check the result of toText() for "INT_MIN" in int32_t.  It's in the
+    // 68-year range from the faked current time, so the result should be
+    // in year 2038, instead of 1901.
+    EXPECT_EQ("20380119031408", timeToText64(0x80000000L));
+    EXPECT_EQ("20380119031408", timeToText32(0x80000000L));
+
+    // A controversial case: what should we do with "-1"?  It's out of range
+    // in future, but according to RFC time before epoch doesn't seem to be
+    // considered "in-range" either.  Our toText() implementation handles
+    // this range as a special case and always treats them as future time
+    // until year 2038.  This won't be a real issue in practice, though,
+    // since such too large values won't be used in actual deployment by then.
+    EXPECT_EQ("21060207062815", timeToText32(0xffffffffL));
+
+    // After the singular point of year 2038, the first half of uint32 can
+    // point to a future time.
+    // Set the current time to: Apr 1 00:00:00 UTC 2038:
+    dnssectime::detail::gettimeFunction = testGetTime<2153692800LL>;
+    // then time "10" is Feb 7 06:28:26 UTC 2106
+    EXPECT_EQ("21060207062826", timeToText32(10));
+    // in 64-bit, it's 2^32 + 10
+    EXPECT_EQ("21060207062826", timeToText64(0x10000000aLL));
+
+    // After year 2106, the upper half of uint32 can point to past time
+    // (as it should).
+    dnssectime::detail::gettimeFunction = testGetTime<0x10000000aLL>;
+    EXPECT_EQ("21060207062815", timeToText32(0xffffffffL));
+
+    // Try very large time value.  Actually it's the possible farthest time
+    // that can be represented in the form of YYYYMMDDHHmmSS.
+    EXPECT_EQ("99991231235959", timeToText64(YEAR10K_EVE));
+    dnssectime::detail::gettimeFunction = testGetTime<YEAR10K_EVE - 10>;
+    EXPECT_EQ("99991231235959", timeToText32(4294197631LU));
+}
+
+TEST_F(DNSSECTimeTest, overflow) {
     // Jan 1, Year 10,000.
-    if (sizeof(time_t) > 4) {
-        EXPECT_THROW(timeToText(static_cast<time_t>(253402300800LL)),
-                     InvalidTime);
-    }
+    EXPECT_THROW(timeToText64(253402300800LL), InvalidTime);
+    dnssectime::detail::gettimeFunction = testGetTime<YEAR10K_EVE - 10>;
+    EXPECT_THROW(timeToText32(4294197632LU), InvalidTime);
 }
 
 }
diff --git a/src/lib/dns/tests/rdata_mx_unittest.cc b/src/lib/dns/tests/rdata_mx_unittest.cc
index 4491f86..dd7677d 100644
--- a/src/lib/dns/tests/rdata_mx_unittest.cc
+++ b/src/lib/dns/tests/rdata_mx_unittest.cc
@@ -74,12 +74,9 @@ TEST_F(Rdata_MX_Test, toWireRenderer) {
 TEST_F(Rdata_MX_Test, toWireBuffer) {
     renderer.writeName(Name("example.com"));
     rdata_mx.toWire(obuffer);
-}
 
-TEST_F(Rdata_MX_Test, DISABLED_toWireBuffer) {
-// XXX: does not pass
     vector<unsigned char> data;
-    UnitTestUtil::readWireData("rdata_mx_toWire1", data);
+    UnitTestUtil::readWireData("rdata_mx_toWire2", data);
     EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, obuffer.getData(),
                         obuffer.getLength(), &data[0], data.size());
 }
diff --git a/src/lib/dns/tests/testdata/Makefile.am b/src/lib/dns/tests/testdata/Makefile.am
index bbba8ed..1aaddb6 100644
--- a/src/lib/dns/tests/testdata/Makefile.am
+++ b/src/lib/dns/tests/testdata/Makefile.am
@@ -50,7 +50,8 @@ EXTRA_DIST += name_toWire5.spec name_toWire6.spec
 EXTRA_DIST += question_fromWire question_toWire1 question_toWire2
 EXTRA_DIST += rdata_cname_fromWire rdata_dname_fromWire rdata_dnskey_fromWire
 EXTRA_DIST += rdata_ds_fromWire rdata_in_a_fromWire rdata_in_aaaa_fromWire
-EXTRA_DIST += rdata_mx_fromWire rdata_mx_toWire1 rdata_ns_fromWire
+EXTRA_DIST += rdata_mx_fromWire rdata_mx_toWire1 rdata_mx_toWire2
+EXTRA_DIST += rdata_ns_fromWire
 EXTRA_DIST += rdata_nsec_fromWire1 rdata_nsec_fromWire2 rdata_nsec_fromWire3
 EXTRA_DIST += rdata_nsec_fromWire4.spec rdata_nsec_fromWire5.spec
 EXTRA_DIST += rdata_nsec_fromWire6.spec rdata_nsec_fromWire7.spec
diff --git a/src/lib/dns/tests/testdata/rdata_mx_toWire2 b/src/lib/dns/tests/testdata/rdata_mx_toWire2
new file mode 100644
index 0000000..ebd2f27
--- /dev/null
+++ b/src/lib/dns/tests/testdata/rdata_mx_toWire2
@@ -0,0 +1,12 @@
+#
+# compressed MX RDATA stored in an output buffer
+#
+# sentinel name: example.com.
+# 0  1  2  3  4  5  6  7  8  9 10  1  2 (bytes)
+#(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
+# PREFERENCE: 10
+  00 0a
+# EXCHANGE: not compressed
+#(4) m  x ptr=0
+ 02 6d 78 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
diff --git a/src/lib/log/Makefile.am b/src/lib/log/Makefile.am
index 416fd06..5bcafa8 100644
--- a/src/lib/log/Makefile.am
+++ b/src/lib/log/Makefile.am
@@ -2,7 +2,6 @@ SUBDIRS = . compiler tests
 
 AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
 AM_CPPFLAGS += $(BOOST_INCLUDES)
-# AM_CPPFLAGS += $(LOG4CXX_INCLUDES)
 AM_CPPFLAGS += -I$(top_srcdir)/src/lib/log -I$(top_builddir)/src/lib/log
 
 CLEANFILES = *.gcno *.gcda
diff --git a/src/lib/nsas/Makefile.am b/src/lib/nsas/Makefile.am
index a88bd22..04a765b 100644
--- a/src/lib/nsas/Makefile.am
+++ b/src/lib/nsas/Makefile.am
@@ -25,6 +25,7 @@ libnsas_la_SOURCES += asiolink.h
 libnsas_la_SOURCES += hash.cc hash.h
 libnsas_la_SOURCES += hash_deleter.h
 libnsas_la_SOURCES += hash_key.cc hash_key.h
+libnsas_la_SOURCES += locks.h
 libnsas_la_SOURCES += hash_table.h
 libnsas_la_SOURCES += lru_list.h
 libnsas_la_SOURCES += nameserver_address_store.cc nameserver_address_store.h
diff --git a/src/lib/nsas/hash_table.h b/src/lib/nsas/hash_table.h
index 3c34ee9..e46d687 100644
--- a/src/lib/nsas/hash_table.h
+++ b/src/lib/nsas/hash_table.h
@@ -15,25 +15,11 @@
 #ifndef __HASH_TABLE_H
 #define __HASH_TABLE_H
 
-// Workaround for a problem with boost and sunstudio 5.10
-// There is a version check in there that appears wrong,
-// which makes including boost/thread.hpp fail
-// This will probably be fixed in a future version of boost,
-// in which case this part can be removed then
-#ifdef NEED_SUNPRO_WORKAROUND
-#if defined(__SUNPRO_CC) && __SUNPRO_CC == 0x5100
-#undef __SUNPRO_CC
-#define __SUNPRO_CC 0x5090
-#endif
-#endif // NEED_SUNPRO_WORKAROUND
+#include <list>
 
 #include <boost/shared_ptr.hpp>
-#include <boost/thread.hpp>
-#include <boost/interprocess/sync/sharable_lock.hpp>
-#include <boost/interprocess/sync/scoped_lock.hpp>
-#include <boost/interprocess/sync/interprocess_upgradable_mutex.hpp>
-#include <list>
 
+#include "locks.h"
 #include "hash.h"
 #include "hash_key.h"
 
@@ -61,7 +47,7 @@ struct HashTableSlot {
     typedef typename std::list<boost::shared_ptr<T> >::iterator  iterator;
                                     ///< Iterator over elements with same hash
 
-    typedef boost::interprocess::interprocess_upgradable_mutex mutex_type;
+    typedef isc::locks::upgradable_mutex mutex_type;
                                     ///< Mutex protecting this slot
     //@}
 
@@ -128,11 +114,11 @@ public:
     ///
     //@{
     typedef typename
-    boost::interprocess::sharable_lock<typename HashTableSlot<T>::mutex_type>
+    isc::locks::sharable_lock<typename HashTableSlot<T>::mutex_type>
     sharable_lock;                  ///< Type for a scope-limited read-lock
 
     typedef typename
-    boost::interprocess::scoped_lock<typename HashTableSlot<T>::mutex_type>
+    isc::locks::scoped_lock<typename HashTableSlot<T>::mutex_type>
     scoped_lock;                    ///< Type for a scope-limited write-lock
     //@}
 
diff --git a/src/lib/nsas/locks.h b/src/lib/nsas/locks.h
new file mode 100644
index 0000000..98197c3
--- /dev/null
+++ b/src/lib/nsas/locks.h
@@ -0,0 +1,116 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+/// This file (right now) provides dummy locks
+/// It also contains code to use boost/threads locks:
+///
+/// if USE_BOOST_THREADS is defined, we typedef the relevant classes
+/// and derive from the relevant templates so our dummy locks are
+/// replaced by the boost locks (--enable-boost-threads)
+///
+/// If USE_BOOST_THREADS is NOT defined, all locks are dummy classes
+/// that don't actually do anything. At this moment, only the very
+/// minimal set of methods that we actually use is defined.
+///
+/// Note that we need to include <config.h> in our .cc files for that
+/// to be set. we might want to enfore this at compile time with a check
+/// (TODO)
+/// Note that this also contains a workaround for Sunstudio; which
+/// probably won't completely work right now (that is, if the TODO
+/// above is completed), since that would also require some changes
+/// in most (at first glance unrelated) Makefiles
+/// (TODO2)
+
+#ifndef __LOCKS_
+#define __LOCKS_
+
+#ifndef USE_BOOST_THREADS
+
+namespace isc {
+namespace locks {
+
+class mutex {
+};
+
+class recursive_mutex {
+};
+
+class upgradable_mutex {
+};
+
+template <typename T>
+class sharable_lock {
+public:
+    sharable_lock(T) { }
+};
+
+template <typename T>
+class scoped_lock {
+public:
+    scoped_lock(T) { }
+
+    void lock() {}
+    void unlock() {}
+};
+
+}
+}
+
+#else // USE_BOOST_THREADS
+
+// Workaround for a problem with boost and sunstudio 5.10
+// There is a version check in there that appears wrong,
+// which makes including boost/thread.hpp fail
+// This will probably be fixed in a future version of boost,
+// in which case this part can be removed then
+#ifdef NEED_SUNPRO_WORKAROUND
+#if defined(__SUNPRO_CC) && __SUNPRO_CC == 0x5100
+#undef __SUNPRO_CC
+#define __SUNPRO_CC 0x5090
+#endif
+#endif // NEED_SUNPRO_WORKAROUND
+
+#include <boost/thread.hpp>
+#include <boost/interprocess/sync/sharable_lock.hpp>
+#include <boost/interprocess/sync/scoped_lock.hpp>
+#include <boost/interprocess/sync/interprocess_upgradable_mutex.hpp>
+#include <boost/interprocess/sync/interprocess_recursive_mutex.hpp>
+
+namespace isc {
+namespace locks {
+
+typedef boost::mutex mutex;
+typedef boost::interprocess::interprocess_upgradable_mutex upgradable_mutex;
+typedef boost::interprocess::interprocess_recursive_mutex recursive_mutex;
+
+template <typename T>
+struct sharable_lock : public boost::interprocess::sharable_lock<T> {
+public:
+    sharable_lock(T&  mtype) : boost::interprocess::sharable_lock<T>(mtype) {}
+};
+
+
+template <class T>
+struct scoped_lock : public boost::interprocess::scoped_lock<T> {
+public:
+    scoped_lock(T& mtype) : boost::interprocess::scoped_lock<T>(mtype) { }
+};
+
+}
+}
+
+
+#endif // USE_BOOST_THREADS
+
+#endif // __LOCKS_
diff --git a/src/lib/nsas/lru_list.h b/src/lib/nsas/lru_list.h
index a3e0974..993eb89 100644
--- a/src/lib/nsas/lru_list.h
+++ b/src/lib/nsas/lru_list.h
@@ -18,22 +18,10 @@
 #include <list>
 #include <string>
 
-// Workaround for a problem with boost and sunstudio 5.10
-// There is a version check in there that appears wrong,
-// which makes including boost/thread.hpp fail
-// This will probably be fixed in a future version of boost,
-// in which case this part can be removed then
-#ifdef NEED_SUNPRO_WORKAROUND
-#if defined(__SUNPRO_CC) && __SUNPRO_CC == 0x5100
-#undef __SUNPRO_CC
-#define __SUNPRO_CC 0x5090
-#endif
-#endif // NEED_SUNPRO_WORKAROUND
-
 #include <boost/noncopyable.hpp>
 #include <boost/shared_ptr.hpp>
-#include <boost/thread.hpp>
-#include <boost/interprocess/sync/scoped_lock.hpp>
+
+#include "locks.h"
 
 namespace isc {
 namespace nsas {
@@ -151,7 +139,7 @@ public:
     }
 
 private:
-    boost::mutex                        mutex_;     ///< List protection
+    isc::locks::mutex                   mutex_;     ///< List protection
     std::list<boost::shared_ptr<T> >    lru_;       ///< The LRU list itself
     uint32_t                            max_size_;  ///< Max size of the list
     uint32_t                            count_;     ///< Count of elements
@@ -163,7 +151,7 @@ template <typename T>
 void LruList<T>::add(boost::shared_ptr<T>& element) {
 
     // Protect list against concurrent access
-    boost::interprocess::scoped_lock<boost::mutex> lock(mutex_);
+    isc::locks::scoped_lock<isc::locks::mutex> lock(mutex_);
 
     // Add the entry and set its pointer field to point into the list.
     // insert() is used to get the pointer.
@@ -212,7 +200,7 @@ void LruList<T>::remove(boost::shared_ptr<T>& element) {
     if (element->iteratorValid()) {
 
         // Is valid, so protect list against concurrent access
-        boost::interprocess::scoped_lock<boost::mutex> lock(mutex_);
+        isc::locks::scoped_lock<isc::locks::mutex> lock(mutex_);
 
         lru_.erase(element->getLruIterator());  // Remove element from list
         element->invalidateIterator();          // Invalidate pointer
@@ -228,7 +216,7 @@ void LruList<T>::touch(boost::shared_ptr<T>& element) {
     if (element->iteratorValid()) {
 
         // Protect list against concurrent access
-        boost::interprocess::scoped_lock<boost::mutex> lock(mutex_);
+        isc::locks::scoped_lock<isc::locks::mutex> lock(mutex_);
 
         // Move the element to the end of the list.
         lru_.splice(lru_.end(), lru_, element->getLruIterator());
diff --git a/src/lib/nsas/nameserver_address_store.cc b/src/lib/nsas/nameserver_address_store.cc
index 0ba9c8e..7bb0eee 100644
--- a/src/lib/nsas/nameserver_address_store.cc
+++ b/src/lib/nsas/nameserver_address_store.cc
@@ -14,20 +14,6 @@
 
 #include <config.h>
 
-// Workaround for a problem with boost and sunstudio 5.10
-// There is a version check in there that appears wrong,
-// which makes including boost/thread.hpp fail
-// This will probably be fixed in a future version of boost,
-// in which case this part can be removed then
-#ifdef NEED_SUNPRO_WORKAROUND
-#if defined(__SUNPRO_CC) && __SUNPRO_CC == 0x5100
-#undef __SUNPRO_CC
-#define __SUNPRO_CC 0x5090
-#endif
-#endif // NEED_SUNPRO_WORKAROUND
-
-
-#include <boost/thread.hpp>
 #include <boost/shared_ptr.hpp>
 #include <boost/foreach.hpp>
 #include <boost/bind.hpp>
@@ -35,6 +21,7 @@
 #include <config.h>
 #include <dns/rdataclass.h>
 
+#include "locks.h"
 #include "hash_table.h"
 #include "lru_list.h"
 #include "hash_deleter.h"
diff --git a/src/lib/nsas/nameserver_entry.cc b/src/lib/nsas/nameserver_entry.cc
index 53f4233..9522e81 100644
--- a/src/lib/nsas/nameserver_entry.cc
+++ b/src/lib/nsas/nameserver_entry.cc
@@ -50,7 +50,7 @@ namespace nsas {
 namespace {
 
 // Just shorter type alias
-typedef boost::recursive_mutex::scoped_lock Lock;
+typedef isc::locks::scoped_lock<isc::locks::recursive_mutex> Lock;
 
 }
 
diff --git a/src/lib/nsas/nameserver_entry.h b/src/lib/nsas/nameserver_entry.h
index 9a8e542..c3ddcd4 100644
--- a/src/lib/nsas/nameserver_entry.h
+++ b/src/lib/nsas/nameserver_entry.h
@@ -15,21 +15,8 @@
 #ifndef __NAMESERVER_ENTRY_H
 #define __NAMESERVER_ENTRY_H
 
-// Workaround for a problem with boost and sunstudio 5.10
-// There is a version check in there that appears wrong,
-// which makes including boost/thread.hpp fail
-// This will probably be fixed in a future version of boost,
-// in which case this part can be removed then
-#ifdef NEED_SUNPRO_WORKAROUND
-#if defined(__SUNPRO_CC) && __SUNPRO_CC == 0x5100
-#undef __SUNPRO_CC
-#define __SUNPRO_CC 0x5090
-#endif
-#endif // NEED_SUNPRO_WORKAROUND
-
 #include <string>
 #include <vector>
-#include <boost/thread.hpp>
 #include <boost/enable_shared_from_this.hpp>
 
 #include <exceptions/exceptions.h>
@@ -252,7 +239,7 @@ public:
     //@}
 
 private:
-    mutable boost::recursive_mutex    mutex_;     ///< Mutex protecting this object
+    mutable isc::locks::recursive_mutex    mutex_;///< Mutex protecting this object
     std::string     name_;              ///< Canonical name of the nameserver
     isc::dns::RRClass classCode_;       ///< Class of the nameserver
     /**
diff --git a/src/lib/nsas/tests/nsas_test.h b/src/lib/nsas/tests/nsas_test.h
index b4446a4..926e859 100644
--- a/src/lib/nsas/tests/nsas_test.h
+++ b/src/lib/nsas/tests/nsas_test.h
@@ -22,6 +22,7 @@
 
 #include <string>
 #include <vector>
+#include <map>
 
 #include <config.h>
 
diff --git a/src/lib/nsas/zone_entry.cc b/src/lib/nsas/zone_entry.cc
index 395b06c..77f3dad 100644
--- a/src/lib/nsas/zone_entry.cc
+++ b/src/lib/nsas/zone_entry.cc
@@ -12,6 +12,8 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include <map>
+
 #include <config.h>
 
 #include "zone_entry.h"
@@ -49,7 +51,7 @@ ZoneEntry::ZoneEntry(
 
 namespace {
 // Shorter aliases for frequently used types
-typedef boost::recursive_mutex::scoped_lock Lock; // Local lock, nameservers not locked
+typedef isc::locks::scoped_lock<isc::locks::recursive_mutex> Lock; // Local lock, nameservers not locked
 typedef boost::shared_ptr<AddressRequestCallback> CallbackPtr;
 
 /*
diff --git a/src/lib/nsas/zone_entry.h b/src/lib/nsas/zone_entry.h
index c819692..28a42ea 100644
--- a/src/lib/nsas/zone_entry.h
+++ b/src/lib/nsas/zone_entry.h
@@ -15,22 +15,9 @@
 #ifndef __ZONE_ENTRY_H
 #define __ZONE_ENTRY_H
 
-// Workaround for a problem with boost and sunstudio 5.10
-// There is a version check in there that appears wrong,
-// which makes including boost/thread.hpp fail
-// This will probably be fixed in a future version of boost,
-// in which case this part can be removed then
-#ifdef NEED_SUNPRO_WORKAROUND
-#if defined(__SUNPRO_CC) && __SUNPRO_CC == 0x5100
-#undef __SUNPRO_CC
-#define __SUNPRO_CC 0x5090
-#endif
-#endif // NEED_SUNPRO_WORKAROUND
-
 #include <string>
 #include <vector>
 #include <set>
-#include <boost/thread.hpp>
 #include <boost/shared_ptr.hpp>
 #include <boost/enable_shared_from_this.hpp>
 
@@ -38,6 +25,7 @@
 
 #include <resolve/resolver_interface.h>
 
+#include "locks.h"
 #include "hash_key.h"
 #include "nsas_entry.h"
 #include "asiolink.h"
@@ -131,7 +119,7 @@ protected:
     time_t          expiry_;    ///< Expiry time of this entry, 0 means not set
     //}@
 private:
-    mutable boost::recursive_mutex    mutex_;     ///< Mutex protecting this zone entry
+    mutable isc::locks::recursive_mutex    mutex_;///< Mutex protecting this zone entry
     std::string     name_;      ///< Canonical zone name
     isc::dns::RRClass        class_code_; ///< Class code
     /**
diff --git a/src/lib/python/isc/cc/data.py b/src/lib/python/isc/cc/data.py
index c75fbfe..ce1bba0 100644
--- a/src/lib/python/isc/cc/data.py
+++ b/src/lib/python/isc/cc/data.py
@@ -100,8 +100,8 @@ def split_identifier_list_indices(identifier):
 
     i = id_str.find('[')
     if i < 0:
-        if identifier.find(']') >= 0:
-            raise DataTypeError("Bad format in identifier: " + str(identifier))
+        if id_str.find(']') >= 0:
+            raise DataTypeError("Bad format in identifier (] but no [): " + str(identifier))
         return identifier, None
 
     # keep the non-index part of that to replace later
@@ -110,7 +110,7 @@ def split_identifier_list_indices(identifier):
     while i >= 0:
         e = id_str.find(']')
         if e < i + 1:
-            raise DataTypeError("Bad format in identifier: " + str(identifier))
+            raise DataTypeError("Bad format in identifier (] before [): " + str(identifier))
         try:
             indices.append(int(id_str[i+1:e]))
         except ValueError:
@@ -118,9 +118,9 @@ def split_identifier_list_indices(identifier):
         id_str = id_str[e + 1:]
         i = id_str.find('[')
         if i > 0:
-            raise DataTypeError("Bad format in identifier: " + str(identifier))
+            raise DataTypeError("Bad format in identifier ([ within []): " + str(identifier))
     if id.find(']') >= 0 or len(id_str) > 0:
-        raise DataTypeError("Bad format in identifier: " + str(identifier))
+        raise DataTypeError("Bad format in identifier (extra ]): " + str(identifier))
 
     # we replace the final part of the original identifier with
     # the stripped string
diff --git a/src/lib/python/isc/config/ccsession.py b/src/lib/python/isc/config/ccsession.py
index e7fdb86..0e602b7 100644
--- a/src/lib/python/isc/config/ccsession.py
+++ b/src/lib/python/isc/config/ccsession.py
@@ -374,20 +374,34 @@ class UIModuleCCSession(MultiConfigData):
         self._set_current_config(config)
 
 
-    def add_value(self, identifier, value_str):
+    def add_value(self, identifier, value_str = None):
         """Add a value to a configuration list. Raises a DataTypeError
            if the value does not conform to the list_item_spec field
-           of the module config data specification"""
+           of the module config data specification. If value_str is
+           not given, we add the default as specified by the .spec
+           file."""
         module_spec = self.find_spec_part(identifier)
         if (type(module_spec) != dict or "list_item_spec" not in module_spec):
             raise isc.cc.data.DataNotFoundError(str(identifier) + " is not a list")
-        value = isc.cc.data.parse_value_str(value_str)
+
         cur_list, status = self.get_value(identifier)
         if not cur_list:
             cur_list = []
+
+        # Hmm. Do we need to check for duplicates?
+        value = None
+        if value_str is not None:
+            value = isc.cc.data.parse_value_str(value_str)
+        else:
+            if "item_default" in module_spec["list_item_spec"]:
+                value = module_spec["list_item_spec"]["item_default"]
+
+        if value is None:
+            raise isc.cc.data.DataNotFoundError("No value given and no default for " + str(identifier))
+            
         if value not in cur_list:
             cur_list.append(value)
-        self.set_value(identifier, cur_list)
+            self.set_value(identifier, cur_list)
 
     def remove_value(self, identifier, value_str):
         """Remove a value from a configuration list. The value string
diff --git a/src/lib/python/isc/config/config_data.py b/src/lib/python/isc/config/config_data.py
index 1e3afd1..582c11c 100644
--- a/src/lib/python/isc/config/config_data.py
+++ b/src/lib/python/isc/config/config_data.py
@@ -121,6 +121,7 @@ def find_spec_part(element, identifier):
         # strip list selector part
         # don't need it for the spec part, so just drop it
         id, list_indices = isc.cc.data.split_identifier_list_indices(id_part)
+        # is this part still needed? (see below)
         if type(cur_el) == dict and 'map_item_spec' in cur_el.keys():
             found = False
             for cur_el_item in cur_el['map_item_spec']:
@@ -128,15 +129,24 @@ def find_spec_part(element, identifier):
                     cur_el = cur_el_item
                     found = True
             if not found:
-                raise isc.cc.data.DataNotFoundError(id + " in " + str(cur_el))
+                raise isc.cc.data.DataNotFoundError(id + " not found")
+        elif type(cur_el) == dict and 'list_item_spec' in cur_el.keys():
+            cur_el = cur_el['list_item_spec']
         elif type(cur_el) == list:
             found = False
             for cur_el_item in cur_el:
                 if cur_el_item['item_name'] == id:
                     cur_el = cur_el_item
+                    # if we need to go further, we may need to 'skip' a step here
+                    # but not if we're done
+                    if id_parts[-1] != id_part and type(cur_el) == dict:
+                        if "map_item_spec" in cur_el:
+                            cur_el = cur_el["map_item_spec"]
+                        elif "list_item_spec" in cur_el:
+                            cur_el = cur_el["list_item_spec"]
                     found = True
             if not found:
-                raise isc.cc.data.DataNotFoundError(id + " in " + str(cur_el))
+                raise isc.cc.data.DataNotFoundError(id + " not found")
         else:
             raise isc.cc.data.DataNotFoundError("Not a correct config specification")
     return cur_el
@@ -354,26 +364,63 @@ class MultiConfigData:
            See get_value() for a general way to find a configuration
            value
         """
-        if identifier[0] == '/':
-            identifier = identifier[1:]
-        module, sep, id = identifier.partition("/")
         try:
+            if identifier[0] == '/':
+                identifier = identifier[1:]
+            module, sep, id = identifier.partition("/")
+            # if there is a 'higher-level' list index specified, we need
+            # to check if that list specification has a default that
+            # overrides the more specific default in the final spec item
+            # (ie. list_default = [1, 2, 3], list_item_spec=int, default=0)
+            # def default list[1] should return 2, not 0
+            id_parts = isc.cc.data.split_identifier(id)
+            id_prefix = ""
+            while len(id_parts) > 0:
+                id_part = id_parts.pop(0)
+                item_id, list_indices = isc.cc.data.split_identifier_list_indices(id_part)
+                id_list = module + "/" + id_prefix + "/" + item_id
+                id_prefix += "/" + id_part
+                if list_indices is not None:
+                    # there's actually two kinds of default here for
+                    # lists; they can have a default value (like an
+                    # empty list), but their elements can  also have
+                    # default values.
+                    # So if the list item *itself* is a default,
+                    # we need to get the value out of that. If not, we
+                    # need to find the default for the specific element.
+                    list_value, type = self.get_value(id_list) 
+                    list_spec = find_spec_part(self._specifications[module].get_config_spec(), id_prefix)
+                    if type == self.DEFAULT:
+                        if 'item_default' in list_spec:
+                            list_value = list_spec['item_default']
+                            for i in list_indices:
+                                if i < len(list_value):
+                                    list_value = list_value[i]
+                                else:
+                                    # out of range, return None
+                                    return None
+                                
+                            if len(id_parts) > 0:
+                                rest_of_id = "/".join(id_parts)
+                                return isc.cc.data.find(list_value, rest_of_id)
+                            else:
+                                return list_value
+                    else:
+                        # we do have a non-default list, see if our indices
+                        # exist
+                        for i in list_indices:
+                            if i < len(list_value):
+                                list_value = list_value[i]
+                            else:
+                                # out of range, return None
+                                return None
+                    
             spec = find_spec_part(self._specifications[module].get_config_spec(), id)
             if 'item_default' in spec:
-                id, list_indices = isc.cc.data.split_identifier_list_indices(id)
-                if list_indices is not None and \
-                   type(spec['item_default']) == list:
-                    if len(list_indices) == 1:
-                        default_list = spec['item_default']
-                        index = list_indices[0]
-                        if index < len(default_list):
-                            return default_list[index]
-                        else:
-                            return None
-                else:
-                    return spec['item_default']
+                return spec['item_default']
             else:
                 return None
+
         except isc.cc.data.DataNotFoundError as dnfe:
             return None
 
@@ -398,13 +445,60 @@ class MultiConfigData:
                 return value, self.DEFAULT
         return None, self.NONE
 
-    def get_value_maps(self, identifier = None):
+    def _append_value_item(self, result, spec_part, identifier, all, first = False):
+        # Look at the spec; it is a list of items, or a map containing 'item_name' etc
+        if type(spec_part) == list:
+            for spec_part_element in spec_part:
+                spec_part_element_name = spec_part_element['item_name']
+                self._append_value_item(result, spec_part_element, identifier + "/" + spec_part_element_name, all)
+        elif type(spec_part) == dict:
+            # depending on item type, and the value of argument 'all'
+            # we need to either add an item, or recursively go on
+            # In the case of a list that is empty, we do need to show that
+            item_name = spec_part['item_name']
+            item_type = spec_part['item_type']
+            if item_type == "list" and (all or first):
+                spec_part_list = spec_part['list_item_spec']
+                list_value, status = self.get_value(identifier)
+                if list_value is None:
+                    print("Error: identifier '%s' not found" % identifier)
+                    return
+                if type(list_value) != list:
+                    # the identifier specified a single element
+                    self._append_value_item(result, spec_part_list, identifier, all)
+                else:
+                    list_len = len(list_value)
+                    if len(list_value) == 0 and (all or first):
+                        entry = _create_value_map_entry(identifier,
+                                                        item_type,
+                                                        [], status)
+                        result.append(entry)
+                    else:
+                        for i in range(len(list_value)):
+                            self._append_value_item(result, spec_part_list, "%s[%d]" % (identifier, i), all)
+            elif item_type == "map":
+                # just show the specific contents of a map, we are
+                # almost never interested in just its name
+                spec_part_map = spec_part['map_item_spec']
+                self._append_value_item(result, spec_part_map, identifier, all)
+            else:
+                value, status = self.get_value(identifier)
+                entry = _create_value_map_entry(identifier,
+                                                item_type,
+                                                value, status)
+                result.append(entry)
+        return
+
+
+    def get_value_maps(self, identifier = None, all = False):
         """Returns a list of dicts, containing the following values:
            name: name of the entry (string)
            type: string containing the type of the value (or 'module')
            value: value of the entry if it is a string, int, double or bool
-           modified: true if the value is a local change
-           default: true if the value has been changed
+           modified: true if the value is a local change that has not
+                     been committed
+           default: true if the value has not been changed (i.e. the
+                    value is the default from the specification)
            TODO: use the consts for those last ones
            Throws DataNotFoundError if the identifier is bad
         """
@@ -412,8 +506,14 @@ class MultiConfigData:
         if not identifier:
             # No identifier, so we need the list of current modules
             for module in self._specifications.keys():
-                entry = _create_value_map_entry(module, 'module', None)
-                result.append(entry)
+                if all:
+                    spec = self.get_module_spec(module)
+                    if spec:
+                        spec_part = spec.get_config_spec()
+                        self._append_value_item(result, spec_part, module, all, True)
+                else:
+                    entry = _create_value_map_entry(module, 'module', None)
+                    result.append(entry)
         else:
             if identifier[0] == '/':
                 identifier = identifier[1:]
@@ -421,42 +521,7 @@ class MultiConfigData:
             spec = self.get_module_spec(module)
             if spec:
                 spec_part = find_spec_part(spec.get_config_spec(), id)
-                if type(spec_part) == list:
-                    # list of items to show
-                    for item in spec_part:
-                        value, status = self.get_value("/" + identifier\
-                                              + "/" + item['item_name'])
-                        entry = _create_value_map_entry(item['item_name'],
-                                                        item['item_type'],
-                                                        value, status)
-                        result.append(entry)
-                elif type(spec_part) == dict:
-                    # Sub-specification
-                    item = spec_part
-                    if item['item_type'] == 'list':
-                        li_spec = item['list_item_spec']
-                        value, status =  self.get_value("/" + identifier)
-                        if type(value) == list:
-                            for list_value in value:
-                                result_part2 = _create_value_map_entry(
-                                                   li_spec['item_name'],
-                                                   li_spec['item_type'],
-                                                   list_value)
-                                result.append(result_part2)
-                        elif value is not None:
-                            entry = _create_value_map_entry(
-                                        li_spec['item_name'],
-                                        li_spec['item_type'],
-                                        value, status)
-                            result.append(entry)
-                    else:
-                        value, status = self.get_value("/" + identifier)
-                        if value is not None:
-                            entry = _create_value_map_entry(
-                                        item['item_name'],
-                                        item['item_type'],
-                                        value, status)
-                            result.append(entry)
+                self._append_value_item(result, spec_part, identifier, all, True)
         return result
 
     def set_value(self, identifier, value):
diff --git a/src/lib/python/isc/config/tests/config_data_test.py b/src/lib/python/isc/config/tests/config_data_test.py
index 4710b00..1aded94 100644
--- a/src/lib/python/isc/config/tests/config_data_test.py
+++ b/src/lib/python/isc/config/tests/config_data_test.py
@@ -358,6 +358,8 @@ class TestMultiConfigData(unittest.TestCase):
         self.assertEqual(1, value)
         value = self.mcd.get_default_value("Spec2/item5[0]")
         self.assertEqual('a', value)
+        value = self.mcd.get_default_value("Spec2/item5[1]")
+        self.assertEqual('b', value)
         value = self.mcd.get_default_value("Spec2/item5[5]")
         self.assertEqual(None, value)
         value = self.mcd.get_default_value("Spec2/item5[0][1]")
@@ -392,6 +394,10 @@ class TestMultiConfigData(unittest.TestCase):
         self.assertEqual(None, value)
         self.assertEqual(MultiConfigData.NONE, status)
 
+        value, status = self.mcd.get_value("Spec2/item5")
+        self.assertEqual(['a', 'b'], value)
+        self.assertEqual(MultiConfigData.DEFAULT, status)
+
         value, status = self.mcd.get_value("Spec2/item5[0]")
         self.assertEqual("a", value)
         self.assertEqual(MultiConfigData.DEFAULT, status)
@@ -400,6 +406,11 @@ class TestMultiConfigData(unittest.TestCase):
         self.assertEqual(None, value)
         self.assertEqual(MultiConfigData.NONE, status)
 
+        value, status = self.mcd.get_value("Spec2/item5[1]")
+        self.assertEqual("b", value)
+        self.assertEqual(MultiConfigData.DEFAULT, status)
+
+
 
     def test_get_value_maps(self):
         maps = self.mcd.get_value_maps()
@@ -423,32 +434,34 @@ class TestMultiConfigData(unittest.TestCase):
         self.mcd._set_current_config({ "Spec2": { "item1": 2 } })
         self.mcd.set_value("Spec2/item3", False)
         maps = self.mcd.get_value_maps("/Spec2")
-        self.assertEqual([{'default': False, 'type': 'integer', 'name': 'item1', 'value': 2, 'modified': False},
-                          {'default': True, 'type': 'real', 'name': 'item2', 'value': 1.1, 'modified': False},
-                          {'default': False, 'type': 'boolean', 'name': 'item3', 'value': False, 'modified': True},
-                          {'default': True, 'type': 'string', 'name': 'item4', 'value': 'test', 'modified': False},
-                          {'default': True, 'type': 'list', 'name': 'item5', 'value': ['a', 'b'], 'modified': False},
-                          {'default': True, 'type': 'map', 'name': 'item6', 'value': {}, 'modified': False}], maps)
+        self.assertEqual([{'default': False, 'type': 'integer', 'name': 'Spec2/item1', 'value': 2, 'modified': False},
+                          {'default': True, 'type': 'real', 'name': 'Spec2/item2', 'value': 1.1, 'modified': False},
+                          {'default': False, 'type': 'boolean', 'name': 'Spec2/item3', 'value': False, 'modified': True},
+                          {'default': True, 'type': 'string', 'name': 'Spec2/item4', 'value': 'test', 'modified': False},
+                          {'default': True, 'type': 'list', 'name': 'Spec2/item5', 'value': ['a', 'b'], 'modified': False},
+                          {'default': True, 'type': 'string', 'name': 'Spec2/item6/value1', 'value': 'default', 'modified': False},
+                          {'default': False, 'type': 'integer', 'name': 'Spec2/item6/value2', 'value': None, 'modified': False}], maps)
         maps = self.mcd.get_value_maps("Spec2")
-        self.assertEqual([{'default': False, 'type': 'integer', 'name': 'item1', 'value': 2, 'modified': False},
-                          {'default': True, 'type': 'real', 'name': 'item2', 'value': 1.1, 'modified': False},
-                          {'default': False, 'type': 'boolean', 'name': 'item3', 'value': False, 'modified': True},
-                          {'default': True, 'type': 'string', 'name': 'item4', 'value': 'test', 'modified': False},
-                          {'default': True, 'type': 'list', 'name': 'item5', 'value': ['a', 'b'], 'modified': False},
-                          {'default': True, 'type': 'map', 'name': 'item6', 'value': {}, 'modified': False}], maps)
+        self.assertEqual([{'default': False, 'type': 'integer', 'name': 'Spec2/item1', 'value': 2, 'modified': False},
+                          {'default': True, 'type': 'real', 'name': 'Spec2/item2', 'value': 1.1, 'modified': False},
+                          {'default': False, 'type': 'boolean', 'name': 'Spec2/item3', 'value': False, 'modified': True},
+                          {'default': True, 'type': 'string', 'name': 'Spec2/item4', 'value': 'test', 'modified': False},
+                          {'default': True, 'type': 'list', 'name': 'Spec2/item5', 'value': ['a', 'b'], 'modified': False},
+                          {'default': True, 'type': 'string', 'name': 'Spec2/item6/value1', 'value': 'default', 'modified': False},
+                          {'default': False, 'type': 'integer', 'name': 'Spec2/item6/value2', 'value': None, 'modified': False}], maps)
         maps = self.mcd.get_value_maps("/Spec2/item5")
-        self.assertEqual([{'default': False, 'type': 'string', 'name': 'list_element', 'value': 'a', 'modified': False},
-                          {'default': False, 'type': 'string', 'name': 'list_element', 'value': 'b', 'modified': False}], maps)
+        self.assertEqual([{'default': True, 'type': 'string', 'name': 'Spec2/item5[0]', 'value': 'a', 'modified': False},
+                          {'default': True, 'type': 'string', 'name': 'Spec2/item5[1]', 'value': 'b', 'modified': False}], maps)
         maps = self.mcd.get_value_maps("/Spec2/item5[0]")
-        self.assertEqual([{'default': True, 'modified': False, 'name': 'list_element', 'type': 'string', 'value': 'a'}], maps)
+        self.assertEqual([{'default': True, 'modified': False, 'name': 'Spec2/item5[0]', 'type': 'string', 'value': 'a'}], maps)
         maps = self.mcd.get_value_maps("/Spec2/item1")
-        self.assertEqual([{'default': False, 'type': 'integer', 'name': 'item1', 'value': 2, 'modified': False}], maps)
+        self.assertEqual([{'default': False, 'type': 'integer', 'name': 'Spec2/item1', 'value': 2, 'modified': False}], maps)
         maps = self.mcd.get_value_maps("/Spec2/item2")
-        self.assertEqual([{'default': True, 'type': 'real', 'name': 'item2', 'value': 1.1, 'modified': False}], maps)
+        self.assertEqual([{'default': True, 'type': 'real', 'name': 'Spec2/item2', 'value': 1.1, 'modified': False}], maps)
         maps = self.mcd.get_value_maps("/Spec2/item3")
-        self.assertEqual([{'default': False, 'type': 'boolean', 'name': 'item3', 'value': False, 'modified': True}], maps)
+        self.assertEqual([{'default': False, 'type': 'boolean', 'name': 'Spec2/item3', 'value': False, 'modified': True}], maps)
         maps = self.mcd.get_value_maps("/Spec2/item4")
-        self.assertEqual([{'default': True, 'type': 'string', 'name': 'item4', 'value': 'test', 'modified': False}], maps)
+        self.assertEqual([{'default': True, 'type': 'string', 'name': 'Spec2/item4', 'value': 'test', 'modified': False}], maps)
 
         module_spec = isc.config.module_spec_from_file(self.data_path + os.sep + "spec24.spec")
         self.mcd.set_specification(module_spec)
@@ -456,9 +469,30 @@ class TestMultiConfigData(unittest.TestCase):
         self.assertEqual([], maps)
         self.mcd._set_current_config({ "Spec24": { "item": [] } })
         maps = self.mcd.get_value_maps("/Spec24/item")
-        self.assertEqual([], maps)
-
+        self.assertEqual([{'default': False, 'modified': False, 'name': 'Spec24/item', 'type': 'list', 'value': []}], maps)
 
+        module_spec = isc.config.module_spec_from_file(self.data_path + os.sep + "spec22.spec")
+        self.mcd.set_specification(module_spec)
+        expected = [{'default': True,
+                     'modified': False,
+                     'name': 'Spec22/value9/v91',
+                     'type': 'string',
+                     'value': 'def'},
+                    {'default': True,
+                     'modified': False,
+                     'name': 'Spec22/value9/v92/v92a',
+                     'type': 'string',
+                     'value': 'Hello'
+                    },
+                    {'default': True,
+                     'modified': False,
+                     'name': 'Spec22/value9/v92/v92b',
+                     'type': 'integer',
+                     'value': 47806
+                    }
+                   ]
+        maps = self.mcd.get_value_maps("/Spec22/value9")
+        self.assertEqual(expected, maps)
 
     def test_set_value(self):
         module_spec = isc.config.module_spec_from_file(self.data_path + os.sep + "spec2.spec")
diff --git a/src/lib/python/isc/notify/tests/notify_out_test.py b/src/lib/python/isc/notify/tests/notify_out_test.py
index 0816e07..c4c149c 100644
--- a/src/lib/python/isc/notify/tests/notify_out_test.py
+++ b/src/lib/python/isc/notify/tests/notify_out_test.py
@@ -22,9 +22,45 @@ import socket
 from isc.datasrc import sqlite3_ds
 from isc.notify import notify_out, SOCK_DATA
 
+# our fake socket, where we can read and insert messages
+class MockSocket():
+    def __init__(self, family, type):
+        self.family = family
+        self.type = type
+        self._local_sock, self._remote_sock = socket.socketpair()
+
+    def connect(self, to):
+        pass
+
+    def fileno(self):
+        return self._local_sock.fileno()
+
+    def close(self):
+        self._local_sock.close()
+        self._remote_sock.close()
+
+    def sendto(self, data, flag, dst):
+        return self._local_sock.send(data)
+
+    def recvfrom(self, length):
+        data = self._local_sock.recv(length)
+        return (data, None)
+
+    # provide a remote end which can write data to MockSocket for testing.
+    def remote_end(self):
+        return self._remote_sock
+
+# We subclass the ZoneNotifyInfo class we're testing here, only
+# to override the prepare_notify_out() method.
+class MockZoneNotifyInfo(notify_out.ZoneNotifyInfo):
+    def prepare_notify_out(self):
+        super().prepare_notify_out();
+        self._sock.close()
+        self._sock = MockSocket(socket.AF_INET, socket.SOCK_DGRAM)
+
 class TestZoneNotifyInfo(unittest.TestCase):
     def setUp(self):
-        self.info = notify_out.ZoneNotifyInfo('cn.', 'IN')
+        self.info = notify_out.ZoneNotifyInfo('example.net.', 'IN')
 
     def test_prepare_finish_notify_out(self):
         self.info.prepare_notify_out()
@@ -46,7 +82,7 @@ class TestZoneNotifyInfo(unittest.TestCase):
         self.info.set_next_notify_target()
         self.assertIsNone(self.info.get_current_notify_target())
 
-        temp_info = notify_out.ZoneNotifyInfo('com.', 'IN')
+        temp_info = notify_out.ZoneNotifyInfo('example.com.', 'IN')
         temp_info.prepare_notify_out()
         self.assertIsNone(temp_info.get_current_notify_target())
 
@@ -54,16 +90,16 @@ class TestZoneNotifyInfo(unittest.TestCase):
 class TestNotifyOut(unittest.TestCase):
     def setUp(self):
         self._db_file = tempfile.NamedTemporaryFile(delete=False)
-        sqlite3_ds.load(self._db_file.name, 'cn.', self._cn_data_reader)
-        sqlite3_ds.load(self._db_file.name, 'com.', self._com_data_reader)
+        sqlite3_ds.load(self._db_file.name, 'example.net.', self._example_net_data_reader)
+        sqlite3_ds.load(self._db_file.name, 'example.com.', self._example_com_data_reader)
         self._notify = notify_out.NotifyOut(self._db_file.name)
-        self._notify._notify_infos[('com.', 'IN')] = notify_out.ZoneNotifyInfo('com.', 'IN')
-        self._notify._notify_infos[('com.', 'CH')] = notify_out.ZoneNotifyInfo('com.', 'CH')
-        self._notify._notify_infos[('cn.', 'IN')] = notify_out.ZoneNotifyInfo('cn.', 'IN')
-        self._notify._notify_infos[('org.', 'IN')] = notify_out.ZoneNotifyInfo('org.', 'IN')
-        self._notify._notify_infos[('org.', 'CH')] = notify_out.ZoneNotifyInfo('org.', 'CH')
-        
-        info = self._notify._notify_infos[('cn.', 'IN')]
+        self._notify._notify_infos[('example.com.', 'IN')] = MockZoneNotifyInfo('example.com.', 'IN')
+        self._notify._notify_infos[('example.com.', 'CH')] = MockZoneNotifyInfo('example.com.', 'CH')
+        self._notify._notify_infos[('example.net.', 'IN')] = MockZoneNotifyInfo('example.net.', 'IN')
+        self._notify._notify_infos[('example.org.', 'IN')] = MockZoneNotifyInfo('example.org.', 'IN')
+        self._notify._notify_infos[('example.org.', 'CH')] = MockZoneNotifyInfo('example.org.', 'CH')
+
+        info = self._notify._notify_infos[('example.net.', 'IN')]
         info.notify_slaves.append(('127.0.0.1', 53))
         info.notify_slaves.append(('1.1.1.1', 5353))
 
@@ -72,62 +108,59 @@ class TestNotifyOut(unittest.TestCase):
         os.unlink(self._db_file.name)
 
     def test_send_notify(self):
-        self._notify.send_notify('cn')
+        self._notify.send_notify('example.net')
         self.assertEqual(self._notify.notify_num, 1)
-        self.assertEqual(self._notify._notifying_zones[0], ('cn.','IN'))
+        self.assertEqual(self._notify._notifying_zones[0], ('example.net.','IN'))
 
-        self._notify.send_notify('com')
+        self._notify.send_notify('example.com')
         self.assertEqual(self._notify.notify_num, 2)
-        self.assertEqual(self._notify._notifying_zones[1], ('com.','IN'))
+        self.assertEqual(self._notify._notifying_zones[1], ('example.com.','IN'))
 
         notify_out._MAX_NOTIFY_NUM = 3
-        self._notify.send_notify('com', 'CH')
+        self._notify.send_notify('example.com', 'CH')
         self.assertEqual(self._notify.notify_num, 3)
-        self.assertEqual(self._notify._notifying_zones[2], ('com.','CH'))
-    
-        self._notify.send_notify('org.')
-        self.assertEqual(self._notify._waiting_zones[0], ('org.', 'IN'))
-        self._notify.send_notify('org.')
+        self.assertEqual(self._notify._notifying_zones[2], ('example.com.','CH'))
+
+        self._notify.send_notify('example.org.')
+        self.assertEqual(self._notify._waiting_zones[0], ('example.org.', 'IN'))
+        self._notify.send_notify('example.org.')
         self.assertEqual(1, len(self._notify._waiting_zones))
 
-        self._notify.send_notify('org.', 'CH')
+        self._notify.send_notify('example.org.', 'CH')
         self.assertEqual(2, len(self._notify._waiting_zones))
-        self.assertEqual(self._notify._waiting_zones[1], ('org.', 'CH'))
+        self.assertEqual(self._notify._waiting_zones[1], ('example.org.', 'CH'))
 
     def test_wait_for_notify_reply(self):
-        self._notify.send_notify('cn.')
-        self._notify.send_notify('com.')
-    
+        self._notify.send_notify('example.net.')
+        self._notify.send_notify('example.com.')
+
         notify_out._MAX_NOTIFY_NUM = 2
-        self._notify.send_notify('org.')
+        self._notify.send_notify('example.org.')
         replied_zones, timeout_zones = self._notify._wait_for_notify_reply()
         self.assertEqual(len(replied_zones), 0)
         self.assertEqual(len(timeout_zones), 2)
 
         # Now make one socket be readable
-        addr = ('localhost', 12340)
-        self._notify._notify_infos[('cn.', 'IN')]._sock.bind(addr)
-        self._notify._notify_infos[('cn.', 'IN')].notify_timeout = time.time() + 10
-        self._notify._notify_infos[('com.', 'IN')].notify_timeout = time.time() + 10
-        
-        send_fd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+        self._notify._notify_infos[('example.net.', 'IN')].notify_timeout = time.time() + 10
+        self._notify._notify_infos[('example.com.', 'IN')].notify_timeout = time.time() + 10
+
         #Send some data to socket 12340, to make the target socket be readable
-        send_fd.sendto(b'data', addr)
+        self._notify._notify_infos[('example.net.', 'IN')]._sock.remote_end().send(b'data')
         replied_zones, timeout_zones = self._notify._wait_for_notify_reply()
         self.assertEqual(len(replied_zones), 1)
         self.assertEqual(len(timeout_zones), 1)
-        self.assertTrue(('cn.', 'IN') in replied_zones.keys())
-        self.assertTrue(('com.', 'IN') in timeout_zones.keys())
-        self.assertLess(time.time(), self._notify._notify_infos[('com.', 'IN')].notify_timeout)
-    
+        self.assertTrue(('example.net.', 'IN') in replied_zones.keys())
+        self.assertTrue(('example.com.', 'IN') in timeout_zones.keys())
+        self.assertLess(time.time(), self._notify._notify_infos[('example.com.', 'IN')].notify_timeout)
+
     def test_wait_for_notify_reply_2(self):
         # Test the returned value when the read_side socket is readable.
-        self._notify.send_notify('cn.')
-        self._notify.send_notify('com.')
+        self._notify.send_notify('example.net.')
+        self._notify.send_notify('example.com.')
 
         # Now make one socket be readable
-        self._notify._notify_infos[('cn.', 'IN')].notify_timeout = time.time() + 10
-        self._notify._notify_infos[('com.', 'IN')].notify_timeout = time.time() + 10
+        self._notify._notify_infos[('example.net.', 'IN')].notify_timeout = time.time() + 10
+        self._notify._notify_infos[('example.com.', 'IN')].notify_timeout = time.time() + 10
         self._notify._read_sock, self._notify._write_sock = socket.socketpair()
         self._notify._write_sock.send(SOCK_DATA)
         replied_zones, timeout_zones = self._notify._wait_for_notify_reply()
@@ -135,13 +168,13 @@ class TestNotifyOut(unittest.TestCase):
         self.assertEqual(0, len(timeout_zones))
 
     def test_notify_next_target(self):
-        self._notify.send_notify('cn.')
-        self._notify.send_notify('com.')
+        self._notify.send_notify('example.net.')
+        self._notify.send_notify('example.com.')
         notify_out._MAX_NOTIFY_NUM = 2
-        self._notify.send_notify('org.')
-        self._notify.send_notify('com.', 'CH')
+        self._notify.send_notify('example.org.')
+        self._notify.send_notify('example.com.', 'CH')
 
-        info = self._notify._notify_infos[('cn.', 'IN')]
+        info = self._notify._notify_infos[('example.net.', 'IN')]
         self._notify._notify_next_target(info)
         self.assertEqual(0, info.notify_try_num)
         self.assertEqual(info.get_current_notify_target(), ('1.1.1.1', 5353))
@@ -153,101 +186,101 @@ class TestNotifyOut(unittest.TestCase):
         self.assertEqual(2, self._notify.notify_num)
         self.assertEqual(1, len(self._notify._waiting_zones))
 
-        com_info = self._notify._notify_infos[('com.', 'IN')]
-        self._notify._notify_next_target(com_info)
+        example_com_info = self._notify._notify_infos[('example.com.', 'IN')]
+        self._notify._notify_next_target(example_com_info)
         self.assertEqual(2, self._notify.notify_num)
         self.assertEqual(2, len(self._notify._notifying_zones))
-    
+
     def test_handle_notify_reply(self):
         self.assertEqual(notify_out._BAD_REPLY_PACKET, self._notify._handle_notify_reply(None, b'badmsg'))
-        com_info = self._notify._notify_infos[('com.', 'IN')]
-        com_info.notify_msg_id = 0X2f18
+        example_com_info = self._notify._notify_infos[('example.com.', 'IN')]
+        example_com_info.notify_msg_id = 0X2f18
 
         # test with right notify reply message
-        data = b'\x2f\x18\xa0\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03com\x00\x00\x06\x00\x01'
-        self.assertEqual(notify_out._REPLY_OK, self._notify._handle_notify_reply(com_info, data))
+        data = b'\x2f\x18\xa0\x00\x00\x01\x00\x00\x00\x00\x00\x00\x07example\03com\x00\x00\x06\x00\x01'
+        self.assertEqual(notify_out._REPLY_OK, self._notify._handle_notify_reply(example_com_info, data))
 
         # test with unright query id
-        data = b'\x2e\x18\xa0\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03com\x00\x00\x06\x00\x01'
-        self.assertEqual(notify_out._BAD_QUERY_ID, self._notify._handle_notify_reply(com_info, data))
+        data = b'\x2e\x18\xa0\x00\x00\x01\x00\x00\x00\x00\x00\x00\x07example\03com\x00\x00\x06\x00\x01'
+        self.assertEqual(notify_out._BAD_QUERY_ID, self._notify._handle_notify_reply(example_com_info, data))
 
         # test with unright query name
-        data = b'\x2f\x18\xa0\x00\x00\x01\x00\x00\x00\x00\x00\x00\x02cn\x00\x00\x06\x00\x01'
-        self.assertEqual(notify_out._BAD_QUERY_NAME, self._notify._handle_notify_reply(com_info, data))
+        data = b'\x2f\x18\xa0\x00\x00\x01\x00\x00\x00\x00\x00\x00\x07example\03net\x00\x00\x06\x00\x01'
+        self.assertEqual(notify_out._BAD_QUERY_NAME, self._notify._handle_notify_reply(example_com_info, data))
 
         # test with unright opcode
-        data = b'\x2f\x18\x80\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03com\x00\x00\x06\x00\x01'
-        self.assertEqual(notify_out._BAD_OPCODE, self._notify._handle_notify_reply(com_info, data))
+        data = b'\x2f\x18\x80\x00\x00\x01\x00\x00\x00\x00\x00\x00\x07example\03com\x00\x00\x06\x00\x01'
+        self.assertEqual(notify_out._BAD_OPCODE, self._notify._handle_notify_reply(example_com_info, data))
 
         # test with unright qr
-        data = b'\x2f\x18\x10\x10\x00\x01\x00\x00\x00\x00\x00\x00\x03com\x00\x00\x06\x00\x01'
-        self.assertEqual(notify_out._BAD_QR, self._notify._handle_notify_reply(com_info, data))
+        data = b'\x2f\x18\x10\x10\x00\x01\x00\x00\x00\x00\x00\x00\x07example\03com\x00\x00\x06\x00\x01'
+        self.assertEqual(notify_out._BAD_QR, self._notify._handle_notify_reply(example_com_info, data))
 
     def test_send_notify_message_udp(self):
-        com_info = self._notify._notify_infos[('cn.', 'IN')]
-        com_info.prepare_notify_out()
-        ret = self._notify._send_notify_message_udp(com_info, ('1.1.1.1', 53))
+        example_com_info = self._notify._notify_infos[('example.net.', 'IN')]
+        example_com_info.prepare_notify_out()
+        ret = self._notify._send_notify_message_udp(example_com_info, ('1.1.1.1', 53))
         self.assertTrue(ret)
 
     def test_zone_notify_handler(self):
         old_send_msg = self._notify._send_notify_message_udp
-        def _fake_send_notify_message_udp(va1, va2): 
+        def _fake_send_notify_message_udp(va1, va2):
             pass
         self._notify._send_notify_message_udp = _fake_send_notify_message_udp
-        self._notify.send_notify('cn.')
-        self._notify.send_notify('com.')
+        self._notify.send_notify('example.net.')
+        self._notify.send_notify('example.com.')
         notify_out._MAX_NOTIFY_NUM = 2
-        self._notify.send_notify('org.')
+        self._notify.send_notify('example.org.')
 
-        cn_info = self._notify._notify_infos[('cn.', 'IN')]
-        cn_info.prepare_notify_out()
+        example_net_info = self._notify._notify_infos[('example.net.', 'IN')]
+        example_net_info.prepare_notify_out()
 
-        cn_info.notify_try_num = 2
-        self._notify._zone_notify_handler(cn_info, notify_out._EVENT_TIMEOUT)
-        self.assertEqual(3, cn_info.notify_try_num)
+        example_net_info.notify_try_num = 2
+        self._notify._zone_notify_handler(example_net_info, notify_out._EVENT_TIMEOUT)
+        self.assertEqual(3, example_net_info.notify_try_num)
 
-        time1 = cn_info.notify_timeout
-        self._notify._zone_notify_handler(cn_info, notify_out._EVENT_TIMEOUT)
-        self.assertEqual(4, cn_info.notify_try_num)
-        self.assertGreater(cn_info.notify_timeout, time1 + 2) # bigger than 2 seconds
+        time1 = example_net_info.notify_timeout
+        self._notify._zone_notify_handler(example_net_info, notify_out._EVENT_TIMEOUT)
+        self.assertEqual(4, example_net_info.notify_try_num)
+        self.assertGreater(example_net_info.notify_timeout, time1 + 2) # bigger than 2 seconds
 
-        cur_tgt = cn_info._notify_current
-        cn_info.notify_try_num = notify_out._MAX_NOTIFY_TRY_NUM
-        self._notify._zone_notify_handler(cn_info, notify_out._EVENT_NONE)
-        self.assertNotEqual(cur_tgt, cn_info._notify_current)
+        cur_tgt = example_net_info._notify_current
+        example_net_info.notify_try_num = notify_out._MAX_NOTIFY_TRY_NUM
+        self._notify._zone_notify_handler(example_net_info, notify_out._EVENT_NONE)
+        self.assertNotEqual(cur_tgt, example_net_info._notify_current)
 
-    def _cn_data_reader(self):
+    def _example_net_data_reader(self):
         zone_data = [
-        ('cn.',         '1000',  'IN',  'SOA', 'a.dns.cn. mail.cn. 1 1 1 1 1'),
-        ('cn.',         '1000',  'IN',  'NS',  'a.dns.cn.'),
-        ('cn.',         '1000',  'IN',  'NS',  'b.dns.cn.'),
-        ('cn.',         '1000',  'IN',  'NS',  'c.dns.cn.'),
-        ('a.dns.cn.',   '1000',  'IN',  'A',    '1.1.1.1'),
-        ('a.dns.cn.',   '1000',  'IN',  'AAAA', '2:2::2:2'),
-        ('b.dns.cn.',   '1000',  'IN',  'A',    '3.3.3.3'),
-        ('b.dns.cn.',   '1000',  'IN',  'AAAA', '4:4::4:4'),
-        ('b.dns.cn.',   '1000',  'IN',  'AAAA', '5:5::5:5'),
-        ('c.dns.cn.',   '1000',  'IN',  'A',    '6.6.6.6'),
-        ('c.dns.cn.',   '1000',  'IN',  'A',    '7.7.7.7'),
-        ('c.dns.cn.',   '1000',  'IN',  'AAAA', '8:8::8:8')]
+        ('example.net.',         '1000',  'IN',  'SOA', 'a.dns.example.net. mail.example.net. 1 1 1 1 1'),
+        ('example.net.',         '1000',  'IN',  'NS',  'a.dns.example.net.'),
+        ('example.net.',         '1000',  'IN',  'NS',  'b.dns.example.net.'),
+        ('example.net.',         '1000',  'IN',  'NS',  'c.dns.example.net.'),
+        ('a.dns.example.net.',   '1000',  'IN',  'A',    '1.1.1.1'),
+        ('a.dns.example.net.',   '1000',  'IN',  'AAAA', '2:2::2:2'),
+        ('b.dns.example.net.',   '1000',  'IN',  'A',    '3.3.3.3'),
+        ('b.dns.example.net.',   '1000',  'IN',  'AAAA', '4:4::4:4'),
+        ('b.dns.example.net.',   '1000',  'IN',  'AAAA', '5:5::5:5'),
+        ('c.dns.example.net.',   '1000',  'IN',  'A',    '6.6.6.6'),
+        ('c.dns.example.net.',   '1000',  'IN',  'A',    '7.7.7.7'),
+        ('c.dns.example.net.',   '1000',  'IN',  'AAAA', '8:8::8:8')]
         for item in zone_data:
             yield item
 
-    def _com_data_reader(self):
+    def _example_com_data_reader(self):
         zone_data = [
-        ('com.',         '1000',  'IN',  'SOA', 'a.dns.com. mail.com. 1 1 1 1 1'),
-        ('com.',         '1000',  'IN',  'NS',  'a.dns.com.'),
-        ('com.',         '1000',  'IN',  'NS',  'b.dns.com.'),
-        ('com.',         '1000',  'IN',  'NS',  'c.dns.com.'),
-        ('a.dns.com.',   '1000',  'IN',  'A',    '1.1.1.1'),
-        ('b.dns.com.',   '1000',  'IN',  'A',    '3.3.3.3'),
-        ('b.dns.com.',   '1000',  'IN',  'AAAA', '4:4::4:4'),
-        ('b.dns.com.',   '1000',  'IN',  'AAAA', '5:5::5:5')]
+        ('example.com.',         '1000',  'IN',  'SOA', 'a.dns.example.com. mail.example.com. 1 1 1 1 1'),
+        ('example.com.',         '1000',  'IN',  'NS',  'a.dns.example.com.'),
+        ('example.com.',         '1000',  'IN',  'NS',  'b.dns.example.com.'),
+        ('example.com.',         '1000',  'IN',  'NS',  'c.dns.example.com.'),
+        ('a.dns.example.com.',   '1000',  'IN',  'A',    '1.1.1.1'),
+        ('b.dns.example.com.',   '1000',  'IN',  'A',    '3.3.3.3'),
+        ('b.dns.example.com.',   '1000',  'IN',  'AAAA', '4:4::4:4'),
+        ('b.dns.example.com.',   '1000',  'IN',  'AAAA', '5:5::5:5')]
         for item in zone_data:
             yield item
 
     def test_get_notify_slaves_from_ns(self):
-        records = self._notify._get_notify_slaves_from_ns('cn.')
+        records = self._notify._get_notify_slaves_from_ns('example.net.')
         self.assertEqual(6, len(records))
         self.assertEqual('8:8::8:8', records[5])
         self.assertEqual('7.7.7.7', records[4])
@@ -256,36 +289,36 @@ class TestNotifyOut(unittest.TestCase):
         self.assertEqual('4:4::4:4', records[1])
         self.assertEqual('3.3.3.3', records[0])
 
-        records = self._notify._get_notify_slaves_from_ns('com.')
+        records = self._notify._get_notify_slaves_from_ns('example.com.')
         self.assertEqual(3, len(records))
         self.assertEqual('5:5::5:5', records[2])
         self.assertEqual('4:4::4:4', records[1])
         self.assertEqual('3.3.3.3', records[0])
-    
+
     def test_init_notify_out(self):
         self._notify._init_notify_out(self._db_file.name)
-        self.assertListEqual([('3.3.3.3', 53), ('4:4::4:4', 53), ('5:5::5:5', 53)], 
-                             self._notify._notify_infos[('com.', 'IN')].notify_slaves)
-        
+        self.assertListEqual([('3.3.3.3', 53), ('4:4::4:4', 53), ('5:5::5:5', 53)],
+                             self._notify._notify_infos[('example.com.', 'IN')].notify_slaves)
+
     def test_prepare_select_info(self):
         timeout, valid_fds, notifying_zones = self._notify._prepare_select_info()
         self.assertEqual(notify_out._IDLE_SLEEP_TIME, timeout)
         self.assertListEqual([], valid_fds)
 
-        self._notify._notify_infos[('cn.', 'IN')]._sock = 1
-        self._notify._notify_infos[('cn.', 'IN')].notify_timeout = time.time() + 5
+        self._notify._notify_infos[('example.net.', 'IN')]._sock = 1
+        self._notify._notify_infos[('example.net.', 'IN')].notify_timeout = time.time() + 5
         timeout, valid_fds, notifying_zones = self._notify._prepare_select_info()
         self.assertGreater(timeout, 0)
         self.assertListEqual([1], valid_fds)
 
-        self._notify._notify_infos[('cn.', 'IN')]._sock = 1
-        self._notify._notify_infos[('cn.', 'IN')].notify_timeout = time.time() - 5
+        self._notify._notify_infos[('example.net.', 'IN')]._sock = 1
+        self._notify._notify_infos[('example.net.', 'IN')].notify_timeout = time.time() - 5
         timeout, valid_fds, notifying_zones = self._notify._prepare_select_info()
         self.assertEqual(timeout, 0)
         self.assertListEqual([1], valid_fds)
 
-        self._notify._notify_infos[('com.', 'IN')]._sock = 2
-        self._notify._notify_infos[('com.', 'IN')].notify_timeout = time.time() + 5
+        self._notify._notify_infos[('example.com.', 'IN')]._sock = 2
+        self._notify._notify_infos[('example.com.', 'IN')].notify_timeout = time.time() + 5
         timeout, valid_fds, notifying_zones = self._notify._prepare_select_info()
         self.assertEqual(timeout, 0)
         self.assertListEqual([2, 1], valid_fds)
diff --git a/src/lib/resolve/resolve.cc b/src/lib/resolve/resolve.cc
index 312c89d..f741121 100644
--- a/src/lib/resolve/resolve.cc
+++ b/src/lib/resolve/resolve.cc
@@ -14,6 +14,9 @@
 
 #include <resolve/resolve.h>
 
+#include <dns/message.h>
+#include <dns/opcode.h>
+
 using namespace isc::dns;
 
 namespace {
@@ -44,6 +47,23 @@ makeErrorMessage(MessagePtr answer_message,
     answer_message->setRcode(error_code);
 }
 
+void initResponseMessage(const isc::dns::Message& query_message,
+                         isc::dns::Message& response_message)
+{
+    response_message.setOpcode(query_message.getOpcode());
+    response_message.setQid(query_message.getQid());
+    assert(response_message.getRRCount(Message::SECTION_QUESTION) == 0);
+    response_message.appendSection(Message::SECTION_QUESTION,
+        query_message);
+}
+
+void initResponseMessage(const isc::dns::Question& question,
+                         isc::dns::Message& response_message)
+{
+    response_message.setOpcode(isc::dns::Opcode::QUERY());
+    response_message.addQuestion(question);
+}
+
 void copyResponseMessage(const Message& source, MessagePtr target) {
     target->setRcode(source.getRcode());
 
diff --git a/src/lib/resolve/resolve.h b/src/lib/resolve/resolve.h
index b08c30c..550b620 100644
--- a/src/lib/resolve/resolve.h
+++ b/src/lib/resolve/resolve.h
@@ -42,6 +42,42 @@ namespace resolve {
 void makeErrorMessage(isc::dns::MessagePtr answer_message,
                       const isc::dns::Rcode& error_code);
 
+
+/// \brief Initialize a response message
+///
+/// Based on the given query message, this fills in the very
+/// first details of the response (i.e. the Question section and
+/// the Opcode). This allows for direct usage of makeErrorMessage(),
+/// as well as ResolveCache.lookup().
+///
+/// Raises an isc::dns::InvalidMessageOperation if reponse_message is
+/// not in RENDER mode.
+///
+/// \param query_message The query message to take the Question, Qid,
+///                      and Opcode from.
+/// \param response_message The fresh response message to initialize
+///                         (must be in RENDER mode)
+void initResponseMessage(const isc::dns::Message& query_message,
+                         isc::dns::Message& response_message);
+
+
+/// \brief Initialize a response message
+///
+/// Based on the given question, this fills in the very
+/// first details of the response (i.e. the Question section and the
+/// Opcode Query). This allows for direct usage of makeErrorMessage(),
+/// as well as ResolveCache.lookup().
+///
+/// Raises an isc::dns::InvalidMessageOperation if reponse_message is
+/// not in RENDER mode.
+///
+/// \param question The question to place in the Question section
+/// \param response_message The fresh response message to initialize
+///                         (must be in RENDER mode)
+void initResponseMessage(const isc::dns::Question& question,
+                         isc::dns::Message& response_message);
+
+
 /// \brief Copies the parts relevant for a DNS response to the
 /// target message
 ///
diff --git a/src/lib/resolve/tests/Makefile.am b/src/lib/resolve/tests/Makefile.am
index 3b5f41a..e669384 100644
--- a/src/lib/resolve/tests/Makefile.am
+++ b/src/lib/resolve/tests/Makefile.am
@@ -19,6 +19,7 @@ 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/exceptions/libexceptions.la
 run_unittests_LDADD +=  $(top_builddir)/src/lib/resolve/libresolve.la
 run_unittests_LDADD +=  $(top_builddir)/src/lib/dns/libdns++.la
 
diff --git a/src/lib/resolve/tests/resolve_unittest.cc b/src/lib/resolve/tests/resolve_unittest.cc
index fd68f16..85d264d 100644
--- a/src/lib/resolve/tests/resolve_unittest.cc
+++ b/src/lib/resolve/tests/resolve_unittest.cc
@@ -122,21 +122,47 @@ TEST_F(ResolveHelperFunctionsTest, makeErrorMessageNonEmptyMessage) {
 }
 
 void
-compareSections(const MessagePtr message_a, const MessagePtr message_b,
+compareSections(const Message& message_a, const Message& 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)
+    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));
+    EXPECT_TRUE(rrs_a == message_a.endSection(section));
+    EXPECT_TRUE(rrs_b == message_b.endSection(section));
+}
+
+TEST_F(ResolveHelperFunctionsTest, initResponseMessage) {
+    Message response_parse(Message::PARSE);
+    EXPECT_THROW(isc::resolve::initResponseMessage(*message_a_,
+                                                   response_parse),
+                 isc::dns::InvalidMessageOperation);
+    EXPECT_THROW(isc::resolve::initResponseMessage(*question_,
+                                                   response_parse),
+                 isc::dns::InvalidMessageOperation);
+    
+    Message response1(Message::RENDER);
+    isc::resolve::initResponseMessage(*message_a_, response1);
+    ASSERT_EQ(message_a_->getOpcode(), response1.getOpcode());
+    ASSERT_EQ(message_a_->getQid(), response1.getQid());
+    isc::dns::QuestionIterator qi = response1.beginQuestion();
+    ASSERT_EQ(*question_, **qi);
+    ASSERT_TRUE(++qi == response1.endQuestion());
+
+    Message response2(Message::RENDER);
+    isc::resolve::initResponseMessage(*question_, response2);
+    ASSERT_EQ(Opcode::QUERY(), response2.getOpcode());
+    ASSERT_EQ(0, response2.getQid());
+    qi = response2.beginQuestion();
+    ASSERT_EQ(*question_, **qi);
+    ASSERT_TRUE(++qi == response2.endQuestion());
 }
 
 TEST_F(ResolveHelperFunctionsTest, copyAnswerMessage) {
@@ -164,9 +190,9 @@ TEST_F(ResolveHelperFunctionsTest, copyAnswerMessage) {
               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);
+    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




More information about the bind10-changes mailing list