Browse Source

imoprted Francis's python binding code

git-svn-id: svn://bind10.isc.org/svn/bind10/experiments/python-binding@1671 e5f2f494-b856-4b98-b285-d166d9295462
JINMEI Tatuya 15 years ago
parent
commit
c31aa1717f

+ 1 - 0
configure.ac

@@ -373,6 +373,7 @@ AC_CONFIG_FILES([Makefile
                  src/lib/config/tests/Makefile
                  src/lib/dns/Makefile
                  src/lib/dns/tests/Makefile
+                 src/lib/dns-python/Makefile
                  src/lib/exceptions/Makefile
                  src/lib/auth/Makefile
                  src/lib/auth/tests/Makefile

+ 1 - 1
src/lib/Makefile.am

@@ -1,4 +1,4 @@
-SUBDIRS = exceptions dns cc config auth python
+SUBDIRS = exceptions dns cc config auth dns-python python
 if HAVE_BOOST_PYTHON
 if HAVE_BOOST_SYSTEM
 SUBDIRS += xfr

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

@@ -0,0 +1,15 @@
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+if GCC_WERROR_OK
+AM_CPPFLAGS += -Werror
+endif
+
+pyexec_LTLIBRARIES = bind10_dns_noboost.la
+bind10_dns_noboost_la_SOURCES = cpp_binding.cc
+bind10_dns_noboost_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES)
+bind10_dns_noboost_la_LDFLAGS = $(PYTHON_LDFLAGS)
+# Python prefers .so, while some OSes (specifically MacOS) use a different
+# suffix for dynamic objects.  -module is necessary to work this around.
+bind10_dns_noboost_la_LDFLAGS += -module
+bind10_dns_noboost_la_LIBADD = $(top_builddir)/src/lib/dns/libdns.la
+bind10_dns_noboost_la_LIBADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+bind10_dns_noboost_la_LIBADD += $(PYTHON_LIB)

+ 768 - 0
src/lib/dns-python/cpp_binding.cc

@@ -0,0 +1,768 @@
+// 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.
+
+// $Id$
+
+/* isc::dns::Name Python bindings */
+
+#define PY_SSIZE_T_CLEAN
+
+#include "config.h"
+
+#include <Python.h>
+#include <structmember.h>
+
+#include <exceptions/exceptions.h>
+
+#include <dns/buffer.h>
+#include <dns/exceptions.h>
+#include <dns/name.h>
+#include <dns/messagerenderer.h>
+
+#include <cassert>
+
+using isc::OutOfRange;
+using isc::dns::DNSMessageFORMERR;
+using isc::dns::InvalidBufferPosition;
+using isc::dns::InputBuffer;
+using isc::dns::OutputBuffer;
+using isc::dns::EmptyLabel;
+using isc::dns::TooLongName;
+using isc::dns::TooLongLabel;
+using isc::dns::BadLabelType;
+using isc::dns::BadEscape;
+using isc::dns::IncompleteName;
+using isc::dns::NameComparisonResult;
+using isc::dns::Name;
+using isc::dns::MessageRenderer;
+
+static PyObject *cName_Exception;
+static PyObject *cName_Relation;
+static PyObject *cName_MAX_WIRE;
+static PyObject *cName_MAX_LABELS;
+static PyObject *cName_MAX_LABELLEN;
+static PyObject *cName_MAX_COMPRESS_POINTER;
+static PyObject *cName_COMPRESS_POINTER_MARK8;
+static PyObject *cName_COMPRESS_POINTER_MARK16;
+
+typedef struct {
+    PyObject_HEAD
+    NameComparisonResult *ncr;
+} cNCR;
+
+static void cNCR_dealloc(cNCR *self);
+static int cNCR_init(cNCR *self, PyObject *args);
+static PyObject *cNCR_getOrder(cNCR *self);
+static PyObject *cNCR_getCommonLabels(cNCR *self);
+static PyObject *cNCR_getRelation(cNCR *self);
+
+static PyMethodDef cNCR_Methods[] = {
+    { "getOrder", (PyCFunction)cNCR_getOrder, METH_NOARGS,
+      "Return the ordering of the comparison result" },
+    { "getCommonLabels", (PyCFunction)cNCR_getCommonLabels, METH_NOARGS,
+      "Return the number of common labels of the comparison result" },
+    { "getRelation", (PyCFunction)cNCR_getRelation, METH_NOARGS,
+      "Return the NameRelation of the comparison result" },
+    { NULL, NULL, 0, NULL }
+};
+
+static PyTypeObject cNCR_Type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "bind10_dns_noboost.cNameComparisonResult", /* tp_name */
+    sizeof(cNCR),                       /* tp_basicsize */
+    0,                                  /* tp_itemsize */
+    (destructor)cNCR_dealloc,           /* 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 */
+    "C++ NameComparisonResult Object",  /* tp_doc */
+    NULL,                               /* tp_traverse */
+    NULL,                               /* tp_clear */
+    NULL,                               /* tp_richcompare */
+    0,                                  /* tp_weaklistoffset */
+    NULL,                               /* tp_iter */
+    NULL,                               /* tp_iternext */
+    cNCR_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)cNCR_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 void
+cNCR_dealloc(cNCR *self) {
+    if (self->ncr != NULL)
+        delete self->ncr;
+    self->ncr = NULL;
+    Py_TYPE(self)->tp_free(self);
+}
+
+static int
+cNCR_init(cNCR *self UNUSED_PARAM, PyObject *args UNUSED_PARAM) {
+    PyErr_SetString(PyExc_NotImplementedError,
+                    "cNameComparisonResult can't be built");
+    return -1;
+}
+
+static PyObject *
+cNCR_getOrder(cNCR *self) {
+    return Py_BuildValue("i", self->ncr->getOrder());
+}
+
+static PyObject *
+cNCR_getCommonLabels(cNCR *self) {
+    return Py_BuildValue("I", self->ncr->getCommonLabels());
+}
+
+static PyObject *
+cNCR_getRelation(cNCR *self) {
+    return Py_BuildValue("i", (int) self->ncr->getRelation());
+}
+
+typedef struct {
+    PyObject_HEAD
+    Name *name;
+    size_t position;
+} cName;
+
+static void cName_dealloc(cName *self);
+static int cName_init(cName *self, PyObject *args);
+static PyObject *cName_getLength(cName *self);
+static PyObject *cName_getLabelCount(cName *self);
+static PyObject *cName_toText(cName *self, PyObject *args);
+static PyObject *cName_toWire(cName *self, PyObject *args);
+static PyObject *cName_compare(cName *self, PyObject *args);
+static PyObject *cName_equals(cName *self, PyObject *args);
+static PyObject *cName_nequals(cName *self, PyObject *args);
+static PyObject *cName_richcmp(cName *n1, cName *n2, int op);
+static PyObject *cName_split(cName *self, PyObject *args);
+static PyObject *cName_concatenate(cName *self, PyObject *args);
+static PyObject *cName_downcase(cName *self);
+static PyObject *cName_isWildcard(cName *self);
+static PyObject *cName_getPosition(cName *self);
+static PyObject *cName_clone(cName *self, PyObject *args);
+
+static PyMethodDef cName_Methods[] = {
+    { "getLength", (PyCFunction)cName_getLength, METH_NOARGS,
+      "Return the length of the Name" },
+    { "getLabelCount", (PyCFunction)cName_getLabelCount, METH_NOARGS,
+      "Return the number of labels" },
+    { "toText", (PyCFunction)cName_toText, METH_VARARGS,
+      "Convert the Name to a string" },
+    { "toWire", (PyCFunction)cName_toWire, METH_VARARGS,
+      "Render the Name in the wire format" },
+    { "compare", (PyCFunction)cName_compare, METH_VARARGS,
+      "Compare two Names" },
+    { "equals", (PyCFunction)cName_equals, METH_VARARGS,
+      "Return true iff two Names are equal" },
+    { "nequals", (PyCFunction)cName_nequals, METH_VARARGS,
+      "Return true iff two Names are not equal" },
+    { "split", (PyCFunction)cName_split, METH_VARARGS,
+      "Extract a specified subpart of Name" },
+    { "concatenate", (PyCFunction)cName_concatenate, METH_VARARGS,
+      "Concatenate two Names" },
+    { "downcase", (PyCFunction)cName_downcase, METH_NOARGS,
+      "Downcase the calling object" },
+    { "isWildcard", (PyCFunction)cName_isWildcard, METH_NOARGS,
+      "Test if this is a wildcard Name" },
+    { "getPosition", (PyCFunction)cName_getPosition, METH_NOARGS,
+      "Return the end position in the wire buffer" },
+    { "clone", (PyCFunction)cName_clone, METH_NOARGS,
+      "Clone a Name" },
+    { NULL, NULL, 0, NULL }
+};
+
+static PyTypeObject cName_Type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "bind10_dns_noboost.cName",         /* tp_name */
+    sizeof(cName),                      /* tp_basicsize */
+    0,                                  /* tp_itemsize */
+    (destructor)cName_dealloc,          /* 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|Py_TPFLAGS_BASETYPE,     /* tp_flags */
+    "C++ Name Object",                  /* tp_doc */
+    NULL,                               /* tp_traverse */
+    NULL,                               /* tp_clear */
+    (richcmpfunc)cName_richcmp,         /* tp_richcompare */
+    0,                                  /* tp_weaklistoffset */
+    NULL,                               /* tp_iter */
+    NULL,                               /* tp_iternext */
+    cName_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)cName_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 void
+cName_dealloc(cName *self) {
+    if (self->name != NULL)
+        delete self->name;
+    self->name = NULL;
+    Py_TYPE(self)->tp_free(self);
+}
+
+static int
+cName_init(cName *self, PyObject *args) {
+    const char *s;
+    PyObject *downcase = Py_False;
+
+    /* fromText */
+    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 (EmptyLabel) {
+            PyErr_SetString(cName_Exception, "EmptyLabel");
+            return -1;
+        } catch (TooLongLabel) {
+            PyErr_SetString(cName_Exception, "TooLongLabel");
+            return -1;
+        } catch (BadLabelType) {
+            PyErr_SetString(cName_Exception, "BadLabelType");
+            return -1;
+        } catch (BadEscape) {
+            PyErr_SetString(cName_Exception, "BadEscape");
+            return -1;
+        } catch (TooLongName) {
+            PyErr_SetString(cName_Exception, "TooLongName");
+            return -1;
+        } catch (IncompleteName) {
+            PyErr_SetString(cName_Exception, "IncompleteName");
+            return -1;
+#ifdef CATCHMEMERR
+        } catch (std::bad_alloc) {
+            PyErr_NoMemory();
+            return -1;
+#endif
+        } catch (...) {
+            PyErr_SetString(cName_Exception, "Unexpected?!");
+            return -1;
+        }
+        return 0;
+    }
+    PyErr_Clear();
+
+    const char *b;
+    Py_ssize_t len;
+    unsigned int position;
+
+    /* fromWire */
+    if (PyArg_ParseTuple(args, "y#I|O!", &b, &len, &position,
+                         &PyBool_Type, &downcase)) {
+        try {
+            InputBuffer buffer(b, len);
+
+            buffer.setPosition(position);
+            self->name = new Name(buffer, downcase == Py_True);
+            self->position = buffer.getPosition();
+        } catch (InvalidBufferPosition) {
+            PyErr_SetString(cName_Exception,
+                            "InvalidBufferPosition");
+            return -1;
+        } catch (TooLongName) {
+            PyErr_SetString(cName_Exception, "TooLongName");
+            return -1;
+        } catch (BadLabelType) {
+            PyErr_SetString(cName_Exception, "BadLabelType");
+            return -1;
+        } catch (DNSMessageFORMERR) {
+            PyErr_SetString(cName_Exception, "DNSMessageFORMERR");
+            return -1;
+        } catch (IncompleteName) {
+            PyErr_SetString(cName_Exception, "IncompleteName");
+            return -1;
+#ifdef CATCHMEMERR
+        } catch (std::bad_alloc) {
+            PyErr_NoMemory();
+            return -1;
+#endif
+        } catch (...) {
+            PyErr_SetString(cName_Exception, "Unexpected?!");
+            return -1;
+        }
+        return 0;
+    }
+
+    PyErr_Clear();
+    PyErr_SetString(PyExc_TypeError,
+                    "fromText and fromWire Name constructors don't match");
+    return -1;
+}
+
+static PyObject *
+cName_getLength(cName *self) {
+    return Py_BuildValue("I", (unsigned int) self->name->getLength());
+}
+
+static PyObject *
+cName_getLabelCount(cName *self) {
+    return Py_BuildValue("I", self->name->getLabelCount());
+}
+
+static PyObject *
+cName_toText(cName *self, PyObject *args) {
+    PyObject *omit = Py_False;
+
+    if (!PyArg_ParseTuple(args, "|O!", &PyBool_Type, &omit))
+        return NULL;
+
+    try {
+        std::string s = self->name->toText(omit == Py_True);
+
+        return Py_BuildValue("s", s.c_str());
+    } catch (BadLabelType) {
+        PyErr_SetString(cName_Exception, "BadLabelType");
+        return NULL;
+    }
+}
+
+static PyObject *
+cName_toWire(cName *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ""))
+        return NULL;
+
+    OutputBuffer buffer(255);
+
+    self->name->toWire(buffer);
+    return Py_BuildValue("y#", buffer.getData(),
+                         (Py_ssize_t) buffer.getLength());
+}
+
+static PyObject *
+cName_compare(cName *self, PyObject *args) {
+    cName *other;
+
+    if (!PyArg_ParseTuple(args, "O!", &cName_Type, (PyObject **) &other))
+        return NULL;
+
+    cNCR *ret = PyObject_New(cNCR, &cNCR_Type);
+    if (ret != NULL) {
+        ret->ncr = new NameComparisonResult(
+            self->name->compare(*other->name));
+        if (ret->ncr == NULL) {
+            Py_DECREF(ret);
+            return NULL;
+        }
+    }
+    return (PyObject *) ret;
+}
+
+static PyObject *
+cName_equals(cName *self, PyObject *args) {
+    cName *other;
+
+    if (!PyArg_ParseTuple(args, "O!", &cName_Type, (PyObject **) &other))
+        return NULL;
+
+    if (self->name->equals(*other->name))
+        Py_RETURN_TRUE;
+    else
+        Py_RETURN_FALSE;
+}
+
+static PyObject *
+cName_nequals(cName *self, PyObject *args) {
+    cName *other;
+
+    if (!PyArg_ParseTuple(args, "O!", &cName_Type, (PyObject **) &other))
+        return NULL;
+
+    if (self->name->nequals(*other->name))
+        Py_RETURN_TRUE;
+    else
+        Py_RETURN_FALSE;
+}
+
+static PyObject *
+cName_richcmp(cName *n1, cName *n2, int op) {
+    bool c;
+
+    switch (op) {
+    case Py_LT:
+        c = n1->name->lthan(*n2->name);
+        break;
+    case Py_LE:
+        c = n1->name->leq(*n2->name);
+        break;
+    case Py_EQ:
+        c = n1->name->equals(*n2->name);
+        break;
+    case Py_NE:
+        c = n1->name->nequals(*n2->name);
+        break;
+    case Py_GT:
+        c = n1->name->gthan(*n2->name);
+        break;
+    case Py_GE:
+        c = n1->name->geq(*n2->name);
+        break;
+    default:
+        assert(0);              // XXX: should trigger an exception
+    }
+    if (c)
+        Py_RETURN_TRUE;
+    else
+        Py_RETURN_FALSE;
+}
+
+static PyObject *
+cName_split(cName *self, PyObject *args) {
+    unsigned int first, n;
+
+    if (!PyArg_ParseTuple(args, "II", &first, &n))
+        return NULL;
+
+    cName *ret = PyObject_New(cName, &cName_Type);
+    if (ret != NULL) {
+        ret->name = NULL;
+        try {
+            ret->name = new Name(self->name->split(first, n));
+        } catch(OutOfRange) {
+            PyErr_SetString(cName_Exception, "OutOfRange");
+        }
+        if (ret->name == NULL) {
+            Py_DECREF(ret);
+            return NULL;
+        }
+    }
+    return (PyObject *) ret;
+}
+
+static PyObject *
+cName_concatenate(cName *self, PyObject *args) {
+    cName *suffix;
+
+    if (!PyArg_ParseTuple(args, "O!", &cName_Type, (PyObject **) &suffix))
+        return NULL;
+
+    cName *ret = PyObject_New(cName, &cName_Type);
+    if (ret != NULL) {
+        ret->name = NULL;
+        try {
+            ret->name = new Name(
+                self->name->concatenate(*suffix->name));
+        } catch(TooLongName) {
+            PyErr_SetString(cName_Exception, "TooLongName");
+        }
+        if (ret->name == NULL) {
+            Py_DECREF(ret);
+            return NULL;
+        }
+    }
+    return (PyObject *) ret;
+}
+
+static PyObject *
+cName_downcase(cName *self) {
+    self->name->downcase();
+    Py_RETURN_NONE;
+}
+
+static PyObject *
+cName_isWildcard(cName *self) {
+    if (self->name->isWildcard())
+        Py_RETURN_TRUE;
+    else
+        Py_RETURN_FALSE;
+}
+
+static PyObject *
+cName_getPosition(cName *self) {
+    return Py_BuildValue("I", (unsigned int) self->position);
+}
+
+static PyObject *
+cName_clone(cName *self, PyObject *args UNUSED_PARAM) {
+    cName *copy = PyObject_New(cName, &cName_Type);
+
+    if (copy != NULL) {
+        copy->name = new Name(*self->name);
+        if (copy->name == NULL) {
+            Py_DECREF(copy);
+            return NULL;
+        }
+    }
+    return (PyObject *) copy;
+}
+
+typedef struct {
+    PyObject_HEAD
+    MessageRenderer *mr;
+    OutputBuffer *buffer;
+} cMR;
+
+static void cMR_dealloc(cMR *self);
+static int cMR_init(cMR *self, PyObject *args);
+static PyObject *cMR_dump(cMR *self);
+static PyObject *cMR_writeData(cMR *self, PyObject *args);
+static PyObject *cMR_writeName(cMR *self, PyObject *args);
+
+static PyMethodDef cMR_Methods[] = {
+    { "dump", (PyCFunction)cMR_dump, METH_NOARGS,
+      "Return content of the MessageRenderer" },
+    { "writeData", (PyCFunction)cMR_writeData, METH_VARARGS,
+      "Write data into the MessageRenderer" },
+    { "writeName", (PyCFunction)cMR_writeName, METH_VARARGS,
+      "Write a Name into the MessageRenderer" },
+    { NULL, NULL, 0, NULL }
+};
+
+static PyTypeObject cMR_Type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "bind10_dns_noboost.cMessageRenderer", /* tp_name */
+    sizeof(cMR),                        /* tp_basicsize */
+    0,                                  /* tp_itemsize */
+    (destructor)cMR_dealloc,            /* 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|Py_TPFLAGS_BASETYPE,     /* tp_flags */
+    "C++ MessageRenderer Object",       /* tp_doc */
+    NULL,                               /* tp_traverse */
+    NULL,                               /* tp_clear */
+    NULL,                               /* tp_richcompare */
+    0,                                  /* tp_weaklistoffset */
+    NULL,                               /* tp_iter */
+    NULL,                               /* tp_iternext */
+    cMR_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)cMR_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 void
+cMR_dealloc(cMR *self) {
+    if (self->mr != NULL)
+        delete self->mr;
+    self->mr = NULL;
+    if (self->buffer != NULL)
+        delete self->buffer;
+    self->buffer = NULL;
+    Py_TYPE(self)->tp_free(self);
+}
+
+static int
+cMR_init(cMR *self, PyObject *args) {
+    if (!PyArg_ParseTuple(args, ""))
+        return -1;
+
+    self->buffer = new OutputBuffer(1024);
+    if (self->buffer == NULL) {
+        (void) PyErr_NoMemory();
+        return -1;
+    }
+    self->mr = new MessageRenderer(*self->buffer);
+    if (self->mr == NULL) {
+        delete self->buffer;
+        self->buffer = NULL;
+        return -1;
+    }
+    return 0;
+}
+
+static PyObject *
+cMR_dump(cMR *self) {
+    return Py_BuildValue("y#", self->buffer->getData(),
+                         (Py_ssize_t) self->buffer->getLength());
+}
+
+static PyObject *
+cMR_writeData(cMR *self, PyObject *args) {
+    const char *b;
+    Py_ssize_t len;
+
+    if (!PyArg_ParseTuple(args, "y#", &b, &len))
+        return NULL;
+
+    self->buffer->writeData(b, len);
+    Py_RETURN_NONE;
+}
+
+static PyObject *
+cMR_writeName(cMR *self, PyObject *args) {
+    cName *name;
+    PyObject *compress = Py_True;
+
+    if (!PyArg_ParseTuple(args, "O!|O!",
+                          &cName_Type, (PyObject **) &name,
+                          &PyBool_Type, &compress))
+        return NULL;
+
+    self->mr->writeName(*name->name, compress == Py_True);
+    Py_RETURN_NONE;
+}
+
+static PyModuleDef cName_Module = {
+    { PyObject_HEAD_INIT(NULL) NULL, 0, NULL}, // XXX: ad hoc init values
+    "bind10_dns_noboost",                       /* m_name */
+    "Python bindings for C++ Name Module",      /* m_doc */
+    -1,                                 /* m_size */
+    NULL,                               /* m_methods */
+    NULL,                               /* m_reload */
+    NULL,                               /* m_traverse */
+    NULL,                               /* m_clear */
+    NULL,                               /* m_free */
+};
+
+PyMODINIT_FUNC
+PyInit_bind10_dns_noboost(void) {
+    if (PyType_Ready(&cName_Type) < 0)
+        return NULL;
+    if (PyType_Ready(&cNCR_Type) < 0)
+        return NULL;
+    if (PyType_Ready(&cMR_Type) < 0)
+        return NULL;
+
+    PyObject *mod;
+
+    mod = PyModule_Create(&cName_Module);
+    if (mod == NULL)
+        return NULL;
+
+    Py_INCREF(&cName_Type);
+    PyModule_AddObject(mod, "cName", (PyObject *) &cName_Type);
+    Py_INCREF(&cNCR_Type);
+    PyModule_AddObject(mod, "cNameComparisonResult",
+                       (PyObject *) &cNCR_Type);
+    Py_INCREF(&cMR_Type);
+    PyModule_AddObject(mod, "cMessageRenderer", (PyObject *) &cMR_Type);
+        
+    cName_Exception = PyErr_NewException("bind10_dns_noboost.DNSException", NULL,
+                                         NULL);
+    Py_INCREF(cName_Exception);
+    PyModule_AddObject(mod, "DNSException", cName_Exception);
+
+    cName_Relation = Py_BuildValue("{i:s,i:s,i:s,i:s}",
+                                   0, "SUPERDOMAIN",
+                                   1, "SUBDOMAIN",
+                                   2, "EQUAL",
+                                   3, "COMMONANCESTOR");
+    Py_INCREF(cName_Relation);
+    PyModule_AddObject(mod, "NameRelation", cName_Relation);
+
+    cName_MAX_WIRE = Py_BuildValue("I", 255U);
+    Py_INCREF(cName_MAX_WIRE);
+    PyModule_AddObject(mod, "MAX_WIRE", cName_MAX_WIRE);
+    cName_MAX_LABELS = Py_BuildValue("I", 128U);
+    Py_INCREF(cName_MAX_LABELS);
+    PyModule_AddObject(mod, "MAX_LABELS", cName_MAX_LABELS);
+    cName_MAX_LABELLEN = Py_BuildValue("I", 63U);
+    Py_INCREF(cName_MAX_LABELLEN);
+    PyModule_AddObject(mod, "MAX_LABELLEN", cName_MAX_LABELLEN);
+    cName_MAX_COMPRESS_POINTER = Py_BuildValue("I", 0x3fffU);
+    Py_INCREF(cName_MAX_COMPRESS_POINTER);
+    PyModule_AddObject(mod, "MAX_COMPRESS_POINTER",
+                       cName_MAX_COMPRESS_POINTER);
+    cName_COMPRESS_POINTER_MARK8 = Py_BuildValue("I", 0xc0U);
+    Py_INCREF(cName_COMPRESS_POINTER_MARK8);
+    PyModule_AddObject(mod, "COMPRESS_POINTER_MARK8",
+                       cName_COMPRESS_POINTER_MARK8);
+    cName_COMPRESS_POINTER_MARK16 = Py_BuildValue("I", 0xc000U);
+    Py_INCREF(cName_COMPRESS_POINTER_MARK16);
+    PyModule_AddObject(mod, "COMPRESS_POINTER_MARK16",
+                       cName_COMPRESS_POINTER_MARK16);
+
+    return mod;
+}

