]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Add generic extensions for different dictionary structures
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Mon, 19 Oct 2020 18:07:35 +0000 (13:07 -0500)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Tue, 20 Oct 2020 01:12:11 +0000 (20:12 -0500)
src/lib/util/dict.h
src/lib/util/dict_ext.c
src/lib/util/dict_ext.h [new file with mode: 0644]
src/lib/util/dict_ext_priv.h [new file with mode: 0644]
src/lib/util/dict_priv.h
src/lib/util/dict_unknown.c
src/lib/util/dict_util.c
src/lib/util/ext.c [new file with mode: 0644]
src/lib/util/ext.h [new file with mode: 0644]
src/lib/util/libfreeradius-util.mk
src/lib/util/talloc.h

index 68c0726749afdbb319799a5619b48f22bc84db51..0d9b70292543b08434c211f6c99da7b8b52a8d54 100644 (file)
@@ -21,6 +21,7 @@
  *
  * @copyright 2015 The FreeRADIUS server project
  */
+RCSIDH(dict_h, "$Id$")
 
 #ifdef __cplusplus
 extern "C" {
@@ -29,6 +30,7 @@ extern "C" {
 #include <freeradius-devel/build.h>
 #include <freeradius-devel/missing.h>
 #include <freeradius-devel/util/dl.h>
+#include <freeradius-devel/util/ext.h>
 #include <freeradius-devel/util/rbtree.h>
 #include <freeradius-devel/util/sbuff.h>
 #include <freeradius-devel/util/table.h>
@@ -37,7 +39,6 @@ extern "C" {
 #include <stdbool.h>
 #include <stdint.h>
 #include <talloc.h>
-#include <limits.h>
 
 /*
  *     Avoid circular type references.
@@ -114,7 +115,7 @@ extern const size_t dict_attr_sizes[FR_TYPE_MAX + 1][2];
 
 /** Extension identifier
  *
- * @note New extension structures should also be added to the #fr_dict_ext_length_min table in dict_ext.c
+ * @note New extension structures should also be added to the #fr_dict_attr_ext_info table in dict_ext.c
  */
 typedef enum {
        FR_DICT_ATTR_EXT_NAME = 0,                              //!< Name of the attribute.
@@ -128,72 +129,6 @@ typedef enum {
        FR_DICT_ATTR_EXT_MAX
 } fr_dict_attr_ext_t;
 
-/** The alignment of object extension structures
- *
- */
-#ifdef __WORD_SIZE
-#  if __WORD_SIZE < 4
-#    define FR_DICT_ATTR_EXT_ALIGNMENT sizeof(uint32_t)
-#  else
-#    define FR_DICT_ATTR_EXT_ALIGNMENT __WORD_SIZE             /* From limits.h */
-#  endif
-#else
-#  define FR_DICT_ATTR_EXT_ALIGNMENT   sizeof(uint64_t)
-#endif
-
-/** Attribute extension - Holds children for an attribute
- *
- * Children are possible for:
- *
- * #FR_TYPE_TLV, #FR_TYPE_VENDOR, #FR_TYPE_VSA, #FR_TYPE_STRUCT
- *
- * *or* where the parent->parent->type is
- * #FR_TYPE_STRUCT, and "parent" is a "key"
- * field.  Note that these attributes therefore
- * cannot have VALUEs, as the child defines their
- * VALUE.  See dict_attr_can_have_children() for details.
- */
-typedef struct {
-       fr_hash_table_t         *child_by_name;                 //!< Namespace at this level in the hierarchy.
-       fr_dict_attr_t const    **children;                     //!< Children of this attribute.
-} fr_dict_attr_ext_children_t;
-
-/** Attribute extension - Holds a reference to an attribute in another dictionary
- *
- */
-typedef struct {
-       fr_dict_attr_t const    *ref;                           //!< reference, only for #FR_TYPE_GROUP
-} fr_dict_attr_ext_ref_t;
-
-/** Attribute extension - Cached vendor pointer
- *
- */
-typedef struct {
-       fr_dict_attr_t const    *vendor;                        //!< ancestor which has type #FR_TYPE_VENDOR
-} fr_dict_attr_ext_vendor_t;
-
-/** Attribute extension - Stack of dictionary attributes that describe the path back to the root of the dictionary
- *
- */
-typedef struct {
-       fr_dict_attr_t const    *da_stack[0];                   //!< Stack of dictionary attributes
-} fr_dict_attr_ext_da_stack_t;
-
-/** Attribute extension - Holds enumeration values
- *
- */
-typedef struct {
-       fr_hash_table_t         *value_by_name;                 //!< Lookup an enumeration value by name
-       fr_hash_table_t         *name_by_value;                 //!< Lookup a name by value
-} fr_dict_attr_ext_enumv_t;
-
-/** Attribute extension - Protocol-specific
- *
- */
-typedef struct {
-       void                    *uctx;                          //!< Protocol specific extensions
-} fr_dict_attr_ext_protocol_specific_t;
-
 /** Dictionary attribute
  */
 struct dict_attr_s {
@@ -214,7 +149,16 @@ struct dict_attr_s {
        fr_dict_attr_flags_t    flags;                          //!< Flags.
 
        uint8_t                 ext[FR_DICT_ATTR_EXT_MAX];      //!< Extensions to the dictionary attribute.
-} CC_HINT(aligned(FR_DICT_ATTR_EXT_ALIGNMENT));
+} CC_HINT(aligned(FR_EXT_ALIGNMENT));
+
+/** Extension identifier
+ *
+ * @note New extension structures should also be added to the #fr_dict_ext_length_min table in dict_ext.c
+ */
+typedef enum {
+       FR_DICT_ENUM_EXT_UNION_REF = 0,                         //!< Reference to a union/subs-struct.
+       FR_DICT_ENUM_EXT_MAX
+} fr_dict_enum_ext_t;
 
 /** Value of an enumerated attribute
  *
@@ -226,8 +170,10 @@ typedef struct {
                                                                ///< on partial buffers.
        fr_value_box_t const    *value;                         //!< Enum value (what name maps to).
 
+       uint8_t                 ext[FR_DICT_ENUM_EXT_MAX];      //!< Extensions to the dictionary attribute.
+
        fr_dict_attr_t const    *child_struct[0];               //!< for key fields
-} fr_dict_enum_t;
+} fr_dict_enum_t CC_HINT(aligned(FR_EXT_ALIGNMENT));
 
 /** Private enterprise
  *
@@ -335,76 +281,11 @@ typedef struct fr_dict_gctx_s fr_dict_gctx_t;
 extern bool const      fr_dict_attr_allowed_chars[UINT8_MAX + 1];
 extern bool const      fr_dict_non_data_types[FR_TYPE_MAX + 1];
 
-/** @name Add extension structures to attributes
+/** @name Dictionary structure extensions
  *
  * @{
  */
-
-/** Return a pointer to the specified extension structure
- */
-#define DICT_EXT_OFFSET(_ptr, _ext) ((void *)(((_ptr)->ext[_ext] * FR_DICT_ATTR_EXT_ALIGNMENT) + ((uintptr_t)(_ptr))))
-
-/* Retrieve an extension structure for a dictionary attribute
- *
- * @param[in] da       to retrieve structure from.
- * @param[in] ext      to retrieve.
- * @return
- *     - NULL if the extension wasn't found.
- *     - A pointer to the start of the extension.
- */
-static inline void *fr_dict_attr_ext(fr_dict_attr_t const *da, fr_dict_attr_ext_t ext)
-{
-       if (!da->ext[ext]) return NULL;
-
-       return DICT_EXT_OFFSET(da, ext);
-}
-
-/** Return whether a da has a given extension or not
- *
- * @param[in] da       to check for extensions.
- * @param[in] ext      to check.
- * @return
- *      - true if the da has the specified extension.
- *     - false if the da does not have the specified extension
- */
-static inline bool fr_dict_attr_has_ext(fr_dict_attr_t const *da, fr_dict_attr_ext_t ext)
-{
-       return (da->ext[ext] > 0);
-}
-
-/** Return the cached da stack (if any) associated with an attribute
- *
- * @param[in] da       to return cached da stack for.
- * @return
- *     - NULL if no da stack available.
- *     - The cached da stack on success.
- */
-static inline fr_dict_attr_t const **fr_dict_attr_da_stack(fr_dict_attr_t const *da)
-{
-       fr_dict_attr_ext_da_stack_t *ext;
-
-       ext = fr_dict_attr_ext(da, FR_DICT_ATTR_EXT_DA_STACK);
-       if (!ext) return NULL;
-
-       return ext->da_stack;
-}
-
-/** Return the reference associated with a group type attribute
- *
- * @param[in] da       to return the reference for.
- * @return
- *     - NULL if no reference available.
- *     - A pointer to the attribute being referenced.
- */
-static inline fr_dict_attr_t const *fr_dict_attr_ref(fr_dict_attr_t const *da)
-{
-       fr_dict_attr_ext_ref_t *ext;
-
-       ext = fr_dict_attr_ext(da, FR_DICT_ATTR_EXT_REF);
-       if (!ext) return NULL;
-
-       return ext->ref;
-}
+#include <freeradius-devel/util/dict_ext.h>
 /** @} */
 
 /** @name Programatically create dictionary attributes and values
@@ -421,6 +302,7 @@ int                 fr_dict_attr_enum_add_name_next(fr_dict_attr_t *da, char const *name) CC_H
 
 int                    fr_dict_str_to_argv(char *str, char **argv, int max_argc);
 /** @} */
+
 /** @name Unknown ephemeral attributes
  *
  * @{
@@ -508,6 +390,8 @@ ssize_t                     fr_dict_attr_by_oid(fr_dict_t const *dict, fr_dict_attr_t const **pare
  */
 fr_dict_attr_t const   *fr_dict_root(fr_dict_t const *dict);
 
+bool                   fr_dict_is_read_only(fr_dict_t const *dict);
+
 ssize_t                        fr_dict_by_protocol_substr(fr_dict_attr_err_t *err,
                                                   fr_dict_t const **out, fr_sbuff_t *name, fr_dict_t const *dict_def);
 
@@ -533,46 +417,6 @@ static inline bool fr_dict_attr_is_top_level(fr_dict_attr_t const *da)
        return true;
 }
 
-/** Return the vendor number for an attribute
- *
- * @param[in] da               The dictionary attribute to find the
- *                             vendor for.
- * @return
- *     - 0 this isn't a vendor specific attribute.
- *     - The vendor PEN.
- */
-static inline uint32_t fr_dict_vendor_num_by_da(fr_dict_attr_t const *da)
-{
-       fr_dict_attr_ext_vendor_t *ext;
-
-       if (da->type == FR_TYPE_VENDOR) return da->attr;
-
-       ext = fr_dict_attr_ext(da, FR_DICT_ATTR_EXT_VENDOR);
-       if (!ext || !ext->vendor) return 0;
-
-       return ext->vendor->attr;
-}
-
-/** Return the vendor da for an attribute
- *
- * @param[in] da               The dictionary attribute to find the
- *                             vendor for.
- * @return
- *     - 0 this isn't a vendor specific attribute.
- *     - The vendor PEN.
- */
-static inline fr_dict_attr_t const *fr_dict_vendor_da_by_da(fr_dict_attr_t const *da)
-{
-       fr_dict_attr_ext_vendor_t *ext;
-
-       if (da->type == FR_TYPE_VENDOR) return da;
-
-       ext = fr_dict_attr_ext(da, FR_DICT_ATTR_EXT_VENDOR);
-       if (!ext) return NULL;
-
-       return ext->vendor;
-}
-
 fr_dict_vendor_t const *fr_dict_vendor_by_da(fr_dict_attr_t const *da);
 
 fr_dict_vendor_t const *fr_dict_vendor_by_name(fr_dict_t const *dict, char const *name);
index 4aa586e6439a5c21c90d7a6e51caa31ef91f3fd5..cea3a39a1056406a09b0ab6628b875d345870c00 100644 (file)
 RCSID("$Id$")
 
 #include <freeradius-devel/util/dict_priv.h>
+#include <freeradius-devel/util/dict_ext_priv.h>
 #include <freeradius-devel/util/debug.h>
 #include <freeradius-devel/util/table.h>
 #include <freeradius-devel/util/talloc.h>
 
-/** Copy function for fixing up extensions after they're copy
- *
- */
-typedef void *(* fr_dict_attr_ext_copy_t)(TALLOC_CTX *ctx, fr_dict_attr_t **da_out_p,
-                                         fr_dict_attr_ext_t ext, void *ext_ptr, size_t ext_len);
-
-/** Additional information for a given extension
- */
-typedef struct {
-       size_t                          min;            //!< Minimum size of extension.
-       bool                            has_hdr;        //!< Has additional metadata allocated before
-                                                       ///< the extension data.
-       bool                            can_copy;       //!< Copying this extension between attributes is allowed.
-       fr_dict_attr_ext_copy_t         copy;           //!< Override the normal copy operation with a callback.
-} fr_dict_attr_ext_info_t;
-
-static void *fr_dict_attr_ext_enumv_copy(TALLOC_CTX *ctx, fr_dict_attr_t **da_p,
-                                        fr_dict_attr_ext_t ext, void *ext_ptr, size_t ext_len);
-
-
-static void *fr_dict_attr_ext_vendor_copy(TALLOC_CTX *ctx, fr_dict_attr_t **da_p,
-                                         fr_dict_attr_ext_t ext, void *ext_ptr, UNUSED size_t ext_len);
-
 static fr_table_num_ordered_t const dict_attr_ext_table[] = {
        { L("name"),            FR_DICT_ATTR_EXT_NAME           },
        { L("children"),        FR_DICT_ATTR_EXT_CHILDREN       },
@@ -61,61 +39,18 @@ static fr_table_num_ordered_t const dict_attr_ext_table[] = {
 };
 static size_t dict_attr_ext_table_len = NUM_ELEMENTS(dict_attr_ext_table);
 
-/** Holds additional information about extension structures
- *
- */
-static fr_dict_attr_ext_info_t const fr_dict_ext_info[] = {
-       [FR_DICT_ATTR_EXT_NAME]         = {
-                                               .min = sizeof(char),
-                                               .has_hdr = true,
-                                               .can_copy = false,      /* Name may change, and we can only set it once */
-                                       },
-       [FR_DICT_ATTR_EXT_CHILDREN]     = {
-                                               .min = sizeof(fr_dict_attr_ext_children_t),
-                                               .can_copy = false,      /* Limitation in hashing scheme we use */
-                                       },
-       [FR_DICT_ATTR_EXT_REF]          = {
-                                               .min = sizeof(fr_dict_attr_ext_ref_t),
-                                               .can_copy = true,
-                                       },
-       [FR_DICT_ATTR_EXT_VENDOR]       = {
-                                               .min = sizeof(fr_dict_attr_ext_vendor_t),
-                                               .can_copy = true,
-                                               .copy = fr_dict_attr_ext_vendor_copy
-                                       },
-       [FR_DICT_ATTR_EXT_ENUMV]        = {
-                                               .min = sizeof(fr_dict_attr_ext_enumv_t),
-                                               .can_copy = true,
-                                               .copy = fr_dict_attr_ext_enumv_copy
-                                       },
-       [FR_DICT_ATTR_EXT_DA_STACK]     = {
-                                               .min = sizeof(fr_dict_attr_ext_da_stack_t),
-                                               .has_hdr = true,
-                                               .can_copy = false       /* Reinitialised for each new attribute */
-                                       },
-       [FR_DICT_ATTR_EXT_MAX]          = {}
-};
-
-/** Optional extension header struct
- *
- */
-typedef struct {
-       size_t                  len;            //!< Length of extension data.
-       uint8_t                 data[];         //!< Extension data
-} CC_HINT(aligned(FR_DICT_ATTR_EXT_ALIGNMENT)) dict_attr_ext_hdr_t;
-
 /** Copy all enumeration values from one attribute to another
  *
  */
-static void *fr_dict_attr_ext_enumv_copy(TALLOC_CTX *ctx, fr_dict_attr_t **da_p,
-                                        fr_dict_attr_ext_t ext, void *ext_ptr, UNUSED size_t ext_len)
+static void *fr_dict_attr_ext_enumv_copy(void **chunk_p, int ext, void *ext_ptr, UNUSED size_t ext_len)
 {
+       fr_dict_attr_t                  **da_p = (fr_dict_attr_t **)chunk_p;
        fr_dict_attr_ext_enumv_t        *new_ext, *old_ext;
        fr_hash_iter_t                  iter;
        fr_dict_enum_t                  *enumv;
        bool                            has_child = da_is_key_field(*da_p);
 
-       new_ext = dict_attr_ext_alloc(ctx, da_p, ext);
+       new_ext = dict_attr_ext_alloc(da_p, ext);
        if (!new_ext) return NULL;
 
        old_ext = ext_ptr;
@@ -154,9 +89,9 @@ static void *fr_dict_attr_ext_enumv_copy(TALLOC_CTX *ctx, fr_dict_attr_t **da_p,
 /** Rediscover the parent of this attribute, and cache it
  *
  */
-static void *fr_dict_attr_ext_vendor_copy(TALLOC_CTX *ctx, fr_dict_attr_t **da_p,
-                                         fr_dict_attr_ext_t ext, void *ext_ptr, UNUSED size_t ext_len)
+static void *fr_dict_attr_ext_vendor_copy(void **chunk_p, int ext, void *ext_ptr, UNUSED size_t ext_len)
 {
+       fr_dict_attr_t                  **da_p = (fr_dict_attr_t **)chunk_p;
        fr_dict_attr_ext_vendor_t       *new_ext, *old_ext = ext_ptr;
        fr_dict_attr_t const            **da_stack;
        fr_dict_attr_t const            *old_vendor = old_ext->vendor;
@@ -171,7 +106,7 @@ static void *fr_dict_attr_ext_vendor_copy(TALLOC_CTX *ctx, fr_dict_attr_t **da_p
        if (da_stack) {
                new_vendor = da_stack[old_vendor->depth];
                if ((new_vendor->type == old_vendor->type) && (new_vendor->attr == old_vendor->attr)) {
-                       new_ext = dict_attr_ext_alloc(ctx, da_p, ext);
+                       new_ext = dict_attr_ext_alloc(da_p, ext);
                        if (!new_ext) return NULL;
 
                        new_ext->vendor = new_vendor;
@@ -188,7 +123,7 @@ static void *fr_dict_attr_ext_vendor_copy(TALLOC_CTX *ctx, fr_dict_attr_t **da_p
         */
        for (da = (*da_p)->parent; da; da = da->parent) {
                if ((da->type == old_vendor->type) && (da->attr == old_vendor->attr)) {
-                       new_ext = dict_attr_ext_alloc(ctx, da_p, ext);
+                       new_ext = dict_attr_ext_alloc(da_p, ext);
                        if (!new_ext) return NULL;
 
                        new_ext->vendor = da;
@@ -199,231 +134,70 @@ static void *fr_dict_attr_ext_vendor_copy(TALLOC_CTX *ctx, fr_dict_attr_t **da_p
        return NULL;
 }
 
-/** Add a variable length extension to a dictionary attribute
- *
- * Extensions are appended to the existing #fr_dict_attr_t memory chunk
- * using realloc.
- *
- * When a new extension is allocated it will not be initialised.
- *
- * @param[in] ctx      the dict attr was originally allocated in.
- * @param[in,out] da_p The dictionary attribute to add an extension for.
- *                     Under certain circumstances the value of *da_p will
- *                     be changed to point to a new memory block.
- *                     All cached copied of the previous pointer should be
- *                     updated.  This means that attributes that have
- *                     already been added to a dictionary should not have
- *                     extensions allocated unless care is taken to update
- *                     all references.
- * @param[in] ext      to alloc.
- * @param[in] ext_len  The length of the extension.
- * @return
- *     - NULL if we failed allocating an extension.
- *     - A pointer to the extension we allocated.
- */
-void *dict_attr_ext_alloc_size(TALLOC_CTX *ctx, fr_dict_attr_t **da_p, fr_dict_attr_ext_t ext, size_t ext_len)
-{
-       size_t                  aligned_len = ROUND_UP_POW2(ext_len, FR_DICT_ATTR_EXT_ALIGNMENT);
-       size_t                  da_len;
-       size_t                  hdr_len = 0;
-
-       size_t                  offset;
-
-       fr_dict_attr_ext_info_t const *info;
-       fr_dict_attr_t          *n_da, *da = *da_p;
-       uint8_t                 *ext_ptr;
-
-       (void)talloc_get_type_abort(da, fr_dict_attr_t);
-
-       if (da->ext[ext]) return fr_dict_attr_ext(da, ext);
-
-       if (unlikely(da->dict && da->dict->read_only)) {
-               fr_strerror_printf("%s dictionary has been marked as read only", fr_dict_root(da->dict)->name);
-               return NULL;
-       }
-
-       info = &fr_dict_ext_info[ext];
-       if (info->has_hdr) hdr_len = sizeof(dict_attr_ext_hdr_t);       /* Add space for a length prefix */
-
-       /*
-        *      Packing the offsets into a uint8_t means
-        *      the offset address of the final extension
-        *      must be less than or equal to
-        *      UINT8_MAX * FR_DICT_ATTR_EXT_ALIGNMENT
-        */
-       da_len = talloc_length(da);
-       offset = (da_len + hdr_len) / FR_DICT_ATTR_EXT_ALIGNMENT;
-       if (unlikely(offset > UINT8_MAX)) {
-               fr_strerror_printf("Insufficient space remaining for extensions");
-               return NULL;
-       }
-
-       n_da = talloc_realloc_size(ctx, da, da_len + hdr_len + aligned_len);
-       if (!n_da) {
-               fr_strerror_printf("Failed in realloc for dictionary extension '%s'. "
-                                  "Tried to realloc %zu bytes -> %zu bytes",
-                                  fr_table_str_by_value(dict_attr_ext_table, ext, "<INVALID>"),
-                                  da_len, da_len + aligned_len);
-               return NULL;
-       }
-       talloc_set_type(n_da, fr_dict_attr_t);
-       *da_p = n_da;
-
-       n_da->ext[ext] = (uint8_t)offset;
-       ext_ptr = ((uint8_t *)n_da) + da_len;
-
-       if (info->has_hdr) {
-               dict_attr_ext_hdr_t *ext_hdr = (dict_attr_ext_hdr_t *)ext_ptr;
-
-               ext_hdr->len = ext_len;         /* Record the real size */
-               return &ext_hdr->data;          /* Pointer to the data portion */
-       }
-
-       return ext_ptr;
-}
-
-/** Add a fixed length extension to a dictionary attribute
- *
- * Extensions are appended to the existing #fr_dict_attr_t memory chunk
- * using realloc.
- *
- * When a new extension is allocated it will not be initialised.
- * In the majority of instances this is OK as its value will be set
- * immediately, but care should be taken to ensure it is initialised
- * as some point.
- *
- * @param[in] ctx      the dict attr was originally allocated in.
- * @param[in,out] da_p The dictionary attribute to add an extension for.
- *                     Under certain circumstances the value of *da_p will
- *                     be changed to point to a new memory block.
- *                     All cached copied of the previous pointer should be
- *                     updated.  This means that attributes that have
- *                     already been added to a dictionary should not have
- *                     extensions allocated unless care is taken to update
- *                     all references.
- * @param[in] ext      to alloc.
- * @return
- *     - NULL if we failed allocating an extension.
- *     - A pointer to the extension we allocated.
- */
-void *dict_attr_ext_alloc(TALLOC_CTX *ctx, fr_dict_attr_t **da_p, fr_dict_attr_ext_t ext)
-{
-       return dict_attr_ext_alloc_size(ctx, da_p, ext, fr_dict_ext_info[ext].min);
-}
-
-/** Return the length of an extension
- *
- * @param[in] da       to return extension length for.
- * @param[in] ext      to return length for.
- * @return
- *     - 0 if no extension exists.
- *     - >0 the length of the extension.
- */
-size_t dict_attr_ext_len(fr_dict_attr_t const *da, fr_dict_attr_ext_t ext)
-{
-       uint8_t                         offset;
-       fr_dict_attr_ext_info_t const   *info;
-       dict_attr_ext_hdr_t             *ext_hdr;
-
-       offset = da->ext[ext];
-       if (!offset) return 0;
-
-       info = &fr_dict_ext_info[ext];
-       if (!info->has_hdr) return info->min;   /* Fixed size */
-
-       ext_hdr = (dict_attr_ext_hdr_t *)((uintptr_t)da) + ((offset * FR_DICT_ATTR_EXT_ALIGNMENT) - sizeof(dict_attr_ext_hdr_t));
-       return ext_hdr->len;
-}
-
-/** Copy extension data from one attribute to another
+/** Holds additional information about extension structures
  *
- * @param[in] ctx      to realloc da_out in.
- * @param[in] da_in    to copy extension from.
- * @param[in] ext      to copy.
- * @return
- *     - NULL if we failed to allocate an extension structure.
- *     - A pointer to the offset of the extension in da_out.
  */
-void *dict_attr_ext_copy(TALLOC_CTX *ctx,
-                        fr_dict_attr_t **da_out_p, fr_dict_attr_t const *da_in, fr_dict_attr_ext_t ext)
-{
-       void    *ext_ptr, *new_ext_ptr;
-       size_t  ext_len;
-
-       fr_dict_attr_ext_info_t const *info;
-
-       if (unlikely((*da_out_p)->dict && (*da_out_p)->dict->read_only)) {
-               fr_strerror_printf("%s dictionary has been marked as read only", fr_dict_root((*da_out_p)->dict)->name);
-               return NULL;
+fr_ext_t const fr_dict_attr_ext_def = {
+       .offset_of_exts = __offsetof(fr_dict_attr_t, ext),
+       .name_table     = dict_attr_ext_table,
+       .name_table_len = &dict_attr_ext_table_len,
+       .max            = FR_DICT_ATTR_EXT_MAX,
+       .info           = {
+               [FR_DICT_ATTR_EXT_NAME]         = {
+                                                       .min = sizeof(char),
+                                                       .has_hdr = true,
+                                                       .can_copy = false,      /* Name may change, and we can only set it once */
+                                               },
+               [FR_DICT_ATTR_EXT_CHILDREN]     = {
+                                                       .min = sizeof(fr_dict_attr_ext_children_t),
+                                                       .can_copy = false,      /* Limitation in hashing scheme we use */
+                                               },
+               [FR_DICT_ATTR_EXT_REF]          = {
+                                                       .min = sizeof(fr_dict_attr_ext_ref_t),
+                                                       .can_copy = true,
+                                               },
+               [FR_DICT_ATTR_EXT_VENDOR]       = {
+                                                       .min = sizeof(fr_dict_attr_ext_vendor_t),
+                                                       .can_copy = true,
+                                                       .copy = fr_dict_attr_ext_vendor_copy
+                                               },
+               [FR_DICT_ATTR_EXT_ENUMV]        = {
+                                                       .min = sizeof(fr_dict_attr_ext_enumv_t),
+                                                       .can_copy = true,
+                                                       .copy = fr_dict_attr_ext_enumv_copy
+                                               },
+               [FR_DICT_ATTR_EXT_DA_STACK]     = {
+                                                       .min = sizeof(fr_dict_attr_ext_da_stack_t),
+                                                       .has_hdr = true,
+                                                       .can_copy = false       /* Reinitialised for each new attribute */
+                                               },
+               [FR_DICT_ATTR_EXT_PROTOCOL_SPECIFIC] = {
+                                                       .min = sizeof(fr_dict_ext_protocol_specific_t),
+                                                       .can_copy = true
+                                               },
+               [FR_DICT_ATTR_EXT_MAX]          = {}
        }
+};
 
-       info = &fr_dict_ext_info[ext];
-       if (!info->can_copy) {
-               fr_strerror_printf("Extension cannot be copied");
-               return NULL;
-       }
-
-       ext_len = dict_attr_ext_len(da_in, ext);
-       ext_ptr = fr_dict_attr_ext(da_in, ext);
-       if (!ext_ptr) return NULL;
-
-       /*
-        *      Use the special copy function.
-        *      Its responsible for allocating the extension in the
-        *      destination attribute.
-        */
-       if (info->copy) return info->copy(ctx, da_out_p, ext, ext_ptr, ext_len);
-
-       /*
-        *      If there's no special function
-        *      just memcpy the data over.
-        */
-       new_ext_ptr = dict_attr_ext_alloc_size(ctx, da_out_p, ext, ext_len);
-       if (!new_ext_ptr) return NULL;
-       memcpy(new_ext_ptr, ext_ptr, ext_len);
-
-       return new_ext_ptr;
-}
+static fr_table_num_ordered_t const dict_enum_ext_table[] = {
+       { L("union_ref"),       FR_DICT_ENUM_EXT_UNION_REF      }
+};
+static size_t dict_enum_ext_table_len = NUM_ELEMENTS(dict_enum_ext_table);
 
-/** Copy all the extensions from one attribute to another
+/** Holds additional information about extension structures
  *
  */
-int dict_attr_ext_copy_all(TALLOC_CTX *ctx,
-                          fr_dict_attr_t **da_out_p, fr_dict_attr_t const *da_in)
-{
-       fr_dict_attr_ext_t i;
-
-       for (i = 0; i < NUM_ELEMENTS(da_in->ext); i++) {
-               if (!da_in->ext[i] || !fr_dict_ext_info[i].can_copy) continue;
-               if (!dict_attr_ext_copy(ctx, da_out_p, da_in, i)) return -1;
+fr_ext_t const fr_dict_enum_ext_def = {
+       .offset_of_exts = __offsetof(fr_dict_enum_t, ext),
+       .name_table     = dict_enum_ext_table,
+       .name_table_len = &dict_enum_ext_table_len,
+       .max            = FR_DICT_ENUM_EXT_MAX,
+       .info           = {
+               [FR_DICT_ENUM_EXT_UNION_REF]    = {
+                                                       .min = sizeof(fr_dict_enum_ext_union_ref_t),
+                                                       .can_copy = true
+                                               },
+               [FR_DICT_ENUM_EXT_MAX]          = {}
        }
+};
 
-       return 0;
-}
-
-void dict_attr_ext_debug(fr_dict_attr_t const *da)
-{
-       fr_dict_attr_ext_t i;
-
-       FR_FAULT_LOG("ext: %s - total len = %zu, ext len = %zu",
-                    da->name, talloc_length(da), talloc_length(da) - sizeof(fr_dict_attr_t));
-
-       for (i = 0; i < NUM_ELEMENTS(da->ext); i++) {
-               if (fr_dict_attr_has_ext(da, i)) {
-                       void    *ext = fr_dict_attr_ext(da, i);
-                       size_t  ext_len = dict_attr_ext_len(da, i);
-
-                       if (ext_len > 1024) {
-                               FR_FAULT_LOG("ext: %s - bad length %zu - limiting to 1024",
-                                            fr_table_str_by_value(dict_attr_ext_table, i, "<INVALID>"),
-                                            ext_len);
-                               ext_len = 1024;
-                       }
-
-                       FR_FAULT_LOG("ext: %s - start=%p end=%p len=%zu",
-                                    fr_table_str_by_value(dict_attr_ext_table, i, "<INVALID>"),
-                                    ext, ((uint8_t *)ext) + ext_len, ext_len);
-                       FR_FAULT_LOG_HEX(ext, ext_len);
-               }
-       }
-}
diff --git a/src/lib/util/dict_ext.h b/src/lib/util/dict_ext.h
new file mode 100644 (file)
index 0000000..a1efb9f
--- /dev/null
@@ -0,0 +1,207 @@
+#pragma once
+/*
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/** Multi-protocol AVP dictionary API
+ *
+ * @file src/lib/util/dict_ext.h
+ *
+ * @copyright 2020 The FreeRADIUS server project
+ * @copyright 2020 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ */
+RCSIDH(dict_ext_h, "$Id$")
+
+#include <freeradius-devel/util/dict.h>
+#include <freeradius-devel/util/ext.h>
+
+#include <limits.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Attribute extension - Holds children for an attribute
+ *
+ * Children are possible for:
+ *
+ * #FR_TYPE_TLV, #FR_TYPE_VENDOR, #FR_TYPE_VSA, #FR_TYPE_STRUCT
+ *
+ * *or* where the parent->parent->type is
+ * #FR_TYPE_STRUCT, and "parent" is a "key"
+ * field.  Note that these attributes therefore
+ * cannot have VALUEs, as the child defines their
+ * VALUE.  See dict_attr_can_have_children() for details.
+ */
+typedef struct {
+       fr_hash_table_t         *child_by_name;                 //!< Namespace at this level in the hierarchy.
+       fr_dict_attr_t const    **children;                     //!< Children of this attribute.
+} fr_dict_attr_ext_children_t;
+
+/** Attribute extension - Holds a reference to an attribute in another dictionary
+ *
+ */
+typedef struct {
+       fr_dict_attr_t const    *ref;                           //!< reference, only for #FR_TYPE_GROUP
+} fr_dict_attr_ext_ref_t;
+
+/** Attribute extension - Cached vendor pointer
+ *
+ */
+typedef struct {
+       fr_dict_attr_t const    *vendor;                        //!< ancestor which has type #FR_TYPE_VENDOR
+} fr_dict_attr_ext_vendor_t;
+
+/** Attribute extension - Stack of dictionary attributes that describe the path back to the root of the dictionary
+ *
+ */
+typedef struct {
+       fr_dict_attr_t const    *da_stack[0];                   //!< Stack of dictionary attributes
+} fr_dict_attr_ext_da_stack_t;
+
+/** Attribute extension - Holds enumeration values
+ *
+ */
+typedef struct {
+       fr_hash_table_t         *value_by_name;                 //!< Lookup an enumeration value by name
+       fr_hash_table_t         *name_by_value;                 //!< Lookup a name by value
+} fr_dict_attr_ext_enumv_t;
+
+/** Enum extension - Sub-struct or union pointer
+ *
+ */
+typedef struct {
+       fr_dict_attr_t const    *union_ref;                     //!< The union da this value points into.
+} fr_dict_enum_ext_union_ref_t;
+
+/** Attribute extension - Protocol-specific
+ *
+ */
+typedef struct {
+       void                    *uctx;                          //!< Protocol specific extensions
+} fr_dict_ext_protocol_specific_t;
+
+/** @name Add extension structures to attributes
+ *
+ * @{
+ */
+
+/* Retrieve an extension structure for a dictionary attribute
+ *
+ * @param[in] da       to retrieve structure from.
+ * @param[in] ext      to retrieve.
+ * @return
+ *     - NULL if the extension wasn't found.
+ *     - A pointer to the start of the extension.
+ */
+static inline void *fr_dict_attr_ext(fr_dict_attr_t const *da, fr_dict_attr_ext_t ext)
+{
+       if (!da->ext[ext]) return NULL;
+
+       return FR_EXT_PTR(da, ext, ext);
+}
+
+/** Return whether a da has a given extension or not
+ *
+ * @param[in] da       to check for extensions.
+ * @param[in] ext      to check.
+ * @return
+ *      - true if the da has the specified extension.
+ *     - false if the da does not have the specified extension
+ */
+static inline bool fr_dict_attr_has_ext(fr_dict_attr_t const *da, fr_dict_attr_ext_t ext)
+{
+       return (da->ext[ext] > 0);
+}
+
+/** Return the cached da stack (if any) associated with an attribute
+ *
+ * @param[in] da       to return cached da stack for.
+ * @return
+ *     - NULL if no da stack available.
+ *     - The cached da stack on success.
+ */
+static inline fr_dict_attr_t const **fr_dict_attr_da_stack(fr_dict_attr_t const *da)
+{
+       fr_dict_attr_ext_da_stack_t *ext;
+
+       ext = fr_dict_attr_ext(da, FR_DICT_ATTR_EXT_DA_STACK);
+       if (!ext) return NULL;
+
+       return ext->da_stack;
+}
+
+/** Return the reference associated with a group type attribute
+ *
+ * @param[in] da       to return the reference for.
+ * @return
+ *     - NULL if no reference available.
+ *     - A pointer to the attribute being referenced.
+ */
+static inline fr_dict_attr_t const *fr_dict_attr_ref(fr_dict_attr_t const *da)
+{
+       fr_dict_attr_ext_ref_t *ext;
+
+       ext = fr_dict_attr_ext(da, FR_DICT_ATTR_EXT_REF);
+       if (!ext) return NULL;
+
+       return ext->ref;
+}
+
+/** Return the vendor number for an attribute
+ *
+ * @param[in] da               The dictionary attribute to find the
+ *                             vendor for.
+ * @return
+ *     - 0 this isn't a vendor specific attribute.
+ *     - The vendor PEN.
+ */
+static inline uint32_t fr_dict_vendor_num_by_da(fr_dict_attr_t const *da)
+{
+       fr_dict_attr_ext_vendor_t *ext;
+
+       if (da->type == FR_TYPE_VENDOR) return da->attr;
+
+       ext = fr_dict_attr_ext(da, FR_DICT_ATTR_EXT_VENDOR);
+       if (!ext || !ext->vendor) return 0;
+
+       return ext->vendor->attr;
+}
+
+/** Return the vendor da for an attribute
+ *
+ * @param[in] da               The dictionary attribute to find the
+ *                             vendor for.
+ * @return
+ *     - 0 this isn't a vendor specific attribute.
+ *     - The vendor PEN.
+ */
+static inline fr_dict_attr_t const *fr_dict_vendor_da_by_da(fr_dict_attr_t const *da)
+{
+       fr_dict_attr_ext_vendor_t *ext;
+
+       if (da->type == FR_TYPE_VENDOR) return da;
+
+       ext = fr_dict_attr_ext(da, FR_DICT_ATTR_EXT_VENDOR);
+       if (!ext) return NULL;
+
+       return ext->vendor;
+}
+
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/src/lib/util/dict_ext_priv.h b/src/lib/util/dict_ext_priv.h
new file mode 100644 (file)
index 0000000..9771262
--- /dev/null
@@ -0,0 +1,161 @@
+#pragma once
+/*
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/** Extensions for dictionary definitions
+ *
+ * @file src/lib/util/dict_ext_priv.h
+ *
+ * @copyright 2020 The FreeRADIUS server project
+ * @copyright 2020 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ */
+RCSIDH(dict_ext_priv_h, "$Id$")
+
+#include <freeradius-devel/util/dict.h>
+#include <limits.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern fr_ext_t const fr_dict_attr_ext_def;
+extern fr_ext_t const fr_dict_enum_ext_def;
+
+/** @name Add extension structures to attributes
+ *
+ * @{
+ */
+
+/** Allocate an attribute extension of a particular size
+ *
+ */
+static inline void *dict_attr_ext_alloc_size(fr_dict_attr_t **da_p, fr_dict_attr_ext_t ext, size_t ext_len)
+{
+       if (unlikely((*da_p)->dict && fr_dict_is_read_only((*da_p)->dict))) {
+               fr_strerror_printf("%s dictionary has been marked as read only", fr_dict_root((*da_p)->dict)->name);
+               return NULL;
+       }
+
+       return fr_ext_alloc_size(&fr_dict_attr_ext_def, (void **)da_p, ext, ext_len);
+}
+
+/** Allocate an attribute extension
+ *
+ */
+static inline void *dict_attr_ext_alloc(fr_dict_attr_t **da_p, fr_dict_attr_ext_t ext)
+{
+       if (unlikely((*da_p)->dict && fr_dict_is_read_only((*da_p)->dict))) {
+               fr_strerror_printf("%s dictionary has been marked as read only", fr_dict_root((*da_p)->dict)->name);
+               return NULL;
+       }
+
+       return fr_ext_alloc_size(&fr_dict_attr_ext_def, (void **)da_p, ext, fr_dict_attr_ext_def.info[ext].min);
+}
+
+/** Return the length of an attribute extension
+ *
+ */
+static inline size_t dict_attr_ext_len(fr_dict_attr_t const *da, fr_dict_attr_ext_t ext)
+{
+       return fr_ext_len(&fr_dict_attr_ext_def, (void const *)da, ext);
+}
+
+/** Copy a single attribute extension from one attribute to another
+ *
+ */
+static inline void *dict_attr_ext_copy(fr_dict_attr_t **da_out_p, fr_dict_attr_t const *da_in, fr_dict_attr_ext_t ext)
+{
+       if (unlikely((*da_out_p)->dict && fr_dict_is_read_only((*da_out_p)->dict))) {
+               fr_strerror_printf("%s dictionary has been marked as read only", fr_dict_root((*da_out_p)->dict)->name);
+               return NULL;
+       }
+
+       return fr_ext_copy(&fr_dict_attr_ext_def, (void **)da_out_p, (void const *)da_in, ext);
+}
+
+/** Copy all attribute extensions from one attribute to another
+ *
+ */
+static inline int dict_attr_ext_copy_all(fr_dict_attr_t **da_out_p, fr_dict_attr_t const *da_in)
+{
+       if (unlikely((*da_out_p)->dict && fr_dict_is_read_only((*da_out_p)->dict))) {
+               fr_strerror_printf("%s dictionary has been marked as read only", fr_dict_root((*da_out_p)->dict)->name);
+               return -1;
+       }
+
+       return fr_ext_copy_all(&fr_dict_attr_ext_def, (void **)da_out_p, (void const *)da_in);
+}
+
+/** Print extension debug information for attributes
+ *
+ */
+static inline void dict_attr_ext_debug(fr_dict_attr_t const *da)
+{
+       fr_ext_debug(&fr_dict_attr_ext_def, da->name, da);
+}
+/** @} */
+
+/** @name Convenience functions for populating attribute extensions
+ *
+ * @{
+ */
+static inline int dict_attr_ref_set(fr_dict_attr_t const *da, fr_dict_attr_t const *ref)
+{
+       fr_dict_attr_ext_ref_t  *ext;
+
+       ext = fr_dict_attr_ext(da, FR_DICT_ATTR_EXT_REF);
+       if (unlikely(!ext)) {
+               fr_strerror_printf("%s (%s) contains no 'ref' extension", da->name,
+                                  fr_table_str_by_value(fr_value_box_type_table, da->type, "<UNKNOWN>"));
+               return -1;
+       }
+       ext->ref = ref;
+
+       return 0;
+}
+
+static inline int dict_attr_children_set(fr_dict_attr_t const *da, fr_dict_attr_t const        **children)
+{
+       fr_dict_attr_ext_children_t *ext;
+
+       ext = fr_dict_attr_ext(da, FR_DICT_ATTR_EXT_CHILDREN);
+       if (unlikely(!ext)) {
+               fr_strerror_printf("%s (%s) contains no 'children' extension", da->name,
+                                  fr_table_str_by_value(fr_value_box_type_table, da->type, "<UNKNOWN>"));
+               return -1;
+       }
+       ext->children = children;
+
+       return 0;
+}
+
+static inline fr_dict_attr_t const **dict_attr_children(fr_dict_attr_t const *da)
+{
+       fr_dict_attr_ext_children_t *ext;
+
+       ext = fr_dict_attr_ext(da, FR_DICT_ATTR_EXT_CHILDREN);
+       if (unlikely(!ext)) {
+               fr_strerror_printf("%s (%s) contains no 'children' extension", da->name,
+                                  fr_table_str_by_value(fr_value_box_type_table, da->type, "<UNKNOWN>"));
+               return NULL;
+       }
+       return ext->children;
+}
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif
index 3e1e6a20bf28d432cf9fb28cc0367fb9dcaa731c..8b257a798ac343e551c18a9c23112574295f457d 100644 (file)
@@ -27,10 +27,11 @@ extern "C" {
 
 #define _DICT_PRIVATE 1
 
+#include <freeradius-devel/protocol/base.h>
 #include <freeradius-devel/util/dict.h>
-#include <freeradius-devel/util/hash.h>
+#include <freeradius-devel/util/dict_ext_priv.h>
 #include <freeradius-devel/util/dl.h>
-#include <freeradius-devel/protocol/base.h>
+#include <freeradius-devel/util/hash.h>
 
 #define DICT_POOL_SIZE         (1024 * 1024 * 2)
 #define DICT_FIXUP_POOL_SIZE   (1024)
@@ -135,7 +136,7 @@ int                 dict_dlopen(fr_dict_t *dict, char const *name);
 
 fr_dict_attr_t                 *dict_attr_alloc_null(TALLOC_CTX *ctx);
 
-int                    dict_attr_init(TALLOC_CTX *ctx, fr_dict_attr_t **da_p,
+int                    dict_attr_init(fr_dict_attr_t **da_p,
                                       fr_dict_attr_t const *parent,
                                       char const *name, int attr,
                                       fr_type_t type, fr_dict_attr_flags_t const *flags);
@@ -147,70 +148,6 @@ fr_dict_attr_t             *dict_attr_alloc(TALLOC_CTX *ctx,
 
 fr_dict_attr_t         *dict_attr_acopy(TALLOC_CTX *ctx, fr_dict_attr_t const *in, char const *new_name);
 
-/** @name Add extension structures to attributes
- *
- * @{
- */
-void                   *dict_attr_ext_alloc_size(TALLOC_CTX *ctx, fr_dict_attr_t **da_p,
-                                                 fr_dict_attr_ext_t ext, size_t ext_len);
-
-void                   *dict_attr_ext_alloc(TALLOC_CTX *ctx, fr_dict_attr_t **da_p, fr_dict_attr_ext_t ext);
-
-size_t                 dict_attr_ext_len(fr_dict_attr_t const *da, fr_dict_attr_ext_t ext);
-
-void                   *dict_attr_ext_copy(TALLOC_CTX *ctx,
-                                           fr_dict_attr_t **da_out_p, fr_dict_attr_t const *da_in,
-                                           fr_dict_attr_ext_t ext);
-
-int                    dict_attr_ext_copy_all(TALLOC_CTX *ctx,
-                                              fr_dict_attr_t **da_out_p, fr_dict_attr_t const *da_in);
-
-void                   dict_attr_ext_debug(fr_dict_attr_t const *da);
-
-static inline int dict_attr_ref_set(fr_dict_attr_t const *da, fr_dict_attr_t const *ref)
-{
-       fr_dict_attr_ext_ref_t  *ext;
-
-       ext = fr_dict_attr_ext(da, FR_DICT_ATTR_EXT_REF);
-       if (unlikely(!ext)) {
-               fr_strerror_printf("%s (%s) contains no 'ref' extension", da->name,
-                                  fr_table_str_by_value(fr_value_box_type_table, da->type, "<UNKNOWN>"));
-               return -1;
-       }
-       ext->ref = ref;
-
-       return 0;
-}
-
-static inline int dict_attr_children_set(fr_dict_attr_t const *da, fr_dict_attr_t const        **children)
-{
-       fr_dict_attr_ext_children_t *ext;
-
-       ext = fr_dict_attr_ext(da, FR_DICT_ATTR_EXT_CHILDREN);
-       if (unlikely(!ext)) {
-               fr_strerror_printf("%s (%s) contains no 'children' extension", da->name,
-                                  fr_table_str_by_value(fr_value_box_type_table, da->type, "<UNKNOWN>"));
-               return -1;
-       }
-       ext->children = children;
-
-       return 0;
-}
-
-static inline fr_dict_attr_t const **dict_attr_children(fr_dict_attr_t const *da)
-{
-       fr_dict_attr_ext_children_t *ext;
-
-       ext = fr_dict_attr_ext(da, FR_DICT_ATTR_EXT_CHILDREN);
-       if (unlikely(!ext)) {
-               fr_strerror_printf("%s (%s) contains no 'children' extension", da->name,
-                                  fr_table_str_by_value(fr_value_box_type_table, da->type, "<UNKNOWN>"));
-               return NULL;
-       }
-       return ext->children;
-}
-/** @} */
-
 int                    dict_attr_child_add(fr_dict_attr_t *parent, fr_dict_attr_t *child);
 
 int                    dict_protocol_add(fr_dict_t *dict);
index bbb7ebfdb7c8c2a56d5486339298b3a381d29882..5ae98bfda8b0448565244ae91278944abd64646d 100644 (file)
@@ -85,8 +85,8 @@ fr_dict_attr_t *fr_dict_unknown_acopy(TALLOC_CTX *ctx, fr_dict_attr_t const *da,
        /*
         *      Initialize the rest of the fields.
         */
-       dict_attr_init(ctx, &n, parent, new_name ? new_name : da->name, da->attr, type, &flags);
-       dict_attr_ext_copy_all(ctx, &n, da);
+       dict_attr_init(&n, parent, new_name ? new_name : da->name, da->attr, type, &flags);
+       dict_attr_ext_copy_all(&n, da);
        DA_VERIFY(n);
 
        return n;
@@ -533,7 +533,7 @@ ssize_t fr_dict_unknown_afrom_oid_str(TALLOC_CTX *ctx, fr_dict_attr_t **out,
                 *      Leaf attribute
                 */
                case '\0':
-                       dict_attr_init(ctx, &n, our_parent, oid_str, num, FR_TYPE_OCTETS, &flags);
+                       dict_attr_init(&n, our_parent, oid_str, num, FR_TYPE_OCTETS, &flags);
                        break;
                }
                p++;
index dc81855d7cfc4cc58fa9db2093b1d01d06ce18d5..f13f2f9005cd3f9c8bcc23cbf8e9af5be6bbbaec 100644 (file)
@@ -350,11 +350,10 @@ static int dict_enum_value_cmp(void const *one, void const *two)
  *
  * @note This function can only be used _before_ the attribute is inserted into the dictionary.
  *
- * @param[in] ctx      to realloc attribute in.
  * @param[in] da_p     to set name for.
  * @param[in] name     to set.  If NULL a name will be automatically generated.
  */
-static inline CC_HINT(always_inline) int dict_attr_name_set(TALLOC_CTX *ctx, fr_dict_attr_t **da_p, char const *name)
+static inline CC_HINT(always_inline) int dict_attr_name_set(fr_dict_attr_t **da_p, char const *name)
 {
        char            buffer[FR_DICT_ATTR_MAX_NAME_LEN + 1];
        size_t          name_len;
@@ -392,7 +391,7 @@ static inline CC_HINT(always_inline) int dict_attr_name_set(TALLOC_CTX *ctx, fr_
         *      We do still need to fixup the da->name pointer
         *      though.
         */
-       name_start = dict_attr_ext_alloc_size(ctx, da_p, FR_DICT_ATTR_EXT_NAME, name_len + 1);
+       name_start = dict_attr_ext_alloc_size(da_p, FR_DICT_ATTR_EXT_NAME, name_len + 1);
        if (!name_start) return -1;
 
        name_end = name_start + name_len;
@@ -410,14 +409,13 @@ static inline CC_HINT(always_inline) int dict_attr_name_set(TALLOC_CTX *ctx, fr_
  *
  * @note This function can only be used _before_ the attribute is inserted into the dictionary.
  *
- * @param[in] ctx              to realloc attribute in.
  * @param[in] da_p             to set a group reference for.
  */
-static inline CC_HINT(always_inline) int dict_attr_children_init(TALLOC_CTX *ctx, fr_dict_attr_t **da_p)
+static inline CC_HINT(always_inline) int dict_attr_children_init(fr_dict_attr_t **da_p)
 {
        fr_dict_attr_ext_children_t     *ext;
 
-       ext = dict_attr_ext_alloc(ctx, da_p, FR_DICT_ATTR_EXT_CHILDREN);
+       ext = dict_attr_ext_alloc(da_p, FR_DICT_ATTR_EXT_CHILDREN);
        if (unlikely(!ext)) return -1;
        memset(ext, 0, sizeof(*ext));
 
@@ -428,14 +426,13 @@ static inline CC_HINT(always_inline) int dict_attr_children_init(TALLOC_CTX *ctx
  *
  * @note This function can only be used _before_ the attribute is inserted into the dictionary.
  *
- * @param[in] ctx              to realloc attribute in.
  * @param[in] da_p             to set a group reference for.
  */
-static inline CC_HINT(always_inline) int dict_attr_ref_init(TALLOC_CTX *ctx, fr_dict_attr_t **da_p)
+static inline CC_HINT(always_inline) int dict_attr_ref_init(fr_dict_attr_t **da_p)
 {
        fr_dict_attr_ext_ref_t          *ext;
 
-       ext = dict_attr_ext_alloc(ctx, da_p, FR_DICT_ATTR_EXT_REF);
+       ext = dict_attr_ext_alloc(da_p, FR_DICT_ATTR_EXT_REF);
        if (unlikely(!ext)) return -1;
        memset(ext, 0, sizeof(*ext));
 
@@ -446,16 +443,14 @@ static inline CC_HINT(always_inline) int dict_attr_ref_init(TALLOC_CTX *ctx, fr_
  *
  * @note This function can only be used _before_ the attribute is inserted into the dictionary.
  *
- * @param[in] ctx              to realloc attribute in.
  * @param[in] da_p             to set a group reference for.
  * @param[in] vendor           to set.
  */
-static inline CC_HINT(always_inline) int dict_attr_vendor_set(TALLOC_CTX *ctx,
-                                                             fr_dict_attr_t **da_p, fr_dict_attr_t const *vendor)
+static inline CC_HINT(always_inline) int dict_attr_vendor_set(fr_dict_attr_t **da_p, fr_dict_attr_t const *vendor)
 {
        fr_dict_attr_ext_vendor_t       *ext;
 
-       ext = dict_attr_ext_alloc(ctx, da_p, FR_DICT_ATTR_EXT_VENDOR);
+       ext = dict_attr_ext_alloc(da_p, FR_DICT_ATTR_EXT_VENDOR);
        if (unlikely(!ext)) return -1;
 
        ext->vendor = vendor;
@@ -467,10 +462,9 @@ static inline CC_HINT(always_inline) int dict_attr_vendor_set(TALLOC_CTX *ctx,
  *
  * @note This function can only be used _before_ the attribute is inserted into the dictionary.
  *
- * @param[in] ctx              to realloc attribute in.
  * @param[in] da_p             to populate the da_stack for.
  */
-static inline CC_HINT(always_inline) int dict_attr_da_stack_set(TALLOC_CTX *ctx, fr_dict_attr_t **da_p)
+static inline CC_HINT(always_inline) int dict_attr_da_stack_set(fr_dict_attr_t **da_p)
 {
        fr_dict_attr_ext_da_stack_t     *ext, *p_ext;
        fr_dict_attr_t                  *da = *da_p;
@@ -483,7 +477,7 @@ static inline CC_HINT(always_inline) int dict_attr_da_stack_set(TALLOC_CTX *ctx,
        p_ext = fr_dict_attr_ext(parent, FR_DICT_ATTR_EXT_DA_STACK);
        if (!p_ext) return 1;
 
-       ext = dict_attr_ext_alloc_size(ctx, da_p, FR_DICT_ATTR_EXT_DA_STACK, sizeof(ext->da_stack[0]) * (da->depth + 1));
+       ext = dict_attr_ext_alloc_size(da_p, FR_DICT_ATTR_EXT_DA_STACK, sizeof(ext->da_stack[0]) * (da->depth + 1));
        if (unlikely(!ext)) return -1;
 
        memcpy(ext->da_stack, p_ext->da_stack, sizeof(ext->da_stack[0]) * parent->depth);
@@ -500,14 +494,13 @@ static inline CC_HINT(always_inline) int dict_attr_da_stack_set(TALLOC_CTX *ctx,
  *
  * @note This function can only be used _before_ the attribute is inserted into the dictionary.
  *
- * @param[in] ctx              to realloc attribute in.
  * @param[in] da_p             to set a group reference for.
  */
-static inline CC_HINT(always_inline) int dict_attr_enumv_init(TALLOC_CTX *ctx, fr_dict_attr_t **da_p)
+static inline CC_HINT(always_inline) int dict_attr_enumv_init(fr_dict_attr_t **da_p)
 {
        fr_dict_attr_ext_enumv_t        *ext;
 
-       ext = dict_attr_ext_alloc(ctx, da_p, FR_DICT_ATTR_EXT_ENUMV);
+       ext = dict_attr_ext_alloc(da_p, FR_DICT_ATTR_EXT_ENUMV);
        if (unlikely(!ext)) return -1;
        memset(ext, 0, sizeof(*ext));
 
@@ -518,7 +511,6 @@ static inline CC_HINT(always_inline) int dict_attr_enumv_init(TALLOC_CTX *ctx, f
  *
  * @note This function can only be used _before_ the attribute is inserted into the dictionary.
  *
- * @param[in] ctx              To use if we need to re-alloc the da
  * @param[in] da_p             to initialise.
  * @param[in] parent           of the attribute, if none, should be
  *                             the dictionary root.
@@ -527,7 +519,7 @@ static inline CC_HINT(always_inline) int dict_attr_enumv_init(TALLOC_CTX *ctx, f
  * @param[in] type             of the attribute.
  * @param[in] flags            to assign.
  */
-int dict_attr_init(TALLOC_CTX *ctx, fr_dict_attr_t **da_p,
+int dict_attr_init(fr_dict_attr_t **da_p,
                   fr_dict_attr_t const *parent,
                   char const *name, int attr,
                   fr_type_t type, fr_dict_attr_flags_t const *flags)
@@ -551,9 +543,9 @@ int dict_attr_init(TALLOC_CTX *ctx, fr_dict_attr_t **da_p,
                 *      attributes are VSAs, caching this pointer will help.
                 */
                if (parent->type == FR_TYPE_VENDOR) {
-                       if (dict_attr_vendor_set(ctx, da_p, parent) < 0) return -1;
+                       if (dict_attr_vendor_set(da_p, parent) < 0) return -1;
                } else {
-                       dict_attr_ext_copy(ctx, da_p, parent, FR_DICT_ATTR_EXT_VENDOR); /* Noop if no vendor extension */
+                       dict_attr_ext_copy(da_p, parent, FR_DICT_ATTR_EXT_VENDOR); /* Noop if no vendor extension */
                }
        } else {
                (*da_p)->depth = 0;
@@ -563,7 +555,7 @@ int dict_attr_init(TALLOC_CTX *ctx, fr_dict_attr_t **da_p,
         *      Cache the da_stack so we don't need
         *      to generate it at runtime.
         */
-       dict_attr_da_stack_set(ctx, da_p);
+       dict_attr_da_stack_set(da_p);
 
        /*
         *      Structural types can have children
@@ -573,15 +565,15 @@ int dict_attr_init(TALLOC_CTX *ctx, fr_dict_attr_t **da_p,
        case FR_TYPE_STRUCTURAL:
        structural:
                if (type == FR_TYPE_GROUP) {
-                       if (dict_attr_ref_init(ctx, da_p) < 0) return -1;
+                       if (dict_attr_ref_init(da_p) < 0) return -1;
                        break;
                }
 
                if (type == FR_TYPE_TLV) {
-                       if (dict_attr_ref_init(ctx, da_p) < 0) return -1;
+                       if (dict_attr_ref_init(da_p) < 0) return -1;
                }
 
-               if (dict_attr_children_init(ctx, da_p) < 0) return -1;
+               if (dict_attr_children_init(da_p) < 0) return -1;
                break;
 
        /*
@@ -589,14 +581,14 @@ int dict_attr_init(TALLOC_CTX *ctx, fr_dict_attr_t **da_p,
         */
        case FR_TYPE_UINT8:     /* Hopefully temporary until unions are done properly */
        case FR_TYPE_UINT16:    /* Same here */
-               if (dict_attr_enumv_init(ctx, da_p) < 0) return -1;
+               if (dict_attr_enumv_init(da_p) < 0) return -1;
                goto structural;
 
        /*
         *      Leaf types
         */
        default:
-               if (dict_attr_enumv_init(ctx, da_p) < 0) return -1;
+               if (dict_attr_enumv_init(da_p) < 0) return -1;
                break;
        }
 
@@ -604,7 +596,7 @@ int dict_attr_init(TALLOC_CTX *ctx, fr_dict_attr_t **da_p,
         *      Name is a separate talloc chunk.  We allocate
         *      it last because we cache the pointer value.
         */
-       if (dict_attr_name_set(ctx, da_p, name) < 0) return -1;
+       if (dict_attr_name_set(da_p, name) < 0) return -1;
 
        DA_VERIFY(*da_p);
 
@@ -658,7 +650,7 @@ fr_dict_attr_t *dict_attr_alloc(TALLOC_CTX *ctx,
        n = dict_attr_alloc_null(ctx);
        if (unlikely(!n)) return NULL;
 
-       if (dict_attr_init(ctx, &n, parent, name, attr, type, flags) < 0) {
+       if (dict_attr_init(&n, parent, name, attr, type, flags) < 0) {
                talloc_free(n);
                return NULL;
        }
@@ -683,7 +675,7 @@ fr_dict_attr_t *dict_attr_acopy(TALLOC_CTX *ctx, fr_dict_attr_t const *in, char
        n = dict_attr_alloc(ctx, in->parent, new_name ? new_name : in->name, in->attr, in->type, &in->flags);
        if (unlikely(!n)) return NULL;
 
-       if (dict_attr_ext_copy_all(ctx, &n, in) < 0) {
+       if (dict_attr_ext_copy_all(&n, in) < 0) {
                talloc_free(n);
                return NULL;
        }
@@ -1637,6 +1629,11 @@ fr_dict_attr_t const *fr_dict_root(fr_dict_t const *dict)
        return dict->root;
 }
 
+bool fr_dict_is_read_only(fr_dict_t const *dict)
+{
+       return dict->read_only;
+}
+
 ssize_t dict_by_protocol_substr(fr_dict_attr_err_t *err,
                                fr_dict_t **out, fr_sbuff_t *name, fr_dict_t const *dict_def)
 {
diff --git a/src/lib/util/ext.c b/src/lib/util/ext.c
new file mode 100644 (file)
index 0000000..d9d97e9
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/** Extensions to talloced structures
+ *
+ * @file src/lib/util/ext.c
+ *
+ * @copyright 2020 The FreeRADIUS server project
+ * @copyright 2020 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ */
+RCSID("$Id$")
+
+#include <freeradius-devel/util/debug.h>
+#include <freeradius-devel/util/ext.h>
+#include <freeradius-devel/util/misc.h>
+#include <freeradius-devel/util/table.h>
+#include <freeradius-devel/util/talloc.h>
+
+#define CHUNK_EXT_PTR(_chunk, _chunk_ext, _ext) ((void *)((chunk_ext[_ext] * FR_EXT_ALIGNMENT) + ((uintptr_t)(_chunk))))
+#define CHUNK_EXT(_chunk, _offset) ((uint8_t *)(((uintptr_t)_chunk) + (_offset)))
+
+/** Add a variable length extension to a dictionary attribute
+ *
+ * Extensions are appended to the existing #fr_dict_attr_t memory chunk
+ * using realloc.
+ *
+ * When a new extension is allocated it will not be initialised.
+ *
+ * @param[in] def              Extension definitions.
+ * @param[in,out] chunk_p      The chunk to add an extension for.
+ *                             Under certain circumstances the value of *chunk_p will
+ *                             be changed to point to a new memory block.
+ *                             All cached copied of the previous pointer should be
+ *                             updated.  This means that attributes that have
+ *                             already been added to a dictionary should not have
+ *                             extensions allocated unless care is taken to update
+ *                             all references.
+ * @param[in] ext              to alloc.
+ * @param[in] ext_len          The length of the extension.
+ * @return
+ *     - NULL if we failed allocating an extension.
+ *     - A pointer to the extension we allocated.
+ */
+void *fr_ext_alloc_size(fr_ext_t const *def, void **chunk_p, int ext, size_t ext_len)
+{
+       size_t                  aligned_len = ROUND_UP_POW2(ext_len, FR_EXT_ALIGNMENT);
+       size_t                  chunk_len;
+       size_t                  hdr_len = 0;
+
+       size_t                  offset;
+
+       fr_ext_info_t const     *info;
+       void                    *n_chunk, *chunk = *chunk_p;
+       uint8_t                 *chunk_ext;
+       uint8_t                 *ext_ptr;
+       char const              *type;
+
+       chunk_ext = CHUNK_EXT(*chunk_p, def->offset_of_exts);
+       if (chunk_ext[ext]) return CHUNK_EXT_PTR(*chunk_p, chunk_ext, ext);
+
+       info = &def->info[ext];
+       if (info->has_hdr) hdr_len = sizeof(dict_ext_hdr_t);    /* Add space for a length prefix */
+
+       /*
+        *      Packing the offsets into a uint8_t means
+        *      the offset address of the final extension
+        *      must be less than or equal to
+        *      UINT8_MAX * FR_EXT_ALIGNMENT
+        */
+       chunk_len = talloc_get_size(chunk);
+       offset = (chunk_len + hdr_len) / FR_EXT_ALIGNMENT;
+       if (unlikely(offset > UINT8_MAX)) {
+               fr_strerror_printf("Insufficient space remaining for extensions");
+               return NULL;
+       }
+
+       type = talloc_get_name(chunk);
+       n_chunk = talloc_realloc_size(NULL, chunk, chunk_len + hdr_len + aligned_len);
+       if (!n_chunk) {
+               fr_strerror_printf("Failed reallocing %s.  Tried to realloc %zu bytes -> %zu bytes",
+                                  type, chunk_len, chunk_len + aligned_len);
+               return NULL;
+       }
+       talloc_set_name_const(n_chunk, type);
+       *chunk_p = n_chunk;
+
+       chunk_ext = CHUNK_EXT(*chunk_p, def->offset_of_exts);
+       chunk_ext[ext] = (uint8_t)offset;
+
+       ext_ptr = ((uint8_t *)n_chunk) + chunk_len;
+
+       if (info->has_hdr) {
+               dict_ext_hdr_t *ext_hdr = (dict_ext_hdr_t *)ext_ptr;
+
+               ext_hdr->len = ext_len;         /* Record the real size */
+               return &ext_hdr->data;          /* Pointer to the data portion */
+       }
+
+       return ext_ptr;
+}
+
+/** Return the length of an extension
+ *
+ * @param[in] def              Extension definitions.
+ * @param[in] chunk            to return extension length for.
+ * @param[in] ext              to return length for.
+ * @return
+ *     - 0 if no extension exists.
+ *     - >0 the length of the extension.
+ */
+size_t fr_ext_len(fr_ext_t const *def, void const *chunk, int ext)
+{
+       uint8_t                 offset;
+       fr_ext_info_t const     *info;
+       dict_ext_hdr_t          *ext_hdr;
+       uint8_t                 *chunk_ext;
+
+       chunk_ext = CHUNK_EXT(chunk, def->offset_of_exts);
+       offset = chunk_ext[ext];
+       if (!offset) return 0;
+
+       info = &def->info[ext];
+       if (!info->has_hdr) return info->min;   /* Fixed size */
+
+       ext_hdr = (dict_ext_hdr_t *)((uintptr_t)chunk) + ((offset * FR_EXT_ALIGNMENT) - sizeof(dict_ext_hdr_t));
+       return ext_hdr->len;
+}
+
+/** Copy extension data from one attribute to another
+ *
+ * @param[in] def              Extension definitions.
+ * @param[in,out] chunk_out    to copy extension to.
+ * @param[in] chunk_in         to copy extension from.
+ * @param[in] ext              to copy.
+ * @return
+ *     - NULL if we failed to allocate an extension structure.
+ *     - A pointer to the offset of the extension in da_out.
+ */
+void *fr_ext_copy(fr_ext_t const *def, void **chunk_out, void const *chunk_in, int ext)
+{
+       void    *ext_ptr, *new_ext_ptr;
+       uint8_t *chunk_ext;
+       size_t  ext_len;
+
+       fr_ext_info_t const *info;
+
+       info = &def->info[ext];
+       if (!info->can_copy) {
+               fr_strerror_printf("Extension cannot be copied");
+               return NULL;
+       }
+
+       chunk_ext = CHUNK_EXT(chunk_in, def->offset_of_exts);
+       ext_ptr = CHUNK_EXT_PTR(chunk_in, chunk_ext, ext);
+       if (!ext_ptr) return NULL;
+
+       ext_len = fr_ext_len(def, chunk_in, ext);
+
+       /*
+        *      Use the special copy function.
+        *      Its responsible for allocating the extension in the
+        *      destination attribute.
+        */
+       if (info->copy) return info->copy(chunk_out, ext, ext_ptr, ext_len);
+
+       /*
+        *      If there's no special function
+        *      just memcpy the data over.
+        */
+       new_ext_ptr = fr_ext_alloc_size(def, chunk_out, ext, ext_len);
+       if (!new_ext_ptr) return NULL;
+       memcpy(new_ext_ptr, ext_ptr, ext_len);
+
+       return new_ext_ptr;
+}
+
+/** Copy all the extensions from one attribute to another
+ *
+ */
+int fr_ext_copy_all(fr_ext_t const *def, void **chunk_out, void const *chunk_in)
+{
+       int     i;
+       uint8_t *ext_in = CHUNK_EXT(chunk_in, def->offset_of_exts);
+
+       for (i = 0; i < def->max; i++) {
+               if (!ext_in[i] || !def->info[i].can_copy) continue;
+               if (!fr_ext_copy(def, chunk_out, chunk_in, i)) return -1;
+       }
+
+       return 0;
+}
+
+void fr_ext_debug(fr_ext_t const *def, char const *name, void const *chunk)
+{
+       int i;
+
+       FR_FAULT_LOG("ext: %s - total len = %zu", name, talloc_get_size(chunk));
+       for (i = 0; i < (int)def->max; i++) {
+               uint8_t *chunk_ext = CHUNK_EXT(def->info, def->offset_of_exts);
+               if (chunk_ext[i]) {
+                       void            *ext = CHUNK_EXT_PTR(chunk, chunk_ext, i);
+                       size_t          ext_len = fr_ext_len(def, chunk, i);
+
+                       char const      *ext_name = fr_table_ordered_str_by_num(def->name_table,
+                                                                               *def->name_table_len,
+                                                                               i, "<INVALID>");
+
+                       if (ext_len > 1024) {
+                               FR_FAULT_LOG("ext: %s - possibly bad length %zu - limiting dump to 1024",
+                                            ext_name, ext_len);
+                               ext_len = 1024;
+                       }
+
+                       FR_FAULT_LOG("ext: %s - start=%p end=%p len=%zu",
+                                    ext_name, ext, ((uint8_t *)ext) + ext_len, ext_len);
+                       FR_FAULT_LOG_HEX(ext, ext_len);
+               }
+       }
+}
diff --git a/src/lib/util/ext.h b/src/lib/util/ext.h
new file mode 100644 (file)
index 0000000..5b84fcc
--- /dev/null
@@ -0,0 +1,116 @@
+#pragma once
+/*
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/** Extensions to talloced structures
+ *
+ * These allow multiple variable length chunks to be appended to talloced
+ * structures.  Extensions can either contain a header in which case the
+ * exact length is recorded, or they can be of a fixed size.
+ *
+ * The structure being extended must be padded to a multiple of FR_EXT_ALIGNMENT.
+ *
+ * It is strongly recommended that extended structures are allocated in a
+ * talloc_pool() to avoid the overhead of multiple reallocs.
+ *
+ * @file src/lib/util/ext.h
+ *
+ * @copyright 2020 The FreeRADIUS server project
+ * @copyright 2020 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ */
+RCSIDH(ext_h, "$Id$")
+
+#include <freeradius-devel/util/table.h>
+#include <limits.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** The alignment of object extension structures
+ *
+ */
+#ifdef __WORD_SIZE
+#  if __WORD_SIZE < 4
+#    define FR_EXT_ALIGNMENT   sizeof(uint32_t)
+#  else
+#    define FR_EXT_ALIGNMENT   __WORD_SIZE             /* From limits.h */
+#  endif
+#else
+#  define FR_EXT_ALIGNMENT     sizeof(uint64_t)
+#endif
+
+/** Function for fixing up extensions after they're copied
+ *
+ */
+typedef void *(* fr_ext_copy_t)(void **chunk_p, int ext, void *ext_ptr, size_t ext_len);
+
+/** Additional information for a given extension
+ */
+typedef struct {
+       size_t                  min;                    //!< Minimum size of extension.
+       bool                    has_hdr;                //!< Has additional metadata allocated before
+                                                       ///< the extension data.
+       bool                    can_copy;               //!< Copying this extension between attributes is allowed.
+       fr_ext_copy_t           copy;                   //!< Override the normal copy operation with a callback.
+} fr_ext_info_t;
+
+/** Structure to define a set of extensions
+ *
+ */
+typedef struct {
+       size_t                  offset_of_exts;         //!< Where in the extended struct the extensions array starts.
+       fr_table_num_ordered_t const    *name_table;    //!< String identifiers for the extensions.
+       size_t                  *name_table_len;        //!< How many extensions there are in the table.
+       int                     max;                    //!< The highest extension value.
+       fr_ext_info_t const     info[];                 //!< Additional information about each extension.
+} fr_ext_t;
+
+/** Optional extension header struct
+ *
+ */
+typedef struct {
+       size_t                  len;                    //!< Length of extension data.
+       uint8_t                 data[];                 //!< Extension data
+} CC_HINT(aligned(FR_EXT_ALIGNMENT)) dict_ext_hdr_t;
+
+/** @name Generic extension manipulation functions that can be used with any talloced chunk
+ *
+ * @{
+ */
+
+/** Return a pointer to the specified extension structure
+ *
+ * @param[in] _ptr     to fetch extension for.
+ * @param[in] _field   Array of extensions.
+ * @param[in] _ext     to retrieve.
+ */
+#define FR_EXT_PTR(_ptr, _field, _ext) ((void *)(((_ptr)->_field[_ext] * FR_EXT_ALIGNMENT) + ((uintptr_t)(_ptr))))
+
+void   *fr_ext_alloc_size(fr_ext_t const *def, void **chunk_p, int ext, size_t ext_len);
+
+size_t fr_ext_len(fr_ext_t const *def, void const *chunk_in, int ext);
+
+void   *fr_ext_copy(fr_ext_t const *def, void **chunk_out, void const *chunk_in, int ext);
+
+int    fr_ext_copy_all(fr_ext_t const *def, void **chunk_out, void const *chunk_in);
+
+void   fr_ext_debug(fr_ext_t const *def, char const *name, void const *chunk);
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif
index 0febfb05aed39f49ef0d83ea515844e25513acba..8ae65bada107b2c05c92ca535af3cd6445c90b20 100644 (file)
@@ -21,6 +21,7 @@ SOURCES               := \
                   dl.c \
                   dns.c \
                   event.c \
+                  ext.c \
                   fifo.c \
                   file.c \
                   fopencookie.c \
index e41cefcabefa014173039980432e6a24fe165905..8a216b1c11b25ae53ca92d4d8fa87807f0c01cf6 100644 (file)
@@ -63,11 +63,6 @@ talloc_foreach(vpt_m, vpt) {
             (_p < _end) && (_iter = *((void **)(_p))); \
             _p = (__typeof__(_p))((__typeof__(_array))_p) + 1)
 
-/** Get the number of bytes in a given talloc chunk
- *
- */
-#define talloc_length(_ptr) talloc_array_length((uint8_t const *)(_ptr))
-
 typedef int(* fr_talloc_free_func_t)(void *fire_ctx, void *uctx);
 
 typedef struct fr_talloc_destructor_s fr_talloc_destructor_t;