Browse Source

[1575] added python binding of NSEC3Hash.

JINMEI Tatuya 13 years ago
parent
commit
08796619c9

+ 2 - 0
src/lib/dns/python/Makefile.am

@@ -7,6 +7,7 @@ AM_CXXFLAGS = $(B10_CXXFLAGS)
 lib_LTLIBRARIES = libpydnspp.la
 libpydnspp_la_SOURCES = pydnspp_common.cc pydnspp_common.h pydnspp_towire.h
 libpydnspp_la_SOURCES += name_python.cc name_python.h
+libpydnspp_la_SOURCES += nsec3hash_python.cc nsec3hash_python.h
 libpydnspp_la_SOURCES += rrset_python.cc rrset_python.h
 libpydnspp_la_SOURCES += rrclass_python.cc rrclass_python.h
 libpydnspp_la_SOURCES += rrtype_python.cc rrtype_python.h
@@ -41,6 +42,7 @@ pydnspp_la_LDFLAGS = $(PYTHON_LDFLAGS)
 
 EXTRA_DIST = tsigerror_python_inc.cc
 EXTRA_DIST += message_python_inc.cc
+EXTRA_DIST += nsec3hash_python_inc.cc
 
 # Python prefers .so, while some OSes (specifically MacOS) use a different
 # suffix for dynamic objects.  -module is necessary to work this around.

+ 215 - 0
src/lib/dns/python/nsec3hash_python.cc

@@ -0,0 +1,215 @@
+// Copyright (C) 2012  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.
+
+// Enable this if you use s# variants with PyArg_ParseTuple(), see
+// http://docs.python.org/py3k/c-api/arg.html#strings-and-buffers
+//#define PY_SSIZE_T_CLEAN
+
+// Python.h needs to be placed at the head of the program file, see:
+// http://docs.python.org/py3k/extending/extending.html#a-simple-example
+#include <Python.h>
+
+#include <string>
+#include <stdexcept>
+
+#include <util/python/pycppwrapper_util.h>
+
+#include <dns/nsec3hash.h>
+#include <dns/rdataclass.h>
+
+#include "pydnspp_common.h"
+#include "name_python.h"
+#include "nsec3hash_python.h"
+#include "rdata_python.h"
+
+using namespace std;
+using namespace isc::util::python;
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+using namespace isc::dns::python;
+
+// Import pydoc text
+#include "nsec3hash_python_inc.cc"
+
+// Trivial constructor.
+s_NSEC3Hash::s_NSEC3Hash() : cppobj(NULL) {
+}
+
+namespace {
+// This is a template of typical code logic of python class initialization
+// with C++ backend.  You'll need to adjust it according to details of the
+// actual C++ class.
+int
+NSEC3Hash_init(PyObject* po_self, PyObject* args, PyObject*) {
+    s_NSEC3Hash* const self = static_cast<s_NSEC3Hash*>(po_self);
+    try {
+        PyObject* po_rdata;
+        if (PyArg_ParseTuple(args, "O", &po_rdata)) {
+            if (!PyRdata_Check(po_rdata)) {
+                PyErr_SetString(PyExc_TypeError,
+                                "function must be given NSEC3HASH Rdata");
+                return (-1);
+            }
+            self->cppobj = new NSEC3Hash(
+                dynamic_cast<const generic::NSEC3PARAM&>(
+                    PyRdata_ToRdata(po_rdata)));
+            return (0);
+        }
+    } catch (const UnknownNSEC3HashAlgorithm& ex) {
+        PyErr_SetString(po_UnknownNSEC3HashAlgorithm, ex.what());
+        return (-1);
+    } catch (const exception& ex) {
+        const string ex_what = "Failed to construct NSEC3Hash object: " +
+            string(ex.what());
+        PyErr_SetString(po_IscException, ex_what.c_str());
+        return (-1);
+    } catch (...) {
+        PyErr_SetString(PyExc_SystemError, "Unexpected C++ exception");
+        return (-1);
+    }
+
+    return (-1);
+}
+
+void
+NSEC3Hash_destroy(PyObject* po_self) {
+    s_NSEC3Hash* self = static_cast<s_NSEC3Hash*>(po_self);
+    delete self->cppobj;
+    self->cppobj = NULL;
+    Py_TYPE(self)->tp_free(self);
+}
+
+PyObject*
+NSEC3Hash_calculate(PyObject* po_self, PyObject* args) {
+    s_NSEC3Hash* const self = static_cast<s_NSEC3Hash*>(po_self);
+
+    try {
+        PyObject* po_name;
+        if (PyArg_ParseTuple(args, "O", &po_name)) {
+            if (!PyName_Check(po_name)) {
+                PyErr_SetString(PyExc_TypeError,
+                                "function must be given Name object");
+                return (NULL);
+            }
+            const string hash =
+                self->cppobj->calculate(PyName_ToName(po_name));
+            return (Py_BuildValue("s", hash.c_str()));
+        }
+    } catch (const exception& ex) {
+        const string ex_what = "Unexpected failure in NSEC3Hash.calculate: " +
+            string(ex.what());
+        PyErr_SetString(po_IscException, ex_what.c_str());
+        return (NULL);
+    } catch (...) {
+        PyErr_SetString(PyExc_SystemError, "Unexpected C++ exception");
+        return (NULL);
+    }
+
+    return (NULL);
+}
+
+// This list contains the actual set of functions we have in
+// python. Each entry has
+// 1. Python method name
+// 2. Our static function here
+// 3. Argument type
+// 4. Documentation
+PyMethodDef NSEC3Hash_methods[] = {
+    { "calculate", NSEC3Hash_calculate, METH_VARARGS, NSEC3Hash_calculate_doc },
+    { NULL, NULL, 0, NULL }
+};
+} // end of unnamed namespace
+
+namespace isc {
+namespace dns {
+namespace python {
+//
+// Declaration of the custom exceptions
+// Initialization and addition of these go in pydnspp.cc
+//
+PyObject* po_UnknownNSEC3HashAlgorithm;
+
+// This defines the complete type for reflection in python and
+// parsing of PyObject* to s_NSEC3Hash
+// Most of the functions are not actually implemented and NULL here.
+PyTypeObject nsec3hash_type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "dns.NSEC3Hash",
+    sizeof(s_NSEC3Hash),                 // tp_basicsize
+    0,                                  // tp_itemsize
+    NSEC3Hash_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
+    NSEC3Hash_doc,
+    NULL,                               // tp_traverse
+    NULL,                               // tp_clear
+    NULL,                               // tp_richcompare
+    0,                                  // tp_weaklistoffset
+    NULL,                               // tp_iter
+    NULL,                               // tp_iternext
+    NSEC3Hash_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
+    NSEC3Hash_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
+};
+
+// Module Initialization, all statics are initialized here
+bool
+initModulePart_NSEC3Hash(PyObject* mod) {
+    // We initialize the static description object with PyType_Ready(),
+    // then add it to the module. This is not just a check! (leaving
+    // this out results in segmentation faults)
+    if (PyType_Ready(&nsec3hash_type) < 0) {
+        return (false);
+    }
+    void* p = &nsec3hash_type;
+    if (PyModule_AddObject(mod, "NSEC3Hash", static_cast<PyObject*>(p)) < 0) {
+        return (false);
+    }
+    Py_INCREF(&nsec3hash_type);
+
+    return (true);
+}
+} // namespace python
+} // namespace dns
+} // namespace isc

