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