Browse Source

merged trac #381 (tsigkey class)

git-svn-id: svn://bind10.isc.org/svn/bind10/trunk@3622 e5f2f494-b856-4b98-b285-d166d9295462
JINMEI Tatuya 14 years ago
parent
commit
f4e7402099

+ 2 - 2
src/lib/dns/Makefile.am

@@ -81,7 +81,7 @@ libdns___la_SOURCES += rrttl.h rrttl.cc
 libdns___la_SOURCES += rrtype.cc
 libdns___la_SOURCES += rrtype.cc
 libdns___la_SOURCES += question.h question.cc
 libdns___la_SOURCES += question.h question.cc
 libdns___la_SOURCES += util/sha1.h util/sha1.cc
 libdns___la_SOURCES += util/sha1.h util/sha1.cc
-libdns___la_SOURCES += tsig.h tsig.cc
+libdns___la_SOURCES += tsigkey.h tsigkey.cc
 
 
 nodist_libdns___la_SOURCES = rdataclass.cc rrclass.h rrtype.h
 nodist_libdns___la_SOURCES = rdataclass.cc rrclass.h rrtype.h
 nodist_libdns___la_SOURCES += rrparamregistry.cc
 nodist_libdns___la_SOURCES += rrparamregistry.cc
@@ -109,7 +109,7 @@ libdns___include_HEADERS = \
 	rrsetlist.h \
 	rrsetlist.h \
 	rrttl.h \
 	rrttl.h \
 	rrtype.h \
 	rrtype.h \
-	tsig.h
+	tsigkey.h
 # Purposely not installing these headers:
 # Purposely not installing these headers:
 # util/*.h: used only internally, and not actually DNS specific
 # util/*.h: used only internally, and not actually DNS specific
 # rrclass-placeholder.h
 # rrclass-placeholder.h

+ 1 - 0
src/lib/dns/python/Makefile.am

@@ -24,6 +24,7 @@ EXTRA_DIST += question_python.cc
 EXTRA_DIST += rrttl_python.cc
 EXTRA_DIST += rrttl_python.cc
 EXTRA_DIST += rdata_python.cc
 EXTRA_DIST += rdata_python.cc
 EXTRA_DIST += rrtype_python.cc
 EXTRA_DIST += rrtype_python.cc
+EXTRA_DIST += tsigkey_python.cc
 
 
 # Python prefers .so, while some OSes (specifically MacOS) use a different
 # Python prefers .so, while some OSes (specifically MacOS) use a different
 # suffix for dynamic objects.  -module is necessary to work this around.
 # suffix for dynamic objects.  -module is necessary to work this around.

+ 9 - 0
src/lib/dns/python/pydnspp.cc

@@ -57,6 +57,7 @@ static PyObject* po_DNSMessageBADVERS;
 #include <dns/python/rrset_python.cc>          // needs Rdata, RRTTL
 #include <dns/python/rrset_python.cc>          // needs Rdata, RRTTL
 #include <dns/python/question_python.cc>       // needs RRClass, RRType, RRTTL,
 #include <dns/python/question_python.cc>       // needs RRClass, RRType, RRTTL,
                                                // Name
                                                // Name
+#include <dns/python/tsigkey_python.cc>        // needs Name
 #include <dns/python/opcode_python.cc>
 #include <dns/python/opcode_python.cc>
 #include <dns/python/rcode_python.cc>
 #include <dns/python/rcode_python.cc>
 #include <dns/python/edns_python.cc>           // needs Messagerenderer, Rcode
 #include <dns/python/edns_python.cc>           // needs Messagerenderer, Rcode
@@ -146,6 +147,14 @@ PyInit_pydnspp(void) {
         return (NULL);
         return (NULL);
     }
     }
 
 
+    if (!initModulePart_TSIGKey(mod)) {
+        return (NULL);
+    }
+
+    if (!initModulePart_TSIGKeyRing(mod)) {
+        return (NULL);
+    }
+
     return (mod);
     return (mod);
 }
 }
 
 

+ 1 - 0
src/lib/dns/python/tests/Makefile.am

@@ -10,6 +10,7 @@ PYTESTS += rrclass_python_test.py
 PYTESTS += rrset_python_test.py
 PYTESTS += rrset_python_test.py
 PYTESTS += rrttl_python_test.py
 PYTESTS += rrttl_python_test.py
 PYTESTS += rrtype_python_test.py
 PYTESTS += rrtype_python_test.py
+PYTESTS += tsigkey_python_test.py
 
 
 EXTRA_DIST = $(PYTESTS)
 EXTRA_DIST = $(PYTESTS)
 EXTRA_DIST += testutil.py
 EXTRA_DIST += testutil.py

+ 174 - 0
src/lib/dns/python/tests/tsigkey_python_test.py

