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