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