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