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