Commit 5b64e839 authored by Michal 'vorner' Vaner's avatar Michal 'vorner' Vaner
Browse files

Merge #1407

parents eb06cb8d 4dc636c8
......@@ -16,6 +16,7 @@
#include <Python.h>
#include <dns/rdata.h>
#include <dns/messagerenderer.h>
#include <dns/exceptions.h>
#include <util/buffer.h>
#include <util/python/pycppwrapper_util.h>
......@@ -23,6 +24,7 @@
#include "rrtype_python.h"
#include "rrclass_python.h"
#include "messagerenderer_python.h"
#include "name_python.h"
using namespace isc::dns;
using namespace isc::dns::python;
......@@ -31,6 +33,27 @@ using namespace isc::util::python;
using namespace isc::dns::rdata;
namespace {
typedef PyObject* method(PyObject* self, PyObject* args);
// Wrap a method into an exception handling, converting C++ exceptions
// to python ones. The params and return value is just passed through.
PyObject*
exception_wrap(method* method, PyObject* self, PyObject* args) {
try {
return (method(self, args));
} catch (const std::exception& ex) {
// FIXME: These exceptions are not tested, I don't know how or if
// at all they can be triggered. But they are caught just in the case.
PyErr_SetString(PyExc_Exception, (std::string("Unknown exception: ") +
ex.what()).c_str());
return (NULL);
} catch (...) {
PyErr_SetString(PyExc_Exception, "Unknown exception");
return (NULL);
}
}
class s_Rdata : public PyObject {
public:
isc::dns::rdata::ConstRdataPtr cppobj;
......@@ -44,16 +67,16 @@ typedef CPPPyObjectContainer<s_Rdata, Rdata> RdataContainer;
//
// General creation and destruction
int Rdata_init(s_Rdata* self, PyObject* args);
void Rdata_destroy(s_Rdata* self);
int Rdata_init(PyObject* self, PyObject* args, PyObject*);
void Rdata_destroy(PyObject* self);
// These are the functions we export
PyObject* Rdata_toText(s_Rdata* self);
PyObject* Rdata_toText(PyObject* self, PyObject*);
// This is a second version of toText, we need one where the argument
// is a PyObject*, for the str() function in python.
PyObject* Rdata_str(PyObject* self);
PyObject* Rdata_toWire(s_Rdata* self, PyObject* args);
PyObject* RData_richcmp(s_Rdata* self, s_Rdata* other, int op);
PyObject* Rdata_toWire(PyObject* self, PyObject* args);
PyObject* RData_richcmp(PyObject* self, PyObject* other, int op);
// This list contains the actual set of functions we have in
// python. Each entry has
......@@ -62,9 +85,9 @@ PyObject* RData_richcmp(s_Rdata* self, s_Rdata* other, int op);
// 3. Argument type
// 4. Documentation
PyMethodDef Rdata_methods[] = {
{ "to_text", reinterpret_cast<PyCFunction>(Rdata_toText), METH_NOARGS,
{ "to_text", Rdata_toText, METH_NOARGS,
"Returns the string representation" },
{ "to_wire", reinterpret_cast<PyCFunction>(Rdata_toWire), METH_VARARGS,
{ "to_wire", Rdata_toWire, METH_VARARGS,
"Converts the Rdata object to wire format.\n"
"The argument can be either a MessageRenderer or an object that "
"implements the sequence interface. If the object is mutable "
......@@ -75,58 +98,89 @@ PyMethodDef Rdata_methods[] = {
};
int
Rdata_init(s_Rdata* self, PyObject* args) {
Rdata_init(PyObject* self_p, PyObject* args, PyObject*) {
PyObject* rrtype;
PyObject* rrclass;
const char* s;
const char* data;
Py_ssize_t len;
s_Rdata* self(static_cast<s_Rdata*>(self_p));
// Create from string
if (PyArg_ParseTuple(args, "O!O!s", &rrtype_type, &rrtype,
&rrclass_type, &rrclass,
&s)) {
self->cppobj = createRdata(PyRRType_ToRRType(rrtype),
PyRRClass_ToRRClass(rrclass), s);
return (0);
} else if (PyArg_ParseTuple(args, "O!O!y#", &rrtype_type, &rrtype,
&rrclass_type, &rrclass, &data, &len)) {
InputBuffer input_buffer(data, len);
self->cppobj = createRdata(PyRRType_ToRRType(rrtype),
PyRRClass_ToRRClass(rrclass),
input_buffer, len);
return (0);
try {
// Create from string
if (PyArg_ParseTuple(args, "O!O!s", &rrtype_type, &rrtype,
&rrclass_type, &rrclass,
&s)) {
self->cppobj = createRdata(PyRRType_ToRRType(rrtype),
PyRRClass_ToRRClass(rrclass), s);
return (0);
} else if (PyArg_ParseTuple(args, "O!O!y#", &rrtype_type, &rrtype,
&rrclass_type, &rrclass, &data, &len)) {
InputBuffer input_buffer(data, len);
self->cppobj = createRdata(PyRRType_ToRRType(rrtype),
PyRRClass_ToRRClass(rrclass),
input_buffer, len);
return (0);
}
} catch (const isc::dns::rdata::InvalidRdataText& irdt) {
PyErr_SetString(po_InvalidRdataText, irdt.what());
return (-1);
} catch (const isc::dns::rdata::InvalidRdataLength& irdl) {
PyErr_SetString(po_InvalidRdataLength, irdl.what());
return (-1);
} catch (const isc::dns::rdata::CharStringTooLong& cstl) {
PyErr_SetString(po_CharStringTooLong, cstl.what());
return (-1);
} catch (const isc::dns::DNSMessageFORMERR& dmfe) {
PyErr_SetString(po_DNSMessageFORMERR, dmfe.what());
return (-1);
} catch (const std::exception& ex) {
// FIXME: These exceptions are not tested, I don't know how or if
// at all they can be triggered. But they are caught just in the case.
PyErr_SetString(PyExc_Exception, (std::string("Unknown exception: ") +
ex.what()).c_str());
return (-1);
} catch (...) {
PyErr_SetString(PyExc_Exception, "Unknown exception");
return (-1);
}
return (-1);
}
void
Rdata_destroy(s_Rdata* self) {
Rdata_destroy(PyObject* self) {
// Clear the shared_ptr so that its reference count is zero
// before we call tp_free() (there is no direct release())
self->cppobj.reset();
static_cast<s_Rdata*>(self)->cppobj.reset();
Py_TYPE(self)->tp_free(self);
}
PyObject*
Rdata_toText(s_Rdata* self) {
Rdata_toText_internal(PyObject* self, PyObject*) {
// Py_BuildValue makes python objects from native data
return (Py_BuildValue("s", self->cppobj->toText().c_str()));
return (Py_BuildValue("s", static_cast<const s_Rdata*>(self)->cppobj->
toText().c_str()));
}
PyObject*
Rdata_toText(PyObject* self, PyObject* args) {
return (exception_wrap(&Rdata_toText_internal, self, args));
}
PyObject*
Rdata_str(PyObject* self) {
// Simply call the to_text method we already defined
return (PyObject_CallMethod(self,
const_cast<char*>("to_text"),
const_cast<char*>("to_text"),
const_cast<char*>("")));
}
PyObject*
Rdata_toWire(s_Rdata* self, PyObject* args) {
Rdata_toWire_internal(PyObject* self_p, PyObject* args) {
PyObject* bytes;
PyObject* mr;
const s_Rdata* self(static_cast<const s_Rdata*>(self_p));
if (PyArg_ParseTuple(args, "O", &bytes) && PySequence_Check(bytes)) {
PyObject* bytes_o = bytes;
......@@ -134,6 +188,11 @@ Rdata_toWire(s_Rdata* self, PyObject* args) {
OutputBuffer buffer(4);
self->cppobj->toWire(buffer);
PyObject* rd_bytes = PyBytes_FromStringAndSize(static_cast<const char*>(buffer.getData()), buffer.getLength());
// Make sure exceptions from here are propagated.
// The exception is already set, so we just return NULL
if (rd_bytes == NULL) {
return (NULL);
}
PyObject* result = PySequence_InPlaceConcat(bytes_o, rd_bytes);
// We need to release the object we temporarily created here
// to prevent memory leak
......@@ -152,45 +211,64 @@ Rdata_toWire(s_Rdata* self, PyObject* args) {
}
PyObject*
RData_richcmp(s_Rdata* self, s_Rdata* other, int op) {
bool c;
Rdata_toWire(PyObject* self, PyObject* args) {
return (exception_wrap(&Rdata_toWire_internal, self, args));
}
PyObject*
RData_richcmp(PyObject* self_p, PyObject* other_p, int op) {
try {
bool c;
const s_Rdata* self(static_cast<const s_Rdata*>(self_p)),
* other(static_cast<const s_Rdata*>(other_p));
// Check for null and if the types match. If different type,
// simply return False
if (!other || (self->ob_type != other->ob_type)) {
Py_RETURN_FALSE;
}
// Check for null and if the types match. If different type,
// simply return False
if (!other || (self->ob_type != other->ob_type)) {
Py_RETURN_FALSE;
}
switch (op) {
case Py_LT:
c = self->cppobj->compare(*other->cppobj) < 0;
break;
case Py_LE:
c = self->cppobj->compare(*other->cppobj) < 0 ||
self->cppobj->compare(*other->cppobj) == 0;
break;
case Py_EQ:
c = self->cppobj->compare(*other->cppobj) == 0;
break;
case Py_NE:
c = self->cppobj->compare(*other->cppobj) != 0;
break;
case Py_GT:
c = self->cppobj->compare(*other->cppobj) > 0;
break;
case Py_GE:
c = self->cppobj->compare(*other->cppobj) > 0 ||
self->cppobj->compare(*other->cppobj) == 0;
break;
default:
PyErr_SetString(PyExc_IndexError,
"Unhandled rich comparison operator");
switch (op) {
case Py_LT:
c = self->cppobj->compare(*other->cppobj) < 0;
break;
case Py_LE:
c = self->cppobj->compare(*other->cppobj) < 0 ||
self->cppobj->compare(*other->cppobj) == 0;
break;
case Py_EQ:
c = self->cppobj->compare(*other->cppobj) == 0;
break;
case Py_NE:
c = self->cppobj->compare(*other->cppobj) != 0;
break;
case Py_GT:
c = self->cppobj->compare(*other->cppobj) > 0;
break;
case Py_GE:
c = self->cppobj->compare(*other->cppobj) > 0 ||
self->cppobj->compare(*other->cppobj) == 0;
break;
default:
PyErr_SetString(PyExc_IndexError,
"Unhandled rich comparison operator");
return (NULL);
}
if (c) {
Py_RETURN_TRUE;
} else {
Py_RETURN_FALSE;
}
} catch (const std::exception& ex) {
// FIXME: These exceptions are not tested, I don't know how or if
// at all they can be triggered. But they are caught just in the case.
PyErr_SetString(PyExc_Exception, (std::string("Unknown exception: ") +
ex.what()).c_str());
return (NULL);
} catch (...) {
PyErr_SetString(PyExc_Exception, "Unknown exception");
return (NULL);
}
if (c)
Py_RETURN_TRUE;
else
Py_RETURN_FALSE;
}
} // end of unnamed namespace
......@@ -217,7 +295,7 @@ PyTypeObject rdata_type = {
"pydnspp.Rdata",
sizeof(s_Rdata), // tp_basicsize
0, // tp_itemsize
(destructor)Rdata_destroy, // tp_dealloc
Rdata_destroy, // tp_dealloc
NULL, // tp_print
NULL, // tp_getattr
NULL, // tp_setattr
......@@ -237,7 +315,7 @@ PyTypeObject rdata_type = {
"a set of common interfaces to manipulate concrete RDATA objects.",
NULL, // tp_traverse
NULL, // tp_clear
(richcmpfunc)RData_richcmp, // tp_richcompare
RData_richcmp, // tp_richcompare
0, // tp_weaklistoffset
NULL, // tp_iter
NULL, // tp_iternext
......@@ -249,7 +327,7 @@ PyTypeObject rdata_type = {
NULL, // tp_descr_get
NULL, // tp_descr_set
0, // tp_dictoffset
(initproc)Rdata_init, // tp_init
Rdata_init, // tp_init
NULL, // tp_alloc
PyType_GenericNew, // tp_new
NULL, // tp_free
......
......@@ -52,51 +52,51 @@ public:
int RRset_init(s_RRset* self, PyObject* args);
void RRset_destroy(s_RRset* self);
PyObject* RRset_getRdataCount(s_RRset* self);
PyObject* RRset_getName(s_RRset* self);
PyObject* RRset_getClass(s_RRset* self);
PyObject* RRset_getType(s_RRset* self);
PyObject* RRset_getTTL(s_RRset* self);
PyObject* RRset_setName(s_RRset* self, PyObject* args);
PyObject* RRset_setTTL(s_RRset* self, PyObject* args);
PyObject* RRset_toText(s_RRset* self);
PyObject* RRset_getRdataCount(PyObject* self, PyObject* args);
PyObject* RRset_getName(PyObject* self, PyObject* args);
PyObject* RRset_getClass(PyObject* self, PyObject* args);
PyObject* RRset_getType(PyObject* self, PyObject* args);
PyObject* RRset_getTTL(PyObject* self, PyObject* args);
PyObject* RRset_setName(PyObject* self, PyObject* args);
PyObject* RRset_setTTL(PyObject* self, PyObject* args);
PyObject* RRset_toText(PyObject* self, PyObject* args);
PyObject* RRset_str(PyObject* self);
PyObject* RRset_toWire(s_RRset* self, PyObject* args);
PyObject* RRset_addRdata(s_RRset* self, PyObject* args);
PyObject* RRset_getRdata(PyObject* po_self, PyObject*);
PyObject* RRset_removeRRsig(s_RRset* self);
PyObject* RRset_toWire(PyObject* self, PyObject* args);
PyObject* RRset_addRdata(PyObject* self, PyObject* args);
PyObject* RRset_getRdata(PyObject* po_self, PyObject* args);
PyObject* RRset_removeRRsig(PyObject* self, PyObject* args);
// TODO: iterator?
PyMethodDef RRset_methods[] = {
{ "get_rdata_count", reinterpret_cast<PyCFunction>(RRset_getRdataCount), METH_NOARGS,
{ "get_rdata_count", RRset_getRdataCount, METH_NOARGS,
"Returns the number of rdata fields." },
{ "get_name", reinterpret_cast<PyCFunction>(RRset_getName), METH_NOARGS,
{ "get_name", RRset_getName, METH_NOARGS,
"Returns the name of the RRset, as a Name object." },
{ "get_class", reinterpret_cast<PyCFunction>(RRset_getClass), METH_NOARGS,
{ "get_class", RRset_getClass, METH_NOARGS,
"Returns the class of the RRset as an RRClass object." },
{ "get_type", reinterpret_cast<PyCFunction>(RRset_getType), METH_NOARGS,
{ "get_type", RRset_getType, METH_NOARGS,
"Returns the type of the RRset as an RRType object." },
{ "get_ttl", reinterpret_cast<PyCFunction>(RRset_getTTL), METH_NOARGS,
{ "get_ttl", RRset_getTTL, METH_NOARGS,
"Returns the TTL of the RRset as an RRTTL object." },
{ "set_name", reinterpret_cast<PyCFunction>(RRset_setName), METH_VARARGS,
{ "set_name", RRset_setName, METH_VARARGS,
"Sets the name of the RRset.\nTakes a Name object as an argument." },
{ "set_ttl", reinterpret_cast<PyCFunction>(RRset_setTTL), METH_VARARGS,
{ "set_ttl", RRset_setTTL, METH_VARARGS,
"Sets the TTL of the RRset.\nTakes an RRTTL object as an argument." },
{ "to_text", reinterpret_cast<PyCFunction>(RRset_toText), METH_NOARGS,
{ "to_text", RRset_toText, METH_NOARGS,
"Returns the text representation of the RRset as a string" },
{ "to_wire", reinterpret_cast<PyCFunction>(RRset_toWire), METH_VARARGS,
{ "to_wire", RRset_toWire, METH_VARARGS,
"Converts the RRset object to wire format.\n"
"The argument can be either a MessageRenderer or an object that "
"implements the sequence interface. If the object is mutable "
"(for instance a bytearray()), the wire data is added in-place.\n"
"If it is not (for instance a bytes() object), a new object is "
"returned" },
{ "add_rdata", reinterpret_cast<PyCFunction>(RRset_addRdata), METH_VARARGS,
{ "add_rdata", RRset_addRdata, METH_VARARGS,
"Adds the rdata for one RR to the RRset.\nTakes an Rdata object as an argument" },
{ "get_rdata", RRset_getRdata, METH_NOARGS,
"Returns a List containing all Rdata elements" },
{ "remove_rrsig", reinterpret_cast<PyCFunction>(RRset_removeRRsig), METH_NOARGS,
{ "remove_rrsig", RRset_removeRRsig, METH_NOARGS,
"Clears the list of RRsigs for this RRset" },
{ NULL, NULL, 0, NULL }
};
......@@ -133,14 +133,16 @@ RRset_destroy(s_RRset* self) {
}
PyObject*
RRset_getRdataCount(s_RRset* self) {
return (Py_BuildValue("I", self->cppobj->getRdataCount()));
RRset_getRdataCount(PyObject* self, PyObject*) {
return (Py_BuildValue("I", static_cast<const s_RRset*>(self)->cppobj->
getRdataCount()));
}
PyObject*
RRset_getName(s_RRset* self) {
RRset_getName(PyObject* self, PyObject*) {
try {
return (createNameObject(self->cppobj->getName()));
return (createNameObject(static_cast<const s_RRset*>(self)->cppobj->
getName()));
} catch (const exception& ex) {
const string ex_what =
"Unexpected failure getting rrset Name: " +
......@@ -154,9 +156,10 @@ RRset_getName(s_RRset* self) {
}
PyObject*
RRset_getClass(s_RRset* self) {
RRset_getClass(PyObject* self, PyObject*) {
try {
return (createRRClassObject(self->cppobj->getClass()));
return (createRRClassObject(static_cast<const s_RRset*>(self)->cppobj->
getClass()));
} catch (const exception& ex) {
const string ex_what =
"Unexpected failure getting question RRClass: " +
......@@ -170,9 +173,10 @@ RRset_getClass(s_RRset* self) {
}
PyObject*
RRset_getType(s_RRset* self) {
RRset_getType(PyObject* self, PyObject*) {
try {
return (createRRTypeObject(self->cppobj->getType()));
return (createRRTypeObject(static_cast<const s_RRset*>(self)->cppobj->
getType()));
} catch (const exception& ex) {
const string ex_what =
"Unexpected failure getting question RRType: " +
......@@ -186,9 +190,10 @@ RRset_getType(s_RRset* self) {
}
PyObject*
RRset_getTTL(s_RRset* self) {
RRset_getTTL(PyObject* self, PyObject*) {
try {
return (createRRTTLObject(self->cppobj->getTTL()));
return (createRRTTLObject(static_cast<const s_RRset*>(self)->cppobj->
getTTL()));
} catch (const exception& ex) {
const string ex_what =
"Unexpected failure getting question TTL: " +
......@@ -202,29 +207,30 @@ RRset_getTTL(s_RRset* self) {
}
PyObject*
RRset_setName(s_RRset* self, PyObject* args) {
RRset_setName(PyObject* self, PyObject* args) {
PyObject* name;
if (!PyArg_ParseTuple(args, "O!", &name_type, &name)) {
return (NULL);
}
self->cppobj->setName(PyName_ToName(name));
static_cast<s_RRset*>(self)->cppobj->setName(PyName_ToName(name));
Py_RETURN_NONE;
}
PyObject*
RRset_setTTL(s_RRset* self, PyObject* args) {
RRset_setTTL(PyObject* self, PyObject* args) {
PyObject* rrttl;
if (!PyArg_ParseTuple(args, "O!", &rrttl_type, &rrttl)) {
return (NULL);
}
self->cppobj->setTTL(PyRRTTL_ToRRTTL(rrttl));
static_cast<s_RRset*>(self)->cppobj->setTTL(PyRRTTL_ToRRTTL(rrttl));
Py_RETURN_NONE;
}
PyObject*
RRset_toText(s_RRset* self) {
RRset_toText(PyObject* self, PyObject*) {
try {
return (Py_BuildValue("s", self->cppobj->toText().c_str()));
return (Py_BuildValue("s", static_cast<const s_RRset*>(self)->cppobj->
toText().c_str()));
} catch (const EmptyRRset& ers) {
PyErr_SetString(po_EmptyRRset, ers.what());
return (NULL);
......@@ -235,14 +241,15 @@ PyObject*
RRset_str(PyObject* self) {
// Simply call the to_text method we already defined
return (PyObject_CallMethod(self,
const_cast<char*>("to_text"),
const_cast<char*>("to_text"),
const_cast<char*>("")));
}
PyObject*
RRset_toWire(s_RRset* self, PyObject* args) {
RRset_toWire(PyObject* self_p, PyObject* args) {
PyObject* bytes;
PyObject* mr;
const s_RRset* self(static_cast<const s_RRset*>(self_p));
try {
if (PyArg_ParseTuple(args, "O", &bytes) && PySequence_Check(bytes)) {
......@@ -274,13 +281,13 @@ RRset_toWire(s_RRset* self, PyObject* args) {
}
PyObject*
RRset_addRdata(s_RRset* self, PyObject* args) {
RRset_addRdata(PyObject* self, PyObject* args) {
PyObject* rdata;
if (!PyArg_ParseTuple(args, "O!", &rdata_type, &rdata)) {
return (NULL);
}
try {
self->cppobj->addRdata(PyRdata_ToRdata(rdata));
static_cast<s_RRset*>(self)->cppobj->addRdata(PyRdata_ToRdata(rdata));
Py_RETURN_NONE;
} catch (const std::bad_cast&) {
PyErr_Clear();
......@@ -324,8 +331,8 @@ RRset_getRdata(PyObject* po_self, PyObject*) {
}
PyObject*
RRset_removeRRsig(s_RRset* self) {
self->cppobj->removeRRsig();
RRset_removeRRsig(PyObject* self, PyObject*) {
static_cast<s_RRset*>(self)->cppobj->removeRRsig();
Py_RETURN_NONE;
}
......
......@@ -35,6 +35,14 @@ class RdataTest(unittest.TestCase):
self.assertRaises(TypeError, Rdata, "wrong", RRClass("IN"), "192.0.2.99")
self.assertRaises(TypeError, Rdata, RRType("A"), "wrong", "192.0.2.99")
self.assertRaises(TypeError, Rdata, RRType("A"), RRClass("IN"), 1)
self.assertRaises(InvalidRdataText, Rdata, RRType("A"), RRClass("IN"),
"Invalid Rdata Text")
self.assertRaises(CharStringTooLong, Rdata, RRType("TXT"),
RRClass("IN"), ' ' * 256)
self.assertRaises(InvalidRdataLength, Rdata, RRType("TXT"),
RRClass("IN"), bytes(65536))
self.assertRaises(DNSMessageFORMERR, Rdata, RRType("TXT"),
RRClass("IN"), b"\xff")
def test_rdata_to_wire(self):
b = bytearray()
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment