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: a4f6db8b9fe66a0c5816aa821e8e5d8573bd18ed $")
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_SET },
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/** Our version of a DA stack.
144 *
145 * @todo - add in whether or not we added / created the vp? maybe an edit list?
146 * and then we can clean up the unknown DAs, simply by talloc freeing the edit list.
147 */
148typedef struct {
149 int depth;
150 fr_dict_attr_t const *da[FR_DICT_MAX_TLV_STACK]; //!< parent for parsing
151 fr_pair_t *vp[FR_DICT_MAX_TLV_STACK]; //!< which VP we have created or found
153
154/** Parse a #fr_pair_list_t from a substring
155 *
156 * Syntax: ([raw.]|.)<name>[<.name>] op [(cast)] value...
157 *
158 * A "raw" prefix creates a raw attribute, which allows us to encode raw data which might be invalid for
159 * the given data type. Or if a "(cast)" is given, the value is parsed as the specified data type. Note
160 * that casts can only be to a "leaf" data type, and not to a structural type such as "tlv", "group",
161 * "struct", etc. The "(cast)" syntax can only be used for "raw" attributes, and not for attributes
162 * which are known. The "name" can be either a known attribute, or a numerical OID. Either way, the
163 * final attribute which is created is marked as "raw" or "unknown", and is encoded via the "raw" rules,
164 * and not as the known data type.
165 *
166 * If the first name begins with ".", then it is a _relative_ name. The attribute is created in the
167 * context of the most recently created "structural" data type.
168 *
169 * TBD - we have to determine what the heck that means...
170 *
171 * The "name" can be one or more names from the input dictionary. The names must be known, as numerical
172 * OIDs can only be used when the "raw" prefix is used.
173 *
174 * If there are multiple names (e.g. "foo.bar.baz"), then only the last name can be a "leaf" data
175 * type. All of the intermediate names must be "structural" data types.
176 *
177 * Depending on the input arguments, the operator can be a comparison operator (==, <=, etc.). Or, else
178 * it can be an assignment operator (=, +=). The "=" operator is used to assign, and the "+=" operator
179 * is used to append. No other assignment operators are permitted. Note that "+=" cannot be used with
180 * relative names (i.e. where the name begins with ".")
181 *
182 * The "value" can either be a "leaf" data type (e.g. number, IP address, etc.) or for "structural" data
183 * types it can be a sub-list. A sub-list is a set of attribute assignments which are surrounded by
184 * curly brackets "{...}". When a sub-list is specified, the contents must be either children of the
185 * parent attribute (for "tlv", "struct"), or children referenced by a "group", or internal attributes.
186 *
187 * If an intermediate "name" is an ALIAS, then the attributes are created / used as if all intermediate
188 * names were specified. i.e. ALIAS is a short-cut for names (think "soft link), but it does not change
189 * the hierarchy for normal attributes.
190 *
191 *
192 * Examples
193 * --------
194 *
195 * Name = value
196 * Leaf attributes.
197 * The value MUST be parsed as the leaf data type.
198 *
199 * Name = { children }
200 * Structural attributes.
201 * The children MUST be children of the parent.
202 * OR the children can be from the "internal" dictionary.
203 * OR for type 'group', children of the group reference (usually the dictionary root)
204 *
205 * raw.Name = 0xabcdef
206 * Raw attributes.
207 * The value MUST be a hex string.
208 *
209 * raw.Name = { children }
210 *
211 * @param[in] root where we start parsing from
212 * @param[in,out] relative where we left off, or where we should continue from
213 * @param[in] in input sbuff
214 * @return
215 * - <0 on error
216 * - 0 on no input
217 * - >0 on how many bytes of input we read
218 */
220 fr_sbuff_t *in)
221{
222 int i, components;
223 bool raw, was_unknown;
224 bool was_relative = false;
225 bool append;
226 bool keep_going;
227 fr_type_t raw_type;
228 fr_token_t op;
229 fr_slen_t slen;
230 fr_pair_t *vp;
232 fr_sbuff_marker_t lhs_m, op_m, rhs_m;
233 fr_sbuff_t our_in = FR_SBUFF(in);
234 legacy_da_stack_t da_stack = {};
235
236 if (unlikely(!root->ctx)) {
237 fr_strerror_const("Missing input context (fr_pair_parse_t)");
238 return -1;
239 }
240
241 if (unlikely(!root->da)) {
242 fr_strerror_const("Missing namespace attribute");
243 return -1;
244 }
245
246 if (unlikely(!root->list)) {
247 fr_strerror_const("Missing list");
248 return -1;
249 }
250
251 fr_sbuff_adv_past_blank(&our_in, SIZE_MAX, NULL);
252
253 if (fr_sbuff_remaining(&our_in) == 0) return 0;
254
255 /*
256 * Boot strap the relative references from the root.
257 *
258 * The comparison operations are only used for internal tests, and should not be used by
259 * administrators. So we disallow them, unless the destination list is empty. This check
260 * prevents them from being used in administrative policies.
261 */
262 if (!relative->da) {
263 if (root->allow_compare && !fr_pair_list_empty(root->list)) {
264 fr_strerror_const("Attribute comparisons can only be used when the destination list is empty");
265 return -1;
266 }
267
268 *relative = *root;
269 }
270
271#define CLEAN_DA_STACK do { if (was_unknown) { \
272 for (i = 1; i < da_stack.depth; i++) { \
273 fr_dict_attr_unknown_free(&da_stack.da[i]); \
274 } } } while (0)
275
276
277redo:
278 raw = false;
279 raw_type = FR_TYPE_NULL;
280 relative->last_char = 0;
281 was_unknown = false;
282 vp = NULL;
283
284 fr_sbuff_adv_past_blank(&our_in, SIZE_MAX, NULL);
285
286 /*
287 * STEP 1: Figure out if we have relative or absolute attributes.
288 *
289 * Absolute attributes start from the root list / parent.
290 * Or, when there is no previous relative setting.
291 *
292 * Relative attributes start from the input list / parent.
293 *
294 * Once we decide where we start parsing from, all subsequent operations are on the "relative"
295 * structure.
296 */
297 if (!fr_sbuff_next_if_char(&our_in, '.')) {
298 *relative = *root;
299
300 append = !was_relative;
301 was_relative = false;
302
303 /*
304 * Be nice to people who expect to use '&' everywhere.
305 */
306 (void) fr_sbuff_next_if_char(&our_in, '&');
307
308 /*
309 * Raw attributes can only be at our root.
310 *
311 * "raw.foo" means that SOME component of the OID is raw. But the starting bits might be known.
312 *
313 * Raw attributes cannot be created in the internal namespace. But an internal group can
314 * contain raw protocol attributes.
315 */
316 if (fr_sbuff_is_str_literal(&our_in, "raw.")) {
317 fr_sbuff_advance(&our_in, 4);
318 goto is_raw;
319 }
320
321 } else if (relative->da->flags.is_root) {
322 fr_strerror_const("The '.Attribute' syntax cannot be used at the root of a dictionary");
323
324 error:
326 return fr_sbuff_error(&our_in);
327
328 } else if (relative->da->type == FR_TYPE_GROUP) {
329 fr_strerror_printf("The '.Attribute' syntax cannot be used with parent %s of data type 'group'",
330 relative->da->name);
331 goto error;
332
333 } else {
334 fr_assert(relative->ctx);
335 fr_assert(relative->list);
336
337 was_relative = true;
338 append = true;
339 }
340
341 /*
342 * If the input root is an unknown attribute, then forbid internal ones, and force everything
343 * else to be raw, too.
344 */
345 if (relative->da->flags.is_unknown) {
346 is_raw:
347 raw = true;
348 }
349
350 /*
351 * Raw internal attributes don't make sense. An internal group can contain raw protocol
352 * attributes, but the group is not raw.
353 */
354 if (raw && relative->da->flags.internal) {
355 fr_strerror_const("Cannot create internal attributes which are 'raw'");
356 goto error;
357 }
358
359 /*
360 * Set the LHS marker to be after any initial '.'
361 */
362 fr_sbuff_marker(&lhs_m, &our_in);
363
364 /*
365 * STEP 2: Find and check the operator.
366 *
367 * Skip over the attribute name. We need to get the operator _before_ creating the VPs.
368 */
369 components = 0;
370 do {
371 if (fr_sbuff_adv_past_allowed(&our_in, SIZE_MAX, fr_dict_attr_allowed_chars, NULL) == 0) break;
372 components++;
373 } while (fr_sbuff_next_if_char(&our_in, '.'));
374
375 /*
376 * Couldn't find anything.
377 */
378 if (!components) goto done;
379
380 fr_sbuff_marker(&op_m, &our_in);
381 fr_sbuff_adv_past_blank(&our_in, SIZE_MAX, NULL);
382
383 /*
384 * Look for the operator.
385 */
386 if (relative->allow_compare) {
388 if (op == T_INVALID) {
389 fr_strerror_const("Expecting operator");
390 goto error;
391 }
392
393 } else {
394 /*
395 * @todo - handle different operators ala v3?
396 * What is the difference between ":=" and "="? Perhaps nothing?
397 */
399 if (op == T_INVALID) {
400 fr_strerror_const("Expecting operator");
401 goto error;
402 }
403
404 /*
405 * += means "append"
406 * := menas "don't append".
407 */
408 if (op != T_OP_EQ) {
409 if (was_relative) {
410 fr_strerror_printf("The '.Attribute' syntax cannot be used along with the '%s' operator",
411 fr_tokens[op]);
412 goto error;
413 }
414 }
415
416 if (op == T_OP_ADD_EQ) {
417 append = true;
418 }
419
420 if (op == T_OP_SET) {
421 append = false;
422 }
423
424 op = T_OP_EQ;
425 }
426
427 /*
428 * Check the character after the operator. This check is only necessary to produce better error
429 * messages. i.e. We allow "=", but the user enters "==".
430 */
431 {
432 uint8_t c = fr_sbuff_char(&our_in, '\0');
433 static const bool invalid[UINT8_MAX + 1] = {
434 ['!'] = true, ['#'] = true, ['$'] = true, ['*'] = true,
435 ['+'] = true, ['-'] = true, ['/'] = true, ['<'] = true,
436 ['='] = true, ['>'] = true, ['?'] = true, ['|'] = true,
437 ['~'] = true,
438 };
439
440 if (c && invalid[c]) {
441 fr_strerror_printf("Invalid character '%c' after operator '%s'",
442 (char) c, fr_tokens[op]);
443 goto error;
444 }
445 }
446
447 /*
448 * Skip past whitespace, and set a marker at the RHS value. We do a quick peek at the value, to
449 * set the data type of the RHS. This allows us to parse raw TLVs.
450 */
451 fr_sbuff_adv_past_blank(&our_in, SIZE_MAX, NULL);
452
453 /*
454 * STEP 3: Try to guess the data type for "raw" attributes.
455 *
456 * If the attribute is raw, and the value of the attribute is 0x..., then we always force the raw
457 * type to be octets, even if the attribute is named and known. e.g. raw.Framed-IP-Address =
458 * 0x01.
459 *
460 * OR if the attribute is entirely unknown (and not a raw version of a known one), then we allow a
461 * cast which sets the data type.
462 */
463 if (raw) {
464 if (fr_sbuff_is_str_literal(&our_in, "0x")) {
465 raw_type = FR_TYPE_OCTETS;
466
467 } else if (fr_sbuff_next_if_char(&our_in, '(')) {
468 fr_sbuff_marker(&rhs_m, &our_in);
469
470 fr_sbuff_out_by_longest_prefix(&slen, &raw_type, fr_type_table, &our_in, FR_TYPE_NULL);
471
472 /*
473 * The input has to be a real (non-NULL) leaf. The input shouldn't be cast to a
474 * TLV. Instead, the value should just start with '{'.
475 */
476 if (!fr_type_is_leaf(raw_type)) {
477 fr_sbuff_set(&our_in, &rhs_m);
478 fr_strerror_const("Invalid data type in cast");
479 goto error;
480 }
481
482 if (!fr_sbuff_next_if_char(&our_in, ')')) {
483 fr_strerror_const("Missing ')' in cast");
484 goto error;
485 }
486
487 fr_sbuff_adv_past_blank(&our_in, SIZE_MAX, NULL);
488
489 } else if (fr_sbuff_is_char(&our_in, '{')) {
490 /*
491 * Raw attributes default to data type TLV.
492 */
493 raw_type = FR_TYPE_TLV;
494 append = false;
495 }
496 }
497
498 fr_sbuff_marker(&rhs_m, &our_in);
499
500 fr_sbuff_set(&our_in, &lhs_m);
501
502 /*
503 * That we know the data type, parse each OID component. We build the DA stack from top to bottom.
504 *
505 * 0 is our relative root. 1..N are the DAs that we find or create.
506 */
507 da_stack = (legacy_da_stack_t) {
508 .da = {
509 [0] = relative->da,
510 },
511 .depth = 1,
512 };
513
514 /*
515 * STEP 4: Re-parse the attributes, building up the da_stack of #fr_dict_attr_t that we will be
516 * using as parents.
517 */
518 for (i = 1; i <= components; i++, da_stack.depth++) {
520 fr_dict_attr_t const *da = NULL;
521 fr_dict_attr_t const *da_unknown = NULL;
522 fr_dict_attr_t const *parent;
523 fr_dict_attr_t const *ref;
524 fr_type_t unknown_type;
525
526 if (da_stack.depth >= FR_DICT_MAX_TLV_STACK) {
527 fr_strerror_printf("Attributes are nested too deeply at \"%.*s\"",
528 (int) fr_sbuff_diff(&op_m, &lhs_m), fr_sbuff_current(&lhs_m));
529 goto error;
530 }
531
532 fr_sbuff_marker(&lhs_m, &our_in);
533
534 /*
535 * The fr_pair_t parent might be a group, in which case the fr_dict_attr_t parent will be
536 * different.
537 */
538 parent = da_stack.da[da_stack.depth - 1];
539 if (parent->type == FR_TYPE_GROUP) {
541 fr_assert(parent != NULL);
542 }
543
544 /*
545 * Once we parse a completely unknown attribute, all of the rest of them have to be
546 * unknown, too. We cannot allow unknown TLVs to contain internal attributes, for
547 * example.
548 */
549 if (was_unknown) {
550 goto alloc_unknown;
551 }
552
553 /*
554 * Look up the name (or number). If it's found, life is easy. Otherwise, we jump
555 * through a bunch of hoops to see if we are changing dictionaries, or creating a raw OID
556 * from a number, etc.
557 */
558 slen = fr_dict_oid_component(&err, &da, parent, &our_in, &bareword_terminals);
559 if (err != FR_DICT_ATTR_OK) {
560 /*
561 * We were looking in the internal dictionary. Maybe this attribute is instead
562 * in the protocol dictionary?
563 */
564 if ((relative->da->dict == relative->internal) && relative->dict) {
565 fr_assert(relative->dict != relative->internal);
566
567 /*
568 * Internal groups can be used to cache protocol data. Internal
569 * structural attributes cannot.
570 *
571 * @todo - this restriction makes sense, but maybe people want to do that
572 * anyways?
573 */
574 if (relative->da->type != FR_TYPE_GROUP) {
575 fr_strerror_printf("Internal attribute '%s' of data type '%s' cannot contain protocol attributes",
576 relative->da->name, fr_type_to_str(relative->da->type));
577 goto error;
578 }
579
580 slen = fr_dict_oid_component(&err, &da, fr_dict_root(relative->dict), &our_in, &bareword_terminals);
581 if (err == FR_DICT_ATTR_OK) {
582 ref = fr_dict_root(relative->dict);
583 goto found;
584 }
585 }
586
587 /*
588 * Try to parse the name from the internal namespace first, as this is the most
589 * likely case. Plus, if we parse the OIDs second, the errors for unknown
590 * attributes mention the protocol dictionary, and not the internal one.
591 *
592 * Raw attributes also cannot be created in the internal dictionary space.
593 */
594 if (!raw && relative->internal) {
595 /*
596 * If the current dictionary isn't internal, then look up the attribute
597 * in the internal dictionary.
598 *
599 * Buf if the current dictionary is internal, AND the internal type is
600 * GROUP, AND we we have a protocol dictionary, then allow an internal
601 * group to contain protocol attributes.
602 */
603 if (parent->dict != relative->internal) {
604 ref = fr_dict_root(relative->internal);
605
606 } else if ((da_stack.da[da_stack.depth - 1]->type == FR_TYPE_GROUP) && (root->da->dict != root->internal)) {
607 ref = fr_dict_root(root->da->dict);
608
609 } else {
610 /*
611 * Otherwise we are already in the internal dictionary, and the
612 * attribute was not found. So don't search for it again in the
613 * internal dictionary. And because we're in the internal
614 * dictionary, we don't allow raw attributes.
615 */
616 goto notfound;
617 }
618
619 slen = fr_dict_oid_component(&err, &da, ref, &our_in, &bareword_terminals);
620 if (err == FR_DICT_ATTR_OK) {
621 goto found;
622 }
623
624 goto notfound;
625 }
626
627 /*
628 * We didn't find anything, that's an error.
629 */
630 if (!raw) {
631 notfound:
632 fr_strerror_printf("Unknown attribute \"%.*s\" for parent \"%s\"",
633 (int) fr_sbuff_diff(&op_m, &our_in), fr_sbuff_current(&our_in),
634 da_stack.da[da_stack.depth - 1]->name);
635 goto error;
636 }
637
638 alloc_unknown:
639 /*
640 * We looked up raw.FOO, and FOO wasn't found. See if we can still parse it.
641 */
642 if (da_stack.da[da_stack.depth - 1]->type == FR_TYPE_GROUP) {
643 fr_strerror_printf("Cannot create 'raw' children in attribute %s of data type 'group'",
644 da_stack.da[da_stack.depth - 1]->name);
645 goto error;
646 }
647
648 /*
649 * Unknown attributes must be 'raw.1234'.
650 */
651 if (!fr_sbuff_is_digit(&our_in)) {
652 goto notfound;
653 }
654
655 /*
656 * Figure out the data type for unknown attributes. Intermediate attributes are
657 * structural. Only the final attribute is forced to "raw_type".
658 */
659 if (i < components) {
660 if (parent->type == FR_TYPE_VSA) {
661 unknown_type = FR_TYPE_VENDOR;
662 } else {
663 unknown_type = FR_TYPE_TLV;
664 }
665
666 } else if (raw_type == FR_TYPE_NULL) {
667 unknown_type = FR_TYPE_OCTETS;
668
669 } else if ((raw_type == FR_TYPE_TLV) && (parent->type == FR_TYPE_VSA)) {
670 /*
671 * We had previously parsed a known VSA, but this component is
672 * perhaps a numerical OID. Set the data type to VENDOR, so that
673 * the hierachy is correct.
674 */
675 unknown_type = FR_TYPE_VENDOR;
676
677 } else {
678 unknown_type = raw_type;
679 }
680
681 da_unknown = fr_dict_attr_unknown_afrom_oid(root->ctx, parent, &our_in, unknown_type);
682 if (!da_unknown) goto error;
683
684 da = da_unknown;
685 was_unknown = true;
686
687 goto next;
688 } /* huge block of "we didn't find a known attribute" */
689
690 /*
691 * We found the component. It MIGHT be an ALIAS which jumps down a few levels. Or, it
692 * might be a group which jumps back to the dictionary root. Or it may suddenly be an
693 * internal attribute.
694 *
695 * For an ALIAS, we need to add intermediate nodes up to the parent.
696 *
697 * For a GROUP, we need to add nodes up to the ref of the group.
698 *
699 * For internal attributes, we need to add nodes up to the root of the internal
700 * dictionary.
701 */
702 if (da->parent != parent) {
703 int j, diff;
704 fr_dict_attr_t const *up;
705
706 ref = parent;
707
708 found:
709 fr_assert(fr_dict_attr_common_parent(ref, da, true) == ref);
710
711 diff = da->depth - ref->depth;
712 fr_assert(diff >= 1);
713
714 diff--;
715
716 if ((da_stack.depth + diff) >= FR_DICT_MAX_TLV_STACK) {
717 fr_strerror_printf("Attributes are nested too deeply at \"%.*s\"",
718 (int) fr_sbuff_diff(&op_m, &lhs_m), fr_sbuff_current(&lhs_m));
719 goto error;
720 }
721
722 /*
723 * Go back up the da_stack, setting the parent.
724 */
725 up = da;
726 for (j = da_stack.depth + diff; j >= da_stack.depth; j--) {
727 da_stack.da[j] = up;
728 up = up->parent;
729 }
730
731 for (j = da_stack.depth; j <= da_stack.depth + diff; j++) {
732 fr_assert(da_stack.da[j] != NULL);
733 }
734
735 /*
736 * Record that we've added more attributes to the da_stack.
737 */
738 da_stack.depth += diff;
739 }
740
741 next:
742 /*
743 * Limit the data types that we can parse. This check is mainly to get better error
744 * messages.
745 */
746 switch (da->type) {
747 case FR_TYPE_GROUP:
748 if (raw && (raw_type != FR_TYPE_OCTETS)) {
749 fr_strerror_printf("Cannot create 'raw' attributes for data type '%s'", fr_type_to_str(da->type));
750 goto error;
751 }
752 break;
753
755 case FR_TYPE_LEAF:
756 break;
757
758 default:
759 fr_strerror_printf("Invalid data type '%s'", fr_type_to_str(da->type));
760 goto error;
761 }
762
763 /*
764 * Everything until the last component must end with a '.', because otherwise there would
765 * be no next component.
766 */
767 if (i < components) {
768 if (!fr_sbuff_next_if_char(&our_in, '.')) {
769 fr_strerror_printf("Missing '.' at \"%.*s\"",
770 (int) fr_sbuff_diff(&op_m, &lhs_m), fr_sbuff_current(&lhs_m));
771 goto error;
772 }
773
774 /*
775 * Leaf attributes cannot appear in the middle of the OID list.
776 */
777 if (fr_type_is_leaf(da->type)) {
779 fr_strerror_printf("Please remove the reference to key field '%s' from the input string",
780 da->name);
781 } else {
782 fr_strerror_printf("Leaf attribute '%s' cannot have children", da->name);
783 }
784
785 goto error;
786 }
787
788 } else if (raw && !da->flags.is_unknown) {
789 /*
790 * Only the last component can be raw. If the attribute we found isn't unknown,
791 * then create an unknown DA from the known one.
792 *
793 * We have parsed the full OID tree, *and* found a known attribute. e.g. raw.Vendor-Specific = ...
794 *
795 * For some reason, we allow: raw.Vendor-Specific = { ... }
796 *
797 * But this is what we really want: raw.Vendor-Specific = 0xabcdef
798 */
799 if ((raw_type != FR_TYPE_OCTETS) && (raw_type != da->type)) {
800 /*
801 * @todo - because it breaks a lot of the encoders.
802 */
803 fr_strerror_printf("Cannot create raw attribute %s which changes data type from %s to %s",
804 da->name, fr_type_to_str(da->type), fr_type_to_str(raw_type));
805 fr_sbuff_set(&our_in, &lhs_m);
806 goto error;
807 }
808
809 da_unknown = fr_dict_attr_unknown_alloc(root->ctx, da, raw_type);
810 if (!da_unknown) goto error;
811
812 da = da_unknown;
813 was_unknown = true;
814 }
815
816 da_stack.da[da_stack.depth] = da;
817 }
818
819 /*
820 * at least [0]=root, [1]=da, [2]=NULL
821 */
822 if (da_stack.depth <= 1) {
823 fr_strerror_const("Internal sanity check failed on depth 1");
824 return fr_sbuff_error(&our_in);
825 }
826
827 if (da_stack.depth <= components) {
828 fr_strerror_const("Internal sanity check failed on depth 2");
829 return fr_sbuff_error(&our_in);
830 }
831
832 /*
833 * STEP 5: Reset the parser to the value, and double-check if it's what we expect.
834 */
835 fr_sbuff_set(&our_in, &rhs_m);
836
837 if (fr_type_is_structural(da_stack.da[da_stack.depth - 1]->type)) {
838 if (!fr_sbuff_is_char(&our_in, '{')) {
839 fr_strerror_printf("Group list for %s MUST start with '{'", da_stack.da[da_stack.depth - 1]->name);
840 goto error;
841 }
842
843 /*
844 * The fr_pair_validate() function doesn't support operators for structural attributes,
845 * so we forbid them here.
846 */
847 if (relative->allow_compare && (op != T_OP_EQ) && (op != T_OP_CMP_EQ)) {
848 fr_strerror_printf("Structural attribute '%s' must use '=' or '==' for comparisons",
849 da_stack.da[da_stack.depth - 1]->name);
850 goto error;
851 }
852
853 /*
854 * If we have "foo = { ... }", then we just create the attribute.
855 */
856 if (components == 1) append = (op != T_OP_EQ);
857 }
858
859#if 0
860 /*
861 * STEP 5.1: Flatten the hierarchy if necessary.
862 */
863 if ((relative->da->flags.allow_flat) && (da_stack.depth > 2)) {
864 da_stack.da[1] = da_stack.da[da_stack.depth - 1];
865
866 da_stack.depth = 2;
867 }
868#endif
869
870 /*
871 * STEP 6: Use the da_stack to either find or add intermediate #fr_pair_t.
872 */
873 my = *relative;
874 for (i = 1; i < da_stack.depth; i++) {
875 fr_dict_attr_t const *da;
876
877 da = da_stack.da[i];
878
879 /*
880 * When we have a full path that contains MEMBERs of a STRUCT, we need to check ordering.
881 * The children MUST be added in order. If we see a child that is out of order, then
882 * that means we need to start a new parent STRUCT.
883 */
884 if ((da->parent->type == FR_TYPE_STRUCT) && (i > 1)) {
885 fr_assert(da_stack.da[i - 1] == da->parent);
886 fr_assert(da_stack.vp[i - 1] != NULL);
887 fr_assert(my.ctx == da_stack.vp[i - 1]);
888
889 /*
890 * @todo - cache the last previous child that we added? Or maybe the DA of the
891 * last child?
892 */
893 for (vp = fr_pair_list_tail(my.list);
894 vp != NULL;
895 vp = fr_pair_list_prev(my.list, vp)) {
896 if (!vp->da->flags.internal) break;
897 }
898
899 if (vp && (vp->da->attr > da->attr)) {
900 fr_pair_t *parent = da_stack.vp[i - 2];
901
902 if (parent) {
903 if (fr_pair_append_by_da(parent, &vp, &parent->vp_group, da->parent) < 0) {
904 goto error;
905 }
906 } else {
907 if (fr_pair_append_by_da(root->ctx, &vp, root->list, da->parent) < 0) {
908 goto error;
909 }
910 }
911
912 vp->op = T_OP_EQ;
914 my.ctx = vp;
915 my.list = &vp->vp_group;
916 }
917 }
918
919 /*
920 * Everything up to the last entry must be structural.
921 *
922 * The last entry may be structural, or else it might be a leaf.
923 */
924 if (fr_type_is_structural(da->type)) {
925 if (append) {
926 vp = fr_pair_find_last_by_da(my.list, NULL, da);
927 if (vp) goto update_relative;
928 }
929
930 if (fr_pair_append_by_da(my.ctx, &vp, my.list, da) < 0) {
931 goto error;
932 }
933
934 vp->op = T_OP_EQ;
936
937 update_relative:
938 da_stack.vp[i] = vp;
939
940 my.ctx = vp;
941 my.da = vp->da;
942 my.list = &vp->vp_group;
943 continue;
944 }
945
946 /*
947 * We're finally at the leaf attribute, which must be the last attribute.
948 */
949 fr_assert(i == (da_stack.depth - 1));
950
951 vp = fr_pair_afrom_da(my.ctx, da);
952 if (!vp) goto error;
953
955 vp->op = op;
956 da_stack.vp[i] = vp;
957 }
958
959 /*
960 * Intermediate nodes always use the operator '='. The final one uses the assigned operator.
961 */
962 fr_assert(vp != NULL);
963 fr_assert(vp->op != T_INVALID);
964
965 /*
966 * STEP 7: Parse the value, recursing if necessary.
967 *
968 * @todo - do all kinds of cleanups if anything fails. TBH, this really needs the edit lists,
969 * and that might be a bit much overhead for this code.
970 */
971 if (fr_type_is_structural(vp->da->type)) {
974 .dict = root->dict,
975 .internal = root->internal,
976 };
977
978 if (!fr_sbuff_next_if_char(&our_in, '{')) {
979 fr_strerror_printf("Child list for %s MUST start with '{'", vp->da->name);
980 goto error;
981 }
982
983 fr_assert(my.ctx == vp);
984 my.allow_compare = root->allow_compare;
985 my.end_of_list = true;
986
987 while (true) {
988 fr_sbuff_adv_past_blank(&our_in, SIZE_MAX, NULL);
989
990 if (fr_sbuff_is_char(&our_in, '}')) {
991 break;
992 }
993
994 slen = fr_pair_list_afrom_substr(&my, &child, &our_in);
995 if (!slen) break;
996
997 if (slen < 0) goto error;
998 }
999
1000 if (!fr_sbuff_next_if_char(&our_in, '}')) {
1001 fr_strerror_const("Failed to end list with '}'");
1002 goto error;
1003 }
1004
1005 /*
1006 * This structure was the last thing we parsed. The next thing starts from here.
1007 */
1008 *relative = my;
1009
1010 } else {
1011 slen = fr_pair_value_from_substr(vp, &our_in, relative->tainted);
1012 if (slen <= 0) goto error;
1013
1014 fr_pair_append(my.list, vp);
1015 }
1016
1017 PAIR_VERIFY(vp);
1018
1020
1021 fr_sbuff_adv_past_blank(&our_in, SIZE_MAX, NULL);
1022
1023 /*
1024 * STEP 8: See if we're done, or if we need to stop parsing this #fr_pair_t.
1025 *
1026 * Allow a limited set of characters after a value.
1027 *
1028 * It can be "," OR "CRLF" OR ",CRLF". But not anything else.
1029 */
1030 keep_going = false;
1031 if (fr_sbuff_next_if_char(&our_in, ',')) {
1032 fr_sbuff_adv_past_blank(&our_in, SIZE_MAX, NULL);
1033
1034 keep_going = true;
1035 relative->last_char = ',';
1036 }
1037
1038 /*
1039 * We hit the end of the parent list. There's no need to update "relative", we just return, and
1040 * let the caller end the list.
1041 *
1042 * Note that we allow trailing commas: Foo = { Bar = Baz, }
1043 *
1044 * We don't care about any trailing data.
1045 */
1046 if (relative->end_of_list && fr_sbuff_is_char(&our_in, '}')) {
1047 relative->last_char = '\0';
1048 goto done;
1049 }
1050
1051 if (relative->allow_crlf) {
1052 size_t len;
1053
1054 len = fr_sbuff_adv_past_allowed(&our_in, SIZE_MAX, sbuff_char_line_endings, NULL);
1055 if (len) {
1056 keep_going = true;
1057 if (!relative->last_char) relative->last_char = '\n';
1058 }
1059 }
1060
1061 /*
1062 * This is mainly for the detail file reader. We allow zeros as end of "attr op value". But we
1063 * also treat zeros as "don't keep going".
1064 */
1065 if (relative->allow_zeros) {
1066 while (fr_sbuff_next_if_char(&our_in, '\0')) {
1067 /* nothing */
1068 }
1069
1070 goto done;
1071 }
1072
1073 /*
1074 * There's no more input, we're done. Any next attributes will cause the input to be parsed from
1075 * the root again.
1076 */
1077 (void) fr_sbuff_extend(&our_in);
1078 if (!fr_sbuff_remaining(&our_in)) goto done;
1079
1080 /*
1081 * STEP 9: If we need to keep going, then set up the relative references based on what we've
1082 * done, and go back to start over again.
1083 *
1084 * The caller is responsible for checking whether or not we have too much data.
1085 */
1086 if (keep_going) {
1087 /*
1088 * Update the relative list for parsing the next pair.
1089 */
1090 if (fr_type_is_leaf(vp->da->type)) {
1092
1094 if (!parent) {
1095 *relative = *root;
1096
1097 } else {
1098 relative->ctx = parent;
1099 relative->da = parent->da;
1100 relative->list = &parent->vp_group;
1101 }
1102
1103 } else {
1104 relative->ctx = vp;
1105 relative->da = vp->da;
1106 relative->list = &vp->vp_group;
1107 }
1108
1109 goto redo;
1110 }
1111
1112 /*
1113 * STEP 10: Complain if we have unexpected input.
1114 *
1115 * We have more input, BUT we didn't have a comma or CRLF to explicitly finish the last pair we
1116 * read. That's a problem.
1117 */
1118 if (!relative->last_char) {
1119 size_t remaining;
1120
1121 remaining = fr_sbuff_remaining(&our_in);
1122
1123 if (remaining > 20) remaining = 20;
1124
1125 fr_strerror_printf("Unexpected text '%.*s ...' after value",
1126 (int) remaining, fr_sbuff_current(&our_in));
1127 return fr_sbuff_error(&our_in); /* da_stack has already been cleaned */
1128 }
1129
1130done:
1131 /*
1132 * STEP 11: Finally done.
1133 */
1134 FR_SBUFF_SET_RETURN(in, &our_in);
1135}
1136
1137/** Read valuepairs from the fp up to End-Of-File.
1138 *
1139 * @param[in] ctx for talloc
1140 * @param[in] dict to resolve attributes in.
1141 * @param[in,out] out where the parsed fr_pair_ts will be appended.
1142 * @param[in] fp to read valuepairs from.
1143 * @param[out] pfiledone true if file parsing complete;
1144 * @return
1145 * - 0 on success
1146 * - -1 on error
1147 */
1148int fr_pair_list_afrom_file(TALLOC_CTX *ctx, fr_dict_t const *dict, fr_pair_list_t *out, FILE *fp, bool *pfiledone)
1149{
1150 fr_pair_list_t tmp_list;
1151 fr_pair_parse_t root, relative;
1152 bool found = false;
1153 char buf[8192];
1154
1155 /*
1156 * Read all of the attributes on the current line.
1157 *
1158 * If we get nothing but an EOL, it's likely OK.
1159 */
1160 fr_pair_list_init(&tmp_list);
1161
1162 root = (fr_pair_parse_t) {
1163 .ctx = ctx,
1164 .da = fr_dict_root(dict),
1165 .list = &tmp_list,
1166 .dict = dict,
1167 .internal = fr_dict_internal(),
1168 .allow_crlf = true,
1169 .allow_compare = true,
1170 };
1171 relative = (fr_pair_parse_t) { };
1172
1173 while (fgets(buf, sizeof(buf), fp) != NULL) {
1174 /*
1175 * If we get a '\n' by itself, we assume that's
1176 * the end of that VP list.
1177 */
1178 if ((buf[0] == '\n') || (buf[0] == '\r')) {
1179 if (found) {
1180 *pfiledone = false;
1181 break;
1182 }
1183 continue;
1184 }
1185
1186 /*
1187 * Comments get ignored
1188 */
1189 if (buf[0] == '#') continue;
1190
1191 /*
1192 * Leave "relative" between calls, so that we can do:
1193 *
1194 * foo = {}
1195 * .bar = baz
1196 *
1197 * and get
1198 *
1199 * foo = { bar = baz }
1200 */
1201 if (fr_pair_list_afrom_substr(&root, &relative, &FR_SBUFF_IN_STR(buf)) < 0) {
1202 *pfiledone = false;
1203 fr_pair_list_free(&tmp_list);
1204 return -1;
1205 }
1206
1207 found = true;
1208 }
1209
1210#ifdef WITH_VERIFY_PTR
1211 fr_pair_list_verify(__FILE__, __LINE__, ctx, &tmp_list, true);
1212#endif
1213
1214 fr_pair_list_append(out, &tmp_list);
1215
1216 *pfiledone = true;
1217 return 0;
1218}
1219
1220
1221/** Move pairs from source list to destination list respecting operator
1222 *
1223 * @note This function does some additional magic that's probably not needed in most places. Consider using
1224 * radius_legacy_map_cmp() and radius_legacy_map_apply() instead.
1225 *
1226 * @note fr_pair_list_free should be called on the head of the source list to free
1227 * unmoved attributes (if they're no longer needed).
1228 *
1229 * @param[in,out] to destination list.
1230 * @param[in,out] from source list.
1231 * @param[in] op operator for list move.
1232 */
1234{
1235 fr_pair_t *vp, *next, *found;
1236 fr_pair_list_t head_append, head_prepend;
1237
1238 if (!to || fr_pair_list_empty(from)) return;
1239
1240 /*
1241 * We're editing the "to" list while we're adding new
1242 * attributes to it. We don't want the new attributes to
1243 * be edited, so we create an intermediate list to hold
1244 * them during the editing process.
1245 */
1246 fr_pair_list_init(&head_append);
1247
1248 /*
1249 * Any attributes that are requested to be prepended
1250 * are added to a temporary list here
1251 */
1252 fr_pair_list_init(&head_prepend);
1253
1254 /*
1255 * We're looping over the "from" list, moving some
1256 * attributes out, but leaving others in place.
1257 */
1258 for (vp = fr_pair_list_head(from); vp != NULL; vp = next) {
1259 PAIR_VERIFY(vp);
1260 next = fr_pair_list_next(from, vp);
1261
1262 /*
1263 * We never move Fall-Through.
1264 */
1265 if (fr_dict_attr_is_top_level(vp->da) && (vp->da->attr == FR_FALL_THROUGH) &&
1267 continue;
1268 }
1269
1270 /*
1271 * Unlike previous versions, we treat all other
1272 * attributes as normal. i.e. there's no special
1273 * treatment for passwords or Hint.
1274 */
1275
1276 switch (vp->op) {
1277 /*
1278 * Anything else are operators which
1279 * shouldn't occur. We ignore them, and
1280 * leave them in place.
1281 */
1282 default:
1283 continue;
1284
1285 /*
1286 * Add it to the "to" list, but only if
1287 * it doesn't already exist.
1288 */
1289 case T_OP_EQ:
1290 found = fr_pair_find_by_da(to, NULL, vp->da);
1291 if (!found) goto do_add;
1292 continue;
1293
1294 /*
1295 * Add it to the "to" list, and delete any attribute
1296 * of the same vendor/attr which already exists.
1297 */
1298 case T_OP_SET:
1299 found = fr_pair_find_by_da(to, NULL, vp->da);
1300 if (!found) goto do_add;
1301
1302 /*
1303 * Delete *all* matching attributes.
1304 */
1305 fr_pair_delete_by_da(to, found->da);
1306 goto do_add;
1307
1308 /*
1309 * Move it from the old list and add it
1310 * to the new list.
1311 */
1312 case T_OP_ADD_EQ:
1313 do_add:
1314 fr_pair_remove(from, vp);
1315 fr_pair_append(&head_append, vp);
1316 continue;
1317
1318 case T_OP_PREPEND:
1319 fr_pair_remove(from, vp);
1320 fr_pair_prepend(&head_prepend, vp);
1321 continue;
1322 }
1323 } /* loop over the "from" list. */
1324
1325 /*
1326 * If the op parameter was prepend, add the "new list
1327 * attributes first as those whose individual operator
1328 * is prepend should be prepended to the resulting list
1329 */
1330 if (op == T_OP_PREPEND) fr_pair_list_prepend(to, &head_append);
1331
1332 /*
1333 * If there are any items in the prepend list prepend
1334 * it to the "to" list
1335 */
1336 fr_pair_list_prepend(to, &head_prepend);
1337
1338 /*
1339 * If the op parameter was not prepend, take the "new"
1340 * list, and append it to the "to" list.
1341 */
1342 if (op != T_OP_PREPEND) fr_pair_list_append(to, &head_append);
1343
1344 fr_pair_list_free(from);
1345}
#define RCSID(id)
Definition build.h:485
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:209
#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:2913
fr_dict_attr_t const * fr_dict_attr_common_parent(fr_dict_attr_t const *a, fr_dict_attr_t const *b, bool is_ancestor)
Find a common ancestor that two TLV type attributes share.
Definition dict_util.c:2353
static fr_slen_t err
Definition dict.h:889
static fr_dict_attr_t * fr_dict_attr_unknown_afrom_oid(TALLOC_CTX *ctx, fr_dict_attr_t const *parent, fr_sbuff_t *in, fr_type_t type)
Definition dict.h:625
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_alloc(TALLOC_CTX *ctx, fr_dict_attr_t const *da, fr_type_t type))
Allocate an unknown DA.
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition dict_util.c:2707
fr_dict_t const * fr_dict_internal(void)
Definition dict_util.c:4960
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:816
#define FR_DICT_MAX_TLV_STACK
Maximum TLV stack size.
Definition dict.h:524
fr_dict_attr_err_t
Errors returned by attribute lookup functions.
Definition dict.h:320
@ FR_DICT_ATTR_OK
No error.
Definition dict.h:321
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:2532
#define fr_dict_attr_is_key_field(_da)
Definition dict.h:172
static fr_slen_t in
Definition dict.h:889
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:179
void fr_bio_shutdown & my
Definition fd_errno.h:70
fr_type_t
@ FR_TYPE_TLV
Contains nested attributes.
@ FR_TYPE_NULL
Invalid (uninitialised) attribute type.
@ 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
unsigned char uint8_t
ssize_t fr_slen_t
#define UINT8_MAX
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:1467
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:703
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:1348
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:1692
fr_pair_t * fr_pair_parent(fr_pair_t const *vp)
Return a pointer to the parent pair.
Definition pair.c:952
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:289
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:1317
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:727
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
fr_pair_t * vp[FR_DICT_MAX_TLV_STACK]
which VP we have created or found
#define CLEAN_DA_STACK
static ssize_t fr_pair_value_from_substr(fr_pair_t *vp, fr_sbuff_t *in, UNUSED bool tainted)
fr_dict_attr_t const * da[FR_DICT_MAX_TLV_STACK]
parent for parsing
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
Our version of a DA stack.
bool allow_zeros
allow '\0' as end of attribute
Definition pair_legacy.h:52
struct fr_pair_parse_s fr_pair_parse_t
bool end_of_list
do we expect an end of list '}' character?
Definition pair_legacy.h:55
fr_pair_list_t * list
list where output is placed
Definition pair_legacy.h:45
fr_dict_t const * internal
a cached pointer to the internal dictionary
Definition pair_legacy.h:48
TALLOC_CTX * ctx
Definition pair_legacy.h:43
fr_dict_t const * dict
the protocol dictionary we use
Definition pair_legacy.h:47
bool allow_compare
allow comparison operators
Definition pair_legacy.h:50
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:53
bool allow_crlf
allow CRLF, and treat like comma
Definition pair_legacy.h:51
char last_char
last character we read - ',', ' ', or 0 for EOF
Definition pair_legacy.h:54
#define fr_assert(_expr)
Definition rad_assert.h:38
static bool done
Definition radclient.c:83
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_current(_sbuff_or_marker)
#define fr_sbuff_char(_sbuff_or_marker, _eob)
#define FR_SBUFF_TERMS(...)
Initialise a terminal structure with a list of sorted strings.
Definition sbuff.h:193
#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_adv_past_blank(_sbuff, _len, _tt)
#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
char const * fr_tokens[T_TOKEN_LAST]
Definition token.c:79
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
#define PAIR_ALLOCED(_x)
Definition pair.h:212
bool fr_pair_list_empty(fr_pair_list_t const *list)
Is a valuepair list empty.
#define PAIR_VERIFY(_x)
Definition pair.h:204
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_prev(fr_pair_list_t const *list, fr_pair_t const *item))
Get the previous item in a valuepair list before a specific entry.
Definition pair_inline.c:82
fr_pair_t * fr_pair_list_head(fr_pair_list_t const *list)
Get the head of a valuepair list.
Definition pair_inline.c:42
static fr_slen_t parent
Definition pair.h:857
#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_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
#define FR_TYPE_LEAF
Definition types.h:318
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:5325
static size_t char ** out
Definition value.h:1023