--- /dev/null
+setenv KEATOP /tmp/kea
+
+setenv KEASRC $KEATOP/src/lib
+
+setenv DYLD_LIBRARY_PATH $KEASRC/dhcpsrv/.libs:$KEASRC/eval/.libs:$KEASRC/dhcp_ddns/.libs:$KEASRC/stats/.libs:$KEASRC/hooks/.libs:$KEASRC/config/.libs:$KEASRC/dhcp/.libs:$KEASRC/asiolink/.libs:$KEASRC/dns/.libs:$KEASRC/cc/.libs:$KEASRC/cryptolink/.libs:$KEASRC/log/.libs:$KEASRC/util/threads/.libs:$KEASRC/util/.libs:$KEASRC/exceptions/.libs
+
+setenv PYTHONPATH $KEATOP/src/hooks/external/python
--- /dev/null
+// Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <Python.h>
+
+#include <hooks/hooks.h>
+
+#include <hooks/external/python/module.h>
+#include <hooks/external/python/ppkt4.h>
+
+#include <iostream>
+
+using namespace std;
+using namespace isc::data;
+using namespace isc::dhcp;
+using namespace isc::hooks;
+using namespace isc::python;
+
+namespace {
+ // Program name in Unicode
+ wchar_t* wprogname;
+
+ // Script module
+ PyObject* modscript;
+
+ // Pkt4 receive handler
+ PyObject* pkt4_rcv_hndl;
+};
+
+extern "C" {
+
+// Framework functions
+
+// version
+int version() {
+ return (KEA_HOOKS_VERSION);
+}
+
+// load
+int load(LibraryHandle& handle) {
+ // Add kea as a built-in module
+ if (PyImport_AppendInittab("kea", PyInit_kea) < 0) {
+ cerr << "PyImport_AppendInittab failed\n";
+ return (1);
+ }
+
+ // Set the program name (default "kea")
+ ConstElementPtr program = handle.getParameter("program");
+ string progname = "kea";
+ if (program && program->getType() == Element::string) {
+ progname = program->stringValue();
+ } else {
+ cout << "no \"program\" parameter: using \"kea\"\n";
+ }
+ wprogname = Py_DecodeLocale(progname.c_str(), NULL);
+ if (!wprogname) {
+ cerr << "Py_DecodeLocale failed\n";
+ return (2);
+ }
+ Py_SetProgramName(wprogname);
+
+ // Initialize the python interpreter without signal handlers
+ Py_InitializeEx(0);
+
+ // Get the script module name (default "hook" for the "hook.py" file)
+ ConstElementPtr script = handle.getParameter("script");
+ string scptname = "hook";
+ if (script && script->getType() == Element::string) {
+ scptname = script->stringValue();
+ } else {
+ cout << "no \"script\" parameter: using \"hook\"\n";
+ }
+ PyObject* pyscript = PyUnicode_DecodeFSDefault(scptname.c_str());
+ if (!pyscript) {
+ PyErr_Print();
+ cerr << "PyUnicode_DecodeFSDefault failed\n";
+ return (3);
+ }
+ modscript = PyImport_Import(pyscript);
+ Py_DECREF(pyscript);
+ if (!modscript) {
+ PyErr_Print();
+ cerr << "PyImport_Import failed\n";
+ return (4);
+ }
+
+ pkt4_rcv_hndl = PyObject_GetAttrString(modscript, "pkt4_receive");
+ if (pkt4_rcv_hndl) {
+ if (!PyCallable_Check(pkt4_rcv_hndl)) {
+ cerr << "pkt4_receive is not callable\n";
+ Py_DECREF(pkt4_rcv_hndl);
+ pkt4_rcv_hndl = NULL;
+ } else {
+ cout << "got pkt4_receive\n";
+ }
+ }
+
+ return (0);
+}
+
+// unload
+int unload() {
+ // Free script module
+ Py_XDECREF(modscript);
+
+ // Shutdown
+ Py_Finalize();
+
+ // Free wprogname
+ PyMem_RawFree(wprogname);
+
+ return (0);
+}
+
+// pkt4_receive hook
+int pkt4_receive(CalloutHandle& handle) {
+ if (!pkt4_rcv_hndl) {
+ return (0);
+ }
+ cout << "pkt4_receive: enter\n";
+
+ Pkt4Ptr query4;
+ handle.getArgument("query4", query4);
+ if (!query4) {
+ cerr << "pkt4_receive: null query4\n";
+ return (0);
+ }
+
+ PyObject* query = pkt4_type.tp_alloc(&pkt4_type, 0);
+ if (!query) {
+ PyErr_Print();
+ cerr << "pkt4_alloc failed\n";
+ return (0);
+ }
+ (static_cast<py_pkt4*>(query))->object = query4;
+
+ PyObject* args = Py_BuildValue("(O)", query);
+ if (!args) {
+ PyErr_Print();
+ cerr << "Py_BuildValue failed\n";
+ return (0);
+ }
+ PyObject* ret = PyObject_CallObject(pkt4_rcv_hndl, args);
+ Py_DECREF(args);
+ if (!ret) {
+ PyErr_Print();
+ cerr << "pkt4_rcv_hndl failed\n";
+ return (0);
+ }
+ if (!PyLong_Check(ret)) {
+ PyErr_Print();
+ cerr << "pkt4_rcv_hndl didn't return an int\n";
+ Py_DECREF(ret);
+ return (0);
+ }
+ int result = static_cast<int>(PyLong_AsLong(ret));
+ Py_DECREF(ret);
+ if (PyErr_Occurred()) {
+ PyErr_Print();
+ cerr << "PyLong_AsLong failed\n";
+ return (0);
+ }
+
+ cout << "pkt4_receive: return " << result << "\n";
+ return (result);
+}
+
+}
--- /dev/null
+# Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC")
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+'''Support for Kea hook in python'''
+
+print("hook.py is loading")
+
+import kea
+
+NEXT_STEP_CONTINUE = 0
+NEXT_STEP_SKIP = 1
+NEXT_STEP_DROP = 2
+
+def pkt4_receive(query4):
+ """pkt4_receive hook point.
+
+ parameter: inout Pkt4Ptr query4
+ return: next step
+ """
+ print("pkt4_receive: handler is called with", query4)
+ return NEXT_STEP_CONTINUE
+
+print("hook.py loaded")
--- /dev/null
+// Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <Python.h>
+
+#include <hooks/external/python/poption.h>
+#include <hooks/external/python/ppkt4.h>
+
+using namespace std;
+using namespace isc::python;
+
+namespace { // anonymous namespace
+
+// Module definition
+PyModuleDef kea = {
+ PyModuleDef_HEAD_INIT,
+ "kea", // m_name
+ "kea hook support", // m_doc
+ -1, // m_size
+ NULL, // m_methods
+ NULL, // m_slots
+ NULL, // m_traverse
+ NULL, // m_clear
+ NULL // m_free
+};
+
+} // end of anonymous namespace
+
+// Module initialization
+PyMODINIT_FUNC
+PyInit_kea(void) {
+ // Create module
+ PyObject* mod = PyModule_Create(&kea);
+ if (!mod) {
+ return (NULL);
+ }
+
+ // Initialize option
+ if (!initmod_option(mod)) {
+ Py_DECREF(mod);
+ return (NULL);
+ }
+
+ // Initialize pkt4
+ if (!initmod_pkt4(mod)) {
+ Py_DECREF(mod);
+ return (NULL);
+ }
+
+ // Constants
+
+ return (mod);
+}
--- /dev/null
+// Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef KEA_PYTHON_MODULE_H
+#define KEA_PYTHON_MODULE_H 1
+
+PyMODINIT_FUNC PyInit_kea(void);
+
+#endif // KEA_PYTHON_MODULE_H
--- /dev/null
+// Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#define PY_SSIZE_T_CLEAN
+#include <Python.h>
+
+#include <hooks/external/python/poption.h>
+
+using namespace std;
+using namespace isc::dhcp;
+using namespace isc::python;
+
+// Constructor
+py_option::py_option() {}
+
+namespace { // anonymous namespace
+
+// tp_init
+int
+option_init(PyObject* obj, PyObject* args, PyObject*) {
+ unsigned char u;
+ unsigned short int t;
+ PyBytesObject* d = NULL;
+
+ if (!PyArg_ParseTuple(args, "bHS", &u, &t, &d)) {
+ return (-1);
+ }
+ Option::Universe universe;
+ switch (u) {
+ case 4:
+ universe = Option::V4;
+ if (t > 255) {
+ PyErr_Format(PyExc_ValueError,
+ "out of range type for DHCPv4: %u",
+ static_cast<unsigned>(t));
+ return (-1);
+ }
+ break;
+ case 6:
+ universe = Option::V6;
+ break;
+ default:
+ PyErr_Format(PyExc_ValueError,
+ "universe must be 4 or 6 (not %u)",
+ static_cast<unsigned>(u));
+ return (-1);
+ }
+
+ OptionBuffer data;
+ data.resize(PyBytes_GET_SIZE(d));
+ memmove(&data[0], PyBytes_AS_STRING(d), data.size());
+
+ py_option* const self = static_cast<py_option*>(obj);
+ self->object.reset(new Option(universe, t, data));
+
+ return (0);
+}
+
+// tp_dealloc
+void
+option_dealloc(PyObject* obj) {
+ py_option* const self = static_cast<py_option*>(obj);
+ self->object.reset();
+ Py_TYPE(self)->tp_free(self);
+}
+
+// tp_str
+PyObject*
+option_str(PyObject* obj) {
+ py_option* const self = static_cast<py_option*>(obj);
+ return (PyUnicode_FromString(self->object->toText(0).c_str()));
+}
+
+// getUniverse() method
+PyObject*
+getUniverse(PyObject* obj) {
+ py_option* const self = static_cast<py_option*>(obj);
+ switch (self->object->getUniverse()) {
+ case Option::V4:
+ return (PyLong_FromLong(4l));
+ case Option::V6:
+ return (PyLong_FromLong(6l));
+ default:
+ PyErr_SetString(PyExc_SystemError, "getUniverse");
+ return (NULL);
+ }
+}
+
+// toBinary(bool include_header = false) method
+PyObject*
+toBinary(PyObject* obj, PyObject* args) {
+ int ih = 0;
+
+ if (!PyArg_ParseTuple(args, "|p", &ih)) {
+ return (NULL);
+ }
+
+ py_option* const self = static_cast<py_option*>(obj);
+ vector<uint8_t> bin = self->object->toBinary(ih != 0);
+ return (PyBytes_FromStringAndSize(reinterpret_cast<char*>(&bin[0]),
+ static_cast<Py_ssize_t>(bin.size())));
+}
+
+// getType() method
+PyObject*
+getType(PyObject* obj) {
+ py_option* const self = static_cast<py_option*>(obj);
+ return (PyLong_FromLong(static_cast<long>(self->object->getType())));
+}
+
+// len() method
+PyObject*
+len(PyObject* obj) {
+ py_option* const self = static_cast<py_option*>(obj);
+ return (PyLong_FromLong(static_cast<long>(self->object->len())));
+}
+
+// getHeaderLen() method
+PyObject*
+getHeaderLen(PyObject* obj) {
+ py_option* const self = static_cast<py_option*>(obj);
+ return (PyLong_FromLong(static_cast<long>(self->object->getHeaderLen())));
+}
+
+// getData() method
+PyObject*
+getData(PyObject* obj) {
+ py_option* const self = static_cast<py_option*>(obj);
+ const OptionBuffer& data = self->object->getData();
+ return (PyBytes_FromStringAndSize(reinterpret_cast<const char*>(&data[0]),
+ static_cast<Py_ssize_t>(data.size())));
+}
+
+// addOption(OptionPtr opt) method
+PyObject*
+addOption(PyObject* obj, PyObject* args) {
+ PyObject* s = NULL;
+
+ if (!PyArg_ParseTuple(args, "O!", &option_type, &s)) {
+ return (NULL);
+ }
+ py_option* const sub = static_cast<py_option*>(s);
+ py_option* const self = static_cast<py_option*>(obj);
+ self->object->addOption(sub->object);
+ Py_RETURN_NONE;
+}
+
+// getOption(uint16_t type) method
+PyObject*
+getOption(PyObject* obj, PyObject* args) {
+ unsigned short t;
+
+ if (!PyArg_ParseTuple(args, "H", &t)) {
+ return (NULL);
+ }
+ py_option* const self = static_cast<py_option*>(obj);
+ OptionPtr sub = self->object->getOption(t);
+ if (sub) {
+ PyObject* ret = option_type.tp_alloc(&option_type, 0);
+ if (ret) {
+ (static_cast<py_option*>(ret))->object = sub;
+ }
+ return (ret);
+ }
+ Py_RETURN_NONE;
+}
+
+// TODO: getOptions() method
+
+// delOption(uint16_t type) method
+PyObject*
+delOption(PyObject* obj, PyObject* args) {
+ unsigned short t;
+
+ if (!PyArg_ParseTuple(args, "H", &t)) {
+ return (NULL);
+ }
+ py_option* const self = static_cast<py_option*>(obj);
+ bool ret = self->object->delOption(t);
+ return (PyBool_FromLong(ret ? 1l : 0l));
+}
+
+// setData(<byte>) method
+PyObject*
+setData(PyObject* obj, PyObject* args) {
+ PyBytesObject* d = NULL;
+
+ if (!PyArg_ParseTuple(args, "S", &d)) {
+ return (NULL);
+ }
+ vector<uint8_t> data;
+ data.resize(PyBytes_GET_SIZE(d));
+ memmove(&data[0], PyBytes_AS_STRING(d), data.size());
+
+ py_option* const self = static_cast<py_option*>(obj);
+ self->object->setData(data.begin(), data.end());
+ Py_RETURN_NONE;
+}
+
+// setEncapsulatedSpace(const string& encapsulated_space) method
+PyObject*
+setEncapsulatedSpace(PyObject* obj, PyObject* args) {
+ const char* es = NULL;
+
+ if (!PyArg_ParseTuple(args, "s", &es)) {
+ return (NULL);
+ }
+ const string& encapsulated_space(es);
+
+ py_option* const self = static_cast<py_option*>(obj);
+ self->object->setEncapsulatedSpace(encapsulated_space);
+ Py_RETURN_NONE;
+}
+
+// getEncapsulatedSpace() method
+PyObject*
+getEncapsulatedSpace(PyObject* obj) {
+ py_option* const self = static_cast<py_option*>(obj);
+ return (Py_BuildValue("s", self->object->getEncapsulatedSpace().c_str()));
+}
+
+// Method table
+PyMethodDef option_method[] = {
+ { "getUniverse", (PyCFunction)getUniverse, METH_NOARGS,
+ "returns option universe (4 or 6" },
+ { "toBinary", toBinary, METH_VARARGS,
+ "returns binary representation of the option" },
+ { "getType", (PyCFunction)getType, METH_NOARGS,
+ "returns option type" },
+ { "len", (PyCFunction)len, METH_NOARGS,
+ "returns length of the complete option" },
+ { "getHeaderLen", (PyCFunction)getHeaderLen, METH_NOARGS,
+ "returns length of option header" },
+ { "getData", (PyCFunction)getData, METH_NOARGS,
+ "returns actual data" },
+ { "addOption", addOption, METH_VARARGS,
+ "adds a sub-option" },
+ { "getOption", getOption, METH_VARARGS,
+ "return suboption of specific type" },
+ { "delOption", delOption, METH_VARARGS,
+ "attempts to delete first suboption of requested type" },
+ { "setData", setData, METH_VARARGS,
+ "sets content of this option" },
+ { "setEncapsulatedSpace", setEncapsulatedSpace, METH_VARARGS,
+ "sets the name of the option space encapsulated by this option" },
+ { "getEncapsulatedSpace", (PyCFunction)getEncapsulatedSpace, METH_NOARGS,
+ "returns the name of the option space encapsulated by this option" },
+ { NULL, NULL, 0, NULL }
+};
+
+} // end of anonymous namespace
+
+namespace isc {
+namespace python {
+
+// Python option type definition
+PyTypeObject option_type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "kea-option", // tp_name
+ sizeof(py_option), // tp_basicsize
+ 0, // tp_itemsize
+ option_dealloc, // tp_dealloc
+ NULL, // tp_print
+ NULL, // tp_getattr
+ NULL, // tp_setattr
+ NULL, // tp_reserved
+ NULL, // tp_repr
+ NULL, // tp_as_number
+ NULL, // tp_as_sequence
+ NULL, // tp_as_mapping
+ NULL, // tp_hash
+ NULL, // tp_call
+ option_str, // tp_str
+ NULL, // tp_getattro
+ NULL, // tp_setattro
+ NULL, // tp_as_buffer
+ Py_TPFLAGS_DEFAULT, // tp_flags
+ "kea option", // tp_doc
+ NULL, // tp_traverse
+ NULL, // tp_clear
+ NULL, // tp_richcompare
+ 0, // tp_weaklistoffset
+ NULL, // tp_iter
+ NULL, // tp_iternext
+ option_method, // 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
+ option_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
+ NULL // tp_finalize
+};
+
+bool
+initmod_option(PyObject* mod) {
+ // Initialize the description object
+ if (PyType_Ready(&option_type) < 0) {
+ return (false);
+ }
+ // Add it to the module
+ void* p = &option_type;
+ if (PyModule_AddObject(mod, "Option", static_cast<PyObject*>(p)) < 0) {
+ return (false);
+ }
+ Py_INCREF(&option_type);
+
+ return (true);
+}
+
+} // namespace python
+} // namespace isc
--- /dev/null
+// Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef POPTION_H
+#define POPTION_H 1
+
+#include <Python.h>
+
+#include <dhcp/option.h>
+
+namespace isc {
+namespace python {
+
+// Python option class
+class py_option : public PyObject {
+ py_option();
+
+public:
+ isc::dhcp::OptionPtr object;
+};
+
+extern PyTypeObject option_type;
+
+bool initmod_option(PyObject* mod);
+
+} // namespace python
+} // namespace isc
+
+#endif // POPTION_H
--- /dev/null
+// Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#define PY_SSIZE_T_CLEAN
+#include <Python.h>
+
+#include <hooks/external/python/poption.h>
+#include <hooks/external/python/ppkt4.h>
+
+using namespace std;
+using namespace isc::dhcp;
+using namespace isc::python;
+
+// Constructor
+py_pkt4::py_pkt4() {}
+
+namespace { // anonymous namespace
+
+// tp_init
+int
+pkt4_init(PyObject*, PyObject*, PyObject*) {
+ PyErr_SetString(PyExc_SystemError,
+ "pkt4 cannot be directly constructed");
+ return (-1);
+}
+
+// tp_dealloc
+void
+pkt4_dealloc(PyObject* obj) {
+ py_pkt4* const self = static_cast<py_pkt4*>(obj);
+ self->object.reset();
+ Py_TYPE(self)->tp_free(self);
+}
+
+// tp_str
+PyObject*
+pkt4_str(PyObject* obj) {
+ py_pkt4* const self = static_cast<py_pkt4*>(obj);
+ return (PyUnicode_FromString(self->object->toText().c_str()));
+}
+
+// addOption(const OptionPtr opt) method
+PyObject*
+addOption(PyObject* obj, PyObject* args) {
+ PyObject* s = NULL;
+
+ if (!PyArg_ParseTuple(args, "O!", &option_type, &s)) {
+ return (NULL);
+ }
+ py_option* const sub = static_cast<py_option*>(s);
+ py_pkt4* const self = static_cast<py_pkt4*>(obj);
+ self->object->addOption(sub->object);
+ Py_RETURN_NONE;
+}
+
+// delOption(uint16_t type) method
+PyObject*
+delOption(PyObject* obj, PyObject* args) {
+ unsigned short t;
+
+ if (!PyArg_ParseTuple(args, "H", &t)) {
+ return (NULL);
+ }
+ py_pkt4* const self = static_cast<py_pkt4*>(obj);
+ bool ret = self->object->delOption(t);
+ return (PyBool_FromLong(ret ? 1l : 0l));
+}
+
+// len() method
+PyObject*
+len(PyObject* obj) {
+ py_pkt4* const self = static_cast<py_pkt4*>(obj);
+ return (PyLong_FromLong(static_cast<long>(self->object->len())));
+}
+
+// getType() method
+PyObject*
+getType(PyObject* obj) {
+ py_pkt4* const self = static_cast<py_pkt4*>(obj);
+ return (PyLong_FromLong(static_cast<long>(self->object->getType())));
+}
+
+// setType(uint8_t type) method
+PyObject*
+setType(PyObject* obj, PyObject* args) {
+ unsigned char t;
+
+ if (!PyArg_ParseTuple(args, "b", &t)) {
+ return (NULL);
+ }
+ py_pkt4* const self = static_cast<py_pkt4*>(obj);
+ self->object->setType(t);
+ Py_RETURN_NONE;
+}
+
+// setTransid(uint32_t transid) method
+PyObject*
+setTransid(PyObject* obj, PyObject* args) {
+ unsigned int t;
+
+ if (!PyArg_ParseTuple(args, "I", &t)) {
+ return (NULL);
+ }
+ py_pkt4* const self = static_cast<py_pkt4*>(obj);
+ self->object->setTransid(static_cast<uint32_t>(t));
+ Py_RETURN_NONE;
+}
+
+// getTransid() method
+PyObject*
+getTransid(PyObject* obj) {
+ py_pkt4* const self = static_cast<py_pkt4*>(obj);
+ uint32_t transid = self->object->getTransid();
+ return (PyLong_FromUnsignedLong(static_cast<unsigned long>(transid)));
+}
+
+// inClass(const isc::dhcp::ClientClass& client_class) method
+PyObject*
+inClass(PyObject* obj, PyObject* args) {
+ const char* cc = NULL;
+
+ if (!PyArg_ParseTuple(args, "s", &cc)) {
+ return (NULL);
+ }
+ const ClientClass& client_class(cc);
+
+ py_pkt4* const self = static_cast<py_pkt4*>(obj);
+ bool ret = self->object->inClass(client_class);
+ return (PyBool_FromLong(ret ? 1l : 0l));
+}
+
+// addClass(const isc::dhcp::ClientClass& client_class) method
+PyObject*
+addClass(PyObject* obj, PyObject* args) {
+ const char* cc = NULL;
+
+ if (!PyArg_ParseTuple(args, "s", &cc)) {
+ return (NULL);
+ }
+ const ClientClass& client_class(cc);
+
+ py_pkt4* const self = static_cast<py_pkt4*>(obj);
+ self->object->addClass(client_class);
+ Py_RETURN_NONE;
+}
+
+// TODO: getClasses() method
+
+// getOption(uint16_t type) method
+PyObject*
+getOption(PyObject* obj, PyObject* args) {
+ unsigned short t;
+
+ if (!PyArg_ParseTuple(args, "H", &t)) {
+ return (NULL);
+ }
+ py_pkt4* const self = static_cast<py_pkt4*>(obj);
+ OptionPtr sub = self->object->getOption(t);
+ if (sub) {
+ PyObject* ret = option_type.tp_alloc(&option_type, 0);
+ if (ret) {
+ (static_cast<py_option*>(ret))->object = sub;
+ }
+ return (ret);
+ }
+ Py_RETURN_NONE;
+}
+
+// TODO: getTimestamp() method
+
+// TODO: set/getLocal/RemoteAddr/Port methods
+
+// setIndex(uint32_t ifindex) method
+PyObject*
+setIndex(PyObject* obj, PyObject* args) {
+ unsigned int i;
+
+ if (!PyArg_ParseTuple(args, "I", &i)) {
+ return (NULL);
+ }
+ py_pkt4* const self = static_cast<py_pkt4*>(obj);
+ self->object->setIndex(static_cast<uint32_t>(i));
+ Py_RETURN_NONE;
+}
+
+// getIndex() method
+PyObject*
+getIndex(PyObject* obj) {
+ py_pkt4* const self = static_cast<py_pkt4*>(obj);
+ uint32_t ifindex = self->object->getIndex();
+ return (PyLong_FromUnsignedLong(static_cast<unsigned long>(ifindex)));
+}
+
+// getIface() method
+PyObject*
+getIface(PyObject* obj) {
+ py_pkt4* const self = static_cast<py_pkt4*>(obj);
+ return (Py_BuildValue("s", self->object->getIface().c_str()));
+}
+
+// setIface(const std::string& iface) method
+PyObject*
+setIface(PyObject* obj, PyObject* args) {
+ const char* ifn = NULL;
+
+ if (!PyArg_ParseTuple(args, "s", &ifn)) {
+ return (NULL);
+ }
+ const string iface(ifn);
+
+ py_pkt4* const self = static_cast<py_pkt4*>(obj);
+ self->object->setIface(iface);
+ Py_RETURN_NONE;
+}
+
+// Method table
+PyMethodDef pkt4_method[] = {
+ { "addOption", addOption, METH_VARARGS,
+ "adds an option to this packet" },
+ { "delOption", delOption, METH_VARARGS,
+ "attempts to delete first suboption of requested type" },
+ { "len", (PyCFunction)len, METH_NOARGS,
+ "returns packet size in binary format" },
+ { "getType", (PyCFunction)getType, METH_NOARGS,
+ "returns message type" },
+ { "setType", setType, METH_VARARGS,
+ "sets message type" },
+ { "setTransid", setTransid, METH_VARARGS,
+ "sets transaction-id value" },
+ { "getTransid", (PyCFunction)getTransid, METH_NOARGS,
+ "returns value of transaction-id field" },
+ { "inClass", inClass, METH_VARARGS,
+ "checks whether a packet belongs to a given class" },
+ { "addClass", addClass, METH_VARARGS,
+ "adds packet to a specified class" },
+ { "getOption", getOption, METH_VARARGS,
+ "returns the first option of specified type" },
+ { "setIndex", setIndex, METH_VARARGS,
+ "sets interface index" },
+ { "getIndex", (PyCFunction)getIndex, METH_NOARGS,
+ "returns interface index" },
+ { "getIface", (PyCFunction)getIface, METH_NOARGS,
+ "return interface name" },
+ { "setIface", setIface, METH_VARARGS,
+ "sets interface name" },
+ { NULL, NULL, 0, NULL }
+};
+
+} // end of anonymous namespace
+
+namespace isc {
+namespace python {
+
+// Python pkt4 type definition
+PyTypeObject pkt4_type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "kea-pkt4", // tp_name
+ sizeof(py_pkt4), // tp_basicsize
+ 0, // tp_itemsize
+ pkt4_dealloc, // tp_dealloc
+ NULL, // tp_print
+ NULL, // tp_getattr
+ NULL, // tp_setattr
+ NULL, // tp_reserved
+ NULL, // tp_repr
+ NULL, // tp_as_number
+ NULL, // tp_as_sequence
+ NULL, // tp_as_mapping
+ NULL, // tp_hash
+ NULL, // tp_call
+ pkt4_str, // tp_str
+ NULL, // tp_getattro
+ NULL, // tp_setattro
+ NULL, // tp_as_buffer
+ Py_TPFLAGS_DEFAULT, // tp_flags
+ "kea pkt4", // tp_doc
+ NULL, // tp_traverse
+ NULL, // tp_clear
+ NULL, // tp_richcompare
+ 0, // tp_weaklistoffset
+ NULL, // tp_iter
+ NULL, // tp_iternext
+ pkt4_method, // 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
+ pkt4_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
+ NULL // tp_finalize
+};
+
+bool
+initmod_pkt4(PyObject* mod) {
+ // Initialize the description object
+ if (PyType_Ready(&pkt4_type) < 0) {
+ return (false);
+ }
+ // Add it to the module
+ void* p = &pkt4_type;
+ if (PyModule_AddObject(mod, "Pkt4", static_cast<PyObject*>(p)) < 0) {
+ return (false);
+ }
+ Py_INCREF(&pkt4_type);
+
+ return (true);
+}
+
+} // namespace python
+} // namespace isc
--- /dev/null
+// Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef PPKT4_H
+#define PPKT4_H 1
+
+#include <Python.h>
+
+#include <dhcp/pkt4.h>
+
+namespace isc {
+namespace python {
+
+// Python pkt4 class
+class py_pkt4 : public PyObject {
+ py_pkt4();
+
+public:
+ isc::dhcp::Pkt4Ptr object;
+};
+
+extern PyTypeObject pkt4_type;
+
+bool initmod_pkt4(PyObject* mod);
+
+} // namespace python
+} // namespace isc
+
+#endif // PPKT4_H
--- /dev/null
+// Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <cc/data.h>
+#include <dhcp/pkt4.h>
+#include <dhcpsrv/parsers/dhcp_parsers.h>
+#include <dhcpsrv/callout_handle_store.h>
+#include <hooks/hooks_manager.h>
+#include <log/logger_support.h>
+
+#include <boost/foreach.hpp>
+
+#include <iostream>
+
+using namespace std;
+using namespace isc;
+using namespace isc::asiolink;
+using namespace isc::data;
+using namespace isc::dhcp;
+using namespace isc::hooks;
+using namespace isc::log;
+
+// config fragment for hooks-libraries
+const string config =
+ "{ \"hooks-libraries\": ["
+ " { \"library\": \"kea.so\", "
+ " \"parameters\": "
+ " { \"program\": \"kea\", "
+ " \"script\": \"hook\" }"
+ " }] }";
+
+// main routine
+int main() {
+ // must be first
+ int hi_pkt4_receive = HooksManager::registerHook("pkt4_receive");
+ cout << "pkt4_receive is hook#" << hi_pkt4_receive << "\n";
+
+ initLogger();
+
+ // check if there is a library already loaded
+ vector<string> hooks_libraries = HooksManager::getLibraryNames();
+ if (!hooks_libraries.empty()) {
+ cerr << "hooks_libraries is not empty\n";
+ }
+
+ // parse config into json
+ ElementPtr json = Element::fromJSON(config);
+ if (!json) {
+ cerr << "fatal: fromJSON failed\n";
+ exit(-1);
+ }
+ cout << "config parsed\n";
+
+ // call the hooks-libraries parser
+ boost::shared_ptr<HooksLibrariesParser> parser;
+ try {
+ const map<string, ConstElementPtr>& cmap = json->mapValue();
+ if (cmap.empty()) {
+ cerr << "fatal: config map is empty\n";
+ exit(-1);
+ }
+ if (cmap.size() > 1) {
+ cerr << "config map has more than one element\n";
+ }
+ if (cmap.count("hooks-libraries") == 0) {
+ cerr << "fatal: no \"hooks-libraries\" in config\n";
+ exit(-1);
+ }
+ const ConstElementPtr& hl_value = cmap.find("hooks-libraries")->second;
+ if (!hl_value) {
+ cerr << "fatal: empty \"hooks-libraries\" value\n";
+ exit(-1);
+ }
+ parser.reset(new HooksLibrariesParser("hooks-libraries"));
+ parser->build(hl_value);
+ parser->commit();
+ cout << "config committed\n";
+ } catch (const Exception& ex) {
+ cerr << "fatal: config parsing failed: " << ex.what() << "\n";
+ exit(-1);
+ }
+
+ // check if the library was loaded
+ HookLibsCollection libraries;
+ bool changed = false;
+ parser->getLibraries(libraries, changed);
+ if (!changed) {
+ cerr << "commit didn't change libraries\n";
+ }
+ if (libraries.empty()) {
+ cerr << "fatal: no libraries\n";
+ exit(-1);
+ }
+ if (libraries.size() > 1) {
+ cerr << "more than one library\n";
+ }
+ cout << "library is \"" + libraries[0].first + "\"\n";
+ if (libraries[0].first != "kea.so") {
+ cerr << "fatal: library is not \"kea.so\"\n";
+ exit(-1);
+ }
+ ConstElementPtr params = libraries[0].second;
+ if (!params) {
+ cerr << "no parameters\n";
+ } else {
+ cout << "got " << params->size() << " parameters\n";
+ }
+
+ // note we can't know this way if it was successfully loaded
+
+ // get the callout
+ if (!HooksManager::calloutsPresent(hi_pkt4_receive)) {
+ cerr << "fatal: no callout present for pkt4_receive\n";
+ exit(-1);
+ }
+
+ // from pkt4_unittests.cc
+ Pkt4Ptr pkt(new Pkt4(DHCPDISCOVER, 0x12345678));
+ const uint8_t macAddr[] = {0, 1, 2, 3, 4, 5};
+ vector<uint8_t> vectorMacAddr(macAddr, macAddr + sizeof(macAddr));
+ pkt->setHWAddr(6, 6, vectorMacAddr);
+ pkt->setHops(13);
+ // Transaction-id is already set.
+ pkt->setSecs(42);
+ pkt->setFlags(BOOTP_BROADCAST);
+ pkt->setCiaddr(IOAddress("192.0.2.1"));
+ pkt->setYiaddr(IOAddress("1.2.3.4"));
+ pkt->setSiaddr(IOAddress("192.0.2.255"));
+ pkt->setGiaddr(IOAddress("255.255.255.255"));
+ // Chaddr already set with setHWAddr().
+
+ // from dhcp4_srv.cc
+ CalloutHandlePtr co_handle = getCalloutHandle(pkt);
+ co_handle->deleteAllArguments();
+ co_handle->setArgument("query4", pkt);
+ cout << "calling pkt4_receive callout\n";
+ HooksManager::callCallouts(hi_pkt4_receive, *co_handle);
+ cout << "pkt4_receive callout status " << co_handle->getStatus() << "\n";
+ co_handle->getArgument("query4", pkt);
+
+ // TODO...
+
+ exit(0);
+}