BIND 10 trac812, updated. 475e26bae3b3c4c8823c4931638d0a7af0ac66cd [trac812] makefile updates, mainly for the additional dependency on libcryptolink.

BIND 10 source code commits bind10-changes at lists.isc.org
Wed Apr 27 06:19:35 UTC 2011


The branch, trac812 has been updated
       via  475e26bae3b3c4c8823c4931638d0a7af0ac66cd (commit)
       via  f4ad75548592b2f3eeae22de5685cacbf5c82bae (commit)
       via  dabf205231ff9e720d8693800e311a89ace18488 (commit)
       via  ed5025a1322aec3c6aa2855ec807bdcb8e013f1d (commit)
       via  e4034daa010c24286b969cd9ad6d75c30895cd52 (commit)
       via  0660b35c79dce9ff27361fee48da8c174bf05cd5 (commit)
       via  3e37899f95fe561fee0a73fb5d85071065ef3c7d (commit)
       via  088bb8cbca563a17b97166bcbb799fd50bbbca4f (commit)
       via  f099c11348b7947c64f7467677e9afa58ca7023c (commit)
       via  ad134794bd3fdbb058fe5d50d8e92c71d03d47cc (commit)
       via  5cd716bd9844e154e6ca58a981787614a3a6fe6c (commit)
       via  adfd1014c34da20e272f058c9cce43130c637071 (commit)
      from  65874313065ddb02756d5ef06c8802c7540c17c7 (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 475e26bae3b3c4c8823c4931638d0a7af0ac66cd
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Tue Apr 26 23:18:58 2011 -0700

    [trac812] makefile updates, mainly for the additional dependency on libcryptolink.

commit f4ad75548592b2f3eeae22de5685cacbf5c82bae
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Tue Apr 26 23:17:42 2011 -0700

    [trac812] ensure TSIGContext::sign() provides strong exception guarantee
    added new test for that.
    with some other cleanups.

commit dabf205231ff9e720d8693800e311a89ace18488
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Tue Apr 26 15:37:40 2011 -0700

    [trac812] reject NULL or len=0 data

commit ed5025a1322aec3c6aa2855ec807bdcb8e013f1d
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Tue Apr 26 15:19:18 2011 -0700

    [trac812] some cleanups

commit e4034daa010c24286b969cd9ad6d75c30895cd52
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Tue Apr 26 14:35:50 2011 -0700

    [trac812] reduce dependency: removed unused function in lib/dns/tests/

commit 0660b35c79dce9ff27361fee48da8c174bf05cd5
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Tue Apr 26 13:31:26 2011 -0700

    [trac812] reduce dependency: asiolink now doesn't need libdns++.

commit 3e37899f95fe561fee0a73fb5d85071065ef3c7d
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Tue Apr 26 12:43:13 2011 -0700

    [trac812] reduce dependency: we don't need libdns++ for cc tests.

commit 088bb8cbca563a17b97166bcbb799fd50bbbca4f
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Tue Apr 26 12:23:11 2011 -0700

    [trac812] main code for TSIG signing.

commit f099c11348b7947c64f7467677e9afa58ca7023c
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Tue Apr 26 12:22:04 2011 -0700

    [trac812] added mostly trivial TSIGError class.

commit ad134794bd3fdbb058fe5d50d8e92c71d03d47cc
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Tue Apr 26 12:13:50 2011 -0700

    [trac812] another prerequisite update to TSIGKey: we need an ability to
    convert TSIG name to the generic crypto hash algorithm enum.

commit 5cd716bd9844e154e6ca58a981787614a3a6fe6c
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Tue Apr 26 11:42:44 2011 -0700

    [trac812] added a test case for the previous change to TSIGKey

commit adfd1014c34da20e272f058c9cce43130c637071
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Tue Apr 26 11:36:53 2011 -0700

    [trac812] a prerequisite fixes to tsigkey: make sure key name is also
    in the canonical form (i.e. downcased).

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

Summary of changes:
 src/bin/xfrin/tests/Makefile.am                    |    2 +-
 src/bin/xfrout/tests/Makefile.am                   |    2 +-
 src/lib/asiolink/Makefile.am                       |    1 -
 src/lib/asiolink/tests/Makefile.am                 |    4 -
 src/lib/asiolink/tests/run_unittests.cc            |    1 -
 src/lib/cc/tests/Makefile.am                       |    1 -
 src/lib/dns/Makefile.am                            |    8 +-
 src/lib/dns/python/tests/Makefile.am               |    2 +-
 src/lib/dns/tests/Makefile.am                      |    3 +
 src/lib/dns/tests/tsig_unittest.cc                 |  505 ++++++++++++++++++++
 src/lib/dns/tests/tsigerror_unittest.cc            |  102 ++++
 src/lib/dns/tests/tsigkey_unittest.cc              |   16 +
 src/lib/dns/tsig.cc                                |  226 +++++++++
 src/lib/dns/tsig.h                                 |  302 ++++++++++++
 src/lib/{asiolink/io_error.h => dns/tsigerror.cc}  |   52 ++-
 src/lib/dns/tsigerror.h                            |  328 +++++++++++++
 src/lib/dns/tsigkey.cc                             |   53 ++-
 src/lib/dns/tsigkey.h                              |    5 +
 src/lib/python/isc/notify/tests/Makefile.am        |    2 +-
 src/lib/util/unittests/Makefile.am                 |    1 +
 .../unittests/newhook.cc}                          |   55 ++-
 src/lib/util/unittests/newhook.h                   |   76 +++
 22 files changed, 1675 insertions(+), 72 deletions(-)
 create mode 100644 src/lib/dns/tests/tsig_unittest.cc
 create mode 100644 src/lib/dns/tests/tsigerror_unittest.cc
 create mode 100644 src/lib/dns/tsig.cc
 create mode 100644 src/lib/dns/tsig.h
 copy src/lib/{asiolink/io_error.h => dns/tsigerror.cc} (50%)
 create mode 100644 src/lib/dns/tsigerror.h
 copy src/lib/{log/root_logger_name.cc => util/unittests/newhook.cc} (57%)
 create mode 100644 src/lib/util/unittests/newhook.h

-----------------------------------------------------------------------
diff --git a/src/bin/xfrin/tests/Makefile.am b/src/bin/xfrin/tests/Makefile.am
index dfa7714..d4efbc7 100644
--- a/src/bin/xfrin/tests/Makefile.am
+++ b/src/bin/xfrin/tests/Makefile.am
@@ -6,7 +6,7 @@ EXTRA_DIST = $(PYTESTS)
 # required by loadable python modules.
 LIBRARY_PATH_PLACEHOLDER =
 if SET_ENV_LIBRARY_PATH
-LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/xfr/.libs:$$$(ENV_LIBRARY_PATH)
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/xfr/.libs:$$$(ENV_LIBRARY_PATH)
 endif
 
 # test using command-line arguments, so use check-local target instead of TESTS
diff --git a/src/bin/xfrout/tests/Makefile.am b/src/bin/xfrout/tests/Makefile.am
index 1ec3c06..11916af 100644
--- a/src/bin/xfrout/tests/Makefile.am
+++ b/src/bin/xfrout/tests/Makefile.am
@@ -6,7 +6,7 @@ EXTRA_DIST = $(PYTESTS)
 # required by loadable python modules.
 LIBRARY_PATH_PLACEHOLDER =
 if SET_ENV_LIBRARY_PATH
-LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$$$(ENV_LIBRARY_PATH)
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$$$(ENV_LIBRARY_PATH)
 endif
 
 # test using command-line arguments, so use check-local target instead of TESTS
diff --git a/src/lib/asiolink/Makefile.am b/src/lib/asiolink/Makefile.am
index 66d5eda..22b3a8e 100644
--- a/src/lib/asiolink/Makefile.am
+++ b/src/lib/asiolink/Makefile.am
@@ -2,7 +2,6 @@ SUBDIRS = . tests
 
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
 AM_CPPFLAGS += $(BOOST_INCLUDES)
-AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
 
 AM_CXXFLAGS = $(B10_CXXFLAGS)
 
diff --git a/src/lib/asiolink/tests/Makefile.am b/src/lib/asiolink/tests/Makefile.am
index 37d9ef3..bfdf7c1 100644
--- a/src/lib/asiolink/tests/Makefile.am
+++ b/src/lib/asiolink/tests/Makefile.am
@@ -1,6 +1,5 @@
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
 AM_CPPFLAGS += $(BOOST_INCLUDES)
-AM_CPPFLAGS += -I$(top_builddir)/src/lib/dns -I$(top_srcdir)/src/bin
 AM_CPPFLAGS += -I$(top_builddir)/src/lib/util -I$(top_srcdir)/src/util
 AM_CPPFLAGS += -I$(top_builddir)/src/lib/cc
 AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(srcdir)/testdata\"
@@ -17,8 +16,6 @@ TESTS =
 if HAVE_GTEST
 TESTS += run_unittests
 run_unittests_SOURCES  = run_unittests.cc
-run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.h
-run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
 run_unittests_SOURCES += io_address_unittest.cc
 run_unittests_SOURCES += io_endpoint_unittest.cc
 run_unittests_SOURCES += io_socket_unittest.cc
@@ -32,7 +29,6 @@ run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 
 run_unittests_LDADD  = $(GTEST_LDADD)
 run_unittests_LDADD += $(SQLITE_LIBS)
-run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
 run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
 run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
 run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
diff --git a/src/lib/asiolink/tests/run_unittests.cc b/src/lib/asiolink/tests/run_unittests.cc
index c285f9e..97bcb65 100644
--- a/src/lib/asiolink/tests/run_unittests.cc
+++ b/src/lib/asiolink/tests/run_unittests.cc
@@ -22,7 +22,6 @@ main(int argc, char* argv[])
 {
     ::testing::InitGoogleTest(&argc, argv);         // Initialize Google test
     isc::log::setRootLoggerName("unittest");        // Set a root logger name
-    isc::UnitTestUtil::addDataPath(TEST_DATA_DIR);  // Add location of test data
 
     return (RUN_ALL_TESTS());
 }
diff --git a/src/lib/cc/tests/Makefile.am b/src/lib/cc/tests/Makefile.am
index 535c464..71e6988 100644
--- a/src/lib/cc/tests/Makefile.am
+++ b/src/lib/cc/tests/Makefile.am
@@ -26,7 +26,6 @@ run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
 
 run_unittests_LDADD = $(GTEST_LDADD)
 run_unittests_LDADD +=  $(top_builddir)/src/lib/cc/libcc.la
-run_unittests_LDADD +=  $(top_builddir)/src/lib/dns/libdns++.la
 run_unittests_LDADD +=  $(top_builddir)/src/lib/exceptions/libexceptions.la
 
 endif