+ 47 - 0
src/lib/dns/python/nsec3hash_python.h

@@ -0,0 +1,47 @@
+// Copyright (C) 2012  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.
+
+#ifndef __PYTHON_NSEC3HASH_H
+#define __PYTHON_NSEC3HASH_H 1
+
+#include <Python.h>
+
+namespace isc {
+namespace dns {
+class NSEC3Hash;
+
+namespace python {
+
+// The s_* Class simply covers one instantiation of the object
+class s_NSEC3Hash : public PyObject {
+public:
+    s_NSEC3Hash();
+    NSEC3Hash* cppobj;
+};
+
+extern PyTypeObject nsec3hash_type;
+
+// Public exception object.
+extern PyObject* po_UnknownNSEC3HashAlgorithm;
+
+bool initModulePart_NSEC3Hash(PyObject* mod);
+
+} // namespace python
+} // namespace dns
+} // namespace isc
+#endif // __PYTHON_NSEC3HASH_H
+
+// Local Variables:
+// mode: c++
+// End:

+ 45 - 0
src/lib/dns/python/nsec3hash_python_inc.cc

@@ -0,0 +1,45 @@
+namespace {
+// Modifications
+//   - removed intermediate details note, mainly for brevity
+//   - removed std::bad_alloc
+const char* const NSEC3Hash_doc = "\
+A calculator of NSEC3 hashes.\n\
+\n\
+This is a simple class that encapsulates the algorithm of calculating\n\
+NSEC3 hash values as defined in RFC5155.\n\
+\n\
+NSEC3Hash(param)\n\
+\n\
+    Constructor from NSEC3PARAM RDATA.\n\
+\n\
+    The hash algorithm given via param must be known to the\n\
+    implementation. Otherwise UnknownNSEC3HashAlgorithm exception will\n\
+    be thrown.\n\
+\n\
+    Exceptions:\n\
+      UnknownNSEC3HashAlgorithm The specified algorithm in param is\n\
+                 unknown.\n\
+\n\
+    Parameters:\n\
+      param      NSEC3 parameters used for subsequent calculation.\n\
+\n\
+";
+
+const char* const NSEC3Hash_calculate_doc = "\
+calculate(Name) -> string\n\
+\n\
+Calculate the NSEC3 hash.\n\
+\n\
+This method calculates the NSEC3 hash value for the given name with\n\
+the hash parameters (algorithm, iterations and salt) given at\n\
+construction, and returns the value in a base32hex-encoded string\n\
+(without containing any white spaces). All alphabets in the string\n\
+will be upper cased.\n\
+\n\
+Parameters:\n\
+  name       The domain name for which the hash value is to be\n\
+             calculated.\n\
+\n\
+Return Value(s): Base32hex-encoded string of the hash value.\n\
+";
+} // unnamed namespace

