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, 44 region, **kwargs): 45 """ 46 Function that will be registered as an inplace callback function. 47 It will be called when answering with a resolved query. 48 49 :param qinfo: query_info struct; 50 :param qstate: module qstate. It contains the available opt_lists; It 51 SHOULD NOT be altered; 52 :param rep: reply_info struct; 53 :param rcode: return code for the query; 54 :param edns: edns_data to be sent to the client side. It SHOULD NOT be 55 altered; 56 :param opt_list_out: the list with the EDNS options that will be sent as a 57 reply. It can be populated with EDNS options; 58 :param region: region to allocate temporary data. Needs to be used when we 59 want to append a new option to opt_list_out. 60 :param **kwargs: Dictionary that may contain parameters added in a future 61 release. Current parameters: 62 ``repinfo``: Reply information for a communication point (comm_reply). 63 It is None when the callback happens in the mesh states. 64 65 :return: True on success, False on failure. 66 67 """ 68 69.. note:: The function's name is irrelevant. 70 71We can register such function as: 72 73.. code-block:: python 74 75 if not register_inplace_cb_reply(inplace_reply_callback, env, id): 76 log_info("python: Could not register inplace callback function.") 77 78 79Inplace callback reply_cache functions 80...................................... 81 82Called when answering *from cache*. 83 84The callback function's prototype is the following: 85 86.. code-block:: python 87 88 def inplace_cache_callback(qinfo, qstate, rep, rcode, edns, opt_list_out, 89 region, **kwargs): 90 """ 91 Function that will be registered as an inplace callback function. 92 It will be called when answering from the cache. 93 94 :param qinfo: query_info struct; 95 :param qstate: module qstate. None; 96 :param rep: reply_info struct; 97 :param rcode: return code for the query; 98 :param edns: edns_data sent from the client side. The list with the EDNS 99 options is accessible through edns.opt_list. It SHOULD NOT be 100 altered; 101 :param opt_list_out: the list with the EDNS options that will be sent as a 102 reply. It can be populated with EDNS options; 103 :param region: region to allocate temporary data. Needs to be used when we 104 want to append a new option to opt_list_out. 105 :param **kwargs: Dictionary that may contain parameters added in a future 106 release. Current parameters: 107 ``repinfo``: Reply information for a communication point (comm_reply). 108 It is None when the callback happens in the mesh 109 states(modules). 110 111 :return: True on success, False on failure. 112 113 For demonstration purposes we want to see if EDNS option 65002 is present 114 and reply with a new value. 115 116 """ 117 118.. note:: The function's name is irrelevant. 119 120We can register such function as: 121 122.. code-block:: python 123 124 if not register_inplace_cb_reply_cache(inplace_cache_callback, env, id): 125 log_info("python: Could not register inplace callback function.") 126 127 128Inplace callback reply_local functions 129...................................... 130 131Called when answering with *local data* or a *Chaos(CH) reply*. 132 133The callback function's prototype is the following: 134 135.. code-block:: python 136 137 def inplace_local_callback(qinfo, qstate, rep, rcode, edns, opt_list_out, 138 region, **kwargs): 139 """ 140 Function that will be registered as an inplace callback function. 141 It will be called when answering from local data. 142 143 :param qinfo: query_info struct; 144 :param qstate: module qstate. None; 145 :param rep: reply_info struct; 146 :param rcode: return code for the query; 147 :param edns: edns_data sent from the client side. The list with the 148 EDNS options is accessible through edns.opt_list. It 149 SHOULD NOT be altered; 150 :param opt_list_out: the list with the EDNS options that will be sent as a 151 reply. It can be populated with EDNS options; 152 :param region: region to allocate temporary data. Needs to be used when we 153 want to append a new option to opt_list_out. 154 :param **kwargs: Dictionary that may contain parameters added in a future 155 release. Current parameters: 156 ``repinfo``: Reply information for a communication point (comm_reply). 157 It is None when the callback happens in the mesh 158 states(modules). 159 160 :return: True on success, False on failure. 161 162 """ 163 164.. note:: The function's name is irrelevant. 165 166We can register such function as: 167 168.. code-block:: python 169 170 if not register_inplace_cb_reply_local(inplace_local_callback, env, id): 171 log_info("python: Could not register inplace callback function.") 172 173 174Inplace callback reply_servfail functions 175......................................... 176 177Called when answering with *SERVFAIL*. 178 179The callback function's prototype is the following: 180 181.. code-block:: python 182 183 def inplace_servfail_callback(qinfo, qstate, rep, rcode, edns, opt_list_out, 184 region, **kwargs): 185 """ 186 Function that will be registered as an inplace callback function. 187 It will be called when answering with SERVFAIL. 188 189 :param qinfo: query_info struct; 190 :param qstate: module qstate. If not None the relevant opt_lists are 191 available here; 192 :param rep: reply_info struct. None; 193 :param rcode: return code for the query. LDNS_RCODE_SERVFAIL; 194 :param edns: edns_data to be sent to the client side. If qstate is None 195 edns.opt_list contains the EDNS options sent from the client 196 side. It SHOULD NOT be altered; 197 :param opt_list_out: the list with the EDNS options that will be sent as a 198 reply. It can be populated with EDNS options; 199 :param region: region to allocate temporary data. Needs to be used when we 200 want to append a new option to opt_list_out. 201 :param **kwargs: Dictionary that may contain parameters added in a future 202 release. Current parameters: 203 ``repinfo``: Reply information for a communication point (comm_reply). 204 It is None when the callback happens in the mesh 205 states(modules). 206 207 :return: True on success, False on failure. 208 209 For demonstration purposes we want to reply with an empty EDNS code '65003' 210 and log the IP address(es) of the client(s). 211 212 """ 213 214.. note:: The function's name is irrelevant. 215 216We can register such function as: 217 218.. code-block:: python 219 220 if not register_inplace_cb_reply_servfail(inplace_servfail_callback, env, id): 221 log_info("python: Could not register inplace callback function.") 222 223 224Testing 225~~~~~~~ 226 227Run the Unbound server: :: 228 229 root@localhost$ unbound -dv -c ./test-inplace_callbacks.conf 230 231In case you use your own configuration file, don't forget to enable the Python 232module:: 233 234 module-config: "validator python iterator" 235 236and use a valid script path :: 237 238 python-script: "./examples/inplace_callbacks.py" 239 240On the first query for the nlnetlabs.nl A record we get no EDNS option back: 241 242:: 243 244 root@localhost$ dig @localhost nlnetlabs.nl +ednsopt=65002 245 246 ; <<>> DiG 9.10.3-P4-Ubuntu <<>> @localhost nlnetlabs.nl +ednsopt=65002 247 ; (1 server found) 248 ;; global options: +cmd 249 ;; Got answer: 250 ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 48057 251 ;; flags: qr rd ra ad; QUERY: 1, ANSWER: 1, AUTHORITY: 4, ADDITIONAL: 3 252 253 ;; OPT PSEUDOSECTION: 254 ; EDNS: version: 0, flags:; udp: 4096 255 ;; QUESTION SECTION: 256 ;nlnetlabs.nl. IN A 257 258 ;; ANSWER SECTION: 259 nlnetlabs.nl. 10200 IN A 185.49.140.10 260 261 ;; AUTHORITY SECTION: 262 nlnetlabs.nl. 10200 IN NS ns.nlnetlabs.nl. 263 nlnetlabs.nl. 10200 IN NS sec2.authdns.ripe.net. 264 nlnetlabs.nl. 10200 IN NS anyns.pch.net. 265 nlnetlabs.nl. 10200 IN NS ns-ext1.sidn.nl. 266 267 ;; ADDITIONAL SECTION: 268 ns.nlnetlabs.nl. 10200 IN A 185.49.140.60 269 ns.nlnetlabs.nl. 10200 IN AAAA 2a04:b900::8:0:0:60 270 271 ;; Query time: 813 msec 272 ;; SERVER: 127.0.0.1#53(127.0.0.1) 273 ;; WHEN: Mon Dec 05 16:15:32 CET 2016 274 ;; MSG SIZE rcvd: 204 275 276When we issue the same query again we get a cached response and the expected 277``65002: 0xdeadbeef`` EDNS option: 278 279:: 280 281 root@localhost$ dig @localhost nlnetlabs.nl +ednsopt=65002 282 283 ; <<>> DiG 9.10.3-P4-Ubuntu <<>> @localhost nlnetlabs.nl +ednsopt=65002 284 ; (1 server found) 285 ;; global options: +cmd 286 ;; Got answer: 287 ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 26489 288 ;; flags: qr rd ra ad; QUERY: 1, ANSWER: 1, AUTHORITY: 4, ADDITIONAL: 3 289 290 ;; OPT PSEUDOSECTION: 291 ; EDNS: version: 0, flags:; udp: 4096 292 ; OPT=65002: de ad be ef ("....") 293 ;; QUESTION SECTION: 294 ;nlnetlabs.nl. IN A 295 296 ;; ANSWER SECTION: 297 nlnetlabs.nl. 10197 IN A 185.49.140.10 298 299 ;; AUTHORITY SECTION: 300 nlnetlabs.nl. 10197 IN NS ns.nlnetlabs.nl. 301 nlnetlabs.nl. 10197 IN NS sec2.authdns.ripe.net. 302 nlnetlabs.nl. 10197 IN NS anyns.pch.net. 303 nlnetlabs.nl. 10197 IN NS ns-ext1.sidn.nl. 304 305 ;; ADDITIONAL SECTION: 306 ns.nlnetlabs.nl. 10197 IN AAAA 2a04:b900::8:0:0:60 307 ns.nlnetlabs.nl. 10197 IN A 185.49.140.60 308 309 ;; Query time: 0 msec 310 ;; SERVER: 127.0.0.1#53(127.0.0.1) 311 ;; WHEN: Mon Dec 05 16:50:04 CET 2016 312 ;; MSG SIZE rcvd: 212 313 314By issuing a query for a bogus domain unbound replies with SERVFAIL and an 315empty EDNS option code ``65003``. *For this example to work unbound needs to be 316validating*: 317 318:: 319 320 root@localhost$ dig @localhost bogus.nlnetlabs.nl txt 321 322 ; <<>> DiG 9.10.3-P4-Ubuntu <<>> @localhost bogus.nlnetlabs.nl txt 323 ; (1 server found) 324 ;; global options: +cmd 325 ;; Got answer: 326 ;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 19865 327 ;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1 328 329 ;; OPT PSEUDOSECTION: 330 ; EDNS: version: 0, flags:; udp: 4096 331 ; OPT=65003 332 ;; QUESTION SECTION: 333 ;bogus.nlnetlabs.nl. IN TXT 334 335 ;; Query time: 11 msec 336 ;; SERVER: 127.0.0.1#53(127.0.0.1) 337 ;; WHEN: Mon Dec 05 17:06:01 CET 2016 338 ;; MSG SIZE rcvd: 51 339 340 341Complete source code 342~~~~~~~~~~~~~~~~~~~~ 343.. literalinclude:: ../../examples/inplace_callbacks.py 344 :language: python 345