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