The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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 
29 RCSID("$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 
45 static 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 
53 static 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  */
75 static 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) {
92  talloc_free(n);
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  */
116 static inline vp_list_mod_t *list_mod_delete_afrom_map(TALLOC_CTX *ctx,
117  map_t const *original, map_t const *mutated)
118 {
119  vp_list_mod_t *n;
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  */
156 static inline vp_list_mod_t *list_mod_empty_string_afrom_map(TALLOC_CTX *ctx,
157  map_t const *original, map_t const *mutated)
158 {
159  vp_list_mod_t *n;
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  */
205 static 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  */
251 int 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) &&
364  tmpl_attr_tail_da_is_structural(mutated->lhs) && tmpl_attr_tail_da_is_structural(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);
511  fr_dcursor_free_list(&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);
521  fr_dcursor_free_list(&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);
733  fr_dcursor_free_item(&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);
740  fr_dcursor_free_item(&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);
748  fr_dcursor_free_item(&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 
781 finish:
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 
796 error:
797  talloc_free(tmp_ctx);
798  talloc_free(n); /* Frees all mod maps too */
799  return -1;
800 }
801 
802 static 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 
806  MEM(vp = fr_pair_afrom_da(ctx, tmpl_attr_tail_da(attr)));
807  if (fr_value_box_copy(vp, &vp->data, value) < 0) {
808  talloc_free(vp);
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  */
819 static 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  */
860 static 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  */
922 int map_list_mod_apply(request_t *request, vp_list_mod_t const *vlm)
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 
951  fr_assert(tmpl_is_attr(mod->lhs));
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 
976  fr_assert(parent);
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 
1266 finish:
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:444
static void * fr_dcursor_remove(fr_dcursor_t *cursor)
Remove the current item.
Definition: dcursor.h:479
static int fr_dcursor_append(fr_dcursor_t *cursor, void *v)
Insert a single item at the end of the list.
Definition: dcursor.h:405
static void fr_dcursor_merge(fr_dcursor_t *cursor, fr_dcursor_t *to_append)
Moves items from one cursor to another.
Definition: dcursor.h:519
#define fr_dcursor_init(_cursor, _head)
Initialise a cursor.
Definition: dcursor.h:728
static void fr_dcursor_free_item(fr_dcursor_t *cursor)
talloc_free the current item
Definition: dcursor.h:801
static void fr_dcursor_free_list(fr_dcursor_t *cursor)
Free the current item and all items after it.
Definition: dcursor.h:659
static void * fr_dcursor_next(fr_dcursor_t *cursor)
Advanced the cursor to the next item.
Definition: dcursor.h:287
static void * fr_dcursor_head(fr_dcursor_t *cursor)
Rewind cursor to the start of the list.
Definition: dcursor.h:233
static void * fr_dcursor_current(fr_dcursor_t *cursor)
Return the item the cursor current points to.
Definition: dcursor.h:336
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition: debug.h:137
static fr_slen_t err
Definition: dict.h:645
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:1351
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 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
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
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_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 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 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 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
static map_t * map_alloc(TALLOC_CTX *ctx)
Definition: map_async.c:53
fr_type_t
Definition: merged_model.c:80
@ FR_TYPE_STRING
String of printable characters.
Definition: merged_model.c:83
@ FR_TYPE_NULL
Invalid (uninitialised) attribute type.
Definition: merged_model.c:81
long int ssize_t
Definition: merged_model.c:24
unsigned char uint8_t
Definition: merged_model.c:30
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:278
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:1340
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:3579
int8_t fr_pair_cmp_by_da(void const *a, void const *b)
Order attributes by their da, and tag.
Definition: pair.c:1841
char * fr_asprintf(TALLOC_CTX *ctx, char const *fmt,...)
Special version of asprintf which implements custom format specifiers.
Definition: print.c:876
#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:880
#define tmpl_is_xlat(vpt)
Definition: tmpl.h:215
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:114
#define tmpl_value(_tmpl)
Definition: tmpl.h:932
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:910
static fr_dict_attr_t const * tmpl_attr_tail_da(tmpl_t const *vpt)
Return the last attribute reference da.
Definition: tmpl.h:796
#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:925
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:830
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:74
static bool tmpl_is_list(tmpl_t const *vpt)
Definition: tmpl.h:915
#define tmpl_is_data(vpt)
Definition: tmpl.h:211
#define tmpl_value_type(_tmpl)
Definition: tmpl.h:934
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:167
#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.
#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 fr_dict_attr_t const * tmpl_list(tmpl_t const *vpt)
Definition: tmpl.h:899
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.
Optional arguments passed to vp_tmpl functions.
Definition: tmpl.h:341
fr_assert(0)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
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:253
char * talloc_typed_strdup(TALLOC_CTX *ctx, char const *p)
Call talloc_strdup, setting the type on the new chunk correctly.
Definition: talloc.c:333
void tmpl_dcursor_clear(tmpl_dcursor_ctx_t *cc)
Clear any temporary state allocations.
Definition: tmpl_dcursor.c:419
#define tmpl_dcursor_init(_err, _ctx, _cc, _cursor, _request, _vpt)
Definition: tmpl_dcursor.h:99
Maintains state between cursor calls.
Definition: tmpl_dcursor.h:61
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:408
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
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
bool fr_pair_list_empty(fr_pair_list_t const *list)
Is a valuepair list empty.
Definition: pair_inline.c:125
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
#define PAIR_VERIFY(_x)
Definition: pair.h:190
#define fr_pair_list_foreach(_list_head, _iter)
Iterate over the contents of a fr_pair_list_t.
Definition: pair.h:260
void fr_pair_list_free(fr_pair_list_t *list)
Free memory used by a valuepair list.
Definition: pair_inline.c:113
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.
Definition: pair_inline.c:182
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.
Definition: pair_inline.c:195
#define fr_pair_dcursor_init(_cursor, _list)
Initialises a special dcursor with callbacks that will maintain the attr sublists correctly.
Definition: pair.h:590
static fr_slen_t parent
Definition: pair.h:844
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:5264
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:3301
int8_t fr_value_box_cmp(fr_value_box_t const *a, fr_value_box_t const *b)
Compare two values.
Definition: value.c:640
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:3689
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:884
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:5725
@ FR_VALUE_BOX_LIST_FREE
Definition: value.h:214
#define fr_box_strvalue_len(_val, _len)
Definition: value.h:279
#define fr_value_box_alloc_null(_ctx)
Allocate a value box for later use with a value assignment function.
Definition: value.h:619
static size_t char ** out
Definition: value.h:984