diff options
Diffstat (limited to 'pythonmod')
| -rw-r--r-- | pythonmod/doc/examples/example6.rst | 61 | ||||
| -rw-r--r-- | pythonmod/examples/inplace_callbacks.py | 103 | ||||
| -rw-r--r-- | pythonmod/examples/resip.py | 20 | ||||
| -rw-r--r-- | pythonmod/interface.i | 17 | ||||
| -rw-r--r-- | pythonmod/pythonmod.h | 3 |
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 */ |
