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 check
5016 *
5017 * Known attribute cannot come after unresolved or unknown attributes
5018 * Unknown attributes cannot come after unresolved attributes
5019 */
5020 if (!tmpl_is_list(vpt)) while ((ar = tmpl_attr_list_next(tmpl_attr(vpt), ar))) {
5021 switch (ar->type) {
5023 if (seen_unknown) {
5025 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: "
5026 "TMPL_TYPE_ATTR known attribute \"%s\" "
5027 "occurred after unknown attribute %s "
5028 "in attr ref list",
5029 file, line,
5030 ar->da->name,
5031 ar->unknown.da->name);
5032 }
5033 if (seen_unresolved) {
5035 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: "
5036 "TMPL_TYPE_ATTR known attribute \"%s\" "
5037 "occurred after unresolved attribute \"%s\""
5038 "in attr ref list",
5039 file, line,
5040 ar->da->name,
5041 ar->ar_unresolved);
5042 }
5043 fr_fatal_assert_msg(ar->ar_parent,
5044 "CONSISTENCY CHECK FAILED %s[%u]: attr ref missing parent",
5045 file, line);
5046 break;
5047
5049 if (seen_unknown) {
5051 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: "
5052 "TMPL_TYPE_ATTR unspecified attribute "
5053 "occurred after unknown attribute %s "
5054 "in attr ref list",
5055 file, line,
5056 ar->unknown.da->name);
5057 }
5058 if (seen_unresolved) {
5060 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: "
5061 "TMPL_TYPE_ATTR unspecified attribute "
5062 "occurred after unresolved attribute \"%s\""
5063 "in attr ref list",
5064 file, line,
5065 ar->ar_unresolved);
5066 }
5067 break;
5068
5070 seen_unresolved = ar;
5071 fr_fatal_assert_msg(ar->ar_unresolved_namespace,
5072 "CONSISTENCY CHECK FAILED %s[%u]: unresolved attr ref missing namespace",
5073 file, line);
5074 break;
5075
5077 seen_unknown = ar;
5078 if (seen_unresolved) {
5080 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: "
5081 "TMPL_TYPE_ATTR unknown attribute \"%s\" "
5082 "occurred after unresolved attribute %s "
5083 "in attr ref list",
5084 file, line, ar->da->name,
5085 ar->ar_unresolved);
5086 }
5087 break;
5088 }
5089 }
5090}
5091
5092/** Verify fields of a tmpl_t make sense
5093 *
5094 * @note If the #tmpl_t is invalid, causes the server to exit.
5095 *
5096 * @param file obtained with __FILE__.
5097 * @param line obtained with __LINE__.
5098 * @param vpt to check.
5099 */
5100void tmpl_verify(char const *file, int line, tmpl_t const *vpt)
5101{
5102 uint8_t const *nz;
5103
5104 fr_assert(vpt);
5105
5107 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: tmpl_t type was "
5108 "TMPL_TYPE_UNINITIALISED (uninitialised)", file, line);
5109 }
5110
5111 if (vpt->type >= TMPL_TYPE_MAX) {
5112 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: tmpl_t type was %i "
5113 "(outside range of tmpl_type_table)", file, line, vpt->type);
5114 }
5115
5116 if (!vpt->name && (vpt->quote != T_INVALID)) {
5117 char quote = vpt->quote >= T_TOKEN_LAST ? '?' : fr_token_quote[vpt->quote];
5118
5119 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: Quote type '%c' (%i) was set for NULL name",
5120 file, line, quote, vpt->quote);
5121 }
5122
5123 if (vpt->name && (vpt->quote == T_INVALID)) {
5124 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: No quoting type was set for name \"%.*s\"",
5125 file, line, (int)vpt->len, vpt->name);
5126 }
5127
5128 /*
5129 * Do a memcmp of the bytes after where the space allocated for
5130 * the union member should have ended and the end of the union.
5131 * These should always be zero if the union has been initialised
5132 * properly.
5133 *
5134 * If they're still all zero, do TMPL_TYPE specific checks.
5135 */
5136 switch (vpt->type) {
5137 case TMPL_TYPE_NULL:
5138 if ((nz = is_zeroed((uint8_t const *)&vpt->data, sizeof(vpt->data)))) {
5139 HEX_MARKER1((uint8_t const *)&vpt->data, sizeof(vpt->data),
5140 nz - (uint8_t const *)&vpt->data, "non-zero memory", "");
5141 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_NULL "
5142 "has non-zero bytes in its data union", file, line);
5143 }
5144 break;
5145
5147 if (!vpt->data.unescaped) {
5148 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_DATA_UNRESOLVED "
5149 "unescaped field is NULL", file, line);
5150 }
5151 break;
5152
5154 if (!vpt->data.xlat.ex) {
5155 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_XLAT "
5156 "has a NULL xlat.ex field", file, line);
5157
5158 }
5159
5160 if (!xlat_needs_resolving(vpt->data.xlat.ex)) {
5161 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_XLAT_UNRESOLVED "
5162 "does not have 'needs resolving' flag set", file, line);
5163 }
5164 break;
5165
5166 case TMPL_TYPE_XLAT:
5167 if (!vpt->data.xlat.ex) {
5168 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_XLAT "
5169 "has a NULL xlat.ex field", file, line);
5170
5171 }
5172 break;
5173
5174/* @todo When regexes get converted to xlat the flags field of the regex union is used
5175 case TMPL_TYPE_XLAT_UNRESOLVED:
5176 if (is_zeroed((uint8_t const *)&vpt->data, sizeof(vpt->data))) {
5177 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_XLAT_UNRESOLVED "
5178 "has non-zero bytes in its data union", file, line);
5179 }
5180 break;
5181
5182 case TMPL_TYPE_XLAT:
5183 if (CHECK_ZEROED(vpt, xlat)) {
5184 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_XLAT "
5185 "has non-zero bytes after the data.xlat pointer in the union", file, line);
5186 }
5187 break;
5188*/
5189
5190 case TMPL_TYPE_EXEC:
5192 /* tmpl_xlat(vpt) can be initialized */
5193 break;
5194
5196 if ((tmpl_attr_list_num_elements(tmpl_attr(vpt)) > 0) &&
5197 ((tmpl_attr_t *)tmpl_attr_list_tail(tmpl_attr(vpt)))->da) {
5198#ifndef NDEBUG
5200#endif
5201 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_ATTR_UNRESOLVED contains %u "
5202 "references", file, line, tmpl_attr_list_num_elements(tmpl_attr(vpt)));
5203 }
5204 break;
5205
5206 case TMPL_TYPE_ATTR:
5207 if ((nz = CHECK_ZEROED(vpt, attribute))) {
5208 PRINT_NON_ZEROED(vpt, attribute, nz);
5209 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_ATTR "
5210 "has non-zero bytes after the data.attribute struct in the union",
5211 file, line);
5212 }
5213
5215 fr_assert(vpt->rules.cast == FR_TYPE_NULL);
5216 break;
5217 }
5218
5221 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_ATTR "
5222 "da is marked as unknown, but address is not equal to the template's "
5223 "unknown da pointer", file, line);
5224 }
5225 /*
5226 * Raw attributes may not have been added to the dictionary yet
5227 */
5228 } else {
5229 fr_dict_attr_t const *da;
5230 fr_dict_t const *dict;
5231
5232 /*
5233 * Attribute may be present with multiple names
5234 */
5236 if (!dict) {
5237 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_ATTR "
5238 "attribute \"%s\" (%s) not rooted in a dictionary",
5241 }
5242
5243 da = tmpl_attr_tail_da(vpt);
5244 if (!tmpl_attr_tail_is_raw(vpt) && (da != tmpl_attr_tail_da(vpt))) {
5245 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_ATTR "
5246 "dictionary pointer %p \"%s\" (%s) "
5247 "and global dictionary pointer %p \"%s\" (%s) differ",
5248 file, line,
5251 da, da->name,
5252 fr_type_to_str(da->type));
5253 }
5254
5255 tmpl_attr_verify(file, line, vpt);
5256 }
5257 break;
5258
5259 case TMPL_TYPE_DATA:
5260 if ((nz = CHECK_ZEROED(vpt, literal))) {
5261 PRINT_NON_ZEROED(vpt, literal, nz);
5262 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_DATA "
5263 "has non-zero bytes after the data.literal struct in the union",
5264 file, line);
5265 }
5266
5268 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_DATA type was "
5269 "FR_TYPE_NULL (uninitialised)", file, line);
5270 }
5271
5273 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_DATA type was "
5274 "%i (outside the range of fr_type_ts)", file, line, tmpl_value_type(vpt));
5275 }
5276 /*
5277 * Unlike fr_pair_ts we can't guarantee that fr_pair_t_TMPL buffers will
5278 * be talloced. They may be allocated on the stack or in global variables.
5279 */
5280 switch (tmpl_value_type(vpt)) {
5281 case FR_TYPE_STRING:
5283 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_DATA char buffer not \\0 "
5284 "terminated", file, line);
5285 }
5286 break;
5287
5288 case FR_TYPE_STRUCTURAL:
5289 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_DATA is of type TLV",
5290 file, line);
5291
5292 default:
5293 break;
5294 }
5295
5296 break;
5297
5301#ifndef HAVE_REGEX
5302 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_REGEX_XLAT_UNRESOLVED - No regex support",
5303 file, line);
5304#endif
5305 break;
5306
5307 case TMPL_TYPE_REGEX:
5308#ifdef HAVE_REGEX
5309 if (tmpl_regex(vpt) == NULL) {
5310 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_REGEX "
5311 "reg.ex field was NULL", file, line);
5312 }
5313#else
5314 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_REGEX - No regex support",
5315 file, line);
5316#endif
5317 break;
5318
5320 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_UNINITIALISED", file, line);
5321
5322 case TMPL_TYPE_MAX:
5323 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_MAX", file, line);
5324 }
5325}
5326#endif
5327
5328#define return_P(_x) fr_strerror_const(_x);goto return_p
5329
5330/** Preparse a string in preparation for passing it to tmpl_afrom_substr()
5331 *
5332 * Note that the input string is not modified, which means that the
5333 * tmpl_afrom_substr() function MUST un-escape it.
5334 *
5335 * The caller should pass 'out' and 'outlen' to tmpl_afrom_substr()
5336 * as 'in' and 'inlen'. The caller should also pass 'type'.
5337 * The caller should also pass do_unescape=true.
5338 *
5339 * @param[out] out start of the string to parse
5340 * @param[out] outlen length of the string to parse
5341 * @param in where we start looking for the string
5342 * @param inlen length of the input string
5343 * @param[out] type token type of the string.
5344 * @return
5345 * - > 0, amount of parsed string to skip, to get to the next token
5346 * - <=0, -offset in 'start' where the parse error was located
5347 */
5348ssize_t tmpl_preparse(char const **out, size_t *outlen, char const *in, size_t inlen,
5350{
5351 char const *p = in, *end = in + inlen;
5352 char quote;
5353 char close;
5354 int depth;
5355 bool triple;
5356
5357 *type = T_INVALID;
5358
5359 while (isspace((uint8_t) *p) && (p < end)) p++;
5360 if (p >= end) return p - in;
5361
5362 switch (*p) {
5363 /*
5364 * Allow bare xlat's
5365 */
5366 case '%':
5367 if (p[1] != '{') {
5368 char const *q;
5369
5370 q = p + 1;
5371
5372 /*
5373 * Function syntax: %foo(...)
5374 */
5375 while ((q < end) && (isalnum((int) *q) || (*q == '.') || (*q == '_') || (*q == '-'))) {
5376 q++;
5377 }
5378
5379 if (*q != '(') {
5380 p++;
5381 fr_strerror_const("Invalid character after '%'");
5382 return_p:
5383 return -(p - in);
5384 }
5385
5386 /*
5387 * Return the whole %foo(...) string.
5388 */
5389 *out = p;
5390 if (*type == T_INVALID) *type = T_BARE_WORD;
5391 close = ')';
5392
5393 p = q + 1;
5394 depth = 1;
5395 goto loop;
5396 }
5397
5398 /*
5399 * For now, %{...} is treated as a double-quoted
5400 * string. Once we clean other things up, the
5401 * xlats will be treated as strongly typed values
5402 * / lists on their own.
5403 */
5404 if (*type == T_INVALID) *type = T_BARE_WORD;
5405 depth = 0;
5406 close = '}';
5407
5408 /*
5409 * Xlat's are quoted by %{...} / %(...) nesting, not by
5410 * escapes, so we need to do special escaping.
5411 */
5412 *out = p;
5413 loop:
5414 while (*p) {
5415 /*
5416 * End of expansion. Return the entire
5417 * expansion, including the enclosing %{}
5418 * characters.
5419 */
5420 if ((*p == '}') || (*p == ')')) {
5421 bool match = (*p == close);
5422
5423 p++;
5424 depth--;
5425
5426 if (depth == 0) {
5427 if (!match) break;
5428
5429 *outlen = p - (*out);
5430 return p - in;
5431 }
5432 continue;
5433 }
5434
5435 if (*p == '\\') {
5436 p++;
5437 if (!p[1]) {
5438 return_P("End of string after escape");
5439 }
5440
5441 p++;
5442 continue;
5443 }
5444
5445 if ((p[0] == '%') && ((p[1] == '{') || (p[1] == '('))) {
5446 if (!p[2]) {
5447 return_P("End of string after expansion");
5448 }
5449
5450 p += 2;
5451 depth++;
5452 continue;
5453 }
5454
5455 /*
5456 * Allow (...) and {...}
5457 */
5458 if ((*p == '{') || (*p == '(')) {
5459 p++;
5460 depth++;
5461 continue;
5462 }
5463
5464 p++;
5465 }
5466
5467 /*
5468 * End of input without end of string.
5469 * Point the error to the start of the string.
5470 */
5471 p = *out;
5472 return_P("Unterminated expansion");
5473
5474 case '/':
5475 goto bare_word;
5476
5477 case '\'':
5478 quote = *(p++);
5480 goto skip_string;
5481
5482 case '`':
5483 quote = *(p++);
5485 goto skip_string;
5486
5487 case '"':
5488 quote = *(p++);
5490
5491 /*
5492 * We're not trying to do a *correct* parsing of
5493 * every string here. We're trying to do a
5494 * simple parse that isn't wrong. We therefore
5495 * accept most anything that's vaguely well
5496 * formed, and rely on the next stage to do a
5497 * more rigorous check.
5498 */
5499 skip_string:
5500 if ((inlen > 3) && (p[0] == quote) && (p[1] == quote)) {
5501 triple = true;
5502 p += 2;
5503 } else {
5504 triple = false;
5505 }
5506 *out = p;
5507
5508 while (*p) {
5509 if (p >= end) goto unterminated;
5510
5511 /*
5512 * End of string. Tell the caller the
5513 * length of the data inside of the
5514 * string, and return the number of
5515 * characters to skip.
5516 */
5517 if (*p == quote) {
5518 if (!triple) {
5519 *outlen = p - (*out);
5520 p++;
5521 return p - in;
5522
5523 }
5524
5525 if (((end - p) >= 3) && (p[1] == quote) && (p[2] == quote)) {
5526 *outlen = p - (*out);
5527 p += 3;
5528 return p - in;
5529 }
5530
5531 p++;
5532 continue;
5533 }
5534
5535 if (*p == '\\') {
5536 p++;
5537 if (!p[1]) {
5538 return_P("End of string after escape");
5539 }
5540 }
5541 p++;
5542 }
5543
5544 /*
5545 * End of input without end of string.
5546 * Point the error to the start of the string.
5547 */
5548 unterminated:
5549 p = *out;
5550 return_P("Unterminated string");
5551
5552 case '&':
5553 *out = p; /* the output string starts with '&' */
5554 p++;
5555 quote = '[';
5556 goto skip_word;
5557
5558 default:
5559 bare_word:
5560 *out = p;
5561
5563 quote = '['; /* foo[1] is OK */
5564 } else {
5565 quote = '\0';
5566 }
5567
5568 skip_word:
5569 *type = T_BARE_WORD;
5570 depth = 0;
5571
5572 /*
5573 * Allow *most* things. But stop on spaces and special characters.
5574 */
5575 while (*p) {
5576 if (isspace((uint8_t) *p)) {
5577 break;
5578 }
5579
5580 if (*p == '$') {
5581 if (p[1] == '{') {
5582 p += 2;
5583 depth++;
5584 continue;
5585
5586 } else if ((p[1] == 'E') &&
5587 (p[2] == 'N') &&
5588 (p[3] == 'V') &&
5589 (p[4] == '{')) {
5590 p += 5;
5591 depth++;
5592 continue;
5593
5594 } else {
5595 /*
5596 * Bare '$' is wrong...
5597 */
5598 break;
5599 }
5600 }
5601
5602 if (*p == '%') {
5603 if (p[1] == '{') {
5604 p += 2;
5605 depth++;
5606 continue;
5607 }
5608
5609 p++;
5610 continue;
5611 }
5612
5613 /*
5614 * If we're inside of a ${...} expansion,
5615 * then allow everything until the
5616 * closing '}'. This means that we can
5617 * do ${foo[bar].baz}, among other
5618 * thingds.
5619 */
5620 if (depth > 0) {
5621 if (*p == '}') {
5622 depth--;
5623 }
5624
5625 p++;
5626 continue;
5627 }
5628
5629 /*
5630 * '-' is special. We allow it for
5631 * attribute names, BUT it's a
5632 * terminating token if the NEXT
5633 * character is '='.
5634 *
5635 * We have the same criteria for IPv6
5636 * addresses and tagged attributes. ':'
5637 * is allowed, but ':=' is a breaking
5638 * token.
5639 */
5640 if ((*p == '-') || (*p == ':')) {
5641 if (p[1] == '=') break;
5642 p++;
5643 continue;
5644 }
5645
5646 /*
5647 * Allowed in attribute names, and/or
5648 * host names and IP addresses, and IPv6 addresses.
5649 */
5650 if ((*p == '.') || (*p == '/') || (*p == '_') || (*p == '*') ||
5651 (*p == ']') || (*p == '@')) {
5652 p++;
5653 continue;
5654 }
5655
5656 /*
5657 * [...] is an IPv6 address.
5658 */
5659 if ((p == in) && (*p == '[')) {
5660 p++;
5661 continue;
5662 }
5663
5664 /*
5665 * Allow letters and numbers
5666 */
5667 if (((*p >= 'a') && (*p <= 'z')) ||
5668 ((*p >= 'A') && (*p <= 'Z')) ||
5669 ((*p >= '0') && (*p <= '9'))) {
5670 p++;
5671 continue;
5672 }
5673
5674 /*
5675 * Allow UTF-8 sequences.
5676 */
5677 if (*(uint8_t const *)p > 0x80) {
5678 p++;
5679 continue;
5680 }
5681
5682 /*
5683 * If it's an attribute reference, allow
5684 * a few more things inside of a "[...]"
5685 * block.
5686 */
5687 if (*p == quote) {
5688 p++;
5689
5690 /*
5691 * Allow [#], etc. But stop
5692 * immediately after the ']'.
5693 */
5694 if ((*p == '#') || (*p == '*') || (*p == 'n')) {
5695 p++;
5696
5697 } else {
5698 /*
5699 * Allow numbers as array indexes
5700 */
5701 while ((*p >= '0') && (*p <= '9')) {
5702 p++;
5703 }
5704
5705 if (*p != ']') {
5706 return_P("Array index is not an integer");
5707 }
5708 }
5709
5710 if (*p == ']') {
5711 p++;
5712 continue;
5713 }
5714 }
5715
5716 /*
5717 * Everything else is a breaking token
5718 */
5719 break;
5720 }
5721
5722 /*
5723 * Give some slightly better error messages.
5724 */
5725 if (*p == '\\') {
5726 return_P("Unexpected escape");
5727 }
5728
5729 if ((*p == '"') || (*p == '\'') || (*p == '`')) {
5730 return_P("Unexpected start of string");
5731 }
5732
5733 if (p == *out) {
5734 return_P("Empty string is invalid");
5735 }
5736
5737 *outlen = p - (*out);
5738 break;
5739 }
5740
5741 return p - in;
5742}
5743
5744/** Return whether or not async is required for this tmpl.
5745 *
5746 * If the tmpl is needs_async, then it is async
5747 * If the tmpl is not needs_async, then it will not yield
5748 *
5749 * If the tmpl yields, then async is required.
5750 */
5752{
5753 switch (vpt->type) {
5754 case TMPL_TYPE_EXEC: /* we don't have "exec no-wait" here */
5755 case TMPL_TYPE_XLAT_UNRESOLVED: /* we have no idea, so be safe */
5756#ifndef HAVE_REGEX
5758#endif
5759 return true;
5760
5761#ifndef HAVE_REGEX
5763#endif
5764 case TMPL_TYPE_XLAT: /* synchronous xlats use unlang_interpret_synchronous() */
5765 default:
5766 return false;
5767 }
5768}
5769
5770/** Initialize a set of rules from a parent set of rules, and a parsed tmpl_t
5771 *
5772 */
5774{
5775 fr_dict_attr_t const *da;
5776 fr_dict_attr_t const *ref;
5777 fr_dict_t const *dict, *internal;
5778
5779 *out = *parent;
5780 /* don't set ->parent=parent, that is only for switching subrequest, etc. */
5781
5782 if (!tmpl_is_attr(vpt)) return;
5783
5784 da = tmpl_attr_tail_da(vpt);
5785
5786 /*
5787 * The input tmpl is a leaf. We must parse the child as
5788 * a normal attribute reference (as with the parent tmpl).
5789 */
5790 if (!fr_type_structural[da->type]) {
5791 return;
5792 }
5793
5794 if (vpt->rules.attr.request_def) {
5795 tmpl_request_ref_list_acopy(ctx, &out->attr.request_def, vpt->rules.attr.request_def);
5796 }
5797 out->attr.list_def = tmpl_list(vpt);
5798
5799 /*
5800 * Parse the child attributes in the context of the parent struct / tlv / whatever.
5801 */
5802 if (da->type != FR_TYPE_GROUP) {
5803 out->attr.dict_def = fr_dict_by_da(da);
5804 out->attr.namespace = da;
5805 return;
5806 }
5807
5808 ref = fr_dict_attr_ref(da);
5809 dict = fr_dict_by_da(ref);
5810 internal = fr_dict_internal();
5811
5812 /*
5813 * Groups MAY change dictionaries. If so, then swap the dictionary and the parent.
5814 */
5815 if ((dict != internal) && (dict != out->attr.dict_def)) {
5816 out->attr.dict_def = dict;
5817 out->attr.namespace = ref;
5818 }
5819
5820 /*
5821 * Otherwise the reference is swapping FROM a protocol
5822 * dictionary TO the internal dictionary, and TO an
5823 * internal group. We fall back to leaving well enough
5824 * alone, and leave things as-is. This allows internal
5825 * grouping attributes to appear anywhere.
5826 */
5827}
5828
5829static void tmpl_attr_rules_debug(tmpl_attr_rules_t const *at_rules)
5830{
5831 FR_FAULT_LOG("\tdict_def = %s", at_rules->dict_def ? fr_dict_root(at_rules->dict_def)->name : "");
5832 FR_FAULT_LOG("\tnamespace = %s", at_rules->namespace ? at_rules->namespace->name : "");
5833
5834 FR_FAULT_LOG("\tlist_def = %s", at_rules->list_def ? at_rules->list_def->name : "");
5835
5836 FR_FAULT_LOG("\tallow_unknown = %u", at_rules->allow_unknown);
5837 FR_FAULT_LOG("\tallow_unresolved = %u", at_rules->allow_unresolved);
5838 FR_FAULT_LOG("\tallow_wildcard = %u", at_rules->allow_wildcard);
5839 FR_FAULT_LOG("\tallow_foreign = %u", at_rules->allow_foreign);
5840 FR_FAULT_LOG("\tdisallow_filters = %u", at_rules->disallow_filters);
5841}
5842
5843
5845{
5846 FR_FAULT_LOG("\tparent = %p", rules->parent);
5847 FR_FAULT_LOG(" attr {");
5848 tmpl_attr_rules_debug(&rules->attr);
5849 FR_FAULT_LOG(" }");
5850 FR_FAULT_LOG("\tenumv = %s", rules->enumv ? rules->enumv->name : "");
5851 FR_FAULT_LOG("\tcast = %s", fr_type_to_str(rules->cast));
5852 FR_FAULT_LOG("\tat_runtime = %u", rules->at_runtime);
5853
5854}
static int const char char buffer[256]
Definition acutest.h:576
int const char * file
Definition acutest.h:702
va_end(args)
static int const char * fmt
Definition acutest.h:573
int const char int line
Definition acutest.h:702
va_start(args, fmt)
#define fr_base16_decode(_err, _out, _in, _no_trailing)
Definition base16.h:95
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
Definition build.h:167
#define RCSID(id)
Definition build.h:483
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:209
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition build.h:322
#define STRINGIFY(x)
Definition build.h:197
#define CMP_RETURN(_a, _b, _field)
Return if the comparison is not 0 (is unequal)
Definition build.h:121
#define CMP(_a, _b)
Same as CMP_PREFER_SMALLER use when you don't really care about ordering, you just want an ordering.
Definition build.h:112
#define unlikely(_x)
Definition build.h:381
#define NUM_ELEMENTS(_t)
Definition build.h:337
static char const * skip_word(char const *text)
Definition command.c:1872
#define fr_dbuff_init(_out, _start, _len_or_end)
Initialise an dbuff for encoding or decoding.
Definition dbuff.h:354
#define FR_DBUFF_TMP(_start, _len_or_end)
Creates a compound literal to pass into functions which accept a dbuff.
Definition dbuff.h:514
#define fr_fatal_assert_fail(_msg,...)
Calls panic_action ifndef NDEBUG, else logs error and causes the server to exit immediately with code...
Definition debug.h:191
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:139
#define fr_assert_msg(_x, _msg,...)
Calls panic_action ifndef NDEBUG, else logs error and causes the server to exit immediately with code...
Definition debug.h:210
#define fr_assert_fail(_msg,...)
Calls panic_action ifndef NDEBUG, else logs error.
Definition debug.h:216
#define FR_FAULT_LOG(_fmt,...)
Definition debug.h:49
#define fr_fatal_assert_msg(_x, _fmt,...)
Calls panic_action ifndef NDEBUG, else logs error and causes the server to exit immediately with code...
Definition debug.h:184
#define MEM(x)
Definition debug.h:36
fr_slen_t fr_dict_attr_by_name_substr(fr_dict_attr_err_t *err, fr_dict_attr_t const **out, fr_dict_attr_t const *parent, fr_sbuff_t *name, fr_sbuff_term_t const *tt))
unsigned int name_only
this attribute should always be referred to by name.
Definition dict.h:99
fr_dict_t * fr_dict_unconst(fr_dict_t const *dict)
Coerce to non-const.
Definition dict_util.c:4585
fr_dict_t const * fr_dict_by_da(fr_dict_attr_t const *da)
Attempt to locate the protocol dictionary containing an attribute.
Definition dict_util.c:2606
fr_dict_attr_t const * fr_dict_attr_common_parent(fr_dict_attr_t const *a, fr_dict_attr_t const *b, bool is_ancestor)
Find a common ancestor that two TLV type attributes share.
Definition dict_util.c:2037
static fr_slen_t err
Definition dict.h:824
static fr_dict_attr_t * fr_dict_attr_unknown_vendor_afrom_num(TALLOC_CTX *ctx, fr_dict_attr_t const *parent, unsigned int vendor)
Definition dict.h:573
bool const fr_dict_attr_allowed_chars[UINT8_MAX+1]
Characters that are allowed in dictionary attribute names.
Definition dict_util.c:51
static fr_dict_attr_t * fr_dict_attr_unknown_copy(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
Definition dict.h:553
fr_dict_attr_t const * fr_dict_attr_unknown_add(fr_dict_t *dict, fr_dict_attr_t const *old)
Converts an unknown to a known by adding it to the internal dictionaries.
fr_dict_attr_t const * fr_dict_attr_by_name(fr_dict_attr_err_t *err, fr_dict_attr_t const *parent, char const *attr))
Locate a fr_dict_attr_t by its name.
Definition dict_util.c:3263
fr_dict_attr_t * fr_dict_attr_unconst(fr_dict_attr_t const *da)
Coerce to non-const.
Definition dict_util.c:4597
fr_dict_attr_t * fr_dict_attr_unknown_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da))
Copy a known or unknown attribute to produce an unknown attribute with the specified name.
static fr_dict_attr_t * fr_dict_attr_unknown_raw_afrom_num(TALLOC_CTX *ctx, fr_dict_attr_t const *parent, unsigned int attr)
Definition dict.h:580
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition dict_util.c:2400
int fr_dict_attr_unknown_parent_to_known(fr_dict_attr_t *da, fr_dict_attr_t const *parent)
Fixup the parent of an unknown attribute using an equivalent known attribute.
fr_value_box_t const * value
Enum value (what name maps to).
Definition dict.h:231
fr_dict_enum_value_t * fr_dict_enum_by_name(fr_dict_attr_t const *da, char const *name, ssize_t len)
Definition dict_util.c:3395
void fr_dict_attr_unknown_free(fr_dict_attr_t const **da)
Free dynamically allocated (unknown attributes)
fr_dict_t const * fr_dict_internal(void)
Definition dict_util.c:4610
fr_slen_t fr_dict_attr_search_by_qualified_name_substr(fr_dict_attr_err_t *err, fr_dict_attr_t const **out, fr_dict_t const *dict_def, fr_sbuff_t *name, fr_sbuff_term_t const *tt, bool internal, bool foreign))
Locate a qualified fr_dict_attr_t by its name and a dictionary qualifier.
Definition dict_util.c:2975
fr_slen_t fr_dict_attr_search_by_name_substr(fr_dict_attr_err_t *err, fr_dict_attr_t const **out, fr_dict_t const *dict_def, fr_sbuff_t *name, fr_sbuff_term_t const *tt, bool internal, bool foreign))
Locate a fr_dict_attr_t by its name in the top level namespace of a dictionary.
Definition dict_util.c:3004
static fr_slen_t fr_dict_enum_name_afrom_substr(TALLOC_CTX *ctx, char **out, fr_sbuff_parse_error_t *err, fr_sbuff_t *in, fr_sbuff_term_t const *tt) 1(fr_dict_enum_name_from_substr
#define FR_DICT_MAX_TLV_STACK
Maximum TLV stack size.
Definition dict.h:495
fr_dict_attr_err_t
Errors returned by attribute lookup functions.
Definition dict.h:290
@ FR_DICT_ATTR_OK
No error.
Definition dict.h:291
@ FR_DICT_ATTR_NOT_DESCENDENT
Attribute is not a descendent of the parent attribute.
Definition dict.h:297
@ FR_DICT_ATTR_NO_CHILDREN
Child lookup in attribute with no children.
Definition dict.h:301
int fr_dict_attr_add(fr_dict_t *dict, fr_dict_attr_t const *parent, char const *name, unsigned int attr, fr_type_t type, fr_dict_attr_flags_t const *flags))
Add an attribute to the dictionary.
Definition dict_util.c:1712
fr_dict_attr_t const * fr_dict_attr_child_by_num(fr_dict_attr_t const *parent, unsigned int attr)
Check if a child attribute exists in a parent using an attribute number.
Definition dict_util.c:3328
#define fr_dict_attr_is_key_field(_da)
Definition dict.h:153
static fr_slen_t in
Definition dict.h:824
#define FR_DICT_ATTR_MAX_NAME_LEN
Maximum length of a attribute name.
Definition dict.h:475
Values of the encryption flags.
Value of an enumerated attribute.
Definition dict.h:227
static fr_dict_attr_t const * fr_dict_attr_ref(fr_dict_attr_t const *da)
Return the reference associated with a group type attribute.
Definition dict_ext.h:184
#define FR_DLIST_HEAD(_name)
Expands to the type name used for the head wrapper structure.
Definition dlist.h:1122
#define FR_IPADDR_STRLEN
Like INET6_ADDRSTRLEN but includes space for the textual Zone ID.
Definition inet.h:89
#define HEX_MARKER1(_data, _len, _slen, _error, _fmt,...)
Definition log.h:741
talloc_free(reap)
static char * stack[MAX_STACK]
Definition radmin.c:158
tmpl_attr_prefix_t
@ TMPL_ATTR_REF_PREFIX_NO
Attribute refs have no '&' prefix.
@ TMPL_ATTR_REF_PREFIX_AUTO
Attribute refs may have a '&' prefix.
@ TMPL_ATTR_REF_PREFIX_YES
Attribute refs must have '&' prefix.
unsigned short uint16_t
fr_type_t
@ FR_TYPE_TIME_DELTA
A period of time measured in nanoseconds.
@ FR_TYPE_IPV4_ADDR
32 Bit IPv4 Address.
@ FR_TYPE_INT8
8 Bit signed integer.
@ FR_TYPE_TLV
Contains nested attributes.
@ FR_TYPE_ETHERNET
48 Bit Mac-Address.
@ FR_TYPE_IPV6_PREFIX
IPv6 Prefix.
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_MAX
Number of defined data types.
@ FR_TYPE_NULL
Invalid (uninitialised) attribute type.
@ FR_TYPE_UINT16
16 Bit unsigned integer.
@ FR_TYPE_INT64
64 Bit signed integer.
@ FR_TYPE_INT16
16 Bit signed integer.
@ FR_TYPE_UINT8
8 Bit unsigned integer.
@ FR_TYPE_UINT32
32 Bit unsigned integer.
@ FR_TYPE_STRUCT
like TLV, but without T or L, and fixed-width children
@ FR_TYPE_INT32
32 Bit signed integer.
@ FR_TYPE_VENDOR
Attribute that represents a vendor in the attribute tree.
@ FR_TYPE_UINT64
64 Bit unsigned integer.
@ FR_TYPE_IPV6_ADDR
128 Bit IPv6 Address.
@ FR_TYPE_IPV4_PREFIX
IPv4 Prefix.
@ FR_TYPE_BOOL
A truth value.
@ FR_TYPE_VSA
Vendor-Specific, for RADIUS attribute 26.
@ FR_TYPE_OCTETS
Raw octets.
@ FR_TYPE_GROUP
A grouping of other attributes.
@ FR_TYPE_FLOAT64
Double precision floating point.
unsigned int uint32_t
long int ssize_t
unsigned char uint8_t
ssize_t fr_slen_t
unsigned long int size_t
#define UINT8_MAX
fr_sbuff_parse_error_t
@ FR_SBUFF_PARSE_ERROR_NOT_FOUND
String does not contain a token matching the output type.
@ FR_SBUFF_PARSE_ERROR_FORMAT
Format of data was invalid.
@ FR_SBUFF_PARSE_OK
No error.
static uint8_t depth(fr_minmax_heap_index_t i)
Definition minmax_heap.c:83
char * fr_vasprintf(TALLOC_CTX *ctx, char const *fmt, va_list ap)
Definition print.c:851
void fr_proto_da_stack_build_partial(fr_da_stack_t *stack, fr_dict_attr_t const *parent, fr_dict_attr_t const *da)
Complete the DA stack for a child attribute.
Definition proto.c:159
#define fr_assert(_expr)
Definition rad_assert.h:38
static bool done
Definition radclient.c:80
static uint32_t mask
Definition rbmonkey.c:39
fr_dict_attr_t const * request_attr_request
Definition request.c:45
fr_dict_attr_t const * request_attr_control
Definition request.c:47
fr_dict_attr_t const * request_attr_local
Definition request.c:49
fr_dict_attr_t const * request_attr_state
Definition request.c:48
fr_dict_attr_t const * request_attr_reply
Definition request.c:46
static char const * name
size_t fr_sbuff_adv_past_allowed(fr_sbuff_t *sbuff, size_t len, bool const allowed[static UINT8_MAX+1], fr_sbuff_term_t const *tt)
Wind position past characters in the allowed set.
Definition sbuff.c:1777
bool const sbuff_char_class_hex[UINT8_MAX+1]
Definition sbuff.c:94
bool fr_sbuff_is_terminal(fr_sbuff_t *in, fr_sbuff_term_t const *tt)
Efficient terminal string search.
Definition sbuff.c:2152
size_t fr_sbuff_adv_past_strcase(fr_sbuff_t *sbuff, char const *needle, size_t needle_len)
Return true and advance past the end of the needle if needle occurs next in the sbuff.
Definition sbuff.c:1743
size_t fr_sbuff_adv_until(fr_sbuff_t *sbuff, size_t len, fr_sbuff_term_t const *tt, char escape_chr)
Wind position until we hit a character in the terminal set.
Definition sbuff.c:1852
bool fr_sbuff_next_if_char(fr_sbuff_t *sbuff, char c)
Return true if the current char matches, and if it does, advance.
Definition sbuff.c:2088
#define fr_sbuff_start(_sbuff_or_marker)
#define fr_sbuff_out_by_longest_prefix(_match_len, _out, _table, _sbuff, _def)
#define FR_SBUFF_IN_TABLE_STR_RETURN(_sbuff, _table, _number, _def)
#define fr_sbuff_adv_past_str_literal(_sbuff, _needle)
#define FR_SBUFF_IN_CHAR_RETURN(_sbuff,...)
#define fr_sbuff_set(_dst, _src)
#define FR_SBUFF_IN(_start, _len_or_end)
#define fr_sbuff_adv_past_whitespace(_sbuff, _len, _tt)
#define fr_sbuff_adv_past_strcase_literal(_sbuff, _needle)
#define fr_sbuff_current(_sbuff_or_marker)
#define FR_SBUFF_REPARSE(_sbuff_or_marker)
#define FR_SBUFF_IN_ESCAPE_BUFFER_RETURN(...)
#define FR_SBUFF_TERMS(...)
Initialise a terminal structure with a list of sorted strings.
Definition sbuff.h:192
#define FR_SBUFF_IN_STRCPY_LITERAL_RETURN(_sbuff, _str)
#define fr_sbuff_extend(_sbuff_or_marker)
#define FR_SBUFF_RETURN(_func, _sbuff,...)
#define fr_sbuff_is_char(_sbuff_or_marker, _c)
#define FR_SBUFF_ERROR_RETURN(_sbuff_or_marker)
#define FR_SBUFF_SET_RETURN(_dst, _src)
#define FR_SBUFF_IN_SPRINTF_RETURN(...)
#define FR_SBUFF(_sbuff_or_marker)
#define FR_SBUFF_IN_BSTRNCPY_RETURN(...)
#define fr_sbuff_advance(_sbuff_or_marker, _len)
#define fr_sbuff_out(_err, _out, _in)
#define fr_sbuff_switch(_sbuff_or_marker, _eob)
#define FR_SBUFF_OUT(_start, _len_or_end)
#define fr_sbuff_used(_sbuff_or_marker)
#define FR_SBUFF_IN_STRCPY_RETURN(...)
#define FR_SBUFF_IN_BSTRCPY_BUFFER_RETURN(...)
Set of terminal elements.
static fr_slen_t ar_prefix
Definition tmpl.h:1279
#define tmpl_contains_xlat(vpt)
Definition tmpl.h:231
#define TMPL_VERIFY(_vpt)
Definition tmpl.h:969
#define tmpl_value_length(_tmpl)
Definition tmpl.h:949
static char const * tmpl_type_to_str(tmpl_type_t type)
Return a static string containing the type name.
Definition tmpl.h:645
#define tmpl_is_uninitialised(vpt)
Helpers to verify the type of tmpl_t.
Definition tmpl.h:208
#define tmpl_is_attr_unresolved(vpt)
Definition tmpl.h:224
enum requests_ref_e tmpl_request_ref_t
static bool tmpl_attr_tail_is_unspecified(tmpl_t const *vpt)
Return true if the last attribute reference is "unspecified".
Definition tmpl.h:737
#define NUM_LAST
Definition tmpl.h:402
#define tmpl_rules_enumv(_tmpl)
Definition tmpl.h:954
static fr_slen_t vpt
Definition tmpl.h:1272
static bool tmpl_attr_tail_is_normal(tmpl_t const *vpt)
Return true if the last attribute reference is "normal".
Definition tmpl.h:721
#define tmpl_value(_tmpl)
Definition tmpl.h:948
#define ar_is_unknown(_ar)
Definition tmpl.h:516
static bool tmpl_attr_tail_is_unknown(tmpl_t const *vpt)
Return true if the last attribute reference is "unknown".
Definition tmpl.h:753
#define tmpl_contains_regex(vpt)
Definition tmpl.h:230
fr_value_box_safe_for_t literals_safe_for
safe_for value assigned to literal values in xlats, execs, and data.
Definition tmpl.h:356
#define ar_is_raw(_ar)
Definition tmpl.h:518
#define tmpl_is_attr(vpt)
Definition tmpl.h:213
#define NUM_ALL
Definition tmpl.h:400
fr_dict_attr_t const * enumv
Enumeration attribute used to resolve enum values.
Definition tmpl.h:347
tmpl_rules_t const * parent
for parent / child relationships
Definition tmpl.h:342
#define tmpl_value_enumv(_tmpl)
Definition tmpl.h:951
#define tmpl_xlat(_tmpl)
Definition tmpl.h:941
static fr_dict_attr_t const * tmpl_list(tmpl_t const *vpt)
Definition tmpl.h:915
static bool tmpl_attr_is_list_attr(tmpl_attr_t const *ar)
Return true if the tmpl_attr is one of the list types.
Definition tmpl.h:690
#define ar_filter_is_num(_ar)
Definition tmpl.h:527
#define tmpl_rules_cast(_tmpl)
Definition tmpl.h:953
@ TMPL_TYPE_REGEX_UNCOMPILED
Regex where compilation is possible but hasn't been performed yet.
Definition tmpl.h:162
@ TMPL_TYPE_MAX
Marker for the last tmpl type.
Definition tmpl.h:203
@ TMPL_TYPE_ATTR_UNRESOLVED
An attribute reference that we couldn't resolve but looked valid.
Definition tmpl.h:189
@ TMPL_TYPE_ATTR
Reference to one or more attributes.
Definition tmpl.h:146
@ TMPL_TYPE_XLAT
Pre-parsed xlat expansion.
Definition tmpl.h:150
@ TMPL_TYPE_NULL
Has no value.
Definition tmpl.h:138
@ TMPL_TYPE_EXEC
Callout to an external script or program.
Definition tmpl.h:154
@ TMPL_TYPE_REGEX_XLAT_UNRESOLVED
A regular expression with unresolved xlat functions or attribute references.
Definition tmpl.h:201
@ TMPL_TYPE_DATA
Value in native boxed format.
Definition tmpl.h:142
@ TMPL_TYPE_REGEX
Compiled (and possibly JIT'd) regular expression.
Definition tmpl.h:158
@ TMPL_TYPE_DATA_UNRESOLVED
Unparsed literal string.
Definition tmpl.h:183
@ TMPL_TYPE_XLAT_UNRESOLVED
A xlat expansion with unresolved xlat functions or attribute references.
Definition tmpl.h:197
@ TMPL_TYPE_REGEX_XLAT
A regex containing xlat expansions.
Definition tmpl.h:166
@ TMPL_TYPE_EXEC_UNRESOLVED
An exec with unresolved xlat function or attribute references.
Definition tmpl.h:193
@ TMPL_TYPE_UNINITIALISED
Uninitialised.
Definition tmpl.h:134
#define tmpl_is_regex_xlat(vpt)
Definition tmpl.h:220
#define NUM_COUNT
Definition tmpl.h:401
#define tmpl_assert_type(_cond)
Convenience macro for printing a meaningful assert message when we get a bad tmpl type.
Definition tmpl.h:631
#define tmpl_contains_attr(vpt)
Definition tmpl.h:229
#define ar_da
Definition tmpl.h:508
#define TMPL_FLAG_REGEX
Is a type of regular expression.
Definition tmpl.h:116
#define ar_filter_is_cond(_ar)
Definition tmpl.h:528
tmpl_xlat_rules_t xlat
Rules/data for parsing xlats.
Definition tmpl.h:345
static char const * tmpl_attr_tail_unresolved(tmpl_t const *vpt)
Return the last attribute reference unresolved da.
Definition tmpl.h:880
bool at_runtime
Produce an ephemeral/runtime tmpl.
Definition tmpl.h:353
static bool tmpl_is_list(tmpl_t const *vpt)
Definition tmpl.h:931
#define TMPL_ATTR_VERIFY(_vpt)
Definition tmpl.h:968
bool force_dict_def
Use supplied dict_def even if original vpt->rules->dict_def was not NULL.
Definition tmpl.h:379
#define tmpl_is_data(vpt)
Definition tmpl.h:211
fr_dict_t const * dict_def
Alternative default dictionary to use if vpt->rules->dict_def is NULL.
Definition tmpl.h:374
#define NUM_UNSPEC
Definition tmpl.h:399
static size_t tmpl_attr_num_elements(tmpl_t const *vpt)
The number of attribute references contained within a tmpl.
Definition tmpl.h:907
#define tmpl_value_type(_tmpl)
Definition tmpl.h:950
#define tmpl_attr(_tmpl)
Definition tmpl.h:665
tmpl_attr_error_t
Definition tmpl.h:1012
@ TMPL_ATTR_ERROR_INVALID_ARRAY_INDEX
Invalid array index.
Definition tmpl.h:1031
@ TMPL_ATTR_ERROR_LIST_NOT_ALLOWED
List qualifier is not allowed here.
Definition tmpl.h:1016
@ TMPL_ATTR_ERROR_UNRESOLVED_NOT_ALLOWED
Attribute couldn't be found in the dictionaries.
Definition tmpl.h:1022
@ TMPL_ATTR_ERROR_BAD_CAST
Specified cast was invalid.
Definition tmpl.h:1035
@ TMPL_ATTR_ERROR_INVALID_NAME
Attribute ref length is zero, or longer than the maximum.
Definition tmpl.h:1024
@ TMPL_ATTR_ERROR_MISSING_TERMINATOR
Unexpected text found after attribute reference.
Definition tmpl.h:1034
@ TMPL_ATTR_ERROR_LIST_MISSING
List qualifier is required, but missing.
Definition tmpl.h:1017
@ TMPL_ATTR_ERROR_NONE
No error.
Definition tmpl.h:1013
@ TMPL_ATTR_ERROR_FOREIGN_NOT_ALLOWED
Attribute resolved in a dictionary different to the one specified.
Definition tmpl.h:1028
@ TMPL_ATTR_ERROR_UNKNOWN_NOT_ALLOWED
Attribute specified as OID, could not be found in the dictionaries, and is disallowed because 'disall...
Definition tmpl.h:1018
@ TMPL_ATTR_ERROR_FILTER_NOT_ALLOWED
Filters disallowed by rules.
Definition tmpl.h:1030
@ TMPL_ATTR_ERROR_EMPTY
Attribute ref contains no data.
Definition tmpl.h:1014
@ TMPL_ATTR_ERROR_NESTING_TOO_DEEP
Too many levels of nesting.
Definition tmpl.h:1033
@ TMPL_ATTR_ERROR_BAD_PREFIX
Missing '&' or has '&' when it shouldn't.
Definition tmpl.h:1015
#define tmpl_is_data_unresolved(vpt)
Definition tmpl.h:222
fr_type_t cast
Whether there was an explicit cast.
Definition tmpl.h:349
tmpl_attr_rules_t attr
Rules/data for parsing attribute references.
Definition tmpl.h:344
static fr_dict_attr_t const * tmpl_attr_tail_da(tmpl_t const *vpt)
Return the last attribute reference da.
Definition tmpl.h:812
@ TMPL_ATTR_LIST_REQUIRE
Attribute refs are required to have a list.
Definition tmpl.h:276
@ TMPL_ATTR_LIST_ALLOW
Attribute refs are allowed to have a list.
Definition tmpl.h:274
@ TMPL_ATTR_LIST_FORBID
Attribute refs are forbidden from having a list.
Definition tmpl.h:275
enum tmpl_type_e tmpl_type_t
Types of tmpl_t.
static fr_dict_attr_t const * tmpl_attr_tail_unknown(tmpl_t const *vpt)
Return the last attribute reference unknown da.
Definition tmpl.h:864
#define tmpl_is_regex(vpt)
Definition tmpl.h:218
static char const * tmpl_list_name(fr_dict_attr_t const *list, char const *def)
Return the name of a tmpl list or def if list not provided.
Definition tmpl.h:926
static bool tmpl_attr_tail_is_raw(tmpl_t const *vpt)
Return true if the last attribute reference is "raw".
Definition tmpl.h:785
@ REQUEST_OUTER
request_t containing the outer layer of the EAP conversation.
Definition tmpl.h:92
@ REQUEST_PARENT
Parent (whatever it is).
Definition tmpl.h:96
@ REQUEST_UNKNOWN
Unknown request.
Definition tmpl.h:97
@ REQUEST_CURRENT
The current request (default).
Definition tmpl.h:91
struct tmpl_rules_s tmpl_rules_t
Definition tmpl.h:236
#define tmpl_is_regex_xlat_unresolved(vpt)
Definition tmpl.h:226
#define tmpl_is_regex_uncompiled(vpt)
Definition tmpl.h:219
fr_dict_attr_t const * enumv
for resolving T_BARE_WORD
Definition tmpl.h:382
#define TMPL_MAX_REQUEST_REF_NESTING
The maximum number of request references allowed.
Definition tmpl.h:85
tmpl_attr_type_t
Definition tmpl.h:385
@ TMPL_ATTR_TYPE_UNSPEC
No attribute was specified as this level only a filter.
Definition tmpl.h:387
@ TMPL_ATTR_TYPE_NORMAL
Normal, resolved, attribute ref.
Definition tmpl.h:386
@ TMPL_ATTR_TYPE_UNKNOWN
We have an attribute number but it doesn't match anything in the dictionary, or isn't a child of the ...
Definition tmpl.h:389
@ TMPL_ATTR_TYPE_UNRESOLVED
We have a name, but nothing else to identify the attribute.
Definition tmpl.h:394
@ TMPL_ATTR_FILTER_TYPE_TMPL
Filter is a tmpl.
Definition tmpl.h:416
@ TMPL_ATTR_FILTER_TYPE_INDEX
Filter is an index type.
Definition tmpl.h:414
@ TMPL_ATTR_FILTER_TYPE_CONDITION
Filter is a condition.
Definition tmpl.h:415
@ TMPL_ATTR_FILTER_TYPE_NONE
No filter present.
Definition tmpl.h:413
@ TMPL_ATTR_FILTER_TYPE_EXPR
Filter is an expression.
Definition tmpl.h:417
fr_event_list_t * runtime_el
The eventlist to use for runtime instantiation of xlats.
Definition tmpl.h:333
#define tmpl_needs_resolving(vpt)
Definition tmpl.h:228
tmpl_attr_filter_type_t _CONST type
Type of filter this is.
Definition tmpl.h:421
#define ar_filter_is_none(_ar)
Definition tmpl.h:526
Similar to tmpl_rules_t, but used to specify parameters that may change during subsequent resolution ...
Definition tmpl.h:373
Optional arguments passed to vp_tmpl functions.
Definition tmpl.h:341
static char buff[sizeof("18446744073709551615")+3]
Definition size_tests.c:41
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition snprintf.c:689
fr_aka_sim_id_type_t type
Define entry and head types for tmpl request references.
Definition tmpl.h:284
tmpl_attr_list_presence_t list_presence
Whether the attribute reference can have a list, forbid it, or require it.
Definition tmpl.h:313
fr_dict_attr_t const * list_def
Default list to use with unqualified attribute reference.
Definition tmpl.h:307
uint8_t disallow_filters
disallow filters.
Definition tmpl.h:329
uint8_t allow_unresolved
Allow attributes that look valid but were not found in the dictionaries.
Definition tmpl.h:319
uint8_t allow_foreign
Allow arguments not found in dict_def.
Definition tmpl.h:327
fr_dict_t const * dict_def
Default dictionary to use with unqualified attribute references.
Definition tmpl.h:285
uint8_t allow_wildcard
Allow the special case of .
Definition tmpl.h:324
uint8_t allow_unknown
Allow unknown attributes i.e.
Definition tmpl.h:316
tmpl_attr_prefix_t prefix
Whether the attribute reference requires a prefix.
Definition tmpl.h:310
An element in a list of nested attribute references.
Definition tmpl.h:439
unsigned int _CONST resolve_only
This reference and those before it.
Definition tmpl.h:462
unsigned int _CONST is_raw
Definition tmpl.h:465
fr_dict_attr_t const *_CONST da
Resolved dictionary attribute.
Definition tmpl.h:443
fr_dict_attr_t const *_CONST parent
The parent we used when trying to resolve the attribute originally.
Definition tmpl.h:457
tmpl_attr_filter_t _CONST filter
Filter associated with the attribute reference.
Definition tmpl.h:469
tmpl_attr_type_t _CONST type
is a raw reference
Definition tmpl.h:467
Define manipulation functions for the attribute reference list.
Definition tmpl.h:480
tmpl_request_ref_t _CONST request
Definition tmpl.h:484
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition table.h:772
An element in an arbitrarily ordered array of name to num mappings.
Definition table.h:57
An element in a lexicographically sorted array of name to num mappings.
Definition table.h:49
char * talloc_bstrdup(TALLOC_CTX *ctx, char const *in)
Binary safe strdup function.
Definition talloc.c:536
char * talloc_bstrndup(TALLOC_CTX *ctx, char const *in, size_t inlen)
Binary safe strndup function.
Definition talloc.c:564
char * talloc_typed_strdup(TALLOC_CTX *ctx, char const *p)
Call talloc_strdup, setting the type on the new chunk correctly.
Definition talloc.c:445
#define talloc_get_type_abort_const
Definition talloc.h:282
static int talloc_const_free(void const *ptr)
Free const'd memory.
Definition talloc.h:224
#define talloc_pooled_object(_ctx, _type, _num_subobjects, _total_subobjects_size)
Definition talloc.h:180
fr_slen_t fr_time_delta_from_substr(fr_time_delta_t *out, fr_sbuff_t *in, fr_time_res_t hint, bool no_trailing, fr_sbuff_term_t const *tt)
Create fr_time_delta_t from a string.
Definition time.c:214
@ FR_TIME_RES_SEC
Definition time.h:50
A time delta, a difference in time measured in nanoseconds.
Definition time.h:80
Escaping rules for tmpls.
Definition tmpl_escape.h:80
int8_t tmpl_request_ref_list_cmp(FR_DLIST_HEAD(tmpl_request_list) const *a, FR_DLIST_HEAD(tmpl_request_list) const *b)
Compare a list of request qualifiers.
static ssize_t tmpl_afrom_time_delta(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules)
void tmpl_request_ref_list_debug(FR_DLIST_HEAD(tmpl_request_list) const *rql)
Dump a request list to stderr.
fr_slen_t tmpl_request_ref_list_print(fr_sbuff_t *out, FR_DLIST_HEAD(tmpl_request_list) const *rql)
void tmpl_attr_set_list(tmpl_t *vpt, fr_dict_attr_t const *list)
tmpl_t * tmpl_init_shallow(tmpl_t *vpt, tmpl_type_t type, fr_token_t quote, char const *name, ssize_t len, tmpl_rules_t const *t_rules)
Initialise a tmpl without copying the input name string.
fr_slen_t tmpl_request_ref_list_afrom_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err, FR_DLIST_HEAD(tmpl_request_list) **out, fr_sbuff_t *in)
Parse one or more request references, allocing a new list and adding the references to it.
static fr_table_num_sorted_t const attr_num_table[]
Special attribute reference indexes.
void tmpl_unresolve(tmpl_t *vpt)
Reset the tmpl, leaving only the name in place.
void tmpl_set_xlat(tmpl_t *vpt, xlat_exp_head_t *xlat)
Change the default dictionary in the tmpl's resolution rules.
static void tmpl_type_init(tmpl_t *vpt, tmpl_type_t type)
Initialise fields inside a tmpl depending on its type.
#define RESOLVED_SET(_flags)
static fr_slen_t tmpl_attr_ref_afrom_unresolved_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err, tmpl_t *vpt, fr_dict_attr_t const *parent, fr_dict_attr_t const *namespace, fr_sbuff_t *name, tmpl_attr_rules_t const *at_rules)
Parse an unresolved attribute, i.e.
void tmpl_attr_set_request_ref(tmpl_t *vpt, FR_DLIST_HEAD(tmpl_request_list) const *request_def)
Set the request for an attribute ref.
void tmpl_set_name_printf(tmpl_t *vpt, fr_token_t quote, char const *fmt,...)
Set the name on a pre-initialised tmpl.
int tmpl_attr_set_da(tmpl_t *vpt, fr_dict_attr_t const *da)
Replace the current attribute reference.
static fr_table_num_ordered_t const attr_table[]
Attr ref types.
static int tmpl_xlat_resolve(tmpl_t *vpt, tmpl_res_rules_t const *tr_rules)
Resolve an unresolved xlat, i.e.
fr_table_num_sorted_t const tmpl_request_ref_table[]
Map keywords to tmpl_request_ref_t values.
fr_slen_t tmpl_print(fr_sbuff_t *out, tmpl_t const *vpt, tmpl_attr_prefix_t ar_prefix, fr_sbuff_escape_rules_t const *e_rules)
Print a tmpl_t to a string.
int tmpl_afrom_value_box(TALLOC_CTX *ctx, tmpl_t **out, fr_value_box_t *data, bool steal)
Create a tmpl_t from a fr_value_box_t.
tmpl_t * tmpl_alloc(TALLOC_CTX *ctx, tmpl_type_t type, fr_token_t quote, char const *name, ssize_t len)
Create a new heap allocated tmpl_t.
int tmpl_attr_unknown_add(tmpl_t *vpt)
Add an unknown fr_dict_attr_t specified by a tmpl_t to the main dictionary.
bool tmpl_require_enum_prefix
void tmpl_set_escape(tmpl_t *vpt, tmpl_escape_t const *escape)
Set escape parameters for the tmpl output.
bool tmpl_async_required(tmpl_t const *vpt)
Return whether or not async is required for this tmpl.
void tmpl_rules_child_init(TALLOC_CTX *ctx, tmpl_rules_t *out, tmpl_rules_t const *parent, tmpl_t *vpt)
Initialize a set of rules from a parent set of rules, and a parsed tmpl_t.
ssize_t tmpl_afrom_attr_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err, tmpl_t **out, fr_sbuff_t *name, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules)
Parse a string into a TMPL_TYPE_ATTR_* type tmpl_t.
static fr_slen_t tmpl_attr_ref_from_unspecified_substr(tmpl_attr_t *ar, tmpl_attr_error_t *err, tmpl_t *vpt, fr_sbuff_t *name, tmpl_attr_rules_t const *at_rules)
static void attr_to_raw(tmpl_t *vpt, tmpl_attr_t *ref)
tmpl_t * tmpl_init_printf(tmpl_t *vpt, tmpl_type_t type, fr_token_t quote, char const *fmt,...)
Initialise a tmpl using a format string to create the name.
static void tmpl_attr_insert(tmpl_t *vpt, tmpl_attr_t *ar)
Insert an attribute reference into a tmpl.
int tmpl_cast_set(tmpl_t *vpt, fr_type_t dst_type)
Set a cast for a tmpl.
static fr_slen_t tmpl_afrom_ipv4_substr(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules)
Parse bareword as an IPv4 address or prefix.
void tmpl_set_name(tmpl_t *vpt, fr_token_t quote, char const *name, ssize_t len)
Set the name on a pre-initialised tmpl.
int tmpl_attr_afrom_list(TALLOC_CTX *ctx, tmpl_t **out, tmpl_t const *list, fr_dict_attr_t const *da)
Create a new tmpl from a list tmpl and a da.
void tmpl_attr_to_raw(tmpl_t *vpt)
Convert the leaf attribute of a tmpl to a unknown/raw type.
tmpl_t * tmpl_copy(TALLOC_CTX *ctx, tmpl_t const *in)
Copy a tmpl.
void tmpl_attr_ref_debug(const tmpl_attr_t *ar, int i)
void tmpl_attr_rewrite_leaf_num(tmpl_t *vpt, int16_t to)
Rewrite the leaf's instance number.
static bool tmpl_substr_terminal_check(fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules)
Verify, after skipping whitespace, that a substring ends in a terminal char, or ends without further ...
static void tmpl_request_ref_list_copy(TALLOC_CTX *ctx, FR_DLIST_HEAD(tmpl_request_list) *out, FR_DLIST_HEAD(tmpl_request_list) const *in)
Allocate a new request reference and add it to the end of the attribute reference list.
static void tmpl_attr_rules_debug(tmpl_attr_rules_t const *at_rules)
ssize_t tmpl_preparse(char const **out, size_t *outlen, char const *in, size_t inlen, fr_token_t *type)
Preparse a string in preparation for passing it to tmpl_afrom_substr()
#define TMPL_REQUEST_REF_DEF(_name, _ref)
Define a global variable for specifying a default request reference.
#define UNRESOLVED_SET(_flags)
void tmpl_debug(tmpl_t const *vpt)
void tmpl_set_name_shallow(tmpl_t *vpt, fr_token_t quote, char const *name, ssize_t len)
Set the name on a pre-initialised tmpl.
fr_slen_t tmpl_attr_print(fr_sbuff_t *out, tmpl_t const *vpt, tmpl_attr_prefix_t ar_prefix)
Print an attribute or list tmpl_t to a string.
static int tmpl_attr_afrom_attr_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err, tmpl_t *vpt, fr_dict_attr_t const *parent, fr_dict_attr_t const *namespace, fr_sbuff_t *name, fr_sbuff_parse_rules_t const *p_rules, tmpl_attr_rules_t const *at_rules, unsigned int depth)
Parse an attribute reference, either an OID or attribute name.
static fr_slen_t tmpl_afrom_ipv6_substr(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules)
Parse bareword as an IPv6 address or prefix.
static fr_slen_t tmpl_afrom_bool_substr(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules)
Parse a truth value.
static tmpl_attr_t * tmpl_attr_add(tmpl_t *vpt, tmpl_attr_type_t type)
Allocate a new attribute reference and add it to the end of the attribute reference list.
static size_t attr_num_table_len
#define return_P(_x)
ssize_t tmpl_afrom_attr_str(TALLOC_CTX *ctx, tmpl_attr_error_t *err, tmpl_t **out, char const *name, tmpl_rules_t const *t_rules)
Parse a string into a TMPL_TYPE_ATTR_* type tmpl_t.
fr_slen_t tmpl_print_quoted(fr_sbuff_t *out, tmpl_t const *vpt, tmpl_attr_prefix_t ar_prefix)
Print a tmpl_t to a string with quotes.
void tmpl_attr_ref_list_debug(FR_DLIST_HEAD(tmpl_attr_list) const *ar_head)
ssize_t tmpl_cast_from_substr(tmpl_rules_t *rules, fr_sbuff_t *in)
Parse a cast specifier.
static tmpl_t * tmpl_alloc_null(TALLOC_CTX *ctx)
Create a new heap allocated tmpl_t.
static fr_slen_t tmpl_request_ref_list_from_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err, FR_DLIST_HEAD(tmpl_request_list) *out, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules, fr_dict_attr_t const **namespace)
Parse one or more request references, writing the list to out.
void tmpl_rules_debug(tmpl_rules_t const *rules)
int tmpl_attr_copy(tmpl_t *dst, tmpl_t const *src)
Copy a list of attribute and request references from one tmpl to another.
static ssize_t tmpl_afrom_ether_substr(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules)
Try and parse signed or unsigned integers.
static ssize_t tmpl_afrom_float_substr(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules)
static fr_slen_t tmpl_afrom_octets_substr(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules)
Parse bareword as an octet string.
size_t tmpl_type_table_len
static int tmpl_attr_resolve(tmpl_t *vpt, tmpl_res_rules_t const *tr_rules)
Resolve an unresolved attribute.
static ssize_t tmpl_afrom_enum(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules)
int tmpl_attr_set_leaf_da(tmpl_t *vpt, fr_dict_attr_t const *da)
Replace the leaf attribute only.
fr_slen_t tmpl_afrom_substr(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in, fr_token_t quote, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules)
Convert an arbitrary string into a tmpl_t.
static fr_token_t tmpl_cast_quote(fr_token_t existing_quote, fr_type_t type, fr_dict_attr_t const *enumv, char const *unescaped, size_t unescaped_len)
Determine the correct quoting after a cast.
fr_dict_attr_t const * tmpl_attr_unspec
Placeholder attribute for uses of unspecified attribute references.
Definition tmpl_eval.c:61
static fr_slen_t tmpl_afrom_value_substr(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in, fr_token_t quote, tmpl_rules_t const *t_rules, bool allow_enum, fr_sbuff_parse_rules_t const *p_rules)
Create TMPL_TYPE_DATA from a string.
int tmpl_resolve(tmpl_t *vpt, tmpl_res_rules_t const *tr_rules)
Attempt to resolve functions and attributes in xlats and attribute references.
void tmpl_set_dict_def(tmpl_t *vpt, fr_dict_t const *dict)
Change the default dictionary in the tmpl's resolution rules.
tmpl_t * tmpl_init(tmpl_t *vpt, tmpl_type_t type, fr_token_t quote, char const *name, ssize_t len, tmpl_rules_t const *t_rules)
Initialise a tmpl using a literal string to create the name.
static void tmpl_request_ref_list_acopy(TALLOC_CTX *ctx, FR_DLIST_HEAD(tmpl_request_list) **out, FR_DLIST_HEAD(tmpl_request_list) const *in)
Allocate a new request reference list and copy request references into it.
static fr_slen_t tmpl_afrom_integer_substr(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules)
Try and parse signed or unsigned integers.
int tmpl_cast_in_place(tmpl_t *vpt, fr_type_t type, fr_dict_attr_t const *enumv)
Convert tmpl_t of type TMPL_TYPE_DATA_UNRESOLVED or TMPL_TYPE_DATA to TMPL_TYPE_DATA of type specifie...
static void tmpl_attr_ref_fixup(TALLOC_CTX *ctx, tmpl_t *vpt, fr_dict_attr_t const *da, fr_dict_attr_t const *parent)
static fr_slen_t tmpl_attr_parse_filter(tmpl_attr_error_t *err, tmpl_attr_t *ar, fr_sbuff_t *name, tmpl_attr_rules_t const *at_rules)
Parse array subscript and in future other filters.
int tmpl_attr_to_xlat(TALLOC_CTX *ctx, tmpl_t **vpt_p)
Convert an attribute reference to an xlat expansion.
fr_slen_t tmpl_attr_list_from_substr(fr_dict_attr_t const **da_p, fr_sbuff_t *in)
Parse one a single list reference.
#define DEFAULT_RULES
Default parser rules.
fr_table_num_ordered_t const tmpl_type_table[]
Map tmpl_type_t values to descriptive strings.
void tmpl_attr_debug(tmpl_t const *vpt)
static size_t attr_table_len
int tmpl_attr_tail_unresolved_add(fr_dict_t *dict_def, tmpl_t *vpt, fr_type_t type, fr_dict_attr_flags_t const *flags)
Add an unresolved fr_dict_attr_t specified by a tmpl_t to the main dictionary.
size_t tmpl_request_ref_table_len
fr_table_num_sorted_t const fr_token_quotes_table[]
Definition token.c:66
const char fr_token_quote[T_TOKEN_LAST]
Convert tokens back to a quoting character.
Definition token.c:156
enum fr_token fr_token_t
@ T_INVALID
Definition token.h:39
@ T_SINGLE_QUOTED_STRING
Definition token.h:122
@ T_BARE_WORD
Definition token.h:120
@ T_BACK_QUOTED_STRING
Definition token.h:123
@ T_DOUBLE_QUOTED_STRING
Definition token.h:121
@ T_SOLIDUS_QUOTED_STRING
Definition token.h:124
#define T_TOKEN_LAST
Definition token.h:129
close(uq->fd)
bool xlat_needs_resolving(xlat_exp_head_t const *head)
Check to see if the expansion needs resolving.
bool xlat_is_literal(xlat_exp_head_t const *head)
Check to see if the expansion consists entirely of value-box elements.
int xlat_from_tmpl_attr(TALLOC_CTX *ctx, xlat_exp_head_t **head, tmpl_t **vpt_p)
Convert attr tmpl to an xlat for &attr[*].
fr_slen_t xlat_print(fr_sbuff_t *in, xlat_exp_head_t const *node, fr_sbuff_escape_rules_t const *e_rules)
Reconstitute an xlat expression from its constituent nodes.
bool xlat_impure_func(xlat_exp_head_t const *head)
fr_slen_t xlat_tokenize_argv(TALLOC_CTX *ctx, xlat_exp_head_t **head, fr_sbuff_t *in, xlat_t const *xlat, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules, bool comma, bool allow_attr)
Tokenize an xlat expansion into a series of XLAT_TYPE_CHILD arguments.
static fr_slen_t head
Definition xlat.h:422
static fr_slen_t xlat_aprint(TALLOC_CTX *ctx, char **out, xlat_exp_head_t const *head, fr_sbuff_escape_rules_t const *e_rules) 1(xlat_print
bool xlat_to_string(TALLOC_CTX *ctx, char **str, xlat_exp_head_t **head)
Convert an xlat node to an unescaped literal string and free the original node.
#define xlat_copy(_ctx, _out, _in)
Definition xlat.h:459
fr_slen_t xlat_tokenize(TALLOC_CTX *ctx, xlat_exp_head_t **head, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules, fr_value_box_safe_for_t literals_safe_for)
Tokenize an xlat expansion.
int xlat_resolve(xlat_exp_head_t *head, xlat_res_rules_t const *xr_rules)
Walk over an xlat tree recursively, resolving any unresolved functions or references.
int xlat_finalize(xlat_exp_head_t *head, fr_event_list_t *runtime_el)
Bootstrap static xlats, or instantiate ephemeral ones.
Definition xlat_inst.c:695
fr_slen_t xlat_tokenize_condition(TALLOC_CTX *ctx, xlat_exp_head_t **head, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules)
Definition xlat_expr.c:3171
fr_slen_t xlat_tokenize_expression(TALLOC_CTX *ctx, xlat_exp_head_t **head, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules)
Definition xlat_expr.c:3165
static fr_slen_t parent
Definition pair.h:851
Structure for holding the stack of dictionary attributes being encoded.
Definition proto.h:54
void fr_strerror_clear(void)
Clears all pending messages from the talloc pools.
Definition strerror.c:577
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
#define fr_strerror_const_push(_msg)
Definition strerror.h:227
#define fr_strerror_const(_msg)
Definition strerror.h:223
fr_table_num_ordered_t const fr_type_table[]
Map data types to names representing those types.
Definition types.c:31
bool fr_type_cast(fr_type_t dst, fr_type_t src)
Return if we're allowed to cast the types.
Definition types.c:283
bool const fr_type_numeric[FR_TYPE_MAX+1]
Definition types.c:173
bool const fr_type_structural[FR_TYPE_MAX+1]
Definition types.c:183
#define fr_type_is_non_leaf(_x)
Definition types.h:373
#define fr_type_is_octets(_x)
Definition types.h:328
#define fr_type_is_structural(_x)
Definition types.h:371
#define fr_type_is_string(_x)
Definition types.h:327
#define FR_TYPE_STRUCTURAL
Definition types.h:296
#define fr_type_is_null(_x)
Definition types.h:326
#define fr_type_is_leaf(_x)
Definition types.h:372
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
Definition types.h:433
#define FR_TYPE_LEAF
Definition types.h:297
ssize_t fr_value_box_from_substr(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *rules, bool tainted)
Convert string value to a fr_value_box_t type.
Definition value.c:4802
ssize_t fr_value_box_from_str(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, char const *in, size_t inlen, fr_sbuff_unescape_rules_t const *erules, bool tainted)
Definition value.c:5315
ssize_t fr_value_box_print(fr_sbuff_t *out, fr_value_box_t const *data, fr_sbuff_escape_rules_t const *e_rules)
Print one boxed value to a string.
Definition value.c:5352
int fr_value_box_copy(TALLOC_CTX *ctx, fr_value_box_t *dst, const fr_value_box_t *src)
Copy value data verbatim duplicating any buffers.
Definition value.c:3740
int fr_value_box_cast_in_place(TALLOC_CTX *ctx, fr_value_box_t *vb, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv)
Convert one type of fr_value_box_t to another in place.
Definition value.c:3572
void fr_value_box_memdup_shallow(fr_value_box_t *dst, fr_dict_attr_t const *enumv, uint8_t const *src, size_t len, bool tainted)
Assign a buffer to a box, but don't copy it.
Definition value.c:4548
void fr_value_box_copy_shallow(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_value_box_t const *src)
Perform a shallow copy of a value_box.
Definition value.c:3834
fr_sbuff_escape_rules_t * fr_value_escape_by_quote[T_TOKEN_LAST]
Definition value.c:441
int fr_value_box_steal(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_value_box_t *src)
Copy value data verbatim moving any buffers to the specified context.
Definition value.c:3858
void fr_value_box_clear(fr_value_box_t *data)
Clear/free any existing value and metadata.
Definition value.c:3723
int fr_value_box_memdup(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_dict_attr_t const *enumv, uint8_t const *src, size_t len, bool tainted)
Copy a buffer to a fr_value_box_t.
Definition value.c:4468
#define fr_value_box_mark_safe_for(_box, _safe_for)
Definition value.h:1048
static fr_slen_t fr_value_box_aprint(TALLOC_CTX *ctx, char **out, fr_value_box_t const *data, fr_sbuff_escape_rules_t const *e_rules) 1(fr_value_box_print
#define fr_box_strvalue_buffer(_val)
Definition value.h:289
static fr_slen_t data
Definition value.h:1265
#define fr_box_strvalue_len(_val, _len)
Definition value.h:286
static size_t char fr_sbuff_t size_t inlen
Definition value.h:997
#define vb_strvalue
Definition value.h:241
int nonnull(2, 5))
#define fr_value_box_init(_vb, _type, _enumv, _tainted)
Initialise a fr_value_box_t.
Definition value.h:587
static size_t char ** out
Definition value.h:997