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