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