BIND 10 master, updated. 78502c021478d97672232015b7df06a7d52e531b [master] Merge branch 'trac914' with fixing conflicts in src/bin/xfrin/tests/xfrin_test.py Also made a couple of cleanups to prevent disruption: - initialize s_Name::position in constructor - add tsigctx_mock.py to EXTRA_DIST

BIND 10 source code commits bind10-changes at lists.isc.org
Wed May 18 18:46:16 UTC 2011


The branch, master has been updated
       via  78502c021478d97672232015b7df06a7d52e531b (commit)
       via  0555ab65d0e43d03b2d40c95d833dd050eea6c23 (commit)
       via  23d63caf0cc09f19a01794458bf2457a67caac05 (commit)
       via  031eda633fe45496f47fad9d4b656a0100144f75 (commit)
       via  58a5aabf65705e4107cc59ad25c76bbbcedc52bf (commit)
       via  43f5888605c770c012421a420766b01e1ad8a96b (commit)
       via  141bf465a7b13f1e4c92a76ca2df208c8d375385 (commit)
       via  7a87432e627715d9062367e2321427a4510d5822 (commit)
       via  ac2e283bd92dcc5af494938d6cfed82e4074abde (commit)
       via  03fd43339b3ffd2537a1621d628d504cee13c9d5 (commit)
       via  6ed8870ee7ac32e786030403de4423b8d7647679 (commit)
       via  7a6f36fc9073def2a531a4090b97d223d5a5c69d (commit)
       via  3bb68553bb39502b749265542c5b6d36ad80e32d (commit)
       via  ade0b1a890f1fe21c075d4fef332e3e35407f086 (commit)
       via  ff20711233b8bd6a5df5a896c0d2855222291a9e (commit)
       via  3e8ef1595dd2c7095540b22cca77e69991ad8ee1 (commit)
       via  c6827475447b07375cfdd2902c08519cd1cc9dde (commit)
       via  04611df9f5e34b2a8d6949b5b00d25a065c7c920 (commit)
       via  76a1d7e547e9eecdc4aecfa3cf6cc4ba940ad725 (commit)
       via  82ead9ea724a5482c44e8a6235bcd4634eacce2c (commit)
       via  4a78ffbf8a2b9d1171415d7f0af1b7adc4e53481 (commit)
      from  080db7e6245ebf63f0b3104e92b99142c87fe291 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 78502c021478d97672232015b7df06a7d52e531b
Merge: 0555ab6 58a5aab
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Wed May 18 11:44:49 2011 -0700

    [master] Merge branch 'trac914'
    with fixing conflicts in src/bin/xfrin/tests/xfrin_test.py
    Also made a couple of cleanups to prevent disruption:
     - initialize s_Name::position in constructor
     - add tsigctx_mock.py to EXTRA_DIST

commit 0555ab65d0e43d03b2d40c95d833dd050eea6c23
Merge: 080db7e 23d63ca
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Wed May 18 11:10:40 2011 -0700

    [master] Merge branch 'trac915'
    with fixing (trivial) conflicts in:
    	src/lib/dns/python/Makefile.am
    	src/lib/dns/python/tsigerror_python.cc
    Also moved tsigerror_python_inc.cc to EXTRA_DIST because it doesn't have
    to be compiled separately.

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

Summary of changes:
 configure.ac                                       |    1 +
 src/bin/xfrin/tests/xfrin_test.py                  |  182 ++++++-
 src/bin/xfrin/xfrin.py.in                          |   29 +-
 src/lib/dns/python/Makefile.am                     |   16 +-
 src/lib/dns/python/edns_python.cc                  |    6 +-
 src/lib/dns/python/message_python.cc               |   36 ++-
 src/lib/dns/python/messagerenderer_python.cc       |  189 ++++----
 ...tsigerror_python.h => messagerenderer_python.h} |   28 +-
 src/lib/dns/python/name_python.cc                  |  479 +++++++++---------
 src/lib/dns/python/name_python.h                   |   84 +++
 src/lib/dns/python/pydnspp.cc                      |   24 +-
 src/lib/dns/python/pydnspp_towire.h                |  127 +++++
 src/lib/dns/python/question_python.cc              |    4 +-
 src/lib/dns/python/rrset_python.cc                 |    8 +-
 src/lib/dns/python/tests/Makefile.am               |    4 +-
 src/lib/dns/python/tests/message_python_test.py    |   49 ++-
 src/lib/dns/python/tests/tsig_python_test.py       |  535 +++++++++++++++++++-
 ...ig_python_test.py => tsig_rdata_python_test.py} |   19 +-
 src/lib/dns/python/tests/tsigrecord_python_test.py |   44 ++
 src/lib/dns/python/tsig_python.cc                  |  310 ++++++++++--
 .../python/{tsigerror_python.h => tsig_python.h}   |   21 +-
 .../python/tsig_rdata_python.cc}                   |  262 ++++++----
 .../{tsigerror_python.h => tsig_rdata_python.h}    |   31 +-
 src/lib/dns/python/tsigerror_python.cc             |   10 +-
 src/lib/dns/python/tsigerror_python.h              |    8 +
 src/lib/dns/python/tsigkey_python.cc               |  378 ++++++++-------
 .../{tsigerror_python.h => tsigkey_python.h}       |   27 +-
 .../python/tsigrecord_python.cc}                   |  218 +++++----
 .../{tsigerror_python.h => tsigrecord_python.h}    |   27 +-
 src/lib/dns/rdata/any_255/tsig_250.cc              |    2 +-
 src/lib/python/isc/testutils/Makefile.am           |    2 +-
 src/lib/python/isc/testutils/tsigctx_mock.py       |   53 ++
 src/lib/util/Makefile.am                           |    2 +-
 src/lib/util/python/wrapper_template.cc            |   18 +-
 src/lib/util/python/wrapper_template.h             |   15 +
 src/lib/util/pyunittests/Makefile.am               |   14 +
 src/lib/util/pyunittests/pyunittests_util.cc       |   84 +++
 37 files changed, 2482 insertions(+), 864 deletions(-)
 copy src/lib/dns/python/{tsigerror_python.h => messagerenderer_python.h} (59%)
 create mode 100644 src/lib/dns/python/name_python.h
 create mode 100644 src/lib/dns/python/pydnspp_towire.h
 copy src/lib/dns/python/tests/{tsig_python_test.py => tsig_rdata_python_test.py} (63%)
 create mode 100644 src/lib/dns/python/tests/tsigrecord_python_test.py
 copy src/lib/dns/python/{tsigerror_python.h => tsig_python.h} (74%)
 copy src/lib/{util/python/wrapper_template.cc => dns/python/tsig_rdata_python.cc} (52%)
 copy src/lib/dns/python/{tsigerror_python.h => tsig_rdata_python.h} (57%)
 copy src/lib/dns/python/{tsigerror_python.h => tsigkey_python.h} (69%)
 copy src/lib/{util/python/wrapper_template.cc => dns/python/tsigrecord_python.cc} (57%)
 copy src/lib/dns/python/{tsigerror_python.h => tsigrecord_python.h} (57%)
 create mode 100644 src/lib/python/isc/testutils/tsigctx_mock.py
 create mode 100644 src/lib/util/pyunittests/Makefile.am
 create mode 100644 src/lib/util/pyunittests/pyunittests_util.cc

-----------------------------------------------------------------------
diff --git a/configure.ac b/configure.ac
index d478924..999639d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -777,6 +777,7 @@ AC_CONFIG_FILES([Makefile
                  src/lib/util/io/Makefile
                  src/lib/util/io/tests/Makefile
                  src/lib/util/unittests/Makefile
+                 src/lib/util/pyunittests/Makefile
                  src/lib/util/tests/Makefile
                  tests/Makefile
                  tests/system/Makefile
diff --git a/src/bin/xfrin/tests/xfrin_test.py b/src/bin/xfrin/tests/xfrin_test.py
index e5c4fcb..a967996 100644
--- a/src/bin/xfrin/tests/xfrin_test.py
+++ b/src/bin/xfrin/tests/xfrin_test.py
@@ -15,6 +15,7 @@
 
 import unittest
 import socket
+from isc.testutils.tsigctx_mock import MockTSIGContext
 from xfrin import *
 
 #
@@ -55,13 +56,6 @@ default_answers = [soa_rrset]
 class XfrinTestException(Exception):
     pass
 
-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 MockCC():
     def get_default_value(self, identifier):
         if identifier == "zones/master_port":
@@ -151,10 +145,11 @@ class MockXfrinConnection(XfrinConnection):
                 self.response_generator()
         return len(data)
 
-    def create_response_data(self, response = True, bad_qid = False,
-                             rcode = Rcode.NOERROR(),
-                             questions = default_questions,
-                             answers = default_answers):
+    def create_response_data(self, response=True, bad_qid=False,
+                             rcode=Rcode.NOERROR(),
+                             questions=default_questions,
+                             answers=default_answers,
+                             tsig=False):
         resp = Message(Message.RENDER)
         qid = self.qid
         if bad_qid:
@@ -168,7 +163,13 @@ class MockXfrinConnection(XfrinConnection):
         [resp.add_rrset(Message.SECTION_ANSWER, a) for a in answers]
 
         renderer = MessageRenderer()
-        resp.to_wire(renderer)
+        if tsig:
+            # for now, we don't need a valid SIG.  We only need to include
+            # TSIG RR.  So how to add it and which key is used don't matter.
+            tsig_ctx = TSIGContext(TSIG_KEY)
+            resp.to_wire(renderer, tsig_ctx)
+        else:
+            resp.to_wire(renderer)
         reply_data = struct.pack('H', socket.htons(renderer.get_length()))
         reply_data += renderer.get_data()
 
@@ -183,14 +184,18 @@ class TestXfrinConnection(unittest.TestCase):
                                         TEST_RRCLASS, TEST_DB_FILE,
                                         threading.Event(),
                                         TEST_MASTER_IPV4_ADDRINFO)
-        self.axfr_after_soa = False
         self.soa_response_params = {
             'questions': [example_soa_question],
             'bad_qid': False,
             'response': True,
             'rcode': Rcode.NOERROR(),
+            'tsig': False,
             'axfr_after_soa': self._create_normal_response_data
             }
+        self.axfr_response_params = {
+            'tsig_1st': False,
+            'tsig_2nd': False
+            }
 
     def tearDown(self):
         self.conn.close()
@@ -236,6 +241,15 @@ class TestXfrinConnection(unittest.TestCase):
             query_question = Question(Name("example.com."), RRClass.IN(), query_type)
             msg.add_question(query_question)
             return msg
+
+        def message_has_tsig(data):
+            # a simple check if the actual data contains a TSIG RR.
+            # At our level this simple check should suffice; other detailed
+            # tests regarding the TSIG protocol are done in pydnspp.
+            msg = Message(Message.PARSE)
+            msg.from_wire(data)
+            return msg.get_tsig_record() is not None
+
         self.conn._create_query = create_msg
         # soa request
         self.conn._send_query(RRType.SOA())
@@ -245,22 +259,20 @@ class TestXfrinConnection(unittest.TestCase):
         self.assertEqual(self.conn.query_data, b'\x00\x1d\x105\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x07example\x03com\x00\x00\xfc\x00\x01')
 
         # soa request with tsig
-        self.conn._tsig_ctx = TSIGContext(TSIG_KEY)
+        self.conn._tsig_ctx = MockTSIGContext(TSIG_KEY)
         self.conn._send_query(RRType.SOA())
-        tsig_soa_data = strip_mutable_tsig_data(self.conn.query_data)
-        self.assertEqual(tsig_soa_data, b'\x00n\x105\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x07example\x03com\x00\x00\x06\x00\x01\x07example\x03com\x00\x00\xfa\x00\xff\x00\x00\x00\x00\x00:\x08hmac-md5\x07sig-alg\x03reg\x03int\x00\x01,\x00\x10\x105\x00\x00\x00\x00')
+        self.assertTrue(message_has_tsig(self.conn.query_data[2:]))
 
         # axfr request with tsig
         self.conn._send_query(RRType.AXFR())
-        tsig_axfr_data = strip_mutable_tsig_data(self.conn.query_data)
-        self.assertEqual(tsig_axfr_data, b'\x00n\x105\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x07example\x03com\x00\x00\xfc\x00\x01\x07example\x03com\x00\x00\xfa\x00\xff\x00\x00\x00\x00\x00:\x08hmac-md5\x07sig-alg\x03reg\x03int\x00\x01,\x00\x10\x105\x00\x00\x00\x00')
+        self.assertTrue(message_has_tsig(self.conn.query_data[2:]))
 
     def test_response_with_invalid_msg(self):
         self.conn.reply_data = b'aaaxxxx'
         self.assertRaises(XfrinTestException, self._handle_xfrin_response)
 
-    def test_response_with_tsig(self):
-        self.conn._tsig_ctx = TSIGContext(TSIG_KEY)
+    def test_response_with_tsigfail(self):
+        self.conn._tsig_ctx = MockTSIGContext(TSIG_KEY)
         # server tsig check fail, return with RCODE 9 (NOTAUTH)
         self.conn._send_query(RRType.SOA())
         self.conn.reply_data = self.conn.create_response_data(rcode=Rcode.NOTAUTH())
@@ -330,6 +342,52 @@ class TestXfrinConnection(unittest.TestCase):
         self.conn.response_generator = self._create_soa_response_data
         self.assertRaises(XfrinException, self.conn._check_soa_serial)
 
+    def test_soacheck_with_tsig(self):
+        self.conn._tsig_ctx = MockTSIGContext(TSIG_KEY)
+        self.conn.response_generator = self._create_soa_response_data
+        # emulate a validly signed response
+        self.conn._tsig_ctx.error = TSIGError.NOERROR
+        self.assertEqual(self.conn._check_soa_serial(), XFRIN_OK)
+        self.assertEqual(self.conn._tsig_ctx.get_error(), TSIGError.NOERROR)
+
+    def test_soacheck_with_tsig_notauth(self):
+        self.conn._tsig_ctx = MockTSIGContext(TSIG_KEY)
+
+        # emulate a valid error response
+        self.soa_response_params['rcode'] = Rcode.NOTAUTH()
+        self.conn.response_generator = self._create_soa_response_data
+        self.conn._tsig_ctx.error = TSIGError.BAD_SIG
+
+        self.assertRaises(XfrinException, self.conn._check_soa_serial)
+
+    def test_soacheck_with_tsig_noerror_badsig(self):
+        self.conn._tsig_ctx = MockTSIGContext(TSIG_KEY)
+
+        # emulate a normal response bad verification failure due to BADSIG.
+        # According RFC2845, in this case we should ignore it and keep
+        # waiting for a valid response until a timeout.  But we immediately
+        # treat this as a final failure (just as BIND 9 does).
+        self.conn.response_generator = self._create_soa_response_data
+        self.conn._tsig_ctx.error = TSIGError.BAD_SIG
+
+        self.assertRaises(XfrinException, self.conn._check_soa_serial)
+
+    def test_soacheck_with_tsig_unsigned_response(self):
+        # we can use a real TSIGContext for this.  the response doesn't
+        # contain a TSIG while we sent a signed query.  RFC2845 states
+        # we should wait for a valid response in this case, but we treat
+        # it as a fatal transaction failure, too.
+        self.conn._tsig_ctx = TSIGContext(TSIG_KEY)
+        self.conn.response_generator = self._create_soa_response_data
+        self.assertRaises(XfrinException, self.conn._check_soa_serial)
+
+    def test_soacheck_with_unexpected_tsig_response(self):
+        # we reject unexpected TSIG in responses (following BIND 9's
+        # behavior)
+        self.soa_response_params['tsig'] = True
+        self.conn.response_generator = self._create_soa_response_data
+        self.assertRaises(XfrinException, self.conn._check_soa_serial)
+
     def test_response_shutdown(self):
         self.conn.response_generator = self._create_normal_response_data
         self.conn._shutdown_event.set()
@@ -363,6 +421,81 @@ class TestXfrinConnection(unittest.TestCase):
         self.conn.response_generator = self._create_normal_response_data
         self.assertEqual(self.conn.do_xfrin(False), XFRIN_OK)
 
+    def test_do_xfrin_with_tsig(self):
+        # use TSIG with a mock context.  we fake all verify results to
+        # emulate successful verification.
+        self.conn._tsig_ctx = MockTSIGContext(TSIG_KEY)
+        self.conn._tsig_ctx.error = TSIGError.NOERROR
+        self.conn.response_generator = self._create_normal_response_data
+        self.assertEqual(self.conn.do_xfrin(False), XFRIN_OK)
+        # We use two messages in the tests.  The same context should have been
+        # usef for both.
+        self.assertEqual(2, self.conn._tsig_ctx.verify_called)
+
+    def test_do_xfrin_with_tsig_fail(self):
+        # TSIG verify will fail for the first message.  xfrin should fail
+        # immediately.
+        self.conn._tsig_ctx = MockTSIGContext(TSIG_KEY)
+        self.conn._tsig_ctx.error = TSIGError.BAD_SIG
+        self.conn.response_generator = self._create_normal_response_data
+        self.assertEqual(self.conn.do_xfrin(False), XFRIN_FAIL)
+        self.assertEqual(1, self.conn._tsig_ctx.verify_called)
+
+    def test_do_xfrin_with_tsig_fail_for_second_message(self):
+        # Similar to the previous test, but first verify succeeds.  There
+        # should be a second verify attempt, which will fail, which should
+        # make xfrin fail.
+        def fake_tsig_error(ctx):
+            if self.conn._tsig_ctx.verify_called == 1:
+                return TSIGError.NOERROR
+            return TSIGError.BAD_SIG
+        self.conn._tsig_ctx = MockTSIGContext(TSIG_KEY)
+        self.conn._tsig_ctx.error = fake_tsig_error
+        self.conn.response_generator = self._create_normal_response_data
+        self.assertEqual(self.conn.do_xfrin(False), XFRIN_FAIL)
+        self.assertEqual(2, self.conn._tsig_ctx.verify_called)
+
+    def test_do_xfrin_with_missing_tsig(self):
+        # XFR request sent with TSIG, but the response doesn't have TSIG.
+        # xfr should fail.
+        self.conn._tsig_ctx = MockTSIGContext(TSIG_KEY)
+        self.conn.response_generator = self._create_normal_response_data
+        self.assertEqual(self.conn.do_xfrin(False), XFRIN_FAIL)
+        self.assertEqual(1, self.conn._tsig_ctx.verify_called)
+
+    def test_do_xfrin_with_missing_tsig_for_second_message(self):
+        # Similar to the previous test, but firt one contains TSIG and verify
+        # succeeds (due to fake).  The second message lacks TSIG.
+        #
+        # Note: this test case is actually not that trivial:  Skipping
+        # intermediate TSIG is allowed.  In this case, however, the second
+        # message is the last one, which must contain TSIG anyway, so the
+        # expected result is correct.  If/when we support skipping
+        # intermediate TSIGs, we'll need additional test cases.
+        def fake_tsig_error(ctx):
+            if self.conn._tsig_ctx.verify_called == 1:
+                return TSIGError.NOERROR
+            return TSIGError.FORMERR
+        self.conn._tsig_ctx = MockTSIGContext(TSIG_KEY)
+        self.conn._tsig_ctx.error = fake_tsig_error
+        self.conn.response_generator = self._create_normal_response_data
+        self.assertEqual(self.conn.do_xfrin(False), XFRIN_FAIL)
+        self.assertEqual(2, self.conn._tsig_ctx.verify_called)
+
+    def test_do_xfrin_with_unexpected_tsig(self):
+        # XFR request wasn't signed, but response includes TSIG.  Like BIND 9,
+        # we reject that.
+        self.axfr_response_params['tsig_1st'] = True
+        self.conn.response_generator = self._create_normal_response_data
+        self.assertEqual(self.conn.do_xfrin(False), XFRIN_FAIL)
+
+    def test_do_xfrin_with_unexpected_tsig_for_second_message(self):
+        # similar to the previous test, but the first message is normal.
+        # the second one contains an unexpected TSIG.  should be rejected.
+        self.axfr_response_params['tsig_2nd'] = True
+        self.conn.response_generator = self._create_normal_response_data
+        self.assertEqual(self.conn.do_xfrin(False), XFRIN_FAIL)
+
     def test_do_xfrin_empty_response(self):
         # skipping the creation of response data, so the transfer will fail.
         self.assertEqual(self.conn.do_xfrin(False), XFRIN_FAIL)
@@ -408,8 +541,10 @@ class TestXfrinConnection(unittest.TestCase):
         # This helper method creates a simple sequence of DNS messages that
         # forms a valid XFR transaction.  It consists of two messages, each
         # containing just a single SOA RR.
-        self.conn.reply_data = self.conn.create_response_data()
-        self.conn.reply_data += self.conn.create_response_data()
+        tsig_1st = self.axfr_response_params['tsig_1st']
+        tsig_2nd = self.axfr_response_params['tsig_2nd']
+        self.conn.reply_data = self.conn.create_response_data(tsig=tsig_1st)
+        self.conn.reply_data += self.conn.create_response_data(tsig=tsig_2nd)
 
     def _create_soa_response_data(self):
         # This helper method creates a DNS message that is supposed to be
@@ -420,7 +555,8 @@ class TestXfrinConnection(unittest.TestCase):
             bad_qid=self.soa_response_params['bad_qid'],
             response=self.soa_response_params['response'],
             rcode=self.soa_response_params['rcode'],
-            questions=self.soa_response_params['questions'])
+            questions=self.soa_response_params['questions'],
+            tsig=self.soa_response_params['tsig'])
         if self.soa_response_params['axfr_after_soa'] != None:
             self.conn.response_generator = self.soa_response_params['axfr_after_soa']
 
diff --git a/src/bin/xfrin/xfrin.py.in b/src/bin/xfrin/xfrin.py.in
index 257d0fa..8fb102f 100755
--- a/src/bin/xfrin/xfrin.py.in
+++ b/src/bin/xfrin/xfrin.py.in
@@ -207,6 +207,22 @@ class XfrinConnection(asyncore.dispatcher):
 
         return data
 
