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