diff --git a/src/lib/dns/Makefile.am b/src/lib/dns/Makefile.am
index 5a7151e..3cfc871 100644
--- a/src/lib/dns/Makefile.am
+++ b/src/lib/dns/Makefile.am
@@ -80,12 +80,18 @@ libdns___la_SOURCES += rrsetlist.h rrsetlist.cc
 libdns___la_SOURCES += rrttl.h rrttl.cc
 libdns___la_SOURCES += rrtype.cc
 libdns___la_SOURCES += question.h question.cc
+libdns___la_SOURCES += tsig.h tsig.cc
+libdns___la_SOURCES += tsigerror.h tsigerror.cc
 libdns___la_SOURCES += tsigkey.h tsigkey.cc
 libdns___la_SOURCES += rdata/generic/detail/nsec_bitmap.h
 libdns___la_SOURCES += rdata/generic/detail/nsec_bitmap.cc
 
 libdns___la_CPPFLAGS = $(AM_CPPFLAGS)
-libdns___la_LIBADD = $(top_builddir)/src/lib/util/libutil.la
+# Most applications of libdns++ will only implicitly rely on libcryptolink,
+# so we add the dependency here so that the applications don't have to link
+# libcryptolink explicitly.
+libdns___la_LIBADD = $(top_builddir)/src/lib/cryptolink/libcryptolink.la
+libdns___la_LIBADD += $(top_builddir)/src/lib/util/libutil.la
 
 nodist_libdns___la_SOURCES = rdataclass.cc rrclass.h rrtype.h
 nodist_libdns___la_SOURCES += rrparamregistry.cc
diff --git a/src/lib/dns/python/tests/Makefile.am b/src/lib/dns/python/tests/Makefile.am
index 272f1c1..184f06d 100644
--- a/src/lib/dns/python/tests/Makefile.am
+++ b/src/lib/dns/python/tests/Makefile.am
@@ -20,7 +20,7 @@ EXTRA_DIST += testutil.py
 # required by loadable python modules.
 LIBRARY_PATH_PLACEHOLDER =
 if SET_ENV_LIBRARY_PATH
-LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
 endif
 
 # test using command-line arguments, so use check-local target instead of TESTS
diff --git a/src/lib/dns/tests/Makefile.am b/src/lib/dns/tests/Makefile.am
index f5dd512..d49feb3 100644
--- a/src/lib/dns/tests/Makefile.am
+++ b/src/lib/dns/tests/Makefile.am
@@ -47,6 +47,8 @@ run_unittests_SOURCES += question_unittest.cc
 run_unittests_SOURCES += rrparamregistry_unittest.cc
 run_unittests_SOURCES += masterload_unittest.cc
 run_unittests_SOURCES += message_unittest.cc
+run_unittests_SOURCES += tsig_unittest.cc
+run_unittests_SOURCES += tsigerror_unittest.cc
 run_unittests_SOURCES += tsigkey_unittest.cc
 run_unittests_SOURCES += run_unittests.cc
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
@@ -54,6 +56,7 @@ run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
 run_unittests_LDADD = $(GTEST_LDADD)
 run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
 run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
 run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 endif
 