+    def _check_response_tsig(self, msg, response_data):
+        tsig_record = msg.get_tsig_record()
+        if self._tsig_ctx is not None:
+            tsig_error = self._tsig_ctx.verify(tsig_record, response_data)
+            if tsig_error != TSIGError.NOERROR:
+                raise XfrinException('TSIG verify fail: %s' % str(tsig_error))
+        elif tsig_record is not None:
+            # If the response includes a TSIG while we didn't sign the query,
+            # we treat it as an error.  RFC doesn't say anything about this
+            # case, but it clearly states the server must not sign a response
+            # to an unsigned request.  Although we could be flexible, no sane
+            # implementation would return such a response, and since this is
+            # part of security mechanism, it's probably better to be more
+            # strict.
+            raise XfrinException('Unexpected TSIG in response')
+
     def _check_soa_serial(self):
         ''' Compare the soa serial, if soa serial in master is less than
         the soa serial in local, Finish xfrin.
@@ -214,7 +230,7 @@ class XfrinConnection(asyncore.dispatcher):
         True: soa serial in master is bigger
         '''
 
-        self._send_query(RRType("SOA"))
+        self._send_query(RRType.SOA())
         data_len = self._get_request_response(2)
         msg_len = socket.htons(struct.unpack('H', data_len)[0])
         soa_response = self._get_request_response(msg_len)
@@ -225,6 +241,9 @@ class XfrinConnection(asyncore.dispatcher):
         # strict we should be (see the comment in _check_response_header())
         self._check_response_header(msg)
 
+        # TSIG related checks, including an unexpected signed response
+        self._check_response_tsig(msg, soa_response)
+
         # TODO, need select soa record from data source then compare the two
         # serial, current just return OK, since this function hasn't been used
         # now.
@@ -242,8 +261,7 @@ class XfrinConnection(asyncore.dispatcher):
             logstr = 'transfer of \'%s\': AXFR ' % self._zone_name
             if ret == XFRIN_OK:
                 self.log_msg(logstr + 'started')
-                # TODO: .AXFR() RRType.AXFR()
-                self._send_query(RRType(252))
+                self._send_query(RRType.AXFR())
                 isc.datasrc.sqlite3_ds.load(self._db_file, self._zone_name,
                                             self._handle_xfrin_response)
 
@@ -314,7 +332,7 @@ class XfrinConnection(asyncore.dispatcher):
 
             for rdata in rrset.get_rdata():
                 # Count the soa record count
-                if rrset.get_type() == RRType("SOA"):
+                if rrset.get_type() == RRType.SOA():
                     self._soa_rr_count += 1
 
                     # XXX: the current DNS message parser can't preserve the
@@ -340,6 +358,9 @@ class XfrinConnection(asyncore.dispatcher):
             msg.from_wire(recvdata)
             self._check_response_status(msg)
 
+            # TSIG related checks, including an unexpected signed response
+            self._check_response_tsig(msg, recvdata)
+
             answer_section = msg.get_section(Message.SECTION_ANSWER)
             for rr in self._handle_answer_section(answer_section):
                 yield rr
diff --git a/src/lib/dns/python/Makefile.am b/src/lib/dns/python/Makefile.am
index 1c9afc7..aa9d062 100644
--- a/src/lib/dns/python/Makefile.am
+++ b/src/lib/dns/python/Makefile.am
@@ -5,10 +5,16 @@ AM_CPPFLAGS += $(BOOST_INCLUDES)
 AM_CXXFLAGS = $(B10_CXXFLAGS)
 
 pyexec_LTLIBRARIES = pydnspp.la
-pydnspp_la_SOURCES = pydnspp.cc pydnspp_common.cc
+pydnspp_la_SOURCES = pydnspp.cc pydnspp_common.cc pydnspp_towire.h
+pydnspp_la_SOURCES += name_python.cc name_python.h
+pydnspp_la_SOURCES += messagerenderer_python.cc messagerenderer_python.h
 pydnspp_la_SOURCES += rcode_python.cc rcode_python.h
+pydnspp_la_SOURCES += tsigkey_python.cc tsigkey_python.h
 pydnspp_la_SOURCES += tsigerror_python.cc tsigerror_python.h
-pydnspp_la_SOURCES += tsigerror_python_inc.cc
+pydnspp_la_SOURCES += tsig_rdata_python.cc tsig_rdata_python.h
+pydnspp_la_SOURCES += tsigrecord_python.cc tsigrecord_python.h
+pydnspp_la_SOURCES += tsig_python.cc tsig_python.h
+
 pydnspp_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES)
 pydnspp_la_LDFLAGS = $(PYTHON_LDFLAGS)
 
@@ -16,19 +22,15 @@ pydnspp_la_LDFLAGS = $(PYTHON_LDFLAGS)
 # rules
 EXTRA_DIST = pydnspp_common.h
 EXTRA_DIST += edns_python.cc
-EXTRA_DIST += messagerenderer_python.cc
 EXTRA_DIST += message_python.cc
 EXTRA_DIST += rrclass_python.cc
-EXTRA_DIST += name_python.cc
 EXTRA_DIST += opcode_python.cc
-EXTRA_DIST += rcode_python.cc
 EXTRA_DIST += rrset_python.cc
 EXTRA_DIST += question_python.cc
 EXTRA_DIST += rrttl_python.cc
 EXTRA_DIST += rdata_python.cc
 EXTRA_DIST += rrtype_python.cc
-EXTRA_DIST += tsigkey_python.cc
-EXTRA_DIST += tsig_python.cc
+EXTRA_DIST += tsigerror_python_inc.cc
 
 # Python prefers .so, while some OSes (specifically MacOS) use a different
 # suffix for dynamic objects.  -module is necessary to work this around.
