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