]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - pythonmod/examples/inplace_callbacks.py
Vendor import of Unbound 1.6.2.
[FreeBSD/FreeBSD.git] / pythonmod / examples / inplace_callbacks.py
1 # -*- coding: utf-8 -*-
2 '''
3  inplace_callbacks.py: python module showcasing inplace callback function
4                        registration and functionality.
5
6  Copyright (c) 2016, NLnet Labs.
7
8  This software is open source.
9  
10  Redistribution and use in source and binary forms, with or without
11  modification, are permitted provided that the following conditions
12  are met:
13  
14     * Redistributions of source code must retain the above copyright notice,
15       this list of conditions and the following disclaimer.
16  
17     * Redistributions in binary form must reproduce the above copyright notice,
18       this list of conditions and the following disclaimer in the documentation
19       and/or other materials provided with the distribution.
20  
21     * Neither the name of the organization nor the names of its
22       contributors may be used to endorse or promote products derived from this
23       software without specific prior written permission.
24
25  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
29  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35  POSSIBILITY OF SUCH DAMAGE.
36 '''
37 #Try:
38 # - dig @localhost nlnetlabs.nl +ednsopt=65002:
39 #       This query *could* be answered from cache. If so, unbound will reply
40 #       with the same EDNS option 65002, but with hexdata 'deadbeef' as data.
41 #
42 # - dig @localhost bogus.nlnetlabs.nl txt:
43 #       This query returns SERVFAIL as the txt record of bogus.nlnetlabs.nl is
44 #       intentionally bogus. The reply will contain an empty EDNS option
45 #       with option code 65003.
46 #       (unbound needs to be validating for this example to work)
47
48 # Useful functions:
49 #   register_inplace_cb_reply(inplace_reply_callback, env, id):
50 #       Register the reply_callback function as an inplace callback function
51 #       when answering with a resolved query.
52 #       Return True on success, False on failure.
53 #
54 #   register_inplace_cb_reply_cache(inplace_reply_cache_callback, env, id):
55 #       Register the reply_cache_callback function as an inplace callback
56 #       function when answering from cache.
57 #       Return True on success, False on failure.
58 #
59 #   register_inplace_cb_reply_local(inplace_reply_local_callback, env, id):
60 #       Register the reply_local_callback function as an inplace callback
61 #       function when answering from local data or chaos reply.
62 #       Return True on success, False on failure.
63 #
64 #   register_inplace_cb_reply_servfail(inplace_reply_servfail_callback, env, id):
65 #       Register the reply_servfail_callback function as an inplace callback
66 #       function when answering with servfail.
67 #       Return True on success, False on failure.
68 #
69 # Examples on how to use the functions are given in this file.
70
71
72 def inplace_reply_callback(qinfo, qstate, rep, rcode, edns, opt_list_out,
73                            region):
74     """Function that will be registered as an inplace callback function.
75     It will be called when answering with a resolved query.
76     :param qinfo: query_info struct;
77     :param qstate: module qstate. It contains the available opt_lists; It
78                    SHOULD NOT be altered;
79     :param rep: reply_info struct;
80     :param rcode: return code for the query;
81     :param edns: edns_data to be sent to the client side. It SHOULD NOT be
82                  altered;
83     :param opt_list_out: the list with the EDNS options that will be sent as a
84                          reply. It can be populated with EDNS options;
85     :param region: region to allocate temporary data. Needs to be used when we
86                    want to append a new option to opt_list_out.
87     :return: True on success, False on failure.
88     """
89     log_info("python: called back while replying.")
90     return True
91
92
93 def inplace_cache_callback(qinfo, qstate, rep, rcode, edns, opt_list_out,
94                            region):
95     """Function that will be registered as an inplace callback function.
96     It will be called when answering from the cache.
97     :param qinfo: query_info struct;
98     :param qstate: module qstate. None;
99     :param rep: reply_info struct;
100     :param rcode: return code for the query;
101     :param edns: edns_data sent from the client side. The list with the EDNS
102                  options is accesible through edns.opt_list. It SHOULD NOT be
103                  altered;
104     :param opt_list_out: the list with the EDNS options that will be sent as a
105                          reply. It can be populated with EDNS options;
106     :param region: region to allocate temporary data. Needs to be used when we
107                    want to append a new option to opt_list_out.
108     :return: True on success, False on failure.
109
110     For demostration purposes we want to see if EDNS option 65002 is present
111     and reply with a new value.
112     """
113     log_info("python: called back while answering from cache.")
114     # Inspect the incoming EDNS options.
115     if not edns_opt_list_is_empty(edns.opt_list):
116         log_info("python: available EDNS options:")
117         for o in edns.opt_list_iter:
118             log_info("python:    Code: {}, Data: '{}'".format(o.code,
119                 "".join('{:02x}'.format(x) for x in o.data)))
120             if o.code == 65002:
121                 log_info("python: *found option code 65002*")
122
123                 # add to opt_list
124                 # Data MUST be represented in a bytearray.
125                 b = bytearray.fromhex("deadbeef")
126                 if edns_opt_list_append(opt_list_out, o.code, b, region):
127                     log_info("python: *added new option code 65002*")
128                 else:
129                     log_info("python: *failed to add new option code 65002*")
130                     return False
131                 break
132
133     return True
134
135
136 def inplace_local_callback(qinfo, qstate, rep, rcode, edns, opt_list_out,
137                            region):
138     """Function that will be registered as an inplace callback function.
139     It will be called when answering from local data.
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 accesible 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     :return: True on success, False on failure.
152     """
153     log_info("python: called back while replying with local data or chaos"
154              " reply.")
155     return True
156
157
158 def inplace_servfail_callback(qinfo, qstate, rep, rcode, edns, opt_list_out,
159                               region):
160     """Function that will be registered as an inplace callback function.
161     It will be called when answering with SERVFAIL.
162     :param qinfo: query_info struct;
163     :param qstate: module qstate. If not None the relevant opt_lists are
164                    available here;
165     :param rep: reply_info struct. None;
166     :param rcode: return code for the query. LDNS_RCODE_SERVFAIL;
167     :param edns: edns_data to be sent to the client side. If qstate is None
168                  edns.opt_list contains the EDNS options sent from the client
169                  side. It SHOULD NOT be altered;
170     :param opt_list_out: the list with the EDNS options that will be sent as a
171                          reply. It can be populated with EDNS options;
172     :param region: region to allocate temporary data. Needs to be used when we
173                    want to append a new option to opt_list_out.
174     :return: True on success, False on failure.
175
176     For demostration purposes we want to reply with an empty EDNS code '65003'.
177     """
178     log_info("python: called back while servfail.")
179     b = bytearray.fromhex("")
180     edns_opt_list_append(opt_list_out, 65003, b, region)
181     return True
182
183
184 def init_standard(id, env):
185     """New version of the init function.
186     The function's signature is the same as the C counterpart and allows for
187     extra functionality during init.
188     ..note:: This function is preferred by unbound over the old init function.
189     ..note:: The previously accesible configuration options can now be found in
190              env.cgf.
191     """
192     log_info("python: inited script {}".format(env.cfg.python_script))
193
194     # Register the inplace_reply_callback function as an inplace callback
195     # function when answering a resolved query.
196     if not register_inplace_cb_reply(inplace_reply_callback, env, id):
197         return False
198
199     # Register the inplace_cache_callback function as an inplace callback
200     # function when answering from cache.
201     if not register_inplace_cb_reply_cache(inplace_cache_callback, env, id):
202         return False
203
204     # Register the inplace_local_callback function as an inplace callback
205     # function when answering from local data.
206     if not register_inplace_cb_reply_local(inplace_local_callback, env, id):
207         return False
208
209     # Register the inplace_servfail_callback function as an inplace callback
210     # function when answering with SERVFAIL.
211     if not register_inplace_cb_reply_servfail(inplace_servfail_callback, env, id):
212         return False
213
214     return True
215
216
217 def init(id, cfg):
218     """Previous version init function.
219     ..note:: This function is still supported for backwards compatibility when
220              the init_standard function is missing. When init_standard is
221              present this function SHOULD be ommited to avoid confusion to the
222              reader.
223     """
224     return True
225
226
227 def deinit(id): return True
228
229
230 def inform_super(id, qstate, superqstate, qdata): return True
231
232
233 def operate(id, event, qstate, qdata):
234     if (event == MODULE_EVENT_NEW) or (event == MODULE_EVENT_PASS):
235         qstate.ext_state[id] = MODULE_WAIT_MODULE 
236         return True
237
238     elif event == MODULE_EVENT_MODDONE:
239         qstate.ext_state[id] = MODULE_FINISHED
240         return True
241
242     log_err("pythonmod: Unknown event")
243     qstate.ext_state[id] = MODULE_ERROR
244     return True