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