Home | History | Annotate | Line # | Download | only in examples
      1      1.1  christos # -*- coding: utf-8 -*-
      2      1.1  christos '''
      3      1.1  christos  edns.py: python module showcasing EDNS option functionality.
      4      1.1  christos 
      5      1.1  christos  Copyright (c) 2016, NLnet Labs.
      6      1.1  christos 
      7      1.1  christos  This software is open source.
      8      1.1  christos  
      9      1.1  christos  Redistribution and use in source and binary forms, with or without
     10      1.1  christos  modification, are permitted provided that the following conditions
     11      1.1  christos  are met:
     12      1.1  christos  
     13      1.1  christos     * Redistributions of source code must retain the above copyright notice,
     14      1.1  christos       this list of conditions and the following disclaimer.
     15      1.1  christos  
     16      1.1  christos     * Redistributions in binary form must reproduce the above copyright notice,
     17      1.1  christos       this list of conditions and the following disclaimer in the documentation
     18      1.1  christos       and/or other materials provided with the distribution.
     19      1.1  christos  
     20      1.1  christos     * Neither the name of the organization nor the names of its
     21      1.1  christos       contributors may be used to endorse or promote products derived from this
     22      1.1  christos       software without specific prior written permission.
     23      1.1  christos 
     24      1.1  christos  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     25      1.1  christos  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     26      1.1  christos  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     27      1.1  christos  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
     28      1.1  christos  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     29      1.1  christos  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     30      1.1  christos  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     31      1.1  christos  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     32      1.1  christos  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     33      1.1  christos  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     34      1.1  christos  POSSIBILITY OF SUCH DAMAGE.
     35      1.1  christos '''
     36      1.1  christos #Try:
     37      1.1  christos # - dig @localhost nlnetlabs.nl +ednsopt=65001:c001
     38      1.1  christos #       This query will always reach the modules stage as EDNS option 65001 is
     39      1.1  christos #       registered to bypass the cache response stage. It will also be handled
     40      1.1  christos #       as a unique query because of the no_aggregation flag. This means that
     41      1.1  christos #       it will not be aggregated with other queries for the same qinfo.
     42      1.1  christos #       For demonstration purposes when option 65001 with hexdata 'c001' is
     43      1.1  christos #       sent from the client side this module will reply with the same code and
     44      1.1  christos #       data 'deadbeef'.
     45      1.1  christos 
     46      1.1  christos # Useful functions:
     47      1.1  christos #   edns_opt_list_is_empty(edns_opt_list):
     48      1.1  christos #       Check if the option list is empty.
     49      1.1  christos #       Return True if empty, False otherwise.
     50      1.1  christos #
     51      1.1  christos #   edns_opt_list_append(edns_opt_list, code, data_bytearray, region): 
     52      1.1  christos #       Append the EDNS option with code and data_bytearray to the given
     53      1.1  christos #           edns_opt_list.
     54      1.1  christos #       NOTE: data_bytearray MUST be a Python bytearray.
     55      1.1  christos #       Return True on success, False on failure.
     56      1.1  christos #
     57      1.1  christos #   edns_opt_list_remove(edns_opt_list, code):
     58  1.1.1.3  christos #       Remove all occurrences of the given EDNS option code from the
     59      1.1  christos #           edns_opt_list.
     60      1.1  christos #       Return True when at least one EDNS option was removed, False otherwise.
     61      1.1  christos #
     62      1.1  christos #   register_edns_option(env, code, bypass_cache_stage=True,
     63      1.1  christos #                        no_aggregation=True):
     64      1.1  christos #       Register EDNS option code as a known EDNS option.
     65      1.1  christos #       bypass_cache_stage:
     66      1.1  christos #           bypasses answering from cache and allows the query to reach the
     67      1.1  christos #           modules for further EDNS handling.
     68      1.1  christos #       no_aggregation:
     69      1.1  christos #           makes every query with the said EDNS option code unique.
     70      1.1  christos #       Return True on success, False on failure.
     71      1.1  christos #
     72      1.1  christos # Examples on how to use the functions are given in this file.
     73      1.1  christos 
     74      1.1  christos 
     75      1.1  christos def init_standard(id, env):
     76      1.1  christos     """New version of the init function.
     77      1.1  christos     The function's signature is the same as the C counterpart and allows for
     78      1.1  christos     extra functionality during init.
     79      1.1  christos     ..note:: This function is preferred by unbound over the old init function.
     80      1.1  christos     ..note:: The previously accessible configuration options can now be found in
     81  1.1.1.2  christos              env.cfg.
     82      1.1  christos     """
     83  1.1.1.4  christos     log_info("python: inited script {}".format(mod_env['script']))
     84      1.1  christos 
     85      1.1  christos     # Register EDNS option 65001 as a known EDNS option.
     86      1.1  christos     if not register_edns_option(env, 65001, bypass_cache_stage=True,
     87      1.1  christos                                 no_aggregation=True):
     88      1.1  christos         return False
     89      1.1  christos 
     90      1.1  christos     return True
     91      1.1  christos 
     92      1.1  christos 
     93      1.1  christos def init(id, cfg):
     94      1.1  christos     """Previous version init function.
     95      1.1  christos     ..note:: This function is still supported for backwards compatibility when
     96      1.1  christos              the init_standard function is missing. When init_standard is
     97      1.1  christos              present this function SHOULD be omitted to avoid confusion to the
     98      1.1  christos              reader.
     99      1.1  christos     """
    100      1.1  christos     return True
    101      1.1  christos 
    102      1.1  christos 
    103      1.1  christos def deinit(id): return True
    104      1.1  christos 
    105      1.1  christos 
    106      1.1  christos def inform_super(id, qstate, superqstate, qdata): return True
    107      1.1  christos 
    108      1.1  christos 
    109      1.1  christos def operate(id, event, qstate, qdata):
    110      1.1  christos     if (event == MODULE_EVENT_NEW) or (event == MODULE_EVENT_PASS):
    111      1.1  christos         # Detect if EDNS option code 56001 is present from the client side. If
    112      1.1  christos         # so turn on the flags for cache management.
    113      1.1  christos         if not edns_opt_list_is_empty(qstate.edns_opts_front_in):
    114      1.1  christos             log_info("python: searching for EDNS option code 65001 during NEW "
    115      1.1  christos                      "or PASS event ")
    116      1.1  christos             for o in qstate.edns_opts_front_in_iter:
    117      1.1  christos                 if o.code == 65001:
    118      1.1  christos                     log_info("python: found EDNS option code 65001")
    119      1.1  christos                     # Instruct other modules to not lookup for an
    120      1.1  christos                     # answer in the cache.
    121      1.1  christos                     qstate.no_cache_lookup = 1
    122      1.1  christos                     log_info("python: enabled no_cache_lookup")
    123      1.1  christos 
    124      1.1  christos                     # Instruct other modules to not store the answer in
    125      1.1  christos                     # the cache.
    126      1.1  christos                     qstate.no_cache_store = 1
    127      1.1  christos                     log_info("python: enabled no_cache_store")
    128      1.1  christos 
    129      1.1  christos         #Pass on the query
    130      1.1  christos         qstate.ext_state[id] = MODULE_WAIT_MODULE 
    131      1.1  christos         return True
    132      1.1  christos 
    133      1.1  christos     elif event == MODULE_EVENT_MODDONE:
    134      1.1  christos         # If the client sent EDNS option code 65001 and data 'c001' reply
    135      1.1  christos         # with the same code and data 'deadbeef'.
    136      1.1  christos         if not edns_opt_list_is_empty(qstate.edns_opts_front_in):
    137      1.1  christos             log_info("python: searching for EDNS option code 65001 during "
    138      1.1  christos                      "MODDONE")
    139      1.1  christos             for o in qstate.edns_opts_front_in_iter:
    140      1.1  christos                 if o.code == 65001 and o.data == bytearray.fromhex("c001"):
    141      1.1  christos                     b = bytearray.fromhex("deadbeef")
    142      1.1  christos                     if not edns_opt_list_append(qstate.edns_opts_front_out,
    143      1.1  christos                                            o.code, b, qstate.region):
    144      1.1  christos                         qstate.ext_state[id] = MODULE_ERROR
    145      1.1  christos                         return False
    146      1.1  christos 
    147      1.1  christos         # List every EDNS option in all lists.
    148      1.1  christos         # The available lists are:
    149      1.1  christos         #   - qstate.edns_opts_front_in:  EDNS options that came from the
    150      1.1  christos         #                                 client side. SHOULD NOT be changed;
    151      1.1  christos         #
    152      1.1  christos         #   - qstate.edns_opts_back_out:  EDNS options that will be sent to the
    153      1.1  christos         #                                 server side. Can be populated by
    154      1.1  christos         #                                 EDNS literate modules;
    155      1.1  christos         #
    156      1.1  christos         #   - qstate.edns_opts_back_in:   EDNS options that came from the
    157      1.1  christos         #                                 server side. SHOULD NOT be changed;
    158      1.1  christos         #
    159      1.1  christos         #   - qstate.edns_opts_front_out: EDNS options that will be sent to the
    160      1.1  christos         #                                 client side. Can be populated by
    161      1.1  christos         #                                 EDNS literate modules;
    162      1.1  christos         #
    163      1.1  christos         # The lists' contents can be accessed in python by their _iter
    164      1.1  christos         # counterpart as an iterator.
    165      1.1  christos         if not edns_opt_list_is_empty(qstate.edns_opts_front_in):
    166      1.1  christos             log_info("python: EDNS options in edns_opts_front_in:")
    167      1.1  christos             for o in qstate.edns_opts_front_in_iter:
    168      1.1  christos                 log_info("python:    Code: {}, Data: '{}'".format(o.code,
    169      1.1  christos                                 "".join('{:02x}'.format(x) for x in o.data)))
    170      1.1  christos 
    171      1.1  christos         if not edns_opt_list_is_empty(qstate.edns_opts_back_out):
    172      1.1  christos             log_info("python: EDNS options in edns_opts_back_out:")
    173      1.1  christos             for o in qstate.edns_opts_back_out_iter:
    174      1.1  christos                 log_info("python:    Code: {}, Data: '{}'".format(o.code,
    175      1.1  christos                                 "".join('{:02x}'.format(x) for x in o.data)))
    176      1.1  christos 
    177      1.1  christos         if not edns_opt_list_is_empty(qstate.edns_opts_back_in):
    178      1.1  christos             log_info("python: EDNS options in edns_opts_back_in:")
    179      1.1  christos             for o in qstate.edns_opts_back_in_iter:
    180      1.1  christos                 log_info("python:    Code: {}, Data: '{}'".format(o.code,
    181      1.1  christos                                 "".join('{:02x}'.format(x) for x in o.data)))
    182      1.1  christos 
    183      1.1  christos         if not edns_opt_list_is_empty(qstate.edns_opts_front_out):
    184      1.1  christos             log_info("python: EDNS options in edns_opts_front_out:")
    185      1.1  christos             for o in qstate.edns_opts_front_out_iter:
    186      1.1  christos                 log_info("python:    Code: {}, Data: '{}'".format(o.code,
    187      1.1  christos                                 "".join('{:02x}'.format(x) for x in o.data)))
    188      1.1  christos 
    189      1.1  christos         qstate.ext_state[id] = MODULE_FINISHED
    190      1.1  christos         return True
    191      1.1  christos 
    192      1.1  christos     log_err("pythonmod: Unknown event")
    193      1.1  christos     qstate.ext_state[id] = MODULE_ERROR
    194      1.1  christos     return True
    195