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 module_event_moddone). In the end, when the python module is done, validator is called.

Note that the python module is called with 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 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:

init(id, cfg)

Initialize module internals, like database etc. Called just once on module load.

Parameters:
  • id – module identifier (integer)
  • cfg

    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
deinit(id)

Deinitialize module internals. Called just once on module unload.

Parameters:
  • id – module identifier (integer)
def deinit(id):
   log_info("pythonmod: deinit called, module id is %d" % id)
   return True
inform_super(id, qstate, superqstate, qdata)

Inform super querystate about the results from this subquerystate. Is called when the querystate is finished.

Parameters:
def inform_super(id, qstate, superqstate, qdata):
   return True
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.

Parameters:
  • id – module identifier (integer)
  • qstatemodule_qstate query state structure
  • qdataquery_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

print mod_env.fname   # Print module script name
mod_env.data = "test" # Store global module data

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.")

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.