2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-31 05:55:28 +00:00

imoprted Francis's python binding code

git-svn-id: svn://bind10.isc.org/svn/bind10/experiments/python-binding@1671 e5f2f494-b856-4b98-b285-d166d9295462
This commit is contained in:
JINMEI Tatuya
2010-04-01 17:31:17 +00:00
parent b91c3ab937
commit c31aa1717f
6 changed files with 1424 additions and 1 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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
src/lib/dns-python/dns.py Normal file
View File

@@ -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
src/lib/dns-python/test.py Normal file
View File

@@ -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")