# define INTERNAL_IF_NULL(_dict) if (!_dict) _dict = fr_dict_internal
#endif
+static inline int dict_attr_child_add(fr_dict_attr_t *parent, fr_dict_attr_t *child);
static int dict_from_file(fr_dict_t *dict,
char const *dir_name, char const *filename,
char const *src_file, int src_line);
return 0;
}
-/** Allocate a dictionary attribute as a pool, and assign a name buffer
- *
- * @param[in] ctx to allocate attribute in.
- * @param[in] name to set.
- * @return
- * - 0 on success.
- * - -1 on failure (memory allocation error).
- */
-static fr_dict_attr_t *dict_attr_alloc_name(TALLOC_CTX *ctx, char const *name)
-{
- fr_dict_attr_t *da;
-
- if (!name) {
- fr_strerror_printf("No attribute name provided");
- return NULL;
- }
-
-#ifdef HAVE_TALLOC_POOLED_OBJECT
- da = talloc_pooled_object(ctx, fr_dict_attr_t, 1, strlen(name) + 1);
- memset(da, 0, sizeof(*da));
-#else
- da = talloc_zero(ctx, fr_dict_attr_t);
-#endif
-
- da->name = talloc_typed_strdup(da, name);
- if (!da->name) {
- talloc_free(da);
- fr_strerror_printf("Out of memory");
- return NULL;
- }
-
- return da;
-}
-
-/** Copy a an existing attribute
+/** Validate a new attribute definition
*
- * @param[in] ctx to allocate new attribute in.
- * @param[in] in attribute to copy.
- * @return
- * - A copy of the input fr_dict_attr_t on success.
- * - NULL on failure.
- */
-static fr_dict_attr_t *dict_attr_acopy(TALLOC_CTX *ctx, fr_dict_attr_t const *in)
-{
- fr_dict_attr_t *n;
-
- n = dict_attr_alloc_name(ctx, in->name);
- if (!n) return NULL;
-
- n->attr = in->attr;
- n->type = in->type;
- n->flags = in->flags;
- n->parent = in->parent;
- n->depth = in->depth;
-
- return n;
-}
-
-/** Allocate a dictionary attribute on the heap
+ * @todo we need to check length of none vendor attributes.
*
- * @param[in] ctx to allocate the attribute in.
- * @param[in] parent of the attribute, if none, should be
- * the dictionary root.
- * @param[in] name of the attribute. If NULL an OID string
- * will be created and set as the name.
+ * @param[in] dict of protocol context we're operating in.
+ * If NULL the internal dictionary will be used.
+ * @param[in] parent to add attribute under.
+ * @param[in] name of the attribute.
* @param[in] attr number.
- * @param[in] type of the attribute.
- * @param[in] flags to assign.
+ * @param[in] type of attribute.
+ * @param[in] flags to set in the attribute.
* @return
- * - A new fr_dict_attr_t on success.
- * - NULL on failure.
+ * - true if attribute definition is valid.
+ * - false if attribute definition is not valid.
*/
-static fr_dict_attr_t *dict_attr_alloc(TALLOC_CTX *ctx,
- fr_dict_attr_t const *parent,
- char const *name, int attr,
- fr_type_t type, fr_dict_attr_flags_t const *flags)
+static bool dict_attr_fields_valid(fr_dict_t *dict, fr_dict_attr_t const *parent,
+ char const *name, int *attr, fr_type_t type, fr_dict_attr_flags_t *flags)
{
- fr_dict_attr_t *da;
- fr_dict_attr_t new = {
- .attr = attr,
- .type = type,
- .flags = *flags,
- .parent = parent,
- .depth = parent->depth + 1,
- };
+ size_t name_len;
+ fr_dict_attr_t const *v;
if (!fr_cond_assert(parent)) return NULL;
- if (!name) {
- char buffer[FR_DICT_ATTR_MAX_NAME_LEN + 1];
- char *p = buffer;
- size_t len;
+ name_len = strlen(name);
+ if (name_len >= FR_DICT_ATTR_MAX_NAME_LEN) {
+ fr_strerror_printf("Attribute name too long");
+ error:
+ fr_strerror_printf_push("Definition for '%s' is invalid", name);
+ return false;
+ }
- len = snprintf(p, sizeof(buffer), "Attr-");
- p += len;
+ if (fr_dict_valid_name(name, -1) < 0) return NULL;
- len = fr_dict_print_attr_oid(p, sizeof(buffer) - (p - buffer), NULL, &new);
- if (is_truncated(len, sizeof(buffer) - (p - buffer))) {
- fr_strerror_printf("OID string too long for unknown attribute");
- return NULL;
+ /*
+ * type_size is used to limit the maximum attribute number, so it's checked first.
+ */
+ if (flags->type_size) {
+ if ((type != FR_TYPE_TLV) && (type != FR_TYPE_VENDOR)) {
+ fr_strerror_printf("The 'format=' flag can only be used with attributes of type 'tlv'");
+ goto error;
}
- da = dict_attr_alloc_name(ctx, buffer);
- } else {
- da = dict_attr_alloc_name(ctx, name);
+ if ((flags->type_size != 1) &&
+ (flags->type_size != 2) &&
+ (flags->type_size != 4)) {
+ fr_strerror_printf("The 'format=' flag can only be used with attributes of type size 1,2 or 4");
+ goto error;
+ }
}
- new.name = da->name;
- memcpy(da, &new, sizeof(*da));
-
- return da;
-}
-
-/** Set a new root dictionary attribute
- *
- * @note Must only be called once per dictionary.
- *
- * @param[in] dict to modify.
- * @param[in] name of dictionary root.
- * @param[in] protocol_number The artificial (or IANA allocated) number for the protocol.
- * This is only used for
- * @return
- * - 0 on success.
- * - -1 on failure.
- */
-static int dict_root_set(fr_dict_t *dict, char const *name, unsigned int proto_number)
-{
- if (!fr_cond_assert(!dict->root)) {
- fr_strerror_printf("Dictionary root already set");
- return -1;
- }
+ /******************** sanity check attribute number ********************/
- dict->root = dict_attr_alloc_name(dict, name);
- if (!dict->root) return -1;
+ if (parent->flags.is_root) {
+ static unsigned int max_attr = UINT8_MAX + 1;
- dict->root->attr = proto_number;
- dict->root->flags.is_root = 1;
- dict->root->type = FR_TYPE_TLV;
- dict->root->flags.type_size = 1;
- dict->root->flags.length = 1;
- VERIFY_DA(dict->root);
+ if (*attr == -1) {
+ if (fr_dict_attr_by_name(dict, name)) return 0; /* exists, don't add it again */
+ *attr = ++max_attr;
+ flags->internal = 1;
- return 0;
-}
+ } else if (*attr <= 0) {
+ fr_strerror_printf("ATTRIBUTE number %i is invalid, must be greater than zero", *attr);
+ goto error;
-/** Allocate a new dictionary
- *
- * @param[in] ctx to allocate dictionary in.
- * @return
- * - NULL on memory allocation error.
- */
-static fr_dict_t *dict_alloc(TALLOC_CTX *ctx)
-{
- fr_dict_t *dict;
+ } else if ((unsigned int) *attr > max_attr) {
+ max_attr = *attr;
+ }
- dict = talloc_zero(ctx, fr_dict_t);
- if (!dict) {
- error:
- fr_strerror_printf("Failed allocating memory for dictionary");
- talloc_free(dict);
- return NULL;
+ /*
+ * Auto-set internal flags for raddb/dictionary.
+ * So that the end user doesn't have to know
+ * about internal implementation of the server.
+ */
+ if ((parent->flags.type_size == 1) &&
+ (*attr >= 3000) && (*attr < 4000)) {
+ flags->internal = true;
+ }
}
/*
- * Pre-Allocate 5MB of pool memory for rapid startup
+ * Any other negative attribute number is wrong.
*/
- dict->pool = talloc_pool(dict, (1024 * 1024 * 5));
- if (!dict->pool) goto error;
+ if (*attr < 0) {
+ fr_strerror_printf("ATTRIBUTE number %i is invalid, must be greater than zero", *attr);
+ goto error;
+ }
/*
- * Create the table of vendor by name. There MAY NOT
- * be multiple vendors of the same name.
+ * If attributes have number greater than 255, do sanity checks.
+ *
+ * We assume that the root attribute is of type TLV, with
+ * the appropriate flags set for attributes in this
+ * space.
*/
- dict->vendors_by_name = fr_hash_table_create(dict, dict_vendor_name_hash, dict_vendor_name_cmp, hash_pool_free);
- if (!dict->vendors_by_name) goto error;
+ if ((*attr > UINT8_MAX) && !flags->internal &&
+ !((strncmp("VMPS", name, 4) == 0) || (strncmp("VQP", name, 3) == 0))) { /* Fixme */
+ for (v = parent; v != NULL; v = v->parent) {
+ if ((v->type == FR_TYPE_TLV) || (v->type == FR_TYPE_VENDOR)) {
+ if ((v->flags.type_size < 4) &&
+ (*attr >= (1 << (8 * v->flags.type_size)))) {
+ fr_strerror_printf("Attributes must have value between 1..%u",
+ (1 << (8 * v->flags.type_size)) - 1);
+ goto error;
+ }
+ break;
+ }
+ }
+ }
- /*
- * Create the table of vendors by value. There MAY
- * be vendors of the same value. If there are, we
- * pick the latest one.
- */
- dict->vendors_by_num = fr_hash_table_create(dict, dict_vendor_vendorpec_hash, dict_vendor_vendorpec_cmp, NULL);
- if (!dict->vendors_by_num) goto error;
+ /******************** sanity check flags ********************/
/*
- * Create the table of attributes by name. There MAY NOT
- * be multiple attributes of the same name.
+ * virtual attributes are special.
*/
- dict->attributes_by_name = fr_hash_table_create(dict, dict_attr_name_hash, dict_attr_name_cmp, NULL);
- if (!dict->attributes_by_name) goto error;
+ if (flags->virtual) {
+ if (!parent->flags.is_root) {
+ fr_strerror_printf("The 'virtual' flag can only be used for normal attributes");
+ goto error;
+ }
+
+ if (*attr <= (1 << (8 * parent->flags.type_size))) {
+ fr_strerror_printf("The 'virtual' flag can only be used for non-protocol attributes");
+ goto error;
+ }
+ }
/*
- * Horrible hacks for combo-IP.
+ * Tags can only be used in a few limited situations.
*/
- dict->attributes_combo = fr_hash_table_create(dict, dict_attr_combo_hash, dict_attr_combo_cmp, hash_pool_free);
- if (!dict->attributes_combo) goto error;
-
- dict->values_by_alias = fr_hash_table_create(dict, dict_enum_alias_hash, dict_enum_alias_cmp, hash_pool_free);
- if (!dict->values_by_alias) goto error;
+ if (flags->has_tag) {
+ if ((type != FR_TYPE_UINT32) && (type != FR_TYPE_STRING)) {
+ fr_strerror_printf("The 'has_tag' flag can only be used for attributes of type 'integer' "
+ "or 'string'");
+ goto error;
+ }
- dict->values_by_da = fr_hash_table_create(dict, dict_enum_value_hash, dict_enum_value_cmp, hash_pool_free);
- if (!dict->values_by_da) goto error;
+ if (!(parent->flags.is_root ||
+ ((parent->type == FR_TYPE_VENDOR) &&
+ (parent->parent && parent->parent->type == FR_TYPE_VSA)))) {
+ fr_strerror_printf("The 'has_tag' flag can only be used with RFC and VSA attributes");
+ goto error;
+ }
- return dict;
-}
+ if (flags->array || flags->has_value || flags->concat || flags->virtual || flags->length) {
+ fr_strerror_printf("The 'has_tag' flag cannot be used with any other flag");
+ goto error;
+ }
-/** Add a child to a parent.
- *
- * @param[in] parent we're adding a child to.
- * @param[in] child to add to parent.
- * @return
- * - 0 on success.
- * - -1 on failure (memory allocation error).
- */
-static inline int dict_attr_child_add(fr_dict_attr_t *parent, fr_dict_attr_t *child)
-{
- fr_dict_attr_t const * const *bin;
- fr_dict_attr_t **this;
+ if (flags->encrypt && (flags->encrypt != FLAG_ENCRYPT_TUNNEL_PASSWORD)) {
+ fr_strerror_printf("The 'has_tag' flag can only be used with 'encrypt=2'");
+ goto error;
+ }
+ }
/*
- * Setup fields in the child
+ * 'concat' can only be used in a few limited situations.
*/
- child->parent = parent;
- child->depth = parent->depth + 1;
+ if (flags->concat) {
+ if (type != FR_TYPE_OCTETS) {
+ fr_strerror_printf("The 'concat' flag can only be used for attributes of type 'octets'");
+ goto error;
+ }
- VERIFY_DA(child);
+ if (!parent->flags.is_root) {
+ fr_strerror_printf("The 'concat' flag can only be used with RFC attributes");
+ goto error;
+ }
- /*
- * We only allocate the pointer array *if* the parent has children.
- */
- if (!parent->children) parent->children = talloc_zero_array(parent, fr_dict_attr_t const *, UINT8_MAX + 1);
- if (!parent->children) return -1;
+ if (flags->array || flags->internal || flags->has_value || flags->virtual ||
+ flags->encrypt || flags->length) {
+ fr_strerror_printf("The 'concat' flag cannot be used any other flag");
+ goto error;
+ }
+ }
/*
- * Treat the array as a hash of 255 bins, with attributes
- * sorted into bins using num % 255.
- *
- * Although the various protocols may define numbers higher than 255:
- *
- * RADIUS/DHCPv4 - 1-255
- * Diameter/Internal - 1-4294967295
- * DHCPv6 - 1-65535
- *
- * In reality very few will ever use attribute numbers > 500, so for
- * the majority of lookups we get O(1) performance.
- *
- * Attributes are inserted into the bin in order of their attribute
- * numbers to allow slightly more efficient lookups.
+ * 'octets[n]' can only be used in a few limited situations.
*/
- bin = &parent->children[child->attr & 0xff];
- for (;;) {
- bool child_is_struct = false;
- bool bin_is_struct = false;
-
- if (!*bin) break;
-
- /*
- * Workaround for vendors that overload the RFC space.
- * Structural attributes always take priority.
- */
- switch (child->type) {
- case FR_TYPE_STRUCTURAL:
- child_is_struct = true;
- break;
+ if (flags->length) {
+ if (flags->has_value || flags->virtual) {
+ fr_strerror_printf("The 'octets[...]' syntax cannot be used any other flag");
+ goto error;
+ }
- default:
- break;
+ if (flags->length > 253) {
+ fr_strerror_printf("Invalid length %d", flags->length);
+ return NULL;
}
- switch ((*bin)->type) {
- case FR_TYPE_STRUCTURAL:
- bin_is_struct = true;
- break;
+ if ((type == FR_TYPE_TLV) || (type == FR_TYPE_VENDOR)) {
+ if ((flags->length != 1) &&
+ (flags->length != 2) &&
+ (flags->length != 4)) {
+ fr_strerror_printf("The 'length' flag can only be used with attributes of TLV lengths of 1,2 or 4");
+ goto error;
+ }
- default:
- break;
+ } else if ((type != FR_TYPE_OCTETS) &&
+ (type != FR_TYPE_STRUCT)) {
+ fr_strerror_printf("The 'length' flag can only be set for attributes of type 'octets' or 'struct'");
+ goto error;
}
- if (child_is_struct && !bin_is_struct) break;
- else if (fr_dict_vendor_num_by_da(child) <= fr_dict_vendor_num_by_da(*bin)) break; /* Prioritise RFC attributes */
- else if (child->attr <= (*bin)->attr) break;
+ if (type == FR_TYPE_STRUCT) {
+ if (flags->type_size != 0) {
+ fr_strerror_printf("Invalid initializer for type_size");
+ goto error;
+ }
- bin = &(*bin)->next;
+ /*
+ * Set maximum length for the struct, and
+ * initialize the current length to be zero.
+ */
+ flags->type_size = flags->length;
+ flags->length = 0;
+ }
}
- memcpy(&this, &bin, sizeof(this));
- child->next = *this;
- *this = child;
-
- return 0;
-}
-
-/** (re)initialize a protocol dictionary
- *
- * Initialize the directory, then fix the attr member of all attributes.
- *
- * First dictionary initialised will be set as the default internal dictionary.
- *
- * @param[in] ctx to allocate the dictionary from.
- * @param[out] out Where to write a pointer to the new dictionary.
- * Will free existing dictionary if files have
- * changed and *out is not NULL.
- * @param[in] dir to read dictionary files from.
- * @param[in] fn file name to read.
- * @param[in] name to use for the root attributes.
- * @return
- * - 0 on success.
- * - -1 on failure.
- */
-int fr_dict_from_file(TALLOC_CTX *ctx, fr_dict_t **out, char const *dir, char const *fn, char const *name)
-{
- static bool defined_cast_types;
- fr_dict_t *dict;
-
- dict = dict_alloc(ctx);
- if (!dict) return -1;
-
- /*
- * Free the old dictionaries
- */
- if (*out == fr_dict_internal) fr_dict_internal = dict;
- TALLOC_FREE(*out);
-
- /*
- * Remove this at some point...
- */
- if (!fr_dict_internal) fr_dict_internal = dict;
-
/*
- * Magic dictionary root attribute
- */
- dict_root_set(dict, name, 0);
-
- /*
- * Add cast attributes. We do it this way,
- * so cast attributes get added automatically for new types.
+ * DHCP options allow for packing multiple values into one option.
*
- * We manually add the attributes to the dictionary, and bypass
- * fr_dict_attr_add(), because we know what we're doing, and
- * that function does too many checks.
+ * We allow it for DHCP and FreeDHCP dictionaries. Not anywhere else.
*/
- if (!defined_cast_types) {
- FR_NAME_NUMBER const *p;
- fr_dict_attr_flags_t flags;
- char *type_name;
-
- memset(&flags, 0, sizeof(flags));
-
- flags.internal = 1;
-
- for (p = dict_attr_types; p->name; p++) {
- fr_dict_attr_t *n;
-
- type_name = talloc_typed_asprintf(dict->pool, "Tmp-Cast-%s", p->name);
+ if (flags->array) {
+ for (v = parent; v != NULL; v = v->parent) {
+ if (v->type != FR_TYPE_VENDOR) continue;
- n = dict_attr_alloc(dict->pool, dict->root, type_name,
- FR_CAST_BASE + p->number, p->number, &flags);
- if (!n) {
- error:
- talloc_free(dict);
- return -1;
+ if ((v->attr != 34673) && /* freedhcp */
+ (v->attr != DHCP_MAGIC_VENDOR)) {
+ fr_strerror_printf("The 'array' flag can only be used with DHCP options");
+ goto error;
}
+ break;
+ }
- if (!fr_hash_table_insert(dict->attributes_by_name, n)) goto error;
+ switch (type) {
+ default:
+ fr_strerror_printf("The 'array' flag cannot be used with attributes of type '%s'",
+ fr_int2str(dict_attr_types, type, "<UNKNOWN>"));
+ goto error;
- /*
- * Set up parenting for the attribute.
- */
- if (dict_attr_child_add(dict->root, n) < 0) goto error;
+ case FR_TYPE_IPV4_ADDR:
+ case FR_TYPE_IPV6_ADDR:
+ case FR_TYPE_UINT8:
+ case FR_TYPE_UINT16:
+ case FR_TYPE_UINT32:
+ case FR_TYPE_DATE:
+ case FR_TYPE_STRING:
+ case FR_TYPE_OCTETS:
+ break;
+ }
- talloc_free(type_name);
+ if (flags->internal || flags->has_value || flags->encrypt || flags->virtual) {
+ fr_strerror_printf("The 'array' flag cannot be used any other flag");
+ goto error;
}
- defined_cast_types = true;
}
- if (dict_from_file(dict, dir, fn, NULL, 0) < 0) goto error;
-
/*
- * Resolve any VALUE aliases (enums) that were defined
- * before the attributes they reference.
+ * 'has_value' should only be set internally. If the
+ * caller sets it, we still sanity check it.
*/
- if (dict->enum_fixup) {
- fr_dict_attr_t const *da;
- dict_enum_fixup_t *this, *next;
+ if (flags->has_value) {
+ if (type != FR_TYPE_UINT32) {
+ fr_strerror_printf("The 'has_value' flag can only be used with attributes "
+ "of type 'integer'");
+ goto error;
+ }
- for (this = dict->enum_fixup; this != NULL; this = next) {
- fr_value_box_t value;
- fr_type_t type;
+ if (flags->encrypt || flags->virtual) {
+ fr_strerror_printf("The 'has_value' flag cannot be used with any other flag");
+ goto error;
+ }
+ }
- next = this->next;
- da = fr_dict_attr_by_name(dict, this->attribute);
- if (!da) {
- fr_strerror_printf("No ATTRIBUTE '%s' defined for VALUE '%s'",
- this->attribute, this->alias);
+ if (flags->encrypt) {
+ /*
+ * Stupid hacks for MS-CHAP-MPPE-Keys. The User-Password
+ * encryption method has no provisions for encoding the
+ * length of the data. For User-Password, the data is
+ * (presumably) all printable non-zero data. For
+ * MS-CHAP-MPPE-Keys, the data is binary crap. So... we
+ * MUST specify a length in the dictionary.
+ */
+ if ((flags->encrypt == FLAG_ENCRYPT_USER_PASSWORD) && (type != FR_TYPE_STRING)) {
+ if (type != FR_TYPE_OCTETS) {
+ fr_strerror_printf("The 'encrypt=1' flag can only be used with "
+ "attributes of type 'string'");
goto error;
}
- type = da->type;
- if (fr_value_box_from_str(this, &value, &type, NULL,
- this->value, talloc_array_length(this->value) - 1, '\0', false) < 0) {
- fr_strerror_printf_push("Invalid VALUE for ATTRIBUTE \"%s\"", da->name);
+ if (flags->length == 0) {
+ fr_strerror_printf("The 'encrypt=1' flag MUST be used with an explicit length for "
+ "'octets' data types");
goto error;
}
-
- if (fr_dict_enum_add_alias(da, this->alias, &value, false, false) < 0) goto error;
-
- /*
- * Just so we don't lose track of things.
- */
- dict->enum_fixup = next;
}
- }
- /*
- * Walk over all of the hash tables to ensure they're
- * initialized. We do this because the threads may perform
- * lookups, and we don't want multi-threaded re-ordering
- * of the table entries. That would be bad.
- */
- fr_hash_table_walk(dict->vendors_by_name, hash_null_callback, NULL);
- fr_hash_table_walk(dict->vendors_by_num, hash_null_callback, NULL);
+ if (flags->encrypt > FLAG_ENCRYPT_OTHER) {
+ fr_strerror_printf("The 'encrypt' flag can only be 0..4");
+ goto error;
+ }
- fr_hash_table_walk(dict->values_by_da, hash_null_callback, NULL);
- fr_hash_table_walk(dict->values_by_alias, hash_null_callback, NULL);
+ /*
+ * The Tunnel-Password encryption method can be used anywhere.
+ *
+ * We forbid User-Password and Ascend-Send-Secret
+ * methods in the extended space.
+ */
+ if ((flags->encrypt != FLAG_ENCRYPT_TUNNEL_PASSWORD) && !flags->internal && !parent->flags.internal) {
+ for (v = parent; v != NULL; v = v->parent) {
+ switch (v->type) {
+ case FR_TYPE_EXTENDED:
+ case FR_TYPE_LONG_EXTENDED:
+ case FR_TYPE_EVS:
+ fr_strerror_printf("The 'encrypt=%d' flag cannot be used with attributes "
+ "of type '%s'", flags->encrypt,
+ fr_int2str(dict_attr_types, type, "<UNKNOWN>"));
+ goto error;
- *out = dict;
+ default:
+ break;
+ }
- return 0;
-}
+ }
+ }
-/** (Re-)Initialize the special internal dictionary
- *
- * This dictionary has additional programatically generated attributes added to it.
- *
- * @param[in] ctx to allocate dictionary in.
- * @param[out] out Where to write pointer to the internal dictionary.
- * @param[in] dir dictionary is located in.
- * @param[in] internal_name name of the internal dictionary dir (may be NULL).
- * @return
- * - 0 on success.
- * - -1 on failure.
- */
-int fr_dict_internal_afrom_file(TALLOC_CTX *ctx, fr_dict_t **out, char const *dir, char const *internal_name)
-{
- fr_dict_t *dict = fr_dict_internal;
- char *dict_dir;
- char *tmp;
- FR_NAME_NUMBER const *p;
- fr_dict_attr_flags_t flags = { .internal = true };
- char *type_name;
+ switch (type) {
+ case FR_TYPE_TLV:
+ if (flags->internal || parent->flags.internal) break;
+ /* FALL-THROUGH */
- memcpy(&tmp, &dir, sizeof(tmp));
- dict_dir = internal_name ? talloc_asprintf(NULL, "%s%c%s", dir, FR_DIR_SEP, internal_name) : tmp;
+ default:
+ encrypt_fail:
+ fr_strerror_printf("The 'encrypt' flag cannot be used with attributes of type '%s'",
+ fr_int2str(dict_attr_types, type, "<UNKNOWN>"));
+ goto error;
- if ((!protocol_by_name || !protocol_by_num) && (dict_global_init(ctx) < 0)) return -1;
+ case FR_TYPE_IPV4_ADDR:
+ case FR_TYPE_UINT32:
+ case FR_TYPE_OCTETS:
+ if (flags->encrypt == FLAG_ENCRYPT_ASCEND_SECRET) goto encrypt_fail;
- if (!dict) {
- dict = dict_alloc(ctx);
- if (!dict) {
- error:
- if (!fr_dict_internal) talloc_free(dict);
- if (internal_name) talloc_free(dict_dir);
- return -1;
+ case FR_TYPE_STRING:
+ break;
}
+ }
- /*
- * Set the root name of the dictionary
- */
- dict_root_set(dict, "internal", 0);
- } else {
- if (dict_stat_check(dict, dir, FR_DICTIONARY_FILE)) {
- if (internal_name) talloc_free(dict_dir);
- return 0;
+ /******************** sanity check data types and parents ********************/
+
+ /*
+ * Enforce restrictions on which data types can appear where.
+ */
+ switch (type) {
+ /*
+ * These types may only be parented from the root of the dictionary
+ */
+ case FR_TYPE_EXTENDED:
+ case FR_TYPE_LONG_EXTENDED:
+// case FR_TYPE_VSA:
+ if (!parent->flags.is_root) {
+ fr_strerror_printf("Attributes of type '%s' can only be used in the RFC space",
+ fr_int2str(dict_attr_types, type, "?Unknown?"));
+ goto error;
}
- }
+ break;
+
/*
- * Add cast attributes. We do it this way,
- * so cast attributes get added automatically for new types.
- *
- * We manually add the attributes to the dictionary, and bypass
- * fr_dict_attr_add(), because we know what we're doing, and
- * that function does too many checks.
+ * EVS may only occur under extended and long extended.
*/
- for (p = dict_attr_types; p->name; p++) {
- fr_dict_attr_t *n;
+ case FR_TYPE_EVS:
+ if ((parent->type != FR_TYPE_EXTENDED) && (parent->type != FR_TYPE_LONG_EXTENDED)) {
+ fr_strerror_printf("Attributes of type 'evs' MUST have a parent of type 'extended', "
+ "instead of '%s'", fr_int2str(dict_attr_types, parent->type, "?Unknown?"));
+ goto error;
+ }
+ break;
- type_name = talloc_typed_asprintf(dict->pool, "Tmp-Cast-%s", p->name);
+ case FR_TYPE_VENDOR:
+ if ((parent->type != FR_TYPE_VSA) && (parent->type != FR_TYPE_EVS)) {
+ fr_strerror_printf("Attributes of type 'vendor' MUST have a parent of type 'vsa' or "
+ "'evs', instead of '%s'",
+ fr_int2str(dict_attr_types, parent->type, "?Unknown?"));
+ goto error;
+ }
- n = dict_attr_alloc(dict->pool, dict->root, type_name,
- FR_CAST_BASE + p->number, p->number, &flags);
- if (!n) goto error;
+ if (parent->type == FR_TYPE_VSA) {
+ fr_dict_vendor_t const *dv;
- if (!fr_hash_table_insert(dict->attributes_by_name, n)) {
- fr_strerror_printf("Failed inserting \"%s\" into internal dictionary", type_name);
- goto error;
+ dv = fr_dict_vendor_by_num(dict, *attr);
+ if (dv) {
+ flags->type_size = dv->type;
+ flags->length = dv->length;
+ } else {
+ flags->type_size = 1;
+ flags->length = 1;
+ }
+ } else {
+ flags->type_size = 1;
+ flags->length = 1;
}
+ break;
+ case FR_TYPE_TLV:
/*
- * Set up parenting for the attribute.
+ * Ensure that type_size and length are set.
*/
- if (dict_attr_child_add(dict->root, n) < 0) goto error;
+ for (v = parent; v != NULL; v = v->parent) {
+ if ((v->type == FR_TYPE_TLV) || (v->type == FR_TYPE_VENDOR)) {
+ break;
+ }
+ }
- talloc_free(type_name);
- }
+ /*
+ * root is always FR_TYPE_TLV, so we're OK.
+ */
+ if (!v) {
+ fr_strerror_printf("Attributes of type '%s' require a parent attribute",
+ fr_int2str(dict_attr_types, type, "?Unknown?"));
+ goto error;
+ }
- if (dict_dir && dict_from_file(dict, dict_dir, FR_DICTIONARY_FILE, NULL, 0) < 0) goto error;
+ /*
+ * Over-ride whatever was there before, so we
+ * don't have multiple formats of VSAs.
+ */
+ flags->type_size = v->flags.type_size;
+ flags->length = v->flags.length;
+ break;
- *out = dict;
- if (!fr_dict_internal) fr_dict_internal = dict;
+ case FR_TYPE_COMBO_IP_ADDR:
+ /*
+ * RFC 6929 says that this is a terrible idea.
+ */
+ for (v = parent; v != NULL; v = v->parent) {
+ if (v->type == FR_TYPE_VSA) {
+ break;
+ }
+ }
- return 0;
-}
+ if (!v) {
+ fr_strerror_printf("Attributes of type '%s' can only be used in VSA dictionaries",
+ fr_int2str(dict_attr_types, type, "?Unknown?"));
+ goto error;
+ }
+ break;
-/** (Re)-initialize a protocol dictionary
- *
- * Initialize the directory, then fix the attr member of all attributes.
- *
- * First dictionary initialised will be set as the default internal dictionary.
- *
- * @param[in] ctx to allocate the dictionary from.
- * @param[out] out Where to write a pointer to the new dictionary. Will free existing
- * dictionary if files have changed and *out is not NULL.
- * @param[in] base_dir containing all the protocol directories.
- * @param[in] proto_name that we're loading the dictionary for.
- * @return
- * - 0 on success.
- * - -1 on failure.
- */
-int fr_dict_protocol_afrom_file(TALLOC_CTX *ctx, fr_dict_t **out,
- char const *base_dir, char const *proto_name)
-{
- fr_dict_t *dict;
- char *dir;
- char *proto_dir;
- char *p;
+ case FR_TYPE_INVALID:
+ case FR_TYPE_TIMEVAL:
+ case FR_TYPE_FLOAT64:
+ case FR_TYPE_COMBO_IP_PREFIX:
+ fr_strerror_printf("Attributes of type '%s' cannot be used in dictionaries",
+ fr_int2str(dict_attr_types, type, "?Unknown?"));
+ goto error;
- if (!protocol_by_name || !protocol_by_num) {
- fr_strerror_printf("Dictionary not yet initialized call fr_dict_internal_afrom_file first");
- return -1;
+ default:
+ break;
}
/*
- * Increment the reference count if the dictionary
- * has already been loaded.
+ * Force "length" for data types of fixed length;
*/
- if (!*out) {
- *out = fr_dict_by_protocol_name(proto_name);
- if (*out) {
- talloc_increase_ref_count(*out);
- return 0;
- }
- }
+ switch (type) {
+ case FR_TYPE_UINT8:
+ case FR_TYPE_BOOL:
+ flags->length = 1;
+ break;
- /*
- * Replace '_' with '/'
- */
- proto_dir = talloc_strdup(ctx, proto_name);
- for (p = proto_dir; *p; p++) if (*p == '_') *p = FR_DIR_SEP;
+ case FR_TYPE_UINT16:
+ flags->length = 2;
+ break;
- dir = talloc_asprintf(proto_dir, "%s%c%s", base_dir, FR_DIR_SEP, proto_dir);
- if (!*out) {
- dict = dict_alloc(ctx);
- if (!dict) {
- error:
- talloc_free(proto_dir);
- return -1;
- }
- } else {
- dict = *out;
- if (dict_stat_check(dict, dir, FR_DICTIONARY_FILE)) return 0;
- }
+ case FR_TYPE_DATE:
+ case FR_TYPE_IPV4_ADDR:
+ case FR_TYPE_UINT32:
+ case FR_TYPE_INT32:
+ flags->length = 4;
+ break;
- dict->enum_fixup = NULL; /* just to be safe. */
+ case FR_TYPE_UINT64:
+ flags->length = 8;
+ break;
- if (dict_from_file(dict, dir, FR_DICTIONARY_FILE, NULL, 0) < 0) goto error;
+ case FR_TYPE_SIZE:
+ flags->length = sizeof(size_t);
+ break;
- talloc_free(proto_dir);
+ case FR_TYPE_ETHERNET:
+ flags->length = 6;
+ break;
- /*
- * Resolve any VALUE aliases (enums) that were defined
- * before the attributes they reference.
- */
- if (dict->enum_fixup) {
- fr_dict_attr_t const *da;
- dict_enum_fixup_t *this, *next;
+ case FR_TYPE_IFID:
+ flags->length = 8;
+ break;
- for (this = dict->enum_fixup; this != NULL; this = next) {
- fr_value_box_t value;
- fr_type_t type;
+ case FR_TYPE_IPV6_ADDR:
+ flags->length = 16;
+ break;
- next = this->next;
- da = fr_dict_attr_by_name(dict, this->attribute);
- if (!da) {
- fr_strerror_printf("No ATTRIBUTE '%s' defined for VALUE '%s'",
- this->attribute, this->alias);
- goto error;
- }
- type = da->type;
+ case FR_TYPE_EXTENDED:
+ if (!parent->flags.is_root || (*attr < 241)) {
+ fr_strerror_printf("Attributes of type 'extended' MUST be "
+ "RFC attributes with value >= 241.");
+ goto error;
+ }
+ flags->length = 0;
+ break;
- if (fr_value_box_from_str(this, &value, &type, NULL,
- this->value, talloc_array_length(this->value) - 1, '\0', false) < 0) {
- fr_strerror_printf_push("Invalid VALUE for ATTRIBUTE \"%s\"", da->name);
- goto error;
- }
+ case FR_TYPE_LONG_EXTENDED:
+ if (!parent->flags.is_root || (*attr < 241)) {
+ fr_strerror_printf("Attributes of type 'long-extended' MUST "
+ "be RFC attributes with value >= 241.");
+ goto error;
+ }
- if (fr_dict_enum_add_alias(da, this->alias, &value, false, false) < 0) goto error;
+ flags->length = 0;
+ break;
- /*
- * Just so we don't lose track of things.
- */
- dict->enum_fixup = next;
+ case FR_TYPE_EVS:
+ if (*attr != FR_VENDOR_SPECIFIC) {
+ fr_strerror_printf("Attributes of type 'evs' MUST have attribute code 26, got %i", *attr);
+ goto error;
}
+
+ flags->length = 0;
+ break;
+
+ /*
+ * The length is calculated from th children, not
+ * input as the flags.
+ */
+ case FR_TYPE_STRUCT:
+ flags->length = 0;
+ break;
+
+ case FR_TYPE_STRING:
+ case FR_TYPE_OCTETS:
+ case FR_TYPE_TLV:
+ break;
+
+ default:
+ break;
}
/*
- * Walk over all of the hash tables to ensure they're
- * initialized. We do this because the threads may perform
- * lookups, and we don't want multi-threaded re-ordering
- * of the table entries. That would be bad.
+ * Validate attribute based on parent.
*/
- fr_hash_table_walk(dict->vendors_by_name, hash_null_callback, NULL);
- fr_hash_table_walk(dict->vendors_by_num, hash_null_callback, NULL);
-
- fr_hash_table_walk(dict->values_by_da, hash_null_callback, NULL);
- fr_hash_table_walk(dict->values_by_alias, hash_null_callback, NULL);
+ if (parent->type == FR_TYPE_STRUCT) {
+ fr_dict_attr_t *mutable;
- *out = dict;
+ /*
+ * STRUCTs will have their length filled in later.
+ */
+ if ((type != FR_TYPE_STRUCT) && (flags->length == 0)) {
+ fr_strerror_printf("Children of 'struct' type attributes MUST have fixed length.");
+ goto error;
+ }
- return 0;
-}
+ if ((*attr > 1) && !parent->flags.length) {
+ fr_strerror_printf("Children of 'struct' type attributes MUST start with sub-attribute 1.");
+ goto error;
+ }
-int fr_dict_read(fr_dict_t *dict, char const *dir, char const *filename)
-{
- INTERNAL_IF_NULL(dict);
+ /*
+ * Sneak in the length of the children.
+ */
+ memcpy(&mutable, &parent, sizeof(mutable));
+ mutable->flags.length += flags->length;
- if (!dict->attributes_by_name) {
- fr_strerror_printf("%s: Must call fr_dict_from_file() before fr_dict_read()", "Error reading dictionary");
- return -1;
+ /*
+ * The struct has a maximum size. Complain if we exceed it.
+ */
+ if (mutable->flags.type_size && (mutable->flags.length > mutable->flags.type_size)) {
+ fr_strerror_printf("Child attribute causes struct to overflow maximum size of %d octets",
+ mutable->flags.type_size);
+ goto error;
+ }
}
- return dict_from_file(dict, dir, filename, NULL, 0);
+ return true;
}
-/** Add a vendor to the dictionary
- *
- * Inserts a vendor entry into the vendor hash table. This must be done before adding
- * attributes under a VSA.
+/** Allocate a dictionary attribute as a pool, and assign a name buffer
*
- * @param[in] dict of protocol context we're operating in.
- * If NULL the internal dictionary will be used.
- * @param[in] name of the vendor.
- * @param[in] num Vendor's Private Enterprise Number.
+ * @param[in] ctx to allocate attribute in.
+ * @param[in] name to set.
* @return
- * - 0 on success.
- * - -1 on failure.
+ * - 0 on success.
+ * - -1 on failure (memory allocation error).
*/
-int fr_dict_vendor_add(fr_dict_t *dict, char const *name, unsigned int num)
+static fr_dict_attr_t *dict_attr_alloc_name(TALLOC_CTX *ctx, char const *name)
{
- INTERNAL_IF_NULL(dict);
- size_t len;
- fr_dict_vendor_t *vendor;
+ fr_dict_attr_t *da;
- len = strlen(name);
- if (len >= FR_DICT_VENDOR_MAX_NAME_LEN) {
- fr_strerror_printf("%s: Vendor name too long", __FUNCTION__);
- return -1;
+ if (!name) {
+ fr_strerror_printf("No attribute name provided");
+ return NULL;
}
#ifdef HAVE_TALLOC_POOLED_OBJECT
- vendor = talloc_pooled_object(dict, fr_dict_vendor_t, 1, strlen(name) + 1);
- memset(vendor, 0, sizeof(*vendor));
+ da = talloc_pooled_object(ctx, fr_dict_attr_t, 1, strlen(name) + 1);
+ memset(da, 0, sizeof(*da));
#else
- vendor = talloc_zero(dict, fr_dict_vendor_t);
+ da = talloc_zero(ctx, fr_dict_attr_t);
#endif
- vendor->name = talloc_typed_strdup(vendor, name);
- if (!vendor->name) {
- talloc_free(vendor);
+ da->name = talloc_typed_strdup(da, name);
+ if (!da->name) {
+ talloc_free(da);
fr_strerror_printf("Out of memory");
- return -1;
+ return NULL;
}
- vendor->vendorpec = num;
- vendor->type = vendor->length = 1; /* defaults */
- if (!fr_hash_table_insert(dict->vendors_by_name, vendor)) {
- fr_dict_vendor_t *old_vendor;
+ return da;
+}
- old_vendor = fr_hash_table_finddata(dict->vendors_by_name, vendor);
- if (!old_vendor) {
- fr_strerror_printf("%s: Failed inserting vendor name %s", __FUNCTION__, name);
- return -1;
- }
- if ((strcmp(old_vendor->name, vendor->name) == 0) && (old_vendor->vendorpec != vendor->vendorpec)) {
- fr_strerror_printf("%s: Duplicate vendor name %s", __FUNCTION__, name);
- return -1;
- }
+/** Initialise fields in a dictionary attribute structure
+ *
+ * @param[in] da to initialise.
+ * @param[in] parent of the attribute, if none, should be
+ * the dictionary root.
+ * @param[in] attr number.
+ * @param[in] type of the attribute.
+ * @param[in] flags to assign.
+ */
+static inline void dict_attr_init(fr_dict_attr_t *da,
+ fr_dict_attr_t const *parent, int attr,
+ fr_type_t type, fr_dict_attr_flags_t const *flags)
+{
+ da->attr = attr;
+ da->type = type;
+ da->flags = *flags;
+ da->parent = parent;
+ da->depth = parent ? parent->depth + 1 : 0;
+}
- /*
- * Already inserted. Discard the duplicate entry.
- */
- talloc_free(vendor);
+/** Allocate a dictionary attribute on the heap
+ *
+ * @param[in] ctx to allocate the attribute in.
+ * @param[in] parent of the attribute, if none, should be
+ * the dictionary root.
+ * @param[in] name of the attribute. If NULL an OID string
+ * will be created and set as the name.
+ * @param[in] attr number.
+ * @param[in] type of the attribute.
+ * @param[in] flags to assign.
+ * @return
+ * - A new fr_dict_attr_t on success.
+ * - NULL on failure.
+ */
+static fr_dict_attr_t *dict_attr_alloc(TALLOC_CTX *ctx,
+ fr_dict_attr_t const *parent,
+ char const *name, int attr,
+ fr_type_t type, fr_dict_attr_flags_t const *flags)
+{
+ fr_dict_attr_t *n;
- return 0;
- }
+ if (!fr_cond_assert(parent)) return NULL;
/*
- * Insert the SAME pointer (not free'd when this table is
- * deleted), into another table.
- *
- * We want this behaviour because we want OLD names for
- * the attributes to be read from the configuration
- * files, but when we're printing them, (and looking up
- * by value) we want to use the NEW name.
+ * Allocate a new attribute
*/
- if (!fr_hash_table_replace(dict->vendors_by_num, vendor)) {
- fr_strerror_printf("%s: Failed inserting vendor %s", __FUNCTION__, name);
- return -1;
+ if (!name) {
+ char buffer[FR_DICT_ATTR_MAX_NAME_LEN + 1];
+ char *p = buffer;
+ size_t len;
+ fr_dict_attr_t tmp;
+
+ memset(&tmp, 0, sizeof(tmp));
+ dict_attr_init(&tmp, parent, attr, type, flags);
+
+ len = snprintf(p, sizeof(buffer), "Attr-");
+ p += len;
+
+ len = fr_dict_print_attr_oid(p, sizeof(buffer) - (p - buffer), NULL, &tmp);
+ if (is_truncated(len, sizeof(buffer) - (p - buffer))) {
+ fr_strerror_printf("OID string too long for unknown attribute");
+ return NULL;
+ }
+
+ n = dict_attr_alloc_name(ctx, buffer);
+ } else {
+ n = dict_attr_alloc_name(ctx, name);
}
- return 0;
+ dict_attr_init(n, parent, attr, type, flags);
+ VERIFY_DA(n);
+
+ return n;
}
-/** Add a protocol to the global protocol table
- *
- * Inserts a protocol into the global protocol table. Uses the root attributes
- * of the dictionary for comparisons.
+/** Copy a an existing attribute
*
- * @param[in] dict of protocol we're inserting.
+ * @param[in] ctx to allocate new attribute in.
+ * @param[in] in attribute to copy.
* @return
- * - 0 on success.
- * - -1 on failure.
+ * - A copy of the input fr_dict_attr_t on success.
+ * - NULL on failure.
*/
-static int dict_protocol_add(fr_dict_t *dict)
+static fr_dict_attr_t *dict_attr_acopy(TALLOC_CTX *ctx, fr_dict_attr_t const *in)
{
- if (!dict->root) return -1; /* Should always have root */
-
- if (!fr_hash_table_insert(protocol_by_name, dict)) {
- fr_dict_t *old_proto;
-
- old_proto = fr_hash_table_finddata(protocol_by_name, dict);
- if (!old_proto) {
- fr_strerror_printf("%s: Failed inserting protocol name %s", __FUNCTION__, dict->root->name);
- return -1;
- }
-
- if ((strcmp(old_proto->root->name, dict->root->name) == 0) &&
- (old_proto->root->name == dict->root->name)) {
- fr_strerror_printf("%s: Duplicate protocol name %s", __FUNCTION__, dict->root->name);
- return -1;
- }
+ fr_dict_attr_t *n;
- return 0;
- }
+ n = dict_attr_alloc_name(ctx, in->name);
+ if (!n) return NULL;
- if (!fr_hash_table_insert(protocol_by_num, dict)) {
- fr_strerror_printf("%s: Duplicate protocol number %i", __FUNCTION__, dict->root->attr);
- return -1;
- }
+ dict_attr_init(n, in->parent, in->attr, in->type, &in->flags);
+ VERIFY_DA(n);
- return 0;
+ return n;
}
-/** Add an attribute to the name table for the dictionary.
- *
- * @todo we need to check length of none vendor attributes.
+/** Allocate a special "reference" attribute
*
* @param[in] dict of protocol context we're operating in.
* If NULL the internal dictionary will be used.
* @param[in] type of attribute.
* @param[in] flags to set in the attribute.
* @return
- * - fr_dict_attr_t on success
- * - NULL on failure
+ * - 0 on success.
+ * - -1 on failure.
*/
-static fr_dict_attr_t *dict_attr_add_by_name(fr_dict_t *dict, fr_dict_attr_t const *parent,
- char const *name, int attr, fr_type_t type, fr_dict_attr_flags_t flags)
+static fr_dict_attr_t *dict_attr_ref_alloc(fr_dict_t *dict, fr_dict_attr_t const *parent,
+ char const *name, int attr, fr_type_t type,
+ fr_dict_attr_flags_t const *flags, fr_dict_attr_t const *ref)
{
- size_t namelen;
- fr_dict_attr_t *n;
- fr_dict_attr_t const *v;
-
- INTERNAL_IF_NULL(dict);
+ fr_dict_attr_ref_t *ref_n;
- VERIFY_DA(parent);
+ if (!name) {
+ fr_strerror_printf("No attribute name provided");
+ return NULL;
+ }
- if (!fr_cond_assert(parent)) return NULL;
+#ifdef HAVE_TALLOC_POOLED_OBJECT
+ ref_n = talloc_pooled_object(dict->pool, fr_dict_attr_ref_t, 1, strlen(name) + 1);
+ memset(ref_n, 0, sizeof(*ref_n));
+#else
+ ref_n = talloc_zero(dict->pool, fr_dict_attr_ref_t);
+#endif
- namelen = strlen(name);
- if (namelen >= FR_DICT_ATTR_MAX_NAME_LEN) {
- fr_strerror_printf("Attribute name too long");
- error:
- fr_strerror_printf_push("fr_dict_attr_add: Failed adding '%s'", name);
+ ref_n->tlv.name = talloc_typed_strdup(ref, name);
+ if (!ref_n->tlv.name) {
+ talloc_free(ref_n);
+ fr_strerror_printf("Out of memory");
return NULL;
}
- if (fr_dict_valid_name(name, -1) < 0) return NULL;
+ dict_attr_init(&ref_n->tlv, parent, attr, type, flags);
+ ref_n->dict = fr_dict_by_da(ref); /* Cache the dictionary */
+ ref_n->to = ref;
- /*
- * type_size is used to limit the maximum attribute number, so it's checked first.
- */
- if (flags.type_size) {
- if ((type != FR_TYPE_TLV) && (type != FR_TYPE_VENDOR)) {
- fr_strerror_printf("The 'format=' flag can only be used with attributes of type 'tlv'");
- goto error;
- }
+ return (fr_dict_attr_t *)ref_n;
+}
- if ((flags.type_size != 1) &&
- (flags.type_size != 2) &&
- (flags.type_size != 4)) {
- fr_strerror_printf("The 'format=' flag can only be used with attributes of type size 1,2 or 4");
- goto error;
- }
- }
+/** Set a new root dictionary attribute
+ *
+ * @note Must only be called once per dictionary.
+ *
+ * @param[in] dict to modify.
+ * @param[in] name of dictionary root.
+ * @param[in] protocol_number The artificial (or IANA allocated) number for the protocol.
+ * This is only used for
+ * @return
+ * - 0 on success.
+ * - -1 on failure.
+ */
+static int dict_root_set(fr_dict_t *dict, char const *name, unsigned int proto_number)
+{
+ fr_dict_attr_flags_t flags = {
+ .is_root = 1,
+ .type_size = 1,
+ .length = 1
+ };
- /******************** sanity check attribute number ********************/
+ if (!fr_cond_assert(!dict->root)) {
+ fr_strerror_printf("Dictionary root already set");
+ return -1;
+ }
- if (parent->flags.is_root) {
- static unsigned int max_attr = UINT8_MAX + 1;
+ dict->root = dict_attr_alloc_name(dict, name);
+ if (!dict->root) return -1;
- if (attr == -1) {
- if (fr_dict_attr_by_name(dict, name)) return 0; /* exists, don't add it again */
- attr = ++max_attr;
- flags.internal = 1;
+ dict_attr_init(dict->root, NULL, proto_number, FR_TYPE_TLV, &flags);
+ VERIFY_DA(dict->root);
- } else if (attr <= 0) {
- fr_strerror_printf("ATTRIBUTE number %i is invalid, must be greater than zero", attr);
- goto error;
+ return 0;
+}
- } else if ((unsigned int) attr > max_attr) {
- max_attr = attr;
- }
+/** Allocate a new dictionary
+ *
+ * @param[in] ctx to allocate dictionary in.
+ * @return
+ * - NULL on memory allocation error.
+ */
+static fr_dict_t *dict_alloc(TALLOC_CTX *ctx)
+{
+ fr_dict_t *dict;
- /*
- * Auto-set internal flags for raddb/dictionary.
- * So that the end user doesn't have to know
- * about internal implementation of the server.
- */
- if ((parent->flags.type_size == 1) &&
- (attr >= 3000) && (attr < 4000)) {
- flags.internal = true;
- }
+ dict = talloc_zero(ctx, fr_dict_t);
+ if (!dict) {
+ error:
+ fr_strerror_printf("Failed allocating memory for dictionary");
+ talloc_free(dict);
+ return NULL;
}
/*
- * Any other negative attribute number is wrong.
+ * Pre-Allocate 5MB of pool memory for rapid startup
*/
- if (attr < 0) {
- fr_strerror_printf("ATTRIBUTE number %i is invalid, must be greater than zero", attr);
- goto error;
- }
+ dict->pool = talloc_pool(dict, (1024 * 1024 * 5));
+ if (!dict->pool) goto error;
/*
- * If attributes have number greater than 255, do sanity checks.
- *
- * We assume that the root attribute is of type TLV, with
- * the appropriate flags set for attributes in this
- * space.
+ * Create the table of vendor by name. There MAY NOT
+ * be multiple vendors of the same name.
*/
- if ((attr > UINT8_MAX) && !flags.internal &&
- !((strncmp("VMPS", name, 4) == 0) || (strncmp("VQP", name, 3) == 0))) { /* Fixme */
- for (v = parent; v != NULL; v = v->parent) {
- if ((v->type == FR_TYPE_TLV) || (v->type == FR_TYPE_VENDOR)) {
- if ((v->flags.type_size < 4) &&
- (attr >= (1 << (8 * v->flags.type_size)))) {
- fr_strerror_printf("Attributes must have value between 1..%u",
- (1 << (8 * v->flags.type_size)) - 1);
- goto error;
- }
- break;
- }
- }
- }
-
- /******************** sanity check flags ********************/
+ dict->vendors_by_name = fr_hash_table_create(dict, dict_vendor_name_hash, dict_vendor_name_cmp, hash_pool_free);
+ if (!dict->vendors_by_name) goto error;
/*
- * virtual attributes are special.
+ * Create the table of vendors by value. There MAY
+ * be vendors of the same value. If there are, we
+ * pick the latest one.
*/
- if (flags.virtual) {
- if (!parent->flags.is_root) {
- fr_strerror_printf("The 'virtual' flag can only be used for normal attributes");
- goto error;
- }
+ dict->vendors_by_num = fr_hash_table_create(dict, dict_vendor_vendorpec_hash, dict_vendor_vendorpec_cmp, NULL);
+ if (!dict->vendors_by_num) goto error;
- if (attr <= (1 << (8 * parent->flags.type_size))) {
- fr_strerror_printf("The 'virtual' flag can only be used for non-protocol attributes");
- goto error;
- }
- }
+ /*
+ * Create the table of attributes by name. There MAY NOT
+ * be multiple attributes of the same name.
+ */
+ dict->attributes_by_name = fr_hash_table_create(dict, dict_attr_name_hash, dict_attr_name_cmp, NULL);
+ if (!dict->attributes_by_name) goto error;
/*
- * Tags can only be used in a few limited situations.
+ * Horrible hacks for combo-IP.
*/
- if (flags.has_tag) {
- if ((type != FR_TYPE_UINT32) && (type != FR_TYPE_STRING)) {
- fr_strerror_printf("The 'has_tag' flag can only be used for attributes of type 'integer' "
- "or 'string'");
- goto error;
- }
+ dict->attributes_combo = fr_hash_table_create(dict, dict_attr_combo_hash, dict_attr_combo_cmp, hash_pool_free);
+ if (!dict->attributes_combo) goto error;
- if (!(parent->flags.is_root ||
- ((parent->type == FR_TYPE_VENDOR) &&
- (parent->parent && parent->parent->type == FR_TYPE_VSA)))) {
- fr_strerror_printf("The 'has_tag' flag can only be used with RFC and VSA attributes");
- goto error;
- }
+ dict->values_by_alias = fr_hash_table_create(dict, dict_enum_alias_hash, dict_enum_alias_cmp, hash_pool_free);
+ if (!dict->values_by_alias) goto error;
- if (flags.array || flags.has_value || flags.concat || flags.virtual ||
- flags.length) {
- fr_strerror_printf("The 'has_tag' flag cannot be used with any other flag");
- goto error;
- }
+ dict->values_by_da = fr_hash_table_create(dict, dict_enum_value_hash, dict_enum_value_cmp, hash_pool_free);
+ if (!dict->values_by_da) goto error;
- if (flags.encrypt && (flags.encrypt != FLAG_ENCRYPT_TUNNEL_PASSWORD)) {
- fr_strerror_printf("The 'has_tag' flag can only be used with 'encrypt=2'");
- goto error;
- }
- }
+ return dict;
+}
+
+/** (re)initialize a protocol dictionary
+ *
+ * Initialize the directory, then fix the attr member of all attributes.
+ *
+ * First dictionary initialised will be set as the default internal dictionary.
+ *
+ * @param[in] ctx to allocate the dictionary from.
+ * @param[out] out Where to write a pointer to the new dictionary.
+ * Will free existing dictionary if files have
+ * changed and *out is not NULL.
+ * @param[in] dir to read dictionary files from.
+ * @param[in] fn file name to read.
+ * @param[in] name to use for the root attributes.
+ * @return
+ * - 0 on success.
+ * - -1 on failure.
+ */
+int fr_dict_from_file(TALLOC_CTX *ctx, fr_dict_t **out, char const *dir, char const *fn, char const *name)
+{
+ static bool defined_cast_types;
+ fr_dict_t *dict;
+
+ dict = dict_alloc(ctx);
+ if (!dict) return -1;
/*
- * 'concat' can only be used in a few limited situations.
+ * Free the old dictionaries
*/
- if (flags.concat) {
- if (type != FR_TYPE_OCTETS) {
- fr_strerror_printf("The 'concat' flag can only be used for attributes of type 'octets'");
- goto error;
- }
+ if (*out == fr_dict_internal) fr_dict_internal = dict;
+ TALLOC_FREE(*out);
- if (!parent->flags.is_root) {
- fr_strerror_printf("The 'concat' flag can only be used with RFC attributes");
- goto error;
- }
+ /*
+ * Remove this at some point...
+ */
+ if (!fr_dict_internal) fr_dict_internal = dict;
- if (flags.array || flags.internal || flags.has_value || flags.virtual ||
- flags.encrypt || flags.length) {
- fr_strerror_printf("The 'concat' flag cannot be used any other flag");
- goto error;
- }
- }
+ /*
+ * Magic dictionary root attribute
+ */
+ dict_root_set(dict, name, 0);
/*
- * 'octets[n]' can only be used in a few limited situations.
+ * Add cast attributes. We do it this way,
+ * so cast attributes get added automatically for new types.
+ *
+ * We manually add the attributes to the dictionary, and bypass
+ * fr_dict_attr_add(), because we know what we're doing, and
+ * that function does too many checks.
*/
- if (flags.length) {
- if (flags.has_value || flags.virtual) {
- fr_strerror_printf("The 'octets[...]' syntax cannot be used any other flag");
- goto error;
- }
+ if (!defined_cast_types) {
+ FR_NAME_NUMBER const *p;
+ fr_dict_attr_flags_t flags;
+ char *type_name;
- if (flags.length > 253) {
- fr_strerror_printf("Invalid length %d", flags.length);
- return NULL;
- }
+ memset(&flags, 0, sizeof(flags));
- if ((type == FR_TYPE_TLV) || (type == FR_TYPE_VENDOR)) {
- if ((flags.length != 1) &&
- (flags.length != 2) &&
- (flags.length != 4)) {
- fr_strerror_printf("The 'length' flag can only be used with attributes of TLV lengths of 1,2 or 4");
- goto error;
- }
+ flags.internal = 1;
- } else if ((type != FR_TYPE_OCTETS) &&
- (type != FR_TYPE_STRUCT)) {
- fr_strerror_printf("The 'length' flag can only be set for attributes of type 'octets' or 'struct'");
- goto error;
- }
+ for (p = dict_attr_types; p->name; p++) {
+ fr_dict_attr_t *n;
- if (type == FR_TYPE_STRUCT) {
- if (flags.type_size != 0) {
- fr_strerror_printf("Invalid initializer for type_size");
- goto error;
+ type_name = talloc_typed_asprintf(dict->pool, "Tmp-Cast-%s", p->name);
+
+ n = dict_attr_alloc(dict->pool, dict->root, type_name,
+ FR_CAST_BASE + p->number, p->number, &flags);
+ if (!n) {
+ error:
+ talloc_free(dict);
+ return -1;
}
+ if (!fr_hash_table_insert(dict->attributes_by_name, n)) goto error;
+
/*
- * Set maximum length for the struct, and
- * initialize the current length to be zero.
+ * Set up parenting for the attribute.
*/
- flags.type_size = flags.length;
- flags.length = 0;
+ if (dict_attr_child_add(dict->root, n) < 0) goto error;
+
+ talloc_free(type_name);
}
+ defined_cast_types = true;
}
+ if (dict_from_file(dict, dir, fn, NULL, 0) < 0) goto error;
+
/*
- * DHCP options allow for packing multiple values into one option.
- *
- * We allow it for DHCP and FreeDHCP dictionaries. Not anywhere else.
+ * Resolve any VALUE aliases (enums) that were defined
+ * before the attributes they reference.
*/
- if (flags.array) {
- for (v = parent; v != NULL; v = v->parent) {
- if (v->type != FR_TYPE_VENDOR) continue;
+ if (dict->enum_fixup) {
+ fr_dict_attr_t const *da;
+ dict_enum_fixup_t *this, *next;
- if ((v->attr != 34673) && /* freedhcp */
- (v->attr != DHCP_MAGIC_VENDOR)) {
- fr_strerror_printf("The 'array' flag can only be used with DHCP options");
+ for (this = dict->enum_fixup; this != NULL; this = next) {
+ fr_value_box_t value;
+ fr_type_t type;
+
+ next = this->next;
+ da = fr_dict_attr_by_name(dict, this->attribute);
+ if (!da) {
+ fr_strerror_printf("No ATTRIBUTE '%s' defined for VALUE '%s'",
+ this->attribute, this->alias);
goto error;
}
- break;
- }
+ type = da->type;
- switch (type) {
- default:
- fr_strerror_printf("The 'array' flag cannot be used with attributes of type '%s'",
- fr_int2str(dict_attr_types, type, "<UNKNOWN>"));
- goto error;
+ if (fr_value_box_from_str(this, &value, &type, NULL,
+ this->value, talloc_array_length(this->value) - 1, '\0', false) < 0) {
+ fr_strerror_printf_push("Invalid VALUE for ATTRIBUTE \"%s\"", da->name);
+ goto error;
+ }
- case FR_TYPE_IPV4_ADDR:
- case FR_TYPE_IPV6_ADDR:
- case FR_TYPE_UINT8:
- case FR_TYPE_UINT16:
- case FR_TYPE_UINT32:
- case FR_TYPE_DATE:
- case FR_TYPE_STRING:
- case FR_TYPE_OCTETS:
- break;
- }
+ if (fr_dict_enum_add_alias(da, this->alias, &value, false, false) < 0) goto error;
- if (flags.internal || flags.has_value || flags.encrypt || flags.virtual) {
- fr_strerror_printf("The 'array' flag cannot be used any other flag");
- goto error;
+ /*
+ * Just so we don't lose track of things.
+ */
+ dict->enum_fixup = next;
}
}
/*
- * 'has_value' should only be set internally. If the
- * caller sets it, we still sanity check it.
+ * Walk over all of the hash tables to ensure they're
+ * initialized. We do this because the threads may perform
+ * lookups, and we don't want multi-threaded re-ordering
+ * of the table entries. That would be bad.
*/
- if (flags.has_value) {
- if (type != FR_TYPE_UINT32) {
- fr_strerror_printf("The 'has_value' flag can only be used with attributes "
- "of type 'integer'");
- goto error;
- }
+ fr_hash_table_walk(dict->vendors_by_name, hash_null_callback, NULL);
+ fr_hash_table_walk(dict->vendors_by_num, hash_null_callback, NULL);
- if (flags.encrypt || flags.virtual) {
- fr_strerror_printf("The 'has_value' flag cannot be used with any other flag");
- goto error;
+ fr_hash_table_walk(dict->values_by_da, hash_null_callback, NULL);
+ fr_hash_table_walk(dict->values_by_alias, hash_null_callback, NULL);
+
+ *out = dict;
+
+ return 0;
+}
+
+/** (Re-)Initialize the special internal dictionary
+ *
+ * This dictionary has additional programatically generated attributes added to it.
+ *
+ * @param[in] ctx to allocate dictionary in.
+ * @param[out] out Where to write pointer to the internal dictionary.
+ * @param[in] dir dictionary is located in.
+ * @param[in] internal_name name of the internal dictionary dir (may be NULL).
+ * @return
+ * - 0 on success.
+ * - -1 on failure.
+ */
+int fr_dict_internal_afrom_file(TALLOC_CTX *ctx, fr_dict_t **out, char const *dir, char const *internal_name)
+{
+ fr_dict_t *dict = fr_dict_internal;
+ char *dict_dir;
+ char *tmp;
+ FR_NAME_NUMBER const *p;
+ fr_dict_attr_flags_t flags = { .internal = true };
+ char *type_name;
+
+ memcpy(&tmp, &dir, sizeof(tmp));
+ dict_dir = internal_name ? talloc_asprintf(NULL, "%s%c%s", dir, FR_DIR_SEP, internal_name) : tmp;
+
+ if ((!protocol_by_name || !protocol_by_num) && (dict_global_init(ctx) < 0)) return -1;
+
+ if (!dict) {
+ dict = dict_alloc(ctx);
+ if (!dict) {
+ error:
+ if (!fr_dict_internal) talloc_free(dict);
+ if (internal_name) talloc_free(dict_dir);
+ return -1;
}
- }
- if (flags.encrypt) {
/*
- * Stupid hacks for MS-CHAP-MPPE-Keys. The User-Password
- * encryption method has no provisions for encoding the
- * length of the data. For User-Password, the data is
- * (presumably) all printable non-zero data. For
- * MS-CHAP-MPPE-Keys, the data is binary crap. So... we
- * MUST specify a length in the dictionary.
+ * Set the root name of the dictionary
*/
- if ((flags.encrypt == FLAG_ENCRYPT_USER_PASSWORD) && (type != FR_TYPE_STRING)) {
- if (type != FR_TYPE_OCTETS) {
- fr_strerror_printf("The 'encrypt=1' flag can only be used with "
- "attributes of type 'string'");
- goto error;
- }
-
- if (flags.length == 0) {
- fr_strerror_printf("The 'encrypt=1' flag MUST be used with an explicit length for "
- "'octets' data types");
- goto error;
- }
+ dict_root_set(dict, "internal", 0);
+ } else {
+ if (dict_stat_check(dict, dir, FR_DICTIONARY_FILE)) {
+ if (internal_name) talloc_free(dict_dir);
+ return 0;
}
+ }
+ /*
+ * Add cast attributes. We do it this way,
+ * so cast attributes get added automatically for new types.
+ *
+ * We manually add the attributes to the dictionary, and bypass
+ * fr_dict_attr_add(), because we know what we're doing, and
+ * that function does too many checks.
+ */
+ for (p = dict_attr_types; p->name; p++) {
+ fr_dict_attr_t *n;
- if (flags.encrypt > FLAG_ENCRYPT_OTHER) {
- fr_strerror_printf("The 'encrypt' flag can only be 0..4");
+ type_name = talloc_typed_asprintf(dict->pool, "Tmp-Cast-%s", p->name);
+
+ n = dict_attr_alloc(dict->pool, dict->root, type_name,
+ FR_CAST_BASE + p->number, p->number, &flags);
+ if (!n) goto error;
+
+ if (!fr_hash_table_insert(dict->attributes_by_name, n)) {
+ fr_strerror_printf("Failed inserting \"%s\" into internal dictionary", type_name);
goto error;
}
/*
- * The Tunnel-Password encryption method can be used anywhere.
- *
- * We forbid User-Password and Ascend-Send-Secret
- * methods in the extended space.
+ * Set up parenting for the attribute.
*/
- if ((flags.encrypt != FLAG_ENCRYPT_TUNNEL_PASSWORD) && !flags.internal && !parent->flags.internal) {
- for (v = parent; v != NULL; v = v->parent) {
- switch (v->type) {
- case FR_TYPE_EXTENDED:
- case FR_TYPE_LONG_EXTENDED:
- case FR_TYPE_EVS:
- fr_strerror_printf("The 'encrypt=%d' flag cannot be used with attributes "
- "of type '%s'", flags.encrypt,
- fr_int2str(dict_attr_types, type, "<UNKNOWN>"));
- goto error;
+ if (dict_attr_child_add(dict->root, n) < 0) goto error;
- default:
- break;
- }
+ talloc_free(type_name);
+ }
- }
- }
+ if (dict_dir && dict_from_file(dict, dict_dir, FR_DICTIONARY_FILE, NULL, 0) < 0) goto error;
- switch (type) {
- case FR_TYPE_TLV:
- if (flags.internal || parent->flags.internal) break;
- /* FALL-THROUGH */
+ *out = dict;
+ if (!fr_dict_internal) fr_dict_internal = dict;
- default:
- encrypt_fail:
- fr_strerror_printf("The 'encrypt' flag cannot be used with attributes of type '%s'",
- fr_int2str(dict_attr_types, type, "<UNKNOWN>"));
- goto error;
+ return 0;
+}
- case FR_TYPE_IPV4_ADDR:
- case FR_TYPE_UINT32:
- case FR_TYPE_OCTETS:
- if (flags.encrypt == FLAG_ENCRYPT_ASCEND_SECRET) goto encrypt_fail;
+/** (Re)-initialize a protocol dictionary
+ *
+ * Initialize the directory, then fix the attr member of all attributes.
+ *
+ * First dictionary initialised will be set as the default internal dictionary.
+ *
+ * @param[in] ctx to allocate the dictionary from.
+ * @param[out] out Where to write a pointer to the new dictionary. Will free existing
+ * dictionary if files have changed and *out is not NULL.
+ * @param[in] base_dir containing all the protocol directories.
+ * @param[in] proto_name that we're loading the dictionary for.
+ * @return
+ * - 0 on success.
+ * - -1 on failure.
+ */
+int fr_dict_protocol_afrom_file(TALLOC_CTX *ctx, fr_dict_t **out,
+ char const *base_dir, char const *proto_name)
+{
+ fr_dict_t *dict;
+ char *dir;
+ char *proto_dir;
+ char *p;
- case FR_TYPE_STRING:
- break;
- }
+ if (!protocol_by_name || !protocol_by_num) {
+ fr_strerror_printf("Dictionary not yet initialized call fr_dict_internal_afrom_file first");
+ return -1;
}
- /******************** sanity check data types and parents ********************/
-
- /*
- * Enforce restrictions on which data types can appear where.
- */
- switch (type) {
/*
- * These types may only be parented from the root of the dictionary
+ * Increment the reference count if the dictionary
+ * has already been loaded.
*/
- case FR_TYPE_EXTENDED:
- case FR_TYPE_LONG_EXTENDED:
-// case FR_TYPE_VSA:
- if (!parent->flags.is_root) {
- fr_strerror_printf("Attributes of type '%s' can only be used in the RFC space",
- fr_int2str(dict_attr_types, type, "?Unknown?"));
- goto error;
+ if (!*out) {
+ *out = fr_dict_by_protocol_name(proto_name);
+ if (*out) {
+ talloc_increase_ref_count(*out);
+ return 0;
}
- break;
+ }
/*
- * EVS may only occur under extended and long extended.
+ * Replace '_' with '/'
*/
- case FR_TYPE_EVS:
- if ((parent->type != FR_TYPE_EXTENDED) && (parent->type != FR_TYPE_LONG_EXTENDED)) {
- fr_strerror_printf("Attributes of type 'evs' MUST have a parent of type 'extended', "
- "instead of '%s'", fr_int2str(dict_attr_types, parent->type, "?Unknown?"));
- goto error;
- }
- break;
+ proto_dir = talloc_strdup(ctx, proto_name);
+ for (p = proto_dir; *p; p++) if (*p == '_') *p = FR_DIR_SEP;
- case FR_TYPE_VENDOR:
- if ((parent->type != FR_TYPE_VSA) && (parent->type != FR_TYPE_EVS)) {
- fr_strerror_printf("Attributes of type 'vendor' MUST have a parent of type 'vsa' or "
- "'evs', instead of '%s'",
- fr_int2str(dict_attr_types, parent->type, "?Unknown?"));
- goto error;
+ dir = talloc_asprintf(proto_dir, "%s%c%s", base_dir, FR_DIR_SEP, proto_dir);
+ if (!*out) {
+ dict = dict_alloc(ctx);
+ if (!dict) {
+ error:
+ talloc_free(proto_dir);
+ return -1;
}
+ } else {
+ dict = *out;
+ if (dict_stat_check(dict, dir, FR_DICTIONARY_FILE)) return 0;
+ }
- if (parent->type == FR_TYPE_VSA) {
- fr_dict_vendor_t const *dv;
+ dict->enum_fixup = NULL; /* just to be safe. */
- dv = fr_dict_vendor_by_num(dict, attr);
- if (dv) {
- flags.type_size = dv->type;
- flags.length = dv->length;
- } else {
- flags.type_size = 1;
- flags.length = 1;
- }
- } else {
- flags.type_size = 1;
- flags.length = 1;
- }
- break;
+ if (dict_from_file(dict, dir, FR_DICTIONARY_FILE, NULL, 0) < 0) goto error;
- case FR_TYPE_TLV:
- /*
- * Ensure that type_size and length are set.
- */
- for (v = parent; v != NULL; v = v->parent) {
- if ((v->type == FR_TYPE_TLV) || (v->type == FR_TYPE_VENDOR)) {
- break;
- }
- }
+ talloc_free(proto_dir);
- /*
- * root is always FR_TYPE_TLV, so we're OK.
- */
- if (!v) {
- fr_strerror_printf("Attributes of type '%s' require a parent attribute",
- fr_int2str(dict_attr_types, type, "?Unknown?"));
- goto error;
- }
+ /*
+ * Resolve any VALUE aliases (enums) that were defined
+ * before the attributes they reference.
+ */
+ if (dict->enum_fixup) {
+ fr_dict_attr_t const *da;
+ dict_enum_fixup_t *this, *next;
- /*
- * Over-ride whatever was there before, so we
- * don't have multiple formats of VSAs.
- */
- flags.type_size = v->flags.type_size;
- flags.length = v->flags.length;
- break;
+ for (this = dict->enum_fixup; this != NULL; this = next) {
+ fr_value_box_t value;
+ fr_type_t type;
- case FR_TYPE_COMBO_IP_ADDR:
- /*
- * RFC 6929 says that this is a terrible idea.
- */
- for (v = parent; v != NULL; v = v->parent) {
- if (v->type == FR_TYPE_VSA) {
- break;
+ next = this->next;
+ da = fr_dict_attr_by_name(dict, this->attribute);
+ if (!da) {
+ fr_strerror_printf("No ATTRIBUTE '%s' defined for VALUE '%s'",
+ this->attribute, this->alias);
+ goto error;
}
- }
+ type = da->type;
- if (!v) {
- fr_strerror_printf("Attributes of type '%s' can only be used in VSA dictionaries",
- fr_int2str(dict_attr_types, type, "?Unknown?"));
- goto error;
- }
- break;
+ if (fr_value_box_from_str(this, &value, &type, NULL,
+ this->value, talloc_array_length(this->value) - 1, '\0', false) < 0) {
+ fr_strerror_printf_push("Invalid VALUE for ATTRIBUTE \"%s\"", da->name);
+ goto error;
+ }
- case FR_TYPE_INVALID:
- case FR_TYPE_TIMEVAL:
- case FR_TYPE_FLOAT64:
- case FR_TYPE_COMBO_IP_PREFIX:
- fr_strerror_printf("Attributes of type '%s' cannot be used in dictionaries",
- fr_int2str(dict_attr_types, type, "?Unknown?"));
- goto error;
+ if (fr_dict_enum_add_alias(da, this->alias, &value, false, false) < 0) goto error;
- default:
- break;
+ /*
+ * Just so we don't lose track of things.
+ */
+ dict->enum_fixup = next;
+ }
}
/*
- * Force "length" for data types of fixed length;
+ * Walk over all of the hash tables to ensure they're
+ * initialized. We do this because the threads may perform
+ * lookups, and we don't want multi-threaded re-ordering
+ * of the table entries. That would be bad.
*/
- switch (type) {
- case FR_TYPE_UINT8:
- case FR_TYPE_BOOL:
- flags.length = 1;
- break;
+ fr_hash_table_walk(dict->vendors_by_name, hash_null_callback, NULL);
+ fr_hash_table_walk(dict->vendors_by_num, hash_null_callback, NULL);
- case FR_TYPE_UINT16:
- flags.length = 2;
- break;
+ fr_hash_table_walk(dict->values_by_da, hash_null_callback, NULL);
+ fr_hash_table_walk(dict->values_by_alias, hash_null_callback, NULL);
- case FR_TYPE_DATE:
- case FR_TYPE_IPV4_ADDR:
- case FR_TYPE_UINT32:
- case FR_TYPE_INT32:
- flags.length = 4;
- break;
+ *out = dict;
- case FR_TYPE_UINT64:
- flags.length = 8;
- break;
+ return 0;
+}
- case FR_TYPE_SIZE:
- flags.length = sizeof(size_t);
- break;
+/** Read supplementary attribute definitions into an existing dictionary
+ *
+ * @param[in] dict Existing dictionary.
+ * @param[in] dir dictionary is located in.
+ * @param[in] filename of the dictionary.
+ */
+int fr_dict_read(fr_dict_t *dict, char const *dir, char const *filename)
+{
+ INTERNAL_IF_NULL(dict);
- case FR_TYPE_ETHERNET:
- flags.length = 6;
- break;
+ if (!dict->attributes_by_name) {
+ fr_strerror_printf("%s: Must call fr_dict_from_file() before fr_dict_read()", __FUNCTION__);
+ return -1;
+ }
- case FR_TYPE_IFID:
- flags.length = 8;
- break;
+ return dict_from_file(dict, dir, filename, NULL, 0);
+}
- case FR_TYPE_IPV6_ADDR:
- flags.length = 16;
- break;
+/** Add a protocol to the global protocol table
+ *
+ * Inserts a protocol into the global protocol table. Uses the root attributes
+ * of the dictionary for comparisons.
+ *
+ * @param[in] dict of protocol we're inserting.
+ * @return
+ * - 0 on success.
+ * - -1 on failure.
+ */
+static int dict_protocol_add(fr_dict_t *dict)
+{
+ if (!dict->root) return -1; /* Should always have root */
- case FR_TYPE_EXTENDED:
- if (!parent->flags.is_root || (attr < 241)) {
- fr_strerror_printf("Attributes of type 'extended' MUST be "
- "RFC attributes with value >= 241.");
- goto error;
+ if (!fr_hash_table_insert(protocol_by_name, dict)) {
+ fr_dict_t *old_proto;
+
+ old_proto = fr_hash_table_finddata(protocol_by_name, dict);
+ if (!old_proto) {
+ fr_strerror_printf("%s: Failed inserting protocol name %s", __FUNCTION__, dict->root->name);
+ return -1;
}
- flags.length = 0;
- break;
- case FR_TYPE_LONG_EXTENDED:
- if (!parent->flags.is_root || (attr < 241)) {
- fr_strerror_printf("Attributes of type 'long-extended' MUST "
- "be RFC attributes with value >= 241.");
- goto error;
+ if ((strcmp(old_proto->root->name, dict->root->name) == 0) &&
+ (old_proto->root->name == dict->root->name)) {
+ fr_strerror_printf("%s: Duplicate protocol name %s", __FUNCTION__, dict->root->name);
+ return -1;
}
- flags.length = 0;
- break;
+ return 0;
+ }
- case FR_TYPE_EVS:
- if (attr != FR_VENDOR_SPECIFIC) {
- fr_strerror_printf("Attributes of type 'evs' MUST have attribute code 26, got %i", attr);
- goto error;
- }
+ if (!fr_hash_table_insert(protocol_by_num, dict)) {
+ fr_strerror_printf("%s: Duplicate protocol number %i", __FUNCTION__, dict->root->attr);
+ return -1;
+ }
- flags.length = 0;
- break;
+ return 0;
+}
+
+/** Add a vendor to the dictionary
+ *
+ * Inserts a vendor entry into the vendor hash table. This must be done before adding
+ * attributes under a VSA.
+ *
+ * @param[in] dict of protocol context we're operating in.
+ * If NULL the internal dictionary will be used.
+ * @param[in] name of the vendor.
+ * @param[in] num Vendor's Private Enterprise Number.
+ * @return
+ * - 0 on success.
+ * - -1 on failure.
+ */
+static int dict_vendor_add(fr_dict_t *dict, char const *name, unsigned int num)
+{
+ INTERNAL_IF_NULL(dict);
+ size_t len;
+ fr_dict_vendor_t *vendor;
+
+ len = strlen(name);
+ if (len >= FR_DICT_VENDOR_MAX_NAME_LEN) {
+ fr_strerror_printf("%s: Vendor name too long", __FUNCTION__);
+ return -1;
+ }
+
+#ifdef HAVE_TALLOC_POOLED_OBJECT
+ vendor = talloc_pooled_object(dict, fr_dict_vendor_t, 1, strlen(name) + 1);
+ memset(vendor, 0, sizeof(*vendor));
+#else
+ vendor = talloc_zero(dict, fr_dict_vendor_t);
+#endif
+
+ vendor->name = talloc_typed_strdup(vendor, name);
+ if (!vendor->name) {
+ talloc_free(vendor);
+ fr_strerror_printf("Out of memory");
+ return -1;
+ }
+ vendor->vendorpec = num;
+ vendor->type = vendor->length = 1; /* defaults */
+
+ if (!fr_hash_table_insert(dict->vendors_by_name, vendor)) {
+ fr_dict_vendor_t *old_vendor;
+
+ old_vendor = fr_hash_table_finddata(dict->vendors_by_name, vendor);
+ if (!old_vendor) {
+ fr_strerror_printf("%s: Failed inserting vendor name %s", __FUNCTION__, name);
+ return -1;
+ }
+ if ((strcmp(old_vendor->name, vendor->name) == 0) && (old_vendor->vendorpec != vendor->vendorpec)) {
+ fr_strerror_printf("%s: Duplicate vendor name %s", __FUNCTION__, name);
+ return -1;
+ }
/*
- * The length is calculated from th children, not
- * input as the flags.
+ * Already inserted. Discard the duplicate entry.
*/
- case FR_TYPE_STRUCT:
- flags.length = 0;
- break;
+ talloc_free(vendor);
- case FR_TYPE_STRING:
- case FR_TYPE_OCTETS:
- case FR_TYPE_TLV:
- break;
+ return 0;
+ }
- default:
- break;
+ /*
+ * Insert the SAME pointer (not free'd when this table is
+ * deleted), into another table.
+ *
+ * We want this behaviour because we want OLD names for
+ * the attributes to be read from the configuration
+ * files, but when we're printing them, (and looking up
+ * by value) we want to use the NEW name.
+ */
+ if (!fr_hash_table_replace(dict->vendors_by_num, vendor)) {
+ fr_strerror_printf("%s: Failed inserting vendor %s", __FUNCTION__, name);
+ return -1;
}
+ return 0;
+}
+
+/** Add a child to a parent.
+ *
+ * @param[in] parent we're adding a child to.
+ * @param[in] child to add to parent.
+ * @return
+ * - 0 on success.
+ * - -1 on failure (memory allocation error).
+ */
+static inline int dict_attr_child_add(fr_dict_attr_t *parent, fr_dict_attr_t *child)
+{
+ fr_dict_attr_t const * const *bin;
+ fr_dict_attr_t **this;
+
/*
- * Validate attribute based on parent.
+ * Setup fields in the child
+ */
+ child->parent = parent;
+ child->depth = parent->depth + 1;
+
+ VERIFY_DA(child);
+
+ /*
+ * We only allocate the pointer array *if* the parent has children.
+ */
+ if (!parent->children) parent->children = talloc_zero_array(parent, fr_dict_attr_t const *, UINT8_MAX + 1);
+ if (!parent->children) return -1;
+
+ /*
+ * Treat the array as a hash of 255 bins, with attributes
+ * sorted into bins using num % 255.
+ *
+ * Although the various protocols may define numbers higher than 255:
+ *
+ * RADIUS/DHCPv4 - 1-255
+ * Diameter/Internal - 1-4294967295
+ * DHCPv6 - 1-65535
+ *
+ * In reality very few will ever use attribute numbers > 500, so for
+ * the majority of lookups we get O(1) performance.
+ *
+ * Attributes are inserted into the bin in order of their attribute
+ * numbers to allow slightly more efficient lookups.
*/
- if (parent->type == FR_TYPE_STRUCT) {
- fr_dict_attr_t *mutable;
+ bin = &parent->children[child->attr & 0xff];
+ for (;;) {
+ bool child_is_struct = false;
+ bool bin_is_struct = false;
+
+ if (!*bin) break;
/*
- * STRUCTs will have their length filled in later.
+ * Workaround for vendors that overload the RFC space.
+ * Structural attributes always take priority.
*/
- if ((type != FR_TYPE_STRUCT) && (flags.length == 0)) {
- fr_strerror_printf("Children of 'struct' type attributes MUST have fixed length.");
- goto error;
- }
+ switch (child->type) {
+ case FR_TYPE_STRUCTURAL:
+ child_is_struct = true;
+ break;
- if ((attr > 1) && !parent->flags.length) {
- fr_strerror_printf("Children of 'struct' type attributes MUST start with sub-attribute 1.");
- goto error;
+ default:
+ break;
}
- /*
- * Sneak in the length of the children.
- */
- memcpy(&mutable, &parent, sizeof(mutable));
- mutable->flags.length += flags.length;
+ switch ((*bin)->type) {
+ case FR_TYPE_STRUCTURAL:
+ bin_is_struct = true;
+ break;
- /*
- * The struct has a maximum size. Complain if we exceed it.
- */
- if (mutable->flags.type_size && (mutable->flags.length > mutable->flags.type_size)) {
- fr_strerror_printf("Child attribute causes struct to overflow maximum size of %d octets",
- mutable->flags.type_size);
- goto error;
+ default:
+ break;
}
- }
- n = dict_attr_alloc(dict->pool, parent, name, attr, type, &flags);
- if (!n) {
- fr_strerror_printf("Out of memory");
- goto error;
+ if (child_is_struct && !bin_is_struct) break;
+ else if (fr_dict_vendor_num_by_da(child) <= fr_dict_vendor_num_by_da(*bin)) break; /* Prioritise RFC attributes */
+ else if (child->attr <= (*bin)->attr) break;
+
+ bin = &(*bin)->next;
}
+ memcpy(&this, &bin, sizeof(this));
+ child->next = *this;
+ *this = child;
+
+ return 0;
+}
+
+/** Add an attribute to the name table for the dictionary.
+ *
+ * @param[in] dict of protocol context we're operating in.
+ * If NULL the internal dictionary will be used.
+ * @param[in] parent to add attribute under.
+ * @param[in] name of the attribute.
+ * @param[in] attr number.
+ * @param[in] type of attribute.
+ * @param[in] flags to set in the attribute.
+ * @return
+ * - 0 on success.
+ * - -1 on failure.
+ */
+static int dict_attr_add_by_name(fr_dict_t *dict, fr_dict_attr_t *da)
+{
/*
* Insert the attribute, only if it's not a duplicate.
*/
- if (!fr_hash_table_insert(dict->attributes_by_name, n)) {
+ if (!fr_hash_table_insert(dict->attributes_by_name, da)) {
fr_dict_attr_t *a;
/*
* error out. We don't allow duplicate attribute
* definitions.
*/
- a = fr_hash_table_finddata(dict->attributes_by_name, n);
- if (a && (strcasecmp(a->name, n->name) == 0)) {
- if ((a->attr != n->attr) || (a->parent != n->parent)) {
+ a = fr_hash_table_finddata(dict->attributes_by_name, da);
+ if (a && (strcasecmp(a->name, da->name) == 0)) {
+ if ((a->attr != da->attr) || (a->parent != da->parent)) {
fr_strerror_printf("Duplicate attribute name");
- talloc_free(n);
- goto error;
+ error:
+ return -1;
}
}
* dictionary but entry in the name hash table is
* updated to point to the new definition.
*/
- if (!fr_hash_table_replace(dict->attributes_by_name, n)) {
+ if (!fr_hash_table_replace(dict->attributes_by_name, da)) {
fr_strerror_printf("Internal error storing attribute");
- talloc_free(n);
goto error;
}
}
/*
- * Hacks for combo-IP
+ * Insert copies of the attribute into the
+ * polymorphic attribute table.
+ *
+ * This allows an abstract attribute type
+ * like combo IP to be resolved to a
+ * concrete one later.
*/
- if (n->type == FR_TYPE_COMBO_IP_ADDR) {
+ switch (da->type) {
+ case FR_TYPE_COMBO_IP_ADDR:
+ {
fr_dict_attr_t *v4, *v6;
- v4 = dict_attr_acopy(dict->pool, n);
- if (!v4) {
- talloc_free(n);
+ v4 = dict_attr_acopy(dict->pool, da);
+ if (!v4) goto error;
+ v4->type = FR_TYPE_IPV4_ADDR;
+
+ v6 = dict_attr_acopy(dict->pool, da);
+ if (!v6) goto error;
+ v6->type = FR_TYPE_IPV6_ADDR;
+
+ if (!fr_hash_table_replace(dict->attributes_combo, v4)) {
+ fr_strerror_printf("Failed inserting IPv4 version of combo attribute");
goto error;
}
- v6 = dict_attr_acopy(dict->pool, n);
- if (!v6) {
- talloc_free(n);
+ if (!fr_hash_table_replace(dict->attributes_combo, v6)) {
+ fr_strerror_printf("Failed inserting IPv6 version of combo attribute");
goto error;
}
- v4->type = FR_TYPE_IPV4_ADDR;
+ }
+
+ case FR_TYPE_COMBO_IP_PREFIX:
+ {
+ fr_dict_attr_t *v4, *v6;
+
+ v4 = dict_attr_acopy(dict->pool, da);
+ if (!v4) goto error;
+ v4->type = FR_TYPE_IPV4_PREFIX;
+
+ v6 = dict_attr_acopy(dict->pool, da);
+ if (!v6) goto error;
+ v6->type = FR_TYPE_IPV6_PREFIX;
if (!fr_hash_table_replace(dict->attributes_combo, v4)) {
fr_strerror_printf("Failed inserting IPv4 version of combo attribute");
goto error;
}
}
+ default:
+ break;
+ }
- return n;
+ return 0;
}
-/** Add an attribute to the dictionary
+
+/** Add an reference to the dictionary
*
- * @todo we need to check length of none vendor attributes.
+ * @param[in] dict of protocol context we're operating in.
+ * If NULL the internal dictionary will be used.
+ * @param[in] parent to add attribute under.
+ * @param[in] name of the attribute.
+ * @param[in] attr number.
+ * @param[in] type of attribute.
+ * @param[in] flags to set in the attribute.
+ * @param[in] ref The attribute we're referencing. May be in a foreign
+ * dictionary.
+ * @return
+ * - 0 on success.
+ * - -1 on failure.
+ */
+static int dict_attr_ref_add(fr_dict_t *dict, fr_dict_attr_t const *parent,
+ char const *name, int attr, fr_type_t type, fr_dict_attr_flags_t const *flags,
+ fr_dict_attr_t const *ref)
+{
+ fr_dict_attr_t *n;
+ fr_dict_attr_t *mutable;
+ fr_dict_attr_flags_t our_flags = *flags;
+
+ /*
+ * Check it's valid
+ */
+ if (!dict_attr_fields_valid(dict, parent, name, &attr, type, &our_flags)) return -1;
+
+ /*
+ * Check we're not creating a direct loop
+ */
+ if (ref->flags.is_reference) {
+ fr_dict_attr_ref_t const *to_ref = talloc_get_type_abort_const(ref, fr_dict_attr_ref_t);
+
+ if (to_ref->to == ref) {
+ fr_strerror_printf("Circular reference between \"%s\" and \"%s\"", name, ref->name);
+ return -1;
+ }
+ }
+
+ /*
+ * Check the referenced attribute is a root
+ * or a TLV attribute.
+ */
+ if (!ref->flags.is_root && (ref->type != FR_TYPE_TLV)) {
+ fr_strerror_printf("Referenced attribute \"%s\" is not a TLV", ref->name);
+ return -1;
+ }
+
+ n = dict_attr_ref_alloc(dict->pool, parent, name, attr, type, &our_flags, ref);
+ if (!n) return -1;
+
+ if (dict_attr_add_by_name(dict, n) < 0) {
+ error:
+ talloc_free(n);
+ return -1;
+ }
+
+ /*
+ * Setup parenting for the attribute
+ */
+ memcpy(&mutable, &parent, sizeof(mutable));
+
+ /*
+ * Add in by number
+ */
+ if (dict_attr_child_add(mutable, n) < 0) goto error;
+
+ return 0;
+}
+
+/** Add an attribute to the dictionary
*
* @param[in] dict of protocol context we're operating in.
* If NULL the internal dictionary will be used.
* - -1 on failure.
*/
int fr_dict_attr_add(fr_dict_t *dict, fr_dict_attr_t const *parent,
- char const *name, int attr, fr_type_t type, fr_dict_attr_flags_t flags)
+ char const *name, int attr, fr_type_t type, fr_dict_attr_flags_t const *flags)
{
- fr_dict_attr_t *n;
- fr_dict_attr_t *mutable;
+ fr_dict_attr_t *n;
+ fr_dict_attr_t *mutable;
+ fr_dict_attr_flags_t our_flags = *flags;
+
+ /*
+ * Check it's valid
+ */
+ if (!dict_attr_fields_valid(dict, parent, name, &attr, type, &our_flags)) return -1;
- n = dict_attr_add_by_name(dict, parent, name, attr, type, flags);
+ n = dict_attr_alloc(dict->pool, parent, name, attr, type, &our_flags);
if (!n) return -1;
+ if (dict_attr_add_by_name(dict, n) < 0) {
+ error:
+ talloc_free(n);
+ return -1;
+ }
+
/*
* Setup parenting for the attribute
*/
memcpy(&mutable, &parent, sizeof(mutable));
- if (dict_attr_child_add(mutable, n) < 0) return -1;
+ /*
+ * Add in by number
+ */
+ if (dict_attr_child_add(mutable, n) < 0) goto error;
return 0;
}
fr_dict_attr_t const *parent; //!< Current parent attribute (root/vendor/tlv).
} dict_from_file_ctx_t;
+/** Lookup a dictionary reference
+ *
+ * Format is [<proto>].[<attr>]
+ *
+ * If protocol is omitted lookup is in the current dictionary.
+ *
+ * FIXME: Probably needs the dictionary equivalent of pass2, to fixup circular dependencies
+ * DHCPv4->RADIUS and RADIUS->DHCPv4 are both valid.
+ *
+ * @param[in] dict The current dictionary we're parsing.
+ * @param[in,out] ref The reference string. Pointer advanced to the end of the string.
+ * @return
+ * - NULL if the reference is invalid.
+ * - A local or foreign attribute representing the target of the reference.
+ */
+static fr_dict_attr_t const *dict_resolve_reference(fr_dict_t *dict, char const *ref)
+{
+ char const *p = ref, *q, *end = p + strlen(ref);
+ fr_dict_t *proto_dict;
+ fr_dict_attr_t const *da;
+
+ /*
+ * If the reference does not begin with .
+ * then it's a reference into a foreign
+ * protocol.
+ */
+ if (*p != '.') {
+ char buffer[FR_DICT_PROTO_MAX_NAME_LEN + 1];
+
+ q = strchr(p, '.');
+ if (!q) q = end;
+
+ if ((size_t)(q - p) > sizeof(buffer)) {
+ fr_strerror_printf("Protocol name too long");
+ return NULL;
+ }
+
+ strlcpy(buffer, p, (q - p + 1));
+ p = q;
+
+ dict = fr_dict_by_protocol_name(buffer);
+ if (!dict) {
+ fr_strerror_printf("Referenced protocol \"%s\" not found", buffer);
+ return NULL;
+ }
+
+ return NULL;
+ /*
+ * If the reference string begins with .
+ * then the reference is in the current
+ * dictionary.
+ */
+ } else {
+ proto_dict = dict;
+ }
+
+ /*
+ * If there's a '.' after the dictionary, then
+ * the reference is to a specific attribute.
+ */
+ if (*p == '.') {
+ p++;
+
+ da = fr_dict_attr_by_name_substr(proto_dict, &p);
+ if (!da) {
+ fr_strerror_printf("Referenced attribute \"%s\" not found", p);
+ return NULL;
+ }
+ }
+
+ da = fr_dict_root(proto_dict);
+ if (!da) {
+ fr_strerror_printf("Dictionary missing attribute root");
+ return NULL;
+ }
+
+ return da;
+}
+
/*
* Process the ATTRIBUTE command
*/
int type;
unsigned int length;
fr_dict_attr_flags_t flags;
+ fr_dict_attr_t const *ref = NULL;
char *p;
if ((argc < 3) || (argc > 4)) {
oid = true;
slen = fr_dict_attr_by_oid(dict, &parent, &attr, argv[1]);
- if (slen <= 0) {
- return -1;
- }
+ if (slen <= 0) return -1;
if (!fr_cond_assert(parent)) return -1; /* Should have provided us with a parent */
}
return -1;
}
- *q = 0;
+ *q = '\0';
if (!dict_read_sscanf_i(&length, p + 1)) {
fr_strerror_printf("Invalid length for '%s[...]'", argv[2]);
* Parse options.
*/
if (argc >= 4) {
- char *key, *next, *last;
+ char *q, *v;
- key = argv[3];
+ p = argv[3];
do {
- next = strchr(key, ',');
- if (next) *(next++) = '\0';
+ char key[64], value[256];
+
+ q = strchr(p, ',');
+ if (!q) q = p + strlen(p);
+
+ /*
+ * Nothing after the trailing comma
+ */
+ if (p == q) break;
+
+ if ((size_t)(q - p) > sizeof(key)) {
+ fr_strerror_printf("ATTRIBUTE option key too long");
+ return -1;
+ }
+
+ /*
+ * Copy key and value
+ */
+ if (!(v = memchr(p, '=', q - p)) || (v == q)) {
+ value[0] = '\0';
+ strlcpy(key, p, (q - p) + 1);
+ } else {
+ strlcpy(key, p, (v - p) + 1);
+ strlcpy(value, v + 1, q - v);
+ }
/*
* Boolean flag, means this is a tagged
* attribute.
*/
- if ((strcmp(key, "has_tag") == 0) || (strcmp(key, "has_tag=1") == 0)) {
+ if (strcmp(key, "has_tag") == 0) {
flags.has_tag = 1;
/*
* Encryption method.
*/
- } else if (strncmp(key, "encrypt=", 8) == 0) {
- flags.encrypt = strtol(key + 8, &last, 0);
- if (*last) {
- fr_strerror_printf("Invalid option %s", key);
+ } else if (strcmp(key, "encrypt") == 0) {
+ char *qq;
+
+ flags.encrypt = strtol(value, &qq, 0);
+ if (*qq) {
+ fr_strerror_printf("Invalid encrypt value \"%s\"", value);
return -1;
}
- /*
- * Marks the attribute up as internal.
- * This means it can use numbers outside of the allowed
- * protocol range, and also means it will not be included
- * in replies or proxy requests.
- */
- } else if (strncmp(key, "internal", 9) == 0) {
+ /*
+ * Marks the attribute up as internal.
+ * This means it can use numbers outside of the allowed
+ * protocol range, and also means it will not be included
+ * in replies or proxy requests.
+ */
+ } else if (strcmp(key, "internal") == 0) {
flags.internal = 1;
- } else if (strncmp(key, "array", 6) == 0) {
+ } else if (strcmp(key, "array") == 0) {
flags.array = 1;
- } else if (strncmp(key, "concat", 7) == 0) {
+ } else if (strcmp(key, "concat") == 0) {
flags.concat = 1;
- } else if (strncmp(key, "virtual", 8) == 0) {
+ } else if (strcmp(key, "virtual") == 0) {
flags.virtual = 1;
+ } else if (strcmp(key, "reference") == 0) {
+ ref = dict_resolve_reference(dict, value);
+ if (!ref) return -1;
+ flags.is_reference = 1;
+
/*
- * The only thing is the vendor name,
- * and it's a known name: allow it.
+ * The only thing is the vendor name, and it's a known name:
+ * allow it.
*/
- } else if ((key == argv[3]) && !next) {
+ } else if ((argv[3] == p) && (*q == '\0')) {
if (oid) {
fr_strerror_printf("ATTRIBUTE cannot use a 'vendor' flag");
return -1;
fr_strerror_printf("Unknown option '%s'", key);
return -1;
}
-
- key = next;
- if (key && !*key) break;
- } while (key);
+ p = q;
+ } while (*p++);
}
#ifdef WITH_DICTIONARY_WARNINGS
#endif
/*
- * Add it in.
+ * Add in a normal attribute
+ */
+ if (!ref) {
+ if (fr_dict_attr_add(dict, parent, argv[0], attr, type, &flags) < 0) return -1;
+ /*
+ * Add in a special reference attribute
*/
- if (fr_dict_attr_add(dict, parent, argv[0], attr, type, flags) < 0) return -1;
+ } else {
+ if (dict_attr_ref_add(dict, parent, argv[0], attr, type, &flags, ref) < 0) return -1;
+ }
return 0;
}
*/
static int dict_read_process_named_attribute(fr_dict_t *dict, fr_dict_attr_t const *parent,
char **argv, int argc,
- fr_dict_attr_flags_t *base_flags)
+ fr_dict_attr_flags_t const *base_flags)
{
int type;
unsigned int attr;
/*
* Add it in.
*/
- if (fr_dict_attr_add(dict, parent, argv[0], attr, type, *base_flags) < 0) {
- return -1;
- }
+ if (fr_dict_attr_add(dict, parent, argv[0], attr, type, base_flags) < 0) return -1;
return 0;
}
}
/* Create a new VENDOR entry for the list */
- if (fr_dict_vendor_add(dict, argv[0], value) < 0) {
- return -1;
- }
+ if (dict_vendor_add(dict, argv[0], value) < 0) return -1;
/*
* Look for a format statement. Allow it to over-ride the hard-coded formats below.
*/
if (argc == 3) {
- if (dict_read_parse_format(argv[2], &value, &type, &length, &continuation) < 0) {
- return -1;
- }
+ if (dict_read_parse_format(argv[2], &value, &type, &length, &continuation) < 0) return -1;
} else if (value == VENDORPEC_USR) { /* catch dictionary screw-ups */
type = 4;
if (old->type == FR_TYPE_VENDOR) {
fr_dict_attr_t *mutable, *n;
- if (fr_dict_vendor_add(dict, old->name, old->attr) < 0) return NULL;
+ if (dict_vendor_add(dict, old->name, old->attr) < 0) return NULL;
n = dict_attr_alloc(dict->pool, parent, old->name, old->attr, old->type, &flags);
*/
da = fr_dict_attr_child_by_num(parent, old->attr);
if (da) {
+ fr_dict_attr_t *n;
+
+ n = dict_attr_alloc(dict->pool, parent, old->name, old->attr, old->type, &flags);
+ if (!n) return NULL;
+
/*
* Add the unknown by NAME. e.g. if the admin does "Attr-26", we want
* to return "Attr-26", and NOT "Vendor-Specific". The rest of the server
* is responsible for converting "Attr-26 = 0x..." to an actual attribute,
* if it so desires.
*/
- return dict_attr_add_by_name(dict, parent, old->name, old->attr, old->type, flags);
+ if (dict_attr_add_by_name(dict, n) < 0) {
+ talloc_free(n);
+ return NULL;
+ }
+
+ return n;
}
/*
* Add the attribute by both name and number.
*/
- if (fr_dict_attr_add(dict, parent, old->name, old->attr, old->type, flags) < 0) return NULL;
+ if (fr_dict_attr_add(dict, parent, old->name, old->attr, old->type, &flags) < 0) return NULL;
/*
* For paranoia, return it by name.
end = p + len;
do {
- if (!fr_dict_attr_allowed_chars[*p]) {
+ if (!fr_dict_attr_allowed_chars[(int)*p]) {
char buff[5];
fr_snprint(buff, sizeof(buff), (char const *)p, 1, '\'');