rrset_python.cc 17 KB

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