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