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