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