The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
map_async.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: e40ffd2e362f803aec9d548d0d3691698be8791c $
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: e40ffd2e362f803aec9d548d0d3691698be8791c $")
30
31#include <freeradius-devel/server/exec.h>
32#include <freeradius-devel/server/map.h>
33#include <freeradius-devel/server/paircmp.h>
34#include <freeradius-devel/server/tmpl_dcursor.h>
35
36#include <freeradius-devel/util/debug.h>
37#include <freeradius-devel/util/misc.h>
38#include <freeradius-devel/util/pair_legacy.h>
39
40#include <freeradius-devel/protocol/radius/rfc2865.h>
41#include <freeradius-devel/protocol/freeradius/freeradius.internal.h>
42
43#include <ctype.h>
44
45static inline vp_list_mod_t *list_mod_alloc(TALLOC_CTX *ctx)
46{
47 vp_list_mod_t *mod;
48 mod = talloc_zero(ctx, vp_list_mod_t);
49 map_list_init(&mod->mod);
50 return mod;
51}
52
53static inline map_t *map_alloc(TALLOC_CTX *ctx)
54{
55 map_t *map;
56 map = talloc_zero(ctx, map_t);
57 map_list_init(&map->child);
58 return map;
59}
60
61/** Allocate a 'generic' #vp_list_mod_t
62 *
63 * This covers most cases, where we need to allocate a #vp_list_mod_t with a single
64 * modification map, with an attribute ref LHS, and a boxed value RHS.
65 *
66 * @param[in] ctx to allocate #vp_list_mod_t in.
67 * @param[in] original The map from the update section.
68 * @param[in] mutated The original map but with a altered dst (LHS).
69 * If the LHS of the original map was not expanded, this should be
70 * the same as original.
71 * @return
72 * - A new vlm structure on success.
73 * - NULL on failure.
74 */
75static inline vp_list_mod_t *list_mod_generic_afrom_map(TALLOC_CTX *ctx,
76 map_t const *original, map_t const *mutated)
77{
79 map_t *mod;
80
81 n = list_mod_alloc(ctx);
82 if (!n) return NULL;
83
84 n->map = original;
85
86 mod = map_alloc(n);
87 if (!mod) return NULL;
88 mod->lhs = mutated->lhs;
89 mod->op = mutated->op;
90 mod->rhs = tmpl_alloc(mod, TMPL_TYPE_DATA, T_BARE_WORD, NULL, 0);
91 if (!mod->rhs) {
93 return NULL;
94 }
95 map_list_insert_tail(&n->mod, mod);
96
97 return n;
98}
99
100/** Allocate a 'delete' #vp_list_mod_t
101 *
102 * This will cause the dst (LHS) to be deleted when applied. This is intended to be
103 * used where the RHS expansion is NULL, and we're doing a := assignment, so need to
104 * delete the LHS.
105 *
106 * @param[in] ctx to allocate #vp_list_mod_t in.
107 * @param[in] original The map from the update section.
108 * @param[in] mutated The original map but with a altered dst (LHS).
109 * If the LHS of the original map was not expanded, this should be
110 * the same as original.
111 *
112 * @return
113 * - A new vlm structure on success.
114 * - NULL on failure.
115 */
116static inline vp_list_mod_t *list_mod_delete_afrom_map(TALLOC_CTX *ctx,
117 map_t const *original, map_t const *mutated)
118{
120 map_t *mod;
121
122 n = list_mod_alloc(ctx);
123 if (!n) return NULL;
124
125 n->map = original;
126
127 mod = map_alloc(n);
128 if (!mod) return NULL;
129
130 mod->lhs = mutated->lhs;
131 mod->op = T_OP_CMP_FALSE; /* Means delete the LHS */
132 mod->rhs = tmpl_alloc(mod, TMPL_TYPE_NULL, T_BARE_WORD, NULL, 0);
133 if (!mod->rhs) {
134 talloc_free(n);
135 return NULL;
136 }
137 map_list_insert_tail(&n->mod, mod);
138
139 return n;
140}
141
142/** Allocate an 'empty_string' #vp_list_mod_t
143 *
144 * This shallow copies the mutated map, but sets the RHS to be an empty string.
145 *
146 * @param[in] ctx to allocate #vp_list_mod_t in.
147 * @param[in] original The map from the update section.
148 * @param[in] mutated The original map but with a altered dst (LHS).
149 * If the LHS of the original map was not expanded, this should be
150 * the same as original.
151 *
152 * @return
153 * - A new vlm structure on success.
154 * - NULL on failure.
155 */
156static inline vp_list_mod_t *list_mod_empty_string_afrom_map(TALLOC_CTX *ctx,
157 map_t const *original, map_t const *mutated)
158{
160 map_t *mod;
161 fr_value_box_t empty_string = *fr_box_strvalue_len("", 0);
162
163 n = list_mod_alloc(ctx);
164 if (!n) return NULL;
165
166 n->map = original;
167
168 mod = map_alloc(n);
169 if (!mod) return NULL;
170
171 mod->lhs = mutated->lhs;
172 mod->op = mutated->op;
173 mod->rhs = tmpl_alloc(mod, TMPL_TYPE_DATA, T_DOUBLE_QUOTED_STRING, NULL, -1);
174 if (!mod->rhs) {
175 talloc_free(n);
176 return NULL;
177 }
178
179 /*
180 * For consistent behaviour we don't try and guess
181 * what value we should assign, we try and cast a
182 * zero length string to the specified type and
183 * see what happens...
184 */
185 if (fr_value_box_cast(mod->rhs, tmpl_value(mod->rhs),
186 mutated->cast ? mutated->cast : tmpl_attr_tail_da(mutated->lhs)->type,
187 tmpl_attr_tail_da(mutated->lhs), &empty_string) < 0) {
188 talloc_free(n);
189 return NULL;
190 }
191 map_list_insert_tail(&n->mod, mod);
192
193 return n;
194}
195
196/** Check that the destination list is currently value
197 *
198 * @param[in] request to resolve in the list in.
199 * @param[in] map to check
200 * @param[in] src_dst a lhs or rhs tmpl to check.
201 * @return
202 * - destination list if list is valid.
203 * - NULL if destination list is invalid.
204 */
205static inline fr_pair_list_t *map_check_src_or_dst(request_t *request, map_t const *map, tmpl_t const *src_dst)
206{
207 request_t *context = request;
208 fr_pair_list_t *list;
209 fr_dict_attr_t const *list_ref;
210
211 if (tmpl_request_ptr(&context, tmpl_request(src_dst)) < 0) {
212 RPEDEBUG("Mapping \"%.*s\" -> \"%.*s\" cannot be performed",
213 (int)map->rhs->len, map->rhs->name, (int)map->lhs->len, map->lhs->name);
214 return NULL;
215 }
216
217 list_ref = tmpl_list(src_dst);
218 list = tmpl_list_head(context, list_ref);
219 if (!list) {
220 REDEBUG("Mapping \"%.*s\" -> \"%.*s\" cannot be performed due to to invalid list qualifier \"%s\"",
221 (int)map->rhs->len, map->rhs->name, (int)map->lhs->len, map->lhs->name,
222 tmpl_list_name(list_ref, "<INVALID>"));
223 return NULL;
224 }
225
226 return list;
227}
228
229/** Evaluate a map creating a new map with #TMPL_TYPE_ATTR LHS and #TMPL_TYPE_DATA RHS
230 *
231 * This function creates maps for consumption by map_to_request.
232 *
233 * @param[in,out] ctx to allocate modification maps in.
234 * @param[out] out Where to write the #fr_pair_t (s), which may be NULL if not found
235 * @param[in] request The current request.
236 * @param[in] original the map. The LHS (dst) has to be #TMPL_TYPE_ATTR.
237 * @param[in] lhs_result of previous stack based rhs evaluation.
238 * Must be provided for rhs types:
239 * - TMPL_TYPE_XLAT
240 * - TMPL_TYPE_EXEC (in future)
241 * @param[in] rhs_result of previous stack based rhs evaluation.
242 * Must be provided for rhs types:
243 * - TMPL_TYPE_XLAT
244 * - TMPL_TYPE_EXEC (in future)
245 * Once this function returns result will be invalidated even
246 * if this function errors.
247 * @return
248 * - 0 on success.
249 * - -1 on failure.
250 */
251int map_to_list_mod(TALLOC_CTX *ctx, vp_list_mod_t **out,
252 request_t *request, map_t const *original,
253 fr_value_box_list_t *lhs_result, fr_value_box_list_t *rhs_result)
254{
255 vp_list_mod_t *n = NULL;
256 map_t map_tmp;
257 map_t const *mutated = original;
258
259 fr_dcursor_t values;
260 fr_value_box_list_t head;
261 TALLOC_CTX *tmp_ctx = NULL;
262
263 MAP_VERIFY(original);
264
265 if (!fr_cond_assert(original->lhs != NULL)) return -1;
266 if (!fr_cond_assert(original->rhs != NULL)) return -1;
267
268 fr_assert(tmpl_is_attr(original->lhs) ||
269 tmpl_is_xlat(original->lhs));
270
271 *out = NULL;
272 fr_value_box_list_init(&head);
273
274 /*
275 * Preprocessing of the LHS of the map.
276 */
277 switch (original->lhs->type) {
278 /*
279 * Already in the correct form.
280 */
281 case TMPL_TYPE_ATTR:
282 break;
283
284 /*
285 * Everything else gets expanded, then re-parsed as an attribute reference.
286 *
287 * This allows the syntax like:
288 * - "Attr-%{number}" := "value"
289 */
290 case TMPL_TYPE_EXEC:
291 case TMPL_TYPE_XLAT:
292 {
293 ssize_t slen;
294 fr_value_box_t *lhs_result_head = fr_value_box_list_head(lhs_result);
295
296 /*
297 * Get our own mutable copy of the original so we can
298 * dynamically expand the LHS.
299 */
300 memcpy(&map_tmp, original, sizeof(map_tmp));
301 mutated = &map_tmp;
302
303 tmp_ctx = talloc_new(NULL);
304
305 fr_assert(!fr_value_box_list_empty(lhs_result));
306
307 /*
308 * This should always be a noop, but included
309 * here for robustness.
310 */
311 if (fr_value_box_list_concat_in_place(lhs_result_head,
312 lhs_result_head, lhs_result, FR_TYPE_STRING,
314 SIZE_MAX) < 0) {
315 RPEDEBUG("Left side expansion failed");
316 fr_value_box_list_talloc_free(lhs_result);
317 goto error;
318 }
319
320 slen = tmpl_afrom_attr_str(tmp_ctx, NULL, &map_tmp.lhs, lhs_result_head->vb_strvalue,
321 &(tmpl_rules_t){
322 .attr = {
323 .dict_def = request->dict,
324 .list_def = request_attr_request,
325 }
326 });
327 if (slen <= 0) {
328 RPEDEBUG("Left side expansion result \"%s\" is not an attribute reference",
329 lhs_result_head->vb_strvalue);
330 fr_value_box_list_talloc_free(lhs_result);
331 goto error;
332 }
333 fr_assert(tmpl_is_attr(mutated->lhs));
334 }
335 break;
336
337 default:
338 fr_assert(0);
339 break;
340 }
341
342 /*
343 * Special case for !*, we don't need to parse RHS as this is a unary operator.
344 */
345 if (mutated->op == T_OP_CMP_FALSE) {
346 map_t *mod;
347 n = list_mod_alloc(ctx);
348 if (!n) goto error;
349
350 n->map = original;
351 mod = map_alloc(n); /* Need to duplicate input map, so next pointer is NULL */
352 mod->lhs = mutated->lhs;
353 mod->op = mutated->op;
354 mod->rhs = mutated->rhs;
355 map_list_insert_tail(&n->mod, mod);
356 goto finish;
357 }
358
359 /*
360 * List to list copy.
361 */
362 if (tmpl_is_attr(mutated->lhs) && tmpl_is_attr(mutated->rhs) &&
364 fr_pair_list_t *list = NULL;
365 fr_pair_t *vp = NULL;
366
367 /*
368 * Check source list
369 */
370 list = map_check_src_or_dst(request, mutated, mutated->rhs);
371 if (!list) goto error;
372
373 vp = fr_pair_list_head(list);
374 /*
375 * No attributes found on LHS.
376 */
377 if (!vp) {
378 /*
379 * Special case for := if RHS was NULL.
380 * Should delete all LHS attributes.
381 */
382 if (mutated->op == T_OP_SET) n = list_mod_delete_afrom_map(ctx, original, mutated);
383 goto finish;
384 }
385
386 n = list_mod_alloc(ctx);
387 n->map = original;
388
389 /*
390 * Iterate over all attributes in that list
391 */
392 do {
393 map_t *n_mod;
394
395 n_mod = map_alloc(n);
396 if (!n_mod) goto error;
397
398 n_mod->op = mutated->op;
399
400 /*
401 * For the LHS we need to create a reference to
402 * the attribute, with the same destination list
403 * as the current LHS map.
404 */
405 n_mod->lhs = tmpl_alloc(n, TMPL_TYPE_ATTR, T_BARE_WORD, mutated->lhs->name, mutated->lhs->len);
406 if (!n_mod->lhs) goto error;
407
408 if (tmpl_attr_copy(n_mod->lhs, mutated->lhs) < 0) goto error;
409
410 tmpl_attr_set_leaf_da(n_mod->lhs, vp->da);
411
412 /*
413 * For the RHS we copy the value of the attribute
414 * we just found, creating data (literal) tmpl.
415 */
416 n_mod->rhs = tmpl_alloc(n_mod, TMPL_TYPE_DATA,
418 NULL, 0);
419 if (!n_mod->rhs) goto error;
420
421 /*
422 * Have to do a full copy, as the attribute we're
423 * getting the buffer value from may be freed
424 * before this map is applied.
425 */
426 if (fr_value_box_copy(n_mod->rhs, tmpl_value(n_mod->rhs), &vp->data) < 0) goto error;
427 map_list_insert_tail(&n->mod, n_mod);
428
429 MAP_VERIFY(n_mod);
430 } while ((vp = fr_pair_list_next(list, vp)));
431
432 goto finish;
433 }
434
435 /*
436 * Unparsed. These are easy because they
437 * can only have a single value.
438 */
439 if (tmpl_is_data_unresolved(mutated->rhs)) {
440 fr_type_t type = tmpl_attr_tail_da(mutated->lhs)->type;
441
442 fr_assert(tmpl_is_attr(mutated->lhs));
443 fr_assert(tmpl_attr_tail_da(mutated->lhs)); /* We need to know which attribute to create */
444
445 n = list_mod_generic_afrom_map(ctx, original, mutated);
446 if (!n) goto error;
447
448 fr_dcursor_init(&values, fr_value_box_list_dlist_head(&head));
449
450 if (fr_value_box_from_str(map_list_head(&n->mod),
451 tmpl_value(map_list_head(&n->mod)->rhs), type,
452 tmpl_attr_tail_da(mutated->lhs),
453 mutated->rhs->name, mutated->rhs->len,
454 fr_value_unescape_by_quote[(uint8_t)mutated->rhs->quote])) {
455 RPEDEBUG("Assigning value to \"%s\" failed", tmpl_attr_tail_da(mutated->lhs)->name);
456 goto error;
457 }
458 goto finish;
459 }
460
461 /*
462 * Check destination list
463 */
464 if (!map_check_src_or_dst(request, mutated, mutated->lhs)) goto error;
465
466 (void)fr_dcursor_init(&values, fr_value_box_list_dlist_head(&head));
467
468 switch (mutated->rhs->type) {
469 case TMPL_TYPE_XLAT:
470 {
471 fr_dcursor_t from;
472 fr_value_box_t *vb, *n_vb;
473
474 fr_assert(tmpl_xlat(mutated->rhs) != NULL);
475
476 assign_values:
477 fr_assert(tmpl_is_attr(mutated->lhs));
478 fr_assert(tmpl_attr_tail_da(mutated->lhs)); /* We need to know which attribute to create */
479
480 /*
481 * Empty value - Try and cast an empty string
482 * to the destination type, and see what
483 * happens. This is only for XLATs and in future
484 * EXECs.
485 */
486 if (fr_value_box_list_empty(rhs_result)) {
487 n = list_mod_empty_string_afrom_map(ctx, original, mutated);
488 if (!n) {
489 RPEDEBUG("Assigning value to \"%s\" failed", tmpl_attr_tail_da(mutated->lhs)->name);
490 xlat_error:
491 fr_dcursor_head(&values);
492 fr_dcursor_free_list(&values);
493 goto error;
494 }
495 goto finish;
496 }
497
498 /*
499 * Non-Empty value
500 */
501 n = list_mod_generic_afrom_map(ctx, original, mutated);
502 if (!n) goto error;
503
504 (void)fr_dcursor_init(&from, fr_value_box_list_dlist_head(rhs_result));
505 while ((vb = fr_dcursor_remove(&from))) {
506 if (vb->type != tmpl_attr_tail_da(mutated->lhs)->type) {
507 n_vb = fr_value_box_alloc_null(map_list_head(&n->mod)->rhs);
508 if (!n_vb) {
509 fr_dcursor_head(&from);
511 goto xlat_error;
512 }
513
514 if (fr_value_box_cast(n_vb, n_vb,
515 mutated->cast ? mutated->cast : tmpl_attr_tail_da(mutated->lhs)->type,
516 tmpl_attr_tail_da(mutated->lhs), vb) < 0) {
517 RPEDEBUG("Assigning value to \"%s\" failed", tmpl_attr_tail_da(mutated->lhs)->name);
518
519 fr_dcursor_head(&from);
521 goto xlat_error;
522 }
523 talloc_free(vb);
524 } else {
525 n_vb = talloc_steal(n, vb); /* Should already be in ctx of n's parent */
526 }
527 fr_dcursor_append(&values, n_vb);
528 }
529 }
530 break;
531
532 case TMPL_TYPE_ATTR:
533 {
534 fr_dcursor_t from;
535 tmpl_dcursor_ctx_t cc_attr;
536 fr_pair_t *vp;
537 fr_value_box_t *n_vb;
538 int err;
539
540 fr_assert(fr_value_box_list_empty(rhs_result));
541 fr_assert(tmpl_is_attr(mutated->lhs) && tmpl_attr_tail_da(mutated->lhs));
542
543 /*
544 * Check source list
545 */
546 if (!map_check_src_or_dst(request, mutated, mutated->rhs)) goto error;
547
548 /*
549 * Check we have pairs to copy *before*
550 * doing any expensive allocations.
551 */
552 vp = tmpl_dcursor_init(&err, request, &cc_attr, &from, request, mutated->rhs);
553 if (!vp) switch (err) {
554 default:
555 break;
556
557 case -1: /* No input pairs */
558 RDEBUG3("No matching pairs found for \"%s\"", tmpl_attr_tail_da(mutated->rhs)->name);
559 /*
560 * Special case for := if RHS had no attributes
561 * we should delete all LHS attributes.
562 */
563 if (mutated->op == T_OP_SET) n = list_mod_delete_afrom_map(ctx, original, mutated);
564 tmpl_dcursor_clear(&cc_attr);
565 goto finish;
566
567 case -2: /* No matching list */
568 case -3: /* No request context */
569 case -4: /* memory allocation error */
570 RPEDEBUG("Failed resolving attribute source");
571 tmpl_dcursor_clear(&cc_attr);
572 goto error;
573 }
574
575 n = list_mod_generic_afrom_map(ctx, original, mutated);
576 if (!n) {
577 tmpl_dcursor_clear(&cc_attr);
578 goto error;
579 }
580
581 vp = fr_dcursor_current(&from);
582 fr_assert(vp); /* Should have errored out */
583 do {
584 n_vb = fr_value_box_alloc_null(map_list_head(&n->mod)->rhs);
585 if (!n_vb) {
586 attr_error:
587 fr_dcursor_head(&values);
588 fr_dcursor_free_list(&values);
589 tmpl_dcursor_clear(&cc_attr);
590 goto error;
591 }
592
593 if (vp->data.type != tmpl_attr_tail_da(mutated->lhs)->type) {
594 if (fr_value_box_cast(n_vb, n_vb,
595 mutated->cast ? mutated->cast : tmpl_attr_tail_da(mutated->lhs)->type,
596 tmpl_attr_tail_da(mutated->lhs), &vp->data) < 0) {
597 RPEDEBUG("Assigning value to \"%s\" failed", tmpl_attr_tail_da(mutated->lhs)->name);
598
599 goto attr_error;
600 }
601 } else {
602 fr_value_box_copy(n_vb, n_vb, &vp->data);
603 }
604 fr_dcursor_append(&values, n_vb);
605 } while ((vp = fr_dcursor_next(&from)));
606
607 tmpl_dcursor_clear(&cc_attr);
608 }
609 break;
610
611 case TMPL_TYPE_DATA:
612 {
613 fr_value_box_t *vb, *n_vb;
614
615 fr_assert(fr_value_box_list_empty(rhs_result));
616 fr_assert(tmpl_attr_tail_da(mutated->lhs));
617 fr_assert(tmpl_is_attr(mutated->lhs));
618
619 n = list_mod_generic_afrom_map(ctx, original, mutated);
620 if (!n) goto error;
621
622 vb = tmpl_value(mutated->rhs);
623
624 n_vb = fr_value_box_alloc_null(map_list_head(&n->mod)->rhs);
625 if (!n_vb) {
626 data_error:
627 fr_dcursor_head(&values);
628 fr_dcursor_free_list(&values);
629 goto error;
630 }
631 /*
632 * This should be optimised away by the map
633 * parser, but in case we're applying runtime
634 * maps we still need to check if we need to
635 * cast.
636 */
637 if (tmpl_attr_tail_da(mutated->lhs)->type != tmpl_value_type(mutated->rhs)) {
638 if (fr_value_box_cast(n_vb, n_vb,
639 mutated->cast ? mutated->cast : tmpl_attr_tail_da(mutated->lhs)->type,
640 tmpl_attr_tail_da(mutated->lhs), vb) < 0) {
641 RPEDEBUG("Assigning value to \"%s\" failed", tmpl_attr_tail_da(mutated->lhs)->name);
642 goto data_error;
643 }
644 } else {
645 /*
646 * We need to do a full copy, as shallow
647 * copy would increase the reference count
648 * on the static/global buffers and possibly
649 * lead to threading issues.
650 */
651 if (fr_value_box_copy(n_vb, n_vb, vb) < 0) goto data_error;
652 }
653 fr_dcursor_append(&values, n_vb);
654 }
655 break;
656
657 /*
658 * The result of an exec is a value if the LHS is an
659 * attribute, or a set of VPs, if the LHS is a list.
660 *
661 * @todo - we should just create maps from the RHS
662 * instead of VPs, and then converting them to maps.
663 */
664 case TMPL_TYPE_EXEC:
665 {
666 fr_dcursor_t from;
667 fr_pair_list_t vp_head;
668 fr_pair_t *vp;
669 fr_value_box_t *rhs_result_head = fr_value_box_list_head(rhs_result);
670
671 fr_pair_list_init(&vp_head);
672 /*
673 * If the LHS is an attribute, we just do the
674 * same thing as an xlat expansion.
675 */
676 if (tmpl_is_attr(mutated->lhs)) goto assign_values;
677
678 fr_assert(tmpl_is_list(mutated->lhs));
679
680 /*
681 * Empty value - Try and cast an empty string
682 * to the destination type, and see what
683 * happens. This is only for XLATs and in future
684 * EXECs.
685 */
686 if (fr_value_box_list_empty(rhs_result)) {
687 RPEDEBUG("Cannot assign empty value to \"%s\"", mutated->lhs->name);
688 goto error;
689 }
690
691 /*
692 * This should always be a noop, but included
693 * here for robustness.
694 */
695 if (fr_value_box_list_concat_in_place(rhs_result_head,
696 rhs_result_head, rhs_result, FR_TYPE_STRING,
698 SIZE_MAX) < 0) {
699 RPEDEBUG("Right side expansion failed");
700 fr_value_box_list_talloc_free(rhs_result);
701 goto error;
702 }
703
704 n = list_mod_alloc(ctx);
705 if (!n) goto error;
706
707 n->map = original;
708
709 /*
710 * Parse the VPs from the RHS.
711 */
712 fr_pair_list_afrom_box(ctx, &vp_head, request->dict, rhs_result_head);
713 if (fr_pair_list_empty(&vp_head)) {
714 talloc_free(n);
715 RDEBUG2("No pairs returned by exec");
716 return 0; /* No pairs returned */
717 }
718
719 (void)fr_pair_dcursor_init(&from, &vp_head);
720 while ((vp = fr_dcursor_remove(&from))) {
721 map_t *mod;
722 tmpl_rules_t rules = {
723 .attr = {
724 .request_def = tmpl_request(mutated->lhs),
725 .list_def = tmpl_list(mutated->lhs)
726 }
727 };
728
729 if (map_afrom_vp(n, &mod, vp, &rules) < 0) {
730 RPEDEBUG("Failed converting VP to map");
731 fr_dcursor_head(&from);
733 goto error;
734 }
735
736 if (tmpl_is_exec(mod->lhs) || tmpl_is_exec(mod->rhs)) {
737 RPEDEBUG("Program output cannot request execution of another program for attribute %s", vp->da->name);
738 fr_dcursor_head(&from);
740 goto error;
741 }
742
743
744 if ((vp->op == T_OP_REG_EQ) || (vp->op == T_OP_REG_NE)) {
745 RPEDEBUG("Program output cannot request regular expression matching for attribute %s", vp->da->name);
746 fr_dcursor_head(&from);
748 goto error;
749 }
750
751 mod->op = vp->op;
752 map_list_insert_tail(&n->mod, mod);
753 }
754
755 }
756 goto finish;
757
758 default:
759 fr_assert(0); /* Should have been caught at parse time */
760 goto error;
761 }
762
763 fr_assert(!fr_value_box_list_empty(&head) || !n);
764
765 /*
766 * FIXME: This is only required because
767 * tmpls allocate space for a value.
768 *
769 * If tmpl_value were a pointer we could
770 * assign values directly.
771 */
772 fr_value_box_copy(map_list_head(&n->mod)->rhs, tmpl_value(map_list_head(&n->mod)->rhs), fr_value_box_list_head(&head));
773 /*
774 * value boxes in tmpls cannot now be the head of a list
775 *
776 *tmpl_value(map_list_head(&n->mod)->rhs)->next = head->next;
777 */
778 fr_value_box_list_talloc_free(&head);
779
780finish:
781 if (n) {
782 MAP_VERIFY(n->map);
783 *out = n;
784 }
785
786 /*
787 * Reparent ephemeral LHS to the vp_list_mod_t.
788 */
789 if (tmp_ctx) {
790 if (talloc_parent(mutated->lhs) == tmp_ctx) talloc_steal(n, mutated->lhs);
791 talloc_free(tmp_ctx);
792 }
793 return 0;
794
795error:
796 talloc_free(tmp_ctx);
797 talloc_free(n); /* Frees all mod maps too */
798 return -1;
799}
800
801static inline fr_pair_t *map_list_mod_to_vp(TALLOC_CTX *ctx, tmpl_t const *attr, fr_value_box_t const *value)
802{
803 fr_pair_t *vp;
804
806 if (fr_value_box_copy(vp, &vp->data, value) < 0) {
808 return NULL;
809 }
810 PAIR_VERIFY(vp); /* Check we created something sane */
811
812 return vp;
813}
814
815/** Allocate one or more fr_pair_ts from a #vp_list_mod_t
816 *
817 */
818static void map_list_mod_to_vps(TALLOC_CTX *ctx, fr_pair_list_t *list, vp_list_mod_t const *vlm)
819{
820 map_t *mod;
821
822 fr_assert(!map_list_empty(&vlm->mod));
823
824 /*
825 * Fast path...
826 */
827 mod = map_list_head(&vlm->mod);
828 if (map_list_num_elements(&vlm->mod) == 1) {
829 fr_pair_t *vp;
830 vp = map_list_mod_to_vp(ctx, mod->lhs, tmpl_value(mod->rhs));
831 fr_pair_append(list, vp);
832 return;
833 }
834
835 /*
836 * Slow path. This may generate multiple attributes.
837 */
838 for (;
839 mod;
840 mod = map_list_next(&vlm->mod, mod)) {
841 fr_pair_t *vp;
842
843 vp = map_list_mod_to_vp(ctx, mod->lhs, tmpl_value(mod->rhs));
844 if (!vp) {
845 fr_pair_list_free(list);
846 return;
847 }
848 fr_pair_append(list, vp);
849 }
850}
851
852/** Print debug for a modification map
853 *
854 * @param[in] request being modified.
855 * @param[in] map The original map.
856 * @param[in] mod The ephemeral map which describes the change.
857 * @param[in] vb The value in the ephemeral map.
858 */
859static inline void map_list_mod_debug(request_t *request,
860 map_t const *map, map_t const *mod, fr_value_box_t const *vb)
861{
862 char *rhs = NULL;
863 char const *quote = "";
864
865 if (!fr_cond_assert(map->lhs != NULL)) return;
866 if (!fr_cond_assert(map->rhs != NULL)) return;
867
868 fr_assert(mod || tmpl_is_null(map->rhs));
869
870 if (vb && (vb->type == FR_TYPE_STRING)) quote = "\"";
871
872 /*
873 * If it's an exec, ignore the list
874 */
875 if (tmpl_is_exec(map->rhs)) {
876 RDEBUG2("%s %s %s%pV%s", mod->lhs->name, fr_table_str_by_value(fr_tokens_table, mod->op, "<INVALID>"),
877 quote, vb, quote);
878 return;
879 }
880
881 switch (map->rhs->type) {
882 /*
883 * Just print the value being assigned
884 */
885 default:
886 case TMPL_TYPE_XLAT:
888 case TMPL_TYPE_DATA:
889 rhs = fr_asprintf(request, "%s%pV%s", quote, vb, quote);
890 break;
891
892 case TMPL_TYPE_ATTR:
893 rhs = fr_asprintf(request, "%s -> %s%pV%s", map->rhs->name, quote, vb, quote);
894 break;
895
896 case TMPL_TYPE_NULL:
897 rhs = talloc_typed_strdup(request, "ANY");
898 break;
899 }
900
901 switch (map->lhs->type) {
902 case TMPL_TYPE_ATTR:
903 RDEBUG2("%s %s %s", map->lhs->name, fr_table_str_by_value(fr_tokens_table, mod->op, "<INVALID>"), rhs);
904 break;
905
906 default:
907 break;
908 }
909
910 /*
911 * Must be LIFO free order so we don't leak pool memory
912 */
913 talloc_free(rhs);
914}
915
916/** Apply the output of #map_to_list_mod to a request
917 *
918 * @param request to modify.
919 * @param vlm VP List Modification to apply.
920 */
922{
923 int rcode = 0;
924
925 map_t const *map = vlm->map, *mod = NULL;
926 fr_pair_list_t *vp_list;
927 fr_pair_t *found;
929 TALLOC_CTX *parent;
930
931 fr_dcursor_t list;
933
934 memset(&cc, 0, sizeof(cc));
935
936 MAP_VERIFY(map);
937 fr_assert(!map_list_empty(&vlm->mod));
938
939 /*
940 * Print debug information for the mods being applied
941 */
942 while ((mod = map_list_next(&vlm->mod, mod))) {
943 fr_value_box_t *vb;
944
945 MAP_VERIFY(mod);
946
947 fr_assert(mod->lhs != NULL);
948 fr_assert(mod->rhs != NULL);
949
951 fr_assert(((mod->op == T_OP_CMP_FALSE) && tmpl_is_null(mod->rhs)) ||
952 tmpl_is_data(mod->rhs));
953
954 /*
955 * map_list_mod_debug()
956 */
957 if (RDEBUG_ENABLED2) {
958 vb = tmpl_value(mod->rhs);
959
960 map_list_mod_debug(request, map, mod, vb->type != FR_TYPE_NULL ? vb : NULL);
961 }
962 }
963 mod = map_list_head(&vlm->mod); /* Reset */
964
965 /*
966 * All this has been checked by #map_to_list_mod
967 */
968 context = request;
969 if (!fr_cond_assert(mod && tmpl_request_ptr(&context, tmpl_request(mod->lhs)) == 0)) return -1;
970
971 vp_list = tmpl_list_head(context, tmpl_list(mod->lhs));
972 if (!fr_cond_assert(vp_list)) return -1;
973
976
977 /*
978 * The destination is a list (which is a completely different set of operations)
979 */
980 if (tmpl_is_list(map->lhs)) {
981 switch (mod->op) {
982 case T_OP_CMP_FALSE:
983 fr_pair_list_free(vp_list); /* Clear the entire list */
984 goto finish;
985
986 case T_OP_SET:
987 {
988 fr_pair_list_t tmp_list;
989 fr_pair_list_init(&tmp_list);
990 fr_pair_list_free(vp_list); /* Clear the existing list */
991 map_list_mod_to_vps(parent, &tmp_list, vlm); /* Replace with a new list */
992 fr_pair_list_append(vp_list, &tmp_list);
993 goto finish;
994 }
995
996 /*
997 * Ugh... exponential... Fixme? Build a tree if number
998 * of attribute in to is > n?
999 */
1000 case T_OP_EQ:
1001 {
1002 bool exists = false;
1003 fr_pair_list_t vp_from, vp_to_insert;
1004 fr_pair_t *vp;
1005
1006 fr_pair_list_init(&vp_from);
1007 fr_pair_list_init(&vp_to_insert);
1008 map_list_mod_to_vps(parent, &vp_from, vlm);
1009 if (fr_pair_list_empty(&vp_from)) goto finish;
1010
1011 while ((vp = fr_pair_remove(&vp_from, fr_pair_list_head(&vp_from)))) {
1012 fr_pair_list_foreach(vp_list, vp_to) {
1013 if (fr_pair_cmp_by_da(vp_to, vp) == 0) {
1014 exists = true;
1015 break;
1016 }
1017 }
1018
1019 if (exists) {
1020 talloc_free(vp); /* Don't overwrite */
1021 } else {
1022 fr_pair_append(&vp_to_insert, vp);
1023 }
1024 }
1025
1026 fr_pair_list_append(vp_list, &vp_to_insert); /* Do this last so we don't expand the 'to' set */
1027 }
1028 goto finish;
1029
1030 case T_OP_ADD_EQ:
1031 {
1032 fr_pair_list_t vp_from;
1033
1034 fr_pair_list_init(&vp_from);
1035 map_list_mod_to_vps(parent, &vp_from, vlm);
1036 fr_assert(!fr_pair_list_empty(&vp_from));
1037
1038 fr_pair_list_append(vp_list, &vp_from);
1039 }
1040 goto finish;
1041
1042 case T_OP_PREPEND:
1043 {
1044 fr_pair_list_t vp_from;
1045
1046 fr_pair_list_init(&vp_from);
1047 map_list_mod_to_vps(parent, &vp_from, vlm);
1048 fr_assert(!fr_pair_list_empty(&vp_from));
1049
1050 fr_pair_list_prepend(vp_list, &vp_from);
1051
1052 goto finish;
1053 }
1054
1055 default:
1056 rcode = -1;
1057 goto finish;
1058 }
1059 }
1060
1061 fr_assert(!map_list_next(&vlm->mod, mod));
1062
1063 /*
1064 * Find the destination attribute. We leave with either
1065 * the list and vp pointing to the attribute or the VP
1066 * being NULL (no attribute at that index).
1067 */
1068 found = tmpl_dcursor_init(NULL, request, &cc, &list, request, mod->lhs);
1069
1070 /*
1071 * The destination is an attribute
1072 */
1073 switch (mod->op) {
1074 /*
1075 * !* - Remove all attributes which match the LHS attribute.
1076 */
1077 case T_OP_CMP_FALSE:
1078 if (!found) goto finish;
1079
1080 /*
1081 * The cursor was set to the Nth one. Delete it, and only it.
1082 */
1083 if (tmpl_attr_tail_num(map->lhs) != NUM_ALL) {
1084 fr_dcursor_free_item(&list);
1085 /*
1086 * Wildcard: delete all of the matching ones
1087 */
1088 } else {
1089 fr_dcursor_free_list(&list); /* Remember, we're using a custom iterator */
1090 }
1091
1092 /*
1093 * Check that the User-Name and User-Password
1094 * caches point to the correct attribute.
1095 */
1096 goto finish;
1097
1098 /*
1099 * -= - Delete attributes in the found list which match any of the
1100 * src_list attributes.
1101 *
1102 * This operation has two modes:
1103 * - If tmpl_attr_tail_num(map->lhs) > 0, we check each of the src_list attributes against
1104 * the found attribute, to see if any of their values match.
1105 * - If tmpl_attr_tail_num(map->lhs) == NUM_UNSPEC, we compare all instances of the found attribute
1106 * against each of the src_list attributes.
1107 */
1108 case T_OP_SUB_EQ:
1109 {
1110 /* We didn't find any attributes earlier */
1111 if (!found) goto finish;
1112
1113 /*
1114 * Instance specific[n] delete
1115 *
1116 * i.e. Remove this single instance if it matches
1117 * any of these values.
1118 */
1119 if (tmpl_attr_tail_num(map->lhs) != NUM_ALL) {
1120 fr_value_box_t *vb = tmpl_value(map_list_head(&vlm->mod)->rhs);
1121
1122 if (fr_value_box_cmp(vb, &found->data) == 0) {
1123 fr_dcursor_free_item(&list);
1124 goto finish;
1125 }
1126
1127 goto finish; /* Wasn't found */
1128 }
1129
1130 /*
1131 * All instances[*] delete
1132 *
1133 * i.e. Remove any instance of this attribute which
1134 * matches any of these values.
1135 */
1136 do {
1137 fr_value_box_t *vb = tmpl_value(map_list_head(&vlm->mod)->rhs);
1138
1139 if (fr_value_box_cmp(vb, &found->data) == 0) {
1140 fr_dcursor_free_item(&list);
1141 break;
1142 }
1143 } while ((found = fr_dcursor_next(&list)));
1144 }
1145 goto finish;
1146
1147 /*
1148 * += - Add all attributes to the destination
1149 */
1150 case T_OP_ADD_EQ:
1151 do_add:
1152 {
1153 fr_pair_list_t vp_from;
1154
1155 fr_pair_list_init(&vp_from);
1156 map_list_mod_to_vps(parent, &vp_from, vlm);
1157 if (fr_pair_list_empty(&vp_from)) goto finish;
1158
1159 fr_pair_list_append(vp_list, &vp_from);
1160 }
1161 goto finish;
1162
1163 case T_OP_PREPEND:
1164 {
1165 fr_pair_list_t vp_from;
1166
1167 fr_pair_list_init(&vp_from);
1168 map_list_mod_to_vps(parent, &vp_from, vlm);
1169 fr_assert(!fr_pair_list_empty(&vp_from));
1170
1171 fr_pair_list_prepend(vp_list, &vp_from);
1172
1173 goto finish;
1174 }
1175
1176 /*
1177 * = - Set only if not already set
1178 */
1179 case T_OP_EQ:
1180 if (found) {
1181 RDEBUG3("Refusing to overwrite (use :=)");
1182 goto finish;
1183 }
1184 goto do_add;
1185
1186 /*
1187 * := - Overwrite existing attribute with last src_list attribute
1188 */
1189 case T_OP_SET:
1190 if (!found) goto do_add;
1191
1192 /*
1193 * Instance specific[n] overwrite
1194 */
1195 if (tmpl_attr_tail_num(map->lhs) != NUM_ALL) {
1196 fr_dcursor_t from;
1197 fr_pair_list_t vp_from;
1198
1199 fr_pair_list_init(&vp_from);
1200 map_list_mod_to_vps(parent, &vp_from, vlm);
1201 if (fr_pair_list_empty(&vp_from)) goto finish;
1202
1203 fr_pair_dcursor_init(&from, &vp_from);
1204
1205 fr_dcursor_merge(&list, &from); /* Merge first (insert after current attribute) */
1206 fr_dcursor_free_item(&list); /* Then free the current attribute */
1207 goto finish;
1208 }
1209
1210 /*
1211 * All instances[*] overwrite
1212 */
1213 fr_dcursor_free_list(&list); /* Remember, we're using a custom iterator */
1214 goto do_add;
1215
1216 /*
1217 * !=, ==, >=, >, <=, < - Filter operators
1218 */
1219 case T_OP_NE:
1220 case T_OP_CMP_EQ:
1221 case T_OP_GE:
1222 case T_OP_GT:
1223 case T_OP_LE:
1224 case T_OP_LT:
1225 {
1226 if (!found) goto finish;
1227
1228 /*
1229 * Instance specific[n] filter
1230 */
1231 if (tmpl_attr_tail_num(map->lhs) != NUM_ALL) {
1232 fr_value_box_t *vb = tmpl_value(mod->rhs);
1233 bool remove = true;
1234
1235 if (fr_value_box_cmp_op(mod->op, &found->data, vb) == 1) remove = false;
1236
1237 if (remove) fr_dcursor_free_item(&list);
1238 goto finish;
1239 }
1240
1241 /*
1242 * All instances[*] filter
1243 */
1244 do {
1245 fr_value_box_t *vb = tmpl_value(mod->rhs);
1246 bool remove = true;
1247
1248 if (fr_value_box_cmp_op(mod->op, &found->data, vb) == 1) remove = false;
1249
1250 if (remove) {
1251 fr_dcursor_free_item(&list);
1252 } else {
1253 fr_dcursor_next(&list);
1254 }
1255 } while ((found = fr_dcursor_current(&list)));
1256 }
1257 goto finish;
1258
1259 default:
1260 fr_assert(0); /* Should have been caught be the caller */
1261 rcode = -1;
1262 goto finish;
1263 }
1264
1265finish:
1266 tmpl_dcursor_clear(&cc);
1267 return rcode;
1268}
int n
Definition acutest.h:577
static int context
Definition radmin.c:71
#define RCSID(id)
Definition build.h:485
static void * fr_dcursor_next(fr_dcursor_t *cursor)
Advanced the cursor to the next item.
Definition dcursor.h:288
static int fr_dcursor_append(fr_dcursor_t *cursor, void *v)
Insert a single item at the end of the list.
Definition dcursor.h:406
static void fr_dcursor_merge(fr_dcursor_t *cursor, fr_dcursor_t *to_append)
Moves items from one cursor to another.
Definition dcursor.h:520
#define fr_dcursor_init(_cursor, _head)
Initialise a cursor.
Definition dcursor.h:732
static void fr_dcursor_free_item(fr_dcursor_t *cursor)
talloc_free the current item
Definition dcursor.h:805
static void * fr_dcursor_remove(fr_dcursor_t *cursor)
Remove the current item.
Definition dcursor.h:480
static void fr_dcursor_free_list(fr_dcursor_t *cursor)
Free the current item and all items after it.
Definition dcursor.h:663
static void * fr_dcursor_current(fr_dcursor_t *cursor)
Return the item the cursor current points to.
Definition dcursor.h:337
static void * fr_dcursor_head(fr_dcursor_t *cursor)
Rewind cursor to the start of the list.
Definition dcursor.h:234
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:139
#define MEM(x)
Definition debug.h:36
static fr_slen_t err
Definition dict.h:831
Test enumeration values.
Definition dict_test.h:92
#define RDEBUG3(fmt,...)
Definition log.h:343
#define RPEDEBUG(fmt,...)
Definition log.h:376
int map_afrom_vp(TALLOC_CTX *ctx, map_t **out, fr_pair_t *vp, tmpl_rules_t const *rules)
Convert a fr_pair_t into a map.
Definition map.c:1436
talloc_free(reap)
#define MAP_VERIFY(_x)
Definition map.h:108
map_t const * map
Original map describing the change to be made.
Definition map.h:100
map_list_t mod
New map containing the destination (LHS) and values (RHS).
Definition map.h:102
A list modification.
Definition map.h:99
static void map_list_mod_debug(request_t *request, map_t const *map, map_t const *mod, fr_value_box_t const *vb)
Print debug for a modification map.
Definition map_async.c:859
static map_t * map_alloc(TALLOC_CTX *ctx)
Definition map_async.c:53
static vp_list_mod_t * list_mod_delete_afrom_map(TALLOC_CTX *ctx, map_t const *original, map_t const *mutated)
Allocate a 'delete' vp_list_mod_t.
Definition map_async.c:116
static vp_list_mod_t * list_mod_generic_afrom_map(TALLOC_CTX *ctx, map_t const *original, map_t const *mutated)
Allocate a 'generic' vp_list_mod_t.
Definition map_async.c:75
static vp_list_mod_t * list_mod_empty_string_afrom_map(TALLOC_CTX *ctx, map_t const *original, map_t const *mutated)
Allocate an 'empty_string' vp_list_mod_t.
Definition map_async.c:156
int map_list_mod_apply(request_t *request, vp_list_mod_t const *vlm)
Apply the output of map_to_list_mod to a request.
Definition map_async.c:921
static fr_pair_list_t * map_check_src_or_dst(request_t *request, map_t const *map, tmpl_t const *src_dst)
Check that the destination list is currently value.
Definition map_async.c:205
static vp_list_mod_t * list_mod_alloc(TALLOC_CTX *ctx)
Definition map_async.c:45
static fr_pair_t * map_list_mod_to_vp(TALLOC_CTX *ctx, tmpl_t const *attr, fr_value_box_t const *value)
Definition map_async.c:801
static void map_list_mod_to_vps(TALLOC_CTX *ctx, fr_pair_list_t *list, vp_list_mod_t const *vlm)
Allocate one or more fr_pair_ts from a vp_list_mod_t.
Definition map_async.c:818
int map_to_list_mod(TALLOC_CTX *ctx, vp_list_mod_t **out, request_t *request, map_t const *original, fr_value_box_list_t *lhs_result, fr_value_box_list_t *rhs_result)
Evaluate a map creating a new map with TMPL_TYPE_ATTR LHS and TMPL_TYPE_DATA RHS.
Definition map_async.c:251
fr_type_t
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_NULL
Invalid (uninitialised) attribute type.
long int ssize_t
unsigned char uint8_t
int fr_pair_append(fr_pair_list_t *list, fr_pair_t *to_add)
Add a VP to the end of the list.
Definition pair.c:1347
fr_pair_t * fr_pair_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
Dynamically allocate a new attribute and assign a fr_dict_attr_t.
Definition pair.c:285
void fr_pair_list_init(fr_pair_list_t *list)
Initialise a pair list header.
Definition pair.c:46
void fr_pair_list_afrom_box(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_t const *dict, fr_value_box_t *box)
Parse a list of VPs from a value box.
Definition pair.c:3481
int8_t fr_pair_cmp_by_da(void const *a, void const *b)
Order attributes by their da, and tag.
Definition pair.c:1846
char * fr_asprintf(TALLOC_CTX *ctx, char const *fmt,...)
Special version of asprintf which implements custom format specifiers.
Definition print.c:874
#define fr_assert(_expr)
Definition rad_assert.h:38
#define REDEBUG(fmt,...)
Definition radclient.h:52
#define RDEBUG_ENABLED2()
Definition radclient.h:50
#define RDEBUG2(fmt,...)
Definition radclient.h:54
static int16_t tmpl_attr_tail_num(tmpl_t const *vpt)
Return the last attribute reference's attribute number.
Definition tmpl.h:890
#define tmpl_is_xlat(vpt)
Definition tmpl.h:215
#define tmpl_value(_tmpl)
Definition tmpl.h:942
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.
#define tmpl_is_attr(vpt)
Definition tmpl.h:213
#define NUM_ALL
Definition tmpl.h:396
#define tmpl_is_exec(vpt)
Definition tmpl.h:216
#define tmpl_xlat(_tmpl)
Definition tmpl.h:935
static fr_dict_attr_t const * tmpl_list(tmpl_t const *vpt)
Definition tmpl.h:909
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.
@ TMPL_TYPE_ATTR
Reference to one or more attributes.
Definition tmpl.h:146
@ TMPL_TYPE_XLAT
Pre-parsed xlat expansion.
Definition tmpl.h:150
@ TMPL_TYPE_NULL
Has no value.
Definition tmpl.h:138
@ TMPL_TYPE_EXEC
Callout to an external script or program.
Definition tmpl.h:154
@ TMPL_TYPE_DATA
Value in native boxed format.
Definition tmpl.h:142
@ TMPL_TYPE_DATA_UNRESOLVED
Unparsed literal string.
Definition tmpl.h:183
static bool tmpl_attr_tail_da_is_structural(tmpl_t const *vpt)
Return true if the the last attribute reference is a structural attribute.
Definition tmpl.h:840
fr_pair_list_t * tmpl_list_head(request_t *request, fr_dict_attr_t const *list)
Resolve attribute fr_pair_list_t value to an attribute list.
Definition tmpl_eval.c:76
TALLOC_CTX * tmpl_list_ctx(request_t *request, fr_dict_attr_t const *list)
Return the correct TALLOC_CTX to alloc fr_pair_t in, for a list.
Definition tmpl_eval.c:116
static bool tmpl_is_list(tmpl_t const *vpt)
Definition tmpl.h:925
#define tmpl_is_data(vpt)
Definition tmpl.h:211
#define tmpl_value_type(_tmpl)
Definition tmpl.h:944
int tmpl_request_ptr(request_t **request, FR_DLIST_HEAD(tmpl_request_list) const *rql)
Resolve a tmpl_request_ref_t to a request_t.
Definition tmpl_eval.c:169
#define tmpl_is_data_unresolved(vpt)
Definition tmpl.h:222
tmpl_attr_rules_t attr
Rules/data for parsing attribute references.
Definition tmpl.h:340
int tmpl_attr_copy(tmpl_t *dst, tmpl_t const *src)
Copy a list of attribute and request references from one tmpl to another.
static fr_dict_attr_t const * tmpl_attr_tail_da(tmpl_t const *vpt)
Return the last attribute reference da.
Definition tmpl.h:806
#define tmpl_is_null(vpt)
Definition tmpl.h:210
int tmpl_attr_set_leaf_da(tmpl_t *vpt, fr_dict_attr_t const *da)
Replace the leaf attribute only.
static char const * tmpl_list_name(fr_dict_attr_t const *list, char const *def)
Return the name of a tmpl list or def if list not provided.
Definition tmpl.h:920
Optional arguments passed to vp_tmpl functions.
Definition tmpl.h:337
fr_aka_sim_id_type_t type
fr_pair_t * vp
Value pair map.
Definition map.h:77
fr_token_t op
The operator that controls insertion of the dst attribute.
Definition map.h:82
tmpl_t * lhs
Typically describes the attribute to add, modify or compare.
Definition map.h:78
map_list_t child
parent map, for nested ones
Definition map.h:89
tmpl_t * rhs
Typically describes a literal value or a src attribute to copy or compare.
Definition map.h:79
fr_type_t cast
Cast value to this type.
Definition map.h:83
fr_dict_attr_t const * list_def
Default list to use with unqualified attribute reference.
Definition tmpl.h:300
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_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition table.h:772
char * talloc_typed_strdup(TALLOC_CTX *ctx, char const *p)
Call talloc_strdup, setting the type on the new chunk correctly.
Definition talloc.c:445
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.
fr_table_num_ordered_t const fr_tokens_table[]
Definition token.c:33
@ T_OP_SUB_EQ
Definition token.h:70
@ T_BARE_WORD
Definition token.h:120
@ T_OP_EQ
Definition token.h:83
@ 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_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_OP_LT
Definition token.h:101
@ T_OP_REG_NE
Definition token.h:103
@ T_OP_PREPEND
Definition token.h:85
static fr_slen_t head
Definition xlat.h:418
bool fr_pair_list_empty(fr_pair_list_t const *list)
Is a valuepair list empty.
#define PAIR_VERIFY(_x)
Definition pair.h:191
fr_pair_t * fr_pair_list_next(fr_pair_list_t const *list, fr_pair_t const *item))
Get the next item in a valuepair list after a specific entry.
Definition pair_inline.c:70
fr_pair_t * fr_pair_remove(fr_pair_list_t *list, fr_pair_t *vp)
Remove fr_pair_t from a list without freeing.
Definition pair_inline.c:94
#define fr_pair_list_foreach(_list_head, _iter)
Iterate over the contents of a fr_pair_list_t.
Definition pair.h:261
void fr_pair_list_free(fr_pair_list_t *list)
Free memory used by a valuepair list.
void fr_pair_list_append(fr_pair_list_t *dst, fr_pair_list_t *src)
Appends a list of fr_pair_t from a temporary list to a destination list.
void fr_pair_list_prepend(fr_pair_list_t *dst, fr_pair_list_t *src)
Move a list of fr_pair_t from a temporary list to the head of a destination list.
#define fr_pair_dcursor_init(_cursor, _list)
Initialises a special dcursor with callbacks that will maintain the attr sublists correctly.
Definition pair.h:591
fr_pair_t * fr_pair_list_head(fr_pair_list_t const *list)
Get the head of a valuepair list.
Definition pair_inline.c:43
static fr_slen_t parent
Definition pair.h:845
int fr_value_box_cast(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, fr_value_box_t const *src)
Convert one type of fr_value_box_t to another.
Definition value.c:3370
int8_t fr_value_box_cmp(fr_value_box_t const *a, fr_value_box_t const *b)
Compare two values.
Definition value.c:676
int fr_value_box_copy(TALLOC_CTX *ctx, fr_value_box_t *dst, const fr_value_box_t *src)
Copy value data verbatim duplicating any buffers.
Definition value.c:3759
int fr_value_box_cmp_op(fr_token_t op, fr_value_box_t const *a, fr_value_box_t const *b)
Compare two attributes using an operator.
Definition value.c:929
ssize_t fr_value_box_from_str(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, char const *in, size_t inlen, fr_sbuff_unescape_rules_t const *erules)
Definition value.c:5246
fr_sbuff_unescape_rules_t * fr_value_unescape_by_quote[T_TOKEN_LAST]
Definition value.c:336
int fr_value_box_list_concat_in_place(TALLOC_CTX *ctx, fr_value_box_t *out, fr_value_box_list_t *list, fr_type_t type, fr_value_box_list_action_t proc_action, bool flatten, size_t max_size)
Concatenate a list of value boxes.
Definition value.c:5734
@ FR_VALUE_BOX_LIST_FREE
Definition value.h:232
static fr_slen_t data
Definition value.h:1274
#define fr_box_strvalue_len(_val, _len)
Definition value.h:297
#define fr_value_box_alloc_null(_ctx)
Allocate a value box for later use with a value assignment function.
Definition value.h:643
static size_t char ** out
Definition value.h:1012