diff options
Diffstat (limited to '')
-rw-r--r-- | external/unbound/pythonmod/doc/conf.py | 5 | ||||
-rw-r--r-- | external/unbound/pythonmod/doc/examples/example1.rst | 14 | ||||
-rw-r--r-- | external/unbound/pythonmod/doc/examples/example2.rst | 45 | ||||
-rw-r--r-- | external/unbound/pythonmod/doc/examples/example4.rst | 180 | ||||
-rw-r--r-- | external/unbound/pythonmod/doc/examples/example5.rst | 191 | ||||
-rw-r--r-- | external/unbound/pythonmod/doc/examples/example6.rst | 299 | ||||
-rw-r--r-- | external/unbound/pythonmod/doc/examples/index.rst | 17 | ||||
-rw-r--r-- | external/unbound/pythonmod/doc/install.rst | 44 | ||||
-rw-r--r-- | external/unbound/pythonmod/doc/modules/functions.rst | 139 | ||||
-rw-r--r-- | external/unbound/pythonmod/doc/modules/struct.rst | 187 | ||||
-rw-r--r-- | external/unbound/pythonmod/interface.i | 565 | ||||
-rw-r--r-- | external/unbound/pythonmod/pythonmod.c | 111 | ||||
-rw-r--r-- | external/unbound/pythonmod/pythonmod.h | 12 | ||||
-rw-r--r-- | external/unbound/pythonmod/pythonmod_utils.c | 5 | ||||
-rw-r--r-- | external/unbound/pythonmod/test-edns.conf | 17 | ||||
-rw-r--r-- | external/unbound/pythonmod/test-inplace_callbacks.conf | 17 |
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" |