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