Branch data Line data Source code
1 : : // Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
2 : : //
3 : : // Permission to use, copy, modify, and/or distribute this software for any
4 : : // purpose with or without fee is hereby granted, provided that the above
5 : : // copyright notice and this permission notice appear in all copies.
6 : : //
7 : : // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
8 : : // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
9 : : // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
10 : : // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
11 : : // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
12 : : // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
13 : : // PERFORMANCE OF THIS SOFTWARE.
14 : :
15 : : #define PY_SSIZE_T_CLEAN
16 : : #include <Python.h>
17 : : #include <dns/rdata.h>
18 : : #include <dns/messagerenderer.h>
19 : : #include <dns/exceptions.h>
20 : : #include <util/buffer.h>
21 : : #include <util/python/pycppwrapper_util.h>
22 : :
23 : : #include "rdata_python.h"
24 : : #include "rrtype_python.h"
25 : : #include "rrclass_python.h"
26 : : #include "messagerenderer_python.h"
27 : : #include "name_python.h"
28 : :
29 : : using namespace isc::dns;
30 : : using namespace isc::dns::python;
31 : : using namespace isc::util;
32 : : using namespace isc::util::python;
33 : : using namespace isc::dns::rdata;
34 : :
35 : : namespace {
36 : :
37 : : typedef PyObject* method(PyObject* self, PyObject* args);
38 : :
39 : : // Wrap a method into an exception handling, converting C++ exceptions
40 : : // to python ones. The params and return value is just passed through.
41 : : PyObject*
42 : 571 : exception_wrap(method* method, PyObject* self, PyObject* args) {
43 : : try {
44 [ + - ]: 571 : return (method(self, args));
45 : 0 : } catch (const std::exception& ex) {
46 : : // FIXME: These exceptions are not tested, I don't know how or if
47 : : // at all they can be triggered. But they are caught just in the case.
48 : 0 : PyErr_SetString(PyExc_Exception, (std::string("Unknown exception: ") +
49 [ # # ][ # # ]: 0 : ex.what()).c_str());
[ # # ]
50 : : return (NULL);
51 [ # # ]: 0 : } catch (...) {
52 [ # # ]: 0 : PyErr_SetString(PyExc_Exception, "Unknown exception");
53 : : return (NULL);
54 : : }
55 : : }
56 : :
57 : : class s_Rdata : public PyObject {
58 : : public:
59 : : isc::dns::rdata::ConstRdataPtr cppobj;
60 : : };
61 : :
62 : : typedef CPPPyObjectContainer<s_Rdata, Rdata> RdataContainer;
63 : :
64 : : //
65 : : // We declare the functions here, the definitions are below
66 : : // the type definition of the object, since both can use the other
67 : : //
68 : :
69 : : // General creation and destruction
70 : : int Rdata_init(PyObject* self, PyObject* args, PyObject*);
71 : : void Rdata_destroy(PyObject* self);
72 : :
73 : : // These are the functions we export
74 : : PyObject* Rdata_toText(PyObject* self, PyObject*);
75 : : // This is a second version of toText, we need one where the argument
76 : : // is a PyObject*, for the str() function in python.
77 : : PyObject* Rdata_str(PyObject* self);
78 : : PyObject* Rdata_toWire(PyObject* self, PyObject* args);
79 : : PyObject* RData_richcmp(PyObject* self, PyObject* other, int op);
80 : :
81 : : // This list contains the actual set of functions we have in
82 : : // python. Each entry has
83 : : // 1. Python method name
84 : : // 2. Our static function here
85 : : // 3. Argument type
86 : : // 4. Documentation
87 : : PyMethodDef Rdata_methods[] = {
88 : : { "to_text", Rdata_toText, METH_NOARGS,
89 : : "Returns the string representation" },
90 : : { "to_wire", Rdata_toWire, METH_VARARGS,
91 : : "Converts the Rdata object to wire format.\n"
92 : : "The argument can be either a MessageRenderer or an object that "
93 : : "implements the sequence interface. If the object is mutable "
94 : : "(for instance a bytearray()), the wire data is added in-place.\n"
95 : : "If it is not (for instance a bytes() object), a new object is "
96 : : "returned" },
97 : : { NULL, NULL, 0, NULL }
98 : : };
99 : :
100 : : int
101 : 1205 : Rdata_init(PyObject* self_p, PyObject* args, PyObject*) {
102 : : PyObject* rrtype;
103 : : PyObject* rrclass;
104 : : const char* s;
105 : : const char* data;
106 : : Py_ssize_t len;
107 : 1205 : s_Rdata* self(static_cast<s_Rdata*>(self_p));
108 : :
109 : : try {
110 : : // Create from string
111 [ + + ]: 1205 : if (PyArg_ParseTuple(args, "O!O!s", &rrtype_type, &rrtype,
112 : : &rrclass_type, &rrclass,
113 [ + - ]: 1205 : &s)) {
114 [ + - ]: 1191 : self->cppobj = createRdata(PyRRType_ToRRType(rrtype),
115 [ + - ][ + - ]: 3571 : PyRRClass_ToRRClass(rrclass), s);
[ + + ][ + - ]
116 : : return (0);
117 [ + + ]: 14 : } else if (PyArg_ParseTuple(args, "O!O!y#", &rrtype_type, &rrtype,
118 [ + - ]: 14 : &rrclass_type, &rrclass, &data, &len)) {
119 : 11 : InputBuffer input_buffer(data, len);
120 [ + - ]: 11 : self->cppobj = createRdata(PyRRType_ToRRType(rrtype),
121 [ + - ]: 11 : PyRRClass_ToRRClass(rrclass),
122 [ + + ][ + - ]: 11 : input_buffer, len);
123 : : return (0);
124 : : }
125 : 2 : } catch (const isc::dns::rdata::InvalidRdataText& irdt) {
126 [ - + ]: 1 : PyErr_SetString(po_InvalidRdataText, irdt.what());
127 : : return (-1);
128 : 2 : } catch (const isc::dns::rdata::InvalidRdataLength& irdl) {
129 [ - + ]: 1 : PyErr_SetString(po_InvalidRdataLength, irdl.what());
130 : : return (-1);
131 : 2 : } catch (const isc::dns::rdata::CharStringTooLong& cstl) {
132 [ - + ]: 1 : PyErr_SetString(po_CharStringTooLong, cstl.what());
133 : : return (-1);
134 : 2 : } catch (const isc::dns::DNSMessageFORMERR& dmfe) {
135 [ - + ]: 1 : PyErr_SetString(po_DNSMessageFORMERR, dmfe.what());
136 : : return (-1);
137 : 0 : } catch (const std::exception& ex) {
138 : : // FIXME: These exceptions are not tested, I don't know how or if
139 : : // at all they can be triggered. But they are caught just in the case.
140 : 0 : PyErr_SetString(PyExc_Exception, (std::string("Unknown exception: ") +
141 [ # # ][ # # ]: 0 : ex.what()).c_str());
[ # # ]
142 : : return (-1);
143 [ + + + + : 4 : } catch (...) {
- - ]
144 [ # # ]: 0 : PyErr_SetString(PyExc_Exception, "Unknown exception");
145 : : return (-1);
146 : : }
147 : :
148 : : return (-1);
149 : : }
150 : :
151 : : void
152 : 2602 : Rdata_destroy(PyObject* self) {
153 : : // Clear the shared_ptr so that its reference count is zero
154 : : // before we call tp_free() (there is no direct release())
155 : 2602 : static_cast<s_Rdata*>(self)->cppobj.reset();
156 : 2602 : Py_TYPE(self)->tp_free(self);
157 : 2602 : }
158 : :
159 : : PyObject*
160 : 564 : Rdata_toText_internal(PyObject* self, PyObject*) {
161 : : // Py_BuildValue makes python objects from native data
162 : 564 : return (Py_BuildValue("s", static_cast<const s_Rdata*>(self)->cppobj->
163 [ + - ]: 564 : toText().c_str()));
164 : : }
165 : :
166 : : PyObject*
167 : 564 : Rdata_toText(PyObject* self, PyObject* args) {
168 : 564 : return (exception_wrap(&Rdata_toText_internal, self, args));
169 : : }
170 : :
171 : : PyObject*
172 : 15 : Rdata_str(PyObject* self) {
173 : : // Simply call the to_text method we already defined
174 : : return (PyObject_CallMethod(self,
175 : : const_cast<char*>("to_text"),
176 : 15 : const_cast<char*>("")));
177 : : }
178 : :
179 : : PyObject*
180 : 7 : Rdata_toWire_internal(PyObject* self_p, PyObject* args) {
181 : : PyObject* bytes;
182 : : PyObject* mr;
183 : 7 : const s_Rdata* self(static_cast<const s_Rdata*>(self_p));
184 : :
185 [ + - ][ + + ]: 7 : if (PyArg_ParseTuple(args, "O", &bytes) && PySequence_Check(bytes)) {
[ + + ]
186 : 4 : PyObject* bytes_o = bytes;
187 : :
188 : 4 : OutputBuffer buffer(4);
189 [ + - ]: 4 : self->cppobj->toWire(buffer);
190 [ + - ]: 4 : PyObject* rd_bytes = PyBytes_FromStringAndSize(static_cast<const char*>(buffer.getData()), buffer.getLength());
191 : : // Make sure exceptions from here are propagated.
192 : : // The exception is already set, so we just return NULL
193 [ + - ]: 4 : if (rd_bytes == NULL) {
194 : : return (NULL);
195 : : }
196 [ + - ]: 4 : PyObject* result = PySequence_InPlaceConcat(bytes_o, rd_bytes);
197 : : // We need to release the object we temporarily created here
198 : : // to prevent memory leak
199 [ + - ][ + - ]: 4 : Py_DECREF(rd_bytes);
200 : : return (result);
201 [ + + ]: 3 : } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) {
202 : 2 : self->cppobj->toWire(PyMessageRenderer_ToMessageRenderer(mr));
203 : : // If we return NULL it is seen as an error, so use this for
204 : : // None returns
205 : 2 : Py_RETURN_NONE;
206 : : }
207 : 1 : PyErr_Clear();
208 : : PyErr_SetString(PyExc_TypeError,
209 : 1 : "toWire argument must be a sequence object or a MessageRenderer");
210 : 7 : return (NULL);
211 : : }
212 : :
213 : : PyObject*
214 : 7 : Rdata_toWire(PyObject* self, PyObject* args) {
215 : 7 : return (exception_wrap(&Rdata_toWire_internal, self, args));
216 : : }
217 : :
218 : : PyObject*
219 : 349 : RData_richcmp(PyObject* self_p, PyObject* other_p, int op) {
220 : : try {
221 : : bool c;
222 : 349 : const s_Rdata* self(static_cast<const s_Rdata*>(self_p)),
223 : 349 : * other(static_cast<const s_Rdata*>(other_p));
224 : :
225 : : // Check for null and if the types match. If different type,
226 : : // simply return False
227 [ + - ][ - + ]: 349 : if (!other || (self->ob_type != other->ob_type)) {
228 : 0 : Py_RETURN_FALSE;
229 : : }
230 : :
231 [ + + + + : 349 : switch (op) {
+ + - ]
232 : : case Py_LT:
233 [ + - ]: 18 : c = self->cppobj->compare(*other->cppobj) < 0;
234 : 18 : break;
235 : : case Py_LE:
236 [ + - ]: 2 : c = self->cppobj->compare(*other->cppobj) < 0 ||
237 [ - + ][ # # ]: 2 : self->cppobj->compare(*other->cppobj) == 0;
[ # # ]
238 : 2 : break;
239 : : case Py_EQ:
240 [ + - ]: 323 : c = self->cppobj->compare(*other->cppobj) == 0;
241 : 323 : break;
242 : : case Py_NE:
243 [ + - ]: 2 : c = self->cppobj->compare(*other->cppobj) != 0;
244 : 2 : break;
245 : : case Py_GT:
246 [ + - ]: 2 : c = self->cppobj->compare(*other->cppobj) > 0;
247 : 2 : break;
248 : : case Py_GE:
249 [ + - ]: 2 : c = self->cppobj->compare(*other->cppobj) > 0 ||
250 [ + - ][ + - ]: 2 : self->cppobj->compare(*other->cppobj) == 0;
[ - + ]
251 : 2 : break;
252 : : default:
253 : : PyErr_SetString(PyExc_IndexError,
254 [ # # ]: 0 : "Unhandled rich comparison operator");
255 : : return (NULL);
256 : : }
257 [ + + ]: 349 : if (c) {
258 : 327 : Py_RETURN_TRUE;
259 : : } else {
260 : 349 : Py_RETURN_FALSE;
261 : : }
262 : 0 : } catch (const std::exception& ex) {
263 : : // FIXME: These exceptions are not tested, I don't know how or if
264 : : // at all they can be triggered. But they are caught just in the case.
265 : 0 : PyErr_SetString(PyExc_Exception, (std::string("Unknown exception: ") +
266 [ # # ][ # # ]: 0 : ex.what()).c_str());
[ # # ]
267 : : return (NULL);
268 [ # # ]: 0 : } catch (...) {
269 [ # # ]: 0 : PyErr_SetString(PyExc_Exception, "Unknown exception");
270 : : return (NULL);
271 : : }
272 : : }
273 : :
274 : : } // end of unnamed namespace
275 : :
276 : : namespace isc {
277 : : namespace dns {
278 : : namespace python {
279 : :
280 : :
281 : : //
282 : : // Declaration of the custom exceptions
283 : : // Initialization and addition of these go in the initModulePart
284 : : // function in pydnspp
285 : : //
286 : : PyObject* po_InvalidRdataLength;
287 : : PyObject* po_InvalidRdataText;
288 : : PyObject* po_CharStringTooLong;
289 : :
290 : : // This defines the complete type for reflection in python and
291 : : // parsing of PyObject* to s_Rdata
292 : : // Most of the functions are not actually implemented and NULL here.
293 : : PyTypeObject rdata_type = {
294 : : PyVarObject_HEAD_INIT(NULL, 0)
295 : : "pydnspp.Rdata",
296 : : sizeof(s_Rdata), // tp_basicsize
297 : : 0, // tp_itemsize
298 : : Rdata_destroy, // tp_dealloc
299 : : NULL, // tp_print
300 : : NULL, // tp_getattr
301 : : NULL, // tp_setattr
302 : : NULL, // tp_reserved
303 : : NULL, // tp_repr
304 : : NULL, // tp_as_number
305 : : NULL, // tp_as_sequence
306 : : NULL, // tp_as_mapping
307 : : NULL, // tp_hash
308 : : NULL, // tp_call
309 : : Rdata_str, // tp_str
310 : : NULL, // tp_getattro
311 : : NULL, // tp_setattro
312 : : NULL, // tp_as_buffer
313 : : Py_TPFLAGS_DEFAULT, // tp_flags
314 : : "The Rdata class is an abstract base class that provides "
315 : : "a set of common interfaces to manipulate concrete RDATA objects.",
316 : : NULL, // tp_traverse
317 : : NULL, // tp_clear
318 : : RData_richcmp, // tp_richcompare
319 : : 0, // tp_weaklistoffset
320 : : NULL, // tp_iter
321 : : NULL, // tp_iternext
322 : : Rdata_methods, // tp_methods
323 : : NULL, // tp_members
324 : : NULL, // tp_getset
325 : : NULL, // tp_base
326 : : NULL, // tp_dict
327 : : NULL, // tp_descr_get
328 : : NULL, // tp_descr_set
329 : : 0, // tp_dictoffset
330 : : Rdata_init, // tp_init
331 : : NULL, // tp_alloc
332 : : PyType_GenericNew, // tp_new
333 : : NULL, // tp_free
334 : : NULL, // tp_is_gc
335 : : NULL, // tp_bases
336 : : NULL, // tp_mro
337 : : NULL, // tp_cache
338 : : NULL, // tp_subclasses
339 : : NULL, // tp_weaklist
340 : : NULL, // tp_del
341 : : 0 // tp_version_tag
342 : : };
343 : :
344 : : PyObject*
345 : 1397 : createRdataObject(ConstRdataPtr source) {
346 : : s_Rdata* py_rdata =
347 : 1397 : static_cast<s_Rdata*>(rdata_type.tp_alloc(&rdata_type, 0));
348 [ - + ]: 1397 : if (py_rdata == NULL) {
349 [ # # ][ # # ]: 0 : isc_throw(PyCPPWrapperException, "Unexpected NULL C++ object, "
350 : : "probably due to short memory");
351 : : }
352 : 1397 : py_rdata->cppobj = source;
353 : 1397 : return (py_rdata);
354 : : }
355 : :
356 : : bool
357 : 42 : PyRdata_Check(PyObject* obj) {
358 [ - + ]: 42 : if (obj == NULL) {
359 [ # # ][ # # ]: 0 : isc_throw(PyCPPWrapperException, "obj argument NULL in typecheck");
360 : : }
361 [ + + ][ + - ]: 42 : return (PyObject_TypeCheck(obj, &rdata_type));
362 : : }
363 : :
364 : : const Rdata&
365 : 1418 : PyRdata_ToRdata(const PyObject* rdata_obj) {
366 [ - + ]: 1418 : if (rdata_obj == NULL) {
367 [ # # ][ # # ]: 0 : isc_throw(PyCPPWrapperException,
368 : : "obj argument NULL in Rdata PyObject conversion");
369 : : }
370 : 1418 : const s_Rdata* rdata = static_cast<const s_Rdata*>(rdata_obj);
371 : 1418 : return (*rdata->cppobj);
372 : : }
373 : :
374 : : } // end python namespace
375 : : } // end dns namespace
376 : 2690 : } // end isc namespace
|