/src/freeradius-server/src/lib/server/request.c
Line | Count | Source |
1 | | /* |
2 | | * This program is free software; you can redistribute it and/or modify |
3 | | * it under the terms of the GNU General Public License as published by |
4 | | * the Free Software Foundation; either version 2 of the License, or |
5 | | * (at your option) any later version. |
6 | | * |
7 | | * This program is distributed in the hope that it will be useful, |
8 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
9 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
10 | | * GNU General Public License for more details. |
11 | | * |
12 | | * You should have received a copy of the GNU General Public License |
13 | | * along with this program; if not, write to the Free Software |
14 | | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
15 | | */ |
16 | | |
17 | | /** |
18 | | * $Id: 4ab9c9fb47727b3ec532ad67adc3ed94ee7e1b6e $ |
19 | | * |
20 | | * @brief Functions for allocating requests and storing internal data in them. |
21 | | * @file src/lib/server/request.c |
22 | | * |
23 | | * @copyright 2015 The FreeRADIUS server project |
24 | | */ |
25 | | RCSID("$Id: 4ab9c9fb47727b3ec532ad67adc3ed94ee7e1b6e $") |
26 | | |
27 | | #include <freeradius-devel/server/request.h> |
28 | | #include <freeradius-devel/server/request_data.h> |
29 | | #include <freeradius-devel/unlang/interpret.h> |
30 | | |
31 | | #include <freeradius-devel/util/debug.h> |
32 | | #include <freeradius-devel/util/atexit.h> |
33 | | |
34 | | static fr_dict_t const *dict_freeradius; |
35 | | |
36 | | extern fr_dict_autoload_t request_dict[]; |
37 | | fr_dict_autoload_t request_dict[] = { |
38 | | { .out = &dict_freeradius, .proto = "freeradius" }, |
39 | | DICT_AUTOLOAD_TERMINATOR |
40 | | }; |
41 | | |
42 | | fr_dict_attr_t const *request_attr_root; |
43 | | fr_dict_attr_t const *request_attr_request; |
44 | | fr_dict_attr_t const *request_attr_reply; |
45 | | fr_dict_attr_t const *request_attr_control; |
46 | | fr_dict_attr_t const *request_attr_state; |
47 | | fr_dict_attr_t const *request_attr_local; |
48 | | |
49 | | extern fr_dict_attr_autoload_t request_dict_attr[]; |
50 | | fr_dict_attr_autoload_t request_dict_attr[] = { |
51 | | { .out = &request_attr_root, .name = "root", .type = FR_TYPE_GROUP, .dict = &dict_freeradius }, |
52 | | { .out = &request_attr_request, .name = "request", .type = FR_TYPE_GROUP, .dict = &dict_freeradius }, |
53 | | { .out = &request_attr_reply, .name = "reply", .type = FR_TYPE_GROUP, .dict = &dict_freeradius }, |
54 | | { .out = &request_attr_control, .name = "control", .type = FR_TYPE_GROUP, .dict = &dict_freeradius }, |
55 | | { .out = &request_attr_state, .name = "session-state", .type = FR_TYPE_GROUP, .dict = &dict_freeradius }, |
56 | | { .out = &request_attr_local, .name = "local-variables", .type = FR_TYPE_GROUP, .dict = &dict_freeradius }, |
57 | | DICT_AUTOLOAD_TERMINATOR |
58 | | }; |
59 | | |
60 | | #ifndef NDEBUG |
61 | | static int _state_ctx_free(fr_pair_t *state) |
62 | 0 | { |
63 | 0 | DEBUG4("state-ctx %p freed", state); |
64 | |
|
65 | 0 | return 0; |
66 | 0 | } |
67 | | #endif |
68 | | |
69 | | static inline void CC_HINT(always_inline) request_log_init_orphan(request_t *request) |
70 | 0 | { |
71 | | /* |
72 | | * These may be changed later by request_pre_handler |
73 | | */ |
74 | 0 | request->log.lvl = fr_debug_lvl; /* Default to global debug level */ |
75 | 0 | if (!request->log.dst) { |
76 | 0 | request->log.dst = talloc_zero(request, log_dst_t); |
77 | 0 | } else { |
78 | 0 | memset(request->log.dst, 0, sizeof(*request->log.dst)); |
79 | 0 | } |
80 | 0 | request->log.dst->func = vlog_request; |
81 | 0 | request->log.dst->uctx = &default_log; |
82 | 0 | request->log.dst->lvl = fr_debug_lvl; |
83 | 0 | } |
84 | | |
85 | | /** Prepend another logging destination to the list. |
86 | | * |
87 | | |
88 | | * @param request the request |
89 | | * @param log_dst the logging destination |
90 | | * @param lvl the new request debug lvl |
91 | | */ |
92 | | void request_log_prepend(request_t *request, fr_log_t *log_dst, fr_log_lvl_t lvl) |
93 | 0 | { |
94 | 0 | log_dst_t *dst; |
95 | |
|
96 | 0 | if (lvl == L_DBG_LVL_DISABLE) { |
97 | 0 | while (request->log.dst) { |
98 | 0 | dst = request->log.dst->next; |
99 | 0 | talloc_free(request->log.dst); |
100 | 0 | request->log.dst = dst; |
101 | 0 | } |
102 | 0 | request->log.lvl = L_DBG_LVL_OFF; |
103 | 0 | return; |
104 | 0 | } |
105 | | |
106 | | /* |
107 | | * Remove a particular log destination. |
108 | | */ |
109 | 0 | if (lvl == L_DBG_LVL_OFF) { |
110 | 0 | log_dst_t **last; |
111 | |
|
112 | 0 | last = &request->log.dst; |
113 | 0 | while (*last) { |
114 | 0 | dst = *last; |
115 | 0 | if (((fr_log_t *)dst->uctx)->parent == log_dst) { |
116 | 0 | *last = dst->next; |
117 | 0 | talloc_free(dst); |
118 | 0 | if (!request->log.dst) request->log.lvl = L_DBG_LVL_OFF; |
119 | 0 | return; |
120 | 0 | } |
121 | | |
122 | 0 | last = &(dst->next); |
123 | 0 | } |
124 | | |
125 | 0 | return; |
126 | 0 | } |
127 | | |
128 | | /* |
129 | | * Change the debug level of an existing destination. |
130 | | */ |
131 | 0 | for (dst = request->log.dst; dst != NULL; dst = dst->next) { |
132 | 0 | if (((fr_log_t *)dst->uctx)->parent == log_dst) { |
133 | 0 | dst->lvl = lvl; |
134 | 0 | if (lvl > request->log.lvl) request->log.lvl = lvl; |
135 | 0 | return; |
136 | 0 | } |
137 | 0 | } |
138 | | |
139 | | /* |
140 | | * Not found, add a new log destination. |
141 | | */ |
142 | 0 | MEM(dst = talloc_zero(request, log_dst_t)); |
143 | |
|
144 | 0 | dst->func = vlog_request; |
145 | 0 | dst->uctx = log_dst; |
146 | |
|
147 | 0 | dst->lvl = lvl; |
148 | 0 | if (lvl > request->log.lvl) request->log.lvl = lvl; |
149 | 0 | dst->next = request->log.dst; |
150 | |
|
151 | 0 | request->log.dst = dst; |
152 | 0 | } |
153 | | |
154 | | static inline void CC_HINT(always_inline) request_log_init_child(request_t *child, request_t const *parent) |
155 | 0 | { |
156 | | /* |
157 | | * Copy debug information. |
158 | | */ |
159 | 0 | memcpy(&(child->log), &(parent->log), sizeof(child->log)); |
160 | 0 | child->log.indent.unlang = 0; /* Apart from the indent which we reset */ |
161 | 0 | child->log.indent.module = 0; /* Apart from the indent which we reset */ |
162 | 0 | child->log.lvl = parent->log.lvl; |
163 | 0 | } |
164 | | |
165 | | static inline void CC_HINT(always_inline) request_log_init_detachable(request_t *child, request_t const *parent) |
166 | 0 | { |
167 | 0 | request_log_init_child(child, parent); |
168 | | |
169 | | /* |
170 | | * Ensure that we use our own version of the logging |
171 | | * information, and not the original request one. |
172 | | */ |
173 | 0 | child->log.dst = talloc_zero(child, log_dst_t); |
174 | 0 | memcpy(child->log.dst, parent->log.dst, sizeof(*child->log.dst)); |
175 | 0 | } |
176 | | |
177 | | static inline CC_HINT(always_inline) int request_detachable_init(request_t *child, request_t *parent) |
178 | 0 | { |
179 | | /* |
180 | | * Associate the child with the parent, using the child's |
181 | | * pointer as a unique identifier. Free it if the parent |
182 | | * goes away, but don't persist it across |
183 | | * challenge-response boundaries. |
184 | | */ |
185 | 0 | if (request_data_talloc_add(parent, child, 0, request_t, child, true, true, false) < 0) return -1; |
186 | | |
187 | 0 | return 0; |
188 | 0 | } |
189 | | |
190 | | static inline CC_HINT(always_inline) int request_child_init(request_t *child, request_t *parent) |
191 | 0 | { |
192 | 0 | child->number = parent->child_number++; |
193 | 0 | if (!child->proto_dict) { |
194 | 0 | child->proto_dict = parent->proto_dict; |
195 | 0 | child->local_dict = parent->proto_dict; |
196 | 0 | } |
197 | |
|
198 | 0 | if ((parent->seq_start == 0) || (parent->number == parent->seq_start)) { |
199 | 0 | child->name = talloc_typed_asprintf(child, "%s.%" PRIu64, parent->name, child->number); |
200 | 0 | } else { |
201 | 0 | child->name = talloc_typed_asprintf(child, "(%s,%" PRIu64 ").%" PRIu64, |
202 | 0 | parent->name, parent->seq_start, child->number); |
203 | 0 | } |
204 | 0 | child->seq_start = 0; /* children always start with their own sequence */ |
205 | 0 | child->parent = parent; |
206 | | |
207 | | /* |
208 | | * For new server support. |
209 | | * |
210 | | * FIXME: Key instead off of a "virtual server" data structure. |
211 | | * |
212 | | * FIXME: Permit different servers for inner && outer sessions? |
213 | | */ |
214 | 0 | child->packet = fr_packet_alloc(child, true); |
215 | 0 | if (!child->packet) { |
216 | 0 | talloc_free(child); |
217 | 0 | return -1; |
218 | 0 | } |
219 | | |
220 | 0 | child->reply = fr_packet_alloc(child, false); |
221 | 0 | if (!child->reply) { |
222 | 0 | talloc_free(child); |
223 | 0 | return -1; |
224 | 0 | } |
225 | | |
226 | 0 | return 0; |
227 | 0 | } |
228 | | |
229 | | /** Setup logging and other fields for a request |
230 | | * |
231 | | * @param[in] file the request was allocated in. |
232 | | * @param[in] line the request was allocated on. |
233 | | * @param[in] request to (re)-initialise. |
234 | | * @param[in] type of request to initialise. |
235 | | * @param[in] args Other optional arguments. |
236 | | */ |
237 | | int _request_init(char const *file, int line, |
238 | | request_t *request, request_type_t type, |
239 | | request_init_args_t const *args) |
240 | 0 | { |
241 | 0 | fr_dict_t const *dict; |
242 | | |
243 | | /* |
244 | | * Sanity checks for different requests types |
245 | | */ |
246 | 0 | switch (type) { |
247 | 0 | case REQUEST_TYPE_EXTERNAL: |
248 | 0 | fr_assert(args); |
249 | |
|
250 | 0 | if (!fr_cond_assert_msg(!args->parent, "External requests must NOT have a parent")) return -1; |
251 | | |
252 | 0 | fr_assert(args->namespace); |
253 | |
|
254 | 0 | dict = args->namespace; |
255 | 0 | break; |
256 | | |
257 | 0 | case REQUEST_TYPE_INTERNAL: |
258 | 0 | if (!args || !args->namespace) { |
259 | 0 | dict = fr_dict_internal(); |
260 | 0 | } else { |
261 | 0 | dict = args->namespace; |
262 | 0 | } |
263 | 0 | break; |
264 | | |
265 | 0 | case REQUEST_TYPE_DETACHED: |
266 | 0 | fr_assert_fail("Detached requests should start as type == REQUEST_TYPE_INTERNAL, " |
267 | 0 | "args->detachable and be detached later"); |
268 | 0 | return -1; |
269 | | |
270 | | /* Quiet GCC */ |
271 | 0 | default: |
272 | 0 | fr_assert_fail("Invalid request type"); |
273 | 0 | return -1; |
274 | 0 | } |
275 | | |
276 | 0 | *request = (request_t){ |
277 | 0 | #ifndef NDEBUG |
278 | 0 | .magic = REQUEST_MAGIC, |
279 | 0 | #endif |
280 | 0 | .type = type, |
281 | 0 | .master_state = REQUEST_ACTIVE, |
282 | 0 | .proto_dict = fr_dict_proto_dict(dict), |
283 | 0 | .local_dict = dict, |
284 | 0 | .component = "<pre-core>", |
285 | 0 | .flags = { |
286 | 0 | .detachable = args && args->detachable, |
287 | 0 | }, |
288 | 0 | .alloc_file = file, |
289 | 0 | .alloc_line = line |
290 | 0 | }; |
291 | | |
292 | | |
293 | | /* |
294 | | * Initialise the stack |
295 | | */ |
296 | 0 | MEM(request->stack = unlang_interpret_stack_alloc(request)); |
297 | | |
298 | | /* |
299 | | * Initialise the request data list |
300 | | */ |
301 | 0 | request_data_list_init(&request->data); |
302 | |
|
303 | 0 | { |
304 | 0 | fr_pair_t *vp = NULL, *pair_root; |
305 | | |
306 | | /* |
307 | | * Alloc the pair root this is a |
308 | | * special pair which does not |
309 | | * free its children when it is |
310 | | * freed. |
311 | | */ |
312 | 0 | pair_root = fr_pair_root_afrom_da(request, request_attr_root); |
313 | 0 | if (unlikely(!pair_root)) return -1; |
314 | 0 | request->pair_root = pair_root; |
315 | | |
316 | | /* |
317 | | * Copy all the pair lists over into |
318 | | * the request. We then check for |
319 | | * the any uninitialised lists and |
320 | | * create them locally. |
321 | | */ |
322 | 0 | if (args) memcpy(&request->pair_list, &args->pair_list, sizeof(request->pair_list)); |
323 | |
|
324 | 0 | #define list_init(_ctx, _list) \ |
325 | 0 | do { \ |
326 | 0 | vp = fr_pair_afrom_da(_ctx, request_attr_##_list); \ |
327 | 0 | if (unlikely(!vp)) { \ |
328 | 0 | talloc_free(pair_root); \ |
329 | 0 | memset(&request->pair_list, 0, sizeof(request->pair_list)); \ |
330 | 0 | return -1; \ |
331 | 0 | } \ |
332 | 0 | fr_pair_append(&pair_root->children, vp); \ |
333 | 0 | request->pair_list._list = vp; \ |
334 | 0 | } while(0) |
335 | |
|
336 | 0 | if (!request->pair_list.request) list_init(request->pair_root, request); |
337 | 0 | if (!request->pair_list.reply) list_init(request->pair_root, reply); |
338 | 0 | if (!request->pair_list.control) list_init(request->pair_root, control); |
339 | 0 | if (!request->pair_list.local) list_init(request->pair_root, local); |
340 | 0 | if (!request->pair_list.state) { |
341 | 0 | list_init(NULL, state); |
342 | 0 | #ifndef NDEBUG |
343 | 0 | talloc_set_destructor(request->pair_list.state, _state_ctx_free); |
344 | 0 | #endif |
345 | 0 | } |
346 | 0 | } |
347 | | |
348 | | /* |
349 | | * Initialise packets and additional |
350 | | * fields if this is going to be a |
351 | | * child request. |
352 | | */ |
353 | 0 | if (args && args->parent) { |
354 | 0 | if (request_child_init(request, args->parent) < 0) return -1; |
355 | | |
356 | 0 | if (args->detachable) { |
357 | 0 | if (request_detachable_init(request, args->parent) < 0) return -1; |
358 | 0 | request_log_init_detachable(request, args->parent); |
359 | 0 | } else { |
360 | 0 | request_log_init_child(request, args->parent); |
361 | 0 | } |
362 | 0 | } else { |
363 | 0 | request_log_init_orphan(request); |
364 | 0 | } |
365 | | |
366 | | /* |
367 | | * This is only used by src/lib/io/worker.c |
368 | | */ |
369 | 0 | fr_dlist_entry_init(&request->listen_entry); |
370 | |
|
371 | 0 | return 0; |
372 | 0 | } |
373 | | |
374 | | /** Callback for slabs to deinitialise the request |
375 | | * |
376 | | * Does not need to be called for local requests. |
377 | | * |
378 | | * @param[in] request deinitialise |
379 | | * @return |
380 | | * - 0 in the request was deinitialised. |
381 | | * - -1 if the request is in an unexpected state. |
382 | | */ |
383 | | int request_slab_deinit(request_t *request) |
384 | 0 | { |
385 | 0 | fr_assert_msg(!fr_timer_armed(request->timeout), |
386 | 0 | "alloced %s:%i: %s still in the timeout sublist", |
387 | 0 | request->alloc_file, |
388 | 0 | request->alloc_line, |
389 | 0 | request->name ? request->name : "(null)"); |
390 | 0 | fr_assert_msg(!fr_heap_entry_inserted(request->runnable), |
391 | 0 | "alloced %s:%i: %s still in the runnable heap ID %i", |
392 | 0 | request->alloc_file, |
393 | 0 | request->alloc_line, |
394 | 0 | request->name ? request->name : "(null)", request->runnable); |
395 | |
|
396 | 0 | RDEBUG3("Request deinitialising (%p)", request); |
397 | | |
398 | | /* |
399 | | * state_ctx is parented separately. |
400 | | */ |
401 | 0 | if (request->session_state_ctx) TALLOC_FREE(request->session_state_ctx); |
402 | | |
403 | | /* |
404 | | * Zero out everything. |
405 | | */ |
406 | 0 | memset(request, 0, sizeof(*request)); |
407 | |
|
408 | 0 | #ifndef NDEBUG |
409 | 0 | request->component = "free_list"; |
410 | 0 | request->runnable = FR_HEAP_INDEX_INVALID; |
411 | 0 | request->magic = 0x01020304; /* set the request to be nonsense */ |
412 | 0 | #endif |
413 | |
|
414 | 0 | return 0; |
415 | 0 | } |
416 | | |
417 | | static inline CC_HINT(always_inline) request_t *request_alloc_pool(TALLOC_CTX *ctx) |
418 | 0 | { |
419 | 0 | request_t *request; |
420 | | |
421 | | /* |
422 | | * Only allocate requests in the NULL |
423 | | * ctx. There's no scenario where it's |
424 | | * appropriate to allocate them in a |
425 | | * pool, and using a strict talloc |
426 | | * hierarchy means that child requests |
427 | | * cannot be returned to a free list |
428 | | * and would have to be freed. |
429 | | */ |
430 | 0 | MEM(request = talloc_pooled_object(ctx, request_t, |
431 | 0 | REQUEST_POOL_NUM_OBJECTS, |
432 | 0 | REQUEST_POOL_SIZE)); |
433 | 0 | fr_assert(ctx != request); |
434 | |
|
435 | 0 | return request; |
436 | 0 | } |
437 | | |
438 | | static int _request_local_free(request_t *request) |
439 | 0 | { |
440 | 0 | RDEBUG4("Local request freed (%p)", request); |
441 | | |
442 | | /* |
443 | | * Ensure anything that might reference the request is |
444 | | * freed before it is. |
445 | | */ |
446 | 0 | talloc_free_children(request); |
447 | | |
448 | | /* |
449 | | * state_ctx is parented separately. |
450 | | * |
451 | | * The reason why it's OK to do this, is if the state attributes |
452 | | * need to persist across requests, they will already have been |
453 | | * moved to a fr_state_entry_t, with the state pointers in the |
454 | | * request being set to NULL, before the request is freed/ |
455 | | * |
456 | | * Note also that we do NOT call TALLOC_FREE(), which |
457 | | * sets state_ctx=NULL. We don't control the order in |
458 | | * which talloc frees the children. And the parents |
459 | | * state_ctx pointer needs to stick around so that all of |
460 | | * the children can check it. |
461 | | * |
462 | | * If this assertion hits, it means that someone didn't |
463 | | * call fr_state_store_in_parent() |
464 | | */ |
465 | 0 | if (request->session_state_ctx) { |
466 | 0 | fr_assert(!request->parent || (request->session_state_ctx != request->parent->session_state_ctx)); |
467 | |
|
468 | 0 | talloc_free(request->session_state_ctx); |
469 | 0 | } |
470 | |
|
471 | 0 | #ifndef NDEBUG |
472 | 0 | request->magic = 0x01020304; /* set the request to be nonsense */ |
473 | 0 | #endif |
474 | |
|
475 | 0 | return 0; |
476 | 0 | } |
477 | | |
478 | | /** Allocate a request that's not in the free list |
479 | | * |
480 | | * This can be useful if modules need a persistent request for their own purposes |
481 | | * which needs to be outside of the normal free list, so that it can be freed |
482 | | * when the module requires, not when the thread destructor runs. |
483 | | */ |
484 | | request_t *_request_local_alloc(char const *file, int line, TALLOC_CTX *ctx, |
485 | | request_type_t type, request_init_args_t const *args) |
486 | 0 | { |
487 | 0 | request_t *request; |
488 | |
|
489 | 0 | request = request_alloc_pool(ctx); |
490 | 0 | if (_request_init(file, line, request, type, args) < 0) return NULL; |
491 | | |
492 | 0 | talloc_set_destructor(request, _request_local_free); |
493 | |
|
494 | 0 | return request; |
495 | 0 | } |
496 | | |
497 | | /** Replace the session_state_ctx with a new one. |
498 | | * |
499 | | * NOTHING should rewrite request->session_state_ctx. |
500 | | * |
501 | | * It's now a pair, and is stored in request->pair_root. |
502 | | * So it's wrong for anyone other than this function to play games with it. |
503 | | * |
504 | | * @param[in] request to replace the state of. |
505 | | * @param[in] new_state state to assign to the request. |
506 | | * May be NULL in which case a new_state state will |
507 | | * be alloced and assigned. |
508 | | * |
509 | | * @return the fr_pair_t containing the old state list. |
510 | | */ |
511 | | fr_pair_t *request_state_replace(request_t *request, fr_pair_t *new_state) |
512 | | { |
513 | | fr_pair_t *old = request->session_state_ctx; |
514 | | |
515 | | fr_assert(request->session_state_ctx != NULL); |
516 | | fr_assert(request->session_state_ctx != new_state); |
517 | | |
518 | | fr_pair_remove(&request->pair_root->children, old); |
519 | | |
520 | | /* |
521 | | * Save (or delete) the existing state, and re-initialize |
522 | | * it with a brand new one. |
523 | | */ |
524 | | if (!new_state) MEM(new_state = fr_pair_afrom_da(NULL, request_attr_state)); |
525 | | |
526 | | request->session_state_ctx = new_state; |
527 | | |
528 | | fr_pair_append(&request->pair_root->children, new_state); |
529 | | |
530 | | return old; |
531 | | } |
532 | | |
533 | | /** Unlink a subrequest from its parent |
534 | | * |
535 | | * @note This should be used for requests in preparation for freeing them. |
536 | | * |
537 | | * @param[in] child request to unlink. |
538 | | * @return |
539 | | * - 0 on success. |
540 | | * - -1 on failure. |
541 | | */ |
542 | | int request_detach(request_t *child) |
543 | 0 | { |
544 | 0 | request_t *request = child->parent; |
545 | | |
546 | | /* |
547 | | * Already detached or not detachable |
548 | | */ |
549 | 0 | if (request_is_detached(child)) return 0; |
550 | | |
551 | 0 | if (!request_is_detachable(child)) { |
552 | 0 | fr_strerror_const("Request is not detachable"); |
553 | 0 | return -1; |
554 | 0 | } |
555 | | |
556 | | /* |
557 | | * Unlink the child from the parent. |
558 | | */ |
559 | 0 | request_data_get(request, child, 0); |
560 | |
|
561 | 0 | child->parent = NULL; |
562 | | |
563 | | /* |
564 | | * Request is now detached |
565 | | */ |
566 | 0 | child->type = REQUEST_TYPE_DETACHED; |
567 | | |
568 | | /* |
569 | | * ...and is no longer detachable. |
570 | | */ |
571 | 0 | child->flags.detachable = 0; |
572 | |
|
573 | 0 | return 0; |
574 | 0 | } |
575 | | |
576 | | static int _request_global_free(UNUSED void *uctx) |
577 | 0 | { |
578 | 0 | fr_dict_autofree(request_dict); |
579 | 0 | return 0; |
580 | 0 | } |
581 | | |
582 | | static int _request_global_init(UNUSED void *uctx) |
583 | 0 | { |
584 | 0 | if (fr_dict_autoload(request_dict) < 0) { |
585 | 0 | PERROR("%s", __FUNCTION__); |
586 | 0 | return -1; |
587 | 0 | } |
588 | 0 | if (fr_dict_attr_autoload(request_dict_attr) < 0) { |
589 | 0 | PERROR("%s", __FUNCTION__); |
590 | 0 | fr_dict_autofree(request_dict); |
591 | 0 | return -1; |
592 | 0 | } |
593 | 0 | return 0; |
594 | 0 | } |
595 | | |
596 | | int request_global_init(void) |
597 | 0 | { |
598 | 0 | int ret; |
599 | 0 | fr_atexit_global_once_ret(&ret, _request_global_init, _request_global_free, NULL); |
600 | 0 | return ret; |
601 | 0 | } |
602 | | |
603 | | #ifdef WITH_VERIFY_PTR |
604 | | /* |
605 | | * Verify a packet. |
606 | | */ |
607 | | static void packet_verify(char const *file, int line, |
608 | | request_t const *request, fr_packet_t const *packet, fr_pair_list_t *list, char const *type) |
609 | 0 | { |
610 | 0 | TALLOC_CTX *parent; |
611 | |
|
612 | 0 | fr_fatal_assert_msg(packet, "CONSISTENCY CHECK FAILED %s[%i]: fr_packet_t %s pointer was NULL", |
613 | 0 | file, line, type); |
614 | |
|
615 | 0 | parent = talloc_parent(packet); |
616 | 0 | if (parent != request) { |
617 | 0 | fr_log_talloc_report(packet); |
618 | 0 | if (parent) fr_log_talloc_report(parent); |
619 | | |
620 | |
|
621 | 0 | fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%i]: Expected fr_packet_t %s to be parented " |
622 | 0 | "by %p (%s), but parented by %p (%s)", |
623 | 0 | file, line, type, request, talloc_get_name(request), |
624 | 0 | parent, parent ? talloc_get_name(parent) : "NULL"); |
625 | 0 | } |
626 | | |
627 | | /* |
628 | | * Enforce nesting at the top level. This catches minor programming bugs in the server core. |
629 | | * |
630 | | * If we care more, we could do these checks recursively. But the tmpl_tokenize code already |
631 | | * enforces parent / child namespaces. So the end user shouldn't be able to break the parenting. |
632 | | * |
633 | | * This code really only checks for programming bugs where the C code creates a pair, and then |
634 | | * adds it to the wrong list. This was happening during the transition from flat to nested, as |
635 | | * the code was in the middle of being fixed. It should only happen now if the programmer |
636 | | * forgets, and uses the wrong APIs. |
637 | | */ |
638 | 0 | fr_pair_list_foreach(list, vp) { |
639 | 0 | if (vp->da->flags.is_raw) continue; |
640 | | |
641 | 0 | if (vp->da->flags.internal) continue; |
642 | | |
643 | 0 | if (vp->da->depth > 1) { |
644 | 0 | fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%i]: Expected fr_pair_t %s to be parented " |
645 | 0 | "by (%s), but it is instead at the top-level %s list", |
646 | 0 | file, line, vp->da->name, vp->da->parent->name, type); |
647 | 0 | } |
648 | 0 | } |
649 | |
|
650 | 0 | PACKET_VERIFY(packet); |
651 | 0 | } |
652 | | |
653 | | /* |
654 | | * Catch horrible talloc errors. |
655 | | */ |
656 | | void request_verify(char const *file, int line, request_t const *request) |
657 | 0 | { |
658 | 0 | request_data_t *rd = NULL; |
659 | |
|
660 | 0 | fr_fatal_assert_msg(request, "CONSISTENCY CHECK FAILED %s[%i]: request_t pointer was NULL", file, line); |
661 | |
|
662 | 0 | (void) talloc_get_type_abort_const(request, request_t); |
663 | |
|
664 | 0 | fr_assert(request->magic == REQUEST_MAGIC); |
665 | |
|
666 | 0 | (void)talloc_get_type_abort(request->request_ctx, fr_pair_t); |
667 | 0 | fr_pair_list_verify(file, line, request->request_ctx, &request->request_pairs, true); |
668 | 0 | (void)talloc_get_type_abort(request->reply_ctx, fr_pair_t); |
669 | 0 | fr_pair_list_verify(file, line, request->reply_ctx, &request->reply_pairs, true); |
670 | 0 | (void)talloc_get_type_abort(request->control_ctx, fr_pair_t); |
671 | 0 | fr_pair_list_verify(file, line, request->control_ctx, &request->control_pairs, true); |
672 | 0 | (void)talloc_get_type_abort(request->session_state_ctx, fr_pair_t); |
673 | |
|
674 | 0 | #ifndef NDEBUG |
675 | 0 | { |
676 | 0 | TALLOC_CTX *parent = talloc_parent(request->session_state_ctx); |
677 | |
|
678 | 0 | fr_assert_msg((parent == NULL) || (parent == talloc_null_ctx()), |
679 | 0 | "session_state_ctx must not be parented by another chunk, but is parented by %s", |
680 | 0 | talloc_get_name(talloc_parent(request->session_state_ctx))); |
681 | 0 | } |
682 | 0 | #endif |
683 | |
|
684 | 0 | fr_pair_list_verify(file, line, request->session_state_ctx, &request->session_state_pairs, true); |
685 | 0 | fr_pair_list_verify(file, line, request->local_ctx, &request->local_pairs, true); |
686 | |
|
687 | 0 | fr_assert(request->proto_dict != NULL); |
688 | 0 | fr_assert(request->local_dict != NULL); |
689 | |
|
690 | 0 | if (request->packet) { |
691 | 0 | packet_verify(file, line, request, request->packet, &request->request_pairs, "request"); |
692 | 0 | } |
693 | 0 | if (request->reply) { |
694 | 0 | packet_verify(file, line, request, request->reply, &request->reply_pairs, "reply"); |
695 | 0 | } |
696 | |
|
697 | 0 | if (request->async) { |
698 | 0 | (void) talloc_get_type_abort(request->async, fr_async_t); |
699 | 0 | fr_assert(talloc_parent(request->async) == request); |
700 | 0 | } |
701 | |
|
702 | 0 | while ((rd = fr_dlist_next(&request->data, rd))) { |
703 | 0 | (void) talloc_get_type_abort(rd, request_data_t); |
704 | |
|
705 | 0 | if (request_data_persistable(rd)) { |
706 | 0 | fr_assert(request->session_state_ctx); |
707 | 0 | fr_assert(talloc_parent(rd) == request->session_state_ctx); |
708 | 0 | } else { |
709 | | fr_assert(talloc_parent(rd) == request); |
710 | 0 | } |
711 | 0 | } |
712 | 0 | } |
713 | | #endif |