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