diff --git a/src/lib/dns/python/edns_python.cc b/src/lib/dns/python/edns_python.cc
index d781e89..83c3bfa 100644
--- a/src/lib/dns/python/edns_python.cc
+++ b/src/lib/dns/python/edns_python.cc
@@ -108,7 +108,7 @@ PyMethodDef EDNS_methods[] = {
 // Most of the functions are not actually implemented and NULL here.
 PyTypeObject edns_type = {
     PyVarObject_HEAD_INIT(NULL, 0)
-    "libdns_python.EDNS",
+    "pydnspp.EDNS",
     sizeof(s_EDNS),                     // tp_basicsize
     0,                                  // tp_itemsize
     (destructor)EDNS_destroy,           // tp_dealloc
@@ -203,7 +203,7 @@ EDNS_init(s_EDNS* self, PyObject* args) {
         // in this context so that we can share the try-catch logic with
         // EDNS_createFromRR() (see below).
         uint8_t extended_rcode;
-        self->edns = createFromRR(*name->name, *rrclass->rrclass,
+        self->edns = createFromRR(*name->cppobj, *rrclass->rrclass,
                                   *rrtype->rrtype, *rrttl->rrttl,
                                   *rdata->rdata, extended_rcode);
         return (self->edns != NULL ? 0 : -1);
@@ -334,7 +334,7 @@ EDNS_createFromRR(const s_EDNS* null_self, PyObject* args) {
             return (NULL);
         }
 
-        edns_obj->edns = createFromRR(*name->name, *rrclass->rrclass,
+        edns_obj->edns = createFromRR(*name->cppobj, *rrclass->rrclass,
                                       *rrtype->rrtype, *rrttl->rrttl,
                                       *rdata->rdata, extended_rcode);
         if (edns_obj->edns != NULL) {
diff --git a/src/lib/dns/python/message_python.cc b/src/lib/dns/python/message_python.cc
index 381b7bc..9744aab 100644
--- a/src/lib/dns/python/message_python.cc
+++ b/src/lib/dns/python/message_python.cc
@@ -14,6 +14,8 @@
 
 #include <exceptions/exceptions.h>
 #include <dns/message.h>
+#include <dns/rcode.h>
+
 using namespace isc::dns;
 using namespace isc::util;
 
@@ -65,6 +67,7 @@ PyObject* Message_getOpcode(s_Message* self);
 PyObject* Message_setOpcode(s_Message* self, PyObject* args);
 PyObject* Message_getEDNS(s_Message* self);
 PyObject* Message_setEDNS(s_Message* self, PyObject* args);
+PyObject* Message_getTSIGRecord(s_Message* self);
 PyObject* Message_getRRCount(s_Message* self, PyObject* args);
 // use direct iterators for these? (or simply lists for now?)
 PyObject* Message_getQuestion(s_Message* self);
@@ -123,6 +126,11 @@ PyMethodDef Message_methods[] = {
     { "set_edns", reinterpret_cast<PyCFunction>(Message_setEDNS), METH_VARARGS,
       "Set EDNS for the message."
     },
+    { "get_tsig_record",
+      reinterpret_cast<PyCFunction>(Message_getTSIGRecord), METH_NOARGS,
+      "Return, if any, the TSIG record contained in the received message. "
+      "If no TSIG RR is set in the message, None will be returned."
+    },
     { "get_rr_count", reinterpret_cast<PyCFunction>(Message_getRRCount), METH_VARARGS,
       "Returns the number of RRs contained in the given section." },
     { "get_question", reinterpret_cast<PyCFunction>(Message_getQuestion), METH_NOARGS,
@@ -442,6 +450,29 @@ Message_setEDNS(s_Message* self, PyObject* args) {
 }
 
 PyObject*
+Message_getTSIGRecord(s_Message* self) {
+    try {
+        const TSIGRecord* tsig_record = self->message->getTSIGRecord();
+
+        if (tsig_record == NULL) {
+            Py_RETURN_NONE;
+        }
+        return (createTSIGRecordObject(*tsig_record));
+    } catch (const InvalidMessageOperation& ex) {
+        PyErr_SetString(po_InvalidMessageOperation, ex.what());
+    } catch (const exception& ex) {
+        const string ex_what =
+            "Unexpected failure in getting TSIGRecord from message: " +
+            string(ex.what());
+        PyErr_SetString(po_IscException, ex_what.c_str());
+    } catch (...) {
+        PyErr_SetString(PyExc_SystemError, "Unexpected failure in "
+                        "getting TSIGRecord from message");
+    }
+    return (NULL);
+}
+
+PyObject*
 Message_getRRCount(s_Message* self, PyObject* args) {
     unsigned int section;
     if (!PyArg_ParseTuple(args, "I", &section)) {
@@ -652,13 +683,12 @@ Message_toWire(s_Message* self, PyObject* args) {
     s_TSIGContext* tsig_ctx = NULL;
     
     if (PyArg_ParseTuple(args, "O!|O!", &messagerenderer_type, &mr,
-                         &tsig_context_type, &tsig_ctx)) {
+                         &tsigcontext_type, &tsig_ctx)) {
         try {
             if (tsig_ctx == NULL) {
                 self->message->toWire(*mr->messagerenderer);
             } else {
-                self->message->toWire(*mr->messagerenderer,
-                                      *tsig_ctx->tsig_ctx);
+                self->message->toWire(*mr->messagerenderer, *tsig_ctx->cppobj);
             }
             // If we return NULL it is seen as an error, so use this for
             // None returns
diff --git a/src/lib/dns/python/messagerenderer_python.cc b/src/lib/dns/python/messagerenderer_python.cc
index 85a4f17..e6f5d3e 100644
--- a/src/lib/dns/python/messagerenderer_python.cc
+++ b/src/lib/dns/python/messagerenderer_python.cc
@@ -12,39 +12,41 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include <Python.h>
+
+#include <util/buffer.h>
+
 #include <dns/messagerenderer.h>
 
-// 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
+#include "pydnspp_common.h"
+#include "messagerenderer_python.h"
+
 using namespace isc::dns;
+using namespace isc::dns::python;
 using namespace isc::util;
 
 // MessageRenderer
 
-// since we don't use *Buffer in the python version (but work with
-// the already existing bytearray type where we use these custom buffers
-// in c++, we need to keep track of one here.
-class s_MessageRenderer : public PyObject {
-public:
-    OutputBuffer* outputbuffer;
-    MessageRenderer* messagerenderer;
-};
+s_MessageRenderer::s_MessageRenderer() : outputbuffer(NULL),
+                                         messagerenderer(NULL)
+{
+}
 
-static int MessageRenderer_init(s_MessageRenderer* self);
-static void MessageRenderer_destroy(s_MessageRenderer* self);
+namespace {
+int MessageRenderer_init(s_MessageRenderer* self);
+void MessageRenderer_destroy(s_MessageRenderer* self);
 
-static PyObject* MessageRenderer_getData(s_MessageRenderer* self);
-static PyObject* MessageRenderer_getLength(s_MessageRenderer* self);
-static PyObject* MessageRenderer_isTruncated(s_MessageRenderer* self);
-static PyObject* MessageRenderer_getLengthLimit(s_MessageRenderer* self);
-static PyObject* MessageRenderer_getCompressMode(s_MessageRenderer* self);
-static PyObject* MessageRenderer_setTruncated(s_MessageRenderer* self);
-static PyObject* MessageRenderer_setLengthLimit(s_MessageRenderer* self, PyObject* args);
-static PyObject* MessageRenderer_setCompressMode(s_MessageRenderer* self, PyObject* args);
-static PyObject* MessageRenderer_clear(s_MessageRenderer* self);
+PyObject* MessageRenderer_getData(s_MessageRenderer* self);
+PyObject* MessageRenderer_getLength(s_MessageRenderer* self);
+PyObject* MessageRenderer_isTruncated(s_MessageRenderer* self);
+PyObject* MessageRenderer_getLengthLimit(s_MessageRenderer* self);
+PyObject* MessageRenderer_getCompressMode(s_MessageRenderer* self);
+PyObject* MessageRenderer_setTruncated(s_MessageRenderer* self);
+PyObject* MessageRenderer_setLengthLimit(s_MessageRenderer* self, PyObject* args);
+PyObject* MessageRenderer_setCompressMode(s_MessageRenderer* self, PyObject* args);
+PyObject* MessageRenderer_clear(s_MessageRenderer* self);
 
-static PyMethodDef MessageRenderer_methods[] = {
+PyMethodDef MessageRenderer_methods[] = {
     { "get_data", reinterpret_cast<PyCFunction>(MessageRenderer_getData), METH_NOARGS,
       "Returns the data as a bytes() object" },
     { "get_length", reinterpret_cast<PyCFunction>(MessageRenderer_getLength), METH_NOARGS,
@@ -67,69 +69,14 @@ static PyMethodDef MessageRenderer_methods[] = {
     { NULL, NULL, 0, NULL }
 };
 
-static PyTypeObject messagerenderer_type = {
-    PyVarObject_HEAD_INIT(NULL, 0)
-    "pydnspp.MessageRenderer",
-    sizeof(s_MessageRenderer),          // tp_basicsize
-    0,                                  // tp_itemsize
-    (destructor)MessageRenderer_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
-    "The MessageRenderer class encapsulates implementation details "
-    "of rendering a DNS message into a buffer in wire format. "
-    "In effect, it's simply responsible for name compression at least in the "
-    "current implementation. A MessageRenderer class object manages the "
-    "positions of names rendered in a buffer and uses that information to render "
-    "subsequent names with compression.",
-    NULL,                               // tp_traverse
-    NULL,                               // tp_clear
-    NULL,                               // tp_richcompare
-    0,                                  // tp_weaklistoffset
-    NULL,                               // tp_iter
-    NULL,                               // tp_iternext
-    MessageRenderer_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
-    (initproc)MessageRenderer_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
-};
-
-static int
+int
 MessageRenderer_init(s_MessageRenderer* self) {
     self->outputbuffer = new OutputBuffer(4096);
     self->messagerenderer = new MessageRenderer(*self->outputbuffer);
     return (0);
 }
 
-static void
+void
 MessageRenderer_destroy(s_MessageRenderer* self) {
     delete self->messagerenderer;
     delete self->outputbuffer;
@@ -138,19 +85,19 @@ MessageRenderer_destroy(s_MessageRenderer* self) {
     Py_TYPE(self)->tp_free(self);
 }
 
-static PyObject*
+PyObject*
 MessageRenderer_getData(s_MessageRenderer* self) {
     return (Py_BuildValue("y#",
                          self->messagerenderer->getData(),
                           self->messagerenderer->getLength()));
 }
 
-static PyObject*
+PyObject*
 MessageRenderer_getLength(s_MessageRenderer* self) {
     return (Py_BuildValue("I", self->messagerenderer->getLength()));
 }
 
-static PyObject*
+PyObject*
 MessageRenderer_isTruncated(s_MessageRenderer* self) {
     if (self->messagerenderer->isTruncated()) {
         Py_RETURN_TRUE;
@@ -159,23 +106,23 @@ MessageRenderer_isTruncated(s_MessageRenderer* self) {
     }
 }
 
-static PyObject*
+PyObject*
 MessageRenderer_getLengthLimit(s_MessageRenderer* self) {
     return (Py_BuildValue("I", self->messagerenderer->getLengthLimit()));
 }
 
-static PyObject*
+PyObject*
 MessageRenderer_getCompressMode(s_MessageRenderer* self) {
     return (Py_BuildValue("I", self->messagerenderer->getCompressMode()));
 }
 
-static PyObject*
+PyObject*
 MessageRenderer_setTruncated(s_MessageRenderer* self) {
     self->messagerenderer->setTruncated();
     Py_RETURN_NONE;
 }
 
-static PyObject*
+PyObject*
 MessageRenderer_setLengthLimit(s_MessageRenderer* self,
                                PyObject* args)
 {
@@ -195,7 +142,7 @@ MessageRenderer_setLengthLimit(s_MessageRenderer* self,
     Py_RETURN_NONE;
 }
 
-static PyObject*
+PyObject*
 MessageRenderer_setCompressMode(s_MessageRenderer* self,
                                PyObject* args)
 {
@@ -220,14 +167,71 @@ MessageRenderer_setCompressMode(s_MessageRenderer* self,
     }
 }
 
-static PyObject*
+PyObject*
 MessageRenderer_clear(s_MessageRenderer* self) {
     self->messagerenderer->clear();
     Py_RETURN_NONE;
 }
+} // end of unnamed namespace
 
 // end of MessageRenderer
-
+namespace isc {
+namespace dns {
+namespace python {
+PyTypeObject messagerenderer_type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "pydnspp.MessageRenderer",
+    sizeof(s_MessageRenderer),          // tp_basicsize
+    0,                                  // tp_itemsize
+    (destructor)MessageRenderer_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
+    "The MessageRenderer class encapsulates implementation details "
+    "of rendering a DNS message into a buffer in wire format. "
+    "In effect, it's simply responsible for name compression at least in the "
+    "current implementation. A MessageRenderer class object manages the "
+    "positions of names rendered in a buffer and uses that information to render "
+    "subsequent names with compression.",
+    NULL,                               // tp_traverse
+    NULL,                               // tp_clear
+    NULL,                               // tp_richcompare
+    0,                                  // tp_weaklistoffset
+    NULL,                               // tp_iter
+    NULL,                               // tp_iternext
+    MessageRenderer_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
+    (initproc)MessageRenderer_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
+};
 
 // Module Initialization, all statics are initialized here
 bool
@@ -260,5 +264,6 @@ initModulePart_MessageRenderer(PyObject* mod) {
     
     return (true);
 }
-
-
+} // namespace python
+} // namespace dns
+} // namespace isc
diff --git a/src/lib/dns/python/messagerenderer_python.h b/src/lib/dns/python/messagerenderer_python.h
new file mode 100644
index 0000000..3bb096e
--- /dev/null
+++ b/src/lib/dns/python/messagerenderer_python.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_MESSAGERENDERER_H
+#define __PYTHON_MESSAGERENDERER_H 1
+
+#include <Python.h>
+
+namespace isc {
+namespace util {
+class OutputBuffer;
+}
+namespace dns {
+class MessageRenderer;
+
+namespace python {
+
+// The s_* Class simply covers one instantiation of the object.
+//
+// since we don't use *Buffer in the python version (but work with
+// the already existing bytearray type where we use these custom buffers
+// in C++, we need to keep track of one here.
+class s_MessageRenderer : public PyObject {
+public:
+    s_MessageRenderer();
+    isc::util::OutputBuffer* outputbuffer;
+    MessageRenderer* messagerenderer;
+};
+
+extern PyTypeObject messagerenderer_type;
+
+bool initModulePart_MessageRenderer(PyObject* mod);
+
+} // namespace python
+} // namespace dns
+} // namespace isc
+#endif // __PYTHON_MESSAGERENDERER_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/python/name_python.cc b/src/lib/dns/python/name_python.cc
index b030ba1..d00c6f7 100644
--- a/src/lib/dns/python/name_python.cc
+++ b/src/lib/dns/python/name_python.cc
@@ -12,26 +12,18 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-//
-// Declaration of the custom exceptions
-// Initialization and addition of these go in the module init at the
-// end
-//
-static PyObject* po_EmptyLabel;
-static PyObject* po_TooLongName;
-static PyObject* po_TooLongLabel;
-static PyObject* po_BadLabelType;
-static PyObject* po_BadEscape;
-static PyObject* po_IncompleteName;
-static PyObject* po_InvalidBufferPosition;
-static PyObject* po_DNSMessageFORMERR;
+#include <Python.h>
 
-//
-// Declaration of enums
-// Initialization and addition of these go in the module init at the
-// end
-//
-static PyObject* po_NameRelation;
+#include <util/buffer.h>
+#include <util/python/pycppwrapper_util.h>
+
+#include <dns/exceptions.h>
+#include <dns/messagerenderer.h>
+#include <dns/name.h>
+
+#include "pydnspp_common.h"
+#include "messagerenderer_python.h"
+#include "name_python.h"
 
 //
 // Definition of the classes
@@ -41,21 +33,20 @@ static PyObject* po_NameRelation;
 // and static wrappers around the methods we export), a list of methods,
 // and a type description
 using namespace isc::dns;
+using namespace isc::dns::python;
 using namespace isc::util;
+using namespace isc::util::python;
 
+namespace {
 // NameComparisonResult
-class s_NameComparisonResult : public PyObject {
-public:
-    isc::dns::NameComparisonResult* ncr;
-};
 
-static int NameComparisonResult_init(s_NameComparisonResult*, PyObject*);
-static void NameComparisonResult_destroy(s_NameComparisonResult* self);
-static PyObject* NameComparisonResult_getOrder(s_NameComparisonResult* self);
-static PyObject* NameComparisonResult_getCommonLabels(s_NameComparisonResult* self);
-static PyObject* NameComparisonResult_getRelation(s_NameComparisonResult* self);
+int NameComparisonResult_init(s_NameComparisonResult*, PyObject*);
+void NameComparisonResult_destroy(s_NameComparisonResult* self);
+PyObject* NameComparisonResult_getOrder(s_NameComparisonResult* self);
+PyObject* NameComparisonResult_getCommonLabels(s_NameComparisonResult* self);
+PyObject* NameComparisonResult_getRelation(s_NameComparisonResult* self);
 
-static PyMethodDef NameComparisonResult_methods[] = {
+PyMethodDef NameComparisonResult_methods[] = {
     { "get_order", reinterpret_cast<PyCFunction>(NameComparisonResult_getOrder), METH_NOARGS,
       "Returns the order" },
     { "get_common_labels", reinterpret_cast<PyCFunction>(NameComparisonResult_getCommonLabels), METH_NOARGS,
@@ -65,122 +56,61 @@ static PyMethodDef NameComparisonResult_methods[] = {
     { NULL, NULL, 0, NULL }
 };
 
-static PyTypeObject name_comparison_result_type = {
-    PyVarObject_HEAD_INIT(NULL, 0)
-    "pydnspp.NameComparisonResult",
-    sizeof(s_NameComparisonResult),           // tp_basicsize
-    0,                                        // tp_itemsize
-    (destructor)NameComparisonResult_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
-    "This is a supplemental class used only as a return value of Name.compare(). "
-    "It encapsulate a tuple of the comparison: ordering, number of common labels, "
-    "and relationship as follows:\n"
-    "- ordering: relative ordering under the DNSSEC order relation\n"
-    "- labels: the number of common significant labels of the two names being"
-    "  compared\n"
-    "- relationship: see NameComparisonResult.NameRelation\n",
-    NULL,                                     // tp_traverse
-    NULL,                                     // tp_clear
-    NULL,                                     // tp_richcompare
-    0,                                        // tp_weaklistoffset
-    NULL,                                     // tp_iter
-    NULL,                                     // tp_iternext
-    NameComparisonResult_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
-    (initproc)NameComparisonResult_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
-};
-
-static int
+int
 NameComparisonResult_init(s_NameComparisonResult*, PyObject*) {
     PyErr_SetString(PyExc_NotImplementedError,
                     "NameComparisonResult can't be built directly");
     return (-1);
 }
 
-static void
+void
 NameComparisonResult_destroy(s_NameComparisonResult* self) {
-    delete self->ncr;
-    self->ncr = NULL;
+    delete self->cppobj;
+    self->cppobj = NULL;
     Py_TYPE(self)->tp_free(self);
 }
 
-static PyObject* 
+PyObject*
 NameComparisonResult_getOrder(s_NameComparisonResult* self) {
-    return (Py_BuildValue("i", self->ncr->getOrder()));
+    return (Py_BuildValue("i", self->cppobj->getOrder()));
 }
 
-static PyObject* 
+PyObject*
 NameComparisonResult_getCommonLabels(s_NameComparisonResult* self) {
-    return (Py_BuildValue("I", self->ncr->getCommonLabels()));
+    return (Py_BuildValue("I", self->cppobj->getCommonLabels()));
 }
 
-static PyObject* 
+PyObject*
 NameComparisonResult_getRelation(s_NameComparisonResult* self) {
-    return (Py_BuildValue("I", self->ncr->getRelation()));
+    return (Py_BuildValue("I", self->cppobj->getRelation()));
 }
-
 // end of NameComparisonResult
 
 // Name
-
-class s_Name : public PyObject {
-public:
-    isc::dns::Name* name;
-    size_t position;
-};
-
-static int Name_init(s_Name* self, PyObject* args);
-static void Name_destroy(s_Name* self);
-
-static PyObject* Name_toWire(s_Name* self, PyObject* args);
-static PyObject* Name_toText(s_Name* self);
-static PyObject* Name_str(PyObject* self);
-static PyObject* Name_getLabelCount(s_Name* self);
-static PyObject* Name_at(s_Name* self, PyObject* args);
-static PyObject* Name_getLength(s_Name* self);
-
-static PyObject* Name_compare(s_Name* self, PyObject* args);
-static PyObject* Name_equals(s_Name* self, PyObject* args);
-
-static PyObject* Name_richcmp(s_Name* self, s_Name* other, int op);
-static PyObject* Name_split(s_Name* self, PyObject* args);
-static PyObject* Name_reverse(s_Name* self);
-static PyObject* Name_concatenate(s_Name* self, PyObject* args);
-static PyObject* Name_downcase(s_Name* self);
-static PyObject* Name_isWildCard(s_Name* self);
-
-static PyMethodDef Name_methods[] = {
+// Shortcut type which would be convenient for adding class variables safely.
+typedef CPPPyObjectContainer<s_Name, Name> NameContainer;
+
+int Name_init(s_Name* self, PyObject* args);
+void Name_destroy(s_Name* self);
+
+PyObject* Name_toWire(s_Name* self, PyObject* args);
+PyObject* Name_toText(s_Name* self);
+PyObject* Name_str(PyObject* self);
+PyObject* Name_getLabelCount(s_Name* self);
+PyObject* Name_at(s_Name* self, PyObject* args);
+PyObject* Name_getLength(s_Name* self);
+
+PyObject* Name_compare(s_Name* self, PyObject* args);
+PyObject* Name_equals(s_Name* self, PyObject* args);
+
+PyObject* Name_richcmp(s_Name* self, s_Name* other, int op);
+PyObject* Name_split(s_Name* self, PyObject* args);
+PyObject* Name_reverse(s_Name* self);
+PyObject* Name_concatenate(s_Name* self, PyObject* args);
+PyObject* Name_downcase(s_Name* self);
+PyObject* Name_isWildCard(s_Name* self);
+
+PyMethodDef Name_methods[] = {
     { "at", reinterpret_cast<PyCFunction>(Name_at), METH_VARARGS,
       "Returns the integer value of the name data at the specified position" },
     { "get_length", reinterpret_cast<PyCFunction>(Name_getLength), METH_NOARGS,
@@ -217,63 +147,7 @@ static PyMethodDef Name_methods[] = {
     { NULL, NULL, 0, NULL }
 };
 
-static PyTypeObject name_type = {
-    PyVarObject_HEAD_INIT(NULL, 0)
-    "pydnspp.Name",
-    sizeof(s_Name),                     // tp_basicsize
-    0,                                  // tp_itemsize
-    (destructor)Name_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
-    Name_str,                           // tp_str
-    NULL,                               // tp_getattro
-    NULL,                               // tp_setattro
-    NULL,                               // tp_as_buffer
-    Py_TPFLAGS_DEFAULT,                 // tp_flags
-    "The Name class encapsulates DNS names.\n"
-    "It provides interfaces to construct a name from string or wire-format data, "
-    "transform a name into a string or wire-format data, compare two names, get "
-    "access to various properties of a name, etc.",
-    NULL,                               // tp_traverse
-    NULL,                               // tp_clear
-    (richcmpfunc)Name_richcmp,          // tp_richcompare
-    0,                                  // tp_weaklistoffset
-    NULL,                               // tp_iter
-    NULL,                               // tp_iternext
-    Name_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
-    (initproc)Name_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
-    // Note: not sure if the following are correct.  Added them just to
-    // make the compiler happy.
-    NULL,                               // tp_del
-    0                                   // tp_version_tag
-};
-
-
-static int
+int
 Name_init(s_Name* self, PyObject* args) {
     const char* s;
     PyObject* downcase = Py_False;
@@ -286,7 +160,7 @@ Name_init(s_Name* self, PyObject* args) {
         try {
             const std::string n(s);
 
-            self->name = new Name(n, downcase == Py_True);
+            self->cppobj = new Name(n, downcase == Py_True);
             self->position = 0;
         } catch (const EmptyLabel&) {
             PyErr_SetString(po_EmptyLabel, "EmptyLabel");
@@ -339,7 +213,7 @@ Name_init(s_Name* self, PyObject* args) {
             InputBuffer buffer(bytes, len);
 
             buffer.setPosition(position);
-            self->name = new Name(buffer, downcase == Py_True);
+            self->cppobj = new Name(buffer, downcase == Py_True);
             self->position = buffer.getPosition();
         } catch (const InvalidBufferPosition&) {
             PyErr_SetString(po_InvalidBufferPosition,
@@ -361,14 +235,14 @@ Name_init(s_Name* self, PyObject* args) {
     return (-1);
 }
 
-static void
+void
 Name_destroy(s_Name* self) {
-    delete self->name;
-    self->name = NULL;
+    delete self->cppobj;
+    self->cppobj = NULL;
     Py_TYPE(self)->tp_free(self);
 }
 
-static PyObject*
+PyObject*
 Name_at(s_Name* self, PyObject* args) {
     int pos;
     if (!PyArg_ParseTuple(args, "i", &pos)) {
@@ -382,7 +256,7 @@ Name_at(s_Name* self, PyObject* args) {
     }
 
     try {
-        return (Py_BuildValue("I", self->name->at(pos)));
+        return (Py_BuildValue("I", self->cppobj->at(pos)));
     } catch (const isc::OutOfRange&) {
         PyErr_SetString(PyExc_IndexError,
                         "name index out of range");
@@ -390,22 +264,22 @@ Name_at(s_Name* self, PyObject* args) {
     }
 }
 
-static PyObject*
+PyObject*
 Name_getLength(s_Name* self) {
-    return (Py_BuildValue("i", self->name->getLength()));
+    return (Py_BuildValue("i", self->cppobj->getLength()));
 }
 
-static PyObject*
+PyObject*
 Name_getLabelCount(s_Name* self) {
-    return (Py_BuildValue("i", self->name->getLabelCount()));
+    return (Py_BuildValue("i", self->cppobj->getLabelCount()));
 }
 
-static PyObject*
+PyObject*
 Name_toText(s_Name* self) {
-    return (Py_BuildValue("s", self->name->toText().c_str()));
+    return (Py_BuildValue("s", self->cppobj->toText().c_str()));
 }
 
-static PyObject*
+PyObject*
 Name_str(PyObject* self) {
     // Simply call the to_text method we already defined
     // str() is not defined in the c++ version, only to_text
@@ -415,7 +289,7 @@ Name_str(PyObject* self) {
                                 const_cast<char*>("")));
 }
 
-static PyObject*
+PyObject*
 Name_toWire(s_Name* self, PyObject* args) {
     PyObject* bytes;
     s_MessageRenderer* mr;
@@ -424,7 +298,7 @@ Name_toWire(s_Name* self, PyObject* args) {
         PyObject* bytes_o = bytes;
 
         OutputBuffer buffer(Name::MAX_WIRE);
-        self->name->toWire(buffer);
+        self->cppobj->toWire(buffer);
         PyObject* name_bytes = PyBytes_FromStringAndSize(static_cast<const char*>(buffer.getData()), buffer.getLength());
         PyObject* result = PySequence_InPlaceConcat(bytes_o, name_bytes);
         // We need to release the object we temporarily created here
@@ -432,7 +306,7 @@ Name_toWire(s_Name* self, PyObject* args) {
         Py_DECREF(name_bytes);
         return (result);
     } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) {
-        self->name->toWire(*mr->messagerenderer);
+        self->cppobj->toWire(*mr->messagerenderer);
         // If we return NULL it is seen as an error, so use this for
         // None returns
         Py_RETURN_NONE;
@@ -443,7 +317,7 @@ Name_toWire(s_Name* self, PyObject* args) {
     return (NULL);
 }
 
-static PyObject*
+PyObject*
 Name_compare(s_Name* self, PyObject* args) {
     s_Name* other;
 
@@ -452,26 +326,26 @@ Name_compare(s_Name* self, PyObject* args) {
 
     s_NameComparisonResult* ret = PyObject_New(s_NameComparisonResult, &name_comparison_result_type);
     if (ret != NULL) {
-        ret->ncr = new NameComparisonResult(
-            self->name->compare(*other->name));
+        ret->cppobj = new NameComparisonResult(
+            self->cppobj->compare(*other->cppobj));
     }
     return (ret);
 }
 
-static PyObject* 
+PyObject*
 Name_equals(s_Name* self, PyObject* args) {
     s_Name* other;
 
     if (!PyArg_ParseTuple(args, "O!", &name_type, &other))
         return (NULL);
 
-    if (self->name->equals(*other->name))
+    if (self->cppobj->equals(*other->cppobj))
         Py_RETURN_TRUE;
     else
         Py_RETURN_FALSE;
 }
 
-static PyObject*
+PyObject*
 Name_split(s_Name* self, PyObject* args) {
     int first, n;
     s_Name* ret = NULL;
@@ -485,14 +359,14 @@ Name_split(s_Name* self, PyObject* args) {
         }
         ret = PyObject_New(s_Name, &name_type);
         if (ret != NULL) {
-            ret->name = NULL;
+            ret->cppobj = NULL;
             try {
-                ret->name = new Name(self->name->split(first, n));
+                ret->cppobj = new Name(self->cppobj->split(first, n));
             } catch(const isc::OutOfRange& oor) {
                 PyErr_SetString(PyExc_IndexError, oor.what());
-                ret->name = NULL;
+                ret->cppobj = NULL;
             }
-            if (ret->name == NULL) {
+            if (ret->cppobj == NULL) {
                 Py_DECREF(ret);
                 return (NULL);
             }
@@ -507,14 +381,14 @@ Name_split(s_Name* self, PyObject* args) {
         }
         ret = PyObject_New(s_Name, &name_type);
         if (ret != NULL) {
-            ret->name = NULL;
+            ret->cppobj = NULL;
             try {
-                ret->name = new Name(self->name->split(n));
+                ret->cppobj = new Name(self->cppobj->split(n));
             } catch(const isc::OutOfRange& oor) {
                 PyErr_SetString(PyExc_IndexError, oor.what());
-                ret->name = NULL;
+                ret->cppobj = NULL;
             }
-            if (ret->name == NULL) {
+            if (ret->cppobj == NULL) {
                 Py_DECREF(ret);
                 return (NULL);
             }
@@ -526,14 +400,13 @@ Name_split(s_Name* self, PyObject* args) {
                     "No valid type in split argument");
     return (ret);
 }
-#include <iostream>
 
 //
 // richcmp defines the ==, !=, >, <, >= and <= operators in python
 // It is translated to a function that gets 3 arguments, an object,
 // an object to compare to, and an operator.
 //
-static PyObject*
+PyObject*
 Name_richcmp(s_Name* self, s_Name* other, int op) {
     bool c;
 
@@ -545,22 +418,22 @@ Name_richcmp(s_Name* self, s_Name* other, int op) {
 
     switch (op) {
     case Py_LT:
-        c = *self->name < *other->name;
+        c = *self->cppobj < *other->cppobj;
         break;
     case Py_LE:
-        c = *self->name <= *other->name;
+        c = *self->cppobj <= *other->cppobj;
         break;
     case Py_EQ:
-        c = *self->name == *other->name;
+        c = *self->cppobj == *other->cppobj;
         break;
     case Py_NE:
-        c = *self->name != *other->name;
+        c = *self->cppobj != *other->cppobj;
         break;
     case Py_GT:
-        c = *self->name > *other->name;
+        c = *self->cppobj > *other->cppobj;
         break;
     case Py_GE:
-        c = *self->name >= *other->name;
+        c = *self->cppobj >= *other->cppobj;
         break;
     default:
         PyErr_SetString(PyExc_IndexError,
@@ -574,13 +447,13 @@ Name_richcmp(s_Name* self, s_Name* other, int op) {
     }
 }
 
-static PyObject*
+PyObject*
 Name_reverse(s_Name* self) {
     s_Name* ret = PyObject_New(s_Name, &name_type);
 
     if (ret != NULL) {
-        ret->name = new Name(self->name->reverse());
-        if (ret->name == NULL) {
+        ret->cppobj = new Name(self->cppobj->reverse());
+        if (ret->cppobj == NULL) {
             Py_DECREF(ret);
             return (NULL);
         }
@@ -588,7 +461,7 @@ Name_reverse(s_Name* self) {
     return (ret);
 }
 
-static PyObject*
+PyObject*
 Name_concatenate(s_Name* self, PyObject* args) {
     s_Name* other;
 
@@ -598,7 +471,7 @@ Name_concatenate(s_Name* self, PyObject* args) {
     s_Name* ret = PyObject_New(s_Name, &name_type);
     if (ret != NULL) {
         try {
-            ret->name = new Name(self->name->concatenate(*other->name));
+            ret->cppobj = new Name(self->cppobj->concatenate(*other->cppobj));
         } catch (const TooLongName& tln) {
             PyErr_SetString(po_TooLongName, tln.what());
             return (NULL);
@@ -607,23 +480,159 @@ Name_concatenate(s_Name* self, PyObject* args) {
     return (ret);
 }
 
-static PyObject*
+PyObject*
 Name_downcase(s_Name* self) {
-    self->name->downcase();
+    self->cppobj->downcase();
     Py_INCREF(self);
     return (self);
 }
 
-static PyObject*
+PyObject*
 Name_isWildCard(s_Name* self) {
-    if (self->name->isWildcard()) {
+    if (self->cppobj->isWildcard()) {
         Py_RETURN_TRUE;
     } else {
         Py_RETURN_FALSE;
     }
 }
 // end of Name
+} // end of unnamed namespace
+
+namespace isc {
+namespace dns {
+namespace python {
+
+//
+// Definition of the custom exceptions
+// Initialization and addition of these go in the module init at the
+// end
+//
+PyObject* po_EmptyLabel;
+PyObject* po_TooLongName;
+PyObject* po_TooLongLabel;
+PyObject* po_BadLabelType;
+PyObject* po_BadEscape;
+PyObject* po_IncompleteName;
+PyObject* po_InvalidBufferPosition;
+PyObject* po_DNSMessageFORMERR;
 
+//
+// Definition of enums
+// Initialization and addition of these go in the module init at the
+// end
+//
+PyObject* po_NameRelation;
+
+PyTypeObject name_comparison_result_type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "pydnspp.NameComparisonResult",
+    sizeof(s_NameComparisonResult),           // tp_basicsize
+    0,                                        // tp_itemsize
+    (destructor)NameComparisonResult_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
+    "This is a supplemental class used only as a return value of Name.compare(). "
+    "It encapsulate a tuple of the comparison: ordering, number of common labels, "
+    "and relationship as follows:\n"
+    "- ordering: relative ordering under the DNSSEC order relation\n"
+    "- labels: the number of common significant labels of the two names being"
+    "  compared\n"
+    "- relationship: see NameComparisonResult.NameRelation\n",
+    NULL,                                     // tp_traverse
+    NULL,                                     // tp_clear
+    NULL,                                     // tp_richcompare
+    0,                                        // tp_weaklistoffset
+    NULL,                                     // tp_iter
+    NULL,                                     // tp_iternext
+    NameComparisonResult_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
+    (initproc)NameComparisonResult_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
+};
+
+PyTypeObject name_type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "pydnspp.Name",
+    sizeof(s_Name),                     // tp_basicsize
+    0,                                  // tp_itemsize
+    (destructor)Name_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
+    Name_str,                           // tp_str
+    NULL,                               // tp_getattro
+    NULL,                               // tp_setattro
+    NULL,                               // tp_as_buffer
+    Py_TPFLAGS_DEFAULT,                 // tp_flags
+    "The Name class encapsulates DNS names.\n"
+    "It provides interfaces to construct a name from string or wire-format data, "
+    "transform a name into a string or wire-format data, compare two names, get "
+    "access to various properties of a name, etc.",
+    NULL,                               // tp_traverse
+    NULL,                               // tp_clear
+    (richcmpfunc)Name_richcmp,          // tp_richcompare
+    0,                                  // tp_weaklistoffset
+    NULL,                               // tp_iter
+    NULL,                               // tp_iternext
+    Name_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
+    (initproc)Name_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
+    // Note: not sure if the following are correct.  Added them just to
+    // make the compiler happy.
+    NULL,                               // tp_del
+    0                                   // tp_version_tag
+};
 
 // Module Initialization, all statics are initialized here
 bool
@@ -669,7 +678,7 @@ initModulePart_Name(PyObject* mod) {
     addClassVariable(name_type, "COMPRESS_POINTER_MARK16", Py_BuildValue("I", Name::COMPRESS_POINTER_MARK16));
 
     s_Name* root_name = PyObject_New(s_Name, &name_type);
-    root_name->name = new Name(Name::ROOT_NAME());
+    root_name->cppobj = new Name(Name::ROOT_NAME());
     PyObject* po_ROOT_NAME = root_name;
     addClassVariable(name_type, "ROOT_NAME", po_ROOT_NAME);
 
@@ -706,3 +715,13 @@ initModulePart_Name(PyObject* mod) {
 
     return (true);
 }
+
+PyObject*
+createNameObject(const Name& source) {
+    NameContainer container = PyObject_New(s_Name, &name_type);
+    container.set(new Name(source));
+    return (container.release());
+}
+} // namespace python
+} // namespace dns
+} // namespace isc
diff --git a/src/lib/dns/python/name_python.h b/src/lib/dns/python/name_python.h
new file mode 100644
index 0000000..f8e793d
--- /dev/null
+++ b/src/lib/dns/python/name_python.h
@@ -0,0 +1,84 @@
+// 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_NAME_H
+#define __PYTHON_NAME_H 1
+
+#include <Python.h>
+
+#include <util/python/pycppwrapper_util.h>
+
+namespace isc {
+namespace dns {
+class NameComparisonResult;
+class Name;
+
+namespace python {
+
+//
+// Declaration of the custom exceptions
+// Initialization and addition of these go in the module init at the
+// end
+//
+extern PyObject* po_EmptyLabel;
+extern PyObject* po_TooLongName;
+extern PyObject* po_TooLongLabel;
+extern PyObject* po_BadLabelType;
+extern PyObject* po_BadEscape;
+extern PyObject* po_IncompleteName;
+extern PyObject* po_InvalidBufferPosition;
+extern PyObject* po_DNSMessageFORMERR;
+
+//
+// Declaration of enums
+// Initialization and addition of these go in the module init at the
+// end
+//
+extern PyObject* po_NameRelation;
+
+// The s_* Class simply covers one instantiation of the object.
+class s_NameComparisonResult : public PyObject {
+public:
+    s_NameComparisonResult() : cppobj(NULL) {}
+    NameComparisonResult* cppobj;
+};
+
+class s_Name : public PyObject {
+public:
+    s_Name() : cppobj(NULL), position(0) {}
+    Name* cppobj;
+    size_t position;
+};
+
+extern PyTypeObject name_comparison_result_type;
+extern PyTypeObject name_type;
+
+bool initModulePart_Name(PyObject* mod);
+
+/// This is A simple shortcut to create a python Name object (in the
+/// form of a pointer to PyObject) with minimal exception safety.
+/// On success, it returns a valid pointer to PyObject with a reference
+/// counter of 1; if something goes wrong it throws an exception (it never
+/// returns a NULL pointer).
+/// This function is expected to be called with in a try block
+/// followed by necessary setup for python exception.
+PyObject* createNameObject(const Name& source);
+} // namespace python
+} // namespace dns
+} // namespace isc
+#endif // __PYTHON_NAME_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/python/pydnspp.cc b/src/lib/dns/python/pydnspp.cc
index 0be346e..07abf71 100644
--- a/src/lib/dns/python/pydnspp.cc
+++ b/src/lib/dns/python/pydnspp.cc
@@ -38,6 +38,14 @@
 #include <dns/messagerenderer.h>
 
 #include "pydnspp_common.h"
+#include "messagerenderer_python.h"
+#include "name_python.h"
+#include "rcode_python.h"
+#include "tsigkey_python.h"
+#include "tsig_rdata_python.h"
+#include "tsigerror_python.h"
+#include "tsigrecord_python.h"
+#include "tsig_python.h"
 
 namespace isc {
 namespace dns {
@@ -52,14 +60,9 @@ PyObject* po_DNSMessageBADVERS;
 }
 }
 
-#include "rcode_python.h"
-#include "tsigerror_python.h"
-
 // order is important here!
 using namespace isc::dns::python;
 
-#include <dns/python/messagerenderer_python.cc>
-#include <dns/python/name_python.cc>           // needs Messagerenderer
 #include <dns/python/rrclass_python.cc>        // needs Messagerenderer
 #include <dns/python/rrtype_python.cc>         // needs Messagerenderer
 #include <dns/python/rrttl_python.cc>          // needs Messagerenderer
@@ -67,8 +70,6 @@ using namespace isc::dns::python;
 #include <dns/python/rrset_python.cc>          // needs Rdata, RRTTL
 #include <dns/python/question_python.cc>       // needs RRClass, RRType, RRTTL,
                                                // Name
-#include <dns/python/tsigkey_python.cc>        // needs Name
-#include <dns/python/tsig_python.cc>           // needs tsigkey
 #include <dns/python/opcode_python.cc>
 #include <dns/python/edns_python.cc>           // needs Messagerenderer, Rcode
 #include <dns/python/message_python.cc>        // needs RRset, Question
@@ -167,14 +168,21 @@ PyInit_pydnspp(void) {
         return (NULL);
     }
 
+    if (!initModulePart_TSIG(mod)) {
+        return (NULL);
+    }
+
     if (!initModulePart_TSIGError(mod)) {
         return (NULL);
     }
 
+    if (!initModulePart_TSIGRecord(mod)) {
+        return (NULL);
+    }
+
     if (!initModulePart_TSIGContext(mod)) {
         return (NULL);
     }
 
     return (mod);
 }
-
diff --git a/src/lib/dns/python/pydnspp_towire.h b/src/lib/dns/python/pydnspp_towire.h
new file mode 100644
index 0000000..66362a0
--- /dev/null
+++ b/src/lib/dns/python/pydnspp_towire.h
@@ -0,0 +1,127 @@
+// 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 __LIBDNS_PYTHON_TOWIRE_H
+#define __LIBDNS_PYTHON_TOWIRE_H 1
+
+#include <Python.h>
+
+#include <stdexcept>
+#include <string>
+
+#include <dns/messagerenderer.h>
+
+#include <util/buffer.h>
+#include <util/python/pycppwrapper_util.h>
+
+#include "messagerenderer_python.h"
+
+namespace isc {
+namespace dns {
+namespace python {
+
+// The following two templated structures are a helper to use the same
+// toWire() template implementation for two types of toWire() methods:
+// return an integer or have no return value.
+template <typename CPPCLASS>
+struct ToWireCallVoid {
+    ToWireCallVoid(CPPCLASS& cppobj) : cppobj_(cppobj) {}
+    int operator()(AbstractMessageRenderer& renderer) const {
+        cppobj_.toWire(renderer);
+        return (0);
+    }
+    const CPPCLASS& cppobj_;
+};
+
+template <typename CPPCLASS>
+struct ToWireCallInt {
+    ToWireCallInt(CPPCLASS& cppobj) : cppobj_(cppobj) {}
+    int operator()(AbstractMessageRenderer& renderer) const {
+        return (cppobj_.toWire(renderer));
+    }
+    const CPPCLASS& cppobj_;
+};
+
+// This templated function gives a common implementation of the toWire()
+// wrapper for various libdns++ classes.  PYSTRUCT and CPPCLASS are
+// (C++ binding of) python and (pure) C++ classes (e.g., s_Name and Name),
+// and TOWIRECALLER is either ToWireCallVoid<CPPCLASS> or
+// ToWireCallInt<CPPCLASS>, depending on the toWire() method of the class
+// returns a value or not.
+//
+// See, e.g., tsigrecord_python.cc for how to use it.
+//
+// This should be able to be used without modification for most classes that
+// have toWire().  But if the underlying toWire() has an extra argument, the
+// definition will need to be adjusted accordingly.
+template <typename PYSTRUCT, typename CPPCLASS, typename TOWIRECALLER>
+PyObject*
+toWireWrapper(const PYSTRUCT* const self, PyObject* args) {
+    try {
+        // To OutputBuffer version
+        PyObject* bytes; // this won't have own reference, no risk of leak.
+        if (PyArg_ParseTuple(args, "O", &bytes) && PySequence_Check(bytes)) {
+            // render the object into a buffer (this can throw)
+            isc::util::OutputBuffer buffer(0);
+            self->cppobj->toWire(buffer);
+
+            // convert the rendered data into PyObject.  This could leak later,
+            // so we need to store it in a container.
+            PyObject* rd_bytes = PyBytes_FromStringAndSize(
+                static_cast<const char*>(buffer.getData()),
+                buffer.getLength());
+            isc::util::python::PyObjectContainer rd_bytes_container(rd_bytes);
+
+            // concat the latest data to the given existing sequence.  concat
+            // operation could fail, so we use a container to clean it up
+            // safely should that happen.
+            PyObject* result = PySequence_InPlaceConcat(bytes, rd_bytes);
+            isc::util::python::PyObjectContainer result_container(result);
+
+            return (result_container.release());
+        }
+
+        // To MessageRenderer version
+        s_MessageRenderer* renderer;
+        if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &renderer)) {
+            const unsigned int n = TOWIRECALLER(*self->cppobj)(
+                *renderer->messagerenderer);
+
+            return (Py_BuildValue("I", n));
+        }
+    } catch (const std::exception& ex) {
+        const std::string ex_what =
+            "Failed to render an libdns++ object wire-format: "
+            + std::string(ex.what());
+        PyErr_SetString(po_IscException, ex_what.c_str());
+        return (NULL);
+    } catch (...) {
+        PyErr_SetString(po_IscException, "Unexpectedly failed to render an "
+                        "libdns++ object wire-format.");
+        return (NULL);
+    }
+
+    PyErr_Clear();
+    PyErr_SetString(PyExc_TypeError,
+                    "Incorrect arguments for a to_wire() method");
+    return (NULL);
+}
+} // namespace python
+} // namespace dns
+} // namespace isc
+#endif // __LIBDNS_PYTHON_TOWIRE_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/python/question_python.cc b/src/lib/dns/python/question_python.cc
index 2889350..c702f85 100644
--- a/src/lib/dns/python/question_python.cc
+++ b/src/lib/dns/python/question_python.cc
@@ -144,7 +144,7 @@ Question_init(s_Question* self, PyObject* args) {
                                                &rrclass_type, &rrclass,
                                                &rrtype_type, &rrtype
            )) {
-            self->question = QuestionPtr(new Question(*name->name, *rrclass->rrclass,
+            self->question = QuestionPtr(new Question(*name->cppobj, *rrclass->rrclass,
                                           *rrtype->rrtype));
             return (0);
         } else if (PyArg_ParseTuple(args, "y#|I", &b, &len, &position)) {
@@ -189,7 +189,7 @@ Question_getName(s_Question* self) {
     // is this the best way to do this?
     name = static_cast<s_Name*>(name_type.tp_alloc(&name_type, 0));
     if (name != NULL) {
-        name->name = new Name(self->question->getName());
+        name->cppobj = new Name(self->question->getName());
     }
 
     return (name);
diff --git a/src/lib/dns/python/rrset_python.cc b/src/lib/dns/python/rrset_python.cc
index c7d05d1..71a0710 100644
--- a/src/lib/dns/python/rrset_python.cc
+++ b/src/lib/dns/python/rrset_python.cc
@@ -168,7 +168,7 @@ RRset_init(s_RRset* self, PyObject* args) {
                                            &rrtype_type, &rrtype,
                                            &rrttl_type, &rrttl
        )) {
-        self->rrset = RRsetPtr(new RRset(*name->name, *rrclass->rrclass,
+        self->rrset = RRsetPtr(new RRset(*name->cppobj, *rrclass->rrclass,
                                 *rrtype->rrtype, *rrttl->rrttl));
         return (0);
     }
@@ -197,8 +197,8 @@ RRset_getName(s_RRset* self) {
     // is this the best way to do this?
     name = static_cast<s_Name*>(name_type.tp_alloc(&name_type, 0));
     if (name != NULL) {
-        name->name = new Name(self->rrset->getName());
-        if (name->name == NULL)
+        name->cppobj = new Name(self->rrset->getName());
+        if (name->cppobj == NULL)
           {
             Py_DECREF(name);
             return (NULL);
@@ -265,7 +265,7 @@ RRset_setName(s_RRset* self, PyObject* args) {
     if (!PyArg_ParseTuple(args, "O!", &name_type, &name)) {
         return (NULL);
     }
-    self->rrset->setName(*name->name);
+    self->rrset->setName(*name->cppobj);
     Py_RETURN_NONE;
 }
 
diff --git a/src/lib/dns/python/tests/Makefile.am b/src/lib/dns/python/tests/Makefile.am
index 1149a47..eb0c136 100644
--- a/src/lib/dns/python/tests/Makefile.am
+++ b/src/lib/dns/python/tests/Makefile.am
@@ -12,8 +12,10 @@ PYTESTS += rrset_python_test.py
 PYTESTS += rrttl_python_test.py
 PYTESTS += rrtype_python_test.py
 PYTESTS += tsig_python_test.py
+PYTESTS += tsig_rdata_python_test.py
 PYTESTS += tsigerror_python_test.py
 PYTESTS += tsigkey_python_test.py
+PYTESTS += tsigrecord_python_test.py
 
 EXTRA_DIST = $(PYTESTS)
 EXTRA_DIST += testutil.py
@@ -34,7 +36,7 @@ if ENABLE_PYTHON_COVERAGE
 endif
 	for pytest in $(PYTESTS) ; do \
 	echo Running test: $$pytest ; \
-	env PYTHONPATH=$(abs_top_srcdir)/src/lib/dns/.libs:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/dns/python/.libs \
+	env PYTHONPATH=$(abs_top_builddir)/src/lib/util/pyunittests/.libs:$(abs_top_srcdir)/src/lib/dns/.libs:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/dns/python/.libs \
 	TESTDATA_PATH=$(abs_top_srcdir)/src/lib/dns/tests/testdata:$(abs_top_builddir)/src/lib/dns/tests/testdata \
 	$(LIBRARY_PATH_PLACEHOLDER) \
 	$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
diff --git a/src/lib/dns/python/tests/message_python_test.py b/src/lib/dns/python/tests/message_python_test.py
index 72807cc..41b9a67 100644
--- a/src/lib/dns/python/tests/message_python_test.py
+++ b/src/lib/dns/python/tests/message_python_test.py
@@ -422,7 +422,54 @@ test.example.com. 3600 IN A 192.0.2.2
                           factoryFromFile,
                           message_parse,
                           "message_fromWire9")
-    
+
+    def test_from_wire_with_tsig(self):
+        # Initially there should be no TSIG
+        self.assertEqual(None, self.p.get_tsig_record())
+
+        # getTSIGRecord() is only valid in the parse mode.
+        self.assertRaises(InvalidMessageOperation, self.r.get_tsig_record)
+
+        factoryFromFile(self.p, "message_toWire2.wire")
+        tsig_rr = self.p.get_tsig_record()
+        self.assertEqual(Name("www.example.com"), tsig_rr.get_name())
+        self.assertEqual(85, tsig_rr.get_length())
+        self.assertEqual(TSIGKey.HMACMD5_NAME,
+                         tsig_rr.get_rdata().get_algorithm())
+
+        # If we clear the message for reuse, the recorded TSIG will be cleared.
+        self.p.clear(Message.PARSE)
+        self.assertEqual(None, self.p.get_tsig_record())
+
+    def test_from_wire_with_tsigcompressed(self):
+        # Mostly same as fromWireWithTSIG, but the TSIG owner name is
+        # compressed.
+        factoryFromFile(self.p, "message_fromWire12.wire");
+        tsig_rr = self.p.get_tsig_record()
+        self.assertEqual(Name("www.example.com"), tsig_rr.get_name())
+        # len(www.example.com) = 17, but when fully compressed, the length is
+        # 2 bytes.  So the length of the record should be 15 bytes shorter.
+        self.assertEqual(70, tsig_rr.get_length())
+
+    def test_from_wire_with_badtsig(self):
+        # Multiple TSIG RRs
+        self.assertRaises(DNSMessageFORMERR, factoryFromFile,
+                          self.p, "message_fromWire13.wire")
+        self.p.clear(Message.PARSE)
+
+        # TSIG in the answer section (must be in additional)
+        self.assertRaises(DNSMessageFORMERR, factoryFromFile,
+                          self.p, "message_fromWire14.wire")
+        self.p.clear(Message.PARSE)
+
+        # TSIG is not the last record.
+        self.assertRaises(DNSMessageFORMERR, factoryFromFile,
+                          self.p, "message_fromWire15.wire")
+        self.p.clear(Message.PARSE)
+
+        # Unexpected RR Class (this will fail in constructing TSIGRecord)
+        self.assertRaises(DNSMessageFORMERR, factoryFromFile,
+                          self.p, "message_fromWire16.wire")
 
 if __name__ == '__main__':
     unittest.main()
diff --git a/src/lib/dns/python/tests/tsig_python_test.py b/src/lib/dns/python/tests/tsig_python_test.py
index bffa0cf..7e5515d 100644
--- a/src/lib/dns/python/tests/tsig_python_test.py
+++ b/src/lib/dns/python/tests/tsig_python_test.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2010  Internet Systems Consortium.
+# 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
@@ -13,17 +13,542 @@
 # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
-import unittest
+import base64, sys, time, unittest
 from pydnspp import *
+from testutil import *
+from pyunittests_util import fix_current_time
+
+# bit-wise constant flags to configure DNS header flags for test
+# messages.
+QR_FLAG = 0x1
+AA_FLAG = 0x2
+RD_FLAG = 0x4
+
+COMMON_EXPECTED_MAC = b"\x22\x70\x26\xad\x29\x7b\xee\xe7\x21\xce\x6c\x6f\xff\x1e\x9e\xf3"
+DUMMY_DATA = b"\xdd" * 100
 
 class TSIGContextTest(unittest.TestCase):
     tsig_key = TSIGKey('www.example.com:SFuWd/q99SzF8Yzd1QbB9g==')
 
     def setUp(self):
-        # In the minimal implementation, we simply check constructing a
-        # TSIGContext doesn't cause any disruption.  We can add more tests
-        # later.
+        # make sure we don't use faked time unless explicitly do so in tests
+        fix_current_time(None)
+        self.qid = 0x2d65
+        self.test_name = Name("www.example.com")
         self.tsig_ctx = TSIGContext(self.tsig_key)
+        self.tsig_verify_ctx = TSIGContext(self.tsig_key)
+        self.keyring = TSIGKeyRing()
+        self.message = Message(Message.RENDER)
+        self.renderer = MessageRenderer()
+        self.test_class = RRClass.IN()
+        self.test_ttl = RRTTL(86400)
+        self.secret = base64.b64decode(b"SFuWd/q99SzF8Yzd1QbB9g==")
+        self.tsig_ctx = TSIGContext(TSIGKey(self.test_name,
+                                            TSIGKey.HMACMD5_NAME,
+                                            self.secret))
+        self.badkey_name = Name("badkey.example.com")
+        self.dummy_record = TSIGRecord(self.badkey_name,
+                                       TSIG("hmac-md5.sig-alg.reg.int. " + \
+                                                "1302890362 300 0 11621 " + \
+                                                "0 0"))
+
+    def tearDown(self):
+        # reset any faked current time setting (it would affect other tests)
+        fix_current_time(None)
+
+    # Note: intentionally use camelCase so that we can easily copy-paste
+    # corresponding C++ tests.
+    def createMessageAndSign(self, id, qname, ctx, message_flags=RD_FLAG,
+                             qtype=RRType.A(), answer_data=None,
+                             answer_type=None, add_question=True,
+                             rcode=Rcode.NOERROR()):
+        self.message.clear(Message.RENDER)
+        self.message.set_qid(id)
+        self.message.set_opcode(Opcode.QUERY())
+        self.message.set_rcode(rcode)
+        if (message_flags & QR_FLAG) != 0:
+            self.message.set_header_flag(Message.HEADERFLAG_QR)
+        if (message_flags & AA_FLAG) != 0:
+            self.message.set_header_flag(Message.HEADERFLAG_AA)
+        if (message_flags & RD_FLAG) != 0:
+            self.message.set_header_flag(Message.HEADERFLAG_RD)
+        if add_question:
+            self.message.add_question(Question(qname, self.test_class, qtype))
+        if answer_data is not None:
+            if answer_type is None:
+                answer_type = qtype
+            answer_rrset = RRset(qname, self.test_class, answer_type,
+                                 self.test_ttl)
+            answer_rrset.add_rdata(Rdata(answer_type, self.test_class,
+                                         answer_data))
+            self.message.add_rrset(Message.SECTION_ANSWER, answer_rrset)
+        self.renderer.clear()
+        self.message.to_wire(self.renderer)
+
+        if ctx.get_state() == TSIGContext.STATE_INIT:
+            expected_new_state = TSIGContext.STATE_SENT_REQUEST
+        else:
+            expected_new_state = TSIGContext.STATE_SENT_RESPONSE
+        tsig = ctx.sign(id, self.renderer.get_data())
+
+        return tsig
+
+    # Note: intentionally use camelCase so that we can easily copy-paste
+    # corresponding C++ tests.
+    def createMessageFromFile(self, file):
+        self.message.clear(Message.PARSE)
+        self.received_data = read_wire_data(file)
+        self.message.from_wire(self.received_data)
+
+    # Note: intentionally use camelCase so that we can easily copy-paste
+    # corresponding C++ tests.
+    def commonSignChecks(self, tsig, expected_qid, expected_timesigned,
+                         expected_mac, expected_error=0,
+                         expected_otherdata=None,
+                         expected_algorithm=TSIGKey.HMACMD5_NAME):
+        tsig_rdata = tsig.get_rdata()
+        self.assertEqual(expected_algorithm, tsig_rdata.get_algorithm())
+        self.assertEqual(expected_timesigned, tsig_rdata.get_timesigned())
+        self.assertEqual(300, tsig_rdata.get_fudge())
+        self.assertEqual(expected_mac, tsig_rdata.get_mac())
+        self.assertEqual(expected_qid, tsig_rdata.get_original_id())
+        self.assertEqual(expected_error, tsig_rdata.get_error())
+        self.assertEqual(expected_otherdata, tsig_rdata.get_other_data())
+
+    def test_initial_state(self):
+        # Until signing or verifying, the state should be INIT
+        self.assertEqual(TSIGContext.STATE_INIT, self.tsig_ctx.get_state())
+
+        # And there should be no error code.
+        self.assertEqual(TSIGError(Rcode.NOERROR()), self.tsig_ctx.get_error())
+
+    # Note: intentionally use camelCase so that we can easily copy-paste
+    # corresponding C++ tests.
+    def commonVerifyChecks(self, ctx, record, data, expected_error,
+                           expected_new_state=\
+                               TSIGContext.STATE_VERIFIED_RESPONSE):
+        self.assertEqual(expected_error, ctx.verify(record, data))
+        self.assertEqual(expected_error, ctx.get_error())
+        self.assertEqual(expected_new_state, ctx.get_state())
+
+    def test_from_keyring(self):
+        # Construct a TSIG context with an empty key ring.  Key shouldn't be
+        # found, and the BAD_KEY error should be recorded.
+        ctx = TSIGContext(self.test_name, TSIGKey.HMACMD5_NAME, self.keyring)
+        self.assertEqual(TSIGContext.STATE_INIT, ctx.get_state())
+        self.assertEqual(TSIGError.BAD_KEY, ctx.get_error())
+        # check get_error() doesn't cause ref leak.  Note: we can't
+        # realiably do this check for get_state(), as it returns an integer
+        # object, which could have many references
+        self.assertEqual(1, sys.getrefcount(ctx.get_error()))
+
+        # Add a matching key (we don't use the secret so leave it empty), and
+        # construct it again.  This time it should be constructed with a valid
+        # key.
+        self.keyring.add(TSIGKey(self.test_name, TSIGKey.HMACMD5_NAME, b""))
+        ctx = TSIGContext(self.test_name, TSIGKey.HMACMD5_NAME, self.keyring)
+        self.assertEqual(TSIGContext.STATE_INIT, ctx.get_state())
+        self.assertEqual(TSIGError.NOERROR, ctx.get_error())
+
+        # Similar to the first case except that the key ring isn't empty but
+        # it doesn't contain a matching key.
+        ctx = TSIGContext(self.test_name, TSIGKey.HMACSHA1_NAME, self.keyring)
+        self.assertEqual(TSIGContext.STATE_INIT, ctx.get_state())
+        self.assertEqual(TSIGError.BAD_KEY, ctx.get_error())
+
+        ctx = TSIGContext(Name("different-key.example"),
+                          TSIGKey.HMACMD5_NAME, self.keyring)
+        self.assertEqual(TSIGContext.STATE_INIT, ctx.get_state())
+        self.assertEqual(TSIGError.BAD_KEY, ctx.get_error())
+
+        # "Unknown" algorithm name will result in BADKEY, too.
+        ctx = TSIGContext(self.test_name, Name("unknown.algorithm"),
+                          self.keyring)
+        self.assertEqual(TSIGContext.STATE_INIT, ctx.get_state())
+        self.assertEqual(TSIGError.BAD_KEY, ctx.get_error())
+
+    def test_sign(self):
+        fix_current_time(0x4da8877a)
+        tsig = self.createMessageAndSign(self.qid, self.test_name,
+                                         self.tsig_ctx)
+        self.commonSignChecks(tsig, self.qid, 0x4da8877a, COMMON_EXPECTED_MAC)
+
+    # Same test as sign, but specifying the key name with upper-case (i.e.
+    # non canonical) characters.  The digest must be the same.  It should
+    # actually be ensured at the level of TSIGKey, but we confirm that at
+    # this level, too.
+    def test_sign_using_uppercase_keyname(self):
+        fix_current_time(0x4da8877a)
+        cap_ctx = TSIGContext(TSIGKey(Name("WWW.EXAMPLE.COM"),
+                                      TSIGKey.HMACMD5_NAME, self.secret))
+        tsig = self.createMessageAndSign(self.qid, self.test_name, cap_ctx)
+        self.commonSignChecks(tsig, self.qid, 0x4da8877a, COMMON_EXPECTED_MAC)
+
+    # Same as the previous test, but for the algorithm name.
+    def test_sign_using_uppercase_algorithm_name(self):
+        fix_current_time(0x4da8877a)
+        cap_ctx = TSIGContext(TSIGKey(self.test_name,
+                                      Name("HMAC-md5.SIG-alg.REG.int"),
+                                      self.secret))
+        tsig = self.createMessageAndSign(self.qid, self.test_name, cap_ctx)
+        self.commonSignChecks(tsig, self.qid, 0x4da8877a, COMMON_EXPECTED_MAC)
+
+    # Sign the message using the actual time, and check the accuracy of it.
+    # We cannot reasonably predict the expected MAC, so don't bother to
+    # check it.
+    def test_sign_at_actual_time(self):
+        now = int(time.time())
+        tsig = self.createMessageAndSign(self.qid, self.test_name,
+                                         self.tsig_ctx)
+        tsig_rdata = tsig.get_rdata()
+
+        # Check the resulted time signed is in the range of [now, now + 5]
+        self.assertTrue(now <= tsig_rdata.get_timesigned())
+        self.assertTrue(now + 5 >= tsig_rdata.get_timesigned())
+
+    def test_bad_data(self):
+        self.assertRaises(TypeError, self.tsig_ctx.sign, None, 10)
+
+    def test_verify_bad_data(self):
+        # the data must at least hold the DNS message header and the specified
+        # TSIG.
+        bad_len = 12 + self.dummy_record.get_length() - 1
+        self.assertRaises(InvalidParameter, self.tsig_ctx.verify,
+                          self.dummy_record, DUMMY_DATA[:bad_len])
+
+    def test_sign_using_hmacsha1(self):
+        fix_current_time(0x4dae7d5f)
+
+        secret = base64.b64decode(b"MA+QDhXbyqUak+qnMFyTyEirzng=")
+        sha1_ctx = TSIGContext(TSIGKey(self.test_name, TSIGKey.HMACSHA1_NAME,
+                                       secret))
+        qid = 0x0967
+        expected_mac = b"\x41\x53\x40\xc7\xda\xf8\x24\xed\x68\x4e\xe5\x86" + \
+            b"\xf7\xb5\xa6\x7a\x2f\xeb\xc0\xd3"
+        tsig = self.createMessageAndSign(qid, self.test_name, sha1_ctx)
+        self.commonSignChecks(tsig, qid, 0x4dae7d5f, expected_mac,
+                              0, None, TSIGKey.HMACSHA1_NAME)
+
+    def test_verify_then_sign_response(self):
+        fix_current_time(0x4da8877a)
+
+        self.createMessageFromFile("message_toWire2.wire")
+        self.commonVerifyChecks(self.tsig_verify_ctx,
+                                self.message.get_tsig_record(),
+                                self.received_data, TSIGError.NOERROR,
+                                TSIGContext.STATE_RECEIVED_REQUEST)
+
+        tsig = self.createMessageAndSign(self.qid, self.test_name,
+                                         self.tsig_verify_ctx,
+                                         QR_FLAG|AA_FLAG|RD_FLAG,
+                                         RRType.A(), "192.0.2.1")
+
+        expected_mac = b"\x8f\xcd\xa6\x6a\x7c\xd1\xa3\xb9\x94\x8e\xb1\x86" + \
+            b"\x9d\x38\x4a\x9f"
+        self.commonSignChecks(tsig, self.qid, 0x4da8877a, expected_mac)
+
+    def test_verify_uppercase_names(self):
+        fix_current_time(0x4da8877a)
+
+        self.createMessageFromFile("tsig_verify9.wire")
+        self.commonVerifyChecks(self.tsig_verify_ctx,
+                                self.message.get_tsig_record(),
+                                self.received_data, TSIGError.NOERROR,
+                                TSIGContext.STATE_RECEIVED_REQUEST)
+
+    def test_verify_forward_message(self):
+        fix_current_time(0x4da8877a)
+
+        self.createMessageFromFile("tsig_verify6.wire")
+        self.commonVerifyChecks(self.tsig_verify_ctx,
+                                self.message.get_tsig_record(),
+                                self.received_data, TSIGError.NOERROR,
+                                TSIGContext.STATE_RECEIVED_REQUEST)
+
+    def test_sign_continuation(self):
+        fix_current_time(0x4da8e951)
+
+        axfr_qid = 0x3410
+        zone_name = Name("example.com")
+
+        tsig = self.createMessageAndSign(axfr_qid, zone_name, self.tsig_ctx,
+                                         0, RRType.AXFR())
+
+        received_data = read_wire_data("tsig_verify1.wire")
+        self.commonVerifyChecks(self.tsig_verify_ctx, tsig, received_data,
+                                TSIGError.NOERROR,
+                                TSIGContext.STATE_RECEIVED_REQUEST)
+
+        tsig = self.createMessageAndSign(axfr_qid, zone_name,
+                                         self.tsig_verify_ctx,
+                                         AA_FLAG|QR_FLAG, RRType.AXFR(),
+                                         "ns.example.com. root.example.com." +\
+                                         " 2011041503 7200 3600 2592000 1200",
+                                         RRType.SOA())
+
+        received_data = read_wire_data("tsig_verify2.wire")
+        self.commonVerifyChecks(self.tsig_ctx, tsig, received_data,
+                                TSIGError.NOERROR)
+
+        expected_mac = b"\x10\x24\x58\xf7\xf6\x2d\xdd\x7d\x63\x8d\x74" +\
+            b"\x60\x34\x13\x09\x68"
+        tsig = self.createMessageAndSign(axfr_qid, zone_name,
+                                         self.tsig_verify_ctx,
+                                         AA_FLAG|QR_FLAG, RRType.AXFR(),
+                                         "ns.example.com.", RRType.NS(),
+                                         False)
+        self.commonSignChecks(tsig, axfr_qid, 0x4da8e951, expected_mac)
+
+        received_data = read_wire_data("tsig_verify3.wire")
+        self.commonVerifyChecks(self.tsig_ctx, tsig, received_data,
+                                TSIGError.NOERROR)
+
+    def test_badtime_response(self):
+        fix_current_time(0x4da8b9d6)
+
+        test_qid = 0x7fc4
+        tsig = self.createMessageAndSign(test_qid, self.test_name,
+                                         self.tsig_ctx, 0, RRType.SOA())
+
+        # "advance the clock" and try validating, which should fail due to
+        # BADTIME
+        fix_current_time(0x4da8be86)
+        self.commonVerifyChecks(self.tsig_verify_ctx, tsig, DUMMY_DATA,
+                                TSIGError.BAD_TIME,
+                                TSIGContext.STATE_RECEIVED_REQUEST)
+
+        # make and sign a response in the context of TSIG error.
+        tsig = self.createMessageAndSign(test_qid, self.test_name,
+                                         self.tsig_verify_ctx,
+                                         QR_FLAG, RRType.SOA(), None, None,
+                                         True, Rcode.NOTAUTH())
+
+        expected_otherdata = b"\x00\x00\x4d\xa8\xbe\x86"
+        expected_mac = b"\xd4\xb0\x43\xf6\xf4\x44\x95\xec\x8a\x01\x26" +\
+            b"\x0e\x39\x15\x9d\x76"
+
+        self.commonSignChecks(tsig, self.message.get_qid(), 0x4da8b9d6,
+                              expected_mac,
+                              18,     # error: BADTIME
+                              expected_otherdata)
+
+    def test_badtime_response2(self):
+        fix_current_time(0x4da8b9d6)
+
+        tsig = self.createMessageAndSign(self.qid, self.test_name,
+                                         self.tsig_ctx, 0, RRType.SOA())
+
+        # "rewind the clock" and try validating, which should fail due to
+        # BADTIME
+        fix_current_time(0x4da8b9d6 - 600)
+        self.commonVerifyChecks(self.tsig_verify_ctx, tsig, DUMMY_DATA,
+                           TSIGError.BAD_TIME,
+                                TSIGContext.STATE_RECEIVED_REQUEST)
+
+    # Test various boundary conditions.  We intentionally use the magic
+    # number of 300 instead of the constant variable for testing.
+    # In the okay cases, signature is not correct, but it's sufficient to
+    # check the error code isn't BADTIME for the purpose of this test.
+    def test_badtime_boundaries(self):
+        fix_current_time(0x4da8b9d6)
+
+        tsig = self.createMessageAndSign(self.qid, self.test_name,
+                                         self.tsig_ctx, 0, RRType.SOA())
+                                         
+        fix_current_time(0x4da8b9d6 + 301)
+        self.assertEqual(TSIGError.BAD_TIME,
+                         self.tsig_verify_ctx.verify(tsig, DUMMY_DATA))
+
+        fix_current_time(0x4da8b9d6 + 300)
+        self.assertNotEqual(TSIGError.BAD_TIME,
+                            self.tsig_verify_ctx.verify(tsig, DUMMY_DATA))
+
+        fix_current_time(0x4da8b9d6 - 301)
+        self.assertEqual(TSIGError.BAD_TIME,
+                         self.tsig_verify_ctx.verify(tsig, DUMMY_DATA))
+
+        fix_current_time(0x4da8b9d6 - 300)
+        self.assertNotEqual(TSIGError.BAD_TIME,
+                            self.tsig_verify_ctx.verify(tsig, DUMMY_DATA))
+
+    def test_badtime_overflow(self):
+        fix_current_time(200)
+        tsig = self.createMessageAndSign(self.qid, self.test_name,
+                                         self.tsig_ctx, 0, RRType.SOA())
+
+        # This should be in the okay range, but since "200 - fudge" overflows
+        # and we compare them as 64-bit unsigned integers, it results in a
+        # false positive (we intentionally accept that).
+        fix_current_time(100)
+        self.assertEqual(TSIGError.BAD_TIME,
+                         self.tsig_verify_ctx.verify(tsig, DUMMY_DATA))
+
+    def test_badsig_response(self):
+        fix_current_time(0x4da8877a)
+
+        # Try to sign a simple message with bogus secret.  It should fail
+        # with BADSIG.
+        self.createMessageFromFile("message_toWire2.wire")
+        bad_ctx = TSIGContext(TSIGKey(self.test_name, TSIGKey.HMACMD5_NAME,
+                                      DUMMY_DATA))
+        self.commonVerifyChecks(bad_ctx, self.message.get_tsig_record(),
+                                self.received_data, TSIGError.BAD_SIG,
+                                TSIGContext.STATE_RECEIVED_REQUEST)
+
+        # Sign the same message (which doesn't matter for this test) with the
+        # context of "checked state".
+        tsig = self.createMessageAndSign(self.qid, self.test_name, bad_ctx)
+        self.commonSignChecks(tsig, self.message.get_qid(), 0x4da8877a, None,
+                              16)   # 16: BADSIG
+
+    def test_badkey_response(self):
+        # A similar test as badsigResponse but for BADKEY
+        fix_current_time(0x4da8877a)
+        tsig_ctx = TSIGContext(self.badkey_name, TSIGKey.HMACMD5_NAME,
+                               self.keyring)
+        self.commonVerifyChecks(tsig_ctx, self.dummy_record, DUMMY_DATA,
+                                TSIGError.BAD_KEY,
+                                TSIGContext.STATE_RECEIVED_REQUEST)
+
+        sig = self.createMessageAndSign(self.qid, self.test_name, tsig_ctx)
+        self.assertEqual(self.badkey_name, sig.get_name())
+        self.commonSignChecks(sig, self.qid, 0x4da8877a, None, 17) # 17: BADKEY
+
+    def test_badkey_for_response(self):
+        # "BADKEY" case for a response to a signed message
+        self.createMessageAndSign(self.qid, self.test_name, self.tsig_ctx)
+        self.commonVerifyChecks(self.tsig_ctx, self.dummy_record, DUMMY_DATA,
+                                TSIGError.BAD_KEY,
+                                TSIGContext.STATE_SENT_REQUEST)
+
+        # A similar case with a different algorithm
+        dummy_record = TSIGRecord(self.test_name,
+                                  TSIG("hmac-sha1. 1302890362 300 0 "
+                                       "11621 0 0"))
+        self.commonVerifyChecks(self.tsig_ctx, dummy_record, DUMMY_DATA,
+                                TSIGError.BAD_KEY,
+                                TSIGContext.STATE_SENT_REQUEST)
+
+    # According to RFC2845 4.6, if TSIG verification fails the client
+    # should discard that message and wait for another signed response.
+    # This test emulates that situation.
+    def test_badsig_then_validate(self):
+        fix_current_time(0x4da8877a)
+
+        self.createMessageAndSign(self.qid, self.test_name, self.tsig_ctx)
+        self.createMessageFromFile("tsig_verify4.wire")
+
+        self.commonVerifyChecks(self.tsig_ctx, self.message.get_tsig_record(),
+                                self.received_data, TSIGError.BAD_SIG,
+                                TSIGContext.STATE_SENT_REQUEST)
+
+        self.createMessageFromFile("tsig_verify5.wire")
+        self.commonVerifyChecks(self.tsig_ctx, self.message.get_tsig_record(),
+                                self.received_data, TSIGError.NOERROR,
+                                TSIGContext.STATE_VERIFIED_RESPONSE)
+
+    # Similar to the previous test, but the first response doesn't contain
+    # TSIG.
+    def test_nosig_then_validate(self):
+        fix_current_time(0x4da8877a)
+        self.createMessageAndSign(self.qid, self.test_name, self.tsig_ctx)
+
+        self.commonVerifyChecks(self.tsig_ctx, None, DUMMY_DATA,
+                           TSIGError.FORMERR, TSIGContext.STATE_SENT_REQUEST)
+
+        self.createMessageFromFile("tsig_verify5.wire")
+        self.commonVerifyChecks(self.tsig_ctx, self.message.get_tsig_record(),
+                                self.received_data, TSIGError.NOERROR,
+                                TSIGContext.STATE_VERIFIED_RESPONSE)
+
+    # Similar to the previous test, but the first response results in BADTIME.
+    def test_badtime_then_validate(self):
+        fix_current_time(0x4da8877a)
+        tsig = self.createMessageAndSign(self.qid, self.test_name,
+                                         self.tsig_ctx)
+
+        # "advance the clock" and try validating, which should fail due to
+        # BADTIME
+        fix_current_time(0x4da8877a + 600)
+        self.commonVerifyChecks(self.tsig_ctx, tsig, DUMMY_DATA,
+                           TSIGError.BAD_TIME, TSIGContext.STATE_SENT_REQUEST)
+
+        # revert the clock again.
+        fix_current_time(0x4da8877a)
+        self.createMessageFromFile("tsig_verify5.wire")
+        self.commonVerifyChecks(self.tsig_ctx, self.message.get_tsig_record(),
+                                self.received_data, TSIGError.NOERROR,
+                                TSIGContext.STATE_VERIFIED_RESPONSE)
+
+    # We don't allow empty MAC unless the TSIG error is BADSIG or BADKEY.
+    def test_empty_mac(self):
+        fix_current_time(0x4da8877a)
+
+        self.createMessageFromFile("tsig_verify7.wire")
+
+        self.commonVerifyChecks(self.tsig_verify_ctx,
+                                self.message.get_tsig_record(),
+                                self.received_data,
+                                TSIGError.BAD_SIG,
+                                TSIGContext.STATE_RECEIVED_REQUEST)
+
+        # If the empty MAC comes with a BADKEY error, the error is passed
+        # transparently.
+        self.createMessageFromFile("tsig_verify8.wire")
+        self.commonVerifyChecks(self.tsig_verify_ctx,
+                                self.message.get_tsig_record(),
+                                self.received_data,
+                                TSIGError.BAD_KEY,
+                                TSIGContext.STATE_RECEIVED_REQUEST)
+
+    # Once the context is used for sending a signed response, it shouldn't
+    # be used for further verification.
+    def test_verify_after_sendresponse(self):
+        fix_current_time(0x4da8877a)
+
+        self.createMessageFromFile("message_toWire2.wire")
+        self.tsig_verify_ctx.verify(self.message.get_tsig_record(),
+                                    self.received_data)
+        self.assertEqual(TSIGContext.STATE_RECEIVED_REQUEST,
+                         self.tsig_verify_ctx.get_state())
+        self.createMessageAndSign(self.qid, self.test_name,
+                                  self.tsig_verify_ctx,
+                                  QR_FLAG|AA_FLAG|RD_FLAG, RRType.A(),
+                                  "192.0.2.1")
+        self.assertEqual(TSIGContext.STATE_SENT_RESPONSE,
+                         self.tsig_verify_ctx.get_state())
+
+        # Now trying further verification.
+        self.createMessageFromFile("message_toWire2.wire")
+        self.assertRaises(TSIGContextError, self.tsig_verify_ctx.verify,
+                          self.message.get_tsig_record(), self.received_data)
+
+    # Likewise, once the context verifies a response, it shouldn't for
+    # signing any more.
+    def test_sign_after_verified(self):
+        fix_current_time(0x4da8877a)
+
+        self.createMessageAndSign(self.qid, self.test_name, self.tsig_ctx)
+        self.createMessageFromFile("tsig_verify5.wire")
+        self.tsig_ctx.verify(self.message.get_tsig_record(),
+                             self.received_data)
+        self.assertEqual(TSIGContext.STATE_VERIFIED_RESPONSE,
+                         self.tsig_ctx.get_state())
+
+        # Now trying further signing.
+        self.assertRaises(TSIGContextError, self.createMessageAndSign,
+                          self.qid, self.test_name, self.tsig_ctx)
+
+    # Too short MAC should be rejected.
+    # Note: when we implement RFC4635-based checks, the error code will
+    # (probably) be FORMERR.
+    def test_too_short_mac(self):
+        fix_current_time(0x4da8877a)
+        self.createMessageFromFile("tsig_verify10.wire")
+        self.commonVerifyChecks(self.tsig_verify_ctx,
+                                self.message.get_tsig_record(),
+                                self.received_data, TSIGError.BAD_SIG,
+                                TSIGContext.STATE_RECEIVED_REQUEST)
 
 if __name__ == '__main__':
     unittest.main()
diff --git a/src/lib/dns/python/tests/tsig_rdata_python_test.py b/src/lib/dns/python/tests/tsig_rdata_python_test.py
new file mode 100644
index 0000000..7b861d6
--- /dev/null
+++ b/src/lib/dns/python/tests/tsig_rdata_python_test.py
@@ -0,0 +1,30 @@
+# Copyright (C) 2011  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import unittest
+import sys
+from pydnspp import *
+
+class TSIGRdataTest(unittest.TestCase):
+    VALID_TEXT1 = "hmac-md5.sig-alg.reg.int. 1286779327 300 0 16020 BADKEY 0"
+    def test_from_string(self):
+        tsig = TSIG(self.VALID_TEXT1)
+        self.assertEqual(Name("hmac-md5.sig-alg.reg.int"),
+                         tsig.get_algorithm())
+        # check there's no leak in creating the name object:
+        self.assertEqual(1, sys.getrefcount(tsig.get_algorithm()))
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/src/lib/dns/python/tests/tsigrecord_python_test.py b/src/lib/dns/python/tests/tsigrecord_python_test.py
new file mode 100644
index 0000000..813a23b
--- /dev/null
+++ b/src/lib/dns/python/tests/tsigrecord_python_test.py
@@ -0,0 +1,44 @@
+# 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 sys
+from pydnspp import *
+
+class TSIGRecordTest(unittest.TestCase):
+    def setUp(self):
+        self.test_name = Name("www.example.com")
+        self.test_rdata = TSIG("hmac-md5.sig-alg.reg.int. 1302890362 " + \
+                                   "300 16 2tra2tra2tra2tra2tra2g== " + \
+                                   "11621 0 0")
+        self.test_record = TSIGRecord(self.test_name, self.test_rdata)
+
+    def test_getname(self):
+        self.assertEqual(self.test_name, self.test_record.get_name())
+        self.assertEqual(1, sys.getrefcount(self.test_record.get_name()))
+
+    def test_get_length(self):
+        # see the C++ test for the magic number
+        self.assertEqual(85, self.test_record.get_length())
+
+    def test_to_text(self):
+        expected_text = "www.example.com. 0 ANY TSIG " + \
+            "hmac-md5.sig-alg.reg.int. 1302890362 300 16 " + \
+            "2tra2tra2tra2tra2tra2g== 11621 NOERROR 0\n"
+        self.assertEqual(expected_text, self.test_record.to_text())
+        self.assertEqual(expected_text, str(self.test_record))
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/src/lib/dns/python/tsig_python.cc b/src/lib/dns/python/tsig_python.cc
index 7b6ba01..db93a08 100644
--- a/src/lib/dns/python/tsig_python.cc
+++ b/src/lib/dns/python/tsig_python.cc
@@ -12,9 +12,30 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#define PY_SSIZE_T_CLEAN        // need for "y#" below
+#include <Python.h>
+
+#include <string>
+#include <stdexcept>
+
+#include <exceptions/exceptions.h>
+
+#include <util/python/pycppwrapper_util.h>
+
 #include <dns/tsig.h>
 
+#include "pydnspp_common.h"
+#include "name_python.h"
+#include "tsigkey_python.h"
+#include "tsigerror_python.h"
+#include "tsigrecord_python.h"
+#include "tsig_python.h"
+
+using namespace std;
+using namespace isc;
+using namespace isc::util::python;
 using namespace isc::dns;
+using namespace isc::dns::python;
 
 //
 // Definition of the classes
@@ -24,12 +45,17 @@ using namespace isc::dns;
 // and static wrappers around the methods we export), a list of methods,
 // and a type description
 
+//
+// TSIGContext
+//
+
+// Trivial constructor.
+s_TSIGContext::s_TSIGContext() : cppobj(NULL) {
+}
+
 namespace {
-// The s_* Class simply covers one instantiation of the object
-class s_TSIGContext : public PyObject {
-public:
-    TSIGContext* tsig_ctx;
-};
+// Shortcut type which would be convenient for adding class variables safely.
+typedef CPPPyObjectContainer<s_TSIGContext, TSIGContext> TSIGContextContainer;
 
 //
 // We declare the functions here, the definitions are below
@@ -40,6 +66,12 @@ public:
 int TSIGContext_init(s_TSIGContext* self, PyObject* args);
 void TSIGContext_destroy(s_TSIGContext* self);
 
+// Class specific methods
+PyObject* TSIGContext_getState(s_TSIGContext* self);
+PyObject* TSIGContext_getError(s_TSIGContext* self);
+PyObject* TSIGContext_sign(s_TSIGContext* self, PyObject* args);
+PyObject* TSIGContext_verify(s_TSIGContext* self, PyObject* args);
+
 // These are the functions we export
 // For a minimal support, we don't need them.
 
@@ -50,18 +82,180 @@ void TSIGContext_destroy(s_TSIGContext* self);
 // 3. Argument type
 // 4. Documentation
 PyMethodDef TSIGContext_methods[] = {
+    { "get_state", reinterpret_cast<PyCFunction>(TSIGContext_getState),
+      METH_NOARGS,
+      "Return the current state of the context (mainly for tests)" },
+    { "get_error", reinterpret_cast<PyCFunction>(TSIGContext_getError),
+      METH_NOARGS,
+      "Return the TSIG error as a result of the latest verification" },
+    { "sign",
+      reinterpret_cast<PyCFunction>(TSIGContext_sign), METH_VARARGS,
+      "Sign a DNS message." },
+    { "verify",
+      reinterpret_cast<PyCFunction>(TSIGContext_verify), METH_VARARGS,
+      "Verify a DNS message." },
     { NULL, NULL, 0, NULL }
 };
 
+int
+TSIGContext_init(s_TSIGContext* self, PyObject* args) {
+    try {
+        // "From key" constructor
+        const s_TSIGKey* tsigkey_obj;
+        if (PyArg_ParseTuple(args, "O!", &tsigkey_type, &tsigkey_obj)) {
+            self->cppobj = new TSIGContext(*tsigkey_obj->cppobj);
+            return (0);
+        }
+
+        // "From key param + keyring" constructor
+        PyErr_Clear();
+        const s_Name* keyname_obj;
+        const s_Name* algname_obj;
+        const s_TSIGKeyRing* keyring_obj;
+        if (PyArg_ParseTuple(args, "O!O!O!", &name_type, &keyname_obj,
+                             &name_type, &algname_obj, &tsigkeyring_type,
+                             &keyring_obj)) {
+            self->cppobj = new TSIGContext(*keyname_obj->cppobj,
+                                           *algname_obj->cppobj,
+                                           *keyring_obj->cppobj);
+            return (0);
+        }
+    } catch (const exception& ex) {
+        const string ex_what = "Failed to construct TSIGContext object: " +
+            string(ex.what());
+        PyErr_SetString(po_IscException, ex_what.c_str());
+        return (-1);
+    } catch (...) {
+        PyErr_SetString(po_IscException,
+                        "Unexpected exception in constructing TSIGContext");
+        return (-1);
+    }
+
+    PyErr_SetString(PyExc_TypeError,
+                    "Invalid arguments to TSIGContext constructor");
+
+    return (-1);
+}
+
+void
+TSIGContext_destroy(s_TSIGContext* const self) {
+    delete self->cppobj;
+    self->cppobj = NULL;
+    Py_TYPE(self)->tp_free(self);
+}
+
+PyObject*
+TSIGContext_getState(s_TSIGContext* self) {
+    return (Py_BuildValue("I", self->cppobj->getState()));
+}
+
+PyObject*
+TSIGContext_getError(s_TSIGContext* self) {
+    try {
+        PyObjectContainer container(createTSIGErrorObject(
+                                        self->cppobj->getError()));
+        return (Py_BuildValue("O", container.get()));
+    } catch (const exception& ex) {
+        const string ex_what =
+            "Unexpectedly failed to get TSIGContext error: " +
+            string(ex.what());
+        PyErr_SetString(po_IscException, ex_what.c_str());
+    } catch (...) {
+        PyErr_SetString(po_IscException,
+                        "Unexpected exception in TSIGContext.get_error");
+    }
+    return (NULL);
+}
+
+PyObject*
+TSIGContext_sign(s_TSIGContext* self, PyObject* args) {
+    long qid = 0;
+    const char* mac;
+    Py_ssize_t mac_size;
+
+    if (PyArg_ParseTuple(args, "ly#", &qid, &mac, &mac_size)) {
+        if (qid < 0 || qid > 0xffff) {
+            PyErr_SetString(PyExc_ValueError,
+                            "TSIGContext.sign: QID out of range");
+            return (NULL);
+        }
+
+        try {
+            ConstTSIGRecordPtr record = self->cppobj->sign(qid, mac, mac_size);
+            return (createTSIGRecordObject(*record));
+        } catch (const TSIGContextError& ex) {
+            PyErr_SetString(po_TSIGContextError, ex.what());
+        } catch (const exception& ex) {
+            const string ex_what = "Unexpected failure in TSIG sign: " +
+                string(ex.what());
+            PyErr_SetString(po_IscException, ex_what.c_str());
+        } catch (...) {
+            PyErr_SetString(PyExc_SystemError,
+                            "Unexpected failure in TSIG sign");
+        }
+    } else {
+        PyErr_SetString(PyExc_TypeError,
+                        "Invalid arguments to TSIGContext.sign");
+    }
+
+    return (NULL);
+}
+
+PyObject*
+TSIGContext_verify(s_TSIGContext* self, PyObject* args) {
+    const char* data;
+    Py_ssize_t data_len;
+    s_TSIGRecord* py_record;
+    PyObject* py_maybe_none;
+    TSIGRecord* record;
+
+    if (PyArg_ParseTuple(args, "O!y#", &tsigrecord_type, &py_record,
+                         &data, &data_len)) {
+        record = py_record->cppobj;
+    } else if (PyArg_ParseTuple(args, "Oy#", &py_maybe_none, &data,
+                                &data_len)) {
+        record = NULL;
+    } else {
+        PyErr_SetString(PyExc_TypeError,
+                        "Invalid arguments to TSIGContext.verify");
+        return (NULL);
+    }
+    PyErr_Clear();
+
+    try {
+        const TSIGError error = self->cppobj->verify(record, data, data_len);
+        return (createTSIGErrorObject(error));
+    } catch (const TSIGContextError& ex) {
+        PyErr_SetString(po_TSIGContextError, ex.what());
+    } catch (const InvalidParameter& ex) {
+        PyErr_SetString(po_InvalidParameter, ex.what());
+    } catch (const exception& ex) {
+        const string ex_what = "Unexpected failure in TSIG verify: " +
+            string(ex.what());
+        PyErr_SetString(po_IscException, ex_what.c_str());
+    } catch (...) {
+        PyErr_SetString(PyExc_SystemError, "Unexpected failure in TSIG verify");
+    }
+
+    return (NULL);
+}
+} // end of unnamed namespace
+
+namespace isc {
+namespace dns {
+namespace python {
+// Definition of class specific exception(s)
+PyObject* po_TSIGContextError;
+
 // This defines the complete type for reflection in python and
-// parsing of PyObject* to s_EDNS
+// parsing of PyObject* to s_TSIGContext
 // Most of the functions are not actually implemented and NULL here.
-PyTypeObject tsig_context_type = {
+PyTypeObject tsigcontext_type = {
     PyVarObject_HEAD_INIT(NULL, 0)
-    "libdns_python.TSIGContext",
-    sizeof(s_TSIGContext),              // tp_basicsize
+    "pydnspp.TSIGContext",
+    sizeof(s_TSIGContext),                 // tp_basicsize
     0,                                  // tp_itemsize
-    (destructor)TSIGContext_destroy,    // tp_dealloc
+    reinterpret_cast<destructor>(TSIGContext_destroy),       // tp_dealloc
     NULL,                               // tp_print
     NULL,                               // tp_getattr
     NULL,                               // tp_setattr
@@ -76,16 +270,22 @@ PyTypeObject tsig_context_type = {
     NULL,                               // tp_getattro
     NULL,                               // tp_setattro
     NULL,                               // tp_as_buffer
-    Py_TPFLAGS_DEFAULT,                 // tp_flags
-    "The TSIGContext class maintains a context of a signed session of "
-    "DNS transactions by TSIG.",
+
+    // We allow the python version of TSIGContext to act as a base class.
+    // From pure design point of view, this is wrong because it's not intended
+    // to be inherited.  However, cryptographic operations are generally
+    // difficult to test, so it would be very advantageous if we can define
+    // a mock context class.
+    Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, // tp_flags
+
+    "The TSIGContext class objects is...(COMPLETE THIS)",
     NULL,                               // tp_traverse
     NULL,                               // tp_clear
-    NULL,                               // tp_richcompare
+    NULL, // tp_richcompare
     0,                                  // tp_weaklistoffset
     NULL,                               // tp_iter
     NULL,                               // tp_iternext
-    TSIGContext_methods,                // tp_methods
+    TSIGContext_methods,                   // tp_methods
     NULL,                               // tp_members
     NULL,                               // tp_getset
     NULL,                               // tp_base
@@ -93,7 +293,7 @@ PyTypeObject tsig_context_type = {
     NULL,                               // tp_descr_get
     NULL,                               // tp_descr_set
     0,                                  // tp_dictoffset
-    (initproc)TSIGContext_init,         // tp_init
+    reinterpret_cast<initproc>(TSIGContext_init),            // tp_init
     NULL,                               // tp_alloc
     PyType_GenericNew,                  // tp_new
     NULL,                               // tp_free
@@ -107,50 +307,58 @@ PyTypeObject tsig_context_type = {
     0                                   // tp_version_tag
 };
 
-int
-TSIGContext_init(s_TSIGContext* self, PyObject* args) {
-    const s_TSIGKey* tsigkey_obj;
-
-    try {
-        if (PyArg_ParseTuple(args, "O!", &tsigkey_type, &tsigkey_obj)) {
-            self->tsig_ctx = new TSIGContext(*tsigkey_obj->tsigkey);
-            return (0);
-        }
-    } catch (...) {
-        PyErr_SetString(po_IscException, "Unexpected exception");
-        return (-1);
-    }
-
-    PyErr_Clear();
-    PyErr_SetString(PyExc_TypeError,
-                    "Invalid arguments to TSIGContext constructor");
-
-    return (-1);
-}
-
-void
-TSIGContext_destroy(s_TSIGContext* const self) {
-    delete self->tsig_ctx;
-    self->tsig_ctx = NULL;
-    Py_TYPE(self)->tp_free(self);
-}
-
 // Module Initialization, all statics are initialized here
 bool
 initModulePart_TSIGContext(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(&tsig_context_type) < 0) {
+    if (PyType_Ready(&tsigcontext_type) < 0) {
         return (false);
     }
-    Py_INCREF(&tsig_context_type);
-    void* p = &tsig_context_type;
-    PyModule_AddObject(mod, "TSIGContext", static_cast<PyObject*>(p));
+    void* p = &tsigcontext_type;
+    if (PyModule_AddObject(mod, "TSIGContext",
+                           static_cast<PyObject*>(p)) < 0) {
+        return (false);
+    }
+    Py_INCREF(&tsigcontext_type);
+
+    try {
+        // Class specific exceptions
+        po_TSIGContextError = PyErr_NewException("pydnspp.TSIGContextError",
+                                                 po_IscException, NULL);
+        PyObjectContainer(po_TSIGContextError).installToModule(
+            mod, "TSIGContextError");
+
+        // Constant class variables
+        installClassVariable(tsigcontext_type, "STATE_INIT",
+                             Py_BuildValue("I", TSIGContext::INIT));
+        installClassVariable(tsigcontext_type, "STATE_SENT_REQUEST",
+                             Py_BuildValue("I", TSIGContext::SENT_REQUEST));
+        installClassVariable(tsigcontext_type, "STATE_RECEIVED_REQUEST",
+                             Py_BuildValue("I", TSIGContext::RECEIVED_REQUEST));
+        installClassVariable(tsigcontext_type, "STATE_SENT_RESPONSE",
+                             Py_BuildValue("I", TSIGContext::SENT_RESPONSE));
+        installClassVariable(tsigcontext_type, "STATE_VERIFIED_RESPONSE",
+                             Py_BuildValue("I",
+                                           TSIGContext::VERIFIED_RESPONSE));
 
-    addClassVariable(tsig_context_type, "DEFAULT_FUDGE",
-                     Py_BuildValue("H", TSIGContext::DEFAULT_FUDGE));
+        installClassVariable(tsigcontext_type, "DEFAULT_FUDGE",
+                             Py_BuildValue("H", TSIGContext::DEFAULT_FUDGE));
+    } catch (const exception& ex) {
+        const string ex_what =
+            "Unexpected failure in TSIGContext initialization: " +
+            string(ex.what());
+        PyErr_SetString(po_IscException, ex_what.c_str());
+        return (false);
+    } catch (...) {
+        PyErr_SetString(PyExc_SystemError,
+                        "Unexpected failure in TSIGContext initialization");
+        return (false);
+    }
 
     return (true);
 }
-} // end of anonymous namespace
+} // namespace python
+} // namespace dns
+} // namespace isc
diff --git a/src/lib/dns/python/tsig_python.h b/src/lib/dns/python/tsig_python.h
new file mode 100644
index 0000000..f9b4f7b
--- /dev/null
+++ b/src/lib/dns/python/tsig_python.h
@@ -0,0 +1,47 @@
+// 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_TSIGCONTEXT_H
+#define __PYTHON_TSIGCONTEXT_H 1
+
+#include <Python.h>
+
+namespace isc {
+namespace dns {
+class TSIGContext;
+
+namespace python {
+
+// The s_* Class simply covers one instantiation of the object
+class s_TSIGContext : public PyObject {
+public:
+    s_TSIGContext();
+    TSIGContext* cppobj;
+};
+
+extern PyTypeObject tsigcontext_type;
+
+// Class specific exceptions
+extern PyObject* po_TSIGContextError;
+
+bool initModulePart_TSIGContext(PyObject* mod);
+
+} // namespace python
+} // namespace dns
+} // namespace isc
+#endif // __PYTHON_TSIGCONTEXT_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/python/tsig_rdata_python.cc b/src/lib/dns/python/tsig_rdata_python.cc
new file mode 100644
index 0000000..4e4f287
--- /dev/null
+++ b/src/lib/dns/python/tsig_rdata_python.cc
@@ -0,0 +1,369 @@
+// 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 <string>
+#include <stdexcept>
+
+#include <util/python/pycppwrapper_util.h>
+
+#include <dns/rdataclass.h>
+
+#include "pydnspp_common.h"
+#include "pydnspp_towire.h"
+#include "name_python.h"
+#include "tsig_rdata_python.h"
+
+using namespace std;
+using namespace isc::util::python;
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+using namespace isc::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
+
+//
+// TSIG RDATA
+//
+
+// Trivial constructor.
+s_TSIG::s_TSIG() : cppobj(NULL) {
+}
+
+namespace {
+// Shortcut type which would be convenient for adding class variables safely.
+typedef CPPPyObjectContainer<s_TSIG, any::TSIG> TSIGContainer;
+
+//
+// We declare the functions here, the definitions are below
+// the type definition of the object, since both can use the other
+//
+
+// General creation and destruction
+int TSIG_init(s_TSIG* self, PyObject* args);
+void TSIG_destroy(s_TSIG* self);
+
+// These are the functions we export
+// ADD/REMOVE/MODIFY THE FOLLOWING AS APPROPRIATE FOR THE ACTUAL CLASS.
+//
+PyObject* TSIG_toText(const s_TSIG* const self);
+PyObject* TSIG_getAlgorithm(const s_TSIG* const self);
+PyObject* TSIG_getTimeSigned(const s_TSIG* const self);
+PyObject* TSIG_getFudge(const s_TSIG* const self);
+PyObject* TSIG_getOriginalID(const s_TSIG* const self);
+PyObject* TSIG_getError(const s_TSIG* const self);
+PyObject* TSIG_getMAC(const s_TSIG* const self);
+PyObject* TSIG_getOtherData(const s_TSIG* const self);
+PyObject* TSIG_str(PyObject* self);
+PyObject* TSIG_richcmp(const s_TSIG* const self,
+                       const s_TSIG* const other, int op);
+PyObject* TSIG_toWire(const s_TSIG* self, PyObject* args);
+
+// These are the functions we export
+// For a minimal support, we don't need them.
+
+// 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 TSIG_methods[] = {
+    { "get_algorithm", reinterpret_cast<PyCFunction>(TSIG_getAlgorithm),
+      METH_NOARGS,
+      "Return the algorithm name." },
+    { "get_timesigned", reinterpret_cast<PyCFunction>(TSIG_getTimeSigned),
+      METH_NOARGS,
+      "Return the value of the Time Signed field. "
+      "The returned value does not exceed 2^48-1."
+    },
+    { "get_fudge", reinterpret_cast<PyCFunction>(TSIG_getFudge),
+      METH_NOARGS,
+      "Return the value of the Fudge field." },
+    { "get_original_id", reinterpret_cast<PyCFunction>(TSIG_getOriginalID),
+      METH_NOARGS,
+      "Return the value of the Original ID field." },
+    { "get_error", reinterpret_cast<PyCFunction>(TSIG_getError),
+      METH_NOARGS,
+      "Return the value of the Error field." },
+    { "get_mac", reinterpret_cast<PyCFunction>(TSIG_getMAC),
+      METH_NOARGS,
+      "Return the value of the MAC field."
+      "If it's empty, return None." },
+    { "get_other_data", reinterpret_cast<PyCFunction>(TSIG_getOtherData),
+      METH_NOARGS,
+      "Return the value of the Other Data field."
+      "If it's empty, return None." },
+    { "to_text", reinterpret_cast<PyCFunction>(TSIG_toText), METH_NOARGS,
+      "Returns the text representation" },
+    { "to_wire", reinterpret_cast<PyCFunction>(TSIG_toWire), METH_VARARGS,
+      "Converts the TSIG object to wire format.\n"
+      "The argument can be either a MessageRenderer or an object that "
+      "implements the sequence interface. If the object is mutable "
+      "(for instance a bytearray()), the wire data is added in-place.\n"
+      "If it is not (for instance a bytes() object), a new object is "
+      "returned" },
+    { NULL, NULL, 0, NULL }
+};
+
+int
+TSIG_init(s_TSIG* self, PyObject* args) {
+    try {
+        // constructor from string
+        const char* rdata_str;
+        if (PyArg_ParseTuple(args, "s", &rdata_str)) {
+            self->cppobj = new any::TSIG(string(rdata_str));
+            return (0);
+        }
+    } catch (const exception& ex) {
+        const string ex_what = "Failed to construct TSIG object: " +
+            string(ex.what());
+        PyErr_SetString(po_IscException, ex_what.c_str());
+        return (-1);
+    } catch (...) {
+        PyErr_SetString(po_IscException,
+                        "Unexpected exception in constructing TSIG");
+        return (-1);
+    }
+
+    PyErr_SetString(PyExc_TypeError,
+                    "Invalid arguments to TSIG constructor");
+
+    return (-1);
+}
+
+void
+TSIG_destroy(s_TSIG* const self) {
+    delete self->cppobj;
+    self->cppobj = NULL;
+    Py_TYPE(self)->tp_free(self);
+}
+
+PyObject*
+TSIG_getAlgorithm(const s_TSIG* const self) {
+    try {
+        return (createNameObject(self->cppobj->getAlgorithm()));
+    } catch (const exception& ex) {
+        const string ex_what =
+            "Failed to get TSIG algorithm: " + string(ex.what());
+        PyErr_SetString(po_IscException, ex_what.c_str());
+    } catch (...) {
+        PyErr_SetString(PyExc_SystemError, "Unexpected failure in "
+                        "getting TSIG algorithm");
+    }
+    return (NULL);
+}
+
+PyObject*
+TSIG_getTimeSigned(const s_TSIG* const self) {
+    return (Py_BuildValue("K", self->cppobj->getTimeSigned()));
+}
+
+PyObject*
+TSIG_getFudge(const s_TSIG* const self) {
+    return (Py_BuildValue("H", self->cppobj->getFudge()));
+}
+
+PyObject*
+TSIG_getOriginalID(const s_TSIG* const self) {
+    return (Py_BuildValue("H", self->cppobj->getOriginalID()));
+}
+
+PyObject*
+TSIG_getError(const s_TSIG* const self) {
+    return (Py_BuildValue("H", self->cppobj->getError()));
+}
+
+PyObject*
+TSIG_getMAC(const s_TSIG* const self) {
+    return (Py_BuildValue("y#", self->cppobj->getMAC(),
+                          self->cppobj->getMACSize()));
+}
+
+PyObject*
+TSIG_getOtherData(const s_TSIG* const self) {
+    return (Py_BuildValue("y#", self->cppobj->getOtherData(),
+                          self->cppobj->getOtherLen()));
+}
+
+PyObject*
+TSIG_toText(const s_TSIG* const self) {
+    try {
+        // toText() could throw, so we need to catch any exceptions below.
+        return (Py_BuildValue("s", self->cppobj->toText().c_str()));
+    } catch (const exception& ex) {
+        const string ex_what =
+            "Failed to convert TSIG object to text: " +
+            string(ex.what());
+        PyErr_SetString(po_IscException, ex_what.c_str());
+    } catch (...) {
+        PyErr_SetString(PyExc_SystemError, "Unexpected failure in "
+                        "converting TSIG object to text");
+    }
+    return (NULL);
+}
+
+PyObject*
+TSIG_str(PyObject* self) {
+    // Simply call the to_text method we already defined
+    return (PyObject_CallMethod(self, const_cast<char*>("to_text"),
+                                const_cast<char*>("")));
+}
+
+PyObject*
+TSIG_toWire(const s_TSIG* const self, PyObject* args) {
+    typedef any::TSIG TSIGRdata;
+    return (toWireWrapper<s_TSIG, TSIGRdata, ToWireCallVoid<const TSIGRdata> >(
+                self, args));
+}
+
+PyObject* 
+TSIG_richcmp(const s_TSIG* const self,
+                   const s_TSIG* const other,
+                   const int op)
+{
+    bool c = false;
+
+    // Check for null and if the types match. If different type,
+    // simply return False
+    if (other == NULL || (self->ob_type != other->ob_type)) {
+        Py_RETURN_FALSE;
+    }
+
+    // Only equals and not equals here, unorderable type
+    const int cmp = self->cppobj->compare(*other->cppobj);
+    switch (op) {
+    case Py_EQ:
+        c = (cmp == 0);
+        break;
+    case Py_NE:
+        c = (cmp != 0);
+        break;
+    case Py_GT:
+        c = (cmp > 0);
+        break;
+    case Py_GE:
+        c = (cmp >= 0);
+        break;
+    case Py_LT:
+        c = (cmp < 0);
+        break;
+    case Py_LE:
+        c = (cmp <= 0);
+        break;
+    default:
+        PyErr_SetString(PyExc_IndexError,
+                        "Unhandled rich comparison operator for TSIG");
+        return (NULL);
+    }
+    if (c) {
+        Py_RETURN_TRUE;
+    } else {
+        Py_RETURN_FALSE;
+    }
+}
+} // end of unnamed namespace
+
+namespace isc {
+namespace dns {
+namespace python {
+// This defines the complete type for reflection in python and
+// parsing of PyObject* to s_TSIG
+// Most of the functions are not actually implemented and NULL here.
+PyTypeObject tsig_type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "pydnspp.TSIG",
+    sizeof(s_TSIG),                 // tp_basicsize
+    0,                                  // tp_itemsize
+    reinterpret_cast<destructor>(TSIG_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
+    TSIG_str,                       // tp_str
+    NULL,                               // tp_getattro
+    NULL,                               // tp_setattro
+    NULL,                               // tp_as_buffer
+    Py_TPFLAGS_DEFAULT,                 // tp_flags
+    "The TSIG class objects represents the TSIG RDATA as defined in RFC2845.",
+    NULL,                               // tp_traverse
+    NULL,                               // tp_clear
+    reinterpret_cast<richcmpfunc>(TSIG_richcmp), // tp_richcompare
+    0,                                  // tp_weaklistoffset
+    NULL,                               // tp_iter
+    NULL,                               // tp_iternext
+    TSIG_methods,                   // tp_methods
+    NULL,                               // tp_members
+    NULL,                               // tp_getset
+    // At the moment, we leave tp_base NULL as we won't use this class
+    // in a polymorphic way for our immediate need.
+    NULL,                               // tp_base
+    NULL,                               // tp_dict
+    NULL,                               // tp_descr_get
+    NULL,                               // tp_descr_set
+    0,                                  // tp_dictoffset
+    reinterpret_cast<initproc>(TSIG_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
+};
+
+// Module Initialization, all statics are initialized here
+bool
+initModulePart_TSIG(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(&tsig_type) < 0) {
+        return (false);
+    }
+    void* p = &tsig_type;
+    if (PyModule_AddObject(mod, "TSIG", static_cast<PyObject*>(p)) < 0) {
+        return (false);
+    }
+    Py_INCREF(&tsig_type);
+
+    return (true);
+}
+
+PyObject*
+createTSIGObject(const any::TSIG& source) {
+    TSIGContainer container = PyObject_New(s_TSIG, &tsig_type);
+    container.set(new any::TSIG(source));
+    return (container.release());
+}
+} // namespace python
+} // namespace dns
+} // namespace isc
diff --git a/src/lib/dns/python/tsig_rdata_python.h b/src/lib/dns/python/tsig_rdata_python.h
new file mode 100644
index 0000000..e5e0c6c
--- /dev/null
+++ b/src/lib/dns/python/tsig_rdata_python.h
@@ -0,0 +1,57 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __PYTHON_TSIG_H
+#define __PYTHON_TSIG_H 1
+
+#include <Python.h>
+
+namespace isc {
+namespace dns {
+namespace rdata {
+namespace any {
+class TSIG;
+}
+}
+
+namespace python {
+
+// The s_* Class simply covers one instantiation of the object
+class s_TSIG : public PyObject {
+public:
+    s_TSIG();
+    const rdata::any::TSIG* cppobj;
+};
+
+extern PyTypeObject tsig_type;
+
+bool initModulePart_TSIG(PyObject* mod);
+
+/// This is A simple shortcut to create a python TSIG object (in the
+/// form of a pointer to PyObject) with minimal exception safety.
+/// On success, it returns a valid pointer to PyObject with a reference
+/// counter of 1; if something goes wrong it throws an exception (it never
+/// returns a NULL pointer).
+/// This function is expected to be called with in a try block
+/// followed by necessary setup for python exception.
+PyObject* createTSIGObject(const rdata::any::TSIG& source);
+
+} // namespace python
+} // namespace dns
+} // namespace isc
+#endif // __PYTHON_TSIG_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/python/tsigerror_python.cc b/src/lib/dns/python/tsigerror_python.cc
index e973772..0ad4716 100644
--- a/src/lib/dns/python/tsigerror_python.cc
+++ b/src/lib/dns/python/tsigerror_python.cc
@@ -106,6 +106,7 @@ TSIGError_init(s_TSIGError* self, PyObject* args) {
         }
 
         // Constructor from Rcode
+        PyErr_Clear();
         s_Rcode* py_rcode;
         if (PyArg_ParseTuple(args, "O!", &rcode_type, &py_rcode)) {
             self->cppobj = new TSIGError(*py_rcode->cppobj);
@@ -239,7 +240,7 @@ namespace python {
 // Most of the functions are not actually implemented and NULL here.
 PyTypeObject tsigerror_type = {
     PyVarObject_HEAD_INIT(NULL, 0)
-    "libdns_python.TSIGError",
+    "pydnspp.TSIGError",
     sizeof(s_TSIGError),                 // tp_basicsize
     0,                                  // tp_itemsize
     reinterpret_cast<destructor>(TSIGError_destroy),       // tp_dealloc
@@ -357,6 +358,13 @@ initModulePart_TSIGError(PyObject* mod) {
 
     return (true);
 }
+
+PyObject*
+createTSIGErrorObject(const TSIGError& source) {
+    TSIGErrorContainer container = PyObject_New(s_TSIGError, &tsigerror_type);
+    container.set(new TSIGError(source));
+    return (container.release());
+}
 } // namespace python
 } // namespace dns
 } // namespace isc
diff --git a/src/lib/dns/python/tsigerror_python.h b/src/lib/dns/python/tsigerror_python.h
index f0c66cc..735a480 100644
--- a/src/lib/dns/python/tsigerror_python.h
+++ b/src/lib/dns/python/tsigerror_python.h
@@ -34,6 +34,14 @@ extern PyTypeObject tsigerror_type;
 
 bool initModulePart_TSIGError(PyObject* mod);
 
+/// This is A simple shortcut to create a python TSIGError object (in the
+/// form of a pointer to PyObject) with minimal exception safety.
+/// On success, it returns a valid pointer to PyObject with a reference
+/// counter of 1; if something goes wrong it throws an exception (it never
+/// returns a NULL pointer).
+/// This function is expected to be called with in a try block
+/// followed by necessary setup for python exception.
+PyObject* createTSIGErrorObject(const TSIGError& source);
 } // namespace python
 } // namespace dns
 } // namespace isc
diff --git a/src/lib/dns/python/tsigkey_python.cc b/src/lib/dns/python/tsigkey_python.cc
index 4ca7bcd..875a2c1 100644
--- a/src/lib/dns/python/tsigkey_python.cc
+++ b/src/lib/dns/python/tsigkey_python.cc
@@ -12,12 +12,24 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-#include <new>
+#include <Python.h>
 
+#include <stdexcept>
+
+#include <util/python/pycppwrapper_util.h>
+
+#include <dns/name.h>
 #include <dns/tsigkey.h>
+#include <dns/rdata.h>
 
+#include "pydnspp_common.h"
+#include "name_python.h"
+#include "tsigkey_python.h"
+
+using namespace std;
+using namespace isc::util::python;
 using namespace isc::dns;
-using namespace isc::dns::rdata;
+using namespace isc::dns::python;
 
 //
 // Definition of the classes
@@ -27,19 +39,15 @@ using namespace isc::dns::rdata;
 // and static wrappers around the methods we export), a list of methods,
 // and a type description
 
-namespace {
 //
 // TSIGKey
 //
 
 // The s_* Class simply covers one instantiation of the object
 
-class s_TSIGKey : public PyObject {
-public:
-    s_TSIGKey() : tsigkey(NULL) {}
-    TSIGKey* tsigkey;
-};
+s_TSIGKey::s_TSIGKey() : cppobj(NULL) {}
 
+namespace {
 //
 // We declare the functions here, the definitions are below
 // the type definition of the object, since both can use the other
@@ -78,12 +86,105 @@ PyMethodDef TSIGKey_methods[] = {
     { NULL, NULL, 0, NULL }
 };
 
+int
+TSIGKey_init(s_TSIGKey* self, PyObject* args) {
+    try {
+        const char* str;
+        if (PyArg_ParseTuple(args, "s", &str)) {
+            self->cppobj = new TSIGKey(str);
+            return (0);
+        }
+
+        PyErr_Clear();
+        const s_Name* key_name;
+        const s_Name* algorithm_name;
+        PyObject* bytes_obj;
+        const char* secret;
+        Py_ssize_t secret_len;
+        if (PyArg_ParseTuple(args, "O!O!O", &name_type, &key_name,
+                             &name_type, &algorithm_name, &bytes_obj) &&
+            PyObject_AsCharBuffer(bytes_obj, &secret, &secret_len) == 0) {
+            if (secret_len == 0) {
+                secret = NULL;
+            }
+            self->cppobj = new TSIGKey(*key_name->cppobj,
+                                       *algorithm_name->cppobj,
+                                       secret, secret_len);
+            return (0);
+        }
+    } catch (const isc::InvalidParameter& ex) {
+        PyErr_SetString(po_InvalidParameter, ex.what());
+        return (-1);
+    } catch (...) {
+        PyErr_SetString(po_IscException, "Unexpected exception");
+        return (-1);
+    }
+
+    PyErr_Clear();
+    PyErr_SetString(PyExc_TypeError,
+                    "Invalid arguments to TSIGKey constructor");
+
+    return (-1);
+}
+
+void
+TSIGKey_destroy(s_TSIGKey* const self) {
+    delete self->cppobj;
+    self->cppobj = NULL;
+    Py_TYPE(self)->tp_free(self);
+}
+
+PyObject*
+TSIGKey_getKeyName(const s_TSIGKey* const self) {
+    try {
+        return (createNameObject(self->cppobj->getKeyName()));
+    } catch (const exception& ex) {
+        const string ex_what =
+            "Failed to get key name of TSIGKey: " + string(ex.what());
+        PyErr_SetString(po_IscException, ex_what.c_str());
+    } catch (...) {
+        PyErr_SetString(PyExc_SystemError, "Unexpected failure in "
+                        "getting key name of TSIGKey");
+    }
+    return (NULL);
+}
+
+PyObject*
+TSIGKey_getAlgorithmName(const s_TSIGKey* const self) {
+    try {
+        return (createNameObject(self->cppobj->getAlgorithmName()));
+    } catch (const exception& ex) {
+        const string ex_what =
+            "Failed to get algorithm name of TSIGKey: " + string(ex.what());
+        PyErr_SetString(po_IscException, ex_what.c_str());
+    } catch (...) {
+        PyErr_SetString(PyExc_SystemError, "Unexpected failure in "
+                        "getting algorithm name of TSIGKey");
+    }
+    return (NULL);
+}
+
+PyObject*
+TSIGKey_getSecret(const s_TSIGKey* const self) {
+    return (Py_BuildValue("y#", self->cppobj->getSecret(),
+                          self->cppobj->getSecretLength()));
+}
+
+PyObject*
+TSIGKey_toText(const s_TSIGKey* self) {
+    return (Py_BuildValue("s", self->cppobj->toText().c_str()));
+}
+} // end of unnamed namespace
+
+namespace isc {
+namespace dns {
+namespace python {
 // This defines the complete type for reflection in python and
 // parsing of PyObject* to s_EDNS
 // Most of the functions are not actually implemented and NULL here.
 PyTypeObject tsigkey_type = {
     PyVarObject_HEAD_INIT(NULL, 0)
-    "libdns_python.TSIGKey",
+    "pydnspp.TSIGKey",
     sizeof(s_TSIGKey),                  // tp_basicsize
     0,                                  // tp_itemsize
     (destructor)TSIGKey_destroy,        // tp_dealloc
@@ -132,89 +233,6 @@ PyTypeObject tsigkey_type = {
     0                                   // tp_version_tag
 };
 
-// A helper function to build a python "Name" object with error handling
-// encapsulated.
-s_Name*
-createNameObject(const Name& source) {
-    s_Name* name = PyObject_New(s_Name, &name_type);
-    if (name == NULL) {
-        return (NULL);
-    }
-    name->name = new(nothrow) Name(source);
-    if (name->name == NULL) {
-        Py_DECREF(name);
-        PyErr_SetString(po_IscException, "Allocating Name object failed");
-        return (NULL);
-    }
-    return (name);
-}
-
-int
-TSIGKey_init(s_TSIGKey* self, PyObject* args) {
-    const char* str;
-
-    const s_Name* key_name;
-    const s_Name* algorithm_name;
-    PyObject* bytes_obj;
-    const char* secret;
-    Py_ssize_t secret_len;
-
-
-    try {
-        if (PyArg_ParseTuple(args, "s", &str)) {
-            self->tsigkey = new TSIGKey(str);
-            return (0);
-        } else if (PyArg_ParseTuple(args, "O!O!O", &name_type, &key_name,
-                         &name_type, &algorithm_name, &bytes_obj) &&
-            PyObject_AsCharBuffer(bytes_obj, &secret, &secret_len) != -1) {
-                self->tsigkey = new TSIGKey(*key_name->name,
-                                            *algorithm_name->name,
-                                            secret, secret_len);
-            return (0);
-        }
-    } catch (const isc::InvalidParameter& ex) {
-        PyErr_SetString(po_InvalidParameter, ex.what());
-        return (-1);
-    } catch (...) {
-        PyErr_SetString(po_IscException, "Unexpected exception");
-        return (-1);
-    }
-
-    PyErr_Clear();
-    PyErr_SetString(PyExc_TypeError,
-                    "Invalid arguments to TSIGKey constructor");
-
-    return (-1);
-}
-
-void
-TSIGKey_destroy(s_TSIGKey* const self) {
-    delete self->tsigkey;
-    self->tsigkey = NULL;
-    Py_TYPE(self)->tp_free(self);
-}
-
-PyObject*
-TSIGKey_getKeyName(const s_TSIGKey* const self) {
-    return (createNameObject(self->tsigkey->getKeyName()));
-}
-
-PyObject*
-TSIGKey_getAlgorithmName(const s_TSIGKey* const self) {
-    return (createNameObject(self->tsigkey->getAlgorithmName()));
-}
-
-PyObject*
-TSIGKey_getSecret(const s_TSIGKey* const self) {
-    return (Py_BuildValue("y#", self->tsigkey->getSecret(),
-                          self->tsigkey->getSecretLength()));
-}
-
-PyObject*
-TSIGKey_toText(const s_TSIGKey* self) {
-    return (Py_BuildValue("s", self->tsigkey->toText().c_str()));
-}
-
 // Module Initialization, all statics are initialized here
 bool
 initModulePart_TSIGKey(PyObject* mod) {
@@ -224,33 +242,37 @@ initModulePart_TSIGKey(PyObject* mod) {
     if (PyType_Ready(&tsigkey_type) < 0) {
         return (false);
     }
-    Py_INCREF(&tsigkey_type);
     void* p = &tsigkey_type;
     if (PyModule_AddObject(mod, "TSIGKey", static_cast<PyObject*>(p)) != 0) {
-        Py_DECREF(&tsigkey_type);
         return (false);
     }
+    Py_INCREF(&tsigkey_type);
 
-    s_Name* name;
-    if ((name = createNameObject(TSIGKey::HMACMD5_NAME())) == NULL) {
-        goto cleanup;
-    }
-    addClassVariable(tsigkey_type, "HMACMD5_NAME", name);
-    if ((name = createNameObject(TSIGKey::HMACSHA1_NAME())) == NULL) {
-        goto cleanup;
-    }
-    addClassVariable(tsigkey_type, "HMACSHA1_NAME", name);
-    if ((name = createNameObject(TSIGKey::HMACSHA256_NAME())) == NULL) {
-        goto cleanup;
+    try {
+        // Constant class variables
+        installClassVariable(tsigkey_type, "HMACMD5_NAME",
+                             createNameObject(TSIGKey::HMACMD5_NAME()));
+        installClassVariable(tsigkey_type, "HMACSHA1_NAME",
+                             createNameObject(TSIGKey::HMACSHA1_NAME()));
+        installClassVariable(tsigkey_type, "HMACSHA256_NAME",
+                             createNameObject(TSIGKey::HMACSHA256_NAME()));
+    } catch (const exception& ex) {
+        const string ex_what =
+            "Unexpected failure in TSIGKey initialization: " +
+            string(ex.what());
+        PyErr_SetString(po_IscException, ex_what.c_str());
+        return (false);
+    } catch (...) {
+        PyErr_SetString(PyExc_SystemError,
+                        "Unexpected failure in TSIGKey initialization");
+        return (false);
     }
-    addClassVariable(tsigkey_type, "HMACSHA256_NAME", name);
 
     return (true);
-
-  cleanup:
-    Py_DECREF(&tsigkey_type);
-    return (false);
 }
+} // namespace python
+} // namespace dns
+} // namespace isc
 //
 // End of TSIGKey
 //
@@ -263,12 +285,9 @@ initModulePart_TSIGKey(PyObject* mod) {
 
 // The s_* Class simply covers one instantiation of the object
 
-class s_TSIGKeyRing : public PyObject {
-public:
-    s_TSIGKeyRing() : keyring(NULL) {}
-    TSIGKeyRing* keyring;
-};
+s_TSIGKeyRing::s_TSIGKeyRing() : cppobj(NULL) {}
 
+namespace {
 //
 // We declare the functions here, the definitions are below
 // the type definition of the object, since both can use the other
@@ -296,56 +315,6 @@ PyMethodDef TSIGKeyRing_methods[] = {
     { NULL, NULL, 0, NULL }
 };
 
-PyTypeObject tsigkeyring_type = {
-    PyVarObject_HEAD_INIT(NULL, 0)
-    "libdns_python.TSIGKeyRing",
-    sizeof(s_TSIGKeyRing),              // tp_basicsize
-    0,                                  // tp_itemsize
-    (destructor)TSIGKeyRing_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
-    "A simple repository of a set of TSIGKey objects.",
-    NULL,                               // tp_traverse
-    NULL,                               // tp_clear
-    NULL,                               // tp_richcompare
-    0,                                  // tp_weaklistoffset
-    NULL,                               // tp_iter
-    NULL,                               // tp_iternext
-    TSIGKeyRing_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
-    (initproc)TSIGKeyRing_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
-};
-
 int
 TSIGKeyRing_init(s_TSIGKeyRing* self, PyObject* args) {
     if (!PyArg_ParseTuple(args, "")) {
@@ -355,8 +324,8 @@ TSIGKeyRing_init(s_TSIGKeyRing* self, PyObject* args) {
         return (-1);
     }
     
-    self->keyring = new(nothrow) TSIGKeyRing();
-    if (self->keyring == NULL) {
+    self->cppobj = new(nothrow) TSIGKeyRing();
+    if (self->cppobj == NULL) {
         PyErr_SetString(po_IscException, "Allocating TSIGKeyRing failed");
         return (-1);
     }
@@ -366,14 +335,14 @@ TSIGKeyRing_init(s_TSIGKeyRing* self, PyObject* args) {
 
 void
 TSIGKeyRing_destroy(s_TSIGKeyRing* self) {
-    delete self->keyring;
-    self->keyring = NULL;
+    delete self->cppobj;
+    self->cppobj = NULL;
     Py_TYPE(self)->tp_free(self);
 }
 
 PyObject*
 TSIGKeyRing_size(const s_TSIGKeyRing* const self) {
-    return (Py_BuildValue("I", self->keyring->size()));
+    return (Py_BuildValue("I", self->cppobj->size()));
 }
 
 PyObject*
@@ -383,7 +352,7 @@ TSIGKeyRing_add(const s_TSIGKeyRing* const self, PyObject* args) {
     if (PyArg_ParseTuple(args, "O!", &tsigkey_type, &tsigkey)) {
         try {
             const TSIGKeyRing::Result result =
-                self->keyring->add(*tsigkey->tsigkey);
+                self->cppobj->add(*tsigkey->cppobj);
             return (Py_BuildValue("I", result));
         } catch (...) {
             PyErr_SetString(po_IscException, "Unexpected exception");
@@ -403,7 +372,7 @@ TSIGKeyRing_remove(const s_TSIGKeyRing* self, PyObject* args) {
 
     if (PyArg_ParseTuple(args, "O!", &name_type, &key_name)) {
         const TSIGKeyRing::Result result =
-            self->keyring->remove(*key_name->name);
+            self->cppobj->remove(*key_name->cppobj);
         return (Py_BuildValue("I", result));
     }
 
@@ -421,14 +390,14 @@ TSIGKeyRing_find(const s_TSIGKeyRing* self, PyObject* args) {
     if (PyArg_ParseTuple(args, "O!O!", &name_type, &key_name,
                          &name_type, &algorithm_name)) {
         const TSIGKeyRing::FindResult result =
-            self->keyring->find(*key_name->name, *algorithm_name->name);
+            self->cppobj->find(*key_name->cppobj, *algorithm_name->cppobj);
         if (result.key != NULL) {
             s_TSIGKey* key = PyObject_New(s_TSIGKey, &tsigkey_type);
             if (key == NULL) {
                 return (NULL);
             }
-            key->tsigkey = new(nothrow) TSIGKey(*result.key);
-            if (key->tsigkey == NULL) {
+            key->cppobj = new(nothrow) TSIGKey(*result.key);
+            if (key->cppobj == NULL) {
                 Py_DECREF(key);
                 PyErr_SetString(po_IscException,
                                 "Allocating TSIGKey object failed");
@@ -442,6 +411,60 @@ TSIGKeyRing_find(const s_TSIGKeyRing* self, PyObject* args) {
 
     return (NULL);
 }
+} // end of unnamed namespace
+
+namespace isc {
+namespace dns {
+namespace python {
+PyTypeObject tsigkeyring_type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "pydnspp.TSIGKeyRing",
+    sizeof(s_TSIGKeyRing),              // tp_basicsize
+    0,                                  // tp_itemsize
+    (destructor)TSIGKeyRing_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
+    "A simple repository of a set of TSIGKey objects.",
+    NULL,                               // tp_traverse
+    NULL,                               // tp_clear
+    NULL,                               // tp_richcompare
+    0,                                  // tp_weaklistoffset
+    NULL,                               // tp_iter
+    NULL,                               // tp_iternext
+    TSIGKeyRing_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
+    (initproc)TSIGKeyRing_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_TSIGKeyRing(PyObject* mod) {
@@ -465,5 +488,6 @@ initModulePart_TSIGKeyRing(PyObject* mod) {
 
     return (true);
 }
-
-} // end of unnamed namespace
+} // namespace python
+} // namespace dns
+} // namespace isc
diff --git a/src/lib/dns/python/tsigkey_python.h b/src/lib/dns/python/tsigkey_python.h
new file mode 100644
index 0000000..51b3ae7
--- /dev/null
+++ b/src/lib/dns/python/tsigkey_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_TSIGKEY_H
+#define __PYTHON_TSIGKEY_H 1
+
+#include <Python.h>
+
+namespace isc {
+namespace dns {
+class TSIGKey;
+class TSIGKeyRing;
+
+namespace python {
+
+// The s_* Class simply covers one instantiation of the object
+class s_TSIGKey : public PyObject {
+public:
+    s_TSIGKey();
+    TSIGKey* cppobj;
+};
+
+class s_TSIGKeyRing : public PyObject {
+public:
+    s_TSIGKeyRing();
+    TSIGKeyRing* cppobj;
+};
+
+extern PyTypeObject tsigkey_type;
+extern PyTypeObject tsigkeyring_type;
+
+bool initModulePart_TSIGKey(PyObject* mod);
+bool initModulePart_TSIGKeyRing(PyObject* mod);
+
+} // namespace python
+} // namespace dns
+} // namespace isc
+#endif // __PYTHON_TSIGKEY_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/python/tsigrecord_python.cc b/src/lib/dns/python/tsigrecord_python.cc
new file mode 100644
index 0000000..8a78b5e
--- /dev/null
+++ b/src/lib/dns/python/tsigrecord_python.cc
@@ -0,0 +1,311 @@
+// 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 <string>
+#include <stdexcept>
+
+#include <util/python/pycppwrapper_util.h>
+
+#include <dns/tsigrecord.h>
+
+#include "pydnspp_common.h"
+#include "pydnspp_towire.h"
+#include "name_python.h"
+#include "tsig_rdata_python.h"
+#include "tsigrecord_python.h"
+
+using namespace std;
+using namespace isc::util::python;
+using namespace isc::dns;
+using namespace isc::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
+
+//
+// TSIGRecord
+//
+
+// Trivial constructor.
+s_TSIGRecord::s_TSIGRecord() : cppobj(NULL) {
+}
+
+namespace {
+// Shortcut type which would be convenient for adding class variables safely.
+typedef CPPPyObjectContainer<s_TSIGRecord, TSIGRecord> TSIGRecordContainer;
+
+//
+// We declare the functions here, the definitions are below
+// the type definition of the object, since both can use the other
+//
+
+// General creation and destruction
+int TSIGRecord_init(s_TSIGRecord* self, PyObject* args);
+void TSIGRecord_destroy(s_TSIGRecord* self);
+PyObject* TSIGRecord_toText(const s_TSIGRecord* const self);
+PyObject* TSIGRecord_str(PyObject* self);
+PyObject* TSIGRecord_toWire(const s_TSIGRecord* self, PyObject* args);
+PyObject* TSIGRecord_getName(const s_TSIGRecord* self);
+PyObject* TSIGRecord_getLength(const s_TSIGRecord* self);
+PyObject* TSIGRecord_getRdata(const s_TSIGRecord* self);
+
+// These are the functions we export
+// For a minimal support, we don't need them.
+
+// 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 TSIGRecord_methods[] = {
+    { "get_name", reinterpret_cast<PyCFunction>(TSIGRecord_getName),
+      METH_NOARGS,
+      "Return the owner name of the TSIG RR, which is the TSIG key name" },
+    { "get_length", reinterpret_cast<PyCFunction>(TSIGRecord_getLength),
+      METH_NOARGS,
+      "Return the length of the TSIG record" },
+    { "get_rdata", reinterpret_cast<PyCFunction>(TSIGRecord_getRdata),
+      METH_NOARGS,
+      "Return the RDATA of the TSIG RR" },
+    { "to_text", reinterpret_cast<PyCFunction>(TSIGRecord_toText), METH_NOARGS,
+      "Returns the text representation" },
+    { "to_wire", reinterpret_cast<PyCFunction>(TSIGRecord_toWire),
+      METH_VARARGS,
+      "Converts the TSIGRecord object to wire format.\n"
+      "The argument can be either a MessageRenderer or an object that "
+      "implements the sequence interface. If the object is mutable "
+      "(for instance a bytearray()), the wire data is added in-place.\n"
+      "If it is not (for instance a bytes() object), a new object is "
+      "returned" },
+    { NULL, NULL, 0, NULL }
+};
+
+int
+TSIGRecord_init(s_TSIGRecord* self, PyObject* args) {
+    try {
+        const s_Name* py_name;
+        const s_TSIG* py_tsig;
+        if (PyArg_ParseTuple(args, "O!O!", &name_type, &py_name,
+                             &tsig_type, &py_tsig)) {
+            self->cppobj = new TSIGRecord(*py_name->cppobj, *py_tsig->cppobj);
+            return (0);
+        }
+    } catch (const exception& ex) {
+        const string ex_what = "Failed to construct TSIGRecord object: " +
+            string(ex.what());
+        PyErr_SetString(po_IscException, ex_what.c_str());
+        return (-1);
+    } catch (...) {
+        PyErr_SetString(po_IscException,
+                        "Unexpected exception in constructing TSIGRecord");
+        return (-1);
+    }
+
+    PyErr_SetString(PyExc_TypeError,
+                    "Invalid arguments to TSIGRecord constructor");
+
+    return (-1);
+}
+
+// This is a template of typical code logic of python object destructor.
+// In many cases you can use it without modification, but check that carefully.
+void
+TSIGRecord_destroy(s_TSIGRecord* const self) {
+    delete self->cppobj;
+    self->cppobj = NULL;
+    Py_TYPE(self)->tp_free(self);
+}
+
+// This should be able to be used without modification as long as the
+// underlying C++ class has toText().
+PyObject*
+TSIGRecord_toText(const s_TSIGRecord* const self) {
+    try {
+        // toText() could throw, so we need to catch any exceptions below.
+        return (Py_BuildValue("s", self->cppobj->toText().c_str()));
+    } catch (const exception& ex) {
+        const string ex_what =
+            "Failed to convert TSIGRecord object to text: " +
+            string(ex.what());
+        PyErr_SetString(po_IscException, ex_what.c_str());
+    } catch (...) {
+        PyErr_SetString(PyExc_SystemError, "Unexpected failure in "
+                        "converting TSIGRecord object to text");
+    }
+    return (NULL);
+}
+
+PyObject*
+TSIGRecord_str(PyObject* self) {
+    // Simply call the to_text method we already defined
+    return (PyObject_CallMethod(self, const_cast<char*>("to_text"),
+                                const_cast<char*>("")));
+}
+
+PyObject*
+TSIGRecord_toWire(const s_TSIGRecord* const self, PyObject* args) {
+    typedef ToWireCallInt<const TSIGRecord> ToWireCall;
+    PyObject* (*towire_fn)(const s_TSIGRecord* const, PyObject*) =
+        toWireWrapper<s_TSIGRecord, TSIGRecord, ToWireCall>;
+    return (towire_fn(self, args));
+}
+
+PyObject*
+TSIGRecord_getName(const s_TSIGRecord* const self) {
+    try {
+        return (createNameObject(self->cppobj->getName()));
+    } catch (const exception& ex) {
+        const string ex_what =
+            "Failed to get TSIGRecord name: " + string(ex.what());
+        PyErr_SetString(po_IscException, ex_what.c_str());
+    } catch (...) {
+        PyErr_SetString(PyExc_SystemError, "Unexpected failure in "
+                        "getting TSIGRecord name");
+    }
+    return (NULL);
+}
+
+PyObject*
+TSIGRecord_getLength(const s_TSIGRecord* const self) {
+    return (Py_BuildValue("H", self->cppobj->getLength()));
+}
+
+PyObject*
+TSIGRecord_getRdata(const s_TSIGRecord* const self) {
+    try {
+        return (createTSIGObject(self->cppobj->getRdata()));
+    } catch (const exception& ex) {
+        const string ex_what =
+            "Failed to get TSIGRecord RDATA: " + string(ex.what());
+        PyErr_SetString(po_IscException, ex_what.c_str());
+    } catch (...) {
+        PyErr_SetString(PyExc_SystemError, "Unexpected failure in "
+                        "getting TSIGRecord RDATA");
+    }
+    return (NULL);
+}
+
+} // end of unnamed namespace
+
+namespace isc {
+namespace dns {
+namespace python {
+// This defines the complete type for reflection in python and
+// parsing of PyObject* to s_TSIGRecord
+// Most of the functions are not actually implemented and NULL here.
+PyTypeObject tsigrecord_type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "pydnspp.TSIGRecord",
+    sizeof(s_TSIGRecord),                 // tp_basicsize
+    0,                                  // tp_itemsize
+    reinterpret_cast<destructor>(TSIGRecord_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
+    TSIGRecord_str,                       // tp_str
+    NULL,                               // tp_getattro
+    NULL,                               // tp_setattro
+    NULL,                               // tp_as_buffer
+    Py_TPFLAGS_DEFAULT,                 // tp_flags
+    "The TSIGRecord class objects is...(COMPLETE THIS)",
+    NULL,                               // tp_traverse
+    NULL,                               // tp_clear
+    NULL,                               // tp_richcompare
+    0,                                  // tp_weaklistoffset
+    NULL,                               // tp_iter
+    NULL,                               // tp_iternext
+    TSIGRecord_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
+    reinterpret_cast<initproc>(TSIGRecord_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
+};
+
+// Module Initialization, all statics are initialized here
+bool
+initModulePart_TSIGRecord(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(&tsigrecord_type) < 0) {
+        return (false);
+    }
+    void* p = &tsigrecord_type;
+    if (PyModule_AddObject(mod, "TSIGRecord", static_cast<PyObject*>(p)) < 0) {
+        return (false);
+    }
+    Py_INCREF(&tsigrecord_type);
+
+    // The following template is the typical procedure for installing class
+    // variables.  If the class doesn't have a class variable, remove the
+    // entire try-catch clauses.
+    try {
+        // Constant class variables
+        installClassVariable(tsigrecord_type, "TSIG_TTL",
+                             Py_BuildValue("I", 0));
+    } catch (const exception& ex) {
+        const string ex_what =
+            "Unexpected failure in TSIGRecord initialization: " +
+            string(ex.what());
+        PyErr_SetString(po_IscException, ex_what.c_str());
+        return (false);
+    } catch (...) {
+        PyErr_SetString(PyExc_SystemError,
+                        "Unexpected failure in TSIGRecord initialization");
+        return (false);
+    }
+
+    return (true);
+}
+
+PyObject*
+createTSIGRecordObject(const TSIGRecord& source) {
+    TSIGRecordContainer container = PyObject_New(s_TSIGRecord,
+                                                 &tsigrecord_type);
+    container.set(new TSIGRecord(source));
+    return (container.release());
+}
+} // namespace python
+} // namespace dns
+} // namespace isc
diff --git a/src/lib/dns/python/tsigrecord_python.h b/src/lib/dns/python/tsigrecord_python.h
new file mode 100644
index 0000000..e0a3526
--- /dev/null
+++ b/src/lib/dns/python/tsigrecord_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_TSIGRECORD_H
+#define __PYTHON_TSIGRECORD_H 1
+
+#include <Python.h>
+
+namespace isc {
+namespace dns {
+class TSIGRecord;
+
+namespace python {
+
+// The s_* Class simply covers one instantiation of the object
+class s_TSIGRecord : public PyObject {
+public:
+    s_TSIGRecord();
+    TSIGRecord* cppobj;
+};
+
+extern PyTypeObject tsigrecord_type;
+
+bool initModulePart_TSIGRecord(PyObject* mod);
+
+/// This is A simple shortcut to create a python TSIGRecord object (in the
+/// form of a pointer to PyObject) with minimal exception safety.
+/// On success, it returns a valid pointer to PyObject with a reference
+/// counter of 1; if something goes wrong it throws an exception (it never
+/// returns a NULL pointer).
+/// This function is expected to be called with in a try block
+/// followed by necessary setup for python exception.
+PyObject* createTSIGRecordObject(const TSIGRecord& source);
+
+} // namespace python
+} // namespace dns
+} // namespace isc
+#endif // __PYTHON_TSIGRECORD_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/rdata/any_255/tsig_250.cc b/src/lib/dns/rdata/any_255/tsig_250.cc
index 8211e7f..2557965 100644
--- a/src/lib/dns/rdata/any_255/tsig_250.cc
+++ b/src/lib/dns/rdata/any_255/tsig_250.cc
@@ -71,7 +71,7 @@ getToken(istringstream& iss, const string& full_input) {
     string token;
     iss >> token;
     if (iss.bad() || iss.fail()) {
-        isc_throw(InvalidRdataText, "Invalid TSIG text: parse error" <<
+        isc_throw(InvalidRdataText, "Invalid TSIG text: parse error " <<
                   full_input);
     }
     return (token);
diff --git a/src/lib/python/isc/testutils/Makefile.am b/src/lib/python/isc/testutils/Makefile.am
index 8f00a9b..dd032fb 100644
--- a/src/lib/python/isc/testutils/Makefile.am
+++ b/src/lib/python/isc/testutils/Makefile.am
@@ -1 +1 @@
-EXTRA_DIST = __init__.py parse_args.py
+EXTRA_DIST = __init__.py parse_args.py tsigctx_mock.py
diff --git a/src/lib/python/isc/testutils/tsigctx_mock.py b/src/lib/python/isc/testutils/tsigctx_mock.py
new file mode 100644
index 0000000..a9af9b9
--- /dev/null
+++ b/src/lib/python/isc/testutils/tsigctx_mock.py
@@ -0,0 +1,53 @@
+# Copyright (C) 2011  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+from pydnspp import *
+
+class MockTSIGContext(TSIGContext):
+    """Tthis is a mock of TSIGContext class for testing.
+    Via its "error" attribute, you can fake the result of verify(), thereby
+    you can test many of TSIG related tests without requiring actual crypto
+    setups.  "error" should be either a TSIGError type value or a callable
+    object (typically a function).  In the latter case, the callable object
+    will take the context as a parameter, and is expected to return a
+    TSIGError object.
+    """
+
+    def __init__(self, tsig_key):
+        super().__init__(tsig_key)
+        self.error = None
+        self.verify_called = 0  # number of verify() called
+
+    def sign(self, qid, data):
+        """Transparently delegate the processing to the super class.
+        It doesn't matter much anyway because normal applications that would
+        be implemented in Python normally won't call TSIGContext.sign()
+        directly.
+        """
+        return super().sign(qid, data)
+
+    def verify(self, tsig_record, data):
+        self.verify_called += 1
+        # call real "verify" so that we can notice any misue (which would
+        # result in exception.
+        super().verify(tsig_record, data)
+        return self.get_error()
+
+    def get_error(self):
+        if self.error is None:
+            return super().get_error()
+        if hasattr(self.error, '__call__'):
+            return self.error(self)
+        return self.error
diff --git a/src/lib/util/Makefile.am b/src/lib/util/Makefile.am
index 6deec27..3cb1229 100644
--- a/src/lib/util/Makefile.am
+++ b/src/lib/util/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = . tests unittests io
+SUBDIRS = . tests unittests io pyunittests
 # The io/tests is hack, because otherwise we can not order these directories
 # properly. Unittests use io and io/tests use unittest.
 
diff --git a/src/lib/util/python/wrapper_template.cc b/src/lib/util/python/wrapper_template.cc
index 2fe7567..691e4bf 100644
--- a/src/lib/util/python/wrapper_template.cc
+++ b/src/lib/util/python/wrapper_template.cc
@@ -12,6 +12,14 @@
 // 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>
 
@@ -202,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)
-    "libdns_python. at CPPCLASS@",
+    "pydnspp. at CPPCLASS@",
     sizeof(s_ at CPPCLASS@),                 // tp_basicsize
     0,                                  // tp_itemsize
     reinterpret_cast<destructor>(@CPPCLASS at _destroy),       // tp_dealloc
@@ -288,6 +296,14 @@ initModulePart_ at CPPCLASS@(PyObject* mod) {
 
     return (true);
 }
+
+PyObject*
+create at CPPCLASS@Object(const @CPPCLASS@& source) {
+    @CPPCLASS at Container container =
+        PyObject_New(s_ at CPPCLASS@, &@cppclass at _type);
+    container.set(new @CPPCLASS@(source));
+    return (container.release());
+}
 } // namespace python
 } // namespace @MODULE@
 } // namespace isc
diff --git a/src/lib/util/python/wrapper_template.h b/src/lib/util/python/wrapper_template.h
index 1f983e4..d68a658 100644
--- a/src/lib/util/python/wrapper_template.h
+++ b/src/lib/util/python/wrapper_template.h
@@ -34,6 +34,21 @@ extern PyTypeObject @cppclass at _type;
 
 bool initModulePart_ at CPPCLASS@(PyObject* mod);
 
+// Note: this utility function works only when @CPPCLASS@ is a copy
+// constructable.
+// And, it would only be useful when python binding needs to create this
+// object frequently.  Otherwise, it would (or should) probably better to
+// remove the declaration and definition of this function.
+//
+/// This is A simple shortcut to create a python @CPPCLASS@ object (in the
+/// form of a pointer to PyObject) with minimal exception safety.
+/// On success, it returns a valid pointer to PyObject with a reference
+/// counter of 1; if something goes wrong it throws an exception (it never
+/// returns a NULL pointer).
+/// This function is expected to be called with in a try block
+/// followed by necessary setup for python exception.
+PyObject* create at CPPCLASS@Object(const @CPPCLASS@& source);
+
 } // namespace python
 } // namespace @MODULE@
 } // namespace isc
diff --git a/src/lib/util/pyunittests/Makefile.am b/src/lib/util/pyunittests/Makefile.am
new file mode 100644
index 0000000..e8fefbd
--- /dev/null
+++ b/src/lib/util/pyunittests/Makefile.am
@@ -0,0 +1,14 @@
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+pyexec_LTLIBRARIES = pyunittests_util.la
+pyunittests_util_la_SOURCES = pyunittests_util.cc
+pyunittests_util_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES)
+pyunittests_util_la_LDFLAGS = $(PYTHON_LDFLAGS)
+
+# Python prefers .so, while some OSes (specifically MacOS) use a different
+# suffix for dynamic objects.  -module is necessary to work this around.
+pyunittests_util_la_LDFLAGS += -module
+pyunittests_util_la_LIBADD = $(top_builddir)/src/lib/util/libutil.la
+pyunittests_util_la_LIBADD += $(PYTHON_LIB)
diff --git a/src/lib/util/pyunittests/pyunittests_util.cc b/src/lib/util/pyunittests/pyunittests_util.cc
new file mode 100644
index 0000000..d266c84
--- /dev/null
+++ b/src/lib/util/pyunittests/pyunittests_util.cc
@@ -0,0 +1,84 @@
+// 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 <stdint.h>
+
+// see util/time_utilities.h
+namespace isc {
+namespace util {
+namespace detail {
+extern int64_t (*gettimeFunction)();
+}
+}
+}
+
+namespace {
+int64_t fake_current_time;
+
+int64_t
+getFakeTime() {
+    return (fake_current_time);
+}
+
+PyObject*
+fixCurrentTime(PyObject*, PyObject* args) {
+    PyObject* maybe_none;
+    if (PyArg_ParseTuple(args, "L", &fake_current_time)) {
+        isc::util::detail::gettimeFunction = getFakeTime;
+    } else if (PyArg_ParseTuple(args, "O", &maybe_none) &&
+               maybe_none == Py_None) {
+        isc::util::detail::gettimeFunction = NULL;
+    } else {
+         PyErr_SetString(PyExc_TypeError, "Invalid arguments to "
+                         "pyunittests_util.fix_current_time");
+         return (NULL);
+    }
+
+    PyErr_Clear();
+    Py_RETURN_NONE;
+}
+
+PyMethodDef PyUnittestsUtilMethods[] = {
+    { "fix_current_time", fixCurrentTime, METH_VARARGS,
+      "Fix the current system time at the specified (fake) value.\n\n"
+      "This is useful for testing modules that depend on the current time.\n"
+      "Note that it only affects C++ modules that use gettimeWrapper() "
+      "defined in libutil, which allows a hook for testing.\n"
+      "If an integer (signed 64bit) is given, the current time will be fixed "
+      "to that value; if None is specified (which is the default) the use of "
+      "faked time will be canceled."
+    },
+    { NULL, NULL, 0, NULL}
+};
+
+PyModuleDef pyunittests_util = {
+    { PyObject_HEAD_INIT(NULL) NULL, 0, NULL},
+    "pyunittests_util",
+    "This module is a collection of utilities useful for testing "
+    "the BIND 10 C++ binding modules.",
+    -1,
+    PyUnittestsUtilMethods,
+    NULL,
+    NULL,
+    NULL,
+    NULL
+};
+} // end of unnamed namespace
+
+PyMODINIT_FUNC
+PyInit_pyunittests_util(void) {
+    return (PyModule_Create(&pyunittests_util));
+}




More information about the bind10-changes mailing list