The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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: b55850d4b1ea96d0bbdab66dc874024e0aafd78e $
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: b55850d4b1ea96d0bbdab66dc874024e0aafd78e $")
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] file request data was added in.
177 * @param[in] line request data was added on.
178 * @return
179 * - -2 on bad arguments.
180 * - -1 on memory allocation error.
181 * - 0 on success.
182 */
183int _request_data_add(request_t *request, void const *unique_ptr, int unique_int, char const *type, void *opaque,
184 bool free_on_replace, bool free_on_parent, bool persist,
185#ifndef NDEBUG
186 char const *file, int line
187#else
188 UNUSED char const *file, UNUSED int line
189#endif
190 )
191{
192 request_data_t *rd = NULL;
193
194 /*
195 * Request must have a state ctx
196 */
197 fr_assert(request);
198 fr_assert(!persist || request->session_state_ctx);
199 fr_assert(!persist ||
200 (talloc_parent(opaque) == request->session_state_ctx) ||
201 (talloc_parent(opaque) == talloc_null_ctx()));
202 fr_assert(!free_on_parent || (talloc_parent(opaque) != request));
203
204#ifndef TALLOC_GET_TYPE_ABORT_NOOP
205 if (type) opaque = _talloc_get_type_abort(opaque, type, __location__);
206#endif
207
208 while ((rd = fr_dlist_next(&request->data, rd))) {
209 if ((rd->unique_ptr != unique_ptr) || (rd->unique_int != unique_int)) continue;
210
211 fr_dlist_remove(&request->data, rd); /* Unlink from the list */
212
213 /*
214 * If caller requires custom behaviour on free
215 * they must set a destructor.
216 */
217 if (rd->free_on_replace && rd->opaque) {
218 RDEBUG4("%s: Freeing %s%s%p at %p:%i via replacement",
219 __FUNCTION__,
220 rd->type ? rd->type : "", rd->type ? " " : "",
221 rd->opaque, rd->unique_ptr, rd->unique_int);
222 talloc_free(rd->opaque);
223 }
224 /*
225 * Need a new one, rd one's parent is wrong.
226 * And no, we can't just steal.
227 */
228 if (rd->persist != persist) {
229 rd->free_on_parent = false;
230 TALLOC_FREE(rd);
231 }
232
233 break; /* replace the existing entry */
234 }
235
236 /*
237 * Only alloc new memory if we're not replacing
238 * an existing entry.
239 *
240 * Tie the lifecycle of the data to either the state_ctx
241 * or the request, depending on whether it should
242 * persist or not.
243 */
244 if (!rd) {
245 if (persist) {
246 fr_assert(request->session_state_ctx);
247 rd = request_data_alloc(request->session_state_ctx);
248 } else {
249 rd = request_data_alloc(request);
250 }
251
252 }
253 if (!rd) return -1;
254
255 rd->unique_ptr = unique_ptr;
256 rd->unique_int = unique_int;
257 rd->type = type;
258 rd->opaque = opaque;
259 rd->free_on_replace = free_on_replace;
260 rd->free_on_parent = free_on_parent;
261 rd->persist = persist;
262#ifndef NDEBUG
263 rd->file = file;
264 rd->line = line;
265#endif
266
267 fr_dlist_insert_head(&request->data, rd);
268
269 RDEBUG4("%s: %s%s%p at %p:%i, free_on_replace: %s, free_on_parent: %s, persist: %s",
270 __FUNCTION__,
271 rd->type ? rd->type : "", rd->type ? " " : "",
272 rd->opaque, rd->unique_ptr, rd->unique_int,
273 free_on_replace ? "yes" : "no",
274 free_on_parent ? "yes" : "no",
275 persist ? "yes" : "no");
276
277 return 0;
278}
279
280/** Get opaque data from a request
281 *
282 * @note The unique ptr is meant to be a module configuration, and the unique
283 * integer allows the caller to have multiple opaque data associated with a request_t.
284 *
285 * @param[in] request to retrieve data from.
286 * @param[in] unique_ptr Identifier for the data.
287 * @param[in] unique_int Qualifier for the identifier.
288 * @return
289 * - NULL if no opaque data could be found.
290 * - the opaque data. The entry holding the opaque data is removed from the request.
291 */
292void *request_data_get(request_t *request, void const *unique_ptr, int unique_int)
293{
294 request_data_t *rd = NULL;
295
296 if (!request) return NULL;
297
298 while ((rd = fr_dlist_next(&request->data, rd))) {
299 void *ptr;
300
301 if ((rd->unique_ptr != unique_ptr) || (rd->unique_int != unique_int)) continue;
302
303 ptr = rd->opaque;
304
305 rd->free_on_parent = false; /* Don't free opaque data we're handing back */
306 fr_dlist_remove(&request->data, rd);
307
308#ifndef TALLOC_GET_TYPE_ABORT_NOOP
309 if (rd->type) ptr = _talloc_get_type_abort(ptr, rd->type, __location__);
310#endif
311
312 RDEBUG4("%s: %s%s%p at %p:%i retrieved and unlinked",
313 __FUNCTION__,
314 rd->type ? rd->type : "", rd->type ? " " : "",
315 rd->opaque, rd->unique_ptr, rd->unique_int);
316
317 talloc_free(rd);
318
319 return ptr;
320 }
321
322 RDEBUG4("%s: No request data found at %p:%i", __FUNCTION__, unique_ptr, unique_int);
323
324 return NULL; /* wasn't found, too bad... */
325}
326
327/** Get opaque data from a request without removing it
328 *
329 * @note The unique ptr is meant to be a module configuration, and the unique
330 * integer allows the caller to have multiple opaque data associated with a request_t.
331 *
332 * @param request to retrieve data from.
333 * @param unique_ptr Identifier for the data.
334 * @param unique_int Qualifier for the identifier.
335 * @return
336 * - NULL if no opaque data could be found.
337 * - the opaque data.
338 */
339void *request_data_reference(request_t *request, void const *unique_ptr, int unique_int)
340{
341 request_data_t *rd = NULL;
342
343 if (!request) return NULL;
344
345 while ((rd = fr_dlist_next(&request->data, rd))) {
346 if ((rd->unique_ptr != unique_ptr) || (rd->unique_int != unique_int)) continue;
347
348#ifndef TALLOC_GET_TYPE_ABORT_NOOP
349 if (rd->type) rd->opaque = _talloc_get_type_abort(rd->opaque, rd->type, __location__);
350#endif
351
352 RDEBUG4("%s: %s%s%p at %p:%i retrieved",
353 __FUNCTION__,
354 rd->type ? rd->type : "", rd->type ? " " : "",
355 rd->opaque, rd->unique_ptr, rd->unique_int);
356
357 return rd->opaque;
358 }
359
360 RDEBUG4("%s: No request data found at %p:%i", __FUNCTION__, unique_ptr, unique_int);
361
362 return NULL; /* wasn't found, too bad... */
363}
364
365/** Loop over all the request data, pulling out ones matching persist state
366 *
367 * @param[out] out Head of result list.
368 * @param[in] request to search for request_data_t in.
369 * @param[in] persist Whether to pull persistable or non-persistable data.
370 * @return number of request_data_t retrieved.
371 */
373{
374 int count = 0;
375 request_data_t *rd = NULL, *prev;
376
377 while ((rd = fr_dlist_next(&request->data, rd))) {
378 if (rd->persist != persist) continue;
379
380 prev = fr_dlist_remove(&request->data, rd);
382 rd = prev;
383 }
384
385 return count;
386}
387
388/** Loop over all the request data, copying, then freeing ones matching persist state
389 *
390 * @param[in] ctx To allocate new request_data_t.
391 * @param[out] out Head of result list. If NULL, data
392 * will be reparented in place.
393 * @param[in] request to search for request_data_t in.
394 * @param[in] persist Whether to pull persistable or non-persistable data.
395 * @return number of request_data_t retrieved.
396 */
397int request_data_by_persistance_reparent(TALLOC_CTX *ctx, fr_dlist_head_t *out, request_t *request, bool persist)
398{
399 int count = 0;
400 request_data_t *rd = NULL, *new, *prev;
402
404
405 while ((rd = fr_dlist_next(&request->data, rd))) {
406 if (rd->persist != persist) continue;
407
408 prev = fr_dlist_remove(&request->data, rd);
409
410 new = request_data_alloc(ctx);
411 memcpy(new, rd, sizeof(*new));
412
413 /*
414 * Clear the list pointers...
415 */
416 memset(&new->list, 0, sizeof(new->list));
417 rd->free_on_parent = false;
418 talloc_free(rd);
419
420 if (out) {
422 } else {
424 }
425 rd = prev;
426 }
427
428 if (!out) fr_dlist_move(&request->data, &head);
429
430 return count;
431}
432
433/** Return how many request data entries exist of a given persistence
434 *
435 * @param[in] request to check in.
436 * @param[in] persist Whether to count persistable or non-persistable data.
437 * @return number of request_data_t that exist in persistable or non-persistable form
438 */
440{
441 int count = 0;
442 request_data_t *rd = NULL;
443
444 while ((rd = fr_dlist_next(&request->data, rd))) {
445 if (rd->persist != persist) continue;
446
447 count++;
448 }
449
450 return count;
451}
452
453/** Add request data back to a request
454 *
455 * @note May add multiple entries (if they're linked).
456 * @note Will not check for duplicates.
457 *
458 * @param request to add data to.
459 * @param in Data to add.
460 */
462{
463 fr_dlist_move(&request->data, in);
464}
465
466/** Used for removing data from subrequests that are about to be freed
467 *
468 * @param[in] request to remove persistable data from.
469 */
480
481
483{
484 request_data_t *rd = NULL;
485
486 if (fr_dlist_empty(head)) return;
487
488 while ((rd = fr_dlist_next(head, rd))) {
489 char *desc;
490
491 desc = request_data_description(NULL, rd);
492 ROPTIONAL(RDEBUG, DEBUG, "%s", desc);
493 talloc_free(desc);
494 }
495}
496
498{
499 request_data_list_dump(request, &request->data);
500}
501
502#ifdef WITH_VERIFY_PTR
503bool request_data_persistable(request_data_t *rd)
504{
505 return rd->persist;
506}
507
508/** Verify all request data is parented by the specified context
509 *
510 * @note Only available if built with WITH_VERIFY_PTR
511 *
512 * @param parent that should hold the request data.
513 * @param entry to verify.
514 * @return
515 * - true if chunk lineage is correct.
516 * - false if one of the chunks is parented by something else.
517 */
518bool request_data_verify_parent(TALLOC_CTX *parent, fr_dlist_head_t *entry)
519{
520 request_data_t *rd = NULL;
521
522 while ((rd = fr_dlist_next(entry, rd))) if (talloc_parent(rd) != parent) return false;
523
524 return true;
525}
526#endif
int const char * file
Definition acutest.h:702
int const char int line
Definition acutest.h:702
#define RCSID(id)
Definition build.h:483
#define UNUSED
Definition build.h:315
#define MEM(x)
Definition debug.h:36
#define DEBUG(fmt,...)
Definition dhcpclient.c:39
static fr_slen_t in
Definition dict.h:824
static void * fr_dlist_remove(fr_dlist_head_t *list_head, void *ptr)
Remove an item from the list.
Definition dlist.h:638
static void fr_dlist_talloc_free(fr_dlist_head_t *head)
Free all items in a doubly linked list (with talloc)
Definition dlist.h:908
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:146
static bool fr_dlist_empty(fr_dlist_head_t const *list_head)
Check whether a list has any items.
Definition dlist.h:501
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:378
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:763
#define fr_dlist_talloc_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
Definition dlist.h:275
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:338
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:555
Head of a doubly linked list.
Definition dlist.h:51
Entry in a doubly linked list.
Definition dlist.h:41
#define ROPTIONAL(_l_request, _l_global, _fmt,...)
Use different logging functions depending on whether request is NULL or not.
Definition log.h:528
#define DEBUG_ENABLED4
True if global debug level 1-3 messages are enabled.
Definition log.h:260
#define DEBUG4(_fmt,...)
Definition log.h:267
#define RDEBUG4(fmt,...)
Definition log.h:344
talloc_free(reap)
#define fr_assert(_expr)
Definition rad_assert.h:38
#define RDEBUG(fmt,...)
Definition radclient.h:53
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, 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:163
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:492
void * talloc_null_ctx(void)
Retrieve the current talloc NULL ctx.
Definition talloc.c:53
static fr_slen_t head
Definition xlat.h:422
static fr_slen_t parent
Definition pair.h:851
static fr_slen_t data
Definition value.h:1265
static size_t char ** out
Definition value.h:997