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