@@ -0,0 +1,174 @@
+# Copyright (C) 2010  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.
+
+# $Id$
+
+import unittest
+from pydnspp import *
+
+class TSIGKeyTest(unittest.TestCase):
+    key_name = Name('example.com')
+    secret = b'anotherRandomData'
+
+    def test_algorithm_names(self):
+        self.assertEqual(Name('hmac-md5.sig-alg.reg.int'),
+                         TSIGKey.HMACMD5_NAME)
+        self.assertEqual(Name('hmac-sha1'), TSIGKey.HMACSHA1_NAME)
+        self.assertEqual(Name('hmac-sha256'), TSIGKey.HMACSHA256_NAME)
+
+    def test_init(self):
+        key = TSIGKey(self.key_name, TSIGKey.HMACMD5_NAME, self.secret)
+        self.assertEqual(self.key_name, key.get_key_name())
+        self.assertEqual(Name('hmac-md5.sig-alg.reg.int'),
+                         key.get_algorithm_name())
+        self.assertEqual(self.secret, key.get_secret())
+
+        self.assertRaises(InvalidParameter, TSIGKey, self.key_name,
+                          Name('unknown-alg'), self.secret)
+
+        self.assertEqual('hmac-sha1.',
+                         TSIGKey(self.key_name, TSIGKey.HMACSHA1_NAME,
+                                 self.secret).get_algorithm_name().to_text())
+
+        self.assertRaises(TypeError, TSIGKey, self.key_name,
+                          TSIGKey.HMACMD5_NAME,
+                          'should be binary') # signature mismatch
+
+class TSIGKeyRingTest(unittest.TestCase):
+    key_name = Name('example.com')
+    secret = b'someRandomData'
+
+    def setUp(self):
+        self.keyring = TSIGKeyRing()
+
+    def test_init(self):
+        self.assertEqual(0, self.keyring.size())
+        self.assertRaises(TypeError, TSIGKeyRing, 1)
+        self.assertRaises(TypeError, TSIGKeyRing, 'there should not be arg')
+
+    def test_add(self):
+        self.assertEqual(TSIGKeyRing.SUCCESS,
+                         self.keyring.add(TSIGKey(self.key_name,
+                                                  TSIGKey.HMACSHA256_NAME,
+                                                  self.secret)))
+        self.assertEqual(1, self.keyring.size())
+        self.assertEqual(TSIGKeyRing.EXIST,
+                         self.keyring.add(TSIGKey(self.key_name,
+                                                  TSIGKey.HMACSHA256_NAME,
+                                                  self.secret)))
+        self.assertEqual(TSIGKeyRing.EXIST,
+                         self.keyring.add(TSIGKey(self.key_name,
+                                                  TSIGKey.HMACSHA1_NAME,
+                                                  self.secret)))
+        self.assertEqual(TSIGKeyRing.EXIST,
+                         self.keyring.add(TSIGKey(Name('EXAMPLE.COM'),
+                                                  TSIGKey.HMACSHA1_NAME,
+                                                  self.secret)))
+        self.assertEqual(1, self.keyring.size())
+
+    def test_add_more(self):
+        self.assertEqual(TSIGKeyRing.SUCCESS,
+                         self.keyring.add(TSIGKey(self.key_name,
+                                                  TSIGKey.HMACSHA256_NAME,
+                                                  self.secret)))
+        self.assertEqual(TSIGKeyRing.SUCCESS,
+                         self.keyring.add(TSIGKey(Name('another.example'),
+                                                  TSIGKey.HMACMD5_NAME,
+                                                  self.secret)))
+        self.assertEqual(TSIGKeyRing.SUCCESS,
+                         self.keyring.add(TSIGKey(Name('more.example'),
+                                                  TSIGKey.HMACSHA1_NAME,
+                                                  self.secret)))
+        self.assertEqual(3, self.keyring.size())
+
+        self.assertRaises(TypeError, self.keyring.add, 1)
+        self.assertRaises(TypeError, self.keyring.add, 'invalid arg')
+
+    def test_remove(self):
+        self.assertEqual(TSIGKeyRing.SUCCESS,
+                         self.keyring.add(TSIGKey(self.key_name,
+                                                  TSIGKey.HMACSHA256_NAME,
+                                                  self.secret)))
+        self.assertEqual(TSIGKeyRing.SUCCESS,
+                         self.keyring.remove(self.key_name))
+        self.assertEqual(TSIGKeyRing.NOTFOUND,
+                         self.keyring.remove(self.key_name))
+
+        self.assertRaises(TypeError, self.keyring.add, 1)
+        self.assertRaises(TypeError, self.keyring.add, 'invalid arg')
+        self.assertRaises(TypeError, self.keyring.add, self.key_name, 0)
+
+    def test_remove_from_some(self):
+        self.assertEqual(TSIGKeyRing.SUCCESS,
+                         self.keyring.add(TSIGKey(self.key_name,
+                                                  TSIGKey.HMACSHA256_NAME,
+                                                  self.secret)))
+        self.assertEqual(TSIGKeyRing.SUCCESS,
+                         self.keyring.add(TSIGKey(Name('another.example'),
+                                                  TSIGKey.HMACMD5_NAME,
+                                                  self.secret)))
+        self.assertEqual(TSIGKeyRing.SUCCESS,
+                         self.keyring.add(TSIGKey(Name('more.example'),
+                                                  TSIGKey.HMACSHA1_NAME,
+                                                  self.secret)))
+
+        self.assertEqual(TSIGKeyRing.SUCCESS,
+                         self.keyring.remove(Name('another.example')))
+        self.assertEqual(TSIGKeyRing.NOTFOUND,
+                         self.keyring.remove(Name('noexist.example')))
+        self.assertEqual(2, self.keyring.size())
+
+    def test_find(self):
+        self.assertEqual((TSIGKeyRing.NOTFOUND, None),
+                         self.keyring.find(self.key_name))
+
+        self.assertEqual(TSIGKeyRing.SUCCESS,
+                         self.keyring.add(TSIGKey(self.key_name,
+                                                  TSIGKey.HMACSHA256_NAME,
+                                                  self.secret)))
+        (code, key) = self.keyring.find(self.key_name)
+        self.assertEqual(TSIGKeyRing.SUCCESS, code)
+        self.assertEqual(self.key_name, key.get_key_name())
+        self.assertEqual(TSIGKey.HMACSHA256_NAME, key.get_algorithm_name())
+        self.assertEqual(self.secret, key.get_secret())
+
+        self.assertRaises(TypeError, self.keyring.find, 1)
+        self.assertRaises(TypeError, self.keyring.find, 'should be a name')
+        self.assertRaises(TypeError, self.keyring.find, self.key_name, 0)
+
+    def test_find_from_some(self):
+        self.assertEqual(TSIGKeyRing.SUCCESS,
+                         self.keyring.add(TSIGKey(self.key_name,
+                                                  TSIGKey.HMACSHA256_NAME,
+                                                  self.secret)))
+        self.assertEqual(TSIGKeyRing.SUCCESS,
+                         self.keyring.add(TSIGKey(Name('another.example'),
+                                                  TSIGKey.HMACMD5_NAME,
+                                                  self.secret)))
+        self.assertEqual(TSIGKeyRing.SUCCESS,
+                         self.keyring.add(TSIGKey(Name('more.example'),
+                                                  TSIGKey.HMACSHA1_NAME,
+                                                  self.secret)))
+
+        (code, key) = self.keyring.find(Name('another.example'))
+        self.assertEqual(TSIGKeyRing.SUCCESS, code)
+        self.assertEqual(Name('another.example'), key.get_key_name())
+        self.assertEqual(TSIGKey.HMACMD5_NAME, key.get_algorithm_name())
+
+        self.assertEqual((TSIGKeyRing.NOTFOUND, None),
+                         self.keyring.find(Name('noexist.example')))
+
+if __name__ == '__main__':
+    unittest.main()

