Commit e08e6404 authored by JINMEI Tatuya's avatar JINMEI Tatuya
Browse files

Initial implementation of TSIGKey and TSIGKeyRing, including detailed tests,...

Initial implementation of TSIGKey and TSIGKeyRing, including detailed tests, documentation, and python binding.


git-svn-id: svn://bind10.isc.org/svn/bind10/branches/trac381@3278 e5f2f494-b856-4b98-b285-d166d9295462
parent ad41fd35
......@@ -80,7 +80,7 @@ libdns___la_SOURCES += rrttl.h rrttl.cc
libdns___la_SOURCES += rrtype.cc
libdns___la_SOURCES += question.h question.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 += rrparamregistry.cc
......@@ -108,7 +108,7 @@ libdns___include_HEADERS = \
rrsetlist.h \
rrttl.h \
rrtype.h \
tsig.h
tsigkey.h
# Purposely not installing these headers:
# util/*.h: used only internally, and not actually DNS specific
# rrclass-placeholder.h
......
......@@ -23,6 +23,7 @@ EXTRA_DIST += question_python.cc
EXTRA_DIST += rrttl_python.cc
EXTRA_DIST += rdata_python.cc
EXTRA_DIST += rrtype_python.cc
EXTRA_DIST += tsigkey_python.cc
# Python prefers .so, while some OSes (specifically MacOS) use a different
# suffix for dynamic objects. -module is necessary to work this around.
......
......@@ -57,6 +57,7 @@ static PyObject* po_DNSMessageBADVERS;
#include <dns/python/rrset_python.cc> // needs Rdata, RRTTL
#include <dns/python/question_python.cc> // needs RRClass, RRType, RRTTL,
// Name
#include <dns/python/tsigkey_python.cc> // needs Name
#include <dns/python/opcode_python.cc>
#include <dns/python/rcode_python.cc>
#include <dns/python/edns_python.cc> // needs Messagerenderer, Rcode
......@@ -146,6 +147,14 @@ PyInit_pydnspp(void) {
return (NULL);
}
if (!initModulePart_TSIGKey(mod)) {
return (NULL);
}
if (!initModulePart_TSIGKeyRing(mod)) {
return (NULL);
}
return (mod);
}
......@@ -10,6 +10,7 @@ PYTESTS += rrclass_python_test.py
PYTESTS += rrset_python_test.py
PYTESTS += rrttl_python_test.py
PYTESTS += rrtype_python_test.py
PYTESTS += tsigkey_python_test.py
EXTRA_DIST = $(PYTESTS)
EXTRA_DIST += testutil.py
......
# 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()
// 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