The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
Loading...
Searching...
No Matches
xlat_expr.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: f0fdccaac1d76ad07538a40959b7563e6c273ff3 $
19 *
20 * @file xlat_expr.c
21 * @brief Tokenizers and support functions for xlat expressions
22 *
23 * @copyright 2021 The FreeRADIUS server project
24 * @copyright 2021 Network RADIUS SAS (legal@networkradius.com)
25 */
26RCSID("$Id: f0fdccaac1d76ad07538a40959b7563e6c273ff3 $")
27
28#include <freeradius-devel/server/base.h>
29#include <freeradius-devel/unlang/xlat_priv.h>
30#include <freeradius-devel/util/calc.h>
31#include <freeradius-devel/util/debug.h>
32#include <freeradius-devel/server/tmpl_dcursor.h>
33
34#undef XLAT_DEBUG
35#ifdef DEBUG_XLAT
36# define XLAT_DEBUG(_fmt, ...) DEBUG3("%s[%i] "_fmt, __FILE__, __LINE__, ##__VA_ARGS__)
37#else
38# define XLAT_DEBUG(...)
39#endif
40
41/*
42 * The new tokenizer accepts most things which are accepted by the old one. Many of the errors will be
43 * different, though.
44 *
45 * @todo - add a "output" fr_type_t to xlat_t, which is mainly used by the comparison functions. Right
46 * now it will happily parse things like:
47 *
48 * (1 < 2) < 3
49 *
50 * though the result of (1 < 2) is a boolean, so the result is always true. We probably want to have
51 * that as a compile-time error / check. This can probably just be done with xlat_purify() ? which
52 * doesn't need to interpret the LHS, but just knows its limits. We perhaps want a "range compare"
53 * function, which just checks ranges on one side against values on the right.
54 *
55 * Even worse, when we do "((bool) 1) < 3", the RHS is cast to the type of the LHS by
56 * tmpl_afrom_substr(). This is because we pass the LHS data type recursively down, which works most of
57 * the time, but not all of the time. There are currently hacks in the "upcast" code here to fix this,
58 * but it's a hack.
59 *
60 * @todo - add instantiation routines for assignment operations. This lets us do things
61 * like:
62 * if ((&foo += 4) > 6) ...
63 *
64 * However, this would also require us adding an edit list pointer to the xlat evaluation functions,
65 * which is not trivial. Or, maybe we attach it to the request somehow?
66 */
67
68static xlat_exp_t *xlat_exists_alloc(TALLOC_CTX *ctx, xlat_exp_t *child);
69
70static void xlat_func_append_arg(xlat_exp_t *head, xlat_exp_t *node, bool exists)
71{
72 xlat_exp_t *group;
73
74 fr_assert(head->type == XLAT_FUNC);
75
76 /*
77 * Wrap existence checks for attribute reference.
78 */
79 if (exists && (node->type == XLAT_TMPL) && tmpl_contains_attr(node->vpt)) {
80 node = xlat_exists_alloc(head, node);
81 }
82
83 if (!head->call.args) {
84 MEM(head->call.args = xlat_exp_head_alloc(head));
85 head->call.args->is_argv = true;
86 }
87
88 /*
89 * Wrap it in a group.
90 */
91 group = xlat_exp_alloc(head->call.args, XLAT_GROUP, NULL, 0);
92 group->quote = T_BARE_WORD;
93
94 xlat_exp_set_name_shallow(group, node->fmt); /* not entirely correct, but good enough for now */
95 group->flags = node->flags;
96
97 talloc_steal(group->group, node);
98 xlat_exp_insert_tail(group->group, node);
99
100 xlat_exp_insert_tail(head->call.args, group);
101
102 xlat_flags_merge(&head->flags, &head->call.args->flags);
103}
104
105
106/** Allocate a specific cast node.
107 *
108 * With the first argument being a UINT8 of the data type.
109 * See xlat_func_cast() for the implementation.
110 *
111 */
112static xlat_exp_t *xlat_exists_alloc(TALLOC_CTX *ctx, xlat_exp_t *child)
113{
114 xlat_exp_t *node;
115
116 fr_assert(child->type == XLAT_TMPL);
117 fr_assert(tmpl_contains_attr(child->vpt));
118
119 /*
120 * Create an "exists" node.
121 */
122 MEM(node = xlat_exp_alloc(ctx, XLAT_FUNC, "exists", 6));
123 xlat_exp_set_name_shallow(node, child->vpt->name);
124
125 MEM(node->call.func = xlat_func_find("exists", 6));
126 fr_assert(node->call.func != NULL);
127
128 /*
129 * The attribute may need resolving, in which case we have to set the flag as appropriate.
130 */
131 node->flags = (xlat_flags_t) { .needs_resolving = tmpl_needs_resolving(child->vpt)};
132
133 if (!node->flags.needs_resolving) node->call.dict = tmpl_attr_tail_da(child->vpt)->dict;
134
135 xlat_func_append_arg(node, child, false);
136
137 XLAT_VERIFY(node);
138
139 return node;
140}
141
142
144{
145 size_t at_in = fr_sbuff_used_total(out);
146
147 FR_SBUFF_IN_STRCPY_RETURN(out, fr_tokens[node->call.func->token]);
148 xlat_print_node(out, node->call.args, xlat_exp_head(node->call.args), e_rules, 0);
149
150 return fr_sbuff_used_total(out) - at_in;
151}
152
154{
155 size_t at_in = fr_sbuff_used_total(out);
156 xlat_exp_t *child;
157
158 XLAT_VERIFY(node);
159
160 child = xlat_exp_head(node->call.args);
161 fr_assert(child != NULL);
162
164 xlat_print_node(out, node->call.args, child, e_rules, 0); /* prints a space after the first argument */
165
166 FR_SBUFF_IN_STRCPY_RETURN(out, fr_tokens[node->call.func->token]);
168
169 child = xlat_exp_next(node->call.args, child);
170 if (child) {
171 xlat_print_node(out, node->call.args, child, e_rules, 0);
172 } else {
174 }
175
177
178 return fr_sbuff_used_total(out) - at_in;
179}
180
181static int xlat_expr_resolve_binary(xlat_exp_t *node, UNUSED void *inst, xlat_res_rules_t const *xr_rules)
182{
183 xlat_exp_t *arg1, *arg2;
184 xlat_exp_t *a, *b;
185 tmpl_res_rules_t my_tr_rules;
186
187 XLAT_DEBUG("RESOLVE %s\n", node->fmt);
188
189 arg1 = xlat_exp_head(node->call.args);
190 fr_assert(arg1);
191 fr_assert(arg1->type == XLAT_GROUP);
192
193 arg2 = xlat_exp_next(node->call.args, arg1);
194 fr_assert(arg2);
195 fr_assert(arg2->type == XLAT_GROUP);
196
197 a = xlat_exp_head(arg1->group);
198 b = xlat_exp_head(arg2->group);
199
200 /*
201 * We have many things here, just call resolve recursively.
202 */
203 if (xlat_exp_next(arg1->group, a) || (xlat_exp_next(arg2->group, b))) goto resolve;
204
205 /*
206 * Anything else must get resolved at run time.
207 */
208 if ((a->type != XLAT_TMPL) || (b->type != XLAT_TMPL)) goto resolve;
209
210 /*
211 * The tr_rules should always contain dict_def
212 */
213 fr_assert(xr_rules); /* always set by xlat_resolve() */
214 if (xr_rules->tr_rules) {
215 my_tr_rules = *xr_rules->tr_rules;
216 } else {
217 my_tr_rules = (tmpl_res_rules_t) { };
218 }
219
220 /*
221 * The LHS attribute dictates the enumv for the RHS one.
222 */
223 if (tmpl_contains_attr(a->vpt)) {
224 XLAT_DEBUG("\ta - %s %s\n", a->fmt, b->fmt);
225
226 if (a->flags.needs_resolving) {
227 XLAT_DEBUG("\tresolve attr a\n");
228 if (tmpl_resolve(a->vpt, &my_tr_rules) < 0) return -1;
229 a->flags.needs_resolving = false;
230 }
231
232 my_tr_rules.enumv = tmpl_attr_tail_da(a->vpt);
233
234 XLAT_DEBUG("\tresolve other b\n");
235 if (tmpl_resolve(b->vpt, &my_tr_rules) < 0) return -1;
236
237 b->flags.needs_resolving = false;
238 b->flags.pure = tmpl_is_data(b->vpt);
239 b->flags.constant = b->flags.pure;
240 goto flags;
241 }
242
243 if (tmpl_contains_attr(b->vpt)) {
244 XLAT_DEBUG("\tb - %s %s\n", a->fmt, b->fmt);
245
246 if (b->flags.needs_resolving) {
247 XLAT_DEBUG("\tresolve attr b\n");
248 if (tmpl_resolve(b->vpt, &my_tr_rules) < 0) return -1;
249
250 b->flags.needs_resolving = false;
251 }
252
253 my_tr_rules.enumv = tmpl_attr_tail_da(b->vpt);
254
255 XLAT_DEBUG("\tresolve other a\n");
256 if (tmpl_resolve(a->vpt, &my_tr_rules) < 0) return -1;
257
258 a->flags.needs_resolving = false;
259 a->flags.pure = tmpl_is_data(a->vpt);
260 a->flags.constant = a->flags.pure;
261 goto flags;
262 }
263
264resolve:
265 /*
266 * This call will fix everything recursively.
267 */
268 return xlat_resolve(node->call.args, xr_rules);
269
270flags:
271 arg1->flags = arg1->group->flags = a->flags;
272 arg2->flags = arg2->group->flags = b->flags;
273 xlat_flags_merge(&node->call.args->flags, &arg2->flags);
274
277
280
281 node->call.args->flags.needs_resolving = false;
282 xlat_flags_merge(&node->flags, &node->call.args->flags);
283
284 return 0;
285}
286
288{
289 switch (type) {
290 case FR_TYPE_STRING:
291 fr_value_box_strdup_shallow(vb, NULL, "", false);
292 break;
293
294 case FR_TYPE_OCTETS:
295 fr_value_box_memdup_shallow(vb, NULL, (void const *) "", 0, false);
296 break;
297
298 default:
299 fr_value_box_init(vb, type, NULL, false);
300 break;
301 }
302}
303
304/*
305 * While binary operations do require two arguments, handling that
306 * with the xlat argument parser is too aggressive, and causes unlang like
307 * `foo = bar + baz` to set the rcode to fail, rather than soft failing
308 * as would be expected.
309 */
311 { .required = false, .type = FR_TYPE_VOID },
312 { .required = false, .type = FR_TYPE_VOID },
314};
315
317 UNUSED xlat_ctx_t const *xctx,
318 request_t *request, fr_value_box_list_t *in,
319 fr_token_t op,
320 fr_type_t default_type, fr_dict_attr_t const *enumv)
321{
322 int rcode;
323 fr_value_box_t *dst, *a, *b;
324 fr_value_box_t one, two;
325
326 MEM(dst = fr_value_box_alloc_null(ctx));
327
328 /*
329 * Each argument is a FR_TYPE_GROUP, with one or more elements in a list.
330 */
331 a = fr_value_box_list_head(in);
332 if (!a) {
333 REDEBUG("Left argument to %s is missing", fr_tokens[op]);
334 fail:
335 talloc_free(dst);
336 return XLAT_ACTION_FAIL;
337 }
338
339 b = fr_value_box_list_next(in, a);
340 if (!b) {
341 REDEBUG("Right argument to %s is missing", fr_tokens[op]);
342 goto fail;
343 }
344
346
347 if (fr_value_box_list_num_elements(&a->vb_group) > 1) {
348 REDEBUG("Expected one value as the first argument, got %u",
349 fr_value_box_list_num_elements(&a->vb_group));
350 goto fail;
351 }
352 a = fr_value_box_list_head(&a->vb_group);
353
354 if (fr_value_box_list_num_elements(&b->vb_group) > 1) {
355 REDEBUG("Expected one value as the second argument, got %u",
356 fr_value_box_list_num_elements(&b->vb_group));
357 goto fail;
358 }
359 b = fr_value_box_list_head(&b->vb_group);
360
361 if (!a) {
362 a = &one;
363 fr_value_box_init_zero(a, b ? b->type : default_type);
364 }
365
366 if (!b) {
367 b = &two;
368 fr_value_box_init_zero(b, a ? a->type : default_type);
369 }
370
371 rcode = fr_value_calc_binary_op(dst, dst, default_type, a, op, b);
372 if (rcode < 0) {
373 RPEDEBUG("Failed calculating '%pR %s %pR'", a, fr_tokens[op], b);
374 goto fail;
375 }
376
377 /*
378 * Over-write, but only if it's present. Otherwise leave
379 * any existing enum alone.
380 */
381 if (enumv) dst->enumv = enumv;
383
384 VALUE_BOX_LIST_VERIFY((fr_value_box_list_t *)fr_dcursor_list(out));
385 return XLAT_ACTION_DONE;
386}
387
388#define XLAT_BINARY_FUNC(_name, _op) \
389static xlat_action_t xlat_func_ ## _name(TALLOC_CTX *ctx, fr_dcursor_t *out, \
390 xlat_ctx_t const *xctx, \
391 request_t *request, fr_value_box_list_t *in) \
392{ \
393 return xlat_binary_op(ctx, out, xctx, request, in, _op, FR_TYPE_NULL, NULL); \
394}
395
396XLAT_BINARY_FUNC(op_add, T_ADD)
397XLAT_BINARY_FUNC(op_sub, T_SUB)
398XLAT_BINARY_FUNC(op_mul, T_MUL)
399XLAT_BINARY_FUNC(op_div, T_DIV)
400XLAT_BINARY_FUNC(op_mod, T_MOD)
401XLAT_BINARY_FUNC(op_and, T_AND)
403XLAT_BINARY_FUNC(op_xor, T_XOR)
404XLAT_BINARY_FUNC(op_rshift, T_RSHIFT)
405XLAT_BINARY_FUNC(op_lshift, T_LSHIFT)
406
407/*
408 * While binary operations do require two arguments, handling that
409 * with the xlat argument parser is too aggressive, and causes unlang like
410 * `if (foo == bar)` to set the rcode to fail, rather than soft failing
411 * as would be expected.
412 */
414 { .required = false, .type = FR_TYPE_VOID },
415 { .required = false, .type = FR_TYPE_VOID },
417};
418
419/*
420 * @todo - arguably this function should process its own arguments, like logical_or.
421 *
422 * That way it can return XLAT_ACTION_FAIL if either argument fails to be found,
423 * or if the comparison matches, it returns the RHS value. This behavior will
424 * let us to multiple comparisons, like:
425 *
426 * if (0 < x < 5) ...
427 *
428 * Which is then syntactic sugar for
429 *
430 * if ((0 < x) < 5) ...
431 *
432 * That means the comparisons no longer return "bool", so if we
433 * want to do this, the change has to be made before v4 is
434 * released.
435 *
436 * Other operators like "+=" would return their LHS value. But in order to do that, we would have to
437 * update the expression parser to allow in-place edits, and that may be a fair bit of work.
438 *
439 * It also lets us do more interesting selectors, such as:
440 *
441 * foo || (0 < x)
442 *
443 * which if "foo" doesn't exist, evaluates the RHS, and then returns "x" only if x is greater than zero.
444 * This short-hand can remove a lot of complex / nested "if" conditions.
445 *
446 * It could also allow us to do better attribute filtering:
447 *
448 * foo := (0 < foo < 5)
449 *
450 * Which ensures that "foo" has value only 1..4.
451 *
452 * It would be nice to have a syntax for "self", so we could instead do:
453 *
454 * foo := (0 < $$ < 5)
455 *
456 * Which could then also be used inside of attribute selectors:
457 *
458 * foreach foo (Vendor-Specific.Cisco.AVPair[$$ =~ /^x/]) { ...
459 *
460 * and now that we have pair cursors as value-boxes, this becomes a lot easier.
461 * "$$" then becomes syntactic sugar for "the pair at the current cursor".
462 *
463 * It also means tracking somehow the value of $$ in the interpreter? Maybe as a short-hand, just update
464 * the #request_t to add a #fr_pair_t of the current cursor value. This is a horrible hack, but would be
465 * easy to do. It doesn't allow nested cursors, but whatever. The syntax for that would be hard to get
466 * right.
467 */
468static xlat_action_t xlat_cmp_op(TALLOC_CTX *ctx, fr_dcursor_t *out,
469 UNUSED xlat_ctx_t const *xctx,
470 UNUSED request_t *request, fr_value_box_list_t *in,
471 fr_token_t op)
472{
473 int rcode;
474 fr_value_box_t *dst, *a, *b;
475
476 /*
477 * Each argument is a FR_TYPE_GROUP, with one or more elements in a list.
478 */
479 a = fr_value_box_list_head(in);
480 if (!a) {
481 REDEBUG("Left argument to %s is missing", fr_tokens[op]);
482 return XLAT_ACTION_FAIL;
483 }
484
485 b = fr_value_box_list_next(in, a);
486 if (!b) {
487 REDEBUG("Right argument to %s is missing", fr_tokens[op]);
488 return XLAT_ACTION_FAIL;
489 }
490
491 fr_assert(a->type == FR_TYPE_GROUP);
492 fr_assert(b->type == FR_TYPE_GROUP);
493
495
497
498 rcode = fr_value_calc_list_cmp(dst, dst, &a->vb_group, op, &b->vb_group);
499 if (rcode < 0) {
500 talloc_free(dst);
501 return XLAT_ACTION_FAIL;
502 }
503
504 fr_assert(dst->type == FR_TYPE_BOOL);
505 dst->enumv = attr_expr_bool_enum;
506
508 VALUE_BOX_LIST_VERIFY((fr_value_box_list_t *)fr_dcursor_list(out));
509 return XLAT_ACTION_DONE;
510}
511
512
513#define XLAT_CMP_FUNC(_name, _op) \
514static xlat_action_t xlat_func_ ## _name(TALLOC_CTX *ctx, fr_dcursor_t *out, \
515 xlat_ctx_t const *xctx, \
516 request_t *request, fr_value_box_list_t *in) \
517{ \
518 return xlat_cmp_op(ctx, out, xctx, request, in, _op); \
519}
520
522XLAT_CMP_FUNC(cmp_ne, T_OP_NE)
523XLAT_CMP_FUNC(cmp_lt, T_OP_LT)
524XLAT_CMP_FUNC(cmp_le, T_OP_LE)
525XLAT_CMP_FUNC(cmp_gt, T_OP_GT)
526XLAT_CMP_FUNC(cmp_ge, T_OP_GE)
529
530typedef struct {
532 regex_t *regex; //!< precompiled regex
533 xlat_exp_t *xlat; //!< to expand
534 fr_regex_flags_t *regex_flags;
536
537typedef struct {
539 fr_value_box_list_t list;
541
542static fr_slen_t xlat_expr_print_regex(fr_sbuff_t *out, xlat_exp_t const *node, void *instance, fr_sbuff_escape_rules_t const *e_rules)
543{
544 size_t at_in = fr_sbuff_used_total(out);
545 xlat_exp_t *child = xlat_exp_head(node->call.args);
546 xlat_regex_inst_t *inst = instance;
547
548 fr_assert(child != NULL);
549
551 xlat_print_node(out, node->call.args, child, e_rules, 0);
552
553 /*
554 * A space is printed after the first argument only if
555 * there's a second one. So add one if we "ate" the second argument.
556 */
557 if (inst->xlat) FR_SBUFF_IN_CHAR_RETURN(out, ' ');
558
559 FR_SBUFF_IN_STRCPY_RETURN(out, fr_tokens[node->call.func->token]);
561
562 /*
563 * Regexes which aren't instantiated: only for unit tests.
564 */
565 if (!inst->xlat) {
566 child = xlat_exp_next(node->call.args, child);
567
568 if (!child) goto done;
569 fr_assert(!xlat_exp_next(node->call.args, child));
570 fr_assert(child->type == XLAT_GROUP);
571
575
576 child = xlat_exp_head(child->group);
577 if (!child) goto done;
578 fr_assert(child->type == XLAT_TMPL);
579
580 /*
581 * The RHS may be a group
582 */
583 FR_SBUFF_RETURN(regex_flags_print, out, tmpl_regex_flags(child->vpt));
584 goto done;
585 }
586
588
589 if (inst->xlat->quote == T_SINGLE_QUOTED_STRING) FR_SBUFF_IN_CHAR_RETURN(out, 'm');
591 FR_SBUFF_IN_STRCPY_RETURN(out, inst->xlat->vpt->name);
593
594 FR_SBUFF_RETURN(regex_flags_print, out, inst->regex_flags);
595
596done:
598
599 return fr_sbuff_used_total(out) - at_in;
600}
601
602
603/*
604 * Each argument is it's own head, because we do NOT always want
605 * to go to the next argument.
606 */
608{
609 xlat_regex_inst_t *inst = talloc_get_type_abort(xctx->inst, xlat_regex_inst_t);
610 xlat_exp_t *lhs, *rhs, *regex;
611
612 lhs = xlat_exp_head(xctx->ex->call.args);
613 rhs = xlat_exp_next(xctx->ex->call.args, lhs);
614
615 fr_assert(rhs);
616 fr_assert(rhs->type == XLAT_GROUP);
617 (void) fr_dlist_remove(&xctx->ex->call.args->dlist, rhs);
618
619 regex = xlat_exp_head(rhs->group);
620 fr_assert(regex->type == XLAT_TMPL);
621 fr_assert(tmpl_contains_regex(regex->vpt));
622
623 inst->op = xctx->ex->call.func->token;
624 inst->regex_flags = tmpl_regex_flags(regex->vpt);
625
626 inst->xlat = talloc_steal(inst, regex);
627 talloc_free(rhs); /* group wrapper is no longer needed */
628
629 /*
630 * The RHS is more then just one regex node, it has to be dynamically expanded.
631 */
632 if (tmpl_contains_xlat(regex->vpt)) {
633 return 0;
634 }
635
636 if (tmpl_is_data_unresolved(regex->vpt)) {
637 fr_strerror_const("Regex must be resolved before instantiation");
638 return -1;
639 }
640
641 /*
642 * Must have been caught in the parse phase.
643 */
644 fr_assert(tmpl_is_regex(regex->vpt));
645
646 inst->regex = tmpl_regex(regex->vpt);
647
648 return 0;
649}
650
651
653 { .required = true, .type = FR_TYPE_STRING },
654 { .concat = true, .type = FR_TYPE_STRING },
656};
657
659 /*
660 * A pattern to be compiled has to be escaped, unless it
661 * is marked as REGEX_SAFE_FOR.
662 */
663 { .required = true, .concat = true, .type = FR_TYPE_STRING,
664 .func = xlat_regex_escape, .safe_for = FR_REGEX_SAFE_FOR }, /* regex string */
665 { .required = true, .concat = false, .type = FR_TYPE_STRING }, /* broken out things to match */
666 { .required = false, .concat = true, .type = FR_TYPE_STRING }, /* flags */
668};
669
670
671/** Perform a regular expressions comparison between two operands
672 *
673 * @param[in] ctx to allocate resulting box in.
674 * @param[in] request The current request.
675 * @param[in] in list of item or items
676 * @param[in,out] preg Pointer to pre-compiled or runtime-compiled
677 * regular expression. In the case of runtime-compiled
678 * the pattern may be stolen by the `regex_sub_to_request`
679 * function as the original pattern is needed to resolve
680 * capture groups.
681 * The caller should only free the `regex_t *` if it
682 * compiled it, and the pointer has not been set to NULL
683 * when this function returns.
684 * @param[out] out Where result is written.
685 * @param[in] op the operation to perform.
686 * @return
687 * - -1 on failure.
688 * - 0 for "no match".
689 * - 1 for "match".
690 */
691static xlat_action_t xlat_regex_do_op(TALLOC_CTX *ctx, request_t *request, fr_value_box_list_t *in, regex_t **preg,
693{
694 uint32_t subcaptures;
695 int ret = 0;
696
697 fr_regmatch_t *regmatch;
698 fr_value_box_t *dst;
699 fr_value_box_t *arg, *vb;
700 fr_sbuff_t *agg;
701 char const *subject;
702 size_t len;
703 fr_value_box_safety_t safety = {};
704
705 FR_SBUFF_TALLOC_THREAD_LOCAL(&agg, 256, 8192);
706
707 arg = fr_value_box_list_head(in);
708 fr_assert(arg != NULL);
709 fr_assert(arg->type == FR_TYPE_GROUP);
710
711 subcaptures = regex_subcapture_count(*preg);
712 if (!subcaptures) subcaptures = REQUEST_MAX_REGEX + 1; /* +1 for %{0} (whole match) capture group */
713 MEM(regmatch = regex_match_data_alloc(NULL, subcaptures));
714
715 while ((vb = fr_value_box_list_pop_head(&arg->vb_group)) != NULL) {
716 if (vb->type == FR_TYPE_STRING) {
717 subject = vb->vb_strvalue;
718 len = vb->vb_length;
719 safety = vb->safety;
720
721 } else {
722 fr_value_box_list_t list;
723
724 fr_value_box_list_init(&list);
725 fr_value_box_list_insert_head(&list, vb);
727
728 vb = NULL;
729
730 /*
731 * Concatenate everything, and escape untrusted inputs.
732 */
733 if (fr_value_box_list_concat_as_string(&safety, agg, &list, NULL, 0, &regex_escape_rules,
734 FR_VALUE_BOX_LIST_FREE_BOX, FR_REGEX_SAFE_FOR, true) < 0) {
735 RPEDEBUG("Failed concatenating regular expression string");
736 talloc_free(regmatch);
737 return XLAT_ACTION_FAIL;
738 }
739
740 subject = fr_sbuff_start(agg);
741 len = fr_sbuff_used(agg);
742 }
743
744 /*
745 * Evaluate the expression
746 */
747 ret = regex_exec(*preg, subject, len, regmatch);
748 talloc_free(vb);
749
750 switch (ret) {
751 default:
752 RPEDEBUG("REGEX failed");
753 talloc_free(regmatch);
754 return XLAT_ACTION_FAIL;
755
756 case 0:
757 regex_sub_to_request(request, NULL, NULL, NULL); /* clear out old entries */
758 continue;
759
760 case 1:
761 regex_sub_to_request(request, preg, &regmatch, &safety);
762 goto done;
763 }
764 }
765
766done:
767 talloc_free(regmatch); /* free if not consumed */
768
770 dst->vb_bool = (ret == (op == T_OP_REG_EQ));
771
773
774 return XLAT_ACTION_DONE;
775}
776
778 xlat_ctx_t const *xctx,
779 request_t *request, fr_value_box_list_t *in)
780{
782 xlat_regex_rctx_t *rctx = talloc_get_type_abort(xctx->rctx, xlat_regex_rctx_t);
783 ssize_t slen;
784 regex_t *preg = NULL;
785 fr_sbuff_t *agg;
786
787 FR_SBUFF_TALLOC_THREAD_LOCAL(&agg, 256, 8192);
788
789 /*
790 * If the expansions fails, then we fail the entire thing.
791 */
792 if (!XLAT_RESULT_SUCCESS(&rctx->last_result)) {
793 talloc_free(rctx);
794 return XLAT_ACTION_FAIL;
795 }
796
797 /*
798 * Because we expanded the RHS ourselves, the "concat"
799 * flag to the RHS argument is ignored. So we just
800 * concatenate it here. We escape the various untrusted inputs.
801 */
802 if (fr_value_box_list_concat_as_string(NULL, agg, &rctx->list, NULL, 0, &regex_escape_rules,
803 FR_VALUE_BOX_LIST_FREE_BOX, FR_REGEX_SAFE_FOR, true) < 0) {
804 RPEDEBUG("Failed concatenating regular expression string");
805 return XLAT_ACTION_FAIL;
806 }
807
808 fr_assert(inst->regex == NULL);
809
810 slen = regex_compile(rctx, &preg, fr_sbuff_start(agg), fr_sbuff_used(agg),
811 tmpl_regex_flags(inst->xlat->vpt), true, true); /* flags, allow subcaptures, at runtime */
812 if (slen <= 0) return XLAT_ACTION_FAIL;
813
814 return xlat_regex_do_op(ctx, request, in, &preg, out, inst->op);
815}
816
818 xlat_ctx_t const *xctx,
819 request_t *request, fr_value_box_list_t *in,
820 fr_token_t op)
821{
823 xlat_regex_rctx_t *rctx;
824 regex_t *preg;
825
826 /*
827 * Just run precompiled regexes.
828 */
829 if (inst->regex) {
830 preg = tmpl_regex(inst->xlat->vpt);
831
832 return xlat_regex_do_op(ctx, request, in, &preg, out, op);
833 }
834
836 fr_value_box_list_init(&rctx->list);
837
839 fail:
840 talloc_free(rctx);
841 return XLAT_ACTION_FAIL;
842 }
843
844 if (unlang_xlat_push(ctx, &rctx->last_result, &rctx->list,
845 request, tmpl_xlat(inst->xlat->vpt), UNLANG_SUB_FRAME) < 0) goto fail;
846
848}
849
850#define XLAT_REGEX_FUNC(_name, _op) \
851static xlat_action_t xlat_func_ ## _name(TALLOC_CTX *ctx, fr_dcursor_t *out, \
852 xlat_ctx_t const *xctx, \
853 request_t *request, fr_value_box_list_t *in) \
854{ \
855 return xlat_regex_op(ctx, out, xctx, request, in, _op); \
856}
857
860
862 UNUSED xlat_ctx_t const *xctx,
863 request_t *request, fr_value_box_list_t *in)
864{
865 ssize_t slen;
866 regex_t *preg;
867 fr_value_box_t *regex;
868 xlat_action_t action;
869
870 regex = fr_value_box_list_pop_head(in);
871 fr_assert(regex);
872 fr_assert(regex->type == FR_TYPE_STRING);
873
874 slen = regex_compile(ctx, &preg, regex->vb_strvalue, regex->vb_length,
875 NULL, true, true); /* flags, allow subcaptures, at runtime */
876 if (slen <= 0) {
877 RPEDEBUG("Failed parsing regular expression %pV", regex);
878 talloc_free(regex);
879 return XLAT_ACTION_FAIL;
880 }
881
882 action = xlat_regex_do_op(ctx, request, in, &preg, out, T_OP_REG_EQ);
883 talloc_free(regex);
884 talloc_free(preg);
885 return action;
886}
887
894
895typedef struct {
896 TALLOC_CTX *ctx;
898 fr_value_box_t *box; //!< output value-box
900 fr_value_box_list_t list;
902
903static fr_slen_t xlat_expr_print_nary(fr_sbuff_t *out, xlat_exp_t const *node, void *instance, fr_sbuff_escape_rules_t const *e_rules)
904{
905 size_t at_in = fr_sbuff_used_total(out);
906 xlat_logical_inst_t *inst = instance;
908
910
911 /*
912 * We might get called before the node is instantiated.
913 */
914 if (!inst->argv) {
915 head = node->call.args;
916
917 fr_assert(head != NULL);
918
919 xlat_exp_foreach(head, child) {
920 xlat_print_node(out, head, child, e_rules, 0);
921
922 if (!xlat_exp_next(head, child)) break;
923
924 FR_SBUFF_IN_STRCPY_RETURN(out, fr_tokens[node->call.func->token]);
926 }
927 } else {
928 int i;
929
930 for (i = 0; i < inst->argc; i++) {
931 xlat_print(out, inst->argv[i], e_rules);
932 if (i == (inst->argc - 1)) break;
933
935 FR_SBUFF_IN_STRCPY_RETURN(out, fr_tokens[node->call.func->token]);
936 if ((i + 1) < inst->argc) FR_SBUFF_IN_CHAR_RETURN(out, ' ');
937 }
938 }
939
941
942 return fr_sbuff_used_total(out) - at_in;
943}
944
945/*
946 * This returns "false" for "ignore this argument"
947 *
948 * result is "false" for "delete this argument"
949 * result is "true" for "return this argument".
950 */
951static bool xlat_node_matches_bool(bool *result, xlat_exp_t *parent, xlat_exp_head_t *head, bool sense)
952{
953 fr_value_box_t *box;
954 xlat_exp_t *node;
955
956 if (!head->flags.pure) return false;
957
958 node = xlat_exp_head(head);
959 if (!node || xlat_exp_next(head, node)) {
960 return false;
961 }
962
963 if (node->type == XLAT_BOX) {
964 box = &node->data;
965 goto check;
966 }
967
968 if (node->type != XLAT_TMPL) {
969 return false;
970 }
971
972 if (!tmpl_is_data(node->vpt)) {
973 return false;
974 }
975
976 box = tmpl_value(node->vpt);
977
978check:
979 /*
980 * On "true", replace the entire logical operation with the value-box.
981 *
982 * On "false", omit this argument, and go to the next one.
983 */
984 *result = (fr_value_box_is_truthy(box) == sense);
985
986 if (!*result) return true;
987
989
991 if (!fr_cond_assert(fr_value_box_copy(parent, &parent->data, box) == 0)) return false;
992
993 return true;
994}
995
996/** Undo work which shouldn't have been done. :(
997 *
998 */
1000{
1001 xlat_exp_t *group, *node;
1002
1003 group = xlat_exp_head(head);
1004 if (!group || xlat_exp_next(head, group)) return;
1005
1006 if (group->type != XLAT_GROUP) return;
1007
1008 node = xlat_exp_head(group->group);
1009 if (!node || xlat_exp_next(group->group, node)) return;
1010
1011 (void) fr_dlist_remove(&head->dlist, group);
1012 (void) fr_dlist_remove(&group->group->dlist, node);
1013 (void) talloc_steal(head, node);
1014
1015 talloc_free(group);
1016
1017 fr_dlist_insert_tail(&head->dlist, node);
1018 head->flags = node->flags;
1019}
1020
1021/** If any argument resolves to inst->stop_on_match, the entire thing is a bool of inst->stop_on_match.
1022 *
1023 * If any argument resolves to !inst->stop_on_match, it is removed.
1024 */
1025static int xlat_expr_logical_purify(xlat_exp_t *node, void *instance, request_t *request)
1026{
1027 int i, j;
1028 int deleted = 0;
1029 bool result;
1030 xlat_logical_inst_t *inst = talloc_get_type_abort(instance, xlat_logical_inst_t);
1031 xlat_exp_head_t *group;
1032
1033 fr_assert(node->type == XLAT_FUNC);
1034
1035 /*
1036 * Don't check the last argument. If everything else gets deleted,
1037 * then we just return the last argument.
1038 */
1039 for (i = 0; i < inst->argc; i++) {
1040 /*
1041 * The argument is pure, so we purify it before
1042 * doing any other checks.
1043 */
1044 if (inst->argv[i]->flags.can_purify) {
1045 if (xlat_purify_list(inst->argv[i], request) < 0) return -1;
1046
1047 /*
1048 * xlat_purify_list expects that its outputs will be arguments to functions, so
1049 * they're grouped. We con't need that, so we ungroup them here.
1050 */
1051 xlat_ungroup(inst->argv[i]);
1052 }
1053
1054 /*
1055 * This returns "false" for "ignore".
1056 *
1057 * result is "false" for "delete this argument"
1058 * result is "true" for "return this argument".
1059 */
1060 if (!xlat_node_matches_bool(&result, node, inst->argv[i], inst->stop_on_match)) continue;
1061
1062 /*
1063 * 0 && EXPR --> 0.
1064 * 1 || EXPR --> 1
1065 *
1066 * Parent is now an XLAT_BOX, so we're done.
1067 */
1068 if (result) return 0;
1069
1070 /*
1071 * We're at the last argument. If we've deleted everything else, then just leave the
1072 * last argument alone. Otherwise some arguments remain, so we can delete the last one.
1073 */
1074 if (((i + 1) == inst->argc) && (deleted == i)) break;
1075
1076 TALLOC_FREE(inst->argv[i]);
1077 deleted++;
1078 }
1079
1080 if (!deleted) return 0;
1081
1082 /*
1083 * Pack the array. We insert at i, and read from j. We don't need to read the deleted entries,
1084 * as they all MUST be NULL.
1085 */
1086 i = 0;
1087 j = -1;
1088 while (i < (inst->argc - deleted)) {
1089 if (j >= inst->argc) break;
1090
1091 if (inst->argv[i]) {
1092 i++;
1093 continue;
1094 }
1095
1096 /*
1097 * Start searching from the next entry, OR start searching from where we left off before.
1098 */
1099 if (j < 0) j = i + 1;
1100
1101 /*
1102 * Find the first non-NULL entry, and insert it in argv[i]. We search here until the end
1103 * of the array, because we may have deleted entries from the start of the array.
1104 */
1105 while (j < inst->argc) {
1106 if (inst->argv[j]) break;
1107 j++;
1108 }
1109
1110 /*
1111 * Move the entry down, and clear out the tail end of the array.
1112 */
1113 inst->argv[i++] = inst->argv[j];
1114 inst->argv[j++] = NULL;
1115 }
1116
1117 inst->argc -= deleted;
1118
1119 if (inst->argc > 1) return 0;
1120
1121 /*
1122 * Only one argument left. We can hoist the child into ourselves, and omit the logical operation.
1123 */
1124 group = inst->argv[0];
1125 fr_assert(group != NULL);
1126 talloc_steal(node, group);
1127
1130
1131 /* re-print, with purified nodes removed */
1132 {
1133 char *name;
1134
1135 MEM(xlat_aprint(node, &name, group, NULL) >= 0);
1137 }
1138
1139 talloc_free(node->group);
1140 node->group = group;
1141 node->flags = group->flags;
1142
1143 return 0;
1144}
1145
1146/** Process one argument of a logical operation.
1147 *
1148 * If we see a list in a truthy context, then we DON'T expand the list. Instead, we return a bool which
1149 * indicates if the list was empty (or not). This prevents us from returning a whole mess of value-boxes
1150 * when the user just wanted to see if the list existed.
1151 *
1152 * Otherwise, we expand the xlat, and continue.
1153 */
1155 xlat_ctx_t const *xctx,
1156 request_t *request, UNUSED fr_value_box_list_t *in)
1157{
1159 xlat_logical_rctx_t *rctx = talloc_get_type_abort(xctx->rctx, xlat_logical_rctx_t);
1160
1161 /*
1162 * Push the xlat onto the stack for expansion.
1163 */
1164 if (unlang_xlat_yield(request, inst->callback, NULL, 0, rctx) != XLAT_ACTION_YIELD) {
1165 fail:
1166 talloc_free(rctx->box);
1167 talloc_free(rctx);
1168 return XLAT_ACTION_FAIL;
1169 }
1170
1171 if (unlang_xlat_push(rctx, &rctx->last_result, &rctx->list,
1172 request, inst->argv[rctx->current], UNLANG_SUB_FRAME) < 0) goto fail;
1173
1175}
1176
1177/** See if the input is truthy or not.
1178 *
1179 * @param[in] rctx our ctx
1180 * @param[in] in list of value-boxes to check
1181 * @return
1182 * - false if there are no truthy values. The last box is copied to the rctx.
1183 * This is to allow us to return default values which may not be truthy,
1184 * e.g. %{&Counter || 0} or %{&Framed-IP-Address || 0.0.0.0}.
1185 * If we don't copy the last box to the rctx, the expression just returns NULL
1186 * which is never useful...
1187 * - true if we find a truthy value. The first truthy box is copied to the rctx.
1188 *
1189 * Empty lists are not truthy.
1190 */
1191static bool xlat_logical_or(xlat_logical_rctx_t *rctx, fr_value_box_list_t const *in)
1192{
1193 fr_value_box_t *last = NULL;
1194 bool ret = false;
1195
1196 /*
1197 * Empty lists are !truthy.
1198 */
1199 if (!fr_value_box_list_num_elements(in)) return false;
1200
1201 /*
1202 * Loop over the input list. We CANNOT do groups.
1203 */
1205 fr_assert(fr_type_is_leaf(box->type) || fr_type_is_null(box->type));
1206
1207 last = box;
1208
1209 /*
1210 * Remember the last box we found.
1211 *
1212 * If it's truthy, then we stop immediately.
1213 */
1214 if (fr_value_box_is_truthy(box)) {
1215 ret = true;
1216 break;
1217 }
1218 }
1219
1220 if (!rctx->box) {
1221 MEM(rctx->box = fr_value_box_alloc_null(rctx->ctx));
1222 } else {
1223 fr_value_box_clear(rctx->box);
1224 }
1225 if (last && !fr_cond_assert(fr_value_box_copy(rctx->box, rctx->box, last) == 0)) return false;
1226
1227 return ret;
1228}
1229
1230/*
1231 * We've evaluated an expression. Let's see if we need to continue with ||
1232 */
1234 xlat_ctx_t const *xctx,
1235 request_t *request, fr_value_box_list_t *in)
1236{
1238 xlat_logical_rctx_t *rctx = talloc_get_type_abort(xctx->rctx, xlat_logical_rctx_t);
1239 bool match;
1240
1241 /*
1242 * If the expansions fails, then we fail the entire thing.
1243 */
1244 if (!XLAT_RESULT_SUCCESS(&rctx->last_result)) {
1245 talloc_free(rctx->box);
1246 talloc_free(rctx);
1247 return XLAT_ACTION_FAIL;
1248 }
1249
1250 /*
1251 * Recursively check groups. i.e. we effectively flatten each list.
1252 *
1253 * (a, b, c) || (d, e, f) == a || b || c || d || e || f
1254 */
1255 match = xlat_logical_or(rctx, &rctx->list);
1256 if (match) goto done;
1257
1258 fr_value_box_list_talloc_free(&rctx->list);
1259
1260 rctx->current++;
1261
1262 /*
1263 * Nothing to expand, return the final value we saw.
1264 */
1265 if (rctx->current >= inst->argc) {
1266 /*
1267 * Otherwise we stop on failure, with the boolean
1268 * we just updated.
1269 */
1270 done:
1271 if (rctx->box) fr_dcursor_append(out, rctx->box);
1272
1273 talloc_free(rctx);
1274 return XLAT_ACTION_DONE;
1275 }
1276
1277 return xlat_logical_process_arg(ctx, out, xctx, request, in);
1278}
1279
1280/** See if the input is truthy or not.
1281 *
1282 * @param[in] rctx our ctx
1283 * @param[in] in list of value-boxes to check
1284 * @return
1285 * - false on failure
1286 * - true for match, with dst updated to contain the relevant box.
1287 *
1288 * Empty lists are not truthy.
1289 */
1290static bool xlat_logical_and(xlat_logical_rctx_t *rctx, fr_value_box_list_t const *in)
1291{
1292 fr_value_box_t *found = NULL;
1293
1294 /*
1295 * Empty lists are !truthy.
1296 */
1297 if (!fr_value_box_list_num_elements(in)) return false;
1298
1299 /*
1300 * Loop over the input list. We CANNOT do groups.
1301 */
1303 fr_assert(fr_type_is_leaf(box->type));
1304
1305 /*
1306 * Remember the last box we found.
1307 *
1308 * If it's truthy, then we keep going either
1309 * until the end, or until we get a "false".
1310 */
1311 if (fr_value_box_is_truthy(box)) {
1312 found = box;
1313 continue;
1314 }
1315
1316 /*
1317 * Stop on the first "false"
1318 */
1319 return false;
1320 }
1321
1322 if (!found) return false;
1323
1324 if (!rctx->box) {
1325 MEM(rctx->box = fr_value_box_alloc_null(rctx->ctx));
1326 } else {
1327 fr_value_box_clear(rctx->box);
1328 }
1329 if (!fr_cond_assert(fr_value_box_copy(rctx->box, rctx->box, found) == 0)) return false;
1330
1331 return true;
1332}
1333
1334/*
1335 * We've evaluated an expression. Let's see if we need to continue with &&
1336 */
1338 xlat_ctx_t const *xctx,
1339 request_t *request, fr_value_box_list_t *in)
1340{
1342 xlat_logical_rctx_t *rctx = talloc_get_type_abort(xctx->rctx, xlat_logical_rctx_t);
1343 bool match;
1344
1345 /*
1346 * If the expansions fails, then we fail the entire thing.
1347 */
1348 if (!XLAT_RESULT_SUCCESS(&rctx->last_result)) {
1349 talloc_free(rctx->box);
1350 talloc_free(rctx);
1351 return XLAT_ACTION_FAIL;
1352 }
1353
1354 /*
1355 * Recursively check groups. i.e. we effectively flatten each list.
1356 *
1357 * (a, b, c) && (d, e, f) == a && b && c && d && e && f
1358 */
1359 match = xlat_logical_and(rctx, &rctx->list);
1360 if (!match) {
1361 TALLOC_FREE(rctx->box); /* parented from ctx */
1362 goto done;
1363 }
1364
1365 fr_value_box_list_talloc_free(&rctx->list);
1366
1367 rctx->current++;
1368
1369 /*
1370 * Nothing to expand, return the final value we saw.
1371 */
1372 if (rctx->current >= inst->argc) {
1373 /*
1374 * Otherwise we stop on failure, with the boolean
1375 * we just updated.
1376 */
1377 fr_assert(rctx->box != NULL);
1378 fr_dcursor_append(out, rctx->box);
1379
1380 done:
1381 talloc_free(rctx);
1382 return XLAT_ACTION_DONE;
1383 }
1384
1385 return xlat_logical_process_arg(ctx, out, xctx, request, in);
1386}
1387
1388/*
1389 * Each argument is it's own head, because we do NOT always want
1390 * to go to the next argument.
1391 */
1393{
1394 xlat_logical_inst_t *inst = talloc_get_type_abort(xctx->inst, xlat_logical_inst_t);
1395
1396 inst->argc = xlat_flatten_to_argv(inst, &inst->argv, xctx->ex->call.args);
1397 if (xctx->ex->call.func->token == T_LOR) {
1398 inst->callback = xlat_logical_or_resume;
1399 inst->stop_on_match = true;
1400 } else {
1401 inst->callback = xlat_logical_and_resume;
1402 inst->stop_on_match = false;
1403 }
1404
1405 return 0;
1406}
1407
1408
1409/** Process logical &&, ||
1410 *
1411 */
1413 xlat_ctx_t const *xctx,
1414 request_t *request, fr_value_box_list_t *in)
1415{
1416 xlat_logical_rctx_t *rctx;
1418
1420 rctx->ctx = ctx;
1421 rctx->current = 0;
1422
1423 if (inst->stop_on_match) {
1424 rctx->box = NULL;
1425 } else {
1427 rctx->box->vb_bool = true;
1428 }
1429 fr_value_box_list_init(&rctx->list);
1430
1431 (UNCONST(xlat_ctx_t *, xctx))->rctx = rctx; /* ensure it's there before a resume! */
1432
1433 return xlat_logical_process_arg(ctx, out, xctx, request, in);
1434}
1435
1436
1438 { .required = true, .single = true, .concat = true },
1440};
1441
1443 UNUSED xlat_ctx_t const *xctx,
1444 request_t *request, fr_value_box_list_t *in, fr_token_t op)
1445{
1446 int rcode;
1447 fr_value_box_t *dst, *group, *vb;
1448
1449 /*
1450 * We do some basic type checks here.
1451 */
1452 group = fr_value_box_list_head(in);
1453 vb = fr_value_box_list_head(&group->vb_group);
1454
1455 /*
1456 * -NULL is an error
1457 * ~NULL is an error
1458 * !NULL is handled by xlat_func_unary_not
1459 */
1460 if (!vb) {
1461 fr_strerror_printf("Input is empty");
1462 return XLAT_ACTION_FAIL;
1463 }
1464
1465 if (!fr_type_is_leaf(vb->type) || fr_type_is_variable_size(vb->type)) {
1466 REDEBUG("Cannot perform operation on data type %s", fr_type_to_str(vb->type));
1467 return XLAT_ACTION_FAIL;
1468 }
1469
1470 MEM(dst = fr_value_box_alloc_null(ctx));
1471
1472 /*
1473 * We rely on this function to do the remainder of the type checking.
1474 */
1475 rcode = fr_value_calc_unary_op(dst, dst, op, vb);
1476 if ((rcode < 0) || fr_type_is_null(dst->type)) {
1477 talloc_free(dst);
1478 return XLAT_ACTION_FAIL;
1479 }
1480
1481 fr_dcursor_append(out, dst);
1482 return XLAT_ACTION_DONE;
1483}
1484
1485
1487 UNUSED xlat_ctx_t const *xctx,
1488 UNUSED request_t *request, fr_value_box_list_t *in)
1489{
1490 fr_value_box_t *dst, *group, *vb;
1491
1492 group = fr_value_box_list_head(in);
1493 vb = fr_value_box_list_head(&group->vb_group);
1494
1495 /*
1496 * Don't call calc_unary_op(), because we want the enum names.
1497 */
1499
1500 /*
1501 * !NULL = true
1502 */
1503 if (!vb) {
1504 dst->vb_bool = true;
1505 } else {
1506 dst->vb_bool = !fr_value_box_is_truthy(vb);
1507 }
1508
1509 fr_dcursor_append(out, dst);
1510 return XLAT_ACTION_DONE;
1511}
1512
1514 xlat_ctx_t const *xctx,
1515 request_t *request, fr_value_box_list_t *in)
1516{
1517 return xlat_func_unary_op(ctx, out, xctx, request, in, T_SUB);
1518}
1519
1521 xlat_ctx_t const *xctx,
1522 request_t *request, fr_value_box_list_t *in)
1523{
1524 return xlat_func_unary_op(ctx, out, xctx, request, in, T_COMPLEMENT);
1525}
1526
1528 { .concat = true, .type = FR_TYPE_STRING },
1530};
1531
1532/** Holds the result of pre-parsing the rcode on startup
1533 */
1534typedef struct {
1535 rlm_rcode_t rcode; //!< The preparsed rcode.
1537
1538/** Convert static expr_rcode arguments into rcodes
1539 *
1540 * This saves doing the lookup at runtime, which given how frequently this xlat is used
1541 * could get quite expensive.
1542 */
1544{
1545 xlat_rcode_inst_t *inst = talloc_get_type_abort(xctx->inst, xlat_rcode_inst_t);
1546 xlat_exp_t *arg;
1547 xlat_exp_t *rcode_arg;
1548 fr_value_box_t *rcode;
1549
1550 /*
1551 * If it's literal data, then we can pre-resolve it to
1552 * a rcode now, and skip that at runtime.
1553 */
1554 arg = xlat_exp_head(xctx->ex->call.args);
1555 fr_assert(arg->type == XLAT_GROUP);
1556
1557 /*
1558 * We can only pre-parse if this if the value is
1559 * in a single box...
1560 */
1561 if (fr_dlist_num_elements(&arg->group->dlist) != 1) return 0;
1562 rcode_arg = xlat_exp_head(arg->group);
1563
1564 /*
1565 * We can only pre-parse is this is a static value.
1566 */
1567 if (rcode_arg->type != XLAT_BOX) return 0;
1568
1569 rcode = &rcode_arg->data;
1570
1571 switch (rcode->type) {
1572 case FR_TYPE_STRING:
1573 inst->rcode = fr_table_value_by_str(rcode_table, rcode->vb_strvalue, RLM_MODULE_NOT_SET);
1574 if (inst->rcode == RLM_MODULE_NOT_SET) {
1575 unknown:
1576 ERROR("Unknown rcode '%pV'", rcode);
1577 return -1;
1578 }
1579 break;
1580
1581 case FR_TYPE_INT8:
1582 case FR_TYPE_INT16:
1583 case FR_TYPE_INT32:
1584 case FR_TYPE_INT64:
1585 case FR_TYPE_UINT16:
1586 case FR_TYPE_UINT32:
1587 case FR_TYPE_UINT64:
1588 case FR_TYPE_SIZE:
1589 if (fr_value_box_cast_in_place(rcode_arg, rcode, FR_TYPE_UINT8, NULL) < 0) {
1590 invalid:
1591 ERROR("Invalid value for rcode '%pV'", rcode);
1592 return -1;
1593 }
1595
1596 case FR_TYPE_UINT8:
1597 if (rcode->vb_uint8 >= RLM_MODULE_NUMCODES) goto invalid;
1598 inst->rcode = rcode->vb_uint8;
1599 break;
1600
1601 default:
1602 goto unknown;
1603 }
1604
1605 /*
1606 * No point in creating useless boxes at runtime,
1607 * nuke the argument now.
1608 */
1609 (void) fr_dlist_remove(&xctx->ex->call.args->dlist, arg);
1610 talloc_free(arg);
1611
1612 return 0;
1613}
1614
1615static fr_slen_t xlat_expr_print_rcode(fr_sbuff_t *out, xlat_exp_t const *node, void *instance, UNUSED fr_sbuff_escape_rules_t const *e_rules)
1616{
1617 size_t at_in = fr_sbuff_used_total(out);
1618 xlat_rcode_inst_t *inst = instance;
1619
1620 FR_SBUFF_IN_STRCPY_LITERAL_RETURN(out, "%interpreter.rcode('");
1621 if (xlat_exp_head(node->call.args)) {
1622 ssize_t slen;
1623
1624 xlat_exp_foreach(node->call.args, child) {
1625 slen = xlat_print_node(out, node->call.args, child, NULL, 0);
1626 if (slen < 0) return slen;
1627 }
1628 } else {
1630 }
1632
1633 return fr_sbuff_used_total(out) - at_in;
1634}
1635
1636/** Match the passed rcode against request->rcode
1637 *
1638 * Example:
1639@verbatim
1640%interpreter.rcode('handled') == true
1641
1642# ...or how it's used normally used
1643if (handled) {
1644 ...
1645}
1646@endverbatim
1647 *
1648 * @ingroup xlat_functions
1649 */
1651 xlat_ctx_t const *xctx,
1652 request_t *request, fr_value_box_list_t *args)
1653{
1655 fr_value_box_t *arg_rcode;
1656 rlm_rcode_t rcode;
1657 fr_value_box_t *vb;
1658
1659 /*
1660 * If we have zero args, it's because the instantiation
1661 * function consumed them. Unless the user read the debug
1662 * output, and tried to see what the rcode is, in case we
1663 */
1664 if (fr_value_box_list_num_elements(args) == 0) {
1665 if (inst->rcode == RLM_MODULE_NOT_SET) {
1666 RDEBUG("Request rcode is '%s'",
1667 fr_table_str_by_value(rcode_table, request->rcode, "<INVALID>"));
1668 return XLAT_ACTION_DONE;
1669 }
1670
1671 rcode = inst->rcode;
1672 } else {
1673 XLAT_ARGS(args, &arg_rcode);
1674 rcode = fr_table_value_by_str(rcode_table, arg_rcode->vb_strvalue, RLM_MODULE_NOT_SET);
1675 if (rcode == RLM_MODULE_NOT_SET) {
1676 REDEBUG("Invalid rcode '%pV'", arg_rcode);
1677 return XLAT_ACTION_FAIL;
1678 }
1679 }
1680
1681 RDEBUG3("Request rcode is '%s'",
1682 fr_table_str_by_value(rcode_table, request->rcode, "<INVALID>"));
1683
1686 vb->vb_bool = (request->rcode == rcode);
1687
1688 return XLAT_ACTION_DONE;
1689}
1690
1691/** Takes no arguments
1692 */
1694 XLAT_ARG_PARSER_TERMINATOR, /* Coverity gets tripped up by only having a single entry here */
1696};
1697
1698/** Return the current rcode as a string
1699 *
1700 * Example:
1701@verbatim
1702"%rcode()" == "handled"
1703@endverbatim
1704 *
1705 * @ingroup xlat_functions
1706 */
1708 UNUSED xlat_ctx_t const *xctx,
1709 request_t *request, UNUSED fr_value_box_list_t *args)
1710{
1711 fr_value_box_t *vb;
1712
1713 /*
1714 * FIXME - This should really be an enum
1715 */
1716 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_STRING, NULL));
1717 if (fr_value_box_strdup(vb, vb, NULL, fr_table_str_by_value(rcode_table, request->rcode, "<INVALID>"), false) < 0) {
1718 talloc_free(vb);
1719 return XLAT_ACTION_FAIL;
1720 }
1722
1723 return XLAT_ACTION_DONE;
1724}
1725
1726typedef struct {
1727 tmpl_t const *vpt; //!< the attribute reference
1729
1734
1735/*
1736 * We just print the node as-is.
1737 */
1738static fr_slen_t xlat_expr_print_exists(fr_sbuff_t *out, xlat_exp_t const *node, void *instance, fr_sbuff_escape_rules_t const *e_rules)
1739{
1740 size_t at_in = fr_sbuff_used_total(out);
1741 xlat_exists_inst_t *inst = instance;
1742
1743 if (inst->vpt) {
1744 FR_SBUFF_IN_STRCPY_RETURN(out, inst->vpt->name);
1745 } else {
1746 xlat_print_node(out, node->call.args, xlat_exp_head(node->call.args), e_rules, 0);
1747 }
1748
1749 return fr_sbuff_used_total(out) - at_in;
1750}
1751
1752/*
1753 * Don't expand the argument if it's already an attribute reference.
1754 */
1756{
1757 xlat_exists_inst_t *inst = talloc_get_type_abort(xctx->inst, xlat_exists_inst_t);
1758 xlat_exp_t *arg, *node;
1759
1760 arg = xlat_exp_head(xctx->ex->call.args);
1761
1762 fr_assert(arg->type == XLAT_GROUP);
1763 node = xlat_exp_head(arg->group);
1764
1765 /*
1766 * @todo - add an escape callback to this xlat
1767 * registration, so that it can take untrusted inputs.
1768 */
1769 if ((node->type != XLAT_TMPL) || !tmpl_contains_attr(node->vpt)) {
1770 fr_strerror_const("The %exists() function can only be used internally");
1771 return -1;
1772 }
1773
1774 inst->vpt = talloc_steal(inst, node->vpt);
1775
1776 /*
1777 * Free the input arguments so that they don't get expanded.
1778 */
1779 while ((arg = fr_dlist_pop_head(&xctx->ex->call.args->dlist)) != NULL) {
1780 talloc_free(arg);
1781 }
1782
1783 return 0;
1784}
1785
1787 request_t *request, tmpl_t const *vpt, bool do_free)
1788{
1789 fr_pair_t *vp;
1790 fr_value_box_t *dst;
1791 fr_dcursor_t cursor;
1793
1795
1796 vp = tmpl_dcursor_init(NULL, NULL, &cc, &cursor, request, vpt);
1797 dst->vb_bool = (vp != NULL);
1798
1799 if (do_free) talloc_const_free(vpt);
1800 tmpl_dcursor_clear(&cc);
1801 fr_dcursor_append(out, dst);
1802 return XLAT_ACTION_DONE;
1803}
1804
1805/** See if a named attribute exists
1806 *
1807 * Example:
1808@verbatim
1809"%{exists:&Foo}" == true
1810@endverbatim
1811 *
1812 * @ingroup xlat_functions
1813 */
1815 xlat_ctx_t const *xctx,
1816 request_t *request, UNUSED fr_value_box_list_t *in)
1817{
1819
1820 /*
1821 * We return "true" if the attribute exists. Otherwise we return "false".
1822 */
1823 fr_assert(inst->vpt);
1824
1825 return xlat_attr_exists(ctx, out, request, inst->vpt, false);
1826}
1827
1828#undef XLAT_REGISTER_BINARY_OP
1829#define XLAT_REGISTER_BINARY_OP(_op, _name) \
1830do { \
1831 if (unlikely((xlat = xlat_func_register(NULL, "op_" STRINGIFY(_name), xlat_func_op_ ## _name, FR_TYPE_VOID)) == NULL)) return -1; \
1832 xlat_func_args_set(xlat, binary_op_xlat_args); \
1833 xlat_func_flags_set(xlat, XLAT_FUNC_FLAG_PURE | XLAT_FUNC_FLAG_INTERNAL | XLAT_FUNC_FLAG_PRIVATE); \
1834 xlat_func_print_set(xlat, xlat_expr_print_binary); \
1835 xlat->token = _op; \
1836} while (0)
1837
1838#undef XLAT_REGISTER_BINARY_CMP
1839#define XLAT_REGISTER_BINARY_CMP(_op, _name) \
1840do { \
1841 if (unlikely((xlat = xlat_func_register(NULL, "cmp_" STRINGIFY(_name), xlat_func_cmp_ ## _name, FR_TYPE_BOOL)) == NULL)) return -1; \
1842 xlat_func_args_set(xlat, binary_cmp_xlat_args); \
1843 xlat_func_flags_set(xlat, XLAT_FUNC_FLAG_PURE | XLAT_FUNC_FLAG_INTERNAL | XLAT_FUNC_FLAG_PRIVATE); \
1844 xlat_func_print_set(xlat, xlat_expr_print_binary); \
1845 xlat_func_resolve_set(xlat, xlat_expr_resolve_binary); \
1846 xlat->token = _op; \
1847} while (0)
1848
1849#undef XLAT_REGISTER_NARY_OP
1850#define XLAT_REGISTER_NARY_OP(_op, _name, _func_name) \
1851do { \
1852 if (unlikely((xlat = xlat_func_register(NULL, STRINGIFY(_name), xlat_func_ ## _func_name, FR_TYPE_VOID)) == NULL)) return -1; \
1853 xlat_func_instantiate_set(xlat, xlat_instantiate_ ## _func_name, xlat_ ## _func_name ## _inst_t, NULL, NULL); \
1854 xlat_func_flags_set(xlat, XLAT_FUNC_FLAG_PURE | XLAT_FUNC_FLAG_INTERNAL | XLAT_FUNC_FLAG_PRIVATE); \
1855 xlat_func_print_set(xlat, xlat_expr_print_nary); \
1856 xlat_purify_func_set(xlat, xlat_expr_logical_purify); \
1857 xlat->token = _op; \
1858} while (0)
1859
1860#undef XLAT_REGISTER_REGEX_OP
1861#define XLAT_REGISTER_REGEX_OP(_op, _name) \
1862do { \
1863 if (unlikely((xlat = xlat_func_register(NULL, STRINGIFY(_name), xlat_func_ ## _name, FR_TYPE_BOOL)) == NULL)) return -1; \
1864 xlat_func_args_set(xlat, regex_op_xlat_args); \
1865 xlat_func_flags_set(xlat, XLAT_FUNC_FLAG_PURE | XLAT_FUNC_FLAG_INTERNAL | XLAT_FUNC_FLAG_PRIVATE); \
1866 xlat_func_instantiate_set(xlat, xlat_instantiate_regex, xlat_regex_inst_t, NULL, NULL); \
1867 xlat_func_print_set(xlat, xlat_expr_print_regex); \
1868 xlat->token = _op; \
1869} while (0)
1870
1871#define XLAT_REGISTER_BOOL(_xlat, _func, _arg, _ret_type) \
1872do { \
1873 if (unlikely((xlat = xlat_func_register(NULL, _xlat, _func, _ret_type)) == NULL)) return -1; \
1874 xlat_func_args_set(xlat, _arg); \
1875 xlat_func_flags_set(xlat, XLAT_FUNC_FLAG_INTERNAL); \
1876} while (0)
1877
1878#define XLAT_REGISTER_UNARY(_op, _xlat, _func) \
1879do { \
1880 if (unlikely((xlat = xlat_func_register(NULL, _xlat, _func, FR_TYPE_VOID)) == NULL)) return -1; \
1881 xlat_func_args_set(xlat, unary_op_xlat_args); \
1882 xlat_func_flags_set(xlat, XLAT_FUNC_FLAG_PURE | XLAT_FUNC_FLAG_INTERNAL | XLAT_FUNC_FLAG_PRIVATE); \
1883 xlat_func_print_set(xlat, xlat_expr_print_unary); \
1884 xlat->token = _op; \
1885} while (0)
1886
1888{
1889 xlat_t *xlat;
1890
1901
1910
1913
1914 if (unlikely((xlat = xlat_func_register(NULL, "regex.search", xlat_func_regex_search, FR_TYPE_BOOL)) == NULL)) return -1;
1917
1918 /*
1919 * &&, ||
1920 *
1921 * @todo - remove tmpl_resolve() from tokenize_field(), and add xlat_resolve_logical_or() / xlat_resolve_logical_and()
1922 * functions which do partial resolution.
1923 */
1924 XLAT_REGISTER_NARY_OP(T_LAND, logical_and, logical);
1925 XLAT_REGISTER_NARY_OP(T_LOR, logical_or, logical);
1926
1930
1932 xlat->deprecated = true;
1935
1940
1941 if (unlikely((xlat = xlat_func_register(NULL, "rcode", xlat_func_rcode, FR_TYPE_STRING)) == NULL)) return -1;
1944
1945 /*
1946 * -EXPR
1947 * ~EXPR
1948 * !EXPR
1949 */
1953
1954 return 0;
1955}
1956
1957/*
1958 * Must use the same names as above.
1959 */
1961 [ T_ADD ] = L("op_add"),
1962 [ T_SUB ] = L("op_sub"),
1963 [ T_MUL ] = L("op_mul"),
1964 [ T_DIV ] = L("op_div"),
1965 [ T_MOD ] = L("op_mod"),
1966 [ T_AND ] = L("op_and"),
1967 [ T_OR ] = L("op_or"),
1968 [ T_XOR ] = L("op_xor"),
1969 [ T_RSHIFT ] = L("op_rshift"),
1970 [ T_LSHIFT ] = L("op_lshift"),
1971
1972 [ T_LAND ] = L("logical_and"),
1973 [ T_LOR ] = L("logical_or"),
1974
1975 [ T_OP_CMP_EQ ] = L("cmp_eq"),
1976 [ T_OP_NE ] = L("cmp_ne"),
1977 [ T_OP_LT ] = L("cmp_lt"),
1978 [ T_OP_LE ] = L("cmp_le"),
1979 [ T_OP_GT ] = L("cmp_gt"),
1980 [ T_OP_GE ] = L("cmp_ge"),
1981
1982 [ T_OP_CMP_EQ_TYPE ] = L("cmp_eq_type"),
1983 [ T_OP_CMP_NE_TYPE ] = L("cmp_ne_type"),
1984
1985 [ T_OP_REG_EQ ] = L("reg_eq"),
1986 [ T_OP_REG_NE ] = L("reg_ne"),
1987};
1988
1989/*
1990 * Which are logical operations
1991 */
1992static const bool logical_ops[T_TOKEN_LAST] = {
1993 [T_LAND] = true,
1994 [T_LOR] = true,
1995};
1996
1997/*
1998 * These operators can take multiple arguments.
1999 *
2000 * @todo - include T_ADD, T_SUB, T_MUL, T_AND, T_OR, T_XOR, here too.
2001 *
2002 * This array should contain a function pointer to the code which either appends the results, or does
2003 * peephole optimizations to merge the arguments together. This merging will reduce run-time effort.
2004 */
2005static const bool multivalue_ops[T_TOKEN_LAST] = {
2006 [T_LAND] = true,
2007 [T_LOR] = true,
2008};
2009
2010/*
2011 * Allow for BEDMAS ordering. Gross ordering is first number,
2012 * fine ordering is second number. Unused operators are assigned as zero.
2013 *
2014 * Larger numbers are higher precedence.
2015 */
2016#define P(_x, _y) (((_x) << 4) | (_y))
2017
2018static const int precedence[T_TOKEN_LAST] = {
2019 [T_INVALID] = 0,
2020
2021 /*
2022 * Assignment operators go here as P(1,n)
2023 *
2024 * += -= *= /= %= <<= >>= &= ^= |=
2025 *
2026 * We want the output of the assignment operators to be the result of the assignment. This means
2027 * that the assignments can really only be done for simple attributes, and not tmpls with filters
2028 * which select multiple attributes.
2029 *
2030 * Which (for now) means that we likely want to disallow assignments in expressions. That's
2031 * fine, as this isn't C, and we're not sure that it makes sense to do something like:
2032 *
2033 * if ((&foo += 5) > 60) ...
2034 *
2035 * Or maybe it does. Who knows?
2036 */
2037
2038 [T_LOR] = P(2,0),
2039 [T_LAND] = P(2,1),
2040
2041 [T_OR] = P(3,0),
2042 [T_XOR] = P(3,1),
2043 [T_AND] = P(3,2),
2044
2045 [T_OP_REG_EQ] = P(4,0),
2046 [T_OP_REG_NE] = P(4,0),
2047
2048 [T_OP_CMP_EQ] = P(4,1),
2049 [T_OP_NE] = P(4,1),
2050
2051 [T_OP_CMP_EQ_TYPE] = P(4,1),
2052 [T_OP_CMP_NE_TYPE] = P(4,1),
2053
2054 [T_OP_LT] = P(5,0),
2055 [T_OP_LE] = P(5,0),
2056 [T_OP_GT] = P(5,0),
2057 [T_OP_GE] = P(5,0),
2058
2059 [T_RSHIFT] = P(6,0),
2060 [T_LSHIFT] = P(6,0),
2061
2062 [T_SUB] = P(7,0),
2063 [T_ADD] = P(7,1),
2064
2065 [T_MOD] = P(8,0),
2066 [T_MUL] = P(8,1),
2067 [T_DIV] = P(8,2),
2068
2069 [T_LBRACE] = P(10,0),
2070};
2071
2072#define fr_sbuff_skip_whitespace(_x) \
2073 do { \
2074 while (isspace(fr_sbuff_uint8(_x, '\0'))) fr_sbuff_advance(_x, 1); \
2075 } while (0)
2076
2078 fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules,
2079 fr_token_t prev, fr_sbuff_parse_rules_t const *bracket_rules,
2080 fr_sbuff_parse_rules_t const *input_rules, bool cond) CC_HINT(nonnull(1,2,3,4,5));
2081
2083 fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules,
2084 fr_sbuff_parse_rules_t const *bracket_rules, char *out_c, bool cond) CC_HINT(nonnull(1,2,3,4,5));
2085
2087 fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules,
2088 fr_sbuff_parse_rules_t const *bracket_rules, char *out_c, bool cond) CC_HINT(nonnull(1,2,3,4,5));
2089
2091 { L("\""), T_DOUBLE_QUOTED_STRING }, /* Don't re-order, backslash throws off ordering */
2092 { L("'"), T_SINGLE_QUOTED_STRING },
2093 { L("/"), T_SOLIDUS_QUOTED_STRING },
2094 { L("`"), T_BACK_QUOTED_STRING }
2095};
2097
2098
2099/*
2100 * Look for prefix operators
2101 *
2102 * + = ignore
2103 * - = unary_minus(next)
2104 * ! = unary_not(next)
2105 * ~ = unary_xor(0, next)
2106 * (expr) = recurse, and parse expr
2107 *
2108 * as a special case, <type> is a cast. Which lets us know how
2109 * to parse the next thing we get. Otherwise, parse the thing as
2110 * int64_t.
2111 */
2113 fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules,
2114 fr_sbuff_parse_rules_t const *bracket_rules, char *out_c, bool cond)
2115{
2116 xlat_exp_t *node = NULL, *unary = NULL;
2117 xlat_t *func = NULL;
2118 fr_sbuff_t our_in = FR_SBUFF(in);
2119 char c = '\0';
2120 tmpl_rules_t our_t_rules;
2121
2123
2124 tmpl_rules_copy_depth(&our_t_rules, t_rules);
2125 if (tmpl_rules_depth_exceeded(&our_t_rules)) FR_SBUFF_ERROR_RETURN(&our_in);
2126 t_rules = &our_t_rules;
2127
2128 /*
2129 * Handle !-~ by adding a unary function to the xlat
2130 * node, with the first argument being the _next_ thing
2131 * we allocate.
2132 */
2133 if (fr_sbuff_next_if_char(&our_in, '!')) { /* unary not */
2134 func = xlat_func_find("unary_not", 9);
2135 fr_assert(func != NULL);
2136 c = '!';
2137 goto check_for_double;
2138
2139 }
2140 else if (fr_sbuff_next_if_char(&our_in, '-')) { /* unary minus */
2141 fr_sbuff_skip_whitespace(&our_in);
2142
2143 /*
2144 * -4 is a number, not minus(4).
2145 */
2146 if (fr_sbuff_is_digit(&our_in)) goto field;
2147
2148 func = xlat_func_find("unary_minus", 11);
2149 fr_assert(func != NULL);
2150 c = '-';
2151 goto check_for_double;
2152
2153 }
2154 else if (fr_sbuff_next_if_char(&our_in, '~')) { /* unary complement */
2155 func = xlat_func_find("unary_complement", 16);
2156 fr_assert(func != NULL);
2157 c = '~';
2158 goto check_for_double;
2159
2160 }
2161 else if (fr_sbuff_next_if_char(&our_in, '+')) { /* ignore unary + */
2162 c = '+';
2163
2164 check_for_double:
2165 fr_sbuff_skip_whitespace(&our_in);
2166 fr_sbuff_skip_whitespace(&our_in);
2167 if (fr_sbuff_is_char(&our_in, c)) {
2168 fr_strerror_const("Double operator is invalid");
2169 FR_SBUFF_ERROR_RETURN(&our_in);
2170 }
2171 }
2172
2173 /*
2174 * Maybe we have a unary not / etc. If so, make sure
2175 * that we return that, and not the child node
2176 */
2177 if (!func) {
2178 field:
2179 return tokenize_field(head, out, in, p_rules, t_rules, bracket_rules, out_c, cond);
2180 }
2181
2182 /*
2183 * Tokenize_field may reset this if the operation is wrapped inside of another expression.
2184 */
2185 *out_c = c;
2186
2187 MEM(unary = xlat_exp_alloc(head, XLAT_FUNC, fr_tokens[func->token], strlen(fr_tokens[func->token])));
2188 xlat_exp_set_func(unary, func, t_rules->attr.dict_def);
2189 MEM(unary->call.args = xlat_exp_head_alloc(unary));
2190 unary->call.args->is_argv = true;
2191
2192 if (tokenize_field(unary->call.args, &node, &our_in, p_rules, t_rules, bracket_rules, out_c, (c == '!')) <= 0) {
2193 talloc_free(unary);
2194 FR_SBUFF_ERROR_RETURN(&our_in);
2195 }
2196
2197 if (!node) {
2198 fr_strerror_const("Empty expressions are invalid");
2199 FR_SBUFF_ERROR_RETURN(&our_in);
2200 }
2201
2202 xlat_func_append_arg(unary, node, (c == '!'));
2203 unary->flags.can_purify = (unary->call.func->flags.pure && unary->call.args->flags.pure) | unary->call.args->flags.can_purify;
2204
2205 /*
2206 * Don't add it to head->flags, that will be done when it's actually inserted.
2207 */
2208
2209 XLAT_VERIFY(unary);
2210 *out = unary;
2211
2212 FR_SBUFF_SET_RETURN(in, &our_in);
2213}
2214
2215/** Allocate a specific cast node.
2216 *
2217 * With the first argument being a UINT8 of the data type.
2218 * See xlat_func_cast() for the implementation.
2219 *
2220 */
2221static xlat_exp_t *expr_cast_alloc(TALLOC_CTX *ctx, fr_type_t type, xlat_exp_t *child)
2222{
2223 xlat_exp_t *cast, *node;
2224 char const *str;
2225
2226 /*
2227 * Create a "cast" node. The first argument is a UINT8 value-box of the cast type. The RHS is
2228 * whatever "node" comes next.
2229 */
2230 MEM(cast = xlat_exp_alloc(ctx, XLAT_FUNC, "cast", 4));
2231 MEM(cast->call.func = xlat_func_find("cast", 4));
2232 // no need to set dict here
2233 fr_assert(cast->call.func != NULL);
2234 cast->flags = cast->call.func->flags;
2235
2236 /*
2237 * Create argv[0] UINT8, with "Cast-Base" as
2238 * the "da". This allows the printing routines
2239 * to print the name of the type, and not the
2240 * number.
2241 */
2242 str = fr_type_to_str(type);
2243 fr_assert(str != NULL);
2244
2245 MEM(node = xlat_exp_alloc(cast, XLAT_BOX, NULL, 0));
2246 xlat_exp_set_name(node, str, strlen(str));
2247
2248 fr_value_box_init(&node->data, FR_TYPE_UINT8, attr_cast_base, false);
2249 node->data.vb_uint8 = type;
2250
2251 xlat_func_append_arg(cast, node, false);
2252 (void) talloc_steal(cast, child);
2253 xlat_func_append_arg(cast, child, false);
2254
2255 XLAT_VERIFY(cast);
2256
2257 return cast;
2258}
2259
2261{
2262 fr_sbuff_t our_in = FR_SBUFF(in);
2263 ssize_t slen;
2264
2265 if (!fr_sbuff_next_if_char(&our_in, '(')) {
2266 no_cast:
2267 *cast = FR_TYPE_NULL;
2268 return 0;
2269 }
2270
2271 /*
2272 * Check for an actual data type.
2273 */
2275
2276 /*
2277 * It's not a known data type, so it's not a cast.
2278 */
2279 if (*cast == FR_TYPE_NULL) {
2280 goto no_cast;
2281 }
2282
2283 /*
2284 * We're not allowed to start expressions with data types:
2285 *
2286 * (ipaddr ...
2287 * (ipaddr+...
2288 * (ipaddr(...
2289 */
2290 if (!fr_sbuff_next_if_char(&our_in, ')')) {
2291 if (!fr_sbuff_is_in_charset(&our_in, sbuff_char_word)) {
2292 fr_strerror_printf("Unexpected text after data type '%s'", fr_type_to_str(*cast));
2293 FR_SBUFF_ERROR_RETURN(&our_in);
2294 }
2295
2296 goto no_cast;
2297 }
2298
2299 /*
2300 * We're not allowed to cast to a structural data type: (group)
2301 *
2302 * @todo - maybe cast to structural data type could mean "parse it as a string"? But then where
2303 * do the pairs go..
2304 */
2305 if (!fr_type_is_leaf(*cast)) {
2306 fr_strerror_printf("Invalid structural data type '%s' in cast", fr_type_to_str(*cast));
2307 FR_SBUFF_ERROR_RETURN(&our_in);
2308 }
2309
2310 fr_sbuff_adv_past_whitespace(&our_in, SIZE_MAX, NULL);
2311 FR_SBUFF_SET_RETURN(in, &our_in);
2312}
2313
2314/*
2315 * Tokenize the RHS of a regular expression.
2316 */
2318 tmpl_rules_t const *t_rules,
2319 fr_sbuff_parse_rules_t const *bracket_rules)
2320{
2321 ssize_t slen;
2322 xlat_exp_t *node = NULL;
2323 fr_sbuff_t our_in = FR_SBUFF(in);
2324 fr_sbuff_marker_t opand_m, flag;
2325 tmpl_t *vpt;
2327 tmpl_rules_t our_t_rules;
2328
2330
2331 tmpl_rules_copy_depth(&our_t_rules, t_rules);
2332 if (tmpl_rules_depth_exceeded(&our_t_rules)) FR_SBUFF_ERROR_RETURN(&our_in);
2333 t_rules = &our_t_rules;
2334
2335 fr_sbuff_skip_whitespace(&our_in);
2336
2337 /*
2338 * Record where the operand begins for better error offsets later
2339 */
2340 fr_sbuff_marker(&opand_m, &our_in);
2341
2342 /*
2343 * Regexes cannot have casts or sub-expressions.
2344 */
2345 if (!fr_sbuff_next_if_char(&our_in, '/')) {
2346 /*
2347 * Allow for m'...' ala Perl
2348 */
2349 if (!fr_sbuff_is_str(&our_in, "m'", 2)) {
2350 fr_strerror_const("Expected regular expression");
2351 goto error;
2352 }
2353
2354 fr_sbuff_advance(&our_in, 2);
2355 quote = T_SINGLE_QUOTED_STRING;
2356 }
2357
2358 /*
2359 * Allocate the xlat node now so the talloc hierarchy is correct
2360 */
2361 MEM(node = xlat_exp_alloc(head, XLAT_TMPL, NULL, 0));
2362
2363 /*
2364 * tmpl_afrom_substr does pretty much all the work of parsing the operand. Note that we pass '/'
2365 * as the quote, so that the tmpl gets parsed as a regex.
2366 */
2367 (void) tmpl_afrom_substr(node, &vpt, &our_in, T_SOLIDUS_QUOTED_STRING, value_parse_rules_quoted[quote], t_rules);
2368 if (!vpt) {
2369 error:
2370 talloc_free(node);
2371 FR_SBUFF_ERROR_RETURN(&our_in);
2372 }
2373
2374 /*
2375 * @todo - allow for the RHS to be an attribute, too?
2376 */
2377
2378 /*
2379 * It would be nice if tmpl_afrom_substr() did this :(
2380 */
2381 if (!fr_sbuff_next_if_char(&our_in, fr_token_quote[quote])) {
2382 fr_strerror_const("Unterminated regular expression");
2383 goto error;
2384 }
2385
2386 /*
2387 * Remember where the flags start
2388 */
2389 fr_sbuff_marker(&flag, &our_in);
2390 if (tmpl_regex_flags_substr(vpt, &our_in, bracket_rules->terminals) < 0) {
2391 talloc_free(node);
2392 FR_SBUFF_ERROR_RETURN(&our_in);
2393 }
2394
2395 fr_sbuff_skip_whitespace(&our_in);
2396
2397 /*
2398 * Try to compile regular expressions, but only if
2399 * they're not being dynamically expanded.
2400 */
2401 if (!tmpl_contains_xlat(vpt)) {
2402 slen = tmpl_regex_compile(vpt, true);
2403 if (slen <= 0) goto error;
2404 }
2405
2406 node->quote = quote;
2407 xlat_exp_set_vpt(node, vpt);
2408
2409 XLAT_VERIFY(node);
2410 *out = node;
2411
2412 FR_SBUFF_SET_RETURN(in, &our_in);
2413}
2414
2415
2417{
2418 rlm_rcode_t rcode;
2419 ssize_t slen;
2420 xlat_t *func;
2421 xlat_exp_t *node, *arg;
2422 fr_sbuff_t our_in = FR_SBUFF(in);
2423
2424 fr_sbuff_out_by_longest_prefix(&slen, &rcode, rcode_table, &our_in, T_BARE_WORD);
2425 if (slen <= 0) return 0;
2426
2427 if (!fr_sbuff_is_terminal(&our_in, terminals)) {
2428 if (!fr_dict_attr_allowed_chars[fr_sbuff_uint8(&our_in, '\0')]) {
2429 fr_strerror_const("Unexpected text after return code");
2430 FR_SBUFF_ERROR_RETURN(&our_in);
2431 }
2432 return 0;
2433 }
2434
2435 /*
2436 * We do NOT do math on return codes. But these two characters are allowed for attribute names.
2437 * So we don't parse "Invalid-Packet" as "Invalid - packet".
2438 */
2439 if (fr_sbuff_is_char(&our_in, '-') || fr_sbuff_is_char(&our_in, '/')) {
2440 return 0;
2441 }
2442
2443 /*
2444 * @todo - allow for attributes to have the name "ok-foo" ???
2445 */
2446 func = xlat_func_find("interpreter.rcode", -1);
2447 fr_assert(func != NULL);
2448
2449 MEM(node = xlat_exp_alloc(head, XLAT_FUNC, fr_sbuff_start(&our_in), slen));
2450 node->call.func = func;
2451 // no need to set dict here
2452 node->flags = func->flags; /* rcode is impure, but can be calculated statically */
2453
2454 MEM(arg = xlat_exp_alloc(node, XLAT_BOX, fr_sbuff_start(&our_in), slen));
2455
2456 /*
2457 * We need a string for unit tests, but this should really be just a number.
2458 */
2459 fr_value_box_init(&arg->data, FR_TYPE_STRING, NULL, false);
2460 (void) fr_value_box_bstrndup(arg, &arg->data, NULL, fr_sbuff_start(&our_in), slen, false);
2461
2462 xlat_func_append_arg(node, arg, false);
2463
2464 XLAT_VERIFY(node);
2465 *out = node;
2466
2467 FR_SBUFF_SET_RETURN(in, &our_in);
2468}
2469
2470
2471/*
2472 * Tokenize a field without unary operators.
2473 */
2475 fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules,
2476 fr_sbuff_parse_rules_t const *bracket_rules, char *out_c, bool cond)
2477{
2478 fr_slen_t slen;
2479 xlat_exp_t *node = NULL;
2480 fr_sbuff_t our_in = FR_SBUFF(in);
2481 fr_sbuff_marker_t opand_m;
2482 tmpl_rules_t our_t_rules;
2483 tmpl_t *vpt = NULL;
2484 fr_token_t quote;
2485 fr_type_t cast_type;
2486 fr_dict_attr_t const *enumv;
2487
2489
2490 /*
2491 * Allow for explicit casts. Non-leaf types are forbidden.
2492 */
2493 if (expr_cast_from_substr(&cast_type, &our_in) < 0) FR_SBUFF_ERROR_RETURN(&our_in);
2494
2495 /*
2496 * Do NOT pass the cast down to the next set of parsing routines. Instead, let the next data be
2497 * parsed as whatever, and then add a cast, or cast in place as necessary.
2498 */
2499 tmpl_rules_copy_depth(&our_t_rules, t_rules);
2500 if (tmpl_rules_depth_exceeded(&our_t_rules)) FR_SBUFF_ERROR_RETURN(&our_in);
2501
2502 if (cast_type == FR_TYPE_NULL) {
2503 cast_type = our_t_rules.cast;
2504 enumv = our_t_rules.enumv;
2505 } else {
2506 enumv = NULL;
2507 }
2508
2509 our_t_rules.cast = FR_TYPE_NULL;
2510// our_t_rules.enumv = NULL;
2511
2512 /*
2513 * As a special case, we allow
2514 *
2515 * &reply = "foo = bar"
2516 *
2517 * and then we don't parse the RHS as any enum.
2518 */
2519 if ( our_t_rules.enumv && !fr_type_is_leaf(our_t_rules.enumv->type)) {
2520 our_t_rules.enumv = enumv = NULL;
2521 }
2522
2523 /*
2524 * If we still have '(', then recurse for other expressions
2525 *
2526 * Tokenize the sub-expression, ensuring that we stop at ')'.
2527 *
2528 * Note that if we have a sub-expression, then we don't use the hinting for "type".
2529 * That's because we're parsing a complete expression here (EXPR). So the intermediate
2530 * nodes in the expression can be almost anything. And we only cast it to the final
2531 * value when we get the output of the expression.
2532 */
2533 if (fr_sbuff_next_if_char(&our_in, '(')) {
2534 our_t_rules.cast = FR_TYPE_NULL;
2535 our_t_rules.enumv = NULL;
2536
2537 fr_sbuff_skip_whitespace(&our_in);
2538 if (fr_sbuff_is_char(&our_in, ')')) {
2539 fr_strerror_printf("Empty expressions are invalid");
2540 FR_SBUFF_ERROR_RETURN(&our_in);
2541 }
2542
2543 /*
2544 * No input rules means "ignore external terminal sequences, as we're expecting a ')' as
2545 * our terminal sequence.
2546 */
2547 if (tokenize_expression(head, &node, &our_in, bracket_rules, &our_t_rules, T_INVALID, bracket_rules, NULL, cond) <= 0) {
2548 FR_SBUFF_ERROR_RETURN(&our_in);
2549 }
2550
2551 if (!fr_sbuff_next_if_char(&our_in, ')')) {
2552 fr_strerror_printf("Failed to find trailing ')'");
2553 FR_SBUFF_ERROR_RETURN(&our_in);
2554 }
2555
2556 /*
2557 * We've parsed one "thing", so we stop. The next thing should be an operator, not
2558 * another value.
2559 *
2560 * The nested call to tokenize_expression() can return >=0 if there are spaces followed by a
2561 * terminal character. So "node" may be NULL;
2562 */
2563 if (!node) {
2564 fr_strerror_const("Empty expressions are invalid");
2565 FR_SBUFF_ERROR_RETURN(&our_in);
2566 }
2567
2568 *out_c = '\0';
2569 goto done;
2570 }
2571
2572 /*
2573 * Record where the operand begins for better error offsets later
2574 */
2575 fr_sbuff_skip_whitespace(&our_in);
2576 fr_sbuff_marker(&opand_m, &our_in);
2577
2579
2580 switch (quote) {
2581 case T_BARE_WORD:
2582 p_rules = bracket_rules;
2583
2584 /*
2585 * Peek for rcodes.
2586 */
2587 if (cond) {
2588 slen = tokenize_rcode(head, &node, &our_in, p_rules->terminals);
2589 if (slen < 0) FR_SBUFF_ERROR_RETURN(&our_in);
2590
2591 if (slen > 0) {
2592 fr_assert(node != NULL);
2593 goto done;
2594 }
2595 }
2597
2598 default:
2599 slen = xlat_tokenize_word(head, &node, &our_in, quote, p_rules, &our_t_rules);
2600 if (slen <= 0) FR_SBUFF_ERROR_RETURN(&our_in);
2601
2602 fr_assert(node != NULL);
2603 break;
2604 }
2605
2606 /*
2607 * Cast value-box.
2608 */
2609 if (node->type == XLAT_BOX) {
2610 if (cast_type != FR_TYPE_NULL) {
2611 if (node->data.type != cast_type) {
2612 if (fr_value_box_cast_in_place(node, &node->data, cast_type, NULL) < 0) goto error;
2613 }
2614
2615 cast_type = FR_TYPE_NULL;
2616 }
2617 }
2618
2619 /*
2620 * Something other than a tmpl, we can just return.
2621 */
2622 if (node->type != XLAT_TMPL) {
2623 xlat_exp_set_name(node, fr_sbuff_current(&opand_m), fr_sbuff_behind(&opand_m));
2624 goto done;
2625 }
2626
2627 vpt = node->vpt;
2628
2629 /*
2630 * The tmpl has a cast, and it's the same as the explicit cast we were given, we can sometimes
2631 * discard the explicit cast.
2632 */
2633 if (cast_type != FR_TYPE_NULL) {
2634 if (tmpl_rules_cast(vpt) == cast_type) {
2635 fr_assert(0);
2636 cast_type = FR_TYPE_NULL;
2637
2638 } else if (tmpl_is_attr(vpt)) {
2639 fr_dict_attr_t const *da;
2640
2642
2643 da = tmpl_attr_tail_da(vpt); /* could be a list! */
2644
2645 /*
2646 * Set the cast for attributes. Note that tmpl_cast_set() will take care of
2647 * suppressing redundant casts. But it still allows (uint32)&Service-Type,
2648 * which means "return the raw value", and not "return enum name".
2649 */
2650 if (da) {
2651 if (tmpl_cast_set(vpt, cast_type) < 0) {
2652 error:
2653 fr_sbuff_set(&our_in, &opand_m);
2654 talloc_free(node);
2655 FR_SBUFF_ERROR_RETURN(&our_in);
2656 }
2657
2658 cast_type = FR_TYPE_NULL;
2659
2660 } else { /* it's something like &reply. */
2661 fr_assert(0);
2662 }
2663
2664 } else if (tmpl_is_data(vpt)) {
2666
2667 /*
2668 * Omit our cast type if the data is already of the right type.
2669 *
2670 * Otherwise if we have a cast, then convert the data now, and then reset the
2671 * cast_type to nothing. This work allows for better errors at startup, and
2672 * minimizes run-time work.
2673 */
2674 if (tmpl_value_type(vpt) == cast_type) {
2675 cast_type = FR_TYPE_NULL;
2676
2677 } else if (tmpl_cast_in_place(vpt, cast_type, enumv) < 0) {
2678 fr_sbuff_set(&our_in, &opand_m);
2679 goto error;
2680
2681 } else {
2682 /*
2683 * We've parsed the data as the new data type, so we don't need any more
2684 * casting.
2685 */
2686 cast_type = FR_TYPE_NULL;
2687 }
2688
2689 } else if (tmpl_contains_xlat(vpt)) {
2690 /*
2691 * (string) "foo %{...}" is redundant. Drop the cast.
2692 */
2693 if ((cast_type == FR_TYPE_STRING) && (vpt->quote != T_BARE_WORD)) {
2695 cast_type = FR_TYPE_NULL;
2696
2697 } else {
2698 /*
2699 * Push the cast to the tmpl.
2700 */
2701 tmpl_cast_set(vpt, cast_type);
2702 cast_type = FR_TYPE_NULL;
2703 }
2704
2705 } else if (tmpl_is_attr_unresolved(vpt)) {
2707
2708 } else if (tmpl_is_data_unresolved(vpt)) {
2709 fr_assert(0);
2710
2711 fr_assert(quote == T_BARE_WORD);
2712 fr_strerror_const("Failed parsing input");
2713 fr_sbuff_set(&our_in, &opand_m);
2714 goto error;
2715
2716 } else {
2717 /*
2718 * Regex? Or something else weird?
2719 */
2720 tmpl_debug(stderr, vpt);
2721 fr_assert(0);
2722 }
2723 }
2724
2726
2727done:
2728 /*
2729 * If there is a cast, then reparent the node with a cast wrapper.
2730 */
2731 if (cast_type != FR_TYPE_NULL) {
2732 xlat_exp_t *cast;
2733
2734 MEM(cast = expr_cast_alloc(head, cast_type, node));
2735 node = cast;
2736 }
2737
2738 XLAT_VERIFY(node);
2739 *out = node;
2740
2741 fr_sbuff_skip_whitespace(&our_in);
2742 FR_SBUFF_SET_RETURN(in, &our_in);
2743}
2744
2745/*
2746 * A mapping of operators to tokens.
2747 */
2749 { L("!="), T_OP_NE },
2750 { L("!=="), T_OP_CMP_NE_TYPE },
2751
2752 { L("&"), T_AND },
2753 { L("&&"), T_LAND },
2754 { L("*"), T_MUL },
2755 { L("+"), T_ADD },
2756 { L("-"), T_SUB },
2757 { L("/"), T_DIV },
2758 { L("%"), T_MOD },
2759 { L("^"), T_XOR },
2760
2761 { L("|"), T_OR },
2762 { L("||"), T_LOR },
2763
2764 { L("<"), T_OP_LT },
2765 { L("<<"), T_LSHIFT },
2766 { L("<="), T_OP_LE },
2767
2768 { L("="), T_OP_EQ },
2769 { L("=="), T_OP_CMP_EQ },
2770 { L("==="), T_OP_CMP_EQ_TYPE },
2771
2772 { L("=~"), T_OP_REG_EQ },
2773 { L("!~"), T_OP_REG_NE },
2774
2775 { L(">"), T_OP_GT },
2776 { L(">="), T_OP_GE },
2777 { L(">>"), T_RSHIFT },
2778
2779};
2781
2782static bool valid_type(xlat_exp_t *node)
2783{
2784 fr_dict_attr_t const *da;
2785
2786#ifdef STATIC_ANALYZER
2787 if (!node) return false;
2788#endif
2789
2790 if (node->type != XLAT_TMPL) return true;
2791
2792 if (tmpl_is_list(node->vpt)) {
2793 list:
2794 fr_strerror_const("Cannot use list references in condition");
2795 return false;
2796 }
2797
2798 if (!tmpl_is_attr(node->vpt)) return true;
2799
2800 da = tmpl_attr_tail_da(node->vpt);
2801 if (fr_type_is_structural(da->type)) {
2802 if (da->dict == fr_dict_internal()) goto list;
2803
2804 fr_strerror_const("Cannot use structural types in condition");
2805 return false;
2806 }
2807
2808 return true;
2809}
2810
2811
2812/** Tokenize a mathematical operation.
2813 *
2814 * (EXPR)
2815 * !EXPR
2816 * A OP B
2817 *
2818 * If "out" is NULL then the expression is added to "head".
2819 * Otherwise, it's returned to the caller.
2820 */
2822 fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules,
2823 fr_token_t prev, fr_sbuff_parse_rules_t const *bracket_rules,
2824 fr_sbuff_parse_rules_t const *input_rules, bool cond)
2825{
2826 xlat_exp_t *lhs = NULL, *rhs, *node;
2827 xlat_t *func = NULL;
2828 fr_token_t op;
2829 fr_slen_t slen;
2830 fr_sbuff_marker_t m_lhs, m_op, m_rhs;
2831 fr_sbuff_t our_in = FR_SBUFF(in);
2832 char c = '\0';
2833 tmpl_rules_t our_t_rules;
2834
2836
2837 tmpl_rules_copy_depth(&our_t_rules, t_rules);
2838 if (tmpl_rules_depth_exceeded(&our_t_rules)) FR_SBUFF_ERROR_RETURN(&our_in);
2839 t_rules = &our_t_rules;
2840
2841 fr_sbuff_skip_whitespace(&our_in);
2842
2843 fr_sbuff_marker(&m_lhs, &our_in);
2844
2845 /*
2846 * Get the LHS of the operation.
2847 */
2848 slen = tokenize_unary(head, &lhs, &our_in, p_rules, t_rules, bracket_rules, &c, cond);
2849 if (slen <= 0) FR_SBUFF_ERROR_RETURN(&our_in);
2850
2851 if (slen == 0) {
2852 fr_assert(lhs == NULL);
2853 *out = NULL;
2854 FR_SBUFF_SET_RETURN(in, &our_in);
2855 }
2856
2857redo:
2858 rhs = NULL;
2859
2860 fr_sbuff_skip_whitespace(&our_in);
2861
2862 /*
2863 * No more input, we're done.
2864 */
2865 if (fr_sbuff_extend(&our_in) == 0) {
2866 done:
2867 /*
2868 * LHS may be NULL if the expression has spaces followed by a terminal character.
2869 */
2870 if (lhs) XLAT_VERIFY(lhs);
2871 *out = lhs;
2872 FR_SBUFF_SET_RETURN(in, &our_in);
2873 }
2874
2875 /*
2876 * ')' is a terminal, even if we didn't expect it.
2877 * Because if we didn't expect it, then it's an error.
2878 *
2879 * If we did expect it, then we return whatever we found,
2880 * and let the caller eat the ')'.
2881 */
2882 if (fr_sbuff_is_char(&our_in, ')')) {
2883 if (!bracket_rules) {
2884 fr_strerror_printf("Unexpected ')'");
2885 FR_SBUFF_ERROR_RETURN(&our_in);
2886 }
2887
2888 goto done;
2889 }
2890 fr_sbuff_skip_whitespace(&our_in);
2891
2892 /*
2893 * We hit a terminal sequence, stop.
2894 */
2895 if (input_rules && fr_sbuff_is_terminal(&our_in, input_rules->terminals)) goto done;
2896
2897 /*
2898 * Remember where we were after parsing the LHS.
2899 */
2900 fr_sbuff_marker(&m_op, &our_in);
2901
2902 /*
2903 * Get the operator.
2904 */
2905 XLAT_DEBUG(" operator <-- %pV", fr_box_strvalue_len(fr_sbuff_current(&our_in), fr_sbuff_remaining(&our_in)));
2907 if ((op == T_INVALID) || !binary_ops[op].str) {
2908 fr_strerror_const("Invalid operator");
2909 fr_sbuff_set(&our_in, &m_op);
2910 talloc_free(lhs);
2911 FR_SBUFF_ERROR_RETURN(&our_in);
2912 }
2913
2914 /*
2915 * We can't (yet) do &list1 = &list2 + &list3
2916 */
2917 if (fr_binary_op[op] && t_rules->enumv && fr_type_is_structural(t_rules->enumv->type)) {
2918 fr_strerror_const("Invalid operator for structural attribute");
2919 fr_sbuff_set(&our_in, &m_op);
2920 talloc_free(lhs);
2921 FR_SBUFF_ERROR_RETURN(&our_in);
2922 }
2923
2924 fr_assert(precedence[op] != 0);
2925
2926 /*
2927 * a * b + c ... = (a * b) + c ...
2928 *
2929 * Feed the current expression to the caller, who will
2930 * take care of continuing.
2931 */
2932 if (precedence[op] <= precedence[prev]) {
2933 fr_sbuff_set(&our_in, &m_op);
2934 goto done;
2935 }
2936
2937 /*
2938 * &Foo and !&Foo are permitted as the LHS of || and &&
2939 */
2940 if (((c == '!') || (c == '~')) && (op != T_LAND) && (op != T_LOR)) {
2941 fr_strerror_printf("Operator '%c' is only applied to the left hand side of the '%s' operation, add (..) to evaluate the operation first", c, fr_tokens[op]);
2942 fail_lhs:
2943 fr_sbuff_set(&our_in, &m_lhs);
2944 FR_SBUFF_ERROR_RETURN(&our_in);
2945 }
2946
2947 fr_sbuff_skip_whitespace(&our_in);
2948 fr_sbuff_marker(&m_rhs, &our_in);
2949
2950 /*
2951 * We now parse the RHS, allowing a (perhaps different) cast on the RHS.
2952 */
2953 XLAT_DEBUG(" recurse RHS <-- %pV", fr_box_strvalue_len(fr_sbuff_current(&our_in), fr_sbuff_remaining(&our_in)));
2954 if ((op == T_OP_REG_EQ) || (op == T_OP_REG_NE)) {
2956
2957 /*
2958 * @todo - LHS shouldn't be anything else.
2959 */
2960 switch (lhs->type) {
2961 case XLAT_TMPL:
2962 type = tmpl_cast_get(lhs->vpt);
2963 if ((type != FR_TYPE_NULL) && (type != FR_TYPE_STRING)) {
2964 fr_strerror_const("Casts cannot be used with regular expressions");
2965 fr_sbuff_set(&our_in, &m_lhs);
2966 FR_SBUFF_ERROR_RETURN(&our_in);
2967 }
2968
2969 /*
2970 * Cast the LHS to a string, if it's not already one!
2971 */
2972 if (lhs->vpt->quote == T_BARE_WORD) tmpl_cast_set(lhs->vpt, FR_TYPE_STRING);
2973 break;
2974
2975 case XLAT_BOX:
2976 /*
2977 * 192.168.0.1 =~ /foo/
2978 *
2979 * Gets the LHS automatically converted to a string.
2980 */
2981 if (lhs->data.type != FR_TYPE_STRING) {
2982 if (fr_value_box_cast_in_place(lhs, &lhs->data, FR_TYPE_STRING, NULL) < 0) {
2983 fr_sbuff_set(&our_in, &m_lhs);
2984 FR_SBUFF_ERROR_RETURN(&our_in);
2985 }
2986 }
2987 break;
2988
2989 case XLAT_GROUP:
2990 /*
2991 * A hoisted xlat has no tmpl to hang a cast on, so wrap it in an explicit cast
2992 * to a string. Without this, %hash.md5(...) =~ /foo/ would match the regex
2993 * against octets rather than against a string.
2994 */
2995 MEM(lhs = expr_cast_alloc(head, FR_TYPE_STRING, lhs));
2996 break;
2997
2998 default:
3000 if ((type != FR_TYPE_NULL) && (type != FR_TYPE_STRING)) {
3001 fr_strerror_const("Function (or cast) needs to return data type 'string' in order to be used with regular expressions");
3002 fr_sbuff_set(&our_in, &m_lhs);
3003 FR_SBUFF_ERROR_RETURN(&our_in);
3004 }
3005 break;
3006 }
3007
3008 slen = tokenize_regex_rhs(head, &rhs, &our_in, t_rules, bracket_rules);
3009 } else {
3010 tmpl_rules_t rhs_t_rules = *t_rules;
3011
3012 /*
3013 * Pass the enumv down ONLY if the RHS name begins with "::".
3014 *
3015 * Otherwise, the terminal rules for expressions includes "-" and "+", both of which are
3016 * allowed in enum names. If we pass the enumv down to the next function, it will see
3017 * "Access-Accept", and then only parse "Access". Which is wrong.
3018 */
3019 if ((lhs->type == XLAT_TMPL) && tmpl_is_attr(lhs->vpt) &&
3020 fr_sbuff_is_str_literal(&our_in, "::")) {
3021 rhs_t_rules.enumv = tmpl_attr_tail_da(lhs->vpt);
3022 }
3023
3024 slen = tokenize_expression(head, &rhs, &our_in, p_rules, &rhs_t_rules, op, bracket_rules, input_rules, cond);
3025 }
3026 if (slen <= 0) {
3027 talloc_free(lhs);
3028 FR_SBUFF_ERROR_RETURN(&our_in);
3029 }
3030
3031 /*
3032 * The nested call to tokenize_expression() can return >=0 if there are spaces followed by a
3033 * terminal character.
3034 */
3035 if (!rhs) goto done;
3036
3037 XLAT_VERIFY(rhs);
3038
3039 func = xlat_func_find(binary_ops[op].str, binary_ops[op].len);
3040 fr_assert(func != NULL);
3041
3042 if (multivalue_ops[op]) {
3043 if ((lhs->type == XLAT_FUNC) && (lhs->call.func->token == op)) {
3044 xlat_func_append_arg(lhs, rhs, cond);
3045
3046 lhs->call.args->flags.can_purify |= rhs->flags.can_purify | rhs->flags.pure;
3047 lhs->flags.can_purify = lhs->call.args->flags.can_purify;
3048 goto redo;
3049 }
3050 goto purify;
3051 }
3052
3053 /*
3054 * Complain on comparisons between invalid data types.
3055 *
3056 * @todo - allow
3057 *
3058 * &structural == {}
3059 * &structural != {}
3060 *
3061 * as special cases, so we can check lists for emptiness.
3062 */
3063 if (fr_comparison_op[op]) {
3064 if (!valid_type(lhs)) goto fail_lhs;
3065 if (!valid_type(rhs)) {
3066 fr_sbuff_set(&our_in, &m_rhs);
3067 FR_SBUFF_ERROR_RETURN(&our_in);
3068 }
3069
3070 /*
3071 * Peephole optimization. If both LHS
3072 * and RHS are static values, then just call the
3073 * relevant condition code to get the result.
3074 */
3075 if (cond) {
3076 int rcode;
3077
3078 purify:
3079 rcode = xlat_purify_op(head, &node, lhs, op, rhs);
3080 if (rcode < 0) goto fail_lhs;
3081
3082 if (rcode) {
3083 lhs = node;
3084 goto redo;
3085 }
3086 }
3087 }
3088
3089 /*
3090 * Create the function node, with the LHS / RHS arguments.
3091 */
3092 MEM(node = xlat_exp_alloc(head, XLAT_FUNC, fr_tokens[op], strlen(fr_tokens[op])));
3093 xlat_exp_set_func(node, func, t_rules->attr.dict_def);
3094
3095 xlat_func_append_arg(node, lhs, logical_ops[op] && cond);
3096 xlat_func_append_arg(node, rhs, logical_ops[op] && cond);
3097
3098 fr_assert(xlat_exp_head(node->call.args) != NULL);
3099
3100 /*
3101 * Logical operations can be purified if ANY of their arguments can be purified.
3102 */
3103 if (logical_ops[op]) {
3104 xlat_exp_foreach(node->call.args, arg) {
3105 node->call.args->flags.can_purify |= arg->flags.can_purify | arg->flags.pure;
3106 if (node->call.args->flags.can_purify) break;
3107 }
3108 node->flags.can_purify = node->call.args->flags.can_purify;
3109
3110 } else {
3111 node->flags.can_purify = (node->call.func->flags.pure && node->call.args->flags.pure) | node->call.args->flags.can_purify;
3112 }
3113
3114 lhs = node;
3115 XLAT_VERIFY(lhs);
3116 goto redo;
3117}
3118
3120 L(""),
3121 L(")"),
3122);
3123
3125 L("\t"),
3126 L("\n"),
3127 L("\r"),
3128 L(" "),
3129 L("!"),
3130 L("%"),
3131 L("&"),
3132 L("*"),
3133 L("+"),
3134 L("-"),
3135 L("/"),
3136 L("<"),
3137 L("="),
3138 L(">"),
3139 L("^"),
3140 L("|"),
3141 L("~"),
3142);
3143
3145 fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules, bool cond)
3146{
3147 fr_slen_t slen;
3148 fr_sbuff_parse_rules_t *bracket_rules = NULL;
3149 fr_sbuff_parse_rules_t *terminal_rules = NULL;
3150 tmpl_rules_t my_rules = { };
3152 xlat_exp_t *node = NULL;
3153
3154 /*
3155 * Whatever the caller passes, ensure that we have a
3156 * terminal rule which ends on operators, and a terminal
3157 * rule which ends on ')'.
3158 */
3159 MEM(bracket_rules = talloc_zero(ctx, fr_sbuff_parse_rules_t));
3160 MEM(terminal_rules = talloc_zero(ctx, fr_sbuff_parse_rules_t));
3161 if (p_rules) {
3162 *bracket_rules = *p_rules;
3163 *terminal_rules = *p_rules;
3164
3165 if (p_rules->terminals) {
3166 MEM(terminal_rules->terminals = fr_sbuff_terminals_amerge(terminal_rules,
3167 p_rules->terminals,
3168 &operator_terms));
3169 } else {
3170 terminal_rules->terminals = &operator_terms;
3171 }
3172 } else {
3173 terminal_rules->terminals = &operator_terms;
3174 }
3175 MEM(bracket_rules->terminals = fr_sbuff_terminals_amerge(bracket_rules,
3176 terminal_rules->terminals,
3177 &bracket_terms));
3178
3180 if (!t_rules) t_rules = &my_rules;
3181
3182 slen = tokenize_expression(head, &node, in, terminal_rules, t_rules, T_INVALID, bracket_rules, p_rules, cond);
3183 talloc_free(bracket_rules);
3184 talloc_free(terminal_rules);
3185
3186 if (slen <= 0) {
3188 return slen;
3189 }
3190
3191 if (!node) {
3193 *out = head;
3194
3195 return slen;
3196 }
3197
3198 /*
3199 * If the tmpl is not resolved, then it refers to an attribute which doesn't exist. That's an
3200 * error.
3201 */
3202 if (node->type == XLAT_TMPL) {
3203 if (tmpl_is_data_unresolved(node->vpt)) {
3204 fr_strerror_const("Unknown attribute");
3205 return -1;
3206 }
3207
3208 /*
3209 * Convert raw existence checks to existence functions.
3210 */
3211 if (tmpl_contains_attr(node->vpt)) {
3212 if (cond) MEM(node = xlat_exists_alloc(head, node));
3213 }
3214 }
3215
3217
3219 *out = head;
3220
3221 return slen;
3222}
3223
3225 fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules)
3226{
3227 fr_slen_t slen;
3228
3229 slen = xlat_tokenize_expression_internal(ctx, out, in, p_rules, t_rules, false);
3230 if (slen < 0) return slen;
3231
3232#ifdef STATIC_ANALYZER
3233 /*
3234 * Coverity doesn't realise that out will be set by this point
3235 * by a successful call to xlat_tokenize_expression_internal.
3236 */
3237 if (!out) return -1;
3238#endif
3239 if (!*out) {
3240 fr_strerror_const("Empty expressions are invalid");
3241 return -1;
3242 }
3243
3244 if (xlat_finalize(*out, t_rules->xlat.runtime_el) < 0) {
3245 TALLOC_FREE(*out);
3246 return -1;
3247 }
3248
3249 return slen;
3250}
3251
3253 fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules)
3254{
3255 fr_slen_t slen;
3256
3257 slen = xlat_tokenize_expression_internal(ctx, out, in, p_rules, t_rules, true);
3258 if (slen < 0) return slen;
3259
3260#ifdef STATIC_ANALYZER
3261 if (!out) return -1;
3262#endif
3263 if (!*out) {
3264 fr_strerror_const("Empty conditions are invalid");
3265 return -1;
3266 }
3267
3268 if (xlat_finalize(*out, t_rules->xlat.runtime_el) < 0) {
3269 TALLOC_FREE(*out);
3270 return -1;
3271 }
3272
3273 return slen;
3274}
3275
3276/** Allow callers to see if an xlat is truthy
3277 *
3278 * So the caller can cache it, and needs to check fewer things at run
3279 * time.
3280 *
3281 * @param[in] head of the xlat to check
3282 * @param[out] out truthiness of the box
3283 * @return
3284 * - false - xlat is not truthy, *out is unchanged.
3285 * - true - xlat is truthy, *out is the result of fr_value_box_is_truthy()
3286 */
3288{
3289 xlat_exp_t const *node;
3290 fr_value_box_t const *box;
3291
3292 /*
3293 * Only pure / constant things can be truthy.
3294 */
3295 if (!head->flags.pure) goto return_false;
3296
3297 node = xlat_exp_head(head);
3298 if (!node) {
3299 *out = false;
3300 return true;
3301 }
3302
3303 if (xlat_exp_next(head, node)) goto return_false;
3304
3305 if (node->type == XLAT_BOX) {
3306 box = &node->data;
3307
3308 } else if ((node->type == XLAT_TMPL) && tmpl_is_data(node->vpt)) {
3309 box = tmpl_value(node->vpt);
3310
3311 } else {
3312 return_false:
3313 *out = false;
3314 return false;
3315 }
3316
3318 return true;
3319}
va_list args
Definition acutest.h:917
static uint8_t request[4]
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
Definition build.h:186
#define RCSID(id)
Definition build.h:560
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:228
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition build.h:391
#define unlikely(_x)
Definition build.h:455
#define UNUSED
Definition build.h:384
#define NUM_ELEMENTS(_t)
Definition build.h:406
int fr_value_calc_list_cmp(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_value_box_list_t const *list1, fr_token_t op, fr_value_box_list_t const *list2)
Definition calc.c:2755
int fr_value_calc_binary_op(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t hint, fr_value_box_t const *a, fr_token_t op, fr_value_box_t const *b)
Calculate DST = A OP B.
Definition calc.c:2014
int fr_value_calc_unary_op(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_token_t op, fr_value_box_t const *src)
Calculate unary operations.
Definition calc.c:2589
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
#define fr_dcursor_list(_cursor)
Definition dcursor.h:812
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:177
#define MEM(x)
Definition debug.h:38
#define ERROR(fmt,...)
Definition dhcpclient.c:40
bool const fr_dict_attr_allowed_chars[SBUFF_CHAR_CLASS]
Characters allowed in a single dictionary attribute name.
Definition dict_util.c:56
fr_dict_t const * fr_dict_internal(void)
Definition dict_util.c:5068
static void * fr_dlist_remove(fr_dlist_head_t *list_head, void *ptr)
Remove an item from the list.
Definition dlist.h:620
static unsigned int fr_dlist_num_elements(fr_dlist_head_t const *head)
Return the number of elements in the dlist.
Definition dlist.h:921
static void * fr_dlist_pop_head(fr_dlist_head_t *list_head)
Remove the head item in a list.
Definition dlist.h:654
static int fr_dlist_insert_tail(fr_dlist_head_t *list_head, void *ptr)
Insert an item into the tail of a list.
Definition dlist.h:360
static xlat_action_t xlat_func_rcode(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, UNUSED fr_value_box_list_t *args)
Return the current rcode as a string.
Definition xlat_expr.c:1707
static xlat_action_t xlat_func_exists(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, UNUSED fr_value_box_list_t *in)
See if a named attribute exists.
Definition xlat_expr.c:1814
static xlat_action_t xlat_func_expr_rcode(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
Match the passed rcode against request->rcode.
Definition xlat_expr.c:1650
talloc_free(hp)
TALLOC_CTX * unlang_interpret_frame_talloc_ctx(request_t *request)
Get a talloc_ctx which is valid only for this frame.
Definition interpret.c:2053
#define UNLANG_SUB_FRAME
Definition interpret.h:37
#define RDEBUG3(fmt,...)
Definition log.h:360
#define RPEDEBUG(fmt,...)
Definition log.h:393
fr_type_t
@ FR_TYPE_INT8
8 Bit signed integer.
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_NULL
Invalid (uninitialised) attribute type.
@ FR_TYPE_UINT16
16 Bit unsigned integer.
@ FR_TYPE_INT64
64 Bit signed integer.
@ FR_TYPE_INT16
16 Bit signed integer.
@ FR_TYPE_UINT8
8 Bit unsigned integer.
@ FR_TYPE_UINT32
32 Bit unsigned integer.
@ FR_TYPE_INT32
32 Bit signed integer.
@ FR_TYPE_UINT64
64 Bit unsigned integer.
@ FR_TYPE_VOID
User data.
@ FR_TYPE_BOOL
A truth value.
@ FR_TYPE_SIZE
Unsigned integer capable of representing any memory address on the local system.
@ FR_TYPE_OCTETS
Raw octets.
@ FR_TYPE_GROUP
A grouping of other attributes.
unsigned int uint32_t
long int ssize_t
ssize_t fr_slen_t
#define fr_assert(_expr)
Definition rad_assert.h:37
#define REDEBUG(fmt,...)
#define RDEBUG(fmt,...)
static bool done
Definition radclient.c:80
fr_table_num_sorted_t const rcode_table[]
Definition rcode.c:35
rlm_rcode_t
Return codes indicating the result of the module call.
Definition rcode.h:44
@ RLM_MODULE_NOT_SET
Error resolving rcode (should not be returned by modules).
Definition rcode.h:45
@ RLM_MODULE_NUMCODES
How many valid return codes there are.
Definition rcode.h:57
static char const * name
bool const sbuff_char_word[SBUFF_CHAR_CLASS]
Definition sbuff.c:111
bool fr_sbuff_is_terminal(fr_sbuff_t *in, fr_sbuff_term_t const *tt)
Efficient terminal string search.
Definition sbuff.c:2311
fr_sbuff_term_t * fr_sbuff_terminals_amerge(TALLOC_CTX *ctx, fr_sbuff_term_t const *a, fr_sbuff_term_t const *b)
Merge two sets of terminal strings.
Definition sbuff.c:675
bool fr_sbuff_next_if_char(fr_sbuff_t *sbuff, char c)
Return true if the current char matches, and if it does, advance.
Definition sbuff.c:2247
#define fr_sbuff_start(_sbuff_or_marker)
#define fr_sbuff_out_by_longest_prefix(_match_len, _out, _table, _sbuff, _def)
#define fr_sbuff_is_str_literal(_sbuff, _str)
#define FR_SBUFF_IN_CHAR_RETURN(_sbuff,...)
#define fr_sbuff_set(_dst, _src)
#define fr_sbuff_adv_past_whitespace(_sbuff, _len, _tt)
#define fr_sbuff_current(_sbuff_or_marker)
#define FR_SBUFF_TERMS(...)
Initialise a terminal structure with a list of sorted strings.
Definition sbuff.h:190
#define FR_SBUFF_IN_STRCPY_LITERAL_RETURN(_sbuff, _str)
#define fr_sbuff_extend(_sbuff_or_marker)
#define fr_sbuff_used_total(_sbuff_or_marker)
#define FR_SBUFF_RETURN(_func, _sbuff,...)
#define fr_sbuff_is_char(_sbuff_or_marker, _c)
#define FR_SBUFF_ERROR_RETURN(_sbuff_or_marker)
#define FR_SBUFF_SET_RETURN(_dst, _src)
#define fr_sbuff_is_digit(_sbuff_or_marker)
#define fr_sbuff_uint8(_sbuff_or_marker, _eob)
#define FR_SBUFF(_sbuff_or_marker)
#define fr_sbuff_advance(_sbuff_or_marker, _len)
#define fr_sbuff_remaining(_sbuff_or_marker)
#define fr_sbuff_used(_sbuff_or_marker)
#define fr_sbuff_behind(_sbuff_or_marker)
#define FR_SBUFF_IN_STRCPY_RETURN(...)
#define FR_SBUFF_TALLOC_THREAD_LOCAL(_out, _init, _max)
Terminal element with pre-calculated lengths.
Definition sbuff.h:159
Set of terminal elements.
#define tmpl_contains_xlat(vpt)
Definition tmpl.h:234
#define tmpl_is_attr_unresolved(vpt)
Definition tmpl.h:226
int tmpl_resolve(tmpl_t *vpt, tmpl_res_rules_t const *tr_rules))
Attempt to resolve functions and attributes in xlats and attribute references.
#define tmpl_value(_tmpl)
Definition tmpl.h:960
#define tmpl_contains_regex(vpt)
Definition tmpl.h:233
#define tmpl_is_attr(vpt)
Definition tmpl.h:215
fr_dict_attr_t const * enumv
Enumeration attribute used to resolve enum values.
Definition tmpl.h:349
#define tmpl_xlat(_tmpl)
Definition tmpl.h:953
#define tmpl_rules_cast(_tmpl)
Definition tmpl.h:965
#define tmpl_contains_attr(vpt)
Definition tmpl.h:232
ssize_t tmpl_afrom_substr(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in, fr_token_t quote, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules))
Convert an arbitrary string into a tmpl_t.
tmpl_xlat_rules_t xlat
Rules/data for parsing xlats.
Definition tmpl.h:347
static bool tmpl_is_list(tmpl_t const *vpt)
Definition tmpl.h:943
int tmpl_cast_in_place(tmpl_t *vpt, fr_type_t type, fr_dict_attr_t const *enumv))
Convert tmpl_t of type TMPL_TYPE_DATA_UNRESOLVED or TMPL_TYPE_DATA to TMPL_TYPE_DATA of type specifie...
#define tmpl_is_data(vpt)
Definition tmpl.h:213
static fr_slen_t vpt
Definition tmpl.h:1298
#define tmpl_rules_copy_depth(_out, _in)
Copy parse rules for a nested parse, and increment the depth field.
Definition tmpl.h:373
void tmpl_debug(FILE *fp, tmpl_t const *vpt)
#define tmpl_value_type(_tmpl)
Definition tmpl.h:962
static fr_type_t tmpl_cast_get(tmpl_t *vpt)
Definition tmpl.h:1249
#define tmpl_is_data_unresolved(vpt)
Definition tmpl.h:224
fr_type_t cast
Whether there was an explicit cast.
Definition tmpl.h:351
tmpl_attr_rules_t attr
Rules/data for parsing attribute references.
Definition tmpl.h:346
bool tmpl_rules_depth_exceeded(tmpl_rules_t const *t_rules)
True when a parse has nested past the point where the stack is safe.
static fr_dict_attr_t const * tmpl_attr_tail_da(tmpl_t const *vpt)
Return the last attribute reference da.
Definition tmpl.h:824
struct tmpl_res_rules_s tmpl_res_rules_t
Definition tmpl.h:244
#define tmpl_is_regex(vpt)
Definition tmpl.h:220
fr_dict_attr_t const * enumv
for resolving T_BARE_WORD
Definition tmpl.h:400
fr_event_list_t * runtime_el
The eventlist to use for runtime instantiation of xlats.
Definition tmpl.h:335
#define tmpl_needs_resolving(vpt)
Definition tmpl.h:230
int tmpl_cast_set(tmpl_t *vpt, fr_type_t type)
Set a cast for a tmpl.
Similar to tmpl_rules_t, but used to specify parameters that may change during subsequent resolution ...
Definition tmpl.h:391
Optional arguments passed to vp_tmpl functions.
Definition tmpl.h:343
static void xor(char *out, char *in1, char *in2, int n)
Definition smbdes.c:183
static void lshift(char *d, int count, int n)
Definition smbdes.c:165
eap_aka_sim_process_conf_t * inst
fr_aka_sim_id_type_t type
fr_pair_t * vp
fr_dict_t const * dict_def
Default dictionary to use with unqualified attribute references.
Definition tmpl.h:280
unsigned int allow_unresolved
Allow attributes that look valid but were not found in the dictionaries.
Definition tmpl.h:313
Stores an attribute, a value and various bits of other data.
Definition pair.h:68
#define fr_table_value_by_str(_table, _name, _def)
Convert a string to a value using a sorted or ordered table.
Definition table.h:685
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition table.h:804
An element in an arbitrarily ordered array of name to num mappings.
Definition table.h:57
An element in a lexicographically sorted array of name to num mappings.
Definition table.h:49
#define talloc_get_type_abort_const
Definition talloc.h:117
static int talloc_const_free(void const *ptr)
Free const'd memory.
Definition talloc.h:289
void tmpl_dcursor_clear(tmpl_dcursor_ctx_t *cc)
Clear any temporary state allocations.
#define tmpl_dcursor_init(_err, _ctx, _cc, _cursor, _request, _vpt)
Maintains state between cursor calls.
const char fr_token_quote[T_TOKEN_LAST]
Convert tokens back to a quoting character.
Definition token.c:224
char const * fr_tokens[T_TOKEN_LAST]
Definition token.c:146
const bool fr_comparison_op[T_TOKEN_LAST]
Definition token.c:266
const bool fr_binary_op[T_TOKEN_LAST]
Definition token.c:284
enum fr_token fr_token_t
@ T_AND
Definition token.h:53
@ T_INVALID
Definition token.h:37
@ T_SUB
Definition token.h:50
@ T_RSHIFT
Definition token.h:60
@ T_NOT
Definition token.h:55
@ T_XOR
Definition token.h:56
@ T_DIV
Definition token.h:52
@ T_SINGLE_QUOTED_STRING
Definition token.h:120
@ T_MOD
Definition token.h:58
@ T_BARE_WORD
Definition token.h:118
@ T_OP_EQ
Definition token.h:81
@ T_LAND
Definition token.h:89
@ T_COMPLEMENT
Definition token.h:57
@ T_ADD
Definition token.h:49
@ T_BACK_QUOTED_STRING
Definition token.h:121
@ T_OP_NE
Definition token.h:95
@ T_LOR
Definition token.h:90
@ T_LSHIFT
Definition token.h:61
@ T_OP_REG_EQ
Definition token.h:100
@ T_OP_CMP_EQ_TYPE
Definition token.h:105
@ T_DOUBLE_QUOTED_STRING
Definition token.h:119
@ T_OP_CMP_EQ
Definition token.h:104
@ T_LBRACE
Definition token.h:41
@ T_MUL
Definition token.h:51
@ T_OP_LE
Definition token.h:98
@ T_OP_CMP_NE_TYPE
Definition token.h:106
@ T_OP_GE
Definition token.h:96
@ T_OP_GT
Definition token.h:97
@ T_SOLIDUS_QUOTED_STRING
Definition token.h:122
@ T_OP_LT
Definition token.h:99
@ T_OP_REG_NE
Definition token.h:101
@ T_OR
Definition token.h:54
#define T_TOKEN_LAST
Definition token.h:127
xlat_action_t unlang_xlat_yield(request_t *request, xlat_func_t resume, xlat_func_signal_t signal, fr_signal_t sigmask, void *rctx)
Yield a request back to the interpreter from within a module.
Definition xlat.c:543
int unlang_xlat_push(TALLOC_CTX *ctx, unlang_result_t *p_result, fr_value_box_list_t *out, request_t *request, xlat_exp_head_t const *xlat, bool top_frame)
Push a pre-compiled xlat onto the stack for evaluation.
Definition xlat.c:269
tmpl_res_rules_t const * tr_rules
tmpl resolution rules.
Definition xlat.h:155
fr_type_t type
Type to cast argument to.
Definition xlat.h:145
#define XLAT_HEAD_VERIFY(_head)
Definition xlat.h:456
unsigned int pure
has no external side effects, true for BOX, LITERAL, and some functions
Definition xlat.h:110
fr_slen_t xlat_print(fr_sbuff_t *in, xlat_exp_head_t const *node, fr_sbuff_escape_rules_t const *e_rules)
Reconstitute an xlat expression from its constituent nodes.
unsigned int concat
Concat boxes together.
Definition xlat.h:137
static fr_slen_t head
Definition xlat.h:410
#define XLAT_RESULT_SUCCESS(_p_result)
Definition xlat.h:492
static fr_slen_t xlat_aprint(TALLOC_CTX *ctx, char **out, xlat_exp_head_t const *head, fr_sbuff_escape_rules_t const *e_rules) 1(xlat_print
int xlat_purify_op(TALLOC_CTX *ctx, xlat_exp_t **out, xlat_exp_t *lhs, fr_token_t op, xlat_exp_t *rhs)
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:222
#define XLAT_ARGS(_list,...)
Populate local variables with value boxes from the input list.
Definition xlat.h:373
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:1939
unsigned int required
Argument must be present, and non-empty.
Definition xlat.h:136
#define XLAT_VERIFY(_node)
Definition xlat.h:455
int xlat_resolve(xlat_exp_head_t *head, xlat_res_rules_t const *xr_rules)
Walk over an xlat tree recursively, resolving any unresolved functions or references.
#define XLAT_ARG_PARSER_TERMINATOR
Definition xlat.h:160
int xlat_finalize(xlat_exp_head_t *head, fr_event_list_t *runtime_el)
Bootstrap static xlats, or instantiate ephemeral ones.
Definition xlat_inst.c:699
xlat_action_t
Definition xlat.h:37
@ XLAT_ACTION_FAIL
An xlat function failed.
Definition xlat.h:44
@ XLAT_ACTION_YIELD
An xlat function pushed a resume frame onto the stack.
Definition xlat.h:42
@ XLAT_ACTION_PUSH_UNLANG
An xlat function pushed an unlang frame onto the unlang stack.
Definition xlat.h:39
@ XLAT_ACTION_DONE
We're done evaluating this level of nesting.
Definition xlat.h:43
unsigned int can_purify
if the xlat has a pure function with pure arguments.
Definition xlat.h:112
unsigned int constant
xlat is just tmpl_attr_tail_data, or XLAT_BOX
Definition xlat.h:114
unsigned int needs_resolving
Needs pass2 resolution.
Definition xlat.h:109
int xlat_instance_unregister_func(xlat_exp_t *node)
Remove a node from the list of xlat instance data.
Definition xlat_inst.c:547
fr_type_t xlat_node_data_type(xlat_exp_t const *node)
Definition for a single argument consumed by an xlat function.
Definition xlat.h:135
Flags that control resolution and evaluation.
Definition xlat.h:108
static fr_slen_t parent
Definition pair.h:864
#define add(_type, _out, _in)
Definition stats.c:187
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
#define fr_strerror_const(_msg)
Definition strerror.h:223
fr_table_num_ordered_t const fr_type_table[]
Map data types to names representing those types.
Definition types.c:31
#define fr_type_is_variable_size(_x)
Definition types.h:388
#define fr_type_is_structural(_x)
Definition types.h:392
#define fr_type_is_null(_x)
Definition types.h:347
#define fr_type_is_leaf(_x)
Definition types.h:393
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
Definition types.h:454
fr_sbuff_parse_rules_t const * value_parse_rules_quoted[T_TOKEN_LAST]
Parse rules for quoted strings.
Definition value.c:622
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:4434
bool fr_value_box_is_truthy(fr_value_box_t const *in)
Check truthiness of values.
Definition value.c:7596
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:4234
void fr_value_box_memdup_shallow(fr_value_box_t *dst, fr_dict_attr_t const *enumv, uint8_t const *src, size_t len, bool tainted)
Assign a buffer to a box, but don't copy it.
Definition value.c:5247
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:4671
ssize_t fr_value_box_list_concat_as_string(fr_value_box_safety_t *safety, fr_sbuff_t *sbuff, fr_value_box_list_t *list, char const *sep, size_t sep_len, fr_sbuff_escape_rules_t const *e_rules, fr_value_box_list_action_t proc_action, fr_value_box_safe_for_t safe_for, bool flatten)
Concatenate a list of value boxes together.
Definition value.c:6484
void fr_value_box_strdup_shallow(fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, bool tainted)
Assign a buffer containing a nul terminated string to a box, but don't copy it.
Definition value.c:4781
void fr_value_box_clear(fr_value_box_t *data)
Clear/free any existing value and metadata.
Definition value.c:4417
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:4922
@ FR_VALUE_BOX_LIST_FREE_BOX
Free each processed box.
Definition value.h:264
#define fr_value_box_alloc(_ctx, _type, _enumv)
Allocate a value box of a specific type.
Definition value.h:668
static fr_sbuff_err_t char size_t fr_sbuff_t * in
Definition value.h:1061
fr_value_box_safe_for_t safe_for
A unique value to indicate if that value box is safe for consumption by a particular module for a par...
Definition value.h:187
#define fr_box_strvalue_len(_val, _len)
Definition value.h:340
static fr_sbuff_err_t char ** out
Definition value.h:1061
static fr_sbuff_err_t char size_t * len
Definition value.h:1061
#define VALUE_BOX_LIST_VERIFY(_x)
Definition value.h:1389
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:679
#define fr_value_box_init(_vb, _type, _enumv, _tainted)
Initialise a fr_value_box_t.
Definition value.h:634
#define fr_value_box_list_foreach(_list_head, _iter)
Definition value.h:253
#define FR_VALUE_BOX_SAFE_FOR_ANY
Definition value.h:178
The safety of a value.
Definition value.h:186
void xlat_exp_set_vpt(xlat_exp_t *node, tmpl_t *vpt)
Set the tmpl for a node, along with flags and the name.
Definition xlat_alloc.c:278
void xlat_exp_set_name(xlat_exp_t *node, char const *fmt, size_t len)
Set the format string for an xlat node.
Definition xlat_alloc.c:334
void xlat_exp_set_func(xlat_exp_t *node, xlat_t const *func, fr_dict_t const *dict)
Set the function for a node.
Definition xlat_alloc.c:300
void xlat_exp_set_name_shallow(xlat_exp_t *node, char const *fmt)
Set the format string for an xlat node from a pre-existing buffer.
Definition xlat_alloc.c:364
int xlat_regex_escape(fr_value_box_t *vb, UNUSED void *uctx)
Escape a value box so that it is safe to use as a regular expression.
void * rctx
Resume context.
Definition xlat_ctx.h:54
xlat_exp_t * ex
Tokenized expression to use in expansion.
Definition xlat_ctx.h:64
void const * inst
xlat instance data.
Definition xlat_ctx.h:50
void * inst
xlat instance data to populate.
Definition xlat_ctx.h:63
An xlat calling ctx.
Definition xlat_ctx.h:49
An xlat instantiation ctx.
Definition xlat_ctx.h:62
fr_dict_attr_t const * attr_expr_bool_enum
Definition xlat_eval.c:42
fr_dict_attr_t const * attr_cast_base
Definition xlat_eval.c:43
static xlat_action_t xlat_func_regex_search(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *in)
Definition xlat_expr.c:861
tmpl_t const * vpt
the attribute reference
Definition xlat_expr.c:1727
#define fr_sbuff_skip_whitespace(_x)
Definition xlat_expr.c:2072
static size_t const expr_assignment_op_table_len
Definition xlat_expr.c:2780
static fr_slen_t xlat_expr_print_binary(fr_sbuff_t *out, xlat_exp_t const *node, UNUSED void *inst, fr_sbuff_escape_rules_t const *e_rules)
Definition xlat_expr.c:153
#define XLAT_REGISTER_BOOL(_xlat, _func, _arg, _ret_type)
Definition xlat_expr.c:1871
static fr_slen_t xlat_expr_print_rcode(fr_sbuff_t *out, xlat_exp_t const *node, void *instance, UNUSED fr_sbuff_escape_rules_t const *e_rules)
Definition xlat_expr.c:1615
static void xlat_func_append_arg(xlat_exp_t *head, xlat_exp_t *node, bool exists)
Definition xlat_expr.c:70
bool xlat_is_truthy(xlat_exp_head_t const *head, bool *out)
Allow callers to see if an xlat is truthy.
Definition xlat_expr.c:3287
static xlat_action_t xlat_func_logical(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *in)
Process logical &&, ||.
Definition xlat_expr.c:1412
xlat_exp_t * xlat
to expand
Definition xlat_expr.c:533
#define XLAT_BINARY_FUNC(_name, _op)
Definition xlat_expr.c:388
static const fr_sbuff_term_t bracket_terms
Definition xlat_expr.c:3119
static fr_slen_t tokenize_rcode(xlat_exp_head_t *head, xlat_exp_t **out, fr_sbuff_t *in, fr_sbuff_term_t const *terminals)
Definition xlat_expr.c:2416
static size_t expr_quote_table_len
Definition xlat_expr.c:2096
static int xlat_instantiate_exists(xlat_inst_ctx_t const *xctx)
Definition xlat_expr.c:1755
static fr_slen_t xlat_expr_print_nary(fr_sbuff_t *out, xlat_exp_t const *node, void *instance, fr_sbuff_escape_rules_t const *e_rules)
Definition xlat_expr.c:903
int xlat_register_expressions(void)
Definition xlat_expr.c:1887
static xlat_arg_parser_t const xlat_func_exists_arg[]
Definition xlat_expr.c:1730
static xlat_action_t xlat_logical_process_arg(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, UNUSED fr_value_box_list_t *in)
Process one argument of a logical operation.
Definition xlat_expr.c:1154
static fr_slen_t xlat_tokenize_expression_internal(TALLOC_CTX *ctx, xlat_exp_head_t **out, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules, bool cond)
Definition xlat_expr.c:3144
static fr_slen_t xlat_expr_print_unary(fr_sbuff_t *out, xlat_exp_t const *node, UNUSED void *inst, fr_sbuff_escape_rules_t const *e_rules)
Definition xlat_expr.c:143
#define P(_x, _y)
Definition xlat_expr.c:2016
static xlat_arg_parser_t const regex_op_xlat_args[]
Definition xlat_expr.c:652
static void xlat_ungroup(xlat_exp_head_t *head)
Undo work which shouldn't have been done.
Definition xlat_expr.c:999
static xlat_action_t xlat_func_unary_complement(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *in)
Definition xlat_expr.c:1520
static xlat_action_t xlat_cmp_op(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, fr_value_box_list_t *in, fr_token_t op)
Definition xlat_expr.c:468
static const int precedence[T_TOKEN_LAST]
Definition xlat_expr.c:2018
static xlat_action_t xlat_func_unary_minus(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *in)
Definition xlat_expr.c:1513
static xlat_action_t xlat_logical_or_resume(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *in)
Definition xlat_expr.c:1233
unlang_result_t last_result
Definition xlat_expr.c:897
static fr_table_num_sorted_t const expr_quote_table[]
Definition xlat_expr.c:2090
#define XLAT_REGISTER_BINARY_OP(_op, _name)
Definition xlat_expr.c:1829
#define XLAT_REGEX_FUNC(_name, _op)
Definition xlat_expr.c:850
static xlat_action_t xlat_binary_op(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *in, fr_token_t op, fr_type_t default_type, fr_dict_attr_t const *enumv)
Definition xlat_expr.c:316
static bool valid_type(xlat_exp_t *node)
Definition xlat_expr.c:2782
static bool xlat_logical_and(xlat_logical_rctx_t *rctx, fr_value_box_list_t const *in)
See if the input is truthy or not.
Definition xlat_expr.c:1290
fr_value_box_t * box
output value-box
Definition xlat_expr.c:898
#define XLAT_DEBUG(...)
Definition xlat_expr.c:38
fr_value_box_list_t list
Definition xlat_expr.c:539
static xlat_action_t xlat_regex_op(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *in, fr_token_t op)
Definition xlat_expr.c:817
static xlat_action_t xlat_func_unary_not(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, fr_value_box_list_t *in)
Definition xlat_expr.c:1486
fr_regex_flags_t * regex_flags
Definition xlat_expr.c:534
static bool xlat_node_matches_bool(bool *result, xlat_exp_t *parent, xlat_exp_head_t *head, bool sense)
Definition xlat_expr.c:951
static int xlat_instantiate_regex(xlat_inst_ctx_t const *xctx)
Definition xlat_expr.c:607
static xlat_action_t xlat_func_unary_op(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *in, fr_token_t op)
Definition xlat_expr.c:1442
static int xlat_expr_resolve_binary(xlat_exp_t *node, UNUSED void *inst, xlat_res_rules_t const *xr_rules)
Definition xlat_expr.c:181
static const bool logical_ops[T_TOKEN_LAST]
Definition xlat_expr.c:1992
xlat_func_t callback
Definition xlat_expr.c:890
static xlat_arg_parser_t const binary_op_xlat_args[]
Definition xlat_expr.c:310
static xlat_action_t xlat_attr_exists(TALLOC_CTX *ctx, fr_dcursor_t *out, request_t *request, tmpl_t const *vpt, bool do_free)
Definition xlat_expr.c:1786
static xlat_arg_parser_t const binary_cmp_xlat_args[]
Definition xlat_expr.c:413
static const bool multivalue_ops[T_TOKEN_LAST]
Definition xlat_expr.c:2005
static fr_slen_t tokenize_unary(xlat_exp_head_t *head, xlat_exp_t **out, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules, fr_sbuff_parse_rules_t const *bracket_rules, char *out_c, bool cond))
Definition xlat_expr.c:2112
static const fr_sbuff_term_elem_t binary_ops[T_TOKEN_LAST]
Definition xlat_expr.c:1960
static bool xlat_logical_or(xlat_logical_rctx_t *rctx, fr_value_box_list_t const *in)
See if the input is truthy or not.
Definition xlat_expr.c:1191
fr_token_t op
Definition xlat_expr.c:531
TALLOC_CTX * ctx
Definition xlat_expr.c:896
regex_t * regex
precompiled regex
Definition xlat_expr.c:532
#define XLAT_REGISTER_NARY_OP(_op, _name, _func_name)
Definition xlat_expr.c:1850
static int xlat_instantiate_expr_rcode(xlat_inst_ctx_t const *xctx)
Convert static expr_rcode arguments into rcodes.
Definition xlat_expr.c:1543
static void fr_value_box_init_zero(fr_value_box_t *vb, fr_type_t type)
Definition xlat_expr.c:287
static xlat_action_t xlat_regex_resume(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *in)
Definition xlat_expr.c:777
static fr_table_num_ordered_t const expr_assignment_op_table[]
Definition xlat_expr.c:2748
static xlat_exp_t * xlat_exists_alloc(TALLOC_CTX *ctx, xlat_exp_t *child)
Allocate a specific cast node.
Definition xlat_expr.c:112
static xlat_exp_t * expr_cast_alloc(TALLOC_CTX *ctx, fr_type_t type, xlat_exp_t *child)
Allocate a specific cast node.
Definition xlat_expr.c:2221
static xlat_arg_parser_t const regex_search_xlat_args[]
Definition xlat_expr.c:658
static fr_slen_t expr_cast_from_substr(fr_type_t *cast, fr_sbuff_t *in)
Definition xlat_expr.c:2260
#define XLAT_REGISTER_BINARY_CMP(_op, _name)
Definition xlat_expr.c:1839
static xlat_arg_parser_t const xlat_func_rcode_arg[]
Takes no arguments.
Definition xlat_expr.c:1693
static int xlat_expr_logical_purify(xlat_exp_t *node, void *instance, request_t *request)
If any argument resolves to inst->stop_on_match, the entire thing is a bool of inst->stop_on_match.
Definition xlat_expr.c:1025
fr_value_box_list_t list
Definition xlat_expr.c:900
rlm_rcode_t rcode
The preparsed rcode.
Definition xlat_expr.c:1535
xlat_exp_head_t ** argv
Definition xlat_expr.c:892
static fr_slen_t xlat_expr_print_exists(fr_sbuff_t *out, xlat_exp_t const *node, void *instance, fr_sbuff_escape_rules_t const *e_rules)
Definition xlat_expr.c:1738
static int xlat_instantiate_logical(xlat_inst_ctx_t const *xctx)
Definition xlat_expr.c:1392
#define XLAT_REGISTER_UNARY(_op, _xlat, _func)
Definition xlat_expr.c:1878
static xlat_arg_parser_t const unary_op_xlat_args[]
Definition xlat_expr.c:1437
fr_slen_t xlat_tokenize_expression(TALLOC_CTX *ctx, xlat_exp_head_t **out, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules)
Definition xlat_expr.c:3224
static xlat_arg_parser_t const xlat_func_expr_rcode_arg[]
Definition xlat_expr.c:1527
static fr_slen_t tokenize_regex_rhs(xlat_exp_head_t *head, xlat_exp_t **out, fr_sbuff_t *in, tmpl_rules_t const *t_rules, fr_sbuff_parse_rules_t const *bracket_rules)
Definition xlat_expr.c:2317
static fr_slen_t xlat_expr_print_regex(fr_sbuff_t *out, xlat_exp_t const *node, void *instance, fr_sbuff_escape_rules_t const *e_rules)
Definition xlat_expr.c:542
static const fr_sbuff_term_t operator_terms
Definition xlat_expr.c:3124
#define XLAT_REGISTER_REGEX_OP(_op, _name)
Definition xlat_expr.c:1861
static xlat_action_t xlat_logical_and_resume(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *in)
Definition xlat_expr.c:1337
static ssize_t tokenize_expression(xlat_exp_head_t *head, xlat_exp_t **out, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules, fr_token_t prev, fr_sbuff_parse_rules_t const *bracket_rules, fr_sbuff_parse_rules_t const *input_rules, bool cond))
Tokenize a mathematical operation.
Definition xlat_expr.c:2821
#define XLAT_CMP_FUNC(_name, _op)
Definition xlat_expr.c:513
static ssize_t tokenize_field(xlat_exp_head_t *head, xlat_exp_t **out, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules, fr_sbuff_parse_rules_t const *bracket_rules, char *out_c, bool cond))
Definition xlat_expr.c:2474
unlang_result_t last_result
Definition xlat_expr.c:538
fr_slen_t xlat_tokenize_condition(TALLOC_CTX *ctx, xlat_exp_head_t **out, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules)
Definition xlat_expr.c:3252
static xlat_action_t xlat_regex_do_op(TALLOC_CTX *ctx, request_t *request, fr_value_box_list_t *in, regex_t **preg, fr_dcursor_t *out, fr_token_t op)
Perform a regular expressions comparison between two operands.
Definition xlat_expr.c:691
Holds the result of pre-parsing the rcode on startup.
Definition xlat_expr.c:1534
void xlat_func_flags_set(xlat_t *x, xlat_func_flags_t flags)
Specify flags that alter the xlat's behaviour.
Definition xlat_func.c:401
int xlat_func_args_set(xlat_t *x, xlat_arg_parser_t const args[])
Register the arguments of an xlat.
Definition xlat_func.c:374
xlat_t * xlat_func_register(TALLOC_CTX *ctx, char const *name, xlat_func_t func, fr_type_t return_type)
Register an xlat function.
Definition xlat_func.c:225
void xlat_func_print_set(xlat_t *xlat, xlat_print_t func)
Set a print routine for an xlat function.
Definition xlat_func.c:415
xlat_t * xlat_func_find(char const *in, ssize_t inlen)
Definition xlat_func.c:77
#define xlat_func_instantiate_set(_xlat, _instantiate, _inst_struct, _detach, _uctx)
Set a callback for global instantiation of xlat functions.
Definition xlat_func.h:95
@ XLAT_FUNC_FLAG_PURE
Definition xlat_func.h:38
@ XLAT_FUNC_FLAG_INTERNAL
Definition xlat_func.h:39
@ XLAT_FUNC_FLAG_PRIVATE
Definition xlat_func.h:41
#define xlat_exp_head_alloc(_ctx)
Definition xlat_priv.h:276
xlat_flags_t flags
Flags that control resolution and evaluation.
Definition xlat_priv.h:156
static xlat_exp_t * xlat_exp_next(xlat_exp_head_t const *head, xlat_exp_t const *node)
Definition xlat_priv.h:249
xlat_flags_t flags
Flags that control resolution and evaluation.
Definition xlat_priv.h:192
fr_slen_t xlat_tokenize_word(TALLOC_CTX *ctx, xlat_exp_t **out, fr_sbuff_t *in, fr_token_t quote, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules)
fr_token_t quote
Type of quoting around XLAT_GROUP types.
Definition xlat_priv.h:154
@ XLAT_BOX
fr_value_box_t
Definition xlat_priv.h:110
@ XLAT_TMPL
xlat attribute
Definition xlat_priv.h:114
@ XLAT_FUNC
xlat module
Definition xlat_priv.h:112
@ XLAT_GROUP
encapsulated string of xlats
Definition xlat_priv.h:118
bool deprecated
this function was deprecated
Definition xlat_priv.h:70
static void xlat_flags_merge(xlat_flags_t *parent, xlat_flags_t const *child)
Merge flags from child to parent.
Definition xlat_priv.h:232
#define xlat_exp_set_type(_node, _type)
Definition xlat_priv.h:279
fr_token_t token
for expressions
Definition xlat_priv.h:72
char const *_CONST fmt
The original format string (a talloced buffer).
Definition xlat_priv.h:153
ssize_t xlat_print_node(fr_sbuff_t *out, xlat_exp_head_t const *head, xlat_exp_t const *node, fr_sbuff_escape_rules_t const *e_rules, char c)
int xlat_purify_list(xlat_exp_head_t *head, request_t *request)
Definition xlat_purify.c:65
xlat_type_t _CONST type
type of this expansion.
Definition xlat_priv.h:157
#define xlat_exp_alloc(_ctx, _type, _in, _inlen)
Definition xlat_priv.h:285
xlat_flags_t flags
various flags
Definition xlat_priv.h:94
#define xlat_exp_foreach(_list_head, _iter)
Iterate over the contents of a list, only one level.
Definition xlat_priv.h:225
static int xlat_exp_insert_tail(xlat_exp_head_t *head, xlat_exp_t *node)
Definition xlat_priv.h:241
static xlat_exp_t * xlat_exp_head(xlat_exp_head_t const *head)
Definition xlat_priv.h:212
An xlat expansion node.
Definition xlat_priv.h:150