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