summaryrefslogtreecommitdiff
path: root/pythonmod
diff options
context:
space:
mode:
authorDag-Erling Smørgrav <des@FreeBSD.org>2018-09-10 16:32:55 +0000
committerDag-Erling Smørgrav <des@FreeBSD.org>2018-09-10 16:32:55 +0000
commitdcaa814d350c5ee7deb2164502a24f2f698b9799 (patch)
tree9cb62373e6c424da021043a171564ced3bb19501 /pythonmod
parent4aea2433fa04a7a86c8972869bd021b7a3622dc8 (diff)
Notes
Diffstat (limited to 'pythonmod')
-rw-r--r--pythonmod/doc/examples/example6.rst61
-rw-r--r--pythonmod/examples/inplace_callbacks.py103
-rw-r--r--pythonmod/examples/resip.py20
-rw-r--r--pythonmod/interface.i17
-rw-r--r--pythonmod/pythonmod.h3
5 files changed, 165 insertions, 39 deletions
diff --git a/pythonmod/doc/examples/example6.rst b/pythonmod/doc/examples/example6.rst
index eb32540229469..d294fb8be618e 100644
--- a/pythonmod/doc/examples/example6.rst
+++ b/pythonmod/doc/examples/example6.rst
@@ -40,9 +40,12 @@ The callback function's prototype is the following:
.. code-block:: python
- def inplace_reply_callback(qinfo, qstate, rep, rcode, edns, opt_list_out, region):
- """Function that will be registered as an inplace callback function.
+ def inplace_reply_callback(qinfo, qstate, rep, rcode, edns, opt_list_out,
+ region, **kwargs):
+ """
+ Function that will be registered as an inplace callback function.
It will be called when answering with a resolved query.
+
:param qinfo: query_info struct;
:param qstate: module qstate. It contains the available opt_lists; It
SHOULD NOT be altered;
@@ -54,7 +57,13 @@ The callback function's prototype is the following:
reply. It can be populated with EDNS options;
:param region: region to allocate temporary data. Needs to be used when we
want to append a new option to opt_list_out.
+ :param **kwargs: Dictionary that may contain parameters added in a future
+ release. Current parameters:
+ ``repinfo``: Reply information for a communication point (comm_reply).
+ It is None when the callback happens in the mesh states.
+
:return: True on success, False on failure.
+
"""
.. note:: The function's name is irrelevant.
@@ -76,9 +85,12 @@ The callback function's prototype is the following:
.. code-block:: python
- def inplace_cache_callback(qinfo, qstate, rep, rcode, edns, opt_list_out, region):
- """Function that will be registered as an inplace callback function.
+ def inplace_cache_callback(qinfo, qstate, rep, rcode, edns, opt_list_out,
+ region, **kwargs):
+ """
+ Function that will be registered as an inplace callback function.
It will be called when answering from the cache.
+
:param qinfo: query_info struct;
:param qstate: module qstate. None;
:param rep: reply_info struct;
@@ -90,7 +102,17 @@ The callback function's prototype is the following:
reply. It can be populated with EDNS options;
:param region: region to allocate temporary data. Needs to be used when we
want to append a new option to opt_list_out.
+ :param **kwargs: Dictionary that may contain parameters added in a future
+ release. Current parameters:
+ ``repinfo``: Reply information for a communication point (comm_reply).
+ It is None when the callback happens in the mesh
+ states(modules).
+
:return: True on success, False on failure.
+
+ For demonstration purposes we want to see if EDNS option 65002 is present
+ and reply with a new value.
+
"""
.. note:: The function's name is irrelevant.
@@ -112,9 +134,12 @@ The callback function's prototype is the following:
.. code-block:: python
- def inplace_local_callback(qinfo, qstate, rep, rcode, edns, opt_list_out, region):
- """Function that will be registered as an inplace callback function.
+ def inplace_local_callback(qinfo, qstate, rep, rcode, edns, opt_list_out,
+ region, **kwargs):
+ """
+ Function that will be registered as an inplace callback function.
It will be called when answering from local data.
+
:param qinfo: query_info struct;
:param qstate: module qstate. None;
:param rep: reply_info struct;
@@ -126,7 +151,14 @@ The callback function's prototype is the following:
reply. It can be populated with EDNS options;
:param region: region to allocate temporary data. Needs to be used when we
want to append a new option to opt_list_out.
+ :param **kwargs: Dictionary that may contain parameters added in a future
+ release. Current parameters:
+ ``repinfo``: Reply information for a communication point (comm_reply).
+ It is None when the callback happens in the mesh
+ states(modules).
+
:return: True on success, False on failure.
+
"""
.. note:: The function's name is irrelevant.
@@ -148,9 +180,12 @@ The callback function's prototype is the following:
.. code-block:: python
- def inplace_servfail_callback(qinfo, qstate, rep, rcode, edns, opt_list_out, region):
- """Function that will be registered as an inplace callback function.
+ def inplace_servfail_callback(qinfo, qstate, rep, rcode, edns, opt_list_out,
+ region, **kwargs):
+ """
+ Function that will be registered as an inplace callback function.
It will be called when answering with SERVFAIL.
+
:param qinfo: query_info struct;
:param qstate: module qstate. If not None the relevant opt_lists are
available here;
@@ -163,7 +198,17 @@ The callback function's prototype is the following:
reply. It can be populated with EDNS options;
:param region: region to allocate temporary data. Needs to be used when we
want to append a new option to opt_list_out.
+ :param **kwargs: Dictionary that may contain parameters added in a future
+ release. Current parameters:
+ ``repinfo``: Reply information for a communication point (comm_reply).
+ It is None when the callback happens in the mesh
+ states(modules).
+
:return: True on success, False on failure.
+
+ For demonstration purposes we want to reply with an empty EDNS code '65003'
+ and log the IP address(es) of the client(s).
+
"""
.. note:: The function's name is irrelevant.
diff --git a/pythonmod/examples/inplace_callbacks.py b/pythonmod/examples/inplace_callbacks.py
index 02ee56e36e21e..e47fc8292be27 100644
--- a/pythonmod/examples/inplace_callbacks.py
+++ b/pythonmod/examples/inplace_callbacks.py
@@ -6,18 +6,18 @@
Copyright (c) 2016, NLnet Labs.
This software is open source.
-
+
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
-
+
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
-
+
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
-
+
* Neither the name of the organization nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
@@ -43,6 +43,8 @@
# This query returns SERVFAIL as the txt record of bogus.nlnetlabs.nl is
# intentionally bogus. The reply will contain an empty EDNS option
# with option code 65003.
+# Unbound will also log the source address(es) of the client(s) that made
+# the request.
# (unbound needs to be validating for this example to work)
# Useful functions:
@@ -70,9 +72,11 @@
def inplace_reply_callback(qinfo, qstate, rep, rcode, edns, opt_list_out,
- region):
- """Function that will be registered as an inplace callback function.
+ region, **kwargs):
+ """
+ Function that will be registered as an inplace callback function.
It will be called when answering with a resolved query.
+
:param qinfo: query_info struct;
:param qstate: module qstate. It contains the available opt_lists; It
SHOULD NOT be altered;
@@ -84,16 +88,25 @@ def inplace_reply_callback(qinfo, qstate, rep, rcode, edns, opt_list_out,
reply. It can be populated with EDNS options;
:param region: region to allocate temporary data. Needs to be used when we
want to append a new option to opt_list_out.
+ :param **kwargs: Dictionary that may contain parameters added in a future
+ release. Current parameters:
+ ``repinfo``: Reply information for a communication point (comm_reply).
+ It is None when the callback happens in the mesh
+ states(modules).
+
:return: True on success, False on failure.
+
"""
log_info("python: called back while replying.")
return True
def inplace_cache_callback(qinfo, qstate, rep, rcode, edns, opt_list_out,
- region):
- """Function that will be registered as an inplace callback function.
+ region, **kwargs):
+ """
+ Function that will be registered as an inplace callback function.
It will be called when answering from the cache.
+
:param qinfo: query_info struct;
:param qstate: module qstate. None;
:param rep: reply_info struct;
@@ -105,10 +118,17 @@ def inplace_cache_callback(qinfo, qstate, rep, rcode, edns, opt_list_out,
reply. It can be populated with EDNS options;
:param region: region to allocate temporary data. Needs to be used when we
want to append a new option to opt_list_out.
+ :param **kwargs: Dictionary that may contain parameters added in a future
+ release. Current parameters:
+ ``repinfo``: Reply information for a communication point (comm_reply).
+ It is None when the callback happens in the mesh
+ states(modules).
+
:return: True on success, False on failure.
For demonstration purposes we want to see if EDNS option 65002 is present
and reply with a new value.
+
"""
log_info("python: called back while answering from cache.")
# Inspect the incoming EDNS options.
@@ -134,9 +154,11 @@ def inplace_cache_callback(qinfo, qstate, rep, rcode, edns, opt_list_out,
def inplace_local_callback(qinfo, qstate, rep, rcode, edns, opt_list_out,
- region):
- """Function that will be registered as an inplace callback function.
+ region, **kwargs):
+ """
+ Function that will be registered as an inplace callback function.
It will be called when answering from local data.
+
:param qinfo: query_info struct;
:param qstate: module qstate. None;
:param rep: reply_info struct;
@@ -148,7 +170,14 @@ def inplace_local_callback(qinfo, qstate, rep, rcode, edns, opt_list_out,
reply. It can be populated with EDNS options;
:param region: region to allocate temporary data. Needs to be used when we
want to append a new option to opt_list_out.
+ :param **kwargs: Dictionary that may contain parameters added in a future
+ release. Current parameters:
+ ``repinfo``: Reply information for a communication point (comm_reply).
+ It is None when the callback happens in the mesh
+ states(modules).
+
:return: True on success, False on failure.
+
"""
log_info("python: called back while replying with local data or chaos"
" reply.")
@@ -156,9 +185,11 @@ def inplace_local_callback(qinfo, qstate, rep, rcode, edns, opt_list_out,
def inplace_servfail_callback(qinfo, qstate, rep, rcode, edns, opt_list_out,
- region):
- """Function that will be registered as an inplace callback function.
+ region, **kwargs):
+ """
+ Function that will be registered as an inplace callback function.
It will be called when answering with SERVFAIL.
+
:param qinfo: query_info struct;
:param qstate: module qstate. If not None the relevant opt_lists are
available here;
@@ -171,23 +202,62 @@ def inplace_servfail_callback(qinfo, qstate, rep, rcode, edns, opt_list_out,
reply. It can be populated with EDNS options;
:param region: region to allocate temporary data. Needs to be used when we
want to append a new option to opt_list_out.
+ :param **kwargs: Dictionary that may contain parameters added in a future
+ release. Current parameters:
+ ``repinfo``: Reply information for a communication point (comm_reply).
+ It is None when the callback happens in the mesh
+ states(modules).
+
:return: True on success, False on failure.
- For demonstration purposes we want to reply with an empty EDNS code '65003'.
+ For demonstration purposes we want to reply with an empty EDNS code '65003'
+ and log the IP address(es) of the client(s).
+
"""
log_info("python: called back while servfail.")
+ # Append the example ENDS option
b = bytearray.fromhex("")
edns_opt_list_append(opt_list_out, 65003, b, region)
+
+ # Log the client(s) IP address(es)
+ comm_reply = kwargs['repinfo']
+ if comm_reply:
+ # If it is not None this callback was called before the query reached
+ # the mesh states(modules). There is only one client associated with
+ # this query.
+ addr = comm_reply.addr
+ port = comm_reply.port
+ addr_family = comm_reply.family
+ log_info("python: Client IP: {}({}), port: {}"
+ "".format(addr, addr_family, port))
+ else:
+ # If it is not None this callback was called while the query is in the
+ # mesh states(modules). In this case they may be multiple clients
+ # waiting for this query.
+ # The following code is the same as with the resip.py example.
+ rl = qstate.mesh_info.reply_list
+ while (rl):
+ if rl.query_reply:
+ q = rl.query_reply
+ log_info("python: Client IP: {}({}), port: {}"
+ "".format(q.addr, q.family, q.port))
+ rl = rl.next
+
+
return True
def init_standard(id, env):
- """New version of the init function.
+ """
+ New version of the init function.
+
The function's signature is the same as the C counterpart and allows for
extra functionality during init.
+
..note:: This function is preferred by unbound over the old init function.
..note:: The previously accessible configuration options can now be found in
env.cgf.
+
"""
log_info("python: inited script {}".format(env.cfg.python_script))
@@ -215,11 +285,14 @@ def init_standard(id, env):
def init(id, cfg):
- """Previous version init function.
+ """
+ Previous version of the init function.
+
..note:: This function is still supported for backwards compatibility when
the init_standard function is missing. When init_standard is
present this function SHOULD be omitted to avoid confusion to the
reader.
+
"""
return True
diff --git a/pythonmod/examples/resip.py b/pythonmod/examples/resip.py
index 6bcac7252cd29..4a9f24fc64480 100644
--- a/pythonmod/examples/resip.py
+++ b/pythonmod/examples/resip.py
@@ -47,7 +47,7 @@ def deinit(id): return True
def inform_super(id, qstate, superqstate, qdata): return True
def operate(id, event, qstate, qdata):
- print "Operate", event,"state:",qstate
+ print("Operate {} state: {}".format(event, qstate))
# Please note that if this module blocks, by moving to the validator
# to validate or iterator to lookup or spawn a subquery to look up,
@@ -61,14 +61,14 @@ def operate(id, event, qstate, qdata):
msg = DNSMessage(qstate.qinfo.qname_str, RR_TYPE_TXT, RR_CLASS_IN, PKT_QR | PKT_RA | PKT_AA)
#append RR
if (qstate.qinfo.qtype == RR_TYPE_TXT) or (qstate.qinfo.qtype == RR_TYPE_ANY):
- rl = qstate.mesh_info.reply_list
- while (rl):
- if rl.query_reply:
- q = rl.query_reply
- # The TTL of 0 is mandatory, otherwise it ends up in
- # the cache, and is returned to other IP addresses.
- msg.answer.append("%s 0 IN TXT \"%s %d (%s)\"" % (qstate.qinfo.qname_str, q.addr,q.port,q.family))
- rl = rl.next
+ rl = qstate.mesh_info.reply_list
+ while (rl):
+ if rl.query_reply:
+ q = rl.query_reply
+ # The TTL of 0 is mandatory, otherwise it ends up in
+ # the cache, and is returned to other IP addresses.
+ msg.answer.append("%s 0 IN TXT \"%s %d (%s)\"" % (qstate.qinfo.qname_str, q.addr,q.port,q.family))
+ rl = rl.next
#set qstate.return_msg
if not msg.set_return_msg(qstate):
@@ -90,7 +90,7 @@ def operate(id, event, qstate, qdata):
log_info("pythonmod: iterator module done")
qstate.ext_state[id] = MODULE_FINISHED
return True
-
+
log_err("pythonmod: bad event")
qstate.ext_state[id] = MODULE_ERROR
return True
diff --git a/pythonmod/interface.i b/pythonmod/interface.i
index df06d1064b040..96accb69cca6e 100644
--- a/pythonmod/interface.i
+++ b/pythonmod/interface.i
@@ -1365,11 +1365,12 @@ int edns_opt_list_append(struct edns_option** list, uint16_t code, size_t len,
int python_inplace_cb_reply_generic(struct query_info* qinfo,
struct module_qstate* qstate, struct reply_info* rep, int rcode,
struct edns_data* edns, struct edns_option** opt_list_out,
- struct regional* region, int id, void* python_callback)
+ struct comm_reply* repinfo, struct regional* region, int id,
+ void* python_callback)
{
PyObject *func, *py_edns, *py_qstate, *py_opt_list_out, *py_qinfo;
- PyObject *py_rep, *py_region;
- PyObject *result;
+ PyObject *py_rep, *py_repinfo, *py_region;
+ PyObject *py_args, *py_kwargs, *result;
int res = 0;
PyGILState_STATE gstate = PyGILState_Ensure();
@@ -1381,15 +1382,21 @@ int edns_opt_list_append(struct edns_option** list, uint16_t code, size_t len,
SWIGTYPE_p_p_edns_option, 0);
py_qinfo = SWIG_NewPointerObj((void*) qinfo, SWIGTYPE_p_query_info, 0);
py_rep = SWIG_NewPointerObj((void*) rep, SWIGTYPE_p_reply_info, 0);
+ py_repinfo = SWIG_NewPointerObj((void*) repinfo, SWIGTYPE_p_comm_reply, 0);
py_region = SWIG_NewPointerObj((void*) region, SWIGTYPE_p_regional, 0);
- result = PyObject_CallFunction(func, "OOOiOOO", py_qinfo, py_qstate,
- py_rep, rcode, py_edns, py_opt_list_out, py_region);
+ py_args = Py_BuildValue("(OOOiOOO)", py_qinfo, py_qstate, py_rep,
+ rcode, py_edns, py_opt_list_out, py_region);
+ py_kwargs = Py_BuildValue("{s:O}", "repinfo", py_repinfo);
+ result = PyObject_Call(func, py_args, py_kwargs);
Py_XDECREF(py_edns);
Py_XDECREF(py_qstate);
Py_XDECREF(py_opt_list_out);
Py_XDECREF(py_qinfo);
Py_XDECREF(py_rep);
+ Py_XDECREF(py_repinfo);
Py_XDECREF(py_region);
+ Py_XDECREF(py_args);
+ Py_XDECREF(py_kwargs);
if (result) {
res = PyInt_AsLong(result);
}
diff --git a/pythonmod/pythonmod.h b/pythonmod/pythonmod.h
index 7c7c0e751c30c..991ae51a20a74 100644
--- a/pythonmod/pythonmod.h
+++ b/pythonmod/pythonmod.h
@@ -72,5 +72,6 @@ size_t pythonmod_get_mem(struct module_env* env, int id);
int python_inplace_cb_reply_generic(struct query_info* qinfo,
struct module_qstate* qstate, struct reply_info* rep, int rcode,
struct edns_data* edns, struct edns_option** opt_list_out,
- struct regional* region, int id, void* python_callback);
+ struct comm_reply* repinfo, struct regional* region, int id,
+ void* python_callback);
#endif /* PYTHONMOD_H */