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