aboutsummaryrefslogtreecommitdiff
path: root/external/unbound/pythonmod
diff options
context:
space:
mode:
Diffstat (limited to 'external/unbound/pythonmod')
-rw-r--r--external/unbound/pythonmod/doc/conf.py5
-rw-r--r--external/unbound/pythonmod/doc/examples/example1.rst14
-rw-r--r--external/unbound/pythonmod/doc/examples/example2.rst45
-rw-r--r--external/unbound/pythonmod/doc/examples/example4.rst180
-rw-r--r--external/unbound/pythonmod/doc/examples/example5.rst191
-rw-r--r--external/unbound/pythonmod/doc/examples/example6.rst299
-rw-r--r--external/unbound/pythonmod/doc/examples/index.rst17
-rw-r--r--external/unbound/pythonmod/doc/install.rst44
-rw-r--r--external/unbound/pythonmod/doc/modules/functions.rst139
-rw-r--r--external/unbound/pythonmod/doc/modules/struct.rst187
-rw-r--r--external/unbound/pythonmod/interface.i565
-rw-r--r--external/unbound/pythonmod/pythonmod.c111
-rw-r--r--external/unbound/pythonmod/pythonmod.h12
-rw-r--r--external/unbound/pythonmod/pythonmod_utils.c5
-rw-r--r--external/unbound/pythonmod/test-edns.conf17
-rw-r--r--external/unbound/pythonmod/test-inplace_callbacks.conf17
16 files changed, 1502 insertions, 346 deletions
diff --git a/external/unbound/pythonmod/doc/conf.py b/external/unbound/pythonmod/doc/conf.py
index bc7a5aba6..7fcfe2d05 100644
--- a/external/unbound/pythonmod/doc/conf.py
+++ b/external/unbound/pythonmod/doc/conf.py
@@ -80,10 +80,13 @@ pygments_style = 'sphinx'
# Options for HTML output
# -----------------------
+# The theme that the html output should use.
+html_theme = "classic"
+
# The style sheet to use for HTML and HTML Help pages. A file of that name
# must exist either in Sphinx' static/ path, or in one of the custom paths
# given in html_static_path.
-html_style = 'default.css'
+#html_style = 'default.css'
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
diff --git a/external/unbound/pythonmod/doc/examples/example1.rst b/external/unbound/pythonmod/doc/examples/example1.rst
index b49e64409..ccd76da5a 100644
--- a/external/unbound/pythonmod/doc/examples/example1.rst
+++ b/external/unbound/pythonmod/doc/examples/example1.rst
@@ -1,10 +1,12 @@
.. _log_handler:
Packet logger
-=========================
+=============
This example shows how to log and print details about query and response.
-As soon as the ``iterator`` has finished (event is :data:`module_event_moddone`), ``qstate.return_msg`` contains response packet or ``None``.
+As soon as the ``iterator`` has finished (event is
+:data:`module_event_moddone`), ``qstate.return_msg`` contains response packet
+or ``None``.
This packet will be send to a client that asked for it.
Complete source code
@@ -14,14 +16,16 @@ Complete source code
:language: python
Testing
-------------------
+-------
Run the unbound server:
``root@localhost>unbound -dv -c ./test-log.conf``
-In case you use own configuration file, don't forget to enable python module: ``module-config: "validator python iterator"`` and use valid script path: ``python-script: "./examples/log.py"``.
+In case you use own configuration file, don't forget to enable python module:
+``module-config: "validator python iterator"`` and use valid script path:
+``python-script: "./examples/log.py"``.
-Example of output::
+Example of output::
[1231790168] unbound[7941:0] info: response for <f.gtld-servers.NET. AAAA IN>
[1231790168] unbound[7941:0] info: reply from <gtld-servers.NET.> 192.5.6.31#53
diff --git a/external/unbound/pythonmod/doc/examples/example2.rst b/external/unbound/pythonmod/doc/examples/example2.rst
index f00fcc239..4ba9239a0 100644
--- a/external/unbound/pythonmod/doc/examples/example2.rst
+++ b/external/unbound/pythonmod/doc/examples/example2.rst
@@ -1,12 +1,14 @@
Response generation
-=====================
+===================
This example shows how to handle queries and generate response packet.
.. note::
- If the python module is the first module and validator module is enabled (``module-config: "python validator iterator"``),
- a return_msg security flag has to be set at least to 2. Leaving security flag untouched causes that the
- response will be refused by unbound worker as unbound will consider it as non-valid response.
+ If the python module is the first module and validator module is enabled
+ (``module-config: "python validator iterator"``), a return_msg security flag
+ has to be set at least to 2. Leaving security flag untouched causes that the
+ response will be refused by unbound worker as unbound will consider it as
+ non-valid response.
Complete source code
--------------------
@@ -27,20 +29,21 @@ Query for a A record ending with .localdomain
Dig produces the following output::
- ;; global options: printcmd
- ;; Got answer:
- ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 48426
- ;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
-
- ;; QUESTION SECTION:
- ;test.xxx.localdomain. IN A
-
- ;; ANSWER SECTION:
- test.xxx.localdomain. 10 IN A 127.0.0.1
-
- ;; Query time: 2 msec
- ;; SERVER: 127.0.0.1#53(127.0.0.1)
- ;; WHEN: Mon Jan 01 12:46:02 2009
- ;; MSG SIZE rcvd: 54
-
-As we handle (override) in python module only queries ending with "localdomain.", the unboud can still resolve host names.
+ ;; global options: printcmd
+ ;; Got answer:
+ ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 48426
+ ;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
+
+ ;; QUESTION SECTION:
+ ;test.xxx.localdomain. IN A
+
+ ;; ANSWER SECTION:
+ test.xxx.localdomain. 10 IN A 127.0.0.1
+
+ ;; Query time: 2 msec
+ ;; SERVER: 127.0.0.1#53(127.0.0.1)
+ ;; WHEN: Mon Jan 01 12:46:02 2009
+ ;; MSG SIZE rcvd: 54
+
+As we handle (override) in the python module only queries ending with
+``localdomain.``, unboud can still resolve host names.
diff --git a/external/unbound/pythonmod/doc/examples/example4.rst b/external/unbound/pythonmod/doc/examples/example4.rst
index b665351e8..338210990 100644
--- a/external/unbound/pythonmod/doc/examples/example4.rst
+++ b/external/unbound/pythonmod/doc/examples/example4.rst
@@ -1,15 +1,19 @@
DNS-based language dictionary
-===============================
+=============================
This example shows how to create a simple language dictionary based on **DNS**
-service within 15 minutes. The translation will be performed using TXT resource records.
+service within 15 minutes. The translation will be performed using TXT resource
+records.
Key parts
------------
+---------
Initialization
-~~~~~~~~~~~~~~~~~~~~~~~
-On **init()** module loads dictionary from a text file containing records in ``word [tab] translation`` format.
+~~~~~~~~~~~~~~
+
+On **init()** module loads dictionary from a text file containing records in
+``word [tab] translation`` format.
+
::
def init(id, cfg):
@@ -20,11 +24,14 @@ On **init()** module loads dictionary from a text file containing records in ``w
The suitable file can be found at http://slovnik.zcu.cz
DNS query and word lookup
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
+~~~~~~~~~~~~~~~~~~~~~~~~~
-Let's define the following format od DNS queries: ``word1[.]word2[.] ... wordN[.]{en,cs}[._dict_.cz.]``.
+Let's define the following format od DNS queries:
+``word1[.]word2[.] ... wordN[.]{en,cs}[._dict_.cz.]``.
Word lookup is done by simple ``dict`` lookup from broken DNS request.
-Query name is divided into a list of labels. This list is accessible as qname_list attribute.
+Query name is divided into a list of labels. This list is accessible as
+``qname_list`` attribute.
+
::
aword = ' '.join(qstate.qinfo.qname_list[0:-4]) #skip last four labels
@@ -37,35 +44,40 @@ Query name is divided into a list of labels. This list is accessible as qname_li
if (adict == "cs") and (aword in cz_dict):
words = cz_dict[aword] # CS -> EN
-In the first step, we get a string in the form: ``word1[space]word2[space]...word[space]``.
-In the second assignment, fourth label from the end is obtained. This label should contains *"cs"* or *"en"*.
-This label determines the direction of translation.
-
+In the first step, we get a string in the form:
+``word1[space]word2[space]...word[space]``.
+In the second assignment, fourth label from the end is obtained. This label
+should contains *"cs"* or *"en"*. This label determines the direction of
+translation.
Forming of a DNS reply
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+~~~~~~~~~~~~~~~~~~~~~~
DNS reply is formed only on valid match and added as TXT answer.
+
::
- msg = DNSMessage(qstate.qinfo.qname_str, RR_TYPE_TXT, RR_CLASS_IN, PKT_AA)
+ msg = DNSMessage(qstate.qinfo.qname_str, RR_TYPE_TXT, RR_CLASS_IN, PKT_AA)
- for w in words:
- msg.answer.append("%s 300 IN TXT \"%s\"" % (qstate.qinfo.qname_str, w.replace("\"", "\\\"")))
+ for w in words:
+ msg.answer.append("%s 300 IN TXT \"%s\"" % (qstate.qinfo.qname_str, w.replace("\"", "\\\"")))
- if not msg.set_return_msg(qstate):
- qstate.ext_state[id] = MODULE_ERROR
- return True
+ if not msg.set_return_msg(qstate):
+ qstate.ext_state[id] = MODULE_ERROR
+ return True
- qstate.return_rcode = RCODE_NOERROR
- qstate.ext_state[id] = MODULE_FINISHED
- return True
+ qstate.return_rcode = RCODE_NOERROR
+ qstate.ext_state[id] = MODULE_FINISHED
+ return True
-In the first step, a :class:`DNSMessage` instance is created for a given query *(type TXT)*.
+In the first step, a :class:`DNSMessage` instance is created for a given query
+*(type TXT)*.
The fourth argument specifies the flags *(authoritative answer)*.
-In the second step, we append TXT records containing the translation *(on the right side of RR)*.
+In the second step, we append TXT records containing the translation *(on the
+right side of RR)*.
Then, the response is finished and ``qstate.return_msg`` contains new response.
-If no error, the module sets :attr:`module_qstate.return_rcode` and :attr:`module_qstate.ext_state`.
+If no error, the module sets :attr:`module_qstate.return_rcode` and
+:attr:`module_qstate.ext_state`.
**Steps:**
@@ -82,80 +94,82 @@ Run the Unbound server:
In case you use own configuration file, don't forget to enable Python module::
- module-config: "validator python iterator"
+ module-config: "validator python iterator"
and use valid script path::
- python-script: "./examples/dict.py"
+ python-script: "./examples/dict.py"
The translation from english word *"a bar fly"* to Czech can be done by doing:
``>>>dig TXT @127.0.0.1 a.bar.fly.en._dict_.cz``
-::
+::
+
+ ; (1 server found)
+ ;; global options: printcmd
+ ;; Got answer:
+ ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 48691
+ ;; flags: aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
+
+ ;; QUESTION SECTION:
+ ;a.bar.fly.en._dict_.cz. IN TXT
+
+ ;; ANSWER SECTION:
+ a.bar.fly.en._dict_.cz. 300 IN TXT "barov\253 povale\232"
+
+ ;; Query time: 5 msec
+ ;; SERVER: 127.0.0.1#53(127.0.0.1)
+ ;; WHEN: Mon Jan 01 17:44:18 2009
+ ;; MSG SIZE rcvd: 67
- ; (1 server found)
- ;; global options: printcmd
- ;; Got answer:
- ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 48691
- ;; flags: aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
-
- ;; QUESTION SECTION:
- ;a.bar.fly.en._dict_.cz. IN TXT
-
- ;; ANSWER SECTION:
- a.bar.fly.en._dict_.cz. 300 IN TXT "barov\253 povale\232"
-
- ;; Query time: 5 msec
- ;; SERVER: 127.0.0.1#53(127.0.0.1)
- ;; WHEN: Mon Jan 01 17:44:18 2009
- ;; MSG SIZE rcvd: 67
-
``>>>dig TXT @127.0.0.1 nic.cs._dict_.cz``
+
::
- ; <<>> DiG 9.5.0-P2 <<>> TXT @127.0.0.1 nic.cs._dict_.cz
- ; (1 server found)
- ;; global options: printcmd
- ;; Got answer:
- ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 58710
- ;; flags: aa rd ra; QUERY: 1, ANSWER: 6, AUTHORITY: 0, ADDITIONAL: 0
-
- ;; QUESTION SECTION:
- ;nic.cs._dict_.cz. IN TXT
-
- ;; ANSWER SECTION:
- nic.cs._dict_.cz. 300 IN TXT "aught"
- nic.cs._dict_.cz. 300 IN TXT "naught"
- nic.cs._dict_.cz. 300 IN TXT "nihil"
- nic.cs._dict_.cz. 300 IN TXT "nix"
- nic.cs._dict_.cz. 300 IN TXT "nothing"
- nic.cs._dict_.cz. 300 IN TXT "zilch"
-
- ;; Query time: 0 msec
- ;; SERVER: 127.0.0.1#53(127.0.0.1)
- ;; WHEN: Mon Jan 01 17:45:39 2009
- ;; MSG SIZE rcvd: 143
-
-Proof that the unbound still works as resolver.
+ ; <<>> DiG 9.5.0-P2 <<>> TXT @127.0.0.1 nic.cs._dict_.cz
+ ; (1 server found)
+ ;; global options: printcmd
+ ;; Got answer:
+ ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 58710
+ ;; flags: aa rd ra; QUERY: 1, ANSWER: 6, AUTHORITY: 0, ADDITIONAL: 0
+
+ ;; QUESTION SECTION:
+ ;nic.cs._dict_.cz. IN TXT
+
+ ;; ANSWER SECTION:
+ nic.cs._dict_.cz. 300 IN TXT "aught"
+ nic.cs._dict_.cz. 300 IN TXT "naught"
+ nic.cs._dict_.cz. 300 IN TXT "nihil"
+ nic.cs._dict_.cz. 300 IN TXT "nix"
+ nic.cs._dict_.cz. 300 IN TXT "nothing"
+ nic.cs._dict_.cz. 300 IN TXT "zilch"
+
+ ;; Query time: 0 msec
+ ;; SERVER: 127.0.0.1#53(127.0.0.1)
+ ;; WHEN: Mon Jan 01 17:45:39 2009
+ ;; MSG SIZE rcvd: 143
+
+ Proof that the unbound still works as resolver.
``>>>dig A @127.0.0.1 www.nic.cz``
+
::
- ; (1 server found)
- ;; global options: printcmd
- ;; Got answer:
- ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 19996
- ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 3, ADDITIONAL: 5
-
- ;; QUESTION SECTION:
- ;www.nic.cz. IN A
-
- ;; ANSWER SECTION:
- www.nic.cz. 1662 IN A 217.31.205.50
-
- ;; AUTHORITY SECTION:
- ...
+ ; (1 server found)
+ ;; global options: printcmd
+ ;; Got answer:
+ ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 19996
+ ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 3, ADDITIONAL: 5
+
+ ;; QUESTION SECTION:
+ ;www.nic.cz. IN A
+
+ ;; ANSWER SECTION:
+ www.nic.cz. 1662 IN A 217.31.205.50
+
+ ;; AUTHORITY SECTION:
+ ...
Complete source code
--------------------
diff --git a/external/unbound/pythonmod/doc/examples/example5.rst b/external/unbound/pythonmod/doc/examples/example5.rst
new file mode 100644
index 000000000..058fc331e
--- /dev/null
+++ b/external/unbound/pythonmod/doc/examples/example5.rst
@@ -0,0 +1,191 @@
+EDNS options
+============
+
+This example shows how to interact with EDNS options.
+
+When quering unbound with the EDNS option ``65001`` and data ``0xc001`` we
+expect an answer with the same EDNS option code and data ``0xdeadbeef``.
+
+
+Key parts
+~~~~~~~~~
+
+This example relies on the following functionalities:
+
+
+Registering EDNS options
+------------------------
+
+By registering EDNS options we can tune unbound's behavior when encountering a
+query with a known EDNS option. The two available options are:
+
+- ``bypass_cache_stage``: If set to ``True`` unbound will not try to answer
+ from cache. Instead execution is passed to the modules
+- ``no_aggregation``: If set to ``True`` unbound will consider this query
+ unique and will not aggregate it with similar queries
+
+Both values default to ``False``.
+
+.. code-block:: python
+
+ if not register_edns_option(env, 65001, bypass_cache_stage=True,
+ no_aggregation=True):
+ log_info("python: Could not register EDNS option {}".format(65001))
+
+
+EDNS option lists
+-----------------
+
+EDNS option lists can be found in the :class:`module_qstate` class. There are
+four available lists in total:
+
+- :class:`module_qstate.edns_opts_front_in`: options that came from the client
+ side. **Should not** be changed
+- :class:`module_qstate.edns_opts_back_out`: options that will be sent to the
+ server side. Can be populated by edns literate modules
+- :class:`module_qstate.edns_opts_back_in`: options that came from the server
+ side. **Should not** be changed
+- :class:`module_qstate.edns_opts_front_out`: options that will be sent to the
+ client side. Can be populated by edns literate modules
+
+Each list element has the following members:
+
+- ``code``: the EDNS option code;
+- ``data``: the EDNS option data.
+
+
+Reading an EDNS option list
+...........................
+
+The lists' contents can be accessed in python by their ``_iter`` counterpart as
+an iterator:
+
+.. code-block:: python
+
+ if not edns_opt_list_is_empty(qstate.edns_opts_front_in):
+ for o in qstate.edns_opts_front_in_iter:
+ log_info("python: Code: {}, Data: '{}'".format(o.code,
+ "".join('{:02x}'.format(x) for x in o.data)))
+
+
+Writing to an EDNS option list
+..............................
+
+By appending to an EDNS option list we can add new EDNS options. The new
+element is going to be allocated in :class:`module_qstate.region`. The data
+**must** be represented with a python ``bytearray``:
+
+.. code-block:: python
+
+ b = bytearray.fromhex("deadbeef")
+ if not edns_opt_list_append(qstate.edns_opts_front_out,
+ o.code, b, qstate.region):
+ log_info("python: Could not append EDNS option {}".format(o.code))
+
+We can also remove an EDNS option code from an EDNS option list.
+
+.. code-block:: python
+
+ if not edns_opt_list_remove(edns_opt_list, code):
+ log_info("python: Option code {} was not found in the "
+ "list.".format(code))
+
+.. note:: All occurences of the EDNS option code will be removed from the list:
+
+
+Controlling other modules' cache behavior
+-----------------------------------------
+
+During the modules' operation, some modules may interact with the cache
+(e.g., iterator). This behavior can be controlled by using the following
+:class:`module_qstate` flags:
+
+- :class:`module_qstate.no_cache_lookup`: Modules *operating after* this module
+ will not lookup the cache for an answer
+- :class:`module_qstate.no_cache_store`: Modules *operating after* this module
+ will not store the response in the cache
+
+Both values default to ``0``.
+
+.. code-block:: python
+
+ def operate(id, event, qstate, qdata):
+ if (event == MODULE_EVENT_NEW) or (event == MODULE_EVENT_PASS):
+ # Detect if edns option code 56001 is present from the client side. If
+ # so turn on the flags for cache management.
+ if not edns_opt_list_is_empty(qstate.edns_opts_front_in):
+ log_info("python: searching for edns option code 65001 during NEW "
+ "or PASS event ")
+ for o in qstate.edns_opts_front_in_iter:
+ if o.code == 65001:
+ log_info("python: found edns option code 65001")
+ # Instruct other modules to not lookup for an
+ # answer in the cache.
+ qstate.no_cache_lookup = 1
+ log_info("python: enabled no_cache_lookup")
+
+ # Instruct other modules to not store the answer in
+ # the cache.
+ qstate.no_cache_store = 1
+ log_info("python: enabled no_cache_store")
+
+
+Testing
+~~~~~~~
+
+Run the Unbound server: ::
+
+ root@localhost$ unbound -dv -c ./test-edns.conf
+
+In case you use your own configuration file, don't forget to enable the Python
+module::
+
+ module-config: "validator python iterator"
+
+and use a valid script path::
+
+ python-script: "./examples/edns.py"
+
+Quering with EDNS option ``65001:0xc001``:
+
+::
+
+ root@localhost$ dig @localhost nlnetlabs.nl +ednsopt=65001:c001
+
+ ; <<>> DiG 9.10.3-P4-Ubuntu <<>> @localhost nlnetlabs.nl +ednsopt=65001:c001
+ ; (1 server found)
+ ;; global options: +cmd
+ ;; Got answer:
+ ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 33450
+ ;; flags: qr rd ra ad; QUERY: 1, ANSWER: 1, AUTHORITY: 4, ADDITIONAL: 3
+
+ ;; OPT PSEUDOSECTION:
+ ; EDNS: version: 0, flags:; udp: 4096
+ ; OPT=65001: de ad be ef ("....")
+ ;; QUESTION SECTION:
+ ;nlnetlabs.nl. IN A
+
+ ;; ANSWER SECTION:
+ nlnetlabs.nl. 10200 IN A 185.49.140.10
+
+ ;; AUTHORITY SECTION:
+ nlnetlabs.nl. 10200 IN NS anyns.pch.net.
+ nlnetlabs.nl. 10200 IN NS ns.nlnetlabs.nl.
+ nlnetlabs.nl. 10200 IN NS ns-ext1.sidn.nl.
+ nlnetlabs.nl. 10200 IN NS sec2.authdns.ripe.net.
+
+ ;; ADDITIONAL SECTION:
+ ns.nlnetlabs.nl. 10200 IN AAAA 2a04:b900::8:0:0:60
+ ns.nlnetlabs.nl. 10200 IN A 185.49.140.60
+
+ ;; Query time: 10 msec
+ ;; SERVER: 127.0.0.1#53(127.0.0.1)
+ ;; WHEN: Mon Dec 05 14:50:56 CET 2016
+ ;; MSG SIZE rcvd: 212
+
+
+Complete source code
+~~~~~~~~~~~~~~~~~~~~
+
+.. literalinclude:: ../../examples/edns.py
+ :language: python
diff --git a/external/unbound/pythonmod/doc/examples/example6.rst b/external/unbound/pythonmod/doc/examples/example6.rst
new file mode 100644
index 000000000..07117cd55
--- /dev/null
+++ b/external/unbound/pythonmod/doc/examples/example6.rst
@@ -0,0 +1,299 @@
+Inplace callbacks
+=================
+
+This example shows how to register and use inplace callback functions. These
+functions are going to be called just before unbound replies back to a client.
+They can perform certain actions without interrupting unbound's execution flow
+(e.g. add/remove EDNS options, manipulate the reply).
+
+Two different scenarios will be shown:
+
+- If answering from cache and the client used EDNS option code ``65002`` we
+ will answer with the same code but with data ``0xdeadbeef``;
+- When answering with a SERVFAIL we also add an empty EDNS option code
+ ``65003``.
+
+
+Key parts
+~~~~~~~~~
+
+This example relies on the following functionalities:
+
+
+Registering inplace callback functions
+--------------------------------------
+
+There are four types of inplace callback functions:
+
+- `inplace callback reply functions`_
+- `inplace callback reply_cache functions`_
+- `inplace callback reply_local functions`_
+- `inplace callback reply_servfail functions`_
+
+
+Inplace callback reply functions
+................................
+
+Called when answering with a *resolved* query.
+
+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.
+ 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;
+ :param rep: reply_info struct;
+ :param rcode: return code for the query;
+ :param edns: edns_data to be sent to the client side. It SHOULD NOT be
+ altered;
+ :param opt_list_out: the list with the EDNS options that will be sent as a
+ 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.
+ :return: True on success, False on failure.
+ """
+
+.. note:: The function's name is irrelevant.
+
+We can register such function as:
+
+.. code-block:: python
+
+ if not register_inplace_cb_reply(inplace_reply_callback, env, id):
+ log_info("python: Could not register inplace callback function.")
+
+
+Inplace callback reply_cache functions
+......................................
+
+Called when answering *from cache*.
+
+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.
+ It will be called when answering from the cache.
+ :param qinfo: query_info struct;
+ :param qstate: module qstate. None;
+ :param rep: reply_info struct;
+ :param rcode: return code for the query;
+ :param edns: edns_data sent from the client side. The list with the EDNS
+ options is accesible through edns.opt_list. It SHOULD NOT be
+ altered;
+ :param opt_list_out: the list with the EDNS options that will be sent as a
+ 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.
+ :return: True on success, False on failure.
+ """
+
+.. note:: The function's name is irrelevant.
+
+We can register such function as:
+
+.. code-block:: python
+
+ if not register_inplace_cb_reply_cache(inplace_cache_callback, env, id):
+ log_info("python: Could not register inplace callback function.")
+
+
+Inplace callback reply_local functions
+......................................
+
+Called when answering with *local data* or a *Chaos(CH) reply*.
+
+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.
+ It will be called when answering from local data.
+ :param qinfo: query_info struct;
+ :param qstate: module qstate. None;
+ :param rep: reply_info struct;
+ :param rcode: return code for the query;
+ :param edns: edns_data sent from the client side. The list with the
+ EDNS options is accesible through edns.opt_list. It
+ SHOULD NOT be altered;
+ :param opt_list_out: the list with the EDNS options that will be sent as a
+ 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.
+ :return: True on success, False on failure.
+ """
+
+.. note:: The function's name is irrelevant.
+
+We can register such function as:
+
+.. code-block:: python
+
+ if not register_inplace_cb_reply_local(inplace_local_callback, env, id):
+ log_info("python: Could not register inplace callback function.")
+
+
+Inplace callback reply_servfail functions
+.........................................
+
+Called when answering with *SERVFAIL*.
+
+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.
+ 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;
+ :param rep: reply_info struct. None;
+ :param rcode: return code for the query. LDNS_RCODE_SERVFAIL;
+ :param edns: edns_data to be sent to the client side. If qstate is None
+ edns.opt_list contains the EDNS options sent from the client
+ side. It SHOULD NOT be altered;
+ :param opt_list_out: the list with the EDNS options that will be sent as a
+ 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.
+ :return: True on success, False on failure.
+ """
+
+.. note:: The function's name is irrelevant.
+
+We can register such function as:
+
+.. code-block:: python
+
+ if not register_inplace_cb_reply_servfail(inplace_servfail_callback, env, id):
+ log_info("python: Could not register inplace callback function.")
+
+
+Testing
+~~~~~~~
+
+Run the Unbound server: ::
+
+ root@localhost$ unbound -dv -c ./test-inplace_callbacks.conf
+
+In case you use your own configuration file, don't forget to enable the Python
+module::
+
+ module-config: "validator python iterator"
+
+and use a valid script path ::
+
+ python-script: "./examples/inplace_callbacks.py"
+
+On the first query for the nlnetlabs.nl A record we get no EDNS option back:
+
+::
+
+ root@localhost$ dig @localhost nlnetlabs.nl +ednsopt=65002
+
+ ; <<>> DiG 9.10.3-P4-Ubuntu <<>> @localhost nlnetlabs.nl +ednsopt=65002
+ ; (1 server found)
+ ;; global options: +cmd
+ ;; Got answer:
+ ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 48057
+ ;; flags: qr rd ra ad; QUERY: 1, ANSWER: 1, AUTHORITY: 4, ADDITIONAL: 3
+
+ ;; OPT PSEUDOSECTION:
+ ; EDNS: version: 0, flags:; udp: 4096
+ ;; QUESTION SECTION:
+ ;nlnetlabs.nl. IN A
+
+ ;; ANSWER SECTION:
+ nlnetlabs.nl. 10200 IN A 185.49.140.10
+
+ ;; AUTHORITY SECTION:
+ nlnetlabs.nl. 10200 IN NS ns.nlnetlabs.nl.
+ nlnetlabs.nl. 10200 IN NS sec2.authdns.ripe.net.
+ nlnetlabs.nl. 10200 IN NS anyns.pch.net.
+ nlnetlabs.nl. 10200 IN NS ns-ext1.sidn.nl.
+
+ ;; ADDITIONAL SECTION:
+ ns.nlnetlabs.nl. 10200 IN A 185.49.140.60
+ ns.nlnetlabs.nl. 10200 IN AAAA 2a04:b900::8:0:0:60
+
+ ;; Query time: 813 msec
+ ;; SERVER: 127.0.0.1#53(127.0.0.1)
+ ;; WHEN: Mon Dec 05 16:15:32 CET 2016
+ ;; MSG SIZE rcvd: 204
+
+When we issue the same query again we get a cached response and the expected
+``65002: 0xdeadbeef`` EDNS option:
+
+::
+
+ root@localhost$ dig @localhost nlnetlabs.nl +ednsopt=65002
+
+ ; <<>> DiG 9.10.3-P4-Ubuntu <<>> @localhost nlnetlabs.nl +ednsopt=65002
+ ; (1 server found)
+ ;; global options: +cmd
+ ;; Got answer:
+ ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 26489
+ ;; flags: qr rd ra ad; QUERY: 1, ANSWER: 1, AUTHORITY: 4, ADDITIONAL: 3
+
+ ;; OPT PSEUDOSECTION:
+ ; EDNS: version: 0, flags:; udp: 4096
+ ; OPT=65002: de ad be ef ("....")
+ ;; QUESTION SECTION:
+ ;nlnetlabs.nl. IN A
+
+ ;; ANSWER SECTION:
+ nlnetlabs.nl. 10197 IN A 185.49.140.10
+
+ ;; AUTHORITY SECTION:
+ nlnetlabs.nl. 10197 IN NS ns.nlnetlabs.nl.
+ nlnetlabs.nl. 10197 IN NS sec2.authdns.ripe.net.
+ nlnetlabs.nl. 10197 IN NS anyns.pch.net.
+ nlnetlabs.nl. 10197 IN NS ns-ext1.sidn.nl.
+
+ ;; ADDITIONAL SECTION:
+ ns.nlnetlabs.nl. 10197 IN AAAA 2a04:b900::8:0:0:60
+ ns.nlnetlabs.nl. 10197 IN A 185.49.140.60
+
+ ;; Query time: 0 msec
+ ;; SERVER: 127.0.0.1#53(127.0.0.1)
+ ;; WHEN: Mon Dec 05 16:50:04 CET 2016
+ ;; MSG SIZE rcvd: 212
+
+By issuing a query for a bogus domain unbound replies with SERVFAIL and an
+empty EDNS option code ``65003``. *For this example to work unbound needs to be
+validating*:
+
+::
+
+ root@localhost$ dig @localhost bogus.nlnetlabs.nl txt
+
+ ; <<>> DiG 9.10.3-P4-Ubuntu <<>> @localhost bogus.nlnetlabs.nl txt
+ ; (1 server found)
+ ;; global options: +cmd
+ ;; Got answer:
+ ;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 19865
+ ;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1
+
+ ;; OPT PSEUDOSECTION:
+ ; EDNS: version: 0, flags:; udp: 4096
+ ; OPT=65003
+ ;; QUESTION SECTION:
+ ;bogus.nlnetlabs.nl. IN TXT
+
+ ;; Query time: 11 msec
+ ;; SERVER: 127.0.0.1#53(127.0.0.1)
+ ;; WHEN: Mon Dec 05 17:06:01 CET 2016
+ ;; MSG SIZE rcvd: 51
+
+
+Complete source code
+~~~~~~~~~~~~~~~~~~~~
+.. literalinclude:: ../../examples/inplace_callbacks.py
+ :language: python
diff --git a/external/unbound/pythonmod/doc/examples/index.rst b/external/unbound/pythonmod/doc/examples/index.rst
index 6c5022581..93d9b8e1e 100644
--- a/external/unbound/pythonmod/doc/examples/index.rst
+++ b/external/unbound/pythonmod/doc/examples/index.rst
@@ -1,15 +1,16 @@
.. _Tutorials:
-==============================
-Tutorials
-==============================
+Examples
+========
-Here you can find several tutorials which clarify the usage and capabilities of Unbound scriptable interface.
+Here you can find several tutorials which clarify the usage and capabilities of
+the Unbound scriptable interface.
-`Tutorials`
+Tutorials
+---------
.. toctree::
- :maxdepth: 2
- :glob:
+ :maxdepth: 2
+ :glob:
- example*
+ example*
diff --git a/external/unbound/pythonmod/doc/install.rst b/external/unbound/pythonmod/doc/install.rst
index 991e2b4be..b8d0b9fa6 100644
--- a/external/unbound/pythonmod/doc/install.rst
+++ b/external/unbound/pythonmod/doc/install.rst
@@ -1,39 +1,44 @@
Installation
-===================================
+============
-**Prerequisites**
+Prerequisites
+-------------
Python 2.4 or higher, SWIG 1.3 or higher, GNU make
-**Download**
+Download
+--------
You can download the source codes `here`_.
The latest release is 1.1.1, Jan 15, 2009.
.. _here: unbound-1.1.1-py.tar.gz
-**Compiling**
+Compiling
+---------
After downloading, you can compile the Unbound library by doing::
- > tar -xzf unbound-1.1.1-py.tar.gz
- > cd unbound-1.1.1
- > ./configure --with-pythonmodule
- > make
+ > tar -xzf unbound-1.1.1-py.tar.gz
+ > cd unbound-1.1.1
+ > ./configure --with-pythonmodule
+ > make
You need GNU make to compile sources.
SWIG and Python devel libraries to compile extension module.
-**Testing**
+Testing
+-------
If the compilation is successful, you can test the extension module by::
- > cd pythonmod
- > make sudo # or "make test" or "make suexec"
+ > cd pythonmod
+ > make sudo # or "make test" or "make suexec"
-This will start unbound server with language dictionary service (see :ref:`Tutorials`).
+This will start unbound server with language dictionary service
+(see :ref:`Tutorials`).
In order to test this service, type::
-
+
> dig TXT @127.0.0.1 aught.en._dict_.cz
Dig should print this message (czech equivalent of aught)::
@@ -44,16 +49,17 @@ Dig should print this message (czech equivalent of aught)::
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 30085
;; flags: aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
-
+
;; QUESTION SECTION:
- ;aught.en._dict_.cz. IN TXT
-
+ ;aught.en._dict_.cz. IN TXT
+
;; ANSWER SECTION:
- aught.en._dict_.cz. 300 IN TXT "nic"
-
+ aught.en._dict_.cz. 300 IN TXT "nic"
+
;; Query time: 11 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Thu Jan 10 16:45:58 2009
;; MSG SIZE rcvd: 52
-The ``pythonmod/examples`` directory contains simple applications written in Python.
+The ``pythonmod/examples`` directory contains simple applications written in
+Python.
diff --git a/external/unbound/pythonmod/doc/modules/functions.rst b/external/unbound/pythonmod/doc/modules/functions.rst
index 45a469fec..627d44922 100644
--- a/external/unbound/pythonmod/doc/modules/functions.rst
+++ b/external/unbound/pythonmod/doc/modules/functions.rst
@@ -7,25 +7,26 @@ Network
.. function:: ntohs(netshort)
This subroutine converts values between the host and network byte order.
- Specifically, **ntohs()** converts 16-bit quantities from network byte order to host byte order.
-
+ Specifically, **ntohs()** converts 16-bit quantities from network byte order
+ to host byte order.
+
:param netshort: 16-bit short addr
:rtype: converted addr
-
-
+
+
Cache
-----
.. function:: storeQueryInCache(qstate, qinfo, msgrep, is_referral)
Store pending query in local cache.
-
+
:param qstate: :class:`module_qstate`
:param qinfo: :class:`query_info`
:param msgrep: :class:`reply_info`
:param is_referal: integer
:rtype: boolean
-
+
.. function:: invalidateQueryInCache(qstate, qinfo)
Invalidate record in local cache.
@@ -34,6 +35,111 @@ Cache
:param qinfo: :class:`query_info`
+EDNS options
+------------
+
+.. function:: register_edns_option(env, code, bypass_cache_stage=False, no_aggregation=False)
+
+ Register EDNS option code.
+
+ :param env: :class:`module_env`
+ :param code: option code(integer)
+ :param bypass_cache_stage: whether to bypass the cache response stage
+ :param no_aggregation: whether this query should be unique
+ :return: ``1`` if successful, ``0`` otherwise
+ :rtype: integer
+
+.. function:: edns_opt_list_find(list, code)
+
+ Find the EDNS option code in the EDNS option list.
+
+ :param list: linked list of :class:`edns_option`
+ :param code: option code (integer)
+ :return: the edns option if found or None
+ :rtype: :class:`edns_option` or None
+
+.. function:: edns_opt_list_remove(list, code);
+
+ Remove an ENDS option code from the list.
+ .. note:: All :class:`edns_option` with the code will be removed
+
+ :param list: linked list of :class:`edns_option`
+ :param code: option code (integer)
+ :return: ``1`` if at least one :class:`edns_option` was removed, ``0`` otherwise
+ :rtype: integer
+
+.. function:: edns_opt_list_append(list, code, data, region)
+
+ Append given EDNS option code with data to the list.
+
+ :param list: linked list of :class:`edns_option`
+ :param code: option code (integer)
+ :param data: EDNS data. **Must** be a :class:`bytearray`
+ :param region: :class:`regional`
+
+.. function:: edns_opt_list_is_empty(list)
+
+ Check if an EDNS option list is empty.
+
+ :param list: linked list of :class:`edns_option`
+ :return: ``1`` if list is empty, ``0`` otherwise
+ :rtype: integer
+
+
+Inplace callbacks
+-----------------
+
+.. function:: inplace_cb_reply(qinfo, qstate, rep, rcode, edns, opt_list_out, region)
+
+ Function prototype for callback functions used in
+ `register_inplace_cb_reply`_, `register_inplace_cb_reply_cache`_,
+ `register_inplace_cb_reply_local` and `register_inplace_cb_reply_servfail`.
+
+ :param qinfo: :class:`query_info`
+ :param qstate: :class:`module_qstate`
+ :param rep: :class:`reply_info`
+ :param rcode: return code (integer), check ``RCODE_`` constants.
+ :param edns: :class:`edns_data`
+ :param opt_list_out: :class:`edns_option`. EDNS option list to append options to.
+ :param region: :class:`regional`
+
+.. function:: register_inplace_cb_reply(py_cb, env)
+
+ Register py_cb as an inplace reply callback function.
+
+ :param py_cb: Python function that follows `inplace_cb_reply`_'s prototype. **Must** be callable.
+ :param env: :class:`module_env`
+ :return: True on success, False otherwise
+ :rtype: boolean
+
+.. function:: register_inplace_cb_reply_cache(py_cb, env)
+
+ Register py_cb as an inplace reply_cache callback function.
+
+ :param py_cb: Python function that follows `inplace_cb_reply`_'s prototype. **Must** be callable.
+ :param env: :class:`module_env`
+ :return: True on success, False otherwise
+ :rtype: boolean
+
+.. function:: register_inplace_cb_reply_local(py_cb, env)
+
+ Register py_cb as an inplace reply_local callback function.
+
+ :param py_cb: Python function that follows `inplace_cb_reply`_'s prototype. **Must** be callable.
+ :param env: :class:`module_env`
+ :return: True on success, False otherwise
+ :rtype: boolean
+
+.. function:: register_inplace_cb_reply_servfail(py_cb, env)
+
+ Register py_cb as an inplace reply_servfail callback function.
+
+ :param py_cb: Python function that follows `inplace_cb_reply`_'s prototype. **Must** be callable.
+ :param env: :class:`module_env`
+ :return: True on success, False otherwise
+ :rtype: boolean
+
+
Logging
-------
@@ -71,50 +177,51 @@ Logging
:param msg: string desc to accompany the hexdump.
:param data: data to dump in hex format.
:param length: length of data.
-
+
.. function:: log_dns_msg(str, qinfo, reply)
Log DNS message.
-
+
:param str: string message
:param qinfo: :class:`query_info`
:param reply: :class:`reply_info`
-
+
.. function:: log_query_info(verbosity_value, str, qinf)
Log query information.
-
+
:param verbosity_value: see constants
:param str: string message
:param qinf: :class:`query_info`
-
+
.. function:: regional_log_stats(r)
Log regional statistics.
-
+
:param r: :class:`regional`
+
Debugging
---------
.. function:: strextstate(module_ext_state)
Debug utility, module external qstate to string.
-
+
:param module_ext_state: the state value.
:rtype: descriptive string.
.. function:: strmodulevent(module_event)
Debug utility, module event to string.
-
+
:param module_event: the module event value.
:rtype: descriptive string.
-
+
.. function:: ldns_rr_type2str(atype)
Convert RR type to string.
-
+
.. function:: ldns_rr_class2str(aclass)
Convert RR class to string.
diff --git a/external/unbound/pythonmod/doc/modules/struct.rst b/external/unbound/pythonmod/doc/modules/struct.rst
index 669f36d91..3af5d8a48 100644
--- a/external/unbound/pythonmod/doc/modules/struct.rst
+++ b/external/unbound/pythonmod/doc/modules/struct.rst
@@ -6,55 +6,94 @@ module_qstate
.. class:: module_qstate
- Module state, per query.
-
- This class provides these data attributes:
-
- .. attribute:: qinfo
-
- (:class:`query_info`) Informations about query being answered. Name, RR type, RR class.
-
- .. attribute:: query_flags
-
- (uint16) Flags for query. See QF_BIT\_ predefined constants.
-
- .. attribute:: is_priming
-
- If this is a (stub or root) priming query (with hints).
-
- .. attribute:: reply
-
- comm_reply contains server replies.
-
- .. attribute:: return_msg
-
- (:class:`dns_msg`) The reply message, with message for client and calling module (read-only attribute).
- Note that if you want to create of modify return_msg you should use :class:`DNSMessage`.
-
- .. attribute:: return_rcode
-
- The rcode, in case of error, instead of a reply message. Determines whether the return_msg contains reply.
-
- .. attribute:: region
-
- Region for this query. Cleared when query process finishes.
-
- .. attribute:: curmod
-
- Which module is executing.
-
- .. attribute:: ext_state[]
-
- Module states.
-
- .. attribute:: env
-
- Environment for this query.
-
- .. attribute:: mesh_info
-
- Mesh related information for this query.
+ Module state, per query.
+
+ This class provides these data attributes:
+
+ .. attribute:: qinfo
+
+ (:class:`query_info`) Informations about query being answered. Name, RR type, RR class.
+
+ .. attribute:: query_flags
+
+ (uint16) Flags for query. See QF_BIT\_ predefined constants.
+
+ .. attribute:: is_priming
+
+ If this is a (stub or root) priming query (with hints).
+
+ .. attribute:: reply
+
+ comm_reply contains server replies.
+
+ .. attribute:: return_msg
+
+ (:class:`dns_msg`) The reply message, with message for client and calling module (read-only attribute).
+ Note that if you want to create of modify return_msg you should use :class:`DNSMessage`.
+
+ .. attribute:: return_rcode
+
+ The rcode, in case of error, instead of a reply message. Determines whether the return_msg contains reply.
+
+ .. attribute:: region
+
+ Region for this query. Cleared when query process finishes.
+
+ .. attribute:: curmod
+
+ Which module is executing.
+
+ .. attribute:: ext_state[]
+
+ Module states.
+
+ .. attribute:: env
+
+ Environment for this query.
+ .. attribute:: mesh_info
+
+ Mesh related information for this query.
+
+ .. attribute:: edns_opts_front_in
+
+ Incoming EDNS options from the front end.
+
+ .. attribute:: edns_opts_front_in_iter
+
+ Iterator for `edns_opts_front_in`.
+
+ .. attribute:: edns_opts_back_out
+
+ Outgoing EDNS options to the back end.
+
+ .. attribute:: edns_opts_back_out_iter
+
+ Iterator for `edns_opts_back_out`.
+
+ .. attribute:: edns_opts_back_in
+
+ Incoming EDNS options from the back end.
+
+ .. attribute:: edns_opts_back_in_iter
+
+ Iterator for `ends_opts_back_in`.
+
+ .. attribute:: edns_opts_front_out
+
+ Outgoing EDNS options to the front end.
+
+ .. attribute:: edns_opts_front_out_iter
+
+ Iterator for `edns_opts_front_out`.
+
+ .. attribute:: no_cache_lookup
+
+ Flag to indicate whether modules should answer from the cache.
+
+ .. attribute:: no_cache_store
+
+ Flag to indicate whether modules should store answer in the cache.
query_info
----------------
@@ -94,7 +133,57 @@ query_info
.. attribute:: qclass_str
The ``qclass`` in display presentation format (string).
-
+
+edns_data
+---------
+
+.. class:: edns_data
+
+ This class represents the EDNS information parsed/encoded from/to a packet. It provides these data attributes:
+
+ .. attribute:: edns_present
+
+ If EDNS OPT record is present.
+
+ .. attribute:: ext_rcode
+
+ Extended RCODE.
+
+ .. attribute:: edns_version
+
+ The EDNS version number.
+
+ .. attribute:: bits
+
+ The EDNS bits field from ttl (host order): Z.
+
+ .. attribute:: udp_size
+
+ UDP reassembly size.
+
+ .. attribute:: opt_list
+
+ The EDNS option list.
+
+ .. attribute:: opt_list_iter
+
+ Iterator for `opt_list`.
+
+edns_option
+-----------
+
+.. class:: edns_option
+
+ This class represents an EDNS option (code, data) found in EDNS option lists. It provides these data attributes:
+
+ .. attribute:: code
+
+ The EDNS option code.
+
+ .. attribute:: data
+
+ The EDNS option data.
+
reply_info
--------------------
diff --git a/external/unbound/pythonmod/interface.i b/external/unbound/pythonmod/interface.i
index 4b20c6ec1..09d726f2c 100644
--- a/external/unbound/pythonmod/interface.i
+++ b/external/unbound/pythonmod/interface.i
@@ -1,11 +1,10 @@
/*
* interface.i: unbound python module
*/
-
%module unboundmodule
%{
/**
- * \file
+ * \file
* This is the interface between the unbound server and a python module
* called to perform operations on queries.
*/
@@ -34,10 +33,10 @@
#include "sldns/pkthdr.h"
%}
-%include "stdint.i" // uint_16_t can be known type now
+%include "stdint.i" /* uint_16_t can be known type now */
%inline %{
- //converts [len][data][len][data][0] string to a List of labels (PyBytes)
+ /* converts [len][data][len][data][0] string to a List of labels (PyBytes) */
PyObject* GetNameAsLabelList(const char* name, int len) {
PyObject* list;
int cnt=0, i;
@@ -59,7 +58,7 @@
}
%}
-/* ************************************************************************************ *
+/* ************************************************************************************ *
Structure query_info
* ************************************************************************************ */
/* Query info */
@@ -77,24 +76,24 @@ struct query_info {
};
%inline %{
- enum enum_rr_class {
+ enum enum_rr_class {
RR_CLASS_IN = 1,
RR_CLASS_CH = 3,
RR_CLASS_HS = 4,
RR_CLASS_NONE = 254,
RR_CLASS_ANY = 255,
};
-
+
enum enum_rr_type {
- RR_TYPE_A = 1,
- RR_TYPE_NS = 2,
- RR_TYPE_MD = 3,
- RR_TYPE_MF = 4,
- RR_TYPE_CNAME = 5,
- RR_TYPE_SOA = 6,
- RR_TYPE_MB = 7,
- RR_TYPE_MG = 8,
- RR_TYPE_MR = 9,
+ RR_TYPE_A = 1,
+ RR_TYPE_NS = 2,
+ RR_TYPE_MD = 3,
+ RR_TYPE_MF = 4,
+ RR_TYPE_CNAME = 5,
+ RR_TYPE_SOA = 6,
+ RR_TYPE_MB = 7,
+ RR_TYPE_MG = 8,
+ RR_TYPE_MR = 9,
RR_TYPE_NULL = 10,
RR_TYPE_WKS = 11,
RR_TYPE_PTR = 12,
@@ -132,7 +131,7 @@ struct query_info {
RR_TYPE_SSHFP = 44,
RR_TYPE_IPSECKEY = 45,
RR_TYPE_RRSIG = 46,
- RR_TYPE_NSEC = 47,
+ RR_TYPE_NSEC = 47,
RR_TYPE_DNSKEY = 48,
RR_TYPE_DHCID = 49,
RR_TYPE_NSEC3 = 50,
@@ -152,7 +151,7 @@ struct query_info {
PyObject* _get_qname(struct query_info* q) {
return PyBytes_FromStringAndSize((char*)q->qname, q->qname_len);
- }
+ }
PyObject* _get_qname_components(struct query_info* q) {
return GetNameAsLabelList((const char*)q->qname, q->qname_len);
@@ -180,7 +179,7 @@ struct query_info {
__swig_getmethods__["qname"] = _unboundmodule._get_qname
if _newclass:qname = _swig_property(_unboundmodule._get_qname)
-
+
__swig_getmethods__["qname_list"] = _unboundmodule._get_qname_components
if _newclass:qname_list = _swig_property(_unboundmodule._get_qname_components)
@@ -190,7 +189,7 @@ struct query_info {
%}
}
-/* ************************************************************************************ *
+/* ************************************************************************************ *
Structure packed_rrset_key
* ************************************************************************************ */
%ignore packed_rrset_key::dname;
@@ -201,20 +200,23 @@ struct packed_rrset_key {
%immutable;
char* dname;
size_t dname_len;
- uint32_t flags;
- uint16_t type; //rrset type in network format
- uint16_t rrset_class; //rrset class in network format
+ uint32_t flags;
+ uint16_t type; /* rrset type in network format */
+ uint16_t rrset_class; /* rrset class in network format */
%mutable;
};
-//This subroutine converts values between the host and network byte order.
-//Specifically, ntohs() converts 16-bit quantities from network byte order to host byte order.
+/**
+ * This subroutine converts values between the host and network byte order.
+ * Specifically, ntohs() converts 16-bit quantities from network byte order to
+ * host byte order.
+ */
uint16_t ntohs(uint16_t netshort);
%inline %{
PyObject* _get_dname(struct packed_rrset_key* k) {
return PyBytes_FromStringAndSize((char*)k->dname, k->dname_len);
- }
+ }
PyObject* _get_dname_components(struct packed_rrset_key* k) {
return GetNameAsLabelList((char*)k->dname, k->dname_len);
}
@@ -242,24 +244,24 @@ uint16_t ntohs(uint16_t netshort);
%}
}
-#if defined(SWIGWORDSIZE64)
-typedef long int rrset_id_t;
-#else
-typedef long long int rrset_id_t;
-#endif
+#if defined(SWIGWORDSIZE64)
+typedef long int rrset_id_type;
+#else
+typedef long long int rrset_id_type;
+#endif
struct ub_packed_rrset_key {
struct lruhash_entry entry;
- rrset_id_t id;
+ rrset_id_type id;
struct packed_rrset_key rk;
};
struct lruhash_entry {
- lock_rw_t lock;
+ lock_rw_type lock;
struct lruhash_entry* overflow_next;
struct lruhash_entry* lru_next;
struct lruhash_entry* lru_prev;
- hashvalue_t hash;
+ hashvalue_type hash;
void* key;
struct packed_rrset_data* data;
};
@@ -269,17 +271,24 @@ struct lruhash_entry {
%ignore packed_rrset_data::rr_data;
struct packed_rrset_data {
- uint32_t ttl; //TTL (in seconds like time())
+ /* TTL (in seconds like time()) */
+ uint32_t ttl;
- size_t count; //number of rrs
- size_t rrsig_count; //number of rrsigs
+ /* number of rrs */
+ size_t count;
+ /* number of rrsigs */
+ size_t rrsig_count;
- enum rrset_trust trust;
+ enum rrset_trust trust;
enum sec_status security;
- size_t* rr_len; //length of every rr's rdata
- uint32_t *rr_ttl; //ttl of every rr
- uint8_t** rr_data; //array of pointers to every rr's rdata; The rr_data[i] rdata is stored in uncompressed wireformat.
+ /* length of every rr's rdata */
+ size_t* rr_len;
+ /* ttl of every rr */
+ uint32_t *rr_ttl;
+ /* array of pointers to every rr's rdata. The rr_data[i] rdata is stored in
+ * uncompressed wireformat. */
+ uint8_t** rr_data;
};
%pythoncode %{
@@ -300,26 +309,26 @@ struct packed_rrset_data {
%inline %{
PyObject* _get_data_rr_len(struct packed_rrset_data* d, int idx) {
- if ((d != NULL) && (idx >= 0) &&
- ((size_t)idx < (d->count+d->rrsig_count)))
+ if ((d != NULL) && (idx >= 0) &&
+ ((size_t)idx < (d->count+d->rrsig_count)))
return PyInt_FromLong(d->rr_len[idx]);
return Py_None;
}
void _set_data_rr_ttl(struct packed_rrset_data* d, int idx, uint32_t ttl)
{
- if ((d != NULL) && (idx >= 0) &&
- ((size_t)idx < (d->count+d->rrsig_count)))
+ if ((d != NULL) && (idx >= 0) &&
+ ((size_t)idx < (d->count+d->rrsig_count)))
d->rr_ttl[idx] = ttl;
}
PyObject* _get_data_rr_ttl(struct packed_rrset_data* d, int idx) {
- if ((d != NULL) && (idx >= 0) &&
- ((size_t)idx < (d->count+d->rrsig_count)))
+ if ((d != NULL) && (idx >= 0) &&
+ ((size_t)idx < (d->count+d->rrsig_count)))
return PyInt_FromLong(d->rr_ttl[idx]);
return Py_None;
}
PyObject* _get_data_rr_data(struct packed_rrset_data* d, int idx) {
- if ((d != NULL) && (idx >= 0) &&
- ((size_t)idx < (d->count+d->rrsig_count)))
+ if ((d != NULL) && (idx >= 0) &&
+ ((size_t)idx < (d->count+d->rrsig_count)))
return PyBytes_FromStringAndSize((char*)d->rr_data[idx],
d->rr_len[idx]);
return Py_None;
@@ -340,7 +349,7 @@ struct packed_rrset_data {
%}
}
-/* ************************************************************************************ *
+/* ************************************************************************************ *
Structure reply_info
* ************************************************************************************ */
/* Messages */
@@ -359,15 +368,15 @@ struct reply_info {
size_t an_numrrsets;
size_t ns_numrrsets;
size_t ar_numrrsets;
- size_t rrset_count; // an_numrrsets + ns_numrrsets + ar_numrrsets
+ size_t rrset_count; /* an_numrrsets + ns_numrrsets + ar_numrrsets */
struct ub_packed_rrset_key** rrsets;
- struct rrset_ref ref[1]; //?
+ struct rrset_ref ref[1]; /* ? */
};
struct rrset_ref {
struct ub_packed_rrset_key* key;
- rrset_id_t id;
+ rrset_id_type id;
};
struct dns_msg {
@@ -396,11 +405,11 @@ struct dns_msg {
struct rrset_ref* _rrset_ref_get(struct reply_info* r, int idx) {
if ((r != NULL) && (idx >= 0) && ((size_t)idx < r->rrset_count)) {
-//printf("_rrset_ref_get: %lX key:%lX\n", r->ref + idx, r->ref[idx].key);
+/* printf("_rrset_ref_get: %lX key:%lX\n", r->ref + idx, r->ref[idx].key); */
return &(r->ref[idx]);
-// return &(r->ref[idx]);
+/* return &(r->ref[idx]); */
}
-//printf("_rrset_ref_get: NULL\n");
+/* printf("_rrset_ref_get: NULL\n"); */
return NULL;
}
%}
@@ -417,7 +426,7 @@ struct dns_msg {
%}
}
-/* ************************************************************************************ *
+/* ************************************************************************************ *
Structure mesh_state
* ************************************************************************************ */
struct mesh_state {
@@ -430,7 +439,7 @@ struct mesh_reply {
};
struct comm_reply {
-
+
};
%inline %{
@@ -479,30 +488,165 @@ struct comm_reply {
if _newclass:family = _swig_property(_family_get)
%}
}
-/* ************************************************************************************ *
+
+/* ************************************************************************************ *
+ Structure edns_option
+ * ************************************************************************************ */
+/* Rename the members to follow the python convention of marking them as
+ * private. Access to the opt_code and opt_data members is given by the later
+ * python defined code and data members respectively. */
+%rename(_next) edns_option::next;
+%rename(_opt_code) edns_option::opt_code;
+%rename(_opt_len) edns_option::opt_len;
+%rename(_opt_data) edns_option::opt_data;
+struct edns_option {
+ struct edns_option* next;
+ uint16_t opt_code;
+ size_t opt_len;
+ uint8_t* opt_data;
+};
+
+%inline %{
+ PyObject* _edns_option_opt_code_get(struct edns_option* option) {
+ uint16_t opt_code = option->opt_code;
+ return PyInt_FromLong(opt_code);
+ }
+
+ PyObject* _edns_option_opt_data_get(struct edns_option* option) {
+ return PyByteArray_FromStringAndSize((void*)option->opt_data,
+ option->opt_len);
+ }
+%}
+%extend edns_option {
+ %pythoncode %{
+ def _opt_code_get(self): return _edns_option_opt_code_get(self)
+ __swig_getmethods__["code"] = _opt_code_get
+ if _newclass: opt_code = _swig_property(_opt_code_get)
+
+ def _opt_data_get(self): return _edns_option_opt_data_get(self)
+ __swig_getmethods__["data"] = _opt_data_get
+ if _newclass: opt_data = _swig_property(_opt_data_get)
+ %}
+}
+
+/* ************************************************************************************ *
+ Structure edns_data
+ * ************************************************************************************ */
+/* This is ignored because we will pass a double pointer of this to Python
+ * with custom getmethods. This is done to bypass Swig's behavior to pass NULL
+ * pointers as None. */
+%ignore edns_data::opt_list;
+struct edns_data {
+ int edns_present;
+ uint8_t ext_rcode;
+ uint8_t edns_version;
+ uint16_t bits;
+ uint16_t udp_size;
+ struct edns_option* opt_list;
+};
+%inline %{
+ struct edns_option** _edns_data_opt_list_get(struct edns_data* edns) {
+ return &edns->opt_list;
+ }
+%}
+%extend edns_data {
+ %pythoncode %{
+ def _opt_list_iter(self): return EdnsOptsListIter(self.opt_list)
+ __swig_getmethods__["opt_list_iter"] = _opt_list_iter
+ if _newclass:opt_list_iter = _swig_property(_opt_list_iter)
+ def _opt_list(self): return _edns_data_opt_list_get(self)
+ __swig_getmethods__["opt_list"] = _opt_list
+ if _newclass:opt_list = _swig_property(_opt_list)
+ %}
+}
+
+/* ************************************************************************************ *
+ Structure module_env
+ * ************************************************************************************ */
+struct module_env {
+ struct config_file* cfg;
+ struct slabhash* msg_cache;
+ struct rrset_cache* rrset_cache;
+ struct infra_cache* infra_cache;
+ struct key_cache* key_cache;
+
+ /* --- services --- */
+ struct outbound_entry* (*send_query)(struct query_info* qinfo,
+ uint16_t flags, int dnssec, int want_dnssec, int nocaps,
+ struct sockaddr_storage* addr, socklen_t addrlen,
+ uint8_t* zone, size_t zonelen, int ssl_upstream,
+ struct module_qstate* q);
+ void (*detach_subs)(struct module_qstate* qstate);
+ int (*attach_sub)(struct module_qstate* qstate,
+ struct query_info* qinfo, uint16_t qflags, int prime,
+ int valrec, struct module_qstate** newq);
+ void (*kill_sub)(struct module_qstate* newq);
+ int (*detect_cycle)(struct module_qstate* qstate,
+ struct query_info* qinfo, uint16_t flags, int prime,
+ int valrec);
+
+ struct regional* scratch;
+ struct sldns_buffer* scratch_buffer;
+ struct worker* worker;
+ struct mesh_area* mesh;
+ struct alloc_cache* alloc;
+ struct ub_randstate* rnd;
+ time_t* now;
+ struct timeval* now_tv;
+ int need_to_validate;
+ struct val_anchors* anchors;
+ struct val_neg_cache* neg_cache;
+ struct comm_timer* probe_timer;
+ struct iter_forwards* fwds;
+ struct iter_hints* hints;
+ void* modinfo[MAX_MODULE];
+
+ void* inplace_cb_lists[inplace_cb_types_total];
+ struct edns_known_option* edns_known_options;
+ size_t edns_known_options_num;
+};
+
+/* ************************************************************************************ *
Structure module_qstate
* ************************************************************************************ */
%ignore module_qstate::ext_state;
%ignore module_qstate::minfo;
+/* These are ignored because we will pass a double pointer of them to Python
+ * with custom getmethods. This is done to bypass Swig's behavior to pass NULL
+ * pointers as None. */
+%ignore module_qstate::edns_opts_front_in;
+%ignore module_qstate::edns_opts_back_out;
+%ignore module_qstate::edns_opts_back_in;
+%ignore module_qstate::edns_opts_front_out;
+
/* Query state */
struct module_qstate {
struct query_info qinfo;
- uint16_t query_flags; //See QF_BIT_xx constants
- int is_priming;
+ uint16_t query_flags; /* See QF_BIT_xx constants */
+ int is_priming;
+ int is_valrec;
struct comm_reply* reply;
struct dns_msg* return_msg;
- int return_rcode;
+ int return_rcode;
struct regional* region; /* unwrapped */
- int curmod;
+ int curmod;
- enum module_ext_state ext_state[MAX_MODULE];
- void* minfo[MAX_MODULE];
+ enum module_ext_state ext_state[MAX_MODULE];
+ void* minfo[MAX_MODULE];
+ time_t prefetch_leeway;
struct module_env* env; /* unwrapped */
struct mesh_state* mesh_info;
+
+ struct edns_option* edns_opts_front_in;
+ struct edns_option* edns_opts_back_out;
+ struct edns_option* edns_opts_back_in;
+ struct edns_option* edns_opts_front_out;
+ int no_cache_lookup;
+ int no_cache_store;
};
%constant int MODULE_COUNT = MAX_MODULE;
@@ -540,20 +684,69 @@ struct module_qstate {
def __getitem__(self, index): return _unboundmodule._ext_state_get(self.obj, index)
def __setitem__(self, index, value): _unboundmodule._ext_state_set(self.obj, index, value)
def __len__(self): return _unboundmodule.MODULE_COUNT
+
+ class EdnsOptsListIter:
+ def __init__(self, obj):
+ self._current = obj
+ self._temp = None
+ def __iter__(self): return self
+ def __next__(self):
+ """Python 3 compatibility"""
+ return self._get_next()
+ def next(self):
+ """Python 2 compatibility"""
+ return self._get_next()
+ def _get_next(self):
+ if not edns_opt_list_is_empty(self._current):
+ self._temp = self._current
+ self._current = _p_p_edns_option_get_next(self._current)
+ return _dereference_edns_option(self._temp)
+ else:
+ raise StopIteration
%}
%inline %{
enum module_ext_state _ext_state_get(struct module_qstate* q, int idx) {
if ((q != NULL) && (idx >= 0) && (idx < MAX_MODULE)) {
return q->ext_state[idx];
- }
+ }
return 0;
}
-
+
void _ext_state_set(struct module_qstate* q, int idx, enum module_ext_state state) {
if ((q != NULL) && (idx >= 0) && (idx < MAX_MODULE)) {
q->ext_state[idx] = state;
- }
+ }
+ }
+
+ int edns_opt_list_is_empty(struct edns_option** opt) {
+ if (!opt || !(*opt)) return 1;
+ return 0;
+ }
+
+ struct edns_option* _dereference_edns_option(struct edns_option** opt) {
+ if (!opt) return NULL;
+ return *opt;
+ }
+
+ struct edns_option** _p_p_edns_option_get_next(struct edns_option** opt) {
+ return &(*opt)->next;
+ }
+
+ struct edns_option** _edns_opts_front_in_get(struct module_qstate* q) {
+ return &q->edns_opts_front_in;
+ }
+
+ struct edns_option** _edns_opts_back_out_get(struct module_qstate* q) {
+ return &q->edns_opts_back_out;
+ }
+
+ struct edns_option** _edns_opts_back_in_get(struct module_qstate* q) {
+ return &q->edns_opts_back_in;
+ }
+
+ struct edns_option** _edns_opts_front_out_get(struct module_qstate* q) {
+ return &q->edns_opts_front_out;
}
%}
@@ -566,10 +759,36 @@ struct module_qstate {
def __ext_state_get(self): return ExtState(self)
__swig_getmethods__["ext_state"] = __ext_state_get
if _newclass:ext_state = _swig_property(__ext_state_get)#, __ext_state_set)
+
+ def _edns_opts_front_in_iter(self): return EdnsOptsListIter(self.edns_opts_front_in)
+ __swig_getmethods__["edns_opts_front_in_iter"] = _edns_opts_front_in_iter
+ if _newclass:edns_opts_front_in_iter = _swig_property(_edns_opts_front_in_iter)
+ def _edns_opts_back_out_iter(self): return EdnsOptsListIter(self.edns_opts_back_out)
+ __swig_getmethods__["edns_opts_back_out_iter"] = _edns_opts_back_out_iter
+ if _newclass:edns_opts_back_out_iter = _swig_property(_edns_opts_back_out_iter)
+ def _edns_opts_back_in_iter(self): return EdnsOptsListIter(self.edns_opts_back_in)
+ __swig_getmethods__["edns_opts_back_in_iter"] = _edns_opts_back_in_iter
+ if _newclass:edns_opts_back_in_iter = _swig_property(_edns_opts_back_in_iter)
+ def _edns_opts_front_out_iter(self): return EdnsOptsListIter(self.edns_opts_front_out)
+ __swig_getmethods__["edns_opts_front_out_iter"] = _edns_opts_front_out_iter
+ if _newclass:edns_opts_front_out_iter = _swig_property(_edns_opts_front_out_iter)
+
+ def _edns_opts_front_in(self): return _edns_opts_front_in_get(self)
+ __swig_getmethods__["edns_opts_front_in"] = _edns_opts_front_in
+ if _newclass:edns_opts_front_in = _swig_property(_edns_opts_front_in)
+ def _edns_opts_back_out(self): return _edns_opts_back_out_get(self)
+ __swig_getmethods__["edns_opts_back_out"] = _edns_opts_back_out
+ if _newclass:edns_opts_back_out = _swig_property(_edns_opts_back_out)
+ def _edns_opts_back_in(self): return _edns_opts_back_in_get(self)
+ __swig_getmethods__["edns_opts_back_in"] = _edns_opts_back_in
+ if _newclass:edns_opts_back_in = _swig_property(_edns_opts_back_in)
+ def _edns_opts_front_out(self): return _edns_opts_front_out_get(self)
+ __swig_getmethods__["edns_opts_front_out"] = _edns_opts_front_out
+ if _newclass:edns_opts_front_out = _swig_property(_edns_opts_front_out)
%}
}
-/* ************************************************************************************ *
+/* ************************************************************************************ *
Structure config_strlist
* ************************************************************************************ */
struct config_strlist {
@@ -577,7 +796,7 @@ struct config_strlist {
char* str;
};
-/* ************************************************************************************ *
+/* ************************************************************************************ *
Structure config_str2list
* ************************************************************************************ */
struct config_str2list {
@@ -586,7 +805,7 @@ struct config_str2list {
char* str2;
};
-/* ************************************************************************************ *
+/* ************************************************************************************ *
Structure config_file
* ************************************************************************************ */
struct config_file {
@@ -653,7 +872,7 @@ struct config_file {
struct config_strlist* dlv_anchor_list;
int max_ttl;
int32_t val_date_override;
- int bogus_ttl;
+ int bogus_ttl;
int val_clean_additional;
int val_permissive_mode;
char* val_nsec3_key_iterations;
@@ -674,7 +893,7 @@ struct config_file {
char* python_script;
};
-/* ************************************************************************************ *
+/* ************************************************************************************ *
ASN: Adding structures related to forwards_lookup and dns_cache_find_delegation
* ************************************************************************************ */
struct delegpt_ns {
@@ -712,7 +931,7 @@ struct delegpt {
%inline %{
PyObject* _get_dp_dname(struct delegpt* dp) {
return PyBytes_FromStringAndSize((char*)dp->name, dp->namelen);
- }
+ }
PyObject* _get_dp_dname_components(struct delegpt* dp) {
return GetNameAsLabelList((char*)dp->name, dp->namelen);
}
@@ -767,7 +986,7 @@ struct delegpt {
%}
}
-/* ************************************************************************************ *
+/* ************************************************************************************ *
Enums
* ************************************************************************************ */
%rename ("MODULE_STATE_INITIAL") "module_state_initial";
@@ -820,6 +1039,26 @@ enum verbosity_value {
VERB_ALGO
};
+enum inplace_cb_list_type {
+ /* Inplace callbacks for when a resolved reply is ready to be sent to the
+ * front.*/
+ inplace_cb_reply = 0,
+ /* Inplace callbacks for when a reply is given from the cache. */
+ inplace_cb_reply_cache,
+ /* Inplace callbacks for when a reply is given with local data
+ * (or Chaos reply). */
+ inplace_cb_reply_local,
+ /* Inplace callbacks for when the reply is servfail. */
+ inplace_cb_reply_servfail,
+ /* Inplace callbacks for when a query is ready to be sent to the back.*/
+ inplace_cb_query,
+ /* Inplace callback for when a reply is received from the back. */
+ inplace_cb_edns_back_parsed,
+ /* Total number of types. Used for array initialization.
+ * Should always be last. */
+ inplace_cb_types_total
+};
+
%constant uint16_t PKT_QR = 1; /* QueRy - query flag */
%constant uint16_t PKT_AA = 2; /* Authoritative Answer - server flag */
%constant uint16_t PKT_TC = 4; /* TrunCated - server flag */
@@ -829,17 +1068,17 @@ enum verbosity_value {
%constant uint16_t PKT_AD = 64; /* Authenticated Data - server flag */
%{
-int checkList(PyObject *l)
+int checkList(PyObject *l)
{
PyObject* item;
int i;
- if (l == Py_None)
+ if (l == Py_None)
return 1;
- if (PyList_Check(l))
+ if (PyList_Check(l))
{
- for (i=0; i < PyList_Size(l); i++)
+ for (i=0; i < PyList_Size(l); i++)
{
item = PyList_GetItem(l, i);
if (!PyBytes_Check(item))
@@ -858,7 +1097,7 @@ int pushRRList(sldns_buffer* qb, PyObject *l, uint32_t default_ttl, int qsec,
int i;
size_t len;
- for (i=0; i < PyList_Size(l); i++)
+ for (i=0; i < PyList_Size(l); i++)
{
item = PyList_GetItem(l, i);
@@ -882,9 +1121,9 @@ int pushRRList(sldns_buffer* qb, PyObject *l, uint32_t default_ttl, int qsec,
return 1;
}
-int set_return_msg(struct module_qstate* qstate,
+int set_return_msg(struct module_qstate* qstate,
const char* rr_name, sldns_rr_type rr_type, sldns_rr_class rr_class , uint16_t flags, uint32_t default_ttl,
- PyObject* question, PyObject* answer, PyObject* authority, PyObject* additional)
+ PyObject* question, PyObject* answer, PyObject* authority, PyObject* additional)
{
sldns_buffer *qb = 0;
int res = 1;
@@ -896,7 +1135,7 @@ int set_return_msg(struct module_qstate* qstate,
uint16_t PKT_CD = 16;
uint16_t PKT_RA = 32;
uint16_t PKT_AD = 64;
-
+
if ((!checkList(question)) || (!checkList(answer)) || (!checkList(authority)) || (!checkList(additional)))
return 0;
if ((qb = sldns_buffer_new(LDNS_RR_BUF_SIZE)) == 0) return 0;
@@ -945,7 +1184,7 @@ int set_return_msg(struct module_qstate* qstate,
}
%}
-int set_return_msg(struct module_qstate* qstate,
+int set_return_msg(struct module_qstate* qstate,
const char* rr_name, int rr_type, int rr_class , uint16_t flags, uint32_t default_ttl,
PyObject* question, PyObject* answer, PyObject* authority, PyObject* additional);
@@ -965,17 +1204,17 @@ int set_return_msg(struct module_qstate* qstate,
def set_return_msg(self, qstate):
"""Returns 1 if OK"""
- status = _unboundmodule.set_return_msg(qstate, self.rr_name, self.rr_type, self.rr_class,
+ status = _unboundmodule.set_return_msg(qstate, self.rr_name, self.rr_type, self.rr_class,
self.query_flags, self.default_ttl,
self.question, self.answer, self.authority, self.additional)
if (status) and (PKT_AA & self.query_flags):
qstate.return_msg.rep.authoritative = 1
- return status
+ return status
%}
-/* ************************************************************************************ *
+/* ************************************************************************************ *
ASN: Delegation pointer related functions
* ************************************************************************************ */
@@ -1034,11 +1273,12 @@ struct delegpt* find_delegation(struct module_qstate* qstate, char *nm, size_t n
}
%}
-/* ************************************************************************************ *
+/* ************************************************************************************ *
Functions
* ************************************************************************************ */
-
-// Various debuging functions
+/******************************
+ * Various debuging functions *
+ ******************************/
void verbose(enum verbosity_value level, const char* format, ...);
void log_info(const char* format, ...);
void log_err(const char* format, ...);
@@ -1048,24 +1288,159 @@ void log_dns_msg(const char* str, struct query_info* qinfo, struct reply_info* r
void log_query_info(enum verbosity_value v, const char* str, struct query_info* qinf);
void regional_log_stats(struct regional *r);
-// Free allocated memory from marked sources returning corresponding types
+/***************************************************************************
+ * Free allocated memory from marked sources returning corresponding types *
+ ***************************************************************************/
%typemap(newfree, noblock = 1) char * {
free($1);
}
-// Mark as source returning newly allocated memory
+/***************************************************
+ * Mark as source returning newly allocated memory *
+ ***************************************************/
%newobject sldns_wire2str_type;
%newobject sldns_wire2str_class;
-// LDNS functions
+/******************
+ * LDNS functions *
+ ******************/
char *sldns_wire2str_type(const uint16_t atype);
char *sldns_wire2str_class(const uint16_t aclass);
-// Functions from pythonmod_utils
+/**********************************
+ * Functions from pythonmod_utils *
+ **********************************/
int storeQueryInCache(struct module_qstate* qstate, struct query_info* qinfo, struct reply_info* msgrep, int is_referral);
void invalidateQueryInCache(struct module_qstate* qstate, struct query_info* qinfo);
-// Module conversion functions
+/*******************************
+ * Module conversion functions *
+ *******************************/
const char* strextstate(enum module_ext_state s);
const char* strmodulevent(enum module_ev e);
+/**************************
+ * Edns related functions *
+ **************************/
+struct edns_option* edns_opt_list_find(struct edns_option* list, uint16_t code);
+int edns_register_option(uint16_t opt_code, int bypass_cache_stage,
+ int no_aggregation, struct module_env* env);
+
+%pythoncode %{
+ def register_edns_option(env, code, bypass_cache_stage=False,
+ no_aggregation=False):
+ """Wrapper function to provide keyword attributes."""
+ return edns_register_option(code, bypass_cache_stage,
+ no_aggregation, env)
+%}
+
+/******************************
+ * Callback related functions *
+ ******************************/
+/* typemap to check if argument is callable */
+%typemap(in) PyObject *py_cb {
+ if (!PyCallable_Check($input)) {
+ SWIG_exception_fail(SWIG_TypeError, "Need a callable object!");
+ return NULL;
+ }
+ $1 = $input;
+}
+/* typemap to get content/size from a bytearray */
+%typemap(in) (size_t len, uint8_t* py_bytearray_data) {
+ if (!PyByteArray_CheckExact($input)) {
+ SWIG_exception_fail(SWIG_TypeError, "Expected bytearray!");
+ return NULL;
+ }
+ $2 = (void*)PyByteArray_AsString($input);
+ $1 = PyByteArray_Size($input);
+}
+
+int edns_opt_list_remove(struct edns_option** list, uint16_t code);
+int edns_opt_list_append(struct edns_option** list, uint16_t code, size_t len,
+ uint8_t* py_bytearray_data, struct regional* region);
+
+%{
+ /* This function is called by unbound in order to call the python
+ * callback function. */
+ 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)
+ {
+ PyObject *func, *py_edns, *py_qstate, *py_opt_list_out, *py_qinfo;
+ PyObject *py_rep, *py_region;
+ PyObject *result;
+ int res = 0;
+
+ PyGILState_STATE gstate = PyGILState_Ensure();
+ func = (PyObject *) python_callback;
+ py_edns = SWIG_NewPointerObj((void*) edns, SWIGTYPE_p_edns_data, 0);
+ py_qstate = SWIG_NewPointerObj((void*) qstate,
+ SWIGTYPE_p_module_qstate, 0);
+ py_opt_list_out = SWIG_NewPointerObj((void*) opt_list_out,
+ 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_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_XDECREF(py_edns);
+ Py_XDECREF(py_qstate);
+ Py_XDECREF(py_opt_list_out);
+ Py_XDECREF(py_qinfo);
+ Py_XDECREF(py_rep);
+ Py_XDECREF(py_region);
+ if (result) {
+ res = PyInt_AsLong(result);
+ }
+ Py_XDECREF(result);
+ PyGILState_Release(gstate);
+ return res;
+ }
+
+ /* register a callback */
+ static int python_inplace_cb_register(enum inplace_cb_list_type type,
+ PyObject* py_cb, struct module_env* env, int id)
+ {
+ int ret = inplace_cb_register(python_inplace_cb_reply_generic,
+ type, (void*) py_cb, env, id);
+ if (ret) Py_INCREF(py_cb);
+ return ret;
+ }
+
+ /* Swig implementations for Python */
+ static int register_inplace_cb_reply(PyObject* py_cb,
+ struct module_env* env, int id)
+ {
+ return python_inplace_cb_register(inplace_cb_reply, py_cb, env, id);
+ }
+ static int register_inplace_cb_reply_cache(PyObject* py_cb,
+ struct module_env* env, int id)
+ {
+ return python_inplace_cb_register(inplace_cb_reply_cache, py_cb, env, id);
+ }
+ static int register_inplace_cb_reply_local(PyObject* py_cb,
+ struct module_env* env, int id)
+ {
+ return python_inplace_cb_register(inplace_cb_reply_local, py_cb, env, id);
+ }
+ static int register_inplace_cb_reply_servfail(PyObject* py_cb,
+ struct module_env* env, int id)
+ {
+ return python_inplace_cb_register(inplace_cb_reply_servfail,
+ py_cb, env, id);
+ }
+%}
+/* C declarations */
+int inplace_cb_register(void* cb, enum inplace_cb_list_type type, void* cbarg,
+ struct module_env* env, int id);
+
+/* Swig declarations */
+static int register_inplace_cb_reply(PyObject* py_cb,
+ struct module_env* env, int id);
+static int register_inplace_cb_reply_cache(PyObject* py_cb,
+ struct module_env* env, int id);
+static int register_inplace_cb_reply_local(PyObject* py_cb,
+ struct module_env* env, int id);
+static int register_inplace_cb_reply_servfail(PyObject* py_cb,
+ struct module_env* env, int id);
diff --git a/external/unbound/pythonmod/pythonmod.c b/external/unbound/pythonmod/pythonmod.c
index 48dbc0169..dde7e54b2 100644
--- a/external/unbound/pythonmod/pythonmod.c
+++ b/external/unbound/pythonmod/pythonmod.c
@@ -1,22 +1,22 @@
/*
* pythonmod.c: unbound module C wrapper
- *
+ *
* Copyright (c) 2009, Zdenek Vasicek (vasicek AT fit.vutbr.cz)
* Marek Vavrusa (xvavru00 AT stud.fit.vutbr.cz)
*
* 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.
@@ -63,7 +63,7 @@ typedef void* PyGILState_STATE;
#endif
/**
- * Global state for the module.
+ * Global state for the module.
*/
struct pythonmod_env {
@@ -112,10 +112,12 @@ int pythonmod_init(struct module_env* env, int id)
{
/* Initialize module */
FILE* script_py = NULL;
- PyObject* py_cfg, *res;
+ PyObject* py_init_arg, *res;
PyGILState_STATE gil;
+ int init_standard = 1;
+
struct pythonmod_env* pe = (struct pythonmod_env*)calloc(1, sizeof(struct pythonmod_env));
- if (!pe)
+ if (!pe)
{
log_err("pythonmod: malloc failure");
return 0;
@@ -131,7 +133,7 @@ int pythonmod_init(struct module_env* env, int id)
}
/* Initialize Python libraries */
- if (!Py_IsInitialized())
+ if (!Py_IsInitialized())
{
#if PY_MAJOR_VERSION >= 3
wchar_t progname[8];
@@ -141,6 +143,9 @@ int pythonmod_init(struct module_env* env, int id)
#endif
Py_SetProgramName(progname);
Py_NoSiteFlag = 1;
+#if PY_MAJOR_VERSION >= 3
+ PyImport_AppendInittab(SWIG_name, (void*)SWIG_init);
+#endif
Py_Initialize();
PyEval_InitThreads();
SWIG_init();
@@ -153,10 +158,10 @@ int pythonmod_init(struct module_env* env, int id)
PyRun_SimpleString("import sys \n");
PyRun_SimpleString("sys.path.append('.') \n");
if(env->cfg->directory && env->cfg->directory[0]) {
- char wdir[1524];
- snprintf(wdir, sizeof(wdir), "sys.path.append('%s') \n",
- env->cfg->directory);
- PyRun_SimpleString(wdir);
+ char wdir[1524];
+ snprintf(wdir, sizeof(wdir), "sys.path.append('%s') \n",
+ env->cfg->directory);
+ PyRun_SimpleString(wdir);
}
PyRun_SimpleString("sys.path.append('"RUN_DIR"') \n");
PyRun_SimpleString("sys.path.append('"SHARE_DIR"') \n");
@@ -164,13 +169,13 @@ int pythonmod_init(struct module_env* env, int id)
PyRun_SimpleString("sys.path.append(distutils.sysconfig.get_python_lib(1,0)) \n");
if (PyRun_SimpleString("from unboundmodule import *\n") < 0)
{
- log_err("pythonmod: cannot initialize core module: unboundmodule.py");
+ log_err("pythonmod: cannot initialize core module: unboundmodule.py");
PyGILState_Release(gil);
return 0;
}
/* Check Python file load */
- if ((script_py = fopen(pe->fname, "r")) == NULL)
+ if ((script_py = fopen(pe->fname, "r")) == NULL)
{
log_err("pythonmod: can't open file %s for reading", pe->fname);
PyGILState_Release(gil);
@@ -185,8 +190,8 @@ int pythonmod_init(struct module_env* env, int id)
PyModule_AddObject(pe->module, "mod_env", pe->data);
/* TODO: deallocation of pe->... if an error occurs */
-
- if (PyRun_SimpleFile(script_py, pe->fname) < 0)
+
+ if (PyRun_SimpleFile(script_py, pe->fname) < 0)
{
log_err("pythonmod: can't parse Python script %s", pe->fname);
PyGILState_Release(gil);
@@ -195,41 +200,57 @@ int pythonmod_init(struct module_env* env, int id)
fclose(script_py);
- if ((pe->func_init = PyDict_GetItemString(pe->dict, "init")) == NULL)
+ if ((pe->func_init = PyDict_GetItemString(pe->dict, "init_standard")) == NULL)
{
- log_err("pythonmod: function init is missing in %s", pe->fname);
- PyGILState_Release(gil);
- return 0;
+ init_standard = 0;
+ if ((pe->func_init = PyDict_GetItemString(pe->dict, "init")) == NULL)
+ {
+ log_err("pythonmod: function init is missing in %s", pe->fname);
+ PyGILState_Release(gil);
+ return 0;
+ }
}
- if ((pe->func_deinit = PyDict_GetItemString(pe->dict, "deinit")) == NULL)
+ if ((pe->func_deinit = PyDict_GetItemString(pe->dict, "deinit")) == NULL)
{
log_err("pythonmod: function deinit is missing in %s", pe->fname);
PyGILState_Release(gil);
return 0;
}
- if ((pe->func_operate = PyDict_GetItemString(pe->dict, "operate")) == NULL)
+ if ((pe->func_operate = PyDict_GetItemString(pe->dict, "operate")) == NULL)
{
log_err("pythonmod: function operate is missing in %s", pe->fname);
PyGILState_Release(gil);
return 0;
}
- if ((pe->func_inform = PyDict_GetItemString(pe->dict, "inform_super")) == NULL)
+ if ((pe->func_inform = PyDict_GetItemString(pe->dict, "inform_super")) == NULL)
{
log_err("pythonmod: function inform_super is missing in %s", pe->fname);
PyGILState_Release(gil);
return 0;
}
- py_cfg = SWIG_NewPointerObj((void*) env->cfg, SWIGTYPE_p_config_file, 0);
- res = PyObject_CallFunction(pe->func_init, "iO", id, py_cfg);
- if (PyErr_Occurred())
+ if (init_standard)
+ {
+ py_init_arg = SWIG_NewPointerObj((void*) env, SWIGTYPE_p_module_env, 0);
+ }
+ else
+ {
+ py_init_arg = SWIG_NewPointerObj((void*) env->cfg,
+ SWIGTYPE_p_config_file, 0);
+ }
+ res = PyObject_CallFunction(pe->func_init, "iO", id, py_init_arg);
+ if (PyErr_Occurred())
{
log_err("pythonmod: Exception occurred in function init");
PyErr_Print();
+ Py_XDECREF(res);
+ Py_XDECREF(py_init_arg);
+ PyGILState_Release(gil);
+ return 0;
}
Py_XDECREF(res);
- Py_XDECREF(py_cfg);
+ Py_XDECREF(py_init_arg);
PyGILState_Release(gil);
return 1;
@@ -283,20 +304,20 @@ void pythonmod_inform_super(struct module_qstate* qstate, int id, struct module_
py_qstate = SWIG_NewPointerObj((void*) qstate, SWIGTYPE_p_module_qstate, 0);
py_sqstate = SWIG_NewPointerObj((void*) super, SWIGTYPE_p_module_qstate, 0);
- res = PyObject_CallFunction(pe->func_inform, "iOOO", id, py_qstate,
+ res = PyObject_CallFunction(pe->func_inform, "iOOO", id, py_qstate,
py_sqstate, pq->data);
- if (PyErr_Occurred())
+ if (PyErr_Occurred())
{
log_err("pythonmod: Exception occurred in function inform_super");
PyErr_Print();
qstate->ext_state[id] = module_error;
- }
- else if ((res == NULL) || (!PyObject_IsTrue(res)))
+ }
+ else if ((res == NULL) || (!PyObject_IsTrue(res)))
{
log_err("pythonmod: python returned bad code in inform_super");
qstate->ext_state[id] = module_error;
- }
+ }
Py_XDECREF(res);
Py_XDECREF(py_sqstate);
@@ -305,7 +326,7 @@ void pythonmod_inform_super(struct module_qstate* qstate, int id, struct module_
PyGILState_Release(gil);
}
-void pythonmod_operate(struct module_qstate* qstate, enum module_ev event,
+void pythonmod_operate(struct module_qstate* qstate, enum module_ev event,
int id, struct outbound_entry* ATTR_UNUSED(outbound))
{
struct pythonmod_env* pe = (struct pythonmod_env*)qstate->env->modinfo[id];
@@ -314,10 +335,10 @@ void pythonmod_operate(struct module_qstate* qstate, enum module_ev event,
PyGILState_STATE gil = PyGILState_Ensure();
if ( pq == NULL)
- {
+ {
/* create qstate */
pq = qstate->minfo[id] = malloc(sizeof(struct pythonmod_qstate));
-
+
/* Initialize per query data */
pq->data = Py_None;
Py_INCREF(pq->data);
@@ -325,19 +346,19 @@ void pythonmod_operate(struct module_qstate* qstate, enum module_ev event,
/* Call operate */
py_qstate = SWIG_NewPointerObj((void*) qstate, SWIGTYPE_p_module_qstate, 0);
- res = PyObject_CallFunction(pe->func_operate, "iiOO", id, (int) event,
+ res = PyObject_CallFunction(pe->func_operate, "iiOO", id, (int) event,
py_qstate, pq->data);
- if (PyErr_Occurred())
+ if (PyErr_Occurred())
{
log_err("pythonmod: Exception occurred in function operate, event: %s", strmodulevent(event));
PyErr_Print();
qstate->ext_state[id] = module_error;
- }
- else if ((res == NULL) || (!PyObject_IsTrue(res)))
+ }
+ else if ((res == NULL) || (!PyObject_IsTrue(res)))
{
log_err("pythonmod: python returned bad code, event: %s", strmodulevent(event));
qstate->ext_state[id] = module_error;
- }
+ }
Py_XDECREF(res);
Py_XDECREF(py_qstate);
@@ -351,7 +372,7 @@ void pythonmod_clear(struct module_qstate* qstate, int id)
return;
pq = (struct pythonmod_qstate*)qstate->minfo[id];
- verbose(VERB_ALGO, "pythonmod: clear, id: %d, pq:%lX", id,
+ verbose(VERB_ALGO, "pythonmod: clear, id: %d, pq:%lX", id,
(unsigned long int)pq);
if(pq != NULL)
{
@@ -368,7 +389,7 @@ void pythonmod_clear(struct module_qstate* qstate, int id)
size_t pythonmod_get_mem(struct module_env* env, int id)
{
struct pythonmod_env* pe = (struct pythonmod_env*)env->modinfo[id];
- verbose(VERB_ALGO, "pythonmod: get_mem, id: %d, pe:%lX", id,
+ verbose(VERB_ALGO, "pythonmod: get_mem, id: %d, pe:%lX", id,
(unsigned long int)pe);
if(!pe)
return 0;
@@ -376,11 +397,11 @@ size_t pythonmod_get_mem(struct module_env* env, int id)
}
/**
- * The module function block
+ * The module function block
*/
static struct module_func_block pythonmod_block = {
"python",
- &pythonmod_init, &pythonmod_deinit, &pythonmod_operate, &pythonmod_inform_super,
+ &pythonmod_init, &pythonmod_deinit, &pythonmod_operate, &pythonmod_inform_super,
&pythonmod_clear, &pythonmod_get_mem
};
diff --git a/external/unbound/pythonmod/pythonmod.h b/external/unbound/pythonmod/pythonmod.h
index b108cf923..7c7c0e751 100644
--- a/external/unbound/pythonmod/pythonmod.h
+++ b/external/unbound/pythonmod/pythonmod.h
@@ -55,14 +55,22 @@ int pythonmod_init(struct module_env* env, int id);
void pythonmod_deinit(struct module_env* env, int id);
/** python module operate on a query */
-void pythonmod_operate(struct module_qstate* qstate, enum module_ev event, int id, struct outbound_entry* outbound);
+void pythonmod_operate(struct module_qstate* qstate, enum module_ev event,
+ int id, struct outbound_entry* outbound);
/** python module */
-void pythonmod_inform_super(struct module_qstate* qstate, int id, struct module_qstate* super);
+void pythonmod_inform_super(struct module_qstate* qstate, int id,
+ struct module_qstate* super);
/** python module cleanup query state */
void pythonmod_clear(struct module_qstate* qstate, int id);
/** python module alloc size routine */
size_t pythonmod_get_mem(struct module_env* env, int id);
+
+/** Declared here for fptr_wlist access. The definition is in interface.i. */
+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);
#endif /* PYTHONMOD_H */
diff --git a/external/unbound/pythonmod/pythonmod_utils.c b/external/unbound/pythonmod/pythonmod_utils.c
index 5120074e8..5d70f2b4b 100644
--- a/external/unbound/pythonmod/pythonmod_utils.c
+++ b/external/unbound/pythonmod/pythonmod_utils.c
@@ -74,7 +74,7 @@ int storeQueryInCache(struct module_qstate* qstate, struct query_info* qinfo, st
/* Invalidate the message associated with query_info stored in message cache */
void invalidateQueryInCache(struct module_qstate* qstate, struct query_info* qinfo)
{
- hashvalue_t h;
+ hashvalue_type h;
struct lruhash_entry* e;
struct reply_info *r;
size_t i, j;
@@ -129,7 +129,8 @@ int createResponse(struct module_qstate* qstate, sldns_buffer* pkt)
return 0;
}
/* edns is not examined, but removed from message to help cache */
- if(parse_extract_edns(prs, &edns) != LDNS_RCODE_NOERROR)
+ if(parse_extract_edns(prs, &edns, qstate->env->scratch) !=
+ LDNS_RCODE_NOERROR)
return 0;
/* remove CD-bit, we asked for in case we handle validation ourself */
diff --git a/external/unbound/pythonmod/test-edns.conf b/external/unbound/pythonmod/test-edns.conf
new file mode 100644
index 000000000..440947f01
--- /dev/null
+++ b/external/unbound/pythonmod/test-edns.conf
@@ -0,0 +1,17 @@
+# Example configuration file for edns.py
+server:
+ verbosity: 1
+ interface: 0.0.0.0
+ do-daemonize: no
+ access-control: 0.0.0.0/0 allow
+ chroot: ""
+ username: ""
+ directory: ""
+ logfile: ""
+ pidfile: "unbound.pid"
+ module-config: "validator python iterator"
+
+# Python config section
+python:
+ # Script file to load
+ python-script: "./examples/edns.py"
diff --git a/external/unbound/pythonmod/test-inplace_callbacks.conf b/external/unbound/pythonmod/test-inplace_callbacks.conf
new file mode 100644
index 000000000..d7081faa6
--- /dev/null
+++ b/external/unbound/pythonmod/test-inplace_callbacks.conf
@@ -0,0 +1,17 @@
+# Example configuration file for edns.py
+server:
+ verbosity: 1
+ interface: 0.0.0.0
+ do-daemonize: no
+ access-control: 0.0.0.0/0 allow
+ chroot: ""
+ username: ""
+ directory: ""
+ logfile: ""
+ pidfile: "unbound.pid"
+ module-config: "validator python iterator"
+
+# Python config section
+python:
+ # Script file to load
+ python-script: "./examples/inplace_callbacks.py"