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: 405c45ef1ab4506cf3663998e89626b3da5944bd $
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: 405c45ef1ab4506cf3663998e89626b3da5944bd $")
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.
98 *
99 * The caller sets the literal parse rules for outside of expansions when they
100 * call xlat_tokenize.
101 */
102static fr_sbuff_parse_rules_t const xlat_function_arg_rules = {
103 .escapes = &xlat_unescape,
104 .terminals = &FR_SBUFF_TERMS( /* These get merged with other literal terminals */
105 L(")"),
106 L(","),
107 ),
108};
109
110#ifdef HAVE_REGEX
111/** Parse an xlat reference
112 *
113 * Allows access to a subcapture groups
114 * @verbatim %{<num>} @endverbatim
115 */
117{
118 uint8_t num;
119 xlat_exp_t *node;
121
122 XLAT_DEBUG("REGEX <-- %.*s", (int) fr_sbuff_remaining(in), fr_sbuff_current(in));
123
124 /*
125 * Not a number, ignore it.
126 */
127 (void) fr_sbuff_out(&err, &num, in);
128 if (err != FR_SBUFF_PARSE_OK) return 0;
129
130 /*
131 * Not %{\d+}, ignore it.
132 */
133 if (!fr_sbuff_is_char(in, '}')) return 0;
134
135 /*
136 * It is a regex ref, but it has to be a valid one.
137 */
138 if (num > REQUEST_MAX_REGEX) {
139 fr_strerror_printf("Invalid regex reference. Must be in range 0-%d", REQUEST_MAX_REGEX);
140 fr_sbuff_set(in, m_s);
141 return -1;
142 }
143
144 MEM(node = xlat_exp_alloc(head, XLAT_REGEX, fr_sbuff_current(m_s), fr_sbuff_behind(m_s)));
145 node->regex_index = num;
146
147 *out = node;
148
149 (void) fr_sbuff_advance(in, 1); /* must be '}' */
150 return 1;
151}
152#endif
153
154bool const xlat_func_chars[UINT8_MAX + 1] = {
156 ['.'] = true, ['-'] = true, ['_'] = true,
157};
158
159
160/** Normalize an xlat which contains a tmpl.
161 *
162 * Constant data is turned into XLAT_BOX, and some other thingies are done.
163 */
165{
166 tmpl_t *vpt = node->vpt;
167
168 /*
169 * Any casting, etc. has to be taken care of in the xlat expression parser, and not here.
170 */
172
173 if (tmpl_is_attr_unresolved(node->vpt)) {
174 return 0;
175 }
176
177 /*
178 * Add in unknown attributes, by defining them in the local dictionary.
179 */
180 if (tmpl_is_attr(vpt)) {
181 if (tmpl_attr_unknown_add(vpt) < 0) {
182 fr_strerror_printf("Failed defining attribute %s", tmpl_attr_tail_da(vpt)->name);
183 return -1;
184 }
185
186 return 0;
187 }
188
189 if (!tmpl_contains_data(vpt)) {
191 return 0;
192 }
193
194 if (tmpl_is_data_unresolved(vpt) && (tmpl_resolve(vpt, NULL) < 0)) return -1;
195
196 /*
197 * Hoist data to an XLAT_BOX instead of an XLAT_TMPL
198 */
200
201 /*
202 * Print "true" and "false" instead of "yes" and "no".
203 */
206 }
207
208 /*
209 * Convert the XLAT_TMPL to XLAT_BOX
210 */
212
213 return 0;
214}
215
216/** Validate and sanity check function arguments.
217 *
218 */
219static int xlat_validate_function_arg(xlat_arg_parser_t const *arg_p, xlat_exp_t *arg, int argc)
220{
221 xlat_exp_t *node;
222
223 fr_assert(arg->type == XLAT_GROUP);
224
225 /*
226 * "is_argv" does dual duty. One, it causes xlat_print() to print spaces in between arguments.
227 *
228 * Two, it is checked by xlat_frame_eval_repeat(), which then does NOT concatenate strings in
229 * place. Instead, it just passes the strings though to xlat_process_arg_list(). Which calls
230 * xlat_arg_stringify(), and that does the escaping and final concatenation.
231 */
232 arg->group->is_argv = (arg_p->func != NULL) | arg_p->will_escape;
233
234 node = xlat_exp_head(arg->group);
235
236 if (!node) {
237 if (!arg_p->required) return 0;
238
239 fr_strerror_const("Missing argument");
240 return -1;
241 }
242
243 /*
244 * The caller doesn't care about the type, we don't do any validation.
245 */
246 if (arg_p->type == FR_TYPE_VOID) return 0;
247
248 /*
249 * A cursor should be (for now) a named string.
250 */
251 if (arg_p->type == FR_TYPE_PAIR_CURSOR) {
252 if (node->type == XLAT_BOX) {
253 check_box:
254 if (node->data.type != FR_TYPE_STRING) {
255 fr_strerror_printf("Cursor must be a string attribute reference, not %s",
256 fr_type_to_str(node->data.type));
257 return -1;
258 }
259
260 return 0;
261 }
262
263 /*
264 * The expression parser should not allow anything else here.
265 */
266 fr_assert((node->type == XLAT_TMPL) || (node->type == XLAT_GROUP));
267
268 /*
269 * Func, etc.
270 */
271 if (node->type != XLAT_TMPL) return 0;
272
273 if (tmpl_rules_cast(node->vpt) != FR_TYPE_NULL) {
274 fr_strerror_const("Cursor cannot have cast");
275 return -1;
276 }
277
278 if (xlat_tmpl_normalize(node) < 0) return -1;
279
280 if (node->type == XLAT_BOX) goto check_box;
281
282 if (!tmpl_is_attr(node->vpt)) {
283 fr_strerror_printf("Invalid argument - expected attribute reference");
284 return -1;
285 }
286
287 /*
288 * Bare attribute references are allowed, but are marked up as "return a cursor to this
289 * thing, don't return a value".
290 */
291 arg->group->cursor = true;
292 return 0;
293 }
294
295 /*
296 * The argument is either ONE tmpl / value-box, OR is an
297 * xlat group which contains a double-quoted string.
298 */
299 fr_assert(fr_dlist_num_elements(&arg->group->dlist) == 1);
300
301 /*
302 * Do at least somewhat of a pass of normalizing the nodes, even if there are more than one.
303 */
304 if (node->type == XLAT_TMPL) {
305 return xlat_tmpl_normalize(node);
306 }
307
308 /*
309 * @todo - probably move the double-quoted string "node->flags.constant" check here, to more
310 * clearly separate parsing from normalization.
311 */
312
313 if (node->type != XLAT_BOX) {
314 return 0;
315 }
316
317 /*
318 * If it's the correct data type, then we don't need to do anything.
319 */
320 if (arg_p->type == node->data.type) {
321 return 0;
322 }
323
324 /*
325 * Cast (or parse) the input data to the expected argument data type.
326 */
327 if (fr_value_box_cast_in_place(node, &node->data, arg_p->type, NULL) < 0) {
328 fr_strerror_printf("Invalid argument %d - %s", argc, fr_strerror());
329 return -1;
330 }
331
332 return 0;
333}
334
336{
337 xlat_arg_parser_t const *arg_p;
338 xlat_exp_t *arg = xlat_exp_head(node->call.args);
339 int i = 1;
340
341 fr_assert(node->type == XLAT_FUNC);
342
343 /*
344 * Check the function definition against what the user passed in.
345 */
346 if (!node->call.func->args) {
347 if (node->call.args) {
348 fr_strerror_const("Too many arguments to function call, expected 0");
349 return -1;
350 }
351
352 /*
353 * Function takes no arguments, and none were passed in. There's nothing to verify.
354 */
355 return 0;
356 }
357
358 if (!node->call.args) {
359 fr_strerror_const("Too few arguments to function call");
360 return -1;
361 }
362
363 /*
364 * The function both has arguments defined, and the user has supplied them.
365 */
366 for (arg_p = node->call.func->args, i = 0; arg_p->type != FR_TYPE_NULL; arg_p++) {
367 if (!arg_p->required) break;
368
369 if (!arg) {
370 fr_strerror_printf("Missing required argument %u",
371 (unsigned int)(arg_p - node->call.func->args) + 1);
372 return -1;
373 }
374
375 /*
376 * All arguments MUST be put into a group, even
377 * if they're just one element.
378 */
379 fr_assert(arg->type == XLAT_GROUP);
380
381 if (xlat_validate_function_arg(arg_p, arg, i) < 0) return -1;
382
383 arg = xlat_exp_next(node->call.args, arg);
384 i++;
385 }
386
387 /*
388 * @todo - check if there is a trailing argument. But for functions which take no arguments, the
389 * "arg" is an empty group.
390 */
391
392 return 0;
393}
394
395/** Parse an xlat function and its child argument
396 *
397 * Parses a function call string in the format
398 * @verbatim %<func>(<argument>) @endverbatim
399 *
400 * @return
401 * - 0 if the string was parsed into a function.
402 * - <0 on parse error.
403 */
405{
406 char c;
407 xlat_exp_t *node;
408 xlat_t *func;
410 tmpl_rules_t my_t_rules;
411
412 fr_sbuff_marker(&m_s, in);
413
415
416 /*
417 * The caller ensures that the first character aftet the percent exists, and is alphanumeric.
418 */
419 c = fr_sbuff_char(in, '\0');
420
421 /*
422 * Even if it is alphanumeric, only a limited set of characters are one-letter expansions.
423 *
424 * And even then only if the character after them is a terminal character.
425 */
426 if (strchr("cCdDeGHIlmMnSstTY", c) != NULL) {
427 char n;
428
429 fr_sbuff_next(in);
430
431 /*
432 * End of buffer == one letter expansion.
433 */
434 n = fr_sbuff_char(in, '\0');
435 if (!n) goto one_letter;
436
437 /*
438 * %Y() is the new format.
439 */
440 if (n == '(') {
441 fr_sbuff_next(in);
442
443 if (!fr_sbuff_next_if_char(in, ')')) {
444 fr_strerror_const("Missing closing brace ')'");
445 return -1;
446 }
447
448 goto one_letter;
449 }
450
451 /*
452 * %M. or %Y- is a one-letter expansion followed by the other character.
453 */
454 if (!sbuff_char_alpha_num[(unsigned int) n]) {
455 one_letter:
456 XLAT_DEBUG("ONE-LETTER <-- %c", c);
458
459 xlat_exp_set_name(node, fr_sbuff_current(&m_s), 1);
460 xlat_exp_set_type(node, XLAT_ONE_LETTER); /* needs node->fmt to be set */
461
462 fr_sbuff_marker_release(&m_s);
463
464#ifdef STATIC_ANALYZER
465 if (!node->fmt) return -1;
466#endif
467
469 return 0;
470 }
471
472 /*
473 * Anything else, it must be a full function name.
474 */
475 fr_sbuff_set(in, &m_s);
476 }
477
479
481
482 if (!fr_sbuff_is_char(in, '(')) {
483 fr_strerror_printf("Missing '('");
484 return -1;
485 }
486
487 /*
488 * Allocate a node to hold the function
489 */
491 if (!func) {
492 if (!t_rules->attr.allow_unresolved|| t_rules->at_runtime) {
493 fr_strerror_const("Unresolved expansion functions are not allowed here");
494 fr_sbuff_set(in, &m_s); /* backtrack */
495 fr_sbuff_marker_release(&m_s);
496 return -1;
497 }
499
500 } else {
501 xlat_exp_set_func(node, func, t_rules->attr.dict_def);
502 }
503
504 fr_sbuff_marker_release(&m_s);
505
506 (void) fr_sbuff_next(in); /* skip the '(' */
507
508 /*
509 * The caller might want the _output_ cast to something. But that doesn't mean we cast each
510 * _argument_ to the xlat function.
511 */
512 if (t_rules->cast != FR_TYPE_NULL) {
513 my_t_rules = *t_rules;
514 my_t_rules.cast = FR_TYPE_NULL;
515 t_rules = &my_t_rules;
516 }
517
518 /*
519 * Now parse the child nodes that form the
520 * function's arguments.
521 */
522 if (xlat_tokenize_argv(node, &node->call.args, in, func,
523 &xlat_function_arg_rules, t_rules, false) < 0) {
524 error:
525 talloc_free(node);
526 return -1;
527 }
528
529 if (!fr_sbuff_next_if_char(in, ')')) {
530 fr_strerror_const("Missing closing brace ')'");
531 goto error;
532 }
533
535
537 return 0;
538}
539
540/** Parse an attribute ref or a virtual attribute
541 *
542 */
544 fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules)
545{
547 tmpl_t *vpt = NULL;
548 xlat_exp_t *node;
549
551 tmpl_rules_t our_t_rules;
552 fr_sbuff_t our_in = FR_SBUFF(in);
553
554 XLAT_DEBUG("ATTRIBUTE <-- %.*s", (int) fr_sbuff_remaining(in), fr_sbuff_current(in));
555
556 /*
557 * We are called from %{foo}. So we don't use attribute prefixes.
558 */
559 our_t_rules = *t_rules;
560 our_t_rules.attr.allow_wildcard = true;
561
562 fr_sbuff_marker(&m_s, in);
563
565 if (tmpl_afrom_attr_substr(node, &err, &vpt, &our_in, p_rules, &our_t_rules) < 0) {
566 /*
567 * If the parse error occurred before a terminator,
568 * then the error is changed to 'Unknown module',
569 * as it was more likely to be a bad module name,
570 * than a request qualifier.
571 */
573 error:
574 fr_sbuff_marker_release(&m_s);
575 talloc_free(node);
576 FR_SBUFF_ERROR_RETURN(&our_in);
577 }
578
579 /*
580 * Deal with unresolved attributes.
581 */
583 if (!t_rules->attr.allow_unresolved) {
585
586 fr_strerror_const("Unresolved attributes not allowed in expansions here");
587 fr_sbuff_set(&our_in, &m_s); /* Error at the start of the attribute */
588 goto error;
589 }
590 }
591
592 /*
593 * Deal with normal attribute (or list)
594 */
596 xlat_exp_set_vpt(node, vpt);
597
598 /*
599 * Remember that it was %{User-Name}
600 *
601 * This is a temporary hack until all of the unit tests
602 * pass without '&'.
603 */
604 UNCONST(tmpl_attr_rules_t *, &vpt->rules.attr)->xlat = true;
605
607
608 fr_sbuff_marker_release(&m_s);
609 return fr_sbuff_set(in, &our_in);
610}
611
612static bool const tmpl_attr_allowed_chars[UINT8_MAX + 1] = {
614 ['-'] = true, ['/'] = true, ['_'] = true, // fr_dict_attr_allowed_chars
615 ['.'] = true, ['*'] = true, ['#'] = true,
616 ['['] = true, [']'] = true, // tmpls and attribute arrays
617};
618
620 tmpl_rules_t const *t_rules)
621{
622 size_t len;
623 int ret;
625 char hint;
626 fr_sbuff_term_t hint_tokens = FR_SBUFF_TERMS(
627 L(" "), /* First special token is a ' ' - Likely a syntax error */
628 L("["), /* First special token is a '[' i.e. '%{attr[<idx>]}' */
629 L("}") /* First special token is a '}' i.e. '%{<attrref>}' */
630 );
631
632 fr_sbuff_parse_rules_t attr_p_rules = {
633 .escapes = &xlat_unescape,
634 .terminals = &FR_SBUFF_TERM("}")
635 };
636#ifdef HAVE_REGEX
637 xlat_exp_t *node;
638#endif
639
640 XLAT_DEBUG("EXPANSION <-- %.*s", (int) fr_sbuff_remaining(in), fr_sbuff_current(in));
641
642 fr_sbuff_marker(&m_s, in);
643
644#ifdef HAVE_REGEX
645 ret = xlat_tokenize_regex(head, &node, in, &m_s);
646 if (ret < 0) return ret;
647
648 if (ret == 1) {
649 fr_assert(node != NULL);
651 return 0;
652 }
653
654 fr_sbuff_set(in, &m_s); /* backtrack to the start of the expression */
655#endif /* HAVE_REGEX */
656
657 /*
658 * See if it's an attribute reference, with possible array stuff.
659 */
661 if (fr_sbuff_is_char(in, '}')) {
662 if (!len) goto empty_disallowed;
663 goto check_for_attr;
664 }
665
666 if (!fr_sbuff_extend(in)) {
667 fr_strerror_const("Missing closing brace '}'");
668 fr_sbuff_marker_release(&m_s);
669 return -1;
670 }
671
672 /*
673 * It must be an expression.
674 *
675 * We wrap the xlat in a group, and then mark the group to be hoisted.
676 */
677 {
678 tmpl_rules_t my_rules;
679
680 fr_sbuff_set(in, &m_s); /* backtrack to the start of the expression */
681
682 MEM(node = xlat_exp_alloc(head, XLAT_GROUP, NULL, 0));
683
684 if (t_rules) {
685 my_rules = *t_rules;
686 my_rules.enumv = NULL;
687 my_rules.cast = FR_TYPE_NULL;
688 t_rules = &my_rules;
689 }
690
691 ret = xlat_tokenize_expression(node, &node->group, in, &attr_p_rules, t_rules);
692 if (ret <= 0) {
693 talloc_free(node);
694 return ret;
695 }
696
697 if (!fr_sbuff_is_char(in, '}')) {
698 fr_strerror_const("Missing closing brace '}'");
699 return -1;
700 }
701
703 node->flags = node->group->flags;
704
705 /*
706 * Print it as %{...}. Then when we're evaluating a string, hoist the results.
707 */
708 node->flags.xlat = true;
709 node->hoist = true;
710
712
713 (void) fr_sbuff_next(in); /* skip '}' */
714 return ret;
715 }
716
717check_for_attr:
718 fr_sbuff_set(in, &m_s); /* backtrack */
719
720 /*
721 * %{Attr-Name}
722 * %{Attr-Name[#]}
723 * %{request.Attr-Name}
724 */
725
726 /*
727 * Check for empty expressions %{} %{: %{[
728 */
729 fr_sbuff_marker(&m_s, in);
730 len = fr_sbuff_adv_until(in, SIZE_MAX, &hint_tokens, '\0');
731
732 /*
733 * This means the end of a string not containing any of the other
734 * tokens was reached.
735 *
736 * e.g. '%{myfirstxlat'
737 */
738 if (!fr_sbuff_extend(in)) {
739 fr_strerror_const("Missing closing brace '}'");
740 fr_sbuff_marker_release(&m_s);
741 return -1;
742 }
743
744 hint = fr_sbuff_char(in, '\0');
745
746 XLAT_DEBUG("EXPANSION HINT TOKEN '%c'", hint);
747 if (len == 0) {
748 switch (hint) {
749 case '}':
750 empty_disallowed:
751 fr_strerror_const("Empty expressions are invalid");
752 return -1;
753
754 case '[':
755 fr_strerror_const("Missing attribute name");
756 return -1;
757
758 default:
759 break;
760 }
761 }
762
763 switch (hint) {
764 /*
765 * Hint token is a:
766 * - '[' - Which is an attribute index, so it must be an attribute.
767 * - '}' - The end of the expansion, which means it was a bareword.
768 */
769 case '.':
770 case '}':
771 case '[':
772 fr_sbuff_set(in, &m_s); /* backtrack */
773 fr_sbuff_marker_release(&m_s);
774
775 if (xlat_tokenize_attribute(head, in, &attr_p_rules, t_rules) < 0) return -1;
776
777 if (!fr_sbuff_next_if_char(in, '}')) {
778 fr_strerror_const("Missing closing brace '}'");
779 return -1;
780 }
781
782 return 0;
783
784 /*
785 * Hint token was whitespace
786 *
787 * e.g. '%{my '
788 */
789 default:
790 break;
791 }
792
793 /*
794 * Box print is so we get \t \n etc..
795 */
796 fr_strerror_printf("Invalid char '%pV' in expression", fr_box_strvalue_len(fr_sbuff_current(in), 1));
797 return -1;
798}
799
800/** Parse an xlat string i.e. a non-expansion or non-function
801 *
802 * When this function is being used outside of an xlat expansion, i.e. on a string
803 * which contains one or more xlat expansions, it uses the terminal grammar and
804 * escaping rules of that string type.
805 *
806 * Which this function is being used inside of an xlat expansion, it uses xlat specific
807 * terminal grammar and escaping rules.
808 *
809 * This allows us to be smart about processing quotes within the expansions themselves.
810 *
811 * @param[out] head to allocate nodes in, and where to write the first
812 * child, and where the flags are stored.
813 * @param[in] in sbuff to parse.
814 * @param[in] p_rules that control parsing.
815 * @param[in] t_rules that control attribute reference and xlat function parsing.
816 * @return
817 * - <0 on failure
818 * - >=0 for number of bytes parsed
819 */
821 fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules)
822{
823 xlat_exp_t *node = NULL;
824 fr_slen_t slen;
826 L("%"),
827 );
828 fr_sbuff_term_t *tokens;
830 fr_sbuff_t our_in = FR_SBUFF(in);
831
832 XLAT_DEBUG("STRING <-- %.*s", (int) fr_sbuff_remaining(in), fr_sbuff_current(in));
833
834 escapes = p_rules ? p_rules->escapes : NULL;
835 tokens = p_rules && p_rules->terminals ?
836 fr_sbuff_terminals_amerge(NULL, p_rules->terminals, &terminals) : &terminals;
837
838 for (;;) {
839 char *str;
841
842 /*
843 * pre-allocate the node so we don't have to steal it later.
844 */
845 node = xlat_exp_alloc(head, XLAT_BOX, NULL, 0);
846
847 /*
848 * Find the next token
849 */
850 fr_sbuff_marker(&m_s, &our_in);
851 slen = fr_sbuff_out_aunescape_until(node, &str, &our_in, SIZE_MAX, tokens, escapes);
852
853 if (slen < 0) {
854 error:
855 talloc_free(node);
856
857 /*
858 * Free our temporary array of terminals
859 */
860 if (tokens != &terminals) talloc_free(tokens);
861 fr_sbuff_marker_release(&m_s);
862 FR_SBUFF_ERROR_RETURN(&our_in);
863 }
864
865 /*
866 * It's a value box, create an appropriate node
867 */
868 if (slen > 0) {
869 do_value_box:
870 xlat_exp_set_name_shallow(node, str);
871 fr_value_box_bstrndup(node, &node->data, NULL, str, talloc_array_length(str) - 1, false);
872 fr_value_box_mark_safe_for(&node->data, t_rules->literals_safe_for);
873
874 if (!escapes) {
875 XLAT_DEBUG("VALUE-BOX %s <-- %.*s", str,
876 (int) fr_sbuff_behind(&m_s), fr_sbuff_current(&m_s));
877 } else {
878 XLAT_DEBUG("VALUE-BOX (%s) %s <-- %.*s", escapes->name, str,
879 (int) fr_sbuff_behind(&m_s), fr_sbuff_current(&m_s));
880 }
881 XLAT_HEXDUMP((uint8_t const *)str, talloc_array_length(str) - 1, " VALUE-BOX ");
882
884
885 node = NULL;
886 fr_sbuff_marker_release(&m_s);
887 continue;
888 }
889
890 /*
891 * We have parsed as much as we can as unescaped
892 * input. Either some text (and added the node
893 * to the list), or zero text. We now try to
894 * parse '%' expansions.
895 */
896
897 /*
898 * Attribute, function call, or other expansion.
899 */
900 if (fr_sbuff_adv_past_str_literal(&our_in, "%{")) {
901 TALLOC_FREE(node); /* nope, couldn't use it */
902
903 if (xlat_tokenize_expansion(head, &our_in, t_rules) < 0) goto error;
904
905 if (fr_sbuff_is_str_literal(&our_in, ":-")) {
906 fr_strerror_const("Old style alternation of %{...:-...} is no longer supported");
907 goto error;
908 }
909
910 next:
911 fr_sbuff_marker_release(&m_s);
912 continue;
913 }
914
915 /*
916 * More migration hacks: allow %foo(...)
917 */
918 if (fr_sbuff_next_if_char(&our_in, '%')) {
919 /*
920 * % non-alphanumeric, create a value-box for just the "%" character.
921 */
922 if (!fr_sbuff_is_alnum(&our_in)) {
923 if (fr_sbuff_next_if_char(&our_in, '%')) { /* nothing */ }
924
925 str = talloc_typed_strdup(node, "%");
926 goto do_value_box;
927 }
928
929 TALLOC_FREE(node); /* nope, couldn't use it */
930
931 /*
932 * Tokenize the function arguments using the new method.
933 */
934 if (xlat_tokenize_function_args(head, &our_in, t_rules) < 0) goto error;
935 goto next;
936 }
937
938 /*
939 * Nothing we recognize. Just return nothing.
940 */
941 TALLOC_FREE(node);
942 XLAT_DEBUG("VALUE-BOX <-- (empty)");
943 fr_sbuff_marker_release(&m_s);
944 break;
945 }
946
947 /*
948 * Free our temporary array of terminals
949 */
950 if (tokens != &terminals) talloc_free(tokens);
951
952 return fr_sbuff_set(in, &our_in);
953}
954
956 { L("\""), T_DOUBLE_QUOTED_STRING }, /* Don't re-order, backslash throws off ordering */
957 { L("'"), T_SINGLE_QUOTED_STRING },
958 { L("`"), T_BACK_QUOTED_STRING }
959};
961
962#define INFO_INDENT(_fmt, ...) INFO("%*s"_fmt, depth * 2, " ", ## __VA_ARGS__)
963
964static void _xlat_debug_head(xlat_exp_head_t const *head, int depth);
965static void _xlat_debug_node(xlat_exp_t const *node, int depth, bool print_flags)
966{
967 INFO_INDENT("{ -- %s", node->fmt);
968#ifndef NDEBUG
969// INFO_INDENT(" %s:%d", node->file, node->line);
970#endif
971
972 if (print_flags) {
973 INFO_INDENT("flags = %s %s %s %s %s",
974 node->flags.needs_resolving ? "need_resolving" : "",
975 node->flags.pure ? "pure" : "",
976 node->flags.can_purify ? "can_purify" : "",
977 node->flags.constant ? "constant" : "",
978 node->flags.xlat ? "xlat" : "");
979 }
980
981 depth++;
982
983 if (node->quote != T_BARE_WORD) INFO_INDENT("quote = %c", fr_token_quote[node->quote]);
984
985 switch (node->type) {
986 case XLAT_BOX:
987 INFO_INDENT("value %s --> %pV", fr_type_to_str(node->data.type), &node->data);
988 break;
989
990 case XLAT_GROUP:
991 INFO_INDENT("group");
992 INFO_INDENT("{");
993 _xlat_debug_head(node->group, depth + 1);
994 INFO_INDENT("}");
995 break;
996
997 case XLAT_ONE_LETTER:
998 INFO_INDENT("percent (%c)", node->fmt[0]);
999 break;
1000
1001 case XLAT_TMPL:
1002 {
1003 if (tmpl_cast_get(node->vpt) != FR_TYPE_NULL) {
1004 INFO_INDENT("cast (%s)", fr_type_to_str(tmpl_cast_get(node->vpt)));
1005 }
1006
1007 if (tmpl_is_attr(node->vpt)) {
1008 fr_assert(!node->flags.pure);
1009 if (tmpl_attr_tail_da(node->vpt)) INFO_INDENT("tmpl attribute (%s)", tmpl_attr_tail_da(node->vpt)->name);
1010 if (tmpl_attr_tail_num(node->vpt) != NUM_UNSPEC) {
1011 FR_DLIST_HEAD(tmpl_request_list) const *list;
1012 tmpl_request_t *rr = NULL;
1013
1014 INFO_INDENT("{");
1015
1016 /*
1017 * Loop over the request references
1018 */
1019 list = tmpl_request(node->vpt);
1020 while ((rr = tmpl_request_list_next(list, rr))) {
1021 INFO_INDENT("ref %u", rr->request);
1022 }
1023 INFO_INDENT("list %s", tmpl_list_name(tmpl_list(node->vpt), "<INVALID>"));
1024 if (tmpl_attr_tail_num(node->vpt) != NUM_UNSPEC) {
1025 if (tmpl_attr_tail_num(node->vpt) == NUM_COUNT) {
1026 INFO_INDENT("[#]");
1027 } else if (tmpl_attr_tail_num(node->vpt) == NUM_ALL) {
1028 INFO_INDENT("[*]");
1029 } else {
1030 INFO_INDENT("[%d]", tmpl_attr_tail_num(node->vpt));
1031 }
1032 }
1033 INFO_INDENT("}");
1034 }
1035 } else if (tmpl_is_data(node->vpt)) {
1036 INFO_INDENT("tmpl (%s) type %s", node->fmt, fr_type_to_str(tmpl_value_type(node->vpt)));
1037
1038 } else if (tmpl_is_xlat(node->vpt)) {
1039 INFO_INDENT("tmpl xlat (%s)", node->fmt);
1040 _xlat_debug_head(tmpl_xlat(node->vpt), depth + 1);
1041
1042 } else {
1043 INFO_INDENT("tmpl (%s)", node->fmt);
1044 }
1045 }
1046 break;
1047
1048 case XLAT_FUNC:
1049 fr_assert(node->call.func != NULL);
1050 INFO_INDENT("func (%s)", node->call.func->name);
1051 if (xlat_exp_head(node->call.args)) {
1052 INFO_INDENT("{");
1053 _xlat_debug_head(node->call.args, depth + 1);
1054 INFO_INDENT("}");
1055 }
1056 break;
1057
1059 INFO_INDENT("func-unresolved (%s)", node->fmt);
1060 if (xlat_exp_head(node->call.args)) {
1061 INFO_INDENT("{");
1062 _xlat_debug_head(node->call.args, depth + 1);
1063 INFO_INDENT("}");
1064 }
1065 break;
1066
1067#ifdef HAVE_REGEX
1068 case XLAT_REGEX:
1069 INFO_INDENT("regex-var -- %d", node->regex_index);
1070 break;
1071#endif
1072
1073 case XLAT_INVALID:
1074 DEBUG("XLAT-INVALID");
1075 break;
1076 }
1077
1078 depth--;
1079 INFO_INDENT("}");
1080}
1081
1082void xlat_debug(xlat_exp_t const *node)
1083{
1084 _xlat_debug_node(node, 0, true);
1085}
1086
1088{
1089 int i = 0;
1090
1091 fr_assert(head != NULL);
1092
1093 INFO_INDENT("head flags = %s %s %s %s %s",
1094 head->flags.needs_resolving ? "need_resolving," : "",
1095 head->flags.pure ? "pure" : "",
1096 head->flags.can_purify ? "can_purify" : "",
1097 head->flags.constant ? "constant" : "",
1098 head->flags.xlat ? "xlat" : "");
1099
1100 depth++;
1101
1102 xlat_exp_foreach(head, node) {
1103 INFO_INDENT("[%d] flags = %s %s %s %s %s", i++,
1104 node->flags.needs_resolving ? "need_resolving" : "",
1105 node->flags.pure ? "pure" : "",
1106 node->flags.can_purify ? "can_purify" : "",
1107 node->flags.constant ? "constant" : "",
1108 node->flags.xlat ? "xlat" : "");
1109
1110 _xlat_debug_node(node, depth, false);
1111 }
1112}
1113
1115{
1117}
1118
1120 fr_sbuff_escape_rules_t const *e_rules, char c)
1121{
1122 ssize_t slen;
1123 size_t at_in = fr_sbuff_used_total(out);
1124 char close;
1125
1126 if (!node) return 0;
1127
1128 if (node->flags.xlat) FR_SBUFF_IN_CHAR_RETURN(out, '%', '{');
1129
1130 switch (node->type) {
1131 case XLAT_GROUP:
1133 xlat_print(out, node->group, fr_value_escape_by_quote[node->quote]);
1135
1136 if (xlat_exp_next(head, node)) {
1137 if (c) FR_SBUFF_IN_CHAR_RETURN(out, c);
1138
1139 if (head->is_argv) FR_SBUFF_IN_CHAR_RETURN(out, ' '); /* Add ' ' between args */
1140 }
1141 goto done;
1142
1143 case XLAT_BOX:
1144 /*
1145 * @todo - respect node->quote here, too. Which also means updating the parser.
1146 */
1147 if (node->quote == T_BARE_WORD) {
1148 if (node->data.enumv &&
1149 (strncmp(node->fmt, "::", 2) == 0)) {
1151 }
1152
1153 FR_SBUFF_RETURN(fr_value_box_print, out, &node->data, e_rules);
1154 } else {
1155 FR_SBUFF_RETURN(fr_value_box_print_quoted, out, &node->data, node->quote);
1156 }
1157 goto done;
1158
1159 case XLAT_TMPL:
1160 if (node->vpt->rules.cast != FR_TYPE_NULL) {
1162 FR_SBUFF_IN_STRCPY_RETURN(out, fr_type_to_str(node->vpt->rules.cast));
1164 }
1165
1166 if (tmpl_is_data(node->vpt)) {
1167 /*
1168 * @todo - until such time as the value
1169 * box functions print "::" before enum
1170 * names.
1171 *
1172 * Arguably it should _always_ print the
1173 * "::" before enum names, even if the
1174 * input didn't have "::". But that's
1175 * addressed when the prefix is required,
1176 * OR when the value-box functions are
1177 * updated.
1178 */
1179 if (node->vpt->data.literal.enumv &&
1180 (strncmp(node->fmt, "::", 2) == 0)) {
1182 }
1184 goto done;
1185 }
1186 if (tmpl_needs_resolving(node->vpt)) {
1187 if (node->vpt->quote != T_BARE_WORD) {
1189 }
1190 FR_SBUFF_IN_STRCPY_RETURN(out, node->vpt->name); /* @todo - escape it? */
1191 if (node->vpt->quote != T_BARE_WORD) {
1193 }
1194 goto done;
1195 }
1196
1197 if (tmpl_contains_xlat(node->vpt)) { /* xlat and exec */
1198 if (node->vpt->quote == T_BARE_WORD) {
1199 xlat_print(out, tmpl_xlat(node->vpt), NULL);
1200 } else {
1204 }
1205 goto done;
1206 }
1207
1208 /*
1209 * Regexes need their own print routine, as they need to print the flags, too.
1210 *
1211 * Regexes should also "eat" their arguments into their instance data, so that we should
1212 * never try to print a regex.
1213 */
1214 fr_assert(!tmpl_contains_regex(node->vpt));
1215
1216 // attr or list
1217 fr_assert(tmpl_is_attr(node->vpt));
1218 fr_assert(talloc_parent(node->vpt) == node);
1219 fr_assert(!node->flags.pure);
1220
1221 /*
1222 * No '&', print the name, BUT without any attribute prefix.
1223 */
1224 if (!node->vpt->rules.attr.xlat) {
1225 char const *p = node->fmt;
1226
1227 if (*p == '&') p++;
1228
1230 goto done;
1231 }
1232 break;
1233
1234 case XLAT_ONE_LETTER:
1235 FR_SBUFF_IN_CHAR_RETURN(out, '%', node->fmt[0]);
1236 goto done;
1237
1238 case XLAT_FUNC:
1239 /*
1240 * We have a callback for printing this node, go
1241 * call it.
1242 */
1243 if (node->call.func->print) {
1244 slen = node->call.func->print(out, node, node->call.inst->data, e_rules);
1245 if (slen < 0) return slen;
1246 goto done;
1247 }
1248 break;
1249
1250 default:
1251 break;
1252 }
1253
1254 /*
1255 * Now print %(...) or %{...}
1256 */
1257 if ((node->type == XLAT_FUNC) || (node->type == XLAT_FUNC_UNRESOLVED)) {
1258 FR_SBUFF_IN_CHAR_RETURN(out, '%'); /* then the name */
1259 close = ')';
1260 } else {
1262 close = '}';
1263 }
1264
1265 switch (node->type) {
1266 case XLAT_TMPL:
1267 slen = tmpl_attr_print(out, node->vpt);
1268 if (slen < 0) return slen;
1269 break;
1270
1271#ifdef HAVE_REGEX
1272 case XLAT_REGEX:
1273 FR_SBUFF_IN_SPRINTF_RETURN(out, "%i", node->regex_index);
1274 break;
1275#endif
1276
1277 case XLAT_FUNC:
1278 FR_SBUFF_IN_BSTRCPY_BUFFER_RETURN(out, node->call.func->name);
1280
1281 goto print_args;
1282
1286
1287 print_args:
1288 if (xlat_exp_head(node->call.args)) {
1289 xlat_exp_foreach(node->call.args, child) {
1290 slen = xlat_print_node(out, node->call.args, child, &xlat_escape, ',');
1291 if (slen < 0) return slen;
1292 }
1293 }
1294 break;
1295
1296 case XLAT_INVALID:
1297 case XLAT_BOX:
1298 case XLAT_ONE_LETTER:
1299 case XLAT_GROUP:
1300 fr_assert_fail(NULL);
1301 break;
1302 }
1304
1305done:
1306 if (node->flags.xlat) FR_SBUFF_IN_CHAR_RETURN(out, '}');
1307
1308 return fr_sbuff_used_total(out) - at_in;
1309}
1310
1311/** Reconstitute an xlat expression from its constituent nodes
1312 *
1313 * @param[in] out Where to write the output string.
1314 * @param[in] head First node to print.
1315 * @param[in] e_rules Specifying how to escape literal values.
1316 */
1318{
1319 ssize_t slen;
1320 size_t at_in = fr_sbuff_used_total(out);
1321
1322 xlat_exp_foreach(head, node) {
1323 slen = xlat_print_node(out, head, node, e_rules, 0);
1324 if (slen < 0) {
1325 /* coverity[return_overflow] */
1326 return slen - (fr_sbuff_used_total(out) - at_in);
1327 }
1328 }
1329
1330 return fr_sbuff_used_total(out) - at_in;
1331}
1332
1333#if 0
1334static void xlat_safe_for(xlat_exp_head_t *head, fr_value_box_safe_for_t safe_for)
1335{
1336 xlat_exp_foreach(head, node) {
1337 switch (node->type) {
1338 case XLAT_BOX:
1339 if (node->data.safe_for != safe_for) {
1340 ERROR("FAILED %lx %lx - %s", node->data.safe_for, safe_for, node->fmt);
1341 }
1342 fr_assert(node->data.safe_for == safe_for);
1343 break;
1344
1345 case XLAT_GROUP:
1346 xlat_safe_for(node->group, safe_for);
1347 break;
1348
1349 case XLAT_TMPL:
1350 if (!tmpl_is_xlat(node->vpt)) break;
1351
1352 xlat_safe_for(tmpl_xlat(node->vpt), safe_for);
1353 break;
1354
1355 default:
1356 break;
1357 }
1358 }
1359}
1360#endif
1361
1362
1364 fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules)
1365{
1366 int triple = 1;
1367 ssize_t slen;
1368 fr_sbuff_t our_in = FR_SBUFF(in);
1369 xlat_exp_t *node;
1371
1372 /*
1373 * Triple-quoted strings have different terminal conditions.
1374 */
1375 switch (quote) {
1377 fr_strerror_const("Unexpected regular expression");
1378 fr_sbuff_advance(in, -1); /* to the actual '/' */
1379 our_in = FR_SBUFF(in);
1380 FR_SBUFF_ERROR_RETURN(&our_in);
1381
1382 default:
1383 fr_assert(0);
1384 FR_SBUFF_ERROR_RETURN(&our_in);
1385
1386 case T_BARE_WORD:
1387#ifdef HAVE_REGEX
1388 fr_sbuff_marker(&m, &our_in);
1389
1390 /*
1391 * Regular expression expansions are %{...}
1392 */
1393 if (fr_sbuff_adv_past_str_literal(&our_in, "%{")) {
1394 int ret;
1396
1397 fr_sbuff_marker(&m_s, &our_in);
1398
1399 ret = xlat_tokenize_regex(ctx, &node, &our_in, &m_s);
1400 if (ret < 0) FR_SBUFF_ERROR_RETURN(&our_in);
1401
1402 if (ret == 1) goto done;
1403
1404 fr_sbuff_set(&our_in, &m);
1405 }
1406#endif /* HAVE_REGEX */
1407
1408#if 0
1409 /*
1410 * Avoid a bounce through tmpls for %{...} and %func()
1411 *
1412 * @todo %{...} --> tokenize expression
1413 * %foo(..) --> tokenize_function_args (and have that function look for ()
1414 * %Y or %Y() --> one letter
1415 */
1416 if (fr_sbuff_is_char(&our_in, '%')) {
1417 xlat_exp_head_t *head = NULL;
1418
1420
1421 slen = xlat_tokenize_input(head, &our_in, p_rules, t_rules);
1422 if (slen <= 0) {
1424 FR_SBUFF_ERROR_RETURN(&our_in);
1425 }
1426
1427 fr_assert(fr_dlist_num_elements(&head->dlist) == 1);
1428
1429 node = fr_dlist_pop_head(&head->dlist);
1430 fr_assert(node != NULL);
1431 (void) talloc_steal(ctx, node);
1433 goto done;
1434 }
1435#endif
1436 break;
1437
1441 p_rules = value_parse_rules_quoted[quote];
1442
1443 if (fr_sbuff_remaining(&our_in) >= 2) {
1444 char const *p = fr_sbuff_current(&our_in);
1445 char c = fr_token_quote[quote];
1446
1447 /*
1448 * """foo "quote" and end"""
1449 */
1450 if ((p[0] == c) && (p[1] == c)) {
1451 triple = 3;
1452 (void) fr_sbuff_advance(&our_in, 2);
1453 p_rules = value_parse_rules_3quoted[quote];
1454 }
1455 }
1456 break;
1457 }
1458
1459 switch (quote) {
1460 /*
1461 * `foo` is a tmpl, and is NOT a group.
1462 */
1464 case T_BARE_WORD:
1465 MEM(node = xlat_exp_alloc(ctx, XLAT_TMPL, NULL, 0));
1466 node->quote = quote;
1467
1468 /*
1469 * tmpl_afrom_substr does pretty much all the work of
1470 * parsing the operand. It pays attention to the cast on
1471 * our_t_rules, and will try to parse any data there as
1472 * of the correct type.
1473 */
1474 slen = tmpl_afrom_substr(node, &node->vpt, &our_in, quote, p_rules, t_rules);
1475 if (slen <= 0) {
1476 fr_sbuff_advance(&our_in, -slen - 1); /* point to the correct offset */
1477
1478 error:
1479 talloc_free(node);
1480 FR_SBUFF_ERROR_RETURN(&our_in);
1481 }
1482 xlat_exp_set_vpt(node, node->vpt); /* sets flags */
1483
1484 if (xlat_tmpl_normalize(node) < 0) goto error;
1485
1486 if (quote == T_BARE_WORD) goto done;
1487
1488 break; /* exec - look for closing quote */
1489
1490 /*
1491 * "Double quoted strings may contain %{expansions}"
1492 */
1494 MEM(node = xlat_exp_alloc(ctx, XLAT_GROUP, NULL, 0));
1495 node->quote = quote;
1496
1497 fr_sbuff_marker(&m, &our_in);
1498 XLAT_DEBUG("ARGV double quotes <-- %.*s", (int) fr_sbuff_remaining(&our_in), fr_sbuff_current(&our_in));
1499
1500 if (xlat_tokenize_input(node->group, &our_in, p_rules, t_rules) < 0) goto error;
1501
1502 node->flags = node->group->flags;
1503 node->hoist = true;
1505
1506 /*
1507 * There's no expansion in the string. Hoist the value-box.
1508 */
1509 if (node->flags.constant) {
1510 xlat_exp_t *child;
1511
1512 /*
1513 * The list is either empty, or else it has one child, which is the constant
1514 * node.
1515 */
1516 if (fr_dlist_num_elements(&node->group->dlist) == 0) {
1518
1519 fr_value_box_init(&node->data, FR_TYPE_STRING, NULL, false);
1520 fr_value_box_strdup(node, &node->data, NULL, "", false);
1521
1522 } else {
1523 fr_assert(fr_dlist_num_elements(&node->group->dlist) == 1);
1524
1525 child = talloc_steal(ctx, xlat_exp_head(node->group));
1526 talloc_free(node);
1527 node = child;
1528 }
1529
1530 fr_assert(node->type == XLAT_BOX);
1531
1532 node->quote = quote; /* not the same node! */
1533 }
1534 break;
1535
1536 /*
1537 * 'Single quoted strings get parsed as literal strings'
1538 */
1540 {
1541 char *str;
1542
1543 XLAT_DEBUG("ARGV single quotes <-- %.*s", (int) fr_sbuff_remaining(&our_in), fr_sbuff_current(&our_in));
1544
1545 node = xlat_exp_alloc(ctx, XLAT_BOX, NULL, 0);
1546 node->quote = quote;
1547
1548 slen = fr_sbuff_out_aunescape_until(node, &str, &our_in, SIZE_MAX, p_rules->terminals, p_rules->escapes);
1549 if (slen < 0) goto error;
1550
1551 xlat_exp_set_name_shallow(node, str);
1552 fr_value_box_strdup(node, &node->data, NULL, str, false);
1553 fr_value_box_mark_safe_for(&node->data, t_rules->literals_safe_for); /* Literal values are treated as implicitly safe */
1554 }
1555 break;
1556
1557 default:
1558 fr_strerror_const("Internal sanity check failed in tokenizing expansion word");
1559 FR_SBUFF_ERROR_RETURN(&our_in);
1560 }
1561
1562 /*
1563 * Ensure that the string ends with the correct number of quotes.
1564 */
1565 do {
1566 if (!fr_sbuff_is_char(&our_in, fr_token_quote[quote])) {
1567 fr_strerror_const("Unterminated string");
1568 fr_sbuff_set_to_start(&our_in);
1569 goto error;
1570 }
1571
1572 fr_sbuff_advance(&our_in, 1);
1573 } while (--triple > 0);
1574
1575done:
1576 *out = node;
1577
1578 FR_SBUFF_SET_RETURN(in, &our_in);
1579}
1580
1581/** Tokenize an xlat expansion into a series of XLAT_TYPE_CHILD arguments
1582 *
1583 * @param[in] ctx to allocate nodes in. Note: All nodes will be
1584 * allocated in the same ctx. This is to allow
1585 * manipulation by xlat instantiation functions
1586 * later.
1587 * @param[out] out the head of the xlat list / tree structure.
1588 * @param[in] in the format string to expand.
1589 * @param[in] xlat we're tokenizing arguments for.
1590 * @param[in] p_rules controlling how to parse the string outside of
1591 * any expansions.
1592 * @param[in] t_rules controlling how attribute references are parsed.
1593 * @param[in] spaces whether the arguments are delimited by spaces
1594 * @return
1595 * - < 0 on error.
1596 * - >0 on success which is the number of characters parsed.
1597 */
1599 xlat_t const *xlat,
1600 fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules, bool spaces)
1601{
1602 int argc;
1603 fr_sbuff_t our_in = FR_SBUFF(in);
1604 ssize_t slen;
1606 fr_sbuff_parse_rules_t const *our_p_rules; /* Bareword parse rules */
1607 fr_sbuff_parse_rules_t tmp_p_rules;
1609 xlat_arg_parser_t const *arg = NULL, *arg_start;
1610 tmpl_rules_t arg_t_rules;
1611
1612 if (xlat && xlat->args) {
1613 arg_start = arg = xlat->args; /* Track the arguments as we parse */
1614 } else {
1615 static xlat_arg_parser_t const default_arg[] = { { .variadic = XLAT_ARG_VARIADIC_EMPTY_SQUASH, .type = FR_TYPE_VOID },
1617 arg_start = arg = &default_arg[0];
1618 }
1619 arg_t_rules = *t_rules;
1620
1621 if (unlikely(spaces)) {
1623 if (p_rules) { /* only for tmpl_tokenize, and back-ticks */
1624 fr_assert(p_rules->terminals);
1625
1626 tmp_p_rules = (fr_sbuff_parse_rules_t){ /* Stack allocated due to CL scope */
1627 .terminals = fr_sbuff_terminals_amerge(NULL, p_rules->terminals,
1629 .escapes = (p_rules->escapes ? p_rules->escapes : value_parse_rules_bareword_quoted.escapes)
1630 };
1631 our_p_rules = &tmp_p_rules;
1632 } else {
1633 our_p_rules = &value_parse_rules_bareword_quoted;
1634 }
1635
1636 } else {
1638 fr_assert(p_rules->terminals);
1639
1640 our_p_rules = p_rules;
1641
1642 /*
1643 * The arguments to a function are NOT the output data type of the function.
1644 *
1645 * We do NOT check for quotation characters. We DO update t_rules to strip any casts. The
1646 * OUTPUT of the function is cast to the relevant data type, but each ARGUMENT is just an
1647 * expression with no given data type. Parsing the expression is NOT done with the cast of
1648 * arg->type, as that means each individual piece of the expression is parsed as the type. We
1649 * have to cast on the final _output_ of the expression, and we allow the _input_ pieces of the
1650 * expression to be just about anything.
1651 */
1652 arg_t_rules.enumv = NULL;
1653 arg_t_rules.cast = FR_TYPE_NULL;
1654 arg_t_rules.attr.namespace = NULL;
1655 arg_t_rules.attr.request_def = NULL;
1656 arg_t_rules.attr.list_def = request_attr_request;
1658 }
1659
1661
1662 /*
1663 * skip spaces at the beginning as we don't want them to become a whitespace literal.
1664 */
1665 fr_sbuff_adv_past_whitespace(&our_in, SIZE_MAX, NULL);
1666 fr_sbuff_marker(&m, &our_in);
1667 argc = 1;
1668
1669 while (fr_sbuff_extend(&our_in)) {
1670 xlat_exp_t *node = NULL;
1671 fr_token_t quote;
1672 size_t len;
1673
1674 arg_t_rules.literals_safe_for = arg->safe_for;
1675
1676 fr_sbuff_adv_past_whitespace(&our_in, SIZE_MAX, NULL);
1677 fr_sbuff_set(&m, &our_in); /* Record start of argument */
1678
1679 MEM(node = xlat_exp_alloc(ctx, XLAT_GROUP, NULL, 0)); /* quote = T_BARE_WORD */
1680
1681 if (likely(!spaces)) {
1682 /*
1683 * We've reached the end of the arguments, don't try to tokenize anything else.
1684 */
1685 if (fr_sbuff_is_char(&our_in, ')')) {
1686 slen = 0;
1687
1688 } else {
1689 /*
1690 * Parse a full expression as an argv, all the way to a terminal character.
1691 * We use the input parse rules here.
1692 */
1693 slen = xlat_tokenize_expression(node, &node->group, &our_in, our_p_rules, &arg_t_rules);
1694 }
1695 } else {
1697
1698 node->quote = quote;
1699
1700 if (quote == T_BARE_WORD) {
1701 /*
1702 * Each argument is a bare word all by itself, OR an xlat thing all by itself.
1703 */
1704 slen = xlat_tokenize_input(node->group, &our_in, our_p_rules, &arg_t_rules);
1705
1706 } else {
1707 xlat_exp_t *child = NULL;
1708
1709 slen = xlat_tokenize_word(node->group, &child, &our_in, quote, our_p_rules, &arg_t_rules);
1710 if (child) {
1711 fr_assert(slen > 0);
1712
1713 xlat_exp_insert_tail(node->group, child);
1714 }
1715 }
1716 }
1717
1718 if (slen < 0) {
1719 error:
1720 if (our_p_rules == &tmp_p_rules) talloc_const_free(our_p_rules->terminals);
1722
1723 FR_SBUFF_ERROR_RETURN(&our_in); /* error */
1724 }
1725 fr_assert(node != NULL);
1726
1727 /*
1728 * No data, but the argument was required. Complain.
1729 */
1730 if (!slen && arg->required) {
1731 fr_strerror_printf("Missing required arg %u", argc);
1732 goto error;
1733 }
1734
1735 fr_assert(node->type == XLAT_GROUP);
1736 node->flags = node->group->flags;
1737
1738 /*
1739 * Check number of arguments.
1740 */
1741 if (arg->type == FR_TYPE_NULL) {
1742 fr_strerror_printf("Too many arguments, expected %zu, got %d",
1743 (size_t) (arg - arg_start), argc);
1744 fr_sbuff_set(&our_in, &m);
1745 goto error;
1746 }
1747
1748 if (!node->fmt) xlat_exp_set_name(node, fr_sbuff_current(&m), fr_sbuff_behind(&m));
1749
1750 /*
1751 * Ensure that the function args are correct.
1752 */
1753 if (xlat_validate_function_arg(arg, node, argc) < 0) {
1754 fr_sbuff_set(&our_in, &m);
1755 goto error;
1756 }
1757
1759
1760 /*
1761 * If we're not and the end of the string
1762 * and there's no whitespace between tokens
1763 * then error.
1764 */
1765 fr_sbuff_set(&m, &our_in);
1766 len = fr_sbuff_adv_past_whitespace(&our_in, SIZE_MAX, NULL);
1767
1768 /*
1769 * Commas are in the list of terminals, but we skip over them, and keep parsing more
1770 * arguments.
1771 */
1772 if (!spaces) {
1773 fr_assert(p_rules && p_rules->terminals);
1774
1775 if (fr_sbuff_next_if_char(&our_in, ',')) goto next;
1776
1777 if (fr_sbuff_is_char(&our_in, ')')) break;
1778
1779 if (fr_sbuff_eof(&our_in)) {
1780 fr_strerror_printf("Missing ')' after argument %d", argc);
1781 goto error;
1782 }
1783
1784 fr_strerror_printf("Unexpected text after argument %d", argc);
1785 goto error;
1786 }
1787
1788 /*
1789 * Check to see if we have a terminal char, which at this point has to be '``.
1790 */
1791 if (our_p_rules->terminals) {
1792 if (fr_sbuff_is_terminal(&our_in, our_p_rules->terminals)) break;
1793
1794 if (fr_sbuff_eof(&our_in)) {
1795 fr_strerror_printf("Unexpected end of input string after argument %d", argc);
1796 goto error;
1797 }
1798 }
1799
1800 /*
1801 * Otherwise, if we can extend, and found
1802 * no additional whitespace, it means two
1803 * arguments were smushed together.
1804 */
1805 if (fr_sbuff_extend(&our_in) && (len == 0)) {
1806 fr_strerror_const("Unexpected text after argument");
1807 goto error;
1808 }
1809 next:
1810 if (!arg->variadic) {
1811 arg++;
1812 argc++;
1813
1814 if (arg->type == FR_TYPE_NULL) {
1815 fr_strerror_printf("Too many arguments, expected %zu, got %d",
1816 (size_t) (arg - arg_start), argc);
1817 goto error;
1818 }
1819 }
1820 }
1821
1822 if (our_p_rules == &tmp_p_rules) talloc_const_free(our_p_rules->terminals);
1823
1825 *out = head;
1826
1827 FR_SBUFF_SET_RETURN(in, &our_in);
1828}
1829
1830/** Tokenize an xlat expansion
1831 *
1832 * @param[in] ctx to allocate dynamic buffers in.
1833 * @param[out] out the head of the xlat list / tree structure.
1834 * @param[in] in the format string to expand.
1835 * @param[in] p_rules controlling how the string containing the xlat
1836 * expansions should be parsed.
1837 * @param[in] t_rules controlling how attribute references are parsed.
1838 * @return
1839 * - >0 on success.
1840 * - 0 and *head == NULL - Parse failure on first char.
1841 * - 0 and *head != NULL - Zero length expansion
1842 * - < 0 the negative offset of the parse failure.
1843 */
1845 fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules)
1846{
1847 fr_sbuff_t our_in = FR_SBUFF(in);
1849
1851 fr_strerror_clear(); /* Clear error buffer */
1852
1853 if (xlat_tokenize_input(head, &our_in, p_rules, t_rules) < 0) {
1855 FR_SBUFF_ERROR_RETURN(&our_in);
1856 }
1857
1858 /*
1859 * Add nodes that need to be bootstrapped to
1860 * the registry.
1861 */
1862 if (xlat_finalize(head, t_rules->xlat.runtime_el) < 0) {
1864 return 0;
1865 }
1866
1868 *out = head;
1869
1870 FR_SBUFF_SET_RETURN(in, &our_in);
1871}
1872
1873/** Check to see if the expansion consists entirely of value-box elements
1874 *
1875 * @param[in] head to check.
1876 * @return
1877 * - true if expansion contains only literal elements.
1878 * - false if expansion contains expandable elements.
1879 */
1881{
1882 xlat_exp_foreach(head, node) {
1883 if (node->type != XLAT_BOX) return false;
1884 }
1885
1886 return true;
1887}
1888
1889/** Check to see if the expansion needs resolving
1890 *
1891 * @param[in] head to check.
1892 * @return
1893 * - true if expansion needs resolving
1894 * - false otherwise
1895 */
1897{
1898 return head->flags.needs_resolving;
1899}
1900
1901/** Convert an xlat node to an unescaped literal string and free the original node
1902 *
1903 * This is really "unparse the xlat nodes, and convert back to their original string".
1904 *
1905 * @param[in] ctx to allocate the new string in.
1906 * @param[out] str a duplicate of the node's fmt string.
1907 * @param[in,out] head to convert.
1908 * @return
1909 * - true the tree consists of a single value node which was converted.
1910 * - false the tree was more complex than a single literal, op was a noop.
1911 */
1912bool xlat_to_string(TALLOC_CTX *ctx, char **str, xlat_exp_head_t **head)
1913{
1916 size_t len = 0;
1917
1918 if (!*head) return false;
1919
1920 /*
1921 * Instantiation functions may chop
1922 * up the node list into multiple
1923 * literals, so we need to walk the
1924 * list until we find a non-literal.
1925 */
1926 xlat_exp_foreach(*head, node) {
1927 if (node->type != XLAT_BOX) return false;
1928 len += talloc_array_length(node->fmt) - 1;
1929 }
1930
1931 fr_sbuff_init_talloc(ctx, &out, &tctx, len, SIZE_MAX);
1932
1933 xlat_exp_foreach(*head, node) {
1935 }
1936
1937 *str = fr_sbuff_buff(&out); /* No need to trim, should be the correct length */
1938
1939 return true;
1940}
1941
1942/** Walk over an xlat tree recursively, resolving any unresolved functions or references
1943 *
1944 * @param[in,out] head of xlat tree to resolve.
1945 * @param[in] xr_rules Specifies rules to use for resolution passes after initial
1946 * tokenization.
1947 * @return
1948 * - 0 on success.
1949 * - -1 on failure.
1950 */
1952{
1953 static xlat_res_rules_t xr_default;
1954 xlat_flags_t our_flags;
1955 xlat_t *func;
1956
1957 if (!head->flags.needs_resolving) return 0; /* Already done */
1958
1959 if (!xr_rules) xr_rules = &xr_default;
1960
1961 our_flags = XLAT_FLAGS_INIT;
1962
1963 xlat_exp_foreach(head, node) {
1964 /*
1965 * This node and none of its children need resolving
1966 */
1967 if (!node->flags.needs_resolving) {
1968 xlat_flags_merge(&our_flags, &node->flags);
1969 continue;
1970 }
1971
1972 switch (node->type) {
1973 case XLAT_GROUP:
1974 if (xlat_resolve(node->group, xr_rules) < 0) return -1;
1975 node->flags = node->group->flags;
1976 break;
1977
1978 /*
1979 * An unresolved function.
1980 */
1982 /*
1983 * Try to find the function
1984 */
1985 func = xlat_func_find(node->fmt, talloc_array_length(node->fmt) - 1);
1986 if (!func) {
1987 /*
1988 * FIXME - Produce proper error with marker
1989 */
1990 if (!xr_rules->allow_unresolved) {
1991 fr_strerror_printf("Failed resolving function \"%pV\"",
1993 return -1;
1994 }
1995 break;
1996 }
1997
1999 xlat_exp_set_func(node, func, xr_rules->tr_rules->dict_def);
2000
2001 /*
2002 * Check input arguments of our freshly resolved function
2003 */
2004 if (xlat_validate_function_args(node) < 0) return -1;
2005
2006 /*
2007 * Add the freshly resolved function
2008 * to the bootstrap tree.
2009 */
2010 if (xlat_instance_register_func(node) < 0) return -1;
2011
2012 /*
2013 * The function is now resolved, so we go through the normal process of resolving
2014 * its arguments, etc.
2015 */
2017
2018 /*
2019 * A resolved function with unresolved args. We re-initialize the flags from the
2020 * function definition, resolve the arguments, and update the flags.
2021 */
2022 case XLAT_FUNC:
2023 node->flags = node->call.func->flags;
2024
2025 if (node->call.func->resolve) {
2026 void *inst = node->call.inst ? node->call.inst->data : NULL;
2027
2028 if (node->call.func->resolve(node, inst, xr_rules) < 0) return -1;
2029
2030 } else if (node->call.args) {
2031 if (xlat_resolve(node->call.args, xr_rules) < 0) return -1;
2032
2033 } /* else the function takes no arguments */
2034
2035 node->flags.needs_resolving = false;
2037 break;
2038
2039 case XLAT_TMPL:
2040 /*
2041 * Resolve any nested xlats in regexes, exec, or xlats.
2042 */
2043 if (tmpl_resolve(node->vpt, xr_rules->tr_rules) < 0) return -1;
2044
2045 fr_assert(!tmpl_needs_resolving(node->vpt));
2046 node->flags.needs_resolving = false;
2047
2048 if (xlat_tmpl_normalize(node) < 0) return -1;
2049 break;
2050
2051 default:
2052 fr_assert(0); /* boxes, one letter, etc. should not have been marked as unresolved */
2053 return -1;
2054 }
2055
2056 xlat_flags_merge(&our_flags, &node->flags);
2057 }
2058
2059 head->flags = our_flags;
2060
2061 fr_assert(!head->flags.needs_resolving);
2062
2063 return 0;
2064}
2065
2066
2067/** Try to convert an xlat to a tmpl for efficiency
2068 *
2069 * @param ctx to allocate new tmpl_t in.
2070 * @param head to convert.
2071 * @return
2072 * - NULL if unable to convert (not necessarily error).
2073 * - A new #tmpl_t.
2074 */
2076{
2077 tmpl_t *vpt;
2078 xlat_exp_t *node = xlat_exp_head(head);
2079
2080 if (!node || (node->type != XLAT_TMPL) || !tmpl_is_attr(node->vpt)) return NULL;
2081
2082 /*
2083 * Concat means something completely different as an attribute reference
2084 * Count isn't implemented.
2085 */
2086 if ((tmpl_attr_tail_num(node->vpt) == NUM_COUNT) || (tmpl_attr_tail_num(node->vpt) == NUM_ALL)) return NULL;
2087
2088 vpt = tmpl_alloc(ctx, TMPL_TYPE_ATTR, T_BARE_WORD, node->fmt, talloc_array_length(node->fmt) - 1);
2089 if (!vpt) return NULL;
2090
2091 tmpl_attr_copy(vpt, node->vpt);
2092
2094
2095 return vpt;
2096}
2097
2099{
2100 return head->flags.impure_func;
2101}
2102
2103/*
2104 * Try to determine the output data type of an expansion.
2105 *
2106 * This is only a best guess for now.
2107 */
2109{
2110 xlat_exp_t *node;
2111
2112 node = xlat_exp_head(head);
2113 fr_assert(node);
2114
2115 if (xlat_exp_next(head, node)) return FR_TYPE_NULL;
2116
2117 if (node->quote != T_BARE_WORD) return FR_TYPE_STRING;
2118
2119 if (node->type == XLAT_FUNC) {
2120 return node->call.func->return_type;
2121 }
2122
2123 if (node->type == XLAT_TMPL) {
2124 return tmpl_data_type(node->vpt);
2125 }
2126
2127 return FR_TYPE_NULL;
2128}
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:485
#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:324
#define unlikely(_x)
Definition build.h:383
#define NUM_ELEMENTS(_t)
Definition build.h:339
#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:370
#define ERROR(fmt,...)
Definition dhcpclient.c:41
#define DEBUG(fmt,...)
Definition dhcpclient.c:39
static fr_slen_t err
Definition dict.h:840
static fr_slen_t in
Definition dict.h:840
static unsigned int fr_dlist_num_elements(fr_dlist_head_t const *head)
Return the number of elements in the dlist.
Definition dlist.h:939
static void * fr_dlist_pop_head(fr_dlist_head_t *list_head)
Remove the head item in a list.
Definition dlist.h:672
#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)
fr_type_t
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_NULL
Invalid (uninitialised) attribute type.
@ FR_TYPE_VOID
User data.
@ FR_TYPE_BOOL
A truth value.
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:81
fr_dict_attr_t const * request_attr_request
Definition request.c:43
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:1779
bool fr_sbuff_is_terminal(fr_sbuff_t *in, fr_sbuff_term_t const *tt)
Efficient terminal string search.
Definition sbuff.c:2154
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:1854
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:1504
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:647
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:2090
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_is_str_literal(_sbuff, _str)
#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_advance(_sbuff_or_marker, _len)
#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:885
#define tmpl_contains_xlat(vpt)
Definition tmpl.h:227
#define TMPL_VERIFY(_vpt)
Definition tmpl.h:961
#define tmpl_is_xlat(vpt)
Definition tmpl.h:210
#define tmpl_is_attr_unresolved(vpt)
Definition tmpl.h:219
#define tmpl_contains_data(vpt)
Definition tmpl.h:224
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:937
static fr_slen_t rql fr_slen_t tmpl_attr_print(fr_sbuff_t *out, tmpl_t const *vpt)
Print an attribute or list tmpl_t to a string.
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.
int tmpl_attr_unknown_add(tmpl_t *vpt)
Add an unknown fr_dict_attr_t specified by a tmpl_t to the main dictionary.
#define tmpl_contains_regex(vpt)
Definition tmpl.h:226
fr_value_box_safe_for_t literals_safe_for
safe_for value assigned to literal values in xlats, execs, and data.
Definition tmpl.h:347
#define tmpl_is_attr(vpt)
Definition tmpl.h:208
#define NUM_ALL
Definition tmpl.h:391
fr_dict_attr_t const * enumv
Enumeration attribute used to resolve enum values.
Definition tmpl.h:338
#define tmpl_value_enumv(_tmpl)
Definition tmpl.h:940
#define tmpl_xlat(_tmpl)
Definition tmpl.h:930
static fr_dict_attr_t const * tmpl_list(tmpl_t const *vpt)
Definition tmpl.h:904
#define tmpl_rules_cast(_tmpl)
Definition tmpl.h:942
@ TMPL_TYPE_ATTR
Reference to one or more attributes.
Definition tmpl.h:142
#define NUM_COUNT
Definition tmpl.h:392
ssize_t tmpl_afrom_substr(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in, fr_token_t quote, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules))
Convert an arbitrary string into a tmpl_t.
fr_type_t tmpl_data_type(tmpl_t const *vpt)
Definition tmpl_eval.c:1335
tmpl_xlat_rules_t xlat
Rules/data for parsing xlats.
Definition tmpl.h:336
bool at_runtime
Produce an ephemeral/runtime tmpl.
Definition tmpl.h:344
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:206
static fr_slen_t vpt
Definition tmpl.h:1269
fr_dict_t const * dict_def
Alternative default dictionary to use if vpt->rules->dict_def is NULL.
Definition tmpl.h:365
#define NUM_UNSPEC
Definition tmpl.h:390
#define tmpl_value_type(_tmpl)
Definition tmpl.h:939
static fr_type_t tmpl_cast_get(tmpl_t *vpt)
Definition tmpl.h:1218
tmpl_attr_error_t
Definition tmpl.h:1004
@ TMPL_ATTR_ERROR_MISSING_TERMINATOR
Unexpected text found after attribute reference.
Definition tmpl.h:1026
#define tmpl_is_data_unresolved(vpt)
Definition tmpl.h:217
fr_type_t cast
Whether there was an explicit cast.
Definition tmpl.h:340
tmpl_attr_rules_t attr
Rules/data for parsing attribute references.
Definition tmpl.h:335
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:801
@ TMPL_ATTR_LIST_ALLOW
Attribute refs are allowed to have a list.
Definition tmpl.h:262
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:915
fr_event_list_t * runtime_el
The eventlist to use for runtime instantiation of xlats.
Definition tmpl.h:324
#define tmpl_needs_resolving(vpt)
Definition tmpl.h:223
Optional arguments passed to vp_tmpl functions.
Definition tmpl.h:332
eap_aka_sim_process_conf_t * inst
Define entry and head types for tmpl request references.
Definition tmpl.h:272
tmpl_attr_list_presence_t list_presence
Whether the attribute reference can have a list, forbid it, or require it.
Definition tmpl.h:298
fr_dict_attr_t const * list_def
Default list to use with unqualified attribute reference.
Definition tmpl.h:295
uint8_t allow_unresolved
Allow attributes that look valid but were not found in the dictionaries.
Definition tmpl.h:304
fr_dict_t const * dict_def
Default dictionary to use with unqualified attribute references.
Definition tmpl.h:273
uint8_t allow_wildcard
Allow the special case of .
Definition tmpl.h:309
Define manipulation functions for the attribute reference list.
Definition tmpl.h:471
tmpl_request_ref_t _CONST request
Definition tmpl.h:475
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:467
static int talloc_const_free(void const *ptr)
Free const'd memory.
Definition talloc.h:229
const char fr_token_quote[T_TOKEN_LAST]
Convert tokens back to a quoting character.
Definition token.c:157
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
@ T_SOLIDUS_QUOTED_STRING
Definition token.h:124
close(uq->fd)
tmpl_res_rules_t const * tr_rules
tmpl resolution rules.
Definition xlat.h:164
fr_type_t type
Type to cast argument to.
Definition xlat.h:154
#define XLAT_HEAD_VERIFY(_head)
Definition xlat.h:466
uint8_t xlat
it's an xlat wrapper
Definition xlat.h:114
#define XLAT_FLAGS_INIT
Definition xlat.h:117
int xlat_instance_register_func(xlat_exp_t *node)
Callback for creating "permanent" instance data for a xlat_exp_t.
Definition xlat_inst.c:593
xlat_escape_func_t func
Function to handle tainted values.
Definition xlat.h:155
bool allow_unresolved
If false, all resolution steps must be completed.
Definition xlat.h:165
@ XLAT_ARG_VARIADIC_EMPTY_SQUASH
Empty argument groups are removed.
Definition xlat.h:135
static fr_slen_t head
Definition xlat.h:419
xlat_arg_parser_variadic_t variadic
All additional boxes should be processed using this definition.
Definition xlat.h:152
uint8_t will_escape
the function will do escaping and concatenation.
Definition xlat.h:149
fr_value_box_safe_for_t safe_for
Escaped value to set for boxes processed by this escape function.
Definition xlat.h:156
uint8_t required
Argument must be present, and non-empty.
Definition xlat.h:145
uint8_t needs_resolving
Needs pass2 resolution.
Definition xlat.h:108
uint8_t can_purify
if the xlat has a pure function with pure arguments.
Definition xlat.h:111
uint8_t pure
has no external side effects, true for BOX, LITERAL, and some functions
Definition xlat.h:109
#define XLAT_ARG_PARSER_TERMINATOR
Definition xlat.h:169
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:693
uint8_t constant
xlat is just tmpl_attr_tail_data, or XLAT_BOX
Definition xlat.h:113
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:3120
Definition for a single argument consumend by an xlat function.
Definition xlat.h:144
Flags that control resolution and evaluation.
Definition xlat.h:107
char const * fr_strerror(void)
Get the last library error.
Definition strerror.c:553
void fr_strerror_clear(void)
Clears all pending messages from the talloc pools.
Definition strerror.c:576
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
#define fr_strerror_const(_msg)
Definition strerror.h:223
@ FR_TYPE_PAIR_CURSOR
cursor over a fr_pair_t
Definition types.h:89
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
Definition types.h:450
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:5496
fr_sbuff_parse_rules_t const * value_parse_rules_quoted[T_TOKEN_LAST]
Parse rules for quoted strings.
Definition value.c:605
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:3790
fr_sbuff_escape_rules_t * fr_value_escape_by_quote[T_TOKEN_LAST]
Definition value.c:440
fr_sbuff_parse_rules_t const * value_parse_rules_3quoted[T_TOKEN_LAST]
Definition value.c:621
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:4158
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:5690
fr_sbuff_parse_rules_t const value_parse_rules_bareword_quoted
Definition value.c:523
int fr_value_box_bstrndup(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, size_t len, bool tainted)
Copy a string to to a fr_value_box_t.
Definition value.c:4379
#define fr_value_box_mark_safe_for(_box, _safe_for)
Definition value.h:1071
#define fr_box_strvalue_buffer(_val)
Definition value.h:308
#define fr_box_strvalue_len(_val, _len)
Definition value.h:305
uintptr_t fr_value_box_safe_for_t
Escaping that's been applied to a value box.
Definition value.h:160
int nonnull(2, 5))
#define fr_value_box_init(_vb, _type, _enumv, _tainted)
Initialise a fr_value_box_t.
Definition value.h:606
static size_t char ** out
Definition value.h:1020
void xlat_exp_finalize_func(xlat_exp_t *node)
Definition xlat_alloc.c:284
void xlat_exp_set_vpt(xlat_exp_t *node, tmpl_t *vpt)
Set the tmpl for a node, along with flags and the name.
Definition xlat_alloc.c:252
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:308
void xlat_exp_set_func(xlat_exp_t *node, xlat_t const *func, fr_dict_t const *dict)
Set the function for a node.
Definition xlat_alloc.c:274
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:338
fr_dict_attr_t const * attr_expr_bool_enum
Definition xlat_eval.c:42
xlat_t * xlat_func_find(char const *in, ssize_t inlen)
Definition xlat_func.c:77
#define xlat_exp_head_alloc(_ctx)
Definition xlat_priv.h:273
xlat_flags_t flags
Flags that control resolution and evaluation.
Definition xlat_priv.h:154
#define xlat_exp_alloc_null(_ctx)
Definition xlat_priv.h:279
static xlat_exp_t * xlat_exp_next(xlat_exp_head_t const *head, xlat_exp_t const *node)
Definition xlat_priv.h:246
int xlat_tokenize_regex(xlat_exp_head_t *head, xlat_exp_t **out, fr_sbuff_t *in, fr_sbuff_marker_t *m_s)
fr_token_t quote
Type of quoting around XLAT_GROUP types.
Definition xlat_priv.h:152
@ XLAT_ONE_LETTER
Special "one-letter" expansion.
Definition xlat_priv.h:109
@ XLAT_BOX
fr_value_box_t
Definition xlat_priv.h:108
@ XLAT_TMPL
xlat attribute
Definition xlat_priv.h:112
@ XLAT_FUNC
xlat module
Definition xlat_priv.h:110
@ XLAT_GROUP
encapsulated string of xlats
Definition xlat_priv.h:116
@ XLAT_FUNC_UNRESOLVED
func needs resolution during pass2.
Definition xlat_priv.h:111
@ XLAT_INVALID
Bad expansion.
Definition xlat_priv.h:107
xlat_arg_parser_t const * args
Definition of args consumed.
Definition xlat_priv.h:94
static void xlat_flags_merge(xlat_flags_t *parent, xlat_flags_t const *child)
Merge flags from child to parent.
Definition xlat_priv.h:229
#define xlat_exp_set_type(_node, _type)
Definition xlat_priv.h:276
char const *_CONST fmt
The original format string (a talloced buffer).
Definition xlat_priv.h:151
xlat_type_t _CONST type
type of this expansion.
Definition xlat_priv.h:155
#define xlat_exp_alloc(_ctx, _type, _in, _inlen)
Definition xlat_priv.h:282
#define xlat_exp_foreach(_list_head, _iter)
Iterate over the contents of a list, only one level.
Definition xlat_priv.h:222
static int xlat_exp_insert_tail(xlat_exp_head_t *head, xlat_exp_t *node)
Definition xlat_priv.h:238
static xlat_exp_t * xlat_exp_head(xlat_exp_head_t const *head)
Definition xlat_priv.h:209
An xlat expansion node.
Definition xlat_priv.h:148
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)
Validate and sanity check function arguments.
bool const xlat_func_chars[UINT8_MAX+1]
int xlat_validate_function_args(xlat_exp_t *node)
static fr_table_num_sorted_t const xlat_quote_table[]
void xlat_debug_head(xlat_exp_head_t const *head)
static void _xlat_debug_head(xlat_exp_head_t const *head, int depth)
bool xlat_impure_func(xlat_exp_head_t const *head)
fr_slen_t xlat_tokenize_word(TALLOC_CTX *ctx, xlat_exp_t **out, fr_sbuff_t *in, fr_token_t quote, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules)
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, bool print_flags)
static int xlat_tmpl_normalize(xlat_exp_t *node)
Normalize an xlat which contains a tmpl.
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)
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.
static int xlat_tokenize_expansion(xlat_exp_head_t *head, fr_sbuff_t *in, tmpl_rules_t const *t_rules)
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)
Parse an attribute ref or a virtual attribute.
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_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.
fr_type_t xlat_data_type(xlat_exp_head_t const *head)