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