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