+ 455 - 0
src/lib/dns/python/tsigkey_python.cc

@@ -0,0 +1,455 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <new>
+
+#include <dns/tsigkey.h>
+
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+
+//
+// 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
+
+namespace {
+//
+// TSIGKey
+//
+
+// The s_* Class simply covers one instantiation of the object
+
+class s_TSIGKey : public PyObject {
+public:
+    s_TSIGKey() : tsigkey(NULL) {}
+    TSIGKey* tsigkey;
+};
+
+//
+// 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 TSIGKey_init(s_TSIGKey* self, PyObject* args);
+void TSIGKey_destroy(s_TSIGKey* self);
+
+// These are the functions we export
+// This is a second version of toText, we need one where the argument
+// is a PyObject*, for the str() function in python.
+PyObject* TSIGKey_getKeyName(const s_TSIGKey* self);
+PyObject* TSIGKey_getAlgorithmName(const s_TSIGKey* self);
+PyObject* TSIGKey_getSecret(const s_TSIGKey* self);
+
+// 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 TSIGKey_methods[] = {
+    { "get_key_name",
+      reinterpret_cast<PyCFunction>(TSIGKey_getKeyName), METH_NOARGS,
+      "Return the key name." },
+    { "get_algorithm_name",
+      reinterpret_cast<PyCFunction>(TSIGKey_getAlgorithmName), METH_NOARGS,
+      "Return the algorithm name." },
+    { "get_secret",
+      reinterpret_cast<PyCFunction>(TSIGKey_getSecret), METH_NOARGS,
+      "Return the value of the TSIG secret." },
+    { NULL, NULL, 0, NULL }
+};
+
+// 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",
+    sizeof(s_TSIGKey),                  // tp_basicsize
+    0,                                  // tp_itemsize
+    (destructor)TSIGKey_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 TSIGKey class holds a TSIG key along with some related attributes as "
+    "defined in RFC2845.",
+    NULL,                               // tp_traverse
+    NULL,                               // tp_clear
+    NULL,                               // tp_richcompare
+    0,                                  // tp_weaklistoffset
+    NULL,                               // tp_iter
+    NULL,                               // tp_iternext
+    TSIGKey_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)TSIGKey_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
+};
+
+// 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 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) != -1) {
+        try {
+            self->tsigkey = new TSIGKey(*key_name->name,
+                                        *algorithm_name->name,
+                                        secret, secret_len);
+        } catch (const isc::InvalidParameter& ex) {
+            PyErr_SetString(po_InvalidParameter, ex.what());
+            return (-1);
+        } catch (...) {
+            PyErr_SetString(po_IscException, "Unexpected exception");
+            return (-1);
+        }
+        return (0);
+    }
+
+    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()));
+}
+
+// Module Initialization, all statics are initialized here
+bool
+initModulePart_TSIGKey(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(&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);
+    }
+
+    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;
+    }
+    addClassVariable(tsigkey_type, "HMACSHA256_NAME", name);
+
+    return (true);
+
+  cleanup:
+    Py_DECREF(&tsigkey_type);
+    return (false);
+}
+//
+// End of TSIGKey
+//
+
+//
+// TSIGKeyRing
+//
+
+// The s_* Class simply covers one instantiation of the object
+
+// The s_* Class simply covers one instantiation of the object
+
+class s_TSIGKeyRing : public PyObject {
+public:
+    s_TSIGKeyRing() : keyring(NULL) {}
+    TSIGKeyRing* keyring;
+};
+
+//
+// We declare the functions here, the definitions are below
+// the type definition of the object, since both can use the other
+//
+
+int TSIGKeyRing_init(s_TSIGKeyRing* self, PyObject* args);
+void TSIGKeyRing_destroy(s_TSIGKeyRing* self);
+
+PyObject* TSIGKeyRing_size(const s_TSIGKeyRing* self);
+PyObject* TSIGKeyRing_add(const s_TSIGKeyRing* self, PyObject* args);
+PyObject* TSIGKeyRing_remove(const s_TSIGKeyRing* self, PyObject* args);
+PyObject* TSIGKeyRing_find(const s_TSIGKeyRing* self, PyObject* args);
+
+PyMethodDef TSIGKeyRing_methods[] = {
+    { "size", reinterpret_cast<PyCFunction>(TSIGKeyRing_size), METH_NOARGS,
+      "Return the number of keys stored in the TSIGKeyRing." },
+    { "add", reinterpret_cast<PyCFunction>(TSIGKeyRing_add), METH_VARARGS,
+      "Add a TSIGKey to the TSIGKeyRing." },
+    { "remove", reinterpret_cast<PyCFunction>(TSIGKeyRing_remove),
+      METH_VARARGS,
+      "Remove a TSIGKey for the given name from the TSIGKeyRing." },
+    { "find", reinterpret_cast<PyCFunction>(TSIGKeyRing_find), METH_VARARGS,
+      "Find a TSIGKey for the given name in the TSIGKeyRing. "
+      "It returns a tuple of (result_code, key)." },
+    { 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, "")) {
+        PyErr_Clear();
+        PyErr_SetString(PyExc_TypeError,
+                        "Invalid arguments to TSIGKeyRing constructor");
+        return (-1);
+    }
+    
+    self->keyring = new(nothrow) TSIGKeyRing();
+    if (self->keyring == NULL) {
+        PyErr_SetString(po_IscException, "Allocating TSIGKeyRing failed");
+        return (-1);
+    }
+
+    return (0);
+}
+
+void
+TSIGKeyRing_destroy(s_TSIGKeyRing* self) {
+    delete self->keyring;
+    self->keyring = NULL;
+    Py_TYPE(self)->tp_free(self);
+}
+
+PyObject*
+TSIGKeyRing_size(const s_TSIGKeyRing* const self) {
+    return (Py_BuildValue("I", self->keyring->size()));
+}
+
+PyObject*
+TSIGKeyRing_add(const s_TSIGKeyRing* const self, PyObject* args) {
+    s_TSIGKey* tsigkey;
+    
+    if (PyArg_ParseTuple(args, "O!", &tsigkey_type, &tsigkey)) {
+        try {
+            const TSIGKeyRing::Result result =
+                self->keyring->add(*tsigkey->tsigkey);
+            return (Py_BuildValue("I", result));
+        } catch (...) {
+            PyErr_SetString(po_IscException, "Unexpected exception");
+            return (NULL);
+        }
+    }
+
+    PyErr_Clear();
+    PyErr_SetString(PyExc_TypeError, "Invalid arguments to TSIGKeyRing.add");
+
+    return (NULL);
+}
+
+PyObject*
+TSIGKeyRing_remove(const s_TSIGKeyRing* self, PyObject* args) {
+    s_Name* key_name;
+
+    if (PyArg_ParseTuple(args, "O!", &name_type, &key_name)) {
+        const TSIGKeyRing::Result result =
+            self->keyring->remove(*key_name->name);
+        return (Py_BuildValue("I", result));
+    }
+
+    PyErr_Clear();
+    PyErr_SetString(PyExc_TypeError, "Invalid arguments to TSIGKeyRing.add");
+
+    return (NULL);
+}
+
+PyObject*
+TSIGKeyRing_find(const s_TSIGKeyRing* self, PyObject* args) {
+    s_Name* key_name;
+
+    if (PyArg_ParseTuple(args, "O!", &name_type, &key_name)) {
+        const TSIGKeyRing::FindResult result =
+            self->keyring->find(*key_name->name);
+        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) {
+                Py_DECREF(key);
+                PyErr_SetString(po_IscException,
+                                "Allocating TSIGKey object failed");
+                return (NULL);
+            }
+            return (Py_BuildValue("IN", result.code, key));
+        } else {
+            return (Py_BuildValue("Is", result.code, NULL));
+        }
+    }
+
+    return (NULL);
+}
+
+bool
+initModulePart_TSIGKeyRing(PyObject* mod) {
+    if (PyType_Ready(&tsigkeyring_type) < 0) {
+        return (false);
+    }
+    Py_INCREF(&tsigkeyring_type);
+    void* p = &tsigkeyring_type;
+    if (PyModule_AddObject(mod, "TSIGKeyRing",
+                           static_cast<PyObject*>(p)) != 0) {
+        Py_DECREF(&tsigkeyring_type);
+        return (false);
+    }
+
+    addClassVariable(tsigkeyring_type, "SUCCESS",
+                     Py_BuildValue("I", TSIGKeyRing::SUCCESS));
+    addClassVariable(tsigkeyring_type, "EXIST",
+                     Py_BuildValue("I", TSIGKeyRing::EXIST));
+    addClassVariable(tsigkeyring_type, "NOTFOUND",
+                     Py_BuildValue("I", TSIGKeyRing::NOTFOUND));
+
+    return (true);
+}
+
+} // end of unnamed namespace

