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