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: 89d829788ad86caa06e1b143e42fc0abefdc4e1b $
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: 89d829788ad86caa06e1b143e42fc0abefdc4e1b $")
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#include <freeradius-devel/util/skip.h>
37
38
39/*
40 * For xlat_exp_head_alloc(), because xlat_copy() doesn't create an output head.
41 */
42#include <freeradius-devel/unlang/xlat_priv.h>
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(FILE *fp, 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 fprintf(fp, "\t[%u] %s null%s%s%s\n",
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 fprintf(fp, "\t[%u] %s %s %s%s%s%s (%p) attr %u\n ",
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 fprintf(fp, "\t is_raw : %s\n", ar_is_raw(ar) ? "yes" : "no");
228 fprintf(fp, "\t is_unknown : %s", ar_is_unknown(ar) ? "yes" : "no");
229 if (ar->ar_parent) fprintf(fp, "\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 fprintf(fp, "\t[%u] %s %s%s%s%s\n",
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) fprintf(fp, "\t parent : %s", ar->ar_parent->name);
246 if (ar->ar_unresolved_namespace) fprintf(fp, "\t namespace : %s", ar->ar_unresolved_namespace->name);
247 break;
248
249 default:
250 fprintf(fp, "\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(FILE *fp, FR_DLIST_HEAD(tmpl_attr_list) const *ar_head)
257{
258 tmpl_attr_t *ar = NULL;
259 unsigned int i = 0;
260
261 fprintf(fp, "attribute references:\n");
262 /*
263 * Print all the attribute references
264 */
265 while ((ar = tmpl_attr_list_next(ar_head, ar))) {
266 tmpl_attr_ref_debug(fp, ar, i);
267 i++;
268 }
269}
270
271void tmpl_attr_debug(FILE *fp, tmpl_t const *vpt)
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 fprintf(fp, "%s can't print tmpls of type %s\n", __FUNCTION__,
283 tmpl_type_to_str(vpt->type));
284 return;
285 }
286
287 fprintf(fp, "tmpl_t %s (%.8x) \"%pV\" (%p)\n",
288 tmpl_type_to_str(vpt->type),
289 vpt->type,
290 fr_box_strvalue_len(vpt->name, vpt->len), vpt);
291
292 fprintf(fp, "\tcast : %s\n", fr_type_to_str(tmpl_rules_cast(vpt)));
293 fprintf(fp, "\tquote : %s\n", fr_table_str_by_value(fr_token_quotes_table, vpt->quote, "<INVALID>"));
294
295 fprintf(fp, "request references:");
296
297 /*
298 * Print all the request references
299 */
300 while ((rr = tmpl_request_list_next(&vpt->data.attribute.rr, rr))) {
301 fprintf(fp, "\t[%u] %s (%u)\n", i,
303 i++;
304 }
305
306 fprintf(fp, "list: %s\n", tmpl_list_name(tmpl_list(vpt), "<INVALID>"));
308}
309
310void tmpl_debug(FILE *fp, tmpl_t const *vpt)
311{
312 switch (vpt->type) {
313 case TMPL_TYPE_ATTR:
315 tmpl_attr_debug(fp, vpt);
316 return;
317
318 default:
319 break;
320 }
321
322 fprintf(fp, "tmpl_t %s (%.8x) \"%pR\" (%p)\n",
323 tmpl_type_to_str(vpt->type),
324 vpt->type,
325 vpt->name, vpt);
326
327 fprintf(fp, "\tcast : %s\n", fr_type_to_str(tmpl_rules_cast(vpt)));
328 fprintf(fp, "\tquote : %s\n", fr_table_str_by_value(fr_token_quotes_table, vpt->quote, "<INVALID>"));
329 switch (vpt->type) {
330 case TMPL_TYPE_DATA:
331 fprintf(fp, "\ttype : %s\n", fr_type_to_str(tmpl_value_type(vpt)));
332 fprintf(fp, "\tlen : %zu\n", tmpl_value_length(vpt));
333 fprintf(fp, "\tvalue : %pV\n", tmpl_value(vpt));
334
335 if (tmpl_value_enumv(vpt)) fprintf(fp, "\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 fprintf(fp, "\texpansion : %s\n", str);
348
349 talloc_free(str);
350 }
351 break;
352
353 case TMPL_TYPE_REGEX:
354 {
355 fprintf(fp, "\tpattern : %s\n", vpt->name);
356 }
357 break;
358
359 default:
362 fprintf(fp, "\tunescaped : %s\n", vpt->data.unescaped);
363 fprintf(fp, "\tlen : %zu\n", talloc_array_length(vpt->data.unescaped) - 1);
364 } else {
365 fprintf(fp, "\tunresolved : %s\n", vpt->name);
366 fprintf(fp, "\tlen : %zu\n", vpt->len);
367 }
368 } else {
369 fprintf(fp, "debug nyi\n");
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) || (vpt->type == TMPL_TYPE_EXEC));
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 (unlikely(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 tmp = FR_SBUFF(&our_name);
1415 t_rules = (tmpl_rules_t) {};
1416 t_rules.attr = *at_rules;
1417
1418 /*
1419 * Unspecified child, we can create a filter starting from the children.
1420 *
1421 * @todo - When parsing the condition, we need to ensure that the condition contains a
1422 * reference to the current cursor, and we need to decide what that syntax is.
1423 */
1424 if (ar->type == TMPL_ATTR_TYPE_UNSPEC) {
1425 if (at_rules->dict_def) t_rules.attr.namespace = fr_dict_root(at_rules->dict_def);
1426
1427 } else {
1428 if (!ar->ar_da || !fr_type_is_structural(ar->ar_da->type)) {
1429 fr_strerror_printf("Invalid filter - cannot use filter on leaf attributes");
1430 ar->ar_num = 0;
1431 goto error;
1432 }
1433 t_rules.attr.namespace = ar->ar_da;
1434 }
1435
1436 p_rules = (fr_sbuff_parse_rules_t) {
1437 .terminals = &filter_terminals,
1438 .escapes = NULL
1439 };
1440
1441 /*
1442 * Check if it's a condition.
1443 */
1444 slen = xlat_tokenize_condition(ar, &ar->ar_cond, &tmp, &p_rules, &t_rules);
1445 if (slen < 0) goto error;
1446
1447 if (xlat_impure_func(ar->ar_cond)) {
1448 fr_strerror_const("Condition in attribute index cannot depend on functions which call external databases");
1449 goto error;
1450 }
1451
1452 ar->ar_filter_type = TMPL_ATTR_FILTER_TYPE_CONDITION;
1453 fr_sbuff_set(&our_name, &tmp); /* Advance name _AFTER_ doing checks */
1454 break;
1455 }
1456
1457 case '%': /* ${...} expansion */
1458 {
1459 fr_sbuff_t tmp = FR_SBUFF(&our_name);
1460 fr_slen_t slen;
1461 tmpl_rules_t t_rules;
1462 fr_sbuff_parse_rules_t p_rules;
1463 fr_sbuff_term_t const filter_terminals = FR_SBUFF_TERMS(L("]"));
1464
1465 if (!fr_sbuff_is_str(&our_name, "%{", 2)) {
1466 fr_strerror_const("Invalid expression in attribute index");
1467 goto error;
1468 }
1469
1470 tmp = FR_SBUFF(&our_name);
1471 t_rules = (tmpl_rules_t) {};
1472 t_rules.attr = *at_rules;
1473
1474 p_rules = (fr_sbuff_parse_rules_t) {
1475 .terminals = &filter_terminals,
1476 .escapes = NULL
1477 };
1478
1479 /*
1480 * Check if it's an expression.
1481 */
1482 slen = xlat_tokenize_expression(ar, &ar->ar_expr, &tmp, &p_rules, &t_rules);
1483 if (slen < 0) goto error;
1484
1485 if (xlat_impure_func(ar->ar_expr)) {
1486 fr_strerror_const("Expression in attribute index cannot depend on functions which call external databases");
1487 goto error;
1488 }
1489
1490 ar->ar_filter_type = TMPL_ATTR_FILTER_TYPE_EXPR;
1491
1492 fr_sbuff_set(&our_name, &tmp); /* Advance name _AFTER_ doing checks */
1493 break;
1494 }
1495
1496 case 'n':
1497 /*
1498 * [n] is the last one
1499 *
1500 * [nope] is a reference to "nope".
1501 */
1502 if (fr_sbuff_is_str(&our_name, "n]", 2)) {
1503 ar->ar_num = NUM_LAST;
1504 fr_sbuff_next(&our_name);
1505 break;
1506 }
1508
1509 default:
1510 parse_tmpl:
1511 {
1512 fr_sbuff_t tmp = FR_SBUFF(&our_name);
1513 ssize_t slen;
1514 tmpl_rules_t t_rules;
1515 fr_sbuff_parse_rules_t p_rules;
1516 fr_sbuff_term_t const filter_terminals = FR_SBUFF_TERMS(L("]"));
1517
1518 tmp = FR_SBUFF(&our_name);
1519 t_rules = (tmpl_rules_t) {};
1520 t_rules.attr = *at_rules;
1521
1522 /*
1523 * Don't reset namespace, we always want to start searching from the top level of the
1524 * dictionaries.
1525 */
1526
1527 p_rules = (fr_sbuff_parse_rules_t) {
1528 .terminals = &filter_terminals,
1529 .escapes = NULL
1530 };
1531
1532 /*
1533 * @todo - for some reason, the tokenize_condition code allows for internal
1534 * vs protocol vs local attributes, whereas the tmpl function only accepts
1535 * internal ones.
1536 */
1537 slen = tmpl_afrom_substr(ar, &ar->ar_tmpl, &tmp, T_BARE_WORD, &p_rules, &t_rules);
1538 if (slen <= 0) goto error;
1539
1540 if (!tmpl_is_attr(ar->ar_tmpl)) {
1541 fr_strerror_printf("Invalid array index '%s'", ar->ar_tmpl->name);
1542 goto error;
1543 }
1544
1545 /*
1546 * Arguably we _could_ say &User-Name["foo"] matches all user-name with value "foo",
1547 * but that would confuse the issue for &Integer-Thing[4].
1548 *
1549 * For matching therefore, we really need to have a way to define "self".
1550 */
1551 if (!fr_type_numeric[tmpl_attr_tail_da(ar->ar_tmpl)->type]) {
1552 fr_strerror_const("Invalid data type for array index (must be numeric)");
1553 goto error;
1554 }
1555
1556 ar->ar_filter_type = TMPL_ATTR_FILTER_TYPE_TMPL;
1557 fr_sbuff_set(&our_name, &tmp); /* Advance name _AFTER_ doing checks */
1558 break;
1559 }
1560 }
1561
1562 /*
1563 * Always advance here, so the error
1564 * marker points to the bad char.
1565 */
1566 if (!fr_sbuff_next_if_char(&our_name, ']')) goto missing_closing;
1567
1568 FR_SBUFF_SET_RETURN(name, &our_name);
1569}
1570
1571extern fr_dict_attr_t const *tmpl_attr_unspec;
1572
1573static inline CC_HINT(nonnull(3,4))
1575 tmpl_t *vpt,
1576 fr_sbuff_t *name, tmpl_attr_rules_t const *at_rules)
1577{
1578 fr_slen_t slen;
1579
1580 *ar = (tmpl_attr_t){
1581 .ar_num = NUM_UNSPEC, /* May be changed by tmpl_attr_parse_filter */
1582 .ar_type = TMPL_ATTR_TYPE_UNSPEC,
1583 .ar_da = tmpl_attr_unspec,
1584 };
1585
1586 slen = tmpl_attr_parse_filter(err, ar, name, at_rules);
1587 if (slen < 0) {
1588 return slen;
1589
1590 /*
1591 * No filters and no previous elements is the equivalent of '&'
1592 * which is not allowed.
1593 *
1594 * &[<filter>] is allowed as this lets us perform filtering operations
1595 * at the root.
1596 */
1597 } else if ((slen == 0) && (tmpl_attr_num_elements(vpt) == 0)) {
1598 fr_strerror_const("Invalid attribute name");
1600 return -1;
1601 }
1602
1603 tmpl_attr_insert(vpt, ar);
1604
1605 return slen;
1606}
1607
1608/** Parse an unresolved attribute, i.e. one which can't be found in the current dictionary
1609 *
1610 * This function calls itself recursively to process additional OID
1611 * components once we've failed to resolve one component.
1612 *
1613 * @note Do not call directly.
1614 *
1615 * @param[in] ctx to allocate new attribute reference in.
1616 * @param[out] err Parse error.
1617 * @param[in,out] vpt to append this reference to.
1618 * @param[in] parent Last known parent.
1619 * @param[in] namespace in which the attribute will be resolved.
1620 * @param[in] name to parse.
1621 * @param[in] at_rules see tmpl_attr_afrom_attr_substr.
1622 * @return
1623 * - <0 on error.
1624 * - 0 on success.
1625 */
1626static inline CC_HINT(nonnull(3,6))
1628 tmpl_t *vpt,
1629 fr_dict_attr_t const *parent, fr_dict_attr_t const *namespace,
1630 fr_sbuff_t *name, tmpl_attr_rules_t const *at_rules)
1631{
1632 tmpl_attr_t *ar = NULL, *ar_curr;
1633 fr_sbuff_t our_name = FR_SBUFF(name);
1634 fr_slen_t slen;
1635 char *unresolved;
1636
1637 /*
1638 * Point we free from if something goes wrong.
1639 */
1640 ar_curr = tmpl_attr_list_tail(tmpl_attr(vpt));
1641 for (;;) {
1642 MEM(ar = talloc(ctx, tmpl_attr_t));
1643 /*
1644 * Copy out a string of allowed dictionary chars to form
1645 * the unresolved attribute name.
1646 *
1647 * This will be resolved later (outside of this function).
1648 */
1649 slen = fr_sbuff_out_abstrncpy_allowed(ar, &unresolved,
1650 &our_name, FR_DICT_ATTR_MAX_NAME_LEN + 1,
1652 if (slen == 0) {
1653 slen = tmpl_attr_ref_from_unspecified_substr(ar, err, vpt, &our_name, at_rules);
1654 if (slen < 0) {
1655 fr_sbuff_advance(&our_name, +slen);
1656 error:
1657 talloc_free(ar);
1658 tmpl_attr_list_talloc_free_to_tail(tmpl_attr(vpt), ar_curr);
1659 return -1;
1660 }
1661 return fr_sbuff_set(name, &our_name);
1662 } else if (slen > FR_DICT_ATTR_MAX_NAME_LEN) {
1663 fr_strerror_const("Attribute name is too long");
1665 goto error;
1666 }
1667
1668 *ar = (tmpl_attr_t){
1669 .ar_num = NUM_UNSPEC,
1670 .ar_type = TMPL_ATTR_TYPE_UNRESOLVED,
1671 .ar_unresolved = unresolved,
1672 .ar_unresolved_namespace = namespace,
1673 .ar_parent = parent
1674 };
1675
1676 if (tmpl_attr_parse_filter(err, ar, &our_name, at_rules) < 0) goto error;
1677
1678 /*
1679 * Insert the ar into the list of attribute references
1680 */
1681 tmpl_attr_insert(vpt, ar);
1682
1683 /*
1684 * Once one OID component is created as unresolved all
1685 * future OID components are also unresolved.
1686 */
1687 if (!fr_sbuff_next_if_char(&our_name, '.')) break;
1688 }
1689
1690 /*
1691 * Mark the tmpl up as an unresolved attribute reference
1692 * the attribute reference will be resolved later.
1693 */
1695
1696 return fr_sbuff_set(name, &our_name);
1697}
1698
1699/*
1700 * Add attr_ref when we've parsed an intermediate dictionary name
1701 * which is itself a ref.
1702 */
1703static void tmpl_attr_ref_fixup(TALLOC_CTX *ctx, tmpl_t *vpt, fr_dict_attr_t const *da, fr_dict_attr_t const *parent)
1704{
1705 tmpl_attr_t *ar;
1706
1707 if (tmpl_attr_tail_da(vpt) == da) return;
1708
1709 if (da->parent != parent) tmpl_attr_ref_fixup(ctx, vpt, da->parent, parent);
1710
1711 MEM(ar = talloc(ctx, tmpl_attr_t));
1712 *ar = (tmpl_attr_t) {
1713 .ar_num = NUM_UNSPEC,
1714 .ar_type = TMPL_ATTR_TYPE_NORMAL,
1715 .ar_da = da,
1716 .ar_parent = da->parent,
1717 };
1718
1719 tmpl_attr_insert(vpt, ar);
1720}
1721
1722/** Parse an attribute reference, either an OID or attribute name
1723 *
1724 * @note Do not call directly.
1725 *
1726 * @param[in] ctx to allocate new attribute reference in.
1727 * @param[out] err Parse error.
1728 * @param[in,out] vpt to append this reference to.
1729 * @param[in] parent Parent where the attribute will be placed (group, struct, tlv, etc).
1730 * @param[in] namespace Where the child attribute will be parsed from (dict root, struct member, TLV child, etc)
1731 * @param[in] name to parse.
1732 * @param[in] p_rules Formatting rules used to check for trailing garbage.
1733 * @param[in] at_rules which places constraints on attribute reference parsing.
1734 * Rules interpreted by this function is:
1735 * - allow_unknown - If false unknown OID components
1736 * result in a parse error.
1737 * - allow_unresolved - If false unknown attribute names
1738 * result in a parse error.
1739 * - allow_foreign - If an attribute resolves in a dictionary
1740 * that does not match the parent
1741 * (exception being FR_TYPE_GROUP) then that results
1742 * in a parse error.
1743 * @param[in] depth How deep we are. Used to check for maximum nesting level.
1744 * @return
1745 * - <0 on error.
1746 * - 0 on success.
1747 */
1748static inline int tmpl_attr_afrom_attr_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err,
1749 tmpl_t *vpt,
1750 fr_dict_attr_t const *parent, fr_dict_attr_t const *namespace,
1752 fr_sbuff_parse_rules_t const *p_rules, tmpl_attr_rules_t const *at_rules,
1753 unsigned int depth)
1754{
1755 uint32_t oid = 0;
1756 tmpl_attr_t *ar = NULL;
1757 fr_dict_attr_t const *da;
1759 fr_dict_attr_err_t dict_err;
1760 fr_dict_attr_t const *our_parent = parent;
1761
1762 fr_sbuff_marker(&m_s, name);
1763
1765 fr_strerror_const("Attribute nesting too deep");
1767 error:
1768 talloc_free(ar);
1769 fr_sbuff_marker_release(&m_s);
1771 }
1772
1773 /*
1774 * Input too short
1775 */
1776 if (!fr_sbuff_extend(name)) {
1777 fr_strerror_const("Missing attribute reference");
1779 goto error;
1780 }
1781
1782 /*
1783 * Maybe there's no child namespace (struct member, tlv child, etc). In which case we must
1784 * search from the default dictionary root.
1785 *
1786 * This search is probably wrong in some cases. See the comments below around FR_TYPE_GROUP.
1787 *
1788 * If we change out the dictionaries, we should arguably also change dict_def in the
1789 * tmpl_attr_rules_t. On top of that, the "dict_attr_search" functions take a #fr_dict_t
1790 * pointer, and not a pointer to the dict root. So we can't pass them a namespace.
1791 */
1792 if (!namespace) {
1793 fr_assert(parent == NULL);
1794
1796 at_rules->dict_def,
1797 name, p_rules ? p_rules->terminals : NULL,
1798 true,
1799 at_rules->allow_foreign);
1800 /*
1801 * The attribute was found either in the dict_def root, OR in the internal root, OR if
1802 * !dict_def && allow_foreign, in some other dictionary root.
1803 *
1804 * Otherwise we're still not sure what the attribute is. It may end up being an
1805 * unresolved one.
1806 */
1807 if (da) {
1808 our_parent = da->parent;
1809
1810 if (!our_parent->flags.is_root) {
1811 tmpl_attr_ref_fixup(ctx, vpt, our_parent, fr_dict_root(da->dict));
1812 }
1813 }
1814 } else {
1815 fr_assert(parent != NULL);
1816
1817 /*
1818 * Otherwise we're resolving the next piece in the context of where-ever we ended up from
1819 * parsing the last bit.
1820 *
1821 * The "parent" could be the same as "namespace", if both are at a dictionary root, OR
1822 * both are from a struct / tlv attribute.
1823
1824 * Or, "parent" could be a grouping attribute (e.g. request), and "namespace" could be
1825 * the dictionary root.
1826 */
1827 (void)fr_dict_attr_by_name_substr(&dict_err,
1828 &da,
1829 namespace,
1830 name,
1831 p_rules ? p_rules->terminals : NULL);
1832
1833 /*
1834 * Allow fallback to internal attributes
1835 * if the parent was a group, and we're
1836 * allowing internal resolution.
1837 *
1838 * Discard any errors here... It's more
1839 * useful to have the original.
1840 */
1841 if (!da) {
1842 ar = tmpl_attr_list_tail(&vpt->data.attribute.ar);
1843 if (!ar || ((ar->type == TMPL_ATTR_TYPE_NORMAL) && (ar->ar_da->type == FR_TYPE_GROUP))) {
1844 fr_dict_attr_t const *internal_root = fr_dict_root(fr_dict_internal());
1845
1846 (void)fr_dict_attr_by_name_substr(NULL,
1847 &da, internal_root,
1848 name,
1849 p_rules ? p_rules->terminals : NULL);
1850 if (da) {
1851 dict_err = FR_DICT_ATTR_OK;
1852 our_parent = internal_root;
1853 }
1854 }
1855 ar = NULL;
1856
1857 } else {
1858 /*
1859 * If we searched in a local dictionary, but found a real attribute
1860 * switch the namespace.
1861 */
1862 if (!da->flags.local && namespace->flags.local) namespace = our_parent = fr_dict_root(da->dict);
1863
1864 /*
1865 * We had an alias in the same namespace,
1866 * go add more things in.
1867 */
1868 if (da->parent != our_parent) {
1869 fr_assert(namespace == our_parent);
1870 tmpl_attr_ref_fixup(ctx, vpt, da->parent, our_parent);
1871 }
1872 }
1873 }
1874
1875 /*
1876 * Fatal errors related to nesting...
1877 */
1878 switch (dict_err) {
1880 fr_assert(our_parent != NULL);
1881 if (our_parent->flags.is_unknown) break;
1882 goto error;
1883
1885 goto error;
1886
1887 default:
1888 if (!da) break;
1889
1890 /*
1891 * The named component was a known attribute
1892 * so record it as a normal attribute
1893 * reference.
1894 */
1895 fr_assert(our_parent != NULL);
1896 goto alloc_ar;
1897 }
1898
1899 /*
1900 * At this point we haven't found a known attribute. What remains MUST be an OID component, OR an
1901 * unresolved attribute.
1902 *
1903 * The default is to parse the OIDs in the current namespace. If there is none, then we parse
1904 * the OIDs and unresolved attributes in the dict_def. And if that doesn't exist, in the
1905 * internal dictionaries.
1906 *
1907 * Note that we do NOT allow unknown attributes in the internal dictionary. Those attributes are
1908 * generally just DEFINEs, and their numbers have no meaning.
1909 */
1910 if (!namespace) {
1911 if (at_rules->dict_def) {
1912 our_parent = namespace = fr_dict_root(at_rules->dict_def);
1913 } else {
1914 our_parent = namespace = fr_dict_root(fr_dict_internal());
1915 }
1916 }
1917
1918 fr_assert(our_parent != NULL);
1919 fr_assert(namespace != NULL);
1920
1921 /*
1922 * See if the ref begins with an unsigned integer
1923 * if it does it's probably an OID component
1924 *
1925 * .<oid>
1926 */
1927 if (fr_sbuff_out(NULL, &oid, name) > 0) {
1928 namespace = fr_dict_unlocal(namespace);
1929
1930 fr_assert(ar == NULL);
1931
1932 fr_strerror_clear(); /* Clear out any existing errors */
1933
1934 if (fr_dict_by_da(namespace) == fr_dict_internal()) goto disallow_unknown;
1935
1936 /*
1937 * The OID component was a known attribute
1938 * so record it as a normal attribute
1939 * reference.
1940 */
1941 da = fr_dict_attr_child_by_num(namespace, oid);
1942 if (da) goto alloc_ar;
1943
1944 if (!at_rules->allow_unknown) {
1945 disallow_unknown:
1946 fr_strerror_const("Unknown attributes not allowed here");
1948 fr_sbuff_set(name, &m_s);
1949 goto error;
1950 }
1951
1952 /*
1953 * If it's numeric and not a known attribute
1954 * then we create an unknown attribute with
1955 * the specified attribute number.
1956 */
1957 MEM(ar = talloc(ctx, tmpl_attr_t));
1958
1959 /*
1960 * VSAs have VENDORs as children. All others are just normal things.
1961 */
1962 switch (namespace->type) {
1963 case FR_TYPE_VSA:
1964 da = fr_dict_attr_unknown_vendor_afrom_num(ar, namespace, oid);
1965 break;
1966
1967 default:
1968 da = fr_dict_attr_unknown_raw_afrom_num(ar, namespace, oid);
1969 break;
1970 }
1971
1972 if (!da) {
1973 if (err) *err = TMPL_ATTR_ERROR_UNKNOWN_NOT_ALLOWED; /* strerror set by dict function */
1974 goto error;
1975 }
1976
1977 *ar = (tmpl_attr_t){
1978 .ar_num = NUM_UNSPEC,
1979 .ar_type = TMPL_ATTR_TYPE_UNKNOWN,
1980 .ar_unknown = UNCONST(fr_dict_attr_t *, da),
1981 .ar_da = da,
1982 .ar_parent = our_parent,
1983 };
1984 goto do_suffix;
1985 }
1986
1987 /*
1988 * Can't parse it as an attribute, might be a literal string
1989 * let the caller decide.
1990 *
1991 * Don't alter the fr_strerror buffer, may contain useful
1992 * errors from the dictionary code.
1993 */
1994 if (!at_rules->allow_unresolved && !(at_rules->allow_wildcard && fr_sbuff_is_char(name, '['))) {
1995 fr_strerror_const_push("Unresolved attributes are not allowed here");
1997 fr_sbuff_set(name, &m_s);
1998 goto error;
1999 }
2000
2001 fr_sbuff_marker_release(&m_s);
2002
2003 /*
2004 * Once we hit one unresolved attribute we have to treat
2005 * the rest of the components are unresolved as well.
2006 */
2007 return tmpl_attr_ref_afrom_unresolved_substr(ctx, err, vpt, our_parent, namespace, name, at_rules);
2008
2009alloc_ar:
2010 /*
2011 * We have a da, remove any of the errors recorded from failed
2012 * searches to find the attribute to avoid misleading messages
2013 * if something else fails.
2014 */
2016
2017 MEM(ar = talloc(ctx, tmpl_attr_t));
2018 *ar = (tmpl_attr_t) {
2019 .ar_num = NUM_UNSPEC,
2020 .ar_type = TMPL_ATTR_TYPE_NORMAL,
2021 .ar_da = da,
2022 .ar_parent = our_parent,
2023 };
2024
2025do_suffix:
2026 /*
2027 * Parse the attribute reference filter
2028 *
2029 * Error out immediately if the filter is bad
2030 * otherwise determine whether to keep the
2031 * attribute reference or omit it based on:
2032 *
2033 * - Whether there was a filter present.
2034 * - The type of attribute.
2035 * - If this is the leaf attribute reference.
2036 */
2037 if (tmpl_attr_parse_filter(err, ar, name, at_rules) < 0) goto error;
2038
2039 /*
2040 * Local variables are always unitary.
2041 *
2042 * [0] is allowed, as is [n], [*], and [#]. But [1], etc. aren't allowed.
2043 */
2044 if (da->flags.local && (ar->ar_num > 0)) {
2045 fr_strerror_printf("Invalid array reference for local variable");
2047 fr_sbuff_set(name, &m_s);
2048 goto error;
2049 }
2050
2051 /*
2052 * At the end of the attribute reference. If there's a
2053 * trailing '.' then there's another attribute reference
2054 * we need to parse, otherwise we're done.
2055 */
2056 fr_sbuff_marker_release(&m_s);
2057 fr_sbuff_marker(&m_s, name);
2058
2059 if (fr_sbuff_next_if_char(name, '.')) {
2060 fr_dict_attr_t const *ref;
2061
2062 switch (da->type) {
2063 case FR_TYPE_GROUP:
2064 ref = fr_dict_attr_ref(da);
2065
2066 /*
2067 * If the ref is outside of the internal namespace, then we use it.
2068 *
2069 * If the ref is inside of the internal namespace (e.g. "request"), then we do
2070 * something else.
2071 *
2072 * If we were given a root dictionary on input, use that. We have to follow this
2073 * dictionary because this function calls itself recursively, WITHOUT updating
2074 * "dict_def" in the attr_rules. So the dict-def there is whatever got passed
2075 * into tmpl_afrom_attr_substr(), BEFORE the "parent.parent.parent..." parsing.
2076 * Which means that in many cases, the "dict_def" is completely irrelevant.
2077 *
2078 * If there is no parent on input, then we just use dict_def.
2079 *
2080 * Otherwise we search through all of the dictionaries.
2081 *
2082 * Note that we cannot put random protocol attributes into an internal attribute
2083 * of type "group".
2084 */
2085 if (ref != fr_dict_root(fr_dict_internal())) {
2086 our_parent = namespace = ref;
2087
2088 } else if (parent && parent->flags.is_root) {
2089 our_parent = namespace = parent;
2090
2091 } else if (at_rules->dict_def) {
2092 our_parent = namespace = fr_dict_root(at_rules->dict_def);
2093
2094 } else {
2095 our_parent = namespace = NULL;
2096 }
2097 break;
2098
2100 is_union:
2101 /*
2102 * Structural types are parented and namespaced from their parent da.
2103 */
2104 namespace = our_parent = da;
2105 break;
2106
2107 default:
2108 /*
2109 * Key fields can have children, because we really don't know how else to
2110 * represent the child structures.
2111 *
2112 * @todo - remove after migration_union_key is deleted
2113 */
2114 if (fr_dict_attr_is_key_field(da)) goto is_union;
2115
2116 fr_strerror_printf("Parent type of nested attribute %s must be of type "
2117 "\"struct\", \"tlv\", \"vendor\", \"vsa\" or \"group\", got \"%s\"",
2118 da->name,
2119 fr_type_to_str(da->type));
2120 fr_sbuff_set(name, &m_s);
2121 goto error;
2122 }
2123
2124 if (ar) tmpl_attr_insert(vpt, ar);
2125
2126 if (tmpl_attr_afrom_attr_substr(ctx, err, vpt, our_parent, namespace, name, p_rules, at_rules, depth + 1) < 0) {
2127 if (ar) {
2128 tmpl_attr_list_talloc_free_tail(&vpt->data.attribute.ar); /* Remove and free ar */
2129 ar = NULL;
2130 }
2131 goto error;
2132 }
2133
2134 /*
2135 * If it's a leaf we always insert the attribute
2136 * reference into the list, even if it's a
2137 * nesting attribute.
2138 *
2139 * This is useful for nested edit sections
2140 * where the tmpl might be the name of a new
2141 * subsection.
2142 */
2143 } else {
2144 tmpl_attr_insert(vpt, ar);
2145 }
2146
2147 /*
2148 * Remove unnecessary casts.
2149 */
2152
2154
2155 fr_sbuff_marker_release(&m_s);
2156 return 0;
2157}
2158
2159/** Parse a string into a TMPL_TYPE_ATTR_* type #tmpl_t
2160 *
2161 * @param[in,out] ctx to allocate #tmpl_t in.
2162 * @param[out] err May be NULL. Provides the exact error that the parser hit
2163 * when processing the attribute ref.
2164 * @param[out] out Where to write pointer to new #tmpl_t.
2165 * @param[in] name of attribute including #tmpl_request_ref_t and #fr_pair_list_t qualifiers.
2166 * @param[in] p_rules Formatting rules used to check for trailing garbage.
2167 * @param[in] t_rules Rules which control parsing:
2168 * - dict_def The default dictionary to use if attributes
2169 * are unqualified.
2170 * - request_def The default #request_t to set if no
2171 * #tmpl_request_ref_t qualifiers are found in name.
2172 * - list_def The default list to set if no #fr_pair_list_t
2173 * qualifiers are found in the name.
2174 * - allow_unknown If true attributes in the format accepted by
2175 * #fr_dict_attr_unknown_afrom_oid_substr will be allowed,
2176 * even if they're not in the main dictionaries.
2177 * If an unknown attribute is found a #TMPL_TYPE_ATTR
2178 * #tmpl_t will be produced.
2179 * If #tmpl_afrom_attr_substr is being called on
2180 * startup, the #tmpl_t may be passed to
2181 * #tmpl_attr_unknown_add to
2182 * add the unknown attribute to the main dictionary.
2183 * If the unknown attribute is not added to
2184 * the main dictionary the #tmpl_t cannot be used
2185 * to search for a #fr_pair_t in a #request_t.
2186 * - allow_unresolved If true, we don't generate a parse error on
2187 * unknown attributes. If an unknown attribute is
2188 * found a #TMPL_TYPE_ATTR_UNRESOLVED
2189 * #tmpl_t will be produced.
2190 * - allow_foreign If true, allow attribute names to be qualified
2191 * with a protocol outside of the passed dict_def.
2192 * - disallow_filters
2193 *
2194 * @see REMARKER to produce pretty error markers from the return value.
2195 *
2196 * @return
2197 * - <= 0 on error (offset as negative integer)
2198 * - > 0 on success (number of bytes parsed).
2199 */
2202 fr_sbuff_parse_rules_t const *p_rules,
2203 tmpl_rules_t const *t_rules)
2204{
2205 int ret;
2206 tmpl_t *vpt;
2207 fr_sbuff_t our_name = FR_SBUFF(name); /* Take a local copy in case we need to back track */
2208 bool is_raw = false;
2209 tmpl_attr_rules_t const *at_rules;
2211 fr_dict_attr_t const *namespace;
2213
2215
2216 at_rules = &t_rules->attr;
2217
2219
2220 if (!fr_sbuff_extend(&our_name)) {
2221 fr_strerror_const("Empty attribute reference");
2223 FR_SBUFF_ERROR_RETURN(&our_name);
2224 }
2225
2226 /*
2227 * '&' prefix is ignored.
2228 */
2229 (void) fr_sbuff_next_if_char(&our_name, '&');
2230
2231 /*
2232 * We parsed the tmpl as User-Name, but NOT %{User-Name}.
2233 */
2234 MEM(vpt = tmpl_alloc(ctx, TMPL_TYPE_ATTR, T_BARE_WORD, NULL, 0));
2235
2236 /*
2237 * The "raw." prefix marks up the leaf attribute
2238 * as unknown if it wasn't already which allows
2239 * users to stick whatever they want in there as
2240 * a value.
2241 */
2242 if (fr_sbuff_adv_past_strcase_literal(&our_name, "raw.")) is_raw = true;
2243
2244 /*
2245 * Parse one or more request references
2246 */
2248 &vpt->data.attribute.rr,
2249 &our_name,
2250 p_rules,
2251 t_rules,
2252 &namespace);
2253 if (ret < 0) {
2254 error:
2255 *out = NULL;
2257 FR_SBUFF_ERROR_RETURN(&our_name);
2258 }
2259
2260 fr_sbuff_marker(&m_l, &our_name);
2261
2262 /*
2263 * Parse the list and / or attribute reference
2264 */
2266 vpt,
2267 namespace, namespace,
2268 &our_name, p_rules, at_rules, 0);
2269 if (ret < 0) goto error;
2270
2271 /*
2272 * Check to see if the user wants the leaf
2273 * attribute to be raw.
2274 *
2275 * We can only do the conversion now _if_
2276 * the complete hierarchy has been resolved
2277 * otherwise we'll need to do the conversion
2278 * later.
2279 */
2280 if (tmpl_is_attr(vpt) && is_raw) tmpl_attr_to_raw(vpt);
2281
2282 /*
2283 * Local variables cannot be given a list modifier.
2284 */
2286 tmpl_attr_t *ar = tmpl_attr_list_head(tmpl_attr(vpt));
2287 bool is_local = ar->ar_da->flags.local;
2288
2289 for (; ar != NULL;
2290 ar = tmpl_attr_list_next(tmpl_attr(vpt), ar)) {
2291 if (!ar->ar_da->flags.local ||
2292 (ar->ar_da->flags.local && is_local)) continue;
2293
2294 fr_strerror_printf("Local attributes cannot be used in any list");
2296 fr_sbuff_set(&our_name, &m_l);
2297 goto error;
2298 }
2299
2300 /*
2301 * That being said, local variables are named "foo", but are always put into the local list.
2302 */
2303 if (is_local && (at_rules->list_presence != TMPL_ATTR_LIST_FORBID)) {
2304 MEM(ar = talloc(vpt, tmpl_attr_t));
2305 *ar = (tmpl_attr_t){
2306 .ar_type = TMPL_ATTR_TYPE_NORMAL,
2307 .ar_da = request_attr_local,
2308 .ar_parent = fr_dict_root(fr_dict_internal())
2309 };
2310
2311 /*
2312 * Prepend the local list ref so it gets evaluated
2313 * first.
2314 */
2315 tmpl_attr_list_insert_head(tmpl_attr(vpt), ar);
2316 }
2317 }
2318
2319 /*
2320 * Check whether the tmpl has a list qualifier.
2321 */
2322 switch (at_rules->list_presence) {
2324 break;
2325
2327 if (tmpl_attr_is_list_attr(tmpl_attr_list_head(tmpl_attr(vpt)))) {
2328 fr_strerror_const("List qualifiers are not allowed here.");
2330 goto error;
2331 }
2332 break;
2333
2335 if (!tmpl_attr_is_list_attr(tmpl_attr_list_head(tmpl_attr(vpt)))) {
2336 fr_strerror_const("List qualifier is required, but no list was found.");
2338 goto error;
2339 }
2340 break;
2341 }
2342
2343 /*
2344 * If we're using lists, ensure that the default list is specified.
2345 */
2346 if (!tmpl_attr_is_list_attr(tmpl_attr_list_head(tmpl_attr(vpt)))) {
2347 tmpl_attr_t *ar;
2348
2349 MEM(ar = talloc(vpt, tmpl_attr_t));
2350 *ar = (tmpl_attr_t){
2351 .ar_type = TMPL_ATTR_TYPE_NORMAL,
2352 .ar_parent = fr_dict_root(fr_dict_internal())
2353 };
2354
2355 fr_assert(at_rules->list_def);
2356 ar->ar_da = at_rules->list_def;
2357
2358 /*
2359 * Prepend the list ref so it gets evaluated
2360 * first.
2361 */
2362 tmpl_attr_list_insert_head(tmpl_attr(vpt), ar);
2363 }
2364
2365 tmpl_set_name(vpt, T_BARE_WORD, fr_sbuff_start(&our_name), fr_sbuff_used(&our_name));
2366 vpt->rules = *t_rules; /* Record the rules */
2367
2368 /*
2369 * If there are actual requests, duplicate them
2370 * and move them into the list.
2371 *
2372 * A NULL request_def pointer is equivalent to the
2373 * current request.
2374 */
2375 if (t_rules->attr.request_def) {
2376 tmpl_request_ref_list_acopy(vpt, &vpt->rules.attr.request_def, t_rules->attr.request_def);
2377 }
2378
2379 if (tmpl_is_attr(vpt)) {
2380 tmpl_attr_t *ar;
2381
2382 /*
2383 * Suppress useless casts.
2384 */
2386 vpt->rules.cast = FR_TYPE_NULL;
2387 }
2388
2389 /*
2390 * Ensure that the list is set correctly, so that
2391 * the returned vpt just doesn't just match the
2392 * input rules, it is also internally consistent.
2393 */
2394 ar = tmpl_attr_list_head(tmpl_attr(vpt));
2395 fr_assert(ar != NULL);
2396
2397 if (tmpl_attr_is_list_attr(ar)) vpt->rules.attr.list_def = ar->ar_da;
2398 }
2399
2400 if (!tmpl_substr_terminal_check(&our_name, p_rules)) {
2401 fr_strerror_const("Unexpected text after attribute reference");
2403 goto error;
2404 }
2405
2406 /*
2407 * If everything was resolved correctly
2408 * we now need to check the cast type.
2409 */
2410 if (!tmpl_needs_resolving(vpt) && !fr_type_is_null(t_rules->cast) &&
2411 !fr_type_cast(t_rules->cast, tmpl_attr_tail_da(vpt)->type)) {
2412 fr_strerror_printf("Cannot cast type '%s' to '%s'",
2415 fr_sbuff_set_to_start(&our_name);
2416 goto error;
2417 }
2418
2419 TMPL_VERIFY(vpt); /* Because we want to ensure we produced something sane */
2420
2421 *out = vpt;
2422 FR_SBUFF_SET_RETURN(name, &our_name);
2423}
2424
2425/** Parse a string into a TMPL_TYPE_ATTR_* type #tmpl_t
2426 *
2427 * @param[in,out] ctx to allocate #tmpl_t in.
2428 * @param[out] err May be NULL. Provides the exact error that the parser hit
2429 * when processing the attribute ref.
2430 * @param[out] out Where to write pointer to new #tmpl_t.
2431 * @param[in] name of attribute including #tmpl_request_ref_t and #fr_pair_list_t qualifiers.
2432 * @param[in] t_rules Rules which control parsing. See tmpl_afrom_attr_substr() for details.
2433 *
2434 * @note Unlike #tmpl_afrom_attr_substr this function will error out if the entire
2435 * name string isn't parsed.
2436 */
2438 tmpl_t **out, char const *name, tmpl_rules_t const *t_rules)
2439{
2440 ssize_t slen, name_len;
2442
2444
2445 name_len = strlen(name);
2446 slen = tmpl_afrom_attr_substr(ctx, err, out, &FR_SBUFF_IN(name, name_len), NULL, t_rules);
2447 if (slen <= 0) return slen;
2448
2449 if (!fr_cond_assert(*out)) return -1;
2450
2451 if (slen != name_len) {
2452 /* This looks wrong, but it produces meaningful errors for unknown attrs */
2453 fr_strerror_printf("Unexpected text after %s",
2454 tmpl_type_to_str((*out)->type));
2455 return -slen;
2456 }
2457
2458 TMPL_VERIFY(*out);
2459
2460 return slen;
2461}
2462
2463/** Create TMPL_TYPE_DATA from a string
2464 *
2465 * @param[in] ctx to allocate tmpl to.
2466 * @param[out] out where to write tmpl.
2467 * @param[in] in sbuff to parse.
2468 * @param[in] quote surrounding the operand to parse.
2469 * @param[in] t_rules specifying the cast and any enumeration values.
2470 * @param[in] allow_enum Whether parsing the value as an enum should be allowed.
2471 * @param[in] p_rules formatting rules.
2472 * @return
2473 * - <0 on error
2474 * - >=0 on success.
2475 */
2477 fr_token_t quote,
2478 tmpl_rules_t const *t_rules, bool allow_enum,
2479 fr_sbuff_parse_rules_t const *p_rules)
2480{
2481 fr_sbuff_t our_in = FR_SBUFF(in);
2482 fr_value_box_t tmp;
2483 tmpl_t *vpt;
2485
2486 if (!fr_type_is_null(t_rules->cast)) cast = t_rules->cast;
2487
2488 if (!fr_type_is_leaf(cast)) {
2489 fr_strerror_printf("%s is not a valid cast type",
2490 fr_type_to_str(cast));
2491 FR_SBUFF_ERROR_RETURN(&our_in);
2492 }
2493
2494 vpt = tmpl_alloc_null(ctx);
2495 if (fr_value_box_from_substr(vpt, &tmp,
2496 cast, allow_enum ? t_rules->enumv : NULL,
2497 &our_in, p_rules) < 0) {
2499 FR_SBUFF_ERROR_RETURN(&our_in);
2500 }
2502
2503 tmpl_init(vpt, TMPL_TYPE_DATA, quote, fr_sbuff_start(&our_in), fr_sbuff_used(&our_in), t_rules);
2504
2506
2507 *out = vpt;
2508
2509 if (cast == tmpl_value_type(vpt)) vpt->rules.cast = FR_TYPE_NULL;
2510
2512
2513 FR_SBUFF_SET_RETURN(in, &our_in);
2514}
2515
2516/** Parse a truth value
2517 *
2518 * @param[in] ctx to allocate tmpl to.
2519 * @param[out] out where to write tmpl.
2520 * @param[in] in sbuff to parse.
2521 * @param[in] p_rules formatting rules.
2522 * @return
2523 * - < 0 sbuff does not contain a boolean value.
2524 * - > 0 how many bytes were parsed.
2525 */
2527 fr_sbuff_parse_rules_t const *p_rules)
2528{
2529 fr_sbuff_t our_in = FR_SBUFF(in);
2530 bool a_bool;
2531 tmpl_t *vpt;
2532
2533 if (fr_sbuff_out(NULL, &a_bool, &our_in) < 0) {
2534 fr_strerror_const("Not a boolean value");
2535 return 0;
2536 }
2537
2538 if (!tmpl_substr_terminal_check(&our_in, p_rules)) {
2539 fr_strerror_const("Unexpected text after bool");
2541 }
2542
2544
2545 fr_value_box_init(&vpt->data.literal, FR_TYPE_BOOL, NULL, false);
2546 vpt->data.literal.vb_bool = a_bool;
2547
2548 *out = vpt;
2549
2550 FR_SBUFF_SET_RETURN(in, &our_in);
2551}
2552
2553/** Parse bareword as an octet string
2554 *
2555 * @param[in] ctx to allocate tmpl to.
2556 * @param[out] out where to write tmpl.
2557 * @param[in] in sbuff to parse.
2558 * @param[in] p_rules formatting rules.
2559 * @return
2560 * - < 0 negative offset where parse error occurred.
2561 * - 0 sbuff does not contain a hex string.
2562 * - > 0 how many bytes were parsed.
2563 */
2565 fr_sbuff_parse_rules_t const *p_rules)
2566{
2567 fr_sbuff_t our_in = FR_SBUFF(in);
2568 tmpl_t *vpt;
2569 char *hex;
2570 size_t binlen, len;
2571 uint8_t *bin;
2572
2573 if (!fr_sbuff_adv_past_strcase_literal(&our_in, "0x")) return 0;
2574
2575 MEM(vpt = tmpl_alloc(ctx, TMPL_TYPE_DATA, T_BARE_WORD, NULL, 0));
2576
2577 /*
2578 * This allows stream parsing to work correctly
2579 * we could be less lazy and copy hex data in
2580 * chunks, but never mind...
2581 */
2582 len = fr_sbuff_out_abstrncpy_allowed(vpt, &hex, &our_in, SIZE_MAX, sbuff_char_class_hex);
2583 if (len & 0x01) {
2584 fr_strerror_const("Hex string not even length");
2585 error:
2587 FR_SBUFF_ERROR_RETURN(&our_in);
2588 }
2589 if (len == 0) {
2590 fr_strerror_const("Zero length hex string is invalid");
2591 goto error;
2592 }
2593
2594 if (!tmpl_substr_terminal_check(&our_in, p_rules)) {
2595 fr_strerror_const("Unexpected text after hex string");
2596 goto error;
2597 }
2598
2599 bin = (uint8_t *)hex;
2600 binlen = len / 2;
2601
2603
2604 (void)fr_base16_decode(NULL, &FR_DBUFF_TMP(bin, binlen), &FR_SBUFF_IN(hex, len), false);
2605 MEM(bin = talloc_realloc_size(vpt, bin, binlen)); /* Realloc to the correct length */
2606 (void)fr_value_box_memdup_shallow(&vpt->data.literal, NULL, bin, binlen, false);
2607
2608 *out = vpt;
2609
2610 FR_SBUFF_SET_RETURN(in, &our_in);
2611}
2612
2613/** Parse bareword as an IPv4 address or prefix
2614 *
2615 * @param[in] ctx to allocate tmpl to.
2616 * @param[out] out where to write tmpl.
2617 * @param[in] in sbuff to parse.
2618 * @param[in] p_rules formatting rules.
2619 * @return
2620 * - < 0 sbuff does not contain an IPv4 address or prefix.
2621 * - > 0 how many bytes were parsed.
2622 */
2624 fr_sbuff_parse_rules_t const *p_rules)
2625{
2626 tmpl_t *vpt;
2627 fr_sbuff_t our_in = FR_SBUFF(in);
2629 int count;
2630 uint32_t ipaddr;
2631 uint8_t addr[4] = {}, prefix = 32;
2632
2633 for (count = 0; count < 4; count++) {
2634 if (!fr_sbuff_out(NULL, &addr[count], &our_in)) FR_SBUFF_ERROR_RETURN(&our_in);
2635
2636 if (count == 3) break;
2637
2638 if (fr_sbuff_next_if_char(&our_in, '.')) continue;
2639
2640 if (!fr_sbuff_is_char(&our_in, '/')) FR_SBUFF_ERROR_RETURN(&our_in);
2641 }
2642
2643 /*
2644 * If it has a trailing '/' then it's an IP prefix.
2645 */
2646 if (fr_sbuff_next_if_char(&our_in, '/')) {
2647 if (fr_sbuff_out(NULL, &prefix, &our_in) < 0) {
2648 fr_strerror_const("IPv4 CIDR mask malformed");
2649 FR_SBUFF_ERROR_RETURN(&our_in);
2650 }
2651
2652 if (prefix > 32) {
2653 fr_strerror_const("IPv4 CIDR mask too high");
2654 FR_SBUFF_ERROR_RETURN(&our_in);
2655 }
2656
2658 } else {
2660 }
2661
2662 if (!tmpl_substr_terminal_check(&our_in, p_rules)) {
2663 fr_strerror_const("Unexpected text after IPv4 string or prefix");
2664 FR_SBUFF_ERROR_RETURN(&our_in);
2665 }
2666
2668 fr_value_box_init(&vpt->data.literal, type, NULL, false);
2669 vpt->data.literal.vb_ip.af = AF_INET;
2670 vpt->data.literal.vb_ip.prefix = prefix;
2671
2672 /*
2673 * Zero out lower bits
2674 */
2675 ipaddr = (((uint32_t) addr[0]) << 24) | (((uint32_t) addr[1]) << 16) | (((uint32_t) addr[2]) << 8) | addr[3];
2676 if (prefix < 32) {
2677 ipaddr &= ~((uint32_t) 0) << (32 - prefix);
2678 }
2679 vpt->data.literal.vb_ip.addr.v4.s_addr = htonl(ipaddr);
2680
2681 *out = vpt;
2682
2683 FR_SBUFF_SET_RETURN(in, &our_in);
2684}
2685
2686/** Parse bareword as an IPv6 address or prefix
2687 *
2688 * @param[in] ctx to allocate tmpl to.
2689 * @param[out] out where to write tmpl.
2690 * @param[in] in sbuff to parse.
2691 * @param[in] p_rules formatting rules.
2692 * @return
2693 * - < 0 sbuff does not contain an IPv4 address or prefix.
2694 * - > 0 how many bytes were parsed.
2695 */
2697 fr_sbuff_parse_rules_t const *p_rules)
2698{
2699 tmpl_t *vpt;
2700 fr_sbuff_t our_in = FR_SBUFF(in);
2703 size_t len;
2704 char *sep_a, *sep_b;
2705
2706 static bool ipv6_chars[UINT8_MAX + 1] = {
2707 ['0'] = true, ['1'] = true, ['2'] = true, ['3'] = true, ['4'] = true,
2708 ['5'] = true, ['6'] = true, ['7'] = true, ['8'] = true, ['9'] = true,
2709 ['a'] = true, ['b'] = true, ['c'] = true, ['d'] = true, ['e'] = true,
2710 ['f'] = true,
2711 ['A'] = true, ['B'] = true, ['C'] = true, ['D'] = true, ['E'] = true,
2712 ['F'] = true,
2713 [':'] = true, ['.'] = true
2714 };
2715
2716 /*
2717 * Drop a marker to pin the start of the
2718 * address in the buffer.
2719 */
2720 fr_sbuff_marker(&m, &our_in);
2721
2722 /*
2723 * Check for something looking like an IPv6 address
2724 *
2725 * Minimum string is '::'
2726 */
2727 len = fr_sbuff_adv_past_allowed(&our_in, FR_IPADDR_STRLEN + 1, ipv6_chars, NULL);
2728 if ((len < 2) || (len > FR_IPADDR_STRLEN)) {
2729 error:
2730 FR_SBUFF_ERROR_RETURN(&our_in);
2731 }
2732
2733 /*
2734 * Got ':' after '.', this isn't allowed.
2735 *
2736 * We need this check else IPv4 gets parsed
2737 * as blank IPv6 address.
2738 */
2739 sep_a = memchr(fr_sbuff_current(&m), '.', len);
2740 if (sep_a && (!(sep_b = memchr(fr_sbuff_current(&m), ':', len)) || (sep_b > sep_a))) {
2741 fr_strerror_const("First IPv6 component separator was a '.'");
2742 goto error;
2743 }
2744
2745 /*
2746 * The v6 parse function will happily turn
2747 * integers into v6 addresses *sigh*.
2748 */
2749 sep_a = memchr(fr_sbuff_current(&m), ':', len);
2750 if (!sep_a) {
2751 fr_strerror_const("No IPv6 component separator");
2752 goto error;
2753 }
2754
2755 /*
2756 * Handle scope
2757 */
2758 if (fr_sbuff_next_if_char(&our_in, '%')) {
2759 len = fr_sbuff_adv_until(&our_in, IFNAMSIZ + 1, p_rules->terminals, '\0');
2760 if ((len < 1) || (len > IFNAMSIZ)) {
2761 fr_strerror_const("IPv6 scope too long");
2762 goto error;
2763 }
2764 }
2765
2766 /*
2767 * ...and finally the prefix.
2768 */
2769 if (fr_sbuff_next_if_char(&our_in, '/')) {
2770 uint8_t mask;
2771
2772 if (fr_sbuff_out(NULL, &mask, &our_in) < 0) {
2773 fr_strerror_const("IPv6 CIDR mask malformed");
2774 goto error;
2775 }
2776 if (mask > 128) {
2777 fr_strerror_const("IPv6 CIDR mask too high");
2778 goto error;
2779 }
2780
2782 } else {
2784 }
2785
2786 if (!tmpl_substr_terminal_check(&our_in, p_rules)) {
2787 fr_strerror_const("Unexpected text after IPv6 string or prefix");
2788 goto error;
2789 }
2790
2792 if (fr_value_box_from_substr(vpt, &vpt->data.literal, type, NULL,
2793 &FR_SBUFF_REPARSE(&our_in),
2794 NULL) < 0) {
2796 goto error;
2797 }
2798 *out = vpt;
2799
2800 FR_SBUFF_SET_RETURN(in, &our_in);
2801}
2802
2803
2804/** Try and parse signed or unsigned integers
2805 *
2806 * @param[in] ctx to allocate tmpl to.
2807 * @param[out] out where to write tmpl.
2808 * @param[in] in sbuff to parse.
2809 * @param[in] p_rules formatting rules.
2810 * @return
2811 * - < 0 sbuff does not contain a mac address.
2812 * - > 0 how many bytes were parsed.
2813 */
2815 fr_sbuff_parse_rules_t const *p_rules)
2816{
2817 tmpl_t *vpt;
2818 fr_sbuff_t our_in = FR_SBUFF(in);
2819 uint8_t buff[6] = {};
2820 fr_dbuff_t dbuff;
2821 fr_value_box_t *vb;
2823
2824 fr_dbuff_init(&dbuff, buff, sizeof(buff));
2825
2826 fr_base16_decode(&err, &dbuff, &our_in, true);
2827 if (err != FR_SBUFF_PARSE_OK) return 0;
2828
2829 if (!fr_sbuff_next_if_char(&our_in, ':')) return 0;
2830
2831 fr_base16_decode(&err, &dbuff, &our_in, true);
2832 if (err != FR_SBUFF_PARSE_OK) return 0;
2833
2834 if (!fr_sbuff_next_if_char(&our_in, ':')) return 0;
2835
2836 fr_base16_decode(&err, &dbuff, &our_in, true);
2837 if (err != FR_SBUFF_PARSE_OK) return 0;
2838
2839 if (!fr_sbuff_next_if_char(&our_in, ':')) return 0;
2840
2841 fr_base16_decode(&err, &dbuff, &our_in, true);
2842 if (err != FR_SBUFF_PARSE_OK) return 0;
2843
2844 if (!fr_sbuff_next_if_char(&our_in, ':')) return 0;
2845
2846 fr_base16_decode(&err, &dbuff, &our_in, true);
2847 if (err != FR_SBUFF_PARSE_OK) return 0;
2848
2849 if (!fr_sbuff_next_if_char(&our_in, ':')) return 0;
2850
2851 fr_base16_decode(&err, &dbuff, &our_in, true);
2852 if (err != FR_SBUFF_PARSE_OK) return 0;
2853
2854 if (!tmpl_substr_terminal_check(&our_in, p_rules)) {
2855 fr_strerror_const("Unexpected text after mac address");
2856 return 0;
2857 }
2858
2860 T_BARE_WORD, fr_sbuff_start(&our_in), fr_sbuff_used(&our_in)));
2861 vb = tmpl_value(vpt);
2862
2863 fr_value_box_init(vb, FR_TYPE_ETHERNET, NULL, false);
2864 memcpy(vb->vb_ether, buff, sizeof(vb->vb_ether));
2865
2866 *out = vpt;
2867
2868 FR_SBUFF_SET_RETURN(in, &our_in);
2869}
2870
2871/** Try and parse signed or unsigned integers
2872 *
2873 * @param[in] ctx to allocate tmpl to.
2874 * @param[out] out where to write tmpl.
2875 * @param[in] in sbuff to parse.
2876 * @param[in] p_rules formatting rules.
2877 * @return
2878 * - < 0 sbuff does not contain an integer.
2879 * - > 0 how many bytes were parsed.
2880 */
2882 fr_sbuff_parse_rules_t const *p_rules)
2883{
2884 tmpl_t *vpt;
2885 fr_sbuff_t our_in = FR_SBUFF(in);
2886 ssize_t slen;
2887 fr_value_box_t *vb;
2888
2889 /*
2890 * Pick the narrowest signed type
2891 */
2892 if (fr_sbuff_is_char(&our_in, '-')) {
2893 int64_t a_int;
2894
2895 slen = fr_sbuff_out(NULL, &a_int, &our_in);
2896 if (slen <= 0) return 0;
2897
2898 if (!tmpl_substr_terminal_check(&our_in, p_rules)) {
2899 fr_strerror_const("Unexpected text after signed integer");
2900 error:
2901 FR_SBUFF_ERROR_RETURN(&our_in);
2902 }
2903
2905 T_BARE_WORD, fr_sbuff_start(&our_in), fr_sbuff_used(&our_in)));
2906 vb = tmpl_value(vpt);
2907 if (a_int >= INT8_MIN) {
2908 fr_value_box_init(vb, FR_TYPE_INT8, NULL, false);
2909 vb->vb_int8 = (int8_t)a_int;
2910 } else if (a_int >= INT16_MIN) {
2911 fr_value_box_init(vb, FR_TYPE_INT16, NULL, false);
2912 vb->vb_int16 = (int16_t)a_int;
2913 } else if (a_int >= INT32_MIN) {
2914 fr_value_box_init(vb, FR_TYPE_INT32, NULL, false);
2915 vb->vb_int32 = (int32_t)a_int;
2916 } else {
2917 fr_value_box_init(vb, FR_TYPE_INT64, NULL, false);
2918 vb->vb_int64 = (int64_t)a_int;
2919 }
2920 /*
2921 * Pick the narrowest unsigned type
2922 */
2923 } else {
2924 uint64_t a_uint;
2925
2926 slen = fr_sbuff_out(NULL, &a_uint, &our_in);
2927 if (slen <= 0) return slen;
2928
2929 if (!tmpl_substr_terminal_check(&our_in, p_rules)) {
2930 fr_strerror_const("Unexpected text after unsigned integer");
2931 goto error;
2932 }
2933
2935 T_BARE_WORD, fr_sbuff_start(&our_in), fr_sbuff_used(&our_in)));
2936 vb = tmpl_value(vpt);
2937 if (a_uint <= UINT8_MAX) {
2938 fr_value_box_init(vb, FR_TYPE_UINT8, NULL, false);
2939 vb->vb_uint8 = (uint8_t)a_uint;
2940 } else if (a_uint <= UINT16_MAX) {
2941 fr_value_box_init(vb, FR_TYPE_UINT16, NULL, false);
2942 vb->vb_uint16 = (uint16_t)a_uint;
2943 } else if (a_uint <= UINT32_MAX) {
2944 fr_value_box_init(vb, FR_TYPE_UINT32, NULL, false);
2945 vb->vb_uint32 = (uint32_t)a_uint;
2946 } else {
2947 fr_value_box_init(vb, FR_TYPE_UINT64, NULL, false);
2948 vb->vb_uint64 = (uint64_t)a_uint;
2949 }
2950 }
2951
2952 *out = vpt;
2953
2954 FR_SBUFF_SET_RETURN(in, &our_in);
2955}
2956
2958 fr_sbuff_parse_rules_t const *p_rules)
2959{
2960 tmpl_t *vpt;
2961 fr_sbuff_t our_in = FR_SBUFF(in);
2962 double a_float;
2963 ssize_t slen;
2964 fr_value_box_t *vb;
2965
2966 slen = fr_sbuff_out(NULL, &a_float, &our_in);
2967 if (slen <= 0) return 0;
2968
2969 if (!tmpl_substr_terminal_check(&our_in, p_rules)) {
2970 fr_strerror_const("Unexpected text after float");
2971 FR_SBUFF_ERROR_RETURN(&our_in);
2972 }
2973
2975 T_BARE_WORD, fr_sbuff_start(&our_in), fr_sbuff_used(&our_in)));
2976 vb = tmpl_value(vpt);
2977 fr_value_box_init(vb, FR_TYPE_FLOAT64, NULL, false);
2978 vb->vb_float64 = a_float;
2979
2980 *out = vpt;
2981
2982 FR_SBUFF_SET_RETURN(in, &our_in);
2983}
2984
2986 fr_sbuff_parse_rules_t const *p_rules)
2987{
2988 tmpl_t *vpt;
2989 fr_sbuff_t our_in = FR_SBUFF(in);
2990 fr_time_delta_t a_delta;
2991 fr_slen_t slen;
2992 fr_value_box_t *vb;
2993
2994 slen = fr_time_delta_from_substr(&a_delta, &our_in, FR_TIME_RES_SEC, true, p_rules ? p_rules->terminals : NULL);
2995 if (slen <= 0) return 0;
2996
2998 T_BARE_WORD, fr_sbuff_start(&our_in), fr_sbuff_used(&our_in)));
2999 vb = tmpl_value(vpt);
3000 fr_value_box_init(vb, FR_TYPE_TIME_DELTA, NULL, false);
3001 vb->vb_time_delta = a_delta;
3002
3003 *out = vpt;
3004
3005 FR_SBUFF_SET_RETURN(in, &our_in);
3006}
3007
3008/*
3009 * ::value
3010 *
3011 * Treated as enum name. Note that this check MUST be done after the test for IPv6, as
3012 * "::1" is an allowed IPv6 address.
3013 *
3014 * @todo - Mark this up as an enum name? Or do we really care? Maybe we want to allow
3015 *
3016 * Service-Type == 'Framed-User'
3017 *
3018 * or
3019 *
3020 * Service-Type == "Framed-User'
3021 *
3022 * as the second one allows for xlat expansions of enum names.
3023 *
3024 * We probably do want to forbid the single-quoted form of enums,
3025 * as that doesn't seem to make sense.
3026 *
3027 * We also need to distinguish unresolved bare words as enums
3028 * (with :: prefix) from unresolved attributes without an & prefix.
3029 */
3030static ssize_t tmpl_afrom_enum(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in,
3031 fr_sbuff_parse_rules_t const *p_rules,
3032 tmpl_rules_t const *t_rules)
3033{
3034 tmpl_t *vpt;
3036 fr_sbuff_t our_in = FR_SBUFF(in);
3037 fr_sbuff_t *enum_buff;
3038
3039 FR_SBUFF_TALLOC_THREAD_LOCAL(&enum_buff, 1024, SIZE_MAX);
3040
3041 /*
3042 * If there isn't a "::" prefix, then check for migration flags, and enum.
3043 *
3044 * If we require an enum prefix, then the input can't be an enum, and we don't do any more
3045 * parsing.
3046 *
3047 * Otherwise if there's no prefix and no enumv, we know this input can't be an enum name.
3048 */
3049 if (!fr_sbuff_adv_past_str_literal(&our_in, "::")) {
3050 return 0;
3051
3052 } else if (t_rules->enumv &&
3053 ((t_rules->enumv->type == FR_TYPE_IPV6_ADDR) ||
3054 ((t_rules->enumv->type == FR_TYPE_IPV6_PREFIX)))) {
3055
3056 /*
3057 * We can't have enumerated names for IPv6 addresses.
3058 *
3059 * @todo - allow them ONLY if the RHS string is a valid enum name.
3060 */
3061 return 0;
3062 }
3063
3064 /*
3065 * Need to store the value with the prefix, because the value box functions
3066 * expect it to be there...
3067 */
3068 fr_sbuff_in_strcpy_literal(enum_buff, "::");
3069
3070 vpt = tmpl_alloc_null(ctx);
3071
3072 /*
3073 * If it doesn't match any other type of bareword, parse it as an enum name.
3074 *
3075 * Note that we don't actually try to resolve the enum name. The caller is responsible
3076 * for doing that.
3077 */
3078 if (fr_dict_enum_name_from_substr(enum_buff, &sberr, &our_in, p_rules ? p_rules->terminals : NULL) < 0) {
3079 /*
3080 * Produce our own errors which make
3081 * more sense in the context of tmpls
3082 */
3083 switch (sberr) {
3085 fr_strerror_const("No operand found. Expected &ref, literal, "
3086 "'quoted literal', \"%{expansion}\", or enum value");
3087 break;
3088
3090 fr_strerror_const("enum values must contain at least one alpha character");
3091 break;
3092
3093 default:
3094 fr_strerror_const("Unexpected text after enum value.");
3095 break;
3096 }
3097
3099 FR_SBUFF_ERROR_RETURN(&our_in);
3100 }
3101
3102 /*
3103 * If there's a valid enum name, then we use it. Otherwise we leave name resolution to run time.
3104 */
3105 if (t_rules->enumv) {
3106 fr_dict_enum_value_t const *dv;
3107
3108 dv = fr_dict_enum_by_name(t_rules->enumv, fr_sbuff_start(enum_buff), fr_sbuff_used(enum_buff));
3109 if (dv) {
3111 fr_sbuff_start(&our_in), fr_sbuff_used(&our_in), t_rules);
3112 if (unlikely(fr_value_box_copy(vpt, &vpt->data.literal, dv->value) < 0)) {
3114 return -1;
3115 }
3116 vpt->data.literal.enumv = t_rules->enumv;
3117
3118 *out = vpt;
3119 FR_SBUFF_SET_RETURN(in, &our_in);
3120 }
3121 }
3122
3123 /*
3124 * Either there's no enum, or the enum name didn't match one of the listed ones. There's no
3125 * point in waiting for an enum which might be declared later. That's not possible, so we fall
3126 * back to parsing the various data types.
3127 */
3128 if (t_rules->at_runtime) return 0;
3129
3131 fr_sbuff_start(&our_in), fr_sbuff_used(&our_in), t_rules);
3132 MEM(vpt->data.unescaped = talloc_bstrndup(vpt, fr_sbuff_start(enum_buff), fr_sbuff_used(enum_buff)));
3133 *out = vpt;
3134
3135 FR_SBUFF_SET_RETURN(in, &our_in);
3136}
3137
3138/** Convert an arbitrary string into a #tmpl_t
3139 *
3140 * @note Unlike #tmpl_afrom_attr_str return code 0 doesn't necessarily indicate failure,
3141 * may just mean a 0 length string was parsed. Check to see if the function emitted
3142 * a #tmpl_t in *out.
3143 *
3144 * @note xlats and regexes are left uncompiled. This is to support the two pass parsing
3145 * done by the modcall code. Compilation on pass1 of that code could fail, as
3146 * attributes or xlat functions registered by modules may not be available (yet).
3147 *
3148 * @note For details of attribute parsing see #tmpl_afrom_attr_substr.
3149 *
3150 * @param[in,out] ctx To allocate #tmpl_t in.
3151 * @param[out] out Where to write the pointer to the new #tmpl_t.
3152 * @param[in] in String to parse.
3153 * @param[in] quote Quoting around the tmpl. Determines what we
3154 * attempt to parse the string as.
3155 * @param[in] p_rules Formatting rules for the tmpl.
3156 * @param[in] t_rules Validation rules for attribute references.
3157 * @return
3158 * - < 0 on error (offset as negative integer)
3159 * - >= 0 on success (number of bytes parsed).
3160 *
3161 * @see REMARKER to produce pretty error markers from the return value.
3162 *
3163 * @see tmpl_afrom_attr_substr
3164 */
3166 fr_sbuff_t *in, fr_token_t quote,
3167 fr_sbuff_parse_rules_t const *p_rules,
3168 tmpl_rules_t const *t_rules)
3169{
3170 fr_sbuff_t our_in = FR_SBUFF(in);
3171
3172 fr_slen_t slen;
3174 char *str;
3175
3176 tmpl_t *vpt = NULL;
3178
3180
3181 *out = NULL;
3182
3183 switch (quote) {
3184 case T_BARE_WORD:
3185 /*
3186 * Skip other bareword types if
3187 * we find a '&' prefix.
3188 */
3189 if (fr_sbuff_is_char(&our_in, '&')) return tmpl_afrom_attr_substr(ctx, NULL, out, in,
3190 p_rules, t_rules);
3191
3192 /*
3193 * Allow bareword xlats if we
3194 * find a '%' prefix.
3195 */
3196 if (fr_sbuff_is_char(&our_in, '%')) {
3198 xlat_exp_head_t *head = NULL;
3199
3200 vpt = tmpl_alloc_null(ctx);
3201 slen = xlat_tokenize(vpt, &head, &our_in, p_rules, t_rules);
3202 if (slen <= 0) FR_SBUFF_ERROR_RETURN(&our_in);
3203
3206 goto set_tmpl;
3207
3208 } else if (fr_dlist_num_elements(&head->dlist) == 1) {
3209 xlat_exp_t *node = xlat_exp_head(head);
3210 tmpl_t *hoisted;
3211
3212 if (node->type != XLAT_TMPL) goto set_tmpl;
3213
3214 /*
3215 * We were asked to parse a tmpl. But it turned out to be an xlat %{...}
3216 *
3217 * If that xlat is identically a tmpl such as %{User-Name}, then we just
3218 * hoist the tmpl to this node. Otherwise at run time, we will have an
3219 * extra bounce through the xlat code, for no real reason.
3220 */
3221 hoisted = node->vpt;
3222
3223 (void) talloc_steal(ctx, hoisted);
3225 vpt = hoisted;
3226
3227 } else {
3228 set_tmpl:
3229 tmpl_init(vpt, type, quote, fr_sbuff_start(&our_in), slen, t_rules);
3230 vpt->data.xlat.ex = head;
3231 }
3232
3233 *out = vpt;
3234
3236
3237 FR_SBUFF_SET_RETURN(in, &our_in);
3238 }
3239
3240 /*
3241 * Deal with explicit casts...
3242 */
3243 if (!fr_type_is_null(t_rules->cast)) return tmpl_afrom_value_substr(ctx, out, in, quote,
3244 t_rules, true, p_rules);
3245
3246 /*
3247 * We're at runtime and have a data type. Just parse it as that data type, without doing
3248 * endless "maybe it's this thing" attempts.
3249 */
3250 if (t_rules->at_runtime && t_rules->enumv) {
3251 tmpl_rules_t my_t_rules = *t_rules;
3252
3253 fr_assert(fr_type_is_leaf(t_rules->enumv->type));
3254
3255 my_t_rules.cast = my_t_rules.enumv->type;
3256
3257 return tmpl_afrom_value_substr(ctx, out, in, quote, &my_t_rules, true, p_rules);
3258 }
3259
3260 /*
3261 * Prefer enum names to IPv6 addresses.
3262 */
3263 if (t_rules->enumv && fr_sbuff_is_str_literal(&our_in, "::")) {
3264 slen = tmpl_afrom_enum(ctx, out, &our_in, p_rules, t_rules);
3265 if (slen > 0) goto done_bareword;
3266 fr_assert(!*out);
3267 }
3268
3269 /*
3270 * See if it's a boolean value
3271 */
3272 slen = tmpl_afrom_bool_substr(ctx, out, &our_in, p_rules);
3273 if (slen > 0) {
3274 done_bareword:
3275 TMPL_VERIFY(*out);
3276
3277 FR_SBUFF_SET_RETURN(in, &our_in);
3278 }
3279 fr_assert(!*out);
3280
3281 /*
3282 * See if it's an octets string
3283 */
3284 slen = tmpl_afrom_octets_substr(ctx, out, &our_in, p_rules);
3285 if (slen > 0) goto done_bareword;
3286 fr_assert(!*out);
3287
3288 /*
3289 * See if it's a mac address
3290 *
3291 * Needs to be before IPv6 as the pton functions
3292 * are too greedy, and on macOS will happily
3293 * convert a mac address to an IPv6 address.
3294 */
3295 slen = tmpl_afrom_ether_substr(ctx, out, &our_in, p_rules);
3296 if (slen > 0) goto done_bareword;
3297 fr_assert(!*out);
3298
3299 /*
3300 * See if it's an IPv4 address or prefix
3301 */
3302 slen = tmpl_afrom_ipv4_substr(ctx, out, &our_in, p_rules);
3303 if (slen > 0) goto done_bareword;
3304 fr_assert(!*out);
3305
3306 /*
3307 * See if it's an IPv6 address or prefix
3308 */
3309 slen = tmpl_afrom_ipv6_substr(ctx, out, &our_in, p_rules);
3310 if (slen > 0) goto done_bareword;
3311 fr_assert(!*out);
3312
3313 slen = tmpl_afrom_enum(ctx, out, &our_in, p_rules, t_rules);
3314 if (slen > 0) goto done_bareword;
3315 fr_assert(!*out);
3316
3317 /*
3318 * See if it's a integer
3319 */
3320 slen = tmpl_afrom_integer_substr(ctx, out, &our_in, p_rules);
3321 if (slen > 0) goto done_bareword;
3322 fr_assert(!*out);
3323
3324 /*
3325 * See if it's a float
3326 */
3327 slen = tmpl_afrom_float_substr(ctx, out, &our_in, p_rules);
3328 if (slen > 0) goto done_bareword;
3329 fr_assert(!*out);
3330
3331 /*
3332 * See if it's a time delta
3333 *
3334 * We do this after floats and integers so that
3335 * they get parsed as integer and float types
3336 * and not time deltas.
3337 */
3338 slen = tmpl_afrom_time_delta(ctx, out, &our_in, p_rules);
3339 if (slen > 0) goto done_bareword;
3340 fr_assert(!*out);
3341
3342 /*
3343 * See if it's an attribute reference
3344 * without the prefix.
3345 */
3346 slen = tmpl_afrom_attr_substr(ctx, NULL, out, &our_in, p_rules, t_rules);
3347 if (slen > 0) goto done_bareword;
3348 fr_assert(!*out);
3349
3350 /*
3351 * We can't parse it as anything, that's an error.
3352 *
3353 * But it may be an enumeration value for an
3354 * attribute which is loaded later. In which
3355 * case we allow parsing the enumeration.
3356 */
3357 if (!fr_sbuff_is_str_literal(&our_in, "::")) {
3358 /*
3359 * Return the error string from parsing the attribute!
3360 */
3361 FR_SBUFF_ERROR_RETURN(&our_in);
3362 }
3363
3364 /*
3365 * Attempt to resolve enumeration values
3366 */
3367 vpt = tmpl_alloc_null(ctx);
3368
3369 /*
3370 * If it doesn't match any other type of bareword, parse it as an enum name.
3371 *
3372 * Note that we don't actually try to resolve the enum name. The caller is responsible
3373 * for doing that.
3374 */
3375 if (fr_dict_enum_name_afrom_substr(vpt, &str, &sberr, &our_in, p_rules ? p_rules->terminals : NULL) < 0) {
3376 /*
3377 * Produce our own errors which make
3378 * more sense in the context of tmpls
3379 */
3380 switch (sberr) {
3382 fr_strerror_const("No operand found. Expected &ref, literal, "
3383 "'quoted literal', \"%{expansion}\", or enum value");
3384 break;
3385
3387 fr_strerror_const("enum values must contain at least one alpha character");
3388 break;
3389
3390 default:
3391 fr_strerror_const("Unexpected text after enum value.");
3392 break;
3393 }
3394
3396 FR_SBUFF_ERROR_RETURN(&our_in);
3397 }
3398
3400 fr_sbuff_start(&our_in), fr_sbuff_used(&our_in), t_rules);
3401 vpt->data.unescaped = str;
3402 *out = vpt;
3403
3404 FR_SBUFF_SET_RETURN(in, &our_in);
3405
3407 /*
3408 * Single quoted strings can be cast
3409 * to a specific data type immediately
3410 * as they cannot contain expansions.
3411 */
3412 if (!fr_type_is_null(t_rules->cast)) return tmpl_afrom_value_substr(ctx, out, in, quote,
3413 t_rules, false,
3414 p_rules);
3415 vpt = tmpl_alloc_null(ctx);
3416 slen = fr_sbuff_out_aunescape_until(vpt, &str, &our_in, SIZE_MAX,
3417 p_rules ? p_rules->terminals : NULL,
3418 p_rules ? p_rules->escapes : NULL);
3419 tmpl_init(vpt, TMPL_TYPE_DATA_UNRESOLVED, quote, fr_sbuff_start(&our_in), slen, t_rules);
3420 vpt->data.unescaped = str;
3421 break;
3422
3424 {
3425 xlat_exp_head_t *head = NULL;
3427
3428 vpt = tmpl_alloc_null(ctx);
3429
3430 slen = xlat_tokenize(vpt, &head, &our_in, p_rules, t_rules);
3431 if (slen < 0) FR_SBUFF_ERROR_RETURN(&our_in);
3432
3433 /*
3434 * If the string doesn't contain an xlat,
3435 * and we want to cast it as a specific
3436 * type, then do the conversion now.
3437 */
3438 if (xlat_is_literal(head)) {
3439 if (!fr_type_is_null(t_rules->cast)) {
3440 talloc_free(vpt); /* Also frees any nodes */
3441
3442 return tmpl_afrom_value_substr(ctx, out,
3443 in, quote,
3444 t_rules, false, p_rules);
3445 }
3446
3447 /*
3448 * If the string doesn't contain an xlat
3449 * and there's no cast, we just store
3450 * the string for conversion later.
3451 */
3452 if (xlat_to_string(vpt, &str, &head)) {
3453 TALLOC_FREE(head);
3454
3456 fr_sbuff_start(&our_in), slen, t_rules);
3457 vpt->data.unescaped = str; /* Store the unescaped string for parsing later */
3458 break;
3459 }
3460 }
3461
3462 /*
3463 * If the string actually contains an xlat
3464 * store the compiled xlat.
3465 */
3467
3468 tmpl_init(vpt, type, quote, fr_sbuff_start(&our_in), slen, t_rules);
3469 vpt->data.xlat.ex = head;
3470 }
3471 break;
3472
3474 {
3476 xlat_exp_head_t *head = NULL;
3477
3478 vpt = tmpl_alloc_null(ctx);
3479
3480 /*
3481 * Ensure that we pre-parse the exec string.
3482 * This allows us to catch parse errors as early
3483 * as possible.
3484 *
3485 * FIXME - We need an ephemeral version of this
3486 * too.
3487 */
3488 slen = xlat_tokenize_argv(vpt, &head, &our_in, NULL, p_rules, t_rules, true);
3489 if ((slen <= 0) || !head) {
3491 FR_SBUFF_ERROR_RETURN(&our_in);
3492 }
3493
3494 /*
3495 * Ensure any xlats produced are bootstrapped
3496 * so that their instance data will be created.
3497 */
3498 if (xlat_finalize(head, t_rules->xlat.runtime_el) < 0) {
3499 fr_strerror_const("Failed to bootstrap xlat");
3500 FR_SBUFF_ERROR_RETURN(&our_in);
3501 }
3502
3504
3505 tmpl_init(vpt, type, quote, fr_sbuff_start(&our_in), slen, t_rules);
3506 vpt->data.xlat.ex = head;
3507 }
3508 break;
3509
3511 {
3512 xlat_exp_head_t *head = NULL;
3514 tmpl_rules_t arg_t_rules = *t_rules;
3515
3516 arg_t_rules.literals_safe_for = FR_REGEX_SAFE_FOR;
3517
3518 if (!fr_type_is_null(t_rules->cast)) {
3519 fr_strerror_const("Casts cannot be used with regular expressions");
3520 fr_sbuff_set_to_start(&our_in); /* Point to the cast */
3521 FR_SBUFF_ERROR_RETURN(&our_in);
3522 }
3523
3524 vpt = tmpl_alloc_null(ctx);
3525
3526 slen = xlat_tokenize(vpt, &head, &our_in, p_rules, &arg_t_rules);
3527 if (slen < 0) FR_SBUFF_ERROR_RETURN(&our_in);
3528
3529 /*
3530 * Check if the string actually contains an xlat
3531 * if it doesn't, we unfortunately still
3532 * can't compile the regex here, as we don't know if
3533 * it should be ephemeral or what flags should be used
3534 * during the compilation.
3535 *
3536 * The caller will need to do the compilation after we
3537 * return.
3538 */
3539 if (xlat_to_string(vpt, &str, &head)) {
3541 fr_sbuff_start(&our_in), slen, t_rules);
3542 vpt->data.unescaped = str; /* Store the unescaped string for compilation later */
3543 break;
3544 }
3545 /*
3546 * Mark the regex up as a regex-xlat which
3547 * will need expanding before evaluation, and can never
3548 * be pre-compiled.
3549 */
3551
3552 tmpl_init(vpt, type, quote, fr_sbuff_start(&our_in), slen, t_rules);
3553 vpt->data.xlat.ex = head;
3554 }
3555 break;
3556
3557 default:
3558 fr_assert_msg(0, "Unknown quote type %i", quote);
3559 FR_SBUFF_ERROR_RETURN(&our_in);
3560 }
3561
3563 *out = vpt;
3564
3565 FR_SBUFF_SET_RETURN(in, &our_in);
3566}
3567
3568/** Copy a tmpl
3569 *
3570 * Fully duplicates the contents of a tmpl including any nested attribute
3571 * references.
3572 *
3573 * @param[in] ctx to perform allocations under.
3574 * @param[in] in tmpl to duplicate.
3575 * @return
3576 * - NULL on error.
3577 * - A new tmpl on success.
3578 */
3579tmpl_t *tmpl_copy(TALLOC_CTX *ctx, tmpl_t const *in)
3580{
3581 tmpl_t *vpt;
3582
3583 MEM(vpt = tmpl_alloc(ctx, in->type, in->quote, in->name, in->len));
3584 vpt->rules = in->rules;
3585
3586 /*
3587 * Copy over the unescaped data
3588 */
3590 if (unlikely(!(vpt->data.unescaped = talloc_bstrdup(vpt, in->data.unescaped)))) {
3591 error:
3593 return NULL;
3594 }
3595 }
3596
3597 /*
3598 * Copy attribute references
3599 */
3600 else if (tmpl_contains_attr(vpt)) {
3601 if (unlikely(tmpl_attr_copy(vpt, in) < 0)) goto error;
3602
3603 /*
3604 * Copy flags for all regex flavours (and possibly recompile the regex)
3605 */
3606 } else if (tmpl_contains_regex(vpt)) {
3607 vpt->data.reg_flags = in->data.reg_flags;
3608
3609 /*
3610 * If the tmpl contains a _compiled_ regex
3611 * then convert it back to an uncompiled
3612 * regex and recompile.
3613 *
3614 * Most of the regex libraries don't allow
3615 * copying compiled expressions.
3616 */
3617 if (tmpl_is_regex(vpt)) {
3619 if (unlikely(!(vpt->data.unescaped = talloc_bstrdup(vpt, in->data.reg.src)))) goto error;
3620 if (unlikely(tmpl_regex_compile(vpt, vpt->data.reg.subcaptures) < 0)) goto error;
3621 return vpt;
3622 }
3623
3624 /*
3625 * Copy the xlat component.
3626 *
3627 * @todo - in general we can't copy an xlat, as the instances need resolving!
3628 *
3629 * We add an assertion here because nothing allocates the head, and we need it.
3630 */
3631 } else if (tmpl_contains_xlat(vpt)) {
3632 fr_assert(in->data.xlat.ex != NULL);
3633
3634 vpt->data.xlat.ex = xlat_exp_head_alloc(vpt);
3635 if (!vpt->data.xlat.ex) goto error;
3636
3637 if (unlikely(xlat_copy(vpt, vpt->data.xlat.ex, in->data.xlat.ex) < 0)) goto error;
3638
3639 } else if (tmpl_is_data(vpt)) {
3640 if (unlikely(fr_value_box_copy(vpt, &vpt->data.literal, &in->data.literal) < 0)) goto error;
3641
3642 } else {
3643 fr_assert(0); /* copy of this type is unimplemented */
3644 }
3645
3647
3648 return vpt;
3649}
3650
3651/** Parse a cast specifier
3652 *
3653 * Note that casts are
3654 *
3655 * (foo)
3656 *
3657 * and NOT
3658 *
3659 * ( foo )
3660 *
3661 * Not for any particular reason, but to emphasize a bit that they're
3662 * not mathematical expressions.
3663 *
3664 * @param[out] rules to set the cast type in.
3665 * @param[in] in String containing the cast marker.
3666 * @return
3667 * - 0 no cast specifier found.
3668 * - >0 the number of bytes parsed.
3669 * - <0 offset of parse error.
3670 */
3672{
3673 char close = '\0';
3674 fr_sbuff_t our_in = FR_SBUFF(in);
3676 fr_type_t cast;
3677 ssize_t slen;
3678
3679 if (fr_sbuff_next_if_char(&our_in, '(')) {
3680 close = ')';
3681
3682 } else {
3683 if (rules) rules->cast = FR_TYPE_NULL;
3684 return 0;
3685 }
3686
3687 fr_sbuff_marker(&m, &our_in);
3689 if (fr_type_is_null(cast)) {
3690 fr_strerror_const("Unknown data type");
3691 FR_SBUFF_ERROR_RETURN(&our_in);
3692 }
3693 if (fr_type_is_non_leaf(cast)) {
3694 fr_strerror_printf("Forbidden data type '%s' in cast", fr_type_to_str(cast));
3696 }
3697
3698 if (!fr_sbuff_next_if_char(&our_in, close)) {
3699 fr_strerror_const("Unterminated cast");
3700 FR_SBUFF_ERROR_RETURN(&our_in);
3701 }
3702 fr_sbuff_adv_past_whitespace(&our_in, SIZE_MAX, NULL);
3703
3704 if (rules) rules->cast = cast;
3705
3706 FR_SBUFF_SET_RETURN(in, &our_in);
3707}
3708
3709/** Set a cast for a tmpl
3710 *
3711 * @param[in,out] vpt to set cast for.
3712 * @param[in] dst_type to set.
3713 * @return
3714 * - 0 on success.
3715 * - -1 on failure.
3716 */
3718{
3719 fr_type_t src_type;
3720
3721 switch (dst_type) {
3722 default:
3723 fr_strerror_printf("Forbidden data type '%s' in cast",
3724 fr_type_to_str(dst_type));
3725 return -1;
3726
3727 /*
3728 * We can always remove a cast.
3729 */
3730 case FR_TYPE_NULL:
3731 goto done;
3732
3733 /*
3734 * Only "base" data types are allowed. Structural types
3735 * and horrid WiMAX crap is forbidden.
3736 */
3737 case FR_TYPE_LEAF:
3738 break;
3739 }
3740
3741 switch (vpt->type) {
3742 /*
3743 * This should have been fixed before we got here.
3744 */
3746
3747 /*
3748 * By default, tmpl types cannot be cast to anything.
3749 */
3750 default:
3751 fr_strerror_const("Cannot use cast here.");
3752 return -1;
3753
3754 /*
3755 * These tmpl types are effectively of data type
3756 * "string", so they can be cast to anything.
3757 */
3758 case TMPL_TYPE_XLAT:
3759 case TMPL_TYPE_EXEC:
3763 break;
3764
3765 case TMPL_TYPE_DATA:
3766 src_type = tmpl_value_type(vpt);
3767 goto check_types;
3768
3769 case TMPL_TYPE_ATTR:
3770 {
3772
3773 /*
3774 * If the attribute has an enum, then the cast means "use the raw value, and not
3775 * the enum name".
3776 */
3777 if (da->type == dst_type) {
3778 if (da->flags.has_value) goto done;
3779 return 0;
3780 }
3781 src_type = da->type;
3782 }
3783
3784 /*
3785 * Suppress casts where they are duplicate, unless there's an enumv. In which case the
3786 * cast means "don't print the enumv value, just print the raw data".
3787 */
3788 check_types:
3789 if (src_type == dst_type) {
3790 /*
3791 * Cast with enumv means "use the raw value, and not the enum name".
3792 */
3793 if (tmpl_rules_enumv(vpt)) {
3794 tmpl_rules_enumv(vpt) = NULL;
3795 goto done;
3796 }
3797 return 0;
3798 }
3799
3800 if (!fr_type_cast(dst_type, src_type)) {
3801 fr_strerror_printf("Cannot cast type '%s' to '%s'",
3802 fr_type_to_str(src_type),
3803 fr_type_to_str(dst_type));
3804 return -1;
3805 }
3806 break;
3807 }
3808
3809done:
3810 vpt->rules.cast = dst_type;
3811 return 0;
3812}
3813
3814#ifdef HAVE_REGEX
3815/** Parse a set of regular expression flags
3816 *
3817 * @param[out] vpt Write the flags to the regex flags field in this #tmpl_t.
3818 * @param[in] in Where to parse the flag string from.
3819 * @param[in] terminals That mark the end of the regex flag string.
3820 * @return
3821 * - 0 no flags found.
3822 * - >0 the number of bytes of flags parsed.
3823 * - <0 offset of parse error.
3824 */
3825ssize_t tmpl_regex_flags_substr(tmpl_t *vpt, fr_sbuff_t *in, fr_sbuff_term_t const *terminals)
3826{
3827 fr_slen_t slen;
3828 int err = 0;
3829
3831
3832 slen = regex_flags_parse(&err, &vpt->data.reg_flags, in, terminals, true);
3833 switch (err) {
3834 case 0:
3835 break;
3836
3837 case -1: /* Non-flag and non-terminal */
3838 case -2: /* Duplicate flag */
3839 return slen;
3840 }
3841
3842 return slen;
3843}
3844#endif
3845
3846/** @name Change a #tmpl_t type, usually by casting or resolving a reference
3847 *
3848 * #tmpl_cast_in_place can be used to convert #TMPL_TYPE_DATA_UNRESOLVED to a #TMPL_TYPE_DATA of a
3849 * specified #fr_type_t.
3850 *
3851 * #tmpl_attr_unknown_add converts a #TMPL_TYPE_ATTR with an unknown #fr_dict_attr_t to a
3852 * #TMPL_TYPE_ATTR with a known #fr_dict_attr_t, by adding the unknown #fr_dict_attr_t to the main
3853 * dictionary, and updating the ``tmpl_attr_tail_da`` pointer.
3854 * @{
3855 */
3856
3857/** Determine the correct quoting after a cast
3858 *
3859 * @param[in] existing_quote Exiting quotation type.
3860 * @param[in] type Cast type.
3861 * @param[in] enumv Enumeration values.
3862 * @param[in] unescaped The unescaped value of an enumeration.
3863 * @param[in] unescaped_len Length of unescaped.
3864 */
3865static inline CC_HINT(always_inline)
3867 fr_type_t type, fr_dict_attr_t const *enumv,
3868 char const *unescaped, size_t unescaped_len)
3869{
3870 if (!fr_type_is_string(type)) return T_BARE_WORD;
3871
3872 if (enumv && fr_dict_enum_by_name(enumv, unescaped, unescaped_len)) return T_BARE_WORD;
3873
3874 /*
3875 * Leave the original quoting if it's
3876 * single or double, else default to
3877 * single quoting.
3878 */
3879 switch (existing_quote) {
3882 return existing_quote;
3883
3884 default:
3886 }
3887}
3888
3889
3890/** Convert #tmpl_t of type #TMPL_TYPE_DATA_UNRESOLVED or #TMPL_TYPE_DATA to #TMPL_TYPE_DATA of type specified
3891 *
3892 * @note Conversion is done in place.
3893 * @note Irrespective of whether the #tmpl_t was #TMPL_TYPE_DATA_UNRESOLVED or #TMPL_TYPE_DATA,
3894 * on successful cast it will be #TMPL_TYPE_DATA.
3895 *
3896 * @param[in,out] vpt The template to modify. Must be of type #TMPL_TYPE_DATA_UNRESOLVED
3897 * or #TMPL_TYPE_DATA.
3898 * @param[in] type to cast to.
3899 * @param[in] enumv Enumerated dictionary values associated with a #fr_dict_attr_t.
3900 * @return
3901 * - 0 on success.
3902 * - -1 on failure.
3903 */
3905{
3907
3909
3910 switch (vpt->type) {
3912 {
3913 char *unescaped = vpt->data.unescaped;
3914
3915 /*
3916 * We're trying to convert an unresolved (bareword)
3917 * tmpl to octets.
3918 *
3919 * tmpl_afrom_substr uses the 0x prefix as type
3920 * inference, so if it was a hex string the tmpl
3921 * type would not have fallen through to
3922 * unresolved.
3923 *
3924 * That means if we're trying to resolve it here
3925 * it's really a printable string, not a sequence
3926 * of hexits, so we just want the binary
3927 * representation of that string, and not the hex
3928 * to bin conversion.
3929 */
3930 if (fr_type_is_octets(type)) {
3931 if (fr_value_box_memdup(vpt, &vpt->data.literal, enumv,
3932 (uint8_t const *)unescaped, talloc_array_length(unescaped) - 1,
3933 false) < 0) return -1;
3934 } else {
3935 if (fr_value_box_from_str(vpt, &vpt->data.literal, type,
3936 enumv,
3937 unescaped, talloc_array_length(unescaped) - 1,
3938 NULL) < 0) return -1;
3939 }
3940 vpt->type = TMPL_TYPE_DATA;
3941 vpt->quote = tmpl_cast_quote(vpt->quote, type, enumv,
3942 unescaped, talloc_array_length(unescaped) - 1);
3943 talloc_free(unescaped);
3944 fr_value_box_mark_safe_for(&vpt->data.literal, vpt->rules.literals_safe_for);
3945
3946 /*
3947 * The data is now of the correct type, so we don't need to keep a cast.
3948 */
3949 vpt->rules.cast = FR_TYPE_NULL;
3950 }
3951 break;
3952
3953 case TMPL_TYPE_DATA:
3954 {
3955 if (type == tmpl_value_type(vpt)) return 0; /* noop */
3956
3957 /*
3958 * Enumerations aren't used when casting between
3959 * data types. They're only used when processing
3960 * unresolved tmpls.
3961 *
3962 * i.e. TMPL_TYPE_DATA_UNRESOLVED != TMPL_TYPE_DATA(FR_TYPE_STRING)
3963 */
3964 if (fr_value_box_cast_in_place(vpt, &vpt->data.literal, type, NULL) < 0) return -1;
3965// fr_value_box_mark_safe_for(&vpt->data.literal, vpt->rules.literals_safe_for); ??? is this necessary?
3966
3967 /*
3968 * Strings get quoted, everything else is a bare
3969 * word...
3970 */
3971 if (fr_type_is_string(type)) {
3972 vpt->quote = T_SINGLE_QUOTED_STRING;
3973 } else {
3974 vpt->quote = T_BARE_WORD;
3975 }
3976
3977 /*
3978 * The data is now of the correct type, so we don't need to keep a cast.
3979 */
3980 vpt->rules.cast = FR_TYPE_NULL;
3981 }
3982 break;
3983
3984 case TMPL_TYPE_ATTR:
3985 /*
3986 * Suppress casts to the same type.
3987 */
3988 if (tmpl_attr_tail_da(vpt)->type == type) {
3989 vpt->rules.cast = FR_TYPE_NULL;
3990 break;
3991 }
3993
3995 vpt->rules.cast = type;
3996 break;
3997
3998 default:
3999 fr_assert(0);
4000 }
4002
4003 return 0;
4004}
4005
4006/** Resolve an unresolved attribute
4007 *
4008 * Multi-pass parsing fixups for attribute references.
4009 *
4010 * @param[in] vpt to resolve.
4011 * @param[in] tr_rules Combined with the original parse rules for
4012 * additional resolution passes.
4013 * @return
4014 * - 0 if all references were resolved.
4015 * - -1 if there are unknown attributes which need
4016 * adding to the global dictionary first.
4017 * - -2 if there are attributes we couldn't resolve.
4018 */
4019static inline CC_HINT(always_inline) int tmpl_attr_resolve(tmpl_t *vpt, tmpl_res_rules_t const *tr_rules)
4020{
4021 tmpl_attr_t *ar = NULL, *next, *prev;
4022 fr_dict_attr_t const *da, *namespace;
4023 fr_dict_t const *dict_def;
4024
4026
4028
4029 dict_def = vpt->rules.attr.dict_def;
4030 if (!dict_def || tr_rules->force_dict_def) dict_def = tr_rules->dict_def;
4031
4032 /*
4033 * First component is special because we may need
4034 * to search for it in multiple dictionaries.
4035 *
4036 * This emulates what's done in the initial
4037 * tokenizer function.
4038 */
4039 ar = tmpl_attr_list_head(tmpl_attr(vpt));
4040 if (ar->type == TMPL_ATTR_TYPE_UNRESOLVED) {
4042 &da,
4043 dict_def,
4044 &FR_SBUFF_IN(ar->ar_unresolved,
4045 talloc_array_length(ar->ar_unresolved) - 1),
4046 NULL,
4047 true,
4048 vpt->rules.attr.allow_foreign);
4049 if (!da) return -2; /* Can't resolve, maybe the caller can resolve later */
4050
4051 ar->ar_type = TMPL_ATTR_TYPE_NORMAL;
4052 ar->ar_da = da;
4053 ar->ar_parent = fr_dict_root(fr_dict_by_da(da));
4054
4055 /*
4056 * Record the dictionary that was
4057 * successfully used for resolution.
4058 */
4059 vpt->rules.attr.dict_def = tr_rules->dict_def;
4060
4061 /*
4062 * Reach into the next reference
4063 * and correct its parent and
4064 * namespace.
4065 */
4066 next = tmpl_attr_list_next(tmpl_attr(vpt), ar);
4067 if (next) {
4068 next->ar_parent = da;
4069 next->ar_unresolved_namespace = da;
4070 }
4071 }
4072
4073 /*
4074 * Loop, resolving each unresolved attribute in turn
4075 */
4076 while ((ar = tmpl_attr_list_next(tmpl_attr(vpt), ar))) {
4077 switch (ar->type) {
4080 continue; /* Don't need to resolve */
4081
4083 return -1; /* Unknown attributes must be resolved first */
4084
4085 default:
4086 break;
4087 }
4088
4089 prev = tmpl_attr_list_prev(tmpl_attr(vpt), ar);
4090
4091 /*
4092 * If the parent is a list AR, then use the default dictionary for the namespace
4093 */
4094 namespace = (prev && dict_def && tmpl_attr_is_list_attr(prev)) ? fr_dict_root(dict_def) : ar->ar_unresolved_namespace;
4095
4096 (void)fr_dict_attr_by_name_substr(NULL,
4097 &da,
4098 namespace,
4099 &FR_SBUFF_IN(ar->ar_unresolved,
4100 talloc_array_length(ar->ar_unresolved) - 1),
4101 NULL);
4102 /*
4103 * Still can't resolve, check to see if
4104 * the last attribute reference was a
4105 * group.
4106 *
4107 * If it was, then we may be able to
4108 * fall back to resolving the attribute
4109 * in the internal dictionary.
4110 */
4111 if (!da) {
4112 if (prev && (prev->ar_da->type == FR_TYPE_GROUP)) {
4113 (void)fr_dict_attr_by_name_substr(NULL,
4114 &da,
4116 &FR_SBUFF_IN(ar->ar_unresolved,
4117 talloc_array_length(ar->ar_unresolved) - 1),
4118 NULL);
4119 }
4120 if (!da) return -2;
4121 }
4122
4123 /*
4124 * Known attribute, just rewrite.
4125 */
4126 ar->ar_type = TMPL_ATTR_TYPE_NORMAL;
4127 ar->ar_da = da;
4128
4129 /*
4130 * Parent should have been corrected in
4131 * the previous loop iteration.
4132 */
4133 fr_assert(ar->ar_parent && !ar->ar_parent->flags.is_unknown);
4134
4135 /*
4136 * Reach into the next reference
4137 * and correct its parent.
4138 */
4139 next = tmpl_attr_list_next(tmpl_attr(vpt), ar);
4140 if (next) {
4141 next->ar_parent = da;
4142 next->ar_unresolved_namespace = da;
4143 }
4144
4145 /*
4146 * Remove redundant attributes
4147 *
4148 * If it's not a group or does not specify
4149 * an index, the ar is redundant and should
4150 * be removed.
4151 */
4152 prev = tmpl_attr_list_prev(tmpl_attr(vpt), ar);
4153 if (prev && (prev->ar_da->type != FR_TYPE_GROUP) && (prev->ar_num == NUM_UNSPEC)) {
4154 tmpl_attr_list_remove(tmpl_attr(vpt), prev);
4155 ar->ar_parent = prev->ar_parent;
4156 talloc_free(prev);
4157 }
4158 }
4159
4160 RESOLVED_SET(&vpt->type);
4162
4163 return 0;
4164}
4165
4166/** Resolve an unresolved xlat, i.e. one containing unresolved attribute references or xlat functions
4167 *
4168 * Multi-pass parsing fixups for attribute references.
4169 *
4170 * Works for base types:
4171 * - TMPL_TYPE_XLAT
4172 * - TMPL_TYPE_EXEC
4173 * - TMPL_TYPE_REGEX_XLAT
4174 *
4175 * @param[in] vpt Containing the xlat expansion to resolve.
4176 * @param[in] tr_rules Combined with the original parse rules for
4177 * additional resolution passes.
4178 * @return
4179 * - 0 on success.
4180 * - -1 on failure.
4181 */
4182static inline CC_HINT(always_inline)
4184{
4185 if (xlat_resolve(vpt->data.xlat.ex,
4187 .tr_rules = tr_rules,
4188 .allow_unresolved = false
4189 }) < 0) return -1;
4190
4191 fr_assert(!xlat_needs_resolving(vpt->data.xlat.ex));
4192
4193 RESOLVED_SET(&vpt->type);
4195
4196 return 0;
4197}
4198
4199/** Attempt to resolve functions and attributes in xlats and attribute references
4200 *
4201 * @note If resolution is successful, the rules->attr.dict_def field will be modified to
4202 * reflect the dictionary resolution was successful in.
4203 *
4204 * @param[in,out] vpt to resolve. Should be of type TMPL_TYPE_XLAT_UNRESOLVED
4205 * or TMPL_TYPE_ATTR_UNRESOLVED. All other types will be
4206 * noops.
4207 * @param[in] tr_rules Combined with the original parse rules for
4208 * additional resolution passes.
4209 * @return
4210 * - 0 on success.
4211 * - -1 on failure.
4212 */
4214{
4215 static tmpl_res_rules_t const default_tr_rules;
4216
4217 int ret = 0;
4218
4219 if (!tmpl_needs_resolving(vpt)) return 0; /* Nothing to do */
4220
4221 if (!tr_rules) tr_rules = &default_tr_rules;
4222
4223 /*
4224 * Sanity check. There shouldn't be conflicting
4225 * enumvs between the original rules and resolution
4226 * rules.
4227 *
4228 * Either the enumv was available during parsing
4229 * and shouldn't have changed during subsequent
4230 * resolution passes, or it wasn't available at
4231 * parse-time, but now is.
4232 */
4233 if (tr_rules->enumv && tmpl_rules_enumv(vpt) && !tmpl_rules_enumv(vpt)->flags.is_unknown &&
4234 (tr_rules->enumv != tmpl_rules_enumv(vpt))) {
4235 fr_strerror_printf("mismatch between parse-time enumv '%s' and resolution-time enumv '%s'",
4236 tmpl_rules_enumv(vpt)->name, tr_rules->enumv->name);
4237
4238 return -1;
4239 }
4240
4241 /*
4242 * The xlat component of the #tmpl_t needs resolving.
4243 *
4244 * This includes exec tmpls, which are largely xlats
4245 * "under the hood".
4246 */
4247 if (tmpl_contains_xlat(vpt)) {
4248 ret = tmpl_xlat_resolve(vpt, tr_rules);
4249
4250 /*
4251 * The attribute reference needs resolving.
4252 */
4253 } else if (tmpl_contains_attr(vpt)) {
4254 fr_type_t dst_type = tmpl_rules_cast(vpt);
4255
4256 fr_assert(vpt->quote == T_BARE_WORD); /* 'User-Name' or "User-Name" is not allowed. */
4257
4258 ret = tmpl_attr_resolve(vpt, tr_rules);
4259 if (ret < 0) return ret;
4260
4261 if (dst_type == tmpl_attr_tail_da(vpt)->type) {
4262 vpt->rules.cast = FR_TYPE_NULL;
4263 }
4264
4265 /*
4266 * Convert unresolved tmpls into enumvs, or failing that, string values.
4267 *
4268 * Unresolved tmpls are by definition TMPL_TYPE_DATA.
4269 */
4270 } else if (tmpl_is_data_unresolved(vpt)) {
4271 fr_type_t dst_type = tmpl_rules_cast(vpt);
4272 fr_dict_attr_t const *enumv = tmpl_rules_enumv(vpt);
4273
4274 /*
4275 * If there wasn't an enumv set in the
4276 * original rules, and we now have one
4277 * (possibly because the other side of a
4278 * binary expression has been resolved),
4279 * then use the new enumv.
4280 */
4281 if (!enumv) enumv = tr_rules->enumv;
4282
4283 /*
4284 * We don't have an explicit output type. Try to
4285 * interpret the data os the enumv data type, OR
4286 * if all else fails, it's a string.
4287 */
4288 if (fr_type_is_null(dst_type)) {
4289 /*
4290 * Infer the cast from the enumv type.
4291 */
4292 if (enumv) {
4293 dst_type = enumv->type;
4294
4295 } else if (vpt->quote != T_BARE_WORD) {
4296 dst_type = FR_TYPE_STRING; /* quoted strings are strings */
4297
4298 } else if (strncmp(vpt->data.unescaped, "::", 2) != 0) {
4299 /*
4300 * The rest of the code should have errored out before this.
4301 */
4302 fr_strerror_printf("Failed resolving data '%s' - it is not an attribute name or a quoted string", vpt->data.unescaped);
4303 return -1;
4304
4305 } else {
4306 /*
4307 * It's a valid enum ::NAME which was added _after_ the dictionaries were
4308 * loaded. That's fine. fr_value_box_from_substr() will skip over the
4309 * "::", and parse the enum name.
4310 */
4311 }
4312 }
4313
4314 /*
4315 * tmpl_cast_in_place first resolves using
4316 * the enumv, _then_ casts using the type.
4317 */
4318 if (tmpl_cast_in_place(vpt, dst_type, enumv) < 0) return -1;
4319
4321 /*
4322 * Catch any other cases of unresolved things
4323 * we need to address. We put the assert here
4324 * so we don't end up running inappropriate
4325 * code for non-debug builds.
4326 */
4327 } else {
4328 fr_assert(0);
4329 }
4330
4332
4333 return ret;
4334}
4335
4336/** Reset the tmpl, leaving only the name in place
4337 *
4338 * After calling this function, the tmpl type will revert to TMPL_TYPE_DATA_UNRESOLVED
4339 * and only the name and quoting will be preserved.
4340 *
4341 * @param[in] vpt to reset.
4342 */
4344{
4345 tmpl_t tmp = {
4347 .name = vpt->name,
4348 .len = vpt->len,
4349 .quote = vpt->quote
4350 };
4351
4352 switch (vpt->type) {
4354 case TMPL_TYPE_MAX:
4355 fr_assert(0);
4356 break;
4357
4360 break;
4361
4362 case TMPL_TYPE_DATA:
4363 fr_value_box_clear(&vpt->data.literal);
4364 break;
4365
4366 /*
4367 * These types contain dynamically allocated
4368 * attribute and request references.
4369 */
4370 case TMPL_TYPE_ATTR:
4372 tmpl_attr_list_talloc_free(tmpl_attr(vpt));
4373 tmpl_request_list_talloc_free(&vpt->data.attribute.rr);
4374 break;
4375
4376 /*
4377 * These all store an xlat expansion
4378 */
4379 case TMPL_TYPE_EXEC:
4380 case TMPL_TYPE_XLAT:
4385 TALLOC_FREE(vpt->data.xlat.ex);
4386 break;
4387
4388 case TMPL_TYPE_REGEX:
4389 talloc_free(vpt->data.reg.ex);
4390 break;
4391
4392 }
4393
4394 memcpy(vpt, &tmp, sizeof(*vpt));
4395
4397}
4398
4400{
4401 if (!ref) return;
4402
4403 switch (ref->type) {
4405 {
4406 ref->da = ref->ar_unknown = fr_dict_attr_unknown_afrom_da(vpt, ref->da);
4407 ref->ar_unknown->type = FR_TYPE_OCTETS;
4408 ref->is_raw = 1;
4409 ref->ar_unknown->flags.is_unknown = 1;
4411 }
4412 break;
4413 case TMPL_ATTR_TYPE_UNSPEC: /* noop */
4414 break;
4415
4417 ref->ar_unknown->type = FR_TYPE_OCTETS;
4418 ref->is_raw = 1;
4419 break;
4420
4422 ref->is_raw = true;
4423 break;
4424 }
4425
4427}
4428
4429/** Convert the leaf attribute of a tmpl to a unknown/raw type
4430 *
4431 */
4433{
4434 attr_to_raw(vpt, tmpl_attr_list_tail(tmpl_attr(vpt)));
4435}
4436
4437/** Add an unknown #fr_dict_attr_t specified by a #tmpl_t to the main dictionary
4438 *
4439 * @param vpt to add. ``tmpl_attr_tail_da`` pointer will be updated to point to the
4440 * #fr_dict_attr_t inserted into the dictionary.
4441 * @return
4442 * - 1 noop (did nothing) - Not possible to convert tmpl.
4443 * - 0 on success.
4444 * - -1 on failure.
4445 */
4447{
4448 tmpl_attr_t *ar = NULL, *next = NULL;
4449
4450 if (!vpt) return 1;
4451
4452 /*
4453 * Can't do this for expressions parsed at runtime
4454 */
4455 if (vpt->rules.at_runtime) return 1;
4456
4458
4460
4461 if (!tmpl_attr_tail_is_unknown(vpt)) return 1; /* Ensure at least the leaf is unknown */
4462
4463 while ((ar = tmpl_attr_list_next(tmpl_attr(vpt), ar))) {
4464 fr_dict_attr_t const *unknown, *known;
4465
4466 switch (ar->type) {
4467 case TMPL_ATTR_TYPE_NORMAL: /* Skip */
4469 continue;
4470
4471 case TMPL_ATTR_TYPE_UNRESOLVED: /* Shouldn't have been called */
4472 fr_strerror_const("Remaining attributes are unresolved");
4473 return -1;
4474
4476 break;
4477 }
4478
4479 unknown = ar->ar_unknown;
4480 known = fr_dict_attr_unknown_add(fr_dict_unconst(fr_dict_by_da(unknown)), unknown);
4481 if (!known) return -1;
4482
4483 /*
4484 * Fixup the parent of the next unknown
4485 * now it's known.
4486 */
4487 next = tmpl_attr_list_next(tmpl_attr(vpt), ar);
4488 if (next && (next->type == TMPL_ATTR_TYPE_UNKNOWN) &&
4489 (next->ar_da->parent == unknown)) {
4491 known) < 0) return -1;
4492 next->ar_parent = known;
4493 }
4494
4495 /*
4496 * Convert the ref to a normal type.
4497 * At runtime there should be no
4498 * "unknown" references as they should
4499 * have all been added to a
4500 * dictionary.
4501 */
4503
4504 /*
4505 * If the attribute is *NOT* raw then
4506 * swap the canonical unknown with the
4507 * one that was previously associated
4508 * with the tmpl.
4509 *
4510 * This establishes the unknown attribute
4511 * in the dictionary if it was really
4512 * unknown whilst not mucking up the
4513 * types for raw attributes.
4514 */
4515 if (!ar_is_raw(ar)) {
4516 fr_dict_attr_unknown_free(&ar->ar_da);
4517 ar->ar_da = known;
4518 } else if (!fr_cond_assert(!next)) {
4519 fr_strerror_const("Only the leaf may be raw");
4520 return -1;
4521 }
4522 }
4523
4524 return 0;
4525}
4526
4527/** Add an unresolved #fr_dict_attr_t specified by a #tmpl_t to the main dictionary
4528 *
4529 * @note fr_dict_attr_add will not return an error if the attribute already exists
4530 * meaning that multiple #tmpl_t specifying the same attribute can be
4531 * passed to this function to be fixed up, so long as the type and flags
4532 * are identical.
4533 *
4534 * @param[in] dict_def Default dictionary to use if none is
4535 * specified by the tmpl_attr_tail_unresolved.
4536 * @param[in] vpt specifying unresolved attribute to add.
4537 * ``tmpl_attr_tail_da`` pointer will be updated to
4538 * point to the #fr_dict_attr_t inserted
4539 * into the dictionary. Lists and requests
4540 * will be preserved.
4541 * @param[in] type to define unresolved attribute as.
4542 * @param[in] flags to define unresolved attribute with.
4543 * @return
4544 * - 1 noop (did nothing) - Not possible to convert tmpl.
4545 * - 0 on success.
4546 * - -1 on failure.
4547 */
4549 fr_type_t type, fr_dict_attr_flags_t const *flags)
4550{
4551 fr_dict_attr_t const *da;
4552 fr_dict_attr_flags_t our_flags = *flags;
4553
4554 our_flags.name_only = true;
4555
4556 if (!vpt) return -1;
4557
4559
4560 if (!tmpl_is_attr_unresolved(vpt)) return 1;
4561
4562 if (fr_dict_attr_add(dict_def,
4564 return -1;
4565 }
4567 if (!da) return -1;
4568
4569 if (type != da->type) {
4570 fr_strerror_printf("Attribute %s of type %s already defined with type %s",
4571 da->name, fr_type_to_str(type),
4572 fr_type_to_str(da->type));
4573 return -1;
4574 }
4575
4576 if (memcmp(flags, &da->flags, sizeof(*flags)) != 0) {
4577 fr_strerror_printf("Attribute %s already defined with different flags", da->name);
4578 return -1;
4579 }
4580
4581 tmpl_attr_set_da(vpt, da);
4582 vpt->type = TMPL_TYPE_ATTR;
4583
4584 return 0;
4585}
4586
4587#ifdef HAVE_REGEX
4588/** Convert a TMPL_TYPE_REGEX_UNCOMPILED into a TMPL_TYPE_REGEX
4589 *
4590 * Other regex types become noops.
4591 */
4592ssize_t tmpl_regex_compile(tmpl_t *vpt, bool subcaptures)
4593{
4594 ssize_t slen;
4595 char *unescaped = vpt->data.unescaped;
4596
4597 if (tmpl_is_regex_xlat(vpt) || tmpl_is_regex(vpt)) return 0; /* Don't need compiling */
4598
4600
4601 slen = regex_compile(vpt, &vpt->data.reg.ex,
4602 unescaped, talloc_array_length(unescaped) - 1,
4603 &vpt->data.reg_flags, subcaptures, vpt->rules.at_runtime);
4604 if (slen <= 0) return vpt->quote != T_BARE_WORD ? slen - 1 : slen; /* Account for the quoting */
4605
4606 vpt->type = TMPL_TYPE_REGEX;
4607 vpt->data.reg.src = unescaped; /* Keep this around for debugging and copying */
4608 vpt->data.reg.subcaptures = subcaptures;
4609
4611
4612 return slen;
4613}
4614#endif
4615/** @} */
4616
4617/** @name Print the contents of a #tmpl_t
4618 * @{
4619 */
4621{
4622 fr_sbuff_t our_out = FR_SBUFF(out);
4623 tmpl_request_t *rr = tmpl_request_list_head(rql);
4624
4625 /*
4626 * Print request references
4627 */
4628 while (rr) {
4629 FR_SBUFF_IN_TABLE_STR_RETURN(&our_out, tmpl_request_ref_table, rr->request, "<INVALID>");
4630 rr = tmpl_request_list_next(rql, rr);
4631 if (rr) FR_SBUFF_IN_CHAR_RETURN(&our_out, '.');
4632 }
4633
4634 FR_SBUFF_SET_RETURN(out, &our_out);
4635}
4636
4637/** Print an attribute or list #tmpl_t to a string
4638 *
4639 * This function is the direct counterpart to #tmpl_afrom_attr_substr.
4640 *
4641 * @param[in] out Where to write the presentation format #tmpl_t string.
4642 * @param[in] vpt to print.
4643 * @return
4644 * - >0 the number of bytes written to the out buffer.
4645 * - 0 invalid argument.
4646 * - <0 the number of bytes we would have needed to complete the print.
4647 */
4649{
4650 tmpl_attr_t *ar = NULL;
4652 fr_sbuff_t our_out = FR_SBUFF(out);
4653 fr_slen_t slen;
4654
4656
4657 /*
4658 * Only print things we can print...
4659 */
4660 switch (vpt->type) {
4662 case TMPL_TYPE_ATTR:
4663 break;
4664
4665 default:
4666 fr_assert(0);
4667 return 0;
4668 }
4669
4670 /*
4671 * Print request references
4672 */
4673 slen = tmpl_request_ref_list_print(&our_out, &vpt->data.attribute.rr);
4674 if (slen > 0) FR_SBUFF_IN_CHAR_RETURN(&our_out, '.');
4675 if (slen < 0) return slen;
4676
4677 /*
4678 *
4679 * If the leaf attribute is unknown and raw we
4680 * add the raw. prefix.
4681 *
4682 * If the leaf attribute is unknown and not raw
4683 * we add the .unknown prefix.
4684 *
4685 */
4687
4688 /*
4689 * Print attribute identifiers
4690 */
4691 ar = NULL;
4692 while ((ar = tmpl_attr_list_next(tmpl_attr(vpt), ar))) {
4693 switch(ar->type) {
4695 break;
4696
4699 {
4700 int i, depth = 0;
4701
4702 fr_assert(ar->ar_parent); /* All normal and unknown attributes must have parents */
4703
4704 fr_proto_da_stack_build_partial(&stack, ar->ar_parent, ar->ar_da);
4705
4706 /*
4707 * First component in the list has everything built
4708 */
4709 if (ar == tmpl_attr_list_head(tmpl_attr(vpt))) {
4710 depth = ar->ar_parent->depth - 1; /* Adjust for array index */
4711 /*
4712 * Everything else skips the first component
4713 */
4714 } else {
4715 depth = ar->ar_parent->depth;
4716 }
4717
4718 /*
4719 * Root attributes will be skipped by the build
4720 * function, so da[0] contains the attribute
4721 * we're looking for.
4722 */
4723 if (depth < 0) depth = 0;
4724
4725 /*
4726 * Print from our parent depth to the AR we're processing
4727 *
4728 * For refs we skip the attribute pointed to be the ref
4729 * and just print its children.
4730 *
4731 * In addition skip printing "request." in most cases.
4732 */
4733 if ((stack.da[depth] == request_attr_request) && tmpl_attr_list_next(tmpl_attr(vpt), ar) &&
4734 (ar->filter.type == TMPL_ATTR_FILTER_TYPE_NONE)) continue;
4735
4736 for (i = depth; (unsigned int)i < ar->ar_da->depth; i++) {
4737 FR_SBUFF_IN_STRCPY_RETURN(&our_out, stack.da[i]->name);
4738
4739 /*
4740 * Print intermediary separators
4741 * if necessary.
4742 */
4743 if (((unsigned int)i + 1) < ar->ar_da->depth) FR_SBUFF_IN_CHAR_RETURN(&our_out, '.');
4744 }
4745 }
4746 break;
4747
4748 /*
4749 * For unresolved attribute we print the raw identifier we
4750 * got when parsing the tmpl.
4751 */
4753 {
4754 unsigned int i, depth;
4755
4756 /*
4757 * This is the first unresolved component in a potential
4758 * chain of unresolved components. Print the path up to
4759 * the last known parent.
4760 */
4761 if (ar->ar_parent && !ar->ar_parent->flags.is_root) {
4762 fr_proto_da_stack_build_partial(&stack, ar->ar_parent, ar->ar_parent);
4763 if (ar->ar_parent->flags.is_root) {
4764 depth = 0;
4765 } else {
4766 depth = ar->ar_parent->depth - 1;
4767 }
4768
4769 for (i = depth; i < ar->ar_parent->depth; i++) {
4770 FR_SBUFF_IN_STRCPY_RETURN(&our_out, stack.da[i]->name);
4771 FR_SBUFF_IN_CHAR_RETURN(&our_out, '.');
4772 }
4773 }
4774 /*
4775 * Then print the unresolved component
4776 */
4777 FR_SBUFF_IN_BSTRCPY_BUFFER_RETURN(&our_out, ar->ar_unresolved);
4778 break;
4779 }
4780 }
4781
4782 if (ar_filter_is_none(ar)) {
4783 /* do nothing */
4784
4785 } else if (ar_filter_is_num(ar)) {
4786 switch (ar->ar_num) {
4787 case NUM_UNSPEC:
4788 break;
4789
4790 case NUM_ALL:
4791 FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "[*]");
4792 break;
4793
4794 case NUM_COUNT:
4795 FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "[#]");
4796 break;
4797
4798 case NUM_LAST:
4799 FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "[n]");
4800 break;
4801
4802 default:
4803 FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "[%i]", ar->ar_num);
4804 break;
4805 }
4806
4807 } else if (ar_filter_is_cond(ar)) {
4808 FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "[");
4809 (void) xlat_print(&our_out, ar->ar_cond, NULL);
4810 FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "]");
4811
4812 } else {
4813 fr_assert(0);
4814 }
4815
4816 if (tmpl_attr_list_next(tmpl_attr(vpt), ar)) FR_SBUFF_IN_CHAR_RETURN(&our_out, '.');
4817 }
4818 FR_SBUFF_SET_RETURN(out, &our_out);
4819}
4820
4821/** Print a #tmpl_t to a string
4822 *
4823 * This function should primarily be used for regenerating vpt->name when the contents
4824 * of the #tmpl_t is changed programmatically, or when the #tmpl_t is being serialized
4825 * in some non-standard way, i.e. as a value for a field in a database.
4826 *
4827 * This function is the direct counterpart to #tmpl_afrom_substr.
4828 *
4829 * @note Does not print flags for regular expressions, as the quoting char is needed
4830 * to separate the elements of the expression.
4831 * Call regex_flags_print to write the flags values to the output buffer.
4832 *
4833 * @param[out] out Where to write the presentation format #tmpl_t string.
4834 * @param[in] vpt to print.
4835 * @param[in] e_rules Escaping rules used to print strings.
4836 * @return
4837 * - >0 the number of bytes written to the out buffer.
4838 * - 0 invalid argument.
4839 * - <0 the number of bytes we would have needed to complete the print.
4840 */
4842 fr_sbuff_escape_rules_t const *e_rules)
4843{
4844 fr_sbuff_t our_out = FR_SBUFF(out);
4845
4847
4848 switch (vpt->type) {
4850 case TMPL_TYPE_ATTR:
4852 break;
4853
4854 case TMPL_TYPE_DATA:
4855 FR_SBUFF_RETURN(fr_value_box_print, &our_out, tmpl_value(vpt), e_rules);
4856 break;
4857
4858 case TMPL_TYPE_REGEX:
4859 FR_SBUFF_IN_BSTRNCPY_RETURN(&our_out, vpt->name, vpt->len); /* Fixme - double escapes */
4860 break;
4861
4863 FR_SBUFF_IN_ESCAPE_BUFFER_RETURN(&our_out, vpt->data.unescaped, e_rules);
4864 break;
4865
4867 case TMPL_TYPE_MAX:
4868 fr_sbuff_terminate(out);
4869 break;
4870
4871 /*
4872 * The remaining types will either
4873 * be xlat expansions, or need
4874 * resolving, in which case the
4875 * unescaped string is available
4876 * in vpt->unescaped.
4877 */
4878 default:
4879 if (tmpl_contains_xlat(vpt)) {
4880 FR_SBUFF_RETURN(xlat_print, &our_out, tmpl_xlat(vpt), e_rules);
4881 break;
4882 }
4883
4885 FR_SBUFF_IN_ESCAPE_BUFFER_RETURN(&our_out, vpt->data.unescaped, e_rules);
4886 break;
4887 }
4888
4889 fr_assert_fail("Can't print invalid tmpl type %s", tmpl_type_to_str(vpt->type));
4890
4891 /*
4892 * Ensure we do something sane for non-debug builds
4893 */
4894 fr_sbuff_terminate(out);
4895 return 0;
4896 }
4897
4898 FR_SBUFF_SET_RETURN(out, &our_out);
4899}
4900
4901/** Print a #tmpl_t to a string with quotes
4902 *
4903 * This function should be used when the tmpl is embedded in some other construct
4904 * in the server's configuration.
4905 *
4906 * It adds standard quoting around tmpl's used as operands in expressions and applies
4907 * the correct escaping rules.
4908 *
4909 * @param[out] out Where to write the presentation format #tmpl_t string.
4910 * @param[in] vpt to print.
4911 * @return
4912 * - >0 the number of bytes written to the out buffer.
4913 * - 0 invalid argument.
4914 * - <0 the number of bytes we would have needed to complete the print.
4915 */
4917{
4918 fr_sbuff_t our_out = FR_SBUFF(out);
4919
4920 char quote = fr_token_quote[vpt->quote];
4921
4922 if (quote != '\0') FR_SBUFF_IN_CHAR_RETURN(&our_out, quote);
4923 FR_SBUFF_RETURN(tmpl_print, &our_out, vpt,
4925 if (quote != '\0') FR_SBUFF_IN_CHAR_RETURN(&our_out, quote);
4926
4927 /*
4928 * Optionally print the flags
4929 */
4930 if (vpt->type & TMPL_FLAG_REGEX) FR_SBUFF_RETURN(regex_flags_print, &our_out, tmpl_regex_flags(vpt));
4931
4932 FR_SBUFF_SET_RETURN(out, &our_out);
4933}
4934/** @} */
4935
4936
4937#ifdef WITH_VERIFY_PTR
4938/** Used to check whether areas of a tmpl_t are zeroed out
4939 *
4940 * @param ptr Offset to begin checking at.
4941 * @param len How many bytes to check.
4942 * @return
4943 * - Pointer to the first non-zero byte.
4944 * - NULL if all bytes were zero.
4945 */
4946static uint8_t const *is_zeroed(uint8_t const *ptr, size_t len)
4947{
4948 size_t i;
4949
4950 for (i = 0; i < len; i++) {
4951 if (ptr[i] != 0x00) return ptr + i;
4952 }
4953
4954 return NULL;
4955}
4956
4957/** Verify that unused regions of the struct are zeroed out
4958 *
4959 */
4960#define CHECK_ZEROED(_vpt, _field) is_zeroed(((uint8_t const *)&(_vpt)->data) + sizeof((_vpt)->data._field), sizeof((_vpt)->data) - sizeof((_vpt)->data._field))
4961
4962
4963/** Print hex data
4964 *
4965 */
4966#define PRINT_NON_ZEROED(_vpt, _field, _nz_ptr) \
4967do { \
4968 DEBUG("Expected live portion %p-%p (0-%zu)", \
4969 _vpt, \
4970 (uint8_t const *)&(_vpt)->data + sizeof((_vpt)->data._field), \
4971 sizeof((_vpt)->data._field)); \
4972 DEBUG("Expected zero portion %p-%p (%zu-%zu)", \
4973 (uint8_t const *)&(_vpt)->data + sizeof((_vpt)->data._field), \
4974 (uint8_t const *)&(_vpt)->data + sizeof((_vpt)->data), \
4975 sizeof((_vpt)->data._field), sizeof((_vpt)->data)); \
4976 HEX_MARKER1((uint8_t const *)&vpt->data, sizeof(vpt->data), nz - (uint8_t const *)&vpt->data, "non-zero memory", ""); \
4977} while (0)
4978
4979
4980/** Verify the attribute reference in a tmpl_t make sense
4981 *
4982 * @note If the attribute reference is is invalid, causes the server to exit.
4983 *
4984 * @param file obtained with __FILE__.
4985 * @param line obtained with __LINE__.
4986 * @param vpt to check.
4987 */
4988void tmpl_attr_verify(char const *file, int line, tmpl_t const *vpt)
4989{
4990 tmpl_attr_t *ar = NULL;
4991 tmpl_attr_t *slow = NULL, *fast = NULL;
4992 tmpl_attr_t *seen_unknown = NULL;
4993 tmpl_attr_t *seen_unresolved = NULL;
4994
4996
4997 /*
4998 * Loop detection
4999 */
5000 while ((slow = tmpl_attr_list_next(tmpl_attr(vpt), slow)) &&
5001 (fast = tmpl_attr_list_next(tmpl_attr(vpt), fast))) {
5002
5003 /*
5004 * Advances twice as fast as slow...
5005 */
5006 fast = tmpl_attr_list_next(tmpl_attr(vpt), fast);
5007 fr_fatal_assert_msg(fast != slow,
5008 "CONSISTENCY CHECK FAILED %s[%u]: Looping reference list found. "
5009 "Fast pointer hit slow pointer at \"%s\"",
5010 file, line,
5011 slow->type == TMPL_ATTR_TYPE_UNRESOLVED ? slow->ar_unresolved :
5012 slow->da ? slow->da->name : "(null-attr)");
5013 }
5014
5015 /*
5016 * Lineage type check
5017 *
5018 * Known attribute cannot come after unresolved or unknown attributes
5019 * Unknown attributes cannot come after unresolved attributes
5020 */
5021 if (!tmpl_is_list(vpt)) while ((ar = tmpl_attr_list_next(tmpl_attr(vpt), ar))) {
5022 switch (ar->type) {
5024 if (seen_unknown) {
5025 tmpl_attr_debug(stderr, vpt);
5026 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: "
5027 "TMPL_TYPE_ATTR known attribute \"%s\" "
5028 "occurred after unknown attribute %s "
5029 "in attr ref list",
5030 file, line,
5031 ar->da->name,
5032 ar->unknown.da->name);
5033 }
5034 if (seen_unresolved) {
5035 tmpl_attr_debug(stderr, vpt);
5036 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: "
5037 "TMPL_TYPE_ATTR known attribute \"%s\" "
5038 "occurred after unresolved attribute \"%s\""
5039 "in attr ref list",
5040 file, line,
5041 ar->da->name,
5042 ar->ar_unresolved);
5043 }
5044 fr_fatal_assert_msg(ar->ar_parent,
5045 "CONSISTENCY CHECK FAILED %s[%u]: attr ref missing parent",
5046 file, line);
5047 break;
5048
5050 if (seen_unknown) {
5051 tmpl_attr_debug(stderr, vpt);
5052 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: "
5053 "TMPL_TYPE_ATTR unspecified attribute "
5054 "occurred after unknown attribute %s "
5055 "in attr ref list",
5056 file, line,
5057 ar->unknown.da->name);
5058 }
5059 if (seen_unresolved) {
5060 tmpl_attr_debug(stderr, vpt);
5061 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: "
5062 "TMPL_TYPE_ATTR unspecified attribute "
5063 "occurred after unresolved attribute \"%s\""
5064 "in attr ref list",
5065 file, line,
5066 ar->ar_unresolved);
5067 }
5068 break;
5069
5071 seen_unresolved = ar;
5072 fr_fatal_assert_msg(ar->ar_unresolved_namespace,
5073 "CONSISTENCY CHECK FAILED %s[%u]: unresolved attr ref missing namespace",
5074 file, line);
5075 break;
5076
5078 seen_unknown = ar;
5079 if (seen_unresolved) {
5080 tmpl_attr_debug(stderr, vpt);
5081 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: "
5082 "TMPL_TYPE_ATTR unknown attribute \"%s\" "
5083 "occurred after unresolved attribute %s "
5084 "in attr ref list",
5085 file, line, ar->da->name,
5086 ar->ar_unresolved);
5087 }
5088 break;
5089 }
5090 }
5091}
5092
5093/** Verify fields of a tmpl_t make sense
5094 *
5095 * @note If the #tmpl_t is invalid, causes the server to exit.
5096 *
5097 * @param file obtained with __FILE__.
5098 * @param line obtained with __LINE__.
5099 * @param vpt to check.
5100 */
5101void tmpl_verify(char const *file, int line, tmpl_t const *vpt)
5102{
5103 uint8_t const *nz;
5104
5105 fr_assert(vpt);
5106
5108 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: tmpl_t type was "
5109 "TMPL_TYPE_UNINITIALISED (uninitialised)", file, line);
5110 }
5111
5112 if (vpt->type >= TMPL_TYPE_MAX) {
5113 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: tmpl_t type was %i "
5114 "(outside range of tmpl_type_table)", file, line, vpt->type);
5115 }
5116
5117 if (!vpt->name && (vpt->quote != T_INVALID)) {
5118 char quote = vpt->quote >= T_TOKEN_LAST ? '?' : fr_token_quote[vpt->quote];
5119
5120 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: Quote type '%c' (%i) was set for NULL name",
5121 file, line, quote, vpt->quote);
5122 }
5123
5124 if (vpt->name && (vpt->quote == T_INVALID)) {
5125 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: No quoting type was set for name \"%.*s\"",
5126 file, line, (int)vpt->len, vpt->name);
5127 }
5128
5129 /*
5130 * Do a memcmp of the bytes after where the space allocated for
5131 * the union member should have ended and the end of the union.
5132 * These should always be zero if the union has been initialised
5133 * properly.
5134 *
5135 * If they're still all zero, do TMPL_TYPE specific checks.
5136 */
5137 switch (vpt->type) {
5139 if (!vpt->data.unescaped) {
5140 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_DATA_UNRESOLVED "
5141 "unescaped field is NULL", file, line);
5142 }
5143 break;
5144
5146 if (!vpt->data.xlat.ex) {
5147 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_XLAT "
5148 "has a NULL xlat.ex field", file, line);
5149
5150 }
5151
5152 if (!xlat_needs_resolving(vpt->data.xlat.ex)) {
5153 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_XLAT_UNRESOLVED "
5154 "does not have 'needs resolving' flag set", file, line);
5155 }
5156 break;
5157
5158 case TMPL_TYPE_XLAT:
5159 if (!vpt->data.xlat.ex) {
5160 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_XLAT "
5161 "has a NULL xlat.ex field", file, line);
5162
5163 }
5164 break;
5165
5166/* @todo When regexes get converted to xlat the flags field of the regex union is used
5167 case TMPL_TYPE_XLAT_UNRESOLVED:
5168 if (is_zeroed((uint8_t const *)&vpt->data, sizeof(vpt->data))) {
5169 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_XLAT_UNRESOLVED "
5170 "has non-zero bytes in its data union", file, line);
5171 }
5172 break;
5173
5174 case TMPL_TYPE_XLAT:
5175 if (CHECK_ZEROED(vpt, xlat)) {
5176 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_XLAT "
5177 "has non-zero bytes after the data.xlat pointer in the union", file, line);
5178 }
5179 break;
5180*/
5181
5182 case TMPL_TYPE_EXEC:
5184 /* tmpl_xlat(vpt) can be initialized */
5185 break;
5186
5188 if ((tmpl_attr_list_num_elements(tmpl_attr(vpt)) > 0) &&
5189 ((tmpl_attr_t *)tmpl_attr_list_tail(tmpl_attr(vpt)))->da) {
5190#ifndef NDEBUG
5191 tmpl_attr_debug(stderr, vpt);
5192#endif
5193 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_ATTR_UNRESOLVED contains %u "
5194 "references", file, line, tmpl_attr_list_num_elements(tmpl_attr(vpt)));
5195 }
5196 break;
5197
5198 case TMPL_TYPE_ATTR:
5199 if ((nz = CHECK_ZEROED(vpt, attribute))) {
5200 PRINT_NON_ZEROED(vpt, attribute, nz);
5201 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_ATTR "
5202 "has non-zero bytes after the data.attribute struct in the union",
5203 file, line);
5204 }
5205
5207 fr_assert(vpt->rules.cast == FR_TYPE_NULL);
5208 break;
5209 }
5210
5213 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_ATTR "
5214 "da is marked as unknown, but address is not equal to the template's "
5215 "unknown da pointer", file, line);
5216 }
5217 /*
5218 * Raw attributes may not have been added to the dictionary yet
5219 */
5220 } else {
5221 fr_dict_attr_t const *da;
5222 fr_dict_t const *dict;
5223
5224 /*
5225 * Attribute may be present with multiple names
5226 */
5228 if (!dict) {
5229 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_ATTR "
5230 "attribute \"%s\" (%s) not rooted in a dictionary",
5233 }
5234
5235 da = tmpl_attr_tail_da(vpt);
5236 if (!tmpl_attr_tail_is_raw(vpt) && (da != tmpl_attr_tail_da(vpt))) {
5237 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_ATTR "
5238 "dictionary pointer %p \"%s\" (%s) "
5239 "and global dictionary pointer %p \"%s\" (%s) differ",
5240 file, line,
5243 da, da->name,
5244 fr_type_to_str(da->type));
5245 }
5246
5247 tmpl_attr_verify(file, line, vpt);
5248 }
5249 break;
5250
5251 case TMPL_TYPE_DATA:
5252 if ((nz = CHECK_ZEROED(vpt, literal))) {
5253 PRINT_NON_ZEROED(vpt, literal, nz);
5254 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_DATA "
5255 "has non-zero bytes after the data.literal struct in the union",
5256 file, line);
5257 }
5258
5260 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_DATA type was "
5261 "FR_TYPE_NULL (uninitialised)", file, line);
5262 }
5263
5265 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_DATA type was "
5266 "%i (outside the range of fr_type_ts)", file, line, tmpl_value_type(vpt));
5267 }
5268 /*
5269 * Unlike fr_pair_ts we can't guarantee that fr_pair_t_TMPL buffers will
5270 * be talloced. They may be allocated on the stack or in global variables.
5271 */
5272 switch (tmpl_value_type(vpt)) {
5273 case FR_TYPE_STRING:
5275 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_DATA char buffer not \\0 "
5276 "terminated", file, line);
5277 }
5278 break;
5279
5280 case FR_TYPE_STRUCTURAL:
5281 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_DATA is of type TLV",
5282 file, line);
5283
5284 default:
5285 break;
5286 }
5287
5288 break;
5289
5293#ifndef HAVE_REGEX
5294 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_REGEX_XLAT_UNRESOLVED - No regex support",
5295 file, line);
5296#endif
5297 break;
5298
5299 case TMPL_TYPE_REGEX:
5300#ifdef HAVE_REGEX
5301 if (tmpl_regex(vpt) == NULL) {
5302 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_REGEX "
5303 "reg.ex field was NULL", file, line);
5304 }
5305#else
5306 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_REGEX - No regex support",
5307 file, line);
5308#endif
5309 break;
5310
5312 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_UNINITIALISED", file, line);
5313
5314 case TMPL_TYPE_MAX:
5315 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_MAX", file, line);
5316 }
5317}
5318#endif
5319
5320static const bool array_terminal[UINT8_MAX + 1] = {
5321 [ ']' ] = true,
5322};
5323
5324#define return_P(_x) fr_strerror_const(_x);goto return_p
5325
5326/** Preparse a string in preparation for passing it to tmpl_afrom_substr()
5327 *
5328 * Note that the input string is not modified, which means that the
5329 * tmpl_afrom_substr() function MUST un-escape it.
5330 *
5331 * The caller should pass 'out' and 'outlen' to tmpl_afrom_substr()
5332 * as 'in' and 'inlen'. The caller should also pass 'type'.
5333 * The caller should also pass do_unescape=true.
5334 *
5335 * @param[out] out start of the string to parse
5336 * @param[out] outlen length of the string to parse
5337 * @param in where we start looking for the string
5338 * @param inlen length of the input string
5339 * @param[out] type token type of the string.
5340 * @return
5341 * - > 0, amount of parsed string to skip, to get to the next token
5342 * - <=0, -offset in 'start' where the parse error was located
5343 */
5344ssize_t tmpl_preparse(char const **out, size_t *outlen, char const *in, size_t inlen,
5346{
5347 char const *p = in, *end = in + inlen;
5348 char quote;
5349 char close;
5350 int depth;
5351 bool triple;
5352
5353 *type = T_INVALID;
5354
5355 while (isspace((uint8_t) *p) && (p < end)) p++;
5356 if (p >= end) return p - in;
5357
5358 switch (*p) {
5359 /*
5360 * Allow bare xlat's
5361 */
5362 case '%':
5363 if (p[1] != '{') {
5364 char const *q;
5365
5366 q = p + 1;
5367
5368 /*
5369 * Function syntax: %foo(...)
5370 */
5371 while ((q < end) && (isalnum((int) *q) || (*q == '.') || (*q == '_') || (*q == '-'))) {
5372 q++;
5373 }
5374
5375 if (*q != '(') {
5376 p++;
5377 fr_strerror_const("Invalid character after '%'");
5378 return_p:
5379 return -(p - in);
5380 }
5381
5382 /*
5383 * Return the whole %foo(...) string.
5384 */
5385 *out = p;
5386 if (*type == T_INVALID) *type = T_BARE_WORD;
5387 close = ')';
5388
5389 p = q + 1;
5390 depth = 1;
5391 goto loop;
5392 }
5393
5394 /*
5395 * For now, %{...} is treated as a double-quoted
5396 * string. Once we clean other things up, the
5397 * xlats will be treated as strongly typed values
5398 * / lists on their own.
5399 */
5400 if (*type == T_INVALID) *type = T_BARE_WORD;
5401 depth = 0;
5402 close = '}';
5403
5404 /*
5405 * Xlat's are quoted by %{...} / %(...) nesting, not by
5406 * escapes, so we need to do special escaping.
5407 */
5408 *out = p;
5409 loop:
5410 while (*p) {
5411 /*
5412 * End of expansion. Return the entire
5413 * expansion, including the enclosing %{}
5414 * characters.
5415 */
5416 if ((*p == '}') || (*p == ')')) {
5417 bool match = (*p == close);
5418
5419 p++;
5420 depth--;
5421
5422 if (depth == 0) {
5423 if (!match) break;
5424
5425 *outlen = p - (*out);
5426 return p - in;
5427 }
5428 continue;
5429 }
5430
5431 if (*p == '\\') {
5432 p++;
5433 if (!p[1]) {
5434 return_P("End of string after escape");
5435 }
5436
5437 p++;
5438 continue;
5439 }
5440
5441 if ((p[0] == '%') && ((p[1] == '{') || (p[1] == '('))) {
5442 if (!p[2]) {
5443 return_P("End of string after expansion");
5444 }
5445
5446 p += 2;
5447 depth++;
5448 continue;
5449 }
5450
5451 /*
5452 * Allow (...) and {...}
5453 */
5454 if ((*p == '{') || (*p == '(')) {
5455 p++;
5456 depth++;
5457 continue;
5458 }
5459
5460 p++;
5461 }
5462
5463 /*
5464 * End of input without end of string.
5465 * Point the error to the start of the string.
5466 */
5467 p = *out;
5468 return_P("Unterminated expansion");
5469
5470 case '/':
5471 goto bare_word;
5472
5473 case '\'':
5474 quote = *(p++);
5476 goto skip_string;
5477
5478 case '`':
5479 quote = *(p++);
5481 goto skip_string;
5482
5483 case '"':
5484 quote = *(p++);
5486
5487 /*
5488 * We're not trying to do a *correct* parsing of
5489 * every string here. We're trying to do a
5490 * simple parse that isn't wrong. We therefore
5491 * accept most anything that's vaguely well
5492 * formed, and rely on the next stage to do a
5493 * more rigorous check.
5494 */
5495 skip_string:
5496 if ((inlen > 3) && (p[0] == quote) && (p[1] == quote)) {
5497 triple = true;
5498 p += 2;
5499 } else {
5500 triple = false;
5501 }
5502 *out = p;
5503
5504 while (*p) {
5505 if (p >= end) goto unterminated;
5506
5507 /*
5508 * End of string. Tell the caller the
5509 * length of the data inside of the
5510 * string, and return the number of
5511 * characters to skip.
5512 */
5513 if (*p == quote) {
5514 if (!triple) {
5515 *outlen = p - (*out);
5516 p++;
5517 return p - in;
5518
5519 }
5520
5521 if (((end - p) >= 3) && (p[1] == quote) && (p[2] == quote)) {
5522 *outlen = p - (*out);
5523 p += 3;
5524 return p - in;
5525 }
5526
5527 p++;
5528 continue;
5529 }
5530
5531 if (*p == '\\') {
5532 p++;
5533 if (!p[1]) {
5534 return_P("End of string after escape");
5535 }
5536 }
5537 p++;
5538 }
5539
5540 /*
5541 * End of input without end of string.
5542 * Point the error to the start of the string.
5543 */
5544 unterminated:
5545 p = *out;
5546 return_P("Unterminated string");
5547
5548 case '&':
5549 *out = p; /* the output string starts with '&' */
5550 p++;
5551 quote = '[';
5552 goto skip_word;
5553
5554 default:
5555 bare_word:
5556 *out = p;
5557 quote = '['; /* foo[1] is OK */
5558
5559 skip_word:
5560 *type = T_BARE_WORD;
5561 depth = 0;
5562
5563 /*
5564 * Allow *most* things. But stop on spaces and special characters.
5565 */
5566 while (*p) {
5567 if (isspace((uint8_t) *p)) {
5568 break;
5569 }
5570
5571 if (*p == '$') {
5572 if (p[1] == '{') {
5573 p += 2;
5574 depth++;
5575 continue;
5576
5577 } else if ((p[1] == 'E') &&
5578 (p[2] == 'N') &&
5579 (p[3] == 'V') &&
5580 (p[4] == '{')) {
5581 p += 5;
5582 depth++;
5583 continue;
5584
5585 } else {
5586 /*
5587 * Bare '$' is wrong...
5588 */
5589 break;
5590 }
5591 }
5592
5593 if (*p == '%') {
5594 if (p[1] == '{') {
5595 p += 2;
5596 depth++;
5597 continue;
5598 }
5599
5600 p++;
5601 continue;
5602 }
5603
5604 /*
5605 * If we're inside of a ${...} expansion,
5606 * then allow everything until the
5607 * closing '}'. This means that we can
5608 * do ${foo[bar].baz}, among other
5609 * thingds.
5610 */
5611 if (depth > 0) {
5612 if (*p == '}') {
5613 depth--;
5614 }
5615
5616 p++;
5617 continue;
5618 }
5619
5620 /*
5621 * '-' is special. We allow it for
5622 * attribute names, BUT it's a
5623 * terminating token if the NEXT
5624 * character is '='.
5625 *
5626 * We have the same criteria for IPv6
5627 * addresses and tagged attributes. ':'
5628 * is allowed, but ':=' is a breaking
5629 * token.
5630 */
5631 if ((*p == '-') || (*p == ':')) {
5632 if (p[1] == '=') break;
5633 p++;
5634 continue;
5635 }
5636
5637 /*
5638 * Allowed in attribute names, and/or
5639 * host names and IP addresses, and IPv6 addresses.
5640 */
5641 if ((*p == '.') || (*p == '/') || (*p == '_') || (*p == '*') ||
5642 (*p == ']') || (*p == '@')) {
5643 p++;
5644 continue;
5645 }
5646
5647 /*
5648 * [...] is an IPv6 address.
5649 */
5650 if ((p == in) && (*p == '[')) {
5651 p++;
5652 continue;
5653 }
5654
5655 /*
5656 * Allow letters and numbers
5657 */
5658 if (((*p >= 'a') && (*p <= 'z')) ||
5659 ((*p >= 'A') && (*p <= 'Z')) ||
5660 ((*p >= '0') && (*p <= '9'))) {
5661 p++;
5662 continue;
5663 }
5664
5665 /*
5666 * Allow UTF-8 sequences.
5667 */
5668 if (*(uint8_t const *)p > 0x80) {
5669 p++;
5670 continue;
5671 }
5672
5673 /*
5674 * If it's an attribute reference, allow
5675 * a few more things inside of a "[...]"
5676 * block.
5677 */
5678 if (*p == '[') {
5679 if (quote != '[') {
5680 return_P("Invalid location for '['");
5681 }
5682
5683 p++;
5684
5685 /*
5686 * Allow [#], etc. But stop
5687 * immediately after the ']'.
5688 */
5689 if ((*p == '#') || (*p == '*') || (*p == 'n')) {
5690 p++;
5691
5692 } else if (*p == '(') {
5693 ssize_t slen;
5694 bool eol = false;
5695
5696 slen = fr_skip_condition(p, end, array_terminal, &eol);
5697 if (slen < 0) {
5698 p += -slen;
5699 return -(p - in);
5700 }
5701 p += slen;
5702 continue;
5703
5704 } else {
5705 /*
5706 * Allow numbers as array indexes
5707 */
5708 while ((*p >= '0') && (*p <= '9')) {
5709 p++;
5710 }
5711
5712 if (*p != ']') {
5713 return_P("Array index is not an integer");
5714 }
5715 }
5716
5717 if (*p == ']') {
5718 p++;
5719 continue;
5720 }
5721 }
5722
5723 /*
5724 * Everything else is a breaking token
5725 */
5726 break;
5727 }
5728
5729 /*
5730 * Give some slightly better error messages.
5731 */
5732 if (*p == '\\') {
5733 return_P("Unexpected escape");
5734 }
5735
5736 if ((*p == '"') || (*p == '\'') || (*p == '`')) {
5737 return_P("Unexpected start of string");
5738 }
5739
5740 if (p == *out) {
5741 return_P("Empty string is invalid");
5742 }
5743
5744 *outlen = p - (*out);
5745 break;
5746 }
5747
5748 return p - in;
5749}
5750
5751/** Return whether or not async is required for this tmpl.
5752 *
5753 * If the tmpl is needs_async, then it is async
5754 * If the tmpl is not needs_async, then it will not yield
5755 *
5756 * If the tmpl yields, then async is required.
5757 */
5759{
5760 switch (vpt->type) {
5761 case TMPL_TYPE_EXEC: /* we don't have "exec no-wait" here */
5762 case TMPL_TYPE_XLAT_UNRESOLVED: /* we have no idea, so be safe */
5763#ifndef HAVE_REGEX
5765#endif
5766 return true;
5767
5768#ifndef HAVE_REGEX
5770#endif
5771 case TMPL_TYPE_XLAT: /* synchronous xlats use unlang_interpret_synchronous() */
5772 default:
5773 return false;
5774 }
5775}
5776
5777/** Initialize a set of rules from a parent set of rules, and a parsed tmpl_t
5778 *
5779 */
5781{
5782 fr_dict_attr_t const *da;
5783 fr_dict_attr_t const *ref;
5784 fr_dict_t const *dict, *internal;
5785
5786 *out = *parent;
5787 /* don't set ->parent=parent, that is only for switching subrequest, etc. */
5788
5789 if (!tmpl_is_attr(vpt)) return;
5790
5791 da = tmpl_attr_tail_da(vpt);
5792
5793 /*
5794 * The input tmpl is a leaf. We must parse the child as
5795 * a normal attribute reference (as with the parent tmpl).
5796 */
5797 if (!fr_type_structural[da->type]) {
5798 return;
5799 }
5800
5801 if (vpt->rules.attr.request_def) {
5802 tmpl_request_ref_list_acopy(ctx, &out->attr.request_def, vpt->rules.attr.request_def);
5803 }
5804 out->attr.list_def = tmpl_list(vpt);
5805
5806 /*
5807 * Parse the child attributes in the context of the parent struct / tlv / whatever.
5808 */
5809 if (da->type != FR_TYPE_GROUP) {
5810 out->attr.dict_def = fr_dict_by_da(da);
5811 out->attr.namespace = da;
5812 return;
5813 }
5814
5815 ref = fr_dict_attr_ref(da);
5816 dict = fr_dict_by_da(ref);
5817 internal = fr_dict_internal();
5818
5819 /*
5820 * Groups MAY change dictionaries. If so, then swap the dictionary and the parent.
5821 */
5822 if ((dict != internal) && (dict != out->attr.dict_def)) {
5823 out->attr.dict_def = dict;
5824 out->attr.namespace = ref;
5825 }
5826
5827 /*
5828 * Otherwise the reference is swapping FROM a protocol
5829 * dictionary TO the internal dictionary, and TO an
5830 * internal group. We fall back to leaving well enough
5831 * alone, and leave things as-is. This allows internal
5832 * grouping attributes to appear anywhere.
5833 */
5834}
5835
5836static void tmpl_attr_rules_debug(tmpl_attr_rules_t const *at_rules)
5837{
5838 FR_FAULT_LOG("\tdict_def = %s", at_rules->dict_def ? fr_dict_root(at_rules->dict_def)->name : "");
5839 FR_FAULT_LOG("\tnamespace = %s", at_rules->namespace ? at_rules->namespace->name : "");
5840
5841 FR_FAULT_LOG("\tlist_def = %s", at_rules->list_def ? at_rules->list_def->name : "");
5842
5843 FR_FAULT_LOG("\tallow_unknown = %u", at_rules->allow_unknown);
5844 FR_FAULT_LOG("\tallow_unresolved = %u", at_rules->allow_unresolved);
5845 FR_FAULT_LOG("\tallow_wildcard = %u", at_rules->allow_wildcard);
5846 FR_FAULT_LOG("\tallow_foreign = %u", at_rules->allow_foreign);
5847 FR_FAULT_LOG("\tdisallow_filters = %u", at_rules->disallow_filters);
5848}
5849
5850
5852{
5853 FR_FAULT_LOG("\tparent = %p", rules->parent);
5854 FR_FAULT_LOG(" attr {");
5855 tmpl_attr_rules_debug(&rules->attr);
5856 FR_FAULT_LOG(" }");
5857 FR_FAULT_LOG("\tenumv = %s", rules->enumv ? rules->enumv->name : "");
5858 FR_FAULT_LOG("\tcast = %s", fr_type_to_str(rules->cast));
5859 FR_FAULT_LOG("\tat_runtime = %u", rules->at_runtime);
5860 FR_FAULT_LOG("\tliterals_safe_for = %lx", rules->literals_safe_for);
5861
5862}
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:183
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:131
#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:202
#define fr_assert_fail(_msg,...)
Calls panic_action ifndef NDEBUG, else logs error.
Definition debug.h:208
#define FR_FAULT_LOG(_fmt,...)
Definition debug.h:50
#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:176
#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:101
fr_slen_t fr_dict_enum_name_from_substr(fr_sbuff_t *out, fr_sbuff_parse_error_t *err, fr_sbuff_t *in, fr_sbuff_term_t const *tt)
Extract an enumeration name from a string.
Definition dict_util.c:3652
fr_dict_t * fr_dict_unconst(fr_dict_t const *dict)
Coerce to non-const.
Definition dict_util.c:4724
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:2702
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:2133
static fr_slen_t err
Definition dict.h:861
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:598
bool const fr_dict_attr_allowed_chars[UINT8_MAX+1]
Characters allowed in a single dictionary attribute name.
Definition dict_util.c:64
static fr_dict_attr_t * fr_dict_attr_unknown_copy(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
Definition dict.h:578
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:3359
fr_dict_attr_t * fr_dict_attr_unconst(fr_dict_attr_t const *da)
Coerce to non-const.
Definition dict_util.c:4736
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:605
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition dict_util.c:2496
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:250
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:4749
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:3071
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:3100
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:513
fr_dict_attr_err_t
Errors returned by attribute lookup functions.
Definition dict.h:309
@ FR_DICT_ATTR_OK
No error.
Definition dict.h:310
@ FR_DICT_ATTR_NOT_DESCENDENT
Attribute is not a descendent of the parent attribute.
Definition dict.h:316
@ FR_DICT_ATTR_NO_CHILDREN
Child lookup in attribute with no children.
Definition dict.h:320
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:1790
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:3424
#define fr_dict_attr_is_key_field(_da)
Definition dict.h:169
fr_dict_enum_value_t const * fr_dict_enum_by_name(fr_dict_attr_t const *da, char const *name, ssize_t len)
Definition dict_util.c:3532
static fr_slen_t in
Definition dict.h:861
#define FR_DICT_ATTR_MAX_NAME_LEN
Maximum length of a attribute name.
Definition dict.h:493
Values of the encryption flags.
Value of an enumerated attribute.
Definition dict.h:246
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:185
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:159
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_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_INT32
32 Bit signed integer.
@ 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:1805
bool const sbuff_char_class_hex[UINT8_MAX+1]
Definition sbuff.c:97
bool fr_sbuff_is_terminal(fr_sbuff_t *in, fr_sbuff_term_t const *tt)
Efficient terminal string search.
Definition sbuff.c:2180
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:1771
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:1880
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:2116
#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_literal(_sbuff, _str)
#define FR_SBUFF_IN_STRCPY_RETURN(...)
#define FR_SBUFF_TALLOC_THREAD_LOCAL(_out, _init, _max)
#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
ssize_t fr_skip_condition(char const *start, char const *end, bool const terminal[static UINT8_MAX+1], bool *eol)
Skip a conditional expression.
Definition skip.c:286
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:558
char * talloc_bstrndup(TALLOC_CTX *ctx, char const *in, size_t inlen)
Binary safe strndup function.
Definition talloc.c:586
char * talloc_typed_strdup(TALLOC_CTX *ctx, char const *p)
Call talloc_strdup, setting the type on the new chunk correctly.
Definition talloc.c:467
#define talloc_get_type_abort_const
Definition talloc.h:287
static int talloc_const_free(void const *ptr)
Free const'd memory.
Definition talloc.h:229
#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.
void tmpl_attr_ref_debug(FILE *fp, const tmpl_attr_t *ar, int i)
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.
void tmpl_attr_debug(FILE *fp, tmpl_t const *vpt)
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.
void tmpl_attr_ref_list_debug(FILE *fp, FR_DLIST_HEAD(tmpl_attr_list) const *ar_head)
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_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 const bool array_terminal[UINT8_MAX+1]
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_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)
void tmpl_debug(FILE *fp, tmpl_t const *vpt)
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
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.
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:67
const char fr_token_quote[T_TOKEN_LAST]
Convert tokens back to a quoting character.
Definition token.c:157
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:3163
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:420
static fr_slen_t xlat_aprint(TALLOC_CTX *ctx, char **out, xlat_exp_head_t const *head, fr_sbuff_escape_rules_t const *e_rules) 1(xlat_print
bool xlat_to_string(TALLOC_CTX *ctx, char **str, xlat_exp_head_t **head)
Convert an xlat node to an unescaped literal string and free the original node.
#define xlat_copy(_ctx, _out, _in)
Definition xlat.h:458
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_argv(TALLOC_CTX *ctx, xlat_exp_head_t **head, fr_sbuff_t *in, xlat_arg_parser_t const *xlat_args, 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.
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:3135
static fr_slen_t parent
Definition pair.h:841
Structure for holding the stack of dictionary attributes being encoded.
Definition proto.h:55
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:294
bool const fr_type_numeric[FR_TYPE_MAX+1]
Definition types.c:184
bool const fr_type_structural[FR_TYPE_MAX+1]
Definition types.c:194
#define FR_TYPE_STRUCTURAL_EXCEPT_GROUP
Definition types.h:316
#define fr_type_is_non_leaf(_x)
Definition types.h:395
#define fr_type_is_octets(_x)
Definition types.h:350
#define fr_type_is_structural(_x)
Definition types.h:393
#define fr_type_is_string(_x)
Definition types.h:349
#define FR_TYPE_STRUCTURAL
Definition types.h:317
#define fr_type_is_null(_x)
Definition types.h:348
#define fr_type_is_leaf(_x)
Definition types.h:394
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
Definition types.h:455
#define FR_TYPE_LEAF
Definition types.h:318
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:5784
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:4148
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:3976
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:4910
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:4272
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:5747
fr_sbuff_escape_rules_t * fr_value_escape_by_quote[T_TOKEN_LAST]
Definition value.c:447
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:4301
void fr_value_box_clear(fr_value_box_t *data)
Clear/free any existing value and metadata.
Definition value.c:4131
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:5110
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:4830
#define fr_value_box_mark_safe_for(_box, _safe_for)
Definition value.h:1074
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:1291
#define fr_box_strvalue_len(_val, _len)
Definition value.h:308
static size_t char fr_sbuff_t size_t inlen
Definition value.h:1023
#define vb_strvalue
Definition value.h:259
int nonnull(2, 5))
#define fr_value_box_init(_vb, _type, _enumv, _tainted)
Initialise a fr_value_box_t.
Definition value.h:609
static size_t char ** out
Definition value.h:1023
#define xlat_exp_head_alloc(_ctx)
Definition xlat_priv.h:273
@ 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:209
An xlat expansion node.
Definition xlat_priv.h:148