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/question.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 "pydnspp_common.h"
24 : : #include "question_python.h"
25 : : #include "name_python.h"
26 : : #include "rrclass_python.h"
27 : : #include "rrtype_python.h"
28 : : #include "messagerenderer_python.h"
29 : :
30 : : using namespace std;
31 : : using namespace isc::dns;
32 : : using namespace isc::dns::python;
33 : : using namespace isc::util;
34 : : using namespace isc::util::python;
35 : : using namespace isc;
36 : :
37 : : namespace {
38 : : class s_Question : public PyObject {
39 : : public:
40 : : isc::dns::QuestionPtr cppobj;
41 : : };
42 : :
43 : : static int Question_init(s_Question* self, PyObject* args);
44 : : static void Question_destroy(s_Question* self);
45 : :
46 : : // These are the functions we export
47 : : static PyObject* Question_getName(s_Question* self);
48 : : static PyObject* Question_getType(s_Question* self);
49 : : static PyObject* Question_getClass(s_Question* self);
50 : : static PyObject* Question_toText(s_Question* self);
51 : : // This is a second version of toText, we need one where the argument
52 : : // is a PyObject*, for the str() function in python.
53 : : static PyObject* Question_str(PyObject* self);
54 : : static PyObject* Question_toWire(s_Question* self, PyObject* args);
55 : :
56 : : // This list contains the actual set of functions we have in
57 : : // python. Each entry has
58 : : // 1. Python method name
59 : : // 2. Our static function here
60 : : // 3. Argument type
61 : : // 4. Documentation
62 : : static PyMethodDef Question_methods[] = {
63 : : { "get_name", reinterpret_cast<PyCFunction>(Question_getName), METH_NOARGS,
64 : : "Returns the Name" },
65 : : { "get_type", reinterpret_cast<PyCFunction>(Question_getType), METH_NOARGS,
66 : : "Returns the RRType" },
67 : : { "get_class", reinterpret_cast<PyCFunction>(Question_getClass), METH_NOARGS,
68 : : "Returns the RRClass" },
69 : : { "to_text", reinterpret_cast<PyCFunction>(Question_toText), METH_NOARGS,
70 : : "Returns the string representation" },
71 : : { "to_wire", reinterpret_cast<PyCFunction>(Question_toWire), METH_VARARGS,
72 : : "Converts the Question object to wire format.\n"
73 : : "The argument can be either a MessageRenderer or an object that "
74 : : "implements the sequence interface. If the object is mutable "
75 : : "(for instance a bytearray()), the wire data is added in-place.\n"
76 : : "If it is not (for instance a bytes() object), a new object is "
77 : : "returned" },
78 : : { NULL, NULL, 0, NULL }
79 : : };
80 : :
81 : : static int
82 : 322 : Question_init(s_Question* self, PyObject* args) {
83 : : // Try out the various combinations of arguments to call the
84 : : // correct cpp constructor.
85 : : // Note that PyArg_ParseType can set PyError, and we need to clear
86 : : // that if we try several like here. Otherwise the *next* python
87 : : // call will suddenly appear to throw an exception.
88 : : // (the way to do exceptions is to set PyErr and return -1)
89 : : PyObject* name;
90 : : PyObject* rrclass;
91 : : PyObject* rrtype;
92 : :
93 : : const char* b;
94 : : Py_ssize_t len;
95 : 322 : unsigned int position = 0;
96 : :
97 : : try {
98 [ + + ]: 322 : if (PyArg_ParseTuple(args, "O!O!O!", &name_type, &name,
99 : : &rrclass_type, &rrclass,
100 : : &rrtype_type, &rrtype
101 [ + - ]: 322 : )) {
102 : 317 : self->cppobj = QuestionPtr(new Question(PyName_ToName(name),
103 : 317 : PyRRClass_ToRRClass(rrclass),
104 [ + - ][ + - ]: 634 : PyRRType_ToRRType(rrtype)));
[ + - ][ + - ]
[ + - ][ + - ]
105 : : return (0);
106 [ + - ][ + + ]: 5 : } else if (PyArg_ParseTuple(args, "y#|I", &b, &len, &position)) {
107 [ + - ]: 4 : PyErr_Clear();
108 : 4 : InputBuffer inbuf(b, len);
109 : 4 : inbuf.setPosition(position);
110 [ + - ][ + + ]: 4 : self->cppobj = QuestionPtr(new Question(inbuf));
[ + - ][ + - ]
111 : : return (0);
112 : : }
113 : 2 : } catch (const DNSMessageFORMERR& dmfe) {
114 [ - + ]: 1 : PyErr_Clear();
115 [ - + ]: 1 : PyErr_SetString(po_DNSMessageFORMERR, dmfe.what());
116 : : return (-1);
117 : 2 : } catch (const IncompleteRRClass& irc) {
118 [ - + ]: 1 : PyErr_Clear();
119 [ - + ]: 1 : PyErr_SetString(po_IncompleteRRClass, irc.what());
120 : : return (-1);
121 [ - + + - ]: 2 : } catch (const IncompleteRRType& irt) {
122 [ # # ]: 0 : PyErr_Clear();
123 [ # # ]: 0 : PyErr_SetString(po_IncompleteRRType, irt.what());
124 : : return (-1);
125 : : }
126 : :
127 [ + - ]: 1 : self->cppobj = QuestionPtr();
128 : :
129 : 1 : PyErr_Clear();
130 : : PyErr_SetString(PyExc_TypeError,
131 : 1 : "no valid type in constructor argument");
132 : 322 : return (-1);
133 : : }
134 : :
135 : : static void
136 : 427 : Question_destroy(s_Question* self) {
137 : 427 : self->cppobj.reset();
138 : 427 : Py_TYPE(self)->tp_free(self);
139 : 427 : }
140 : :
141 : : static PyObject*
142 : 99 : Question_getName(s_Question* self) {
143 : : try {
144 [ + - ]: 99 : return (createNameObject(self->cppobj->getName()));
145 : 0 : } catch (const exception& ex) {
146 : : const string ex_what =
147 : : "Unexpected failure getting question Name: " +
148 [ # # ][ # # ]: 0 : string(ex.what());
149 [ # # ]: 0 : PyErr_SetString(po_IscException, ex_what.c_str());
150 [ # # ]: 0 : } catch (...) {
151 : : PyErr_SetString(PyExc_SystemError,
152 [ # # ]: 0 : "Unexpected failure getting question Name");
153 : : }
154 : : return (NULL);
155 : : }
156 : :
157 : : static PyObject*
158 : 71 : Question_getType(s_Question* self) {
159 : : try {
160 [ + - ]: 71 : return (createRRTypeObject(self->cppobj->getType()));
161 : 0 : } catch (const exception& ex) {
162 : : const string ex_what =
163 : : "Unexpected failure getting question RRType: " +
164 [ # # ][ # # ]: 0 : string(ex.what());
165 [ # # ]: 0 : PyErr_SetString(po_IscException, ex_what.c_str());
166 [ # # ]: 0 : } catch (...) {
167 : : PyErr_SetString(PyExc_SystemError,
168 [ # # ]: 0 : "Unexpected failure getting question RRType");
169 : : }
170 : : return (NULL);
171 : : }
172 : :
173 : : static PyObject*
174 : 76 : Question_getClass(s_Question* self) {
175 : : try {
176 [ + - ]: 76 : return (createRRClassObject(self->cppobj->getClass()));
177 : 0 : } catch (const exception& ex) {
178 : : const string ex_what =
179 : : "Unexpected failure getting question RRClass: " +
180 [ # # ][ # # ]: 0 : string(ex.what());
181 [ # # ]: 0 : PyErr_SetString(po_IscException, ex_what.c_str());
182 [ # # ]: 0 : } catch (...) {
183 : : PyErr_SetString(PyExc_SystemError,
184 [ # # ]: 0 : "Unexpected failure getting question RRClass");
185 : : }
186 : : return (NULL);
187 : : }
188 : :
189 : : static PyObject*
190 : 8 : Question_toText(s_Question* self) {
191 : : // Py_BuildValue makes python objects from native data
192 [ + - ]: 8 : return (Py_BuildValue("s", self->cppobj->toText().c_str()));
193 : : }
194 : :
195 : : static PyObject*
196 : 6 : Question_str(PyObject* self) {
197 : : // Simply call the to_text method we already defined
198 : : return (PyObject_CallMethod(self,
199 : : const_cast<char*>("to_text"),
200 : 6 : const_cast<char*>("")));
201 : : }
202 : :
203 : : static PyObject*
204 : 6 : Question_toWire(s_Question* self, PyObject* args) {
205 : : PyObject* bytes;
206 : : PyObject* mr;
207 : :
208 [ + - ][ + + ]: 6 : if (PyArg_ParseTuple(args, "O", &bytes) && PySequence_Check(bytes)) {
[ + + ]
209 : 2 : PyObject* bytes_o = bytes;
210 : :
211 : : // Max length is Name::MAX_WIRE + rrclass (2) + rrtype (2)
212 : 2 : OutputBuffer buffer(Name::MAX_WIRE + 4);
213 [ + - ]: 2 : self->cppobj->toWire(buffer);
214 : 2 : PyObject* n = PyBytes_FromStringAndSize(static_cast<const char*>(buffer.getData()),
215 [ + - ]: 2 : buffer.getLength());
216 [ + - ]: 2 : PyObject* result = PySequence_InPlaceConcat(bytes_o, n);
217 : : // We need to release the object we temporarily created here
218 : : // to prevent memory leak
219 [ + + ][ + - ]: 2 : Py_DECREF(n);
220 : : return (result);
221 [ + + ]: 4 : } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) {
222 : 3 : self->cppobj->toWire(PyMessageRenderer_ToMessageRenderer(mr));
223 : : // If we return NULL it is seen as an error, so use this for
224 : : // None returns
225 : 3 : Py_RETURN_NONE;
226 : : }
227 : 1 : PyErr_Clear();
228 : : PyErr_SetString(PyExc_TypeError,
229 : 1 : "toWire argument must be a sequence object or a MessageRenderer");
230 : 6 : return (NULL);
231 : : }
232 : :
233 : : } // end of unnamed namespace
234 : :
235 : : namespace isc {
236 : : namespace dns {
237 : : namespace python {
238 : :
239 : : // This defines the complete type for reflection in python and
240 : : // parsing of PyObject* to s_Question
241 : : // Most of the functions are not actually implemented and NULL here.
242 : : PyTypeObject question_type = {
243 : : PyVarObject_HEAD_INIT(NULL, 0)
244 : : "pydnspp.Question",
245 : : sizeof(s_Question), // tp_basicsize
246 : : 0, // tp_itemsize
247 : : (destructor)Question_destroy, // tp_dealloc
248 : : NULL, // tp_print
249 : : NULL, // tp_getattr
250 : : NULL, // tp_setattr
251 : : NULL, // tp_reserved
252 : : NULL, // tp_repr
253 : : NULL, // tp_as_number
254 : : NULL, // tp_as_sequence
255 : : NULL, // tp_as_mapping
256 : : NULL, // tp_hash
257 : : NULL, // tp_call
258 : : Question_str, // tp_str
259 : : NULL, // tp_getattro
260 : : NULL, // tp_setattro
261 : : NULL, // tp_as_buffer
262 : : Py_TPFLAGS_DEFAULT, // tp_flags
263 : : "The Question class encapsulates the common search key of DNS"
264 : : "lookup, consisting of owner name, RR type and RR class.",
265 : : NULL, // tp_traverse
266 : : NULL, // tp_clear
267 : : NULL, // tp_richcompare
268 : : 0, // tp_weaklistoffset
269 : : NULL, // tp_iter
270 : : NULL, // tp_iternext
271 : : Question_methods, // tp_methods
272 : : NULL, // tp_members
273 : : NULL, // tp_getset
274 : : NULL, // tp_base
275 : : NULL, // tp_dict
276 : : NULL, // tp_descr_get
277 : : NULL, // tp_descr_set
278 : : 0, // tp_dictoffset
279 : : (initproc)Question_init, // tp_init
280 : : NULL, // tp_alloc
281 : : PyType_GenericNew, // tp_new
282 : : NULL, // tp_free
283 : : NULL, // tp_is_gc
284 : : NULL, // tp_bases
285 : : NULL, // tp_mro
286 : : NULL, // tp_cache
287 : : NULL, // tp_subclasses
288 : : NULL, // tp_weaklist
289 : : NULL, // tp_del
290 : : 0 // tp_version_tag
291 : : };
292 : :
293 : : PyObject*
294 : 106 : createQuestionObject(const Question& source) {
295 : : s_Question* question =
296 : 106 : static_cast<s_Question*>(question_type.tp_alloc(&question_type, 0));
297 [ + - ]: 212 : question->cppobj = QuestionPtr(new Question(source));
298 : 106 : return (question);
299 : : }
300 : :
301 : : bool
302 : 0 : PyQuestion_Check(PyObject* obj) {
303 [ # # ]: 0 : if (obj == NULL) {
304 [ # # ]: 0 : isc_throw(PyCPPWrapperException, "obj argument NULL in typecheck");
305 : : }
306 [ # # ][ # # ]: 0 : return (PyObject_TypeCheck(obj, &question_type));
307 : : }
308 : :
309 : : const Question&
310 : 371 : PyQuestion_ToQuestion(const PyObject* question_obj) {
311 [ - + ]: 371 : if (question_obj == NULL) {
312 [ # # ]: 0 : isc_throw(PyCPPWrapperException,
313 : : "obj argument NULL in Question PyObject conversion");
314 : : }
315 : 371 : const s_Question* question = static_cast<const s_Question*>(question_obj);
316 : 371 : return (*question->cppobj);
317 : : }
318 : :
319 : : } // end python namespace
320 : : } // end dns namespace
321 : 123 : } // end isc namespace
|