+ 9 - 0
src/lib/dns/python/pydnspp.cc

@@ -39,6 +39,7 @@
 #include "message_python.h"
 #include "messagerenderer_python.h"
 #include "name_python.h"
+#include "nsec3hash_python.h"
 #include "opcode_python.h"
 #include "pydnspp_common.h"
 #include "pydnspp_towire.h"
@@ -164,6 +165,10 @@ initModulePart_Message(PyObject* mod) {
             PyErr_NewException("pydnspp.DNSMessageBADVERS", NULL, NULL);
         PyObjectContainer(po_DNSMessageBADVERS).installToModule(
             mod, "DNSMessageBADVERS");
+        po_UnknownNSEC3HashAlgorithm =
+            PyErr_NewException("pydnspp.UnknownNSEC3HashAlgorithm", NULL, NULL);
+        PyObjectContainer(po_UnknownNSEC3HashAlgorithm).installToModule(
+            mod, "UnknownNSEC3HashAlgorithm");
     } catch (const std::exception& ex) {
         const std::string ex_what =
             "Unexpected failure in Message initialization: " +
@@ -777,6 +782,10 @@ PyInit_pydnspp(void) {
         return (NULL);
     }
 
+    if (!initModulePart_NSEC3Hash(mod)) {
+        return (NULL);
+    }
+
     if (!initModulePart_RRClass(mod)) {
         return (NULL);
     }

+ 1 - 0
src/lib/dns/python/tests/Makefile.am

@@ -3,6 +3,7 @@ PYTESTS = edns_python_test.py
 PYTESTS += message_python_test.py
 PYTESTS += messagerenderer_python_test.py
 PYTESTS += name_python_test.py
+PYTESTS += nsec3hash_python_test.py
 PYTESTS += question_python_test.py
 PYTESTS += opcode_python_test.py
 PYTESTS += rcode_python_test.py

+ 77 - 0
src/lib/dns/python/tests/nsec3hash_python_test.py

@@ -0,0 +1,77 @@
+# Copyright (C) 2012  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and 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 INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM 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.
+
+import unittest
+from pydnspp import *
+
+class NSEC3HashTest(unittest.TestCase):
+    '''These tests are mostly straightforward conversion of C++ tests
+    except for python specific type checks.
+
+    '''
+
+    def setUp(self):
+        self.test_hash = NSEC3Hash(Rdata(RRType.NSEC3PARAM(), RRClass.IN(),
+                                         "1 0 12 aabbccdd"))
+
+    def test_bad_construct(self):
+        # missing parameter
+        self.assertRaises(TypeError, NSEC3Hash)
+
+        # invalid type of argument
+        self.assertRaises(TypeError, NSEC3Hash, "1 0 12 aabbccdd")
+
+        # additional parameter
+        self.assertRaises(TypeError, NSEC3Hash, Rdata(RRType.NSEC3PARAM(),
+                                                      RRClass.IN(),
+                                                      "1 0 12 aabbccdd"), 1)
+
+    def test_unknown_algorithm(self):
+        self.assertRaises(UnknownNSEC3HashAlgorithm, NSEC3Hash,
+                          Rdata(RRType.NSEC3PARAM(), RRClass.IN(),
+                                "2 0 12 aabbccdd"))
+
+    def test_calculate(self):
+        # A couple of normal cases from the RFC5155 example.
+        self.assertEqual("0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM",
+                         self.test_hash.calculate(Name("example")))
+        self.assertEqual("35MTHGPGCU1QG68FAB165KLNSNK3DPVL",
+                         self.test_hash.calculate(Name("a.example")))
+
+        # Check case-insensitiveness
+        self.assertEqual("0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM",
+                         self.test_hash.calculate(Name("EXAMPLE")))
+
+        # Some boundary cases: 0-iteration and empty salt.  Borrowed from the
+        # .com zone data.
+        self.test_hash = NSEC3Hash(Rdata(RRType.NSEC3PARAM(),
+                                         RRClass.IN(),"1 0 0 -"))
+        self.assertEqual("CK0POJMG874LJREF7EFN8430QVIT8BSM",
+                         self.test_hash.calculate(Name("com")))
+
+        # Using unusually large iterations, something larger than the 8-bit
+        #range.  (expected hash value generated by BIND 9's dnssec-signzone)
+        self.test_hash = NSEC3Hash(Rdata(RRType.NSEC3PARAM(),
+                                         RRClass.IN(), "1 0 256 AABBCCDD"))
+        self.assertEqual("COG6A52MJ96MNMV3QUCAGGCO0RHCC2Q3",
+                         self.test_hash.calculate(Name("example.org")))
+
+    def test_calculate_badparam(self):
+        self.assertRaises(TypeError, self.test_hash.calculate, "example")
+        self.assertRaises(TypeError, self.test_hash.calculate)
+        self.assertRaises(TypeError, self.test_hash.calculate, Name("."), 1)
+
+if __name__ == '__main__':
+    unittest.main()