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