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