BIND 10 trac930, updated. 372471fb8b522e639b6a4bcf8a93eb0d06dfbe6a [trac930] - increase seconds in sleep time which is before HTTP client connects to the server - delete 'test_log_message' because of the deletion of original function

BIND 10 source code commits bind10-changes at lists.isc.org
Fri Jul 15 04:37:44 UTC 2011


The branch, trac930 has been updated
  discards  3225575a1d7dc447b802efe5cc15687465acbbc4 (commit)
  discards  fd9ecffcb0f2efec55134214f592775f09beeeb5 (commit)
  discards  22b53398bc9734bf49214280c9d1c4690796dca6 (commit)
  discards  bc84f2f852ee70b36eaaef4b4b0c57acc0c3fbba (commit)
  discards  f4b6ee76908f0929a38fadce33f096ca2b90e97b (commit)
  discards  ce0d7695c182d35cd859d65bd1020009418ce82a (commit)
  discards  0470f0fbba242529e3bfb96b36e077655c6ab78b (commit)
  discards  f0212ac979bd415e91993472dc13d4e3ce8af9ff (commit)
  discards  de90b6a18aa67cc4e80038d248b69fe700d398f8 (commit)
  discards  3b1918aeaa02185365241b58f95c971e1626ce58 (commit)
  discards  04027a910fa41a6ae2f6fec871f3717f07c7bcd8 (commit)
  discards  2b03d7e359f4f00c681f249ac37a9ce1cf9a8003 (commit)
  discards  ef2750436b5dcaa010963d30b62b9adb8f8067a9 (commit)
  discards  deaff8e25096bc988ea64bea7fbf3e58900c8d19 (commit)
  discards  806855d68d9298d08d57d3aadac26246fa29f884 (commit)
  discards  99d21295b8f7ac9b59b5a3959ce5a6e58cebef09 (commit)
  discards  f8750c5b4683f932f633c24acb27ebee164677cd (commit)
  discards  7ed4e6195bf07482d197e2d27512cb1621032b36 (commit)
  discards  7963c5d1e8dccbd13408b1c43de7243a1eaa4e03 (commit)
  discards  80940bf1435927f3dadbe1a3ff247ab5521beeb9 (commit)
  discards  31f9234c32e59d1b3d447dfe333974266dcf9c1e (commit)
  discards  88c1fc7576f04eb0b8b47ce00c81891be1265492 (commit)
  discards  33ae22f4a64faeef24511ebc2b25f5c32bc47842 (commit)
  discards  041dbe39c85f68a3e0477e8fdd759b4b8186a2aa (commit)
  discards  4535d9fe3867d4a6337bda857a7443a0cb23f26d (commit)
  discards  a4956bfe4e6b765230e45c88916e36b84ce2ea8a (commit)
  discards  541c8d912d6f144e3e4b2a30e5b12f4ffcc9b527 (commit)
  discards  c3373dcea6452a9a65e0268201bd400d22ccfb89 (commit)
  discards  e5f51ef865edde9f2e67135ada97fe3ef70981d6 (commit)
  discards  5fd27905ae1ffc496c26a96683f030d62a78914d (commit)
  discards  51c0586605bfe33d1a13cc1a7e09ba7a93ae8e7b (commit)
  discards  6e61199d3b33484d8efe60fd49d4d8785ffefa1c (commit)
  discards  7031d4e705b574397935e475052216d9afda51b2 (commit)
  discards  67a06df027a4b3cd8a40b6f6e8b63b6c379994de (commit)
  discards  9be74601f8138e9ce1f39666c3e340770d59c2b0 (commit)
  discards  6b6d555095e57db791015bd5177820b22e4d1a13 (commit)
  discards  58a29dd0d21d5ca88dd8b2dd17b72c67372b1722 (commit)
  discards  3b684780512c6f819179649d929240a30a35c13f (commit)
  discards  768a68aec51a97686b7b620307e2d1b1270656f7 (commit)
  discards  e4c39d8814553522ed0e9ed069e2a4fbb65de7de (commit)
  discards  456bec184115ee88a63d72d54d533d485b2a6aa8 (commit)
  discards  b9ab106d39d75bebbc94e11ca64274a62a18a5ae (commit)
  discards  a68dabdfb3f3590fba4ddca7c7c407b8b1712e97 (commit)
  discards  e689ae1785a1fa5941471e1f144c202fd7d036b7 (commit)
       via  372471fb8b522e639b6a4bcf8a93eb0d06dfbe6a (commit)
       via  b2aec5f9113d8fff197c8779e09cf0e8e3ed5a53 (commit)
       via  3fe1e47ee5d0896e62fbfdb73be724e7fe48ee16 (commit)
       via  60f8ec5a6641c0553d2c8933101551bd6142b3a3 (commit)
       via  47988290be92cd09e32d2ee3f51179f9133e2bfc (commit)
       via  edf3a943c94921c528a4132b2c65b609701a5668 (commit)
       via  6f67259c2de2a336fa5e038756da57be1a70e427 (commit)
       via  b8f9e3f9bbcd6626064093f474940ad06e884d0f (commit)
       via  133f0001fafea84190e73e766fca8b6d87c8c09c (commit)
       via  514963d8458ef888d9bc07e138e0051d3f697bac (commit)
       via  1087e06463720a1491aafcce6ee7f8694e83ebc5 (commit)
       via  e75e95121605158605452a9ba6917d1e29b925af (commit)
       via  0b81f6faa78ba4ad7c010af476a5af6a3fd1db54 (commit)
       via  4201a627a42c9b7e9cbf9c8ac17792d07686e9b7 (commit)
       via  13252373f8307c13642990ce7acd3ac5a3395f5e (commit)
       via  86671ed6103a70fb0d7df2bd5fff4dc7e3b05dd1 (commit)
       via  5475b3a80199fed9a007e6d5d89ef18013a15b67 (commit)
       via  a13e9592cec4afb50759e2900551a62867b8c9a6 (commit)
       via  acc6f06a09cce9a11ebf1ea8441aa78ba5a8c6b9 (commit)
       via  4822c0d4fb880fe881b505fb78578f2076c9ba95 (commit)
       via  d0ec422478e20dedd71c836cc76d9afc0e2fd8e9 (commit)
       via  c2f743fb187ae98111aaa5b4789ba1641d32f986 (commit)
       via  a365c21da34b70f50459137ae242767cc336f191 (commit)
       via  d13509441ce77077ccf21b9442458b0fb52b1c07 (commit)
       via  8e00f359e81c3cb03c5075710ead0f87f87e3220 (commit)
       via  f52ff519388e7f3ab4e903695b731a2a7000fcf5 (commit)
       via  f63ff922e713a04b3f4391d509c2206ac32edbb5 (commit)
       via  72a0beb8dfe85b303f546d09986461886fe7a3d8 (commit)
       via  aa4405d57bec097972c4d5b60d1cfd6a06f84bf1 (commit)
       via  4d17de950b96631d01c7928b9cab24860b2e29e5 (commit)
       via  365948a46f61db8726a24bfd0c625d26a014f63a (commit)
       via  c24553e21fe01121a42e2136d0a1230d75812b27 (commit)
       via  151ea34890984f1fb2404df848c1dcbf3e61d765 (commit)
       via  dfd8332b1a958ed9aeb6ae423ea937b5e08024f8 (commit)
       via  54c3708f45c72065cefd4d6013be5467bee65f85 (commit)
       via  146c48357b32d26019675834eda1daddde95302c (commit)
       via  8cec4587428e4fba8f5cf8791f19f8373212b250 (commit)
       via  090c4c5abac33b2b28d7bdcf3039005a014f9c5b (commit)
       via  62f912bd96a5fefeb0eb8b017ff12335810483b0 (commit)
       via  3b4b066b5d1c3726f51e52fee52c317a3ae3f9e3 (commit)
       via  a7047de1ec7aece83271cc28605ea1d790afee67 (commit)
       via  71eee6e279d7527adbc1e325b0cca49d824b67ee (commit)
       via  0958095d36903cd821afc57be0c038896dd1acdb (commit)
       via  8e66cc336b29bd5acc1f764f26cb0b116db4dc87 (commit)
       via  e540aaf2cedae6cfeb4c0ea063f8693cf5999822 (commit)
       via  aac974498b0a9513f3caf341e1eecbe4adbcff0a (commit)
       via  e7cf8992bed2ef0be2843da6f0eedf9fa6d5f66b (commit)
       via  eea48a1e96605accf8579ae4b7fb869295c9ff99 (commit)
       via  cca39b307de50546d7e3c4cd9fe4c2435223bf21 (commit)
       via  dffeeebd09195ad602090501c8c9b05b55885596 (commit)
       via  673a619cd628130b0506a5d3669fd6a4d139c790 (commit)
       via  f8092952b50ef238e2ffc63ccb6d17a469f22966 (commit)
       via  7cb53c7b33c41bc8c5d76c6994caae800692108d (commit)
       via  d0df4daafee6703a7b52609b5681846f83310182 (commit)
       via  d23f84732df2786fad5bf31f3446e0e088d941ec (commit)
       via  963e72656e6a5d8303034f9085c87834a75c44ce (commit)
       via  fd2daaa2c1a27140568cf5a4f04baf57682214d2 (commit)
       via  78942e3fc11f22f1bdbbd8fdd629691d5c510a55 (commit)
       via  8945eccce758dd466ac42c6521a3fc4ada5a9226 (commit)
       via  f29890eed7bad4aead5e95cfa6aae147287a0b10 (commit)
       via  7469b1f920d47306f87aab0e2fa0533903bc61af (commit)
       via  4d685db094731fccfa684f5c0b26ebfc1c28ca2c (commit)
       via  b327d9aac9bfd87e175d03421069ae679087dd00 (commit)
       via  829edd5488aa90324ddc4036dbaf4f2578be9e76 (commit)
       via  d81a47d3366b6c6ed14edff69188b60ed3655f28 (commit)
       via  a29b113e5b418921dffaf9b4cfc562ae887a7960 (commit)
       via  5024b68a04ecc7ff1c73299fa986cac740cb3e8b (commit)
       via  56b188c6e4e36a28b54cab442677e2fa748f0bae (commit)
       via  d7d60797272f02e6f3f09b659922c71f2c49ffec (commit)
       via  570bbcef51aa6a5bc920faabd850cd6a86c0d421 (commit)
       via  e090ab50879c15c850df8e8145f01d39dbd6b87b (commit)
       via  832cd1c032222fec662f9320e6f564f55b75cc8a (commit)
       via  7b2691afaea9ccefa2db073f8a717e003f2ad07e (commit)
       via  e8e411dd27068279b58bc3527d1b60878ed19d0b (commit)
       via  bfbd97a0fa52c122c6d0ab5239524b7be58b62be (commit)
       via  0e6771fbedb4081dc867e845b541023a673a1da6 (commit)
       via  b9bc51b44f59c9e93eaa5a21ae7658a320741e08 (commit)
       via  0d68ac445710fdb4d9d89ca2055b206c9a06dc94 (commit)
       via  b5e49faa3340628865ea28a60d3dc36d3e08511d (commit)
       via  535a401494dd268de77cccfaba68cacbaa1b2a6e (commit)
       via  0252f1b276eaf8e72d42510546f594b9d0703a58 (commit)
       via  026699b978f21466cdd20b09dba3fe0448e0592f (commit)
       via  6ec7cbb9976f68a0ca265e72dadfbb867d59581f (commit)
       via  8faa21b81fde5c30ca1df72739b9a0dd27005402 (commit)
       via  159caa607fc11e4b7c1b5efcbb28d0ebf5e99903 (commit)
       via  2bfafa08c054715e6163a91da334e1e4fa780740 (commit)
       via  7819ba75f5c170afa06a5a27b8c64e13ae094b74 (commit)
       via  cf5ed0e7c52e8a97ec48525ee2181e31aaa4184a (commit)
       via  47286f0bb01c6dbe0e48fc080f931d7b93e22063 (commit)
       via  191329567e3cab6ae2f0752f2e70880b8d97271a (commit)
       via  99a63ce0a562d9b26ef1ad68b9426d91e6ec35d7 (commit)
       via  cf4605bebe7b0266f21376b796d4863aca01f63e (commit)
       via  99c025349129904b864806049ea8761940ba0ecc (commit)
       via  cb8f695c11b2a6e5402ca58fabcc8a17800177ee (commit)

This update added new revisions after undoing existing revisions.  That is
to say, the old revision is not a strict subset of the new revision.  This
situation occurs when you --force push a change and generate a repository
containing something like this:

 * -- * -- B -- O -- O -- O (3225575a1d7dc447b802efe5cc15687465acbbc4)
            \
             N -- N -- N (372471fb8b522e639b6a4bcf8a93eb0d06dfbe6a)

When this happens we assume that you've already had alert emails for all
of the O revisions, and so we here report only the revisions in the N
branch from the common base, B.

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 372471fb8b522e639b6a4bcf8a93eb0d06dfbe6a
Author: Naoki Kambe <kambe at jprs.co.jp>
Date:   Wed Jul 13 20:25:54 2011 +0900

    [trac930]
     - increase seconds in sleep time which is before HTTP client connects to the server
     - delete 'test_log_message' because of the deletion of original function

commit b2aec5f9113d8fff197c8779e09cf0e8e3ed5a53
Author: Naoki Kambe <kambe at jprs.co.jp>
Date:   Fri Jul 8 21:22:34 2011 +0900

    [trac930] remove unneeded empty TODO comments

commit 3fe1e47ee5d0896e62fbfdb73be724e7fe48ee16
Author: Naoki Kambe <kambe at jprs.co.jp>
Date:   Fri Jul 8 21:09:41 2011 +0900

    [trac930] add new entry for #928-#930

commit 60f8ec5a6641c0553d2c8933101551bd6142b3a3
Author: Naoki Kambe <kambe at jprs.co.jp>
Date:   Fri Jul 8 20:08:22 2011 +0900

    [trac930] refurbish the unittests for new stats module, new stats httpd module
    and new mockups and utilities in test_utils.py

commit 47988290be92cd09e32d2ee3f51179f9133e2bfc
Author: Naoki Kambe <kambe at jprs.co.jp>
Date:   Fri Jul 8 19:56:24 2011 +0900

    [trac930] modify Stats
     - remove unneeded subject and listener classes
    
     - add StatsError for handling errors in Stats
    
     - add some new methods (update_modules, update_statistics_data and
       get_statistics_data)
    
     - modify implementations of existent commands(show and set) according changes
       stats.spec
    
     - remove reset and remove command because stats module couldn't manage other
       modules' statistics data schema
    
     - add implementation of strict validation of each statistics data
       (If the validation is failed, it puts out the error.)
    
     - stats module shows its PID when status command invoked
    
     - add new command showschema invokable via bindctl
    
     - set command requires arguments of owner module name and statistics item name
    
     - show and showschema commands accepts arguments of owner module name and
       statistics item name
    
     - exits at exit code 1 if got runtime errors
    
     - has boot time in _BASETIME

commit edf3a943c94921c528a4132b2c65b609701a5668
Author: Naoki Kambe <kambe at jprs.co.jp>
Date:   Fri Jul 8 19:40:15 2011 +0900

    [trac930]
     - remove "stats-schema.spec" setting and getting statistics data schema via
       this spec file
    
     - add "version" item in DEFAULT_CONFIG
    
     - get the address family by socket.getaddrinfo function with specified
       server_address in advance, and create HttpServer object once, in stead of
       creating double HttpServer objects for IPv6 and IPv4 in the prior code
       (It is aimed for avoiding to fail to close the once opened sockets.)
    
     - open HTTP port in start method
    
     - avoid calling config_handler recursively in the except statement
    
     - create XML, XSD, XSL documents after getting statistics data and schema from
       remote stats module via CC session
    
     - definitely close once opened template file object

commit 6f67259c2de2a336fa5e038756da57be1a70e427
Author: Naoki Kambe <kambe at jprs.co.jp>
Date:   Fri Jul 8 16:33:59 2011 +0900

    [trac930] update spec file of stats module
    - update description of status command, shutdown command and show command
    - change argument of show command (Owner module name of statistics data can be
    specified)
    - change argument of set command (Owner module name of statistics data is
    always required)
    - add showschema command which shows statistics data schema of each module
    specified)
    - disabled reset command and remove command

commit b8f9e3f9bbcd6626064093f474940ad06e884d0f
Author: Naoki Kambe <kambe at jprs.co.jp>
Date:   Fri Jul 8 16:21:49 2011 +0900

    [trac930] update argument name and argument format of set command in auth module and boss module
    and also update related unittests of their modules

commit 133f0001fafea84190e73e766fca8b6d87c8c09c
Author: Naoki Kambe <kambe at jprs.co.jp>
Date:   Fri Jul 8 16:18:38 2011 +0900

    [trac930] remove description about removing statistics data by stats module
    update example format in bindctl when show command of stats module is invoked

commit 514963d8458ef888d9bc07e138e0051d3f697bac
Author: Naoki Kambe <kambe at jprs.co.jp>
Date:   Fri Jul 8 16:13:17 2011 +0900

    [trac930] add a column "Owner" in the table tag

commit 1087e06463720a1491aafcce6ee7f8694e83ebc5
Author: Naoki Kambe <kambe at jprs.co.jp>
Date:   Fri Jul 8 16:12:09 2011 +0900

    [trac930] remove descriptions about "stats-schema.spec" and add description about new
    features because stats module can be requested to show statistics data schema.

commit e75e95121605158605452a9ba6917d1e29b925af
Author: Naoki Kambe <kambe at jprs.co.jp>
Date:   Fri Jul 8 16:00:30 2011 +0900

    [trac930] add utilities and mock-up modules for unittests of
    statistics modules and change some environ variables (PYTHONPATH,
    CONFIG_TESTDATA_PATH) in Makefile
    
    test_utilies.py internally calls msgq, cfgmgr and some mock modules
    with threads for as real situation as possible.

commit 0b81f6faa78ba4ad7c010af476a5af6a3fd1db54
Author: Naoki Kambe <kambe at jprs.co.jp>
Date:   Fri Jul 8 15:57:41 2011 +0900

    [trac930] remove unneeded mockups, fake modules and dummy data

commit 4201a627a42c9b7e9cbf9c8ac17792d07686e9b7
Author: Naoki Kambe <kambe at jprs.co.jp>
Date:   Fri Jul 8 15:55:55 2011 +0900

    [trac930] remove unneeded specfile "stats-schema.spec"

commit 13252373f8307c13642990ce7acd3ac5a3395f5e
Author: Naoki Kambe <kambe at jprs.co.jp>
Date:   Fri Jul 8 15:47:09 2011 +0900

    [trac929] add unittest of "get_statistics_spec"

commit 86671ed6103a70fb0d7df2bd5fff4dc7e3b05dd1
Author: Naoki Kambe <kambe at jprs.co.jp>
Date:   Fri Jul 8 15:46:57 2011 +0900

    [trac929] add unittests for the functions:
     - validate_format
     - check_format
     - validate_format

commit 5475b3a80199fed9a007e6d5d89ef18013a15b67
Author: Naoki Kambe <kambe at jprs.co.jp>
Date:   Fri Jul 8 15:46:46 2011 +0900

    [trac929] add "validate_statistics" which validates statistics specification in the spec file
    It checks data types and data format of statistics specification

commit a13e9592cec4afb50759e2900551a62867b8c9a6
Author: Naoki Kambe <kambe at jprs.co.jp>
Date:   Fri Jul 8 15:46:27 2011 +0900

    [trac929] add "get_statistics_spec" into cfgmgr.py
    it pushes contents in statistics category of each spec file.

commit acc6f06a09cce9a11ebf1ea8441aa78ba5a8c6b9
Author: Naoki Kambe <kambe at jprs.co.jp>
Date:   Fri Jul 8 15:45:28 2011 +0900

    [trac929] add COMMAND_GET_STATISTICS_SPEC for "get_statistics_spec"

commit 4822c0d4fb880fe881b505fb78578f2076c9ba95
Author: Naoki Kambe <kambe at jprs.co.jp>
Date:   Fri Jul 8 15:45:15 2011 +0900

    [trac929] add a statistics category into "spec2.spec"
    and modify message string to be compared with in EXPECT_EQ

commit d0ec422478e20dedd71c836cc76d9afc0e2fd8e9
Author: Naoki Kambe <kambe at jprs.co.jp>
Date:   Fri Jul 8 15:44:40 2011 +0900

    [trac929] add some spec files for unittest of statistics category

commit c2f743fb187ae98111aaa5b4789ba1641d32f986
Author: Naoki Kambe <kambe at jprs.co.jp>
Date:   Fri Jul 8 21:00:23 2011 +0900

    [trac928] add statistics category in each spec file and add statistics items in
    some spec files (bob.spec, auth.spec, stats.spec)

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

Summary of changes:
 ChangeLog                                          |   23 ++
 configure.ac                                       |   12 +
 src/bin/bind10/bind10.py.in                        |    3 +-
 src/bin/bind10/tests/bind10_test.py.in             |    2 +
 src/bin/cmdctl/cmdctl.py.in                        |    3 +-
 src/bin/cmdctl/tests/cmdctl_test.py                |    2 +
 src/bin/resolver/main.cc                           |    3 +-
 src/bin/stats/tests/b10-stats_test.py              |    1 +
 src/bin/xfrin/tests/xfrin_test.py                  |    2 +
 src/bin/xfrin/xfrin.py.in                          |    3 +-
 src/bin/xfrout/tests/xfrout_test.py.in             |    2 +
 src/bin/xfrout/xfrout.py.in                        |    2 +-
 src/lib/Makefile.am                                |    4 +-
 src/lib/acl/acl.h                                  |    3 +
 src/lib/acl/loader.h                               |   30 +-
 src/lib/asiodns/tests/run_unittests.cc             |    4 +-
 src/lib/asiolink/tests/interval_timer_unittest.cc  |   63 ++---
 src/lib/config/ccsession.h                         |    4 +-
 src/lib/config/tests/ccsession_unittests.cc        |   32 ++-
 src/lib/datasrc/cache.cc                           |    3 +-
 src/lib/datasrc/data_source.cc                     |    4 +-
 src/lib/datasrc/datasrc_messages.mes               |  126 ++++----
 src/lib/datasrc/memory_datasrc.cc                  |    4 +-
 src/lib/datasrc/sqlite3_datasrc.cc                 |    3 +-
 src/lib/datasrc/static_datasrc.cc                  |    2 +-
 src/lib/dns/message.cc                             |   49 +++-
 src/lib/dns/message.h                              |   11 +
 src/lib/dns/python/message_python.cc               |    9 +
 src/lib/dns/python/tests/message_python_test.py    |  121 +++++++-
 src/lib/dns/python/tests/question_python_test.py   |   10 +-
 src/lib/dns/question.cc                            |    9 +
 src/lib/dns/question.h                             |   16 +-
 src/lib/dns/tests/message_unittest.cc              |  226 +++++++++++++-
 src/lib/dns/tests/question_unittest.cc             |   16 +
 src/lib/dns/tests/testdata/Makefile.am             |    6 +-
 src/lib/dns/tests/testdata/gen-wiredata.py.in      |   12 +-
 src/lib/dns/tests/testdata/message_fromWire17.spec |   22 ++
 src/lib/dns/tests/testdata/message_fromWire18.spec |   23 ++
 src/lib/dns/tests/testdata/message_toWire4.spec    |   27 ++
 src/lib/dns/tests/testdata/message_toWire5.spec    |   36 +++
 src/lib/dns/tests/tsig_unittest.cc                 |   72 +++++
 src/lib/dns/tsig.cc                                |  103 ++++++-
 src/lib/dns/tsig.h                                 |   21 ++
 src/lib/log/Makefile.am                            |    1 +
 src/lib/log/logger_support.cc                      |  150 +---------
 src/lib/log/logger_support.h                       |   62 +----
 src/lib/log/logger_unittest_support.cc             |  175 +++++++++++
 src/lib/log/logger_unittest_support.h              |  126 ++++++++
 src/lib/log/tests/init_logger_test.sh.in           |    6 +-
 src/lib/log/tests/logger_level_impl_unittest.cc    |    7 +-
 src/lib/log/tests/logger_level_unittest.cc         |    8 +-
 src/lib/log/tests/logger_support_unittest.cc       |   15 +-
 src/lib/python/isc/Makefile.am                     |    2 +-
 src/lib/python/isc/acl/Makefile.am                 |   45 +++
 src/lib/python/isc/acl/__init__.py                 |   11 +
 src/lib/python/isc/acl/acl.cc                      |   80 +++++
 src/lib/python/isc/acl/acl.py                      |   29 ++
 src/lib/python/isc/acl/acl_inc.cc                  |   16 +
 src/lib/python/isc/acl/dns.cc                      |  135 +++++++++
 src/lib/python/isc/acl/dns.h                       |   52 ++++
 src/lib/python/isc/acl/dns.py                      |   33 ++
 src/lib/python/isc/acl/dns_requestacl_inc.cc       |   33 ++
 src/lib/python/isc/acl/dns_requestacl_python.cc    |  184 +++++++++++
 src/lib/python/isc/acl/dns_requestacl_python.h     |   53 ++++
 src/lib/python/isc/acl/dns_requestcontext_inc.cc   |   30 ++
 .../python/isc/acl/dns_requestcontext_python.cc    |  319 ++++++++++++++++++++
 src/lib/python/isc/acl/dns_requestcontext_python.h |   54 ++++
 src/lib/python/isc/acl/dns_requestloader_inc.cc    |   87 ++++++
 src/lib/python/isc/acl/dns_requestloader_python.cc |  270 +++++++++++++++++
 src/lib/python/isc/acl/dns_requestloader_python.h  |   46 +++
 src/lib/python/isc/acl/dnsacl_inc.cc               |   17 +
 src/lib/python/isc/acl/tests/Makefile.am           |   30 ++
 src/lib/python/isc/acl/tests/acl_test.py           |   29 ++
 src/lib/python/isc/acl/tests/dns_test.py           |  280 +++++++++++++++++
 src/lib/python/isc/config/ccsession.py             |    4 +-
 src/lib/python/isc/config/tests/ccsession_test.py  |    7 +-
 src/lib/python/isc/log/log.cc                      |   30 ++-
 src/lib/server_common/tests/keyring_test.cc        |    3 +-
 src/lib/util/python/pycppwrapper_util.h            |   29 ++-
 src/lib/util/python/wrapper_template.cc            |    4 +-
 80 files changed, 3154 insertions(+), 437 deletions(-)
 create mode 100644 src/lib/dns/tests/testdata/message_fromWire17.spec
 create mode 100644 src/lib/dns/tests/testdata/message_fromWire18.spec
 create mode 100644 src/lib/dns/tests/testdata/message_toWire4.spec
 create mode 100644 src/lib/dns/tests/testdata/message_toWire5.spec
 create mode 100644 src/lib/log/logger_unittest_support.cc
 create mode 100644 src/lib/log/logger_unittest_support.h
 create mode 100644 src/lib/python/isc/acl/Makefile.am
 create mode 100644 src/lib/python/isc/acl/__init__.py
 create mode 100644 src/lib/python/isc/acl/acl.cc
 create mode 100644 src/lib/python/isc/acl/acl.py
 create mode 100644 src/lib/python/isc/acl/acl_inc.cc
 create mode 100644 src/lib/python/isc/acl/dns.cc
 create mode 100644 src/lib/python/isc/acl/dns.h
 create mode 100644 src/lib/python/isc/acl/dns.py
 create mode 100644 src/lib/python/isc/acl/dns_requestacl_inc.cc
 create mode 100644 src/lib/python/isc/acl/dns_requestacl_python.cc
 create mode 100644 src/lib/python/isc/acl/dns_requestacl_python.h
 create mode 100644 src/lib/python/isc/acl/dns_requestcontext_inc.cc
 create mode 100644 src/lib/python/isc/acl/dns_requestcontext_python.cc
 create mode 100644 src/lib/python/isc/acl/dns_requestcontext_python.h
 create mode 100644 src/lib/python/isc/acl/dns_requestloader_inc.cc
 create mode 100644 src/lib/python/isc/acl/dns_requestloader_python.cc
 create mode 100644 src/lib/python/isc/acl/dns_requestloader_python.h
 create mode 100644 src/lib/python/isc/acl/dnsacl_inc.cc
 create mode 100644 src/lib/python/isc/acl/tests/Makefile.am
 create mode 100644 src/lib/python/isc/acl/tests/acl_test.py
 create mode 100644 src/lib/python/isc/acl/tests/dns_test.py