+ 49 - 0
src/lib/dns-python/dns.py

@@ -0,0 +1,49 @@
+"""Python C bindings"""
+
+import bind10_dns_noboost
+
+class Name(bind10_dns_noboost.cName):
+    """Python shadow class of cName"""
+
+    def __init__(self, *args):
+        """initializer"""
+        super(Name, self).__init__(*args)
+
+    def __len__(self):
+        """length"""
+        return self.getLength()
+
+    def __str__(self):
+        """string"""
+        return self.toText()
+
+    def __repr__(self):
+        """representation"""
+        return '<Name: ' + str(self) +'>'
+
+class MessageRenderer(bind10_dns_noboost.cMessageRenderer):
+    """Python shadow class of cMessageRenderer"""
+
+    def __init__(self):
+        """initializer"""
+        super(MessageRenderer, self).__init__()
+
+def fromText(text, downcase=False):
+    """fromText factory"""
+    return Name(text, downcase)
+
+def fromWire(wire, position, downcase=False):
+    """fromWire factory"""
+    n = Name(wire, position, downcase)
+    return (n, n.getPosition() - position)
+
+def compare(n1, n2):
+    """compare"""
+    ncr = n1.compare(n2)
+    return (ncr.getOrder(), \
+                ncr.getCommonLabels(), \
+                bind10_dns_noboost.NameRelation[ncr.getRelation()])
+
+def concatenate(n1, n2):
+    """concatenate"""
+    return n1.concatenate(n2)

