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