rrset_python.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. // Copyright (C) 2009 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. // $Id$
  15. #include <dns/rrset.h>
  16. //
  17. // Declaration of the custom exceptions
  18. // Initialization and addition of these go in the module init at the
  19. // end
  20. //
  21. static PyObject* po_EmptyRRset;
  22. // This one's for our 'general' exception
  23. // TODO: does this need an according general class?
  24. // should we replace it with some more direct base exception?
  25. //
  26. // Declaration of enums
  27. // Initialization and addition of these go in the module init at the
  28. // end
  29. //
  30. //
  31. // Declaration of class constants
  32. // Initialization and addition of these go in the module init at the
  33. // end
  34. //
  35. //
  36. // Definition of the classes
  37. //
  38. // For each class, we need a struct, a helper functions (init, destroy,
  39. // and static wrappers around the methods we export), a list of methods,
  40. // and a type description
  41. using namespace isc::dns;
  42. // RRset
  43. typedef struct {
  44. PyObject_HEAD
  45. RRsetPtr rrset;
  46. } s_RRset;
  47. static int RRset_init(s_RRset* self, PyObject* args);
  48. static void RRset_destroy(s_RRset* self);
  49. static PyObject* RRset_getRdataCount(s_RRset* self);
  50. static PyObject* RRset_getName(s_RRset* self);
  51. static PyObject* RRset_getClass(s_RRset* self);
  52. static PyObject* RRset_getType(s_RRset* self);
  53. static PyObject* RRset_getTTL(s_RRset* self);
  54. static PyObject* RRset_setName(s_RRset* self, PyObject* args);
  55. static PyObject* RRset_setTTL(s_RRset* self, PyObject* args);
  56. static PyObject* RRset_toText(s_RRset* self);
  57. static PyObject* RRset_str(PyObject* self);
  58. static PyObject* RRset_toWire(s_RRset* self, PyObject* args);
  59. static PyObject* RRset_addRdata(s_RRset* self, PyObject* args);
  60. static PyObject* RRset_getRdata(s_RRset* self);
  61. // TODO: iterator?
  62. static PyMethodDef RRset_methods[] = {
  63. { "get_rdata_count", (PyCFunction)RRset_getRdataCount, METH_NOARGS,
  64. "Returns the number of rdata fields." },
  65. { "get_name", (PyCFunction)RRset_getName, METH_NOARGS,
  66. "Returns the name of the RRset, as a Name object." },
  67. { "get_class", (PyCFunction)RRset_getClass, METH_NOARGS,
  68. "Returns the class of the RRset as an RRClass object." },
  69. { "get_type", (PyCFunction)RRset_getType, METH_NOARGS,
  70. "Returns the type of the RRset as an RRType object." },
  71. { "get_ttl", (PyCFunction)RRset_getTTL, METH_NOARGS,
  72. "Returns the TTL of the RRset as an RRTTL object." },
  73. { "set_name", (PyCFunction)RRset_setName, METH_VARARGS,
  74. "Sets the name of the RRset.\nTakes a Name object as an argument." },
  75. { "set_ttl", (PyCFunction)RRset_setTTL, METH_VARARGS,
  76. "Sets the TTL of the RRset.\nTakes an RRTTL object as an argument." },
  77. { "to_text", (PyCFunction)RRset_toText, METH_NOARGS,
  78. "Returns the text representation of the RRset as a string" },
  79. { "to_wire", (PyCFunction)RRset_toWire, METH_VARARGS,
  80. "Converts the RRset object to wire format.\n"
  81. "The argument can be either a MessageRenderer or an object that "
  82. "implements the sequence interface. If the object is mutable "
  83. "(for instance a bytearray()), the wire data is added in-place.\n"
  84. "If it is not (for instance a bytes() object), a new object is "
  85. "returned" },
  86. { "add_rdata", (PyCFunction)RRset_addRdata, METH_VARARGS,
  87. "Adds the rdata for one RR to the RRset.\nTakes an Rdata object as an argument" },
  88. { "get_rdata", (PyCFunction)RRset_getRdata, METH_NOARGS,
  89. "Returns a List containing all Rdata elements" },
  90. { NULL, NULL, 0, NULL }
  91. };
  92. static PyTypeObject rrset_type = {
  93. PyVarObject_HEAD_INIT(NULL, 0)
  94. "libdns_python.RRset",
  95. sizeof(s_RRset), /* tp_basicsize */
  96. 0, /* tp_itemsize */
  97. (destructor)RRset_destroy, /* tp_dealloc */
  98. NULL, /* tp_print */
  99. NULL, /* tp_getattr */
  100. NULL, /* tp_setattr */
  101. NULL, /* tp_reserved */
  102. NULL, /* tp_repr */
  103. NULL, /* tp_as_number */
  104. NULL, /* tp_as_sequence */
  105. NULL, /* tp_as_mapping */
  106. NULL, /* tp_hash */
  107. NULL, /* tp_call */
  108. RRset_str, /* tp_str */
  109. NULL, /* tp_getattro */
  110. NULL, /* tp_setattro */
  111. NULL, /* tp_as_buffer */
  112. Py_TPFLAGS_DEFAULT, /* tp_flags */
  113. "The AbstractRRset class is an abstract base class that "
  114. "models a DNS RRset.\n\n"
  115. "An object of (a specific derived class of) AbstractRRset "
  116. "models an RRset as described in the DNS standard:\n"
  117. "A set of DNS resource records (RRs) of the same type and class. "
  118. "The standard requires the TTL of all RRs in an RRset be the same; "
  119. "this class follows that requirement.\n\n"
  120. "Note about duplicate RDATA: RFC2181 states that it's meaningless that an "
  121. "RRset contains two identical RRs and that name servers should suppress "
  122. "such duplicates.\n"
  123. "This class is not responsible for ensuring this requirement: For example, "
  124. "addRdata() method doesn't check if there's already RDATA identical "
  125. "to the one being added.\n"
  126. "This is because such checks can be expensive, and it's often easy to "
  127. "ensure the uniqueness requirement at the %data preparation phase "
  128. "(e.g. when loading a zone).",
  129. NULL, /* tp_traverse */
  130. NULL, /* tp_clear */
  131. NULL, /* tp_richcompare */
  132. 0, /* tp_weaklistoffset */
  133. NULL, /* tp_iter */
  134. NULL, /* tp_iternext */
  135. RRset_methods, /* tp_methods */
  136. NULL, /* tp_members */
  137. NULL, /* tp_getset */
  138. NULL, /* tp_base */
  139. NULL, /* tp_dict */
  140. NULL, /* tp_descr_get */
  141. NULL, /* tp_descr_set */
  142. 0, /* tp_dictoffset */
  143. (initproc)RRset_init, /* tp_init */
  144. NULL, /* tp_alloc */
  145. PyType_GenericNew, /* tp_new */
  146. NULL, /* tp_free */
  147. NULL, /* tp_is_gc */
  148. NULL, /* tp_bases */
  149. NULL, /* tp_mro */
  150. NULL, /* tp_cache */
  151. NULL, /* tp_subclasses */
  152. NULL, /* tp_weaklist */
  153. // Note: not sure if the following are correct. Added them just to
  154. // make the compiler happy.
  155. NULL, /* tp_del */
  156. 0 /* tp_version_tag */
  157. };
  158. static int
  159. RRset_init(s_RRset* self, PyObject* args UNUSED_PARAM)
  160. {
  161. s_Name* name;
  162. s_RRClass* rrclass;
  163. s_RRType* rrtype;
  164. s_RRTTL* rrttl;
  165. if (PyArg_ParseTuple(args, "O!O!O!O!", &name_type, &name,
  166. &rrclass_type, &rrclass,
  167. &rrtype_type, &rrtype,
  168. &rrttl_type, &rrttl
  169. )) {
  170. self->rrset = RRsetPtr(new RRset(*name->name, *rrclass->rrclass,
  171. *rrtype->rrtype, *rrttl->rrttl));
  172. return 0;
  173. }
  174. self->rrset = RRsetPtr();
  175. return -1;
  176. }
  177. static void
  178. RRset_destroy(s_RRset* self)
  179. {
  180. // Clear the shared_ptr so that its reference count is zero
  181. // before we call tp_free() (there is no direct release())
  182. self->rrset.reset();
  183. Py_TYPE(self)->tp_free(self);
  184. }
  185. static PyObject*
  186. RRset_getRdataCount(s_RRset* self)
  187. {
  188. return Py_BuildValue("I", self->rrset->getRdataCount());
  189. }
  190. static PyObject*
  191. RRset_getName(s_RRset* self)
  192. {
  193. s_Name* name;
  194. // is this the best way to do this?
  195. name = (s_Name*)name_type.tp_alloc(&name_type, 0);
  196. if (name != NULL) {
  197. name->name = new Name(self->rrset->getName());
  198. if (name->name == NULL)
  199. {
  200. Py_DECREF(name);
  201. return NULL;
  202. }
  203. }
  204. return (PyObject*)name;
  205. }
  206. static PyObject*
  207. RRset_getClass(s_RRset* self)
  208. {
  209. s_RRClass* rrclass;
  210. rrclass = (s_RRClass*)rrclass_type.tp_alloc(&rrclass_type, 0);
  211. if (rrclass != NULL) {
  212. rrclass->rrclass = new RRClass(self->rrset->getClass());
  213. if (rrclass->rrclass == NULL)
  214. {
  215. Py_DECREF(rrclass);
  216. return NULL;
  217. }
  218. }
  219. return (PyObject*)rrclass;
  220. }
  221. static PyObject*
  222. RRset_getType(s_RRset* self)
  223. {
  224. s_RRType* rrtype;
  225. rrtype = (s_RRType*)rrtype_type.tp_alloc(&rrtype_type, 0);
  226. if (rrtype != NULL) {
  227. rrtype->rrtype = new RRType(self->rrset->getType());
  228. if (rrtype->rrtype == NULL)
  229. {
  230. Py_DECREF(rrtype);
  231. return NULL;
  232. }
  233. }
  234. return (PyObject*)rrtype;
  235. }
  236. static PyObject*
  237. RRset_getTTL(s_RRset* self)
  238. {
  239. s_RRTTL* rrttl;
  240. rrttl = (s_RRTTL*)rrttl_type.tp_alloc(&rrttl_type, 0);
  241. if (rrttl != NULL) {
  242. rrttl->rrttl = new RRTTL(self->rrset->getTTL());
  243. if (rrttl->rrttl == NULL)
  244. {
  245. Py_DECREF(rrttl);
  246. return NULL;
  247. }
  248. }
  249. return (PyObject*)rrttl;
  250. }
  251. static PyObject*
  252. RRset_setName(s_RRset* self, PyObject* args)
  253. {
  254. s_Name* name;
  255. if (!PyArg_ParseTuple(args, "O!", &name_type, &name)) {
  256. return NULL;
  257. }
  258. self->rrset->setName(*name->name);
  259. Py_RETURN_NONE;
  260. }
  261. static PyObject*
  262. RRset_setTTL(s_RRset* self, PyObject* args)
  263. {
  264. s_RRTTL* rrttl;
  265. if (!PyArg_ParseTuple(args, "O!", &rrttl_type, &rrttl)) {
  266. return NULL;
  267. }
  268. self->rrset->setTTL(*rrttl->rrttl);
  269. Py_RETURN_NONE;
  270. }
  271. static PyObject*
  272. RRset_toText(s_RRset* self)
  273. {
  274. try {
  275. return Py_BuildValue("s", self->rrset->toText().c_str());
  276. } catch (EmptyRRset ers) {
  277. PyErr_SetString(po_EmptyRRset, ers.what());
  278. return NULL;
  279. }
  280. }
  281. static PyObject*
  282. RRset_str(PyObject* self)
  283. {
  284. // Simply call the to_text method we already defined
  285. return PyObject_CallMethod(self, (char*)"to_text", (char*)"");
  286. }
  287. static PyObject*
  288. RRset_toWire(s_RRset* self, PyObject* args)
  289. {
  290. PyObject* bytes;
  291. s_MessageRenderer* mr;
  292. try {
  293. if (PyArg_ParseTuple(args, "O", &bytes) && PySequence_Check(bytes)) {
  294. PyObject* bytes_o = bytes;
  295. OutputBuffer buffer(4096);
  296. self->rrset->toWire(buffer);
  297. PyObject* n = PyBytes_FromStringAndSize((const char*) buffer.getData(), buffer.getLength());
  298. PyObject* result = PySequence_InPlaceConcat(bytes_o, n);
  299. // We need to release the object we temporarily created here
  300. // to prevent memory leak
  301. Py_DECREF(n);
  302. return result;
  303. } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, (PyObject**) &mr)) {
  304. self->rrset->toWire(*mr->messagerenderer);
  305. // If we return NULL it is seen as an error, so use this for
  306. // None returns
  307. Py_RETURN_NONE;
  308. }
  309. } catch (EmptyRRset ers) {
  310. PyErr_Clear();
  311. PyErr_SetString(po_EmptyRRset, ers.what());
  312. return NULL;
  313. }
  314. PyErr_Clear();
  315. PyErr_SetString(PyExc_TypeError,
  316. "toWire argument must be a sequence object or a MessageRenderer");
  317. return NULL;
  318. }
  319. static PyObject*
  320. RRset_addRdata(s_RRset* self, PyObject* args)
  321. {
  322. s_Rdata* rdata;
  323. if (!PyArg_ParseTuple(args, "O!", &rdata_type, &rdata)) {
  324. return NULL;
  325. }
  326. try {
  327. self->rrset->addRdata(*rdata->rdata);
  328. Py_RETURN_NONE;
  329. } catch (std::bad_cast) {
  330. PyErr_Clear();
  331. PyErr_SetString(PyExc_TypeError,
  332. "Rdata type to add must match type of RRset");
  333. return NULL;
  334. }
  335. }
  336. static PyObject*
  337. RRset_getRdata(s_RRset* self)
  338. {
  339. PyObject* list = PyList_New(0);
  340. RdataIteratorPtr it = self->rrset->getRdataIterator();
  341. for (it->first(); !it->isLast(); it->next()) {
  342. s_Rdata *rds = (s_Rdata*)rdata_type.tp_alloc(&rdata_type, 0);
  343. if (rds != NULL) {
  344. // hmz them iterators/shared_ptrs and private constructors
  345. // make this a bit weird, so we create a new one with
  346. // the data available
  347. const Rdata *rd = &it->getCurrent();
  348. rds->rdata = createRdata(self->rrset->getType(), self->rrset->getClass(), *rd);
  349. PyList_Append(list, (PyObject*) rds);
  350. } else {
  351. return NULL;
  352. }
  353. }
  354. return list;
  355. }
  356. // end of RRset
  357. // Module Initialization, all statics are initialized here
  358. bool
  359. initModulePart_RRset(PyObject* mod)
  360. {
  361. // Add the exceptions to the module
  362. po_EmptyRRset = PyErr_NewException("libdns_python.EmptyRRset", NULL, NULL);
  363. PyModule_AddObject(mod, "EmptyRRset", po_EmptyRRset);
  364. // Add the enums to the module
  365. // Add the constants to the module
  366. // Add the classes to the module
  367. // We initialize the static description object with PyType_Ready(),
  368. // then add it to the module
  369. // NameComparisonResult
  370. if (PyType_Ready(&rrset_type) < 0) {
  371. return false;
  372. }
  373. Py_INCREF(&rrset_type);
  374. PyModule_AddObject(mod, "RRset",
  375. (PyObject*) &rrset_type);
  376. return true;
  377. }