The FreeRADIUS server $Id: a4e7f8c94e8038b7627bcf52356b7ed15955c1d7 $
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: 35a9ea3cfc45a5d8a2604bba8cf78a30e5210ebd $
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: 35a9ea3cfc45a5d8a2604bba8cf78a30e5210ebd $")
30
31#include <freeradius-devel/util/debug.h>
32#include <freeradius-devel/util/event.h>
33#include <freeradius-devel/util/value.h>
34#include <freeradius-devel/server/regex.h>
35#include <freeradius-devel/unlang/xlat_priv.h>
36
37#undef XLAT_DEBUG
38#undef XLAT_HEXDUMP
39#ifdef DEBUG_XLAT
40# define XLAT_DEBUG(_fmt, ...) DEBUG3("%s[%i] "_fmt, __FILE__, __LINE__, ##__VA_ARGS__)
41# define XLAT_HEXDUMP(_data, _len, _fmt, ...) HEXDUMP3(_data, _len, "%s[%i] "_fmt, __FILE__, __LINE__, ##__VA_ARGS__)
42#else
43# define XLAT_DEBUG(...)
44# define XLAT_HEXDUMP(...)
45#endif
46
47/** These rules apply to literal values and function arguments inside of an expansion
48 *
49 */
51 .name = "xlat",
52 .chr = '\\',
53 .subs = {
54 ['a'] = '\a',
55 ['b'] = '\b',
56 ['e'] = '\\', /* escape character, not \e */
57 ['n'] = '\n',
58 ['r'] = '\r',
59 ['t'] = '\t',
60 ['v'] = '\v',
61 ['\\'] = '\\',
62 ['%'] = '%', /* Expansion begin */
63 ['}'] = '}' /* Expansion end */
64 },
65 .do_hex = true,
66 .do_oct = true
67};
68
69/** These rules apply to literal values and function arguments inside of an expansion
70 *
71 */
73 .name = "xlat",
74 .chr = '\\',
75 .subs = {
76 ['\a'] = 'a',
77 ['\b'] = 'b',
78 ['\n'] = 'n',
79 ['\r'] = 'r',
80 ['\t'] = 't',
81 ['\v'] = 'v',
82 ['\\'] = '\\',
83 ['%'] = '%', /* Expansion begin */
84 ['}'] = '}' /* Expansion end */
85 },
86 .esc = {
89 },
90 .do_utf8 = true,
91 .do_oct = true
92};
93
94/** Parse rules for literal values inside of an expansion
95 *
96 * These rules are used to parse literals as arguments to functions.
97 *
98 * The caller sets the literal parse rules for outside of expansions when they
99 * call xlat_tokenize.
100 */
101static fr_sbuff_parse_rules_t const xlat_function_arg_rules = {
102 .escapes = &xlat_unescape,
103 .terminals = &FR_SBUFF_TERMS( /* These get merged with other literal terminals */
104 L(")"),
105 L(","),
106 ),
107};
108
109#ifdef HAVE_REGEX
110/** Parse an xlat reference
111 *
112 * Allows access to a subcapture groups
113 * @verbatim %{<num>} @endverbatim
114 */
116{
117 uint8_t num;
118 xlat_exp_t *node;
119
120 XLAT_DEBUG("REGEX <-- %.*s", (int) fr_sbuff_remaining(in), fr_sbuff_current(in));
121
122 /*
123 * Not a number, ignore it.
124 */
125 if (fr_sbuff_out(&num, in) < 0) return 0;
126
127 /*
128 * Not %{\d+}, ignore it.
129 */
130 if (!fr_sbuff_is_char(in, '}')) return 0;
131
132 /*
133 * It is a regex ref, but it has to be a valid one.
134 */
135 if (num > REQUEST_MAX_REGEX) {
136 fr_strerror_printf("Invalid regex reference. Must be in range 0-%d", REQUEST_MAX_REGEX);
137 fr_sbuff_set(in, m_s);
138 return -1;
139 }
140
141 MEM(node = xlat_exp_alloc(head, XLAT_REGEX, fr_sbuff_current(m_s), fr_sbuff_behind(m_s)));
142 node->regex_index = num;
143
144 XLAT_VERIFY(node);
145
146 *out = node;
147
148 (void) fr_sbuff_advance(in, 1); /* must be '}' */
149 return 1;
150}
151#endif
152
155 ['.'] = true, ['-'] = true, ['_'] = true,
156};
157
158
159/** Normalize an xlat which contains a tmpl.
160 *
161 * Constant data is turned into XLAT_BOX, and some other thingies are done.
162 */
164{
165 tmpl_t *vpt = node->vpt;
166
167 XLAT_VERIFY(node);
168
170
171 if (tmpl_is_attr_unresolved(node->vpt)) {
172 return 0;
173 }
174
175 /*
176 * Add in unknown attributes, by defining them in the local dictionary.
177 */
178 if (tmpl_is_attr(vpt)) {
179 if (tmpl_attr_unknown_add(vpt) < 0) {
180 fr_strerror_printf("Failed defining attribute %s", vpt->name);
181 return -1;
182 }
183
184 /*
185 * The caller should have omitted duplicate casts.
186 */
188 ((tmpl_attr_tail_da(vpt) != NULL) &&
190 return 0;
191 }
192
193 /*
194 * Most callers should hoist xlat -> tmpl -> xlat to just an xlat.
195 */
196 if (tmpl_contains_xlat(vpt)) {
199 return 0;
200 }
201
202 /*
203 * @todo - check what `exec` returns. Arguably we should convert `exec` to %exec(...)
204 */
205 if (tmpl_is_exec(vpt)) {
206 return 0;
207 }
208
209 if (tmpl_is_data_unresolved(vpt) && (tmpl_resolve(vpt, NULL) < 0)) return -1;
210
211 /*
212 * If we don't have data, then don't do anything with it.
213 */
214 if (!tmpl_is_data(vpt)) return 0;
215
216 /*
217 * The caller should have cast the data to the correct data type.
218 */
220
221 /*
222 * Print "true" and "false" instead of "yes" and "no".
223 */
226 }
227
228 /*
229 * Convert the XLAT_TMPL to XLAT_BOX
230 */
232 XLAT_VERIFY(node);
233
234 return 0;
235}
236
237/** Validate and sanity check function arguments.
238 *
239 */
240static int xlat_validate_function_arg(xlat_arg_parser_t const *arg_p, xlat_exp_t *arg, unsigned int argc)
241{
242 xlat_exp_t *node;
243
244 fr_assert(arg->type == XLAT_GROUP);
245
246 /*
247 * "is_argv" does dual duty. One, it causes xlat_print() to print spaces in between arguments.
248 *
249 * Two, it is checked by xlat_frame_eval_repeat(), which then does NOT concatenate strings in
250 * place. Instead, it just passes the strings though to xlat_process_arg_list(). Which calls
251 * xlat_arg_stringify(), and that does the escaping and final concatenation.
252 */
253 arg->group->is_argv = (arg_p->func != NULL) | arg_p->will_escape;
254
255 node = xlat_exp_head(arg->group);
256
257 if (!node) {
258 if (!arg_p->required) return 0;
259
260 fr_strerror_const("Missing argument");
261 return -1;
262 }
263
264 /*
265 * The caller doesn't care about the type, we don't do any validation.
266 */
267 if (arg_p->type == FR_TYPE_VOID) return 0;
268
269 /*
270 * A cursor should be (for now) a named string.
271 */
272 if (arg_p->type == FR_TYPE_PAIR_CURSOR) {
273 if (node->type == XLAT_BOX) {
274 check_box:
275 if (node->data.type != FR_TYPE_STRING) {
276 fr_strerror_printf("Cursor must be a string attribute reference, not %s",
277 fr_type_to_str(node->data.type));
278 return -1;
279 }
280
281 return 0;
282 }
283
284 /*
285 * The expression parser should not allow anything else here.
286 */
287 fr_assert((node->type == XLAT_TMPL) || (node->type == XLAT_GROUP) || (node->type == XLAT_FUNC));
288
289 /*
290 * Func, etc.
291 */
292 if (node->type != XLAT_TMPL) return 0;
293
294 if (tmpl_rules_cast(node->vpt) != FR_TYPE_NULL) {
295 fr_strerror_const("Cursor cannot have cast");
296 return -1;
297 }
298
299 if (xlat_tmpl_normalize(node) < 0) return -1;
300
301 if (node->type == XLAT_BOX) goto check_box;
302
303 if (!tmpl_is_attr(node->vpt)) {
304 fr_strerror_printf("Invalid argument - expected attribute reference");
305 return -1;
306 }
307
308 /*
309 * Bare attribute references are allowed, but are marked up as "return a cursor to this
310 * thing, don't return a value".
311 */
312 arg->group->cursor = true;
313 return 0;
314 }
315
316 /*
317 * An attribute argument results in an FR_TYPE_ATTR box, rather than the value of the attribute
318 */
319 if (arg_p->type == FR_TYPE_ATTR) {
320 if (node->type != XLAT_TMPL) {
321 fr_strerror_printf("Attribute must be a bare word");
322 return -1;
323 }
324
325 if (xlat_tmpl_normalize(node) < 0) return -1;
326
327 if (!tmpl_is_attr(node->vpt)) {
328 fr_strerror_printf("Invalid argument - expected attribute reference");
329 return -1;
330 }
331
332 arg->group->is_attr = true;
333 return 0;
334 }
335
336 /*
337 * The argument is either ONE tmpl / value-box, OR is an
338 * xlat group which contains a double-quoted string.
339 */
340 fr_assert(fr_dlist_num_elements(&arg->group->dlist) == 1);
341
342 /*
343 * Do at least somewhat of a pass of normalizing the nodes, even if there are more than one.
344 */
345 if (node->type == XLAT_TMPL) {
346 return xlat_tmpl_normalize(node);
347 }
348
349 /*
350 * @todo - probably move the double-quoted string "node->flags.constant" check here, to more
351 * clearly separate parsing from normalization.
352 */
353
354 if (node->type != XLAT_BOX) {
355 return 0;
356 }
357
358 /*
359 * If it's the correct data type, then we don't need to do anything.
360 */
361 if (arg_p->type == node->data.type) {
362 return 0;
363 }
364
365 /*
366 * An explicit `null` literal is preserved unchanged - the xlat
367 * body receives an FR_TYPE_NULL box in this arg slot and can
368 * decide what to do with it. Casting would collapse it into a
369 * zero-length value of the declared type and hide the intent.
370 *
371 * Callers that marked the arg `required = true` are asking for
372 * a concrete value, not a placeholder - reject the literal up
373 * front so the config error surfaces at startup rather than on
374 * the first request that hits this node.
375 */
376 if (fr_type_is_null(node->data.type)) {
377 if (arg_p->required) {
378 fr_strerror_printf("Invalid argument %u - `null` is not allowed for a required argument", argc);
379 return -1;
380 }
381 return 0;
382 }
383
384 /*
385 * Cast (or parse) the input data to the expected argument data type.
386 */
387 if (fr_value_box_cast_in_place(node, &node->data, arg_p->type, NULL) < 0) {
388 fr_strerror_printf("Invalid argument %u - %s", argc, fr_strerror());
389 return -1;
390 }
391
392 return 0;
393}
394
396{
397 xlat_arg_parser_t const *arg_p;
398 xlat_exp_t *arg = xlat_exp_head(node->call.args);
399 unsigned int i = 1;
400
401 fr_assert(node->type == XLAT_FUNC);
402
403 /*
404 * Check the function definition against what the user passed in.
405 */
406 if (!node->call.func->args) {
407 if (node->call.args) {
408 fr_strerror_const("Too many arguments to function call, expected 0");
409 return -1;
410 }
411
412 /*
413 * Function takes no arguments, and none were passed in. There's nothing to verify.
414 */
415 return 0;
416 }
417
418 if (!node->call.args) {
419 fr_strerror_const("Too few arguments to function call");
420 return -1;
421 }
422
423 /*
424 * The function both has arguments defined, and the user has supplied them.
425 */
426 for (arg_p = node->call.func->args, i = 0; arg_p->type != FR_TYPE_NULL; arg_p++) {
427 if (!arg) {
428 if (arg_p->required) {
429 fr_strerror_printf("Missing required argument %u",
430 (unsigned int)(arg_p - node->call.func->args) + 1);
431 return -1;
432 }
433
434 /*
435 * No arg and not required, we can stop.
436 *
437 * If there is an arg, we validate it, even if it isn't required.
438 */
439 break;
440 }
441
442 /*
443 * All arguments MUST be put into a group, even
444 * if they're just one element.
445 */
446 fr_assert(arg->type == XLAT_GROUP);
447
448 if (xlat_validate_function_arg(arg_p, arg, i) < 0) return -1;
449
450 arg = xlat_exp_next(node->call.args, arg);
451 i++;
452 }
453
454 /*
455 * @todo - check if there is a trailing argument. But for functions which take no arguments, the
456 * "arg" is an empty group.
457 */
458
459 return 0;
460}
461
462/** Parse an xlat function and its child argument
463 *
464 * Parses a function call string in the format
465 * @verbatim %<func>(<argument>) @endverbatim
466 *
467 * @return
468 * - 0 if the string was parsed into a function.
469 * - <0 on parse error.
470 */
472{
473 char c;
474 xlat_exp_t *node;
475 xlat_t *func;
477 tmpl_rules_t my_t_rules;
478
479 tmpl_rules_copy_depth(&my_t_rules, t_rules);
480 if (tmpl_rules_depth_exceeded(&my_t_rules)) return -1;
481 t_rules = &my_t_rules;
482
483 fr_sbuff_marker(&m_s, in);
484
486
487 /*
488 * The caller ensures that the first character after the percent exists, and is alphanumeric.
489 */
490 c = fr_sbuff_char(in, '\0');
491
492 /*
493 * Even if it is alphanumeric, only a limited set of characters are one-letter expansions.
494 *
495 * And even then only if the character after them is a terminal character.
496 */
497 if (strchr("cCdDeGHIlmMnSstTY", c) != NULL) {
498 uint8_t n;
499
500 fr_sbuff_next(in);
501
502 /*
503 * End of buffer == one letter expansion.
504 */
505 n = fr_sbuff_uint8(in, '\0');
506 if (!n) goto one_letter;
507
508 /*
509 * %Y() is the new format.
510 */
511 if (n == '(') {
512 fr_sbuff_next(in);
513
514 if (!fr_sbuff_next_if_char(in, ')')) {
515 fr_strerror_const("Missing closing brace ')'");
516 return -1;
517 }
518
519 goto one_letter;
520 }
521
522 /*
523 * %M. or %Y- is a one-letter expansion followed by the other character.
524 */
525 if (!sbuff_char_alpha_num[n]) {
526 one_letter:
527 XLAT_DEBUG("ONE-LETTER <-- %c", c);
529
530 xlat_exp_set_name(node, fr_sbuff_current(&m_s), 1);
531 xlat_exp_set_type(node, XLAT_ONE_LETTER); /* needs node->fmt to be set */
532
533 fr_sbuff_marker_release(&m_s);
534
535#ifdef STATIC_ANALYZER
536 if (!node->fmt) return -1;
537#endif
538
540 return 0;
541 }
542
543 /*
544 * Anything else, it must be a full function name.
545 */
546 fr_sbuff_set(in, &m_s);
547 }
548
550
552
553 /*
554 * Private functions are created by the parser as part of expanding operators, and are not
555 * callable by name. Their arguments are built to invariants which only the parser knows, so
556 * pretend that they don't exist.
557 */
558 if (func && func->private) {
559 fr_strerror_printf("Unknown expansion function \"%.*s\"",
560 (int) fr_sbuff_behind(&m_s), fr_sbuff_current(&m_s));
561 fr_sbuff_set(in, &m_s); /* backtrack */
562 fr_sbuff_marker_release(&m_s);
563 return -1;
564 }
565
566 if (!fr_sbuff_is_char(in, '(')) {
567 fr_strerror_printf("Missing '('");
568 return -1;
569 }
570
571 /*
572 * Check for failure.
573 */
574 if (!func && (!t_rules->attr.allow_unresolved|| t_rules->at_runtime)) {
575 fr_strerror_const("Unresolved expansion functions are not allowed here");
576 fr_sbuff_set(in, &m_s); /* backtrack */
577 fr_sbuff_marker_release(&m_s);
578 return -1;
579 }
580
581 /*
582 * Allocate a node to hold the function
583 */
585 if (!func) {
587
588 } else {
589 xlat_exp_set_func(node, func, t_rules->attr.dict_def);
590 }
591
592 fr_sbuff_marker_release(&m_s);
593
594 (void) fr_sbuff_next(in); /* skip the '(' */
595
596 /*
597 * The caller might want the _output_ cast to something. But that doesn't mean we cast each
598 * _argument_ to the xlat function.
599 */
600 my_t_rules.cast = FR_TYPE_NULL;
601
602 /*
603 * Now parse the child nodes that form the
604 * function's arguments.
605 */
606 if (xlat_tokenize_argv(node, &node->call.args, in, func ? func->args : NULL,
607 &xlat_function_arg_rules, t_rules, false) < 0) {
608 error:
609 talloc_free(node);
610 return -1;
611 }
612
613 if (!fr_sbuff_next_if_char(in, ')')) {
614 fr_strerror_const("Missing closing brace ')'");
615 goto error;
616 }
617
619
621 return 0;
622}
623
624/** Parse an attribute ref or a virtual attribute
625 *
626 */
628 fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules)
629{
631 tmpl_t *vpt = NULL;
632 xlat_exp_t *node;
633
635 tmpl_rules_t our_t_rules;
636 fr_sbuff_t our_in = FR_SBUFF(in);
637
638 XLAT_DEBUG("ATTRIBUTE <-- %.*s", (int) fr_sbuff_remaining(in), fr_sbuff_current(in));
639
640 /*
641 * We are called from %{foo}. So we don't use attribute prefixes.
642 */
643 tmpl_rules_copy_depth(&our_t_rules, t_rules);
644 if (tmpl_rules_depth_exceeded(&our_t_rules)) return -1;
645 our_t_rules.attr.allow_wildcard = true;
646
647 fr_sbuff_marker(&m_s, in);
648
650 if (tmpl_afrom_attr_substr(node, &err, &vpt, &our_in, p_rules, &our_t_rules) < 0) {
651 /*
652 * If the parse error occurred before a terminator,
653 * then the error is changed to 'Unknown module',
654 * as it was more likely to be a bad module name,
655 * than a request qualifier.
656 */
657 if (err == TMPL_ATTR_ERROR_MISSING_TERMINATOR) fr_sbuff_set(&our_in, &m_s);
658 error:
659 fr_sbuff_marker_release(&m_s);
660 talloc_free(node);
661 FR_SBUFF_ERROR_RETURN(&our_in);
662 }
663
664 /*
665 * Deal with unresolved attributes.
666 */
668 if (!t_rules->attr.allow_unresolved) {
670
671 fr_strerror_const("Unresolved attributes not allowed in expansions here");
672 fr_sbuff_set(&our_in, &m_s); /* Error at the start of the attribute */
673 goto error;
674 }
675 }
676
677 /*
678 * Deal with normal attribute (or list)
679 */
681 xlat_exp_set_vpt(node, vpt);
682
683 /*
684 * Remember that it was %{User-Name}
685 *
686 * This is a temporary hack until all of the unit tests
687 * pass without '&'.
688 */
689 UNCONST(tmpl_attr_rules_t *, &vpt->rules.attr)->xlat = true;
690
692
693 fr_sbuff_marker_release(&m_s);
694 return fr_sbuff_set(in, &our_in);
695}
696
699 ['-'] = true, ['/'] = true, ['_'] = true, // fr_dict_attr_allowed_chars
700 ['.'] = true, ['*'] = true, ['#'] = true,
701 ['['] = true, [']'] = true, // tmpls and attribute arrays
702};
703
705 tmpl_rules_t const *t_rules)
706{
707 size_t len;
708 int ret;
710 char hint;
711 fr_sbuff_term_t hint_tokens = FR_SBUFF_TERMS(
712 L(" "), /* First special token is a ' ' - Likely a syntax error */
713 L("["), /* First special token is a '[' i.e. '%{attr[<idx>]}' */
714 L("}") /* First special token is a '}' i.e. '%{<attrref>}' */
715 );
716
717 fr_sbuff_parse_rules_t attr_p_rules = {
718 .escapes = &xlat_unescape,
719 .terminals = &FR_SBUFF_TERM("}")
720 };
721#ifdef HAVE_REGEX
722 xlat_exp_t *node;
723#endif
724
725 XLAT_DEBUG("EXPANSION <-- %.*s", (int) fr_sbuff_remaining(in), fr_sbuff_current(in));
726
727 fr_sbuff_marker(&m_s, in);
728
729#ifdef HAVE_REGEX
730 ret = xlat_tokenize_regex(head, &node, in, &m_s);
731 if (ret < 0) {
732 fr_sbuff_marker_release(&m_s);
733 return ret;
734 }
735
736 if (ret == 1) {
737 fr_assert(node != NULL);
739 fr_sbuff_marker_release(&m_s);
740 return 0;
741 }
742
743 fr_sbuff_set(in, &m_s); /* backtrack to the start of the expression */
744#endif /* HAVE_REGEX */
745
746 /*
747 * See if it's an attribute reference, with possible array stuff.
748 */
750 if (fr_sbuff_is_char(in, '}')) {
751 if (!len) goto empty_disallowed;
752 goto check_for_attr;
753 }
754
755 if (!fr_sbuff_extend(in)) goto missing_brace;
756
757 /*
758 * It must be an expression.
759 *
760 * We wrap the xlat in a group, and then mark the group to be hoisted.
761 */
762 {
763 tmpl_rules_t my_rules;
764
765 fr_sbuff_set(in, &m_s); /* backtrack to the start of the expression */
766
767 MEM(node = xlat_exp_alloc(head, XLAT_GROUP, NULL, 0));
768
769 tmpl_rules_copy_depth(&my_rules, t_rules);
770 if (tmpl_rules_depth_exceeded(&my_rules)) {
771 talloc_free(node);
772 goto release;
773 }
774 my_rules.enumv = NULL;
775 my_rules.cast = FR_TYPE_NULL;
776 t_rules = &my_rules;
777
778 ret = xlat_tokenize_expression(node, &node->group, in, &attr_p_rules, t_rules);
779 if (ret <= 0) {
780 talloc_free(node);
781 fr_sbuff_marker_release(&m_s);
782 return ret;
783 }
784
785 if (!fr_sbuff_is_char(in, '}')) {
786 talloc_free(node);
787 missing_brace:
788 fr_strerror_const("Missing closing brace '}'");
789 goto release;
790 }
791
793 node->flags = node->group->flags;
794
795 /*
796 * Print it as %{...}. Then when we're evaluating a string, hoist the results.
797 */
798 node->flags.xlat = true;
799 node->hoist = true;
800
802
803 (void) fr_sbuff_next(in); /* skip '}' */
804 fr_sbuff_marker_release(&m_s);
805 return ret;
806 }
807
808check_for_attr:
809 fr_sbuff_set(in, &m_s); /* backtrack */
810
811 /*
812 * %{Attr-Name}
813 * %{Attr-Name[#]}
814 * %{request.Attr-Name}
815 */
816
817 /*
818 * Check for empty expressions %{} %{: %{[
819 */
820 len = fr_sbuff_adv_until(in, SIZE_MAX, &hint_tokens, '\0');
821
822 /*
823 * This means the end of a string not containing any of the other
824 * tokens was reached.
825 *
826 * e.g. '%{myfirstxlat'
827 */
828 if (!fr_sbuff_extend(in)) {
829 fr_strerror_const("Missing closing brace '}'");
830 goto release;
831 }
832
833 hint = fr_sbuff_char(in, '\0');
834
835 XLAT_DEBUG("EXPANSION HINT TOKEN '%c'", hint);
836 if (len == 0) {
837 switch (hint) {
838 case '}':
839 empty_disallowed:
840 fr_strerror_const("Empty expressions are invalid");
841 goto release;
842
843 case '[':
844 fr_strerror_const("Missing attribute name");
845 goto release;
846
847 default:
848 break;
849 }
850 }
851
852 switch (hint) {
853 /*
854 * Hint token is a:
855 * - '[' - Which is an attribute index, so it must be an attribute.
856 * - '}' - The end of the expansion, which means it was a bareword.
857 */
858 case '.':
859 case '}':
860 case '[':
861 fr_sbuff_set(in, &m_s); /* backtrack */
862 fr_sbuff_marker_release(&m_s);
863
864 if (xlat_tokenize_attribute(head, in, &attr_p_rules, t_rules) < 0) return -1;
865
866 if (!fr_sbuff_next_if_char(in, '}')) {
867 fr_strerror_const("Missing closing brace '}'");
868 return -1;
869 }
870
871 return 0;
872
873 /*
874 * Hint token was whitespace
875 *
876 * e.g. '%{my '
877 */
878 default:
879 break;
880 }
881
882 /*
883 * Box print is so we get \t \n etc..
884 */
885 fr_strerror_printf("Invalid char '%pV' in expression", fr_box_strvalue_len(fr_sbuff_current(in), 1));
886
887release:
888 fr_sbuff_marker_release(&m_s);
889 return -1;
890}
891
892/** Parse an xlat string i.e. a non-expansion or non-function
893 *
894 * When this function is being used outside of an xlat expansion, i.e. on a string
895 * which contains one or more xlat expansions, it uses the terminal grammar and
896 * escaping rules of that string type.
897 *
898 * Which this function is being used inside of an xlat expansion, it uses xlat specific
899 * terminal grammar and escaping rules.
900 *
901 * This allows us to be smart about processing quotes within the expansions themselves.
902 *
903 * @param[out] head to allocate nodes in, and where to write the first
904 * child, and where the flags are stored.
905 * @param[in] in sbuff to parse.
906 * @param[in] p_rules that control parsing.
907 * @param[in] t_rules that control attribute reference and xlat function parsing.
908 * @return
909 * - <0 on failure
910 * - >=0 for number of bytes parsed
911 */
913 fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules)
914{
915 xlat_exp_t *node = NULL;
916 xlat_exp_t *prev = NULL;
918 L("%"),
919 );
920 fr_sbuff_term_t *tokens;
922 fr_sbuff_t our_in = FR_SBUFF(in);
923 tmpl_rules_t our_t_rules;
924
925 XLAT_DEBUG("STRING <-- %.*s", (int) fr_sbuff_remaining(in), fr_sbuff_current(in));
926
927 tmpl_rules_copy_depth(&our_t_rules, t_rules);
928 if (tmpl_rules_depth_exceeded(&our_t_rules)) return -1;
929 t_rules = &our_t_rules;
930
931 escapes = p_rules ? p_rules->escapes : NULL;
932 tokens = p_rules && p_rules->terminals ?
933 fr_sbuff_terminals_amerge(NULL, p_rules->terminals, &terminals) : &terminals;
934
935 for (;;) {
936 char *str;
937 size_t len;
939
940 /*
941 * pre-allocate the node so we don't have to steal it later.
942 */
943 node = xlat_exp_alloc(head, XLAT_BOX, NULL, 0);
944
945 /*
946 * Find the next token
947 */
948 skip_alloc:
949 fr_sbuff_marker(&m_s, &our_in);
950 if (fr_sbuff_out_aunescape_until(node, &str, &len, &our_in, SIZE_MAX, tokens, escapes) < 0) {
951 error:
952 talloc_free(node);
953
954 /*
955 * Free our temporary array of terminals
956 */
957 if (tokens != &terminals) talloc_free(tokens);
958 fr_sbuff_marker_release(&m_s);
959 FR_SBUFF_ERROR_RETURN(&our_in);
960 }
961
962 /*
963 * It's a value box, create an appropriate node
964 */
965 if (len > 0) {
966 do_value_box:
967 /*
968 * If the previous node was also a constant value-box, we can merge the new
969 * string into it instead of inserting a fresh node. This merge ensures that we
970 * only have one constant value-box produced, instead of many.
971 */
972 if (prev && (prev->type == XLAT_BOX) && (prev->data.type == FR_TYPE_STRING)) {
973 size_t prev_len = prev->data.vb_length;
974 size_t add_len = talloc_strlen(str);
975 size_t total = prev_len + add_len;
976 char *merged;
977
978 MEM(fr_value_box_bstr_realloc(prev, &merged, &prev->data, total) == 0);
979 memcpy(merged + prev_len, str, add_len + 1);
980
981 xlat_exp_set_name(prev, merged, total);
982 talloc_free(str);
983
984 XLAT_DEBUG("VALUE-BOX merged --> %s", prev->fmt);
985
986 fr_sbuff_marker_release(&m_s);
987
988 /*
989 * Keep "node", as we haven't used it.
990 */
991 goto skip_alloc;
992 }
993
994 xlat_exp_set_name_shallow(node, str);
995 fr_value_box_bstrndup(node, &node->data, NULL, str, talloc_strlen(str), false);
996 fr_value_box_mark_safe_for(&node->data, t_rules->literals_safe_for);
997
998 if (!escapes) {
999 XLAT_DEBUG("VALUE-BOX %s <-- %.*s", str,
1000 (int) fr_sbuff_behind(&m_s), fr_sbuff_current(&m_s));
1001 } else {
1002 XLAT_DEBUG("VALUE-BOX (%s) %s <-- %.*s", escapes->name, str,
1003 (int) fr_sbuff_behind(&m_s), fr_sbuff_current(&m_s));
1004 }
1005 XLAT_HEXDUMP((uint8_t const *)str, talloc_strlen(str), " VALUE-BOX ");
1006
1008
1009 prev = node;
1010 node = NULL;
1011 fr_sbuff_marker_release(&m_s);
1012 continue;
1013 }
1014
1015 /*
1016 * We have parsed as much as we can as unescaped
1017 * input. Either some text (and added the node
1018 * to the list), or zero text. We now try to
1019 * parse '%' expansions.
1020 */
1021
1022 /*
1023 * Attribute, function call, or other expansion.
1024 */
1025 if (fr_sbuff_adv_past_str_literal(&our_in, "%{")) {
1026 TALLOC_FREE(node); /* nope, couldn't use it */
1027
1028 if (xlat_tokenize_expansion(head, &our_in, t_rules) < 0) goto error;
1029
1030 if (fr_sbuff_is_str_literal(&our_in, ":-")) {
1031 fr_strerror_const("Old style alternation of %{...:-...} is no longer supported");
1032 goto error;
1033 }
1034
1035 prev = NULL; /* non-value-box inserted; subsequent text must not merge */
1036
1037 next:
1038 fr_sbuff_marker_release(&m_s);
1039 continue;
1040 }
1041
1042 /*
1043 * More migration hacks: allow %foo(...)
1044 */
1045 if (fr_sbuff_next_if_char(&our_in, '%')) {
1046 /*
1047 * % non-alphanumeric, create a value-box for just the "%" character.
1048 */
1049 if (!fr_sbuff_is_alnum(&our_in)) {
1050 if (fr_sbuff_next_if_char(&our_in, '%')) { /* nothing */ }
1051
1052 str = talloc_strdup(node, "%");
1053 goto do_value_box;
1054 }
1055
1056 TALLOC_FREE(node); /* nope, couldn't use it */
1057
1058 /*
1059 * Tokenize the function arguments using the new method.
1060 */
1061 if (xlat_tokenize_function_args(head, &our_in, t_rules) < 0) goto error;
1062 prev = NULL; /* non-value-box inserted; subsequent text must not merge */
1063 goto next;
1064 }
1065
1066 /*
1067 * Nothing we recognize. Just return nothing.
1068 */
1069 TALLOC_FREE(node);
1070 XLAT_DEBUG("VALUE-BOX <-- (empty)");
1071 fr_sbuff_marker_release(&m_s);
1072 break;
1073 }
1074
1075 /*
1076 * Free our temporary array of terminals
1077 */
1078 if (tokens != &terminals) talloc_free(tokens);
1079
1080 return fr_sbuff_set(in, &our_in);
1081}
1082
1084 { L("\""), T_DOUBLE_QUOTED_STRING }, /* Don't re-order, backslash throws off ordering */
1085 { L("'"), T_SINGLE_QUOTED_STRING },
1086 { L("`"), T_BACK_QUOTED_STRING }
1087};
1089
1090#define INFO_INDENT(_fmt, ...) INFO("%*s"_fmt, depth * 2, " ", ## __VA_ARGS__)
1091
1092static void _xlat_debug_head(xlat_exp_head_t const *head, int depth) CC_HINT(nonnull);
1093static void _xlat_debug_node(xlat_exp_t const *node, int depth, bool print_flags) CC_HINT(nonnull);
1094
1095static void _xlat_debug_node(xlat_exp_t const *node, int depth, bool print_flags)
1096{
1097 INFO_INDENT("{ -- %s", node->fmt);
1098#ifndef NDEBUG
1099// INFO_INDENT(" %s:%d", node->file, node->line);
1100#endif
1101
1102 if (print_flags) {
1103 INFO_INDENT("flags = %s %s %s %s %s %s",
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 node->flags.use_module_status ? "use_module_status" : "");
1110 }
1111
1112 depth++;
1113
1114 if (node->quote != T_BARE_WORD) INFO_INDENT("quote = %c", fr_token_quote[node->quote]);
1115
1116 switch (node->type) {
1117 case XLAT_BOX:
1118 INFO_INDENT("value %s --> %pV", fr_type_to_str(node->data.type), &node->data);
1119 break;
1120
1121 case XLAT_GROUP:
1122 INFO_INDENT("group");
1123 INFO_INDENT("{");
1124 _xlat_debug_head(node->group, depth + 1);
1125 INFO_INDENT("}");
1126 break;
1127
1128 case XLAT_ONE_LETTER:
1129 INFO_INDENT("percent (%c)", node->fmt[0]);
1130 break;
1131
1132 case XLAT_TMPL:
1133 {
1134 if (tmpl_cast_get(node->vpt) != FR_TYPE_NULL) {
1135 INFO_INDENT("cast (%s)", fr_type_to_str(tmpl_cast_get(node->vpt)));
1136 }
1137
1138 if (tmpl_is_attr(node->vpt)) {
1139 fr_assert(!node->flags.pure);
1140 if (tmpl_attr_tail_da(node->vpt)) INFO_INDENT("tmpl attribute (%s)", tmpl_attr_tail_da(node->vpt)->name);
1141 if (tmpl_attr_tail_num(node->vpt) != NUM_UNSPEC) {
1142 FR_DLIST_HEAD(tmpl_request_list) const *list;
1143 tmpl_request_t *rr = NULL;
1144
1145 INFO_INDENT("{");
1146
1147 /*
1148 * Loop over the request references
1149 */
1150 list = tmpl_request(node->vpt);
1151 while ((rr = tmpl_request_list_next(list, rr))) {
1152 INFO_INDENT("ref %u", rr->request);
1153 }
1154 INFO_INDENT("list %s", tmpl_list_name(tmpl_list(node->vpt), "<INVALID>"));
1155 if (tmpl_attr_tail_num(node->vpt) != NUM_UNSPEC) {
1156 if (tmpl_attr_tail_num(node->vpt) == NUM_COUNT) {
1157 INFO_INDENT("[#]");
1158 } else if (tmpl_attr_tail_num(node->vpt) == NUM_ALL) {
1159 INFO_INDENT("[*]");
1160 } else {
1161 INFO_INDENT("[%d]", tmpl_attr_tail_num(node->vpt));
1162 }
1163 }
1164 INFO_INDENT("}");
1165 }
1166 } else if (tmpl_is_data(node->vpt)) {
1167 INFO_INDENT("tmpl (%s) type %s", node->fmt, fr_type_to_str(tmpl_value_type(node->vpt)));
1168
1169 } else if (tmpl_is_xlat(node->vpt)) {
1170 INFO_INDENT("tmpl xlat (%s)", node->fmt);
1171 _xlat_debug_head(tmpl_xlat(node->vpt), depth + 1);
1172
1173 } else {
1174 INFO_INDENT("tmpl (%s)", node->fmt);
1175 }
1176 }
1177 break;
1178
1179 case XLAT_FUNC:
1180 fr_assert(node->call.func != NULL);
1181 INFO_INDENT("func (%s)", node->call.func->name);
1182 if (xlat_exp_head(node->call.args)) {
1183 INFO_INDENT("{");
1184 _xlat_debug_head(node->call.args, depth + 1);
1185 INFO_INDENT("}");
1186 }
1187 break;
1188
1190 INFO_INDENT("func-unresolved (%s)", node->fmt);
1191 if (xlat_exp_head(node->call.args)) {
1192 INFO_INDENT("{");
1193 _xlat_debug_head(node->call.args, depth + 1);
1194 INFO_INDENT("}");
1195 }
1196 break;
1197
1198#ifdef HAVE_REGEX
1199 case XLAT_REGEX:
1200 INFO_INDENT("regex-var -- %d", node->regex_index);
1201 break;
1202#endif
1203
1204 case XLAT_INVALID:
1205 DEBUG("XLAT-INVALID");
1206 break;
1207 }
1208
1209 depth--;
1210 INFO_INDENT("}");
1211}
1212
1213void xlat_debug(xlat_exp_t const *node)
1214{
1215 _xlat_debug_node(node, 0, true);
1216}
1217
1219{
1220 int i = 0;
1221
1222 INFO_INDENT("head flags = %s %s %s %s %s %s",
1223 head->flags.needs_resolving ? "need_resolving," : "",
1224 head->flags.pure ? "pure" : "",
1225 head->flags.can_purify ? "can_purify" : "",
1226 head->flags.constant ? "constant" : "",
1227 head->flags.xlat ? "xlat" : "",
1228 head->flags.use_module_status ? "use_module_status" : "");
1229
1230 depth++;
1231
1232 xlat_exp_foreach(head, node) {
1233 INFO_INDENT("[%d] flags = %s %s %s %s %s %s", i++,
1234 node->flags.needs_resolving ? "need_resolving" : "",
1235 node->flags.pure ? "pure" : "",
1236 node->flags.can_purify ? "can_purify" : "",
1237 node->flags.constant ? "constant" : "",
1238 node->flags.xlat ? "xlat" : "",
1239 node->flags.use_module_status ? "use_module_status" : "");
1240
1241 _xlat_debug_node(node, depth, false);
1242 }
1243}
1244
1246{
1248}
1249
1251 fr_sbuff_escape_rules_t const *e_rules, char c)
1252{
1253 ssize_t slen;
1254 size_t at_in = fr_sbuff_used_total(out);
1255 char close;
1256
1257 if (!node) return 0;
1258
1259 if (node->flags.xlat) FR_SBUFF_IN_CHAR_RETURN(out, '%', '{');
1260
1261 switch (node->type) {
1262 case XLAT_GROUP:
1264 xlat_print(out, node->group, fr_value_escape_by_quote[node->quote]);
1266
1267 if (xlat_exp_next(head, node)) {
1268 if (c) FR_SBUFF_IN_CHAR_RETURN(out, c);
1269
1270 if (head->is_argv) FR_SBUFF_IN_CHAR_RETURN(out, ' '); /* Add ' ' between args */
1271 }
1272 goto done;
1273
1274 case XLAT_BOX:
1275 /*
1276 * @todo - respect node->quote here, too. Which also means updating the parser.
1277 */
1278 if (node->quote == T_BARE_WORD) {
1279 if (node->data.enumv &&
1280 (strncmp(node->fmt, "::", 2) == 0)) {
1282 }
1283
1284 FR_SBUFF_RETURN(fr_value_box_print, out, &node->data, e_rules);
1285 } else {
1286 FR_SBUFF_RETURN(fr_value_box_print_quoted, out, &node->data, node->quote);
1287 }
1288 goto done;
1289
1290 case XLAT_TMPL:
1291 if (node->vpt->rules.cast != FR_TYPE_NULL) {
1293 FR_SBUFF_IN_STRCPY_RETURN(out, fr_type_to_str(node->vpt->rules.cast));
1295 }
1296
1297 if (tmpl_is_data(node->vpt)) {
1298 /*
1299 * Manually add enum prefix when printing.
1300 */
1301 if (node->vpt->data.literal.enumv &&
1302 ((node->vpt->data.literal.type != FR_TYPE_BOOL) || da_is_bit_field(node->vpt->data.literal.enumv)) &&
1303 (strncmp(node->fmt, "::", 2) == 0)) {
1304 FR_SBUFF_IN_CHAR_RETURN(out, ':', ':');
1305 }
1307 goto done;
1308 }
1309 if (tmpl_needs_resolving(node->vpt)) {
1310 if (node->vpt->quote != T_BARE_WORD) {
1312 }
1313 FR_SBUFF_IN_STRCPY_RETURN(out, node->vpt->name); /* @todo - escape it? */
1314 if (node->vpt->quote != T_BARE_WORD) {
1316 }
1317 goto done;
1318 }
1319
1320 if (tmpl_contains_xlat(node->vpt)) { /* xlat and exec */
1321 if (node->vpt->quote == T_BARE_WORD) {
1322 xlat_print(out, tmpl_xlat(node->vpt), NULL);
1323 } else {
1327 }
1328 goto done;
1329 }
1330
1331 /*
1332 * Regexes need their own print routine, as they need to print the flags, too.
1333 *
1334 * Regexes should also "eat" their arguments into their instance data, so that we should
1335 * never try to print a regex.
1336 */
1337 fr_assert(!tmpl_contains_regex(node->vpt));
1338
1339 // attr or list
1340 fr_assert(tmpl_is_attr(node->vpt));
1341 fr_assert(talloc_parent(node->vpt) == node);
1342 fr_assert(!node->flags.pure);
1343
1344 /*
1345 * No '&', print the name, BUT without any attribute prefix.
1346 */
1347 if (!node->vpt->rules.attr.xlat) {
1348 char const *p = node->fmt;
1349
1350 if (*p == '&') p++;
1351
1353 goto done;
1354 }
1355 break;
1356
1357 case XLAT_ONE_LETTER:
1358 FR_SBUFF_IN_CHAR_RETURN(out, '%', node->fmt[0]);
1359 goto done;
1360
1361 case XLAT_FUNC:
1362 /*
1363 * We have a callback for printing this node, go
1364 * call it.
1365 */
1366 if (node->call.func->print) {
1367 slen = node->call.func->print(out, node, node->call.inst ? node->call.inst->data : NULL, e_rules);
1368 if (slen < 0) return slen;
1369 goto done;
1370 }
1371 break;
1372
1373 default:
1374 break;
1375 }
1376
1377 /*
1378 * Now print %(...) or %{...}
1379 */
1380 if ((node->type == XLAT_FUNC) || (node->type == XLAT_FUNC_UNRESOLVED)) {
1381 FR_SBUFF_IN_CHAR_RETURN(out, '%'); /* then the name */
1382 close = ')';
1383 } else {
1385 close = '}';
1386 }
1387
1388 switch (node->type) {
1389 case XLAT_TMPL:
1390 slen = tmpl_attr_print(out, node->vpt);
1391 if (slen < 0) return slen;
1392 break;
1393
1394#ifdef HAVE_REGEX
1395 case XLAT_REGEX:
1396 FR_SBUFF_IN_SPRINTF_RETURN(out, "%i", node->regex_index);
1397 break;
1398#endif
1399
1400 case XLAT_FUNC:
1401 FR_SBUFF_IN_BSTRCPY_BUFFER_RETURN(out, node->call.func->name);
1403
1404 goto print_args;
1405
1409
1410 print_args:
1411 if (xlat_exp_head(node->call.args)) {
1412 xlat_exp_foreach(node->call.args, child) {
1413 slen = xlat_print_node(out, node->call.args, child, &xlat_escape, ',');
1414 if (slen < 0) return slen;
1415 }
1416 }
1417 break;
1418
1419 case XLAT_INVALID:
1420 case XLAT_BOX:
1421 case XLAT_ONE_LETTER:
1422 case XLAT_GROUP:
1423 fr_assert_fail(NULL);
1424 break;
1425 }
1427
1428done:
1429 if (node->flags.xlat) FR_SBUFF_IN_CHAR_RETURN(out, '}');
1430
1431 return fr_sbuff_used_total(out) - at_in;
1432}
1433
1434/** Reconstitute an xlat expression from its constituent nodes
1435 *
1436 * @param[in] out Where to write the output string.
1437 * @param[in] head First node to print.
1438 * @param[in] e_rules Specifying how to escape literal values.
1439 */
1441{
1442 ssize_t slen;
1443 size_t at_in = fr_sbuff_used_total(out);
1444
1445 xlat_exp_foreach(head, node) {
1446 slen = xlat_print_node(out, head, node, e_rules, 0);
1447 if (slen < 0) {
1448 return slen - (fr_sbuff_used_total(out) - at_in);
1449 }
1450 }
1451
1452 return fr_sbuff_used_total(out) - at_in;
1453}
1454
1455#if 0
1456static void xlat_safe_for(xlat_exp_head_t *head, fr_value_box_safe_for_t safe_for)
1457{
1458 xlat_exp_foreach(head, node) {
1459 switch (node->type) {
1460 case XLAT_BOX:
1461 if (node->data.vb_safefor != safe_for) {
1462 ERROR("FAILED %lx %lx - %s", node->data.vb_safefor, safe_for, node->fmt);
1463 }
1464 fr_assert(node->data.vb_safefor == safe_for);
1465 break;
1466
1467 case XLAT_GROUP:
1468 xlat_safe_for(node->group, safe_for);
1469 break;
1470
1471 case XLAT_TMPL:
1472 if (!tmpl_is_xlat(node->vpt)) break;
1473
1474 xlat_safe_for(tmpl_xlat(node->vpt), safe_for);
1475 break;
1476
1477 default:
1478 break;
1479 }
1480 }
1481}
1482#endif
1483
1484
1486 fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules)
1487{
1488 int triple = 1;
1489 fr_slen_t slen;
1490 fr_sbuff_t our_in = FR_SBUFF(in);
1491 xlat_exp_t *node;
1493 tmpl_rules_t our_t_rules;
1494
1495 tmpl_rules_copy_depth(&our_t_rules, t_rules);
1496 if (tmpl_rules_depth_exceeded(&our_t_rules)) FR_SBUFF_ERROR_RETURN(&our_in);
1497 t_rules = &our_t_rules;
1498
1499 /*
1500 * Triple-quoted strings have different terminal conditions.
1501 */
1502 switch (quote) {
1504 fr_strerror_const("Unexpected regular expression");
1505 fr_sbuff_advance(in, -1); /* to the actual '/' */
1506 our_in = FR_SBUFF(in);
1507 FR_SBUFF_ERROR_RETURN(&our_in);
1508
1509 default:
1510 fr_assert(0);
1511 FR_SBUFF_ERROR_RETURN(&our_in);
1512
1513 case T_BARE_WORD:
1514#ifdef HAVE_REGEX
1515 fr_sbuff_marker(&m, &our_in);
1516
1517 /*
1518 * Regular expression expansions are %{...}
1519 */
1520 if (fr_sbuff_adv_past_str_literal(&our_in, "%{")) {
1521 int ret;
1523
1524 fr_sbuff_marker(&m_s, &our_in);
1525
1526 ret = xlat_tokenize_regex(ctx, &node, &our_in, &m_s);
1527 if (ret < 0) FR_SBUFF_ERROR_RETURN(&our_in);
1528
1529 if (ret == 1) goto done;
1530
1531 fr_sbuff_set(&our_in, &m);
1532 }
1533#endif /* HAVE_REGEX */
1534
1535#if 0
1536 /*
1537 * Avoid a bounce through tmpls for %{...} and %func()
1538 *
1539 * @todo %{...} --> tokenize expression
1540 * %foo(..) --> tokenize_function_args (and have that function look for ()
1541 * %Y or %Y() --> one letter
1542 */
1543 if (fr_sbuff_is_char(&our_in, '%')) {
1544 xlat_exp_head_t *head = NULL;
1545
1547
1548 slen = xlat_tokenize_input(head, &our_in, p_rules, t_rules);
1549 if (slen <= 0) {
1551 FR_SBUFF_ERROR_RETURN(&our_in);
1552 }
1553
1554 fr_assert(fr_dlist_num_elements(&head->dlist) == 1);
1555
1556 node = fr_dlist_pop_head(&head->dlist);
1557 fr_assert(node != NULL);
1558 (void) talloc_steal(ctx, node);
1560 goto done;
1561 }
1562#endif
1563 break;
1564
1568 p_rules = value_parse_rules_quoted[quote];
1569
1570 if (fr_sbuff_remaining(&our_in) >= 2) {
1571 char const *p = fr_sbuff_current(&our_in);
1572 char c = fr_token_quote[quote];
1573
1574 /*
1575 * """foo "quote" and end"""
1576 */
1577 if ((p[0] == c) && (p[1] == c)) {
1578 triple = 3;
1579 (void) fr_sbuff_advance(&our_in, 2);
1580 p_rules = value_parse_rules_3quoted[quote];
1581 }
1582 }
1583 break;
1584 }
1585
1586 switch (quote) {
1587 case T_BARE_WORD:
1588 MEM(node = xlat_exp_alloc(ctx, XLAT_TMPL, NULL, 0));
1589 node->quote = quote;
1590
1591 /*
1592 * tmpl_afrom_substr does pretty much all the work of
1593 * parsing the operand. It pays attention to the cast on
1594 * our_t_rules, and will try to parse any data there as
1595 * of the correct type.
1596 */
1597 slen = tmpl_afrom_substr(node, &node->vpt, &our_in, quote, p_rules, t_rules);
1598 if (slen <= 0) {
1599 fr_sbuff_advance(&our_in, -slen - 1); /* point to the correct offset */
1600
1601 error:
1602 talloc_free(node);
1603 FR_SBUFF_ERROR_RETURN(&our_in);
1604 }
1605 xlat_exp_set_vpt(node, node->vpt); /* sets flags */
1606
1607 if (xlat_tmpl_normalize(node) < 0) goto error;
1608
1609 /*
1610 * A bare word which turns out to be an xlat does not need a tmpl to wrap it.
1611 * Hoist the xlat out of the tmpl, which avoids an xlat -> tmpl -> xlat bounce at
1612 * run time.
1613 *
1614 * If there's a cast, it has to stay on the tmpl. The case applies to the result of the
1615 * expansion, and not to any one node inside of it. Quoted strings are other cases in
1616 * this switch, and never get here.
1617 */
1618 if ((node->type == XLAT_TMPL) && tmpl_is_xlat(node->vpt) && (tmpl_rules_cast(node->vpt) == FR_TYPE_NULL)) {
1620
1621 /*
1622 * The group is one expansion, and not a value in its own right. Pass the
1623 * children's boxes straight out, instead of wrapping them in a group box.
1624 */
1625 node->hoist = true;
1626 }
1627
1628 goto done;
1629
1630 /*
1631 * `foo` is a tmpl, and is NOT a group.
1632 */
1634 MEM(node = xlat_exp_alloc(ctx, XLAT_TMPL, NULL, 0));
1635 node->quote = quote;
1636
1637 /*
1638 * tmpl_afrom_substr does pretty much all the work of
1639 * parsing the operand. It pays attention to the cast on
1640 * our_t_rules, and will try to parse any data there as
1641 * of the correct type.
1642 */
1643 slen = tmpl_afrom_substr(node, &node->vpt, &our_in, quote, p_rules, t_rules);
1644 if (slen <= 0) {
1645 fr_sbuff_advance(&our_in, -slen - 1); /* point to the correct offset */
1646 goto error;
1647 }
1648 xlat_exp_set_vpt(node, node->vpt); /* sets flags */
1649
1650 if (xlat_tmpl_normalize(node) < 0) goto error;
1651
1652 break; /* look for closing quote */
1653
1654 /*
1655 * "Double quoted strings may contain %{expansions}"
1656 */
1658 MEM(node = xlat_exp_alloc(ctx, XLAT_GROUP, NULL, 0));
1659 node->quote = quote;
1660
1661 fr_sbuff_marker(&m, &our_in);
1662 XLAT_DEBUG("ARGV double quotes <-- %.*s", (int) fr_sbuff_remaining(&our_in), fr_sbuff_current(&our_in));
1663
1664 if (xlat_tokenize_input(node->group, &our_in, p_rules, t_rules) < 0) goto error;
1665
1666 node->flags = node->group->flags;
1667 node->hoist = true;
1669
1670 /*
1671 * There's no expansion in the string. Hoist the value-box where we can.
1672 */
1673 if (node->flags.constant) {
1674 size_t num = fr_dlist_num_elements(&node->group->dlist);
1675
1676 /*
1677 * Empty string: convert the wrapper to an empty XLAT_BOX.
1678 *
1679 * Exactly one child of type XLAT_BOX: hoist the child up in place of the
1680 * wrapper.
1681 *
1682 * Anything else (multiple constant children, or a single non-box child like
1683 * a hoisted %{(const)} expression result) is left wrapped in an XLAT_GROUP.
1684 * The hoist flag on the GROUP makes the runtime concatenate the children's
1685 * stringified values at eval time, which is the correct semantics for cases
1686 * like "x%{(1)}y". The integer 1 still needs to be stringified before it
1687 * can be merged into the surrounding literal text.
1688 */
1689 if (num == 0) {
1691
1692 fr_value_box_init(&node->data, FR_TYPE_STRING, NULL, false);
1693 fr_value_box_strdup(node, &node->data, NULL, "", false);
1694
1695 fr_assert(node->type == XLAT_BOX);
1696 node->quote = quote;
1697
1698 } else if (num == 1) {
1699 xlat_exp_t *child = xlat_exp_head(node->group);
1700 fr_assert(child != NULL);
1701
1702 if (child->type == XLAT_BOX) {
1703 (void) talloc_steal(ctx, child);
1704 talloc_free(node);
1705 node = child;
1706 node->quote = quote; /* not the same node! */
1707 } /* else there are single non-box constant child, leave the wrapper alone */
1708 } /* else there are multiple constant children, leave the wrapper alone */
1709 }
1710 break;
1711
1712 /*
1713 * 'Single quoted strings get parsed as literal strings'
1714 */
1716 {
1717 char *str;
1718
1719 XLAT_DEBUG("ARGV single quotes <-- %.*s", (int) fr_sbuff_remaining(&our_in), fr_sbuff_current(&our_in));
1720
1721 node = xlat_exp_alloc(ctx, XLAT_BOX, NULL, 0);
1722 node->quote = quote;
1723
1724 if (fr_sbuff_out_aunescape_until(node, &str, NULL, &our_in, SIZE_MAX,
1725 p_rules->terminals, p_rules->escapes) < 0) goto error;
1726
1727 xlat_exp_set_name_shallow(node, str);
1728 fr_value_box_strdup(node, &node->data, NULL, str, false);
1729 fr_value_box_mark_safe_for(&node->data, t_rules->literals_safe_for); /* Literal values are treated as implicitly safe */
1730 }
1731 break;
1732
1733 default:
1734 fr_strerror_const("Internal sanity check failed in tokenizing expansion word");
1735 FR_SBUFF_ERROR_RETURN(&our_in);
1736 }
1737
1738 /*
1739 * Ensure that the string ends with the correct number of quotes.
1740 */
1741 do {
1742 if (!fr_sbuff_is_char(&our_in, fr_token_quote[quote])) {
1743 fr_strerror_const("Unterminated string");
1744 fr_sbuff_set_to_start(&our_in);
1745 goto error;
1746 }
1747
1748 fr_sbuff_advance(&our_in, 1);
1749 } while (--triple > 0);
1750
1751done:
1752 XLAT_VERIFY(node);
1753 *out = node;
1754
1755 FR_SBUFF_SET_RETURN(in, &our_in);
1756}
1757
1758/** Tokenize an xlat expansion into a series of XLAT_TYPE_CHILD arguments
1759 *
1760 * @param[in] ctx to allocate nodes in. Note: All nodes will be
1761 * allocated in the same ctx. This is to allow
1762 * manipulation by xlat instantiation functions
1763 * later.
1764 * @param[out] out the head of the xlat list / tree structure.
1765 * @param[in] in the format string to expand.
1766 * @param[in] xlat_args the arguments
1767 * @param[in] p_rules controlling how to parse the string outside of
1768 * any expansions.
1769 * @param[in] t_rules controlling how attribute references are parsed.
1770 * @param[in] spaces whether the arguments are delimited by spaces
1771 * @return
1772 * - < 0 on error.
1773 * - >0 on success which is the number of characters parsed.
1774 */
1776 xlat_arg_parser_t const *xlat_args,
1777 fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules, bool spaces)
1778{
1779 unsigned int argc;
1780 fr_sbuff_t our_in = FR_SBUFF(in);
1781 fr_slen_t slen;
1783 fr_sbuff_parse_rules_t const *our_p_rules; /* Bareword parse rules */
1784 fr_sbuff_parse_rules_t tmp_p_rules;
1786 xlat_arg_parser_t const *arg = NULL, *arg_start;
1787 tmpl_rules_t arg_t_rules;
1788
1789 if (xlat_args) {
1790 arg_start = arg = xlat_args; /* Track the arguments as we parse */
1791 } else {
1792 static xlat_arg_parser_t const default_arg[] = { { .variadic = XLAT_ARG_VARIADIC_EMPTY_SQUASH, .type = FR_TYPE_VOID },
1794 arg_start = arg = &default_arg[0];
1795 }
1796 tmpl_rules_copy_depth(&arg_t_rules, t_rules);
1797 if (tmpl_rules_depth_exceeded(&arg_t_rules)) FR_SBUFF_ERROR_RETURN(&our_in);
1798
1799 if (unlikely(spaces)) {
1801 if (p_rules) { /* only for tmpl_tokenize, and back-ticks */
1802 fr_assert(p_rules->terminals);
1803
1804 tmp_p_rules = (fr_sbuff_parse_rules_t){ /* Stack allocated due to CL scope */
1805 .terminals = fr_sbuff_terminals_amerge(NULL, p_rules->terminals,
1807 .escapes = (p_rules->escapes ? p_rules->escapes : value_parse_rules_bareword_quoted.escapes)
1808 };
1809 our_p_rules = &tmp_p_rules;
1810 } else {
1811 our_p_rules = &value_parse_rules_bareword_quoted;
1812 }
1813
1814 } else {
1815 if (!p_rules) {
1816 p_rules = &xlat_function_arg_rules;
1817 } else {
1819 }
1820 fr_assert(p_rules->terminals);
1821
1822 our_p_rules = p_rules;
1823
1824 /*
1825 * The arguments to a function are NOT the output data type of the function.
1826 *
1827 * We do NOT check for quotation characters. We DO update t_rules to strip any casts. The
1828 * OUTPUT of the function is cast to the relevant data type, but each ARGUMENT is just an
1829 * expression with no given data type. Parsing the expression is NOT done with the cast of
1830 * arg->type, as that means each individual piece of the expression is parsed as the type. We
1831 * have to cast on the final _output_ of the expression, and we allow the _input_ pieces of the
1832 * expression to be just about anything.
1833 */
1834 arg_t_rules.enumv = NULL;
1835 arg_t_rules.cast = FR_TYPE_NULL;
1836 arg_t_rules.attr.namespace = NULL;
1837 arg_t_rules.attr.request_def = NULL;
1838 arg_t_rules.attr.list_def = request_attr_request;
1840 }
1841
1843
1844 /*
1845 * skip spaces at the beginning as we don't want them to become a whitespace literal.
1846 */
1847 fr_sbuff_adv_past_whitespace(&our_in, SIZE_MAX, NULL);
1848 fr_sbuff_marker(&m, &our_in);
1849 argc = 1;
1850
1851 while (fr_sbuff_extend(&our_in)) {
1852 xlat_exp_t *node = NULL;
1853 fr_token_t quote;
1854 size_t len;
1855
1856 /*
1857 * A literal in an argument is safe for the consumer named by the argument's
1858 * safe_for. Two cases leave the token from the tmpl rules in place:
1859 *
1860 * - The rules mark literals safe for anything. Nothing is more permissive,
1861 * so there is nothing to narrow.
1862 * - The argument does not name a consumer, its safe_for is
1863 * FR_VALUE_BOX_SAFE_FOR_NONE.
1864 */
1865 arg_t_rules.literals_safe_for = t_rules->literals_safe_for;
1866 if ((arg_t_rules.literals_safe_for != FR_VALUE_BOX_SAFE_FOR_ANY) &&
1868 arg_t_rules.literals_safe_for = arg->safe_for;
1869 }
1870
1871 fr_sbuff_adv_past_whitespace(&our_in, SIZE_MAX, NULL);
1872 fr_sbuff_set(&m, &our_in); /* Record start of argument */
1873
1874 MEM(node = xlat_exp_alloc(ctx, XLAT_GROUP, NULL, 0)); /* quote = T_BARE_WORD */
1875
1876 if (likely(!spaces)) {
1877 /*
1878 * We've reached the end of the arguments, don't try to tokenize anything else.
1879 */
1880 if (fr_sbuff_is_char(&our_in, ')')) {
1881 slen = 0;
1882
1883 } else {
1884 /*
1885 * Parse a full expression as an argv, all the way to a terminal character.
1886 * We use the input parse rules here.
1887 */
1888 slen = xlat_tokenize_expression(node, &node->group, &our_in, our_p_rules, &arg_t_rules);
1889 }
1890 } else {
1892
1893 node->quote = quote;
1894
1895 if (quote == T_BARE_WORD) {
1896 /*
1897 * Each argument is a bare word all by itself, OR an xlat thing all by itself.
1898 */
1899 slen = xlat_tokenize_input(node->group, &our_in, our_p_rules, &arg_t_rules);
1900
1901 } else {
1902 xlat_exp_t *child = NULL;
1903
1904 slen = xlat_tokenize_word(node->group, &child, &our_in, quote, our_p_rules, &arg_t_rules);
1905 if (child) {
1906 fr_assert(slen > 0);
1907
1908 xlat_exp_insert_tail(node->group, child);
1909 }
1910 }
1911 }
1912
1913 if (slen < 0) {
1914 error:
1915 if (our_p_rules == &tmp_p_rules) talloc_const_free(our_p_rules->terminals);
1917
1918 FR_SBUFF_ERROR_RETURN(&our_in); /* error */
1919 }
1920 fr_assert(node != NULL);
1921
1922 /*
1923 * No data, but the argument was required. Complain.
1924 */
1925 if (!slen && arg->required) {
1926 fr_strerror_printf("Missing required arg %u", argc);
1927 goto error;
1928 }
1929
1930 fr_assert(node->type == XLAT_GROUP);
1931 node->flags = node->group->flags;
1932
1933 /*
1934 * Check number of arguments.
1935 */
1936 if (arg->type == FR_TYPE_NULL) {
1937 fr_strerror_printf("Too many arguments, expected %zu, got %u",
1938 (size_t) (arg - arg_start), argc);
1939 fr_sbuff_set(&our_in, &m);
1940 goto error;
1941 }
1942
1943 if (!node->fmt) xlat_exp_set_name(node, fr_sbuff_current(&m), fr_sbuff_behind(&m));
1944
1945 /*
1946 * Ensure that the function args are correct.
1947 */
1948 if (xlat_validate_function_arg(arg, node, argc) < 0) {
1949 fr_sbuff_set(&our_in, &m);
1950 goto error;
1951 }
1952
1954
1955 /*
1956 * If we're not and the end of the string
1957 * and there's no whitespace between tokens
1958 * then error.
1959 */
1960 fr_sbuff_set(&m, &our_in);
1961 len = fr_sbuff_adv_past_whitespace(&our_in, SIZE_MAX, NULL);
1962
1963 /*
1964 * Commas are in the list of terminals, but we skip over them, and keep parsing more
1965 * arguments.
1966 */
1967 if (!spaces) {
1968 fr_assert(p_rules && p_rules->terminals);
1969
1970 if (fr_sbuff_next_if_char(&our_in, ',')) goto next;
1971
1972 if (fr_sbuff_is_char(&our_in, ')')) break;
1973
1974 if (fr_sbuff_eof(&our_in)) {
1975 fr_strerror_printf("Missing ')' after argument %u", argc);
1976 goto error;
1977 }
1978
1979 fr_strerror_printf("Unexpected text after argument %u", argc);
1980 goto error;
1981 }
1982
1983 /*
1984 * Check to see if we have a terminal char, which at this point has to be '``.
1985 */
1986 if (our_p_rules->terminals) {
1987 if (fr_sbuff_is_terminal(&our_in, our_p_rules->terminals)) break;
1988
1989 if (fr_sbuff_eof(&our_in)) {
1990 fr_strerror_printf("Unexpected end of input string after argument %u", argc);
1991 goto error;
1992 }
1993 }
1994
1995 /*
1996 * Otherwise, if we can extend, and found
1997 * no additional whitespace, it means two
1998 * arguments were smushed together.
1999 */
2000 if (fr_sbuff_extend(&our_in) && (len == 0)) {
2001 fr_strerror_const("Unexpected text after argument");
2002 goto error;
2003 }
2004 next:
2005 if (!arg->variadic) {
2006 arg++;
2007 argc++;
2008
2009 if (arg->type == FR_TYPE_NULL) {
2010 fr_strerror_printf("Too many arguments, expected %zu, got %u",
2011 (size_t) (arg - arg_start), argc);
2012 goto error;
2013 }
2014 }
2015 }
2016
2017 if (our_p_rules == &tmp_p_rules) talloc_const_free(our_p_rules->terminals);
2018
2020 *out = head;
2021
2022 FR_SBUFF_SET_RETURN(in, &our_in);
2023}
2024
2025/** Tokenize an xlat expansion
2026 *
2027 * @param[in] ctx to allocate dynamic buffers in.
2028 * @param[out] out the head of the xlat list / tree structure.
2029 * @param[in] in the format string to expand.
2030 * @param[in] p_rules controlling how the string containing the xlat
2031 * expansions should be parsed.
2032 * @param[in] t_rules controlling how attribute references are parsed.
2033 * @return
2034 * - >0 on success.
2035 * - 0 and *head == NULL - Parse failure on first char.
2036 * - 0 and *head != NULL - Zero length expansion
2037 * - < 0 the negative offset of the parse failure.
2038 */
2040 fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules)
2041{
2042 fr_sbuff_t our_in = FR_SBUFF(in);
2044 tmpl_rules_t default_rules = { .attr = { .list_def = request_attr_request }};
2045
2046 fr_assert(!t_rules || !t_rules->at_runtime || (t_rules->xlat.runtime_el != NULL));
2047
2048 /*
2049 * We need to track parse depth, which means using a
2050 * default set of rules, even if we weren't passed any.
2051 */
2052 if (!t_rules) t_rules = &default_rules;
2053
2055 fr_strerror_clear(); /* Clear error buffer */
2056
2057 if (xlat_tokenize_input(head, &our_in, p_rules, t_rules) < 0) {
2058 *out = NULL;
2060 FR_SBUFF_ERROR_RETURN(&our_in);
2061 }
2062
2063 /*
2064 * Add nodes that need to be bootstrapped to the
2065 * registry. If we can't do that, then return "didn't
2066 * parse it".
2067 *
2068 * We can't return an error here, because this is called
2069 * from the configuration file parser for modules. Which
2070 * doesn't pass in "allow_unresolved". But (TBD later),
2071 * it still gets resolved at run-time, and still works.
2072 *
2073 * So 'test.modules.mschap' fails if we return an error.
2074 *
2075 * @todo - fix this so that the call_envs are parsed at
2076 * load time, and not at run time.
2077 */
2078 if (xlat_finalize(head, t_rules->xlat.runtime_el) < 0) {
2079 *out = NULL;
2081 return 0;
2082 }
2083
2085 *out = head;
2086
2087 FR_SBUFF_SET_RETURN(in, &our_in);
2088}
2089
2090/** Check to see if the expansion consists entirely of value-box elements
2091 *
2092 * @param[in] head to check.
2093 * @return
2094 * - true if expansion contains only literal elements.
2095 * - false if expansion contains expandable elements.
2096 */
2098{
2099 xlat_exp_foreach(head, node) {
2100 if (node->type != XLAT_BOX) return false;
2101 }
2102
2103 return true;
2104}
2105
2106/** Check to see if the expansion needs resolving
2107 *
2108 * @param[in] head to check.
2109 * @return
2110 * - true if expansion needs resolving
2111 * - false otherwise
2112 */
2114{
2115 return head->flags.needs_resolving;
2116}
2117
2118/** Convert an xlat node to an unescaped literal string and free the original node
2119 *
2120 * This is really "unparse the xlat nodes, and convert back to their original string".
2121 *
2122 * @param[in] ctx to allocate the new string in.
2123 * @param[out] str a duplicate of the node's fmt string.
2124 * @param[in,out] head to convert.
2125 * @return
2126 * - true the tree consists of a single value node which was converted.
2127 * - false the tree was more complex than a single literal, op was a noop.
2128 */
2129bool xlat_to_string(TALLOC_CTX *ctx, char **str, xlat_exp_head_t **head)
2130{
2133 size_t len = 0;
2134
2135 if (!*head) return false;
2136
2137 /*
2138 * Instantiation functions may chop
2139 * up the node list into multiple
2140 * literals, so we need to walk the
2141 * list until we find a non-literal.
2142 */
2143 xlat_exp_foreach(*head, node) {
2144 if (node->type != XLAT_BOX) return false;
2145 len += talloc_strlen(node->fmt);
2146 }
2147
2148 fr_sbuff_init_talloc(ctx, &out, &tctx, len, SIZE_MAX);
2149
2150 xlat_exp_foreach(*head, node) {
2152 }
2153
2154 *str = fr_sbuff_buff(&out); /* No need to trim, should be the correct length */
2155
2156 return true;
2157}
2158
2159/** Walk over an xlat tree recursively, resolving any unresolved functions or references
2160 *
2161 * @param[in,out] head of xlat tree to resolve.
2162 * @param[in] xr_rules Specifies rules to use for resolution passes after initial
2163 * tokenization.
2164 * @return
2165 * - 0 on success.
2166 * - -1 on failure.
2167 */
2169{
2170 static xlat_res_rules_t xr_default;
2171 xlat_flags_t our_flags;
2172 xlat_t *func;
2173
2174 if (!head->flags.needs_resolving) return 0; /* Already done */
2175
2176 if (!xr_rules) xr_rules = &xr_default;
2177
2178 our_flags = XLAT_FLAGS_INIT;
2179
2180 xlat_exp_foreach(head, node) {
2181 /*
2182 * This node and none of its children need resolving
2183 */
2184 if (!node->flags.needs_resolving) {
2185 xlat_flags_merge(&our_flags, &node->flags);
2186 continue;
2187 }
2188
2189 switch (node->type) {
2190 case XLAT_GROUP:
2191 if (xlat_resolve(node->group, xr_rules) < 0) return -1;
2192 node->flags = node->group->flags;
2193 break;
2194
2195 /*
2196 * An unresolved function.
2197 */
2199 /*
2200 * Try to find the function
2201 */
2202 func = xlat_func_find(node->fmt, talloc_strlen(node->fmt));
2203 if (!func) {
2204 /*
2205 * FIXME - Produce proper error with marker
2206 */
2207 if (!xr_rules->allow_unresolved) {
2208 fr_strerror_printf("Failed resolving function %pV",
2210 return -1;
2211 }
2212 break;
2213 }
2214
2216 xlat_exp_set_func(node, func, xr_rules->tr_rules->dict_def);
2217
2218 /*
2219 * Check input arguments of our freshly resolved function
2220 */
2221 if (xlat_validate_function_args(node) < 0) return -1;
2222
2223 /*
2224 * Add the freshly resolved function
2225 * to the bootstrap tree.
2226 */
2227 if (xlat_instance_register_func(node) < 0) return -1;
2228
2229 /*
2230 * The function is now resolved, so we go through the normal process of resolving
2231 * its arguments, etc.
2232 */
2234
2235 /*
2236 * A resolved function with unresolved args. We re-initialize the flags from the
2237 * function definition, resolve the arguments, and update the flags.
2238 */
2239 case XLAT_FUNC:
2240 node->flags = node->call.func->flags;
2241
2242 if (node->call.func->resolve) {
2243 void *inst = node->call.inst ? node->call.inst->data : NULL;
2244
2245 if (node->call.func->resolve(node, inst, xr_rules) < 0) return -1;
2246
2247 } else if (node->call.args) {
2248 if (xlat_resolve(node->call.args, xr_rules) < 0) return -1;
2249
2250 } /* else the function takes no arguments */
2251
2252 node->flags.needs_resolving = false;
2254 XLAT_VERIFY(node);
2255 break;
2256
2257 case XLAT_TMPL:
2258 /*
2259 * Resolve any nested xlats in regexes, exec, or xlats.
2260 */
2261 if (tmpl_resolve(node->vpt, xr_rules->tr_rules) < 0) return -1;
2262
2263 fr_assert(!tmpl_needs_resolving(node->vpt));
2264 node->flags.needs_resolving = false;
2265
2266 if (xlat_tmpl_normalize(node) < 0) return -1;
2267 break;
2268
2269 default:
2270 fr_assert(0); /* boxes, one letter, etc. should not have been marked as unresolved */
2271 return -1;
2272 }
2273
2274 xlat_flags_merge(&our_flags, &node->flags);
2275 }
2276
2277 head->flags = our_flags;
2278
2279 fr_assert(!head->flags.needs_resolving);
2280
2281 /*
2282 * Resolving walks the whole tree, so check the result.
2283 */
2285
2286 return 0;
2287}
2288
2289
2290/** Try to convert an xlat to a tmpl for efficiency
2291 *
2292 * @param ctx to allocate new tmpl_t in.
2293 * @param head to convert.
2294 * @return
2295 * - NULL if unable to convert (not necessarily error).
2296 * - A new #tmpl_t.
2297 */
2299{
2300 tmpl_t *vpt;
2301 xlat_exp_t *node = xlat_exp_head(head);
2302
2303 if (!node || (node->type != XLAT_TMPL) || !tmpl_is_attr(node->vpt)) return NULL;
2304
2305 /*
2306 * Concat means something completely different as an attribute reference
2307 * Count isn't implemented.
2308 */
2309 if ((tmpl_attr_tail_num(node->vpt) == NUM_COUNT) || (tmpl_attr_tail_num(node->vpt) == NUM_ALL)) return NULL;
2310
2312 if (!vpt) return NULL;
2313
2314 tmpl_attr_copy(vpt, node->vpt);
2315
2317
2318 return vpt;
2319}
2320
2322{
2323 return head->flags.impure_func;
2324}
2325
2326/*
2327 * Return output data type of an expansionm node.
2328 */
2330{
2331 if (node->quote != T_BARE_WORD) return FR_TYPE_STRING;
2332
2333 if (node->type == XLAT_FUNC) {
2334 if (node->call.func->func != xlat_func_cast) {
2335 return node->call.func->return_type;
2336 }
2337
2338 /*
2339 * "cast" is special. It nominally returns VOID, but only because the first argument is
2340 * the actual type.
2341 */
2342 node = xlat_exp_head(node->call.args);
2343 fr_assert(node != NULL);
2344 fr_assert(node->type == XLAT_GROUP);
2345 node = xlat_exp_head(node->group);
2346 fr_assert(node != NULL);
2347 fr_assert(node->type == XLAT_BOX);
2348 fr_assert(node->data.type == FR_TYPE_UINT8);
2349 return (fr_type_t)node->data.vb_uint8;
2350 }
2351
2352 if (node->type == XLAT_TMPL) {
2353 return tmpl_data_type(node->vpt);
2354 }
2355
2356 return FR_TYPE_NULL;
2357}
2358
2359/*
2360 * Return output data type of an expansion list.
2361 */
2363{
2364 xlat_exp_t const *node;
2365
2366 node = xlat_exp_head(head);
2367 if (!fr_cond_assert(node)) return FR_TYPE_NULL;
2368
2369 /*
2370 * There are multiple things in this expansion. We don't want to return only the data type of
2371 * the first one.
2372 */
2373 if (xlat_exp_next(head, node)) return FR_TYPE_NULL;
2374
2375 return xlat_node_data_type(node);
2376}
int n
Definition acutest.h:642
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
Definition build.h:203
#define RCSID(id)
Definition build.h:603
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:245
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition build.h:422
#define unlikely(_x)
Definition build.h:486
#define NUM_ELEMENTS(_t)
Definition build.h:437
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:177
#define fr_assert_fail(_msg,...)
Calls panic_action ifndef NDEBUG, else logs error.
Definition debug.h:254
#define MEM(x)
Definition debug.h:38
#define ERROR(fmt,...)
Definition dhcpclient.c:40
#define DEBUG(fmt,...)
Definition dhcpclient.c:38
#define da_is_bit_field(_da)
Definition dict.h:171
static unsigned int fr_dlist_num_elements(fr_dlist_head_t const *head)
Return the number of elements in the dlist.
Definition dlist.h:921
static void * fr_dlist_pop_head(fr_dlist_head_t *list_head)
Remove the head item in a list.
Definition dlist.h:654
#define FR_DLIST_HEAD(_name)
Expands to the type name used for the head wrapper structure.
Definition dlist.h:1143
xlat_action_t xlat_func_cast(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
Cast one or more output value-boxes to the given type.
talloc_free(hp)
static const bool escapes[SBUFF_CHAR_CLASS]
Definition util.c:40
#define SBUFF_CHAR_CLASS
fr_type_t
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_NULL
Invalid (uninitialised) attribute type.
@ FR_TYPE_UINT8
8 Bit unsigned integer.
@ FR_TYPE_VOID
User data.
@ FR_TYPE_BOOL
A truth value.
long int ssize_t
unsigned char uint8_t
ssize_t fr_slen_t
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:356
#define fr_assert(_expr)
Definition rad_assert.h:37
static bool done
Definition radclient.c:80
static const char * spaces
Definition radict.c:185
fr_dict_attr_t const * request_attr_request
Definition request.c:43
size_t fr_sbuff_adv_past_allowed(fr_sbuff_t *sbuff, size_t len, bool const allowed[static SBUFF_CHAR_CLASS], fr_sbuff_term_t const *tt)
Wind position past characters in the allowed set.
Definition sbuff.c:1936
bool fr_sbuff_is_terminal(fr_sbuff_t *in, fr_sbuff_term_t const *tt)
Efficient terminal string search.
Definition sbuff.c:2311
bool const sbuff_char_alpha_num[SBUFF_CHAR_CLASS]
Definition sbuff.c:110
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:2011
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:1573
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:675
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:2247
#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:191
char const * name
Name for rule set to aid we debugging.
Definition sbuff.h:210
#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 fr_sbuff_uint8(_sbuff_or_marker, _eob)
#define SBUFF_CHAR_UNPRINTABLES_EXTENDED
#define FR_SBUFF(_sbuff_or_marker)
#define fr_sbuff_advance(_sbuff_or_marker, _len)
#define fr_sbuff_out(_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:179
#define FR_SBUFF_IN_STRCPY_RETURN(...)
#define FR_SBUFF_IN_BSTRCPY_BUFFER_RETURN(...)
Set of terminal elements.
Talloc sbuff extension structure.
Definition sbuff.h:138
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:908
#define tmpl_contains_xlat(vpt)
Definition tmpl.h:234
#define TMPL_VERIFY(_vpt)
Definition tmpl.h:984
#define tmpl_is_xlat(vpt)
Definition tmpl.h:217
#define tmpl_is_attr_unresolved(vpt)
Definition tmpl.h:226
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:960
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:233
fr_value_box_safe_for_t literals_safe_for
safe_for value assigned to literal values in xlats, execs, and data.
Definition tmpl.h:360
#define tmpl_is_attr(vpt)
Definition tmpl.h:215
#define NUM_ALL
Definition tmpl.h:418
#define tmpl_is_exec(vpt)
Definition tmpl.h:218
fr_dict_attr_t const * enumv
Enumeration attribute used to resolve enum values.
Definition tmpl.h:349
#define tmpl_value_enumv(_tmpl)
Definition tmpl.h:963
#define tmpl_xlat(_tmpl)
Definition tmpl.h:953
static fr_dict_attr_t const * tmpl_list(tmpl_t const *vpt)
Definition tmpl.h:927
@ TMPL_ATTR_LIST_ALLOW
Attribute refs are allowed to have a list.
Definition tmpl.h:269
#define tmpl_rules_cast(_tmpl)
Definition tmpl.h:965
#define NUM_COUNT
Definition tmpl.h:419
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.
tmpl_attr_error_t
Definition tmpl.h:1027
@ TMPL_ATTR_ERROR_MISSING_TERMINATOR
Unexpected text found after attribute reference.
Definition tmpl.h:1050
fr_slen_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.
fr_type_t tmpl_data_type(tmpl_t const *vpt)
Definition tmpl_eval.c:1343
tmpl_xlat_rules_t xlat
Rules/data for parsing xlats.
Definition tmpl.h:347
bool at_runtime
Produce an ephemeral/runtime tmpl.
Definition tmpl.h:357
#define tmpl_is_data(vpt)
Definition tmpl.h:213
static fr_slen_t vpt
Definition tmpl.h:1298
fr_dict_t const * dict_def
Alternative default dictionary to use if vpt->rules->dict_def is NULL.
Definition tmpl.h:392
#define tmpl_rules_copy_depth(_out, _in)
Copy parse rules for a nested parse, and increment the depth field.
Definition tmpl.h:373
#define NUM_UNSPEC
Definition tmpl.h:417
#define tmpl_value_type(_tmpl)
Definition tmpl.h:962
static fr_type_t tmpl_cast_get(tmpl_t *vpt)
Definition tmpl.h:1249
#define tmpl_is_data_unresolved(vpt)
Definition tmpl.h:224
fr_type_t cast
Whether there was an explicit cast.
Definition tmpl.h:351
tmpl_attr_rules_t attr
Rules/data for parsing attribute references.
Definition tmpl.h:346
bool tmpl_rules_depth_exceeded(tmpl_rules_t const *t_rules)
True when a parse has nested past the point where the stack is safe.
@ TMPL_TYPE_ATTR
Reference to one or more attributes.
Definition tmpl.h:149
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:824
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:938
static fr_slen_t rql ssize_t tmpl_attr_print(fr_sbuff_t *out, tmpl_t const *vpt)
Print an attribute or list tmpl_t to a string.
fr_event_list_t * runtime_el
The eventlist to use for runtime instantiation of xlats.
Definition tmpl.h:335
#define tmpl_needs_resolving(vpt)
Definition tmpl.h:230
Optional arguments passed to vp_tmpl functions.
Definition tmpl.h:343
eap_aka_sim_process_conf_t * inst
fr_aka_sim_id_type_t type
Define entry and head types for tmpl request references.
Definition tmpl.h:279
tmpl_attr_list_presence_t list_presence
Whether the attribute reference can have a list, forbid it, or require it.
Definition tmpl.h:305
fr_dict_attr_t const * list_def
Default list to use with unqualified attribute reference.
Definition tmpl.h:302
unsigned int allow_wildcard
Allow the special case of .
Definition tmpl.h:318
fr_dict_t const * dict_def
Default dictionary to use with unqualified attribute references.
Definition tmpl.h:280
unsigned int allow_unresolved
Allow attributes that look valid but were not found in the dictionaries.
Definition tmpl.h:313
Define manipulation functions for the attribute reference list.
Definition tmpl.h:498
tmpl_request_ref_t _CONST request
Definition tmpl.h:502
An element in a lexicographically sorted array of name to num mappings.
Definition table.h:49
static int talloc_const_free(void const *ptr)
Free const'd memory.
Definition talloc.h:289
#define talloc_strdup(_ctx, _str)
Definition talloc.h:149
static size_t talloc_strlen(char const *s)
Returns the length of a talloc array containing a string.
Definition talloc.h:143
const char fr_token_quote[T_TOKEN_LAST]
Convert tokens back to a quoting character.
Definition token.c:222
@ T_SINGLE_QUOTED_STRING
Definition token.h:120
@ T_BARE_WORD
Definition token.h:118
@ T_BACK_QUOTED_STRING
Definition token.h:121
@ T_DOUBLE_QUOTED_STRING
Definition token.h:119
@ T_SOLIDUS_QUOTED_STRING
Definition token.h:122
enum fr_token fr_token_t
tmpl_res_rules_t const * tr_rules
tmpl resolution rules.
Definition xlat.h:155
fr_type_t type
Type to cast argument to.
Definition xlat.h:145
#define XLAT_HEAD_VERIFY(_head)
Definition xlat.h:456
#define XLAT_FLAGS_INIT
Definition xlat.h:119
unsigned int pure
has no external side effects, true for BOX, LITERAL, and some functions
Definition xlat.h:110
int xlat_instance_register_func(xlat_exp_t *node)
Callback for creating "permanent" instance data for a xlat_exp_t.
Definition xlat_inst.c:594
unsigned int xlat
it's an xlat wrapper
Definition xlat.h:115
fr_value_box_escape_func_t func
Function to escape unsafe values.
Definition xlat.h:146
bool allow_unresolved
If false, all resolution steps must be completed.
Definition xlat.h:156
@ XLAT_ARG_VARIADIC_EMPTY_SQUASH
Empty argument groups are removed.
Definition xlat.h:126
static fr_slen_t head
Definition xlat.h:410
xlat_arg_parser_variadic_t variadic
All additional boxes should be processed using this definition.
Definition xlat.h:143
fr_value_box_safe_for_t safe_for
Escaped value to set for boxes processed by this escape function.
Definition xlat.h:147
unsigned int required
Argument must be present, and non-empty.
Definition xlat.h:136
#define XLAT_VERIFY(_node)
Definition xlat.h:455
unsigned int use_module_status
use the module thread status to force early failure.
Definition xlat.h:116
#define XLAT_ARG_PARSER_TERMINATOR
Definition xlat.h:160
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:699
unsigned int can_purify
if the xlat has a pure function with pure arguments.
Definition xlat.h:112
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:3224
unsigned int will_escape
the function will do escaping and concatenation.
Definition xlat.h:140
unsigned int constant
xlat is just tmpl_attr_tail_data, or XLAT_BOX
Definition xlat.h:114
unsigned int needs_resolving
Needs pass2 resolution.
Definition xlat.h:109
Definition for a single argument consumed by an xlat function.
Definition xlat.h:135
Flags that control resolution and evaluation.
Definition xlat.h:108
char const * fr_strerror(void)
Get the last library error.
Definition strerror.c:558
void fr_strerror_clear(void)
Clears all pending messages from the talloc pools.
Definition strerror.c:581
#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_ATTR
A contains an attribute reference.
Definition types.h:83
@ FR_TYPE_PAIR_CURSOR
cursor over a fr_pair_t
Definition types.h:90
#define fr_type_is_null(_x)
Definition types.h:347
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
Definition types.h:454
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:6232
fr_sbuff_parse_rules_t const * value_parse_rules_quoted[T_TOKEN_LAST]
Parse rules for quoted strings.
Definition value.c:620
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:4265
fr_sbuff_parse_rules_t const * value_parse_rules_3quoted[T_TOKEN_LAST]
Definition value.c:636
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:4702
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:6472
fr_sbuff_parse_rules_t const value_parse_rules_bareword_quoted
Definition value.c:538
int fr_value_box_bstr_realloc(TALLOC_CTX *ctx, char **out, fr_value_box_t *dst, size_t len)
Change the length of a buffer already allocated to a value box.
Definition value.c:4912
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:4953
fr_sbuff_escape_rules_t const * fr_value_escape_by_quote[T_TOKEN_LAST]
Definition value.c:455
static fr_sbuff_err_t char size_t fr_sbuff_t * in
Definition value.h:1061
#define fr_value_box_mark_safe_for(_box, _safe_for)
Definition value.h:1124
#define fr_box_strvalue_buffer(_val)
Definition value.h:343
#define fr_box_strvalue_len(_val, _len)
Definition value.h:340
static fr_sbuff_err_t char ** out
Definition value.h:1061
static fr_sbuff_err_t char size_t * len
Definition value.h:1061
#define FR_VALUE_BOX_SAFE_FOR_NONE
Definition value.h:177
uintptr_t fr_value_box_safe_for_t
Escaping that's been applied to a value box.
Definition value.h:167
int nonnull(2, 5))
#define fr_value_box_init(_vb, _type, _enumv, _tainted)
Initialise a fr_value_box_t.
Definition value.h:634
#define FR_VALUE_BOX_SAFE_FOR_ANY
Definition value.h:178
void xlat_exp_finalize_func(xlat_exp_t *node)
Definition xlat_alloc.c:310
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:278
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:334
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:300
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:364
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:276
xlat_flags_t flags
Flags that control resolution and evaluation.
Definition xlat_priv.h:156
@ XLAT_ONE_LETTER
Special "one-letter" expansion.
Definition xlat_priv.h:111
@ XLAT_BOX
fr_value_box_t
Definition xlat_priv.h:110
@ XLAT_TMPL
xlat attribute
Definition xlat_priv.h:114
@ XLAT_FUNC
xlat module
Definition xlat_priv.h:112
@ XLAT_GROUP
encapsulated string of xlats
Definition xlat_priv.h:118
@ XLAT_FUNC_UNRESOLVED
func needs resolution during pass2.
Definition xlat_priv.h:113
@ XLAT_INVALID
Bad expansion.
Definition xlat_priv.h:109
#define xlat_exp_alloc_null(_ctx)
Definition xlat_priv.h:282
static xlat_exp_t * xlat_exp_next(xlat_exp_head_t const *head, xlat_exp_t const *node)
Definition xlat_priv.h:249
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:154
xlat_arg_parser_t const * args
Definition of args consumed.
Definition xlat_priv.h:96
static void xlat_flags_merge(xlat_flags_t *parent, xlat_flags_t const *child)
Merge flags from child to parent.
Definition xlat_priv.h:232
#define xlat_exp_set_type(_node, _type)
Definition xlat_priv.h:279
char const *_CONST fmt
The original format string (a talloced buffer).
Definition xlat_priv.h:153
xlat_type_t _CONST type
type of this expansion.
Definition xlat_priv.h:157
bool private
If true, can only be created by the parser, and is invisible to func() in the input.
Definition xlat_priv.h:68
#define xlat_exp_alloc(_ctx, _type, _in, _inlen)
Definition xlat_priv.h:285
#define xlat_exp_foreach(_list_head, _iter)
Iterate over the contents of a list, only one level.
Definition xlat_priv.h:225
static int xlat_exp_insert_tail(xlat_exp_head_t *head, xlat_exp_t *node)
Definition xlat_priv.h:241
static xlat_exp_t * xlat_exp_head(xlat_exp_head_t const *head)
Definition xlat_priv.h:212
An xlat expansion node.
Definition xlat_priv.h:150
bool xlat_needs_resolving(xlat_exp_head_t const *head)
Check to see if the expansion needs resolving.
#define INFO_INDENT(_fmt,...)
static bool const tmpl_attr_allowed_chars[SBUFF_CHAR_CLASS]
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.
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)
static int xlat_validate_function_arg(xlat_arg_parser_t const *arg_p, xlat_exp_t *arg, unsigned int argc)
Validate and sanity check function arguments.
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
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 const xlat_func_chars[SBUFF_CHAR_CLASS]
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)
fr_slen_t xlat_tokenize_argv(TALLOC_CTX *ctx, xlat_exp_head_t **out, fr_sbuff_t *in, xlat_arg_parser_t const *xlat_args, 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.
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 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_node_data_type(xlat_exp_t const *node)
fr_type_t xlat_data_type(xlat_exp_head_t const *head)