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