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