+ 1 - 1
src/lib/dns/tests/Makefile.am

@@ -46,7 +46,7 @@ run_unittests_SOURCES += base32hex_unittest.cc
 run_unittests_SOURCES += base64_unittest.cc
 run_unittests_SOURCES += base64_unittest.cc
 run_unittests_SOURCES += hex_unittest.cc
 run_unittests_SOURCES += hex_unittest.cc
 run_unittests_SOURCES += sha1_unittest.cc
 run_unittests_SOURCES += sha1_unittest.cc
-run_unittests_SOURCES += tsig_unittest.cc
+run_unittests_SOURCES += tsigkey_unittest.cc
 run_unittests_SOURCES += run_unittests.cc
 run_unittests_SOURCES += run_unittests.cc
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
 run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)

+ 0 - 41
src/lib/dns/tests/tsig_unittest.cc

@@ -1,41 +0,0 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-// $Id: rrtype_unittest.cc 476 2010-01-19 00:29:28Z jinmei $
-
-#include <gtest/gtest.h>
-
-#include <dns/tsig.h>
-
-#include <dns/tests/unittest_util.h>
-
-using isc::UnitTestUtil;
-using namespace std;
-using namespace isc::dns;
-
-namespace {
-class TsigTest : public ::testing::Test {
-protected:
-    TsigTest() {}
-};
-
-// simple creation test to get the testing ball rolling
-TEST_F(TsigTest, creates) {
-    Tsig tsig(Name("example.com"), Tsig::HMACMD5, "someRandomData");
-    EXPECT_TRUE(1);
-}
-
-} // end namespace
-
-

+ 232 - 0
src/lib/dns/tests/tsigkey_unittest.cc

