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 : : #include <Python.h>
16 : :
17 : : #include <util/python/pycppwrapper_util.h>
18 : :
19 : : #include <dns/rrset.h>
20 : : #include <dns/name.h>
21 : : #include <dns/messagerenderer.h>
22 : :
23 : : #include "name_python.h"
24 : : #include "pydnspp_common.h"
25 : : #include "rrset_python.h"
26 : : #include "rrclass_python.h"
27 : : #include "rrtype_python.h"
28 : : #include "rrttl_python.h"
29 : : #include "rdata_python.h"
30 : : #include "messagerenderer_python.h"
31 : :
32 : : using namespace std;
33 : : using namespace isc::dns;
34 : : using namespace isc::dns::python;
35 : : using namespace isc::util;
36 : : using namespace isc::util::python;
37 : :
38 : : namespace {
39 : :
40 : : // The s_* Class simply coverst one instantiation of the object
41 : :
42 : : // Using a shared_ptr here should not really be necessary (PyObject
43 : : // is already reference-counted), however internally on the cpp side,
44 : : // not doing so might result in problems, since we can't copy construct
45 : : // rdata field, adding them to rrsets results in a problem when the
46 : : // rrset is destroyed later
47 : : class s_RRset : public PyObject {
48 : : public:
49 : : isc::dns::RRsetPtr cppobj;
50 : : };
51 : :
52 : : int RRset_init(s_RRset* self, PyObject* args);
53 : : void RRset_destroy(s_RRset* self);
54 : :
55 : : PyObject* RRset_getRdataCount(PyObject* self, PyObject* args);
56 : : PyObject* RRset_getName(PyObject* self, PyObject* args);
57 : : PyObject* RRset_getClass(PyObject* self, PyObject* args);
58 : : PyObject* RRset_getType(PyObject* self, PyObject* args);
59 : : PyObject* RRset_getTTL(PyObject* self, PyObject* args);
60 : : PyObject* RRset_setName(PyObject* self, PyObject* args);
61 : : PyObject* RRset_setTTL(PyObject* self, PyObject* args);
62 : : PyObject* RRset_toText(PyObject* self, PyObject* args);
63 : : PyObject* RRset_str(PyObject* self);
64 : : PyObject* RRset_toWire(PyObject* self, PyObject* args);
65 : : PyObject* RRset_addRdata(PyObject* self, PyObject* args);
66 : : PyObject* RRset_getRdata(PyObject* po_self, PyObject* args);
67 : : PyObject* RRset_removeRRsig(PyObject* self, PyObject* args);
68 : :
69 : : // TODO: iterator?
70 : :
71 : : PyMethodDef RRset_methods[] = {
72 : : { "get_rdata_count", RRset_getRdataCount, METH_NOARGS,
73 : : "Returns the number of rdata fields." },
74 : : { "get_name", RRset_getName, METH_NOARGS,
75 : : "Returns the name of the RRset, as a Name object." },
76 : : { "get_class", RRset_getClass, METH_NOARGS,
77 : : "Returns the class of the RRset as an RRClass object." },
78 : : { "get_type", RRset_getType, METH_NOARGS,
79 : : "Returns the type of the RRset as an RRType object." },
80 : : { "get_ttl", RRset_getTTL, METH_NOARGS,
81 : : "Returns the TTL of the RRset as an RRTTL object." },
82 : : { "set_name", RRset_setName, METH_VARARGS,
83 : : "Sets the name of the RRset.\nTakes a Name object as an argument." },
84 : : { "set_ttl", RRset_setTTL, METH_VARARGS,
85 : : "Sets the TTL of the RRset.\nTakes an RRTTL object as an argument." },
86 : : { "to_text", RRset_toText, METH_NOARGS,
87 : : "Returns the text representation of the RRset as a string" },
88 : : { "to_wire", RRset_toWire, METH_VARARGS,
89 : : "Converts the RRset object to wire format.\n"
90 : : "The argument can be either a MessageRenderer or an object that "
91 : : "implements the sequence interface. If the object is mutable "
92 : : "(for instance a bytearray()), the wire data is added in-place.\n"
93 : : "If it is not (for instance a bytes() object), a new object is "
94 : : "returned" },
95 : : { "add_rdata", RRset_addRdata, METH_VARARGS,
96 : : "Adds the rdata for one RR to the RRset.\nTakes an Rdata object as an argument" },
97 : : { "get_rdata", RRset_getRdata, METH_NOARGS,
98 : : "Returns a List containing all Rdata elements" },
99 : : { "remove_rrsig", RRset_removeRRsig, METH_NOARGS,
100 : : "Clears the list of RRsigs for this RRset" },
101 : : { NULL, NULL, 0, NULL }
102 : : };
103 : :
104 : : int
105 : 1121 : RRset_init(s_RRset* self, PyObject* args) {
106 : : PyObject* name;
107 : : PyObject* rrclass;
108 : : PyObject* rrtype;
109 : : PyObject* rrttl;
110 : :
111 [ + + ]: 1121 : if (PyArg_ParseTuple(args, "O!O!O!O!", &name_type, &name,
112 : : &rrclass_type, &rrclass,
113 : : &rrtype_type, &rrtype,
114 : : &rrttl_type, &rrttl
115 : 1121 : )) {
116 : 1120 : self->cppobj = RRsetPtr(new RRset(PyName_ToName(name),
117 : 1120 : PyRRClass_ToRRClass(rrclass),
118 : 1120 : PyRRType_ToRRType(rrtype),
119 [ + - ][ + - ]: 1120 : PyRRTTL_ToRRTTL(rrttl)));
120 : : return (0);
121 : : }
122 : :
123 [ + - ]: 1 : self->cppobj = RRsetPtr();
124 : : return (-1);
125 : : }
126 : :
127 : : void
128 : 2049 : RRset_destroy(s_RRset* self) {
129 : : // Clear the shared_ptr so that its reference count is zero
130 : : // before we call tp_free() (there is no direct release())
131 : 2049 : self->cppobj.reset();
132 : 2049 : Py_TYPE(self)->tp_free(self);
133 : 2049 : }
134 : :
135 : : PyObject*
136 : 786 : RRset_getRdataCount(PyObject* self, PyObject*) {
137 : 786 : return (Py_BuildValue("I", static_cast<const s_RRset*>(self)->cppobj->
138 : 786 : getRdataCount()));
139 : : }
140 : :
141 : : PyObject*
142 : 1034 : RRset_getName(PyObject* self, PyObject*) {
143 : : try {
144 : 1034 : return (createNameObject(static_cast<const s_RRset*>(self)->cppobj->
145 [ + - ][ + - ]: 1034 : getName()));
146 : 0 : } catch (const exception& ex) {
147 : : const string ex_what =
148 : : "Unexpected failure getting rrset Name: " +
149 [ # # ][ # # ]: 0 : string(ex.what());
150 [ # # ]: 0 : PyErr_SetString(po_IscException, ex_what.c_str());
151 [ # # ]: 0 : } catch (...) {
152 : : PyErr_SetString(PyExc_SystemError,
153 [ # # ]: 0 : "Unexpected failure getting rrset Name");
154 : : }
155 : : return (NULL);
156 : : }
157 : :
158 : : PyObject*
159 : 1070 : RRset_getClass(PyObject* self, PyObject*) {
160 : : try {
161 : 1070 : return (createRRClassObject(static_cast<const s_RRset*>(self)->cppobj->
162 [ + - ][ + - ]: 1070 : getClass()));
163 : 0 : } catch (const exception& ex) {
164 : : const string ex_what =
165 : : "Unexpected failure getting question RRClass: " +
166 [ # # ][ # # ]: 0 : string(ex.what());
167 [ # # ]: 0 : PyErr_SetString(po_IscException, ex_what.c_str());
168 [ # # ]: 0 : } catch (...) {
169 : : PyErr_SetString(PyExc_SystemError,
170 [ # # ]: 0 : "Unexpected failure getting question RRClass");
171 : : }
172 : : return (NULL);
173 : : }
174 : :
175 : : PyObject*
176 : 1741 : RRset_getType(PyObject* self, PyObject*) {
177 : : try {
178 : 1741 : return (createRRTypeObject(static_cast<const s_RRset*>(self)->cppobj->
179 [ + - ][ + - ]: 1741 : getType()));
180 : 0 : } catch (const exception& ex) {
181 : : const string ex_what =
182 : : "Unexpected failure getting question RRType: " +
183 [ # # ][ # # ]: 0 : string(ex.what());
184 [ # # ]: 0 : PyErr_SetString(po_IscException, ex_what.c_str());
185 [ # # ]: 0 : } catch (...) {
186 : : PyErr_SetString(PyExc_SystemError,
187 [ # # ]: 0 : "Unexpected failure getting question RRType");
188 : : }
189 : : return (NULL);
190 : : }
191 : :
192 : : PyObject*
193 : 1021 : RRset_getTTL(PyObject* self, PyObject*) {
194 : : try {
195 : 1021 : return (createRRTTLObject(static_cast<const s_RRset*>(self)->cppobj->
196 [ + - ][ + - ]: 1021 : getTTL()));
197 : 0 : } catch (const exception& ex) {
198 : : const string ex_what =
199 : : "Unexpected failure getting question TTL: " +
200 [ # # ][ # # ]: 0 : string(ex.what());
201 [ # # ]: 0 : PyErr_SetString(po_IscException, ex_what.c_str());
202 [ # # ]: 0 : } catch (...) {
203 : : PyErr_SetString(PyExc_SystemError,
204 [ # # ]: 0 : "Unexpected failure getting question TTL");
205 : : }
206 : : return (NULL);
207 : : }
208 : :
209 : : PyObject*
210 : 2 : RRset_setName(PyObject* self, PyObject* args) {
211 : : PyObject* name;
212 [ + + ]: 2 : if (!PyArg_ParseTuple(args, "O!", &name_type, &name)) {
213 : : return (NULL);
214 : : }
215 : 1 : static_cast<s_RRset*>(self)->cppobj->setName(PyName_ToName(name));
216 : 2 : Py_RETURN_NONE;
217 : : }
218 : :
219 : : PyObject*
220 : 3 : RRset_setTTL(PyObject* self, PyObject* args) {
221 : : PyObject* rrttl;
222 [ + + ]: 3 : if (!PyArg_ParseTuple(args, "O!", &rrttl_type, &rrttl)) {
223 : : return (NULL);
224 : : }
225 : 2 : static_cast<s_RRset*>(self)->cppobj->setTTL(PyRRTTL_ToRRTTL(rrttl));
226 : 3 : Py_RETURN_NONE;
227 : : }
228 : :
229 : : PyObject*
230 : 50 : RRset_toText(PyObject* self, PyObject*) {
231 : : try {
232 : 50 : return (Py_BuildValue("s", static_cast<const s_RRset*>(self)->cppobj->
233 [ + + ][ + - ]: 51 : toText().c_str()));
234 [ - + ]: 2 : } catch (const EmptyRRset& ers) {
235 [ - + ]: 1 : PyErr_SetString(po_EmptyRRset, ers.what());
236 : : return (NULL);
237 : : }
238 : : }
239 : :
240 : : PyObject*
241 : 9 : RRset_str(PyObject* self) {
242 : : // Simply call the to_text method we already defined
243 : : return (PyObject_CallMethod(self,
244 : : const_cast<char*>("to_text"),
245 : 9 : const_cast<char*>("")));
246 : : }
247 : :
248 : : PyObject*
249 : 64 : RRset_toWire(PyObject* self_p, PyObject* args) {
250 : : PyObject* bytes;
251 : : PyObject* mr;
252 : 64 : const s_RRset* self(static_cast<const s_RRset*>(self_p));
253 : :
254 : : try {
255 [ + - ][ + - ]: 64 : if (PyArg_ParseTuple(args, "O", &bytes) && PySequence_Check(bytes)) {
[ + - ][ + + ]
[ + + ]
256 : 62 : PyObject* bytes_o = bytes;
257 : :
258 : 62 : OutputBuffer buffer(4096);
259 [ + + ]: 62 : self->cppobj->toWire(buffer);
260 [ + - ]: 61 : PyObject* n = PyBytes_FromStringAndSize(static_cast<const char*>(buffer.getData()), buffer.getLength());
261 [ + - ]: 61 : PyObject* result = PySequence_InPlaceConcat(bytes_o, n);
262 : : // We need to release the object we temporarily created here
263 : : // to prevent memory leak
264 [ + - ][ + - ]: 61 : Py_DECREF(n);
265 : : return (result);
266 [ + - ][ + + ]: 2 : } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) {
267 [ + - ][ + - ]: 1 : self->cppobj->toWire(PyMessageRenderer_ToMessageRenderer(mr));
268 : : // If we return NULL it is seen as an error, so use this for
269 : : // None returns
270 : 1 : Py_RETURN_NONE;
271 : : }
272 [ - + ]: 2 : } catch (const EmptyRRset& ers) {
273 [ - + ]: 1 : PyErr_Clear();
274 [ - + ]: 1 : PyErr_SetString(po_EmptyRRset, ers.what());
275 : : return (NULL);
276 : : }
277 : 1 : PyErr_Clear();
278 : : PyErr_SetString(PyExc_TypeError,
279 : 1 : "toWire argument must be a sequence object or a MessageRenderer");
280 : 64 : return (NULL);
281 : : }
282 : :
283 : : PyObject*
284 : 1357 : RRset_addRdata(PyObject* self, PyObject* args) {
285 : : PyObject* rdata;
286 [ + - ]: 1357 : if (!PyArg_ParseTuple(args, "O!", &rdata_type, &rdata)) {
287 : : return (NULL);
288 : : }
289 : : try {
290 [ + - ][ + + ]: 1357 : static_cast<s_RRset*>(self)->cppobj->addRdata(PyRdata_ToRdata(rdata));
291 : 1357 : Py_RETURN_NONE;
292 [ - + ]: 2 : } catch (const std::bad_cast&) {
293 [ - + ]: 1 : PyErr_Clear();
294 : : PyErr_SetString(PyExc_TypeError,
295 [ - + ]: 1 : "Rdata type to add must match type of RRset");
296 : : return (NULL);
297 : : }
298 : : }
299 : :
300 : : PyObject*
301 : 1227 : RRset_getRdata(PyObject* po_self, PyObject*) {
302 : 1227 : const s_RRset* const self = static_cast<s_RRset*>(po_self);
303 : :
304 : : try {
305 [ + - ][ + - ]: 2454 : PyObjectContainer list_container(PyList_New(0));
[ + - ]
306 : :
307 [ + - ][ + + ]: 3851 : for (RdataIteratorPtr it = self->cppobj->getRdataIterator();
308 [ + - ][ + - ]: 2624 : !it->isLast(); it->next()) {
309 [ - + ]: 1397 : if (PyList_Append(list_container.get(),
310 : : PyObjectContainer(
311 : : createRdataObject(
312 [ + - ]: 1397 : createRdata(self->cppobj->getType(),
313 [ + - ]: 1397 : self->cppobj->getClass(),
314 [ + - ][ + - ]: 4191 : it->getCurrent()))).get())
[ + - ][ + - ]
[ + - ][ + - ]
315 : : == -1) {
316 [ # # ][ # # ]: 0 : isc_throw(PyCPPWrapperException, "PyList_Append failed, "
[ # # ]
317 : : "probably due to short memory");
318 : : }
319 : : }
320 : : return (list_container.release());
321 : 0 : } catch (const exception& ex) {
322 : : const string ex_what =
323 : : "Unexpected failure getting rrset Rdata: " +
324 [ # # ][ # # ]: 0 : string(ex.what());
325 [ # # ]: 0 : PyErr_SetString(po_IscException, ex_what.c_str());
326 [ # # ]: 0 : } catch (...) {
327 : : PyErr_SetString(PyExc_SystemError,
328 [ # # ]: 0 : "Unexpected failure getting rrset Rdata");
329 : : }
330 : : return (NULL);
331 : : }
332 : :
333 : : PyObject*
334 : 2 : RRset_removeRRsig(PyObject* self, PyObject*) {
335 : 2 : static_cast<s_RRset*>(self)->cppobj->removeRRsig();
336 : 2 : Py_RETURN_NONE;
337 : : }
338 : :
339 : : } // end of unnamed namespace
340 : :
341 : : namespace isc {
342 : : namespace dns {
343 : : namespace python {
344 : :
345 : : //
346 : : // Declaration of the custom exceptions
347 : : // Initialization and addition of these go in the module init at the
348 : : // end
349 : : //
350 : : PyObject* po_EmptyRRset;
351 : :
352 : : PyTypeObject rrset_type = {
353 : : PyVarObject_HEAD_INIT(NULL, 0)
354 : : "pydnspp.RRset",
355 : : sizeof(s_RRset), // tp_basicsize
356 : : 0, // tp_itemsize
357 : : (destructor)RRset_destroy, // tp_dealloc
358 : : NULL, // tp_print
359 : : NULL, // tp_getattr
360 : : NULL, // tp_setattr
361 : : NULL, // tp_reserved
362 : : NULL, // tp_repr
363 : : NULL, // tp_as_number
364 : : NULL, // tp_as_sequence
365 : : NULL, // tp_as_mapping
366 : : NULL, // tp_hash
367 : : NULL, // tp_call
368 : : RRset_str, // tp_str
369 : : NULL, // tp_getattro
370 : : NULL, // tp_setattro
371 : : NULL, // tp_as_buffer
372 : : Py_TPFLAGS_DEFAULT, // tp_flags
373 : : "The AbstractRRset class is an abstract base class that "
374 : : "models a DNS RRset.\n\n"
375 : : "An object of (a specific derived class of) AbstractRRset "
376 : : "models an RRset as described in the DNS standard:\n"
377 : : "A set of DNS resource records (RRs) of the same type and class. "
378 : : "The standard requires the TTL of all RRs in an RRset be the same; "
379 : : "this class follows that requirement.\n\n"
380 : : "Note about duplicate RDATA: RFC2181 states that it's meaningless that an "
381 : : "RRset contains two identical RRs and that name servers should suppress "
382 : : "such duplicates.\n"
383 : : "This class is not responsible for ensuring this requirement: For example, "
384 : : "addRdata() method doesn't check if there's already RDATA identical "
385 : : "to the one being added.\n"
386 : : "This is because such checks can be expensive, and it's often easy to "
387 : : "ensure the uniqueness requirement at the %data preparation phase "
388 : : "(e.g. when loading a zone).",
389 : : NULL, // tp_traverse
390 : : NULL, // tp_clear
391 : : NULL, // tp_richcompare
392 : : 0, // tp_weaklistoffset
393 : : NULL, // tp_iter
394 : : NULL, // tp_iternext
395 : : RRset_methods, // tp_methods
396 : : NULL, // tp_members
397 : : NULL, // tp_getset
398 : : NULL, // tp_base
399 : : NULL, // tp_dict
400 : : NULL, // tp_descr_get
401 : : NULL, // tp_descr_set
402 : : 0, // tp_dictoffset
403 : : (initproc)RRset_init, // tp_init
404 : : NULL, // tp_alloc
405 : : PyType_GenericNew, // tp_new
406 : : NULL, // tp_free
407 : : NULL, // tp_is_gc
408 : : NULL, // tp_bases
409 : : NULL, // tp_mro
410 : : NULL, // tp_cache
411 : : NULL, // tp_subclasses
412 : : NULL, // tp_weaklist
413 : : NULL, // tp_del
414 : : 0 // tp_version_tag
415 : : };
416 : :
417 : : PyObject*
418 : 929 : createRRsetObject(const AbstractRRset& source) {
419 : :
420 : : // RRsets are noncopyable, so as a workaround we recreate a new one
421 : : // and copy over all content
422 : : RRsetPtr new_rrset = isc::dns::RRsetPtr(
423 : 1858 : new isc::dns::RRset(source.getName(), source.getClass(),
424 [ + - ]: 929 : source.getType(), source.getTTL()));
425 : :
426 [ + - ]: 929 : isc::dns::RdataIteratorPtr rdata_it(source.getRdataIterator());
427 [ + - ][ + - ]: 2049 : for (rdata_it->first(); !rdata_it->isLast(); rdata_it->next()) {
[ + - ][ + + ]
428 [ + - ][ + - ]: 1120 : new_rrset->addRdata(rdata_it->getCurrent());
429 : : }
430 : :
431 [ + - ]: 929 : isc::dns::RRsetPtr sigs = source.getRRsig();
432 [ + + ]: 929 : if (sigs) {
433 [ + - ]: 12 : new_rrset->addRRsig(sigs);
434 : : }
435 : : s_RRset* py_rrset =
436 [ + - ]: 929 : static_cast<s_RRset*>(rrset_type.tp_alloc(&rrset_type, 0));
437 [ - + ]: 929 : if (py_rrset == NULL) {
438 [ # # ][ # # ]: 0 : isc_throw(PyCPPWrapperException, "Unexpected NULL C++ object, "
[ # # ]
439 : : "probably due to short memory");
440 : : }
441 [ + - ]: 929 : py_rrset->cppobj = new_rrset;
442 : 929 : return (py_rrset);
443 : : }
444 : :
445 : : bool
446 : 0 : PyRRset_Check(PyObject* obj) {
447 [ # # ]: 0 : if (obj == NULL) {
448 [ # # ][ # # ]: 0 : isc_throw(PyCPPWrapperException, "obj argument NULL in typecheck");
449 : : }
450 [ # # ][ # # ]: 0 : return (PyObject_TypeCheck(obj, &rrset_type));
451 : : }
452 : :
453 : : AbstractRRset&
454 : 431 : PyRRset_ToRRset(PyObject* rrset_obj) {
455 : 431 : s_RRset* rrset = static_cast<s_RRset*>(rrset_obj);
456 : 431 : return (*rrset->cppobj);
457 : : }
458 : :
459 : : RRsetPtr
460 : 400 : PyRRset_ToRRsetPtr(PyObject* rrset_obj) {
461 [ - + ]: 400 : if (rrset_obj == NULL) {
462 [ # # ][ # # ]: 0 : isc_throw(PyCPPWrapperException,
463 : : "obj argument NULL in RRset PyObject conversion");
464 : : }
465 : 400 : s_RRset* rrset = static_cast<s_RRset*>(rrset_obj);
466 : 400 : return (rrset->cppobj);
467 : : }
468 : :
469 : :
470 : : } // end python namespace
471 : : } // end dns namespace
472 : 123 : } // end isc namespace
|