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