@@ -0,0 +1,232 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id: rrtype_unittest.cc 476 2010-01-19 00:29:28Z jinmei $
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include <exceptions/exceptions.h>
+
+#include <dns/tsigkey.h>
+
+#include <dns/tests/unittest_util.h>
+
+using namespace std;
+using namespace isc::dns;
+using isc::UnitTestUtil;
+
+namespace {
+class TSIGKeyTest : public ::testing::Test {
+protected:
+    TSIGKeyTest() : secret("someRandomData"), key_name("example.com") {}
+    string secret;
+    Name key_name;
+};
+
+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());
+}
+
+TEST_F(TSIGKeyTest, construct) {
+    TSIGKey key(key_name, TSIGKey::HMACMD5_NAME(),
+                secret.c_str(), secret.size());
+    EXPECT_EQ(key_name, key.getKeyName());
+    EXPECT_EQ(Name("hmac-md5.sig-alg.reg.int"), key.getAlgorithmName());
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, secret.c_str(),
+                        secret.size(), key.getSecret(), key.getSecretLength());
+
+    EXPECT_THROW(TSIGKey(key_name, Name("unknown-alg"),
+                         secret.c_str(), secret.size()),
+                 isc::InvalidParameter);
+
+    // The algorithm name should be converted to the canonical form.
+    EXPECT_EQ("hmac-sha1.",
+              TSIGKey(key_name, Name("HMAC-sha1"),
+                      secret.c_str(),
+                      secret.size()).getAlgorithmName().toText());
+
+    // Invalid combinations of secret and secret_len:
+    EXPECT_THROW(TSIGKey(key_name, TSIGKey::HMACSHA1_NAME(), secret.c_str(), 0),
+                 isc::InvalidParameter);
+    EXPECT_THROW(TSIGKey(key_name, TSIGKey::HMACSHA256_NAME(), NULL, 16),
+                 isc::InvalidParameter);
+}
+
+void
+compareTSIGKeys(const TSIGKey& expect, const TSIGKey& actual) {
+    EXPECT_EQ(expect.getKeyName(), actual.getKeyName());
+    EXPECT_EQ(expect.getAlgorithmName(), actual.getAlgorithmName());
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
+                        expect.getSecret(), expect.getSecretLength(),
+                        actual.getSecret(), actual.getSecretLength());
+}
+
+TEST_F(TSIGKeyTest, copyConstruct) {
+    const TSIGKey original(key_name, TSIGKey::HMACSHA256_NAME(),
+                           secret.c_str(), secret.size());
+    const TSIGKey copy(original);
+    compareTSIGKeys(original, copy);
+
+    // Check the copied data is valid even after the original is deleted
+    TSIGKey* copy2 = new TSIGKey(original);
+    TSIGKey copy3(*copy2);
+    delete copy2;
+    compareTSIGKeys(original, copy3);
+}
+
+TEST_F(TSIGKeyTest, assignment) {
+    const TSIGKey original(key_name, TSIGKey::HMACSHA256_NAME(),
+                           secret.c_str(), secret.size());
+    TSIGKey copy = original;
+    compareTSIGKeys(original, copy);
+
+    // Check if the copied data is valid even after the original is deleted
+    TSIGKey* copy2 = new TSIGKey(original);
+    TSIGKey copy3(original);
+    copy3 = *copy2;
+    delete copy2;
+    compareTSIGKeys(original, copy3);
+
+    // self assignment
+    copy = copy;
+    compareTSIGKeys(original, copy);
+}
+
+class TSIGKeyRingTest : public ::testing::Test {
+protected:
+    TSIGKeyRingTest() :
+        key_name("example.com"),
+        secretstring("anotherRandomData"),
+        secret(secretstring.c_str()),
+        secret_len(secretstring.size())
+    {}
+    TSIGKeyRing keyring;
+    Name key_name;
+private:
+    const string secretstring;
+protected:
+    const char* secret;
+    size_t secret_len;
+};
+
+TEST_F(TSIGKeyRingTest, init) {
+    EXPECT_EQ(0, keyring.size());
+}
+
+TEST_F(TSIGKeyRingTest, add) {
+    EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add(
+                  TSIGKey(key_name, TSIGKey::HMACSHA256_NAME(),
+                          secret, secret_len)));
+    EXPECT_EQ(1, keyring.size());
+    EXPECT_EQ(TSIGKeyRing::EXIST, keyring.add(
+                  TSIGKey(key_name, TSIGKey::HMACSHA256_NAME(),
+                          secret, secret_len)));
+    // keys are identified their names, the same name of key with a different
+    // algorithm would be considered a duplicate.
+    EXPECT_EQ(TSIGKeyRing::EXIST, keyring.add(
+                  TSIGKey(Name("example.com"), TSIGKey::HMACSHA1_NAME(),
+                          secret, secret_len)));
+    // names are compared in a case insensitive manner.
+    EXPECT_EQ(TSIGKeyRing::EXIST, keyring.add(
+                  TSIGKey(Name("EXAMPLE.COM"), TSIGKey::HMACSHA1_NAME(),
+                          secret, secret_len)));
+    EXPECT_EQ(1, keyring.size());
+}
+
+TEST_F(TSIGKeyRingTest, addMore) {
+    // essentially the same test, but try adding more than 1
+    EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add(
+                  TSIGKey(key_name, TSIGKey::HMACSHA256_NAME(),
+                          secret, secret_len)));
+    EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add(
+                  TSIGKey(Name("another.example"), TSIGKey::HMACMD5_NAME(),
+                          secret, secret_len)));
+    EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add(
+                  TSIGKey(Name("more.example"), TSIGKey::HMACSHA1_NAME(),
+                          secret, secret_len)));
+    EXPECT_EQ(3, keyring.size());
+}
+
+TEST_F(TSIGKeyRingTest, remove) {
+    EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add(
+                  TSIGKey(key_name, TSIGKey::HMACSHA256_NAME(),
+                          secret, secret_len)));
+    EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.remove(key_name));
+    EXPECT_EQ(TSIGKeyRing::NOTFOUND, keyring.remove(key_name));
+}
+
+TEST_F(TSIGKeyRingTest, removeFromSome) {
+    // essentially the same test, but try removing from a larger set
+
+    EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add(
+                  TSIGKey(key_name, TSIGKey::HMACSHA256_NAME(),
+                          secret, secret_len)));
+    EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add(
+                  TSIGKey(Name("another.example"), TSIGKey::HMACMD5_NAME(),
+                          secret, secret_len)));
+    EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add(
+                  TSIGKey(Name("more.example"), TSIGKey::HMACSHA1_NAME(),
+                          secret, secret_len)));
+
+    EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.remove(Name("another.example")));
+    EXPECT_EQ(TSIGKeyRing::NOTFOUND, keyring.remove(Name("noexist.example")));
+    EXPECT_EQ(2, keyring.size());
+}
+
+TEST_F(TSIGKeyRingTest, find) {
+    EXPECT_EQ(TSIGKeyRing::NOTFOUND, keyring.find(key_name).code);
+    EXPECT_EQ(static_cast<const TSIGKey*>(NULL), keyring.find(key_name).key);
+
+    EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add(
+                  TSIGKey(key_name, TSIGKey::HMACSHA256_NAME(),
+                          secret, secret_len)));
+    const TSIGKeyRing::FindResult result(keyring.find(key_name));
+    EXPECT_EQ(TSIGKeyRing::SUCCESS, result.code);
+    EXPECT_EQ(key_name, result.key->getKeyName());
+    EXPECT_EQ(TSIGKey::HMACSHA256_NAME(), result.key->getAlgorithmName());
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, secret, secret_len,
+                        result.key->getSecret(),
+                        result.key->getSecretLength());
+}
+
+TEST_F(TSIGKeyRingTest, findFromSome) {
+    // essentially the same test, but search a larger set
+
+    EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add(
+                  TSIGKey(key_name, TSIGKey::HMACSHA256_NAME(),
+                          secret, secret_len)));
+    EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add(
+                  TSIGKey(Name("another.example"), TSIGKey::HMACMD5_NAME(),
+                          secret, secret_len)));
+    EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add(
+                  TSIGKey(Name("more.example"), TSIGKey::HMACSHA1_NAME(),
+                          secret, secret_len)));
+
+    const TSIGKeyRing::FindResult result(
+        keyring.find(Name("another.example")));
+    EXPECT_EQ(TSIGKeyRing::SUCCESS, result.code);
+    EXPECT_EQ(Name("another.example"), result.key->getKeyName());
+    EXPECT_EQ(TSIGKey::HMACMD5_NAME(), result.key->getAlgorithmName());
+
+    EXPECT_EQ(TSIGKeyRing::NOTFOUND,
+              keyring.find(Name("noexist.example")).code);
+    EXPECT_EQ(static_cast<const TSIGKey*>(NULL),
+              keyring.find(Name("noexist.example")).key);
+}
+
+} // end namespace

