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