BIND 10 master, updated. c24553e21fe01121a42e2136d0a1230d75812b27 [master] Merge branch 'trac983'
BIND 10 source code commits
bind10-changes at lists.isc.org
Thu Jul 14 08:38:54 UTC 2011
The branch, master has been updated
via c24553e21fe01121a42e2136d0a1230d75812b27 (commit)
via 151ea34890984f1fb2404df848c1dcbf3e61d765 (commit)
via 3b4b066b5d1c3726f51e52fee52c317a3ae3f9e3 (commit)
via cca39b307de50546d7e3c4cd9fe4c2435223bf21 (commit)
via dffeeebd09195ad602090501c8c9b05b55885596 (commit)
via 673a619cd628130b0506a5d3669fd6a4d139c790 (commit)
via f8092952b50ef238e2ffc63ccb6d17a469f22966 (commit)
via 7cb53c7b33c41bc8c5d76c6994caae800692108d (commit)
via d0df4daafee6703a7b52609b5681846f83310182 (commit)
via d23f84732df2786fad5bf31f3446e0e088d941ec (commit)
via fd2daaa2c1a27140568cf5a4f04baf57682214d2 (commit)
via 78942e3fc11f22f1bdbbd8fdd629691d5c510a55 (commit)
via 8945eccce758dd466ac42c6521a3fc4ada5a9226 (commit)
via f29890eed7bad4aead5e95cfa6aae147287a0b10 (commit)
via 7469b1f920d47306f87aab0e2fa0533903bc61af (commit)
via b327d9aac9bfd87e175d03421069ae679087dd00 (commit)
via e090ab50879c15c850df8e8145f01d39dbd6b87b (commit)
via 832cd1c032222fec662f9320e6f564f55b75cc8a (commit)
via 7b2691afaea9ccefa2db073f8a717e003f2ad07e (commit)
via bfbd97a0fa52c122c6d0ab5239524b7be58b62be (commit)
via 0e6771fbedb4081dc867e845b541023a673a1da6 (commit)
via b9bc51b44f59c9e93eaa5a21ae7658a320741e08 (commit)
via 0252f1b276eaf8e72d42510546f594b9d0703a58 (commit)
via 026699b978f21466cdd20b09dba3fe0448e0592f (commit)
via 6ec7cbb9976f68a0ca265e72dadfbb867d59581f (commit)
via 8faa21b81fde5c30ca1df72739b9a0dd27005402 (commit)
via 159caa607fc11e4b7c1b5efcbb28d0ebf5e99903 (commit)
via 2bfafa08c054715e6163a91da334e1e4fa780740 (commit)
via 7819ba75f5c170afa06a5a27b8c64e13ae094b74 (commit)
via cf5ed0e7c52e8a97ec48525ee2181e31aaa4184a (commit)
via 47286f0bb01c6dbe0e48fc080f931d7b93e22063 (commit)
via 191329567e3cab6ae2f0752f2e70880b8d97271a (commit)
via 99a63ce0a562d9b26ef1ad68b9426d91e6ec35d7 (commit)
via cf4605bebe7b0266f21376b796d4863aca01f63e (commit)
from 8cec4587428e4fba8f5cf8791f19f8373212b250 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit c24553e21fe01121a42e2136d0a1230d75812b27
Merge: 8cec458 151ea34
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Thu Jul 14 01:25:29 2011 -0700
[master] Merge branch 'trac983'
-----------------------------------------------------------------------
Summary of changes:
configure.ac | 12 +
src/lib/Makefile.am | 4 +-
src/lib/acl/acl.h | 3 +
src/lib/acl/loader.h | 30 +-
src/lib/python/isc/Makefile.am | 2 +-
src/lib/python/isc/acl/Makefile.am | 45 +++
src/lib/python/isc/acl/__init__.py | 11 +
src/lib/python/isc/acl/acl.cc | 80 +++++
.../isc/{testutils/__init__.py => acl/acl.py} | 16 +-
src/lib/python/isc/acl/acl_inc.cc | 16 +
src/lib/python/isc/acl/dns.cc | 135 +++++++++
src/lib/python/isc/acl/dns.h | 52 ++++
src/lib/python/isc/{log/__init__.py => acl/dns.py} | 8 +-
src/lib/python/isc/acl/dns_requestacl_inc.cc | 33 ++
src/lib/python/isc/acl/dns_requestacl_python.cc | 184 +++++++++++
.../isc/acl/dns_requestacl_python.h} | 32 ++-
src/lib/python/isc/acl/dns_requestcontext_inc.cc | 30 ++
.../python/isc/acl/dns_requestcontext_python.cc | 319 ++++++++++++++++++++
.../isc/acl/dns_requestcontext_python.h} | 39 ++--
src/lib/python/isc/acl/dns_requestloader_inc.cc | 87 ++++++
src/lib/python/isc/acl/dns_requestloader_python.cc | 270 +++++++++++++++++
.../isc/acl/dns_requestloader_python.h} | 25 +-
src/lib/python/isc/acl/dnsacl_inc.cc | 17 +
.../python/isc/acl}/tests/Makefile.am | 11 +-
.../__init__.py => acl/tests/acl_test.py} | 16 +-
src/lib/python/isc/acl/tests/dns_test.py | 280 +++++++++++++++++
src/lib/util/python/pycppwrapper_util.h | 29 ++-
src/lib/util/python/wrapper_template.cc | 4 +-
28 files changed, 1712 insertions(+), 78 deletions(-)
create mode 100644 src/lib/python/isc/acl/Makefile.am
create mode 100644 src/lib/python/isc/acl/__init__.py
create mode 100644 src/lib/python/isc/acl/acl.cc
copy src/lib/python/isc/{testutils/__init__.py => acl/acl.py} (65%)
create mode 100644 src/lib/python/isc/acl/acl_inc.cc
create mode 100644 src/lib/python/isc/acl/dns.cc
create mode 100644 src/lib/python/isc/acl/dns.h
copy src/lib/python/isc/{log/__init__.py => acl/dns.py} (90%)
create mode 100644 src/lib/python/isc/acl/dns_requestacl_inc.cc
create mode 100644 src/lib/python/isc/acl/dns_requestacl_python.cc
copy src/lib/{dns/python/tsig_python.h => python/isc/acl/dns_requestacl_python.h} (62%)
create mode 100644 src/lib/python/isc/acl/dns_requestcontext_inc.cc
create mode 100644 src/lib/python/isc/acl/dns_requestcontext_python.cc
copy src/lib/{dns/python/tsigkey_python.h => python/isc/acl/dns_requestcontext_python.h} (59%)
create mode 100644 src/lib/python/isc/acl/dns_requestloader_inc.cc
create mode 100644 src/lib/python/isc/acl/dns_requestloader_python.cc
copy src/lib/{dns/python/tsig_python.h => python/isc/acl/dns_requestloader_python.h} (74%)
create mode 100644 src/lib/python/isc/acl/dnsacl_inc.cc
copy src/{bin/cfgmgr/plugins => lib/python/isc/acl}/tests/Makefile.am (54%)
copy src/lib/python/isc/{testutils/__init__.py => acl/tests/acl_test.py} (67%)
create mode 100644 src/lib/python/isc/acl/tests/dns_test.py
-----------------------------------------------------------------------
diff --git a/configure.ac b/configure.ac
index 0253e3f..48a79d2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -139,6 +139,16 @@ else
AC_SUBST(pkgpyexecdir)
fi
+# We need to store the default pyexecdir in a separate variable so that
+# we can specify in Makefile.am the install directory of various BIND 10
+# python scripts and loadable modules; in Makefile.am we cannot replace
+# $(pyexecdir) using itself, e.g, this doesn't work:
+# pyexecdir = $(pyexecdir)/isc/some_module
+# The separate variable makes this setup possible as follows:
+# pyexecdir = $(PYTHON_SITEPKG_DIR)/isc/some_module
+PYTHON_SITEPKG_DIR=${pyexecdir}
+AC_SUBST(PYTHON_SITEPKG_DIR)
+
# Check for python development environments
if test -x ${PYTHON}-config; then
PYTHON_INCLUDES=`${PYTHON}-config --includes`
@@ -810,6 +820,8 @@ AC_CONFIG_FILES([Makefile
src/lib/cc/tests/Makefile
src/lib/python/Makefile
src/lib/python/isc/Makefile
+ src/lib/python/isc/acl/Makefile
+ src/lib/python/isc/acl/tests/Makefile
src/lib/python/isc/util/Makefile
src/lib/python/isc/util/tests/Makefile
src/lib/python/isc/datasrc/Makefile
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
index f4bef6b..5adf150 100644
--- a/src/lib/Makefile.am
+++ b/src/lib/Makefile.am
@@ -1,3 +1,3 @@
-SUBDIRS = exceptions util log cryptolink dns cc config python xfr \
+SUBDIRS = exceptions util log cryptolink dns cc config acl python xfr \
bench asiolink asiodns nsas cache resolve testutils datasrc \
- acl server_common
+ server_common
diff --git a/src/lib/acl/acl.h b/src/lib/acl/acl.h
index 998b2b0..76039c9 100644
--- a/src/lib/acl/acl.h
+++ b/src/lib/acl/acl.h
@@ -88,8 +88,11 @@ public:
* the context against conditions and if it matches, returns the
* action that belongs to the first matched entry or default action
* if nothing matches.
+ *
* \param context The thing that should be checked. It is directly
* passed to the checks.
+ *
+ * \return The action for the ACL entry that first matches the context.
*/
const Action& execute(const Context& context) const {
const typename Entries::const_iterator end(entries_.end());
diff --git a/src/lib/acl/loader.h b/src/lib/acl/loader.h
index c86373e..f60b144 100644
--- a/src/lib/acl/loader.h
+++ b/src/lib/acl/loader.h
@@ -101,21 +101,21 @@ BasicAction defaultActionLoader(data::ConstElementPtr action);
*
* An ACL definition looks like this:
* \verbatim
- * [
- * {
- * "action": "ACCEPT",
- * "match-type": <parameter>
- * },
- * {
- * "action": "REJECT",
- * "match-type": <parameter>
- * "another-match-type": [<parameter1>, <parameter2>]
-* },
-* {
-* "action": "DROP"
-* }
- * ]
- * \endverbatim
+ [
+ {
+ "action": "ACCEPT",
+ "match-type": <parameter>
+ },
+ {
+ "action": "REJECT",
+ "match-type": <parameter>,
+ "another-match-type": [<parameter1>, <parameter2>]
+ },
+ {
+ "action": "DROP"
+ }
+ ]
+ \endverbatim
*
* This is a list of elements. Each element must have an "action"
* entry/keyword. That one specifies which action is returned if this
diff --git a/src/lib/python/isc/Makefile.am b/src/lib/python/isc/Makefile.am
index bfc5a91..b391c1e 100644
--- a/src/lib/python/isc/Makefile.am
+++ b/src/lib/python/isc/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = datasrc cc config log net notify util testutils
+SUBDIRS = datasrc cc config log net notify util testutils acl
python_PYTHON = __init__.py
diff --git a/src/lib/python/isc/acl/Makefile.am b/src/lib/python/isc/acl/Makefile.am
new file mode 100644
index 0000000..cabc0a3
--- /dev/null
+++ b/src/lib/python/isc/acl/Makefile.am
@@ -0,0 +1,45 @@
+SUBDIRS = . tests
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+python_PYTHON = __init__.py
+pythondir = $(PYTHON_SITEPKG_DIR)/isc/acl
+
+pyexec_LTLIBRARIES = acl.la dns.la
+pyexecdir = $(PYTHON_SITEPKG_DIR)/isc/acl
+
+acl_la_SOURCES = acl.cc
+acl_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES)
+acl_la_LDFLAGS = $(PYTHON_LDFLAGS)
+acl_la_CXXFLAGS = $(AM_CXXFLAGS) $(PYTHON_CXXFLAGS)
+
+dns_la_SOURCES = dns.h dns.cc dns_requestacl_python.h dns_requestacl_python.cc
+dns_la_SOURCES += dns_requestcontext_python.h dns_requestcontext_python.cc
+dns_la_SOURCES += dns_requestloader_python.h dns_requestloader_python.cc
+dns_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES)
+dns_la_LDFLAGS = $(PYTHON_LDFLAGS)
+# Note: PYTHON_CXXFLAGS may have some -Wno... workaround, which must be
+# placed after -Wextra defined in AM_CXXFLAGS
+dns_la_CXXFLAGS = $(AM_CXXFLAGS) $(PYTHON_CXXFLAGS)
+
+# Python prefers .so, while some OSes (specifically MacOS) use a different
+# suffix for dynamic objects. -module is necessary to work this around.
+acl_la_LDFLAGS += -module
+acl_la_LIBADD = $(top_builddir)/src/lib/acl/libacl.la
+acl_la_LIBADD += $(PYTHON_LIB)
+
+dns_la_LDFLAGS += -module
+dns_la_LIBADD = $(top_builddir)/src/lib/acl/libdnsacl.la
+dns_la_LIBADD += $(PYTHON_LIB)
+
+EXTRA_DIST = acl.py dns.py
+EXTRA_DIST += acl_inc.cc
+EXTRA_DIST += dnsacl_inc.cc dns_requestacl_inc.cc dns_requestcontext_inc.cc
+EXTRA_DIST += dns_requestloader_inc.cc
+
+CLEANDIRS = __pycache__
+
+clean-local:
+ rm -rf $(CLEANDIRS)
diff --git a/src/lib/python/isc/acl/__init__.py b/src/lib/python/isc/acl/__init__.py
new file mode 100644
index 0000000..d9b2838
--- /dev/null
+++ b/src/lib/python/isc/acl/__init__.py
@@ -0,0 +1,11 @@
+"""
+Here are function and classes for manipulating access control lists.
+"""
+
+# The DNS ACL loader would need the json module. Make sure it's imported
+# beforehand.
+import json
+
+# Other ACL modules highly depends on the main acl sub module, so it's
+# explicitly imported here.
+import isc.acl.acl
diff --git a/src/lib/python/isc/acl/acl.cc b/src/lib/python/isc/acl/acl.cc
new file mode 100644
index 0000000..6517a12
--- /dev/null
+++ b/src/lib/python/isc/acl/acl.cc
@@ -0,0 +1,80 @@
+// Copyright (C) 2011 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.
+
+#include <Python.h>
+
+#include <util/python/pycppwrapper_util.h>
+
+#include <acl/acl.h>
+
+using namespace isc::util::python;
+
+#include "acl_inc.cc"
+
+namespace {
+// Commonly used Python exception objects. Right now the acl module consists
+// of only one .cc file, so we hide them in an unnamed namespace. If and when
+// we extend this module with multiple .cc files, we should move them to
+// a named namespace, say isc::acl::python, and declare them in a separate
+// header file.
+PyObject* po_ACLError;
+PyObject* po_LoaderError;
+}
+
+namespace {
+PyModuleDef acl = {
+ { PyObject_HEAD_INIT(NULL) NULL, 0, NULL},
+ "isc.acl.acl",
+ acl_doc,
+ -1,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+} // end of unnamed namespace
+
+PyMODINIT_FUNC
+PyInit_acl(void) {
+ PyObject* mod = PyModule_Create(&acl);
+ if (mod == NULL) {
+ return (NULL);
+ }
+
+ try {
+ po_ACLError = PyErr_NewException("isc.acl.Error", NULL, NULL);
+ PyObjectContainer(po_ACLError).installToModule(mod, "Error");
+
+ po_LoaderError = PyErr_NewException("isc.acl.LoaderError", NULL, NULL);
+ PyObjectContainer(po_LoaderError).installToModule(mod, "LoaderError");
+
+ // Install module constants. Note that we can let Py_BuildValue
+ // "steal" the references to these object (by specifying false to
+ // installToModule), because, unlike the exception cases above,
+ // we don't have corresponding C++ variables (see the note in
+ // pycppwrapper_util for more details).
+ PyObjectContainer(Py_BuildValue("I", isc::acl::ACCEPT)).
+ installToModule(mod, "ACCEPT", false);
+ PyObjectContainer(Py_BuildValue("I", isc::acl::REJECT)).
+ installToModule(mod, "REJECT", false);
+ PyObjectContainer(Py_BuildValue("I", isc::acl::DROP)).
+ installToModule(mod, "DROP", false);
+ } catch (...) {
+ Py_DECREF(mod);
+ return (NULL);
+ }
+
+ return (mod);
+}
diff --git a/src/lib/python/isc/acl/acl.py b/src/lib/python/isc/acl/acl.py
new file mode 100644
index 0000000..804d78b
--- /dev/null
+++ b/src/lib/python/isc/acl/acl.py
@@ -0,0 +1,29 @@
+# Copyright (C) 2011 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.
+
+# This file is not installed; The .so version will be installed into the right
+# place at installation time.
+# This helper script is only to find it in the .libs directory when we run
+# as a test or from the build directory.
+
+import os
+import sys
+
+for base in sys.path[:]:
+ bindingdir = os.path.join(base, 'isc/acl/.libs')
+ if os.path.exists(bindingdir):
+ sys.path.insert(0, bindingdir)
+
+from acl import *
diff --git a/src/lib/python/isc/acl/acl_inc.cc b/src/lib/python/isc/acl/acl_inc.cc
new file mode 100644
index 0000000..a9f7c9d
--- /dev/null
+++ b/src/lib/python/isc/acl/acl_inc.cc
@@ -0,0 +1,16 @@
+namespace {
+const char* const acl_doc = "\
+Implementation module for ACL operations\n\n\
+This module provides Python bindings for the C++ classes in the\n\
+isc::acl namespace.\n\
+\n\
+Integer constants:\n\
+\n\
+ACCEPT, REJECT, DROP -- Default actions an ACL could perform.\n\
+ These are the commonly used actions in specific ACLs.\n\
+ It is possible to specify any other values, as the ACL class does\n\
+ nothing about them, but these look reasonable, so they are provided\n\
+ for convenience. It is not specified what exactly these mean and it's\n\
+ up to whoever uses them.\n\
+";
+} // unnamed namespace
diff --git a/src/lib/python/isc/acl/dns.cc b/src/lib/python/isc/acl/dns.cc
new file mode 100644
index 0000000..351a8b3
--- /dev/null
+++ b/src/lib/python/isc/acl/dns.cc
@@ -0,0 +1,135 @@
+// Copyright (C) 2011 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.
+
+#include <Python.h>
+
+#include <stdexcept>
+#include <boost/shared_ptr.hpp>
+
+#include <util/python/pycppwrapper_util.h>
+
+#include <cc/data.h>
+
+#include <acl/acl.h>
+#include <acl/dns.h>
+
+#include "dns.h"
+#include "dns_requestcontext_python.h"
+#include "dns_requestacl_python.h"
+#include "dns_requestloader_python.h"
+
+using namespace std;
+using boost::shared_ptr;
+using namespace isc::util::python;
+using namespace isc::data;
+using namespace isc::acl::dns;
+using namespace isc::acl::dns::python;
+
+#include "dnsacl_inc.cc"
+
+namespace {
+// This is a Python binding object corresponding to the singleton loader used
+// in the C++ version of the library.
+// We can define it as a pure object rather than through an accessor function,
+// because in Python we can ensure it has been created and initialized
+// in the module initializer by the time it's actually used.
+s_RequestLoader* po_REQUEST_LOADER;
+
+PyMethodDef methods[] = {
+ { NULL, NULL, 0, NULL }
+};
+
+PyModuleDef dnsacl = {
+ { PyObject_HEAD_INIT(NULL) NULL, 0, NULL},
+ "isc.acl.dns",
+ dnsacl_doc,
+ -1,
+ methods,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+} // end of unnamed namespace
+
+namespace isc {
+namespace acl {
+namespace dns {
+namespace python {
+PyObject*
+getACLException(const char* ex_name) {
+ PyObject* ex_obj = NULL;
+
+ PyObject* acl_module = PyImport_AddModule("isc.acl.acl");
+ if (acl_module != NULL) {
+ PyObject* acl_dict = PyModule_GetDict(acl_module);
+ if (acl_dict != NULL) {
+ ex_obj = PyDict_GetItemString(acl_dict, ex_name);
+ }
+ }
+
+ if (ex_obj == NULL) {
+ ex_obj = PyExc_RuntimeError;
+ }
+ return (ex_obj);
+}
+}
+}
+}
+}
+
+PyMODINIT_FUNC
+PyInit_dns(void) {
+ PyObject* mod = PyModule_Create(&dnsacl);
+ if (mod == NULL) {
+ return (NULL);
+ }
+
+ if (!initModulePart_RequestContext(mod)) {
+ Py_DECREF(mod);
+ return (NULL);
+ }
+ if (!initModulePart_RequestACL(mod)) {
+ Py_DECREF(mod);
+ return (NULL);
+ }
+ if (!initModulePart_RequestLoader(mod)) {
+ Py_DECREF(mod);
+ return (NULL);
+ }
+
+ // Module constants
+ try {
+ if (po_REQUEST_LOADER == NULL) {
+ po_REQUEST_LOADER = static_cast<s_RequestLoader*>(
+ requestloader_type.tp_alloc(&requestloader_type, 0));
+ }
+ if (po_REQUEST_LOADER != NULL) {
+ // We gain and keep our own reference to the singleton object
+ // for the same reason as that for exception objects (see comments
+ // in pycppwrapper_util for more details). Note also that we don't
+ // bother to release the reference even if exception is thrown
+ // below (in fact, we cannot delete the singleton loader).
+ po_REQUEST_LOADER->cppobj = &getRequestLoader();
+ Py_INCREF(po_REQUEST_LOADER);
+ }
+ PyObjectContainer(po_REQUEST_LOADER).installToModule(mod,
+ "REQUEST_LOADER");
+ } catch (...) {
+ Py_DECREF(mod);
+ return (NULL);
+ }
+
+ return (mod);
+}
diff --git a/src/lib/python/isc/acl/dns.h b/src/lib/python/isc/acl/dns.h
new file mode 100644
index 0000000..76849c5
--- /dev/null
+++ b/src/lib/python/isc/acl/dns.h
@@ -0,0 +1,52 @@
+// Copyright (C) 2011 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_ACL_DNS_H
+#define __PYTHON_ACL_DNS_H 1
+
+#include <Python.h>
+
+namespace isc {
+namespace acl {
+namespace dns {
+namespace python {
+
+// Return a Python exception object of the given name (ex_name) defined in
+// the isc.acl.acl loadable module.
+//
+// Since the acl module is a different binary image and is loaded separately
+// from the dns module, it would be very tricky to directly access to
+// C/C++ symbols defined in that module. So we get access to these object
+// using the Python interpretor through this wrapper function.
+//
+// The __init__.py file should ensure isc.acl.acl has been loaded by the time
+// whenever this function is called, and there shouldn't be any operation
+// within this function that can fail (such as dynamic memory allocation),
+// so this function should always succeed. Yet there may be an overlooked
+// failure mode, perhaps due to a bug in the binding implementation, or
+// due to invalid usage. As a last resort for such cases, this function
+// returns PyExc_RuntimeError (a C binding of Python's RuntimeError) should
+// it encounters an unexpected failure.
+extern PyObject* getACLException(const char* ex_name);
+
+} // namespace python
+} // namespace dns
+} // namespace acl
+} // namespace isc
+
+#endif // __PYTHON_ACL_DNS_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/python/isc/acl/dns.py b/src/lib/python/isc/acl/dns.py
new file mode 100644
index 0000000..8070559
--- /dev/null
+++ b/src/lib/python/isc/acl/dns.py
@@ -0,0 +1,33 @@
+# Copyright (C) 2011 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.
+
+# This file is not installed. The log.so is installed into the right place.
+# It is only to find it in the .libs directory when we run as a test or
+# from the build directory.
+# But as nobody gives us the builddir explicitly (and we can't use generation
+# from .in file, as it would put us into the builddir and we wouldn't be found)
+# we guess from current directory. Any idea for something better? This should
+# be enough for the tests, but would it work for B10_FROM_SOURCE as well?
+# Should we look there? Or define something in bind10_config?
+
+import os
+import sys
+
+for base in sys.path[:]:
+ bindingdir = os.path.join(base, 'isc/acl/.libs')
+ if os.path.exists(bindingdir):
+ sys.path.insert(0, bindingdir)
+
+from dns import *
diff --git a/src/lib/python/isc/acl/dns_requestacl_inc.cc b/src/lib/python/isc/acl/dns_requestacl_inc.cc
new file mode 100644
index 0000000..673fa23
--- /dev/null
+++ b/src/lib/python/isc/acl/dns_requestacl_inc.cc
@@ -0,0 +1,33 @@
+namespace {
+const char* const RequestACL_doc = "\
+The DNS Request ACL.\n\
+\n\
+It holds bunch of ordered entries, each one consisting of a check for\n\
+a given DNS Request context and an action, which is one of ACCEPT,\n\
+REJECT, or DROP, as defined in the isc.acl.acl module.\n\
+The checks are tested in the order and first match counts.\n\
+\n\
+A RequestACL object cannot be constructed directly; an application\n\
+must use isc.acl.dns.load_request_acl() to create a RequestACL object.\n\
+\n\
+";
+
+const char* const RequestACL_execute_doc = "\
+execute(context) -> action \n\
+\n\
+The returned action is one of ACCEPT, REJECT or DROP as defined in\n\
+the isc.acl.acl module.\n\
+\n\
+This is the function that takes the ACL entries one by one, checks the\n\
+context against conditions and if it matches, returns the action that\n\
+belongs to the first matched entry or default action if nothing\n\
+matches.\n\
+\n\
+Parameters:\n\
+ context The thing that should be checked. It is directly passed\n\
+ to the checks.\n\
+\n\
+Return Value(s): The action for the ACL entry that first matches the\n\
+context.\n\
+";
+} // unnamed namespace
diff --git a/src/lib/python/isc/acl/dns_requestacl_python.cc b/src/lib/python/isc/acl/dns_requestacl_python.cc
new file mode 100644
index 0000000..5e5acea
--- /dev/null
+++ b/src/lib/python/isc/acl/dns_requestacl_python.cc
@@ -0,0 +1,184 @@
+// Copyright (C) 2011 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 <acl/acl.h>
+#include <acl/dns.h>
+
+#include "dns.h"
+#include "dns_requestacl_python.h"
+#include "dns_requestcontext_python.h"
+
+using namespace std;
+using namespace isc::util::python;
+using namespace isc::acl;
+using namespace isc::acl::dns;
+using namespace isc::acl::dns::python;
+
+//
+// Definition of the classes
+//
+
+// For each class, we need a struct, a helper functions (init, destroy,
+// and static wrappers around the methods we export), a list of methods,
+// and a type description
+
+//
+// RequestACL
+//
+
+// Trivial constructor.
+s_RequestACL::s_RequestACL() {}
+
+// Import pydoc text
+#include "dns_requestacl_inc.cc"
+
+namespace {
+int
+RequestACL_init(PyObject*, PyObject*, PyObject*) {
+ PyErr_SetString(getACLException("Error"),
+ "RequestACL cannot be directly constructed");
+ return (-1);
+}
+
+void
+RequestACL_destroy(PyObject* po_self) {
+ s_RequestACL* const self = static_cast<s_RequestACL*>(po_self);
+ self->cppobj.reset();
+ Py_TYPE(self)->tp_free(self);
+}
+
+PyObject*
+RequestACL_execute(PyObject* po_self, PyObject* args) {
+ s_RequestACL* const self = static_cast<s_RequestACL*>(po_self);
+
+ try {
+ const s_RequestContext* po_context;
+ if (PyArg_ParseTuple(args, "O!", &requestcontext_type, &po_context)) {
+ const BasicAction action =
+ self->cppobj->execute(*po_context->cppobj);
+ return (Py_BuildValue("I", action));
+ }
+ } catch (const exception& ex) {
+ const string ex_what = "Failed to execute ACL: " + string(ex.what());
+ PyErr_SetString(getACLException("Error"), ex_what.c_str());
+ } catch (...) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "Unexpected exception in executing ACL");
+ }
+
+ 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 RequestACL_methods[] = {
+ { "execute", RequestACL_execute, METH_VARARGS, RequestACL_execute_doc },
+ { NULL, NULL, 0, NULL }
+};
+} // end of unnamed namespace
+
+namespace isc {
+namespace acl {
+namespace dns {
+namespace python {
+// This defines the complete type for reflection in python and
+// parsing of PyObject* to s_RequestACL
+// Most of the functions are not actually implemented and NULL here.
+PyTypeObject requestacl_type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "isc.acl.dns.RequestACL",
+ sizeof(s_RequestACL), // tp_basicsize
+ 0, // tp_itemsize
+ RequestACL_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
+ RequestACL_doc,
+ NULL, // tp_traverse
+ NULL, // tp_clear
+ NULL, // tp_richcompare
+ 0, // tp_weaklistoffset
+ NULL, // tp_iter
+ NULL, // tp_iternext
+ RequestACL_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
+ RequestACL_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
+};
+
+bool
+initModulePart_RequestACL(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(&requestacl_type) < 0) {
+ return (false);
+ }
+ void* p = &requestacl_type;
+ if (PyModule_AddObject(mod, "RequestACL", static_cast<PyObject*>(p)) < 0) {
+ return (false);
+ }
+ Py_INCREF(&requestacl_type);
+
+ return (true);
+}
+} // namespace python
+} // namespace dns
+} // namespace acl
+} // namespace isc
diff --git a/src/lib/python/isc/acl/dns_requestacl_python.h b/src/lib/python/isc/acl/dns_requestacl_python.h
new file mode 100644
index 0000000..8f7ad8a
--- /dev/null
+++ b/src/lib/python/isc/acl/dns_requestacl_python.h
@@ -0,0 +1,53 @@
+// Copyright (C) 2011 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_REQUESTACL_H
+#define __PYTHON_REQUESTACL_H 1
+
+#include <Python.h>
+
+#include <boost/shared_ptr.hpp>
+
+#include <acl/dns.h>
+
+namespace isc {
+namespace acl {
+namespace dns {
+namespace python {
+
+// The s_* Class simply covers one instantiation of the object
+class s_RequestACL : public PyObject {
+public:
+ s_RequestACL();
+
+ // We don't have to use a shared pointer for its original purposes as
+ // the python object maintains reference counters itself. But the
+ // underlying C++ API only exposes a shared pointer for the ACL objects,
+ // so we store it in that form.
+ boost::shared_ptr<RequestACL> cppobj;
+};
+
+extern PyTypeObject requestacl_type;
+
+bool initModulePart_RequestACL(PyObject* mod);
+
+} // namespace python
+} // namespace dns
+} // namespace acl
+} // namespace isc
+#endif // __PYTHON_REQUESTACL_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/python/isc/acl/dns_requestcontext_inc.cc b/src/lib/python/isc/acl/dns_requestcontext_inc.cc
new file mode 100644
index 0000000..9e80e1f
--- /dev/null
+++ b/src/lib/python/isc/acl/dns_requestcontext_inc.cc
@@ -0,0 +1,30 @@
+namespace {
+const char* const RequestContext_doc = "\
+DNS request to be checked.\n\
+\n\
+This plays the role of ACL context for the RequestACL object.\n\
+\n\
+Based on the minimalist philosophy, the initial implementation only\n\
+maintains the remote (source) IP address of the request. The plan is\n\
+to add more parameters of the request. A scheduled next step is to\n\
+support the TSIG key (if it's included in the request). Other\n\
+possibilities are the local (destination) IP address, the remote and\n\
+local port numbers, various fields of the DNS request (e.g. a\n\
+particular header flag value).\n\
+\n\
+RequestContext(remote_address)\n\
+\n\
+ In this initial implementation, the constructor only takes a\n\
+ remote IP address in the form of a socket address as used in the\n\
+ Python socket module.\n\
+\n\
+ Exceptions:\n\
+ isc.acl.ACLError Normally shouldn't happen, but still possible\n\
+ for unexpected errors such as memory allocation\n\
+ failure or an invalid address text being passed.\n\
+\n\
+ Parameters:\n\
+ remote_address The remote IP address\n\
+\n\
+";
+} // unnamed namespace
diff --git a/src/lib/python/isc/acl/dns_requestcontext_python.cc b/src/lib/python/isc/acl/dns_requestcontext_python.cc
new file mode 100644
index 0000000..6c63b59
--- /dev/null
+++ b/src/lib/python/isc/acl/dns_requestcontext_python.cc
@@ -0,0 +1,319 @@
+// Copyright (C) 2011 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 <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <string.h>
+
+#include <cassert>
+#include <memory>
+#include <string>
+#include <sstream>
+#include <stdexcept>
+
+#include <boost/scoped_ptr.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include <exceptions/exceptions.h>
+
+#include <util/python/pycppwrapper_util.h>
+
+#include <acl/dns.h>
+#include <acl/ip_check.h>
+
+#include "dns.h"
+#include "dns_requestcontext_python.h"
+
+using namespace std;
+using boost::scoped_ptr;
+using boost::lexical_cast;
+using namespace isc;
+using namespace isc::util::python;
+using namespace isc::acl::dns;
+using namespace isc::acl::dns::python;
+
+namespace isc {
+namespace acl {
+namespace dns {
+namespace python {
+
+struct s_RequestContext::Data {
+ // The constructor. Currently it only accepts the information of the
+ // request source address, and contains all necessary logic in the body
+ // of the constructor. As it's extended we may have refactor it by
+ // introducing helper methods.
+ Data(const char* const remote_addr, const unsigned short remote_port) {
+ struct addrinfo hints, *res;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_protocol = IPPROTO_UDP;
+ hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
+ const int error(getaddrinfo(remote_addr,
+ lexical_cast<string>(remote_port).c_str(),
+ &hints, &res));
+ if (error != 0) {
+ isc_throw(InvalidParameter, "Failed to convert [" << remote_addr
+ << "]:" << remote_port << ", " << gai_strerror(error));
+ }
+ assert(sizeof(remote_ss) > res->ai_addrlen);
+ memcpy(&remote_ss, res->ai_addr, res->ai_addrlen);
+ remote_salen = res->ai_addrlen;
+ freeaddrinfo(res);
+
+ remote_ipaddr.reset(new IPAddress(getRemoteSockaddr()));
+ }
+
+ // A convenient type converter from sockaddr_storage to sockaddr
+ const struct sockaddr& getRemoteSockaddr() const {
+ const void* p = &remote_ss;
+ return (*static_cast<const struct sockaddr*>(p));
+ }
+
+ // The remote (source) IP address the request. Note that it needs
+ // a reference to remote_ss. That's why the latter is stored within
+ // this structure.
+ scoped_ptr<IPAddress> remote_ipaddr;
+
+ // The effective length of remote_ss. It's necessary for getnameinf()
+ // called from sockaddrToText (__str__ backend).
+ socklen_t remote_salen;
+
+private:
+ struct sockaddr_storage remote_ss;
+};
+
+} // namespace python
+} // namespace dns
+} // namespace acl
+} // namespace isc
+
+
+//
+// Definition of the classes
+//
+
+// For each class, we need a struct, a helper functions (init, destroy,
+// and static wrappers around the methods we export), a list of methods,
+// and a type description
+
+//
+// RequestContext
+//
+
+// Trivial constructor.
+s_RequestContext::s_RequestContext() : cppobj(NULL), data_(NULL) {
+}
+
+// Import pydoc text
+#include "dns_requestcontext_inc.cc"
+
+namespace {
+// 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 RequestContext_methods[] = {
+ { NULL, NULL, 0, NULL }
+};
+
+int
+RequestContext_init(PyObject* po_self, PyObject* args, PyObject*) {
+ s_RequestContext* const self = static_cast<s_RequestContext*>(po_self);
+
+ try {
+ // In this initial implementation, the constructor is simply: It
+ // takes a single parameter, which should be a Python socket address
+ // object. For IPv4, it's ('address test', numeric_port); for IPv6,
+ // it's ('address text', num_port, num_flowid, num_zoneid).
+ // Below, we parse the argument in the most straightforward way.
+ // As the constructor becomes more complicated, we should probably
+ // make it more structural (for example, we should first retrieve
+ // the socket address as a PyObject, and parse it recursively)
+
+ const char* remote_addr;
+ unsigned short remote_port;
+ unsigned int remote_flowinfo; // IPv6 only, unused here
+ unsigned int remote_zoneid; // IPv6 only, unused here
+
+ if (PyArg_ParseTuple(args, "(sH)", &remote_addr, &remote_port) ||
+ PyArg_ParseTuple(args, "(sHII)", &remote_addr, &remote_port,
+ &remote_flowinfo, &remote_zoneid))
+ {
+ // We need to clear the error in case the first call to PareTuple
+ // fails.
+ PyErr_Clear();
+
+ auto_ptr<s_RequestContext::Data> dataptr(
+ new s_RequestContext::Data(remote_addr, remote_port));
+ self->cppobj = new RequestContext(*dataptr->remote_ipaddr);
+ self->data_ = dataptr.release();
+ return (0);
+ }
+ } catch (const exception& ex) {
+ const string ex_what = "Failed to construct RequestContext object: " +
+ string(ex.what());
+ PyErr_SetString(getACLException("Error"), ex_what.c_str());
+ return (-1);
+ } catch (...) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "Unexpected exception in constructing RequestContext");
+ return (-1);
+ }
+
+ PyErr_SetString(PyExc_TypeError,
+ "Invalid arguments to RequestContext constructor");
+
+ return (-1);
+}
+
+void
+RequestContext_destroy(PyObject* po_self) {
+ s_RequestContext* const self = static_cast<s_RequestContext*>(po_self);
+
+ delete self->cppobj;
+ delete self->data_;
+ Py_TYPE(self)->tp_free(self);
+}
+
+// A helper function for __str__()
+string
+sockaddrToText(const struct sockaddr& sa, socklen_t sa_len) {
+ char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
+ if (getnameinfo(&sa, sa_len, hbuf, sizeof(hbuf), sbuf, sizeof(sbuf),
+ NI_NUMERICHOST | NI_NUMERICSERV)) {
+ // In this context this should never fail.
+ isc_throw(Unexpected, "Unexpected failure in getnameinfo");
+ }
+
+ return ("[" + string(hbuf) + "]:" + string(sbuf));
+}
+
+// for the __str__() method. This method is provided mainly for internal
+// testing.
+PyObject*
+RequestContext_str(PyObject* po_self) {
+ const s_RequestContext* const self =
+ static_cast<s_RequestContext*>(po_self);
+
+ try {
+ stringstream objss;
+ objss << "<" << requestcontext_type.tp_name << " object, "
+ << "remote_addr="
+ << sockaddrToText(self->data_->getRemoteSockaddr(),
+ self->data_->remote_salen) << ">";
+ return (Py_BuildValue("s", objss.str().c_str()));
+ } catch (const exception& ex) {
+ const string ex_what =
+ "Failed to convert RequestContext object to text: " +
+ string(ex.what());
+ PyErr_SetString(PyExc_RuntimeError, ex_what.c_str());
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError, "Unexpected failure in "
+ "converting RequestContext object to text");
+ }
+ return (NULL);
+}
+} // end of unnamed namespace
+
+namespace isc {
+namespace acl {
+namespace dns {
+namespace python {
+// This defines the complete type for reflection in python and
+// parsing of PyObject* to s_RequestContext
+// Most of the functions are not actually implemented and NULL here.
+PyTypeObject requestcontext_type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "isc.acl.dns.RequestContext",
+ sizeof(s_RequestContext), // tp_basicsize
+ 0, // tp_itemsize
+ RequestContext_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
+ RequestContext_str, // tp_str
+ NULL, // tp_getattro
+ NULL, // tp_setattro
+ NULL, // tp_as_buffer
+ Py_TPFLAGS_DEFAULT, // tp_flags
+ RequestContext_doc,
+ NULL, // tp_traverse
+ NULL, // tp_clear
+ NULL, // tp_richcompare
+ 0, // tp_weaklistoffset
+ NULL, // tp_iter
+ NULL, // tp_iternext
+ RequestContext_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
+ RequestContext_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
+};
+
+bool
+initModulePart_RequestContext(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(&requestcontext_type) < 0) {
+ return (false);
+ }
+ void* p = &requestcontext_type;
+ if (PyModule_AddObject(mod, "RequestContext",
+ static_cast<PyObject*>(p)) < 0) {
+ return (false);
+ }
+ Py_INCREF(&requestcontext_type);
+
+ return (true);
+}
+} // namespace python
+} // namespace dns
+} // namespace acl
+} // namespace isc
diff --git a/src/lib/python/isc/acl/dns_requestcontext_python.h b/src/lib/python/isc/acl/dns_requestcontext_python.h
new file mode 100644
index 0000000..766133b
--- /dev/null
+++ b/src/lib/python/isc/acl/dns_requestcontext_python.h
@@ -0,0 +1,54 @@
+// Copyright (C) 2011 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_REQUESTCONTEXT_H
+#define __PYTHON_REQUESTCONTEXT_H 1
+
+#include <Python.h>
+
+#include <acl/dns.h>
+
+namespace isc {
+namespace acl {
+namespace dns {
+namespace python {
+
+// The s_* Class simply covers one instantiation of the object
+class s_RequestContext : public PyObject {
+public:
+ s_RequestContext();
+ RequestContext* cppobj;
+
+ // This object needs to maintain some source data to construct the
+ // underlying RequestContext object throughout its lifetime.
+ // These are "public" so that it can be accessed in the python wrapper
+ // implementation, but essentially they should be private, and the
+ // implementation details are hidden.
+ struct Data;
+ Data* data_;
+};
+
+extern PyTypeObject requestcontext_type;
+
+bool initModulePart_RequestContext(PyObject* mod);
+
+} // namespace python
+} // namespace dns
+} // namespace acl
+} // namespace isc
+#endif // __PYTHON_REQUESTCONTEXT_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/python/isc/acl/dns_requestloader_inc.cc b/src/lib/python/isc/acl/dns_requestloader_inc.cc
new file mode 100644
index 0000000..a911275
--- /dev/null
+++ b/src/lib/python/isc/acl/dns_requestloader_inc.cc
@@ -0,0 +1,87 @@
+namespace {
+
+// Note: this is derived from the generic Loader class of the C++
+// implementation, but is slightly different from the original.
+// Be careful when you make further merge from the C++ document.
+const char* const RequestLoader_doc = "\
+Loader of DNS Request ACLs.\n\
+\n\
+The goal of this class is to convert JSON description of an ACL to\n\
+object of the ACL class (including the checks inside it).\n\
+\n\
+To allow any kind of checks to exist in the application, creators are\n\
+registered for the names of the checks (this feature is not yet\n\
+available for the python API).\n\
+\n\
+An ACL definition looks like this: [\n\
+ {\n\
+ \"action\": \"ACCEPT\",\n\
+ \"match-type\": <parameter>\n\
+ },\n\
+ {\n\
+ \"action\": \"REJECT\",\n\
+ \"match-type\": <parameter>,\n\
+ \"another-match-type\": [<parameter1>, <parameter2>]\n\
+ },\n\
+ {\n\
+ \"action\": \"DROP\"\n\
+ }\n\
+ ]\n\
+ \n\
+\n\
+This is a list of elements. Each element must have an \"action\"\n\
+entry/keyword. That one specifies which action is returned if this\n\
+element matches (the value of the key is passed to the action loader\n\
+(see the constructor), which is one of ACCEPT,\n\
+REJECT, or DROP, as defined in the isc.acl.acl module.\n\
+\n\
+The rest of the element are matches. The left side is the name of the\n\
+match type (for example \"from\" to match for source IP address).\n\
+The <parameter> is whatever is needed to describe the\n\
+match and depends on the match type, the loader passes it verbatim to\n\
+creator of that match type.\n\
+\n\
+There may be multiple match types in single element. In such case, all\n\
+of the matches must match for the element to take action (so, in the\n\
+second element, both \"match-type\" and \"another-match-type\" must be\n\
+satisfied). If there's no match in the element, the action is\n\
+taken/returned without conditions, every time (makes sense as the last\n\
+entry, as the ACL will never get past it).\n\
+\n\
+The second entry shows another thing - if there's a list as the value\n\
+for some match and the match itself is not expecting a list, it is\n\
+taken as an \"or\" - a match for at last one of the choices in the\n\
+list must match. So, for the second entry, both \"match-type\" and\n\
+\"another-match-type\" must be satisfied, but the another one is\n\
+satisfied by either parameter1 or parameter2.\n\
+\n\
+Currently, a RequestLoader object cannot be constructed directly;\n\
+an application must use the singleton loader defined in the\n\
+isc.acl.dns module, i.e., isc.acl.dns.REQUEST_LOADER.\n\
+A future version of this implementation may be extended to give\n\
+applications full flexibility of creating arbitrary loader, when\n\
+this restriction may be removed.\n\
+";
+
+const char* const RequestLoader_load_doc = "\
+load(description) -> RequestACL\n\
+\n\
+Load a DNS (Request) ACL.\n\
+\n\
+This parses an ACL list, creates internal data for each rule\n\
+and returns a RequestACl object that contains all given rules.\n\
+\n\
+Exceptions:\n\
+ LoaderError Load failed. The most likely cause of this is a syntax\n\
+ error in the description. Other internal errors such as\n\
+ memory allocation failure is also converted to this\n\
+ exception.\n\
+\n\
+Parameters:\n\
+ description String or Python representation of the JSON list of\n\
+ ACL. The Python representation is ones accepted by the\n\
+ standard json module.\n\
+\n\
+Return Value(s): The newly created RequestACL object\n\
+";
+} // unnamed namespace
diff --git a/src/lib/python/isc/acl/dns_requestloader_python.cc b/src/lib/python/isc/acl/dns_requestloader_python.cc
new file mode 100644
index 0000000..1ddff4c
--- /dev/null
+++ b/src/lib/python/isc/acl/dns_requestloader_python.cc
@@ -0,0 +1,270 @@
+// Copyright (C) 2011 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 <boost/shared_ptr.hpp>
+
+#include <util/python/pycppwrapper_util.h>
+
+#include <cc/data.h>
+
+#include <acl/dns.h>
+
+#include "dns.h"
+#include "dns_requestacl_python.h"
+#include "dns_requestloader_python.h"
+
+using namespace std;
+using boost::shared_ptr;
+using namespace isc::util::python;
+using namespace isc::data;
+using namespace isc::acl::dns;
+using namespace isc::acl::dns::python;
+
+//
+// Definition of the classes
+//
+
+// For each class, we need a struct, a helper functions (init, destroy,
+// and static wrappers around the methods we export), a list of methods,
+// and a type description
+
+//
+// RequestLoader
+//
+
+// Trivial constructor.
+s_RequestLoader::s_RequestLoader() : cppobj(NULL) {
+}
+
+// Import pydoc text
+#include "dns_requestloader_inc.cc"
+
+namespace {
+//
+// We declare the functions here, the definitions are below
+// the type definition of the object, since both can use the other
+//
+
+int
+RequestLoader_init(PyObject*, PyObject*, PyObject*) {
+ PyErr_SetString(getACLException("Error"),
+ "RequestLoader cannot be directly constructed");
+ return (-1);
+}
+
+void
+RequestLoader_destroy(PyObject* po_self) {
+ s_RequestLoader* const self = static_cast<s_RequestLoader*>(po_self);
+ delete self->cppobj;
+ self->cppobj = NULL;
+ Py_TYPE(self)->tp_free(self);
+}
+
+// This C structure corresponds to a Python callable object for json.dumps().
+// This is initialized at the class initialization time (in
+// initModulePart_RequestLoader() below) and it's ensured to be non NULL and
+// valid in the rest of the class implementation.
+// Getting access to the json module this way and call one of its functions
+// via PyObject_CallObject() may exceed the reasonably acceptable level for
+// straightforward bindings. But the alternative would be to write a Python
+// frontend for the entire module only for this conversion, which would also
+// be too much. So, right now, we implement everything within the binding
+// implementation. If future extensions require more such non trivial
+// wrappers, we should consider the frontend approach more seriously.
+PyObject* json_dumps_obj = NULL;
+
+PyObject*
+RequestLoader_load(PyObject* po_self, PyObject* args) {
+ s_RequestLoader* const self = static_cast<s_RequestLoader*>(po_self);
+
+ try {
+ PyObjectContainer c1, c2; // placeholder for temporary py objects
+ const char* acl_config;
+
+ // First, try string
+ int py_result = PyArg_ParseTuple(args, "s", &acl_config);
+ if (!py_result) {
+ PyErr_Clear(); // need to clear the error from ParseTuple
+
+ // If that fails, confirm the argument is a single Python object,
+ // and pass the argument to json.dumps() without conversion.
+ // Note that we should pass 'args', not 'json_obj' to
+ // PyObject_CallObject(), since this function expects a form of
+ // tuple as its argument parameter, just like ParseTuple.
+ PyObject* json_obj;
+ if (PyArg_ParseTuple(args, "O", &json_obj)) {
+ c1.reset(PyObject_CallObject(json_dumps_obj, args));
+ c2.reset(Py_BuildValue("(O)", c1.get()));
+ py_result = PyArg_ParseTuple(c2.get(), "s", &acl_config);
+ }
+ }
+ if (py_result) {
+ shared_ptr<RequestACL> acl(
+ self->cppobj->load(Element::fromJSON(acl_config)));
+ s_RequestACL* py_acl = static_cast<s_RequestACL*>(
+ requestacl_type.tp_alloc(&requestacl_type, 0));
+ if (py_acl != NULL) {
+ py_acl->cppobj = acl;
+ }
+ return (py_acl);
+ }
+ } catch (const PyCPPWrapperException&) {
+ // If the wrapper utility throws, it's most likely because an invalid
+ // type of argument is passed (and the call to json.dumps() failed
+ // above), rather than a rare case of system errors such as memory
+ // allocation failure. So we fall through to the end of this function
+ // and raise a TypeError.
+ ;
+ } catch (const exception& ex) {
+ PyErr_SetString(getACLException("LoaderError"), ex.what());
+ return (NULL);
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError, "Unexpected C++ exception");
+ return (NULL);
+ }
+
+ PyErr_SetString(PyExc_TypeError, "RequestLoader.load() "
+ "expects str or python representation of JSON");
+ 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 RequestLoader_methods[] = {
+ { "load", RequestLoader_load, METH_VARARGS, RequestLoader_load_doc },
+ { NULL, NULL, 0, NULL }
+};
+} // end of unnamed namespace
+
+namespace isc {
+namespace acl {
+namespace dns {
+namespace python {
+// This defines the complete type for reflection in python and
+// parsing of PyObject* to s_RequestLoader
+// Most of the functions are not actually implemented and NULL here.
+PyTypeObject requestloader_type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "isc.acl.dns.RequestLoader",
+ sizeof(s_RequestLoader), // tp_basicsize
+ 0, // tp_itemsize
+ RequestLoader_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
+ RequestLoader_doc,
+ NULL, // tp_traverse
+ NULL, // tp_clear
+ NULL, // tp_richcompare
+ 0, // tp_weaklistoffset
+ NULL, // tp_iter
+ NULL, // tp_iternext
+ RequestLoader_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
+ RequestLoader_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
+};
+
+bool
+initModulePart_RequestLoader(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(&requestloader_type) < 0) {
+ return (false);
+ }
+ void* p = &requestloader_type;
+ if (PyModule_AddObject(mod, "RequestLoader",
+ static_cast<PyObject*>(p)) < 0) {
+ return (false);
+ }
+
+ // Get and hold our own reference to json.dumps() for later use.
+ // Normally it should succeed as __init__.py of the isc.acl package
+ // explicitly imports the json module, and the code below should be
+ // error free (e.g. they don't require memory allocation) under this
+ // condition.
+ // This could still fail with deviant or evil Python code such as those
+ // that first import json and then delete the reference to it from
+ // sys.modules before it imports the acl.dns module. The RequestLoader
+ // class could still work as long as it doesn't use the JSON decoder,
+ // but we'd rather refuse to import the module than allowing the partially
+ // workable class to keep running.
+ PyObject* json_module = PyImport_AddModule("json");
+ if (json_module != NULL) {
+ PyObject* json_dict = PyModule_GetDict(json_module);
+ if (json_dict != NULL) {
+ json_dumps_obj = PyDict_GetItemString(json_dict, "dumps");
+ }
+ }
+ if (json_dumps_obj != NULL) {
+ Py_INCREF(json_dumps_obj);
+ } else {
+ PyErr_SetString(PyExc_RuntimeError,
+ "isc.acl.dns.RequestLoader needs the json module, but "
+ "it's missing");
+ return (false);
+ }
+
+ Py_INCREF(&requestloader_type);
+
+ return (true);
+}
+} // namespace python
+} // namespace dns
+} // namespace acl
+} // namespace isc
diff --git a/src/lib/python/isc/acl/dns_requestloader_python.h b/src/lib/python/isc/acl/dns_requestloader_python.h
new file mode 100644
index 0000000..9d0b63e
--- /dev/null
+++ b/src/lib/python/isc/acl/dns_requestloader_python.h
@@ -0,0 +1,46 @@
+// Copyright (C) 2011 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_REQUESTLOADER_H
+#define __PYTHON_REQUESTLOADER_H 1
+
+#include <Python.h>
+
+#include <acl/dns.h>
+
+namespace isc {
+namespace acl {
+namespace dns {
+namespace python {
+
+// The s_* Class simply covers one instantiation of the object
+class s_RequestLoader : public PyObject {
+public:
+ s_RequestLoader();
+ RequestLoader* cppobj;
+};
+
+extern PyTypeObject requestloader_type;
+
+bool initModulePart_RequestLoader(PyObject* mod);
+
+} // namespace python
+} // namespace dns
+} // namespace acl
+} // namespace isc
+#endif // __PYTHON_REQUESTLOADER_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/python/isc/acl/dnsacl_inc.cc b/src/lib/python/isc/acl/dnsacl_inc.cc
new file mode 100644
index 0000000..b2e7338
--- /dev/null
+++ b/src/lib/python/isc/acl/dnsacl_inc.cc
@@ -0,0 +1,17 @@
+namespace {
+const char* const dnsacl_doc = "\
+Implementation module for DNS ACL operations\n\n\
+This module provides Python bindings for the C++ classes in the\n\
+isc::acl::dns namespace. Specifically, it defines Python interfaces of\n\
+handling access control lists (ACLs) with DNS related contexts.\n\
+These bindings are close match to the C++ API, but they are not complete\n\
+(some parts are not needed) and some are done in more python-like ways.\n\
+\n\
+Special objects:\n\
+\n\
+REQUEST_LOADER -- A singleton loader of ACLs. It is expected applications\n\
+ will use this function instead of creating their own loaders, because\n\
+ one is enough, this one will have registered default checks and it is\n\
+ known one, so any plugins can registrer additional checks as well.\n\
+";
+} // unnamed namespace
diff --git a/src/lib/python/isc/acl/tests/Makefile.am b/src/lib/python/isc/acl/tests/Makefile.am
new file mode 100644
index 0000000..64737d2
--- /dev/null
+++ b/src/lib/python/isc/acl/tests/Makefile.am
@@ -0,0 +1,30 @@
+PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
+PYTESTS = acl_test.py dns_test.py
+
+EXTRA_DIST = $(PYTESTS)
+
+# If necessary (rare cases), explicitly specify paths to dynamic libraries
+# required by loadable python modules.
+LIBRARY_PATH_PLACEHOLDER =
+if SET_ENV_LIBRARY_PATH
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/acl/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
+endif
+
+# test using command-line arguments, so use check-local target instead of TESTS
+check-local:
+if ENABLE_PYTHON_COVERAGE
+ touch $(abs_top_srcdir)/.coverage
+ rm -f .coverage
+ ${LN_S} $(abs_top_srcdir)/.coverage .coverage
+endif
+ for pytest in $(PYTESTS) ; do \
+ echo Running test: $$pytest ; \
+ env PYTHONPATH=$(abs_top_builddir)/src/lib/isc/python/acl/.libs:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python \
+ $(LIBRARY_PATH_PLACEHOLDER) \
+ $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
+ done
+
+CLEANDIRS = __pycache__
+
+clean-local:
+ rm -rf $(CLEANDIRS)
diff --git a/src/lib/python/isc/acl/tests/acl_test.py b/src/lib/python/isc/acl/tests/acl_test.py
new file mode 100644
index 0000000..24a0c94
--- /dev/null
+++ b/src/lib/python/isc/acl/tests/acl_test.py
@@ -0,0 +1,29 @@
+# Copyright (C) 2011 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 isc.acl.acl import *
+
+class ACLTest(unittest.TestCase):
+
+ def test_actions(self):
+ # These are simple tests just checking the pre defined actions have
+ # different values
+ self.assertTrue(ACCEPT != REJECT)
+ self.assertTrue(REJECT != DROP)
+ self.assertTrue(DROP != ACCEPT)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/src/lib/python/isc/acl/tests/dns_test.py b/src/lib/python/isc/acl/tests/dns_test.py
new file mode 100644
index 0000000..acaf32b
--- /dev/null
+++ b/src/lib/python/isc/acl/tests/dns_test.py
@@ -0,0 +1,280 @@
+# Copyright (C) 2011 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
+import socket
+from isc.acl.acl import LoaderError, Error, ACCEPT, REJECT, DROP
+from isc.acl.dns import *
+
+def get_sockaddr(address, port):
+ '''This is a simple shortcut wrapper for getaddrinfo'''
+ ai = socket.getaddrinfo(address, port, 0, socket.SOCK_DGRAM,
+ socket.IPPROTO_UDP, socket.AI_NUMERICHOST)[0]
+ return ai[4]
+
+def get_acl(prefix):
+ '''This is a simple shortcut for creating an ACL containing single rule
+ that accepts addresses for the given IP prefix (and reject any others
+ by default)
+ '''
+ return REQUEST_LOADER.load('[{"action": "ACCEPT", "from": "' + \
+ prefix + '"}]')
+
+def get_acl_json(prefix):
+ '''Same as get_acl, but this function passes a Python representation of
+ JSON to the loader, not a string.'''
+ json = [{"action": "ACCEPT"}]
+ json[0]["from"] = prefix
+ return REQUEST_LOADER.load(json)
+
+def get_context(address):
+ '''This is a simple shortcut wrapper for creating a RequestContext
+ object with a given IP address. Port number doesn't matter in the test
+ (as of the initial implementation), so it's fixed for simplicity.
+ '''
+ return RequestContext(get_sockaddr(address, 53000))
+
+# These are commonly used RequestContext object
+CONTEXT4 = get_context('192.0.2.1')
+CONTEXT6 = get_context('2001:db8::1')
+
+class RequestContextTest(unittest.TestCase):
+
+ def test_construct(self):
+ # Construct the context from IPv4/IPv6 addresses, check the object
+ # by printing it.
+ self.assertEqual('<isc.acl.dns.RequestContext object, ' + \
+ 'remote_addr=[192.0.2.1]:53001>',
+ RequestContext(('192.0.2.1', 53001)).__str__())
+ self.assertEqual('<isc.acl.dns.RequestContext object, ' + \
+ 'remote_addr=[2001:db8::1234]:53006>',
+ RequestContext(('2001:db8::1234', 53006,
+ 0, 0)).__str__())
+
+ # Unusual case: port number overflows (this constructor allows that,
+ # although it should be rare anyway; the socket address should
+ # normally come from the Python socket module.
+ self.assertEqual('<isc.acl.dns.RequestContext object, ' + \
+ 'remote_addr=[192.0.2.1]:0>',
+ RequestContext(('192.0.2.1', 65536)).__str__())
+
+ # same test using socket.getaddrinfo() to ensure it accepts the sock
+ # address representation used in the Python socket module.
+ self.assertEqual('<isc.acl.dns.RequestContext object, ' + \
+ 'remote_addr=[192.0.2.1]:53001>',
+ RequestContext(get_sockaddr('192.0.2.1',
+ 53001)).__str__())
+ self.assertEqual('<isc.acl.dns.RequestContext object, ' + \
+ 'remote_addr=[2001:db8::1234]:53006>',
+ RequestContext(get_sockaddr('2001:db8::1234',
+ 53006)).__str__())
+
+ #
+ # Invalid parameters (in our expected usage this should not happen
+ # because the sockaddr would come from the Python socket module, but
+ # validation should still be performed correctly)
+ #
+ # not a tuple
+ self.assertRaises(TypeError, RequestContext, 1)
+ # invalid number of parameters
+ self.assertRaises(TypeError, RequestContext, ('192.0.2.1', 53), 0)
+ # tuple is not in the form of sockaddr
+ self.assertRaises(TypeError, RequestContext, (0, 53))
+ self.assertRaises(TypeError, RequestContext, ('192.0.2.1', 'http'))
+ self.assertRaises(TypeError, RequestContext, ('::', 0, 'flow', 0))
+ # invalid address
+ self.assertRaises(Error, RequestContext, ('example.com', 5300))
+ self.assertRaises(Error, RequestContext, ('192.0.2.1.1', 5300))
+ self.assertRaises(Error, RequestContext, ('2001:db8:::1', 5300))
+
+class RequestACLTest(unittest.TestCase):
+
+ def test_direct_construct(self):
+ self.assertRaises(Error, RequestACL)
+
+ def test_request_loader(self):
+ # these shouldn't raise an exception
+ REQUEST_LOADER.load('[{"action": "DROP"}]')
+ REQUEST_LOADER.load([{"action": "DROP"}])
+ REQUEST_LOADER.load('[{"action": "DROP", "from": "192.0.2.1"}]')
+ REQUEST_LOADER.load([{"action": "DROP", "from": "192.0.2.1"}])
+
+ # Invalid types (note that arguments like '1' or '[]' is of valid
+ # 'type' (but syntax error at a higher level)). So we need to use
+ # something that is not really JSON nor string.
+ self.assertRaises(TypeError, REQUEST_LOADER.load, b'')
+
+ # Incorrect number of arguments
+ self.assertRaises(TypeError, REQUEST_LOADER.load,
+ '[{"action": "DROP"}]', 0)
+
+ def test_bad_acl_syntax(self):
+ # the following are derived from loader_test.cc
+ self.assertRaises(LoaderError, REQUEST_LOADER.load, '{}');
+ self.assertRaises(LoaderError, REQUEST_LOADER.load, {});
+ self.assertRaises(LoaderError, REQUEST_LOADER.load, '42');
+ self.assertRaises(LoaderError, REQUEST_LOADER.load, 42);
+ self.assertRaises(LoaderError, REQUEST_LOADER.load, 'true');
+ self.assertRaises(LoaderError, REQUEST_LOADER.load, True);
+ self.assertRaises(LoaderError, REQUEST_LOADER.load, 'null');
+ self.assertRaises(LoaderError, REQUEST_LOADER.load, None);
+ self.assertRaises(LoaderError, REQUEST_LOADER.load, '"hello"');
+ self.assertRaises(LoaderError, REQUEST_LOADER.load, "hello");
+ self.assertRaises(LoaderError, REQUEST_LOADER.load, '[42]');
+ self.assertRaises(LoaderError, REQUEST_LOADER.load, [42]);
+ self.assertRaises(LoaderError, REQUEST_LOADER.load, '["hello"]');
+ self.assertRaises(LoaderError, REQUEST_LOADER.load, ["hello"]);
+ self.assertRaises(LoaderError, REQUEST_LOADER.load, '[[]]');
+ self.assertRaises(LoaderError, REQUEST_LOADER.load, [[]]);
+ self.assertRaises(LoaderError, REQUEST_LOADER.load, '[true]');
+ self.assertRaises(LoaderError, REQUEST_LOADER.load, [True]);
+ self.assertRaises(LoaderError, REQUEST_LOADER.load, '[null]');
+ self.assertRaises(LoaderError, REQUEST_LOADER.load, [None]);
+ self.assertRaises(LoaderError, REQUEST_LOADER.load, '[{}]');
+ self.assertRaises(LoaderError, REQUEST_LOADER.load, [{}]);
+
+ # the following are derived from dns_test.cc
+ self.assertRaises(LoaderError, REQUEST_LOADER.load,
+ '[{"action": "ACCEPT", "bad": "192.0.2.1"}]')
+ self.assertRaises(LoaderError, REQUEST_LOADER.load,
+ [{"action": "ACCEPT", "bad": "192.0.2.1"}])
+ self.assertRaises(LoaderError, REQUEST_LOADER.load,
+ '[{"action": "ACCEPT", "from": 4}]')
+ self.assertRaises(LoaderError, REQUEST_LOADER.load,
+ [{"action": "ACCEPT", "from": 4}])
+ self.assertRaises(LoaderError, REQUEST_LOADER.load,
+ '[{"action": "ACCEPT", "from": []}]')
+ self.assertRaises(LoaderError, REQUEST_LOADER.load,
+ [{"action": "ACCEPT", "from": []}])
+ self.assertRaises(LoaderError, REQUEST_LOADER.load,
+ '[{"action": "ACCEPT", "from": "bad"}]')
+ self.assertRaises(LoaderError, REQUEST_LOADER.load,
+ [{"action": "ACCEPT", "from": "bad"}])
+ self.assertRaises(LoaderError, REQUEST_LOADER.load,
+ '[{"action": "ACCEPT", "from": null}]')
+ self.assertRaises(LoaderError, REQUEST_LOADER.load,
+ [{"action": "ACCEPT", "from": None}])
+
+ def test_bad_acl_ipsyntax(self):
+ # this test is derived from ip_check_unittest.cc
+ self.assertRaises(LoaderError, REQUEST_LOADER.load,
+ '[{"action": "DROP", "from": "192.0.2.43/-1"}]')
+ self.assertRaises(LoaderError, REQUEST_LOADER.load,
+ [{"action": "DROP", "from": "192.0.2.43/-1"}])
+ self.assertRaises(LoaderError, REQUEST_LOADER.load,
+ '[{"action": "DROP", "from": "192.0.2.43//1"}]')
+ self.assertRaises(LoaderError, REQUEST_LOADER.load,
+ [{"action": "DROP", "from": "192.0.2.43//1"}])
+ self.assertRaises(LoaderError, REQUEST_LOADER.load,
+ '[{"action": "DROP", "from": "192.0.2.43/1/"}]')
+ self.assertRaises(LoaderError, REQUEST_LOADER.load,
+ [{"action": "DROP", "from": "192.0.2.43/1/"}])
+ self.assertRaises(LoaderError, REQUEST_LOADER.load,
+ '[{"action": "DROP", "from": "/192.0.2.43/1"}]')
+ self.assertRaises(LoaderError, REQUEST_LOADER.load,
+ [{"action": "DROP", "from": "/192.0.2.43/1"}])
+ self.assertRaises(LoaderError, REQUEST_LOADER.load,
+ '[{"action": "DROP", "from": "2001:db8::/xxxx"}]')
+ self.assertRaises(LoaderError, REQUEST_LOADER.load,
+ [{"action": "DROP", "from": "2001:db8::/xxxx"}])
+ self.assertRaises(LoaderError, REQUEST_LOADER.load,
+ '[{"action": "DROP", "from": "2001:db8::/32/s"}]')
+ self.assertRaises(LoaderError, REQUEST_LOADER.load,
+ [{"action": "DROP", "from": "2001:db8::/32/s"}])
+ self.assertRaises(LoaderError, REQUEST_LOADER.load,
+ '[{"action": "DROP", "from": "1/"}]')
+ self.assertRaises(LoaderError, REQUEST_LOADER.load,
+ [{"action": "DROP", "from": "1/"}])
+ self.assertRaises(LoaderError, REQUEST_LOADER.load,
+ '[{"action": "DROP", "from": "/1"}]')
+ self.assertRaises(LoaderError, REQUEST_LOADER.load,
+ [{"action": "DROP", "from": "/1"}])
+ self.assertRaises(LoaderError, REQUEST_LOADER.load,
+ '[{"action": "DROP", "from": "192.0.2.0/33"}]')
+ self.assertRaises(LoaderError, REQUEST_LOADER.load,
+ [{"action": "DROP", "from": "192.0.2.0/33"}])
+ self.assertRaises(LoaderError, REQUEST_LOADER.load,
+ '[{"action": "DROP", "from": "::1/129"}]')
+ self.assertRaises(LoaderError, REQUEST_LOADER.load,
+ [{"action": "DROP", "from": "::1/129"}])
+
+ def test_execute(self):
+ # tests derived from dns_test.cc. We don't directly expose checks
+ # in the python wrapper, so we test it via execute().
+ self.assertEqual(ACCEPT, get_acl('192.0.2.1').execute(CONTEXT4))
+ self.assertEqual(ACCEPT, get_acl_json('192.0.2.1').execute(CONTEXT4))
+ self.assertEqual(REJECT, get_acl('192.0.2.53').execute(CONTEXT4))
+ self.assertEqual(REJECT, get_acl_json('192.0.2.53').execute(CONTEXT4))
+ self.assertEqual(ACCEPT, get_acl('192.0.2.0/24').execute(CONTEXT4))
+ self.assertEqual(ACCEPT, get_acl_json('192.0.2.0/24').execute(CONTEXT4))
+ self.assertEqual(REJECT, get_acl('192.0.1.0/24').execute(CONTEXT4))
+ self.assertEqual(REJECT, get_acl_json('192.0.1.0/24').execute(CONTEXT4))
+ self.assertEqual(REJECT, get_acl('192.0.1.0/24').execute(CONTEXT4))
+ self.assertEqual(REJECT, get_acl_json('192.0.1.0/24').execute(CONTEXT4))
+
+ self.assertEqual(ACCEPT, get_acl('2001:db8::1').execute(CONTEXT6))
+ self.assertEqual(ACCEPT, get_acl_json('2001:db8::1').execute(CONTEXT6))
+ self.assertEqual(REJECT, get_acl('2001:db8::53').execute(CONTEXT6))
+ self.assertEqual(REJECT, get_acl_json('2001:db8::53').execute(CONTEXT6))
+ self.assertEqual(ACCEPT, get_acl('2001:db8::/64').execute(CONTEXT6))
+ self.assertEqual(ACCEPT,
+ get_acl_json('2001:db8::/64').execute(CONTEXT6))
+ self.assertEqual(REJECT, get_acl('2001:db8:1::/64').execute(CONTEXT6))
+ self.assertEqual(REJECT,
+ get_acl_json('2001:db8:1::/64').execute(CONTEXT6))
+ self.assertEqual(REJECT, get_acl('32.1.13.184').execute(CONTEXT6))
+ self.assertEqual(REJECT, get_acl_json('32.1.13.184').execute(CONTEXT6))
+
+ # A bit more complicated example, derived from resolver_config_unittest
+ acl = REQUEST_LOADER.load('[ {"action": "ACCEPT", ' +
+ ' "from": "192.0.2.1"},' +
+ ' {"action": "REJECT",' +
+ ' "from": "192.0.2.0/24"},' +
+ ' {"action": "DROP",' +
+ ' "from": "2001:db8::1"},' +
+ '] }')
+ self.assertEqual(ACCEPT, acl.execute(CONTEXT4))
+ self.assertEqual(REJECT, acl.execute(get_context('192.0.2.2')))
+ self.assertEqual(DROP, acl.execute(get_context('2001:db8::1')))
+ self.assertEqual(REJECT, acl.execute(get_context('2001:db8::2')))
+
+ # same test using the JSON representation
+ acl = REQUEST_LOADER.load([{"action": "ACCEPT", "from": "192.0.2.1"},
+ {"action": "REJECT",
+ "from": "192.0.2.0/24"},
+ {"action": "DROP", "from": "2001:db8::1"}])
+ self.assertEqual(ACCEPT, acl.execute(CONTEXT4))
+ self.assertEqual(REJECT, acl.execute(get_context('192.0.2.2')))
+ self.assertEqual(DROP, acl.execute(get_context('2001:db8::1')))
+ self.assertEqual(REJECT, acl.execute(get_context('2001:db8::2')))
+
+ def test_bad_execute(self):
+ acl = get_acl('192.0.2.1')
+ # missing parameter
+ self.assertRaises(TypeError, acl.execute)
+ # too many parameters
+ self.assertRaises(TypeError, acl.execute, get_context('192.0.2.2'), 0)
+ # type mismatch
+ self.assertRaises(TypeError, acl.execute, 'bad parameter')
+
+class RequestLoaderTest(unittest.TestCase):
+ # Note: loading ACLs is tested in other test cases.
+
+ def test_construct(self):
+ # at least for now, we don't allow direct construction.
+ self.assertRaises(Error, RequestLoader)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/src/lib/util/python/pycppwrapper_util.h b/src/lib/util/python/pycppwrapper_util.h
index fd55c19..3f396e2 100644
--- a/src/lib/util/python/pycppwrapper_util.h
+++ b/src/lib/util/python/pycppwrapper_util.h
@@ -94,6 +94,22 @@ public:
/// the reference to be decreased, the original bare pointer should be
/// extracted using the \c release() method.
///
+/// In some other cases, it would be convenient if it's possible to create
+/// an "empty" container and reset it with a Python object later.
+/// For example, we may want to create a temporary Python object in the
+/// middle of a function and make sure that it's valid within the rest of
+/// the function scope, while we want to make sure its reference is released
+/// when the function returns (either normally or as a result of exception).
+/// To allow this scenario, this class defines the default constructor
+/// and the \c reset() method. The default constructor allows the class
+/// object with an "empty" (NULL) Python object, while \c reset() allows
+/// the stored object to be replaced with a new one. If there's a valid
+/// object was already set, \c reset() releases its reference.
+/// In general, it's safer to construct the container object with a valid
+/// Python object pointer. The use of the default constructor and
+/// \c reset() should therefore be restricted to cases where it's
+/// absolutely necessary.
+///
/// There are two convenience methods for commonly used operations:
/// \c installAsClassVariable() to add the PyObject as a class variable
/// and \c installToModule to add the PyObject to a specified python module.
@@ -166,16 +182,27 @@ public:
/// exception in a python biding written in C/C++. See the code comment
/// of the method for more details.
struct PyObjectContainer {
+ PyObjectContainer() : obj_(NULL) {}
PyObjectContainer(PyObject* obj) : obj_(obj) {
if (obj_ == NULL) {
isc_throw(PyCPPWrapperException, "Unexpected NULL PyObject, "
"probably due to short memory");
}
}
- virtual ~PyObjectContainer() {
+ ~PyObjectContainer() {
+ if (obj_ != NULL) {
+ Py_DECREF(obj_);
+ }
+ }
+ void reset(PyObject* obj) {
+ if (obj == NULL) {
+ isc_throw(PyCPPWrapperException, "Unexpected NULL PyObject, "
+ "probably due to short memory");
+ }
if (obj_ != NULL) {
Py_DECREF(obj_);
}
+ obj_ = obj;
}
PyObject* get() {
return (obj_);
diff --git a/src/lib/util/python/wrapper_template.cc b/src/lib/util/python/wrapper_template.cc
index 691e4bf..a703731 100644
--- a/src/lib/util/python/wrapper_template.cc
+++ b/src/lib/util/python/wrapper_template.cc
@@ -210,7 +210,7 @@ namespace python {
// Most of the functions are not actually implemented and NULL here.
PyTypeObject @cppclass at _type = {
PyVarObject_HEAD_INIT(NULL, 0)
- "pydnspp. at CPPCLASS@",
+ "@MODULE at .@CPPCLASS@",
sizeof(s_ at CPPCLASS@), // tp_basicsize
0, // tp_itemsize
reinterpret_cast<destructor>(@CPPCLASS at _destroy), // tp_dealloc
@@ -222,7 +222,7 @@ PyTypeObject @cppclass at _type = {
NULL, // tp_as_number
NULL, // tp_as_sequence
NULL, // tp_as_mapping
- NULL, // tp_hash
+ NULL, // tp_hash
NULL, // tp_call
// THIS MAY HAVE TO BE CHANGED TO NULL:
@CPPCLASS at _str, // tp_str
More information about the bind10-changes
mailing list