diff --git a/src/lib/dns/tests/tsig_unittest.cc b/src/lib/dns/tests/tsig_unittest.cc
new file mode 100644
index 0000000..ad2676a
--- /dev/null
+++ b/src/lib/dns/tests/tsig_unittest.cc
@@ -0,0 +1,505 @@
+// 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 <time.h>
+#include <string>
+#include <stdexcept>
+#include <vector>
+
+#include <boost/scoped_ptr.hpp>
+
+#include <gtest/gtest.h>
+
+#include <exceptions/exceptions.h>
+
+#include <util/buffer.h>
+#include <util/encode/base64.h>
+#include <util/unittests/newhook.h>
+
+#include <dns/message.h>
+#include <dns/messagerenderer.h>
+#include <dns/question.h>
+#include <dns/opcode.h>
+#include <dns/rcode.h>
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+#include <dns/tsig.h>
+#include <dns/tsigkey.h>
+
+#include <dns/tests/unittest_util.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::dns;
+using namespace isc::util;
+using namespace isc::util::encode;
+using namespace isc::dns::rdata;
+using isc::UnitTestUtil;
+
+// See dnssectime.cc
+namespace isc {
+namespace dns {
+namespace tsig {
+namespace detail {
+extern int64_t (*gettimeFunction)();
+}
+}
+}
+}
+
+namespace {
+// See dnssectime_unittest.cc
+template <int64_t NOW>
+int64_t
+testGetTime() {
+    return (NOW);
+}
+
+class TSIGTest : public ::testing::Test {
+protected:
+    TSIGTest() :
+        tsig_ctx(NULL), qid(0x2d65), test_name("www.example.com"),
+        test_class(RRClass::IN()), test_ttl(86400), message(Message::RENDER),
+        buffer(0), renderer(buffer) 
+    {
+        // Make sure we use the system time by default so that we won't be
+        // confused due to other tests that tweak the time.
+        tsig::detail::gettimeFunction = NULL;
+
+        decodeBase64("SFuWd/q99SzF8Yzd1QbB9g==", secret);
+        tsig_ctx.reset(new TSIGContext(TSIGKey(test_name,
+                                               TSIGKey::HMACMD5_NAME(),
+                                               &secret[0], secret.size())));
+        tsig_verify_ctx.reset(new TSIGContext(TSIGKey(test_name,
+                                                      TSIGKey::HMACMD5_NAME(),
+                                                      &secret[0],
+                                                      secret.size())));
+    }
+    ~TSIGTest() {
+        tsig::detail::gettimeFunction = NULL;
+    }
+
+    // Many of the tests below create some DNS message and sign it under
+    // some specific TSIG context.  This helper method unifies the common
+    // logic with slightly different parameters.
+    ConstTSIGRecordPtr createMessageAndSign(uint16_t qid, const Name& qname,
+                                            TSIGContext* ctx,
+                                            unsigned int message_flags =
+                                            RD_FLAG,
+                                            RRType qtype = RRType::A(),
+                                            const char* answer_data = NULL,
+                                            const RRType* answer_type = NULL,
+                                            bool add_question = true,
+                                            Rcode rcode = Rcode::NOERROR());
+
+    // bit-wise constant flags to configure DNS header flags for test
+    // messages.
+    static const unsigned int QR_FLAG = 0x1;
+    static const unsigned int AA_FLAG = 0x2;
+    static const unsigned int RD_FLAG = 0x4;
+
+    boost::scoped_ptr<TSIGContext> tsig_ctx;
+    boost::scoped_ptr<TSIGContext> tsig_verify_ctx;
+    const uint16_t qid;
+    const Name test_name;
+    const RRClass test_class;
+    const RRTTL test_ttl;
+    Message message;
+    OutputBuffer buffer;
+    MessageRenderer renderer;
+    vector<uint8_t> secret;
+};
+
+ConstTSIGRecordPtr
+TSIGTest::createMessageAndSign(uint16_t id, const Name& qname,
+                               TSIGContext* ctx, unsigned int message_flags,
+                               RRType qtype, const char* answer_data,
+                               const RRType* answer_type, bool add_question,
+                               Rcode rcode)
+{
+    message.clear(Message::RENDER);
+    message.setQid(id);
+    message.setOpcode(Opcode::QUERY());
+    message.setRcode(rcode);
+    if ((message_flags & QR_FLAG) != 0) {
+        message.setHeaderFlag(Message::HEADERFLAG_QR);
+    }
+    if ((message_flags & AA_FLAG) != 0) {
+        message.setHeaderFlag(Message::HEADERFLAG_AA);
+    }
+    if ((message_flags & RD_FLAG) != 0) {
+        message.setHeaderFlag(Message::HEADERFLAG_RD);
+    }
+    if (add_question) {
+        message.addQuestion(Question(qname, test_class, qtype));
+    }
+    if (answer_data != NULL) {
+        if (answer_type == NULL) {
+            answer_type = &qtype;
+        }
+        RRsetPtr answer_rrset(new RRset(qname, test_class, *answer_type,
+                                        test_ttl));
+        answer_rrset->addRdata(createRdata(*answer_type, test_class,
+                                           answer_data));
+        message.addRRset(Message::SECTION_ANSWER, answer_rrset);
+    }
+    renderer.clear();
+    message.toWire(renderer);
+
+    ConstTSIGRecordPtr tsig = ctx->sign(id, renderer.getData(),
+                                        renderer.getLength());
+    EXPECT_EQ(TSIGContext::SIGNED, ctx->getState());
+
+    return (tsig);
+}
+
+void
+commonTSIGChecks(ConstTSIGRecordPtr tsig, uint16_t expected_qid,
+                 uint64_t expected_timesigned,
+                 const uint8_t* expected_mac, size_t expected_maclen,
+                 uint16_t expected_error = 0,
+                 uint16_t expected_otherlen = 0,
+                 const uint8_t* expected_otherdata = NULL,
+                 const Name& expected_algorithm = TSIGKey::HMACMD5_NAME())
+{
+    ASSERT_TRUE(tsig != NULL);
+    const any::TSIG& tsig_rdata = tsig->getRdata();
+
+    EXPECT_EQ(expected_algorithm, tsig_rdata.getAlgorithm());
+    EXPECT_EQ(expected_timesigned, tsig_rdata.getTimeSigned());
+    EXPECT_EQ(300, tsig_rdata.getFudge());
+    EXPECT_EQ(expected_maclen, tsig_rdata.getMACSize());
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
+                        tsig_rdata.getMAC(), tsig_rdata.getMACSize(),
+                        expected_mac, expected_maclen);
+    EXPECT_EQ(expected_qid, tsig_rdata.getOriginalID());
+    EXPECT_EQ(expected_error, tsig_rdata.getError());
+    EXPECT_EQ(expected_otherlen, tsig_rdata.getOtherLen());
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
+                        tsig_rdata.getOtherData(), tsig_rdata.getOtherLen(),
+                        expected_otherdata, expected_otherlen);
+}
+
+TEST_F(TSIGTest, initialState) {
+    // Until signing or verifying, the state should be INIT
+    EXPECT_EQ(TSIGContext::INIT, tsig_ctx->getState());
+
+    // And there should be no error code.
+    EXPECT_EQ(TSIGError(Rcode::NOERROR()), tsig_ctx->getError());
+}
+
+// Example output generated by
+// "dig -y www.example.com:SFuWd/q99SzF8Yzd1QbB9g== www.example.com
+// QID: 0x2d65
+// Time Signed: 0x00004da8877a
+// MAC: 227026ad297beee721ce6c6fff1e9ef3
+const uint8_t common_expected_mac[] = {
+    0x22, 0x70, 0x26, 0xad, 0x29, 0x7b, 0xee, 0xe7,
+    0x21, 0xce, 0x6c, 0x6f, 0xff, 0x1e, 0x9e, 0xf3
+};
+TEST_F(TSIGTest, sign) {
+    tsig::detail::gettimeFunction = testGetTime<0x4da8877a>;
+
+    {
+        SCOPED_TRACE("Sign test for query");
+        commonTSIGChecks(createMessageAndSign(qid, test_name, tsig_ctx.get()),
+                         qid, 0x4da8877a, common_expected_mac,
+                         sizeof(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.
+TEST_F(TSIGTest, signUsingUpperCasedKeyName) {
+    tsig::detail::gettimeFunction = testGetTime<0x4da8877a>;
+
+    TSIGContext cap_ctx(TSIGKey(Name("WWW.EXAMPLE.COM"),
+                                TSIGKey::HMACMD5_NAME(),
+                                &secret[0], secret.size()));
+
+    {
+        SCOPED_TRACE("Sign test for query using non canonical key name");
+        commonTSIGChecks(createMessageAndSign(qid, test_name, &cap_ctx), qid,
+                         0x4da8877a, common_expected_mac,
+                         sizeof(common_expected_mac));
+    }
+}
+
+// Same as the previous test, but for the algorithm name.
+TEST_F(TSIGTest, signUsingUpperCasedAlgorithmName) {
+    tsig::detail::gettimeFunction = testGetTime<0x4da8877a>;
+
+    TSIGContext cap_ctx(TSIGKey(test_name,
+                                Name("HMAC-md5.SIG-alg.REG.int"),
+                                &secret[0], secret.size()));
+
+    {
+        SCOPED_TRACE("Sign test for query using non canonical algorithm name");
+        commonTSIGChecks(createMessageAndSign(qid, test_name, &cap_ctx), qid,
+                         0x4da8877a, common_expected_mac,
+                         sizeof(common_expected_mac));
+    }
+}
+
+TEST_F(TSIGTest, signAtActualTime) {
+    // 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.
+    const uint64_t now = static_cast<uint64_t>(time(NULL));
+
+    {
+        SCOPED_TRACE("Sign test for query at actual time");
+        ConstTSIGRecordPtr tsig = createMessageAndSign(qid, test_name,
+                                                       tsig_ctx.get());
+        const any::TSIG& tsig_rdata = tsig->getRdata();
+
+        // Check the resulted time signed is in the range of [now, now + 5]
+        // (5 is an arbitrary choice).  Note that due to the order of the call
+        // to time() and sign(), time signed must not be smaller than the
+        // current time.
+        EXPECT_LE(now, tsig_rdata.getTimeSigned());
+        EXPECT_GE(now + 5, tsig_rdata.getTimeSigned());
+    }
+}
+
+TEST_F(TSIGTest, signBadData) {
+    // some specific bad data should be rejected proactively.
+    const unsigned char dummy_data = 0;
+    EXPECT_THROW(tsig_ctx->sign(0, NULL, 10), InvalidParameter);
+    EXPECT_THROW(tsig_ctx->sign(0, &dummy_data, 0), InvalidParameter);
+}
+
+#ifdef ENABLE_CUSTOM_OPERATOR_NEW
+// We enable this test only when we enable custom new/delete at build time
+// We could enable/disable the test runtime using the gtest filter, but
+// we'd basically like to minimize the number of disabled tests (they
+// should generally be considered tests that temporarily fail and should
+// be fixed).
+TEST_F(TSIGTest, signExceptionSafety) {
+    // Check sign() provides the strong exception guarantee for the simpler
+    // case (with a key error and empty MAC).  The general case is more
+    // complicated and involves more memory allocation, so the test result
+    // won't be reliable.
+
+    tsig_verify_ctx->verifyTentative(createMessageAndSign(qid, test_name,
+                                                          tsig_ctx.get()),
+                                     TSIGError::BAD_KEY());
+    // At this point the state should be changed to "CHECKED"
+    ASSERT_EQ(TSIGContext::CHECKED, tsig_verify_ctx->getState());
+    try {
+        int dummydata;
+        isc::util::unittests::force_throw_on_new = true;
+        isc::util::unittests::throw_size_on_new = sizeof(TSIGRecord);
+        tsig_verify_ctx->sign(0, &dummydata, sizeof(dummydata));
+        isc::util::unittests::force_throw_on_new = false;
+        ASSERT_FALSE(true) << "Expected throw on new, but it didn't happen";
+    } catch (const std::bad_alloc&) {
+        isc::util::unittests::force_throw_on_new = false;
+
+        // sign() threw, so the state should still be "CHECKED".
+        EXPECT_EQ(TSIGContext::CHECKED, tsig_verify_ctx->getState());
+    }
+    isc::util::unittests::force_throw_on_new = false;
+}
+#endif  // ENABLE_CUSTOM_OPERATOR_NEW
+
+// Same test as "sign" but use a different algorithm just to confirm we don't
+// naively hardcode constants specific to a particular algorithm.
+// Test data generated by
+// "dig -y hmac-sha1:www.example.com:MA+QDhXbyqUak+qnMFyTyEirzng= www.example.com"
+//   QID: 0x0967, RDflag
+//   Current Time: 00004da8be86
+//   Time Signed:  00004dae7d5f
+//   HMAC Size: 20
+//   HMAC: 415340c7daf824ed684ee586f7b5a67a2febc0d3
+TEST_F(TSIGTest, signUsingHMACSHA1) {
+    tsig::detail::gettimeFunction = testGetTime<0x4dae7d5f>;
+
+    secret.clear();
+    decodeBase64("MA+QDhXbyqUak+qnMFyTyEirzng=", secret);
+    TSIGContext sha1_ctx(TSIGKey(test_name, TSIGKey::HMACSHA1_NAME(),
+                                 &secret[0], secret.size()));
+
+    const uint16_t sha1_qid = 0x0967;
+    const uint8_t expected_mac[] = {
+        0x41, 0x53, 0x40, 0xc7, 0xda, 0xf8, 0x24, 0xed, 0x68, 0x4e,
+        0xe5, 0x86, 0xf7, 0xb5, 0xa6, 0x7a, 0x2f, 0xeb, 0xc0, 0xd3
+    };
+    {
+        SCOPED_TRACE("Sign test using HMAC-SHA1");
+        commonTSIGChecks(createMessageAndSign(sha1_qid, test_name, &sha1_ctx),
+                         sha1_qid, 0x4dae7d5f, expected_mac,
+                         sizeof(expected_mac), 0, 0, NULL,
+                         TSIGKey::HMACSHA1_NAME());
+    }
+}
+
+// An example response to the signed query used for the "sign" test.
+// Answer: www.example.com. 86400 IN A 192.0.2.1
+// MAC: 8fcda66a7cd1a3b9948eb1869d384a9f
+TEST_F(TSIGTest, signResponse) {
+    tsig::detail::gettimeFunction = testGetTime<0x4da8877a>;
+
+    ConstTSIGRecordPtr tsig = createMessageAndSign(qid, test_name,
+                                                   tsig_ctx.get());
+    tsig_verify_ctx->verifyTentative(tsig);
+    EXPECT_EQ(TSIGContext::CHECKED, tsig_verify_ctx->getState());
+
+    // Transform the original message to a response, then sign the response
+    // with the context of "verified state".
+    tsig = createMessageAndSign(qid, test_name, tsig_verify_ctx.get(),
+                                QR_FLAG|AA_FLAG|RD_FLAG,
+                                RRType::A(), "192.0.2.1");
+    const uint8_t expected_mac[] = {
+        0x8f, 0xcd, 0xa6, 0x6a, 0x7c, 0xd1, 0xa3, 0xb9,
+        0x94, 0x8e, 0xb1, 0x86, 0x9d, 0x38, 0x4a, 0x9f
+    };
+    {
+        SCOPED_TRACE("Sign test for response");
+        commonTSIGChecks(tsig, qid, 0x4da8877a,
+                         expected_mac, sizeof(expected_mac));
+    }
+}
+
+// Example of signing multiple messages in a single TCP stream,
+// taken from data using BIND 9's "one-answer" transfer-format.
+// First message:
+//   QID: 0x3410, flags QR, AA
+//   Question: example.com/IN/AXFR
+//   Answer: example.com. 86400 IN SOA ns.example.com. root.example.com. (
+//                          2011041503 7200 3600 2592000 1200)
+//   Time Signed: 0x4da8e951
+// Second message:
+//    Answer: example.com. 86400 IN NS ns.example.com.
+//    MAC: 102458f7f62ddd7d638d746034130968
+TEST_F(TSIGTest, signContinuation) {
+    tsig::detail::gettimeFunction = testGetTime<0x4da8e951>;
+
+    const uint16_t axfr_qid = 0x3410;
+    const Name zone_name("example.com");
+
+    // Create and sign the AXFR request, then verify it.
+    tsig_verify_ctx->verifyTentative(createMessageAndSign(axfr_qid, zone_name,
+                                                          tsig_ctx.get(), 0,
+                                                          RRType::AXFR()));
+    EXPECT_EQ(TSIGContext::CHECKED, tsig_verify_ctx->getState());
+
+    // Create and sign the first response message (we don't need the result
+    // for the purpose of this test)
+    createMessageAndSign(axfr_qid, zone_name, tsig_verify_ctx.get(),
+                         AA_FLAG|QR_FLAG, RRType::AXFR(),
+                         "ns.example.com. root.example.com. "
+                         "2011041503 7200 3600 2592000 1200",
+                         &RRType::SOA());
+
+    // Create and sign the second response message
+    const uint8_t expected_mac[] = {
+        0x10, 0x24, 0x58, 0xf7, 0xf6, 0x2d, 0xdd, 0x7d,
+        0x63, 0x8d, 0x74, 0x60, 0x34, 0x13, 0x09, 0x68 
+    };
+    {
+        SCOPED_TRACE("Sign test for continued response in TCP stream");
+        commonTSIGChecks(createMessageAndSign(axfr_qid, zone_name,
+                                              tsig_verify_ctx.get(),
+                                              AA_FLAG|QR_FLAG, RRType::AXFR(),
+                                              "ns.example.com.", &RRType::NS(),
+                                              false),
+                         axfr_qid, 0x4da8e951,
+                         expected_mac, sizeof(expected_mac));
+    }
+}
+
+// BADTIME example, taken from data using specially hacked BIND 9's nsupdate
+// Query:
+//   QID: 0x1830, RD flag
+//   Current Time: 00004da8be86
+//   Time Signed:  00004da8b9d6
+//   Question: www.example.com/IN/SOA
+//(mac) 8406 7d50 b8e7 d054 3d50 5bd9 de2a bb68
+// Response:
+//   QRbit, RCODE=9(NOTAUTH)
+//   Time Signed: 00004da8b9d6 (the one in the query)
+//   MAC: d4b043f6f44495ec8a01260e39159d76
+//   Error: 0x12 (BADTIME), Other Len: 6
+//   Other data: 00004da8be86
+TEST_F(TSIGTest, badtimeResponse) {
+    tsig::detail::gettimeFunction = testGetTime<0x4da8b9d6>;
+
+    const uint16_t test_qid = 0x7fc4;
+    ConstTSIGRecordPtr tsig = createMessageAndSign(test_qid, test_name,
+                                                   tsig_ctx.get(), 0,
+                                                   RRType::SOA());
+
+    // "advance the clock" and try validating, which should fail due to BADTIME
+    // (verifyTentative actually doesn't check the time, though)
+    tsig::detail::gettimeFunction = testGetTime<0x4da8be86>;
+    tsig_verify_ctx->verifyTentative(tsig, TSIGError::BAD_TIME());
+    EXPECT_EQ(TSIGError::BAD_TIME(), tsig_verify_ctx->getError());
+
+    // make and sign a response in the context of TSIG error.
+    tsig = createMessageAndSign(test_qid, test_name, tsig_verify_ctx.get(),
+                                QR_FLAG, RRType::SOA(), NULL, NULL,
+                                true, Rcode::NOTAUTH());
+    const uint8_t expected_otherdata[] = { 0, 0, 0x4d, 0xa8, 0xbe, 0x86 };
+    const uint8_t expected_mac[] = {
+        0xd4, 0xb0, 0x43, 0xf6, 0xf4, 0x44, 0x95, 0xec,
+        0x8a, 0x01, 0x26, 0x0e, 0x39, 0x15, 0x9d, 0x76 
+    };
+    {
+        SCOPED_TRACE("Sign test for response with BADTIME");
+        commonTSIGChecks(tsig, message.getQid(), 0x4da8b9d6,
+                         expected_mac, sizeof(expected_mac),
+                         18,     // error: BADTIME
+                         sizeof(expected_otherdata),
+                         expected_otherdata);
+    }
+}
+
+TEST_F(TSIGTest, badsigResponse) {
+    tsig::detail::gettimeFunction = testGetTime<0x4da8877a>;
+
+    // Sign a simple message, and force the verification to fail with
+    // BADSIG.
+    tsig_verify_ctx->verifyTentative(createMessageAndSign(qid, test_name,
+                                                          tsig_ctx.get()),
+                                     TSIGError::BAD_SIG());
+
+    // Sign the same message (which doesn't matter for this test) with the
+    // context of "checked state".
+    {
+        SCOPED_TRACE("Sign test for response with BADSIG error");
+        commonTSIGChecks(createMessageAndSign(qid, test_name,
+                                              tsig_verify_ctx.get()),
+                         message.getQid(), 0x4da8877a, NULL, 0,
+                         16);   // 16: BADSIG
+    }
+}
+
+TEST_F(TSIGTest, badkeyResponse) {
+    // A similar test as badsigResponse but for BADKEY
+    tsig::detail::gettimeFunction = testGetTime<0x4da8877a>;
+    tsig_verify_ctx->verifyTentative(createMessageAndSign(qid, test_name,
+                                                          tsig_ctx.get()),
+                                     TSIGError::BAD_KEY());
+    {
+        SCOPED_TRACE("Sign test for response with BADKEY error");
+        commonTSIGChecks(createMessageAndSign(qid, test_name,
+                                              tsig_verify_ctx.get()),
+                         message.getQid(), 0x4da8877a, NULL, 0,
+                         17);   // 17: BADKEYSIG
+    }
+}
+
+} // end namespace
diff --git a/src/lib/dns/tests/tsigerror_unittest.cc b/src/lib/dns/tests/tsigerror_unittest.cc
new file mode 100644
index 0000000..5866587
--- /dev/null
+++ b/src/lib/dns/tests/tsigerror_unittest.cc
@@ -0,0 +1,102 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <string>
+#include <ostream>
+
+#include <gtest/gtest.h>
+
+#include <exceptions/exceptions.h>
+
+#include <dns/rcode.h>
+#include <dns/tsigerror.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::dns;
+
+namespace {
+TEST(TSIGErrorTest, constructFromErrorCode) {
+    // These are pretty trivial, and also test getCode();
+    EXPECT_EQ(0, TSIGError(0).getCode());
+    EXPECT_EQ(18, TSIGError(18).getCode());
+    EXPECT_EQ(65535, TSIGError(65535).getCode());
+}
+
+TEST(TSIGErrorTest, constructFromRcode) {
+    // We use RCODE for code values from 0-15.
+    EXPECT_EQ(0, TSIGError(Rcode::NOERROR()).getCode());
+    EXPECT_EQ(15, TSIGError(Rcode(15)).getCode());
+
+    // From error code 16 TSIG errors define a separate space, so passing
+    // corresponding RCODE for such code values should be prohibited.
+    EXPECT_THROW(TSIGError(Rcode(16)).getCode(), OutOfRange);
+}
+
+TEST(TSIGErrorTest, constants) {
+    // We'll only test arbitrarily chosen subsets of the codes.
+    // This class is quite simple, so it should be suffice.
+
+    EXPECT_EQ(TSIGError::BAD_SIG_CODE, TSIGError(16).getCode());
+    EXPECT_EQ(TSIGError::BAD_KEY_CODE, TSIGError(17).getCode());
+    EXPECT_EQ(TSIGError::BAD_TIME_CODE, TSIGError(18).getCode());
+
+    EXPECT_EQ(0, TSIGError::NOERROR().getCode());
+    EXPECT_EQ(9, TSIGError::NOTAUTH().getCode());
+    EXPECT_EQ(14, TSIGError::RESERVED14().getCode());
+    EXPECT_EQ(TSIGError::BAD_SIG_CODE, TSIGError::BAD_SIG().getCode());
+    EXPECT_EQ(TSIGError::BAD_KEY_CODE, TSIGError::BAD_KEY().getCode());
+    EXPECT_EQ(TSIGError::BAD_TIME_CODE, TSIGError::BAD_TIME().getCode());
+}
+
+TEST(TSIGErrorTest, equal) {
+    EXPECT_TRUE(TSIGError::NOERROR() == TSIGError(Rcode::NOERROR()));
+    EXPECT_TRUE(TSIGError(Rcode::NOERROR()) == TSIGError::NOERROR());
+    EXPECT_TRUE(TSIGError::NOERROR().equals(TSIGError(Rcode::NOERROR())));
+    EXPECT_TRUE(TSIGError::NOERROR().equals(TSIGError(Rcode::NOERROR())));
+
+    EXPECT_TRUE(TSIGError::BAD_SIG() == TSIGError(16));
+    EXPECT_TRUE(TSIGError(16) == TSIGError::BAD_SIG());
+    EXPECT_TRUE(TSIGError::BAD_SIG().equals(TSIGError(16)));
+    EXPECT_TRUE(TSIGError(16).equals(TSIGError::BAD_SIG()));
+}
+
+TEST(TSIGErrorTest, nequal) {
+    EXPECT_TRUE(TSIGError::BAD_KEY() != TSIGError(Rcode::NOERROR()));
+    EXPECT_TRUE(TSIGError(Rcode::NOERROR()) != TSIGError::BAD_KEY());
+    EXPECT_TRUE(TSIGError::BAD_KEY().nequals(TSIGError(Rcode::NOERROR())));
+    EXPECT_TRUE(TSIGError(Rcode::NOERROR()).nequals(TSIGError::BAD_KEY()));
+}
+
+TEST(TSIGErrorTest, toText) {
+    // TSIGError derived from the standard Rcode
+    EXPECT_EQ("NOERROR", TSIGError(Rcode::NOERROR()).toText());
+
+    // Well known TSIG errors
+    EXPECT_EQ("BADSIG", TSIGError::BAD_SIG().toText());
+    EXPECT_EQ("BADKEY", TSIGError::BAD_KEY().toText());
+    EXPECT_EQ("BADTIME", TSIGError::BAD_TIME().toText());
+
+    // Unknown (or not yet supported) codes.  Simply converted as numeric.
+    EXPECT_EQ("19", TSIGError(19).toText());
+    EXPECT_EQ("65535", TSIGError(65535).toText());
+}
+
+// test operator<<.  We simply confirm it appends the result of toText().
+TEST(TSIGErrorTest, LeftShiftOperator) {
+    ostringstream oss;
+    oss << TSIGError::BAD_KEY();
+    EXPECT_EQ(TSIGError::BAD_KEY().toText(), oss.str());
+}
+} // end namespace
diff --git a/src/lib/dns/tests/tsigkey_unittest.cc b/src/lib/dns/tests/tsigkey_unittest.cc
index c10ffe0..0354cb1 100644
--- a/src/lib/dns/tests/tsigkey_unittest.cc
+++ b/src/lib/dns/tests/tsigkey_unittest.cc
@@ -18,6 +18,8 @@
 
 #include <exceptions/exceptions.h>
 
+#include <cryptolink/cryptolink.h>
+
 #include <dns/tsigkey.h>
 
 #include <dns/tests/unittest_util.h>
@@ -38,6 +40,15 @@ TEST_F(TSIGKeyTest, algorithmNames) {
     EXPECT_EQ(Name("hmac-md5.sig-alg.reg.int"), TSIGKey::HMACMD5_NAME());
     EXPECT_EQ(Name("hmac-sha1"), TSIGKey::HMACSHA1_NAME());
     EXPECT_EQ(Name("hmac-sha256"), TSIGKey::HMACSHA256_NAME());
+
+    // Also check conversion to cryptolink definitions
+    EXPECT_EQ(isc::cryptolink::MD5, TSIGKey(key_name, TSIGKey::HMACMD5_NAME(),
+                                            NULL, 0).getCryptoAlgorithm());
+    EXPECT_EQ(isc::cryptolink::SHA1, TSIGKey(key_name, TSIGKey::HMACSHA1_NAME(),
+                                             NULL, 0).getCryptoAlgorithm());
+    EXPECT_EQ(isc::cryptolink::SHA256, TSIGKey(key_name,
+                                               TSIGKey::HMACSHA256_NAME(),
+                                               NULL, 0).getCryptoAlgorithm());
 }
 
 TEST_F(TSIGKeyTest, construct) {
@@ -58,6 +69,11 @@ TEST_F(TSIGKeyTest, construct) {
                       secret.c_str(),
                       secret.size()).getAlgorithmName().toText());
 
+    EXPECT_EQ("example.com.",
+              TSIGKey(Name("EXAMPLE.CoM."), TSIGKey::HMACSHA256_NAME(),
+                      secret.c_str(),
+                      secret.size()).getKeyName().toText());
+
     // Invalid combinations of secret and secret_len:
     EXPECT_THROW(TSIGKey(key_name, TSIGKey::HMACSHA1_NAME(), secret.c_str(), 0),
                  isc::InvalidParameter);
diff --git a/src/lib/dns/tsig.cc b/src/lib/dns/tsig.cc
new file mode 100644
index 0000000..6e24eb6
--- /dev/null
+++ b/src/lib/dns/tsig.cc
@@ -0,0 +1,226 @@
+// 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 <sys/time.h>
+
+#include <stdint.h>
+
+#include <cassert>              // for the tentative verifyTentative()
+#include <vector>
+
+#include <boost/shared_ptr.hpp>
+
+#include <exceptions/exceptions.h>
+
+#include <util/buffer.h>
+
+#include <dns/rdataclass.h>
+#include <dns/rrclass.h>
+#include <dns/tsig.h>
+#include <dns/tsigerror.h>
+#include <dns/tsigkey.h>
+
+#include <cryptolink/cryptolink.h>
+#include <cryptolink/crypto_hmac.h>
+
+using namespace std;
+using namespace isc::util;
+using namespace isc::cryptolink;
+using namespace isc::dns::rdata;
+
+namespace isc {
+namespace dns {
+
+// Borrowed from dnssectime.cc.  This trick should be unified somewhere.
+namespace tsig {
+namespace detail {
+int64_t (*gettimeFunction)() = NULL;
+}
+}
+
+namespace {
+int64_t
+gettimeofdayWrapper() {
+    using namespace tsig::detail;
+    if (gettimeFunction != NULL) {
+        return (gettimeFunction());
+    }
+
+    struct timeval now;
+    gettimeofday(&now, NULL);
+
+    return (static_cast<int64_t>(now.tv_sec));
+}
+}
+
+namespace {
+typedef boost::shared_ptr<HMAC> HMACPtr;
+}
+
+const RRClass&
+TSIGRecord::getClass() {
+    return (RRClass::ANY());
+}
+
+struct TSIGContext::TSIGContextImpl {
+    TSIGContextImpl(const TSIGKey& key) :
+        state_(INIT), key_(key), error_(Rcode::NOERROR()),
+        previous_timesigned_(0)
+    {}
+    State state_;
+    TSIGKey key_;
+    vector<uint8_t> previous_digest_;
+    TSIGError error_;
+    uint64_t previous_timesigned_; // only meaningful for response with BADTIME
+};
+
+TSIGContext::TSIGContext(const TSIGKey& key) : impl_(new TSIGContextImpl(key))
+{
+}
+
+TSIGContext::~TSIGContext() {
+    delete impl_;
+}
+
+TSIGContext::State
+TSIGContext::getState() const {
+    return (impl_->state_);
+}
+
+TSIGError
+TSIGContext::getError() const {
+    return (impl_->error_);
+}
+
+ConstTSIGRecordPtr
+TSIGContext::sign(const uint16_t qid, const void* const data,
+                  const size_t data_len)
+{
+    if (data == NULL || data_len == 0) {
+        isc_throw(InvalidParameter, "TSIG sign error: empty data is given");
+    }
+
+    TSIGError error(TSIGError::NOERROR());
+    const uint64_t now = (gettimeofdayWrapper() & 0x0000ffffffffffffULL);
+
+    // For responses adjust the error code.
+    if (impl_->state_ == CHECKED) {
+        error = impl_->error_;
+    }
+
+    // For errors related to key or MAC, return an unsigned response as
+    // specified in Section 4.3 of RFC2845.
+    if (error == TSIGError::BAD_SIG() || error == TSIGError::BAD_KEY()) {
+        ConstTSIGRecordPtr tsig(new TSIGRecord(
+                                    any::TSIG(impl_->key_.getAlgorithmName(),
+                                              now, DEFAULT_FUDGE, NULL, 0,
+                                              qid, error.getCode(), 0, NULL)));
+        impl_->previous_digest_.clear();
+        impl_->state_ = SIGNED;
+        return (tsig);
+    }
+
+    OutputBuffer variables(0);
+    HMACPtr hmac(CryptoLink::getCryptoLink().createHMAC(
+                     impl_->key_.getSecret(),
+                     impl_->key_.getSecretLength(),
+                     impl_->key_.getCryptoAlgorithm()),
+                 deleteHMAC);
+
+    // If the context has previous MAC (either the Request MAC or its own
+    // previous MAC), digest it.
+    if (impl_->state_ != INIT) {
+        const uint16_t previous_digest_len(impl_->previous_digest_.size());
+        variables.writeUint16(previous_digest_len);
+        if (previous_digest_len != 0) {
+            variables.writeData(&impl_->previous_digest_[0],
+                                previous_digest_len);
+        }
+        hmac->update(variables.getData(), variables.getLength());
+    }
+
+    // Digest the message (without TSIG)
+    hmac->update(data, data_len);
+
+    //
+    // Digest TSIG variables.  If state_ is SIGNED we skip digesting them
+    // except for time related variables (RFC2845 4.4).
+    //
+    variables.clear();
+    if (impl_->state_ != SIGNED) {
+        impl_->key_.getKeyName().toWire(variables);
+        TSIGRecord::getClass().toWire(variables);
+        variables.writeUint32(TSIGRecord::TSIG_TTL);
+        impl_->key_.getAlgorithmName().toWire(variables);
+    }
+    const uint64_t time_signed = (error == TSIGError::BAD_TIME()) ?
+        impl_->previous_timesigned_ : now;
+    variables.writeUint16(time_signed >> 32);
+    variables.writeUint32(time_signed & 0xffffffff);
+    variables.writeUint16(DEFAULT_FUDGE);
+    hmac->update(variables.getData(), variables.getLength());
+    variables.clear();
+
+    if (impl_->state_ != SIGNED) {
+        variables.writeUint16(error.getCode());
+
+        // For BADTIME error, digest 6 bytes of other data.
+        // (6 bytes = size of time signed value)
+        variables.writeUint16((error == TSIGError::BAD_TIME()) ? 6 : 0);
+        hmac->update(variables.getData(), variables.getLength());
+
+        variables.clear();
+        if (error == TSIGError::BAD_TIME()) {
+            variables.writeUint16(now >> 32);
+            variables.writeUint32(now & 0xffffffff);
+            hmac->update(variables.getData(), variables.getLength());
+        }
+    }
+    const uint16_t otherlen = variables.getLength();
+
+    // Get the final digest, update internal state, then finish.
+    vector<uint8_t> digest = hmac->sign();
+    ConstTSIGRecordPtr tsig(new TSIGRecord(
+                                any::TSIG(impl_->key_.getAlgorithmName(),
+                                          time_signed, DEFAULT_FUDGE,
+                                          digest.size(), &digest[0],
+                                          qid, error.getCode(), otherlen,
+                                          otherlen == 0 ?
+                                          NULL : variables.getData())));
+    // Exception free from now on.
+    impl_->previous_digest_.swap(digest);
+    impl_->state_ = SIGNED;
+    return (tsig);
+}
+
+void
+TSIGContext::verifyTentative(ConstTSIGRecordPtr tsig, TSIGError error) {
+    const any::TSIG tsig_rdata = tsig->getRdata();
+
+    impl_->error_ = error;
+    if (error == TSIGError::BAD_TIME()) {
+        impl_->previous_timesigned_ = tsig_rdata.getTimeSigned();
+    }
+
+    // For simplicity we assume non empty digests.
+    assert(tsig_rdata.getMACSize() != 0);
+    impl_->previous_digest_.assign(
+        static_cast<const uint8_t*>(tsig_rdata.getMAC()),
+        static_cast<const uint8_t*>(tsig_rdata.getMAC()) +
+        tsig_rdata.getMACSize());
+
+    impl_->state_ = CHECKED;
+}
+} // namespace dns
+} // namespace isc
diff --git a/src/lib/dns/tsig.h b/src/lib/dns/tsig.h
new file mode 100644
index 0000000..55ab41e
--- /dev/null
+++ b/src/lib/dns/tsig.h
@@ -0,0 +1,302 @@
+// 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 __TSIG_H
+#define __TSIG_H 1
+
+#include <boost/shared_ptr.hpp>
+#include <boost/noncopyable.hpp>
+
+#include <dns/rdataclass.h>
+#include <dns/tsigerror.h>
+#include <dns/tsigkey.h>
+
+namespace isc {
+namespace dns {
+/// TSIG resource record.
+///
+/// A \c TSIGRecord class object represents a TSIG resource record and is
+/// responsible for conversion to and from wire format TSIG record based on
+/// the protocol specification (RFC2845).
+/// This class is provided so that other classes and applications can handle
+/// TSIG without knowing protocol details of TSIG, such as that it uses a
+/// fixed constant of TTL.
+///
+/// \note So the plan is to eventually provide a \c toWire() method and
+/// the "from wire" constructor.  They are not yet provided in this initial
+/// step.
+///
+/// \note
+/// This class could be a derived class of \c AbstractRRset.  That way
+/// it would be able to be used in a polymorphic way; for example,
+/// an application can construct a TSIG RR by itself and insert it to a
+/// \c Message object as a generic RRset.  On the other hand, it would mean
+/// this class would have to implement an \c RdataIterator (even though it
+/// can be done via straightforward forwarding) while the iterator is mostly
+/// redundant since there should be one and only one RDATA for a valid TSIG
+/// RR.  Likewise, some methods such as \c setTTL() method wouldn't be well
+/// defined due to such special rules for TSIG as using a fixed TTL.
+/// Overall, TSIG is a very special RR type that simply uses the compatible
+/// resource record format, and it will be unlikely that a user wants to
+/// handle it through a generic interface in a polymorphic way.
+/// We therefore chose to define it as a separate class.  This is also
+/// similar to why \c EDNS is a separate class.
+class TSIGRecord {
+public:
+    /// Constructor from TSIG RDATA
+    ///
+    /// \exception std::bad_alloc Resource allocation for copying the RDATA
+    /// fails
+    explicit TSIGRecord(const rdata::any::TSIG& tsig_rdata) :
+        rdata_(tsig_rdata)
+    {}
+
+    /// Return the RDATA of the TSIG RR
+    ///
+    /// \exception None
+    const rdata::any::TSIG& getRdata() const { return (rdata_); }
+
+    /// \name Protocol constants and defaults
+    ///
+    //@{
+    /// Return the RR class of TSIG
+    ///
+    /// TSIG always uses the ANY RR class.  This static method returns it,
+    /// when, though unlikely, an application wants to know which class TSIG
+    /// is supposed to use.
+    ///
+    /// \exception None
+    static const RRClass& getClass();
+
+    /// The TTL value to be used in TSIG RRs.
+    static const uint32_t TSIG_TTL = 0;
+    //@}
+
+private:
+    const rdata::any::TSIG rdata_;
+};
+
+/// A pointer-like type pointing to a \c TSIGRecord object.
+typedef boost::shared_ptr<TSIGRecord> TSIGRecordPtr;
+
+/// A pointer-like type pointing to an immutable \c TSIGRecord object.
+typedef boost::shared_ptr<const TSIGRecord> ConstTSIGRecordPtr;
+
+/// TSIG session context.
+///
+/// The \c TSIGContext class maintains a context of a signed session of
+/// DNS transactions by TSIG.  In many cases a TSIG signed session consists
+/// of a single set of request (e.g. normal query) and reply (e.g. normal
+/// response), where the request is initially signed by the client, and the
+/// reply is signed by the server using the initial signature.  As mentioned
+/// in RFC2845, a session can consist of multiple exchanges in a TCP
+/// connection.  As also mentioned in the RFC, an AXFR response often contains
+/// multiple DNS messages, which can belong to the same TSIG session.
+/// This class supports all these cases.
+///
+/// A \c TSIGContext object is generally constructed with a TSIG key to be
+/// used for the session, and keeps track of various kinds of session specific
+/// information, such as the original digest while waiting for a response or
+/// verification error information that is to be used for a subsequent
+/// response.
+///
+/// This class has two main methods, \c sign() and \c verify().
+/// The \c sign() method signs given data (which is supposed to be a complete
+/// DNS message without the TSIG itself) using the TSIG key and other
+/// related information associated with the \c TSIGContext object.
+/// The \c verify() method verifies a given DNS message that contains a TSIG
+/// RR using the key and other internal information.
+///
+/// In general, a DNS client that wants to send a signed query will construct
+/// a \c TSIGContext object with the TSIG key that the client is intending to
+/// use, and sign the query with the context.  The client will keeps the
+/// context, and verify the response with it.
+///
+/// On the other hand, a DNS server will construct a \c TSIGContext object
+/// with the information of the TSIG RR included in a query with a set of
+/// possible keys (in the form of a \c TSIGKeyRing object).  The constructor
+/// in this mode will identify the appropriate TSIG key (or internally record
+/// an error if it doesn't find a key).  The server will then verify the
+/// query with the context, and generate a signed response using the same
+/// same context.  (Note: this mode is not yet implemented and may change,
+/// see below).
+///
+/// When multiple messages belong to the same TSIG session, either side
+/// (signer or verifier) will keep using the same context.  It records
+/// the latest session state (such as the previous digest) so that repeated
+/// calls to \c sign() or \c verify() work correctly in terms of the TSIG
+/// protocol.
+///
+/// \note The \c verify() method is not yet implemented.  The implementation
+/// and documentation should be updated in the corresponding task.
+///
+/// <b>TCP Consideration</b>
+///
+/// RFC2845 describes the case where a single TSIG session is used for
+/// multiple DNS messages (Section 4.4).  This class supports signing and
+/// verifying the messages in this scenario, but does not care if the messages
+/// were delivered over a TCP connection or not.  If, for example, the
+/// same \c TSIGContext object is used to sign two independent DNS queries
+/// sent over UDP, they will be considered to belong to the same TSIG
+/// session, and, as a result, verification will be likely to fail.
+///
+/// \b Copyability
+///
+/// This class is currently non copyable based on the observation of the
+/// typical usage as described above.  But there is no strong technical
+/// reason why this class cannot be copyable.  If we see the need for it
+/// in future we may change the implementation on this point.
+///
+/// <b>Note to developers:</b>
+/// One basic design choice is to make the \c TSIGContext class is as
+/// independent from the \c Message class.  This is because the latter is
+/// much more complicated, depending on many other classes, while TSIG is
+/// a very specific part of the entire DNS protocol set.  If the \c TSIGContext
+/// class depends on \c \c Message, it will be more vulnerable to changes
+/// to other classes, and will be more difficult to test due to the
+/// direct or indirect dependencies.  The interface of \c sign() that takes
+/// opaque data (instead of, e.g., a \c Message or \c MessageRenderer object)
+/// is therefore a deliberate design decision.
+class TSIGContext : boost::noncopyable {
+public:
+    /// Internal state of context
+    ///
+    /// The constants of this enum type define a specific state of
+    /// \c TSIGContext to adjust the behavior.  The definition is public
+    /// and the state can be seen via the \c getState() method, but this is
+    /// mostly private information.  It's publicly visible mainly for testing
+    /// purposes; there is no API for the application to change the state
+    /// directly.
+    enum State {
+        INIT,                   ///< Initial state
+        SIGNED,                 ///< Sign completed
+        CHECKED ///< Verification completed (may or may not successfully)
+    };
+
+    /// \name Constructors and destructor
+    ///
+    //@{
+    /// Constructor from a TSIG key.
+    ///
+    /// \exception std::bad_alloc Resource allocation for internal data fails
+    ///
+    /// \param key The TSIG key to be used for TSIG sessions with this context.
+    explicit TSIGContext(const TSIGKey& key);
+
+    /// The destructor.
+    ~TSIGContext();
+    //@}
+
+    /// Sign a DNS message.
+    ///
+    /// This method computes the TSIG MAC for the given data, which is
+    /// generally expected to be a complete, wire-format DNS message
+    /// that doesn't contain a TSIG RR, based on the TSIG key and
+    /// other context information of \c TSIGContext, and returns a
+    /// result in the form of a (pointer object pointing to)
+    /// \c TSIGRecord object.
+    ///
+    /// The caller of this method will use the returned value to render a
+    /// complete TSIG RR into the message that has been signed so that it
+    /// will become a complete TSIG-signed message.
+    ///
+    /// \note Normal applications are not expected to call this method
+    /// directly; they will usually use the \c Message::toWire() method
+    /// with a \c TSIGContext object being a parameter and have the
+    /// \c Message class create a complete signed message.
+    ///
+    /// This method treats the given data as opaque, even though it's generally
+    /// expected to represent a wire-format DNS message (see also the class
+    /// description), and doesn't inspect it in any way.  For example, it
+    /// doesn't check whether the data length is sane for a valid DNS message.
+    /// This is also the reason why this method takes the \c qid parameter,
+    /// which will be used as the original ID of the resulting
+    /// \c TSIGRecordx object, even though this value should be stored in the
+    /// first two octets (in wire format) of the given data.
+    ///
+    /// \note This method still checks and rejects empty data (\c NULL pointer
+    /// data or the specified data length is 0) in order to avoid catastrophic
+    /// effect such as program crash.  Empty data is not necessarily invalid
+    /// for HMAC computation, but obviously it doesn't make sense for a DNS
+    /// message.
+    ///
+    /// This method provides the strong exception guarantee; unless the method
+    /// returns (without an exception being thrown), the internal state of
+    /// the \c TSIGContext won't be modified.
+    ///
+    /// \exception InvalidParameter \c data is NULL or \c data_len is 0
+    /// \exception cryptolink::LibraryError Some unexpected error in the
+    /// underlying crypto operation
+    /// \exception std::bad_alloc Temporary resource allocation failure
+    ///
+    /// \param qid The QID to be as the value of the original ID field of
+    /// the resulting TSIG record
+    /// \param data Points to the wire-format data to be signed
+    /// \param data_len The length of \c data in bytes
+    ///
+    /// \return A TSIG record for the given data along with the context.
+    ConstTSIGRecordPtr sign(const uint16_t qid, const void* const data,
+                            const size_t data_len);
+
+    /// Return the current state of the context
+    ///
+    /// \note
+    /// The states are visible in public mainly for testing purposes.
+    /// Normal applications won't have to deal with them.
+    ///
+    /// \exception None
+    State getState() const;
+
+    /// Return the TSIG error as a result of the latest verification
+    ///
+    /// This method can be called even before verifying anything, but the
+    /// returned value is meaningless in that case.
+    ///
+    /// \exception None
+    TSIGError getError() const;
+
+    // This method is tentatively added for testing until a complete
+    // verify() method is implemented.  Once it's done this should be
+    // removed, and corresponding tests should be updated.
+    //
+    // This tentative "verify" method changes the internal state of
+    // the TSIGContext to the CHECKED as if it were verified (though possibly
+    // unsuccessfully) with given tsig_rdata.  If the error parameter is
+    // given and not NOERROR, it's recorded inside the context so that the
+    // subsequent sign() will behave accordingly.
+    void verifyTentative(ConstTSIGRecordPtr tsig,
+                         TSIGError error = TSIGError::NOERROR());
+
+    /// \name Protocol constants and defaults
+    ///
+    //@{
+    /// The recommended fudge value (in seconds) by RFC2845.
+    ///
+    /// Right now fudge is not tunable, and all TSIGs generated by this API
+    /// will have this value of fudge.
+    static const uint16_t DEFAULT_FUDGE = 300;
+    //@}
+
+private:
+    struct TSIGContextImpl;
+    TSIGContextImpl* impl_;
+};
+}
+}
+
+#endif  // __TSIG_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/tsigerror.cc b/src/lib/dns/tsigerror.cc
new file mode 100644
index 0000000..e63c9ab
--- /dev/null
+++ b/src/lib/dns/tsigerror.cc
@@ -0,0 +1,57 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <ostream>
+#include <string>
+
+#include <boost/lexical_cast.hpp>
+
+#include <exceptions/exceptions.h>
+
+#include <dns/rcode.h>
+#include <dns/tsigerror.h>
+
+namespace isc {
+namespace dns {
+namespace {
+const char* const tsigerror_text[] = {
+    "BADSIG",
+    "BADKEY",
+    "BADTIME"
+};
+}
+
+TSIGError::TSIGError(Rcode rcode) : code_(rcode.getCode()) {
+    if (code_ > MAX_RCODE_FOR_TSIGERROR) {
+        isc_throw(OutOfRange, "Invalid RCODE for TSIG Error: " << rcode);
+    }
+}
+
+std::string
+TSIGError::toText() const {
+    if (code_ <= MAX_RCODE_FOR_TSIGERROR) {
+        return (Rcode(code_).toText());
+    } else if (code_ <= BAD_TIME_CODE) {
+        return (tsigerror_text[code_ - (MAX_RCODE_FOR_TSIGERROR + 1)]);
+    } else {
+        return (boost::lexical_cast<std::string>(code_));
+    }
+}
+
+std::ostream&
+operator<<(std::ostream& os, const TSIGError& error) {
+    return (os << error.toText());
+}
+} // namespace dns
+} // namespace isc
diff --git a/src/lib/dns/tsigerror.h b/src/lib/dns/tsigerror.h
new file mode 100644
index 0000000..4463daf
--- /dev/null
+++ b/src/lib/dns/tsigerror.h
@@ -0,0 +1,328 @@
+// 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 __TSIGERROR_H
+#define __TSIGERROR_H 1
+
+#include <ostream>
+#include <string>
+
+#include <dns/rcode.h>
+
+namespace isc {
+namespace dns {
+
+class RRClass;
+
+/// TSIG errors
+///
+/// The \c TSIGError class objects represent standard errors related to
+/// TSIG protocol operations as defined in related specifications, mainly
+/// in RFC2845.
+///
+/// (RCODEs) of the header section of DNS messages, and extended response
+/// codes as defined in the EDNS specification.
+class TSIGError {
+public:
+    /// Constants for pre-defined TSIG error values.
+    ///
+    /// Code values from 0 through 15 (inclusive) are derived from those of
+    /// RCODE and are not defined here.  See the \c Rcode class.
+    ///
+    /// \note Unfortunately some systems define "BADSIG" as a macro in a public
+    /// header file.  To avoid conflict with it we add an underscore to our
+    /// definitions.
+    enum CodeValue {
+        BAD_SIG_CODE = 16, ///< 16: TSIG verification failure
+        BAD_KEY_CODE = 17, ///< 17: TSIG key is not recognized
+        BAD_TIME_CODE = 18 ///< 18: Current time and time signed are too different
+    };
+
+    /// \name Constructors
+    ///
+    /// We use the default versions of destructor, copy constructor,
+    /// and assignment operator.
+    //@{
+    /// Constructor from the code value.
+    ///
+    /// \exception None
+    ///
+    /// \param code The underlying 16-bit error code value of the \c TSIGError.
+    explicit TSIGError(uint16_t error_code) : code_(error_code) {}
+
+    /// Constructor from \c Rcode.
+    ///
+    /// As defined in RFC2845, error code values from 0 to 15 (inclusive) are
+    /// derived from the DNS RCODEs, which are represented via the \c Rcode
+    /// class in this library.  This constructor works as a converter from
+    /// these RCODEs to corresponding TSIGError objects.
+    ///
+    /// \exception isc::OutOfRange Given rcode is not convertible to
+    /// TSIGErrors.
+    ///
+    /// \param rcode the \c Rcode from which the TSIGError should be derived.
+    explicit TSIGError(Rcode rcode);
+    //@}
+
+    /// \brief Returns the \c TSIGCode error code value.
+    ///
+    /// \exception None
+    ///
+    /// \return The underlying code value corresponding to the \c TSIGError.
+    uint16_t getCode() const { return (code_); }
+
+    /// \brief Return true iff two \c TSIGError objects are equal.
+    ///
+    /// Two TSIGError objects are equal iff their error codes are equal.
+    ///
+    /// \exception None
+    ///
+    /// \param other the \c TSIGError object to compare against.
+    /// \return true if the two TSIGError are equal; otherwise false.
+    bool equals(const TSIGError& other) const
+    { return (code_ == other.code_); }
+
+    /// \brief Same as \c equals().
+    bool operator==(const TSIGError& other) const { return (equals(other)); }
+
+    /// \brief Return true iff two \c TSIGError objects are not equal.
+    ///
+    /// \exception None
+    ///
+    /// \param other the \c TSIGError object to compare against.
+    /// \return true if the two TSIGError objects are not equal;
+    /// otherwise false.
+    bool nequals(const TSIGError& other) const
+    { return (code_ != other.code_); }
+
+    /// \brief Same as \c nequals().
+    bool operator!=(const TSIGError& other) const { return (nequals(other)); }
+
+    /// \brief Convert the \c TSIGError to a string.
+    ///
+    /// For codes derived from RCODEs up to 15, this method returns the
+    /// same string as \c Rcode::toText() for the corresponding code.
+    /// For other pre-defined code values (see TSIGError::CodeValue),
+    /// this method returns a string representation of the "mnemonic' used
+    /// for the enum and constant objects as defined in RFC2845.
+    /// For example, the string for code value 16 is "BADSIG", etc.
+    /// For other code values it returns a string representation of the decimal
+    /// number of the value, e.g. "32", "100", etc.
+    ///
+    /// \exception std::bad_alloc Resource allocation for the string fails
+    ///
+    /// \return A string representation of the \c TSIGError.
+    std::string toText() const;
+
+    /// A constant TSIG error object derived from \c Rcode::NOERROR()
+    static const TSIGError& NOERROR();
+
+    /// A constant TSIG error object derived from \c Rcode::FORMERR()
+    static const TSIGError& FORMERR();
+
+    /// A constant TSIG error object derived from \c Rcode::SERVFAIL()
+    static const TSIGError& SERVFAIL();
+
+    /// A constant TSIG error object derived from \c Rcode::NXDOMAIN()
+    static const TSIGError& NXDOMAIN();
+
+    /// A constant TSIG error object derived from \c Rcode::NOTIMP()
+    static const TSIGError& NOTIMP();
+
+    /// A constant TSIG error object derived from \c Rcode::REFUSED()
+    static const TSIGError& REFUSED();
+
+    /// A constant TSIG error object derived from \c Rcode::YXDOMAIN()
+    static const TSIGError& YXDOMAIN();
+
+    /// A constant TSIG error object derived from \c Rcode::YXRRSET()
+    static const TSIGError& YXRRSET();
+
+    /// A constant TSIG error object derived from \c Rcode::NXRRSET()
+    static const TSIGError& NXRRSET();
+
+    /// A constant TSIG error object derived from \c Rcode::NOTAUTH()
+    static const TSIGError& NOTAUTH();
+
+    /// A constant TSIG error object derived from \c Rcode::NOTZONE()
+    static const TSIGError& NOTZONE();
+
+    /// A constant TSIG error object derived from \c Rcode::RESERVED11()
+    static const TSIGError& RESERVED11();
+
+    /// A constant TSIG error object derived from \c Rcode::RESERVED12()
+    static const TSIGError& RESERVED12();
+
+    /// A constant TSIG error object derived from \c Rcode::RESERVED13()
+    static const TSIGError& RESERVED13();
+
+    /// A constant TSIG error object derived from \c Rcode::RESERVED14()
+    static const TSIGError& RESERVED14();
+
+    /// A constant TSIG error object derived from \c Rcode::RESERVED15()
+    static const TSIGError& RESERVED15();
+
+    /// A constant TSIG error object for the BADSIG code
+    /// (see \c TSIGError::BAD_SIG_CODE).
+    static const TSIGError& BAD_SIG();
+
+    /// A constant TSIG error object for the BADKEY code
+    /// (see \c TSIGError::BAD_KEY_CODE).
+    static const TSIGError& BAD_KEY();
+
+    /// A constant TSIG error object for the BADTIME code
+    /// (see \c TSIGError::BAD_TIME_CODE).
+    static const TSIGError& BAD_TIME();
+
+private:
+    // This is internally used to specify the maximum possible RCODE value
+    // that can be convertible to TSIGErrors.
+    static const int MAX_RCODE_FOR_TSIGERROR = 15;
+
+    uint16_t code_;
+};
+
+inline const TSIGError&
+TSIGError::NOERROR() {
+    static TSIGError e(Rcode::NOERROR());
+    return (e);
+}
+
+inline const TSIGError&
+TSIGError::FORMERR() {
+    static TSIGError e(Rcode::FORMERR());
+    return (e);
+}
+
+inline const TSIGError&
+TSIGError::SERVFAIL() {
+    static TSIGError e(Rcode::SERVFAIL());
+    return (e);
+}
+
+inline const TSIGError&
+TSIGError::NXDOMAIN() {
+    static TSIGError e(Rcode::NXDOMAIN());
+    return (e);
+}
+
+inline const TSIGError&
+TSIGError::NOTIMP() {
+    static TSIGError e(Rcode::NOTIMP());
+    return (e);
+}
+
+inline const TSIGError&
+TSIGError::REFUSED() {
+    static TSIGError e(Rcode::REFUSED());
+    return (e);
+}
+
+inline const TSIGError&
+TSIGError::YXDOMAIN() {
+    static TSIGError e(Rcode::YXDOMAIN());
+    return (e);
+}
+
+inline const TSIGError&
+TSIGError::YXRRSET() {
+    static TSIGError e(Rcode::YXRRSET());
+    return (e);
+}
+
+inline const TSIGError&
+TSIGError::NXRRSET() {
+    static TSIGError e(Rcode::NXRRSET());
+    return (e);
+}
+
+inline const TSIGError&
+TSIGError::NOTAUTH() {
+    static TSIGError e(Rcode::NOTAUTH());
+    return (e);
+}
+
+inline const TSIGError&
+TSIGError::NOTZONE() {
+    static TSIGError e(Rcode::NOTZONE());
+    return (e);
+}
+
+inline const TSIGError&
+TSIGError::RESERVED11() {
+    static TSIGError e(Rcode::RESERVED11());
+    return (e);
+}
+
+inline const TSIGError&
+TSIGError::RESERVED12() {
+    static TSIGError e(Rcode::RESERVED12());
+    return (e);
+}
+
+inline const TSIGError&
+TSIGError::RESERVED13() {
+    static TSIGError e(Rcode::RESERVED13());
+    return (e);
+}
+
+inline const TSIGError&
+TSIGError::RESERVED14() {
+    static TSIGError e(Rcode::RESERVED14());
+    return (e);
+}
+
+inline const TSIGError&
+TSIGError::RESERVED15() {
+    static TSIGError e(Rcode::RESERVED15());
+    return (e);
+}
+
+inline const TSIGError&
+TSIGError::BAD_SIG() {
+    static TSIGError e(BAD_SIG_CODE);
+    return (e);
+}
+
+inline const TSIGError&
+TSIGError::BAD_KEY() {
+    static TSIGError e(BAD_KEY_CODE);
+    return (e);
+}
+
+inline const TSIGError&
+TSIGError::BAD_TIME() {
+    static TSIGError e(BAD_TIME_CODE);
+    return (e);
+}
+
+/// Insert the \c TSIGError as a string into stream.
+///
+/// This method convert \c tsig_error into a string and inserts it into the
+/// output stream \c os.
+///
+/// \param os A \c std::ostream object on which the insertion operation is
+/// performed.
+/// \param tsig_error An \c TSIGError object output by the operation.
+/// \return A reference to the same \c std::ostream object referenced by
+/// parameter \c os after the insertion operation.
+std::ostream& operator<<(std::ostream& os, const TSIGError& tsig_error);
+}
+}
+
+#endif  // __TSIGERROR_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/tsigkey.cc b/src/lib/dns/tsigkey.cc
index a3eb76b..615b5b6 100644
--- a/src/lib/dns/tsigkey.cc
+++ b/src/lib/dns/tsigkey.cc
@@ -26,47 +26,58 @@
 #include <dns/tsigkey.h>
 
 using namespace std;
+using namespace isc::cryptolink;
 
+namespace isc {
+namespace dns {
 namespace {
-    bool isValidAlgorithmName(const isc::dns::Name& name) {
-        return (name == isc::dns::TSIGKey::HMACMD5_NAME() ||
-                name == isc::dns::TSIGKey::HMACSHA1_NAME() ||
-                name == isc::dns::TSIGKey::HMACSHA256_NAME());
+    HashAlgorithm
+    convertAlgorithmName(const isc::dns::Name& name) {
+        if (name == TSIGKey::HMACMD5_NAME()) {
+            return (isc::cryptolink::MD5);
+        }
+        if (name == TSIGKey::HMACSHA1_NAME()) {
+            return (isc::cryptolink::SHA1);
+        }
+        if (name == TSIGKey::HMACSHA256_NAME()) {
+            return (isc::cryptolink::SHA256);
+        }
+        isc_throw(InvalidParameter,
+                  "Unknown TSIG algorithm is specified: " << name);
     }
 }
 
-namespace isc {
-namespace dns {
 struct
 TSIGKey::TSIGKeyImpl {
     TSIGKeyImpl(const Name& key_name, const Name& algorithm_name,
+                isc::cryptolink::HashAlgorithm algorithm,
                 const void* secret, size_t secret_len) :
         key_name_(key_name), algorithm_name_(algorithm_name),
+        algorithm_(algorithm),
         secret_(static_cast<const uint8_t*>(secret),
                 static_cast<const uint8_t*>(secret) + secret_len)
     {
-        // Convert the name to the canonical form.
+        // Convert the key and algorithm names to the canonical form.
+        key_name_.downcase();
         algorithm_name_.downcase();
     }
-    const Name key_name_;
+    Name key_name_;
     Name algorithm_name_;
+    const isc::cryptolink::HashAlgorithm algorithm_;
     const vector<uint8_t> secret_;
 };
 
 TSIGKey::TSIGKey(const Name& key_name, const Name& algorithm_name,
                  const void* secret, size_t secret_len) : impl_(NULL)
 {
-    if (!isValidAlgorithmName(algorithm_name)) {
-        isc_throw(InvalidParameter, "Unknown TSIG algorithm is specified: " <<
-                  algorithm_name);
-    }
+    const HashAlgorithm algorithm = convertAlgorithmName(algorithm_name);
     if ((secret != NULL && secret_len == 0) ||
         (secret == NULL && secret_len != 0)) {
         isc_throw(InvalidParameter,
                   "TSIGKey secret and its length are inconsistent");
     }
-
-    impl_ = new TSIGKeyImpl(key_name, algorithm_name, secret, secret_len);
+    impl_ = new TSIGKeyImpl(key_name, algorithm_name, algorithm, secret,
+                            secret_len);
 }
 
 TSIGKey::TSIGKey(const std::string& str) : impl_(NULL) {
@@ -95,16 +106,13 @@ TSIGKey::TSIGKey(const std::string& str) : impl_(NULL) {
 
         const Name algo_name(algo_str.empty() ? "hmac-md5.sig-alg.reg.int" :
                              algo_str);
-        if (!isValidAlgorithmName(algo_name)) {
-            isc_throw(InvalidParameter, "Unknown TSIG algorithm is specified: " <<
-                      algo_name);
-        }
+        const HashAlgorithm algorithm = convertAlgorithmName(algo_name);
 
         vector<uint8_t> secret;
         util::encode::decodeBase64(secret_str, secret);
 
-        impl_ = new TSIGKeyImpl(Name(keyname_str), algo_name, &secret[0],
-                                secret.size());
+        impl_ = new TSIGKeyImpl(Name(keyname_str), algo_name, algorithm,
+                                &secret[0], secret.size());
     } catch (const Exception& e) {
         // 'reduce' the several types of exceptions name parsing and
         // Base64 decoding can throw to just the InvalidParameter
@@ -143,6 +151,11 @@ TSIGKey::getAlgorithmName() const {
     return (impl_->algorithm_name_);
 }
 
+isc::cryptolink::HashAlgorithm
+TSIGKey::getCryptoAlgorithm() const {
+    return (impl_->algorithm_);
+}
+
 const void*
 TSIGKey::getSecret() const {
     return ((impl_->secret_.size() > 0) ? &impl_->secret_[0] : NULL);
diff --git a/src/lib/dns/tsigkey.h b/src/lib/dns/tsigkey.h
index ca9e9ec..4136278 100644
--- a/src/lib/dns/tsigkey.h
+++ b/src/lib/dns/tsigkey.h
@@ -15,6 +15,8 @@
 #ifndef __TSIGKEY_H
 #define __TSIGKEY_H 1
 
+#include <cryptolink/cryptolink.h>
+
 namespace isc {
 namespace dns {
 
@@ -142,6 +144,9 @@ public:
     /// Return the algorithm name.
     const Name& getAlgorithmName() const;
 
+    /// Return the hash algorithm name in the form of cryptolink::HashAlgorithm
+    isc::cryptolink::HashAlgorithm getCryptoAlgorithm() const;
+
     /// Return the length of the TSIG secret in bytes.
     size_t getSecretLength() const;
 
diff --git a/src/lib/python/isc/notify/tests/Makefile.am b/src/lib/python/isc/notify/tests/Makefile.am
index 07129ec..a83ff86 100644
--- a/src/lib/python/isc/notify/tests/Makefile.am
+++ b/src/lib/python/isc/notify/tests/Makefile.am
@@ -6,7 +6,7 @@ EXTRA_DIST = $(PYTESTS)
 # required by loadable python modules.
 LIBRARY_PATH_PLACEHOLDER =
 if SET_ENV_LIBRARY_PATH
-LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
 endif
 
 # test using command-line arguments, so use check-local target instead of TESTS
diff --git a/src/lib/util/unittests/Makefile.am b/src/lib/util/unittests/Makefile.am
index b702647..e7cb447 100644
--- a/src/lib/util/unittests/Makefile.am
+++ b/src/lib/util/unittests/Makefile.am
@@ -3,5 +3,6 @@ AM_CXXFLAGS = $(B10_CXXFLAGS)
 
 lib_LTLIBRARIES = libutil_unittests.la
 libutil_unittests_la_SOURCES = fork.h fork.cc
+libutil_unittests_la_SOURCES += newhook.h newhook.cc
 
 CLEANFILES = *.gcno *.gcda
diff --git a/src/lib/util/unittests/newhook.cc b/src/lib/util/unittests/newhook.cc
new file mode 100644
index 0000000..b9d9fb6
--- /dev/null
+++ b/src/lib/util/unittests/newhook.cc
@@ -0,0 +1,51 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <stdlib.h>
+
+#include <new>
+#include <stdexcept>
+
+#include "newhook.h"
+
+#ifdef ENABLE_CUSTOM_OPERATOR_NEW
+void*
+operator new(size_t size) throw(std::bad_alloc) {
+    if (isc::util::unittests::force_throw_on_new &&
+        size == isc::util::unittests::throw_size_on_new) {
+        throw std::bad_alloc();
+    }
+    void* p = malloc(size);
+    if (p == NULL) {
+        throw std::bad_alloc();
+    }
+    return (p);
+}
+
+void
+operator delete(void* p) throw() {
+    if (p != NULL) {
+        free (p);
+    }
+}
+#endif
+
+namespace isc {
+namespace util {
+namespace unittests {
+bool force_throw_on_new = false;
+size_t throw_size_on_new = 0;
+}
+}
+}
diff --git a/src/lib/util/unittests/newhook.h b/src/lib/util/unittests/newhook.h
new file mode 100644
index 0000000..b0dbb64
--- /dev/null
+++ b/src/lib/util/unittests/newhook.h
@@ -0,0 +1,76 @@
+// 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 __UTIL_UNITTESTS_NEWHOOK_H
+#define __UTIL_UNITTESTS_NEWHOOK_H 1
+
+/**
+ * @file newhook.h
+ * @short Enable the use of special operator new that throws for testing.
+ *
+ * This small utility allows a test case to force the global operator new
+ * to throw for a given size to test a case where memory allocation fails
+ * (which normally doesn't happen).  To enable the feature, everything must
+ * be built with defining ENABLE_CUSTOM_OPERATOR_NEW beforehand, and set
+ * \c force_throw_on_new to \c true and \c throw_size_on_new to the size
+ * of data that should trigger the exception, immediately before starting
+ * the specific test that needs the exception.
+ *
+ * Example:
+ * \code #include <util/unittests/newhook.h>
+ * ...
+ * TEST(SomeTest, newException) {
+ *        isc::util::unittests::force_throw_on_new = true;
+ *        isc::util::unittests::throw_size_on_new = sizeof(Foo);
+ *        try {
+ *            // this will do 'new Foo()' internally and should throw
+ *            createFoo();
+ *            isc::util::unittests::force_throw_on_new = false;
+ *            ASSERT_FALSE(true) << "Expected throw on new";
+ *        } catch (const std::bad_alloc&) {
+ *            isc::util::unittests::force_throw_on_new = false;
+ *            // do some integrity check, etc, if necessary
+ *        }
+ * } \endcode
+ *
+ * Replacing the global operator new (and delete) is a dangerous technique,
+ * and triggering an exception solely based on the allocation size is not
+ * reliable, so this feature is disabled by default two-fold: The
+ * ENABLE_CUSTOM_OPERATOR_NEW build time variable, and run-time
+ * \c force_throw_on_new.
+ */
+
+namespace isc {
+namespace util {
+namespace unittests {
+/// Switch to enable the use of special operator new
+///
+/// This is set to \c false by default.
+extern bool force_throw_on_new;
+
+/// The allocation size that triggers an exception in the special operator new
+///
+/// The default value is 0.  The value of this variable has no meaning
+/// unless the use of the special operator is enabled at build time and
+/// via \c force_throw_on_new.
+extern size_t throw_size_on_new;
+}
+}
+}
+
+#endif // __UTIL_UNITTESTS_NEWHOOK_H
+
+// Local Variables:
+// mode: c++
+// End:




More information about the bind10-changes mailing list