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