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