1Inplace callbacks 2================= 3 4This example shows how to register and use inplace callback functions. These 5functions are going to be called just before unbound replies back to a client. 6They can perform certain actions without interrupting unbound's execution flow 7(e.g. add/remove EDNS options, manipulate the reply). 8 9Two different scenarios will be shown: 10 11- If answering from cache and the client used EDNS option code ``65002`` we 12 will answer with the same code but with data ``0xdeadbeef``; 13- When answering with a SERVFAIL we also add an empty EDNS option code 14 ``65003``. 15 16 17Key parts 18~~~~~~~~~ 19 20This example relies on the following functionalities: 21 22 23Registering inplace callback functions 24-------------------------------------- 25 26There are four types of inplace callback functions: 27 28- `inplace callback reply functions`_ 29- `inplace callback reply_cache functions`_ 30- `inplace callback reply_local functions`_ 31- `inplace callback reply_servfail functions`_ 32 33 34Inplace callback reply functions 35................................ 36 37Called when answering with a *resolved* query. 38 39The callback function's prototype is the following: 40 41.. code-block:: python 42 43 def inplace_reply_callback(qinfo, qstate, rep, rcode, edns, opt_list_out, region): 44 """Function that will be registered as an inplace callback function. 45 It will be called when answering with a resolved query. 46 :param qinfo: query_info struct; 47 :param qstate: module qstate. It contains the available opt_lists; It 48 SHOULD NOT be altered; 49 :param rep: reply_info struct; 50 :param rcode: return code for the query; 51 :param edns: edns_data to be sent to the client side. It SHOULD NOT be 52 altered; 53 :param opt_list_out: the list with the EDNS options that will be sent as a 54 reply. It can be populated with EDNS options; 55 :param region: region to allocate temporary data. Needs to be used when we 56 want to append a new option to opt_list_out. 57 :return: True on success, False on failure. 58 """ 59 60.. note:: The function's name is irrelevant. 61 62We can register such function as: 63 64.. code-block:: python 65 66 if not register_inplace_cb_reply(inplace_reply_callback, env, id): 67 log_info("python: Could not register inplace callback function.") 68 69 70Inplace callback reply_cache functions 71...................................... 72 73Called when answering *from cache*. 74 75The callback function's prototype is the following: 76 77.. code-block:: python 78 79 def inplace_cache_callback(qinfo, qstate, rep, rcode, edns, opt_list_out, region): 80 """Function that will be registered as an inplace callback function. 81 It will be called when answering from the cache. 82 :param qinfo: query_info struct; 83 :param qstate: module qstate. None; 84 :param rep: reply_info struct; 85 :param rcode: return code for the query; 86 :param edns: edns_data sent from the client side. The list with the EDNS 87 options is accessible through edns.opt_list. It SHOULD NOT be 88 altered; 89 :param opt_list_out: the list with the EDNS options that will be sent as a 90 reply. It can be populated with EDNS options; 91 :param region: region to allocate temporary data. Needs to be used when we 92 want to append a new option to opt_list_out. 93 :return: True on success, False on failure. 94 """ 95 96.. note:: The function's name is irrelevant. 97 98We can register such function as: 99 100.. code-block:: python 101 102 if not register_inplace_cb_reply_cache(inplace_cache_callback, env, id): 103 log_info("python: Could not register inplace callback function.") 104 105 106Inplace callback reply_local functions 107...................................... 108 109Called when answering with *local data* or a *Chaos(CH) reply*. 110 111The callback function's prototype is the following: 112 113.. code-block:: python 114 115 def inplace_local_callback(qinfo, qstate, rep, rcode, edns, opt_list_out, region): 116 """Function that will be registered as an inplace callback function. 117 It will be called when answering from local data. 118 :param qinfo: query_info struct; 119 :param qstate: module qstate. None; 120 :param rep: reply_info struct; 121 :param rcode: return code for the query; 122 :param edns: edns_data sent from the client side. The list with the 123 EDNS options is accessible through edns.opt_list. It 124 SHOULD NOT be altered; 125 :param opt_list_out: the list with the EDNS options that will be sent as a 126 reply. It can be populated with EDNS options; 127 :param region: region to allocate temporary data. Needs to be used when we 128 want to append a new option to opt_list_out. 129 :return: True on success, False on failure. 130 """ 131 132.. note:: The function's name is irrelevant. 133 134We can register such function as: 135 136.. code-block:: python 137 138 if not register_inplace_cb_reply_local(inplace_local_callback, env, id): 139 log_info("python: Could not register inplace callback function.") 140 141 142Inplace callback reply_servfail functions 143......................................... 144 145Called when answering with *SERVFAIL*. 146 147The callback function's prototype is the following: 148 149.. code-block:: python 150 151 def inplace_servfail_callback(qinfo, qstate, rep, rcode, edns, opt_list_out, region): 152 """Function that will be registered as an inplace callback function. 153 It will be called when answering with SERVFAIL. 154 :param qinfo: query_info struct; 155 :param qstate: module qstate. If not None the relevant opt_lists are 156 available here; 157 :param rep: reply_info struct. None; 158 :param rcode: return code for the query. LDNS_RCODE_SERVFAIL; 159 :param edns: edns_data to be sent to the client side. If qstate is None 160 edns.opt_list contains the EDNS options sent from the client 161 side. It SHOULD NOT be altered; 162 :param opt_list_out: the list with the EDNS options that will be sent as a 163 reply. It can be populated with EDNS options; 164 :param region: region to allocate temporary data. Needs to be used when we 165 want to append a new option to opt_list_out. 166 :return: True on success, False on failure. 167 """ 168 169.. note:: The function's name is irrelevant. 170 171We can register such function as: 172 173.. code-block:: python 174 175 if not register_inplace_cb_reply_servfail(inplace_servfail_callback, env, id): 176 log_info("python: Could not register inplace callback function.") 177 178 179Testing 180~~~~~~~ 181 182Run the Unbound server: :: 183 184 root@localhost$ unbound -dv -c ./test-inplace_callbacks.conf 185 186In case you use your own configuration file, don't forget to enable the Python 187module:: 188 189 module-config: "validator python iterator" 190 191and use a valid script path :: 192 193 python-script: "./examples/inplace_callbacks.py" 194 195On the first query for the nlnetlabs.nl A record we get no EDNS option back: 196 197:: 198 199 root@localhost$ dig @localhost nlnetlabs.nl +ednsopt=65002 200 201 ; <<>> DiG 9.10.3-P4-Ubuntu <<>> @localhost nlnetlabs.nl +ednsopt=65002 202 ; (1 server found) 203 ;; global options: +cmd 204 ;; Got answer: 205 ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 48057 206 ;; flags: qr rd ra ad; QUERY: 1, ANSWER: 1, AUTHORITY: 4, ADDITIONAL: 3 207 208 ;; OPT PSEUDOSECTION: 209 ; EDNS: version: 0, flags:; udp: 4096 210 ;; QUESTION SECTION: 211 ;nlnetlabs.nl. IN A 212 213 ;; ANSWER SECTION: 214 nlnetlabs.nl. 10200 IN A 185.49.140.10 215 216 ;; AUTHORITY SECTION: 217 nlnetlabs.nl. 10200 IN NS ns.nlnetlabs.nl. 218 nlnetlabs.nl. 10200 IN NS sec2.authdns.ripe.net. 219 nlnetlabs.nl. 10200 IN NS anyns.pch.net. 220 nlnetlabs.nl. 10200 IN NS ns-ext1.sidn.nl. 221 222 ;; ADDITIONAL SECTION: 223 ns.nlnetlabs.nl. 10200 IN A 185.49.140.60 224 ns.nlnetlabs.nl. 10200 IN AAAA 2a04:b900::8:0:0:60 225 226 ;; Query time: 813 msec 227 ;; SERVER: 127.0.0.1#53(127.0.0.1) 228 ;; WHEN: Mon Dec 05 16:15:32 CET 2016 229 ;; MSG SIZE rcvd: 204 230 231When we issue the same query again we get a cached response and the expected 232``65002: 0xdeadbeef`` EDNS option: 233 234:: 235 236 root@localhost$ dig @localhost nlnetlabs.nl +ednsopt=65002 237 238 ; <<>> DiG 9.10.3-P4-Ubuntu <<>> @localhost nlnetlabs.nl +ednsopt=65002 239 ; (1 server found) 240 ;; global options: +cmd 241 ;; Got answer: 242 ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 26489 243 ;; flags: qr rd ra ad; QUERY: 1, ANSWER: 1, AUTHORITY: 4, ADDITIONAL: 3 244 245 ;; OPT PSEUDOSECTION: 246 ; EDNS: version: 0, flags:; udp: 4096 247 ; OPT=65002: de ad be ef ("....") 248 ;; QUESTION SECTION: 249 ;nlnetlabs.nl. IN A 250 251 ;; ANSWER SECTION: 252 nlnetlabs.nl. 10197 IN A 185.49.140.10 253 254 ;; AUTHORITY SECTION: 255 nlnetlabs.nl. 10197 IN NS ns.nlnetlabs.nl. 256 nlnetlabs.nl. 10197 IN NS sec2.authdns.ripe.net. 257 nlnetlabs.nl. 10197 IN NS anyns.pch.net. 258 nlnetlabs.nl. 10197 IN NS ns-ext1.sidn.nl. 259 260 ;; ADDITIONAL SECTION: 261 ns.nlnetlabs.nl. 10197 IN AAAA 2a04:b900::8:0:0:60 262 ns.nlnetlabs.nl. 10197 IN A 185.49.140.60 263 264 ;; Query time: 0 msec 265 ;; SERVER: 127.0.0.1#53(127.0.0.1) 266 ;; WHEN: Mon Dec 05 16:50:04 CET 2016 267 ;; MSG SIZE rcvd: 212 268 269By issuing a query for a bogus domain unbound replies with SERVFAIL and an 270empty EDNS option code ``65003``. *For this example to work unbound needs to be 271validating*: 272 273:: 274 275 root@localhost$ dig @localhost bogus.nlnetlabs.nl txt 276 277 ; <<>> DiG 9.10.3-P4-Ubuntu <<>> @localhost bogus.nlnetlabs.nl txt 278 ; (1 server found) 279 ;; global options: +cmd 280 ;; Got answer: 281 ;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 19865 282 ;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1 283 284 ;; OPT PSEUDOSECTION: 285 ; EDNS: version: 0, flags:; udp: 4096 286 ; OPT=65003 287 ;; QUESTION SECTION: 288 ;bogus.nlnetlabs.nl. IN TXT 289 290 ;; Query time: 11 msec 291 ;; SERVER: 127.0.0.1#53(127.0.0.1) 292 ;; WHEN: Mon Dec 05 17:06:01 CET 2016 293 ;; MSG SIZE rcvd: 51 294 295 296Complete source code 297~~~~~~~~~~~~~~~~~~~~ 298.. literalinclude:: ../../examples/inplace_callbacks.py 299 :language: python 300