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