The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
Loading...
Searching...
No Matches
xlat_eval.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: 314ad4dfa8d581660196e15a718f6e80d187b6bc $
19 *
20 * @file xlat_eval.c
21 * @brief String expansion ("translation"). Evaluation of pre-parsed xlat expansions.
22 *
23 * @copyright 2018-2021 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
24 * @copyright 2000,2006 The FreeRADIUS server project
25 * @copyright 2000 Alan DeKok (aland@freeradius.org)
26 */
27RCSID("$Id: 314ad4dfa8d581660196e15a718f6e80d187b6bc $")
28
29#include <freeradius-devel/server/base.h>
30#include <freeradius-devel/server/tmpl_dcursor.h>
31#include <freeradius-devel/unlang/xlat_priv.h>
32
33static int instance_count = 0;
34
36
38 { .out = &dict_freeradius, .proto = "freeradius" },
40};
41
42fr_dict_attr_t const *attr_expr_bool_enum; /* xlat_expr.c */
43fr_dict_attr_t const *attr_cast_base; /* xlat_expr.c */
44
56
58 { .out = &attr_expr_bool_enum, .name = "Expr-Bool-Enum", .type = FR_TYPE_BOOL, .dict = &dict_freeradius },
59 { .out = &attr_cast_base, .name = "Cast-Base", .type = FR_TYPE_UINT8, .dict = &dict_freeradius },
60
61 { .out = &attr_cast_time_res_sec, .name = "Cast-Time-Res-Sec", .type = FR_TYPE_TIME_DELTA, .dict = &dict_freeradius },
62 { .out = &attr_cast_time_res_min, .name = "Cast-Time-Res-Min", .type = FR_TYPE_TIME_DELTA, .dict = &dict_freeradius },
63 { .out = &attr_cast_time_res_hour, .name = "Cast-Time-Res-Hour", .type = FR_TYPE_TIME_DELTA, .dict = &dict_freeradius },
64 { .out = &attr_cast_time_res_day, .name = "Cast-Time-Res-Day", .type = FR_TYPE_TIME_DELTA, .dict = &dict_freeradius },
65 { .out = &attr_cast_time_res_week, .name = "Cast-Time-Res-Week", .type = FR_TYPE_TIME_DELTA, .dict = &dict_freeradius },
66 { .out = &attr_cast_time_res_month, .name = "Cast-Time-Res-Month", .type = FR_TYPE_TIME_DELTA, .dict = &dict_freeradius },
67 { .out = &attr_cast_time_res_year, .name = "Cast-Time-Res-Year", .type = FR_TYPE_TIME_DELTA, .dict = &dict_freeradius },
68 { .out = &attr_cast_time_res_csec, .name = "Cast-Time-Res-Centi-Sec", .type = FR_TYPE_TIME_DELTA, .dict = &dict_freeradius },
69 { .out = &attr_cast_time_res_msec, .name = "Cast-Time-Res-Milli-Sec", .type = FR_TYPE_TIME_DELTA, .dict = &dict_freeradius },
70 { .out = &attr_cast_time_res_usec, .name = "Cast-Time-Res-Micro-Sec", .type = FR_TYPE_TIME_DELTA, .dict = &dict_freeradius },
71 { .out = &attr_cast_time_res_nsec, .name = "Cast-Time-Res-Nano-Sec", .type = FR_TYPE_TIME_DELTA, .dict = &dict_freeradius },
72
74};
75
77 { L("done"), XLAT_ACTION_DONE },
78 { L("fail"), XLAT_ACTION_FAIL },
79 { L("push-child"), XLAT_ACTION_PUSH_CHILD },
80 { L("yield"), XLAT_ACTION_YIELD }
81};
83
84/*
85 * This should be updated if fr_time_precision_table[] adds more time resolutions.
86 */
88 { L("microseconds"), &attr_cast_time_res_usec },
89 { L("us"), &attr_cast_time_res_usec },
90
91 { L("nanoseconds"), &attr_cast_time_res_nsec },
92 { L("ns"), &attr_cast_time_res_nsec },
93
94 { L("milliseconds"), &attr_cast_time_res_msec },
95 { L("ms"), &attr_cast_time_res_msec },
96
97 { L("centiseconds"), &attr_cast_time_res_csec },
98 { L("cs"), &attr_cast_time_res_csec },
99
100 { L("seconds"), &attr_cast_time_res_sec },
101 { L("s"), &attr_cast_time_res_sec },
102
103 { L("minutes"), &attr_cast_time_res_min },
104 { L("m"), &attr_cast_time_res_min },
105
106 { L("hours"), &attr_cast_time_res_hour },
107 { L("h"), &attr_cast_time_res_hour },
108
109 { L("days"), &attr_cast_time_res_day },
110 { L("d"), &attr_cast_time_res_day },
111
112 { L("weeks"), &attr_cast_time_res_week },
113 { L("w"), &attr_cast_time_res_week },
114
115 /*
116 * These use special values FR_TIME_DUR_MONTH and FR_TIME_DUR_YEAR
117 */
118 { L("months"), &attr_cast_time_res_month },
119 { L("M"), &attr_cast_time_res_month },
120
121 { L("years"), &attr_cast_time_res_year },
122 { L("y"), &attr_cast_time_res_year },
123
124};
126
127fr_dict_attr_t const *xlat_time_res_attr(char const *res)
128{
129 fr_dict_attr_t const **da_p;
130
132 if (!da_p) return NULL;
133
134 return *da_p;
135}
136
137static ssize_t xlat_eval_sync(TALLOC_CTX *ctx, char **out, request_t *request, xlat_exp_head_t const * const head,
138 xlat_escape_legacy_t escape, void const *escape_ctx);
139
140/** Reconstruct the original expansion string from an xlat tree
141 *
142 * @param[in] out sbuff to print result in.
143 * @param[in] node in the tree to start printing.
144 * @return
145 * - The original expansion string on success.
146 * - NULL on error.
147 */
149{
150 switch (node->type) {
151 case XLAT_BOX:
152 case XLAT_GROUP:
153 fr_assert(node->fmt != NULL);
154 return fr_sbuff_in_sprintf(out, "%pV", fr_box_strvalue_buffer(node->fmt));
155
156 case XLAT_ONE_LETTER:
157 fr_assert(node->fmt != NULL);
158 return fr_sbuff_in_sprintf(out, "%%%s", node->fmt);
159
160 case XLAT_TMPL:
161 fr_assert(node->fmt != NULL);
162
163 /*
164 * Just print the attribute name, or the nested xlat.
165 */
166 if (tmpl_is_attr(node->vpt) || (tmpl_is_xlat(node->vpt))) {
167 return fr_sbuff_in_strcpy(out, node->fmt);
168
169 } else {
170 return fr_sbuff_in_sprintf(out, "%%{%s}", node->fmt);
171 }
172
173#ifdef HAVE_REGEX
174 case XLAT_REGEX:
175 return fr_sbuff_in_sprintf(out, "%%{%u}", node->regex_index);
176#endif
177
178 case XLAT_FUNC:
179 {
180 bool first_done = false;
181 fr_sbuff_t our_out;
182 fr_slen_t slen;
183
184 /*
185 * No arguments, just print an empty function.
186 */
187 if (!xlat_exp_head(node->call.args)) return fr_sbuff_in_sprintf(out, "%%%s()", node->call.func->name);
188
189 our_out = FR_SBUFF(out);
190 FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%%%s(", node->call.func->name);
191
192 if (node->call.args) {
193 xlat_exp_foreach(node->call.args, arg) {
194 if (first_done && (node->call.func->args)) {
195 FR_SBUFF_IN_CHAR_RETURN(&our_out, ',');
196 }
197
198 slen = xlat_fmt_print(&our_out, arg);
199 if (slen < 0) return slen - fr_sbuff_used(&our_out);
200
201 first_done = true;
202 }
203 }
204
205 FR_SBUFF_IN_CHAR_RETURN(&our_out, ')');
206 return fr_sbuff_set(out, &our_out);
207 }
208
209 default:
210 return 0;
211 }
212}
213
214/** Output what we're currently expanding
215 *
216 * @param[in] request The current request.
217 * @param[in] node Being processed.
218 * @param[in] args from previous expansion.
219 * @param[in] line Unused
220 */
221static inline void xlat_debug_log_expansion(request_t *request, xlat_exp_t const *node, fr_value_box_list_t const *args, UNUSED int line)
222{
223 if (node->flags.constant) return;
224
225 if (!RDEBUG_ENABLED2) return;
226
227 /*
228 * Because it's difficult to keep track of what
229 * the function was actually called with,
230 * we print the concatenated arguments list as
231 * well as the original fmt string.
232 */
233 if ((node->type == XLAT_FUNC) && !xlat_is_literal(node->call.args)) {
234 fr_token_t token = node->call.func->token;
235
236 if ((token == T_INVALID) || (!fr_comparison_op[token] && !fr_binary_op[token])) {
237 RDEBUG2("| %%%s(%pM)", node->call.func->name, args);
238 } else {
239 fr_value_box_t *a, *b;
240
241 a = fr_value_box_list_head(args);
242 if (!a) return;
243 b = fr_value_box_list_next(args, a);
244
245 if (b) {
246 RDEBUG2("| (%pR %s %pR)", a, fr_tokens[node->call.func->token], b);
247 } else {
248 /*
249 * @todo - things like regexes "steal" their arguments. we should really
250 * have a way to print those arguments here.
251 */
252 RDEBUG2("| (%pR %s ...)", a, fr_tokens[node->call.func->token]);
253 }
254 }
255 } else {
256 fr_sbuff_t *agg;
257
258 FR_SBUFF_TALLOC_THREAD_LOCAL(&agg, 1024, SIZE_MAX);
259
260 if (xlat_fmt_print(agg, node) < 0) {
261 RERROR("Failed printing expansion");
262 return;
263 }
264 RDEBUG2("| %s", fr_sbuff_start(agg)); /* print line number here for debugging */
265 }
266}
267
268/** Output the list result of an expansion
269 *
270 * @param[in] request The current request.
271 * @param[in] node which was expanded.
272 * @param[in] result of the expansion.
273 */
274static inline void xlat_debug_log_list_result(request_t *request, xlat_exp_t const *node, fr_value_box_list_t const *result)
275{
276 if (node->flags.constant) return;
277
278 if (!RDEBUG_ENABLED2) return;
279
280 RDEBUG2("| --> %pM", result);
281}
282
283/** Output the result of an expansion
284 *
285 * @param[in] request The current request.
286 * @param[in] node which was expanded.
287 * @param[in] result of the expansion.
288 */
289static inline void xlat_debug_log_result(request_t *request, xlat_exp_t const *node, fr_value_box_t const *result)
290{
291 if (node->flags.constant) return;
292
293 if (!RDEBUG_ENABLED2) return;
294
295 RDEBUG2("| --> %pR", result);
296}
297
298static int xlat_arg_stringify(request_t *request, xlat_arg_parser_t const *arg, xlat_exp_t const *node, fr_value_box_t *vb)
299{
300 int rcode;
301
302 if (vb->type == FR_TYPE_GROUP) {
303 fr_value_box_list_foreach(&vb->vb_group, child) {
304 if (xlat_arg_stringify(request, arg, NULL, child) < 0) return -1;
305 }
306
307 if (!node || (node->quote == T_BARE_WORD)) return 0;
308
309 fr_assert(node->type == XLAT_GROUP);
310
311 /*
312 * Empty lists are empty strings.
313 */
314 if (!fr_value_box_list_head(&vb->vb_group)) {
315 fr_value_box_entry_t entry;
316
317 entry = vb->entry;
318 fr_value_box_init(vb, FR_TYPE_STRING, NULL, false);
319 fr_value_box_strdup(vb, vb, NULL, "", false);
320 vb->entry = entry;
321
323 return 0;
324 }
325
326 /*
327 * Mash all of the child value-box to a string.
328 */
329 if (fr_value_box_list_concat_in_place(vb, vb, &vb->vb_group, FR_TYPE_STRING, FR_VALUE_BOX_LIST_FREE, true, SIZE_MAX) < 0) {
330 return -1;
331 }
332
333 /*
334 * Do NOT mark this as safe for anything. The inputs could have come from anywhere.
335 *
336 * The arg->safe_for value is set ONLY after the data has been escaped.
337 */
338 return 0;
339 }
340
341 if (fr_value_box_is_safe_for(vb, arg->safe_for) && !arg->always_escape) return 0;
342
343 rcode = arg->func(request, vb, arg->uctx);
344 if (rcode != 0) return rcode;
345
347 return 0;
348}
349
350/** Process an individual xlat argument value box group
351 *
352 * @param[in] ctx to allocate any additional buffers in
353 * @param[in,out] list of value boxes representing one argument
354 * @param[in] request currently being processed
355 * @param[in] name of the function being called
356 * @param[in] arg specification of current argument
357 * @param[in] node expansion for the current argument
358 * @param[in] arg_num number of current argument in the argument specifications
359 * @return
360 * - XLAT_ACTION_DONE on success.
361 * - XLAT_ACTION_FAIL on failure.
362 */
363static xlat_action_t xlat_process_arg_list(TALLOC_CTX *ctx, fr_value_box_list_t *list, request_t *request,
364 char const *name, xlat_arg_parser_t const *arg, xlat_exp_t const *node, unsigned int arg_num)
365{
366 fr_value_box_t *vb;
367 bool concat = false;
368 bool quoted = false;
370
371 /*
372 * The function does it's own escaping and concatenation.
373 */
374 if (arg->will_escape) {
376 return XLAT_ACTION_DONE;
377 }
378
379 /*
380 * See if we have to concatenate multiple value-boxes into one output string / whatever.
381 *
382 * If the input xlat is more complicated expression, it's going to be a function, e.g.
383 *
384 * 1+2 --> %op_add(1,2).
385 *
386 * And then we can't do escaping. Note that this is also the case for
387 *
388 * "foo" + User-Name --> %op_add("foo", User-Name)
389 *
390 * Arguably, we DO want to escape User-Name, but not Foo. Because "+" here is a special case. :(
391 */
392 if ((fr_dlist_num_elements(&node->group->dlist) == 1) && (xlat_exp_head(node->group)->quote != T_BARE_WORD)) {
393 quoted = concat = true;
395
396 } else {
397 concat = arg->concat;
398 type = arg->type;
399 }
400
401 /*
402 * No data - nothing to do.
403 */
404 if (fr_value_box_list_empty(list)) {
405 /*
406 * The expansion resulted in no data, BUT the admin wants a string. So we create an
407 * empty string.
408 *
409 * i.e. If attribute 'foo' doesn't exist, then we have:
410 *
411 * %{foo} --> nothing, because 'foo' doesn't exist
412 * "%{foo}" --> "", because we want a string, therefore the contents of the string are nothing.
413 *
414 * Also note that an empty string satisfies a required argument.
415 */
416 if (quoted) {
417 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_STRING, NULL));
418 fr_value_box_strdup(vb, vb, NULL, "", false);
419 fr_value_box_list_insert_tail(list, vb);
420
421 return XLAT_ACTION_DONE;
422 }
423
424 if (arg->required) {
425 REDEBUG("Function \"%s\" is missing required argument %u", name, arg_num);
426 return XLAT_ACTION_FAIL;
427 }
428
429 return XLAT_ACTION_DONE;
430 }
431
432 /*
433 * The function may be URI or SQL, which have different sub-types. So we call the function if it
434 * is NOT marked as "globally safe for SQL", but the called function may check the more specific
435 * flag "safe for MySQL". And then things which aren't safe for MySQL are escaped, and then
436 * marked as "safe for MySQL".
437 *
438 * If the escape function returns "0", then we set the safe_for value. If the escape function
439 * returns "1", then it has set the safe_for value.
440 */
441 if (arg->func) {
442 for (vb = fr_value_box_list_head(list);
443 vb != NULL;
444 vb = fr_value_box_list_next(list, vb)) {
445 if (xlat_arg_stringify(request, arg, node, vb) < 0) {
446 RPEDEBUG("Function \"%s\" failed escaping argument %u", name, arg_num);
447 return XLAT_ACTION_FAIL;
448 }
449 }
450 }
451
452 vb = fr_value_box_list_head(list);
453 fr_assert(node->type == XLAT_GROUP);
454
455 /*
456 * Coverity doesn't understand that the previous check for an empty list
457 * means that fr_value_box_list_head() will return a box.
458 */
459#ifdef __COVERITY__
460 if (!vb) return XLAT_ACTION_DONE;
461#endif
462
463 /*
464 * Concatenate child boxes, then cast to the desired type.
465 * Skip for an explicit `null` - concatenating would force
466 * a cast of FR_TYPE_NULL to the arg's declared type, which
467 * would either error or silently coerce to a zero-length
468 * value. Let the null box reach check_types intact.
469 */
470 if (concat && !(fr_value_box_list_num_elements(list) == 1 && fr_type_is_null(vb->type))) {
471 if (fr_value_box_list_concat_in_place(ctx, vb, list, type, FR_VALUE_BOX_LIST_FREE, true, SIZE_MAX) < 0) {
472 RPEDEBUG("Function \"%s\" failed concatenating arguments to type %s", name, fr_type_to_str(type));
473 return XLAT_ACTION_FAIL;
474 }
475 fr_assert(fr_value_box_list_num_elements(list) == 1);
476
477 goto check_types;
478 }
479
480 if (concat) goto check_types;
481
482 /*
483 * Only a single child box is valid here. Check there is
484 * just one, cast to the correct type
485 */
486 if (arg->single) {
487 if (fr_value_box_list_num_elements(list) > 1) {
488 RPEDEBUG("Function \"%s\" was provided an incorrect number of values at argument %u, "
489 "expected %s got %u",
490 name, arg_num,
491 arg->required ? "1" : "0-1",
492 fr_value_box_list_num_elements(list));
493 return XLAT_ACTION_FAIL;
494 }
495
496 check_types:
497 if (!fr_type_is_leaf(arg->type)) goto check_non_leaf;
498
499 /*
500 * FR_TYPE_NULL is an explicit "no value" placeholder
501 * (the `null` keyword). Passing it through to the
502 * xlat body lets the implementation distinguish it
503 * from a zero-length value of the declared type; the
504 * author opted-in by writing `null` in the source.
505 * Casting it would paper over the distinction.
506 */
507 if (fr_type_is_null(vb->type)) return XLAT_ACTION_DONE;
508
509 /*
510 * Cast to the correct type if necessary.
511 */
512 if (vb->type != arg->type) {
513 if (fr_value_box_cast_in_place(ctx, vb, arg->type, NULL) < 0) {
514 cast_error:
515 RPEDEBUG("Function \"%s\" failed to cast argument %u to type %s", name, arg_num, fr_type_to_str(arg->type));
516 return XLAT_ACTION_FAIL;
517 }
518 }
519
520 return XLAT_ACTION_DONE;
521 }
522
523 /*
524 * We're neither concatenating nor do we only expect a single value,
525 * cast all child values to the required type.
526 */
527 if (fr_type_is_leaf(arg->type)) {
528 do {
529 if (vb->type == arg->type) continue;
530 if (fr_value_box_cast_in_place(ctx, vb,
531 arg->type, NULL) < 0) goto cast_error;
532 } while ((vb = fr_value_box_list_next(list, vb)));
533
534 return XLAT_ACTION_DONE;
535 }
536
537check_non_leaf:
538 if (arg->type == FR_TYPE_VOID) return XLAT_ACTION_DONE;
539
540 /*
541 * We already have a pair cursor, the argument was an attribute reference.
542 * Check if the arg is required that it has at least one pair.
543 */
544 if (vb->type == FR_TYPE_PAIR_CURSOR) {
546 return XLAT_ACTION_DONE;
547 }
548
549 /*
550 * If the argument is a pair
551 */
552 fr_assert(vb->type != FR_TYPE_PAIR_CURSOR);
553
554 {
555 int err;
556 tmpl_t *vpt;
557
558 /*
559 * Cursor names have to be strings, which are completely safe.
560 */
561 if (vb->type != FR_TYPE_STRING) {
562 REDEBUG("Expected attribute reference as string, not %s", fr_type_to_str(vb->type));
563 return XLAT_ACTION_FAIL;
564 }
565
568 REDEBUG("Refusing to reference attribute from unsafe data");
569 return XLAT_ACTION_FAIL;
570 }
571
572 if (tmpl_afrom_attr_str(ctx, NULL, &vpt, vb->vb_strvalue,
573 &(tmpl_rules_t){
574 .attr = {
575 .dict_def = request->local_dict,
576 .list_def = request_attr_request,
577 .allow_wildcard = arg->allow_wildcard,
578 }
579 }) <= 0) {
580 RPEDEBUG("Failed parsing attribute reference");
581 return XLAT_ACTION_FAIL;
582 }
583
585
586 /*
587 * The cursor can return something, nothing (-1), or no list (-2) or no context (-3). Of
588 * these, only the last two are actually errors.
589 *
590 * "no matching pair" returns an empty cursor.
591 */
592 (void) tmpl_dcursor_value_box_init(&err, vb, vb, request, vpt);
593 if (err < -1) return XLAT_ACTION_FAIL;
594 if (arg->required && err == -1) return XLAT_ACTION_FAIL;
595 }
596
597#undef ESCAPE
598
599 return XLAT_ACTION_DONE;
600}
601
602
603/** Process list of boxed values provided as input to an xlat
604 *
605 * Ensures that the value boxes passed to an xlat function match the
606 * requirements listed in its "args", and escapes any tainted boxes
607 * using the specified escaping routine.
608 *
609 * @param[in] ctx in which to allocate any buffers.
610 * @param[in,out] list value boxes provided as input.
611 * List will be modified in accordance to rules
612 * provided in the args array.
613 * @param[in] request being processed.
614 * @param[in] node which is a function
615 */
616static inline CC_HINT(always_inline)
617xlat_action_t xlat_process_args(TALLOC_CTX *ctx, fr_value_box_list_t *list,
618 request_t *request, xlat_exp_t const *node)
619{
620 xlat_t const *func = node->call.func;
621 xlat_arg_parser_t const *arg_p = func->args;
622 xlat_exp_t *arg, *arg_next;
623 xlat_action_t xa;
624 fr_value_box_t *vb, *vb_next;
625
626 /*
627 * No args registered for this xlat
628 */
629 if (!func->args) return XLAT_ACTION_DONE;
630
631 /*
632 * Manage the arguments.
633 */
634 vb = fr_value_box_list_head(list);
635 arg = xlat_exp_head(node->call.args);
636
637 while (arg_p->type != FR_TYPE_NULL) {
638 /*
639 * Separate check to see if the group
640 * box is there. Check in
641 * xlat_process_arg_list verifies it
642 * has a value.
643 */
644 if (!vb) {
645 if (arg_p->required) {
646 missing:
647 REDEBUG("Function \"%s\" is missing required argument %u",
648 func->name, (unsigned int)((arg_p - func->args) + 1));
649 return XLAT_ACTION_FAIL;
650 }
651
652 /*
653 * The argument isn't required. Just omit it. xlat_func_args_set() enforces
654 * that optional arguments are at the end of the argument list.
655 */
656 return XLAT_ACTION_DONE;
657 }
658
659 /*
660 * Everything in the top level list should be
661 * groups
662 */
663 if (!fr_cond_assert(vb->type == FR_TYPE_GROUP)) return XLAT_ACTION_FAIL;
664
665 /*
666 * pre-advance, in case the vb is replaced
667 * during processing.
668 */
669 vb_next = fr_value_box_list_next(list, vb);
670 arg_next = xlat_exp_next(node->call.args, arg);
671
672 xa = xlat_process_arg_list(ctx, &vb->vb_group, request, func->name, arg_p, arg,
673 (unsigned int)((arg_p - func->args) + 1));
674 if (xa != XLAT_ACTION_DONE) return xa;
675
676 /*
677 * This argument doesn't exist. That might be OK, or it may be a fatal error.
678 */
679 if (fr_value_box_list_empty(&vb->vb_group)) {
680 /*
681 * Variadic rules deal with empty boxes differently...
682 */
683 switch (arg_p->variadic) {
685 fr_value_box_list_talloc_free_head(list);
686 goto do_next;
687
689 goto empty_ok;
690
692 break;
693 }
694
695 /*
696 * Empty groups for optional arguments are OK, we can just stop processing the list.
697 */
698 if (!arg_p->required) {
699 /*
700 * If the caller doesn't care about the type, then we leave the
701 * empty group there.
702 */
703 if (arg_p->type == FR_TYPE_VOID) goto do_next;
704
705 /*
706 * The caller does care about the type, and we don't have any
707 * matching data. Omit this argument, and all arguments after it.
708 *
709 * i.e. if the caller has 3 optional arguments, all
710 * FR_TYPE_UINT8, and the first one is missing, then we MUST
711 * either supply boxes all of FR_TYPE_UINT8, OR we supply nothing.
712 *
713 * We can't supply a box of any other type, because the caller
714 * has declared that it wants FR_TYPE_UINT8, and is naively
715 * accessing the box as vb_uint8, hoping that it's being passed
716 * the right thing.
717 */
718 fr_value_box_list_talloc_free_head(list);
719 break;
720 }
721
722 /*
723 * If the caller is expecting a particular type, then getting nothing is
724 * an error.
725 *
726 * If the caller manually checks the input type, then we can leave it as
727 * an empty group.
728 */
729 if (arg_p->type != FR_TYPE_VOID) goto missing;
730 }
731
732 empty_ok:
733 /*
734 * In some cases we replace the current argument with the head of the group.
735 *
736 * xlat_process_arg_list() has already done concatenations for us.
737 */
738 if (arg_p->single || arg_p->concat) {
739 fr_value_box_t *head = fr_value_box_list_pop_head(&vb->vb_group);
740
741 /*
742 * If we're meant to be smashing the argument
743 * to a single box, but the group was empty,
744 * add a null box instead so ordering is maintained
745 * for subsequent boxes.
746 */
747 if (!head) head = fr_value_box_alloc_null(ctx);
748 fr_value_box_list_replace(list, vb, head);
749 talloc_free(vb);
750 }
751
752 do_next:
753 if (arg_p->variadic) {
754 if (!vb_next) break;
755 } else {
756 arg_p++;
757 arg = arg_next;
758 }
759 vb = vb_next;
760 }
761
762 return XLAT_ACTION_DONE;
763}
764
765/** Validate that the return values from an xlat function match what it registered
766 *
767 * @param[in] request The current request.
768 * @param[in] func that was called.
769 * @param[in] returned the output list of the function.
770 * @param[in] pos current position in the output list.
771 * @return
772 * - true - If return values were correct.
773 * - false - If the return values were incorrect.
774 */
775static inline CC_HINT(nonnull(1,2,3))
776bool xlat_process_return(request_t *request, xlat_t const *func, fr_value_box_list_t const *returned, fr_value_box_t *pos)
777{
778 unsigned int count = 0;
779
780 /*
781 * Nothing to validate. We don't yet enforce that functions
782 * must return at least one instance of their type.
783 */
784 if (!pos || fr_type_is_void(func->return_type)) return true;
785
786 if (fr_type_is_null(func->return_type)) {
787 /* Dynamic expansion to get the right name */
788 REDEBUG("%s return type registered as %s, but %s expansion produced data",
789 func->name, func->name, fr_type_to_str(func->return_type));
790
791 /* We are not forgiving for debug builds */
792 fr_assert_fail("Treating invalid return type as fatal");
793
794 return false;
795 }
796
797 do {
798 if (pos->type != func->return_type) {
799 REDEBUG("%s returned invalid result type at index %u. Expected type %s, got type %s",
800 func->name, count, fr_type_to_str(func->return_type), fr_type_to_str(pos->type));
801
802 /* We are not forgiving for debug builds */
803 fr_assert_fail("Treating invalid return type as fatal");
804#ifdef NDEBUG
805 return false;
806#endif
807 }
808 fr_value_box_mark_safe_for(pos, func->return_safe_for); /* Always set this */
809 count++;
810 } while ((pos = fr_value_box_list_next(returned, pos)));
811
812 return true;
813}
814
815/** One letter expansions
816 *
817 * @param[in] ctx to allocate boxed value, and buffers in.
818 * @param[out] out Where to write the boxed value.
819 * @param[in] request The current request.
820 * @param[in] letter to expand.
821 * @return
822 * - #XLAT_ACTION_FAIL on memory allocation errors.
823 * - #XLAT_ACTION_DONE if we're done processing this node.
824 *
825 */
826static inline CC_HINT(always_inline)
827xlat_action_t xlat_eval_one_letter(TALLOC_CTX *ctx, fr_value_box_list_t *out,
828 request_t *request, char letter)
829{
830
831 char buffer[64];
832 struct tm ts;
833 time_t now;
835
836 now = fr_time_to_sec(request->packet->timestamp);
837
838 switch (letter) {
839 case '%':
841 MEM(fr_value_box_strdup(value, value, NULL, "%", false) >= 0);
842 break;
843
844 /*
845 * RADIUS request values
846 */
847
848 case 'I': /* Request ID */
850 value->vb_uint32 = request->packet->id;
851 break;
852
853 case 'n': /* Request number */
855 value->vb_uint64 = request->number;
856 break;
857
858 case 's': /* First request in this sequence */
860 value->vb_uint64 = request->seq_start;
861 break;
862
863 /*
864 * Current time
865 */
866
867 case 'c': /* Current epoch time seconds */
868 /*
869 * Note that this number MUST be an integer,
870 * otherwise it will get printed as an actual
871 * date!
872 */
874 value->vb_uint64 = (uint64_t)fr_time_to_sec(fr_time());
875 break;
876
877 case 'C': /* Current epoch time microsecond component */
879 value->vb_uint64 = (uint64_t)fr_time_to_usec(fr_time()) % 1000000;
880 break;
881
882 /*
883 * Time of the current request
884 */
885
886 case 'd': /* Request day */
887 if (!localtime_r(&now, &ts)) {
888 error:
889 REDEBUG("Failed converting packet timestamp to localtime: %s", fr_syserror(errno));
890 return XLAT_ACTION_FAIL;
891 }
892
894 value->vb_uint8 = ts.tm_mday;
895 break;
896
897 case 'D': /* Request date */
898 if (!localtime_r(&now, &ts)) goto error;
899
900 strftime(buffer, sizeof(buffer), "%Y%m%d", &ts);
901
903 MEM(fr_value_box_strdup(value, value, NULL, buffer, false) >= 0);
904 break;
905
906 case 'e': /* Request second */
907 if (!localtime_r(&now, &ts)) goto error;
908
910 value->vb_uint8 = ts.tm_sec;
911 break;
912
913 case 'G': /* Request minute */
914 if (!localtime_r(&now, &ts)) goto error;
915
917 value->vb_uint8 = ts.tm_min;
918 break;
919
920 case 'H': /* Request hour */
921 if (!localtime_r(&now, &ts)) goto error;
922
924 value->vb_uint8 = ts.tm_hour;
925 break;
926
927 case 'l': /* Request timestamp as seconds since the epoch */
928 /*
929 * Note that this number MUST be an integer,
930 * otherwise it will get printed as an actual
931 * date!
932 */
934 value->vb_uint64 = (uint64_t) now;
935 break;
936
937 case 'm': /* Request month */
938 if (!localtime_r(&now, &ts)) goto error;
939
941 value->vb_uint8 = ts.tm_mon + 1;
942 break;
943
944 case 'M': /* Request time microsecond component */
946 value->vb_uint64 = (uint64_t)fr_time_to_usec(request->packet->timestamp) % 1000000;
947 break;
948
949 case 'S': /* Request timestamp in SQL format */
950 if (!localtime_r(&now, &ts)) goto error;
951
952 strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &ts);
953
955 MEM(fr_value_box_strdup(value, value, NULL, buffer, false) >= 0);
956 break;
957
958 case 't': /* Request timestamp in CTIME format */
959 {
960 char *p;
961
962 CTIME_R(&now, buffer, sizeof(buffer));
963 p = strchr(buffer, '\n');
964 if (p) *p = '\0';
965
967 MEM(fr_value_box_strdup(value, value, NULL, buffer, false) >= 0);
968 }
969 break;
970
971 case 'T': /* Request timestamp in ISO format */
972 {
973 int len = 0;
974
975 if (!gmtime_r(&now, &ts)) goto error;
976
977 if (!(len = strftime(buffer, sizeof(buffer) - 1, "%Y-%m-%dT%H:%M:%S", &ts))) {
978 REDEBUG("Failed converting packet timestamp to gmtime: Buffer full");
979 return XLAT_ACTION_FAIL;
980 }
981 strcat(buffer, ".");
982 len++;
983 snprintf(buffer + len, sizeof(buffer) - len, "%03i",
984 (int) fr_time_to_msec(request->packet->timestamp) % 1000);
985
987 MEM(fr_value_box_strdup(value, value, NULL, buffer, false) >= 0);
988 }
989 break;
990
991 case 'Y': /* Request year */
992 if (!localtime_r(&now, &ts)) goto error;
993
995
996 value->vb_uint16 = ts.tm_year + 1900;
997 break;
998
999 default:
1000 fr_assert_fail("%%%c is not a valid one letter expansion", letter);
1001 return XLAT_ACTION_FAIL;
1002 }
1003
1004 fr_value_box_list_insert_tail(out, value);
1005
1006 return XLAT_ACTION_DONE;
1007}
1008
1009typedef struct {
1011 fr_value_box_list_t list;
1014
1016 xlat_ctx_t const *xctx,
1017 UNUSED request_t *request, UNUSED fr_value_box_list_t *in)
1018{
1019 xlat_exec_rctx_t *rctx = talloc_get_type_abort(xctx->rctx, xlat_exec_rctx_t);
1020
1021 if (rctx->status != 0) {
1022 fr_strerror_printf("Program failed with status %d", rctx->status);
1023 return XLAT_ACTION_FAIL;
1024 }
1025
1026#if 0
1027 /*
1028 * Comment this out until such time as we better track exceptions.
1029 *
1030 * Enabling this code causes some keyword tests to fail, specifically
1031 * xlat-alternation-with-func and if-regex-match-named.
1032 *
1033 * The regex tests are failing because the various regex_request_to_sub() functions are returning
1034 * errors when there is no previous regex, OR when the referenced regex match doesn't exist.
1035 * This should arguably be a success with NULL results.
1036 *
1037 * The alternation test is failing because a function is called with an argument that doesn't
1038 * exist, inside of an alternation. e.g. %{%foo(nope) || bar}. We arguably want the alternation
1039 * to catch this error, and run the alternate path "bar".
1040 *
1041 * However, doing that would involve more changes. Alternation could catch LHS errors of
1042 * XLAT_FAIL, and then run the RHS. Doing that would require it to manually expand each
1043 * argument, and catch the errors. Note that this is largely what Perl and Python do with their
1044 * logical "and" / "or" functions.
1045 *
1046 * For our use-case, we could perhaps have a variante of || which "catches" errors. One proposal
1047 * is to use a %catch(...) function, but that seems ugly. Pretty much everything would need to
1048 * be wrapped in %catch().
1049 *
1050 * Another option is to extend the || operator. e.g. %{foo(nope) ||? bar}. But that seems ugly,
1051 * too.
1052 *
1053 * Another option is to change the behavior so that failed xlats just result in empty
1054 * value-boxes. However, it then becomes difficult to distinguish the situations for
1055 * %sql("SELECT...") where the SELECT returns nothing, versus the SQL connection is down.
1056 */
1057 if (rctx->result.rcode != RLM_MODULE_OK) {
1058 fr_strerror_printf("Expansion failed with code %s",
1059 fr_table_str_by_value(rcode_table, rctx->result.rcode, "<INVALID>"));
1060 return XLAT_ACTION_FAIL;
1061 }
1062#endif
1063
1064 fr_value_box_list_move((fr_value_box_list_t *)out->dlist, &rctx->list);
1065
1066 return XLAT_ACTION_DONE;
1067}
1068
1069
1070/** Signal an xlat function
1071 *
1072 * @param[in] signal function to call.
1073 * @param[in] exp Xlat node that previously yielded.
1074 * @param[in] request The current request.
1075 * @param[in] rctx Opaque (to us), resume ctx provided by the xlat function
1076 * when it yielded.
1077 * @param[in] action What the request should do (the type of signal).
1078 */
1080 request_t *request, void *rctx, fr_signal_t action)
1081{
1083
1084 signal(XLAT_CTX(exp->call.inst, t->data, exp, t->mctx, NULL, rctx), request, action);
1085}
1086
1088 UNUSED xlat_ctx_t const *xctx,
1089 UNUSED request_t *request, UNUSED fr_value_box_list_t *in)
1090{
1091 return XLAT_ACTION_DONE;
1092}
1093
1094/** Call an xlat's resumption method
1095 *
1096 * @param[in] ctx to allocate value boxes in.
1097 * @param[out] out a list of #fr_value_box_t to append to.
1098 * @param[out] child to evaluate. If a child needs to be evaluated
1099 * by the caller, we return XLAT_ACTION_PUSH_CHILD
1100 * and place the child to be evaluated here.
1101 * Once evaluation is complete, the caller
1102 * should call us with the same #xlat_exp_t and the
1103 * result of the nested evaluation in result.
1104 * @param[in] request the current request.
1105 * @param[in] head of the list to evaluate
1106 * @param[in,out] in xlat node to evaluate. Advanced as we process
1107 * additional #xlat_exp_t.
1108 * @param[in] result Previously expanded arguments to this xlat function.
1109 * @param[in] resume function to call.
1110 * @param[in] rctx Opaque (to us), resume ctx provided by xlat function
1111 * when it yielded.
1112 */
1114 xlat_exp_head_t const **child,
1115 request_t *request, xlat_exp_head_t const *head, xlat_exp_t const **in,
1116 fr_value_box_list_t *result, xlat_func_t resume, void *rctx)
1117{
1118 xlat_action_t xa;
1119 xlat_exp_t const *node = *in;
1120
1121 /*
1122 * It's important that callbacks leave the result list
1123 * in a valid state, as it leads to all kinds of hard
1124 * to debug problems if they free or change elements
1125 * and don't remove them from the list.
1126 */
1127 VALUE_BOX_LIST_VERIFY(result);
1128
1129 if (node->type != XLAT_FUNC) {
1130 xa = resume(ctx, out, XLAT_CTX(NULL, NULL, NULL, NULL, NULL, rctx), request, result);
1131 } else {
1133 t = xlat_thread_instance_find(node);
1134 xa = resume(ctx, out, XLAT_CTX(node->call.inst->data, t->data, node, t->mctx, NULL, rctx), request, result);
1135 VALUE_BOX_LIST_VERIFY(result);
1136
1137 RDEBUG2("| %%%s(...)", node->call.func->name);
1138 }
1139
1140 switch (xa) {
1141 case XLAT_ACTION_YIELD:
1142 RDEBUG2("| (YIELD)");
1143 return xa;
1144
1145 case XLAT_ACTION_DONE:
1146 if (unlang_xlat_yield(request, xlat_null_resume, NULL, 0, NULL) != XLAT_ACTION_YIELD) return XLAT_ACTION_FAIL;
1147
1148 fr_dcursor_next(out); /* Wind to the start of this functions output */
1149 if ((node->type == XLAT_FUNC) && (node->call.func)) {
1150 RDEBUG2("| --> %pR", fr_dcursor_current(out));
1151 if (!xlat_process_return(request, node->call.func, (fr_value_box_list_t *)out->dlist,
1153 }
1154
1155 /*
1156 * It's easier if we get xlat_frame_eval to continue evaluating the frame.
1157 */
1158 *in = xlat_exp_next(head, *in); /* advance */
1159 return xlat_frame_eval(ctx, out, child, request, head, in);
1160
1163 case XLAT_ACTION_FAIL:
1164 break;
1165 }
1166
1167 return xa;
1168}
1169
1170/** Process the result of a previous nested expansion
1171 *
1172 * @param[in] ctx to allocate value boxes in.
1173 * @param[out] out a list of #fr_value_box_t to append to.
1174 * @param[out] child to evaluate. If a child needs to be evaluated
1175 * by the caller, we return XLAT_ACTION_PUSH_CHILD
1176 * and place the child to be evaluated here.
1177 * Once evaluation is complete, the caller
1178 * should call us with the same #xlat_exp_t and the
1179 * result of the nested evaluation in result.
1180 * @param[in] request the current request.
1181 * @param[in] head of the list to evaluate
1182 * @param[in,out] in xlat node to evaluate. Advanced as we process
1183 * additional #xlat_exp_t.
1184 * @param[in] env_data Expanded call env.
1185 * @param[in] result of a previous nested evaluation.
1186 */
1188 xlat_exp_head_t const **child,
1189 request_t *request, xlat_exp_head_t const *head, xlat_exp_t const **in,
1190 void *env_data, fr_value_box_list_t *result)
1191{
1192 xlat_exp_t const *node = *in;
1193
1194 fr_dcursor_tail(out); /* Needed for reentrant behaviour and debugging */
1195
1196 switch (node->type) {
1197 case XLAT_FUNC:
1198 {
1199 xlat_action_t xa;
1201
1202 t = xlat_thread_instance_find(node);
1203 fr_assert(t);
1204
1205 XLAT_DEBUG("** [%i] %s(func-async) - %%%s(%pM)",
1206 unlang_interpret_stack_depth(request), __FUNCTION__,
1207 node->fmt, result);
1208
1209 VALUE_BOX_LIST_VERIFY(result);
1210
1211 if (RDEBUG_ENABLED2) {
1212 REXDENT();
1213 xlat_debug_log_expansion(request, *in, result, __LINE__);
1214 RINDENT();
1215 }
1216
1217 if (t->mctx && node->flags.use_module_status) {
1219 if (thread->force) {
1220 /*
1221 * If the module forced rcode is one of the reject ones
1222 * fail the XLAT early.
1223 */
1224 switch (thread->rcode) {
1225 default:
1226 break;
1227
1229 RDEBUG2("Failing due to failed module thread instance");
1230 return XLAT_ACTION_FAIL;
1231 }
1232 }
1233 }
1234
1235 xa = xlat_process_args(ctx, result, request, node);
1236 if (xa == XLAT_ACTION_FAIL) {
1237 return xa;
1238 }
1239
1240 VALUE_BOX_LIST_VERIFY(result);
1241 xa = node->call.func->func(ctx, out,
1242 XLAT_CTX(node->call.inst->data, t->data, node, t->mctx, env_data, NULL),
1243 request, result);
1244 VALUE_BOX_LIST_VERIFY(result);
1245
1246 switch (xa) {
1247 case XLAT_ACTION_FAIL:
1248 fr_value_box_list_talloc_free_head(result);
1249 return xa;
1250
1252 RDEBUG3("| -- CHILD");
1253 return xa;
1254
1256 RDEBUG3("| -- UNLANG");
1257 return xa;
1258
1259 case XLAT_ACTION_YIELD:
1260 RDEBUG3("| -- YIELD");
1261 return xa;
1262
1263 case XLAT_ACTION_DONE: /* Process the result */
1264 fr_value_box_list_talloc_free_head(result);
1266
1267 REXDENT();
1269 if (!xlat_process_return(request, node->call.func,
1270 (fr_value_box_list_t *)out->dlist,
1272 RINDENT();
1273 return XLAT_ACTION_FAIL;
1274 }
1275 RINDENT();
1276 break;
1277 }
1278 }
1279 break;
1280
1281 case XLAT_GROUP:
1282 {
1283 fr_value_box_t *arg;
1284
1285 /*
1286 * We'd like to do indent / exdent for groups, but that also involves fixing all of the
1287 * error paths. Which we won't do right now.
1288 */
1289 XLAT_DEBUG("** [%i] %s(child) - continuing %%{%s ...}", unlang_interpret_stack_depth(request), __FUNCTION__,
1290 node->fmt);
1291
1292 /*
1293 * Hoist %{...} to its results.
1294 *
1295 * There may be zero or more results.
1296 */
1297 if (node->hoist) {
1298 /*
1299 * Mash quoted strings, UNLESS they're in a function argument. In which case the argument parser
1300 * will do escaping.
1301 *
1302 * @todo - when pushing the xlat for expansion, also push the escaping rules. In which case we can do escaping here.
1303 */
1304 if ((node->quote != T_BARE_WORD) && !head->is_argv) {
1305 if (!fr_value_box_list_head(result)) {
1306 MEM(arg = fr_value_box_alloc(ctx, FR_TYPE_STRING, NULL));
1307 MEM(fr_value_box_strdup(arg, arg, NULL, "", false) >= 0);
1308 fr_dcursor_insert(out, arg);
1309 break;
1310 }
1311
1312 /*
1313 * Mash all of the child value-box to a string.
1314 */
1315 arg = fr_value_box_list_head(result);
1316 fr_assert(arg != NULL);
1317
1318 if (fr_value_box_list_concat_in_place(arg, arg, result, FR_TYPE_STRING, FR_VALUE_BOX_LIST_FREE, true, SIZE_MAX) < 0) {
1319 return XLAT_ACTION_FAIL;
1320 }
1321 }
1322
1323 while ((arg = fr_value_box_list_pop_head(result)) != NULL) {
1324 talloc_steal(ctx, arg);
1325 fr_dcursor_insert(out, arg);
1326 }
1327 break;
1328 }
1329
1330 MEM(arg = fr_value_box_alloc(ctx, FR_TYPE_GROUP, NULL));
1331
1332 if (!fr_value_box_list_empty(result)) {
1333 VALUE_BOX_LIST_VERIFY(result);
1334 fr_value_box_list_move(&arg->vb_group, result);
1335 }
1336
1337 VALUE_BOX_VERIFY(arg);
1338
1339 fr_dcursor_insert(out, arg);
1340 }
1341 break;
1342
1343 case XLAT_TMPL:
1344 fr_assert(tmpl_is_exec(node->vpt));
1345
1346 if (tmpl_eval_cast_in_place(result, request, node->vpt) < 0) {
1347 fr_value_box_list_talloc_free(result);
1348 return XLAT_ACTION_FAIL;
1349 }
1350
1351 /*
1352 * First entry is the command to run. Subsequent entries are the options to pass to the
1353 * command.
1354 */
1355 fr_value_box_list_move((fr_value_box_list_t *)out->dlist, result);
1356 break;
1357
1358 default:
1359 fr_assert(0);
1360 return XLAT_ACTION_FAIL;
1361 }
1362
1363 /*
1364 * It's easier if we get xlat_frame_eval to continue evaluating the frame.
1365 */
1366 *in = xlat_exp_next(head, *in); /* advance */
1367 return xlat_frame_eval(ctx, out, child, request, head, in);
1368}
1369
1370/** Converts xlat nodes to value boxes
1371 *
1372 * Evaluates a single level of expansions.
1373 *
1374 * @param[in] ctx to allocate value boxes in.
1375 * @param[out] out a list of #fr_value_box_t to append to.
1376 * @param[out] child to evaluate. If a child needs to be evaluated
1377 * by the caller, we return XLAT_ACTION_PUSH_CHILD
1378 * and place the child to be evaluated here.
1379 * Once evaluation is complete, the caller
1380 * should call us with the same #xlat_exp_t and the
1381 * result of the nested evaluation in result.
1382 * @param[in] request the current request.
1383 * @param[in] head of the list to evaluate
1384 * @param[in,out] in xlat node to evaluate. Advanced as we process
1385 * additional #xlat_exp_t.
1386 * @return
1387 * - XLAT_ACTION_PUSH_CHILD if we need to evaluate a deeper level of nested.
1388 * child will be filled with the node that needs to be evaluated.
1389 * call #xlat_frame_eval_repeat on this node, once there are results
1390 * from the nested expansion.
1391 * - XLAT_ACTION_YIELD a resumption frame was pushed onto the stack by an
1392 * xlat function and we need to wait for the request to be resumed
1393 * before continuing.
1394 * - XLAT_ACTION_DONE we're done, pop the frame.
1395 * - XLAT_ACTION_FAIL an xlat module failed.
1396 */
1398 request_t *request, xlat_exp_head_t const *head, xlat_exp_t const **in)
1399{
1401 xlat_exp_t const *node;
1402 fr_value_box_list_t result; /* tmp list so debug works correctly */
1403 fr_value_box_t *value = NULL;
1404
1405 fr_value_box_list_init(&result);
1406
1407 *child = NULL;
1408
1409 if (!*in) return XLAT_ACTION_DONE;
1410
1411 /*
1412 * An attribute reference which is a cursor just gets a
1413 * value-box of cursor returned. That is filled in
1414 * later.
1415 */
1416 if (unlikely(head && head->cursor)) {
1417 int err;
1418
1419 fr_assert((*in)->type == XLAT_TMPL);
1420
1422
1423 (void) tmpl_dcursor_value_box_init(&err, value, value, request, (*in)->vpt);
1424 if (err < -1) return XLAT_ACTION_FAIL;
1425
1427 goto finish;
1428 }
1429
1430 /*
1431 * An attribute reference which produces a box of type FR_TYPE_ATTR
1432 */
1433 if (unlikely(head && head->is_attr)) {
1434 fr_assert((*in)->type == XLAT_TMPL);
1435
1438
1440 goto finish;
1441 }
1442
1443 XLAT_DEBUG("** [%i] %s >> entered", unlang_interpret_stack_depth(request), __FUNCTION__);
1444
1445 for (node = *in; node; node = xlat_exp_next(head, node)) {
1446 *in = node; /* Update node in our caller */
1447 fr_dcursor_tail(out); /* Needed for debugging */
1448 VALUE_BOX_LIST_VERIFY((fr_value_box_list_t *)out->dlist);
1449
1450 fr_assert(fr_value_box_list_num_elements(&result) == 0); /* Should all have been moved */
1451
1452 switch (node->type) {
1453 case XLAT_BOX:
1454 XLAT_DEBUG("** [%i] %s(value_box) - %s", unlang_interpret_stack_depth(request), __FUNCTION__, node->fmt);
1455
1456 /*
1457 * Empty boxes are only allowed if
1458 * they're the only node in the expansion.
1459 *
1460 * If they're found anywhere else the xlat
1461 * parser has an error.
1462 */
1463 fr_assert(((node == *in) && !xlat_exp_next(head, node)) || (talloc_array_length(node->fmt) > 1));
1464
1465 /*
1466 * We unfortunately need to dup the buffer
1467 * because references aren't threadsafe.
1468 */
1470 MEM(fr_value_box_copy(value, value, &node->data) >= 0);
1472 continue;
1473
1474 case XLAT_ONE_LETTER:
1475 XLAT_DEBUG("** [%i] %s(one-letter) - %%%s", unlang_interpret_stack_depth(request), __FUNCTION__,
1476 node->fmt);
1477
1478 xlat_debug_log_expansion(request, node, NULL, __LINE__);
1479 if (xlat_eval_one_letter(ctx, &result, request, node->fmt[0]) == XLAT_ACTION_FAIL) {
1480 fail:
1481 fr_value_box_list_talloc_free(&result);
1482 xa = XLAT_ACTION_FAIL;
1483 goto finish;
1484 }
1485 xlat_debug_log_list_result(request, *in, &result);
1486 fr_value_box_list_move((fr_value_box_list_t *)out->dlist, &result);
1487 continue;
1488
1489 case XLAT_TMPL:
1490 /*
1491 * Everything should have been resolved.
1492 */
1493 fr_assert(!tmpl_needs_resolving(node->vpt));
1494
1495 if (tmpl_is_data(node->vpt)) {
1496 XLAT_DEBUG("** [%i] %s(value) - %s", unlang_interpret_stack_depth(request), __FUNCTION__,
1497 node->vpt->name);
1498
1499 MEM(value = fr_value_box_alloc(ctx, tmpl_value_type(node->vpt), NULL));
1500
1501 if (unlikely(fr_value_box_copy(value, value, tmpl_value(node->vpt)) < 0)) {
1503 goto fail;
1504 }; /* Also dups taint */
1505 fr_value_box_list_insert_tail(&result, value);
1506
1507 /*
1508 * Cast the results if necessary.
1509 */
1510 if (tmpl_eval_cast_in_place(&result, request, node->vpt) < 0) goto fail;
1511
1512 fr_value_box_list_move((fr_value_box_list_t *)out->dlist, &result);
1513 continue;
1514
1515 } else if (tmpl_is_attr(node->vpt)) {
1516 if (node->fmt[0] == '&') {
1517 XLAT_DEBUG("** [%i] %s(attribute) - %s", unlang_interpret_stack_depth(request), __FUNCTION__,
1518 node->fmt);
1519 } else {
1520 XLAT_DEBUG("** [%i] %s(attribute) - %%{%s}", unlang_interpret_stack_depth(request), __FUNCTION__,
1521 node->fmt);
1522 }
1523 xlat_debug_log_expansion(request, node, NULL, __LINE__);
1524
1525 if (tmpl_eval_pair(ctx, &result, request, node->vpt) < 0) goto fail;
1526
1527 } else if (tmpl_is_exec(node->vpt) || tmpl_is_xlat(node->vpt)) {
1528 xlat_exec_rctx_t *rctx;
1529
1530 /*
1531 * Allocate and initialize the output context, with value-boxes, exec status, etc.
1532 */
1533 MEM(rctx = talloc_zero(unlang_interpret_frame_talloc_ctx(request), xlat_exec_rctx_t));
1534 fr_value_box_list_init(&rctx->list);
1536
1537 xlat_debug_log_expansion(request, node, NULL, __LINE__);
1538
1539 if (unlang_xlat_yield(request, xlat_exec_resume, NULL, 0, rctx) != XLAT_ACTION_YIELD) goto fail;
1540
1541 if (unlang_tmpl_push(ctx, &rctx->result, &rctx->list, request, node->vpt,
1543 false, &rctx->status), UNLANG_SUB_FRAME) < 0) goto fail;
1544
1546 goto finish;
1547
1548 } else {
1549#ifdef NDEBUG
1550 xa = XLAT_ACTION_FAIL;
1551 goto finish;
1552#endif
1553
1554 /*
1555 * Either this should have been handled previously, or we need to write
1556 * code to deal with this case.
1557 */
1558 fr_assert(0);
1559 }
1560
1561 xlat_debug_log_list_result(request, node, &result);
1562 fr_value_box_list_move((fr_value_box_list_t *)out->dlist, &result);
1563 continue;
1564
1565 case XLAT_FUNC:
1566 XLAT_DEBUG("** [%i] %s(func) - %%%s(...)", unlang_interpret_stack_depth(request), __FUNCTION__,
1567 node->fmt);
1568
1569 /*
1570 * Hand back the child node to the caller
1571 * for evaluation.
1572 */
1573 if (xlat_exp_head(node->call.args)) {
1574 *child = node->call.args;
1576 goto finish;
1577 }
1578
1579 /*
1580 * If there's no children we can just
1581 * call the function directly.
1582 */
1583 xa = xlat_frame_eval_repeat(ctx, out, child, request, head, in, NULL, &result);
1584 if (xa != XLAT_ACTION_DONE || (!*in)) goto finish;
1585 continue;
1586
1587#ifdef HAVE_REGEX
1588 case XLAT_REGEX:
1589 XLAT_DEBUG("** [%i] %s(regex) - %%{%s}", unlang_interpret_stack_depth(request), __FUNCTION__,
1590 node->fmt);
1591
1592 xlat_debug_log_expansion(request, node, NULL, __LINE__);
1594 if (regex_request_to_sub(value, value, request, node->regex_index) < 0) {
1596 continue;
1597 }
1598
1599 xlat_debug_log_result(request, node, value);
1601 continue;
1602#endif
1603
1604 case XLAT_GROUP:
1605 XLAT_DEBUG("** [%i] %s(child) - %%{%s ...}", unlang_interpret_stack_depth(request), __FUNCTION__,
1606 node->fmt);
1607 if (!node->group) continue; /* empty group means we just keep going */
1608
1609 /*
1610 * Hand back the child node to the caller
1611 * for evaluation.
1612 */
1613 *child = node->group;
1615 goto finish;
1616
1617 /*
1618 * Should have been fixed up during pass2
1619 */
1620 case XLAT_INVALID:
1622 fr_assert(0);
1623 return XLAT_ACTION_FAIL;
1624 }
1625 }
1626
1627finish:
1628 VALUE_BOX_LIST_VERIFY((fr_value_box_list_t *)out->dlist);
1629 XLAT_DEBUG("** [%i] %s << %s", unlang_interpret_stack_depth(request),
1630 __FUNCTION__, fr_table_str_by_value(xlat_action_table, xa, "<INVALID>"));
1631
1632 return xa;
1633}
1634
1635static int xlat_sync_stringify(TALLOC_CTX *ctx, request_t *request, xlat_exp_head_t const *head, fr_value_box_list_t *list,
1636 xlat_escape_legacy_t escape, void const *escape_ctx)
1637{
1638 fr_value_box_t *vb, *box;
1639 xlat_exp_t *node;
1640 fr_value_box_safe_for_t safe_for_expected = escape ? (fr_value_box_safe_for_t) escape : FR_VALUE_BOX_SAFE_FOR_ANY;
1642
1643 vb = fr_value_box_list_head(list);
1644 if (!vb) return 0;
1645
1646 node = xlat_exp_head(head);
1647 fr_assert(node != NULL);
1648
1649 do {
1650 size_t len, real_len;
1651 char *escaped;
1652
1653 /*
1654 * Groups commonly are because of quoted strings.
1655 *
1656 * However, we sometimes have a group because of %{...}, in which case the result is just
1657 * a leaf value.
1658 */
1659 if ((node->type == XLAT_GROUP) && (vb->type == FR_TYPE_GROUP)) {
1660 fr_assert(node->quote != T_BARE_WORD);
1661
1662 if (xlat_sync_stringify(vb, request, node->group, &vb->vb_group, escape, escape_ctx) < 0) return -1;
1663
1664 /*
1665 * Replace the group wuth a fixed string.
1666 */
1667 MEM(box = fr_value_box_alloc_null(ctx));
1668
1669 if (fr_value_box_cast(box, box, FR_TYPE_STRING, NULL, vb) < 0) return -1;
1670
1671 /*
1672 * Remove the group, and replace it with the string.
1673 */
1674 fr_value_box_list_insert_before(list, vb, box);
1675 fr_value_box_list_remove(list, vb);
1676 talloc_free(vb);
1677 vb = box;
1678
1679 /*
1680 * It's now safe, so we don't need to do anything else.
1681 */
1682 fr_value_box_mark_safe_for(vb, safe_for_mark);
1683 goto next;
1684 }
1685
1686 if (!escape) goto next;
1687
1688 if (fr_value_box_is_safe_for(vb, safe_for_expected)) goto next;
1689
1690 /*
1691 * We cast EVERYTHING to a string and also escape everything.
1692 */
1693 if (fr_value_box_cast_in_place(vb, vb, FR_TYPE_STRING, NULL) < 0) {
1694 return -1;
1695 }
1696
1697 len = vb->vb_length * 3;
1698 MEM(escaped = talloc_array(vb, char, len + 1));
1699 real_len = escape(request, escaped, len, vb->vb_strvalue, UNCONST(void *, escape_ctx));
1700
1701 fr_value_box_strdup_shallow_replace(vb, escaped, real_len);
1702 fr_value_box_mark_safe_for(vb, safe_for_mark);
1703
1704 next:
1705 vb = fr_value_box_list_next(list, vb);
1706 node = xlat_exp_next(head, node);
1707 } while (node && vb);
1708
1709 return 0;
1710}
1711
1712static ssize_t xlat_eval_sync(TALLOC_CTX *ctx, char **out, request_t *request, xlat_exp_head_t const * const head,
1713 xlat_escape_legacy_t escape, void const *escape_ctx)
1714{
1715 fr_value_box_list_t result;
1716 unlang_result_t unlang_result = UNLANG_RESULT_NOT_SET;
1717 TALLOC_CTX *pool = talloc_new(NULL);
1718 rlm_rcode_t rcode;
1719 char *str;
1720
1721 XLAT_DEBUG("xlat_eval_sync");
1722
1723 *out = NULL;
1724
1725 fr_value_box_list_init(&result);
1726
1727 /*
1728 * Use the unlang stack to evaluate the xlat.
1729 */
1730 if (unlang_xlat_push(pool, &unlang_result, &result, request, head, UNLANG_TOP_FRAME) < 0) {
1731 fail:
1732 talloc_free(pool);
1733 return -1;
1734 }
1735
1736 /*
1737 * Pure functions don't yield, and can therefore be
1738 * expanded in place. This check saves an expensive
1739 * bounce through a new synchronous interpreter.
1740 */
1741 if (!xlat_impure_func(head) && unlang_interpret_get(request)) {
1742 rcode = unlang_interpret(request, UNLANG_REQUEST_RUNNING);
1743 } else {
1745 }
1746
1747 switch (rcode) {
1748 default:
1749 if (XLAT_RESULT_SUCCESS(&unlang_result)) {
1750 break;
1751 }
1753
1754 case RLM_MODULE_REJECT:
1755 case RLM_MODULE_FAIL:
1756 goto fail;
1757 }
1758
1759 if (!fr_value_box_list_empty(&result)) {
1760 /*
1761 * Walk over the data recursively, escaping it, and converting quoted groups to strings.
1762 */
1763 if (xlat_sync_stringify(pool, request, head, &result, escape, escape_ctx) < 0) {
1764 goto fail;
1765 }
1766
1767 str = fr_value_box_list_aprint(ctx, &result, NULL, NULL);
1768 if (!str) goto fail;
1769 } else {
1770 str = talloc_strdup(ctx, "");
1771 }
1772 talloc_free(pool); /* Memory should be in new ctx */
1773
1774 *out = str;
1775
1776 return talloc_strlen(str);
1777}
1778
1779/** Replace %whatever in a string.
1780 *
1781 * See 'doc/unlang/xlat.adoc' for more information.
1782 *
1783 * @param[in] ctx to allocate expansion buffers in.
1784 * @param[out] out Where to write pointer to output buffer.
1785 * @param[in] outlen Size of out.
1786 * @param[in] request current request.
1787 * @param[in] head the xlat structure to expand
1788 * @param[in] escape function to escape final value e.g. SQL quoting.
1789 * @param[in] escape_ctx pointer to pass to escape function.
1790 * @return length of string written @bug should really have -1 for failure.
1791 */
1792static ssize_t _xlat_eval_compiled(TALLOC_CTX *ctx, char **out, size_t outlen, request_t *request,
1793 xlat_exp_head_t const *head, xlat_escape_legacy_t escape, void const *escape_ctx)
1794{
1795 char *buff;
1796 ssize_t slen;
1797
1798 fr_assert(head != NULL);
1799
1800 slen = xlat_eval_sync(ctx, &buff, request, head, escape, escape_ctx);
1801 if (slen < 0) {
1802 fr_assert(buff == NULL);
1803 if (*out) **out = '\0';
1804 return slen;
1805 }
1806
1807 /*
1808 * If out doesn't point to an existing buffer
1809 * copy the pointer to our buffer over.
1810 */
1811 if (!*out) {
1812 *out = buff;
1813 return slen;
1814 }
1815
1816 if ((size_t)slen >= outlen) {
1818 fr_strerror_const("Insufficient output buffer space");
1819 return -1;
1820 }
1821
1822 /*
1823 * Otherwise copy the talloced buffer to the fixed one.
1824 */
1825 memcpy(*out, buff, slen);
1826 (*out)[slen] = '\0';
1828
1829 return slen;
1830}
1831
1832/** Replace %whatever in a string.
1833 *
1834 * See 'doc/unlang/xlat.adoc' for more information.
1835 *
1836 * @param[in] ctx to allocate expansion buffers in.
1837 * @param[out] out Where to write pointer to output buffer.
1838 * @param[in] outlen Size of out.
1839 * @param[in] request current request.
1840 * @param[in] fmt string to expand.
1841 * @param[in] escape function to escape final value e.g. SQL quoting.
1842 * @param[in] escape_ctx pointer to pass to escape function.
1843 * @return length of string written @bug should really have -1 for failure.
1844 */
1845static CC_HINT(nonnull (2, 4, 5))
1846ssize_t _xlat_eval(TALLOC_CTX *ctx, char **out, size_t outlen, request_t *request, char const *fmt,
1847 xlat_escape_legacy_t escape, void const *escape_ctx)
1848{
1849 ssize_t len;
1851
1852 RINDENT();
1853
1854 /*
1855 * Give better errors than the old code.
1856 */
1857 len = xlat_tokenize(ctx, &head,
1859 NULL,
1860 &(tmpl_rules_t){
1861 .attr = {
1862 .dict_def = request->local_dict,
1863 .list_def = request_attr_request,
1864 },
1865 .xlat = {
1866 .runtime_el = unlang_interpret_event_list(request),
1867 },
1868 .at_runtime = true,
1869 });
1870 if (len == 0) {
1871 if (*out) {
1872 **out = '\0';
1873 } else {
1874 *out = talloc_zero_array(ctx, char, 1);
1875 }
1876 REXDENT();
1877 return 0;
1878 }
1879
1880 if (len < 0) {
1881 REMARKER(fmt, -(len), "%s", fr_strerror());
1882 if (*out) **out = '\0';
1883 REXDENT();
1884 return -1;
1885 }
1886
1887 len = _xlat_eval_compiled(ctx, out, outlen, request, head, escape, escape_ctx);
1889
1890 REXDENT();
1891
1892 return len;
1893}
1894
1895ssize_t xlat_eval(char *out, size_t outlen, request_t *request,
1896 char const *fmt, xlat_escape_legacy_t escape, void const *escape_ctx)
1897{
1899
1900 return _xlat_eval(request, &out, outlen, request, fmt, escape, escape_ctx);
1901}
1902
1903ssize_t xlat_eval_compiled(char *out, size_t outlen, request_t *request,
1904 xlat_exp_head_t const *xlat, xlat_escape_legacy_t escape, void const *escape_ctx)
1905{
1907
1908 return _xlat_eval_compiled(request, &out, outlen, request, xlat, escape, escape_ctx);
1909}
1910
1911ssize_t xlat_aeval(TALLOC_CTX *ctx, char **out, request_t *request, char const *fmt,
1912 xlat_escape_legacy_t escape, void const *escape_ctx)
1913{
1915
1916 *out = NULL;
1917 return _xlat_eval(ctx, out, 0, request, fmt, escape, escape_ctx);
1918}
1919
1920ssize_t xlat_aeval_compiled(TALLOC_CTX *ctx, char **out, request_t *request,
1921 xlat_exp_head_t const *xlat, xlat_escape_legacy_t escape, void const *escape_ctx)
1922{
1924
1925 *out = NULL;
1926 return _xlat_eval_compiled(ctx, out, 0, request, xlat, escape, escape_ctx);
1927}
1928
1929
1930/** Turn am xlat list into an argv[] array, and nuke the input list.
1931 *
1932 * This is mostly for async use.
1933 */
1935{
1936 int i;
1937 xlat_exp_head_t **my_argv;
1938 size_t count;
1939
1940 if (head->flags.needs_resolving) {
1941 fr_strerror_printf("Cannot flatten expression with unresolved functions");
1942 return -1;
1943 }
1944
1945 count = 0;
1946 xlat_exp_foreach(head, node) {
1947 count++;
1948 }
1949
1950 MEM(my_argv = talloc_zero_array(ctx, xlat_exp_head_t *, count + 1));
1951 *argv = my_argv;
1952
1954
1955 i = 0;
1956 xlat_exp_foreach(head, node) {
1957 fr_assert(node->type == XLAT_GROUP);
1958 my_argv[i++] = talloc_steal(my_argv, node->group);
1959 }
1960
1961 fr_value_box_list_talloc_free((fr_value_box_list_t *)&head->dlist);
1962
1963 return count;
1964}
1965
1966/** Walk over all xlat nodes (depth first) in a xlat expansion, calling a callback
1967 *
1968 * @param[in] head to evaluate.
1969 * @param[in] walker callback to pass nodes to.
1970 * @param[in] type if > 0 a mask of types to call walker for.
1971 * @param[in] uctx to pass to walker.
1972 * @return
1973 * - 0 on success (walker always returned 0).
1974 * - <0 if walker returned <0.
1975 */
1977{
1978 int ret;
1979
1980 /*
1981 * Iterate over nodes at the same depth
1982 */
1983 xlat_exp_foreach(head, node) {
1984 switch (node->type){
1985 case XLAT_FUNC:
1986 /*
1987 * Evaluate the function's arguments
1988 * first, as they may get moved around
1989 * when the function is instantiated.
1990 */
1991 if (xlat_exp_head(node->call.args)) {
1992 ret = xlat_eval_walk(node->call.args, walker, type, uctx);
1993 if (ret < 0) return ret;
1994 }
1995
1996 if (!type || (type & XLAT_FUNC)) {
1997 ret = walker(node, uctx);
1998 if (ret < 0) return ret;
1999 }
2000 break;
2001
2003 if (xlat_exp_head(node->call.args)) {
2004 ret = xlat_eval_walk(node->call.args, walker, type, uctx);
2005 if (ret < 0) return ret;
2006 }
2007
2008 if (!type || (type & XLAT_FUNC_UNRESOLVED)) {
2009 ret = walker(node, uctx);
2010 if (ret < 0) return ret;
2011 }
2012 break;
2013
2014 case XLAT_GROUP:
2015 if (!type || (type & XLAT_GROUP)) {
2016 ret = walker(node, uctx);
2017 if (ret < 0) return ret;
2018 if (ret > 0) continue;
2019 }
2020
2021 /*
2022 * Evaluate the child.
2023 */
2024 ret = xlat_eval_walk(node->group, walker, type, uctx);
2025 if (ret < 0) return ret;
2026 break;
2027
2028 default:
2029 if (!type || (type & node->type)) {
2030 ret = walker(node, uctx);
2031 if (ret < 0) return ret;
2032 }
2033 break;
2034 }
2035 }
2036
2037 return 0;
2038}
2039
2041{
2042 if (instance_count > 0) {
2044 return 0;
2045 }
2046
2048 PERROR("%s", __FUNCTION__);
2049 return -1;
2050 }
2051
2053 PERROR("%s", __FUNCTION__);
2055 return -1;
2056 }
2057
2059 return 0;
2060}
2061
2063{
2065
2066 if (--instance_count > 0) return;
2067
2069}
static int const char char buffer[256]
Definition acutest.h:576
va_list args
Definition acutest.h:770
static int const char * fmt
Definition acutest.h:573
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
Definition build.h:186
#define RCSID(id)
Definition build.h:560
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:228
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition build.h:391
#define unlikely(_x)
Definition build.h:455
#define UNUSED
Definition build.h:384
#define NUM_ELEMENTS(_t)
Definition build.h:406
static void * fr_dcursor_next(fr_dcursor_t *cursor)
Advanced the cursor to the next item.
Definition dcursor.h:288
static int fr_dcursor_append(fr_dcursor_t *cursor, void *v)
Insert a single item at the end of the list.
Definition dcursor.h:406
static void * fr_dcursor_tail(fr_dcursor_t *cursor)
Wind cursor to the tail item in the list.
Definition dcursor.h:258
static int fr_dcursor_insert(fr_dcursor_t *cursor, void *v)
Insert directly after the current item.
Definition dcursor.h:435
static void * fr_dcursor_current(fr_dcursor_t *cursor)
Return the item the cursor current points to.
Definition dcursor.h:337
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:172
#define fr_assert_fail(_msg,...)
Calls panic_action ifndef NDEBUG, else logs error.
Definition debug.h:249
#define MEM(x)
Definition debug.h:38
#define fr_dict_autofree(_to_free)
Definition dict.h:915
static fr_slen_t err
Definition dict.h:882
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition dict.h:292
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition dict.h:305
int fr_dict_attr_autoload(fr_dict_attr_autoload_t const *to_load)
Process a dict_attr_autoload element to load/verify a dictionary attribute.
Definition dict_util.c:4393
#define fr_dict_autoload(_to_load)
Definition dict.h:912
#define DICT_AUTOLOAD_TERMINATOR
Definition dict.h:311
static fr_slen_t in
Definition dict.h:882
Specifies an attribute which must be present for the module to function.
Definition dict.h:291
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition dict.h:304
Test enumeration values.
Definition dict_test.h:92
static unsigned int fr_dlist_num_elements(fr_dlist_head_t const *head)
Return the number of elements in the dlist.
Definition dlist.h:921
Definition dwarf.c:563
#define EXEC_TIMEOUT
Default wait time for exec calls (in seconds).
Definition exec.h:32
talloc_free(hp)
rlm_rcode_t unlang_interpret(request_t *request, bool running)
Run the interpreter for a current request.
Definition interpret.c:1302
unlang_interpret_t * unlang_interpret_get(request_t *request)
Get the interpreter set for a request.
Definition interpret.c:2528
int unlang_interpret_stack_depth(request_t *request)
Return the depth of the request's stack.
Definition interpret.c:1921
TALLOC_CTX * unlang_interpret_frame_talloc_ctx(request_t *request)
Get a talloc_ctx which is valid only for this frame.
Definition interpret.c:2053
fr_event_list_t * unlang_interpret_event_list(request_t *request)
Get the event list for the current interpreter.
Definition interpret.c:2538
#define UNLANG_RESULT_NOT_SET
Definition interpret.h:145
#define UNLANG_SUB_FRAME
Definition interpret.h:37
rlm_rcode_t rcode
The current rcode, from executing the instruction or merging the result from a frame.
Definition interpret.h:140
#define UNLANG_TOP_FRAME
Definition interpret.h:36
#define UNLANG_REQUEST_RUNNING
Definition interpret.h:47
rlm_rcode_t unlang_interpret_synchronous(fr_event_list_t *el, request_t *request)
Execute an unlang section synchronously.
#define UNLANG_RESULT_RCODE(_x)
Definition interpret.h:146
#define PERROR(_fmt,...)
Definition log.h:233
#define REXDENT()
Exdent (unindent) R* messages by one level.
Definition log.h:460
#define RDEBUG3(fmt,...)
Definition log.h:360
#define RERROR(fmt,...)
Definition log.h:315
#define REMARKER(_str, _marker_idx, _marker,...)
Output string with error marker, showing where format error occurred.
Definition log.h:515
#define RPEDEBUG(fmt,...)
Definition log.h:393
#define RINDENT()
Indent R* messages by one level.
Definition log.h:447
#define fr_time()
Definition event.c:60
FILE * fr_log_fp
Definition log.c:40
size_t(* xlat_escape_legacy_t)(request_t *request, char *out, size_t outlen, char const *in, void *arg)
fr_type_t
@ FR_TYPE_TIME_DELTA
A period of time measured in nanoseconds.
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_NULL
Invalid (uninitialised) attribute type.
@ FR_TYPE_UINT16
16 Bit unsigned integer.
@ FR_TYPE_UINT8
8 Bit unsigned integer.
@ FR_TYPE_UINT32
32 Bit unsigned integer.
@ FR_TYPE_UINT64
64 Bit unsigned integer.
@ FR_TYPE_VOID
User data.
@ FR_TYPE_BOOL
A truth value.
@ FR_TYPE_GROUP
A grouping of other attributes.
long int ssize_t
unsigned char bool
ssize_t fr_slen_t
struct tm * gmtime_r(time_t const *l_clock, struct tm *result)
Definition missing.c:205
struct tm * localtime_r(time_t const *l_clock, struct tm *result)
Definition missing.c:162
module_instance_t const * mi
Instance of the module being instantiated.
Definition module_ctx.h:42
#define fr_assert(_expr)
Definition rad_assert.h:37
#define REDEBUG(fmt,...)
#define RDEBUG_ENABLED2()
#define RDEBUG2(fmt,...)
fr_table_num_sorted_t const rcode_table[]
Definition rcode.c:35
#define RLM_MODULE_USER_SECTION_REJECT
Rcodes that translate to a user configurable section failing overall.
Definition rcode.h:79
rlm_rcode_t
Return codes indicating the result of the module call.
Definition rcode.h:44
@ RLM_MODULE_OK
The module is OK, continue.
Definition rcode.h:49
@ RLM_MODULE_FAIL
Module failed, don't reply.
Definition rcode.h:48
@ RLM_MODULE_REJECT
Immediately reject the request.
Definition rcode.h:47
fr_dict_attr_t const * request_attr_request
Definition request.c:43
static char const * name
ssize_t fr_sbuff_in_strcpy(fr_sbuff_t *sbuff, char const *str)
Copy bytes into the sbuff up to the first \0.
Definition sbuff.c:1471
ssize_t fr_sbuff_in_sprintf(fr_sbuff_t *sbuff, char const *fmt,...)
Print using a fmt string to an sbuff.
Definition sbuff.c:1611
#define fr_sbuff_start(_sbuff_or_marker)
#define FR_SBUFF_IN_CHAR_RETURN(_sbuff,...)
#define fr_sbuff_set(_dst, _src)
#define FR_SBUFF_IN_SPRINTF_RETURN(...)
#define FR_SBUFF_IN_STR(_start)
#define FR_SBUFF(_sbuff_or_marker)
#define fr_sbuff_used(_sbuff_or_marker)
#define FR_SBUFF_TALLOC_THREAD_LOCAL(_out, _init, _max)
bool force
Force the module to return a specific code.
Definition module.h:386
rlm_rcode_t rcode
Code module will return when 'force' has has been set to true.
Definition module.h:389
static module_thread_instance_t * module_thread(module_instance_t const *mi)
Retrieve module/thread specific instance for a module.
Definition module.h:513
Per thread per instance data.
Definition module.h:369
#define tmpl_is_xlat(vpt)
Definition tmpl.h:210
#define tmpl_value(_tmpl)
Definition tmpl.h:937
#define tmpl_is_attr(vpt)
Definition tmpl.h:208
#define tmpl_is_exec(vpt)
Definition tmpl.h:211
ssize_t tmpl_afrom_attr_str(TALLOC_CTX *ctx, tmpl_attr_error_t *err, tmpl_t **out, char const *name, tmpl_rules_t const *rules))
Parse a string into a TMPL_TYPE_ATTR_* type tmpl_t.
int tmpl_eval_cast_in_place(fr_value_box_list_t *out, request_t *request, tmpl_t const *vpt)
Casts a value or list of values according to the tmpl.
Definition tmpl_eval.c:1234
int tmpl_eval_pair(TALLOC_CTX *ctx, fr_value_box_list_t *out, request_t *request, tmpl_t const *vpt)
Gets the value of a real or virtual attribute.
Definition tmpl_eval.c:953
#define tmpl_is_data(vpt)
Definition tmpl.h:206
static fr_slen_t vpt
Definition tmpl.h:1267
#define tmpl_value_type(_tmpl)
Definition tmpl.h:939
static fr_dict_attr_t const * tmpl_attr_tail_da(tmpl_t const *vpt)
Return the last attribute reference da.
Definition tmpl.h:801
#define tmpl_needs_resolving(vpt)
Definition tmpl.h:223
Optional arguments passed to vp_tmpl functions.
Definition tmpl.h:336
fr_signal_t
Signals that can be generated/processed by request signal handlers.
Definition signal.h:38
static char buff[sizeof("18446744073709551615")+3]
Definition size_tests.c:37
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition snprintf.c:689
fr_aka_sim_id_type_t type
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition syserror.c:243
#define fr_table_value_by_str(_table, _name, _def)
Convert a string to a value using a sorted or ordered table.
Definition table.h:685
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition table.h:804
An element in a lexicographically sorted array of name to num mappings.
Definition table.h:49
An element in an arbitrarily ordered array of name to ptr mappings.
Definition table.h:73
#define talloc_strdup(_ctx, _str)
Definition talloc.h:149
static size_t talloc_strlen(char const *s)
Returns the length of a talloc array containing a string.
Definition talloc.h:143
static int64_t fr_time_to_sec(fr_time_t when)
Convert an fr_time_t (internal time) to number of sec since the unix epoch (wallclock time)
Definition time.h:731
static int64_t fr_time_to_msec(fr_time_t when)
Convert an fr_time_t (internal time) to number of msec since the unix epoch (wallclock time)
Definition time.h:711
static fr_time_delta_t fr_time_delta_from_sec(int64_t sec)
Definition time.h:590
static int64_t fr_time_to_usec(fr_time_t when)
Convert an fr_time_t (internal time) to number of usec since the unix epoch (wallclock time)
Definition time.h:701
int unlang_tmpl_push(TALLOC_CTX *ctx, unlang_result_t *p_result, fr_value_box_list_t *out, request_t *request, tmpl_t const *tmpl, unlang_tmpl_args_t *args, bool top_frame)
Push a tmpl onto the stack for evaluation.
Definition tmpl.c:276
fr_pair_t * tmpl_dcursor_value_box_init(int *err, TALLOC_CTX *ctx, fr_value_box_t *vb, request_t *request, tmpl_t const *vpt)
Initialize a #tmpl_dcursor_t into a fr_value_box_t.
char const * fr_tokens[T_TOKEN_LAST]
Definition token.c:146
const bool fr_comparison_op[T_TOKEN_LAST]
Definition token.c:266
const bool fr_binary_op[T_TOKEN_LAST]
Definition token.c:284
enum fr_token fr_token_t
@ T_INVALID
Definition token.h:37
@ T_BARE_WORD
Definition token.h:118
static unsigned count
Definition unittest.c:47
#define TMPL_ARGS_EXEC(_env, _timeout, _stdout_on_error, _status_out)
Create a temporary argument structure for evaluating an exec type tmpl.
Definition tmpl.h:76
xlat_action_t unlang_xlat_yield(request_t *request, xlat_func_t resume, xlat_func_signal_t signal, fr_signal_t sigmask, void *rctx)
Yield a request back to the interpreter from within a module.
Definition xlat.c:543
int unlang_xlat_push(TALLOC_CTX *ctx, unlang_result_t *p_result, fr_value_box_list_t *out, request_t *request, xlat_exp_head_t const *xlat, bool top_frame)
Push a pre-compiled xlat onto the stack for evaluation.
Definition xlat.c:269
fr_type_t type
Type to cast argument to.
Definition xlat.h:156
void * data
Thread specific instance data.
Definition xlat.h:94
xlat_thread_inst_t * xlat_thread_instance_find(xlat_exp_t const *node)
Retrieve xlat/thread specific instance data.
Definition xlat_inst.c:404
bool xlat_is_literal(xlat_exp_head_t const *head)
Check to see if the expansion consists entirely of value-box elements.
void * uctx
Argument to pass to escape callback.
Definition xlat.h:160
bool xlat_impure_func(xlat_exp_head_t const *head)
xlat_escape_func_t func
Function to handle tainted values.
Definition xlat.h:157
fr_slen_t xlat_tokenize(TALLOC_CTX *ctx, xlat_exp_head_t **head, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules)
Tokenize an xlat expansion.
unsigned int concat
Concat boxes together.
Definition xlat.h:148
@ XLAT_ARG_VARIADIC_EMPTY_KEEP
Empty argument groups are left alone, and either passed through as empty groups or null boxes.
Definition xlat.h:138
@ XLAT_ARG_VARIADIC_EMPTY_SQUASH
Empty argument groups are removed.
Definition xlat.h:137
@ XLAT_ARG_VARIADIC_DISABLED
Definition xlat.h:136
static fr_slen_t head
Definition xlat.h:421
xlat_arg_parser_variadic_t variadic
All additional boxes should be processed using this definition.
Definition xlat.h:154
#define XLAT_RESULT_SUCCESS(_p_result)
Definition xlat.h:501
void(* xlat_func_signal_t)(xlat_ctx_t const *xctx, request_t *request, fr_signal_t action)
A callback when the request gets a fr_signal_t.
Definition xlat.h:244
fr_value_box_safe_for_t safe_for
Escaped value to set for boxes processed by this escape function.
Definition xlat.h:158
xlat_action_t(* xlat_func_t)(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *in)
xlat callback function
Definition xlat.h:233
unsigned int always_escape
Pass all arguments to escape function not just tainted ones.
Definition xlat.h:152
unsigned int required
Argument must be present, and non-empty.
Definition xlat.h:147
unsigned int use_module_status
use the module thread status to force early failure.
Definition xlat.h:116
unsigned int single
Argument must only contain a single box.
Definition xlat.h:149
xlat_action_t
Definition xlat.h:37
@ XLAT_ACTION_FAIL
An xlat function failed.
Definition xlat.h:44
@ XLAT_ACTION_YIELD
An xlat function pushed a resume frame onto the stack.
Definition xlat.h:42
@ XLAT_ACTION_PUSH_UNLANG
An xlat function pushed an unlang frame onto the unlang stack.
Definition xlat.h:39
@ XLAT_ACTION_DONE
We're done evaluating this level of nesting.
Definition xlat.h:43
@ XLAT_ACTION_PUSH_CHILD
A deeper level of nesting needs to be evaluated.
Definition xlat.h:38
unsigned int will_escape
the function will do escaping and concatenation.
Definition xlat.h:151
unsigned int constant
xlat is just tmpl_attr_tail_data, or XLAT_BOX
Definition xlat.h:114
module_ctx_t const * mctx
A synthesised module calling ctx containing module global and thread instance data.
Definition xlat.h:96
Definition for a single argument consumed by an xlat function.
Definition xlat.h:146
Thread specific instance data for xlat expansion node.
Definition xlat.h:85
char const * fr_strerror(void)
Get the last library error.
Definition strerror.c:558
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
#define fr_strerror_const(_msg)
Definition strerror.h:223
#define fr_type_is_void(_x)
Definition types.h:378
@ FR_TYPE_PAIR_CURSOR
cursor over a fr_pair_t
Definition types.h:90
#define fr_type_is_null(_x)
Definition types.h:347
#define fr_type_is_leaf(_x)
Definition types.h:393
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
Definition types.h:454
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:3968
char * fr_value_box_list_aprint(TALLOC_CTX *ctx, fr_value_box_list_t const *list, char const *delim, fr_sbuff_escape_rules_t const *e_rules)
Concatenate the string representations of a list of value boxes together.
Definition value.c:7013
int fr_value_box_copy(TALLOC_CTX *ctx, fr_value_box_t *dst, const fr_value_box_t *src)
Copy value data verbatim duplicating any buffers.
Definition value.c:4416
int fr_value_box_cast_in_place(TALLOC_CTX *ctx, fr_value_box_t *vb, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv)
Convert one type of fr_value_box_t to another in place.
Definition value.c:4218
void fr_value_box_clear_value(fr_value_box_t *data)
Clear/free any existing value.
Definition value.c:4353
void fr_value_box_set_attr(fr_value_box_t *dst, fr_dict_attr_t const *da)
Definition value.c:5265
int fr_value_box_strdup(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, bool tainted)
Copy a nul terminated string to a fr_value_box_t.
Definition value.c:4643
void fr_value_box_strdup_shallow_replace(fr_value_box_t *vb, char const *src, ssize_t len)
Free the existing buffer (if talloced) associated with the valuebox, and replace it with a new one.
Definition value.c:4769
void fr_value_box_debug(FILE *fp, fr_value_box_t const *vb)
Print the value of a box as info messages.
Definition value.c:7528
int fr_value_box_list_concat_in_place(TALLOC_CTX *ctx, fr_value_box_t *out, fr_value_box_list_t *list, fr_type_t type, fr_value_box_list_action_t proc_action, bool flatten, size_t max_size)
Concatenate a list of value boxes.
Definition value.c:6630
@ FR_VALUE_BOX_LIST_FREE
Definition value.h:238
#define fr_value_box_alloc(_ctx, _type, _enumv)
Allocate a value box of a specific type.
Definition value.h:644
#define fr_value_box_mark_safe_for(_box, _safe_for)
Definition value.h:1093
#define fr_box_strvalue_buffer(_val)
Definition value.h:312
#define fr_value_box_is_safe_for(_box, _safe_for)
Definition value.h:1100
#define fr_value_box_get_cursor(_dst)
Definition value.h:1261
#define FR_VALUE_BOX_SAFE_FOR_NONE
Definition value.h:172
uintptr_t fr_value_box_safe_for_t
Escaping that's been applied to a value box.
Definition value.h:162
#define VALUE_BOX_VERIFY(_x)
Definition value.h:1370
#define VALUE_BOX_LIST_VERIFY(_x)
Definition value.h:1371
int nonnull(2, 5))
#define fr_value_box_alloc_null(_ctx)
Allocate a value box for later use with a value assignment function.
Definition value.h:655
#define fr_value_box_init(_vb, _type, _enumv, _tainted)
Initialise a fr_value_box_t.
Definition value.h:610
#define fr_value_box_list_foreach(_list_head, _iter)
Definition value.h:224
static size_t char ** out
Definition value.h:1030
#define FR_VALUE_BOX_SAFE_FOR_ANY
Definition value.h:173
void * rctx
Resume context.
Definition xlat_ctx.h:54
#define XLAT_CTX(_inst, _thread, _ex, _mctx, _env_data, _rctx)
Wrapper to create a xlat_ctx_t as a compound literal.
Definition xlat_ctx.h:95
An xlat calling ctx.
Definition xlat_ctx.h:49
ssize_t xlat_eval_compiled(char *out, size_t outlen, request_t *request, xlat_exp_head_t const *xlat, xlat_escape_legacy_t escape, void const *escape_ctx)
Definition xlat_eval.c:1903
static size_t xlat_time_precision_table_len
Definition xlat_eval.c:125
static fr_dict_attr_t const * attr_cast_time_res_week
Definition xlat_eval.c:49
unlang_result_t result
Definition xlat_eval.c:1012
static fr_table_ptr_ordered_t const xlat_time_precision_table[]
Definition xlat_eval.c:87
static fr_slen_t xlat_fmt_print(fr_sbuff_t *out, xlat_exp_t const *node)
Reconstruct the original expansion string from an xlat tree.
Definition xlat_eval.c:148
static void xlat_debug_log_expansion(request_t *request, xlat_exp_t const *node, fr_value_box_list_t const *args, UNUSED int line)
Output what we're currently expanding.
Definition xlat_eval.c:221
xlat_action_t xlat_frame_eval_repeat(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_exp_head_t const **child, request_t *request, xlat_exp_head_t const *head, xlat_exp_t const **in, void *env_data, fr_value_box_list_t *result)
Process the result of a previous nested expansion.
Definition xlat_eval.c:1187
void xlat_signal(xlat_func_signal_t signal, xlat_exp_t const *exp, request_t *request, void *rctx, fr_signal_t action)
Signal an xlat function.
Definition xlat_eval.c:1079
fr_dict_attr_t const * xlat_time_res_attr(char const *res)
Definition xlat_eval.c:127
static void xlat_debug_log_result(request_t *request, xlat_exp_t const *node, fr_value_box_t const *result)
Output the result of an expansion.
Definition xlat_eval.c:289
int xlat_eval_init(void)
Definition xlat_eval.c:2040
fr_table_num_sorted_t const xlat_action_table[]
Definition xlat_eval.c:76
xlat_action_t xlat_frame_eval_resume(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_exp_head_t const **child, request_t *request, xlat_exp_head_t const *head, xlat_exp_t const **in, fr_value_box_list_t *result, xlat_func_t resume, void *rctx)
Call an xlat's resumption method.
Definition xlat_eval.c:1113
static fr_dict_attr_t const * attr_cast_time_res_year
Definition xlat_eval.c:51
static fr_dict_t const * dict_freeradius
Definition xlat_eval.c:35
static fr_dict_attr_t const * attr_cast_time_res_nsec
Definition xlat_eval.c:55
static xlat_action_t xlat_eval_one_letter(TALLOC_CTX *ctx, fr_value_box_list_t *out, request_t *request, char letter)
One letter expansions.
Definition xlat_eval.c:827
static fr_dict_attr_t const * attr_cast_time_res_month
Definition xlat_eval.c:50
static fr_dict_attr_t const * attr_cast_time_res_usec
Definition xlat_eval.c:54
fr_dict_attr_t const * attr_expr_bool_enum
Definition xlat_eval.c:42
static fr_dict_attr_t const * attr_cast_time_res_csec
Definition xlat_eval.c:52
size_t xlat_action_table_len
Definition xlat_eval.c:82
static ssize_t _xlat_eval(TALLOC_CTX *ctx, char **out, size_t outlen, request_t *request, char const *fmt, xlat_escape_legacy_t escape, void const *escape_ctx)
Replace whatever in a string.
Definition xlat_eval.c:1846
static xlat_action_t xlat_process_arg_list(TALLOC_CTX *ctx, fr_value_box_list_t *list, request_t *request, char const *name, xlat_arg_parser_t const *arg, xlat_exp_t const *node, unsigned int arg_num)
Process an individual xlat argument value box group.
Definition xlat_eval.c:363
static fr_dict_autoload_t xlat_eval_dict[]
Definition xlat_eval.c:37
static fr_dict_attr_t const * attr_cast_time_res_msec
Definition xlat_eval.c:53
fr_dict_attr_t const * attr_cast_base
Definition xlat_eval.c:43
fr_value_box_list_t list
Definition xlat_eval.c:1011
ssize_t xlat_eval(char *out, size_t outlen, request_t *request, char const *fmt, xlat_escape_legacy_t escape, void const *escape_ctx)
Definition xlat_eval.c:1895
xlat_action_t xlat_frame_eval(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_exp_head_t const **child, request_t *request, xlat_exp_head_t const *head, xlat_exp_t const **in)
Converts xlat nodes to value boxes.
Definition xlat_eval.c:1397
int xlat_flatten_to_argv(TALLOC_CTX *ctx, xlat_exp_head_t ***argv, xlat_exp_head_t *head)
Turn am xlat list into an argv[] array, and nuke the input list.
Definition xlat_eval.c:1934
static fr_dict_attr_autoload_t xlat_eval_dict_attr[]
Definition xlat_eval.c:57
static xlat_action_t xlat_process_args(TALLOC_CTX *ctx, fr_value_box_list_t *list, request_t *request, xlat_exp_t const *node)
Process list of boxed values provided as input to an xlat.
Definition xlat_eval.c:617
static bool xlat_process_return(request_t *request, xlat_t const *func, fr_value_box_list_t const *returned, fr_value_box_t *pos)
Validate that the return values from an xlat function match what it registered.
Definition xlat_eval.c:776
void xlat_eval_free(void)
Definition xlat_eval.c:2062
static int xlat_arg_stringify(request_t *request, xlat_arg_parser_t const *arg, xlat_exp_t const *node, fr_value_box_t *vb)
Definition xlat_eval.c:298
int xlat_eval_walk(xlat_exp_head_t *head, xlat_walker_t walker, xlat_type_t type, void *uctx)
Walk over all xlat nodes (depth first) in a xlat expansion, calling a callback.
Definition xlat_eval.c:1976
static fr_dict_attr_t const * attr_cast_time_res_sec
Definition xlat_eval.c:45
static xlat_action_t xlat_null_resume(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, UNUSED fr_value_box_list_t *in)
Definition xlat_eval.c:1087
ssize_t xlat_aeval(TALLOC_CTX *ctx, char **out, request_t *request, char const *fmt, xlat_escape_legacy_t escape, void const *escape_ctx)
Definition xlat_eval.c:1911
static fr_dict_attr_t const * attr_cast_time_res_day
Definition xlat_eval.c:48
static ssize_t _xlat_eval_compiled(TALLOC_CTX *ctx, char **out, size_t outlen, request_t *request, xlat_exp_head_t const *head, xlat_escape_legacy_t escape, void const *escape_ctx)
Replace whatever in a string.
Definition xlat_eval.c:1792
ssize_t xlat_aeval_compiled(TALLOC_CTX *ctx, char **out, request_t *request, xlat_exp_head_t const *xlat, xlat_escape_legacy_t escape, void const *escape_ctx)
Definition xlat_eval.c:1920
static int xlat_sync_stringify(TALLOC_CTX *ctx, request_t *request, xlat_exp_head_t const *head, fr_value_box_list_t *list, xlat_escape_legacy_t escape, void const *escape_ctx)
Definition xlat_eval.c:1635
static void xlat_debug_log_list_result(request_t *request, xlat_exp_t const *node, fr_value_box_list_t const *result)
Output the list result of an expansion.
Definition xlat_eval.c:274
static fr_dict_attr_t const * attr_cast_time_res_min
Definition xlat_eval.c:46
static ssize_t xlat_eval_sync(TALLOC_CTX *ctx, char **out, request_t *request, xlat_exp_head_t const *const head, xlat_escape_legacy_t escape, void const *escape_ctx)
Definition xlat_eval.c:1712
static xlat_action_t xlat_exec_resume(UNUSED TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx, UNUSED request_t *request, UNUSED fr_value_box_list_t *in)
Definition xlat_eval.c:1015
static fr_dict_attr_t const * attr_cast_time_res_hour
Definition xlat_eval.c:47
static int instance_count
Definition xlat_eval.c:33
#define XLAT_DEBUG(...)
Definition xlat_expr.c:38
char const * name
Name of xlat function.
Definition xlat_priv.h:64
xlat_flags_t flags
Flags that control resolution and evaluation.
Definition xlat_priv.h:154
fr_type_t return_type
Function is guaranteed to return one or more boxes of this type.
Definition xlat_priv.h:101
static xlat_exp_t * xlat_exp_next(xlat_exp_head_t const *head, xlat_exp_t const *node)
Definition xlat_priv.h:247
xlat_func_t func
async xlat function (async unsafe).
Definition xlat_priv.h:65
int(* xlat_walker_t)(xlat_exp_t *exp, void *uctx)
Walker callback for xlat_walk()
Definition xlat_priv.h:268
fr_token_t quote
Type of quoting around XLAT_GROUP types.
Definition xlat_priv.h:152
xlat_type_t
Definition xlat_priv.h:106
@ XLAT_ONE_LETTER
Special "one-letter" expansion.
Definition xlat_priv.h:109
@ XLAT_BOX
fr_value_box_t
Definition xlat_priv.h:108
@ XLAT_TMPL
xlat attribute
Definition xlat_priv.h:112
@ XLAT_FUNC
xlat module
Definition xlat_priv.h:110
@ XLAT_GROUP
encapsulated string of xlats
Definition xlat_priv.h:116
@ XLAT_FUNC_UNRESOLVED
func needs resolution during pass2.
Definition xlat_priv.h:111
@ XLAT_INVALID
Bad expansion.
Definition xlat_priv.h:107
xlat_arg_parser_t const * args
Definition of args consumed.
Definition xlat_priv.h:94
char const *_CONST fmt
The original format string (a talloced buffer).
Definition xlat_priv.h:151
xlat_type_t _CONST type
type of this expansion.
Definition xlat_priv.h:155
fr_value_box_safe_for_t return_safe_for
Escaped value to set in output boxes.
Definition xlat_priv.h:100
#define xlat_exp_foreach(_list_head, _iter)
Iterate over the contents of a list, only one level.
Definition xlat_priv.h:223
static xlat_exp_t * xlat_exp_head(xlat_exp_head_t const *head)
Definition xlat_priv.h:210
An xlat expansion node.
Definition xlat_priv.h:148