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