Basics

This basic example shows how to create simple python module which will pass on the requests to the iterator.

How to get pythonmod working

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. To get the pythonmod works use this string:

module-config: "validator python iterator"

As soon as the DNS query arrive, the unbound call the validator module (it is the first module in the list). The validator does not know the answer (it can validate only), thus it will pass on the event to the next module. Next module is pythonmod 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. In the end, when the python module is done, validator is called.

In order to change the scripts, a configuration option is provided. When unbound starts and initializes the modules, the pythonmod loads the script which name is given in python-script option:

python-script: "/unbound/test/ubmodule.py"

Basic python module step by step

Each python module 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 – 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 mesh.

Parameters:
  • id – module identifier (integer)
  • qstate – Query state
  • superqstate – Mesh state
  • qdata – Query data
def inform_super(id, qstate, superqstate, qdata):
   return True
operate(id, event, qstate, qdata)

Perform action on pending query. It’s basically a state machine (MODULE_EVENT_NEW, MODULE_EVENT_PASS, MODULE_EVENT_MODDONE), which can be extended for your needs. Result is stored in ext_state under module identifier.

Parameters:
  • id – module identifier (integer)
  • qstate – Query state
  • qdata – Query 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:
      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_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: 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_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.