Commit c24553e2 authored by JINMEI Tatuya's avatar JINMEI Tatuya
Browse files

[master] Merge branch 'trac983'

parents 8cec4587 151ea348
......@@ -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
......
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
......@@ -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());
......
......@@ -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
......
SUBDIRS = datasrc cc config log net notify util testutils
SUBDIRS = datasrc cc config log net notify util testutils acl
python_PYTHON = __init__.py
......
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)
"""
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
// 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);
}
# 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 *
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
// 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);
}
// 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:
# 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 *
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
// 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());