The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
pair_legacy.c
Go to the documentation of this file.
1/*
2 * This library is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2.1 of the License, or (at your option) any later version.
6 *
7 * This library 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 GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with this library; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/** AVP manipulation and search API
18 *
19 * @file src/lib/util/pair_legacy.c
20 *
21 * @copyright 2000,2006,2015 The FreeRADIUS server project
22 */
23
24RCSID("$Id: 3eed1c66694e5b606879dae0ad4618bb347d058f $")
25
26#include <freeradius-devel/util/dict.h>
27#include <freeradius-devel/util/pair.h>
28#include <freeradius-devel/util/pair_legacy.h>
29#include <freeradius-devel/util/proto.h>
30#include <freeradius-devel/util/regex.h>
31
32#include <freeradius-devel/protocol/radius/rfc2865.h>
33#include <freeradius-devel/protocol/freeradius/freeradius.internal.h>
34
37 L("\t"),
38 L("\n"),
39 L(" "),
40 L("!*"),
41 L("!="),
42 L("!~"),
43 L("&&"), /* Logical operator */
44 L(")"), /* Close condition/sub-condition */
45 L("+="),
46 L("-="),
47 L(":="),
48 L("<"),
49 L("<="),
50 L("=*"),
51 L("=="),
52 L("=~"),
53 L(">"),
54 L(">="),
55 L("||"), /* Logical operator */
56 );
57
59 { L("+="), T_OP_ADD_EQ },
60 { L(":="), T_OP_EQ },
61 { L("="), T_OP_EQ },
62};
64
66 { L("!*"), T_OP_CMP_FALSE },
67 { L("!="), T_OP_NE },
68 { L("!~"), T_OP_REG_NE },
69 { L("+="), T_OP_ADD_EQ },
70 { L(":="), T_OP_SET },
71 { L("<"), T_OP_LT },
72 { L("<="), T_OP_LE },
73 { L("="), T_OP_EQ },
74 { L("=*"), T_OP_CMP_TRUE },
75 { L("=="), T_OP_CMP_EQ },
76 { L("=~"), T_OP_REG_EQ },
77 { L(">"), T_OP_GT },
78 { L(">="), T_OP_GE }
79};
81
82/*
83 * Stop parsing bare words at whitespace, comma, or end of list.
84 *
85 * Note that we don't allow escaping of bare words here, as that screws up parsing of raw attributes with
86 * 0x... prefixes.
87 */
88static fr_sbuff_parse_rules_t const bareword_unquoted = {
89 .terminals = &FR_SBUFF_TERMS(
90 L(""),
91 L("\t"),
92 L("\n"),
93 L("\r"),
94 L(" "),
95 L(","),
96 L("}")
97 )
98};
99
100
102{
103 char quote;
104 ssize_t slen;
105 fr_sbuff_parse_rules_t const *rules;
106
107 if (fr_sbuff_next_if_char(in, '"')) {
109 quote = '"';
110
111 } else if (fr_sbuff_next_if_char(in, '\'')) {
113 quote = '\'';
114
115 /*
116 * We don't support backticks here.
117 */
118 } else if (fr_sbuff_is_char(in, '\'')) {
119 fr_strerror_const("Backticks are not supported here");
120 return 0;
121
122 } else {
123 rules = &bareword_unquoted;
124 quote = '\0';
125 }
126
127 slen = fr_value_box_from_substr(vp, &vp->data, vp->da->type, vp->da, in, rules);
128 if (slen < 0) {
129 fr_assert(slen >= -((ssize_t) 1 << 20));
130 return slen - (quote != 0);
131 }
132
133 if (quote && !fr_sbuff_next_if_char(in, quote)) {
134 fr_strerror_const("Unterminated string");
135 return 0;
136 }
137
138 fr_assert(slen <= ((ssize_t) 1 << 20));
139
140 return slen + ((quote != 0) << 1);
141}
142
143/** Parse a #fr_pair_list_t from a substring
144 *
145 * @param[in] root where we start parsing from
146 * @param[in,out] relative where we left off, or where we should continue from
147 * @param[in] in input sbuff
148 * @return
149 * - <0 on error
150 * - 0 on no input
151 * - >0 on how many bytes of input we read
152 *
153 */
155 fr_sbuff_t *in)
156{
157 int i, components;
158 bool raw;
159 bool was_relative = false;
160 bool append;
161 bool keep_going;
162 fr_type_t raw_type;
163 fr_token_t op;
164 fr_slen_t slen;
165 fr_pair_t *vp;
166 fr_dict_attr_t const *internal = NULL;
167 fr_sbuff_marker_t lhs_m, rhs_m;
168 fr_sbuff_t our_in = FR_SBUFF(in);
169
170 if (unlikely(!root->ctx)) {
171 fr_strerror_const("Missing ctx fr_pair_parse_t");
172 return -1;
173 }
174
175 if (unlikely(!root->da)) {
176 fr_strerror_const("Missing namespace attribute");
177 return -1;
178 }
179
180 if (unlikely(!root->list)) {
181 fr_strerror_const("Missing list");
182 return -1;
183 }
184
186 if (internal == root->da) internal = NULL;
187
188 if (fr_sbuff_remaining(&our_in) == 0) return 0;
189
190redo:
191 append = true;
192 raw = false;
193 raw_type = FR_TYPE_NULL;
194 relative->last_char = 0;
195 vp = NULL;
196
197 fr_sbuff_adv_past_whitespace(&our_in, SIZE_MAX, NULL);
198
199 /*
200 * Relative attributes start from the input list / parent.
201 *
202 * Absolute attributes start from the root list / parent.
203 *
204 * Once we decide where we are coming from, all subsequent operations are on the "relative"
205 * structure.
206 */
207 if (!fr_sbuff_next_if_char(&our_in, '.')) {
208 *relative = *root;
209
210 append = !was_relative;
211 was_relative = false;
212
213 /*
214 * Be nice to people who expect to see '&' everywhere.
215 */
216 (void) fr_sbuff_next_if_char(&our_in, '&');
217
218 /*
219 * Raw attributes can only be at our root.
220 *
221 * "raw.foo" means that SOME component of the OID is raw. But the starting bits might be known.
222 */
223 if (fr_sbuff_is_str_literal(&our_in, "raw.")) {
224 raw = true;
225 fr_sbuff_advance(&our_in, 4);
226 }
227 } else if (!relative->ctx || !relative->da || !relative->list) {
228 fr_strerror_const("The '.Attribute' syntax can only be used if the previous attribute is structural, and the line ends with ','");
229 return -1;
230 } else {
231 was_relative = true;
232 }
233
234 /*
235 * Set the LHS marker to be after any initial '.'
236 */
237 fr_sbuff_marker(&lhs_m, &our_in);
238
239 /*
240 * Skip over the attribute name. We need to get the operator _before_ creating the VPs.
241 */
242 components = 0;
243 do {
244 if (fr_sbuff_adv_past_allowed(&our_in, SIZE_MAX, fr_dict_attr_allowed_chars, NULL) == 0) break;
245 components++;
246 } while (fr_sbuff_next_if_char(&our_in, '.'));
247
248 /*
249 * Couldn't find anything.
250 */
251 if (!components) {
252 fr_strerror_const("Empty input");
253 return 0;
254 }
255
256 fr_sbuff_adv_past_whitespace(&our_in, SIZE_MAX, NULL);
257
258 /*
259 * Look for the operator.
260 */
261 if (relative->allow_compare) {
263 } else {
265 }
266 if (op == T_INVALID) {
267 fr_strerror_const("Expecting operator");
268 return fr_sbuff_error(&our_in);
269 }
270
271 /*
272 * Skip past whitespace, and set a marker at the RHS. Then reset the input to the LHS attribute
273 * name, so that we can go back and parse / create the attributes.
274 */
275 fr_sbuff_adv_past_whitespace(&our_in, SIZE_MAX, NULL);
276
277 fr_sbuff_marker(&rhs_m, &our_in);
278
279 /*
280 * If the value of the attribute is 0x..., then we always force the raw type to be octets, even
281 * if the attribute is named and known. e.g. raw.Framed-IP-Address = 0x01
282 *
283 * OR if the attribute is entirely unknown (and not a raw version of a known one), then we allow a
284 * cast to set the data type.
285 */
286 if (raw) {
287 if (fr_sbuff_is_str_literal(&our_in, "0x")) {
288 raw_type = FR_TYPE_OCTETS;
289
290 } else if (fr_sbuff_next_if_char(&our_in, '(')) {
292
293 fr_sbuff_marker(&m, &our_in);
294
295 fr_sbuff_out_by_longest_prefix(&slen, &raw_type, fr_type_table, &our_in, FR_TYPE_NULL);
296 if ((raw_type == FR_TYPE_NULL) || !fr_type_is_leaf(raw_type)) {
297 fr_sbuff_set(&our_in, &rhs_m);
298 fr_strerror_const("Invalid data type in cast");
299 return fr_sbuff_error(&our_in);
300 }
301
302 if (!fr_sbuff_next_if_char(&our_in, ')')) {
303 fr_strerror_const("Missing ')' in cast");
304 return fr_sbuff_error(&our_in);
305 }
306
307 fr_sbuff_adv_past_whitespace(&our_in, SIZE_MAX, NULL);
308 fr_sbuff_marker(&rhs_m, &our_in);
309
310 } else if (fr_sbuff_is_char(&our_in, '{')) {
311 raw_type = FR_TYPE_TLV;
312 }
313 }
314
315 fr_sbuff_set(&our_in, &lhs_m);
316
317 /*
318 * Parse each OID component, creating pairs along the way.
319 */
320 i = 1;
321 do {
323 fr_dict_attr_t const *da = NULL;
324 fr_dict_attr_t const *da_unknown = NULL;
325
326 slen = fr_dict_oid_component(&err, &da, relative->da, &our_in, &bareword_terminals);
327 if (err == FR_DICT_ATTR_NOTFOUND) {
328 if (raw) {
329 /*
330 * We looked up raw.FOO, and FOO wasn't found. The component must be a number.
331 */
332 if (!fr_sbuff_is_digit(&our_in)) goto notfound;
333
334 if (raw_type == FR_TYPE_NULL) {
335 raw_type = FR_TYPE_OCTETS;
336
337 } else if (raw_type == FR_TYPE_TLV) {
338 /*
339 * Reset the type based on the parent.
340 */
341 if (relative->da->type == FR_TYPE_VSA) {
342 raw_type = FR_TYPE_VENDOR;
343 }
344 }
345
346 slen = fr_dict_attr_unknown_afrom_oid_substr(root->ctx, &da_unknown, relative->da, &our_in, raw_type);
347 if (slen < 0) return fr_sbuff_error(&our_in) + slen;
348
349 fr_assert(da_unknown);
350
351 /*
352 * Append from the root list, starting at the root depth.
353 */
354 vp = fr_pair_afrom_da_depth_nested(root->ctx, root->list, da_unknown,
355 root->da->depth);
356 fr_dict_attr_unknown_free(&da_unknown);
357
358 if (!vp) return fr_sbuff_error(&our_in);
359
361
362 /*
363 * The above function MAY have jumped ahead a few levels. Ensure
364 * that the relative structure is set correctly for the parent,
365 * but only if the parent changed.
366 */
367 if (relative->da != vp->da->parent) {
368 fr_pair_t *parent_vp;
369
370 parent_vp = fr_pair_parent(vp);
371 fr_assert(parent_vp);
372
373 relative->ctx = parent_vp;
374 relative->da = parent_vp->da;
375 relative->list = &parent_vp->vp_group;
376 }
377
378 /*
379 * Update the new relative information for the current VP, which
380 * may be structural, or a key field.
381 */
382 fr_assert(!fr_sbuff_is_char(&our_in, '.')); /* be sure the loop exits */
383 goto update_relative;
384 }
385
386 if (internal) {
387 slen = fr_dict_oid_component(&err, &da, internal, &our_in, &bareword_terminals);
388 }
389
390 } else if (raw && fr_type_is_structural(da->type) && (raw_type != FR_TYPE_OCTETS)) {
391 /*
392 * We were asked to do a "raw" thing, but we found a known attribute matching
393 * that description.
394 *
395 * @todo - this is only allowed because we can't distinguish between "raw.1" and
396 * "raw.User-Name".
397 */
398 raw = false;
399 raw_type = FR_TYPE_NULL;
400 }
401
402 if (err != FR_DICT_ATTR_OK) {
403 notfound:
404 fr_sbuff_marker(&rhs_m, &our_in);
406
407 fr_strerror_printf("Unknown attribute \"%.*s\" for parent \"%s\"",
408 (int) fr_sbuff_diff(&our_in, &rhs_m), fr_sbuff_current(&rhs_m),
409 relative->da->name);
410 return fr_sbuff_error(&our_in);
411 }
412 fr_assert(da != NULL);
413
414#if 0
415 /*
416 * @todo - If we're at the root, then aliases can cause us to jump over intermediate
417 * attributes. In which case we have to create the intermediate attributes, too.
418 */
419 if (relative->da) {
420 if (relative->da->flags.is_root) {
421 fr_assert(da->depth == 1);
422 }
423 }
424#endif
425
426 /*
427 * Intermediate components are always found / created. The final component is
428 * always appended, no matter the operator.
429 */
430 if (i < components) {
431 if (append) {
432 vp = fr_pair_find_last_by_da(relative->list, NULL, da);
433 if (!vp) {
434 if (fr_pair_append_by_da(relative->ctx, &vp, relative->list, da) < 0) {
435 return fr_sbuff_error(&our_in);
436 }
437 }
438 } else {
439 vp = fr_pair_afrom_da(relative->ctx, da);
440 if (!vp) return fr_sbuff_error(&our_in);
441
442 fr_pair_append(relative->list, vp);
443 }
444
445 /*
446 * We had a raw type and we're passing
447 * raw octets to it. We don't care if
448 * its structural or anything else. Just
449 * create the raw attribute.
450 */
451 } else if (raw_type != FR_TYPE_NULL) {
452 /*
453 * We have parsed the full OID tree, *and* found a known attribute. e.g. raw.Vendor-Specific = ...
454 *
455 * For some reason, we allow: raw.Vendor-Specific = { ... }
456 *
457 * But this is what we really want: raw.Vendor-Specific = 0xabcdef
458 */
459 fr_assert(!da_unknown);
460
461 if ((raw_type != FR_TYPE_OCTETS) && (raw_type != da->type)) {
462 fr_strerror_printf("Cannot create raw attribute %s which changes data type from %s to %s",
463 da->name, fr_type_to_str(da->type), fr_type_to_str(raw_type));
464 return fr_sbuff_error(&our_in);
465 }
466
467 /*
468 * If we're parsing raw octets, create a raw octets attribute.
469 *
470 * Otherwise create one of type 'tlv', and then parse the children.
471 */
472 if (raw_type == FR_TYPE_OCTETS) {
473 da_unknown = fr_dict_attr_unknown_raw_afrom_da(root->ctx, da);
474 } else {
475 da_unknown = fr_dict_attr_unknown_afrom_da(root->ctx, da);
476 }
477 if (!da_unknown) return fr_sbuff_error(&our_in);
478
479 if (fr_pair_append_by_da(relative->ctx, &vp, relative->list, da_unknown) < 0) {
480 fr_dict_attr_unknown_free(&da_unknown);
481 return fr_sbuff_error(&our_in);
482 }
483
484 fr_dict_attr_unknown_free(&da_unknown);
485
486 /*
487 * Just create the leaf attribute.
488 */
489 } else if (da->parent->type == FR_TYPE_STRUCT) {
490 fr_pair_t *tail = fr_pair_list_tail(relative->list);
491
492 /*
493 * If the structure member is _less_ than the last one, go create a new structure
494 * in the grandparent.
495 */
496 if (tail && (tail->da->attr >= da->attr) && !da->flags.array) {
497 fr_pair_t *parent_vp, *grand_vp;
498
499 parent_vp = fr_pair_list_parent(relative->list);
500 if (!parent_vp) goto leaf;
501
502 fr_assert(da->parent == parent_vp->da);
503
504 grand_vp = fr_pair_parent(parent_vp);
505 if (!grand_vp) goto leaf;
506
507 /*
508 * Create a new parent in the context of the grandparent.
509 */
510 if (fr_pair_append_by_da(grand_vp, &vp, &grand_vp->vp_group, parent_vp->da) < 0) {
511 return fr_sbuff_error(&our_in);
512 }
513
514 relative->ctx = vp;
515 fr_assert(relative->da == vp->da);
516 relative->list = &vp->vp_group;
517 }
518
519 goto leaf;
520 } else {
521 leaf:
522 if (fr_pair_append_by_da(relative->ctx, &vp, relative->list, da) < 0) {
523 return fr_sbuff_error(&our_in);
524 }
525 }
526
527 fr_assert(vp != NULL);
528
529 update_relative:
530 /*
531 * Reset the parsing to the new namespace if necessary.
532 */
533 switch (vp->vp_type) {
535 relative->ctx = vp;
536 relative->da = vp->da;
537 relative->list = &vp->vp_group;
538 break;
539
540 /*
541 * Groups reset the namespace to the da referenced by the group.
542 *
543 * Internal groups get their namespace to the root namespace.
544 */
545 case FR_TYPE_GROUP:
546 relative->ctx = vp;
547 relative->da = fr_dict_attr_ref(vp->da);
548 if (relative->da == internal) {
549 relative->da = fr_dict_root(root->da->dict);
550 }
551 fr_assert(relative->da != NULL);
552 relative->list = &vp->vp_group;
553 break;
554
555 case FR_TYPE_UINT8:
556 case FR_TYPE_UINT16:
557 case FR_TYPE_UINT32:
558 /*
559 * Key fields have children in their namespace, but for OLD style, the children
560 * go into the parents context and list.
561 */
562 if (fr_dict_attr_is_key_field(vp->da) && !vp->da->flags.migration_union_key) {
563 fr_pair_t *parent_vp;
564
565 parent_vp = fr_pair_parent(vp);
566 fr_assert(parent_vp);
567
568 relative->ctx = parent_vp;
569 relative->da = vp->da;
570 relative->list = &parent_vp->vp_group;
571 break;
572 }
574
575 default:
576 break;
577
578 case FR_TYPE_INTERNAL:
579 fr_strerror_printf("Cannot parse internal data type %s", fr_type_to_str(vp->vp_type));
580 return fr_sbuff_error(&our_in);
581 }
582
583 i++;
584 } while (fr_sbuff_next_if_char(&our_in, '.'));
585
586 if (relative->allow_compare) {
587 vp->op = op;
588 } else {
589 vp->op = T_OP_EQ;
590 }
591
592 /*
593 * Reset the parser to the RHS so that we can parse the value.
594 */
595 fr_sbuff_set(&our_in, &rhs_m);
596
597 /*
598 * The RHS is a list, go parse the nested attributes.
599 */
600 if (fr_sbuff_next_if_char(&our_in, '{')) {
603 };
604
605 if (!fr_type_is_structural(vp->vp_type)) {
606 fr_strerror_printf("Cannot assign list to leaf data type %s for attribute %s",
607 fr_type_to_str(vp->vp_type), vp->da->name);
608 return fr_sbuff_error(&our_in);
609 }
610
611 while (true) {
612 fr_sbuff_adv_past_whitespace(&our_in, SIZE_MAX, NULL);
613
614 if (fr_sbuff_is_char(&our_in, '}')) {
615 break;
616 }
617
618 slen = fr_pair_list_afrom_substr(relative, &child, &our_in);
619 if (!slen) break;
620
621 if (slen < 0) return fr_sbuff_error(&our_in) + slen;
622 }
623
624 if (!fr_sbuff_next_if_char(&our_in, '}')) {
625 fr_strerror_const("Failed to end list with '}'");
626 return fr_sbuff_error(&our_in);
627 }
628
629 goto done;
630 }
631
632 if (fr_type_is_structural(vp->vp_type)) {
633 fr_strerror_printf("Group list for %s MUST start with '{'", vp->da->name);
634 return fr_sbuff_error(&our_in);
635 }
636
637 slen = fr_pair_value_from_substr(vp, &our_in, relative->tainted);
638 if (slen <= 0) return fr_sbuff_error(&our_in) + slen;
639
640done:
642
643 keep_going = false;
644 if (fr_sbuff_next_if_char(&our_in, ',')) {
645 keep_going = true;
646 relative->last_char = ',';
647 }
648
649 if (relative->allow_crlf) {
650 size_t len;
651
652 len = fr_sbuff_adv_past_allowed(&our_in, SIZE_MAX, sbuff_char_line_endings, NULL);
653 if (len) {
654 keep_going |= true;
655 if (!relative->last_char) relative->last_char = '\n';
656 }
657 }
658
659 keep_going &= ((fr_sbuff_remaining(&our_in) > 0) || (fr_sbuff_extend(&our_in) > 0));
660
661 if (keep_going) goto redo;
662
663 FR_SBUFF_SET_RETURN(in, &our_in);
664}
665
666/** Read valuepairs from the fp up to End-Of-File.
667 *
668 * @param[in] ctx for talloc
669 * @param[in] dict to resolve attributes in.
670 * @param[in,out] out where the parsed fr_pair_ts will be appended.
671 * @param[in] fp to read valuepairs from.
672 * @param[out] pfiledone true if file parsing complete;
673 * @return
674 * - 0 on success
675 * - -1 on error
676 */
677int fr_pair_list_afrom_file(TALLOC_CTX *ctx, fr_dict_t const *dict, fr_pair_list_t *out, FILE *fp, bool *pfiledone)
678{
679 fr_pair_list_t tmp_list;
680 fr_pair_parse_t root, relative;
681 bool found = false;
682 char buf[8192];
683
684 /*
685 * Read all of the attributes on the current line.
686 *
687 * If we get nothing but an EOL, it's likely OK.
688 */
689 fr_pair_list_init(&tmp_list);
690
691 root = (fr_pair_parse_t) {
692 .ctx = ctx,
693 .da = fr_dict_root(dict),
694 .list = &tmp_list,
695 .allow_crlf = true,
696 .allow_compare = true,
697 };
698 relative = (fr_pair_parse_t) { };
699
700 while (fgets(buf, sizeof(buf), fp) != NULL) {
701 /*
702 * If we get a '\n' by itself, we assume that's
703 * the end of that VP list.
704 */
705 if ((buf[0] == '\n') || (buf[0] == '\r')) {
706 if (found) {
707 *pfiledone = false;
708 break;
709 }
710 continue;
711 }
712
713 /*
714 * Comments get ignored
715 */
716 if (buf[0] == '#') continue;
717
718 /*
719 * Leave "relative" between calls, so that we can do:
720 *
721 * foo = {}
722 * .bar = baz
723 *
724 * and get
725 *
726 * foo = { bar = baz }
727 */
728 if (fr_pair_list_afrom_substr(&root, &relative, &FR_SBUFF_IN_STR(buf)) < 0) {
729 *pfiledone = false;
730 fr_pair_list_free(&tmp_list);
731 return -1;
732 }
733
734 found = true;
735 }
736
737 fr_pair_list_append(out, &tmp_list);
738
739 *pfiledone = true;
740 return 0;
741}
742
743
744/** Move pairs from source list to destination list respecting operator
745 *
746 * @note This function does some additional magic that's probably not needed in most places. Consider using
747 * radius_legacy_map_cmp() and radius_legacy_map_apply() instead.
748 *
749 * @note fr_pair_list_free should be called on the head of the source list to free
750 * unmoved attributes (if they're no longer needed).
751 *
752 * @param[in,out] to destination list.
753 * @param[in,out] from source list.
754 * @param[in] op operator for list move.
755 */
757{
758 fr_pair_t *vp, *next, *found;
759 fr_pair_list_t head_append, head_prepend;
760
761 if (!to || fr_pair_list_empty(from)) return;
762
763 /*
764 * We're editing the "to" list while we're adding new
765 * attributes to it. We don't want the new attributes to
766 * be edited, so we create an intermediate list to hold
767 * them during the editing process.
768 */
769 fr_pair_list_init(&head_append);
770
771 /*
772 * Any attributes that are requested to be prepended
773 * are added to a temporary list here
774 */
775 fr_pair_list_init(&head_prepend);
776
777 /*
778 * We're looping over the "from" list, moving some
779 * attributes out, but leaving others in place.
780 */
781 for (vp = fr_pair_list_head(from); vp != NULL; vp = next) {
783 next = fr_pair_list_next(from, vp);
784
785 /*
786 * We never move Fall-Through.
787 */
788 if (fr_dict_attr_is_top_level(vp->da) && (vp->da->attr == FR_FALL_THROUGH) &&
790 continue;
791 }
792
793 /*
794 * Unlike previous versions, we treat all other
795 * attributes as normal. i.e. there's no special
796 * treatment for passwords or Hint.
797 */
798
799 switch (vp->op) {
800 /*
801 * Anything else are operators which
802 * shouldn't occur. We ignore them, and
803 * leave them in place.
804 */
805 default:
806 continue;
807
808 /*
809 * Add it to the "to" list, but only if
810 * it doesn't already exist.
811 */
812 case T_OP_EQ:
813 found = fr_pair_find_by_da(to, NULL, vp->da);
814 if (!found) goto do_add;
815 continue;
816
817 /*
818 * Add it to the "to" list, and delete any attribute
819 * of the same vendor/attr which already exists.
820 */
821 case T_OP_SET:
822 found = fr_pair_find_by_da(to, NULL, vp->da);
823 if (!found) goto do_add;
824
825 /*
826 * Delete *all* matching attributes.
827 */
828 fr_pair_delete_by_da(to, found->da);
829 goto do_add;
830
831 /*
832 * Move it from the old list and add it
833 * to the new list.
834 */
835 case T_OP_ADD_EQ:
836 do_add:
837 fr_pair_remove(from, vp);
838 fr_pair_append(&head_append, vp);
839 continue;
840
841 case T_OP_PREPEND:
842 fr_pair_remove(from, vp);
843 fr_pair_prepend(&head_prepend, vp);
844 continue;
845 }
846 } /* loop over the "from" list. */
847
848 /*
849 * If the op parameter was prepend, add the "new list
850 * attributes first as those whose individual operator
851 * is prepend should be prepended to the resulting list
852 */
853 if (op == T_OP_PREPEND) fr_pair_list_prepend(to, &head_append);
854
855 /*
856 * If there are any items in the prepend list prepend
857 * it to the "to" list
858 */
859 fr_pair_list_prepend(to, &head_prepend);
860
861 /*
862 * If the op parameter was not prepend, take the "new"
863 * list, and append it to the "to" list.
864 */
865 if (op != T_OP_PREPEND) fr_pair_list_append(to, &head_append);
866
867 fr_pair_list_free(from);
868}
#define RCSID(id)
Definition build.h:485
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:209
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition build.h:324
#define unlikely(_x)
Definition build.h:383
#define UNUSED
Definition build.h:317
#define NUM_ELEMENTS(_t)
Definition build.h:339
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:2702
static fr_slen_t err
Definition dict.h:861
bool const fr_dict_attr_allowed_chars[UINT8_MAX+1]
Characters allowed in a single dictionary attribute name.
Definition dict_util.c:64
fr_dict_attr_t * fr_dict_attr_unknown_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da))
Copy a known or unknown attribute to produce an unknown attribute with the specified name.
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition dict_util.c:2496
fr_dict_attr_t * fr_dict_attr_unknown_raw_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da))
Initialise an octets type attribute from a da.
void fr_dict_attr_unknown_free(fr_dict_attr_t const **da)
Free dynamically allocated (unknown attributes)
fr_dict_t const * fr_dict_internal(void)
Definition dict_util.c:4749
fr_slen_t fr_dict_attr_unknown_afrom_oid_substr(TALLOC_CTX *ctx, fr_dict_attr_t const **out, fr_dict_attr_t const *parent, fr_sbuff_t *in, fr_type_t type))
Create a fr_dict_attr_t from an ASCII attribute and value.
static bool fr_dict_attr_is_top_level(fr_dict_attr_t const *da)
Return true if this attribute is parented directly off the dictionary root.
Definition dict.h:788
fr_dict_attr_err_t
Errors returned by attribute lookup functions.
Definition dict.h:309
@ FR_DICT_ATTR_OK
No error.
Definition dict.h:310
@ FR_DICT_ATTR_NOTFOUND
Attribute couldn't be found.
Definition dict.h:311
fr_slen_t fr_dict_oid_component(fr_dict_attr_err_t *err, fr_dict_attr_t const **out, fr_dict_attr_t const *parent, fr_sbuff_t *in, fr_sbuff_term_t const *tt))
Parse an OID component, resolving it to a defined attribute.
Definition dict_util.c:2325
#define fr_dict_attr_is_key_field(_da)
Definition dict.h:169
static fr_slen_t in
Definition dict.h:861
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:185
fr_type_t
@ FR_TYPE_TLV
Contains nested attributes.
@ FR_TYPE_NULL
Invalid (uninitialised) attribute type.
@ FR_TYPE_UINT16
16 Bit unsigned integer.
@ FR_TYPE_UINT8
8 Bit unsigned integer.
@ FR_TYPE_UINT32
32 Bit unsigned integer.
@ FR_TYPE_STRUCT
like TLV, but without T or L, and fixed-width children
@ FR_TYPE_VENDOR
Attribute that represents a vendor in the attribute tree.
@ FR_TYPE_VSA
Vendor-Specific, for RADIUS attribute 26.
@ FR_TYPE_OCTETS
Raw octets.
@ FR_TYPE_GROUP
A grouping of other attributes.
long int ssize_t
ssize_t fr_slen_t
fr_pair_t * fr_pair_list_parent(fr_pair_list_t const *list)
Return a pointer to the parent pair which contains this list.
Definition pair.c:963
int fr_pair_append_by_da(TALLOC_CTX *ctx, fr_pair_t **out, fr_pair_list_t *list, fr_dict_attr_t const *da)
Alloc a new fr_pair_t (and append)
Definition pair.c:1464
fr_pair_t * fr_pair_find_by_da(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find the first pair with a matching da.
Definition pair.c:700
int fr_pair_append(fr_pair_list_t *list, fr_pair_t *to_add)
Add a VP to the end of the list.
Definition pair.c:1345
int fr_pair_delete_by_da(fr_pair_list_t *list, fr_dict_attr_t const *da)
Delete matching pairs from the specified list.
Definition pair.c:1687
fr_pair_t * fr_pair_parent(fr_pair_t const *vp)
Return a pointer to the parent pair.
Definition pair.c:949
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:287
void fr_pair_list_init(fr_pair_list_t *list)
Initialise a pair list header.
Definition pair.c:46
int fr_pair_prepend(fr_pair_list_t *list, fr_pair_t *to_add)
Add a VP to the start of the list.
Definition pair.c:1314
fr_pair_t * fr_pair_afrom_da_depth_nested(TALLOC_CTX *ctx, fr_pair_list_t *list, fr_dict_attr_t const *da, int start)
Create a pair (and all intermediate parents), and append it to the list.
Definition pair.c:412
fr_pair_t * fr_pair_find_last_by_da(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find the last pair with a matching da.
Definition pair.c:724
static fr_sbuff_parse_rules_t const bareword_unquoted
Definition pair_legacy.c:88
static fr_table_num_sorted_t const pair_assignment_op_table[]
Definition pair_legacy.c:58
fr_slen_t fr_pair_list_afrom_substr(fr_pair_parse_t const *root, fr_pair_parse_t *relative, fr_sbuff_t *in)
Parse a fr_pair_list_t from a substring.
int fr_pair_list_afrom_file(TALLOC_CTX *ctx, fr_dict_t const *dict, fr_pair_list_t *out, FILE *fp, bool *pfiledone)
Read valuepairs from the fp up to End-Of-File.
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.
static size_t pair_comparison_op_table_len
Definition pair_legacy.c:80
static ssize_t fr_pair_value_from_substr(fr_pair_t *vp, fr_sbuff_t *in, UNUSED bool tainted)
static fr_table_num_sorted_t const pair_comparison_op_table[]
Definition pair_legacy.c:65
static fr_sbuff_term_t const bareword_terminals
Definition pair_legacy.c:35
static ssize_t pair_assignment_op_table_len
Definition pair_legacy.c:63
struct fr_pair_parse_s fr_pair_parse_t
fr_pair_list_t * list
list where output is placed
Definition pair_legacy.h:45
TALLOC_CTX * ctx
Definition pair_legacy.h:43
bool allow_compare
allow comparison operators
Definition pair_legacy.h:46
fr_dict_attr_t const * da
root da to start parsing from
Definition pair_legacy.h:44
bool tainted
source is tainted
Definition pair_legacy.h:48
bool allow_crlf
allow CRLF, and treat like comma
Definition pair_legacy.h:47
char last_char
last character we read - ',', ' ', or 0 for EOF
Definition pair_legacy.h:49
#define fr_assert(_expr)
Definition rad_assert.h:38
static bool done
Definition radclient.c:81
size_t fr_sbuff_adv_past_allowed(fr_sbuff_t *sbuff, size_t len, bool const allowed[static UINT8_MAX+1], fr_sbuff_term_t const *tt)
Wind position past characters in the allowed set.
Definition sbuff.c:1805
bool const sbuff_char_line_endings[UINT8_MAX+1]
Definition sbuff.c:107
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:2116
#define fr_sbuff_out_by_longest_prefix(_match_len, _out, _table, _sbuff, _def)
#define fr_sbuff_is_str_literal(_sbuff, _str)
#define fr_sbuff_set(_dst, _src)
#define fr_sbuff_diff(_a, _b)
#define fr_sbuff_adv_past_whitespace(_sbuff, _len, _tt)
#define fr_sbuff_current(_sbuff_or_marker)
#define FR_SBUFF_TERMS(...)
Initialise a terminal structure with a list of sorted strings.
Definition sbuff.h:192
#define fr_sbuff_extend(_sbuff_or_marker)
#define fr_sbuff_is_char(_sbuff_or_marker, _c)
#define FR_SBUFF_SET_RETURN(_dst, _src)
#define fr_sbuff_is_digit(_sbuff_or_marker)
#define FR_SBUFF_IN_STR(_start)
#define fr_sbuff_error(_sbuff_or_marker)
#define FR_SBUFF(_sbuff_or_marker)
#define fr_sbuff_advance(_sbuff_or_marker, _len)
#define fr_sbuff_remaining(_sbuff_or_marker)
Set of terminal elements.
fr_pair_t * vp
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
An element in a lexicographically sorted array of name to num mappings.
Definition table.h:49
enum fr_token fr_token_t
@ T_INVALID
Definition token.h:39
@ T_OP_CMP_TRUE
Definition token.h:104
@ T_OP_EQ
Definition token.h:83
@ T_OP_SET
Definition token.h:84
@ T_OP_NE
Definition token.h:97
@ T_OP_ADD_EQ
Definition token.h:69
@ T_OP_CMP_FALSE
Definition token.h:105
@ T_OP_REG_EQ
Definition token.h:102
@ T_OP_CMP_EQ
Definition token.h:106
@ T_OP_LE
Definition token.h:100
@ T_OP_GE
Definition token.h:98
@ T_OP_GT
Definition token.h:99
@ T_OP_LT
Definition token.h:101
@ T_OP_REG_NE
Definition token.h:103
@ T_OP_PREPEND
Definition token.h:85
bool fr_pair_list_empty(fr_pair_list_t const *list)
Is a valuepair list empty.
#define PAIR_VERIFY(_x)
Definition pair.h:193
fr_pair_t * fr_pair_list_next(fr_pair_list_t const *list, fr_pair_t const *item))
Get the next item in a valuepair list after a specific entry.
Definition pair_inline.c:69
fr_pair_t * fr_pair_list_tail(fr_pair_list_t const *list)
Get the tail of a valuepair list.
Definition pair_inline.c:55
fr_pair_t * fr_pair_remove(fr_pair_list_t *list, fr_pair_t *vp)
Remove fr_pair_t from a list without freeing.
Definition pair_inline.c:93
void fr_pair_list_free(fr_pair_list_t *list)
Free memory used by a valuepair list.
void fr_pair_list_append(fr_pair_list_t *dst, fr_pair_list_t *src)
Appends a list of fr_pair_t from a temporary list to a destination list.
void fr_pair_list_prepend(fr_pair_list_t *dst, fr_pair_list_t *src)
Move a list of fr_pair_t from a temporary list to the head of a destination list.
fr_pair_t * fr_pair_list_head(fr_pair_list_t const *list)
Get the head of a valuepair list.
Definition pair_inline.c:42
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
#define fr_strerror_const(_msg)
Definition strerror.h:223
fr_table_num_ordered_t const fr_type_table[]
Map data types to names representing those types.
Definition types.c:31
#define FR_TYPE_STRUCTURAL_EXCEPT_GROUP
Definition types.h:316
#define fr_type_is_structural(_x)
Definition types.h:393
#define FR_TYPE_INTERNAL
Definition types.h:320
#define fr_type_is_leaf(_x)
Definition types.h:394
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
Definition types.h:455
fr_sbuff_parse_rules_t const value_parse_rules_single_quoted
Definition value.c:559
fr_sbuff_parse_rules_t const value_parse_rules_double_quoted
Definition value.c:553
ssize_t fr_value_box_from_substr(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *rules)
Convert string value to a fr_value_box_t type.
Definition value.c:5110
static size_t char ** out
Definition value.h:1023