Commit 3bb68553 authored by JINMEI Tatuya's avatar JINMEI Tatuya
Browse files

[trac915] added new support for the rest of TSIG related implementations

and detailed tests.
parent ade0b1a8
......@@ -5,9 +5,15 @@ AM_CPPFLAGS += $(BOOST_INCLUDES)
AM_CXXFLAGS = $(B10_CXXFLAGS)
pyexec_LTLIBRARIES = pydnspp.la
pydnspp_la_SOURCES = pydnspp.cc pydnspp_common.cc
pydnspp_la_SOURCES = pydnspp.cc pydnspp_common.cc pydnspp_towire.h
pydnspp_la_SOURCES += name_python.cc name_python.h
pydnspp_la_SOURCES += messagerenderer_python.cc messagerenderer_python.h
pydnspp_la_SOURCES += rcode_python.cc rcode_python.h
pydnspp_la_SOURCES += tsigkey_python.cc tsigkey_python.h
pydnspp_la_SOURCES += tsigerror_python.cc tsigerror_python.h
pydnspp_la_SOURCES += tsig_rdata_python.cc tsig_rdata_python.h
pydnspp_la_SOURCES += tsigrecord_python.cc tsigrecord_python.h
pydnspp_la_SOURCES += tsig_python.cc tsig_python.h
pydnspp_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES)
pydnspp_la_LDFLAGS = $(PYTHON_LDFLAGS)
......
......@@ -38,6 +38,14 @@
#include <dns/messagerenderer.h>
#include "pydnspp_common.h"
#include "messagerenderer_python.h"
#include "name_python.h"
#include "rcode_python.h"
#include "tsigkey_python.h"
#include "tsig_rdata_python.h"
#include "tsigerror_python.h"
#include "tsigrecord_python.h"
#include "tsig_python.h"
namespace isc {
namespace dns {
......@@ -52,14 +60,9 @@ PyObject* po_DNSMessageBADVERS;
}
}
#include "rcode_python.h"
#include "tsigerror_python.h"
// order is important here!
using namespace isc::dns::python;
#include <dns/python/messagerenderer_python.cc>
#include <dns/python/name_python.cc> // needs Messagerenderer
#include <dns/python/rrclass_python.cc> // needs Messagerenderer
#include <dns/python/rrtype_python.cc> // needs Messagerenderer
#include <dns/python/rrttl_python.cc> // needs Messagerenderer
......@@ -67,8 +70,6 @@ using namespace isc::dns::python;
#include <dns/python/rrset_python.cc> // needs Rdata, RRTTL
#include <dns/python/question_python.cc> // needs RRClass, RRType, RRTTL,
// Name
#include <dns/python/tsigkey_python.cc> // needs Name
#include <dns/python/tsig_python.cc> // needs tsigkey
#include <dns/python/opcode_python.cc>
#include <dns/python/edns_python.cc> // needs Messagerenderer, Rcode
#include <dns/python/message_python.cc> // needs RRset, Question
......@@ -167,14 +168,21 @@ PyInit_pydnspp(void) {
return (NULL);
}
if (!initModulePart_TSIG(mod)) {
return (NULL);
}
if (!initModulePart_TSIGError(mod)) {
return (NULL);
}
if (!initModulePart_TSIGRecord(mod)) {
return (NULL);
}
if (!initModulePart_TSIGContext(mod)) {
return (NULL);
}
return (mod);
}
......@@ -12,8 +12,10 @@ PYTESTS += rrset_python_test.py
PYTESTS += rrttl_python_test.py
PYTESTS += rrtype_python_test.py
PYTESTS += tsig_python_test.py
PYTESTS += tsig_rdata_python_test.py
PYTESTS += tsigerror_python_test.py
PYTESTS += tsigkey_python_test.py
PYTESTS += tsigrecord_python_test.py
EXTRA_DIST = $(PYTESTS)
EXTRA_DIST += testutil.py
......@@ -34,7 +36,7 @@ if ENABLE_PYTHON_COVERAGE
endif
for pytest in $(PYTESTS) ; do \
echo Running test: $$pytest ; \
env PYTHONPATH=$(abs_top_srcdir)/src/lib/dns/.libs:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/dns/python/.libs \
env PYTHONPATH=$(abs_top_builddir)/src/lib/util/pyunittests/.libs:$(abs_top_srcdir)/src/lib/dns/.libs:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/dns/python/.libs \
TESTDATA_PATH=$(abs_top_srcdir)/src/lib/dns/tests/testdata:$(abs_top_builddir)/src/lib/dns/tests/testdata \
$(LIBRARY_PATH_PLACEHOLDER) \
$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
......
......@@ -422,7 +422,54 @@ test.example.com. 3600 IN A 192.0.2.2
factoryFromFile,
message_parse,
"message_fromWire9")
def test_from_wire_with_tsig(self):
# Initially there should be no TSIG
self.assertEqual(None, self.p.get_tsig_record())
# getTSIGRecord() is only valid in the parse mode.
self.assertRaises(InvalidMessageOperation, self.r.get_tsig_record)
factoryFromFile(self.p, "message_toWire2.wire")
tsig_rr = self.p.get_tsig_record()
self.assertEqual(Name("www.example.com"), tsig_rr.get_name())
self.assertEqual(85, tsig_rr.get_length())
self.assertEqual(TSIGKey.HMACMD5_NAME,
tsig_rr.get_rdata().get_algorithm())
# If we clear the message for reuse, the recorded TSIG will be cleared.
self.p.clear(Message.PARSE)
self.assertEqual(None, self.p.get_tsig_record())
def test_from_wire_with_tsigcompressed(self):
# Mostly same as fromWireWithTSIG, but the TSIG owner name is
# compressed.
factoryFromFile(self.p, "message_fromWire12.wire");
tsig_rr = self.p.get_tsig_record()
self.assertEqual(Name("www.example.com"), tsig_rr.get_name())
# len(www.example.com) = 17, but when fully compressed, the length is
# 2 bytes. So the length of the record should be 15 bytes shorter.
self.assertEqual(70, tsig_rr.get_length())
def test_from_wire_with_badtsig(self):
# Multiple TSIG RRs
self.assertRaises(DNSMessageFORMERR, factoryFromFile,
self.p, "message_fromWire13.wire")
self.p.clear(Message.PARSE)
# TSIG in the answer section (must be in additional)
self.assertRaises(DNSMessageFORMERR, factoryFromFile,
self.p, "message_fromWire14.wire")
self.p.clear(Message.PARSE)
# TSIG is not the last record.
self.assertRaises(DNSMessageFORMERR, factoryFromFile,
self.p, "message_fromWire15.wire")
self.p.clear(Message.PARSE)
# Unexpected RR Class (this will fail in constructing TSIGRecord)
self.assertRaises(DNSMessageFORMERR, factoryFromFile,
self.p, "message_fromWire16.wire")
if __name__ == '__main__':
unittest.main()
# Copyright (C) 2011 Internet Systems Consortium.
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import unittest
import sys
from pydnspp import *
class TSIGRdataTest(unittest.TestCase):
VALID_TEXT1 = "hmac-md5.sig-alg.reg.int. 1286779327 300 0 16020 BADKEY 0"
def test_from_string(self):
tsig = TSIG(self.VALID_TEXT1)
self.assertEqual(Name("hmac-md5.sig-alg.reg.int"),
tsig.get_algorithm())
# check there's no leak in creating the name object:
self.assertEqual(1, sys.getrefcount(tsig.get_algorithm()))
if __name__ == '__main__':
unittest.main()
# Copyright (C) 2011 Internet Systems Consortium.
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import unittest
import sys
from pydnspp import *
class TSIGRecordTest(unittest.TestCase):
def setUp(self):
self.test_name = Name("www.example.com")
self.test_rdata = TSIG("hmac-md5.sig-alg.reg.int. 1302890362 " + \
"300 16 2tra2tra2tra2tra2tra2g== " + \
"11621 0 0")
self.test_record = TSIGRecord(self.test_name, self.test_rdata)
def test_getname(self):
self.assertEqual(self.test_name, self.test_record.get_name())
self.assertEqual(1, sys.getrefcount(self.test_record.get_name()))
def test_get_length(self):
# see the C++ test for the magic number
self.assertEqual(85, self.test_record.get_length())
def test_to_text(self):
expected_text = "www.example.com. 0 ANY TSIG " + \
"hmac-md5.sig-alg.reg.int. 1302890362 300 16 " + \
"2tra2tra2tra2tra2tra2g== 11621 NOERROR 0\n"
self.assertEqual(expected_text, self.test_record.to_text())
self.assertEqual(expected_text, str(self.test_record))
if __name__ == '__main__':
unittest.main()
......@@ -12,9 +12,30 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#define PY_SSIZE_T_CLEAN // need for "y#" below
#include <Python.h>
#include <string>
#include <stdexcept>
#include <exceptions/exceptions.h>
#include <util/python/pycppwrapper_util.h>
#include <dns/tsig.h>
#include "pydnspp_common.h"
#include "name_python.h"
#include "tsigkey_python.h"
#include "tsigerror_python.h"
#include "tsigrecord_python.h"
#include "tsig_python.h"
using namespace std;
using namespace isc;
using namespace isc::util::python;
using namespace isc::dns;
using namespace isc::dns::python;
//
// Definition of the classes
......@@ -24,12 +45,17 @@ using namespace isc::dns;
// and static wrappers around the methods we export), a list of methods,
// and a type description
//
// TSIGContext
//
// Trivial constructor.
s_TSIGContext::s_TSIGContext() : cppobj(NULL) {
}
namespace {
// The s_* Class simply covers one instantiation of the object
class s_TSIGContext : public PyObject {
public:
TSIGContext* tsig_ctx;
};
// Shortcut type which would be convenient for adding class variables safely.
typedef CPPPyObjectContainer<s_TSIGContext, TSIGContext> TSIGContextContainer;
//
// We declare the functions here, the definitions are below
......@@ -40,6 +66,12 @@ public:
int TSIGContext_init(s_TSIGContext* self, PyObject* args);
void TSIGContext_destroy(s_TSIGContext* self);
// Class specific methods
PyObject* TSIGContext_getState(s_TSIGContext* self);
PyObject* TSIGContext_getError(s_TSIGContext* self);
PyObject* TSIGContext_sign(s_TSIGContext* self, PyObject* args);
PyObject* TSIGContext_verify(s_TSIGContext* self, PyObject* args);
// These are the functions we export
// For a minimal support, we don't need them.
......@@ -50,18 +82,180 @@ void TSIGContext_destroy(s_TSIGContext* self);
// 3. Argument type
// 4. Documentation
PyMethodDef TSIGContext_methods[] = {
{ "get_state", reinterpret_cast<PyCFunction>(TSIGContext_getState),
METH_NOARGS,
"Return the current state of the context (mainly for tests)" },
{ "get_error", reinterpret_cast<PyCFunction>(TSIGContext_getError),
METH_NOARGS,
"Return the TSIG error as a result of the latest verification" },
{ "sign",
reinterpret_cast<PyCFunction>(TSIGContext_sign), METH_VARARGS,
"Sign a DNS message." },
{ "verify",
reinterpret_cast<PyCFunction>(TSIGContext_verify), METH_VARARGS,
"Verify a DNS message." },
{ NULL, NULL, 0, NULL }
};
int
TSIGContext_init(s_TSIGContext* self, PyObject* args) {
try {
// "From key" constructor
const s_TSIGKey* tsigkey_obj;
if (PyArg_ParseTuple(args, "O!", &tsigkey_type, &tsigkey_obj)) {
self->cppobj = new TSIGContext(*tsigkey_obj->cppobj);
return (0);
}
// "From key param + keyring" constructor
PyErr_Clear();
const s_Name* keyname_obj;
const s_Name* algname_obj;
const s_TSIGKeyRing* keyring_obj;
if (PyArg_ParseTuple(args, "O!O!O!", &name_type, &keyname_obj,
&name_type, &algname_obj, &tsigkeyring_type,
&keyring_obj)) {
self->cppobj = new TSIGContext(*keyname_obj->cppobj,
*algname_obj->cppobj,
*keyring_obj->cppobj);
return (0);
}
} catch (const exception& ex) {
const string ex_what = "Failed to construct TSIGContext object: " +
string(ex.what());
PyErr_SetString(po_IscException, ex_what.c_str());
return (-1);
} catch (...) {
PyErr_SetString(po_IscException,
"Unexpected exception in constructing TSIGContext");
return (-1);
}
PyErr_SetString(PyExc_TypeError,
"Invalid arguments to TSIGContext constructor");
return (-1);
}
void
TSIGContext_destroy(s_TSIGContext* const self) {
delete self->cppobj;
self->cppobj = NULL;
Py_TYPE(self)->tp_free(self);
}
PyObject*
TSIGContext_getState(s_TSIGContext* self) {
return (Py_BuildValue("I", self->cppobj->getState()));
}
PyObject*
TSIGContext_getError(s_TSIGContext* self) {
try {
PyObjectContainer container(createTSIGErrorObject(
self->cppobj->getError()));
return (Py_BuildValue("O", container.get()));
} catch (const exception& ex) {
const string ex_what =
"Unexpectedly failed to get TSIGContext error: " +
string(ex.what());
PyErr_SetString(po_IscException, ex_what.c_str());
} catch (...) {
PyErr_SetString(po_IscException,
"Unexpected exception in TSIGContext.get_error");
}
return (NULL);
}
PyObject*
TSIGContext_sign(s_TSIGContext* self, PyObject* args) {
long qid = 0;
const char* mac;
Py_ssize_t mac_size;
if (PyArg_ParseTuple(args, "ly#", &qid, &mac, &mac_size)) {
if (qid < 0 || qid > 0xffff) {
PyErr_SetString(PyExc_ValueError,
"TSIGContext.sign: QID out of range");
return (NULL);
}
try {
ConstTSIGRecordPtr record = self->cppobj->sign(qid, mac, mac_size);
return (createTSIGRecordObject(*record));
} catch (const TSIGContextError& ex) {
PyErr_SetString(po_TSIGContextError, ex.what());
} catch (const exception& ex) {
const string ex_what = "Unexpected failure in TSIG sign: " +
string(ex.what());
PyErr_SetString(po_IscException, ex_what.c_str());
} catch (...) {
PyErr_SetString(PyExc_SystemError,
"Unexpected failure in TSIG sign");
}
} else {
PyErr_SetString(PyExc_TypeError,
"Invalid arguments to TSIGContext.sign");
}
return (NULL);
}
PyObject*
TSIGContext_verify(s_TSIGContext* self, PyObject* args) {
const char* data;
Py_ssize_t data_len;
s_TSIGRecord* py_record;
PyObject* py_maybe_none;
TSIGRecord* record;
if (PyArg_ParseTuple(args, "O!y#", &tsigrecord_type, &py_record,
&data, &data_len)) {
record = py_record->cppobj;
} else if (PyArg_ParseTuple(args, "Oy#", &py_maybe_none, &data,
&data_len)) {
record = NULL;
} else {
PyErr_SetString(PyExc_TypeError,
"Invalid arguments to TSIGContext.verify");
return (NULL);
}
PyErr_Clear();
try {
const TSIGError error = self->cppobj->verify(record, data, data_len);
return (createTSIGErrorObject(error));
} catch (const TSIGContextError& ex) {
PyErr_SetString(po_TSIGContextError, ex.what());
} catch (const InvalidParameter& ex) {
PyErr_SetString(po_InvalidParameter, ex.what());
} catch (const exception& ex) {
const string ex_what = "Unexpected failure in TSIG verify: " +
string(ex.what());
PyErr_SetString(po_IscException, ex_what.c_str());
} catch (...) {
PyErr_SetString(PyExc_SystemError, "Unexpected failure in TSIG verify");
}
return (NULL);
}
} // end of unnamed namespace
namespace isc {
namespace dns {
namespace python {
// Definition of class specific exception(s)
PyObject* po_TSIGContextError;
// This defines the complete type for reflection in python and
// parsing of PyObject* to s_EDNS
// parsing of PyObject* to s_TSIGContext
// Most of the functions are not actually implemented and NULL here.
PyTypeObject tsig_context_type = {
PyTypeObject tsigcontext_type = {
PyVarObject_HEAD_INIT(NULL, 0)
"libdns_python.TSIGContext",
sizeof(s_TSIGContext), // tp_basicsize
sizeof(s_TSIGContext), // tp_basicsize
0, // tp_itemsize
(destructor)TSIGContext_destroy, // tp_dealloc
reinterpret_cast<destructor>(TSIGContext_destroy), // tp_dealloc
NULL, // tp_print
NULL, // tp_getattr
NULL, // tp_setattr
......@@ -77,15 +271,14 @@ PyTypeObject tsig_context_type = {
NULL, // tp_setattro
NULL, // tp_as_buffer
Py_TPFLAGS_DEFAULT, // tp_flags
"The TSIGContext class maintains a context of a signed session of "
"DNS transactions by TSIG.",
"The TSIGContext class objects is...(COMPLETE THIS)",
NULL, // tp_traverse
NULL, // tp_clear
NULL, // tp_richcompare
NULL, // tp_richcompare
0, // tp_weaklistoffset
NULL, // tp_iter
NULL, // tp_iternext
TSIGContext_methods, // tp_methods
TSIGContext_methods, // tp_methods
NULL, // tp_members
NULL, // tp_getset
NULL, // tp_base
......@@ -93,7 +286,7 @@ PyTypeObject tsig_context_type = {
NULL, // tp_descr_get
NULL, // tp_descr_set
0, // tp_dictoffset
(initproc)TSIGContext_init, // tp_init
reinterpret_cast<initproc>(TSIGContext_init), // tp_init
NULL, // tp_alloc
PyType_GenericNew, // tp_new
NULL, // tp_free
......@@ -107,50 +300,58 @@ PyTypeObject tsig_context_type = {
0 // tp_version_tag
};
int
TSIGContext_init(s_TSIGContext* self, PyObject* args) {
const s_TSIGKey* tsigkey_obj;
try {
if (PyArg_ParseTuple(args, "O!", &tsigkey_type, &tsigkey_obj)) {
self->tsig_ctx = new TSIGContext(*tsigkey_obj->tsigkey);
return (0);
}
} catch (...) {
PyErr_SetString(po_IscException, "Unexpected exception");
return (-1);
}
PyErr_Clear();
PyErr_SetString(PyExc_TypeError,
"Invalid arguments to TSIGContext constructor");
return (-1);
}
void
TSIGContext_destroy(s_TSIGContext* const self) {
delete self->tsig_ctx;
self->tsig_ctx = NULL;
Py_TYPE(self)->tp_free(self);
}
// Module Initialization, all statics are initialized here
bool
initModulePart_TSIGContext(PyObject* mod) {
// We initialize the static description object with PyType_Ready(),
// then add it to the module. This is not just a check! (leaving
// this out results in segmentation faults)
if (PyType_Ready(&tsig_context_type) < 0) {
if (PyType_Ready(&tsigcontext_type) < 0) {
return (false);
}
void* p = &tsigcontext_type;
if (PyModule_AddObject(mod, "TSIGContext",
static_cast<PyObject*>(p)) < 0) {
return (false);
}
Py_INCREF(&tsig_context_type);
void* p = &tsig_context_type;
PyModule_AddObject(mod, "TSIGContext", static_cast<PyObject*>(p));
Py_INCREF(&tsigcontext_type);
try {
// Class specific exceptions
po_TSIGContextError = PyErr_NewException("pydnspp.TSIGContextError",
po_IscException, NULL);
PyObjectContainer(po_TSIGContextError).installToModule(
mod, "TSIGContextError");
addClassVariable(tsig_context_type, "DEFAULT_FUDGE",
Py_BuildValue("H", TSIGContext::DEFAULT_FUDGE));
// Constant class variables
installClassVariable(tsigcontext_type, "STATE_INIT",
Py_BuildValue("I", TSIGContext::INIT));
installClassVariable(tsigcontext_type, "STATE_SENT_REQUEST",
Py_BuildValue("I", TSIGContext::SENT_REQUEST));
installClassVariable(tsigcontext_type, "STATE_RECEIVED_REQUEST",
Py_BuildValue("I", TSIGContext::RECEIVED_REQUEST));
installClassVariable(tsigcontext_type, "STATE_SENT_RESPONSE",
Py_BuildValue("I", TSIGContext::SENT_RESPONSE));
installClassVariable(tsigcontext_type, "STATE_VERIFIED_RESPONSE",
Py_BuildValue("I",
TSIGContext::VERIFIED_RESPONSE));
installClassVariable(tsigcontext_type, "DEFAULT_FUDGE",
Py_BuildValue("H", TSIGContext::DEFAULT_FUDGE));
} catch (const exception& ex) {
const string ex_what =
"Unexpected failure in TSIGContext initialization: " +
string(ex.what());
PyErr_SetString(po_IscException, ex_what.c_str());
return (false);
} catch (...) {
PyErr_SetString(PyExc_SystemError,
"Unexpected failure in TSIGContext initialization");
return (false);
}
return (true);
}
} // end of anonymous namespace
} // namespace python
} // namespace dns
} // namespace isc
// 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