The FreeRADIUS server $Id: a4e7f8c94e8038b7627bcf52356b7ed15955c1d7 $
Loading...
Searching...
No Matches
map.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: cd3e3c495a0e20e80018088bc39a84ad4fbb181c $
19 *
20 * @brief map / template functions
21 * @file src/lib/server/map.c
22 *
23 * @ingroup AVP
24 *
25 * @copyright 2013 The FreeRADIUS server project
26 * @copyright 2013 Alan DeKok (aland@freeradius.org)
27 */
28
29RCSID("$Id: cd3e3c495a0e20e80018088bc39a84ad4fbb181c $")
30
31#include <freeradius-devel/server/exec.h>
32#include <freeradius-devel/server/exec_legacy.h>
33#include <freeradius-devel/server/map.h>
34#include <freeradius-devel/server/paircmp.h>
35#include <freeradius-devel/server/tmpl_dcursor.h>
36
37#include <freeradius-devel/unlang/interpret.h>
38
39#include <freeradius-devel/util/base16.h>
40#include <freeradius-devel/util/pair_legacy.h>
41
42#include <freeradius-devel/protocol/radius/rfc2865.h>
43#include <freeradius-devel/protocol/freeradius/freeradius.internal.h>
44
45
47 { L("\""), T_DOUBLE_QUOTED_STRING }, /* Don't re-order, backslash throws off ordering */
48 { L("'"), T_SINGLE_QUOTED_STRING },
49 { L("/"), T_SOLIDUS_QUOTED_STRING },
50 { L("`"), T_BACK_QUOTED_STRING }
51};
53
54
55#ifdef DEBUG_MAP
56static void map_dump(request_t *request, map_t const *map)
57{
58 if (map->rhs) {
59 RDEBUG2(">>> MAP NAMES %s %s", map->lhs->name, map->rhs->name);
60
61 RDEBUG2(">>> MAP TYPES LHS: %s, RHS: %s",
62 tmpl_type_to_str(map->lhs->type),
63 tmpl_type_to_str(map->rhs->type));
64 } else {
65 RDEBUG2(">>> MAP NAMES %s", map->lhs->name);
66
67 RDEBUG2(">>> MAP TYPES LHS: %s",
68 tmpl_type_to_str(map->lhs->type));
69 }
70}
71#endif
72
73static inline map_t *map_alloc(TALLOC_CTX *ctx, map_t *parent)
74{
75 map_t *map;
76
77 if (parent) {
78 map = talloc_zero(parent, map_t);
79 } else {
80 map = talloc_zero(ctx, map_t);
81 }
82 map->parent = parent;
83
84 map_list_init(&map->child);
85 return map;
86}
87
88/** Convert CONFIG_PAIR (which may contain refs) to map_t.
89 *
90 * Treats the left operand as an attribute reference
91 * @verbatim<request>.<list>.<attribute>@endverbatim
92 *
93 * Treatment of left operand depends on quotation, barewords are treated as
94 * attribute references, double quoted values are treated as expandable strings,
95 * single quoted values are treated as literal strings.
96 *
97 * Return must be freed with talloc_free
98 *
99 * @param[in] ctx for talloc.
100 * @param[in] out Where to write the pointer to the new #map_t.
101 * @param[in] parent the parent map
102 * @param[in] cp to convert to map.
103 * @param[in] lhs_rules rules for parsing LHS attribute references.
104 * @param[in] input_rhs_rules rules for parsing RHS attribute references.
105 * @param[in] edit treat the map as an edit
106 * @return
107 * - #map_t if successful.
108 * - NULL on error.
109 */
110int map_afrom_cp(TALLOC_CTX *ctx, map_t **out, map_t *parent, CONF_PAIR *cp,
111 tmpl_rules_t const *lhs_rules, tmpl_rules_t const *input_rhs_rules, bool edit)
112{
113 map_t *map;
114 char const *attr, *value, *marker_subject;
115 char *unescaped_value = NULL;
116 fr_sbuff_parse_rules_t const *p_rules;
117 fr_slen_t slen;
118 fr_token_t quote;
119 fr_dict_attr_t const *da;
120 tmpl_rules_t my_rhs_rules = {};
121 tmpl_rules_t our_lhs_rules;
122 tmpl_rules_t const *rhs_rules = input_rhs_rules;
123 TALLOC_CTX *child_ctx = NULL;
124
125 *out = NULL;
126
127 if (!cp) return -1;
128
129 tmpl_rules_copy_depth(&our_lhs_rules, lhs_rules);
130 if (tmpl_rules_depth_exceeded(&our_lhs_rules)) {
131 cf_log_perr(cp, "Failed parsing configuration item");
132 return -1;
133 }
134 lhs_rules = &our_lhs_rules;
135
136 MEM(map = map_alloc(ctx, parent));
137 map->op = cf_pair_operator(cp);
138 map->ci = cf_pair_to_item(cp);
139
140 attr = cf_pair_attr(cp);
141 value = cf_pair_value(cp);
142 if (!value) {
143 cf_log_err(cp, "Missing attribute value");
144 goto error;
145 }
146
147 /*
148 * Allow for the RHS rules to be taken from the LHS rules.
149 */
150 if (!rhs_rules) rhs_rules = lhs_rules;
151
152 MEM(child_ctx = talloc(map, uint8_t));
153
154 /*
155 * LHS may be an expansion (that expands to an attribute reference)
156 * or an attribute reference. Quoting determines which it is.
157 */
158 quote = cf_pair_attr_quote(cp);
159 switch (quote) {
162 slen = tmpl_afrom_substr(ctx, &map->lhs,
163 &FR_SBUFF_IN(attr, talloc_strlen(attr)),
164 quote,
165 value_parse_rules_unquoted[quote], /* We're not searching for quotes */
166 lhs_rules);
167 if (slen <= 0) {
168 char *spaces, *text;
169
170 marker_subject = attr;
171 marker:
172 fr_canonicalize_error(ctx, &spaces, &text, slen, marker_subject);
173 cf_log_err(cp, "%s", text);
174 cf_log_perr(cp, "%s^", spaces);
175
177 talloc_free(text);
178 goto error;
179 }
180 break;
181
182 default:
183 slen = tmpl_afrom_attr_str(ctx, NULL, &map->lhs, attr, lhs_rules);
184 if (slen <= 0) {
185 cf_log_err(cp, "Failed parsing attribute reference %s - %s", attr, fr_strerror());
186 marker_subject = attr;
187 goto marker;
188 }
189
191 cf_log_perr(cp, "Failed creating attribute %s", map->lhs->name);
192 goto error;
193 }
194
196
197 /*
198 * The caller wants the RHS attributes to be
199 * parsed in the context of the LHS, but only if
200 * the LHS attribute was a group / structural attribute.
201 */
202 if (!input_rhs_rules) {
203 tmpl_rules_child_init(child_ctx, &my_rhs_rules, lhs_rules, map->lhs);
204 } else {
205 my_rhs_rules = *input_rhs_rules;
206 }
207 rhs_rules = &my_rhs_rules;
208
209 if (edit) {
210 if ((map->op != T_OP_RSHIFT_EQ) && (map->op != T_OP_LSHIFT_EQ)) {
211 my_rhs_rules.enumv = tmpl_attr_tail_da(map->lhs);
212 }
213 } else {
214 /*
215 * If we parsed an attribute on the LHS, and the RHS looks like an enumerated
216 * value, then set the enumv.
217 */
218 if (!rhs_rules->enumv && tmpl_is_attr(map->lhs) &&
219 (value[0] == ':') && value[1] == ':') {
220 my_rhs_rules.enumv = tmpl_attr_tail_da(map->lhs);
221 }
222 }
223 break;
224 }
225
226 /*
227 * RHS might be an attribute reference.
228 */
229 quote = cf_pair_value_quote(cp);
230 p_rules = value_parse_rules_unquoted[quote]; /* We're not searching for quotes */
231 if (quote == T_DOUBLE_QUOTED_STRING || quote == T_BACK_QUOTED_STRING) {
232 size_t len;
233
234 if (fr_sbuff_out_aunescape_until(child_ctx, &unescaped_value, &len,
235 &FR_SBUFF_IN(value, talloc_strlen(value)), SIZE_MAX,
236 p_rules->terminals, p_rules->escapes) < 0) {
237 slen = -1;
238 marker_subject = value;
239 goto marker;
240 }
241 value = unescaped_value;
242 slen = (fr_slen_t)len;
243 p_rules = NULL;
244
245 } else if (edit && (quote == T_HASH)) {
246 fr_slen_t slent;
248
249 /*
250 * Not a bare word, it's marked up as a complex expression.
251 *
252 * See cf_file.c, parse_input() and the use of T_HASH when doing alloc_pair,
253 * in order to see what magic is going on here.
254 */
255 slen = talloc_strlen(value);
256
257 MEM(map->rhs = tmpl_alloc(map, TMPL_TYPE_XLAT, T_BARE_WORD, value, slen));
258
259 /*
260 * Tokenize the expression.
261 */
262 slent = xlat_tokenize_expression(map->rhs, &head, &FR_SBUFF_IN(value, slen), p_rules, rhs_rules);
263 if (slent <= 0) {
264 slen = slent + 1;
265 marker_subject = value;
266 goto marker;
267 }
268
269 tmpl_set_xlat(map->rhs, head);
270 goto verify;
271
272 } else if ((map->op == T_OP_CMP_TRUE) || (map->op == T_OP_CMP_FALSE)) {
273 /*
274 * These operators require a hard-coded string on the RHS.
275 */
276 if (strcmp(value, "ANY") != 0) {
277 fr_strerror_printf("Invalid value for %s", fr_tokens[map->op]);
278 goto error;
279 }
280
281 (void) tmpl_afrom_value_box(map, &map->rhs, fr_box_strvalue("ANY"), false);
282 goto verify;
283
284 } else {
285 slen = talloc_strlen(value);
286 }
287
288 /*
289 * If we're assigning to a structural attribute, AND the
290 * RHS side is a string, THEN don't parse the RHS as a
291 * "group". The edit code will take the string, create
292 * pairs, and work on that.
293 */
294 if (edit && (rhs_rules == &my_rhs_rules) && my_rhs_rules.enumv && fr_type_is_structural(my_rhs_rules.enumv->type) &&
295 ((quote == T_DOUBLE_QUOTED_STRING) || (quote == T_BACK_QUOTED_STRING) || (quote == T_SINGLE_QUOTED_STRING))) {
296 my_rhs_rules.enumv = NULL;
297 }
298
299 slen = tmpl_afrom_substr(map, &map->rhs,
300 &FR_SBUFF_IN(value, slen),
301 quote,
302 p_rules,
303 rhs_rules);
304 if (slen < 0) {
305 marker_subject = value;
306 goto marker;
307 }
308
309 if (!map->rhs) {
310 cf_log_perr(cp, "Failed parsing RHS");
311 goto error;
312 }
313
314 /*
315 * RHS was resolved in the context of the LHS. If the LHS is not
316 * just a list, e.g.
317 *
318 * request.Vendor-Specific -= Cisco
319 *
320 * this results in an RHS tmpl with references starting from the
321 * first parsed child attribute. Fix this up to create full paths.
322 */
323 if (!input_rhs_rules && (tmpl_attr_rebase(map->rhs, map->lhs) < 0)) {
324 cf_log_perr(cp, "Failed resolving %s in the context of %s", map->rhs->name, map->lhs->name);
325 goto error;
326 }
327
328 if (tmpl_is_attr(map->rhs) && (tmpl_attr_unknown_add(map->rhs) < 0)) {
329 cf_log_perr(cp, "Failed creating attribute %s", map->rhs->name);
330 goto error;
331 }
332
333 /*
334 * We cannot assign a count to an attribute. That must
335 * be done in an xlat.
336 */
337 if (tmpl_is_attr(map->rhs) &&
338 (tmpl_attr_tail_num(map->rhs) == NUM_COUNT)) {
339 cf_log_err(cp, "Cannot assign from a count");
340 goto error;
341 }
342
343 /*
344 * If we can resolve the RHS in the context of the LHS,
345 * and are allowed to do so, then do that now.
346 *
347 * The "map" keyword uses the RHS not as the value which
348 * is assigned to the LHS. Instead, it is a pointer to
349 * the value. As such, we cannot immediately resolve the
350 * RHS in the context of the LHS.
351 *
352 * The edit code allows string assignments to lists, and
353 * will interpret the string as a sequence of edit
354 * operations. So we skip casting strings to lists.
355 *
356 * @todo - that could arguably be done immediately here,
357 * and the RHS could be parsed and created as a child
358 * map. That would allow for more compile-time sanity
359 * checks.
360 */
361 if (tmpl_is_attr(map->lhs) && tmpl_is_data(map->rhs) &&
362 (!input_rhs_rules || !input_rhs_rules->attr.disallow_rhs_resolve) &&
364 fr_type_t cast_type;
365
366 if ((map->op != T_OP_RSHIFT_EQ) && (map->op != T_OP_LSHIFT_EQ)) {
367 da = tmpl_attr_tail_da(map->lhs);
368 cast_type = da->type;
369 } else {
370 da = NULL;
371 cast_type = FR_TYPE_UINT32;
372 }
373
374 if (tmpl_cast_in_place(map->rhs, cast_type, da) < 0) {
375 cf_log_err(cp, "Invalid assignment - %s", fr_strerror());
376 goto error;
377 }
378 }
379
380verify:
381 MAP_VERIFY(map);
382 TALLOC_FREE(child_ctx);
383
384 *out = map;
385
386 return 0;
387
388error:
389 TALLOC_FREE(child_ctx);
390 talloc_free(map);
391 return -1;
392}
393
395 { L("!*"), T_OP_CMP_FALSE },
396 { L("!="), T_OP_NE },
397 { L("!~"), T_OP_REG_NE },
398 { L("+="), T_OP_ADD_EQ },
399 { L("-="), T_OP_SUB_EQ },
400 { L(":="), T_OP_SET },
401 { L("<"), T_OP_LT },
402 { L("<="), T_OP_LE },
403 { L("="), T_OP_EQ },
404 { L("=*"), T_OP_CMP_TRUE },
405 { L("=="), T_OP_CMP_EQ },
406 { L("=~"), T_OP_REG_EQ },
407 { L(">"), T_OP_GT },
408 { L(">="), T_OP_GE }
409};
411
412fr_sbuff_parse_rules_t const map_parse_rules_bareword_quoted = {
413 .escapes = &(fr_sbuff_unescape_rules_t){
414 .chr = '\\',
415 /*
416 * Allow barewords to contain whitespace
417 * if they're escaped.
418 */
419 .subs = {
420 ['\t'] = '\t',
421 ['\n'] = '\n',
422 [' '] = ' '
423 },
424 .do_hex = false,
425 .do_oct = false
426 },
427
428 /*
429 * We want to stop on _any_ terminal character, even if
430 * the token itself isn't valid here. Doing so means
431 * that we don't have the parser accept things like:
432 *
433 * User-Name,,,,=bob===
434 */
435 .terminals = &FR_SBUFF_TERMS(
436 L("\t"),
437 L("\n"),
438 L(" "),
439 L("!*"),
440 L("!="),
441 L("!~"),
442 L("+="),
443 L(","),
444 L("-="),
445 L(":="),
446 L("<"),
447 L("<="),
448 L("=*"),
449 L("=="),
450 L("=~"),
451 L(">"),
452 L(">="),
453 )
454};
455
463
464/** Parse sbuff into (which may contain refs) to map_t.
465 *
466 * This function uses the legacy operator meanings. The maps created here
467 * should only be used with radius_legacy_map_cmp() and
468 * radius_legacy_map_apply()
469 *
470 * The left operand MUST be an attribute reference
471 * @verbatim<request>.<list>.<attribute>@endverbatim
472 *
473 * The op_table should be #cond_cmp_op_table for check items, and
474 * #map_assignment_op_table for reply items.
475 *
476 * Return must be freed with talloc_free
477 *
478 * @param[in] ctx for talloc.
479 * @param[in] out Where to write the pointer to the new #map_t.
480 * @param[in,out] parent_p the parent map, updated for relative maps
481 * @param[in] in the data to parse for creating the map.
482 * @param[in] op_table for lhs OP rhs
483 * @param[in] op_table_len length of op_table
484 * @param[in] lhs_rules rules for parsing LHS attribute references.
485 * @param[in] rhs_rules rules for parsing RHS attribute references.
486 * @param[in] p_rules a list of terminals which will stop string
487 * parsing.
488 * @return
489 * - >0 on success.
490 * - <=0 on error.
491 */
492ssize_t map_afrom_substr(TALLOC_CTX *ctx, map_t **out, map_t **parent_p, fr_sbuff_t *in,
493 fr_table_num_sorted_t const *op_table, size_t op_table_len,
494 tmpl_rules_t const *lhs_rules, tmpl_rules_t const *rhs_rules,
495 fr_sbuff_parse_rules_t const *p_rules)
496{
497 fr_slen_t slen;
498 fr_token_t token;
499 map_t *map;
500 fr_sbuff_t our_in = FR_SBUFF(in);
501 fr_sbuff_marker_t m_lhs, m_rhs, m_op;
502 fr_sbuff_term_t const *tt = p_rules ? p_rules->terminals : NULL;
503 map_t *parent;
504 fr_dict_attr_t const *enumv;
505 tmpl_rules_t our_rhs_rules;
506
507 if (parent_p) {
508 parent = *parent_p;
509 } else {
510 parent = NULL;
511 }
512
513 *out = NULL;
514 MEM(map = map_alloc(ctx, NULL));
515
516 (void)fr_sbuff_adv_past_whitespace(&our_in, SIZE_MAX, tt);
517
518 fr_sbuff_marker(&m_lhs, &our_in);
520 switch (token) {
521 default:
522 fr_strerror_const("Expected attribute reference, not string");
523 goto error;
524
525 case T_BARE_WORD:
526 {
527 tmpl_rules_t our_lhs_rules;
528
529 our_lhs_rules = *lhs_rules;
530
531 /*
532 * Bare words on the LHS are always attributes.
533 *
534 * It doesn't make sense to allow "5 = 4". Or, "5 = NAS-Port".
535 *
536 * The conditions do allow "5 == NAS-Port", but
537 * they use a different parser for that.
538 */
539
540 /*
541 * Absolute references are from the root.
542 */
543 if (!fr_sbuff_next_if_char(&our_in, '.')) {
544 parent = NULL;
545
546 lhs_root:
547 slen = tmpl_afrom_attr_substr(map, NULL, &map->lhs, &our_in,
548 &map_parse_rules_bareword_quoted, &our_lhs_rules);
549 break;
550 }
551
552 /*
553 * Allow for ".foo" to refer to the current
554 * parents list. Allow for "..foo" to refer to
555 * the grandparent list.
556 */
557
558 /*
559 * Relative references must have a parent.
560 */
561 if (!parent) {
562 fr_strerror_const("Unexpected location for relative attribute - no parent attribute exists");
563 goto error;
564 }
565
566 /*
567 * Multiple '.' means "go to our parents parent".
568 */
569 while (fr_sbuff_next_if_char(&our_in, '.')) {
570 if (!parent) {
571 fr_strerror_const("Too many '.' in relative reference");
572 goto error;
573 }
574 parent = parent->parent;
575 }
576
577 if (!parent) goto lhs_root;
578
579 /*
580 * Start looking in the correct parent, not in whatever we were handed.
581 */
583 our_lhs_rules.attr.namespace = tmpl_attr_tail_da(parent->lhs);
584
585 slen = tmpl_afrom_attr_substr(map, NULL, &map->lhs, &our_in,
586 &map_parse_rules_bareword_quoted, &our_lhs_rules);
587 break;
588 }
589 }
590
591 if (!map->lhs) {
592 error:
593 slen = 0;
594
595 error_adj:
596 talloc_free(map);
597 FR_SBUFF_ERROR_RETURN(&our_in);
598 }
600
601 (void)fr_sbuff_adv_past_whitespace(&our_in, SIZE_MAX, tt);
602 fr_sbuff_marker(&m_op, &our_in);
603
604 /*
605 * Parse operator.
606 */
607 fr_sbuff_out_by_longest_prefix(&slen, &map->op, op_table, &our_in, T_INVALID);
608 if (map->op == T_INVALID) {
609 fr_strerror_const("Invalid operator");
610 goto error_adj;
611 }
612
613 /*
614 * Validate operators for check items.
615 *
616 * We can have comparison operators for reply items, as the rlm_attr_filter module
617 * uses that.
618 *
619 * However, we can't do comparisons on structural entries, except for existence checks.
620 */
621 if (!parent_p && tmpl_attr_tail_da_is_structural(map->lhs)) {
622 if (fr_comparison_op[map->op] && (map->op != T_OP_CMP_TRUE) && (map->op != T_OP_CMP_FALSE)) {
623 fr_sbuff_set(&our_in, &m_op);
624 fr_strerror_const("Comparison operators cannot be used inside of structural data types");
625 goto error;
626 }
627 }
628
629 (void)fr_sbuff_adv_past_whitespace(&our_in, SIZE_MAX, tt);
630
631 /*
632 * Copy LHS code above, except parsing in RHS, with some
633 * minor modifications.
634 */
635 fr_sbuff_marker(&m_rhs, &our_in);
636
637 /*
638 * If the LHS is a structural attribute, then (for now),
639 * the RHS can only be {}. This limitation should only
640 * be temporary, as we should really recurse to create
641 * child maps. And then the child maps should not have
642 * '.' as prefixes, and should require that the LHS can
643 * only be an attribute, etc. Not trivial, so we'll just
644 * skip all that for now.
645 */
646 switch (tmpl_attr_tail_da(map->lhs)->type) {
648 if ((map->op == T_OP_REG_EQ) || (map->op == T_OP_REG_NE)) {
649 fr_sbuff_set(&our_in, &m_op);
650 fr_assert(0);
651 fr_strerror_const("Regular expressions cannot be used for structural attributes");
652 goto error;
653 }
654
655 /*
656 * To match Vendor-Specific =* ANY
657 *
658 * Which is a damned hack.
659 */
660 if ((map->op == T_OP_CMP_TRUE) || (map->op == T_OP_CMP_FALSE)) goto parse_rhs;
661
662 if (fr_comparison_op[map->op]) {
663 fr_sbuff_set(&our_in, &m_op);
664 fr_strerror_const("Comparison operators cannot be used for structural attributes");
665 goto error;
666 }
667
668 /*
669 * radius_legacy_map_cmp() and radius_legacy_map_apply() both support structural
670 * attributes with RHS strings. And this function is only called from
671 * users_file.c. The consumers of the users file only call the radius legacy map
672 * functions.
673 */
674 if (!fr_sbuff_next_if_char(&our_in, '{')) {
675 goto parse_rhs;
676 }
677
678 fr_sbuff_adv_past_whitespace(&our_in, SIZE_MAX, tt);
679
680 /*
681 * Peek at the next character. If it's '}', stop.
682 */
683 if (!fr_sbuff_next_if_char(&our_in, '}')) {
684 fr_sbuff_set(&our_in, &m_rhs);
685 fr_strerror_const("Unexpected text before '}'");
686 goto error;
687 }
688
689 /*
690 * @todo - call ourselves recursively, with a special flag saying '.' is no
691 * longer necessary. And that we need to stop on '}'
692 */
693
694 /*
695 * Create an empty RHS.
696 */
698 (void) fr_value_box_strdup(map->rhs, tmpl_value(map->rhs), NULL, "", false);
699 goto check_for_child;
700
701 default:
702 break;
703 }
704
705parse_rhs:
706 /*
707 * These operators require a hard-coded string on the
708 * RHS. And we don't care about enums.
709 */
710 if ((map->op == T_OP_CMP_TRUE) || (map->op == T_OP_CMP_FALSE)) {
711 if (fr_sbuff_adv_past_str_literal(&our_in, "ANY") <= 0) {
712 fr_strerror_printf("Invalid value for %s", fr_tokens[map->op]);
713 goto error;
714 }
715
716 (void) tmpl_afrom_value_box(map, &map->rhs, fr_box_strvalue("ANY"), false);
717
718 goto check_for_child;
719 }
720
721 enumv = tmpl_attr_tail_da(map->lhs);
722
723 /*
724 * LHS is a structural type. The RHS is either empty (create empty LHS), or it's a string
725 * containing a list of attributes to create.
726 */
727 if (fr_type_is_leaf(enumv->type)) {
728 our_rhs_rules = *rhs_rules;
729 our_rhs_rules.enumv = enumv;
730 rhs_rules = &our_rhs_rules;
731 }
732
734 switch (token) {
739 slen = tmpl_afrom_substr(map, &map->rhs, &our_in, token,
740 value_parse_rules_quoted[token], rhs_rules);
741 break;
742
743 default:
744 if (rhs_rules->attr.bare_word_enum && rhs_rules->enumv) {
745 fr_value_box_t *vb;
746
747 MEM(vb = fr_value_box_alloc(map, rhs_rules->enumv->type, rhs_rules->enumv));
748
749 if (!p_rules) p_rules = &value_parse_rules_bareword_quoted;
750
751 /*
752 * It MUST be the given data type, and it MAY be an enum name.
753 */
754 slen = fr_value_box_from_substr(map, vb, rhs_rules->enumv->type, rhs_rules->enumv,
755 &our_in, p_rules);
756 if (slen < 0) goto error;
757
758 if (tmpl_afrom_value_box(map, &map->rhs, vb, true) < 0) {
759 goto error;
760 }
761
762 } else {
763 if (!p_rules) p_rules = &value_parse_rules_bareword_quoted;
764
765 /*
766 * Use the RHS termination rules ONLY for bare
767 * words. For quoted strings we already know how
768 * to terminate the input string.
769 */
770 slen = tmpl_afrom_substr(map, &map->rhs, &our_in, token, p_rules, rhs_rules);
771 }
772 break;
773 }
774 if (!map->rhs) goto error_adj;
775
776 /*
777 * Check for, and skip, the trailing quote if we had a leading quote.
778 */
779 if (token != T_BARE_WORD) {
780 if (!fr_sbuff_next_if_char(&our_in, fr_token_quote[token])) {
781 fr_strerror_const("Unexpected end of quoted string");
782 goto error;
783 }
784
785 /*
786 * The tmpl code does NOT return tmpl_type_data
787 * for string data without xlat. Instead, it
788 * creates TMPL_TYPE_DATA_UNRESOLVED.
789 */
790 if (tmpl_resolve(map->rhs, NULL) < 0) {
791 fr_sbuff_set(&our_in, &m_rhs); /* Marker points to RHS */
792 goto error;
793 }
794 } else if (tmpl_is_attr(map->lhs) && (tmpl_is_data_unresolved(map->rhs) || tmpl_is_data(map->rhs))) {
795 /*
796 * If the operator is "true" or "false", just
797 * cast the RHS to string, as no one will care
798 * about it.
799 */
800 if ((map->op != T_OP_CMP_TRUE) && (map->op != T_OP_CMP_FALSE)) {
801 fr_dict_attr_t const *da = tmpl_attr_tail_da(map->lhs);
802
803 if (tmpl_cast_in_place(map->rhs, da->type, da) < 0) {
804 fr_sbuff_set(&our_in, &m_rhs); /* Marker points to RHS */
805 goto error;
806 }
807 } else {
808 if (tmpl_cast_in_place(map->rhs, FR_TYPE_STRING, NULL) < 0) {
809 fr_sbuff_set(&our_in, &m_rhs); /* Marker points to RHS */
810 goto error;
811 }
812 }
813 }
814
815 if (tmpl_contains_regex(map->lhs)) {
816 fr_sbuff_set(&our_in, &m_lhs);
817 fr_strerror_const("Unexpected regular expression");
818 goto error;
819 }
820
821 if ((map->op == T_OP_REG_EQ) || (map->op == T_OP_REG_NE)) {
822 if (!tmpl_contains_regex(map->rhs)) {
823 fr_sbuff_set(&our_in, &m_rhs);
824 fr_strerror_const("Expected regular expression after regex operator");
825 goto error;
826 }
827 } else {
828 if (tmpl_contains_regex(map->rhs)) {
829 fr_sbuff_set(&our_in, &m_rhs);
830 fr_strerror_const("Unexpected regular expression");
831 goto error;
832 }
833 }
834
835check_for_child:
836 /*
837 * Add this map to the parents list. Note that the caller
838 * will have to check for this, but checking if map->parent
839 * exists.
840 */
841 if (parent) {
842 (void) talloc_steal(parent, map);
843 map->parent = parent;
844 map_list_insert_tail(&parent->child, map);
845 }
846
847 if (parent_p) {
849 *parent_p = map;
850 } else {
851 *parent_p = parent;
852 }
853 }
854
855 /*
856 * Xlat expansions are cast to strings for structural data types.
857 */
860 }
861
862 MAP_VERIFY(map);
863 *out = map;
864
865 FR_SBUFF_SET_RETURN(in, &our_in);
866}
867
868static int _map_afrom_cs(TALLOC_CTX *ctx, map_list_t *out, map_t *parent, CONF_SECTION const *cs,
869 tmpl_rules_t const *lhs_rules, tmpl_rules_t const *rhs_rules,
870 map_validate_t validate, void *uctx,
871 unsigned int max, bool update, bool edit)
872{
873 CONF_ITEM *ci;
874 CONF_PAIR *cp;
875
876 unsigned int total = 0;
877 map_t *map;
878 TALLOC_CTX *parent_ctx;
879
880 tmpl_rules_t our_lhs_rules;
881 tmpl_rules_t child_rhs_rules = *rhs_rules;
882 tmpl_rules_t const *our_rhs_rules;
883
884 tmpl_rules_copy_depth(&our_lhs_rules, lhs_rules); /* Mutable copy of the destination */
885 if (tmpl_rules_depth_exceeded(&our_lhs_rules)) {
886 cf_log_perr(cs, "Failed parsing configuration section");
887 return -1;
888 }
889
890 /*
891 * The first map has ctx as the parent context.
892 * The rest have the previous map as the parent context.
893 */
894 parent_ctx = ctx;
895
896 ci = cf_section_to_item(cs);
897
898 /*
899 * Check the "update" section for destination lists.
900 */
901 if (update) {
902 char const *name2 = cf_section_name2(cs);
903
904 if (name2) {
905 fr_dict_attr_t const *list_def = NULL;
906 fr_sbuff_t sbuff = FR_SBUFF_IN(name2, strlen(name2));
907
908 /*
909 * There's a name, but it's not one we recognize. Or, it's one we recognize but
910 * there are things after it, we forbid it.
911 *
912 * This code is ONLY used for "update" sections in modules. None of those
913 * examples use a destination list. If we allow any destination list (including
914 * parent, outer, etc.) then we allow the possibility of changing protocols,
915 * which is bad.
916 *
917 * This limitation also means that the module "update" lists can't automatically
918 * edit other structural attributes, such as "reply.foo". But that seems a small
919 * price to pay.
920 *
921 * The admin can still specify outer / parent / etc. on the individual entries in
922 * an "update" section, but we'll let that go for now.
923 *
924 * @todo - We should arguably forbid parent/outer list references in an "update"
925 * section.
926 */
927 if ((tmpl_attr_list_from_substr(&list_def, &sbuff) <= 0) ||
928 !fr_sbuff_eof(&sbuff)) {
929 cf_log_err(ci, "Invalid destination list specifier for 'update' - must be one of 'request', 'reply', 'control', or 'state'");
930 return -1;
931 }
932
933 our_lhs_rules.attr.list_def = list_def;
934 }
935 }
936
937 for (ci = cf_item_next(cs, NULL);
938 ci != NULL;
939 ci = cf_item_next(cs, ci)) {
940 tmpl_rules_t child_lhs_rules = our_lhs_rules;
941
942 /*
943 * Disallow list references on the LHS of child lists for edit sections.
944 */
945 if (edit) child_lhs_rules.attr.list_presence = TMPL_ATTR_LIST_FORBID;
946
947 if (total++ == max) {
948 cf_log_err(ci, "Map size exceeded");
949 error:
950 /*
951 * Free in reverse as successive entries have their
952 * prececessors as talloc parent contexts
953 */
954 map_list_talloc_reverse_free(out);
955 return -1;
956 }
957
958 /*
959 * If we have a subsection, AND the name2 is an
960 * assignment operator, THEN we allow sub-maps.
961 */
962 if (cf_item_is_section(ci)) {
963 CONF_SECTION *subcs;
964 fr_token_t token;
965 fr_slen_t slen;
966 map_list_t child_list;
967
968 map_list_init(&child_list);
969 subcs = cf_item_to_section(ci);
970 token = cf_section_name2_quote(subcs);
971
972 if (token == T_INVALID) {
973 cf_log_err(ci, "Section '%s { ... }' is missing the '=' operator", cf_section_name1(subcs));
974 goto error;
975 }
976
977 if (!fr_assignment_op[token]) {
978 cf_log_err(ci, "Invalid operator '%s'", fr_tokens[token]);
979 goto error;
980 }
981
982 MEM(map = map_alloc(parent_ctx, parent));
983 map->op = token;
984 map->ci = ci;
985
986 /*
987 * The LHS MUST be an attribute name.
988 * map_afrom_cp() allows for dynamic
989 * names, but for simplicity we forbid
990 * them for now. Once the functionality
991 * is tested and used, we can allow that.
992 */
993 slen = tmpl_afrom_attr_str(map, NULL, &map->lhs, cf_section_name1(subcs), &our_lhs_rules);
994 if (slen <= 0) {
995 cf_log_err(ci, "Failed parsing attribute reference for list %s - %s",
996 cf_section_name1(subcs), fr_strerror());
997 talloc_free(map);
998 goto error; /* re-do "goto marker" stuff to print out spaces ? */
999 }
1000
1001 /*
1002 * The LHS MUST be an attribute reference
1003 * for now.
1004 */
1005 if (!tmpl_is_attr(map->lhs)) {
1006 cf_log_err(ci, "Left side of group '%s' is NOT an attribute reference",
1007 map->lhs->name);
1008 talloc_free(map);
1009 goto error; /* re-do "goto marker" stuff to print out spaces ? */
1010 }
1011
1012 if (tmpl_attr_tail_da(map->lhs)->flags.is_unknown) {
1013 cf_log_err(ci, "Unknown attribute '%s'", map->lhs->name);
1014 talloc_free(map);
1015 goto error; /* re-do "goto marker" stuff to print out spaces ? */
1016 }
1017
1018 /*
1019 * The leaf reference of the outer section
1020 * is used as the parsing context of the
1021 * inner section.
1022 */
1023 child_lhs_rules.attr.namespace = tmpl_attr_tail_da(map->lhs);
1024
1025 /*
1026 * Groups MAY change dictionaries. If so, then swap the dictionary and the parent.
1027 */
1028 if (child_lhs_rules.attr.namespace->type == FR_TYPE_GROUP) {
1029 fr_dict_attr_t const *ref;
1030 fr_dict_t const *dict, *internal;
1031
1032 ref = fr_dict_attr_ref(child_lhs_rules.attr.namespace);
1033 dict = fr_dict_by_da(ref);
1034 internal = fr_dict_internal();
1035
1036 if (dict != internal) {
1037 if (dict != child_lhs_rules.attr.dict_def) {
1038 child_lhs_rules.attr.dict_def = dict;
1039 child_lhs_rules.attr.namespace = ref;
1040 }
1041 } else {
1042 /*
1043 * We're internal: don't use it, and instead rely on dict_def.
1044 */
1045 child_lhs_rules.attr.namespace = NULL;
1046 }
1047 }
1048
1049 if (fr_type_is_structural(tmpl_attr_tail_da(map->lhs)->type)) {
1050 /*
1051 * This prints out any relevant error
1052 * messages. We MAY want to print out
1053 * additional ones, but that might get
1054 * complex and confusing.
1055 *
1056 * We call out internal _map_afrom_cs()
1057 * function, in order to pass in the
1058 * correct parent map.
1059 */
1060 if (_map_afrom_cs(map, &child_list, map, cf_item_to_section(ci),
1061 &child_lhs_rules, rhs_rules, validate, uctx, max, false, edit) < 0) {
1062 fail:
1063 map_list_talloc_free(&child_list);
1064 talloc_free(map);
1065 goto error;
1066 }
1067 map_list_move(&map->child, &child_list);
1068 } else {
1070
1071 /*
1072 * foo := { a, b, c }
1073 *
1074 * @todo - maybe lhs_rules? But definitely not child_lhs_rules.
1075 *
1076 * @todo - this code is taken from compile_edit_section(), we might want
1077 * to just move that code here, for fewer special cases.
1078 */
1079 if (map_list_afrom_cs(map, &child_list, cf_item_to_section(ci), rhs_rules, NULL, NULL, max) < 0) {
1080 goto fail;
1081 }
1082
1083 if ((map->op != T_OP_SET) && !map_list_num_elements(&child_list)) {
1084 cf_log_err(cs, "Cannot use operator '%s' for assigning empty list to '%s' data type.",
1085 fr_tokens[map->op], fr_type_to_str(tmpl_attr_tail_da(map->lhs)->type));
1086 goto fail;
1087 }
1088
1089 map_list_move(&map->child, &child_list);
1090 }
1091
1092 MAP_VERIFY(map);
1093 goto next;
1094 }
1095
1096 if (!cf_item_is_pair(ci)) {
1097 cf_log_err(ci, "Entry is not in \"attribute = value\" format");
1098 goto error;
1099 }
1100
1101 cp = cf_item_to_pair(ci);
1102 fr_assert(cp != NULL);
1103
1104 /*
1105 * Over-ride RHS rules for
1106 *
1107 * reply += {
1108 * User-Name = User-Name
1109 * }
1110 *
1111 * Which looks stupid. Instead we require
1112 *
1113 * reply += {
1114 * User-Name = request.User-Name
1115 * }
1116 *
1117 * On the other hand, any xlats on the RHS don't use the full path. :( And we still need
1118 * to allow relative attribute references via ".foo", when updating structures.
1119 */
1120 our_rhs_rules = rhs_rules;
1121 if (edit && (rhs_rules->attr.list_def != child_lhs_rules.attr.list_def)) {
1122 char const *value = cf_pair_value(cp);
1123
1124 if (value && (*value == '&')) {
1125 child_rhs_rules.attr.list_presence = TMPL_ATTR_LIST_REQUIRE;
1126 our_rhs_rules = &child_rhs_rules;
1127 }
1128 }
1129
1130 if (map_afrom_cp(parent_ctx, &map, parent, cp, &child_lhs_rules, our_rhs_rules, edit) < 0) {
1131 cf_log_err(ci, "Failed creating map from '%s = %s'",
1132 cf_pair_attr(cp), cf_pair_value(cp));
1133 goto error;
1134 }
1135
1136 MAP_VERIFY(map);
1137
1138 /*
1139 * Check the types in the map are valid
1140 */
1141 if (validate && (validate(map, uctx) < 0)) goto error;
1142
1143 next:
1144 parent_ctx = map;
1145 map_list_insert_tail(out, map);
1146 }
1147
1148 return 0;
1149
1150}
1151
1152/** Convert a config section into an attribute map.
1153 *
1154 * For "update" sections, Uses 'name2' of section to set default request and lists.
1155 *
1156 * @param[in] ctx for talloc.
1157 * @param[out] out Where to store the allocated map.
1158 * @param[in] cs the update section
1159 * @param[in] lhs_rules rules for parsing LHS attribute references.
1160 * @param[in] rhs_rules rules for parsing RHS attribute references.
1161 * @param[in] validate map using this callback (may be NULL).
1162 * @param[in] uctx to pass to callback.
1163 * @param[in] max number of mappings to process.
1164 * @return
1165 * - 0 on success.
1166 * - -1 on failure.
1167 */
1168int map_afrom_cs(TALLOC_CTX *ctx, map_list_t *out, CONF_SECTION const *cs,
1169 tmpl_rules_t const *lhs_rules, tmpl_rules_t const *rhs_rules,
1170 map_validate_t validate, void *uctx,
1171 unsigned int max)
1172{
1173 bool update;
1174 char const *name2;
1175 map_t *parent = NULL;
1176
1177 name2 = cf_section_name1(cs);
1178 update = (name2 && (strcmp(name2, "update") == 0));
1179
1180 if (ctx) parent = talloc_get_type(ctx, map_t);
1181
1182 return _map_afrom_cs(ctx, out, parent, cs, lhs_rules, rhs_rules, validate, uctx, max, update, false);
1183}
1184
1185/** Convert a config section into an attribute map for editing
1186 *
1187 * @param[in] ctx for talloc.
1188 * @param[out] out Where to store the allocated map.
1189 * @param[in] cs the update section
1190 * @param[in] lhs_rules rules for parsing LHS attribute references.
1191 * @param[in] rhs_rules rules for parsing RHS attribute references.
1192 * @param[in] validate map using this callback (may be NULL).
1193 * @param[in] uctx to pass to callback.
1194 * @param[in] max number of mappings to process.
1195 * @return
1196 * - 0 on success.
1197 * - -1 on failure.
1198 */
1199int map_afrom_cs_edit(TALLOC_CTX *ctx, map_list_t *out, CONF_SECTION *cs,
1200 tmpl_rules_t const *lhs_rules, tmpl_rules_t const *rhs_rules,
1201 map_validate_t validate, void *uctx,
1202 unsigned int max)
1203{
1204 map_t *parent = NULL;
1205
1206 if (ctx) parent = talloc_get_type(ctx, map_t);
1207
1208 return _map_afrom_cs(ctx, out, parent, cs, lhs_rules, rhs_rules, validate, uctx, max, false, true);
1209}
1210
1211/** Convert CONFIG_PAIR (which may contain refs) to map_t.
1212 *
1213 * Treats the CONFIG_PAIR name as a value.
1214 *
1215 * Treatment of left operand depends on quotation, barewords are treated as
1216 * attribute , double quoted values are treated as expandable strings,
1217 * single quoted values are treated as literal strings.
1218 *
1219 * Return must be freed with talloc_free
1220 *
1221 * @param[in] ctx for talloc.
1222 * @param[in] out Where to write the pointer to the new #map_t.
1223 * @param[in] parent the parent map
1224 * @param[in] cp to convert to map.
1225 * @param[in] t_rules rules for parsing name
1226 * @return
1227 * - #map_t if successful.
1228 * - NULL on error.
1229 */
1230static int map_value_afrom_cp(TALLOC_CTX *ctx, map_t **out, map_t *parent, CONF_PAIR *cp,
1231 tmpl_rules_t const *t_rules)
1232{
1233 map_t *map;
1234 char const *attr, *marker_subject;
1235 fr_slen_t slen;
1237
1238 *out = NULL;
1239
1240 if (!cp) return -1;
1241
1242 MEM(map = map_alloc(ctx, parent));
1243 map->op = T_OP_EQ; /* @todo - should probably be T_INVALID */
1244 map->ci = cf_pair_to_item(cp);
1245
1246 attr = cf_pair_attr(cp);
1247
1248 /*
1249 * LHS may be an expansion (that expands to an attribute reference)
1250 * or an attribute reference. Quoting determines which it is.
1251 */
1253 switch (type) {
1257 slen = tmpl_afrom_substr(ctx, &map->lhs,
1258 &FR_SBUFF_IN(attr, talloc_strlen(attr)),
1259 type,
1260 value_parse_rules_unquoted[type], /* We're not searching for quotes */
1261 t_rules);
1262 if (slen <= 0) {
1263 char *spaces, *text;
1264
1265 marker:
1266 marker_subject = attr;
1267 fr_canonicalize_error(ctx, &spaces, &text, slen, marker_subject);
1268 cf_log_err(cp, "%s", text);
1269 cf_log_perr(cp, "%s^", spaces);
1270
1272 talloc_free(text);
1273 goto error;
1274 }
1275 break;
1276
1278 fr_strerror_const("Invalid location for regular expression");
1279 slen = 0;
1280 goto marker;
1281
1282 default:
1283 /*
1284 * Let the tmpl code determine if it's an attribute reference or else is a raw value.
1285 */
1286 slen = tmpl_afrom_substr(ctx, &map->lhs, &FR_SBUFF_IN(attr, talloc_strlen(attr)), T_BARE_WORD, NULL, t_rules);
1287 if (slen <= 0) goto marker;
1288
1289 if (tmpl_is_attr(map->lhs) && (tmpl_attr_unknown_add(map->lhs) < 0)) {
1290 fr_strerror_printf("Failed defining attribute %s", map->lhs->name);
1291 goto error;
1292 }
1293 break;
1294 }
1295
1296 MAP_VERIFY(map);
1297
1298 *out = map;
1299
1300 return 0;
1301
1302error:
1303 talloc_free(map);
1304 return -1;
1305}
1306
1307/*
1308 * Where the RHS are all values.
1309 */
1310static int _map_list_afrom_cs(TALLOC_CTX *ctx, map_list_t *out, map_t *parent, CONF_SECTION *cs,
1311 tmpl_rules_t const *t_rules,
1312 map_validate_t validate, void *uctx,
1313 unsigned int max)
1314{
1315 CONF_ITEM *ci;
1316 CONF_PAIR *cp;
1317
1318 unsigned int total = 0;
1319 map_t *map;
1320 TALLOC_CTX *parent_ctx;
1321
1322 /*
1323 * The first map has ctx as the parent context.
1324 * The rest have the previous map as the parent context.
1325 */
1326 parent_ctx = ctx;
1327
1328 for (ci = cf_item_next(cs, NULL);
1329 ci != NULL;
1330 ci = cf_item_next(cs, ci)) {
1331 if (total++ == max) {
1332 cf_log_err(ci, "Map size exceeded");
1333 error:
1334 /*
1335 * Free in reverse as successive entries have their
1336 * prececessors as talloc parent contexts
1337 */
1338 map_list_talloc_reverse_free(out);
1339 return -1;
1340 }
1341
1342 if (cf_item_is_section(ci)) {
1343 cf_log_err(ci, "Cannot create sub-lists");
1344 goto error;
1345 }
1346
1347 cp = cf_item_to_pair(ci);
1348 fr_assert(cp != NULL);
1349
1350 if (map_value_afrom_cp(parent_ctx, &map, parent, cp, t_rules) < 0) {
1351 fr_assert(0);
1352 cf_log_err(ci, "Failed creating map from '%s'", cf_pair_attr(cp));
1353 goto error;
1354 }
1355
1356 MAP_VERIFY(map);
1357
1358 /*
1359 * Check the types in the map are valid
1360 */
1361 if (validate && (validate(map, uctx) < 0)) goto error;
1362
1363 parent_ctx = map;
1364 map_list_insert_tail(out, map);
1365 }
1366
1367 return 0;
1368
1369}
1370
1371/** Convert a config section into a list of { a, b, c, d, ... }
1372 *
1373 * @param[in] ctx for talloc.
1374 * @param[out] out Where to store the allocated map.
1375 * @param[in] cs the update section
1376 * @param[in] t_rules rules for parsing the data.
1377 * @param[in] validate map using this callback (may be NULL).
1378 * @param[in] uctx to pass to callback.
1379 * @param[in] max number of mappings to process.
1380 * @return
1381 * - 0 on success.
1382 * - -1 on failure.
1383 */
1384int map_list_afrom_cs(TALLOC_CTX *ctx, map_list_t *out, CONF_SECTION *cs,
1385 tmpl_rules_t const *t_rules,
1386 map_validate_t validate, void *uctx,
1387 unsigned int max)
1388{
1389 map_t *parent = NULL;
1390
1391 if (ctx) parent = talloc_get_type(ctx, map_t);
1392
1393 return _map_list_afrom_cs(ctx, out, parent, cs, t_rules, validate, uctx, max);
1394}
1395
1396/** Convert a value box to a map
1397 *
1398 * This is mainly used in IO modules, where another function is used to convert
1399 * between the foreign value type and internal values, and the destination
1400 * attribute is provided as a string.
1401 *
1402 * @param[in] ctx for talloc
1403 * @param[out] out Where to store the head of the map.
1404 * @param[in] lhs of the operation
1405 * @param[in] lhs_quote type of the LHS string
1406 * @param[in] lhs_rules rules that control parsing of the LHS string.
1407 * @param[in] op the operation to perform
1408 * @param[in] rhs of the operation
1409 * @param[in] steal_rhs_buffs Whether we attempt to save allocs by stealing the buffers
1410 * from the rhs #fr_value_box_t.
1411 * @return
1412 * - #map_t if successful.
1413 * - NULL on error.
1414 */
1415int map_afrom_value_box(TALLOC_CTX *ctx, map_t **out,
1416 char const *lhs, fr_token_t lhs_quote, tmpl_rules_t const *lhs_rules,
1417 fr_token_t op,
1418 fr_value_box_t *rhs, bool steal_rhs_buffs)
1419{
1420 fr_slen_t slen;
1421 map_t *map;
1422
1423 map = map_alloc(ctx, NULL);
1424
1425 slen = tmpl_afrom_substr(map, &map->lhs,
1426 &FR_SBUFF_IN_STR(lhs),
1427 lhs_quote,
1428 NULL,
1429 lhs_rules);
1430 if (slen < 0) {
1431 error:
1432 talloc_free(map);
1433 return -1;
1434 }
1435
1436 map->op = op;
1437
1438 if (tmpl_afrom_value_box(map, &map->rhs, rhs, steal_rhs_buffs) < 0) goto error;
1439
1440 MAP_VERIFY(map);
1441 *out = map;
1442
1443 return 0;
1444}
1445
1446/** Convert a value pair string to valuepair map
1447 *
1448 * Takes a valuepair string with list and request qualifiers and converts it into a
1449 * #map_t.
1450 *
1451 * Attribute string is in the format (where @verbatim <qu> @endverbatim is a quotation char ['"]):
1452 @verbatim
1453 [<list>.][<qu>]<attribute>[<qu>] <op> [<qu>]<value>[<qu>]
1454 @endverbatim
1455 *
1456 * @param[in] ctx where to allocate the map.
1457 * @param[out] out Where to write the new map.
1458 * @param[in] vp_str string to parse.
1459 * @param[in] lhs_rules rules for parsing LHS attribute references.
1460 * @param[in] rhs_rules rules for parsing RHS attribute references.
1461 * @return
1462 * - 0 on success.
1463 * - < 0 on error.
1464 */
1465int map_afrom_attr_str(TALLOC_CTX *ctx, map_t **out, char const *vp_str,
1466 tmpl_rules_t const *lhs_rules, tmpl_rules_t const *rhs_rules)
1467{
1468 fr_sbuff_t sbuff = FR_SBUFF_IN(vp_str, strlen(vp_str));
1469
1471 lhs_rules, rhs_rules, NULL) < 0) {
1472 return -1;
1473 }
1474
1475 if (!fr_cond_assert(*out != NULL)) return -1;
1476
1477 if (!tmpl_is_attr((*out)->lhs)) {
1478 TALLOC_FREE(*out);
1479 fr_strerror_const("Left operand must be an attribute");
1480 return -1;
1481 }
1482
1483 return 0;
1484}
1485
1486/** Process map which has exec as a src
1487 *
1488 * Evaluate maps which specify exec as a src. This may be used by various sorts of update sections, and so
1489 * has been broken out into it's own function.
1490 *
1491 * @param[in,out] ctx to allocate new #fr_pair_t (s) in.
1492 * @param[out] out Where to write the #fr_pair_t (s).
1493 * @param[in] request structure (used only for talloc).
1494 * @param[in] map the map. The LHS (dst) must be #TMPL_TYPE_ATTR.
1495 * The RHS (src) must be #TMPL_TYPE_EXEC.
1496 * @return
1497 * - 0 on success.
1498 * - -1 on failure.
1499 */
1500static int map_exec_to_vp(TALLOC_CTX *ctx, fr_pair_list_t *out, request_t *request, map_t const *map)
1501{
1502 int result;
1503 fr_pair_t *vp;
1504 fr_pair_list_t *input_pairs = NULL;
1505 char answer[1024];
1506
1508
1509 MAP_VERIFY(map);
1510
1511 fr_assert(map->rhs); /* Quite clang scan */
1512 fr_assert(tmpl_is_exec(map->rhs));
1513 fr_assert(tmpl_is_attr(map->lhs));
1514
1515 if (fr_type_is_structural(tmpl_attr_tail_da(map->lhs)->type)) {
1516 REDEBUG("Cannot assign `exec` output to structural attribute %s", map->lhs->name);
1517 return -1;
1518 }
1519
1520 /*
1521 * We always put the request pairs into the environment
1522 */
1524
1525 /*
1526 * Automagically switch output type depending on our destination
1527 * @todo - If dst is a list, then we create attributes from the output of the program
1528 * if dst is an attribute, then we create an attribute of that type and then
1529 * call fr_pair_value_from_str on the output of the script.
1530 */
1531 result = radius_exec_program_legacy(answer, sizeof(answer),
1532 request, map->rhs->name, input_pairs ? input_pairs : NULL,
1534 if (result != 0) {
1535 REDEBUG("Exec failed with code (%i)", result);
1536 return -1;
1537 }
1538
1539 /*
1540 * @todo - we completely ignore the operator here :( Arguably the caller should be calling ONLY
1541 * the legacy pair move functions with the results of this function.
1542 */
1544 vp->op = map->op;
1545 if (fr_pair_value_from_str(vp, answer, strlen(answer), &fr_value_unescape_single, false) < 0) {
1546 RPEDEBUG("Failed parsing exec output");
1547 talloc_free(vp);
1548 return -2;
1549 }
1551
1552 return 0;
1553}
1554
1555/** Convert a map to a #fr_pair_t
1556 *
1557 * @param[in,out] ctx to allocate #fr_pair_t (s) in.
1558 * @param[out] out Where to write the #fr_pair_t (s), which may be NULL if not found
1559 * @param[in] request The current request.
1560 * @param[in] map the map. The LHS (dst) has to be #TMPL_TYPE_ATTR.
1561 * @param[in] uctx unused.
1562 * @return
1563 * - 0 on success.
1564 * - -1 on failure.
1565 */
1566int map_to_vp(TALLOC_CTX *ctx, fr_pair_list_t *out, request_t *request, map_t const *map, UNUSED void *uctx)
1567{
1568 int rcode = 0;
1569 fr_pair_t *n = NULL;
1570 fr_pair_list_t found;
1572 ssize_t slen;
1573 char *str;
1574
1575 fr_pair_list_init(&found);
1577
1578 MAP_VERIFY(map);
1579 if (!fr_cond_assert(map->lhs != NULL)) return -1;
1580
1581 fr_assert(tmpl_is_attr(map->lhs));
1582
1583 /*
1584 * If there's no RHS, then it MUST be an attribute, and
1585 * it MUST be structural. And it MAY have children.
1586 */
1587 if (!map->rhs) {
1588 map_t *child;
1589
1590 if (!tmpl_is_attr(map->lhs)) return -1;
1591
1592 switch (tmpl_attr_tail_da(map->lhs)->type) {
1593 case FR_TYPE_STRUCTURAL:
1594 break;
1595
1596 default:
1597 return -1;
1598 }
1599
1600 /*
1601 * Create the parent attribute, and
1602 * recurse to generate the children into
1603 * vp->vp_group
1604 */
1606 n->op = map->op;
1607
1608 for (child = map_list_next(&map->child, NULL);
1609 child != NULL;
1610 child = map_list_next(&map->child, child)) {
1611 fr_pair_list_t list;
1612
1613 /*
1614 * map_to_vp() frees "out", so we need to
1615 * work around that by creating a
1616 * temporary list.
1617 */
1618 fr_pair_list_init(&list);
1619 if (map_to_vp(n, &list, request, child, NULL) < 0) {
1620 talloc_free(n);
1621 return -1;
1622 }
1623
1624 fr_pair_list_append(&n->vp_group, &list);
1625 }
1626
1628 return 0;
1629 }
1630
1631 /*
1632 * List to list found, this is a special case because we don't need
1633 * to allocate any attributes, just finding the current list, and change
1634 * the op.
1635 */
1638 fr_pair_list_t *from = NULL;
1639
1640 if (tmpl_request_ptr(&context, tmpl_request(map->rhs)) == 0) {
1641 from = tmpl_list_head(context, tmpl_list(map->rhs));
1642 }
1643 if (!from) return 0;
1644
1645 if (fr_pair_list_copy(ctx, &found, from) < 0) return -1;
1646
1647 /*
1648 * List to list copy is empty if the src list has no attributes.
1649 */
1650 if (fr_pair_list_empty(&found)) return 0;
1651
1652 fr_pair_list_foreach(&found, vp) {
1653 vp->op = T_OP_ADD_EQ;
1654 }
1655
1656 fr_pair_list_append(out, &found);
1657
1658 return 0;
1659 }
1660
1661 /*
1662 * And parse the RHS
1663 */
1664 switch (map->rhs->type) {
1665 case TMPL_TYPE_XLAT:
1666 fr_assert(tmpl_is_attr(map->lhs));
1667 fr_assert(tmpl_attr_tail_da(map->lhs)); /* We need to know which attribute to create */
1668 fr_assert(tmpl_xlat(map->rhs) != NULL);
1669
1671
1672 /*
1673 * We do the debug printing because xlat_aeval_compiled
1674 * doesn't have access to the original string. It's been
1675 * mangled during the parsing to an internal data structure
1676 */
1677 RDEBUG2("EXPAND %s", map->rhs->name);
1678 RINDENT();
1679
1680 str = NULL;
1681 slen = xlat_aeval_compiled(request, &str, request, tmpl_xlat(map->rhs), NULL, NULL);
1682 REXDENT();
1683
1684 if (slen < 0) {
1685 rcode = -1;
1686 goto error;
1687 }
1688
1689 RDEBUG2("--> %s", str);
1690
1691 rcode = fr_pair_value_from_str(n, str, strlen(str), NULL, false);
1692 talloc_free(str);
1693 if (rcode < 0) {
1694 goto error;
1695 }
1696
1697 n->op = map->op;
1699 break;
1700
1702 fr_assert(tmpl_is_attr(map->lhs));
1703 fr_assert(tmpl_attr_tail_da(map->lhs)); /* We need to know which attribute to create */
1704
1706
1707 if (fr_pair_value_from_str(n, map->rhs->name, strlen(map->rhs->name), NULL, false) < 0) {
1708 rcode = -1;
1709 goto error;
1710 }
1711 n->op = map->op;
1713 break;
1714
1715 case TMPL_TYPE_ATTR:
1716 {
1717 fr_pair_t *vp;
1718 fr_dcursor_t from;
1719
1721
1722 /*
1723 * @todo should log error, and return -1 for v3.1 (causes update to fail)
1724 */
1725 if (tmpl_copy_pairs(ctx, &found, request, map->rhs) < 0) return 0;
1726
1727 vp = fr_pair_dcursor_init(&from, &found);
1728
1729 /*
1730 * Src/Dst attributes don't match, convert src attributes
1731 * to match dst.
1732 */
1733 if (tmpl_is_attr(map->lhs) && tmpl_attr_tail_da_is_leaf(map->lhs) &&
1734 (tmpl_attr_tail_da(map->rhs)->type != tmpl_attr_tail_da(map->lhs)->type)) {
1735 for (; vp; vp = fr_dcursor_current(&from)) {
1737
1738 if (fr_value_box_cast(n, &n->data,
1739 tmpl_attr_tail_da(map->lhs)->type, tmpl_attr_tail_da(map->lhs), &vp->data) < 0) {
1740 RPEDEBUG("Attribute conversion failed");
1741 fr_pair_list_free(&found);
1742 talloc_free(n);
1743 return -1;
1744 }
1745 vp = fr_dcursor_remove(&from); /* advances cursor */
1746 talloc_free(vp);
1747
1748 fr_assert((n->vp_type != FR_TYPE_STRING) || (n->vp_strvalue != NULL));
1749
1750 n->op = map->op;
1752 }
1753
1754 return 0;
1755 }
1756
1757 /*
1758 * Otherwise we just need to fixup the attribute types
1759 * and operators
1760 */
1761 for (; vp; vp = fr_dcursor_next(&from)) {
1763 vp->op = map->op;
1764 }
1765 fr_pair_list_append(out, &found);
1766 }
1767 break;
1768
1769 case TMPL_TYPE_DATA:
1771 fr_assert(tmpl_is_attr(map->lhs));
1772
1774
1775 if (tmpl_attr_tail_da(map->lhs)->type == tmpl_value_type(map->rhs)) {
1776 if (unlikely(fr_value_box_copy(n, &n->data, tmpl_value(map->rhs)) < 0)) {
1777 rcode = -1;
1778 talloc_free(n);
1779 goto error;
1780 }
1781
1782 } else if (fr_value_box_cast(n, &n->data, n->vp_type, n->da, tmpl_value(map->rhs)) < 0) {
1783 RPEDEBUG("Implicit cast failed");
1784 rcode = -1;
1785 goto error;
1786 }
1787 n->op = map->op;
1789
1790 MAP_VERIFY(map);
1791 break;
1792
1793 /*
1794 * This essentially does the same as rlm_exec xlat, except it's non-configurable.
1795 * It's only really here as a convenience for people who expect the contents of
1796 * backticks to be executed in a shell.
1797 *
1798 * exec string is xlat expanded and arguments are shell escaped.
1799 */
1800 case TMPL_TYPE_EXEC:
1801 return map_exec_to_vp(ctx, out, request, map);
1802
1803 default:
1804 fr_strerror_const("Internal sanity check failure");
1805 rcode = -1;
1806 error:
1807 talloc_free(n);
1808 return rcode;
1809 }
1810
1811 return 0;
1812}
1813
1814#define DEBUG_OVERWRITE(_old, _new) \
1815do {\
1816 if (RDEBUG_ENABLED3) {\
1817 char *our_old; \
1818 char *our_new; \
1819 fr_pair_aprint_value_quoted(request, &our_old, _old, T_DOUBLE_QUOTED_STRING); \
1820 fr_pair_aprint_value_quoted(request, &our_new, _new, T_DOUBLE_QUOTED_STRING); \
1821 RINDENT(); \
1822 RDEBUG3("--> overwriting %s with %s", our_old, our_new); \
1823 REXDENT(); \
1824 talloc_free(our_old); \
1825 talloc_free(our_new); \
1826 } \
1827} while (0)
1828
1829/** Convert #map_t to #fr_pair_t (s) and add them to a #request_t.
1830 *
1831 * Takes a single #map_t, resolves request and list identifiers
1832 * to pointers in the current request, then attempts to retrieve module
1833 * specific value(s) using callback, and adds the resulting values to the
1834 * correct request/list.
1835 *
1836 * @param request The current request.
1837 * @param map specifying destination attribute and location and src identifier.
1838 * @param func to retrieve module specific values and convert them to
1839 * #fr_pair_t.
1840 * @param ctx to be passed to func.
1841 * @return
1842 * - -1 if the operation failed.
1843 * - -2 in the source attribute wasn't valid.
1844 * - 0 on success.
1845 */
1847{
1848 int rcode = 0;
1849 fr_pair_t *dst;
1850 fr_pair_list_t *list, src_list;
1851 request_t *context, *tmp_ctx = NULL;
1852 TALLOC_CTX *parent;
1853 fr_dcursor_t dst_list;
1854
1855 map_t exp_map;
1856 tmpl_t *exp_lhs;
1857 fr_dict_attr_t const *list_ref;
1858
1859 tmpl_dcursor_ctx_t cc = {};
1860
1861 fr_pair_list_init(&src_list);
1862 MAP_VERIFY(map);
1863 fr_assert(map->lhs != NULL);
1864 fr_assert(map->rhs != NULL);
1865
1866 /*
1867 * This function is called only when creating attributes. It cannot be called for conditions.
1868 */
1869 if (fr_comparison_op[map->op]) {
1870 REDEBUG("Invalid operator in %s %s ...", map->lhs->name, fr_tokens[map->op]);
1871 return -1;
1872 }
1873
1874 tmp_ctx = talloc_pool(request, 1024);
1875
1876 /*
1877 * Preprocessing of the LHS of the map.
1878 */
1879 switch (map->lhs->type) {
1880 /*
1881 * Already in the correct form.
1882 */
1883 case TMPL_TYPE_ATTR:
1884 break;
1885
1886 /*
1887 * Everything else gets expanded, then re-parsed as an attribute reference.
1888 *
1889 * This allows the syntax like:
1890 * - "Attr-%{number}" := "value"
1891 */
1892 case TMPL_TYPE_XLAT:
1893 case TMPL_TYPE_EXEC:
1894 {
1895 char *attr_str;
1896 fr_slen_t slen;
1897
1898 slen = tmpl_aexpand(request, &attr_str, request, map->lhs, NULL, NULL);
1899 if (slen <= 0) {
1900 RPEDEBUG("Left side expansion failed");
1901 fr_assert(!attr_str);
1902 rcode = -1;
1903 goto finish;
1904 }
1905
1906 slen = tmpl_afrom_attr_str(tmp_ctx, NULL, &exp_lhs, attr_str,
1907 &(tmpl_rules_t){
1908 .attr = {
1909 .dict_def = request->local_dict,
1910 .list_def = request_attr_request,
1911 }
1912 });
1913 if (slen <= 0) {
1914 RPEDEBUG("Left side expansion result \"%s\" is not an attribute reference", attr_str);
1915 talloc_free(attr_str);
1916 rcode = -1;
1917 goto finish;
1918 }
1919 talloc_free(attr_str);
1920 fr_assert(tmpl_is_attr(exp_lhs));
1921
1922 memcpy(&exp_map, map, sizeof(exp_map));
1923 exp_map.lhs = exp_lhs;
1924 map = &exp_map;
1925 }
1926 break;
1927
1928 default:
1929 fr_assert(0);
1930 break;
1931 }
1932
1933
1934 /*
1935 * Sanity check inputs. We can have a list or attribute
1936 * as a destination.
1937 */
1938 if (!tmpl_is_attr(map->lhs)) {
1939 REDEBUG("Left side \"%.*s\" of map should be an attr or list but is an %s",
1940 (int)map->lhs->len, map->lhs->name,
1941 tmpl_type_to_str(map->lhs->type));
1942 rcode = -2;
1943 goto finish;
1944 }
1945
1946 context = request;
1947 if (tmpl_request_ptr(&context, tmpl_request(map->lhs)) < 0) {
1948 RPEDEBUG("Mapping \"%.*s\" -> \"%.*s\" cannot be performed due to error in left side of map",
1949 (int)map->rhs->len, map->rhs->name, (int)map->lhs->len, map->lhs->name);
1950 rcode = -2;
1951 goto finish;
1952 }
1953
1954 list_ref = tmpl_list(map->lhs);
1955 list = tmpl_list_head(context, list_ref);
1956 if (!list) {
1957 REDEBUG("Mapping \"%.*s\" -> \"%.*s\" cannot be performed due to invalid list qualifier \"%s\" in left side of map",
1958 (int)map->rhs->len, map->rhs->name, (int)map->lhs->len, map->lhs->name,
1959 tmpl_list_name(list_ref, "<INVALID>"));
1960 rcode = -2;
1961 goto finish;
1962 }
1963
1966
1967 /*
1968 * The callback should either return -1 to signify operations error,
1969 * -2 when it can't find the attribute or list being referenced, or
1970 * 0 to signify success. It may return "success", but still have no
1971 * VPs to work with.
1972 */
1973 rcode = func(parent, &src_list, request, map, ctx);
1974 if (rcode < 0) {
1975 fr_assert(fr_pair_list_empty(&src_list));
1976 goto finish;
1977 }
1978 if (fr_pair_list_empty(&src_list)) {
1979 RDEBUG2("%.*s skipped: No values available", (int)map->lhs->len, map->lhs->name);
1980 goto finish;
1981 }
1982
1983 /*
1984 * Print the VPs
1985 */
1986#ifndef WITH_VERIFY_PTR
1987 if (RDEBUG_ENABLED)
1988#endif
1989 {
1990 fr_pair_list_foreach(&src_list, vp) {
1991 PAIR_VERIFY(vp);
1992
1994 }
1995 }
1996
1997 /*
1998 * The destination is a list (which is a completely different set of operations)
1999 */
2000 if (tmpl_is_list(map->lhs)) {
2001 switch (map->op) {
2002 case T_OP_CMP_FALSE:
2003 /* We don't need the src VPs (should just be 'ANY') */
2004 fr_assert(fr_pair_list_empty(&src_list));
2005
2006 /* Clear the entire dst list */
2007 fr_pair_list_free(list);
2008 goto finish;
2009
2010 case T_OP_SET:
2011 if (tmpl_is_list(map->rhs)) {
2012 fr_pair_list_free(list);
2013 fr_pair_list_append(list, &src_list);
2014 fr_pair_list_init(&src_list);
2015 } else {
2017
2018 case T_OP_EQ:
2019 fr_assert(tmpl_is_exec(map->rhs));
2021
2022 case T_OP_ADD_EQ:
2023 fr_pair_list_move_op(list, &src_list, T_OP_ADD_EQ);
2024 }
2025 goto finish;
2026
2027 case T_OP_PREPEND:
2028 fr_pair_list_move_op(list, &src_list, T_OP_PREPEND);
2029 goto finish;
2030
2031 default:
2032 rcode = -1;
2033 goto finish;
2034 }
2035 }
2036
2037 /*
2038 * Find the destination attribute. We leave with either
2039 * the dst_list and vp pointing to the attribute or the VP
2040 * being NULL (no attribute at that index).
2041 */
2042 dst = tmpl_dcursor_init(NULL, tmp_ctx, &cc, &dst_list, request, map->lhs);
2043
2044 /*
2045 * The destination is an attribute
2046 */
2047 switch (map->op) {
2048 default:
2049 break;
2050 /*
2051 * !* - Remove all attributes which match dst in the specified list.
2052 * This doesn't use attributes returned by the func(), and immediately frees them.
2053 */
2054 case T_OP_CMP_FALSE:
2055 /* We don't need the src VPs (should just be 'ANY') */
2056 fr_assert(fr_pair_list_empty(&src_list));
2057 if (!dst) goto finish;
2058
2059 /*
2060 * Wildcard: delete all of the matching ones
2061 */
2062 if (tmpl_attr_tail_num(map->lhs) == NUM_UNSPEC) {
2063 fr_pair_delete_by_child_num(list, tmpl_attr_tail_da(map->lhs)->parent, tmpl_attr_tail_da(map->lhs)->attr);
2064 dst = NULL;
2065 /*
2066 * We've found the Nth one. Delete it, and only it.
2067 */
2068 } else {
2069 dst = fr_dcursor_remove(&dst_list);
2070 talloc_free(dst);
2071 }
2072
2073 /*
2074 * Check that the User-Name and User-Password
2075 * caches point to the correct attribute.
2076 */
2077 goto finish;
2078
2079 /*
2080 * -= - Delete attributes in the dst list which match any of the
2081 * src_list attributes.
2082 *
2083 * This operation has two modes:
2084 * - If tmpl_attr_tail_num(map->lhs) > 0, we check each of the src_list attributes against
2085 * the dst attribute, to see if any of their values match.
2086 * - If tmpl_attr_tail_num(map->lhs) == NUM_UNSPEC, we compare all instances of the dst attribute
2087 * against each of the src_list attributes.
2088 */
2089 case T_OP_SUB_EQ:
2090 /* We didn't find any attributes earlier */
2091 if (!dst) {
2092 goto finish;
2093 } else {
2094 fr_pair_list_t free_list;
2095
2096 fr_pair_list_init(&free_list);
2097
2098 /*
2099 * Instance specific[n] delete
2100 */
2101 if (tmpl_attr_tail_num(map->lhs) != NUM_UNSPEC) {
2102 fr_pair_list_foreach(&src_list, vp) {
2103 vp->op = T_OP_CMP_EQ;
2104 rcode = paircmp_pairs(request, vp, dst);
2105 if (rcode == 0) {
2106 dst = fr_dcursor_remove(&dst_list);
2107 fr_pair_append(&free_list, dst);
2108 break;
2109 }
2110 }
2111
2112 goto done_subeq;
2113 }
2114
2115 /*
2116 * All instances[*] delete
2117 */
2118 for (dst = fr_dcursor_current(&dst_list);
2119 dst;
2121 fr_pair_list_foreach(&src_list, vp) {
2122 vp->op = T_OP_CMP_EQ;
2123 rcode = paircmp_pairs(request, vp, dst);
2124 if (rcode == 0) {
2125 dst = fr_dcursor_remove(&dst_list);
2126 fr_pair_append(&free_list, dst);
2127 }
2128 }
2129 }
2130
2131 done_subeq:
2132 fr_pair_list_free(&free_list);
2133 }
2134
2135 rcode = 0;
2136 goto finish;
2137 }
2138
2139 switch (map->op) {
2140 /*
2141 * = - Set only if not already set
2142 */
2143 case T_OP_EQ:
2144 {
2145 tmpl_attr_extent_t *extent = NULL;
2146 fr_dlist_head_t leaf;
2147 fr_dlist_head_t interior;
2148 fr_pair_t *src_vp;
2149
2150 if (dst) {
2151 RDEBUG3("Refusing to overwrite (use :=)");
2152 goto finish;
2153 }
2154
2156 fr_dlist_talloc_init(&interior, tmpl_attr_extent_t, entry);
2157
2158 /*
2159 * Find out what we need to build and build it
2160 */
2161 if ((tmpl_extents_find(tmp_ctx, &leaf, &interior, request, map->lhs) < 0) ||
2162 (tmpl_extents_build_to_leaf_parent(&leaf, &interior, map->lhs) < 0)) {
2163 fr_dlist_talloc_free(&leaf);
2164 fr_dlist_talloc_free(&interior);
2165 rcode = -1;
2166 goto finish;
2167 }
2168
2169 /*
2170 * Need to copy src to all dsts
2171 */
2172 src_vp = fr_pair_list_head(&src_list);
2173 if (!src_vp) {
2174 fr_dlist_talloc_free(&leaf);
2175 rcode = -1;
2176 goto finish;
2177 }
2178
2179 if (fr_dlist_num_elements(&leaf) > 1) {
2180 while ((extent = fr_dlist_tail(&leaf))) {
2181 fr_pair_append(extent->list, fr_pair_copy(extent->list_ctx, src_vp));
2183 }
2184 } else {
2185 extent = fr_dlist_head(&leaf);
2186 fr_pair_append(extent->list, fr_pair_copy(extent->list_ctx, src_vp));
2188 }
2189
2190 /* Free any we didn't insert */
2191 fr_assert(fr_dlist_num_elements(&interior) == 0);
2192 fr_assert(fr_dlist_num_elements(&leaf) == 0);
2193 }
2194 break;
2195
2196 /*
2197 * := - Overwrite existing attribute with last src_list attribute
2198 */
2199 case T_OP_SET:
2200 {
2201 tmpl_attr_extent_t *extent = NULL;
2202 fr_dlist_head_t leaf;
2203 fr_dlist_head_t interior;
2204 fr_pair_t *src_vp;
2205
2206 src_vp = fr_pair_list_tail(&src_list);
2207
2208 if (dst) {
2209 DEBUG_OVERWRITE(dst, src_vp);
2210
2211 fr_pair_reinit_from_da(NULL, dst, src_vp->da);
2212 fr_pair_value_copy(dst, src_vp);
2213 break;
2214 }
2215
2217 fr_dlist_talloc_init(&interior, tmpl_attr_extent_t, entry);
2218
2219 /*
2220 * Find out what we need to build and build it
2221 */
2222 if ((tmpl_extents_find(tmp_ctx, &leaf, &interior, request, map->lhs) < 0) ||
2223 (tmpl_extents_build_to_leaf_parent(&leaf, &interior, map->lhs) < 0)) {
2224 op_set_error:
2225 fr_dlist_talloc_free(&leaf);
2226 fr_dlist_talloc_free(&interior);
2227 rcode = -1;
2228 goto finish;
2229 }
2230
2231 if (fr_dlist_num_elements(&leaf) > 1) {
2232 ERROR("Not yet supported");
2233 goto op_set_error;
2234 }
2235
2236 extent = fr_dlist_head(&leaf);
2237 fr_pair_append(extent->list, fr_pair_copy(extent->list_ctx, src_vp));
2238
2239 fr_assert(fr_dlist_num_elements(&interior) == 0);
2240 fr_dlist_talloc_free(&leaf);
2241 }
2242 break;
2243
2244 /*
2245 * ^= - Prepend src_list attributes to the destination
2246 */
2247 case T_OP_PREPEND:
2248 fr_pair_list_prepend(list, &src_list);
2249 break;
2250
2251 /*
2252 * += - Add all src_list attributes to the destination
2253 */
2254 case T_OP_ADD_EQ:
2255 {
2256 tmpl_attr_extent_t *extent = NULL;
2257 fr_dlist_head_t leaf;
2258 fr_dlist_head_t interior;
2259
2261 fr_dlist_talloc_init(&interior, tmpl_attr_extent_t, entry);
2262
2263 /*
2264 * Find out what we need to build and build it
2265 */
2266 if ((tmpl_extents_find(tmp_ctx, &leaf, &interior, request, map->lhs) < 0) ||
2267 (tmpl_extents_build_to_leaf_parent(&leaf, &interior, map->lhs) < 0)) {
2268 fr_dlist_talloc_free(&leaf);
2269 fr_dlist_talloc_free(&interior);
2270 rcode = -1;
2271 goto finish;
2272 }
2273
2274 if (fr_dlist_num_elements(&leaf) > 1) {
2275 while ((extent = fr_dlist_tail(&leaf))) {
2276 (void) fr_pair_list_copy(extent->list_ctx, extent->list, &src_list);
2278 }
2279 } else {
2280 extent = fr_dlist_head(&leaf);
2281 (void) fr_pair_list_copy(extent->list_ctx, extent->list, &src_list);
2283 }
2284
2285 fr_assert(fr_dlist_num_elements(&interior) == 0);
2286 fr_assert(fr_dlist_num_elements(&leaf) == 0);
2287 }
2288 break;
2289
2290 /*
2291 * Filter operators
2292 */
2293 case T_OP_NE:
2294 case T_OP_CMP_EQ:
2295 case T_OP_GE:
2296 case T_OP_GT:
2297 case T_OP_LE:
2298 case T_OP_LT:
2299 {
2300 fr_pair_t *a;
2301
2304
2305 fr_dcursor_head(&dst_list);
2306
2307 fr_pair_list_foreach(&src_list, b) {
2308 for (a = fr_dcursor_current(&dst_list);
2309 a;
2310 a = fr_dcursor_next(&dst_list)) {
2311 int8_t cmp;
2312
2313 cmp = fr_pair_cmp_by_da(a, b); /* attribute and tag match */
2314 if (cmp > 0) break;
2315 else if (cmp < 0) continue;
2316
2317 cmp = (fr_value_box_cmp_op(map->op, &a->data, &b->data) == 0);
2318 if (cmp != 0) {
2319 a = fr_dcursor_remove(&dst_list);
2320 talloc_free(a);
2321 }
2322 }
2323 if (!a) break; /* end of the list */
2324 }
2325 }
2326 break;
2327
2328 default:
2329 fr_assert(0); /* Should have been caught be the caller */
2330 rcode = -1;
2331 break;
2332 }
2333
2334finish:
2335 fr_pair_list_free(&src_list);
2336 tmpl_dcursor_clear(&cc);
2337 talloc_free(tmp_ctx);
2338 return rcode;
2339}
2340
2341/** Print a map to a string
2342 *
2343 * @param[out] out Buffer to write string to.
2344 * @param[in] map to print.
2345 * @return
2346 * - The number of bytes written to the out buffer.
2347 * - A number >= outlen if truncation has occurred.
2348 */
2350{
2351 fr_sbuff_t our_out = FR_SBUFF(out);
2352
2353 MAP_VERIFY(map);
2354
2355 /*
2356 * Print the lhs
2357 */
2358 if (tmpl_rules_cast(map->lhs)) {
2359 FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "<%s>",
2361 }
2362 FR_SBUFF_RETURN(tmpl_print_quoted, &our_out, map->lhs);
2363
2364 /*
2365 * Print separators and operator
2366 */
2367 FR_SBUFF_IN_CHAR_RETURN(&our_out, ' ');
2369 FR_SBUFF_IN_CHAR_RETURN(&our_out, ' ');
2370
2371 /*
2372 * The RHS doesn't matter for many operators
2373 */
2374 if ((map->op == T_OP_CMP_TRUE) || (map->op == T_OP_CMP_FALSE)) {
2375 FR_SBUFF_IN_STRCPY_RETURN(&our_out, "ANY");
2376 FR_SBUFF_SET_RETURN(out, &our_out);
2377 }
2378
2379 /*
2380 * If there's no RHS but no children, then the map was
2381 * invalid.
2382 */
2383 if (!map->rhs) {
2384 fr_assert(!map_list_empty(&map->child));
2385 fr_sbuff_terminate(out);
2386 return 0;
2387 }
2388
2389 if (tmpl_rules_cast(map->rhs)) {
2390 FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "<%s>",
2392 }
2393
2394 /*
2395 * Print the RHS.
2396 */
2397 FR_SBUFF_RETURN(tmpl_print_quoted, &our_out, map->rhs);
2398
2399 FR_SBUFF_SET_RETURN(out, &our_out);
2400}
2401
2402/*
2403 * Debug print a map / VP
2404 */
2406{
2407 char *rhs = NULL, *value = NULL;
2408 char buffer[256];
2409
2410 MAP_VERIFY(map);
2411 if (!fr_cond_assert(map->lhs != NULL)) return;
2412 if (!fr_cond_assert(map->rhs != NULL)) return;
2413
2414 fr_assert(vp);
2415
2416 switch (map->rhs->type) {
2417 /*
2418 * Just print the value being assigned
2419 */
2420 default:
2422 fr_pair_aprint_value_quoted(request, &rhs, vp, map->rhs->quote);
2423 break;
2424
2425 case TMPL_TYPE_XLAT:
2426 fr_pair_aprint_value_quoted(request, &rhs, vp, map->rhs->quote);
2427 break;
2428
2429 case TMPL_TYPE_DATA:
2430 fr_pair_aprint_value_quoted(request, &rhs, vp, map->rhs->quote);
2431 break;
2432
2433 case TMPL_TYPE_ATTR:
2434 {
2435 fr_token_t quote;
2436
2437 switch (vp->vp_type) {
2438 case FR_TYPE_QUOTED:
2439 quote = T_DOUBLE_QUOTED_STRING;
2440 break;
2441 default:
2442 quote = T_BARE_WORD;
2443 break;
2444 }
2445
2446 /*
2447 * Not appropriate to use map->rhs->quote here, as that's the quoting
2448 * around the attr ref. The attribute value has no quoting, so we choose
2449 * the quoting based on the data type.
2450 */
2452 tmpl_print(&FR_SBUFF_OUT(buffer, sizeof(buffer)), map->rhs, NULL);
2453 rhs = talloc_typed_asprintf(request, "%s -> %s", buffer, value);
2454 }
2455 break;
2456 }
2457
2458 switch (map->lhs->type) {
2459 case TMPL_TYPE_ATTR:
2460 tmpl_print(&FR_SBUFF_OUT(buffer, sizeof(buffer)), map->lhs, NULL);
2461 RDEBUG2("%s %s %s", buffer, fr_table_str_by_value(fr_tokens_table, vp ? vp->op : map->op, "<INVALID>"), rhs);
2462 break;
2463
2464 default:
2465 break;
2466 }
2467
2468 /*
2469 * Must be LIFO free order so we don't leak pool memory
2470 */
2471 talloc_free(rhs);
2473}
2474
2475/** Convert a fr_pair_t into a map
2476 *
2477 * @param[in] ctx where to allocate the map.
2478 * @param[out] out Where to write the new map (must be freed with talloc_free()).
2479 * @param[in,out] parent_p the parent map, updated for relative maps
2480 * @param[in] request the request
2481 * @param[in] lhs of map
2482 * @param[in] op_str operator for map
2483 * @param[in] rhs of map
2484 * @param[in] lhs_rules for parsing the LHS
2485 * @param[in] rhs_rules for parsing the RHS
2486 * @param[in] bare_word_only RHS is bare words, and nothing else.
2487 * @return
2488 * - 0 on success.
2489 * - -1 on failure.
2490 */
2491int map_afrom_fields(TALLOC_CTX *ctx, map_t **out, map_t **parent_p, request_t *request,
2492 char const *lhs, char const *op_str, char const *rhs,
2493 tmpl_rules_t const *lhs_rules, tmpl_rules_t const *rhs_rules, bool bare_word_only)
2494{
2495 fr_slen_t slen;
2496 fr_token_t quote, op;
2497 map_t *map;
2498 map_t *parent = *parent_p;
2499 tmpl_rules_t my_lhs_rules, my_rhs_rules;
2500
2502 if (op == T_INVALID) {
2503 RPEDEBUG("Invalid operator '%s'", op_str);
2504 return -1;
2505 }
2506
2507 /*
2508 * Reply items can be expanded. Check items cannot be,
2509 * unless the operator is a comparison operator.
2510 */
2511 if (!bare_word_only) {
2512 if (!fr_assignment_op[op]) {
2513 RPEDEBUG("Invalid operator '%s' for assignment in reply item", op_str);
2514 return -1;
2515 }
2516
2517 } else if (!fr_assignment_op[op] && !fr_comparison_op[op]) {
2518 RPEDEBUG("Invalid operator '%s' for check item", op_str);
2519 return -1;
2520 }
2521
2522 my_lhs_rules = *lhs_rules;
2523 lhs_rules = &my_lhs_rules;
2524
2525 /*
2526 * We're only called from SQL. If the default list is request, then we only use that for
2527 * comparisons. We rewrite assignments to use the control list.
2528 *
2529 * @todo - as we expand the use of this function, perhaps add another argument which controls
2530 * this flag. But this function already has parameter overload :(
2531 */
2532 if (fr_assignment_op[op] && (lhs_rules->attr.list_def == request_attr_request)) {
2533 my_lhs_rules.attr.list_def = request_attr_control;
2534 }
2535
2536 /*
2537 * One '.' means "the current parent".
2538 */
2539 if (*lhs == '.') {
2540 if (!parent) {
2541 no_parent:
2542 RPEDEBUG("Unexpected location for relative attribute - no parent attribute exists");
2543 return -1;
2544 }
2545 lhs++;
2546
2547 /*
2548 * Multiple '.' means "go to our parents parent".
2549 */
2550 while (*lhs == '.') {
2551 if (!parent) goto no_parent;
2552
2553 parent = parent->parent;
2554 lhs++;
2555 }
2556
2557 /*
2558 * Child elements can only be "=".
2559 */
2560 if (parent) {
2561 if (fr_comparison_op[op]) {
2562 RPEDEBUG("Comparison operators cannot be used inside of structural data types");
2563 return -1;
2564 }
2565
2566 if (op != T_OP_EQ) {
2567 RPEDEBUG("Invalid operator inside of structural data type - must be '='");
2568 return -1;
2569 }
2570 }
2571 }
2572
2573 MEM(map = map_alloc(ctx, parent));
2574 map->op = op;
2575
2576 /*
2577 * Start looking in the correct parent, not in whatever we were handed.
2578 */
2579 if (parent) {
2581 my_lhs_rules.attr.namespace = tmpl_attr_tail_da(parent->lhs);
2582
2583 slen = tmpl_afrom_attr_substr(map, NULL, &map->lhs, &FR_SBUFF_IN_STR(lhs),
2585 } else {
2586 /*
2587 * There's no '.', so this
2588 * attribute MUST come from the
2589 * root of the dictionary tree.
2590 */
2591 parent = NULL;
2592
2593 /*
2594 * Allocate the LHS, which must be an attribute.
2595 *
2596 * @todo - track relative attributes, which begin with a '.'
2597 */
2598 slen = tmpl_afrom_attr_str(map, NULL, &map->lhs, lhs, lhs_rules);
2599 }
2600 if (slen <= 0) {
2601 error:
2602 talloc_free(map);
2603 return -1;
2604 }
2605
2606 if (tmpl_attr_tail_is_unknown(map->lhs) && tmpl_attr_unknown_add(map->lhs) < 0) {
2607 RPEDEBUG("Failed creating attribute %s", map->lhs->name);
2608 goto error;
2609 }
2610
2611 my_rhs_rules = *rhs_rules;
2612 my_rhs_rules.at_runtime = true;
2614 my_rhs_rules.enumv = tmpl_attr_tail_da(map->lhs);
2615
2616 /*
2617 * LHS is a structureal type. The RHS is either empty (create empty LHS), or it's a string
2618 * containing a list of attributes to create.
2619 */
2620 if (!fr_type_is_leaf(my_rhs_rules.enumv->type)) {
2621 my_rhs_rules.enumv = NULL;
2622 }
2623
2624 /*
2625 * Try to figure out what we should do with the RHS.
2626 */
2627 if ((map->op == T_OP_CMP_TRUE) || (map->op == T_OP_CMP_FALSE)) {
2628 /*
2629 * These operators require a hard-coded string on the RHS.
2630 */
2631 if (strcmp(rhs, "ANY") != 0) {
2632 RPEDEBUG("Invalid value %s for operator %s", rhs, fr_tokens[map->op]);
2633 goto error;
2634 }
2635
2636 if (tmpl_afrom_value_box(map, &map->rhs, fr_box_strvalue("ANY"), false) < 0) goto error;
2637
2638 } else if (bare_word_only) {
2639 fr_value_box_t *vb;
2640
2641 /*
2642 * No value, or no enum, parse it as a bare-word string.
2643 */
2644 if (!rhs[0] || !my_rhs_rules.enumv) goto do_bare_word;
2645
2646 MEM(vb = fr_value_box_alloc(map, my_rhs_rules.enumv->type, my_rhs_rules.enumv));
2647
2648 /*
2649 * It MUST be the given data type.
2650 */
2651 slen = fr_value_box_from_str(map, vb, my_rhs_rules.enumv->type, my_rhs_rules.enumv,
2652 rhs, strlen(rhs), NULL);
2653 if (slen <= 0) goto error;
2654
2655 if (tmpl_afrom_value_box(map, &map->rhs, vb, true) < 0) {
2656 goto error;
2657 }
2658
2659 } else if (rhs[0] == '"') {
2660 /*
2661 * We've been asked to expand the RHS. Passwords like
2662 *
2663 * "%{Calling-Station-ID}"
2664 *
2665 * might not do what you want.
2666 */
2667 quote = T_DOUBLE_QUOTED_STRING;
2668 goto parse_quoted;
2669
2670 } else if (rhs[0] == '\'') {
2671 size_t len;
2672
2673 quote = T_SINGLE_QUOTED_STRING;
2674
2675 parse_quoted:
2676 len = strlen(rhs + 1);
2677 if (len == 0) {
2678 RPEDEBUG("Unclosed quote on right side");
2679 return -1;
2680 }
2681
2682 if (len == 1) {
2683 if (rhs[1] != rhs[0]) {
2684 RPEDEBUG("Invalid string on right side");
2685 goto error;
2686 }
2687
2688 rhs = "";
2689 goto alloc_empty;
2690 }
2691
2692 slen = tmpl_afrom_substr(map, &map->rhs, &FR_SBUFF_IN(rhs + 1, len),
2693 quote, value_parse_rules_quoted[quote], &my_rhs_rules);
2694 if (slen < 0) {
2695 REDEBUG("Failed parsing right-hand side as quoted string.");
2696 goto error;
2697
2698 fail_rhs:
2699 RPEDEBUG("Failed parsing right-hand side of assignment");
2700 goto error;
2701 }
2702
2703 fr_assert((size_t) slen <= (len + 1));
2704
2705 if (rhs[slen + 1] != rhs[0]) {
2706 RPEDEBUG("Failed parsing right-hand side, missing end quote in ... %s", rhs);
2707 goto error;
2708 }
2709
2710 if (rhs[slen + 2]) {
2711 RPEDEBUG("Failed parsing right-hand side, unexpected data after end quote");
2712 goto error;
2713 }
2714
2715 if (slen == 0) {
2716 rhs = "";
2717 goto alloc_empty;
2718 }
2719
2720 } else if (rhs[0] == '&') {
2721 /*
2722 * No enums here.
2723 */
2725
2726 parse_as_attr:
2727 my_rhs_rules.enumv = NULL;
2728
2729 slen = tmpl_afrom_attr_str(map, NULL, &map->rhs, rhs, rhs_rules);
2730 if (slen <= 0) {
2731 REDEBUG3("Failed parsing right-hand side as attribute.");
2732 goto fail_rhs;
2733 }
2734
2735 } else if (!rhs[0] || !my_rhs_rules.enumv || (my_rhs_rules.enumv->type == FR_TYPE_STRING)) {
2736 do_bare_word:
2737 quote = T_BARE_WORD;
2738
2739 if (tmpl_attr_tail_da_is_structural(map->lhs) && !*rhs) goto done;
2740
2741 alloc_empty:
2742 MEM(map->rhs = tmpl_alloc(map, TMPL_TYPE_DATA, quote, rhs, strlen(rhs)));
2743
2744 /*
2745 * Create it when we have
2746 *
2747 * my-struct = ""
2748 */
2749 (void) fr_value_box_strdup(map->rhs, tmpl_value(map->rhs), NULL, rhs, false);
2750
2751 } else {
2752 /*
2753 * Parse it as the given data type.
2754 */
2755 slen = tmpl_afrom_substr(map, &map->rhs, &FR_SBUFF_IN_STR(rhs),
2757 if (slen <= 0) {
2758 goto parse_as_attr;
2759 }
2760
2761 /*
2762 * Xlat expansions are cast to strings for structural data types.
2763 */
2764 if (tmpl_attr_tail_da_is_structural(map->lhs) && (tmpl_is_xlat(map->rhs))) {
2766 }
2767 }
2768
2769 if (tmpl_needs_resolving(map->rhs)) {
2771 .dict_def = lhs_rules->attr.dict_def,
2772 .enumv = tmpl_attr_tail_da(map->lhs)
2773 };
2774
2776
2777 if (tmpl_resolve(map->rhs, &tr_rules) < 0) {
2778 REDEBUG3("Failed resolving right-hand side.");
2779 goto fail_rhs;
2780 }
2781 }
2782
2783 /*
2784 * @todo - check that the entire string was parsed.
2785 */
2786
2787done:
2788 /*
2789 * If the tail is a leaf, we don't change parent.
2790 * Otherwise the structural attribute is the new parent.
2791 */
2792 if (tmpl_attr_tail_da_is_leaf(map->lhs)) {
2793 *parent_p = parent;
2794 } else {
2795 *parent_p = map;
2796 }
2797
2798 MAP_VERIFY(map);
2799
2800 if (parent) map_list_insert_tail(&parent->child, map);
2801 *out = map;
2802
2803 return 0;
2804}
static int const char char buffer[256]
Definition acutest.h:641
int n
Definition acutest.h:642
static int context
Definition radmin.c:69
static uint8_t request[4]
#define RCSID(id)
Definition build.h:603
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:245
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition build.h:422
#define unlikely(_x)
Definition build.h:486
#define UNUSED
Definition build.h:415
#define NUM_ELEMENTS(_t)
Definition build.h:437
Common header for all CONF_* types.
Definition cf_priv.h:54
Configuration AVP similar to a fr_pair_t.
Definition cf_priv.h:77
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:106
bool cf_item_is_pair(CONF_ITEM const *ci)
Determine if CONF_ITEM is a CONF_PAIR.
Definition cf_util.c:619
fr_token_t cf_pair_attr_quote(CONF_PAIR const *pair)
Return the value (lhs) quoting of a pair.
Definition cf_util.c:1787
char const * cf_section_name2(CONF_SECTION const *cs)
Return the second identifier of a CONF_SECTION.
Definition cf_util.c:1405
CONF_ITEM * cf_section_to_item(CONF_SECTION const *cs)
Cast a CONF_SECTION to a CONF_ITEM.
Definition cf_util.c:725
char const * cf_section_name1(CONF_SECTION const *cs)
Return the first identifier of a CONF_SECTION.
Definition cf_util.c:1391
CONF_SECTION * cf_item_to_section(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_SECTION.
Definition cf_util.c:671
fr_token_t cf_pair_operator(CONF_PAIR const *pair)
Return the operator of a pair.
Definition cf_util.c:1772
fr_token_t cf_pair_value_quote(CONF_PAIR const *pair)
Return the value (rhs) quoting of a pair.
Definition cf_util.c:1802
bool cf_item_is_section(CONF_ITEM const *ci)
Determine if CONF_ITEM is a CONF_SECTION.
Definition cf_util.c:605
CONF_PAIR * cf_item_to_pair(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_PAIR.
Definition cf_util.c:651
fr_token_t cf_section_name2_quote(CONF_SECTION const *cs)
Return the quoting of the name2 identifier.
Definition cf_util.c:1450
char const * cf_pair_value(CONF_PAIR const *pair)
Return the value of a CONF_PAIR.
Definition cf_util.c:1758
CONF_ITEM * cf_pair_to_item(CONF_PAIR const *cp)
Cast a CONF_PAIR to a CONF_ITEM.
Definition cf_util.c:709
char const * cf_pair_attr(CONF_PAIR const *pair)
Return the attr of a CONF_PAIR.
Definition cf_util.c:1742
#define cf_log_err(_cf, _fmt,...)
Definition cf_util.h:340
#define cf_item_next(_parent, _curr)
Definition cf_util.h:91
#define cf_log_perr(_cf, _fmt,...)
Definition cf_util.h:347
fr_dict_t * dict
Definition common.c:31
static void * fr_dcursor_next(fr_dcursor_t *cursor)
Advanced the cursor to the next item.
Definition dcursor.h:288
static void * fr_dcursor_filter_next(fr_dcursor_t *cursor, fr_dcursor_eval_t eval, void const *uctx)
Return the next item, skipping the current item, that satisfies an evaluation function.
Definition dcursor.h:545
static void * fr_dcursor_remove(fr_dcursor_t *cursor)
Remove the current item.
Definition dcursor.h:480
static void * fr_dcursor_current(fr_dcursor_t *cursor)
Return the item the cursor current points to.
Definition dcursor.h:337
static void * fr_dcursor_head(fr_dcursor_t *cursor)
Rewind cursor to the start of the list.
Definition dcursor.h:232
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:177
#define MEM(x)
Definition debug.h:38
#define ERROR(fmt,...)
Definition dhcpclient.c:40
fr_dict_t const * fr_dict_by_da(fr_dict_attr_t const *da)
Attempt to locate the protocol dictionary containing an attribute.
Definition dict_util.c:2935
fr_dict_t const * fr_dict_internal(void)
Definition dict_util.c:5065
static fr_dict_attr_t const * fr_dict_attr_ref(fr_dict_attr_t const *da)
Return the reference associated with a group type attribute.
Definition dict_ext.h:149
Test enumeration values.
Definition dict_test.h:92
static void * fr_dlist_head(fr_dlist_head_t const *list_head)
Return the HEAD item of a list or NULL if the list is empty.
Definition dlist.h:468
static int fr_dlist_talloc_free_head(fr_dlist_head_t *list_head)
Free the first item in the list.
Definition dlist.h:837
static void fr_dlist_talloc_free(fr_dlist_head_t *head)
Free all items in a doubly linked list (with talloc)
Definition dlist.h:892
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
static void * fr_dlist_tail(fr_dlist_head_t const *list_head)
Return the TAIL item of a list or NULL if the list is empty.
Definition dlist.h:513
#define fr_dlist_talloc_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
Definition dlist.h:257
static int fr_dlist_talloc_free_tail(fr_dlist_head_t *list_head)
Free the last item in the list.
Definition dlist.h:847
Head of a doubly linked list.
Definition dlist.h:51
#define EXEC_TIMEOUT
Default wait time for exec calls (in seconds).
Definition exec.h:32
int radius_exec_program_legacy(char *out, size_t outlen, request_t *request, char const *cmd, fr_pair_list_t *input_pairs, bool exec_wait, bool shell_escape, fr_time_delta_t timeout)
Execute a program.
static tmpl_res_rules_t tr_rules
Definition fuzzer_tmpl.c:53
talloc_free(hp)
fr_event_list_t * unlang_interpret_event_list(request_t *request)
Get the event list for the current interpreter.
Definition interpret.c:2538
#define REXDENT()
Exdent (unindent) R* messages by one level.
Definition log.h:460
#define RDEBUG3(fmt,...)
Definition log.h:360
#define REDEBUG3(fmt,...)
Definition log.h:390
#define RPEDEBUG(fmt,...)
Definition log.h:393
#define RINDENT()
Indent R* messages by one level.
Definition log.h:447
static int map_value_afrom_cp(TALLOC_CTX *ctx, map_t **out, map_t *parent, CONF_PAIR *cp, tmpl_rules_t const *t_rules)
Convert CONFIG_PAIR (which may contain refs) to map_t.
Definition map.c:1230
int map_afrom_fields(TALLOC_CTX *ctx, map_t **out, map_t **parent_p, request_t *request, char const *lhs, char const *op_str, char const *rhs, tmpl_rules_t const *lhs_rules, tmpl_rules_t const *rhs_rules, bool bare_word_only)
Convert a fr_pair_t into a map.
Definition map.c:2491
#define DEBUG_OVERWRITE(_old, _new)
Definition map.c:1814
int map_to_vp(TALLOC_CTX *ctx, fr_pair_list_t *out, request_t *request, map_t const *map, UNUSED void *uctx)
Convert a map to a fr_pair_t.
Definition map.c:1566
int map_afrom_cs_edit(TALLOC_CTX *ctx, map_list_t *out, CONF_SECTION *cs, tmpl_rules_t const *lhs_rules, tmpl_rules_t const *rhs_rules, map_validate_t validate, void *uctx, unsigned int max)
Convert a config section into an attribute map for editing.
Definition map.c:1199
fr_table_num_sorted_t const map_assignment_op_table[]
Definition map.c:394
fr_sbuff_parse_rules_t const map_parse_rules_bareword_quoted
Definition map.c:412
ssize_t map_afrom_substr(TALLOC_CTX *ctx, map_t **out, map_t **parent_p, fr_sbuff_t *in, fr_table_num_sorted_t const *op_table, size_t op_table_len, tmpl_rules_t const *lhs_rules, tmpl_rules_t const *rhs_rules, fr_sbuff_parse_rules_t const *p_rules)
Parse sbuff into (which may contain refs) to map_t.
Definition map.c:492
static map_t * map_alloc(TALLOC_CTX *ctx, map_t *parent)
Definition map.c:73
static int _map_list_afrom_cs(TALLOC_CTX *ctx, map_list_t *out, map_t *parent, CONF_SECTION *cs, tmpl_rules_t const *t_rules, map_validate_t validate, void *uctx, unsigned int max)
Definition map.c:1310
int map_afrom_cp(TALLOC_CTX *ctx, map_t **out, map_t *parent, CONF_PAIR *cp, tmpl_rules_t const *lhs_rules, tmpl_rules_t const *input_rhs_rules, bool edit)
Convert CONFIG_PAIR (which may contain refs) to map_t.
Definition map.c:110
static fr_table_num_sorted_t const cond_quote_table[]
Definition map.c:46
int map_afrom_value_box(TALLOC_CTX *ctx, map_t **out, char const *lhs, fr_token_t lhs_quote, tmpl_rules_t const *lhs_rules, fr_token_t op, fr_value_box_t *rhs, bool steal_rhs_buffs)
Convert a value box to a map.
Definition map.c:1415
int map_afrom_cs(TALLOC_CTX *ctx, map_list_t *out, CONF_SECTION const *cs, tmpl_rules_t const *lhs_rules, tmpl_rules_t const *rhs_rules, map_validate_t validate, void *uctx, unsigned int max)
Convert a config section into an attribute map.
Definition map.c:1168
int map_to_request(request_t *request, map_t const *map, radius_map_getvalue_t func, void *ctx)
Convert map_t to fr_pair_t (s) and add them to a request_t.
Definition map.c:1846
static int map_exec_to_vp(TALLOC_CTX *ctx, fr_pair_list_t *out, request_t *request, map_t const *map)
Process map which has exec as a src.
Definition map.c:1500
static size_t cond_quote_table_len
Definition map.c:52
int map_list_afrom_cs(TALLOC_CTX *ctx, map_list_t *out, CONF_SECTION *cs, tmpl_rules_t const *t_rules, map_validate_t validate, void *uctx, unsigned int max)
Convert a config section into a list of { a, b, c, d, ... }.
Definition map.c:1384
static int _map_afrom_cs(TALLOC_CTX *ctx, map_list_t *out, map_t *parent, CONF_SECTION const *cs, tmpl_rules_t const *lhs_rules, tmpl_rules_t const *rhs_rules, map_validate_t validate, void *uctx, unsigned int max, bool update, bool edit)
Definition map.c:868
int map_afrom_attr_str(TALLOC_CTX *ctx, map_t **out, char const *vp_str, tmpl_rules_t const *lhs_rules, tmpl_rules_t const *rhs_rules)
Convert a value pair string to valuepair map.
Definition map.c:1465
ssize_t map_print(fr_sbuff_t *out, map_t const *map)
Print a map to a string.
Definition map.c:2349
size_t map_assignment_op_table_len
Definition map.c:410
fr_sbuff_parse_rules_t const * map_parse_rules_quoted[T_TOKEN_LAST]
Definition map.c:456
void map_debug_log(request_t *request, map_t const *map, fr_pair_t const *vp)
Definition map.c:2405
void fr_canonicalize_error(TALLOC_CTX *ctx, char **sp, char **text, ssize_t slen, char const *fmt)
Canonicalize error strings, removing tabs, and generate spaces for error marker.
Definition log.c:105
fr_type_t
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_UINT32
32 Bit unsigned integer.
@ FR_TYPE_GROUP
A grouping of other attributes.
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)
bool fr_pair_matches_da(void const *item, void const *uctx)
Evaluation function for matching if vp matches a given da.
Definition pair.c:3444
int fr_pair_list_copy(TALLOC_CTX *ctx, fr_pair_list_t *to, fr_pair_list_t const *from)
Duplicate a list of pairs.
Definition pair.c:2303
int fr_pair_value_from_str(fr_pair_t *vp, char const *value, size_t inlen, fr_sbuff_unescape_rules_t const *uerules, UNUSED bool tainted)
Convert string value to native attribute value.
Definition pair.c:2552
int fr_pair_append(fr_pair_list_t *list, fr_pair_t *to_add)
Add a VP to the end of the list.
Definition pair.c:1297
fr_cmp_ret_t fr_pair_cmp_by_da(void const *a, void const *b)
Order attributes by their da.
Definition pair.c:1796
fr_pair_t * fr_pair_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
Dynamically allocate a new attribute and assign a fr_dict_attr_t.
Definition pair.c:284
void fr_pair_list_init(fr_pair_list_t *list)
Initialise a pair list header.
Definition pair.c:47
int fr_pair_delete_by_child_num(fr_pair_list_t *list, fr_dict_attr_t const *parent, unsigned int attr)
Delete matching pairs from the specified list.
Definition pair.c:1760
int fr_pair_value_copy(fr_pair_t *dst, fr_pair_t *src)
Copy the value from one pair to another.
Definition pair.c:2515
int fr_pair_reinit_from_da(fr_pair_list_t *list, fr_pair_t *vp, fr_dict_attr_t const *da)
Re-initialise an attribute with a different da.
Definition pair.c:321
fr_pair_t * fr_pair_copy(TALLOC_CTX *ctx, fr_pair_t const *vp)
Copy a single valuepair.
Definition pair.c:500
void fr_pair_list_move_op(fr_pair_list_t *to, fr_pair_list_t *from, fr_token_t op)
Move pairs from source list to destination list respecting operator.
int paircmp_pairs(UNUSED request_t *request, fr_pair_t const *check, fr_pair_t *vp)
Compares check and vp by value.
Definition paircmp.c:53
#define fr_assert(_expr)
Definition rad_assert.h:37
#define REDEBUG(fmt,...)
#define RDEBUG2(fmt,...)
#define RDEBUG_ENABLED()
static bool done
Definition radclient.c:80
static const char * spaces
Definition radict.c:185
fr_dict_attr_t const * request_attr_request
Definition request.c:43
fr_dict_attr_t const * request_attr_control
Definition request.c:45
bool fr_sbuff_next_if_char(fr_sbuff_t *sbuff, char c)
Return true if the current char matches, and if it does, advance.
Definition sbuff.c:2247
#define fr_sbuff_out_by_longest_prefix(_match_len, _out, _table, _sbuff, _def)
#define fr_sbuff_adv_past_str_literal(_sbuff, _needle)
#define FR_SBUFF_IN_CHAR_RETURN(_sbuff,...)
#define fr_sbuff_set(_dst, _src)
#define FR_SBUFF_IN(_start, _len_or_end)
#define fr_sbuff_adv_past_whitespace(_sbuff, _len, _tt)
#define FR_SBUFF_TERMS(...)
Initialise a terminal structure with a list of sorted strings.
Definition sbuff.h:191
#define FR_SBUFF_RETURN(_func, _sbuff,...)
#define fr_sbuff_eof(_x)
#define FR_SBUFF_ERROR_RETURN(_sbuff_or_marker)
#define FR_SBUFF_SET_RETURN(_dst, _src)
#define FR_SBUFF_IN_SPRINTF_RETURN(...)
#define FR_SBUFF_IN_STR(_start)
#define FR_SBUFF(_sbuff_or_marker)
#define FR_SBUFF_OUT(_start, _len_or_end)
#define FR_SBUFF_IN_STRCPY_RETURN(...)
Set of terminal elements.
Set of parsing rules for *unescape_until functions.
#define MAP_VERIFY(_x)
Definition map.h:108
int(* radius_map_getvalue_t)(TALLOC_CTX *ctx, fr_pair_list_t *out, request_t *request, map_t const *map, void *uctx)
Definition map.h:117
int(* map_validate_t)(map_t *map, void *ctx)
Definition map.h:116
static int16_t tmpl_attr_tail_num(tmpl_t const *vpt)
Return the last attribute reference's attribute number.
Definition tmpl.h:910
static char const * tmpl_type_to_str(tmpl_type_t type)
Return a static string containing the type name.
Definition tmpl.h:661
#define tmpl_is_xlat(vpt)
Definition tmpl.h:217
void tmpl_set_xlat(tmpl_t *vpt, xlat_exp_head_t *xlat)
Change the default dictionary in the tmpl's resolution rules.
int tmpl_resolve(tmpl_t *vpt, tmpl_res_rules_t const *tr_rules))
Attempt to resolve functions and attributes in xlats and attribute references.
#define tmpl_value(_tmpl)
Definition tmpl.h:962
TALLOC_CTX * list_ctx
Where to allocate new attributes if building out from the current extents of the tree.
Definition tmpl.h:637
int tmpl_afrom_value_box(TALLOC_CTX *ctx, tmpl_t **out, fr_value_box_t *data, bool steal)
Create a tmpl_t from a fr_value_box_t.
tmpl_t * tmpl_alloc(TALLOC_CTX *ctx, tmpl_type_t type, fr_token_t quote, char const *name, ssize_t len)
Create a new heap allocated tmpl_t.
int tmpl_attr_unknown_add(tmpl_t *vpt)
Add an unknown fr_dict_attr_t specified by a tmpl_t to the main dictionary.
static bool tmpl_attr_tail_is_unknown(tmpl_t const *vpt)
Return true if the last attribute reference is "unknown".
Definition tmpl.h:767
#define tmpl_contains_regex(vpt)
Definition tmpl.h:233
int tmpl_extents_build_to_leaf_parent(fr_dlist_head_t *leaf, fr_dlist_head_t *interior, tmpl_t const *vpt)
Allocate interior pairs.
#define tmpl_is_attr(vpt)
Definition tmpl.h:215
#define tmpl_is_exec(vpt)
Definition tmpl.h:218
fr_dict_attr_t const * enumv
Enumeration attribute used to resolve enum values.
Definition tmpl.h:349
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_xlat(_tmpl)
Definition tmpl.h:955
static fr_dict_attr_t const * tmpl_list(tmpl_t const *vpt)
Definition tmpl.h:929
@ TMPL_ATTR_LIST_REQUIRE
Attribute refs are required to have a list.
Definition tmpl.h:271
@ TMPL_ATTR_LIST_FORBID
Attribute refs are forbidden from having a list.
Definition tmpl.h:270
int tmpl_extents_find(TALLOC_CTX *ctx, fr_dlist_head_t *leaf, fr_dlist_head_t *interior, request_t *request, tmpl_t const *vpt))
Determines points where the reference list extends beyond the current pair tree.
#define tmpl_rules_cast(_tmpl)
Definition tmpl.h:967
static bool tmpl_attr_tail_da_is_leaf(tmpl_t const *vpt)
Return true if the last attribute reference is a leaf attribute.
Definition tmpl.h:842
#define NUM_COUNT
Definition tmpl.h:419
#define tmpl_contains_attr(vpt)
Definition tmpl.h:232
static bool tmpl_attr_tail_da_is_structural(tmpl_t const *vpt)
Return true if the last attribute reference is a structural attribute.
Definition tmpl.h:860
int tmpl_copy_pairs(TALLOC_CTX *ctx, fr_pair_list_t *out, request_t *request, tmpl_t const *vpt))
Copy pairs matching a tmpl_t in the current request_t.
Definition tmpl_eval.c:686
fr_pair_list_t * tmpl_list_head(request_t *request, fr_dict_attr_t const *list)
Resolve attribute fr_pair_list_t value to an attribute list.
Definition tmpl_eval.c:70
TALLOC_CTX * tmpl_list_ctx(request_t *request, fr_dict_attr_t const *list)
Return the correct TALLOC_CTX to alloc fr_pair_t in, for a list.
Definition tmpl_eval.c:110
ssize_t tmpl_afrom_substr(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in, fr_token_t quote, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules))
Convert an arbitrary string into a tmpl_t.
fr_slen_t tmpl_afrom_attr_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err, tmpl_t **out, fr_sbuff_t *name, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules))
Parse a string into a TMPL_TYPE_ATTR_* type tmpl_t.
static fr_slen_t e_rules fr_slen_t tmpl_print_quoted(fr_sbuff_t *out, tmpl_t const *vpt)
Print a tmpl_t to a string with quotes.
tmpl_xlat_rules_t xlat
Rules/data for parsing xlats.
Definition tmpl.h:347
bool at_runtime
Produce an ephemeral/runtime tmpl.
Definition tmpl.h:357
static bool tmpl_is_list(tmpl_t const *vpt)
Definition tmpl.h:945
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...
#define tmpl_is_data(vpt)
Definition tmpl.h:213
fr_dict_t const * dict_def
Alternative default dictionary to use if vpt->rules->dict_def is NULL.
Definition tmpl.h:392
#define tmpl_rules_copy_depth(_out, _in)
Copy parse rules for a nested parse, and increment the depth field.
Definition tmpl.h:373
#define NUM_UNSPEC
Definition tmpl.h:417
int tmpl_attr_rebase(tmpl_t *vpt, tmpl_t const *parent)
Rewrite an attribute reference, filling in missing references.
#define tmpl_value_type(_tmpl)
Definition tmpl.h:964
int tmpl_request_ptr(request_t **request, FR_DLIST_HEAD(tmpl_request_list) const *rql)
Resolve a tmpl_request_ref_t to a request_t.
Definition tmpl_eval.c:163
#define tmpl_is_data_unresolved(vpt)
Definition tmpl.h:224
tmpl_attr_rules_t attr
Rules/data for parsing attribute references.
Definition tmpl.h:346
bool tmpl_rules_depth_exceeded(tmpl_rules_t const *t_rules)
True when a parse has nested past the point where the stack is safe.
@ TMPL_TYPE_ATTR
Reference to one or more attributes.
Definition tmpl.h:149
@ TMPL_TYPE_XLAT
Pre-parsed xlat expansion.
Definition tmpl.h:153
@ TMPL_TYPE_EXEC
Callout to an external script or program.
Definition tmpl.h:157
@ TMPL_TYPE_DATA
Value in native boxed format.
Definition tmpl.h:145
@ TMPL_TYPE_DATA_UNRESOLVED
Unparsed literal string.
Definition tmpl.h:186
fr_pair_list_t * list
List that we tried to evaluate ar in and failed.
Definition tmpl.h:639
static fr_dict_attr_t const * tmpl_attr_tail_da(tmpl_t const *vpt)
Return the last attribute reference da.
Definition tmpl.h:826
fr_slen_t tmpl_afrom_attr_str(TALLOC_CTX *ctx, tmpl_attr_error_t *err, tmpl_t **out, char const *name, tmpl_rules_t const *rules))
Parse a string into a TMPL_TYPE_ATTR_* type tmpl_t.
struct tmpl_res_rules_s tmpl_res_rules_t
Definition tmpl.h:244
static char const * tmpl_list_name(fr_dict_attr_t const *list, char const *def)
Return the name of a tmpl list or def if list not provided.
Definition tmpl.h:940
#define tmpl_aexpand(_ctx, _out, _request, _vpt, _escape, _escape_ctx)
Expand a tmpl to a C type, allocing a new buffer to hold the string.
Definition tmpl.h:1089
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.
fr_event_list_t * runtime_el
The eventlist to use for runtime instantiation of xlats.
Definition tmpl.h:335
#define tmpl_needs_resolving(vpt)
Definition tmpl.h:230
int tmpl_cast_set(tmpl_t *vpt, fr_type_t type)
Set a cast for a tmpl.
Describes the current extents of a pair tree in relation to the tree described by a tmpl_t.
Definition tmpl.h:628
Similar to tmpl_rules_t, but used to specify parameters that may change during subsequent resolution ...
Definition tmpl.h:391
Optional arguments passed to vp_tmpl functions.
Definition tmpl.h:343
fr_aka_sim_id_type_t type
fr_pair_t * vp
Value pair map.
Definition map.h:77
fr_token_t op
The operator that controls insertion of the dst attribute.
Definition map.h:82
tmpl_t * lhs
Typically describes the attribute to add, modify or compare.
Definition map.h:78
map_list_t child
parent map, for nested ones
Definition map.h:89
map_t * parent
Definition map.h:88
tmpl_t * rhs
Typically describes a literal value or a src attribute to copy or compare.
Definition map.h:79
CONF_ITEM * ci
Config item that the map was created from.
Definition map.h:85
tmpl_attr_list_presence_t list_presence
Whether the attribute reference can have a list, forbid it, or require it.
Definition tmpl.h:305
fr_dict_attr_t const * list_def
Default list to use with unqualified attribute reference.
Definition tmpl.h:302
unsigned int disallow_rhs_resolve
map RHS is NOT immediately resolved in the context of the LHS.
Definition tmpl.h:331
fr_dict_t const * dict_def
Default dictionary to use with unqualified attribute references.
Definition tmpl.h:280
unsigned int bare_word_enum
for v3 compatibility.
Definition tmpl.h:329
Stores an attribute, a value and various bits of other data.
Definition pair.h:68
fr_dict_attr_t const *_CONST da
Dictionary attribute defines the attribute number, vendor and type of the pair.
Definition pair.h:69
#define fr_table_value_by_str(_table, _name, _def)
Convert a string to a value using a sorted or ordered table.
Definition table.h:685
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition table.h:804
An element in a lexicographically sorted array of name to num mappings.
Definition table.h:49
char * talloc_typed_asprintf(TALLOC_CTX *ctx, char const *fmt,...)
Call talloc vasprintf, setting the type on the new chunk correctly.
Definition talloc.c:621
static size_t talloc_strlen(char const *s)
Returns the length of a talloc array containing a string.
Definition talloc.h:143
static fr_time_delta_t fr_time_delta_from_sec(int64_t sec)
Definition time.h:599
void tmpl_dcursor_clear(tmpl_dcursor_ctx_t *cc)
Clear any temporary state allocations.
#define tmpl_dcursor_init(_err, _ctx, _cc, _cursor, _request, _vpt)
Maintains state between cursor calls.
const bool fr_assignment_op[T_TOKEN_LAST]
Definition token.c:234
char const * fr_token_name(int token)
Definition token.c:579
fr_table_num_ordered_t const fr_tokens_table[]
Definition token.c:33
const char fr_token_quote[T_TOKEN_LAST]
Convert tokens back to a quoting character.
Definition token.c:222
char const * fr_tokens[T_TOKEN_LAST]
Definition token.c:146
const bool fr_comparison_op[T_TOKEN_LAST]
Definition token.c:264
@ T_OP_SUB_EQ
Definition token.h:68
@ T_INVALID
Definition token.h:37
@ T_SINGLE_QUOTED_STRING
Definition token.h:120
@ T_OP_CMP_TRUE
Definition token.h:102
@ T_BARE_WORD
Definition token.h:118
@ T_OP_EQ
Definition token.h:81
@ T_BACK_QUOTED_STRING
Definition token.h:121
@ T_HASH
Definition token.h:117
@ T_OP_SET
Definition token.h:82
@ T_OP_NE
Definition token.h:95
@ T_OP_ADD_EQ
Definition token.h:67
@ T_OP_CMP_FALSE
Definition token.h:103
@ T_OP_LSHIFT_EQ
Definition token.h:75
@ T_OP_RSHIFT_EQ
Definition token.h:74
@ T_OP_REG_EQ
Definition token.h:100
@ T_DOUBLE_QUOTED_STRING
Definition token.h:119
@ T_OP_CMP_EQ
Definition token.h:104
@ T_OP_LE
Definition token.h:98
@ T_OP_GE
Definition token.h:96
@ T_OP_GT
Definition token.h:97
@ T_SOLIDUS_QUOTED_STRING
Definition token.h:122
@ T_OP_LT
Definition token.h:99
@ T_OP_REG_NE
Definition token.h:101
@ T_OP_PREPEND
Definition token.h:83
enum fr_token fr_token_t
#define T_TOKEN_LAST
Definition token.h:127
static fr_slen_t head
Definition xlat.h:410
ssize_t xlat_aeval_compiled(TALLOC_CTX *ctx, char **out, request_t *request, xlat_exp_head_t const *head, xlat_escape_legacy_t escape, void const *escape_ctx))
Definition xlat_eval.c:1925
fr_slen_t xlat_tokenize_expression(TALLOC_CTX *ctx, xlat_exp_head_t **head, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules))
Definition xlat_expr.c:3227
bool fr_pair_list_empty(fr_pair_list_t const *list)
Is a valuepair list empty.
#define PAIR_VERIFY(_x)
Definition pair.h:209
fr_pair_t * fr_pair_list_tail(fr_pair_list_t const *list)
Get the tail of a valuepair list.
Definition pair_inline.c:55
#define fr_pair_list_foreach(_list_head, _iter)
Iterate over the contents of a fr_pair_list_t.
Definition pair.h:285
void fr_pair_list_free(fr_pair_list_t *list)
Free memory used by a valuepair list.
void fr_pair_list_append(fr_pair_list_t *dst, fr_pair_list_t *src)
Appends a list of fr_pair_t from a temporary list to a destination list.
void fr_pair_list_prepend(fr_pair_list_t *dst, fr_pair_list_t *src)
Move a list of fr_pair_t from a temporary list to the head of a destination list.
int fr_pair_list_sort(fr_pair_list_t *list, fr_cmp_t cmp)
Sort a doubly linked list of fr_pair_ts using merge sort.
static fr_slen_t fr_pair_aprint_value_quoted(TALLOC_CTX *ctx, char **out, fr_pair_t const *vp, fr_token_t quote) 1(fr_pair_print_value_quoted
#define fr_pair_dcursor_init(_cursor, _list)
Initialises a special dcursor with callbacks that will maintain the attr sublists correctly.
Definition pair.h:605
fr_pair_t * fr_pair_list_head(fr_pair_list_t const *list)
Get the head of a valuepair list.
Definition pair_inline.c:42
static fr_slen_t parent
Definition pair.h:864
char const * fr_strerror(void)
Get the last library error.
Definition strerror.c:558
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
#define fr_strerror_const(_msg)
Definition strerror.h:223
#define FR_TYPE_QUOTED
Definition types.h:312
#define fr_type_is_structural(_x)
Definition types.h:392
#define FR_TYPE_STRUCTURAL
Definition types.h:316
#define fr_type_is_leaf(_x)
Definition types.h:393
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
Definition types.h:454
fr_slen_t fr_value_box_from_str(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, char const *in, size_t inlen, fr_sbuff_unescape_rules_t const *erules)
Definition value.c:6256
fr_sbuff_unescape_rules_t const fr_value_unescape_single
Definition value.c:301
fr_sbuff_parse_rules_t const value_parse_rules_solidus_quoted
Definition value.c:573
int fr_value_box_cast(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, fr_value_box_t const *src)
Convert one type of fr_value_box_t to another.
Definition value.c:4071
fr_sbuff_parse_rules_t const * value_parse_rules_quoted[T_TOKEN_LAST]
Parse rules for quoted strings.
Definition value.c:620
int fr_value_box_copy(TALLOC_CTX *ctx, fr_value_box_t *dst, const fr_value_box_t *src)
Copy value data verbatim duplicating any buffers.
Definition value.c:4526
int fr_value_box_cmp_op(fr_token_t op, fr_value_box_t const *a, fr_value_box_t const *b)
Compare two attributes using an operator.
Definition value.c:1017
fr_sbuff_parse_rules_t const value_parse_rules_single_quoted
Definition value.c:567
fr_slen_t fr_value_box_from_substr(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *rules)
Convert string value to a fr_value_box_t type.
Definition value.c:5584
int fr_value_box_strdup(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, bool tainted)
Copy a nul terminated string to a fr_value_box_t.
Definition value.c:4763
fr_sbuff_parse_rules_t const value_parse_rules_bareword_quoted
Definition value.c:538
fr_sbuff_parse_rules_t const value_parse_rules_backtick_quoted
Definition value.c:579
fr_sbuff_parse_rules_t const * value_parse_rules_unquoted[T_TOKEN_LAST]
Parse rules for non-quoted strings.
Definition value.c:522
fr_sbuff_parse_rules_t const value_parse_rules_double_quoted
Definition value.c:561
#define fr_value_box_alloc(_ctx, _type, _enumv)
Allocate a value box of a specific type.
Definition value.h:668
static fr_sbuff_err_t char size_t fr_sbuff_t * in
Definition value.h:1061
static fr_sbuff_err_t char size_t fr_sbuff_t size_t max
Definition value.h:1061
static fr_sbuff_err_t char ** out
Definition value.h:1061
static fr_sbuff_err_t char size_t * len
Definition value.h:1061
#define fr_box_strvalue(_val)
Definition value.h:339