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