question_python.cc 11 KB

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