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