+ 0 - 33
src/lib/dns/tsig.cc

@@ -1,33 +0,0 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-// $Id$
-
-#include <cctype>
-#include <cassert>
-#include <iterator>
-#include <functional>
-
-#include <algorithm>
-
-#include <dns/tsig.h>
-
-using namespace std;
-using isc::dns::MessageRenderer;
-
-namespace isc {
-namespace dns {
-
-} // namespace dns
-} // namespace isc

+ 0 - 72
src/lib/dns/tsig.h

@@ -1,72 +0,0 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-// $Id$
-
-#ifndef __TSIG_H
-#define __TSIG_H 1
-
-#include <string>
-#include <vector>
-
-#include <exceptions/exceptions.h>
-
-#include <dns/name.h>
-#include <dns/message.h>
-
-namespace isc {
-namespace dns {
-
-class BadTsigKey : public Exception {
-public:
-    BadTsigKey(const char* file, size_t line, const char* what) :
-        isc::Exception(file, line, what) {}
-};
-
-//
-// This class holds a Tsig key, including all its attributes.
-//
-class Tsig {
-public:
-    enum TsigAlgorithm {
-        HMACMD5 = 0,
-        GSS = 1,
-        HMACSHA1 = 2,
-        HMACSHA224 = 3,
-        HMACSHA265 = 4,
-        HMACSHA384 = 5,
-        HMACSHA512 = 6,
-    };
-
-    Tsig(const Name& name, TsigAlgorithm algorithm,
-         const std::string& algorithm_data) :
-         name_(name), algorithm_(algorithm), algorithm_data_(algorithm_data) {};
-
-    bool signMessage(const Message& message);
-    bool verifyMessage(const Message &message);
-
-private:
-    Name name_;
-    TsigAlgorithm algorithm_;
-    std::string algorithm_data_;
-};
-
-}
-}
-
-#endif  // __TSIG_H
-
-// Local Variables: 
-// mode: c++
-// End: 

+ 167 - 0
src/lib/dns/tsigkey.cc

