The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
subrequest.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: 35c8e18c71d17cfa0644fa5f01de83c1c76ed96a $
19 *
20 * @file unlang/subrequest.c
21 * @brief Unlang "subrequest" and "detach" keyword evaluation.
22 *
23 * @copyright 2006-2019 The FreeRADIUS server project
24 */
25RCSID("$Id: 35c8e18c71d17cfa0644fa5f01de83c1c76ed96a $")
26
27#include <freeradius-devel/server/state.h>
28#include <freeradius-devel/server/tmpl_dcursor.h>
29#include <freeradius-devel/unlang/action.h>
30#include "unlang_priv.h"
31#include "interpret_priv.h"
32#include "subrequest_priv.h"
34
35/** Send a signal from parent request to subrequest
36 *
37 */
39 fr_signal_t action)
40{
41 unlang_frame_state_subrequest_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_subrequest_t);
42 request_t *child = talloc_get_type_abort(state->child, request_t);
43
44 /*
45 * Parent should never receive a detach
46 * signal whilst the child is running.
47 *
48 * Only the child receives a detach
49 * signal when the detach keyword is used.
50 */
51 fr_assert(action != FR_SIGNAL_DETACH);
52
53 /*
54 * If the server is stopped, inside a breakpoint,
55 * whilst processing a child, on resumption both
56 * requests (parent and child) may need to be
57 * cancelled as they've both hit max request_time.
58 *
59 * Sometimes the child will run to completion before
60 * the cancellation is processed, but the parent
61 * will still be cancelled.
62 *
63 * When the parent is cancelled this function is
64 * executed, which will signal an already stopped
65 * child to cancel itself.
66 *
67 * This triggers asserts in the time tracking code.
68 *
69 * ...so we check to see if the child is done before
70 * sending a signal.
71 */
72 if (unlang_request_is_done(child)) return;
73
74 /*
75 * Forward other signals to the child
76 */
77 unlang_interpret_signal(child, action);
78}
79
80/** Parent being resumed after a child completes
81 *
82 */
85{
87 unlang_frame_state_subrequest_t *state = talloc_get_type_abort(frame->state,
89 request_t *child = state->child;
91
92 /*
93 * Child detached
94 */
95 if (!state->child) {
96 RDEBUG3("subrequest detached during its execution - Not updating rcode or reply attributes");
97
98 /*
99 * If the child detached the subrequest section
100 * should become entirely transparent, and
101 * should not update the section rcode.
102 */
104 }
105
106 RDEBUG3("subrequest complete");
107
108 /*
109 * If there's a no destination tmpl, we're done.
110 */
111 if (!child->reply) {
114 }
115
116 /*
117 * Otherwise... copy reply attributes into the
118 * specified destination.
119 */
121 if (gext->dst) {
122 fr_pair_t *vp = NULL;
124 fr_dcursor_t cursor;
125
126 /*
127 * Use callback to build missing destination container.
128 */
129 vp = tmpl_dcursor_build_init(NULL, request, &cc, &cursor, request, gext->dst, tmpl_dcursor_pair_build, NULL);
130 if (!vp) {
131 RPDEBUG("Discarding subrequest attributes - Failed allocating groups");
132 *p_result = RLM_MODULE_FAIL;
135 }
136
137 MEM(fr_pair_list_copy(vp, &vp->vp_group, &child->reply_pairs) >= 0);
138
140 }
141
144}
145
148{
149 unlang_frame_state_subrequest_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_subrequest_t);
150 request_t *child;
151 rlm_rcode_t rcode;
152 fr_pair_t *vp;
153
156
157 /*
158 * Initialize the state
159 */
161 if (!g->num_children) {
162 *p_result = RLM_MODULE_NOOP;
164 }
165
167 child = state->child = unlang_io_subrequest_alloc(request, gext->dict, UNLANG_DETACHABLE);
168 if (!child) {
169 fail:
170 rcode = RLM_MODULE_FAIL;
171
172 /*
173 * Pass the result back to the module
174 * that created the subrequest, or
175 * use it to modify the current section
176 * rcode.
177 */
178 if (state->p_result) *state->p_result = rcode;
179
180 *p_result = rcode;
182 }
183 /*
184 * Set the packet type.
185 */
186 MEM(vp = fr_pair_afrom_da(child->request_ctx, gext->attr_packet_type));
187 if (gext->type_enum) {
188 child->packet->code = vp->vp_uint32 = gext->type_enum->value->vb_uint32;
189 } else {
190 fr_dict_enum_value_t const *type_enum;
191 fr_pair_t *attr;
192
193 if (tmpl_find_vp(&attr, request, gext->vpt) < 0) {
194 RDEBUG("Failed finding attribute %s", gext->vpt->name);
195 goto fail;
196 }
197
198 if (tmpl_attr_tail_da(gext->vpt)->type == FR_TYPE_STRING) {
199 type_enum = fr_dict_enum_by_name(gext->attr_packet_type, attr->vp_strvalue, attr->vp_length);
200 if (!type_enum) {
201 RDEBUG("Unknown Packet-Type %pV", &attr->data);
202 goto fail;
203 }
204
205 child->packet->code = vp->vp_uint32 = type_enum->value->vb_uint32;
206 } else {
207 fr_value_box_t box;
208
209 fr_value_box_init(&box, FR_TYPE_UINT32, NULL, false);
210 if (fr_value_box_cast(request, &box, FR_TYPE_UINT32, NULL, &attr->data) < 0) {
211 RDEBUG("Failed casting value from %pV to data type uint32", &attr->data);
212 goto fail;
213 }
214
215 /*
216 * Check that the value is known to the server.
217 *
218 * If it isn't known, then there's no
219 * "recv foo" section for it and we can't
220 * do anything with this packet.
221 */
222 type_enum = fr_dict_enum_by_value(gext->attr_packet_type, &box);
223 if (!type_enum) {
224 RDEBUG("Invalid value %pV for Packet-Type", &box);
225 goto fail;
226 }
227
228 child->packet->code = vp->vp_uint32 = box.vb_uint32;
229 }
230
231 }
232 fr_pair_append(&child->request_pairs, vp);
233
234 if ((gext->src) && (tmpl_copy_pair_children(child->request_ctx, &child->request_pairs, request, gext->src) < -1)) {
235 RPEDEBUG("Failed copying source attributes into subrequest");
236 goto fail;
237 }
238
239 /*
240 * Setup the child so it'll inform us when
241 * it resumes, or if it detaches.
242 */
243 if (unlang_subrequest_child_push_resume(child, state) < 0) goto fail;
244
245 /*
246 * Push the first instruction the child's
247 * going to run.
248 */
249 if (unlang_interpret_push(child, g->children, frame->result,
250 UNLANG_NEXT_SIBLING, UNLANG_SUB_FRAME) < 0) goto fail;
251
252 state->p_result = p_result;
253 state->detachable = true;
254
255 /*
256 * Store/restore session information in the subrequest
257 * keyed off the exact subrequest keyword.
258 */
259 state->session.enable = true;
260 state->session.unique_ptr = frame->instruction;
261 state->session.unique_int = 0;
262
264
265 return unlang_subrequest_child_run(p_result, request, frame); /* returns UNLANG_ACTION_YIELD */
266}
267
268/** Free a child request, detaching it from its parent and freeing allocated memory
269 *
270 * @param[in] child to free.
271 */
273{
274 request_detach(*child);
275 talloc_free(*child);
276 *child = NULL;
277}
278
279/** Allocate a subrequest to run through a virtual server at some point in the future
280 *
281 * @param[in] parent to hang sub request off of.
282 * @param[in] namespace the child will operate in.
283 * @return
284 * - A new child request.
285 * - NULL on failure.
286 */
291
292/** Initialise subrequest ops
293 *
294 */
296{
298 &(unlang_op_t){
299 .name = "subrequest",
302 .rcode_set = true,
303 .debug_braces = true,
304 .frame_state_size = sizeof(unlang_frame_state_subrequest_t),
305 .frame_state_type = "unlang_frame_state_subrequest_t",
306 });
307
308 if (unlang_subrequest_child_op_init() < 0) return -1;
309
310 return 0;
311}
312
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition action.h:35
@ UNLANG_ACTION_EXECUTE_NEXT
Execute the next unlang_t.
Definition action.h:38
@ UNLANG_ACTION_CALCULATE_RESULT
Calculate a new section rlm_rcode_t value.
Definition action.h:37
#define RCSID(id)
Definition build.h:483
#define UNUSED
Definition build.h:315
#define MEM(x)
Definition debug.h:36
fr_dict_enum_value_t * fr_dict_enum_by_value(fr_dict_attr_t const *da, fr_value_box_t const *value)
Lookup the structure representing an enum value in a fr_dict_attr_t.
Definition dict_util.c:3349
fr_value_box_t const * value
Enum value (what name maps to).
Definition dict.h:231
fr_dict_enum_value_t * fr_dict_enum_by_name(fr_dict_attr_t const *da, char const *name, ssize_t len)
Definition dict_util.c:3395
Value of an enumerated attribute.
Definition dict.h:227
bool unlang_request_is_done(request_t const *request)
Return whether a request has been marked done.
Definition interpret.c:1329
int unlang_interpret_push(request_t *request, unlang_t const *instruction, rlm_rcode_t default_rcode, bool do_next_sibling, bool top_frame)
Push a new frame onto the stack.
Definition interpret.c:161
void unlang_interpret_signal(request_t *request, fr_signal_t action)
Send a signal (usually stop) to a request.
Definition interpret.c:1196
#define UNLANG_SUB_FRAME
Definition interpret.h:36
Private declarations for the unlang interpreter.
#define RDEBUG3(fmt,...)
Definition log.h:343
#define RPDEBUG(fmt,...)
Definition log.h:346
#define RPEDEBUG(fmt,...)
Definition log.h:376
void unlang_register(int type, unlang_op_t *op)
Register an operation with the interpreter.
Definition base.c:63
request_t * unlang_io_subrequest_alloc(request_t *parent, fr_dict_t const *namespace, bool detachable)
Allocate a child request based on the parent.
Definition io.c:39
talloc_free(reap)
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_UINT32
32 Bit unsigned integer.
int fr_pair_list_copy(TALLOC_CTX *ctx, fr_pair_list_t *to, fr_pair_list_t const *from)
Duplicate a list of pairs.
Definition pair.c:2319
int fr_pair_append(fr_pair_list_t *list, fr_pair_t *to_add)
Add a VP to the end of the list.
Definition pair.c:1345
fr_pair_t * fr_pair_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
Dynamically allocate a new attribute and assign a fr_dict_attr_t.
Definition pair.c:283
#define fr_assert(_expr)
Definition rad_assert.h:38
#define RDEBUG(fmt,...)
Definition radclient.h:53
rlm_rcode_t
Return codes indicating the result of the module call.
Definition rcode.h:40
@ RLM_MODULE_FAIL
Module failed, don't reply.
Definition rcode.h:42
@ RLM_MODULE_NOOP
Module succeeded without doing anything.
Definition rcode.h:48
int request_detach(request_t *child)
Unlink a subrequest from its parent.
Definition request.c:668
int tmpl_find_vp(fr_pair_t **out, request_t *request, tmpl_t const *vpt))
Returns the first VP matching a tmpl_t.
Definition tmpl_eval.c:889
static fr_dict_attr_t const * tmpl_attr_tail_da(tmpl_t const *vpt)
Return the last attribute reference da.
Definition tmpl.h:812
int tmpl_copy_pair_children(TALLOC_CTX *ctx, fr_pair_list_t *out, request_t *request, tmpl_t const *vpt))
Copy children of pairs matching a tmpl_t in the current request_t.
Definition tmpl_eval.c:841
fr_signal_t
Signals that can be generated/processed by request signal handlers.
Definition signal.h:38
@ FR_SIGNAL_DETACH
Request is being detached from its parent.
Definition signal.h:45
fr_pair_t * vp
Stores an attribute, a value and various bits of other data.
Definition pair.h:68
static unlang_action_t unlang_subrequest_parent_resume(rlm_rcode_t *p_result, request_t *request, unlang_stack_frame_t *frame)
Parent being resumed after a child completes.
Definition subrequest.c:83
int unlang_subrequest_op_init(void)
Initialise subrequest ops.
Definition subrequest.c:295
static void unlang_subrequest_signal_child(UNUSED request_t *request, unlang_stack_frame_t *frame, fr_signal_t action)
Send a signal from parent request to subrequest.
Definition subrequest.c:38
request_t * unlang_subrequest_alloc(request_t *parent, fr_dict_t const *namespace)
Allocate a subrequest to run through a virtual server at some point in the future.
Definition subrequest.c:287
void unlang_subrequest_detach_and_free(request_t **child)
Free a child request, detaching it from its parent and freeing allocated memory.
Definition subrequest.c:272
static unlang_action_t unlang_subrequest_parent_init(rlm_rcode_t *p_result, request_t *request, unlang_stack_frame_t *frame)
Definition subrequest.c:146
void unlang_subrequest_op_free(void)
Definition subrequest.c:313
int unique_int
Session unique int identifier.
Definition subrequest.h:37
void const * unique_ptr
Session unique ptr identifier.
Definition subrequest.h:36
bool enable
Whether we should store/restore sessions.
Definition subrequest.h:35
void unlang_subrequest_child_op_free(void)
int unlang_subrequest_child_push_resume(request_t *child, unlang_frame_state_subrequest_t *state)
Push a resumption frame onto a child's stack.
int unlang_subrequest_child_op_init(void)
unlang_action_t unlang_subrequest_child_run(UNUSED rlm_rcode_t *p_result, UNUSED request_t *request, unlang_stack_frame_t *frame)
Function called by the unlang interpreter to start the child running.
static unlang_subrequest_t * unlang_group_to_subrequest(unlang_group_t *g)
Cast a group structure to the subrequest keyword extension.
tmpl_t * vpt
Value to expand to find the value to place into the packet-type attribute.
unlang_subrequest_session_t session
Session configuration.
fr_dict_attr_t const * attr_packet_type
Packet-type attribute in the subrequest protocol.
request_t * child
Pre-allocated child request.
tmpl_t * src
Pairs to copy into the subrequest request list.
fr_dict_t const * dict
Dictionary of the subrequest protocol.
tmpl_t * dst
Where to copy pairs from the reply list in the subrequest to.
fr_dict_enum_value_t const * type_enum
Static enumeration value for attr_packet_type.
rlm_rcode_t * p_result
Where to store the result.
bool detachable
Whether the request can be detached.
Parameters for initialising the subrequest (parent's frame state)
fr_pair_t * tmpl_dcursor_pair_build(fr_pair_t *parent, fr_dcursor_t *cursor, fr_dict_attr_t const *da, UNUSED void *uctx)
Simple pair building callback for use with tmpl_dcursors.
void tmpl_dcursor_clear(tmpl_dcursor_ctx_t *cc)
Clear any temporary state allocations.
#define tmpl_dcursor_build_init(_err, _ctx, _cc, _cursor, _request, _vpt, _build, _uctx)
Maintains state between cursor calls.
Private interpreter structures and functions.
#define UNLANG_NORMAL_CHILD
Definition unlang_priv.h:96
#define UNLANG_DETACHABLE
Definition unlang_priv.h:95
#define UNLANG_NEXT_SIBLING
Definition unlang_priv.h:93
void * state
Stack frame specialisations.
static unlang_group_t * unlang_generic_to_group(unlang_t const *p)
@ UNLANG_TYPE_SUBREQUEST
create a child subrequest
Definition unlang_priv.h:64
static void frame_repeat(unlang_stack_frame_t *frame, unlang_process_t process)
Mark the current stack frame up for repeat, and set a new process function.
unlang_t const * instruction
The unlang node we're evaluating.
rlm_rcode_t result
The result from executing the instruction.
unlang_t * children
Children beneath this group.
Generic representation of a grouping.
An unlang operation.
Our interpreter stack, as distinct from the C stack.
static fr_slen_t parent
Definition pair.h:851
int fr_value_box_cast(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, fr_value_box_t const *src)
Convert one type of fr_value_box_t to another.
Definition value.c:3352
#define fr_value_box_init(_vb, _type, _enumv, _tainted)
Initialise a fr_value_box_t.
Definition value.h:587