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