10cd9f4ecSchristos# -*- coding: utf-8 -*- 20cd9f4ecSchristos''' 30cd9f4ecSchristos edns.py: python module showcasing EDNS option functionality. 40cd9f4ecSchristos 50cd9f4ecSchristos Copyright (c) 2016, NLnet Labs. 60cd9f4ecSchristos 70cd9f4ecSchristos This software is open source. 80cd9f4ecSchristos 90cd9f4ecSchristos Redistribution and use in source and binary forms, with or without 100cd9f4ecSchristos modification, are permitted provided that the following conditions 110cd9f4ecSchristos are met: 120cd9f4ecSchristos 130cd9f4ecSchristos * Redistributions of source code must retain the above copyright notice, 140cd9f4ecSchristos this list of conditions and the following disclaimer. 150cd9f4ecSchristos 160cd9f4ecSchristos * Redistributions in binary form must reproduce the above copyright notice, 170cd9f4ecSchristos this list of conditions and the following disclaimer in the documentation 180cd9f4ecSchristos and/or other materials provided with the distribution. 190cd9f4ecSchristos 200cd9f4ecSchristos * Neither the name of the organization nor the names of its 210cd9f4ecSchristos contributors may be used to endorse or promote products derived from this 220cd9f4ecSchristos software without specific prior written permission. 230cd9f4ecSchristos 240cd9f4ecSchristos THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 250cd9f4ecSchristos "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 260cd9f4ecSchristos TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 270cd9f4ecSchristos PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE 280cd9f4ecSchristos LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 290cd9f4ecSchristos CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 300cd9f4ecSchristos SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 310cd9f4ecSchristos INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 320cd9f4ecSchristos CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 330cd9f4ecSchristos ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 340cd9f4ecSchristos POSSIBILITY OF SUCH DAMAGE. 350cd9f4ecSchristos''' 360cd9f4ecSchristos#Try: 370cd9f4ecSchristos# - dig @localhost nlnetlabs.nl +ednsopt=65001:c001 380cd9f4ecSchristos# This query will always reach the modules stage as EDNS option 65001 is 390cd9f4ecSchristos# registered to bypass the cache response stage. It will also be handled 400cd9f4ecSchristos# as a unique query because of the no_aggregation flag. This means that 410cd9f4ecSchristos# it will not be aggregated with other queries for the same qinfo. 420cd9f4ecSchristos# For demonstration purposes when option 65001 with hexdata 'c001' is 430cd9f4ecSchristos# sent from the client side this module will reply with the same code and 440cd9f4ecSchristos# data 'deadbeef'. 450cd9f4ecSchristos 460cd9f4ecSchristos# Useful functions: 470cd9f4ecSchristos# edns_opt_list_is_empty(edns_opt_list): 480cd9f4ecSchristos# Check if the option list is empty. 490cd9f4ecSchristos# Return True if empty, False otherwise. 500cd9f4ecSchristos# 510cd9f4ecSchristos# edns_opt_list_append(edns_opt_list, code, data_bytearray, region): 520cd9f4ecSchristos# Append the EDNS option with code and data_bytearray to the given 530cd9f4ecSchristos# edns_opt_list. 540cd9f4ecSchristos# NOTE: data_bytearray MUST be a Python bytearray. 550cd9f4ecSchristos# Return True on success, False on failure. 560cd9f4ecSchristos# 570cd9f4ecSchristos# edns_opt_list_remove(edns_opt_list, code): 587a540f2bSchristos# Remove all occurrences of the given EDNS option code from the 590cd9f4ecSchristos# edns_opt_list. 600cd9f4ecSchristos# Return True when at least one EDNS option was removed, False otherwise. 610cd9f4ecSchristos# 620cd9f4ecSchristos# register_edns_option(env, code, bypass_cache_stage=True, 630cd9f4ecSchristos# no_aggregation=True): 640cd9f4ecSchristos# Register EDNS option code as a known EDNS option. 650cd9f4ecSchristos# bypass_cache_stage: 660cd9f4ecSchristos# bypasses answering from cache and allows the query to reach the 670cd9f4ecSchristos# modules for further EDNS handling. 680cd9f4ecSchristos# no_aggregation: 690cd9f4ecSchristos# makes every query with the said EDNS option code unique. 700cd9f4ecSchristos# Return True on success, False on failure. 710cd9f4ecSchristos# 720cd9f4ecSchristos# Examples on how to use the functions are given in this file. 730cd9f4ecSchristos 740cd9f4ecSchristos 750cd9f4ecSchristosdef init_standard(id, env): 760cd9f4ecSchristos """New version of the init function. 770cd9f4ecSchristos The function's signature is the same as the C counterpart and allows for 780cd9f4ecSchristos extra functionality during init. 790cd9f4ecSchristos ..note:: This function is preferred by unbound over the old init function. 800cd9f4ecSchristos ..note:: The previously accessible configuration options can now be found in 8101049ae6Schristos env.cfg. 820cd9f4ecSchristos """ 83*91f7d55fSchristos log_info("python: inited script {}".format(mod_env['script'])) 840cd9f4ecSchristos 850cd9f4ecSchristos # Register EDNS option 65001 as a known EDNS option. 860cd9f4ecSchristos if not register_edns_option(env, 65001, bypass_cache_stage=True, 870cd9f4ecSchristos no_aggregation=True): 880cd9f4ecSchristos return False 890cd9f4ecSchristos 900cd9f4ecSchristos return True 910cd9f4ecSchristos 920cd9f4ecSchristos 930cd9f4ecSchristosdef init(id, cfg): 940cd9f4ecSchristos """Previous version init function. 950cd9f4ecSchristos ..note:: This function is still supported for backwards compatibility when 960cd9f4ecSchristos the init_standard function is missing. When init_standard is 970cd9f4ecSchristos present this function SHOULD be omitted to avoid confusion to the 980cd9f4ecSchristos reader. 990cd9f4ecSchristos """ 1000cd9f4ecSchristos return True 1010cd9f4ecSchristos 1020cd9f4ecSchristos 1030cd9f4ecSchristosdef deinit(id): return True 1040cd9f4ecSchristos 1050cd9f4ecSchristos 1060cd9f4ecSchristosdef inform_super(id, qstate, superqstate, qdata): return True 1070cd9f4ecSchristos 1080cd9f4ecSchristos 1090cd9f4ecSchristosdef operate(id, event, qstate, qdata): 1100cd9f4ecSchristos if (event == MODULE_EVENT_NEW) or (event == MODULE_EVENT_PASS): 1110cd9f4ecSchristos # Detect if EDNS option code 56001 is present from the client side. If 1120cd9f4ecSchristos # so turn on the flags for cache management. 1130cd9f4ecSchristos if not edns_opt_list_is_empty(qstate.edns_opts_front_in): 1140cd9f4ecSchristos log_info("python: searching for EDNS option code 65001 during NEW " 1150cd9f4ecSchristos "or PASS event ") 1160cd9f4ecSchristos for o in qstate.edns_opts_front_in_iter: 1170cd9f4ecSchristos if o.code == 65001: 1180cd9f4ecSchristos log_info("python: found EDNS option code 65001") 1190cd9f4ecSchristos # Instruct other modules to not lookup for an 1200cd9f4ecSchristos # answer in the cache. 1210cd9f4ecSchristos qstate.no_cache_lookup = 1 1220cd9f4ecSchristos log_info("python: enabled no_cache_lookup") 1230cd9f4ecSchristos 1240cd9f4ecSchristos # Instruct other modules to not store the answer in 1250cd9f4ecSchristos # the cache. 1260cd9f4ecSchristos qstate.no_cache_store = 1 1270cd9f4ecSchristos log_info("python: enabled no_cache_store") 1280cd9f4ecSchristos 1290cd9f4ecSchristos #Pass on the query 1300cd9f4ecSchristos qstate.ext_state[id] = MODULE_WAIT_MODULE 1310cd9f4ecSchristos return True 1320cd9f4ecSchristos 1330cd9f4ecSchristos elif event == MODULE_EVENT_MODDONE: 1340cd9f4ecSchristos # If the client sent EDNS option code 65001 and data 'c001' reply 1350cd9f4ecSchristos # with the same code and data 'deadbeef'. 1360cd9f4ecSchristos if not edns_opt_list_is_empty(qstate.edns_opts_front_in): 1370cd9f4ecSchristos log_info("python: searching for EDNS option code 65001 during " 1380cd9f4ecSchristos "MODDONE") 1390cd9f4ecSchristos for o in qstate.edns_opts_front_in_iter: 1400cd9f4ecSchristos if o.code == 65001 and o.data == bytearray.fromhex("c001"): 1410cd9f4ecSchristos b = bytearray.fromhex("deadbeef") 1420cd9f4ecSchristos if not edns_opt_list_append(qstate.edns_opts_front_out, 1430cd9f4ecSchristos o.code, b, qstate.region): 1440cd9f4ecSchristos qstate.ext_state[id] = MODULE_ERROR 1450cd9f4ecSchristos return False 1460cd9f4ecSchristos 1470cd9f4ecSchristos # List every EDNS option in all lists. 1480cd9f4ecSchristos # The available lists are: 1490cd9f4ecSchristos # - qstate.edns_opts_front_in: EDNS options that came from the 1500cd9f4ecSchristos # client side. SHOULD NOT be changed; 1510cd9f4ecSchristos # 1520cd9f4ecSchristos # - qstate.edns_opts_back_out: EDNS options that will be sent to the 1530cd9f4ecSchristos # server side. Can be populated by 1540cd9f4ecSchristos # EDNS literate modules; 1550cd9f4ecSchristos # 1560cd9f4ecSchristos # - qstate.edns_opts_back_in: EDNS options that came from the 1570cd9f4ecSchristos # server side. SHOULD NOT be changed; 1580cd9f4ecSchristos # 1590cd9f4ecSchristos # - qstate.edns_opts_front_out: EDNS options that will be sent to the 1600cd9f4ecSchristos # client side. Can be populated by 1610cd9f4ecSchristos # EDNS literate modules; 1620cd9f4ecSchristos # 1630cd9f4ecSchristos # The lists' contents can be accessed in python by their _iter 1640cd9f4ecSchristos # counterpart as an iterator. 1650cd9f4ecSchristos if not edns_opt_list_is_empty(qstate.edns_opts_front_in): 1660cd9f4ecSchristos log_info("python: EDNS options in edns_opts_front_in:") 1670cd9f4ecSchristos for o in qstate.edns_opts_front_in_iter: 1680cd9f4ecSchristos log_info("python: Code: {}, Data: '{}'".format(o.code, 1690cd9f4ecSchristos "".join('{:02x}'.format(x) for x in o.data))) 1700cd9f4ecSchristos 1710cd9f4ecSchristos if not edns_opt_list_is_empty(qstate.edns_opts_back_out): 1720cd9f4ecSchristos log_info("python: EDNS options in edns_opts_back_out:") 1730cd9f4ecSchristos for o in qstate.edns_opts_back_out_iter: 1740cd9f4ecSchristos log_info("python: Code: {}, Data: '{}'".format(o.code, 1750cd9f4ecSchristos "".join('{:02x}'.format(x) for x in o.data))) 1760cd9f4ecSchristos 1770cd9f4ecSchristos if not edns_opt_list_is_empty(qstate.edns_opts_back_in): 1780cd9f4ecSchristos log_info("python: EDNS options in edns_opts_back_in:") 1790cd9f4ecSchristos for o in qstate.edns_opts_back_in_iter: 1800cd9f4ecSchristos log_info("python: Code: {}, Data: '{}'".format(o.code, 1810cd9f4ecSchristos "".join('{:02x}'.format(x) for x in o.data))) 1820cd9f4ecSchristos 1830cd9f4ecSchristos if not edns_opt_list_is_empty(qstate.edns_opts_front_out): 1840cd9f4ecSchristos log_info("python: EDNS options in edns_opts_front_out:") 1850cd9f4ecSchristos for o in qstate.edns_opts_front_out_iter: 1860cd9f4ecSchristos log_info("python: Code: {}, Data: '{}'".format(o.code, 1870cd9f4ecSchristos "".join('{:02x}'.format(x) for x in o.data))) 1880cd9f4ecSchristos 1890cd9f4ecSchristos qstate.ext_state[id] = MODULE_FINISHED 1900cd9f4ecSchristos return True 1910cd9f4ecSchristos 1920cd9f4ecSchristos log_err("pythonmod: Unknown event") 1930cd9f4ecSchristos qstate.ext_state[id] = MODULE_ERROR 1940cd9f4ecSchristos return True 195