aboutsummaryrefslogtreecommitdiff
path: root/external/unbound/pythonmod
diff options
context:
space:
mode:
authorRiccardo Spagni <ric@spagni.net>2014-10-05 23:44:31 +0200
committerRiccardo Spagni <ric@spagni.net>2014-10-05 23:44:31 +0200
commit9ef094b356b4da7542c3cab898dac7e135b76903 (patch)
tree99b5876712b0b1551fc042fe75447b998e4b0fc1 /external/unbound/pythonmod
parentsplit mnemonic printout over 3 lines (diff)
downloadmonero-9ef094b356b4da7542c3cab898dac7e135b76903.tar.xz
added unbound to external deps
Diffstat (limited to 'external/unbound/pythonmod')
-rw-r--r--external/unbound/pythonmod/LICENSE28
-rw-r--r--external/unbound/pythonmod/Makefile58
-rw-r--r--external/unbound/pythonmod/doc/_static/readme1
-rw-r--r--external/unbound/pythonmod/doc/conf.py179
-rw-r--r--external/unbound/pythonmod/doc/examples/example0-1.py34
-rw-r--r--external/unbound/pythonmod/doc/examples/example0.rst129
-rw-r--r--external/unbound/pythonmod/doc/examples/example1.rst42
-rw-r--r--external/unbound/pythonmod/doc/examples/example2.rst46
-rw-r--r--external/unbound/pythonmod/doc/examples/example3.rst63
-rw-r--r--external/unbound/pythonmod/doc/examples/example4.rst164
-rw-r--r--external/unbound/pythonmod/doc/examples/index.rst15
-rw-r--r--external/unbound/pythonmod/doc/index.rst34
-rw-r--r--external/unbound/pythonmod/doc/install.rst59
-rw-r--r--external/unbound/pythonmod/doc/modules/config.rst350
-rw-r--r--external/unbound/pythonmod/doc/modules/env.rst412
-rw-r--r--external/unbound/pythonmod/doc/modules/functions.rst120
-rw-r--r--external/unbound/pythonmod/doc/modules/index.rst11
-rw-r--r--external/unbound/pythonmod/doc/modules/struct.rst427
-rw-r--r--external/unbound/pythonmod/doc/usecase.rst38
-rw-r--r--external/unbound/pythonmod/examples/calc.py77
-rw-r--r--external/unbound/pythonmod/examples/dict.py121
-rw-r--r--external/unbound/pythonmod/examples/dict_data.txt6
-rw-r--r--external/unbound/pythonmod/examples/log.py119
-rw-r--r--external/unbound/pythonmod/examples/resgen.py73
-rw-r--r--external/unbound/pythonmod/examples/resip.py96
-rw-r--r--external/unbound/pythonmod/examples/resmod.py88
-rw-r--r--external/unbound/pythonmod/interface.i916
-rw-r--r--external/unbound/pythonmod/pythonmod.c384
-rw-r--r--external/unbound/pythonmod/pythonmod.h68
-rw-r--r--external/unbound/pythonmod/pythonmod_utils.c178
-rw-r--r--external/unbound/pythonmod/pythonmod_utils.h89
-rw-r--r--external/unbound/pythonmod/test-calc.conf18
-rw-r--r--external/unbound/pythonmod/test-dict.conf18
-rw-r--r--external/unbound/pythonmod/test-log.conf17
-rw-r--r--external/unbound/pythonmod/test-resgen.conf18
-rw-r--r--external/unbound/pythonmod/test-resip.conf18
-rw-r--r--external/unbound/pythonmod/test-resmod.conf19
-rw-r--r--external/unbound/pythonmod/ubmodule-msg.py156
-rw-r--r--external/unbound/pythonmod/ubmodule-tst.py149
39 files changed, 4838 insertions, 0 deletions
diff --git a/external/unbound/pythonmod/LICENSE b/external/unbound/pythonmod/LICENSE
new file mode 100644
index 000000000..7b769d091
--- /dev/null
+++ b/external/unbound/pythonmod/LICENSE
@@ -0,0 +1,28 @@
+Copyright (c) 2009, Zdenek Vasicek (vasicek AT fit.vutbr.cz)
+ Marek Vavrusa (xvavru00 AT stud.fit.vutbr.cz)
+
+All rights reserved.
+
+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.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
diff --git a/external/unbound/pythonmod/Makefile b/external/unbound/pythonmod/Makefile
new file mode 100644
index 000000000..2a0015241
--- /dev/null
+++ b/external/unbound/pythonmod/Makefile
@@ -0,0 +1,58 @@
+# Makefile: tests unbound python module (please edit SCRIPT variable)
+#
+# 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.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+SUEXEC = sudo
+UNBOUND = ../unbound
+SCRIPT = ./test-resip.conf
+
+UNBOUND_OPTS = -dv -c $(SCRIPT)
+
+.PHONY: test sudo suexec doc
+
+all: test
+
+$(UNBOUND):
+ make -C ..
+
+test: $(UNBOUND)
+ $(UNBOUND) $(UNBOUND_OPTS)
+
+sudo: $(UNBOUND)
+ sudo $(UNBOUND) $(UNBOUND_OPTS)
+
+suexec: $(UNBOUND)
+ su -c "$(UNBOUND) $(UNBOUND_OPTS)"
+
+doc:
+ $(MAKE) -C doc html
diff --git a/external/unbound/pythonmod/doc/_static/readme b/external/unbound/pythonmod/doc/_static/readme
new file mode 100644
index 000000000..db676aebb
--- /dev/null
+++ b/external/unbound/pythonmod/doc/_static/readme
@@ -0,0 +1 @@
+this directory exists to pacify sphinx-build.
diff --git a/external/unbound/pythonmod/doc/conf.py b/external/unbound/pythonmod/doc/conf.py
new file mode 100644
index 000000000..bc7a5aba6
--- /dev/null
+++ b/external/unbound/pythonmod/doc/conf.py
@@ -0,0 +1,179 @@
+# -*- coding: utf-8 -*-
+#
+# Unbound scripting interface documentation build configuration file
+#
+# This file is execfile()d with the current directory set to its containing dir.
+#
+# The contents of this file are pickled, so don't put values in the namespace
+# that aren't pickleable (module imports are okay, they're removed automatically).
+#
+# All configuration values have a default value; values that are commented out
+# serve to show the default value.
+
+import sys, os
+
+# If your extensions are in another directory, add it here. If the directory
+# is relative to the documentation root, use os.path.abspath to make it
+# absolute, like shown here.
+sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__),'../..')))
+#print sys.path
+
+# General configuration
+# ---------------------
+
+# Add any Sphinx extension module names here, as strings. They can be extensions
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest']
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General substitutions.
+project = 'Unbound scriptable interface'
+copyright = '2009, Zdenek Vasicek, Marek Vavrusa'
+
+# The default replacements for |version| and |release|, also used in various
+# other places throughout the built documents.
+#
+# The short X.Y version.
+version = '1.0'
+# The full version, including alpha/beta/rc tags.
+release = '1.0.0'
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+today_fmt = '%B %d, %Y'
+
+# List of documents that shouldn't be included in the build.
+#unused_docs = []
+
+# List of directories, relative to source directories, that shouldn't be searched
+# for source files.
+#exclude_dirs = []
+
+# The reST default role (used for this markup: `text`) to use for all documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+
+# Options for HTML output
+# -----------------------
+
+# 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'
+
+# The name for this set of Sphinx documents. If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar. Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (within the static path) to place at the top of
+# the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+html_use_modindex = False
+
+# If false, no index is generated.
+html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, the reST sources are included in the HTML build as _sources/<name>.
+html_copy_source = False
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it. The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = ''
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'unbound_interface'
+
+
+# Options for LaTeX output
+# ------------------------
+
+# The paper size ('letter' or 'a4').
+#latex_paper_size = 'letter'
+
+# The font size ('10pt', '11pt' or '12pt').
+#latex_font_size = '10pt'
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, document class [howto/manual]).
+latex_documents = [
+ ('index', 'Unbound_interface.tex', 'Unbound scriptable interface',
+ 'Zdenek Vasicek, Marek Vavrusa', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# Additional stuff for the LaTeX preamble.
+#latex_preamble = ''
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_use_modindex = True
diff --git a/external/unbound/pythonmod/doc/examples/example0-1.py b/external/unbound/pythonmod/doc/examples/example0-1.py
new file mode 100644
index 000000000..3b234f1e0
--- /dev/null
+++ b/external/unbound/pythonmod/doc/examples/example0-1.py
@@ -0,0 +1,34 @@
+
+def init(id, cfg):
+ log_info("pythonmod: init called, module id is %d port: %d script: %s" % (id, cfg.port, cfg.python_script))
+ return True
+
+def deinit(id):
+ log_info("pythonmod: deinit called, module id is %d" % id)
+ return True
+
+def inform_super(id, qstate, superqstate, qdata):
+ return True
+
+def operate(id, event, qstate, qdata):
+ log_info("pythonmod: operate called, id: %d, event:%s" % (id, strmodulevent(event)))
+
+ if event == MODULE_EVENT_NEW:
+ qstate.ext_state[id] = MODULE_WAIT_MODULE
+ return True
+
+ if event == MODULE_EVENT_MODDONE:
+ log_info("pythonmod: module we are waiting for is done")
+ qstate.ext_state[id] = MODULE_FINISHED
+ return True
+
+ if event == MODULE_EVENT_PASS:
+ log_info("pythonmod: event_pass")
+ qstate.ext_state[id] = MODULE_ERROR
+ return True
+
+ log_err("pythonmod: BAD event")
+ qstate.ext_state[id] = MODULE_ERROR
+ return True
+
+log_info("pythonmod: script loaded.")
diff --git a/external/unbound/pythonmod/doc/examples/example0.rst b/external/unbound/pythonmod/doc/examples/example0.rst
new file mode 100644
index 000000000..80eca5ea6
--- /dev/null
+++ b/external/unbound/pythonmod/doc/examples/example0.rst
@@ -0,0 +1,129 @@
+.. _example_handler:
+
+Fundamentals
+================
+
+This basic example shows how to create simple python module which will pass on the requests to the iterator.
+
+How to enable python module
+----------------------------
+If you look into unbound configuration file, you can find the option `module-config` which specifies the names and the order of modules to be used.
+Example configuration::
+
+ module-config: "validator python iterator"
+
+As soon as the DNS query arrives, Unbound calls modules starting from leftmost - the validator *(it is the first module on the list)*.
+The validator does not know the answer *(it can only validate)*, thus it will pass on the event to the next module.
+Next module is python which can
+
+ a) generate answer *(response)*
+ When python module generates the response unbound calls validator. Validator grabs the answer and determines the security flag.
+
+ b) pass on the event to the iterator.
+ When iterator resolves the query, Unbound informs python module (event :data:`module_event_moddone`). In the end, when the python module is done, validator is called.
+
+Note that the python module is called with :data:`module_event_pass` event, because new DNS event was already handled by validator.
+
+Another situation occurs when we use the following configuration::
+
+ module-config: "python validator iterator"
+
+Python module is the first module here, so it's invoked with :data:`module_event_new` event *(new query)*.
+
+On Python module initialization, module loads script from `python-script` option::
+
+ python-script: "/unbound/test/ubmodule.py"
+
+Simple python module step by step
+---------------------------------
+
+Script file must contain four compulsory functions:
+
+.. function:: init(id, cfg)
+
+ Initialize module internals, like database etc.
+ Called just once on module load.
+
+ :param id: module identifier (integer)
+ :param cfg: :class:`config_file` configuration structure
+
+::
+
+ def init(id, cfg):
+ log_info("pythonmod: init called, module id is %d port: %d script: %s" % (id, cfg.port, cfg.python_script))
+ return True
+
+
+.. function:: deinit(id)
+
+ Deinitialize module internals.
+ Called just once on module unload.
+
+ :param id: module identifier (integer)
+
+::
+
+ def deinit(id):
+ log_info("pythonmod: deinit called, module id is %d" % id)
+ return True
+
+
+.. function:: inform_super(id, qstate, superqstate, qdata)
+
+ Inform super querystate about the results from this subquerystate.
+ Is called when the querystate is finished.
+
+ :param id: module identifier (integer)
+ :param qstate: :class:`module_qstate` Query state
+ :param superqstate: :class:`pythonmod_qstate` Mesh state
+ :param qdata: :class:`query_info` Query data
+
+::
+
+ def inform_super(id, qstate, superqstate, qdata):
+ return True
+
+
+
+.. function:: operate(id, event, qstate, qdata)
+
+ Perform action on pending query. Accepts a new query, or work on pending query.
+
+ You have to set qstate.ext_state on exit.
+ The state informs unbound about result and controls the following states.
+
+ :param id: module identifier (integer)
+ :param qstate: :class:`module_qstate` query state structure
+ :param qdata: :class:`query_info` per query data, here you can store your own data
+
+::
+
+ def operate(id, event, qstate, qdata):
+ log_info("pythonmod: operate called, id: %d, event:%s" % (id, strmodulevent(event)))
+ if event == MODULE_EVENT_NEW:
+ qstate.ext_state[id] = MODULE_WAIT_MODULE
+ return True
+
+ if event == MODULE_EVENT_MODDONE:
+ qstate.ext_state[id] = MODULE_FINISHED
+ return True
+
+ if event == MODULE_EVENT_PASS:
+ qstate.ext_state[id] = MODULE_ERROR
+ return True
+
+ log_err("pythonmod: BAD event")
+ qstate.ext_state[id] = MODULE_ERROR
+ return True
+
+
+Complete source code
+--------------------
+
+.. literalinclude:: example0-1.py
+ :language: python
+
+As you can see, the source code is much more flexible in contrast to C modules.
+Moreover, compulsory functions called on appropriate module events allows to handle almost
+anything from web control to query analysis.
+
diff --git a/external/unbound/pythonmod/doc/examples/example1.rst b/external/unbound/pythonmod/doc/examples/example1.rst
new file mode 100644
index 000000000..b49e64409
--- /dev/null
+++ b/external/unbound/pythonmod/doc/examples/example1.rst
@@ -0,0 +1,42 @@
+.. _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``.
+This packet will be send to a client that asked for it.
+
+Complete source code
+--------------------
+
+.. literalinclude:: ../../examples/log.py
+ :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"``.
+
+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
+ [1231790168] unbound[7941:0] info: query response was ANSWER
+ [1231790168] unbound[7941:0] info: pythonmod: operate called, id: 1, event:module_event_moddone
+ ----------------------------------------------------------------------------------------------------
+ Query: f.gtld-servers.NET., type: AAAA (28), class: IN (1)
+ ----------------------------------------------------------------------------------------------------
+ Return reply :: flags: 8080, QDcount: 1, Security:0, TTL=86400
+ qinfo :: qname: ['f', 'gtld-servers', 'NET', ''] f.gtld-servers.NET., qtype: AAAA, qclass: IN
+ Reply:
+ 0 : ['gtld-servers', 'NET', ''] gtld-servers.NET. flags: 0000 type: SOA (6) class: IN (1)
+ 0 : TTL= 86400
+ 0x00 | 00 3A 02 41 32 05 4E 53 54 4C 44 03 43 4F 4D 00 05 | . : . A 2 . N S T L D . C O M . .
+ 0x10 | 05 6E 73 74 6C 64 0C 76 65 72 69 73 69 67 6E 2D 67 | . n s t l d . v e r i s i g n - g
+ 0x20 | 67 72 73 03 43 4F 4D 00 77 74 2D 64 00 00 0E 10 00 | g r s . C O M . w t - d . . . . .
+ 0x30 | 00 00 03 84 00 12 75 00 00 01 51 80 | . . . . . . u . . . Q .
+
diff --git a/external/unbound/pythonmod/doc/examples/example2.rst b/external/unbound/pythonmod/doc/examples/example2.rst
new file mode 100644
index 000000000..f00fcc239
--- /dev/null
+++ b/external/unbound/pythonmod/doc/examples/example2.rst
@@ -0,0 +1,46 @@
+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.
+
+Complete source code
+--------------------
+
+.. literalinclude:: ../../examples/resgen.py
+ :language: python
+
+Testing
+-------
+
+Run the unbound server:
+
+``root@localhost>unbound -dv -c ./test-resgen.conf``
+
+Query for a A record ending with .localdomain
+
+``dig A test.xxx.localdomain @127.0.0.1``
+
+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.
diff --git a/external/unbound/pythonmod/doc/examples/example3.rst b/external/unbound/pythonmod/doc/examples/example3.rst
new file mode 100644
index 000000000..6213dc188
--- /dev/null
+++ b/external/unbound/pythonmod/doc/examples/example3.rst
@@ -0,0 +1,63 @@
+Response modification
+=====================
+
+This example shows how to modify the response produced by the ``iterator`` module.
+
+As soon as the iterator module returns the response, we :
+
+1. invalidate the data in cache
+2. modify the response *TTL*
+3. rewrite the data in cache
+4. return modified packet
+
+Note that the steps 1 and 3 are neccessary only in case, the python module is the first module in the processing chain.
+In other cases, the validator module guarantees updating data which are produced by iterator module.
+
+Complete source code
+--------------------
+
+.. literalinclude:: ../../examples/resmod.py
+ :language: python
+
+Testing
+-------
+
+Run Unbound server:
+
+``root@localhost>unbound -dv -c ./test-resmod.conf``
+
+Issue a query for name ending with "nic.cz."
+
+``>>>dig A @127.0.0.1 www.nic.cz``
+
+::
+
+ ;; global options: printcmd
+ ;; Got answer:
+ ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 48831
+ ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 3, ADDITIONAL: 5
+
+ ;; QUESTION SECTION:
+ ;www.nic.cz. IN A
+
+ ;; ANSWER SECTION:
+ www.nic.cz. 10 IN A 217.31.205.50
+
+ ;; AUTHORITY SECTION:
+ nic.cz. 10 IN NS e.ns.nic.cz.
+ nic.cz. 10 IN NS a.ns.nic.cz.
+ nic.cz. 10 IN NS c.ns.nic.cz.
+
+ ;; ADDITIONAL SECTION:
+ a.ns.nic.cz. 10 IN A 217.31.205.180
+ a.ns.nic.cz. 10 IN AAAA 2001:1488:dada:176::180
+ c.ns.nic.cz. 10 IN A 195.66.241.202
+ c.ns.nic.cz. 10 IN AAAA 2a01:40:1000::2
+ e.ns.nic.cz. 10 IN A 194.146.105.38
+
+ ;; Query time: 166 msec
+ ;; SERVER: 127.0.0.1#53(127.0.0.1)
+ ;; WHEN: Mon Jan 02 13:39:43 2009
+ ;; MSG SIZE rcvd: 199
+
+As you can see, TTL of all the records is set to 10.
diff --git a/external/unbound/pythonmod/doc/examples/example4.rst b/external/unbound/pythonmod/doc/examples/example4.rst
new file mode 100644
index 000000000..6cc484797
--- /dev/null
+++ b/external/unbound/pythonmod/doc/examples/example4.rst
@@ -0,0 +1,164 @@
+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.
+
+Key parts
+-----------
+
+Initialization
+~~~~~~~~~~~~~~~~~~~~~~~
+On **init()** module loads dictionary from a text file containing records in ``word [tab] translation`` format.
+::
+
+ def init(id, cfg):
+ log_info("pythonmod: dict init")
+ f = open("examples/dict_data.txt", "r")
+ ...
+
+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.]``.
+Word lookup is done by simple ``dict`` lookup from broken DNS request.
+Query name is divided into a list of labels. This list is accesible as qname_list attribute.
+::
+
+ aword = ' '.join(qstate.qinfo.qname_list[0:-4]) #skip last four labels
+ adict = qstate.qinfo.qname_list[-4] #get 4th label from the end
+
+ words = [] #list of words
+ if (adict == "en") and (aword in en_dict):
+ words = en_dict[aword]
+
+ 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.
+
+
+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)
+
+ 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
+
+ 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)*.
+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)*.
+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`.
+
+**Steps:**
+
+1. create :class:`DNSMessage` instance
+2. append TXT records containing the translation
+3. set response to ``qstate.return_msg``
+
+Testing
+-------
+
+Run the Unbound server:
+
+``root@localhost>unbound -dv -c ./test-dict.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/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
+
+``>>>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 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:
+ ...
+
+Complete source code
+--------------------
+
+.. literalinclude:: ../../examples/dict.py
+ :language: python
diff --git a/external/unbound/pythonmod/doc/examples/index.rst b/external/unbound/pythonmod/doc/examples/index.rst
new file mode 100644
index 000000000..6c5022581
--- /dev/null
+++ b/external/unbound/pythonmod/doc/examples/index.rst
@@ -0,0 +1,15 @@
+.. _Tutorials:
+
+==============================
+Tutorials
+==============================
+
+Here you can find several tutorials which clarify the usage and capabilities of Unbound scriptable interface.
+
+`Tutorials`
+
+.. toctree::
+ :maxdepth: 2
+ :glob:
+
+ example*
diff --git a/external/unbound/pythonmod/doc/index.rst b/external/unbound/pythonmod/doc/index.rst
new file mode 100644
index 000000000..fe9bcf42b
--- /dev/null
+++ b/external/unbound/pythonmod/doc/index.rst
@@ -0,0 +1,34 @@
+Unbound scriptable interface
+=======================================
+
+Python module for **Unbound** provides easy-to-use flexible solution,
+for scripting query events and much more!
+
+Along with extensible **SWIG** interface, it turns **Unbound** into dynamic *DNS* service
+designed for rapid development of *DNS* based applications, like detailed *(per query/domain)* statistics,
+monitoring with anything Python can offer *(database backend, http server)*.
+
+**Key features**
+ * Rapid dynamic DNS-based application development in **Python**
+ * Extensible interface with **SWIG**
+ * Easy to use debugging and analysis tool
+ * Capable to produce authoritative answers
+ * Support for logging or doing detailed statistics
+ * Allows to manipulate with content of cache memory
+
+Contents
+--------
+.. toctree::
+ :maxdepth: 2
+
+ install
+ examples/index
+ usecase
+ modules/index
+
+Indices and tables
+-------------------
+
+* :ref:`genindex`
+* :ref:`search`
+
diff --git a/external/unbound/pythonmod/doc/install.rst b/external/unbound/pythonmod/doc/install.rst
new file mode 100644
index 000000000..991e2b4be
--- /dev/null
+++ b/external/unbound/pythonmod/doc/install.rst
@@ -0,0 +1,59 @@
+Installation
+===================================
+
+**Prerequisites**
+
+Python 2.4 or higher, SWIG 1.3 or higher, GNU make
+
+**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**
+
+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
+
+You need GNU make to compile sources.
+SWIG and Python devel libraries to compile extension module.
+
+**Testing**
+
+If the compilation is successful, you can test the extension module by::
+
+ > cd pythonmod
+ > make sudo # or "make test" or "make suexec"
+
+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)::
+
+ ; <<>> DiG 9.5.0-P2 <<>> TXT @127.0.0.1 aught.en._dict_.cz
+ ; (1 server found)
+ ;; global options: printcmd
+ ;; 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
+
+ ;; ANSWER SECTION:
+ 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.
diff --git a/external/unbound/pythonmod/doc/modules/config.rst b/external/unbound/pythonmod/doc/modules/config.rst
new file mode 100644
index 000000000..1277bcedd
--- /dev/null
+++ b/external/unbound/pythonmod/doc/modules/config.rst
@@ -0,0 +1,350 @@
+Configuration interface
+=======================
+
+Currently passed to Python module in init(module_id, cfg).
+
+config_file
+--------------------
+
+.. class:: config_file
+
+ This class provides these data attributes:
+
+ .. attribute:: verbosity
+
+ Verbosity level as specified in the config file.
+
+ .. attribute:: stat_interval
+
+ Statistics interval (in seconds).
+
+ .. attribute:: stat_cumulative
+
+ If false, statistics values are reset after printing them.
+
+ .. attribute:: stat_extended
+
+ If true, the statistics are kept in greater detail.
+
+ .. attribute:: num_threads
+
+ Number of threads to create.
+
+ .. attribute:: port
+
+ Port on which queries are answered.
+
+ .. attribute:: do_ip4
+
+ Do ip4 query support.
+
+ .. attribute:: do_ip6
+
+ Do ip6 query support.
+
+ .. attribute:: do_udp
+
+ Do udp query support.
+
+ .. attribute:: do_tcp
+
+ Do tcp query support.
+
+ .. attribute:: outgoing_num_ports
+
+ Outgoing port range number of ports (per thread).
+
+ .. attribute:: outgoing_num_tcp
+
+ Number of outgoing tcp buffers per (per thread).
+
+ .. attribute:: incoming_num_tcp
+
+ Number of incoming tcp buffers per (per thread).
+
+ .. attribute:: outgoing_avail_ports
+
+ Allowed udp port numbers, array with 0 if not allowed.
+
+ .. attribute:: msg_buffer_size
+
+ Number of bytes buffer size for DNS messages.
+
+ .. attribute:: msg_cache_size
+
+ Size of the message cache.
+
+ .. attribute:: msg_cache_slabs
+
+ Slabs in the message cache.
+
+ .. attribute:: num_queries_per_thread
+
+ Number of queries every thread can service.
+
+ .. attribute:: jostle_time
+
+ Number of msec to wait before items can be jostled out.
+
+ .. attribute:: rrset_cache_size
+
+ Size of the rrset cache.
+
+ .. attribute:: rrset_cache_slabs
+
+ Slabs in the rrset cache.
+
+ .. attribute:: host_ttl
+
+ Host cache ttl in seconds.
+
+ .. attribute:: lame_ttl
+
+ Host is lame for a zone ttl, in seconds.
+
+ .. attribute:: infra_cache_slabs
+
+ Number of slabs in the infra host cache.
+
+ .. attribute:: infra_cache_numhosts
+
+ Max number of hosts in the infra cache.
+
+ .. attribute:: infra_cache_lame_size
+
+ Max size of lame zones per host in the infra cache.
+
+ .. attribute:: target_fetch_policy
+
+ The target fetch policy for the iterator.
+
+ .. attribute:: if_automatic
+
+ Automatic interface for incoming messages. Uses ipv6 remapping,
+ and recvmsg/sendmsg ancillary data to detect interfaces, boolean.
+
+ .. attribute:: num_ifs
+
+ Number of interfaces to open. If 0 default all interfaces.
+
+ .. attribute:: ifs
+
+ Interface description strings (IP addresses).
+
+ .. attribute:: num_out_ifs
+
+ Number of outgoing interfaces to open.
+ If 0 default all interfaces.
+
+ .. attribute:: out_ifs
+
+ Outgoing interface description strings (IP addresses).
+
+ .. attribute:: root_hints
+
+ The root hints.
+
+ .. attribute:: stubs
+
+ The stub definitions, linked list.
+
+ .. attribute:: forwards
+
+ The forward zone definitions, linked list.
+
+ .. attribute:: donotqueryaddrs
+
+ List of donotquery addresses, linked list.
+
+ .. attribute:: acls
+
+ List of access control entries, linked list.
+
+ .. attribute:: donotquery_localhost
+
+ Use default localhost donotqueryaddr entries.
+
+ .. attribute:: harden_short_bufsize
+
+ Harden against very small edns buffer sizes.
+
+ .. attribute:: harden_large_queries
+
+ Harden against very large query sizes.
+
+ .. attribute:: harden_glue
+
+ Harden against spoofed glue (out of zone data).
+
+ .. attribute:: harden_dnssec_stripped
+
+ Harden against receiving no DNSSEC data for trust anchor.
+
+ .. attribute:: harden_referral_path
+
+ Harden the referral path, query for NS,A,AAAA and validate.
+
+ .. attribute:: use_caps_bits_for_id
+
+ Use 0x20 bits in query as random ID bits.
+
+ .. attribute:: private_address
+
+ Strip away these private addrs from answers, no DNS Rebinding.
+
+ .. attribute:: private_domain
+
+ Allow domain (and subdomains) to use private address space.
+
+ .. attribute:: unwanted_threshold
+
+ What threshold for unwanted action.
+
+ .. attribute:: chrootdir
+
+ Chrootdir, if not "" or chroot will be done.
+
+ .. attribute:: username
+
+ Username to change to, if not "".
+
+ .. attribute:: directory
+
+ Working directory.
+
+ .. attribute:: logfile
+
+ Filename to log to.
+
+ .. attribute:: pidfile
+
+ Pidfile to write pid to.
+
+ .. attribute:: use_syslog
+
+ Should log messages be sent to syslogd.
+
+ .. attribute:: hide_identity
+
+ Do not report identity (id.server, hostname.bind).
+
+ .. attribute:: hide_version
+
+ Do not report version (version.server, version.bind).
+
+ .. attribute:: identity
+
+ Identity, hostname is returned if "".
+
+ .. attribute:: version
+
+ Version, package version returned if "".
+
+ .. attribute:: module_conf
+
+ The module configuration string.
+
+ .. attribute:: trust_anchor_file_list
+
+ Files with trusted DS and DNSKEYs in zonefile format, list.
+
+ .. attribute:: trust_anchor_list
+
+ List of trustanchor keys, linked list.
+
+ .. attribute:: trusted_keys_file_list
+
+ Files with trusted DNSKEYs in named.conf format, list.
+
+ .. attribute:: dlv_anchor_file
+
+ DLV anchor file.
+
+ .. attribute:: dlv_anchor_list
+
+ DLV anchor inline.
+
+ .. attribute:: max_ttl
+
+ The number of seconds maximal TTL used for RRsets and messages.
+
+ .. attribute:: val_date_override
+
+ If not 0, this value is the validation date for RRSIGs.
+
+ .. attribute:: bogus_ttl
+
+ This value sets the number of seconds before revalidating bogus.
+
+ .. attribute:: val_clean_additional
+
+ Should validator clean additional section for secure msgs.
+
+ .. attribute:: val_permissive_mode
+
+ Should validator allow bogus messages to go through.
+
+ .. attribute:: val_nsec3_key_iterations
+
+ Nsec3 maximum iterations per key size, string.
+
+ .. attribute:: key_cache_size
+
+ Size of the key cache.
+
+ .. attribute:: key_cache_slabs
+
+ Slabs in the key cache.
+
+ .. attribute:: neg_cache_size
+
+ Size of the neg cache.
+
+
+ .. attribute:: local_zones
+
+ Local zones config.
+
+ .. attribute:: local_zones_nodefault
+
+ Local zones nodefault list.
+
+ .. attribute:: local_data
+
+ Local data RRs configged.
+
+ .. attribute:: remote_control_enable
+
+ Remote control section. enable toggle.
+
+ .. attribute:: control_ifs
+
+ The interfaces the remote control should listen on.
+
+ .. attribute:: control_port
+
+ Port number for the control port.
+
+ .. attribute:: server_key_file
+
+ Private key file for server.
+
+ .. attribute:: server_cert_file
+
+ Certificate file for server.
+
+ .. attribute:: control_key_file
+
+ Private key file for unbound-control.
+
+ .. attribute:: control_cert_file
+
+ Certificate file for unbound-control.
+
+ .. attribute:: do_daemonize
+
+ Daemonize, i.e. fork into the background.
+
+ .. attribute:: python_script
+
+ Python script file.
diff --git a/external/unbound/pythonmod/doc/modules/env.rst b/external/unbound/pythonmod/doc/modules/env.rst
new file mode 100644
index 000000000..42dbbd1cf
--- /dev/null
+++ b/external/unbound/pythonmod/doc/modules/env.rst
@@ -0,0 +1,412 @@
+Global environment
+==================
+
+Global variables
+----------------
+
+.. envvar:: mod_env
+
+ Module environment, contains data pointer for module-specific data.
+ See :class:`pythonmod_env`.
+
+
+Predefined constants
+-----------------------
+
+Module extended state
+~~~~~~~~~~~~~~~~~~~~~~~
+
+.. data:: module_state_initial
+
+ Initial state - new DNS query.
+
+.. data:: module_wait_reply
+
+ Waiting for reply to outgoing network query.
+
+.. data:: module_wait_module
+
+ Module is waiting for another module.
+
+.. data:: module_wait_subquery
+
+ Module is waiting for sub-query.
+
+.. data:: module_error
+
+ Module could not finish the query.
+
+.. data:: module_finished
+
+ Module is finished with query.
+
+Module event
+~~~~~~~~~~~~~
+.. data:: module_event_new
+
+ New DNS query.
+
+.. data:: module_event_pass
+
+ Query passed by other module.
+
+.. data:: module_event_reply
+
+ Reply inbound from server.
+
+.. data:: module_event_noreply
+
+ No reply, timeout or other error.
+
+.. data:: module_event_capsfail
+
+ Reply is there, but capitalisation check failed.
+
+.. data:: module_event_moddone
+
+ Next module is done, and its reply is awaiting you.
+
+.. data:: module_event_error
+
+ Error occured.
+
+Security status
+~~~~~~~~~~~~~~~~
+
+.. data:: sec_status_unchecked
+
+ Means that object has yet to be validated.
+
+.. data:: sec_status_bogus
+
+ Means that the object *(RRset or message)* failed to validate
+ *(according to local policy)*, but should have validated.
+
+.. data:: sec_status_indeterminate
+
+ Means that the object is insecure, but not
+ authoritatively so. Generally this means that the RRset is not
+ below a configured trust anchor.
+
+.. data:: sec_status_insecure
+
+ Means that the object is authoritatively known to be
+ insecure. Generally this means that this RRset is below a trust
+ anchor, but also below a verified, insecure delegation.
+
+.. data:: sec_status_secure
+
+ Means that the object (RRset or message) validated according to local policy.
+
+Resource records (RR sets)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The different RR classes.
+
+ .. data:: RR_CLASS_IN
+
+ Internet.
+
+ .. data:: RR_CLASS_CH
+
+ Chaos.
+
+ .. data:: RR_CLASS_HS
+
+ Hesiod (Dyer 87)
+
+ .. data:: RR_CLASS_NONE
+
+ None class, dynamic update.
+
+ .. data:: RR_CLASS_ANY
+
+ Any class.
+
+
+The different RR types.
+
+
+ .. data:: RR_TYPE_A
+
+ A host address.
+
+ .. data:: RR_TYPE_NS
+
+ An authoritative name server.
+
+ .. data:: RR_TYPE_MD
+
+ A mail destination (Obsolete - use MX).
+
+ .. data:: RR_TYPE_MF
+
+ A mail forwarder (Obsolete - use MX).
+
+ .. data:: RR_TYPE_CNAME
+
+ The canonical name for an alias.
+
+ .. data:: RR_TYPE_SOA
+
+ Marks the start of a zone of authority.
+
+ .. data:: RR_TYPE_MB
+
+ A mailbox domain name (EXPERIMENTAL).
+
+ .. data:: RR_TYPE_MG
+
+ A mail group member (EXPERIMENTAL).
+
+ .. data:: RR_TYPE_MR
+
+ A mail rename domain name (EXPERIMENTAL).
+
+ .. data:: RR_TYPE_NULL
+
+ A null RR (EXPERIMENTAL).
+
+ .. data:: RR_TYPE_WKS
+
+ A well known service description.
+
+ .. data:: RR_TYPE_PTR
+
+ A domain name pointer.
+
+ .. data:: RR_TYPE_HINFO
+
+ Host information.
+
+ .. data:: RR_TYPE_MINFO
+
+ Mailbox or mail list information.
+
+ .. data:: RR_TYPE_MX
+
+ Mail exchange.
+
+ .. data:: RR_TYPE_TXT
+
+ Text strings.
+
+ .. data:: RR_TYPE_RP
+
+ RFC1183.
+
+ .. data:: RR_TYPE_AFSDB
+
+ RFC1183.
+
+ .. data:: RR_TYPE_X25
+
+ RFC1183.
+
+ .. data:: RR_TYPE_ISDN
+
+ RFC1183.
+
+ .. data:: RR_TYPE_RT
+
+ RFC1183.
+
+ .. data:: RR_TYPE_NSAP
+
+ RFC1706.
+
+ .. data:: RR_TYPE_NSAP_PTR
+
+ RFC1348.
+
+ .. data:: RR_TYPE_SIG
+
+ 2535typecode.
+
+ .. data:: RR_TYPE_KEY
+
+ 2535typecode.
+
+ .. data:: RR_TYPE_PX
+
+ RFC2163.
+
+ .. data:: RR_TYPE_GPOS
+
+ RFC1712.
+
+ .. data:: RR_TYPE_AAAA
+
+ IPv6 address.
+
+ .. data:: RR_TYPE_LOC
+
+ LOC record RFC1876.
+
+ .. data:: RR_TYPE_NXT
+
+ 2535typecode.
+
+ .. data:: RR_TYPE_EID
+
+ draft-ietf-nimrod-dns-01.txt.
+
+ .. data:: RR_TYPE_NIMLOC
+
+ draft-ietf-nimrod-dns-01.txt.
+
+ .. data:: RR_TYPE_SRV
+
+ SRV record RFC2782.
+
+ .. data:: RR_TYPE_ATMA
+
+ http://www.jhsoft.com/rfc/af-saa-0069.000.rtf.
+
+ .. data:: RR_TYPE_NAPTR
+
+ RFC2915.
+
+ .. data:: RR_TYPE_KX
+
+ RFC2230.
+
+ .. data:: RR_TYPE_CERT
+
+ RFC2538.
+
+ .. data:: RR_TYPE_A6
+
+ RFC2874.
+
+ .. data:: RR_TYPE_DNAME
+
+ RFC2672.
+
+ .. data:: RR_TYPE_SINK
+
+ dnsind-kitchen-sink-02.txt.
+
+ .. data:: RR_TYPE_OPT
+
+ Pseudo OPT record.
+
+ .. data:: RR_TYPE_APL
+
+ RFC3123.
+
+ .. data:: RR_TYPE_DS
+
+ draft-ietf-dnsext-delegation.
+
+ .. data:: RR_TYPE_SSHFP
+
+ SSH Key Fingerprint.
+
+ .. data:: RR_TYPE_IPSECKEY
+
+ draft-richardson-ipseckey-rr-11.txt.
+
+ .. data:: RR_TYPE_RRSIG
+
+ draft-ietf-dnsext-dnssec-25.
+
+ .. data:: RR_TYPE_NSEC
+ .. data:: RR_TYPE_DNSKEY
+ .. data:: RR_TYPE_DHCID
+ .. data:: RR_TYPE_NSEC3
+ .. data:: RR_TYPE_NSEC3PARAMS
+ .. data:: RR_TYPE_UINFO
+ .. data:: RR_TYPE_UID
+ .. data:: RR_TYPE_GID
+ .. data:: RR_TYPE_UNSPEC
+ .. data:: RR_TYPE_TSIG
+ .. data:: RR_TYPE_IXFR
+ .. data:: RR_TYPE_AXFR
+ .. data:: RR_TYPE_MAILB
+
+ A request for mailbox-related records (MB, MG or MR).
+
+ .. data:: RR_TYPE_MAILA
+
+ A request for mail agent RRs (Obsolete - see MX).
+
+ .. data:: RR_TYPE_ANY
+
+ Any type *(wildcard)*.
+
+ .. data:: RR_TYPE_DLV
+
+ RFC 4431, 5074, DNSSEC Lookaside Validation.
+
+Return codes
+~~~~~~~~~~~~
+
+Return codes for packets.
+
+.. data:: RCODE_NOERROR
+.. data:: RCODE_FORMERR
+.. data:: RCODE_SERVFAIL
+.. data:: RCODE_NXDOMAIN
+.. data:: RCODE_NOTIMPL
+.. data:: RCODE_REFUSED
+.. data:: RCODE_YXDOMAIN
+.. data:: RCODE_YXRRSET
+.. data:: RCODE_NXRRSET
+.. data:: RCODE_NOTAUTH
+.. data:: RCODE_NOTZONE
+
+Packet data
+~~~~~~~~~~~~
+
+.. data:: PKT_QR
+
+ Query - query flag.
+
+.. data:: PKT_AA
+
+ Authoritative Answer - server flag.
+
+.. data:: PKT_TC
+
+ Truncated - server flag.
+
+.. data:: PKT_RD
+
+ Recursion desired - query flag.
+
+.. data:: PKT_CD
+
+ Checking disabled - query flag.
+
+.. data:: PKT_RA
+
+ Recursion available - server flag.
+
+.. data:: PKT_AD
+
+ Authenticated data - server flag.
+
+
+Verbosity value
+~~~~~~~~~~~~~~~~
+
+.. data:: NO_VERBOSE
+
+ No verbose messages.
+
+.. data:: VERB_OPS
+
+ Operational information.
+
+.. data:: VERB_DETAIL
+
+ Detailed information.
+
+.. data:: VERB_QUERY
+
+ Query level information.
+
+.. data:: VERB_ALGO
+
+ Algorithm level information.
diff --git a/external/unbound/pythonmod/doc/modules/functions.rst b/external/unbound/pythonmod/doc/modules/functions.rst
new file mode 100644
index 000000000..45a469fec
--- /dev/null
+++ b/external/unbound/pythonmod/doc/modules/functions.rst
@@ -0,0 +1,120 @@
+Scriptable functions
+====================
+
+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.
+
+ :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.
+
+ :param qstate: :class:`module_qstate`
+ :param qinfo: :class:`query_info`
+
+
+Logging
+-------
+
+.. function:: verbose(level, msg)
+
+ Log a verbose message, pass the level for this message.
+ No trailing newline is needed.
+
+ :param level: verbosity level for this message, compared to global verbosity setting.
+ :param msg: string message
+
+.. function:: log_info(msg)
+
+ Log informational message. No trailing newline is needed.
+
+ :param msg: string message
+
+.. function:: log_err(msg)
+
+ Log error message. No trailing newline is needed.
+
+ :param msg: string message
+
+.. function:: log_warn(msg)
+
+ Log warning message. No trailing newline is needed.
+
+ :param msg: string message
+
+.. function:: log_hex(msg, data, length)
+
+ Log a hex-string to the log. Can be any length.
+ performs mallocs to do so, slow. But debug useful.
+
+ :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/index.rst b/external/unbound/pythonmod/doc/modules/index.rst
new file mode 100644
index 000000000..ff0b95695
--- /dev/null
+++ b/external/unbound/pythonmod/doc/modules/index.rst
@@ -0,0 +1,11 @@
+Unbound module documentation
+=======================================
+
+.. toctree::
+ :maxdepth: 2
+
+ env
+ struct
+ functions
+ config
+
diff --git a/external/unbound/pythonmod/doc/modules/struct.rst b/external/unbound/pythonmod/doc/modules/struct.rst
new file mode 100644
index 000000000..c41e10b73
--- /dev/null
+++ b/external/unbound/pythonmod/doc/modules/struct.rst
@@ -0,0 +1,427 @@
+Scriptable structures
+=====================
+
+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.
+
+
+query_info
+----------------
+
+.. class:: query_info
+
+ This class provides these data attributes:
+
+ .. attribute:: qname
+
+ The original question in the wireformat format (e.g. \\x03www\\x03nic\\x02cz\\x00 for www.nic.cz)
+
+ .. attribute:: qname_len
+
+ Lenght of question name (number of bytes).
+
+ .. attribute:: qname_list[]
+
+ The question ``qname`` converted into list of labels (e.g. ['www','nic','cz',''] for www.nic.cz)
+
+ .. attribute:: qname_str
+
+ The question ``qname`` converted into string (e.g. www.nic.cz. for www.nic.cz)
+
+ .. attribute:: qtype
+
+ The class type asked for. See RR_TYPE\_ predefined constants.
+
+ .. attribute:: qtype_str
+
+ The ``qtype`` in display presentation format (string) (e.g 'A' for RR_TYPE_A)
+
+ .. attribute:: qclass
+
+ The question class. See RR_CLASS\_ predefined constants.
+
+ .. attribute:: qclass_str
+
+ The ``qclass`` in display presentation format (string).
+
+reply_info
+--------------------
+
+.. class:: reply_info
+
+ This class provides these data attributes:
+
+ .. attribute:: flags
+
+ The flags for the answer, host byte order.
+
+ .. attribute:: qdcount
+
+ Number of RRs in the query section.
+ If qdcount is not 0, then it is 1, and the data that appears
+ in the reply is the same as the query_info.
+ Host byte order.
+
+ .. attribute:: ttl
+
+ TTL of the entire reply (for negative caching).
+ only for use when there are 0 RRsets in this message.
+ if there are RRsets, check those instead.
+
+ .. attribute:: security
+
+ The security status from DNSSEC validation of this message. See sec_status\_ predefined constants.
+
+ .. attribute:: an_numrrsets
+
+ Number of RRsets in each section.
+ The answer section. Add up the RRs in every RRset to calculate
+ the number of RRs, and the count for the dns packet.
+ The number of RRs in RRsets can change due to RRset updates.
+
+ .. attribute:: ns_numrrsets
+
+ Count of authority section RRsets
+
+ .. attribute:: ar_numrrsets
+
+ Count of additional section RRsets
+
+ .. attribute:: rrset_count
+
+ Number of RRsets: an_numrrsets + ns_numrrsets + ar_numrrsets
+
+ .. attribute:: rrsets[]
+
+ (:class:`ub_packed_rrset_key`) List of RR sets in the order in which they appear in the reply message.
+ Number of elements is ancount + nscount + arcount RRsets.
+
+ .. attribute:: ref[]
+
+ (:class:`rrset_ref`) Packed array of ids (see counts) and pointers to packed_rrset_key.
+ The number equals ancount + nscount + arcount RRsets.
+ These are sorted in ascending pointer, the locking order. So
+ this list can be locked (and id, ttl checked), to see if
+ all the data is available and recent enough.
+
+
+dns_msg
+--------------
+
+.. class:: dns_msg
+
+ Region allocated message reply
+
+ This class provides these data attributes:
+
+ .. attribute:: qinfo
+
+ (:class:`query_info`) Informations about query.
+
+ .. attribute:: rep
+
+ (:class:`reply_info`) This attribute points to the packed reply structure.
+
+
+packed_rrset_key
+----------------------
+
+.. class:: packed_rrset_key
+
+ The identifying information for an RRset.
+
+ This class provides these data attributes:
+
+ .. attribute:: dname
+
+ The domain name. If not empty (for ``id = None``) it is allocated, and
+ contains the wireformat domain name. This dname is not canonicalized.
+ E.g., the dname contains \\x03www\\x03nic\\x02cz\\x00 for www.nic.cz.
+
+ .. attribute:: dname_len
+
+ Length of the domain name, including last 0 root octet.
+
+ .. attribute:: dname_list[]
+
+ The domain name ``dname`` converted into list of labels (see :attr:`query_info.qname_list`).
+
+ .. attribute:: dname_str
+
+ The domain name ``dname`` converted into string (see :attr:`query_info.qname_str`).
+
+ .. attribute:: flags
+
+ Flags.
+
+ .. attribute:: type
+
+ The rrset type in network format.
+
+ .. attribute:: type_str
+
+ The rrset type in display presentation format.
+
+ .. attribute:: rrset_class
+
+ The rrset class in network format.
+
+ .. attribute:: rrset_class_str
+
+ The rrset class in display presentation format.
+
+ub_packed_rrset_key
+-------------------------
+
+.. class:: ub_packed_rrset_key
+
+ This structure contains an RRset. A set of resource records that
+ share the same domain name, type and class.
+ Due to memory management and threading, the key structure cannot be
+ deleted, although the data can be. The id can be set to 0 to store and the
+ structure can be recycled with a new id.
+
+ The :class:`ub_packed_rrset_key` provides these data attributes:
+
+ .. attribute:: entry
+
+ (:class:`lruhash_entry`) Entry into hashtable. Note the lock is never destroyed,
+ even when this key is retired to the cache.
+ the data pointer (if not None) points to a :class:`packed_rrset`.
+
+ .. attribute:: id
+
+ The ID of this rrset. unique, based on threadid + sequenceno.
+ ids are not reused, except after flushing the cache.
+ zero is an unused entry, and never a valid id.
+ Check this value after getting entry.lock.
+ The other values in this struct may only be altered after changing
+ the id (which needs a writelock on entry.lock).
+
+ .. attribute:: rk
+
+ (:class:`packed_rrset_key`) RR set data.
+
+
+lruhash_entry
+-------------------------
+
+.. class:: lruhash_entry
+
+ The :class:`ub_packed_rrset_key` provides these data attributes:
+
+ .. attribute:: lock
+
+ rwlock for access to the contents of the entry. Note that you cannot change hash and key, if so, you have to delete it to change hash or key.
+
+ .. attribute:: data
+
+ (:class:`packed_rrset_data`) entry data stored in wireformat (RRs and RRsigs).
+
+packed_rrset_data
+-----------------------
+
+.. class:: packed_rrset_data
+
+ Rdata is stored in wireformat. The dname is stored in wireformat.
+
+ TTLs are stored as absolute values (and could be expired).
+
+ RRSIGs are stored in the arrays after the regular rrs.
+
+ You need the packed_rrset_key to know dname, type, class of the
+ resource records in this RRset. (if signed the rrsig gives the type too).
+
+ The :class:`packed_rrset_data` provides these data attributes:
+
+ .. attribute:: ttl
+
+ TTL (in seconds like time()) of the RRset.
+ Same for all RRs see rfc2181(5.2).
+
+ .. attribute:: count
+
+ Number of RRs.
+
+ .. attribute:: rrsig_count
+
+ Number of rrsigs, if 0 no rrsigs.
+
+ .. attribute:: trust
+
+ The trustworthiness of the RRset data.
+
+ .. attribute:: security
+
+ Security status of the RRset data. See sec_status\_ predefined constants.
+
+ .. attribute:: rr_len[]
+
+ Length of every RR's rdata, rr_len[i] is size of rr_data[i].
+
+ .. attribute:: rr_ttl[]
+
+ TTL of every rr. rr_ttl[i] ttl of rr i.
+
+ .. attribute:: rr_data[]
+
+ Array of RR's rdata (list of strings). The rdata is stored in uncompressed wireformat.
+ The first 16B of rr_data[i] is rdlength in network format.
+
+
+DNSMessage
+----------------
+
+.. class:: DNSMessage
+
+ Abstract representation of DNS message.
+
+ **Usage**
+
+ This example shows how to create an authoritative answer response
+
+ ::
+
+ msg = DNSMessage(qstate.qinfo.qname_str, RR_TYPE_A, RR_CLASS_IN, PKT_AA)
+
+ #append RR
+ if (qstate.qinfo.qtype == RR_TYPE_A) or (qstate.qinfo.qtype == RR_TYPE_ANY):
+ msg.answer.append("%s 10 IN A 127.0.0.1" % qstate.qinfo.qname_str)
+
+ #set qstate.return_msg
+ if not msg.set_return_msg(qstate):
+ raise Exception("Can't create response")
+
+ The :class:`DNSMessage` provides these methods and data attributes:
+
+ .. method:: __init__(self, rr_name, rr_type, rr_class = RR_CLASS_IN, query_flags = 0, default_ttl = 0)
+
+ Prepares an answer (DNS packet) from qiven information. Query flags are combination of PKT_xx contants.
+
+ .. method:: set_return_msg(self, qstate)
+
+ This method fills qstate return message according to the given informations.
+ It takes lists of RRs in each section of answer, created necessray RRsets in wire format and store the result in :attr:`qstate.return_msg`.
+ Returns 1 if OK.
+
+ .. attribute:: rr_name
+
+ RR name of question.
+
+ .. attribute:: rr_type
+
+ RR type of question.
+
+ .. attribute:: rr_class
+
+ RR class of question.
+
+ .. attribute:: default_ttl
+
+ Default time-to-live.
+
+ .. attribute:: query_flags
+
+ Query flags. See PKT\_ predefined constants.
+
+ .. attribute:: question[]
+
+ List of resource records that should appear (in the same order) in question section of answer.
+
+ .. attribute:: answer[]
+
+ List of resource records that should appear (in the same order) in answer section of answer.
+
+ .. attribute:: authority[]
+
+ List of resource records that should appear (in the same order) in authority section of answer.
+
+ .. attribute:: additional[]
+
+ List of resource records that should appear (in the same order) in additional section of answer.
+
+pythonmod_env
+-----------------------
+
+.. class:: pythonmod_env
+
+ Global state for the module.
+
+ This class provides these data attributes:
+
+ .. attribute:: data
+
+ Here you can keep your own data shared across each thread.
+
+ .. attribute:: fname
+
+ Python script filename.
+
+ .. attribute:: qstate
+
+ Module query state.
+
+pythonmod_qstate
+-----------------------
+
+.. class:: pythonmod_qstate
+
+ Per query state for the iterator module.
+
+ This class provides these data attributes:
+
+ .. attribute:: data
+
+ Here you can keep your own private data (each thread has own data object).
+
diff --git a/external/unbound/pythonmod/doc/usecase.rst b/external/unbound/pythonmod/doc/usecase.rst
new file mode 100644
index 000000000..7a77349f1
--- /dev/null
+++ b/external/unbound/pythonmod/doc/usecase.rst
@@ -0,0 +1,38 @@
+Use cases (examples)
+====================
+
+Dynamic DNS Service discovery (DNS-SD_)
+-------------------------------------------
+Synchronized with database engine, for example *MySQL*.
+
+.. _DNS-SD: http://www.dns-sd.org/
+
+Firewall control
+----------------
+Control firewall (e.g. enable incomming SSH connection) with DNS query signed with private key.
+So firewall can blocks every service during normal operation.
+
+Scriptable DNS-based blacklist (DNS-BL_)
+-------------------------------------------
+Scripted in Python with already provided features, takes advantage of DNS reply, because
+almost every mail server supports DNS based blacklisting.
+
+.. _DNS-BL: http://www.dnsbl.org
+
+DNS based Wake-On-Lan
+---------------------
+Controled by secured queries secured with private key.
+
+Dynamic translation service
+---------------------------
+DNS request can be translated to virtualy any answer, that's easy to implement in client side
+because of many DNS libraries available.
+
+Examples :
+ * **Dictionary** - using *IDN* for non-ascii strings transfer, ``dig TXT slovo.en._dict_.nic.cz`` returns translation of "slovo" to EN.
+ * **Translation** - Extends *DNS-SD*, for example DNS to Jabber to find out people logged in.
+ * **Exchange rate calculator** - ``dig TXT 1000.99.czk.eur._rates_.nic.cz`` returns the given sum (1000.99 CZK) in EURs.
+
+Dynamic ENUM service
+--------------------
+Support for redirection, synchronization, etc.
diff --git a/external/unbound/pythonmod/examples/calc.py b/external/unbound/pythonmod/examples/calc.py
new file mode 100644
index 000000000..3230e37e3
--- /dev/null
+++ b/external/unbound/pythonmod/examples/calc.py
@@ -0,0 +1,77 @@
+# -*- coding: utf-8 -*-
+'''
+ calc.py: DNS-based calculator
+
+ 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.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+'''
+
+#Try: dig @localhost 1*25._calc_.cz.
+
+def init(id, cfg): return True
+def deinit(id): return True
+def inform_super(id, qstate, superqstate, qdata): return True
+
+def operate(id, event, qstate, qdata):
+
+ if (event == MODULE_EVENT_NEW) or (event == MODULE_EVENT_PASS):
+
+ if qstate.qinfo.qname_str.endswith("._calc_.cz."):
+ try:
+ res = eval(''.join(qstate.qinfo.qname_list[0:-3]))
+ except:
+ res = "exception"
+
+ msg = DNSMessage(qstate.qinfo.qname_str, RR_TYPE_TXT, RR_CLASS_IN, PKT_QR | PKT_RA | PKT_AA) #, 300)
+ msg.answer.append("%s 300 IN TXT \"%s\"" % (qstate.qinfo.qname_str,res))
+ 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
+
+ else:
+ #Pass on the unknown query to the iterator
+ qstate.ext_state[id] = MODULE_WAIT_MODULE
+ return True
+
+ elif event == MODULE_EVENT_MODDONE:
+ #the iterator has finished
+ qstate.ext_state[id] = MODULE_FINISHED
+ return True
+
+ log_err("pythonmod: Unknown event")
+ qstate.ext_state[id] = MODULE_ERROR
+ return True
+
diff --git a/external/unbound/pythonmod/examples/dict.py b/external/unbound/pythonmod/examples/dict.py
new file mode 100644
index 000000000..c8088a89c
--- /dev/null
+++ b/external/unbound/pythonmod/examples/dict.py
@@ -0,0 +1,121 @@
+# -*- coding: utf-8 -*-
+'''
+ calc.py: DNS-based czech-english dictionary
+
+ 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.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+'''
+import os
+cz_dict = {}
+en_dict = {}
+
+def init(id, cfg):
+ log_info("pythonmod: dict init")
+ f = open("examples/dict_data.txt", "r")
+ try:
+ for line in f:
+ if line.startswith('#'):
+ continue
+ itm = line.split("\t", 3)
+ if len(itm) < 2:
+ continue
+ en,cs = itm[0:2]
+
+ if not (cs in cz_dict):
+ cz_dict[cs] = [en] # [cs] = en
+ else:
+ cz_dict[cs].append(en) # [cs] = en
+
+ if not (en in en_dict):
+ en_dict[en] = [cs] # [en] = cs
+ else:
+ en_dict[en].append(cs) # [en] = cs
+
+ finally:
+ f.close()
+ return True
+
+def deinit(id):
+ log_info("pythonmod: dict deinit")
+ return True
+
+def operate(id, event, qstate, qdata):
+ if (event == MODULE_EVENT_NEW) or (event == MODULE_EVENT_PASS):
+
+ if qstate.qinfo.qname_str.endswith("._dict_.cz."):
+
+ aword = ' '.join(qstate.qinfo.qname_list[0:-4])
+ adict = qstate.qinfo.qname_list[-4]
+
+ log_info("pythonmod: dictionary look up; word:%s dict:%s" % (aword,adict))
+
+ words = []
+ if (adict == "en") and (aword in en_dict):
+ words = en_dict[aword] # EN -> CS
+ if (adict == "cs") and (aword in cz_dict):
+ words = cz_dict[aword] # CS -> EN
+
+ if len(words) and ((qstate.qinfo.qtype == RR_TYPE_TXT) or (qstate.qinfo.qtype == RR_TYPE_ANY)):
+
+ msg = DNSMessage(qstate.qinfo.qname_str, RR_TYPE_TXT, RR_CLASS_IN, PKT_RD | PKT_RA | PKT_AA)
+ 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
+
+ qstate.return_rcode = RCODE_NOERROR
+ qstate.ext_state[id] = MODULE_FINISHED
+ return True
+
+ else:
+ qstate.return_rcode = RCODE_SERVFAIL
+ qstate.ext_state[id] = MODULE_FINISHED
+ return True
+
+ else: #Pass on the unknown query to the iterator
+ qstate.ext_state[id] = MODULE_WAIT_MODULE
+ return True
+
+ elif event == MODULE_EVENT_MODDONE: #the iterator has finished
+ #we don't need modify result
+ qstate.ext_state[id] = MODULE_FINISHED
+ return True
+
+ log_err("pythonmod: Unknown event")
+ qstate.ext_state[id] = MODULE_ERROR
+ return True
+
+def inform_super(id, qstate, superqstate, qdata):
+ return True
+
diff --git a/external/unbound/pythonmod/examples/dict_data.txt b/external/unbound/pythonmod/examples/dict_data.txt
new file mode 100644
index 000000000..04cd3badf
--- /dev/null
+++ b/external/unbound/pythonmod/examples/dict_data.txt
@@ -0,0 +1,6 @@
+* * web
+computer poèítaèový adj: Zdenìk Bro¾
+computer poèítaè n:
+domain doména n: Zdenìk Bro¾
+query otazník n: Zdenìk Bro¾
+network sí» n: [it.] poèítaèová
diff --git a/external/unbound/pythonmod/examples/log.py b/external/unbound/pythonmod/examples/log.py
new file mode 100644
index 000000000..c17106b0f
--- /dev/null
+++ b/external/unbound/pythonmod/examples/log.py
@@ -0,0 +1,119 @@
+import os
+'''
+ calc.py: Response packet logger
+
+ 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.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+'''
+
+def dataHex(data, prefix=""):
+ """Converts binary string data to display representation form"""
+ res = ""
+ for i in range(0, (len(data)+15)/16):
+ res += "%s0x%02X | " % (prefix, i*16)
+ d = map(lambda x:ord(x), data[i*16:i*16+17])
+ for ch in d:
+ res += "%02X " % ch
+ for i in range(0,17-len(d)):
+ res += " "
+ res += "| "
+ for ch in d:
+ if (ch < 32) or (ch > 127):
+ res += ". "
+ else:
+ res += "%c " % ch
+ res += "\n"
+ return res
+
+def logDnsMsg(qstate):
+ """Logs response"""
+
+ r = qstate.return_msg.rep
+ q = qstate.return_msg.qinfo
+
+ print "-"*100
+ print("Query: %s, type: %s (%d), class: %s (%d) " % (
+ qstate.qinfo.qname_str, qstate.qinfo.qtype_str, qstate.qinfo.qtype,
+ qstate.qinfo.qclass_str, qstate.qinfo.qclass))
+ print "-"*100
+ print "Return reply :: flags: %04X, QDcount: %d, Security:%d, TTL=%d" % (r.flags, r.qdcount, r.security, r.ttl)
+ print " qinfo :: qname: %s %s, qtype: %s, qclass: %s" % (str(q.qname_list), q.qname_str, q.qtype_str, q.qclass_str)
+
+ if (r):
+ print "Reply:"
+ for i in range(0, r.rrset_count):
+ rr = r.rrsets[i]
+
+ rk = rr.rk
+ print i,":",rk.dname_list, rk.dname_str, "flags: %04X" % rk.flags,
+ print "type:",rk.type_str,"(%d)" % ntohs(rk.type), "class:",rk.rrset_class_str,"(%d)" % ntohs(rk.rrset_class)
+
+ d = rr.entry.data
+ for j in range(0,d.count+d.rrsig_count):
+ print " ",j,":","TTL=",d.rr_ttl[j],
+ if (j >= d.count): print "rrsig",
+ print
+ print dataHex(d.rr_data[j]," ")
+
+ print "-"*100
+
+def init(id, cfg):
+ log_info("pythonmod: init called, module id is %d port: %d script: %s" % (id, cfg.port, cfg.python_script))
+ return True
+
+def deinit(id):
+ log_info("pythonmod: deinit called, module id is %d" % id)
+ return True
+
+def inform_super(id, qstate, superqstate, qdata):
+ return True
+
+def operate(id, event, qstate, qdata):
+ log_info("pythonmod: operate called, id: %d, event:%s" % (id, strmodulevent(event)))
+
+ if (event == MODULE_EVENT_NEW) or (event == MODULE_EVENT_PASS):
+ #Pass on the new event to the iterator
+ qstate.ext_state[id] = MODULE_WAIT_MODULE
+ return True
+
+ if event == MODULE_EVENT_MODDONE:
+ #Iterator finished, show response (if any)
+
+ if (qstate.return_msg):
+ logDnsMsg(qstate)
+
+ qstate.ext_state[id] = MODULE_FINISHED
+ return True
+
+ qstate.ext_state[id] = MODULE_ERROR
+ return True
+
diff --git a/external/unbound/pythonmod/examples/resgen.py b/external/unbound/pythonmod/examples/resgen.py
new file mode 100644
index 000000000..804c0bd1d
--- /dev/null
+++ b/external/unbound/pythonmod/examples/resgen.py
@@ -0,0 +1,73 @@
+'''
+ resgen.py: This example shows how to generate authoritative response
+
+ 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.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+'''
+def init(id, cfg): return True
+
+def deinit(id): return True
+
+def inform_super(id, qstate, superqstate, qdata): return True
+
+def operate(id, event, qstate, qdata):
+ if (event == MODULE_EVENT_NEW) or (event == MODULE_EVENT_PASS):
+ if (qstate.qinfo.qname_str.endswith(".localdomain.")): #query name ends with localdomain
+ #create instance of DNS message (packet) with given parameters
+ msg = DNSMessage(qstate.qinfo.qname_str, RR_TYPE_A, RR_CLASS_IN, PKT_QR | PKT_RA | PKT_AA)
+ #append RR
+ if (qstate.qinfo.qtype == RR_TYPE_A) or (qstate.qinfo.qtype == RR_TYPE_ANY):
+ msg.answer.append("%s 10 IN A 127.0.0.1" % qstate.qinfo.qname_str)
+ #set qstate.return_msg
+ if not msg.set_return_msg(qstate):
+ qstate.ext_state[id] = MODULE_ERROR
+ return True
+
+ #we don't need validation, result is valid
+ qstate.return_msg.rep.security = 2
+
+ qstate.return_rcode = RCODE_NOERROR
+ qstate.ext_state[id] = MODULE_FINISHED
+ return True
+ else:
+ #pass the query to validator
+ qstate.ext_state[id] = MODULE_WAIT_MODULE
+ return True
+
+ if event == MODULE_EVENT_MODDONE:
+ log_info("pythonmod: iterator module done")
+ qstate.ext_state[id] = MODULE_FINISHED
+ return True
+
+ log_err("pythonmod: bad event")
+ qstate.ext_state[id] = MODULE_ERROR
+ return True
diff --git a/external/unbound/pythonmod/examples/resip.py b/external/unbound/pythonmod/examples/resip.py
new file mode 100644
index 000000000..6bcac7252
--- /dev/null
+++ b/external/unbound/pythonmod/examples/resip.py
@@ -0,0 +1,96 @@
+'''
+ resip.py: This example shows how to generate authoritative response
+ and how to find out the IP address of a client
+
+ 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.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ Usage:
+
+ dig @127.0.0.1 -t TXT what.is.my.ip.
+'''
+
+def init(id, cfg): return True
+
+def deinit(id): return True
+
+def inform_super(id, qstate, superqstate, qdata): return True
+
+def operate(id, event, qstate, qdata):
+ print "Operate", event,"state:",qstate
+
+ # Please note that if this module blocks, by moving to the validator
+ # to validate or iterator to lookup or spawn a subquery to look up,
+ # then, other incoming queries are queued up onto this module and
+ # all of them receive the same reply.
+ # You can inspect the cache.
+
+ if (event == MODULE_EVENT_NEW) or (event == MODULE_EVENT_PASS):
+ if (qstate.qinfo.qname_str.endswith("what.is.my.ip.")): #query name ends with localdomain
+ #create instance of DNS message (packet) with given parameters
+ msg = DNSMessage(qstate.qinfo.qname_str, RR_TYPE_TXT, RR_CLASS_IN, PKT_QR | PKT_RA | PKT_AA)
+ #append RR
+ if (qstate.qinfo.qtype == RR_TYPE_TXT) or (qstate.qinfo.qtype == RR_TYPE_ANY):
+ rl = qstate.mesh_info.reply_list
+ while (rl):
+ if rl.query_reply:
+ q = rl.query_reply
+ # The TTL of 0 is mandatory, otherwise it ends up in
+ # the cache, and is returned to other IP addresses.
+ msg.answer.append("%s 0 IN TXT \"%s %d (%s)\"" % (qstate.qinfo.qname_str, q.addr,q.port,q.family))
+ rl = rl.next
+
+ #set qstate.return_msg
+ if not msg.set_return_msg(qstate):
+ qstate.ext_state[id] = MODULE_ERROR
+ return True
+
+ #we don't need validation, result is valid
+ qstate.return_msg.rep.security = 2
+
+ qstate.return_rcode = RCODE_NOERROR
+ qstate.ext_state[id] = MODULE_FINISHED
+ return True
+ else:
+ #pass the query to validator
+ qstate.ext_state[id] = MODULE_WAIT_MODULE
+ return True
+
+ if event == MODULE_EVENT_MODDONE:
+ log_info("pythonmod: iterator module done")
+ qstate.ext_state[id] = MODULE_FINISHED
+ return True
+
+ log_err("pythonmod: bad event")
+ qstate.ext_state[id] = MODULE_ERROR
+ return True
diff --git a/external/unbound/pythonmod/examples/resmod.py b/external/unbound/pythonmod/examples/resmod.py
new file mode 100644
index 000000000..cf392e4da
--- /dev/null
+++ b/external/unbound/pythonmod/examples/resmod.py
@@ -0,0 +1,88 @@
+'''
+ resmod.py: This example shows how to modify the response from iterator
+
+ 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.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+'''
+
+def init(id, cfg): return True
+
+def deinit(id): return True
+
+def inform_super(id, qstate, superqstate, qdata): return True
+
+def setTTL(qstate, ttl):
+ """Updates return_msg TTL and the TTL of all the RRs"""
+ if qstate.return_msg:
+ qstate.return_msg.rep.ttl = ttl
+ if (qstate.return_msg.rep):
+ for i in range(0,qstate.return_msg.rep.rrset_count):
+ d = qstate.return_msg.rep.rrsets[i].entry.data
+ for j in range(0,d.count+d.rrsig_count):
+ d.rr_ttl[j] = ttl
+
+def operate(id, event, qstate, qdata):
+ if (event == MODULE_EVENT_NEW) or (event == MODULE_EVENT_PASS):
+ #pass the query to validator
+ qstate.ext_state[id] = MODULE_WAIT_MODULE
+ return True
+
+ if event == MODULE_EVENT_MODDONE:
+ log_info("pythonmod: iterator module done")
+
+ if not qstate.return_msg:
+ qstate.ext_state[id] = MODULE_FINISHED
+ return True
+
+ #modify the response
+
+ qdn = qstate.qinfo.qname_str
+ if qdn.endswith(".nic.cz."):
+ #invalidate response in cache added by iterator
+ #invalidateQueryInCache(qstate, qstate.return_msg.qinfo)
+
+ #modify TTL to 10 secs and store response in cache
+ #setTTL(qstate, 5)
+ #if not storeQueryInCache(qstate, qstate.return_msg.qinfo, qstate.return_msg.rep, 0):
+ # qstate.ext_state[id] = MODULE_ERROR
+ # return False
+
+ #modify TTL of response, which will be send to a) validator and then b) client
+ setTTL(qstate, 10)
+ qstate.return_rcode = RCODE_NOERROR
+
+ qstate.ext_state[id] = MODULE_FINISHED
+ return True
+
+ log_err("pythonmod: bad event")
+ qstate.ext_state[id] = MODULE_ERROR
+ return True
diff --git a/external/unbound/pythonmod/interface.i b/external/unbound/pythonmod/interface.i
new file mode 100644
index 000000000..4f1a25f21
--- /dev/null
+++ b/external/unbound/pythonmod/interface.i
@@ -0,0 +1,916 @@
+/*
+ * interface.i: unbound python module
+ */
+
+%module unboundmodule
+%{
+/**
+ * \file
+ * This is the interface between the unbound server and a python module
+ * called to perform operations on queries.
+ */
+ #include <sys/types.h>
+ #include <sys/socket.h>
+ #include <netinet/in.h>
+ #include <arpa/inet.h>
+ #include <stdarg.h>
+ #include "config.h"
+ #include "util/log.h"
+ #include "util/module.h"
+ #include "util/netevent.h"
+ #include "util/regional.h"
+ #include "util/config_file.h"
+ #include "util/data/msgreply.h"
+ #include "util/data/packed_rrset.h"
+ #include "util/data/dname.h"
+ #include "util/storage/lruhash.h"
+ #include "services/cache/dns.h"
+ #include "services/mesh.h"
+ #include "ldns/wire2str.h"
+ #include "ldns/str2wire.h"
+ #include "ldns/pkthdr.h"
+%}
+
+%include "stdint.i" // uint_16_t can be known type now
+
+%inline %{
+ //converts [len][data][len][data][0] string to a List of labels (PyStrings)
+ PyObject* GetNameAsLabelList(const char* name, int len) {
+ PyObject* list;
+ int cnt=0, i;
+
+ i = 0;
+ while (i < len) {
+ i += name[i] + 1;
+ cnt++;
+ }
+
+ list = PyList_New(cnt);
+ i = 0; cnt = 0;
+ while (i < len) {
+ PyList_SetItem(list, cnt, PyBytes_FromStringAndSize(name + i + 1, name[i]));
+ i += name[i] + 1;
+ cnt++;
+ }
+ return list;
+ }
+%}
+
+/* ************************************************************************************ *
+ Structure query_info
+ * ************************************************************************************ */
+/* Query info */
+%ignore query_info::qname;
+%ignore query_info::qname_len;
+
+
+struct query_info {
+ %immutable;
+ char* qname;
+ size_t qname_len;
+ uint16_t qtype;
+ uint16_t qclass;
+ %mutable;
+};
+
+%inline %{
+ 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_NULL = 10,
+ RR_TYPE_WKS = 11,
+ RR_TYPE_PTR = 12,
+ RR_TYPE_HINFO = 13,
+ RR_TYPE_MINFO = 14,
+ RR_TYPE_MX = 15,
+ RR_TYPE_TXT = 16,
+ RR_TYPE_RP = 17,
+ RR_TYPE_AFSDB = 18,
+ RR_TYPE_X25 = 19,
+ RR_TYPE_ISDN = 20,
+ RR_TYPE_RT = 21,
+ RR_TYPE_NSAP = 22,
+ RR_TYPE_NSAP_PTR = 23,
+ RR_TYPE_SIG = 24,
+ RR_TYPE_KEY = 25,
+ RR_TYPE_PX = 26,
+ RR_TYPE_GPOS = 27,
+ RR_TYPE_AAAA = 28,
+ RR_TYPE_LOC = 29,
+ RR_TYPE_NXT = 30,
+ RR_TYPE_EID = 31,
+ RR_TYPE_NIMLOC = 32,
+ RR_TYPE_SRV = 33,
+ RR_TYPE_ATMA = 34,
+ RR_TYPE_NAPTR = 35,
+ RR_TYPE_KX = 36,
+ RR_TYPE_CERT = 37,
+ RR_TYPE_A6 = 38,
+ RR_TYPE_DNAME = 39,
+ RR_TYPE_SINK = 40,
+ RR_TYPE_OPT = 41,
+ RR_TYPE_APL = 42,
+ RR_TYPE_DS = 43,
+ RR_TYPE_SSHFP = 44,
+ RR_TYPE_IPSECKEY = 45,
+ RR_TYPE_RRSIG = 46,
+ RR_TYPE_NSEC = 47,
+ RR_TYPE_DNSKEY = 48,
+ RR_TYPE_DHCID = 49,
+ RR_TYPE_NSEC3 = 50,
+ RR_TYPE_NSEC3PARAMS = 51,
+ RR_TYPE_UINFO = 100,
+ RR_TYPE_UID = 101,
+ RR_TYPE_GID = 102,
+ RR_TYPE_UNSPEC = 103,
+ RR_TYPE_TSIG = 250,
+ RR_TYPE_IXFR = 251,
+ RR_TYPE_AXFR = 252,
+ RR_TYPE_MAILB = 253,
+ RR_TYPE_MAILA = 254,
+ RR_TYPE_ANY = 255,
+ RR_TYPE_DLV = 32769,
+ };
+
+ 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);
+ }
+%}
+
+%inline %{
+ PyObject* dnameAsStr(const char* dname) {
+ char buf[LDNS_MAX_DOMAINLEN+1];
+ buf[0] = '\0';
+ dname_str((uint8_t*)dname, buf);
+ return PyString_FromString(buf);
+ }
+%}
+
+%extend query_info {
+ %pythoncode %{
+ def _get_qtype_str(self): return sldns_wire2str_type(self.qtype)
+ __swig_getmethods__["qtype_str"] = _get_qtype_str
+ if _newclass:qtype_str = _swig_property(_get_qtype_str)
+
+ def _get_qclass_str(self): return sldns_wire2str_class(self.qclass)
+ __swig_getmethods__["qclass_str"] = _get_qclass_str
+ if _newclass:qclass_str = _swig_property(_get_qclass_str)
+
+ __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)
+
+ def _get_qname_str(self): return dnameAsStr(self.qname)
+ __swig_getmethods__["qname_str"] = _get_qname_str
+ if _newclass:qname_str = _swig_property(_get_qname_str)
+ %}
+}
+
+/* ************************************************************************************ *
+ Structure packed_rrset_key
+ * ************************************************************************************ */
+%ignore packed_rrset_key::dname;
+%ignore packed_rrset_key::dname_len;
+
+/* RRsets */
+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
+ %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.
+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);
+ }
+%}
+
+%extend packed_rrset_key {
+ %pythoncode %{
+ def _get_type_str(self): return sldns_wire2str_type(_unboundmodule.ntohs(self.type))
+ __swig_getmethods__["type_str"] = _get_type_str
+ if _newclass:type_str = _swig_property(_get_type_str)
+
+ def _get_class_str(self): return sldns_wire2str_class(_unboundmodule.ntohs(self.rrset_class))
+ __swig_getmethods__["rrset_class_str"] = _get_class_str
+ if _newclass:rrset_class_str = _swig_property(_get_class_str)
+
+ __swig_getmethods__["dname"] = _unboundmodule._get_dname
+ if _newclass:dname = _swig_property(_unboundmodule._get_dname)
+
+ __swig_getmethods__["dname_list"] = _unboundmodule._get_dname_components
+ if _newclass:dname_list = _swig_property(_unboundmodule._get_dname_components)
+
+ def _get_dname_str(self): return dnameAsStr(self.dname)
+ __swig_getmethods__["dname_str"] = _get_dname_str
+ if _newclass:dname_str = _swig_property(_get_dname_str)
+ %}
+}
+
+#if defined(SWIGWORDSIZE64)
+typedef long int rrset_id_t;
+#else
+typedef long long int rrset_id_t;
+#endif
+
+struct ub_packed_rrset_key {
+ struct lruhash_entry entry;
+ rrset_id_t id;
+ struct packed_rrset_key rk;
+};
+
+struct lruhash_entry {
+ lock_rw_t lock;
+ struct lruhash_entry* overflow_next;
+ struct lruhash_entry* lru_next;
+ struct lruhash_entry* lru_prev;
+ hashvalue_t hash;
+ void* key;
+ struct packed_rrset_data* data;
+};
+
+%ignore packed_rrset_data::rr_len;
+%ignore packed_rrset_data::rr_ttl;
+%ignore packed_rrset_data::rr_data;
+
+struct packed_rrset_data {
+ uint32_t ttl; //TTL (in seconds like time())
+
+ size_t count; //number of rrs
+ size_t rrsig_count; //number of rrsigs
+
+ 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.
+};
+
+%pythoncode %{
+ class RRSetData_RRLen:
+ def __init__(self, obj): self.obj = obj
+ def __getitem__(self, index): return _unboundmodule._get_data_rr_len(self.obj, index)
+ def __len__(self): return obj.count + obj.rrsig_count
+ class RRSetData_RRTTL:
+ def __init__(self, obj): self.obj = obj
+ def __getitem__(self, index): return _unboundmodule._get_data_rr_ttl(self.obj, index)
+ def __setitem__(self, index, value): _unboundmodule._set_data_rr_ttl(self.obj, index, value)
+ def __len__(self): return obj.count + obj.rrsig_count
+ class RRSetData_RRData:
+ def __init__(self, obj): self.obj = obj
+ def __getitem__(self, index): return _unboundmodule._get_data_rr_data(self.obj, index)
+ def __len__(self): return obj.count + obj.rrsig_count
+%}
+
+%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)))
+ 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)))
+ 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)))
+ 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)))
+ return PyBytes_FromStringAndSize((char*)d->rr_data[idx],
+ d->rr_len[idx]);
+ return Py_None;
+ }
+%}
+
+%extend packed_rrset_data {
+ %pythoncode %{
+ def _get_data_rr_len(self): return RRSetData_RRLen(self)
+ __swig_getmethods__["rr_len"] = _get_data_rr_len
+ if _newclass:rr_len = _swig_property(_get_data_rr_len)
+ def _get_data_rr_ttl(self): return RRSetData_RRTTL(self)
+ __swig_getmethods__["rr_ttl"] =_get_data_rr_ttl
+ if _newclass:rr_len = _swig_property(_get_data_rr_ttl)
+ def _get_data_rr_data(self): return RRSetData_RRData(self)
+ __swig_getmethods__["rr_data"] = _get_data_rr_data
+ if _newclass:rr_len = _swig_property(_get_data_rr_data)
+ %}
+}
+
+/* ************************************************************************************ *
+ Structure reply_info
+ * ************************************************************************************ */
+/* Messages */
+%ignore reply_info::rrsets;
+%ignore reply_info::ref;
+
+struct reply_info {
+ uint16_t flags;
+ uint16_t qdcount;
+ uint32_t ttl;
+ uint32_t prefetch_ttl;
+
+ uint16_t authoritative;
+ enum sec_status security;
+
+ size_t an_numrrsets;
+ size_t ns_numrrsets;
+ size_t 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 {
+ struct ub_packed_rrset_key* key;
+ rrset_id_t id;
+};
+
+struct dns_msg {
+ struct query_info qinfo;
+ struct reply_info *rep;
+};
+
+%pythoncode %{
+ class ReplyInfo_RRSet:
+ def __init__(self, obj): self.obj = obj
+ def __getitem__(self, index): return _unboundmodule._rrset_rrsets_get(self.obj, index)
+ def __len__(self): return obj.rrset_count
+
+ class ReplyInfo_Ref:
+ def __init__(self, obj): self.obj = obj
+ def __getitem__(self, index): return _unboundmodule._rrset_ref_get(self.obj, index)
+ def __len__(self): return obj.rrset_count
+%}
+
+%inline %{
+ struct ub_packed_rrset_key* _rrset_rrsets_get(struct reply_info* r, int idx) {
+ if ((r != NULL) && (idx >= 0) && ((size_t)idx < r->rrset_count))
+ return r->rrsets[idx];
+ return NULL;
+ }
+
+ 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);
+ return &(r->ref[idx]);
+// return &(r->ref[idx]);
+ }
+//printf("_rrset_ref_get: NULL\n");
+ return NULL;
+ }
+%}
+
+%extend reply_info {
+ %pythoncode %{
+ def _rrset_ref_get(self): return ReplyInfo_Ref(self)
+ __swig_getmethods__["ref"] = _rrset_ref_get
+ if _newclass:ref = _swig_property(_rrset_ref_get)
+
+ def _rrset_rrsets_get(self): return ReplyInfo_RRSet(self)
+ __swig_getmethods__["rrsets"] = _rrset_rrsets_get
+ if _newclass:rrsets = _swig_property(_rrset_rrsets_get)
+ %}
+}
+
+/* ************************************************************************************ *
+ Structure mesh_state
+ * ************************************************************************************ */
+struct mesh_state {
+ struct mesh_reply* reply_list;
+};
+
+struct mesh_reply {
+ struct mesh_reply* next;
+ struct comm_reply query_reply;
+};
+
+struct comm_reply {
+
+};
+
+%inline %{
+
+ PyObject* _comm_reply_addr_get(struct comm_reply* reply) {
+ char dest[64];
+ reply_addr2str(reply, dest, 64);
+ if (dest[0] == 0)
+ return Py_None;
+ return PyString_FromString(dest);
+ }
+
+ PyObject* _comm_reply_family_get(struct comm_reply* reply) {
+
+ int af = (int)((struct sockaddr_in*) &(reply->addr))->sin_family;
+
+ switch(af) {
+ case AF_INET: return PyString_FromString("ip4");
+ case AF_INET6: return PyString_FromString("ip6");
+ case AF_UNIX: return PyString_FromString("unix");
+ }
+
+ return Py_None;
+ }
+
+ PyObject* _comm_reply_port_get(struct comm_reply* reply) {
+ uint16_t port;
+ port = ntohs(((struct sockaddr_in*)&(reply->addr))->sin_port);
+ return PyInt_FromLong(port);
+ }
+
+%}
+
+%extend comm_reply {
+ %pythoncode %{
+ def _addr_get(self): return _comm_reply_addr_get(self)
+ __swig_getmethods__["addr"] = _addr_get
+ if _newclass:addr = _swig_property(_addr_get)
+
+ def _port_get(self): return _comm_reply_port_get(self)
+ __swig_getmethods__["port"] = _port_get
+ if _newclass:port = _swig_property(_port_get)
+
+ def _family_get(self): return _comm_reply_family_get(self)
+ __swig_getmethods__["family"] = _family_get
+ if _newclass:family = _swig_property(_family_get)
+ %}
+}
+/* ************************************************************************************ *
+ Structure module_qstate
+ * ************************************************************************************ */
+%ignore module_qstate::ext_state;
+%ignore module_qstate::minfo;
+
+/* Query state */
+struct module_qstate {
+ struct query_info qinfo;
+ uint16_t query_flags; //See QF_BIT_xx constants
+ int is_priming;
+
+ struct comm_reply* reply;
+ struct dns_msg* return_msg;
+ int return_rcode;
+ struct regional* region; /* unwrapped */
+
+ int curmod;
+
+ enum module_ext_state ext_state[MAX_MODULE];
+ void* minfo[MAX_MODULE];
+
+ struct module_env* env; /* unwrapped */
+ struct mesh_state* mesh_info;
+};
+
+%constant int MODULE_COUNT = MAX_MODULE;
+
+%constant int QF_BIT_CD = 0x0010;
+%constant int QF_BIT_AD = 0x0020;
+%constant int QF_BIT_Z = 0x0040;
+%constant int QF_BIT_RA = 0x0080;
+%constant int QF_BIT_RD = 0x0100;
+%constant int QF_BIT_TC = 0x0200;
+%constant int QF_BIT_AA = 0x0400;
+%constant int QF_BIT_QR = 0x8000;
+
+%inline %{
+ enum enum_return_rcode {
+ RCODE_NOERROR = 0,
+ RCODE_FORMERR = 1,
+ RCODE_SERVFAIL = 2,
+ RCODE_NXDOMAIN = 3,
+ RCODE_NOTIMPL = 4,
+ RCODE_REFUSED = 5,
+ RCODE_YXDOMAIN = 6,
+ RCODE_YXRRSET = 7,
+ RCODE_NXRRSET = 8,
+ RCODE_NOTAUTH = 9,
+ RCODE_NOTZONE = 10
+ };
+%}
+
+%pythoncode %{
+ class ExtState:
+ def __init__(self, obj): self.obj = obj
+ def __str__(self):
+ return ", ".join([_unboundmodule.strextstate(_unboundmodule._ext_state_get(self.obj,a)) for a in range(0, _unboundmodule.MODULE_COUNT)])
+ 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
+%}
+
+%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;
+ }
+ }
+%}
+
+%extend module_qstate {
+ %pythoncode %{
+ def set_ext_state(self, id, state):
+ """Sets the ext state"""
+ _unboundmodule._ext_state_set(self, id, state)
+
+ 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)
+ %}
+}
+
+/* ************************************************************************************ *
+ Structure config_strlist
+ * ************************************************************************************ */
+struct config_strlist {
+ struct config_strlist* next;
+ char* str;
+};
+
+/* ************************************************************************************ *
+ Structure config_str2list
+ * ************************************************************************************ */
+struct config_str2list {
+ struct config_str2list* next;
+ char* str;
+ char* str2;
+};
+
+/* ************************************************************************************ *
+ Structure config_file
+ * ************************************************************************************ */
+struct config_file {
+ int verbosity;
+ int stat_interval;
+ int stat_cumulative;
+ int stat_extended;
+ int num_threads;
+ int port;
+ int do_ip4;
+ int do_ip6;
+ int do_udp;
+ int do_tcp;
+ int outgoing_num_ports;
+ size_t outgoing_num_tcp;
+ size_t incoming_num_tcp;
+ int* outgoing_avail_ports;
+ size_t msg_buffer_size;
+ size_t msg_cache_size;
+ size_t msg_cache_slabs;
+ size_t num_queries_per_thread;
+ size_t jostle_time;
+ size_t rrset_cache_size;
+ size_t rrset_cache_slabs;
+ int host_ttl;
+ size_t infra_cache_slabs;
+ size_t infra_cache_numhosts;
+ char* target_fetch_policy;
+ int if_automatic;
+ int num_ifs;
+ char **ifs;
+ int num_out_ifs;
+ char **out_ifs;
+ struct config_strlist* root_hints;
+ struct config_stub* stubs;
+ struct config_stub* forwards;
+ struct config_strlist* donotqueryaddrs;
+ struct config_str2list* acls;
+ int donotquery_localhost;
+ int harden_short_bufsize;
+ int harden_large_queries;
+ int harden_glue;
+ int harden_dnssec_stripped;
+ int harden_referral_path;
+ int use_caps_bits_for_id;
+ struct config_strlist* private_address;
+ struct config_strlist* private_domain;
+ size_t unwanted_threshold;
+ char* chrootdir;
+ char* username;
+ char* directory;
+ char* logfile;
+ char* pidfile;
+ int use_syslog;
+ int hide_identity;
+ int hide_version;
+ char* identity;
+ char* version;
+ char* module_conf;
+ struct config_strlist* trust_anchor_file_list;
+ struct config_strlist* trust_anchor_list;
+ struct config_strlist* trusted_keys_file_list;
+ char* dlv_anchor_file;
+ struct config_strlist* dlv_anchor_list;
+ int max_ttl;
+ int32_t val_date_override;
+ int bogus_ttl;
+ int val_clean_additional;
+ int val_permissive_mode;
+ char* val_nsec3_key_iterations;
+ size_t key_cache_size;
+ size_t key_cache_slabs;
+ size_t neg_cache_size;
+ struct config_str2list* local_zones;
+ struct config_strlist* local_zones_nodefault;
+ struct config_strlist* local_data;
+ int remote_control_enable;
+ struct config_strlist* control_ifs;
+ int control_port;
+ char* server_key_file;
+ char* server_cert_file;
+ char* control_key_file;
+ char* control_cert_file;
+ int do_daemonize;
+ char* python_script;
+};
+
+/* ************************************************************************************ *
+ Enums
+ * ************************************************************************************ */
+%rename ("MODULE_STATE_INITIAL") "module_state_initial";
+%rename ("MODULE_WAIT_REPLY") "module_wait_reply";
+%rename ("MODULE_WAIT_MODULE") "module_wait_module";
+%rename ("MODULE_WAIT_SUBQUERY") "module_wait_subquery";
+%rename ("MODULE_ERROR") "module_error";
+%rename ("MODULE_FINISHED") "module_finished";
+
+enum module_ext_state {
+ module_state_initial = 0,
+ module_wait_reply,
+ module_wait_module,
+ module_wait_subquery,
+ module_error,
+ module_finished
+};
+
+%rename ("MODULE_EVENT_NEW") "module_event_new";
+%rename ("MODULE_EVENT_PASS") "module_event_pass";
+%rename ("MODULE_EVENT_REPLY") "module_event_reply";
+%rename ("MODULE_EVENT_NOREPLY") "module_event_noreply";
+%rename ("MODULE_EVENT_CAPSFAIL") "module_event_capsfail";
+%rename ("MODULE_EVENT_MODDONE") "module_event_moddone";
+%rename ("MODULE_EVENT_ERROR") "module_event_error";
+
+enum module_ev {
+ module_event_new = 0,
+ module_event_pass,
+ module_event_reply,
+ module_event_noreply,
+ module_event_capsfail,
+ module_event_moddone,
+ module_event_error
+};
+
+enum sec_status {
+ sec_status_unchecked = 0,
+ sec_status_bogus,
+ sec_status_indeterminate,
+ sec_status_insecure,
+ sec_status_secure
+};
+
+enum verbosity_value {
+ NO_VERBOSE = 0,
+ VERB_OPS,
+ VERB_DETAIL,
+ VERB_QUERY,
+ VERB_ALGO
+};
+
+%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 */
+%constant uint16_t PKT_RD = 8; /* Recursion Desired - query flag */
+%constant uint16_t PKT_CD = 16; /* Checking Disabled - query flag */
+%constant uint16_t PKT_RA = 32; /* Recursion Available - server flag */
+%constant uint16_t PKT_AD = 64; /* Authenticated Data - server flag */
+
+%{
+int checkList(PyObject *l)
+{
+ PyObject* item;
+ int i;
+
+ if (l == Py_None)
+ return 1;
+
+ if (PyList_Check(l))
+ {
+ for (i=0; i < PyList_Size(l); i++)
+ {
+ item = PyList_GetItem(l, i);
+ if (!PyString_Check(item))
+ return 0;
+ }
+ return 1;
+ }
+
+ return 0;
+}
+
+int pushRRList(sldns_buffer* qb, PyObject *l, uint32_t default_ttl, int qsec,
+ size_t count_offset)
+{
+ PyObject* item;
+ int i;
+ size_t len;
+
+ for (i=0; i < PyList_Size(l); i++)
+ {
+ item = PyList_GetItem(l, i);
+
+ len = sldns_buffer_remaining(qb);
+ if(qsec) {
+ if(sldns_str2wire_rr_question_buf(PyString_AsString(item),
+ sldns_buffer_current(qb), &len, NULL, NULL, 0, NULL, 0)
+ != 0)
+ return 0;
+ } else {
+ if(sldns_str2wire_rr_buf(PyString_AsString(item),
+ sldns_buffer_current(qb), &len, NULL, default_ttl,
+ NULL, 0, NULL, 0) != 0)
+ return 0;
+ }
+ sldns_buffer_skip(qb, len);
+
+ sldns_buffer_write_u16_at(qb, count_offset,
+ sldns_buffer_read_u16_at(qb, count_offset)+1);
+ }
+ return 1;
+}
+
+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)
+{
+ sldns_buffer *qb = 0;
+ int res = 1;
+ size_t l;
+ uint16_t PKT_QR = 1;
+ uint16_t PKT_AA = 2;
+ uint16_t PKT_TC = 4;
+ uint16_t PKT_RD = 8;
+ 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;
+
+ /* write header */
+ sldns_buffer_write_u16(qb, 0); /* ID */
+ sldns_buffer_write_u16(qb, 0); /* flags */
+ sldns_buffer_write_u16(qb, 1); /* qdcount */
+ sldns_buffer_write_u16(qb, 0); /* ancount */
+ sldns_buffer_write_u16(qb, 0); /* nscount */
+ sldns_buffer_write_u16(qb, 0); /* arcount */
+ if ((flags&PKT_QR)) LDNS_QR_SET(sldns_buffer_begin(qb));
+ if ((flags&PKT_AA)) LDNS_AA_SET(sldns_buffer_begin(qb));
+ if ((flags&PKT_TC)) LDNS_TC_SET(sldns_buffer_begin(qb));
+ if ((flags&PKT_RD)) LDNS_RD_SET(sldns_buffer_begin(qb));
+ if ((flags&PKT_CD)) LDNS_CD_SET(sldns_buffer_begin(qb));
+ if ((flags&PKT_RA)) LDNS_RA_SET(sldns_buffer_begin(qb));
+ if ((flags&PKT_AD)) LDNS_AD_SET(sldns_buffer_begin(qb));
+
+ /* write the query */
+ l = sldns_buffer_remaining(qb);
+ if(sldns_str2wire_dname_buf(rr_name, sldns_buffer_current(qb), &l) != 0) {
+ sldns_buffer_free(qb);
+ return 0;
+ }
+ sldns_buffer_skip(qb, l);
+ if (rr_type == 0) { rr_type = LDNS_RR_TYPE_A; }
+ if (rr_class == 0) { rr_class = LDNS_RR_CLASS_IN; }
+ sldns_buffer_write_u16(qb, rr_type);
+ sldns_buffer_write_u16(qb, rr_class);
+
+ /* write RR sections */
+ if(res && !pushRRList(qb, question, default_ttl, 1, LDNS_QDCOUNT_OFF))
+ res = 0;
+ if(res && !pushRRList(qb, answer, default_ttl, 0, LDNS_ANCOUNT_OFF))
+ res = 0;
+ if(res && !pushRRList(qb, authority, default_ttl, 0, LDNS_NSCOUNT_OFF))
+ res = 0;
+ if(res && !pushRRList(qb, additional, default_ttl, 0, LDNS_ARCOUNT_OFF))
+ res = 0;
+
+ if (res) res = createResponse(qstate, qb);
+
+ if (qb) sldns_buffer_free(qb);
+ return res;
+}
+%}
+
+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);
+
+%pythoncode %{
+ class DNSMessage:
+ def __init__(self, rr_name, rr_type, rr_class = RR_CLASS_IN, query_flags = 0, default_ttl = 0):
+ """Query flags is a combination of PKT_xx contants"""
+ self.rr_name = rr_name
+ self.rr_type = rr_type
+ self.rr_class = rr_class
+ self.default_ttl = default_ttl
+ self.query_flags = query_flags
+ self.question = []
+ self.answer = []
+ self.authority = []
+ self.additional = []
+
+ 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,
+ 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
+
+%}
+/* ************************************************************************************ *
+ 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, ...);
+void log_warn(const char* format, ...);
+void log_hex(const char* msg, void* data, size_t length);
+void log_dns_msg(const char* str, struct query_info* qinfo, struct reply_info* rep);
+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
+%typemap(newfree, noblock = 1) char * {
+ free($1);
+}
+
+// Mark as source returning newly allocated memory
+%newobject sldns_wire2str_type;
+%newobject sldns_wire2str_class;
+
+// LDNS functions
+char *sldns_wire2str_type(const uint16_t atype);
+char *sldns_wire2str_class(const uint16_t aclass);
+
+// 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
+const char* strextstate(enum module_ext_state s);
+const char* strmodulevent(enum module_ev e);
+
diff --git a/external/unbound/pythonmod/pythonmod.c b/external/unbound/pythonmod/pythonmod.c
new file mode 100644
index 000000000..359eea0c6
--- /dev/null
+++ b/external/unbound/pythonmod/pythonmod.c
@@ -0,0 +1,384 @@
+/*
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * \file
+ * Python module for unbound. Calls python script.
+ */
+
+/* ignore the varargs unused warning from SWIGs internal vararg support */
+#ifdef __GNUC__
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
+#endif
+
+#include "config.h"
+#include "ldns/sbuffer.h"
+
+#undef _POSIX_C_SOURCE
+#undef _XOPEN_SOURCE
+#include <Python.h>
+
+#include "pythonmod/pythonmod.h"
+#include "util/module.h"
+#include "util/config_file.h"
+#include "pythonmod_utils.h"
+
+#ifdef S_SPLINT_S
+typedef struct PyObject PyObject;
+typedef struct PyThreadState PyThreadState;
+typedef void* PyGILState_STATE;
+#endif
+
+/**
+ * Global state for the module.
+ */
+struct pythonmod_env {
+
+ /** Python script filename. */
+ const char* fname;
+
+ /** Python main thread */
+ PyThreadState* mainthr;
+ /** Python module. */
+ PyObject* module;
+
+ /** Module init function */
+ PyObject* func_init;
+ /** Module deinit function */
+ PyObject* func_deinit;
+ /** Module operate function */
+ PyObject* func_operate;
+ /** Module super_inform function */
+ PyObject* func_inform;
+
+ /** Python dictionary. */
+ PyObject* dict;
+
+ /** Module data. */
+ PyObject* data;
+
+ /** Module qstate. */
+ struct module_qstate* qstate;
+};
+
+/**
+ * Per query state for the iterator module.
+ */
+struct pythonmod_qstate {
+
+ /** Module per query data. */
+ PyObject* data;
+};
+
+/* Generated */
+#ifndef S_SPLINT_S
+#include "pythonmod/interface.h"
+#endif
+
+int pythonmod_init(struct module_env* env, int id)
+{
+ /* Initialize module */
+ FILE* script_py = NULL;
+ PyObject* py_cfg, *res;
+ PyGILState_STATE gil;
+ struct pythonmod_env* pe = (struct pythonmod_env*)calloc(1, sizeof(struct pythonmod_env));
+ if (!pe)
+ {
+ log_err("pythonmod: malloc failure");
+ return 0;
+ }
+
+ env->modinfo[id] = (void*) pe;
+
+ /* Initialize module */
+ pe->fname = env->cfg->python_script;
+ if(pe->fname==NULL || pe->fname[0]==0) {
+ log_err("pythonmod: no script given.");
+ return 0;
+ }
+
+ /* Initialize Python libraries */
+ if (!Py_IsInitialized())
+ {
+ Py_SetProgramName("unbound");
+ Py_NoSiteFlag = 1;
+ Py_Initialize();
+ PyEval_InitThreads();
+ SWIG_init();
+ pe->mainthr = PyEval_SaveThread();
+ }
+
+ gil = PyGILState_Ensure();
+
+ /* Initialize Python */
+ 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);
+ }
+ PyRun_SimpleString("sys.path.append('"RUN_DIR"') \n");
+ PyRun_SimpleString("sys.path.append('"SHARE_DIR"') \n");
+ PyRun_SimpleString("import distutils.sysconfig \n");
+ 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");
+ PyGILState_Release(gil);
+ return 0;
+ }
+
+ /* Check Python file load */
+ if ((script_py = fopen(pe->fname, "r")) == NULL)
+ {
+ log_err("pythonmod: can't open file %s for reading", pe->fname);
+ PyGILState_Release(gil);
+ return 0;
+ }
+
+ /* Load file */
+ pe->module = PyImport_AddModule("__main__");
+ pe->dict = PyModule_GetDict(pe->module);
+ pe->data = Py_None;
+ Py_INCREF(pe->data);
+ PyModule_AddObject(pe->module, "mod_env", pe->data);
+
+ /* TODO: deallocation of pe->... if an error occurs */
+
+ if (PyRun_SimpleFile(script_py, pe->fname) < 0)
+ {
+ log_err("pythonmod: can't parse Python script %s", pe->fname);
+ PyGILState_Release(gil);
+ return 0;
+ }
+
+ fclose(script_py);
+
+ 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)
+ {
+ 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)
+ {
+ 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)
+ {
+ 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())
+ {
+ log_err("pythonmod: Exception occurred in function init");
+ PyErr_Print();
+ }
+
+ Py_XDECREF(res);
+ Py_XDECREF(py_cfg);
+ PyGILState_Release(gil);
+
+ return 1;
+}
+
+void pythonmod_deinit(struct module_env* env, int id)
+{
+ struct pythonmod_env* pe = env->modinfo[id];
+ if(pe == NULL)
+ return;
+
+ /* Free Python resources */
+ if(pe->module != NULL)
+ {
+ PyObject* res;
+ PyGILState_STATE gil = PyGILState_Ensure();
+
+ /* Deinit module */
+ res = PyObject_CallFunction(pe->func_deinit, "i", id);
+ if (PyErr_Occurred()) {
+ log_err("pythonmod: Exception occurred in function deinit");
+ PyErr_Print();
+ }
+ /* Free result if any */
+ Py_XDECREF(res);
+ /* Free shared data if any */
+ Py_XDECREF(pe->data);
+ PyGILState_Release(gil);
+
+ PyEval_RestoreThread(pe->mainthr);
+ Py_Finalize();
+ pe->mainthr = NULL;
+ }
+ pe->fname = NULL;
+ free(pe);
+
+ /* Module is deallocated in Python */
+ env->modinfo[id] = NULL;
+}
+
+void pythonmod_inform_super(struct module_qstate* qstate, int id, struct module_qstate* super)
+{
+ struct pythonmod_env* pe = (struct pythonmod_env*)qstate->env->modinfo[id];
+ struct pythonmod_qstate* pq = (struct pythonmod_qstate*)qstate->minfo[id];
+ PyObject* py_qstate, *py_sqstate, *res;
+ PyGILState_STATE gil = PyGILState_Ensure();
+
+ log_query_info(VERB_ALGO, "pythonmod: inform_super, sub is", &qstate->qinfo);
+ log_query_info(VERB_ALGO, "super is", &super->qinfo);
+
+ 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,
+ py_sqstate, pq->data);
+
+ 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)))
+ {
+ log_err("pythonmod: python returned bad code in inform_super");
+ qstate->ext_state[id] = module_error;
+ }
+
+ Py_XDECREF(res);
+ Py_XDECREF(py_sqstate);
+ Py_XDECREF(py_qstate);
+
+ PyGILState_Release(gil);
+}
+
+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];
+ struct pythonmod_qstate* pq = (struct pythonmod_qstate*)qstate->minfo[id];
+ PyObject* py_qstate, *res;
+ 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);
+ }
+
+ /* Call operate */
+ py_qstate = SWIG_NewPointerObj((void*) qstate, SWIGTYPE_p_module_qstate, 0);
+ res = PyObject_CallFunction(pe->func_operate, "iiOO", id, (int) event,
+ py_qstate, pq->data);
+ 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)))
+ {
+ log_err("pythonmod: python returned bad code, event: %s", strmodulevent(event));
+ qstate->ext_state[id] = module_error;
+ }
+ Py_XDECREF(res);
+ Py_XDECREF(py_qstate);
+
+ PyGILState_Release(gil);
+}
+
+void pythonmod_clear(struct module_qstate* qstate, int id)
+{
+ struct pythonmod_qstate* pq;
+ if (qstate == NULL)
+ return;
+
+ pq = (struct pythonmod_qstate*)qstate->minfo[id];
+ verbose(VERB_ALGO, "pythonmod: clear, id: %d, pq:%lX", id,
+ (unsigned long int)pq);
+ if(pq != NULL)
+ {
+ PyGILState_STATE gil = PyGILState_Ensure();
+ Py_DECREF(pq->data);
+ PyGILState_Release(gil);
+ /* Free qstate */
+ free(pq);
+ }
+
+ qstate->minfo[id] = NULL;
+}
+
+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,
+ (unsigned long int)pe);
+ if(!pe)
+ return 0;
+ return sizeof(*pe);
+}
+
+/**
+ * The module function block
+ */
+static struct module_func_block pythonmod_block = {
+ "python",
+ &pythonmod_init, &pythonmod_deinit, &pythonmod_operate, &pythonmod_inform_super,
+ &pythonmod_clear, &pythonmod_get_mem
+};
+
+struct module_func_block* pythonmod_get_funcblock(void)
+{
+ return &pythonmod_block;
+}
diff --git a/external/unbound/pythonmod/pythonmod.h b/external/unbound/pythonmod/pythonmod.h
new file mode 100644
index 000000000..b108cf923
--- /dev/null
+++ b/external/unbound/pythonmod/pythonmod.h
@@ -0,0 +1,68 @@
+/*
+ * pythonmod.h: module header file
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * \file
+ * Python module for unbound. Calls python script.
+ */
+#ifndef PYTHONMOD_H
+#define PYTHONMOD_H
+#include "util/module.h"
+#include "services/outbound_list.h"
+
+/**
+ * Get the module function block.
+ * @return: function block with function pointers to module methods.
+ */
+struct module_func_block* pythonmod_get_funcblock(void);
+
+/** python module init */
+int pythonmod_init(struct module_env* env, int id);
+
+/** python module deinit */
+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);
+
+/** python module */
+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);
+#endif /* PYTHONMOD_H */
diff --git a/external/unbound/pythonmod/pythonmod_utils.c b/external/unbound/pythonmod/pythonmod_utils.c
new file mode 100644
index 000000000..2f3848008
--- /dev/null
+++ b/external/unbound/pythonmod/pythonmod_utils.c
@@ -0,0 +1,178 @@
+/*
+ * pythonmod_utils.c: utilities used by 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * \file
+ * Utility functions for the python module that perform stores and loads and
+ * conversions.
+ */
+#include "config.h"
+#include "util/module.h"
+#include "util/netevent.h"
+#include "util/net_help.h"
+#include "services/cache/dns.h"
+#include "services/cache/rrset.h"
+#include "util/data/msgparse.h"
+#include "util/data/msgreply.h"
+#include "util/storage/slabhash.h"
+#include "util/regional.h"
+#include "ldns/sbuffer.h"
+
+#undef _POSIX_C_SOURCE
+#undef _XOPEN_SOURCE
+#include <Python.h>
+
+/* Store the reply_info and query_info pair in message cache (qstate->msg_cache) */
+int storeQueryInCache(struct module_qstate* qstate, struct query_info* qinfo, struct reply_info* msgrep, int is_referral)
+{
+ if (!msgrep)
+ return 0;
+
+ if (msgrep->authoritative) /*authoritative answer can't be stored in cache*/
+ {
+ PyErr_SetString(PyExc_ValueError, "Authoritative answer can't be stored");
+ return 0;
+ }
+
+ return dns_cache_store(qstate->env, qinfo, msgrep, is_referral,
+ qstate->prefetch_leeway, 0, NULL);
+}
+
+/* Invalidate the message associated with query_info stored in message cache */
+void invalidateQueryInCache(struct module_qstate* qstate, struct query_info* qinfo)
+{
+ hashvalue_t h;
+ struct lruhash_entry* e;
+ struct reply_info *r;
+ size_t i, j;
+
+ h = query_info_hash(qinfo);
+ if ((e=slabhash_lookup(qstate->env->msg_cache, h, qinfo, 0)))
+ {
+ r = (struct reply_info*)(e->data);
+ if (r)
+ {
+ r->ttl = 0;
+ if(rrset_array_lock(r->ref, r->rrset_count, *qstate->env->now)) {
+ for(i=0; i< r->rrset_count; i++)
+ {
+ struct packed_rrset_data* data =
+ (struct packed_rrset_data*) r->ref[i].key->entry.data;
+ if(i>0 && r->ref[i].key == r->ref[i-1].key)
+ continue;
+
+ data->ttl = r->ttl;
+ for(j=0; j<data->count + data->rrsig_count; j++)
+ data->rr_ttl[j] = r->ttl;
+ }
+ rrset_array_unlock(r->ref, r->rrset_count);
+ }
+ }
+ lock_rw_unlock(&e->lock);
+ } else {
+ log_info("invalidateQueryInCache: qinfo is not in cache");
+ }
+}
+
+/* Create response according to the ldns packet content */
+int createResponse(struct module_qstate* qstate, sldns_buffer* pkt)
+{
+ struct msg_parse* prs;
+ struct edns_data edns;
+
+ /* parse message */
+ prs = (struct msg_parse*) regional_alloc(qstate->env->scratch, sizeof(struct msg_parse));
+ if (!prs) {
+ log_err("storeResponse: out of memory on incoming message");
+ return 0;
+ }
+
+ memset(prs, 0, sizeof(*prs));
+ memset(&edns, 0, sizeof(edns));
+
+ sldns_buffer_set_position(pkt, 0);
+ if (parse_packet(pkt, prs, qstate->env->scratch) != LDNS_RCODE_NOERROR) {
+ verbose(VERB_ALGO, "storeResponse: parse error on reply packet");
+ return 0;
+ }
+ /* edns is not examined, but removed from message to help cache */
+ if(parse_extract_edns(prs, &edns) != LDNS_RCODE_NOERROR)
+ return 0;
+
+ /* remove CD-bit, we asked for in case we handle validation ourself */
+ prs->flags &= ~BIT_CD;
+
+ /* allocate response dns_msg in region */
+ qstate->return_msg = (struct dns_msg*)regional_alloc(qstate->region, sizeof(struct dns_msg));
+ if (!qstate->return_msg)
+ return 0;
+
+ memset(qstate->return_msg, 0, sizeof(*qstate->return_msg));
+ if(!parse_create_msg(pkt, prs, NULL, &(qstate->return_msg)->qinfo, &(qstate->return_msg)->rep, qstate->region)) {
+ log_err("storeResponse: malloc failure: allocating incoming dns_msg");
+ return 0;
+ }
+
+ /* Make sure that the RA flag is set (since the presence of
+ * this module means that recursion is available) */
+ /* qstate->return_msg->rep->flags |= BIT_RA; */
+
+ /* Clear the AA flag */
+ /* FIXME: does this action go here or in some other module? */
+ /*qstate->return_msg->rep->flags &= ~BIT_AA; */
+
+ /* make sure QR flag is on */
+ /*qstate->return_msg->rep->flags |= BIT_QR; */
+
+ if(verbosity >= VERB_ALGO)
+ log_dns_msg("storeResponse: packet:", &qstate->return_msg->qinfo, qstate->return_msg->rep);
+
+ return 1;
+}
+
+
+/* Convert reply->addr to string */
+void reply_addr2str(struct comm_reply* reply, char* dest, int maxlen)
+{
+ int af = (int)((struct sockaddr_in*) &(reply->addr))->sin_family;
+ void* sinaddr = &((struct sockaddr_in*) &(reply->addr))->sin_addr;
+
+ if(af == AF_INET6)
+ sinaddr = &((struct sockaddr_in6*)&(reply->addr))->sin6_addr;
+ dest[0] = 0;
+ if (inet_ntop(af, sinaddr, dest, (socklen_t)maxlen) == 0)
+ return;
+ dest[maxlen-1] = 0;
+}
diff --git a/external/unbound/pythonmod/pythonmod_utils.h b/external/unbound/pythonmod/pythonmod_utils.h
new file mode 100644
index 000000000..a901f391a
--- /dev/null
+++ b/external/unbound/pythonmod/pythonmod_utils.h
@@ -0,0 +1,89 @@
+/*
+ * pythonmod_utils.h: utils header file
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * \file
+ * Utility functions for the python module that perform stores and loads and
+ * conversions.
+ */
+#ifndef PYTHONMOD_UTILS_H
+#define PYTHONMOD_UTILS_H
+
+#include "util/module.h"
+
+/**
+ * Store the reply_info and query_info pair in message cache (qstate->msg_cache)
+ *
+ * @param qstate: module environment
+ * @param qinfo: query info, the query for which answer is stored.
+ * @param msgrep: reply in dns_msg
+ * @param is_referral: If true, then the given message to be stored is a
+ * referral. The cache implementation may use this as a hint.
+ * It will store only the RRsets, not the message.
+ * @return 0 on alloc error (out of memory).
+ */
+int storeQueryInCache(struct module_qstate* qstate, struct query_info* qinfo, struct reply_info* msgrep, int is_referral);
+
+
+/**
+ * Invalidate the message associated with query_info stored in message cache.
+ *
+ * This function invalidates the record in message cache associated with the given query only if such a record exists.
+ *
+ * @param qstate: module environment
+ * @param qinfo: query info, the query for which answer is stored.
+ */
+void invalidateQueryInCache(struct module_qstate* qstate, struct query_info* qinfo);
+
+/**
+ * Create response according to the ldns packet content
+ *
+ * This function fills qstate.return_msg up with data of a given packet
+ *
+ * @param qstate: module environment
+ * @param pkt: a sldns_buffer which contains sldns_packet data
+ * @return 0 on failure, out of memory or parse error.
+ */
+int createResponse(struct module_qstate* qstate, sldns_buffer* pkt);
+
+/**
+ * Convert reply->addr to string
+ * @param reply: comm reply with address in it.
+ * @param dest: destination string.
+ * @param maxlen: length of string buffer.
+ */
+void reply_addr2str(struct comm_reply* reply, char* dest, int maxlen);
+
+#endif /* PYTHONMOD_UTILS_H */
diff --git a/external/unbound/pythonmod/test-calc.conf b/external/unbound/pythonmod/test-calc.conf
new file mode 100644
index 000000000..ef854ce1b
--- /dev/null
+++ b/external/unbound/pythonmod/test-calc.conf
@@ -0,0 +1,18 @@
+# Example configuration file for calc.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/calc.py"
+
diff --git a/external/unbound/pythonmod/test-dict.conf b/external/unbound/pythonmod/test-dict.conf
new file mode 100644
index 000000000..daab7250e
--- /dev/null
+++ b/external/unbound/pythonmod/test-dict.conf
@@ -0,0 +1,18 @@
+# Example configuration file for dict.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/dict.py"
+
diff --git a/external/unbound/pythonmod/test-log.conf b/external/unbound/pythonmod/test-log.conf
new file mode 100644
index 000000000..02214ba1d
--- /dev/null
+++ b/external/unbound/pythonmod/test-log.conf
@@ -0,0 +1,17 @@
+# Example configuration file for log.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/log.py"
diff --git a/external/unbound/pythonmod/test-resgen.conf b/external/unbound/pythonmod/test-resgen.conf
new file mode 100644
index 000000000..a0a79e42d
--- /dev/null
+++ b/external/unbound/pythonmod/test-resgen.conf
@@ -0,0 +1,18 @@
+# Example configuration file for resgen.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/resgen.py"
+
diff --git a/external/unbound/pythonmod/test-resip.conf b/external/unbound/pythonmod/test-resip.conf
new file mode 100644
index 000000000..7620f736f
--- /dev/null
+++ b/external/unbound/pythonmod/test-resip.conf
@@ -0,0 +1,18 @@
+# Example configuration file for resip.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/resip.py"
+
diff --git a/external/unbound/pythonmod/test-resmod.conf b/external/unbound/pythonmod/test-resmod.conf
new file mode 100644
index 000000000..8de5fd257
--- /dev/null
+++ b/external/unbound/pythonmod/test-resmod.conf
@@ -0,0 +1,19 @@
+# Example configuration file for resmod.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: "python iterator"
+ module-config: "validator python iterator"
+
+# Python config section
+python:
+ # Script file to load
+ python-script: "./examples/resmod.py"
+
diff --git a/external/unbound/pythonmod/ubmodule-msg.py b/external/unbound/pythonmod/ubmodule-msg.py
new file mode 100644
index 000000000..648368080
--- /dev/null
+++ b/external/unbound/pythonmod/ubmodule-msg.py
@@ -0,0 +1,156 @@
+# -*- coding: utf-8 -*-
+'''
+ ubmodule-msg.py: simple response packet logger
+
+ Authors: Zdenek Vasicek (vasicek AT fit.vutbr.cz)
+ Marek Vavrusa (xvavru00 AT stud.fit.vutbr.cz)
+
+ Copyright (c) 2008. All rights reserved.
+
+ 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.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+'''
+import os
+
+def init(id, cfg):
+ log_info("pythonmod: init called, module id is %d port: %d script: %s" % (id, cfg.port, cfg.python_script))
+ return True
+
+def deinit(id):
+ log_info("pythonmod: deinit called, module id is %d" % id)
+ return True
+
+def inform_super(id, qstate, superqstate, qdata):
+ return True
+
+def setTTL(qstate, ttl):
+ """Sets return_msg TTL and all the RRs TTL"""
+ if qstate.return_msg:
+ qstate.return_msg.rep.ttl = ttl
+ if (qstate.return_msg.rep):
+ for i in range(0,qstate.return_msg.rep.rrset_count):
+ d = qstate.return_msg.rep.rrsets[i].entry.data
+ for j in range(0,d.count+d.rrsig_count):
+ d.rr_ttl[j] = ttl
+
+def dataHex(data, prefix=""):
+ res = ""
+ for i in range(0, (len(data)+15)/16):
+ res += "%s0x%02X | " % (prefix, i*16)
+ d = map(lambda x:ord(x), data[i*16:i*16+17])
+ for ch in d:
+ res += "%02X " % ch
+ for i in range(0,17-len(d)):
+ res += " "
+ res += "| "
+ for ch in d:
+ if (ch < 32) or (ch > 127):
+ res += ". "
+ else:
+ res += "%c " % ch
+ res += "\n"
+ return res
+
+def printReturnMsg(qstate):
+ print "Return MSG rep :: flags: %04X, QDcount: %d, Security:%d, TTL=%d" % (qstate.return_msg.rep.flags, qstate.return_msg.rep.qdcount,qstate.return_msg.rep.security, qstate.return_msg.rep.ttl)
+ print " qinfo :: qname:",qstate.return_msg.qinfo.qname_list, qstate.return_msg.qinfo.qname_str, "type:",qstate.return_msg.qinfo.qtype_str, "class:",qstate.return_msg.qinfo.qclass_str
+ if (qstate.return_msg.rep):
+ print "RRSets:",qstate.return_msg.rep.rrset_count
+ prevkey = None
+ for i in range(0,qstate.return_msg.rep.rrset_count):
+ r = qstate.return_msg.rep.rrsets[i]
+ rk = r.rk
+ print i,":",rk.dname_list, rk.dname_str, "flags: %04X" % rk.flags,
+ print "type:",rk.type_str,"(%d)" % ntohs(rk.type), "class:",rk.rrset_class_str,"(%d)" % ntohs(rk.rrset_class)
+
+ d = r.entry.data
+ print " RRDatas:",d.count+d.rrsig_count
+ for j in range(0,d.count+d.rrsig_count):
+ print " ",j,":","TTL=",d.rr_ttl[j],"RR data:"
+ print dataHex(d.rr_data[j]," ")
+
+
+def operate(id, event, qstate, qdata):
+ log_info("pythonmod: operate called, id: %d, event:%s" % (id, strmodulevent(event)))
+ #print "pythonmod: per query data", qdata
+
+ print "Query:", ''.join(map(lambda x:chr(max(32,ord(x))),qstate.qinfo.qname)), qstate.qinfo.qname_list, qstate.qinfo.qname_str,
+ print "Type:",qstate.qinfo.qtype_str,"(%d)" % qstate.qinfo.qtype,
+ print "Class:",qstate.qinfo.qclass_str,"(%d)" % qstate.qinfo.qclass
+ print
+
+ #if event == MODULE_EVENT_PASS: #pokud mame "validator python iterator"
+ if (event == MODULE_EVENT_NEW) and (qstate.qinfo.qname_str.endswith(".seznam.cz.")): #pokud mame "python validator iterator"
+ print qstate.qinfo.qname_str
+
+ qstate.ext_state[id] = MODULE_FINISHED
+
+ msg = DNSMessage(qstate.qinfo.qname_str, RR_TYPE_A, RR_CLASS_IN, PKT_QR | PKT_RA | PKT_AA) #, 300)
+ #msg.authority.append("xxx.seznam.cz. 10 IN A 192.168.1.1")
+ #msg.additional.append("yyy.seznam.cz. 10 IN A 1.1.1.2.")
+
+ if qstate.qinfo.qtype == RR_TYPE_A:
+ msg.answer.append("%s 10 IN A 192.168.1.1" % qstate.qinfo.qname_str)
+ if (qstate.qinfo.qtype == RR_TYPE_SRV) or (qstate.qinfo.qtype == RR_TYPE_ANY):
+ msg.answer.append("%s 10 IN SRV 0 0 80 neinfo.example.com." % qstate.qinfo.qname_str)
+ if (qstate.qinfo.qtype == RR_TYPE_TXT) or (qstate.qinfo.qtype == RR_TYPE_ANY):
+ msg.answer.append("%s 10 IN TXT path=/" % qstate.qinfo.qname_str)
+
+ if not msg.set_return_msg(qstate):
+ qstate.ext_state[id] = MODULE_ERROR
+ return True
+
+ #qstate.return_msg.rep.security = 2 #pokud nebude nasledovat validator, je zapotrebi nastavit security, aby nebyl paket zahozen v mesh_send_reply
+ printReturnMsg(qstate)
+
+ #Authoritative result can't be stored in cache
+ #if (not storeQueryInCache(qstate, qstate.return_msg.qinfo, qstate.return_msg.rep, 0)):
+ # print "Can't store in cache"
+ # qstate.ext_state[id] = MODULE_ERROR
+ # return False
+ #print "Store OK"
+
+ qstate.return_rcode = RCODE_NOERROR
+ return True
+
+ if event == MODULE_EVENT_NEW:
+ qstate.ext_state[id] = MODULE_WAIT_MODULE
+ return True
+
+ if event == MODULE_EVENT_MODDONE:
+ log_info("pythonmod: previous module done")
+ qstate.ext_state[id] = MODULE_FINISHED
+ return True
+
+ if event == MODULE_EVENT_PASS:
+ log_info("pythonmod: event_pass")
+ qstate.ext_state[id] = MODULE_WAIT_MODULE
+ return True
+
+ log_err("pythonmod: BAD event")
+ qstate.ext_state[id] = MODULE_ERROR
+ return True
+
+log_info("pythonmod: script loaded.")
diff --git a/external/unbound/pythonmod/ubmodule-tst.py b/external/unbound/pythonmod/ubmodule-tst.py
new file mode 100644
index 000000000..0b9b5a9d2
--- /dev/null
+++ b/external/unbound/pythonmod/ubmodule-tst.py
@@ -0,0 +1,149 @@
+# -*- coding: utf-8 -*-
+'''
+ ubmodule-tst.py:
+
+ Authors: Zdenek Vasicek (vasicek AT fit.vutbr.cz)
+ Marek Vavrusa (xvavru00 AT stud.fit.vutbr.cz)
+
+ Copyright (c) 2008. All rights reserved.
+
+ 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.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+'''
+def init(id, cfg):
+ log_info("pythonmod: init called, module id is %d port: %d script: %s" % (id, cfg.port, cfg.python_script))
+ return True
+
+def deinit(id):
+ log_info("pythonmod: deinit called, module id is %d" % id)
+ return True
+
+def inform_super(id, qstate, superqstate, qdata):
+ return True
+
+def setTTL(qstate, ttl):
+ """Sets return_msg TTL and all the RRs TTL"""
+ if qstate.return_msg:
+ qstate.return_msg.rep.ttl = ttl
+ if (qstate.return_msg.rep):
+ for i in range(0,qstate.return_msg.rep.rrset_count):
+ d = qstate.return_msg.rep.rrsets[i].entry.data
+ for j in range(0,d.count+d.rrsig_count):
+ d.rr_ttl[j] = ttl
+
+def dataHex(data, prefix=""):
+ res = ""
+ for i in range(0, (len(data)+15)/16):
+ res += "%s0x%02X | " % (prefix, i*16)
+ d = map(lambda x:ord(x), data[i*16:i*16+17])
+ for ch in d:
+ res += "%02X " % ch
+ for i in range(0,17-len(d)):
+ res += " "
+ res += "| "
+ for ch in d:
+ if (ch < 32) or (ch > 127):
+ res += ". "
+ else:
+ res += "%c " % ch
+ res += "\n"
+ return res
+
+def printReturnMsg(qstate):
+ print "Return MSG rep :: flags: %04X, QDcount: %d, Security:%d, TTL=%d" % (qstate.return_msg.rep.flags, qstate.return_msg.rep.qdcount,qstate.return_msg.rep.security, qstate.return_msg.rep.ttl)
+ print " qinfo :: qname:",qstate.return_msg.qinfo.qname_list, qstate.return_msg.qinfo.qname_str, "type:",qstate.return_msg.qinfo.qtype_str, "class:",qstate.return_msg.qinfo.qclass_str
+ if (qstate.return_msg.rep):
+ print "RRSets:",qstate.return_msg.rep.rrset_count
+ prevkey = None
+ for i in range(0,qstate.return_msg.rep.rrset_count):
+ r = qstate.return_msg.rep.rrsets[i]
+ rk = r.rk
+ print i,":",rk.dname_list, rk.dname_str, "flags: %04X" % rk.flags,
+ print "type:",rk.type_str,"(%d)" % ntohs(rk.type), "class:",rk.rrset_class_str,"(%d)" % ntohs(rk.rrset_class)
+
+ d = r.entry.data
+ print " RRDatas:",d.count+d.rrsig_count
+ for j in range(0,d.count+d.rrsig_count):
+ print " ",j,":","TTL=",d.rr_ttl[j],"RR data:"
+ print dataHex(d.rr_data[j]," ")
+
+def operate(id, event, qstate, qdata):
+ log_info("pythonmod: operate called, id: %d, event:%s" % (id, strmodulevent(event)))
+ #print "pythonmod: per query data", qdata
+
+ print "Query:", ''.join(map(lambda x:chr(max(32,ord(x))),qstate.qinfo.qname)), qstate.qinfo.qname_list,
+ print "Type:",qstate.qinfo.qtype_str,"(%d)" % qstate.qinfo.qtype,
+ print "Class:",qstate.qinfo.qclass_str,"(%d)" % qstate.qinfo.qclass
+ print
+
+ # TEST:
+ # > dig @127.0.0.1 www.seznam.cz A
+ # > dig @127.0.0.1 3.76.75.77.in-addr.arpa. PTR
+ # prvni dva dotazy vrati TTL 100
+ # > dig @127.0.0.1 www.seznam.cz A
+ # > dig @127.0.0.1 3.76.75.77.in-addr.arpa. PTR
+ # dalsi dva dotazy vrati TTL 10, ktere se bude s dalsimi dotazy snizovat, nez vyprsi a znovu se zaktivuje mesh
+
+ if qstate.return_msg:
+ printReturnMsg(qstate)
+
+ #qdn = '.'.join(qstate.qinfo.qname_list)
+ qdn = qstate.qinfo.qname_str
+
+ #Pokud dotaz konci na nasledujici, pozmenime TTL zpravy, ktera se posle klientovi (return_msg) i zpravy v CACHE
+ if qdn.endswith(".seznam.cz.") or qdn.endswith('.in-addr.arpa.'):
+ #pokud je v cache odpoved z iteratoru, pak ji zneplatnime, jinak se moduly nazavolaji do te doby, nez vyprsi TTL
+ invalidateQueryInCache(qstate, qstate.return_msg.qinfo)
+
+ if (qstate.return_msg.rep.authoritative):
+ print "X"*300
+
+ setTTL(qstate, 10) #do cache nastavime TTL na 10
+ if not storeQueryInCache(qstate, qstate.return_msg.qinfo, qstate.return_msg.rep, 0):
+ qstate.ext_state[id] = MODULE_ERROR
+ return False
+
+ setTTL(qstate, 100) #odpoved klientovi prijde s TTL 100
+ qstate.return_rcode = RCODE_NOERROR
+
+ if event == MODULE_EVENT_NEW:
+ qstate.ext_state[id] = MODULE_WAIT_MODULE
+ return True
+
+ if event == MODULE_EVENT_MODDONE:
+ log_info("pythonmod: previous module done")
+ qstate.ext_state[id] = MODULE_FINISHED
+ return True
+
+ if event == MODULE_EVENT_PASS:
+ log_info("pythonmod: event_pass")
+ qstate.ext_state[id] = MODULE_WAIT_MODULE
+ return True
+
+ log_err("pythonmod: BAD event")
+ qstate.ext_state[id] = MODULE_ERROR
+ return True
+
+log_info("pythonmod: script loaded.")