@@ -0,0 +1,167 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <map>
+#include <utility>
+#include <vector>
+
+#include <exceptions/exceptions.h>
+
+#include <dns/name.h>
+#include <dns/tsigkey.h>
+
+using namespace std;
+
+namespace isc {
+namespace dns {
+struct
+TSIGKey::TSIGKeyImpl {
+    TSIGKeyImpl(const Name& key_name, const Name& algorithm_name,
+                const void* secret, size_t secret_len) :
+        key_name_(key_name), algorithm_name_(algorithm_name),
+        secret_(static_cast<const uint8_t*>(secret),
+                static_cast<const uint8_t*>(secret) + secret_len)
+    {
+        // Convert the name to the canonical form.
+        algorithm_name_.downcase();
+    }
+    const Name key_name_;
+    Name algorithm_name_;
+    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 (algorithm_name != HMACMD5_NAME() &&
+        algorithm_name != HMACSHA1_NAME() &&
+        algorithm_name != HMACSHA256_NAME()) {
+        isc_throw(InvalidParameter, "Unknown TSIG algorithm is specified: " <<
+                  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);
+}
+
+TSIGKey::TSIGKey(const TSIGKey& source) : impl_(new TSIGKeyImpl(*source.impl_))
+{}
+
+TSIGKey&
+TSIGKey::operator=(const TSIGKey& source) {
+    if (impl_ == source.impl_) {
+        return (*this);
+    }
+
+    TSIGKeyImpl* newimpl = new TSIGKeyImpl(*source.impl_);
+    delete impl_;
+    impl_ = newimpl;
+
+    return (*this);
+}
+
+TSIGKey::~TSIGKey() {
+    delete impl_;
+}
+
+const Name&
+TSIGKey::getKeyName() const {
+    return (impl_->key_name_);
+}
+
+const Name&
+TSIGKey::getAlgorithmName() const {
+    return (impl_->algorithm_name_);
+}
+
+const void*
+TSIGKey::getSecret() const {
+    return ((impl_->secret_.size() > 0) ? &impl_->secret_[0] : NULL);
+}
+
+size_t
+TSIGKey::getSecretLength() const {
+    return (impl_->secret_.size());
+}
+
+const
+Name& TSIGKey::HMACMD5_NAME() {
+    static Name alg_name("hmac-md5.sig-alg.reg.int");
+    return (alg_name);
+}
+
+const
+Name& TSIGKey::HMACSHA1_NAME() {
+    static Name alg_name("hmac-sha1");
+    return (alg_name);
+}
+
+const
+Name& TSIGKey::HMACSHA256_NAME() {
+    static Name alg_name("hmac-sha256");
+    return (alg_name);
+}
+
+struct TSIGKeyRing::TSIGKeyRingImpl {
+    typedef map<Name, TSIGKey> TSIGKeyMap;
+    typedef pair<Name, TSIGKey> NameAndKey;
+    TSIGKeyMap keys;
+};
+
+TSIGKeyRing::TSIGKeyRing() : impl_(new TSIGKeyRingImpl) {
+}
+
+TSIGKeyRing::~TSIGKeyRing() {
+    delete impl_;
+}
+
+unsigned int
+TSIGKeyRing::size() const {
+    return (impl_->keys.size());
+}
+
+TSIGKeyRing::Result
+TSIGKeyRing::add(const TSIGKey& key) {
+    if (impl_->keys.insert(
+                TSIGKeyRingImpl::NameAndKey(key.getKeyName(), key)).second
+        == true) {
+        return (SUCCESS);
+    } else {
+        return (EXIST);
+    }
+}
+
+TSIGKeyRing::Result
+TSIGKeyRing::remove(const Name& key_name) {
+    return (impl_->keys.erase(key_name) == 1 ? SUCCESS : NOTFOUND);
+}
+
+TSIGKeyRing::FindResult
+TSIGKeyRing::find(const Name& key_name) {
+    TSIGKeyRingImpl::TSIGKeyMap::const_iterator found =
+        impl_->keys.find(key_name);
+    if (found == impl_->keys.end()) {
+        return (FindResult(NOTFOUND, NULL));
+    }
+    return (FindResult(SUCCESS, &((*found).second)));
+}
+
+} // namespace dns
+} // namespace isc

+ 300 - 0
src/lib/dns/tsigkey.h

@@ -0,0 +1,300 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#ifndef __TSIGKEY_H
+#define __TSIGKEY_H 1
+
+namespace isc {
+namespace dns {
+
+class Name;
+
+/// \brief TSIG key.
+///
+/// This class holds a TSIG key along with some related attributes as
+/// defined in RFC2845.
+///
+/// A TSIG key consists of the following attributes:
+/// - Key name
+/// - Hash algorithm
+/// - Shared secret
+///
+/// <b>Implementation Notes</b>
+///
+/// We may add more attributes in future versions.  For example, if and when
+/// we support the TKEY protocol (RFC2930), we may need to introduce the
+/// notion of inception and expiration times.
+/// At that point we may also have to introduce a class hierarchy to handle
+/// different types of keys in a polymorphic way.
+/// At the moment we use the straightforward value-type class with minimal
+/// attributes.
+///
+/// In the TSIG protocol, hash algorithms are represented in the form of
+/// domain name.
+/// Our interfaces provide direct translation of this concept; for example,
+/// the constructor from parameters take a \c Name object to specify the
+/// algorithm.
+/// On one hand, this may be counter intuitive.
+/// An API user would rather specify "hmac-md5" instead of
+/// <code>Name("hmac-md5.sig-alg.reg.int")</code>.
+/// On the other hand, it may be more convenient for some kind of applications
+/// if we maintain the algorithm as the expected representation for
+/// protocol operations (such as sign and very a message).
+/// Considering these points, we adopt the interface closer to the protocol
+/// specification for now.
+/// To minimize the burden for API users, we also define a set of constants
+/// for commonly used algorithm names so that the users don't have to
+/// remember the actual domain names defined in the protocol specification.
+/// We may also have to add conversion routines between domain names
+/// and more intuitive representations (e.g. strings) for algorithms.
+class TSIGKey {
+public:
+    ///
+    /// \name Constructors, Assignment Operator and Destructor.
+    ///
+    //@{
+    /// \brief Constructor from key parameters
+    ///
+    /// In the current implementation, \c algorithm_name must be a known
+    /// algorithm to this implementation, which are defined via the
+    /// <code>static const</code> member functions.  For other names
+    /// an exception of class \c InvalidParameter will be thrown.
+    /// Note: This restriction may be too strict, and we may revisit it
+    /// later.
+    ///
+    /// \c secret and \c secret_len must be consistent in that the latter
+    /// is 0 if and only if the former is \c NULL;
+    /// otherwise an exception of type \c InvalidParameter will be thrown.
+    ///
+    /// This constructor internally involves resource allocation, and if
+    /// it fails, a corresponding standard exception will be thrown.
+    ///
+    /// \param key_name The name of the key as a domain name.
+    /// \param algorithm_name The hash algorithm used for this key in the
+    /// form of domain name.  For example, it can be
+    /// \c TSIGKey::HMACSHA256_NAME() for HMAC-SHA256.
+    /// \param secret Point to a binary sequence of the shared secret to be
+    /// used for this key, or \c NULL if the secret is empty.
+    /// \param secret_len The size of the binary %data (\c secret) in bytes.
+    TSIGKey(const Name& key_name, const Name& algorithm_name,
+            const void* secret, size_t secret_len);
+
+    /// \brief The copy constructor.
+    ///
+    /// It internally allocates a resource, and if it fails a corresponding
+    /// standard exception will be thrown.
+    /// This constructor never throws an exception otherwise.
+    TSIGKey(const TSIGKey& source);
+
+    /// \brief Assignment operator.
+    ///
+    /// It internally allocates a resource, and if it fails a corresponding
+    /// standard exception will be thrown.
+    /// This operator never throws an exception otherwise.
+    ///
+    /// This operator provides the strong exception guarantee: When an
+    /// exception is thrown the content of the assignment target will be
+    /// intact.
+    TSIGKey& operator=(const TSIGKey& source);
+
+    /// The destructor.
+    ~TSIGKey();
+    //@}
+
+    ///
+    /// \name Getter Methods
+    ///
+    /// These methods never throw an exception.
+    //@{
+    /// Return the key name.
+    const Name& getKeyName() const;
+
+    /// Return the algorithm name.
+    const Name& getAlgorithmName() const;
+
+    /// Return the length of the TSIG secret in bytes.
+    size_t getSecretLength() const;
+
+    /// Return the value of the TSIG secret.
+    ///
+    /// If it returns a non NULL pointer, the memory region beginning at the
+    /// address returned by this method is valid up to the bytes specified
+    /// by the return value of \c getSecretLength().
+    ///
+    /// The memory region is only valid while the corresponding \c TSIGKey
+    /// object is valid.  The caller must hold the \c TSIGKey object while
+    /// it needs to refer to the region or it must make a local copy of the
+    /// region.
+    const void* getSecret() const;
+    //@}
+
+    ///
+    /// \name Well known algorithm names as defined in RFC2845 and RFC4635.
+    ///
+    /// Note: we begin with the "mandatory" algorithms defined in RFC4635
+    /// as a minimal initial set.
+    /// We'll add others as we see the need for them.
+    //@{
+    static const Name& HMACMD5_NAME();    ///< HMAC-MD5 (RFC2845)
+    static const Name& HMACSHA1_NAME();   ///< HMAC-SHA1 (RFC4635)
+    static const Name& HMACSHA256_NAME(); ///< HMAC-SHA256 (RFC4635)
+    //@}
+
+private:
+    struct TSIGKeyImpl;
+    const TSIGKeyImpl* impl_;
+};
+
+/// \brief A simple repository of a set of \c TSIGKey objects.
+///
+/// This is a "key ring" to maintain TSIG keys (\c TSIGKey objects) and
+/// provides trivial operations such as add, remove, and find.
+///
+/// The keys are identified by their key names.
+/// So, for example, two or more keys of the same key name but of different
+/// algorithms are considered to be the same, and cannot be stored in the
+/// key ring at the same time.
+///
+/// <b>Implementation Note:</b>
+/// For simplicity the initial implementation requests the application make
+/// a copy of keys stored in the key ring if it needs to use the keys for
+/// a long period (during which some of the keys may be removed).
+/// This is based on the observations that a single server will not hold
+/// a huge number of keys nor use keys in many different contexts (such as
+/// in different DNS transactions).
+/// If this assumption does not hold and memory consumption becomes an issue
+/// we may have to revisit the design.
+class TSIGKeyRing {
+public:
+    /// Result codes of various public methods of \c TSIGKeyRing
+    enum Result {
+        SUCCESS = 0,    ///< The operation is successful.
+        EXIST = 1,      ///< A key is already stored in \c TSIGKeyRing.
+        NOTFOUND = 2    ///< The specified key is not found in \c TSIGKeyRing.
+    };
+
+    /// \brief A helper structure to represent the search result of
+    /// <code>TSIGKeyRing::find()</code>.
+    ///
+    /// This is a straightforward pair of the result code and a pointer
+    /// to the found key to represent the result of \c find().
+    /// We use this in order to avoid overloading the return value for both
+    /// the result code ("success" or "not found") and the found object,
+    /// i.e., avoid using \c NULL to mean "not found", etc.
+    ///
+    /// This is a simple value class with no internal state, so for
+    /// convenience we allow the applications to refer to the members
+    /// directly.
+    ///
+    /// See the description of \c find() for the semantics of the member
+    /// variables.
+    struct FindResult {
+        FindResult(Result param_code, const TSIGKey* param_key) :
+            code(param_code), key(param_key)
+        {}
+        const Result code;
+        const TSIGKey* const key;
+    };
+
+    ///
+    /// \name Constructors and Destructor.
+    ///
+    /// \b Note:
+    /// The copy constructor and the assignment operator are
+    /// intentionally defined as private, making this class non copyable.
+    /// There is no technical reason why this class cannot be copied,
+    /// but since the key ring can potentially have a large number of keys,
+    /// a naive copy operation may cause unexpected overhead.
+    /// It's generally expected for an application to share the same
+    /// instance of key ring and share it throughout the program via
+    /// references, so we prevent the copy operation explicitly to avoid
+    /// unexpected copy operations.
+    //@{
+private:
+    TSIGKeyRing(const TSIGKeyRing& source);
+    TSIGKeyRing& operator=(const TSIGKeyRing& source);
+public:
+    /// \brief The default constructor.
+    ///
+    /// This constructor never throws an exception.
+    TSIGKeyRing();
+
+    /// The destructor.
+    ~TSIGKeyRing();
+    //@}
+
+    /// Return the number of keys stored in the \c TSIGKeyRing.
+    ///
+    /// This method never throws an exception.
+    unsigned int size() const;
+
+    /// Add a \c TSIGKey to the \c TSIGKeyRing.
+    ///
+    /// This method will create a local copy of the given key, so the caller
+    /// does not have to keep owning it.
+    ///
+    /// If internal resource allocation fails, a corresponding standard
+    /// exception will be thrown.
+    /// This method never throws an exception otherwise.
+    ///
+    /// \param key A \c TSIGKey to be added.
+    /// \return \c SUCCESS If the key is successfully added to the key ring.
+    /// \return \c EXIST The key ring already stores a key whose name is
+    /// identical to that of \c key.
+    Result add(const TSIGKey& key);
+
+    /// Remove a \c TSIGKey for the given name from the \c TSIGKeyRing.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \param key_name The name of the key to be removed.
+    /// \return \c SUCCESS If the key is successfully removed from the key
+    /// ring.
+    /// \return \c NOTFOUND The key ring does not store the key that matches
+    /// \c key_name.
+    Result remove(const Name& key_name);
+
+    /// Find a \c TSIGKey for the given name in the \c TSIGKeyRing.
+    ///
+    /// It searches the internal storage for a \c TSIGKey whose name is
+    /// \c key_name, and returns the result in the form of a \c FindResult
+    /// object as follows:
+    /// - \c code: \c SUCCESS if a key is found; otherwise \c NOTFOUND.
+    /// - \c key: A pointer to the found \c TSIGKey object if one is found;
+    /// otherwise \c NULL.
+    ///
+    /// The pointer returned in the \c FindResult object is only valid until
+    /// the corresponding key is removed from the key ring.
+    /// The caller must ensure that the key is held in the key ring while
+    /// it needs to refer to it, or it must make a local copy of the key.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \param key_name The name of the key to be found.
+    /// \return A \c FindResult object enclosing the search result (see above).
+    FindResult find(const Name& key_name);
+private:
+    struct TSIGKeyRingImpl;
+    TSIGKeyRingImpl* impl_;
+};
+}
+}
+
+#endif  // __TSIGKEY_H
+
+// Local Variables:
+// mode: c++
+// End: