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