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: c77fd0b4cd1d535e95730ed936b55147abc5d83c $
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: c77fd0b4cd1d535e95730ed936b55147abc5d83c $")
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 int attr_to_raw(tmpl_t *vpt, tmpl_attr_t *ref) CC_HINT(nonnull,warn_unused_result);
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\n", ar_is_unknown(ar) ? "yes" : "no");
228 if (ar->ar_parent) fprintf(fp, "\t parent : %s (%p)\n", 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\n", ar->ar_parent->name);
245 if (ar->ar_unresolved_namespace) fprintf(fp, "\t namespace : %s\n", ar->ar_unresolved_namespace->name);
246 break;
247
248 default:
249 fprintf(fp, "\t[%u] Bad type %s(%u)\n",
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 */
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
1868 /*
1869 * Fatal errors related to nesting...
1870 */
1871 switch (dict_err) {
1873 fr_assert(our_parent != NULL);
1874 if (our_parent->flags.is_unknown) break;
1875 goto error;
1876
1878 goto error;
1879
1880 default:
1881 if (!da) break;
1882
1883 /*
1884 * The named component was a known attribute
1885 * so record it as a normal attribute
1886 * reference.
1887 */
1888 fr_assert(our_parent != NULL);
1889
1890 /*
1891 * We had an alias in the same namespace,
1892 * go add more things in.
1893 */
1894 if (da->parent != our_parent) {
1895 fr_assert(namespace == our_parent);
1896 tmpl_attr_ref_fixup(ctx, vpt, da->parent, our_parent);
1897 }
1898
1899 goto alloc_ar;
1900 }
1901
1902 /*
1903 * At this point we haven't found a known attribute. What remains MUST be an OID component, OR an
1904 * unresolved attribute.
1905 *
1906 * The default is to parse the OIDs in the current namespace. If there is none, then we parse
1907 * the OIDs and unresolved attributes in the dict_def. And if that doesn't exist, in the
1908 * internal dictionaries.
1909 *
1910 * Note that we do NOT allow unknown attributes in the internal dictionary. Those attributes are
1911 * generally just DEFINEs, and their numbers have no meaning.
1912 */
1913 if (!namespace) {
1914 if (at_rules->dict_def) {
1915 our_parent = namespace = fr_dict_root(at_rules->dict_def);
1916 } else {
1917 our_parent = namespace = fr_dict_root(fr_dict_internal());
1918 }
1919 }
1920
1921 fr_assert(our_parent != NULL);
1922 fr_assert(namespace != NULL);
1923
1924 /*
1925 * See if the ref begins with an unsigned integer
1926 * if it does it's probably an OID component
1927 *
1928 * .<oid>
1929 */
1930 if (fr_sbuff_out(NULL, &oid, name) > 0) {
1931 if (!at_rules->allow_oid) {
1932 uint8_t c = fr_sbuff_char(name, '\0');
1933
1934 /*
1935 * This extra test is to give the user better errors. The string "3G" is parsed
1936 * as "3", and then an error of "what the heck do you mean by G?"
1937 *
1938 * In contrast, the string "3." is parsed as "3", and then "nope, that's not an attribute reference".
1939 */
1940 if (c != '.') {
1941 fr_strerror_const("Unexpected text after attribute reference");
1943 } else {
1944 fr_strerror_const("Numerical attribute references are not allowed here");
1946
1947 fr_sbuff_set(name, &m_s);
1948 }
1949 goto error;
1950 }
1951
1952 our_parent = namespace = fr_dict_unlocal(namespace);
1953
1954 fr_assert(ar == NULL);
1955
1956 fr_strerror_clear(); /* Clear out any existing errors */
1957
1958 if (fr_dict_by_da(namespace) == fr_dict_internal()) goto disallow_unknown;
1959
1960 /*
1961 * The OID component was a known attribute
1962 * so record it as a normal attribute
1963 * reference.
1964 */
1965 da = fr_dict_attr_child_by_num(namespace, oid);
1966 if (da) {
1967 fr_assert(da->parent == our_parent);
1968 goto alloc_ar;
1969 }
1970
1971 if (!at_rules->allow_unknown) {
1972 disallow_unknown:
1973 fr_strerror_const("Unknown attributes not allowed here");
1975 fr_sbuff_set(name, &m_s);
1976 goto error;
1977 }
1978
1979 /*
1980 * If it's numeric and not a known attribute
1981 * then we create an unknown attribute with
1982 * the specified attribute number.
1983 */
1984 MEM(ar = talloc(ctx, tmpl_attr_t));
1985
1986 /*
1987 * VSAs have VENDORs as children. All others are just normal things.
1988 */
1989 switch (namespace->type) {
1990 case FR_TYPE_VSA:
1991 da = fr_dict_attr_unknown_vendor_afrom_num(ar, namespace, oid);
1992 break;
1993
1994 default:
1995 da = fr_dict_attr_unknown_raw_afrom_num(ar, namespace, oid);
1996 break;
1997 }
1998
1999 if (!da) {
2000 if (err) *err = TMPL_ATTR_ERROR_UNKNOWN_NOT_ALLOWED; /* strerror set by dict function */
2001 goto error;
2002 }
2003
2004 *ar = (tmpl_attr_t){
2005 .ar_num = NUM_UNSPEC,
2006 .ar_type = TMPL_ATTR_TYPE_UNKNOWN,
2007 .ar_unknown = UNCONST(fr_dict_attr_t *, da),
2008 .ar_da = da,
2009 .ar_parent = our_parent,
2010 };
2011 goto do_suffix;
2012 }
2013
2014 /*
2015 * Can't parse it as an attribute, might be a literal string
2016 * let the caller decide.
2017 *
2018 * Don't alter the fr_strerror buffer, may contain useful
2019 * errors from the dictionary code.
2020 */
2021 if (!at_rules->allow_unresolved && !(at_rules->allow_wildcard && fr_sbuff_is_char(name, '['))) {
2022 fr_strerror_const_push("Unresolved attributes are not allowed here");
2024 fr_sbuff_set(name, &m_s);
2025 goto error;
2026 }
2027
2028 fr_sbuff_marker_release(&m_s);
2029
2030 /*
2031 * Once we hit one unresolved attribute we have to treat
2032 * the rest of the components are unresolved as well.
2033 */
2034 return tmpl_attr_ref_afrom_unresolved_substr(ctx, err, vpt, our_parent, namespace, name, at_rules);
2035
2036alloc_ar:
2037 /*
2038 * We have a da, remove any of the errors recorded from failed
2039 * searches to find the attribute to avoid misleading messages
2040 * if something else fails.
2041 */
2043
2044 MEM(ar = talloc(ctx, tmpl_attr_t));
2045 *ar = (tmpl_attr_t) {
2046 .ar_num = NUM_UNSPEC,
2047 .ar_type = TMPL_ATTR_TYPE_NORMAL,
2048 .ar_da = da,
2049 .ar_parent = da->parent,
2050 };
2051
2052do_suffix:
2053 /*
2054 * Parse the attribute reference filter
2055 *
2056 * Error out immediately if the filter is bad
2057 * otherwise determine whether to keep the
2058 * attribute reference or omit it based on:
2059 *
2060 * - Whether there was a filter present.
2061 * - The type of attribute.
2062 * - If this is the leaf attribute reference.
2063 */
2064 if (tmpl_attr_parse_filter(err, ar, name, at_rules) < 0) goto error;
2065
2066 /*
2067 * Local variables are always unitary.
2068 *
2069 * [0] is allowed, as is [n], [*], and [#]. But [1], etc. aren't allowed.
2070 */
2071 if (da->flags.local && (ar->ar_num > 0)) {
2072 fr_strerror_printf("Invalid array reference for local variable");
2074 fr_sbuff_set(name, &m_s);
2075 goto error;
2076 }
2077
2078 /*
2079 * At the end of the attribute reference. If there's a
2080 * trailing '.' then there's another attribute reference
2081 * we need to parse, otherwise we're done.
2082 */
2083 fr_sbuff_marker_release(&m_s);
2084 fr_sbuff_marker(&m_s, name);
2085
2086 if (fr_sbuff_next_if_char(name, '.')) {
2087 fr_dict_attr_t const *ref;
2088
2089 switch (da->type) {
2090 case FR_TYPE_GROUP:
2091 ref = fr_dict_attr_ref(da);
2092
2093 /*
2094 * If the ref is outside of the internal namespace, then we use it.
2095 *
2096 * If the ref is inside of the internal namespace (e.g. "request"), then we do
2097 * something else.
2098 *
2099 * If we were given a root dictionary on input, use that. We have to follow this
2100 * dictionary because this function calls itself recursively, WITHOUT updating
2101 * "dict_def" in the attr_rules. So the dict-def there is whatever got passed
2102 * into tmpl_afrom_attr_substr(), BEFORE the "parent.parent.parent..." parsing.
2103 * Which means that in many cases, the "dict_def" is completely irrelevant.
2104 *
2105 * If there is no parent on input, then we just use dict_def.
2106 *
2107 * Otherwise we search through all of the dictionaries.
2108 *
2109 * Note that we cannot put random protocol attributes into an internal attribute
2110 * of type "group".
2111 */
2112 if (ref != fr_dict_root(fr_dict_internal())) {
2113 our_parent = namespace = ref;
2114
2115 } else if (parent && parent->flags.is_root) {
2116 our_parent = namespace = parent;
2117
2118 } else if (at_rules->dict_def) {
2119 our_parent = namespace = fr_dict_root(at_rules->dict_def);
2120
2121 } else {
2122 our_parent = namespace = NULL;
2123 }
2124 break;
2125
2127 /*
2128 * Structural types are parented and namespaced from their parent da.
2129 */
2130 namespace = our_parent = da;
2131 break;
2132
2133 default:
2134 fr_strerror_printf("Attribute %s of data type '%s' cannot have child attributes", da->name, fr_type_to_str(da->type));
2135 fr_sbuff_set(name, &m_s);
2136 goto error;
2137 }
2138
2139 if (ar) tmpl_attr_insert(vpt, ar);
2140
2141 if (tmpl_attr_afrom_attr_substr(ctx, err, vpt, our_parent, namespace, name, p_rules, at_rules, depth + 1) < 0) {
2142 if (ar) {
2143 tmpl_attr_list_talloc_free_tail(&vpt->data.attribute.ar); /* Remove and free ar */
2144 ar = NULL;
2145 }
2146 goto error;
2147 }
2148
2149 /*
2150 * If it's a leaf we always insert the attribute
2151 * reference into the list, even if it's a
2152 * nesting attribute.
2153 *
2154 * This is useful for nested edit sections
2155 * where the tmpl might be the name of a new
2156 * subsection.
2157 */
2158 } else {
2159 tmpl_attr_insert(vpt, ar);
2160 }
2161
2162 /*
2163 * Remove unnecessary casts.
2164 */
2167
2169
2170 fr_sbuff_marker_release(&m_s);
2171 return 0;
2172}
2173
2174/** Parse a string into a TMPL_TYPE_ATTR_* type #tmpl_t
2175 *
2176 * @param[in,out] ctx to allocate #tmpl_t in.
2177 * @param[out] err May be NULL. Provides the exact error that the parser hit
2178 * when processing the attribute ref.
2179 * @param[out] out Where to write pointer to new #tmpl_t.
2180 * @param[in] name of attribute including #tmpl_request_ref_t and #fr_pair_list_t qualifiers.
2181 * @param[in] p_rules Formatting rules used to check for trailing garbage.
2182 * @param[in] t_rules Rules which control parsing:
2183 * - dict_def The default dictionary to use if attributes
2184 * are unqualified.
2185 * - request_def The default #request_t to set if no
2186 * #tmpl_request_ref_t qualifiers are found in name.
2187 * - list_def The default list to set if no #fr_pair_list_t
2188 * qualifiers are found in the name.
2189 * - allow_unknown If true, numerical attributes will be allowed,
2190 * even if they're not in the main dictionaries.
2191 * If an unknown attribute is found a #TMPL_TYPE_ATTR
2192 * #tmpl_t will be produced.
2193 * If #tmpl_afrom_attr_substr is being called on
2194 * startup, the #tmpl_t may be passed to
2195 * #tmpl_attr_unknown_add to
2196 * add the unknown attribute to the main dictionary.
2197 * If the unknown attribute is not added to
2198 * the main dictionary the #tmpl_t cannot be used
2199 * to search for a #fr_pair_t in a #request_t.
2200 * - allow_unresolved If true, we don't generate a parse error on
2201 * unknown attributes. If an unknown attribute is
2202 * found a #TMPL_TYPE_ATTR_UNRESOLVED
2203 * #tmpl_t will be produced.
2204 * - allow_foreign If true, allow attribute names to be qualified
2205 * with a protocol outside of the passed dict_def.
2206 * - disallow_filters
2207 *
2208 * @see REMARKER to produce pretty error markers from the return value.
2209 *
2210 * @return
2211 * - <= 0 on error (offset as negative integer)
2212 * - > 0 on success (number of bytes parsed).
2213 */
2216 fr_sbuff_parse_rules_t const *p_rules,
2217 tmpl_rules_t const *t_rules)
2218{
2219 int ret;
2220 tmpl_t *vpt;
2221 fr_sbuff_t our_name = FR_SBUFF(name); /* Take a local copy in case we need to back track */
2222 bool is_raw = false;
2223 tmpl_attr_rules_t const *at_rules;
2224 tmpl_attr_rules_t my_attr_rules;
2226 fr_dict_attr_t const *namespace;
2228
2230
2231 at_rules = &t_rules->attr;
2232
2234
2235 if (!fr_sbuff_extend(&our_name)) {
2236 fr_strerror_const("Empty attribute reference");
2238 FR_SBUFF_ERROR_RETURN(&our_name);
2239 }
2240
2241 /*
2242 * '&' prefix is ignored.
2243 */
2244 if (fr_sbuff_next_if_char(&our_name, '&') && check_config && at_rules->ci) {
2245 cf_log_warn(at_rules->ci, "Using '&' is no longer necessary when referencing attributes, and should be deleted.");
2246 }
2247
2248 /*
2249 * We parsed the tmpl as User-Name, but NOT %{User-Name}.
2250 */
2251 MEM(vpt = tmpl_alloc(ctx, TMPL_TYPE_ATTR, T_BARE_WORD, NULL, 0));
2252
2253 /*
2254 * The "raw." prefix marks up the leaf attribute
2255 * as unknown if it wasn't already which allows
2256 * users to stick whatever they want in there as
2257 * a value.
2258 */
2259 if (fr_sbuff_adv_past_strcase_literal(&our_name, "raw.")) {
2260 my_attr_rules = *at_rules;
2261 my_attr_rules.allow_oid = true;
2262 at_rules = &my_attr_rules;
2263
2264 is_raw = true;
2265 }
2266
2267 /*
2268 * Parse one or more request references
2269 */
2271 &vpt->data.attribute.rr,
2272 &our_name,
2273 p_rules,
2274 t_rules,
2275 &namespace);
2276 if (ret < 0) {
2277 error:
2278 *out = NULL;
2280 FR_SBUFF_ERROR_RETURN(&our_name);
2281 }
2282
2283 fr_sbuff_marker(&m_l, &our_name);
2284
2285 /*
2286 * Parse the list and / or attribute reference
2287 */
2289 vpt,
2290 namespace, namespace,
2291 &our_name, p_rules, at_rules, 0);
2292 if (ret < 0) goto error;
2293
2294 /*
2295 * Check to see if the user wants the leaf
2296 * attribute to be raw.
2297 *
2298 * We can only do the conversion now _if_
2299 * the complete hierarchy has been resolved
2300 * otherwise we'll need to do the conversion
2301 * later.
2302 */
2303 if (tmpl_is_attr(vpt) && is_raw) {
2304 ret = attr_to_raw(vpt, tmpl_attr_list_tail(tmpl_attr(vpt)));
2305 if (ret < 0) goto error;
2306 }
2307
2308 /*
2309 * Local variables cannot be given a list modifier.
2310 */
2312 tmpl_attr_t *ar = tmpl_attr_list_head(tmpl_attr(vpt));
2313 bool is_local = ar->ar_da->flags.local;
2314
2315 for (; ar != NULL;
2316 ar = tmpl_attr_list_next(tmpl_attr(vpt), ar)) {
2317 if (!ar->ar_da->flags.local ||
2318 (ar->ar_da->flags.local && is_local)) continue;
2319
2320 fr_strerror_printf("Local attributes cannot be used in any list");
2322 fr_sbuff_set(&our_name, &m_l);
2323 goto error;
2324 }
2325
2326 /*
2327 * That being said, local variables are named "foo", but are always put into the local list.
2328 */
2329 if (is_local && (at_rules->list_presence != TMPL_ATTR_LIST_FORBID)) {
2330 MEM(ar = talloc(vpt, tmpl_attr_t));
2331 *ar = (tmpl_attr_t){
2332 .ar_type = TMPL_ATTR_TYPE_NORMAL,
2333 .ar_da = request_attr_local,
2334 .ar_parent = fr_dict_root(fr_dict_internal())
2335 };
2336
2337 /*
2338 * Prepend the local list ref so it gets evaluated
2339 * first.
2340 */
2341 tmpl_attr_list_insert_head(tmpl_attr(vpt), ar);
2342 }
2343 }
2344
2345 /*
2346 * Check whether the tmpl has a list qualifier.
2347 */
2348 switch (at_rules->list_presence) {
2350 break;
2351
2353 if (tmpl_attr_is_list_attr(tmpl_attr_list_head(tmpl_attr(vpt)))) {
2354 fr_strerror_const("List qualifiers are not allowed here.");
2356 goto error;
2357 }
2358 break;
2359
2361 if (!tmpl_attr_is_list_attr(tmpl_attr_list_head(tmpl_attr(vpt)))) {
2362 fr_strerror_const("List qualifier is required, but no list was found.");
2364 goto error;
2365 }
2366 break;
2367 }
2368
2369 /*
2370 * If we're using lists, ensure that the default list is specified.
2371 */
2372 if (!tmpl_attr_is_list_attr(tmpl_attr_list_head(tmpl_attr(vpt)))) {
2373 tmpl_attr_t *ar;
2374
2375 MEM(ar = talloc(vpt, tmpl_attr_t));
2376 *ar = (tmpl_attr_t){
2377 .ar_type = TMPL_ATTR_TYPE_NORMAL,
2378 .ar_parent = fr_dict_root(fr_dict_internal())
2379 };
2380
2381 fr_assert(at_rules->list_def);
2382 ar->ar_da = at_rules->list_def;
2383
2384 /*
2385 * Prepend the list ref so it gets evaluated
2386 * first.
2387 */
2388 tmpl_attr_list_insert_head(tmpl_attr(vpt), ar);
2389 }
2390
2391 tmpl_set_name(vpt, T_BARE_WORD, fr_sbuff_start(&our_name), fr_sbuff_used(&our_name));
2392 vpt->rules = *t_rules; /* Record the rules */
2393
2394 /*
2395 * If there are actual requests, duplicate them
2396 * and move them into the list.
2397 *
2398 * A NULL request_def pointer is equivalent to the
2399 * current request.
2400 */
2401 if (t_rules->attr.request_def) {
2402 tmpl_request_ref_list_acopy(vpt, &vpt->rules.attr.request_def, t_rules->attr.request_def);
2403 }
2404
2405 if (tmpl_is_attr(vpt)) {
2406 tmpl_attr_t *ar;
2407
2408 /*
2409 * Suppress useless casts.
2410 */
2412 vpt->rules.cast = FR_TYPE_NULL;
2413 }
2414
2415 /*
2416 * Ensure that the list is set correctly, so that
2417 * the returned vpt just doesn't just match the
2418 * input rules, it is also internally consistent.
2419 */
2420 ar = tmpl_attr_list_head(tmpl_attr(vpt));
2421 fr_assert(ar != NULL);
2422
2423 if (tmpl_attr_is_list_attr(ar)) vpt->rules.attr.list_def = ar->ar_da;
2424 }
2425
2426 if (!tmpl_substr_terminal_check(&our_name, p_rules)) {
2427 fr_strerror_const("Unexpected text after attribute reference");
2429 goto error;
2430 }
2431
2432 /*
2433 * If everything was resolved correctly
2434 * we now need to check the cast type.
2435 */
2436 if (!tmpl_needs_resolving(vpt) && !fr_type_is_null(t_rules->cast) &&
2437 !fr_type_cast(t_rules->cast, tmpl_attr_tail_da(vpt)->type)) {
2438 fr_strerror_printf("Cannot cast type '%s' to '%s'",
2441 fr_sbuff_set_to_start(&our_name);
2442 goto error;
2443 }
2444
2445 TMPL_VERIFY(vpt); /* Because we want to ensure we produced something sane */
2446
2447 *out = vpt;
2448 FR_SBUFF_SET_RETURN(name, &our_name);
2449}
2450
2451/** Parse a string into a TMPL_TYPE_ATTR_* type #tmpl_t
2452 *
2453 * @param[in,out] ctx to allocate #tmpl_t in.
2454 * @param[out] err May be NULL. Provides the exact error that the parser hit
2455 * when processing the attribute ref.
2456 * @param[out] out Where to write pointer to new #tmpl_t.
2457 * @param[in] name of attribute including #tmpl_request_ref_t and #fr_pair_list_t qualifiers.
2458 * @param[in] t_rules Rules which control parsing. See tmpl_afrom_attr_substr() for details.
2459 *
2460 * @note Unlike #tmpl_afrom_attr_substr this function will error out if the entire
2461 * name string isn't parsed.
2462 */
2464 tmpl_t **out, char const *name, tmpl_rules_t const *t_rules)
2465{
2466 ssize_t slen, name_len;
2468
2470
2471 name_len = strlen(name);
2472 slen = tmpl_afrom_attr_substr(ctx, err, out, &FR_SBUFF_IN(name, name_len), NULL, t_rules);
2473 if (slen <= 0) return slen;
2474
2475 if (!fr_cond_assert(*out)) return -1;
2476
2477 if (slen != name_len) {
2478 /* This looks wrong, but it produces meaningful errors for unknown attrs */
2479 fr_strerror_printf("Unexpected text after %s",
2480 tmpl_type_to_str((*out)->type));
2481 return -slen;
2482 }
2483
2484 TMPL_VERIFY(*out);
2485
2486 return slen;
2487}
2488
2489/** Create TMPL_TYPE_DATA from a string
2490 *
2491 * @param[in] ctx to allocate tmpl to.
2492 * @param[out] out where to write tmpl.
2493 * @param[in] in sbuff to parse.
2494 * @param[in] quote surrounding the operand to parse.
2495 * @param[in] t_rules specifying the cast and any enumeration values.
2496 * @param[in] allow_enum Whether parsing the value as an enum should be allowed.
2497 * @param[in] p_rules formatting rules.
2498 * @return
2499 * - <0 on error
2500 * - >=0 on success.
2501 */
2503 fr_token_t quote,
2504 tmpl_rules_t const *t_rules, bool allow_enum,
2505 fr_sbuff_parse_rules_t const *p_rules)
2506{
2507 fr_sbuff_t our_in = FR_SBUFF(in);
2508 fr_value_box_t tmp;
2509 tmpl_t *vpt;
2511
2512 if (!fr_type_is_null(t_rules->cast)) cast = t_rules->cast;
2513
2514 if (!fr_type_is_leaf(cast)) {
2515 fr_strerror_printf("%s is not a valid cast type",
2516 fr_type_to_str(cast));
2517 FR_SBUFF_ERROR_RETURN(&our_in);
2518 }
2519
2520 vpt = tmpl_alloc_null(ctx);
2521 if (fr_value_box_from_substr(vpt, &tmp,
2522 cast, allow_enum ? t_rules->enumv : NULL,
2523 &our_in, p_rules) < 0) {
2525 FR_SBUFF_ERROR_RETURN(&our_in);
2526 }
2528
2529 tmpl_init(vpt, TMPL_TYPE_DATA, quote, fr_sbuff_start(&our_in), fr_sbuff_used(&our_in), t_rules);
2530
2532
2533 *out = vpt;
2534
2535 if (cast == tmpl_value_type(vpt)) vpt->rules.cast = FR_TYPE_NULL;
2536
2538
2539 FR_SBUFF_SET_RETURN(in, &our_in);
2540}
2541
2542/** Parse a truth value
2543 *
2544 * @param[in] ctx to allocate tmpl to.
2545 * @param[out] out where to write tmpl.
2546 * @param[in] in sbuff to parse.
2547 * @param[in] p_rules formatting rules.
2548 * @return
2549 * - < 0 sbuff does not contain a boolean value.
2550 * - > 0 how many bytes were parsed.
2551 */
2553 fr_sbuff_parse_rules_t const *p_rules)
2554{
2555 fr_sbuff_t our_in = FR_SBUFF(in);
2556 bool a_bool;
2557 tmpl_t *vpt;
2558
2559 if (fr_sbuff_out(NULL, &a_bool, &our_in) < 0) {
2560 fr_strerror_const("Not a boolean value");
2561 return 0;
2562 }
2563
2564 if (!tmpl_substr_terminal_check(&our_in, p_rules)) {
2565 fr_strerror_const("Unexpected text after bool");
2567 }
2568
2570
2571 fr_value_box_init(&vpt->data.literal, FR_TYPE_BOOL, NULL, false);
2572 vpt->data.literal.vb_bool = a_bool;
2573
2574 *out = vpt;
2575
2576 FR_SBUFF_SET_RETURN(in, &our_in);
2577}
2578
2579/** Parse bareword as an octet string
2580 *
2581 * @param[in] ctx to allocate tmpl to.
2582 * @param[out] out where to write tmpl.
2583 * @param[in] in sbuff to parse.
2584 * @param[in] p_rules formatting rules.
2585 * @return
2586 * - < 0 negative offset where parse error occurred.
2587 * - 0 sbuff does not contain a hex string.
2588 * - > 0 how many bytes were parsed.
2589 */
2591 fr_sbuff_parse_rules_t const *p_rules)
2592{
2593 fr_sbuff_t our_in = FR_SBUFF(in);
2594 tmpl_t *vpt;
2595 char *hex;
2596 size_t binlen, len;
2597 uint8_t *bin;
2598
2599 if (!fr_sbuff_adv_past_strcase_literal(&our_in, "0x")) return 0;
2600
2601 MEM(vpt = tmpl_alloc(ctx, TMPL_TYPE_DATA, T_BARE_WORD, NULL, 0));
2602
2603 /*
2604 * This allows stream parsing to work correctly
2605 * we could be less lazy and copy hex data in
2606 * chunks, but never mind...
2607 */
2608 len = fr_sbuff_out_abstrncpy_allowed(vpt, &hex, &our_in, SIZE_MAX, sbuff_char_class_hex);
2609 if (len & 0x01) {
2610 fr_strerror_const("Hex string not even length");
2611 error:
2613 FR_SBUFF_ERROR_RETURN(&our_in);
2614 }
2615 if (len == 0) {
2616 fr_strerror_const("Zero length hex string is invalid");
2617 goto error;
2618 }
2619
2620 if (!tmpl_substr_terminal_check(&our_in, p_rules)) {
2621 fr_strerror_const("Unexpected text after hex string");
2622 goto error;
2623 }
2624
2625 bin = (uint8_t *)hex;
2626 binlen = len / 2;
2627
2629
2630 (void)fr_base16_decode(NULL, &FR_DBUFF_TMP(bin, binlen), &FR_SBUFF_IN(hex, len), false);
2631 MEM(bin = talloc_realloc_size(vpt, bin, binlen)); /* Realloc to the correct length */
2632 (void)fr_value_box_memdup_shallow(&vpt->data.literal, NULL, bin, binlen, false);
2633
2634 *out = vpt;
2635
2636 FR_SBUFF_SET_RETURN(in, &our_in);
2637}
2638
2639/** Parse bareword as an IPv4 address or prefix
2640 *
2641 * @param[in] ctx to allocate tmpl to.
2642 * @param[out] out where to write tmpl.
2643 * @param[in] in sbuff to parse.
2644 * @param[in] p_rules formatting rules.
2645 * @return
2646 * - < 0 sbuff does not contain an IPv4 address or prefix.
2647 * - > 0 how many bytes were parsed.
2648 */
2650 fr_sbuff_parse_rules_t const *p_rules)
2651{
2652 tmpl_t *vpt;
2653 fr_sbuff_t our_in = FR_SBUFF(in);
2655 int count;
2656 uint32_t ipaddr;
2657 uint8_t addr[4] = {}, prefix = 32;
2658
2659 for (count = 0; count < 4; count++) {
2660 if (!fr_sbuff_out(NULL, &addr[count], &our_in)) FR_SBUFF_ERROR_RETURN(&our_in);
2661
2662 if (count == 3) break;
2663
2664 if (fr_sbuff_next_if_char(&our_in, '.')) continue;
2665
2666 if (!fr_sbuff_is_char(&our_in, '/')) FR_SBUFF_ERROR_RETURN(&our_in);
2667 }
2668
2669 /*
2670 * If it has a trailing '/' then it's an IP prefix.
2671 */
2672 if (fr_sbuff_next_if_char(&our_in, '/')) {
2673 if (fr_sbuff_out(NULL, &prefix, &our_in) < 0) {
2674 fr_strerror_const("IPv4 CIDR mask malformed");
2675 FR_SBUFF_ERROR_RETURN(&our_in);
2676 }
2677
2678 if (prefix > 32) {
2679 fr_strerror_const("IPv4 CIDR mask too high");
2680 FR_SBUFF_ERROR_RETURN(&our_in);
2681 }
2682
2684 } else {
2686 }
2687
2688 if (!tmpl_substr_terminal_check(&our_in, p_rules)) {
2689 fr_strerror_const("Unexpected text after IPv4 string or prefix");
2690 FR_SBUFF_ERROR_RETURN(&our_in);
2691 }
2692
2694 fr_value_box_init(&vpt->data.literal, type, NULL, false);
2695 vpt->data.literal.vb_ip.af = AF_INET;
2696 vpt->data.literal.vb_ip.prefix = prefix;
2697
2698 /*
2699 * Zero out lower bits
2700 */
2701 ipaddr = (((uint32_t) addr[0]) << 24) | (((uint32_t) addr[1]) << 16) | (((uint32_t) addr[2]) << 8) | addr[3];
2702 if (prefix < 32) {
2703 ipaddr &= ~((uint32_t) 0) << (32 - prefix);
2704 }
2705 vpt->data.literal.vb_ip.addr.v4.s_addr = htonl(ipaddr);
2706
2707 *out = vpt;
2708
2709 FR_SBUFF_SET_RETURN(in, &our_in);
2710}
2711
2712/** Parse bareword as an IPv6 address or prefix
2713 *
2714 * @param[in] ctx to allocate tmpl to.
2715 * @param[out] out where to write tmpl.
2716 * @param[in] in sbuff to parse.
2717 * @param[in] p_rules formatting rules.
2718 * @return
2719 * - < 0 sbuff does not contain an IPv4 address or prefix.
2720 * - > 0 how many bytes were parsed.
2721 */
2723 fr_sbuff_parse_rules_t const *p_rules)
2724{
2725 tmpl_t *vpt;
2726 fr_sbuff_t our_in = FR_SBUFF(in);
2729 size_t len;
2730 char *sep_a, *sep_b;
2731
2732 static bool ipv6_chars[UINT8_MAX + 1] = {
2733 ['0'] = true, ['1'] = true, ['2'] = true, ['3'] = true, ['4'] = true,
2734 ['5'] = true, ['6'] = true, ['7'] = true, ['8'] = true, ['9'] = true,
2735 ['a'] = true, ['b'] = true, ['c'] = true, ['d'] = true, ['e'] = true,
2736 ['f'] = true,
2737 ['A'] = true, ['B'] = true, ['C'] = true, ['D'] = true, ['E'] = true,
2738 ['F'] = true,
2739 [':'] = true, ['.'] = true
2740 };
2741
2742 /*
2743 * Drop a marker to pin the start of the
2744 * address in the buffer.
2745 */
2746 fr_sbuff_marker(&m, &our_in);
2747
2748 /*
2749 * Check for something looking like an IPv6 address
2750 *
2751 * Minimum string is '::'
2752 */
2753 len = fr_sbuff_adv_past_allowed(&our_in, FR_IPADDR_STRLEN + 1, ipv6_chars, NULL);
2754 if ((len < 2) || (len > FR_IPADDR_STRLEN)) {
2755 error:
2756 FR_SBUFF_ERROR_RETURN(&our_in);
2757 }
2758
2759 /*
2760 * Got ':' after '.', this isn't allowed.
2761 *
2762 * We need this check else IPv4 gets parsed
2763 * as blank IPv6 address.
2764 */
2765 sep_a = memchr(fr_sbuff_current(&m), '.', len);
2766 if (sep_a && (!(sep_b = memchr(fr_sbuff_current(&m), ':', len)) || (sep_b > sep_a))) {
2767 fr_strerror_const("First IPv6 component separator was a '.'");
2768 goto error;
2769 }
2770
2771 /*
2772 * The v6 parse function will happily turn
2773 * integers into v6 addresses *sigh*.
2774 */
2775 sep_a = memchr(fr_sbuff_current(&m), ':', len);
2776 if (!sep_a) {
2777 fr_strerror_const("No IPv6 component separator");
2778 goto error;
2779 }
2780
2781 /*
2782 * Handle scope
2783 */
2784 if (fr_sbuff_next_if_char(&our_in, '%')) {
2785 len = fr_sbuff_adv_until(&our_in, IFNAMSIZ + 1, p_rules->terminals, '\0');
2786 if ((len < 1) || (len > IFNAMSIZ)) {
2787 fr_strerror_const("IPv6 scope too long");
2788 goto error;
2789 }
2790 }
2791
2792 /*
2793 * ...and finally the prefix.
2794 */
2795 if (fr_sbuff_next_if_char(&our_in, '/')) {
2796 uint8_t mask;
2797
2798 if (fr_sbuff_out(NULL, &mask, &our_in) < 0) {
2799 fr_strerror_const("IPv6 CIDR mask malformed");
2800 goto error;
2801 }
2802 if (mask > 128) {
2803 fr_strerror_const("IPv6 CIDR mask too high");
2804 goto error;
2805 }
2806
2808 } else {
2810 }
2811
2812 if (!tmpl_substr_terminal_check(&our_in, p_rules)) {
2813 fr_strerror_const("Unexpected text after IPv6 string or prefix");
2814 goto error;
2815 }
2816
2818 if (fr_value_box_from_substr(vpt, &vpt->data.literal, type, NULL,
2819 &FR_SBUFF_REPARSE(&our_in),
2820 NULL) < 0) {
2822 goto error;
2823 }
2824 *out = vpt;
2825
2826 FR_SBUFF_SET_RETURN(in, &our_in);
2827}
2828
2829
2830/** Try and parse signed or unsigned integers
2831 *
2832 * @param[in] ctx to allocate tmpl to.
2833 * @param[out] out where to write tmpl.
2834 * @param[in] in sbuff to parse.
2835 * @param[in] p_rules formatting rules.
2836 * @return
2837 * - < 0 sbuff does not contain a mac address.
2838 * - > 0 how many bytes were parsed.
2839 */
2841 fr_sbuff_parse_rules_t const *p_rules)
2842{
2843 tmpl_t *vpt;
2844 fr_sbuff_t our_in = FR_SBUFF(in);
2845 uint8_t buff[6] = {};
2846 fr_dbuff_t dbuff;
2847 fr_value_box_t *vb;
2849
2850 fr_dbuff_init(&dbuff, buff, sizeof(buff));
2851
2852 fr_base16_decode(&err, &dbuff, &our_in, true);
2853 if (err != FR_SBUFF_PARSE_OK) return 0;
2854
2855 if (!fr_sbuff_next_if_char(&our_in, ':')) return 0;
2856
2857 fr_base16_decode(&err, &dbuff, &our_in, true);
2858 if (err != FR_SBUFF_PARSE_OK) return 0;
2859
2860 if (!fr_sbuff_next_if_char(&our_in, ':')) return 0;
2861
2862 fr_base16_decode(&err, &dbuff, &our_in, true);
2863 if (err != FR_SBUFF_PARSE_OK) return 0;
2864
2865 if (!fr_sbuff_next_if_char(&our_in, ':')) return 0;
2866
2867 fr_base16_decode(&err, &dbuff, &our_in, true);
2868 if (err != FR_SBUFF_PARSE_OK) return 0;
2869
2870 if (!fr_sbuff_next_if_char(&our_in, ':')) return 0;
2871
2872 fr_base16_decode(&err, &dbuff, &our_in, true);
2873 if (err != FR_SBUFF_PARSE_OK) return 0;
2874
2875 if (!fr_sbuff_next_if_char(&our_in, ':')) return 0;
2876
2877 fr_base16_decode(&err, &dbuff, &our_in, true);
2878 if (err != FR_SBUFF_PARSE_OK) return 0;
2879
2880 if (!tmpl_substr_terminal_check(&our_in, p_rules)) {
2881 fr_strerror_const("Unexpected text after mac address");
2882 return 0;
2883 }
2884
2886 T_BARE_WORD, fr_sbuff_start(&our_in), fr_sbuff_used(&our_in)));
2887 vb = tmpl_value(vpt);
2888
2889 fr_value_box_init(vb, FR_TYPE_ETHERNET, NULL, false);
2890 memcpy(vb->vb_ether, buff, sizeof(vb->vb_ether));
2891
2892 *out = vpt;
2893
2894 FR_SBUFF_SET_RETURN(in, &our_in);
2895}
2896
2897/** Try and parse signed or unsigned integers
2898 *
2899 * @param[in] ctx to allocate tmpl to.
2900 * @param[out] out where to write tmpl.
2901 * @param[in] in sbuff to parse.
2902 * @param[in] p_rules formatting rules.
2903 * @return
2904 * - < 0 sbuff does not contain an integer.
2905 * - > 0 how many bytes were parsed.
2906 */
2908 fr_sbuff_parse_rules_t const *p_rules)
2909{
2910 tmpl_t *vpt;
2911 fr_sbuff_t our_in = FR_SBUFF(in);
2912 ssize_t slen;
2913 fr_value_box_t *vb;
2914
2915 /*
2916 * Pick the narrowest signed type
2917 */
2918 if (fr_sbuff_is_char(&our_in, '-')) {
2919 int64_t a_int;
2920
2921 slen = fr_sbuff_out(NULL, &a_int, &our_in);
2922 if (slen <= 0) return 0;
2923
2924 if (!tmpl_substr_terminal_check(&our_in, p_rules)) {
2925 fr_strerror_const("Unexpected text after signed integer");
2926 error:
2927 FR_SBUFF_ERROR_RETURN(&our_in);
2928 }
2929
2931 T_BARE_WORD, fr_sbuff_start(&our_in), fr_sbuff_used(&our_in)));
2932 vb = tmpl_value(vpt);
2933 if (a_int >= INT8_MIN) {
2934 fr_value_box_init(vb, FR_TYPE_INT8, NULL, false);
2935 vb->vb_int8 = (int8_t)a_int;
2936 } else if (a_int >= INT16_MIN) {
2937 fr_value_box_init(vb, FR_TYPE_INT16, NULL, false);
2938 vb->vb_int16 = (int16_t)a_int;
2939 } else if (a_int >= INT32_MIN) {
2940 fr_value_box_init(vb, FR_TYPE_INT32, NULL, false);
2941 vb->vb_int32 = (int32_t)a_int;
2942 } else {
2943 fr_value_box_init(vb, FR_TYPE_INT64, NULL, false);
2944 vb->vb_int64 = (int64_t)a_int;
2945 }
2946 /*
2947 * Pick the narrowest unsigned type
2948 */
2949 } else {
2950 uint64_t a_uint;
2951
2952 slen = fr_sbuff_out(NULL, &a_uint, &our_in);
2953 if (slen <= 0) return slen;
2954
2955 if (!tmpl_substr_terminal_check(&our_in, p_rules)) {
2956 fr_strerror_const("Unexpected text after unsigned integer");
2957 goto error;
2958 }
2959
2961 T_BARE_WORD, fr_sbuff_start(&our_in), fr_sbuff_used(&our_in)));
2962 vb = tmpl_value(vpt);
2963 if (a_uint <= UINT8_MAX) {
2964 fr_value_box_init(vb, FR_TYPE_UINT8, NULL, false);
2965 vb->vb_uint8 = (uint8_t)a_uint;
2966 } else if (a_uint <= UINT16_MAX) {
2967 fr_value_box_init(vb, FR_TYPE_UINT16, NULL, false);
2968 vb->vb_uint16 = (uint16_t)a_uint;
2969 } else if (a_uint <= UINT32_MAX) {
2970 fr_value_box_init(vb, FR_TYPE_UINT32, NULL, false);
2971 vb->vb_uint32 = (uint32_t)a_uint;
2972 } else {
2973 fr_value_box_init(vb, FR_TYPE_UINT64, NULL, false);
2974 vb->vb_uint64 = (uint64_t)a_uint;
2975 }
2976 }
2977
2978 *out = vpt;
2979
2980 FR_SBUFF_SET_RETURN(in, &our_in);
2981}
2982
2984 fr_sbuff_parse_rules_t const *p_rules)
2985{
2986 tmpl_t *vpt;
2987 fr_sbuff_t our_in = FR_SBUFF(in);
2988 double a_float;
2989 ssize_t slen;
2990 fr_value_box_t *vb;
2991
2992 slen = fr_sbuff_out(NULL, &a_float, &our_in);
2993 if (slen <= 0) return 0;
2994
2995 if (!tmpl_substr_terminal_check(&our_in, p_rules)) {
2996 fr_strerror_const("Unexpected text after float");
2997 FR_SBUFF_ERROR_RETURN(&our_in);
2998 }
2999
3001 T_BARE_WORD, fr_sbuff_start(&our_in), fr_sbuff_used(&our_in)));
3002 vb = tmpl_value(vpt);
3003 fr_value_box_init(vb, FR_TYPE_FLOAT64, NULL, false);
3004 vb->vb_float64 = a_float;
3005
3006 *out = vpt;
3007
3008 FR_SBUFF_SET_RETURN(in, &our_in);
3009}
3010
3012 fr_sbuff_parse_rules_t const *p_rules)
3013{
3014 tmpl_t *vpt;
3015 fr_sbuff_t our_in = FR_SBUFF(in);
3016 fr_time_delta_t a_delta;
3017 fr_slen_t slen;
3018 fr_value_box_t *vb;
3019
3020 slen = fr_time_delta_from_substr(&a_delta, &our_in, FR_TIME_RES_SEC, true, p_rules ? p_rules->terminals : NULL);
3021 if (slen <= 0) return 0;
3022
3024 T_BARE_WORD, fr_sbuff_start(&our_in), fr_sbuff_used(&our_in)));
3025 vb = tmpl_value(vpt);
3026 fr_value_box_init(vb, FR_TYPE_TIME_DELTA, NULL, false);
3027 vb->vb_time_delta = a_delta;
3028
3029 *out = vpt;
3030
3031 FR_SBUFF_SET_RETURN(in, &our_in);
3032}
3033
3034/*
3035 * ::value
3036 *
3037 * Treated as enum name. Note that this check MUST be done after the test for IPv6, as
3038 * "::1" is an allowed IPv6 address.
3039 *
3040 * @todo - Mark this up as an enum name? Or do we really care? Maybe we want to allow
3041 *
3042 * Service-Type == 'Framed-User'
3043 *
3044 * or
3045 *
3046 * Service-Type == "Framed-User'
3047 *
3048 * as the second one allows for xlat expansions of enum names.
3049 *
3050 * We probably do want to forbid the single-quoted form of enums,
3051 * as that doesn't seem to make sense.
3052 *
3053 * We also need to distinguish unresolved bare words as enums
3054 * (with :: prefix) from unresolved attributes without an & prefix.
3055 */
3056static ssize_t tmpl_afrom_enum(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in,
3057 fr_sbuff_parse_rules_t const *p_rules,
3058 tmpl_rules_t const *t_rules)
3059{
3060 tmpl_t *vpt;
3062 fr_sbuff_t our_in = FR_SBUFF(in);
3063 fr_sbuff_t *enum_buff;
3064
3065 FR_SBUFF_TALLOC_THREAD_LOCAL(&enum_buff, 1024, SIZE_MAX);
3066
3067 /*
3068 * If there isn't a "::" prefix, then check for migration flags, and enum.
3069 *
3070 * If we require an enum prefix, then the input can't be an enum, and we don't do any more
3071 * parsing.
3072 *
3073 * Otherwise if there's no prefix and no enumv, we know this input can't be an enum name.
3074 */
3075 if (!fr_sbuff_adv_past_str_literal(&our_in, "::")) {
3076 return 0;
3077
3078 } else if (t_rules->enumv &&
3079 ((t_rules->enumv->type == FR_TYPE_IPV6_ADDR) ||
3080 ((t_rules->enumv->type == FR_TYPE_IPV6_PREFIX)))) {
3081
3082 /*
3083 * We can't have enumerated names for IPv6 addresses.
3084 *
3085 * @todo - allow them ONLY if the RHS string is a valid enum name.
3086 */
3087 return 0;
3088 }
3089
3090 /*
3091 * Need to store the value with the prefix, because the value box functions
3092 * expect it to be there...
3093 */
3094 fr_sbuff_in_strcpy_literal(enum_buff, "::");
3095
3096 vpt = tmpl_alloc_null(ctx);
3097
3098 /*
3099 * If it doesn't match any other type of bareword, parse it as an enum name.
3100 *
3101 * Note that we don't actually try to resolve the enum name. The caller is responsible
3102 * for doing that.
3103 */
3104 if (fr_dict_enum_name_from_substr(enum_buff, &sberr, &our_in, p_rules ? p_rules->terminals : NULL) < 0) {
3105 /*
3106 * Produce our own errors which make
3107 * more sense in the context of tmpls
3108 */
3109 switch (sberr) {
3111 fr_strerror_const("No operand found. Expected &ref, literal, "
3112 "'quoted literal', \"%{expansion}\", or enum value");
3113 break;
3114
3116 fr_strerror_const("enum values must contain at least one alpha character");
3117 break;
3118
3119 default:
3120 fr_strerror_const("Unexpected text after enum value.");
3121 break;
3122 }
3123
3125 FR_SBUFF_ERROR_RETURN(&our_in);
3126 }
3127
3128 /*
3129 * If there's a valid enum name, then we use it. Otherwise we leave name resolution to run time.
3130 */
3131 if (t_rules->enumv) {
3132 fr_dict_enum_value_t const *dv;
3133
3134 dv = fr_dict_enum_by_name(t_rules->enumv, fr_sbuff_start(enum_buff), fr_sbuff_used(enum_buff));
3135 if (dv) {
3137 fr_sbuff_start(&our_in), fr_sbuff_used(&our_in), t_rules);
3138 if (unlikely(fr_value_box_copy(vpt, &vpt->data.literal, dv->value) < 0)) {
3140 return -1;
3141 }
3142 vpt->data.literal.enumv = t_rules->enumv;
3143
3144 *out = vpt;
3145 FR_SBUFF_SET_RETURN(in, &our_in);
3146 }
3147 }
3148
3149 /*
3150 * Either there's no enum, or the enum name didn't match one of the listed ones. There's no
3151 * point in waiting for an enum which might be declared later. That's not possible, so we fall
3152 * back to parsing the various data types.
3153 */
3154 if (t_rules->at_runtime) return 0;
3155
3157 fr_sbuff_start(&our_in), fr_sbuff_used(&our_in), t_rules);
3158 MEM(vpt->data.unescaped = talloc_bstrndup(vpt, fr_sbuff_start(enum_buff), fr_sbuff_used(enum_buff)));
3159 *out = vpt;
3160
3161 FR_SBUFF_SET_RETURN(in, &our_in);
3162}
3163
3164/** Convert an arbitrary string into a #tmpl_t
3165 *
3166 * @note Unlike #tmpl_afrom_attr_str return code 0 doesn't necessarily indicate failure,
3167 * may just mean a 0 length string was parsed. Check to see if the function emitted
3168 * a #tmpl_t in *out.
3169 *
3170 * @note xlats and regexes are left uncompiled. This is to support the two pass parsing
3171 * done by the modcall code. Compilation on pass1 of that code could fail, as
3172 * attributes or xlat functions registered by modules may not be available (yet).
3173 *
3174 * @note For details of attribute parsing see #tmpl_afrom_attr_substr.
3175 *
3176 * @param[in,out] ctx To allocate #tmpl_t in.
3177 * @param[out] out Where to write the pointer to the new #tmpl_t.
3178 * @param[in] in String to parse.
3179 * @param[in] quote Quoting around the tmpl. Determines what we
3180 * attempt to parse the string as.
3181 * @param[in] p_rules Formatting rules for the tmpl.
3182 * @param[in] t_rules Validation rules for attribute references.
3183 * @return
3184 * - < 0 on error (offset as negative integer)
3185 * - >= 0 on success (number of bytes parsed).
3186 *
3187 * @see REMARKER to produce pretty error markers from the return value.
3188 *
3189 * @see tmpl_afrom_attr_substr
3190 */
3192 fr_sbuff_t *in, fr_token_t quote,
3193 fr_sbuff_parse_rules_t const *p_rules,
3194 tmpl_rules_t const *t_rules)
3195{
3196 fr_sbuff_t our_in = FR_SBUFF(in);
3197
3198 fr_slen_t slen;
3200 char *str;
3201
3202 tmpl_t *vpt = NULL;
3204
3206
3207 *out = NULL;
3208
3209 switch (quote) {
3210 case T_BARE_WORD:
3211 /*
3212 * Skip other bareword types if
3213 * we find a '&' prefix.
3214 */
3215 if (fr_sbuff_is_char(&our_in, '&')) return tmpl_afrom_attr_substr(ctx, NULL, out, in,
3216 p_rules, t_rules);
3217
3218 /*
3219 * Allow bareword xlats if we
3220 * find a '%' prefix.
3221 */
3222 if (fr_sbuff_is_char(&our_in, '%')) {
3224 xlat_exp_head_t *head = NULL;
3225
3226 vpt = tmpl_alloc_null(ctx);
3227 slen = xlat_tokenize(vpt, &head, &our_in, p_rules, t_rules);
3228 if (slen <= 0) FR_SBUFF_ERROR_RETURN(&our_in);
3229
3232 goto set_tmpl;
3233
3234 } else if (fr_dlist_num_elements(&head->dlist) == 1) {
3235 xlat_exp_t *node = xlat_exp_head(head);
3236 tmpl_t *hoisted;
3237
3238 if (node->type != XLAT_TMPL) goto set_tmpl;
3239
3240 /*
3241 * We were asked to parse a tmpl. But it turned out to be an xlat %{...}
3242 *
3243 * If that xlat is identically a tmpl such as %{User-Name}, then we just
3244 * hoist the tmpl to this node. Otherwise at run time, we will have an
3245 * extra bounce through the xlat code, for no real reason.
3246 */
3247 hoisted = node->vpt;
3248
3249 (void) talloc_steal(ctx, hoisted);
3251 vpt = hoisted;
3252
3253 } else {
3254 set_tmpl:
3255 tmpl_init(vpt, type, quote, fr_sbuff_start(&our_in), slen, t_rules);
3256 vpt->data.xlat.ex = head;
3257 }
3258
3259 *out = vpt;
3260
3262
3263 FR_SBUFF_SET_RETURN(in, &our_in);
3264 }
3265
3266 /*
3267 * Deal with explicit casts...
3268 */
3269 if (!fr_type_is_null(t_rules->cast)) return tmpl_afrom_value_substr(ctx, out, in, quote,
3270 t_rules, true, p_rules);
3271
3272 /*
3273 * We're at runtime and have a data type. Just parse it as that data type, without doing
3274 * endless "maybe it's this thing" attempts.
3275 */
3276 if (t_rules->at_runtime && t_rules->enumv) {
3277 tmpl_rules_t my_t_rules = *t_rules;
3278
3279 fr_assert(fr_type_is_leaf(t_rules->enumv->type));
3280
3281 my_t_rules.cast = my_t_rules.enumv->type;
3282
3283 return tmpl_afrom_value_substr(ctx, out, in, quote, &my_t_rules, true, p_rules);
3284 }
3285
3286 /*
3287 * Prefer enum names to IPv6 addresses.
3288 */
3289 if (t_rules->enumv && fr_sbuff_is_str_literal(&our_in, "::")) {
3290 slen = tmpl_afrom_enum(ctx, out, &our_in, p_rules, t_rules);
3291 if (slen > 0) goto done_bareword;
3292 fr_assert(!*out);
3293 }
3294
3295 /*
3296 * See if it's a boolean value
3297 */
3298 slen = tmpl_afrom_bool_substr(ctx, out, &our_in, p_rules);
3299 if (slen > 0) {
3300 done_bareword:
3301 TMPL_VERIFY(*out);
3302
3303 FR_SBUFF_SET_RETURN(in, &our_in);
3304 }
3305 fr_assert(!*out);
3306
3307 /*
3308 * See if it's an octets string
3309 */
3310 slen = tmpl_afrom_octets_substr(ctx, out, &our_in, p_rules);
3311 if (slen > 0) goto done_bareword;
3312 fr_assert(!*out);
3313
3314 /*
3315 * See if it's a mac address
3316 *
3317 * Needs to be before IPv6 as the pton functions
3318 * are too greedy, and on macOS will happily
3319 * convert a mac address to an IPv6 address.
3320 */
3321 slen = tmpl_afrom_ether_substr(ctx, out, &our_in, p_rules);
3322 if (slen > 0) goto done_bareword;
3323 fr_assert(!*out);
3324
3325 /*
3326 * See if it's an IPv4 address or prefix
3327 */
3328 slen = tmpl_afrom_ipv4_substr(ctx, out, &our_in, p_rules);
3329 if (slen > 0) goto done_bareword;
3330 fr_assert(!*out);
3331
3332 /*
3333 * See if it's an IPv6 address or prefix
3334 */
3335 slen = tmpl_afrom_ipv6_substr(ctx, out, &our_in, p_rules);
3336 if (slen > 0) goto done_bareword;
3337 fr_assert(!*out);
3338
3339 slen = tmpl_afrom_enum(ctx, out, &our_in, p_rules, t_rules);
3340 if (slen > 0) goto done_bareword;
3341 fr_assert(!*out);
3342
3343 /*
3344 * See if it's a integer
3345 */
3346 slen = tmpl_afrom_integer_substr(ctx, out, &our_in, p_rules);
3347 if (slen > 0) goto done_bareword;
3348 fr_assert(!*out);
3349
3350 /*
3351 * See if it's a float
3352 */
3353 slen = tmpl_afrom_float_substr(ctx, out, &our_in, p_rules);
3354 if (slen > 0) goto done_bareword;
3355 fr_assert(!*out);
3356
3357 /*
3358 * See if it's a time delta
3359 *
3360 * We do this after floats and integers so that
3361 * they get parsed as integer and float types
3362 * and not time deltas.
3363 */
3364 slen = tmpl_afrom_time_delta(ctx, out, &our_in, p_rules);
3365 if (slen > 0) goto done_bareword;
3366 fr_assert(!*out);
3367
3368 /*
3369 * See if it's an attribute reference
3370 * without the prefix.
3371 */
3372 slen = tmpl_afrom_attr_substr(ctx, NULL, out, &our_in, p_rules, t_rules);
3373 if (slen > 0) goto done_bareword;
3374 fr_assert(!*out);
3375
3376 /*
3377 * We can't parse it as anything, that's an error.
3378 *
3379 * But it may be an enumeration value for an
3380 * attribute which is loaded later. In which
3381 * case we allow parsing the enumeration.
3382 */
3383 if (!fr_sbuff_is_str_literal(&our_in, "::")) {
3384 /*
3385 * Return the error string from parsing the attribute!
3386 */
3387 FR_SBUFF_ERROR_RETURN(&our_in);
3388 }
3389
3390 /*
3391 * Attempt to resolve enumeration values
3392 */
3393 vpt = tmpl_alloc_null(ctx);
3394
3395 /*
3396 * If it doesn't match any other type of bareword, parse it as an enum name.
3397 *
3398 * Note that we don't actually try to resolve the enum name. The caller is responsible
3399 * for doing that.
3400 */
3401 if (fr_dict_enum_name_afrom_substr(vpt, &str, &sberr, &our_in, p_rules ? p_rules->terminals : NULL) < 0) {
3402 /*
3403 * Produce our own errors which make
3404 * more sense in the context of tmpls
3405 */
3406 switch (sberr) {
3408 fr_strerror_const("No operand found. Expected &ref, literal, "
3409 "'quoted literal', \"%{expansion}\", or enum value");
3410 break;
3411
3413 fr_strerror_const("enum values must contain at least one alpha character");
3414 break;
3415
3416 default:
3417 fr_strerror_const("Unexpected text after enum value.");
3418 break;
3419 }
3420
3422 FR_SBUFF_ERROR_RETURN(&our_in);
3423 }
3424
3426 fr_sbuff_start(&our_in), fr_sbuff_used(&our_in), t_rules);
3427 vpt->data.unescaped = str;
3428 *out = vpt;
3429
3430 FR_SBUFF_SET_RETURN(in, &our_in);
3431
3433 /*
3434 * Single quoted strings can be cast
3435 * to a specific data type immediately
3436 * as they cannot contain expansions.
3437 */
3438 if (!fr_type_is_null(t_rules->cast)) return tmpl_afrom_value_substr(ctx, out, in, quote,
3439 t_rules, false,
3440 p_rules);
3441 vpt = tmpl_alloc_null(ctx);
3442 slen = fr_sbuff_out_aunescape_until(vpt, &str, &our_in, SIZE_MAX,
3443 p_rules ? p_rules->terminals : NULL,
3444 p_rules ? p_rules->escapes : NULL);
3445 tmpl_init(vpt, TMPL_TYPE_DATA_UNRESOLVED, quote, fr_sbuff_start(&our_in), slen, t_rules);
3446 vpt->data.unescaped = str;
3447 break;
3448
3450 {
3451 xlat_exp_head_t *head = NULL;
3453
3454 vpt = tmpl_alloc_null(ctx);
3455
3456 slen = xlat_tokenize(vpt, &head, &our_in, p_rules, t_rules);
3457 if (slen < 0) FR_SBUFF_ERROR_RETURN(&our_in);
3458
3459 /*
3460 * If the string doesn't contain an xlat,
3461 * and we want to cast it as a specific
3462 * type, then do the conversion now.
3463 */
3464 if (xlat_is_literal(head)) {
3465 if (!fr_type_is_null(t_rules->cast)) {
3466 talloc_free(vpt); /* Also frees any nodes */
3467
3468 return tmpl_afrom_value_substr(ctx, out,
3469 in, quote,
3470 t_rules, false, p_rules);
3471 }
3472
3473 /*
3474 * If the string doesn't contain an xlat
3475 * and there's no cast, we just store
3476 * the string for conversion later.
3477 */
3478 if (xlat_to_string(vpt, &str, &head)) {
3479 TALLOC_FREE(head);
3480
3482 fr_sbuff_start(&our_in), slen, t_rules);
3483 vpt->data.unescaped = str; /* Store the unescaped string for parsing later */
3484 break;
3485 }
3486 }
3487
3488 /*
3489 * If the string actually contains an xlat
3490 * store the compiled xlat.
3491 */
3493
3494 tmpl_init(vpt, type, quote, fr_sbuff_start(&our_in), slen, t_rules);
3495 vpt->data.xlat.ex = head;
3496 }
3497 break;
3498
3500 {
3502 xlat_exp_head_t *head = NULL;
3503
3504 vpt = tmpl_alloc_null(ctx);
3505
3506 /*
3507 * Ensure that we pre-parse the exec string.
3508 * This allows us to catch parse errors as early
3509 * as possible.
3510 *
3511 * FIXME - We need an ephemeral version of this
3512 * too.
3513 */
3514 slen = xlat_tokenize_argv(vpt, &head, &our_in, NULL, p_rules, t_rules, true);
3515 if ((slen <= 0) || !head) {
3517 FR_SBUFF_ERROR_RETURN(&our_in);
3518 }
3519
3520 /*
3521 * Ensure any xlats produced are bootstrapped
3522 * so that their instance data will be created.
3523 */
3524 if (xlat_finalize(head, t_rules->xlat.runtime_el) < 0) {
3525 fr_strerror_const("Failed to bootstrap xlat");
3526 FR_SBUFF_ERROR_RETURN(&our_in);
3527 }
3528
3530
3531 tmpl_init(vpt, type, quote, fr_sbuff_start(&our_in), slen, t_rules);
3532 vpt->data.xlat.ex = head;
3533 }
3534 break;
3535
3537 {
3538 xlat_exp_head_t *head = NULL;
3540 tmpl_rules_t arg_t_rules = *t_rules;
3541
3542 arg_t_rules.literals_safe_for = FR_REGEX_SAFE_FOR;
3543
3544 if (!fr_type_is_null(t_rules->cast)) {
3545 fr_strerror_const("Casts cannot be used with regular expressions");
3546 fr_sbuff_set_to_start(&our_in); /* Point to the cast */
3547 FR_SBUFF_ERROR_RETURN(&our_in);
3548 }
3549
3550 vpt = tmpl_alloc_null(ctx);
3551
3552 slen = xlat_tokenize(vpt, &head, &our_in, p_rules, &arg_t_rules);
3553 if (slen < 0) FR_SBUFF_ERROR_RETURN(&our_in);
3554
3555 /*
3556 * Check if the string actually contains an xlat
3557 * if it doesn't, we unfortunately still
3558 * can't compile the regex here, as we don't know if
3559 * it should be ephemeral or what flags should be used
3560 * during the compilation.
3561 *
3562 * The caller will need to do the compilation after we
3563 * return.
3564 */
3565 if (xlat_to_string(vpt, &str, &head)) {
3567 fr_sbuff_start(&our_in), slen, t_rules);
3568 vpt->data.unescaped = str; /* Store the unescaped string for compilation later */
3569 break;
3570 }
3571 /*
3572 * Mark the regex up as a regex-xlat which
3573 * will need expanding before evaluation, and can never
3574 * be pre-compiled.
3575 */
3577
3578 tmpl_init(vpt, type, quote, fr_sbuff_start(&our_in), slen, t_rules);
3579 vpt->data.xlat.ex = head;
3580 }
3581 break;
3582
3583 default:
3584 fr_assert_msg(0, "Unknown quote type %i", quote);
3585 FR_SBUFF_ERROR_RETURN(&our_in);
3586 }
3587
3589 *out = vpt;
3590
3591 FR_SBUFF_SET_RETURN(in, &our_in);
3592}
3593
3594/** Copy a tmpl
3595 *
3596 * Fully duplicates the contents of a tmpl including any nested attribute
3597 * references.
3598 *
3599 * @param[in] ctx to perform allocations under.
3600 * @param[in] in tmpl to duplicate.
3601 * @return
3602 * - NULL on error.
3603 * - A new tmpl on success.
3604 */
3605tmpl_t *tmpl_copy(TALLOC_CTX *ctx, tmpl_t const *in)
3606{
3607 tmpl_t *vpt;
3608
3609 MEM(vpt = tmpl_alloc(ctx, in->type, in->quote, in->name, in->len));
3610 vpt->rules = in->rules;
3611
3612 /*
3613 * Copy over the unescaped data
3614 */
3616 if (unlikely(!(vpt->data.unescaped = talloc_bstrdup(vpt, in->data.unescaped)))) {
3617 error:
3619 return NULL;
3620 }
3621 }
3622
3623 /*
3624 * Copy attribute references
3625 */
3626 else if (tmpl_contains_attr(vpt)) {
3627 if (unlikely(tmpl_attr_copy(vpt, in) < 0)) goto error;
3628
3629 /*
3630 * Copy flags for all regex flavours (and possibly recompile the regex)
3631 */
3632 } else if (tmpl_contains_regex(vpt)) {
3633 vpt->data.reg_flags = in->data.reg_flags;
3634
3635 /*
3636 * If the tmpl contains a _compiled_ regex
3637 * then convert it back to an uncompiled
3638 * regex and recompile.
3639 *
3640 * Most of the regex libraries don't allow
3641 * copying compiled expressions.
3642 */
3643 if (tmpl_is_regex(vpt)) {
3645 if (unlikely(!(vpt->data.unescaped = talloc_bstrdup(vpt, in->data.reg.src)))) goto error;
3646 if (unlikely(tmpl_regex_compile(vpt, vpt->data.reg.subcaptures) < 0)) goto error;
3647 return vpt;
3648 }
3649
3650 /*
3651 * Copy the xlat component.
3652 *
3653 * @todo - in general we can't copy an xlat, as the instances need resolving!
3654 *
3655 * We add an assertion here because nothing allocates the head, and we need it.
3656 */
3657 } else if (tmpl_contains_xlat(vpt)) {
3658 fr_assert(in->data.xlat.ex != NULL);
3659
3660 vpt->data.xlat.ex = xlat_exp_head_alloc(vpt);
3661 if (!vpt->data.xlat.ex) goto error;
3662
3663 if (unlikely(xlat_copy(vpt, vpt->data.xlat.ex, in->data.xlat.ex) < 0)) goto error;
3664
3665 } else if (tmpl_is_data(vpt)) {
3666 if (unlikely(fr_value_box_copy(vpt, &vpt->data.literal, &in->data.literal) < 0)) goto error;
3667
3668 } else {
3669 fr_assert(0); /* copy of this type is unimplemented */
3670 }
3671
3673
3674 return vpt;
3675}
3676
3677/** Parse a cast specifier
3678 *
3679 * Note that casts are
3680 *
3681 * (foo)
3682 *
3683 * and NOT
3684 *
3685 * ( foo )
3686 *
3687 * Not for any particular reason, but to emphasize a bit that they're
3688 * not mathematical expressions.
3689 *
3690 * @param[out] rules to set the cast type in.
3691 * @param[in] in String containing the cast marker.
3692 * @return
3693 * - 0 no cast specifier found.
3694 * - >0 the number of bytes parsed.
3695 * - <0 offset of parse error.
3696 */
3698{
3699 char close = '\0';
3700 fr_sbuff_t our_in = FR_SBUFF(in);
3702 fr_type_t cast;
3703 ssize_t slen;
3704
3705 if (fr_sbuff_next_if_char(&our_in, '(')) {
3706 close = ')';
3707
3708 } else {
3709 if (rules) rules->cast = FR_TYPE_NULL;
3710 return 0;
3711 }
3712
3713 fr_sbuff_marker(&m, &our_in);
3715 if (fr_type_is_null(cast)) {
3716 fr_strerror_const("Unknown data type");
3717 FR_SBUFF_ERROR_RETURN(&our_in);
3718 }
3719 if (fr_type_is_non_leaf(cast)) {
3720 fr_strerror_printf("Forbidden data type '%s' in cast", fr_type_to_str(cast));
3722 }
3723
3724 if (!fr_sbuff_next_if_char(&our_in, close)) {
3725 fr_strerror_const("Unterminated cast");
3726 FR_SBUFF_ERROR_RETURN(&our_in);
3727 }
3728 fr_sbuff_adv_past_whitespace(&our_in, SIZE_MAX, NULL);
3729
3730 if (rules) rules->cast = cast;
3731
3732 FR_SBUFF_SET_RETURN(in, &our_in);
3733}
3734
3735/** Set a cast for a tmpl
3736 *
3737 * @param[in,out] vpt to set cast for.
3738 * @param[in] dst_type to set.
3739 * @return
3740 * - 0 on success.
3741 * - -1 on failure.
3742 */
3744{
3745 fr_type_t src_type;
3746
3747 switch (dst_type) {
3748 default:
3749 fr_strerror_printf("Forbidden data type '%s' in cast",
3750 fr_type_to_str(dst_type));
3751 return -1;
3752
3753 /*
3754 * We can always remove a cast.
3755 */
3756 case FR_TYPE_NULL:
3757 goto done;
3758
3759 /*
3760 * Only "base" data types are allowed. Structural types
3761 * and horrid WiMAX crap is forbidden.
3762 */
3763 case FR_TYPE_LEAF:
3764 break;
3765 }
3766
3767 switch (vpt->type) {
3768 /*
3769 * This should have been fixed before we got here.
3770 */
3772
3773 /*
3774 * By default, tmpl types cannot be cast to anything.
3775 */
3776 default:
3777 fr_strerror_const("Cannot use cast here.");
3778 return -1;
3779
3780 /*
3781 * These tmpl types are effectively of data type
3782 * "string", so they can be cast to anything.
3783 */
3784 case TMPL_TYPE_XLAT:
3785 case TMPL_TYPE_EXEC:
3789 break;
3790
3791 case TMPL_TYPE_DATA:
3792 src_type = tmpl_value_type(vpt);
3793 goto check_types;
3794
3795 case TMPL_TYPE_ATTR:
3796 {
3798
3799 /*
3800 * If the attribute has an enum, then the cast means "use the raw value, and not
3801 * the enum name".
3802 */
3803 if (da->type == dst_type) {
3804 if (da->flags.has_value) goto done;
3805 return 0;
3806 }
3807 src_type = da->type;
3808 }
3809
3810 /*
3811 * Suppress casts where they are duplicate, unless there's an enumv. In which case the
3812 * cast means "don't print the enumv value, just print the raw data".
3813 */
3814 check_types:
3815 if (src_type == dst_type) {
3816 /*
3817 * Cast with enumv means "use the raw value, and not the enum name".
3818 */
3819 if (tmpl_rules_enumv(vpt)) {
3820 tmpl_rules_enumv(vpt) = NULL;
3821 goto done;
3822 }
3823 return 0;
3824 }
3825
3826 if (!fr_type_cast(dst_type, src_type)) {
3827 fr_strerror_printf("Cannot cast type '%s' to '%s'",
3828 fr_type_to_str(src_type),
3829 fr_type_to_str(dst_type));
3830 return -1;
3831 }
3832 break;
3833 }
3834
3835done:
3836 vpt->rules.cast = dst_type;
3837 return 0;
3838}
3839
3840#ifdef HAVE_REGEX
3841/** Parse a set of regular expression flags
3842 *
3843 * @param[out] vpt Write the flags to the regex flags field in this #tmpl_t.
3844 * @param[in] in Where to parse the flag string from.
3845 * @param[in] terminals That mark the end of the regex flag string.
3846 * @return
3847 * - 0 no flags found.
3848 * - >0 the number of bytes of flags parsed.
3849 * - <0 offset of parse error.
3850 */
3851ssize_t tmpl_regex_flags_substr(tmpl_t *vpt, fr_sbuff_t *in, fr_sbuff_term_t const *terminals)
3852{
3853 fr_slen_t slen;
3854 int err = 0;
3855
3857
3858 slen = regex_flags_parse(&err, &vpt->data.reg_flags, in, terminals, true);
3859 switch (err) {
3860 case 0:
3861 break;
3862
3863 case -1: /* Non-flag and non-terminal */
3864 case -2: /* Duplicate flag */
3865 return slen;
3866 }
3867
3868 return slen;
3869}
3870#endif
3871
3872/** @name Change a #tmpl_t type, usually by casting or resolving a reference
3873 *
3874 * #tmpl_cast_in_place can be used to convert #TMPL_TYPE_DATA_UNRESOLVED to a #TMPL_TYPE_DATA of a
3875 * specified #fr_type_t.
3876 *
3877 * #tmpl_attr_unknown_add converts a #TMPL_TYPE_ATTR with an unknown #fr_dict_attr_t to a
3878 * #TMPL_TYPE_ATTR with a known #fr_dict_attr_t, by adding the unknown #fr_dict_attr_t to the main
3879 * dictionary, and updating the ``tmpl_attr_tail_da`` pointer.
3880 * @{
3881 */
3882
3883/** Determine the correct quoting after a cast
3884 *
3885 * @param[in] existing_quote Exiting quotation type.
3886 * @param[in] type Cast type.
3887 * @param[in] enumv Enumeration values.
3888 * @param[in] unescaped The unescaped value of an enumeration.
3889 * @param[in] unescaped_len Length of unescaped.
3890 */
3891static inline CC_HINT(always_inline)
3893 fr_type_t type, fr_dict_attr_t const *enumv,
3894 char const *unescaped, size_t unescaped_len)
3895{
3896 if (!fr_type_is_string(type)) return T_BARE_WORD;
3897
3898 if (enumv && fr_dict_enum_by_name(enumv, unescaped, unescaped_len)) return T_BARE_WORD;
3899
3900 /*
3901 * Leave the original quoting if it's
3902 * single or double, else default to
3903 * single quoting.
3904 */
3905 switch (existing_quote) {
3908 return existing_quote;
3909
3910 default:
3912 }
3913}
3914
3915
3916/** Convert #tmpl_t of type #TMPL_TYPE_DATA_UNRESOLVED or #TMPL_TYPE_DATA to #TMPL_TYPE_DATA of type specified
3917 *
3918 * @note Conversion is done in place.
3919 * @note Irrespective of whether the #tmpl_t was #TMPL_TYPE_DATA_UNRESOLVED or #TMPL_TYPE_DATA,
3920 * on successful cast it will be #TMPL_TYPE_DATA.
3921 *
3922 * @param[in,out] vpt The template to modify. Must be of type #TMPL_TYPE_DATA_UNRESOLVED
3923 * or #TMPL_TYPE_DATA.
3924 * @param[in] type to cast to.
3925 * @param[in] enumv Enumerated dictionary values associated with a #fr_dict_attr_t.
3926 * @return
3927 * - 0 on success.
3928 * - -1 on failure.
3929 */
3931{
3933
3935
3936 switch (vpt->type) {
3938 {
3939 char *unescaped = vpt->data.unescaped;
3940
3941 /*
3942 * We're trying to convert an unresolved (bareword)
3943 * tmpl to octets.
3944 *
3945 * tmpl_afrom_substr uses the 0x prefix as type
3946 * inference, so if it was a hex string the tmpl
3947 * type would not have fallen through to
3948 * unresolved.
3949 *
3950 * That means if we're trying to resolve it here
3951 * it's really a printable string, not a sequence
3952 * of hexits, so we just want the binary
3953 * representation of that string, and not the hex
3954 * to bin conversion.
3955 */
3956 if (fr_type_is_octets(type)) {
3957 if (fr_value_box_memdup(vpt, &vpt->data.literal, enumv,
3958 (uint8_t const *)unescaped, talloc_array_length(unescaped) - 1,
3959 false) < 0) return -1;
3960 } else {
3961 if (fr_value_box_from_str(vpt, &vpt->data.literal, type,
3962 enumv,
3963 unescaped, talloc_array_length(unescaped) - 1,
3964 NULL) < 0) return -1;
3965 }
3966 vpt->type = TMPL_TYPE_DATA;
3967 vpt->quote = tmpl_cast_quote(vpt->quote, type, enumv,
3968 unescaped, talloc_array_length(unescaped) - 1);
3969 talloc_free(unescaped);
3970 fr_value_box_mark_safe_for(&vpt->data.literal, vpt->rules.literals_safe_for);
3971
3972 /*
3973 * The data is now of the correct type, so we don't need to keep a cast.
3974 */
3975 vpt->rules.cast = FR_TYPE_NULL;
3976 }
3977 break;
3978
3979 case TMPL_TYPE_DATA:
3980 {
3981 if (type == tmpl_value_type(vpt)) return 0; /* noop */
3982
3983 /*
3984 * Enumerations aren't used when casting between
3985 * data types. They're only used when processing
3986 * unresolved tmpls.
3987 *
3988 * i.e. TMPL_TYPE_DATA_UNRESOLVED != TMPL_TYPE_DATA(FR_TYPE_STRING)
3989 */
3990 if (fr_value_box_cast_in_place(vpt, &vpt->data.literal, type, NULL) < 0) return -1;
3991// fr_value_box_mark_safe_for(&vpt->data.literal, vpt->rules.literals_safe_for); ??? is this necessary?
3992
3993 /*
3994 * Strings get quoted, everything else is a bare
3995 * word...
3996 */
3997 if (fr_type_is_string(type)) {
3998 vpt->quote = T_SINGLE_QUOTED_STRING;
3999 } else {
4000 vpt->quote = T_BARE_WORD;
4001 }
4002
4003 /*
4004 * The data is now of the correct type, so we don't need to keep a cast.
4005 */
4006 vpt->rules.cast = FR_TYPE_NULL;
4007 }
4008 break;
4009
4010 case TMPL_TYPE_ATTR:
4011 /*
4012 * Suppress casts to the same type.
4013 */
4014 if (tmpl_attr_tail_da(vpt)->type == type) {
4015 vpt->rules.cast = FR_TYPE_NULL;
4016 break;
4017 }
4019
4021 vpt->rules.cast = type;
4022 break;
4023
4024 default:
4025 fr_assert(0);
4026 }
4028
4029 return 0;
4030}
4031
4032/** Resolve an unresolved attribute
4033 *
4034 * Multi-pass parsing fixups for attribute references.
4035 *
4036 * @param[in] vpt to resolve.
4037 * @param[in] tr_rules Combined with the original parse rules for
4038 * additional resolution passes.
4039 * @return
4040 * - 0 if all references were resolved.
4041 * - -1 if there are unknown attributes which need
4042 * adding to the global dictionary first.
4043 * - -2 if there are attributes we couldn't resolve.
4044 */
4045static inline CC_HINT(always_inline) int tmpl_attr_resolve(tmpl_t *vpt, tmpl_res_rules_t const *tr_rules)
4046{
4047 tmpl_attr_t *ar = NULL, *next, *prev;
4048 fr_dict_attr_t const *da, *namespace;
4049 fr_dict_t const *dict_def;
4050
4052
4054
4055 dict_def = vpt->rules.attr.dict_def;
4056 if (!dict_def || tr_rules->force_dict_def) dict_def = tr_rules->dict_def;
4057
4058 /*
4059 * First component is special because we may need
4060 * to search for it in multiple dictionaries.
4061 *
4062 * This emulates what's done in the initial
4063 * tokenizer function.
4064 */
4065 ar = tmpl_attr_list_head(tmpl_attr(vpt));
4066 if (ar->type == TMPL_ATTR_TYPE_UNRESOLVED) {
4068 &da,
4069 dict_def,
4070 &FR_SBUFF_IN(ar->ar_unresolved,
4071 talloc_array_length(ar->ar_unresolved) - 1),
4072 NULL,
4073 true,
4074 vpt->rules.attr.allow_foreign);
4075 if (!da) return -2; /* Can't resolve, maybe the caller can resolve later */
4076
4077 ar->ar_type = TMPL_ATTR_TYPE_NORMAL;
4078 ar->ar_da = da;
4079 ar->ar_parent = fr_dict_root(fr_dict_by_da(da));
4080
4081 /*
4082 * Record the dictionary that was
4083 * successfully used for resolution.
4084 */
4085 vpt->rules.attr.dict_def = tr_rules->dict_def;
4086
4087 /*
4088 * Reach into the next reference
4089 * and correct its parent and
4090 * namespace.
4091 */
4092 next = tmpl_attr_list_next(tmpl_attr(vpt), ar);
4093 if (next) {
4094 next->ar_parent = da;
4095 next->ar_unresolved_namespace = da;
4096 }
4097 }
4098
4099 /*
4100 * Loop, resolving each unresolved attribute in turn
4101 */
4102 while ((ar = tmpl_attr_list_next(tmpl_attr(vpt), ar))) {
4103 switch (ar->type) {
4106 continue; /* Don't need to resolve */
4107
4109 return -1; /* Unknown attributes must be resolved first */
4110
4111 default:
4112 break;
4113 }
4114
4115 prev = tmpl_attr_list_prev(tmpl_attr(vpt), ar);
4116
4117 /*
4118 * If the parent is a list AR, then use the default dictionary for the namespace
4119 */
4120 namespace = (prev && dict_def && tmpl_attr_is_list_attr(prev)) ? fr_dict_root(dict_def) : ar->ar_unresolved_namespace;
4121
4122 (void)fr_dict_attr_by_name_substr(NULL,
4123 &da,
4124 namespace,
4125 &FR_SBUFF_IN(ar->ar_unresolved,
4126 talloc_array_length(ar->ar_unresolved) - 1),
4127 NULL);
4128 /*
4129 * Still can't resolve, check to see if
4130 * the last attribute reference was a
4131 * group.
4132 *
4133 * If it was, then we may be able to
4134 * fall back to resolving the attribute
4135 * in the internal dictionary.
4136 */
4137 if (!da) {
4138 if (prev && (prev->ar_da->type == FR_TYPE_GROUP)) {
4139 (void)fr_dict_attr_by_name_substr(NULL,
4140 &da,
4142 &FR_SBUFF_IN(ar->ar_unresolved,
4143 talloc_array_length(ar->ar_unresolved) - 1),
4144 NULL);
4145 }
4146 if (!da) return -2;
4147 }
4148
4149 /*
4150 * Known attribute, just rewrite.
4151 */
4152 ar->ar_type = TMPL_ATTR_TYPE_NORMAL;
4153 ar->ar_da = da;
4154
4155 /*
4156 * Parent should have been corrected in
4157 * the previous loop iteration.
4158 */
4159 fr_assert(ar->ar_parent && !ar->ar_parent->flags.is_unknown);
4160
4161 /*
4162 * Reach into the next reference
4163 * and correct its parent.
4164 */
4165 next = tmpl_attr_list_next(tmpl_attr(vpt), ar);
4166 if (next) {
4167 next->ar_parent = da;
4168 next->ar_unresolved_namespace = da;
4169 }
4170
4171 /*
4172 * Remove redundant attributes
4173 *
4174 * If it's not a group or does not specify
4175 * an index, the ar is redundant and should
4176 * be removed.
4177 */
4178 prev = tmpl_attr_list_prev(tmpl_attr(vpt), ar);
4179 if (prev && (prev->ar_da->type != FR_TYPE_GROUP) && (prev->ar_num == NUM_UNSPEC)) {
4180 tmpl_attr_list_remove(tmpl_attr(vpt), prev);
4181 ar->ar_parent = prev->ar_parent;
4182 talloc_free(prev);
4183 }
4184 }
4185
4186 RESOLVED_SET(&vpt->type);
4188
4189 return 0;
4190}
4191
4192/** Resolve an unresolved xlat, i.e. one containing unresolved attribute references or xlat functions
4193 *
4194 * Multi-pass parsing fixups for attribute references.
4195 *
4196 * Works for base types:
4197 * - TMPL_TYPE_XLAT
4198 * - TMPL_TYPE_EXEC
4199 * - TMPL_TYPE_REGEX_XLAT
4200 *
4201 * @param[in] vpt Containing the xlat expansion to resolve.
4202 * @param[in] tr_rules Combined with the original parse rules for
4203 * additional resolution passes.
4204 * @return
4205 * - 0 on success.
4206 * - -1 on failure.
4207 */
4208static inline CC_HINT(always_inline)
4210{
4211 if (xlat_resolve(vpt->data.xlat.ex,
4213 .tr_rules = tr_rules,
4214 .allow_unresolved = false
4215 }) < 0) return -1;
4216
4217 fr_assert(!xlat_needs_resolving(vpt->data.xlat.ex));
4218
4219 RESOLVED_SET(&vpt->type);
4221
4222 return 0;
4223}
4224
4225/** Attempt to resolve functions and attributes in xlats and attribute references
4226 *
4227 * @note If resolution is successful, the rules->attr.dict_def field will be modified to
4228 * reflect the dictionary resolution was successful in.
4229 *
4230 * @param[in,out] vpt to resolve. Should be of type TMPL_TYPE_XLAT_UNRESOLVED
4231 * or TMPL_TYPE_ATTR_UNRESOLVED. All other types will be
4232 * noops.
4233 * @param[in] tr_rules Combined with the original parse rules for
4234 * additional resolution passes.
4235 * @return
4236 * - 0 on success.
4237 * - -1 on failure.
4238 */
4240{
4241 static tmpl_res_rules_t const default_tr_rules;
4242
4243 int ret = 0;
4244
4245 if (!tmpl_needs_resolving(vpt)) return 0; /* Nothing to do */
4246
4247 if (!tr_rules) tr_rules = &default_tr_rules;
4248
4249 /*
4250 * Sanity check. There shouldn't be conflicting
4251 * enumvs between the original rules and resolution
4252 * rules.
4253 *
4254 * Either the enumv was available during parsing
4255 * and shouldn't have changed during subsequent
4256 * resolution passes, or it wasn't available at
4257 * parse-time, but now is.
4258 */
4259 if (tr_rules->enumv && tmpl_rules_enumv(vpt) && !tmpl_rules_enumv(vpt)->flags.is_unknown &&
4260 (tr_rules->enumv != tmpl_rules_enumv(vpt))) {
4261 fr_strerror_printf("mismatch between parse-time enumv '%s' and resolution-time enumv '%s'",
4262 tmpl_rules_enumv(vpt)->name, tr_rules->enumv->name);
4263
4264 return -1;
4265 }
4266
4267 /*
4268 * The xlat component of the #tmpl_t needs resolving.
4269 *
4270 * This includes exec tmpls, which are largely xlats
4271 * "under the hood".
4272 */
4273 if (tmpl_contains_xlat(vpt)) {
4274 ret = tmpl_xlat_resolve(vpt, tr_rules);
4275
4276 /*
4277 * The attribute reference needs resolving.
4278 */
4279 } else if (tmpl_contains_attr(vpt)) {
4280 fr_type_t dst_type = tmpl_rules_cast(vpt);
4281
4282 fr_assert(vpt->quote == T_BARE_WORD); /* 'User-Name' or "User-Name" is not allowed. */
4283
4284 ret = tmpl_attr_resolve(vpt, tr_rules);
4285 if (ret < 0) return ret;
4286
4287 if (dst_type == tmpl_attr_tail_da(vpt)->type) {
4288 vpt->rules.cast = FR_TYPE_NULL;
4289 }
4290
4291 /*
4292 * Convert unresolved tmpls into enumvs, or failing that, string values.
4293 *
4294 * Unresolved tmpls are by definition TMPL_TYPE_DATA.
4295 */
4296 } else if (tmpl_is_data_unresolved(vpt)) {
4297 fr_type_t dst_type = tmpl_rules_cast(vpt);
4298 fr_dict_attr_t const *enumv = tmpl_rules_enumv(vpt);
4299
4300 /*
4301 * If there wasn't an enumv set in the
4302 * original rules, and we now have one
4303 * (possibly because the other side of a
4304 * binary expression has been resolved),
4305 * then use the new enumv.
4306 */
4307 if (!enumv) enumv = tr_rules->enumv;
4308
4309 /*
4310 * We don't have an explicit output type. Try to
4311 * interpret the data os the enumv data type, OR
4312 * if all else fails, it's a string.
4313 */
4314 if (fr_type_is_null(dst_type)) {
4315 /*
4316 * Infer the cast from the enumv type.
4317 */
4318 if (enumv) {
4319 dst_type = enumv->type;
4320
4321 } else if (vpt->quote != T_BARE_WORD) {
4322 dst_type = FR_TYPE_STRING; /* quoted strings are strings */
4323
4324 } else if (strncmp(vpt->data.unescaped, "::", 2) != 0) {
4325 /*
4326 * The rest of the code should have errored out before this.
4327 */
4328 fr_strerror_printf("Failed resolving data '%s' - it is not an attribute name or a quoted string", vpt->data.unescaped);
4329 return -1;
4330
4331 } else {
4332 /*
4333 * It's a valid enum ::NAME which was added _after_ the dictionaries were
4334 * loaded. That's fine. fr_value_box_from_substr() will skip over the
4335 * "::", and parse the enum name.
4336 */
4337 }
4338 }
4339
4340 /*
4341 * tmpl_cast_in_place first resolves using
4342 * the enumv, _then_ casts using the type.
4343 */
4344 if (tmpl_cast_in_place(vpt, dst_type, enumv) < 0) return -1;
4345
4347 /*
4348 * Catch any other cases of unresolved things
4349 * we need to address. We put the assert here
4350 * so we don't end up running inappropriate
4351 * code for non-debug builds.
4352 */
4353 } else {
4354 fr_assert(0);
4355 }
4356
4358
4359 return ret;
4360}
4361
4362/** Reset the tmpl, leaving only the name in place
4363 *
4364 * After calling this function, the tmpl type will revert to TMPL_TYPE_DATA_UNRESOLVED
4365 * and only the name and quoting will be preserved.
4366 *
4367 * @param[in] vpt to reset.
4368 */
4370{
4371 tmpl_t tmp = {
4373 .name = vpt->name,
4374 .len = vpt->len,
4375 .quote = vpt->quote
4376 };
4377
4378 switch (vpt->type) {
4380 case TMPL_TYPE_MAX:
4381 fr_assert(0);
4382 break;
4383
4386 break;
4387
4388 case TMPL_TYPE_DATA:
4389 fr_value_box_clear(&vpt->data.literal);
4390 break;
4391
4392 /*
4393 * These types contain dynamically allocated
4394 * attribute and request references.
4395 */
4396 case TMPL_TYPE_ATTR:
4398 tmpl_attr_list_talloc_free(tmpl_attr(vpt));
4399 tmpl_request_list_talloc_free(&vpt->data.attribute.rr);
4400 break;
4401
4402 /*
4403 * These all store an xlat expansion
4404 */
4405 case TMPL_TYPE_EXEC:
4406 case TMPL_TYPE_XLAT:
4411 TALLOC_FREE(vpt->data.xlat.ex);
4412 break;
4413
4414 case TMPL_TYPE_REGEX:
4415 talloc_free(vpt->data.reg.ex);
4416 break;
4417
4418 }
4419
4420 memcpy(vpt, &tmp, sizeof(*vpt));
4421
4423}
4424
4426{
4427 switch (ref->type) {
4429 {
4430 ref->da = ref->ar_unknown = fr_dict_attr_unknown_afrom_da(vpt, ref->da);
4431 if (!ref->da) return -1;
4432
4433 ref->ar_unknown->type = FR_TYPE_OCTETS;
4434 ref->is_raw = 1;
4436 }
4437 break;
4438 case TMPL_ATTR_TYPE_UNSPEC: /* noop */
4439 break;
4440
4442 ref->ar_unknown->type = FR_TYPE_OCTETS;
4443 ref->is_raw = 1;
4444 break;
4445
4447 ref->is_raw = true;
4448 break;
4449 }
4450
4452
4453 return 0;
4454}
4455
4456/** Add an unknown #fr_dict_attr_t specified by a #tmpl_t to the main dictionary
4457 *
4458 * @param vpt to add. ``tmpl_attr_tail_da`` pointer will be updated to point to the
4459 * #fr_dict_attr_t inserted into the dictionary.
4460 * @return
4461 * - 1 noop (did nothing) - Not possible to convert tmpl.
4462 * - 0 on success.
4463 * - -1 on failure.
4464 */
4466{
4467 tmpl_attr_t *ar = NULL, *next = NULL;
4468
4469 if (!vpt) return 1;
4470
4471 /*
4472 * Can't do this for expressions parsed at runtime
4473 */
4474 if (vpt->rules.at_runtime) return 1;
4475
4477
4479
4480 if (!tmpl_attr_tail_is_unknown(vpt)) return 1; /* Ensure at least the leaf is unknown */
4481
4482 while ((ar = tmpl_attr_list_next(tmpl_attr(vpt), ar))) {
4483 fr_dict_attr_t const *unknown, *known;
4484
4485 switch (ar->type) {
4486 case TMPL_ATTR_TYPE_NORMAL: /* Skip */
4488 continue;
4489
4490 case TMPL_ATTR_TYPE_UNRESOLVED: /* Shouldn't have been called */
4491 fr_strerror_const("Remaining attributes are unresolved");
4492 return -1;
4493
4495 break;
4496 }
4497
4498 unknown = ar->ar_unknown;
4499 known = fr_dict_attr_unknown_add(fr_dict_unconst(fr_dict_by_da(unknown)), unknown);
4500 if (!known) return -1;
4501
4502 /*
4503 * Fixup the parent of the next unknown
4504 * now it's known.
4505 */
4506 next = tmpl_attr_list_next(tmpl_attr(vpt), ar);
4507 if (next && (next->type == TMPL_ATTR_TYPE_UNKNOWN) &&
4508 (next->ar_da->parent == unknown)) {
4510 known) < 0) return -1;
4511 next->ar_parent = known;
4512 }
4513
4514 /*
4515 * Convert the ref to a normal type.
4516 * At runtime there should be no
4517 * "unknown" references as they should
4518 * have all been added to a
4519 * dictionary.
4520 */
4522
4523 /*
4524 * If the attribute is *NOT* raw then
4525 * swap the canonical unknown with the
4526 * one that was previously associated
4527 * with the tmpl.
4528 *
4529 * This establishes the unknown attribute
4530 * in the dictionary if it was really
4531 * unknown whilst not mucking up the
4532 * types for raw attributes.
4533 */
4534 if (!ar_is_raw(ar)) {
4535 fr_dict_attr_unknown_free(&ar->ar_da);
4536 ar->ar_da = known;
4537 } else if (!fr_cond_assert(!next)) {
4538 fr_strerror_const("Only the leaf may be raw");
4539 return -1;
4540 }
4541 }
4542
4543 return 0;
4544}
4545
4546/** Add an unresolved #fr_dict_attr_t specified by a #tmpl_t to the main dictionary
4547 *
4548 * @note fr_dict_attr_add will not return an error if the attribute already exists
4549 * meaning that multiple #tmpl_t specifying the same attribute can be
4550 * passed to this function to be fixed up, so long as the type and flags
4551 * are identical.
4552 *
4553 * @param[in] dict_def Default dictionary to use if none is
4554 * specified by the tmpl_attr_tail_unresolved.
4555 * @param[in] vpt specifying unresolved attribute to add.
4556 * ``tmpl_attr_tail_da`` pointer will be updated to
4557 * point to the #fr_dict_attr_t inserted
4558 * into the dictionary. Lists and requests
4559 * will be preserved.
4560 * @param[in] type to define unresolved attribute as.
4561 * @param[in] flags to define unresolved attribute with.
4562 * @return
4563 * - 1 noop (did nothing) - Not possible to convert tmpl.
4564 * - 0 on success.
4565 * - -1 on failure.
4566 */
4568 fr_type_t type, fr_dict_attr_flags_t const *flags)
4569{
4570 fr_dict_attr_t const *da;
4571 fr_dict_attr_flags_t our_flags = *flags;
4572
4573 our_flags.name_only = true;
4574
4575 if (!vpt) return -1;
4576
4578
4579 if (!tmpl_is_attr_unresolved(vpt)) return 1;
4580
4581 if (fr_dict_attr_add(dict_def,
4583 return -1;
4584 }
4586 if (!da) return -1;
4587
4588 if (type != da->type) {
4589 fr_strerror_printf("Attribute %s of type %s already defined with type %s",
4590 da->name, fr_type_to_str(type),
4591 fr_type_to_str(da->type));
4592 return -1;
4593 }
4594
4595 if (memcmp(flags, &da->flags, sizeof(*flags)) != 0) {
4596 fr_strerror_printf("Attribute %s already defined with different flags", da->name);
4597 return -1;
4598 }
4599
4600 tmpl_attr_set_da(vpt, da);
4601 vpt->type = TMPL_TYPE_ATTR;
4602
4603 return 0;
4604}
4605
4606#ifdef HAVE_REGEX
4607/** Convert a TMPL_TYPE_REGEX_UNCOMPILED into a TMPL_TYPE_REGEX
4608 *
4609 * Other regex types become noops.
4610 */
4611ssize_t tmpl_regex_compile(tmpl_t *vpt, bool subcaptures)
4612{
4613 ssize_t slen;
4614 char *unescaped = vpt->data.unescaped;
4615
4616 if (tmpl_is_regex_xlat(vpt) || tmpl_is_regex(vpt)) return 0; /* Don't need compiling */
4617
4619
4620 slen = regex_compile(vpt, &vpt->data.reg.ex,
4621 unescaped, talloc_array_length(unescaped) - 1,
4622 &vpt->data.reg_flags, subcaptures, vpt->rules.at_runtime);
4623 if (slen <= 0) return vpt->quote != T_BARE_WORD ? slen - 1 : slen; /* Account for the quoting */
4624
4625 vpt->type = TMPL_TYPE_REGEX;
4626 vpt->data.reg.src = unescaped; /* Keep this around for debugging and copying */
4627 vpt->data.reg.subcaptures = subcaptures;
4628
4630
4631 return slen;
4632}
4633#endif
4634/** @} */
4635
4636/** @name Print the contents of a #tmpl_t
4637 * @{
4638 */
4640{
4641 fr_sbuff_t our_out = FR_SBUFF(out);
4642 tmpl_request_t *rr = tmpl_request_list_head(rql);
4643
4644 /*
4645 * Print request references
4646 */
4647 while (rr) {
4648 FR_SBUFF_IN_TABLE_STR_RETURN(&our_out, tmpl_request_ref_table, rr->request, "<INVALID>");
4649 rr = tmpl_request_list_next(rql, rr);
4650 if (rr) FR_SBUFF_IN_CHAR_RETURN(&our_out, '.');
4651 }
4652
4653 FR_SBUFF_SET_RETURN(out, &our_out);
4654}
4655
4656/** Print an attribute or list #tmpl_t to a string
4657 *
4658 * This function is the direct counterpart to #tmpl_afrom_attr_substr.
4659 *
4660 * @param[in] out Where to write the presentation format #tmpl_t string.
4661 * @param[in] vpt to print.
4662 * @return
4663 * - >0 the number of bytes written to the out buffer.
4664 * - 0 invalid argument.
4665 * - <0 the number of bytes we would have needed to complete the print.
4666 */
4668{
4669 tmpl_attr_t *ar = NULL;
4671 fr_sbuff_t our_out = FR_SBUFF(out);
4672 fr_slen_t slen;
4673
4675
4676 /*
4677 * Only print things we can print...
4678 */
4679 switch (vpt->type) {
4681 case TMPL_TYPE_ATTR:
4682 break;
4683
4684 default:
4685 fr_assert(0);
4686 return 0;
4687 }
4688
4689 /*
4690 * Print request references
4691 */
4692 slen = tmpl_request_ref_list_print(&our_out, &vpt->data.attribute.rr);
4693 if (slen > 0) FR_SBUFF_IN_CHAR_RETURN(&our_out, '.');
4694 if (slen < 0) return slen;
4695
4696 /*
4697 *
4698 * If the leaf attribute is unknown and raw we
4699 * add the raw. prefix.
4700 *
4701 * If the leaf attribute is unknown and not raw
4702 * we add the .unknown prefix.
4703 *
4704 */
4706
4707 /*
4708 * Print attribute identifiers
4709 */
4710 ar = NULL;
4711 while ((ar = tmpl_attr_list_next(tmpl_attr(vpt), ar))) {
4712 switch(ar->type) {
4714 break;
4715
4718 {
4719 int i, depth = 0;
4720
4721 fr_assert(ar->ar_parent); /* All normal and unknown attributes must have parents */
4722
4723 fr_proto_da_stack_build_partial(&stack, ar->ar_parent, ar->ar_da);
4724
4725 /*
4726 * First component in the list has everything built
4727 */
4728 if (ar == tmpl_attr_list_head(tmpl_attr(vpt))) {
4729 depth = ar->ar_parent->depth - 1; /* Adjust for array index */
4730 /*
4731 * Everything else skips the first component
4732 */
4733 } else {
4734 depth = ar->ar_parent->depth;
4735 }
4736
4737 /*
4738 * Root attributes will be skipped by the build
4739 * function, so da[0] contains the attribute
4740 * we're looking for.
4741 */
4742 if (depth < 0) depth = 0;
4743
4744 /*
4745 * Print from our parent depth to the AR we're processing
4746 *
4747 * For refs we skip the attribute pointed to be the ref
4748 * and just print its children.
4749 *
4750 * In addition skip printing "request." in most cases.
4751 */
4752 if ((stack.da[depth] == request_attr_request) && tmpl_attr_list_next(tmpl_attr(vpt), ar) &&
4753 (ar->filter.type == TMPL_ATTR_FILTER_TYPE_NONE)) continue;
4754
4755 for (i = depth; (unsigned int)i < ar->ar_da->depth; i++) {
4756 FR_SBUFF_IN_STRCPY_RETURN(&our_out, stack.da[i]->name);
4757
4758 /*
4759 * Print intermediary separators
4760 * if necessary.
4761 */
4762 if (((unsigned int)i + 1) < ar->ar_da->depth) FR_SBUFF_IN_CHAR_RETURN(&our_out, '.');
4763 }
4764 }
4765 break;
4766
4767 /*
4768 * For unresolved attribute we print the raw identifier we
4769 * got when parsing the tmpl.
4770 */
4772 {
4773 unsigned int i, depth;
4774
4775 /*
4776 * This is the first unresolved component in a potential
4777 * chain of unresolved components. Print the path up to
4778 * the last known parent.
4779 */
4780 if (ar->ar_parent && !ar->ar_parent->flags.is_root) {
4781 fr_proto_da_stack_build_partial(&stack, ar->ar_parent, ar->ar_parent);
4782 if (ar->ar_parent->flags.is_root) {
4783 depth = 0;
4784 } else {
4785 depth = ar->ar_parent->depth - 1;
4786 }
4787
4788 for (i = depth; i < ar->ar_parent->depth; i++) {
4789 FR_SBUFF_IN_STRCPY_RETURN(&our_out, stack.da[i]->name);
4790 FR_SBUFF_IN_CHAR_RETURN(&our_out, '.');
4791 }
4792 }
4793 /*
4794 * Then print the unresolved component
4795 */
4796 FR_SBUFF_IN_BSTRCPY_BUFFER_RETURN(&our_out, ar->ar_unresolved);
4797 break;
4798 }
4799 }
4800
4801 if (ar_filter_is_none(ar)) {
4802 /* do nothing */
4803
4804 } else if (ar_filter_is_num(ar)) {
4805 switch (ar->ar_num) {
4806 case NUM_UNSPEC:
4807 break;
4808
4809 case NUM_ALL:
4810 FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "[*]");
4811 break;
4812
4813 case NUM_COUNT:
4814 FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "[#]");
4815 break;
4816
4817 case NUM_LAST:
4818 FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "[n]");
4819 break;
4820
4821 default:
4822 FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "[%i]", ar->ar_num);
4823 break;
4824 }
4825
4826 } else if (ar_filter_is_cond(ar)) {
4827 FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "[");
4828 (void) xlat_print(&our_out, ar->ar_cond, NULL);
4829 FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "]");
4830
4831 } else {
4832 fr_assert(0);
4833 }
4834
4835 if (tmpl_attr_list_next(tmpl_attr(vpt), ar)) FR_SBUFF_IN_CHAR_RETURN(&our_out, '.');
4836 }
4837 FR_SBUFF_SET_RETURN(out, &our_out);
4838}
4839
4840/** Print a #tmpl_t to a string
4841 *
4842 * This function should primarily be used for regenerating vpt->name when the contents
4843 * of the #tmpl_t is changed programmatically, or when the #tmpl_t is being serialized
4844 * in some non-standard way, i.e. as a value for a field in a database.
4845 *
4846 * This function is the direct counterpart to #tmpl_afrom_substr.
4847 *
4848 * @note Does not print flags for regular expressions, as the quoting char is needed
4849 * to separate the elements of the expression.
4850 * Call regex_flags_print to write the flags values to the output buffer.
4851 *
4852 * @param[out] out Where to write the presentation format #tmpl_t string.
4853 * @param[in] vpt to print.
4854 * @param[in] e_rules Escaping rules used to print strings.
4855 * @return
4856 * - >0 the number of bytes written to the out buffer.
4857 * - 0 invalid argument.
4858 * - <0 the number of bytes we would have needed to complete the print.
4859 */
4861 fr_sbuff_escape_rules_t const *e_rules)
4862{
4863 fr_sbuff_t our_out = FR_SBUFF(out);
4864
4866
4867 switch (vpt->type) {
4869 case TMPL_TYPE_ATTR:
4871 break;
4872
4873 case TMPL_TYPE_DATA:
4874 FR_SBUFF_RETURN(fr_value_box_print, &our_out, tmpl_value(vpt), e_rules);
4875 break;
4876
4877 case TMPL_TYPE_REGEX:
4878 FR_SBUFF_IN_BSTRNCPY_RETURN(&our_out, vpt->name, vpt->len); /* Fixme - double escapes */
4879 break;
4880
4882 FR_SBUFF_IN_ESCAPE_BUFFER_RETURN(&our_out, vpt->data.unescaped, e_rules);
4883 break;
4884
4886 case TMPL_TYPE_MAX:
4887 fr_sbuff_terminate(out);
4888 break;
4889
4890 /*
4891 * The remaining types will either
4892 * be xlat expansions, or need
4893 * resolving, in which case the
4894 * unescaped string is available
4895 * in vpt->unescaped.
4896 */
4897 default:
4898 if (tmpl_contains_xlat(vpt)) {
4899 FR_SBUFF_RETURN(xlat_print, &our_out, tmpl_xlat(vpt), e_rules);
4900 break;
4901 }
4902
4904 FR_SBUFF_IN_ESCAPE_BUFFER_RETURN(&our_out, vpt->data.unescaped, e_rules);
4905 break;
4906 }
4907
4908 fr_assert_fail("Can't print invalid tmpl type %s", tmpl_type_to_str(vpt->type));
4909
4910 /*
4911 * Ensure we do something sane for non-debug builds
4912 */
4913 fr_sbuff_terminate(out);
4914 return 0;
4915 }
4916
4917 FR_SBUFF_SET_RETURN(out, &our_out);
4918}
4919
4920/** Print a #tmpl_t to a string with quotes
4921 *
4922 * This function should be used when the tmpl is embedded in some other construct
4923 * in the server's configuration.
4924 *
4925 * It adds standard quoting around tmpl's used as operands in expressions and applies
4926 * the correct escaping rules.
4927 *
4928 * @param[out] out Where to write the presentation format #tmpl_t string.
4929 * @param[in] vpt to print.
4930 * @return
4931 * - >0 the number of bytes written to the out buffer.
4932 * - 0 invalid argument.
4933 * - <0 the number of bytes we would have needed to complete the print.
4934 */
4936{
4937 fr_sbuff_t our_out = FR_SBUFF(out);
4938
4939 char quote = fr_token_quote[vpt->quote];
4940
4941 if (quote != '\0') FR_SBUFF_IN_CHAR_RETURN(&our_out, quote);
4942 FR_SBUFF_RETURN(tmpl_print, &our_out, vpt,
4944 if (quote != '\0') FR_SBUFF_IN_CHAR_RETURN(&our_out, quote);
4945
4946 /*
4947 * Optionally print the flags
4948 */
4949 if (vpt->type & TMPL_FLAG_REGEX) FR_SBUFF_RETURN(regex_flags_print, &our_out, tmpl_regex_flags(vpt));
4950
4951 FR_SBUFF_SET_RETURN(out, &our_out);
4952}
4953/** @} */
4954
4955
4956#ifdef WITH_VERIFY_PTR
4957/** Used to check whether areas of a tmpl_t are zeroed out
4958 *
4959 * @param ptr Offset to begin checking at.
4960 * @param len How many bytes to check.
4961 * @return
4962 * - Pointer to the first non-zero byte.
4963 * - NULL if all bytes were zero.
4964 */
4965static uint8_t const *is_zeroed(uint8_t const *ptr, size_t len)
4966{
4967 size_t i;
4968
4969 for (i = 0; i < len; i++) {
4970 if (ptr[i] != 0x00) return ptr + i;
4971 }
4972
4973 return NULL;
4974}
4975
4976/** Verify that unused regions of the struct are zeroed out
4977 *
4978 */
4979#define CHECK_ZEROED(_vpt, _field) is_zeroed(((uint8_t const *)&(_vpt)->data) + sizeof((_vpt)->data._field), sizeof((_vpt)->data) - sizeof((_vpt)->data._field))
4980
4981
4982/** Print hex data
4983 *
4984 */
4985#define PRINT_NON_ZEROED(_vpt, _field, _nz_ptr) \
4986do { \
4987 DEBUG("Expected live portion %p-%p (0-%zu)", \
4988 _vpt, \
4989 (uint8_t const *)&(_vpt)->data + sizeof((_vpt)->data._field), \
4990 sizeof((_vpt)->data._field)); \
4991 DEBUG("Expected zero portion %p-%p (%zu-%zu)", \
4992 (uint8_t const *)&(_vpt)->data + sizeof((_vpt)->data._field), \
4993 (uint8_t const *)&(_vpt)->data + sizeof((_vpt)->data), \
4994 sizeof((_vpt)->data._field), sizeof((_vpt)->data)); \
4995 HEX_MARKER1((uint8_t const *)&vpt->data, sizeof(vpt->data), nz - (uint8_t const *)&vpt->data, "non-zero memory", ""); \
4996} while (0)
4997
4998
4999/** Verify the attribute reference in a tmpl_t make sense
5000 *
5001 * @note If the attribute reference is is invalid, causes the server to exit.
5002 *
5003 * @param file obtained with __FILE__.
5004 * @param line obtained with __LINE__.
5005 * @param vpt to check.
5006 */
5007void tmpl_attr_verify(char const *file, int line, tmpl_t const *vpt)
5008{
5009 tmpl_attr_t *ar = NULL;
5010 tmpl_attr_t *slow = NULL, *fast = NULL;
5011 tmpl_attr_t *seen_unknown = NULL;
5012 tmpl_attr_t *seen_unresolved = NULL;
5013
5015
5016 /*
5017 * Loop detection
5018 */
5019 while ((slow = tmpl_attr_list_next(tmpl_attr(vpt), slow)) &&
5020 (fast = tmpl_attr_list_next(tmpl_attr(vpt), fast))) {
5021
5022 /*
5023 * Advances twice as fast as slow...
5024 */
5025 fast = tmpl_attr_list_next(tmpl_attr(vpt), fast);
5026 fr_fatal_assert_msg(fast != slow,
5027 "CONSISTENCY CHECK FAILED %s[%u]: Looping reference list found. "
5028 "Fast pointer hit slow pointer at \"%s\"",
5029 file, line,
5030 slow->type == TMPL_ATTR_TYPE_UNRESOLVED ? slow->ar_unresolved :
5031 slow->da ? slow->da->name : "(null-attr)");
5032 }
5033
5034 /*
5035 * Lineage type check
5036 *
5037 * Known attribute cannot come after unresolved or unknown attributes
5038 * Unknown attributes cannot come after unresolved attributes
5039 */
5040 if (!tmpl_is_list(vpt)) while ((ar = tmpl_attr_list_next(tmpl_attr(vpt), ar))) {
5041 switch (ar->type) {
5043 if (seen_unknown) {
5044 tmpl_attr_debug(stderr, vpt);
5045 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: "
5046 "TMPL_TYPE_ATTR known attribute \"%s\" "
5047 "occurred after unknown attribute %s "
5048 "in attr ref list",
5049 file, line,
5050 ar->da->name,
5051 ar->unknown.da->name);
5052 }
5053 if (seen_unresolved) {
5054 tmpl_attr_debug(stderr, vpt);
5055 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: "
5056 "TMPL_TYPE_ATTR known attribute \"%s\" "
5057 "occurred after unresolved attribute \"%s\""
5058 "in attr ref list",
5059 file, line,
5060 ar->da->name,
5061 ar->ar_unresolved);
5062 }
5063 fr_fatal_assert_msg(ar->ar_parent,
5064 "CONSISTENCY CHECK FAILED %s[%u]: attr ref missing parent",
5065 file, line);
5066
5067 if (ar->ar_parent->type != FR_TYPE_GROUP) {
5068 fr_fatal_assert_msg(ar->ar_parent == ar->ar_da->parent,
5069 "CONSISTENCY CHECK FAILED %s[%u]: attr ref has wrong parent: "
5070 "Expected %s, got %s",
5071 file, line,
5072 ar->ar_da->parent->name,
5073 ar->ar_parent->name);
5074
5075 }
5076 break;
5077
5079 if (seen_unknown) {
5080 tmpl_attr_debug(stderr, vpt);
5081 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: "
5082 "TMPL_TYPE_ATTR unspecified attribute "
5083 "occurred after unknown attribute %s "
5084 "in attr ref list",
5085 file, line,
5086 ar->unknown.da->name);
5087 }
5088 if (seen_unresolved) {
5089 tmpl_attr_debug(stderr, vpt);
5090 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: "
5091 "TMPL_TYPE_ATTR unspecified attribute "
5092 "occurred after unresolved attribute \"%s\""
5093 "in attr ref list",
5094 file, line,
5095 ar->ar_unresolved);
5096 }
5097 break;
5098
5100 seen_unresolved = ar;
5101 fr_fatal_assert_msg(ar->ar_unresolved_namespace,
5102 "CONSISTENCY CHECK FAILED %s[%u]: unresolved attr ref missing namespace",
5103 file, line);
5104 break;
5105
5107 seen_unknown = ar;
5108 if (seen_unresolved) {
5109 tmpl_attr_debug(stderr, vpt);
5110 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: "
5111 "TMPL_TYPE_ATTR unknown attribute \"%s\" "
5112 "occurred after unresolved attribute %s "
5113 "in attr ref list",
5114 file, line, ar->da->name,
5115 ar->ar_unresolved);
5116 }
5117 break;
5118 }
5119 }
5120}
5121
5122/** Verify fields of a tmpl_t make sense
5123 *
5124 * @note If the #tmpl_t is invalid, causes the server to exit.
5125 *
5126 * @param file obtained with __FILE__.
5127 * @param line obtained with __LINE__.
5128 * @param vpt to check.
5129 */
5130void tmpl_verify(char const *file, int line, tmpl_t const *vpt)
5131{
5132 uint8_t const *nz;
5133
5134 fr_assert(vpt);
5135
5137 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: tmpl_t type was "
5138 "TMPL_TYPE_UNINITIALISED (uninitialised)", file, line);
5139 }
5140
5141 if (vpt->type >= TMPL_TYPE_MAX) {
5142 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: tmpl_t type was %i "
5143 "(outside range of tmpl_type_table)", file, line, vpt->type);
5144 }
5145
5146 if (!vpt->name && (vpt->quote != T_INVALID)) {
5147 char quote = vpt->quote >= T_TOKEN_LAST ? '?' : fr_token_quote[vpt->quote];
5148
5149 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: Quote type '%c' (%i) was set for NULL name",
5150 file, line, quote, vpt->quote);
5151 }
5152
5153 if (vpt->name && (vpt->quote == T_INVALID)) {
5154 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: No quoting type was set for name \"%.*s\"",
5155 file, line, (int)vpt->len, vpt->name);
5156 }
5157
5158 /*
5159 * Do a memcmp of the bytes after where the space allocated for
5160 * the union member should have ended and the end of the union.
5161 * These should always be zero if the union has been initialised
5162 * properly.
5163 *
5164 * If they're still all zero, do TMPL_TYPE specific checks.
5165 */
5166 switch (vpt->type) {
5168 if (!vpt->data.unescaped) {
5169 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_DATA_UNRESOLVED "
5170 "unescaped field is NULL", file, line);
5171 }
5172 break;
5173
5175 if (!vpt->data.xlat.ex) {
5176 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_XLAT "
5177 "has a NULL xlat.ex field", file, line);
5178
5179 }
5180
5181 if (!xlat_needs_resolving(vpt->data.xlat.ex)) {
5182 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_XLAT_UNRESOLVED "
5183 "does not have 'needs resolving' flag set", file, line);
5184 }
5185 break;
5186
5187 case TMPL_TYPE_XLAT:
5188 if (!vpt->data.xlat.ex) {
5189 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_XLAT "
5190 "has a NULL xlat.ex field", file, line);
5191
5192 }
5193 break;
5194
5195/* @todo When regexes get converted to xlat the flags field of the regex union is used
5196 case TMPL_TYPE_XLAT_UNRESOLVED:
5197 if (is_zeroed((uint8_t const *)&vpt->data, sizeof(vpt->data))) {
5198 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_XLAT_UNRESOLVED "
5199 "has non-zero bytes in its data union", file, line);
5200 }
5201 break;
5202
5203 case TMPL_TYPE_XLAT:
5204 if (CHECK_ZEROED(vpt, xlat)) {
5205 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_XLAT "
5206 "has non-zero bytes after the data.xlat pointer in the union", file, line);
5207 }
5208 break;
5209*/
5210
5211 case TMPL_TYPE_EXEC:
5213 /* tmpl_xlat(vpt) can be initialized */
5214 break;
5215
5217 if ((tmpl_attr_list_num_elements(tmpl_attr(vpt)) > 0) &&
5218 ((tmpl_attr_t *)tmpl_attr_list_tail(tmpl_attr(vpt)))->da) {
5219#ifndef NDEBUG
5220 tmpl_attr_debug(stderr, vpt);
5221#endif
5222 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_ATTR_UNRESOLVED contains %u "
5223 "references", file, line, tmpl_attr_list_num_elements(tmpl_attr(vpt)));
5224 }
5225 break;
5226
5227 case TMPL_TYPE_ATTR:
5228 if ((nz = CHECK_ZEROED(vpt, attribute))) {
5229 PRINT_NON_ZEROED(vpt, attribute, nz);
5230 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_ATTR "
5231 "has non-zero bytes after the data.attribute struct in the union",
5232 file, line);
5233 }
5234
5236 fr_assert(vpt->rules.cast == FR_TYPE_NULL);
5237 break;
5238 }
5239
5242 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_ATTR "
5243 "da is marked as unknown, but address is not equal to the template's "
5244 "unknown da pointer", file, line);
5245 }
5246 /*
5247 * Raw attributes may not have been added to the dictionary yet
5248 */
5249 } else {
5250 fr_dict_attr_t const *da;
5251 fr_dict_t const *dict;
5252
5253 /*
5254 * Attribute may be present with multiple names
5255 */
5257 if (!dict) {
5258 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_ATTR "
5259 "attribute \"%s\" (%s) not rooted in a dictionary",
5262 }
5263
5264 da = tmpl_attr_tail_da(vpt);
5265 if (!tmpl_attr_tail_is_raw(vpt) && (da != tmpl_attr_tail_da(vpt))) {
5266 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_ATTR "
5267 "dictionary pointer %p \"%s\" (%s) "
5268 "and global dictionary pointer %p \"%s\" (%s) differ",
5269 file, line,
5272 da, da->name,
5273 fr_type_to_str(da->type));
5274 }
5275
5276 tmpl_attr_verify(file, line, vpt);
5277 }
5278 break;
5279
5280 case TMPL_TYPE_DATA:
5281 if ((nz = CHECK_ZEROED(vpt, literal))) {
5282 PRINT_NON_ZEROED(vpt, literal, nz);
5283 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_DATA "
5284 "has non-zero bytes after the data.literal struct in the union",
5285 file, line);
5286 }
5287
5289 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_DATA type was "
5290 "FR_TYPE_NULL (uninitialised)", file, line);
5291 }
5292
5294 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_DATA type was "
5295 "%i (outside the range of fr_type_ts)", file, line, tmpl_value_type(vpt));
5296 }
5297 /*
5298 * Unlike fr_pair_ts we can't guarantee that fr_pair_t_TMPL buffers will
5299 * be talloced. They may be allocated on the stack or in global variables.
5300 */
5301 switch (tmpl_value_type(vpt)) {
5302 case FR_TYPE_STRING:
5304 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_DATA char buffer not \\0 "
5305 "terminated", file, line);
5306 }
5307 break;
5308
5309 case FR_TYPE_STRUCTURAL:
5310 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_DATA is of type TLV",
5311 file, line);
5312
5313 default:
5314 break;
5315 }
5316
5317 break;
5318
5322#ifndef HAVE_REGEX
5323 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_REGEX_XLAT_UNRESOLVED - No regex support",
5324 file, line);
5325#endif
5326 break;
5327
5328 case TMPL_TYPE_REGEX:
5329#ifdef HAVE_REGEX
5330 if (tmpl_regex(vpt) == NULL) {
5331 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_REGEX "
5332 "reg.ex field was NULL", file, line);
5333 }
5334#else
5335 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_REGEX - No regex support",
5336 file, line);
5337#endif
5338 break;
5339
5341 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_UNINITIALISED", file, line);
5342
5343 case TMPL_TYPE_MAX:
5344 fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_MAX", file, line);
5345 }
5346}
5347#endif
5348
5349static const bool array_terminal[UINT8_MAX + 1] = {
5350 [ ']' ] = true,
5351};
5352
5353#define return_P(_x) fr_strerror_const(_x);goto return_p
5354
5355/** Preparse a string in preparation for passing it to tmpl_afrom_substr()
5356 *
5357 * Note that the input string is not modified, which means that the
5358 * tmpl_afrom_substr() function MUST un-escape it.
5359 *
5360 * The caller should pass 'out' and 'outlen' to tmpl_afrom_substr()
5361 * as 'in' and 'inlen'. The caller should also pass 'type'.
5362 * The caller should also pass do_unescape=true.
5363 *
5364 * @param[out] out start of the string to parse
5365 * @param[out] outlen length of the string to parse
5366 * @param in where we start looking for the string
5367 * @param inlen length of the input string
5368 * @param[out] type token type of the string.
5369 * @return
5370 * - > 0, amount of parsed string to skip, to get to the next token
5371 * - <=0, -offset in 'start' where the parse error was located
5372 */
5373ssize_t tmpl_preparse(char const **out, size_t *outlen, char const *in, size_t inlen,
5375{
5376 char const *p = in, *end = in + inlen;
5377 char quote;
5378 char close;
5379 int depth;
5380 bool triple;
5381
5382 *type = T_INVALID;
5383
5384 while (isspace((uint8_t) *p) && (p < end)) p++;
5385 if (p >= end) return p - in;
5386
5387 switch (*p) {
5388 /*
5389 * Allow bare xlat's
5390 */
5391 case '%':
5392 if (p[1] != '{') {
5393 char const *q;
5394
5395 q = p + 1;
5396
5397 /*
5398 * Function syntax: %foo(...)
5399 */
5400 while ((q < end) && (isalnum((int) *q) || (*q == '.') || (*q == '_') || (*q == '-'))) {
5401 q++;
5402 }
5403
5404 if (*q != '(') {
5405 p++;
5406 fr_strerror_const("Invalid character after '%'");
5407 return_p:
5408 return -(p - in);
5409 }
5410
5411 /*
5412 * Return the whole %foo(...) string.
5413 */
5414 *out = p;
5415 if (*type == T_INVALID) *type = T_BARE_WORD;
5416 close = ')';
5417
5418 p = q + 1;
5419 depth = 1;
5420 goto loop;
5421 }
5422
5423 /*
5424 * For now, %{...} is treated as a double-quoted
5425 * string. Once we clean other things up, the
5426 * xlats will be treated as strongly typed values
5427 * / lists on their own.
5428 */
5429 if (*type == T_INVALID) *type = T_BARE_WORD;
5430 depth = 0;
5431 close = '}';
5432
5433 /*
5434 * Xlat's are quoted by %{...} / %(...) nesting, not by
5435 * escapes, so we need to do special escaping.
5436 */
5437 *out = p;
5438 loop:
5439 while (*p) {
5440 /*
5441 * End of expansion. Return the entire
5442 * expansion, including the enclosing %{}
5443 * characters.
5444 */
5445 if ((*p == '}') || (*p == ')')) {
5446 bool match = (*p == close);
5447
5448 p++;
5449 depth--;
5450
5451 if (depth == 0) {
5452 if (!match) break;
5453
5454 *outlen = p - (*out);
5455 return p - in;
5456 }
5457 continue;
5458 }
5459
5460 if (*p == '\\') {
5461 p++;
5462 if (!p[1]) {
5463 return_P("End of string after escape");
5464 }
5465
5466 p++;
5467 continue;
5468 }
5469
5470 if ((p[0] == '%') && ((p[1] == '{') || (p[1] == '('))) {
5471 if (!p[2]) {
5472 return_P("End of string after expansion");
5473 }
5474
5475 p += 2;
5476 depth++;
5477 continue;
5478 }
5479
5480 /*
5481 * Allow (...) and {...}
5482 */
5483 if ((*p == '{') || (*p == '(')) {
5484 p++;
5485 depth++;
5486 continue;
5487 }
5488
5489 p++;
5490 }
5491
5492 /*
5493 * End of input without end of string.
5494 * Point the error to the start of the string.
5495 */
5496 p = *out;
5497 return_P("Unterminated expansion");
5498
5499 case '/':
5500 goto bare_word;
5501
5502 case '\'':
5503 quote = *(p++);
5505 goto skip_string;
5506
5507 case '`':
5508 quote = *(p++);
5510 goto skip_string;
5511
5512 case '"':
5513 quote = *(p++);
5515
5516 /*
5517 * We're not trying to do a *correct* parsing of
5518 * every string here. We're trying to do a
5519 * simple parse that isn't wrong. We therefore
5520 * accept most anything that's vaguely well
5521 * formed, and rely on the next stage to do a
5522 * more rigorous check.
5523 */
5524 skip_string:
5525 if ((inlen > 3) && (p[0] == quote) && (p[1] == quote)) {
5526 triple = true;
5527 p += 2;
5528 } else {
5529 triple = false;
5530 }
5531 *out = p;
5532
5533 while (*p) {
5534 if (p >= end) goto unterminated;
5535
5536 /*
5537 * End of string. Tell the caller the
5538 * length of the data inside of the
5539 * string, and return the number of
5540 * characters to skip.
5541 */
5542 if (*p == quote) {
5543 if (!triple) {
5544 *outlen = p - (*out);
5545 p++;
5546 return p - in;
5547
5548 }
5549
5550 if (((end - p) >= 3) && (p[1] == quote) && (p[2] == quote)) {
5551 *outlen = p - (*out);
5552 p += 3;
5553 return p - in;
5554 }
5555
5556 p++;
5557 continue;
5558 }
5559
5560 if (*p == '\\') {
5561 p++;
5562 if (!p[1]) {
5563 return_P("End of string after escape");
5564 }
5565 }
5566 p++;
5567 }
5568
5569 /*
5570 * End of input without end of string.
5571 * Point the error to the start of the string.
5572 */
5573 unterminated:
5574 p = *out;
5575 return_P("Unterminated string");
5576
5577 case '&':
5578 *out = p; /* the output string starts with '&' */
5579 p++;
5580 quote = '[';
5581 goto skip_word;
5582
5583 default:
5584 bare_word:
5585 *out = p;
5586 quote = '['; /* foo[1] is OK */
5587
5588 skip_word:
5589 *type = T_BARE_WORD;
5590 depth = 0;
5591
5592 /*
5593 * Allow *most* things. But stop on spaces and special characters.
5594 */
5595 while (*p) {
5596 if (isspace((uint8_t) *p)) {
5597 break;
5598 }
5599
5600 if (*p == '$') {
5601 if (p[1] == '{') {
5602 p += 2;
5603 depth++;
5604 continue;
5605
5606 } else if ((p[1] == 'E') &&
5607 (p[2] == 'N') &&
5608 (p[3] == 'V') &&
5609 (p[4] == '{')) {
5610 p += 5;
5611 depth++;
5612 continue;
5613
5614 } else {
5615 /*
5616 * Bare '$' is wrong...
5617 */
5618 break;
5619 }
5620 }
5621
5622 if (*p == '%') {
5623 if (p[1] == '{') {
5624 p += 2;
5625 depth++;
5626 continue;
5627 }
5628
5629 p++;
5630 continue;
5631 }
5632
5633 /*
5634 * If we're inside of a ${...} expansion,
5635 * then allow everything until the
5636 * closing '}'. This means that we can
5637 * do ${foo[bar].baz}, among other
5638 * thingds.
5639 */
5640 if (depth > 0) {
5641 if (*p == '}') {
5642 depth--;
5643 }
5644
5645 p++;
5646 continue;
5647 }
5648
5649 /*
5650 * '-' is special. We allow it for
5651 * attribute names, BUT it's a
5652 * terminating token if the NEXT
5653 * character is '='.
5654 *
5655 * We have the same criteria for IPv6
5656 * addresses and tagged attributes. ':'
5657 * is allowed, but ':=' is a breaking
5658 * token.
5659 */
5660 if ((*p == '-') || (*p == ':')) {
5661 if (p[1] == '=') break;
5662 p++;
5663 continue;
5664 }
5665
5666 /*
5667 * Allowed in attribute names, and/or
5668 * host names and IP addresses, and IPv6 addresses.
5669 */
5670 if ((*p == '.') || (*p == '/') || (*p == '_') || (*p == '*') ||
5671 (*p == ']') || (*p == '@')) {
5672 p++;
5673 continue;
5674 }
5675
5676 /*
5677 * [...] is an IPv6 address.
5678 */
5679 if ((p == in) && (*p == '[')) {
5680 p++;
5681 continue;
5682 }
5683
5684 /*
5685 * Allow letters and numbers
5686 */
5687 if (((*p >= 'a') && (*p <= 'z')) ||
5688 ((*p >= 'A') && (*p <= 'Z')) ||
5689 ((*p >= '0') && (*p <= '9'))) {
5690 p++;
5691 continue;
5692 }
5693
5694 /*
5695 * Allow UTF-8 sequences.
5696 */
5697 if (*(uint8_t const *)p > 0x80) {
5698 p++;
5699 continue;
5700 }
5701
5702 /*
5703 * If it's an attribute reference, allow
5704 * a few more things inside of a "[...]"
5705 * block.
5706 */
5707 if (*p == '[') {
5708 if (quote != '[') {
5709 return_P("Invalid location for '['");
5710 }
5711
5712 p++;
5713
5714 /*
5715 * Allow [#], etc. But stop
5716 * immediately after the ']'.
5717 */
5718 if ((*p == '#') || (*p == '*') || (*p == 'n')) {
5719 p++;
5720
5721 } else {
5722 ssize_t slen;
5723 bool eol = false;
5724
5725 slen = fr_skip_condition(p, end, array_terminal, &eol);
5726 if (slen < 0) {
5727 p += -slen;
5728 return -(p - in);
5729 }
5730 p += slen;
5731 continue;
5732 }
5733
5734 if (*p == ']') {
5735 p++;
5736 continue;
5737 }
5738 }
5739
5740 /*
5741 * Everything else is a breaking token
5742 */
5743 break;
5744 }
5745
5746 /*
5747 * Give some slightly better error messages.
5748 */
5749 if (*p == '\\') {
5750 return_P("Unexpected escape");
5751 }
5752
5753 if ((*p == '"') || (*p == '\'') || (*p == '`')) {
5754 return_P("Unexpected start of string");
5755 }
5756
5757 if (p == *out) {
5758 return_P("Empty string is invalid");
5759 }
5760
5761 *outlen = p - (*out);
5762 break;
5763 }
5764
5765 return p - in;
5766}
5767
5768/** Return whether or not async is required for this tmpl.
5769 *
5770 * If the tmpl is needs_async, then it is async
5771 * If the tmpl is not needs_async, then it will not yield
5772 *
5773 * If the tmpl yields, then async is required.
5774 */
5776{
5777 switch (vpt->type) {
5778 case TMPL_TYPE_EXEC: /* we don't have "exec no-wait" here */
5779 case TMPL_TYPE_XLAT_UNRESOLVED: /* we have no idea, so be safe */
5780#ifndef HAVE_REGEX
5782#endif
5783 return true;
5784
5785#ifndef HAVE_REGEX
5787#endif
5788 case TMPL_TYPE_XLAT: /* synchronous xlats use unlang_interpret_synchronous() */
5789 default:
5790 return false;
5791 }
5792}
5793
5794/** Initialize a set of rules from a parent set of rules, and a parsed tmpl_t
5795 *
5796 */
5798{
5799 fr_dict_attr_t const *da;
5800 fr_dict_attr_t const *ref;
5801 fr_dict_t const *dict, *internal;
5802
5803 *out = *parent;
5804 /* don't set ->parent=parent, that is only for switching subrequest, etc. */
5805
5806 if (!tmpl_is_attr(vpt)) return;
5807
5808 da = tmpl_attr_tail_da(vpt);
5809
5810 /*
5811 * The input tmpl is a leaf. We must parse the child as
5812 * a normal attribute reference (as with the parent tmpl).
5813 */
5814 if (!fr_type_structural[da->type]) {
5815 return;
5816 }
5817
5818 if (vpt->rules.attr.request_def) {
5819 tmpl_request_ref_list_acopy(ctx, &out->attr.request_def, vpt->rules.attr.request_def);
5820 }
5821 out->attr.list_def = tmpl_list(vpt);
5822
5823 /*
5824 * Parse the child attributes in the context of the parent struct / tlv / whatever.
5825 */
5826 if (da->type != FR_TYPE_GROUP) {
5827 out->attr.dict_def = fr_dict_by_da(da);
5828 out->attr.namespace = da;
5829 return;
5830 }
5831
5832 ref = fr_dict_attr_ref(da);
5833 dict = fr_dict_by_da(ref);
5834 internal = fr_dict_internal();
5835
5836 /*
5837 * Groups MAY change dictionaries. If so, then swap the dictionary and the parent.
5838 */
5839 if ((dict != internal) && (dict != out->attr.dict_def)) {
5840 out->attr.dict_def = dict;
5841 out->attr.namespace = ref;
5842 }
5843
5844 /*
5845 * Otherwise the reference is swapping FROM a protocol
5846 * dictionary TO the internal dictionary, and TO an
5847 * internal group. We fall back to leaving well enough
5848 * alone, and leave things as-is. This allows internal
5849 * grouping attributes to appear anywhere.
5850 */
5851}
5852
5853static void tmpl_attr_rules_debug(tmpl_attr_rules_t const *at_rules)
5854{
5855 FR_FAULT_LOG("\tdict_def = %s", at_rules->dict_def ? fr_dict_root(at_rules->dict_def)->name : "");
5856 FR_FAULT_LOG("\tnamespace = %s", at_rules->namespace ? at_rules->namespace->name : "");
5857
5858 FR_FAULT_LOG("\tlist_def = %s", at_rules->list_def ? at_rules->list_def->name : "");
5859
5860 FR_FAULT_LOG("\tallow_unknown = %u", at_rules->allow_unknown);
5861 FR_FAULT_LOG("\tallow_unresolved = %u", at_rules->allow_unresolved);
5862 FR_FAULT_LOG("\tallow_wildcard = %u", at_rules->allow_wildcard);
5863 FR_FAULT_LOG("\tallow_foreign = %u", at_rules->allow_foreign);
5864 FR_FAULT_LOG("\tdisallow_filters = %u", at_rules->disallow_filters);
5865}
5866
5867
5869{
5870 FR_FAULT_LOG("\tparent = %p", rules->parent);
5871 FR_FAULT_LOG(" attr {");
5872 tmpl_attr_rules_debug(&rules->attr);
5873 FR_FAULT_LOG(" }");
5874 FR_FAULT_LOG("\tenumv = %s", rules->enumv ? rules->enumv->name : "");
5875 FR_FAULT_LOG("\tcast = %s", fr_type_to_str(rules->cast));
5876 FR_FAULT_LOG("\tat_runtime = %u", rules->at_runtime);
5877 FR_FAULT_LOG("\tliterals_safe_for = %lx", rules->literals_safe_for);
5878
5879}
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:364
#define FR_DBUFF_TMP(_start, _len_or_end)
Creates a compound literal to pass into functions which accept a dbuff.
Definition dbuff.h:524
#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:102
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:3845
fr_dict_t * fr_dict_unconst(fr_dict_t const *dict)
Coerce to non-const.
Definition dict_util.c:4917
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:2895
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:2344
static fr_slen_t err
Definition dict.h:887
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:609
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:589
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:3552
fr_dict_attr_t * fr_dict_attr_unconst(fr_dict_attr_t const *da)
Coerce to non-const.
Definition dict_util.c:4929
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:616
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition dict_util.c:2689
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:258
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:4942
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:3264
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:3293
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:522
fr_dict_attr_err_t
Errors returned by attribute lookup functions.
Definition dict.h:318
@ FR_DICT_ATTR_OK
No error.
Definition dict.h:319
@ FR_DICT_ATTR_NOT_DESCENDENT
Attribute is not a descendent of the parent attribute.
Definition dict.h:325
@ FR_DICT_ATTR_NO_CHILDREN
Child lookup in attribute with no children.
Definition dict.h:329
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:1998
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:3617
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:3725
static fr_slen_t in
Definition dict.h:887
#define FR_DICT_ATTR_MAX_NAME_LEN
Maximum length of a attribute name.
Definition dict.h:502
Values of the encryption flags.
Value of an enumerated attribute.
Definition dict.h:254
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:178
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_char(_sbuff_or_marker, _eob)
#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:965
#define tmpl_value_length(_tmpl)
Definition tmpl.h:942
static char const * tmpl_type_to_str(tmpl_type_t type)
Return a static string containing the type name.
Definition tmpl.h:638
#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:730
#define NUM_LAST
Definition tmpl.h:397
#define tmpl_rules_enumv(_tmpl)
Definition tmpl.h:947
static bool tmpl_attr_tail_is_normal(tmpl_t const *vpt)
Return true if the last attribute reference is "normal".
Definition tmpl.h:714
#define tmpl_value(_tmpl)
Definition tmpl.h:941
#define ar_is_unknown(_ar)
Definition tmpl.h:511
static bool tmpl_attr_tail_is_unknown(tmpl_t const *vpt)
Return true if the last attribute reference is "unknown".
Definition tmpl.h:746
#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:351
#define ar_is_raw(_ar)
Definition tmpl.h:513
#define tmpl_is_attr(vpt)
Definition tmpl.h:208
#define NUM_ALL
Definition tmpl.h:395
fr_dict_attr_t const * enumv
Enumeration attribute used to resolve enum values.
Definition tmpl.h:342
tmpl_rules_t const * parent
for parent / child relationships
Definition tmpl.h:337
#define tmpl_value_enumv(_tmpl)
Definition tmpl.h:944
#define tmpl_xlat(_tmpl)
Definition tmpl.h:934
static fr_dict_attr_t const * tmpl_list(tmpl_t const *vpt)
Definition tmpl.h:908
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:683
#define ar_filter_is_num(_ar)
Definition tmpl.h:522
#define tmpl_rules_cast(_tmpl)
Definition tmpl.h:946
@ 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:396
#define tmpl_assert_type(_cond)
Convenience macro for printing a meaningful assert message when we get a bad tmpl type.
Definition tmpl.h:624
#define tmpl_contains_attr(vpt)
Definition tmpl.h:225
#define ar_da
Definition tmpl.h:503
#define TMPL_FLAG_REGEX
Is a type of regular expression.
Definition tmpl.h:116
#define ar_filter_is_cond(_ar)
Definition tmpl.h:523
tmpl_xlat_rules_t xlat
Rules/data for parsing xlats.
Definition tmpl.h:340
static char const * tmpl_attr_tail_unresolved(tmpl_t const *vpt)
Return the last attribute reference unresolved da.
Definition tmpl.h:873
bool at_runtime
Produce an ephemeral/runtime tmpl.
Definition tmpl.h:348
static bool tmpl_is_list(tmpl_t const *vpt)
Definition tmpl.h:924
#define TMPL_ATTR_VERIFY(_vpt)
Definition tmpl.h:964
bool force_dict_def
Use supplied dict_def even if original vpt->rules->dict_def was not NULL.
Definition tmpl.h:374
#define tmpl_is_data(vpt)
Definition tmpl.h:206
static fr_slen_t vpt
Definition tmpl.h:1272
fr_dict_t const * dict_def
Alternative default dictionary to use if vpt->rules->dict_def is NULL.
Definition tmpl.h:369
#define NUM_UNSPEC
Definition tmpl.h:394
static size_t tmpl_attr_num_elements(tmpl_t const *vpt)
The number of attribute references contained within a tmpl.
Definition tmpl.h:900
#define tmpl_value_type(_tmpl)
Definition tmpl.h:943
#define tmpl_attr(_tmpl)
Definition tmpl.h:658
tmpl_attr_error_t
Definition tmpl.h:1008
@ TMPL_ATTR_ERROR_INVALID_ARRAY_INDEX
Invalid array index.
Definition tmpl.h:1027
@ TMPL_ATTR_ERROR_LIST_NOT_ALLOWED
List qualifier is not allowed here.
Definition tmpl.h:1012
@ TMPL_ATTR_ERROR_UNRESOLVED_NOT_ALLOWED
Attribute couldn't be found in the dictionaries.
Definition tmpl.h:1018
@ TMPL_ATTR_ERROR_BAD_CAST
Specified cast was invalid.
Definition tmpl.h:1031
@ TMPL_ATTR_ERROR_INVALID_NAME
Attribute ref length is zero, or longer than the maximum.
Definition tmpl.h:1020
@ TMPL_ATTR_ERROR_MISSING_TERMINATOR
Unexpected text found after attribute reference.
Definition tmpl.h:1030
@ TMPL_ATTR_ERROR_LIST_MISSING
List qualifier is required, but missing.
Definition tmpl.h:1013
@ TMPL_ATTR_ERROR_NONE
No error.
Definition tmpl.h:1009
@ TMPL_ATTR_ERROR_FOREIGN_NOT_ALLOWED
Attribute resolved in a dictionary different to the one specified.
Definition tmpl.h:1024
@ TMPL_ATTR_ERROR_INVALID_OID
OIDs are not allowed.
Definition tmpl.h:1032
@ 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:1014
@ TMPL_ATTR_ERROR_FILTER_NOT_ALLOWED
Filters disallowed by rules.
Definition tmpl.h:1026
@ TMPL_ATTR_ERROR_EMPTY
Attribute ref contains no data.
Definition tmpl.h:1010
@ TMPL_ATTR_ERROR_NESTING_TOO_DEEP
Too many levels of nesting.
Definition tmpl.h:1029
#define tmpl_is_data_unresolved(vpt)
Definition tmpl.h:217
fr_type_t cast
Whether there was an explicit cast.
Definition tmpl.h:344
tmpl_attr_rules_t attr
Rules/data for parsing attribute references.
Definition tmpl.h:339
static fr_dict_attr_t const * tmpl_attr_tail_da(tmpl_t const *vpt)
Return the last attribute reference da.
Definition tmpl.h:805
@ 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:857
#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:919
static bool tmpl_attr_tail_is_raw(tmpl_t const *vpt)
Return true if the last attribute reference is "raw".
Definition tmpl.h:778
@ 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:377
#define TMPL_MAX_REQUEST_REF_NESTING
The maximum number of request references allowed.
Definition tmpl.h:85
tmpl_attr_type_t
Definition tmpl.h:380
@ TMPL_ATTR_TYPE_UNSPEC
No attribute was specified as this level only a filter.
Definition tmpl.h:382
@ TMPL_ATTR_TYPE_NORMAL
Normal, resolved, attribute ref.
Definition tmpl.h:381
@ 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:384
@ TMPL_ATTR_TYPE_UNRESOLVED
We have a name, but nothing else to identify the attribute.
Definition tmpl.h:389
@ TMPL_ATTR_FILTER_TYPE_TMPL
Filter is a tmpl.
Definition tmpl.h:411
@ TMPL_ATTR_FILTER_TYPE_INDEX
Filter is an index type.
Definition tmpl.h:409
@ TMPL_ATTR_FILTER_TYPE_CONDITION
Filter is a condition.
Definition tmpl.h:410
@ TMPL_ATTR_FILTER_TYPE_NONE
No filter present.
Definition tmpl.h:408
@ TMPL_ATTR_FILTER_TYPE_EXPR
Filter is an expression.
Definition tmpl.h:412
fr_event_list_t * runtime_el
The eventlist to use for runtime instantiation of xlats.
Definition tmpl.h:328
#define tmpl_needs_resolving(vpt)
Definition tmpl.h:223
tmpl_attr_filter_type_t _CONST type
Type of filter this is.
Definition tmpl.h:416
#define ar_filter_is_none(_ar)
Definition tmpl.h:521
Similar to tmpl_rules_t, but used to specify parameters that may change during subsequent resolution ...
Definition tmpl.h:368
Optional arguments passed to vp_tmpl functions.
Definition tmpl.h:336
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:318
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
uint8_t allow_oid
allow numerical OIDs.
Definition tmpl.h:316
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:434
unsigned int _CONST resolve_only
This reference and those before it.
Definition tmpl.h:457
unsigned int _CONST is_raw
Definition tmpl.h:460
fr_dict_attr_t const *_CONST da
Resolved dictionary attribute.
Definition tmpl.h:438
fr_dict_attr_t const *_CONST parent
The parent we used when trying to resolve the attribute originally.
Definition tmpl.h:452
tmpl_attr_filter_t _CONST filter
Filter associated with the attribute reference.
Definition tmpl.h:464
tmpl_attr_type_t _CONST type
is a raw reference
Definition tmpl.h:462
Define manipulation functions for the attribute reference list.
Definition tmpl.h:475
tmpl_request_ref_t _CONST request
Definition tmpl.h:479
#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)
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.
tmpl_t * tmpl_copy(TALLOC_CTX *ctx, tmpl_t const *in)
Copy a tmpl.
static int attr_to_raw(tmpl_t *vpt, tmpl_attr_t *ref)
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:854
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:5819
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:4176
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:3978
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:4938
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:4300
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:5782
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:4329
void fr_value_box_clear(fr_value_box_t *data)
Clear/free any existing value and metadata.
Definition value.c:4159
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:5145
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:4858
#define fr_value_box_mark_safe_for(_box, _safe_for)
Definition value.h:1077
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:1322
#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