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