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