// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC") // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above // copyright notice and this permission notice appear in all copies. // // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. // // Declaration of the custom exceptions // Initialization and addition of these go in the module init at the // end // static PyObject* po_EmptyLabel; static PyObject* po_TooLongName; static PyObject* po_TooLongLabel; static PyObject* po_BadLabelType; static PyObject* po_BadEscape; static PyObject* po_IncompleteName; static PyObject* po_InvalidBufferPosition; static PyObject* po_DNSMessageFORMERR; // // Declaration of enums // Initialization and addition of these go in the module init at the // end // static PyObject* po_NameRelation; // // Definition of the classes // // For each class, we need a struct, a helper functions (init, destroy, // and static wrappers around the methods we export), a list of methods, // and a type description using namespace isc::dns; // NameComparisonResult class s_NameComparisonResult : public PyObject { public: isc::dns::NameComparisonResult* ncr; }; static int NameComparisonResult_init(s_NameComparisonResult*, PyObject*); static void NameComparisonResult_destroy(s_NameComparisonResult* self); static PyObject* NameComparisonResult_getOrder(s_NameComparisonResult* self); static PyObject* NameComparisonResult_getCommonLabels(s_NameComparisonResult* self); static PyObject* NameComparisonResult_getRelation(s_NameComparisonResult* self); static PyMethodDef NameComparisonResult_methods[] = { { "get_order", reinterpret_cast(NameComparisonResult_getOrder), METH_NOARGS, "Returns the order" }, { "get_common_labels", reinterpret_cast(NameComparisonResult_getCommonLabels), METH_NOARGS, "Returns the number of common labels" }, { "get_relation", reinterpret_cast(NameComparisonResult_getRelation), METH_NOARGS, "Returns the relation" }, { NULL, NULL, 0, NULL } }; static PyTypeObject name_comparison_result_type = { PyVarObject_HEAD_INIT(NULL, 0) "pydnspp.NameComparisonResult", sizeof(s_NameComparisonResult), // tp_basicsize 0, // tp_itemsize (destructor)NameComparisonResult_destroy, // tp_dealloc NULL, // tp_print NULL, // tp_getattr NULL, // tp_setattr NULL, // tp_reserved NULL, // tp_repr NULL, // tp_as_number NULL, // tp_as_sequence NULL, // tp_as_mapping NULL, // tp_hash NULL, // tp_call NULL, // tp_str NULL, // tp_getattro NULL, // tp_setattro NULL, // tp_as_buffer Py_TPFLAGS_DEFAULT, // tp_flags "This is a supplemental class used only as a return value of Name.compare(). " "It encapsulate a tuple of the comparison: ordering, number of common labels, " "and relationship as follows:\n" "- ordering: relative ordering under the DNSSEC order relation\n" "- labels: the number of common significant labels of the two names being" " compared\n" "- relationship: see NameComparisonResult.NameRelation\n", NULL, // tp_traverse NULL, // tp_clear NULL, // tp_richcompare 0, // tp_weaklistoffset NULL, // tp_iter NULL, // tp_iternext NameComparisonResult_methods, // tp_methods NULL, // tp_members NULL, // tp_getset NULL, // tp_base NULL, // tp_dict NULL, // tp_descr_get NULL, // tp_descr_set 0, // tp_dictoffset (initproc)NameComparisonResult_init, // tp_init NULL, // tp_alloc PyType_GenericNew, // tp_new NULL, // tp_free NULL, // tp_is_gc NULL, // tp_bases NULL, // tp_mro NULL, // tp_cache NULL, // tp_subclasses NULL, // tp_weaklist NULL, // tp_del 0 // tp_version_tag }; static int NameComparisonResult_init(s_NameComparisonResult*, PyObject*) { PyErr_SetString(PyExc_NotImplementedError, "NameComparisonResult can't be built directly"); return (-1); } static void NameComparisonResult_destroy(s_NameComparisonResult* self) { delete self->ncr; self->ncr = NULL; Py_TYPE(self)->tp_free(self); } static PyObject* NameComparisonResult_getOrder(s_NameComparisonResult* self) { return (Py_BuildValue("i", self->ncr->getOrder())); } static PyObject* NameComparisonResult_getCommonLabels(s_NameComparisonResult* self) { return (Py_BuildValue("I", self->ncr->getCommonLabels())); } static PyObject* NameComparisonResult_getRelation(s_NameComparisonResult* self) { return (Py_BuildValue("I", self->ncr->getRelation())); } // end of NameComparisonResult // Name class s_Name : public PyObject { public: isc::dns::Name* name; size_t position; }; static int Name_init(s_Name* self, PyObject* args); static void Name_destroy(s_Name* self); static PyObject* Name_toWire(s_Name* self, PyObject* args); static PyObject* Name_toText(s_Name* self); static PyObject* Name_str(PyObject* self); static PyObject* Name_getLabelCount(s_Name* self); static PyObject* Name_at(s_Name* self, PyObject* args); static PyObject* Name_getLength(s_Name* self); static PyObject* Name_compare(s_Name* self, PyObject* args); static PyObject* Name_equals(s_Name* self, PyObject* args); static PyObject* Name_richcmp(s_Name* self, s_Name* other, int op); static PyObject* Name_split(s_Name* self, PyObject* args); static PyObject* Name_reverse(s_Name* self); static PyObject* Name_concatenate(s_Name* self, PyObject* args); static PyObject* Name_downcase(s_Name* self); static PyObject* Name_isWildCard(s_Name* self); static PyMethodDef Name_methods[] = { { "at", reinterpret_cast(Name_at), METH_VARARGS, "Returns the integer value of the name data at the specified position" }, { "get_length", reinterpret_cast(Name_getLength), METH_NOARGS, "Returns the length" }, { "get_labelcount", reinterpret_cast(Name_getLabelCount), METH_NOARGS, "Returns the number of labels" }, { "to_text", reinterpret_cast(Name_toText), METH_NOARGS, "Returns the string representation" }, { "to_wire", reinterpret_cast(Name_toWire), METH_VARARGS, "Converts the Name object to wire format.\n" "The argument can be either a MessageRenderer or an object that " "implements the sequence interface. If the object is mutable " "(for instance a bytearray()), the wire data is added in-place.\n" "If it is not (for instance a bytes() object), a new object is " "returned" }, { "compare", reinterpret_cast(Name_compare), METH_VARARGS, "Returns a NameComparisonResult object. The argument must be another Name object" }, { "equals", reinterpret_cast(Name_equals), METH_VARARGS, "Returns true if the given Name object is equal to this one" }, { "split", reinterpret_cast(Name_split), METH_VARARGS, "Splits the name, takes two arguments, the first is an integer " "specifying the first label to place in the result. The second " "is an integer specifying the number of labels to put in the " "result. Returns a new Name object" }, { "reverse", reinterpret_cast(Name_reverse), METH_NOARGS, "Returns a new Name object that is the reverse of this one" }, { "concatenate", reinterpret_cast(Name_concatenate), METH_VARARGS, "Concatenates the given Name object to this one and returns the " "result as a new Name object" }, { "downcase", reinterpret_cast(Name_downcase), METH_NOARGS, "Downcases this name object (in-place). Returns a new reference to the Name." }, { "is_wildcard", reinterpret_cast(Name_isWildCard), METH_NOARGS, "Returns True if the Name object represents a wildcard name." }, { NULL, NULL, 0, NULL } }; static PyTypeObject name_type = { PyVarObject_HEAD_INIT(NULL, 0) "pydnspp.Name", sizeof(s_Name), // tp_basicsize 0, // tp_itemsize (destructor)Name_destroy, // tp_dealloc NULL, // tp_print NULL, // tp_getattr NULL, // tp_setattr NULL, // tp_reserved NULL, // tp_repr NULL, // tp_as_number NULL, // tp_as_sequence NULL, // tp_as_mapping NULL, // tp_hash NULL, // tp_call Name_str, // tp_str NULL, // tp_getattro NULL, // tp_setattro NULL, // tp_as_buffer Py_TPFLAGS_DEFAULT, // tp_flags "The Name class encapsulates DNS names.\n" "It provides interfaces to construct a name from string or wire-format data, " "transform a name into a string or wire-format data, compare two names, get " "access to various properties of a name, etc.", NULL, // tp_traverse NULL, // tp_clear (richcmpfunc)Name_richcmp, // tp_richcompare 0, // tp_weaklistoffset NULL, // tp_iter NULL, // tp_iternext Name_methods, // tp_methods NULL, // tp_members NULL, // tp_getset NULL, // tp_base NULL, // tp_dict NULL, // tp_descr_get NULL, // tp_descr_set 0, // tp_dictoffset (initproc)Name_init, // tp_init NULL, // tp_alloc PyType_GenericNew, // tp_new NULL, // tp_free NULL, // tp_is_gc NULL, // tp_bases NULL, // tp_mro NULL, // tp_cache NULL, // tp_subclasses NULL, // tp_weaklist // Note: not sure if the following are correct. Added them just to // make the compiler happy. NULL, // tp_del 0 // tp_version_tag }; static int Name_init(s_Name* self, PyObject* args) { const char* s; PyObject* downcase = Py_False; // Depending on the arguments in *args, we decide which of the // constructors is meant. If the first argument is of type string, // we use the string-based constructor. If it is a bytes object, // we use the wire-based one. if (PyArg_ParseTuple(args, "s|O!", &s, &PyBool_Type, &downcase)) { try { const std::string n(s); self->name = new Name(n, downcase == Py_True); self->position = 0; } catch (const EmptyLabel&) { PyErr_SetString(po_EmptyLabel, "EmptyLabel"); return (-1); } catch (const TooLongLabel&) { PyErr_SetString(po_TooLongLabel, "TooLongLabel"); return (-1); } catch (const BadLabelType&) { PyErr_SetString(po_BadLabelType, "BadLabelType"); return (-1); } catch (const BadEscape&) { PyErr_SetString(po_BadEscape, "BadEscape"); return (-1); } catch (const TooLongName&) { PyErr_SetString(po_TooLongName, "TooLongName"); return (-1); } catch (const IncompleteName&) { PyErr_SetString(po_IncompleteName, "IncompleteName"); return (-1); #ifdef CATCHMEMERR } catch (const std::bad_alloc&) { PyErr_NoMemory(); return (-1); #endif } catch (...) { PyErr_SetString(po_IscException, "Unexpected?!"); return (-1); } return (0); } PyErr_Clear(); PyObject* bytes_obj; const char* bytes; Py_ssize_t len; long position = 0; // It was not a string (see comment above), so try bytes, and // create with buffer object if (PyArg_ParseTuple(args, "O|lO!", &bytes_obj, &position, &PyBool_Type, &downcase) && PyObject_AsCharBuffer(bytes_obj, &bytes, &len) != -1) { try { if (position < 0) { // Throw IndexError here since name index should be unsigned PyErr_SetString(PyExc_IndexError, "Name index shouldn't be negative"); return (-1); } InputBuffer buffer(bytes, len); buffer.setPosition(position); self->name = new Name(buffer, downcase == Py_True); self->position = buffer.getPosition(); } catch (const InvalidBufferPosition&) { PyErr_SetString(po_InvalidBufferPosition, "InvalidBufferPosition"); return (-1); } catch (const DNSMessageFORMERR&) { PyErr_SetString(po_DNSMessageFORMERR, "DNSMessageFORMERR"); return (-1); } catch (...) { PyErr_SetString(po_IscException, "Unexpected?!"); return (-1); } return (0); } PyErr_Clear(); PyErr_SetString(PyExc_TypeError, "No valid types in Name constructor (should be string or sequence and offset"); return (-1); } static void Name_destroy(s_Name* self) { delete self->name; self->name = NULL; Py_TYPE(self)->tp_free(self); } static PyObject* Name_at(s_Name* self, PyObject* args) { int pos; if (!PyArg_ParseTuple(args, "i", &pos)) { return (NULL); } if (pos < 0) { // Throw IndexError here since name index should be unsigned PyErr_SetString(PyExc_IndexError, "name index shouldn't be negative"); return (NULL); } try { return (Py_BuildValue("I", self->name->at(pos))); } catch (const isc::OutOfRange&) { PyErr_SetString(PyExc_IndexError, "name index out of range"); return (NULL); } } static PyObject* Name_getLength(s_Name* self) { return (Py_BuildValue("i", self->name->getLength())); } static PyObject* Name_getLabelCount(s_Name* self) { return (Py_BuildValue("i", self->name->getLabelCount())); } static PyObject* Name_toText(s_Name* self) { return (Py_BuildValue("s", self->name->toText().c_str())); } static PyObject* Name_str(PyObject* self) { // Simply call the to_text method we already defined // str() is not defined in the c++ version, only to_text // and we already have a wrapper for that one. return (PyObject_CallMethod(self, const_cast("to_text"), const_cast(""))); } static PyObject* Name_toWire(s_Name* self, PyObject* args) { PyObject* bytes; s_MessageRenderer* mr; if (PyArg_ParseTuple(args, "O", &bytes) && PySequence_Check(bytes)) { PyObject* bytes_o = bytes; OutputBuffer buffer(Name::MAX_WIRE); self->name->toWire(buffer); PyObject* name_bytes = PyBytes_FromStringAndSize(static_cast(buffer.getData()), buffer.getLength()); PyObject* result = PySequence_InPlaceConcat(bytes_o, name_bytes); // We need to release the object we temporarily created here // to prevent memory leak Py_DECREF(name_bytes); return (result); } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) { self->name->toWire(*mr->messagerenderer); // If we return NULL it is seen as an error, so use this for // None returns Py_RETURN_NONE; } PyErr_Clear(); PyErr_SetString(PyExc_TypeError, "toWire argument must be a sequence object or a MessageRenderer"); return (NULL); } static PyObject* Name_compare(s_Name* self, PyObject* args) { s_Name* other; if (!PyArg_ParseTuple(args, "O!", &name_type, &other)) return (NULL); s_NameComparisonResult* ret = PyObject_New(s_NameComparisonResult, &name_comparison_result_type); if (ret != NULL) { ret->ncr = new NameComparisonResult( self->name->compare(*other->name)); } return (ret); } static PyObject* Name_equals(s_Name* self, PyObject* args) { s_Name* other; if (!PyArg_ParseTuple(args, "O!", &name_type, &other)) return (NULL); if (self->name->equals(*other->name)) Py_RETURN_TRUE; else Py_RETURN_FALSE; } static PyObject* Name_split(s_Name* self, PyObject* args) { int first, n; s_Name* ret = NULL; if (PyArg_ParseTuple(args, "ii", &first, &n)) { if (first < 0 || n < 0) { // Throw IndexError here since name index should be unsigned PyErr_SetString(PyExc_IndexError, "name index shouldn't be negative"); return (NULL); } ret = PyObject_New(s_Name, &name_type); if (ret != NULL) { ret->name = NULL; try { ret->name = new Name(self->name->split(first, n)); } catch(const isc::OutOfRange& oor) { PyErr_SetString(PyExc_IndexError, oor.what()); ret->name = NULL; } if (ret->name == NULL) { Py_DECREF(ret); return (NULL); } } } else if (PyArg_ParseTuple(args, "i", &n)) { PyErr_Clear(); if (n < 0) { // Throw IndexError here since name index should be unsigned PyErr_SetString(PyExc_IndexError, "name index shouldn't be negative"); return (NULL); } ret = PyObject_New(s_Name, &name_type); if (ret != NULL) { ret->name = NULL; try { ret->name = new Name(self->name->split(n)); } catch(const isc::OutOfRange& oor) { PyErr_SetString(PyExc_IndexError, oor.what()); ret->name = NULL; } if (ret->name == NULL) { Py_DECREF(ret); return (NULL); } } } PyErr_Clear(); PyErr_SetString(PyExc_TypeError, "No valid type in split argument"); return (ret); } #include // // richcmp defines the ==, !=, >, <, >= and <= operators in python // It is translated to a function that gets 3 arguments, an object, // an object to compare to, and an operator. // static PyObject* Name_richcmp(s_Name* self, s_Name* other, int op) { bool c; // Check for null and if the types match. If different type, // simply return False if (!other || (self->ob_type != other->ob_type)) { Py_RETURN_FALSE; } switch (op) { case Py_LT: c = *self->name < *other->name; break; case Py_LE: c = *self->name <= *other->name; break; case Py_EQ: c = *self->name == *other->name; break; case Py_NE: c = *self->name != *other->name; break; case Py_GT: c = *self->name > *other->name; break; case Py_GE: c = *self->name >= *other->name; break; default: PyErr_SetString(PyExc_IndexError, "Unhandled rich comparison operator"); return (NULL); } if (c) { Py_RETURN_TRUE; } else { Py_RETURN_FALSE; } } static PyObject* Name_reverse(s_Name* self) { s_Name* ret = PyObject_New(s_Name, &name_type); if (ret != NULL) { ret->name = new Name(self->name->reverse()); if (ret->name == NULL) { Py_DECREF(ret); return (NULL); } } return (ret); } static PyObject* Name_concatenate(s_Name* self, PyObject* args) { s_Name* other; if (!PyArg_ParseTuple(args, "O!", &name_type, &other)) return (NULL); s_Name* ret = PyObject_New(s_Name, &name_type); if (ret != NULL) { try { ret->name = new Name(self->name->concatenate(*other->name)); } catch (const TooLongName& tln) { PyErr_SetString(po_TooLongName, tln.what()); return (NULL); } } return (ret); } static PyObject* Name_downcase(s_Name* self) { self->name->downcase(); Py_INCREF(self); return (self); } static PyObject* Name_isWildCard(s_Name* self) { if (self->name->isWildcard()) { Py_RETURN_TRUE; } else { Py_RETURN_FALSE; } } // end of Name // Module Initialization, all statics are initialized here bool initModulePart_Name(PyObject* mod) { // Add the classes to the module // We initialize the static description object with PyType_Ready(), // then add it to the module // // NameComparisonResult // if (PyType_Ready(&name_comparison_result_type) < 0) { return (false); } Py_INCREF(&name_comparison_result_type); // Add the enums to the module po_NameRelation = Py_BuildValue("{i:s,i:s,i:s,i:s}", NameComparisonResult::SUPERDOMAIN, "SUPERDOMAIN", NameComparisonResult::SUBDOMAIN, "SUBDOMAIN", NameComparisonResult::EQUAL, "EQUAL", NameComparisonResult::COMMONANCESTOR, "COMMONANCESTOR"); addClassVariable(name_comparison_result_type, "NameRelation", po_NameRelation); PyModule_AddObject(mod, "NameComparisonResult", reinterpret_cast(&name_comparison_result_type)); // // Name // if (PyType_Ready(&name_type) < 0) { return (false); } Py_INCREF(&name_type); // Add the constants to the module addClassVariable(name_type, "MAX_WIRE", Py_BuildValue("I", Name::MAX_WIRE)); addClassVariable(name_type, "MAX_LABELS", Py_BuildValue("I", Name::MAX_LABELS)); addClassVariable(name_type, "MAX_LABELLEN", Py_BuildValue("I", Name::MAX_LABELLEN)); addClassVariable(name_type, "MAX_COMPRESS_POINTER", Py_BuildValue("I", Name::MAX_COMPRESS_POINTER)); addClassVariable(name_type, "COMPRESS_POINTER_MARK8", Py_BuildValue("I", Name::COMPRESS_POINTER_MARK8)); addClassVariable(name_type, "COMPRESS_POINTER_MARK16", Py_BuildValue("I", Name::COMPRESS_POINTER_MARK16)); s_Name* root_name = PyObject_New(s_Name, &name_type); root_name->name = new Name(Name::ROOT_NAME()); PyObject* po_ROOT_NAME = root_name; addClassVariable(name_type, "ROOT_NAME", po_ROOT_NAME); PyModule_AddObject(mod, "Name", reinterpret_cast(&name_type)); // Add the exceptions to the module po_EmptyLabel = PyErr_NewException("pydnspp.EmptyLabel", NULL, NULL); PyModule_AddObject(mod, "EmptyLabel", po_EmptyLabel); po_TooLongName = PyErr_NewException("pydnspp.TooLongName", NULL, NULL); PyModule_AddObject(mod, "TooLongName", po_TooLongName); po_TooLongLabel = PyErr_NewException("pydnspp.TooLongLabel", NULL, NULL); PyModule_AddObject(mod, "TooLongLabel", po_TooLongLabel); po_BadLabelType = PyErr_NewException("pydnspp.BadLabelType", NULL, NULL); PyModule_AddObject(mod, "BadLabelType", po_BadLabelType); po_BadEscape = PyErr_NewException("pydnspp.BadEscape", NULL, NULL); PyModule_AddObject(mod, "BadEscape", po_BadEscape); po_IncompleteName = PyErr_NewException("pydnspp.IncompleteName", NULL, NULL); PyModule_AddObject(mod, "IncompleteName", po_IncompleteName); po_InvalidBufferPosition = PyErr_NewException("pydnspp.InvalidBufferPosition", NULL, NULL); PyModule_AddObject(mod, "InvalidBufferPosition", po_InvalidBufferPosition); // This one could have gone into the message_python.cc file, but is // already needed here. po_DNSMessageFORMERR = PyErr_NewException("pydnspp.DNSMessageFORMERR", NULL, NULL); PyModule_AddObject(mod, "DNSMessageFORMERR", po_DNSMessageFORMERR); return (true); }