rrset_python.cc 17 KB

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