+ 590 - 0
src/lib/dns-python/test.py

@@ -0,0 +1,590 @@
+"""Python tests"""
+
+from dns import *
+
+example = fromText("www.example.com")
+upper = fromText("WWW.EXAMPLE.COM")
+small = fromText("aaa.example.com")
+large = fromText("zzz.example.com")
+
+def f2b(name):
+    """file to binary"""
+    f = open(name)
+    rl = f.readlines()
+    f.close()
+    b = bytearray()
+    for l in rl:
+        if l[0] == '#':
+            continue
+        if l[-1] == '\n':
+            l = l[:-1]
+        b += bytearray().fromhex(l)
+    return bytes(b)
+
+def nflc():
+    """name Factory Lower Case"""
+    namestr = ''
+    labelcount = 0
+    for ch in range(0, bind10_dns_noboost.MAX_WIRE + 1):
+        if (ch < ord('A')) or (ch > ord('Z')):
+            ss = '%03d' % ch
+            namestr += '\\' + ss
+            labelcount += 1
+            if labelcount == bind10_dns_noboost.MAX_LABELLEN:
+                namestr += '.'
+                labelcount = 0
+    return Name(namestr)
+
+    
+
+def testFT():
+    """fromText"""
+    nl = []
+    nl.append(fromText("www.example.com"))
+    nl.append(fromText("www.example.com."))  # with a trailing dot
+    nl.append(fromText("wWw.exAmpLe.com"))   # mixed cases
+    nl.append(fromText("\\wWw.exAmpLe.com")) # escape with a backslash
+    # decimal representation for "WWW"
+    nl.append(fromText("\\087\\087\\087.example.com"))
+
+    for it in nl:
+        if it != example:
+            print('FAILED: (FT)',it,'!=',example)
+
+    # root names
+    if fromText("@") != fromText("."):
+        print('FAILED: (FT) root name')
+
+    # downcase
+    n = fromText("Www.eXample.coM", True)
+    if n.toWire() != example.toWire():
+        print('FAILED: (FT) downcase')
+
+    #
+    # Tests for bogus names.  These should trigger an exception.
+    #
+    # empty label cannot be followed by another label
+    try:
+        n = fromText(".a")
+        print('FAILED: (FT) .a')
+    except bind10_dns_noboost.DNSException as e:
+        if str(e) != 'EmptyLabel':
+            print('FAILED: (FT) .a')
+    # duplicate period
+    try:
+        n = fromText("a..")
+        print('FAILED: (FT) a..')
+    except bind10_dns_noboost.DNSException as e:
+        if str(e) != 'EmptyLabel':
+            print('FAILED: (FT) a..')
+    # label length must be < 64
+    try:
+        n = fromText("012345678901234567890123456789" +
+                     "012345678901234567890123456789" +
+                     "0123")
+        print('FAILED: (FT) TooLongLabel')
+    except bind10_dns_noboost.DNSException as e:
+        if str(e) != 'TooLongLabel':
+            print('FAILED: (FT) TooLongLabel')
+    # now-unsupported bitstring labels
+    try:
+        n = fromText("\\[b11010000011101]")
+        print('FAILED: (FT) BadLabelType')
+    except bind10_dns_noboost.DNSException as e:
+        if str(e) != 'BadLabelType':
+            print('FAILED: (FT) BadLabelType')
+    # label length must be < 64
+    try:
+        n = fromText("012345678901234567890123456789" +
+                     "012345678901234567890123456789" +
+                     "012\\x")
+        print('FAILED: (FT) TooLongLabel')
+    except bind10_dns_noboost.DNSException as e:
+        if str(e) != 'TooLongLabel':
+            print('FAILED: (FT) TooLongLabel')
+    # but okay as long as resulting len < 64 even if the original string is
+    # "too long"
+    try:
+        n = fromText("012345678901234567890123456789" +
+                     "012345678901234567890123456789" +
+                     "01\\x")
+    except bind10_dns_noboost.DNSException:
+        print('FAILED: (FT) too long')
+    # incomplete \DDD pattern (exactly 3 D's must appear)
+    try:
+        n = fromText("\\12abc")
+        print('FAILED: (FT) BadEscape')
+    except bind10_dns_noboost.DNSException as e:
+        if str(e) != 'BadEscape':
+            print('FAILED: (FT) BadEscape')
+    # \DDD must not exceed 255
+    try:
+        n = fromText("\\256")
+        print('FAILED: (FT) BadEscape')
+    except bind10_dns_noboost.DNSException as e:
+        if str(e) != 'BadEscape':
+            print('FAILED: (FT) BadEscape')
+    # Same tests for \111 as for \\x above
+    try:
+        n = fromText("012345678901234567890123456789" +
+                     "012345678901234567890123456789" +
+                     "012\\111")
+        print('FAILED: (FT) TooLongLabel')
+    except bind10_dns_noboost.DNSException as e:
+        if str(e) != 'TooLongLabel':
+            print('FAILED: (FT) TooLongLabel')
+    try:
+        n = fromText("012345678901234567890123456789" +
+                     "012345678901234567890123456789" +
+                     "01\\111")
+    except bind10_dns_noboost.DNSException:
+        print('FAILED: (FT) too long')
+    # A domain name must be 255 octets or less
+    try:
+        n = fromText("123456789.123456789.123456789.123456789.123456789." +
+                     "123456789.123456789.123456789.123456789.123456789." +
+                     "123456789.123456789.123456789.123456789.123456789." +
+                     "123456789.123456789.123456789.123456789.123456789." +
+                     "123456789.123456789.123456789.123456789.123456789." +
+                     "1234")
+        print('FAILED: (FT) TooLongName')
+    except bind10_dns_noboost.DNSException as e:
+        if str(e) != 'TooLongName':
+            print('FAILED: (FT) TooLongName')
+    # This is a possible longest name and should be accepted
+    try:
+        n = fromText("123456789.123456789.123456789.123456789.123456789." +
+                     "123456789.123456789.123456789.123456789.123456789." +
+                     "123456789.123456789.123456789.123456789.123456789." +
+                     "123456789.123456789.123456789.123456789.123456789." +
+                     "123456789.123456789.123456789.123456789.123456789." +
+                     "123")
+    except bind10_dns_noboost.DNSException:
+        print('FAILED: (FT) longest')
+    # \DDD must consist of 3 digits
+    try:
+        n = fromText("\\12")
+        print('FAILED: (FT) IncompleteName')
+    except bind10_dns_noboost.DNSException as e:
+        if str(e) != 'IncompleteName':
+            print('FAILED: (FT) IncompleteName')
+
+    # a name with the max number of labels.  should be constructed without
+    # an error, and its length should be the max value.
+    try:
+        n = fromText("0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." + # 40
+                     "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." + # 80
+                     "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." + # 120
+                     "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." + # 160
+                     "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." + # 200
+                     "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." + # 240
+                     "0.1.2.3.4.5.6.")
+        if bind10_dns_noboost.MAX_LABELS != n.getLabelCount():
+            print('FAILED: (FT)',bind10_dns_noboost.MAX_LABELS,'!=',
+                  n.getLabelCount())
+    except bind10_dns_noboost.DNSException:
+        print('FAILED: (FT) maxlabels')
+
+def testFW():
+    """fromWire"""
+    #
+    # test cases derived from BIND9 tests.
+    #
+    # normal case with a compression pointer
+    (n, _) = fromWire(f2b("testdata/name_fromWire1"), 25)
+    if n != fromText("vix.com"):
+        print('FAILED: (FW) n',n.toText(),'!=',"vix.com")
+    # bogus label character (looks like a local compression pointer)
+    try:
+        (n, _) = fromWire(f2b("testdata/name_fromWire2"), 25)
+        print('FAILED: (FW) BadLabelType')
+    except bind10_dns_noboost.DNSException as e:
+        if str(e) != 'BadLabelType':
+            print('FAILED: (FW) BadLabelType')
+    # a bad compression pointer (too big)
+    try:
+        (n, _) = fromWire(f2b("testdata/name_fromWire3_1"), 25)
+        print('FAILED: (FW) BadPointer')
+    except bind10_dns_noboost.DNSException as e:
+        if str(e) != 'BadPointer':
+            print('FAILED: (FW) BadPointer')
+    # forward reference
+    try:
+        (n, _) = fromWire(f2b("testdata/name_fromWire3_2"), 25)
+        print('FAILED: (FW) BadPointer')
+    except bind10_dns_noboost.DNSException as e:
+        if str(e) != 'BadPointer':
+            print('FAILED: (FW) BadPointer')
+    # invalid name length
+    try:
+        (n, _) = fromWire(f2b("testdata/name_fromWire4"), 550)
+        print('FAILED: (FW) TooLongName')
+    except bind10_dns_noboost.DNSException as e:
+        if str(e) != 'TooLongName':
+            print('FAILED: (FW) TooLongName')
+
+    # skip test for from Wire5.  It's for disabling decompression, but our
+    # implementation always allows it.
+
+    # bad pointer (too big)
+    try:
+        (n, _) = fromWire(f2b("testdata/name_fromWire6"), 25)
+        print('FAILED: (FW) BadPointer')
+    except bind10_dns_noboost.DNSException as e:
+        if str(e) != 'BadPointer':
+            print('FAILED: (FW) BadPointer')
+    # input ends unexpectedly
+    try:
+        (n, _) = fromWire(f2b("testdata/name_fromWire7"), 25)
+        print('FAILED: (FW) IncompleteName')
+    except bind10_dns_noboost.DNSException as e:
+        if str(e) != 'IncompleteName':
+            print('FAILED: (FW) IncompleteName')
+    # many hops of compression but valid.  should succeed
+    try:
+        (n, _) = fromWire(f2b("testdata/name_fromWire8"), 383)
+        if n != fromText("vix.com"):
+            print('FAILED: (FW) vix.com')
+    except bind10_dns_noboost.DNSException:
+        print('FAILED: (FW) vix.com')
+
+    #
+    # Additional test cases
+    #
+
+    # large names, a long but valid one, and invalid (too long) one.
+    try:
+        (n, _) = fromWire(f2b("testdata/name_fromWire9"), 0)
+        if n.getLength() != bind10_dns_noboost.MAX_WIRE:
+            print('FAILED: (FW) ong but valid')
+    except bind10_dns_noboost.DNSException:
+        print('FAILED: (FW) ong but valid')
+    try:
+        (n, _) = fromWire(f2b("testdata/name_fromWire10"), 0)
+        print('FAILED: (FW) TooLongName')
+    except bind10_dns_noboost.DNSException as e:
+        if str(e) != 'TooLongName':
+            print('FAILED: (FW) TooLongName')
+
+    # A name with possible maximum number of labels; awkward but valid
+    try:
+        (n, _) = fromWire(f2b("testdata/name_fromWire11"), 0)
+        if n.getLabelCount() != bind10_dns_noboost.MAX_LABELS:
+            print('FAILED: (FW) maxlabels')
+    except bind10_dns_noboost.DNSException:
+        print('FAILED: (FW) maxlabels')
+
+    # Wire format including an invalid label length
+    try:
+        (n, _) = fromWire(f2b("testdata/name_fromWire12"), 0)
+        print('FAILED: (FW) BadLabelType')
+    except bind10_dns_noboost.DNSException as e:
+        if str(e) != 'BadLabelType':
+            print('FAILED: (FW) BadLabelType')
+
+    # converting upper-case letters to down-case
+    (n, _) = fromWire(f2b("testdata/name_fromWire1"), 25, True)
+    if n.toText() != 'vix.com.':
+        print('FAILED: (FW) ',n.toText(),'!=','vix.com.')
+    if n.getLabelCount() != 3:
+        print('FAILED: (FW) ',n.getLabelCount(),'!= 3')
+
+def testTT():
+    """toText"""
+    # tests derived from BIND9
+    n = fromText("a.b.c.d")
+    if n.toText(True) != "a.b.c.d":
+        print('FAILED: (TT)',n.toText(True),'!= a.b.c.d')
+    n = fromText("a.\\\\[\\[.c.d")
+    if n.toText(True) != "a.\\\\[[.c.d":
+        print('FAILED: (TT)',n.toText(),'!= a.\\\\[[.c.d')
+    n = fromText("a.b.C.d")
+    if n.toText(False) != "a.b.C.d.":
+        print('FAILED: (TT)',n.toText(False),'!= a.b.C.d.')
+
+    # test omit_final_dot.  It's false by default.
+    n = fromText("a.b.c.d.")
+    if n.toText(True) != "a.b.c.d":
+        print('FAILED: (TT)',n.toText(True),'!= a.b.c.d')
+    n1 = fromText("a.b.")
+    n2 = fromText("a.b.")
+    if n1.toText(False) != n2.toText():
+        print('FAILED: (TT)',n1.toText(False))
+
+    # the root name is a special case: omit_final_dot will be ignored.
+    n = fromText(".")
+    if n.toText(True) != '.':
+        print('FAILED: (TT)',n.toText(True),' != ,')
+
+def testTW():
+    """toWire"""
+    data = "\x01\x61\x03\x76\x69\x78\x03\x63\x6f\x6d\x00"
+    n = fromText("a.vix.com.")
+    w = n.toWire()
+    if len(w) != len(data):
+        print('FAILED: (TW)',len(w),'!=',len(data))
+
+def testF():
+    """compare"""
+    n1 = fromText("c.d")
+    n2 = fromText("a.b.c.d")
+    t = compare(n1, n2)
+    if t[0] < 0:
+        tt = (-1, t[1], t[2])
+    elif t[0] > 0:
+        tt = (1, t[1],t[2])
+    else:
+        tt = (0, t[1],t[2])
+    if tt != (-1,3,'SUPERDOMAIN'):
+        print('FAILED: (F) ', tt)
+
+    n1 = fromText("a.b.c.d")
+    n2 = fromText("c.d")
+    t = compare(n1, n2)
+    if t[0] < 0:
+        tt = (-1, t[1], t[2])
+    elif t[0] > 0:
+        tt = (1, t[1],t[2])
+    else:
+        tt = (0, t[1],t[2])
+    if tt != (1, 3, 'SUBDOMAIN'):
+        print('FAILED: (F) ', tt)
+
+    n1 = fromText("a.b.c.d")
+    n2 = fromText("c.d.e.f")
+    t = compare(n1, n2)
+    if t[0] < 0:
+        tt = (-1, t[1], t[2])
+    elif t[0] > 0:
+        tt = (1, t[1],t[2])
+    else:
+        tt = (0, t[1],t[2])
+    if tt != (-1, 1, 'COMMONANCESTOR'):
+        print('FAILED: (F) ', tt)
+
+    n1 = fromText("a.b.c.d")
+    n2 = fromText("f.g.c.d")
+    t = compare(n1, n2)
+    if t[0] < 0:
+        tt = (-1, t[1], t[2])
+    elif t[0] > 0:
+        tt = (1, t[1],t[2])
+    else:
+        tt = (0, t[1],t[2])
+    if tt != (-1, 3, 'COMMONANCESTOR'):
+        print('FAILED: (F) ', tt)
+
+    n1 = fromText("a.b.c.d")
+    n2 = fromText("A.b.C.d.")
+    t = compare(n1, n2)
+    if t[0] < 0:
+        tt = (-1, t[1], t[2])
+    elif t[0] > 0:
+        tt = (1, t[1],t[2])
+    else:
+        tt = (0, t[1],t[2])
+    if tt != (0, 5, 'EQUAL'):
+        print('FAILED: (F) ', tt)
+
+def testE():
+    """equal"""
+    n = fromText("WWW.EXAMPLE.COM.")
+    if not (example == n):
+        print('FAILED: (E) 1')
+    if not example.equals(n):
+        print('FAILED: (E) 2')
+    n = fromText("www.example.org.")
+    if not (example != n):
+        print('FAILED: (E) 3')
+    if not example.nequals(n):
+        print('FAILED: (E) 4')
+
+def testIW():
+    """isWildcard"""
+    if not example.isWildcard() is False:
+        print('FAILED: (IW)',example.toText())
+    n = fromText("*.a.example.com")
+    if not n.isWildcard() is True:
+        print('FAILED: (IW)',n.toText())
+    n = fromText("a.*.example.com")
+    if not n.isWildcard() is False:
+        print('FAILED: (IW)',n.toText())
+
+def testC():
+    """concatenate"""
+    nc = fromText("aaa.www.example.com.")
+    n = fromText("aaa")
+    c = compare(nc, concatenate(n, example))
+    if c[2] != 'EQUAL':
+        print('FAILED: (C)',nc,'!=',n,'+',example)
+
+    n = fromText(".")
+    c = compare(example, concatenate(n, example))
+    if c[2] != 'EQUAL':
+        print('FAILED: (C)',example,'!=',n,'+',example)
+
+    c = compare(example, concatenate(example, n))
+    if c[2] != 'EQUAL':
+        print('FAILED: (C)',example,'!=',example,'+',n)
+
+    # concatenating two valid names would result in too long a name.
+    n1 = fromText("123456789.123456789.123456789.123456789.123456789." +
+                  "123456789.123456789.123456789.123456789.123456789." +
+                  "123456789.123456789.123456789.123456789.123456789.")
+    n2 = fromText("123456789.123456789.123456789.123456789.123456789." +
+                  "123456789.123456789.123456789.123456789.123456789." +
+                  "1234.");
+    try:
+        n = concatenate(n1, n2)
+        print('FAILED: (C) toolong')
+    except bind10_dns_noboost.DNSException as e:
+        if str(e) != 'TooLongName':
+            print('FAILED: (C) TooLongName')
+
+def testS():
+    """split"""
+    # normal cases with or without explicitly specifying the trailing dot.
+    n = fromText("example.com.")
+    ns = example.split(1, 2)
+    if n != ns:
+        print('FAILED: (S)',n,'!=',ns)
+    ns = example.split(1, 3)
+    if n != ns:
+        print('FAILED: (S)',n,'!=',ns)
+    # edge cases: only the first or last label.
+    n = fromText("www.")
+    ns = example.split(0, 1)
+    if n != ns:
+        print('FAILED: (S)',n,'!=',ns)
+    n = fromText(".")
+    ns = example.split(3, 1)
+    if n != ns:
+        print('FAILED: (S)',n,'!=',ns)
+    # invalid range: an exception should be thrown.
+    try:
+        n = example.split(1, 0)
+        print('FAILED: (S) OutOfRange')
+    except bind10_dns_noboost.DNSException as e:
+        if str(e) != 'OutOfRange':
+            print('FAILED: (S) OutOfRange')
+    try:
+        n = example.split(2, 3)
+        print('FAILED: (S) OutOfRange')
+    except bind10_dns_noboost.DNSException as e:
+        if str(e) != 'OutOfRange':
+            print('FAILED: (S) OutOfRange')
+
+def testD():
+    """downcase"""
+    # usual case: all-upper case name to all-lower case
+    upper.downcase()
+    if upper.toWire() != example.toWire():
+        print('FAILED: (D) upper')
+    # confirm that non upper-case characters are intact
+    n1 = nflc()
+    n2 = nflc()
+    n1.downcase()
+    if n1.toWire() != n2.toWire():
+        print('FAILED: (D) nameFactoryLowerCase')
+
+def testLE():
+    """leq"""
+    if not (small <= large) or \
+            not (small <= small) or \
+            large <= small:
+        print('FAILED: (LE)')
+
+def testGE():
+    """geq"""
+    if not (large >= small) or \
+            not (large >= large) or \
+            small >= large:
+        print('FAILED: (GE)')
+
+def testLT():
+    """lthan"""
+    if not (small < large) or \
+            small < small or \
+            large < small:
+        print('FAILED: (LT)')
+
+def testGT():
+    """gthan"""
+    if not (large > small) or \
+            large > large or \
+            small > large:
+        print('FAILED: (GT)')
+
+def testWN():
+    """writeName"""
+    data = f2b("testdata/name_toWire1")
+    mr = MessageRenderer()
+    n = fromText("a.example.com.")
+    mr.writeName(n)
+    n = fromText("b.example.com.")
+    mr.writeName(n)
+    n = fromText("a.example.org.")
+    mr.writeName(n)
+    if mr.dump() != data:
+        print('FAILED: (WN)',mr.dump(),'!=',data)
+
+    # toWireInLargeBuffer
+    pad = b'\x00' * 0x3fff
+    data = f2b("testdata/name_toWire2")
+    mr = MessageRenderer()
+    mr.writeData(pad)
+    n = fromText("a.example.com.")
+    mr.writeName(n)
+    n = fromText("a.example.com.")
+    mr.writeName(n)
+    n = fromText("b.example.com.")
+    mr.writeName(n)
+    d = mr.dump()[0x3fff :]
+    if d != data:
+        print('FAILED: (WN) LB',d,'!=',data)
+
+    # toWireWithUncompressed
+    data = f2b("testdata/name_toWire3")
+    mr = MessageRenderer()
+    n = fromText("a.example.com.")
+    mr.writeName(n)
+    n = fromText("b.example.com.")
+    mr.writeName(n, False)
+    n = fromText("b.example.com.")
+    mr.writeName(n)
+    if mr.dump() != data:
+        print('FAILED: (WN) UC',mr.dump(),'!=',data)
+
+    # toWireCaseCompress
+    data = f2b("testdata/name_toWire1")
+    mr = MessageRenderer()
+    n = fromText("a.example.com.")
+    mr.writeName(n)
+    # this should match the first name in terms of compression:
+    n = fromText("b.exAmple.CoM.")
+    mr.writeName(n)
+    n = fromText("a.example.org.")
+    mr.writeName(n)
+    if mr.dump() != data:
+        print('FAILED: (WN) CC',mr.dump(),'!=',data)
+
+def testAll():
+    """All"""
+    testFT()
+    testFW()
+    testTT()
+    testTW()
+    testF()
+    testE()
+    testIW()
+    testC()
+    testS()
+    testD()
+    testLE()
+    testGE()
+    testLT()
+    testGT()
+    testWN()
+
+testAll()
+print("PASSED")