The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
dict_tokenize.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/** Parse dictionary files
18 *
19 * @file src/lib/util/dict_tokenize.c
20 *
21 * @copyright 2019 The FreeRADIUS server project
22 * @copyright 2024 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
23 */
24RCSID("$Id: 76290998d7a1817ce34b97bbf99f11d0456a118c $")
25
26#include <freeradius-devel/radius/defs.h>
27#include <freeradius-devel/util/conf.h>
28#include <freeradius-devel/util/dict_fixup_priv.h>
29#include <freeradius-devel/util/file.h>
30#include <freeradius-devel/util/rand.h>
31#include <freeradius-devel/util/syserror.h>
32
33#include <sys/stat.h>
34
35/** Maximum number of arguments
36 *
37 * For any one keyword, this is the maxiumum number of arguments that can be passed.
38 */
39#define DICT_MAX_ARGV (8)
40
41/** Maximum stack size
42 *
43 * This is the maximum number of nested BEGIN and $INCLUDE statements.
44 */
45#define DICT_MAX_STACK (32)
46
47/** This represents explicit BEGIN/END frames pushed onto the stack
48 *
49 * These are flags to allow multiple nesting types to be passed to the search function.
50 */
51DIAG_OFF(attributes)
52typedef enum CC_HINT(flag_enum) {
53 NEST_NONE = 0x00,
54 NEST_TOP = 0x01, //!< top of the stack
55 NEST_PROTOCOL = 0x02, //!< BEGIN-PROTOCOL
56 NEST_VENDOR = 0x04, //!< BEGIN-VENDOR
57 NEST_ATTRIBUTE = 0x08 //!< BEGIN foo
59DIAG_ON(attributes)
60
61#define NEST_ANY (NEST_TOP | NEST_PROTOCOL | NEST_VENDOR | NEST_ATTRIBUTE)
62
64 { L("ATTRIBUTE"), NEST_ATTRIBUTE },
65 { L("NONE"), NEST_NONE },
66 { L("PROTOCOL"), NEST_PROTOCOL },
67 { L("TOP"), NEST_TOP },
68 { L("VENDOR"), NEST_VENDOR }
69};
71
73
74/** Parser context for dict_from_file
75 *
76 * Allows vendor and TLV context to persist across $INCLUDEs
77 */
78typedef struct {
79 char *filename; //!< name of the file where we read this entry
80 int line; //!< line number where we read this entry
81 fr_dict_attr_t const *da; //!< the da we care about
82 dict_nest_t nest; //!< for manual vs automatic begin / end things
83
84 fr_dict_keyword_finalise_t finalise; //!< function to call when popping
85 int member_num; //!< structure member numbers
86 fr_dict_attr_t const *struct_is_closed; //!< no more members are allowed
87 ssize_t struct_size; //!< size of the struct.
89
91 fr_dict_t *dict; //!< Protocol dictionary we're inserting attributes into.
92
93 dict_tokenize_frame_t stack[DICT_MAX_STACK]; //!< stack of attributes to track
94 int stack_depth; //!< points to the last used stack frame
95
96 fr_dict_attr_t *value_attr; //!< Cache of last attribute to speed up value processing.
97 fr_dict_attr_t const *relative_attr; //!< for ".82" instead of "1.2.3.82". only for parents of type "tlv"
99
100 char *filename; //!< current filename
101 int line; //!< current line
102};
103
105 char const *dir_name, char const *filename,
106 char const *src_file, int src_line);
107
108#define CURRENT_FRAME(_dctx) (&(_dctx)->stack[(_dctx)->stack_depth])
109#define CURRENT_DA(_dctx) (CURRENT_FRAME(_dctx)->da)
110#define CURRENT_FILENAME(_dctx) (CURRENT_FRAME(_dctx)->filename)
111#define CURRENT_LINE(_dctx) (CURRENT_FRAME(_dctx)->line)
112
113#define ASSERT_CURRENT_NEST(_dctx, _nest) fr_assert_msg(CURRENT_FRAME(_dctx)->nest == (_nest), "Expected frame type %s, got %s", \
114 fr_table_str_by_value(dict_nest_table, (_nest), "<INVALID>"), fr_table_str_by_value(dict_nest_table, CURRENT_FRAME(_dctx)->nest, "<INVALID>"))
115
117{
118 int i;
119
120 for (i = 0; i <= dctx->stack_depth; i++) {
121 dict_tokenize_frame_t const *frame = &dctx->stack[i];
122
123 FR_FAULT_LOG("[%d]: %s %s (%s): %s[%d]",
124 i,
125 fr_table_str_by_value(dict_nest_table, frame->nest, "<INVALID>"),
126 frame->da->name,
127 fr_type_to_str(frame->da->type),
128 frame->filename, frame->line);
129 }
130}
131
133{
134 int i;
135
136 for (i = dctx->stack_depth; i >= 0; i--) {
137 if (dctx->stack[i].nest & nest) return &dctx->stack[i];
138 }
139
140 return NULL;
141}
142
143static int CC_HINT(nonnull) dict_dctx_push(dict_tokenize_ctx_t *dctx, fr_dict_attr_t const *da, dict_nest_t nest)
144{
145 if ((dctx->stack_depth + 1) >= DICT_MAX_STACK) {
146 fr_strerror_const("Attribute definitions are nested too deep.");
147 return -1;
148 }
149
150 dctx->stack[++dctx->stack_depth] = (dict_tokenize_frame_t) {
151 .da = da,
152 .filename = dctx->filename,
153 .line = dctx->line,
154 .nest = nest,
155 };
156
157 return 0;
158}
159
160
161/** Pop the current stack frame
162 *
163 * @param[in] dctx Stack to pop from.
164 * @return
165 * - Pointer to the current stack frame.
166 * - NULL, if we're already at the root.
167 */
169{
170 if (dctx->stack_depth == 0) return NULL;
171
172 fr_assert(!dctx->stack[dctx->stack_depth].finalise);
173
174 return &dctx->stack[dctx->stack_depth--];
175}
176
177/** Unwind the stack until it points to a particular type of stack frame
178 *
179 * @param[in] dctx Stack to unwind.
180 * @param[in] nest Frame type to unwind to.
181 * @return
182 * - Pointer to the frame matching nest
183 * - NULL, if we unwound the complete stack and didn't find the frame.
184 */
186{
187 int i;
188
189 for (i = dctx->stack_depth; i >= 0; i--) {
191
192 /*
193 * We mash the stack depth here, because the finalisation function needs it. Plus, if
194 * there's any error, we don't care about the dctx stack, we just return up the C stack.
195 */
196 dctx->stack_depth = i;
197 frame = CURRENT_FRAME(dctx);
198
199 if (frame->finalise) {
200 if (frame->finalise(dctx) < 0) return NULL;
201 frame->finalise = NULL;
202 }
203
204 /*
205 * END-foo cannot be used without BEGIN-foo.
206 */
207 if (frame->filename && (frame->filename != dctx->filename) &&
208 (nest != NEST_ANY)) {
209 char const *name;
210
211 name = fr_table_str_by_value(dict_nest_table, nest, "<INVALID>");
212 fr_strerror_printf("END-%s in file %s[%d] without matching BEGIN-%s",
213 name, dctx->filename, dctx->line, name);
214 return NULL;
215 }
216
217 if ((frame->nest & nest) != 0) {
218 return frame;
219 }
220 }
221
222 return NULL;
223}
224
226{
227 return dict_dctx_unwind_until(dctx, NEST_ANY);
228}
229
230/*
231 * String split routine. Splits an input string IN PLACE
232 * into pieces, based on spaces.
233 */
234int fr_dict_str_to_argv(char *str, char **argv, int max_argc)
235{
236 int argc = 0;
237
238 while (*str) {
239 if (argc >= max_argc) break;
240
241 /*
242 * Chop out comments early.
243 */
244 if (*str == '#') {
245 *str = '\0';
246 break;
247 }
248
249 while ((*str == ' ') ||
250 (*str == '\t') ||
251 (*str == '\r') ||
252 (*str == '\n'))
253 *(str++) = '\0';
254
255 if (!*str) break;
256
257 argv[argc] = str;
258 argc++;
259
260 while (*str &&
261 (*str != ' ') &&
262 (*str != '\t') &&
263 (*str != '\r') &&
264 (*str != '\n'))
265 str++;
266 }
267
268 return argc;
269}
270
271static bool dict_read_sscanf_i(unsigned int *pvalue, char const *str)
272{
273 int unsigned ret = 0;
274 int base = 10;
275 char const *tab = "0123456789";
276
277 if ((str[0] == '0') &&
278 ((str[1] == 'x') || (str[1] == 'X'))) {
279 tab = "0123456789abcdef";
280 base = 16;
281
282 str += 2;
283 }
284
285 while (*str) {
286 char const *c;
287
288 if (*str == '.') break;
289
290 c = memchr(tab, tolower((uint8_t)*str), base);
291 if (!c) return false;
292
293 if (ret >= (UINT_MAX / base)) return false;
294
295 ret *= base;
296 ret += (c - tab);
297 str++;
298 }
299
300 *pvalue = ret;
301 return true;
302}
303
304/** Set a new root dictionary attribute
305 *
306 * @note Must only be called once per dictionary.
307 *
308 * @param[in] dict to modify.
309 * @param[in] name of dictionary root.
310 * @param[in] proto_number The artificial (or IANA allocated) number for the protocol.
311 * This is only used for
312 * @return
313 * - 0 on success.
314 * - -1 on failure.
315 */
316static int dict_root_set(fr_dict_t *dict, char const *name, unsigned int proto_number)
317{
318 fr_dict_attr_t *da;
319
320 fr_dict_attr_flags_t flags = {
321 .is_root = 1,
322 .type_size = dict->proto->default_type_size,
323 .length = dict->proto->default_type_length,
324 };
325
326 if (!fr_cond_assert(!dict->root)) {
327 fr_strerror_const("Dictionary root already set");
328 return -1;
329 }
330
331 da = dict_attr_alloc_root(dict->pool, dict, name, proto_number, &(dict_attr_args_t){ .flags = &flags });
332 if (unlikely(!da)) return -1;
333
334 dict->root = da;
335 dict->root->dict = dict;
336 DA_VERIFY(dict->root);
337
338 return 0;
339}
340
342{
343 char *p;
345
346 /*
347 * Some types can have fixed length
348 */
349 p = strchr(name, '[');
350 if (p) {
351 char *q;
352 unsigned int length;
353
354 *p = '\0';
355 q = strchr(p + 1, ']');
356 if (!q) {
357 fr_strerror_printf("Invalid format for '%s[...]'", name);
358 return -1;
359 }
360
361 *q = '\0';
362 if (q[1]) {
363 fr_strerror_const("length, if present, must end type field");
364 return -1;
365 }
366
367 if (!dict_read_sscanf_i(&length, p + 1)) {
368 fr_strerror_printf("Invalid length for '%s[...]'", name);
369 return -1;
370 }
371
372 /*
373 * "length" has to fit into the flags.length field.
374 */
375 if ((length == 0) || (length > UINT16_MAX)) {
376 fr_strerror_printf("Invalid length for '%s[...]'", name);
377 return -1;
378 }
379
380 /*
381 * Now that we have a length, check the data type.
382 */
383 if (strcmp(name, "octets") == 0) {
385
386 } else if (strcmp(name, "string") == 0) {
388
389 } else if (strcmp(name, "struct") == 0) {
391
392 } else if (strcmp(name, "union") == 0) {
394
395 } else if (strcmp(name, "bit") == 0) {
396 if (CURRENT_FRAME(dctx)->da->type != FR_TYPE_STRUCT) {
397 fr_strerror_const("Bit fields can only be defined as a MEMBER of data type 'struct'");
398 return -1;
399 }
400
401 (*da_p)->flags.extra = 1;
402 (*da_p)->flags.subtype = FLAG_BIT_FIELD;
403
404 if (length == 1) {
406 } else if (length <= 8) {
408 } else if (length <= 16) {
410 } else if (length <= 32) {
412 } else if (length <= 56) { /* for laziness in encode / decode */
414 } else {
415 fr_strerror_const("Invalid length for bit field");
416 return -1;
417 }
418
419 /*
420 * Cache where on a byte boundary this
421 * bit field ends. We could have the
422 * validation function loop through all
423 * previous siblings, but that's
424 * annoying.
425 */
426 (*da_p)->flags.flag_byte_offset = length;
427
428 } else {
429 fr_strerror_printf("Attributes of type '%s' cannot use the %s[...] syntax",
430 name, name);
431 return -1;
432 }
433
434 (*da_p)->flags.is_known_width = true;
435 (*da_p)->flags.length = length;
436 return dict_attr_type_init(da_p, type);
437 }
438
439 /*
440 * We default to using the standard FreeRADIUS types.
441 *
442 * However, if there is a protocol-specific type parsing
443 * function, we call that, too. That ordering allows the
444 * protocol-specific names to over-ride the default ones.
445 */
447
448 if (dctx->dict->proto->attr.type_parse &&
449 !dctx->dict->proto->attr.type_parse(&type, da_p, name)) {
450 return -1;
451 }
452
453 switch (type) {
454 /*
455 * Still not known, or is still a NULL type, that's an error.
456 *
457 * The protocol-specific function can return an error if
458 * it has an error in its parsing. Or, it can return
459 * "true"
460 */
461 case FR_TYPE_NULL:
462 fr_strerror_printf("Unknown data type '%s'", name);
463 return -1;
464
465 case FR_TYPE_LEAF:
466 case FR_TYPE_TLV:
467 case FR_TYPE_STRUCT:
468 case FR_TYPE_VSA:
469 case FR_TYPE_GROUP:
470 case FR_TYPE_UNION:
471 break;
472
473 /*
474 * @todo - allow definitions of type 'vendor' only if we need to have different
475 * type_size/length for VSAs or "evs" in the Extended-Attribute space.
476 */
477 case FR_TYPE_VENDOR:
478 fr_strerror_const("Cannot use data type 'vendor' - use BEGIN-VENDOR instead");
479 return -1;
480
481 default:
482 fr_strerror_printf("Invalid data type '%s'", name);
483 return -1;
484 }
485
486 return dict_attr_type_init(da_p, type);
487}
488
489/** Define a flag setting function, which sets one bit in a fr_dict_attr_flags_t
490 *
491 * This is here, because AFAIK there's no completely portable way to get the bit
492 * offset of a bit field in a structure.
493 */
494#define FLAG_FUNC(_name) \
495static int dict_flag_##_name(fr_dict_attr_t **da_p, UNUSED char const *value, UNUSED fr_dict_flag_parser_rule_t const *rules)\
496{ \
497 (*da_p)->flags._name = 1; \
498 return 0; \
499}
500
501FLAG_FUNC(array)
502
503static int dict_flag_clone(fr_dict_attr_t **da_p, char const *value, UNUSED fr_dict_flag_parser_rule_t const *rules)
504{
505 /*
506 * Clone has a limited scope.
507 */
508 switch ((*da_p)->type) {
509 case FR_TYPE_LEAF:
510 case FR_TYPE_STRUCT:
511 case FR_TYPE_TLV:
512 break;
513
514 default:
515 fr_strerror_printf("Attributes of data type '%s' cannot use 'clone=...'", fr_type_to_str((*da_p)->type));
516 return -1;
517 }
518
519 /*
520 * Allow cloning of any types, so long as
521 * the types are the same. We do the checks later.
522 */
524
525 /*
526 * We don't know how big the cloned reference is, so it isn't known width.
527 */
528 (*da_p)->flags.is_known_width = 0;
529
530 return 0;
531}
532
533FLAG_FUNC(counter)
534
535static int dict_flag_enum(fr_dict_attr_t **da_p, char const *value, UNUSED fr_dict_flag_parser_rule_t const *rule)
536{
537 /*
538 * Allow enum=... as an almost synonym for "clone", where we copy only the VALUEs, and not any
539 * children.
540 */
541 if (!fr_type_is_leaf((*da_p)->type)) {
542 fr_strerror_const("'enum=...' references cannot be used for structural types");
543 return -1;
544 }
545
546 /*
547 * Ensure that this attribute has room for enums.
548 */
549 if (!dict_attr_ext_alloc(da_p, FR_DICT_ATTR_EXT_ENUMV)) return -1;
550
552
553 return 0;
554}
555
556/** "flat"
557 *
558 * We have to parse the flat flag for tests, but only the various
559 * protocol libraries can set it for protocol-specific attributes.
560 */
561static int dict_flag_flat(fr_dict_attr_t **da_p, UNUSED char const *value, UNUSED fr_dict_flag_parser_rule_t const *rule)
562{
563 if ((*da_p)->type != FR_TYPE_GROUP) {
564 fr_strerror_const("'flat' flag can only be used for data type 'group'");
565 return -1;
566 }
567
568 if (!(*da_p)->flags.internal) {
569 fr_strerror_const("'flat' flag can only be used for 'internal' attributes");
570 return -1;
571 }
572
573 (*da_p)->flags.allow_flat = true;
574
575 return 0;
576}
577
578FLAG_FUNC(internal)
579
580static int dict_flag_key(fr_dict_attr_t **da_p, char const *value, UNUSED fr_dict_flag_parser_rule_t const *rule)
581{
582 fr_dict_attr_t *da = *da_p;
583 fr_dict_attr_t const *key;
585
586 if (fr_type_is_leaf(da->type)) {
587 if (value) {
588 fr_strerror_const("Attributes defining a 'key' field cannot specify a key reference");
589 return -1;
590 }
591
592 if ((da->type != FR_TYPE_UINT8) && (da->type != FR_TYPE_UINT16) && (da->type != FR_TYPE_UINT32)) {
593 fr_strerror_const("The 'key' flag can only be used for attributes of type 'uint8', 'uint16', or 'uint32'");
594 return -1;
595 }
596
597 if (da->flags.extra) {
598 fr_strerror_const("Bit fields cannot be key fields");
599 return -1;
600 }
601
602 da->flags.extra = 1;
603 da->flags.subtype = FLAG_KEY_FIELD;
604 return 0;
605 }
606
607 if (da->type != FR_TYPE_UNION) {
608 fr_strerror_printf("Attributes of type '%s' cannot define a 'key' reference", fr_type_to_str(da->type));
609 return -1;
610 }
611
612 if (!value) {
613 fr_strerror_const("Missing reference for 'key=...'");
614 return -1;
615 }
616
617 /*
618 * The reference must be to a sibling, which is marked "is key".
619 */
620 key = fr_dict_attr_by_name(NULL, da->parent, value);
621 if (!key) {
622 fr_strerror_printf("Invalid reference for 'key=...'. Parent %s does not have a child attribute named %s",
623 da->parent->name, value);
624 return -1;
625 }
626
627 if (da->parent != key->parent) {
628 fr_strerror_printf("Invalid reference for 'key=...'. Reference %s does not share a common parent",
629 value);
630 return -1;
631 }
632
633 if (!fr_dict_attr_is_key_field(key)) {
634 fr_strerror_printf("Invalid reference for 'key=...'. Reference %s is not a 'key' field",
635 value);
636 return -1;
637 }
638
639 /*
640 * Allocate the ref and save the value. This link exists solely so that the children of the
641 * UNION can easily find the key field of the parent STRUCT.
642 */
644 if (ext) {
645 fr_strerror_printf("Attribute already has a 'key=...' defined");
646 return -1;
647 }
648
649 ext = dict_attr_ext_alloc(da_p, FR_DICT_ATTR_EXT_KEY); /* can change da_p */
650 if (unlikely(!ext)) return -1;
651
653 ext->ref = key;
654
655 return 0;
656}
657
658static int dict_flag_length(fr_dict_attr_t **da_p, char const *value, UNUSED fr_dict_flag_parser_rule_t const *rule)
659{
660 fr_dict_attr_t *da = *da_p;
661
662 if (strcmp(value, "uint8") == 0) {
663 da->flags.is_known_width = true;
664 da->flags.extra = 1;
665 da->flags.subtype = FLAG_LENGTH_UINT8;
666
667 } else if (strcmp(value, "uint16") == 0) {
668 da->flags.is_known_width = true;
669 da->flags.extra = 1;
670 da->flags.subtype = FLAG_LENGTH_UINT16;
671
672 } else {
673 fr_strerror_const("Invalid value given for the 'length' flag");
674 return -1;
675 }
676 da->flags.type_size = 0;
677
678 return 0;
679}
680
681static int dict_flag_offset(fr_dict_attr_t **da_p, char const *value, UNUSED fr_dict_flag_parser_rule_t const *rule)
682{
683 fr_dict_attr_t *da = *da_p;
684 int offset;
685
686 if (da->type != FR_TYPE_STRUCT) {
687 fr_strerror_const("The 'offset' flag can only be used with data type 'struct'");
688 return -1;
689 }
690
691 if (!da_is_length_field(da)) {
692 fr_strerror_const("The 'offset' flag can only be used in combination with 'length=uint8' or 'length=uint16'");
693 return -1;
694 }
695
696 offset = atoi(value);
697 if ((offset <= 0) || (offset > 255)) {
698 fr_strerror_const("The 'offset' value must be between 1..255");
699 return -1;
700 }
701 da->flags.type_size = offset;
702
703 return 0;
704}
705
707{
708 fr_dict_attr_t *da = *da_p;
709 int precision;
710
711 switch (da->type) {
712 case FR_TYPE_DATE:
714 break;
715
716 default:
717 fr_strerror_const("The 'precision' flag can only be used with data types 'date' or 'time'");
718 return -1;
719 }
720
722 if (precision < 0) {
723 fr_strerror_printf("Unknown %s precision '%s'", fr_type_to_str(da->type), value);
724 return -1;
725 }
726 da->flags.flag_time_res = precision;
727
728 return 0;
729}
730
731static int dict_flag_ref(fr_dict_attr_t **da_p, char const *value, UNUSED fr_dict_flag_parser_rule_t const *rule)
732{
733 fr_dict_attr_t *da = *da_p;
734
735 if (da->flags.extra) {
736 fr_strerror_const("Cannot use 'ref' with other flags");
737 return -1;
738 }
739
740 if (da->type != FR_TYPE_GROUP) {
741 fr_strerror_printf("The 'ref' flag cannot be used for type '%s'",
742 fr_type_to_str(da->type));
743 return -1;
744 }
745
747
748 return 0;
749}
750
752{
753 fr_dict_attr_t *da = *da_p;
754
755 da->flags.secret = 1;
756
757 if ((da->type != FR_TYPE_STRING) && (da->type != FR_TYPE_OCTETS)) {
758 fr_strerror_const("The 'secret' flag can only be used with data types 'string' or 'octets'");
759 return -1;
760 }
761
762 return 0;
763}
764
765static int dict_flag_subtype(fr_dict_attr_t **da_p, char const *value, UNUSED fr_dict_flag_parser_rule_t const *rule)
766{
767 fr_dict_attr_t *da = *da_p;
768 fr_type_t subtype;
769
770 switch (da->type) {
771 case FR_TYPE_DATE:
773 break;
774
775 default:
776 fr_strerror_const("The 'subtype' flag can only be used with data types 'date' or 'time'");
777 return -1;
778 }
779
780 subtype = fr_type_from_str(value);
781 if (fr_type_is_null(subtype)) {
782 unknown_type:
783 fr_strerror_printf("Unknown or unsupported %s type '%s'",
784 fr_type_to_str(subtype),
785 value);
786 return -1;
787 }
788
789 switch (subtype) {
790 default:
791 goto unknown_type;
792
793 case FR_TYPE_INT16:
794 if (da->type == FR_TYPE_DATE) goto unknown_type;
795 da->flags.length = 2;
796 break;
797
798 case FR_TYPE_UINT16:
799 da->flags.is_unsigned = true;
800 da->flags.length = 2;
801 break;
802
803 case FR_TYPE_INT32:
804 if (da->type == FR_TYPE_DATE) goto unknown_type;
805 da->flags.length = 4;
806 break;
807
808 case FR_TYPE_UINT32:
809 da->flags.is_unsigned = true;
810 da->flags.length = 4;
811 break;
812
813 case FR_TYPE_INT64:
814 if (da->type == FR_TYPE_DATE) goto unknown_type;
815 da->flags.length = 8;
816 break;
817
818 case FR_TYPE_UINT64:
819 da->flags.is_unsigned = true;
820 da->flags.length = 8;
821 break;
822 }
823
824 return 0;
825}
826
827FLAG_FUNC(unsafe)
828
829/** A lookup function for dictionary attribute flags
830 *
831 */
833 fr_dict_attr_flag_to_parser, fr_dict_flag_parser_rule_t const *, fr_dict_flag_parser_rule_t const *)
834
835static int CC_HINT(nonnull) dict_process_flag_field(dict_tokenize_ctx_t *dctx, char *name, fr_dict_attr_t **da_p)
836{
837 static fr_dict_flag_parser_t dict_common_flags[] = {
838 { L("array"), { .func = dict_flag_array } },
839 { L("clone"), { .func = dict_flag_clone, .needs_value = true } },
840 { L("counter"), { .func = dict_flag_counter } },
841 { L("enum"), { .func = dict_flag_enum, .needs_value = true } },
842 { L("flat"), { .func = dict_flag_flat } },
843 { L("internal"), { .func = dict_flag_internal } },
844 { L("key"), { .func = dict_flag_key } },
845 { L("length"), { .func = dict_flag_length, .needs_value = true } },
846 { L("offset"), { .func = dict_flag_offset, .needs_value = true } },
847 { L("precision"), { .func = dict_flag_precision, .needs_value = true } },
848 { L("ref"), { .func = dict_flag_ref, .needs_value = true } },
849 { L("secret"), { .func = dict_flag_secret } },
850 { L("subtype"), { .func = dict_flag_subtype, .needs_value = true } },
851 { L("unsafe"), { .func = dict_flag_unsafe } },
852 };
853 static size_t dict_common_flags_len = NUM_ELEMENTS(dict_common_flags);
854
855 char *p, *next = NULL;
856
857 if ((*da_p)->type == FR_TYPE_NULL) {
858 fr_strerror_const("Type must be specified before parsing flags");
859 return -1;
860 }
861
862 for (p = name; p && *p != '\0' ; p = next) {
863 char *key, *value;
864 fr_dict_flag_parser_rule_t const *parser;
865
866 key = p;
867
868 /*
869 * Search for the first '=' or ','
870 */
871 for (next = p + 1; *next && (*next != '=') && (*next != ','); next++) {
872 /* do nothing */
873 }
874
875 /*
876 * We have a value, zero out the '=' and point to the value.
877 */
878 if (*next == '=') {
879 *(next++) = '\0';
880 value = next;
881
882 if (!*value || (*value == ',')) {
883 fr_strerror_printf("Missing value after '%s='", key);
884 return -1;
885 }
886 } else {
887 value = NULL;
888 }
889
890 /*
891 * Skip any trailing text in the value.
892 */
893 for (/* nothing */; *next; next++) {
894 if (*next == ',') {
895 *(next++) = '\0';
896 break;
897 }
898 }
899
900 /*
901 * Search the protocol table, then the main table.
902 * This allows protocols to overload common flags.
903 */
904 if (!((dctx->dict->proto->attr.flags.table &&
905 fr_dict_attr_flag_to_parser(&parser, dctx->dict->proto->attr.flags.table,
906 dctx->dict->proto->attr.flags.table_len, key, NULL)) ||
907 fr_dict_attr_flag_to_parser(&parser, dict_common_flags, dict_common_flags_len, key, NULL))) {
908 fr_strerror_printf("Unknown flag '%s'", key);
909 return -1;
910 }
911
912 if (parser->needs_value && !value) {
913 fr_strerror_printf("Flag '%s' requires a value", key);
914 return -1;
915 }
916
917 if (unlikely(parser->func(da_p, value, parser) < 0)) return -1;
918 }
919
920 /*
921 * Don't check the flags field for validity via
922 * dict_attr_flags_valid(). It may be updated by various
923 * protocol-specific callback functions. And,
924 * fr_dict_attr_add() calls dict_attr_flags_valid() anyways.
925 */
926
927 return 0;
928}
929
931{
932 if (dict_fixup_apply(&dctx->fixup) < 0) return -1;
933
934 dctx->value_attr = NULL;
935 dctx->relative_attr = NULL;
936
937 return 0;
938}
939
940static inline CC_HINT(always_inline)
942{
943 da->filename = CURRENT_FILENAME(dctx);
944 da->line = CURRENT_LINE(dctx);
945}
946
947/** Add an attribute to the dictionary, or add it to a list of attributes to clone later
948 *
949 * @param[in] fixup context to add an entry to (if needed).
950 * @param[in] da_p to either add, or create a fixup for.
951 * @return
952 * - 0 on success, and an attribute was added.
953 * - 1 on success, and a deferred entry was added.
954 * - -1 on failure.
955 */
957{
959 fr_dict_attr_t *da = *da_p;
960 int ret = 0;
961
962 /*
963 * Check for any references associated with the attribute,
964 * if they're unresolved, then add fixups.
965 *
966 * We do this now, as we know the attribute memory chunk
967 * is stable, and we can safely add the fixups.
968 */
970 if (ref && fr_dict_attr_ref_is_unresolved(ref->type)) {
971 /*
972 * See if we can immediately apply the ref.
973 */
974 fr_dict_attr_t const *src;
975
976 switch (fr_dict_attr_ref_type(ref->type)) {
978 /*
979 * IF the ref exists, we can always add it. The ref won't be changed later.
980 */
981 if (fr_dict_protocol_reference(&src, da->parent, &FR_SBUFF_IN_STR(ref->unresolved)) < 0) return -1;
982
983 if (src && (dict_attr_ref_set(*da_p, src, FR_DICT_ATTR_REF_ALIAS) < 0)) return -1;
984
985 if (fr_dict_attr_add_initialised(da) < 0) {
986 error:
987 talloc_free(da);
988 *da_p = NULL;
989 return -1;
990 }
991
992 if (!src && (dict_fixup_group_enqueue(fixup, da, ref->unresolved) < 0)) return -1;
993 ret = 1;
994 break;
995
997 /*
998 * Do NOT copy the enums now. Later dictionaries may add more values, and we
999 * want to be able to copy all values.
1000 */
1001 if (fr_dict_attr_add_initialised(da) < 0) goto error;
1002
1003 if (dict_fixup_clone_enum_enqueue(fixup, da, ref->unresolved) < 0) return -1;
1004 break;
1005
1007 /*
1008 * @todo - if we defer this clone, we get errors loading dictionary.wimax. That
1009 * likely means there are issues with the dict_fixup_clone_apply() function.
1010 */
1011 if (fr_dict_protocol_reference(&src, da->parent, &FR_SBUFF_IN_STR(ref->unresolved)) < 0) return -1;
1012 if (src) {
1013 if (dict_fixup_clone(da_p, src) < 0) return -1;
1014 break;
1015 }
1016
1017 if (dict_fixup_clone_enqueue(fixup, da, ref->unresolved) < 0) return -1;
1018 ret = 1;
1019 break;
1020
1021 default:
1022 fr_strerror_const("Unknown reference type");
1023 return -1;
1024 }
1025 } else {
1026 if (fr_dict_attr_add_initialised(da) < 0) goto error;
1027 }
1028
1029 return ret;
1030}
1031
1032/** Check if this definition is a duplicate, and if it is, whether we should skip it error out
1033 *
1034 * @return
1035 * - 1 if this is not a duplicate.
1036 * - 0 if this is a duplicate, and we should ignore the definition.
1037 * - -1 if this is a duplicate, and we should error out.
1038 */
1040{
1041 fr_dict_attr_t const *dup_name = NULL;
1042 fr_dict_attr_t const *dup_num = NULL;
1043 fr_dict_attr_t const *found;
1044
1045 /*
1046 * Search in the parent for a duplicate by name and then by num
1047 */
1048 if (!da->parent) return 1; /* no parent no conflicts possible */
1049
1050 dup_name = fr_dict_attr_by_name(NULL, da->parent, da->name);
1051 if (!da->flags.name_only) dup_num = fr_dict_attr_child_by_num(da->parent, da->attr);
1052
1053 /*
1054 * Not a duplicate...
1055 */
1056 if (!dup_name && !dup_num) return 1;
1057
1058 found = dup_name ? dup_name : dup_num;
1059
1060 switch (da->type) {
1061 /*
1062 * For certain types, we allow strict duplicates as if
1063 * the user wants to add extra children in the custom
1064 * dictionary, or wants to avoid ordering issues between
1065 * multiple dictionaries, we need to support this.
1066 */
1067 case FR_TYPE_VSA:
1068 case FR_TYPE_VENDOR:
1069 case FR_TYPE_TLV:
1070 if (fr_dict_attr_cmp_fields(da, found) == 0) return 0;
1071 break;
1072
1073 case FR_TYPE_LEAF:
1074 /*
1075 * Leaf types can be duplicated if they are identical.
1076 */
1077 if ((da->type == found->type) &&
1078 (fr_dict_attr_cmp_fields(da, found) == 0)) return 0;
1079 break;
1080
1081 default:
1082 break;
1083 }
1084
1085 if (dup_name) {
1086 fr_strerror_printf("Duplicate attribute name '%s' in namespace '%s'. Originally defined %s[%d]",
1087 da->name, da->parent->name, dup_name->filename, dup_name->line);
1088 return -1;
1089 }
1090
1091 fr_strerror_printf("Duplicate attribute number %u in parent '%s'. Originally defined %s[%d]",
1092 da->attr, da->parent->name, dup_num->filename, dup_num->line);
1093 return -1;
1094}
1095
1097{
1098 fr_dict_attr_t const *da;
1099 dict_tokenize_frame_t const *frame = CURRENT_FRAME(dctx);
1100
1101 da = frame->da;
1102 fr_assert(da->type == FR_TYPE_STRUCT);
1103
1104 /*
1105 * The structure was fixed-size, but the fields don't fill it. That's an error.
1106 *
1107 * Since process_member() checks for overflow, the check here is really only for
1108 * underflow.
1109 */
1110 if (da->flags.is_known_width) {
1111 if (CURRENT_FRAME(dctx)->struct_size != da->flags.length) {
1112 fr_strerror_printf("MEMBERs of %s struct[%u] do not exactly fill the fixed-size structure",
1113 da->name, da->flags.length);
1114 return -1;
1115 }
1116
1117 return 0;
1118 }
1119
1120 /*
1121 * If we have discovered that the structure has a fixed size, then update the da with that
1122 * information.
1123 */
1124 if (frame->struct_size < UINT16_MAX) {
1125 UNCONST(fr_dict_attr_t *, da)->flags.length = frame->struct_size;
1126 } /* else length 0 means "unknown / variable size / too large */
1127
1128 return 0;
1129}
1130
1132{
1133 /*
1134 * Adding an attribute of type 'struct' is an implicit
1135 * BEGIN-STRUCT.
1136 */
1137 if (da->type == FR_TYPE_STRUCT) {
1138 if (dict_dctx_push(dctx, da, NEST_NONE) < 0) return -1;
1139
1140 CURRENT_FRAME(dctx)->finalise = dict_struct_finalise;
1141 dctx->value_attr = NULL;
1142
1143 } else if (fr_type_is_leaf(da->type)) {
1144 dctx->value_attr = da;
1145
1146 } else {
1147 dctx->value_attr = NULL;
1148 }
1149
1150 return 0;
1151}
1152
1154 fr_dict_attr_t const *parent, char const *name,
1155 char const *type_name, char *flag_name,
1156 fr_dict_attr_flags_t const *base_flags)
1157{
1158 fr_dict_attr_t *da, *to_free = NULL;
1159 size_t len;
1160
1161 /*
1162 * Dictionaries need to have real names, not v3-style ones "Attr-#". And not ones which are
1163 * solely numerical.
1164 */
1165 if (strncmp(name, "Attr-", 5) == 0) {
1166 fr_strerror_const("Invalid name - 'Attr-' is an invalid name");
1167 return -1;
1168 }
1169
1170 len = strlen(name);
1171 if (fr_sbuff_adv_past_allowed( &FR_SBUFF_IN(name, len), SIZE_MAX, sbuff_char_class_int, NULL) == len) {
1172 fr_strerror_printf("Invalid attribute name '%s' - the name cannot be an integer", name);
1173 return -1;
1174 }
1175
1176 /*
1177 * Allocate the attribute here, and then fill in the fields
1178 * as we start parsing the various elements of the definition.
1179 */
1180 if (!*da_p) {
1181 da = dict_attr_alloc_null(dctx->dict->pool, dctx->dict->proto);
1182 if (unlikely(!da)) return -1;
1183 to_free = da;
1184
1185 } else {
1186 da = *da_p;
1187 }
1188 dict_attr_location_set(dctx, da);
1189 da->dict = dctx->dict;
1190
1191 /*
1192 * Set some fields to be friendlier to the type / flag
1193 * parsing and validation routines.
1194 */
1195 da->parent = parent;
1196 da->name = name;
1197
1198 /*
1199 * Set the attribute flags from the base flags.
1200 */
1201 memcpy(&da->flags, base_flags, sizeof(da->flags));
1202
1203 if (unlikely(strcmp(type_name, "auto") == 0)) {
1204 fr_dict_attr_t const *src;
1205 char const *p, *end;
1206
1207 if (!flag_name || !(p = strstr(flag_name, "clone="))) {
1208 fr_strerror_const("Data type of 'auto' is missing the required flag 'clone=...'");
1209 goto error;
1210 }
1211
1212 p += 6;
1213 for (end = p; *end != '\0'; end++) {
1214 if (*end == ',') break;
1215 }
1216
1217 if (fr_dict_protocol_reference(&src, parent, &FR_SBUFF_IN(p, end)) < 0) goto error;
1218 if (!src) {
1219 fr_strerror_const("Data type 'auto' requires that the 'clone=...' reference points to an attribute which already exists");
1220 goto error;
1221 }
1222
1223 /*
1224 * Don't copy the source yet, as later things may add enums, children, etc. to the source
1225 * attribute. Instead, we just copy the data type.
1226 */
1227 if (dict_attr_type_init(&da, src->type) < 0) goto error;
1228
1229 } else {
1230 /*
1231 * Set the base type of the attribute.
1232 */
1233 if (dict_process_type_field(dctx, type_name, &da) < 0) {
1234 error:
1235 if (da == to_free) talloc_free(to_free);
1236 return -1;
1237 }
1238 }
1239
1240 /*
1241 * Clear the temporary parent pointer.
1242 */
1243 da->parent = NULL;
1244 if (unlikely(dict_attr_parent_init(&da, parent) < 0)) goto error;
1245
1246 /*
1247 * Parse optional flags. We pass in the partially allocated
1248 * attribute so that flags can be set directly.
1249 *
1250 * Where flags contain variable length fields, this is
1251 * significantly easier than populating a temporary struct.
1252 */
1253 if (flag_name) if (dict_process_flag_field(dctx, flag_name, &da) < 0) goto error;
1254
1255 da->name = NULL; /* the real name will be a talloc'd chunk */
1256
1257 *da_p = da;
1258 return 0;
1259}
1260
1261/*
1262 * Process the $INCLUDE command
1263 */
1264static int dict_read_process_include(dict_tokenize_ctx_t *dctx, char **argv, int argc, char const *dir)
1265{
1266 int rcode;
1267 bool required = true;
1268 int stack_depth = dctx->stack_depth;
1269 char *src_file = dctx->filename;
1270 int src_line = dctx->line;
1271 char *pattern;
1272 char const *filename;
1273 fr_globdir_iter_t iter;
1274
1275 /*
1276 * Allow "$INCLUDE" or "$INCLUDE-", but
1277 * not anything else.
1278 */
1279 if ((argv[0][8] != '\0') && ((argv[0][8] != '-') || (argv[0][9] != '\0'))) {
1280 fr_strerror_printf("Invalid keyword '%s'", argv[0]);
1281 return -1;
1282 }
1283
1284 if (argc != 2) {
1285 fr_strerror_printf("Unexpected text after $INCLUDE at %s[%d]", fr_cwd_strip(src_file), src_line);
1286 return -1;
1287 }
1288
1289 pattern = argv[1];
1290 required = (argv[0][8] != '-');
1291
1292 /*
1293 * Allow limited macro capability, so people don't have
1294 * to remember where the root dictionaries are located.
1295 */
1296 if (strncmp(pattern, "${dictdir}/", 11) == 0) {
1297 dir = fr_dict_global_ctx_dir();
1298 pattern += 11;
1299 }
1300
1301 /*
1302 * Figure out what we need to open, and put the result into "filename".
1303 */
1304 rcode = fr_globdir_iter_init(&filename, dir, pattern, &iter);
1305 if (rcode < 0) {
1306 failed:
1307 fr_strerror_printf("Failed opening $INCLUDE of %s/%s at %s[%d] - %s",
1308 dir, pattern, fr_cwd_strip(src_file), src_line, fr_syserror(errno));
1309 return -1;
1310 }
1311
1312 /*
1313 * No files may or may not be an error, depending on if the $INCLUDE was required.
1314 */
1315 if (rcode == 0) {
1316 if (required) {
1317 errno = ENOENT;
1318 goto failed;
1319 }
1320
1321 fr_strerror_clear(); /* delete all errors */
1322 return 0;
1323 }
1324
1325 /*
1326 * "filename" is already the file, so we use do{}while() instead of while{}
1327 */
1328 do {
1329 rcode = _dict_from_file(dctx, dir, filename, src_file, src_line);
1330 if (rcode < 0) {
1331 fr_strerror_printf_push("from $INCLUDE at %s[%d]", fr_cwd_strip(src_file), src_line);
1332 break;
1333 }
1334
1335 if (dctx->stack_depth < stack_depth) {
1336 fr_strerror_printf("unexpected END-??? in $INCLUDE at %s[%d]",
1337 fr_cwd_strip(src_file), src_line);
1338 rcode = -1;
1339 break;
1340 }
1341
1342 } while ((rcode = fr_globdir_iter_next(&filename, &iter)) == 1);
1343 (void) fr_globdir_iter_free(&iter);
1344
1345 return rcode; /* could be an error! */
1346}
1347
1348static int dict_read_parse_format(char const *format, int *ptype, int *plength, bool *pcontinuation)
1349{
1350 char const *p;
1351 int type, length;
1352 bool continuation = false;
1353
1354 if (strncasecmp(format, "format=", 7) != 0) {
1355 fr_strerror_printf("Invalid format for VENDOR. Expected 'format=', got '%s'",
1356 format);
1357 return -1;
1358 }
1359
1360 p = format + 7;
1361 if ((strlen(p) < 3) ||
1362 !isdigit((uint8_t)p[0]) ||
1363 (p[1] != ',') ||
1364 !isdigit((uint8_t)p[2]) ||
1365 (p[3] && (p[3] != ','))) {
1366 fr_strerror_printf("Invalid format for VENDOR. Expected text like '1,1', got '%s'",
1367 p);
1368 return -1;
1369 }
1370
1371 type = (int)(p[0] - '0');
1372 length = (int)(p[2] - '0');
1373
1374 if ((type != 1) && (type != 2) && (type != 4)) {
1375 fr_strerror_printf("Invalid type value %d for VENDOR", type);
1376 return -1;
1377 }
1378
1379 if ((length != 0) && (length != 1) && (length != 2)) {
1380 fr_strerror_printf("Invalid length value %d for VENDOR", length);
1381 return -1;
1382 }
1383
1384 if (p[3] == ',') {
1385 if (!p[4]) {
1386 fr_strerror_printf("Invalid format for VENDOR. Expected text like '1,1', got '%s'",
1387 p);
1388 return -1;
1389 }
1390
1391 if ((p[4] != 'c') ||
1392 (p[5] != '\0')) {
1393 fr_strerror_printf("Invalid format for VENDOR. Expected text like '1,1', got '%s'",
1394 p);
1395 return -1;
1396 }
1397 continuation = true;
1398
1399 if ((type != 1) || (length != 1)) {
1400 fr_strerror_const("Only VSAs with 'format=1,1' can have continuations");
1401 return -1;
1402 }
1403 }
1404
1405 *ptype = type;
1406 *plength = length;
1407 *pcontinuation = continuation;
1408 return 0;
1409}
1410
1411/*
1412 * Process the ALIAS command
1413 *
1414 * ALIAS name ref
1415 *
1416 * Creates an attribute "name" in the root namespace of the current
1417 * dictionary, which is a pointer to "ref".
1418 */
1419static int dict_read_process_alias(dict_tokenize_ctx_t *dctx, char **argv, int argc, UNUSED fr_dict_attr_flags_t *base_flags)
1420{
1421 fr_dict_attr_t const *da;
1422 fr_dict_attr_t const *parent = CURRENT_FRAME(dctx)->da;
1423
1424 if (argc != 2) {
1425 fr_strerror_const("Invalid ALIAS syntax");
1426 return -1;
1427 }
1428
1429 /*
1430 * Dictionaries need to have real names, not shitty ones.
1431 */
1432 if (strncmp(argv[0], "Attr-", 5) == 0) {
1433 fr_strerror_const("Invalid ALIAS name");
1434 return -1;
1435 }
1436
1437 if (strchr(argv[0], '.') != NULL) {
1438 fr_strerror_const("ALIAS names must be in the local context, and cannot contain '.'");
1439 return -1;
1440 }
1441
1442 /*
1443 * Internally we can add aliases to STRUCTs and GROUPs. But the poor user can't.
1444 *
1445 * This limitation is mainly so that we can differentiate automatically added aliases (which
1446 * point to unions), from ones added by users. If we make dict_attr_acopy_aliases() a little
1447 * smarter, then we can relax those checks.
1448 */
1449 switch (parent->type) {
1450 case FR_TYPE_TLV:
1451 case FR_TYPE_VSA:
1452 case FR_TYPE_VENDOR:
1453 break;
1454
1455 default:
1456 fr_strerror_printf("ALIAS cannot be added to data type '%s'", fr_type_to_str(parent->type));
1457 return -1;
1458 }
1459
1460 /*
1461 * Relative refs get resolved from the current namespace.
1462 */
1463 if (argv[1][0] == '@') {
1464 fr_strerror_const("An ALIAS reference cannot cross protocol boundaries");
1465 return -1;
1466
1467 } else if (argv[1][0] == '.') {
1468 if (argv[1][1] == '.') {
1469 fr_strerror_const("An ALIAS reference cannot use '..' to go back up to another parent");
1470 return -1;
1471 }
1472
1473 } else if (parent != dctx->dict->root) {
1474 fr_strerror_const("An ALIAS reference must use a leading '.' when referring to attributes with the same parent");
1475 return -1;
1476 }
1477
1478 /*
1479 * The <ref> can be a name.
1480 */
1481 da = fr_dict_attr_by_oid(NULL, parent, argv[1]);
1482 if (!da) {
1483 /*
1484 * If we can't find it now, the file containing the ALIASes may have been read before
1485 * the ALIASed attributes.
1486 *
1487 * @todo - we likely just want to forbid this.
1488 */
1489 return dict_fixup_alias_enqueue(&dctx->fixup, CURRENT_FILENAME(dctx), CURRENT_LINE(dctx),
1490 fr_dict_attr_unconst(parent), argv[0],
1491 fr_dict_attr_unconst(parent), argv[1]);
1492 }
1493
1494 return dict_attr_alias_add(fr_dict_attr_unconst(parent), argv[0], da, true);
1495}
1496
1497/*
1498 * Process the ATTRIBUTE command
1499 */
1500static int dict_read_process_attribute(dict_tokenize_ctx_t *dctx, char **argv, int argc, fr_dict_attr_flags_t *base_flags)
1501{
1502 bool set_relative_attr;
1503
1504 ssize_t slen;
1505 unsigned int attr;
1506
1507 fr_dict_attr_t const *parent, *key = NULL;
1508 fr_dict_attr_t *da;
1509 fr_value_box_t box;
1510
1511 if ((argc < 3) || (argc > 4)) {
1512 fr_strerror_const("Invalid ATTRIBUTE syntax");
1513 return -1;
1514 }
1515
1516#ifdef STATIC_ANALYZER
1517 if (!dctx->dict) return -1;
1518#endif
1519
1520 /*
1521 * A non-relative ATTRIBUTE definition means that it is
1522 * in the context of the previous BEGIN-FOO. So we
1523 * unwind the stack to match.
1524 */
1525 if (argv[1][0] != '.') {
1526 dict_tokenize_frame_t const *frame;
1527
1528 frame = dict_dctx_unwind(dctx);
1529 if (!frame) return -1;
1530
1531 parent = frame->da;
1532
1533 /*
1534 * Allow '0xff00' as attribute numbers, but only
1535 * if there is no OID component.
1536 */
1537 if (strchr(argv[1], '.') == 0) {
1538 if (!dict_read_sscanf_i(&attr, argv[1])) {
1539 fr_strerror_const("Invalid ATTRIBUTE number");
1540 return -1;
1541 }
1542
1543 } else {
1544 slen = fr_dict_attr_by_oid_legacy(&parent, &attr, argv[1]);
1545 if (slen <= 0) return -1;
1546 }
1547
1548 /*
1549 * We allow relative attributes only for TLVs.
1550 *
1551 * We haven't parsed the type field yet, so we
1552 * just check it here manually.
1553 */
1554 set_relative_attr = (strcasecmp(argv[2], "tlv") == 0);
1555
1556 } else {
1557 if (!dctx->relative_attr) {
1558 fr_strerror_printf("No parent attribute reference was set for partial OID %s", argv[1]);
1559 return -1;
1560 }
1561
1562 parent = dctx->relative_attr;
1563
1564 slen = fr_dict_attr_by_oid_legacy(&parent, &attr, argv[1]);
1565 if (slen <= 0) return -1;
1566
1567 set_relative_attr = false;
1568 }
1569
1570 if (!fr_cond_assert(parent)) return -1; /* Should have provided us with a parent */
1571
1572 /*
1573 * Members of a 'struct' MUST use MEMBER, not ATTRIBUTE.
1574 */
1575 if (parent->type == FR_TYPE_STRUCT) {
1576 fr_strerror_printf("Member %s of ATTRIBUTE %s type 'struct' MUST use the \"MEMBER\" keyword",
1577 argv[0], parent->name);
1578 return -1;
1579 }
1580
1581 /*
1582 * A UNION can have child ATTRIBUTEs
1583 */
1584 if (parent->type == FR_TYPE_UNION) {
1586
1587 /*
1588 * The parent is a union. Get and verify the key ref.
1589 */
1591 fr_assert(ext != NULL);
1592
1593 /*
1594 * Double-check names against the reference.
1595 */
1596 key = ext->ref;
1597 fr_assert(key);
1599 }
1600
1601 da = dict_attr_alloc_null(dctx->dict->pool, dctx->dict->proto);
1602 if (unlikely(!da)) return -1;
1603
1604 /*
1605 * Record the attribute number BEFORE we parse the type and flags.
1606 *
1607 * This is needed for the DER dictionaries, and 'option'.
1608 *
1609 * It can also be useful for other protocols, which may
1610 * have restrictions on the various fields. It is
1611 * therefore useful to have all fields initialized before
1612 * the type/flag validation routines are called.
1613 */
1614 if (unlikely(dict_attr_num_init(da, attr) < 0)) {
1615 error:
1616 talloc_free(da);
1617 return -1;
1618 }
1619
1620 /*
1621 * Check the attribute number against the allowed values.
1622 */
1623 if (key) {
1624 fr_value_box_init(&box, FR_TYPE_UINT32, NULL, false);
1625 box.vb_uint32 = attr;
1626
1627 if (fr_value_box_cast_in_place(da, &box, key->type, NULL) < 0) {
1628 fr_strerror_printf_push("Invalid attribute number as key field %s has data type %s",
1629 key->name, fr_type_to_str(key->type));
1630 goto error;
1631 }
1632 }
1633
1634 if (dict_read_process_common(dctx, &da, parent, argv[0], argv[2],
1635 (argc > 3) ? argv[3] : NULL, base_flags) < 0) {
1636 goto error;
1637 }
1638
1639 if (da_is_bit_field(da)) {
1640 fr_strerror_const("Bit fields can only be defined as a MEMBER of data type 'struct'");
1641 goto error;
1642 }
1643
1644 /*
1645 * Unions need a key field. And key fields can only appear inside of a struct.
1646 */
1647 if (da->type == FR_TYPE_UNION) {
1648 fr_strerror_const("ATTRIBUTEs of type 'union' can only be defined as a MEMBER of data type 'struct'");
1649 goto error;
1650 }
1651
1652 /*
1653 * Cross-check fixed lengths.
1654 */
1655 if (key && (parent->flags.is_known_width)) {
1656 if (!da->flags.is_known_width) {
1657 da->flags.is_known_width = 1;
1658 da->flags.length = parent->flags.length;
1659
1660 } else if (da->flags.length != parent->flags.length) {
1661 fr_strerror_printf("Invalid length %u for struct, the parent union %s has a different length %u",
1662 da->flags.length, parent->name, parent->flags.length);
1663 goto error;
1664 }
1665 }
1666
1667#ifdef WITH_DICTIONARY_WARNINGS
1668 /*
1669 * Hack to help us discover which vendors have illegal
1670 * attributes.
1671 */
1672 if (!vendor && (attr < 256) &&
1673 !strstr(fn, "rfc") && !strstr(fn, "illegal")) {
1674 fprintf(stderr, "WARNING: Illegal attribute %s in %s\n",
1675 argv[0], fn);
1676 }
1677#endif
1678
1679 /*
1680 * Set the attribute name
1681 */
1682 if (unlikely(dict_attr_finalise(&da, argv[0]) < 0)) {
1683 goto error;
1684 }
1685
1686 /*
1687 * Check to see if this is a duplicate attribute
1688 * and whether we should ignore it or error out...
1689 */
1690 switch (dict_attr_allow_dup(da)) {
1691 case 1:
1692 break;
1693
1694 case 0:
1695 talloc_free(da);
1696 return 0;
1697
1698 default:
1699 goto error;
1700 }
1701
1702 /*
1703 * Add the attribute we allocated earlier
1704 */
1705 switch (dict_attr_add_or_fixup(&dctx->fixup, &da)) {
1706 default:
1707 goto error;
1708
1709 /* New attribute, fixup stack */
1710 case 0:
1711 /*
1712 * Dynamically define where VSAs go. Note that we CANNOT
1713 * define VSAs until we define an attribute of type VSA!
1714 */
1715 if (da->type == FR_TYPE_VSA) {
1716 if (parent->flags.is_root) dctx->dict->vsa_parent = attr;
1717
1718 if (dict_fixup_vsa_enqueue(&dctx->fixup, da) < 0) {
1719 return -1; /* Leaves attr added */
1720 }
1721 }
1722
1723 /*
1724 * Add the VALUE to the key attribute, and ensure that
1725 * the VALUE also contains a pointer to the child struct.
1726 */
1727 if (key && (dict_attr_enum_add_name(fr_dict_attr_unconst(key), da->name, &box, false, true, da) < 0)) {
1728 return -1; /* Leaves attr added */
1729 }
1730
1731 /*
1732 * Adding an attribute of type 'struct' is an implicit
1733 * BEGIN-STRUCT.
1734 */
1735 if (da->type == FR_TYPE_STRUCT) {
1736 if (dict_dctx_push(dctx, da, NEST_NONE) < 0) return -1;
1737
1738 CURRENT_FRAME(dctx)->finalise = dict_struct_finalise;
1739 dctx->value_attr = NULL;
1740 } else {
1741 dctx->value_attr = da;
1742 }
1743
1744 if (set_relative_attr) dctx->relative_attr = da;
1745 break;
1746
1747 /* Deferred attribute, don't begin the TLV section automatically */
1748 case 1:
1749 break;
1750 }
1751
1752 /*
1753 * While UNIONs are named, it's nicer to hide them.
1754 * Therefore we automatically add an ALIAS in the unions
1755 * parent, for the child in the union.
1756 */
1757 if (parent->type == FR_TYPE_UNION) {
1758 fr_assert(parent->parent);
1759
1760 if (dict_attr_alias_add(parent->parent, da->name, da, false) < 0) {
1761 return -1; /* Leaves attr added */
1762 }
1763 }
1764
1765 return 0;
1766}
1767
1768static int dict_read_process_begin(dict_tokenize_ctx_t *dctx, char **argv, int argc, UNUSED fr_dict_attr_flags_t *base_flags)
1769{
1770 dict_tokenize_frame_t const *frame;
1771 fr_dict_attr_t const *da;
1772 fr_dict_attr_t const *common;
1773
1774 dctx->value_attr = NULL;
1775 dctx->relative_attr = NULL;
1776
1777 if (argc != 1) {
1778 fr_strerror_const("Invalid BEGIN keyword. Expected BEGIN <name>");
1779 return -1;
1780 }
1781
1783 if (!fr_cond_assert_msg(frame, "Context stack doesn't have an attribute or dictionary "
1784 "root to begin searching from %s[%d]", CURRENT_FILENAME(dctx), CURRENT_LINE(dctx)) ||
1785 !fr_cond_assert_msg(fr_type_is_structural(frame->da->type), "Context attribute is not structural %s[%d]",
1786 CURRENT_FILENAME(dctx), CURRENT_LINE(dctx))) {
1787 return -1;
1788 }
1789
1790 /*
1791 * Not really a reference as we don't support any of the
1792 * fancy syntaxes like refs do. A straight OID string
1793 * resolved from the current level of nesting is all we support.
1794 */
1795 da = fr_dict_attr_by_oid(NULL, frame->da, argv[0]);
1796 if (!da) {
1797 fr_strerror_printf("BEGIN %s is not resolvable in current context '%s'", argv[0], frame->da->name);
1798 return -1;
1799 }
1800
1801 /*
1802 * We cannot use BEGIN/END on structs. Once they're defined, they can't be modified.
1803 *
1804 * This restriction can be lifted once we don't auto-push on FR_TYPE_STRUCT.
1805 */
1806 if (!fr_type_is_tlv(da->type) && (da->type != FR_TYPE_UNION)) {
1807 fr_strerror_printf("BEGIN %s cannot be used with data type '%s'",
1808 argv[0],
1809 fr_type_to_str(da->type));
1810 return -1;
1811 }
1812
1813 common = fr_dict_attr_common_parent(frame->da, da, true);
1814 if (!common) {
1815 fr_strerror_printf("BEGIN %s should be a child of '%s'",
1816 argv[0], CURRENT_FRAME(dctx)->da->name);
1817 return -1;
1818 }
1819
1820 return dict_dctx_push(dctx, da, NEST_ATTRIBUTE);
1821}
1822
1823static int dict_read_process_begin_protocol(dict_tokenize_ctx_t *dctx, char **argv, int argc,
1824 UNUSED fr_dict_attr_flags_t *base_flags)
1825{
1826 fr_dict_t *found;
1827 dict_tokenize_frame_t const *frame;
1828
1829 dctx->value_attr = NULL;
1830 dctx->relative_attr = NULL;
1831
1832 if (argc != 1) {
1833 fr_strerror_const("Invalid BEGIN-PROTOCOL entry");
1834 return -1;
1835 }
1836
1837 /*
1838 * If we're not parsing in the context of the internal
1839 * dictionary, then we don't allow BEGIN-PROTOCOL
1840 * statements.
1841 */
1842 if (dctx->dict != dict_gctx->internal) {
1843 fr_strerror_const("Nested BEGIN-PROTOCOL statements are not allowed");
1844 return -1;
1845 }
1846
1847 found = dict_by_protocol_name(argv[0]);
1848 if (!found) {
1849 fr_strerror_printf("Unknown protocol '%s'", argv[0]);
1850 return -1;
1851 }
1852
1854 if (frame) {
1855 fr_strerror_printf("BEGIN-PROTOCOL cannot be used inside of any other BEGIN/END block. Previous definition is at %s[%d]",
1856 frame->filename, frame->line);
1857 return -1;
1858 }
1859
1860 /*
1861 * Add a temporary fixup pool
1862 */
1863 if (dict_fixup_init(&dctx->fixup) < 0) return -1;
1864
1865 /*
1866 * We're in the middle of loading this dictionary. Tell
1867 * fr_dict_protocol_afrom_file() to suppress recursive references.
1868 */
1869 found->loading = true;
1870
1871 dctx->dict = found;
1872
1873 return dict_dctx_push(dctx, dctx->dict->root, NEST_PROTOCOL);
1874}
1875
1876static int dict_read_process_begin_vendor(dict_tokenize_ctx_t *dctx, char **argv, int argc,
1877 UNUSED fr_dict_attr_flags_t *base_flags)
1878{
1879 fr_dict_vendor_t const *vendor;
1880
1881 fr_dict_attr_t const *vsa_da;
1882 fr_dict_attr_t const *vendor_da;
1883 fr_dict_attr_t *new;
1884 dict_tokenize_frame_t const *frame;
1885 char *p;
1886
1887 dctx->value_attr = NULL;
1888 dctx->relative_attr = NULL;
1889
1890 if (argc < 1) {
1891 fr_strerror_const("Invalid BEGIN-VENDOR entry");
1892 return -1;
1893 }
1894
1895 vendor = fr_dict_vendor_by_name(dctx->dict, argv[0]);
1896 if (!vendor) {
1897 fr_strerror_printf("Unknown vendor '%s'", argv[0]);
1898 return -1;
1899 }
1900
1901 /*
1902 * Check for extended attr VSAs
1903 *
1904 * BEGIN-VENDOR foo parent=Foo-Encapsulation-Attr
1905 */
1906 if (argc > 1) {
1907 fr_dict_attr_t const *da;
1908
1909 if (strncmp(argv[1], "parent=", 7) != 0) {
1910 fr_strerror_const("BEGIN-VENDOR invalid argument - expected 'parent='");
1911 return -1;
1912 }
1913
1914 p = argv[1] + 7;
1915 da = fr_dict_attr_by_oid(NULL, CURRENT_FRAME(dctx)->da, p);
1916 if (!da) {
1917 fr_strerror_printf("BEGIN-VENDOR Failed to find attribute '%s'", p);
1918 return -1;
1919 }
1920
1921 if (da->type != FR_TYPE_VSA) {
1922 fr_strerror_printf("Invalid parent for BEGIN-VENDOR. "
1923 "Attribute '%s' should be 'vsa' but is '%s'", p,
1924 fr_type_to_str(da->type));
1925 return -1;
1926 }
1927
1928 vsa_da = da;
1929
1930 } else if (dctx->dict->vsa_parent) {
1931 /*
1932 * Check that the protocol-specific VSA parent exists.
1933 */
1934 vsa_da = dict_attr_child_by_num(CURRENT_FRAME(dctx)->da, dctx->dict->vsa_parent);
1935 if (!vsa_da) {
1936 fr_strerror_printf("Failed finding VSA parent for Vendor %s",
1937 vendor->name);
1938 return -1;
1939 }
1940
1941 } else if (dctx->dict->string_based) {
1942 vsa_da = dctx->dict->root;
1943
1944 } else {
1945 fr_strerror_printf("BEGIN-VENDOR is forbidden for protocol %s - it has no ATTRIBUTE of type 'vsa'",
1946 dctx->dict->root->name);
1947 return -1;
1948 }
1949
1950 frame = dict_dctx_find_frame(dctx, NEST_VENDOR);
1951 if (frame) {
1952 fr_strerror_printf("Nested BEGIN-VENDOR is forbidden. Previous definition is at %s[%d]",
1953 frame->filename, frame->line);
1954 return -1;
1955 }
1956
1957 /*
1958 * Check if the VENDOR attribute exists under this VSA. If not, create one.
1959 *
1960 * @todo - There are no vendor fixups, so if the vendor has unusual type sizes, it MUST be
1961 * defined before the BEGIN-VENDOR is used.
1962 */
1963 vendor_da = dict_attr_child_by_num(vsa_da, vendor->pen);
1964 if (!vendor_da) {
1965 fr_dict_attr_flags_t flags = {};
1966
1967 new = dict_attr_alloc(dctx->dict->pool,
1968 vsa_da, argv[0], vendor->pen, FR_TYPE_VENDOR,
1969 &(dict_attr_args_t){ .flags = &flags });
1970 if (unlikely(!new)) return -1;
1971
1972 if (dict_attr_child_add(UNCONST(fr_dict_attr_t *, vsa_da), new) < 0) {
1973 talloc_free(new);
1974 return -1;
1975 }
1976
1977 if (dict_attr_add_to_namespace(UNCONST(fr_dict_attr_t *, vsa_da), new) < 0) {
1978 return -1; /* leaves attr added */
1979 }
1980
1981 vendor_da = new;
1982 } else {
1983 fr_assert(vendor_da->type == FR_TYPE_VENDOR);
1984 }
1985
1986 return dict_dctx_push(dctx, vendor_da, NEST_VENDOR);
1987}
1988
1989/*
1990 * Process the DEFINE command
1991 *
1992 * Which is mostly like ATTRIBUTE, but does not have a number.
1993 */
1994static int dict_read_process_define(dict_tokenize_ctx_t *dctx, char **argv, int argc,
1995 fr_dict_attr_flags_t *base_flags)
1996{
1997 fr_dict_attr_t const *parent;
1998 fr_dict_attr_t *da = NULL;
1999 dict_tokenize_frame_t const *frame;
2000
2001 if ((argc < 2) || (argc > 3)) {
2002 fr_strerror_const("Invalid DEFINE syntax");
2003 return -1;
2004 }
2005
2006 frame = dict_dctx_unwind(dctx);
2007 if (!fr_cond_assert(frame && frame->da)) return -1; /* Should have provided us with a parent */
2008
2009 parent = frame->da;
2010
2011 /*
2012 * Members of a 'struct' MUST use MEMBER, not ATTRIBUTE.
2013 */
2014 if (parent->type == FR_TYPE_STRUCT) {
2015 fr_strerror_printf("Member %s of parent %s type 'struct' MUST use the \"MEMBER\" keyword",
2016 argv[0], parent->name);
2017 return -1;
2018 }
2019
2020 if (parent->type == FR_TYPE_UNION) {
2021 fr_strerror_printf("Parent attribute %s is of type 'union', and cannot use DEFINE for children",
2022 parent->name);
2023 return -1;
2024 }
2025
2026 /*
2027 * We don't set the attribute number before parsing the
2028 * type and flags. The number is chosen internally, and
2029 * no one should depend on it.
2030 */
2031 if (dict_read_process_common(dctx, &da, parent, argv[0], argv[1],
2032 (argc > 2) ? argv[2] : NULL, base_flags) < 0) {
2033 return -1;
2034 }
2035
2036 /*
2037 * Certain structural types MUST have numbers.
2038 */
2039 switch (da->type) {
2040 case FR_TYPE_VSA:
2041 case FR_TYPE_VENDOR:
2042 fr_strerror_printf("DEFINE cannot be used for type '%s'", argv[1]);
2043 error:
2044 talloc_free(da);
2045 return -1;
2046
2047 default:
2048 break;
2049 }
2050
2051 if (da_is_bit_field(da)) {
2052 fr_strerror_const("Bit fields can only be defined as a MEMBER of data type 'struct'");
2053 goto error;
2054 }
2055
2056#ifdef STATIC_ANALYZER
2057 if (!dctx->dict) goto error;
2058#endif
2059
2060 /*
2061 * Since there is no number, the attribute cannot be
2062 * encoded as a number.
2063 */
2064 da->flags.name_only = true;
2065
2066 /*
2067 * Add an attribute number now so the allocations occur in order
2068 */
2069 if (unlikely(dict_attr_num_init_name_only(da) < 0)) goto error;
2070
2071 /*
2072 * Set the attribute name
2073 */
2074 if (unlikely(dict_attr_finalise(&da, argv[0]) < 0)) goto error;
2075
2076 /*
2077 * Check to see if this is a duplicate attribute
2078 * and whether we should ignore it or error out...
2079 */
2080 switch (dict_attr_allow_dup(da)) {
2081 case 1:
2082 break;
2083
2084 case 0:
2085 talloc_free(da);
2086 return 0;
2087
2088 default:
2089 goto error;
2090 }
2091
2092 /*
2093 * Add the attribute we allocated earlier
2094 */
2095 switch (dict_attr_add_or_fixup(&dctx->fixup, &da)) {
2096 default:
2097 goto error;
2098
2099 /* New attribute, fixup stack */
2100 case 0:
2101 if (dict_set_value_attr(dctx, da) < 0) return -1;
2102
2103 if (da->type == FR_TYPE_TLV) {
2104 dctx->relative_attr = da;
2105 } else {
2106 dctx->relative_attr = NULL;
2107 }
2108 break;
2109
2110 /* Deferred attribute, don't begin the TLV section automatically */
2111 case 1:
2112 break;
2113 }
2114
2115 return 0;
2116}
2117
2118static int dict_read_process_end_protocol(dict_tokenize_ctx_t *dctx, char **argv, int argc,
2119 fr_dict_attr_flags_t *base_flags);
2120
2121static int dict_read_process_end_vendor(dict_tokenize_ctx_t *dctx, char **argv, int argc,
2122 fr_dict_attr_flags_t *base_flags);
2123
2124static int dict_read_process_end(dict_tokenize_ctx_t *dctx, char **argv, int argc,
2125 fr_dict_attr_flags_t *base_flags)
2126{
2127 fr_dict_attr_t const *current;
2128 fr_dict_attr_t const *da;
2129 dict_tokenize_frame_t const *frame;
2130
2131 dctx->value_attr = NULL;
2132 dctx->relative_attr = NULL;
2133
2134 if (argc > 2) {
2135 fr_strerror_const("Invalid END syntax, expected END <ref>");
2136 return -1;
2137 }
2138
2139 /*
2140 * Allow for the obvious.
2141 */
2142 if (strcmp(argv[0], "PROTOCOL") == 0) return dict_read_process_end_protocol(dctx, argv + 1, argc - 1, base_flags);
2143
2144 if (strcmp(argv[0], "VENDOR") == 0) return dict_read_process_end_vendor(dctx, argv + 1, argc - 1, base_flags);
2145
2146 /*
2147 * Unwind until we hit an attribute nesting section
2148 */
2150 return -1;
2151 }
2152
2153 /*
2154 * Pop the stack to get the attribute we're ending.
2155 */
2156 current = dict_dctx_pop(dctx)->da;
2157
2158 /*
2159 * No checks on the attribute, we're just popping _A_ frame,
2160 * we don't care what attribute it represents.
2161 */
2162 if (argc == 0) return 0;
2163
2164 /*
2165 * This is where we'll have begun the previous search to
2166 * evaluate the BEGIN keyword.
2167 */
2169 if (!fr_cond_assert(frame)) return -1;
2170
2171 da = fr_dict_attr_by_oid(NULL, frame->da, argv[0]);
2172 if (!da) {
2173 fr_strerror_const_push("Failed resolving attribute in BEGIN entry");
2174 return -1;
2175 }
2176
2177 if (da != current) {
2178 fr_strerror_printf("END %s does not match previous BEGIN %s", argv[0], current->name);
2179 return -1;
2180 }
2181
2182 return 0;
2183}
2184
2185static int dict_read_process_end_protocol(dict_tokenize_ctx_t *dctx, char **argv, int argc,
2186 UNUSED fr_dict_attr_flags_t *base_flags)
2187{
2188 fr_dict_t const *found;
2189
2190 dctx->value_attr = NULL;
2191 dctx->relative_attr = NULL;
2192
2193 if (argc != 1) {
2194 fr_strerror_const("Invalid END-PROTOCOL entry");
2195 return -1;
2196 }
2197
2198 found = dict_by_protocol_name(argv[0]);
2199 if (!found) {
2200 fr_strerror_printf("END-PROTOCOL %s does not refer to a valid protocol", argv[0]);
2201 return -1;
2202 }
2203
2204 if (found != dctx->dict) {
2205 fr_strerror_printf("END-PROTOCOL %s does not match previous BEGIN-PROTOCOL %s",
2206 argv[0], dctx->dict->root->name);
2207 return -1;
2208 }
2209
2210 /*
2211 * Unwind until we get to a BEGIN-PROTOCOL nesting.
2212 */
2214 return -1;
2215 }
2216
2217 if (found->root != CURRENT_FRAME(dctx)->da) {
2218 fr_strerror_printf("END-PROTOCOL %s does not match previous BEGIN-PROTOCOL %s", argv[0],
2219 CURRENT_FRAME(dctx)->da->name);
2220 return -1;
2221 }
2222
2223 /*
2224 * Applies fixups to any attributes added to the protocol
2225 * dictionary. Note that the finalise function prints
2226 * out the original filename / line of the error. So we
2227 * don't need to do that here.
2228 */
2229 if (dict_finalise(dctx) < 0) return -1;
2230
2232
2233 fr_assert(!dctx->stack[dctx->stack_depth].finalise);
2234 dctx->stack_depth--; /* nuke the BEGIN-PROTOCOL */
2235
2237 dctx->dict = dict_gctx->internal;
2238
2239 return 0;
2240}
2241
2242static int dict_read_process_end_vendor(dict_tokenize_ctx_t *dctx, char **argv, int argc,
2243 UNUSED fr_dict_attr_flags_t *base_flags)
2244{
2245 fr_dict_vendor_t const *vendor;
2246
2247 dctx->value_attr = NULL;
2248 dctx->relative_attr = NULL;
2249
2250 if (argc != 1) {
2251 fr_strerror_const("END-VENDOR is missing vendor name");
2252 return -1;
2253 }
2254
2255 vendor = fr_dict_vendor_by_name(dctx->dict, argv[0]);
2256 if (!vendor) {
2257 fr_strerror_printf("Unknown vendor '%s'", argv[0]);
2258 return -1;
2259 }
2260
2261 /*
2262 * Unwind until we get to a BEGIN-VENDOR nesting.
2263 */
2264 if (!dict_dctx_unwind_until(dctx, NEST_VENDOR)) {
2265 return -1;
2266 }
2267
2268 if (vendor->pen != CURRENT_FRAME(dctx)->da->attr) {
2269 fr_strerror_printf("END-VENDOR %s does not match previous BEGIN-VENDOR %s", argv[0],
2270 CURRENT_FRAME(dctx)->da->name);
2271 return -1;
2272 }
2273
2274 fr_assert(!dctx->stack[dctx->stack_depth].finalise);
2275 dctx->stack_depth--; /* nuke the BEGIN-VENDOR */
2276
2277 return 0;
2278}
2279
2280/*
2281 * Process the ENUM command
2282 */
2283static int dict_read_process_enum(dict_tokenize_ctx_t *dctx, char **argv, int argc,
2284 fr_dict_attr_flags_t *base_flags)
2285{
2286 fr_dict_attr_t const *parent;
2287 fr_dict_attr_t *da = NULL;
2288
2289 if (argc != 2) {
2290 fr_strerror_const("Invalid ENUM syntax");
2291 return -1;
2292 }
2293
2294 /*
2295 * Dictionaries need to have real names, not shitty ones.
2296 */
2297 if (strncmp(argv[0], "Attr-", 5) == 0) {
2298 fr_strerror_const("Invalid ENUM name");
2299 return -1;
2300 }
2301
2302#ifdef STATIC_ANALYZER
2303 if (!dctx->dict) goto error;
2304#endif
2305
2306 /*
2307 * Allocate the attribute here, and then fill in the fields
2308 * as we start parsing the various elements of the definition.
2309 */
2310 da = dict_attr_alloc_null(dctx->dict->pool, dctx->dict->proto);
2311 if (unlikely(da == NULL)) return -1;
2312 dict_attr_location_set(dctx, da);
2313 da->dict = dctx->dict;
2314
2315 /*
2316 * Set the attribute flags from the base flags.
2317 */
2318 memcpy(&da->flags, base_flags, sizeof(da->flags));
2319
2320 da->flags.name_only = true; /* values for ENUM are irrelevant */
2321 da->flags.internal = true; /* ENUMs will never get encoded into a protocol */
2322#if 0
2323 flags.is_enum = true; /* it's an enum, and can't be assigned to a #fr_pair_t */
2324#endif
2325
2326 /*
2327 * Set the base type of the attribute.
2328 */
2329 if (dict_process_type_field(dctx, argv[1], &da) < 0) {
2330 error:
2331 talloc_free(da);
2332 return -1;
2333 }
2334
2335 if (da_is_bit_field(da)) {
2336 fr_strerror_const("Bit fields can only be defined as a MEMBER of a data type 'struct'");
2337 goto error;
2338 }
2339
2340 switch (da->type) {
2341 case FR_TYPE_LEAF:
2342 break;
2343
2344 default:
2345 fr_strerror_printf("ENUMs can only be a leaf type, not %s",
2346 fr_type_to_str(da->type));
2347 goto error;
2348 }
2349
2350 parent = CURRENT_FRAME(dctx)->da;
2351 if (!parent) {
2352 fr_strerror_const("Invalid location for ENUM");
2353 goto error;
2354 }
2355
2356 /*
2357 * ENUMs cannot have a flag field, so we don't parse that.
2358 *
2359 * Maybe we do want a flag field for named time deltas?
2360 */
2361
2362 if (unlikely(dict_attr_parent_init(&da, parent) < 0)) goto error;
2363 if (unlikely(dict_attr_finalise(&da, argv[0]) < 0)) goto error;
2364
2365 /*
2366 * Add the attribute we allocated earlier
2367 */
2368 switch (dict_attr_add_or_fixup(&dctx->fixup, &da)) {
2369 default:
2370 goto error;
2371
2372 case 0:
2373 memcpy(&dctx->value_attr, &da, sizeof(da));
2374 break;
2375
2376 case 1:
2377 break;
2378 }
2379
2380 return 0;
2381}
2382
2383/*
2384 * Process the FLAGS command
2385 */
2386static int dict_read_process_flags(UNUSED dict_tokenize_ctx_t *dctx, char **argv, int argc,
2387 fr_dict_attr_flags_t *base_flags)
2388{
2389 bool sense = true;
2390
2391 if (argc == 1) {
2392 char *p;
2393
2394 p = argv[0];
2395 if (*p == '!') {
2396 sense = false;
2397 p++;
2398 }
2399
2400 if (strcmp(p, "internal") == 0) {
2401 base_flags->internal = sense;
2402 return 0;
2403 }
2404 }
2405
2406 fr_strerror_const("Invalid FLAGS syntax");
2407 return -1;
2408}
2409
2410/*
2411 * Process the MEMBER command
2412 */
2413static int dict_read_process_member(dict_tokenize_ctx_t *dctx, char **argv, int argc,
2414 fr_dict_attr_flags_t *base_flags)
2415{
2416 fr_dict_attr_t *da = NULL;
2417
2418 if ((argc < 2) || (argc > 3)) {
2419 fr_strerror_const("Invalid MEMBER syntax");
2420 return -1;
2421 }
2422
2423 if (CURRENT_FRAME(dctx)->da->type != FR_TYPE_STRUCT) {
2424 fr_strerror_printf("MEMBER can only be used for ATTRIBUTEs of type 'struct', not for data type %s",
2425 fr_type_to_str(CURRENT_FRAME(dctx)->da->type));
2426 return -1;
2427 }
2428
2429 /*
2430 * Check if the parent 'struct' is fixed size. And if
2431 * so, complain if we're adding a variable sized member.
2432 */
2433 if (CURRENT_FRAME(dctx)->struct_is_closed) {
2434 fr_strerror_printf("Cannot add MEMBER to 'struct' %s after a variable sized member %s",
2435 CURRENT_FRAME(dctx)->da->name,
2436 CURRENT_FRAME(dctx)->struct_is_closed->name);
2437 return -1;
2438 }
2439
2440 /*
2441 * We don't set the attribute number before parsing the
2442 * type and flags. The number is chosen internally, and
2443 * no one should depend on it.
2444 *
2445 * Although _arguably_, it may be useful to know which
2446 * field this is, 0..N?
2447 */
2448 if (dict_read_process_common(dctx, &da, CURRENT_FRAME(dctx)->da, argv[0], argv[1],
2449 (argc > 2) ? argv[2] : NULL, base_flags) < 0) {
2450 return -1;
2451 }
2452
2453#ifdef STATIC_ANALYZER
2454 if (!dctx->dict) goto error;
2455#endif
2456
2457 /*
2458 * If our parent is a known width struct, then we're
2459 * allowed to be variable width. The parent might just
2460 * have a "length=16" prefix, which lets its children be
2461 * variable sized.
2462 */
2463
2464 /*
2465 * Double check any bit field magic
2466 */
2467 if (CURRENT_FRAME(dctx)->member_num > 0) {
2468 fr_dict_attr_t const *previous;
2469
2470 previous = dict_attr_child_by_num(CURRENT_FRAME(dctx)->da,
2471 CURRENT_FRAME(dctx)->member_num);
2472 /*
2473 * Check that the previous bit field ended on a
2474 * byte boundary.
2475 *
2476 * Note that the previous attribute might be a deferred TLV, in which case it doesn't
2477 * exist. That's fine.
2478 */
2479 if (previous && da_is_bit_field(previous)) {
2480 /*
2481 * This attribute is a bit field. Keep
2482 * track of where in the byte we are
2483 * located.
2484 */
2485 if (da_is_bit_field(da)) {
2486 da->flags.flag_byte_offset = (da->flags.length + previous->flags.flag_byte_offset) & 0x07;
2487
2488 } else {
2489 if (previous->flags.flag_byte_offset != 0) {
2490 fr_strerror_printf("Previous bitfield %s did not end on a byte boundary",
2491 previous->name);
2492 error:
2493 talloc_free(da);
2494 return -1;
2495 }
2496 }
2497 }
2498 }
2499
2500 /*
2501 * Ensure that no previous child has "key" or "length" set.
2502 */
2503 if (da->type == FR_TYPE_TLV) {
2504 fr_dict_attr_t const *key;
2505 int i;
2506
2507 /*
2508 * @todo - cache the key field in the stack frame, so we don't have to loop over the children.
2509 */
2510 for (i = 1; i <= CURRENT_FRAME(dctx)->member_num; i++) {
2511 key = dict_attr_child_by_num(CURRENT_FRAME(dctx)->da, i);
2512 if (!key) continue; /* really should be WTF? */
2513
2514 /*
2515 * @todo - we can allow this if the _rest_ of the struct is fixed size, i.e. if
2516 * there is a key field, and then the union is fixed size.
2517 */
2518 if (fr_dict_attr_is_key_field(key)) {
2519 fr_strerror_printf("'struct' %s has a 'key' field %s, and cannot end with a TLV %s",
2520 CURRENT_FRAME(dctx)->da->name, key->name, argv[0]);
2521 goto error;
2522 }
2523
2524 if (da_is_length_field(key)) {
2525 fr_strerror_printf("'struct' %s has a 'length' field %s, and cannot end with a TLV %s",
2526 CURRENT_FRAME(dctx)->da->name, key->name, argv[0]);
2527 goto error;
2528 }
2529 }
2530
2531 /*
2532 * TLVs are variable sized, and close the parent struct.
2533 */
2534 CURRENT_FRAME(dctx)->struct_is_closed = da;
2535 }
2536
2537 /*
2538 * Unions close the parent struct, even if they're fixed size. For now, the struct to/from
2539 * network code assumes that a union is the last member of a structure.
2540 */
2541 if (da->type == FR_TYPE_UNION) {
2542 CURRENT_FRAME(dctx)->struct_is_closed = da;
2543 }
2544
2545 if (unlikely(dict_attr_num_init(da, CURRENT_FRAME(dctx)->member_num + 1) < 0)) goto error;
2546 if (unlikely(dict_attr_finalise(&da, argv[0]) < 0)) goto error;
2547
2548 /*
2549 * Check to see if this is a duplicate attribute
2550 * and whether we should ignore it or error out...
2551 */
2552 switch (dict_attr_allow_dup(da)) {
2553 case 1:
2554 break;
2555
2556 case 0:
2557 talloc_free(da);
2558 return 0;
2559
2560 default:
2561 goto error;
2562 }
2563
2564 switch (dict_attr_add_or_fixup(&dctx->fixup, &da)) {
2565 default:
2566 goto error;
2567
2568 case 1:
2569 /*
2570 * @todo - a MEMBER can theoretically have a "ref=..", though non currently do.
2571 *
2572 * If the ref is deferred, then we cannot finalise the parent struct until we have
2573 * resolved the reference. But the "finalise struct on fixup" code isn't written. So
2574 * instead of silently doing the wrong thing, we just return an error.
2575 */
2576 fr_strerror_printf("Cannot have MEMBER with deferred ref=...");
2577 return -1;
2578
2579 case 0:
2580 /*
2581 * New attribute - avoid lots of indentation.
2582 */
2583 break;
2584 }
2585
2586 /*
2587 * Check if this MEMBER closes the structure.
2588 *
2589 * Close this struct if the child struct is variable sized. For now, it we only support
2590 * child structs at the end of the parent.
2591 *
2592 * The solution is to update the unwind() function to check if the da we've
2593 * unwound to is a struct, and then if so... get the last child, and mark it
2594 * closed.
2595 *
2596 * @todo - a MEMBER which is of type 'struct' and has 'clone=foo', we delay the clone
2597 * until after all of the dictionaries have been loaded. As such, this attribute
2598 * is unknown width, and MUST be at the end of the parent structure.
2599 *
2600 * If the cloned MEMBER is in the middle of a structure, then the user will get an opaque
2601 * error. But that case should be rare.
2602 */
2603 if (!da->flags.is_known_width) {
2604 /*
2605 * The child is unknown width, but we were told that the parent has known width.
2606 * That's an error.
2607 */
2608 if (CURRENT_FRAME(dctx)->da->flags.length) {
2609 fr_strerror_printf("'struct' %s has fixed size %u, but member %s is of unknown size",
2610 CURRENT_FRAME(dctx)->da->name, CURRENT_FRAME(dctx)->da->flags.length,
2611 argv[0]);
2612 return -1;
2613 }
2614
2615 /*
2616 * Mark the structure as closed by this attribute. And then set the size to
2617 * zero, for "unknown size".
2618 */
2619 CURRENT_FRAME(dctx)->struct_is_closed = da;
2620 CURRENT_FRAME(dctx)->struct_size = 0;
2621
2622 /*
2623 * A 'struct' can have a MEMBER of type 'tlv', but ONLY
2624 * as the last entry in the 'struct'. If we see that,
2625 * set the previous attribute to the TLV we just added.
2626 * This allows the children of the TLV to be parsed as
2627 * partial OIDs, so we don't need to know the full path
2628 * to them.
2629 */
2630 if (da->type == FR_TYPE_TLV) {
2631 dctx->relative_attr = da;
2632 if (dict_dctx_push(dctx, dctx->relative_attr, NEST_NONE) < 0) return -1;
2633 }
2634
2635 } else if (CURRENT_FRAME(dctx)->da->flags.length) {
2636 /*
2637 * The parent is fixed size, so we track the length of the children.
2638 */
2639 CURRENT_FRAME(dctx)->struct_size += da->flags.length;
2640
2641 /*
2642 * Adding this child may result in an overflow, so we check that.
2643 */
2644 if (CURRENT_FRAME(dctx)->struct_size > CURRENT_FRAME(dctx)->da->flags.length) {
2645 fr_strerror_printf("'struct' %s has fixed size %u, but member %s overflows that length",
2646 CURRENT_FRAME(dctx)->da->name, CURRENT_FRAME(dctx)->da->flags.length,
2647 argv[0]);
2648 return -1;
2649 }
2650 }
2651
2652 /*
2653 * Now that we know everything is OK, we can increase the number.
2654 */
2655 CURRENT_FRAME(dctx)->member_num++;
2656
2657 /*
2658 * Set or clear the attribute for VALUE statements.
2659 */
2660 return dict_set_value_attr(dctx, da);
2661}
2662
2663
2664/** Process a value alias
2665 *
2666 */
2667static int dict_read_process_value(dict_tokenize_ctx_t *dctx, char **argv, int argc,
2668 UNUSED fr_dict_attr_flags_t *base_flags)
2669{
2670 fr_dict_attr_t *da;
2672 size_t enum_len;
2673 fr_dict_attr_t const *parent = CURRENT_FRAME(dctx)->da;
2674 fr_dict_attr_t const *enumv = NULL;
2675
2676 if (argc != 3) {
2677 fr_strerror_const("Invalid VALUE syntax");
2678 return -1;
2679 }
2680
2681 /*
2682 * Most VALUEs are bunched together by ATTRIBUTE. We can
2683 * save a lot of lookups on dictionary initialization by
2684 * caching the last attribute for a VALUE.
2685 *
2686 * If it's not the same, we look up the attribute in the
2687 * current context, which is generally:
2688 *
2689 * * the current attribute of type `struct`
2690 * * if no `struct`, then the VENDOR for VSAs
2691 * * if no VENDOR, then the dictionary root
2692 */
2693 if (!dctx->value_attr || (strcasecmp(argv[0], dctx->value_attr->name) != 0)) {
2694 fr_dict_attr_t const *tmp;
2695
2696 if (!(tmp = fr_dict_attr_by_oid(NULL, parent, argv[0]))) goto fixup;
2697 dctx->value_attr = fr_dict_attr_unconst(tmp);
2698 }
2699 da = dctx->value_attr;
2700
2701 /*
2702 * Verify the enum name matches the expected from.
2703 */
2704 enum_len = strlen(argv[1]);
2705 if (fr_dict_enum_name_from_substr(NULL, NULL, &FR_SBUFF_IN(argv[1], enum_len), NULL) != (fr_slen_t) enum_len) {
2706 fr_strerror_printf_push("Invalid VALUE name '%s' for attribute '%s'", argv[1], da->name);
2707 return -1;
2708 }
2709
2710 /*
2711 * enum names cannot be integers. People should just use the integer instead.
2712 *
2713 * But what about IPv6 addresses, which also use a "::" prefix?
2714 *
2715 * The ::FOO addresses were historically part of the "ipv4 compatible ipv6 address" range
2716 * "::0.0.0.0/96". That range has since been deprecated, and the "::FOO" range is tracked in the
2717 * IANA Special-Purpose Address Registry. That lists three things beginning with ::
2718 *
2719 * * ::/128 - unspecified address (i.e. ::0/128).
2720 * * ::1/128 - Loopback address
2721 * * ::ffff:0:0/96 - IPv4-mapped address.
2722 *
2723 * Since IPv6 addresses are 128 bits, the first two are just ::0 and ::1. No other possibilities
2724 * exist.
2725 *
2726 * For the range "::ffff:0:0/96", a value such as "::ffff:192.168.1.2 is not a valid enum name.
2727 * It contains an extra ':' (and MUST contain the extra ':'), and the ':' is not allowed in an
2728 * enum name.
2729 *
2730 * IANA could assign other values in the :: range, but this seems unlikely.
2731 *
2732 * As a result, the only overlap between enum ::FOO and IPv6 addresses is the single case of ::1.
2733 * This check disallows that.
2734 */
2735 if (fr_sbuff_adv_past_allowed( &FR_SBUFF_IN(argv[1], enum_len), SIZE_MAX, sbuff_char_class_int, NULL) == enum_len) {
2736 fr_strerror_printf("Invalid VALUE name '%s' for attribute '%s' - the name cannot be an integer", argv[1], da->name);
2737 return -1;
2738 }
2739
2740 /*
2741 * Remember which attribute is associated with this
2742 * value. This allows us to define enum
2743 * values before the attribute exists, and fix them
2744 * up later.
2745 */
2746 if (!da) {
2747 fixup:
2748 if (!fr_cond_assert_msg(dctx->fixup.pool, "fixup pool context invalid")) return -1;
2749
2751 CURRENT_FILENAME(dctx), CURRENT_LINE(dctx),
2752 argv[0], strlen(argv[0]),
2753 argv[1], strlen(argv[1]),
2754 argv[2], strlen(argv[2]), parent) < 0) {
2755 fr_strerror_const("Out of memory");
2756 return -1;
2757 }
2758 return 0;
2759 }
2760
2761 /*
2762 * Only a leaf types can have values defined.
2763 */
2764 if (!fr_type_is_leaf(da->type)) {
2765 fr_strerror_printf("Cannot define VALUE for attribute '%s' of data type '%s'", da->name,
2766 fr_type_to_str(da->type));
2767 return -1;
2768 }
2769
2770 /*
2771 * Pass in the DA. The value-box parsing functions will figure out where the enums are found.
2772 */
2773 if (da->type == FR_TYPE_ATTR) enumv = da;
2774
2775 if (fr_value_box_from_str(NULL, &value, da->type, enumv,
2776 argv[2], strlen(argv[2]),
2777 NULL) < 0) {
2778 fr_strerror_printf_push("Invalid VALUE '%s' for attribute '%s' of data type '%s'",
2779 argv[2],
2780 da->name,
2781 fr_type_to_str(da->type));
2782 return -1;
2783 }
2784
2785 if (fr_dict_enum_add_name(da, argv[1], &value, false, true) < 0) {
2787 return -1;
2788 }
2790
2791 return 0;
2792}
2793
2794/*
2795 * Process the VENDOR command
2796 */
2797static int dict_read_process_vendor(dict_tokenize_ctx_t *dctx, char **argv, int argc, UNUSED fr_dict_attr_flags_t *base_flags)
2798{
2799 unsigned int value;
2800 int type, length;
2801 bool continuation = false;
2802 fr_dict_vendor_t const *dv;
2803 fr_dict_vendor_t *mutable;
2804 fr_dict_t *dict = dctx->dict;
2805
2806 dctx->value_attr = NULL;
2807 dctx->relative_attr = NULL;
2808
2809 if ((argc < 2) || (argc > 3)) {
2810 fr_strerror_const("Invalid VENDOR syntax");
2811 return -1;
2812 }
2813
2814 /*
2815 * Validate all entries
2816 */
2817 if (!dict_read_sscanf_i(&value, argv[1])) {
2818 fr_strerror_const("Invalid number in VENDOR");
2819 return -1;
2820 }
2821
2822 /*
2823 * Look for a format statement. Allow it to over-ride the hard-coded formats below.
2824 */
2825 if (argc == 3) {
2826 if (dict_read_parse_format(argv[2], &type, &length, &continuation) < 0) return -1;
2827
2828 } else {
2829 type = length = 1;
2830 }
2831
2832 /* Create a new VENDOR entry for the list */
2833 if (dict_vendor_add(dict, argv[0], value) < 0) return -1;
2834
2835 dv = fr_dict_vendor_by_num(dict, value);
2836 if (!dv) {
2837 fr_strerror_const("Failed adding format for VENDOR");
2838 return -1;
2839 }
2840
2841 mutable = UNCONST(fr_dict_vendor_t *, dv);
2842 mutable->type = type;
2843 mutable->length = length;
2844 mutable->continuation = continuation;
2845
2846 return 0;
2847}
2848
2849/** The main protocols that we care about.
2850 *
2851 * Not all of them are listed here, but that should be fine.
2852 *
2853 */
2855 { L("RADIUS"), FR_DICT_PROTO_RADIUS },
2856 { L("DHCPv4"), FR_DICT_PROTO_DHCPv4 },
2857 { L("DHCPv6"), FR_DICT_PROTO_DHCPv6 },
2858 { L("Ethernet"), FR_DICT_PROTO_ETHERNET },
2859 { L("TACACS"), FR_DICT_PROTO_TACACS },
2860 { L("VMPS"), FR_DICT_PROTO_VMPS },
2861 { L("SNMP"), FR_DICT_PROTO_SNMP },
2862 { L("ARP"), FR_DICT_PROTO_ARP },
2863 { L("TFTP"), FR_DICT_PROTO_TFTP },
2864 { L("TLS"), FR_DICT_PROTO_TLS },
2865 { L("DNS"), FR_DICT_PROTO_DNS },
2866 { L("LDAP"), FR_DICT_PROTO_LDAP },
2867 { L("BFD"), FR_DICT_PROTO_BFD },
2868 { L("CRL"), FR_DICT_PROTO_CRL },
2869};
2871
2872/** Register the specified dictionary as a protocol dictionary
2873 *
2874 * Allows vendor and TLV context to persist across $INCLUDEs
2875 */
2876static int dict_read_process_protocol(dict_tokenize_ctx_t *dctx, char **argv, int argc, UNUSED fr_dict_attr_flags_t *base_flag)
2877{
2878 unsigned int value;
2879 unsigned int type_size = 0;
2880 fr_dict_t *dict;
2881 unsigned int required_value;
2882 char const *required_name;
2883 bool require_dl = false;
2884 bool string_based = false;
2885
2886 /*
2887 * We cannot define a PROTOCOL inside of another protocol.
2888 */
2889 if (CURRENT_FRAME(dctx)->nest != NEST_TOP) {
2890 fr_strerror_const("PROTOCOL definitions cannot occur inside of any other BEGIN/END block");
2891 return -1;
2892 }
2893
2894 dctx->value_attr = NULL;
2895 dctx->relative_attr = NULL;
2896
2897 if ((argc < 2) || (argc > 3)) {
2898 fr_strerror_const("Missing arguments after PROTOCOL. Expected PROTOCOL <name> <number>");
2899 return -1;
2900 }
2901
2902 /*
2903 * Validate all entries
2904 */
2905 if (!dict_read_sscanf_i(&value, argv[1])) {
2906 fr_strerror_printf("Invalid number '%s' following PROTOCOL", argv[1]);
2907 return -1;
2908 }
2909
2910 /*
2911 * 255 protocols FR_TYPE_GROUP type_size hack
2912 */
2913 if (!value) {
2914 fr_strerror_printf("Invalid value '%u' following PROTOCOL", value);
2915 return -1;
2916 }
2917
2918 /*
2919 * While the numbers are in the dictionaries, the administrator cannot change "RADIUS" to be a
2920 * different number. Similarly, they can't assign the RADIUS number to a different protocol.
2921 */
2922 required_value = fr_table_value_by_str(dict_proto_table, argv[0], 0);
2923 if (required_value && (required_value != value)) {
2924 fr_strerror_printf("Invalid value '%u' following PROTOCOL - expected '%u'", value, required_value);
2925 return -1;
2926 }
2927
2928 /*
2929 * And the administrator can't define the name to be a different number.
2930 */
2931 required_name = fr_table_str_by_value(dict_proto_table, value, NULL);
2932 if (required_name && (strcasecmp(required_name, argv[0]) != 0)) {
2933 fr_strerror_printf("Invalid value '%u' for PROTOCOL '%s' - that value is already used by '%s'",
2934 value, argv[0], required_name);
2935 return -1;
2936 }
2937
2938 /*
2939 * Look for a format statement. This may specify the
2940 * type length of the protocol's types.
2941 */
2942 if (argc == 3) {
2943 char const *p;
2944 char *q;
2945
2946 /*
2947 * For now, we don't allow multiple options here.
2948 *
2949 * @todo - allow multiple options.
2950 */
2951 if (strcmp(argv[2], "verify=lib") == 0) {
2952 require_dl = true;
2953 goto post_option;
2954 }
2955
2956 if (strcmp(argv[2], "format=string") == 0) {
2957 type_size = 4;
2958 string_based = true;
2959 goto post_option;
2960 }
2961
2962 if (strncasecmp(argv[2], "format=", 7) != 0) {
2963 fr_strerror_printf("Invalid format for PROTOCOL. Expected 'format=', got '%s'", argv[2]);
2964 return -1;
2965 }
2966 p = argv[2] + 7;
2967
2968 type_size = strtoul(p, &q, 10);
2969 if (q != (p + strlen(p))) {
2970 fr_strerror_printf("Found trailing garbage '%s' after format specifier", p);
2971 return -1;
2972 }
2973 }
2974post_option:
2975
2976 /*
2977 * Cross check name / number.
2978 */
2979 dict = dict_by_protocol_name(argv[0]);
2980 if (dict) {
2981#ifdef STATIC_ANALYZER
2982 if (!dict->root) return -1;
2983#endif
2984
2985 if (dict->root->attr != value) {
2986 fr_strerror_printf("Conflicting numbers %u vs %u for PROTOCOL \"%s\"",
2987 dict->root->attr, value, dict->root->name);
2988 return -1;
2989 }
2990
2991 } else if ((dict = dict_by_protocol_num(value)) != NULL) {
2992#ifdef STATIC_ANALYZER
2993 if (!dict->root || !dict->root->name || !argv[0]) return -1;
2994#endif
2995
2996 if (strcasecmp(dict->root->name, argv[0]) != 0) {
2997 fr_strerror_printf("Conflicting names current \"%s\" vs new \"%s\" for PROTOCOL %u",
2998 dict->root->name, argv[0], dict->root->attr);
2999 return -1;
3000 }
3001 }
3002
3003 /*
3004 * And check types no matter what.
3005 */
3006 if (dict) {
3007 if (type_size && (dict->root->flags.type_size != type_size)) {
3008 fr_strerror_printf("Conflicting flags for PROTOCOL \"%s\" (current %d versus new %u)",
3009 dict->root->name, dict->root->flags.type_size, type_size);
3010 return -1;
3011 }
3012
3013 /*
3014 * Do NOT talloc_free() dict on error.
3015 */
3016 return dict_dctx_push(dctx, dict->root, NEST_NONE);
3017 }
3018
3019 dict = dict_alloc(dict_gctx);
3020 if (!dict) return -1;
3021
3022 /*
3023 * Try to load protocol-specific validation routines.
3024 * Some protocols don't need them, so it's OK if the
3025 * validation routines don't exist.
3026 */
3027 if ((dict_dlopen(dict, argv[0]) < 0) && require_dl) {
3028 error:
3029 talloc_free(dict);
3030 return -1;
3031 }
3032
3033 /*
3034 * Set the root attribute with the protocol name
3035 */
3036 if (dict_root_set(dict, argv[0], value) < 0) goto error;
3037
3038 if (dict_protocol_add(dict) < 0) goto error;
3039
3040 dict->string_based = string_based;
3041 if (type_size) {
3042 fr_dict_attr_t *mutable;
3043
3044 mutable = UNCONST(fr_dict_attr_t *, dict->root);
3045 mutable->flags.type_size = type_size;
3046 mutable->flags.length = 1; /* who knows... */
3047 }
3048
3049 /*
3050 * Make the root available on the stack, in case
3051 * something wants to begin it. Note that we mark it as
3052 * NONE, so that it can be cleaned up by anything.
3053 *
3054 * This stack entry is just a place-holder so that the
3055 * BEGIN statement can find the dictionary.
3056 */
3057 if (unlikely(dict_dctx_push(dctx, dict->root, NEST_NONE) < 0)) goto error;
3058
3059 return 0;
3060}
3061
3062/** Maintain a linked list of filenames which we've seen loading this dictionary
3063 *
3064 * This is used for debug messages, so we have a copy of the original file path
3065 * that we can reference from fr_dict_attr_t without having the memory bloat of
3066 * assigning a buffer to every attribute.
3067 */
3068static inline int dict_filename_add(char **filename_out, fr_dict_t *dict, char const *filename,
3069 char const *src_file, int src_line)
3070{
3072
3073 file = talloc_zero(dict, fr_dict_filename_t);
3074 if (unlikely(!file)) {
3075 oom:
3076 fr_strerror_const("Out of memory");
3077 return -1;
3078 }
3079 *filename_out = file->filename = talloc_strdup(file, filename);
3080 if (unlikely(!*filename_out)) goto oom;
3081
3082 if (src_file) {
3083 file->src_line = src_line;
3084 file->src_file = talloc_strdup(file, src_file);
3085 if (!file->src_file) goto oom;
3086 }
3087
3088 fr_dlist_insert_tail(&dict->filenames, file);
3089
3090 return 0;
3091}
3092
3093/** See if we have already loaded the file,
3094 *
3095 */
3096static inline bool dict_filename_loaded(fr_dict_t const *dict, char const *filename,
3097 char const *src_file, int src_line)
3098{
3100
3101 for (file = (fr_dict_filename_t *) fr_dlist_head(&dict->filenames);
3102 file != NULL;
3103 file = (fr_dict_filename_t *) fr_dlist_next(&dict->filenames, &file->entry)) {
3104 if (file->src_file && src_file) {
3105 if (file->src_line != src_line) continue;
3106 if (strcmp(file->src_file, src_file) != 0) continue;
3107 }
3108
3109 if (strcmp(file->filename, filename) == 0) return true; /* this should always be true */
3110 }
3111
3112 return false;
3113}
3114
3115bool fr_dict_filename_loaded(fr_dict_t const *dict, char const *dir, char const *filename)
3116{
3117 char buffer[PATH_MAX];
3118
3119 if (!dict) return false;
3120
3121 snprintf(buffer, sizeof(buffer), "%s/%s", dir, filename);
3122
3123 return dict_filename_loaded(dict, buffer, NULL, 0);
3124}
3125
3126/** Process an inline BEGIN PROTOCOL block
3127 *
3128 * This function is called *after* the PROTOCOL handler.
3129 */
3131{
3133 fr_assert(CURRENT_DA(dctx)->flags.is_root);
3134
3135 /*
3136 * Rewrite it in place.
3137 */
3138 CURRENT_FRAME(dctx)->nest = NEST_PROTOCOL;
3139 dctx->dict = CURRENT_DA(dctx)->dict;
3140
3141 return 0;
3142}
3143
3144/** Keyword parser
3145 *
3146 * @param[in] dctx containing the dictionary we're currently parsing.
3147 * @param[in] argv arguments to the keyword.
3148 * @param[in] argc number of arguments.
3149 * @param[in] base_flags set in the context of the current file.
3150 * @return
3151 * - 0 on success.
3152 * - -1 on failure.
3153 */
3154typedef int (*fr_dict_keyword_parse_t)(dict_tokenize_ctx_t *dctx, char **argv, int argc, fr_dict_attr_flags_t *base_flags);
3155
3156/** Pushes a new frame onto the top of the stack based on the current frame
3157 *
3158 * Whenever a protocol, vendor, or attribute is defined in the dictionary it either mutates or
3159 * pushes a new NONE frame onto the stack. This holds the last defined object at a given level
3160 * of nesting.
3161 *
3162 * This function is used to push an additional frame onto the stack, effectively entering the
3163 * context of the last defined object at a given level of nesting
3164 *
3165 * @param[in] dctx Contains the current state of the dictionary parser.
3166 * Used to track what PROTOCOL, VENDOR or TLV block
3167 * we're in.
3168 * @return
3169 * - 0 on success.
3170 * - -1 on failure.
3171 */
3173
3174typedef struct {
3175 fr_dict_keyword_parse_t parse; //!< Function to parse the keyword with.
3176 fr_dict_section_begin_t begin; //!< Can have a BEGIN prefix
3178
3179typedef struct {
3180 fr_table_elem_name_t name; //!< Name of the keyword, e.g. "ATTRIBUTE"
3181 fr_dict_keyword_parser_t value; //!< Value to return from lookup.
3183
3185 fr_dict_keyword, fr_dict_keyword_parser_t const *, fr_dict_keyword_parser_t const *)
3186
3187/** Parse a dictionary file
3188 *
3189 * @param[in] dctx Contains the current state of the dictionary parser.
3190 * Used to track what PROTOCOL, VENDOR or TLV block
3191 * we're in. Block context changes in $INCLUDEs should
3192 * not affect the context of the including file.
3193 * @param[in] dir Directory containing the dictionary we're loading.
3194 * @param[in] filename we're parsing.
3195 * @param[in] src_file The including file.
3196 * @param[in] src_line Line on which the $INCLUDE or $NCLUDE- statement was found.
3197 * @return
3198 * - 0 on success.
3199 * - -1 on failure.
3200 */
3201static int _dict_from_file(dict_tokenize_ctx_t *dctx,
3202 char const *dir, char const *filename,
3203 char const *src_file, int src_line)
3204{
3205 static fr_dict_keyword_t const keywords[] = {
3206 { L("ALIAS"), { .parse = dict_read_process_alias } },
3207 { L("ATTRIBUTE"), { .parse = dict_read_process_attribute } },
3208 { L("BEGIN-PROTOCOL"), { .parse = dict_read_process_begin_protocol } },
3209 { L("BEGIN-VENDOR"), { .parse = dict_read_process_begin_vendor } },
3210 { L("DEFINE"), { .parse = dict_read_process_define } },
3211 { L("END"), { .parse = dict_read_process_end } },
3212 { L("END-PROTOCOL"), { .parse = dict_read_process_end_protocol } },
3213 { L("END-VENDOR"), { .parse = dict_read_process_end_vendor } },
3214 { L("ENUM"), { .parse = dict_read_process_enum } },
3215 { L("FLAGS"), { .parse = dict_read_process_flags } },
3216 { L("MEMBER"), { .parse = dict_read_process_member } },
3217 { L("PROTOCOL"), { .parse = dict_read_process_protocol, .begin = dict_begin_protocol }},
3218 { L("VALUE"), { .parse = dict_read_process_value } },
3219 { L("VENDOR"), { .parse = dict_read_process_vendor } },
3220 };
3221
3222 FILE *fp;
3223 char filename_buf[256];
3224 char buf[256];
3225 char *p;
3226 int line = 0;
3227
3228 struct stat statbuf;
3229 char *argv[DICT_MAX_ARGV];
3230 int argc;
3231
3232 int stack_depth = dctx->stack_depth;
3233 char *old_filename;
3234 int old_line;
3235
3236 /*
3237 * Base flags are only set for the current file
3238 */
3239 fr_dict_attr_flags_t base_flags = {};
3240
3241 if (!fr_cond_assert(!dctx->dict->root || CURRENT_FRAME(dctx)->da)) return -1;
3242
3243 /*
3244 * The filename is relative to the current directory.
3245 *
3246 * Ensure that the directory name doesn't end with 2 '/',
3247 * and then create the full path from dir + filename.
3248 */
3249 if (FR_DIR_IS_RELATIVE(filename)) {
3250 char const *q;
3251 bool slash = false;
3252
3253 if (!dir) {
3254 fr_strerror_printf("Error reading dictionary: No directory was supplied");
3255 return -1;
3256 }
3257
3258 if ((strlen(dir) + 2 + strlen(filename)) > sizeof(filename_buf)) {
3259 fr_strerror_printf("%s: Filename name too long", "Error reading dictionary");
3260 return -1;
3261 }
3262
3263 /*
3264 * We either have to do strcpy + strrchr(), or manual checks.
3265 */
3266 p = filename_buf;
3267 for (q = dir; *q != '\0'; q++) {
3268 if (*q != '/') {
3269 *(p++) = *q;
3270 slash = false;
3271 continue;
3272 }
3273
3274 /*
3275 * Suppress multiple consecutive slashes.
3276 */
3277 if (slash) continue;
3278
3279 *(p++) = *q;
3280 slash = true;
3281 }
3282
3283 if (!slash) *(p++) = '/';
3284 strcpy(p, filename);
3285
3286 filename = filename_buf;
3287 }
3288 /*
3289 * Else we ignore the input directory. We also assume
3290 * that the filename is normalized, and therefore don't
3291 * change it.
3292 */
3293
3294 /*
3295 * See if we have already loaded this filename. If so, suppress it.
3296 */
3297 if (unlikely(dict_filename_loaded(dctx->dict, filename, src_file, src_line))) {
3298 fr_strerror_printf("ERROR - we have a recursive $INCLUDE or load of file %s", filename);
3299 return -1;
3300 }
3301
3302
3303 if ((fp = fopen(filename, "r")) == NULL) {
3304 if (!src_file) {
3305 fr_strerror_printf("Couldn't open dictionary %s: %s", fr_syserror(errno), filename);
3306 } else {
3307 fr_strerror_printf("Error reading dictionary: %s[%d]: Couldn't open dictionary '%s': %s",
3308 fr_cwd_strip(src_file), src_line, filename,
3309 fr_syserror(errno));
3310 }
3311 return -2;
3312 }
3313
3314 /*
3315 * If fopen works, this works.
3316 */
3317 if (fstat(fileno(fp), &statbuf) < 0) {
3318 fr_strerror_printf("Failed stating dictionary \"%s\" - %s", filename, fr_syserror(errno));
3319
3320 perm_error:
3321 fclose(fp);
3322 return -1;
3323 }
3324
3325 if (!S_ISREG(statbuf.st_mode)) {
3326 fr_strerror_printf("Dictionary is not a regular file: %s", filename);
3327 goto perm_error;
3328 }
3329
3330 /*
3331 * Globally writable dictionaries means that users can control
3332 * the server configuration with little difficulty.
3333 */
3334#ifdef S_IWOTH
3335 if (dict_gctx->perm_check && ((statbuf.st_mode & S_IWOTH) != 0)) {
3336 fr_strerror_printf("Dictionary is globally writable: %s. "
3337 "Refusing to start due to insecure configuration", filename);
3338 goto perm_error;
3339 }
3340#endif
3341
3342 old_filename = dctx->filename;
3343 old_line = dctx->line;
3344
3345 /*
3346 * Now that we've opened the file, copy the filename into the dictionary and add it to the ctx
3347 * This string is safe to assign to the filename pointer in any attributes added beneath the
3348 * dictionary.
3349 */
3350 if (unlikely(dict_filename_add(&dctx->filename, dctx->dict, filename, src_file, src_line) < 0)) {
3351 goto perm_error;
3352 }
3353
3354 CURRENT_FRAME(dctx)->filename = dctx->filename;
3355 CURRENT_FRAME(dctx)->line = line;
3356
3357 while (fgets(buf, sizeof(buf), fp) != NULL) {
3358 bool do_begin = false;
3359 fr_dict_keyword_parser_t const *parser;
3360 char **argv_p = argv;
3361
3362 dctx->line = ++line;
3363
3364 switch (buf[0]) {
3365 case '#':
3366 case '\0':
3367 case '\n':
3368 case '\r':
3369 continue;
3370 }
3371
3372 /*
3373 * Comment characters should NOT be appearing anywhere but
3374 * as start of a comment;
3375 */
3376 p = strchr(buf, '#');
3377 if (p) *p = '\0';
3378
3379 argc = fr_dict_str_to_argv(buf, argv, DICT_MAX_ARGV);
3380 if (argc == 0) continue;
3381
3382 if (argc == 1) {
3383 /*
3384 * Be nice.
3385 */
3386 if ((strcmp(argv[0], "BEGIN") == 0) ||
3387 (fr_dict_keyword(&parser, keywords, NUM_ELEMENTS(keywords), argv_p[0], NULL))) {
3388 fr_strerror_printf("Keyword %s is missing all of its arguments", argv[0]);
3389 } else {
3390 fr_strerror_printf("Invalid syntax - unknown keyword %s", argv[0]);
3391 }
3392
3393 error:
3394 fr_strerror_printf_push("Failed parsing dictionary at %s[%d]", fr_cwd_strip(filename), line);
3395 fclose(fp);
3396
3397 dctx->filename = CURRENT_FRAME(dctx)->filename = old_filename;
3398 dctx->line = CURRENT_FRAME(dctx)->line = old_line;
3399 return -1;
3400 }
3401
3402 /*
3403 * Special prefix for "beginnable" keywords.
3404 * These are keywords that can automatically change
3405 * the context of subsequent definitions if they're
3406 * prefixed with a BEGIN keyword.
3407 */
3408 if (strcasecmp(argv_p[0], "BEGIN") == 0) {
3409 do_begin = true;
3410 argv_p++;
3411 argc--;
3412 }
3413
3414 if (fr_dict_keyword(&parser, keywords, NUM_ELEMENTS(keywords), argv_p[0], NULL)) {
3415 /*
3416 * We are allowed to have attributes
3417 * named for keywords. Most notably
3418 * "value". If there's no such attribute
3419 * 'value', then the user will get a
3420 * descriptive error.
3421 */
3422 if (do_begin && !parser->begin) {
3423 goto process_begin;
3424 }
3425
3426 if (unlikely(parser->parse(dctx, argv_p + 1 , argc - 1, &base_flags) < 0)) goto error;
3427
3428 /*
3429 * We've processed the definition, now enter the section
3430 */
3431 if (do_begin && unlikely(parser->begin(dctx) < 0)) goto error;
3432 continue;
3433 }
3434
3435 /*
3436 * It's a naked BEGIN keyword
3437 */
3438 if (do_begin) {
3439 process_begin:
3440 if (unlikely(dict_read_process_begin(dctx, argv_p, argc, &base_flags) < 0)) goto error;
3441 continue;
3442 }
3443
3444 /*
3445 * See if we need to import another dictionary.
3446 */
3447 if (strncasecmp(argv_p[0], "$INCLUDE", 8) == 0) {
3448 /*
3449 * Included files operate on a copy of the context.
3450 *
3451 * This copy means that they inherit the
3452 * current context, including parents,
3453 * TLVs, etc. But if the included file
3454 * leaves a "dangling" TLV or "last
3455 * attribute", then it won't affect the
3456 * parent.
3457 */
3458 if (dict_read_process_include(dctx, argv_p, argc, dir) < 0) goto error;
3459 continue;
3460 } /* $INCLUDE */
3461
3462 /*
3463 * Any other string: We don't recognize it.
3464 */
3465 fr_strerror_printf("Invalid keyword '%s'", argv_p[0]);
3466 goto error;
3467 }
3468
3469 /*
3470 * Unwind until the stack depth matches what we had on input.
3471 */
3472 while (dctx->stack_depth > stack_depth) {
3473 dict_tokenize_frame_t *frame = CURRENT_FRAME(dctx);
3474
3475 if (frame->nest == NEST_PROTOCOL) {
3476 fr_strerror_printf("BEGIN-PROTOCOL at %s[%d] is missing END-PROTOCOL",
3477 fr_cwd_strip(frame->filename), line);
3478 goto error;
3479 }
3480
3481 if (frame->nest == NEST_ATTRIBUTE) {
3482 fr_strerror_printf("BEGIN %s at %s[%d] is missing END %s",
3483 frame->da->name, fr_cwd_strip(frame->filename), line,
3484 frame->da->name);
3485 goto error;
3486 }
3487
3488 if (frame->nest == NEST_VENDOR) {
3489 fr_strerror_printf("BEGIN-VENDOR at %s[%d] is missing END-VENDOR",
3490 fr_cwd_strip(frame->filename), line);
3491 goto error;
3492 }
3493
3494 /*
3495 * Run any necessary finalise callback, and then pop the frame.
3496 */
3497 if (frame->finalise) {
3498 if (frame->finalise(dctx) < 0) goto error;
3499 frame->finalise = NULL;
3500 }
3501
3502 fr_assert(!dctx->stack[dctx->stack_depth].finalise);
3503 dctx->stack_depth--;
3504 }
3505
3506 fclose(fp);
3507 dctx->filename = CURRENT_FRAME(dctx)->filename = old_filename;
3508 dctx->line = CURRENT_FRAME(dctx)->line = old_line;
3509
3510 return 0;
3511}
3512
3513static int dict_from_file(fr_dict_t *dict,
3514 char const *dir_name, char const *filename,
3515 char const *src_file, int src_line)
3516{
3517 int ret;
3519
3520 memset(&dctx, 0, sizeof(dctx));
3521 dctx.dict = dict;
3522 if (dict_fixup_init(&dctx.fixup) < 0) return -1;
3523 dctx.stack[0].da = dict->root;
3524 dctx.stack[0].nest = NEST_TOP;
3525
3526 ret = _dict_from_file(&dctx, dir_name, filename, src_file, src_line);
3527 if (ret < 0) {
3528 talloc_free(dctx.fixup.pool);
3529 return ret;
3530 }
3531
3532 /*
3533 * Applies to any attributes added to the *internal*
3534 * dictionary.
3535 *
3536 * Fixups should have been applied already to any protocol
3537 * dictionaries.
3538 */
3539 return dict_finalise(&dctx);
3540}
3541
3542/** (Re-)Initialize the special internal dictionary
3543 *
3544 * This dictionary has additional programmatically generated attributes added to it,
3545 * and is checked in addition to the protocol specific dictionaries.
3546 *
3547 * @note The dictionary pointer returned in out must have its reference counter
3548 * decremented with #fr_dict_free when no longer used.
3549 *
3550 * @param[out] out Where to write pointer to the internal dictionary.
3551 * @param[in] dict_subdir name of the internal dictionary dir (may be NULL).
3552 * @param[in] dependent Either C src file, or another dictionary.
3553 * @return
3554 * - 0 on success.
3555 * - -1 on failure.
3556 */
3557int fr_dict_internal_afrom_file(fr_dict_t **out, char const *dict_subdir, char const *dependent)
3558{
3559 fr_dict_t *dict;
3560 char *dict_path = NULL;
3561 size_t i;
3562 fr_dict_attr_flags_t flags = { .internal = true };
3563 char *type_name;
3564 fr_dict_attr_t *cast_base;
3566
3567 if (unlikely(!dict_gctx)) {
3568 fr_strerror_const("fr_dict_global_ctx_init() must be called before loading dictionary files");
3569 return -1;
3570 }
3571
3572 /*
3573 * Increase the reference count of the internal dictionary.
3574 */
3575 if (dict_gctx->internal) {
3578 return 0;
3579 }
3580
3581 dict_path = dict_subdir ?
3582 talloc_asprintf(NULL, "%s%c%s", fr_dict_global_ctx_dir(), FR_DIR_SEP, dict_subdir) :
3584
3585 fr_strerror_clear(); /* Ensure we don't report spurious errors */
3586
3587 dict = dict_alloc(dict_gctx);
3588 if (!dict) {
3589 error:
3590 if (!dict_gctx->internal) talloc_free(dict);
3591 talloc_free(dict_path);
3592 return -1;
3593 }
3594
3595 /*
3596 * Set the root name of the dictionary
3597 */
3598 if (dict_root_set(dict, "internal", 0) < 0) goto error;
3599
3600 if (dict_path && dict_from_file(dict, dict_path, FR_DICTIONARY_FILE, NULL, 0) < 0) goto error;
3601
3602 TALLOC_FREE(dict_path);
3603
3604 dict_dependent_add(dict, dependent);
3605
3606 if (!dict_gctx->internal) {
3607 dict_gctx->internal = dict;
3608 dict_dependent_add(dict, "global");
3609 }
3610
3611 /*
3612 * Try to load libfreeradius-internal, too. If that
3613 * fails (i.e. fuzzers???), ignore it.
3614 */
3615 (void) dict_dlopen(dict, "internal");
3616
3617 cast_base = dict_attr_child_by_num(dict->root, FR_CAST_BASE);
3618 if (!cast_base) {
3619 fr_strerror_printf("Failed to find 'Cast-Base' in internal dictionary");
3620 goto error;
3621 }
3622
3623 fr_assert(cast_base->type == FR_TYPE_UINT8);
3624 fr_value_box_init(&box, FR_TYPE_UINT8, NULL, false);
3625
3626 /*
3627 * Add cast attributes. We do it this way,
3628 * so cast attributes get added automatically for new types.
3629 *
3630 * We manually add the attributes to the dictionary, and bypass
3631 * fr_dict_attr_add(), because we know what we're doing, and
3632 * that function does too many checks.
3633 */
3634 for (i = 0; i < fr_type_table_len; i++) {
3637
3638 switch (p->value) {
3639 case FR_TYPE_NULL: /* Can't cast to NULL */
3640 case FR_TYPE_VENDOR: /* Vendors can't exist in dictionaries as attributes */
3641 continue;
3642 }
3643
3644 type_name = talloc_typed_asprintf(NULL, "Tmp-Cast-%s", p->name.str);
3645
3646 n = dict_attr_alloc(dict->pool, dict->root, type_name,
3647 FR_CAST_BASE + p->value, p->value, &(dict_attr_args_t){ .flags = &flags});
3648 if (!n) {
3649 talloc_free(type_name);
3650 goto error;
3651 }
3652
3653 if (dict_attr_add_to_namespace(dict->root, n) < 0) {
3654 fr_strerror_printf_push("Failed inserting '%s' into internal dictionary", type_name);
3655 talloc_free(type_name);
3656 goto error;
3657 }
3658
3659 talloc_free(type_name);
3660
3661 /*
3662 * Set up parenting for the attribute.
3663 */
3664 if (dict_attr_child_add(dict->root, n) < 0) goto error;
3665
3666 /*
3667 * Add the enum, too.
3668 */
3669 box.vb_uint8 = p->value;
3670 if (dict_attr_enum_add_name(cast_base, p->name.str, &box, false, false, NULL) < 0) {
3671 fr_strerror_printf_push("Failed adding '%s' as a VALUE into internal dictionary", p->name.str);
3672 goto error;
3673 }
3674 }
3675
3676 *out = dict;
3677
3678 return 0;
3679}
3680
3681/** (Re)-initialize a protocol dictionary
3682 *
3683 * Initialize the directory, then fix the attr number of all attributes.
3684 *
3685 * @param[out] out Where to write a pointer to the new dictionary. Will free existing
3686 * dictionary if files have changed and *out is not NULL.
3687 * @param[in] proto_name that we're loading the dictionary for.
3688 * @param[in] proto_dir Explicitly set where to hunt for the dictionary files. May be NULL.
3689 * @param[in] dependent Either C src file, or another dictionary.
3690 * @return
3691 * - 0 on success.
3692 * - -1 on failure.
3693 */
3694int fr_dict_protocol_afrom_file(fr_dict_t **out, char const *proto_name, char const *proto_dir, char const *dependent)
3695{
3696 char *dict_dir = NULL;
3697 fr_dict_t *dict;
3698 bool added = false;
3699
3700 *out = NULL;
3701
3702 if (unlikely(!dict_gctx)) {
3703 fr_strerror_const("fr_dict_global_ctx_init() must be called before loading dictionary files");
3704 return -1;
3705 }
3706
3707 if (unlikely(!dict_gctx->internal)) {
3708 fr_strerror_const("Internal dictionary must be initialised before loading protocol dictionaries");
3709 return -1;
3710 }
3711
3712 /*
3713 * Increment the reference count if the dictionary
3714 * has already been loaded and return that.
3715 */
3716 dict = dict_by_protocol_name(proto_name);
3717 if (dict) {
3718 /*
3719 * If we're in the middle of loading this dictionary, then the only way we get back here
3720 * is via a circular reference. So we catch that, and drop the circular dependency.
3721 *
3722 * When we have A->B->A, it means that we don't need to track B->A, because we track
3723 * A->B. And if A is freed, then B is freed.
3724 */
3725 added = true;
3726 dict_dependent_add(dict, dependent);
3727
3728 /*
3729 * But we only return a pre-existing dict if _this function_ has loaded it.
3730 */
3731 if (dict->loaded) {
3732 *out = dict;
3733 return 0;
3734 }
3735
3736 /*
3737 * Set the flag to true _before_ loading the file. That prevents recursion.
3738 */
3739 dict->loaded = true;
3740 }
3741
3742 if (!proto_dir) {
3743 dict_dir = talloc_asprintf(NULL, "%s%c%s", fr_dict_global_ctx_dir(), FR_DIR_SEP, proto_name);
3744 } else {
3745 dict_dir = talloc_asprintf(NULL, "%s%c%s", fr_dict_global_ctx_dir(), FR_DIR_SEP, proto_dir);
3746 }
3747
3748 fr_strerror_clear(); /* Ensure we don't report spurious errors */
3749
3750 /*
3751 * Start in the context of the internal dictionary,
3752 * and switch to the context of a protocol dictionary
3753 * when we hit a BEGIN-PROTOCOL line.
3754 *
3755 * This allows a single file to provide definitions
3756 * for multiple protocols, which'll probably be useful
3757 * at some point.
3758 */
3759 if (dict_from_file(dict_gctx->internal, dict_dir, FR_DICTIONARY_FILE, NULL, 0) < 0) {
3760 error:
3761 if (dict) dict->loading = false;
3762 talloc_free(dict_dir);
3763 return -1;
3764 }
3765
3766 /*
3767 * Check the dictionary actually defined the protocol
3768 */
3769 dict = dict_by_protocol_name(proto_name);
3770 if (!dict) {
3771 fr_strerror_printf("Dictionary \"%s\" missing \"BEGIN-PROTOCOL %s\" declaration", dict_dir, proto_name);
3772 goto error;
3773 }
3774
3775 /*
3776 * Initialize the library.
3777 */
3778 dict->loaded = true;
3779 if (dict->proto && dict->proto->init) {
3780 if (dict->proto->init() < 0) goto error;
3781 }
3782 dict->loading = false;
3783
3784 dict->dir = talloc_steal(dict, dict_dir);
3785
3786 if (!added) dict_dependent_add(dict, dependent);
3787
3788 *out = dict;
3789
3790 return 0;
3791}
3792
3793/* Alloc a new root dictionary attribute
3794 *
3795 * @note Must only be called once per dictionary.
3796 *
3797 * @param[in] proto_name that we're loading the dictionary for.
3798 * @param[in] proto_number The artificial (or IANA allocated) number for the protocol.
3799 * @return
3800 * - A pointer to the new dict context on success.
3801 * - NULL on failure.
3802 */
3803fr_dict_t *fr_dict_alloc(char const *proto_name, unsigned int proto_number)
3804{
3805 fr_dict_t *dict;
3806
3807 if (unlikely(!dict_gctx)) {
3808 fr_strerror_printf("fr_dict_global_ctx_init() must be called before loading dictionary files");
3809 return NULL;
3810 }
3811
3812 /*
3813 * Alloc dict instance.
3814 */
3815 dict = dict_alloc(dict_gctx);
3816 if (!dict) return NULL;
3817
3818 /*
3819 * Set the root name of the dictionary
3820 */
3821 if (dict_root_set(dict, proto_name, proto_number) < 0) {
3822 talloc_free(dict);
3823 return NULL;
3824 }
3825
3826 return dict;
3827}
3828
3829/** Read supplementary attribute definitions into an existing dictionary
3830 *
3831 * @param[in] dict Existing dictionary.
3832 * @param[in] dir dictionary is located in.
3833 * @param[in] filename of the dictionary.
3834 * @return
3835 * - 0 on success.
3836 * - -1 on failure.
3837 */
3838int fr_dict_read(fr_dict_t *dict, char const *dir, char const *filename)
3839{
3840 INTERNAL_IF_NULL(dict, -1);
3841
3842 if (!dir) dir = dict->dir;
3843
3844 if (unlikely(dict->read_only)) {
3845 fr_strerror_printf("%s dictionary has been marked as read only", fr_dict_root(dict)->name);
3846 return -1;
3847 }
3848
3849 if (!dict->vendors_by_name) {
3850 fr_strerror_printf("%s: Must initialise dictionary before calling fr_dict_read()", __FUNCTION__);
3851 return -1;
3852 }
3853
3854 return dict_from_file(dict, dir, filename, NULL, 0);
3855}
3856
3857/*
3858 * External API for testing
3859 */
3860int fr_dict_parse_str(fr_dict_t *dict, char const *input, fr_dict_attr_t const *parent)
3861{
3862 int argc;
3863 char *argv[DICT_MAX_ARGV];
3864 int ret;
3865 fr_dict_attr_flags_t base_flags = {};
3867 char *buf;
3868
3869 INTERNAL_IF_NULL(dict, -1);
3870
3871 /*
3872 * str_to_argv() mangles the input buffer, which messes with 'unit_test_attribute -w foo'
3873 */
3874 buf = talloc_strdup(NULL, input);
3875
3876 argc = fr_dict_str_to_argv(buf, argv, DICT_MAX_ARGV);
3877 if (argc == 0) {
3878 talloc_free(buf);
3879 return 0;
3880 }
3881
3882 memset(&dctx, 0, sizeof(dctx));
3883 dctx.dict = dict;
3884
3885 dctx.stack[0].da = parent;
3886 dctx.stack[0].nest = NEST_TOP;
3887
3888 if (dict_fixup_init(&dctx.fixup) < 0) {
3889 error:
3890 TALLOC_FREE(dctx.fixup.pool);
3891 talloc_free(buf);
3892 return -1;
3893 }
3894
3895 if (strcasecmp(argv[0], "VALUE") == 0) {
3896 if (argc < 4) {
3897 fr_strerror_printf("VALUE needs at least 4 arguments, got %i", argc);
3898 goto error;
3899 }
3900
3901 if (!fr_dict_attr_by_oid(NULL, fr_dict_root(dict), argv[1])) {
3902 fr_strerror_printf("Attribute '%s' does not exist in dictionary \"%s\"",
3903 argv[1], dict->root->name);
3904 goto error;
3905 }
3906 ret = dict_read_process_value(&dctx, argv + 1, argc - 1, &base_flags);
3907
3908 } else if (strcasecmp(argv[0], "ATTRIBUTE") == 0) {
3909 if (parent != dict->root) {
3910 (void) dict_dctx_push(&dctx, parent, NEST_NONE);
3911 }
3912
3913 ret = dict_read_process_attribute(&dctx,
3914 argv + 1, argc - 1, &base_flags);
3915
3916 } else if (strcasecmp(argv[0], "DEFINE") == 0) {
3917 if (parent != dict->root) {
3918 (void) dict_dctx_push(&dctx, parent, NEST_NONE);
3919 }
3920
3921 ret = dict_read_process_define(&dctx,
3922 argv + 1, argc - 1, &base_flags);
3923
3924 } else if (strcasecmp(argv[0], "VENDOR") == 0) {
3925 ret = dict_read_process_vendor(&dctx, argv + 1, argc - 1, &base_flags);
3926
3927 } else {
3928 fr_strerror_printf("Invalid input '%s'", argv[0]);
3929 goto error;
3930 }
3931
3932 if (ret < 0) goto error;
3933
3934 talloc_free(buf);
3935
3936 return dict_finalise(&dctx);
3937}
3938
3939/** Load one dictionary file.
3940 *
3941 * @param[out] out Where to write a pointer to the new dictionary. Will free existing
3942 * dictionary if files have changed and *out is not NULL.
3943 * @param[in] dir directory
3944 * @param[in] filename file to load.
3945 * @return
3946 * - 0 on success.
3947 * - -1 on failure.
3948 */
3949int fr_dict_afrom_file(fr_dict_t **out, char const *dir, char const *filename)
3950{
3951 fr_dict_t *dict;
3952
3953 *out = NULL;
3954
3955 if (unlikely(!dict_gctx)) {
3956 fr_strerror_const("fr_dict_global_ctx_init() must be called before loading dictionary files");
3957 return -1;
3958 }
3959
3960 if (unlikely(!dict_gctx->internal)) {
3961 fr_strerror_const("Internal dictionary must be initialised before loading test dictionaries");
3962 return -1;
3963 }
3964
3965 fr_strerror_clear(); /* Ensure we don't report spurious errors */
3966
3967 dict = dict_alloc(dict_gctx);
3968 if (!dict) return -1;
3969
3970 if (dict_from_file(dict, dir, filename, NULL, 0) < 0) {
3971 talloc_free(dict);
3972 return -1;
3973 }
3974
3975 /*
3976 * Finalize the library
3977 *
3978 * Note that we cannot use this function to test loading of protocol dictionaries without
3979 * significant changes. We would have to free up all of the dependencies, etc. which isn't
3980 * entirely done right now.
3981 */
3982 fr_assert(dict->proto);
3983 fr_assert(strcmp(dict->proto->name, "default") == 0);
3984 fr_assert(!dict->proto->init);
3985 fr_assert(!dict->proto->free);
3986 fr_assert(!dict->proto->encode);
3987 fr_assert(!dict->proto->decode);
3988
3989 dict->loaded = true;
3990 dict->loading = false;
3991
3992 *out = dict;
3993
3994 return 0;
3995}
static int const char char buffer[256]
Definition acutest.h:576
int const char * file
Definition acutest.h:702
int n
Definition acutest.h:577
strcpy(log_entry->msg, buffer)
int const char int line
Definition acutest.h:702
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
Definition build.h:186
#define RCSID(id)
Definition build.h:512
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:228
#define NDEBUG_UNUSED
Definition build.h:347
#define DIAG_ON(_x)
Definition build.h:487
#define unlikely(_x)
Definition build.h:407
#define UNUSED
Definition build.h:336
#define NUM_ELEMENTS(_t)
Definition build.h:358
#define DIAG_OFF(_x)
Definition build.h:486
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:141
#define FR_FAULT_LOG(_fmt,...)
Definition debug.h:60
#define fr_cond_assert_msg(_x, _fmt,...)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:158
int fr_dict_attr_add_initialised(fr_dict_attr_t *da)
A variant of fr_dict_attr_t that allows a pre-allocated, populated fr_dict_attr_t to be added.
Definition dict_util.c:1845
fr_slen_t fr_dict_enum_name_from_substr(fr_sbuff_t *out, fr_sbuff_parse_error_t *err, fr_sbuff_t *in, fr_sbuff_term_t const *tt)
Extract an enumeration name from a string.
Definition dict_util.c:3821
int fr_dict_enum_add_name(fr_dict_attr_t *da, char const *name, fr_value_box_t const *value, bool coerce, bool replace)
Add a value name.
Definition dict_util.c:2217
char const * name
Vendor name.
Definition dict.h:274
unsigned int is_root
Is root of a dictionary.
Definition dict.h:75
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:2313
int fr_dict_protocol_reference(fr_dict_attr_t const **da_p, fr_dict_attr_t const *root, fr_sbuff_t *in)
Resolve a reference string to a dictionary attribute.
Definition dict_fixup.c:135
fr_dict_attr_t const * fr_dict_attr_by_name(fr_dict_attr_err_t *err, fr_dict_attr_t const *parent, char const *attr))
Locate a fr_dict_attr_t by its name.
Definition dict_util.c:3528
fr_dict_attr_t * fr_dict_attr_unconst(fr_dict_attr_t const *da)
Coerce to non-const.
Definition dict_util.c:4915
fr_slen_t fr_dict_attr_by_oid_legacy(fr_dict_attr_t const **parent, unsigned int *attr, char const *oid)
Get the leaf attribute of an OID string.
Definition dict_util.c:2399
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition dict_util.c:2665
fr_dict_flag_parse_func_t func
Custom parsing function to convert a flag value string to a C type value.
Definition dict.h:395
unsigned int internal
Internal attribute, should not be received in protocol packets, should not be encoded.
Definition dict.h:88
#define DA_VERIFY(_x)
Definition dict.h:66
#define da_is_bit_field(_da)
Definition dict.h:171
uint32_t pen
Private enterprise number.
Definition dict.h:270
#define da_is_length_field(_da)
Definition dict.h:172
char const * fr_dict_global_ctx_dir(void)
Definition dict_util.c:4810
@ FR_DICT_ATTR_EXT_ENUMV
Enumeration values.
Definition dict.h:188
@ FR_DICT_ATTR_EXT_REF
Attribute references another attribute and/or dictionary.
Definition dict.h:184
@ FR_DICT_ATTR_EXT_KEY
UNION attribute references a key.
Definition dict.h:186
fr_dict_vendor_t const * fr_dict_vendor_by_name(fr_dict_t const *dict, char const *name)
Look up a vendor by its name.
Definition dict_util.c:2922
bool needs_value
This parsing flag must have a value. Else we error.
Definition dict.h:397
fr_dict_attr_t const * fr_dict_attr_by_oid(fr_dict_attr_err_t *err, fr_dict_attr_t const *parent, char const *oid))
Resolve an attribute using an OID string.
Definition dict_util.c:2638
fr_dict_vendor_t const * fr_dict_vendor_by_num(fr_dict_t const *dict, uint32_t vendor_pen)
Look up a vendor by its PEN.
Definition dict_util.c:2945
fr_dict_attr_t const * fr_dict_attr_child_by_num(fr_dict_attr_t const *parent, unsigned int attr)
Check if a child attribute exists in a parent using an attribute number.
Definition dict_util.c:3593
#define fr_dict_attr_is_key_field(_da)
Definition dict.h:170
static int8_t fr_dict_attr_cmp_fields(const fr_dict_attr_t *a, const fr_dict_attr_t *b)
Compare two dictionary attributes by their contents.
Definition dict.h:691
@ FLAG_LENGTH_UINT8
string / octets type is prefixed by uint8 of length
Definition dict.h:166
@ FLAG_LENGTH_UINT16
string / octets type is prefixed by uint16 of length
Definition dict.h:167
@ FLAG_KEY_FIELD
this is a key field for a subsequent struct
Definition dict.h:164
@ FLAG_BIT_FIELD
bit field inside of a struct
Definition dict.h:165
Values of the encryption flags.
Protocol specific custom flag definitnion.
Definition dict.h:425
Private enterprise.
Definition dict.h:269
#define fr_dict_attr_ref_type(_type)
Definition dict_ext.h:72
fr_dict_attr_ref_type_t type
The state of the reference.
Definition dict_ext.h:78
static void * fr_dict_attr_ext(fr_dict_attr_t const *da, fr_dict_attr_ext_t ext)
Definition dict_ext.h:121
#define fr_dict_attr_ref_is_unresolved(_type)
Definition dict_ext.h:71
@ FR_DICT_ATTR_REF_ENUM
The attribute is an enumeration value.
Definition dict_ext.h:63
@ FR_DICT_ATTR_REF_KEY
it is a UNION which has a ref to a key, and children.
Definition dict_ext.h:64
@ FR_DICT_ATTR_REF_ALIAS
The attribute is an alias for another attribute.
Definition dict_ext.h:59
@ FR_DICT_ATTR_REF_CLONE
The attribute is a "copy" of another attribute.
Definition dict_ext.h:62
Attribute extension - Holds a reference to an attribute in another dictionary.
Definition dict_ext.h:77
static int dict_attr_ref_set(fr_dict_attr_t const *da, fr_dict_attr_t const *ref, fr_dict_attr_ref_type_t type)
static int dict_attr_ref_aunresolved(fr_dict_attr_t **da_p, char const *ref, fr_dict_attr_ref_type_t type)
static void * dict_attr_ext_alloc(fr_dict_attr_t **da_p, fr_dict_attr_ext_t ext)
Allocate an attribute extension.
int dict_fixup_apply(dict_fixup_ctx_t *fctx)
Apply all outstanding fixes to a set of dictionaries.
Definition dict_fixup.c:810
int dict_fixup_enumv_enqueue(dict_fixup_ctx_t *fctx, char const *filename, int line, char const *attr, size_t attr_len, char const *name, size_t name_len, char const *value, size_t value_len, fr_dict_attr_t const *parent)
Add an enumeration value to an attribute which has not yet been defined.
Definition dict_fixup.c:275
int dict_fixup_alias_enqueue(dict_fixup_ctx_t *fctx, char const *filename, int line, fr_dict_attr_t *alias_parent, char const *alias, fr_dict_attr_t *ref_parent, char const *ref)
Resolve a group reference.
Definition dict_fixup.c:737
int dict_fixup_clone_enqueue(dict_fixup_ctx_t *fctx, fr_dict_attr_t *da, char const *ref)
Clone one area of a tree into another.
Definition dict_fixup.c:425
int dict_fixup_clone_enum_enqueue(dict_fixup_ctx_t *fctx, fr_dict_attr_t *da, char const *ref)
Clone enumeration values from one attribute to another.
Definition dict_fixup.c:577
int dict_fixup_vsa_enqueue(dict_fixup_ctx_t *fctx, fr_dict_attr_t *da)
Push a fixup for a VSA.
Definition dict_fixup.c:676
int dict_fixup_clone(fr_dict_attr_t **dst_p, fr_dict_attr_t const *src)
Clone a dictionary attribute from a ref.
Definition dict_fixup.c:460
int dict_fixup_group_enqueue(dict_fixup_ctx_t *fctx, fr_dict_attr_t *da, char const *ref)
Resolve a group reference.
Definition dict_fixup.c:358
int dict_fixup_init(dict_fixup_ctx_t *fctx)
Initialise a fixup ctx.
Definition dict_fixup.c:790
TALLOC_CTX * pool
Temporary pool for fixups, reduces holes.
int dict_attr_enum_add_name(fr_dict_attr_t *da, char const *name, fr_value_box_t const *value, bool coerce, bool replace, fr_dict_attr_t const *child_struct)
Definition dict_util.c:2020
int dict_attr_type_init(fr_dict_attr_t **da_p, fr_type_t type)
Initialise type specific fields within the dictionary attribute.
Definition dict_util.c:551
int dict_attr_parent_init(fr_dict_attr_t **da_p, fr_dict_attr_t const *parent)
Initialise fields which depend on a parent attribute.
Definition dict_util.c:638
#define dict_attr_alloc(_ctx, _parent, _name, _attr, _type, _args)
Definition dict_priv.h:256
fr_dict_t * dict_alloc(TALLOC_CTX *ctx)
Allocate a new dictionary.
Definition dict_util.c:4177
#define INTERNAL_IF_NULL(_dict, _ret)
Set the internal dictionary if none was provided.
Definition dict_priv.h:45
int dict_attr_add_to_namespace(fr_dict_attr_t const *parent, fr_dict_attr_t *da)
Add an attribute to the name table for an attribute.
Definition dict_util.c:1779
fr_dict_attr_t * dict_attr_child_by_num(fr_dict_attr_t const *parent, unsigned int attr)
Internal version of fr_dict_attr_child_by_num.
Definition dict_util.c:3546
fr_dict_t * dict_by_protocol_num(unsigned int num)
Internal version of fr_dict_by_protocol_num.
Definition dict_util.c:2787
int dict_attr_child_add(fr_dict_attr_t *parent, fr_dict_attr_t *child)
Add a child to a parent.
Definition dict_util.c:1680
int dict_vendor_add(fr_dict_t *dict, char const *name, unsigned int num)
Add a vendor to the dictionary.
Definition dict_util.c:1585
int dict_attr_finalise(fr_dict_attr_t **da_p, char const *name)
Set remaining fields in a dictionary attribute before insertion.
Definition dict_util.c:758
int dict_attr_num_init(fr_dict_attr_t *da, unsigned int num)
Set the attribute number (if any)
Definition dict_util.c:714
int dict_attr_num_init_name_only(fr_dict_attr_t *da)
Set the attribute number (if any)
Definition dict_util.c:732
int dict_dlopen(fr_dict_t *dict, char const *name)
Definition dict_util.c:3861
fr_dict_t * dict_by_protocol_name(char const *name)
Internal version of fr_dict_by_protocol_name.
Definition dict_util.c:2773
@ FR_DICT_PROTO_LDAP
Definition dict_priv.h:172
@ FR_DICT_PROTO_DNS
Definition dict_priv.h:171
@ FR_DICT_PROTO_RADIUS
Definition dict_priv.h:161
@ FR_DICT_PROTO_SNMP
Definition dict_priv.h:167
@ FR_DICT_PROTO_DHCPv4
Definition dict_priv.h:162
@ FR_DICT_PROTO_DHCPv6
Definition dict_priv.h:163
@ FR_DICT_PROTO_TACACS
Definition dict_priv.h:165
@ FR_DICT_PROTO_TLS
Definition dict_priv.h:170
@ FR_DICT_PROTO_CRL
Definition dict_priv.h:174
@ FR_DICT_PROTO_VMPS
Definition dict_priv.h:166
@ FR_DICT_PROTO_ARP
Definition dict_priv.h:168
@ FR_DICT_PROTO_ETHERNET
Definition dict_priv.h:164
@ FR_DICT_PROTO_TFTP
Definition dict_priv.h:169
@ FR_DICT_PROTO_BFD
Definition dict_priv.h:173
int dict_attr_alias_add(fr_dict_attr_t const *parent, char const *alias, fr_dict_attr_t const *ref, bool from_public)
Add an alias to an existing attribute.
Definition dict_util.c:1404
fr_dict_t * internal
Magic internal dictionary.
Definition dict_priv.h:155
fr_dict_attr_t * dict_attr_alloc_null(TALLOC_CTX *ctx, fr_dict_protocol_t const *dict)
Partial initialisation functions.
Definition dict_util.c:989
int dict_dependent_add(fr_dict_t *dict, char const *dependent)
Record a new dependency on a dictionary.
Definition dict_util.c:3952
#define dict_attr_alloc_root(_ctx, _dict, _name, _attr, _args)
Definition dict_priv.h:248
int dict_protocol_add(fr_dict_t *dict)
Add a protocol to the global protocol table.
Definition dict_util.c:1512
fr_dict_gctx_t * dict_gctx
Top level structure containing global dictionary state.
Definition dict_util.c:41
bool perm_check
Whether we should check dictionary file permissions as they're loaded.
Definition dict_priv.h:132
Optional arguments for initialising/allocating attributes.
Definition dict_priv.h:192
Entry in the filename list of files associated with this dictionary.
Definition dict_priv.h:69
Test enumeration values.
Definition dict_test.h:92
fr_dict_keyword_finalise_t finalise
function to call when popping
bool fr_dict_filename_loaded(fr_dict_t const *dict, char const *dir, char const *filename)
static int dict_flag_flat(fr_dict_attr_t **da_p, UNUSED char const *value, UNUSED fr_dict_flag_parser_rule_t const *rule)
"flat"
static fr_table_num_sorted_t const dict_nest_table[]
static int dict_read_process_include(dict_tokenize_ctx_t *dctx, char **argv, int argc, char const *dir)
dict_nest_t nest
for manual vs automatic begin / end things
dict_fixup_ctx_t fixup
static int dict_read_process_common(dict_tokenize_ctx_t *dctx, fr_dict_attr_t **da_p, fr_dict_attr_t const *parent, char const *name, char const *type_name, char *flag_name, fr_dict_attr_flags_t const *base_flags)
#define NEST_ANY
static int dict_read_process_attribute(dict_tokenize_ctx_t *dctx, char **argv, int argc, fr_dict_attr_flags_t *base_flags)
int member_num
structure member numbers
static int dict_filename_add(char **filename_out, fr_dict_t *dict, char const *filename, char const *src_file, int src_line)
Maintain a linked list of filenames which we've seen loading this dictionary.
static int dict_process_type_field(dict_tokenize_ctx_t *dctx, char const *name, fr_dict_attr_t **da_p)
static int dict_read_process_alias(dict_tokenize_ctx_t *dctx, char **argv, int argc, UNUSED fr_dict_attr_flags_t *base_flags)
static int _dict_from_file(dict_tokenize_ctx_t *dctx, char const *dir_name, char const *filename, char const *src_file, int src_line)
fr_dict_section_begin_t begin
Can have a BEGIN prefix.
static int dict_attr_allow_dup(fr_dict_attr_t const *da)
Check if this definition is a duplicate, and if it is, whether we should skip it error out.
int fr_dict_parse_str(fr_dict_t *dict, char const *input, fr_dict_attr_t const *parent)
static int dict_finalise(dict_tokenize_ctx_t *dctx)
static int dict_read_process_member(dict_tokenize_ctx_t *dctx, char **argv, int argc, fr_dict_attr_flags_t *base_flags)
fr_dict_t * fr_dict_alloc(char const *proto_name, unsigned int proto_number)
static int dict_flag_ref(fr_dict_attr_t **da_p, char const *value, UNUSED fr_dict_flag_parser_rule_t const *rule)
static int dict_flag_precision(fr_dict_attr_t **da_p, char const *value, UNUSED fr_dict_flag_parser_rule_t const *rule)
static fr_table_num_ordered_t const dict_proto_table[]
The main protocols that we care about.
static int dict_read_process_end(dict_tokenize_ctx_t *dctx, char **argv, int argc, fr_dict_attr_flags_t *base_flags)
int stack_depth
points to the last used stack frame
ssize_t struct_size
size of the struct.
static bool dict_read_sscanf_i(unsigned int *pvalue, char const *str)
static int dict_read_process_end_protocol(dict_tokenize_ctx_t *dctx, char **argv, int argc, fr_dict_attr_flags_t *base_flags)
int fr_dict_afrom_file(fr_dict_t **out, char const *dir, char const *filename)
Load one dictionary file.
fr_dict_attr_t const * struct_is_closed
no more members are allowed
char * filename
current filename
int(* fr_dict_keyword_parse_t)(dict_tokenize_ctx_t *dctx, char **argv, int argc, fr_dict_attr_flags_t *base_flags)
Keyword parser.
static int dict_read_process_vendor(dict_tokenize_ctx_t *dctx, char **argv, int argc, UNUSED fr_dict_attr_flags_t *base_flags)
int fr_dict_protocol_afrom_file(fr_dict_t **out, char const *proto_name, char const *proto_dir, char const *dependent)
(Re)-initialize a protocol dictionary
int(* fr_dict_section_begin_t)(dict_tokenize_ctx_t *dctx)
Pushes a new frame onto the top of the stack based on the current frame.
int fr_dict_str_to_argv(char *str, char **argv, int max_argc)
static int dict_read_process_define(dict_tokenize_ctx_t *dctx, char **argv, int argc, fr_dict_attr_flags_t *base_flags)
static size_t const dict_nest_table_len
static int dict_read_parse_format(char const *format, int *ptype, int *plength, bool *pcontinuation)
static int dict_read_process_begin_vendor(dict_tokenize_ctx_t *dctx, char **argv, int argc, UNUSED fr_dict_attr_flags_t *base_flags)
#define ASSERT_CURRENT_NEST(_dctx, _nest)
static dict_tokenize_frame_t const * dict_dctx_unwind_until(dict_tokenize_ctx_t *dctx, dict_nest_t nest)
Unwind the stack until it points to a particular type of stack frame.
static int dict_from_file(fr_dict_t *dict, char const *dir_name, char const *filename, char const *src_file, int src_line)
fr_dict_attr_t const * da
the da we care about
fr_dict_attr_t * value_attr
Cache of last attribute to speed up value processing.
static int dict_read_process_protocol(dict_tokenize_ctx_t *dctx, char **argv, int argc, UNUSED fr_dict_attr_flags_t *base_flag)
Register the specified dictionary as a protocol dictionary.
static int dict_read_process_end_vendor(dict_tokenize_ctx_t *dctx, char **argv, int argc, fr_dict_attr_flags_t *base_flags)
#define FLAG_FUNC(_name)
Define a flag setting function, which sets one bit in a fr_dict_attr_flags_t.
#define CURRENT_LINE(_dctx)
static dict_tokenize_frame_t const * dict_dctx_pop(dict_tokenize_ctx_t *dctx)
Pop the current stack frame.
char * filename
name of the file where we read this entry
static int dict_set_value_attr(dict_tokenize_ctx_t *dctx, fr_dict_attr_t *da)
static int dict_read_process_enum(dict_tokenize_ctx_t *dctx, char **argv, int argc, fr_dict_attr_flags_t *base_flags)
int line
current line
#define CURRENT_FILENAME(_dctx)
static int dict_struct_finalise(dict_tokenize_ctx_t *dctx)
static int dict_flag_offset(fr_dict_attr_t **da_p, char const *value, UNUSED fr_dict_flag_parser_rule_t const *rule)
static int dict_read_process_begin_protocol(dict_tokenize_ctx_t *dctx, char **argv, int argc, UNUSED fr_dict_attr_flags_t *base_flags)
static int dict_flag_key(fr_dict_attr_t **da_p, char const *value, UNUSED fr_dict_flag_parser_rule_t const *rule)
fr_dict_t * dict
Protocol dictionary we're inserting attributes into.
static int dict_begin_protocol(NDEBUG_UNUSED dict_tokenize_ctx_t *dctx)
Process an inline BEGIN PROTOCOL block.
static int dict_flag_clone(fr_dict_attr_t **da_p, char const *value, UNUSED fr_dict_flag_parser_rule_t const *rules)
dict_tokenize_frame_t stack[DICT_MAX_STACK]
stack of attributes to track
int line
line number where we read this entry
static int dict_flag_enum(fr_dict_attr_t **da_p, char const *value, UNUSED fr_dict_flag_parser_rule_t const *rule)
static dict_tokenize_frame_t const * dict_dctx_unwind(dict_tokenize_ctx_t *dctx)
#define CURRENT_DA(_dctx)
#define DICT_MAX_ARGV
Maximum number of arguments.
static int dict_read_process_value(dict_tokenize_ctx_t *dctx, char **argv, int argc, UNUSED fr_dict_attr_flags_t *base_flags)
Process a value alias.
static dict_tokenize_frame_t const * dict_dctx_find_frame(dict_tokenize_ctx_t *dctx, dict_nest_t nest)
static size_t const dict_proto_table_len
fr_dict_keyword_parser_t value
Value to return from lookup.
static int dict_flag_length(fr_dict_attr_t **da_p, char const *value, UNUSED fr_dict_flag_parser_rule_t const *rule)
static bool dict_filename_loaded(fr_dict_t const *dict, char const *filename, char const *src_file, int src_line)
See if we have already loaded the file,.
int fr_dict_read(fr_dict_t *dict, char const *dir, char const *filename)
Read supplementary attribute definitions into an existing dictionary.
#define DICT_MAX_STACK
Maximum stack size.
static int dict_flag_subtype(fr_dict_attr_t **da_p, char const *value, UNUSED fr_dict_flag_parser_rule_t const *rule)
static int dict_read_process_begin(dict_tokenize_ctx_t *dctx, char **argv, int argc, UNUSED fr_dict_attr_flags_t *base_flags)
int fr_dict_internal_afrom_file(fr_dict_t **out, char const *dict_subdir, char const *dependent)
(Re-)Initialize the special internal dictionary
static int dict_dctx_push(dict_tokenize_ctx_t *dctx, fr_dict_attr_t const *da, dict_nest_t nest)
static int dict_read_process_flags(UNUSED dict_tokenize_ctx_t *dctx, char **argv, int argc, fr_dict_attr_flags_t *base_flags)
static int dict_attr_add_or_fixup(dict_fixup_ctx_t *fixup, fr_dict_attr_t **da_p)
Add an attribute to the dictionary, or add it to a list of attributes to clone later.
int(* fr_dict_keyword_finalise_t)(dict_tokenize_ctx_t *dctx)
#define CURRENT_FRAME(_dctx)
static void dict_attr_location_set(dict_tokenize_ctx_t *dctx, fr_dict_attr_t *da)
fr_dict_attr_t const * relative_attr
for ".82" instead of "1.2.3.82". only for parents of type "tlv"
static int dict_flag_secret(fr_dict_attr_t **da_p, UNUSED char const *value, UNUSED fr_dict_flag_parser_rule_t const *rule)
fr_table_elem_name_t name
Name of the keyword, e.g. "ATTRIBUTE".
fr_dict_keyword_parse_t parse
Function to parse the keyword with.
void dict_dctx_debug(dict_tokenize_ctx_t *dctx)
dict_nest_t
This represents explicit BEGIN/END frames pushed onto the stack.
@ NEST_TOP
top of the stack
@ NEST_VENDOR
BEGIN-VENDOR.
@ NEST_PROTOCOL
BEGIN-PROTOCOL.
@ NEST_ATTRIBUTE
BEGIN foo.
@ NEST_NONE
static int dict_root_set(fr_dict_t *dict, char const *name, unsigned int proto_number)
Set a new root dictionary attribute.
Parser context for dict_from_file.
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:468
static int fr_dlist_insert_tail(fr_dlist_head_t *list_head, void *ptr)
Insert an item into the tail of a list.
Definition dlist.h:360
static void * fr_dlist_next(fr_dlist_head_t const *list_head, void const *ptr)
Get the next item in a list.
Definition dlist.h:537
talloc_free(hp)
int fr_globdir_iter_init(char const **filename, char const *dir, char const *pattern, fr_globdir_iter_t *iter)
Initialize an iterator over filenames.
Definition file.c:693
int fr_globdir_iter_next(char const **filename, fr_globdir_iter_t *iter)
Get the next filename.
Definition file.c:851
char const * fr_cwd_strip(char const *filename)
Intended to be used in logging functions to make output more readable.
Definition file.c:383
int fr_globdir_iter_free(fr_globdir_iter_t *iter)
Definition file.c:885
static int stack_depth
Definition radmin.c:156
fr_type_t
@ FR_TYPE_TIME_DELTA
A period of time measured in nanoseconds.
@ FR_TYPE_TLV
Contains nested attributes.
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_NULL
Invalid (uninitialised) attribute type.
@ FR_TYPE_UINT16
16 Bit unsigned integer.
@ FR_TYPE_INT64
64 Bit signed integer.
@ FR_TYPE_INT16
16 Bit signed integer.
@ FR_TYPE_DATE
Unix time stamp, always has value >2^31.
@ 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_INT32
32 Bit signed integer.
@ FR_TYPE_VENDOR
Attribute that represents a vendor in the attribute tree.
@ FR_TYPE_UINT64
64 Bit unsigned integer.
@ FR_TYPE_BOOL
A truth value.
@ 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
int strncasecmp(char *s1, char *s2, int n)
Definition missing.c:35
int strcasecmp(char *s1, char *s2)
Definition missing.c:65
#define fr_assert(_expr)
Definition rad_assert.h:37
static char const * name
size_t fr_sbuff_adv_past_allowed(fr_sbuff_t *sbuff, size_t len, bool const allowed[static SBUFF_CHAR_CLASS], fr_sbuff_term_t const *tt)
Wind position past characters in the allowed set.
Definition sbuff.c:1818
bool const sbuff_char_class_int[SBUFF_CHAR_CLASS]
Definition sbuff.c:69
#define FR_SBUFF_IN(_start, _len_or_end)
#define FR_SBUFF_IN_STR(_start)
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition snprintf.c:689
fr_aka_sim_id_type_t type
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition syserror.c:243
char const * str
Literal string.
Definition table.h:42
#define fr_table_value_by_str(_table, _name, _def)
Convert a string to a value using a sorted or ordered table.
Definition table.h:653
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition table.h:772
#define TABLE_TYPE_NAME_FUNC_RPTR(_func, _our_table_type, _our_name, _our_def_type, _our_out_type)
Create a type-specific name-to-value function.
Definition table.h:144
static void const * table_sorted_value_by_str(void const *table, size_t table_len, size_t element_size, char const *name)
Convert a string to a value using a lexicographically sorted table.
Definition table.h:328
fr_table_elem_name_t name
Definition table.h:58
An element in an arbitrarily ordered array of name to num mappings.
Definition table.h:57
An element in a lexicographically sorted array of name to num mappings.
Definition table.h:49
char * talloc_typed_asprintf(TALLOC_CTX *ctx, char const *fmt,...)
Call talloc vasprintf, setting the type on the new chunk correctly.
Definition talloc.c:545
#define talloc_asprintf
Definition talloc.h:144
#define talloc_strdup(_ctx, _str)
Definition talloc.h:142
fr_table_num_ordered_t const fr_time_precision_table[]
Definition time.c:46
#define FR_DICTIONARY_FILE
Definition conf.h:7
static fr_slen_t parent
Definition pair.h:858
void fr_strerror_clear(void)
Clears all pending messages from the talloc pools.
Definition strerror.c:576
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
#define fr_strerror_printf_push(_fmt,...)
Add a message to an existing stack of messages at the tail.
Definition strerror.h:84
#define fr_strerror_const_push(_msg)
Definition strerror.h:227
#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
size_t fr_type_table_len
Definition types.c:87
#define fr_type_is_structural(_x)
Definition types.h:392
@ FR_TYPE_UNION
A union of limited children.
Definition types.h:81
@ FR_TYPE_ATTR
A contains an attribute reference.
Definition types.h:83
#define fr_type_is_null(_x)
Definition types.h:347
#define fr_type_is_tlv(_x)
Definition types.h:372
#define fr_type_is_leaf(_x)
Definition types.h:393
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
Definition types.h:454
static fr_type_t fr_type_from_str(char const *type)
Return the constant value representing a type.
Definition types.h:464
#define FR_TYPE_LEAF
Definition types.h:317
int fr_value_box_cast_in_place(TALLOC_CTX *ctx, fr_value_box_t *vb, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv)
Convert one type of fr_value_box_t to another in place.
Definition value.c:4181
ssize_t fr_value_box_from_str(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, char const *in, size_t inlen, fr_sbuff_unescape_rules_t const *erules)
Definition value.c:6053
void fr_value_box_clear(fr_value_box_t *data)
Clear/free any existing value and metadata.
Definition value.c:4362
#define FR_VALUE_BOX_INITIALISER_NULL(_vb)
A static initialiser for stack/globally allocated boxes.
Definition value.h:511
int nonnull(2, 5))
#define fr_value_box_init(_vb, _type, _enumv, _tainted)
Initialise a fr_value_box_t.
Definition value.h:610
int format(printf, 5, 0))
static size_t char ** out
Definition value.h:1030