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