The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
Loading...
Searching...
No Matches
request_data.c
Go to the documentation of this file.
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: 8600eca2bb09f2b4c1d641b6950c7f66c278a48b $
19 *
20 * @brief Functions for allocating requests and storing internal data in them.
21 * @file src/lib/server/request_data.c
22 *
23 * @copyright 2019 The FreeRADIUS server project
24 */
25RCSID("$Id: 8600eca2bb09f2b4c1d641b6950c7f66c278a48b $")
26
27#include <freeradius-devel/util/debug.h>
28#include <freeradius-devel/server/request_data.h>
29
30/** Per-request opaque data, added by modules
31 *
32 */
34 fr_dlist_t list; //!< Next opaque request data struct linked to this request.
35
36 void const *unique_ptr; //!< Key to lookup request data.
37 int unique_int; //!< Alternative key to lookup request data.
38 char const *type; //!< Opaque type e.g. fr_pair_t, fr_dict_attr_t etc...
39 void *opaque; //!< Opaque data.
40 bool free_on_replace; //!< Whether to talloc_free(opaque) when the request data is removed.
41 bool free_on_parent; //!< Whether to talloc_free(opaque) when the request is freed
42 bool persist; //!< Whether this data should be transferred to a session_entry_t
43 //!< after we're done processing this request.
44
45#ifndef NDEBUG
46 char const *file; //!< File where this request data was added.
47 int line; //!< Line where this request data was added.
48#endif
49};
50
51static char *request_data_description(TALLOC_CTX *ctx, request_data_t *rd)
52{
53 char *where;
54 char *what;
55 char *out;
56
57 /*
58 * Where was the request data added
59 */
60#ifndef NDEBUG
61 where = talloc_typed_asprintf(NULL, " added at %s:%i", rd->file, rd->line);
62#else
63 where = NULL;
64#endif
65
66 /*
67 * What was added
68 */
69 if (rd->type) {
70 what = talloc_typed_asprintf(NULL, "%p (%s)", rd->opaque, rd->type);
71 } else {
72 what = talloc_typed_asprintf(NULL, "%p", rd->opaque);
73 }
74
75 out = talloc_typed_asprintf(ctx, "[0x%012"PRIxPTR":%i]%s %p, opaque %s%s",
76 (uintptr_t)rd->unique_ptr,
77 rd->unique_int,
78 rd->persist ? "[P]" : "",
79 rd,
80 what,
81 where ? where : "");
82 talloc_free(what);
83 talloc_free(where);
84
85 return out;
86}
87
88/* Initialise a dlist for storing request data
89 *
90 * @param[in] list to initialise.
91 */
96
97/** Ensure opaque data is freed by binding its lifetime to the request_data_t
98 *
99 * @param rd Request data being freed.
100 * @return
101 * - 0 if free on parent is false or there's no opaque data.
102 * - ...else whatever the destructor for the opaque data returned.
103 */
105{
106 char *desc = NULL;
107
108 /*
109 * In the vast majority of cases the request data will
110 * unlinked from its list before being freed.
111 * But in case it's not, do this now.
112 *
113 * This helps in a very specific case where there's a list
114 * of request_data_t, and the state_ctx that the
115 * request_data_t is parented off is freed without the
116 * request_data_t being unlinked explicitly, but before
117 * the request itself is freed something attempts to access
118 * the request_data_t list, and runs into freed memory.
119 *
120 * It's a similar pattern to structs removing themselves
121 * from trees when they're freed, but with the added bonus
122 * of never running into use after free errors/
123 */
125
126 if (DEBUG_ENABLED4) desc = request_data_description(rd, rd);
127
128 if (rd->free_on_parent && rd->opaque) {
129 int ret;
130
131 DEBUG4("%s - freed with opaque data", desc);
132
133 ret = talloc_free(rd->opaque);
134 rd->opaque = NULL;
135
136 return ret;
137 }
138
139 DEBUG4("%s - freed, but leaving opaque data", desc);
140
141 return 0;
142}
143
144/** Allocate request data
145 *
146 * @param[in] ctx to allocate request data in.
147 * @return new request data.
148 */
149static inline request_data_t *request_data_alloc(TALLOC_CTX *ctx)
150{
151 request_data_t *rd;
152
153 MEM(rd = talloc_zero(ctx, request_data_t));
154 talloc_set_destructor(rd, _request_data_free);
155
156 return rd;
157}
158
159/** Add opaque data to a request_t
160 *
161 * The unique ptr is meant to be a module configuration, and the unique
162 * integer allows the caller to have multiple opaque data associated with a request_t.
163 *
164 * @param[in] request to associate data with.
165 * @param[in] unique_ptr Identifier for the data.
166 * @param[in] unique_int Qualifier for the identifier.
167 * @param[in] type Type of data (if talloced)
168 * @param[in] opaque Data to associate with the request. May be NULL.
169 * @param[in] free_on_replace Free opaque data if this request_data is replaced.
170 * @param[in] free_on_parent Free opaque data if the request or session is freed.
171 * Must not be set if the opaque data is also parented by
172 * the request or state (double free).
173 * @param[in] persist Transfer request data to an #fr_state_entry_t, and
174 * add it back to the next request we receive for the
175 * session.
176 * @param[in] is_const false if the data was talloc'd, otherwise true if it doesn't need to be freed.
177 * @param[in] file request data was added in.
178 * @param[in] line request data was added on.
179 * @return
180 * - -2 on bad arguments.
181 * - -1 on memory allocation error.
182 * - 0 on success.
183 */
184int _request_data_add(request_t *request, void const *unique_ptr, int unique_int, char const *type, void *opaque,
185 bool free_on_replace, bool free_on_parent, bool persist, bool is_const,
186#ifndef NDEBUG
187 char const *file, int line
188#else
189 UNUSED char const *file, UNUSED int line
190#endif
191 )
192{
193 request_data_t *rd = NULL;
194
195 /*
196 * Request must have a state ctx
197 */
198 fr_assert(request);
199 if (!is_const) {
200 fr_assert(!persist || request->session_state_ctx);
201 fr_assert(!persist ||
202 (talloc_parent(opaque) == request->session_state_ctx) ||
203 (talloc_parent(opaque) == talloc_null_ctx()));
204 fr_assert(!free_on_parent || (talloc_parent(opaque) != request));
205 }
206
207#ifndef TALLOC_GET_TYPE_ABORT_NOOP
208 if (type) opaque = _talloc_get_type_abort(opaque, type, __location__);
209#endif
210
211 while ((rd = fr_dlist_next(&request->data, rd))) {
212 if ((rd->unique_ptr != unique_ptr) || (rd->unique_int != unique_int)) continue;
213
214 fr_dlist_remove(&request->data, rd); /* Unlink from the list */
215
216 /*
217 * If caller requires custom behaviour on free
218 * they must set a destructor.
219 */
220 if (rd->free_on_replace && rd->opaque) {
221 RDEBUG4("%s: Freeing %s%s%p at %p:%i via replacement",
222 __FUNCTION__,
223 rd->type ? rd->type : "", rd->type ? " " : "",
224 rd->opaque, rd->unique_ptr, rd->unique_int);
225 talloc_free(rd->opaque);
226 }
227 /*
228 * Need a new one, rd one's parent is wrong.
229 * And no, we can't just steal.
230 */
231 if (rd->persist != persist) {
232 rd->free_on_parent = false;
233 TALLOC_FREE(rd);
234 }
235
236 break; /* replace the existing entry */
237 }
238
239 /*
240 * Only alloc new memory if we're not replacing
241 * an existing entry.
242 *
243 * Tie the lifecycle of the data to either the state_ctx
244 * or the request, depending on whether it should
245 * persist or not.
246 */
247 if (!rd) {
248 if (persist) {
249 fr_assert(request->session_state_ctx);
250 rd = request_data_alloc(request->session_state_ctx);
251 } else {
252 rd = request_data_alloc(request);
253 }
254
255 }
256 if (!rd) return -1;
257
258 rd->unique_ptr = unique_ptr;
259 rd->unique_int = unique_int;
260 rd->type = type;
261 rd->opaque = opaque;
262 rd->free_on_replace = free_on_replace;
263 rd->free_on_parent = free_on_parent;
264 rd->persist = persist;
265#ifndef NDEBUG
266 rd->file = file;
267 rd->line = line;
268#endif
269
270 fr_dlist_insert_head(&request->data, rd);
271
272 RDEBUG4("%s: %s%s%p at %p:%i, free_on_replace: %s, free_on_parent: %s, persist: %s",
273 __FUNCTION__,
274 rd->type ? rd->type : "", rd->type ? " " : "",
275 rd->opaque, rd->unique_ptr, rd->unique_int,
276 free_on_replace ? "yes" : "no",
277 free_on_parent ? "yes" : "no",
278 persist ? "yes" : "no");
279
280 return 0;
281}
282
283/** Get opaque data from a request
284 *
285 * @note The unique ptr is meant to be a module configuration, and the unique
286 * integer allows the caller to have multiple opaque data associated with a request_t.
287 *
288 * @param[in] request to retrieve data from.
289 * @param[in] unique_ptr Identifier for the data.
290 * @param[in] unique_int Qualifier for the identifier.
291 * @return
292 * - NULL if no opaque data could be found.
293 * - the opaque data. The entry holding the opaque data is removed from the request.
294 */
295void *request_data_get(request_t *request, void const *unique_ptr, int unique_int)
296{
297 request_data_t *rd = NULL;
298
299 if (!request) return NULL;
300
301 while ((rd = fr_dlist_next(&request->data, rd))) {
302 void *ptr;
303
304 if ((rd->unique_ptr != unique_ptr) || (rd->unique_int != unique_int)) continue;
305
306 ptr = rd->opaque;
307
308 rd->free_on_parent = false; /* Don't free opaque data we're handing back */
309 fr_dlist_remove(&request->data, rd);
310
311#ifndef TALLOC_GET_TYPE_ABORT_NOOP
312 if (rd->type) ptr = _talloc_get_type_abort(ptr, rd->type, __location__);
313#endif
314
315 RDEBUG4("%s: %s%s%p at %p:%i retrieved and unlinked",
316 __FUNCTION__,
317 rd->type ? rd->type : "", rd->type ? " " : "",
318 rd->opaque, rd->unique_ptr, rd->unique_int);
319
320 talloc_free(rd);
321
322 return ptr;
323 }
324
325 RDEBUG4("%s: No request data found at %p:%i", __FUNCTION__, unique_ptr, unique_int);
326
327 return NULL; /* wasn't found, too bad... */
328}
329
330/** Get opaque data from a request without removing it
331 *
332 * @note The unique ptr is meant to be a module configuration, and the unique
333 * integer allows the caller to have multiple opaque data associated with a request_t.
334 *
335 * @param request to retrieve data from.
336 * @param unique_ptr Identifier for the data.
337 * @param unique_int Qualifier for the identifier.
338 * @return
339 * - NULL if no opaque data could be found.
340 * - the opaque data.
341 */
342void *request_data_reference(request_t *request, void const *unique_ptr, int unique_int)
343{
344 request_data_t *rd = NULL;
345
346 if (!request) return NULL;
347
348 while ((rd = fr_dlist_next(&request->data, rd))) {
349 if ((rd->unique_ptr != unique_ptr) || (rd->unique_int != unique_int)) continue;
350
351#ifndef TALLOC_GET_TYPE_ABORT_NOOP
352 if (rd->type) rd->opaque = _talloc_get_type_abort(rd->opaque, rd->type, __location__);
353#endif
354
355 RDEBUG4("%s: %s%s%p at %p:%i retrieved",
356 __FUNCTION__,
357 rd->type ? rd->type : "", rd->type ? " " : "",
358 rd->opaque, rd->unique_ptr, rd->unique_int);
359
360 return rd->opaque;
361 }
362
363 RDEBUG4("%s: No request data found at %p:%i", __FUNCTION__, unique_ptr, unique_int);
364
365 return NULL; /* wasn't found, too bad... */
366}
367
368/** Loop over all the request data, pulling out ones matching persist state
369 *
370 * @param[out] out Head of result list.
371 * @param[in] request to search for request_data_t in.
372 * @param[in] persist Whether to pull persistable or non-persistable data.
373 * @return number of request_data_t retrieved.
374 */
376{
377 int count = 0;
378 request_data_t *rd = NULL, *prev;
379
380 while ((rd = fr_dlist_next(&request->data, rd))) {
381 if (rd->persist != persist) continue;
382
383 prev = fr_dlist_remove(&request->data, rd);
385 rd = prev;
386 count++;
387 }
388
389 return count;
390}
391
392/** Loop over all the request data, copying, then freeing ones matching persist state
393 *
394 * @param[in] ctx To allocate new request_data_t.
395 * @param[out] out Head of result list. If NULL, data
396 * will be reparented in place.
397 * @param[in] request to search for request_data_t in.
398 * @param[in] persist Whether to pull persistable or non-persistable data.
399 * @return number of request_data_t retrieved.
400 */
401int request_data_by_persistance_reparent(TALLOC_CTX *ctx, fr_dlist_head_t *out, request_t *request, bool persist)
402{
403 int count = 0;
404 request_data_t *rd = NULL, *new, *prev;
406
408
409 while ((rd = fr_dlist_next(&request->data, rd))) {
410 if (rd->persist != persist) continue;
411
412 prev = fr_dlist_remove(&request->data, rd);
413
414 new = request_data_alloc(ctx);
415 memcpy(new, rd, sizeof(*new));
416
417 /*
418 * Clear the list pointers...
419 */
420 memset(&new->list, 0, sizeof(new->list));
421 rd->free_on_parent = false;
422 talloc_free(rd);
423
424 if (out) {
426 } else {
428 }
429 rd = prev;
430 count++;
431 }
432
433 if (!out) fr_dlist_move(&request->data, &head);
434
435 return count;
436}
437
438/** Return how many request data entries exist of a given persistence
439 *
440 * @param[in] request to check in.
441 * @param[in] persist Whether to count persistable or non-persistable data.
442 * @return number of request_data_t that exist in persistable or non-persistable form
443 */
445{
446 int count = 0;
447 request_data_t *rd = NULL;
448
449 while ((rd = fr_dlist_next(&request->data, rd))) {
450 if (rd->persist != persist) continue;
451
452 count++;
453 }
454
455 return count;
456}
457
458/** Add request data back to a request
459 *
460 * @note May add multiple entries (if they're linked).
461 * @note Will not check for duplicates.
462 *
463 * @param request to add data to.
464 * @param in Data to add.
465 */
467{
468 fr_dlist_move(&request->data, in);
469}
470
471/** Used for removing data from subrequests that are about to be freed
472 *
473 * @param[in] request to remove persistable data from.
474 */
485
486
488{
489 request_data_t *rd = NULL;
490
491 if (fr_dlist_empty(head)) return;
492
493 while ((rd = fr_dlist_next(head, rd))) {
494 char *desc;
495
496 desc = request_data_description(NULL, rd);
497 ROPTIONAL(RDEBUG, DEBUG, "%s", desc);
498 talloc_free(desc);
499 }
500}
501
503{
504 request_data_list_dump(request, &request->data);
505}
506
507#ifdef WITH_VERIFY_PTR
508bool request_data_persistable(request_data_t *rd)
509{
510 return rd->persist;
511}
512
513/** Verify all request data is parented by the specified context
514 *
515 * @note Only available if built with WITH_VERIFY_PTR
516 *
517 * @param parent that should hold the request data.
518 * @param entry to verify.
519 * @return
520 * - true if chunk lineage is correct.
521 * - false if one of the chunks is parented by something else.
522 */
523bool request_data_verify_parent(TALLOC_CTX *parent, fr_dlist_head_t *entry)
524{
525 request_data_t *rd = NULL;
526
527 while ((rd = fr_dlist_next(entry, rd))) if (talloc_parent(rd) != parent) return false;
528
529 return true;
530}
531#endif
int const char * file
Definition acutest.h:702
int const char int line
Definition acutest.h:702
#define RCSID(id)
Definition build.h:560
#define UNUSED
Definition build.h:384
#define MEM(x)
Definition debug.h:36
#define DEBUG(fmt,...)
Definition dhcpclient.c:38
static fr_slen_t in
Definition dict.h:882
static void * fr_dlist_remove(fr_dlist_head_t *list_head, void *ptr)
Remove an item from the list.
Definition dlist.h:620
static void fr_dlist_talloc_free(fr_dlist_head_t *head)
Free all items in a doubly linked list (with talloc)
Definition dlist.h:892
static void fr_dlist_entry_unlink(fr_dlist_t *entry)
Remove an item from the dlist when we don't have access to the head.
Definition dlist.h:128
static bool fr_dlist_empty(fr_dlist_head_t const *list_head)
Check whether a list has any items.
Definition dlist.h:483
static int fr_dlist_insert_tail(fr_dlist_head_t *list_head, void *ptr)
Insert an item into the tail of a list.
Definition dlist.h:360
static int fr_dlist_move(fr_dlist_head_t *list_dst, fr_dlist_head_t *list_src)
Merge two lists, inserting the source at the tail of the destination.
Definition dlist.h:745
#define fr_dlist_talloc_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
Definition dlist.h:257
static int fr_dlist_insert_head(fr_dlist_head_t *list_head, void *ptr)
Insert an item into the head of a list.
Definition dlist.h:320
static void * fr_dlist_next(fr_dlist_head_t const *list_head, void const *ptr)
Get the next item in a list.
Definition dlist.h:537
Head of a doubly linked list.
Definition dlist.h:51
Entry in a doubly linked list.
Definition dlist.h:41
talloc_free(hp)
#define ROPTIONAL(_l_request, _l_global, _fmt,...)
Use different logging functions depending on whether request is NULL or not.
Definition log.h:545
#define DEBUG_ENABLED4
True if global debug level 1-4 messages are enabled.
Definition log.h:265
#define DEBUG4(_fmt,...)
Definition log.h:272
#define RDEBUG4(fmt,...)
Definition log.h:361
#define fr_assert(_expr)
Definition rad_assert.h:37
#define RDEBUG(fmt,...)
bool free_on_replace
Whether to talloc_free(opaque) when the request data is removed.
char const * file
File where this request data was added.
int request_data_by_persistance_count(request_t *request, bool persist)
Return how many request data entries exist of a given persistence.
int request_data_by_persistance(fr_dlist_head_t *out, request_t *request, bool persist)
Loop over all the request data, pulling out ones matching persist state.
fr_dlist_t list
Next opaque request data struct linked to this request.
int _request_data_add(request_t *request, void const *unique_ptr, int unique_int, char const *type, void *opaque, bool free_on_replace, bool free_on_parent, bool persist, bool is_const, char const *file, int line)
Add opaque data to a request_t.
void * opaque
Opaque data.
static int _request_data_free(request_data_t *rd)
Ensure opaque data is freed by binding its lifetime to the request_data_t.
void request_data_list_init(fr_dlist_head_t *data)
void const * unique_ptr
Key to lookup request data.
bool persist
Whether this data should be transferred to a session_entry_t after we're done processing this request...
void request_data_restore(request_t *request, fr_dlist_head_t *in)
Add request data back to a request.
char const * type
Opaque type e.g. fr_pair_t, fr_dict_attr_t etc...
void request_data_dump(request_t *request)
int line
Line where this request data was added.
bool free_on_parent
Whether to talloc_free(opaque) when the request is freed.
void request_data_persistable_free(request_t *request)
Used for removing data from subrequests that are about to be freed.
void * request_data_reference(request_t *request, void const *unique_ptr, int unique_int)
Get opaque data from a request without removing it.
static request_data_t * request_data_alloc(TALLOC_CTX *ctx)
Allocate request data.
int request_data_by_persistance_reparent(TALLOC_CTX *ctx, fr_dlist_head_t *out, request_t *request, bool persist)
Loop over all the request data, copying, then freeing ones matching persist state.
void * request_data_get(request_t *request, void const *unique_ptr, int unique_int)
Get opaque data from a request.
static char * request_data_description(TALLOC_CTX *ctx, request_data_t *rd)
void request_data_list_dump(request_t *request, fr_dlist_head_t *head)
int unique_int
Alternative key to lookup request data.
Per-request opaque data, added by modules.
return count
Definition module.c:155
fr_aka_sim_id_type_t type
char * talloc_typed_asprintf(TALLOC_CTX *ctx, char const *fmt,...)
Call talloc vasprintf, setting the type on the new chunk correctly.
Definition talloc.c:546
void * talloc_null_ctx(void)
Retrieve the current talloc NULL ctx.
Definition talloc.c:50
static fr_slen_t head
Definition xlat.h:420
static fr_slen_t parent
Definition pair.h:858
static fr_slen_t data
Definition value.h:1340
static size_t char ** out
Definition value.h:1030