xref: /netbsd-src/external/bsd/unbound/dist/pythonmod/doc/examples/example6.rst (revision 7330f729ccf0bd976a06f95fad452fe774fc7fd1)
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