The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
tmpl_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: 72e52f6ef937f77d1f914c08dc34eec1874c2263 $
19 *
20 * @brief #fr_pair_t template functions
21 * @file src/lib/server/tmpl_tokenize.c
22 *
23 * @ingroup AVP
24 *
25 * @copyright 2014-2020 The FreeRADIUS server project
26 */
27RCSID("$Id: 72e52f6ef937f77d1f914c08dc34eec1874c2263 $")
28
29#define _TMPL_PRIVATE 1
30
31#include <freeradius-devel/server/tmpl.h>
32#include <freeradius-devel/server/base.h>
33#include <freeradius-devel/protocol/freeradius/freeradius.internal.h>
34
35#include <freeradius-devel/util/debug.h>
36#include <freeradius-devel/util/base16.h>
37#include <freeradius-devel/util/misc.h>
38
39#include <freeradius-devel/util/sbuff.h>
40#include <freeradius-devel/util/value.h>
41
42#include <ctype.h>
43
44/*
45 * Migration flag for enum prefixes.
46 */
47extern bool tmpl_require_enum_prefix;
48
50
51/** Define a global variable for specifying a default request reference
52 *
53 * @param[in] _name what the global variable should be called.
54 * @param[in] _ref one of the values of tmpl_request_ref_t
55 * - REQUEST_CURRENT
56 * - REQUEST_OUTER,
57 * - REQUEST_PARENT,
58 * - REQUEST_UNKNOWN
59 */
60#define TMPL_REQUEST_REF_DEF(_name, _ref) \
61static tmpl_request_t _name ## _entry = { \
62 .entry = { \
63 .entry = { \
64 .next = &_name.head.entry, \
65 .prev = &_name.head.entry \
66 } \
67 }, \
68 .request = _ref \
69}; \
70FR_DLIST_HEAD(tmpl_request_list) _name = { \
71 .head = { \
72 .offset = offsetof(tmpl_request_t, entry), \
73 .entry = { \
74 .next = &_name ## _entry.entry.entry, \
75 .prev = &_name ## _entry.entry.entry, \
76 }, \
77 .num_elements = 1, \
78 } \
79}
80
81/** Use the current request as the default
82 *
83 * Used as .attr.request_def = \&tmpl_request_def_current;
84 */
85TMPL_REQUEST_REF_DEF(tmpl_request_def_current, REQUEST_CURRENT);
86
87/** Use the outer request as the default
88 *
89 * Used as .attr.request_def = \&tmpl_request_def_outer;
90 */
91TMPL_REQUEST_REF_DEF(tmpl_request_def_outer, REQUEST_OUTER);
92
93/** Use the parent request as the default
94 *
95 * Used as .attr.request_def = \&tmpl_request_def_parent;
96 */
97TMPL_REQUEST_REF_DEF(tmpl_request_def_parent, REQUEST_PARENT);
98
99/** Default parser rules
100 *
101 * Because this is getting to be a ridiculous number of parsing rules
102 * to pass in via arguments.
103 *
104 * Defaults are used if a NULL rules pointer is passed to the parsing function.
105 */
106#define DEFAULT_RULES tmpl_rules_t default_rules = { .attr = { .list_def = request_attr_request }}
107
108#define CHECK_T_RULES do { \
109 if (!t_rules) { \
110 t_rules = &default_rules; \
111 if (tmpl_require_enum_prefix) default_rules.attr.prefix = TMPL_ATTR_REF_PREFIX_AUTO; \
112 } else if (tmpl_require_enum_prefix && (t_rules->attr.prefix == TMPL_ATTR_REF_PREFIX_YES)) { \
113 default_rules = *t_rules; \
114 default_rules.attr.prefix = TMPL_ATTR_REF_PREFIX_AUTO; \
115 t_rules = &default_rules; \
116 } \
117 } while (0)
118
119
120/* clang-format off */
121/** Map #tmpl_type_t values to descriptive strings
122 */
124 { L("uninitialised"), TMPL_TYPE_UNINITIALISED },
125
126 { L("null"), TMPL_TYPE_NULL },
127 { L("data"), TMPL_TYPE_DATA },
128
129 { L("attr"), TMPL_TYPE_ATTR },
130
131 { L("exec"), TMPL_TYPE_EXEC },
132 { L("xlat"), TMPL_TYPE_XLAT },
133
134 { L("regex"), TMPL_TYPE_REGEX },
135 { L("regex-uncompiled"), TMPL_TYPE_REGEX_UNCOMPILED },
136 { L("regex-xlat"), TMPL_TYPE_REGEX_XLAT },
137
138 { L("data-unresolved"), TMPL_TYPE_DATA_UNRESOLVED },
139 { L("attr-unresolved"), TMPL_TYPE_ATTR_UNRESOLVED },
140 { L("exec-unresolved"), TMPL_TYPE_EXEC_UNRESOLVED },
141 { L("xlat-unresolved"), TMPL_TYPE_XLAT_UNRESOLVED },
142 { L("regex-unresolved"), TMPL_TYPE_REGEX_XLAT_UNRESOLVED }
143};
145
146/** Attr ref types
147 */
149 { L("normal"), TMPL_ATTR_TYPE_NORMAL },
150 { L("unspecified"), TMPL_ATTR_TYPE_UNSPEC },
151 { L("unknown"), TMPL_ATTR_TYPE_UNKNOWN },
152 { L("unresolved"), TMPL_ATTR_TYPE_UNRESOLVED }
153};
155
156/** Map keywords to #tmpl_request_ref_t values
157 */
159 { L("current"), REQUEST_CURRENT },
160 { L("outer"), REQUEST_OUTER },
161 { L("parent"), REQUEST_PARENT },
162};
164
165
166/** Special attribute reference indexes
167 */
169 { L("*"), NUM_ALL },
170 { L("#"), NUM_COUNT },
171 { L("u"), NUM_UNSPEC },
172 { L("n"), NUM_LAST }
173};
175/* clang-format on */
176
177static void attr_to_raw(tmpl_t *vpt, tmpl_attr_t *ref);
178
179/*
180 * Can't use |= or ^= else we get out of range errors
181 */
182#define UNRESOLVED_SET(_flags) (*(_flags) = (*(_flags) | TMPL_FLAG_UNRESOLVED))
183#define RESOLVED_SET(_flags) (*(_flags) = (*(_flags) & ~TMPL_FLAG_UNRESOLVED))
184
185/** Verify, after skipping whitespace, that a substring ends in a terminal char, or ends without further chars
186 *
187 * @param[in] in the sbuff to check.
188 * @param[in] p_rules to use terminals from.
189 * @return
190 * - true if substr is terminated correctly.
191 * - false if subst is not terminated correctly.
192 */
193static inline bool CC_HINT(always_inline) tmpl_substr_terminal_check(fr_sbuff_t *in,
194 fr_sbuff_parse_rules_t const *p_rules)
195{
197 bool ret;
198
199 if (!fr_sbuff_extend(in)) return true; /* we're at the end of the string */
200 if (!p_rules || !p_rules->terminals) return false; /* more stuff to parse but don't have a terminal set */
201
202 fr_sbuff_marker(&m, in);
203 ret = fr_sbuff_is_terminal(in, p_rules->terminals);
204 fr_sbuff_set(in, &m);
205 fr_sbuff_marker_release(&m);
206 return ret;
207}
208
209void tmpl_attr_ref_debug(const tmpl_attr_t *ar, int i)
210{
211 char buffer[sizeof(STRINGIFY(INT16_MAX)) + 1];
212
213 snprintf(buffer, sizeof(buffer), "%i", ar->ar_num);
214
215 switch (ar->type) {
219 if (!ar->da) {
220 FR_FAULT_LOG("\t[%u] %s null%s%s%s",
221 i,
222 fr_table_str_by_value(attr_table, ar->type, "<INVALID>"),
223 ar->ar_num != NUM_UNSPEC ? "[" : "",
224 ar->ar_num != NUM_UNSPEC ? fr_table_str_by_value(attr_num_table, ar->ar_num, buffer) : "",
225 ar->ar_num != NUM_UNSPEC ? "]" : "");
226 return;
227 }
228
229 FR_FAULT_LOG("\t[%u] %s %s %s%s%s%s (%p) attr %u",
230 i,
231 fr_table_str_by_value(attr_table, ar->type, "<INVALID>"),
232 fr_type_to_str(ar->da->type),
233 ar->da->name,
234 ar->ar_num != NUM_UNSPEC ? "[" : "",
235 ar->ar_num != NUM_UNSPEC ? fr_table_str_by_value(attr_num_table, ar->ar_num, buffer) : "",
236 ar->ar_num != NUM_UNSPEC ? "]" : "",
237 ar->da,
238 ar->da->attr
239 );
240 FR_FAULT_LOG("\t is_raw : %s", ar_is_raw(ar) ? "yes" : "no");
241 FR_FAULT_LOG("\t is_unknown : %s", ar_is_unknown(ar) ? "yes" : "no");
242 if (ar->ar_parent) FR_FAULT_LOG("\t parent : %s (%p)", ar->ar_parent->name, ar->ar_parent);
243 break;
244
245
247 /*
248 * Type reveals unresolved status
249 * so we don't need to add it explicitly
250 */
251 FR_FAULT_LOG("\t[%u] %s %s%s%s%s",
252 i,
253 fr_table_str_by_value(attr_table, ar->type, "<INVALID>"),
254 ar->ar_unresolved,
255 ar->ar_num != NUM_UNSPEC ? "[" : "",
256 ar->ar_num != NUM_UNSPEC ? fr_table_str_by_value(attr_num_table, ar->ar_num, buffer) : "",
257 ar->ar_num != NUM_UNSPEC ? "]" : "");
258 if (ar->ar_parent) FR_FAULT_LOG("\t parent : %s", ar->ar_parent->name);
259 if (ar->ar_unresolved_namespace) FR_FAULT_LOG("\t namespace : %s", ar->ar_unresolved_namespace->name);
260 break;
261
262 default:
263 FR_FAULT_LOG("\t[%u] Bad type %s(%u)",
264 i, fr_table_str_by_value(attr_table, ar->type, "<INVALID>"), ar->type);
265 break;
266 }
267}
268
269void tmpl_attr_ref_list_debug(FR_DLIST_HEAD(tmpl_attr_list) const *ar_head)
270{
271 tmpl_attr_t *ar = NULL;
272 unsigned int i = 0;
273
274 FR_FAULT_LOG("attribute references:");
275 /*
276 * Print all the attribute references
277 */
278 while ((ar = tmpl_attr_list_next(ar_head, ar))) {
279 tmpl_attr_ref_debug(ar, i);
280 i++;
281 }
282}
283
285{
286 tmpl_request_t *rr = NULL;
287 unsigned int i = 0;
288
289 switch (vpt->type) {
290 case TMPL_TYPE_ATTR:
292 break;
293
294 default:
295 FR_FAULT_LOG("%s can't print tmpls of type %s", __FUNCTION__,
296 tmpl_type_to_str(vpt->type));
297 return;
298 }
299
300 FR_FAULT_LOG("tmpl_t %s (%.8x) \"%pV\" (%p)",
301 tmpl_type_to_str(vpt->type),
302 vpt->type,
303 fr_box_strvalue_len(vpt->name, vpt->len), vpt);
304
306 FR_FAULT_LOG("\tquote : %s", fr_table_str_by_value(fr_token_quotes_table, vpt->quote, "<INVALID>"));
307
308 FR_FAULT_LOG("request references:");
309
310 /*
311 * Print all the request references
312 */
313 while ((rr = tmpl_request_list_next(&vpt->data.attribute.rr, rr))) {
314 FR_FAULT_LOG("\t[%u] %s (%u)", i,
316 i++;
317 }
318
319 FR_FAULT_LOG("list: %s", tmpl_list_name(tmpl_list(vpt), "<INVALID>"));
321}
322
324{
325 switch (vpt->type) {
326 case TMPL_TYPE_ATTR:
329 return;
330
331 default:
332 break;
333 }
334
335 FR_FAULT_LOG("tmpl_t %s (%.8x) \"%s\" (%p)",
336 tmpl_type_to_str(vpt->type),
337 vpt->type,
338 vpt->name, vpt);
339
341 FR_FAULT_LOG("\tquote : %s", fr_table_str_by_value(fr_token_quotes_table, vpt->quote, "<INVALID>"));
342 switch (vpt->type) {
343 case TMPL_TYPE_NULL:
344 return;
345
346 case TMPL_TYPE_DATA:
348 FR_FAULT_LOG("\tlen : %zu", tmpl_value_length(vpt));
349 FR_FAULT_LOG("\tvalue : %pV", tmpl_value(vpt));
350
351 if (tmpl_value_enumv(vpt)) FR_FAULT_LOG("\tenumv : %s (%p)",
353 return;
354
355 case TMPL_TYPE_XLAT:
356 case TMPL_TYPE_EXEC:
358 {
359 char *str;
360
361 xlat_aprint(NULL, &str, tmpl_xlat(vpt), NULL);
362
363 FR_FAULT_LOG("\texpansion : %s", str);
364
365 talloc_free(str);
366 }
367 break;
368
369 case TMPL_TYPE_REGEX:
370 {
371 FR_FAULT_LOG("\tpattern : %s", vpt->name);
372 }
373 break;
374
375 default:
378 FR_FAULT_LOG("\tunescaped : %s", vpt->data.unescaped);
379 FR_FAULT_LOG("\tlen : %zu", talloc_array_length(vpt->data.unescaped) - 1);
380 } else {
381 FR_FAULT_LOG("\tunresolved : %s", vpt->name);
382 FR_FAULT_LOG("\tlen : %zu", vpt->len);
383 }
384 } else {
385 FR_FAULT_LOG("debug nyi");
386 }
387 break;
388 }
389}
390
391/** @name Parse list and request qualifiers to #fr_pair_list_t and #tmpl_request_ref_t values
392 *
393 * These functions also resolve #fr_pair_list_t and #tmpl_request_ref_t values to #request_t
394 * structs and the head of #fr_pair_t lists in those structs.
395 *
396 * For adding new #fr_pair_t to the lists, the #tmpl_list_ctx function can be used
397 * to obtain the appropriate TALLOC_CTX pointer.
398 *
399 * @note These don't really have much to do with #tmpl_t. They're in the same
400 * file as they're used almost exclusively by the tmpl_* functions.
401 * @{
402 */
403
404/** Parse one a single list reference
405 *
406 * @param[out] da_p attribute representing a list.
407 * @param[in] in Sbuff to read request references from.
408 * @return
409 * - > 0 the number of bytes parsed.
410 * - 0 no list qualifier found.
411 */
413{
414 fr_dict_attr_t const *da;
415 fr_sbuff_t our_in = FR_SBUFF(in);
416
417 if (((fr_sbuff_adv_past_strcase(&our_in, request_attr_request->name, request_attr_request->name_len)) &&
418 (da = request_attr_request)) ||
420 (da = request_attr_reply)) ||
422 (da = request_attr_control)) ||
424 (da = request_attr_state))) {
425 /* note: no local variables */
426 *da_p = da;
427 FR_SBUFF_SET_RETURN(in, &our_in);
428 }
429
430 return 0;
431}
432
433 /** Allocate a new request reference and add it to the end of the attribute reference list
434 *
435 */
436static inline CC_HINT(always_inline) CC_HINT(nonnull(2,3))
437void tmpl_request_ref_list_copy(TALLOC_CTX *ctx,
438 FR_DLIST_HEAD(tmpl_request_list) *out, FR_DLIST_HEAD(tmpl_request_list) const *in)
439{
440 tmpl_request_t *rr = NULL;
441 tmpl_request_t *n_rr = NULL;
442
443 /*
444 * Duplicate the complete default list
445 */
446 while ((rr = tmpl_request_list_next(in, rr))) {
447 MEM(n_rr = talloc(ctx, tmpl_request_t));
448 *n_rr = (tmpl_request_t){
449 .request = rr->request
450 };
451 tmpl_request_list_insert_tail(out, n_rr);
452 ctx = n_rr; /* Chain the contexts */
453 }
454}
455
456 /** Allocate a new request reference list and copy request references into it
457 *
458 */
459static inline CC_HINT(always_inline) CC_HINT(nonnull(2,3))
460void tmpl_request_ref_list_acopy(TALLOC_CTX *ctx,
461 FR_DLIST_HEAD(tmpl_request_list) **out, FR_DLIST_HEAD(tmpl_request_list) const *in)
462{
463 FR_DLIST_HEAD(tmpl_request_list) *rql;
464
465 MEM(rql = talloc_zero(ctx, FR_DLIST_HEAD(tmpl_request_list)));
466 tmpl_request_list_talloc_init(rql);
467
469
470 *out = rql;
471}
472
473/** Dump a request list to stderr
474 *
475 */
476void tmpl_request_ref_list_debug(FR_DLIST_HEAD(tmpl_request_list) const *rql)
477{
478 tmpl_request_t *rr = NULL;
479
480 while ((rr = tmpl_request_list_next(rql, rr))) {
481 FR_FAULT_LOG("request - %s (%u)",
483 rr->request);
484 }
485}
486
487/** Compare a list of request qualifiers
488 *
489 * @param[in] a first list. If NULL tmpl_request_def_current will be used.
490 * @param[in] b second list. If NULL tmpl_request_def_current will be used.
491 * @return
492 * - >0 a > b
493 * - 0 a == b
494 * - <0 a < b
495 */
496int8_t tmpl_request_ref_list_cmp(FR_DLIST_HEAD(tmpl_request_list) const *a, FR_DLIST_HEAD(tmpl_request_list) const *b)
497{
498 tmpl_request_t *a_rr = NULL, *b_rr = NULL;
499
500 /*
501 * NULL, uninit, empty are all equivalent
502 * to tmpl_request_def_current.
503 *
504 * We need all these equivalent checks to
505 * deal with uninitialised tmpl rules.
506 */
507 if (!a || !tmpl_request_list_initialised(a) || tmpl_request_list_empty(a)) a = &tmpl_request_def_current;
508 if (!b || !tmpl_request_list_initialised(b) || tmpl_request_list_empty(b)) b = &tmpl_request_def_current;
509
510 /*
511 * Fast path...
512 */
513 if (a == b) return 0;
514
515 for (;;) {
516 a_rr = tmpl_request_list_next(a, a_rr);
517 b_rr = tmpl_request_list_next(b, b_rr);
518
519 if (!a_rr || !b_rr) return CMP(tmpl_request_list_num_elements(a), tmpl_request_list_num_elements(b));
520
521 CMP_RETURN(a_rr, b_rr, request);
522 }
523}
524
525/** Parse one or more request references, writing the list to out
526 *
527 * @param[in] ctx to allocate request refs in.
528 * @param[out] err If !NULL where to write the parsing error.
529 * @param[in] out The list to write to.
530 * @param[in] in Sbuff to read request references from.
531 * @param[in] p_rules Parse rules.
532 * @param[in] t_rules Default list and other rules.
533 * @param[out] namespace the namespace to use
534 * @return
535 * - >= 0 the number of bytes parsed.
536 * - <0 negative offset for where the error occurred
537 */
539 FR_DLIST_HEAD(tmpl_request_list) *out,
540 fr_sbuff_t *in,
541 fr_sbuff_parse_rules_t const *p_rules,
542 tmpl_rules_t const *t_rules,
543 fr_dict_attr_t const **namespace)
544{
546 tmpl_request_t *rr;
547 size_t ref_len;
548 fr_sbuff_t our_in = FR_SBUFF(in);
549 tmpl_request_t *tail = tmpl_request_list_tail(out);
550 unsigned int depth = 0;
552 tmpl_attr_rules_t const *at_rules;
554
556 at_rules = &t_rules->attr;
557
558 /*
559 * The caller wants to know the default namespace for
560 * resolving the attribute.
561 *
562 * @todo - why not use dict_def if it's set? Tho TBH we
563 * should probably just remove dict_def, and always use "namespace".
564 */
565 if (namespace) {
566 if (at_rules->namespace) {
567 *namespace = at_rules->namespace;
568 } else {
569 *namespace = NULL;
570 }
571 }
572
573 /*
574 * We could make the caller do this but as this
575 * function is intended to help populate tmpl rules,
576 * just be nice...
577 */
578 if (!tmpl_request_list_initialised(out)) tmpl_request_list_talloc_init(out);
579
580 fr_sbuff_marker(&m, &our_in);
582 bool end;
583
584 /*
585 * Search for a known request reference like
586 * 'current', or 'parent'.
587 */
589
590 /*
591 * No match
592 */
593 if (ref_len == 0) {
594 /*
595 * If depth == 0, we're at the start
596 * so just use the default request
597 * reference.
598 */
599 default_ref:
600 if ((depth == 0) && at_rules->request_def) {
601 tmpl_request_ref_list_copy(ctx, out, at_rules->request_def);
602 }
603 break;
604 }
605
606 /*
607 * We don't want to misidentify the list
608 * as being part of an attribute.
609 */
610 if (!fr_sbuff_is_char(&our_in, '.') && (fr_sbuff_is_in_charset(&our_in, fr_dict_attr_allowed_chars) || !tmpl_substr_terminal_check(&our_in, p_rules))) {
611 goto default_ref;
612 }
613
614 if (depth == 0) {
615 if (at_rules->namespace || (at_rules->list_presence == TMPL_ATTR_LIST_FORBID)) {
616 fr_strerror_const("List qualifiers are not allowed here");
618
619 fr_sbuff_set(&our_in, in); /* Marker at the start */
620 error:
621 tmpl_request_list_talloc_free_to_tail(out, tail);
622 FR_SBUFF_ERROR_RETURN(&our_in);
623 }
624 }
625
626 /*
627 * If the caller is asking for a namespace, then walk back up the tmpl_rules_t to find a parent namespace.
628 */
629 if (namespace && t_rules && t_rules->parent) {
630 t_rules = t_rules->parent;
631
632 switch (ref) {
633 case REQUEST_OUTER:
634 while (t_rules->parent) t_rules = t_rules->parent; /* Walk back to the root */
636
637 case REQUEST_PARENT:
638 if (t_rules->attr.namespace) {
639 *namespace = t_rules->attr.namespace;
640 } else if (t_rules->attr.dict_def) {
641 *namespace = fr_dict_root(t_rules->attr.dict_def);
642 } else {
643 *namespace = NULL;
644 }
645 break;
646
647 default:
648 break;
649 }
650 }
651
652 /*
653 * Add a new entry to the dlist
654 */
655 MEM(rr = talloc(ctx, tmpl_request_t));
656 *rr = (tmpl_request_t){
657 .request = ref
658 };
659 tmpl_request_list_insert_tail(out, rr);
660
661 /*
662 * Advance past the separator (if there is one)
663 */
664 end = !fr_sbuff_next_if_char(&our_in, '.');
665
666 /*
667 * Update to the last successfully parsed component
668 *
669 * This makes it easy to backtrack from refs like
670 *
671 * parent.outer-realm-name
672 */
673 fr_sbuff_set(&m, &our_in);
674
675 if (end) break;
676 }
677
678 /*
679 * Nesting level too deep
680 */
682 fr_strerror_const("Request ref nesting too deep");
684 goto error; /* Leave marker at the end */
685 }
686
688}
689
690/** Parse one or more request references, allocing a new list and adding the references to it
691 *
692 * This can be used to create request ref lists for rules and for tmpls.
693 *
694 * @param[in] ctx to allocate request refs in.
695 * @param[out] err If !NULL where to write the parsing error.
696 * @param[out] out The new list.
697 * @param[in] in Sbuff to read request references from.
698 * @return
699 * - >= 0 the number of bytes parsed.
700 * - <0 negative offset for where the error occurred
701 */
703 FR_DLIST_HEAD(tmpl_request_list) **out,
704 fr_sbuff_t *in)
705{
706 fr_slen_t slen;
707
708 FR_DLIST_HEAD(tmpl_request_list) *rql;
709
710 MEM(rql = talloc_zero(ctx, FR_DLIST_HEAD(tmpl_request_list)));
711 tmpl_request_list_talloc_init(rql);
712
713 slen = tmpl_request_ref_list_from_substr(rql, err, rql, in, NULL, NULL, NULL);
714 if (slen < 0) {
715 talloc_free(rql);
716 return slen;
717 }
718
719 *out = rql;
720
721 return slen;
722}
723/** @} */
724
725/** @name Alloc or initialise #tmpl_t
726 *
727 * @note Should not usually be called outside of tmpl_* functions, use one of
728 * the tmpl_*from_* functions instead.
729 * @{
730 */
731
732/** Initialise fields inside a tmpl depending on its type
733 *
734 */
735static inline CC_HINT(always_inline) void tmpl_type_init(tmpl_t *vpt, tmpl_type_t type)
736{
737
738 switch (type) {
739#ifndef HAVE_REGEX
740 case TMPL_TYPE_REGEX:
744 fr_assert(0);
745 return;
746#endif
747
748 case TMPL_TYPE_ATTR:
750 tmpl_attr_list_talloc_init(tmpl_attr(vpt));
751 tmpl_request_list_talloc_init(&vpt->data.attribute.rr);
752 break;
753
754 default:
755 break;
756 }
757 vpt->type = type;
758 }
759
760/** Set the name on a pre-initialised tmpl
761 *
762 * @param[in] vpt to set the name for.
763 * @param[in] quote Original quoting around the name.
764 * @param[in] fmt string.
765 * @param[in] ... format arguments.
766 */
767void tmpl_set_name_printf(tmpl_t *vpt, fr_token_t quote, char const *fmt, ...)
768{
769 va_list ap;
770 char const *old = NULL;
771
772 if (vpt->type != TMPL_TYPE_UNINITIALISED) old = vpt->name;
773
774 va_start(ap, fmt);
775 vpt->name = fr_vasprintf(vpt, fmt, ap);
776 vpt->quote = quote;
777 vpt->len = talloc_array_length(vpt->name) - 1;
778 va_end(ap);
779
780 talloc_const_free(old); /* Free name last so it can be used in the format string */
781}
782
783/** Set the name on a pre-initialised tmpl
784 *
785 * @param[in] vpt to set the name for.
786 * @param[in] quote Original quoting around the name.
787 * @param[in] name of the #tmpl_t.
788 * @param[in] len The length of the buffer (or a substring of the buffer) pointed to by name.
789 * If < 0 strlen will be used to determine the length.
790 */
791void tmpl_set_name_shallow(tmpl_t *vpt, fr_token_t quote, char const *name, ssize_t len)
792{
794
795 vpt->name = name;
796 vpt->len = len < 0 ? strlen(name) : (size_t)len;
797 vpt->quote = quote;
798}
799
800/** Set the name on a pre-initialised tmpl
801 *
802 * @param[in] vpt to set the name for.
803 * @param[in] quote Original quoting around the name.
804 * @param[in] name of the #tmpl_t.
805 * @param[in] len The length of the buffer (or a substring of the buffer) pointed to by name.
806 * If < 0 strlen will be used to determine the length.
807 */
808void tmpl_set_name(tmpl_t *vpt, fr_token_t quote, char const *name, ssize_t len)
809{
811
812 talloc_const_free(vpt->name);
813
814 vpt->name = talloc_bstrndup(vpt, name, len < 0 ? strlen(name) : (size_t)len);
815 vpt->len = talloc_array_length(vpt->name) - 1;
816 vpt->quote = quote;
817}
818
819/** Change the default dictionary in the tmpl's resolution rules
820 *
821 * @param[in] vpt to alter.
822 * @param[in] dict to set.
823 */
825{
826 vpt->rules.attr.dict_def = dict;
827}
828
829/** Set escape parameters for the tmpl output
830 *
831 * @param[in] vpt to alter.
832 * @param[in] escape to set.
833 */
835{
836 vpt->rules.escape = *escape;
837}
838
839/** Change the default dictionary in the tmpl's resolution rules
840 *
841 * @param[in] vpt to alter.
842 * @param[in] xlat to set.
843 */
845{
846 fr_assert(vpt->type == TMPL_TYPE_XLAT);
847
848 tmpl_xlat(vpt) = xlat;
849}
850
851
852/** Initialise a tmpl using a format string to create the name
853 *
854 * @param[in] vpt to initialise.
855 * @param[in] type of tmpl to initialise.
856 * @param[in] quote Original quoting around the name.
857 * @param[in] fmt string.
858 * @param[in] ... format arguments.
859 * @return A pointer to the newly initialised tmpl.
860 */
862{
863 va_list ap;
864
865 memset(vpt, 0, sizeof(*vpt));
867
868 va_start(ap, fmt);
869 vpt->name = fr_vasprintf(vpt, fmt, ap);
870 vpt->len = talloc_array_length(vpt->name) - 1;
871 vpt->quote = quote;
872 va_end(ap);
873
874 return vpt;
875}
876
877/** Initialise a tmpl without copying the input name string
878 *
879 * @note Name is not talloc_strdup'd or memcpy'd so must be available, and must not change
880 * for the lifetime of the #tmpl_t.
881 *
882 * @param[out] vpt to initialise.
883 * @param[in] type to set in the #tmpl_t.
884 * @param[in] quote The type of quoting around the template name.
885 * @param[in] name of the #tmpl_t.
886 * @param[in] len The length of the buffer (or a substring of the buffer) pointed to by name.
887 * If < 0 strlen will be used to determine the length.
888 * @param[in] t_rules used during parsing.
889 * @return a pointer to the initialised #tmpl_t. The same value as vpt.
890 */
892 char const *name, ssize_t len, tmpl_rules_t const *t_rules)
893{
894 memset(vpt, 0, sizeof(*vpt));
896 tmpl_set_name_shallow(vpt, quote, name, len);
897 if (t_rules) vpt->rules = *t_rules;
898
899 return vpt;
900}
901
902/** Initialise a tmpl using a literal string to create the name
903 *
904 * @param[in] vpt to initialise.
905 * @param[in] type of tmpl to initialise.
906 * @param[in] quote Original quoting around the name.
907 * @param[in] name to set for the tmpl.
908 * @param[in] len Name length. If < 0 strlen will be used
909 * to determine the name.
910 * @param[in] t_rules used during parsing.
911 * @return A pointer to the newly initialised tmpl.
912 */
914 char const *name, ssize_t len, tmpl_rules_t const *t_rules)
915{
916 memset(vpt, 0, sizeof(*vpt));
918 tmpl_set_name(vpt, quote, name, len);
919 if (t_rules) vpt->rules = *t_rules;
920
921 return vpt;
922}
923
924/** Create a new heap allocated #tmpl_t
925 *
926 * Must be later initialised with a tmpl_init_* function.
927 *
928 * This function is provided to allow tmpls to be pre-allocated for talloc purposes before
929 * their name is known.
930 */
931static inline CC_HINT(always_inline) tmpl_t *tmpl_alloc_null(TALLOC_CTX *ctx)
932{
933 tmpl_t *vpt;
934
935 /*
936 * Allocate enough memory to hold at least
937 * one attribute reference and one request
938 * reference.
939 */
940 MEM(vpt = talloc_pooled_object(ctx, tmpl_t, 2, sizeof(tmpl_request_t) + sizeof(tmpl_attr_t)));
942
943 return vpt;
944}
945
946/** Create a new heap allocated #tmpl_t
947 *
948 * @param[in,out] ctx to allocate in.
949 * @param[in] type to set in the #tmpl_t.
950 * @param[in] name of the #tmpl_t (will be copied to a new talloc buffer parented
951 * by the #tmpl_t).
952 * @param[in] len The length of the buffer (or a substring of the buffer) pointed to by name.
953 * If < 0 strlen will be used to determine the length.
954 * @param[in] quote The type of quoting around the template name.
955 * @return the newly allocated #tmpl_t.
956 */
957tmpl_t *tmpl_alloc(TALLOC_CTX *ctx, tmpl_type_t type, fr_token_t quote, char const *name, ssize_t len)
958{
959 tmpl_t *vpt;
960
961 vpt = tmpl_alloc_null(ctx);
962 memset(vpt, 0, sizeof(*vpt));
963
965 if (name) tmpl_set_name(vpt, quote, name, len);
966
967 return vpt;
968}
969/** @} */
970
971/** @name Create new #tmpl_t from a string
972 *
973 * @{
974 */
975
976/** Allocate a new attribute reference and add it to the end of the attribute reference list
977 *
978 */
980{
981 tmpl_attr_t *ar;
982 TALLOC_CTX *ctx;
983
984 if (tmpl_attr_list_num_elements(tmpl_attr(vpt)) == 0) {
985 ctx = vpt;
986 } else {
987 ctx = tmpl_attr_list_tail(tmpl_attr(vpt));
988 }
989
990 MEM(ar = talloc(ctx, tmpl_attr_t));
991 *ar = (tmpl_attr_t){
992 .type = type,
993 .filter = {
995 .num = NUM_UNSPEC
996 }
997 };
998 tmpl_attr_list_insert_tail(tmpl_attr(vpt), ar);
999
1000 return ar;
1001}
1002
1003/** Create a #tmpl_t from a #fr_value_box_t
1004 *
1005 * @param[in,out] ctx to allocate #tmpl_t in.
1006 * @param[out] out Where to write pointer to new #tmpl_t.
1007 * @param[in] data to convert.
1008 * @param[in] steal If true, any buffers are moved to the new
1009 * ctx instead of being duplicated.
1010 * @return
1011 * - 0 on success.
1012 * - -1 on failure.
1013 */
1014int tmpl_afrom_value_box(TALLOC_CTX *ctx, tmpl_t **out, fr_value_box_t *data, bool steal)
1015{
1016 char *name;
1017 fr_slen_t slen;
1018 tmpl_t *vpt;
1020
1021 MEM(vpt = talloc(ctx, tmpl_t));
1023 if (slen < 0) {
1024 error:
1026 return -1;
1027 }
1028
1029 tmpl_init_shallow(vpt, TMPL_TYPE_DATA, quote, name, slen, NULL);
1030
1031 if (steal) {
1032 if (fr_value_box_steal(vpt, tmpl_value(vpt), data) < 0) goto error;
1033 } else {
1034 if (fr_value_box_copy(vpt, tmpl_value(vpt), data) < 0) goto error;
1035 }
1036 *out = vpt;
1037
1038 return 0;
1039}
1040
1041/** Copy a list of attribute and request references from one tmpl to another
1042 *
1043 */
1044int tmpl_attr_copy(tmpl_t *dst, tmpl_t const *src)
1045{
1046 tmpl_attr_t *src_ar = NULL, *dst_ar;
1047
1048 /*
1049 * Clear any existing attribute references
1050 */
1051 if (tmpl_attr_list_num_elements(tmpl_attr(dst)) > 0) tmpl_attr_list_talloc_reverse_free(tmpl_attr(dst));
1052
1053 while ((src_ar = tmpl_attr_list_next(tmpl_attr(src), src_ar))) {
1054 dst_ar = tmpl_attr_add(dst, src_ar->type);
1055
1056 switch (src_ar->type) {
1058 dst_ar->ar_da = src_ar->ar_da;
1059 break;
1060
1061 case TMPL_ATTR_TYPE_UNSPEC: /* Nothing to copy */
1062 break;
1063
1065 dst_ar->ar_unknown = fr_dict_attr_unknown_copy(dst_ar, src_ar->ar_unknown);
1066 break;
1067
1069 dst_ar->ar_unresolved = talloc_bstrdup(dst_ar, src_ar->ar_unresolved);
1070 break;
1071
1072 default:
1073 if (!fr_cond_assert(0)) return -1;
1074 }
1075 dst_ar->ar_num = src_ar->ar_num;
1076 dst_ar->ar_filter_type = src_ar->ar_filter_type;
1077 dst_ar->parent = src_ar->parent;
1078 }
1079
1080 /*
1081 * Clear any existing request references
1082 * and copy the ones from the source.
1083 */
1084 tmpl_request_list_talloc_reverse_free(&dst->data.attribute.rr);
1085 tmpl_request_ref_list_copy(dst, &dst->data.attribute.rr, &src->data.attribute.rr);
1086
1087 /*
1088 * Ensure that we copy over any parsing rules, defaults, etc.
1089 */
1090 dst->rules = src->rules;
1091
1092 TMPL_ATTR_VERIFY(dst);
1093
1094 return 0;
1095}
1096
1097/** Replace the current attribute reference
1098 *
1099 */
1101{
1102 tmpl_attr_t *ref;
1103
1105
1106 /*
1107 * Clear any existing references
1108 */
1109 if (tmpl_attr_list_num_elements(tmpl_attr(vpt)) > 0) {
1110 tmpl_attr_list_talloc_reverse_free(tmpl_attr(vpt));
1111 }
1112
1113 /*
1114 * Unknown attributes get copied
1115 */
1116 if (da->flags.is_unknown) {
1118 ref->da = ref->ar_unknown = fr_dict_attr_unknown_copy(vpt, da);
1119 } else {
1121 ref->da = da;
1122 }
1123 ref->ar_parent = fr_dict_root(fr_dict_by_da(da)); /* Parent is the root of the dictionary */
1124
1126
1127 return 0;
1128}
1129
1130/** Replace the leaf attribute only
1131 *
1132 */
1134{
1135 tmpl_attr_t *ref, *parent = NULL;
1136
1139
1140 /*
1141 * Clear any existing references
1142 */
1143 if (tmpl_attr_list_num_elements(tmpl_attr(vpt)) > 0) {
1144 if (tmpl_attr_list_num_elements(tmpl_attr(vpt)) > 1) {
1145 ref = tmpl_attr_list_tail(tmpl_attr(vpt));
1146 parent = tmpl_attr_list_prev(tmpl_attr(vpt), ref);
1147
1148 if (!fr_dict_attr_common_parent(parent->ar_da, da, true)) {
1149 fr_strerror_const("New leaf da and old leaf da do not share the same ancestor");
1150 return -1;
1151 }
1152 } else {
1153 ref = tmpl_attr_list_tail(tmpl_attr(vpt));
1154 }
1155
1156 /*
1157 * Free old unknown and unresolved attributes...
1158 */
1159 talloc_free_children(ref);
1160
1161 /*
1162 *
1163 */
1164 ref->ar_filter_type = TMPL_ATTR_FILTER_TYPE_NONE;
1165 ref->ar_num = NUM_UNSPEC;
1166
1167 } else {
1168 ref = tmpl_attr_add(vpt, da->flags.is_unknown ? TMPL_ATTR_TYPE_UNKNOWN : TMPL_ATTR_TYPE_NORMAL);
1169 }
1170
1171
1172 /*
1173 * Unknown attributes get copied
1174 */
1175 if (da->flags.is_unknown) {
1176 ref->da = ref->ar_unknown = fr_dict_attr_unknown_copy(vpt, da);
1177 } else {
1178 ref->da = da;
1179 }
1180
1181 /*
1182 * FIXME - Should be calculated from existing ar
1183 */
1184 ref->ar_parent = fr_dict_root(fr_dict_by_da(da)); /* Parent is the root of the dictionary */
1185
1187
1188 return 0;
1189}
1190
1191/** Rewrite the leaf's instance number
1192 *
1193 * This function is _only_ called from the compiler, for "update" and "foreach" keywords. In those cases,
1194 * the user historically did "&foo-bar", but really meant "&foo-bar[*]". We silently update that for
1195 * "update" sections, and complain about it in "foreach" sections.
1196 *
1197 * As the server now supports multiple types of leaf references, we do the rewrite _only_ from "none" (no
1198 * filter), OR where it's a numerical index, AND the index hasn't been specified.
1199 */
1201{
1202 tmpl_attr_t *ref = NULL;
1203
1205
1206 if (tmpl_attr_list_num_elements(tmpl_attr(vpt)) == 0) return;
1207
1208 ref = tmpl_attr_list_tail(tmpl_attr(vpt));
1209
1210 if (ref->ar_filter_type == TMPL_ATTR_FILTER_TYPE_NONE) {
1211 ref->ar_filter_type = TMPL_ATTR_FILTER_TYPE_INDEX;
1212 ref->ar_num = to;
1213
1214 } else if (ref->ar_filter_type != TMPL_ATTR_FILTER_TYPE_INDEX) {
1215 return;
1216
1217 } else if (ref->ar_num == NUM_UNSPEC) {
1218 ref->ar_num = to;
1219 }
1220
1222}
1223
1224/** Set the request for an attribute ref
1225 *
1226 */
1227void tmpl_attr_set_request_ref(tmpl_t *vpt, FR_DLIST_HEAD(tmpl_request_list) const *request_def)
1228{
1229 fr_assert_msg(tmpl_is_attr(vpt), "Expected tmpl type 'attr', got '%s'",
1230 tmpl_type_to_str(vpt->type));
1231
1232 /*
1233 * Clear any existing request references
1234 */
1235 tmpl_request_list_talloc_reverse_free(&vpt->data.attribute.rr);
1236 tmpl_request_ref_list_copy(vpt, &vpt->data.attribute.rr, request_def);
1237
1239}
1240
1242{
1243 tmpl_attr_t *ref = tmpl_attr_list_head(tmpl_attr(vpt));
1244 if (tmpl_attr_is_list_attr(ref)) ref->da = list;
1245
1247}
1248
1249/** Create a new tmpl from a list tmpl and a da
1250 *
1251 */
1252int tmpl_attr_afrom_list(TALLOC_CTX *ctx, tmpl_t **out, tmpl_t const *list, fr_dict_attr_t const *da)
1253{
1254 tmpl_t *vpt;
1255 tmpl_attr_t *ar;
1256
1257 char attr[256];
1258 ssize_t slen;
1259
1260 MEM(vpt = tmpl_alloc(ctx, TMPL_TYPE_ATTR, T_BARE_WORD, NULL, 0));
1261
1262 /*
1263 * Copies request refs and the list ref
1264 */
1265 tmpl_attr_copy(vpt, list);
1267
1268 if (da->flags.is_unknown) {
1270 ar->da = ar->ar_unknown = fr_dict_attr_unknown_copy(vpt, da);
1271 } else {
1273 ar->ar_da = da;
1274 }
1275
1276 ar->ar_parent = fr_dict_root(fr_dict_by_da(da));
1277
1278 /*
1279 * We need to rebuild the attribute name, to be the
1280 * one we copied from the source list.
1281 */
1282 slen = tmpl_print(&FR_SBUFF_OUT(attr, sizeof(attr)), vpt, TMPL_ATTR_REF_PREFIX_YES,
1283 fr_value_escape_by_quote[list->quote]);
1284 if (slen < 0) {
1285 fr_strerror_printf("Serialized attribute too long. Must be < "
1286 STRINGIFY(sizeof(attr)) " bytes, got %zu bytes", (size_t)-slen);
1288 return -1;
1289 }
1290
1291 vpt->len = (size_t)slen;
1292 vpt->name = talloc_typed_strdup(vpt, attr);
1293 vpt->quote = T_BARE_WORD;
1294
1296
1297 *out = vpt;
1298
1299 return 0;
1300}
1301/** @} */
1302
1303/** Insert an attribute reference into a tmpl
1304 *
1305 * Not all attribute references can be used to create new attributes,
1306 * for example those accessing instance > 0 or those that resolve
1307 * to special indexes.
1308 *
1309 * We mark up these references and their parents as resolve only
1310 * meaning that if any code needs to use a reference chain to build
1311 * out a pair tree, it bails out early.
1312 *
1313 * @param[in] vpt containing the reference list.
1314 * @param[in] ar to insert and check.
1315 */
1316static inline CC_HINT(always_inline) void tmpl_attr_insert(tmpl_t *vpt, tmpl_attr_t *ar)
1317{
1318 /*
1319 * Insert the reference into the list.
1320 */
1321 tmpl_attr_list_insert_tail(tmpl_attr(vpt), ar);
1322
1323 switch (ar->ar_num) {
1324 case 0:
1325 case NUM_UNSPEC:
1326 break;
1327
1328 default:
1329 ar->resolve_only = true;
1330 while ((ar = tmpl_attr_list_prev(tmpl_attr(vpt), ar))) ar->resolve_only = true;
1331 break;
1332 }
1333}
1334
1335/** Parse array subscript and in future other filters
1336 *
1337 * @param[out] err Parse error code.
1338 * @param[in] ar to populate filter for.
1339 * @param[in] name containing more attribute ref data.
1340 * @param[in] at_rules see tmpl_attr_afrom_attr_substr.
1341 * @return
1342 * - >0 if a filter was parsed.
1343 * - 0 if no filter was available.
1344 * - <0 on filter parse error.
1345 */
1347 fr_sbuff_t *name, tmpl_attr_rules_t const *at_rules)
1348{
1349 fr_sbuff_t our_name = FR_SBUFF(name);
1350
1351 /*
1352 * Parse array subscript (and eventually complex filters)
1353 */
1354 if (!fr_sbuff_next_if_char(&our_name, '[')) return 0;
1355
1356 if (at_rules->disallow_filters || tmpl_attr_is_list_attr(ar)) {
1357 fr_strerror_const("Filters not allowed here");
1359 fr_sbuff_set_to_start(&our_name);
1360 FR_SBUFF_ERROR_RETURN(&our_name);
1361 }
1362
1363 ar->ar_filter_type = TMPL_ATTR_FILTER_TYPE_INDEX;
1364 fr_sbuff_switch(&our_name, '\0') {
1365 case '#':
1366 ar->ar_num = NUM_COUNT;
1367 fr_sbuff_next(&our_name);
1368 break;
1369
1370 case '*':
1371 ar->ar_num = NUM_ALL;
1372 fr_sbuff_next(&our_name);
1373 break;
1374
1375 case '0':
1376 case '1':
1377 case '2':
1378 case '3':
1379 case '4':
1380 case '5':
1381 case '6':
1382 case '7':
1383 case '8':
1384 case '9':
1385 {
1386 ssize_t rcode;
1388 fr_sbuff_t tmp = FR_SBUFF(&our_name);
1389
1390 /*
1391 * All digits (not hex).
1392 */
1393 rcode = fr_sbuff_out(&sberr, &ar->ar_num, &tmp);
1394 if ((rcode < 0) || !fr_sbuff_is_char(&tmp, ']')) goto parse_tmpl;
1395
1396 if ((ar->ar_num > 1000) || (ar->ar_num < 0)) {
1397 fr_strerror_printf("Invalid array index '%hi' (should be between 0-1000)", ar->ar_num);
1398 ar->ar_num = 0;
1399 goto error;
1400 }
1401
1402 fr_sbuff_set(&our_name, &tmp); /* Advance name _AFTER_ doing checks */
1403 break;
1404 }
1405
1406 case '"':
1407 case '\'':
1408 case '`':
1409 case '/':
1410 fr_strerror_const("Invalid data type for array index");
1411 goto error;
1412
1413 /* Used as EOB here */
1414 missing_closing:
1415 case '\0':
1416 fr_strerror_const("No closing ']' for array index");
1417 error:
1419 FR_SBUFF_ERROR_RETURN(&our_name);
1420
1421 case '(': /* (...) expression */
1422 {
1423 fr_sbuff_t tmp = FR_SBUFF(&our_name);
1424 fr_slen_t slen;
1425 tmpl_rules_t t_rules;
1426 fr_sbuff_parse_rules_t p_rules;
1427 fr_sbuff_term_t const filter_terminals = FR_SBUFF_TERMS(L("]"));
1428
1429 /*
1430 * For now, we don't allow filtering on leaf values. e.g.
1431 *
1432 * &User-Name[(&User-Name == foo)]
1433 *
1434 * @todo - find some sane way of allowing this, without mangling the xlat expression
1435 * parser too badly. The simplest way is likely to just parse the expression, and then
1436 * walk over it, complaining if it contains attribute references other than &User-Name.
1437 *
1438 * Anything else is likely too hard.
1439 *
1440 * We also want to disallow basic conditions like "true" or "false". The conditions
1441 * should only be using attribute references, and those attribute references should be
1442 * limited to certain attributes.
1443 *
1444 * And if the filter attribute is a TLV, the condition code complains with 'Nesting types
1445 * such as groups or TLVs cannot be used in condition comparisons'. This is reasonable,
1446 * as we can't currently compare things like;
1447 *
1448 * &Group-Thingy == { &foo = bar }
1449 *
1450 * Which would involve creating the RHS list, doing an element-by-element comparison, and
1451 * then returning.
1452 *
1453 * In order to fix that, we have to
1454 */
1455 if (!ar->ar_da || !fr_type_is_structural(ar->ar_da->type)) {
1456 fr_strerror_printf("Invalid filter - cannot use filter on leaf attributes");
1457 ar->ar_num = 0;
1458 goto error;
1459 }
1460
1461 tmp = FR_SBUFF(&our_name);
1462 t_rules = (tmpl_rules_t) {};
1463 t_rules.attr = *at_rules;
1464 t_rules.attr.namespace = ar->ar_da; /* @todo - parent? */
1465
1466 p_rules = (fr_sbuff_parse_rules_t) {
1467 .terminals = &filter_terminals,
1468 .escapes = NULL
1469 };
1470
1471 /*
1472 * Check if it's a condition.
1473 */
1474 slen = xlat_tokenize_condition(ar, &ar->ar_cond, &tmp, &p_rules, &t_rules);
1475 if (slen < 0) goto error;
1476
1477 fr_assert(!xlat_impure_func(ar->ar_cond));
1478
1479 ar->ar_filter_type = TMPL_ATTR_FILTER_TYPE_CONDITION;
1480 fr_sbuff_set(&our_name, &tmp); /* Advance name _AFTER_ doing checks */
1481 break;
1482 }
1483
1484 case '%': /* ${...} expansion */
1485 {
1486 fr_sbuff_t tmp = FR_SBUFF(&our_name);
1487 fr_slen_t slen;
1488 tmpl_rules_t t_rules;
1489 fr_sbuff_parse_rules_t p_rules;
1490 fr_sbuff_term_t const filter_terminals = FR_SBUFF_TERMS(L("]"));
1491
1492 if (!fr_sbuff_is_str(&our_name, "%{", 2)) {
1493 fr_strerror_const("Invalid expression in attribute index");
1494 goto error;
1495 }
1496
1497 tmp = FR_SBUFF(&our_name);
1498 t_rules = (tmpl_rules_t) {};
1499 t_rules.attr = *at_rules;
1500
1501 p_rules = (fr_sbuff_parse_rules_t) {
1502 .terminals = &filter_terminals,
1503 .escapes = NULL
1504 };
1505
1506 /*
1507 * Check if it's an expression.
1508 */
1509 slen = xlat_tokenize_expression(ar, &ar->ar_cond, &tmp, &p_rules, &t_rules);
1510 if (slen < 0) goto error;
1511
1512 if (xlat_impure_func(ar->ar_expr)) {
1513 fr_strerror_const("Expression in attribute index cannot depend on functions which call external databases");
1514 goto error;
1515 }
1516
1517 ar->ar_filter_type = TMPL_ATTR_FILTER_TYPE_EXPR;
1518
1519 fr_sbuff_set(&our_name, &tmp); /* Advance name _AFTER_ doing checks */
1520 break;
1521 }
1522
1523 case 'n':
1524 /*
1525 * [n] is the last one
1526 *
1527 * [nope] is a reference to "nope".
1528 */
1529 if (fr_sbuff_is_str(&our_name, "n]", 2)) {
1530 ar->ar_num = NUM_LAST;
1531 fr_sbuff_next(&our_name);
1532 break;
1533 }
1535
1536 default:
1537 parse_tmpl:
1538 {
1539 fr_sbuff_t tmp = FR_SBUFF(&our_name);
1540 ssize_t slen;
1541 tmpl_rules_t t_rules;
1542 fr_sbuff_parse_rules_t p_rules;
1543 fr_sbuff_term_t const filter_terminals = FR_SBUFF_TERMS(L("]"));
1544
1545 tmp = FR_SBUFF(&our_name);
1546 t_rules = (tmpl_rules_t) {};
1547 t_rules.attr = *at_rules;
1548
1549 /*
1550 * Don't reset namespace, we always want to start searching from the top level of the
1551 * dictionaries.
1552 */
1553
1554 p_rules = (fr_sbuff_parse_rules_t) {
1555 .terminals = &filter_terminals,
1556 .escapes = NULL
1557 };
1558
1559 /*
1560 * @todo - for some reason, the tokenize_condition code allows for internal
1561 * vs protocol vs local attributes, whereas the tmpl function only accepts
1562 * internal ones.
1563 */
1564 slen = tmpl_afrom_substr(ar, &ar->ar_tmpl, &tmp, T_BARE_WORD, &p_rules, &t_rules);
1565 if (slen <= 0) goto error;
1566
1567 if (!tmpl_is_attr(ar->ar_tmpl)) {
1568 fr_strerror_printf("Invalid array index '%s'", ar->ar_tmpl->name);
1569 goto error;
1570 }
1571
1572 /*
1573 * Arguably we _could_ say &User-Name["foo"] matches all user-name with value "foo",
1574 * but that would confuse the issue for &Integer-Thing[4].
1575 *
1576 * For matching therefore, we really need to have a way to define "self".
1577 */
1578 if (!fr_type_numeric[tmpl_attr_tail_da(ar->ar_tmpl)->type]) {
1579 fr_strerror_const("Invalid data type for array index (must be numeric)");
1580 goto error;
1581 }
1582
1583 ar->ar_filter_type = TMPL_ATTR_FILTER_TYPE_TMPL;
1584 fr_sbuff_set(&our_name, &tmp); /* Advance name _AFTER_ doing checks */
1585 break;
1586 }
1587 }
1588
1589 /*
1590 * Always advance here, so the error
1591 * marker points to the bad char.
1592 */
1593 if (!fr_sbuff_next_if_char(&our_name, ']')) goto missing_closing;
1594
1595 FR_SBUFF_SET_RETURN(name, &our_name);
1596}
1597
1598extern fr_dict_attr_t const *tmpl_attr_unspec;
1599
1600static inline CC_HINT(nonnull(3,4))
1602 tmpl_t *vpt,
1603 fr_sbuff_t *name, tmpl_attr_rules_t const *at_rules)
1604{
1605 fr_slen_t slen;
1606
1607 *ar = (tmpl_attr_t){
1608 .ar_num = NUM_UNSPEC, /* May be changed by tmpl_attr_parse_filter */
1609 .ar_type = TMPL_ATTR_TYPE_UNSPEC,
1610 .ar_da = tmpl_attr_unspec,
1611 };
1612
1613 slen = tmpl_attr_parse_filter(err, ar, name, at_rules);
1614 if (slen < 0) {
1615 talloc_free(ar);
1616 return slen;
1617 /*
1618 * No filters and no previous elements is the equivalent of '&'
1619 * which is not allowed.
1620 *
1621 * &[<filter>] is allowed as this lets us perform filtering operations
1622 * at the root.
1623 */
1624 } else if ((slen == 0) && (tmpl_attr_num_elements(vpt) == 0)) {
1625 fr_strerror_const("Invalid attribute name");
1627 return -1;
1628 }
1629
1630 tmpl_attr_insert(vpt, ar);
1631
1632 return slen;
1633}
1634
1635/** Parse an unresolved attribute, i.e. one which can't be found in the current dictionary
1636 *
1637 * This function calls itself recursively to process additional OID
1638 * components once we've failed to resolve one component.
1639 *
1640 * @note Do not call directly.
1641 *
1642 * @param[in] ctx to allocate new attribute reference in.
1643 * @param[out] err Parse error.
1644 * @param[in,out] vpt to append this reference to.
1645 * @param[in] parent Last known parent.
1646 * @param[in] namespace in which the attribute will be resolved.
1647 * @param[in] name to parse.
1648 * @param[in] at_rules see tmpl_attr_afrom_attr_substr.
1649 * @return
1650 * - <0 on error.
1651 * - 0 on success.
1652 */
1653static inline CC_HINT(nonnull(3,6))
1655 tmpl_t *vpt,
1656 fr_dict_attr_t const *parent, fr_dict_attr_t const *namespace,
1657 fr_sbuff_t *name, tmpl_attr_rules_t const *at_rules)
1658{
1659 tmpl_attr_t *ar = NULL, *ar_curr;
1660 fr_sbuff_t our_name = FR_SBUFF(name);
1661 fr_slen_t slen;
1662 char *unresolved;
1663
1664 /*
1665 * Point we free from if something goes wrong.
1666 */
1667 ar_curr = tmpl_attr_list_tail(tmpl_attr(vpt));
1668 for (;;) {
1669 MEM(ar = talloc(ctx, tmpl_attr_t));
1670 /*
1671 * Copy out a string of allowed dictionary chars to form
1672 * the unresolved attribute name.
1673 *
1674 * This will be resolved later (outside of this function).
1675 */
1676 slen = fr_sbuff_out_abstrncpy_allowed(ar, &unresolved,
1677 &our_name, FR_DICT_ATTR_MAX_NAME_LEN + 1,
1679 if (slen == 0) {
1680 slen = tmpl_attr_ref_from_unspecified_substr(ar, err, vpt, &our_name, at_rules);
1681 if (slen < 0) {
1682 fr_sbuff_advance(&our_name, +slen);
1683 error:
1684 talloc_free(ar);
1685 tmpl_attr_list_talloc_free_to_tail(tmpl_attr(vpt), ar_curr);
1686 return -1;
1687 }
1688 return fr_sbuff_set(name, &our_name);
1689 } else if (slen > FR_DICT_ATTR_MAX_NAME_LEN) {
1690 fr_strerror_const("Attribute name is too long");
1692 goto error;
1693 }
1694
1695 *ar = (tmpl_attr_t){
1696 .ar_num = NUM_UNSPEC,
1697 .ar_type = TMPL_ATTR_TYPE_UNRESOLVED,
1698 .ar_unresolved = unresolved,
1699 .ar_unresolved_namespace = namespace,
1700 .ar_parent = parent
1701 };
1702
1703 if (tmpl_attr_parse_filter(err, ar, &our_name, at_rules) < 0) goto error;
1704
1705 /*
1706 * Insert the ar into the list of attribute references
1707 */
1708 tmpl_attr_insert(vpt, ar);
1709
1710 /*
1711 * Once one OID component is created as unresolved all
1712 * future OID components are also unresolved.
1713 */
1714 if (!fr_sbuff_next_if_char(&our_name, '.')) break;
1715 }
1716
1717 /*
1718 * Mark the tmpl up as an unresolved attribute reference
1719 * the attribute reference will be resolved later.
1720 */
1722
1723 return fr_sbuff_set(name, &our_name);
1724}
1725
1726/*
1727 * Add attr_ref when we've parsed an intermediate dictionary name
1728 * which is itself a ref.
1729 */
1730static void tmpl_attr_ref_fixup(TALLOC_CTX *ctx, tmpl_t *vpt, fr_dict_attr_t const *da, fr_dict_attr_t const *parent)
1731{
1732 tmpl_attr_t *ar;
1733
1734 if (tmpl_attr_tail_da(vpt) == da) return;
1735
1736 if (da->parent != parent) tmpl_attr_ref_fixup(ctx, vpt, da->parent, parent);
1737
1738 MEM(ar = talloc(ctx, tmpl_attr_t));
1739 *ar = (tmpl_attr_t) {
1740 .ar_num = NUM_UNSPEC,
1741 .ar_type = TMPL_ATTR_TYPE_NORMAL,
1742 .ar_da = da,
1743 .ar_parent = da->parent,
1744 };
1745
1746 tmpl_attr_insert(vpt, ar);
1747}
1748
1749/** Parse an attribute reference, either an OID or attribute name
1750 *
1751 * @note Do not call directly.
1752 *
1753 * @param[in] ctx to allocate new attribute reference in.
1754 * @param[out] err Parse error.
1755 * @param[in,out] vpt to append this reference to.
1756 * @param[in] parent Parent where the attribute will be placed (group, struct, tlv, etc).
1757 * @param[in] namespace Where the child attribute will be parsed from (dict root, struct member, TLV child, etc)
1758 * @param[in] name to parse.
1759 * @param[in] p_rules Formatting rules used to check for trailing garbage.
1760 * @param[in] at_rules which places constraints on attribute reference parsing.
1761 * Rules interpreted by this function is:
1762 * - allow_unknown - If false unknown OID components
1763 * result in a parse error.
1764 * - allow_unresolved - If false unknown attribute names
1765 * result in a parse error.
1766 * - allow_foreign - If an attribute resolves in a dictionary
1767 * that does not match the parent
1768 * (exception being FR_TYPE_GROUP) then that results
1769 * in a parse error.
1770 * @param[in] depth How deep we are. Used to check for maximum nesting level.
1771 * @return
1772 * - <0 on error.
1773 * - 0 on success.
1774 */
1775static inline int tmpl_attr_afrom_attr_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err,
1776 tmpl_t *vpt,
1777 fr_dict_attr_t const *parent, fr_dict_attr_t const *namespace,
1779 fr_sbuff_parse_rules_t const *p_rules, tmpl_attr_rules_t const *at_rules,
1780 unsigned int depth)
1781{
1782 uint32_t oid = 0;
1783 tmpl_attr_t *ar = NULL;
1784 fr_dict_attr_t const *da;
1786 fr_dict_attr_err_t dict_err;
1787 fr_dict_attr_t const *our_parent = parent;
1788
1790
1791 fr_sbuff_marker(&m_s, name);
1792
1794 fr_strerror_const("Attribute nesting too deep");
1796 error:
1797 talloc_free(ar);
1798 fr_sbuff_marker_release(&m_s);
1800 }
1801
1802 /*
1803 * Input too short
1804 */
1805 if (!fr_sbuff_extend(name)) {
1806 fr_strerror_const("Missing attribute reference");
1808 goto error;
1809 }
1810
1811 /*
1812 * Maybe there's no child namespace (struct member, tlv child, etc). In which case we must
1813 * search from the default dictionary root.
1814 *
1815 * This search is probably wrong in some cases. See the comments below around FR_TYPE_GROUP.
1816 *
1817 * If we change out the dictionaries, we should arguably also change dict_def in the
1818 * tmpl_attr_rules_t. On top of that, the "dict_attr_search" functions take a #fr_dict_t
1819 * pointer, and not a pointer to the dict root. So we can't pass them a namespace.
1820 */
1821 if (!namespace) {
1822 fr_assert(parent == NULL);
1823
1825 at_rules->dict_def,
1826 name, p_rules ? p_rules->terminals : NULL,
1827 true,
1828 at_rules->allow_foreign);
1829 /*
1830 * The attribute was found either in the dict_def root, OR in the internal root, OR if
1831 * !dict_def && allow_foreign, in some other dictionary root.
1832 *
1833 * Otherwise we're still not sure what the attribute is. It may end up being an
1834 * unresolved one.
1835 */
1836 if (da) {
1837 our_parent = da->parent;
1838
1839 if (!our_parent->flags.is_root) {
1840 tmpl_attr_ref_fixup(ctx, vpt, our_parent, fr_dict_root(da->dict));
1841 }
1842 }
1843 } else {
1844 fr_assert(parent != NULL);
1845
1846 /*
1847 * Otherwise we're resolving the next piece in the context of where-ever we ended up from
1848 * parsing the last bit.
1849 *
1850 * The "parent" could be the same as "namespace", if both are at a dictionary root, OR
1851 * both are from a struct / tlv attribute.
1852
1853 * Or, "parent" could be a grouping attribute (e.g. request), and "namespace" could be
1854 * the dictionary root.
1855 */
1856 (void)fr_dict_attr_by_name_substr(&dict_err,
1857 &da,
1858 namespace,
1859 name,
1860 p_rules ? p_rules->terminals : NULL);
1861
1862 /*
1863 * Allow fallback to internal attributes
1864 * if the parent was a group, and we're
1865 * allowing internal resolution.
1866 *
1867 * Discard any errors here... It's more
1868 * useful to have the original.
1869 */
1870 if (!da) {
1871 ar = tmpl_attr_list_tail(&vpt->data.attribute.ar);
1872 if (!ar || ((ar->type == TMPL_ATTR_TYPE_NORMAL) && (ar->ar_da->type == FR_TYPE_GROUP))) {
1873 fr_dict_attr_t const *internal_root = fr_dict_root(fr_dict_internal());
1874
1875 (void)fr_dict_attr_by_name_substr(NULL,
1876 &da, internal_root,
1877 name,
1878 p_rules ? p_rules->terminals : NULL);
1879 if (da) {
1880 dict_err = FR_DICT_ATTR_OK;
1881 our_parent = internal_root;
1882 }
1883 }
1884 ar = NULL;
1885
1886 } else {
1887 /*
1888 * If we searched in a local dictionary, but found a real attribute
1889 * switch the namespace.
1890 */
1891 if (!da->flags.local && namespace->flags.local) namespace = our_parent = fr_dict_root(da->dict);
1892
1893 /*
1894 * We had an alias in the same namespace,
1895 * go add more things in.
1896 */
1897 if (da->parent != our_parent) {
1898 fr_assert(namespace == our_parent);
1899 tmpl_attr_ref_fixup(ctx, vpt, da->parent, our_parent);
1900 }
1901 }
1902 }
1903
1904 /*
1905 * Fatal errors related to nesting...
1906 */
1907 switch (dict_err) {
1909 fr_assert(our_parent != NULL);
1910 if (our_parent->flags.is_unknown) break;
1911 goto error;
1912
1914 goto error;
1915
1916 default:
1917 if (!da) break;
1918
1919 /*
1920 * The named component was a known attribute
1921 * so record it as a normal attribute
1922 * reference.
1923 */
1924 fr_assert(our_parent != NULL);
1925 goto alloc_ar;
1926 }
1927
1928 /*
1929 * At this point we haven't found a known attribute. What remains MUST be an OID component, OR an
1930 * unresolved attribute.
1931 *
1932 * The default is to parse the OIDs in the current namespace. If there is none, then we parse
1933 * the OIDs and unresolved attributes in the dict_def. And if that doesn't exist, in the
1934 * internal dictionaries.
1935 *
1936 * Note that we do NOT allow unknown attributes in the internal dictionary. Those attributes are
1937 * generally just DEFINEs, and their numbers have no meaning.
1938 */
1939 if (!namespace) {
1940 if (at_rules->dict_def) {
1941 our_parent = namespace = fr_dict_root(at_rules->dict_def);
1942 } else {
1943 our_parent = namespace = fr_dict_root(fr_dict_internal());
1944 }
1945 }
1946
1947 fr_assert(our_parent != NULL);
1948 fr_assert(namespace != NULL);
1949
1950 /*
1951 * See if the ref begins with an unsigned integer
1952 * if it does it's probably an OID component
1953 *
1954 * .<oid>
1955 */
1956 if (fr_sbuff_out(NULL, &oid, name) > 0) {
1957 namespace = fr_dict_unlocal(namespace);
1958
1959 fr_assert(ar == NULL);
1960
1961 fr_strerror_clear(); /* Clear out any existing errors */
1962
1963 if (fr_dict_by_da(namespace) == fr_dict_internal()) goto disallow_unknown;
1964
1965 /*
1966 * The OID component was a known attribute
1967 * so record it as a normal attribute
1968 * reference.
1969 */
1970 da = fr_dict_attr_child_by_num(namespace, oid);
1971 if (da) goto alloc_ar;
1972
1973 if (!at_rules->allow_unknown) {
1974 disallow_unknown:
1975 fr_strerror_const("Unknown attributes not allowed here");
1977 fr_sbuff_set(name, &m_s);
1978 goto error;
1979 }
1980
1981 /*
1982 * If it's numeric and not a known attribute
1983 * then we create an unknown attribute with
1984 * the specified attribute number.
1985 */
1986 MEM(ar = talloc(ctx, tmpl_attr_t));
1987
1988 /*
1989 * VSAs have VENDORs as children. All others are just normal things.
1990 */
1991 switch (namespace->type) {
1992 case FR_TYPE_VSA:
1993 da = fr_dict_attr_unknown_vendor_afrom_num(ar, namespace, oid);
1994 break;
1995
1996 default:
1997 da = fr_dict_attr_unknown_raw_afrom_num(ar, namespace, oid);
1998 break;
1999 }
2000
2001 if (!da) {
2002 if (err) *err = TMPL_ATTR_ERROR_UNKNOWN_NOT_ALLOWED; /* strerror set by dict function */
2003 goto error;
2004 }
2005
2006 *ar = (tmpl_attr_t){
2007 .ar_num = NUM_UNSPEC,
2008 .ar_type = TMPL_ATTR_TYPE_UNKNOWN,
2009 .ar_unknown = UNCONST(fr_dict_attr_t *, da),
2010 .ar_da = da,
2011 .ar_parent = our_parent,
2012 };
2013 goto do_suffix;
2014 }
2015
2016 /*
2017 * Can't parse it as an attribute, might be a literal string
2018 * let the caller decide.
2019 *
2020 * Don't alter the fr_strerror buffer, may contain useful
2021 * errors from the dictionary code.
2022 */
2023 if (!at_rules->allow_unresolved && !(at_rules->allow_wildcard && fr_sbuff_is_char(name, '['))) {
2024 fr_strerror_const_push("Unresolved attributes are not allowed here");
2026 fr_sbuff_set(name, &m_s);
2027 goto error;
2028 }
2029
2030 fr_sbuff_marker_release(&m_s);
2031
2032 /*
2033 * Once we hit one unresolved attribute we have to treat
2034 * the rest of the components are unresolved as well.
2035 */
2036 return tmpl_attr_ref_afrom_unresolved_substr(ctx, err, vpt, our_parent, namespace, name, at_rules);
2037
2038alloc_ar:
2039 /*
2040 * We have a da, remove any of the errors recorded from failed
2041 * searches to find the attribute to avoid misleading messages
2042 * if something else fails.
2043 */
2045
2046 MEM(ar = talloc(ctx, tmpl_attr_t));
2047 *ar = (tmpl_attr_t) {
2048 .ar_num = NUM_UNSPEC,
2049 .ar_type = TMPL_ATTR_TYPE_NORMAL,
2050 .ar_da = da,
2051 .ar_parent = our_parent,
2052 };
2053
2054do_suffix:
2055 /*
2056 * Parse the attribute reference filter
2057 *
2058 * Error out immediately if the filter is bad
2059 * otherwise determine whether to keep the
2060 * attribute reference or omit it based on:
2061 *
2062 * - Whether there was a filter present.
2063 * - The type of attribute.
2064 * - If this is the leaf attribute reference.
2065 */
2066 if (tmpl_attr_parse_filter(err, ar, name, at_rules) < 0) goto error;
2067
2068 /*
2069 * Local variables are always unitary.
2070 *
2071 * [0] is allowed, as is [n], [*], and [#]. But [1], etc. aren't allowed.
2072 */
2073 if (da->flags.local && (ar->ar_num > 0)) {
2074 fr_strerror_printf("Invalid array reference for local variable");
2076 fr_sbuff_set(name, &m_s);
2077 goto error;
2078 }
2079
2080 /*
2081 * At the end of the attribute reference. If there's a
2082 * trailing '.' then there's another attribute reference
2083 * we need to parse, otherwise we're done.
2084 */
2085 fr_sbuff_marker_release(&m_s);
2086 fr_sbuff_marker(&m_s, name);
2087
2088 if (fr_sbuff_next_if_char(name, '.')) {
2089 fr_dict_attr_t const *ref;
2090
2091 switch (da->type) {
2092 case FR_TYPE_GROUP:
2093 ref = fr_dict_attr_ref(da);
2094
2095 /*
2096 * If the ref is outside of the internal namespace, then we use it.
2097 *
2098 * If the ref is inside of the internal namespace (e.g. "request"), then we do
2099 * something else.
2100 *
2101 * If we were given a root dictionary on input, use that. We have to follow this
2102 * dictionary because this function calls itself recursively, WITHOUT updating
2103 * "dict_def" in the attr_rules. So the dict-def there is whatever got passed
2104 * into tmpl_afrom_attr_substr(), BEFORE the "parent.parent.parent..." parsing.
2105 * Which means that in many cases, the "dict_def" is completely irrelevant.
2106 *
2107 * If there is no parent on input, then we just use dict_def.
2108 *
2109 * Otherwise we search through all of the dictionaries.
2110 *
2111 * Note that we cannot put random protocol attributes into an internal attribute
2112 * of type "group".
2113 */
2114 if (ref != fr_dict_root(fr_dict_internal())) {
2115 our_parent = namespace = ref;
2116
2117 } else if (parent && parent->flags.is_root) {
2118 our_parent = namespace = parent;
2119
2120 } else if (at_rules->dict_def) {
2121 our_parent = namespace = fr_dict_root(at_rules->dict_def);
2122
2123 } else {
2124 our_parent = namespace = NULL;
2125 }
2126 break;
2127
2128 case FR_TYPE_STRUCT:
2129 case FR_TYPE_TLV:
2130 case FR_TYPE_VENDOR:
2131 case FR_TYPE_VSA:
2132 is_union:
2133 /*
2134 * Structural types are parented and namespaced from their parent da.
2135 */
2136 namespace = our_parent = da;
2137 break;
2138
2139 default:
2140 /*
2141 * Key fields can have children, because we really don't know how else to
2142 * represent the child structures.
2143 */
2144 if (fr_dict_attr_is_key_field(da)) goto is_union;
2145
2146 fr_strerror_printf("Parent type of nested attribute %s must be of type "
2147 "\"struct\", \"tlv\", \"vendor\", \"vsa\" or \"group\", got \"%s\"",
2148 da->name,
2149 fr_type_to_str(da->type));
2150 fr_sbuff_set(name, &m_s);
2151 goto error;
2152 }
2153
2154 if (ar) tmpl_attr_insert(vpt, ar);
2155
2156 if (tmpl_attr_afrom_attr_substr(ctx, err, vpt, our_parent, namespace, name, p_rules, at_rules, depth + 1) < 0) {
2157 if (ar) {
2158 tmpl_attr_list_talloc_free_tail(&vpt->data.attribute.ar); /* Remove and free ar */
2159 ar = NULL;
2160 }
2161 goto error;
2162 }
2163
2164 /*
2165 * If it's a leaf we always insert the attribute
2166 * reference into the list, even if it's a
2167 * nesting attribute.
2168 *
2169 * This is useful for nested edit sections
2170 * where the tmpl might be the name of a new
2171 * subsection.
2172 */
2173 } else {
2174 tmpl_attr_insert(vpt, ar);
2175 }
2176
2177 /*
2178 * Remove unnecessary casts.
2179 */
2182
2184
2185 fr_sbuff_marker_release(&m_s);
2186 return 0;
2187}
2188
2189/** Parse a string into a TMPL_TYPE_ATTR_* type #tmpl_t
2190 *
2191 * @param[in,out] ctx to allocate #tmpl_t in.
2192 * @param[out] err May be NULL. Provides the exact error that the parser hit
2193 * when processing the attribute ref.
2194 * @param[out] out Where to write pointer to new #tmpl_t.
2195 * @param[in] name of attribute including #tmpl_request_ref_t and #fr_pair_list_t qualifiers.
2196 * @param[in] p_rules Formatting rules used to check for trailing garbage.
2197 * @param[in] t_rules Rules which control parsing:
2198 * - dict_def The default dictionary to use if attributes
2199 * are unqualified.
2200 * - request_def The default #request_t to set if no
2201 * #tmpl_request_ref_t qualifiers are found in name.
2202 * - list_def The default list to set if no #fr_pair_list_t
2203 * qualifiers are found in the name.
2204 * - allow_unknown If true attributes in the format accepted by
2205 * #fr_dict_attr_unknown_afrom_oid_substr will be allowed,
2206 * even if they're not in the main dictionaries.
2207 * If an unknown attribute is found a #TMPL_TYPE_ATTR
2208 * #tmpl_t will be produced.
2209 * If #tmpl_afrom_attr_substr is being called on
2210 * startup, the #tmpl_t may be passed to
2211 * #tmpl_attr_unknown_add to
2212 * add the unknown attribute to the main dictionary.
2213 * If the unknown attribute is not added to
2214 * the main dictionary the #tmpl_t cannot be used
2215 * to search for a #fr_pair_t in a #request_t.
2216 * - allow_unresolved If true, we don't generate a parse error on
2217 * unknown attributes. If an unknown attribute is
2218 * found a #TMPL_TYPE_ATTR_UNRESOLVED
2219 * #tmpl_t will be produced.
2220 * - allow_foreign If true, allow attribute names to be qualified
2221 * with a protocol outside of the passed dict_def.
2222 * - disallow_filters
2223 *
2224 * @see REMARKER to produce pretty error markers from the return value.
2225 *
2226 * @return
2227 * - <= 0 on error (offset as negative integer)
2228 * - > 0 on success (number of bytes parsed).
2229 */
2232 fr_sbuff_parse_rules_t const *p_rules,
2233 tmpl_rules_t const *t_rules)
2234{
2235 int ret;
2236 tmpl_t *vpt;
2237 fr_sbuff_t our_name = FR_SBUFF(name); /* Take a local copy in case we need to back track */
2238 bool is_raw = false;
2239 tmpl_attr_rules_t const *at_rules;
2240 tmpl_attr_rules_t our_at_rules;
2242 fr_dict_attr_t const *namespace;
2244
2246
2247 at_rules = &t_rules->attr;
2248
2250
2252
2253 if (!fr_sbuff_extend(&our_name)) {
2254 fr_strerror_const("Empty attribute reference");
2256 FR_SBUFF_ERROR_RETURN(&our_name);
2257 }
2258
2259 /*
2260 * Check to see if we expect a reference prefix
2261 */
2262 switch (at_rules->prefix) {
2265 if (!fr_sbuff_next_if_char(&our_name, '&')) {
2266 fr_strerror_const("Invalid attribute reference, missing '&' prefix");
2268 FR_SBUFF_ERROR_RETURN(&our_name);
2269 }
2270 break;
2271 }
2272
2273 /*
2274 * Rewrite the prefix parsing to "auto", which affects the printing.
2275 */
2276 our_at_rules = *at_rules;
2277 our_at_rules.prefix = TMPL_ATTR_REF_PREFIX_AUTO;
2278 at_rules = &our_at_rules;
2279
2280 FALL_THROUGH; /* if we do require enum prefixes, then the '&' is optional */
2281
2283 /*
2284 * '&' prefix can be there, but doesn't have to be
2285 */
2286 (void) fr_sbuff_next_if_char(&our_name, '&');
2287 break;
2288
2290 if (fr_sbuff_is_char(&our_name, '&')) {
2291 fr_strerror_const("Attribute references used here must not have a '&' prefix");
2293 FR_SBUFF_ERROR_RETURN(&our_name);
2294 }
2295 break;
2296 }
2297
2298 /*
2299 * We parsed the tmpl as &User-Name, or just User-Name, but NOT %{User-Name}.
2300 * Mark it up as having a prefix.
2301 */
2302 MEM(vpt = tmpl_alloc(ctx, TMPL_TYPE_ATTR, T_BARE_WORD, NULL, 0));
2303 vpt->data.attribute.ref_prefix = TMPL_ATTR_REF_PREFIX_YES;
2304 vpt->rules.attr.prefix = at_rules->prefix;
2305
2306 /*
2307 * The "raw." prefix marks up the leaf attribute
2308 * as unknown if it wasn't already which allows
2309 * users to stick whatever they want in there as
2310 * a value.
2311 */
2312 if (fr_sbuff_adv_past_strcase_literal(&our_name, "raw.")) is_raw = true;
2313
2314 /*
2315 * Parse one or more request references
2316 */
2318 &vpt->data.attribute.rr,
2319 &our_name,
2320 p_rules,
2321 t_rules,
2322 &namespace);
2323 if (ret < 0) {
2324 error:
2325 *out = NULL;
2327 FR_SBUFF_ERROR_RETURN(&our_name);
2328 }
2329
2330 fr_sbuff_marker(&m_l, &our_name);
2331
2332 /*
2333 * Parse the list and / or attribute reference
2334 */
2336 vpt,
2337 namespace, namespace,
2338 &our_name, p_rules, at_rules, 0);
2339 if (ret < 0) goto error;
2340
2341 /*
2342 * Check to see if the user wants the leaf
2343 * attribute to be raw.
2344 *
2345 * We can only do the conversion now _if_
2346 * the complete hierarchy has been resolved
2347 * otherwise we'll need to do the conversion
2348 * later.
2349 */
2350 if (tmpl_is_attr(vpt) && is_raw) tmpl_attr_to_raw(vpt);
2351
2352 /*
2353 * Local variables cannot be given a list modifier.
2354 */
2356 tmpl_attr_t *ar = tmpl_attr_list_head(tmpl_attr(vpt));
2357 bool is_local = ar->ar_da->flags.local;
2358
2359 for (; ar != NULL;
2360 ar = tmpl_attr_list_next(tmpl_attr(vpt), ar)) {
2361 if (!ar->ar_da->flags.local ||
2362 (ar->ar_da->flags.local && is_local)) continue;
2363
2364 fr_strerror_printf("Local attributes cannot be used in any list");
2366 fr_sbuff_set(&our_name, &m_l);
2367 goto error;
2368 }
2369
2370 /*
2371 * That being said, local variables are named "foo", but are always put into the local list.
2372 */
2373 if (is_local && (at_rules->list_presence != TMPL_ATTR_LIST_FORBID)) {
2374 MEM(ar = talloc(vpt, tmpl_attr_t));
2375 *ar = (tmpl_attr_t){
2376 .ar_type = TMPL_ATTR_TYPE_NORMAL,
2377 .ar_da = request_attr_local,
2378 .ar_parent = fr_dict_root(fr_dict_internal())
2379 };
2380
2381 /*
2382 * Prepend the local list ref so it gets evaluated
2383 * first.
2384 */
2385 tmpl_attr_list_insert_head(tmpl_attr(vpt), ar);
2386 }
2387 }
2388
2389 /*
2390 * Check whether the tmpl has a list qualifier.
2391 */
2392 switch (at_rules->list_presence) {
2394 break;
2395
2397 if (tmpl_attr_is_list_attr(tmpl_attr_list_head(tmpl_attr(vpt)))) {
2398 fr_strerror_const("List qualifiers are not allowed here.");
2400 goto error;
2401 }
2402 break;
2403
2405 if (!tmpl_attr_is_list_attr(tmpl_attr_list_head(tmpl_attr(vpt)))) {
2406 fr_strerror_const("List qualifier is required, but no list was found.");
2408 goto error;
2409 }
2410 break;
2411 }
2412
2413 /*
2414 * If we're using lists, ensure that the default list is specified.
2415 */
2416 if (!tmpl_attr_is_list_attr(tmpl_attr_list_head(tmpl_attr(vpt)))) {
2417 tmpl_attr_t *ar;
2418
2419 MEM(ar = talloc(vpt, tmpl_attr_t));
2420 *ar = (tmpl_attr_t){
2421 .ar_type = TMPL_ATTR_TYPE_NORMAL,
2422 .ar_parent = fr_dict_root(fr_dict_internal())
2423 };
2424
2425 fr_assert(at_rules->list_def);
2426 ar->ar_da = at_rules->list_def;
2427
2428 /*
2429 * Prepend the list ref so it gets evaluated
2430 * first.
2431 */
2432 tmpl_attr_list_insert_head(tmpl_attr(vpt), ar);
2433 }
2434
2435 tmpl_set_name(vpt, T_BARE_WORD, fr_sbuff_start(&our_name), fr_sbuff_used(&our_name));
2436 vpt->rules = *t_rules; /* Record the rules */
2437
2438 /*
2439 * If there are actual requests, duplicate them
2440 * and move them into the list.
2441 *
2442 * A NULL request_def pointer is equivalent to the
2443 * current request.
2444 */
2445 if (t_rules->attr.request_def) {
2446 tmpl_request_ref_list_acopy(vpt, &vpt->rules.attr.request_def, t_rules->attr.request_def);
2447 }
2448
2449 if (tmpl_is_attr(vpt)) {
2450 tmpl_attr_t *ar;
2451
2452 /*
2453 * Suppress useless casts.
2454 */
2456 vpt->rules.cast = FR_TYPE_NULL;
2457 }
2458
2459 /*
2460 * Ensure that the list is set correctly, so that
2461 * the returned vpt just doesn't just match the
2462 * input rules, it is also internally consistent.
2463 */
2464 ar = tmpl_attr_list_head(tmpl_attr(vpt));
2465 fr_assert(ar != NULL);
2466
2467 if (tmpl_attr_is_list_attr(ar)) vpt->rules.attr.list_def = ar->ar_da;
2468
2470
2471 }
2472
2473 if (!tmpl_substr_terminal_check(&our_name, p_rules)) {
2474 fr_strerror_const("Unexpected text after attribute reference");
2476 goto error;
2477 }
2478
2479 /*
2480 * If everything was resolved correctly
2481 * we now need to check the cast type.
2482 */
2483 if (!tmpl_needs_resolving(vpt) && !fr_type_is_null(t_rules->cast) &&
2484 !fr_type_cast(t_rules->cast, tmpl_attr_tail_da(vpt)->type)) {
2485 fr_strerror_printf("Cannot cast type '%s' to '%s'",
2488 fr_sbuff_set_to_start(&our_name);
2489 goto error;
2490 }
2491
2492 TMPL_VERIFY(vpt); /* Because we want to ensure we produced something sane */
2493
2494 *out = vpt;
2495 FR_SBUFF_SET_RETURN(name, &our_name);
2496}
2497
2498/** Parse a string into a TMPL_TYPE_ATTR_* type #tmpl_t
2499 *
2500 * @param[in,out] ctx to allocate #tmpl_t in.
2501 * @param[out] err May be NULL. Provides the exact error that the parser hit
2502 * when processing the attribute ref.
2503 * @param[out] out Where to write pointer to new #tmpl_t.
2504 * @param[in] name of attribute including #tmpl_request_ref_t and #fr_pair_list_t qualifiers.
2505 * @param[in] t_rules Rules which control parsing. See tmpl_afrom_attr_substr() for details.
2506 *
2507 * @note Unlike #tmpl_afrom_attr_substr this function will error out if the entire
2508 * name string isn't parsed.
2509 */
2511 tmpl_t **out, char const *name, tmpl_rules_t const *t_rules)
2512{
2513 ssize_t slen, name_len;
2515
2517
2518 name_len = strlen(name);
2519 slen = tmpl_afrom_attr_substr(ctx, err, out, &FR_SBUFF_IN(name, name_len), NULL, t_rules);
2520 if (slen <= 0) return slen;
2521
2522 if (!fr_cond_assert(*out)) return -1;
2523
2524 if (slen != name_len) {
2525 /* This looks wrong, but it produces meaningful errors for unknown attrs */
2526 fr_strerror_printf("Unexpected text after %s",
2527 tmpl_type_to_str((*out)->type));
2528 return -slen;
2529 }
2530
2531 TMPL_VERIFY(*out);
2532
2533 return slen;
2534}
2535
2536/** Create TMPL_TYPE_DATA from a string
2537 *
2538 * @param[in] ctx to allocate tmpl to.
2539 * @param[out] out where to write tmpl.
2540 * @param[in] in sbuff to parse.
2541 * @param[in] quote surrounding the operand to parse.
2542 * @param[in] t_rules specifying the cast and any enumeration values.
2543 * @param[in] allow_enum Whether parsing the value as an enum should be allowed.
2544 * @param[in] p_rules formatting rules.
2545 * @return
2546 * - <0 on error
2547 * - >=0 on success.
2548 */
2550 fr_token_t quote,
2551 tmpl_rules_t const *t_rules, bool allow_enum,
2552 fr_sbuff_parse_rules_t const *p_rules)
2553{
2554 fr_sbuff_t our_in = FR_SBUFF(in);
2555 fr_value_box_t tmp;
2556 tmpl_t *vpt;
2558
2559 if (!fr_type_is_null(t_rules->cast)) cast = t_rules->cast;
2560
2561 if (!fr_type_is_leaf(cast)) {
2562 fr_strerror_printf("%s is not a valid cast type",
2563 fr_type_to_str(cast));
2564 FR_SBUFF_ERROR_RETURN(&our_in);
2565 }
2566
2567 vpt = tmpl_alloc_null(ctx);
2568 if (fr_value_box_from_substr(vpt, &tmp,
2569 cast, allow_enum ? t_rules->enumv : NULL,
2570 &our_in, p_rules, false) < 0) {
2572 FR_SBUFF_ERROR_RETURN(&our_in);
2573 }
2575
2576 tmpl_init(vpt, TMPL_TYPE_DATA, quote, fr_sbuff_start(&our_in), fr_sbuff_used(&our_in), t_rules);
2577
2579
2580 *out = vpt;
2581
2582 if (cast == tmpl_value_type(vpt)) vpt->rules.cast = FR_TYPE_NULL;
2583
2585
2586 FR_SBUFF_SET_RETURN(in, &our_in);
2587}
2588
2589/** Parse a truth value
2590 *
2591 * @param[in] ctx to allocate tmpl to.
2592 * @param[out] out where to write tmpl.
2593 * @param[in] in sbuff to parse.
2594 * @param[in] p_rules formatting rules.
2595 * @return
2596 * - < 0 sbuff does not contain a boolean value.
2597 * - > 0 how many bytes were parsed.
2598 */
2600 fr_sbuff_parse_rules_t const *p_rules)
2601{
2602 fr_sbuff_t our_in = FR_SBUFF(in);
2603 bool a_bool;
2604 tmpl_t *vpt;
2605
2606 if (fr_sbuff_out(NULL, &a_bool, &our_in) < 0) {
2607 fr_strerror_const("Not a boolean value");
2608 return 0;
2609 }
2610
2611 if (!tmpl_substr_terminal_check(&our_in, p_rules)) {
2612 fr_strerror_const("Unexpected text after bool");
2614 }
2615
2617
2618 fr_value_box_init(&vpt->data.literal, FR_TYPE_BOOL, NULL, false);
2619 vpt->data.literal.vb_bool = a_bool;
2620
2621 *out = vpt;
2622
2623 FR_SBUFF_SET_RETURN(in, &our_in);
2624}
2625
2626/** Parse bareword as an octet string
2627 *
2628 * @param[in] ctx to allocate tmpl to.
2629 * @param[out] out where to write tmpl.
2630 * @param[in] in sbuff to parse.
2631 * @param[in] p_rules formatting rules.
2632 * @return
2633 * - < 0 negative offset where parse error occurred.
2634 * - 0 sbuff does not contain a hex string.
2635 * - > 0 how many bytes were parsed.
2636 */
2638 fr_sbuff_parse_rules_t const *p_rules)
2639{
2640 fr_sbuff_t our_in = FR_SBUFF(in);
2641 tmpl_t *vpt;
2642 char *hex;
2643 size_t binlen, len;
2644 uint8_t *bin;
2645
2646 if (!fr_sbuff_adv_past_strcase_literal(&our_in, "0x")) return 0;
2647
2648 MEM(vpt = tmpl_alloc(ctx, TMPL_TYPE_DATA, T_BARE_WORD, NULL, 0));
2649
2650 /*
2651 * This allows stream parsing to work correctly
2652 * we could be less lazy and copy hex data in
2653 * chunks, but never mind...
2654 */
2655 len = fr_sbuff_out_abstrncpy_allowed(vpt, &hex, &our_in, SIZE_MAX, sbuff_char_class_hex);
2656 if (len & 0x01) {
2657 fr_strerror_const("Hex string not even length");
2658 error:
2660 FR_SBUFF_ERROR_RETURN(&our_in);
2661 }
2662 if (len == 0) {
2663 fr_strerror_const("Zero length hex string is invalid");
2664 goto error;
2665 }
2666
2667 if (!tmpl_substr_terminal_check(&our_in, p_rules)) {
2668 fr_strerror_const("Unexpected text after hex string");
2669 goto error;
2670 }
2671
2672 bin = (uint8_t *)hex;
2673 binlen = len / 2;
2674
2676
2677 (void)fr_base16_decode(NULL, &FR_DBUFF_TMP(bin, binlen), &FR_SBUFF_IN(hex, len), false);
2678 MEM(bin = talloc_realloc_size(vpt, bin, binlen)); /* Realloc to the correct length */
2679 (void)fr_value_box_memdup_shallow(&vpt->data.literal, NULL, bin, binlen, false);
2680
2681 *out = vpt;
2682
2683 FR_SBUFF_SET_RETURN(in, &our_in);
2684}
2685
2686/** Parse bareword as an IPv4 address or prefix
2687 *
2688 * @param[in] ctx to allocate tmpl to.
2689 * @param[out] out where to write tmpl.
2690 * @param[in] in sbuff to parse.
2691 * @param[in] p_rules formatting rules.
2692 * @return
2693 * - < 0 sbuff does not contain an IPv4 address or prefix.
2694 * - > 0 how many bytes were parsed.
2695 */
2697 fr_sbuff_parse_rules_t const *p_rules)
2698{
2699 tmpl_t *vpt;
2700 fr_sbuff_t our_in = FR_SBUFF(in);
2701 uint8_t octet;
2703
2704 /*
2705 * Check for char sequence
2706 *
2707 * xxx.xxx.xxx.xxx
2708 */
2709 if (!(fr_sbuff_out(NULL, &octet, &our_in) && fr_sbuff_next_if_char(&our_in, '.') &&
2710 fr_sbuff_out(NULL, &octet, &our_in) && fr_sbuff_next_if_char(&our_in, '.') &&
2711 fr_sbuff_out(NULL, &octet, &our_in) && fr_sbuff_next_if_char(&our_in, '.') &&
2712 fr_sbuff_out(NULL, &octet, &our_in))) {
2713 error:
2714 FR_SBUFF_ERROR_RETURN(&our_in);
2715 }
2716
2717 /*
2718 * If it has a trailing '/' then it's probably
2719 * an IP prefix.
2720 */
2721 if (fr_sbuff_next_if_char(&our_in, '/')) {
2722 if (fr_sbuff_out(NULL, &octet, &our_in) < 0) {
2723 fr_strerror_const("IPv4 CIDR mask malformed");
2724 goto error;
2725 }
2726
2727 if (octet > 32) {
2728 fr_strerror_const("IPv4 CIDR mask too high");
2729 goto error;
2730 }
2731
2733 } else {
2735 }
2736
2737 if (!tmpl_substr_terminal_check(&our_in, p_rules)) {
2738 fr_strerror_const("Unexpected text after IPv4 string or prefix");
2739 goto error;
2740 }
2741
2743 if (fr_value_box_from_substr(vpt, &vpt->data.literal, type, NULL,
2744 &FR_SBUFF_REPARSE(&our_in),
2745 NULL, false) < 0) {
2747 goto error;
2748 }
2749 *out = vpt;
2750
2751 FR_SBUFF_SET_RETURN(in, &our_in);
2752}
2753
2754/** Parse bareword as an IPv6 address or prefix
2755 *
2756 * @param[in] ctx to allocate tmpl to.
2757 * @param[out] out where to write tmpl.
2758 * @param[in] in sbuff to parse.
2759 * @param[in] p_rules formatting rules.
2760 * @return
2761 * - < 0 sbuff does not contain an IPv4 address or prefix.
2762 * - > 0 how many bytes were parsed.
2763 */
2765 fr_sbuff_parse_rules_t const *p_rules)
2766{
2767 tmpl_t *vpt;
2768 fr_sbuff_t our_in = FR_SBUFF(in);
2771 size_t len;
2772 char *sep_a, *sep_b;
2773
2774 static bool ipv6_chars[UINT8_MAX + 1] = {
2775 ['0'] = true, ['1'] = true, ['2'] = true, ['3'] = true, ['4'] = true,
2776 ['5'] = true, ['6'] = true, ['7'] = true, ['8'] = true, ['9'] = true,
2777 ['a'] = true, ['b'] = true, ['c'] = true, ['d'] = true, ['e'] = true,
2778 ['f'] = true,
2779 ['A'] = true, ['B'] = true, ['C'] = true, ['D'] = true, ['E'] = true,
2780 ['F'] = true,
2781 [':'] = true, ['.'] = true
2782 };
2783
2784 /*
2785 * Drop a marker to pin the start of the
2786 * address in the buffer.
2787 */
2788 fr_sbuff_marker(&m, &our_in);
2789
2790 /*
2791 * Check for something looking like an IPv6 address
2792 *
2793 * Minimum string is '::'
2794 */
2795 len = fr_sbuff_adv_past_allowed(&our_in, FR_IPADDR_STRLEN + 1, ipv6_chars, NULL);
2796 if ((len < 2) || (len > FR_IPADDR_STRLEN)) {
2797 error:
2798 FR_SBUFF_ERROR_RETURN(&our_in);
2799 }
2800
2801 /*
2802 * Got ':' after '.', this isn't allowed.
2803 *
2804 * We need this check else IPv4 gets parsed
2805 * as blank IPv6 address.
2806 */
2807 sep_a = memchr(fr_sbuff_current(&m), '.', len);
2808 if (sep_a && (!(sep_b = memchr(fr_sbuff_current(&m), ':', len)) || (sep_b > sep_a))) {
2809 fr_strerror_const("First IPv6 component separator was a '.'");
2810 goto error;
2811 }
2812
2813 /*
2814 * The v6 parse function will happily turn
2815 * integers into v6 addresses *sigh*.
2816 */
2817 sep_a = memchr(fr_sbuff_current(&m), ':', len);
2818 if (!sep_a) {
2819 fr_strerror_const("No IPv6 component separator");
2820 goto error;
2821 }
2822
2823 /*
2824 * Handle scope
2825 */
2826 if (fr_sbuff_next_if_char(&our_in, '%')) {
2827 len = fr_sbuff_adv_until(&our_in, IFNAMSIZ + 1, p_rules->terminals, '\0');
2828 if ((len < 1) || (len > IFNAMSIZ)) {
2829 fr_strerror_const("IPv6 scope too long");
2830 goto error;
2831 }
2832 }
2833
2834 /*
2835 * ...and finally the prefix.
2836 */
2837 if (fr_sbuff_next_if_char(&our_in, '/')) {
2838 uint8_t mask;
2839
2840 if (fr_sbuff_out(NULL, &mask, &our_in) < 0) {
2841 fr_strerror_const("IPv6 CIDR mask malformed");
2842 goto error;
2843 }
2844 if (mask > 128) {
2845 fr_strerror_const("IPv6 CIDR mask too high");
2846 goto error;
2847 }
2848
2850 } else {
2852 }
2853
2854 if (!tmpl_substr_terminal_check(&our_in, p_rules)) {
2855 fr_strerror_const("Unexpected text after IPv6 string or prefix");
2856 goto error;
2857 }
2858
2860 if (fr_value_box_from_substr(vpt, &vpt->data.literal, type, NULL,
2861 &FR_SBUFF_REPARSE(&our_in),
2862 NULL, false) < 0) {
2864 goto error;
2865 }
2866 *out = vpt;
2867
2868 FR_SBUFF_SET_RETURN(in, &our_in);
2869}
2870
2871
2872/** Try and parse signed or unsigned integers
2873 *
2874 * @param[in] ctx to allocate tmpl to.
2875 * @param[out] out where to write tmpl.
2876 * @param[in] in sbuff to parse.
2877 * @param[in] p_rules formatting rules.
2878 * @return
2879 * - < 0 sbuff does not contain a mac address.
2880 * - > 0 how many bytes were parsed.
2881 */
2883 fr_sbuff_parse_rules_t const *p_rules)
2884{
2885 tmpl_t *vpt;
2886 fr_sbuff_t our_in = FR_SBUFF(in);
2887 uint8_t buff[6] = {};
2888 fr_dbuff_t dbuff;
2889 fr_value_box_t *vb;
2891
2892 fr_dbuff_init(&dbuff, buff, sizeof(buff));
2893
2894 fr_base16_decode(&err, &dbuff, &our_in, true);
2895 if (err != FR_SBUFF_PARSE_OK) return 0;
2896
2897 if (!fr_sbuff_next_if_char(&our_in, ':')) return 0;
2898
2899 fr_base16_decode(&err, &dbuff, &our_in, true);
2900 if (err != FR_SBUFF_PARSE_OK) return 0;
2901
2902 if (!fr_sbuff_next_if_char(&our_in, ':')) return 0;
2903
2904 fr_base16_decode(&err, &dbuff, &our_in, true);
2905 if (err != FR_SBUFF_PARSE_OK) return 0;
2906
2907 if (!fr_sbuff_next_if_char(&our_in, ':')) return 0;
2908
2909 fr_base16_decode(&err, &dbuff, &our_in, true);
2910 if (err != FR_SBUFF_PARSE_OK) return 0;
2911
2912 if (!fr_sbuff_next_if_char(&our_in, ':')) return 0;
2913
2914 fr_base16_decode(&err, &dbuff, &our_in, true);
2915 if (err != FR_SBUFF_PARSE_OK) return 0;
2916
2917 if (!fr_sbuff_next_if_char(&our_in, ':')) return 0;
2918
2919 fr_base16_decode(&err, &dbuff, &our_in, true);
2920 if (err != FR_SBUFF_PARSE_OK) return 0;
2921
2922 if (!tmpl_substr_terminal_check(&our_in, p_rules)) {
2923 fr_strerror_const("Unexpected text after mac address");
2924 return 0;
2925 }
2926
2928 T_BARE_WORD, fr_sbuff_start(&our_in), fr_sbuff_used(&our_in)));
2929 vb = tmpl_value(vpt);
2930
2931 fr_value_box_init(vb, FR_TYPE_ETHERNET, NULL, false);
2932 memcpy(vb->vb_ether, buff, sizeof(vb->vb_ether));
2933
2934 *out = vpt;
2935
2936 FR_SBUFF_SET_RETURN(in, &our_in);
2937}
2938
2939/** Try and parse signed or unsigned integers
2940 *
2941 * @param[in] ctx to allocate tmpl to.
2942 * @param[out] out where to write tmpl.
2943 * @param[in] in sbuff to parse.
2944 * @param[in] p_rules formatting rules.
2945 * @return
2946 * - < 0 sbuff does not contain an integer.
2947 * - > 0 how many bytes were parsed.
2948 */
2950 fr_sbuff_parse_rules_t const *p_rules)
2951{
2952 tmpl_t *vpt;
2953 fr_sbuff_t our_in = FR_SBUFF(in);
2954 ssize_t slen;
2955 fr_value_box_t *vb;
2956
2957 /*
2958 * Pick the narrowest signed type
2959 */
2960 if (fr_sbuff_is_char(&our_in, '-')) {
2961 int64_t a_int;
2962
2963 slen = fr_sbuff_out(NULL, &a_int, &our_in);
2964 if (slen <= 0) return 0;
2965
2966 if (!tmpl_substr_terminal_check(&our_in, p_rules)) {
2967 fr_strerror_const("Unexpected text after signed integer");
2968 error:
2969 FR_SBUFF_ERROR_RETURN(&our_in);
2970 }
2971
2973 T_BARE_WORD, fr_sbuff_start(&our_in), fr_sbuff_used(&our_in)));
2974 vb = tmpl_value(vpt);
2975 if (a_int >= INT8_MIN) {
2976 fr_value_box_init(vb, FR_TYPE_INT8, NULL, false);
2977 vb->vb_int8 = (int8_t)a_int;
2978 } else if (a_int >= INT16_MIN) {
2979 fr_value_box_init(vb, FR_TYPE_INT16, NULL, false);
2980 vb->vb_int16 = (int16_t)a_int;
2981 } else if (a_int >= INT32_MIN) {
2982 fr_value_box_init(vb, FR_TYPE_INT32, NULL, false);
2983 vb->vb_int32 = (int32_t)a_int;
2984 } else {
2985 fr_value_box_init(vb, FR_TYPE_INT64, NULL, false);
2986 vb->vb_int64 = (int64_t)a_int;
2987 }
2988 /*
2989 * Pick the narrowest unsigned type
2990 */
2991 } else {
2992 uint64_t a_uint;
2993
2994 slen = fr_sbuff_out(NULL, &a_uint, &our_in);
2995 if (slen <= 0) return slen;
2996
2997 if (!tmpl_substr_terminal_check(&our_in, p_rules)) {
2998 fr_strerror_const("Unexpected text after unsigned integer");
2999 goto error;
3000 }
3001
3003 T_BARE_WORD, fr_sbuff_start(&our_in), fr_sbuff_used(&our_in)));
3004 vb = tmpl_value(vpt);
3005 if (a_uint <= UINT8_MAX) {
3006 fr_value_box_init(vb, FR_TYPE_UINT8, NULL, false);
3007 vb->vb_uint8 = (uint8_t)a_uint;
3008 } else if (a_uint <= UINT16_MAX) {
3009 fr_value_box_init(vb, FR_TYPE_UINT16, NULL, false);
3010 vb->vb_uint16 = (uint16_t)a_uint;
3011 } else if (a_uint <= UINT32_MAX) {
3012 fr_value_box_init(vb, FR_TYPE_UINT32, NULL, false);
3013 vb->vb_uint32 = (uint32_t)a_uint;
3014 } else {
3015 fr_value_box_init(vb, FR_TYPE_UINT64, NULL, false);
3016 vb->vb_uint64 = (uint64_t)a_uint;
3017 }
3018 }
3019
3020 *out = vpt;
3021
3022 FR_SBUFF_SET_RETURN(in, &our_in);
3023}
3024
3026 fr_sbuff_parse_rules_t const *p_rules)
3027{
3028 tmpl_t *vpt;
3029 fr_sbuff_t our_in = FR_SBUFF(in);
3030 double a_float;
3031 ssize_t slen;
3032 fr_value_box_t *vb;
3033
3034 slen = fr_sbuff_out(NULL, &a_float, &our_in);
3035 if (slen <= 0) return 0;
3036
3037 if (!tmpl_substr_terminal_check(&our_in, p_rules)) {
3038 fr_strerror_const("Unexpected text after float");
3039 FR_SBUFF_ERROR_RETURN(&our_in);
3040 }
3041
3043 T_BARE_WORD, fr_sbuff_start(&our_in), fr_sbuff_used(&our_in)));
3044 vb = tmpl_value(vpt);
3045 fr_value_box_init(vb, FR_TYPE_FLOAT64, NULL, false);
3046 vb->vb_float64 = a_float;
3047
3048 *out = vpt;
3049
3050 FR_SBUFF_SET_RETURN(in, &our_in);
3051}
3052
3054 fr_sbuff_parse_rules_t const *p_rules)
3055{
3056 tmpl_t *vpt;
3057 fr_sbuff_t our_in = FR_SBUFF(in);
3058 fr_time_delta_t a_delta;
3059 fr_slen_t slen;
3060 fr_value_box_t *vb;
3061
3062 slen = fr_time_delta_from_substr(&a_delta, &our_in, FR_TIME_RES_SEC, true, p_rules ? p_rules->terminals : NULL);
3063 if (slen <= 0) return 0;
3064
3066 T_BARE_WORD, fr_sbuff_start(&our_in), fr_sbuff_used(&our_in)));
3067 vb = tmpl_value(vpt);
3068 fr_value_box_init(vb, FR_TYPE_TIME_DELTA, NULL, false);
3069 vb->vb_time_delta = a_delta;
3070
3071 *out = vpt;
3072
3073 FR_SBUFF_SET_RETURN(in, &our_in);
3074}
3075
3076/*
3077 * ::value
3078 *
3079 * Treated as enum name. Note that this check MUST be done after the test for IPv6, as
3080 * "::1" is an allowed IPv6 address.
3081 *
3082 * @todo - Mark this up as an enum name? Or do we really care? Maybe we want to allow
3083 *
3084 * Service-Type == 'Framed-User'
3085 *
3086 * or
3087 *
3088 * Service-Type == "Framed-User'
3089 *
3090 * as the second one allows for xlat expansions of enum names.
3091 *
3092 * We probably do want to forbid the single-quoted form of enums,
3093 * as that doesn't seem to make sense.
3094 *
3095 * We also need to distinguish unresolved bare words as enums
3096 * (with :: prefix) from unresolved attributes without an & prefix.
3097 */
3098static ssize_t tmpl_afrom_enum(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in,
3099 fr_sbuff_parse_rules_t const *p_rules,
3100 tmpl_rules_t const *t_rules)
3101{
3102 tmpl_t *vpt;
3103 char *str;
3105 fr_sbuff_t our_in = FR_SBUFF(in);
3106
3107 /*
3108 * If there isn't a "::" prefix, then check for migration flags, and enum.
3109 *
3110 * If we require an enum prefix, then the input can't be an enum, and we don't do any more
3111 * parsing.
3112 *
3113 * Otherwise if there's no prefix and no enumv, we know this input can't be an enum name.
3114 */
3115 if (!fr_sbuff_adv_past_str_literal(&our_in, "::")) {
3116 if (tmpl_require_enum_prefix) return 0;
3117
3118 if (!t_rules->enumv) return 0;
3119
3120 } else if (t_rules->enumv &&
3121 ((t_rules->enumv->type == FR_TYPE_IPV6_ADDR) ||
3122 ((t_rules->enumv->type == FR_TYPE_IPV6_PREFIX)))) {
3123
3124 /*
3125 * We can't have enumerated names for IPv6 addresses.
3126 *
3127 * @todo - allow them ONLY if the RHS string is a valid enum name.
3128 */
3129 return 0;
3130 }
3131
3132 vpt = tmpl_alloc_null(ctx);
3133
3134 /*
3135 * If it doesn't match any other type of bareword, parse it as an enum name.
3136 *
3137 * Note that we don't actually try to resolve the enum name. The caller is responsible
3138 * for doing that.
3139 */
3140 if (fr_dict_enum_name_afrom_substr(vpt, &str, &sberr, &our_in, p_rules ? p_rules->terminals : NULL) < 0) {
3141 /*
3142 * Produce our own errors which make
3143 * more sense in the context of tmpls
3144 */
3145 switch (sberr) {
3147 fr_strerror_const("No operand found. Expected &ref, literal, "
3148 "'quoted literal', \"%{expansion}\", or enum value");
3149 break;
3150
3152 fr_strerror_const("enum values must contain at least one alpha character");
3153 break;
3154
3155 default:
3156 fr_strerror_const("Unexpected text after enum value.");
3157 break;
3158 }
3159
3161 FR_SBUFF_ERROR_RETURN(&our_in);
3162 }
3163
3164 /*
3165 * If there's a valid enum name, then we use it. Otherwise we leave name resolution to run time.
3166 */
3167 if (t_rules->enumv) {
3169
3170 dv = fr_dict_enum_by_name(t_rules->enumv, str, -1);
3171 if (dv) {
3173 fr_sbuff_start(&our_in), fr_sbuff_used(&our_in), t_rules);
3174 (void) fr_value_box_copy(vpt, &vpt->data.literal, dv->value);
3175 vpt->data.literal.enumv = t_rules->enumv;
3176
3177 *out = vpt;
3178 FR_SBUFF_SET_RETURN(in, &our_in);
3179 }
3180 }
3181
3182 /*
3183 * Either there's no enum, or the enum name didn't match one of the listed ones. There's no
3184 * point in waiting for an enum which might be declared later. That's not possible, so we fall
3185 * back to parsing the various data types.
3186 */
3187 if (t_rules->at_runtime) return 0;
3188
3190 fr_sbuff_start(&our_in), fr_sbuff_used(&our_in), t_rules);
3191 vpt->data.unescaped = str;
3192 *out = vpt;
3193
3194 FR_SBUFF_SET_RETURN(in, &our_in);
3195}
3196
3197/** Convert an arbitrary string into a #tmpl_t
3198 *
3199 * @note Unlike #tmpl_afrom_attr_str return code 0 doesn't necessarily indicate failure,
3200 * may just mean a 0 length string was parsed. Check to see if the function emitted
3201 * a #tmpl_t in *out.
3202 *
3203 * @note xlats and regexes are left uncompiled. This is to support the two pass parsing
3204 * done by the modcall code. Compilation on pass1 of that code could fail, as
3205 * attributes or xlat functions registered by modules may not be available (yet).
3206 *
3207 * @note For details of attribute parsing see #tmpl_afrom_attr_substr.
3208 *
3209 * @param[in,out] ctx To allocate #tmpl_t in.
3210 * @param[out] out Where to write the pointer to the new #tmpl_t.
3211 * @param[in] in String to parse.
3212 * @param[in] quote Quoting around the tmpl. Determines what we
3213 * attempt to parse the string as.
3214 * @param[in] p_rules Formatting rules for the tmpl.
3215 * @param[in] t_rules Validation rules for attribute references.
3216 * @return
3217 * - < 0 on error (offset as negative integer)
3218 * - >= 0 on success (number of bytes parsed).
3219 *
3220 * @see REMARKER to produce pretty error markers from the return value.
3221 *
3222 * @see tmpl_afrom_attr_substr
3223 */
3225 fr_sbuff_t *in, fr_token_t quote,
3226 fr_sbuff_parse_rules_t const *p_rules,
3227 tmpl_rules_t const *t_rules)
3228{
3229 fr_sbuff_t our_in = FR_SBUFF(in);
3230
3231 fr_slen_t slen;
3233 char *str;
3234
3235 tmpl_t *vpt = NULL;
3237
3239
3240 *out = NULL;
3241
3242 switch (quote) {
3243 case T_BARE_WORD:
3244 /*
3245 * Skip other bareword types if
3246 * we find a '&' prefix.
3247 */
3248 if (fr_sbuff_is_char(&our_in, '&')) return tmpl_afrom_attr_substr(ctx, NULL, out, in,
3249 p_rules, t_rules);
3250
3251 /*
3252 * Allow bareword xlats if we
3253 * find a '%' prefix.
3254 */
3255 if (fr_sbuff_is_char(&our_in, '%')) {
3257 xlat_exp_head_t *head = NULL;
3258
3259 vpt = tmpl_alloc_null(ctx);
3260 slen = xlat_tokenize(vpt, &head, &our_in, p_rules, t_rules, t_rules->literals_safe_for);
3261 if (slen <= 0) FR_SBUFF_ERROR_RETURN(&our_in);
3262
3264
3265 tmpl_init(vpt, type, quote, fr_sbuff_start(&our_in), slen, t_rules);
3266 vpt->data.xlat.ex = head;
3267
3268 *out = vpt;
3269
3271
3272 FR_SBUFF_SET_RETURN(in, &our_in);
3273 }
3274
3275 /*
3276 * Deal with explicit casts...
3277 */
3278 if (!fr_type_is_null(t_rules->cast)) return tmpl_afrom_value_substr(ctx, out, in, quote,
3279 t_rules, true, p_rules);
3280
3281 /*
3282 * We're at runtime and have a data type. Just parse it as that data type, without doing
3283 * endless "maybe it's this thing" attempts.
3284 */
3285 if (t_rules->at_runtime && t_rules->enumv) {
3286 tmpl_rules_t my_t_rules = *t_rules;
3287
3288 fr_assert(fr_type_is_leaf(t_rules->enumv->type));
3289
3290 my_t_rules.cast = my_t_rules.enumv->type;
3291
3292 return tmpl_afrom_value_substr(ctx, out, in, quote, &my_t_rules, true, p_rules);
3293 }
3294
3295 /*
3296 * Prefer enum names to IPv6 addresses.
3297 */
3298 if (t_rules->enumv && fr_sbuff_is_str_literal(&our_in, "::")) {
3299 slen = tmpl_afrom_enum(ctx, out, &our_in, p_rules, t_rules);
3300 if (slen > 0) goto done_bareword;
3301 fr_assert(!*out);
3302 }
3303
3304 /*
3305 * See if it's a boolean value
3306 */
3307 slen = tmpl_afrom_bool_substr(ctx, out, &our_in, p_rules);
3308 if (slen > 0) {
3309 done_bareword:
3310 TMPL_VERIFY(*out);
3311
3312 FR_SBUFF_SET_RETURN(in, &our_in);
3313 }
3314 fr_assert(!*out);
3315
3316 /*
3317 * See if it's an octets string
3318 */
3319 slen = tmpl_afrom_octets_substr(ctx, out, &our_in, p_rules);
3320 if (slen > 0) goto done_bareword;
3321 fr_assert(!*out);
3322
3323 /*
3324 * See if it's a mac address
3325 *
3326 * Needs to be before IPv6 as the pton functions
3327 * are too greedy, and on macOS will happily
3328 * convert a mac address to an IPv6 address.
3329 */
3330 slen = tmpl_afrom_ether_substr(ctx, out, &our_in, p_rules);
3331 if (slen > 0) goto done_bareword;
3332 fr_assert(!*out);
3333
3334 /*
3335 * See if it's an IPv4 address or prefix
3336 */
3337 slen = tmpl_afrom_ipv4_substr(ctx, out, &our_in, p_rules);
3338 if (slen > 0) goto done_bareword;
3339 fr_assert(!*out);
3340
3341 /*
3342 * See if it's an IPv6 address or prefix
3343 */
3344 slen = tmpl_afrom_ipv6_substr(ctx, out, &our_in, p_rules);
3345 if (slen > 0) goto done_bareword;
3346 fr_assert(!*out);
3347
3348 slen = tmpl_afrom_enum(ctx, out, &our_in, p_rules, t_rules);
3349 if (slen > 0) goto done_bareword;
3350 fr_assert(!*out);
3351
3352 /*
3353 * See if it's a integer
3354 */
3355 slen = tmpl_afrom_integer_substr(ctx, out, &our_in, p_rules);
3356 if (slen > 0) goto done_bareword;
3357 fr_assert(!*out);
3358
3359 /*
3360 * See if it's a float
3361 */
3362 slen = tmpl_afrom_float_substr(ctx, out, &our_in, p_rules);
3363 if (slen > 0) goto done_bareword;
3364 fr_assert(!*out);
3365
3366 /*
3367 * See if it's a time delta
3368 *
3369 * We do this after floats and integers so that
3370 * they get parsed as integer and float types
3371 * and not time deltas.
3372 */
3373 slen = tmpl_afrom_time_delta(ctx, out, &our_in, p_rules);
3374 if (slen > 0) goto done_bareword;
3375 fr_assert(!*out);
3376
3378
3379 /*
3380 * See if it's an attribute reference
3381 * without the prefix.
3382 */
3383 slen = tmpl_afrom_attr_substr(ctx, NULL, out, &our_in, p_rules, t_rules);
3384 if (slen > 0) goto done_bareword;
3385 fr_assert(!*out);
3386
3387 /*
3388 * We can't parse it as anything, that's an error.
3389 *
3390 * But it may be an enumeration value for an
3391 * attribute which is loaded later. In which
3392 * case we allow parsing the enumeration.
3393 */
3395 if (!fr_sbuff_is_str_literal(&our_in, "::")) {
3396 /*
3397 * Return the error string from parsing the attribute!
3398 */
3399 FR_SBUFF_ERROR_RETURN(&our_in);
3400 }
3401 }
3402
3403 /*
3404 * Attempt to resolve enumeration values
3405 */
3406 vpt = tmpl_alloc_null(ctx);
3407
3408 /*
3409 * If it doesn't match any other type of bareword, parse it as an enum name.
3410 *
3411 * Note that we don't actually try to resolve the enum name. The caller is responsible
3412 * for doing that.
3413 */
3414 if (fr_dict_enum_name_afrom_substr(vpt, &str, &sberr, &our_in, p_rules ? p_rules->terminals : NULL) < 0) {
3415 /*
3416 * Produce our own errors which make
3417 * more sense in the context of tmpls
3418 */
3419 switch (sberr) {
3421 fr_strerror_const("No operand found. Expected &ref, literal, "
3422 "'quoted literal', \"%{expansion}\", or enum value");
3423 break;
3424
3426 fr_strerror_const("enum values must contain at least one alpha character");
3427 break;
3428
3429 default:
3430 fr_strerror_const("Unexpected text after enum value.");
3431 break;
3432 }
3433
3435 FR_SBUFF_ERROR_RETURN(&our_in);
3436 }
3437
3439 fr_sbuff_start(&our_in), fr_sbuff_used(&our_in), t_rules);
3440 vpt->data.unescaped = str;
3441 *out = vpt;
3442
3443 FR_SBUFF_SET_RETURN(in, &our_in);
3444
3446 /*
3447 * Single quoted strings can be cast
3448 * to a specific data type immediately
3449 * as they cannot contain expansions.
3450 */
3451 if (!fr_type_is_null(t_rules->cast)) return tmpl_afrom_value_substr(ctx, out, in, quote,
3452 t_rules, false,
3453 p_rules);
3454 vpt = tmpl_alloc_null(ctx);
3455 slen = fr_sbuff_out_aunescape_until(vpt, &str, &our_in, SIZE_MAX,
3456 p_rules ? p_rules->terminals : NULL,
3457 p_rules ? p_rules->escapes : NULL);
3458 tmpl_init(vpt, TMPL_TYPE_DATA_UNRESOLVED, quote, fr_sbuff_start(&our_in), slen, t_rules);
3459 vpt->data.unescaped = str;
3460 break;
3461
3463 {
3464 xlat_exp_head_t *head = NULL;
3466
3467 vpt = tmpl_alloc_null(ctx);
3468
3469 slen = xlat_tokenize(vpt, &head, &our_in, p_rules, t_rules, t_rules->literals_safe_for);
3470 if (slen < 0) FR_SBUFF_ERROR_RETURN(&our_in);
3471
3472 /*
3473 * If the string doesn't contain an xlat,
3474 * and we want to cast it as a specific
3475 * type, then do the conversion now.
3476 */
3477 if (xlat_is_literal(head)) {
3478 if (!fr_type_is_null(t_rules->cast)) {
3479 talloc_free(vpt); /* Also frees any nodes */
3480
3481 return tmpl_afrom_value_substr(ctx, out,
3482 in, quote,
3483 t_rules, false, p_rules);
3484 }
3485
3486 /*
3487 * If the string doesn't contain an xlat
3488 * and there's no cast, we just store
3489 * the string for conversion later.
3490 */
3491 if (xlat_to_string(vpt, &str, &head)) {
3492 TALLOC_FREE(head);
3493
3495 fr_sbuff_start(&our_in), slen, t_rules);
3496 vpt->data.unescaped = str; /* Store the unescaped string for parsing later */
3497 break;
3498 }
3499 }
3500
3501 /*
3502 * If the string actually contains an xlat
3503 * store the compiled xlat.
3504 */
3506
3507 tmpl_init(vpt, type, quote, fr_sbuff_start(&our_in), slen, t_rules);
3508 vpt->data.xlat.ex = head;
3509 }
3510 break;
3511
3513 {
3515 xlat_exp_head_t *head = NULL;
3516
3517 vpt = tmpl_alloc_null(ctx);
3518
3519 /*
3520 * Ensure that we pre-parse the exec string.
3521 * This allows us to catch parse errors as early
3522 * as possible.
3523 *
3524 * FIXME - We need an ephemeral version of this
3525 * too.
3526 */
3527 slen = xlat_tokenize_argv(vpt, &head, &our_in, NULL, p_rules, t_rules, false, false);
3528 if ((slen <= 0) || !head) {
3530 FR_SBUFF_ERROR_RETURN(&our_in);
3531 }
3532
3533 /*
3534 * Ensure any xlats produced are bootstrapped
3535 * so that their instance data will be created.
3536 */
3537 if (xlat_finalize(head, t_rules->xlat.runtime_el) < 0) {
3538 fr_strerror_const("Failed to bootstrap xlat");
3539 FR_SBUFF_ERROR_RETURN(&our_in);
3540 }
3541
3543
3544 tmpl_init(vpt, type, quote, fr_sbuff_start(&our_in), slen, t_rules);
3545 vpt->data.xlat.ex = head;
3546 }
3547 break;
3548
3550 {
3551 xlat_exp_head_t *head = NULL;
3553
3554 if (!fr_type_is_null(t_rules->cast)) {
3555 fr_strerror_const("Casts cannot be used with regular expressions");
3556 fr_sbuff_set_to_start(&our_in); /* Point to the cast */
3557 FR_SBUFF_ERROR_RETURN(&our_in);
3558 }
3559
3560 vpt = tmpl_alloc_null(ctx);
3561
3562 slen = xlat_tokenize(vpt, &head, &our_in, p_rules, t_rules, FR_REGEX_SAFE_FOR);
3563 if (slen < 0) FR_SBUFF_ERROR_RETURN(&our_in);
3564
3565 /*
3566 * Check if the string actually contains an xlat
3567 * if it doesn't, we unfortunately still
3568 * can't compile the regex here, as we don't know if
3569 * it should be ephemeral or what flags should be used
3570 * during the compilation.
3571 *
3572 * The caller will need to do the compilation after we
3573 * return.
3574 */
3575 if (xlat_to_string(vpt, &str, &head)) {
3577 fr_sbuff_start(&our_in), slen, t_rules);
3578 vpt->data.unescaped = str; /* Store the unescaped string for compilation later */
3579 break;
3580 }
3581 /*
3582 * Mark the regex up as a regex-xlat which
3583 * will need expanding before evaluation, and can never
3584 * be pre-compiled.
3585 */
3587
3588 tmpl_init(vpt, type, quote, fr_sbuff_start(&our_in), slen, t_rules);
3589 vpt->data.xlat.ex = head;
3590 }
3591 break;
3592
3593 default:
3594 fr_assert_msg(0, "Unknown quote type %i", quote);
3595 FR_SBUFF_ERROR_RETURN(&our_in);
3596 }
3597
3599 *out = vpt;
3600
3601 FR_SBUFF_SET_RETURN(in, &our_in);
3602}
3603
3604/** Copy a tmpl
3605 *
3606 * Fully duplicates the contents of a tmpl including any nested attribute
3607 * references.
3608 *
3609 * @param[in] ctx to perform allocations under.
3610 * @param[in] in tmpl to duplicate.
3611 * @return
3612 * - NULL on error.
3613 * - A new tmpl on success.
3614 */
3615tmpl_t *tmpl_copy(TALLOC_CTX *ctx, tmpl_t const *in)
3616{
3617 tmpl_t *vpt;
3618
3619 MEM(vpt = tmpl_alloc(ctx, in->type, in->quote, in->name, in->len));
3620 vpt->rules = in->rules;
3621
3622 /*
3623 * Copy over the unescaped data
3624 */
3626 if (unlikely(!(vpt->data.unescaped = talloc_bstrdup(vpt, in->data.unescaped)))) {
3627 error:
3629 return NULL;
3630 }
3631 }
3632
3633 /*
3634 * Copy attribute references
3635 */
3636 if (tmpl_contains_attr(vpt) && unlikely(tmpl_attr_copy(vpt, in) < 0)) goto error;
3637
3638 /*
3639 * Copy flags for all regex flavours (and possibly recompile the regex)
3640 */
3641 if (tmpl_contains_regex(vpt)) {
3642 vpt->data.reg_flags = in->data.reg_flags;
3643
3644 /*
3645 * If the tmpl contains a _compiled_ regex
3646 * then convert it back to an uncompiled
3647 * regex and recompile.
3648 *
3649 * Most of the regex libraries don't allow
3650 * copying compiled expressions.
3651 */
3652 if (tmpl_is_regex(vpt)) {
3654 if (unlikely(!(vpt->data.unescaped = talloc_bstrdup(vpt, in->data.reg.src)))) goto error;
3655 if (unlikely(tmpl_regex_compile(vpt, vpt->data.reg.subcaptures) < 0)) goto error;
3656 return vpt;
3657 }
3658 }
3659
3660 /*
3661 * Copy the xlat component.
3662 *
3663 * @todo - in general we can't copy an xlat, as the instances need resolving!
3664 *
3665 * We add an assertion here because nothing allocates the head, and we need it.
3666 */
3667 if (tmpl_contains_xlat(vpt)) {
3668 fr_assert(vpt->data.xlat.ex != NULL);
3669
3670 if (unlikely(xlat_copy(vpt, vpt->data.xlat.ex, in->data.xlat.ex) < 0)) goto error;
3671 }
3672
3674
3675 return vpt;
3676}
3677
3678/** Parse a cast specifier
3679 *
3680 * Note that casts are
3681 *
3682 * (foo)
3683 *
3684 * and NOT
3685 *
3686 * ( foo )
3687 *
3688 * Not for any particular reason, but to emphasize a bit that they're
3689 * not mathematical expressions.
3690 *
3691 * @param[out] rules to set the cast type in.
3692 * @param[in] in String containing the cast marker.
3693 * @return
3694 * - 0 no cast specifier found.
3695 * - >0 the number of bytes parsed.
3696 * - <0 offset of parse error.
3697 */
3699{
3700 char close = '\0';
3701 fr_sbuff_t our_in = FR_SBUFF(in);
3703 fr_type_t cast;
3704 ssize_t slen;
3705
3706 if (fr_sbuff_next_if_char(&our_in, '(')) {
3707 close = ')';
3708
3709 } else {
3710 if (rules) rules->cast = FR_TYPE_NULL;
3711 return 0;
3712 }
3713
3714 fr_sbuff_marker(&m, &our_in);
3716 if (fr_type_is_null(cast)) {
3717 fr_strerror_const("Unknown data type");
3718 FR_SBUFF_ERROR_RETURN(&our_in);
3719 }
3720 if (fr_type_is_non_leaf(cast)) {
3721 fr_strerror_printf("Forbidden data type '%s' in cast", fr_type_to_str(cast));
3723 }
3724
3725 if (!fr_sbuff_next_if_char(&our_in, close)) {
3726 fr_strerror_const("Unterminated cast");
3727 FR_SBUFF_ERROR_RETURN(&our_in);
3728 }
3729 fr_sbuff_adv_past_whitespace(&our_in, SIZE_MAX, NULL);
3730
3731 if (rules) rules->cast = cast;
3732
3733 FR_SBUFF_SET_RETURN(in, &our_in);
3734}
3735
3736/** Set a cast for a tmpl
3737 *
3738 * @param[in,out] vpt to set cast for.
3739 * @param[in] dst_type to set.
3740 * @return
3741 * - 0 on success.
3742 * - -1 on failure.
3743 */
3745{
3746 fr_type_t src_type;
3747
3748 switch (dst_type) {
3749 default:
3750 fr_strerror_printf("Forbidden data type '%s' in cast",
3751 fr_type_to_str(dst_type));
3752 return -1;
3753
3754 /*
3755 * We can always remove a cast.
3756 */
3757 case FR_TYPE_NULL:
3758 goto done;
3759
3760 /*
3761 * Only "base" data types are allowed. Structural types
3762 * and horrid WiMAX crap is forbidden.
3763 */
3764 case FR_TYPE_LEAF:
3765 break;
3766 }
3767
3768 switch (vpt->type) {
3769 /*
3770 * This should have been fixed before we got here.
3771 */
3773
3774 /*
3775 * By default, tmpl types cannot be cast to anything.
3776 */
3777 default:
3778 fr_strerror_const("Cannot use cast here.");
3779 return -1;
3780
3781 /*
3782 * These tmpl types are effectively of data type
3783 * "string", so they can be cast to anything.
3784 */
3785 case TMPL_TYPE_XLAT:
3786 case TMPL_TYPE_EXEC:
3790 break;
3791
3792 case TMPL_TYPE_DATA:
3793 src_type = tmpl_value_type(vpt);
3794 goto check_types;
3795
3796 case TMPL_TYPE_ATTR:
3797 {
3799
3800 /*
3801 * If the attribute has an enum, then the cast means "use the raw value, and not
3802 * the enum name".
3803 */
3804 if (da->type == dst_type) {
3805 if (da->flags.has_value) goto done;
3806 return 0;
3807 }
3808 src_type = da->type;
3809 }
3810
3811 /*
3812 * Suppress casts where they are duplicate, unless there's an enumv. In which case the
3813 * cast means "don't print the enumv value, just print the raw data".
3814 */
3815 check_types:
3816 if (src_type == dst_type) {
3817 /*
3818 * Cast with enumv means "use the raw value, and not the enum name".
3819 */
3820 if (tmpl_rules_enumv(vpt)) {
3821 tmpl_rules_enumv(vpt) = NULL;
3822 goto done;
3823 }
3824 return 0;
3825 }
3826
3827 if (!fr_type_cast(dst_type, src_type)) {
3828 fr_strerror_printf("Cannot cast type '%s' to '%s'",
3829 fr_type_to_str(src_type),
3830 fr_type_to_str(dst_type));
3831 return -1;
3832 }
3833 break;
3834 }
3835
3836done:
3837 vpt->rules.cast = dst_type;
3838 return 0;
3839}
3840
3841#ifdef HAVE_REGEX
3842/** Parse a set of regular expression flags
3843 *
3844 * @param[out] vpt Write the flags to the regex flags field in this #tmpl_t.
3845 * @param[in] in Where to parse the flag string from.
3846 * @param[in] terminals That mark the end of the regex flag string.
3847 * @return
3848 * - 0 no flags found.
3849 * - >0 the number of bytes of flags parsed.
3850 * - <0 offset of parse error.
3851 */
3852ssize_t tmpl_regex_flags_substr(tmpl_t *vpt, fr_sbuff_t *in, fr_sbuff_term_t const *terminals)
3853{
3854 fr_slen_t slen;
3855 int err = 0;
3856
3858
3859 slen = regex_flags_parse(&err, &vpt->data.reg_flags, in, terminals, true);
3860 switch (err) {
3861 case 0:
3862 break;
3863
3864 case -1: /* Non-flag and non-terminal */
3865 case -2: /* Duplicate flag */
3866 return slen;
3867 }
3868
3869 return slen;
3870}
3871#endif
3872
3873/** @name Change a #tmpl_t type, usually by casting or resolving a reference
3874 *
3875 * #tmpl_cast_in_place can be used to convert #TMPL_TYPE_DATA_UNRESOLVED to a #TMPL_TYPE_DATA of a
3876 * specified #fr_type_t.
3877 *
3878 * #tmpl_attr_unknown_add converts a #TMPL_TYPE_ATTR with an unknown #fr_dict_attr_t to a
3879 * #TMPL_TYPE_ATTR with a known #fr_dict_attr_t, by adding the unknown #fr_dict_attr_t to the main
3880 * dictionary, and updating the ``tmpl_attr_tail_da`` pointer.
3881 * @{
3882 */
3883
3884/** Determine the correct quoting after a cast
3885 *
3886 * @param[in] existing_quote Exiting quotation type.
3887 * @param[in] type Cast type.
3888 * @param[in] enumv Enumeration values.
3889 * @param[in] unescaped The unescaped value of an enumeration.
3890 * @param[in] unescaped_len Length of unescaped.
3891 */
3892static inline CC_HINT(always_inline)
3894 fr_type_t type, fr_dict_attr_t const *enumv,
3895 char const *unescaped, size_t unescaped_len)
3896{
3897 if (!fr_type_is_string(type)) return T_BARE_WORD;
3898
3899 if (enumv && fr_dict_enum_by_name(enumv, unescaped, unescaped_len)) return T_BARE_WORD;
3900
3901 /*
3902 * Leave the original quoting if it's
3903 * single or double, else default to
3904 * single quoting.
3905 */
3906 switch (existing_quote) {
3909 return existing_quote;
3910
3911 default:
3913 }
3914}
3915
3916
3917/** Convert #tmpl_t of type #TMPL_TYPE_DATA_UNRESOLVED or #TMPL_TYPE_DATA to #TMPL_TYPE_DATA of type specified
3918 *
3919 * @note Conversion is done in place.
3920 * @note Irrespective of whether the #tmpl_t was #TMPL_TYPE_DATA_UNRESOLVED or #TMPL_TYPE_DATA,
3921 * on successful cast it will be #TMPL_TYPE_DATA.
3922 *
3923 * @param[in,out] vpt The template to modify. Must be of type #TMPL_TYPE_DATA_UNRESOLVED
3924 * or #TMPL_TYPE_DATA.
3925 * @param[in] type to cast to.
3926 * @param[in] enumv Enumerated dictionary values associated with a #fr_dict_attr_t.
3927 * @return
3928 * - 0 on success.
3929 * - -1 on failure.
3930 */
3932{
3934
3936
3937 switch (vpt->type) {
3939 {
3940 char *unescaped = vpt->data.unescaped;
3941
3942 /*
3943 * We're trying to convert an unresolved (bareword)
3944 * tmpl to octets.
3945 *
3946 * tmpl_afrom_substr uses the 0x prefix as type
3947 * inference, so if it was a hex string the tmpl
3948 * type would not have fallen through to
3949 * unresolved.
3950 *
3951 * That means if we're trying to resolve it here
3952 * it's really a printable string, not a sequence
3953 * of hexits, so we just want the binary
3954 * representation of that string, and not the hex
3955 * to bin conversion.
3956 */
3957 if (fr_type_is_octets(type)) {
3958 if (fr_value_box_memdup(vpt, &vpt->data.literal, enumv,
3959 (uint8_t const *)unescaped, talloc_array_length(unescaped) - 1,
3960 false) < 0) return -1;
3961 } else {
3962 if (fr_value_box_from_str(vpt, &vpt->data.literal, type,
3963 enumv,
3964 unescaped, talloc_array_length(unescaped) - 1,
3965 NULL, false) < 0) return -1;
3966 }
3967 vpt->type = TMPL_TYPE_DATA;
3968 vpt->quote = tmpl_cast_quote(vpt->quote, type, enumv,
3969 unescaped, talloc_array_length(unescaped) - 1);
3970 talloc_free(unescaped);
3971 fr_value_box_mark_safe_for(&vpt->data.literal, vpt->rules.literals_safe_for);
3972
3973 /*
3974 * The data is now of the correct type, so we don't need to keep a cast.
3975 */
3976 vpt->rules.cast = FR_TYPE_NULL;
3977 }
3978 break;
3979
3980 case TMPL_TYPE_DATA:
3981 {
3982 if (type == tmpl_value_type(vpt)) return 0; /* noop */
3983
3984 /*
3985 * Enumerations aren't used when casting between
3986 * data types. They're only used when processing
3987 * unresolved tmpls.
3988 *
3989 * i.e. TMPL_TYPE_DATA_UNRESOLVED != TMPL_TYPE_DATA(FR_TYPE_STRING)
3990 */
3991 if (fr_value_box_cast_in_place(vpt, &vpt->data.literal, type, NULL) < 0) return -1;
3992
3993 /*
3994 * Strings get quoted, everything else is a bare
3995 * word...
3996 */
3997 if (fr_type_is_string(type)) {
3998 vpt->quote = T_SINGLE_QUOTED_STRING;
3999 } else {
4000 vpt->quote = T_BARE_WORD;
4001 }
4002
4003 /*
4004 * The data is now of the correct type, so we don't need to keep a cast.
4005 */
4006 vpt->rules.cast = FR_TYPE_NULL;
4007 }
4008 break;
4009
4010 case TMPL_TYPE_ATTR:
4011 /*
4012 * Suppress casts to the same type.
4013 */
4014 if (tmpl_attr_tail_da(vpt)->type == type) {
4015 vpt->rules.cast = FR_TYPE_NULL;
4016 break;
4017 }
4019
4021 vpt->rules.cast = type;
4022 break;
4023
4024 default:
4025 fr_assert(0);
4026 }
4028
4029 return 0;
4030}
4031
4032/** Resolve an unresolved attribute
4033 *
4034 * Multi-pass parsing fixups for attribute references.
4035 *
4036 * @param[in] vpt to resolve.
4037 * @param[in] tr_rules Combined with the original parse rules for
4038 * additional resolution passes.
4039 * @return
4040 * - 0 if all references were resolved.
4041 * - -1 if there are unknown attributes which need
4042 * adding to the global dictionary first.
4043 * - -2 if there are attributes we couldn't resolve.
4044 */
4045static inline CC_HINT(always_inline) int tmpl_attr_resolve(tmpl_t *vpt, tmpl_res_rules_t const *tr_rules)
4046{
4047 tmpl_attr_t *ar = NULL, *next, *prev;
4048 fr_dict_attr_t const *da, *namespace;
4049 fr_dict_t const *dict_def;
4050
4052
4054
4055 dict_def = vpt->rules.attr.dict_def;
4056 if (!dict_def || tr_rules->force_dict_def) dict_def = tr_rules->dict_def;
4057
4058 /*
4059 * First component is special because we may need
4060 * to search for it in multiple dictionaries.
4061 *
4062 * This emulates what's done in the initial
4063 * tokenizer function.
4064 */
4065 ar = tmpl_attr_list_head(tmpl_attr(vpt));
4066 if (ar->type == TMPL_ATTR_TYPE_UNRESOLVED) {
4068 &da,
4069 dict_def,
4070 &FR_SBUFF_IN(ar->ar_unresolved,
4071 talloc_array_length(ar->ar_unresolved) - 1),
4072 NULL,
4073 true,
4074 vpt->rules.attr.allow_foreign);
4075 if (!da) return -2; /* Can't resolve, maybe the caller can resolve later */
4076
4077 ar->ar_type = TMPL_ATTR_TYPE_NORMAL;
4078 ar->ar_da = da;
4079 ar->ar_parent = fr_dict_root(fr_dict_by_da(da));
4080
4081 /*
4082 * Record the dictionary that was
4083 * successfully used for resolution.
4084 */
4085 vpt->rules.attr.dict_def = tr_rules->dict_def;
4086
4087 /*
4088 * Reach into the next reference
4089 * and correct its parent and
4090 * namespace.
4091 */
4092 next = tmpl_attr_list_next(tmpl_attr(vpt), ar);
4093 if (next) {
4094 next->ar_parent = da;
4095 next->ar_unresolved_namespace = da;
4096 }
4097 }
4098
4099 /*
4100 * Loop, resolving each unresolved attribute in turn
4101 */
4102 while ((ar = tmpl_attr_list_next(tmpl_attr(vpt), ar))) {
4103 switch (ar->type) {
4106 continue; /* Don't need to resolve */
4107
4109 return -1; /* Unknown attributes must be resolved first */
4110
4111 default:
4112 break;
4113 }
4114
4115 prev = tmpl_attr_list_prev(tmpl_attr(vpt), ar);
4116
4117 /*
4118 * If the parent is a list AR, then use the default dictionary for the namespace
4119 */
4120 namespace = (prev && dict_def && tmpl_attr_is_list_attr(prev)) ? fr_dict_root(dict_def) : ar->ar_unresolved_namespace;
4121
4122 (void)fr_dict_attr_by_name_substr(NULL,
4123 &da,
4124 namespace,
4125 &FR_SBUFF_IN(ar->ar_unresolved,
4126 talloc_array_length(ar->ar_unresolved) - 1),
4127 NULL);
4128 /*
4129 * Still can't resolve, check to see if
4130 * the last attribute reference was a
4131 * group.
4132 *
4133 * If it was, then we may be able to
4134 * fall back to resolving the attribute
4135 * in the internal dictionary.
4136 */
4137 if (!da) {
4138 if (prev && (prev->ar_da->type == FR_TYPE_GROUP)) {
4139 (void)fr_dict_attr_by_name_substr(NULL,
4140 &da,
4142 &FR_SBUFF_IN(ar->ar_unresolved,
4143 talloc_array_length(ar->ar_unresolved) - 1),
4144 NULL);
4145 }
4146 if (!da) return -2;
4147 }
4148
4149 /*
4150 * Known attribute, just rewrite.
4151 */
4152 ar->ar_type = TMPL_ATTR_TYPE_NORMAL;
4153 ar->ar_da = da;
4154
4155 /*
4156 * Parent should have been corrected in
4157 * the previous loop iteration.
4158 */
4159 fr_assert(ar->ar_parent && !ar->ar_parent->flags.is_unknown);
4160
4161 /*
4162 * Reach into the next reference
4163 * and correct its parent.
4164 */
4165 next = tmpl_attr_list_next(tmpl_attr(vpt), ar);
4166 if (next) {
4167 next->ar_parent = da;
4168 next->ar_unresolved_namespace = da;
4169 }
4170
4171 /*
4172 * Remove redundant attributes
4173 *
4174 * If it's not a group or does not specify
4175 * an index, the ar is redundant and should
4176 * be removed.
4177 */
4178 prev = tmpl_attr_list_prev(tmpl_attr(vpt), ar);
4179 if (prev && (prev->ar_da->type != FR_TYPE_GROUP) && (prev->ar_num == NUM_UNSPEC)) {
4180 tmpl_attr_list_remove(tmpl_attr(vpt), prev);
4181 ar->ar_parent = prev->ar_parent;
4182 talloc_free(prev);
4183 }
4184 }
4185
4186 RESOLVED_SET(&vpt->type);
4188
4189 return 0;
4190}
4191
4192/** Resolve an unresolved xlat, i.e. one containing unresolved attribute references or xlat functions
4193 *
4194 * Multi-pass parsing fixups for attribute references.
4195 *
4196 * Works for base types:
4197 * - TMPL_TYPE_XLAT
4198 * - TMPL_TYPE_EXEC
4199 * - TMPL_TYPE_REGEX_XLAT
4200 *
4201 * @param[in] vpt Containing the xlat expansion to resolve.
4202 * @param[in] tr_rules Combined with the original parse rules for
4203 * additional resolution passes.
4204 * @return
4205 * - 0 on success.
4206 * - -1 on failure.
4207 */
4208static inline CC_HINT(always_inline)
4210{
4211 if (xlat_resolve(vpt->data.xlat.ex,
4213 .tr_rules = tr_rules,
4214 .allow_unresolved = false
4215 }) < 0) return -1;
4216
4217 fr_assert(!xlat_needs_resolving(vpt->data.xlat.ex));
4218
4219 RESOLVED_SET(&vpt->type);
4221
4222 return 0;
4223}
4224
4225/** Attempt to resolve functions and attributes in xlats and attribute references
4226 *
4227 * @note If resolution is successful, the rules->attr.dict_def field will be modified to
4228 * reflect the dictionary resolution was successful in.
4229 *
4230 * @param[in,out] vpt to resolve. Should be of type TMPL_TYPE_XLAT_UNRESOLVED
4231 * or TMPL_TYPE_ATTR_UNRESOLVED. All other types will be
4232 * noops.
4233 * @param[in] tr_rules Combined with the original parse rules for
4234 * additional resolution passes.
4235 * @return
4236 * - 0 on success.
4237 * - -1 on failure.
4238 */
4240{
4241 static tmpl_res_rules_t const default_tr_rules;
4242
4243 int ret = 0;
4244
4245 if (!tmpl_needs_resolving(vpt)) return 0; /* Nothing to do */
4246
4247 if (!tr_rules) tr_rules = &default_tr_rules;
4248
4249 /*
4250 * Sanity check. There shouldn't be conflicting
4251 * enumvs between the original rules and resolution
4252 * rules.
4253 *
4254 * Either the enumv was available during parsing
4255 * and shouldn't have changed during subsequent
4256 * resolution passes, or it wasn't available at
4257 * parse-time, but now is.
4258 */
4259 if (tr_rules->enumv && tmpl_rules_enumv(vpt) && !tmpl_rules_enumv(vpt)->flags.is_unknown &&
4260 (tr_rules->enumv != tmpl_rules_enumv(vpt))) {
4261 fr_strerror_printf("mismatch between parse-time enumv '%s' and resolution-time enumv '%s'",
4262 tmpl_rules_enumv(vpt)->name, tr_rules->enumv->name);
4263
4264 return -1;
4265 }
4266
4267 /*
4268 * The xlat component of the #tmpl_t needs resolving.
4269 *
4270 * This includes exec tmpls, which are largely xlats
4271 * "under the hood".
4272 */
4273 if (tmpl_contains_xlat(vpt)) {
4274 ret = tmpl_xlat_resolve(vpt, tr_rules);
4275
4276 /*
4277 * The attribute reference needs resolving.
4278 */
4279 } else if (tmpl_contains_attr(vpt)) {
4280 fr_type_t dst_type = tmpl_rules_cast(vpt);
4281
4282 fr_assert(vpt->quote == T_BARE_WORD); /* 'User-Name' or "User-Name" is not allowed. */
4283
4284 ret = tmpl_attr_resolve(vpt, tr_rules);
4285 if (ret < 0) return ret;
4286
4287 if (dst_type == tmpl_attr_tail_da(vpt)->type) {
4288 vpt->rules.cast = FR_TYPE_NULL;
4289 }
4290
4291 /*
4292 * Convert unresolved tmpls into enumvs, or failing that, string values.
4293 *
4294 * Unresolved tmpls are by definition TMPL_TYPE_DATA.
4295 */
4296 } else if (tmpl_is_data_unresolved(vpt)) {
4297 fr_type_t dst_type = tmpl_rules_cast(vpt);
4298 fr_dict_attr_t const *enumv = tmpl_rules_enumv(vpt);
4299
4300 /*
4301 * If there wasn't an enumv set in the
4302 * original rules, and we now have one
4303 * (possibly because the other side of a
4304 * binary expression has been resolved),
4305 * then use the new enumv.
4306 */
4307 if (!enumv) enumv = tr_rules->enumv;
4308
4309 /*
4310 * We don't have an explicit output type. Try to
4311 * interpret the data os the enumv data type, OR
4312 * if all else fails, it's a string.
4313 */
4314 if (fr_type_is_null(dst_type)) {
4315 /*
4316 * Infer the cast from the enumv type.
4317 */
4318 if (enumv) {
4319 dst_type = enumv->type;
4320
4321 } else if (!tmpl_require_enum_prefix) {
4322 dst_type = FR_TYPE_STRING; /* Default to strings */
4323
4324 } else if (vpt->quote != T_BARE_WORD) {
4325 dst_type = FR_TYPE_STRING; /* quoted strings are strings */
4326
4327 } else if (strncmp(vpt->data.unescaped, "::", 2) != 0) {
4328 /*
4329 * The rest of the code should have errored out before this.
4330 */
4331 fr_strerror_printf("Failed resolving data '%s' - it is not an attribute name or a quoted string", vpt->data.unescaped);
4332 return -1;
4333
4334 } else {
4335 /*
4336 * It's a valid enum ::NAME which was added _after_ the dictionaries were
4337 * loaded. That's fine. fr_value_box_from_substr() will skip over the
4338 * "::", and parse the enum name.
4339 */
4340 }
4341 }
4342
4343 /*
4344 * tmpl_cast_in_place first resolves using
4345 * the enumv, _then_ casts using the type.
4346 */
4347 if (tmpl_cast_in_place(vpt, dst_type, enumv) < 0) return -1;
4348
4350 /*
4351 * Catch any other cases of unresolved things
4352 * we need to address. We put the assert here
4353 * so we don't end up running inappropriate
4354 * code for non-debug builds.
4355 */
4356 } else {
4357 fr_assert(0);
4358 }
4359
4361
4362 return ret;
4363}
4364
4365/** Reset the tmpl, leaving only the name in place
4366 *
4367 * After calling this function, the tmpl type will revert to TMPL_TYPE_DATA_UNRESOLVED
4368 * and only the name and quoting will be preserved.
4369 *
4370 * @param[in] vpt to reset.
4371 */
4373{
4374 tmpl_t tmp = {
4376 .name = vpt->name,
4377 .len = vpt->len,
4378 .quote = vpt->quote
4379 };
4380
4381 switch (vpt->type) {
4383 case TMPL_TYPE_MAX:
4384 fr_assert(0);
4385 break;
4386
4387 case TMPL_TYPE_NULL:
4390 break;
4391
4392 case TMPL_TYPE_DATA:
4393 fr_value_box_clear(&vpt->data.literal);
4394 break;
4395
4396 /*
4397 * These types contain dynamically allocated
4398 * attribute and request references.
4399 */
4400 case TMPL_TYPE_ATTR:
4402 tmpl_attr_list_talloc_free(tmpl_attr(vpt));
4403 tmpl_request_list_talloc_free(&vpt->data.attribute.rr);
4404 break;
4405
4406 /*
4407 * These all store an xlat expansion
4408 */
4409 case TMPL_TYPE_EXEC:
4410 case TMPL_TYPE_XLAT:
4415 TALLOC_FREE(vpt->data.xlat.ex);
4416 break;
4417
4418 case TMPL_TYPE_REGEX:
4419 talloc_free(vpt->data.reg.ex);
4420 break;
4421
4422 }
4423
4424 memcpy(vpt, &tmp, sizeof(*vpt));
4425
4427}
4428
4429/** Convert an attribute reference to an xlat expansion
4430 *
4431 * This is where a user attempts to use an attribute reference which is actually
4432 * a virtual attribute.
4433 *
4434 * @param[in] ctx to convert new tmpl in.
4435 * @param[in,out] vpt_p pointer to #tmpl_t of TMPL_TYPE_ATTR | TMPL_TYPE_ATTR_UNPARSED.
4436 */
4437int tmpl_attr_to_xlat(TALLOC_CTX *ctx, tmpl_t **vpt_p)
4438{
4439
4440 tmpl_t *vpt;
4441 tmpl_t *attr = *vpt_p;
4442
4443 /*
4444 * First alloc a new tmpl to hold the xlat expansion
4445 */
4446 vpt = tmpl_alloc(ctx, TMPL_TYPE_XLAT, attr->quote, attr->name, attr->len);
4447
4448 /*
4449 * ...then wrap the old tmpl_t in an xlat expansion
4450 * doing conversion to a virtual attribute if necessary.
4451 */
4452 if (xlat_from_tmpl_attr(vpt, &vpt->data.xlat.ex, vpt_p) < 0) {
4454 return -1;
4455 }
4456
4457 if (xlat_needs_resolving(vpt->data.xlat.ex)) UNRESOLVED_SET(&vpt->type);
4458
4459 *vpt_p = vpt;
4460
4461 return 0;
4462}
4463
4465{
4466 if (!ref) return;
4467
4468 switch (ref->type) {
4470 {
4471 ref->da = ref->ar_unknown = fr_dict_attr_unknown_afrom_da(vpt, ref->da);
4472 ref->ar_unknown->type = FR_TYPE_OCTETS;
4473 ref->is_raw = 1;
4474 ref->ar_unknown->flags.is_unknown = 1;
4476 }
4477 break;
4478 case TMPL_ATTR_TYPE_UNSPEC: /* noop */
4479 break;
4480
4482 ref->ar_unknown->type = FR_TYPE_OCTETS;
4483 ref->is_raw = 1;
4484 break;
4485
4487 ref->is_raw = true;
4488 break;
4489 }
4490
4492}
4493
4494/** Convert the leaf attribute of a tmpl to a unknown/raw type
4495 *
4496 */
4498{
4499 attr_to_raw(vpt, tmpl_attr_list_tail(tmpl_attr(vpt)));
4500}
4501
4502/** Add an unknown #fr_dict_attr_t specified by a #tmpl_t to the main dictionary
4503 *
4504 * @param vpt to add. ``tmpl_attr_tail_da`` pointer will be updated to point to the
4505 * #fr_dict_attr_t inserted into the dictionary.
4506 * @return
4507 * - 1 noop (did nothing) - Not possible to convert tmpl.
4508 * - 0 on success.
4509 * - -1 on failure.
4510 */
4512{
4513 tmpl_attr_t *ar = NULL, *next = NULL;
4514
4515 if (!vpt) return 1;
4516
4517 /*
4518 * Can't do this for expressions parsed at runtime
4519 */
4520 if (vpt->rules.at_runtime) return 1;
4521
4523
4525
4526 if (!tmpl_attr_tail_is_unknown(vpt)) return 1; /* Ensure at least the leaf is unknown */
4527
4528 while ((ar = tmpl_attr_list_next(tmpl_attr(vpt), ar))) {
4529 fr_dict_attr_t const *unknown, *known;
4530
4531 switch (ar->type) {
4532 case TMPL_ATTR_TYPE_NORMAL: /* Skip */
4534 continue;
4535
4536 case TMPL_ATTR_TYPE_UNRESOLVED: /* Shouldn't have been called */
4537 fr_strerror_const("Remaining attributes are unresolved");
4538 return -1;
4539
4541 break;
4542 }
4543
4544 unknown = ar->ar_unknown;
4545 known = fr_dict_attr_unknown_add(fr_dict_unconst(fr_dict_by_da(unknown)), unknown);
4546 if (!known) return -1;
4547
4548 /*
4549 * Fixup the parent of the next unknown
4550 * now it's known.
4551 */
4552 next = tmpl_attr_list_next(tmpl_attr(vpt), ar);
4553 if (next && (next->type == TMPL_ATTR_TYPE_UNKNOWN) &&
4554 (next->ar_da->parent == unknown)) {
4556 known) < 0) return -1;
4557 next->ar_parent = known;
4558 }
4559
4560 /*
4561 * Convert the ref to a normal type.
4562 * At runtime there should be no
4563 * "unknown" references as they should
4564 * have all been added to a
4565 * dictionary.
4566 */
4568
4569 /*
4570 * If the attribute is *NOT* raw then
4571 * swap the canonical unknown with the
4572 * one that was previously associated
4573 * with the tmpl.
4574 *
4575 * This establishes the unknown attribute
4576 * in the dictionary if it was really
4577 * unknown whilst not mucking up the
4578 * types for raw attributes.
4579 */
4580 if (!ar_is_raw(ar)) {
4581 fr_dict_attr_unknown_free(&ar->ar_da);
4582 ar->ar_da = known;
4583 } else if (!fr_cond_assert(!next)) {
4584 fr_strerror_const("Only the leaf may be raw");
4585 return -1;
4586 }
4587 }
4588
4589 return 0;
4590}
4591
4592/** Add an unresolved #fr_dict_attr_t specified by a #tmpl_t to the main dictionary
4593 *
4594 * @note fr_dict_attr_add will not return an error if the attribute already exists
4595 * meaning that multiple #tmpl_t specifying the same attribute can be
4596 * passed to this function to be fixed up, so long as the type and flags
4597 * are identical.
4598 *
4599 * @param[in] dict_def Default dictionary to use if none is
4600 * specified by the tmpl_attr_tail_unresolved.
4601 * @param[in] vpt specifying unresolved attribute to add.
4602 * ``tmpl_attr_tail_da`` pointer will be updated to
4603 * point to the #fr_dict_attr_t inserted
4604 * into the dictionary. Lists and requests
4605 * will be preserved.
4606 * @param[in] type to define unresolved attribute as.
4607 * @param[in] flags to define unresolved attribute with.
4608 * @return
4609 * - 1 noop (did nothing) - Not possible to convert tmpl.
4610 * - 0 on success.
4611 * - -1 on failure.
4612 */
4614 fr_type_t type, fr_dict_attr_flags_t const *flags)
4615{
4616 fr_dict_attr_t const *da;
4617 fr_dict_attr_flags_t our_flags = *flags;
4618
4619 our_flags.name_only = true;
4620
4621 if (!vpt) return -1;
4622
4624
4625 if (!tmpl_is_attr_unresolved(vpt)) return 1;
4626
4627 if (fr_dict_attr_add(dict_def,
4629 return -1;
4630 }
4632 if (!da) return -1;
4633
4634 if (type != da->type) {
4635 fr_strerror_printf("Attribute %s of type %s already defined with type %s",
4636 da->name, fr_type_to_str(type),
4637 fr_type_to_str(da->type));
4638 return -1;
4639 }
4640
4641 if (memcmp(flags, &da->flags, sizeof(*flags)) != 0) {
4642 fr_strerror_printf("Attribute %s already defined with different flags", da->name);
4643 return -1;
4644 }
4645
4646 tmpl_attr_set_da(vpt, da);
4647 vpt->type = TMPL_TYPE_ATTR;
4648
4649 return 0;
4650}
4651
4652#ifdef HAVE_REGEX
4653/** Convert a TMPL_TYPE_REGEX_UNCOMPILED into a TMPL_TYPE_REGEX
4654 *
4655 * Other regex types become noops.
4656 */
4657ssize_t tmpl_regex_compile(tmpl_t *vpt, bool subcaptures)
4658{
4659 ssize_t slen;
4660 char *unescaped = vpt->data.unescaped;
4661
4662 if (tmpl_is_regex_xlat(vpt) || tmpl_is_regex(vpt)) return 0; /* Don't need compiling */
4663
4665
4666 slen = regex_compile(vpt, &vpt->data.reg.ex,
4667 unescaped, talloc_array_length(unescaped) - 1,
4668 &vpt->data.reg_flags, subcaptures, vpt->rules.at_runtime);
4669 if (slen <= 0) return vpt->quote != T_BARE_WORD ? slen - 1 : slen; /* Account for the quoting */
4670
4671 vpt->type = TMPL_TYPE_REGEX;
4672 vpt->data.reg.src = unescaped; /* Keep this around for debugging and copying */
4673 vpt->data.reg.subcaptures = subcaptures;
4674
4676
4677 return slen;
4678}
4679#endif
4680/** @} */
4681
4682/** @name Print the contents of a #tmpl_t
4683 * @{
4684 */
4686{
4687 fr_sbuff_t our_out = FR_SBUFF(out);
4688 tmpl_request_t *rr = tmpl_request_list_head(rql);
4689
4690 /*
4691 * Print request references
4692 */
4693 while (rr) {
4694 FR_SBUFF_IN_TABLE_STR_RETURN(&our_out, tmpl_request_ref_table, rr->request, "<INVALID>");
4695 rr = tmpl_request_list_next(rql, rr);
4696 if (rr) FR_SBUFF_IN_CHAR_RETURN(&our_out, '.');
4697 }
4698
4699 FR_SBUFF_SET_RETURN(out, &our_out);
4700}
4701
4702/** Print an attribute or list #tmpl_t to a string
4703 *
4704 * This function is the direct counterpart to #tmpl_afrom_attr_substr.
4705 *
4706 * @param[in] out Where to write the presentation format #tmpl_t string.
4707 * @param[in] vpt to print.
4708 * @param[in] ar_prefix Whether to print the '&' at the beginning of attribute
4709 * references.
4710 * - TMPL_ATTR_REF_PREFIX_YES - always print.
4711 * - TMPL_ATTR_REF_PREFIX_NO - never print.
4712 * - TMPL_ATTR_REF_PREFIX_AUTO - print if the original tmpl
4713 * was prefixed.
4714 * @return
4715 * - >0 the number of bytes written to the out buffer.
4716 * - 0 invalid argument.
4717 * - <0 the number of bytes we would have needed to complete the print.
4718 */
4720{
4721 tmpl_attr_t *ar = NULL;
4723 fr_sbuff_t our_out = FR_SBUFF(out);
4724 fr_slen_t slen;
4725
4727
4728 /*
4729 * Only print things we can print...
4730 */
4731 switch (vpt->type) {
4733 case TMPL_TYPE_ATTR:
4734 break;
4735
4736 default:
4737 fr_assert(0);
4738 return 0;
4739 }
4740
4741 /*
4742 * Suppress the prefix on new syntax.
4743 */
4746 }
4747
4748 /*
4749 * Handle printing the request reference
4750 * prefix.
4751 */
4753 ((ar_prefix == TMPL_ATTR_REF_PREFIX_AUTO) && vpt->data.attribute.ref_prefix)) {
4754 FR_SBUFF_IN_CHAR_RETURN(&our_out, '&');
4755 }
4756
4757 /*
4758 * Print request references
4759 */
4760 slen = tmpl_request_ref_list_print(&our_out, &vpt->data.attribute.rr);
4761 if (slen > 0) FR_SBUFF_IN_CHAR_RETURN(&our_out, '.');
4762 if (slen < 0) return slen;
4763
4764 /*
4765 *
4766 * If the leaf attribute is unknown and raw we
4767 * add the raw. prefix.
4768 *
4769 * If the leaf attribute is unknown and not raw
4770 * we add the .unknown prefix.
4771 *
4772 */
4774
4775 /*
4776 * Print attribute identifiers
4777 */
4778 ar = NULL;
4779 while ((ar = tmpl_attr_list_next(tmpl_attr(vpt), ar))) {
4780 switch(ar->type) {
4782 break;
4783
4786 {
4787 int i, depth = 0;
4788
4789 fr_assert(ar->ar_parent); /* All normal and unknown attributes must have parents */
4790
4791 fr_proto_da_stack_build_partial(&stack, ar->ar_parent, ar->ar_da);
4792
4793 /*
4794 * First component in the list has everything built
4795 */
4796 if (ar == tmpl_attr_list_head(tmpl_attr(vpt))) {
4797 depth = ar->ar_parent->depth - 1; /* Adjust for array index */
4798 /*
4799 * Everything else skips the first component
4800 */
4801 } else {
4802 depth = ar->ar_parent->depth;
4803 }
4804
4805 /*
4806 * Root attributes will be skipped by the build
4807 * function, so da[0] contains the attribute
4808 * we're looking for.
4809 */
4810 if (depth < 0) depth = 0;
4811
4812 /*
4813 * Print from our parent depth to the AR we're processing
4814 *
4815 * For refs we skip the attribute pointed to be the ref
4816 * and just print its children.
4817 *
4818 * In addition skip printing "request." in most cases.
4819 */
4820 if ((stack.da[depth] == request_attr_request) && tmpl_attr_list_next(tmpl_attr(vpt), ar) &&
4821 (ar->filter.type == TMPL_ATTR_FILTER_TYPE_NONE)) continue;
4822
4823 for (i = depth; (unsigned int)i < ar->ar_da->depth; i++) {
4824 FR_SBUFF_IN_STRCPY_RETURN(&our_out, stack.da[i]->name);
4825
4826 /*
4827 * Print intermediary separators
4828 * if necessary.
4829 */
4830 if (((unsigned int)i + 1) < ar->ar_da->depth) FR_SBUFF_IN_CHAR_RETURN(&our_out, '.');
4831 }
4832 }
4833 break;
4834
4835 /*
4836 * For unresolved attribute we print the raw identifier we
4837 * got when parsing the tmpl.
4838 */
4840 {
4841 unsigned int i, depth;
4842
4843 /*
4844 * This is the first unresolved component in a potential
4845 * chain of unresolved components. Print the path up to
4846 * the last known parent.
4847 */
4848 if (ar->ar_parent && !ar->ar_parent->flags.is_root) {
4849 fr_proto_da_stack_build_partial(&stack, ar->ar_parent, ar->ar_parent);
4850 if (ar->ar_parent->flags.is_root) {
4851 depth = 0;
4852 } else {
4853 depth = ar->ar_parent->depth - 1;
4854 }
4855
4856 for (i = depth; i < ar->ar_parent->depth; i++) {
4857 FR_SBUFF_IN_STRCPY_RETURN(&our_out, stack.da[i]->name);
4858 FR_SBUFF_IN_CHAR_RETURN(&our_out, '.');
4859 }
4860 }
4861 /*
4862 * Then print the unresolved component
4863 */
4864 FR_SBUFF_IN_BSTRCPY_BUFFER_RETURN(&our_out, ar->ar_unresolved);
4865 break;
4866 }
4867 }
4868
4869 if (ar_filter_is_none(ar)) {
4870 /* do nothing */
4871
4872 } else if (ar_filter_is_num(ar)) {
4873 switch (ar->ar_num) {
4874 case NUM_UNSPEC:
4875 break;
4876
4877 case NUM_ALL:
4878 FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "[*]");
4879 break;
4880
4881 case NUM_COUNT:
4882 FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "[#]");
4883 break;
4884
4885 case NUM_LAST:
4886 FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "[n]");
4887 break;
4888
4889 default:
4890 FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "[%i]", ar->ar_num);
4891 break;
4892 }
4893
4894 } else if (ar_filter_is_cond(ar)) {
4895 FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "[");
4896 (void) xlat_print(&our_out, ar->ar_cond, NULL);
4897 FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "]");
4898
4899 } else {
4900 fr_assert(0);
4901 }
4902
4903 if (tmpl_attr_list_next(tmpl_attr(vpt), ar)) FR_SBUFF_IN_CHAR_RETURN(&our_out, '.');
4904 }
4905 FR_SBUFF_SET_RETURN(out, &our_out);
4906}
4907
4908/** Print a #tmpl_t to a string
4909 *
4910 * This function should primarily be used for regenerating vpt->name when the contents
4911 * of the #tmpl_t is changed programmatically, or when the #tmpl_t is being serialized
4912 * in some non-standard way, i.e. as a value for a field in a database.
4913 *
4914 * This function is the direct counterpart to #tmpl_afrom_substr.
4915 *
4916 * @note Does not print flags for regular expressions, as the quoting char is needed
4917 * to separate the elements of the expression.
4918 * Call regex_flags_print to write the flags values to the output buffer.
4919 *
4920 * @param[out] out Where to write the presentation format #tmpl_t string.
4921 * @param[in] vpt to print.
4922 * @param[in] ar_prefix Whether to print the '&' at the beginning of attribute
4923 * references.
4924 * - TMPL_ATTR_REF_PREFIX_YES - always print.
4925 * - TMPL_ATTR_REF_PREFIX_NO - never print.
4926 * - TMPL_ATTR_REF_PREFIX_AUTO - print if the original tmpl
4927 * was prefixed.
4928 * @param[in] e_rules Escaping rules used to print strings.
4929 * @return
4930 * - >0 the number of bytes written to the out buffer.
4931 * - 0 invalid argument.
4932 * - <0 the number of bytes we would have needed to complete the print.
4933 */
4936{
4937 fr_sbuff_t our_out = FR_SBUFF(out);
4938
4940
4941 /*
4942 * Suppress the prefix on new syntax.
4943 */
4946 }
4947
4948 switch (vpt->type) {
4950 case TMPL_TYPE_ATTR:
4952 break;
4953
4954 case TMPL_TYPE_DATA:
4955 FR_SBUFF_RETURN(fr_value_box_print, &our_out, tmpl_value(vpt), e_rules);
4956 break;
4957
4958 case TMPL_TYPE_REGEX:
4959 FR_SBUFF_IN_BSTRNCPY_RETURN(&our_out, vpt->name, vpt->len); /* Fixme - double escapes */
4960 break;
4961
4963 FR_SBUFF_IN_ESCAPE_BUFFER_RETURN(&our_out, vpt->data.unescaped, e_rules);
4964 break;
4965
4967 case TMPL_TYPE_NULL:
4968 case TMPL_TYPE_MAX:
4969 fr_sbuff_terminate(out);
4970 break;
4971
4972 /*
4973 * The remaining types will either
4974 * be xlat expansions, or need
4975 * resolving, in which case the
4976 * unescaped string is available
4977 * in vpt->unescaped.
4978 */
4979 default:
4980 if (tmpl_contains_xlat(vpt)) {
4981 FR_SBUFF_RETURN(xlat_print, &our_out, tmpl_xlat(vpt), e_rules);
4982 break;
4983 }
4984
4986 FR_SBUFF_IN_ESCAPE_BUFFER_RETURN(&our_out, vpt->data.unescaped, e_rules);
4987 break;
4988 }
4989
4990 fr_assert_fail("Can't print invalid tmpl type %s", tmpl_type_to_str(vpt->type));
4991
4992 /*
4993 * Ensure we do something sane for non-debug builds
4994 */
4995 fr_sbuff_terminate(out);
4996 return 0;
4997 }
4998
4999 FR_SBUFF_SET_RETURN(out, &our_out);
5000}
5001
5002/** Print a #tmpl_t to a string with quotes
5003 *
5004 * This function should be used when the tmpl is embedded in some other construct
5005 * in the server's configuration.
5006 *
5007 * It adds standard quoting around tmpl's used as operands in expressions and applies
5008 * the correct escaping rules.
5009 *
5010 * @param[out] out Where to write the presentation format #tmpl_t string.
5011 * @param[in] vpt to print.
5012 * @param[in] ar_prefix Whether to print the '&' at the beginning of attribute
5013 * references.
5014 * - TMPL_ATTR_REF_PREFIX_YES - always print.
5015 * - TMPL_ATTR_REF_PREFIX_NO - never print.
5016 * - TMPL_ATTR_REF_PREFIX_AUTO - print if the original tmpl
5017 * was prefixed.
5018 * @return
5019 * - >0 the number of bytes written to the out buffer.
5020 * - 0 invalid argument.
5021 * - <0 the number of bytes we would have needed to complete the print.
5022 */
5024{
5025 fr_sbuff_t our_out = FR_SBUFF(out);
5026
5027 char quote = fr_token_quote[vpt->quote];
5028
5029 if (quote != '\0') FR_SBUFF_IN_CHAR_RETURN(&our_out, quote);
5030 FR_SBUFF_RETURN(tmpl_print, &our_out, vpt,
5032 if (quote != '\0') FR_SBUFF_IN_CHAR_RETURN(&our_out, quote);
5033
5034 /*
5035 * Optionally print the flags
5036 */
5037 if (vpt->type & TMPL_FLAG_REGEX) FR_SBUFF_RETURN(regex_flags_print, &our_out, tmpl_regex_flags(vpt));
5038
5039 FR_SBUFF_SET_RETURN(out, &our_out);
5040}
5041/** @} */
5042
5043
5044#ifdef WITH_VERIFY_PTR
5045/** Used to check whether areas of a tmpl_t are zeroed out
5046 *
5047 * @param ptr Offset to begin checking at.
5048 * @param len How many bytes to check.
5049 * @return
5050 * - Pointer to the first non-zero byte.
5051 * - NULL if all bytes were zero.
5052 */
5053static uint8_t const *is_zeroed(uint8_t const *ptr, size_t len)
5054{
5055 size_t i;
5056
5057 for (i = 0; i < len; i++) {
5058 if (ptr[i] != 0x00) return ptr + i;
5059 }
5060
5061 return NULL;
5062}
5063
5064/** Verify that unused regions of the struct are zeroed out
5065 *
5066 */
5067#define CHECK_ZEROED(_vpt, _field) is_zeroed(((uint8_t const *)&(_vpt)->data) + sizeof((_vpt)->data._field), sizeof((_vpt)->data) - sizeof((_vpt)->data._field))
5068
5069
5070/** Print hex data
5071 *
5072 */
5073#define PRINT_NON_ZEROED(_vpt, _field, _nz_ptr) \
5074do { \
5075 DEBUG("Expected live portion %p-%p (0-%zu)", \
5076 _vpt, \
5077 (uint8_t const *)&(_vpt)->data + sizeof((_vpt)->data._field), \
5078 sizeof((_vpt)->data._field)); \
5079 DEBUG("Expected zero portion %p-%p (%zu-%zu)", \
5080 (uint8_t const *)&(_vpt)->data + sizeof((_vpt)->data._field), \
5081 (uint8_t const *)&(_vpt)->data + sizeof((_vpt)->data), \
5082 sizeof((_vpt)->data._field), sizeof((_vpt)->data)); \
5083 HEX_MARKER1((uint8_t const *)&vpt->data, sizeof(vpt->data), nz - (uint8_t const *)&vpt->data, "non-zero memory", ""); \
5084} while (0)
5085
5086
5087/** Verify the attribute reference in a tmpl_t make sense
5088 *
5089 * @note If the attribute reference is is invalid, causes the server to exit.
5090 *
5091 * @param file obtained with __FILE__.
5092 * @param line obtained with __LINE__.
5093 * @param vpt to check.
5094 */
5095void tmpl_attr_verify(char const *file, int line, tmpl_t const *vpt)
5096{
5097 tmpl_attr_t *ar = NULL;
5098 tmpl_attr_t *slow = NULL, *fast = NULL;
5099 tmpl_attr_t *seen_unknown = NULL;
5100 tmpl_attr_t *seen_unresolved = NULL;
5101
5103
5105
5106 /*
5107 * Loop detection
5108 */
5109 while ((slow = tmpl_attr_list_next(tmpl_attr(vpt), slow)) &&
5110 (fast = tmpl_attr_list_next(tmpl_attr(vpt), fast))) {
5111
5112 /*
5113 * Advances twice as fast as slow...
5114 */
5115 fast = tmpl_attr_list_next(tmpl_attr(vpt), fast);
5116 fr_fatal_assert_msg(fast != slow,
5117 "CONSISTENCY CHECK FAILED %s[%u]: Looping reference list found. "
5118 "Fast pointer hit slow pointer at \"%s\"",
5119 file, line,
5120 slow->type == TMPL_ATTR_TYPE_UNRESOLVED ? slow->ar_unresolved :
5121 slow->da ? slow->da->name : "(null-attr)");
5122 }
5123
5124 /*
5125 * Lineage type check
5126 *
5127 * Known attribute cannot come after unresolved or unknown attributes
5128 * Unknown attributes cannot come after unresolved attributes
5129 */
5130 if (!tmpl_is_list(vpt)) while ((ar = tmpl_attr_list_next(tmpl_attr(vpt), ar))) {
5131 switch (ar->type) {
5133 if (seen_unknown) {
5135 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: "
5136 "TMPL_TYPE_ATTR known attribute \"%s\" "
5137 "occurred after unknown attribute %s "
5138 "in attr ref list",
5139 file, line,
5140 ar->da->name,
5141 ar->unknown.da->name);
5142 }
5143 if (seen_unresolved) {
5145 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: "
5146 "TMPL_TYPE_ATTR known attribute \"%s\" "
5147 "occurred after unresolved attribute \"%s\""
5148 "in attr ref list",
5149 file, line,
5150 ar->da->name,
5151 ar->ar_unresolved);
5152 }
5153 fr_fatal_assert_msg(ar->ar_parent,
5154 "CONSISTENCY CHECK FAILED %s[%u]: attr ref missing parent",
5155 file, line);
5156 break;
5157
5159 if (seen_unknown) {
5161 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: "
5162 "TMPL_TYPE_ATTR unspecified attribute "
5163 "occurred after unknown attribute %s "
5164 "in attr ref list",
5165 file, line,
5166 ar->unknown.da->name);
5167 }
5168 if (seen_unresolved) {
5170 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: "
5171 "TMPL_TYPE_ATTR unspecified attribute "
5172 "occurred after unresolved attribute \"%s\""
5173 "in attr ref list",
5174 file, line,
5175 ar->ar_unresolved);
5176 }
5177 break;
5178
5180 seen_unresolved = ar;
5181 fr_fatal_assert_msg(ar->ar_unresolved_namespace,
5182 "CONSISTENCY CHECK FAILED %s[%u]: unresolved attr ref missing namespace",
5183 file, line);
5184 break;
5185
5187 seen_unknown = ar;
5188 if (seen_unresolved) {
5190 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: "
5191 "TMPL_TYPE_ATTR unknown attribute \"%s\" "
5192 "occurred after unresolved attribute %s "
5193 "in attr ref list",
5194 file, line, ar->da->name,
5195 ar->ar_unresolved);
5196 }
5197 break;
5198 }
5199 }
5200}
5201
5202/** Verify fields of a tmpl_t make sense
5203 *
5204 * @note If the #tmpl_t is invalid, causes the server to exit.
5205 *
5206 * @param file obtained with __FILE__.
5207 * @param line obtained with __LINE__.
5208 * @param vpt to check.
5209 */
5210void tmpl_verify(char const *file, int line, tmpl_t const *vpt)
5211{
5212 uint8_t const *nz;
5213
5214 fr_assert(vpt);
5215
5217 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: tmpl_t type was "
5218 "TMPL_TYPE_UNINITIALISED (uninitialised)", file, line);
5219 }
5220
5221 if (vpt->type >= TMPL_TYPE_MAX) {
5222 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: tmpl_t type was %i "
5223 "(outside range of tmpl_type_table)", file, line, vpt->type);
5224 }
5225
5226 if (!vpt->name && (vpt->quote != T_INVALID)) {
5227 char quote = vpt->quote >= T_TOKEN_LAST ? '?' : fr_token_quote[vpt->quote];
5228
5229 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: Quote type '%c' (%i) was set for NULL name",
5230 file, line, quote, vpt->quote);
5231 }
5232
5233 if (vpt->name && (vpt->quote == T_INVALID)) {
5234 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: No quoting type was set for name \"%.*s\"",
5235 file, line, (int)vpt->len, vpt->name);
5236 }
5237
5238 /*
5239 * Do a memcmp of the bytes after where the space allocated for
5240 * the union member should have ended and the end of the union.
5241 * These should always be zero if the union has been initialised
5242 * properly.
5243 *
5244 * If they're still all zero, do TMPL_TYPE specific checks.
5245 */
5246 switch (vpt->type) {
5247 case TMPL_TYPE_NULL:
5248 if ((nz = is_zeroed((uint8_t const *)&vpt->data, sizeof(vpt->data)))) {
5249 HEX_MARKER1((uint8_t const *)&vpt->data, sizeof(vpt->data),
5250 nz - (uint8_t const *)&vpt->data, "non-zero memory", "");
5251 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_NULL "
5252 "has non-zero bytes in its data union", file, line);
5253 }
5254 break;
5255
5257 if (!vpt->data.unescaped) {
5258 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_DATA_UNRESOLVED "
5259 "unescaped field is NULL", file, line);
5260 }
5261 break;
5262
5264 if (!vpt->data.xlat.ex) {
5265 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_XLAT "
5266 "has a NULL xlat.ex field", file, line);
5267
5268 }
5269
5270 if (!xlat_needs_resolving(vpt->data.xlat.ex)) {
5271 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_XLAT_UNRESOLVED "
5272 "does not have 'needs resolving' flag set", file, line);
5273 }
5274 break;
5275
5276 case TMPL_TYPE_XLAT:
5277 if (!vpt->data.xlat.ex) {
5278 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_XLAT "
5279 "has a NULL xlat.ex field", file, line);
5280
5281 }
5282 break;
5283
5284/* @todo When regexes get converted to xlat the flags field of the regex union is used
5285 case TMPL_TYPE_XLAT_UNRESOLVED:
5286 if (is_zeroed((uint8_t const *)&vpt->data, sizeof(vpt->data))) {
5287 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_XLAT_UNRESOLVED "
5288 "has non-zero bytes in its data union", file, line);
5289 }
5290 break;
5291
5292 case TMPL_TYPE_XLAT:
5293 if (CHECK_ZEROED(vpt, xlat)) {
5294 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_XLAT "
5295 "has non-zero bytes after the data.xlat pointer in the union", file, line);
5296 }
5297 break;
5298*/
5299
5300 case TMPL_TYPE_EXEC:
5302 /* tmpl_xlat(vpt) can be initialized */
5303 break;
5304
5306 if ((tmpl_attr_list_num_elements(tmpl_attr(vpt)) > 0) &&
5307 ((tmpl_attr_t *)tmpl_attr_list_tail(tmpl_attr(vpt)))->da) {
5308#ifndef NDEBUG
5310#endif
5311 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_ATTR_UNRESOLVED contains %u "
5312 "references", file, line, tmpl_attr_list_num_elements(tmpl_attr(vpt)));
5313 }
5314 break;
5315
5316 case TMPL_TYPE_ATTR:
5317 if ((nz = CHECK_ZEROED(vpt, attribute))) {
5318 PRINT_NON_ZEROED(vpt, attribute, nz);
5319 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_ATTR "
5320 "has non-zero bytes after the data.attribute struct in the union",
5321 file, line);
5322 }
5323
5325 fr_assert(vpt->rules.cast == FR_TYPE_NULL);
5326 break;
5327 }
5328
5331 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_ATTR "
5332 "da is marked as unknown, but address is not equal to the template's "
5333 "unknown da pointer", file, line);
5334 }
5335 /*
5336 * Raw attributes may not have been added to the dictionary yet
5337 */
5338 } else {
5339 fr_dict_attr_t const *da;
5340 fr_dict_t const *dict;
5341
5342 /*
5343 * Attribute may be present with multiple names
5344 */
5346 if (!dict) {
5347 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_ATTR "
5348 "attribute \"%s\" (%s) not rooted in a dictionary",
5351 }
5352
5353 da = tmpl_attr_tail_da(vpt);
5354 if (!tmpl_attr_tail_is_raw(vpt) && (da != tmpl_attr_tail_da(vpt))) {
5355 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_ATTR "
5356 "dictionary pointer %p \"%s\" (%s) "
5357 "and global dictionary pointer %p \"%s\" (%s) differ",
5358 file, line,
5361 da, da->name,
5362 fr_type_to_str(da->type));
5363 }
5364
5365 tmpl_attr_verify(file, line, vpt);
5366 }
5367 break;
5368
5369 case TMPL_TYPE_DATA:
5370 if ((nz = CHECK_ZEROED(vpt, literal))) {
5371 PRINT_NON_ZEROED(vpt, literal, nz);
5372 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_DATA "
5373 "has non-zero bytes after the data.literal struct in the union",
5374 file, line);
5375 }
5376
5378 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_DATA type was "
5379 "FR_TYPE_NULL (uninitialised)", file, line);
5380 }
5381
5383 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_DATA type was "
5384 "%i (outside the range of fr_type_ts)", file, line, tmpl_value_type(vpt));
5385 }
5386 /*
5387 * Unlike fr_pair_ts we can't guarantee that fr_pair_t_TMPL buffers will
5388 * be talloced. They may be allocated on the stack or in global variables.
5389 */
5390 switch (tmpl_value_type(vpt)) {
5391 case FR_TYPE_STRING:
5393 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_DATA char buffer not \\0 "
5394 "terminated", file, line);
5395 }
5396 break;
5397
5398 case FR_TYPE_STRUCTURAL:
5399 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_DATA is of type TLV",
5400 file, line);
5401
5402 default:
5403 break;
5404 }
5405
5406 break;
5407
5411#ifndef HAVE_REGEX
5412 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_REGEX_XLAT_UNRESOLVED - No regex support",
5413 file, line);
5414#endif
5415 break;
5416
5417 case TMPL_TYPE_REGEX:
5418#ifdef HAVE_REGEX
5419 if (tmpl_regex(vpt) == NULL) {
5420 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_REGEX "
5421 "reg.ex field was NULL", file, line);
5422 }
5423#else
5424 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_REGEX - No regex support",
5425 file, line);
5426#endif
5427 break;
5428
5430 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_UNINITIALISED", file, line);
5431
5432 case TMPL_TYPE_MAX:
5433 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_MAX", file, line);
5434 }
5435}
5436#endif
5437
5438#define return_P(_x) fr_strerror_const(_x);goto return_p
5439
5440/** Preparse a string in preparation for passing it to tmpl_afrom_substr()
5441 *
5442 * Note that the input string is not modified, which means that the
5443 * tmpl_afrom_substr() function MUST un-escape it.
5444 *
5445 * The caller should pass 'out' and 'outlen' to tmpl_afrom_substr()
5446 * as 'in' and 'inlen'. The caller should also pass 'type'.
5447 * The caller should also pass do_unescape=true.
5448 *
5449 * @param[out] out start of the string to parse
5450 * @param[out] outlen length of the string to parse
5451 * @param in where we start looking for the string
5452 * @param inlen length of the input string
5453 * @param[out] type token type of the string.
5454 * @return
5455 * - > 0, amount of parsed string to skip, to get to the next token
5456 * - <=0, -offset in 'start' where the parse error was located
5457 */
5458ssize_t tmpl_preparse(char const **out, size_t *outlen, char const *in, size_t inlen,
5460{
5461 char const *p = in, *end = in + inlen;
5462 char quote;
5463 char close;
5464 int depth;
5465 bool triple;
5466
5467 *type = T_INVALID;
5468
5469 while (isspace((uint8_t) *p) && (p < end)) p++;
5470 if (p >= end) return p - in;
5471
5472 switch (*p) {
5473 /*
5474 * Allow bare xlat's
5475 */
5476 case '%':
5477 if (p[1] != '{') {
5478 char const *q;
5479
5480 q = p + 1;
5481
5482 /*
5483 * Function syntax: %foo(...)
5484 */
5485 while ((q < end) && (isalnum((int) *q) || (*q == '.') || (*q == '_') || (*q == '-'))) {
5486 q++;
5487 }
5488
5489 if (*q != '(') {
5490 p++;
5491 fr_strerror_const("Invalid character after '%'");
5492 return_p:
5493 return -(p - in);
5494 }
5495
5496 /*
5497 * Return the whole %foo(...) string.
5498 */
5499 *out = p;
5500 if (*type == T_INVALID) *type = T_BARE_WORD;
5501 close = ')';
5502
5503 p = q + 1;
5504 depth = 1;
5505 goto loop;
5506 }
5507
5508 /*
5509 * For now, %{...} is treated as a double-quoted
5510 * string. Once we clean other things up, the
5511 * xlats will be treated as strongly typed values
5512 * / lists on their own.
5513 */
5514 if (*type == T_INVALID) *type = T_BARE_WORD;
5515 depth = 0;
5516 close = '}';
5517
5518 /*
5519 * Xlat's are quoted by %{...} / %(...) nesting, not by
5520 * escapes, so we need to do special escaping.
5521 */
5522 *out = p;
5523 loop:
5524 while (*p) {
5525 /*
5526 * End of expansion. Return the entire
5527 * expansion, including the enclosing %{}
5528 * characters.
5529 */
5530 if ((*p == '}') || (*p == ')')) {
5531 bool match = (*p == close);
5532
5533 p++;
5534 depth--;
5535
5536 if (depth == 0) {
5537 if (!match) break;
5538
5539 *outlen = p - (*out);
5540 return p - in;
5541 }
5542 continue;
5543 }
5544
5545 if (*p == '\\') {
5546 p++;
5547 if (!p[1]) {
5548 return_P("End of string after escape");
5549 }
5550
5551 p++;
5552 continue;
5553 }
5554
5555 if ((p[0] == '%') && ((p[1] == '{') || (p[1] == '('))) {
5556 if (!p[2]) {
5557 return_P("End of string after expansion");
5558 }
5559
5560 p += 2;
5561 depth++;
5562 continue;
5563 }
5564
5565 /*
5566 * Allow (...) and {...}
5567 */
5568 if ((*p == '{') || (*p == '(')) {
5569 p++;
5570 depth++;
5571 continue;
5572 }
5573
5574 p++;
5575 }
5576
5577 /*
5578 * End of input without end of string.
5579 * Point the error to the start of the string.
5580 */
5581 p = *out;
5582 return_P("Unterminated expansion");
5583
5584 case '/':
5585 goto bare_word;
5586
5587 case '\'':
5588 quote = *(p++);
5590 goto skip_string;
5591
5592 case '`':
5593 quote = *(p++);
5595 goto skip_string;
5596
5597 case '"':
5598 quote = *(p++);
5600
5601 /*
5602 * We're not trying to do a *correct* parsing of
5603 * every string here. We're trying to do a
5604 * simple parse that isn't wrong. We therefore
5605 * accept most anything that's vaguely well
5606 * formed, and rely on the next stage to do a
5607 * more rigorous check.
5608 */
5609 skip_string:
5610 if ((inlen > 3) && (p[0] == quote) && (p[1] == quote)) {
5611 triple = true;
5612 p += 2;
5613 } else {
5614 triple = false;
5615 }
5616 *out = p;
5617
5618 while (*p) {
5619 if (p >= end) goto unterminated;
5620
5621 /*
5622 * End of string. Tell the caller the
5623 * length of the data inside of the
5624 * string, and return the number of
5625 * characters to skip.
5626 */
5627 if (*p == quote) {
5628 if (!triple) {
5629 *outlen = p - (*out);
5630 p++;
5631 return p - in;
5632
5633 }
5634
5635 if (((end - p) >= 3) && (p[1] == quote) && (p[2] == quote)) {
5636 *outlen = p - (*out);
5637 p += 3;
5638 return p - in;
5639 }
5640
5641 p++;
5642 continue;
5643 }
5644
5645 if (*p == '\\') {
5646 p++;
5647 if (!p[1]) {
5648 return_P("End of string after escape");
5649 }
5650 }
5651 p++;
5652 }
5653
5654 /*
5655 * End of input without end of string.
5656 * Point the error to the start of the string.
5657 */
5658 unterminated:
5659 p = *out;
5660 return_P("Unterminated string");
5661
5662 case '&':
5663 *out = p; /* the output string starts with '&' */
5664 p++;
5665 quote = '[';
5666 goto skip_word;
5667
5668 default:
5669 bare_word:
5670 *out = p;
5671
5673 quote = '['; /* foo[1] is OK */
5674 } else {
5675 quote = '\0';
5676 }
5677
5678 skip_word:
5679 *type = T_BARE_WORD;
5680 depth = 0;
5681
5682 /*
5683 * Allow *most* things. But stop on spaces and special characters.
5684 */
5685 while (*p) {
5686 if (isspace((uint8_t) *p)) {
5687 break;
5688 }
5689
5690 if (*p == '$') {
5691 if (p[1] == '{') {
5692 p += 2;
5693 depth++;
5694 continue;
5695
5696 } else if ((p[1] == 'E') &&
5697 (p[2] == 'N') &&
5698 (p[3] == 'V') &&
5699 (p[4] == '{')) {
5700 p += 5;
5701 depth++;
5702 continue;
5703
5704 } else {
5705 /*
5706 * Bare '$' is wrong...
5707 */
5708 break;
5709 }
5710 }
5711
5712 if (*p == '%') {
5713 if (p[1] == '{') {
5714 p += 2;
5715 depth++;
5716 continue;
5717 }
5718
5719 p++;
5720 continue;
5721 }
5722
5723 /*
5724 * If we're inside of a ${...} expansion,
5725 * then allow everything until the
5726 * closing '}'. This means that we can
5727 * do ${foo[bar].baz}, among other
5728 * thingds.
5729 */
5730 if (depth > 0) {
5731 if (*p == '}') {
5732 depth--;
5733 }
5734
5735 p++;
5736 continue;
5737 }
5738
5739 /*
5740 * '-' is special. We allow it for
5741 * attribute names, BUT it's a
5742 * terminating token if the NEXT
5743 * character is '='.
5744 *
5745 * We have the same criteria for IPv6
5746 * addresses and tagged attributes. ':'
5747 * is allowed, but ':=' is a breaking
5748 * token.
5749 */
5750 if ((*p == '-') || (*p == ':')) {
5751 if (p[1] == '=') break;
5752 p++;
5753 continue;
5754 }
5755
5756 /*
5757 * Allowed in attribute names, and/or
5758 * host names and IP addresses, and IPv6 addresses.
5759 */
5760 if ((*p == '.') || (*p == '/') || (*p == '_') || (*p == '*') ||
5761 (*p == ']') || (*p == '@')) {
5762 p++;
5763 continue;
5764 }
5765
5766 /*
5767 * [...] is an IPv6 address.
5768 */
5769 if ((p == in) && (*p == '[')) {
5770 p++;
5771 continue;
5772 }
5773
5774 /*
5775 * Allow letters and numbers
5776 */
5777 if (((*p >= 'a') && (*p <= 'z')) ||
5778 ((*p >= 'A') && (*p <= 'Z')) ||
5779 ((*p >= '0') && (*p <= '9'))) {
5780 p++;
5781 continue;
5782 }
5783
5784 /*
5785 * Allow UTF-8 sequences.
5786 */
5787 if (*(uint8_t const *)p > 0x80) {
5788 p++;
5789 continue;
5790 }
5791
5792 /*
5793 * If it's an attribute reference, allow
5794 * a few more things inside of a "[...]"
5795 * block.
5796 */
5797 if (*p == quote) {
5798 p++;
5799
5800 /*
5801 * Allow [#], etc. But stop
5802 * immediately after the ']'.
5803 */
5804 if ((*p == '#') || (*p == '*') || (*p == 'n')) {
5805 p++;
5806
5807 } else {
5808 /*
5809 * Allow numbers as array indexes
5810 */
5811 while ((*p >= '0') && (*p <= '9')) {
5812 p++;
5813 }
5814
5815 if (*p != ']') {
5816 return_P("Array index is not an integer");
5817 }
5818 }
5819
5820 if (*p == ']') {
5821 p++;
5822 continue;
5823 }
5824 }
5825
5826 /*
5827 * Everything else is a breaking token
5828 */
5829 break;
5830 }
5831
5832 /*
5833 * Give some slightly better error messages.
5834 */
5835 if (*p == '\\') {
5836 return_P("Unexpected escape");
5837 }
5838
5839 if ((*p == '"') || (*p == '\'') || (*p == '`')) {
5840 return_P("Unexpected start of string");
5841 }
5842
5843 if (p == *out) {
5844 return_P("Empty string is invalid");
5845 }
5846
5847 *outlen = p - (*out);
5848 break;
5849 }
5850
5851 return p - in;
5852}
5853
5854/** Return whether or not async is required for this tmpl.
5855 *
5856 * If the tmpl is needs_async, then it is async
5857 * If the tmpl is not needs_async, then it will not yield
5858 *
5859 * If the tmpl yields, then async is required.
5860 */
5862{
5863 switch (vpt->type) {
5864 case TMPL_TYPE_EXEC: /* we don't have "exec no-wait" here */
5865 case TMPL_TYPE_XLAT_UNRESOLVED: /* we have no idea, so be safe */
5866#ifndef HAVE_REGEX
5868#endif
5869 return true;
5870
5871#ifndef HAVE_REGEX
5873#endif
5874 case TMPL_TYPE_XLAT: /* synchronous xlats use unlang_interpret_synchronous() */
5875 default:
5876 return false;
5877 }
5878}
5879
5880/** Initialize a set of rules from a parent set of rules, and a parsed tmpl_t
5881 *
5882 */
5884{
5885 fr_dict_attr_t const *da;
5886 fr_dict_attr_t const *ref;
5887 fr_dict_t const *dict, *internal;
5888
5889 *out = *parent;
5890 /* don't set ->parent=parent, that is only for switching subrequest, etc. */
5891
5892 if (!tmpl_is_attr(vpt)) return;
5893
5895
5896 da = tmpl_attr_tail_da(vpt);
5897
5898 /*
5899 * The input tmpl is a leaf. We must parse the child as
5900 * a normal attribute reference (as with the parent tmpl).
5901 */
5902 if (!fr_type_structural[da->type]) {
5903 return;
5904 }
5905
5906 if (vpt->rules.attr.request_def) {
5907 tmpl_request_ref_list_acopy(ctx, &out->attr.request_def, vpt->rules.attr.request_def);
5908 }
5909 out->attr.list_def = tmpl_list(vpt);
5910
5911 /*
5912 * Parse the child attributes in the context of the parent struct / tlv / whatever.
5913 */
5914 if (da->type != FR_TYPE_GROUP) {
5915 out->attr.dict_def = fr_dict_by_da(da);
5916 out->attr.namespace = da;
5917 return;
5918 }
5919
5920 ref = fr_dict_attr_ref(da);
5921 dict = fr_dict_by_da(ref);
5922 internal = fr_dict_internal();
5923
5924 /*
5925 * Groups MAY change dictionaries. If so, then swap the dictionary and the parent.
5926 */
5927 if ((dict != internal) && (dict != out->attr.dict_def)) {
5928 out->attr.dict_def = dict;
5929 out->attr.namespace = ref;
5930 }
5931
5932 /*
5933 * Otherwise the reference is swapping FROM a protocol
5934 * dictionary TO the internal dictionary, and TO an
5935 * internal group. We fall back to leaving well enough
5936 * alone, and leave things as-is. This allows internal
5937 * grouping attributes to appear anywhere.
5938 */
5939}
5940
5941static void tmpl_attr_rules_debug(tmpl_attr_rules_t const *at_rules)
5942{
5943 FR_FAULT_LOG("\tdict_def = %s", at_rules->dict_def ? fr_dict_root(at_rules->dict_def)->name : "");
5944 FR_FAULT_LOG("\tnamespace = %s", at_rules->namespace ? at_rules->namespace->name : "");
5945
5946 FR_FAULT_LOG("\tlist_def = %s", at_rules->list_def ? at_rules->list_def->name : "");
5947
5948 FR_FAULT_LOG("\tallow_unknown = %u", at_rules->allow_unknown);
5949 FR_FAULT_LOG("\tallow_unresolved = %u", at_rules->allow_unresolved);
5950 FR_FAULT_LOG("\tallow_wildcard = %u", at_rules->allow_wildcard);
5951 FR_FAULT_LOG("\tallow_foreign = %u", at_rules->allow_foreign);
5952 FR_FAULT_LOG("\tdisallow_filters = %u", at_rules->disallow_filters);
5953}
5954
5955
5957{
5958 FR_FAULT_LOG("\tparent = %p", rules->parent);
5959 FR_FAULT_LOG(" attr {");
5960 tmpl_attr_rules_debug(&rules->attr);
5961 FR_FAULT_LOG(" }");
5962 FR_FAULT_LOG("\tenumv = %s", rules->enumv ? rules->enumv->name : "");
5963 FR_FAULT_LOG("\tcast = %s", fr_type_to_str(rules->cast));
5964 FR_FAULT_LOG("\tat_runtime = %u", rules->at_runtime);
5965
5966}
static int const char char buffer[256]
Definition acutest.h:576
int const char * file
Definition acutest.h:702
va_end(args)
static int const char * fmt
Definition acutest.h:573
int const char int line
Definition acutest.h:702
va_start(args, fmt)
#define fr_base16_decode(_err, _out, _in, _no_trailing)
Definition base16.h:95
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
Definition build.h:167
#define RCSID(id)
Definition build.h:483
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:209
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition build.h:322
#define STRINGIFY(x)
Definition build.h:197
#define CMP_RETURN(_a, _b, _field)
Return if the comparison is not 0 (is unequal)
Definition build.h:121
#define CMP(_a, _b)
Same as CMP_PREFER_SMALLER use when you don't really care about ordering, you just want an ordering.
Definition build.h:112
#define unlikely(_x)
Definition build.h:381
#define NUM_ELEMENTS(_t)
Definition build.h:337
static char const * skip_word(char const *text)
Definition command.c:1872
#define fr_dbuff_init(_out, _start, _len_or_end)
Initialise an dbuff for encoding or decoding.
Definition dbuff.h:354
#define FR_DBUFF_TMP(_start, _len_or_end)
Creates a compound literal to pass into functions which accept a dbuff.
Definition dbuff.h:514
#define fr_fatal_assert_fail(_msg,...)
Calls panic_action ifndef NDEBUG, else logs error and causes the server to exit immediately with code...
Definition debug.h:191
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:139
#define fr_assert_msg(_x, _msg,...)
Calls panic_action ifndef NDEBUG, else logs error and causes the server to exit immediately with code...
Definition debug.h:210
#define fr_assert_fail(_msg,...)
Calls panic_action ifndef NDEBUG, else logs error.
Definition debug.h:216
#define FR_FAULT_LOG(_fmt,...)
Definition debug.h:49
#define fr_fatal_assert_msg(_x, _fmt,...)
Calls panic_action ifndef NDEBUG, else logs error and causes the server to exit immediately with code...
Definition debug.h:184
#define MEM(x)
Definition debug.h:36
fr_slen_t fr_dict_attr_by_name_substr(fr_dict_attr_err_t *err, fr_dict_attr_t const **out, fr_dict_attr_t const *parent, fr_sbuff_t *name, fr_sbuff_term_t const *tt))
unsigned int name_only
this attribute should always be referred to by name.
Definition dict.h:99
fr_dict_t * fr_dict_unconst(fr_dict_t const *dict)
Coerce to non-const.
Definition dict_util.c:4589
fr_dict_t const * fr_dict_by_da(fr_dict_attr_t const *da)
Attempt to locate the protocol dictionary containing an attribute.
Definition dict_util.c:2610
fr_dict_attr_t const * fr_dict_attr_common_parent(fr_dict_attr_t const *a, fr_dict_attr_t const *b, bool is_ancestor)
Find a common ancestor that two TLV type attributes share.
Definition dict_util.c:2041
static fr_slen_t err
Definition dict.h:823
static fr_dict_attr_t * fr_dict_attr_unknown_vendor_afrom_num(TALLOC_CTX *ctx, fr_dict_attr_t const *parent, unsigned int vendor)
Definition dict.h:572
bool const fr_dict_attr_allowed_chars[UINT8_MAX+1]
Characters that are allowed in dictionary attribute names.
Definition dict_util.c:51
static fr_dict_attr_t * fr_dict_attr_unknown_copy(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
Definition dict.h:552
fr_dict_attr_t const * fr_dict_attr_unknown_add(fr_dict_t *dict, fr_dict_attr_t const *old)
Converts an unknown to a known by adding it to the internal dictionaries.
fr_dict_attr_t const * fr_dict_attr_by_name(fr_dict_attr_err_t *err, fr_dict_attr_t const *parent, char const *attr))
Locate a fr_dict_attr_t by its name.
Definition dict_util.c:3267
fr_dict_attr_t * fr_dict_attr_unconst(fr_dict_attr_t const *da)
Coerce to non-const.
Definition dict_util.c:4601
fr_dict_attr_t * fr_dict_attr_unknown_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da))
Copy a known or unknown attribute to produce an unknown attribute with the specified name.
static fr_dict_attr_t * fr_dict_attr_unknown_raw_afrom_num(TALLOC_CTX *ctx, fr_dict_attr_t const *parent, unsigned int attr)
Definition dict.h:579
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition dict_util.c:2404
int fr_dict_attr_unknown_parent_to_known(fr_dict_attr_t *da, fr_dict_attr_t const *parent)
Fixup the parent of an unknown attribute using an equivalent known attribute.
fr_value_box_t const * value
Enum value (what name maps to).
Definition dict.h:231
fr_dict_enum_value_t * fr_dict_enum_by_name(fr_dict_attr_t const *da, char const *name, ssize_t len)
Definition dict_util.c:3399
void fr_dict_attr_unknown_free(fr_dict_attr_t const **da)
Free dynamically allocated (unknown attributes)
fr_dict_t const * fr_dict_internal(void)
Definition dict_util.c:4614
fr_slen_t fr_dict_attr_search_by_qualified_name_substr(fr_dict_attr_err_t *err, fr_dict_attr_t const **out, fr_dict_t const *dict_def, fr_sbuff_t *name, fr_sbuff_term_t const *tt, bool internal, bool foreign))
Locate a qualified fr_dict_attr_t by its name and a dictionary qualifier.
Definition dict_util.c:2979
fr_slen_t fr_dict_attr_search_by_name_substr(fr_dict_attr_err_t *err, fr_dict_attr_t const **out, fr_dict_t const *dict_def, fr_sbuff_t *name, fr_sbuff_term_t const *tt, bool internal, bool foreign))
Locate a fr_dict_attr_t by its name in the top level namespace of a dictionary.
Definition dict_util.c:3008
static fr_slen_t fr_dict_enum_name_afrom_substr(TALLOC_CTX *ctx, char **out, fr_sbuff_parse_error_t *err, fr_sbuff_t *in, fr_sbuff_term_t const *tt) 1(fr_dict_enum_name_from_substr
#define FR_DICT_MAX_TLV_STACK
Maximum TLV stack size.
Definition dict.h:494
fr_dict_attr_err_t
Errors returned by attribute lookup functions.
Definition dict.h:290
@ FR_DICT_ATTR_OK
No error.
Definition dict.h:291
@ FR_DICT_ATTR_NOT_DESCENDENT
Attribute is not a descendent of the parent attribute.
Definition dict.h:297
@ FR_DICT_ATTR_NO_CHILDREN
Child lookup in attribute with no children.
Definition dict.h:301
int fr_dict_attr_add(fr_dict_t *dict, fr_dict_attr_t const *parent, char const *name, unsigned int attr, fr_type_t type, fr_dict_attr_flags_t const *flags))
Add an attribute to the dictionary.
Definition dict_util.c:1716
fr_dict_attr_t const * fr_dict_attr_child_by_num(fr_dict_attr_t const *parent, unsigned int attr)
Check if a child attribute exists in a parent using an attribute number.
Definition dict_util.c:3332
#define fr_dict_attr_is_key_field(_da)
Definition dict.h:153
static fr_slen_t in
Definition dict.h:823
#define FR_DICT_ATTR_MAX_NAME_LEN
Maximum length of a attribute name.
Definition dict.h:474
Values of the encryption flags.
Value of an enumerated attribute.
Definition dict.h:227
static fr_dict_attr_t const * fr_dict_attr_ref(fr_dict_attr_t const *da)
Return the reference associated with a group type attribute.
Definition dict_ext.h:184
#define FR_DLIST_HEAD(_name)
Expands to the type name used for the head wrapper structure.
Definition dlist.h:1122
#define FR_IPADDR_STRLEN
Like INET6_ADDRSTRLEN but includes space for the textual Zone ID.
Definition inet.h:89
#define HEX_MARKER1(_data, _len, _slen, _error, _fmt,...)
Definition log.h:741
talloc_free(reap)
static char * stack[MAX_STACK]
Definition radmin.c:158
tmpl_attr_prefix_t
@ TMPL_ATTR_REF_PREFIX_NO
Attribute refs have no '&' prefix.
@ TMPL_ATTR_REF_PREFIX_AUTO
Attribute refs may have a '&' prefix.
@ TMPL_ATTR_REF_PREFIX_YES
Attribute refs must have '&' prefix.
unsigned short uint16_t
fr_type_t
@ FR_TYPE_TIME_DELTA
A period of time measured in nanoseconds.
@ FR_TYPE_IPV4_ADDR
32 Bit IPv4 Address.
@ FR_TYPE_INT8
8 Bit signed integer.
@ FR_TYPE_TLV
Contains nested attributes.
@ FR_TYPE_ETHERNET
48 Bit Mac-Address.
@ FR_TYPE_IPV6_PREFIX
IPv6 Prefix.
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_MAX
Number of defined data types.
@ FR_TYPE_NULL
Invalid (uninitialised) attribute type.
@ FR_TYPE_UINT16
16 Bit unsigned integer.
@ FR_TYPE_INT64
64 Bit signed integer.
@ FR_TYPE_INT16
16 Bit signed integer.
@ FR_TYPE_UINT8
8 Bit unsigned integer.
@ FR_TYPE_UINT32
32 Bit unsigned integer.
@ FR_TYPE_STRUCT
like TLV, but without T or L, and fixed-width children
@ FR_TYPE_INT32
32 Bit signed integer.
@ FR_TYPE_VENDOR
Attribute that represents a vendor in the attribute tree.
@ FR_TYPE_UINT64
64 Bit unsigned integer.
@ FR_TYPE_IPV6_ADDR
128 Bit IPv6 Address.
@ FR_TYPE_IPV4_PREFIX
IPv4 Prefix.
@ FR_TYPE_BOOL
A truth value.
@ FR_TYPE_VSA
Vendor-Specific, for RADIUS attribute 26.
@ FR_TYPE_OCTETS
Raw octets.
@ FR_TYPE_GROUP
A grouping of other attributes.
@ FR_TYPE_FLOAT64
Double precision floating point.
unsigned int uint32_t
long int ssize_t
unsigned char uint8_t
ssize_t fr_slen_t
unsigned long int size_t
#define UINT8_MAX
fr_sbuff_parse_error_t
@ FR_SBUFF_PARSE_ERROR_NOT_FOUND
String does not contain a token matching the output type.
@ FR_SBUFF_PARSE_ERROR_FORMAT
Format of data was invalid.
@ FR_SBUFF_PARSE_OK
No error.
static uint8_t depth(fr_minmax_heap_index_t i)
Definition minmax_heap.c:83
char * fr_vasprintf(TALLOC_CTX *ctx, char const *fmt, va_list ap)
Definition print.c:851
void fr_proto_da_stack_build_partial(fr_da_stack_t *stack, fr_dict_attr_t const *parent, fr_dict_attr_t const *da)
Complete the DA stack for a child attribute.
Definition proto.c:159
#define fr_assert(_expr)
Definition rad_assert.h:38
static bool done
Definition radclient.c:80
static uint32_t mask
Definition rbmonkey.c:39
fr_dict_attr_t const * request_attr_request
Definition request.c:45
fr_dict_attr_t const * request_attr_control
Definition request.c:47
fr_dict_attr_t const * request_attr_local
Definition request.c:49
fr_dict_attr_t const * request_attr_state
Definition request.c:48
fr_dict_attr_t const * request_attr_reply
Definition request.c:46
static char const * name
size_t fr_sbuff_adv_past_allowed(fr_sbuff_t *sbuff, size_t len, bool const allowed[static UINT8_MAX+1], fr_sbuff_term_t const *tt)
Wind position past characters in the allowed set.
Definition sbuff.c:1777
bool const sbuff_char_class_hex[UINT8_MAX+1]
Definition sbuff.c:94
bool fr_sbuff_is_terminal(fr_sbuff_t *in, fr_sbuff_term_t const *tt)
Efficient terminal string search.
Definition sbuff.c:2152
size_t fr_sbuff_adv_past_strcase(fr_sbuff_t *sbuff, char const *needle, size_t needle_len)
Return true and advance past the end of the needle if needle occurs next in the sbuff.
Definition sbuff.c:1743
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:1852
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:2088
#define fr_sbuff_start(_sbuff_or_marker)
#define fr_sbuff_out_by_longest_prefix(_match_len, _out, _table, _sbuff, _def)
#define FR_SBUFF_IN_TABLE_STR_RETURN(_sbuff, _table, _number, _def)
#define fr_sbuff_adv_past_str_literal(_sbuff, _needle)
#define fr_sbuff_is_str_literal(_sbuff, _str)
#define FR_SBUFF_IN_CHAR_RETURN(_sbuff,...)
#define fr_sbuff_set(_dst, _src)
#define FR_SBUFF_IN(_start, _len_or_end)
#define fr_sbuff_adv_past_whitespace(_sbuff, _len, _tt)
#define fr_sbuff_adv_past_strcase_literal(_sbuff, _needle)
#define fr_sbuff_current(_sbuff_or_marker)
#define FR_SBUFF_REPARSE(_sbuff_or_marker)
#define FR_SBUFF_IN_ESCAPE_BUFFER_RETURN(...)
#define FR_SBUFF_TERMS(...)
Initialise a terminal structure with a list of sorted strings.
Definition sbuff.h:192
#define FR_SBUFF_IN_STRCPY_LITERAL_RETURN(_sbuff, _str)
#define fr_sbuff_extend(_sbuff_or_marker)
#define FR_SBUFF_RETURN(_func, _sbuff,...)
#define fr_sbuff_is_char(_sbuff_or_marker, _c)
#define FR_SBUFF_ERROR_RETURN(_sbuff_or_marker)
#define FR_SBUFF_SET_RETURN(_dst, _src)
#define FR_SBUFF_IN_SPRINTF_RETURN(...)
#define FR_SBUFF(_sbuff_or_marker)
#define FR_SBUFF_IN_BSTRNCPY_RETURN(...)
#define fr_sbuff_advance(_sbuff_or_marker, _len)
#define fr_sbuff_out(_err, _out, _in)
#define fr_sbuff_switch(_sbuff_or_marker, _eob)
#define FR_SBUFF_OUT(_start, _len_or_end)
#define fr_sbuff_used(_sbuff_or_marker)
#define FR_SBUFF_IN_STRCPY_RETURN(...)
#define FR_SBUFF_IN_BSTRCPY_BUFFER_RETURN(...)
Set of terminal elements.
static fr_slen_t ar_prefix
Definition tmpl.h:1285
#define tmpl_contains_xlat(vpt)
Definition tmpl.h:232
#define TMPL_VERIFY(_vpt)
Definition tmpl.h:975
#define tmpl_value_length(_tmpl)
Definition tmpl.h:955
static char const * tmpl_type_to_str(tmpl_type_t type)
Return a static string containing the type name.
Definition tmpl.h:651
#define tmpl_is_uninitialised(vpt)
Helpers to verify the type of tmpl_t.
Definition tmpl.h:208
#define tmpl_is_attr_unresolved(vpt)
Definition tmpl.h:224
enum requests_ref_e tmpl_request_ref_t
static bool tmpl_attr_tail_is_unspecified(tmpl_t const *vpt)
Return true if the last attribute reference is "unspecified".
Definition tmpl.h:743
#define NUM_LAST
Definition tmpl.h:408
#define tmpl_rules_enumv(_tmpl)
Definition tmpl.h:960
static fr_slen_t vpt
Definition tmpl.h:1278
static bool tmpl_attr_tail_is_normal(tmpl_t const *vpt)
Return true if the last attribute reference is "normal".
Definition tmpl.h:727
#define tmpl_value(_tmpl)
Definition tmpl.h:954
#define ar_is_unknown(_ar)
Definition tmpl.h:522
static bool tmpl_attr_tail_is_unknown(tmpl_t const *vpt)
Return true if the last attribute reference is "unknown".
Definition tmpl.h:759
#define tmpl_contains_regex(vpt)
Definition tmpl.h:231
fr_value_box_safe_for_t literals_safe_for
safe_for value assigned to literal values in xlats, execs, and data.
Definition tmpl.h:362
#define ar_is_raw(_ar)
Definition tmpl.h:524
#define tmpl_is_attr(vpt)
Definition tmpl.h:213
#define NUM_ALL
Definition tmpl.h:406
fr_dict_attr_t const * enumv
Enumeration attribute used to resolve enum values.
Definition tmpl.h:353
tmpl_rules_t const * parent
for parent / child relationships
Definition tmpl.h:348
#define tmpl_value_enumv(_tmpl)
Definition tmpl.h:957
#define tmpl_xlat(_tmpl)
Definition tmpl.h:947
static fr_dict_attr_t const * tmpl_list(tmpl_t const *vpt)
Definition tmpl.h:921
static bool tmpl_attr_is_list_attr(tmpl_attr_t const *ar)
Return true if the tmpl_attr is one of the list types.
Definition tmpl.h:696
#define ar_filter_is_num(_ar)
Definition tmpl.h:533
#define tmpl_rules_cast(_tmpl)
Definition tmpl.h:959
@ TMPL_TYPE_REGEX_UNCOMPILED
Regex where compilation is possible but hasn't been performed yet.
Definition tmpl.h:162
@ TMPL_TYPE_MAX
Marker for the last tmpl type.
Definition tmpl.h:203
@ TMPL_TYPE_ATTR_UNRESOLVED
An attribute reference that we couldn't resolve but looked valid.
Definition tmpl.h:189
@ TMPL_TYPE_ATTR
Reference to one or more attributes.
Definition tmpl.h:146
@ TMPL_TYPE_XLAT
Pre-parsed xlat expansion.
Definition tmpl.h:150
@ TMPL_TYPE_NULL
Has no value.
Definition tmpl.h:138
@ TMPL_TYPE_EXEC
Callout to an external script or program.
Definition tmpl.h:154
@ TMPL_TYPE_REGEX_XLAT_UNRESOLVED
A regular expression with unresolved xlat functions or attribute references.
Definition tmpl.h:201
@ TMPL_TYPE_DATA
Value in native boxed format.
Definition tmpl.h:142
@ TMPL_TYPE_REGEX
Compiled (and possibly JIT'd) regular expression.
Definition tmpl.h:158
@ TMPL_TYPE_DATA_UNRESOLVED
Unparsed literal string.
Definition tmpl.h:183
@ TMPL_TYPE_XLAT_UNRESOLVED
A xlat expansion with unresolved xlat functions or attribute references.
Definition tmpl.h:197
@ TMPL_TYPE_REGEX_XLAT
A regex containing xlat expansions.
Definition tmpl.h:166
@ TMPL_TYPE_EXEC_UNRESOLVED
An exec with unresolved xlat function or attribute references.
Definition tmpl.h:193
@ TMPL_TYPE_UNINITIALISED
Uninitialised.
Definition tmpl.h:134
#define tmpl_is_regex_xlat(vpt)
Definition tmpl.h:220
#define NUM_COUNT
Definition tmpl.h:407
#define tmpl_assert_type(_cond)
Convenience macro for printing a meaningful assert message when we get a bad tmpl type.
Definition tmpl.h:637
#define tmpl_contains_attr(vpt)
Definition tmpl.h:230
#define ar_da
Definition tmpl.h:514
#define TMPL_FLAG_REGEX
Is a type of regular expression.
Definition tmpl.h:116
#define ar_filter_is_cond(_ar)
Definition tmpl.h:534
tmpl_xlat_rules_t xlat
Rules/data for parsing xlats.
Definition tmpl.h:351
static char const * tmpl_attr_tail_unresolved(tmpl_t const *vpt)
Return the last attribute reference unresolved da.
Definition tmpl.h:886
bool at_runtime
Produce an ephemeral/runtime tmpl.
Definition tmpl.h:359
static bool tmpl_is_list(tmpl_t const *vpt)
Definition tmpl.h:937
#define TMPL_ATTR_VERIFY(_vpt)
Definition tmpl.h:974
bool force_dict_def
Use supplied dict_def even if original vpt->rules->dict_def was not NULL.
Definition tmpl.h:385
#define tmpl_is_data(vpt)
Definition tmpl.h:211
fr_dict_t const * dict_def
Alternative default dictionary to use if vpt->rules->dict_def is NULL.
Definition tmpl.h:380
#define NUM_UNSPEC
Definition tmpl.h:405
static size_t tmpl_attr_num_elements(tmpl_t const *vpt)
The number of attribute references contained within a tmpl.
Definition tmpl.h:913
#define tmpl_value_type(_tmpl)
Definition tmpl.h:956
#define tmpl_attr(_tmpl)
Definition tmpl.h:671
tmpl_attr_error_t
Definition tmpl.h:1018
@ TMPL_ATTR_ERROR_INVALID_ARRAY_INDEX
Invalid array index.
Definition tmpl.h:1037
@ TMPL_ATTR_ERROR_LIST_NOT_ALLOWED
List qualifier is not allowed here.
Definition tmpl.h:1022
@ TMPL_ATTR_ERROR_UNRESOLVED_NOT_ALLOWED
Attribute couldn't be found in the dictionaries.
Definition tmpl.h:1028
@ TMPL_ATTR_ERROR_BAD_CAST
Specified cast was invalid.
Definition tmpl.h:1041
@ TMPL_ATTR_ERROR_INVALID_NAME
Attribute ref length is zero, or longer than the maximum.
Definition tmpl.h:1030
@ TMPL_ATTR_ERROR_MISSING_TERMINATOR
Unexpected text found after attribute reference.
Definition tmpl.h:1040
@ TMPL_ATTR_ERROR_LIST_MISSING
List qualifier is required, but missing.
Definition tmpl.h:1023
@ TMPL_ATTR_ERROR_NONE
No error.
Definition tmpl.h:1019
@ TMPL_ATTR_ERROR_FOREIGN_NOT_ALLOWED
Attribute resolved in a dictionary different to the one specified.
Definition tmpl.h:1034
@ TMPL_ATTR_ERROR_UNKNOWN_NOT_ALLOWED
Attribute specified as OID, could not be found in the dictionaries, and is disallowed because 'disall...
Definition tmpl.h:1024
@ TMPL_ATTR_ERROR_FILTER_NOT_ALLOWED
Filters disallowed by rules.
Definition tmpl.h:1036
@ TMPL_ATTR_ERROR_EMPTY
Attribute ref contains no data.
Definition tmpl.h:1020
@ TMPL_ATTR_ERROR_NESTING_TOO_DEEP
Too many levels of nesting.
Definition tmpl.h:1039
@ TMPL_ATTR_ERROR_BAD_PREFIX
Missing '&' or has '&' when it shouldn't.
Definition tmpl.h:1021
#define tmpl_is_data_unresolved(vpt)
Definition tmpl.h:222
fr_type_t cast
Whether there was an explicit cast.
Definition tmpl.h:355
tmpl_attr_rules_t attr
Rules/data for parsing attribute references.
Definition tmpl.h:350
static fr_dict_attr_t const * tmpl_attr_tail_da(tmpl_t const *vpt)
Return the last attribute reference da.
Definition tmpl.h:818
@ TMPL_ATTR_LIST_REQUIRE
Attribute refs are required to have a list.
Definition tmpl.h:278
@ TMPL_ATTR_LIST_ALLOW
Attribute refs are allowed to have a list.
Definition tmpl.h:276
@ TMPL_ATTR_LIST_FORBID
Attribute refs are forbidden from having a list.
Definition tmpl.h:277
enum tmpl_type_e tmpl_type_t
Types of tmpl_t.
static fr_dict_attr_t const * tmpl_attr_tail_unknown(tmpl_t const *vpt)
Return the last attribute reference unknown da.
Definition tmpl.h:870
#define tmpl_is_regex(vpt)
Definition tmpl.h:218
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:932
static bool tmpl_attr_tail_is_raw(tmpl_t const *vpt)
Return true if the last attribute reference is "raw".
Definition tmpl.h:791
@ REQUEST_OUTER
request_t containing the outer layer of the EAP conversation.
Definition tmpl.h:92
@ REQUEST_PARENT
Parent (whatever it is).
Definition tmpl.h:96
@ REQUEST_UNKNOWN
Unknown request.
Definition tmpl.h:97
@ REQUEST_CURRENT
The current request (default).
Definition tmpl.h:91
struct tmpl_rules_s tmpl_rules_t
Definition tmpl.h:238
#define tmpl_is_regex_xlat_unresolved(vpt)
Definition tmpl.h:226
#define tmpl_is_regex_uncompiled(vpt)
Definition tmpl.h:219
fr_dict_attr_t const * enumv
for resolving T_BARE_WORD
Definition tmpl.h:388
#define TMPL_MAX_REQUEST_REF_NESTING
The maximum number of request references allowed.
Definition tmpl.h:85
tmpl_attr_type_t
Definition tmpl.h:391
@ TMPL_ATTR_TYPE_UNSPEC
No attribute was specified as this level only a filter.
Definition tmpl.h:393
@ TMPL_ATTR_TYPE_NORMAL
Normal, resolved, attribute ref.
Definition tmpl.h:392
@ TMPL_ATTR_TYPE_UNKNOWN
We have an attribute number but it doesn't match anything in the dictionary, or isn't a child of the ...
Definition tmpl.h:395
@ TMPL_ATTR_TYPE_UNRESOLVED
We have a name, but nothing else to identify the attribute.
Definition tmpl.h:400
@ TMPL_ATTR_FILTER_TYPE_TMPL
Filter is a tmpl.
Definition tmpl.h:422
@ TMPL_ATTR_FILTER_TYPE_INDEX
Filter is an index type.
Definition tmpl.h:420
@ TMPL_ATTR_FILTER_TYPE_CONDITION
Filter is a condition.
Definition tmpl.h:421
@ TMPL_ATTR_FILTER_TYPE_NONE
No filter present.
Definition tmpl.h:419
@ TMPL_ATTR_FILTER_TYPE_EXPR
Filter is an expression.
Definition tmpl.h:423
fr_event_list_t * runtime_el
The eventlist to use for runtime instantiation of xlats.
Definition tmpl.h:339
#define tmpl_needs_resolving(vpt)
Definition tmpl.h:228
tmpl_attr_filter_type_t _CONST type
Type of filter this is.
Definition tmpl.h:427
#define ar_filter_is_none(_ar)
Definition tmpl.h:532
Similar to tmpl_rules_t, but used to specify parameters that may change during subsequent resolution ...
Definition tmpl.h:379
Optional arguments passed to vp_tmpl functions.
Definition tmpl.h:347
static char buff[sizeof("18446744073709551615")+3]
Definition size_tests.c:41
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition snprintf.c:689
fr_aka_sim_id_type_t type
Define entry and head types for tmpl request references.
Definition tmpl.h:286
tmpl_attr_list_presence_t list_presence
Whether the attribute reference can have a list, forbid it, or require it.
Definition tmpl.h:315
fr_dict_attr_t const * list_def
Default list to use with unqualified attribute reference.
Definition tmpl.h:309
uint8_t disallow_filters
disallow filters.
Definition tmpl.h:331
uint8_t allow_unresolved
Allow attributes that look valid but were not found in the dictionaries.
Definition tmpl.h:321
uint8_t allow_foreign
Allow arguments not found in dict_def.
Definition tmpl.h:329
fr_dict_t const * dict_def
Default dictionary to use with unqualified attribute references.
Definition tmpl.h:287
uint8_t allow_wildcard
Allow the special case of .
Definition tmpl.h:326
uint8_t allow_unknown
Allow unknown attributes i.e.
Definition tmpl.h:318
tmpl_attr_prefix_t prefix
Whether the attribute reference requires a prefix.
Definition tmpl.h:312
An element in a list of nested attribute references.
Definition tmpl.h:445
unsigned int _CONST resolve_only
This reference and those before it.
Definition tmpl.h:468
unsigned int _CONST is_raw
Definition tmpl.h:471
fr_dict_attr_t const *_CONST da
Resolved dictionary attribute.
Definition tmpl.h:449
fr_dict_attr_t const *_CONST parent
The parent we used when trying to resolve the attribute originally.
Definition tmpl.h:463
tmpl_attr_filter_t _CONST filter
Filter associated with the attribute reference.
Definition tmpl.h:475
tmpl_attr_type_t _CONST type
is a raw reference
Definition tmpl.h:473
Define manipulation functions for the attribute reference list.
Definition tmpl.h:486
tmpl_request_ref_t _CONST request
Definition tmpl.h:490
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition table.h:772
An element in an arbitrarily ordered array of name to num mappings.
Definition table.h:57
An element in a lexicographically sorted array of name to num mappings.
Definition table.h:49
char * talloc_bstrdup(TALLOC_CTX *ctx, char const *in)
Binary safe strdup function.
Definition talloc.c:536
char * talloc_bstrndup(TALLOC_CTX *ctx, char const *in, size_t inlen)
Binary safe strndup function.
Definition talloc.c:564
char * talloc_typed_strdup(TALLOC_CTX *ctx, char const *p)
Call talloc_strdup, setting the type on the new chunk correctly.
Definition talloc.c:445
#define talloc_get_type_abort_const
Definition talloc.h:282
static int talloc_const_free(void const *ptr)
Free const'd memory.
Definition talloc.h:224
#define talloc_pooled_object(_ctx, _type, _num_subobjects, _total_subobjects_size)
Definition talloc.h:180
fr_slen_t fr_time_delta_from_substr(fr_time_delta_t *out, fr_sbuff_t *in, fr_time_res_t hint, bool no_trailing, fr_sbuff_term_t const *tt)
Create fr_time_delta_t from a string.
Definition time.c:214
@ FR_TIME_RES_SEC
Definition time.h:50
A time delta, a difference in time measured in nanoseconds.
Definition time.h:80
Escaping rules for tmpls.
Definition tmpl_escape.h:80
int8_t tmpl_request_ref_list_cmp(FR_DLIST_HEAD(tmpl_request_list) const *a, FR_DLIST_HEAD(tmpl_request_list) const *b)
Compare a list of request qualifiers.
static ssize_t tmpl_afrom_time_delta(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules)
void tmpl_request_ref_list_debug(FR_DLIST_HEAD(tmpl_request_list) const *rql)
Dump a request list to stderr.
fr_slen_t tmpl_request_ref_list_print(fr_sbuff_t *out, FR_DLIST_HEAD(tmpl_request_list) const *rql)
void tmpl_attr_set_list(tmpl_t *vpt, fr_dict_attr_t const *list)
tmpl_t * tmpl_init_shallow(tmpl_t *vpt, tmpl_type_t type, fr_token_t quote, char const *name, ssize_t len, tmpl_rules_t const *t_rules)
Initialise a tmpl without copying the input name string.
fr_slen_t tmpl_request_ref_list_afrom_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err, FR_DLIST_HEAD(tmpl_request_list) **out, fr_sbuff_t *in)
Parse one or more request references, allocing a new list and adding the references to it.
static fr_table_num_sorted_t const attr_num_table[]
Special attribute reference indexes.
void tmpl_unresolve(tmpl_t *vpt)
Reset the tmpl, leaving only the name in place.
void tmpl_set_xlat(tmpl_t *vpt, xlat_exp_head_t *xlat)
Change the default dictionary in the tmpl's resolution rules.
static void tmpl_type_init(tmpl_t *vpt, tmpl_type_t type)
Initialise fields inside a tmpl depending on its type.
#define RESOLVED_SET(_flags)
static fr_slen_t tmpl_attr_ref_afrom_unresolved_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err, tmpl_t *vpt, fr_dict_attr_t const *parent, fr_dict_attr_t const *namespace, fr_sbuff_t *name, tmpl_attr_rules_t const *at_rules)
Parse an unresolved attribute, i.e.
void tmpl_attr_set_request_ref(tmpl_t *vpt, FR_DLIST_HEAD(tmpl_request_list) const *request_def)
Set the request for an attribute ref.
void tmpl_set_name_printf(tmpl_t *vpt, fr_token_t quote, char const *fmt,...)
Set the name on a pre-initialised tmpl.
int tmpl_attr_set_da(tmpl_t *vpt, fr_dict_attr_t const *da)
Replace the current attribute reference.
static fr_table_num_ordered_t const attr_table[]
Attr ref types.
static int tmpl_xlat_resolve(tmpl_t *vpt, tmpl_res_rules_t const *tr_rules)
Resolve an unresolved xlat, i.e.
fr_table_num_sorted_t const tmpl_request_ref_table[]
Map keywords to tmpl_request_ref_t values.
fr_slen_t tmpl_print(fr_sbuff_t *out, tmpl_t const *vpt, tmpl_attr_prefix_t ar_prefix, fr_sbuff_escape_rules_t const *e_rules)
Print a tmpl_t to a string.
int tmpl_afrom_value_box(TALLOC_CTX *ctx, tmpl_t **out, fr_value_box_t *data, bool steal)
Create a tmpl_t from a fr_value_box_t.
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.
bool tmpl_require_enum_prefix
void tmpl_set_escape(tmpl_t *vpt, tmpl_escape_t const *escape)
Set escape parameters for the tmpl output.
bool tmpl_async_required(tmpl_t const *vpt)
Return whether or not async is required for this tmpl.
void tmpl_rules_child_init(TALLOC_CTX *ctx, tmpl_rules_t *out, tmpl_rules_t const *parent, tmpl_t *vpt)
Initialize a set of rules from a parent set of rules, and a parsed tmpl_t.
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.
static fr_slen_t tmpl_attr_ref_from_unspecified_substr(tmpl_attr_t *ar, tmpl_attr_error_t *err, tmpl_t *vpt, fr_sbuff_t *name, tmpl_attr_rules_t const *at_rules)
static void attr_to_raw(tmpl_t *vpt, tmpl_attr_t *ref)
tmpl_t * tmpl_init_printf(tmpl_t *vpt, tmpl_type_t type, fr_token_t quote, char const *fmt,...)
Initialise a tmpl using a format string to create the name.
static void tmpl_attr_insert(tmpl_t *vpt, tmpl_attr_t *ar)
Insert an attribute reference into a tmpl.
int tmpl_cast_set(tmpl_t *vpt, fr_type_t dst_type)
Set a cast for a tmpl.
static fr_slen_t tmpl_afrom_ipv4_substr(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules)
Parse bareword as an IPv4 address or prefix.
void tmpl_set_name(tmpl_t *vpt, fr_token_t quote, char const *name, ssize_t len)
Set the name on a pre-initialised tmpl.
int tmpl_attr_afrom_list(TALLOC_CTX *ctx, tmpl_t **out, tmpl_t const *list, fr_dict_attr_t const *da)
Create a new tmpl from a list tmpl and a da.
void tmpl_attr_to_raw(tmpl_t *vpt)
Convert the leaf attribute of a tmpl to a unknown/raw type.
tmpl_t * tmpl_copy(TALLOC_CTX *ctx, tmpl_t const *in)
Copy a tmpl.
void tmpl_attr_ref_debug(const tmpl_attr_t *ar, int i)
void tmpl_attr_rewrite_leaf_num(tmpl_t *vpt, int16_t to)
Rewrite the leaf's instance number.
static bool tmpl_substr_terminal_check(fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules)
Verify, after skipping whitespace, that a substring ends in a terminal char, or ends without further ...
static void tmpl_request_ref_list_copy(TALLOC_CTX *ctx, FR_DLIST_HEAD(tmpl_request_list) *out, FR_DLIST_HEAD(tmpl_request_list) const *in)
Allocate a new request reference and add it to the end of the attribute reference list.
static void tmpl_attr_rules_debug(tmpl_attr_rules_t const *at_rules)
ssize_t tmpl_preparse(char const **out, size_t *outlen, char const *in, size_t inlen, fr_token_t *type)
Preparse a string in preparation for passing it to tmpl_afrom_substr()
#define TMPL_REQUEST_REF_DEF(_name, _ref)
Define a global variable for specifying a default request reference.
#define UNRESOLVED_SET(_flags)
void tmpl_debug(tmpl_t const *vpt)
void tmpl_set_name_shallow(tmpl_t *vpt, fr_token_t quote, char const *name, ssize_t len)
Set the name on a pre-initialised tmpl.
fr_slen_t tmpl_attr_print(fr_sbuff_t *out, tmpl_t const *vpt, tmpl_attr_prefix_t ar_prefix)
Print an attribute or list tmpl_t to a string.
static int tmpl_attr_afrom_attr_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err, tmpl_t *vpt, fr_dict_attr_t const *parent, fr_dict_attr_t const *namespace, fr_sbuff_t *name, fr_sbuff_parse_rules_t const *p_rules, tmpl_attr_rules_t const *at_rules, unsigned int depth)
Parse an attribute reference, either an OID or attribute name.
static fr_slen_t tmpl_afrom_ipv6_substr(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules)
Parse bareword as an IPv6 address or prefix.
static fr_slen_t tmpl_afrom_bool_substr(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules)
Parse a truth value.
static tmpl_attr_t * tmpl_attr_add(tmpl_t *vpt, tmpl_attr_type_t type)
Allocate a new attribute reference and add it to the end of the attribute reference list.
static size_t attr_num_table_len
#define return_P(_x)
ssize_t tmpl_afrom_attr_str(TALLOC_CTX *ctx, tmpl_attr_error_t *err, tmpl_t **out, char const *name, tmpl_rules_t const *t_rules)
Parse a string into a TMPL_TYPE_ATTR_* type tmpl_t.
fr_slen_t tmpl_print_quoted(fr_sbuff_t *out, tmpl_t const *vpt, tmpl_attr_prefix_t ar_prefix)
Print a tmpl_t to a string with quotes.
#define CHECK_T_RULES
void tmpl_attr_ref_list_debug(FR_DLIST_HEAD(tmpl_attr_list) const *ar_head)
ssize_t tmpl_cast_from_substr(tmpl_rules_t *rules, fr_sbuff_t *in)
Parse a cast specifier.
static tmpl_t * tmpl_alloc_null(TALLOC_CTX *ctx)
Create a new heap allocated tmpl_t.
static fr_slen_t tmpl_request_ref_list_from_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err, FR_DLIST_HEAD(tmpl_request_list) *out, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules, fr_dict_attr_t const **namespace)
Parse one or more request references, writing the list to out.
void tmpl_rules_debug(tmpl_rules_t const *rules)
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 ssize_t tmpl_afrom_ether_substr(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules)
Try and parse signed or unsigned integers.
static ssize_t tmpl_afrom_float_substr(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules)
static fr_slen_t tmpl_afrom_octets_substr(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules)
Parse bareword as an octet string.
size_t tmpl_type_table_len
static int tmpl_attr_resolve(tmpl_t *vpt, tmpl_res_rules_t const *tr_rules)
Resolve an unresolved attribute.
static ssize_t tmpl_afrom_enum(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules)
int tmpl_attr_set_leaf_da(tmpl_t *vpt, fr_dict_attr_t const *da)
Replace the leaf attribute only.
fr_slen_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.
static fr_token_t tmpl_cast_quote(fr_token_t existing_quote, fr_type_t type, fr_dict_attr_t const *enumv, char const *unescaped, size_t unescaped_len)
Determine the correct quoting after a cast.
fr_dict_attr_t const * tmpl_attr_unspec
Placeholder attribute for uses of unspecified attribute references.
Definition tmpl_eval.c:61
static fr_slen_t tmpl_afrom_value_substr(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in, fr_token_t quote, tmpl_rules_t const *t_rules, bool allow_enum, fr_sbuff_parse_rules_t const *p_rules)
Create TMPL_TYPE_DATA from a string.
int tmpl_resolve(tmpl_t *vpt, tmpl_res_rules_t const *tr_rules)
Attempt to resolve functions and attributes in xlats and attribute references.
void tmpl_set_dict_def(tmpl_t *vpt, fr_dict_t const *dict)
Change the default dictionary in the tmpl's resolution rules.
tmpl_t * tmpl_init(tmpl_t *vpt, tmpl_type_t type, fr_token_t quote, char const *name, ssize_t len, tmpl_rules_t const *t_rules)
Initialise a tmpl using a literal string to create the name.
static void tmpl_request_ref_list_acopy(TALLOC_CTX *ctx, FR_DLIST_HEAD(tmpl_request_list) **out, FR_DLIST_HEAD(tmpl_request_list) const *in)
Allocate a new request reference list and copy request references into it.
static fr_slen_t tmpl_afrom_integer_substr(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules)
Try and parse signed or unsigned integers.
int tmpl_cast_in_place(tmpl_t *vpt, fr_type_t type, fr_dict_attr_t const *enumv)
Convert tmpl_t of type TMPL_TYPE_DATA_UNRESOLVED or TMPL_TYPE_DATA to TMPL_TYPE_DATA of type specifie...
static void tmpl_attr_ref_fixup(TALLOC_CTX *ctx, tmpl_t *vpt, fr_dict_attr_t const *da, fr_dict_attr_t const *parent)
static fr_slen_t tmpl_attr_parse_filter(tmpl_attr_error_t *err, tmpl_attr_t *ar, fr_sbuff_t *name, tmpl_attr_rules_t const *at_rules)
Parse array subscript and in future other filters.
int tmpl_attr_to_xlat(TALLOC_CTX *ctx, tmpl_t **vpt_p)
Convert an attribute reference to an xlat expansion.
fr_slen_t tmpl_attr_list_from_substr(fr_dict_attr_t const **da_p, fr_sbuff_t *in)
Parse one a single list reference.
#define DEFAULT_RULES
Default parser rules.
fr_table_num_ordered_t const tmpl_type_table[]
Map tmpl_type_t values to descriptive strings.
void tmpl_attr_debug(tmpl_t const *vpt)
static size_t attr_table_len
int tmpl_attr_tail_unresolved_add(fr_dict_t *dict_def, tmpl_t *vpt, fr_type_t type, fr_dict_attr_flags_t const *flags)
Add an unresolved fr_dict_attr_t specified by a tmpl_t to the main dictionary.
size_t tmpl_request_ref_table_len
fr_table_num_sorted_t const fr_token_quotes_table[]
Definition token.c:66
const char fr_token_quote[T_TOKEN_LAST]
Convert tokens back to a quoting character.
Definition token.c:156
enum fr_token fr_token_t
@ T_INVALID
Definition token.h:39
@ T_SINGLE_QUOTED_STRING
Definition token.h:122
@ T_BARE_WORD
Definition token.h:120
@ T_BACK_QUOTED_STRING
Definition token.h:123
@ T_DOUBLE_QUOTED_STRING
Definition token.h:121
@ T_SOLIDUS_QUOTED_STRING
Definition token.h:124
#define T_TOKEN_LAST
Definition token.h:129
close(uq->fd)
bool xlat_needs_resolving(xlat_exp_head_t const *head)
Check to see if the expansion needs resolving.
bool xlat_is_literal(xlat_exp_head_t const *head)
Check to see if the expansion consists entirely of value-box elements.
int xlat_from_tmpl_attr(TALLOC_CTX *ctx, xlat_exp_head_t **head, tmpl_t **vpt_p)
Convert attr tmpl to an xlat for &attr[*].
fr_slen_t xlat_print(fr_sbuff_t *in, xlat_exp_head_t const *node, fr_sbuff_escape_rules_t const *e_rules)
Reconstitute an xlat expression from its constituent nodes.
bool xlat_impure_func(xlat_exp_head_t const *head)
fr_slen_t xlat_tokenize_argv(TALLOC_CTX *ctx, xlat_exp_head_t **head, fr_sbuff_t *in, xlat_t const *xlat, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules, bool comma, bool allow_attr)
Tokenize an xlat expansion into a series of XLAT_TYPE_CHILD arguments.
static fr_slen_t head
Definition xlat.h:422
static fr_slen_t xlat_aprint(TALLOC_CTX *ctx, char **out, xlat_exp_head_t const *head, fr_sbuff_escape_rules_t const *e_rules) 1(xlat_print
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.
#define xlat_copy(_ctx, _out, _in)
Definition xlat.h:459
fr_slen_t xlat_tokenize(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, fr_value_box_safe_for_t literals_safe_for)
Tokenize an xlat expansion.
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.
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:695
fr_slen_t xlat_tokenize_condition(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:3264
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:3258
static fr_slen_t parent
Definition pair.h:851
Structure for holding the stack of dictionary attributes being encoded.
Definition proto.h:54
void fr_strerror_clear(void)
Clears all pending messages from the talloc pools.
Definition strerror.c:577
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
#define fr_strerror_const_push(_msg)
Definition strerror.h:227
#define fr_strerror_const(_msg)
Definition strerror.h:223
fr_table_num_ordered_t const fr_type_table[]
Map data types to names representing those types.
Definition types.c:31
bool fr_type_cast(fr_type_t dst, fr_type_t src)
Return if we're allowed to cast the types.
Definition types.c:283
bool const fr_type_numeric[FR_TYPE_MAX+1]
Definition types.c:173
bool const fr_type_structural[FR_TYPE_MAX+1]
Definition types.c:183
#define fr_type_is_non_leaf(_x)
Definition types.h:373
#define fr_type_is_octets(_x)
Definition types.h:328
#define fr_type_is_structural(_x)
Definition types.h:371
#define fr_type_is_string(_x)
Definition types.h:327
#define FR_TYPE_STRUCTURAL
Definition types.h:296
#define fr_type_is_null(_x)
Definition types.h:326
#define fr_type_is_leaf(_x)
Definition types.h:372
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
Definition types.h:433
#define FR_TYPE_LEAF
Definition types.h:297
ssize_t fr_value_box_from_substr(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *rules, bool tainted)
Convert string value to a fr_value_box_t type.
Definition value.c:4802
ssize_t fr_value_box_from_str(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, char const *in, size_t inlen, fr_sbuff_unescape_rules_t const *erules, bool tainted)
Definition value.c:5361
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:5398
int fr_value_box_copy(TALLOC_CTX *ctx, fr_value_box_t *dst, const fr_value_box_t *src)
Copy value data verbatim duplicating any buffers.
Definition value.c:3740
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:3572
void fr_value_box_memdup_shallow(fr_value_box_t *dst, fr_dict_attr_t const *enumv, uint8_t const *src, size_t len, bool tainted)
Assign a buffer to a box, but don't copy it.
Definition value.c:4548
void fr_value_box_copy_shallow(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_value_box_t const *src)
Perform a shallow copy of a value_box.
Definition value.c:3834
fr_sbuff_escape_rules_t * fr_value_escape_by_quote[T_TOKEN_LAST]
Definition value.c:441
int fr_value_box_steal(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_value_box_t *src)
Copy value data verbatim moving any buffers to the specified context.
Definition value.c:3858
void fr_value_box_clear(fr_value_box_t *data)
Clear/free any existing value and metadata.
Definition value.c:3723
int fr_value_box_memdup(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_dict_attr_t const *enumv, uint8_t const *src, size_t len, bool tainted)
Copy a buffer to a fr_value_box_t.
Definition value.c:4468
#define fr_value_box_mark_safe_for(_box, _safe_for)
Definition value.h:1048
static fr_slen_t fr_value_box_aprint(TALLOC_CTX *ctx, char **out, fr_value_box_t const *data, fr_sbuff_escape_rules_t const *e_rules) 1(fr_value_box_print
static fr_slen_t data
Definition value.h:1265
#define fr_box_strvalue_len(_val, _len)
Definition value.h:286
static size_t char fr_sbuff_t size_t inlen
Definition value.h:997
#define vb_strvalue
Definition value.h:241
int nonnull(2, 5))
#define fr_value_box_init(_vb, _type, _enumv, _tainted)
Initialise a fr_value_box_t.
Definition value.h:587
static size_t char ** out
Definition value.h:997