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: 8d8f0ac870f4756f53b86c941de5ea59577b1e65 $
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: 8d8f0ac870f4756f53b86c941de5ea59577b1e65 $")
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 .prefix = TMPL_ATTR_REF_PREFIX_NO
326 }
327 });
328 if (slen <= 0) {
329 RPEDEBUG("Left side expansion result \"%s\" is not an attribute reference",
330 lhs_result_head->vb_strvalue);
331 fr_value_box_list_talloc_free(lhs_result);
332 goto error;
333 }
334 fr_assert(tmpl_is_attr(mutated->lhs));
335 }
336 break;
337
338 default:
339 fr_assert(0);
340 break;
341 }
342
343 /*
344 * Special case for !*, we don't need to parse RHS as this is a unary operator.
345 */
346 if (mutated->op == T_OP_CMP_FALSE) {
347 map_t *mod;
348 n = list_mod_alloc(ctx);
349 if (!n) goto error;
350
351 n->map = original;
352 mod = map_alloc(n); /* Need to duplicate input map, so next pointer is NULL */
353 mod->lhs = mutated->lhs;
354 mod->op = mutated->op;
355 mod->rhs = mutated->rhs;
356 map_list_insert_tail(&n->mod, mod);
357 goto finish;
358 }
359
360 /*
361 * List to list copy.
362 */
363 if (tmpl_is_attr(mutated->lhs) && tmpl_is_attr(mutated->rhs) &&
365 fr_pair_list_t *list = NULL;
366 fr_pair_t *vp = NULL;
367
368 /*
369 * Check source list
370 */
371 list = map_check_src_or_dst(request, mutated, mutated->rhs);
372 if (!list) goto error;
373
374 vp = fr_pair_list_head(list);
375 /*
376 * No attributes found on LHS.
377 */
378 if (!vp) {
379 /*
380 * Special case for := if RHS was NULL.
381 * Should delete all LHS attributes.
382 */
383 if (mutated->op == T_OP_SET) n = list_mod_delete_afrom_map(ctx, original, mutated);
384 goto finish;
385 }
386
387 n = list_mod_alloc(ctx);
388 n->map = original;
389
390 /*
391 * Iterate over all attributes in that list
392 */
393 do {
394 map_t *n_mod;
395
396 n_mod = map_alloc(n);
397 if (!n_mod) goto error;
398
399 n_mod->op = mutated->op;
400
401 /*
402 * For the LHS we need to create a reference to
403 * the attribute, with the same destination list
404 * as the current LHS map.
405 */
406 n_mod->lhs = tmpl_alloc(n, TMPL_TYPE_ATTR, T_BARE_WORD, mutated->lhs->name, mutated->lhs->len);
407 if (!n_mod->lhs) goto error;
408
409 if (tmpl_attr_copy(n_mod->lhs, mutated->lhs) < 0) goto error;
410
411 tmpl_attr_set_leaf_da(n_mod->lhs, vp->da);
412
413 /*
414 * For the RHS we copy the value of the attribute
415 * we just found, creating data (literal) tmpl.
416 */
417 n_mod->rhs = tmpl_alloc(n_mod, TMPL_TYPE_DATA,
419 NULL, 0);
420 if (!n_mod->rhs) goto error;
421
422 /*
423 * Have to do a full copy, as the attribute we're
424 * getting the buffer value from may be freed
425 * before this map is applied.
426 */
427 if (fr_value_box_copy(n_mod->rhs, tmpl_value(n_mod->rhs), &vp->data) < 0) goto error;
428 map_list_insert_tail(&n->mod, n_mod);
429
430 MAP_VERIFY(n_mod);
431 } while ((vp = fr_pair_list_next(list, vp)));
432
433 goto finish;
434 }
435
436 /*
437 * Unparsed. These are easy because they
438 * can only have a single value.
439 */
440 if (tmpl_is_data_unresolved(mutated->rhs)) {
441 fr_type_t type = tmpl_attr_tail_da(mutated->lhs)->type;
442
443 fr_assert(tmpl_is_attr(mutated->lhs));
444 fr_assert(tmpl_attr_tail_da(mutated->lhs)); /* We need to know which attribute to create */
445
446 n = list_mod_generic_afrom_map(ctx, original, mutated);
447 if (!n) goto error;
448
449 fr_dcursor_init(&values, fr_value_box_list_dlist_head(&head));
450
451 if (fr_value_box_from_str(map_list_head(&n->mod),
452 tmpl_value(map_list_head(&n->mod)->rhs), type,
453 tmpl_attr_tail_da(mutated->lhs),
454 mutated->rhs->name, mutated->rhs->len,
455 fr_value_unescape_by_quote[(uint8_t)mutated->rhs->quote], false)) {
456 RPEDEBUG("Assigning value to \"%s\" failed", tmpl_attr_tail_da(mutated->lhs)->name);
457 goto error;
458 }
459 goto finish;
460 }
461
462 /*
463 * Check destination list
464 */
465 if (!map_check_src_or_dst(request, mutated, mutated->lhs)) goto error;
466
467 (void)fr_dcursor_init(&values, fr_value_box_list_dlist_head(&head));
468
469 switch (mutated->rhs->type) {
470 case TMPL_TYPE_XLAT:
471 {
472 fr_dcursor_t from;
473 fr_value_box_t *vb, *n_vb;
474
475 fr_assert(tmpl_xlat(mutated->rhs) != NULL);
476
477 assign_values:
478 fr_assert(tmpl_is_attr(mutated->lhs));
479 fr_assert(tmpl_attr_tail_da(mutated->lhs)); /* We need to know which attribute to create */
480
481 /*
482 * Empty value - Try and cast an empty string
483 * to the destination type, and see what
484 * happens. This is only for XLATs and in future
485 * EXECs.
486 */
487 if (fr_value_box_list_empty(rhs_result)) {
488 n = list_mod_empty_string_afrom_map(ctx, original, mutated);
489 if (!n) {
490 RPEDEBUG("Assigning value to \"%s\" failed", tmpl_attr_tail_da(mutated->lhs)->name);
491 xlat_error:
492 fr_dcursor_head(&values);
493 fr_dcursor_free_list(&values);
494 goto error;
495 }
496 goto finish;
497 }
498
499 /*
500 * Non-Empty value
501 */
502 n = list_mod_generic_afrom_map(ctx, original, mutated);
503 if (!n) goto error;
504
505 (void)fr_dcursor_init(&from, fr_value_box_list_dlist_head(rhs_result));
506 while ((vb = fr_dcursor_remove(&from))) {
507 if (vb->type != tmpl_attr_tail_da(mutated->lhs)->type) {
508 n_vb = fr_value_box_alloc_null(map_list_head(&n->mod)->rhs);
509 if (!n_vb) {
510 fr_dcursor_head(&from);
512 goto xlat_error;
513 }
514
515 if (fr_value_box_cast(n_vb, n_vb,
516 mutated->cast ? mutated->cast : tmpl_attr_tail_da(mutated->lhs)->type,
517 tmpl_attr_tail_da(mutated->lhs), vb) < 0) {
518 RPEDEBUG("Assigning value to \"%s\" failed", tmpl_attr_tail_da(mutated->lhs)->name);
519
520 fr_dcursor_head(&from);
522 goto xlat_error;
523 }
524 talloc_free(vb);
525 } else {
526 n_vb = talloc_steal(n, vb); /* Should already be in ctx of n's parent */
527 }
528 fr_dcursor_append(&values, n_vb);
529 }
530 }
531 break;
532
533 case TMPL_TYPE_ATTR:
534 {
535 fr_dcursor_t from;
536 tmpl_dcursor_ctx_t cc_attr;
537 fr_pair_t *vp;
538 fr_value_box_t *n_vb;
539 int err;
540
541 fr_assert(fr_value_box_list_empty(rhs_result));
542 fr_assert(tmpl_is_attr(mutated->lhs) && tmpl_attr_tail_da(mutated->lhs));
543
544 /*
545 * Check source list
546 */
547 if (!map_check_src_or_dst(request, mutated, mutated->rhs)) goto error;
548
549 /*
550 * Check we have pairs to copy *before*
551 * doing any expensive allocations.
552 */
553 vp = tmpl_dcursor_init(&err, request, &cc_attr, &from, request, mutated->rhs);
554 if (!vp) switch (err) {
555 default:
556 break;
557
558 case -1: /* No input pairs */
559 RDEBUG3("No matching pairs found for \"%s\"", tmpl_attr_tail_da(mutated->rhs)->name);
560 /*
561 * Special case for := if RHS had no attributes
562 * we should delete all LHS attributes.
563 */
564 if (mutated->op == T_OP_SET) n = list_mod_delete_afrom_map(ctx, original, mutated);
565 tmpl_dcursor_clear(&cc_attr);
566 goto finish;
567
568 case -2: /* No matching list */
569 case -3: /* No request context */
570 case -4: /* memory allocation error */
571 RPEDEBUG("Failed resolving attribute source");
572 tmpl_dcursor_clear(&cc_attr);
573 goto error;
574 }
575
576 n = list_mod_generic_afrom_map(ctx, original, mutated);
577 if (!n) {
578 tmpl_dcursor_clear(&cc_attr);
579 goto error;
580 }
581
582 vp = fr_dcursor_current(&from);
583 fr_assert(vp); /* Should have errored out */
584 do {
585 n_vb = fr_value_box_alloc_null(map_list_head(&n->mod)->rhs);
586 if (!n_vb) {
587 attr_error:
588 fr_dcursor_head(&values);
589 fr_dcursor_free_list(&values);
590 tmpl_dcursor_clear(&cc_attr);
591 goto error;
592 }
593
594 if (vp->data.type != tmpl_attr_tail_da(mutated->lhs)->type) {
595 if (fr_value_box_cast(n_vb, n_vb,
596 mutated->cast ? mutated->cast : tmpl_attr_tail_da(mutated->lhs)->type,
597 tmpl_attr_tail_da(mutated->lhs), &vp->data) < 0) {
598 RPEDEBUG("Assigning value to \"%s\" failed", tmpl_attr_tail_da(mutated->lhs)->name);
599
600 goto attr_error;
601 }
602 } else {
603 fr_value_box_copy(n_vb, n_vb, &vp->data);
604 }
605 fr_dcursor_append(&values, n_vb);
606 } while ((vp = fr_dcursor_next(&from)));
607
608 tmpl_dcursor_clear(&cc_attr);
609 }
610 break;
611
612 case TMPL_TYPE_DATA:
613 {
614 fr_value_box_t *vb, *n_vb;
615
616 fr_assert(fr_value_box_list_empty(rhs_result));
617 fr_assert(tmpl_attr_tail_da(mutated->lhs));
618 fr_assert(tmpl_is_attr(mutated->lhs));
619
620 n = list_mod_generic_afrom_map(ctx, original, mutated);
621 if (!n) goto error;
622
623 vb = tmpl_value(mutated->rhs);
624
625 n_vb = fr_value_box_alloc_null(map_list_head(&n->mod)->rhs);
626 if (!n_vb) {
627 data_error:
628 fr_dcursor_head(&values);
629 fr_dcursor_free_list(&values);
630 goto error;
631 }
632 /*
633 * This should be optimised away by the map
634 * parser, but in case we're applying runtime
635 * maps we still need to check if we need to
636 * cast.
637 */
638 if (tmpl_attr_tail_da(mutated->lhs)->type != tmpl_value_type(mutated->rhs)) {
639 if (fr_value_box_cast(n_vb, n_vb,
640 mutated->cast ? mutated->cast : tmpl_attr_tail_da(mutated->lhs)->type,
641 tmpl_attr_tail_da(mutated->lhs), vb) < 0) {
642 RPEDEBUG("Assigning value to \"%s\" failed", tmpl_attr_tail_da(mutated->lhs)->name);
643 goto data_error;
644 }
645 } else {
646 /*
647 * We need to do a full copy, as shallow
648 * copy would increase the reference count
649 * on the static/global buffers and possibly
650 * lead to threading issues.
651 */
652 if (fr_value_box_copy(n_vb, n_vb, vb) < 0) goto data_error;
653 }
654 fr_dcursor_append(&values, n_vb);
655 }
656 break;
657
658 /*
659 * The result of an exec is a value if the LHS is an
660 * attribute, or a set of VPs, if the LHS is a list.
661 *
662 * @todo - we should just create maps from the RHS
663 * instead of VPs, and then converting them to maps.
664 */
665 case TMPL_TYPE_EXEC:
666 {
667 fr_dcursor_t from;
668 fr_pair_list_t vp_head;
669 fr_pair_t *vp;
670 fr_value_box_t *rhs_result_head = fr_value_box_list_head(rhs_result);
671
672 fr_pair_list_init(&vp_head);
673 /*
674 * If the LHS is an attribute, we just do the
675 * same thing as an xlat expansion.
676 */
677 if (tmpl_is_attr(mutated->lhs)) goto assign_values;
678
679 fr_assert(tmpl_is_list(mutated->lhs));
680
681 /*
682 * Empty value - Try and cast an empty string
683 * to the destination type, and see what
684 * happens. This is only for XLATs and in future
685 * EXECs.
686 */
687 if (fr_value_box_list_empty(rhs_result)) {
688 RPEDEBUG("Cannot assign empty value to \"%s\"", mutated->lhs->name);
689 goto error;
690 }
691
692 /*
693 * This should always be a noop, but included
694 * here for robustness.
695 */
696 if (fr_value_box_list_concat_in_place(rhs_result_head,
697 rhs_result_head, rhs_result, FR_TYPE_STRING,
699 SIZE_MAX) < 0) {
700 RPEDEBUG("Right side expansion failed");
701 fr_value_box_list_talloc_free(rhs_result);
702 goto error;
703 }
704
705 n = list_mod_alloc(ctx);
706 if (!n) goto error;
707
708 n->map = original;
709
710 /*
711 * Parse the VPs from the RHS.
712 */
713 fr_pair_list_afrom_box(ctx, &vp_head, request->dict, rhs_result_head);
714 if (fr_pair_list_empty(&vp_head)) {
715 talloc_free(n);
716 RDEBUG2("No pairs returned by exec");
717 return 0; /* No pairs returned */
718 }
719
720 (void)fr_pair_dcursor_init(&from, &vp_head);
721 while ((vp = fr_dcursor_remove(&from))) {
722 map_t *mod;
723 tmpl_rules_t rules = {
724 .attr = {
725 .request_def = tmpl_request(mutated->lhs),
726 .list_def = tmpl_list(mutated->lhs)
727 }
728 };
729
730 if (map_afrom_vp(n, &mod, vp, &rules) < 0) {
731 RPEDEBUG("Failed converting VP to map");
732 fr_dcursor_head(&from);
734 goto error;
735 }
736
737 if (tmpl_is_exec(mod->lhs) || tmpl_is_exec(mod->rhs)) {
738 RPEDEBUG("Program output cannot request execution of another program for attribute %s", vp->da->name);
739 fr_dcursor_head(&from);
741 goto error;
742 }
743
744
745 if ((vp->op == T_OP_REG_EQ) || (vp->op == T_OP_REG_NE)) {
746 RPEDEBUG("Program output cannot request regular expression matching for attribute %s", vp->da->name);
747 fr_dcursor_head(&from);
749 goto error;
750 }
751
752 mod->op = vp->op;
753 map_list_insert_tail(&n->mod, mod);
754 }
755
756 }
757 goto finish;
758
759 default:
760 fr_assert(0); /* Should have been caught at parse time */
761 goto error;
762 }
763
764 fr_assert(!fr_value_box_list_empty(&head) || !n);
765
766 /*
767 * FIXME: This is only required because
768 * tmpls allocate space for a value.
769 *
770 * If tmpl_value were a pointer we could
771 * assign values directly.
772 */
773 fr_value_box_copy(map_list_head(&n->mod)->rhs, tmpl_value(map_list_head(&n->mod)->rhs), fr_value_box_list_head(&head));
774 /*
775 * value boxes in tmpls cannot now be the head of a list
776 *
777 *tmpl_value(map_list_head(&n->mod)->rhs)->next = head->next;
778 */
779 fr_value_box_list_talloc_free(&head);
780
781finish:
782 if (n) {
783 MAP_VERIFY(n->map);
784 *out = n;
785 }
786
787 /*
788 * Reparent ephemeral LHS to the vp_list_mod_t.
789 */
790 if (tmp_ctx) {
791 if (talloc_parent(mutated->lhs) == tmp_ctx) talloc_steal(n, mutated->lhs);
792 talloc_free(tmp_ctx);
793 }
794 return 0;
795
796error:
797 talloc_free(tmp_ctx);
798 talloc_free(n); /* Frees all mod maps too */
799 return -1;
800}
801
802static inline fr_pair_t *map_list_mod_to_vp(TALLOC_CTX *ctx, tmpl_t const *attr, fr_value_box_t const *value)
803{
804 fr_pair_t *vp;
805
807 if (fr_value_box_copy(vp, &vp->data, value) < 0) {
809 return NULL;
810 }
811 PAIR_VERIFY(vp); /* Check we created something sane */
812
813 return vp;
814}
815
816/** Allocate one or more fr_pair_ts from a #vp_list_mod_t
817 *
818 */
819static void map_list_mod_to_vps(TALLOC_CTX *ctx, fr_pair_list_t *list, vp_list_mod_t const *vlm)
820{
821 map_t *mod;
822
823 fr_assert(!map_list_empty(&vlm->mod));
824
825 /*
826 * Fast path...
827 */
828 mod = map_list_head(&vlm->mod);
829 if (map_list_num_elements(&vlm->mod) == 1) {
830 fr_pair_t *vp;
831 vp = map_list_mod_to_vp(ctx, mod->lhs, tmpl_value(mod->rhs));
832 fr_pair_append(list, vp);
833 return;
834 }
835
836 /*
837 * Slow path. This may generate multiple attributes.
838 */
839 for (;
840 mod;
841 mod = map_list_next(&vlm->mod, mod)) {
842 fr_pair_t *vp;
843
844 vp = map_list_mod_to_vp(ctx, mod->lhs, tmpl_value(mod->rhs));
845 if (!vp) {
846 fr_pair_list_free(list);
847 return;
848 }
849 fr_pair_append(list, vp);
850 }
851}
852
853/** Print debug for a modification map
854 *
855 * @param[in] request being modified.
856 * @param[in] map The original map.
857 * @param[in] mod The ephemeral map which describes the change.
858 * @param[in] vb The value in the ephemeral map.
859 */
860static inline void map_list_mod_debug(request_t *request,
861 map_t const *map, map_t const *mod, fr_value_box_t const *vb)
862{
863 char *rhs = NULL;
864 char const *quote = "";
865
866 if (!fr_cond_assert(map->lhs != NULL)) return;
867 if (!fr_cond_assert(map->rhs != NULL)) return;
868
869 fr_assert(mod || tmpl_is_null(map->rhs));
870
871 if (vb && (vb->type == FR_TYPE_STRING)) quote = "\"";
872
873 /*
874 * If it's an exec, ignore the list
875 */
876 if (tmpl_is_exec(map->rhs)) {
877 RDEBUG2("%s %s %s%pV%s", mod->lhs->name, fr_table_str_by_value(fr_tokens_table, mod->op, "<INVALID>"),
878 quote, vb, quote);
879 return;
880 }
881
882 switch (map->rhs->type) {
883 /*
884 * Just print the value being assigned
885 */
886 default:
887 case TMPL_TYPE_XLAT:
889 case TMPL_TYPE_DATA:
890 rhs = fr_asprintf(request, "%s%pV%s", quote, vb, quote);
891 break;
892
893 case TMPL_TYPE_ATTR:
894 rhs = fr_asprintf(request, "%s -> %s%pV%s", map->rhs->name, quote, vb, quote);
895 break;
896
897 case TMPL_TYPE_NULL:
898 rhs = talloc_typed_strdup(request, "ANY");
899 break;
900 }
901
902 switch (map->lhs->type) {
903 case TMPL_TYPE_ATTR:
904 RDEBUG2("%s %s %s", map->lhs->name, fr_table_str_by_value(fr_tokens_table, mod->op, "<INVALID>"), rhs);
905 break;
906
907 default:
908 break;
909 }
910
911 /*
912 * Must be LIFO free order so we don't leak pool memory
913 */
914 talloc_free(rhs);
915}
916
917/** Apply the output of #map_to_list_mod to a request
918 *
919 * @param request to modify.
920 * @param vlm VP List Modification to apply.
921 */
923{
924 int rcode = 0;
925
926 map_t const *map = vlm->map, *mod = NULL;
927 fr_pair_list_t *vp_list;
928 fr_pair_t *found;
930 TALLOC_CTX *parent;
931
932 fr_dcursor_t list;
934
935 memset(&cc, 0, sizeof(cc));
936
937 MAP_VERIFY(map);
938 fr_assert(!map_list_empty(&vlm->mod));
939
940 /*
941 * Print debug information for the mods being applied
942 */
943 while ((mod = map_list_next(&vlm->mod, mod))) {
944 fr_value_box_t *vb;
945
946 MAP_VERIFY(mod);
947
948 fr_assert(mod->lhs != NULL);
949 fr_assert(mod->rhs != NULL);
950
952 fr_assert(((mod->op == T_OP_CMP_FALSE) && tmpl_is_null(mod->rhs)) ||
953 tmpl_is_data(mod->rhs));
954
955 /*
956 * map_list_mod_debug()
957 */
958 if (RDEBUG_ENABLED2) {
959 vb = tmpl_value(mod->rhs);
960
961 map_list_mod_debug(request, map, mod, vb->type != FR_TYPE_NULL ? vb : NULL);
962 }
963 }
964 mod = map_list_head(&vlm->mod); /* Reset */
965
966 /*
967 * All this has been checked by #map_to_list_mod
968 */
969 context = request;
970 if (!fr_cond_assert(mod && tmpl_request_ptr(&context, tmpl_request(mod->lhs)) == 0)) return -1;
971
972 vp_list = tmpl_list_head(context, tmpl_list(mod->lhs));
973 if (!fr_cond_assert(vp_list)) return -1;
974
977
978 /*
979 * The destination is a list (which is a completely different set of operations)
980 */
981 if (tmpl_is_list(map->lhs)) {
982 switch (mod->op) {
983 case T_OP_CMP_FALSE:
984 fr_pair_list_free(vp_list); /* Clear the entire list */
985 goto finish;
986
987 case T_OP_SET:
988 {
989 fr_pair_list_t tmp_list;
990 fr_pair_list_init(&tmp_list);
991 fr_pair_list_free(vp_list); /* Clear the existing list */
992 map_list_mod_to_vps(parent, &tmp_list, vlm); /* Replace with a new list */
993 fr_pair_list_append(vp_list, &tmp_list);
994 goto finish;
995 }
996
997 /*
998 * Ugh... exponential... Fixme? Build a tree if number
999 * of attribute in to is > n?
1000 */
1001 case T_OP_EQ:
1002 {
1003 bool exists = false;
1004 fr_pair_list_t vp_from, vp_to_insert;
1005 fr_pair_t *vp;
1006
1007 fr_pair_list_init(&vp_from);
1008 fr_pair_list_init(&vp_to_insert);
1009 map_list_mod_to_vps(parent, &vp_from, vlm);
1010 if (fr_pair_list_empty(&vp_from)) goto finish;
1011
1012 while ((vp = fr_pair_remove(&vp_from, fr_pair_list_head(&vp_from)))) {
1013 fr_pair_list_foreach(vp_list, vp_to) {
1014 if (fr_pair_cmp_by_da(vp_to, vp) == 0) {
1015 exists = true;
1016 break;
1017 }
1018 }
1019
1020 if (exists) {
1021 talloc_free(vp); /* Don't overwrite */
1022 } else {
1023 fr_pair_append(&vp_to_insert, vp);
1024 }
1025 }
1026
1027 fr_pair_list_append(vp_list, &vp_to_insert); /* Do this last so we don't expand the 'to' set */
1028 }
1029 goto finish;
1030
1031 case T_OP_ADD_EQ:
1032 {
1033 fr_pair_list_t vp_from;
1034
1035 fr_pair_list_init(&vp_from);
1036 map_list_mod_to_vps(parent, &vp_from, vlm);
1037 fr_assert(!fr_pair_list_empty(&vp_from));
1038
1039 fr_pair_list_append(vp_list, &vp_from);
1040 }
1041 goto finish;
1042
1043 case T_OP_PREPEND:
1044 {
1045 fr_pair_list_t vp_from;
1046
1047 fr_pair_list_init(&vp_from);
1048 map_list_mod_to_vps(parent, &vp_from, vlm);
1049 fr_assert(!fr_pair_list_empty(&vp_from));
1050
1051 fr_pair_list_prepend(vp_list, &vp_from);
1052
1053 goto finish;
1054 }
1055
1056 default:
1057 rcode = -1;
1058 goto finish;
1059 }
1060 }
1061
1062 fr_assert(!map_list_next(&vlm->mod, mod));
1063
1064 /*
1065 * Find the destination attribute. We leave with either
1066 * the list and vp pointing to the attribute or the VP
1067 * being NULL (no attribute at that index).
1068 */
1069 found = tmpl_dcursor_init(NULL, request, &cc, &list, request, mod->lhs);
1070
1071 /*
1072 * The destination is an attribute
1073 */
1074 switch (mod->op) {
1075 /*
1076 * !* - Remove all attributes which match the LHS attribute.
1077 */
1078 case T_OP_CMP_FALSE:
1079 if (!found) goto finish;
1080
1081 /*
1082 * The cursor was set to the Nth one. Delete it, and only it.
1083 */
1084 if (tmpl_attr_tail_num(map->lhs) != NUM_ALL) {
1085 fr_dcursor_free_item(&list);
1086 /*
1087 * Wildcard: delete all of the matching ones
1088 */
1089 } else {
1090 fr_dcursor_free_list(&list); /* Remember, we're using a custom iterator */
1091 }
1092
1093 /*
1094 * Check that the User-Name and User-Password
1095 * caches point to the correct attribute.
1096 */
1097 goto finish;
1098
1099 /*
1100 * -= - Delete attributes in the found list which match any of the
1101 * src_list attributes.
1102 *
1103 * This operation has two modes:
1104 * - If tmpl_attr_tail_num(map->lhs) > 0, we check each of the src_list attributes against
1105 * the found attribute, to see if any of their values match.
1106 * - If tmpl_attr_tail_num(map->lhs) == NUM_UNSPEC, we compare all instances of the found attribute
1107 * against each of the src_list attributes.
1108 */
1109 case T_OP_SUB_EQ:
1110 {
1111 /* We didn't find any attributes earlier */
1112 if (!found) goto finish;
1113
1114 /*
1115 * Instance specific[n] delete
1116 *
1117 * i.e. Remove this single instance if it matches
1118 * any of these values.
1119 */
1120 if (tmpl_attr_tail_num(map->lhs) != NUM_ALL) {
1121 fr_value_box_t *vb = tmpl_value(map_list_head(&vlm->mod)->rhs);
1122
1123 if (fr_value_box_cmp(vb, &found->data) == 0) {
1124 fr_dcursor_free_item(&list);
1125 goto finish;
1126 }
1127
1128 goto finish; /* Wasn't found */
1129 }
1130
1131 /*
1132 * All instances[*] delete
1133 *
1134 * i.e. Remove any instance of this attribute which
1135 * matches any of these values.
1136 */
1137 do {
1138 fr_value_box_t *vb = tmpl_value(map_list_head(&vlm->mod)->rhs);
1139
1140 if (fr_value_box_cmp(vb, &found->data) == 0) {
1141 fr_dcursor_free_item(&list);
1142 break;
1143 }
1144 } while ((found = fr_dcursor_next(&list)));
1145 }
1146 goto finish;
1147
1148 /*
1149 * += - Add all attributes to the destination
1150 */
1151 case T_OP_ADD_EQ:
1152 do_add:
1153 {
1154 fr_pair_list_t vp_from;
1155
1156 fr_pair_list_init(&vp_from);
1157 map_list_mod_to_vps(parent, &vp_from, vlm);
1158 if (fr_pair_list_empty(&vp_from)) goto finish;
1159
1160 fr_pair_list_append(vp_list, &vp_from);
1161 }
1162 goto finish;
1163
1164 case T_OP_PREPEND:
1165 {
1166 fr_pair_list_t vp_from;
1167
1168 fr_pair_list_init(&vp_from);
1169 map_list_mod_to_vps(parent, &vp_from, vlm);
1170 fr_assert(!fr_pair_list_empty(&vp_from));
1171
1172 fr_pair_list_prepend(vp_list, &vp_from);
1173
1174 goto finish;
1175 }
1176
1177 /*
1178 * = - Set only if not already set
1179 */
1180 case T_OP_EQ:
1181 if (found) {
1182 RDEBUG3("Refusing to overwrite (use :=)");
1183 goto finish;
1184 }
1185 goto do_add;
1186
1187 /*
1188 * := - Overwrite existing attribute with last src_list attribute
1189 */
1190 case T_OP_SET:
1191 if (!found) goto do_add;
1192
1193 /*
1194 * Instance specific[n] overwrite
1195 */
1196 if (tmpl_attr_tail_num(map->lhs) != NUM_ALL) {
1197 fr_dcursor_t from;
1198 fr_pair_list_t vp_from;
1199
1200 fr_pair_list_init(&vp_from);
1201 map_list_mod_to_vps(parent, &vp_from, vlm);
1202 if (fr_pair_list_empty(&vp_from)) goto finish;
1203
1204 fr_pair_dcursor_init(&from, &vp_from);
1205
1206 fr_dcursor_merge(&list, &from); /* Merge first (insert after current attribute) */
1207 fr_dcursor_free_item(&list); /* Then free the current attribute */
1208 goto finish;
1209 }
1210
1211 /*
1212 * All instances[*] overwrite
1213 */
1214 fr_dcursor_free_list(&list); /* Remember, we're using a custom iterator */
1215 goto do_add;
1216
1217 /*
1218 * !=, ==, >=, >, <=, < - Filter operators
1219 */
1220 case T_OP_NE:
1221 case T_OP_CMP_EQ:
1222 case T_OP_GE:
1223 case T_OP_GT:
1224 case T_OP_LE:
1225 case T_OP_LT:
1226 {
1227 if (!found) goto finish;
1228
1229 /*
1230 * Instance specific[n] filter
1231 */
1232 if (tmpl_attr_tail_num(map->lhs) != NUM_ALL) {
1233 fr_value_box_t *vb = tmpl_value(mod->rhs);
1234 bool remove = true;
1235
1236 if (fr_value_box_cmp_op(mod->op, &found->data, vb) == 1) remove = false;
1237
1238 if (remove) fr_dcursor_free_item(&list);
1239 goto finish;
1240 }
1241
1242 /*
1243 * All instances[*] filter
1244 */
1245 do {
1246 fr_value_box_t *vb = tmpl_value(mod->rhs);
1247 bool remove = true;
1248
1249 if (fr_value_box_cmp_op(mod->op, &found->data, vb) == 1) remove = false;
1250
1251 if (remove) {
1252 fr_dcursor_free_item(&list);
1253 } else {
1254 fr_dcursor_next(&list);
1255 }
1256 } while ((found = fr_dcursor_current(&list)));
1257 }
1258 goto finish;
1259
1260 default:
1261 fr_assert(0); /* Should have been caught be the caller */
1262 rcode = -1;
1263 goto finish;
1264 }
1265
1266finish:
1267 tmpl_dcursor_clear(&cc);
1268 return rcode;
1269}
int n
Definition acutest.h:577
static int context
Definition radmin.c:71
#define RCSID(id)
Definition build.h:483
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:824
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:1350
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:860
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:922
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:802
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:819
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:1345
fr_pair_t * fr_pair_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
Dynamically allocate a new attribute and assign a fr_dict_attr_t.
Definition pair.c:283
void fr_pair_list_init(fr_pair_list_t *list)
Initialise a pair list header.
Definition pair.c:46
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:3572
int8_t fr_pair_cmp_by_da(void const *a, void const *b)
Order attributes by their da, and tag.
Definition pair.c:1844
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:896
#define tmpl_is_xlat(vpt)
Definition tmpl.h:215
#define tmpl_value(_tmpl)
Definition tmpl.h:948
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:400
#define tmpl_is_exec(vpt)
Definition tmpl.h:216
#define tmpl_xlat(_tmpl)
Definition tmpl.h:941
static fr_dict_attr_t const * tmpl_list(tmpl_t const *vpt)
Definition tmpl.h:915
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:846
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:931
#define tmpl_is_data(vpt)
Definition tmpl.h:211
#define tmpl_value_type(_tmpl)
Definition tmpl.h:950
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:344
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:812
#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:926
Optional arguments passed to vp_tmpl functions.
Definition tmpl.h:341
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:307
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:422
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:851
ssize_t fr_value_box_from_str(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, char const *in, size_t inlen, fr_sbuff_unescape_rules_t const *erules, bool tainted)
Definition value.c:5315
int fr_value_box_cast(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, fr_value_box_t const *src)
Convert one type of fr_value_box_t to another.
Definition value.c:3352
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:3740
int fr_value_box_cmp_op(fr_token_t op, fr_value_box_t const *a, fr_value_box_t const *b)
Compare two attributes using an operator.
Definition value.c:929
fr_sbuff_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:5777
@ FR_VALUE_BOX_LIST_FREE
Definition value.h:221
static fr_slen_t data
Definition value.h:1265
#define fr_box_strvalue_len(_val, _len)
Definition value.h:286
#define fr_value_box_alloc_null(_ctx)
Allocate a value box for later use with a value assignment function.
Definition value.h:632
static size_t char ** out
Definition value.h:997