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