Coverage Report

Created: 2026-11-12 06:42

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