-----------------------------------------------------------------------
diff --git a/ChangeLog b/ChangeLog
index 9516302..de99d68 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -8,11 +8,34 @@ xxx.	[func]		naokikambe
 	modules.
 	(Trac #928,#929,#930, git nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn)
 
+272.	[func]		jinmei
+	libdns++/pydnspp: TSIG signing now handles truncated DNS messages
+	(i.e. with TC bit on) with TSIG correctly.
+	(Trac #910, 8e00f359e81c3cb03c5075710ead0f87f87e3220)
+
+271.	[func]		stephen
+	Default logging for unit tests changed to severity DEBUG (level 99)
+	with the output routed to /dev/null.  This can be altered by setting
+	the B10_LOGGER_XXX environment variables.
+	(Trac #1024, git 72a0beb8dfe85b303f546d09986461886fe7a3d8)
+
+270.	[func]		jinmei
+	Added python bindings for ACLs using the DNS request as the
+	context.  They are accessible via the isc.acl.dns module.
+	(Trac #983, git c24553e21fe01121a42e2136d0a1230d75812b27)
+
+269.	[bug]		y-aharen
+	Modified IntervalTimerTest not to rely on the accuracy of the timer.
+	This fix addresses occasional failure of build tests.
+	(Trac #1016, git 090c4c5abac33b2b28d7bdcf3039005a014f9c5b)
+
 268.	[func]		stephen
 	Add environment variable to allow redirection of logging output during
 	unit tests.
 	(Trac #1071, git 05164f9d61006869233b498d248486b4307ea8b6)
 
+bind10-devel-20110705 released on July 05, 2011
+
 267.	[func]		tomek
 	Added a dummy module for DHCP6. This module does not actually
 	do anything at this point, and BIND 10 has no option for
diff --git a/configure.ac b/configure.ac
index d14fcd4..5ba4a47 100644
--- a/configure.ac
+++ b/configure.ac
@@ -139,6 +139,16 @@ else
 	AC_SUBST(pkgpyexecdir)
 fi
 
+# We need to store the default pyexecdir in a separate variable so that
+# we can specify in Makefile.am the install directory of various BIND 10
+# python scripts and loadable modules; in Makefile.am we cannot replace
+# $(pyexecdir) using itself, e.g, this doesn't work:
+# pyexecdir = $(pyexecdir)/isc/some_module
+# The separate variable makes this setup possible as follows:
+# pyexecdir = $(PYTHON_SITEPKG_DIR)/isc/some_module
+PYTHON_SITEPKG_DIR=${pyexecdir}
+AC_SUBST(PYTHON_SITEPKG_DIR)
+
 # Check for python development environments
 if test -x ${PYTHON}-config; then
 	PYTHON_INCLUDES=`${PYTHON}-config --includes`
@@ -803,6 +813,8 @@ AC_CONFIG_FILES([Makefile
                  src/lib/cc/tests/Makefile
                  src/lib/python/Makefile
                  src/lib/python/isc/Makefile
+                 src/lib/python/isc/acl/Makefile
+                 src/lib/python/isc/acl/tests/Makefile
                  src/lib/python/isc/util/Makefile
                  src/lib/python/isc/util/tests/Makefile
                  src/lib/python/isc/datasrc/Makefile
diff --git a/src/bin/bind10/bind10.py.in b/src/bin/bind10/bind10.py.in
index 70efc2b..4814a8a 100755
--- a/src/bin/bind10/bind10.py.in
+++ b/src/bin/bind10/bind10.py.in
@@ -463,8 +463,7 @@ class BoB:
         self.log_starting("ccsession")
         self.ccs = isc.config.ModuleCCSession(SPECFILE_LOCATION, 
                                       self.config_handler,
-                                      self.command_handler,
-                                      None, True)
+                                      self.command_handler)
         self.ccs.start()
         self.log_started()
 
diff --git a/src/bin/bind10/tests/bind10_test.py.in b/src/bin/bind10/tests/bind10_test.py.in
index 100e0bd..7833afb 100644
--- a/src/bin/bind10/tests/bind10_test.py.in
+++ b/src/bin/bind10/tests/bind10_test.py.in
@@ -26,6 +26,7 @@ import socket
 from isc.net.addr import IPAddr
 import time
 import isc
+import isc.log
 
 from isc.testutils.parse_args import TestOptParser, OptsError
 
@@ -765,4 +766,5 @@ class TestBrittle(unittest.TestCase):
         self.assertFalse(bob.runnable)
 
 if __name__ == '__main__':
+    isc.log.resetUnitTestRootLogger()
     unittest.main()
diff --git a/src/bin/cmdctl/cmdctl.py.in b/src/bin/cmdctl/cmdctl.py.in
index 778d38f..2f89894 100755
--- a/src/bin/cmdctl/cmdctl.py.in
+++ b/src/bin/cmdctl/cmdctl.py.in
@@ -252,8 +252,7 @@ class CommandControl():
         self._cc = isc.cc.Session()
         self._module_cc = isc.config.ModuleCCSession(SPECFILE_LOCATION,
                                               self.config_handler,
-                                              self.command_handler,
-                                              None, True)
+                                              self.command_handler)
         self._module_name = self._module_cc.get_module_spec().get_module_name()
         self._cmdctl_config_data = self._module_cc.get_full_config()
         self._module_cc.start()
diff --git a/src/bin/cmdctl/tests/cmdctl_test.py b/src/bin/cmdctl/tests/cmdctl_test.py
index e77c529..3103f47 100644
--- a/src/bin/cmdctl/tests/cmdctl_test.py
+++ b/src/bin/cmdctl/tests/cmdctl_test.py
@@ -19,6 +19,7 @@ import socket
 import tempfile
 import sys
 from cmdctl import *
+import isc.log
 
 SPEC_FILE_PATH = '..' + os.sep
 if 'CMDCTL_SPEC_PATH' in os.environ:
@@ -447,6 +448,7 @@ class TestFuncNotInClass(unittest.TestCase):
 
 
 if __name__== "__main__":
+    isc.log.resetUnitTestRootLogger()
     unittest.main()
 
 
diff --git a/src/bin/resolver/main.cc b/src/bin/resolver/main.cc
index d9c30b9..79146da 100644
--- a/src/bin/resolver/main.cc
+++ b/src/bin/resolver/main.cc
@@ -208,8 +208,7 @@ main(int argc, char* argv[]) {
         cc_session = new Session(io_service.get_io_service());
         config_session = new ModuleCCSession(specfile, *cc_session,
                                              my_config_handler,
-                                             my_command_handler,
-                                             true, true);
+                                             my_command_handler);
         LOG_DEBUG(resolver_logger, RESOLVER_DBG_INIT, RESOLVER_CONFIG_CHANNEL);
 
         // FIXME: This does not belong here, but inside Boss
diff --git a/src/bin/stats/tests/b10-stats_test.py b/src/bin/stats/tests/b10-stats_test.py
index 91fe950..b013c7a 100644
--- a/src/bin/stats/tests/b10-stats_test.py
+++ b/src/bin/stats/tests/b10-stats_test.py
@@ -264,6 +264,7 @@ class TestStats(unittest.TestCase):
             self.assertTrue('item_description' in item)
             if len(item) == 7:
                 self.assertTrue('item_format' in item)
+        
         self.assertEqual(
             send_command('__UNKNOWN__', 'Stats'),
             (1, "Unknown command: '__UNKNOWN__'"))
diff --git a/src/bin/xfrin/tests/xfrin_test.py b/src/bin/xfrin/tests/xfrin_test.py
index 2acd9d6..92bf1b0 100644
--- a/src/bin/xfrin/tests/xfrin_test.py
+++ b/src/bin/xfrin/tests/xfrin_test.py
@@ -18,6 +18,7 @@ import socket
 import io
 from isc.testutils.tsigctx_mock import MockTSIGContext
 from xfrin import *
+import isc.log
 
 #
 # Commonly used (mostly constant) test parameters
@@ -1115,6 +1116,7 @@ class TestMain(unittest.TestCase):
 
 if __name__== "__main__":
     try:
+        isc.log.resetUnitTestRootLogger()
         unittest.main()
     except KeyboardInterrupt as e:
         print(e)
diff --git a/src/bin/xfrin/xfrin.py.in b/src/bin/xfrin/xfrin.py.in
index d1fbbfe..07de8f0 100755
--- a/src/bin/xfrin/xfrin.py.in
+++ b/src/bin/xfrin/xfrin.py.in
@@ -548,8 +548,7 @@ class Xfrin:
         self._send_cc_session = isc.cc.Session()
         self._module_cc = isc.config.ModuleCCSession(SPECFILE_LOCATION,
                                               self.config_handler,
-                                              self.command_handler,
-                                              None, True)
+                                              self.command_handler)
         self._module_cc.start()
         config_data = self._module_cc.get_full_config()
         self.config_handler(config_data)
diff --git a/src/bin/xfrout/tests/xfrout_test.py.in b/src/bin/xfrout/tests/xfrout_test.py.in
index adabf48..7ab4a58 100644
--- a/src/bin/xfrout/tests/xfrout_test.py.in
+++ b/src/bin/xfrout/tests/xfrout_test.py.in
@@ -23,6 +23,7 @@ from isc.cc.session import *
 from pydnspp import *
 from xfrout import *
 import xfrout
+import isc.log
 
 TSIG_KEY = TSIGKey("example.com:SFuWd/q99SzF8Yzd1QbB9g==")
 
@@ -670,4 +671,5 @@ class TestInitialization(unittest.TestCase):
         self.assertEqual(xfrout.UNIX_SOCKET_FILE, "The/Socket/File")
 
 if __name__== "__main__":
+    isc.log.resetUnitTestRootLogger()
     unittest.main()
diff --git a/src/bin/xfrout/xfrout.py.in b/src/bin/xfrout/xfrout.py.in
index a75ff22..b44b099 100755
--- a/src/bin/xfrout/xfrout.py.in
+++ b/src/bin/xfrout/xfrout.py.in
@@ -566,7 +566,7 @@ class XfroutServer:
         #self._log = None
         self._listen_sock_file = UNIX_SOCKET_FILE
         self._shutdown_event = threading.Event()
-        self._cc = isc.config.ModuleCCSession(SPECFILE_LOCATION, self.config_handler, self.command_handler, None, True)
+        self._cc = isc.config.ModuleCCSession(SPECFILE_LOCATION, self.config_handler, self.command_handler)
         self._config_data = self._cc.get_full_config()
         self._cc.start()
         self._cc.add_remote_config(AUTH_SPECFILE_LOCATION);
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
index f4bef6b..5adf150 100644
--- a/src/lib/Makefile.am
+++ b/src/lib/Makefile.am
@@ -1,3 +1,3 @@
-SUBDIRS = exceptions util log cryptolink dns cc config python xfr \
+SUBDIRS = exceptions util log cryptolink dns cc config acl python xfr \
           bench asiolink asiodns nsas cache resolve testutils datasrc \
-          acl server_common
+          server_common
diff --git a/src/lib/acl/acl.h b/src/lib/acl/acl.h
index 998b2b0..76039c9 100644
--- a/src/lib/acl/acl.h
+++ b/src/lib/acl/acl.h
@@ -88,8 +88,11 @@ public:
      * the context against conditions and if it matches, returns the
      * action that belongs to the first matched entry or default action
      * if nothing matches.
+     *
      * \param context The thing that should be checked. It is directly
      *     passed to the checks.
+     *
+     * \return The action for the ACL entry that first matches the context.
      */
     const Action& execute(const Context& context) const {
         const typename Entries::const_iterator end(entries_.end());
diff --git a/src/lib/acl/loader.h b/src/lib/acl/loader.h
index c86373e..f60b144 100644
--- a/src/lib/acl/loader.h
+++ b/src/lib/acl/loader.h
@@ -101,21 +101,21 @@ BasicAction defaultActionLoader(data::ConstElementPtr action);
  *
  * An ACL definition looks like this:
  * \verbatim
- * [
- *   {
- *      "action": "ACCEPT",
- *      "match-type": <parameter>
- *   },
- *   {
- *      "action": "REJECT",
- *      "match-type": <parameter>
- *      "another-match-type": [<parameter1>, <parameter2>]
-*    },
-*    {
-*       "action": "DROP"
-*    }
- * ]
- * \endverbatim
+ [
+   {
+      "action": "ACCEPT",
+      "match-type": <parameter>
+   },
+   {
+      "action": "REJECT",
+      "match-type": <parameter>,
+      "another-match-type": [<parameter1>, <parameter2>]
+   },
+   {
+      "action": "DROP"
+   }
+ ]
+ \endverbatim
  *
  * This is a list of elements. Each element must have an "action"
  * entry/keyword. That one specifies which action is returned if this
diff --git a/src/lib/asiodns/tests/run_unittests.cc b/src/lib/asiodns/tests/run_unittests.cc
index df77368..5cacdaf 100644
--- a/src/lib/asiodns/tests/run_unittests.cc
+++ b/src/lib/asiodns/tests/run_unittests.cc
@@ -15,14 +15,14 @@
 #include <gtest/gtest.h>
 #include <util/unittests/run_all.h>
 
-#include <log/logger_manager.h>
+#include <log/logger_support.h>
 #include <dns/tests/unittest_util.h>
 
 int
 main(int argc, char* argv[])
 {
     ::testing::InitGoogleTest(&argc, argv);         // Initialize Google test
-    isc::log::LoggerManager::init("unittest");      // Set a root logger name
+    isc::log::initLogger();                         // Initialize logging
     isc::UnitTestUtil::addDataPath(TEST_DATA_DIR);  // Add location of test data
 
     return (isc::util::unittests::run_all());
diff --git a/src/lib/asiolink/tests/interval_timer_unittest.cc b/src/lib/asiolink/tests/interval_timer_unittest.cc
index 8e8ef81..420cb90 100644
--- a/src/lib/asiolink/tests/interval_timer_unittest.cc
+++ b/src/lib/asiolink/tests/interval_timer_unittest.cc
@@ -28,7 +28,7 @@ const boost::posix_time::time_duration TIMER_MARGIN_MSEC =
 
 using namespace isc::asiolink;
 
-// This fixture is for testing IntervalTimer. Some callback functors are 
+// This fixture is for testing IntervalTimer. Some callback functors are
 // registered as callback function of the timer to test if they are called
 // or not.
 class IntervalTimerTest : public ::testing::Test {
@@ -50,7 +50,9 @@ protected:
     };
     class TimerCallBackCounter : public std::unary_function<void, void> {
     public:
-        TimerCallBackCounter(IntervalTimerTest* test_obj) : test_obj_(test_obj) {
+        TimerCallBackCounter(IntervalTimerTest* test_obj) :
+            test_obj_(test_obj)
+        {
             counter_ = 0;
         }
         void operator()() {
@@ -164,24 +166,20 @@ TEST_F(IntervalTimerTest, startIntervalTimer) {
     itimer.setup(TimerCallBack(this), 100);
     EXPECT_EQ(100, itimer.getInterval());
     io_service_.run();
-    // reaches here after timer expired
+    // Control reaches here after io_service_ was stopped by TimerCallBack.
+
     // delta: difference between elapsed time and 100 milliseconds.
     boost::posix_time::time_duration test_runtime =
         boost::posix_time::microsec_clock::universal_time() - start;
-    EXPECT_FALSE(test_runtime.is_negative()) << 
-                 "test duration " << test_runtime << 
+    EXPECT_FALSE(test_runtime.is_negative()) <<
+                 "test duration " << test_runtime <<
                  " negative - clock skew?";
-    boost::posix_time::time_duration delta =
-        test_runtime - boost::posix_time::milliseconds(100);
-    if (delta.is_negative()) {
-        delta.invert_sign();
-    }
-    // expect TimerCallBack is called; timer_called_ is true
+    // Expect TimerCallBack is called; timer_called_ is true
     EXPECT_TRUE(timer_called_);
-    // expect interval is 100 milliseconds +/- TIMER_MARGIN_MSEC.
-    EXPECT_TRUE(delta < TIMER_MARGIN_MSEC) << 
-                "delta " << delta.total_milliseconds() << "msec " <<
-                ">= " << TIMER_MARGIN_MSEC.total_milliseconds();
+    // Expect test_runtime is 100 milliseconds or longer.
+    EXPECT_TRUE(test_runtime > boost::posix_time::milliseconds(100)) <<
+                "test runtime " << test_runtime.total_milliseconds() <<
+                "msec " << ">= 100";
 }
 
 TEST_F(IntervalTimerTest, destructIntervalTimer) {
@@ -244,7 +242,7 @@ TEST_F(IntervalTimerTest, cancel) {
 }
 
 TEST_F(IntervalTimerTest, overwriteIntervalTimer) {
-    // Calling setup() multiple times updates call back function and interval.
+    // Call setup() multiple times to update call back function and interval.
     //
     // There are two timers:
     //  itimer (A)
@@ -266,7 +264,7 @@ TEST_F(IntervalTimerTest, overwriteIntervalTimer) {
     //     0  100  200  300  400  500  600  700  800 (ms)
     // (A) i-------------+----C----s
     //                        ^    ^stop io_service
-    //                        |change call back function
+    //                        |change call back function and interval
     // (B) i------------------+-------------------S
     //                                            ^(stop io_service on fail)
     //
@@ -279,30 +277,11 @@ TEST_F(IntervalTimerTest, overwriteIntervalTimer) {
     itimer.setup(TimerCallBackCounter(this), 300);
     itimer_overwriter.setup(TimerCallBackOverwriter(this, itimer), 400);
     io_service_.run();
-    // reaches here after timer expired
-    // if interval is updated, it takes
-    //   400 milliseconds for TimerCallBackOverwriter
-    //   + 100 milliseconds for TimerCallBack (stop)
-    //   = 500 milliseconds.
-    // otherwise (test fails), it takes
-    //   400 milliseconds for TimerCallBackOverwriter
-    //   + 400 milliseconds for TimerCallBackOverwriter (stop)
-    //   = 800 milliseconds.
-    // delta: difference between elapsed time and 400 + 100 milliseconds
-    boost::posix_time::time_duration test_runtime =
-        boost::posix_time::microsec_clock::universal_time() - start;
-    EXPECT_FALSE(test_runtime.is_negative()) << 
-                 "test duration " << test_runtime << 
-                 " negative - clock skew?";
-    boost::posix_time::time_duration delta =
-        test_runtime - boost::posix_time::milliseconds(400 + 100);
-    if (delta.is_negative()) {
-        delta.invert_sign();
-    }
-    // expect callback function is updated: TimerCallBack is called
+    // Control reaches here after io_service_ was stopped by
+    // TimerCallBackCounter or TimerCallBackOverwriter.
+
+    // Expect callback function is updated: TimerCallBack is called
     EXPECT_TRUE(timer_called_);
-    // expect interval is updated
-    EXPECT_TRUE(delta < TIMER_MARGIN_MSEC) << 
-                "delta " << delta.total_milliseconds() << " msec " <<
-                ">= " << TIMER_MARGIN_MSEC.total_milliseconds();
+    // Expect interval is updated: return value of getInterval() is updated
+    EXPECT_EQ(itimer.getInterval(), 100);
 }
diff --git a/src/lib/config/ccsession.h b/src/lib/config/ccsession.h
index 7dc34ba..a39d996 100644
--- a/src/lib/config/ccsession.h
+++ b/src/lib/config/ccsession.h
@@ -179,7 +179,7 @@ public:
      * We'll need to develop a cleaner solution, and then remove this knob)
      * @param handle_logging If true, the ModuleCCSession will automatically
      * take care of logging configuration through the virtual Logging config
-     * module.
+     * module. Defaults to true.
      */
     ModuleCCSession(const std::string& spec_file_name,
                     isc::cc::AbstractSession& session,
@@ -189,7 +189,7 @@ public:
                         const std::string& command,
                         isc::data::ConstElementPtr args) = NULL,
                     bool start_immediately = true,
-                    bool handle_logging = false
+                    bool handle_logging = true
                     );
 
     /// Start receiving new commands and configuration changes asynchronously.
diff --git a/src/lib/config/tests/ccsession_unittests.cc b/src/lib/config/tests/ccsession_unittests.cc
index 6ab1df9..8e473b6 100644
--- a/src/lib/config/tests/ccsession_unittests.cc
+++ b/src/lib/config/tests/ccsession_unittests.cc
@@ -151,7 +151,8 @@ TEST_F(CCSessionTest, parseCommand) {
 
 TEST_F(CCSessionTest, session1) {
     EXPECT_FALSE(session.haveSubscription("Spec1", "*"));
-    ModuleCCSession mccs(ccspecfile("spec1.spec"), session, NULL, NULL);
+    ModuleCCSession mccs(ccspecfile("spec1.spec"), session, NULL, NULL,
+                         true, false);
     EXPECT_TRUE(session.haveSubscription("Spec1", "*"));
 
     EXPECT_EQ(1, session.getMsgQueue()->size());
@@ -163,14 +164,15 @@ TEST_F(CCSessionTest, session1) {
     EXPECT_EQ("*", to);
     EXPECT_EQ(0, session.getMsgQueue()->size());
 
-    // without explicit argument, the session should not automatically
+    // with this argument, the session should not automatically
     // subscribe to logging config
     EXPECT_FALSE(session.haveSubscription("Logging", "*"));
 }
 
 TEST_F(CCSessionTest, session2) {
     EXPECT_FALSE(session.haveSubscription("Spec2", "*"));
-    ModuleCCSession mccs(ccspecfile("spec2.spec"), session, NULL, NULL);
+    ModuleCCSession mccs(ccspecfile("spec2.spec"), session, NULL, NULL,
+                         true, false);
     EXPECT_TRUE(session.haveSubscription("Spec2", "*"));
 
     EXPECT_EQ(1, session.getMsgQueue()->size());
@@ -217,7 +219,7 @@ TEST_F(CCSessionTest, session3) {
 
     EXPECT_FALSE(session.haveSubscription("Spec2", "*"));
     ModuleCCSession mccs(ccspecfile("spec2.spec"), session, my_config_handler,
-                         my_command_handler);
+                         my_command_handler, true, false);
     EXPECT_TRUE(session.haveSubscription("Spec2", "*"));
 
     EXPECT_EQ(2, session.getMsgQueue()->size());
@@ -241,7 +243,7 @@ TEST_F(CCSessionTest, checkCommand) {
 
     EXPECT_FALSE(session.haveSubscription("Spec29", "*"));
     ModuleCCSession mccs(ccspecfile("spec29.spec"), session, my_config_handler,
-                         my_command_handler);
+                         my_command_handler, true, false);
     EXPECT_TRUE(session.haveSubscription("Spec29", "*"));
 
     EXPECT_EQ(2, session.getMsgQueue()->size());
@@ -318,7 +320,7 @@ TEST_F(CCSessionTest, checkCommand2) {
     session.getMessages()->add(createAnswer(0, el("{}")));
     EXPECT_FALSE(session.haveSubscription("Spec29", "*"));
     ModuleCCSession mccs(ccspecfile("spec29.spec"), session, my_config_handler,
-                         my_command_handler);
+                         my_command_handler, true, false);
     EXPECT_TRUE(session.haveSubscription("Spec29", "*"));
     ConstElementPtr msg;
     std::string group, to;
@@ -370,7 +372,8 @@ TEST_F(CCSessionTest, remoteConfig) {
     std::string module_name;
     int item1;
     
-    ModuleCCSession mccs(ccspecfile("spec1.spec"), session, NULL, NULL, false);
+    ModuleCCSession mccs(ccspecfile("spec1.spec"), session, NULL, NULL,
+                         false, false);
     EXPECT_TRUE(session.haveSubscription("Spec1", "*"));
     
     // first simply connect, with no config values, and see we get
@@ -526,7 +529,7 @@ TEST_F(CCSessionTest, ignoreRemoteConfigCommands) {
 
     EXPECT_FALSE(session.haveSubscription("Spec29", "*"));
     ModuleCCSession mccs(ccspecfile("spec29.spec"), session, my_config_handler,
-                         my_command_handler, false);
+                         my_command_handler, false, false);
     EXPECT_TRUE(session.haveSubscription("Spec29", "*"));
 
     EXPECT_EQ(2, session.getMsgQueue()->size());
@@ -578,14 +581,15 @@ TEST_F(CCSessionTest, initializationFail) {
 
 // Test it throws when we try to start it twice (once from the constructor)
 TEST_F(CCSessionTest, doubleStartImplicit) {
-    ModuleCCSession mccs(ccspecfile("spec29.spec"), session, NULL, NULL);
+    ModuleCCSession mccs(ccspecfile("spec29.spec"), session, NULL, NULL,
+                         true, false);
     EXPECT_THROW(mccs.start(), CCSessionError);
 }
 
 // The same, but both starts are explicit
 TEST_F(CCSessionTest, doubleStartExplicit) {
     ModuleCCSession mccs(ccspecfile("spec29.spec"), session, NULL, NULL,
-                         false);
+                         false, false);
     mccs.start();
     EXPECT_THROW(mccs.start(), CCSessionError);
 }
@@ -593,7 +597,8 @@ TEST_F(CCSessionTest, doubleStartExplicit) {
 // Test we can request synchronous receive before we start the session,
 // and check there's the mechanism if we do it after
 TEST_F(CCSessionTest, delayedStart) {
-    ModuleCCSession mccs(ccspecfile("spec2.spec"), session, NULL, NULL, false);
+    ModuleCCSession mccs(ccspecfile("spec2.spec"), session, NULL, NULL,
+                         false, false);
     session.getMessages()->add(createAnswer());
     ConstElementPtr env, answer;
     EXPECT_NO_THROW(session.group_recvmsg(env, answer, false, 3));
@@ -620,7 +625,7 @@ TEST_F(CCSessionTest, loggingStartBadSpec) {
     // just give an empty config
     session.getMessages()->add(createAnswer(0, el("{}")));
     EXPECT_THROW(new ModuleCCSession(ccspecfile("spec2.spec"), session,
-                 NULL, NULL, true, true), ModuleSpecError);
+                 NULL, NULL), ModuleSpecError);
     EXPECT_FALSE(session.haveSubscription("Logging", "*"));
 }
 
@@ -629,7 +634,8 @@ TEST_F(CCSessionTest, loggingStartBadSpec) {
 // if we need to call addRemoteConfig().
 // The correct cases are covered in remoteConfig test.
 TEST_F(CCSessionTest, doubleStartWithAddRemoteConfig) {
-    ModuleCCSession mccs(ccspecfile("spec29.spec"), session, NULL, NULL);
+    ModuleCCSession mccs(ccspecfile("spec29.spec"), session, NULL, NULL,
+                         true, false);
     session.getMessages()->add(createAnswer(0, el("{}")));
     EXPECT_THROW(mccs.addRemoteConfig(ccspecfile("spec2.spec")),
                  FakeSession::DoubleRead);
diff --git a/src/lib/datasrc/cache.cc b/src/lib/datasrc/cache.cc
index 9082a6b..d88e649 100644
--- a/src/lib/datasrc/cache.cc
+++ b/src/lib/datasrc/cache.cc
@@ -232,7 +232,8 @@ HotCacheImpl::insert(const CacheNodePtr node) {
     if (iter != map_.end()) {
         CacheNodePtr old = iter->second;
         if (old && old->isValid()) {
-            LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_CACHE_OLD_FOUND);
+            LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_CACHE_OLD_FOUND)
+                      .arg(node->getNodeName());
             remove(old);
         }
     }
diff --git a/src/lib/datasrc/data_source.cc b/src/lib/datasrc/data_source.cc
index b57a967..94dec89 100644
--- a/src/lib/datasrc/data_source.cc
+++ b/src/lib/datasrc/data_source.cc
@@ -903,7 +903,7 @@ tryWildcard(Query& q, QueryTaskPtr task, ZoneInfo& zoneinfo, bool& found) {
             result = proveNX(q, task, zoneinfo, true);
             if (result != DataSrc::SUCCESS) {
                 m.setRcode(Rcode::SERVFAIL());
-                logger.error(DATASRC_QUERY_WILDCARD_PROVENX_FAIL).
+                logger.error(DATASRC_QUERY_WILDCARD_PROVE_NX_FAIL).
                     arg(task->qname).arg(result);
                 return (DataSrc::ERROR);
             }
@@ -1162,7 +1162,7 @@ DataSrc::doQuery(Query& q) {
                 result = proveNX(q, task, zoneinfo, false);
                 if (result != DataSrc::SUCCESS) {
                     m.setRcode(Rcode::SERVFAIL());
-                    logger.error(DATASRC_QUERY_PROVENX_FAIL).arg(task->qname);
+                    logger.error(DATASRC_QUERY_PROVE_NX_FAIL).arg(task->qname);
                     return;
                 }
             }
diff --git a/src/lib/datasrc/datasrc_messages.mes b/src/lib/datasrc/datasrc_messages.mes
index c692364..3dc69e0 100644
--- a/src/lib/datasrc/datasrc_messages.mes
+++ b/src/lib/datasrc/datasrc_messages.mes
@@ -17,63 +17,63 @@ $NAMESPACE isc::datasrc
 # \brief Messages for the data source library
 
 % DATASRC_CACHE_CREATE creating the hotspot cache
-Debug information that the hotspot cache was created at startup.
+This is a debug message issued during startup when the hotspot cache
+is created.
 
 % DATASRC_CACHE_DESTROY destroying the hotspot cache
 Debug information. The hotspot cache is being destroyed.
 
-% DATASRC_CACHE_DISABLE disabling the cache
-The hotspot cache is disabled from now on. It is not going to store
-information or return anything.
+% DATASRC_CACHE_DISABLE disabling the hotspot cache
+A debug message issued when the hotspot cache is disabled.
 
-% DATASRC_CACHE_ENABLE enabling the cache
-The hotspot cache is enabled from now on.
+% DATASRC_CACHE_ENABLE enabling the hotspot cache
+A debug message issued when the hotspot cache is enabled.
 
-% DATASRC_CACHE_EXPIRED the item '%1' is expired
-Debug information. There was an attempt to look up an item in the hotspot
-cache. And the item was actually there, but it was too old, so it was removed
-instead and nothing is reported (the external behaviour is the same as with
-CACHE_NOT_FOUND).
+% DATASRC_CACHE_EXPIRED item '%1' in the hotspot cache has expired
+A debug message issued when a hotspot cache lookup located the item but it
+had expired.  The item was removed and the program proceeded as if the item
+had not been found.
 
 % DATASRC_CACHE_FOUND the item '%1' was found
-Debug information. An item was successfully looked up in the hotspot cache.
+Debug information. An item was successfully located in the hotspot cache.
 
-% DATASRC_CACHE_FULL cache is full, dropping oldest
+% DATASRC_CACHE_FULL hotspot cache is full, dropping oldest
 Debug information. After inserting an item into the hotspot cache, the
 maximum number of items was exceeded, so the least recently used item will
 be dropped. This should be directly followed by CACHE_REMOVE.
 
-% DATASRC_CACHE_INSERT inserting item '%1' into the cache
-Debug information. It means a new item is being inserted into the hotspot
+% DATASRC_CACHE_INSERT inserting item '%1' into the hotspot cache
+A debug message indicating that a new item is being inserted into the hotspot
 cache.
 
-% DATASRC_CACHE_NOT_FOUND the item '%1' was not found
-Debug information. It was attempted to look up an item in the hotspot cache,
-but it is not there.
+% DATASRC_CACHE_NOT_FOUND the item '%1' was not found in the hotspot cache
+A debug message issued when hotspot cache was searched for the specified
+item but it was not found.
 
-% DATASRC_CACHE_OLD_FOUND older instance of cache item found, replacing
+% DATASRC_CACHE_OLD_FOUND older instance of hotspot cache item '%1' found, replacing
 Debug information. While inserting an item into the hotspot cache, an older
-instance of an item with the same name was found. The old instance will be
-removed. This should be directly followed by CACHE_REMOVE.
+instance of an item with the same name was found; the old instance will be
+removed. This will be directly followed by CACHE_REMOVE.
 
-% DATASRC_CACHE_REMOVE removing '%1' from the cache
+% DATASRC_CACHE_REMOVE removing '%1' from the hotspot cache
 Debug information. An item is being removed from the hotspot cache.
 
-% DATASRC_CACHE_SLOTS setting the cache size to '%1', dropping '%2' items
+% DATASRC_CACHE_SLOTS setting the hotspot cache size to '%1', dropping '%2' items
 The maximum allowed number of items of the hotspot cache is set to the given
 number. If there are too many, some of them will be dropped. The size of 0
 means no limit.
 
 % DATASRC_DO_QUERY handling query for '%1/%2'
-Debug information. We're processing some internal query for given name and
-type.
+A debug message indicating that a query for the given name and RR type is being
+processed.
 
 % DATASRC_MEM_ADD_RRSET adding RRset '%1/%2' into zone '%3'
 Debug information. An RRset is being added to the in-memory data source.
 
 % DATASRC_MEM_ADD_WILDCARD adding wildcards for '%1'
-Debug information. Some special marks above each * in wildcard name are needed.
-They are being added now for this name.
+This is a debug message issued during the processing of a wildcard
+name. The internal domain name tree is scanned and some nodes are
+specially marked to allow the wildcard lookup to succeed.
 
 % DATASRC_MEM_ADD_ZONE adding zone '%1/%2'
 Debug information. A zone is being added into the in-memory data source.
@@ -114,9 +114,9 @@ stop the search.
 Debug information. A DNAME was found instead of the requested information.
 
 % DATASRC_MEM_DNAME_NS DNAME and NS can't coexist in non-apex domain '%1'
-It was requested for DNAME and NS records to be put into the same domain
-which is not the apex (the top of the zone). This is forbidden by RFC
-2672, section 3. This indicates a problem with provided data.
+A request was made for DNAME and NS records to be put into the same
+domain which is not the apex (the top of the zone). This is forbidden
+by RFC 2672 (section 3) and indicates a problem with provided data.
 
 % DATASRC_MEM_DOMAIN_EMPTY requested domain '%1' is empty
 Debug information. The requested domain exists in the tree of domains, but
@@ -142,7 +142,7 @@ in-memory data source.
 % DATASRC_MEM_LOAD loading zone '%1' from file '%2'
 Debug information. The content of master file is being loaded into the memory.
 
-% DATASRC_MEM_NOTFOUND requested domain '%1' not found
+% DATASRC_MEM_NOT_FOUND requested domain '%1' not found
 Debug information. The requested domain does not exist.
 
 % DATASRC_MEM_NS_ENCOUNTERED encountered a NS
@@ -201,11 +201,11 @@ behave and BIND 9 refuses that as well. Please describe your intention using
 different tools.
 
 % DATASRC_META_ADD adding a data source into meta data source
-Debug information. Yet another data source is being added into the meta data
-source. (probably at startup or reconfiguration)
+This is a debug message issued during startup or reconfiguration.
+Another data source is being added into the meta data source.
 
 % DATASRC_META_ADD_CLASS_MISMATCH mismatch between classes '%1' and '%2'
-It was attempted to add a data source into a meta data source. But their
+It was attempted to add a data source into a meta data source, but their
 classes do not match.
 
 % DATASRC_META_REMOVE removing data source from meta data source
@@ -234,11 +234,11 @@ specific error already.
 The domain lives in another zone. But it is not possible to generate referral
 information for it.
 
-% DATASRC_QUERY_CACHED data for %1/%2 found in cache
+% DATASRC_QUERY_CACHED data for %1/%2 found in hotspot cache
 Debug information. The requested data were found in the hotspot cache, so
 no query is sent to the real data source.
 
-% DATASRC_QUERY_CHECK_CACHE checking cache for '%1/%2'
+% DATASRC_QUERY_CHECK_CACHE checking hotspot cache for '%1/%2'
 Debug information. While processing a query, lookup to the hotspot cache
 is being made.
 
@@ -251,10 +251,9 @@ Debug information. The software is trying to identify delegation points on the
 way down to the given domain.
 
 % DATASRC_QUERY_EMPTY_CNAME CNAME at '%1' is empty
-There was an CNAME and it was being followed. But it contains no records,
-so there's nowhere to go. There will be no answer. This indicates a problem
-with supplied data.
-We tried to follow
+A CNAME chain was being followed and an entry was found that pointed
+to a domain name that had no RRsets associated with it. As a result,
+the query cannot be answered. This indicates a problem with supplied data.
 
 % DATASRC_QUERY_EMPTY_DNAME the DNAME on '%1' is empty
 During an attempt to synthesize CNAME from this DNAME it was discovered the
@@ -262,11 +261,11 @@ DNAME is empty (it has no records). This indicates problem with supplied data.
 
 % DATASRC_QUERY_FAIL query failed
 Some subtask of query processing failed. The reason should have been reported
-already. We are returning SERVFAIL.
+already and a SERVFAIL will be returned to the querying system.
 
 % DATASRC_QUERY_FOLLOW_CNAME following CNAME at '%1'
-Debug information. The domain is a CNAME (or a DNAME and we created a CNAME
-for it already), so it's being followed.
+Debug information. The domain is a CNAME (or a DNAME and a CNAME for it
+has already been created) and the search is following this chain.
 
 % DATASRC_QUERY_GET_MX_ADDITIONAL addition of A/AAAA for '%1' requested by MX '%2'
 Debug information. While processing a query, a MX record was met. It
@@ -291,14 +290,14 @@ operation code.
 Debug information. The last DO_QUERY is an auth query.
 
 % DATASRC_QUERY_IS_GLUE glue query (%1/%2)
-Debug information. The last DO_QUERY is query for glue addresses.
+Debug information. The last DO_QUERY is a query for glue addresses.
 
 % DATASRC_QUERY_IS_NOGLUE query for non-glue addresses (%1/%2)
-Debug information. The last DO_QUERY is query for addresses that are not
+Debug information. The last DO_QUERY is a query for addresses that are not
 glue.
 
 % DATASRC_QUERY_IS_REF query for referral (%1/%2)
-Debug information. The last DO_QUERY is query for referral information.
+Debug information. The last DO_QUERY is a query for referral information.
 
 % DATASRC_QUERY_IS_SIMPLE simple query (%1/%2)
 Debug information. The last DO_QUERY is a simple query.
@@ -322,11 +321,11 @@ The underlying data source failed to answer the no-glue query. 1 means some
 error, 2 is not implemented. The data source should have logged the specific
 error already.
 
-% DATASRC_QUERY_NO_CACHE_ANY_AUTH ignoring cache for ANY query (%1/%2 in %3 class)
+% DATASRC_QUERY_NO_CACHE_ANY_AUTH ignoring hotspot cache for ANY query (%1/%2 in %3 class)
 Debug information. The hotspot cache is ignored for authoritative ANY queries
 for consistency reasons.
 
-% DATASRC_QUERY_NO_CACHE_ANY_SIMPLE ignoring cache for ANY query (%1/%2 in %3 class)
+% DATASRC_QUERY_NO_CACHE_ANY_SIMPLE ignoring hotspot cache for ANY query (%1/%2 in %3 class)
 Debug information. The hotspot cache is ignored for ANY queries for consistency
 reasons.
 
@@ -345,7 +344,7 @@ domain. Maybe someone sent a query to the wrong server for some reason.
 % DATASRC_QUERY_PROCESS processing query '%1/%2' in the '%3' class
 Debug information. A sure query is being processed now.
 
-% DATASRC_QUERY_PROVENX_FAIL unable to prove nonexistence of '%1'
+% DATASRC_QUERY_PROVE_NX_FAIL unable to prove nonexistence of '%1'
 The user wants DNSSEC and we discovered the entity doesn't exist (either
 domain or the record). But there was an error getting NSEC/NSEC3 record
 to prove the nonexistence.
@@ -365,9 +364,9 @@ error, 2 is not implemented. The data source should have logged the specific
 error already.
 
 % DATASRC_QUERY_SYNTH_CNAME synthesizing CNAME from DNAME on '%1'
-Debug information. While answering a query, a DNAME was met. The DNAME itself
-will be returned, but along with it a CNAME for clients which don't understand
-DNAMEs will be synthesized.
+This is a debug message. While answering a query, a DNAME was encountered. The
+DNAME itself will be returned, along with a synthesized CNAME for clients that
+do not understand the DNAME RR.
 
 % DATASRC_QUERY_TASK_FAIL task failed with %1
 The query subtask failed. The reason should have been reported by the subtask
@@ -391,7 +390,7 @@ domain is being looked for now.
 During an attempt to cover the domain by a wildcard an error happened. The
 exact kind was hopefully already reported.
 
-% DATASRC_QUERY_WILDCARD_PROVENX_FAIL unable to prove nonexistence of '%1' (%2)
+% DATASRC_QUERY_WILDCARD_PROVE_NX_FAIL unable to prove nonexistence of '%1' (%2)
 While processing a wildcard, it wasn't possible to prove nonexistence of the
 given domain or record.  The code is 1 for error and 2 for not implemented.
 
@@ -411,7 +410,7 @@ Debug information. An instance of SQLite data source is being destroyed.
 Debug information. The SQLite data source is trying to identify which zone
 should hold this domain.
 
-% DATASRC_SQLITE_ENCLOSURE_NOTFOUND no zone contains it
+% DATASRC_SQLITE_ENCLOSURE_NOT_FOUND no zone contains '%1'
 Debug information. The last SQLITE_ENCLOSURE query was unsuccessful; there's
 no such zone in our data.
 
@@ -464,20 +463,27 @@ Debug information. The SQLite data source is loading an SQLite database in
 the provided file.
 
 % DATASRC_SQLITE_PREVIOUS looking for name previous to '%1'
-Debug information. We're trying to look up name preceding the supplied one.
+This is a debug message.  The name given was not found, so the program
+is searching for the next name higher up the hierarchy (e.g. if
+www.example.com were queried for and not found, the software searches
+for the "previous" name, example.com).
 
 % DATASRC_SQLITE_PREVIOUS_NO_ZONE no zone containing '%1'
-The SQLite data source tried to identify name preceding this one. But this
-one is not contained in any zone in the data source.
+The name given was not found, so the program is searching for the next
+name higher up the hierarchy (e.g. if www.example.com were queried
+for and not found, the software searches for the "previous" name,
+example.com). However, this name is not contained in any zone in the
+data source. This is an error since it indicates a problem in the earlier
+processing of the query.
 
 % DATASRC_SQLITE_SETUP setting up SQLite database
 The database for SQLite data source was found empty. It is assumed this is the
 first run and it is being initialized with current schema.  It'll still contain
 no data, but it will be ready for use.
 
-% DATASRC_STATIC_BAD_CLASS static data source can handle CH only
-For some reason, someone asked the static data source a query that is not in
-the CH class.
+% DATASRC_STATIC_CLASS_NOT_CH static data source can handle CH class only
+An error message indicating that a query requesting a RR for a class other
+that CH was sent to the static data source (which only handles CH queries).
 
 % DATASRC_STATIC_CREATE creating the static datasource
 Debug information. The static data source (the one holding stuff like
diff --git a/src/lib/datasrc/memory_datasrc.cc b/src/lib/datasrc/memory_datasrc.cc
index b8019a2..6565000 100644
--- a/src/lib/datasrc/memory_datasrc.cc
+++ b/src/lib/datasrc/memory_datasrc.cc
@@ -129,7 +129,7 @@ struct MemoryZone::MemoryZoneImpl {
         // Ensure CNAME and other type of RR don't coexist for the same
         // owner name.
         if (rrset->getType() == RRType::CNAME()) {
-            // XXX: this check will become incorrect when we support DNSSEC
+            // TODO: this check will become incorrect when we support DNSSEC
             // (depending on how we support DNSSEC).  We should revisit it
             // at that point.
             if (!domain->empty()) {
@@ -523,7 +523,7 @@ struct MemoryZone::MemoryZoneImpl {
 
                 // fall through
             case DomainTree::NOTFOUND:
-                LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_NOTFOUND).
+                LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_NOT_FOUND).
                     arg(name);
                 return (FindResult(NXDOMAIN, ConstRRsetPtr()));
             case DomainTree::EXACTMATCH: // This one is OK, handle it
diff --git a/src/lib/datasrc/sqlite3_datasrc.cc b/src/lib/datasrc/sqlite3_datasrc.cc
index 13d98ed..18ee929 100644
--- a/src/lib/datasrc/sqlite3_datasrc.cc
+++ b/src/lib/datasrc/sqlite3_datasrc.cc
@@ -356,7 +356,8 @@ Sqlite3DataSrc::findClosestEnclosure(DataSrcMatch& match) const {
 
     unsigned int position;
     if (findClosest(match.getName(), &position) == -1) {
-        LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_SQLITE_ENCLOSURE_NOTFOUND);
+        LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_SQLITE_ENCLOSURE_NOT_FOUND)
+                  .arg(match.getName());
         return;
     }
 
diff --git a/src/lib/datasrc/static_datasrc.cc b/src/lib/datasrc/static_datasrc.cc
index dee14b9..65229a0 100644
--- a/src/lib/datasrc/static_datasrc.cc
+++ b/src/lib/datasrc/static_datasrc.cc
@@ -161,7 +161,7 @@ StaticDataSrc::findRRset(const Name& qname,
         arg(qtype);
     flags = 0;
     if (qclass != getClass() && qclass != RRClass::ANY()) {
-        LOG_ERROR(logger, DATASRC_STATIC_BAD_CLASS);
+        LOG_ERROR(logger, DATASRC_STATIC_CLASS_NOT_CH);
         return (ERROR);
     }
 
diff --git a/src/lib/dns/message.cc b/src/lib/dns/message.cc
index bf7ccd5..c5ba4e1 100644
--- a/src/lib/dns/message.cc
+++ b/src/lib/dns/message.cc
@@ -239,7 +239,28 @@ MessageImpl::toWire(AbstractMessageRenderer& renderer, TSIGContext* tsig_ctx) {
                   "Message rendering attempted without Opcode set");
     }
 
+    // Reserve the space for TSIG (if needed) so that we can handle truncation
+    // case correctly later when that happens.  orig_xxx variables remember
+    // some configured parameters of renderer in case they are needed in
+    // truncation processing below.
+    const size_t tsig_len = (tsig_ctx != NULL) ? tsig_ctx->getTSIGLength() : 0;
+    const size_t orig_msg_len_limit = renderer.getLengthLimit();
+    const AbstractMessageRenderer::CompressMode orig_compress_mode =
+        renderer.getCompressMode();
+    if (tsig_len > 0) {
+        if (tsig_len > orig_msg_len_limit) {
+            isc_throw(InvalidParameter, "Failed to render DNS message: "
+                      "too small limit for a TSIG (" <<
+                      orig_msg_len_limit << ")");
+        }
+        renderer.setLengthLimit(orig_msg_len_limit - tsig_len);
+    }
+
     // reserve room for the header
+    if (renderer.getLengthLimit() < HEADERLEN) {
+        isc_throw(InvalidParameter, "Failed to render DNS message: "
+                  "too small limit for a Header");
+    }
     renderer.skip(HEADERLEN);
 
     uint16_t qdcount =
@@ -284,6 +305,22 @@ MessageImpl::toWire(AbstractMessageRenderer& renderer, TSIGContext* tsig_ctx) {
         }
     }
 
+    // If we're adding a TSIG to a truncated message, clear all RRsets
+    // from the message except for the question before adding the TSIG.
+    // If even (some of) the question doesn't fit, don't include it.
+    if (tsig_ctx != NULL && renderer.isTruncated()) {
+        renderer.clear();
+        renderer.setLengthLimit(orig_msg_len_limit - tsig_len);
+        renderer.setCompressMode(orig_compress_mode);
+        renderer.skip(HEADERLEN);
+        qdcount = for_each(questions_.begin(), questions_.end(),
+                           RenderSection<QuestionPtr>(renderer,
+                                                      false)).getTotalCount();
+        ancount = 0;
+        nscount = 0;
+        arcount = 0;
+    }
+
     // Adjust the counter buffer.
     // XXX: these may not be equal to the number of corresponding entries
     // in rrsets_[] or questions_ if truncation occurred or an EDNS OPT RR
@@ -315,10 +352,16 @@ MessageImpl::toWire(AbstractMessageRenderer& renderer, TSIGContext* tsig_ctx) {
     renderer.writeUint16At(arcount, header_pos);
 
     // Add TSIG, if necessary, at the end of the message.
-    // TODO: truncate case consideration
     if (tsig_ctx != NULL) {
-        tsig_ctx->sign(qid_, renderer.getData(),
-                       renderer.getLength())->toWire(renderer);
+        // Release the reserved space in the renderer.
+        renderer.setLengthLimit(orig_msg_len_limit);
+
+        const int tsig_count =
+            tsig_ctx->sign(qid_, renderer.getData(),
+                           renderer.getLength())->toWire(renderer);
+        if (tsig_count != 1) {
+            isc_throw(Unexpected, "Failed to render a TSIG RR");
+        }
 
         // update the ARCOUNT for the TSIG RR.  Note that for a sane DNS
         // message arcount should never overflow to 0.
diff --git a/src/lib/dns/message.h b/src/lib/dns/message.h
index fcc53e9..6a8bf9f 100644
--- a/src/lib/dns/message.h
+++ b/src/lib/dns/message.h
@@ -565,6 +565,17 @@ public:
     /// \c tsig_ctx will be updated based on the fact it was used for signing
     /// and with the latest MAC.
     ///
+    /// \exception InvalidMessageOperation The message is not in the Render
+    /// mode, or either Rcode or Opcode is not set.
+    /// \exception InvalidParameter The allowable limit of \c renderer is too
+    /// small for a TSIG or the Header section.  Note that this shouldn't
+    /// happen with parameters as defined in the standard protocols,
+    /// so it's more likely a program bug.
+    /// \exception Unexpected Rendering the TSIG RR fails.  The implementation
+    /// internally makes sure this doesn't happen, so if that ever occurs
+    /// it should mean a bug either in the TSIG context or in the renderer
+    /// implementation.
+    ///
     /// \param renderer See the other version
     /// \param tsig_ctx A TSIG context that is to be used for signing the
     /// message
diff --git a/src/lib/dns/python/message_python.cc b/src/lib/dns/python/message_python.cc
index 2842588..00596f8 100644
--- a/src/lib/dns/python/message_python.cc
+++ b/src/lib/dns/python/message_python.cc
@@ -703,6 +703,15 @@ Message_toWire(s_Message* self, PyObject* args) {
             // python program has a bug.
             PyErr_SetString(po_TSIGContextError, ex.what());
             return (NULL);
+        } catch (const std::exception& ex) {
+            // Other exceptions should be rare (most likely an implementation
+            // bug)
+            PyErr_SetString(po_TSIGContextError, ex.what());
+            return (NULL);
+        } catch (...) {
+            PyErr_SetString(PyExc_RuntimeError,
+                            "Unexpected C++ exception in Message.to_wire");
+            return (NULL);
         }
     }
     PyErr_Clear();
diff --git a/src/lib/dns/python/tests/message_python_test.py b/src/lib/dns/python/tests/message_python_test.py
index 41b9a67..c731253 100644
--- a/src/lib/dns/python/tests/message_python_test.py
+++ b/src/lib/dns/python/tests/message_python_test.py
@@ -21,6 +21,7 @@ import unittest
 import os
 from pydnspp import *
 from testutil import *
+from pyunittests_util import fix_current_time
 
 # helper functions for tests taken from c++ unittests
 if "TESTDATA_PATH" in os.environ:
@@ -31,7 +32,7 @@ else:
 def factoryFromFile(message, file):
     data = read_wire_data(file)
     message.from_wire(data)
-    pass
+    return data
 
 # we don't have direct comparison for rrsets right now (should we?
 # should go in the cpp version first then), so also no direct list
@@ -44,6 +45,15 @@ def compare_rrset_list(list1, list2):
             return False
     return True
 
+# These are used for TSIG + TC tests
+LONG_TXT1 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde";
+
+LONG_TXT2 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456";
+
+LONG_TXT3 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef01";
+
+LONG_TXT4 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0";
+
 # a complete message taken from cpp tests, for testing towire and totext
 def create_message():
     message_render = Message(Message.RENDER)
@@ -62,16 +72,12 @@ def create_message():
     message_render.add_rrset(Message.SECTION_ANSWER, rrset)
     return message_render
 
-def strip_mutable_tsig_data(data):
-    # Unfortunately we cannot easily compare TSIG RR because we can't tweak
-    # current time.  As a work around this helper function strips off the time
-    # dependent part of TSIG RDATA, i.e., the MAC (assuming HMAC-MD5) and
-    # Time Signed.
-    return data[0:-32] + data[-26:-22] + data[-6:]
-
 class MessageTest(unittest.TestCase):
 
     def setUp(self):
+        # make sure we don't use faked time unless explicitly do so in tests
+        fix_current_time(None)
+
         self.p = Message(Message.PARSE)
         self.r = Message(Message.RENDER)
 
@@ -90,6 +96,10 @@ class MessageTest(unittest.TestCase):
         self.tsig_key = TSIGKey("www.example.com:SFuWd/q99SzF8Yzd1QbB9g==")
         self.tsig_ctx = TSIGContext(self.tsig_key)
 
+    def tearDown(self):
+        # reset any faked current time setting (it would affect other tests)
+        fix_current_time(None)
+
     def test_init(self):
         self.assertRaises(TypeError, Message, -1)
         self.assertRaises(TypeError, Message, 3)
@@ -285,33 +295,112 @@ class MessageTest(unittest.TestCase):
         self.assertRaises(InvalidMessageOperation, self.r.to_wire,
                           MessageRenderer())
 
-    def __common_tsigquery_setup(self):
+    def __common_tsigmessage_setup(self, flags=[Message.HEADERFLAG_RD],
+                                   rrtype=RRType("A"), answer_data=None):
         self.r.set_opcode(Opcode.QUERY())
         self.r.set_rcode(Rcode.NOERROR())
-        self.r.set_header_flag(Message.HEADERFLAG_RD)
+        for flag in flags:
+            self.r.set_header_flag(flag)
+        if answer_data is not None:
+            rrset = RRset(Name("www.example.com"), RRClass("IN"),
+                          rrtype, RRTTL(86400))
+            for rdata in answer_data:
+                rrset.add_rdata(Rdata(rrtype, RRClass("IN"), rdata))
+            self.r.add_rrset(Message.SECTION_ANSWER, rrset)
         self.r.add_question(Question(Name("www.example.com"),
-                                     RRClass("IN"), RRType("A")))
+                                     RRClass("IN"), rrtype))
 
     def __common_tsig_checks(self, expected_file):
         renderer = MessageRenderer()
         self.r.to_wire(renderer, self.tsig_ctx)
-        actual_wire = strip_mutable_tsig_data(renderer.get_data())
-        expected_wire = strip_mutable_tsig_data(read_wire_data(expected_file))
-        self.assertEqual(expected_wire, actual_wire)
+        self.assertEqual(read_wire_data(expected_file), renderer.get_data())
 
     def test_to_wire_with_tsig(self):
+        fix_current_time(0x4da8877a)
         self.r.set_qid(0x2d65)
-        self.__common_tsigquery_setup()
+        self.__common_tsigmessage_setup()
         self.__common_tsig_checks("message_toWire2.wire")
 
     def test_to_wire_with_edns_tsig(self):
+        fix_current_time(0x4db60d1f)
         self.r.set_qid(0x6cd)
-        self.__common_tsigquery_setup()
+        self.__common_tsigmessage_setup()
         edns = EDNS()
         edns.set_udp_size(4096)
         self.r.set_edns(edns)
         self.__common_tsig_checks("message_toWire3.wire")
 
+    def test_to_wire_tsig_truncation(self):
+        fix_current_time(0x4e179212)
+        data = factoryFromFile(self.p, "message_fromWire17.wire")
+        self.assertEqual(TSIGError.NOERROR,
+                         self.tsig_ctx.verify(self.p.get_tsig_record(), data))
+        self.r.set_qid(0x22c2)
+        self.__common_tsigmessage_setup([Message.HEADERFLAG_QR,
+                                         Message.HEADERFLAG_AA,
+                                         Message.HEADERFLAG_RD],
+                                        RRType("TXT"),
+                                        [LONG_TXT1, LONG_TXT2])
+        self.__common_tsig_checks("message_toWire4.wire")
+
+    def test_to_wire_tsig_truncation2(self):
+        fix_current_time(0x4e179212)
+        data = factoryFromFile(self.p, "message_fromWire17.wire")
+        self.assertEqual(TSIGError.NOERROR,
+                         self.tsig_ctx.verify(self.p.get_tsig_record(), data))
+        self.r.set_qid(0x22c2)
+        self.__common_tsigmessage_setup([Message.HEADERFLAG_QR,
+                                         Message.HEADERFLAG_AA,
+                                         Message.HEADERFLAG_RD],
+                                        RRType("TXT"),
+                                        [LONG_TXT1, LONG_TXT3])
+        self.__common_tsig_checks("message_toWire4.wire")
+
+    def test_to_wire_tsig_truncation3(self):
+        self.r.set_opcode(Opcode.QUERY())
+        self.r.set_rcode(Rcode.NOERROR())
+        for i in range(1, 68):
+            self.r.add_question(Question(Name("www.example.com"),
+                                         RRClass("IN"), RRType(i)))
+        renderer = MessageRenderer()
+        self.r.to_wire(renderer, self.tsig_ctx)
+
+        self.p.from_wire(renderer.get_data())
+        self.assertTrue(self.p.get_header_flag(Message.HEADERFLAG_TC))
+        self.assertEqual(66, self.p.get_rr_count(Message.SECTION_QUESTION))
+        self.assertNotEqual(None, self.p.get_tsig_record())
+
+    def test_to_wire_tsig_no_truncation(self):
+        fix_current_time(0x4e17b38d)
+        data = factoryFromFile(self.p, "message_fromWire18.wire")
+        self.assertEqual(TSIGError.NOERROR,
+                         self.tsig_ctx.verify(self.p.get_tsig_record(), data))
+        self.r.set_qid(0xd6e2)
+        self.__common_tsigmessage_setup([Message.HEADERFLAG_QR,
+                                         Message.HEADERFLAG_AA,
+                                         Message.HEADERFLAG_RD],
+                                        RRType("TXT"),
+                                        [LONG_TXT1, LONG_TXT4])
+        self.__common_tsig_checks("message_toWire5.wire")
+
+    def test_to_wire_tsig_length_errors(self):
+        renderer = MessageRenderer()
+        renderer.set_length_limit(84) # 84 = expected TSIG length - 1
+        self.__common_tsigmessage_setup()
+        self.assertRaises(TSIGContextError,
+                          self.r.to_wire, renderer, self.tsig_ctx)
+
+        renderer.clear()
+        self.r.clear(Message.RENDER)
+        renderer.set_length_limit(86) # 86 = expected TSIG length + 1
+        self.__common_tsigmessage_setup()
+        self.assertRaises(TSIGContextError,
+                          self.r.to_wire, renderer, self.tsig_ctx)
+
+        # skip the last test of the corresponding C++ test: it requires
+        # subclassing MessageRenderer, which is (currently) not possible
+        # for python.  In any case, it's very unlikely to happen in practice.
+
     def test_to_text(self):
         message_render = create_message()
         
diff --git a/src/lib/dns/python/tests/question_python_test.py b/src/lib/dns/python/tests/question_python_test.py
index 69e3051..8c8c815 100644
--- a/src/lib/dns/python/tests/question_python_test.py
+++ b/src/lib/dns/python/tests/question_python_test.py
@@ -74,7 +74,6 @@ class QuestionTest(unittest.TestCase):
         self.assertEqual("foo.example.com. IN NS\n", str(self.test_question1))
         self.assertEqual("bar.example.com. CH A\n", self.test_question2.to_text())
     
-    
     def test_to_wire_buffer(self):
         obuffer = bytes()
         obuffer = self.test_question1.to_wire(obuffer)
@@ -82,7 +81,6 @@ class QuestionTest(unittest.TestCase):
         wiredata = read_wire_data("question_toWire1")
         self.assertEqual(obuffer, wiredata)
     
-    
     def test_to_wire_renderer(self):
         renderer = MessageRenderer()
         self.test_question1.to_wire(renderer)
@@ -91,5 +89,13 @@ class QuestionTest(unittest.TestCase):
         self.assertEqual(renderer.get_data(), wiredata)
         self.assertRaises(TypeError, self.test_question1.to_wire, 1)
 
+    def test_to_wire_truncated(self):
+        renderer = MessageRenderer()
+        renderer.set_length_limit(self.example_name1.get_length())
+        self.assertFalse(renderer.is_truncated())
+        self.test_question1.to_wire(renderer)
+        self.assertTrue(renderer.is_truncated())
+        self.assertEqual(0, renderer.get_length())
+
 if __name__ == '__main__':
     unittest.main()
diff --git a/src/lib/dns/question.cc b/src/lib/dns/question.cc
index 96e2a9c..6ccb164 100644
--- a/src/lib/dns/question.cc
+++ b/src/lib/dns/question.cc
@@ -57,10 +57,19 @@ Question::toWire(OutputBuffer& buffer) const {
 
 unsigned int
 Question::toWire(AbstractMessageRenderer& renderer) const {
+    const size_t pos0 = renderer.getLength();
+
     renderer.writeName(name_);
     rrtype_.toWire(renderer);
     rrclass_.toWire(renderer);
 
+    // Make sure the renderer has a room for the question
+    if (renderer.getLength() > renderer.getLengthLimit()) {
+        renderer.trim(renderer.getLength() - pos0);
+        renderer.setTruncated();
+        return (0);
+    }
+
     return (1);                 // number of "entries"
 }
 
diff --git a/src/lib/dns/question.h b/src/lib/dns/question.h
index b3f3d98..5d2783b 100644
--- a/src/lib/dns/question.h
+++ b/src/lib/dns/question.h
@@ -201,23 +201,23 @@ public:
     /// class description).
     ///
     /// The owner name will be compressed if possible, although it's an
-    /// unlikely event in practice because the %Question section a DNS
+    /// unlikely event in practice because the Question section a DNS
     /// message normally doesn't contain multiple question entries and
     /// it's located right after the Header section.
     /// Nevertheless, \c renderer records the information of the owner name
     /// so that it can be pointed by other RRs in other sections (which is
     /// more likely to happen).
     ///
-    /// In theory, an attempt to render a Question may cause truncation
-    /// (when the Question section contains a large number of entries),
-    /// but this implementation doesn't catch that situation.
-    /// It would make the code unnecessarily complicated (though perhaps
-    /// slightly) for almost impossible case in practice.
-    /// An upper layer will handle the pathological case as a general error.
+    /// It could be possible, though very rare in practice, that
+    /// an attempt to render a Question may cause truncation
+    /// (when the Question section contains a large number of entries).
+    /// In such a case this method avoid the rendering and indicate the
+    /// truncation in the \c renderer.  This method returns 0 in this case.
     ///
     /// \param renderer DNS message rendering context that encapsulates the
     /// output buffer and name compression information.
-    /// \return 1
+    ///
+    /// \return 1 on success; 0 if it causes truncation
     unsigned int toWire(AbstractMessageRenderer& renderer) const;
 
     /// \brief Render the Question in the wire format without name compression.
diff --git a/src/lib/dns/tests/message_unittest.cc b/src/lib/dns/tests/message_unittest.cc
index c79ea2c..6430626 100644
--- a/src/lib/dns/tests/message_unittest.cc
+++ b/src/lib/dns/tests/message_unittest.cc
@@ -62,7 +62,6 @@ using namespace isc::dns::rdata;
 //
 
 const uint16_t Message::DEFAULT_MAX_UDPSIZE;
-const Name test_name("test.example.com");
 
 namespace isc {
 namespace util {
@@ -79,7 +78,8 @@ const uint16_t TSIGContext::DEFAULT_FUDGE;
 namespace {
 class MessageTest : public ::testing::Test {
 protected:
-    MessageTest() : obuffer(0), renderer(obuffer),
+    MessageTest() : test_name("test.example.com"), obuffer(0),
+                    renderer(obuffer),
                     message_parse(Message::PARSE),
                     message_render(Message::RENDER),
                     bogus_section(static_cast<Message::Section>(
@@ -103,8 +103,9 @@ protected:
                                              "FAKEFAKEFAKEFAKE"));
         rrset_aaaa->addRRsig(rrset_rrsig);
     }
-    
+
     static Question factoryFromFile(const char* datafile);
+    const Name test_name;
     OutputBuffer obuffer;
     MessageRenderer renderer;
     Message message_parse;
@@ -114,17 +115,18 @@ protected:
     RRsetPtr rrset_aaaa;        // AAAA RRset with one RDATA with RRSIG
     RRsetPtr rrset_rrsig;       // RRSIG for the AAAA RRset
     TSIGContext tsig_ctx;
+    vector<unsigned char> received_data;
     vector<unsigned char> expected_data;
 
-    static void factoryFromFile(Message& message, const char* datafile);
+    void factoryFromFile(Message& message, const char* datafile);
 };
 
 void
 MessageTest::factoryFromFile(Message& message, const char* datafile) {
-    std::vector<unsigned char> data;
-    UnitTestUtil::readWireData(datafile, data);
+    received_data.clear();
+    UnitTestUtil::readWireData(datafile, received_data);
 
-    InputBuffer buffer(&data[0], data.size());
+    InputBuffer buffer(&received_data[0], received_data.size());
     message.fromWire(buffer);
 }
 
@@ -618,15 +620,43 @@ testGetTime() {
     return (NOW);
 }
 
+// bit-wise constant flags to configure DNS header flags for test
+// messages.
+const unsigned int QR_FLAG = 0x1;
+const unsigned int AA_FLAG = 0x2;
+const unsigned int RD_FLAG = 0x4;
+
 void
 commonTSIGToWireCheck(Message& message, MessageRenderer& renderer,
-                      TSIGContext& tsig_ctx, const char* const expected_file)
+                      TSIGContext& tsig_ctx, const char* const expected_file,
+                      unsigned int message_flags = RD_FLAG,
+                      RRType qtype = RRType::A(),
+                      const vector<const char*>* answer_data = NULL)
 {
     message.setOpcode(Opcode::QUERY());
     message.setRcode(Rcode::NOERROR());
-    message.setHeaderFlag(Message::HEADERFLAG_RD, true);
+    if ((message_flags & QR_FLAG) != 0) {
+        message.setHeaderFlag(Message::HEADERFLAG_QR);
+    }
+    if ((message_flags & AA_FLAG) != 0) {
+        message.setHeaderFlag(Message::HEADERFLAG_AA);
+    }
+    if ((message_flags & RD_FLAG) != 0) {
+        message.setHeaderFlag(Message::HEADERFLAG_RD);
+    }
     message.addQuestion(Question(Name("www.example.com"), RRClass::IN(),
-                                 RRType::A()));
+                                 qtype));
+
+    if (answer_data != NULL) {
+        RRsetPtr ans_rrset(new RRset(Name("www.example.com"), RRClass::IN(),
+                                     qtype, RRTTL(86400)));
+        for (vector<const char*>::const_iterator it = answer_data->begin();
+             it != answer_data->end();
+             ++it) {
+            ans_rrset->addRdata(createRdata(qtype, RRClass::IN(), *it));
+        }
+        message.addRRset(Message::SECTION_ANSWER, ans_rrset);
+    }
 
     message.toWire(renderer, tsig_ctx);
     vector<unsigned char> expected_data;
@@ -670,6 +700,182 @@ TEST_F(MessageTest, toWireWithEDNSAndTSIG) {
     }
 }
 
+// Some of the following tests involve truncation.  We use the query name
+// "www.example.com" and some TXT question/answers.  The length of the
+// header and question will be 33 bytes.  If we also try to include a
+// TSIG of the same key name (not compressed) with HMAC-MD5, the TSIG RR
+// will be 85 bytes.
+
+// A long TXT RDATA.  With a fully compressed owner name, the corresponding
+// RR will be 268 bytes.
+const char* const long_txt1 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde";
+
+// With a fully compressed owner name, the corresponding RR will be 212 bytes.
+// It should result in truncation even without TSIG (33 + 268 + 212 = 513)
+const char* const long_txt2 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456";
+
+// With a fully compressed owner name, the corresponding RR will be 127 bytes.
+// So, it can fit in the standard 512 bytes with txt1 and without TSIG, but
+// adding a TSIG would result in truncation (33 + 268 + 127 + 85 = 513)
+const char* const long_txt3 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef01";
+
+// This is 1 byte shorter than txt3, which will result in a possible longest
+// message containing answer RRs and TSIG.
+const char* const long_txt4 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0";
+
+// Example output generated by
+// "dig -y www.example.com:SFuWd/q99SzF8Yzd1QbB9g== www.example.com txt
+// QID: 0x22c2
+// Time Signed: 0x00004e179212
+TEST_F(MessageTest, toWireTSIGTruncation) {
+    isc::util::detail::gettimeFunction = testGetTime<0x4e179212>;
+
+    // Verify a validly signed query so that we can use the TSIG context
+
+    factoryFromFile(message_parse, "message_fromWire17.wire");
+    EXPECT_EQ(TSIGError::NOERROR(),
+              tsig_ctx.verify(message_parse.getTSIGRecord(),
+                              &received_data[0], received_data.size()));
+
+    message_render.setQid(0x22c2);
+    vector<const char*> answer_data;
+    answer_data.push_back(long_txt1);
+    answer_data.push_back(long_txt2);
+    {
+        SCOPED_TRACE("Message sign with TSIG and TC bit on");
+        commonTSIGToWireCheck(message_render, renderer, tsig_ctx,
+                              "message_toWire4.wire",
+                              QR_FLAG|AA_FLAG|RD_FLAG,
+                              RRType::TXT(), &answer_data);
+    }
+}
+
+TEST_F(MessageTest, toWireTSIGTruncation2) {
+    // Similar to the previous test, but without TSIG it wouldn't cause
+    // truncation.
+    isc::util::detail::gettimeFunction = testGetTime<0x4e179212>;
+    factoryFromFile(message_parse, "message_fromWire17.wire");
+    EXPECT_EQ(TSIGError::NOERROR(),
+              tsig_ctx.verify(message_parse.getTSIGRecord(),
+                              &received_data[0], received_data.size()));
+
+    message_render.setQid(0x22c2);
+    vector<const char*> answer_data;
+    answer_data.push_back(long_txt1);
+    answer_data.push_back(long_txt3);
+    {
+        SCOPED_TRACE("Message sign with TSIG and TC bit on (2)");
+        commonTSIGToWireCheck(message_render, renderer, tsig_ctx,
+                              "message_toWire4.wire",
+                              QR_FLAG|AA_FLAG|RD_FLAG,
+                              RRType::TXT(), &answer_data);
+    }
+}
+
+TEST_F(MessageTest, toWireTSIGTruncation3) {
+    // Similar to previous ones, but truncation occurs due to too many
+    // Questions (very unusual, but not necessarily illegal).
+
+    // We are going to create a message starting with a standard
+    // header (12 bytes) and multiple questions in the Question
+    // section of the same owner name (changing the RRType, just so
+    // that it would be the form that would be accepted by the BIND 9
+    // parser).  The first Question is 21 bytes in length, and the subsequent
+    // ones are 6 bytes.  We'll also use a TSIG whose size is 85 bytes.
+    // Up to 66 questions can fit in the standard 512-byte buffer
+    // (12 + 21 + 6 * 65 + 85 = 508).  If we try to add one more it would
+    // result in truncation.
+    message_render.setOpcode(Opcode::QUERY());
+    message_render.setRcode(Rcode::NOERROR());
+    for (int i = 1; i <= 67; ++i) {
+        message_render.addQuestion(Question(Name("www.example.com"),
+                                            RRClass::IN(), RRType(i)));
+    }
+    message_render.toWire(renderer, tsig_ctx);
+
+    // Check the rendered data by parsing it.  We only check it has the
+    // TC bit on, has the correct number of questions, and has a TSIG RR.
+    // Checking the signature wouldn't be necessary for this rare case
+    // scenario.
+    InputBuffer buffer(renderer.getData(), renderer.getLength());
+    message_parse.fromWire(buffer);
+    EXPECT_TRUE(message_parse.getHeaderFlag(Message::HEADERFLAG_TC));
+    // Note that the number of questions are 66, not 67 as we tried to add.
+    EXPECT_EQ(66, message_parse.getRRCount(Message::SECTION_QUESTION));
+    EXPECT_TRUE(message_parse.getTSIGRecord() != NULL);
+}
+
+TEST_F(MessageTest, toWireTSIGNoTruncation) {
+    // A boundary case that shouldn't cause truncation: the resulting
+    // response message with a TSIG will be 512 bytes long.
+    isc::util::detail::gettimeFunction = testGetTime<0x4e17b38d>;
+    factoryFromFile(message_parse, "message_fromWire18.wire");
+    EXPECT_EQ(TSIGError::NOERROR(),
+              tsig_ctx.verify(message_parse.getTSIGRecord(),
+                              &received_data[0], received_data.size()));
+
+    message_render.setQid(0xd6e2);
+    vector<const char*> answer_data;
+    answer_data.push_back(long_txt1);
+    answer_data.push_back(long_txt4);
+    {
+        SCOPED_TRACE("Message sign with TSIG, no truncation");
+        commonTSIGToWireCheck(message_render, renderer, tsig_ctx,
+                              "message_toWire5.wire",
+                              QR_FLAG|AA_FLAG|RD_FLAG,
+                              RRType::TXT(), &answer_data);
+    }
+}
+
+// This is a buggy renderer for testing.  It behaves like the straightforward
+// MessageRenderer, but once it has some data, its setLengthLimit() ignores
+// the given parameter and resets the limit to the current length, making
+// subsequent insertion result in truncation, which would make TSIG RR
+// rendering fail unexpectedly in the test that follows.
+class BadRenderer : public MessageRenderer {
+public:
+    BadRenderer(isc::util::OutputBuffer& buffer) :
+        MessageRenderer(buffer)
+    {}
+    virtual void setLengthLimit(size_t len) {
+        if (getLength() > 0) {
+            MessageRenderer::setLengthLimit(getLength());
+        } else {
+            MessageRenderer::setLengthLimit(len);
+        }
+    }
+};
+
+TEST_F(MessageTest, toWireTSIGLengthErrors) {
+    // specify an unusual short limit that wouldn't be able to hold
+    // the TSIG.
+    renderer.setLengthLimit(tsig_ctx.getTSIGLength() - 1);
+    // Use commonTSIGToWireCheck() only to call toWire() with otherwise valid
+    // conditions.  The checks inside it don't matter because we expect an
+    // exception before any of the checks.
+    EXPECT_THROW(commonTSIGToWireCheck(message_render, renderer, tsig_ctx,
+                                       "message_toWire2.wire"),
+                 InvalidParameter);
+
+    // This one is large enough for TSIG, but the remaining limit isn't
+    // even enough for the Header section.
+    renderer.clear();
+    message_render.clear(Message::RENDER);
+    renderer.setLengthLimit(tsig_ctx.getTSIGLength() + 1);
+    EXPECT_THROW(commonTSIGToWireCheck(message_render, renderer, tsig_ctx,
+                                       "message_toWire2.wire"),
+                 InvalidParameter);
+
+    // Trying to render a message with TSIG using a buggy renderer.
+    obuffer.clear();
+    BadRenderer bad_renderer(obuffer);
+    bad_renderer.setLengthLimit(512);
+    message_render.clear(Message::RENDER);
+    EXPECT_THROW(commonTSIGToWireCheck(message_render, bad_renderer, tsig_ctx,
+                                       "message_toWire2.wire"),
+                 Unexpected);
+}
+
 TEST_F(MessageTest, toWireWithoutOpcode) {
     message_render.setRcode(Rcode::NOERROR());
     EXPECT_THROW(message_render.toWire(renderer), InvalidMessageOperation);
diff --git a/src/lib/dns/tests/question_unittest.cc b/src/lib/dns/tests/question_unittest.cc
index 25fd75b..1d483f2 100644
--- a/src/lib/dns/tests/question_unittest.cc
+++ b/src/lib/dns/tests/question_unittest.cc
@@ -106,6 +106,22 @@ TEST_F(QuestionTest, toWireRenderer) {
                         obuffer.getLength(), &wiredata[0], wiredata.size());
 }
 
+TEST_F(QuestionTest, toWireTruncated) {
+    // If the available length in the renderer is too small, it would require
+    // truncation.  This won't happen in normal cases, but protocol wise it
+    // could still happen if and when we support some (possibly future) opcode
+    // that allows multiple questions.
+
+    // Set the length limit to the qname length so that the whole question
+    // would request truncated
+    renderer.setLengthLimit(example_name1.getLength());
+
+    EXPECT_FALSE(renderer.isTruncated()); // check pre-render condition
+    EXPECT_EQ(0, test_question1.toWire(renderer));
+    EXPECT_TRUE(renderer.isTruncated());
+    EXPECT_EQ(0, renderer.getLength()); // renderer shouldn't have any data
+}
+
 // test operator<<.  We simply confirm it appends the result of toText().
 TEST_F(QuestionTest, LeftShiftOperator) {
     ostringstream oss;
diff --git a/src/lib/dns/tests/testdata/Makefile.am b/src/lib/dns/tests/testdata/Makefile.am
index cb1bb1c..257f2f3 100644
--- a/src/lib/dns/tests/testdata/Makefile.am
+++ b/src/lib/dns/tests/testdata/Makefile.am
@@ -5,8 +5,10 @@ BUILT_SOURCES += edns_toWire4.wire
 BUILT_SOURCES += message_fromWire10.wire message_fromWire11.wire
 BUILT_SOURCES += message_fromWire12.wire message_fromWire13.wire
 BUILT_SOURCES += message_fromWire14.wire message_fromWire15.wire
-BUILT_SOURCES += message_fromWire16.wire
+BUILT_SOURCES += message_fromWire16.wire message_fromWire17.wire
+BUILT_SOURCES += message_fromWire18.wire
 BUILT_SOURCES += message_toWire2.wire message_toWire3.wire
+BUILT_SOURCES += message_toWire4.wire message_toWire5.wire
 BUILT_SOURCES += message_toText1.wire message_toText2.wire
 BUILT_SOURCES += message_toText3.wire
 BUILT_SOURCES += name_toWire5.wire name_toWire6.wire
@@ -59,7 +61,9 @@ EXTRA_DIST += message_fromWire9 message_fromWire10.spec
 EXTRA_DIST += message_fromWire11.spec message_fromWire12.spec
 EXTRA_DIST += message_fromWire13.spec message_fromWire14.spec
 EXTRA_DIST += message_fromWire15.spec message_fromWire16.spec
+EXTRA_DIST += message_fromWire17.spec message_fromWire18.spec
 EXTRA_DIST += message_toWire1 message_toWire2.spec message_toWire3.spec
+EXTRA_DIST += message_toWire4.spec message_toWire5.spec
 EXTRA_DIST += message_toText1.txt message_toText1.spec
 EXTRA_DIST += message_toText2.txt message_toText2.spec
 EXTRA_DIST += message_toText3.txt message_toText3.spec
diff --git a/src/lib/dns/tests/testdata/gen-wiredata.py.in b/src/lib/dns/tests/testdata/gen-wiredata.py.in
index fd98c6e..818c6e9 100755
--- a/src/lib/dns/tests/testdata/gen-wiredata.py.in
+++ b/src/lib/dns/tests/testdata/gen-wiredata.py.in
@@ -307,8 +307,8 @@ class SOA(RR):
                                                 self.retry, self.expire,
                                                 self.minimum))
 
-class TXT:
-    rdlen = -1                  # auto-calculate
+class TXT(RR):
+    rdlen = None                # auto-calculate
     nstring = 1                 # number of character-strings
     stringlen = -1              # default string length, auto-calculate
     string = 'Test String'      # default string
@@ -330,11 +330,9 @@ class TXT:
                 stringlen_list.append(self.stringlen)
             if stringlen_list[-1] < 0:
                 stringlen_list[-1] = int(len(wirestring_list[-1]) / 2)
-        rdlen = self.rdlen
-        if rdlen < 0:
-            rdlen = int(len(''.join(wirestring_list)) / 2) + self.nstring
-        f.write('\n# TXT RDATA (RDLEN=%d)\n' % rdlen)
-        f.write('%04x\n' % rdlen);
+        if self.rdlen is None:
+            self.rdlen = int(len(''.join(wirestring_list)) / 2) + self.nstring
+        self.dump_header(f, self.rdlen)
         for i in range(0, self.nstring):
             f.write('# String Len=%d, String=\"%s\"\n' %
                     (stringlen_list[i], string_list[i]))
diff --git a/src/lib/dns/tests/testdata/message_fromWire17.spec b/src/lib/dns/tests/testdata/message_fromWire17.spec
new file mode 100644
index 0000000..366cf05
--- /dev/null
+++ b/src/lib/dns/tests/testdata/message_fromWire17.spec
@@ -0,0 +1,22 @@
+#
+# A simple DNS query message with TSIG signed
+#
+
+[custom]
+sections: header:question:tsig
+[header]
+id: 0x22c2
+rd: 1
+arcount: 1
+[question]
+name: www.example.com
+rrtype: TXT
+[tsig]
+as_rr: True
+# TSIG QNAME won't be compressed
+rr_name: www.example.com
+algorithm: hmac-md5
+time_signed: 0x4e179212
+mac_size: 16
+mac: 0x8214b04634e32323d651ac60b08e6388
+original_id: 0x22c2
diff --git a/src/lib/dns/tests/testdata/message_fromWire18.spec b/src/lib/dns/tests/testdata/message_fromWire18.spec
new file mode 100644
index 0000000..0b2592a
--- /dev/null
+++ b/src/lib/dns/tests/testdata/message_fromWire18.spec
@@ -0,0 +1,23 @@
+#
+# Another simple DNS query message with TSIG signed.  Only ID and time signed
+# (and MAC as a result) are different.
+#
+
+[custom]
+sections: header:question:tsig
+[header]
+id: 0xd6e2
+rd: 1
+arcount: 1
+[question]
+name: www.example.com
+rrtype: TXT
+[tsig]
+as_rr: True
+# TSIG QNAME won't be compressed
+rr_name: www.example.com
+algorithm: hmac-md5
+time_signed: 0x4e17b38d
+mac_size: 16
+mac: 0x903b5b194a799b03a37718820c2404f2
+original_id: 0xd6e2
diff --git a/src/lib/dns/tests/testdata/message_toWire4.spec b/src/lib/dns/tests/testdata/message_toWire4.spec
new file mode 100644
index 0000000..aab7e10
--- /dev/null
+++ b/src/lib/dns/tests/testdata/message_toWire4.spec
@@ -0,0 +1,27 @@
+#
+# Truncated DNS response with TSIG signed
+# This is expected to be a response to "fromWire17"
+#
+
+[custom]
+sections: header:question:tsig
+[header]
+id: 0x22c2
+rd: 1
+qr: 1
+aa: 1
+# It's "truncated":
+tc: 1
+arcount: 1
+[question]
+name: www.example.com
+rrtype: TXT
+[tsig]
+as_rr: True
+# TSIG QNAME won't be compressed
+rr_name: www.example.com
+algorithm: hmac-md5
+time_signed: 0x4e179212
+mac_size: 16
+mac: 0x88adc3811d1d6bec7c684438906fc694
+original_id: 0x22c2
diff --git a/src/lib/dns/tests/testdata/message_toWire5.spec b/src/lib/dns/tests/testdata/message_toWire5.spec
new file mode 100644
index 0000000..e97fb43
--- /dev/null
+++ b/src/lib/dns/tests/testdata/message_toWire5.spec
@@ -0,0 +1,36 @@
+#
+# A longest possible (without EDNS) DNS response with TSIG, i.e. totatl
+# length should be 512 bytes.
+#
+
+[custom]
+sections: header:question:txt/1:txt/2:tsig
+[header]
+id: 0xd6e2
+rd: 1
+qr: 1
+aa: 1
+ancount: 2
+arcount: 1
+[question]
+name: www.example.com
+rrtype: TXT
+[txt/1]
+as_rr: True
+# QNAME is fully compressed
+rr_name: ptr=12
+string: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde
+[txt/2]
+as_rr: True
+# QNAME is fully compressed
+rr_name: ptr=12
+string: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0
+[tsig]
+as_rr: True
+# TSIG QNAME won't be compressed
+rr_name: www.example.com
+algorithm: hmac-md5
+time_signed: 0x4e17b38d
+mac_size: 16
+mac: 0xbe2ba477373d2496891e2fda240ee4ec
+original_id: 0xd6e2
diff --git a/src/lib/dns/tests/tsig_unittest.cc b/src/lib/dns/tests/tsig_unittest.cc
index cbb1267..7944b29 100644
--- a/src/lib/dns/tests/tsig_unittest.cc
+++ b/src/lib/dns/tests/tsig_unittest.cc
@@ -927,4 +927,76 @@ TEST_F(TSIGTest, tooShortMAC) {
     }
 }
 
+TEST_F(TSIGTest, getTSIGLength) {
+    // Check for the most common case with various algorithms
+    // See the comment in TSIGContext::getTSIGLength() for calculation and
+    // parameter notation.
+    // The key name (www.example.com) is the same for most cases, where n1=17
+
+    // hmac-md5.sig-alg.reg.int.: n2=26, x=16
+    EXPECT_EQ(85, tsig_ctx->getTSIGLength());
+
+    // hmac-sha1: n2=11, x=20
+    tsig_ctx.reset(new TSIGContext(TSIGKey(test_name, TSIGKey::HMACSHA1_NAME(),
+                                           &dummy_data[0], 20)));
+    EXPECT_EQ(74, tsig_ctx->getTSIGLength());
+
+    // hmac-sha256: n2=13, x=32
+    tsig_ctx.reset(new TSIGContext(TSIGKey(test_name,
+                                           TSIGKey::HMACSHA256_NAME(),
+                                           &dummy_data[0], 32)));
+    EXPECT_EQ(88, tsig_ctx->getTSIGLength());
+
+    // hmac-sha224: n2=13, x=28
+    tsig_ctx.reset(new TSIGContext(TSIGKey(test_name,
+                                           TSIGKey::HMACSHA224_NAME(),
+                                           &dummy_data[0], 28)));
+    EXPECT_EQ(84, tsig_ctx->getTSIGLength());
+
+    // hmac-sha384: n2=13, x=48
+    tsig_ctx.reset(new TSIGContext(TSIGKey(test_name,
+                                           TSIGKey::HMACSHA384_NAME(),
+                                           &dummy_data[0], 48)));
+    EXPECT_EQ(104, tsig_ctx->getTSIGLength());
+
+    // hmac-sha512: n2=13, x=64
+    tsig_ctx.reset(new TSIGContext(TSIGKey(test_name,
+                                           TSIGKey::HMACSHA512_NAME(),
+                                           &dummy_data[0], 64)));
+    EXPECT_EQ(120, tsig_ctx->getTSIGLength());
+
+    // bad key case: n1=len(badkey.example.com)=20, n2=26, x=0
+    tsig_ctx.reset(new TSIGContext(badkey_name, TSIGKey::HMACMD5_NAME(),
+                                   keyring));
+    EXPECT_EQ(72, tsig_ctx->getTSIGLength());
+
+    // bad sig case: n1=17, n2=26, x=0
+    isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>;
+    createMessageFromFile("message_toWire2.wire");
+    tsig_ctx.reset(new TSIGContext(TSIGKey(test_name, TSIGKey::HMACMD5_NAME(),
+                                           &dummy_data[0],
+                                           dummy_data.size())));
+    {
+        SCOPED_TRACE("Verify resulting in BADSIG");
+        commonVerifyChecks(*tsig_ctx, message.getTSIGRecord(),
+                           &received_data[0], received_data.size(),
+                           TSIGError::BAD_SIG(), TSIGContext::RECEIVED_REQUEST);
+    }
+    EXPECT_EQ(69, tsig_ctx->getTSIGLength());
+
+    // bad time case: n1=17, n2=26, x=16, y=6
+    isc::util::detail::gettimeFunction = testGetTime<0x4da8877a - 1000>;
+    tsig_ctx.reset(new TSIGContext(TSIGKey(test_name, TSIGKey::HMACMD5_NAME(),
+                                           &dummy_data[0],
+                                           dummy_data.size())));
+    {
+        SCOPED_TRACE("Verify resulting in BADTIME");
+        commonVerifyChecks(*tsig_ctx, message.getTSIGRecord(),
+                           &received_data[0], received_data.size(),
+                           TSIGError::BAD_TIME(),
+                           TSIGContext::RECEIVED_REQUEST);
+    }
+    EXPECT_EQ(91, tsig_ctx->getTSIGLength());
+}
+
 } // end namespace
diff --git a/src/lib/dns/tsig.cc b/src/lib/dns/tsig.cc
index 714b2a5..1bda021 100644
--- a/src/lib/dns/tsig.cc
+++ b/src/lib/dns/tsig.cc
@@ -58,10 +58,32 @@ getTSIGTime() {
 }
 
 struct TSIGContext::TSIGContextImpl {
-    TSIGContextImpl(const TSIGKey& key) :
-        state_(INIT), key_(key), error_(Rcode::NOERROR()),
-        previous_timesigned_(0)
-    {}
+    TSIGContextImpl(const TSIGKey& key,
+                    TSIGError error = TSIGError::NOERROR()) :
+        state_(INIT), key_(key), error_(error),
+        previous_timesigned_(0), digest_len_(0)
+    {
+        if (error == TSIGError::NOERROR()) {
+            // In normal (NOERROR) case, the key should be valid, and we
+            // should be able to pre-create a corresponding HMAC object,
+            // which will be likely to be used for sign or verify later.
+            // We do this in the constructor so that we can know the expected
+            // digest length in advance.  The creation should normally succeed,
+            // but the key information could be still broken, which could
+            // trigger an exception inside the cryptolink module.  We ignore
+            // it at this moment; a subsequent sign/verify operation will try
+            // to create the HMAC, which would also fail.
+            try {
+                hmac_.reset(CryptoLink::getCryptoLink().createHMAC(
+                                key_.getSecret(), key_.getSecretLength(),
+                                key_.getAlgorithm()),
+                            deleteHMAC);
+            } catch (const Exception&) {
+                return;
+            }
+            digest_len_ = hmac_->getOutputLength();
+        }
+    }
 
     // This helper method is used from verify().  It's expected to be called
     // just before verify() returns.  It updates internal state based on
@@ -85,6 +107,23 @@ struct TSIGContext::TSIGContextImpl {
         return (error);
     }
 
+    // A shortcut method to create an HMAC object for sign/verify.  If one
+    // has been successfully created in the constructor, return it; otherwise
+    // create a new one and return it.  In the former case, the ownership is
+    // transferred to the caller; the stored HMAC will be reset after the
+    // call.
+    HMACPtr createHMAC() {
+        if (hmac_) {
+            HMACPtr ret = HMACPtr();
+            ret.swap(hmac_);
+            return (ret);
+        }
+        return (HMACPtr(CryptoLink::getCryptoLink().createHMAC(
+                            key_.getSecret(), key_.getSecretLength(),
+                            key_.getAlgorithm()),
+                        deleteHMAC));
+    }
+
     // The following three are helper methods to compute the digest for
     // TSIG sign/verify in order to unify the common code logic for sign()
     // and verify() and to keep these callers concise.
@@ -111,6 +150,8 @@ struct TSIGContext::TSIGContextImpl {
     vector<uint8_t> previous_digest_;
     TSIGError error_;
     uint64_t previous_timesigned_; // only meaningful for response with BADTIME
+    size_t digest_len_;
+    HMACPtr hmac_;
 };
 
 void
@@ -221,8 +262,7 @@ TSIGContext::TSIGContext(const Name& key_name, const Name& algorithm_name,
         // be used in subsequent response with a TSIG indicating a BADKEY
         // error.
         impl_ = new TSIGContextImpl(TSIGKey(key_name, algorithm_name,
-                                            NULL, 0));
-        impl_->error_ = TSIGError::BAD_KEY();
+                                            NULL, 0), TSIGError::BAD_KEY());
     } else {
         impl_ = new TSIGContextImpl(*result.key);
     }
@@ -232,6 +272,45 @@ TSIGContext::~TSIGContext() {
     delete impl_;
 }
 
+size_t
+TSIGContext::getTSIGLength() const {
+    //
+    // The space required for an TSIG record is:
+    //
+    //	n1 bytes for the (key) name
+    //	2 bytes for the type
+    //	2 bytes for the class
+    //	4 bytes for the ttl
+    //	2 bytes for the rdlength
+    //	n2 bytes for the algorithm name
+    //	6 bytes for the time signed
+    //	2 bytes for the fudge
+    //	2 bytes for the MAC size
+    //	x bytes for the MAC
+    //	2 bytes for the original id
+    //	2 bytes for the error
+    //	2 bytes for the other data length
+    //	y bytes for the other data (at most)
+    // ---------------------------------
+    //     26 + n1 + n2 + x + y bytes
+    //
+
+    // Normally the digest length ("x") is the length of the underlying
+    // hash output.  If a key related error occurred, however, the
+    // corresponding TSIG will be "unsigned", and the digest length will be 0.
+    const size_t digest_len =
+        (impl_->error_ == TSIGError::BAD_KEY() ||
+         impl_->error_ == TSIGError::BAD_SIG()) ? 0 : impl_->digest_len_;
+
+    // Other Len ("y") is normally 0; if BAD_TIME error occurred, the
+    // subsequent TSIG will contain 48 bits of the server current time.
+    const size_t other_len = (impl_->error_ == TSIGError::BAD_TIME()) ? 6 : 0;
+
+    return (26 + impl_->key_.getKeyName().getLength() +
+            impl_->key_.getAlgorithmName().getLength() +
+            digest_len + other_len);
+}
+
 TSIGContext::State
 TSIGContext::getState() const {
     return (impl_->state_);
@@ -276,11 +355,7 @@ TSIGContext::sign(const uint16_t qid, const void* const data,
         return (tsig);
     }
 
-    HMACPtr hmac(CryptoLink::getCryptoLink().createHMAC(
-                     impl_->key_.getSecret(),
-                     impl_->key_.getSecretLength(),
-                     impl_->key_.getAlgorithm()),
-                 deleteHMAC);
+    HMACPtr hmac(impl_->createHMAC());
 
     // If the context has previous MAC (either the Request MAC or its own
     // previous MAC), digest it.
@@ -406,11 +481,7 @@ TSIGContext::verify(const TSIGRecord* const record, const void* const data,
         return (impl_->postVerifyUpdate(error, NULL, 0));
     }
 
-    HMACPtr hmac(CryptoLink::getCryptoLink().createHMAC(
-                     impl_->key_.getSecret(),
-                     impl_->key_.getSecretLength(),
-                     impl_->key_.getAlgorithm()),
-                 deleteHMAC);
+    HMACPtr hmac(impl_->createHMAC());
 
     // If the context has previous MAC (either the Request MAC or its own
     // previous MAC), digest it.
diff --git a/src/lib/dns/tsig.h b/src/lib/dns/tsig.h
index bceec25..028d295 100644
--- a/src/lib/dns/tsig.h
+++ b/src/lib/dns/tsig.h
@@ -353,6 +353,27 @@ public:
     TSIGError verify(const TSIGRecord* const record, const void* const data,
                      const size_t data_len);
 
+    /// Return the expected length of TSIG RR after \c sign()
+    ///
+    /// This method returns the length of the TSIG RR that would be
+    /// produced as a result of \c sign() with the state of the context
+    /// at the time of the call.  The expected length can be decided
+    /// from the key and the algorithm (which determines the MAC size if
+    /// included) and the recorded TSIG error.  Specifically, if a key
+    /// related error has been identified, the MAC will be excluded; if
+    /// a time error has occurred, the TSIG will include "other data".
+    ///
+    /// This method is provided mainly for the convenience of the Message
+    /// class, which needs to know the expected TSIG length in rendering a
+    /// signed DNS message so that it can handle truncated messages with TSIG
+    /// correctly.  Normal applications wouldn't need this method.  The Python
+    /// binding for this method won't be provided for the same reason.
+    ///
+    /// \exception None
+    ///
+    /// \return The expected TISG RR length in bytes
+    size_t getTSIGLength() const;
+
     /// Return the current state of the context
     ///
     /// \note
diff --git a/src/lib/log/Makefile.am b/src/lib/log/Makefile.am
index 63b1dfb..9f52724 100644
--- a/src/lib/log/Makefile.am
+++ b/src/lib/log/Makefile.am
@@ -20,6 +20,7 @@ liblog_la_SOURCES += logger_manager_impl.cc logger_manager_impl.h
 liblog_la_SOURCES += logger_name.cc logger_name.h
 liblog_la_SOURCES += logger_specification.h
 liblog_la_SOURCES += logger_support.cc logger_support.h
+liblog_la_SOURCES += logger_unittest_support.cc logger_unittest_support.h
 liblog_la_SOURCES += macros.h
 liblog_la_SOURCES += log_messages.cc log_messages.h
 liblog_la_SOURCES += message_dictionary.cc message_dictionary.h
diff --git a/src/lib/log/logger_support.cc b/src/lib/log/logger_support.cc
index 2c5ab45..2097136 100644
--- a/src/lib/log/logger_support.cc
+++ b/src/lib/log/logger_support.cc
@@ -12,28 +12,9 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE
 
-/// \brief Temporary Logger Support
-///
-/// Performs run-time initialization of the logger system.  In particular, it
-/// is passed information from the command line and:
-///
-/// a) Sets the severity of the messages being logged (and debug level if
-/// appropriate).
-/// b) Reads in the local message file is one has been supplied.
-///
-/// These functions will be replaced once the code has been written to obtain
-/// the logging parameters from the configuration database.
-
-#include <iostream>
-#include <algorithm>
-#include <iostream>
 #include <string>
-
-#include <log/logger_level.h>
-#include <log/logger_manager.h>
-#include <log/logger_specification.h>
 #include <log/logger_support.h>
-#include <log/output_option.h>
+#include <log/logger_manager.h>
 
 using namespace std;
 
@@ -42,77 +23,6 @@ namespace {
 // Flag to hold logging initialization state.
 bool logging_init_state = false;
 
-
-// Set logging destination according to the setting of B10_LOGGER_DESTINATION.
-// (See header for initLogger() for more details.)  This is a no-op if the
-// environment variable is not defined.
-//
-// \param root Name of the root logger
-// \param severity Severity level to be assigned to the root logger
-// \param dbglevel Debug level
-
-void
-setDestination(const char* root, const isc::log::Severity severity,
-               const int dbglevel) {
-
-    using namespace isc::log;
-
-    const char* destination = getenv("B10_LOGGER_DESTINATION");
-    if (destination != NULL) {
-
-        // Constants: not declared static as this is function is expected to be
-        // called once only
-        const string STDOUT = "stdout";
-        const string STDERR = "stderr";
-        const string SYSLOG = "syslog";
-        const string SYSLOG_COLON = "syslog:";
-
-        // Prepare the objects to define the logging specification
-        LoggerSpecification spec(root, severity, dbglevel);
-        OutputOption option;
-
-        // Set up output option according to destination specification
-        const string dest = destination;
-        if (dest == STDOUT) {
-            option.destination = OutputOption::DEST_CONSOLE;
-            option.stream = OutputOption::STR_STDOUT;
-
-        } else if (dest == STDERR) {
-            option.destination = OutputOption::DEST_CONSOLE;
-            option.stream = OutputOption::STR_STDERR;
-
-        } else if (dest == SYSLOG) {
-            option.destination = OutputOption::DEST_SYSLOG;
-            // Use default specified in OutputOption constructor for the
-            // syslog destination
-
-        } else if (dest.find(SYSLOG_COLON) == 0) {
-            option.destination = OutputOption::DEST_SYSLOG;
-            // Must take account of the string actually being "syslog:"
-            if (dest == SYSLOG_COLON) {
-                cerr << "**ERROR** value for B10_LOGGER_DESTINATION of " <<
-                        SYSLOG_COLON << " is invalid, " << SYSLOG <<
-                        " will be used instead\n";
-                // Use default for logging facility
-
-            } else {
-                // Everything else in the string is the facility name
-                option.facility = dest.substr(SYSLOG_COLON.size());
-            }
-
-        } else {
-            // Not a recognised destination, assume a file
-            option.destination = OutputOption::DEST_FILE;
-            option.filename = dest;
-        }
-
-        // ... and set the destination
-        spec.addOutputOption(option);
-        LoggerManager manager;
-        manager.process(spec);
-    }
-}
-
 } // Anonymous namespace
 
 namespace isc {
@@ -140,63 +50,5 @@ initLogger(const string& root, isc::log::Severity severity, int dbglevel,
     LoggerManager::init(root, severity, dbglevel, file);
 }
 
-// Logger Run-Time Initialization via Environment Variables
-void initLogger(isc::log::Severity severity, int dbglevel) {
-
-    // Root logger name is defined by the environment variable B10_LOGGER_ROOT.
-    // If not present, the name is "bind10".
-    const char* DEFAULT_ROOT = "bind10";
-    const char* root = getenv("B10_LOGGER_ROOT");
-    if (! root) {
-        root = DEFAULT_ROOT;
-    }
-
-    // Set the logging severity.  The environment variable is
-    // B10_LOGGER_SEVERITY, and can be one of "DEBUG", "INFO", "WARN", "ERROR"
-    // of "FATAL".  Note that the string must be in upper case with no leading
-    // of trailing blanks.
-    const char* sev_char = getenv("B10_LOGGER_SEVERITY");
-    if (sev_char) {
-        severity = isc::log::getSeverity(sev_char);
-    }
-
-    // If the severity is debug, get the debug level (environment variable
-    // B10_LOGGER_DBGLEVEL), which should be in the range 0 to 99.
-    if (severity == isc::log::DEBUG) {
-        const char* dbg_char = getenv("B10_LOGGER_DBGLEVEL");
-        if (dbg_char) {
-            int level = 0;
-            try {
-                level = boost::lexical_cast<int>(dbg_char);
-                if (level < MIN_DEBUG_LEVEL) {
-                    cerr << "**ERROR** debug level of " << level
-                         << " is invalid - a value of " << MIN_DEBUG_LEVEL
-                         << " will be used\n";
-                    level = MIN_DEBUG_LEVEL;
-                } else if (level > MAX_DEBUG_LEVEL) {
-                    cerr << "**ERROR** debug level of " << level
-                         << " is invalid - a value of " << MAX_DEBUG_LEVEL
-                         << " will be used\n";
-                    level = MAX_DEBUG_LEVEL;
-                }
-            } catch (...) {
-                // Error, but not fatal to the test
-                cerr << "**ERROR** Unable to translate "
-                        "B10_LOGGER_DBGLEVEL - a value of 0 will be used\n";
-            }
-            dbglevel = level;
-        }
-    }
-
-    // Set the local message file
-    const char* localfile = getenv("B10_LOGGER_LOCALMSG");
-
-    // Initialize logging
-    initLogger(root, severity, dbglevel, localfile);
-
-    // Now set the destination for logging output
-    setDestination(root, severity, dbglevel);
-}
-
 } // namespace log
 } // namespace isc
diff --git a/src/lib/log/logger_support.h b/src/lib/log/logger_support.h
index cf83abc..4ce3ced 100644
--- a/src/lib/log/logger_support.h
+++ b/src/lib/log/logger_support.h
@@ -19,6 +19,13 @@
 
 #include <string>
 #include <log/logger.h>
+#include <log/logger_unittest_support.h>
+
+/// \file
+/// \brief Logging initialization functions
+///
+/// Contains a set of functions relating to logging initialization that are
+/// used by the production code.
 
 namespace isc {
 namespace log {
@@ -33,17 +40,13 @@ namespace log {
 /// \return true if logging has been initialized, false if not
 bool isLoggingInitialized();
 
-/// \brief Set "logging initialized" flag
-///
-/// Sets the state of the "logging initialized" flag.
+/// \brief Set state of "logging initialized" flag
 ///
 /// \param state State to set the flag to. (This is expected to be "true" - the
 ///        default - for all code apart from specific unit tests.)
 void setLoggingInitialized(bool state = true);
 
-
-
-/// \brief Run-Time Initialization
+/// \brief Run-time initialization
 ///
 /// Performs run-time initialization of the logger in particular supplying:
 ///
@@ -62,54 +65,7 @@ void initLogger(const std::string& root,
                 isc::log::Severity severity = isc::log::INFO,
                 int dbglevel = 0, const char* file = NULL);
 
-
-/// \brief Run-Time Initialization from Environment
-///
-/// Performs run-time initialization of the logger via the setting of
-/// environment variables.  These are:
-///
-/// - B10_LOGGER_ROOT\n
-/// Name of the root logger.  If not given, the string "bind10" will be used.
-///
-/// - B10_LOGGER_SEVERITY\n
-/// Severity of messages that will be logged.  This must be one of the strings
-/// "DEBUG", "INFO", "WARN", "ERROR", "FATAL" or "NONE". (Must be upper case
-/// and must not contain leading or trailing spaces.)  If not specified (or if
-/// specified but incorrect), the default passed as argument to this function
-/// (currently INFO) will be used.
-///
-/// - B10_LOGGER_DBGLEVEL\n
-/// Ignored if the level is not DEBUG, this should be a number between 0 and
-/// 99 indicating the logging severity.  The default is 0.  If outside these
-/// limits or if not a number, The value passed to this function (default
-/// of 0) is used.
-///
-/// - B10_LOGGER_LOCALMSG\n
-/// If defined, the path specification of a file that contains message
-/// definitions replacing ones in the default dictionary.
-///
-/// - B10_LOGGER_DESTINATION\n
-/// If defined, the destination of the logging output.  This can be one of:
-///   - \c stdout Send output to stdout.
-///   - \c stderr Send output to stderr
-///   - \c syslog Send output to syslog using the facility local0.
-///   - \c syslog:xxx  Send output to syslog, using the facility xxx. ("xxx"
-///     should be one of the syslog facilities such as "local0".)  There must
-///     be a colon between "syslog" and "xxx
-///   - \c other Anything else is interpreted as the name of a file to which
-///     output is appended.  If the file does not exist, it is created.
-///
-/// Any errors in the settings cause messages to be output to stderr.
-///
-/// This function is aimed at test programs, allowing the default settings to
-/// be overridden by the tester.  It is not intended for use in production
-/// code.
-
-void initLogger(isc::log::Severity severity = isc::log::INFO,
-                int dbglevel = 0);
-
 } // namespace log
 } // namespace isc
 
-
 #endif // __LOGGER_SUPPORT_H
diff --git a/src/lib/log/logger_unittest_support.cc b/src/lib/log/logger_unittest_support.cc
new file mode 100644
index 0000000..a0969be
--- /dev/null
+++ b/src/lib/log/logger_unittest_support.cc
@@ -0,0 +1,175 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <iostream>
+#include <algorithm>
+#include <string>
+
+#include <log/logger_level.h>
+#include <log/logger_name.h>
+#include <log/logger_manager.h>
+#include <log/logger_specification.h>
+#include <log/logger_unittest_support.h>
+#include <log/logger_support.h>
+#include <log/output_option.h>
+
+using namespace std;
+
+namespace isc {
+namespace log {
+
+// Get the logging severity.  This is defined by the environment variable
+// B10_LOGGER_SEVERITY, and can be one of "DEBUG", "INFO", "WARN", "ERROR"
+// of "FATAL".  (Note that the string must be in upper case with no leading
+// of trailing blanks.)  If not present, the default severity passed to the
+// function is returned.
+isc::log::Severity
+b10LoggerSeverity(isc::log::Severity defseverity) {
+    const char* sev_char = getenv("B10_LOGGER_SEVERITY");
+    if (sev_char) {
+        return (isc::log::getSeverity(sev_char));
+    }
+    return (defseverity);
+}
+
+// Get the debug level.  This is defined by the envornment variable
+// B10_LOGGER_DBGLEVEL.  If not defined, a default value passed to the function
+// is returned.
+int
+b10LoggerDbglevel(int defdbglevel) {
+    const char* dbg_char = getenv("B10_LOGGER_DBGLEVEL");
+    if (dbg_char) {
+        int level = 0;
+        try {
+            level = boost::lexical_cast<int>(dbg_char);
+            if (level < MIN_DEBUG_LEVEL) {
+                std::cerr << "**ERROR** debug level of " << level
+                          << " is invalid - a value of " << MIN_DEBUG_LEVEL
+                          << " will be used\n";
+                level = MIN_DEBUG_LEVEL;
+            } else if (level > MAX_DEBUG_LEVEL) {
+                std::cerr << "**ERROR** debug level of " << level
+                          << " is invalid - a value of " << MAX_DEBUG_LEVEL
+                          << " will be used\n";
+                level = MAX_DEBUG_LEVEL;
+            }
+        } catch (...) {
+            // Error, but not fatal to the test
+            std::cerr << "**ERROR** Unable to translate "
+                         "B10_LOGGER_DBGLEVEL - a value of 0 will be used\n";
+        }
+        return (level);
+    }
+
+    return (defdbglevel);
+}
+
+
+// Reset characteristics of the root logger to that set by the environment
+// variables B10_LOGGER_SEVERITY, B10_LOGGER_DBGLEVEL and B10_LOGGER_DESTINATION.
+
+void
+resetUnitTestRootLogger() {
+
+    using namespace isc::log;
+
+    // Constants: not declared static as this is function is expected to be
+    // called once only
+    const string DEVNULL = "/dev/null";
+    const string STDOUT = "stdout";
+    const string STDERR = "stderr";
+    const string SYSLOG = "syslog";
+    const string SYSLOG_COLON = "syslog:";
+
+    // Get the destination.  If not specified, assume /dev/null. (The default
+    // severity for unit tests is DEBUG, which generates a lot of output.
+    // Routing the logging to /dev/null will suppress that, whilst still
+    // ensuring that the code paths are tested.)
+    const char* destination = getenv("B10_LOGGER_DESTINATION");
+    const string dest((destination == NULL) ? DEVNULL : destination);
+
+    // Prepare the objects to define the logging specification
+    LoggerSpecification spec(getRootLoggerName(), 
+                             b10LoggerSeverity(isc::log::DEBUG),
+                             b10LoggerDbglevel(isc::log::MAX_DEBUG_LEVEL));
+    OutputOption option;
+
+    // Set up output option according to destination specification
+    if (dest == STDOUT) {
+        option.destination = OutputOption::DEST_CONSOLE;
+        option.stream = OutputOption::STR_STDOUT;
+
+    } else if (dest == STDERR) {
+        option.destination = OutputOption::DEST_CONSOLE;
+        option.stream = OutputOption::STR_STDERR;
+
+    } else if (dest == SYSLOG) {
+        option.destination = OutputOption::DEST_SYSLOG;
+        // Use default specified in OutputOption constructor for the
+        // syslog destination
+
+    } else if (dest.find(SYSLOG_COLON) == 0) {
+        option.destination = OutputOption::DEST_SYSLOG;
+        // Must take account of the string actually being "syslog:"
+        if (dest == SYSLOG_COLON) {
+            cerr << "**ERROR** value for B10_LOGGER_DESTINATION of " <<
+                    SYSLOG_COLON << " is invalid, " << SYSLOG <<
+                    " will be used instead\n";
+            // Use default for logging facility
+
+        } else {
+            // Everything else in the string is the facility name
+            option.facility = dest.substr(SYSLOG_COLON.size());
+        }
+
+    } else {
+        // Not a recognised destination, assume a file.
+        option.destination = OutputOption::DEST_FILE;
+        option.filename = dest;
+    }
+
+    // ... and set the destination
+    spec.addOutputOption(option);
+    LoggerManager manager;
+    manager.process(spec);
+}
+
+
+// Logger Run-Time Initialization via Environment Variables
+void initLogger(isc::log::Severity severity, int dbglevel) {
+
+    // Root logger name is defined by the environment variable B10_LOGGER_ROOT.
+    // If not present, the name is "bind10".
+    const char* DEFAULT_ROOT = "bind10";
+    const char* root = getenv("B10_LOGGER_ROOT");
+    if (! root) {
+        root = DEFAULT_ROOT;
+    }
+
+    // Set the local message file
+    const char* localfile = getenv("B10_LOGGER_LOCALMSG");
+
+    // Initialize logging
+    initLogger(root, isc::log::DEBUG, isc::log::MAX_DEBUG_LEVEL, localfile);
+
+    // Now set reset the output destination of the root logger, overriding
+    // the default severity, debug level and destination with those specified
+    // in the environment variables.  (The two-step approach is used as the
+    // setUnitTestRootLoggerCharacteristics() function is used in several
+    // places in the BIND 10 tests, and it avoid duplicating code.)
+    resetUnitTestRootLogger();
+} 
+
+} // namespace log
+} // namespace isc
diff --git a/src/lib/log/logger_unittest_support.h b/src/lib/log/logger_unittest_support.h
new file mode 100644
index 0000000..ce9121b
--- /dev/null
+++ b/src/lib/log/logger_unittest_support.h
@@ -0,0 +1,126 @@
+// 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 __LOGGER_UNITTEST_SUPPORT_H
+#define __LOGGER_UNITTEST_SUPPORT_H
+
+#include <string>
+#include <log/logger.h>
+
+/// \file
+/// \brief Miscellaneous logging functions used by the unit tests.
+///
+/// As the configuration database is unsually unavailable during unit tests,
+/// the functions defined here allow a limited amount of logging configuration
+/// through the use of environment variables
+
+namespace isc {
+namespace log {
+
+/// \brief Run-Time Initialization for Unit Tests from Environment
+///
+/// Performs run-time initialization of the logger via the setting of
+/// environment variables.  These are:
+///
+/// - B10_LOGGER_ROOT\n
+/// Name of the root logger.  If not given, the string "bind10" will be used.
+///
+/// - B10_LOGGER_SEVERITY\n
+/// Severity of messages that will be logged.  This must be one of the strings
+/// "DEBUG", "INFO", "WARN", "ERROR", "FATAL" or "NONE". (Must be upper case
+/// and must not contain leading or trailing spaces.)  If not specified (or if
+/// specified but incorrect), the default passed as argument to this function
+/// (currently DEBUG) will be used.
+///
+/// - B10_LOGGER_DBGLEVEL\n
+/// Ignored if the level is not DEBUG, this should be a number between 0 and
+/// 99 indicating the logging severity.  The default is 0.  If outside these
+/// limits or if not a number, The value passed to this function (default
+/// of MAX_DEBUG_LEVEL) is used.
+///
+/// - B10_LOGGER_LOCALMSG\n
+/// If defined, the path specification of a file that contains message
+/// definitions replacing ones in the default dictionary.
+///
+/// - B10_LOGGER_DESTINATION\n
+/// If defined, the destination of the logging output.  This can be one of:
+///   - \c stdout Send output to stdout.
+///   - \c stderr Send output to stderr
+///   - \c syslog Send output to syslog using the facility local0.
+///   - \c syslog:xxx  Send output to syslog, using the facility xxx. ("xxx"
+///     should be one of the syslog facilities such as "local0".)  There must
+///     be a colon between "syslog" and "xxx
+///   - \c other Anything else is interpreted as the name of a file to which
+///     output is appended.  If the file does not exist, it is created.
+///
+/// Any errors in the settings cause messages to be output to stderr.
+///
+/// This function is aimed at test programs, allowing the default settings to
+/// be overridden by the tester.  It is not intended for use in production
+/// code.
+///
+/// TODO: Rename. This function overloads the initLogger() function that can
+///       be used to initialize production programs.  This may lead to confusion.
+void initLogger(isc::log::Severity severity = isc::log::DEBUG,
+                int dbglevel = isc::log::MAX_DEBUG_LEVEL);
+
+
+/// \brief Obtains logging severity from B10_LOGGER_SEVERITY
+///
+/// Support function called by the unit test logging initialization code.
+/// It returns the logging severity defined by B10_LOGGER_SEVERITY.  If
+/// not defined it returns the default passed to it.
+///
+/// \param defseverity Default severity used if B10_LOGGER_SEVERITY is not
+//         defined.
+///
+/// \return Severity to use for the logging.
+isc::log::Severity b10LoggerSeverity(isc::log::Severity defseverity);
+
+
+/// \brief Obtains logging debug level from B10_LOGGER_DBGLEVEL
+///
+/// Support function called by the unit test logging initialization code.
+/// It returns the logging debug level defined by B10_LOGGER_DBGLEVEL.  If
+/// not defined, it returns the default passed to it.
+///
+/// N.B. If there is an error, a message is written to stderr and a value
+/// related to the error is used. (This is because (a) logging is not yet
+/// initialized, hence only the error stream is known to exist, and (b) this
+/// function is only used in unit test logging initialization, so incorrect
+/// selection of a level is not really an issue.)
+///
+/// \param defdbglevel Default debug level to be used if B10_LOGGER_DBGLEVEL
+///        is not defined.
+///
+/// \return Debug level to use.
+int b10LoggerDbglevel(int defdbglevel);
+
+
+/// \brief Reset root logger characteristics
+///
+/// This is a simplified interface into the resetting of the characteristics
+/// of the root logger.  It is aimed for use in unit tests and resets the
+/// characteristics of the root logger to use a severity, debug level and
+/// destination set by the environment variables B10_LOGGER_SEVERITY,
+/// B10_LOGGER_DBGLEVEL and B10_LOGGER_DESTINATION.
+void
+resetUnitTestRootLogger();
+
+} // namespace log
+} // namespace isc
+
+
+
+#endif // __LOGGER_UNITTEST_SUPPORT_H
diff --git a/src/lib/log/tests/init_logger_test.sh.in b/src/lib/log/tests/init_logger_test.sh.in
index d26ca5d..795419b 100755
--- a/src/lib/log/tests/init_logger_test.sh.in
+++ b/src/lib/log/tests/init_logger_test.sh.in
@@ -44,7 +44,7 @@ WARN  [bind10.log] LOG_BAD_STREAM bad log console output stream: warn
 ERROR [bind10.log] LOG_DUPLICATE_MESSAGE_ID duplicate message ID (error) in compiled code
 FATAL [bind10.log] LOG_NO_MESSAGE_ID line fatal: message definition line found without a message ID
 .
-B10_LOGGER_SEVERITY=DEBUG B10_LOGGER_DBGLEVEL=99 ./init_logger_test 2>&1 | \
+B10_LOGGER_DESTINATION=stdout B10_LOGGER_SEVERITY=DEBUG B10_LOGGER_DBGLEVEL=99 ./init_logger_test | \
     cut -d' ' -f3- | diff $tempfile -
 passfail $?
 
@@ -57,7 +57,7 @@ WARN  [bind10.log] LOG_BAD_STREAM bad log console output stream: warn
 ERROR [bind10.log] LOG_DUPLICATE_MESSAGE_ID duplicate message ID (error) in compiled code
 FATAL [bind10.log] LOG_NO_MESSAGE_ID line fatal: message definition line found without a message ID
 .
-B10_LOGGER_SEVERITY=DEBUG B10_LOGGER_DBGLEVEL=50 ./init_logger_test 2>&1 | \
+B10_LOGGER_DESTINATION=stdout B10_LOGGER_SEVERITY=DEBUG B10_LOGGER_DBGLEVEL=50 ./init_logger_test | \
     cut -d' ' -f3- | diff $tempfile -
 passfail $?
 
@@ -67,7 +67,7 @@ WARN  [bind10.log] LOG_BAD_STREAM bad log console output stream: warn
 ERROR [bind10.log] LOG_DUPLICATE_MESSAGE_ID duplicate message ID (error) in compiled code
 FATAL [bind10.log] LOG_NO_MESSAGE_ID line fatal: message definition line found without a message ID
 .
-B10_LOGGER_SEVERITY=WARN ./init_logger_test 2>&1 | \
+B10_LOGGER_DESTINATION=stdout B10_LOGGER_SEVERITY=WARN ./init_logger_test | \
     cut -d' ' -f3- | diff $tempfile -
 passfail $?
 
diff --git a/src/lib/log/tests/logger_level_impl_unittest.cc b/src/lib/log/tests/logger_level_impl_unittest.cc
index 0ded7f9..dacd202 100644
--- a/src/lib/log/tests/logger_level_impl_unittest.cc
+++ b/src/lib/log/tests/logger_level_impl_unittest.cc
@@ -20,6 +20,7 @@
 #include <boost/lexical_cast.hpp>
 
 #include <log/logger_level_impl.h>
+#include <log/logger_support.h>
 #include <log4cplus/logger.h>
 
 using namespace isc::log;
@@ -27,8 +28,10 @@ using namespace std;
 
 class LoggerLevelImplTest : public ::testing::Test {
 protected:
-    LoggerLevelImplTest()
-    {}
+    LoggerLevelImplTest() {
+        // Ensure logging set to default for unit tests
+        resetUnitTestRootLogger();
+    }
 
     ~LoggerLevelImplTest()
     {}
diff --git a/src/lib/log/tests/logger_level_unittest.cc b/src/lib/log/tests/logger_level_unittest.cc
index 8c98091..641a6cc 100644
--- a/src/lib/log/tests/logger_level_unittest.cc
+++ b/src/lib/log/tests/logger_level_unittest.cc
@@ -20,7 +20,7 @@
 #include <log/logger.h>
 #include <log/logger_manager.h>
 #include <log/log_messages.h>
-#include <log/logger_name.h>
+#include <log/logger_support.h>
 
 using namespace isc;
 using namespace isc::log;
@@ -29,7 +29,9 @@ using namespace std;
 class LoggerLevelTest : public ::testing::Test {
 protected:
     LoggerLevelTest() {
-        // Logger initialization is done in main()
+        // Logger initialization is done in main().  As logging tests may
+        // alter the default logging output, it is reset here.
+        resetUnitTestRootLogger();
     }
     ~LoggerLevelTest() {
         LoggerManager::reset();
@@ -57,7 +59,7 @@ TEST_F(LoggerLevelTest, Creation) {
     EXPECT_EQ(42, level3.dbglevel);
 }
 
-TEST(LoggerLevel, getSeverity) {
+TEST_F(LoggerLevelTest, getSeverity) {
     EXPECT_EQ(DEBUG, getSeverity("DEBUG"));
     EXPECT_EQ(DEBUG, getSeverity("debug"));
     EXPECT_EQ(DEBUG, getSeverity("DeBuG"));
diff --git a/src/lib/log/tests/logger_support_unittest.cc b/src/lib/log/tests/logger_support_unittest.cc
index 6a93652..b418906 100644
--- a/src/lib/log/tests/logger_support_unittest.cc
+++ b/src/lib/log/tests/logger_support_unittest.cc
@@ -18,12 +18,23 @@
 
 using namespace isc::log;
 
+class LoggerSupportTest : public ::testing::Test {
+protected:
+    LoggerSupportTest() {
+        // Logger initialization is done in main().  As logging tests may
+        // alter the default logging output, it is reset here.
+        resetUnitTestRootLogger();
+    }
+    ~LoggerSupportTest() {
+    }
+};
+
 // Check that the initialized flag can be manipulated.  This is a bit chicken-
 // -and-egg: we want to reset to the flag to the original value at the end
 // of the test, so use the functions to do that.  But we are trying to check
 // that these functions in fact work.
 
-TEST(LoggerSupportTest, InitializedFlag) {
+TEST_F(LoggerSupportTest, InitializedFlag) {
     bool current_flag = isLoggingInitialized();
 
     // check we can flip the flag.
@@ -51,7 +62,7 @@ TEST(LoggerSupportTest, InitializedFlag) {
 // Check that a logger will throw an exception if logging has not been
 // initialized.
 
-TEST(LoggerSupportTest, LoggingInitializationCheck) {
+TEST_F(LoggerSupportTest, LoggingInitializationCheck) {
 
     // Assert that logging has been initialized (it should be in main()).
     bool current_flag = isLoggingInitialized();
diff --git a/src/lib/python/isc/Makefile.am b/src/lib/python/isc/Makefile.am
index bfc5a91..b391c1e 100644
--- a/src/lib/python/isc/Makefile.am
+++ b/src/lib/python/isc/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = datasrc cc config log net notify util testutils
+SUBDIRS = datasrc cc config log net notify util testutils acl
 
 python_PYTHON = __init__.py
 
diff --git a/src/lib/python/isc/acl/Makefile.am b/src/lib/python/isc/acl/Makefile.am
new file mode 100644
index 0000000..cabc0a3
--- /dev/null
+++ b/src/lib/python/isc/acl/Makefile.am
@@ -0,0 +1,45 @@
+SUBDIRS = . tests
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+python_PYTHON = __init__.py
+pythondir = $(PYTHON_SITEPKG_DIR)/isc/acl
+
+pyexec_LTLIBRARIES = acl.la dns.la
+pyexecdir = $(PYTHON_SITEPKG_DIR)/isc/acl
+
+acl_la_SOURCES = acl.cc
+acl_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES)
+acl_la_LDFLAGS = $(PYTHON_LDFLAGS)
+acl_la_CXXFLAGS = $(AM_CXXFLAGS) $(PYTHON_CXXFLAGS)
+
+dns_la_SOURCES = dns.h dns.cc dns_requestacl_python.h dns_requestacl_python.cc
+dns_la_SOURCES += dns_requestcontext_python.h dns_requestcontext_python.cc
+dns_la_SOURCES += dns_requestloader_python.h dns_requestloader_python.cc
+dns_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES)
+dns_la_LDFLAGS = $(PYTHON_LDFLAGS)
+# Note: PYTHON_CXXFLAGS may have some -Wno... workaround, which must be
+# placed after -Wextra defined in AM_CXXFLAGS
+dns_la_CXXFLAGS = $(AM_CXXFLAGS) $(PYTHON_CXXFLAGS)
+
+# Python prefers .so, while some OSes (specifically MacOS) use a different
+# suffix for dynamic objects.  -module is necessary to work this around.
+acl_la_LDFLAGS += -module
+acl_la_LIBADD = $(top_builddir)/src/lib/acl/libacl.la
+acl_la_LIBADD += $(PYTHON_LIB)
+
+dns_la_LDFLAGS += -module
+dns_la_LIBADD = $(top_builddir)/src/lib/acl/libdnsacl.la
+dns_la_LIBADD += $(PYTHON_LIB)
+
+EXTRA_DIST = acl.py dns.py
+EXTRA_DIST += acl_inc.cc
+EXTRA_DIST += dnsacl_inc.cc dns_requestacl_inc.cc dns_requestcontext_inc.cc
+EXTRA_DIST += dns_requestloader_inc.cc
+
+CLEANDIRS = __pycache__
+
+clean-local:
+	rm -rf $(CLEANDIRS)
diff --git a/src/lib/python/isc/acl/__init__.py b/src/lib/python/isc/acl/__init__.py
new file mode 100644
index 0000000..d9b2838
--- /dev/null
+++ b/src/lib/python/isc/acl/__init__.py
@@ -0,0 +1,11 @@
+"""
+Here are function and classes for manipulating access control lists.
+"""
+
+# The DNS ACL loader would need the json module.  Make sure it's imported
+# beforehand.
+import json
+
+# Other ACL modules highly depends on the main acl sub module, so it's
+# explicitly imported here.
+import isc.acl.acl
diff --git a/src/lib/python/isc/acl/acl.cc b/src/lib/python/isc/acl/acl.cc
new file mode 100644
index 0000000..6517a12
--- /dev/null
+++ b/src/lib/python/isc/acl/acl.cc
@@ -0,0 +1,80 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <Python.h>
+
+#include <util/python/pycppwrapper_util.h>
+
+#include <acl/acl.h>
+
+using namespace isc::util::python;
+
+#include "acl_inc.cc"
+
+namespace {
+// Commonly used Python exception objects.  Right now the acl module consists
+// of only one .cc file, so we hide them in an unnamed namespace.  If and when
+// we extend this module with multiple .cc files, we should move them to
+// a named namespace, say isc::acl::python, and declare them in a separate
+// header file.
+PyObject* po_ACLError;
+PyObject* po_LoaderError;
+}
+
+namespace {
+PyModuleDef acl = {
+    { PyObject_HEAD_INIT(NULL) NULL, 0, NULL},
+    "isc.acl.acl",
+    acl_doc,
+    -1,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL
+};
+} // end of unnamed namespace
+
+PyMODINIT_FUNC
+PyInit_acl(void) {
+    PyObject* mod = PyModule_Create(&acl);
+    if (mod == NULL) {
+        return (NULL);
+    }
+
+    try {
+        po_ACLError = PyErr_NewException("isc.acl.Error", NULL, NULL);
+        PyObjectContainer(po_ACLError).installToModule(mod, "Error");
+
+        po_LoaderError = PyErr_NewException("isc.acl.LoaderError", NULL, NULL);
+        PyObjectContainer(po_LoaderError).installToModule(mod, "LoaderError");
+
+        // Install module constants.  Note that we can let Py_BuildValue
+        // "steal" the references to these object (by specifying false to
+        // installToModule), because, unlike the exception cases above,
+        // we don't have corresponding C++ variables (see the note in
+        // pycppwrapper_util for more details).
+        PyObjectContainer(Py_BuildValue("I", isc::acl::ACCEPT)).
+            installToModule(mod, "ACCEPT", false);
+        PyObjectContainer(Py_BuildValue("I", isc::acl::REJECT)).
+            installToModule(mod, "REJECT", false);
+        PyObjectContainer(Py_BuildValue("I", isc::acl::DROP)).
+            installToModule(mod, "DROP", false);
+    } catch (...) {
+        Py_DECREF(mod);
+        return (NULL);
+    }
+
+    return (mod);
+}
diff --git a/src/lib/python/isc/acl/acl.py b/src/lib/python/isc/acl/acl.py
new file mode 100644
index 0000000..804d78b
--- /dev/null
+++ b/src/lib/python/isc/acl/acl.py
@@ -0,0 +1,29 @@
+# Copyright (C) 2011  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+# This file is not installed; The .so version will be installed into the right
+# place at installation time.
+# This helper script is only to find it in the .libs directory when we run
+# as a test or from the build directory.
+
+import os
+import sys
+
+for base in sys.path[:]:
+    bindingdir = os.path.join(base, 'isc/acl/.libs')
+    if os.path.exists(bindingdir):
+        sys.path.insert(0, bindingdir)
+
+from acl import *
diff --git a/src/lib/python/isc/acl/acl_inc.cc b/src/lib/python/isc/acl/acl_inc.cc
new file mode 100644
index 0000000..a9f7c9d
--- /dev/null
+++ b/src/lib/python/isc/acl/acl_inc.cc
@@ -0,0 +1,16 @@
+namespace {
+const char* const acl_doc = "\
+Implementation module for ACL operations\n\n\
+This module provides Python bindings for the C++ classes in the\n\
+isc::acl namespace.\n\
+\n\
+Integer constants:\n\
+\n\
+ACCEPT, REJECT, DROP -- Default actions an ACL could perform.\n\
+  These are the commonly used actions in specific ACLs.\n\
+  It is possible to specify any other values, as the ACL class does\n\
+  nothing about them, but these look reasonable, so they are provided\n\
+  for convenience. It is not specified what exactly these mean and it's\n\
+  up to whoever uses them.\n\
+";
+} // unnamed namespace
diff --git a/src/lib/python/isc/acl/dns.cc b/src/lib/python/isc/acl/dns.cc
new file mode 100644
index 0000000..351a8b3
--- /dev/null
+++ b/src/lib/python/isc/acl/dns.cc
@@ -0,0 +1,135 @@
+// 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 <Python.h>
+
+#include <stdexcept>
+#include <boost/shared_ptr.hpp>
+
+#include <util/python/pycppwrapper_util.h>
+
+#include <cc/data.h>
+
+#include <acl/acl.h>
+#include <acl/dns.h>
+
+#include "dns.h"
+#include "dns_requestcontext_python.h"
+#include "dns_requestacl_python.h"
+#include "dns_requestloader_python.h"
+
+using namespace std;
+using boost::shared_ptr;
+using namespace isc::util::python;
+using namespace isc::data;
+using namespace isc::acl::dns;
+using namespace isc::acl::dns::python;
+
+#include "dnsacl_inc.cc"
+
+namespace {
+// This is a Python binding object corresponding to the singleton loader used
+// in the C++ version of the library.
+// We can define it as a pure object rather than through an accessor function,
+// because in Python we can ensure it has been created and initialized
+// in the module initializer by the time it's actually used.
+s_RequestLoader* po_REQUEST_LOADER;
+
+PyMethodDef methods[] = {
+    { NULL, NULL, 0, NULL }
+};
+
+PyModuleDef dnsacl = {
+    { PyObject_HEAD_INIT(NULL) NULL, 0, NULL},
+    "isc.acl.dns",
+    dnsacl_doc,
+    -1,
+    methods,
+    NULL,
+    NULL,
+    NULL,
+    NULL
+};
+} // end of unnamed namespace
+
+namespace isc {
+namespace acl {
+namespace dns {
+namespace python {
+PyObject*
+getACLException(const char* ex_name) {
+    PyObject* ex_obj = NULL;
+
+    PyObject* acl_module = PyImport_AddModule("isc.acl.acl");
+    if (acl_module != NULL) {
+        PyObject* acl_dict = PyModule_GetDict(acl_module);
+        if (acl_dict != NULL) {
+            ex_obj = PyDict_GetItemString(acl_dict, ex_name);
+        }
+    }
+
+    if (ex_obj == NULL) {
+        ex_obj = PyExc_RuntimeError;
+    }
+    return (ex_obj);
+}
+}
+}
+}
+}
+
+PyMODINIT_FUNC
+PyInit_dns(void) {
+    PyObject* mod = PyModule_Create(&dnsacl);
+    if (mod == NULL) {
+        return (NULL);
+    }
+
+    if (!initModulePart_RequestContext(mod)) {
+        Py_DECREF(mod);
+        return (NULL);
+    }
+    if (!initModulePart_RequestACL(mod)) {
+        Py_DECREF(mod);
+        return (NULL);
+    }
+    if (!initModulePart_RequestLoader(mod)) {
+        Py_DECREF(mod);
+        return (NULL);
+    }
+
+    // Module constants
+    try {
+        if (po_REQUEST_LOADER == NULL) {
+            po_REQUEST_LOADER = static_cast<s_RequestLoader*>(
+                requestloader_type.tp_alloc(&requestloader_type, 0));
+        }
+        if (po_REQUEST_LOADER != NULL) {
+            // We gain and keep our own reference to the singleton object
+            // for the same reason as that for exception objects (see comments
+            // in pycppwrapper_util for more details).  Note also that we don't
+            // bother to release the reference even if exception is thrown
+            // below (in fact, we cannot delete the singleton loader).
+            po_REQUEST_LOADER->cppobj = &getRequestLoader();
+            Py_INCREF(po_REQUEST_LOADER);
+        }
+        PyObjectContainer(po_REQUEST_LOADER).installToModule(mod,
+                                                             "REQUEST_LOADER");
+    } catch (...) {
+        Py_DECREF(mod);
+        return (NULL);
+    }
+
+    return (mod);
+}
diff --git a/src/lib/python/isc/acl/dns.h b/src/lib/python/isc/acl/dns.h
new file mode 100644
index 0000000..76849c5
--- /dev/null
+++ b/src/lib/python/isc/acl/dns.h
@@ -0,0 +1,52 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __PYTHON_ACL_DNS_H
+#define __PYTHON_ACL_DNS_H 1
+
+#include <Python.h>
+
+namespace isc {
+namespace acl {
+namespace dns {
+namespace python {
+
+// Return a Python exception object of the given name (ex_name) defined in
+// the isc.acl.acl loadable module.
+//
+// Since the acl module is a different binary image and is loaded separately
+// from the dns module, it would be very tricky to directly access to
+// C/C++ symbols defined in that module.  So we get access to these object
+// using the Python interpretor through this wrapper function.
+//
+// The __init__.py file should ensure isc.acl.acl has been loaded by the time
+// whenever this function is called, and there shouldn't be any operation
+// within this function that can fail (such as dynamic memory allocation),
+// so this function should always succeed.  Yet there may be an overlooked
+// failure mode, perhaps due to a bug in the binding implementation, or
+// due to invalid usage.  As a last resort for such cases, this function
+// returns PyExc_RuntimeError (a C binding of Python's RuntimeError) should
+// it encounters an unexpected failure.
+extern PyObject* getACLException(const char* ex_name);
+
+} // namespace python
+} // namespace dns
+} // namespace acl
+} // namespace isc
+
+#endif // __PYTHON_ACL_DNS_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/python/isc/acl/dns.py b/src/lib/python/isc/acl/dns.py
new file mode 100644
index 0000000..8070559
--- /dev/null
+++ b/src/lib/python/isc/acl/dns.py
@@ -0,0 +1,33 @@
+# Copyright (C) 2011  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+# This file is not installed. The log.so is installed into the right place.
+# It is only to find it in the .libs directory when we run as a test or
+# from the build directory.
+# But as nobody gives us the builddir explicitly (and we can't use generation
+# from .in file, as it would put us into the builddir and we wouldn't be found)
+# we guess from current directory. Any idea for something better? This should
+# be enough for the tests, but would it work for B10_FROM_SOURCE as well?
+# Should we look there? Or define something in bind10_config?
+
+import os
+import sys
+
+for base in sys.path[:]:
+    bindingdir = os.path.join(base, 'isc/acl/.libs')
+    if os.path.exists(bindingdir):
+        sys.path.insert(0, bindingdir)
+
+from dns import *
diff --git a/src/lib/python/isc/acl/dns_requestacl_inc.cc b/src/lib/python/isc/acl/dns_requestacl_inc.cc
new file mode 100644
index 0000000..673fa23
--- /dev/null
+++ b/src/lib/python/isc/acl/dns_requestacl_inc.cc
@@ -0,0 +1,33 @@
+namespace {
+const char* const RequestACL_doc = "\
+The DNS Request ACL.\n\
+\n\
+It holds bunch of ordered entries, each one consisting of a check for\n\
+a given DNS Request context and an action, which is one of ACCEPT,\n\
+REJECT, or DROP, as defined in the isc.acl.acl module.\n\
+The checks are tested in the order and first match counts.\n\
+\n\
+A RequestACL object cannot be constructed directly; an application\n\
+must use isc.acl.dns.load_request_acl() to create a RequestACL object.\n\
+\n\
+";
+
+const char* const RequestACL_execute_doc = "\
+execute(context) -> action \n\
+\n\
+The returned action is one of ACCEPT, REJECT or DROP as defined in\n\
+the isc.acl.acl module.\n\
+\n\
+This is the function that takes the ACL entries one by one, checks the\n\
+context against conditions and if it matches, returns the action that\n\
+belongs to the first matched entry or default action if nothing\n\
+matches.\n\
+\n\
+Parameters:\n\
+  context    The thing that should be checked. It is directly passed\n\
+             to the checks.\n\
+\n\
+Return Value(s): The action for the ACL entry that first matches the\n\
+context.\n\
+";
+} // unnamed namespace
diff --git a/src/lib/python/isc/acl/dns_requestacl_python.cc b/src/lib/python/isc/acl/dns_requestacl_python.cc
new file mode 100644
index 0000000..5e5acea
--- /dev/null
+++ b/src/lib/python/isc/acl/dns_requestacl_python.cc
@@ -0,0 +1,184 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// Enable this if you use s# variants with PyArg_ParseTuple(), see
+// http://docs.python.org/py3k/c-api/arg.html#strings-and-buffers
+//#define PY_SSIZE_T_CLEAN
+
+// Python.h needs to be placed at the head of the program file, see:
+// http://docs.python.org/py3k/extending/extending.html#a-simple-example
+#include <Python.h>
+
+#include <string>
+#include <stdexcept>
+
+#include <util/python/pycppwrapper_util.h>
+
+#include <acl/acl.h>
+#include <acl/dns.h>
+
+#include "dns.h"
+#include "dns_requestacl_python.h"
+#include "dns_requestcontext_python.h"
+
+using namespace std;
+using namespace isc::util::python;
+using namespace isc::acl;
+using namespace isc::acl::dns;
+using namespace isc::acl::dns::python;
+
+//
+// Definition of the classes
+//
+
+// For each class, we need a struct, a helper functions (init, destroy,
+// and static wrappers around the methods we export), a list of methods,
+// and a type description
+
+//
+// RequestACL
+//
+
+// Trivial constructor.
+s_RequestACL::s_RequestACL() {}
+
+// Import pydoc text
+#include "dns_requestacl_inc.cc"
+
+namespace {
+int
+RequestACL_init(PyObject*, PyObject*, PyObject*) {
+    PyErr_SetString(getACLException("Error"),
+                    "RequestACL cannot be directly constructed");
+    return (-1);
+}
+
+void
+RequestACL_destroy(PyObject* po_self) {
+    s_RequestACL* const self = static_cast<s_RequestACL*>(po_self);
+    self->cppobj.reset();
+    Py_TYPE(self)->tp_free(self);
+}
+
+PyObject*
+RequestACL_execute(PyObject* po_self, PyObject* args) {
+    s_RequestACL* const self = static_cast<s_RequestACL*>(po_self);
+
+    try {
+        const s_RequestContext* po_context;
+        if (PyArg_ParseTuple(args, "O!", &requestcontext_type, &po_context)) {
+            const BasicAction action =
+                self->cppobj->execute(*po_context->cppobj);
+            return (Py_BuildValue("I", action));
+        }
+    } catch (const exception& ex) {
+        const string ex_what = "Failed to execute ACL: " + string(ex.what());
+        PyErr_SetString(getACLException("Error"), ex_what.c_str());
+    } catch (...) {
+        PyErr_SetString(PyExc_RuntimeError,
+                        "Unexpected exception in executing ACL");
+    }
+
+    return (NULL);
+}
+
+// This list contains the actual set of functions we have in
+// python. Each entry has
+// 1. Python method name
+// 2. Our static function here
+// 3. Argument type
+// 4. Documentation
+PyMethodDef RequestACL_methods[] = {
+    { "execute", RequestACL_execute, METH_VARARGS, RequestACL_execute_doc },
+    { NULL, NULL, 0, NULL }
+};
+} // end of unnamed namespace
+
+namespace isc {
+namespace acl {
+namespace dns {
+namespace python {
+// This defines the complete type for reflection in python and
+// parsing of PyObject* to s_RequestACL
+// Most of the functions are not actually implemented and NULL here.
+PyTypeObject requestacl_type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "isc.acl.dns.RequestACL",
+    sizeof(s_RequestACL),                 // tp_basicsize
+    0,                                  // tp_itemsize
+    RequestACL_destroy,                // tp_dealloc
+    NULL,                               // tp_print
+    NULL,                               // tp_getattr
+    NULL,                               // tp_setattr
+    NULL,                               // tp_reserved
+    NULL,                               // tp_repr
+    NULL,                               // tp_as_number
+    NULL,                               // tp_as_sequence
+    NULL,                               // tp_as_mapping
+    NULL,                               // tp_hash
+    NULL,                               // tp_call
+    NULL,	                       // tp_str
+    NULL,                               // tp_getattro
+    NULL,                               // tp_setattro
+    NULL,                               // tp_as_buffer
+    Py_TPFLAGS_DEFAULT,                 // tp_flags
+    RequestACL_doc,
+    NULL,                               // tp_traverse
+    NULL,                               // tp_clear
+    NULL,				 // tp_richcompare
+    0,                                  // tp_weaklistoffset
+    NULL,                               // tp_iter
+    NULL,                               // tp_iternext
+    RequestACL_methods,                   // tp_methods
+    NULL,                               // tp_members
+    NULL,                               // tp_getset
+    NULL,                               // tp_base
+    NULL,                               // tp_dict
+    NULL,                               // tp_descr_get
+    NULL,                               // tp_descr_set
+    0,                                  // tp_dictoffset
+    RequestACL_init,                    // tp_init
+    NULL,                               // tp_alloc
+    PyType_GenericNew,                  // tp_new
+    NULL,                               // tp_free
+    NULL,                               // tp_is_gc
+    NULL,                               // tp_bases
+    NULL,                               // tp_mro
+    NULL,                               // tp_cache
+    NULL,                               // tp_subclasses
+    NULL,                               // tp_weaklist
+    NULL,                               // tp_del
+    0                                   // tp_version_tag
+};
+
+bool
+initModulePart_RequestACL(PyObject* mod) {
+    // We initialize the static description object with PyType_Ready(),
+    // then add it to the module. This is not just a check! (leaving
+    // this out results in segmentation faults)
+    if (PyType_Ready(&requestacl_type) < 0) {
+        return (false);
+    }
+    void* p = &requestacl_type;
+    if (PyModule_AddObject(mod, "RequestACL", static_cast<PyObject*>(p)) < 0) {
+        return (false);
+    }
+    Py_INCREF(&requestacl_type);
+
+    return (true);
+}
+} // namespace python
+} // namespace dns
+} // namespace acl
+} // namespace isc
diff --git a/src/lib/python/isc/acl/dns_requestacl_python.h b/src/lib/python/isc/acl/dns_requestacl_python.h
new file mode 100644
index 0000000..8f7ad8a
--- /dev/null
+++ b/src/lib/python/isc/acl/dns_requestacl_python.h
@@ -0,0 +1,53 @@
+// 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 __PYTHON_REQUESTACL_H
+#define __PYTHON_REQUESTACL_H 1
+
+#include <Python.h>
+
+#include <boost/shared_ptr.hpp>
+
+#include <acl/dns.h>
+
+namespace isc {
+namespace acl {
+namespace dns {
+namespace python {
+
+// The s_* Class simply covers one instantiation of the object
+class s_RequestACL : public PyObject {
+public:
+    s_RequestACL();
+
+    // We don't have to use a shared pointer for its original purposes as
+    // the python object maintains reference counters itself.  But the
+    // underlying C++ API only exposes a shared pointer for the ACL objects,
+    // so we store it in that form.
+    boost::shared_ptr<RequestACL> cppobj;
+};
+
+extern PyTypeObject requestacl_type;
+
+bool initModulePart_RequestACL(PyObject* mod);
+
+} // namespace python
+} // namespace dns
+} // namespace acl
+} // namespace isc
+#endif // __PYTHON_REQUESTACL_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/python/isc/acl/dns_requestcontext_inc.cc b/src/lib/python/isc/acl/dns_requestcontext_inc.cc
new file mode 100644
index 0000000..9e80e1f
--- /dev/null
+++ b/src/lib/python/isc/acl/dns_requestcontext_inc.cc
@@ -0,0 +1,30 @@
+namespace {
+const char* const RequestContext_doc = "\
+DNS request to be checked.\n\
+\n\
+This plays the role of ACL context for the RequestACL object.\n\
+\n\
+Based on the minimalist philosophy, the initial implementation only\n\
+maintains the remote (source) IP address of the request. The plan is\n\
+to add more parameters of the request. A scheduled next step is to\n\
+support the TSIG key (if it's included in the request). Other\n\
+possibilities are the local (destination) IP address, the remote and\n\
+local port numbers, various fields of the DNS request (e.g. a\n\
+particular header flag value).\n\
+\n\
+RequestContext(remote_address)\n\
+\n\
+    In this initial implementation, the constructor only takes a\n\
+    remote IP address in the form of a socket address as used in the\n\
+    Python socket module.\n\
+\n\
+    Exceptions:\n\
+      isc.acl.ACLError Normally shouldn't happen, but still possible\n\
+                     for unexpected errors such as memory allocation\n\
+                     failure or an invalid address text being passed.\n\
+\n\
+    Parameters:\n\
+      remote_address The remote IP address\n\
+\n\
+";
+} // unnamed namespace
diff --git a/src/lib/python/isc/acl/dns_requestcontext_python.cc b/src/lib/python/isc/acl/dns_requestcontext_python.cc
new file mode 100644
index 0000000..6c63b59
--- /dev/null
+++ b/src/lib/python/isc/acl/dns_requestcontext_python.cc
@@ -0,0 +1,319 @@
+// 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.
+
+// Enable this if you use s# variants with PyArg_ParseTuple(), see
+// http://docs.python.org/py3k/c-api/arg.html#strings-and-buffers
+//#define PY_SSIZE_T_CLEAN
+
+// Python.h needs to be placed at the head of the program file, see:
+// http://docs.python.org/py3k/extending/extending.html#a-simple-example
+#include <Python.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <string.h>
+
+#include <cassert>
+#include <memory>
+#include <string>
+#include <sstream>
+#include <stdexcept>
+
+#include <boost/scoped_ptr.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include <exceptions/exceptions.h>
+
+#include <util/python/pycppwrapper_util.h>
+
+#include <acl/dns.h>
+#include <acl/ip_check.h>
+
+#include "dns.h"
+#include "dns_requestcontext_python.h"
+
+using namespace std;
+using boost::scoped_ptr;
+using boost::lexical_cast;
+using namespace isc;
+using namespace isc::util::python;
+using namespace isc::acl::dns;
+using namespace isc::acl::dns::python;
+
+namespace isc {
+namespace acl {
+namespace dns {
+namespace python {
+
+struct s_RequestContext::Data {
+    // The constructor.  Currently it only accepts the information of the
+    // request source address, and contains all necessary logic in the body
+    // of the constructor.  As it's extended we may have refactor it by
+    // introducing helper methods.
+    Data(const char* const remote_addr, const unsigned short remote_port) {
+        struct addrinfo hints, *res;
+        memset(&hints, 0, sizeof(hints));
+        hints.ai_family = AF_UNSPEC;
+        hints.ai_socktype = SOCK_DGRAM;
+        hints.ai_protocol = IPPROTO_UDP;
+        hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
+        const int error(getaddrinfo(remote_addr,
+                                    lexical_cast<string>(remote_port).c_str(),
+                                    &hints, &res));
+        if (error != 0) {
+            isc_throw(InvalidParameter, "Failed to convert [" << remote_addr
+                      << "]:" << remote_port << ", " << gai_strerror(error));
+        }
+        assert(sizeof(remote_ss) > res->ai_addrlen);
+        memcpy(&remote_ss, res->ai_addr, res->ai_addrlen);
+        remote_salen = res->ai_addrlen;
+        freeaddrinfo(res);
+
+        remote_ipaddr.reset(new IPAddress(getRemoteSockaddr()));
+    }
+
+    // A convenient type converter from sockaddr_storage to sockaddr
+    const struct sockaddr& getRemoteSockaddr() const {
+        const void* p = &remote_ss;
+        return (*static_cast<const struct sockaddr*>(p));
+    }
+
+    // The remote (source) IP address the request.  Note that it needs
+    // a reference to remote_ss.  That's why the latter is stored within
+    // this structure.
+    scoped_ptr<IPAddress> remote_ipaddr;
+
+    // The effective length of remote_ss.  It's necessary for getnameinf()
+    // called from sockaddrToText (__str__ backend).
+    socklen_t remote_salen;
+
+private:
+    struct sockaddr_storage remote_ss;
+};
+
+} // namespace python
+} // namespace dns
+} // namespace acl
+} // namespace isc
+
+
+//
+// Definition of the classes
+//
+
+// For each class, we need a struct, a helper functions (init, destroy,
+// and static wrappers around the methods we export), a list of methods,
+// and a type description
+
+//
+// RequestContext
+//
+
+// Trivial constructor.
+s_RequestContext::s_RequestContext() : cppobj(NULL), data_(NULL) {
+}
+
+// Import pydoc text
+#include "dns_requestcontext_inc.cc"
+
+namespace {
+// This list contains the actual set of functions we have in
+// python. Each entry has
+// 1. Python method name
+// 2. Our static function here
+// 3. Argument type
+// 4. Documentation
+PyMethodDef RequestContext_methods[] = {
+    { NULL, NULL, 0, NULL }
+};
+
+int
+RequestContext_init(PyObject* po_self, PyObject* args, PyObject*) {
+    s_RequestContext* const self = static_cast<s_RequestContext*>(po_self);
+
+    try {
+        // In this initial implementation, the constructor is simply: It
+        // takes a single parameter, which should be a Python socket address
+        // object.  For IPv4, it's ('address test', numeric_port); for IPv6,
+        // it's ('address text', num_port, num_flowid, num_zoneid).
+        // Below, we parse the argument in the most straightforward way.
+        // As the constructor becomes more complicated, we should probably
+        // make it more structural (for example, we should first retrieve
+        // the socket address as a PyObject, and parse it recursively)
+
+        const char* remote_addr;
+        unsigned short remote_port;
+        unsigned int remote_flowinfo; // IPv6 only, unused here
+        unsigned int remote_zoneid; // IPv6 only, unused here
+
+        if (PyArg_ParseTuple(args, "(sH)", &remote_addr, &remote_port) ||
+            PyArg_ParseTuple(args, "(sHII)", &remote_addr, &remote_port,
+                             &remote_flowinfo, &remote_zoneid))
+        {
+            // We need to clear the error in case the first call to PareTuple
+            // fails.
+            PyErr_Clear();
+
+            auto_ptr<s_RequestContext::Data> dataptr(
+                new s_RequestContext::Data(remote_addr, remote_port));
+            self->cppobj = new RequestContext(*dataptr->remote_ipaddr);
+            self->data_ = dataptr.release();
+            return (0);
+        }
+    } catch (const exception& ex) {
+        const string ex_what = "Failed to construct RequestContext object: " +
+            string(ex.what());
+        PyErr_SetString(getACLException("Error"), ex_what.c_str());
+        return (-1);
+    } catch (...) {
+        PyErr_SetString(PyExc_RuntimeError,
+                        "Unexpected exception in constructing RequestContext");
+        return (-1);
+    }
+
+    PyErr_SetString(PyExc_TypeError,
+                    "Invalid arguments to RequestContext constructor");
+
+    return (-1);
+}
+
+void
+RequestContext_destroy(PyObject* po_self) {
+    s_RequestContext* const self = static_cast<s_RequestContext*>(po_self);
+
+    delete self->cppobj;
+    delete self->data_;
+    Py_TYPE(self)->tp_free(self);
+}
+
+// A helper function for __str__()
+string
+sockaddrToText(const struct sockaddr& sa, socklen_t sa_len) {
+    char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
+    if (getnameinfo(&sa, sa_len, hbuf, sizeof(hbuf), sbuf, sizeof(sbuf),
+                    NI_NUMERICHOST | NI_NUMERICSERV)) {
+        // In this context this should never fail.
+        isc_throw(Unexpected, "Unexpected failure in getnameinfo");
+    }
+
+    return ("[" + string(hbuf) + "]:" + string(sbuf));
+}
+
+// for the __str__() method.  This method is provided mainly for internal
+// testing.
+PyObject*
+RequestContext_str(PyObject* po_self) {
+    const s_RequestContext* const self =
+        static_cast<s_RequestContext*>(po_self);
+
+    try {
+        stringstream objss;
+        objss << "<" << requestcontext_type.tp_name << " object, "
+              << "remote_addr="
+              << sockaddrToText(self->data_->getRemoteSockaddr(),
+                                self->data_->remote_salen) << ">";
+        return (Py_BuildValue("s", objss.str().c_str()));
+    } catch (const exception& ex) {
+        const string ex_what =
+            "Failed to convert RequestContext object to text: " +
+            string(ex.what());
+        PyErr_SetString(PyExc_RuntimeError, ex_what.c_str());
+    } catch (...) {
+        PyErr_SetString(PyExc_SystemError, "Unexpected failure in "
+                        "converting RequestContext object to text");
+    }
+    return (NULL);
+}
+} // end of unnamed namespace
+
+namespace isc {
+namespace acl {
+namespace dns {
+namespace python {
+// This defines the complete type for reflection in python and
+// parsing of PyObject* to s_RequestContext
+// Most of the functions are not actually implemented and NULL here.
+PyTypeObject requestcontext_type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "isc.acl.dns.RequestContext",
+    sizeof(s_RequestContext),                 // tp_basicsize
+    0,                                  // tp_itemsize
+    RequestContext_destroy,             // tp_dealloc
+    NULL,                               // tp_print
+    NULL,                               // tp_getattr
+    NULL,                               // tp_setattr
+    NULL,                               // tp_reserved
+    NULL,                               // tp_repr
+    NULL,                               // tp_as_number
+    NULL,                               // tp_as_sequence
+    NULL,                               // tp_as_mapping
+    NULL,                               // tp_hash
+    NULL,                               // tp_call
+    RequestContext_str,                 // tp_str
+    NULL,                               // tp_getattro
+    NULL,                               // tp_setattro
+    NULL,                               // tp_as_buffer
+    Py_TPFLAGS_DEFAULT,                 // tp_flags
+    RequestContext_doc,
+    NULL,                               // tp_traverse
+    NULL,                               // tp_clear
+    NULL, // tp_richcompare
+    0,                                  // tp_weaklistoffset
+    NULL,                               // tp_iter
+    NULL,                               // tp_iternext
+    RequestContext_methods,                   // tp_methods
+    NULL,                               // tp_members
+    NULL,                               // tp_getset
+    NULL,                               // tp_base
+    NULL,                               // tp_dict
+    NULL,                               // tp_descr_get
+    NULL,                               // tp_descr_set
+    0,                                  // tp_dictoffset
+    RequestContext_init,                // tp_init
+    NULL,                               // tp_alloc
+    PyType_GenericNew,                  // tp_new
+    NULL,                               // tp_free
+    NULL,                               // tp_is_gc
+    NULL,                               // tp_bases
+    NULL,                               // tp_mro
+    NULL,                               // tp_cache
+    NULL,                               // tp_subclasses
+    NULL,                               // tp_weaklist
+    NULL,                               // tp_del
+    0                                   // tp_version_tag
+};
+
+bool
+initModulePart_RequestContext(PyObject* mod) {
+    // We initialize the static description object with PyType_Ready(),
+    // then add it to the module. This is not just a check! (leaving
+    // this out results in segmentation faults)
+    if (PyType_Ready(&requestcontext_type) < 0) {
+        return (false);
+    }
+    void* p = &requestcontext_type;
+    if (PyModule_AddObject(mod, "RequestContext",
+                           static_cast<PyObject*>(p)) < 0) {
+        return (false);
+    }
+    Py_INCREF(&requestcontext_type);
+
+    return (true);
+}
+} // namespace python
+} // namespace dns
+} // namespace acl
+} // namespace isc
diff --git a/src/lib/python/isc/acl/dns_requestcontext_python.h b/src/lib/python/isc/acl/dns_requestcontext_python.h
new file mode 100644
index 0000000..766133b
--- /dev/null
+++ b/src/lib/python/isc/acl/dns_requestcontext_python.h
@@ -0,0 +1,54 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __PYTHON_REQUESTCONTEXT_H
+#define __PYTHON_REQUESTCONTEXT_H 1
+
+#include <Python.h>
+
+#include <acl/dns.h>
+
+namespace isc {
+namespace acl {
+namespace dns {
+namespace python {
+
+// The s_* Class simply covers one instantiation of the object
+class s_RequestContext : public PyObject {
+public:
+    s_RequestContext();
+    RequestContext* cppobj;
+
+    // This object needs to maintain some source data to construct the
+    // underlying RequestContext object throughout its lifetime.
+    // These are "public" so that it can be accessed in the python wrapper
+    // implementation, but essentially they should be private, and the
+    // implementation details are hidden.
+    struct Data;
+    Data* data_;
+};
+
+extern PyTypeObject requestcontext_type;
+
+bool initModulePart_RequestContext(PyObject* mod);
+
+} // namespace python
+} // namespace dns
+} // namespace acl
+} // namespace isc
+#endif // __PYTHON_REQUESTCONTEXT_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/python/isc/acl/dns_requestloader_inc.cc b/src/lib/python/isc/acl/dns_requestloader_inc.cc
new file mode 100644
index 0000000..a911275
--- /dev/null
+++ b/src/lib/python/isc/acl/dns_requestloader_inc.cc
@@ -0,0 +1,87 @@
+namespace {
+
+// Note: this is derived from the generic Loader class of the C++
+// implementation, but is slightly different from the original.
+// Be careful when you make further merge from the C++ document.
+const char* const RequestLoader_doc = "\
+Loader of DNS Request ACLs.\n\
+\n\
+The goal of this class is to convert JSON description of an ACL to\n\
+object of the ACL class (including the checks inside it).\n\
+\n\
+To allow any kind of checks to exist in the application, creators are\n\
+registered for the names of the checks (this feature is not yet\n\
+available for the python API).\n\
+\n\
+An ACL definition looks like this:  [\n\
+   {\n\
+      \"action\": \"ACCEPT\",\n\
+      \"match-type\": <parameter>\n\
+   },\n\
+   {\n\
+      \"action\": \"REJECT\",\n\
+      \"match-type\": <parameter>,\n\
+      \"another-match-type\": [<parameter1>, <parameter2>]\n\
+   },\n\
+   {\n\
+      \"action\": \"DROP\"\n\
+   }\n\
+ ]\n\
+ \n\
+\n\
+This is a list of elements. Each element must have an \"action\"\n\
+entry/keyword. That one specifies which action is returned if this\n\
+element matches (the value of the key is passed to the action loader\n\
+(see the constructor), which is one of ACCEPT,\n\
+REJECT, or DROP, as defined in the isc.acl.acl module.\n\
+\n\
+The rest of the element are matches. The left side is the name of the\n\
+match type (for example \"from\" to match for source IP address).\n\
+The <parameter> is whatever is needed to describe the\n\
+match and depends on the match type, the loader passes it verbatim to\n\
+creator of that match type.\n\
+\n\
+There may be multiple match types in single element. In such case, all\n\
+of the matches must match for the element to take action (so, in the\n\
+second element, both \"match-type\" and \"another-match-type\" must be\n\
+satisfied). If there's no match in the element, the action is\n\
+taken/returned without conditions, every time (makes sense as the last\n\
+entry, as the ACL will never get past it).\n\
+\n\
+The second entry shows another thing - if there's a list as the value\n\
+for some match and the match itself is not expecting a list, it is\n\
+taken as an \"or\" - a match for at last one of the choices in the\n\
+list must match. So, for the second entry, both \"match-type\" and\n\
+\"another-match-type\" must be satisfied, but the another one is\n\
+satisfied by either parameter1 or parameter2.\n\
+\n\
+Currently, a RequestLoader object cannot be constructed directly;\n\
+an application must use the singleton loader defined in the\n\
+isc.acl.dns module, i.e., isc.acl.dns.REQUEST_LOADER.\n\
+A future version of this implementation may be extended to give\n\
+applications full flexibility of creating arbitrary loader, when\n\
+this restriction may be removed.\n\
+";
+
+const char* const RequestLoader_load_doc = "\
+load(description) -> RequestACL\n\
+\n\
+Load a DNS (Request) ACL.\n\
+\n\
+This parses an ACL list, creates internal data for each rule\n\
+and returns a RequestACl object that contains all given rules.\n\
+\n\
+Exceptions:\n\
+  LoaderError Load failed.  The most likely cause of this is a syntax\n\
+              error in the description.  Other internal errors such as\n\
+              memory allocation failure is also converted to this\n\
+              exception.\n\
+\n\
+Parameters:\n\
+  description String or Python representation of the JSON list of\n\
+              ACL. The Python representation is ones accepted by the\n\
+              standard json module.\n\
+\n\
+Return Value(s): The newly created RequestACL object\n\
+";
+} // unnamed namespace
diff --git a/src/lib/python/isc/acl/dns_requestloader_python.cc b/src/lib/python/isc/acl/dns_requestloader_python.cc
new file mode 100644
index 0000000..1ddff4c
--- /dev/null
+++ b/src/lib/python/isc/acl/dns_requestloader_python.cc
@@ -0,0 +1,270 @@
+// 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.
+
+// Enable this if you use s# variants with PyArg_ParseTuple(), see
+// http://docs.python.org/py3k/c-api/arg.html#strings-and-buffers
+//#define PY_SSIZE_T_CLEAN
+
+// Python.h needs to be placed at the head of the program file, see:
+// http://docs.python.org/py3k/extending/extending.html#a-simple-example
+#include <Python.h>
+
+#include <string>
+#include <stdexcept>
+
+#include <boost/shared_ptr.hpp>
+
+#include <util/python/pycppwrapper_util.h>
+
+#include <cc/data.h>
+
+#include <acl/dns.h>
+
+#include "dns.h"
+#include "dns_requestacl_python.h"
+#include "dns_requestloader_python.h"
+
+using namespace std;
+using boost::shared_ptr;
+using namespace isc::util::python;
+using namespace isc::data;
+using namespace isc::acl::dns;
+using namespace isc::acl::dns::python;
+
+//
+// Definition of the classes
+//
+
+// For each class, we need a struct, a helper functions (init, destroy,
+// and static wrappers around the methods we export), a list of methods,
+// and a type description
+
+//
+// RequestLoader
+//
+
+// Trivial constructor.
+s_RequestLoader::s_RequestLoader() : cppobj(NULL) {
+}
+
+// Import pydoc text
+#include "dns_requestloader_inc.cc"
+
+namespace {
+//
+// We declare the functions here, the definitions are below
+// the type definition of the object, since both can use the other
+//
+
+int
+RequestLoader_init(PyObject*, PyObject*, PyObject*) {
+    PyErr_SetString(getACLException("Error"),
+                    "RequestLoader cannot be directly constructed");
+    return (-1);
+}
+
+void
+RequestLoader_destroy(PyObject* po_self) {
+    s_RequestLoader* const self = static_cast<s_RequestLoader*>(po_self);
+    delete self->cppobj;
+    self->cppobj = NULL;
+    Py_TYPE(self)->tp_free(self);
+}
+
+// This C structure corresponds to a Python callable object for json.dumps().
+// This is initialized at the class initialization time (in
+// initModulePart_RequestLoader() below) and it's ensured to be non NULL and
+// valid in the rest of the class implementation.
+// Getting access to the json module this way and call one of its functions
+// via PyObject_CallObject() may exceed the reasonably acceptable level for
+// straightforward bindings.  But the alternative would be to write a Python
+// frontend for the entire module only for this conversion, which would also
+// be too much.  So, right now, we implement everything within the binding
+// implementation.  If future extensions require more such non trivial
+// wrappers, we should consider the frontend approach more seriously.
+PyObject* json_dumps_obj = NULL;
+
+PyObject*
+RequestLoader_load(PyObject* po_self, PyObject* args) {
+    s_RequestLoader* const self = static_cast<s_RequestLoader*>(po_self);
+
+    try {
+        PyObjectContainer c1, c2; // placeholder for temporary py objects
+        const char* acl_config;
+
+        // First, try string
+        int py_result = PyArg_ParseTuple(args, "s", &acl_config);
+        if (!py_result) {
+            PyErr_Clear();  // need to clear the error from ParseTuple
+
+            // If that fails, confirm the argument is a single Python object,
+            // and pass the argument to json.dumps() without conversion.
+            // Note that we should pass 'args', not 'json_obj' to
+            // PyObject_CallObject(), since this function expects a form of
+            // tuple as its argument parameter, just like ParseTuple.
+            PyObject* json_obj;
+            if (PyArg_ParseTuple(args, "O", &json_obj)) {
+                c1.reset(PyObject_CallObject(json_dumps_obj, args));
+                c2.reset(Py_BuildValue("(O)", c1.get()));
+                py_result = PyArg_ParseTuple(c2.get(), "s", &acl_config);
+            }
+        }
+        if (py_result) {
+            shared_ptr<RequestACL> acl(
+                self->cppobj->load(Element::fromJSON(acl_config)));
+            s_RequestACL* py_acl = static_cast<s_RequestACL*>(
+                requestacl_type.tp_alloc(&requestacl_type, 0));
+            if (py_acl != NULL) {
+                py_acl->cppobj = acl;
+            }
+            return (py_acl);
+        }
+    } catch (const PyCPPWrapperException&) {
+        // If the wrapper utility throws, it's most likely because an invalid
+        // type of argument is passed (and the call to json.dumps() failed
+        // above), rather than a rare case of system errors such as memory
+        // allocation failure.  So we fall through to the end of this function
+        // and raise a TypeError.
+        ;
+    } catch (const exception& ex) {
+        PyErr_SetString(getACLException("LoaderError"), ex.what());
+        return (NULL);
+    } catch (...) {
+        PyErr_SetString(PyExc_SystemError, "Unexpected C++ exception");
+        return (NULL);
+    }
+
+    PyErr_SetString(PyExc_TypeError, "RequestLoader.load() "
+                    "expects str or python representation of JSON");
+    return (NULL);
+}
+
+// This list contains the actual set of functions we have in
+// python. Each entry has
+// 1. Python method name
+// 2. Our static function here
+// 3. Argument type
+// 4. Documentation
+PyMethodDef RequestLoader_methods[] = {
+    { "load", RequestLoader_load, METH_VARARGS, RequestLoader_load_doc },
+    { NULL, NULL, 0, NULL }
+};
+} // end of unnamed namespace
+
+namespace isc {
+namespace acl {
+namespace dns {
+namespace python {
+// This defines the complete type for reflection in python and
+// parsing of PyObject* to s_RequestLoader
+// Most of the functions are not actually implemented and NULL here.
+PyTypeObject requestloader_type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "isc.acl.dns.RequestLoader",
+    sizeof(s_RequestLoader),                 // tp_basicsize
+    0,                                  // tp_itemsize
+    RequestLoader_destroy,       // tp_dealloc
+    NULL,                               // tp_print
+    NULL,                               // tp_getattr
+    NULL,                               // tp_setattr
+    NULL,                               // tp_reserved
+    NULL,                               // tp_repr
+    NULL,                               // tp_as_number
+    NULL,                               // tp_as_sequence
+    NULL,                               // tp_as_mapping
+    NULL,                               // tp_hash
+    NULL,                               // tp_call
+    NULL,                       // tp_str
+    NULL,                               // tp_getattro
+    NULL,                               // tp_setattro
+    NULL,                               // tp_as_buffer
+    Py_TPFLAGS_DEFAULT,                 // tp_flags
+    RequestLoader_doc,
+    NULL,                               // tp_traverse
+    NULL,                               // tp_clear
+    NULL, // tp_richcompare
+    0,                                  // tp_weaklistoffset
+    NULL,                               // tp_iter
+    NULL,                               // tp_iternext
+    RequestLoader_methods,                   // tp_methods
+    NULL,                               // tp_members
+    NULL,                               // tp_getset
+    NULL,                               // tp_base
+    NULL,                               // tp_dict
+    NULL,                               // tp_descr_get
+    NULL,                               // tp_descr_set
+    0,                                  // tp_dictoffset
+    RequestLoader_init,            // tp_init
+    NULL,                               // tp_alloc
+    PyType_GenericNew,                  // tp_new
+    NULL,                               // tp_free
+    NULL,                               // tp_is_gc
+    NULL,                               // tp_bases
+    NULL,                               // tp_mro
+    NULL,                               // tp_cache
+    NULL,                               // tp_subclasses
+    NULL,                               // tp_weaklist
+    NULL,                               // tp_del
+    0                                   // tp_version_tag
+};
+
+bool
+initModulePart_RequestLoader(PyObject* mod) {
+    // We initialize the static description object with PyType_Ready(),
+    // then add it to the module. This is not just a check! (leaving
+    // this out results in segmentation faults)
+    if (PyType_Ready(&requestloader_type) < 0) {
+        return (false);
+    }
+    void* p = &requestloader_type;
+    if (PyModule_AddObject(mod, "RequestLoader",
+                           static_cast<PyObject*>(p)) < 0) {
+        return (false);
+    }
+
+    // Get and hold our own reference to json.dumps() for later use.
+    // Normally it should succeed as __init__.py of the isc.acl package
+    // explicitly imports the json module, and the code below should be
+    // error free (e.g. they don't require memory allocation) under this
+    // condition.
+    // This could still fail with deviant or evil Python code such as those
+    // that first import json and then delete the reference to it from
+    // sys.modules before it imports the acl.dns module.  The RequestLoader
+    // class could still work as long as it doesn't use the JSON decoder,
+    // but we'd rather refuse to import the module than allowing the partially
+    // workable class to keep running.
+    PyObject* json_module = PyImport_AddModule("json");
+    if (json_module != NULL) {
+        PyObject* json_dict = PyModule_GetDict(json_module);
+        if (json_dict != NULL) {
+            json_dumps_obj = PyDict_GetItemString(json_dict, "dumps");
+        }
+    }
+    if (json_dumps_obj != NULL) {
+        Py_INCREF(json_dumps_obj);
+    } else {
+        PyErr_SetString(PyExc_RuntimeError,
+                        "isc.acl.dns.RequestLoader needs the json module, but "
+                        "it's missing");
+        return (false);
+    }
+
+    Py_INCREF(&requestloader_type);
+
+    return (true);
+}
+} // namespace python
+} // namespace dns
+} // namespace acl
+} // namespace isc
diff --git a/src/lib/python/isc/acl/dns_requestloader_python.h b/src/lib/python/isc/acl/dns_requestloader_python.h
new file mode 100644
index 0000000..9d0b63e
--- /dev/null
+++ b/src/lib/python/isc/acl/dns_requestloader_python.h
@@ -0,0 +1,46 @@
+// 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 __PYTHON_REQUESTLOADER_H
+#define __PYTHON_REQUESTLOADER_H 1
+
+#include <Python.h>
+
+#include <acl/dns.h>
+
+namespace isc {
+namespace acl {
+namespace dns {
+namespace python {
+
+// The s_* Class simply covers one instantiation of the object
+class s_RequestLoader : public PyObject {
+public:
+    s_RequestLoader();
+    RequestLoader* cppobj;
+};
+
+extern PyTypeObject requestloader_type;
+
+bool initModulePart_RequestLoader(PyObject* mod);
+
+} // namespace python
+} // namespace dns
+} // namespace acl
+} // namespace isc
+#endif // __PYTHON_REQUESTLOADER_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/python/isc/acl/dnsacl_inc.cc b/src/lib/python/isc/acl/dnsacl_inc.cc
new file mode 100644
index 0000000..b2e7338
--- /dev/null
+++ b/src/lib/python/isc/acl/dnsacl_inc.cc
@@ -0,0 +1,17 @@
+namespace {
+const char* const dnsacl_doc = "\
+Implementation module for DNS ACL operations\n\n\
+This module provides Python bindings for the C++ classes in the\n\
+isc::acl::dns namespace.  Specifically, it defines Python interfaces of\n\
+handling access control lists (ACLs) with DNS related contexts.\n\
+These bindings are close match to the C++ API, but they are not complete\n\
+(some parts are not needed) and some are done in more python-like ways.\n\
+\n\
+Special objects:\n\
+\n\
+REQUEST_LOADER -- A singleton loader of ACLs. It is expected applications\n\
+  will use this function instead of creating their own loaders, because\n\
+  one is enough, this one will have registered default checks and it is\n\
+  known one, so any plugins can registrer additional checks as well.\n\
+";
+} // unnamed namespace
diff --git a/src/lib/python/isc/acl/tests/Makefile.am b/src/lib/python/isc/acl/tests/Makefile.am
new file mode 100644
index 0000000..64737d2
--- /dev/null
+++ b/src/lib/python/isc/acl/tests/Makefile.am
@@ -0,0 +1,30 @@
+PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
+PYTESTS = acl_test.py dns_test.py
+
+EXTRA_DIST = $(PYTESTS)
+
+# If necessary (rare cases), explicitly specify paths to dynamic libraries
+# required by loadable python modules.
+LIBRARY_PATH_PLACEHOLDER =
+if SET_ENV_LIBRARY_PATH
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/acl/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
+endif
+
+# test using command-line arguments, so use check-local target instead of TESTS
+check-local:
+if ENABLE_PYTHON_COVERAGE
+	touch $(abs_top_srcdir)/.coverage
+	rm -f .coverage
+	${LN_S} $(abs_top_srcdir)/.coverage .coverage
+endif
+	for pytest in $(PYTESTS) ; do \
+	echo Running test: $$pytest ; \
+	env PYTHONPATH=$(abs_top_builddir)/src/lib/isc/python/acl/.libs:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python \
+	$(LIBRARY_PATH_PLACEHOLDER) \
+	$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
+	done
+
+CLEANDIRS = __pycache__
+
+clean-local:
+	rm -rf $(CLEANDIRS)
diff --git a/src/lib/python/isc/acl/tests/acl_test.py b/src/lib/python/isc/acl/tests/acl_test.py
new file mode 100644
index 0000000..24a0c94
--- /dev/null
+++ b/src/lib/python/isc/acl/tests/acl_test.py
@@ -0,0 +1,29 @@
+# Copyright (C) 2011  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import unittest
+from isc.acl.acl import *
+
+class ACLTest(unittest.TestCase):
+
+    def test_actions(self):
+        # These are simple tests just checking the pre defined actions have
+        # different values
+        self.assertTrue(ACCEPT != REJECT)
+        self.assertTrue(REJECT != DROP)
+        self.assertTrue(DROP != ACCEPT)
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/src/lib/python/isc/acl/tests/dns_test.py b/src/lib/python/isc/acl/tests/dns_test.py
new file mode 100644
index 0000000..acaf32b
--- /dev/null
+++ b/src/lib/python/isc/acl/tests/dns_test.py
@@ -0,0 +1,280 @@
+# Copyright (C) 2011  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import unittest
+import socket
+from isc.acl.acl import LoaderError, Error, ACCEPT, REJECT, DROP
+from isc.acl.dns import *
+
+def get_sockaddr(address, port):
+    '''This is a simple shortcut wrapper for getaddrinfo'''
+    ai = socket.getaddrinfo(address, port, 0, socket.SOCK_DGRAM,
+                            socket.IPPROTO_UDP, socket.AI_NUMERICHOST)[0]
+    return ai[4]
+
+def get_acl(prefix):
+    '''This is a simple shortcut for creating an ACL containing single rule
+    that accepts addresses for the given IP prefix (and reject any others
+    by default)
+    '''
+    return REQUEST_LOADER.load('[{"action": "ACCEPT", "from": "' + \
+                                   prefix + '"}]')
+
+def get_acl_json(prefix):
+    '''Same as get_acl, but this function passes a Python representation of
+    JSON to the loader, not a string.'''
+    json = [{"action": "ACCEPT"}]
+    json[0]["from"] = prefix
+    return REQUEST_LOADER.load(json)
+
+def get_context(address):
+    '''This is a simple shortcut wrapper for creating a RequestContext
+    object with a given IP address.  Port number doesn't matter in the test
+    (as of the initial implementation), so it's fixed for simplicity.
+    '''
+    return RequestContext(get_sockaddr(address, 53000))
+
+# These are commonly used RequestContext object
+CONTEXT4 = get_context('192.0.2.1')
+CONTEXT6 = get_context('2001:db8::1')
+
+class RequestContextTest(unittest.TestCase):
+
+    def test_construct(self):
+        # Construct the context from IPv4/IPv6 addresses, check the object
+        # by printing it.
+        self.assertEqual('<isc.acl.dns.RequestContext object, ' + \
+                             'remote_addr=[192.0.2.1]:53001>',
+                         RequestContext(('192.0.2.1', 53001)).__str__())
+        self.assertEqual('<isc.acl.dns.RequestContext object, ' + \
+                             'remote_addr=[2001:db8::1234]:53006>',
+                         RequestContext(('2001:db8::1234', 53006,
+                                         0, 0)).__str__())
+
+        # Unusual case: port number overflows (this constructor allows that,
+        # although it should be rare anyway; the socket address should
+        # normally come from the Python socket module.
+        self.assertEqual('<isc.acl.dns.RequestContext object, ' + \
+                             'remote_addr=[192.0.2.1]:0>',
+                         RequestContext(('192.0.2.1', 65536)).__str__())
+
+        # same test using socket.getaddrinfo() to ensure it accepts the sock
+        # address representation used in the Python socket module.
+        self.assertEqual('<isc.acl.dns.RequestContext object, ' + \
+                             'remote_addr=[192.0.2.1]:53001>',
+                         RequestContext(get_sockaddr('192.0.2.1',
+                                                     53001)).__str__())
+        self.assertEqual('<isc.acl.dns.RequestContext object, ' + \
+                             'remote_addr=[2001:db8::1234]:53006>',
+                         RequestContext(get_sockaddr('2001:db8::1234',
+                                                     53006)).__str__())
+
+        #
+        # Invalid parameters (in our expected usage this should not happen
+        # because the sockaddr would come from the Python socket module, but
+        # validation should still be performed correctly)
+        #
+        # not a tuple
+        self.assertRaises(TypeError, RequestContext, 1)
+        # invalid number of parameters
+        self.assertRaises(TypeError, RequestContext, ('192.0.2.1', 53), 0)
+        # tuple is not in the form of sockaddr
+        self.assertRaises(TypeError, RequestContext, (0, 53))
+        self.assertRaises(TypeError, RequestContext, ('192.0.2.1', 'http'))
+        self.assertRaises(TypeError, RequestContext, ('::', 0, 'flow', 0))
+        # invalid address
+        self.assertRaises(Error, RequestContext, ('example.com', 5300))
+        self.assertRaises(Error, RequestContext, ('192.0.2.1.1', 5300))
+        self.assertRaises(Error, RequestContext, ('2001:db8:::1', 5300))
+
+class RequestACLTest(unittest.TestCase):
+
+    def test_direct_construct(self):
+        self.assertRaises(Error, RequestACL)
+
+    def test_request_loader(self):
+        # these shouldn't raise an exception
+        REQUEST_LOADER.load('[{"action": "DROP"}]')
+        REQUEST_LOADER.load([{"action": "DROP"}])
+        REQUEST_LOADER.load('[{"action": "DROP", "from": "192.0.2.1"}]')
+        REQUEST_LOADER.load([{"action": "DROP", "from": "192.0.2.1"}])
+
+        # Invalid types (note that arguments like '1' or '[]' is of valid
+        # 'type' (but syntax error at a higher level)).  So we need to use
+        # something that is not really JSON nor string.
+        self.assertRaises(TypeError, REQUEST_LOADER.load, b'')
+
+        # Incorrect number of arguments
+        self.assertRaises(TypeError, REQUEST_LOADER.load,
+                          '[{"action": "DROP"}]', 0)
+
+    def test_bad_acl_syntax(self):
+        # the following are derived from loader_test.cc
+        self.assertRaises(LoaderError, REQUEST_LOADER.load, '{}');
+        self.assertRaises(LoaderError, REQUEST_LOADER.load, {});
+        self.assertRaises(LoaderError, REQUEST_LOADER.load, '42');
+        self.assertRaises(LoaderError, REQUEST_LOADER.load, 42);
+        self.assertRaises(LoaderError, REQUEST_LOADER.load, 'true');
+        self.assertRaises(LoaderError, REQUEST_LOADER.load, True);
+        self.assertRaises(LoaderError, REQUEST_LOADER.load, 'null');
+        self.assertRaises(LoaderError, REQUEST_LOADER.load, None);
+        self.assertRaises(LoaderError, REQUEST_LOADER.load, '"hello"');
+        self.assertRaises(LoaderError, REQUEST_LOADER.load, "hello");
+        self.assertRaises(LoaderError, REQUEST_LOADER.load, '[42]');
+        self.assertRaises(LoaderError, REQUEST_LOADER.load, [42]);
+        self.assertRaises(LoaderError, REQUEST_LOADER.load, '["hello"]');
+        self.assertRaises(LoaderError, REQUEST_LOADER.load, ["hello"]);
+        self.assertRaises(LoaderError, REQUEST_LOADER.load, '[[]]');
+        self.assertRaises(LoaderError, REQUEST_LOADER.load, [[]]);
+        self.assertRaises(LoaderError, REQUEST_LOADER.load, '[true]');
+        self.assertRaises(LoaderError, REQUEST_LOADER.load, [True]);
+        self.assertRaises(LoaderError, REQUEST_LOADER.load, '[null]');
+        self.assertRaises(LoaderError, REQUEST_LOADER.load, [None]);
+        self.assertRaises(LoaderError, REQUEST_LOADER.load, '[{}]');
+        self.assertRaises(LoaderError, REQUEST_LOADER.load, [{}]);
+
+        # the following are derived from dns_test.cc
+        self.assertRaises(LoaderError, REQUEST_LOADER.load,
+                          '[{"action": "ACCEPT", "bad": "192.0.2.1"}]')
+        self.assertRaises(LoaderError, REQUEST_LOADER.load,
+                          [{"action": "ACCEPT", "bad": "192.0.2.1"}])
+        self.assertRaises(LoaderError, REQUEST_LOADER.load,
+                          '[{"action": "ACCEPT", "from": 4}]')
+        self.assertRaises(LoaderError, REQUEST_LOADER.load,
+                          [{"action": "ACCEPT", "from": 4}])
+        self.assertRaises(LoaderError, REQUEST_LOADER.load,
+                          '[{"action": "ACCEPT", "from": []}]')
+        self.assertRaises(LoaderError, REQUEST_LOADER.load,
+                          [{"action": "ACCEPT", "from": []}])
+        self.assertRaises(LoaderError, REQUEST_LOADER.load,
+                          '[{"action": "ACCEPT", "from": "bad"}]')
+        self.assertRaises(LoaderError, REQUEST_LOADER.load,
+                          [{"action": "ACCEPT", "from": "bad"}])
+        self.assertRaises(LoaderError, REQUEST_LOADER.load,
+                          '[{"action": "ACCEPT", "from": null}]')
+        self.assertRaises(LoaderError, REQUEST_LOADER.load,
+                          [{"action": "ACCEPT", "from": None}])
+
+    def test_bad_acl_ipsyntax(self):
+        # this test is derived from ip_check_unittest.cc
+        self.assertRaises(LoaderError, REQUEST_LOADER.load,
+                          '[{"action": "DROP", "from": "192.0.2.43/-1"}]')
+        self.assertRaises(LoaderError, REQUEST_LOADER.load,
+                          [{"action": "DROP", "from": "192.0.2.43/-1"}])
+        self.assertRaises(LoaderError, REQUEST_LOADER.load,
+                          '[{"action": "DROP", "from": "192.0.2.43//1"}]')
+        self.assertRaises(LoaderError, REQUEST_LOADER.load,
+                          [{"action": "DROP", "from": "192.0.2.43//1"}])
+        self.assertRaises(LoaderError, REQUEST_LOADER.load,
+                          '[{"action": "DROP", "from": "192.0.2.43/1/"}]')
+        self.assertRaises(LoaderError, REQUEST_LOADER.load,
+                          [{"action": "DROP", "from": "192.0.2.43/1/"}])
+        self.assertRaises(LoaderError, REQUEST_LOADER.load,
+                          '[{"action": "DROP", "from": "/192.0.2.43/1"}]')
+        self.assertRaises(LoaderError, REQUEST_LOADER.load,
+                          [{"action": "DROP", "from": "/192.0.2.43/1"}])
+        self.assertRaises(LoaderError, REQUEST_LOADER.load,
+                          '[{"action": "DROP", "from": "2001:db8::/xxxx"}]')
+        self.assertRaises(LoaderError, REQUEST_LOADER.load,
+                          [{"action": "DROP", "from": "2001:db8::/xxxx"}])
+        self.assertRaises(LoaderError, REQUEST_LOADER.load,
+                          '[{"action": "DROP", "from": "2001:db8::/32/s"}]')
+        self.assertRaises(LoaderError, REQUEST_LOADER.load,
+                          [{"action": "DROP", "from": "2001:db8::/32/s"}])
+        self.assertRaises(LoaderError, REQUEST_LOADER.load,
+                          '[{"action": "DROP", "from": "1/"}]')
+        self.assertRaises(LoaderError, REQUEST_LOADER.load,
+                          [{"action": "DROP", "from": "1/"}])
+        self.assertRaises(LoaderError, REQUEST_LOADER.load,
+                          '[{"action": "DROP", "from": "/1"}]')
+        self.assertRaises(LoaderError, REQUEST_LOADER.load,
+                          [{"action": "DROP", "from": "/1"}])
+        self.assertRaises(LoaderError, REQUEST_LOADER.load,
+                          '[{"action": "DROP", "from": "192.0.2.0/33"}]')
+        self.assertRaises(LoaderError, REQUEST_LOADER.load,
+                          [{"action": "DROP", "from": "192.0.2.0/33"}])
+        self.assertRaises(LoaderError, REQUEST_LOADER.load,
+                          '[{"action": "DROP", "from": "::1/129"}]')
+        self.assertRaises(LoaderError, REQUEST_LOADER.load,
+                          [{"action": "DROP", "from": "::1/129"}])
+
+    def test_execute(self):
+        # tests derived from dns_test.cc.  We don't directly expose checks
+        # in the python wrapper, so we test it via execute().
+        self.assertEqual(ACCEPT, get_acl('192.0.2.1').execute(CONTEXT4))
+        self.assertEqual(ACCEPT, get_acl_json('192.0.2.1').execute(CONTEXT4))
+        self.assertEqual(REJECT, get_acl('192.0.2.53').execute(CONTEXT4))
+        self.assertEqual(REJECT, get_acl_json('192.0.2.53').execute(CONTEXT4))
+        self.assertEqual(ACCEPT, get_acl('192.0.2.0/24').execute(CONTEXT4))
+        self.assertEqual(ACCEPT, get_acl_json('192.0.2.0/24').execute(CONTEXT4))
+        self.assertEqual(REJECT, get_acl('192.0.1.0/24').execute(CONTEXT4))
+        self.assertEqual(REJECT, get_acl_json('192.0.1.0/24').execute(CONTEXT4))
+        self.assertEqual(REJECT, get_acl('192.0.1.0/24').execute(CONTEXT4))
+        self.assertEqual(REJECT, get_acl_json('192.0.1.0/24').execute(CONTEXT4))
+
+        self.assertEqual(ACCEPT, get_acl('2001:db8::1').execute(CONTEXT6))
+        self.assertEqual(ACCEPT, get_acl_json('2001:db8::1').execute(CONTEXT6))
+        self.assertEqual(REJECT, get_acl('2001:db8::53').execute(CONTEXT6))
+        self.assertEqual(REJECT, get_acl_json('2001:db8::53').execute(CONTEXT6))
+        self.assertEqual(ACCEPT, get_acl('2001:db8::/64').execute(CONTEXT6))
+        self.assertEqual(ACCEPT,
+                         get_acl_json('2001:db8::/64').execute(CONTEXT6))
+        self.assertEqual(REJECT, get_acl('2001:db8:1::/64').execute(CONTEXT6))
+        self.assertEqual(REJECT,
+                         get_acl_json('2001:db8:1::/64').execute(CONTEXT6))
+        self.assertEqual(REJECT, get_acl('32.1.13.184').execute(CONTEXT6))
+        self.assertEqual(REJECT, get_acl_json('32.1.13.184').execute(CONTEXT6))
+
+        # A bit more complicated example, derived from resolver_config_unittest
+        acl = REQUEST_LOADER.load('[ {"action": "ACCEPT", ' +
+                                  '     "from": "192.0.2.1"},' +
+                                  '    {"action": "REJECT",' +
+                                  '     "from": "192.0.2.0/24"},' +
+                                  '    {"action": "DROP",' +
+                                  '     "from": "2001:db8::1"},' +
+                                  '] }')
+        self.assertEqual(ACCEPT, acl.execute(CONTEXT4))
+        self.assertEqual(REJECT, acl.execute(get_context('192.0.2.2')))
+        self.assertEqual(DROP, acl.execute(get_context('2001:db8::1')))
+        self.assertEqual(REJECT, acl.execute(get_context('2001:db8::2')))
+
+        # same test using the JSON representation
+        acl = REQUEST_LOADER.load([{"action": "ACCEPT", "from": "192.0.2.1"},
+                                   {"action": "REJECT",
+                                    "from": "192.0.2.0/24"},
+                                   {"action": "DROP", "from": "2001:db8::1"}])
+        self.assertEqual(ACCEPT, acl.execute(CONTEXT4))
+        self.assertEqual(REJECT, acl.execute(get_context('192.0.2.2')))
+        self.assertEqual(DROP, acl.execute(get_context('2001:db8::1')))
+        self.assertEqual(REJECT, acl.execute(get_context('2001:db8::2')))
+
+    def test_bad_execute(self):
+        acl = get_acl('192.0.2.1')
+        # missing parameter
+        self.assertRaises(TypeError, acl.execute)
+        # too many parameters
+        self.assertRaises(TypeError, acl.execute, get_context('192.0.2.2'), 0)
+        # type mismatch
+        self.assertRaises(TypeError, acl.execute, 'bad parameter')
+
+class RequestLoaderTest(unittest.TestCase):
+    # Note: loading ACLs is tested in other test cases.
+
+    def test_construct(self):
+        # at least for now, we don't allow direct construction.
+        self.assertRaises(Error, RequestLoader)
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/src/lib/python/isc/config/ccsession.py b/src/lib/python/isc/config/ccsession.py
index 3914ef9..846c40e 100644
--- a/src/lib/python/isc/config/ccsession.py
+++ b/src/lib/python/isc/config/ccsession.py
@@ -143,7 +143,7 @@ class ModuleCCSession(ConfigData):
        callbacks are called when 'check_command' is called on the
        ModuleCCSession"""
        
-    def __init__(self, spec_file_name, config_handler, command_handler, cc_session=None, handle_logging_config=False):
+    def __init__(self, spec_file_name, config_handler, command_handler, cc_session=None, handle_logging_config=True):
         """Initialize a ModuleCCSession. This does *NOT* send the
            specification and request the configuration yet. Use start()
            for that once the ModuleCCSession has been initialized.
@@ -164,7 +164,7 @@ class ModuleCCSession(ConfigData):
            the logger manager to apply it. It will also inform the
            logger manager when the logging configuration gets updated.
            The module does not need to do anything except intializing
-           its loggers, and provide log messages
+           its loggers, and provide log messages. Defaults to true.
         """
         module_spec = isc.config.module_spec_from_file(spec_file_name)
         ConfigData.__init__(self, module_spec)
diff --git a/src/lib/python/isc/config/tests/ccsession_test.py b/src/lib/python/isc/config/tests/ccsession_test.py
index 5d09c96..ada0c8a 100644
--- a/src/lib/python/isc/config/tests/ccsession_test.py
+++ b/src/lib/python/isc/config/tests/ccsession_test.py
@@ -108,8 +108,11 @@ class TestModuleCCSession(unittest.TestCase):
     def spec_file(self, file):
         return self.data_path + os.sep + file
         
-    def create_session(self, spec_file_name, config_handler = None, command_handler = None, cc_session = None):
-        return ModuleCCSession(self.spec_file(spec_file_name), config_handler, command_handler, cc_session)
+    def create_session(self, spec_file_name, config_handler = None,
+                       command_handler = None, cc_session = None):
+        return ModuleCCSession(self.spec_file(spec_file_name),
+                               config_handler, command_handler,
+                               cc_session, False)
 
     def test_init(self):
         fake_session = FakeModuleCCSession()
diff --git a/src/lib/python/isc/log/log.cc b/src/lib/python/isc/log/log.cc
index 484151f..aa12664 100644
--- a/src/lib/python/isc/log/log.cc
+++ b/src/lib/python/isc/log/log.cc
@@ -20,6 +20,7 @@
 
 #include <log/message_dictionary.h>
 #include <log/logger_manager.h>
+#include <log/logger_support.h>
 #include <log/logger.h>
 
 #include <config/ccsession.h>
@@ -35,7 +36,7 @@ using boost::bind;
 // (tags/RELEASE_28 115909)) on OSX, where unwinding the stack
 // segfaults the moment this exception was thrown and caught.
 //
-// Placing it in a named namespace instead of the original
+// Placing it in a named namespace instead of the originalRecommend
 // unnamed namespace appears to solve this, so as a temporary
 // workaround, we create a local randomly named namespace here
 // to solve this issue.
@@ -184,6 +185,27 @@ init(PyObject*, PyObject* args) {
     Py_RETURN_NONE;
 }
 
+// This initialization is for unit tests.  It allows message settings to be
+// be determined by a set of B10_xxx environment variables.  (See the
+// description of initLogger() for more details.)  The function has been named
+// resetUnitTestRootLogger() here as being more descriptive and
+// trying to avoid confusion.
+PyObject*
+resetUnitTestRootLogger(PyObject*, PyObject*) {
+    try {
+        isc::log::resetUnitTestRootLogger();
+    }
+    catch (const std::exception& e) {
+        PyErr_SetString(PyExc_RuntimeError, e.what());
+        return (NULL);
+    }
+    catch (...) {
+        PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
+        return (NULL);
+    }
+    Py_RETURN_NONE;
+}
+
 PyObject*
 logConfigUpdate(PyObject*, PyObject* args) {
     // we have no wrappers for ElementPtr and ConfigData,
@@ -246,6 +268,12 @@ PyMethodDef methods[] = {
         "logging severity (one of 'DEBUG', 'INFO', 'WARN', 'ERROR' or "
         "'FATAL'), a debug level (integer in the range 0-99) and a file name "
         "of a dictionary with message text translations."},
+    {"resetUnitTestRootLogger", resetUnitTestRootLogger, METH_VARARGS,
+        "Resets the configuration of the root logger to that set by the "
+        "B10_XXX environment variables.  It is aimed at unit tests, where "
+        "the logging is initialized by the code under test; called before "
+        "the unit test starts, this function resets the logging configuration "
+        "to that in use for the C++ unit tests."},
     {"log_config_update", logConfigUpdate, METH_VARARGS,
         "Update logger settings. This method is automatically used when "
         "ModuleCCSession is initialized with handle_logging_config set "
diff --git a/src/lib/server_common/tests/keyring_test.cc b/src/lib/server_common/tests/keyring_test.cc
index d79b541..dab43df 100644
--- a/src/lib/server_common/tests/keyring_test.cc
+++ b/src/lib/server_common/tests/keyring_test.cc
@@ -38,7 +38,8 @@ public:
         specfile(std::string(TEST_DATA_PATH) + "/spec.spec")
     {
         session.getMessages()->add(createAnswer());
-        mccs.reset(new ModuleCCSession(specfile, session, NULL, NULL, false));
+        mccs.reset(new ModuleCCSession(specfile, session, NULL, NULL,
+                                       false, false));
     }
     isc::cc::FakeSession session;
     std::auto_ptr<ModuleCCSession> mccs;
diff --git a/src/lib/util/python/pycppwrapper_util.h b/src/lib/util/python/pycppwrapper_util.h
index fd55c19..3f396e2 100644
--- a/src/lib/util/python/pycppwrapper_util.h
+++ b/src/lib/util/python/pycppwrapper_util.h
@@ -94,6 +94,22 @@ public:
 /// the reference to be decreased, the original bare pointer should be
 /// extracted using the \c release() method.
 ///
+/// In some other cases, it would be convenient if it's possible to create
+/// an "empty" container and reset it with a Python object later.
+/// For example, we may want to create a temporary Python object in the
+/// middle of a function and make sure that it's valid within the rest of
+/// the function scope, while we want to make sure its reference is released
+/// when the function returns (either normally or as a result of exception).
+/// To allow this scenario, this class defines the default constructor
+/// and the \c reset() method.  The default constructor allows the class
+/// object with an "empty" (NULL) Python object, while \c reset() allows
+/// the stored object to be replaced with a new one.  If there's a valid
+/// object was already set, \c reset() releases its reference.
+/// In general, it's safer to construct the container object with a valid
+/// Python object pointer.  The use of the default constructor and
+/// \c reset() should therefore be restricted to cases where it's
+/// absolutely necessary.
+///
 /// There are two convenience methods for commonly used operations:
 /// \c installAsClassVariable() to add the PyObject as a class variable
 /// and \c installToModule to add the PyObject to a specified python module.
@@ -166,16 +182,27 @@ public:
 /// exception in a python biding written in C/C++.  See the code comment
 /// of the method for more details.
 struct PyObjectContainer {
+    PyObjectContainer() : obj_(NULL) {}
     PyObjectContainer(PyObject* obj) : obj_(obj) {
         if (obj_ == NULL) {
             isc_throw(PyCPPWrapperException, "Unexpected NULL PyObject, "
                       "probably due to short memory");
         }
     }
-    virtual ~PyObjectContainer() {
+    ~PyObjectContainer() {
+        if (obj_ != NULL) {
+            Py_DECREF(obj_);
+        }
+    }
+    void reset(PyObject* obj) {
+        if (obj == NULL) {
+            isc_throw(PyCPPWrapperException, "Unexpected NULL PyObject, "
+                      "probably due to short memory");
+        }
         if (obj_ != NULL) {
             Py_DECREF(obj_);
         }
+        obj_ = obj;
     }
     PyObject* get() {
         return (obj_);
diff --git a/src/lib/util/python/wrapper_template.cc b/src/lib/util/python/wrapper_template.cc
index 691e4bf..a703731 100644
--- a/src/lib/util/python/wrapper_template.cc
+++ b/src/lib/util/python/wrapper_template.cc
@@ -210,7 +210,7 @@ namespace python {
 // Most of the functions are not actually implemented and NULL here.
 PyTypeObject @cppclass at _type = {
     PyVarObject_HEAD_INIT(NULL, 0)
-    "pydnspp. at CPPCLASS@",
+    "@MODULE at .@CPPCLASS@",
     sizeof(s_ at CPPCLASS@),                 // tp_basicsize
     0,                                  // tp_itemsize
     reinterpret_cast<destructor>(@CPPCLASS at _destroy),       // tp_dealloc
@@ -222,7 +222,7 @@ PyTypeObject @cppclass at _type = {
     NULL,                               // tp_as_number
     NULL,                               // tp_as_sequence
     NULL,                               // tp_as_mapping
-    NULL,                               // tp_hash 
+    NULL,                               // tp_hash
     NULL,                               // tp_call
     // THIS MAY HAVE TO BE CHANGED TO NULL:
     @CPPCLASS at _str,                       // tp_str




More information about the bind10-changes mailing list