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