The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
xlat_tokenize.c
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or modify
3  * it under the terms of the GNU General Public License as published by
4  * the Free Software Foundation; either version 2 of the License, or
5  * (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15  */
16 
17 /**
18  * $Id: f35029b4b1fcd60ee5db6eda0178bba8c68bf3a7 $
19  *
20  * @file xlat_tokenize.c
21  * @brief String expansion ("translation"). Tokenizes xlat expansion strings.
22  *
23  * @copyright 2017-2021 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
24  * @copyright 2000 Alan DeKok (aland@freeradius.org)
25  * @copyright 2000,2006 The FreeRADIUS server project
26  */
27 
28 #include "lib/util/event.h"
29 #include "lib/util/value.h"
30 RCSID("$Id: f35029b4b1fcd60ee5db6eda0178bba8c68bf3a7 $")
31 
32 #include <freeradius-devel/util/debug.h>
33 #include <freeradius-devel/util/sbuff.h>
34 #include <freeradius-devel/server/regex.h>
35 #include <freeradius-devel/unlang/xlat_priv.h>
36 
37 #undef XLAT_DEBUG
38 #undef XLAT_HEXDUMP
39 #ifdef DEBUG_XLAT
40 # define XLAT_DEBUG(_fmt, ...) DEBUG3("%s[%i] "_fmt, __FILE__, __LINE__, ##__VA_ARGS__)
41 # define XLAT_HEXDUMP(_data, _len, _fmt, ...) HEXDUMP3(_data, _len, "%s[%i] "_fmt, __FILE__, __LINE__, ##__VA_ARGS__)
42 #else
43 # define XLAT_DEBUG(...)
44 # define XLAT_HEXDUMP(...)
45 #endif
46 
47 /** These rules apply to literal values and function arguments inside of an expansion
48  *
49  */
51  .name = "xlat",
52  .chr = '\\',
53  .subs = {
54  ['a'] = '\a',
55  ['b'] = '\b',
56  ['e'] = '\\',
57  ['n'] = '\n',
58  ['r'] = '\r',
59  ['t'] = '\t',
60  ['v'] = '\v',
61  ['\\'] = '\\',
62  ['%'] = '%', /* Expansion begin */
63  ['}'] = '}' /* Expansion end */
64  },
65  .do_hex = true,
66  .do_oct = true
67 };
68 
69 /** These rules apply to literal values and function arguments inside of an expansion
70  *
71  */
73  .name = "xlat",
74  .chr = '\\',
75  .subs = {
76  ['\a'] = 'a',
77  ['\b'] = 'b',
78  ['\n'] = 'n',
79  ['\r'] = 'r',
80  ['\t'] = 't',
81  ['\v'] = 'v',
82  ['\\'] = '\\',
83  ['%'] = '%', /* Expansion begin */
84  ['}'] = '}' /* Expansion end */
85  },
86  .esc = {
89  },
90  .do_utf8 = true,
91  .do_oct = true
92 };
93 
94 /** Parse rules for literal values inside of an expansion
95  *
96  * These rules are used to parse literals as arguments to functions and
97  * on the RHS of alternations.
98  *
99  * The caller sets the literal parse rules for outside of expansions when they
100  * call xlat_tokenize.
101  */
102 static fr_sbuff_parse_rules_t const xlat_function_arg_rules = {
103  .escapes = &xlat_unescape,
104  .terminals = &FR_SBUFF_TERMS( /* These get merged with other literal terminals */
105  L(")"),
106  L(","),
107  ),
108 };
109 
111  fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules, fr_value_box_safe_for_t safe_for);
112 
113 #ifdef HAVE_REGEX
114 /** Parse an xlat reference
115  *
116  * Allows access to a subcapture groups
117  * @verbatim %{<num>} @endverbatim
118  *
119  */
120 static inline int xlat_tokenize_regex(xlat_exp_head_t *head, fr_sbuff_t *in)
121 {
122  uint8_t num;
123  xlat_exp_t *node;
125  fr_sbuff_marker_t m_s;
126 
127  XLAT_DEBUG("REGEX <-- %.*s", (int) fr_sbuff_remaining(in), fr_sbuff_current(in));
128 
129  fr_sbuff_marker(&m_s, in);
130 
131  (void) fr_sbuff_out(&err, &num, in);
132  if (err != FR_SBUFF_PARSE_OK) {
133  invalid_ref:
134  fr_strerror_printf("Invalid regex reference. Must be in range 0-%u", REQUEST_MAX_REGEX);
135  fr_sbuff_marker_release(&m_s);
136  return -1;
137  }
138 
139  if (num > REQUEST_MAX_REGEX) {
140  fr_sbuff_set(in, &m_s);
141  goto invalid_ref;
142  }
143 
144  if (!fr_sbuff_is_char(in, '}')) {
145  if (!fr_sbuff_remaining(in)) {
146  fr_strerror_const("Missing closing brace");
147  fr_sbuff_marker_release(&m_s);
148  return -1;
149  }
150  fr_sbuff_set(in, &m_s);
151  fr_sbuff_marker_release(&m_s);
152  return 1;
153  }
154 
155  node = xlat_exp_alloc(head, XLAT_REGEX, fr_sbuff_current(&m_s), fr_sbuff_behind(&m_s));
156  node->regex_index = num;
157 
158  fr_sbuff_marker_release(&m_s);
159  fr_sbuff_next(in); /* Skip '}' */
160 
161  xlat_exp_insert_tail(head, node);
162 
163  return 0;
164 }
165 #endif
166 
168 {
169  fr_assert(node->type == XLAT_FUNC);
170 
171  if (node->call.func->args && node->call.func->args->required && !xlat_exp_head(node->call.args)) {
172  fr_strerror_const("Missing required input");
173  return -1;
174  }
175 
176  return 0;
177 }
178 
179 bool const xlat_func_chars[UINT8_MAX + 1] = {
181  ['.'] = true, ['-'] = true, ['_'] = true,
182 };
183 
184 
186 {
187  ssize_t slen;
188  xlat_exp_t *node;
189  fr_value_box_t box;
190 
191  /*
192  * The caller doesn't care about the type, OR the type is string, which it already is.
193  */
194  if ((arg_p->type == FR_TYPE_VOID) || (arg_p->type == FR_TYPE_STRING)) {
195  return 0;
196  }
197 
198  node = xlat_exp_head(arg->group);
199 
200  if (!node) return -1;
201 
202  /*
203  * @todo - check arg_p->single, and complain.
204  */
205  if (xlat_exp_next(arg->group, node)) return 0;
206 
207  /*
208  * @todo - These checks are relatively basic. We should do better checks, such as if the
209  * expected type is not string/octets, and the passed arguments are multiple things, then
210  * die?
211  *
212  * And check also the 'concat' flag?
213  */
214  if (node->type != XLAT_BOX) return 0;
215 
216  /*
217  * Boxes are always strings, because of xlat_tokenize_input()
218  */
219  fr_assert(node->data.type == FR_TYPE_STRING);
220 
222 
223  /*
224  * The entire string must be parseable as the data type we expect.
225  */
226  slen = fr_value_box_from_str(node, &box, arg_p->type, NULL, /* no enum */
227  node->data.vb_strvalue, node->data.vb_length,
228  NULL, /* no parse rules */
229  node->data.tainted);
230  if (slen <= 0) return -1;
231 
232  /*
233  * Replace the string value with the parsed data type.
234  */
235  fr_value_box_clear(&node->data);
236  fr_value_box_copy(node, &node->data, &box);
237 
238  return 0;
239 }
240 
242 {
243  xlat_arg_parser_t const *arg_p;
244  xlat_exp_t *arg = xlat_exp_head(node->call.args);
245  int i = 0;
246 
247  fr_assert(node->type == XLAT_FUNC);
248 
249  for (arg_p = node->call.func->args, i = 0; arg_p->type != FR_TYPE_NULL; arg_p++) {
250  if (!arg_p->required) break;
251 
252  if (!arg) {
253  fr_strerror_printf("Missing required arg %u",
254  (unsigned int)(arg_p - node->call.func->args) + 1);
255  return -1;
256  }
257 
258  /*
259  * All arguments MUST be put into a group, even
260  * if they're just one element.
261  */
262  fr_assert(arg->type == XLAT_GROUP);
263 
264  if (xlat_validate_function_arg(arg_p, arg) < 0) {
265  fr_strerror_printf("Failed parsing argument %d as type '%s'", i, fr_type_to_str(arg_p->type));
266  return -1;
267  }
268 
269  arg = xlat_exp_next(node->call.args, arg);
270  i++;
271  }
272 
273  return 0;
274 }
275 
276 /** Parse an xlat function and its child argument
277  *
278  * Parses a function call string in the format
279  * @verbatim %<func>(<argument>) @endverbatim
280  *
281  * @return
282  * - 0 if the string was parsed into a function.
283  * - <0 on parse error.
284  */
286 {
287  char c;
288  xlat_exp_t *node;
289  xlat_t *func;
290  fr_sbuff_marker_t m_s;
291 
292  fr_sbuff_marker(&m_s, in);
293 
295 
296  /*
297  * The caller ensures that the first character aftet the percent exists, and is alphanumeric.
298  */
299  c = fr_sbuff_char(in, '\0');
300 
301  /*
302  * Even if it is alphanumeric, only a limited set of characters are one-letter expansions.
303  *
304  * And even then only if the character after them is a terminal character.
305  */
306  if (strchr("cCdDeGHIlmMnSstTY", c) != NULL) {
307  char n;
308 
309  fr_sbuff_next(in);
310 
311  /*
312  * End of buffer == one letter expansion.
313  */
314  n = fr_sbuff_char(in, '\0');
315  if (!n) goto one_letter;
316 
317  /*
318  * %Y() is the new format.
319  */
320  if (n == '(') {
321  fr_sbuff_next(in);
322 
323  if (!fr_sbuff_next_if_char(in, ')')) {
324  fr_strerror_const("Missing ')'");
325  return -1;
326  }
327 
328  goto one_letter;
329  }
330 
331  /*
332  * %M. or %Y- is a one-letter expansion followed by the other character.
333  */
334  if (!sbuff_char_alpha_num[(unsigned int) n]) {
335  one_letter:
336  XLAT_DEBUG("ONE-LETTER <-- %c", c);
337  node = xlat_exp_alloc_null(head);
338 
340  xlat_exp_set_name(node, fr_sbuff_current(&m_s), 1);
341 
342  fr_sbuff_marker_release(&m_s);
343 
344 #ifdef STATIC_ANALYZER
345  if (!node->fmt) return -1;
346 #endif
347 
348  /*
349  * %% is pure. Everything else is not.
350  */
351  node->flags.pure = (node->fmt[0] == '%');
352 
353  xlat_exp_insert_tail(head, node);
354  return 0;
355  }
356 
357  /*
358  * Anything else, it must be a full function name.
359  */
360  fr_sbuff_set(in, &m_s);
361  }
362 
363  fr_sbuff_adv_past_allowed(in, SIZE_MAX, xlat_func_chars, NULL);
364 
365  func = xlat_func_find(fr_sbuff_current(&m_s), fr_sbuff_behind(&m_s));
366 
367  if (!fr_sbuff_is_char(in, '(')) {
368  fr_strerror_printf("Missing '('");
369  return -1;
370  }
371 
372  /*
373  * Allocate a node to hold the function
374  */
376  if (!func) {
377  if (!t_rules || !t_rules->attr.allow_unresolved|| t_rules->at_runtime) {
378  fr_strerror_const("Unresolved expansion functions are not allowed here");
379  fr_sbuff_set(in, &m_s); /* backtrack */
380  fr_sbuff_marker_release(&m_s);
381  return -1;
382  }
384  node->flags.needs_resolving = true; /* Needs resolution during pass2 */
385  } else {
386  node->call.func = func;
387  if (t_rules) node->call.dict = t_rules->attr.dict_def;
388  node->flags = func->flags;
389  node->call.input_type = func->input_type;
390  }
391 
392  (void) fr_sbuff_next(in); /* skip the '(' */
393 
394  /*
395  * Now parse the child nodes that form the
396  * function's arguments.
397  */
398  if (xlat_tokenize_argv(node, &node->call.args, in, func,
399  &xlat_function_arg_rules, t_rules, true, (node->call.input_type == XLAT_INPUT_MONO)) < 0) {
400 error:
401  talloc_free(node);
402  return -1;
403  }
404 
405  xlat_flags_merge(&node->flags, &node->call.args->flags);
406 
407  if (!fr_sbuff_next_if_char(in, ')')) {
408  fr_strerror_const("Missing closing brace");
409  goto error;
410  }
411 
412  /*
413  * Validate the arguments.
414  */
415  if (node->type == XLAT_FUNC) {
416  switch (node->call.input_type) {
418  break;
419 
420  case XLAT_INPUT_MONO:
421  if (xlat_validate_function_mono(node) < 0) goto error;
422  node->flags.can_purify = (node->call.func->flags.pure && node->call.args->flags.pure) | node->call.args->flags.can_purify;
423  break;
424 
425  case XLAT_INPUT_ARGS:
426  if (xlat_validate_function_args(node) < 0) goto error;
427  node->flags.can_purify = (node->call.func->flags.pure && node->call.args->flags.pure) | node->call.args->flags.can_purify;
428  break;
429  }
430  }
431 
432  xlat_exp_insert_tail(head, node);
433  return 0;
434 }
435 
437 {
438  xlat_t *func;
439 
440  if (tmpl_is_attr(vpt)) {
442  } else {
444  }
445  if (!func) return -1;
446 
449 
450  XLAT_DEBUG("VIRTUAL <-- %pV",
451  fr_box_strvalue_len(vpt->name, vpt->len));
452  node->call.func = func;
453  node->flags = func->flags;
454 
455  return 0;
456 }
457 
458 /** Parse an attribute ref or a virtual attribute
459  *
460  */
462  fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules, tmpl_attr_prefix_t attr_prefix)
463 {
465  tmpl_t *vpt = NULL;
466  xlat_exp_t *node;
467 
468  fr_sbuff_marker_t m_s;
469  tmpl_rules_t our_t_rules;
470 
471  XLAT_DEBUG("ATTRIBUTE <-- %.*s", (int) fr_sbuff_remaining(in), fr_sbuff_current(in));
472 
473  /*
474  * We need a local copy as we always allow unknowns.
475  * This is because not all attribute references
476  * reference real attributes in the dictionaries,
477  * and instead are "virtual" attributes like
478  * Foreach-Variable-N.
479  */
480  if (t_rules) {
481  memset(&our_t_rules, 0, sizeof(our_t_rules));
482  our_t_rules = *t_rules;
483  } else {
484  memset(&our_t_rules, 0, sizeof(our_t_rules));
485  }
486 
487  our_t_rules.attr.allow_unresolved = true; /* So we can check for virtual attributes later */
488  our_t_rules.attr.prefix = attr_prefix; /* Must be NO to stop %{&User-Name} */
489 
490  fr_sbuff_marker(&m_s, in);
491 
492  MEM(node = xlat_exp_alloc_null(head));
493  if (tmpl_afrom_attr_substr(node, &err, &vpt, in, p_rules, &our_t_rules) < 0) {
494  /*
495  * If the parse error occurred before a terminator,
496  * then the error is changed to 'Unknown module',
497  * as it was more likely to be a bad module name,
498  * than a request qualifier.
499  */
501  error:
502  fr_sbuff_marker_release(&m_s);
503  talloc_free(node);
505  }
506 
507  /*
508  * Deal with unresolved attributes.
509  */
511  /*
512  * Could it be a virtual attribute?
513  */
514  if ((tmpl_attr_num_elements(vpt) == 2) && (xlat_resolve_virtual_attribute(node, vpt) == 0)) goto done;
515 
516  if (!t_rules || !t_rules->attr.allow_unresolved) {
517  talloc_free(vpt);
518 
519  fr_strerror_const("Unresolved attributes not allowed in expansions here");
520  fr_sbuff_set(in, &m_s); /* Error at the start of the attribute */
521  goto error;
522  }
523 
524  /*
525  * We don't know it's virtual but
526  * we don't know it's not either...
527  *
528  * Mark it up as virtual-unresolved
529  * and let the resolution code figure
530  * this out in a later pass.
531  */
534  node->vpt = vpt;
535  node->flags.needs_resolving = true;
536  /*
537  * Deal with normal attribute (or list)
538  */
539  } else {
542  node->vpt = vpt;
543  }
544 
545 done:
546  /*
547  * Attributes and module calls aren't pure.
548  */
549  node->flags.pure = false;
550 
551  xlat_exp_insert_tail(head, node);
552 
553  fr_sbuff_marker_release(&m_s);
554  return 0;
555 }
556 
557 static bool const tmpl_attr_allowed_chars[UINT8_MAX + 1] = {
559  ['-'] = true, ['/'] = true, ['_'] = true, // fr_dict_attr_allowed_chars
560  ['.'] = true, ['*'] = true, ['#'] = true,
561  ['['] = true, [']'] = true, // tmpls and attribute arrays
562 };
563 
565  tmpl_rules_t const *t_rules)
566 {
567  size_t len;
568  fr_sbuff_marker_t s_m;
569  char hint;
570  fr_sbuff_term_t hint_tokens = FR_SBUFF_TERMS(
571  L(" "), /* First special token is a ' ' - Likely a syntax error */
572  L("["), /* First special token is a '[' i.e. '%{attr[<idx>]}' */
573  L("}") /* First special token is a '}' i.e. '%{<attrref>}' */
574  );
575 
576  fr_sbuff_parse_rules_t attr_p_rules = {
577  .escapes = &xlat_unescape,
578  .terminals = &FR_SBUFF_TERM("}")
579  };
580 
581  XLAT_DEBUG("EXPANSION <-- %.*s", (int) fr_sbuff_remaining(in), fr_sbuff_current(in));
582 
583 #ifdef HAVE_REGEX
584  fr_sbuff_marker(&s_m, in);
585  len = fr_sbuff_adv_past_allowed(in, SIZE_MAX, sbuff_char_class_uint, NULL);
586 
587  /*
588  * Handle regex's %{<num>} specially. But '3GPP-Foo' is an attribute. :(
589  */
590  if (len && fr_sbuff_is_char(in, '}')) {
591  int ret;
592 
593  fr_sbuff_set(in, &s_m); /* backtrack */
594  ret = xlat_tokenize_regex(head, in);
595  if (ret <= 0) return ret;
596 
597  /* ret==1 means "nope, it's an attribute" */
598  }
599  fr_sbuff_set(in, &s_m); /* backtrack */
600 
601 #endif /* HAVE_REGEX */
602 
603  /*
604  * See if it's an attribute reference, with possible array stuff.
605  */
606  len = fr_sbuff_adv_past_allowed(in, SIZE_MAX, tmpl_attr_allowed_chars, NULL);
607  if (fr_sbuff_is_char(in, '}')) {
608  if (!len) goto empty_disallowed;
609  goto check_for_attr;
610  }
611 
612  if (!fr_sbuff_extend(in)) {
613  fr_strerror_const("Missing closing brace");
614  fr_sbuff_marker_release(&s_m);
615  return -1;
616  }
617 
618  /*
619  * It must be an expression.
620  *
621  * We wrap the xlat in a tmpl, so that the result is just a value, and not wrapped in another
622  * XLAT_GROUP, which turns into a wrapper of FR_TYPE_GROUP in the value-box.
623  */
624  {
625  int ret;
626  char *fmt;
627  xlat_exp_t *node;
628  xlat_exp_head_t *child;
629  tmpl_rules_t my_rules;
630 
631  fr_sbuff_set(in, &s_m); /* backtrack to the start of the expression */
632 
633  MEM(node = xlat_exp_alloc(head, XLAT_TMPL, NULL, 0));
634  MEM(node->vpt = tmpl_alloc(node, TMPL_TYPE_XLAT, T_BARE_WORD, "", 1));
635 
636  if (t_rules) {
637  my_rules = *t_rules;
638  my_rules.enumv = NULL;
639  my_rules.cast = FR_TYPE_NULL;
640  t_rules = &my_rules;
641  }
642 
643  ret = xlat_tokenize_expression(node->vpt, &child, in, &attr_p_rules, t_rules);
644  if (ret <= 0) {
645  talloc_free(node);
646  return ret;
647  }
648 
649  if (!fr_sbuff_is_char(in, '}')) {
650  fr_strerror_const("Missing closing brace");
651  return -1;
652  }
653 
654  MEM(fmt = talloc_bstrndup(node, fr_sbuff_current(&s_m), fr_sbuff_behind(&s_m)));
657 
658  tmpl_set_xlat(node->vpt, child);
659  xlat_exp_insert_tail(head, node);
660 
661  child->flags.xlat = true;
662  node->flags = child->flags;
663  fr_assert(tmpl_xlat(node->vpt) != NULL);
664 
665  (void) fr_sbuff_next(in); /* skip '}' */
666  return ret;
667  }
668 
669 check_for_attr:
670  fr_sbuff_set(in, &s_m); /* backtrack */
671 
672  /*
673  * %{Attr-Name}
674  * %{Attr-Name[#]}
675  * %{request.Attr-Name}
676  */
677 
678  /*
679  * Check for empty expressions %{} %{: %{[
680  */
681  fr_sbuff_marker(&s_m, in);
682  len = fr_sbuff_adv_until(in, SIZE_MAX, &hint_tokens, '\0');
683 
684  /*
685  * This means the end of a string not containing any of the other
686  * tokens was reached.
687  *
688  * e.g. '%{myfirstxlat'
689  */
690  if (!fr_sbuff_extend(in)) {
691  fr_strerror_const("Missing closing brace");
692  fr_sbuff_marker_release(&s_m);
693  return -1;
694  }
695 
696  hint = fr_sbuff_char(in, '\0');
697 
698  XLAT_DEBUG("EXPANSION HINT TOKEN '%c'", hint);
699  if (len == 0) {
700  switch (hint) {
701  case '}':
702  empty_disallowed:
703  fr_strerror_const("Empty expression is invalid");
704  return -1;
705 
706  case '[':
707  fr_strerror_const("Missing attribute name");
708  return -1;
709 
710  default:
711  break;
712  }
713  }
714 
715  switch (hint) {
716  /*
717  * Hint token is a:
718  * - '[' - Which is an attribute index, so it must be an attribute.
719  * - '}' - The end of the expansion, which means it was a bareword.
720  */
721  case '.':
722  case '}':
723  case '[':
724  fr_sbuff_set(in, &s_m); /* backtrack */
725  fr_sbuff_marker_release(&s_m);
726 
727  if (xlat_tokenize_attribute(head, in, &attr_p_rules, t_rules, TMPL_ATTR_REF_PREFIX_NO) < 0) return -1;
728 
729  if (!fr_sbuff_next_if_char(in, '}')) {
730  fr_strerror_const("Missing closing brace");
731  return -1;
732  }
733 
734  return 0;
735 
736  /*
737  * Hint token was whitespace
738  *
739  * e.g. '%{my '
740  */
741  default:
742  break;
743  }
744 
745  /*
746  * Box print is so we get \t \n etc..
747  */
748  fr_strerror_printf("Invalid char '%pV' in expression", fr_box_strvalue_len(fr_sbuff_current(in), 1));
749  return -1;
750 }
751 
752 /** Parse an xlat string i.e. a non-expansion or non-function
753  *
754  * When this function is being used outside of an xlat expansion, i.e. on a string
755  * which contains one or more xlat expansions, it uses the terminal grammar and
756  * escaping rules of that string type.
757  *
758  * Which this function is being used inside of an xlat expansion, it uses xlat specific
759  * terminal grammar and escaping rules.
760  *
761  * This allows us to be smart about processing quotes within the expansions themselves.
762  *
763  * @param[out] head to allocate nodes in, and where to write the first
764  * child, and where the flags are stored.
765  * @param[in] in sbuff to parse.
766  * @param[in] p_rules that control parsing.
767  * @param[in] t_rules that control attribute reference and xlat function parsing.
768  * @param[in] safe_for mark up literal values as being pre-escaped. May be merged
769  * with t_rules in future.
770  * @return
771  * - 0 on success.
772  * - -1 on failure.
773  */
775  fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules,
776  fr_value_box_safe_for_t safe_for)
777 {
778  xlat_exp_t *node = NULL;
779  fr_slen_t slen;
780  fr_sbuff_term_t terminals = FR_SBUFF_TERMS(
781  L("%"),
782  );
783  fr_sbuff_term_t *tokens;
785 
786  XLAT_DEBUG("STRING <-- %.*s", (int) fr_sbuff_remaining(in), fr_sbuff_current(in));
787 
788  escapes = p_rules ? p_rules->escapes : NULL;
789  tokens = p_rules && p_rules->terminals ?
790  fr_sbuff_terminals_amerge(NULL, p_rules->terminals, &terminals) : &terminals;
791 
792  for (;;) {
793  char *str;
794  fr_sbuff_marker_t m_s;
795 
796  /*
797  * pre-allocate the node so we don't have to steal it later.
798  */
799  node = xlat_exp_alloc(head, XLAT_BOX, NULL, 0);
800 
801  /*
802  * Find the next token
803  */
804  fr_sbuff_marker(&m_s, in);
805  slen = fr_sbuff_out_aunescape_until(node, &str, in, SIZE_MAX, tokens, escapes);
806 
807  if (slen < 0) {
808  error:
809  talloc_free(node);
810 
811  /*
812  * Free our temporary array of terminals
813  */
814  if (tokens != &terminals) talloc_free(tokens);
815  fr_sbuff_marker_release(&m_s);
816  return -1;
817  }
818 
819  /*
820  * It's a value box, create an appropriate node
821  */
822  if (slen > 0) {
823  do_value_box:
825  fr_value_box_strdup(node, &node->data, NULL, str, false);
826  fr_value_box_mark_safe_for(&node->data, safe_for);
827  node->flags.constant = true;
828  fr_assert(node->flags.pure);
829 
830  if (!escapes) {
831  XLAT_DEBUG("VALUE-BOX %s <-- %.*s", str,
832  (int) fr_sbuff_behind(&m_s), fr_sbuff_current(&m_s));
833  } else {
834  XLAT_DEBUG("VALUE-BOX (%s) %s <-- %.*s", escapes->name, str,
835  (int) fr_sbuff_behind(&m_s), fr_sbuff_current(&m_s));
836  }
837  XLAT_HEXDUMP((uint8_t const *)str, talloc_array_length(str) - 1, " VALUE-BOX ");
838 
839  xlat_exp_insert_tail(head, node);
840 
841  node = NULL;
842  fr_sbuff_marker_release(&m_s);
843  continue;
844  }
845 
846 
847  /*
848  * We have parsed as much as we can as unescaped
849  * input. Either some text (and added the node
850  * to the list), or zero text. We now try to
851  * parse '%' expansions.
852  */
853 
854  /*
855  * Attribute, function call, or other expansion.
856  */
857  if (fr_sbuff_adv_past_str_literal(in, "%{")) {
858  TALLOC_FREE(node); /* nope, couldn't use it */
859  if (xlat_tokenize_expansion(head, in, t_rules) < 0) goto error;
860  next:
861  fr_sbuff_marker_release(&m_s);
862  continue;
863  }
864 
865  /*
866  * More migration hacks: allow %foo(...)
867  */
868  if (fr_sbuff_next_if_char(in, '%')) {
869  /*
870  * % non-alphanumeric, create a value-box for just the "%" character.
871  */
872  if (!fr_sbuff_is_alnum(in)) {
873  if (fr_sbuff_next_if_char(in, '%')) { /* nothing */ }
874 
875  str = talloc_typed_strdup(node, "%");
876  goto do_value_box;
877  }
878 
879  TALLOC_FREE(node); /* nope, couldn't use it */
880 
881  /*
882  * Tokenize the function arguments using the new method.
883  */
884  if (xlat_tokenize_function_args(head, in, t_rules) < 0) goto error;
885  goto next;
886  }
887 
888  /*
889  * Nothing we recognize. Just return nothing.
890  */
891  TALLOC_FREE(node);
892  XLAT_DEBUG("VALUE-BOX <-- (empty)");
893  fr_sbuff_marker_release(&m_s);
894  break;
895  }
896 
897  /*
898  * Free our temporary array of terminals
899  */
900  if (tokens != &terminals) talloc_free(tokens);
901 
902  return 0;
903 }
904 
906  { L("\""), T_DOUBLE_QUOTED_STRING }, /* Don't re-order, backslash throws off ordering */
907  { L("'"), T_SINGLE_QUOTED_STRING },
908  { L("`"), T_BACK_QUOTED_STRING }
909 };
911 
912 #define INFO_INDENT(_fmt, ...) INFO("%*s"_fmt, depth * 2, " ", ## __VA_ARGS__)
913 
914 static void _xlat_debug_head(xlat_exp_head_t const *head, int depth);
915 static void _xlat_debug_node(xlat_exp_t const *node, int depth)
916 {
917  INFO_INDENT("{");
918  depth++;
919 
920  if (node->quote != T_BARE_WORD) INFO_INDENT("quote = %c", fr_token_quote[node->quote]);
921 
922  switch (node->type) {
923  case XLAT_BOX:
924  INFO_INDENT("value %s --> %pV", fr_type_to_str(node->data.type), &node->data);
925  break;
926 
927  case XLAT_GROUP:
928  INFO_INDENT("group");
929  INFO_INDENT("{");
930  _xlat_debug_head(node->group, depth + 1);
931  INFO_INDENT("}");
932  break;
933 
934  case XLAT_ONE_LETTER:
935  INFO_INDENT("percent (%c)", node->fmt[0]);
936  break;
937 
938  case XLAT_TMPL:
939  {
940  if (tmpl_is_attr(node->vpt)) {
941  fr_assert(!node->flags.pure);
942  INFO_INDENT("attribute (%s)", tmpl_attr_tail_da(node->vpt)->name);
943  if (tmpl_attr_tail_num(node->vpt) != NUM_UNSPEC) {
944  FR_DLIST_HEAD(tmpl_request_list) const *list;
945  tmpl_request_t *rr = NULL;
946 
947  INFO_INDENT("{");
948 
949  /*
950  * Loop over the request references
951  */
952  list = tmpl_request(node->vpt);
953  while ((rr = tmpl_request_list_next(list, rr))) {
954  INFO_INDENT("ref %d", rr->request);
955  }
956  INFO_INDENT("list %s", tmpl_list_name(tmpl_list(node->vpt), "<INVALID>"));
957  if (tmpl_attr_tail_num(node->vpt) != NUM_UNSPEC) {
958  if (tmpl_attr_tail_num(node->vpt) == NUM_COUNT) {
959  INFO_INDENT("[#]");
960  } else if (tmpl_attr_tail_num(node->vpt) == NUM_ALL) {
961  INFO_INDENT("[*]");
962  } else {
963  INFO_INDENT("[%d]", tmpl_attr_tail_num(node->vpt));
964  }
965  }
966  INFO_INDENT("}");
967  }
968  } else if (tmpl_is_data(node->vpt)) {
969  INFO_INDENT("tmpl (%s) type %s", node->fmt, fr_type_to_str(tmpl_value_type(node->vpt)));
970  } else {
971  INFO_INDENT("tmpl (%s)", node->fmt);
972  }
973  }
974  break;
975 
976  case XLAT_VIRTUAL:
977  fr_assert(node->fmt != NULL);
978  INFO_INDENT("virtual (%s)", node->fmt);
979  break;
980 
982  fr_assert(node->fmt != NULL);
983  INFO_INDENT("virtual-unresolved (%s)", node->fmt);
984  break;
985 
986  case XLAT_FUNC:
987  fr_assert(node->call.func != NULL);
988  INFO_INDENT("xlat (%s)", node->call.func->name);
989  if (xlat_exp_head(node->call.args)) {
990  INFO_INDENT("{");
991  _xlat_debug_head(node->call.args, depth + 1);
992  INFO_INDENT("}");
993  }
994  break;
995 
997  INFO_INDENT("xlat-unresolved (%s)", node->fmt);
998  if (xlat_exp_head(node->call.args)) {
999  INFO_INDENT("{");
1000  _xlat_debug_head(node->call.args, depth + 1);
1001  INFO_INDENT("}");
1002  }
1003  break;
1004 
1005 #ifdef HAVE_REGEX
1006  case XLAT_REGEX:
1007  INFO_INDENT("regex-var -- %d", node->regex_index);
1008  break;
1009 #endif
1010 
1011  case XLAT_INVALID:
1012  DEBUG("XLAT-INVALID");
1013  break;
1014  }
1015 
1016  depth--;
1017  INFO_INDENT("}");
1018 }
1019 
1020 void xlat_debug(xlat_exp_t const *node)
1021 {
1022  _xlat_debug_node(node, 0);
1023 }
1024 
1025 static void _xlat_debug_head(xlat_exp_head_t const *head, int depth)
1026 {
1027  int i = 0;
1028 
1029  fr_assert(head != NULL);
1030 
1031  INFO_INDENT("head flags = %s %s %s",
1032  head->flags.needs_resolving ? "need_resolving," : "",
1033  head->flags.pure ? "pure" : "",
1034  head->flags.can_purify ? "can_purify" : "");
1035 
1036  depth++;
1037 
1038  xlat_exp_foreach(head, node) {
1039  INFO_INDENT("[%d] flags = %s %s %s ", i++,
1040  node->flags.needs_resolving ? "need_resolving" : "",
1041  node->flags.pure ? "pure" : "",
1042  node->flags.can_purify ? "can_purify" : "");
1043 
1044  _xlat_debug_node(node, depth);
1045  }
1046 }
1047 
1049 {
1050  _xlat_debug_head(head, 0);
1051 }
1052 
1054  fr_sbuff_escape_rules_t const *e_rules, char c)
1055 {
1056  ssize_t slen;
1057  size_t at_in = fr_sbuff_used_total(out);
1058  char close;
1059 
1060  if (!node) return 0;
1061 
1062  switch (node->type) {
1063  case XLAT_GROUP:
1065  xlat_print(out, node->group, fr_value_escape_by_quote[node->quote]);
1067 
1068  if (xlat_exp_next(head, node)) {
1069  if (c) FR_SBUFF_IN_CHAR_RETURN(out, c);
1070  FR_SBUFF_IN_CHAR_RETURN(out, ' '); /* Add ' ' between args */
1071  }
1072  goto done;
1073 
1074  case XLAT_BOX:
1075  /*
1076  * @todo - respect node->quote here, too. Which also means updating the parser.
1077  */
1078  if (node->quote == T_BARE_WORD) {
1079  FR_SBUFF_RETURN(fr_value_box_print, out, &node->data, e_rules);
1080  } else {
1081  FR_SBUFF_RETURN(fr_value_box_print_quoted, out, &node->data, node->quote);
1082  }
1083  goto done;
1084 
1085  case XLAT_TMPL:
1086  if (node->vpt->rules.cast != FR_TYPE_NULL) {
1088  FR_SBUFF_IN_STRCPY_RETURN(out, fr_type_to_str(node->vpt->rules.cast));
1090  }
1091 
1092  if (tmpl_is_data(node->vpt)) {
1093  FR_SBUFF_RETURN(fr_value_box_print_quoted, out, tmpl_value(node->vpt), node->vpt->quote);
1094  goto done;
1095  }
1096  if (tmpl_needs_resolving(node->vpt)) {
1097  if (node->vpt->quote != T_BARE_WORD) {
1099  }
1100  FR_SBUFF_IN_STRCPY_RETURN(out, node->vpt->name); /* @todo - escape it? */
1101  if (node->vpt->quote != T_BARE_WORD) {
1103  }
1104  goto done;
1105  }
1106 
1107  if (tmpl_contains_xlat(node->vpt)) { /* xlat and exec */
1108  if (node->vpt->quote == T_BARE_WORD) {
1109  if (node->flags.xlat) FR_SBUFF_IN_CHAR_RETURN(out, '%', '{');
1110  xlat_print(out, tmpl_xlat(node->vpt), NULL);
1111  if (node->flags.xlat) FR_SBUFF_IN_CHAR_RETURN(out, '}');
1112  } else {
1114  xlat_print(out, tmpl_xlat(node->vpt), fr_value_escape_by_quote[node->quote]);
1116  }
1117  goto done;
1118  }
1119 
1120  /*
1121  * Regexes need their own print routine, as they need to print the flags, too.
1122  *
1123  * Regexes should also "eat" their arguments into their instance data, so that we should
1124  * never try to print a regex.
1125  */
1126  fr_assert(!tmpl_contains_regex(node->vpt));
1127 
1128  // attr or list
1129  fr_assert(tmpl_is_attr(node->vpt));
1130  fr_assert(talloc_parent(node->vpt) == node);
1131  fr_assert(!node->flags.pure);
1132 
1133  /*
1134  * We prefer the name from the configuration file, otherwise when we get
1135  * &User-Name on input, the tmpl_printer will print the full path, as
1136  * &request[0].User-Name.
1137  */
1138  if (node->vpt->name[0] == '&') {
1140  goto done;
1141  }
1142  break;
1143 
1144  case XLAT_ONE_LETTER:
1145  FR_SBUFF_IN_CHAR_RETURN(out, '%', node->fmt[0]);
1146  goto done;
1147 
1148  case XLAT_FUNC:
1149  /*
1150  * We have a callback for printing this node, go
1151  * call it.
1152  */
1153  if (node->call.func->print) {
1154  slen = node->call.func->print(out, node, node->call.inst->data, e_rules);
1155  if (slen < 0) return slen;
1156  goto done;
1157  }
1158  break;
1159 
1160  default:
1161  break;
1162  }
1163 
1164  /*
1165  * Now print %(...) or %{...}
1166  */
1167  if ((node->type == XLAT_FUNC) || (node->type == XLAT_FUNC_UNRESOLVED)) {
1168  FR_SBUFF_IN_CHAR_RETURN(out, '%'); /* then the name */
1169  close = ')';
1170  } else {
1172  close = '}';
1173  }
1174 
1175  switch (node->type) {
1176  case XLAT_TMPL:
1177  slen = tmpl_attr_print(out, node->vpt, TMPL_ATTR_REF_PREFIX_NO);
1178  if (slen < 0) return slen;
1179  break;
1180 #ifdef HAVE_REGEX
1181  case XLAT_REGEX:
1182  FR_SBUFF_IN_SPRINTF_RETURN(out, "%i", node->regex_index);
1183  break;
1184 #endif
1185  case XLAT_VIRTUAL:
1186  FR_SBUFF_IN_BSTRCPY_BUFFER_RETURN(out, node->call.func->name);
1187  break;
1188 
1191  break;
1192 
1193  case XLAT_FUNC:
1194  FR_SBUFF_IN_BSTRCPY_BUFFER_RETURN(out, node->call.func->name);
1196 
1197  goto print_args;
1198 
1199  case XLAT_FUNC_UNRESOLVED:
1202 
1203  print_args:
1204  if (xlat_exp_head(node->call.args)) {
1205  xlat_exp_foreach(node->call.args, child) {
1206  slen = xlat_print_node(out, node->call.args, child, &xlat_escape, ',');
1207  if (slen < 0) return slen;
1208  }
1209  }
1210  break;
1211 
1212  case XLAT_INVALID:
1213  case XLAT_BOX:
1214  case XLAT_ONE_LETTER:
1215  case XLAT_GROUP:
1216  fr_assert_fail(NULL);
1217  break;
1218  }
1220 
1221 done:
1222  return fr_sbuff_used_total(out) - at_in;
1223 }
1224 
1225 /** Reconstitute an xlat expression from its constituent nodes
1226  *
1227  * @param[in] out Where to write the output string.
1228  * @param[in] head First node to print.
1229  * @param[in] e_rules Specifying how to escape literal values.
1230  */
1232 {
1233  ssize_t slen;
1234  size_t at_in = fr_sbuff_used_total(out);
1235 
1236  xlat_exp_foreach(head, node) {
1237  slen = xlat_print_node(out, head, node, e_rules, 0);
1238  if (slen < 0) return slen - (fr_sbuff_used_total(out) - at_in);
1239  }
1240 
1241  return fr_sbuff_used_total(out) - at_in;
1242 }
1243 
1244 
1245 /** Tokenize an xlat expansion into a series of XLAT_TYPE_CHILD arguments
1246  *
1247  * @param[in] ctx to allocate nodes in. Note: All nodes will be
1248  * allocated in the same ctx. This is to allow
1249  * manipulation by xlat instantiation functions
1250  * later.
1251  * @param[out] out the head of the xlat list / tree structure.
1252  * @param[in] in the format string to expand.
1253  * @param[in] xlat we're tokenizing arguments for.
1254  * @param[in] p_rules controlling how to parse the string outside of
1255  * any expansions.
1256  * @param[in] t_rules controlling how attribute references are parsed.
1257  * @param[in] comma whether the arguments are delimited by commas
1258  * @param[in] allow_attr allow attribute references as arguments
1259  * @return
1260  * - < 0 on error.
1261  * - >0 on success which is the number of characters parsed.
1262  */
1264  xlat_t const *xlat,
1265  fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules, bool comma, bool allow_attr)
1266 {
1267  int argc = 0;
1268  fr_sbuff_t our_in = FR_SBUFF(in);
1269  ssize_t slen;
1271  fr_sbuff_parse_rules_t const *our_p_rules; /* Bareword parse rules */
1272  fr_sbuff_parse_rules_t tmp_p_rules;
1274  xlat_arg_parser_t const *arg = NULL;
1275 
1276  if (xlat && xlat->args) {
1277  arg = xlat->args; /* Track the arguments as we parse */
1278  } else {
1279  static xlat_arg_parser_t default_arg = { .variadic = XLAT_ARG_VARIADIC_EMPTY_SQUASH };
1280  arg = &default_arg;
1281  }
1282 
1283  MEM(head = xlat_exp_head_alloc(ctx));
1284  if (p_rules && p_rules->terminals) {
1285  tmp_p_rules = (fr_sbuff_parse_rules_t){ /* Stack allocated due to CL scope */
1286  .terminals = fr_sbuff_terminals_amerge(NULL, p_rules->terminals,
1288  .escapes = (p_rules->escapes ? p_rules->escapes : value_parse_rules_bareword_quoted.escapes)
1289  };
1290  our_p_rules = &tmp_p_rules;
1291  } else {
1292  our_p_rules = &value_parse_rules_bareword_quoted;
1293  }
1294 
1295  /*
1296  * skip spaces at the beginning as we
1297  * don't want them to become a whitespace
1298  * literal.
1299  */
1300  fr_sbuff_adv_past_whitespace(in, SIZE_MAX, NULL);
1301  fr_sbuff_marker(&m, &our_in);
1302 
1303  while (fr_sbuff_extend(&our_in)) {
1304  xlat_exp_t *node = NULL;
1305  fr_token_t quote;
1306  char *fmt;
1307  size_t len;
1308 
1309  fr_sbuff_set(&m, &our_in); /* Record start of argument */
1310  argc++;
1311 
1312  /*
1313  * Whitespace isn't significant for comma-separated argvs
1314  */
1315  if (comma) fr_sbuff_adv_past_whitespace(&our_in, SIZE_MAX, NULL);
1316 
1318 
1319  /*
1320  * Alloc a new node to hold the child nodes
1321  * that make up the argument.
1322  */
1323  MEM(node = xlat_exp_alloc(head, XLAT_GROUP, NULL, 0));
1324  node->quote = quote;
1325 
1326  switch (quote) {
1327  /*
1328  * Barewords --may-contain=%{expansions}
1329  */
1330  case T_BARE_WORD:
1331  XLAT_DEBUG("ARGV bare word <-- %.*s", (int) fr_sbuff_remaining(&our_in), fr_sbuff_current(&our_in));
1332 
1333  /*
1334  * &User-Name is an attribute reference
1335  *
1336  * @todo - only the mono functions allow this automatic conversion.
1337  * The input args ones (e.g. immutable) take an input string, and parse the tmpl from that.
1338  *
1339  * We need to signal the tokenize / eval code that the parameter here is a tmpl, and not a string.
1340  *
1341  * Perhaps &"foo" can dynamically create the string, and then pass it to the the
1342  * tmpl tokenizer, and then pass the tmpl to the function. Which also means that
1343  * we need to be able to have a fr_value_box_t which holds a ptr to a tmpl. And
1344  * update the function arguments to say "we want a tmpl, not a string".
1345  */
1346  if (allow_attr && fr_sbuff_is_char(&our_in, '&')) {
1347  if (xlat_tokenize_attribute(node->group, &our_in, our_p_rules, t_rules, TMPL_ATTR_REF_PREFIX_YES) < 0) goto error;
1348  break;
1349  }
1350 
1351  if (xlat_tokenize_input(node->group, &our_in,
1352  our_p_rules, t_rules, arg->safe_for) < 0) {
1353  error:
1354  if (our_p_rules != &value_parse_rules_bareword_quoted) {
1355  talloc_const_free(our_p_rules->terminals);
1356  }
1357  talloc_free(head);
1358 
1359  FR_SBUFF_ERROR_RETURN(&our_in); /* error */
1360  }
1361  break;
1362 
1363  /*
1364  * "Double quoted strings may contain %{expansions}"
1365  */
1367  XLAT_DEBUG("ARGV double quotes <-- %.*s", (int) fr_sbuff_remaining(&our_in), fr_sbuff_current(&our_in));
1368 
1369  if (xlat_tokenize_input(node->group, &our_in,
1370  &value_parse_rules_double_quoted, t_rules, arg->safe_for) < 0) goto error;
1371  break;
1372 
1373  /*
1374  * 'Single quoted strings get parsed as literal strings'
1375  */
1377  {
1378  char *str;
1379  xlat_exp_t *child;
1380 
1381  XLAT_DEBUG("ARGV single quotes <-- %.*s", (int) fr_sbuff_remaining(&our_in), fr_sbuff_current(&our_in));
1382 
1383  child = xlat_exp_alloc(node->group, XLAT_BOX, NULL, 0);
1384  slen = fr_sbuff_out_aunescape_until(child, &str, &our_in, SIZE_MAX,
1387  if (slen < 0) goto error;
1388 
1390  fr_value_box_strdup(child, &child->data, NULL, str, false);
1391  fr_value_box_mark_safe_for(&child->data, arg->safe_for); /* Literal values are treated as implicitly safe */
1392  child->flags.constant = true;
1393  fr_assert(child->flags.pure);
1394  xlat_exp_insert_tail(node->group, child);
1395  }
1396  break;
1397 
1398  /*
1399  * `back quoted strings aren't supported`
1400  */
1401  case T_BACK_QUOTED_STRING:
1402  fr_strerror_const("Unexpected `...` string");
1403  goto error;
1404 
1405  default:
1406  fr_assert(0);
1407  break;
1408  }
1409 
1410  if ((quote != T_BARE_WORD) && !fr_sbuff_next_if_char(&our_in, fr_token_quote[quote])) { /* Quoting */
1411  fr_strerror_const("Unterminated string");
1412  fr_sbuff_set(&our_in, &m);
1413  goto error;
1414  }
1415 
1418 
1419  node->flags = node->group->flags;
1420 
1421  xlat_exp_insert_tail(head, node);
1422 
1423  /*
1424  * If we're not and the end of the string
1425  * and there's no whitespace between tokens
1426  * then error.
1427  */
1428  fr_sbuff_set(&m, &our_in);
1429  len = fr_sbuff_adv_past_whitespace(&our_in, SIZE_MAX, NULL);
1430 
1431  /*
1432  * Commas are in the list of terminals, but we skip over them,
1433  */
1434  if (comma) {
1435  fr_assert(p_rules && p_rules->terminals);
1436 
1437  if (fr_sbuff_next_if_char(&our_in, ',')) {
1438  continue;
1439  }
1440 
1441  if (fr_sbuff_is_char(&our_in, ')')) break;
1442 
1443  fr_strerror_printf("Unexpected text after argument %d", argc);
1444  goto error;
1445  }
1446 
1447  /*
1448  * Check to see if we have a terminal char
1449  */
1450  if ((p_rules && p_rules->terminals) && fr_sbuff_is_terminal(&our_in, p_rules->terminals)) break;
1451 
1452  /*
1453  * Otherwise, if we can extend, and found
1454  * no additional whitespace, it means two
1455  * arguments were smushed together.
1456  */
1457  if (fr_sbuff_extend(&our_in) && (len == 0)) {
1458  fr_strerror_const("Unexpected text after argument");
1459  goto error;
1460  }
1461 
1462  if (!arg->variadic) {
1463  arg++;
1464  if (arg->type == FR_TYPE_NULL) {
1465  fr_strerror_printf("Too many arguments, expected %u", argc - 1);
1466  goto error;
1467  }
1468  }
1469  }
1470 
1471  if (our_p_rules != &value_parse_rules_bareword_quoted) talloc_const_free(our_p_rules->terminals);
1472 
1474  *out = head;
1475 
1476  FR_SBUFF_SET_RETURN(in, &our_in);
1477 }
1478 
1479 /** Tokenize an xlat expansion
1480  *
1481  * @param[in] ctx to allocate dynamic buffers in.
1482  * @param[out] out the head of the xlat list / tree structure.
1483  * @param[in] in the format string to expand.
1484  * @param[in] p_rules controlling how the string containing the xlat
1485  * expansions should be parsed.
1486  * @param[in] t_rules controlling how attribute references are parsed.
1487  * Do NOT alter this function to take tmpl_rules_t
1488  * as this provides another value for literals_safe_for
1489  * and this gets very confusing.
1490  * @param[in] literals_safe_for the safe_for value to assign to any literals occurring at the
1491  * top level of the expansion.
1492  * @return
1493  * - >0 on success.
1494  * - 0 and *head == NULL - Parse failure on first char.
1495  * - 0 and *head != NULL - Zero length expansion
1496  * - < 0 the negative offset of the parse failure.
1497  */
1499  fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules,
1500  fr_value_box_safe_for_t literals_safe_for)
1501 {
1502  fr_sbuff_t our_in = FR_SBUFF(in);
1504 
1505 
1506  MEM(head = xlat_exp_head_alloc(ctx));
1507  fr_strerror_clear(); /* Clear error buffer */
1508 
1509  if (xlat_tokenize_input(head, &our_in, p_rules, t_rules, literals_safe_for) < 0) {
1510  talloc_free(head);
1511  FR_SBUFF_ERROR_RETURN(&our_in);
1512  }
1513 
1514  /*
1515  * Add nodes that need to be bootstrapped to
1516  * the registry.
1517  */
1518  if (xlat_finalize(head, t_rules->xlat.runtime_el) < 0) {
1519  talloc_free(head);
1520  return 0;
1521  }
1522 
1524  *out = head;
1525 
1526  FR_SBUFF_SET_RETURN(in, &our_in);
1527 }
1528 
1529 /** Check to see if the expansion consists entirely of value-box elements
1530  *
1531  * @param[in] head to check.
1532  * @return
1533  * - true if expansion contains only literal elements.
1534  * - false if expansion contains expandable elements.
1535  */
1537 {
1538  xlat_exp_foreach(head, node) {
1539  if (node->type != XLAT_BOX) return false;
1540  }
1541 
1542  return true;
1543 }
1544 
1545 /** Check to see if the expansion needs resolving
1546  *
1547  * @param[in] head to check.
1548  * @return
1549  * - true if expansion needs resolving
1550  * - false otherwise
1551  */
1553 {
1554  return head->flags.needs_resolving;
1555 }
1556 
1557 /** Convert an xlat node to an unescaped literal string and free the original node
1558  *
1559  * This is really "unparse the xlat nodes, and convert back to their original string".
1560  *
1561  * @param[in] ctx to allocate the new string in.
1562  * @param[out] str a duplicate of the node's fmt string.
1563  * @param[in,out] head to convert.
1564  * @return
1565  * - true the tree consists of a single value node which was converted.
1566  * - false the tree was more complex than a single literal, op was a noop.
1567  */
1568 bool xlat_to_string(TALLOC_CTX *ctx, char **str, xlat_exp_head_t **head)
1569 {
1570  fr_sbuff_t out;
1572  size_t len = 0;
1573 
1574  if (!*head) return false;
1575 
1576  /*
1577  * Instantiation functions may chop
1578  * up the node list into multiple
1579  * literals, so we need to walk the
1580  * list until we find a non-literal.
1581  */
1582  xlat_exp_foreach(*head, node) {
1583  if (node->type != XLAT_BOX) return false;
1584  len += talloc_array_length(node->fmt) - 1;
1585  }
1586 
1587  fr_sbuff_init_talloc(ctx, &out, &tctx, len, SIZE_MAX);
1588 
1589  xlat_exp_foreach(*head, node) {
1591  }
1592 
1593  *str = fr_sbuff_buff(&out); /* No need to trim, should be the correct length */
1594 
1595  return true;
1596 }
1597 
1598 /** Walk over an xlat tree recursively, resolving any unresolved functions or references
1599  *
1600  * @param[in,out] head of xlat tree to resolve.
1601  * @param[in] xr_rules Specifies rules to use for resolution passes after initial
1602  * tokenization.
1603  * @return
1604  * - 0 on success.
1605  * - -1 on failure.
1606  */
1608 {
1609  static xlat_res_rules_t xr_default;
1610  xlat_flags_t our_flags;
1611 
1612  if (!head->flags.needs_resolving) return 0; /* Already done */
1613 
1614  if (!xr_rules) xr_rules = &xr_default;
1615 
1616  our_flags = head->flags;
1617  our_flags.needs_resolving = false; /* We flip this if not all resolutions are successful */
1618  our_flags.pure = true; /* we flip this if the children are not pure */
1619 
1620  xlat_exp_foreach(head, node) {
1621  /*
1622  * This node and none of its children need resolving
1623  */
1624  if (!node->flags.needs_resolving) {
1625  xlat_flags_merge(&our_flags, &node->flags);
1626  continue;
1627  }
1628 
1629  switch (node->type) {
1630  case XLAT_GROUP:
1631  if (xlat_resolve(node->group, xr_rules) < 0) return -1;
1632  node->flags = node->group->flags;
1633  break;
1634 
1635  /*
1636  * An unresolved function.
1637  */
1638  case XLAT_FUNC_UNRESOLVED:
1639  /*
1640  * Try to find the function
1641  */
1642  node->call.func = xlat_func_find(node->fmt, talloc_array_length(node->fmt) - 1);
1643  if (!node->call.func) {
1644  /*
1645  * FIXME - Produce proper error with marker
1646  */
1647  if (!xr_rules->allow_unresolved) {
1648  fr_strerror_printf("Failed resolving function \"%pV\"",
1649  fr_box_strvalue_buffer(node->fmt));
1650  return -1;
1651  }
1652  break;
1653  }
1654 
1656  node->call.dict = xr_rules->tr_rules->dict_def;
1657 
1658  /*
1659  * Check input arguments of our freshly
1660  * resolved function
1661  */
1662  switch (node->call.func->input_type) {
1664  break;
1665 
1666  case XLAT_INPUT_MONO:
1667  if (node->call.input_type != XLAT_INPUT_MONO) {
1668  fr_strerror_const("Function should be called using %{func:arg} syntax");
1669  return -1;
1670 
1671  }
1672  if (xlat_validate_function_mono(node) < 0) return -1;
1673  break;
1674 
1675  case XLAT_INPUT_ARGS:
1676  if (node->call.input_type != XLAT_INPUT_ARGS) {
1677  fr_strerror_const("Function takes defined arguments and should "
1678  "be called using %(func:args) syntax");
1679  return -1;
1680  }
1681  if (xlat_validate_function_args(node) < 0) return -1;
1682  break;
1683  }
1684 
1685  /*
1686  * Add the freshly resolved function
1687  * to the bootstrap tree.
1688  */
1689  if (xlat_instance_register_func(node) < 0) return -1;
1690 
1691  /*
1692  * The function is now resolved, so we go through the normal process of resolving
1693  * its arguments, etc.
1694  */
1695  FALL_THROUGH;
1696 
1697  /*
1698  * A resolved function with unresolved args
1699  */
1700  case XLAT_FUNC:
1701  node->flags = node->call.func->flags;
1702 
1703  if (node->call.func->resolve) {
1704  void *inst = node->call.inst ? node->call.inst->data : NULL;
1705 
1706  if (node->call.func->resolve(node, inst, xr_rules) < 0) return -1;
1707  } else {
1708  if (xlat_resolve(node->call.args, xr_rules) < 0) return -1;
1709  }
1710 
1711  xlat_flags_merge(&node->flags, &node->call.args->flags);
1712  node->flags.can_purify = (node->call.func->flags.pure && node->call.args->flags.pure) | node->call.args->flags.can_purify;
1713  break;
1714 
1715  /*
1716  * This covers unresolved attributes as well as
1717  * unresolved functions.
1718  */
1720  {
1721  if (xlat_resolve_virtual_attribute(node, node->vpt) == 0) break;
1722 
1723  /*
1724  * Try and resolve (in-place) as an attribute
1725  */
1726  if ((tmpl_resolve(node->vpt, xr_rules->tr_rules) < 0) ||
1727  (node->vpt->type != TMPL_TYPE_ATTR)) {
1728  /*
1729  * FIXME - Produce proper error with marker
1730  */
1731  if (!xr_rules->allow_unresolved) {
1732  error_unresolved:
1733  if (node->quote == T_BARE_WORD) {
1734  fr_strerror_printf_push("Failed resolving expansion: %s",
1735  node->fmt);
1736  } else {
1737  fr_strerror_printf_push("Failed resolving expansion: %c%s%c",
1738  fr_token_quote[node->quote], node->fmt, fr_token_quote[node->quote]);
1739  }
1740  return -1;
1741  }
1742  break;
1743  }
1744 
1745  /*
1746  * Just need to flip the type as the tmpl should already have been fixed up
1747  */
1749 
1750  /*
1751  * Reset node flags. Attributes aren't pure, and don't need further resolving.
1752  */
1753  node->flags = (xlat_flags_t){ };
1754  }
1755  break;
1756 
1757  case XLAT_TMPL:
1758  /*
1759  * Double-quoted etc. strings may contain xlats, so we try to resolve them now.
1760  * Or, convert them to data.
1761  */
1762  if (tmpl_resolve(node->vpt, xr_rules->tr_rules) < 0) return -1;
1763 
1764  node->flags.needs_resolving = false;
1765  node->flags.pure = tmpl_is_data(node->vpt);
1766  break;
1767 
1768 
1769  default:
1770  fr_assert(0); /* Should not have been marked as unresolved */
1771  return -1;
1772  }
1773 
1774  if (node->flags.needs_resolving && !xr_rules->allow_unresolved) goto error_unresolved;
1775 
1776  xlat_flags_merge(&our_flags, &node->flags);
1777  }
1778 
1779  head->flags = our_flags;
1780 
1781  return 0;
1782 }
1783 
1784 
1785 /** Try to convert an xlat to a tmpl for efficiency
1786  *
1787  * @param ctx to allocate new tmpl_t in.
1788  * @param head to convert.
1789  * @return
1790  * - NULL if unable to convert (not necessarily error).
1791  * - A new #tmpl_t.
1792  */
1794 {
1795  tmpl_t *vpt;
1796  xlat_exp_t *node = xlat_exp_head(head);
1797 
1798  if (!node || (node->type != XLAT_TMPL) || !tmpl_is_attr(node->vpt)) return NULL;
1799 
1800  /*
1801  * Concat means something completely different as an attribute reference
1802  * Count isn't implemented.
1803  */
1804  if ((tmpl_attr_tail_num(node->vpt) == NUM_COUNT) || (tmpl_attr_tail_num(node->vpt) == NUM_ALL)) return NULL;
1805 
1806  vpt = tmpl_alloc(ctx, TMPL_TYPE_ATTR, T_BARE_WORD, node->fmt, talloc_array_length(node->fmt) - 1);
1807  if (!vpt) return NULL;
1808 
1809  tmpl_attr_copy(vpt, node->vpt);
1810 
1811  TMPL_VERIFY(vpt);
1812 
1813  return vpt;
1814 }
1815 
1816 /** Convert attr tmpl to an xlat for &attr[*]
1817  *
1818  * @param[in] ctx to allocate new expansion in.
1819  * @param[out] out Where to write new xlat node.
1820  * @param[in,out] vpt_p to convert to xlat expansion.
1821  * Will be set to NULL on completion
1822  * @return
1823  * - 0 on success.
1824  * - -1 on failure.
1825  */
1826 int xlat_from_tmpl_attr(TALLOC_CTX *ctx, xlat_exp_head_t **out, tmpl_t **vpt_p)
1827 {
1828  xlat_exp_t *node;
1829  xlat_t *func;
1830  tmpl_t *vpt = *vpt_p;
1832 
1833  if (!tmpl_is_attr(vpt) && !tmpl_is_attr_unresolved(vpt)) return 0;
1834 
1835  MEM(head = xlat_exp_head_alloc(ctx));
1836 
1837  /*
1838  * If it's a single attribute reference
1839  * see if it's actually a virtual attribute.
1840  */
1841  if ((tmpl_attr_num_elements(vpt) == 1) ||
1842  (((tmpl_attr_list_head(tmpl_attr(vpt))->da) == request_attr_request) && tmpl_attr_num_elements(vpt) == 2)) {
1845  if (!func) {
1846  node = xlat_exp_alloc(head, XLAT_VIRTUAL_UNRESOLVED, vpt->name, vpt->len);
1847 
1848  /*
1849  * FIXME - Need a tmpl_copy function to
1850  * the assignment of the tmpl to the new
1851  * xlat expression
1852  */
1853  node->vpt = talloc_move(node, vpt_p);
1854  node->flags = (xlat_flags_t) { .needs_resolving = true };
1855  goto done;
1856  }
1857 
1858  node = xlat_exp_alloc(head, XLAT_VIRTUAL, vpt->name, vpt->len);
1859  node->vpt = talloc_move(node, vpt_p);
1860  node->call.func = func;
1861  node->flags = func->flags;
1862  goto done;
1863  }
1864  }
1865 
1866  node = xlat_exp_alloc(head, XLAT_TMPL, vpt->name, vpt->len);
1867  node->vpt = talloc_move(node, vpt_p);
1868  fr_assert(!node->flags.pure);
1869 
1870 done:
1871  xlat_exp_insert_tail(head, node);
1872  *out = head;
1873 
1874  return 0;
1875 }
int n
Definition: acutest.h:577
static int const char * fmt
Definition: acutest.h:573
#define RCSID(id)
Definition: build.h:444
#define L(_str)
Helper for initialising arrays of string literals.
Definition: build.h:207
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition: build.h:320
#define NUM_ELEMENTS(_t)
Definition: build.h:335
#define fr_assert_fail(_msg,...)
Calls panic_action ifndef NDEBUG, else logs error.
Definition: debug.h:214
#define DEBUG(fmt,...)
Definition: dhcpclient.c:39
static fr_slen_t err
Definition: dict.h:645
static fr_slen_t in
Definition: dict.h:645
Wrapper around libkqueue to make managing events easier.
static const bool escapes[UINT8_MAX+1]
Definition: util.c:38
talloc_free(reap)
typedef FR_DLIST_HEAD(map_list) map_list_t
Given these are used in so many places, it's more friendly to have a proper type.
tmpl_attr_prefix_t
Definition: merged_model.c:228
@ TMPL_ATTR_REF_PREFIX_NO
Attribute refs have no '&' prefix.
Definition: merged_model.c:230
@ TMPL_ATTR_REF_PREFIX_YES
Attribute refs must have '&' prefix.
Definition: merged_model.c:229
@ FR_TYPE_STRING
String of printable characters.
Definition: merged_model.c:83
@ FR_TYPE_NULL
Invalid (uninitialised) attribute type.
Definition: merged_model.c:81
@ FR_TYPE_VOID
User data.
Definition: merged_model.c:127
long int ssize_t
Definition: merged_model.c:24
unsigned char uint8_t
Definition: merged_model.c:30
ssize_t fr_slen_t
Definition: merged_model.c:35
#define UINT8_MAX
Definition: merged_model.c:32
fr_sbuff_parse_error_t
Definition: merged_model.c:45
@ FR_SBUFF_PARSE_OK
No error.
Definition: merged_model.c:46
static uint8_t depth(fr_minmax_heap_index_t i)
Definition: minmax_heap.c:83
static void print_args(fr_log_t const *log, char const *file, int line, size_t arg_cnt, uint8_t const *argv, uint8_t const *start, uint8_t const *end)
Definition: base.c:388
static bool done
Definition: radclient.c:80
fr_dict_attr_t const * request_attr_request
Definition: request.c:41
static char const * name
size_t fr_sbuff_adv_past_allowed(fr_sbuff_t *sbuff, size_t len, bool const allowed[static UINT8_MAX+1], fr_sbuff_term_t const *tt)
Wind position past characters in the allowed set.
Definition: sbuff.c:1736
fr_sbuff_term_t * fr_sbuff_terminals_amerge(TALLOC_CTX *ctx, fr_sbuff_term_t const *a, fr_sbuff_term_t const *b)
Merge two sets of terminal strings.
Definition: sbuff.c:613
bool const sbuff_char_class_uint[UINT8_MAX+1]
Definition: sbuff.c:52
bool fr_sbuff_is_terminal(fr_sbuff_t *in, fr_sbuff_term_t const *tt)
Efficient terminal string search.
Definition: sbuff.c:2111
size_t fr_sbuff_adv_until(fr_sbuff_t *sbuff, size_t len, fr_sbuff_term_t const *tt, char escape_chr)
Wind position until we hit a character in the terminal set.
Definition: sbuff.c:1811
ssize_t fr_sbuff_in_bstrcpy_buffer(fr_sbuff_t *sbuff, char const *str)
Copy bytes into the sbuff up to the first \0.
Definition: sbuff.c:1467
bool fr_sbuff_next_if_char(fr_sbuff_t *sbuff, char c)
Return true if the current char matches, and if it does, advance.
Definition: sbuff.c:2047
bool const sbuff_char_alpha_num[UINT8_MAX+1]
Definition: sbuff.c:87
#define fr_sbuff_out_by_longest_prefix(_match_len, _out, _table, _sbuff, _def)
#define fr_sbuff_adv_past_str_literal(_sbuff, _needle)
#define FR_SBUFF_IN_CHAR_RETURN(_sbuff,...)
#define fr_sbuff_set(_dst, _src)
#define fr_sbuff_is_alnum(_sbuff_or_marker)
#define fr_sbuff_adv_past_whitespace(_sbuff, _len, _tt)
#define fr_sbuff_current(_sbuff_or_marker)
#define fr_sbuff_char(_sbuff_or_marker, _eob)
#define FR_SBUFF_TERMS(...)
Initialise a terminal structure with a list of sorted strings.
Definition: sbuff.h:167
char const * name
Name for rule set to aid we debugging.
Definition: sbuff.h:177
#define FR_SBUFF_IN_STRCPY_LITERAL_RETURN(_sbuff, _str)
#define fr_sbuff_extend(_sbuff_or_marker)
#define fr_sbuff_buff(_sbuff_or_marker)
#define fr_sbuff_used_total(_sbuff_or_marker)
#define SBUFF_CHAR_CLASS_ALPHA_NUM
#define FR_SBUFF_RETURN(_func, _sbuff,...)
#define fr_sbuff_is_char(_sbuff_or_marker, _c)
#define FR_SBUFF_ERROR_RETURN(_sbuff_or_marker)
#define FR_SBUFF_IN_SPRINTF_RETURN(...)
#define SBUFF_CHAR_UNPRINTABLES_EXTENDED
#define FR_SBUFF(_sbuff_or_marker)
#define fr_sbuff_out(_err, _out, _in)
#define fr_sbuff_remaining(_sbuff_or_marker)
#define SBUFF_CHAR_UNPRINTABLES_LOW
#define fr_sbuff_behind(_sbuff_or_marker)
#define FR_SBUFF_TERM(_str)
Initialise a terminal structure with a single string.
Definition: sbuff.h:155
#define FR_SBUFF_IN_STRCPY_RETURN(...)
#define FR_SBUFF_IN_BSTRCPY_BUFFER_RETURN(...)
Set of terminal elements.
Definition: merged_model.c:161
Talloc sbuff extension structure.
Definition: sbuff.h:114
Set of parsing rules for *unescape_until functions.
Definition: merged_model.c:163
static int16_t tmpl_attr_tail_num(tmpl_t const *vpt)
Return the last attribute reference's attribute number.
Definition: tmpl.h:880
#define tmpl_contains_xlat(vpt)
Definition: tmpl.h:231
#define TMPL_VERIFY(_vpt)
Definition: tmpl.h:953
#define tmpl_is_attr_unresolved(vpt)
Definition: tmpl.h:224
void tmpl_set_xlat(tmpl_t *vpt, xlat_exp_head_t *xlat)
Change the default dictionary in the tmpl's resolution rules.
static fr_slen_t vpt
Definition: tmpl.h:1260
int tmpl_resolve(tmpl_t *vpt, tmpl_res_rules_t const *tr_rules))
Attempt to resolve functions and attributes in xlats and attribute references.
#define tmpl_value(_tmpl)
Definition: tmpl.h:932
static char const * tmpl_list_name(fr_dict_attr_t const *list, char const *def)
Return the name of a tmpl list or def if list not provided.
Definition: tmpl.h:910
#define tmpl_contains_regex(vpt)
Definition: tmpl.h:230
static fr_dict_attr_t const * tmpl_attr_tail_da(tmpl_t const *vpt)
Return the last attribute reference da.
Definition: tmpl.h:796
#define tmpl_is_attr(vpt)
Definition: tmpl.h:213
#define NUM_ALL
Definition: tmpl.h:400
fr_dict_attr_t const * enumv
Enumeration attribute used to resolve enum values.
Definition: tmpl.h:347
#define tmpl_xlat(_tmpl)
Definition: tmpl.h:925
@ TMPL_TYPE_ATTR
Reference to one or more attributes.
Definition: tmpl.h:146
@ TMPL_TYPE_XLAT
Pre-parsed xlat expansion.
Definition: tmpl.h:150
#define NUM_COUNT
Definition: tmpl.h:401
tmpl_xlat_rules_t xlat
Rules/data for parsing xlats.
Definition: tmpl.h:345
bool at_runtime
Produce an ephemeral/runtime tmpl.
Definition: tmpl.h:353
static fr_slen_t rql fr_slen_t tmpl_attr_print(fr_sbuff_t *out, tmpl_t const *vpt, tmpl_attr_prefix_t ar_prefix))
Print an attribute or list tmpl_t to a string.
ssize_t tmpl_afrom_attr_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err, tmpl_t **out, fr_sbuff_t *name, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules))
Parse a string into a TMPL_TYPE_ATTR_* type tmpl_t.
#define tmpl_is_data(vpt)
Definition: tmpl.h:211
void tmpl_set_name_shallow(tmpl_t *vpt, fr_token_t quote, char const *name, ssize_t len)
Set the name on a pre-initialised tmpl.
fr_dict_t const * dict_def
Alternative default dictionary to use if vpt->rules->dict_def is NULL.
Definition: tmpl.h:374
#define NUM_UNSPEC
Definition: tmpl.h:399
static size_t tmpl_attr_num_elements(tmpl_t const *vpt)
The number of attribute references contained within a tmpl.
Definition: tmpl.h:891
#define tmpl_value_type(_tmpl)
Definition: tmpl.h:934
#define tmpl_attr(_tmpl)
Definition: tmpl.h:649
tmpl_attr_error_t
Definition: tmpl.h:996
@ TMPL_ATTR_ERROR_MISSING_TERMINATOR
Unexpected text found after attribute reference.
Definition: tmpl.h:1018
fr_type_t cast
Whether there was an explicit cast.
Definition: tmpl.h:349
tmpl_attr_rules_t attr
Rules/data for parsing attribute references.
Definition: tmpl.h:344
int tmpl_attr_copy(tmpl_t *dst, tmpl_t const *src)
Copy a list of attribute and request references from one tmpl to another.
static char const * tmpl_attr_tail_unresolved(tmpl_t const *vpt)
Return the last attribute reference unresolved da.
Definition: tmpl.h:864
static fr_dict_attr_t const * tmpl_list(tmpl_t const *vpt)
Definition: tmpl.h:899
tmpl_t * tmpl_alloc(TALLOC_CTX *ctx, tmpl_type_t type, fr_token_t quote, char const *name, ssize_t len)
Create a new heap allocated tmpl_t.
fr_event_list_t * runtime_el
The eventlist to use for runtime instantiation of xlats.
Definition: tmpl.h:333
#define tmpl_needs_resolving(vpt)
Definition: tmpl.h:228
Optional arguments passed to vp_tmpl functions.
Definition: tmpl.h:341
if(!subtype_vp) goto fail
fr_assert(0)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
eap_aka_sim_process_conf_t * inst
uint8_t allow_unresolved
Allow attributes that look valid but were not found in the dictionaries.
Definition: tmpl.h:319
fr_dict_t const * dict_def
Default dictionary to use with unqualified attribute references.
Definition: tmpl.h:285
tmpl_attr_prefix_t prefix
Whether the attribute reference requires a prefix.
Definition: tmpl.h:310
Define manipulation functions for the attribute reference list.
Definition: tmpl.h:468
tmpl_request_ref_t _CONST request
Definition: tmpl.h:472
An element in a lexicographically sorted array of name to num mappings.
Definition: table.h:45
char * talloc_typed_strdup(TALLOC_CTX *ctx, char const *p)
Call talloc_strdup, setting the type on the new chunk correctly.
Definition: talloc.c:333
char * talloc_bstrndup(TALLOC_CTX *ctx, char const *in, size_t inlen)
Binary safe strndup function.
Definition: talloc.c:452
static int talloc_const_free(void const *ptr)
Free const'd memory.
Definition: talloc.h:212
const char fr_token_quote[T_TOKEN_LAST]
Convert tokens back to a quoting character.
Definition: token.c:156
enum fr_token fr_token_t
@ T_SINGLE_QUOTED_STRING
Definition: token.h:122
@ T_BARE_WORD
Definition: token.h:120
@ T_BACK_QUOTED_STRING
Definition: token.h:123
@ T_DOUBLE_QUOTED_STRING
Definition: token.h:121
close(uq->fd)
tmpl_res_rules_t const * tr_rules
tmpl resolution rules.
Definition: xlat.h:161
fr_type_t type
Type to cast argument to.
Definition: xlat.h:153
#define XLAT_HEAD_VERIFY(_head)
Definition: xlat.h:454
int xlat_instance_register_func(xlat_exp_t *node)
Callback for creating "permanent" instance data for a xlat_exp_t.
Definition: xlat_inst.c:590
bool required
Argument must be present, and non-empty.
Definition: xlat.h:146
bool allow_unresolved
If false, all resolution steps must be completed.
Definition: xlat.h:162
bool xlat
it's an xlat wrapper
Definition: xlat.h:117
@ XLAT_ARG_VARIADIC_EMPTY_SQUASH
Empty argument groups are removed.
Definition: xlat.h:136
static fr_slen_t head
Definition: xlat.h:408
xlat_arg_parser_variadic_t variadic
All additional boxes should be processed using this definition.
Definition: xlat.h:149
fr_value_box_safe_for_t safe_for
Escaped value to set for boxes processed by this escape function.
Definition: xlat.h:155
bool can_purify
if the xlat has a pure function with pure arguments.
Definition: xlat.h:114
@ XLAT_INPUT_MONO
Ingests a single argument.
Definition: xlat.h:47
@ XLAT_INPUT_ARGS
Ingests a number of arguments.
Definition: xlat.h:48
@ XLAT_INPUT_UNPROCESSED
No input argument processing.
Definition: xlat.h:46
bool pure
has no external side effects, true for BOX, LITERAL, and some functions
Definition: xlat.h:113
bool needs_resolving
Needs pass2 resolution.
Definition: xlat.h:112
int xlat_finalize(xlat_exp_head_t *head, fr_event_list_t *runtime_el)
Bootstrap static xlats, or instantiate ephemeral ones.
Definition: xlat_inst.c:690
bool constant
xlat is just tmpl_attr_tail_data, or XLAT_BOX
Definition: xlat.h:116
fr_slen_t xlat_tokenize_expression(TALLOC_CTX *ctx, xlat_exp_head_t **head, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules)
Definition: xlat_expr.c:3017
Definition for a single argument consumend by an xlat function.
Definition: xlat.h:145
Flags that control resolution and evaluation.
Definition: xlat.h:111
void fr_strerror_clear(void)
Clears all pending messages from the talloc pools.
Definition: strerror.c:577
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition: strerror.h:64
#define fr_strerror_printf_push(_fmt,...)
Add a message to an existing stack of messages at the tail.
Definition: strerror.h:84
#define fr_strerror_const(_msg)
Definition: strerror.h:223
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
Definition: types.h:433
ssize_t fr_value_box_from_str(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, char const *in, size_t inlen, fr_sbuff_unescape_rules_t const *erules, bool tainted)
Definition: value.c:5264
ssize_t fr_value_box_print(fr_sbuff_t *out, fr_value_box_t const *data, fr_sbuff_escape_rules_t const *e_rules)
Print one boxed value to a string.
Definition: value.c:5301
int fr_value_box_copy(TALLOC_CTX *ctx, fr_value_box_t *dst, const fr_value_box_t *src)
Copy value data verbatim duplicating any buffers.
Definition: value.c:3689
fr_sbuff_parse_rules_t const value_parse_rules_single_quoted
Definition: value.c:553
fr_sbuff_escape_rules_t * fr_value_escape_by_quote[T_TOKEN_LAST]
Definition: value.c:441
int fr_value_box_strdup(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, bool tainted)
Copy a nul terminated string to a fr_value_box_t.
Definition: value.c:3876
ssize_t fr_value_box_print_quoted(fr_sbuff_t *out, fr_value_box_t const *data, fr_token_t quote)
Print one boxed value to a string with quotes (where needed)
Definition: value.c:5489
FR_SBUFF_SET_RETURN(sbuff, &our_sbuff)
fr_sbuff_parse_rules_t const value_parse_rules_bareword_quoted
Definition: value.c:524
fr_sbuff_parse_rules_t const value_parse_rules_double_quoted
Definition: value.c:547
void fr_value_box_clear(fr_value_box_t *data)
Clear/free any existing value and metadata.
Definition: value.c:3672
Boxed value structures and functions to manipulate them.
#define fr_value_box_mark_safe_for(_box, _safe_for)
Definition: value.h:1035
#define fr_box_strvalue_buffer(_val)
Definition: value.h:282
#define fr_box_strvalue_len(_val, _len)
Definition: value.h:279
#define fr_value_box_init_null(_vb)
Initialise an empty/null box that will be filled later.
Definition: value.h:580
uintptr_t fr_value_box_safe_for_t
Escaping that's been applied to a value box.
Definition: value.h:155
static size_t char ** out
Definition: value.h:984
void xlat_exp_set_name(xlat_exp_t *node, char const *fmt, size_t len)
Set the format string for an xlat node.
Definition: xlat_alloc.c:191
void xlat_exp_set_name_buffer_shallow(xlat_exp_t *node, char const *fmt)
Set the format string for an xlat node from a pre-existing buffer.
Definition: xlat_alloc.c:213
xlat_t * xlat_func_find(char const *in, ssize_t inlen)
Definition: xlat_func.c:56
static xlat_exp_t * xlat_exp_next(xlat_exp_head_t const *head, xlat_exp_t const *node)
Definition: xlat_priv.h:239
#define xlat_exp_head_alloc(_ctx)
Definition: xlat_priv.h:266
xlat_flags_t flags
Flags that control resolution and evaluation.
Definition: xlat_priv.h:153
#define xlat_exp_alloc_null(_ctx)
Definition: xlat_priv.h:272
xlat_flags_t flags
Flags that control resolution and evaluation.
Definition: xlat_priv.h:186
fr_token_t quote
Type of quoting around XLAT_GROUP types.
Definition: xlat_priv.h:151
@ XLAT_ONE_LETTER
Special "one-letter" expansion.
Definition: xlat_priv.h:104
@ XLAT_BOX
fr_value_box_t
Definition: xlat_priv.h:103
@ XLAT_TMPL
xlat attribute
Definition: xlat_priv.h:109
@ XLAT_VIRTUAL_UNRESOLVED
virtual attribute needs resolution during pass2.
Definition: xlat_priv.h:108
@ XLAT_FUNC
xlat module
Definition: xlat_priv.h:105
@ XLAT_VIRTUAL
virtual attribute
Definition: xlat_priv.h:107
@ XLAT_GROUP
encapsulated string of xlats
Definition: xlat_priv.h:113
@ XLAT_FUNC_UNRESOLVED
func needs resolution during pass2.
Definition: xlat_priv.h:106
@ XLAT_INVALID
Bad expansion.
Definition: xlat_priv.h:102
xlat_arg_parser_t const * args
Definition of args consumed.
Definition: xlat_priv.h:89
static void xlat_flags_merge(xlat_flags_t *parent, xlat_flags_t const *child)
Merge flags from child to parent.
Definition: xlat_priv.h:223
#define xlat_exp_set_type(_node, _type)
Definition: xlat_priv.h:269
static xlat_exp_t * xlat_exp_head(xlat_exp_head_t const *head)
Definition: xlat_priv.h:203
xlat_input_type_t input_type
Type of input used.
Definition: xlat_priv.h:88
char const *_CONST fmt
The original format string (a talloced buffer).
Definition: xlat_priv.h:150
xlat_type_t _CONST type
type of this expansion.
Definition: xlat_priv.h:154
#define xlat_exp_alloc(_ctx, _type, _in, _inlen)
Definition: xlat_priv.h:275
xlat_flags_t flags
various flags
Definition: xlat_priv.h:86
#define xlat_exp_foreach(_list_head, _iter)
Iterate over the contents of a list, only one level.
Definition: xlat_priv.h:216
static int xlat_exp_insert_tail(xlat_exp_head_t *head, xlat_exp_t *node)
Definition: xlat_priv.h:231
An xlat expansion node.
Definition: xlat_priv.h:147
bool xlat_needs_resolving(xlat_exp_head_t const *head)
Check to see if the expansion needs resolving.
#define INFO_INDENT(_fmt,...)
fr_slen_t xlat_tokenize(TALLOC_CTX *ctx, xlat_exp_head_t **out, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules, fr_value_box_safe_for_t literals_safe_for)
Tokenize an xlat expansion.
bool xlat_is_literal(xlat_exp_head_t const *head)
Check to see if the expansion consists entirely of value-box elements.
bool const xlat_func_chars[UINT8_MAX+1]
int xlat_tokenize_expansion(xlat_exp_head_t *head, fr_sbuff_t *in, tmpl_rules_t const *t_rules)
static int xlat_resolve_virtual_attribute(xlat_exp_t *node, tmpl_t *vpt)
int xlat_validate_function_args(xlat_exp_t *node)
static fr_table_num_sorted_t const xlat_quote_table[]
void xlat_debug_head(xlat_exp_head_t const *head)
static void _xlat_debug_head(xlat_exp_head_t const *head, int depth)
static size_t xlat_quote_table_len
static int xlat_validate_function_arg(xlat_arg_parser_t const *arg_p, xlat_exp_t *arg)
ssize_t xlat_print(fr_sbuff_t *out, xlat_exp_head_t const *head, fr_sbuff_escape_rules_t const *e_rules)
Reconstitute an xlat expression from its constituent nodes.
#define XLAT_HEXDUMP(...)
Definition: xlat_tokenize.c:44
static fr_sbuff_parse_rules_t const xlat_function_arg_rules
Parse rules for literal values inside of an expansion.
#define XLAT_DEBUG(...)
Definition: xlat_tokenize.c:43
bool xlat_to_string(TALLOC_CTX *ctx, char **str, xlat_exp_head_t **head)
Convert an xlat node to an unescaped literal string and free the original node.
tmpl_t * xlat_to_tmpl_attr(TALLOC_CTX *ctx, xlat_exp_head_t *head)
Try to convert an xlat to a tmpl for efficiency.
static void _xlat_debug_node(xlat_exp_t const *node, int depth)
int xlat_from_tmpl_attr(TALLOC_CTX *ctx, xlat_exp_head_t **out, tmpl_t **vpt_p)
Convert attr tmpl to an xlat for &attr[*].
static fr_sbuff_unescape_rules_t const xlat_unescape
These rules apply to literal values and function arguments inside of an expansion.
Definition: xlat_tokenize.c:50
void xlat_debug(xlat_exp_t const *node)
ssize_t xlat_print_node(fr_sbuff_t *out, xlat_exp_head_t const *head, xlat_exp_t const *node, fr_sbuff_escape_rules_t const *e_rules, char c)
static int xlat_tokenize_input(xlat_exp_head_t *head, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules, fr_value_box_safe_for_t safe_for)
Parse an xlat string i.e.
static bool const tmpl_attr_allowed_chars[UINT8_MAX+1]
static fr_sbuff_escape_rules_t const xlat_escape
These rules apply to literal values and function arguments inside of an expansion.
Definition: xlat_tokenize.c:72
int xlat_validate_function_mono(xlat_exp_t *node)
fr_slen_t xlat_tokenize_argv(TALLOC_CTX *ctx, xlat_exp_head_t **out, fr_sbuff_t *in, xlat_t const *xlat, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules, bool comma, bool allow_attr)
Tokenize an xlat expansion into a series of XLAT_TYPE_CHILD arguments.
int xlat_resolve(xlat_exp_head_t *head, xlat_res_rules_t const *xr_rules)
Walk over an xlat tree recursively, resolving any unresolved functions or references.
static int xlat_tokenize_attribute(xlat_exp_head_t *head, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules, tmpl_attr_prefix_t attr_prefix)
Parse an attribute ref or a virtual attribute.
static int xlat_tokenize_function_args(xlat_exp_head_t *head, fr_sbuff_t *in, tmpl_rules_t const *t_rules)
Parse an xlat function and its child argument.