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.
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.
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
17 /** Multi-protocol AVP dictionary API
19 * @file src/lib/util/dict.c
21 * @copyright 2000,2006 The FreeRADIUS server project
27 #include <freeradius-devel/radius/defs.h>
28 #include <freeradius-devel/util/conf.h>
29 #include <freeradius-devel/util/hash.h>
30 #include <freeradius-devel/util/misc.h>
31 #include <freeradius-devel/util/proto.h>
32 #include <freeradius-devel/util/rand.h>
33 #include <freeradius-devel/util/syserror.h>
34 #include <freeradius-devel/util/talloc.h>
37 #ifdef HAVE_SYS_STAT_H
38 # include <sys/stat.h>
43 #define DICT_POOL_SIZE (1024 * 1024 * 2)
44 #define DICT_FIXUP_POOL_SIZE (1024 * 1024 * 1)
46 static TALLOC_CTX *dict_ctx;
47 static fr_hash_table_t *protocol_by_name = NULL; //!< Hash containing names of all the registered protocols.
48 static fr_hash_table_t *protocol_by_num = NULL; //!< Hash containing numbers of all the registered protocols.
49 static char *default_dict_dir; //!< The default location for loading dictionaries if one
52 /** Magic internal dictionary
54 * Internal dictionary is checked in addition to the protocol dictionary
55 * when resolving attribute names.
57 * This is because internal attributes are valid for every
60 fr_dict_t *fr_dict_internal = NULL; //!< Internal server dictionary.
62 typedef struct dict_enum_fixup_s dict_enum_fixup_t;
64 /** A temporary enum value, which we'll resolve later
67 struct dict_enum_fixup_s {
68 char *attribute; //!< we couldn't find (and will need to resolve later).
69 char *alias; //!< Raw enum name.
70 char *value; //!< Raw enum value. We can't do anything with this until
71 //!< we know the attribute type, which we only find out later.
73 dict_enum_fixup_t *next; //!< Next in the linked list of fixups.
76 /** Vendors and attribute names
78 * It's very likely that the same vendors will operate in multiple
79 * protocol spaces, but number their attributes differently, so we need
80 * per protocol dictionaries.
82 * There would also be conflicts for DHCP(v6)/RADIUS attributes etc...
85 bool in_protocol_by_name; //!< Whether the dictionary has been inserted into the
86 ///< protocol_by_name hash.
87 bool in_protocol_by_num; //!< Whether the dictionary has been inserted into the
88 //!< protocol_by_num table.
90 bool autoloaded; //!< manual vs autoload
92 fr_hash_table_t *vendors_by_name; //!< Lookup vendor by name.
93 fr_hash_table_t *vendors_by_num; //!< Lookup vendor by PEN.
95 fr_hash_table_t *attributes_by_name; //!< Allow attribute lookup by unique name.
97 fr_hash_table_t *attributes_combo; //!< Lookup variants of polymorphic attributes.
99 fr_hash_table_t *values_by_da; //!< Lookup an attribute enum by its value.
100 fr_hash_table_t *values_by_alias; //!< Lookup an attribute enum by its alias name.
102 fr_dict_attr_t *root; //!< Root attribute of this dictionary.
104 TALLOC_CTX *pool; //!< Talloc memory pool to reduce allocs.
105 ///< in the dictionary.
108 /** Map data types to min / max data sizes
110 size_t const dict_attr_sizes[FR_TYPE_MAX + 1][2] = {
111 [FR_TYPE_INVALID] = {~0, 0}, //!< Ensure array starts at 0 (umm?)
113 [FR_TYPE_STRING] = {0, ~0},
114 [FR_TYPE_OCTETS] = {0, ~0},
116 [FR_TYPE_IPV4_ADDR] = {4, 4},
117 [FR_TYPE_IPV4_PREFIX] = {6, 6},
118 [FR_TYPE_IPV6_ADDR] = {16, 16},
119 [FR_TYPE_IPV6_PREFIX] = {2, 18},
120 [FR_TYPE_COMBO_IP_ADDR] = {4, 16},
121 [FR_TYPE_IFID] = {8, 8},
122 [FR_TYPE_ETHERNET] = {6, 6},
124 [FR_TYPE_BOOL] = {1, 1},
125 [FR_TYPE_UINT8] = {1, 1},
126 [FR_TYPE_UINT16] = {2, 2},
127 [FR_TYPE_UINT32] = {4, 4},
128 [FR_TYPE_UINT64] = {8, 8},
129 [FR_TYPE_SIZE] = {sizeof(size_t), sizeof(size_t)},
130 [FR_TYPE_INT32] = {4, 4},
132 [FR_TYPE_DATE] = {4, 4},
133 [FR_TYPE_ABINARY] = {32, ~0},
135 [FR_TYPE_TLV] = {2, ~0},
136 [FR_TYPE_STRUCT] = {1, ~0},
138 [FR_TYPE_EXTENDED] = {2, ~0},
139 [FR_TYPE_LONG_EXTENDED] = {3, ~0},
141 [FR_TYPE_VSA] = {4, ~0},
142 [FR_TYPE_EVS] = {6, ~0},
144 [FR_TYPE_MAX] = {~0, 0} //!< Ensure array covers all types.
147 /** Characters allowed in dictionary names
150 bool const fr_dict_attr_allowed_chars[UINT8_MAX] = {
151 ['-'] = true, ['.'] = true, ['/'] = true, ['_'] = true,
152 ['0'] = true, ['1'] = true, ['2'] = true, ['3'] = true, ['4'] = true,
153 ['5'] = true, ['6'] = true, ['7'] = true, ['8'] = true, ['9'] = true,
154 ['A'] = true, ['B'] = true, ['C'] = true, ['D'] = true, ['E'] = true,
155 ['F'] = true, ['G'] = true, ['H'] = true, ['I'] = true, ['J'] = true,
156 ['K'] = true, ['L'] = true, ['M'] = true, ['N'] = true, ['O'] = true,
157 ['P'] = true, ['Q'] = true, ['R'] = true, ['S'] = true, ['T'] = true,
158 ['U'] = true, ['V'] = true, ['W'] = true, ['X'] = true, ['Y'] = true,
160 ['a'] = true, ['b'] = true, ['c'] = true, ['d'] = true, ['e'] = true,
161 ['f'] = true, ['g'] = true, ['h'] = true, ['i'] = true, ['j'] = true,
162 ['k'] = true, ['l'] = true, ['m'] = true, ['n'] = true, ['o'] = true,
163 ['p'] = true, ['q'] = true, ['r'] = true, ['s'] = true, ['t'] = true,
164 ['u'] = true, ['v'] = true, ['w'] = true, ['x'] = true, ['y'] = true,
168 /** Structural data types
171 bool const fr_dict_non_data_types[FR_TYPE_MAX + 1] = {
172 [FR_TYPE_TLV] = true,
173 [FR_TYPE_STRUCT] = true,
174 [FR_TYPE_EXTENDED] = true,
175 [FR_TYPE_LONG_EXTENDED] = true,
176 [FR_TYPE_VSA] = true,
177 [FR_TYPE_EVS] = true,
178 [FR_TYPE_VENDOR] = true
182 * Create the hash of the name.
184 * We copy the hash function here because it's substantially faster.
186 #define FNV_MAGIC_INIT (0x811c9dc5)
187 #define FNV_MAGIC_PRIME (0x01000193)
189 /** Set the internal dictionary if none was provided
191 * @param _dict Dict pointer to check/set.
192 * @param _ret Value to return if no dictionaries are available.
194 #define INTERNAL_IF_NULL(_dict, _ret) \
197 _dict = fr_dict_internal; \
198 if (unlikely(!(_dict))) { \
199 fr_strerror_printf("No dictionaries available for attribute resolution"); \
205 /** Empty callback for hash table initialization
208 static int hash_null_callback(UNUSED void *ctx, UNUSED void *data)
213 static void hash_pool_free(void *to_free)
215 talloc_free(to_free);
218 /** Apply a simple (case insensitive) hashing function to the name of an attribute, vendor or protocol
220 * @param[in] name of the attribute, vendor or protocol.
221 * @param[in] len length of the input string.
223 * @return the hashed derived from the name.
225 static uint32_t dict_hash_name(char const *name, size_t len)
227 uint32_t hash = FNV_MAGIC_INIT;
229 char const *p = name, *q = name + len;
232 int c = *(unsigned char const *)p;
233 if (isalpha(c)) c = tolower(c);
235 hash *= FNV_MAGIC_PRIME;
236 hash ^= (uint32_t)(c & 0xff);
243 /** Wrap name hash function for fr_dict_protocol_t
245 * @param[in] data fr_dict_attr_t to hash.
246 * @return the hash derived from the name of the attribute.
248 static uint32_t dict_protocol_name_hash(void const *data)
252 name = ((fr_dict_t const *)data)->root->name;
254 return dict_hash_name(name, strlen(name));
257 /** Compare two protocol names
260 static int dict_protocol_name_cmp(void const *one, void const *two)
262 fr_dict_t const *a = one;
263 fr_dict_t const *b = two;
265 return strcasecmp(a->root->name, b->root->name);
268 /** Hash a protocol number
271 static uint32_t dict_protocol_num_hash(void const *data)
273 return fr_hash(&(((fr_dict_t const *)data)->root->attr), sizeof(((fr_dict_t const *)data)->root->attr));
276 /** Compare two protocol numbers
279 static int dict_protocol_num_cmp(void const *one, void const *two)
281 fr_dict_t const *a = one;
282 fr_dict_t const *b = two;
284 return a->root->attr - b->root->attr;
287 /** Wrap name hash function for fr_dict_attr_t
289 * @param data fr_dict_attr_t to hash.
290 * @return the hash derived from the name of the attribute.
292 static uint32_t dict_attr_name_hash(void const *data)
296 name = ((fr_dict_attr_t const *)data)->name;
298 return dict_hash_name(name, strlen(name));
301 /** Compare two attribute names
304 static int dict_attr_name_cmp(void const *one, void const *two)
306 fr_dict_attr_t const *a = one, *b = two;
308 return strcasecmp(a->name, b->name);
311 /** Hash a combo attribute
314 static uint32_t dict_attr_combo_hash(void const *data)
317 fr_dict_attr_t const *attr = data;
319 hash = fr_hash(&attr->parent, sizeof(attr->parent)); //-V568
320 hash = fr_hash_update(&attr->type, sizeof(attr->type), hash);
321 return fr_hash_update(&attr->attr, sizeof(attr->attr), hash);
324 /** Compare two combo attribute entries
327 static int dict_attr_combo_cmp(void const *one, void const *two)
329 fr_dict_attr_t const *a = one, *b = two;
332 ret = (a->parent < b->parent) - (a->parent > b->parent);
333 if (ret != 0) return ret;
335 ret = (a->type < b->type) - (a->type > b->type);
336 if (ret != 0) return ret;
338 return (a->attr > b->attr) - (a->attr < b->attr);
341 /** Wrap name hash function for fr_dict_vendor_t
343 * @param data fr_dict_vendor_t to hash.
344 * @return the hash derived from the name of the attribute.
346 static uint32_t dict_vendor_name_hash(void const *data)
350 name = ((fr_dict_vendor_t const *)data)->name;
352 return dict_hash_name(name, strlen(name));
355 /** Compare two attribute names
358 static int dict_vendor_name_cmp(void const *one, void const *two)
360 fr_dict_vendor_t const *a = one;
361 fr_dict_vendor_t const *b = two;
363 return strcasecmp(a->name, b->name);
366 /** Hash a vendor number
369 static uint32_t dict_vendor_pen_hash(void const *data)
371 return fr_hash(&(((fr_dict_vendor_t const *)data)->pen),
372 sizeof(((fr_dict_vendor_t const *)data)->pen));
375 /** Compare two vendor numbers
378 static int dict_vendor_pen_cmp(void const *one, void const *two)
380 fr_dict_vendor_t const *a = one;
381 fr_dict_vendor_t const *b = two;
383 return a->pen - b->pen;
386 /** Hash a dictionary name
389 static uint32_t dict_enum_alias_hash(void const *data)
392 fr_dict_enum_t const *enumv = data;
394 hash = dict_hash_name((void const *)enumv->alias, enumv->alias_len);
396 return fr_hash_update(&enumv->da, sizeof(enumv->da), hash); //-V568
399 /** Compare two dictionary attribute enum values
402 static int dict_enum_alias_cmp(void const *one, void const *two)
405 fr_dict_enum_t const *a = one;
406 fr_dict_enum_t const *b = two;
408 rcode = a->da - b->da;
409 if (rcode != 0) return rcode;
411 return strcasecmp(a->alias, b->alias);
414 /** Hash a dictionary enum value
417 static uint32_t dict_enum_value_hash(void const *data)
420 fr_dict_enum_t const *enumv = data;
422 hash = fr_hash_update((void const *)&enumv->da, sizeof(void *), hash); /* Cast to quiet static analysis */
423 return fr_hash_update((void const *)enumv->value, sizeof(void *), hash);
426 /** Compare two dictionary enum values
429 static int dict_enum_value_cmp(void const *one, void const *two)
431 fr_dict_enum_t const *a = one;
432 fr_dict_enum_t const *b = two;
434 return fr_value_box_cmp(a->value, b->value);
437 /** Validate a new attribute definition
439 * @todo we need to check length of none vendor attributes.
441 * @param[in] dict of protocol context we're operating in.
442 * If NULL the internal dictionary will be used.
443 * @param[in] parent to add attribute under.
444 * @param[in] name of the attribute.
445 * @param[in] attr number.
446 * @param[in] type of attribute.
447 * @param[in] flags to set in the attribute.
449 * - true if attribute definition is valid.
450 * - false if attribute definition is not valid.
452 static bool dict_attr_fields_valid(fr_dict_t *dict, fr_dict_attr_t const *parent,
453 char const *name, int *attr, fr_type_t type, fr_dict_attr_flags_t *flags)
455 fr_dict_attr_t const *v;
457 if (!fr_cond_assert(parent)) return false;
459 if (fr_dict_valid_name(name, -1) <= 0) return false;
461 /******************** sanity check attribute number ********************/
463 if (parent->flags.is_root) {
464 static unsigned int max_attr = UINT8_MAX + 1;
467 if (fr_dict_attr_by_name(dict, name)) return false; /* exists, don't add it again */
471 } else if (*attr <= 0) {
472 fr_strerror_printf("ATTRIBUTE number %i is invalid, must be greater than zero", *attr);
475 } else if ((unsigned int) *attr > max_attr) {
480 * Auto-set internal flags for raddb/dictionary.
481 * So that the end user doesn't have to know
482 * about internal implementation of the server.
484 if ((parent->flags.type_size == 1) &&
485 (*attr >= 3000) && (*attr < 4000)) {
486 flags->internal = true;
491 * Any other negative attribute number is wrong.
494 fr_strerror_printf("ATTRIBUTE number %i is invalid, must be greater than zero", *attr);
499 * type_size is used to limit the maximum attribute number, so it's checked first.
501 if (flags->type_size) {
502 if ((type != FR_TYPE_TLV) && (type != FR_TYPE_VENDOR)) {
503 fr_strerror_printf("The 'format=' flag can only be used with attributes of type 'tlv'");
507 if ((flags->type_size != 1) &&
508 (flags->type_size != 2) &&
509 (flags->type_size != 4)) {
510 fr_strerror_printf("The 'format=' flag can only be used with attributes of type size 1,2 or 4");
516 * If attributes have number greater than 255, do sanity checks.
518 * We assume that the root attribute is of type TLV, with
519 * the appropriate flags set for attributes in this
522 if ((*attr > UINT8_MAX) && !flags->internal) {
523 for (v = parent; v != NULL; v = v->parent) {
524 if ((v->type == FR_TYPE_TLV) || (v->type == FR_TYPE_VENDOR)) {
525 if ((v->flags.type_size < 4) &&
526 (*attr >= (1 << (8 * v->flags.type_size)))) {
527 fr_strerror_printf("Attributes must have value between 1..%u",
528 (1 << (8 * v->flags.type_size)) - 1);
536 /******************** sanity check flags ********************/
539 * virtual attributes are special.
541 if (flags->virtual) {
542 if (!parent->flags.is_root) {
543 fr_strerror_printf("The 'virtual' flag can only be used for normal attributes");
547 if (*attr <= (1 << (8 * parent->flags.type_size))) {
548 fr_strerror_printf("The 'virtual' flag can only be used for non-protocol attributes");
554 * Tags can only be used in a few limited situations.
556 if (flags->has_tag) {
557 if ((type != FR_TYPE_UINT32) && (type != FR_TYPE_STRING)) {
558 fr_strerror_printf("The 'has_tag' flag can only be used for attributes of type 'integer' "
563 if (!(parent->flags.is_root ||
564 ((parent->type == FR_TYPE_VENDOR) &&
565 (parent->parent && parent->parent->type == FR_TYPE_VSA)))) {
566 fr_strerror_printf("The 'has_tag' flag can only be used with RFC and VSA attributes");
570 if (flags->array || flags->has_value || flags->concat || flags->virtual || flags->length) {
571 fr_strerror_printf("The 'has_tag' flag cannot be used with any other flag");
575 if (flags->encrypt && (flags->encrypt != FLAG_ENCRYPT_TUNNEL_PASSWORD)) {
576 fr_strerror_printf("The 'has_tag' flag can only be used with 'encrypt=2'");
582 * 'concat' can only be used in a few limited situations.
585 if (type != FR_TYPE_OCTETS) {
586 fr_strerror_printf("The 'concat' flag can only be used for attributes of type 'octets'");
590 if (!parent->flags.is_root) {
591 fr_strerror_printf("The 'concat' flag can only be used with RFC attributes");
595 if (flags->array || flags->internal || flags->has_value || flags->virtual ||
596 flags->encrypt || flags->length) {
597 fr_strerror_printf("The 'concat' flag cannot be used any other flag");
603 * 'octets[n]' can only be used in a few limited situations.
606 if (flags->has_value || flags->virtual) {
607 fr_strerror_printf("The 'octets[...]' syntax cannot be used any other flag");
611 if (flags->length > 253) {
612 fr_strerror_printf("Invalid length %d", flags->length);
616 if ((type == FR_TYPE_TLV) || (type == FR_TYPE_VENDOR)) {
617 if ((flags->length != 1) &&
618 (flags->length != 2) &&
619 (flags->length != 4)) {
620 fr_strerror_printf("The 'length' flag can only be used with attributes of TLV lengths of 1,2 or 4");
623 } else if (type != FR_TYPE_OCTETS) {
624 fr_strerror_printf("The 'length' flag can only be set for attributes of type 'octets' or 'struct'");
630 * Allow arrays anywhere.
635 fr_strerror_printf("The 'array' flag cannot be used with attributes of type '%s'",
636 fr_int2str(fr_value_box_type_table, type, "<UNKNOWN>"));
639 case FR_TYPE_IPV4_ADDR:
640 case FR_TYPE_IPV6_ADDR:
650 if (flags->internal || flags->has_value || flags->encrypt || flags->virtual) {
651 fr_strerror_printf("The 'array' flag cannot be used any other flag");
657 * 'has_value' should only be set internally. If the
658 * caller sets it, we still sanity check it.
660 if (flags->has_value) {
661 if (type != FR_TYPE_UINT32) {
662 fr_strerror_printf("The 'has_value' flag can only be used with attributes "
663 "of type 'integer'");
667 if (flags->encrypt || flags->virtual) {
668 fr_strerror_printf("The 'has_value' flag cannot be used with any other flag");
673 if (flags->encrypt) {
675 * Stupid hacks for MS-CHAP-MPPE-Keys. The User-Password
676 * encryption method has no provisions for encoding the
677 * length of the data. For User-Password, the data is
678 * (presumably) all printable non-zero data. For
679 * MS-CHAP-MPPE-Keys, the data is binary crap. So... we
680 * MUST specify a length in the dictionary.
682 if ((flags->encrypt == FLAG_ENCRYPT_USER_PASSWORD) && (type != FR_TYPE_STRING)) {
683 if (type != FR_TYPE_OCTETS) {
684 fr_strerror_printf("The 'encrypt=1' flag can only be used with "
685 "attributes of type 'string'");
689 if (flags->length == 0) {
690 fr_strerror_printf("The 'encrypt=1' flag MUST be used with an explicit length for "
691 "'octets' data types");
696 if (flags->encrypt > FLAG_ENCRYPT_OTHER) {
697 fr_strerror_printf("The 'encrypt' flag can only be 0..4");
702 * The Tunnel-Password encryption method can be used anywhere.
704 * We forbid User-Password and Ascend-Send-Secret
705 * methods in the extended space.
707 if ((flags->encrypt != FLAG_ENCRYPT_TUNNEL_PASSWORD) && !flags->internal && !parent->flags.internal) {
708 for (v = parent; v != NULL; v = v->parent) {
710 case FR_TYPE_EXTENDED:
711 case FR_TYPE_LONG_EXTENDED:
713 fr_strerror_printf("The 'encrypt=%d' flag cannot be used with attributes "
714 "of type '%s'", flags->encrypt,
715 fr_int2str(fr_value_box_type_table, type, "<UNKNOWN>"));
728 fr_strerror_printf("The 'encrypt' flag cannot be used with attributes of type '%s'",
729 fr_int2str(fr_value_box_type_table, type, "<UNKNOWN>"));
733 case FR_TYPE_IPV4_ADDR:
736 if (flags->encrypt == FLAG_ENCRYPT_ASCEND_SECRET) goto encrypt_fail;
743 /******************** sanity check data types and parents ********************/
746 * Enforce restrictions on which data types can appear where.
750 * These types may only be parented from the root of the dictionary
752 case FR_TYPE_EXTENDED:
753 case FR_TYPE_LONG_EXTENDED:
755 if (!parent->flags.is_root) {
756 fr_strerror_printf("Attributes of type '%s' can only be used in the RFC space",
757 fr_int2str(fr_value_box_type_table, type, "?Unknown?"));
763 * EVS may only occur under extended and long extended.
766 if ((parent->type != FR_TYPE_EXTENDED) && (parent->type != FR_TYPE_LONG_EXTENDED)) {
767 fr_strerror_printf("Attributes of type 'evs' MUST have a parent of type 'extended', "
768 "instead of '%s'", fr_int2str(fr_value_box_type_table, parent->type, "?Unknown?"));
774 if ((parent->type != FR_TYPE_VSA) && (parent->type != FR_TYPE_EVS)) {
775 fr_strerror_printf("Attributes of type 'vendor' MUST have a parent of type 'vsa' or "
776 "'evs', instead of '%s'",
777 fr_int2str(fr_value_box_type_table, parent->type, "?Unknown?"));
781 if (parent->type == FR_TYPE_VSA) {
782 fr_dict_vendor_t const *dv;
784 dv = fr_dict_vendor_by_num(dict, *attr);
786 flags->type_size = dv->type;
787 flags->length = dv->length;
789 flags->type_size = 1;
793 flags->type_size = 1;
800 * Ensure that type_size and length are set.
802 for (v = parent; v != NULL; v = v->parent) {
803 if ((v->type == FR_TYPE_TLV) || (v->type == FR_TYPE_VENDOR)) {
809 * root is always FR_TYPE_TLV, so we're OK.
812 fr_strerror_printf("Attributes of type '%s' require a parent attribute",
813 fr_int2str(fr_value_box_type_table, type, "?Unknown?"));
818 * Over-ride whatever was there before, so we
819 * don't have multiple formats of VSAs.
821 flags->type_size = v->flags.type_size;
822 flags->length = v->flags.length;
825 case FR_TYPE_COMBO_IP_ADDR:
827 * RFC 6929 says that this is a terrible idea.
829 for (v = parent; v != NULL; v = v->parent) {
830 if (v->type == FR_TYPE_VSA) {
836 fr_strerror_printf("Attributes of type '%s' can only be used in VSA dictionaries",
837 fr_int2str(fr_value_box_type_table, type, "?Unknown?"));
842 case FR_TYPE_INVALID:
843 case FR_TYPE_TIMEVAL:
844 case FR_TYPE_FLOAT64:
845 case FR_TYPE_COMBO_IP_PREFIX:
846 fr_strerror_printf("Attributes of type '%s' cannot be used in dictionaries",
847 fr_int2str(fr_value_box_type_table, type, "?Unknown?"));
855 * Force "length" for data types of fixed length;
868 case FR_TYPE_IPV4_ADDR:
879 flags->length = sizeof(size_t);
882 case FR_TYPE_ETHERNET:
890 case FR_TYPE_IPV6_ADDR:
894 case FR_TYPE_EXTENDED:
895 if (!parent->flags.is_root || (*attr < 241)) {
896 fr_strerror_printf("Attributes of type 'extended' MUST be "
897 "RFC attributes with value >= 241.");
903 case FR_TYPE_LONG_EXTENDED:
904 if (!parent->flags.is_root || (*attr < 241)) {
905 fr_strerror_printf("Attributes of type 'long-extended' MUST "
906 "be RFC attributes with value >= 241.");
914 if (*attr != FR_VENDOR_SPECIFIC) {
915 fr_strerror_printf("Attributes of type 'evs' MUST have attribute code 26, got %i", *attr);
923 * The length is calculated from th children, not
924 * input as the flags.
929 if (flags->encrypt != FLAG_ENCRYPT_NONE) {
930 fr_strerror_printf("Attributes of type 'struct' MUST NOT be encrypted.");
934 if (flags->internal || flags->has_tag || flags->array || flags->concat || flags->virtual) {
935 fr_strerror_printf("Invalid flag for attribute of type 'struct'");
950 * Validate attribute based on parent.
952 if (parent->type == FR_TYPE_STRUCT) {
953 if (flags->encrypt != FLAG_ENCRYPT_NONE) {
954 fr_strerror_printf("Attributes inside a 'struct' MUST NOT be encrypted.");
958 if (flags->internal || flags->has_tag || flags->array || flags->concat || flags->virtual) {
959 fr_strerror_printf("Invalid flag for attribute inside a 'struct'");
964 fr_dict_attr_t const *sibling;
966 sibling = fr_dict_attr_child_by_num(parent, (*attr) - 1);
968 fr_strerror_printf("Children of 'struct' type attributes MUST be numbered consecutively.");
972 if (dict_attr_sizes[sibling->type][1] == ~(size_t) 0) {
973 fr_strerror_printf("Only the last child of a 'struct' attribute can have variable length");
979 * The first child can't be variable length, that's stupid.
981 * STRUCTs will have their length filled in later.
983 if ((type != FR_TYPE_STRUCT) && (flags->length == 0)) {
984 fr_strerror_printf("Children of 'struct' type attributes MUST have fixed length.");
993 /** Allocate a dictionary attribute and assign a name
995 * @param[in] ctx to allocate attribute in.
996 * @param[in] name to set.
999 * - -1 on failure (memory allocation error).
1001 static fr_dict_attr_t *dict_attr_alloc_name(TALLOC_CTX *ctx, char const *name)
1006 fr_strerror_printf("No attribute name provided");
1010 da = talloc_zero(ctx, fr_dict_attr_t);
1011 da->name = talloc_typed_strdup(da, name);
1014 fr_strerror_printf("Out of memory");
1021 /** Initialise fields in a dictionary attribute structure
1023 * @param[in] da to initialise.
1024 * @param[in] parent of the attribute, if none, should be
1025 * the dictionary root.
1026 * @param[in] attr number.
1027 * @param[in] type of the attribute.
1028 * @param[in] flags to assign.
1030 static inline void dict_attr_init(fr_dict_attr_t *da,
1031 fr_dict_attr_t const *parent, int attr,
1032 fr_type_t type, fr_dict_attr_flags_t const *flags)
1037 da->parent = parent;
1038 da->depth = parent ? parent->depth + 1 : 0;
1041 /** Allocate a dictionary attribute on the heap
1043 * @param[in] ctx to allocate the attribute in.
1044 * @param[in] parent of the attribute, if none, should be
1045 * the dictionary root.
1046 * @param[in] name of the attribute. If NULL an OID string
1047 * will be created and set as the name.
1048 * @param[in] attr number.
1049 * @param[in] type of the attribute.
1050 * @param[in] flags to assign.
1052 * - A new fr_dict_attr_t on success.
1053 * - NULL on failure.
1055 static fr_dict_attr_t *dict_attr_alloc(TALLOC_CTX *ctx,
1056 fr_dict_attr_t const *parent,
1057 char const *name, int attr,
1058 fr_type_t type, fr_dict_attr_flags_t const *flags)
1062 if (!fr_cond_assert(parent)) return NULL;
1065 * Allocate a new attribute
1068 char buffer[FR_DICT_ATTR_MAX_NAME_LEN + 1];
1073 memset(&tmp, 0, sizeof(tmp));
1074 dict_attr_init(&tmp, parent, attr, type, flags);
1076 len = snprintf(p, sizeof(buffer), "Attr-");
1079 len = fr_dict_print_attr_oid(p, sizeof(buffer) - (p - buffer), NULL, &tmp);
1080 if (is_truncated(len, sizeof(buffer) - (p - buffer))) {
1081 fr_strerror_printf("OID string too long for unknown attribute");
1085 n = dict_attr_alloc_name(ctx, buffer);
1087 n = dict_attr_alloc_name(ctx, name);
1090 dict_attr_init(n, parent, attr, type, flags);
1096 /** Copy a an existing attribute
1098 * @param[in] ctx to allocate new attribute in.
1099 * @param[in] in attribute to copy.
1101 * - A copy of the input fr_dict_attr_t on success.
1102 * - NULL on failure.
1104 static fr_dict_attr_t *dict_attr_acopy(TALLOC_CTX *ctx, fr_dict_attr_t const *in)
1108 n = dict_attr_alloc_name(ctx, in->name);
1109 if (!n) return NULL;
1111 dict_attr_init(n, in->parent, in->attr, in->type, &in->flags);
1117 /** Allocate a special "reference" attribute
1119 * @param[in] dict of protocol context we're operating in.
1120 * If NULL the internal dictionary will be used.
1121 * @param[in] parent to add attribute under.
1122 * @param[in] name of the attribute.
1123 * @param[in] attr number.
1124 * @param[in] type of attribute.
1125 * @param[in] flags to set in the attribute.
1126 * @param[in] ref This reference attribute is pointing to.
1131 static fr_dict_attr_t *dict_attr_ref_alloc(fr_dict_t *dict, fr_dict_attr_t const *parent,
1132 char const *name, int attr, fr_type_t type,
1133 fr_dict_attr_flags_t const *flags, fr_dict_attr_t const *ref)
1135 fr_dict_attr_ref_t *ref_n;
1138 fr_strerror_printf("No attribute name provided");
1142 ref_n = talloc_zero(dict->pool, fr_dict_attr_ref_t);
1143 ref_n->tlv.name = talloc_typed_strdup(ref_n, name);
1144 if (!ref_n->tlv.name) {
1146 fr_strerror_printf("Out of memory");
1150 dict_attr_init(&ref_n->tlv, parent, attr, type, flags);
1151 ref_n->dict = fr_dict_by_da(ref); /* Cache the dictionary */
1154 return (fr_dict_attr_t *)ref_n;
1157 /** Add a protocol to the global protocol table
1159 * Inserts a protocol into the global protocol table. Uses the root attributes
1160 * of the dictionary for comparisons.
1162 * @param[in] dict of protocol we're inserting.
1167 static int dict_protocol_add(fr_dict_t *dict)
1169 if (!dict->root) return -1; /* Should always have root */
1171 if (!fr_hash_table_insert(protocol_by_name, dict)) {
1172 fr_dict_t *old_proto;
1174 old_proto = fr_hash_table_finddata(protocol_by_name, dict);
1176 fr_strerror_printf("%s: Failed inserting protocol name %s", __FUNCTION__, dict->root->name);
1180 if ((strcmp(old_proto->root->name, dict->root->name) == 0) &&
1181 (old_proto->root->name == dict->root->name)) {
1182 fr_strerror_printf("%s: Duplicate protocol name %s", __FUNCTION__, dict->root->name);
1188 dict->in_protocol_by_name = true;
1190 if (!fr_hash_table_insert(protocol_by_num, dict)) {
1191 fr_strerror_printf("%s: Duplicate protocol number %i", __FUNCTION__, dict->root->attr);
1194 dict->in_protocol_by_num = true;
1199 /** Add a vendor to the dictionary
1201 * Inserts a vendor entry into the vendor hash table. This must be done before adding
1202 * attributes under a VSA.
1204 * @param[in] dict of protocol context we're operating in.
1205 * If NULL the internal dictionary will be used.
1206 * @param[in] name of the vendor.
1207 * @param[in] num Vendor's Private Enterprise Number.
1212 static int dict_vendor_add(fr_dict_t *dict, char const *name, unsigned int num)
1215 fr_dict_vendor_t *vendor;
1217 INTERNAL_IF_NULL(dict, -1);
1220 if (len >= FR_DICT_VENDOR_MAX_NAME_LEN) {
1221 fr_strerror_printf("%s: Vendor name too long", __FUNCTION__);
1225 vendor = talloc_zero(dict, fr_dict_vendor_t);
1226 vendor->name = talloc_typed_strdup(vendor, name);
1227 if (!vendor->name) {
1228 talloc_free(vendor);
1229 fr_strerror_printf("Out of memory");
1233 vendor->type = vendor->length = 1; /* defaults */
1235 if (!fr_hash_table_insert(dict->vendors_by_name, vendor)) {
1236 fr_dict_vendor_t const *old_vendor;
1238 old_vendor = fr_hash_table_finddata(dict->vendors_by_name, vendor);
1240 fr_strerror_printf("%s: Failed inserting vendor name %s", __FUNCTION__, name);
1243 if ((strcmp(old_vendor->name, vendor->name) == 0) && (old_vendor->pen != vendor->pen)) {
1244 fr_strerror_printf("%s: Duplicate vendor name %s", __FUNCTION__, name);
1249 * Already inserted. Discard the duplicate entry.
1251 talloc_free(vendor);
1257 * Insert the SAME pointer (not free'd when this table is
1258 * deleted), into another table.
1260 * We want this behaviour because we want OLD names for
1261 * the attributes to be read from the configuration
1262 * files, but when we're printing them, (and looking up
1263 * by value) we want to use the NEW name.
1265 if (!fr_hash_table_replace(dict->vendors_by_num, vendor)) {
1266 fr_strerror_printf("%s: Failed inserting vendor %s", __FUNCTION__, name);
1273 /** Add a child to a parent.
1275 * @param[in] parent we're adding a child to.
1276 * @param[in] child to add to parent.
1279 * - -1 on failure (memory allocation error).
1281 static inline int dict_attr_child_add(fr_dict_attr_t *parent, fr_dict_attr_t *child)
1283 fr_dict_attr_t const * const *bin;
1284 fr_dict_attr_t **this;
1287 * Setup fields in the child
1289 child->parent = parent;
1290 child->depth = parent->depth + 1;
1295 * We only allocate the pointer array *if* the parent has children.
1297 if (!parent->children) parent->children = talloc_zero_array(parent, fr_dict_attr_t const *, UINT8_MAX + 1);
1298 if (!parent->children) {
1299 fr_strerror_printf("Out of memory");
1303 * Treat the array as a hash of 255 bins, with attributes
1304 * sorted into bins using num % 255.
1306 * Although the various protocols may define numbers higher than 255:
1308 * RADIUS/DHCPv4 - 1-255
1309 * Diameter/Internal - 1-4294967295
1312 * In reality very few will ever use attribute numbers > 500, so for
1313 * the majority of lookups we get O(1) performance.
1315 * Attributes are inserted into the bin in order of their attribute
1316 * numbers to allow slightly more efficient lookups.
1318 bin = &parent->children[child->attr & 0xff];
1320 bool child_is_struct = false;
1321 bool bin_is_struct = false;
1326 * Workaround for vendors that overload the RFC space.
1327 * Structural attributes always take priority.
1329 switch (child->type) {
1330 case FR_TYPE_STRUCTURAL:
1331 child_is_struct = true;
1338 switch ((*bin)->type) {
1339 case FR_TYPE_STRUCTURAL:
1340 bin_is_struct = true;
1347 if (child_is_struct && !bin_is_struct) break;
1348 else if (fr_dict_vendor_num_by_da(child) <= fr_dict_vendor_num_by_da(*bin)) break; /* Prioritise RFC attributes */
1349 else if (child->attr <= (*bin)->attr) break;
1351 bin = &(*bin)->next;
1354 memcpy(&this, &bin, sizeof(this));
1355 child->next = *this;
1361 /** Add an attribute to the name table for the dictionary.
1363 * @param[in] dict of protocol context we're operating in.
1364 * If NULL the internal dictionary will be used.
1365 * @param[in] da to add to the name lookup tables.
1370 static int dict_attr_add_by_name(fr_dict_t *dict, fr_dict_attr_t *da)
1373 * Insert the attribute, only if it's not a duplicate.
1375 if (!fr_hash_table_insert(dict->attributes_by_name, da)) {
1379 * Find the old name. If it's the same name and
1380 * but the parent, or number, or type are
1381 * different, that's an error.
1383 a = fr_hash_table_finddata(dict->attributes_by_name, da);
1384 if (a && (strcasecmp(a->name, da->name) == 0)) {
1385 if ((a->attr != da->attr) || (a->type != da->type) || (a->parent != da->parent)) {
1386 fr_strerror_printf("Duplicate attribute name \"%s\"", da->name);
1393 * Otherwise the attribute has been redefined later
1394 * in the dictionary.
1396 * The original fr_dict_attr_t remains in the
1397 * dictionary but entry in the name hash table is
1398 * updated to point to the new definition.
1400 if (!fr_hash_table_replace(dict->attributes_by_name, da)) {
1401 fr_strerror_printf("Internal error storing attribute");
1407 * Insert copies of the attribute into the
1408 * polymorphic attribute table.
1410 * This allows an abstract attribute type
1411 * like combo IP to be resolved to a
1412 * concrete one later.
1415 case FR_TYPE_COMBO_IP_ADDR:
1417 fr_dict_attr_t *v4, *v6;
1419 v4 = dict_attr_acopy(dict->pool, da);
1420 if (!v4) goto error;
1421 v4->type = FR_TYPE_IPV4_ADDR;
1423 v6 = dict_attr_acopy(dict->pool, da);
1424 if (!v6) goto error;
1425 v6->type = FR_TYPE_IPV6_ADDR;
1427 if (!fr_hash_table_replace(dict->attributes_combo, v4)) {
1428 fr_strerror_printf("Failed inserting IPv4 version of combo attribute");
1432 if (!fr_hash_table_replace(dict->attributes_combo, v6)) {
1433 fr_strerror_printf("Failed inserting IPv6 version of combo attribute");
1439 case FR_TYPE_COMBO_IP_PREFIX:
1441 fr_dict_attr_t *v4, *v6;
1443 v4 = dict_attr_acopy(dict->pool, da);
1444 if (!v4) goto error;
1445 v4->type = FR_TYPE_IPV4_PREFIX;
1447 v6 = dict_attr_acopy(dict->pool, da);
1448 if (!v6) goto error;
1449 v6->type = FR_TYPE_IPV6_PREFIX;
1451 if (!fr_hash_table_replace(dict->attributes_combo, v4)) {
1452 fr_strerror_printf("Failed inserting IPv4 version of combo attribute");
1456 if (!fr_hash_table_replace(dict->attributes_combo, v6)) {
1457 fr_strerror_printf("Failed inserting IPv6 version of combo attribute");
1471 /** Add an reference to the dictionary
1473 * @param[in] dict of protocol context we're operating in.
1474 * If NULL the internal dictionary will be used.
1475 * @param[in] parent to add attribute under.
1476 * @param[in] name of the attribute.
1477 * @param[in] attr number.
1478 * @param[in] type of attribute.
1479 * @param[in] flags to set in the attribute.
1480 * @param[in] ref The attribute we're referencing. May be in a foreign
1486 static int dict_attr_ref_add(fr_dict_t *dict, fr_dict_attr_t const *parent,
1487 char const *name, int attr, fr_type_t type, fr_dict_attr_flags_t const *flags,
1488 fr_dict_attr_t const *ref)
1491 fr_dict_attr_t *mutable;
1492 fr_dict_attr_flags_t our_flags = *flags;
1495 * Check that the definition is valid.
1497 if (!dict_attr_fields_valid(dict, parent, name, &attr, type, &our_flags)) return -1;
1500 * Check we're not creating a direct loop
1502 if (ref->flags.is_reference) {
1503 fr_dict_attr_ref_t const *to_ref = talloc_get_type_abort_const(ref, fr_dict_attr_ref_t);
1505 if (to_ref->to == ref) {
1506 fr_strerror_printf("Circular reference between \"%s\" and \"%s\"", name, ref->name);
1512 * Check the referenced attribute is a root
1513 * or a TLV attribute.
1515 if (!ref->flags.is_root && (ref->type != FR_TYPE_TLV)) {
1516 fr_strerror_printf("Referenced attribute \"%s\" must be of type '%s' not a 'tlv'", ref->name,
1517 fr_int2str(fr_value_box_type_table, ref->type, "<INVALID>"));
1522 * Reference must go from a TLV to a TLV
1524 if (type != FR_TYPE_TLV) {
1525 fr_strerror_printf("Reference attribute must be of type 'tlv', not type '%s'",
1526 fr_int2str(fr_value_box_type_table, type, "<INVALID>"));
1530 n = dict_attr_ref_alloc(dict->pool, parent, name, attr, type, &our_flags, ref);
1533 if (dict_attr_add_by_name(dict, n) < 0) {
1540 * Setup parenting for the attribute
1542 memcpy(&mutable, &parent, sizeof(mutable));
1547 if (dict_attr_child_add(mutable, n) < 0) goto error;
1552 /** Add an attribute to the dictionary
1554 * @param[in] dict of protocol context we're operating in.
1555 * If NULL the internal dictionary will be used.
1556 * @param[in] parent to add attribute under.
1557 * @param[in] name of the attribute.
1558 * @param[in] attr number.
1559 * @param[in] type of attribute.
1560 * @param[in] flags to set in the attribute.
1565 int fr_dict_attr_add(fr_dict_t *dict, fr_dict_attr_t const *parent,
1566 char const *name, int attr, fr_type_t type, fr_dict_attr_flags_t const *flags)
1569 fr_dict_attr_t const *old;
1570 fr_dict_attr_t *mutable;
1571 fr_dict_attr_flags_t our_flags = *flags;
1574 * Check that the definition is valid.
1576 if (!dict_attr_fields_valid(dict, parent, name, &attr, type, &our_flags)) return -1;
1579 * Suppress duplicates.
1581 #define FLAGS_EQUAL(_x) (old->flags._x == flags->_x)
1583 old = fr_dict_attr_by_name(dict, name);
1585 if ((old->parent == parent) && (old->attr == (unsigned int) attr) && (old->type == type) &&
1586 FLAGS_EQUAL(has_tag) && FLAGS_EQUAL(array) && FLAGS_EQUAL(concat) && FLAGS_EQUAL(encrypt)) {
1590 if (old->parent != parent) {
1591 fr_strerror_printf_push("Cannot add duplicate name %s with different parent (old %s, new %s)",
1592 name, old->parent->name, parent->name);
1596 if (old->attr != (unsigned int) attr) {
1597 fr_strerror_printf_push("Cannot add duplicate name %s with different number (old %u, new %d)",
1598 name, old->attr, attr);
1602 if (old->type != type) {
1603 fr_strerror_printf_push("Cannot add duplicate name %s with different type (old %s, new %s)",
1605 fr_int2str(fr_value_box_type_table, old->type, "?Unknown?"),
1606 fr_int2str(fr_value_box_type_table, type, "?Unknown?"));
1610 fr_strerror_printf_push("Cannot add duplicate name %s with different flags",
1615 n = dict_attr_alloc(dict->pool, parent, name, attr, type, &our_flags);
1618 if (dict_attr_add_by_name(dict, n) < 0) {
1625 * Setup parenting for the attribute
1627 memcpy(&mutable, &parent, sizeof(mutable));
1632 if (dict_attr_child_add(mutable, n) < 0) goto error;
1637 /** Add a value alias
1639 * Aliases are textual (string) aliases for a given value.
1641 * Value aliases are not limited to integers, and may be added for any non-structural
1644 * @param[in] da to add enumeration value to.
1645 * @param[in] alias Name of value alias.
1646 * @param[in] value to associate with alias.
1647 * @param[in] coerce if the type of the value does not match the
1648 * type of the da, attempt to cast it to match
1649 * the type of the da. If this is false and there's
1650 * a type mismatch, we fail.
1651 * We also fail if the value cannot be coerced to
1652 * the attribute type.
1653 * @param[in] takes_precedence This alias should take precedence over previous
1654 * aliases for the same value, when resolving value
1660 int fr_dict_enum_add_alias(fr_dict_attr_t const *da, char const *alias,
1661 fr_value_box_t const *value,
1662 bool coerce, bool takes_precedence)
1666 fr_dict_enum_t *enumv = NULL;
1667 fr_value_box_t *enum_value = NULL;
1670 fr_strerror_printf("%s: Dictionary attribute not specified", __FUNCTION__);
1675 fr_strerror_printf("%s: Empty names are not permitted", __FUNCTION__);
1679 len = strlen(alias);
1680 if (len >= FR_DICT_ENUM_MAX_NAME_LEN) {
1681 fr_strerror_printf("%s: Value name too long", __FUNCTION__);
1685 dict = fr_dict_by_da(da);
1687 enumv = talloc_zero(dict->pool, fr_dict_enum_t);
1689 fr_strerror_printf("%s: Out of memory", __FUNCTION__);
1692 enumv->alias = talloc_typed_strdup(enumv, alias);
1693 enumv->alias_len = strlen(alias);
1694 enum_value = fr_value_box_alloc(enumv, da->type, NULL, false);
1696 if (da->type != value->type) {
1698 fr_strerror_printf("%s: Type mismatch between attribute (%s) and enum (%s)",
1700 fr_int2str(fr_value_box_type_table, da->type, "<INVALID>"),
1701 fr_int2str(fr_value_box_type_table, value->type, "<INVALID>"));
1705 if (fr_value_box_cast(enumv, enum_value, da->type, NULL, value) < 0) {
1706 fr_strerror_printf_push("%s: Failed coercing enum type (%s) to attribute type (%s)",
1708 fr_int2str(fr_value_box_type_table, value->type, "<INVALID>"),
1709 fr_int2str(fr_value_box_type_table, da->type, "<INVALID>"));
1714 if (fr_value_box_copy(enum_value, enum_value, value) < 0) {
1715 fr_strerror_printf_push("%s: Failed copying value into enum", __FUNCTION__);
1720 enumv->value = enum_value;
1724 * Add the value into the dictionary.
1727 fr_dict_attr_t *tmp;
1728 memcpy(&tmp, &enumv, sizeof(tmp));
1730 if (!fr_hash_table_insert(dict->values_by_alias, tmp)) {
1731 fr_dict_enum_t *old;
1734 * Suppress duplicates with the same
1735 * name and value. There are lots in
1736 * dictionary.ascend.
1738 old = fr_dict_enum_by_alias(da, alias, -1);
1739 if (!fr_cond_assert(old)) return -1;
1741 if (fr_value_box_cmp(old->value, enumv->value) == 0) {
1746 fr_strerror_printf("Duplicate VALUE alias \"%s\" for attribute \"%s\". "
1747 "Old value was \"%pV\", new value was \"%pV\"", alias, da->name,
1748 old->value, enumv->value);
1755 * There are multiple VALUE's, keyed by attribute, so we
1756 * take care of that here.
1758 if (takes_precedence) {
1759 if (!fr_hash_table_replace(dict->values_by_da, enumv)) {
1760 fr_strerror_printf("%s: Failed inserting value %s", __FUNCTION__, alias);
1764 (void) fr_hash_table_insert(dict->values_by_da, enumv);
1768 * Mark the attribute up as having an enumv
1771 fr_dict_attr_t *mutable;
1773 memcpy(&mutable, &da, sizeof(mutable));
1775 mutable->flags.has_value = 1;
1781 /** Add an alias to an integer attribute hashing the alias for the integer value
1784 int fr_dict_enum_add_alias_next(fr_dict_attr_t const *da, char const *alias)
1786 fr_value_box_t v = {
1789 fr_value_box_t s = {
1793 if (fr_dict_enum_by_alias(da, alias, -1)) return 0;
1797 v.vb_int8 = s.vb_int8 = fr_hash_string(alias) & INT8_MAX;
1801 v.vb_int16 = s.vb_int16 = fr_hash_string(alias) & INT16_MAX;
1805 v.vb_int32 = s.vb_int32 = fr_hash_string(alias) & INT32_MAX;
1809 v.vb_int64 = s.vb_int64 = fr_hash_string(alias) & INT64_MAX;
1813 v.vb_uint8 = s.vb_uint8 = fr_hash_string(alias) & UINT8_MAX;
1816 case FR_TYPE_UINT16:
1817 v.vb_uint16 = s.vb_uint16 = fr_hash_string(alias) & UINT16_MAX;
1820 case FR_TYPE_UINT32:
1821 v.vb_uint32 = s.vb_uint32 = fr_hash_string(alias) & UINT32_MAX;
1824 case FR_TYPE_UINT64:
1825 v.vb_uint64 = s.vb_uint64 = fr_hash_string(alias) & UINT64_MAX;
1829 fr_strerror_printf("Attribute is wrong type for auto-numbering, expected numeric type, got %s",
1830 fr_int2str(fr_value_box_type_table, da->type, "?Unknown?"));
1835 * If there's no existing value, add an enum
1836 * with the hash value of the alias.
1838 * This helps with debugging as the values
1841 if (!fr_dict_enum_by_value(da, &v)) {
1843 return fr_dict_enum_add_alias(da, alias, &v, false, false);
1847 fr_value_box_increment(&v);
1849 if (fr_value_box_cmp_op(T_OP_CMP_EQ, &v, &s) == 0) {
1850 fr_strerror_printf("No free integer values for enumeration");
1854 if (!fr_dict_enum_by_value(da, &v)) goto add;
1859 /** Copy a known or unknown attribute to produce an unknown attribute
1861 * Will copy the complete hierarchy down to the first known attribute.
1863 fr_dict_attr_t *fr_dict_unknown_acopy(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
1866 fr_dict_attr_t const *parent;
1869 * Allocate an attribute.
1871 n = dict_attr_alloc_name(ctx, da->name);
1872 if (!n) return NULL;
1875 * We want to have parent / child relationships, AND to
1876 * copy all unknown parents, AND to free the unknown
1877 * parents when this 'da' is freed. We therefore talloc
1878 * the parent from the 'da'.
1880 if (da->parent->flags.is_unknown) {
1881 parent = fr_dict_unknown_acopy(n, da->parent);
1888 parent = da->parent;
1892 * Initialize the rest of the fields.
1894 dict_attr_init(n, parent, da->attr, da->type, &da->flags);
1901 /** Converts an unknown to a known by adding it to the internal dictionaries.
1903 * Does not free old #fr_dict_attr_t, that is left up to the caller.
1905 * @param[in] dict of protocol context we're operating in.
1906 * If NULL the internal dictionary will be used.
1907 * @param[in] old unknown attribute to add.
1909 * - Existing #fr_dict_attr_t if old was found in a dictionary.
1910 * - A new entry representing old.
1912 fr_dict_attr_t const *fr_dict_unknown_add(fr_dict_t *dict, fr_dict_attr_t const *old)
1914 fr_dict_attr_t const *da;
1915 fr_dict_attr_t const *parent;
1916 fr_dict_attr_flags_t flags;
1918 da = fr_dict_attr_by_name(dict, old->name);
1922 * Define the complete unknown hierarchy
1924 if (old->parent && old->parent->flags.is_unknown) {
1925 parent = fr_dict_unknown_add(dict, old->parent);
1927 fr_strerror_printf_push("Failed adding parent \"%s\"", old->parent->name);
1931 #ifdef __clang_analyzer__
1932 if (!old->parent) return NULL;
1934 parent = old->parent;
1937 memcpy(&flags, &old->flags, sizeof(flags));
1938 flags.is_unknown = false;
1939 flags.is_raw = true;
1942 * If this is a vendor, we skip most of the sanity
1943 * checks and add it to the vendor hash, and add it
1944 * as a child attribute to the Vendor-Specific
1947 if (old->type == FR_TYPE_VENDOR) {
1948 fr_dict_attr_t *mutable, *n;
1950 if (dict_vendor_add(dict, old->name, old->attr) < 0) return NULL;
1952 n = dict_attr_alloc(dict->pool, parent, old->name, old->attr, old->type, &flags);
1955 * Setup parenting for the attribute
1957 memcpy(&mutable, &old->parent, sizeof(mutable));
1958 if (dict_attr_child_add(mutable, n) < 0) return NULL;
1964 * Look up the attribute by number. If it doesn't exist,
1965 * add it both by name and by number. If it does exist,
1966 * add it only by name.
1968 da = fr_dict_attr_child_by_num(parent, old->attr);
1972 n = dict_attr_alloc(dict->pool, parent, old->name, old->attr, old->type, &flags);
1973 if (!n) return NULL;
1976 * Add the unknown by NAME. e.g. if the admin does "Attr-26", we want
1977 * to return "Attr-26", and NOT "Vendor-Specific". The rest of the server
1978 * is responsible for converting "Attr-26 = 0x..." to an actual attribute,
1981 if (dict_attr_add_by_name(dict, n) < 0) {
1989 #ifdef __clang_analyzer__
1990 if (!old->name) return NULL;
1994 * Add the attribute by both name and number.
1996 if (fr_dict_attr_add(dict, parent, old->name, old->attr, old->type, &flags) < 0) return NULL;
1999 * For paranoia, return it by name.
2001 return fr_dict_attr_by_name(dict, old->name);
2004 /** Free dynamically allocated (unknown attributes)
2006 * If the da was dynamically allocated it will be freed, else the function
2007 * will return without doing anything.
2009 * @param[in] da to free.
2011 void fr_dict_unknown_free(fr_dict_attr_t const **da)
2013 fr_dict_attr_t **tmp;
2015 if (!da || !*da) return;
2017 /* Don't free real DAs */
2018 if (!(*da)->flags.is_unknown) {
2022 memcpy(&tmp, &da, sizeof(*tmp));
2028 /** Build an unknown vendor, parented by a VSA or EVS attribute
2030 * This allows us to complete the path back to the dictionary root in the case
2031 * of unknown attributes with unknown vendors.
2033 * @note Will return known vendors attributes where possible. Do not free directly,
2034 * use #fr_dict_unknown_free.
2036 * @param[in] ctx to allocate the vendor attribute in.
2037 * @param[out] out Where to write point to new unknown dict attr
2038 * representing the unknown vendor.
2039 * @param[in] parent of the vendor attribute, either an EVS or VSA attribute.
2040 * @param[in] vendor id.
2045 int fr_dict_unknown_vendor_afrom_num(TALLOC_CTX *ctx, fr_dict_attr_t **out,
2046 fr_dict_attr_t const *parent, unsigned int vendor)
2048 fr_dict_attr_flags_t flags = {
2055 if (!fr_cond_assert(parent)) {
2056 fr_strerror_printf("%s: Invalid argument - parent was NULL", __FUNCTION__);
2063 * Vendor attributes can occur under VSA or EVS attributes.
2065 switch (parent->type) {
2068 if (!fr_cond_assert(!parent->flags.is_unknown)) return -1;
2070 *out = dict_attr_alloc(ctx, parent, NULL, vendor, FR_TYPE_VENDOR, &flags);
2074 case FR_TYPE_VENDOR:
2075 if (!fr_cond_assert(!parent->flags.is_unknown)) return -1;
2076 fr_strerror_printf("Unknown vendor cannot be parented by another vendor");
2080 fr_strerror_printf("Unknown vendors can only be parented by 'vsa' or 'evs' "
2081 "attributes, not '%s'", fr_int2str(fr_value_box_type_table, parent->type, "?Unknown?"));
2086 /** Allocates an unknown attribute
2088 * @note If vendor != 0, an unknown vendor (may) also be created, parented by
2089 * the correct EVS or VSA attribute. This is accessible via da->parent,
2090 * and will be use the unknown da as its talloc parent.
2092 * @param[in] ctx to allocate DA in.
2093 * @param[in] parent of the unknown attribute (may also be unknown).
2094 * @param[in] attr number.
2095 * @param[in] vendor number.
2096 * @return 0 on success.
2098 fr_dict_attr_t const *fr_dict_unknown_afrom_fields(TALLOC_CTX *ctx, fr_dict_attr_t const *parent,
2099 unsigned int vendor, unsigned int attr)
2101 fr_dict_attr_t const *da;
2103 fr_dict_attr_t *new_parent = NULL;
2104 fr_dict_attr_flags_t flags = {
2109 if (!fr_cond_assert(parent)) {
2110 fr_strerror_printf("%s: Invalid argument - parent was NULL", __FUNCTION__);
2115 * If there's a vendor specified, we check to see
2116 * if the parent is a VSA or EVS, and if it is
2117 * we either lookup the vendor to get the correct
2118 * attribute, or bridge the gap in the tree, with an
2121 * We need to do the check, as the parent could be
2122 * a TLV, in which case the vendor should be known
2123 * and we don't need to modify the parent.
2125 if (vendor && ((parent->type == FR_TYPE_VSA) || (parent->type == FR_TYPE_EVS))) {
2126 da = fr_dict_attr_child_by_num(parent, vendor);
2128 if (fr_dict_unknown_vendor_afrom_num(ctx, &new_parent, parent, vendor) < 0) return NULL;
2134 * Need to clone the unknown hierachy, as unknown
2135 * attributes must parent the complete heirachy,
2136 * and cannot share any parts with any other unknown
2139 } else if (parent->flags.is_unknown) {
2140 new_parent = fr_dict_unknown_acopy(ctx, parent);
2141 parent = new_parent;
2144 n = dict_attr_alloc(ctx, parent, NULL, attr, FR_TYPE_OCTETS, &flags);
2145 if (!n) return NULL;
2148 * The config files may reference the unknown by name.
2149 * If so, use the pre-defined name instead of an unknown
2152 da = fr_dict_attr_by_name(fr_dict_by_da(parent), n->name);
2154 fr_dict_unknown_free(&parent);
2156 fr_dict_unknown_free(&parent);
2161 * Ensure the parent is freed at the same time as the
2162 * unknown DA. This should be OK as we never parent
2163 * multiple unknown attributes off the same parent.
2165 if (new_parent && new_parent->flags.is_unknown) talloc_steal(n, new_parent);
2170 /** Initialise a fr_dict_attr_t from an ASCII attribute and value
2172 * Where the attribute name is in the form:
2174 * - Attr-%d.%d.%d...
2176 * @copybrief fr_dict_unknown_afrom_fields
2178 * @param[in] ctx to allocate the attribute in.
2179 * @param[out] out Where to write the new attribute to.
2180 * @param[in] parent of the unknown attribute (may also be unknown).
2181 * @param[in] num of the unknown attribute.
2186 static int dict_unknown_attr_afrom_num(TALLOC_CTX *ctx, fr_dict_attr_t **out,
2187 fr_dict_attr_t const *parent, unsigned long num)
2190 fr_dict_attr_flags_t flags = {
2195 if (!fr_cond_assert(parent)) {
2196 fr_strerror_printf("%s: Invalid argument - parent was NULL", __FUNCTION__);
2202 da = dict_attr_alloc(ctx, parent, NULL, num, FR_TYPE_OCTETS, &flags);
2210 /** Create a fr_dict_attr_t from an ASCII attribute and value
2212 * Where the attribute name is in the form:
2214 * - Attr-%d.%d.%d...
2216 * @copybrief fr_dict_unknown_afrom_fields
2218 * @note If vendor != 0, an unknown vendor (may) also be created, parented by
2219 * the correct EVS or VSA attribute. This is accessible via vp->parent,
2220 * and will be use the unknown da as its talloc parent.
2222 * @param[in] ctx to alloc new attribute in.
2223 * @param[out] out Where to write the head of the chain unknown
2224 * dictionary attributes.
2225 * @param[in] parent Attribute to use as the root for resolving OIDs in.
2226 * Usually the root of a protocol dictionary.
2227 * @param[in] oid_str of attribute.
2229 * - The number of bytes parsed on success.
2230 * - <= 0 on failure. Negative offset indicates parse error position.
2232 ssize_t fr_dict_unknown_afrom_oid_str(TALLOC_CTX *ctx, fr_dict_attr_t **out,
2233 fr_dict_attr_t const *parent, char const *oid_str)
2235 char const *p = oid_str, *end = oid_str + strlen(oid_str);
2236 fr_dict_attr_t const *our_parent = parent;
2237 fr_dict_attr_t *n = NULL, *our_da;
2238 fr_dict_attr_flags_t flags = {
2243 if (!fr_cond_assert(parent)) {
2244 fr_strerror_printf("%s: Invalid argument - parent was NULL", __FUNCTION__);
2250 if (fr_dict_valid_oid_str(oid_str, -1) < 0) return -1;
2253 * All unknown attributes are of the form "Attr-#.#.#.#"
2255 if (strncasecmp(p, "Attr-", 5) != 0) {
2256 fr_strerror_printf("Unknown attribute '%s'", oid_str);
2262 * Allocate the final attribute first, so that any
2263 * unknown parents can be freed when this da is freed.
2265 * See fr_dict_unknown_acopy() for more details.
2267 * Note also that we copy the input name, even if it is
2270 * While the name of this attribute is "Attr-#.#.#", one
2271 * or more of the leading components may, in fact, be
2274 n = dict_attr_alloc_name(ctx, oid_str);
2277 * While the name of this attribu
2281 fr_dict_attr_t const *da = NULL;
2283 if (fr_dict_oid_component(&num, &p) < 0) {
2286 return -(p - oid_str);
2291 * Structural attribute
2294 if (!our_parent) goto is_root;
2296 da = fr_dict_attr_child_by_num(our_parent, num);
2297 if (!da) { /* Unknown component */
2298 switch (our_parent->type) {
2301 da = fr_dict_attr_child_by_num(our_parent, num);
2302 if (!fr_cond_assert(!da || (da->type == FR_TYPE_VENDOR))) goto error;
2305 if (fr_dict_unknown_vendor_afrom_num(n, &our_da,
2306 our_parent, num) < 0) {
2314 case FR_TYPE_EXTENDED:
2315 case FR_TYPE_LONG_EXTENDED:
2317 if (dict_unknown_attr_afrom_num(n, &our_da, our_parent, num) < 0) {
2324 * Can't have a FR_TYPE_STRING inside a
2325 * FR_TYPE_STRING (for example)
2328 fr_strerror_printf("Parent OID component (%s) in \"%.*s\" specified a "
2329 "non-structural type (%s)", our_parent->name,
2330 (int)(p - oid_str), oid_str,
2331 fr_int2str(fr_value_box_type_table,
2332 our_parent->type, "<INVALID>"));
2343 dict_attr_init(n, our_parent, num, FR_TYPE_OCTETS, &flags);
2350 * @todo - if we really care about normalization, re-print the name here, normalized.
2357 return end - oid_str;
2360 /** Create a dictionary attribute by name embedded in another string
2362 * Find the first invalid attribute name char in the string pointed to by name.
2364 * Copy the characters between the start of the name string and the first none
2365 * #fr_dict_attr_allowed_chars char to a buffer and initialise da as an unknown
2368 * @param[in] ctx To allocate unknown #fr_dict_attr_t in.
2369 * @param[out] out Where to write the head of the chain unknown
2370 * dictionary attributes.
2371 * @param[in] parent Attribute to use as the root for resolving OIDs in.
2372 * Usually the root of a protocol dictionary.
2373 * @param[in] name string start.
2375 * - <= 0 on failure.
2376 * - The number of bytes of name consumed on success.
2378 ssize_t fr_dict_unknown_afrom_oid_substr(TALLOC_CTX *ctx, fr_dict_attr_t **out,
2379 fr_dict_attr_t const *parent, char const *name)
2383 char buffer[FR_DICT_ATTR_MAX_NAME_LEN + 1];
2386 if (!name || !*name) return 0;
2389 * Advance p until we get something that's not part of
2390 * the dictionary attribute name.
2392 for (p = name; fr_dict_attr_allowed_chars[(uint8_t)*p] || (*p == '.') || (*p == '-'); p++);
2395 if (len > FR_DICT_ATTR_MAX_NAME_LEN) {
2396 fr_strerror_printf("Attribute name too long");
2400 fr_strerror_printf("Invalid attribute name");
2403 strlcpy(buffer, name, len + 1);
2405 slen = fr_dict_unknown_afrom_oid_str(ctx, out, parent, buffer);
2406 if (slen <= 0) return slen;
2412 /** Check to see if we can convert a nested TLV structure to known attributes
2414 * @param[in] dict to search in.
2415 * @param[in] da Nested tlv structure to convert.
2417 * - NULL if we can't.
2418 * - Known attribute if we can.
2420 fr_dict_attr_t const *fr_dict_attr_known(fr_dict_t *dict, fr_dict_attr_t const *da)
2422 INTERNAL_IF_NULL(dict, NULL);
2424 if (!da->flags.is_unknown) return da; /* It's known */
2427 fr_dict_attr_t const *parent;
2429 parent = fr_dict_attr_known(dict, da->parent);
2430 if (!parent) return NULL;
2432 return fr_dict_attr_child_by_num(parent, da->attr);
2435 if (dict->root == da) return dict->root;
2439 ssize_t fr_dict_snprint_flags(char *out, size_t outlen, fr_dict_attr_flags_t const *flags)
2441 char *p = out, *end = p + outlen;
2446 #define FLAG_SET(_flag) \
2448 if (flags->_flag) {\
2449 p += strlcpy(p, STRINGIFY(_flag)",", end - p);\
2450 if (p >= end) return -1;\
2455 FLAG_SET(is_unknown);
2457 FLAG_SET(is_reference);
2461 FLAG_SET(has_value);
2466 if (flags->encrypt) {
2467 p += snprintf(p, end - p, "encrypt=%i,", flags->encrypt);
2468 if (p >= end) return -1;
2471 if (flags->length) {
2472 p += snprintf(p, end - p, "length=%i,", flags->length);
2473 if (p >= end) return -1;
2476 if (!out[0]) return -1;
2482 if (out[len - 1] == ',') out[len - 1] = '\0';
2487 void fr_dict_print(fr_dict_attr_t const *da, int depth)
2493 fr_dict_snprint_flags(buff, sizeof(buff), &da->flags);
2500 case FR_TYPE_EXTENDED:
2512 case FR_TYPE_VENDOR:
2516 case FR_TYPE_LONG_EXTENDED:
2517 name = "LONG EXTENDED";
2520 case FR_TYPE_STRUCT:
2533 printf("%u%.*s%s \"%s\" vendor: %x (%u), num: %x (%u), type: %s, flags: %s\n", da->depth, depth,
2534 "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t", name, da->name,
2535 fr_dict_vendor_num_by_da(da), fr_dict_vendor_num_by_da(da), da->attr, da->attr,
2536 fr_int2str(fr_value_box_type_table, da->type, "?Unknown?"), buff);
2538 if (da->children) for (i = 0; i < talloc_array_length(da->children); i++) {
2539 if (da->children[i]) {
2540 fr_dict_attr_t const *bin;
2542 for (bin = da->children[i]; bin; bin = bin->next) fr_dict_print(bin, depth + 1);
2547 /** Find a common ancestor that two TLV type attributes share
2549 * @param[in] a first TLV attribute.
2550 * @param[in] b second TLV attribute.
2551 * @param[in] is_ancestor Enforce a->b relationship (a is parent or ancestor of b).
2553 * - Common ancestor if one exists.
2554 * - NULL if no common ancestor exists.
2556 fr_dict_attr_t const *fr_dict_parent_common(fr_dict_attr_t const *a, fr_dict_attr_t const *b, bool is_ancestor)
2559 fr_dict_attr_t const *p_a, *p_b;
2561 if (!a || !b) return NULL;
2563 if (is_ancestor && (b->depth <= a->depth)) return NULL;
2566 * Find a common depth to work back from
2568 if (a->depth > b->depth) {
2570 for (p_a = a, i = a->depth - b->depth; p_a && (i > 0); p_a = p_a->parent, i--);
2571 } else if (a->depth < b->depth) {
2573 for (p_b = b, i = b->depth - a->depth; p_b && (i > 0); p_b = p_b->parent, i--);
2579 while (p_a && p_b) {
2580 if (p_a == p_b) return p_a;
2589 /** Process a single OID component
2591 * @param[out] out Value of component.
2592 * @param[in] oid string to parse.
2595 * - -1 on format error.
2597 int fr_dict_oid_component(unsigned int *out, char const **oid)
2599 char const *p = *oid;
2605 num = strtoul(p, &q, 10);
2606 if ((p == q) || (num == ULONG_MAX)) {
2607 fr_strerror_printf("Invalid OID component \"%s\" (%lu)", p, num);
2615 *out = (unsigned int)num;
2620 fr_strerror_printf("Unexpected text after OID component");
2626 /** Build the tlv_stack for the specified DA and encode the path in OID form
2628 * @param[out] out Where to write the OID.
2629 * @param[in] outlen Length of the output buffer.
2630 * @param[in] ancestor If not NULL, only print OID portion between
2632 * @param[in] da to print OID string for.
2633 * @return the number of bytes written to the buffer.
2635 size_t fr_dict_print_attr_oid(char *out, size_t outlen,
2636 fr_dict_attr_t const *ancestor, fr_dict_attr_t const *da)
2639 char *p = out, *end = p + outlen;
2642 fr_dict_attr_t const *tlv_stack[FR_DICT_MAX_TLV_STACK + 1];
2644 if (!outlen) return 0;
2647 * If the ancestor and the DA match, there's
2648 * no OID string to print.
2650 if (ancestor == da) {
2655 fr_proto_tlv_stack_build(tlv_stack, da);
2658 if (tlv_stack[ancestor->depth - 1] != ancestor) {
2659 fr_strerror_printf("Attribute \"%s\" is not a descendent of \"%s\"", da->name, ancestor->name);
2662 depth = ancestor->depth;
2666 * We don't print the ancestor, we print the OID
2667 * between it and the da.
2669 len = snprintf(p, end - p, "%u", tlv_stack[depth]->attr);
2670 if ((p + len) >= end) return p - out;
2674 for (i = depth + 1; i < (int)da->depth; i++) {
2675 len = snprintf(p, end - p, ".%u", tlv_stack[i]->attr);
2676 if ((p + len) >= end) return p - out;
2683 /** Get the leaf attribute of an OID string
2685 * @note On error, vendor will be set (if present), parent will be the
2686 * maximum depth we managed to resolve to, and attr will be the child
2687 * we failed to resolve.
2689 * @param[in] dict of protocol context we're operating in.
2690 * If NULL the internal dictionary will be used.
2691 * @param[out] attr Number we parsed.
2692 * @param[in,out] parent attribute (or root of dictionary).
2693 * Will be updated to the parent directly beneath the leaf.
2694 * @param[in] oid string to parse.
2696 * - > 0 on success (number of bytes parsed).
2697 * - <= 0 on parse error (negative offset of parse error).
2699 ssize_t fr_dict_attr_by_oid(fr_dict_t *dict, fr_dict_attr_t const **parent, unsigned int *attr, char const *oid)
2701 char const *p = oid;
2702 unsigned int num = 0;
2706 * It's a partial OID. Grab it, and skip to the next bit.
2714 if (fr_dict_oid_component(&num, &p) < 0) return oid - p;
2717 * Record progress even if we error out.
2719 * Don't change this, you will break things.
2723 switch ((*parent)->type) {
2724 case FR_TYPE_STRUCTURAL:
2728 fr_strerror_printf("Attribute %s (%i) is not a TLV, so cannot contain a child attribute. "
2729 "Error at sub OID \"%s\"", (*parent)->name, (*parent)->attr, oid);
2730 return 0; /* We parsed nothing */
2734 * If it's not a vendor type, it must be between 0..8*type_size
2736 * @fixme: find the TLV parent, and check it's size
2738 if (((*parent)->type != FR_TYPE_VENDOR) && ((*parent)->type != FR_TYPE_VSA) && !(*parent)->flags.is_root &&
2739 (num > UINT8_MAX)) {
2740 fr_strerror_printf("TLV attributes must be between 0..255 inclusive");
2746 * We've not hit the leaf yet, so the attribute must be
2751 fr_dict_attr_t const *child;
2754 child = fr_dict_attr_child_by_num(*parent, num);
2756 fr_strerror_printf("Unknown attribute \"%i\" in OID string \"%s\"", num, oid);
2757 return 0; /* We parsed nothing */
2761 * Record progress even if we error out.
2763 * Don't change this, you will break things.
2767 slen = fr_dict_attr_by_oid(dict, parent, attr, p);
2768 if (slen <= 0) return slen - (p - oid);
2769 return slen + (p - oid);
2773 * Hit the leaf, this is the attribute we need to define.
2780 fr_strerror_printf("Malformed OID string, got trailing garbage '%s'", p);
2785 /** Return the root attribute of a dictionary
2787 * @param dict to return root for.
2788 * @return the root attribute of the dictionary.
2790 fr_dict_attr_t const *fr_dict_root(fr_dict_t const *dict)
2792 if (!dict) return fr_dict_internal->root; /* Remove me when dictionaries are done */
2796 /** Look up a protocol name embedded in another string
2798 * @param[out] out the resolve dictionary or NULL if the dictionary
2799 * couldn't be resolved.
2800 * @param[in] name string start.
2801 * @param[in] dict_def The dictionary to return if no dictionary qualifier was found.
2803 * - 0 and *out != NULL. Couldn't find a dictionary qualifier, so returned dict_def.
2804 * - <= 0 on error and (*out == NULL) (offset as negative integer)
2805 * - > 0 on success (number of bytes parsed).
2807 ssize_t fr_dict_by_protocol_substr(fr_dict_t const **out, char const *name, fr_dict_t const *dict_def)
2809 fr_dict_attr_t root;
2815 if (!protocol_by_name || !name || !*name || !out) return 0;
2817 memset(&root, 0, sizeof(root));
2820 * Advance p until we get something that's not part of
2821 * the dictionary attribute name.
2823 for (p = name; fr_dict_attr_allowed_chars[(uint8_t)*p] && (*p != '.'); p++);
2826 * If what we stopped at wasn't a '.', then there
2827 * can't be a protocol name in this string.
2830 memcpy(out, &dict_def, sizeof(*out));
2835 if (len > FR_DICT_ATTR_MAX_NAME_LEN) {
2836 fr_strerror_printf("Attribute name too long");
2837 return -(FR_DICT_ATTR_MAX_NAME_LEN);
2840 root.name = talloc_bstrndup(NULL, name, len);
2842 fr_strerror_printf("Out of memory");
2846 dict = fr_hash_table_finddata(protocol_by_name, &(fr_dict_t){ .root = &root });
2847 talloc_const_free(root.name);
2850 fr_strerror_printf("Unknown protocol '%.*s'", (int) len, name);
2859 /** Lookup a protocol by its name
2861 * @param[in] name of the protocol to locate.
2863 * - Attribute matching name.
2864 * - NULL if no matching protocolibute could be found.
2866 fr_dict_t *fr_dict_by_protocol_name(char const *name)
2868 if (!protocol_by_name || !name) return NULL;
2870 return fr_hash_table_finddata(protocol_by_name, &(fr_dict_t){ .root = &(fr_dict_attr_t){ .name = name } });
2873 /** Lookup a protocol by its number.
2875 * Returns the #fr_dict_t belonging to the protocol with the specified number
2876 * if any have been registered.
2878 * @param[in] num to search for.
2879 * @return dictionary representing the protocol (if it exists).
2881 fr_dict_t *fr_dict_by_protocol_num(unsigned int num)
2883 if (!protocol_by_num) return NULL;
2885 return fr_hash_table_finddata(protocol_by_num, &(fr_dict_t) { .root = &(fr_dict_attr_t){ .attr = num } });
2888 /** Dictionary/attribute ctx struct
2892 fr_dict_t *found_dict; //!< Dictionary attribute found in.
2893 fr_dict_attr_t const *found_da; //!< Resolved attribute.
2894 fr_dict_attr_t const *find; //!< Attribute to find.
2895 } dict_attr_search_t;
2897 /** Search for an attribute name in all dictionaries
2899 * @param[in] ctx Attribute to search for.
2900 * @param[in] data Dictionary to search in.
2902 * - 0 if attribute not found in dictionary.
2903 * - 1 if attribute found in dictionary.
2905 static int _dict_attr_find_in_dicts(void *ctx, void *data)
2907 dict_attr_search_t *search = ctx;
2910 if (!data) return 0; /* We get called with NULL data */
2912 dict = talloc_get_type_abort(data, fr_dict_t);
2914 search->found_da = fr_hash_table_finddata(dict->attributes_by_name, search->find);
2915 if (!search->found_da) return 0;
2917 search->found_dict = data;
2922 /** Attempt to locate the protocol dictionary containing an attribute
2924 * @note Unlike fr_dict_by_attr_name, doesn't search through all the dictionaries,
2925 * just uses the fr_dict_attr_t hierarchy and the talloc hierarchy to locate
2926 * the dictionary (much much faster and more scalable).
2928 * @param[in] da To get the containing dictionary for.
2930 * - The dictionary containing da.
2933 fr_dict_t *fr_dict_by_da(fr_dict_attr_t const *da)
2935 fr_dict_attr_t const *da_p = da;
2937 while (da_p->parent) {
2938 da_p = da_p->parent;
2942 if (!da_p->flags.is_root) {
2943 fr_strerror_printf("%s: Attribute %s has not been inserted into a dictionary", __FUNCTION__, da->name);
2948 * Parent of the root attribute must
2949 * be the dictionary.
2951 return talloc_get_type_abort(talloc_parent(da_p), fr_dict_t);
2954 /** Attempt to locate the protocol dictionary containing an attribute
2956 * @note This is O(n) and will only return the first instance of the dictionary.
2958 * @param[out] found the attribute that was resolved from the name. May be NULL.
2959 * @param[in] name the name of the attribute.
2961 * - the dictionary the attribute was found in.
2962 * - NULL if an attribute with the specified name wasn't found in any dictionary.
2964 fr_dict_t *fr_dict_by_attr_name(fr_dict_attr_t const **found, char const *name)
2966 fr_dict_attr_t find = {
2969 dict_attr_search_t search = {
2974 if (found) *found = NULL;
2976 if (!name || !*name) return NULL;
2978 ret = fr_hash_table_walk(protocol_by_name, _dict_attr_find_in_dicts, &search);
2979 if (ret == 0) return NULL;
2981 if (found) *found = search.found_da;
2983 return search.found_dict;
2986 /** Look up a vendor by one of its child attributes
2988 * @param[in] da The vendor attribute.
2991 * - NULL if no vendor with that number was regitered for this protocol.
2993 fr_dict_vendor_t const *fr_dict_vendor_by_da(fr_dict_attr_t const *da)
2996 fr_dict_vendor_t dv;
2998 dv.pen = fr_dict_vendor_num_by_da(da);
2999 if (!dv.pen) return NULL;
3001 dict = fr_dict_by_da(da);
3003 return fr_hash_table_finddata(dict->vendors_by_num, &dv);
3006 /** Look up a vendor by its name
3008 * @param[in] dict of protocol context we're operating in.
3009 * If NULL the internal dictionary will be used.
3010 * @param[in] name to search for.
3013 * - NULL if no vendor with that name was regitered for this protocol.
3015 fr_dict_vendor_t const *fr_dict_vendor_by_name(fr_dict_t const *dict, char const *name)
3017 fr_dict_vendor_t *found;
3019 INTERNAL_IF_NULL(dict, NULL);
3021 if (!name) return 0;
3023 found = fr_hash_table_finddata(dict->vendors_by_name, &(fr_dict_vendor_t) { .name = name });
3024 if (!found) return 0;
3029 /** Look up a vendor by its PEN
3031 * @param[in] dict of protocol context we're operating in.
3032 * If NULL the internal dictionary will be used.
3033 * @param[in] vendor_pen to search for.
3036 * - NULL if no vendor with that number was regitered for this protocol.
3038 fr_dict_vendor_t const *fr_dict_vendor_by_num(fr_dict_t const *dict, uint32_t vendor_pen)
3040 INTERNAL_IF_NULL(dict, NULL);
3042 return fr_hash_table_finddata(dict->vendors_by_num, &(fr_dict_vendor_t) { .pen = vendor_pen });
3045 /** Return the vendor that parents this attribute
3047 * @note Uses the dictionary hierachy to determine the parent
3049 * @param[in] da The dictionary attribute to find parent for.
3051 * - NULL if the attribute has no vendor.
3052 * - A fr_dict_attr_t representing this attribute's associated vendor.
3054 fr_dict_attr_t const *fr_dict_vendor_attr_by_da(fr_dict_attr_t const *da)
3056 fr_dict_attr_t const *da_p = da;
3060 while (da_p->parent) {
3061 if (da_p->type == FR_TYPE_VENDOR) break;
3062 da_p = da_p->parent;
3064 if (!da_p) return NULL;
3066 if (da_p->type != FR_TYPE_VENDOR) return NULL;
3071 /** Return vendor attribute for the specified dictionary and pen
3073 * @param[in] vendor_root of the vendor root attribute. Could be 26 (for example) in RADIUS.
3074 * @param[in] vendor_pen to find.
3076 * - NULL if vendor does not exist.
3077 * - A fr_dict_attr_t representing the vendor in the dictionary hierarchy.
3079 fr_dict_attr_t const *fr_dict_vendor_attr_by_num(fr_dict_attr_t const *vendor_root, uint32_t vendor_pen)
3081 fr_dict_attr_t const *vendor;
3083 switch (vendor_root->type) {
3084 case FR_TYPE_VSA: /* Vendor specific attribute */
3085 case FR_TYPE_EVS: /* Extended vendor specific attribute */
3089 fr_strerror_printf("Wrong type for vendor root, expected '%s' or '%s' got '%s'",
3090 fr_int2str(fr_value_box_type_table, FR_TYPE_VSA, "<INVALID>"),
3091 fr_int2str(fr_value_box_type_table, FR_TYPE_EVS, "<INVALID>"),
3092 fr_int2str(fr_value_box_type_table, vendor_root->type, "<INVALID>"));
3096 vendor = fr_dict_attr_child_by_num(vendor_root, vendor_pen);
3098 fr_strerror_printf("Vendor %i not defined", vendor_pen);
3102 if (vendor->type != FR_TYPE_VENDOR) {
3103 fr_strerror_printf("Wrong type for vendor, expected '%s' got '%s'",
3104 fr_int2str(fr_value_box_type_table, vendor->type, "<INVALID>"),
3105 fr_int2str(fr_value_box_type_table, FR_TYPE_VENDOR, "<INVALID>"));
3112 /** Look up a dictionary attribute by a name embedded in another string
3114 * Find the first invalid attribute name char in the string pointed
3117 * Copy the characters between the start of the name string and the first
3118 * none #fr_dict_attr_allowed_chars char to a buffer and perform a dictionary lookup
3121 * If the attribute exists, advance the pointer pointed to by name
3122 * to the first none #fr_dict_attr_allowed_chars char, and return the DA.
3124 * If the attribute does not exist, don't advance the pointer and return
3127 * @param[out] err Why parsing failed. May be NULL.
3128 * @see fr_dict_attr_err_t
3129 * @param[out] out Where to store the resolve attribute.
3130 * @param[in] dict of protocol context we're operating in.
3131 * If NULL the internal dictionary will be used.
3132 * @param[in] name string start.
3134 * - <= 0 on failure.
3135 * - The number of bytes of name consumed on success.
3137 ssize_t fr_dict_attr_by_name_substr(fr_dict_attr_err_t *err, fr_dict_attr_t const **out,
3138 fr_dict_t const *dict, char const *name)
3140 fr_dict_attr_t const *da;
3143 char buffer[FR_DICT_ATTR_MAX_NAME_LEN + 1];
3147 INTERNAL_IF_NULL(dict, 0);
3150 fr_strerror_printf("Zero length attribute name");
3151 if (err) *err = FR_DICT_ATTR_PARSE_ERROR;
3155 if (err) *err = FR_DICT_ATTR_OK;
3158 * Advance p until we get something that's not part of
3159 * the dictionary attribute name.
3161 for (p = name; fr_dict_attr_allowed_chars[(uint8_t)*p]; p++);
3164 if (len > FR_DICT_ATTR_MAX_NAME_LEN) {
3165 fr_strerror_printf("Attribute name too long");
3166 if (err) *err = FR_DICT_ATTR_PARSE_ERROR;
3167 return -(FR_DICT_ATTR_MAX_NAME_LEN);
3170 memcpy(buffer, name, len);
3173 da = fr_hash_table_finddata(dict->attributes_by_name, &(fr_dict_attr_t){ .name = buffer });
3175 if (err) *err = FR_DICT_ATTR_NOTFOUND;
3176 fr_strerror_printf("Unknown attribute '%.*s'", (int) len, name);
3185 /** Locate a #fr_dict_attr_t by its name
3187 * @note Unlike attribute numbers, attribute names are unique to the dictionary.
3189 * @param[in] dict of protocol context we're operating in.
3190 * If NULL the internal dictionary will be used.
3191 * @param[in] name of the attribute to locate.
3193 * - Attribute matching name.
3194 * - NULL if no matching attribute could be found.
3196 fr_dict_attr_t const *fr_dict_attr_by_name(fr_dict_t const *dict, char const *name)
3198 INTERNAL_IF_NULL(dict, NULL);
3200 if (!name) return NULL;
3202 return fr_hash_table_finddata(dict->attributes_by_name, &(fr_dict_attr_t) { .name = name });
3205 /** Locate a qualified #fr_dict_attr_t by its name and a dictionary qualifier
3207 * @note If calling this function from the server any list or request qualifiers
3208 * should be stripped first.
3210 * @param[out] err Why parsing failed. May be NULL.
3211 * @see fr_dict_attr_err_t
3212 * @param[out] out Dictionary found attribute.
3213 * @param[in] dict_def Default dictionary for non-qualified dictionaries.
3214 * @param[in] name Dictionary/Attribute name.
3215 * @param[in] fallback If true, fallback to the internal dictionary.
3217 * - <= 0 on failure.
3218 * - The number of bytes of name consumed on success.
3220 ssize_t fr_dict_attr_by_qualified_name_substr(fr_dict_attr_err_t *err, fr_dict_attr_t const **out,
3221 fr_dict_t const *dict_def, char const *name, bool fallback)
3223 fr_dict_t const *dict = NULL;
3224 fr_dict_t const *dict_iter = NULL;
3225 char const *p = name;
3227 fr_dict_attr_err_t aerr = FR_DICT_ATTR_OK;
3228 bool internal = false;
3229 fr_hash_iter_t iter;
3233 INTERNAL_IF_NULL(dict_def, -1);
3235 if (err) *err = FR_DICT_ATTR_OK;
3238 * Figure out if we should use the default dictionary
3239 * or if the string was qualified.
3241 slen = fr_dict_by_protocol_substr(&dict, p, dict_def);
3243 if (err) *err = FR_DICT_ATTR_PROTOCOL_NOTFOUND;
3247 * Nothing was parsed, use the default dictionary
3249 } else if (slen == 0) {
3253 * Has dictionary qualifier, can't fallback
3255 } else if (slen > 0) {
3259 * Next thing SHOULD be a '.'
3262 if (err) *err = FR_DICT_ATTR_PARSE_ERROR;
3270 slen = fr_dict_attr_by_name_substr(&aerr, out, dict, p);
3273 case FR_DICT_ATTR_OK:
3276 case FR_DICT_ATTR_NOTFOUND:
3278 * Loop over all the dictionaries
3282 * Haven't started yet, do so.
3286 * Check the internal dictionary
3287 * first, unless it's alreaday
3292 if (dict_def != fr_dict_internal) {
3293 dict = fr_dict_internal;
3299 * Start the iteration over all dictionaries.
3301 dict_iter = fr_hash_table_iter_init(protocol_by_num, &iter);
3304 dict_iter = fr_hash_table_iter_next(protocol_by_num, &iter);
3307 if (!dict_iter) goto fail;
3308 if (dict_iter == dict_def) goto redo;
3315 if (err) *err = aerr;
3316 return -((p - name) + slen);
3319 * Other error codes are the same
3322 if (err) *err = aerr;
3323 return -((p - name) + slen);
3329 * If we're returning a success code indication,
3330 * ensure we populated out
3332 if (!fr_cond_assert(*out)) {
3333 if (err) *err = FR_DICT_ATTR_EINVAL;
3340 /** Locate a qualified #fr_dict_attr_t by its name and a dictionary qualifier
3342 * @param[out] out Dictionary found attribute.
3343 * @param[in] dict_def Default dictionary for non-qualified dictionaries.
3344 * @param[in] attr Dictionary/Attribute name.
3345 * @param[in] fallback If true, fallback to the internal dictionary.
3346 * @return an #fr_dict_attr_err_t value.
3348 fr_dict_attr_err_t fr_dict_attr_by_qualified_name(fr_dict_attr_t const **out, fr_dict_t const *dict_def,
3349 char const *attr, bool fallback)
3352 fr_dict_attr_err_t err = FR_DICT_ATTR_OK;
3354 slen = fr_dict_attr_by_qualified_name_substr(&err, out, dict_def, attr, fallback);
3355 if (slen <= 0) return err;
3357 if ((size_t)slen != strlen(attr)) {
3358 fr_strerror_printf("Trailing garbage after attr string \"%s\"", attr);
3359 return FR_DICT_ATTR_PARSE_ERROR;
3362 return FR_DICT_ATTR_OK;
3365 /** Lookup a attribute by its its vendor and attribute numbers and data type
3367 * @note Only works with FR_TYPE_COMBO_IP
3369 * @param[in] da to look for type variant of.
3370 * @param[in] type Variant of attribute to lookup.
3372 * - Attribute matching parent/attr/type.
3373 * - NULL if no matching attribute could be found.
3375 fr_dict_attr_t const *fr_dict_attr_by_type(fr_dict_attr_t const *da, fr_type_t type)
3377 return fr_hash_table_finddata(fr_dict_by_da(da)->attributes_combo,
3379 .parent = da->parent,
3385 /** Check if a child attribute exists in a parent using a pointer (da)
3387 * @param[in] parent to check for child in.
3388 * @param[in] child to look for.
3390 * - The child attribute on success.
3391 * - NULL if the child attribute does not exist.
3393 fr_dict_attr_t const *fr_dict_attr_child_by_da(fr_dict_attr_t const *parent, fr_dict_attr_t const *child)
3395 fr_dict_attr_t const *bin;
3399 if (!parent->children) return NULL;
3402 * Only some types can have children
3404 switch (parent->type) {
3408 case FR_TYPE_STRUCTURAL:
3413 * Child arrays may be trimmed back to save memory.
3414 * Check that so we don't SEGV.
3416 if ((child->attr & 0xff) > talloc_array_length(parent->children)) return NULL;
3418 bin = parent->children[child->attr & 0xff];
3420 if (!bin) return NULL;
3421 if (bin == child) return bin;
3428 /** Check if a child attribute exists in a parent using an attribute number
3430 * @param[in] parent to check for child in.
3431 * @param[in] attr number to look for.
3433 * - The child attribute on success.
3434 * - NULL if the child attribute does not exist.
3436 inline fr_dict_attr_t const *fr_dict_attr_child_by_num(fr_dict_attr_t const *parent, unsigned int attr)
3438 fr_dict_attr_t const *bin;
3442 if (!parent->children) return NULL;
3445 * Only some types can have children
3447 switch (parent->type) {
3451 case FR_TYPE_STRUCTURAL:
3456 * Child arrays may be trimmed back to save memory.
3457 * Check that so we don't SEGV.
3459 if ((attr & 0xff) > talloc_array_length(parent->children)) return NULL;
3461 bin = parent->children[attr & 0xff];
3463 if (!bin) return NULL;
3464 if (bin->attr == attr) return bin;
3471 /** Lookup the structure representing an enum value in a #fr_dict_attr_t
3473 * @param[in] da to search in.
3474 * @param[in] value to search for.
3476 * - Matching #fr_dict_enum_t.
3477 * - NULL if no matching #fr_dict_enum_t could be found.
3479 fr_dict_enum_t *fr_dict_enum_by_value(fr_dict_attr_t const *da, fr_value_box_t const *value)
3481 fr_dict_enum_t enumv, *dv;
3484 if (!da) return NULL;
3486 dict = fr_dict_by_da(da);
3488 fr_strerror_printf("Attributes \"%s\" not present in any dictionaries", da->name);
3493 * Could be NULL or an unknown attribute, in which case
3494 * we want to avoid the lookup gracefully...
3496 if (value->type != da->type) return NULL;
3499 * First, look up aliases.
3503 enumv.alias_len = 0;
3506 * Look up the attribute alias target, and use
3507 * the correct attribute number if found.
3509 dv = fr_hash_table_finddata(dict->values_by_alias, &enumv);
3510 if (dv) enumv.da = dv->da;
3512 enumv.value = value;
3514 return fr_hash_table_finddata(dict->values_by_da, &enumv);
3517 /** Lookup the name of an enum value in a #fr_dict_attr_t
3519 * @param[in] da to search in.
3520 * @param[in] value number to search for.
3523 * - NULL if no matching value could be found.
3525 char const *fr_dict_enum_alias_by_value(fr_dict_attr_t const *da, fr_value_box_t const *value)
3530 if (!da) return NULL;
3532 dict = fr_dict_by_da(da);
3534 fr_strerror_printf("Attributes \"%s\" not present in any dictionaries", da->name);
3538 dv = fr_dict_enum_by_value(da, value);
3545 * Get a value by its name, keyed off of an attribute.
3547 fr_dict_enum_t *fr_dict_enum_by_alias(fr_dict_attr_t const *da, char const *alias, ssize_t len)
3549 fr_dict_enum_t *found;
3550 fr_dict_enum_t find = {
3556 if (!alias) return NULL;
3558 dict = fr_dict_by_da(da);
3560 fr_strerror_printf("Attributes \"%s\" not present in any dictionaries", da->name);
3564 if (len < 0) len = strlen(alias);
3565 find.alias_len = (size_t)len;
3568 * Look up the attribute alias target, and use
3569 * the correct attribute number if found.
3571 found = fr_hash_table_finddata(dict->values_by_alias, &find);
3572 if (found) find.da = found->da;
3574 return fr_hash_table_finddata(dict->values_by_alias, &find);
3578 * String split routine. Splits an input string IN PLACE
3579 * into pieces, based on spaces.
3581 int fr_dict_str_to_argv(char *str, char **argv, int max_argc)
3586 if (argc >= max_argc) break;
3589 * Chop out comments early.
3596 while ((*str == ' ') ||
3618 static int dict_read_sscanf_i(unsigned int *pvalue, char const *str)
3622 static char const *tab = "0123456789";
3624 if ((str[0] == '0') &&
3625 ((str[1] == 'x') || (str[1] == 'X'))) {
3626 tab = "0123456789abcdef";
3635 if (*str == '.') break;
3637 c = memchr(tab, tolower((int)*str), base);
3649 /** Parser context for dict_from_file
3651 * Allows vendor and TLV context to persist across $INCLUDEs
3654 fr_dict_t *dict; //!< Protocol dictionary we're inserting attributes into.
3655 fr_dict_t *old_dict; //!< The dictionary before the current BEGIN-PROTOCOL block.
3657 fr_dict_vendor_t const *block_vendor; //!< Vendor block we're inserting attributes into.
3658 //!< Can be removed once we remove the vendor field from
3659 //!< #fr_dict_attr_t.
3661 fr_dict_attr_t const *block_tlv[FR_DICT_TLV_NEST_MAX]; //!< Nested TLV block's we're
3662 //!< inserting attributes into.
3663 int block_tlv_depth; //!< Nested TLV block index we're inserting into.
3665 fr_dict_attr_t const *parent; //!< Current parent attribute (root/vendor/tlv).
3667 fr_dict_attr_t const *last_attr; //!< Cache of last attribute to speed up
3668 ///< value processing.
3670 TALLOC_CTX *fixup_pool; //!< Temporary pool for fixups, reduces holes
3671 dict_enum_fixup_t *enum_fixup;
3672 } dict_from_file_ctx_t;
3674 /** Set a new root dictionary attribute
3676 * @note Must only be called once per dictionary.
3678 * @param[in] dict to modify.
3679 * @param[in] name of dictionary root.
3680 * @param[in] proto_number The artificial (or IANA allocated) number for the protocol.
3681 * This is only used for
3686 static int dict_root_set(fr_dict_t *dict, char const *name, unsigned int proto_number)
3688 fr_dict_attr_flags_t flags = {
3694 if (!fr_cond_assert(!dict->root)) {
3695 fr_strerror_printf("Dictionary root already set");
3699 dict->root = dict_attr_alloc_name(dict, name);
3700 if (!dict->root) return -1;
3702 dict_attr_init(dict->root, NULL, proto_number, FR_TYPE_TLV, &flags);
3703 DA_VERIFY(dict->root);
3708 static int _dict_free(fr_dict_t *dict)
3710 if (dict == fr_dict_internal) fr_dict_internal = NULL;
3712 if (!fr_cond_assert(!dict->in_protocol_by_name || fr_hash_table_delete(protocol_by_name, dict))) {
3713 fr_strerror_printf("Failed removing dictionary from protocol hash \"%s\"", dict->root->name);
3716 if (!fr_cond_assert(!dict->in_protocol_by_num || fr_hash_table_delete(protocol_by_num, dict))) {
3717 fr_strerror_printf("Failed removing dictionary from protocol number_hash \"%s\"", dict->root->name);
3724 /** Allocate a new dictionary
3726 * @param[in] ctx to allocate dictionary in.
3728 * - NULL on memory allocation error.
3730 static fr_dict_t *dict_alloc(TALLOC_CTX *ctx)
3734 dict = talloc_zero(ctx, fr_dict_t);
3737 fr_strerror_printf("Failed allocating memory for dictionary");
3742 talloc_set_destructor(dict, _dict_free);
3745 * Pre-Allocate 6MB of pool memory for rapid startup
3746 * As that's the working memory required during
3747 * dictionary initialisation.
3749 dict->pool = talloc_pool(dict, DICT_POOL_SIZE);
3750 if (!dict->pool) goto error;
3753 * Create the table of vendor by name. There MAY NOT
3754 * be multiple vendors of the same name.
3756 dict->vendors_by_name = fr_hash_table_create(dict, dict_vendor_name_hash, dict_vendor_name_cmp, hash_pool_free);
3757 if (!dict->vendors_by_name) goto error;
3760 * Create the table of vendors by value. There MAY
3761 * be vendors of the same value. If there are, we
3762 * pick the latest one.
3764 dict->vendors_by_num = fr_hash_table_create(dict, dict_vendor_pen_hash, dict_vendor_pen_cmp, NULL);
3765 if (!dict->vendors_by_num) goto error;
3768 * Create the table of attributes by name. There MAY NOT
3769 * be multiple attributes of the same name.
3771 dict->attributes_by_name = fr_hash_table_create(dict, dict_attr_name_hash, dict_attr_name_cmp, NULL);
3772 if (!dict->attributes_by_name) goto error;
3775 * Horrible hacks for combo-IP.
3777 dict->attributes_combo = fr_hash_table_create(dict, dict_attr_combo_hash, dict_attr_combo_cmp, hash_pool_free);
3778 if (!dict->attributes_combo) goto error;
3780 dict->values_by_alias = fr_hash_table_create(dict, dict_enum_alias_hash, dict_enum_alias_cmp, hash_pool_free);
3781 if (!dict->values_by_alias) goto error;
3783 dict->values_by_da = fr_hash_table_create(dict, dict_enum_value_hash, dict_enum_value_cmp, hash_pool_free);
3784 if (!dict->values_by_da) goto error;
3789 /** Lookup a dictionary reference
3791 * Format is @verbatim[<proto>].[<attr>]@endverbatim
3793 * If protocol is omitted lookup is in the current dictionary.
3795 * FIXME: Probably needs the dictionary equivalent of pass2, to fixup circular dependencies
3796 * DHCPv4->RADIUS and RADIUS->DHCPv4 are both valid.
3798 * @param[in] dict The current dictionary we're parsing.
3799 * @param[in,out] ref The reference string. Pointer advanced to the end of the string.
3801 * - NULL if the reference is invalid.
3802 * - A local or foreign attribute representing the target of the reference.
3804 static fr_dict_attr_t const *dict_resolve_reference(fr_dict_t *dict, char const *ref)
3806 char const *p = ref, *q, *end = p + strlen(ref);
3807 fr_dict_t *proto_dict;
3808 fr_dict_attr_t const *da;
3812 * If the reference does not begin with .
3813 * then it's a reference into a foreign
3817 char buffer[FR_DICT_PROTO_MAX_NAME_LEN + 1];
3822 if ((size_t)(q - p) > sizeof(buffer)) {
3823 fr_strerror_printf("Protocol name too long");
3827 strlcpy(buffer, p, (q - p + 1));
3829 dict = fr_dict_by_protocol_name(buffer);
3831 fr_strerror_printf("Referenced protocol \"%s\" not found", buffer);
3837 * If the reference string begins with .
3838 * then the reference is in the current
3846 * If there's a '.' after the dictionary, then
3847 * the reference is to a specific attribute.
3852 slen = fr_dict_attr_by_name_substr(NULL, &da, proto_dict, p);
3854 fr_strerror_printf("Referenced attribute \"%s\" not found", p);
3859 da = fr_dict_root(proto_dict);
3861 fr_strerror_printf("Dictionary missing attribute root");
3869 * Process the ATTRIBUTE command
3871 static int dict_read_process_attribute(dict_from_file_ctx_t *ctx, char **argv, int argc,
3872 fr_dict_attr_flags_t *base_flags, fr_dict_attr_t const **previous)
3875 bool set_previous = true;
3877 fr_dict_vendor_t const *vendor;
3881 unsigned int length;
3882 fr_dict_attr_flags_t flags;
3883 fr_dict_attr_t const *ref = NULL;
3884 fr_dict_attr_t const *parent = ctx->parent;
3887 if ((argc < 3) || (argc > 4)) {
3888 fr_strerror_printf("Invalid ATTRIBUTE syntax");
3893 * Dictionaries need to have real names, not shitty ones.
3895 if (strncmp(argv[0], "Attr-", 5) == 0) {
3896 fr_strerror_printf("Invalid ATTRIBUTE name");
3900 memcpy(&flags, base_flags, sizeof(flags));
3903 * Look for OIDs before doing anything else.
3905 if (!strchr(argv[1], '.')) {
3907 * Parse out the attribute number
3909 if (!dict_read_sscanf_i(&attr, argv[1])) {
3910 fr_strerror_printf("Invalid ATTRIBUTE number");
3915 * Got a '.', which means "continue from the
3916 * previously defined attribute, which then must exist.
3918 } else if (argv[1][0] == '.') {
3919 if (!previous || !*previous) {
3920 fr_strerror_printf("Unknown parent for partial OID");
3925 set_previous = false;
3930 * Got an OID string. Every attribute should exist other
3931 * than the leaf, which is the attribute we're defining.
3939 slen = fr_dict_attr_by_oid(ctx->dict, &parent, &attr, argv[1]);
3940 if (slen <= 0) return -1;
3942 if (!fr_cond_assert(parent)) return -1; /* Should have provided us with a parent */
3946 * Some types can have fixed length
3948 p = strchr(argv[2], '[');
3952 * find the type of the attribute.
3954 type = fr_str2int(fr_value_box_type_table, argv[2], -1);
3956 fr_strerror_printf("Unknown data type '%s'", argv[2]);
3963 if (type != FR_TYPE_OCTETS) {
3964 fr_strerror_printf("Only 'octets' types can have a 'length' parameter");
3968 q = strchr(p + 1, ']');
3970 fr_strerror_printf("Invalid format for '%s[...]'", argv[2]);
3976 if (!dict_read_sscanf_i(&length, p + 1)) {
3977 fr_strerror_printf("Invalid length for '%s[...]'", argv[2]);
3981 if ((length == 0) || (length > 253)) {
3982 fr_strerror_printf("Invalid length for '%s[...]'", argv[2]);
3986 flags.length = length;
3997 char key[64], value[256];
4000 if (!q) q = p + strlen(p);
4003 * Nothing after the trailing comma
4007 if ((size_t)(q - p) > sizeof(key)) {
4008 fr_strerror_printf("ATTRIBUTE option key too long");
4013 * Copy key and value
4015 if (!(v = memchr(p, '=', q - p)) || (v == q)) {
4017 strlcpy(key, p, (q - p) + 1);
4019 strlcpy(key, p, (v - p) + 1);
4020 strlcpy(value, v + 1, q - v);
4024 * Boolean flag, means this is a tagged
4027 if (strcmp(key, "has_tag") == 0) {
4031 * Encryption method.
4033 } else if (strcmp(key, "encrypt") == 0) {
4036 flags.encrypt = strtol(value, &qq, 0);
4038 fr_strerror_printf("Invalid encrypt value \"%s\"", value);
4043 * Marks the attribute up as internal.
4044 * This means it can use numbers outside of the allowed
4045 * protocol range, and also means it will not be included
4046 * in replies or proxy requests.
4048 } else if (strcmp(key, "internal") == 0) {
4051 } else if (strcmp(key, "array") == 0) {
4054 } else if (strcmp(key, "concat") == 0) {
4057 } else if (strcmp(key, "virtual") == 0) {
4060 } else if (strcmp(key, "reference") == 0) {
4061 ref = dict_resolve_reference(ctx->dict, value);
4062 if (!ref) return -1;
4063 flags.is_reference = 1;
4066 * The only thing is the vendor name, and it's a known name:
4069 * This format is terrible, and is only
4070 * allowed for backwards compatability.
4072 } else if ((argv[3] == p) && (*q == '\0')) {
4074 fr_strerror_printf("ATTRIBUTE cannot use a 'vendor' flag");
4078 if (ctx->block_vendor) {
4079 fr_strerror_printf("Vendor flag inside of 'BEGIN-VENDOR' is not allowed");
4083 vendor = fr_dict_vendor_by_name(ctx->dict, key);
4084 if (!vendor) goto unknown;
4089 fr_strerror_printf("Unknown option '%s'", key);
4096 #ifdef WITH_DICTIONARY_WARNINGS
4098 * Hack to help us discover which vendors have illegal
4101 if (!vendor && (attr < 256) &&
4102 !strstr(fn, "rfc") && !strstr(fn, "illegal")) {
4103 fprintf(stderr, "WARNING: Illegal Attribute %s in %s\n",
4109 * Add in a normal attribute
4112 if (fr_dict_attr_add(ctx->dict, parent, argv[0], attr, type, &flags) < 0) return -1;
4114 * Add in a special reference attribute
4117 if (dict_attr_ref_add(ctx->dict, parent, argv[0], attr, type, &flags, ref) < 0) return -1;
4121 * If we need to set the previous attribute, we have to
4122 * look it up by number.
4124 if (set_previous && previous) *previous = fr_dict_attr_child_by_num(parent, attr);
4130 * Process the ATTRIBUTE command, where it only has a name.
4132 static int dict_read_process_named_attribute(dict_from_file_ctx_t *ctx,
4133 char **argv, int argc,
4134 fr_dict_attr_flags_t const *base_flags)
4139 char *p, normalized[512];
4142 fr_strerror_printf("Invalid ATTRIBUTE syntax");
4147 * find the type of the attribute.
4149 type = fr_str2int(fr_value_box_type_table, argv[1], -1);
4151 fr_strerror_printf("Unknown data type '%s'", argv[1]);
4155 strlcpy(normalized, argv[0], sizeof(normalized));
4156 for (p = normalized; *p != '\0'; p++) {
4157 if (isupper((int) *p)) {
4158 *p = tolower((int) *p);
4162 hash = fr_hash_string(normalized);
4168 if (fr_dict_attr_add(ctx->dict, ctx->parent, argv[0], attr, type, base_flags) < 0) return -1;
4173 /** Process a value alias
4176 static int dict_read_process_value(dict_from_file_ctx_t *ctx, char **argv, int argc)
4178 fr_dict_attr_t const *da;
4179 fr_value_box_t value;
4182 fr_strerror_printf("Invalid VALUE syntax");
4187 * Most VALUEs are bunched together by ATTRIBUTE. We can
4188 * save a lot of lookups on dictionary initialization by
4189 * caching the last attribute.
4191 if (ctx->last_attr && (strcasecmp(argv[0], ctx->last_attr->name) == 0)) {
4192 da = ctx->last_attr;
4194 da = fr_dict_attr_by_name(ctx->dict, argv[0]);
4195 ctx->last_attr = da;
4199 * Remember which attribute is associated with this
4200 * value. This allows us to define enum
4201 * values before the attribute exists, and fix them
4205 dict_enum_fixup_t *fixup;
4207 if (!fr_cond_assert_msg(ctx->fixup_pool, "fixup pool context invalid")) return -1;
4209 fixup = talloc_zero(ctx->fixup_pool, dict_enum_fixup_t);
4213 fr_strerror_printf("Out of memory");
4216 fixup->attribute = talloc_strdup(fixup, argv[0]);
4217 if (!fixup->attribute) goto oom;
4218 fixup->alias = talloc_strdup(fixup, argv[1]);
4219 if (!fixup->alias) goto oom;
4220 fixup->value = talloc_strdup(fixup, argv[2]);
4221 if (!fixup->value) goto oom;
4224 * Insert to the head of the list.
4226 fixup->next = ctx->enum_fixup;
4227 ctx->enum_fixup = fixup;
4233 * Only a few data types can have VALUEs defined.
4236 case FR_TYPE_ABINARY:
4238 case FR_TYPE_STRUCTURAL:
4239 case FR_TYPE_INVALID:
4241 fr_strerror_printf_push("Cannot define VALUE for ATTRIBUTE \"%s\" of data type \"%s\"", da->name,
4242 fr_int2str(fr_value_box_type_table, da->type, "<INVALID>"));
4250 fr_type_t type = da->type; /* Might change - Stupid combo IP */
4252 if (fr_value_box_from_str(NULL, &value, &type, NULL, argv[2], -1, '\0', false) < 0) {
4253 fr_strerror_printf_push("Invalid VALUE for ATTRIBUTE \"%s\"", da->name);
4258 if (fr_dict_enum_add_alias(da, argv[1], &value, false, true) < 0) {
4259 fr_value_box_clear(&value);
4262 fr_value_box_clear(&value);
4268 * Process the FLAGS command
4270 static int dict_read_process_flags(UNUSED fr_dict_t *dict, char **argv, int argc,
4271 fr_dict_attr_flags_t *base_flags)
4284 if (strcmp(p, "internal") == 0) {
4285 base_flags->internal = sense;
4290 fr_strerror_printf("Invalid FLAGS syntax");
4294 static int dict_read_parse_format(char const *format, unsigned int *pvalue, int *ptype, int *plength,
4295 bool *pcontinuation)
4299 bool continuation = false;
4301 if (strncasecmp(format, "format=", 7) != 0) {
4302 fr_strerror_printf("Invalid format for VENDOR. Expected 'format=', got '%s'",
4308 if ((strlen(p) < 3) ||
4309 !isdigit((int)p[0]) ||
4311 !isdigit((int)p[2]) ||
4312 (p[3] && (p[3] != ','))) {
4313 fr_strerror_printf("Invalid format for VENDOR. Expected text like '1,1', got '%s'",
4318 type = (int)(p[0] - '0');
4319 length = (int)(p[2] - '0');
4321 if ((type != 1) && (type != 2) && (type != 4)) {
4322 fr_strerror_printf("Invalid type value %d for VENDOR", type);
4326 if ((length != 0) && (length != 1) && (length != 2)) {
4327 fr_strerror_printf("Ivalid length value %d for VENDOR", length);
4333 fr_strerror_printf("Invalid format for VENDOR. Expected text like '1,1', got '%s'",
4338 if ((p[4] != 'c') ||
4340 fr_strerror_printf("Invalid format for VENDOR. Expected text like '1,1', got '%s'",
4344 continuation = true;
4346 if ((*pvalue != VENDORPEC_WIMAX) ||
4347 (type != 1) || (length != 1)) {
4348 fr_strerror_printf("Only WiMAX VSAs can have continuations");
4355 *pcontinuation = continuation;
4359 /** Register the specified dictionary as a protocol dictionary
4361 * Allows vendor and TLV context to persist across $INCLUDEs
4363 static int dict_read_process_protocol(char **argv, int argc)
4366 unsigned int type_size = 1;
4369 if ((argc < 2) || (argc > 3)) {
4370 fr_strerror_printf("Missing arguments after PROTOCOL. Expected PROTOCOL <num> <name>");
4375 * Validate all entries
4377 if (!dict_read_sscanf_i(&value, argv[1])) {
4378 fr_strerror_printf("Invalid number '%s' following PROTOCOL", argv[1]);
4383 fr_strerror_printf("Invalid value '%u' following PROTOCOL", value);
4388 * Look for a format statement. This may specify the
4389 * type length of the protocol's types.
4395 if (strncasecmp(argv[2], "format=", 7) != 0) {
4396 fr_strerror_printf("Invalid format for PROTOCOL. Expected 'format=', got '%s'", argv[2]);
4401 type_size = strtoul(p, &q, 10);
4402 if (q != (p + strlen(p))) {
4403 fr_strerror_printf("Found trailing garbage '%s' after format specifier", p);
4409 * Cross check name / number.
4411 dict = fr_dict_by_protocol_name(argv[0]);
4413 if (dict->root->attr != value) {
4414 fr_strerror_printf("Conflicting numbers %u vs %u for PROTOCOL \"%s\"",
4415 dict->root->attr, value, dict->root->name);
4420 dict = fr_dict_by_protocol_num(value);
4422 if (dict && (strcasecmp(dict->root->name, argv[0]) != 0)) {
4423 fr_strerror_printf("Conflicting names \"%s\" vs \"%s\" for PROTOCOL %u",
4424 dict->root->name, argv[0], dict->root->attr);
4430 * And check types no matter what.
4433 if (dict->root->flags.type_size != type_size) {
4434 fr_strerror_printf("Conflicting flags for PROTOCOL \"%s\"", dict->root->name);
4440 dict = dict_alloc(NULL);
4443 * Set the root attribute with the protocol name
4445 dict_root_set(dict, argv[0], value);
4447 if (dict_protocol_add(dict) < 0) return -1;
4453 * Process the VENDOR command
4455 static int dict_read_process_vendor(fr_dict_t *dict, char **argv, int argc)
4459 bool continuation = false;
4460 fr_dict_vendor_t const *dv;
4461 fr_dict_vendor_t *mutable;
4463 if ((argc < 2) || (argc > 3)) {
4464 fr_strerror_printf("Invalid VENDOR syntax");
4469 * Validate all entries
4471 if (!dict_read_sscanf_i(&value, argv[1])) {
4472 fr_strerror_printf("Invalid number in VENDOR");
4477 * Look for a format statement. Allow it to over-ride the hard-coded formats below.
4480 if (dict_read_parse_format(argv[2], &value, &type, &length, &continuation) < 0) return -1;
4486 /* Create a new VENDOR entry for the list */
4487 if (dict_vendor_add(dict, argv[0], value) < 0) return -1;
4489 dv = fr_dict_vendor_by_num(dict, value);
4491 fr_strerror_printf("Failed adding format for VENDOR");
4495 memcpy(&mutable, &dv, sizeof(mutable));
4497 mutable->type = type;
4498 mutable->length = length;
4499 mutable->flags = continuation;
4504 static int fr_dict_finalise(dict_from_file_ctx_t *ctx)
4507 * Resolve any VALUE aliases (enums) that were defined
4508 * before the attributes they reference.
4510 if (ctx->enum_fixup) {
4511 fr_dict_attr_t const *da;
4512 dict_enum_fixup_t *this, *next;
4514 for (this = ctx->enum_fixup; this != NULL; this = next) {
4515 fr_value_box_t value;
4520 da = fr_dict_attr_by_name(ctx->dict, this->attribute);
4522 fr_strerror_printf("No ATTRIBUTE '%s' defined for VALUE '%s'",
4523 this->attribute, this->alias);
4529 if (fr_value_box_from_str(this, &value, &type, NULL,
4530 this->value, talloc_array_length(this->value) - 1, '\0', false) < 0) {
4531 fr_strerror_printf_push("Invalid VALUE for ATTRIBUTE \"%s\"", da->name);
4535 ret = fr_dict_enum_add_alias(da, this->alias, &value, false, false);
4536 fr_value_box_clear(&value);
4538 if (ret < 0) goto error;
4541 * Just so we don't lose track of things.
4543 ctx->enum_fixup = next;
4546 TALLOC_FREE(ctx->fixup_pool);
4549 * Walk over all of the hash tables to ensure they're
4550 * initialized. We do this because the threads may perform
4551 * lookups, and we don't want multi-threaded re-ordering
4552 * of the table entries. That would be bad.
4554 fr_hash_table_walk(ctx->dict->vendors_by_name, hash_null_callback, NULL);
4555 fr_hash_table_walk(ctx->dict->vendors_by_num, hash_null_callback, NULL);
4557 fr_hash_table_walk(ctx->dict->values_by_da, hash_null_callback, NULL);
4558 fr_hash_table_walk(ctx->dict->values_by_alias, hash_null_callback, NULL);
4560 ctx->last_attr = NULL;
4565 /** Parse a dictionary file
4567 * @param[in] ctx Contains the current state of the dictionary parser.
4568 * Used to track what PROTOCOL, VENDOR or TLV block
4569 * we're in. Block context changes in $INCLUDEs should
4570 * not affect the context of the including file.
4571 * @param[in] dir_name Directory containing the dictionary we're loading.
4572 * @param[in] filename we're parsing.
4573 * @param[in] src_file The including file.
4574 * @param[in] src_line Line on which the $INCLUDE or $INCLUDE- statement was found.
4579 static int _dict_from_file(dict_from_file_ctx_t *ctx,
4580 char const *dir_name, char const *filename,
4581 char const *src_file, int src_line)
4584 char dir[256], fn[256];
4589 struct stat statbuf;
4590 char *argv[MAX_ARGV];
4592 fr_dict_attr_t const *da, *previous = NULL;
4595 * Base flags are only set for the current file
4597 fr_dict_attr_flags_t base_flags;
4599 if (!fr_cond_assert(!ctx->dict->root || ctx->parent)) return -1;
4601 if ((strlen(dir_name) + 3 + strlen(filename)) > sizeof(dir)) {
4602 fr_strerror_printf_push("%s: Filename name too long", "Error reading dictionary");
4607 * If it's an absolute dir, forget the parent dir,
4608 * and remember the new one.
4610 * If it's a relative dir, tack on the current filename
4611 * to the parent dir. And use that.
4613 if (!FR_DIR_IS_RELATIVE(filename)) {
4614 strlcpy(dir, filename, sizeof(dir));
4615 p = strrchr(dir, FR_DIR_SEP);
4619 strlcat(dir, "/", sizeof(dir));
4622 strlcpy(fn, filename, sizeof(fn));
4624 strlcpy(dir, dir_name, sizeof(dir));
4625 p = strrchr(dir, FR_DIR_SEP);
4627 if (p[1]) strlcat(dir, "/", sizeof(dir));
4629 strlcat(dir, "/", sizeof(dir));
4631 strlcat(dir, filename, sizeof(dir));
4632 p = strrchr(dir, FR_DIR_SEP);
4636 strlcat(dir, "/", sizeof(dir));
4639 p = strrchr(filename, FR_DIR_SEP);
4641 snprintf(fn, sizeof(fn), "%s%s", dir, p);
4643 snprintf(fn, sizeof(fn), "%s%s", dir, filename);
4647 if ((fp = fopen(fn, "r")) == NULL) {
4649 fr_strerror_printf_push("Couldn't open dictionary %s: %s", fr_syserror(errno), fn);
4651 fr_strerror_printf_push("Error reading dictionary: %s[%d]: Couldn't open dictionary '%s': %s",
4652 src_file, src_line, fn,
4653 fr_syserror(errno));
4659 * If fopen works, this works.
4661 if (stat(fn, &statbuf) < 0) {
4666 if (!S_ISREG(statbuf.st_mode)) {
4668 fr_strerror_printf_push("Dictionary is not a regular file: %s", fn);
4673 * Globally writable dictionaries means that users can control
4674 * the server configuration with little difficulty.
4677 if ((statbuf.st_mode & S_IWOTH) != 0) {
4679 fr_strerror_printf_push("Dictionary is globally writable: %s. "
4680 "Refusing to start due to insecure configuration", fn);
4686 * Seed the random pool with data.
4688 fr_rand_seed(&statbuf, sizeof(statbuf));
4690 memset(&base_flags, 0, sizeof(base_flags));
4692 while (fgets(buf, sizeof(buf), fp) != NULL) {
4704 * Comment characters should NOT be appearing anywhere but
4705 * as start of a comment;
4707 p = strchr(buf, '#');
4710 argc = fr_dict_str_to_argv(buf, argv, MAX_ARGV);
4711 if (argc == 0) continue;
4714 fr_strerror_printf("Invalid entry");
4717 fr_strerror_printf_push("Error reading %s[%d]", fn, line);
4723 * Process VALUE lines.
4725 if (strcasecmp(argv[0], "VALUE") == 0) {
4726 if (dict_read_process_value(ctx, argv + 1, argc - 1) == -1) goto error;
4731 * Perhaps this is an attribute.
4733 if (strcasecmp(argv[0], "ATTRIBUTE") == 0) {
4734 if (!base_flags.named) {
4735 if (dict_read_process_attribute(ctx,
4737 &base_flags, &previous) == -1) goto error;
4739 if (dict_read_process_named_attribute(ctx,
4741 &base_flags) == -1) goto error;
4747 * Process VALUE lines.
4749 if (strcasecmp(argv[0], "FLAGS") == 0) {
4750 if (dict_read_process_flags(ctx->dict, argv + 1, argc - 1, &base_flags) == -1) goto error;
4755 * See if we need to import another dictionary.
4757 if (strncasecmp(argv[0], "$INCLUDE", 8) == 0) {
4759 dict_from_file_ctx_t nctx = *ctx;
4762 * Allow "$INCLUDE" or "$INCLUDE-", but
4763 * not anything else.
4765 if ((argv[0][8] != '\0') && ((argv[0][8] != '-') || (argv[0][9] != '\0'))) goto invalid_keyword;
4768 * Included files operate on a copy of the context.
4770 * This copy means that they inherit the
4771 * current context, including parents,
4772 * TLVs, etc. But if the included file
4773 * leaves a "dangling" TLV or "last
4774 * attribute", then it won't affect the
4778 rcode = _dict_from_file(&nctx, dir, argv[1], fn, line);
4779 if ((rcode == -2) && (argv[0][8] == '-')) {
4780 fr_strerror_printf(NULL); /* delete all errors */
4785 fr_strerror_printf_push("from $INCLUDE at %s[%d]", fn, line);
4791 * Fixups are added to the head of the
4792 * list, so copy the new head over to the
4795 ctx->enum_fixup = nctx.enum_fixup;
4800 * Process VENDOR lines.
4802 if (strcasecmp(argv[0], "VENDOR") == 0) {
4803 if (dict_read_process_vendor(ctx->dict, argv + 1, argc - 1) == -1) goto error;
4808 * Process PROTOCOL line. Defines a new protocol.
4810 if (strcasecmp(argv[0], "PROTOCOL") == 0) {
4812 fr_strerror_printf_push("Invalid PROTOCOL entry");
4815 if (dict_read_process_protocol(argv + 1, argc - 1) == -1) goto error;
4820 * Switches the current protocol context
4822 if (strcasecmp(argv[0], "BEGIN-PROTOCOL") == 0) {
4825 ctx->old_dict = ctx->dict;
4828 fr_strerror_printf_push("Invalid BEGIN-PROTOCOL entry");
4833 * If we're not parsing in the context of the internal
4834 * dictionary, then we don't allow BEGIN-PROTOCOL
4837 if (ctx->dict != fr_dict_internal) {
4838 fr_strerror_printf_push("Nested BEGIN-PROTOCOL statements are not allowed");
4842 found = fr_dict_by_protocol_name(argv[1]);
4844 fr_strerror_printf("Unknown protocol '%s'", argv[1]);
4849 * Add a temporary fixup pool
4851 * @todo - make a nested ctx?
4853 if (!ctx->fixup_pool) ctx->fixup_pool = talloc_pool(NULL, DICT_FIXUP_POOL_SIZE);
4856 ctx->parent = ctx->dict->root;
4861 * Switches back to the previous protocol context
4863 if (strcasecmp(argv[0], "END-PROTOCOL") == 0) {
4864 fr_dict_t const *found;
4867 fr_strerror_printf("Invalid END-PROTOCOL entry");
4871 found = fr_dict_by_protocol_name(argv[1]);
4873 fr_strerror_printf("END-PROTOCOL %s does not refer to a valid protocol", argv[1]);
4877 if (found != ctx->dict) {
4878 fr_strerror_printf("END-PROTOCOL %s does not match previous BEGIN-PROTOCOL %s",
4879 argv[1], found->root->name);
4884 * Applies fixups to any attributes added to
4885 * the protocol dictionary.
4887 if (fr_dict_finalise(ctx) < 0) goto error;
4890 * Switch back to old values.
4892 * @todo - just create a stack of contests, so we don't need "old_foo"
4894 ctx->dict = ctx->old_dict;
4895 ctx->parent = ctx->dict->root;
4900 * Switches TLV parent context
4902 if (strcasecmp(argv[0], "BEGIN-TLV") == 0) {
4903 fr_dict_attr_t const *common;
4905 if ((ctx->block_tlv_depth + 1) > FR_DICT_TLV_NEST_MAX) {
4906 fr_strerror_printf_push("TLVs are nested too deep");
4911 fr_strerror_printf_push("Invalid BEGIN-TLV entry");
4915 da = fr_dict_attr_by_name(ctx->dict, argv[1]);
4917 fr_strerror_printf_push("Unknown attribute '%s'", argv[1]);
4921 if (da->type != FR_TYPE_TLV) {
4922 fr_strerror_printf_push("Attribute '%s' should be a 'tlv', but is a '%s'",
4924 fr_int2str(fr_value_box_type_table, da->type, "?Unknown?"));
4928 common = fr_dict_parent_common(ctx->parent, da, true);
4930 (common->type == FR_TYPE_VSA) ||
4931 (common->type == FR_TYPE_EVS)) {
4932 fr_strerror_printf_push("Attribute '%s' should be a child of '%s'",
4933 argv[1], ctx->parent->name);
4937 ctx->block_tlv[ctx->block_tlv_depth++] = ctx->parent;
4944 * Switches back to previous TLV parent
4946 if (strcasecmp(argv[0], "END-TLV") == 0) {
4947 if (--ctx->block_tlv_depth < 0) {
4948 fr_strerror_printf_push("Too many END-TLV entries. Mismatch at END-TLV %s", argv[1]);
4953 fr_strerror_printf_push("Invalid END-TLV entry");
4957 da = fr_dict_attr_by_name(ctx->dict, argv[1]);
4959 fr_strerror_printf_push("Unknown attribute '%s'", argv[1]);
4963 if (da != ctx->parent) {
4964 fr_strerror_printf_push("END-TLV %s does not match previous BEGIN-TLV %s", argv[1],
4968 ctx->parent = ctx->block_tlv[ctx->block_tlv_depth];
4972 if (strcasecmp(argv[0], "BEGIN-VENDOR") == 0) {
4973 fr_dict_vendor_t const *vendor;
4974 fr_dict_attr_flags_t flags;
4976 fr_dict_attr_t const *vsa_da;
4977 fr_dict_attr_t const *vendor_da;
4978 fr_dict_attr_t *new;
4979 fr_dict_attr_t *mutable;
4982 fr_strerror_printf_push("Invalid BEGIN-VENDOR entry");
4986 vendor = fr_dict_vendor_by_name(ctx->dict, argv[1]);
4988 fr_strerror_printf_push("Unknown vendor '%s'", argv[1]);
4993 * Check for extended attr VSAs
4995 * BEGIN-VENDOR foo format=Foo-Encapsulation-Attr
4998 if (strncmp(argv[2], "format=", 7) != 0) {
4999 fr_strerror_printf_push("Invalid format %s", argv[2]);
5004 da = fr_dict_attr_by_name(ctx->dict, p);
5006 fr_strerror_printf_push("Invalid format for BEGIN-VENDOR: Unknown "
5007 "attribute '%s'", p);
5011 if (da->type != FR_TYPE_EVS) {
5012 fr_strerror_printf_push("Invalid format for BEGIN-VENDOR. "
5013 "Attribute '%s' should be 'evs' but is '%s'", p,
5014 fr_int2str(fr_value_box_type_table, da->type, "?Unknown?"));
5021 * Automagically create Attribute 26
5023 * This should exist, but in case we're starting without
5024 * the RFC dictionaries we need to add it in the case
5027 vsa_da = fr_dict_attr_child_by_num(ctx->parent, FR_VENDOR_SPECIFIC);
5029 memset(&flags, 0, sizeof(flags));
5031 if (fr_dict_attr_add(ctx->dict, ctx->parent, "Vendor-Specific",
5032 FR_VENDOR_SPECIFIC, FR_TYPE_VSA, &flags) < 0) {
5033 fr_strerror_printf_push("Failed adding Vendor-Specific for Vendor %s",
5038 vsa_da = fr_dict_attr_child_by_num(ctx->parent, FR_VENDOR_SPECIFIC);
5040 fr_strerror_printf_push("Failed finding Vendor-Specific for Vendor %s",
5048 * Create a VENDOR attribute on the fly, either in the context
5049 * of the EVS attribute, or the VSA (26) attribute.
5051 vendor_da = fr_dict_attr_child_by_num(vsa_da, vendor->pen);
5053 memset(&flags, 0, sizeof(flags));
5055 if (vsa_da->type == FR_TYPE_VSA) {
5056 fr_dict_vendor_t const *dv;
5058 dv = fr_dict_vendor_by_num(ctx->dict, vendor->pen);
5060 flags.type_size = dv->type;
5061 flags.length = dv->length;
5063 } else { /* unknown vendor, shouldn't happen */
5064 flags.type_size = 1;
5068 } else { /* EVS are always "format=1,1" */
5069 flags.type_size = 1;
5073 memcpy(&mutable, &vsa_da, sizeof(mutable));
5074 new = dict_attr_alloc(mutable, ctx->parent, argv[1],
5075 vendor->pen, FR_TYPE_VENDOR, &flags);
5076 dict_attr_child_add(mutable, new);
5080 ctx->parent = vendor_da;
5081 ctx->block_vendor = vendor;
5083 } /* BEGIN-VENDOR */
5085 if (strcasecmp(argv[0], "END-VENDOR") == 0) {
5086 fr_dict_vendor_t const *vendor;
5089 fr_strerror_printf_push("Invalid END-VENDOR entry");
5093 vendor = fr_dict_vendor_by_name(ctx->dict, argv[1]);
5095 fr_strerror_printf_push("Unknown vendor '%s'", argv[1]);
5099 if (vendor != ctx->block_vendor) {
5100 fr_strerror_printf_push("END-VENDOR '%s' does not match any previous BEGIN-VENDOR",
5104 ctx->parent = ctx->dict->root;
5105 ctx->block_vendor = NULL;
5110 * Any other string: We don't recognize it.
5113 fr_strerror_printf_push("Invalid keyword '%s'", argv[0]);
5120 static int dict_from_file(fr_dict_t *dict,
5121 char const *dir_name, char const *filename,
5122 char const *src_file, int src_line)
5125 dict_from_file_ctx_t ctx;
5127 memset(&ctx, 0, sizeof(ctx));
5129 ctx.parent = dict->root;
5131 rcode = _dict_from_file(&ctx,
5132 dir_name, filename, src_file, src_line);
5134 // free up the various fixups
5139 * Applies to any attributes added to the *internal*
5142 * Fixups should have been applied already to any protocol
5145 return fr_dict_finalise(&ctx);
5148 /** (Re-)Initialize the special internal dictionary
5150 * This dictionary has additional programatically generated attributes added to it,
5151 * and is checked in addition to the protocol specific dictionaries.
5153 * @note The dictionary pointer returned in out must have its reference counter
5154 * decremented with #fr_dict_free when no longer used.
5156 * @param[out] out Where to write pointer to the internal dictionary.
5157 * @param[in] dict_subdir name of the internal dictionary dir (may be NULL).
5162 int fr_dict_internal_afrom_file(fr_dict_t **out, char const *dict_subdir)
5165 char *dict_path = NULL;
5167 FR_NAME_NUMBER const *p;
5168 fr_dict_attr_flags_t flags = { .internal = true };
5171 if (unlikely(!protocol_by_name || !protocol_by_num)) {
5172 fr_strerror_printf("fr_dict_global_init() must be called before loading dictionary files");
5177 * Increase the reference count of the internal dictionary.
5179 if (fr_dict_internal) {
5180 talloc_increase_ref_count(fr_dict_internal);
5181 *out = fr_dict_internal;
5185 memcpy(&tmp, &default_dict_dir, sizeof(tmp));
5186 dict_path = dict_subdir ? talloc_asprintf(NULL, "%s%c%s", default_dict_dir, FR_DIR_SEP, dict_subdir) : tmp;
5188 dict = dict_alloc(dict_ctx);
5191 if (!fr_dict_internal) talloc_free(dict);
5192 talloc_free(dict_path);
5197 * Set the root name of the dictionary
5199 dict_root_set(dict, "internal", 0);
5202 * Add cast attributes. We do it this way,
5203 * so cast attributes get added automatically for new types.
5205 * We manually add the attributes to the dictionary, and bypass
5206 * fr_dict_attr_add(), because we know what we're doing, and
5207 * that function does too many checks.
5209 for (p = fr_value_box_type_table; p->name; p++) {
5212 type_name = talloc_typed_asprintf(NULL, "Tmp-Cast-%s", p->name);
5214 n = dict_attr_alloc(dict->pool, dict->root, type_name,
5215 FR_CAST_BASE + p->number, p->number, &flags);
5217 talloc_free(type_name);
5221 if (!fr_hash_table_insert(dict->attributes_by_name, n)) {
5222 fr_strerror_printf("Failed inserting \"%s\" into internal dictionary", type_name);
5223 talloc_free(type_name);
5227 talloc_free(type_name);
5230 * Set up parenting for the attribute.
5232 if (dict_attr_child_add(dict->root, n) < 0) goto error;
5235 if (dict_path && dict_from_file(dict, dict_path, FR_DICTIONARY_FILE, NULL, 0) < 0) goto error;
5237 talloc_free(dict_path);
5240 if (!fr_dict_internal) fr_dict_internal = dict;
5245 /** (Re)-initialize a protocol dictionary
5247 * Initialize the directory, then fix the attr number of all attributes.
5249 * @param[out] out Where to write a pointer to the new dictionary. Will free existing
5250 * dictionary if files have changed and *out is not NULL.
5251 * @param[in] proto_name that we're loading the dictionary for.
5252 * @param[in] proto_dir Explicitly set where to hunt for the dictionary files. May be NULL.
5257 int fr_dict_protocol_afrom_file(fr_dict_t **out, char const *proto_name, char const *proto_dir)
5259 char *dict_dir = NULL;
5262 if (unlikely(!protocol_by_name || !protocol_by_num)) {
5263 fr_strerror_printf("fr_dict_global_init() must be called before loading dictionary files");
5267 if (unlikely(!fr_dict_internal)) {
5268 fr_strerror_printf("Internal dictionary must be initialised before loading protocol dictionaries");
5273 * Increment the reference count if the dictionary
5274 * has already been loaded and return that.
5276 dict = fr_dict_by_protocol_name(proto_name);
5277 if (dict && dict->autoloaded) {
5278 talloc_increase_ref_count(dict);
5284 dict_dir = talloc_asprintf(NULL, "%s%c%s", default_dict_dir, FR_DIR_SEP, proto_name);
5286 dict_dir = talloc_asprintf(NULL, "%s%c%s", default_dict_dir, FR_DIR_SEP, proto_dir);
5290 * Start in the context of the internal dictionary,
5291 * and switch to the context of a protocol dictionary
5292 * when we hit a BEGIN-PROTOCOL line.
5294 * This allows a single file to provide definitions
5295 * for multiple protocols, which'll probably be useful
5298 if (dict_from_file(fr_dict_internal, dict_dir, FR_DICTIONARY_FILE, NULL, 0) < 0) {
5300 talloc_free(dict_dir);
5305 * Check the dictionary actually defined the protocol
5307 dict = fr_dict_by_protocol_name(proto_name);
5309 fr_strerror_printf("Dictionary \"%s\" missing \"BEGIN-PROTOCOL %s\" declaration", dict_dir, proto_name);
5313 talloc_free(dict_dir);
5316 * If we're autoloading a previously defined dictionary,
5317 * then mark up the dictionary as now autoloaded.
5319 if (!dict->autoloaded) {
5320 // talloc_increase_ref_count(dict);
5321 dict->autoloaded = true;
5329 /** Read supplementary attribute definitions into an existing dictionary
5331 * @param[in] dict Existing dictionary.
5332 * @param[in] dir dictionary is located in.
5333 * @param[in] filename of the dictionary.
5338 int fr_dict_read(fr_dict_t *dict, char const *dir, char const *filename)
5340 INTERNAL_IF_NULL(dict, -1);
5342 if (!dict->attributes_by_name) {
5343 fr_strerror_printf("%s: Must call fr_dict_internal_afrom_file() before fr_dict_read()", __FUNCTION__);
5347 return dict_from_file(dict, dir, filename, NULL, 0);
5351 /** Decrement the reference count on a previously loaded dictionary
5353 * @param[in] dict to free.
5355 void fr_dict_free(fr_dict_t **dict)
5359 talloc_decrease_ref_count(*dict);
5363 /** Process a dict_attr_autoload element to load/verify a dictionary attribute
5365 * @param[in] to_load attribute definition
5370 int fr_dict_attr_autoload(fr_dict_attr_autoload_t const *to_load)
5372 fr_dict_attr_t const *da;
5373 fr_dict_attr_autoload_t const *p = to_load;
5375 for (p = to_load; p->out; p++) {
5377 fr_strerror_printf("Invalid autoload entry, missing dictionary pointer");
5382 fr_strerror_printf("Can't resolve attribute \"%s\", dictionary not loaded", p->name);
5383 fr_strerror_printf_push("Check fr_dict_autoload_t struct has "
5384 "an entry to load the dictionary \"%s\" is located in, and that "
5385 "the symbol name is correct", p->name);
5389 da = fr_dict_attr_by_name(*p->dict, p->name);
5391 fr_strerror_printf("Attribute \"%s\" not found in \"%s\" dictionary", p->name,
5392 *p->dict ? (*p->dict)->root->name : "internal");
5396 if (da->type != p->type) {
5397 fr_strerror_printf("Attribute \"%s\" should be type %s, but defined as type %s", da->name,
5398 fr_int2str(fr_value_box_type_table, p->type, "?Unknown?"),
5399 fr_int2str(fr_value_box_type_table, da->type, "?Unknown?"));
5403 if (p->out) *(p->out) = da;
5409 /** Process a dict_autoload element to load a protocol
5411 * @param[in] to_load dictionary definition.
5416 int fr_dict_autoload(fr_dict_autoload_t const *to_load)
5418 fr_dict_autoload_t const *p;
5420 for (p = to_load; p->out; p++) {
5421 fr_dict_t *dict = NULL;
5423 if (unlikely(!p->proto)) {
5424 fr_strerror_printf("autoload missing parameter proto");
5429 * Load the internal dictionary
5431 if (strcmp(p->proto, "freeradius") == 0) {
5432 if (fr_dict_internal_afrom_file(&dict, p->proto) < 0) return -1;
5434 if (fr_dict_protocol_afrom_file(&dict, p->proto, p->base_dir) < 0) return -1;
5443 /** Decrement the reference count on a previously loaded dictionary
5445 * @param[in] to_free previously loaded dictionary to free.
5447 void fr_dict_autofree(fr_dict_autoload_t const *to_free)
5450 fr_dict_autoload_t const *p;
5452 for (p = to_free; p->out; p++) {
5454 if (!*dict) continue;
5460 static void _fr_dict_dump(fr_dict_attr_t const *da, unsigned int lvl)
5464 fr_dict_attr_t const *p;
5467 fr_dict_snprint_flags(flags, sizeof(flags), &da->flags);
5469 printf("[%02i] 0x%016" PRIxPTR "%*s %s(%u) %s %s\n", lvl, (unsigned long)da, lvl * 2, " ",
5470 da->name, da->attr, fr_int2str(fr_value_box_type_table, da->type, "<INVALID>"), flags);
5472 len = talloc_array_length(da->children);
5473 for (i = 0; i < len; i++) {
5474 for (p = da->children[i]; p; p = p->next) {
5475 _fr_dict_dump(p, lvl + 1);
5480 void fr_dict_dump(fr_dict_t *dict)
5482 _fr_dict_dump(dict->root, 0);
5486 * External API for testing
5488 int fr_dict_parse_str(fr_dict_t *dict, char *buf, fr_dict_attr_t const *parent, unsigned int vendor_pen)
5491 char *argv[MAX_ARGV];
5493 fr_dict_attr_flags_t base_flags;
5494 dict_from_file_ctx_t ctx;
5496 INTERNAL_IF_NULL(dict, -1);
5498 argc = fr_dict_str_to_argv(buf, argv, MAX_ARGV);
5499 if (argc == 0) return 0;
5502 memset(&ctx, 0, sizeof(ctx));
5505 ctx.fixup_pool = talloc_pool(NULL, DICT_FIXUP_POOL_SIZE);
5506 if (!ctx.fixup_pool) return -1;
5508 if (strcasecmp(argv[0], "VALUE") == 0) {
5510 fr_strerror_printf("VALUE needs at least 4 arguments, got %i", argc);
5512 TALLOC_FREE(ctx.fixup_pool);
5516 if (!fr_dict_attr_by_name(dict, argv[1])) {
5517 fr_strerror_printf("Attribute \"%s\" does not exist in dictionary \"%s\"",
5518 argv[1], dict->root->name);
5521 ret = dict_read_process_value(&ctx, argv + 1, argc - 1);
5522 if (ret < 0) goto error;
5524 } else if (strcasecmp(argv[0], "ATTRIBUTE") == 0) {
5525 if (!parent) parent = fr_dict_root(dict);
5527 memset(&base_flags, 0, sizeof(base_flags));
5529 if (vendor_pen) ctx.block_vendor = fr_dict_vendor_by_num(dict, vendor_pen);
5531 ret = dict_read_process_attribute(&ctx,
5532 argv + 1, argc - 1, &base_flags, NULL);
5533 if (ret < 0) goto error;
5534 } else if (strcasecmp(argv[0], "VENDOR") == 0) {
5535 ret = dict_read_process_vendor(dict, argv + 1, argc - 1);
5536 if (ret < 0) goto error;
5538 fr_strerror_printf("Invalid input '%s'", argv[0]);
5542 fr_dict_finalise(&ctx);
5547 /** Initialise the global protocol hashes
5549 * @note Must be called before any other dictionary functions.
5551 * @param[in] ctx to allocate protocol hashes in.
5552 * @param[in] dict_dir the default location for the dictionaries.
5557 int fr_dict_global_init(TALLOC_CTX *ctx, char const *dict_dir)
5559 if (!protocol_by_name) {
5560 protocol_by_name = fr_hash_table_create(ctx, dict_protocol_name_hash, dict_protocol_name_cmp, NULL);
5561 if (!protocol_by_name) {
5562 fr_strerror_printf("Failed initializing protocol_by_name hash");
5567 if (!protocol_by_num) {
5568 protocol_by_num = fr_hash_table_create(ctx, dict_protocol_num_hash, dict_protocol_num_cmp, NULL);
5569 if (!protocol_by_num) {
5570 fr_strerror_printf("Failed initializing protocol_by_num hash");
5575 talloc_free(default_dict_dir); /* Free previous value */
5576 default_dict_dir = talloc_strdup(ctx, dict_dir);
5584 ssize_t fr_dict_valid_name(char const *name, ssize_t len)
5586 char const *p = name, *end;
5588 if (len < 0) len = strlen(name);
5590 if (len > FR_DICT_ATTR_MAX_NAME_LEN) {
5591 fr_strerror_printf("Attribute name is too long");
5598 if (!fr_dict_attr_allowed_chars[(uint8_t)*p]) {
5599 fr_strerror_printf("Invalid character '%pV' in attribute name \"%pV\"",
5600 fr_box_strvalue_len(p, 1), fr_box_strvalue_len(name, len));
5610 ssize_t fr_dict_valid_oid_str(char const *name, ssize_t len)
5612 char const *p = name, *end;
5614 if (len < 0) len = strlen(name);
5618 if (!fr_dict_attr_allowed_chars[(uint8_t)*p] && (*p != '.')) {
5619 fr_strerror_printf("Invalid character '%pV' in oid string \"%pV\"",
5620 fr_box_strvalue_len(p, 1), fr_box_strvalue_len(name, len));
5630 void fr_dict_verify(char const *file, int line, fr_dict_attr_t const *da)
5633 fr_dict_attr_t const *da_p;
5636 FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: fr_dict_attr_t pointer was NULL", file, line);
5638 if (!fr_cond_assert(0)) fr_exit_now(1);
5641 (void) talloc_get_type_abort_const(da, fr_dict_attr_t);
5643 if ((!da->flags.is_root) && (da->depth == 0)) {
5644 FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: fr_dict_attr_t %s vendor: %i, attr %i: "
5645 "Is not root, but depth is 0",
5646 file, line, da->name, fr_dict_vendor_num_by_da(da), da->attr);
5648 if (!fr_cond_assert(0)) fr_exit_now(1);
5651 if (da->depth > FR_DICT_MAX_TLV_STACK) {
5652 FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: fr_dict_attr_t %s vendor: %i, attr %i: "
5653 "Indicated depth (%u) greater than TLV stack depth (%u)",
5654 file, line, da->name, fr_dict_vendor_num_by_da(da), da->attr,
5655 da->depth, FR_DICT_MAX_TLV_STACK);
5657 if (!fr_cond_assert(0)) fr_exit_now(1);
5660 for (da_p = da; da_p; da_p = da_p->next) {
5661 (void) talloc_get_type_abort_const(da_p, fr_dict_attr_t);
5664 for (i = da->depth, da_p = da; (i >= 0) && da; i--, da_p = da_p->parent) {
5665 if (i != (int)da_p->depth) {
5666 FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: fr_dict_attr_t %s vendor: %i, attr %i: "
5667 "Depth out of sequence, expected %i, got %u",
5668 file, line, da->name, fr_dict_vendor_num_by_da(da), da->attr, i, da_p->depth);
5670 if (!fr_cond_assert(0)) fr_exit_now(1);
5676 FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: fr_dict_attr_t top of hierarchy was not at depth 0",
5679 if (!fr_cond_assert(0)) fr_exit_now(1);