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: b8c6088f9d9f4341e692cee4b322c16ccbb5f59f $
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: b8c6088f9d9f4341e692cee4b322c16ccbb5f59f $")
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 const default_rules = { .attr = { .list_def = request_attr_request }}
107
108
109/* clang-format off */
110/** Map #tmpl_type_t values to descriptive strings
111 */
113 { L("uninitialised"), TMPL_TYPE_UNINITIALISED },
114
115 { L("null"), TMPL_TYPE_NULL },
116 { L("data"), TMPL_TYPE_DATA },
117
118 { L("attr"), TMPL_TYPE_ATTR },
119
120 { L("exec"), TMPL_TYPE_EXEC },
121 { L("xlat"), TMPL_TYPE_XLAT },
122
123 { L("regex"), TMPL_TYPE_REGEX },
124 { L("regex-uncompiled"), TMPL_TYPE_REGEX_UNCOMPILED },
125 { L("regex-xlat"), TMPL_TYPE_REGEX_XLAT },
126
127 { L("data-unresolved"), TMPL_TYPE_DATA_UNRESOLVED },
128 { L("attr-unresolved"), TMPL_TYPE_ATTR_UNRESOLVED },
129 { L("exec-unresolved"), TMPL_TYPE_EXEC_UNRESOLVED },
130 { L("xlat-unresolved"), TMPL_TYPE_XLAT_UNRESOLVED },
131 { L("regex-unresolved"), TMPL_TYPE_REGEX_XLAT_UNRESOLVED }
132};
134
135/** Attr ref types
136 */
138 { L("normal"), TMPL_ATTR_TYPE_NORMAL },
139 { L("unspecified"), TMPL_ATTR_TYPE_UNSPEC },
140 { L("unknown"), TMPL_ATTR_TYPE_UNKNOWN },
141 { L("unresolved"), TMPL_ATTR_TYPE_UNRESOLVED }
142};
144
145/** Map keywords to #tmpl_request_ref_t values
146 */
148 { L("current"), REQUEST_CURRENT },
149 { L("outer"), REQUEST_OUTER },
150 { L("parent"), REQUEST_PARENT },
151};
153
154
155/** Special attribute reference indexes
156 */
158 { L("*"), NUM_ALL },
159 { L("#"), NUM_COUNT },
160 { L("u"), NUM_UNSPEC },
161 { L("n"), NUM_LAST }
162};
164/* clang-format on */
165
166static void attr_to_raw(tmpl_t *vpt, tmpl_attr_t *ref);
167
168/*
169 * Can't use |= or ^= else we get out of range errors
170 */
171#define UNRESOLVED_SET(_flags) (*(_flags) = (*(_flags) | TMPL_FLAG_UNRESOLVED))
172#define RESOLVED_SET(_flags) (*(_flags) = (*(_flags) & ~TMPL_FLAG_UNRESOLVED))
173
174/** Verify, after skipping whitespace, that a substring ends in a terminal char, or ends without further chars
175 *
176 * @param[in] in the sbuff to check.
177 * @param[in] p_rules to use terminals from.
178 * @return
179 * - true if substr is terminated correctly.
180 * - false if subst is not terminated correctly.
181 */
182static inline bool CC_HINT(always_inline) tmpl_substr_terminal_check(fr_sbuff_t *in,
183 fr_sbuff_parse_rules_t const *p_rules)
184{
186 bool ret;
187
188 if (!fr_sbuff_extend(in)) return true; /* we're at the end of the string */
189 if (!p_rules || !p_rules->terminals) return false; /* more stuff to parse but don't have a terminal set */
190
191 fr_sbuff_marker(&m, in);
192 ret = fr_sbuff_is_terminal(in, p_rules->terminals);
193 fr_sbuff_set(in, &m);
194 fr_sbuff_marker_release(&m);
195 return ret;
196}
197
198void tmpl_attr_ref_debug(const tmpl_attr_t *ar, int i)
199{
200 char buffer[sizeof(STRINGIFY(INT16_MAX)) + 1];
201
202 snprintf(buffer, sizeof(buffer), "%i", ar->ar_num);
203
204 switch (ar->type) {
208 if (!ar->da) {
209 FR_FAULT_LOG("\t[%u] %s null%s%s%s",
210 i,
211 fr_table_str_by_value(attr_table, ar->type, "<INVALID>"),
212 ar->ar_num != NUM_UNSPEC ? "[" : "",
213 ar->ar_num != NUM_UNSPEC ? fr_table_str_by_value(attr_num_table, ar->ar_num, buffer) : "",
214 ar->ar_num != NUM_UNSPEC ? "]" : "");
215 return;
216 }
217
218 FR_FAULT_LOG("\t[%u] %s %s %s%s%s%s (%p) attr %u",
219 i,
220 fr_table_str_by_value(attr_table, ar->type, "<INVALID>"),
221 fr_type_to_str(ar->da->type),
222 ar->da->name,
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 ar->da,
227 ar->da->attr
228 );
229 FR_FAULT_LOG("\t is_raw : %s", ar_is_raw(ar) ? "yes" : "no");
230 FR_FAULT_LOG("\t is_unknown : %s", ar_is_unknown(ar) ? "yes" : "no");
231 if (ar->ar_parent) FR_FAULT_LOG("\t parent : %s (%p)", ar->ar_parent->name, ar->ar_parent);
232 break;
233
234
236 /*
237 * Type reveals unresolved status
238 * so we don't need to add it explicitly
239 */
240 FR_FAULT_LOG("\t[%u] %s %s%s%s%s",
241 i,
242 fr_table_str_by_value(attr_table, ar->type, "<INVALID>"),
243 ar->ar_unresolved,
244 ar->ar_num != NUM_UNSPEC ? "[" : "",
245 ar->ar_num != NUM_UNSPEC ? fr_table_str_by_value(attr_num_table, ar->ar_num, buffer) : "",
246 ar->ar_num != NUM_UNSPEC ? "]" : "");
247 if (ar->ar_parent) FR_FAULT_LOG("\t parent : %s", ar->ar_parent->name);
248 if (ar->ar_unresolved_namespace) FR_FAULT_LOG("\t namespace : %s", ar->ar_unresolved_namespace->name);
249 break;
250
251 default:
252 FR_FAULT_LOG("\t[%u] Bad type %s(%u)",
253 i, fr_table_str_by_value(attr_table, ar->type, "<INVALID>"), ar->type);
254 break;
255 }
256}
257
258void tmpl_attr_ref_list_debug(FR_DLIST_HEAD(tmpl_attr_list) const *ar_head)
259{
260 tmpl_attr_t *ar = NULL;
261 unsigned int i = 0;
262
263 FR_FAULT_LOG("attribute references:");
264 /*
265 * Print all the attribute references
266 */
267 while ((ar = tmpl_attr_list_next(ar_head, ar))) {
268 tmpl_attr_ref_debug(ar, i);
269 i++;
270 }
271}
272
274{
275 tmpl_request_t *rr = NULL;
276 unsigned int i = 0;
277
278 switch (vpt->type) {
279 case TMPL_TYPE_ATTR:
281 break;
282
283 default:
284 FR_FAULT_LOG("%s can't print tmpls of type %s", __FUNCTION__,
285 tmpl_type_to_str(vpt->type));
286 return;
287 }
288
289 FR_FAULT_LOG("tmpl_t %s (%.8x) \"%pV\" (%p)",
290 tmpl_type_to_str(vpt->type),
291 vpt->type,
292 fr_box_strvalue_len(vpt->name, vpt->len), vpt);
293
295 FR_FAULT_LOG("\tquote : %s", fr_table_str_by_value(fr_token_quotes_table, vpt->quote, "<INVALID>"));
296
297 FR_FAULT_LOG("request references:");
298
299 /*
300 * Print all the request references
301 */
302 while ((rr = tmpl_request_list_next(&vpt->data.attribute.rr, rr))) {
303 FR_FAULT_LOG("\t[%u] %s (%u)", i,
305 i++;
306 }
307
308 FR_FAULT_LOG("list: %s", tmpl_list_name(tmpl_list(vpt), "<INVALID>"));
310}
311
313{
314 switch (vpt->type) {
315 case TMPL_TYPE_ATTR:
318 return;
319
320 default:
321 break;
322 }
323
324 FR_FAULT_LOG("tmpl_t %s (%.8x) \"%pR\" (%p)",
325 tmpl_type_to_str(vpt->type),
326 vpt->type,
327 fr_box_strvalue_len(vpt->name, vpt->len), vpt);
328
330 FR_FAULT_LOG("\tquote : %s", fr_table_str_by_value(fr_token_quotes_table, vpt->quote, "<INVALID>"));
331 switch (vpt->type) {
332 case TMPL_TYPE_NULL:
333 return;
334
335 case TMPL_TYPE_DATA:
337 FR_FAULT_LOG("\tlen : %zu", tmpl_value_length(vpt));
338 FR_FAULT_LOG("\tvalue : %pV", tmpl_value(vpt));
339
340 if (tmpl_value_enumv(vpt)) FR_FAULT_LOG("\tenumv : %s (%p)",
342 return;
343
344 case TMPL_TYPE_XLAT:
345 case TMPL_TYPE_EXEC:
347 {
348 char *str;
349
350 xlat_aprint(NULL, &str, tmpl_xlat(vpt), NULL);
351
352 FR_FAULT_LOG("\texpansion : %pR", fr_box_strvalue_buffer(str));
353
354 talloc_free(str);
355 }
356 break;
357
358 case TMPL_TYPE_REGEX:
359 {
360 FR_FAULT_LOG("\tpattern : %pR", fr_box_strvalue_len(vpt->name, vpt->len));
361 }
362 break;
363
364 default:
367 FR_FAULT_LOG("\tunescaped : %pR", fr_box_strvalue_buffer(vpt->data.unescaped));
368 FR_FAULT_LOG("\tlen : %zu", talloc_array_length(vpt->data.unescaped) - 1);
369 } else {
370 FR_FAULT_LOG("\tunresolved : %pR", fr_box_strvalue_len(vpt->name, vpt->len));
371 FR_FAULT_LOG("\tlen : %zu", vpt->len);
372 }
373 } else {
374 FR_FAULT_LOG("debug nyi");
375 }
376 break;
377 }
378}
379
380/** @name Parse list and request qualifiers to #fr_pair_list_t and #tmpl_request_ref_t values
381 *
382 * These functions also resolve #fr_pair_list_t and #tmpl_request_ref_t values to #request_t
383 * structs and the head of #fr_pair_t lists in those structs.
384 *
385 * For adding new #fr_pair_t to the lists, the #tmpl_list_ctx function can be used
386 * to obtain the appropriate TALLOC_CTX pointer.
387 *
388 * @note These don't really have much to do with #tmpl_t. They're in the same
389 * file as they're used almost exclusively by the tmpl_* functions.
390 * @{
391 */
392
393/** Parse one a single list reference
394 *
395 * @param[out] da_p attribute representing a list.
396 * @param[in] in Sbuff to read request references from.
397 * @return
398 * - > 0 the number of bytes parsed.
399 * - 0 no list qualifier found.
400 */
402{
403 fr_dict_attr_t const *da;
404 fr_sbuff_t our_in = FR_SBUFF(in);
405
406 if (((fr_sbuff_adv_past_strcase(&our_in, request_attr_request->name, request_attr_request->name_len)) &&
407 (da = request_attr_request)) ||
409 (da = request_attr_reply)) ||
411 (da = request_attr_control)) ||
413 (da = request_attr_state))) {
414 /* note: no local variables */
415 *da_p = da;
416 FR_SBUFF_SET_RETURN(in, &our_in);
417 }
418
419 return 0;
420}
421
422 /** Allocate a new request reference and add it to the end of the attribute reference list
423 *
424 */
425static inline CC_HINT(always_inline) CC_HINT(nonnull(2,3))
426void tmpl_request_ref_list_copy(TALLOC_CTX *ctx,
427 FR_DLIST_HEAD(tmpl_request_list) *out, FR_DLIST_HEAD(tmpl_request_list) const *in)
428{
429 tmpl_request_t *rr = NULL;
430 tmpl_request_t *n_rr = NULL;
431
432 /*
433 * Duplicate the complete default list
434 */
435 while ((rr = tmpl_request_list_next(in, rr))) {
436 MEM(n_rr = talloc(ctx, tmpl_request_t));
437 *n_rr = (tmpl_request_t){
438 .request = rr->request
439 };
440 tmpl_request_list_insert_tail(out, n_rr);
441 ctx = n_rr; /* Chain the contexts */
442 }
443}
444
445 /** Allocate a new request reference list and copy request references into it
446 *
447 */
448static inline CC_HINT(always_inline) CC_HINT(nonnull(2,3))
449void tmpl_request_ref_list_acopy(TALLOC_CTX *ctx,
450 FR_DLIST_HEAD(tmpl_request_list) **out, FR_DLIST_HEAD(tmpl_request_list) const *in)
451{
452 FR_DLIST_HEAD(tmpl_request_list) *rql;
453
454 MEM(rql = talloc_zero(ctx, FR_DLIST_HEAD(tmpl_request_list)));
455 tmpl_request_list_talloc_init(rql);
456
458
459 *out = rql;
460}
461
462/** Dump a request list to stderr
463 *
464 */
465void tmpl_request_ref_list_debug(FR_DLIST_HEAD(tmpl_request_list) const *rql)
466{
467 tmpl_request_t *rr = NULL;
468
469 while ((rr = tmpl_request_list_next(rql, rr))) {
470 FR_FAULT_LOG("request - %s (%u)",
472 rr->request);
473 }
474}
475
476/** Compare a list of request qualifiers
477 *
478 * @param[in] a first list. If NULL tmpl_request_def_current will be used.
479 * @param[in] b second list. If NULL tmpl_request_def_current will be used.
480 * @return
481 * - >0 a > b
482 * - 0 a == b
483 * - <0 a < b
484 */
485int8_t tmpl_request_ref_list_cmp(FR_DLIST_HEAD(tmpl_request_list) const *a, FR_DLIST_HEAD(tmpl_request_list) const *b)
486{
487 tmpl_request_t *a_rr = NULL, *b_rr = NULL;
488
489 /*
490 * NULL, uninit, empty are all equivalent
491 * to tmpl_request_def_current.
492 *
493 * We need all these equivalent checks to
494 * deal with uninitialised tmpl rules.
495 */
496 if (!a || !tmpl_request_list_initialised(a) || tmpl_request_list_empty(a)) a = &tmpl_request_def_current;
497 if (!b || !tmpl_request_list_initialised(b) || tmpl_request_list_empty(b)) b = &tmpl_request_def_current;
498
499 /*
500 * Fast path...
501 */
502 if (a == b) return 0;
503
504 for (;;) {
505 a_rr = tmpl_request_list_next(a, a_rr);
506 b_rr = tmpl_request_list_next(b, b_rr);
507
508 if (!a_rr || !b_rr) return CMP(tmpl_request_list_num_elements(a), tmpl_request_list_num_elements(b));
509
510 CMP_RETURN(a_rr, b_rr, request);
511 }
512}
513
514/** Parse one or more request references, writing the list to out
515 *
516 * @param[in] ctx to allocate request refs in.
517 * @param[out] err If !NULL where to write the parsing error.
518 * @param[in] out The list to write to.
519 * @param[in] in Sbuff to read request references from.
520 * @param[in] p_rules Parse rules.
521 * @param[in] t_rules Default list and other rules.
522 * @param[out] namespace the namespace to use
523 * @return
524 * - >= 0 the number of bytes parsed.
525 * - <0 negative offset for where the error occurred
526 */
528 FR_DLIST_HEAD(tmpl_request_list) *out,
529 fr_sbuff_t *in,
530 fr_sbuff_parse_rules_t const *p_rules,
531 tmpl_rules_t const *t_rules,
532 fr_dict_attr_t const **namespace)
533{
535 tmpl_request_t *rr;
536 size_t ref_len;
537 fr_sbuff_t our_in = FR_SBUFF(in);
538 tmpl_request_t *tail = tmpl_request_list_tail(out);
539 unsigned int depth = 0;
541 tmpl_attr_rules_t const *at_rules;
543
544 if (!t_rules) {
545 at_rules = &default_rules.attr;
546 } else {
547 at_rules = &t_rules->attr;
548 }
549
550 /*
551 * The caller wants to know the default namespace for
552 * resolving the attribute.
553 *
554 * @todo - why not use dict_def if it's set? Tho TBH we
555 * should probably just remove dict_def, and always use "namespace".
556 */
557 if (namespace) {
558 if (at_rules->namespace) {
559 *namespace = at_rules->namespace;
560 } else {
561 *namespace = NULL;
562 }
563 }
564
565 /*
566 * We could make the caller do this but as this
567 * function is intended to help populate tmpl rules,
568 * just be nice...
569 */
570 if (!tmpl_request_list_initialised(out)) tmpl_request_list_talloc_init(out);
571
572 fr_sbuff_marker(&m, &our_in);
574 bool end;
575
576 /*
577 * Search for a known request reference like
578 * 'current', or 'parent'.
579 */
581
582 /*
583 * No match
584 */
585 if (ref_len == 0) {
586 /*
587 * If depth == 0, we're at the start
588 * so just use the default request
589 * reference.
590 */
591 default_ref:
592 if ((depth == 0) && at_rules->request_def) {
593 tmpl_request_ref_list_copy(ctx, out, at_rules->request_def);
594 }
595 break;
596 }
597
598 /*
599 * We don't want to misidentify the list
600 * as being part of an attribute.
601 */
602 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))) {
603 goto default_ref;
604 }
605
606 if (depth == 0) {
607 if (at_rules->namespace || (at_rules->list_presence == TMPL_ATTR_LIST_FORBID)) {
608 fr_strerror_const("List qualifiers are not allowed here");
610
611 fr_sbuff_set(&our_in, in); /* Marker at the start */
612 error:
613 tmpl_request_list_talloc_free_to_tail(out, tail);
614 FR_SBUFF_ERROR_RETURN(&our_in);
615 }
616 }
617
618 /*
619 * If the caller is asking for a namespace, then walk back up the tmpl_rules_t to find a parent namespace.
620 */
621 if (namespace && t_rules && t_rules->parent) {
622 t_rules = t_rules->parent;
623
624 switch (ref) {
625 case REQUEST_OUTER:
626 while (t_rules->parent) t_rules = t_rules->parent; /* Walk back to the root */
628
629 case REQUEST_PARENT:
630 if (t_rules->attr.namespace) {
631 *namespace = t_rules->attr.namespace;
632 } else if (t_rules->attr.dict_def) {
633 *namespace = fr_dict_root(t_rules->attr.dict_def);
634 } else {
635 *namespace = NULL;
636 }
637 break;
638
639 default:
640 break;
641 }
642 }
643
644 /*
645 * Add a new entry to the dlist
646 */
647 MEM(rr = talloc(ctx, tmpl_request_t));
648 *rr = (tmpl_request_t){
649 .request = ref
650 };
651 tmpl_request_list_insert_tail(out, rr);
652
653 /*
654 * Advance past the separator (if there is one)
655 */
656 end = !fr_sbuff_next_if_char(&our_in, '.');
657
658 /*
659 * Update to the last successfully parsed component
660 *
661 * This makes it easy to backtrack from refs like
662 *
663 * parent.outer-realm-name
664 */
665 fr_sbuff_set(&m, &our_in);
666
667 if (end) break;
668 }
669
670 /*
671 * Nesting level too deep
672 */
674 fr_strerror_const("Request ref nesting too deep");
676 goto error; /* Leave marker at the end */
677 }
678
680}
681
682/** Parse one or more request references, allocing a new list and adding the references to it
683 *
684 * This can be used to create request ref lists for rules and for tmpls.
685 *
686 * @param[in] ctx to allocate request refs in.
687 * @param[out] err If !NULL where to write the parsing error.
688 * @param[out] out The new list.
689 * @param[in] in Sbuff to read request references from.
690 * @return
691 * - >= 0 the number of bytes parsed.
692 * - <0 negative offset for where the error occurred
693 */
695 FR_DLIST_HEAD(tmpl_request_list) **out,
696 fr_sbuff_t *in)
697{
698 fr_slen_t slen;
699
700 FR_DLIST_HEAD(tmpl_request_list) *rql;
701
702 MEM(rql = talloc_zero(ctx, FR_DLIST_HEAD(tmpl_request_list)));
703 tmpl_request_list_talloc_init(rql);
704
705 slen = tmpl_request_ref_list_from_substr(rql, err, rql, in, NULL, NULL, NULL);
706 if (slen < 0) {
707 talloc_free(rql);
708 return slen;
709 }
710
711 *out = rql;
712
713 return slen;
714}
715/** @} */
716
717/** @name Alloc or initialise #tmpl_t
718 *
719 * @note Should not usually be called outside of tmpl_* functions, use one of
720 * the tmpl_*from_* functions instead.
721 * @{
722 */
723
724/** Initialise fields inside a tmpl depending on its type
725 *
726 */
727static inline CC_HINT(always_inline) void tmpl_type_init(tmpl_t *vpt, tmpl_type_t type)
728{
729
730 switch (type) {
731#ifndef HAVE_REGEX
732 case TMPL_TYPE_REGEX:
736 fr_assert(0);
737 return;
738#endif
739
740 case TMPL_TYPE_ATTR:
742 tmpl_attr_list_talloc_init(tmpl_attr(vpt));
743 tmpl_request_list_talloc_init(&vpt->data.attribute.rr);
744 break;
745
746 default:
747 break;
748 }
749 vpt->type = type;
750 }
751
752/** Set the name on a pre-initialised tmpl
753 *
754 * @param[in] vpt to set the name for.
755 * @param[in] quote Original quoting around the name.
756 * @param[in] fmt string.
757 * @param[in] ... format arguments.
758 */
759void tmpl_set_name_printf(tmpl_t *vpt, fr_token_t quote, char const *fmt, ...)
760{
761 va_list ap;
762 char const *old = NULL;
763
764 if (vpt->type != TMPL_TYPE_UNINITIALISED) old = vpt->name;
765
766 va_start(ap, fmt);
767 vpt->name = fr_vasprintf(vpt, fmt, ap);
768 vpt->quote = quote;
769 vpt->len = talloc_array_length(vpt->name) - 1;
770 va_end(ap);
771
772 talloc_const_free(old); /* Free name last so it can be used in the format string */
773}
774
775/** Set the name on a pre-initialised tmpl
776 *
777 * @param[in] vpt to set the name for.
778 * @param[in] quote Original quoting around the name.
779 * @param[in] name of the #tmpl_t.
780 * @param[in] len The length of the buffer (or a substring of the buffer) pointed to by name.
781 * If < 0 strlen will be used to determine the length.
782 */
783void tmpl_set_name_shallow(tmpl_t *vpt, fr_token_t quote, char const *name, ssize_t len)
784{
786
787 vpt->name = name;
788 vpt->len = len < 0 ? strlen(name) : (size_t)len;
789 vpt->quote = quote;
790}
791
792/** Set the name on a pre-initialised tmpl
793 *
794 * @param[in] vpt to set the name for.
795 * @param[in] quote Original quoting around the name.
796 * @param[in] name of the #tmpl_t.
797 * @param[in] len The length of the buffer (or a substring of the buffer) pointed to by name.
798 * If < 0 strlen will be used to determine the length.
799 */
800void tmpl_set_name(tmpl_t *vpt, fr_token_t quote, char const *name, ssize_t len)
801{
803
804 talloc_const_free(vpt->name);
805
806 vpt->name = talloc_bstrndup(vpt, name, len < 0 ? strlen(name) : (size_t)len);
807 vpt->len = talloc_array_length(vpt->name) - 1;
808 vpt->quote = quote;
809}
810
811/** Change the default dictionary in the tmpl's resolution rules
812 *
813 * @param[in] vpt to alter.
814 * @param[in] dict to set.
815 */
817{
818 vpt->rules.attr.dict_def = dict;
819}
820
821/** Set escape parameters for the tmpl output
822 *
823 * @param[in] vpt to alter.
824 * @param[in] escape to set.
825 */
827{
828 vpt->rules.escape = *escape;
829}
830
831/** Change the default dictionary in the tmpl's resolution rules
832 *
833 * @param[in] vpt to alter.
834 * @param[in] xlat to set.
835 */
837{
838 fr_assert(vpt->type == TMPL_TYPE_XLAT);
839
840 tmpl_xlat(vpt) = xlat;
841}
842
843
844/** Initialise a tmpl using a format string to create the name
845 *
846 * @param[in] vpt to initialise.
847 * @param[in] type of tmpl to initialise.
848 * @param[in] quote Original quoting around the name.
849 * @param[in] fmt string.
850 * @param[in] ... format arguments.
851 * @return A pointer to the newly initialised tmpl.
852 */
854{
855 va_list ap;
856
857 memset(vpt, 0, sizeof(*vpt));
859
860 va_start(ap, fmt);
861 vpt->name = fr_vasprintf(vpt, fmt, ap);
862 vpt->len = talloc_array_length(vpt->name) - 1;
863 vpt->quote = quote;
864 va_end(ap);
865
866 return vpt;
867}
868
869/** Initialise a tmpl without copying the input name string
870 *
871 * @note Name is not talloc_strdup'd or memcpy'd so must be available, and must not change
872 * for the lifetime of the #tmpl_t.
873 *
874 * @param[out] vpt to initialise.
875 * @param[in] type to set in the #tmpl_t.
876 * @param[in] quote The type of quoting around the template name.
877 * @param[in] name of the #tmpl_t.
878 * @param[in] len The length of the buffer (or a substring of the buffer) pointed to by name.
879 * If < 0 strlen will be used to determine the length.
880 * @param[in] t_rules used during parsing.
881 * @return a pointer to the initialised #tmpl_t. The same value as vpt.
882 */
884 char const *name, ssize_t len, tmpl_rules_t const *t_rules)
885{
886 memset(vpt, 0, sizeof(*vpt));
888 tmpl_set_name_shallow(vpt, quote, name, len);
889 if (t_rules) vpt->rules = *t_rules;
890
891 return vpt;
892}
893
894/** Initialise a tmpl using a literal string to create the name
895 *
896 * @param[in] vpt to initialise.
897 * @param[in] type of tmpl to initialise.
898 * @param[in] quote Original quoting around the name.
899 * @param[in] name to set for the tmpl.
900 * @param[in] len Name length. If < 0 strlen will be used
901 * to determine the name.
902 * @param[in] t_rules used during parsing.
903 * @return A pointer to the newly initialised tmpl.
904 */
906 char const *name, ssize_t len, tmpl_rules_t const *t_rules)
907{
908 memset(vpt, 0, sizeof(*vpt));
910 tmpl_set_name(vpt, quote, name, len);
911 if (t_rules) vpt->rules = *t_rules;
912
913 return vpt;
914}
915
916/** Create a new heap allocated #tmpl_t
917 *
918 * Must be later initialised with a tmpl_init_* function.
919 *
920 * This function is provided to allow tmpls to be pre-allocated for talloc purposes before
921 * their name is known.
922 */
923static inline CC_HINT(always_inline) tmpl_t *tmpl_alloc_null(TALLOC_CTX *ctx)
924{
925 tmpl_t *vpt;
926
927 /*
928 * Allocate enough memory to hold at least
929 * one attribute reference and one request
930 * reference.
931 */
932 MEM(vpt = talloc_pooled_object(ctx, tmpl_t, 2, sizeof(tmpl_request_t) + sizeof(tmpl_attr_t)));
934
935 return vpt;
936}
937
938/** Create a new heap allocated #tmpl_t
939 *
940 * @param[in,out] ctx to allocate in.
941 * @param[in] type to set in the #tmpl_t.
942 * @param[in] name of the #tmpl_t (will be copied to a new talloc buffer parented
943 * by the #tmpl_t).
944 * @param[in] len The length of the buffer (or a substring of the buffer) pointed to by name.
945 * If < 0 strlen will be used to determine the length.
946 * @param[in] quote The type of quoting around the template name.
947 * @return the newly allocated #tmpl_t.
948 */
949tmpl_t *tmpl_alloc(TALLOC_CTX *ctx, tmpl_type_t type, fr_token_t quote, char const *name, ssize_t len)
950{
951 tmpl_t *vpt;
952
953 vpt = tmpl_alloc_null(ctx);
954 memset(vpt, 0, sizeof(*vpt));
955
957 if (name) tmpl_set_name(vpt, quote, name, len);
958
959 return vpt;
960}
961/** @} */
962
963/** @name Create new #tmpl_t from a string
964 *
965 * @{
966 */
967
968/** Allocate a new attribute reference and add it to the end of the attribute reference list
969 *
970 */
972{
973 tmpl_attr_t *ar;
974 TALLOC_CTX *ctx;
975
976 if (tmpl_attr_list_num_elements(tmpl_attr(vpt)) == 0) {
977 ctx = vpt;
978 } else {
979 ctx = tmpl_attr_list_tail(tmpl_attr(vpt));
980 }
981
982 MEM(ar = talloc(ctx, tmpl_attr_t));
983 *ar = (tmpl_attr_t){
984 .type = type,
985 .filter = {
987 .num = NUM_UNSPEC
988 }
989 };
990 tmpl_attr_list_insert_tail(tmpl_attr(vpt), ar);
991
992 return ar;
993}
994
995/** Create a #tmpl_t from a #fr_value_box_t
996 *
997 * @param[in,out] ctx to allocate #tmpl_t in.
998 * @param[out] out Where to write pointer to new #tmpl_t.
999 * @param[in] data to convert.
1000 * @param[in] steal If true, any buffers are moved to the new
1001 * ctx instead of being duplicated.
1002 * @return
1003 * - 0 on success.
1004 * - -1 on failure.
1005 */
1006int tmpl_afrom_value_box(TALLOC_CTX *ctx, tmpl_t **out, fr_value_box_t *data, bool steal)
1007{
1008 char *name;
1009 fr_slen_t slen;
1010 tmpl_t *vpt;
1012
1013 MEM(vpt = talloc(ctx, tmpl_t));
1015 if (slen < 0) {
1016 error:
1018 return -1;
1019 }
1020
1021 tmpl_init_shallow(vpt, TMPL_TYPE_DATA, quote, name, slen, NULL);
1022
1023 if (steal) {
1024 if (fr_value_box_steal(vpt, tmpl_value(vpt), data) < 0) goto error;
1025 } else {
1026 if (fr_value_box_copy(vpt, tmpl_value(vpt), data) < 0) goto error;
1027 }
1028 *out = vpt;
1029
1030 return 0;
1031}
1032
1033/** Copy a list of attribute and request references from one tmpl to another
1034 *
1035 */
1036int tmpl_attr_copy(tmpl_t *dst, tmpl_t const *src)
1037{
1038 tmpl_attr_t *src_ar = NULL, *dst_ar;
1039
1040 /*
1041 * Clear any existing attribute references
1042 */
1043 if (tmpl_attr_list_num_elements(tmpl_attr(dst)) > 0) tmpl_attr_list_talloc_reverse_free(tmpl_attr(dst));
1044
1045 while ((src_ar = tmpl_attr_list_next(tmpl_attr(src), src_ar))) {
1046 dst_ar = tmpl_attr_add(dst, src_ar->type);
1047
1048 switch (src_ar->type) {
1050 dst_ar->ar_da = src_ar->ar_da;
1051 break;
1052
1053 case TMPL_ATTR_TYPE_UNSPEC: /* Nothing to copy */
1054 break;
1055
1057 dst_ar->ar_unknown = fr_dict_attr_unknown_copy(dst_ar, src_ar->ar_unknown);
1058 break;
1059
1061 dst_ar->ar_unresolved = talloc_bstrdup(dst_ar, src_ar->ar_unresolved);
1062 break;
1063
1064 default:
1065 if (!fr_cond_assert(0)) return -1;
1066 }
1067 dst_ar->ar_num = src_ar->ar_num;
1068 dst_ar->ar_filter_type = src_ar->ar_filter_type;
1069 dst_ar->parent = src_ar->parent;
1070 }
1071
1072 /*
1073 * Clear any existing request references
1074 * and copy the ones from the source.
1075 */
1076 tmpl_request_list_talloc_reverse_free(&dst->data.attribute.rr);
1077 tmpl_request_ref_list_copy(dst, &dst->data.attribute.rr, &src->data.attribute.rr);
1078
1079 TMPL_ATTR_VERIFY(dst);
1080
1081 return 0;
1082}
1083
1084/** Replace the current attribute reference
1085 *
1086 */
1088{
1089 tmpl_attr_t *ref;
1090
1092
1093 /*
1094 * Clear any existing references
1095 */
1096 if (tmpl_attr_list_num_elements(tmpl_attr(vpt)) > 0) {
1097 tmpl_attr_list_talloc_reverse_free(tmpl_attr(vpt));
1098 }
1099
1100 /*
1101 * Unknown attributes get copied
1102 */
1103 if (da->flags.is_unknown) {
1105 ref->da = ref->ar_unknown = fr_dict_attr_unknown_copy(vpt, da);
1106 } else {
1108 ref->da = da;
1109 }
1110 ref->ar_parent = fr_dict_root(fr_dict_by_da(da)); /* Parent is the root of the dictionary */
1111
1113
1114 return 0;
1115}
1116
1117/** Replace the leaf attribute only
1118 *
1119 */
1121{
1122 tmpl_attr_t *ref, *parent = NULL;
1123
1126
1127 /*
1128 * Clear any existing references
1129 */
1130 if (tmpl_attr_list_num_elements(tmpl_attr(vpt)) > 0) {
1131 if (tmpl_attr_list_num_elements(tmpl_attr(vpt)) > 1) {
1132 ref = tmpl_attr_list_tail(tmpl_attr(vpt));
1133 parent = tmpl_attr_list_prev(tmpl_attr(vpt), ref);
1134
1135 if (!fr_dict_attr_common_parent(parent->ar_da, da, true)) {
1136 fr_strerror_const("New leaf da and old leaf da do not share the same ancestor");
1137 return -1;
1138 }
1139 } else {
1140 ref = tmpl_attr_list_tail(tmpl_attr(vpt));
1141 }
1142
1143 /*
1144 * Free old unknown and unresolved attributes...
1145 */
1146 talloc_free_children(ref);
1147
1148 /*
1149 *
1150 */
1151 ref->ar_filter_type = TMPL_ATTR_FILTER_TYPE_NONE;
1152 ref->ar_num = NUM_UNSPEC;
1153
1154 } else {
1155 ref = tmpl_attr_add(vpt, da->flags.is_unknown ? TMPL_ATTR_TYPE_UNKNOWN : TMPL_ATTR_TYPE_NORMAL);
1156 }
1157
1158
1159 /*
1160 * Unknown attributes get copied
1161 */
1162 if (da->flags.is_unknown) {
1163 ref->da = ref->ar_unknown = fr_dict_attr_unknown_copy(vpt, da);
1164 } else {
1165 ref->da = da;
1166 }
1167
1168 /*
1169 * FIXME - Should be calculated from existing ar
1170 */
1171 ref->ar_parent = fr_dict_root(fr_dict_by_da(da)); /* Parent is the root of the dictionary */
1172
1174
1175 return 0;
1176}
1177
1178/** Rewrite the leaf's instance number
1179 *
1180 * This function is _only_ called from the compiler, for "update" and "foreach" keywords. In those cases,
1181 * the user historically did "&foo-bar", but really meant "&foo-bar[*]". We silently update that for
1182 * "update" sections, and complain about it in "foreach" sections.
1183 *
1184 * As the server now supports multiple types of leaf references, we do the rewrite _only_ from "none" (no
1185 * filter), OR where it's a numerical index, AND the index hasn't been specified.
1186 */
1188{
1189 tmpl_attr_t *ref = NULL;
1190
1192
1193 if (tmpl_attr_list_num_elements(tmpl_attr(vpt)) == 0) return;
1194
1195 ref = tmpl_attr_list_tail(tmpl_attr(vpt));
1196
1197 if (ref->ar_filter_type == TMPL_ATTR_FILTER_TYPE_NONE) {
1198 ref->ar_filter_type = TMPL_ATTR_FILTER_TYPE_INDEX;
1199 ref->ar_num = to;
1200
1201 } else if (ref->ar_filter_type != TMPL_ATTR_FILTER_TYPE_INDEX) {
1202 return;
1203
1204 } else if (ref->ar_num == NUM_UNSPEC) {
1205 ref->ar_num = to;
1206 }
1207
1209}
1210
1211/** Set the request for an attribute ref
1212 *
1213 */
1214void tmpl_attr_set_request_ref(tmpl_t *vpt, FR_DLIST_HEAD(tmpl_request_list) const *request_def)
1215{
1216 fr_assert_msg(tmpl_is_attr(vpt), "Expected tmpl type 'attr', got '%s'",
1217 tmpl_type_to_str(vpt->type));
1218
1219 /*
1220 * Clear any existing request references
1221 */
1222 tmpl_request_list_talloc_reverse_free(&vpt->data.attribute.rr);
1223 tmpl_request_ref_list_copy(vpt, &vpt->data.attribute.rr, request_def);
1224
1226}
1227
1229{
1230 tmpl_attr_t *ref = tmpl_attr_list_head(tmpl_attr(vpt));
1231 if (tmpl_attr_is_list_attr(ref)) ref->da = list;
1232
1234}
1235
1236/** Create a new tmpl from a list tmpl and a da
1237 *
1238 */
1239int tmpl_attr_afrom_list(TALLOC_CTX *ctx, tmpl_t **out, tmpl_t const *list, fr_dict_attr_t const *da)
1240{
1241 tmpl_t *vpt;
1242 tmpl_attr_t *ar;
1243
1244 char attr[256];
1245 ssize_t slen;
1246
1247 MEM(vpt = tmpl_alloc(ctx, TMPL_TYPE_ATTR, T_BARE_WORD, NULL, 0));
1248
1249 /*
1250 * Copies request refs and the list ref
1251 */
1252 tmpl_attr_copy(vpt, list);
1254
1255 if (da->flags.is_unknown) {
1257 ar->da = ar->ar_unknown = fr_dict_attr_unknown_copy(vpt, da);
1258 } else {
1260 ar->ar_da = da;
1261 }
1262
1263 ar->ar_parent = fr_dict_root(fr_dict_by_da(da));
1264
1265 /*
1266 * We need to rebuild the attribute name, to be the
1267 * one we copied from the source list.
1268 */
1269 slen = tmpl_print(&FR_SBUFF_OUT(attr, sizeof(attr)), vpt, TMPL_ATTR_REF_PREFIX_YES,
1270 fr_value_escape_by_quote[list->quote]);
1271 if (slen < 0) {
1272 fr_strerror_printf("Serialized attribute too long. Must be < "
1273 STRINGIFY(sizeof(attr)) " bytes, got %zu bytes", (size_t)-slen);
1275 return -1;
1276 }
1277
1278 vpt->len = (size_t)slen;
1279 vpt->name = talloc_typed_strdup(vpt, attr);
1280 vpt->quote = T_BARE_WORD;
1281
1283
1284 *out = vpt;
1285
1286 return 0;
1287}
1288/** @} */
1289
1290/** Insert an attribute reference into a tmpl
1291 *
1292 * Not all attribute references can be used to create new attributes,
1293 * for example those accessing instance > 0 or those that resolve
1294 * to special indexes.
1295 *
1296 * We mark up these references and their parents as resolve only
1297 * meaning that if any code needs to use a reference chain to build
1298 * out a pair tree, it bails out early.
1299 *
1300 * @param[in] vpt containing the reference list.
1301 * @param[in] ar to insert and check.
1302 */
1303static inline CC_HINT(always_inline) void tmpl_attr_insert(tmpl_t *vpt, tmpl_attr_t *ar)
1304{
1305 /*
1306 * Insert the reference into the list.
1307 */
1308 tmpl_attr_list_insert_tail(tmpl_attr(vpt), ar);
1309
1310 switch (ar->ar_num) {
1311 case 0:
1312 case NUM_UNSPEC:
1313 break;
1314
1315 default:
1316 ar->resolve_only = true;
1317 while ((ar = tmpl_attr_list_prev(tmpl_attr(vpt), ar))) ar->resolve_only = true;
1318 break;
1319 }
1320}
1321
1322/** Parse array subscript and in future other filters
1323 *
1324 * @param[out] err Parse error code.
1325 * @param[in] ar to populate filter for.
1326 * @param[in] name containing more attribute ref data.
1327 * @param[in] at_rules see tmpl_attr_afrom_attr_substr.
1328 * @return
1329 * - >0 if a filter was parsed.
1330 * - 0 if no filter was available.
1331 * - <0 on filter parse error.
1332 */
1334 fr_sbuff_t *name, tmpl_attr_rules_t const *at_rules)
1335{
1336 fr_sbuff_t our_name = FR_SBUFF(name);
1337
1338 /*
1339 * Parse array subscript (and eventually complex filters)
1340 */
1341 if (!fr_sbuff_next_if_char(&our_name, '[')) return 0;
1342
1343 if (at_rules->disallow_filters || tmpl_attr_is_list_attr(ar)) {
1344 fr_strerror_const("Filters not allowed here");
1346 fr_sbuff_set_to_start(&our_name);
1347 FR_SBUFF_ERROR_RETURN(&our_name);
1348 }
1349
1350 ar->ar_filter_type = TMPL_ATTR_FILTER_TYPE_INDEX;
1351 fr_sbuff_switch(&our_name, '\0') {
1352 case '#':
1353 ar->ar_num = NUM_COUNT;
1354 fr_sbuff_next(&our_name);
1355 break;
1356
1357 case '*':
1358 ar->ar_num = NUM_ALL;
1359 fr_sbuff_next(&our_name);
1360 break;
1361
1362 case '0':
1363 case '1':
1364 case '2':
1365 case '3':
1366 case '4':
1367 case '5':
1368 case '6':
1369 case '7':
1370 case '8':
1371 case '9':
1372 {
1373 ssize_t rcode;
1375 fr_sbuff_t tmp = FR_SBUFF(&our_name);
1376
1377 /*
1378 * All digits (not hex).
1379 */
1380 rcode = fr_sbuff_out(&sberr, &ar->ar_num, &tmp);
1381 if ((rcode < 0) || !fr_sbuff_is_char(&tmp, ']')) goto parse_tmpl;
1382
1383 if ((ar->ar_num > 1000) || (ar->ar_num < 0)) {
1384 fr_strerror_printf("Invalid array index '%hi' (should be between 0-1000)", ar->ar_num);
1385 ar->ar_num = 0;
1386 goto error;
1387 }
1388
1389 fr_sbuff_set(&our_name, &tmp); /* Advance name _AFTER_ doing checks */
1390 break;
1391 }
1392
1393 case '"':
1394 case '\'':
1395 case '`':
1396 case '/':
1397 fr_strerror_const("Invalid data type for array index");
1398 goto error;
1399
1400 /* Used as EOB here */
1401 missing_closing:
1402 case '\0':
1403 fr_strerror_const("No closing ']' for array index");
1404 error:
1406 FR_SBUFF_ERROR_RETURN(&our_name);
1407
1408 case '(': /* (...) expression */
1409 {
1410 fr_sbuff_t tmp = FR_SBUFF(&our_name);
1411 fr_slen_t slen;
1412 tmpl_rules_t t_rules;
1413 fr_sbuff_parse_rules_t p_rules;
1414 fr_sbuff_term_t const filter_terminals = FR_SBUFF_TERMS(L("]"));
1415
1416 /*
1417 * For now, we don't allow filtering on leaf values. e.g.
1418 *
1419 * &User-Name[(&User-Name == foo)]
1420 *
1421 * @todo - find some sane way of allowing this, without mangling the xlat expression
1422 * parser too badly. The simplest way is likely to just parse the expression, and then
1423 * walk over it, complaining if it contains attribute references other than &User-Name.
1424 *
1425 * Anything else is likely too hard.
1426 *
1427 * We also want to disallow basic conditions like "true" or "false". The conditions
1428 * should only be using attribute references, and those attribute references should be
1429 * limited to certain attributes.
1430 *
1431 * And if the filter attribute is a TLV, the condition code complains with 'Nesting types
1432 * such as groups or TLVs cannot be used in condition comparisons'. This is reasonable,
1433 * as we can't currently compare things like;
1434 *
1435 * &Group-Thingy == { &foo = bar }
1436 *
1437 * Which would involve creating the RHS list, doing an element-by-element comparison, and
1438 * then returning.
1439 *
1440 * In order to fix that, we have to
1441 */
1442 if (!ar->ar_da || !fr_type_is_structural(ar->ar_da->type)) {
1443 fr_strerror_printf("Invalid filter - cannot use filter on leaf attributes");
1444 ar->ar_num = 0;
1445 goto error;
1446 }
1447
1448 tmp = FR_SBUFF(&our_name);
1449 t_rules = (tmpl_rules_t) {};
1450 t_rules.attr = *at_rules;
1451 t_rules.attr.namespace = ar->ar_da; /* @todo - parent? */
1452
1453 p_rules = (fr_sbuff_parse_rules_t) {
1454 .terminals = &filter_terminals,
1455 .escapes = NULL
1456 };
1457
1458 /*
1459 * Check if it's a condition.
1460 */
1461 slen = xlat_tokenize_condition(ar, &ar->ar_cond, &tmp, &p_rules, &t_rules);
1462 if (slen < 0) goto error;
1463
1464 fr_assert(!xlat_impure_func(ar->ar_cond));
1465
1466 ar->ar_filter_type = TMPL_ATTR_FILTER_TYPE_CONDITION;
1467 fr_sbuff_set(&our_name, &tmp); /* Advance name _AFTER_ doing checks */
1468 break;
1469 }
1470
1471 case '%': /* ${...} expansion */
1472 {
1473 fr_sbuff_t tmp = FR_SBUFF(&our_name);
1474 fr_slen_t slen;
1475 tmpl_rules_t t_rules;
1476 fr_sbuff_parse_rules_t p_rules;
1477 fr_sbuff_term_t const filter_terminals = FR_SBUFF_TERMS(L("]"));
1478
1479 if (!fr_sbuff_is_str(&our_name, "%{", 2)) {
1480 fr_strerror_const("Invalid expression in attribute index");
1481 goto error;
1482 }
1483
1484 tmp = FR_SBUFF(&our_name);
1485 t_rules = (tmpl_rules_t) {};
1486 t_rules.attr = *at_rules;
1487
1488 p_rules = (fr_sbuff_parse_rules_t) {
1489 .terminals = &filter_terminals,
1490 .escapes = NULL
1491 };
1492
1493 /*
1494 * Check if it's an expression.
1495 */
1496 slen = xlat_tokenize_expression(ar, &ar->ar_cond, &tmp, &p_rules, &t_rules);
1497 if (slen < 0) goto error;
1498
1499 if (xlat_impure_func(ar->ar_expr)) {
1500 fr_strerror_const("Expression in attribute index cannot depend on functions which call external databases");
1501 goto error;
1502 }
1503
1504 ar->ar_filter_type = TMPL_ATTR_FILTER_TYPE_EXPR;
1505
1506 fr_sbuff_set(&our_name, &tmp); /* Advance name _AFTER_ doing checks */
1507 break;
1508 }
1509
1510 case 'n':
1511 /*
1512 * [n] is the last one
1513 *
1514 * [nope] is a reference to "nope".
1515 */
1516 if (fr_sbuff_is_str(&our_name, "n]", 2)) {
1517 ar->ar_num = NUM_LAST;
1518 fr_sbuff_next(&our_name);
1519 break;
1520 }
1522
1523 default:
1524 parse_tmpl:
1525 {
1526 fr_sbuff_t tmp = FR_SBUFF(&our_name);
1527 ssize_t slen;
1528 tmpl_rules_t t_rules;
1529 fr_sbuff_parse_rules_t p_rules;
1530 fr_sbuff_term_t const filter_terminals = FR_SBUFF_TERMS(L("]"));
1531
1532 tmp = FR_SBUFF(&our_name);
1533 t_rules = (tmpl_rules_t) {};
1534 t_rules.attr = *at_rules;
1535
1536 /*
1537 * Don't reset namespace, we always want to start searching from the top level of the
1538 * dictionaries.
1539 */
1540
1541 p_rules = (fr_sbuff_parse_rules_t) {
1542 .terminals = &filter_terminals,
1543 .escapes = NULL
1544 };
1545
1546 /*
1547 * @todo - for some reason, the tokenize_condition code allows for internal
1548 * vs protocol vs local attributes, whereas the tmpl function only accepts
1549 * internal ones.
1550 */
1551 slen = tmpl_afrom_substr(ar, &ar->ar_tmpl, &tmp, T_BARE_WORD, &p_rules, &t_rules);
1552 if (slen <= 0) goto error;
1553
1554 if (!tmpl_is_attr(ar->ar_tmpl)) {
1555 fr_strerror_const("Invalid array index");
1556 goto error;
1557 }
1558
1559 /*
1560 * Arguably we _could_ say &User-Name["foo"] matches all user-name with value "foo",
1561 * but that would confuse the issue for &Integer-Thing[4].
1562 *
1563 * For matching therefore, we really need to have a way to define "self".
1564 */
1565 if (!fr_type_numeric[tmpl_attr_tail_da(ar->ar_tmpl)->type]) {
1566 fr_strerror_const("Invalid data type for array index (must be numeric)");
1567 goto error;
1568 }
1569
1570 ar->ar_filter_type = TMPL_ATTR_FILTER_TYPE_TMPL;
1571 fr_sbuff_set(&our_name, &tmp); /* Advance name _AFTER_ doing checks */
1572 break;
1573 }
1574 }
1575
1576 /*
1577 * Always advance here, so the error
1578 * marker points to the bad char.
1579 */
1580 if (!fr_sbuff_next_if_char(&our_name, ']')) goto missing_closing;
1581
1582 FR_SBUFF_SET_RETURN(name, &our_name);
1583}
1584
1585extern fr_dict_attr_t const *tmpl_attr_unspec;
1586
1587static inline CC_HINT(nonnull(3,4))
1589 tmpl_t *vpt,
1590 fr_sbuff_t *name, tmpl_attr_rules_t const *at_rules)
1591{
1592 fr_slen_t slen;
1593
1594 *ar = (tmpl_attr_t){
1595 .ar_num = NUM_UNSPEC, /* May be changed by tmpl_attr_parse_filter */
1596 .ar_type = TMPL_ATTR_TYPE_UNSPEC,
1597 .ar_da = tmpl_attr_unspec,
1598 };
1599
1600 slen = tmpl_attr_parse_filter(err, ar, name, at_rules);
1601 if (slen < 0) {
1602 talloc_free(ar);
1603 return slen;
1604 /*
1605 * No filters and no previous elements is the equivalent of '&'
1606 * which is not allowed.
1607 *
1608 * &[<filter>] is allowed as this lets us perform filtering operations
1609 * at the root.
1610 */
1611 } else if ((slen == 0) && (tmpl_attr_num_elements(vpt) == 0)) {
1612 fr_strerror_const("Invalid attribute name");
1614 return -1;
1615 }
1616
1617 tmpl_attr_insert(vpt, ar);
1618
1619 return slen;
1620}
1621
1622/** Parse an unresolved attribute, i.e. one which can't be found in the current dictionary
1623 *
1624 * This function calls itself recursively to process additional OID
1625 * components once we've failed to resolve one component.
1626 *
1627 * @note Do not call directly.
1628 *
1629 * @param[in] ctx to allocate new attribute reference in.
1630 * @param[out] err Parse error.
1631 * @param[in,out] vpt to append this reference to.
1632 * @param[in] parent Last known parent.
1633 * @param[in] namespace in which the attribute will be resolved.
1634 * @param[in] name to parse.
1635 * @param[in] at_rules see tmpl_attr_afrom_attr_substr.
1636 * @return
1637 * - <0 on error.
1638 * - 0 on success.
1639 */
1640static inline CC_HINT(nonnull(3,6))
1642 tmpl_t *vpt,
1643 fr_dict_attr_t const *parent, fr_dict_attr_t const *namespace,
1644 fr_sbuff_t *name, tmpl_attr_rules_t const *at_rules)
1645{
1646 tmpl_attr_t *ar = NULL, *ar_curr;
1647 fr_sbuff_t our_name = FR_SBUFF(name);
1648 fr_slen_t slen;
1649 char *unresolved;
1650
1651 /*
1652 * Point we free from if something goes wrong.
1653 */
1654 ar_curr = tmpl_attr_list_tail(tmpl_attr(vpt));
1655 for (;;) {
1656 MEM(ar = talloc(ctx, tmpl_attr_t));
1657 /*
1658 * Copy out a string of allowed dictionary chars to form
1659 * the unresolved attribute name.
1660 *
1661 * This will be resolved later (outside of this function).
1662 */
1663 slen = fr_sbuff_out_abstrncpy_allowed(ar, &unresolved,
1664 &our_name, FR_DICT_ATTR_MAX_NAME_LEN + 1,
1666 if (slen == 0) {
1667 slen = tmpl_attr_ref_from_unspecified_substr(ar, err, vpt, &our_name, at_rules);
1668 if (slen < 0) {
1669 fr_sbuff_advance(&our_name, +slen);
1670 error:
1671 talloc_free(ar);
1672 tmpl_attr_list_talloc_free_to_tail(tmpl_attr(vpt), ar_curr);
1673 return -1;
1674 }
1675 return fr_sbuff_set(name, &our_name);
1676 } else if (slen > FR_DICT_ATTR_MAX_NAME_LEN) {
1677 fr_strerror_const("Attribute name is too long");
1679 goto error;
1680 }
1681
1682 *ar = (tmpl_attr_t){
1683 .ar_num = NUM_UNSPEC,
1684 .ar_type = TMPL_ATTR_TYPE_UNRESOLVED,
1685 .ar_unresolved = unresolved,
1686 .ar_unresolved_namespace = namespace,
1687 .ar_parent = parent
1688 };
1689
1690 if (tmpl_attr_parse_filter(err, ar, &our_name, at_rules) < 0) goto error;
1691
1692 /*
1693 * Insert the ar into the list of attribute references
1694 */
1695 tmpl_attr_insert(vpt, ar);
1696
1697 /*
1698 * Once one OID component is created as unresolved all
1699 * future OID components are also unresolved.
1700 */
1701 if (!fr_sbuff_next_if_char(&our_name, '.')) break;
1702 }
1703
1704 /*
1705 * Mark the tmpl up as an unresolved attribute reference
1706 * the attribute reference will be resolved later.
1707 */
1709
1710 return fr_sbuff_set(name, &our_name);
1711}
1712
1713/*
1714 * Add attr_ref when we've parsed an intermediate dictionary name
1715 * which is itself a ref.
1716 */
1717static void tmpl_attr_ref_fixup(TALLOC_CTX *ctx, tmpl_t *vpt, fr_dict_attr_t const *da, fr_dict_attr_t const *parent)
1718{
1719 tmpl_attr_t *ar;
1720
1721 if (tmpl_attr_tail_da(vpt) == da) return;
1722
1723 if (da->parent != parent) tmpl_attr_ref_fixup(ctx, vpt, da->parent, parent);
1724
1725 MEM(ar = talloc(ctx, tmpl_attr_t));
1726 *ar = (tmpl_attr_t) {
1727 .ar_num = NUM_UNSPEC,
1728 .ar_type = TMPL_ATTR_TYPE_NORMAL,
1729 .ar_da = da,
1730 .ar_parent = da->parent,
1731 };
1732
1733 tmpl_attr_insert(vpt, ar);
1734}
1735
1736/** Parse an attribute reference, either an OID or attribute name
1737 *
1738 * @note Do not call directly.
1739 *
1740 * @param[in] ctx to allocate new attribute reference in.
1741 * @param[out] err Parse error.
1742 * @param[in,out] vpt to append this reference to.
1743 * @param[in] parent Parent where the attribute will be placed (group, struct, tlv, etc).
1744 * @param[in] namespace Where the child attribute will be parsed from (dict root, struct member, TLV child, etc)
1745 * @param[in] name to parse.
1746 * @param[in] p_rules Formatting rules used to check for trailing garbage.
1747 * @param[in] at_rules which places constraints on attribute reference parsing.
1748 * Rules interpreted by this function is:
1749 * - allow_unknown - If false unknown OID components
1750 * result in a parse error.
1751 * - allow_unresolved - If false unknown attribute names
1752 * result in a parse error.
1753 * - allow_foreign - If an attribute resolves in a dictionary
1754 * that does not match the parent
1755 * (exception being FR_TYPE_GROUP) then that results
1756 * in a parse error.
1757 * @param[in] depth How deep we are. Used to check for maximum nesting level.
1758 * @return
1759 * - <0 on error.
1760 * - 0 on success.
1761 */
1762static inline int tmpl_attr_afrom_attr_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err,
1763 tmpl_t *vpt,
1764 fr_dict_attr_t const *parent, fr_dict_attr_t const *namespace,
1766 fr_sbuff_parse_rules_t const *p_rules, tmpl_attr_rules_t const *at_rules,
1767 unsigned int depth)
1768{
1769 uint32_t oid = 0;
1770 tmpl_attr_t *ar = NULL;
1771 fr_dict_attr_t const *da;
1773 fr_dict_attr_err_t dict_err;
1774 fr_dict_attr_t const *our_parent = parent;
1775
1776 fr_sbuff_marker(&m_s, name);
1777
1779 fr_strerror_const("Attribute nesting too deep");
1781 error:
1782 talloc_free(ar);
1783 fr_sbuff_marker_release(&m_s);
1785 }
1786
1787 /*
1788 * Input too short
1789 */
1790 if (!fr_sbuff_extend(name)) {
1791 fr_strerror_const("Missing attribute reference");
1793 goto error;
1794 }
1795
1796 /*
1797 * Maybe there's no child namespace (struct member, tlv child, etc). In which case we must
1798 * search from the default dictionary root.
1799 *
1800 * This search is probably wrong in some cases. See the comments below around FR_TYPE_GROUP.
1801 *
1802 * If we change out the dictionaries, we should arguably also change dict_def in the
1803 * tmpl_attr_rules_t. On top of that, the "dict_attr_search" functions take a #fr_dict_t
1804 * pointer, and not a pointer to the dict root. So we can't pass them a namespace.
1805 */
1806 if (!namespace) {
1807 fr_assert(parent == NULL);
1808
1810 at_rules->dict_def,
1811 name, p_rules ? p_rules->terminals : NULL,
1812 true,
1813 at_rules->allow_foreign);
1814 /*
1815 * The attribute was found either in the dict_def root, OR in the internal root, OR if
1816 * !dict_def && allow_foreign, in some other dictionary root.
1817 *
1818 * Otherwise we're still not sure what the attribute is. It may end up being an
1819 * unresolved one.
1820 */
1821 if (da) {
1822 our_parent = da->parent;
1823
1824 if (!our_parent->flags.is_root) {
1825 tmpl_attr_ref_fixup(ctx, vpt, our_parent, fr_dict_root(da->dict));
1826 }
1827 }
1828 } else {
1829 fr_assert(parent != NULL);
1830
1831 /*
1832 * Otherwise we're resolving the next piece in the context of where-ever we ended up from
1833 * parsing the last bit.
1834 *
1835 * The "parent" could be the same as "namespace", if both are at a dictionary root, OR
1836 * both are from a struct / tlv attribute.
1837
1838 * Or, "parent" could be a grouping attribute (e.g. request), and "namespace" could be
1839 * the dictionary root.
1840 */
1841 (void)fr_dict_attr_by_name_substr(&dict_err,
1842 &da,
1843 namespace,
1844 name,
1845 p_rules ? p_rules->terminals : NULL);
1846
1847 /*
1848 * Allow fallback to internal attributes
1849 * if the parent was a group, and we're
1850 * allowing internal resolution.
1851 *
1852 * Discard any errors here... It's more
1853 * useful to have the original.
1854 */
1855 if (!da) {
1856 ar = tmpl_attr_list_tail(&vpt->data.attribute.ar);
1857 if (!ar || ((ar->type == TMPL_ATTR_TYPE_NORMAL) && (ar->ar_da->type == FR_TYPE_GROUP))) {
1858 fr_dict_attr_t const *internal_root = fr_dict_root(fr_dict_internal());
1859
1860 (void)fr_dict_attr_by_name_substr(NULL,
1861 &da, internal_root,
1862 name,
1863 p_rules ? p_rules->terminals : NULL);
1864 if (da) {
1865 dict_err = FR_DICT_ATTR_OK;
1866 our_parent = internal_root;
1867 }
1868 }
1869 ar = NULL;
1870
1871 } else {
1872 /*
1873 * If we searched in a local dictionary, but found a real attribute
1874 * switch the namespace.
1875 */
1876 if (!da->flags.local && namespace->flags.local) namespace = our_parent = fr_dict_root(da->dict);
1877
1878 /*
1879 * We had an alias in the same namespace,
1880 * go add more things in.
1881 */
1882 if (da->parent != our_parent) {
1883 fr_assert(namespace == our_parent);
1884 tmpl_attr_ref_fixup(ctx, vpt, da->parent, our_parent);
1885 }
1886 }
1887 }
1888
1889 /*
1890 * Fatal errors related to nesting...
1891 */
1892 switch (dict_err) {
1894 fr_assert(our_parent != NULL);
1895 if (our_parent->flags.is_unknown) break;
1896 goto error;
1897
1899 goto error;
1900
1901 default:
1902 if (!da) break;
1903
1904 /*
1905 * The named component was a known attribute
1906 * so record it as a normal attribute
1907 * reference.
1908 */
1909 fr_assert(our_parent != NULL);
1910 goto alloc_ar;
1911 }
1912
1913 /*
1914 * At this point we haven't found a known attribute. What remains MUST be an OID component, OR an
1915 * unresolved attribute.
1916 *
1917 * The default is to parse the OIDs in the current namespace. If there is none, then we parse
1918 * the OIDs and unresolved attributes in the dict_def. And if that doesn't exist, in the
1919 * internal dictionaries.
1920 *
1921 * Note that we do NOT allow unknown attributes in the internal dictionary. Those attributes are
1922 * generally just DEFINEs, and their numbers have no meaning.
1923 */
1924 if (!namespace) {
1925 if (at_rules->dict_def) {
1926 our_parent = namespace = fr_dict_root(at_rules->dict_def);
1927 } else {
1928 our_parent = namespace = fr_dict_root(fr_dict_internal());
1929 }
1930 }
1931
1932 fr_assert(our_parent != NULL);
1933 fr_assert(namespace != NULL);
1934
1935 /*
1936 * See if the ref begins with an unsigned integer
1937 * if it does it's probably an OID component
1938 *
1939 * .<oid>
1940 */
1941 if (fr_sbuff_out(NULL, &oid, name) > 0) {
1942 namespace = fr_dict_unlocal(namespace);
1943
1944 fr_strerror_clear(); /* Clear out any existing errors */
1945
1946 if (fr_dict_by_da(namespace) == fr_dict_internal()) goto disallow_unknown;
1947
1948 /*
1949 * The OID component was a known attribute
1950 * so record it as a normal attribute
1951 * reference.
1952 */
1953 da = fr_dict_attr_child_by_num(namespace, oid);
1954 if (da) goto alloc_ar;
1955
1956 if (!at_rules->allow_unknown) {
1957 disallow_unknown:
1958 fr_strerror_const("Unknown attributes not allowed here");
1960 fr_sbuff_set(name, &m_s);
1961 goto error;
1962 }
1963
1964 /*
1965 * If it's numeric and not a known attribute
1966 * then we create an unknown attribute with
1967 * the specified attribute number.
1968 */
1969
1970 /*
1971 * VSAs have VENDORs as children. All others are just normal things.
1972 */
1973 switch (namespace->type) {
1974 case FR_TYPE_VSA:
1975 da = fr_dict_attr_unknown_vendor_afrom_num(ar, namespace, oid);
1976 break;
1977
1978 default:
1979 da = fr_dict_attr_unknown_raw_afrom_num(ar, namespace, oid);
1980 break;
1981 }
1982
1983 if (!da) {
1984 if (err) *err = TMPL_ATTR_ERROR_UNKNOWN_NOT_ALLOWED; /* strerror set by dict function */
1985 goto error;
1986 }
1987
1988 MEM(ar = talloc(ctx, tmpl_attr_t));
1989 *ar = (tmpl_attr_t){
1990 .ar_num = NUM_UNSPEC,
1991 .ar_type = TMPL_ATTR_TYPE_UNKNOWN,
1992 .ar_unknown = UNCONST(fr_dict_attr_t *, da),
1993 .ar_da = da,
1994 .ar_parent = our_parent,
1995 };
1996 goto do_suffix;
1997 }
1998
1999 /*
2000 * Can't parse it as an attribute, might be a literal string
2001 * let the caller decide.
2002 *
2003 * Don't alter the fr_strerror buffer, may contain useful
2004 * errors from the dictionary code.
2005 */
2006 if (!at_rules->allow_unresolved && !(at_rules->allow_wildcard && fr_sbuff_is_char(name, '['))) {
2007 fr_strerror_const_push("Unresolved attributes are not allowed here");
2009 fr_sbuff_set(name, &m_s);
2010 goto error;
2011 }
2012
2013 fr_sbuff_marker_release(&m_s);
2014
2015 /*
2016 * Once we hit one unresolved attribute we have to treat
2017 * the rest of the components are unresolved as well.
2018 */
2019 return tmpl_attr_ref_afrom_unresolved_substr(ctx, err, vpt, our_parent, namespace, name, at_rules);
2020
2021alloc_ar:
2022 /*
2023 * We have a da, remove any of the errors recorded from failed
2024 * searches to find the attribute to avoid misleading messages
2025 * if something else fails.
2026 */
2028
2029 MEM(ar = talloc(ctx, tmpl_attr_t));
2030 *ar = (tmpl_attr_t) {
2031 .ar_num = NUM_UNSPEC,
2032 .ar_type = TMPL_ATTR_TYPE_NORMAL,
2033 .ar_da = da,
2034 .ar_parent = our_parent,
2035 };
2036
2037do_suffix:
2038 /*
2039 * Parse the attribute reference filter
2040 *
2041 * Error out immediately if the filter is bad
2042 * otherwise determine whether to keep the
2043 * attribute reference or omit it based on:
2044 *
2045 * - Whether there was a filter present.
2046 * - The type of attribute.
2047 * - If this is the leaf attribute reference.
2048 */
2049 if (tmpl_attr_parse_filter(err, ar, name, at_rules) < 0) goto error;
2050
2051 /*
2052 * Local variables are always unitary.
2053 *
2054 * [0] is allowed, as is [n], [*], and [#]. But [1], etc. aren't allowed.
2055 */
2056 if (da->flags.local && (ar->ar_num > 0)) {
2057 fr_strerror_printf("Invalid array reference for local variable");
2059 fr_sbuff_set(name, &m_s);
2060 goto error;
2061 }
2062
2063 /*
2064 * At the end of the attribute reference. If there's a
2065 * trailing '.' then there's another attribute reference
2066 * we need to parse, otherwise we're done.
2067 */
2068 fr_sbuff_marker_release(&m_s);
2069 fr_sbuff_marker(&m_s, name);
2070
2071 if (fr_sbuff_next_if_char(name, '.')) {
2072 fr_dict_attr_t const *ref;
2073
2074 switch (da->type) {
2075 case FR_TYPE_GROUP:
2076 ref = fr_dict_attr_ref(da);
2077
2078 /*
2079 * If the ref is outside of the internal namespace, then we use it.
2080 *
2081 * If the ref is inside of the internal namespace (e.g. "request"), then we do
2082 * something else.
2083 *
2084 * If we were given a root dictionary on input, use that. We have to follow this
2085 * dictionary because this function calls itself recursively, WITHOUT updating
2086 * "dict_def" in the attr_rules. So the dict-def there is whatever got passed
2087 * into tmpl_afrom_attr_substr(), BEFORE the "parent.parent.parent..." parsing.
2088 * Which means that in many cases, the "dict_def" is completely irrelevant.
2089 *
2090 * If there is no parent on input, then we just use dict_def.
2091 *
2092 * Otherwise we search through all of the dictionaries.
2093 *
2094 * Note that we cannot put random protocol attributes into an internal attribute
2095 * of type "group".
2096 */
2097 if (ref != fr_dict_root(fr_dict_internal())) {
2098 our_parent = namespace = ref;
2099
2100 } else if (parent && parent->flags.is_root) {
2101 our_parent = namespace = parent;
2102
2103 } else if (at_rules->dict_def) {
2104 our_parent = namespace = fr_dict_root(at_rules->dict_def);
2105
2106 } else {
2107 our_parent = namespace = NULL;
2108 }
2109 break;
2110
2111 case FR_TYPE_STRUCT:
2112 case FR_TYPE_TLV:
2113 case FR_TYPE_VENDOR:
2114 case FR_TYPE_VSA:
2115 is_union:
2116 /*
2117 * Structural types are parented and namespaced from their parent da.
2118 */
2119 namespace = our_parent = da;
2120 break;
2121
2122 default:
2123 /*
2124 * Key fields can have children, because we really don't know how else to
2125 * represent the child structures.
2126 */
2127 if (fr_dict_attr_is_key_field(da)) goto is_union;
2128
2129 fr_strerror_printf("Parent type of nested attribute %s must be of type "
2130 "\"struct\", \"tlv\", \"vendor\", \"vsa\" or \"group\", got \"%s\"",
2131 da->name,
2132 fr_type_to_str(da->type));
2133 fr_sbuff_set(name, &m_s);
2134 goto error;
2135 }
2136
2137 if (ar) tmpl_attr_insert(vpt, ar);
2138
2139 if (tmpl_attr_afrom_attr_substr(ctx, err, vpt, our_parent, namespace, name, p_rules, at_rules, depth + 1) < 0) {
2140 if (ar) {
2141 tmpl_attr_list_talloc_free_tail(&vpt->data.attribute.ar); /* Remove and free ar */
2142 ar = NULL;
2143 }
2144 goto error;
2145 }
2146
2147 /*
2148 * If it's a leaf we always insert the attribute
2149 * reference into the list, even if it's a
2150 * nesting attribute.
2151 *
2152 * This is useful for nested edit sections
2153 * where the tmpl might be the name of a new
2154 * subsection.
2155 */
2156 } else {
2157 tmpl_attr_insert(vpt, ar);
2158 }
2159
2160 /*
2161 * Remove unnecessary casts.
2162 */
2165
2166 fr_sbuff_marker_release(&m_s);
2167 return 0;
2168}
2169
2170/** Parse a string into a TMPL_TYPE_ATTR_* type #tmpl_t
2171 *
2172 * @param[in,out] ctx to allocate #tmpl_t in.
2173 * @param[out] err May be NULL. Provides the exact error that the parser hit
2174 * when processing the attribute ref.
2175 * @param[out] out Where to write pointer to new #tmpl_t.
2176 * @param[in] name of attribute including #tmpl_request_ref_t and #fr_pair_list_t qualifiers.
2177 * @param[in] p_rules Formatting rules used to check for trailing garbage.
2178 * @param[in] t_rules Rules which control parsing:
2179 * - dict_def The default dictionary to use if attributes
2180 * are unqualified.
2181 * - request_def The default #request_t to set if no
2182 * #tmpl_request_ref_t qualifiers are found in name.
2183 * - list_def The default list to set if no #fr_pair_list_t
2184 * qualifiers are found in the name.
2185 * - allow_unknown If true attributes in the format accepted by
2186 * #fr_dict_attr_unknown_afrom_oid_substr will be allowed,
2187 * even if they're not in the main dictionaries.
2188 * If an unknown attribute is found a #TMPL_TYPE_ATTR
2189 * #tmpl_t will be produced.
2190 * If #tmpl_afrom_attr_substr is being called on
2191 * startup, the #tmpl_t may be passed to
2192 * #tmpl_attr_unknown_add to
2193 * add the unknown attribute to the main dictionary.
2194 * If the unknown attribute is not added to
2195 * the main dictionary the #tmpl_t cannot be used
2196 * to search for a #fr_pair_t in a #request_t.
2197 * - allow_unresolved If true, we don't generate a parse error on
2198 * unknown attributes. If an unknown attribute is
2199 * found a #TMPL_TYPE_ATTR_UNRESOLVED
2200 * #tmpl_t will be produced.
2201 * - allow_foreign If true, allow attribute names to be qualified
2202 * with a protocol outside of the passed dict_def.
2203 * - disallow_filters
2204 *
2205 * @see REMARKER to produce pretty error markers from the return value.
2206 *
2207 * @return
2208 * - <= 0 on error (offset as negative integer)
2209 * - > 0 on success (number of bytes parsed).
2210 */
2213 fr_sbuff_parse_rules_t const *p_rules,
2214 tmpl_rules_t const *t_rules)
2215{
2216 int ret;
2217 tmpl_t *vpt;
2218 fr_sbuff_t our_name = FR_SBUFF(name); /* Take a local copy in case we need to back track */
2219 bool is_raw = false;
2220 tmpl_attr_rules_t const *at_rules;
2222 fr_dict_attr_t const *namespace;
2224
2225 if (!t_rules) t_rules = &default_rules;
2226 at_rules = &t_rules->attr;
2227
2229
2230 if (!fr_sbuff_extend(&our_name)) {
2231 fr_strerror_const("Empty attribute reference");
2233 FR_SBUFF_ERROR_RETURN(&our_name);
2234 }
2235
2236 /*
2237 * Check to see if we expect a reference prefix
2238 */
2239 switch (at_rules->prefix) {
2242 if (!fr_sbuff_next_if_char(&our_name, '&')) {
2243 fr_strerror_const("Invalid attribute reference, missing '&' prefix");
2245 FR_SBUFF_ERROR_RETURN(&our_name);
2246 }
2247 break;
2248 }
2249 FALL_THROUGH; /* if we do require enum prefixes, then the '&' is optional */
2250
2252 /*
2253 * '&' prefix can be there, but doesn't have to be
2254 */
2255 (void) fr_sbuff_next_if_char(&our_name, '&');
2256 break;
2257
2259 if (fr_sbuff_is_char(&our_name, '&')) {
2260 fr_strerror_const("Attribute references used here must not have a '&' prefix");
2262 FR_SBUFF_ERROR_RETURN(&our_name);
2263 }
2264 break;
2265 }
2266
2267 /*
2268 * We parsed the tmpl as &User-Name, or just User-Name, but NOT %{User-Name}.
2269 * Mark it up as having a prefix.
2270 */
2271 MEM(vpt = tmpl_alloc(ctx, TMPL_TYPE_ATTR, T_BARE_WORD, NULL, 0));
2272 vpt->data.attribute.ref_prefix = TMPL_ATTR_REF_PREFIX_YES;
2273
2274 /*
2275 * The "raw." prefix marks up the leaf attribute
2276 * as unknown if it wasn't already which allows
2277 * users to stick whatever they want in there as
2278 * a value.
2279 */
2280 if (fr_sbuff_adv_past_strcase_literal(&our_name, "raw.")) is_raw = true;
2281
2282 /*
2283 * Parse one or more request references
2284 */
2286 &vpt->data.attribute.rr,
2287 &our_name,
2288 p_rules,
2289 t_rules,
2290 &namespace);
2291 if (ret < 0) {
2292 error:
2293 *out = NULL;
2295 FR_SBUFF_ERROR_RETURN(&our_name);
2296 }
2297
2298 fr_sbuff_marker(&m_l, &our_name);
2299
2300 /*
2301 * Parse the list and / or attribute reference
2302 */
2304 vpt,
2305 namespace, namespace,
2306 &our_name, p_rules, at_rules, 0);
2307 if (ret < 0) goto error;
2308
2309 /*
2310 * Check to see if the user wants the leaf
2311 * attribute to be raw.
2312 *
2313 * We can only do the conversion now _if_
2314 * the complete hierarchy has been resolved
2315 * otherwise we'll need to do the conversion
2316 * later.
2317 */
2318 if (tmpl_is_attr(vpt) && is_raw) tmpl_attr_to_raw(vpt);
2319
2320 /*
2321 * Local variables cannot be given a list modifier.
2322 */
2324 tmpl_attr_t *ar = tmpl_attr_list_head(tmpl_attr(vpt));
2325 bool is_local = ar->ar_da->flags.local;
2326
2327 for (; ar != NULL;
2328 ar = tmpl_attr_list_next(tmpl_attr(vpt), ar)) {
2329 if (!ar->ar_da->flags.local ||
2330 (ar->ar_da->flags.local && is_local)) continue;
2331
2332 fr_strerror_printf("Local attributes cannot be used in any list");
2334 fr_sbuff_set(&our_name, &m_l);
2335 goto error;
2336 }
2337
2338 /*
2339 * That being said, local variables are named "foo", but are always put into the local list.
2340 */
2341 if (is_local && (at_rules->list_presence != TMPL_ATTR_LIST_FORBID)) {
2342 MEM(ar = talloc(vpt, tmpl_attr_t));
2343 *ar = (tmpl_attr_t){
2344 .ar_type = TMPL_ATTR_TYPE_NORMAL,
2345 .ar_da = request_attr_local,
2346 .ar_parent = fr_dict_root(fr_dict_internal())
2347 };
2348
2349 /*
2350 * Prepend the local list ref so it gets evaluated
2351 * first.
2352 */
2353 tmpl_attr_list_insert_head(tmpl_attr(vpt), ar);
2354 }
2355 }
2356
2357 /*
2358 * Check whether the tmpl has a list qualifier.
2359 */
2360 switch (at_rules->list_presence) {
2362 break;
2363
2365 if (tmpl_attr_is_list_attr(tmpl_attr_list_head(tmpl_attr(vpt)))) {
2366 fr_strerror_const("List qualifiers are not allowed here.");
2368 goto error;
2369 }
2370 break;
2371
2373 if (!tmpl_attr_is_list_attr(tmpl_attr_list_head(tmpl_attr(vpt)))) {
2374 fr_strerror_const("List qualifier is required, but no list was found.");
2376 goto error;
2377 }
2378 break;
2379 }
2380
2381 /*
2382 * If we're using lists, ensure that the default list is specified.
2383 */
2384 if (!tmpl_attr_is_list_attr(tmpl_attr_list_head(tmpl_attr(vpt)))) {
2385 tmpl_attr_t *ar;
2386
2387 MEM(ar = talloc(vpt, tmpl_attr_t));
2388 *ar = (tmpl_attr_t){
2389 .ar_type = TMPL_ATTR_TYPE_NORMAL,
2390 .ar_parent = fr_dict_root(fr_dict_internal())
2391 };
2392
2393 fr_assert(at_rules->list_def);
2394 ar->ar_da = at_rules->list_def;
2395
2396 /*
2397 * Prepend the list ref so it gets evaluated
2398 * first.
2399 */
2400 tmpl_attr_list_insert_head(tmpl_attr(vpt), ar);
2401 }
2402
2403 tmpl_set_name(vpt, T_BARE_WORD, fr_sbuff_start(&our_name), fr_sbuff_used(&our_name));
2404 vpt->rules = *t_rules; /* Record the rules */
2405
2406 /*
2407 * If there are actual requests, duplicate them
2408 * and move them into the list.
2409 *
2410 * A NULL request_def pointer is equivalent to the
2411 * current request.
2412 */
2413 if (t_rules->attr.request_def) {
2414 tmpl_request_ref_list_acopy(vpt, &vpt->rules.attr.request_def, t_rules->attr.request_def);
2415 }
2416
2417 if (tmpl_is_attr(vpt)) {
2418 tmpl_attr_t *ar;
2419
2420 /*
2421 * Suppress useless casts.
2422 */
2424 vpt->rules.cast = FR_TYPE_NULL;
2425 }
2426
2427 /*
2428 * Ensure that the list is set correctly, so that
2429 * the returned vpt just doesn't just match the
2430 * input rules, it is also internally consistent.
2431 */
2432 ar = tmpl_attr_list_head(tmpl_attr(vpt));
2433 fr_assert(ar != NULL);
2434
2435 if (tmpl_attr_is_list_attr(ar)) vpt->rules.attr.list_def = ar->ar_da;
2436 }
2437
2438 if (!tmpl_substr_terminal_check(&our_name, p_rules)) {
2439 fr_strerror_const("Unexpected text after attribute reference");
2441 goto error;
2442 }
2443
2444 /*
2445 * If everything was resolved correctly
2446 * we now need to check the cast type.
2447 */
2448 if (!tmpl_needs_resolving(vpt) && !fr_type_is_null(t_rules->cast) &&
2449 !fr_type_cast(t_rules->cast, tmpl_attr_tail_da(vpt)->type)) {
2450 fr_strerror_printf("Cannot cast type '%s' to '%s'",
2453 fr_sbuff_set_to_start(&our_name);
2454 goto error;
2455 }
2456
2457 TMPL_VERIFY(vpt); /* Because we want to ensure we produced something sane */
2458
2459 *out = vpt;
2460 FR_SBUFF_SET_RETURN(name, &our_name);
2461}
2462
2463/** Parse a string into a TMPL_TYPE_ATTR_* type #tmpl_t
2464 *
2465 * @param[in,out] ctx to allocate #tmpl_t in.
2466 * @param[out] err May be NULL. Provides the exact error that the parser hit
2467 * when processing the attribute ref.
2468 * @param[out] out Where to write pointer to new #tmpl_t.
2469 * @param[in] name of attribute including #tmpl_request_ref_t and #fr_pair_list_t qualifiers.
2470 * @param[in] t_rules Rules which control parsing. See tmpl_afrom_attr_substr() for details.
2471 *
2472 * @note Unlike #tmpl_afrom_attr_substr this function will error out if the entire
2473 * name string isn't parsed.
2474 */
2476 tmpl_t **out, char const *name, tmpl_rules_t const *t_rules)
2477{
2478 ssize_t slen, name_len;
2480
2481 if (!t_rules) t_rules = &default_rules; /* Use the defaults */
2482
2483 name_len = strlen(name);
2484 slen = tmpl_afrom_attr_substr(ctx, err, out, &FR_SBUFF_IN(name, name_len), NULL, t_rules);
2485 if (slen <= 0) return slen;
2486
2487 if (!fr_cond_assert(*out)) return -1;
2488
2489 if (slen != name_len) {
2490 /* This looks wrong, but it produces meaningful errors for unknown attrs */
2491 fr_strerror_printf("Unexpected text after %s",
2492 tmpl_type_to_str((*out)->type));
2493 return -slen;
2494 }
2495
2496 TMPL_VERIFY(*out);
2497
2498 return slen;
2499}
2500
2501/** Create TMPL_TYPE_DATA from a string
2502 *
2503 * @param[in] ctx to allocate tmpl to.
2504 * @param[out] out where to write tmpl.
2505 * @param[in] in sbuff to parse.
2506 * @param[in] quote surrounding the operand to parse.
2507 * @param[in] t_rules specifying the cast and any enumeration values.
2508 * @param[in] allow_enum Whether parsing the value as an enum should be allowed.
2509 * @param[in] p_rules formatting rules.
2510 * @return
2511 * - <0 on error
2512 * - >=0 on success.
2513 */
2515 fr_token_t quote,
2516 tmpl_rules_t const *t_rules, bool allow_enum,
2517 fr_sbuff_parse_rules_t const *p_rules)
2518{
2519 fr_sbuff_t our_in = FR_SBUFF(in);
2520 fr_value_box_t tmp;
2521 tmpl_t *vpt;
2523
2524 if (!fr_type_is_null(t_rules->cast)) cast = t_rules->cast;
2525
2526 if (!fr_type_is_leaf(cast)) {
2527 fr_strerror_printf("%s is not a valid cast type",
2528 fr_type_to_str(cast));
2529 FR_SBUFF_ERROR_RETURN(&our_in);
2530 }
2531
2532 vpt = tmpl_alloc_null(ctx);
2533 if (fr_value_box_from_substr(vpt, &tmp,
2534 cast, allow_enum ? t_rules->enumv : NULL,
2535 &our_in, p_rules, false) < 0) {
2537 FR_SBUFF_ERROR_RETURN(&our_in);
2538 }
2540
2541 tmpl_init(vpt, TMPL_TYPE_DATA, quote, fr_sbuff_start(&our_in), fr_sbuff_used(&our_in), t_rules);
2542
2544
2545 *out = vpt;
2546
2547 if (cast == tmpl_value_type(vpt)) vpt->rules.cast = FR_TYPE_NULL;
2548
2550
2551 FR_SBUFF_SET_RETURN(in, &our_in);
2552}
2553
2554/** Parse a truth value
2555 *
2556 * @param[in] ctx to allocate tmpl to.
2557 * @param[out] out where to write tmpl.
2558 * @param[in] in sbuff to parse.
2559 * @param[in] p_rules formatting rules.
2560 * @return
2561 * - < 0 sbuff does not contain a boolean value.
2562 * - > 0 how many bytes were parsed.
2563 */
2565 fr_sbuff_parse_rules_t const *p_rules)
2566{
2567 fr_sbuff_t our_in = FR_SBUFF(in);
2568 bool a_bool;
2569 tmpl_t *vpt;
2570
2571 if (fr_sbuff_out(NULL, &a_bool, &our_in) < 0) {
2572 fr_strerror_const("Not a boolean value");
2573 return 0;
2574 }
2575
2576 if (!tmpl_substr_terminal_check(&our_in, p_rules)) {
2577 fr_strerror_const("Unexpected text after bool");
2579 }
2580
2582
2583 fr_value_box_init(&vpt->data.literal, FR_TYPE_BOOL, NULL, false);
2584 vpt->data.literal.vb_bool = a_bool;
2585
2586 *out = vpt;
2587
2588 FR_SBUFF_SET_RETURN(in, &our_in);
2589}
2590
2591/** Parse bareword as an octet string
2592 *
2593 * @param[in] ctx to allocate tmpl to.
2594 * @param[out] out where to write tmpl.
2595 * @param[in] in sbuff to parse.
2596 * @param[in] p_rules formatting rules.
2597 * @return
2598 * - < 0 negative offset where parse error occurred.
2599 * - 0 sbuff does not contain a hex string.
2600 * - > 0 how many bytes were parsed.
2601 */
2603 fr_sbuff_parse_rules_t const *p_rules)
2604{
2605 fr_sbuff_t our_in = FR_SBUFF(in);
2606 tmpl_t *vpt;
2607 char *hex;
2608 size_t binlen, len;
2609 uint8_t *bin;
2610
2611 if (!fr_sbuff_adv_past_strcase_literal(&our_in, "0x")) return 0;
2612
2613 MEM(vpt = tmpl_alloc(ctx, TMPL_TYPE_DATA, T_BARE_WORD, NULL, 0));
2614
2615 /*
2616 * This allows stream parsing to work correctly
2617 * we could be less lazy and copy hex data in
2618 * chunks, but never mind...
2619 */
2620 len = fr_sbuff_out_abstrncpy_allowed(vpt, &hex, &our_in, SIZE_MAX, sbuff_char_class_hex);
2621 if (len & 0x01) {
2622 fr_strerror_const("Hex string not even length");
2623 error:
2625 FR_SBUFF_ERROR_RETURN(&our_in);
2626 }
2627 if (len == 0) {
2628 fr_strerror_const("Zero length hex string is invalid");
2629 goto error;
2630 }
2631
2632 if (!tmpl_substr_terminal_check(&our_in, p_rules)) {
2633 fr_strerror_const("Unexpected text after hex string");
2634 goto error;
2635 }
2636
2637 bin = (uint8_t *)hex;
2638 binlen = len / 2;
2639
2641
2642 (void)fr_base16_decode(NULL, &FR_DBUFF_TMP(bin, binlen), &FR_SBUFF_IN(hex, len), false);
2643 MEM(bin = talloc_realloc_size(vpt, bin, binlen)); /* Realloc to the correct length */
2644 (void)fr_value_box_memdup_shallow(&vpt->data.literal, NULL, bin, binlen, false);
2645
2646 *out = vpt;
2647
2648 FR_SBUFF_SET_RETURN(in, &our_in);
2649}
2650
2651/** Parse bareword as an IPv4 address or prefix
2652 *
2653 * @param[in] ctx to allocate tmpl to.
2654 * @param[out] out where to write tmpl.
2655 * @param[in] in sbuff to parse.
2656 * @param[in] p_rules formatting rules.
2657 * @return
2658 * - < 0 sbuff does not contain an IPv4 address or prefix.
2659 * - > 0 how many bytes were parsed.
2660 */
2662 fr_sbuff_parse_rules_t const *p_rules)
2663{
2664 tmpl_t *vpt;
2665 fr_sbuff_t our_in = FR_SBUFF(in);
2666 uint8_t octet;
2668
2669 /*
2670 * Check for char sequence
2671 *
2672 * xxx.xxx.xxx.xxx
2673 */
2674 if (!(fr_sbuff_out(NULL, &octet, &our_in) && fr_sbuff_next_if_char(&our_in, '.') &&
2675 fr_sbuff_out(NULL, &octet, &our_in) && fr_sbuff_next_if_char(&our_in, '.') &&
2676 fr_sbuff_out(NULL, &octet, &our_in) && fr_sbuff_next_if_char(&our_in, '.') &&
2677 fr_sbuff_out(NULL, &octet, &our_in))) {
2678 error:
2679 FR_SBUFF_ERROR_RETURN(&our_in);
2680 }
2681
2682 /*
2683 * If it has a trailing '/' then it's probably
2684 * an IP prefix.
2685 */
2686 if (fr_sbuff_next_if_char(&our_in, '/')) {
2687 if (fr_sbuff_out(NULL, &octet, &our_in) < 0) {
2688 fr_strerror_const("IPv4 CIDR mask malformed");
2689 goto error;
2690 }
2691
2692 if (octet > 32) {
2693 fr_strerror_const("IPv4 CIDR mask too high");
2694 goto error;
2695 }
2696
2698 } else {
2700 }
2701
2702 if (!tmpl_substr_terminal_check(&our_in, p_rules)) {
2703 fr_strerror_const("Unexpected text after IPv4 string or prefix");
2704 goto error;
2705 }
2706
2708 if (fr_value_box_from_substr(vpt, &vpt->data.literal, type, NULL,
2709 &FR_SBUFF_REPARSE(&our_in),
2710 NULL, false) < 0) {
2712 goto error;
2713 }
2714 *out = vpt;
2715
2716 FR_SBUFF_SET_RETURN(in, &our_in);
2717}
2718
2719/** Parse bareword as an IPv6 address or prefix
2720 *
2721 * @param[in] ctx to allocate tmpl to.
2722 * @param[out] out where to write tmpl.
2723 * @param[in] in sbuff to parse.
2724 * @param[in] p_rules formatting rules.
2725 * @return
2726 * - < 0 sbuff does not contain an IPv4 address or prefix.
2727 * - > 0 how many bytes were parsed.
2728 */
2730 fr_sbuff_parse_rules_t const *p_rules)
2731{
2732 tmpl_t *vpt;
2733 fr_sbuff_t our_in = FR_SBUFF(in);
2736 size_t len;
2737 char *sep_a, *sep_b;
2738
2739 static bool ipv6_chars[UINT8_MAX + 1] = {
2740 ['0'] = true, ['1'] = true, ['2'] = true, ['3'] = true, ['4'] = true,
2741 ['5'] = true, ['6'] = true, ['7'] = true, ['8'] = true, ['9'] = true,
2742 ['a'] = true, ['b'] = true, ['c'] = true, ['d'] = true, ['e'] = true,
2743 ['f'] = true,
2744 ['A'] = true, ['B'] = true, ['C'] = true, ['D'] = true, ['E'] = true,
2745 ['F'] = true,
2746 [':'] = true, ['.'] = true
2747 };
2748
2749 /*
2750 * Drop a marker to pin the start of the
2751 * address in the buffer.
2752 */
2753 fr_sbuff_marker(&m, &our_in);
2754
2755 /*
2756 * Check for something looking like an IPv6 address
2757 *
2758 * Minimum string is '::'
2759 */
2760 len = fr_sbuff_adv_past_allowed(&our_in, FR_IPADDR_STRLEN + 1, ipv6_chars, NULL);
2761 if ((len < 2) || (len > FR_IPADDR_STRLEN)) {
2762 error:
2763 FR_SBUFF_ERROR_RETURN(&our_in);
2764 }
2765
2766 /*
2767 * Got ':' after '.', this isn't allowed.
2768 *
2769 * We need this check else IPv4 gets parsed
2770 * as blank IPv6 address.
2771 */
2772 sep_a = memchr(fr_sbuff_current(&m), '.', len);
2773 if (sep_a && (!(sep_b = memchr(fr_sbuff_current(&m), ':', len)) || (sep_b > sep_a))) {
2774 fr_strerror_const("First IPv6 component separator was a '.'");
2775 goto error;
2776 }
2777
2778 /*
2779 * The v6 parse function will happily turn
2780 * integers into v6 addresses *sigh*.
2781 */
2782 sep_a = memchr(fr_sbuff_current(&m), ':', len);
2783 if (!sep_a) {
2784 fr_strerror_const("No IPv6 component separator");
2785 goto error;
2786 }
2787
2788 /*
2789 * Handle scope
2790 */
2791 if (fr_sbuff_next_if_char(&our_in, '%')) {
2792 len = fr_sbuff_adv_until(&our_in, IFNAMSIZ + 1, p_rules->terminals, '\0');
2793 if ((len < 1) || (len > IFNAMSIZ)) {
2794 fr_strerror_const("IPv6 scope too long");
2795 goto error;
2796 }
2797 }
2798
2799 /*
2800 * ...and finally the prefix.
2801 */
2802 if (fr_sbuff_next_if_char(&our_in, '/')) {
2803 uint8_t mask;
2804
2805 if (fr_sbuff_out(NULL, &mask, &our_in) < 0) {
2806 fr_strerror_const("IPv6 CIDR mask malformed");
2807 goto error;
2808 }
2809 if (mask > 128) {
2810 fr_strerror_const("IPv6 CIDR mask too high");
2811 goto error;
2812 }
2813
2815 } else {
2817 }
2818
2819 if (!tmpl_substr_terminal_check(&our_in, p_rules)) {
2820 fr_strerror_const("Unexpected text after IPv6 string or prefix");
2821 goto error;
2822 }
2823
2825 if (fr_value_box_from_substr(vpt, &vpt->data.literal, type, NULL,
2826 &FR_SBUFF_REPARSE(&our_in),
2827 NULL, false) < 0) {
2829 goto error;
2830 }
2831 *out = vpt;
2832
2833 FR_SBUFF_SET_RETURN(in, &our_in);
2834}
2835
2836
2837/** Try and parse signed or unsigned integers
2838 *
2839 * @param[in] ctx to allocate tmpl to.
2840 * @param[out] out where to write tmpl.
2841 * @param[in] in sbuff to parse.
2842 * @param[in] p_rules formatting rules.
2843 * @return
2844 * - < 0 sbuff does not contain a mac address.
2845 * - > 0 how many bytes were parsed.
2846 */
2848 fr_sbuff_parse_rules_t const *p_rules)
2849{
2850 tmpl_t *vpt;
2851 fr_sbuff_t our_in = FR_SBUFF(in);
2852 uint8_t buff[6] = {};
2853 fr_dbuff_t dbuff;
2854 fr_value_box_t *vb;
2856
2857 fr_dbuff_init(&dbuff, buff, sizeof(buff));
2858
2859 fr_base16_decode(&err, &dbuff, &our_in, true);
2860 if (err != FR_SBUFF_PARSE_OK) return 0;
2861
2862 if (!fr_sbuff_next_if_char(&our_in, ':')) return 0;
2863
2864 fr_base16_decode(&err, &dbuff, &our_in, true);
2865 if (err != FR_SBUFF_PARSE_OK) return 0;
2866
2867 if (!fr_sbuff_next_if_char(&our_in, ':')) return 0;
2868
2869 fr_base16_decode(&err, &dbuff, &our_in, true);
2870 if (err != FR_SBUFF_PARSE_OK) return 0;
2871
2872 if (!fr_sbuff_next_if_char(&our_in, ':')) return 0;
2873
2874 fr_base16_decode(&err, &dbuff, &our_in, true);
2875 if (err != FR_SBUFF_PARSE_OK) return 0;
2876
2877 if (!fr_sbuff_next_if_char(&our_in, ':')) return 0;
2878
2879 fr_base16_decode(&err, &dbuff, &our_in, true);
2880 if (err != FR_SBUFF_PARSE_OK) return 0;
2881
2882 if (!fr_sbuff_next_if_char(&our_in, ':')) return 0;
2883
2884 fr_base16_decode(&err, &dbuff, &our_in, true);
2885 if (err != FR_SBUFF_PARSE_OK) return 0;
2886
2887 if (!tmpl_substr_terminal_check(&our_in, p_rules)) {
2888 fr_strerror_const("Unexpected text after mac address");
2889 return 0;
2890 }
2891
2893 T_BARE_WORD, fr_sbuff_start(&our_in), fr_sbuff_used(&our_in)));
2894 vb = tmpl_value(vpt);
2895
2896 fr_value_box_init(vb, FR_TYPE_ETHERNET, NULL, false);
2897 memcpy(vb->vb_ether, buff, sizeof(vb->vb_ether));
2898
2899 *out = vpt;
2900
2901 FR_SBUFF_SET_RETURN(in, &our_in);
2902}
2903
2904/** Try and parse signed or unsigned integers
2905 *
2906 * @param[in] ctx to allocate tmpl to.
2907 * @param[out] out where to write tmpl.
2908 * @param[in] in sbuff to parse.
2909 * @param[in] p_rules formatting rules.
2910 * @return
2911 * - < 0 sbuff does not contain an integer.
2912 * - > 0 how many bytes were parsed.
2913 */
2915 fr_sbuff_parse_rules_t const *p_rules)
2916{
2917 tmpl_t *vpt;
2918 fr_sbuff_t our_in = FR_SBUFF(in);
2919 ssize_t slen;
2920 fr_value_box_t *vb;
2921
2922 /*
2923 * Pick the narrowest signed type
2924 */
2925 if (fr_sbuff_is_char(&our_in, '-')) {
2926 int64_t a_int;
2927
2928 slen = fr_sbuff_out(NULL, &a_int, &our_in);
2929 if (slen <= 0) return 0;
2930
2931 if (!tmpl_substr_terminal_check(&our_in, p_rules)) {
2932 fr_strerror_const("Unexpected text after signed integer");
2933 error:
2934 FR_SBUFF_ERROR_RETURN(&our_in);
2935 }
2936
2938 T_BARE_WORD, fr_sbuff_start(&our_in), fr_sbuff_used(&our_in)));
2939 vb = tmpl_value(vpt);
2940 if (a_int >= INT8_MIN) {
2941 fr_value_box_init(vb, FR_TYPE_INT8, NULL, false);
2942 vb->vb_int8 = (int8_t)a_int;
2943 } else if (a_int >= INT16_MIN) {
2944 fr_value_box_init(vb, FR_TYPE_INT16, NULL, false);
2945 vb->vb_int16 = (int16_t)a_int;
2946 } else if (a_int >= INT32_MIN) {
2947 fr_value_box_init(vb, FR_TYPE_INT32, NULL, false);
2948 vb->vb_int32 = (int32_t)a_int;
2949 } else {
2950 fr_value_box_init(vb, FR_TYPE_INT64, NULL, false);
2951 vb->vb_int64 = (int64_t)a_int;
2952 }
2953 /*
2954 * Pick the narrowest unsigned type
2955 */
2956 } else {
2957 uint64_t a_uint;
2958
2959 slen = fr_sbuff_out(NULL, &a_uint, &our_in);
2960 if (slen <= 0) return slen;
2961
2962 if (!tmpl_substr_terminal_check(&our_in, p_rules)) {
2963 fr_strerror_const("Unexpected text after unsigned integer");
2964 goto error;
2965 }
2966
2968 T_BARE_WORD, fr_sbuff_start(&our_in), fr_sbuff_used(&our_in)));
2969 vb = tmpl_value(vpt);
2970 if (a_uint <= UINT8_MAX) {
2971 fr_value_box_init(vb, FR_TYPE_UINT8, NULL, false);
2972 vb->vb_uint8 = (uint8_t)a_uint;
2973 } else if (a_uint <= UINT16_MAX) {
2974 fr_value_box_init(vb, FR_TYPE_UINT16, NULL, false);
2975 vb->vb_uint16 = (uint16_t)a_uint;
2976 } else if (a_uint <= UINT32_MAX) {
2977 fr_value_box_init(vb, FR_TYPE_UINT32, NULL, false);
2978 vb->vb_uint32 = (uint32_t)a_uint;
2979 } else {
2980 fr_value_box_init(vb, FR_TYPE_UINT64, NULL, false);
2981 vb->vb_uint64 = (uint64_t)a_uint;
2982 }
2983 }
2984
2985 *out = vpt;
2986
2987 FR_SBUFF_SET_RETURN(in, &our_in);
2988}
2989
2991 fr_sbuff_parse_rules_t const *p_rules)
2992{
2993 tmpl_t *vpt;
2994 fr_sbuff_t our_in = FR_SBUFF(in);
2995 double a_float;
2996 ssize_t slen;
2997 fr_value_box_t *vb;
2998
2999 slen = fr_sbuff_out(NULL, &a_float, &our_in);
3000 if (slen <= 0) return 0;
3001
3002 if (!tmpl_substr_terminal_check(&our_in, p_rules)) {
3003 fr_strerror_const("Unexpected text after float");
3004 FR_SBUFF_ERROR_RETURN(&our_in);
3005 }
3006
3008 T_BARE_WORD, fr_sbuff_start(&our_in), fr_sbuff_used(&our_in)));
3009 vb = tmpl_value(vpt);
3010 fr_value_box_init(vb, FR_TYPE_FLOAT64, NULL, false);
3011 vb->vb_float64 = a_float;
3012
3013 *out = vpt;
3014
3015 FR_SBUFF_SET_RETURN(in, &our_in);
3016}
3017
3019 fr_sbuff_parse_rules_t const *p_rules)
3020{
3021 tmpl_t *vpt;
3022 fr_sbuff_t our_in = FR_SBUFF(in);
3023 fr_time_delta_t a_delta;
3024 fr_slen_t slen;
3025 fr_value_box_t *vb;
3026
3027 slen = fr_time_delta_from_substr(&a_delta, &our_in, FR_TIME_RES_SEC, true, p_rules ? p_rules->terminals : NULL);
3028 if (slen <= 0) return 0;
3029
3031 T_BARE_WORD, fr_sbuff_start(&our_in), fr_sbuff_used(&our_in)));
3032 vb = tmpl_value(vpt);
3033 fr_value_box_init(vb, FR_TYPE_TIME_DELTA, NULL, false);
3034 vb->vb_time_delta = a_delta;
3035
3036 *out = vpt;
3037
3038 FR_SBUFF_SET_RETURN(in, &our_in);
3039}
3040
3041/*
3042 * ::value
3043 *
3044 * Treated as enum name. Note that this check MUST be done after the test for IPv6, as
3045 * "::1" is an allowed IPv6 address.
3046 *
3047 * @todo - Mark this up as an enum name? Or do we really care? Maybe we want to allow
3048 *
3049 * Service-Type == 'Framed-User'
3050 *
3051 * or
3052 *
3053 * Service-Type == "Framed-User'
3054 *
3055 * as the second one allows for xlat expansions of enum names.
3056 *
3057 * We probably do want to forbid the single-quoted form of enums,
3058 * as that doesn't seem to make sense.
3059 *
3060 * We also need to distinguish unresolved bare words as enums
3061 * (with :: prefix) from unresolved attributes without an & prefix.
3062 */
3063static ssize_t tmpl_afrom_enum(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in,
3064 fr_sbuff_parse_rules_t const *p_rules,
3065 tmpl_rules_t const *t_rules)
3066{
3067 tmpl_t *vpt;
3068 char *str;
3070 fr_sbuff_t our_in = FR_SBUFF(in);
3071
3072 /*
3073 * If there isn't a "::" prefix, then check for migration flags, and enum.
3074 *
3075 * If we require an enum prefix, then the input can't be an enum, and we don't do any more
3076 * parsing.
3077 *
3078 * Otherwise if there's no prefix and no enumv, we know this input can't be an enum name.
3079 */
3080 if (!fr_sbuff_adv_past_str_literal(&our_in, "::")) {
3081 if (tmpl_require_enum_prefix) return 0;
3082
3083 if (!t_rules->enumv) return 0;
3084 }
3085
3086 vpt = tmpl_alloc_null(ctx);
3087
3088 /*
3089 * If it doesn't match any other type of bareword, parse it as an enum name.
3090 *
3091 * Note that we don't actually try to resolve the enum name. The caller is responsible
3092 * for doing that.
3093 */
3094 if (fr_dict_enum_name_afrom_substr(vpt, &str, &sberr, &our_in, p_rules ? p_rules->terminals : NULL) < 0) {
3095 /*
3096 * Produce our own errors which make
3097 * more sense in the context of tmpls
3098 */
3099 switch (sberr) {
3101 fr_strerror_const("No operand found. Expected &ref, literal, "
3102 "'quoted literal', \"%{expansion}\", or enum value");
3103 break;
3104
3106 fr_strerror_const("enum values must contain at least one alpha character");
3107 break;
3108
3109 default:
3110 fr_strerror_const("Unexpected text after enum value. Expected operator");
3111 break;
3112 }
3113
3115 FR_SBUFF_ERROR_RETURN(&our_in);
3116 }
3117
3118 /*
3119 * If there's a valid enum name, then we use it. Otherwise we leave name resolution to run time.
3120 */
3121 if (t_rules->enumv) {
3123
3124 dv = fr_dict_enum_by_name(t_rules->enumv, str, -1);
3125 if (dv) {
3127 fr_sbuff_start(&our_in), fr_sbuff_used(&our_in), t_rules);
3128 (void) fr_value_box_copy(vpt, &vpt->data.literal, dv->value);
3129
3130 *out = vpt;
3131 FR_SBUFF_SET_RETURN(in, &our_in);
3132 }
3133 }
3134
3135 /*
3136 * Either there's no enum, or the enum name didn't match one of the listed ones. There's no
3137 * point in waiting for an enum which might be declared later. That's not possible, so we fall
3138 * back to parsing the various data types.
3139 */
3140 if (t_rules->at_runtime) return 0;
3141
3143 fr_sbuff_start(&our_in), fr_sbuff_used(&our_in), t_rules);
3144 vpt->data.unescaped = str;
3145 *out = vpt;
3146
3147 FR_SBUFF_SET_RETURN(in, &our_in);
3148}
3149
3150/** Convert an arbitrary string into a #tmpl_t
3151 *
3152 * @note Unlike #tmpl_afrom_attr_str return code 0 doesn't necessarily indicate failure,
3153 * may just mean a 0 length string was parsed. Check to see if the function emitted
3154 * a #tmpl_t in *out.
3155 *
3156 * @note xlats and regexes are left uncompiled. This is to support the two pass parsing
3157 * done by the modcall code. Compilation on pass1 of that code could fail, as
3158 * attributes or xlat functions registered by modules may not be available (yet).
3159 *
3160 * @note For details of attribute parsing see #tmpl_afrom_attr_substr.
3161 *
3162 * @param[in,out] ctx To allocate #tmpl_t in.
3163 * @param[out] out Where to write the pointer to the new #tmpl_t.
3164 * @param[in] in String to parse.
3165 * @param[in] quote Quoting around the tmpl. Determines what we
3166 * attempt to parse the string as.
3167 * @param[in] p_rules Formatting rules for the tmpl.
3168 * @param[in] t_rules Validation rules for attribute references.
3169 * @return
3170 * - < 0 on error (offset as negative integer)
3171 * - >= 0 on success (number of bytes parsed).
3172 *
3173 * @see REMARKER to produce pretty error markers from the return value.
3174 *
3175 * @see tmpl_afrom_attr_substr
3176 */
3178 fr_sbuff_t *in, fr_token_t quote,
3179 fr_sbuff_parse_rules_t const *p_rules,
3180 tmpl_rules_t const *t_rules)
3181{
3182 fr_sbuff_t our_in = FR_SBUFF(in);
3183
3184 fr_slen_t slen;
3186 char *str;
3187
3188 tmpl_t *vpt = NULL;
3190
3191 if (!t_rules) t_rules = &default_rules; /* Use the defaults */
3192
3193 *out = NULL;
3194
3195 switch (quote) {
3196 case T_BARE_WORD:
3197 /*
3198 * Skip other bareword types if
3199 * we find a '&' prefix.
3200 */
3201 if (fr_sbuff_is_char(&our_in, '&')) return tmpl_afrom_attr_substr(ctx, NULL, out, in,
3202 p_rules, t_rules);
3203
3204 /*
3205 * Allow bareword xlats if we
3206 * find a '%' prefix.
3207 */
3208 if (fr_sbuff_is_char(&our_in, '%')) {
3210 xlat_exp_head_t *head = NULL;
3211
3212 vpt = tmpl_alloc_null(ctx);
3213 slen = xlat_tokenize(vpt, &head, &our_in, p_rules, t_rules, t_rules->literals_safe_for);
3214 if (slen <= 0) FR_SBUFF_ERROR_RETURN(&our_in);
3215
3217
3218 tmpl_init(vpt, type, quote, fr_sbuff_start(&our_in), slen, t_rules);
3219 vpt->data.xlat.ex = head;
3220
3221 *out = vpt;
3222
3224
3225 FR_SBUFF_SET_RETURN(in, &our_in);
3226 }
3227
3228 /*
3229 * Deal with explicit casts...
3230 */
3231 if (!fr_type_is_null(t_rules->cast)) return tmpl_afrom_value_substr(ctx, out, in, quote,
3232 t_rules, true, p_rules);
3233
3234 /*
3235 * We're at runtime and have a data type. Just parse it as that data type, without doing
3236 * endless "maybe it's this thing" attempts.
3237 */
3238 if (t_rules->at_runtime && t_rules->enumv) {
3239 tmpl_rules_t my_t_rules = *t_rules;
3240
3241 fr_assert(fr_type_is_leaf(t_rules->enumv->type));
3242
3243 my_t_rules.cast = my_t_rules.enumv->type;
3244
3245 return tmpl_afrom_value_substr(ctx, out, in, quote, &my_t_rules, true, p_rules);
3246 }
3247
3248 /*
3249 * See if it's a boolean value
3250 */
3251 slen = tmpl_afrom_bool_substr(ctx, out, &our_in, p_rules);
3252 if (slen > 0) {
3253 done_bareword:
3254 TMPL_VERIFY(*out);
3255
3256 FR_SBUFF_SET_RETURN(in, &our_in);
3257 }
3258 fr_assert(!*out);
3259
3260 /*
3261 * See if it's an octets string
3262 */
3263 slen = tmpl_afrom_octets_substr(ctx, out, &our_in, p_rules);
3264 if (slen > 0) goto done_bareword;
3265 fr_assert(!*out);
3266
3267 /*
3268 * See if it's a mac address
3269 *
3270 * Needs to be before IPv6 as the pton functions
3271 * are too greedy, and on macOS will happily
3272 * convert a mac address to an IPv6 address.
3273 */
3274 slen = tmpl_afrom_ether_substr(ctx, out, &our_in, p_rules);
3275 if (slen > 0) goto done_bareword;
3276 fr_assert(!*out);
3277
3278 /*
3279 * See if it's an IPv4 address or prefix
3280 */
3281 slen = tmpl_afrom_ipv4_substr(ctx, out, &our_in, p_rules);
3282 if (slen > 0) goto done_bareword;
3283 fr_assert(!*out);
3284
3285 /*
3286 * See if it's an IPv6 address or prefix
3287 */
3288 slen = tmpl_afrom_ipv6_substr(ctx, out, &our_in, p_rules);
3289 if (slen > 0) goto done_bareword;
3290 fr_assert(!*out);
3291
3292 slen = tmpl_afrom_enum(ctx, out, &our_in, p_rules, t_rules);
3293 if (slen > 0) goto done_bareword;
3294 fr_assert(!*out);
3295
3296 /*
3297 * See if it's a integer
3298 */
3299 slen = tmpl_afrom_integer_substr(ctx, out, &our_in, p_rules);
3300 if (slen > 0) goto done_bareword;
3301 fr_assert(!*out);
3302
3303 /*
3304 * See if it's a float
3305 */
3306 slen = tmpl_afrom_float_substr(ctx, out, &our_in, p_rules);
3307 if (slen > 0) goto done_bareword;
3308 fr_assert(!*out);
3309
3310 /*
3311 * See if it's a time delta
3312 *
3313 * We do this after floats and integers so that
3314 * they get parsed as integer and float types
3315 * and not time deltas.
3316 */
3317 slen = tmpl_afrom_time_delta(ctx, out, &our_in, p_rules);
3318 if (slen > 0) goto done_bareword;
3319 fr_assert(!*out);
3320
3321 /*
3322 * See if it's an attribute reference
3323 * without the prefix.
3324 */
3325 slen = tmpl_afrom_attr_substr(ctx, NULL, out, &our_in, p_rules, t_rules);
3326 if (slen > 0) goto done_bareword;
3327 fr_assert(!*out);
3328
3329 /*
3330 * Attempt to resolve enumeration values
3331 */
3332 vpt = tmpl_alloc_null(ctx);
3333
3334 /*
3335 * If it doesn't match any other type of bareword, parse it as an enum name.
3336 *
3337 * Note that we don't actually try to resolve the enum name. The caller is responsible
3338 * for doing that.
3339 */
3340 if (fr_dict_enum_name_afrom_substr(vpt, &str, &sberr, &our_in, p_rules ? p_rules->terminals : NULL) < 0) {
3341 /*
3342 * Produce our own errors which make
3343 * more sense in the context of tmpls
3344 */
3345 switch (sberr) {
3347 fr_strerror_const("No operand found. Expected &ref, literal, "
3348 "'quoted literal', \"%{expansion}\", or enum value");
3349 break;
3350
3352 fr_strerror_const("enum values must contain at least one alpha character");
3353 break;
3354
3355 default:
3356 fr_strerror_const("Unexpected text after enum value. Expected operator");
3357 break;
3358 }
3359
3361 FR_SBUFF_ERROR_RETURN(&our_in);
3362 }
3363
3365 fr_sbuff_start(&our_in), fr_sbuff_used(&our_in), t_rules);
3366 vpt->data.unescaped = str;
3367 *out = vpt;
3368
3369 FR_SBUFF_SET_RETURN(in, &our_in);
3370
3372 /*
3373 * Single quoted strings can be cast
3374 * to a specific data type immediately
3375 * as they cannot contain expansions.
3376 */
3377 if (!fr_type_is_null(t_rules->cast)) return tmpl_afrom_value_substr(ctx, out, in, quote,
3378 t_rules, false,
3379 p_rules);
3380 vpt = tmpl_alloc_null(ctx);
3381 slen = fr_sbuff_out_aunescape_until(vpt, &str, &our_in, SIZE_MAX,
3382 p_rules ? p_rules->terminals : NULL,
3383 p_rules ? p_rules->escapes : NULL);
3384 tmpl_init(vpt, TMPL_TYPE_DATA_UNRESOLVED, quote, fr_sbuff_start(&our_in), slen, t_rules);
3385 vpt->data.unescaped = str;
3386 break;
3387
3389 {
3390 xlat_exp_head_t *head = NULL;
3392
3393 vpt = tmpl_alloc_null(ctx);
3394
3395 slen = xlat_tokenize(vpt, &head, &our_in, p_rules, t_rules, t_rules->literals_safe_for);
3396 if (slen < 0) FR_SBUFF_ERROR_RETURN(&our_in);
3397
3398 /*
3399 * If the string doesn't contain an xlat,
3400 * and we want to cast it as a specific
3401 * type, then do the conversion now.
3402 */
3403 if (xlat_is_literal(head)) {
3404 if (!fr_type_is_null(t_rules->cast)) {
3405 talloc_free(vpt); /* Also frees any nodes */
3406
3407 return tmpl_afrom_value_substr(ctx, out,
3408 in, quote,
3409 t_rules, false, p_rules);
3410 }
3411
3412 /*
3413 * If the string doesn't contain an xlat
3414 * and there's no cast, we just store
3415 * the string for conversion later.
3416 */
3417 if (xlat_to_string(vpt, &str, &head)) {
3418 TALLOC_FREE(head);
3419
3421 fr_sbuff_start(&our_in), slen, t_rules);
3422 vpt->data.unescaped = str; /* Store the unescaped string for parsing later */
3423 break;
3424 }
3425 }
3426
3427 /*
3428 * If the string actually contains an xlat
3429 * store the compiled xlat.
3430 */
3432
3433 tmpl_init(vpt, type, quote, fr_sbuff_start(&our_in), slen, t_rules);
3434 vpt->data.xlat.ex = head;
3435 }
3436 break;
3437
3439 {
3441 xlat_exp_head_t *head = NULL;
3442
3443 vpt = tmpl_alloc_null(ctx);
3444
3445 /*
3446 * Ensure that we pre-parse the exec string.
3447 * This allows us to catch parse errors as early
3448 * as possible.
3449 *
3450 * FIXME - We need an ephemeral version of this
3451 * too.
3452 */
3453 slen = xlat_tokenize_argv(vpt, &head, &our_in, NULL, p_rules, t_rules, false, false);
3454 if ((slen <= 0) || !head) {
3456 FR_SBUFF_ERROR_RETURN(&our_in);
3457 }
3458
3459 /*
3460 * Ensure any xlats produced are bootstrapped
3461 * so that their instance data will be created.
3462 */
3463 if (xlat_finalize(head, t_rules->xlat.runtime_el) < 0) {
3464 fr_strerror_const("Failed to bootstrap xlat");
3465 FR_SBUFF_ERROR_RETURN(&our_in);
3466 }
3467
3469
3470 tmpl_init(vpt, type, quote, fr_sbuff_start(&our_in), slen, t_rules);
3471 vpt->data.xlat.ex = head;
3472 }
3473 break;
3474
3476 {
3477 xlat_exp_head_t *head = NULL;
3479
3480 if (!fr_type_is_null(t_rules->cast)) {
3481 fr_strerror_const("Casts cannot be used with regular expressions");
3482 fr_sbuff_set_to_start(&our_in); /* Point to the cast */
3483 FR_SBUFF_ERROR_RETURN(&our_in);
3484 }
3485
3486 vpt = tmpl_alloc_null(ctx);
3487
3488 slen = xlat_tokenize(vpt, &head, &our_in, p_rules, t_rules, FR_REGEX_SAFE_FOR);
3489 if (slen < 0) FR_SBUFF_ERROR_RETURN(&our_in);
3490
3491 /*
3492 * Check if the string actually contains an xlat
3493 * if it doesn't, we unfortunately still
3494 * can't compile the regex here, as we don't know if
3495 * it should be ephemeral or what flags should be used
3496 * during the compilation.
3497 *
3498 * The caller will need to do the compilation after we
3499 * return.
3500 */
3501 if (xlat_to_string(vpt, &str, &head)) {
3503 fr_sbuff_start(&our_in), slen, t_rules);
3504 vpt->data.unescaped = str; /* Store the unescaped string for compilation later */
3505 break;
3506 }
3507 /*
3508 * Mark the regex up as a regex-xlat which
3509 * will need expanding before evaluation, and can never
3510 * be pre-compiled.
3511 */
3513
3514 tmpl_init(vpt, type, quote, fr_sbuff_start(&our_in), slen, t_rules);
3515 vpt->data.xlat.ex = head;
3516 }
3517 break;
3518
3519 default:
3520 fr_assert_msg(0, "Unknown quote type %i", quote);
3521 FR_SBUFF_ERROR_RETURN(&our_in);
3522 }
3523
3525 *out = vpt;
3526
3527 FR_SBUFF_SET_RETURN(in, &our_in);
3528}
3529
3530/** Copy a tmpl
3531 *
3532 * Fully duplicates the contents of a tmpl including any nested attribute
3533 * references.
3534 *
3535 * @param[in] ctx to perform allocations under.
3536 * @param[in] in tmpl to duplicate.
3537 * @return
3538 * - NULL on error.
3539 * - A new tmpl on success.
3540 */
3541tmpl_t *tmpl_copy(TALLOC_CTX *ctx, tmpl_t const *in)
3542{
3543 tmpl_t *vpt;
3544
3545 MEM(vpt = tmpl_alloc(ctx, in->type, in->quote, in->name, in->len));
3546 vpt->rules = in->rules;
3547
3548 /*
3549 * Copy over the unescaped data
3550 */
3552 if (unlikely(!(vpt->data.unescaped = talloc_bstrdup(vpt, in->data.unescaped)))) {
3553 error:
3555 return NULL;
3556 }
3557 }
3558
3559 /*
3560 * Copy attribute references
3561 */
3562 if (tmpl_contains_attr(vpt) && unlikely(tmpl_attr_copy(vpt, in) < 0)) goto error;
3563
3564 /*
3565 * Copy flags for all regex flavours (and possibly recompile the regex)
3566 */
3567 if (tmpl_contains_regex(vpt)) {
3568 vpt->data.reg_flags = in->data.reg_flags;
3569
3570 /*
3571 * If the tmpl contains a _compiled_ regex
3572 * then convert it back to an uncompiled
3573 * regex and recompile.
3574 *
3575 * Most of the regex libraries don't allow
3576 * copying compiled expressions.
3577 */
3578 if (tmpl_is_regex(vpt)) {
3580 if (unlikely(!(vpt->data.unescaped = talloc_bstrdup(vpt, in->data.reg.src)))) goto error;
3581 if (unlikely(tmpl_regex_compile(vpt, vpt->data.reg.subcaptures) < 0)) goto error;
3582 return vpt;
3583 }
3584 }
3585
3586 /*
3587 * Copy the xlat component.
3588 *
3589 * @todo - in general we can't copy an xlat, as the instances need resolving!
3590 *
3591 * We add an assertion here because nothing allocates the head, and we need it.
3592 */
3593 if (tmpl_contains_xlat(vpt)) {
3594 fr_assert(vpt->data.xlat.ex != NULL);
3595
3596 if (unlikely(xlat_copy(vpt, vpt->data.xlat.ex, in->data.xlat.ex) < 0)) goto error;
3597 }
3598
3599 return vpt;
3600}
3601
3602/** Parse a cast specifier
3603 *
3604 * Note that casts are
3605 *
3606 * (foo)
3607 *
3608 * and NOT
3609 *
3610 * ( foo )
3611 *
3612 * Not for any particular reason, but to emphasize a bit that they're
3613 * not mathematical expressions.
3614 *
3615 * @param[out] rules to set the cast type in.
3616 * @param[in] in String containing the cast marker.
3617 * @return
3618 * - 0 no cast specifier found.
3619 * - >0 the number of bytes parsed.
3620 * - <0 offset of parse error.
3621 */
3623{
3624 char close = '\0';
3625 fr_sbuff_t our_in = FR_SBUFF(in);
3627 fr_type_t cast;
3628 ssize_t slen;
3629
3630 if (fr_sbuff_next_if_char(&our_in, '(')) {
3631 close = ')';
3632
3633 } else {
3634 if (rules) rules->cast = FR_TYPE_NULL;
3635 return 0;
3636 }
3637
3638 fr_sbuff_marker(&m, &our_in);
3640 if (fr_type_is_null(cast)) {
3641 fr_strerror_const("Unknown data type");
3642 FR_SBUFF_ERROR_RETURN(&our_in);
3643 }
3644 if (fr_type_is_non_leaf(cast)) {
3645 fr_strerror_printf("Forbidden data type '%s' in cast", fr_type_to_str(cast));
3647 }
3648
3649 if (!fr_sbuff_next_if_char(&our_in, close)) {
3650 fr_strerror_const("Unterminated cast");
3651 FR_SBUFF_ERROR_RETURN(&our_in);
3652 }
3653 fr_sbuff_adv_past_whitespace(&our_in, SIZE_MAX, NULL);
3654
3655 if (rules) rules->cast = cast;
3656
3657 FR_SBUFF_SET_RETURN(in, &our_in);
3658}
3659
3660/** Set a cast for a tmpl
3661 *
3662 * @param[in,out] vpt to set cast for.
3663 * @param[in] dst_type to set.
3664 * @return
3665 * - 0 on success.
3666 * - -1 on failure.
3667 */
3669{
3670 fr_type_t src_type;
3671
3672 switch (dst_type) {
3673 default:
3674 fr_strerror_printf("Forbidden data type '%s' in cast",
3675 fr_type_to_str(dst_type));
3676 return -1;
3677
3678 /*
3679 * We can always remove a cast.
3680 */
3681 case FR_TYPE_NULL:
3682 goto done;
3683
3684 /*
3685 * Only "base" data types are allowed. Structural types
3686 * and horrid WiMAX crap is forbidden.
3687 */
3688 case FR_TYPE_LEAF:
3689 break;
3690 }
3691
3692 switch (vpt->type) {
3693 /*
3694 * This should have been fixed before we got here.
3695 */
3697
3698 /*
3699 * By default, tmpl types cannot be cast to anything.
3700 */
3701 default:
3702 fr_strerror_const("Cannot use cast here.");
3703 return -1;
3704
3705 /*
3706 * These tmpl types are effectively of data type
3707 * "string", so they can be cast to anything.
3708 */
3709 case TMPL_TYPE_XLAT:
3710 case TMPL_TYPE_EXEC:
3714 break;
3715
3716 case TMPL_TYPE_DATA:
3717 src_type = tmpl_value_type(vpt);
3718 goto check_types;
3719
3720 case TMPL_TYPE_ATTR:
3721 {
3723
3724 /*
3725 * If the attribute has an enum, then the cast means "use the raw value, and not
3726 * the enum name".
3727 */
3728 if (da->type == dst_type) {
3729 if (da->flags.has_value) goto done;
3730 return 0;
3731 }
3732 src_type = da->type;
3733 }
3734
3735 /*
3736 * Suppress casts where they are duplicate, unless there's an enumv. In which case the
3737 * cast means "don't print the enumv value, just print the raw data".
3738 */
3739 check_types:
3740 if (src_type == dst_type) {
3741 /*
3742 * Cast with enumv means "use the raw value, and not the enum name".
3743 */
3744 if (tmpl_rules_enumv(vpt)) {
3745 tmpl_rules_enumv(vpt) = NULL;
3746 goto done;
3747 }
3748 return 0;
3749 }
3750
3751 if (!fr_type_cast(dst_type, src_type)) {
3752 fr_strerror_printf("Cannot cast type '%s' to '%s'",
3753 fr_type_to_str(src_type),
3754 fr_type_to_str(dst_type));
3755 return -1;
3756 }
3757 break;
3758 }
3759
3760done:
3761 vpt->rules.cast = dst_type;
3762 return 0;
3763}
3764
3765#ifdef HAVE_REGEX
3766/** Parse a set of regular expression flags
3767 *
3768 * @param[out] vpt Write the flags to the regex flags field in this #tmpl_t.
3769 * @param[in] in Where to parse the flag string from.
3770 * @param[in] terminals That mark the end of the regex flag string.
3771 * @return
3772 * - 0 no flags found.
3773 * - >0 the number of bytes of flags parsed.
3774 * - <0 offset of parse error.
3775 */
3776ssize_t tmpl_regex_flags_substr(tmpl_t *vpt, fr_sbuff_t *in, fr_sbuff_term_t const *terminals)
3777{
3778 fr_slen_t slen;
3779 int err = 0;
3780
3782
3783 slen = regex_flags_parse(&err, &vpt->data.reg_flags, in, terminals, true);
3784 switch (err) {
3785 case 0:
3786 break;
3787
3788 case -1: /* Non-flag and non-terminal */
3789 case -2: /* Duplicate flag */
3790 return slen;
3791 }
3792
3793 return slen;
3794}
3795#endif
3796
3797/** @name Change a #tmpl_t type, usually by casting or resolving a reference
3798 *
3799 * #tmpl_cast_in_place can be used to convert #TMPL_TYPE_DATA_UNRESOLVED to a #TMPL_TYPE_DATA of a
3800 * specified #fr_type_t.
3801 *
3802 * #tmpl_attr_unknown_add converts a #TMPL_TYPE_ATTR with an unknown #fr_dict_attr_t to a
3803 * #TMPL_TYPE_ATTR with a known #fr_dict_attr_t, by adding the unknown #fr_dict_attr_t to the main
3804 * dictionary, and updating the ``tmpl_attr_tail_da`` pointer.
3805 * @{
3806 */
3807
3808/** Determine the correct quoting after a cast
3809 *
3810 * @param[in] existing_quote Exiting quotation type.
3811 * @param[in] type Cast type.
3812 * @param[in] enumv Enumeration values.
3813 * @param[in] unescaped The unescaped value of an enumeration.
3814 * @param[in] unescaped_len Length of unescaped.
3815 */
3816static inline CC_HINT(always_inline)
3818 fr_type_t type, fr_dict_attr_t const *enumv,
3819 char const *unescaped, size_t unescaped_len)
3820{
3821 if (!fr_type_is_string(type)) return T_BARE_WORD;
3822
3823 if (enumv && fr_dict_enum_by_name(enumv, unescaped, unescaped_len)) return T_BARE_WORD;
3824
3825 /*
3826 * Leave the original quoting if it's
3827 * single or double, else default to
3828 * single quoting.
3829 */
3830 switch (existing_quote) {
3833 return existing_quote;
3834
3835 default:
3837 }
3838}
3839
3840
3841/** Convert #tmpl_t of type #TMPL_TYPE_DATA_UNRESOLVED or #TMPL_TYPE_DATA to #TMPL_TYPE_DATA of type specified
3842 *
3843 * @note Conversion is done in place.
3844 * @note Irrespective of whether the #tmpl_t was #TMPL_TYPE_DATA_UNRESOLVED or #TMPL_TYPE_DATA,
3845 * on successful cast it will be #TMPL_TYPE_DATA.
3846 *
3847 * @param[in,out] vpt The template to modify. Must be of type #TMPL_TYPE_DATA_UNRESOLVED
3848 * or #TMPL_TYPE_DATA.
3849 * @param[in] type to cast to.
3850 * @param[in] enumv Enumerated dictionary values associated with a #fr_dict_attr_t.
3851 * @return
3852 * - 0 on success.
3853 * - -1 on failure.
3854 */
3856{
3858
3860
3861 switch (vpt->type) {
3863 {
3864 char *unescaped = vpt->data.unescaped;
3865
3866 /*
3867 * We're trying to convert an unresolved (bareword)
3868 * tmpl to octets.
3869 *
3870 * tmpl_afrom_substr uses the 0x prefix as type
3871 * inference, so if it was a hex string the tmpl
3872 * type would not have fallen through to
3873 * unresolved.
3874 *
3875 * That means if we're trying to resolve it here
3876 * it's really a printable string, not a sequence
3877 * of hexits, so we just want the binary
3878 * representation of that string, and not the hex
3879 * to bin conversion.
3880 */
3881 if (fr_type_is_octets(type)) {
3882 if (fr_value_box_memdup(vpt, &vpt->data.literal, enumv,
3883 (uint8_t const *)unescaped, talloc_array_length(unescaped) - 1,
3884 false) < 0) return -1;
3885 } else {
3886 if (fr_value_box_from_str(vpt, &vpt->data.literal, type,
3887 enumv,
3888 unescaped, talloc_array_length(unescaped) - 1,
3889 NULL, false) < 0) return -1;
3890 }
3891 vpt->type = TMPL_TYPE_DATA;
3892 vpt->quote = tmpl_cast_quote(vpt->quote, type, enumv,
3893 unescaped, talloc_array_length(unescaped) - 1);
3894 talloc_free(unescaped);
3895 fr_value_box_mark_safe_for(&vpt->data.literal, vpt->rules.literals_safe_for);
3896
3897 /*
3898 * The data is now of the correct type, so we don't need to keep a cast.
3899 */
3900 vpt->rules.cast = FR_TYPE_NULL;
3901 }
3902 break;
3903
3904 case TMPL_TYPE_DATA:
3905 {
3906 if (type == tmpl_value_type(vpt)) return 0; /* noop */
3907
3908 /*
3909 * Enumerations aren't used when casting between
3910 * data types. They're only used when processing
3911 * unresolved tmpls.
3912 *
3913 * i.e. TMPL_TYPE_DATA_UNRESOLVED != TMPL_TYPE_DATA(FR_TYPE_STRING)
3914 */
3915 if (fr_value_box_cast_in_place(vpt, &vpt->data.literal, type, NULL) < 0) return -1;
3916
3917 /*
3918 * Strings get quoted, everything else is a bare
3919 * word...
3920 */
3921 if (fr_type_is_string(type)) {
3922 vpt->quote = T_SINGLE_QUOTED_STRING;
3923 } else {
3924 vpt->quote = T_BARE_WORD;
3925 }
3926
3927 /*
3928 * The data is now of the correct type, so we don't need to keep a cast.
3929 */
3930 vpt->rules.cast = FR_TYPE_NULL;
3931 }
3932 break;
3933
3934 case TMPL_TYPE_ATTR:
3935 /*
3936 * Suppress casts to the same type.
3937 */
3938 if (tmpl_attr_tail_da(vpt)->type == type) {
3939 vpt->rules.cast = FR_TYPE_NULL;
3940 break;
3941 }
3943
3945 vpt->rules.cast = type;
3946 break;
3947
3948 default:
3949 fr_assert(0);
3950 }
3952
3953 return 0;
3954}
3955
3956/** Resolve an unresolved attribute
3957 *
3958 * Multi-pass parsing fixups for attribute references.
3959 *
3960 * @param[in] vpt to resolve.
3961 * @param[in] tr_rules Combined with the original parse rules for
3962 * additional resolution passes.
3963 * @return
3964 * - 0 if all references were resolved.
3965 * - -1 if there are unknown attributes which need
3966 * adding to the global dictionary first.
3967 * - -2 if there are attributes we couldn't resolve.
3968 */
3969static inline CC_HINT(always_inline) int tmpl_attr_resolve(tmpl_t *vpt, tmpl_res_rules_t const *tr_rules)
3970{
3971 tmpl_attr_t *ar = NULL, *next, *prev;
3972 fr_dict_attr_t const *da, *namespace;
3973 fr_dict_t const *dict_def;
3974
3976
3978
3979 dict_def = vpt->rules.attr.dict_def;
3980 if (!dict_def || tr_rules->force_dict_def) dict_def = tr_rules->dict_def;
3981
3982 /*
3983 * First component is special because we may need
3984 * to search for it in multiple dictionaries.
3985 *
3986 * This emulates what's done in the initial
3987 * tokenizer function.
3988 */
3989 ar = tmpl_attr_list_head(tmpl_attr(vpt));
3990 if (ar->type == TMPL_ATTR_TYPE_UNRESOLVED) {
3992 &da,
3993 dict_def,
3994 &FR_SBUFF_IN(ar->ar_unresolved,
3995 talloc_array_length(ar->ar_unresolved) - 1),
3996 NULL,
3997 true,
3998 vpt->rules.attr.allow_foreign);
3999 if (!da) return -2; /* Can't resolve, maybe the caller can resolve later */
4000
4001 ar->ar_type = TMPL_ATTR_TYPE_NORMAL;
4002 ar->ar_da = da;
4003 ar->ar_parent = fr_dict_root(fr_dict_by_da(da));
4004
4005 /*
4006 * Record the dictionary that was
4007 * successfully used for resolution.
4008 */
4009 vpt->rules.attr.dict_def = tr_rules->dict_def;
4010
4011 /*
4012 * Reach into the next reference
4013 * and correct its parent and
4014 * namespace.
4015 */
4016 next = tmpl_attr_list_next(tmpl_attr(vpt), ar);
4017 if (next) {
4018 next->ar_parent = da;
4019 next->ar_unresolved_namespace = da;
4020 }
4021 }
4022
4023 /*
4024 * Loop, resolving each unresolved attribute in turn
4025 */
4026 while ((ar = tmpl_attr_list_next(tmpl_attr(vpt), ar))) {
4027 switch (ar->type) {
4030 continue; /* Don't need to resolve */
4031
4033 return -1; /* Unknown attributes must be resolved first */
4034
4035 default:
4036 break;
4037 }
4038
4039 prev = tmpl_attr_list_prev(tmpl_attr(vpt), ar);
4040
4041 /*
4042 * If the parent is a list AR, then use the default dictionary for the namespace
4043 */
4044 namespace = (prev && dict_def && tmpl_attr_is_list_attr(prev)) ? fr_dict_root(dict_def) : ar->ar_unresolved_namespace;
4045
4046 (void)fr_dict_attr_by_name_substr(NULL,
4047 &da,
4048 namespace,
4049 &FR_SBUFF_IN(ar->ar_unresolved,
4050 talloc_array_length(ar->ar_unresolved) - 1),
4051 NULL);
4052 /*
4053 * Still can't resolve, check to see if
4054 * the last attribute reference was a
4055 * group.
4056 *
4057 * If it was, then we may be able to
4058 * fall back to resolving the attribute
4059 * in the internal dictionary.
4060 */
4061 if (!da) {
4062 if (prev && (prev->ar_da->type == FR_TYPE_GROUP)) {
4063 (void)fr_dict_attr_by_name_substr(NULL,
4064 &da,
4066 &FR_SBUFF_IN(ar->ar_unresolved,
4067 talloc_array_length(ar->ar_unresolved) - 1),
4068 NULL);
4069 }
4070 if (!da) return -2;
4071 }
4072
4073 /*
4074 * Known attribute, just rewrite.
4075 */
4076 ar->ar_type = TMPL_ATTR_TYPE_NORMAL;
4077 ar->ar_da = da;
4078
4079 /*
4080 * Parent should have been corrected in
4081 * the previous loop iteration.
4082 */
4083 fr_assert(ar->ar_parent && !ar->ar_parent->flags.is_unknown);
4084
4085 /*
4086 * Reach into the next reference
4087 * and correct its parent.
4088 */
4089 next = tmpl_attr_list_next(tmpl_attr(vpt), ar);
4090 if (next) {
4091 next->ar_parent = da;
4092 next->ar_unresolved_namespace = da;
4093 }
4094
4095 /*
4096 * Remove redundant attributes
4097 *
4098 * If it's not a group or does not specify
4099 * an index, the ar is redundant and should
4100 * be removed.
4101 */
4102 prev = tmpl_attr_list_prev(tmpl_attr(vpt), ar);
4103 if (prev && (prev->ar_da->type != FR_TYPE_GROUP) && (prev->ar_num == NUM_UNSPEC)) {
4104 tmpl_attr_list_remove(tmpl_attr(vpt), prev);
4105 ar->ar_parent = prev->ar_parent;
4106 talloc_free(prev);
4107 }
4108 }
4109
4110 RESOLVED_SET(&vpt->type);
4112
4113 return 0;
4114}
4115
4116/** Resolve an unresolved xlat, i.e. one containing unresolved attribute references or xlat functions
4117 *
4118 * Multi-pass parsing fixups for attribute references.
4119 *
4120 * Works for base types:
4121 * - TMPL_TYPE_XLAT
4122 * - TMPL_TYPE_EXEC
4123 * - TMPL_TYPE_REGEX_XLAT
4124 *
4125 * @param[in] vpt Containing the xlat expansion to resolve.
4126 * @param[in] tr_rules Combined with the original parse rules for
4127 * additional resolution passes.
4128 * @return
4129 * - 0 on success.
4130 * - -1 on failure.
4131 */
4132static inline CC_HINT(always_inline)
4134{
4135 if (xlat_resolve(vpt->data.xlat.ex,
4137 .tr_rules = tr_rules,
4138 .allow_unresolved = false
4139 }) < 0) return -1;
4140
4141 fr_assert(!xlat_needs_resolving(vpt->data.xlat.ex));
4142
4143 RESOLVED_SET(&vpt->type);
4145
4146 return 0;
4147}
4148
4149/** Attempt to resolve functions and attributes in xlats and attribute references
4150 *
4151 * @note If resolution is successful, the rules->attr.dict_def field will be modified to
4152 * reflect the dictionary resolution was successful in.
4153 *
4154 * @param[in,out] vpt to resolve. Should be of type TMPL_TYPE_XLAT_UNRESOLVED
4155 * or TMPL_TYPE_ATTR_UNRESOLVED. All other types will be
4156 * noops.
4157 * @param[in] tr_rules Combined with the original parse rules for
4158 * additional resolution passes.
4159 * @return
4160 * - 0 on success.
4161 * - -1 on failure.
4162 */
4164{
4165 static tmpl_res_rules_t const default_tr_rules;
4166
4167 int ret = 0;
4168
4169 if (!tmpl_needs_resolving(vpt)) return 0; /* Nothing to do */
4170
4171 if (!tr_rules) tr_rules = &default_tr_rules;
4172
4173 /*
4174 * Sanity check. There shouldn't be conflicting
4175 * enumvs between the original rules and resolution
4176 * rules.
4177 *
4178 * Either the enumv was available during parsing
4179 * and shouldn't have changed during subsequent
4180 * resolution passes, or it wasn't available at
4181 * parse-time, but now is.
4182 */
4183 if (tr_rules->enumv && tmpl_rules_enumv(vpt) && !tmpl_rules_enumv(vpt)->flags.is_unknown &&
4184 (tr_rules->enumv != tmpl_rules_enumv(vpt))) {
4185 fr_strerror_printf("mismatch between parse-time enumv '%s' and resolution-time enumv '%s'",
4186 tmpl_rules_enumv(vpt)->name, tr_rules->enumv->name);
4187
4188 return -1;
4189 }
4190
4191 /*
4192 * The xlat component of the #tmpl_t needs resolving.
4193 *
4194 * This includes exec tmpls, which are largely xlats
4195 * "under the hood".
4196 */
4197 if (tmpl_contains_xlat(vpt)) {
4198 ret = tmpl_xlat_resolve(vpt, tr_rules);
4199
4200 /*
4201 * The attribute reference needs resolving.
4202 */
4203 } else if (tmpl_contains_attr(vpt)) {
4204 fr_type_t dst_type = tmpl_rules_cast(vpt);
4205
4206 fr_assert(vpt->quote == T_BARE_WORD); /* 'User-Name' or "User-Name" is not allowed. */
4207
4208 ret = tmpl_attr_resolve(vpt, tr_rules);
4209 if (ret < 0) return ret;
4210
4211 if (dst_type == tmpl_attr_tail_da(vpt)->type) {
4212 vpt->rules.cast = FR_TYPE_NULL;
4213 }
4214
4215 /*
4216 * Convert unresolved tmpls into enumvs, or failing that, string values.
4217 *
4218 * Unresolved tmpls are by definition TMPL_TYPE_DATA.
4219 */
4220 } else if (tmpl_is_data_unresolved(vpt)) {
4221 fr_type_t dst_type = tmpl_rules_cast(vpt);
4222 fr_dict_attr_t const *enumv = tmpl_rules_enumv(vpt);
4223
4224 /*
4225 * If there wasn't an enumv set in the
4226 * original rules, and we now have one
4227 * (possibly because the other side of a
4228 * binary expression has been resolved),
4229 * then use the new enumv.
4230 */
4231 if (!enumv) enumv = tr_rules->enumv;
4232
4233 /*
4234 * If we've got no explicit casting to do
4235 * check if we've got either an existing
4236 * enumv, or one which came in from the
4237 * resolution rules, and infer our data type
4238 * from that.
4239 */
4240 if (fr_type_is_null(dst_type)) {
4241 /*
4242 * Infer the cast from the enumv type.
4243 */
4244 if (enumv) {
4245 dst_type = enumv->type;
4246 } else {
4247 dst_type = FR_TYPE_STRING; /* Default to strings */
4248 }
4249 }
4250
4251 /*
4252 * tmpl_cast_in_place first resolves using
4253 * the enumv, _then_ casts using the type.
4254 */
4255 if (tmpl_cast_in_place(vpt, dst_type, enumv) < 0) return -1;
4256
4258 /*
4259 * Catch any other cases of unresolved things
4260 * we need to address. We put the assert here
4261 * so we don't end up running inappropriate
4262 * code for non-debug builds.
4263 */
4264 } else {
4265 fr_assert(0);
4266 }
4267
4268 return ret;
4269}
4270
4271/** Reset the tmpl, leaving only the name in place
4272 *
4273 * After calling this function, the tmpl type will revert to TMPL_TYPE_DATA_UNRESOLVED
4274 * and only the name and quoting will be preserved.
4275 *
4276 * @param[in] vpt to reset.
4277 */
4279{
4280 tmpl_t tmp = {
4282 .name = vpt->name,
4283 .len = vpt->len,
4284 .quote = vpt->quote
4285 };
4286
4287 switch (vpt->type) {
4289 case TMPL_TYPE_MAX:
4290 fr_assert(0);
4291 break;
4292
4293 case TMPL_TYPE_NULL:
4296 break;
4297
4298 case TMPL_TYPE_DATA:
4299 fr_value_box_clear(&vpt->data.literal);
4300 break;
4301
4302 /*
4303 * These types contain dynamically allocated
4304 * attribute and request references.
4305 */
4306 case TMPL_TYPE_ATTR:
4308 tmpl_attr_list_talloc_free(tmpl_attr(vpt));
4309 tmpl_request_list_talloc_free(&vpt->data.attribute.rr);
4310 break;
4311
4312 /*
4313 * These all store an xlat expansion
4314 */
4315 case TMPL_TYPE_EXEC:
4316 case TMPL_TYPE_XLAT:
4321 TALLOC_FREE(vpt->data.xlat.ex);
4322 break;
4323
4324 case TMPL_TYPE_REGEX:
4325 talloc_free(vpt->data.reg.ex);
4326 break;
4327
4328 }
4329
4330 memcpy(vpt, &tmp, sizeof(*vpt));
4331
4333}
4334
4335/** Convert an attribute reference to an xlat expansion
4336 *
4337 * This is where a user attempts to use an attribute reference which is actually
4338 * a virtual attribute.
4339 *
4340 * @param[in] ctx to convert new tmpl in.
4341 * @param[in,out] vpt_p pointer to #tmpl_t of TMPL_TYPE_ATTR | TMPL_TYPE_ATTR_UNPARSED.
4342 */
4343int tmpl_attr_to_xlat(TALLOC_CTX *ctx, tmpl_t **vpt_p)
4344{
4345
4346 tmpl_t *vpt;
4347 tmpl_t *attr = *vpt_p;
4348
4349 /*
4350 * First alloc a new tmpl to hold the xlat expansion
4351 */
4352 vpt = tmpl_alloc(ctx, TMPL_TYPE_XLAT, attr->quote, attr->name, attr->len);
4353
4354 /*
4355 * ...then wrap the old tmpl_t in an xlat expansion
4356 * doing conversion to a virtual attribute if necessary.
4357 */
4358 if (xlat_from_tmpl_attr(vpt, &vpt->data.xlat.ex, vpt_p) < 0) {
4360 return -1;
4361 }
4362
4363 if (xlat_needs_resolving(vpt->data.xlat.ex)) UNRESOLVED_SET(&vpt->type);
4364
4365 *vpt_p = vpt;
4366
4367 return 0;
4368}
4369
4371{
4372 if (!ref) return;
4373
4374 switch (ref->type) {
4376 {
4377 ref->da = ref->ar_unknown = fr_dict_attr_unknown_afrom_da(vpt, ref->da);
4378 ref->ar_unknown->type = FR_TYPE_OCTETS;
4379 ref->is_raw = 1;
4380 ref->ar_unknown->flags.is_unknown = 1;
4382 }
4383 break;
4384 case TMPL_ATTR_TYPE_UNSPEC: /* noop */
4385 break;
4386
4388 ref->ar_unknown->type = FR_TYPE_OCTETS;
4389 ref->is_raw = 1;
4390 break;
4391
4393 ref->is_raw = true;
4394 break;
4395 }
4396
4398}
4399
4400/** Convert the leaf attribute of a tmpl to a unknown/raw type
4401 *
4402 */
4404{
4405 attr_to_raw(vpt, tmpl_attr_list_tail(tmpl_attr(vpt)));
4406}
4407
4408/** Add an unknown #fr_dict_attr_t specified by a #tmpl_t to the main dictionary
4409 *
4410 * @param vpt to add. ``tmpl_attr_tail_da`` pointer will be updated to point to the
4411 * #fr_dict_attr_t inserted into the dictionary.
4412 * @return
4413 * - 1 noop (did nothing) - Not possible to convert tmpl.
4414 * - 0 on success.
4415 * - -1 on failure.
4416 */
4418{
4419 tmpl_attr_t *ar = NULL, *next = NULL;
4420
4421 if (!vpt) return 1;
4422
4423 /*
4424 * Can't do this for expressions parsed at runtime
4425 */
4426 if (vpt->rules.at_runtime) return 1;
4427
4429
4431
4432 if (!tmpl_attr_tail_is_unknown(vpt)) return 1; /* Ensure at least the leaf is unknown */
4433
4434 while ((ar = tmpl_attr_list_next(tmpl_attr(vpt), ar))) {
4435 fr_dict_attr_t const *unknown, *known;
4436
4437 switch (ar->type) {
4438 case TMPL_ATTR_TYPE_NORMAL: /* Skip */
4440 continue;
4441
4442 case TMPL_ATTR_TYPE_UNRESOLVED: /* Shouldn't have been called */
4443 fr_strerror_const("Remaining attributes are unresolved");
4444 return -1;
4445
4447 break;
4448 }
4449
4450 unknown = ar->ar_unknown;
4451 known = fr_dict_attr_unknown_add(fr_dict_unconst(fr_dict_by_da(unknown)), unknown);
4452 if (!known) return -1;
4453
4454 /*
4455 * Fixup the parent of the next unknown
4456 * now it's known.
4457 */
4458 next = tmpl_attr_list_next(tmpl_attr(vpt), ar);
4459 if (next && (next->type == TMPL_ATTR_TYPE_UNKNOWN) &&
4460 (next->ar_da->parent == unknown)) {
4462 known) < 0) return -1;
4463 next->ar_parent = known;
4464 }
4465
4466 /*
4467 * Convert the ref to a normal type.
4468 * At runtime there should be no
4469 * "unknown" references as they should
4470 * have all been added to a
4471 * dictionary.
4472 */
4474
4475 /*
4476 * If the attribute is *NOT* raw then
4477 * swap the canonical unknown with the
4478 * one that was previously associated
4479 * with the tmpl.
4480 *
4481 * This establishes the unknown attribute
4482 * in the dictionary if it was really
4483 * unknown whilst not mucking up the
4484 * types for raw attributes.
4485 */
4486 if (!ar_is_raw(ar)) {
4487 fr_dict_attr_unknown_free(&ar->ar_da);
4488 ar->ar_da = known;
4489 } else if (!fr_cond_assert(!next)) {
4490 fr_strerror_const("Only the leaf may be raw");
4491 return -1;
4492 }
4493 }
4494
4495 return 0;
4496}
4497
4498/** Add an unresolved #fr_dict_attr_t specified by a #tmpl_t to the main dictionary
4499 *
4500 * @note fr_dict_attr_add will not return an error if the attribute already exists
4501 * meaning that multiple #tmpl_t specifying the same attribute can be
4502 * passed to this function to be fixed up, so long as the type and flags
4503 * are identical.
4504 *
4505 * @param[in] dict_def Default dictionary to use if none is
4506 * specified by the tmpl_attr_tail_unresolved.
4507 * @param[in] vpt specifying unresolved attribute to add.
4508 * ``tmpl_attr_tail_da`` pointer will be updated to
4509 * point to the #fr_dict_attr_t inserted
4510 * into the dictionary. Lists and requests
4511 * will be preserved.
4512 * @param[in] type to define unresolved attribute as.
4513 * @param[in] flags to define unresolved attribute with.
4514 * @return
4515 * - 1 noop (did nothing) - Not possible to convert tmpl.
4516 * - 0 on success.
4517 * - -1 on failure.
4518 */
4520 fr_type_t type, fr_dict_attr_flags_t const *flags)
4521{
4522 fr_dict_attr_t const *da;
4523 fr_dict_attr_flags_t our_flags = *flags;
4524
4525 our_flags.name_only = true;
4526
4527 if (!vpt) return -1;
4528
4530
4531 if (!tmpl_is_attr_unresolved(vpt)) return 1;
4532
4533 if (fr_dict_attr_add(dict_def,
4535 return -1;
4536 }
4538 if (!da) return -1;
4539
4540 if (type != da->type) {
4541 fr_strerror_printf("Attribute %s of type %s already defined with type %s",
4542 da->name, fr_type_to_str(type),
4543 fr_type_to_str(da->type));
4544 return -1;
4545 }
4546
4547 if (memcmp(flags, &da->flags, sizeof(*flags)) != 0) {
4548 fr_strerror_printf("Attribute %s already defined with different flags", da->name);
4549 return -1;
4550 }
4551
4552 tmpl_attr_set_da(vpt, da);
4553 vpt->type = TMPL_TYPE_ATTR;
4554
4555 return 0;
4556}
4557
4558#ifdef HAVE_REGEX
4559/** Convert a TMPL_TYPE_REGEX_UNCOMPILED into a TMPL_TYPE_REGEX
4560 *
4561 * Other regex types become noops.
4562 */
4563ssize_t tmpl_regex_compile(tmpl_t *vpt, bool subcaptures)
4564{
4565 ssize_t slen;
4566 char *unescaped = vpt->data.unescaped;
4567
4568 if (tmpl_is_regex_xlat(vpt) || tmpl_is_regex(vpt)) return 0; /* Don't need compiling */
4569
4571
4572 slen = regex_compile(vpt, &vpt->data.reg.ex,
4573 unescaped, talloc_array_length(unescaped) - 1,
4574 &vpt->data.reg_flags, subcaptures, vpt->rules.at_runtime);
4575 if (slen <= 0) return vpt->quote != T_BARE_WORD ? slen - 1 : slen; /* Account for the quoting */
4576
4577 vpt->type = TMPL_TYPE_REGEX;
4578 vpt->data.reg.src = unescaped; /* Keep this around for debugging and copying */
4579 vpt->data.reg.subcaptures = subcaptures;
4580
4582
4583 return slen;
4584}
4585#endif
4586/** @} */
4587
4588/** @name Print the contents of a #tmpl_t
4589 * @{
4590 */
4592{
4593 fr_sbuff_t our_out = FR_SBUFF(out);
4594 tmpl_request_t *rr = tmpl_request_list_head(rql);
4595
4596 /*
4597 * Print request references
4598 */
4599 while (rr) {
4600 FR_SBUFF_IN_TABLE_STR_RETURN(&our_out, tmpl_request_ref_table, rr->request, "<INVALID>");
4601 rr = tmpl_request_list_next(rql, rr);
4602 if (rr) FR_SBUFF_IN_CHAR_RETURN(&our_out, '.');
4603 }
4604
4605 FR_SBUFF_SET_RETURN(out, &our_out);
4606}
4607
4608/** Print an attribute or list #tmpl_t to a string
4609 *
4610 * This function is the direct counterpart to #tmpl_afrom_attr_substr.
4611 *
4612 * @param[in] out Where to write the presentation format #tmpl_t string.
4613 * @param[in] vpt to print.
4614 * @param[in] ar_prefix Whether to print the '&' at the beginning of attribute
4615 * references.
4616 * - TMPL_ATTR_REF_PREFIX_YES - always print.
4617 * - TMPL_ATTR_REF_PREFIX_NO - never print.
4618 * - TMPL_ATTR_REF_PREFIX_AUTO - print if the original tmpl
4619 * was prefixed.
4620 * @return
4621 * - >0 the number of bytes written to the out buffer.
4622 * - 0 invalid argument.
4623 * - <0 the number of bytes we would have needed to complete the print.
4624 */
4626{
4627 tmpl_attr_t *ar = NULL;
4629 fr_sbuff_t our_out = FR_SBUFF(out);
4630 fr_slen_t slen;
4631
4633
4634 /*
4635 * Only print things we can print...
4636 */
4637 switch (vpt->type) {
4639 case TMPL_TYPE_ATTR:
4640 break;
4641
4642 default:
4643 fr_assert(0);
4644 return 0;
4645 }
4646
4647 /*
4648 * Handle printing the request reference
4649 * prefix.
4650 */
4652 ((ar_prefix == TMPL_ATTR_REF_PREFIX_AUTO) && vpt->data.attribute.ref_prefix)) {
4653 FR_SBUFF_IN_CHAR_RETURN(&our_out, '&');
4654 }
4655
4656 /*
4657 * Print request references
4658 */
4659 slen = tmpl_request_ref_list_print(&our_out, &vpt->data.attribute.rr);
4660 if (slen > 0) FR_SBUFF_IN_CHAR_RETURN(&our_out, '.');
4661 if (slen < 0) return slen;
4662
4663 /*
4664 *
4665 * If the leaf attribute is unknown and raw we
4666 * add the raw. prefix.
4667 *
4668 * If the leaf attribute is unknown and not raw
4669 * we add the .unknown prefix.
4670 *
4671 */
4673
4674 /*
4675 * Print attribute identifiers
4676 */
4677 ar = NULL;
4678 while ((ar = tmpl_attr_list_next(tmpl_attr(vpt), ar))) {
4679 switch(ar->type) {
4681 break;
4682
4685 {
4686 int i, depth = 0;
4687
4688 fr_assert(ar->ar_parent); /* All normal and unknown attributes must have parents */
4689
4690 fr_proto_da_stack_build_partial(&stack, ar->ar_parent, ar->ar_da);
4691
4692 /*
4693 * First component in the list has everything built
4694 */
4695 if (ar == tmpl_attr_list_head(tmpl_attr(vpt))) {
4696 depth = ar->ar_parent->depth - 1; /* Adjust for array index */
4697 /*
4698 * Everything else skips the first component
4699 */
4700 } else {
4701 depth = ar->ar_parent->depth;
4702 }
4703
4704 /*
4705 * Root attributes will be skipped by the build
4706 * function, so da[0] contains the attribute
4707 * we're looking for.
4708 */
4709 if (depth < 0) depth = 0;
4710
4711 /*
4712 * Print from our parent depth to the AR we're processing
4713 *
4714 * For refs we skip the attribute pointed to be the ref
4715 * and just print its children.
4716 *
4717 * In addition skip printing "request." in most cases.
4718 */
4719 if ((stack.da[depth] == request_attr_request) && tmpl_attr_list_next(tmpl_attr(vpt), ar) &&
4720 (ar->filter.type == TMPL_ATTR_FILTER_TYPE_NONE)) continue;
4721
4722 for (i = depth; (unsigned int)i < ar->ar_da->depth; i++) {
4723 FR_SBUFF_IN_STRCPY_RETURN(&our_out, stack.da[i]->name);
4724
4725 /*
4726 * Print intermediary separators
4727 * if necessary.
4728 */
4729 if (((unsigned int)i + 1) < ar->ar_da->depth) FR_SBUFF_IN_CHAR_RETURN(&our_out, '.');
4730 }
4731 }
4732 break;
4733
4734 /*
4735 * For unresolved attribute we print the raw identifier we
4736 * got when parsing the tmpl.
4737 */
4739 {
4740 unsigned int i, depth;
4741
4742 /*
4743 * This is the first unresolved component in a potential
4744 * chain of unresolved components. Print the path up to
4745 * the last known parent.
4746 */
4747 if (ar->ar_parent && !ar->ar_parent->flags.is_root) {
4748 fr_proto_da_stack_build_partial(&stack, ar->ar_parent, ar->ar_parent);
4749 if (ar->ar_parent->flags.is_root) {
4750 depth = 0;
4751 } else {
4752 depth = ar->ar_parent->depth - 1;
4753 }
4754
4755 for (i = depth; i < ar->ar_parent->depth; i++) {
4756 FR_SBUFF_IN_STRCPY_RETURN(&our_out, stack.da[i]->name);
4757 FR_SBUFF_IN_CHAR_RETURN(&our_out, '.');
4758 }
4759 }
4760 /*
4761 * Then print the unresolved component
4762 */
4763 FR_SBUFF_IN_BSTRCPY_BUFFER_RETURN(&our_out, ar->ar_unresolved);
4764 break;
4765 }
4766 }
4767
4768 if (ar_filter_is_none(ar)) {
4769 /* do nothing */
4770
4771 } else if (ar_filter_is_num(ar)) {
4772 switch (ar->ar_num) {
4773 case NUM_UNSPEC:
4774 break;
4775
4776 case NUM_ALL:
4777 FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "[*]");
4778 break;
4779
4780 case NUM_COUNT:
4781 FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "[#]");
4782 break;
4783
4784 case NUM_LAST:
4785 FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "[n]");
4786 break;
4787
4788 default:
4789 FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "[%i]", ar->ar_num);
4790 break;
4791 }
4792
4793 } else if (ar_filter_is_cond(ar)) {
4794 FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "[");
4795 (void) xlat_print(&our_out, ar->ar_cond, NULL);
4796 FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "]");
4797
4798 } else {
4799 fr_assert(0);
4800 }
4801
4802 if (tmpl_attr_list_next(tmpl_attr(vpt), ar)) FR_SBUFF_IN_CHAR_RETURN(&our_out, '.');
4803 }
4804 FR_SBUFF_SET_RETURN(out, &our_out);
4805}
4806
4807/** Print a #tmpl_t to a string
4808 *
4809 * This function should primarily be used for regenerating vpt->name when the contents
4810 * of the #tmpl_t is changed programmatically, or when the #tmpl_t is being serialized
4811 * in some non-standard way, i.e. as a value for a field in a database.
4812 *
4813 * This function is the direct counterpart to #tmpl_afrom_substr.
4814 *
4815 * @note Does not print flags for regular expressions, as the quoting char is needed
4816 * to separate the elements of the expression.
4817 * Call regex_flags_print to write the flags values to the output buffer.
4818 *
4819 * @param[out] out Where to write the presentation format #tmpl_t string.
4820 * @param[in] vpt to print.
4821 * @param[in] ar_prefix Whether to print the '&' at the beginning of attribute
4822 * references.
4823 * - TMPL_ATTR_REF_PREFIX_YES - always print.
4824 * - TMPL_ATTR_REF_PREFIX_NO - never print.
4825 * - TMPL_ATTR_REF_PREFIX_AUTO - print if the original tmpl
4826 * was prefixed.
4827 * @param[in] e_rules Escaping rules used to print strings.
4828 * @return
4829 * - >0 the number of bytes written to the out buffer.
4830 * - 0 invalid argument.
4831 * - <0 the number of bytes we would have needed to complete the print.
4832 */
4835{
4836 fr_sbuff_t our_out = FR_SBUFF(out);
4837
4839
4840 switch (vpt->type) {
4842 case TMPL_TYPE_ATTR:
4844 break;
4845
4846 case TMPL_TYPE_DATA:
4847 FR_SBUFF_RETURN(fr_value_box_print, &our_out, tmpl_value(vpt), e_rules);
4848 break;
4849
4850 case TMPL_TYPE_REGEX:
4851 FR_SBUFF_IN_BSTRNCPY_RETURN(&our_out, vpt->name, vpt->len); /* Fixme - double escapes */
4852 break;
4853
4855 FR_SBUFF_IN_ESCAPE_BUFFER_RETURN(&our_out, vpt->data.unescaped, e_rules);
4856 break;
4857
4859 case TMPL_TYPE_NULL:
4860 case TMPL_TYPE_MAX:
4861 fr_sbuff_terminate(out);
4862 break;
4863
4864 /*
4865 * The remaining types will either
4866 * be xlat expansions, or need
4867 * resolving, in which case the
4868 * unescaped string is available
4869 * in vpt->unescaped.
4870 */
4871 default:
4872 if (tmpl_contains_xlat(vpt)) {
4873 FR_SBUFF_RETURN(xlat_print, &our_out, tmpl_xlat(vpt), e_rules);
4874 break;
4875 }
4876
4878 FR_SBUFF_IN_ESCAPE_BUFFER_RETURN(&our_out, vpt->data.unescaped, e_rules);
4879 break;
4880 }
4881
4882 fr_assert_fail("Can't print invalid tmpl type %s", tmpl_type_to_str(vpt->type));
4883
4884 /*
4885 * Ensure we do something sane for non-debug builds
4886 */
4887 fr_sbuff_terminate(out);
4888 return 0;
4889 }
4890
4891 FR_SBUFF_SET_RETURN(out, &our_out);
4892}
4893
4894/** Print a #tmpl_t to a string with quotes
4895 *
4896 * This function should be used when the tmpl is embedded in some other construct
4897 * in the server's configuration.
4898 *
4899 * It adds standard quoting around tmpl's used as operands in expressions and applies
4900 * the correct escaping rules.
4901 *
4902 * @param[out] out Where to write the presentation format #tmpl_t string.
4903 * @param[in] vpt to print.
4904 * @param[in] ar_prefix Whether to print the '&' at the beginning of attribute
4905 * references.
4906 * - TMPL_ATTR_REF_PREFIX_YES - always print.
4907 * - TMPL_ATTR_REF_PREFIX_NO - never print.
4908 * - TMPL_ATTR_REF_PREFIX_AUTO - print if the original tmpl
4909 * was prefixed.
4910 * @return
4911 * - >0 the number of bytes written to the out buffer.
4912 * - 0 invalid argument.
4913 * - <0 the number of bytes we would have needed to complete the print.
4914 */
4916{
4917 fr_sbuff_t our_out = FR_SBUFF(out);
4918
4919 char quote = fr_token_quote[vpt->quote];
4920
4921 if (quote != '\0') FR_SBUFF_IN_CHAR_RETURN(&our_out, quote);
4922 FR_SBUFF_RETURN(tmpl_print, &our_out, vpt,
4924 if (quote != '\0') FR_SBUFF_IN_CHAR_RETURN(&our_out, quote);
4925
4926 /*
4927 * Optionally print the flags
4928 */
4929 if (vpt->type & TMPL_FLAG_REGEX) FR_SBUFF_RETURN(regex_flags_print, &our_out, tmpl_regex_flags(vpt));
4930
4931 FR_SBUFF_SET_RETURN(out, &our_out);
4932}
4933/** @} */
4934
4935
4936#ifdef WITH_VERIFY_PTR
4937/** Used to check whether areas of a tmpl_t are zeroed out
4938 *
4939 * @param ptr Offset to begin checking at.
4940 * @param len How many bytes to check.
4941 * @return
4942 * - Pointer to the first non-zero byte.
4943 * - NULL if all bytes were zero.
4944 */
4945static uint8_t const *is_zeroed(uint8_t const *ptr, size_t len)
4946{
4947 size_t i;
4948
4949 for (i = 0; i < len; i++) {
4950 if (ptr[i] != 0x00) return ptr + i;
4951 }
4952
4953 return NULL;
4954}
4955
4956/** Verify that unused regions of the struct are zeroed out
4957 *
4958 */
4959#define CHECK_ZEROED(_vpt, _field) is_zeroed(((uint8_t const *)&(_vpt)->data) + sizeof((_vpt)->data._field), sizeof((_vpt)->data) - sizeof((_vpt)->data._field))
4960
4961
4962/** Print hex data
4963 *
4964 */
4965#define PRINT_NON_ZEROED(_vpt, _field, _nz_ptr) \
4966do { \
4967 DEBUG("Expected live portion %p-%p (0-%zu)", \
4968 _vpt, \
4969 (uint8_t const *)&(_vpt)->data + sizeof((_vpt)->data._field), \
4970 sizeof((_vpt)->data._field)); \
4971 DEBUG("Expected zero portion %p-%p (%zu-%zu)", \
4972 (uint8_t const *)&(_vpt)->data + sizeof((_vpt)->data._field), \
4973 (uint8_t const *)&(_vpt)->data + sizeof((_vpt)->data), \
4974 sizeof((_vpt)->data._field), sizeof((_vpt)->data)); \
4975 HEX_MARKER1((uint8_t const *)&vpt->data, sizeof(vpt->data), nz - (uint8_t const *)&vpt->data, "non-zero memory", ""); \
4976} while (0)
4977
4978
4979/** Verify the attribute reference in a tmpl_t make sense
4980 *
4981 * @note If the attribute reference is is invalid, causes the server to exit.
4982 *
4983 * @param file obtained with __FILE__.
4984 * @param line obtained with __LINE__.
4985 * @param vpt to check.
4986 */
4987void tmpl_attr_verify(char const *file, int line, tmpl_t const *vpt)
4988{
4989 tmpl_attr_t *ar = NULL;
4990 tmpl_attr_t *slow = NULL, *fast = NULL;
4991 tmpl_attr_t *seen_unknown = NULL;
4992 tmpl_attr_t *seen_unresolved = NULL;
4993
4995
4996 /*
4997 * Loop detection
4998 */
4999 while ((slow = tmpl_attr_list_next(tmpl_attr(vpt), slow)) &&
5000 (fast = tmpl_attr_list_next(tmpl_attr(vpt), fast))) {
5001
5002 /*
5003 * Advances twice as fast as slow...
5004 */
5005 fast = tmpl_attr_list_next(tmpl_attr(vpt), fast);
5006 fr_fatal_assert_msg(fast != slow,
5007 "CONSISTENCY CHECK FAILED %s[%u]: Looping reference list found. "
5008 "Fast pointer hit slow pointer at \"%s\"",
5009 file, line,
5010 slow->type == TMPL_ATTR_TYPE_UNRESOLVED ? slow->ar_unresolved :
5011 slow->da ? slow->da->name : "(null-attr)");
5012 }
5013
5014 /*
5015 * Lineage type chec