rrset_python.cc 14 KB

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