/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: 5653d8d5a2f7eec40390fb4dbfb404fab180023e $ |
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: 5653d8d5a2f7eec40390fb4dbfb404fab180023e $") |
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 | | { |
167 | | 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 | | if (!parent->log.dst) return; |
174 | | |
175 | | MEM(child->log.dst = talloc_zero(child, log_dst_t)); |
176 | | memcpy(child->log.dst, parent->log.dst, sizeof(*child->log.dst)); |
177 | | } |
178 | | |
179 | | static inline CC_HINT(always_inline) int request_detachable_init(request_t *child, request_t *parent) |
180 | 0 | { |
181 | | /* |
182 | | * Associate the child with the parent, using the child's |
183 | | * pointer as a unique identifier. Free it if the parent |
184 | | * goes away, but don't persist it across |
185 | | * challenge-response boundaries. |
186 | | */ |
187 | 0 | if (request_data_talloc_add(parent, child, 0, request_t, child, true, true, false) < 0) return -1; |
188 | | |
189 | 0 | return 0; |
190 | 0 | } |
191 | | |
192 | | static inline CC_HINT(always_inline) int request_child_init(request_t *child, request_t *parent) |
193 | 0 | { |
194 | 0 | child->number = parent->child_number++; |
195 | 0 | if (!child->proto_dict) { |
196 | 0 | child->proto_dict = parent->proto_dict; |
197 | 0 | child->local_dict = parent->proto_dict; |
198 | 0 | } |
199 | |
|
200 | 0 | if ((parent->seq_start == 0) || (parent->number == parent->seq_start)) { |
201 | 0 | child->name = talloc_typed_asprintf(child, "%s.%" PRIu64, parent->name, child->number); |
202 | 0 | } else { |
203 | 0 | child->name = talloc_typed_asprintf(child, "(%s,%" PRIu64 ").%" PRIu64, |
204 | 0 | parent->name, parent->seq_start, child->number); |
205 | 0 | } |
206 | 0 | child->seq_start = 0; /* children always start with their own sequence */ |
207 | 0 | child->parent = parent; |
208 | | |
209 | | /* |
210 | | * For new server support. |
211 | | * |
212 | | * FIXME: Key instead off of a "virtual server" data structure. |
213 | | * |
214 | | * FIXME: Permit different servers for inner && outer sessions? |
215 | | */ |
216 | 0 | child->packet = fr_packet_alloc(child, true); |
217 | 0 | if (!child->packet) { |
218 | 0 | talloc_free(child); |
219 | 0 | return -1; |
220 | 0 | } |
221 | | |
222 | 0 | child->reply = fr_packet_alloc(child, false); |
223 | 0 | if (!child->reply) { |
224 | 0 | talloc_free(child); |
225 | 0 | return -1; |
226 | 0 | } |
227 | | |
228 | 0 | return 0; |
229 | 0 | } |
230 | | |
231 | | /** Setup logging and other fields for a request |
232 | | * |
233 | | * @param[in] file the request was allocated in. |
234 | | * @param[in] line the request was allocated on. |
235 | | * @param[in] request to (re)-initialise. |
236 | | * @param[in] type of request to initialise. |
237 | | * @param[in] args Other optional arguments. |
238 | | */ |
239 | | int _request_init(char const *file, int line, |
240 | | request_t *request, request_type_t type, |
241 | | request_init_args_t const *args) |
242 | 0 | { |
243 | 0 | fr_dict_t const *dict; |
244 | | |
245 | | /* |
246 | | * Sanity checks for different requests types |
247 | | */ |
248 | 0 | switch (type) { |
249 | 0 | case REQUEST_TYPE_EXTERNAL: |
250 | 0 | fr_assert(args); |
251 | |
|
252 | 0 | if (!fr_cond_assert_msg(!args->parent, "External requests must NOT have a parent")) return -1; |
253 | | |
254 | 0 | fr_assert(args->namespace); |
255 | |
|
256 | 0 | dict = args->namespace; |
257 | 0 | break; |
258 | | |
259 | 0 | case REQUEST_TYPE_INTERNAL: |
260 | 0 | if (!args || !args->namespace) { |
261 | 0 | dict = fr_dict_internal(); |
262 | 0 | } else { |
263 | 0 | dict = args->namespace; |
264 | 0 | } |
265 | 0 | break; |
266 | | |
267 | 0 | case REQUEST_TYPE_DETACHED: |
268 | 0 | fr_assert_fail("Detached requests should start as type == REQUEST_TYPE_INTERNAL, " |
269 | 0 | "args->detachable and be detached later"); |
270 | 0 | return -1; |
271 | | |
272 | | /* Quiet GCC */ |
273 | 0 | default: |
274 | 0 | fr_assert_fail("Invalid request type"); |
275 | 0 | return -1; |
276 | 0 | } |
277 | | |
278 | 0 | *request = (request_t){ |
279 | 0 | #ifndef NDEBUG |
280 | 0 | .magic = REQUEST_MAGIC, |
281 | 0 | #endif |
282 | 0 | .type = type, |
283 | 0 | .master_state = REQUEST_ACTIVE, |
284 | 0 | .proto_dict = fr_dict_proto_dict(dict), |
285 | 0 | .local_dict = dict, |
286 | 0 | .component = "<pre-core>", |
287 | 0 | .flags = { |
288 | 0 | .detachable = args && args->detachable, |
289 | 0 | }, |
290 | 0 | .alloc_file = file, |
291 | 0 | .alloc_line = line |
292 | 0 | }; |
293 | | |
294 | | |
295 | | /* |
296 | | * Initialise the stack |
297 | | */ |
298 | 0 | MEM(request->stack = unlang_interpret_stack_alloc(request)); |
299 | | |
300 | | /* |
301 | | * Initialise the request data list |
302 | | */ |
303 | 0 | request_data_list_init(&request->data); |
304 | |
|
305 | 0 | { |
306 | 0 | fr_pair_t *vp = NULL, *pair_root; |
307 | | |
308 | | /* |
309 | | * Alloc the pair root this is a |
310 | | * special pair which does not |
311 | | * free its children when it is |
312 | | * freed. |
313 | | */ |
314 | 0 | pair_root = fr_pair_root_afrom_da(request, request_attr_root); |
315 | 0 | if (unlikely(!pair_root)) return -1; |
316 | 0 | request->pair_root = pair_root; |
317 | | |
318 | | /* |
319 | | * Copy all the pair lists over into |
320 | | * the request. We then check for |
321 | | * the any uninitialised lists and |
322 | | * create them locally. |
323 | | */ |
324 | 0 | if (args) memcpy(&request->pair_list, &args->pair_list, sizeof(request->pair_list)); |
325 | |
|
326 | 0 | #define list_init(_ctx, _list) \ |
327 | 0 | do { \ |
328 | 0 | vp = fr_pair_afrom_da(_ctx, request_attr_##_list); \ |
329 | 0 | if (unlikely(!vp)) { \ |
330 | 0 | talloc_free(pair_root); \ |
331 | 0 | memset(&request->pair_list, 0, sizeof(request->pair_list)); \ |
332 | 0 | return -1; \ |
333 | 0 | } \ |
334 | 0 | fr_pair_append(&pair_root->children, vp); \ |
335 | 0 | request->pair_list._list = vp; \ |
336 | 0 | } while(0) |
337 | |
|
338 | 0 | if (!request->pair_list.request) list_init(request->pair_root, request); |
339 | 0 | if (!request->pair_list.reply) list_init(request->pair_root, reply); |
340 | 0 | if (!request->pair_list.control) list_init(request->pair_root, control); |
341 | 0 | if (!request->pair_list.local) list_init(request->pair_root, local); |
342 | 0 | if (!request->pair_list.state) { |
343 | 0 | list_init(NULL, state); |
344 | 0 | #ifndef NDEBUG |
345 | 0 | talloc_set_destructor(request->pair_list.state, _state_ctx_free); |
346 | 0 | #endif |
347 | 0 | } |
348 | 0 | } |
349 | | |
350 | | /* |
351 | | * Initialise packets and additional |
352 | | * fields if this is going to be a |
353 | | * child request. |
354 | | */ |
355 | 0 | if (args && args->parent) { |
356 | 0 | if (request_child_init(request, args->parent) < 0) return -1; |
357 | | |
358 | 0 | if (args->detachable) { |
359 | 0 | if (request_detachable_init(request, args->parent) < 0) return -1; |
360 | 0 | request_log_init_detachable(request, args->parent); |
361 | 0 | } else { |
362 | 0 | request_log_init_child(request, args->parent); |
363 | 0 | } |
364 | 0 | } else { |
365 | 0 | request_log_init_orphan(request); |
366 | 0 | } |
367 | | |
368 | | /* |
369 | | * This is only used by src/lib/io/worker.c |
370 | | */ |
371 | 0 | fr_dlist_entry_init(&request->listen_entry); |
372 | |
|
373 | 0 | return 0; |
374 | 0 | } |
375 | | |
376 | | /** Callback for slabs to deinitialise the request |
377 | | * |
378 | | * Does not need to be called for local requests. |
379 | | * |
380 | | * @param[in] request deinitialise |
381 | | * @return |
382 | | * - 0 in the request was deinitialised. |
383 | | * - -1 if the request is in an unexpected state. |
384 | | */ |
385 | | int request_slab_deinit(request_t *request) |
386 | 0 | { |
387 | 0 | fr_assert_msg(!fr_timer_armed(request->timeout), |
388 | 0 | "alloced %s:%i: %s still in the timeout sublist", |
389 | 0 | request->alloc_file, |
390 | 0 | request->alloc_line, |
391 | 0 | request->name ? request->name : "(null)"); |
392 | 0 | fr_assert_msg(!fr_heap_entry_inserted(request->runnable), |
393 | 0 | "alloced %s:%i: %s still in the runnable heap ID %i", |
394 | 0 | request->alloc_file, |
395 | 0 | request->alloc_line, |
396 | 0 | request->name ? request->name : "(null)", request->runnable); |
397 | |
|
398 | 0 | RDEBUG3("Request deinitialising (%p)", request); |
399 | | |
400 | | /* |
401 | | * state_ctx is parented separately. |
402 | | */ |
403 | 0 | if (request->session_state_ctx) TALLOC_FREE(request->session_state_ctx); |
404 | | |
405 | | /* |
406 | | * Zero out everything. |
407 | | */ |
408 | 0 | memset(request, 0, sizeof(*request)); |
409 | |
|
410 | 0 | #ifndef NDEBUG |
411 | 0 | request->component = "free_list"; |
412 | 0 | request->runnable = FR_HEAP_INDEX_INVALID; |
413 | 0 | request->magic = 0x01020304; /* set the request to be nonsense */ |
414 | 0 | #endif |
415 | |
|
416 | 0 | return 0; |
417 | 0 | } |
418 | | |
419 | | static inline CC_HINT(always_inline) request_t *request_alloc_pool(TALLOC_CTX *ctx) |
420 | 0 | { |
421 | 0 | request_t *request; |
422 | | |
423 | | /* |
424 | | * Only allocate requests in the NULL |
425 | | * ctx. There's no scenario where it's |
426 | | * appropriate to allocate them in a |
427 | | * pool, and using a strict talloc |
428 | | * hierarchy means that child requests |
429 | | * cannot be returned to a free list |
430 | | * and would have to be freed. |
431 | | */ |
432 | 0 | MEM(request = talloc_pooled_object(ctx, request_t, |
433 | 0 | REQUEST_POOL_NUM_OBJECTS, |
434 | 0 | REQUEST_POOL_SIZE)); |
435 | 0 | fr_assert(ctx != request); |
436 | |
|
437 | 0 | return request; |
438 | 0 | } |
439 | | |
440 | | static int _request_local_free(request_t *request) |
441 | 0 | { |
442 | 0 | RDEBUG4("Local request freed (%p)", request); |
443 | | |
444 | | /* |
445 | | * Ensure anything that might reference the request is |
446 | | * freed before it is. |
447 | | */ |
448 | 0 | talloc_free_children(request); |
449 | | |
450 | | /* |
451 | | * state_ctx is parented separately. |
452 | | * |
453 | | * The reason why it's OK to do this, is if the state attributes |
454 | | * need to persist across requests, they will already have been |
455 | | * moved to a fr_state_entry_t, with the state pointers in the |
456 | | * request being set to NULL, before the request is freed/ |
457 | | * |
458 | | * Note also that we do NOT call TALLOC_FREE(), which |
459 | | * sets state_ctx=NULL. We don't control the order in |
460 | | * which talloc frees the children. And the parents |
461 | | * state_ctx pointer needs to stick around so that all of |
462 | | * the children can check it. |
463 | | * |
464 | | * If this assertion hits, it means that someone didn't |
465 | | * call fr_state_store_in_parent() |
466 | | */ |
467 | 0 | if (request->session_state_ctx) { |
468 | 0 | fr_assert(!request->parent || (request->session_state_ctx != request->parent->session_state_ctx)); |
469 | |
|
470 | 0 | talloc_free(request->session_state_ctx); |
471 | 0 | } |
472 | |
|
473 | 0 | #ifndef NDEBUG |
474 | 0 | request->magic = 0x01020304; /* set the request to be nonsense */ |
475 | 0 | #endif |
476 | |
|
477 | 0 | return 0; |
478 | 0 | } |
479 | | |
480 | | /** Allocate a request that's not in the free list |
481 | | * |
482 | | * This can be useful if modules need a persistent request for their own purposes |
483 | | * which needs to be outside of the normal free list, so that it can be freed |
484 | | * when the module requires, not when the thread destructor runs. |
485 | | */ |
486 | | request_t *_request_local_alloc(char const *file, int line, TALLOC_CTX *ctx, |
487 | | request_type_t type, request_init_args_t const *args) |
488 | 0 | { |
489 | 0 | request_t *request; |
490 | |
|
491 | 0 | request = request_alloc_pool(ctx); |
492 | 0 | if (_request_init(file, line, request, type, args) < 0) return NULL; |
493 | | |
494 | 0 | talloc_set_destructor(request, _request_local_free); |
495 | |
|
496 | 0 | return request; |
497 | 0 | } |
498 | | |
499 | | /** Replace the session_state_ctx with a new one. |
500 | | * |
501 | | * NOTHING should rewrite request->session_state_ctx. |
502 | | * |
503 | | * It's now a pair, and is stored in request->pair_root. |
504 | | * So it's wrong for anyone other than this function to play games with it. |
505 | | * |
506 | | * @param[in] request to replace the state of. |
507 | | * @param[in] new_state state to assign to the request. |
508 | | * May be NULL in which case a new_state state will |
509 | | * be alloced and assigned. |
510 | | * |
511 | | * @return the fr_pair_t containing the old state list. |
512 | | */ |
513 | | fr_pair_t *request_state_replace(request_t *request, fr_pair_t *new_state) |
514 | | { |
515 | | fr_pair_t *old = request->session_state_ctx; |
516 | | |
517 | | fr_assert(request->session_state_ctx != NULL); |
518 | | fr_assert(request->session_state_ctx != new_state); |
519 | | |
520 | | fr_pair_remove(&request->pair_root->children, old); |
521 | | |
522 | | /* |
523 | | * Save (or delete) the existing state, and re-initialize |
524 | | * it with a brand new one. |
525 | | */ |
526 | | if (!new_state) MEM(new_state = fr_pair_afrom_da(NULL, request_attr_state)); |
527 | | |
528 | | request->session_state_ctx = new_state; |
529 | | |
530 | | fr_pair_append(&request->pair_root->children, new_state); |
531 | | |
532 | | return old; |
533 | | } |
534 | | |
535 | | /** Unlink a subrequest from its parent |
536 | | * |
537 | | * @note This should be used for requests in preparation for freeing them. |
538 | | * |
539 | | * @param[in] child request to unlink. |
540 | | * @return |
541 | | * - 0 on success. |
542 | | * - -1 on failure. |
543 | | */ |
544 | | int request_detach(request_t *child) |
545 | 0 | { |
546 | 0 | request_t *request = child->parent; |
547 | | |
548 | | /* |
549 | | * Already detached or not detachable |
550 | | */ |
551 | 0 | if (request_is_detached(child)) return 0; |
552 | | |
553 | 0 | if (!request_is_detachable(child)) { |
554 | 0 | fr_strerror_const("Request is not detachable"); |
555 | 0 | return -1; |
556 | 0 | } |
557 | | |
558 | | /* |
559 | | * Unlink the child from the parent. |
560 | | */ |
561 | 0 | request_data_get(request, child, 0); |
562 | |
|
563 | 0 | child->parent = NULL; |
564 | | |
565 | | /* |
566 | | * Request is now detached |
567 | | */ |
568 | 0 | child->type = REQUEST_TYPE_DETACHED; |
569 | | |
570 | | /* |
571 | | * ...and is no longer detachable. |
572 | | */ |
573 | 0 | child->flags.detachable = 0; |
574 | |
|
575 | 0 | return 0; |
576 | 0 | } |
577 | | |
578 | | static int _request_global_free(UNUSED void *uctx) |
579 | 4 | { |
580 | 4 | fr_dict_autofree(request_dict); |
581 | 4 | return 0; |
582 | 4 | } |
583 | | |
584 | | static int _request_global_init(UNUSED void *uctx) |
585 | 4 | { |
586 | 4 | if (fr_dict_autoload(request_dict) < 0) { |
587 | 0 | PERROR("%s", __FUNCTION__); |
588 | 0 | return -1; |
589 | 0 | } |
590 | 4 | if (fr_dict_attr_autoload(request_dict_attr) < 0) { |
591 | 0 | PERROR("%s", __FUNCTION__); |
592 | 0 | fr_dict_autofree(request_dict); |
593 | 0 | return -1; |
594 | 0 | } |
595 | 4 | return 0; |
596 | 4 | } |
597 | | |
598 | | int request_global_init(void) |
599 | 4 | { |
600 | 4 | int ret; |
601 | 4 | fr_atexit_global_once_ret(&ret, _request_global_init, _request_global_free, NULL); |
602 | 4 | return ret; |
603 | 4 | } |
604 | | |
605 | | #ifdef WITH_VERIFY_PTR |
606 | | /* |
607 | | * Verify a packet. |
608 | | */ |
609 | | static void packet_verify(char const *file, int line, |
610 | | request_t const *request, fr_packet_t const *packet, fr_pair_list_t *list, char const *type) |
611 | 0 | { |
612 | 0 | TALLOC_CTX *parent; |
613 | |
|
614 | 0 | fr_fatal_assert_msg(packet, "CONSISTENCY CHECK FAILED %s[%i]: fr_packet_t %s pointer was NULL", |
615 | 0 | file, line, type); |
616 | |
|
617 | 0 | parent = talloc_parent(packet); |
618 | 0 | if (parent != request) { |
619 | 0 | fr_log_talloc_report(packet); |
620 | 0 | if (parent) fr_log_talloc_report(parent); |
621 | | |
622 | |
|
623 | 0 | fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%i]: Expected fr_packet_t %s to be parented " |
624 | 0 | "by %p (%s), but parented by %p (%s)", |
625 | 0 | file, line, type, request, talloc_get_name(request), |
626 | 0 | parent, parent ? talloc_get_name(parent) : "NULL"); |
627 | 0 | } |
628 | | |
629 | | /* |
630 | | * Enforce nesting at the top level. This catches minor programming bugs in the server core. |
631 | | * |
632 | | * If we care more, we could do these checks recursively. But the tmpl_tokenize code already |
633 | | * enforces parent / child namespaces. So the end user shouldn't be able to break the parenting. |
634 | | * |
635 | | * This code really only checks for programming bugs where the C code creates a pair, and then |
636 | | * adds it to the wrong list. This was happening during the transition from flat to nested, as |
637 | | * the code was in the middle of being fixed. It should only happen now if the programmer |
638 | | * forgets, and uses the wrong APIs. |
639 | | */ |
640 | 0 | fr_pair_list_foreach(list, vp) { |
641 | 0 | if (vp->da->flags.is_raw) continue; |
642 | | |
643 | 0 | if (vp->da->flags.internal) continue; |
644 | | |
645 | 0 | if (vp->da->depth > 1) { |
646 | 0 | fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%i]: Expected fr_pair_t %s to be parented " |
647 | 0 | "by (%s), but it is instead at the top-level %s list", |
648 | 0 | file, line, vp->da->name, vp->da->parent->name, type); |
649 | 0 | } |
650 | 0 | } |
651 | |
|
652 | 0 | PACKET_VERIFY(packet); |
653 | 0 | } |
654 | | |
655 | | /* |
656 | | * Catch horrible talloc errors. |
657 | | */ |
658 | | void request_verify(char const *file, int line, request_t const *request) |
659 | 0 | { |
660 | 0 | request_data_t *rd = NULL; |
661 | |
|
662 | 0 | fr_fatal_assert_msg(request, "CONSISTENCY CHECK FAILED %s[%i]: request_t pointer was NULL", file, line); |
663 | |
|
664 | 0 | (void) talloc_get_type_abort_const(request, request_t); |
665 | |
|
666 | 0 | fr_assert(request->magic == REQUEST_MAGIC); |
667 | |
|
668 | 0 | (void)talloc_get_type_abort(request->request_ctx, fr_pair_t); |
669 | 0 | fr_pair_list_verify(file, line, request->request_ctx, &request->request_pairs, true); |
670 | 0 | (void)talloc_get_type_abort(request->reply_ctx, fr_pair_t); |
671 | 0 | fr_pair_list_verify(file, line, request->reply_ctx, &request->reply_pairs, true); |
672 | 0 | (void)talloc_get_type_abort(request->control_ctx, fr_pair_t); |
673 | 0 | fr_pair_list_verify(file, line, request->control_ctx, &request->control_pairs, true); |
674 | 0 | (void)talloc_get_type_abort(request->session_state_ctx, fr_pair_t); |
675 | |
|
676 | 0 | #ifndef NDEBUG |
677 | 0 | { |
678 | 0 | TALLOC_CTX *parent = talloc_parent(request->session_state_ctx); |
679 | |
|
680 | 0 | fr_assert_msg((parent == NULL) || (parent == talloc_null_ctx()), |
681 | 0 | "session_state_ctx must not be parented by another chunk, but is parented by %s", |
682 | 0 | talloc_get_name(talloc_parent(request->session_state_ctx))); |
683 | 0 | } |
684 | 0 | #endif |
685 | |
|
686 | 0 | fr_pair_list_verify(file, line, request->session_state_ctx, &request->session_state_pairs, true); |
687 | 0 | fr_pair_list_verify(file, line, request->local_ctx, &request->local_pairs, true); |
688 | |
|
689 | 0 | fr_assert(request->proto_dict != NULL); |
690 | 0 | fr_assert(request->local_dict != NULL); |
691 | |
|
692 | 0 | if (request->packet) { |
693 | 0 | packet_verify(file, line, request, request->packet, &request->request_pairs, "request"); |
694 | 0 | } |
695 | 0 | if (request->reply) { |
696 | 0 | packet_verify(file, line, request, request->reply, &request->reply_pairs, "reply"); |
697 | 0 | } |
698 | |
|
699 | 0 | if (request->async) { |
700 | 0 | (void) talloc_get_type_abort(request->async, fr_async_t); |
701 | 0 | fr_assert(talloc_parent(request->async) == request); |
702 | 0 | } |
703 | |
|
704 | 0 | while ((rd = fr_dlist_next(&request->data, rd))) { |
705 | 0 | (void) talloc_get_type_abort(rd, request_data_t); |
706 | |
|
707 | 0 | if (request_data_persistable(rd)) { |
708 | 0 | fr_assert(request->session_state_ctx); |
709 | 0 | fr_assert(talloc_parent(rd) == request->session_state_ctx); |
710 | 0 | } else { |
711 | | fr_assert(talloc_parent(rd) == request); |
712 | 0 | } |
713 | 0 | } |
714 | 0 | } |
715 | | #endif |