]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Simplify unknown attribute OID parsing/printing
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Sat, 28 Jan 2017 12:45:01 +0000 (12:45 +0000)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Sat, 28 Jan 2017 12:45:10 +0000 (12:45 +0000)
As tmpls are no longer stack allocated, we can hang additional talloced memory off the structure.

This means unknown attributes no longer need to be placed in a buffer which is part of the tmpl, but can be allocated on the heap.

It also means that where an unknown attribute hierarchy contains an unknown vendor, the unknown vendor can form part of the hierarchy too, without needing a special field.

References #1883

src/include/dict.h
src/include/tmpl.h
src/lib/dict.c
src/lib/pair.c
src/lib/radius_decode.c
src/main/tmpl.c
src/tests/unit/errors.txt
src/tests/unit/unknown.txt [new file with mode: 0644]

index cc3a7311d1cd379ad7ea091bf911385707040c47..10109d4d966dcd75ff4d5703cf5561b325c30f64 100644 (file)
@@ -179,30 +179,27 @@ fr_dict_attr_t const      *fr_dict_root(fr_dict_t const *dict);
 /*
  *     Unknown ephemeral attributes
  */
+
+fr_dict_attr_t         *fr_dict_unknown_acopy(TALLOC_CTX *ctx, fr_dict_attr_t const *da);
+
 fr_dict_attr_t const   *fr_dict_unknown_add(fr_dict_t *dict, fr_dict_attr_t const *old);
 
 void                   fr_dict_unknown_free(fr_dict_attr_t const **da);
 
-int                    fr_dict_unknown_vendor_afrom_num(TALLOC_CTX *ctx, fr_dict_attr_t const **out,
+int                    fr_dict_unknown_vendor_afrom_num(TALLOC_CTX *ctx, fr_dict_attr_t **out,
                                                         fr_dict_attr_t const *parent, unsigned int vendor);
 
 size_t                 dict_print_attr_oid(char *buffer, size_t outlen,
                                            fr_dict_attr_t const *ancestor, fr_dict_attr_t const *da);
 
-int                    fr_dict_unknown_from_fields(fr_dict_attr_t *da, fr_dict_attr_t const *parent,
-                                                   unsigned int vendor, unsigned int attr) CC_HINT(nonnull);
-
 fr_dict_attr_t         *fr_dict_unknown_afrom_fields(TALLOC_CTX *ctx, fr_dict_attr_t const *parent,
                                                      unsigned int vendor, unsigned int attr) CC_HINT(nonnull);
 
-int                    fr_dict_unknown_from_oid(fr_dict_t *dict, fr_dict_attr_t *vendor_da, fr_dict_attr_t *da,
-                                                fr_dict_attr_t const *parent, char const *name);
-
-fr_dict_attr_t const   *fr_dict_unknown_afrom_oid(TALLOC_CTX *ctx, fr_dict_t *dict,
-                                                  fr_dict_attr_t const *parent, char const *name);
+ssize_t                        fr_dict_unknown_afrom_oid_str(TALLOC_CTX *ctx, fr_dict_attr_t **out,
+                                                     fr_dict_attr_t const *parent, char const *oid_str);
 
-int                    fr_dict_unknown_from_suboid(fr_dict_t *dict, fr_dict_attr_t *vendor_da, fr_dict_attr_t *da,
-                                                   fr_dict_attr_t const *parent, char const **name);
+ssize_t                        fr_dict_unknown_afrom_oid_substr(TALLOC_CTX *ctx, fr_dict_attr_t **out,
+                                                        fr_dict_attr_t const *parent, char const *name);
 
 fr_dict_attr_t const   *fr_dict_attr_known(fr_dict_t *dict, fr_dict_attr_t const *da);
 
index aad6ad336a9733f9079c00466553e1bd875d91e5..5b29738ab0ca1af10e565a9be62be07926cdd3cf 100644 (file)
@@ -188,9 +188,9 @@ typedef struct vp_tmpl_t {
 
                        fr_dict_attr_t const    *da;                    //!< Resolved dictionary attribute.
                        union {
-                               uint8_t         da[FR_DICT_ATTR_SIZE];  //!< Unknown dictionary attribute buffer.
-                               uint8_t         vendor[FR_DICT_ATTR_SIZE];      //!< Unknown dictionary attribute buffer.
-                               char            *name;                  //!< Raw unknown dictionary name.
+                               fr_dict_attr_t          *da;            //!< Unknown dictionary 
+                                                                       //!< attribute buffer.
+                               char                    *name;
                        } unknown;
                        int                     num;                     //!< For array references.
                        int8_t                  tag;                     //!< For tag references.
@@ -217,7 +217,6 @@ typedef struct vp_tmpl_t {
 #define tmpl_list              data.attribute.list
 #define tmpl_da                        data.attribute.da
 #define tmpl_unknown           data.attribute.unknown.da
-#define tmpl_unknown_vendor    data.attribute.unknown.vendor
 #define tmpl_unknown_name              data.attribute.unknown.name
 #define tmpl_num               data.attribute.num
 #define tmpl_tag               data.attribute.tag
index 133585c16d29b13476a7e668218fa0172a7f10f2..59251fac077f9a70adbf4787c20614b449f4bd91 100644 (file)
@@ -670,26 +670,147 @@ static inline int fr_dict_attr_child_add(fr_dict_attr_t *parent, fr_dict_attr_t
        return 0;
 }
 
+/** Build the tlv_stack for the specified DA and encode the path in OID form
+ *
+ * @param[out] out Where to write the OID.
+ * @param[in] outlen Length of the output buffer.
+ * @param[in] ancestor If not NULL, only print OID portion between ancestor and da.
+ * @param[in] da to print OID string for.
+ * @return the number of bytes written to the buffer.
+ */
+size_t dict_print_attr_oid(char *out, size_t outlen,
+                          fr_dict_attr_t const *ancestor, fr_dict_attr_t const *da)
+{
+       size_t                  len;
+       char                    *p = out, *end = p + outlen;
+       int                     i;
+       int                     depth = 0;
+       fr_dict_attr_t const    *tlv_stack[FR_DICT_MAX_TLV_STACK + 1];
+
+       if (!outlen) return 0;
+
+       /*
+        *      If the ancestor and the DA match, there's
+        *      no OID string to print.
+        */
+       if (ancestor == da) {
+               out[0] = '\0';
+               return 0;
+       }
+
+       fr_proto_tlv_stack_build(tlv_stack, da);
+
+       if (ancestor) {
+               if (tlv_stack[ancestor->depth - 1] != ancestor) {
+                       fr_strerror_printf("Attribute \"%s\" is not a descendent of \"%s\"", da->name, ancestor->name);
+                       return -1;
+               }
+               depth = ancestor->depth;
+       }
+
+       /*
+        *      We don't print the ancestor, we print the OID
+        *      between it and the da.
+        */
+       len = snprintf(p, end - p, "%u", tlv_stack[depth]->attr);
+       if ((p + len) >= end) return p - out;
+       p += len;
+
+
+       for (i = depth + 1; i < (int)da->depth; i++) {
+               len = snprintf(p, end - p, ".%u", tlv_stack[i]->attr);
+               if ((p + len) >= end) return p - out;
+               p += len;
+       }
+
+       return p - out;
+}
+
+/** Grow or shrink a heap allocated fr_dict_attr_t and copy a new name string into its name buffer
+ *
+ * @param[in] da       to set a new name for.
+ * @param[in] name     to set.
+ * @return
+ *     - 0 on success.
+ *     - -1 on failure (memory allocation error).
+ */
+static int fr_dict_attr_set_name(fr_dict_attr_t **da, char const *name)
+{
+       size_t          len;
+       fr_dict_attr_t  *new;
+
+       len = strlen(name);
+
+       talloc_set_type(*da, uint8_t);
+       new = (fr_dict_attr_t *)talloc_realloc(talloc_parent(*da), *da, uint8_t, sizeof(fr_dict_attr_t) + len + 1);
+       if (!new) return -1;
+
+       talloc_set_type(new, fr_dict_attr_t);
+
+       strlcpy(new->name, name, len + 1);
+
+       *da = new;
+
+       return 0;
+}
+
+/** 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] vendor   of the attribute.  Deprecated.
+ * @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 *fr_dict_attr_alloc(TALLOC_CTX *ctx,
+                                         fr_dict_attr_t const *parent,
                                          char const *name, unsigned int vendor, int attr,
-                                         PW_TYPE type, fr_dict_attr_flags_t flags)
+                                         PW_TYPE type, fr_dict_attr_flags_t const *flags)
 {
        fr_dict_attr_t *da;
-       size_t namelen = strlen(name);
 
-       da = (fr_dict_attr_t *)talloc_zero_array(ctx, uint8_t, sizeof(*da) + namelen);
+       da = (fr_dict_attr_t *)talloc_zero_array(ctx, uint8_t, sizeof(*da));
        if (!da) {
                fr_strerror_printf("Out of memory");
                return NULL;
        }
        talloc_set_type(da, fr_dict_attr_t);
 
-       memcpy(da->name, name, namelen);
-       da->name[namelen] = '\0';
        da->attr = attr;
        da->vendor = vendor;
        da->type = type;
-       da->flags = flags;
+       memcpy(&da->flags, flags, sizeof(*flags));
+       da->parent = parent;
+       da->depth = parent->depth + 1;
+
+       if (!name) {
+               char    buffer[FR_DICT_ATTR_MAX_NAME_LEN + 1];
+               char    *p = buffer;
+               size_t  len;
+
+               len = snprintf(p, sizeof(buffer), "Attr-");
+               p += len;
+
+               len = dict_print_attr_oid(p, sizeof(buffer) - (p - buffer), NULL, da);
+               if (is_truncated(len, sizeof(buffer) - (p - buffer))) {
+                       fr_strerror_printf("OID string too long for unknown attribute");
+                       return NULL;
+               }
+
+               if (fr_dict_attr_set_name(&da, buffer) < 0) {
+               error:
+                       talloc_free(da);
+                       return NULL;
+               }
+               return da;
+       }
+
+       if (fr_dict_attr_set_name(&da, name) < 0) goto error;
 
        return da;
 }
@@ -1289,7 +1410,7 @@ static fr_dict_attr_t *fr_dict_attr_add_by_name(fr_dict_t *dict, fr_dict_attr_t
                vendor = parent->vendor;
        }
 
-       n = fr_dict_attr_alloc(dict->pool, name, vendor, attr, type, flags);
+       n = fr_dict_attr_alloc(dict->pool, parent, name, vendor, attr, type, &flags);
        if (!n) {
        oom:
                fr_strerror_printf("Out of memory");
@@ -1352,9 +1473,6 @@ static fr_dict_attr_t *fr_dict_attr_add_by_name(fr_dict_t *dict, fr_dict_attr_t
                }
        }
 
-       n->parent = parent;
-       n->depth = parent->depth + 1;
-
        return n;
 }
 
@@ -2090,6 +2208,8 @@ static int _dict_from_file(dict_from_file_ctx_t *ctx,
        fr_dict_attr_t const    *da;
        fr_dict_attr_flags_t    base_flags;
 
+       if (!fr_cond_assert(ctx->parent)) return -1;
+
        if ((strlen(dir_name) + 3 + strlen(filename)) > sizeof(dir)) {
                fr_strerror_printf("%s: Filename name too long", __FUNCTION__);
                return -1;
@@ -2359,6 +2479,7 @@ static int _dict_from_file(dict_from_file_ctx_t *ctx,
                        fr_dict_attr_flags_t    flags;
 
                        fr_dict_attr_t const    *vsa_da;
+                       fr_dict_attr_t const    *vendor_da;
                        fr_dict_attr_t          *new;
                        fr_dict_attr_t          *mutable;
 
@@ -2413,8 +2534,8 @@ static int _dict_from_file(dict_from_file_ctx_t *ctx,
                                        memset(&flags, 0, sizeof(flags));
 
                                        memcpy(&mutable, &ctx->parent, sizeof(mutable));
-                                       new = fr_dict_attr_alloc(mutable, "Vendor-Specific", 0,
-                                                                PW_VENDOR_SPECIFIC, PW_TYPE_VSA, flags);
+                                       new = fr_dict_attr_alloc(mutable, fr_dict_root(ctx->dict), "Vendor-Specific", 0,
+                                                                PW_VENDOR_SPECIFIC, PW_TYPE_VSA, &flags);
                                        fr_dict_attr_child_add(mutable, new);
                                        vsa_da = new;
                                }
@@ -2424,8 +2545,8 @@ static int _dict_from_file(dict_from_file_ctx_t *ctx,
                         *      Create a VENDOR attribute on the fly, either in the context
                         *      of the EVS attribute, or the VSA (26) attribute.
                         */
-                       ctx->parent = fr_dict_attr_child_by_num(vsa_da, vendor);
-                       if (!ctx->parent) {
+                       vendor_da = fr_dict_attr_child_by_num(vsa_da, vendor);
+                       if (!vendor_da) {
                                memset(&flags, 0, sizeof(flags));
 
                                if (vsa_da->type == PW_TYPE_VSA) {
@@ -2447,11 +2568,13 @@ static int _dict_from_file(dict_from_file_ctx_t *ctx,
                                }
 
                                memcpy(&mutable, &vsa_da, sizeof(mutable));
-                               new = fr_dict_attr_alloc(mutable, argv[1], 0, vendor, PW_TYPE_VENDOR, flags);
+                               new = fr_dict_attr_alloc(mutable, ctx->parent,
+                                                        argv[1], 0, vendor, PW_TYPE_VENDOR, &flags);
                                fr_dict_attr_child_add(mutable, new);
 
-                               ctx->parent = new;
+                               vendor_da = new;
                        }
+                       ctx->parent = vendor_da;
                        ctx->block_vendor = vendor;
                        continue;
                } /* BEGIN-VENDOR */
@@ -2620,7 +2743,8 @@ int fr_dict_from_file(TALLOC_CTX *ctx, fr_dict_t **out, char const *dir, char co
 
                        type_name = talloc_asprintf(dict->pool, "Tmp-Cast-%s", p->name);
 
-                       n = fr_dict_attr_alloc(dict->pool, type_name, 0, PW_CAST_BASE + p->number, p->number, flags);
+                       n = fr_dict_attr_alloc(dict->pool, dict->root, type_name,
+                                              0, PW_CAST_BASE + p->number, p->number, &flags);
                        if (!n) goto error;
 
                        if (!fr_hash_table_insert(dict->attributes_by_name, n)) goto error;
@@ -2759,7 +2883,7 @@ fr_dict_attr_t const *fr_dict_root(fr_dict_t const *dict)
  *
  * Will copy the complete hierarchy down to the first known attribute.
  */
-static fr_dict_attr_t *fr_dict_unknown_acopy(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
+fr_dict_attr_t *fr_dict_unknown_acopy(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
 {
        fr_dict_attr_t *new, *new_parent = NULL;
        fr_dict_attr_t const *parent;
@@ -2771,9 +2895,7 @@ static fr_dict_attr_t *fr_dict_unknown_acopy(TALLOC_CTX *ctx, fr_dict_attr_t con
                parent = da->parent;
        }
 
-       new = fr_dict_attr_alloc(ctx, da->name, da->vendor, da->attr, da->type, da->flags);
-       new->flags.is_unknown = 1;
-       new->flags.is_raw = 1;
+       new = fr_dict_attr_alloc(ctx, parent, da->name, da->vendor, da->attr, da->type, &da->flags);
        new->parent = parent;
        new->depth = da->depth;
 
@@ -2877,137 +2999,6 @@ void fr_dict_unknown_free(fr_dict_attr_t const **da)
        *tmp = NULL;
 }
 
-/** Build an unknown vendor, parented by a VSA or EVS attribute
- *
- * This allows us to complete the path back to the dictionary root in the case
- * of unknown attributes with unknown vendors.
- *
- * @note Will return known vendors attributes where possible.  Do not free directly,
- *     use #fr_dict_unknown_free.
- *
- * @param[in] ctx to allocate the vendor attribute in.
- * @param[out] out Where to write point to new unknown dict attr representing the unknown vendor.
- * @param[in] parent of the vendor attribute, either an EVS or VSA attribute.
- * @param[in] vendor id.
- * @return
- *     - 0 on success.
- *     - -1 on failure.
- */
-int fr_dict_unknown_vendor_afrom_num(TALLOC_CTX *ctx, fr_dict_attr_t const **out,
-                                    fr_dict_attr_t const *parent, unsigned int vendor)
-{
-       fr_dict_attr_flags_t            flags;
-       fr_dict_attr_t const    *vendor_da;
-       fr_dict_attr_t          *new;
-
-       *out = NULL;
-
-       memset(&flags, 0, sizeof(flags));
-       flags.is_unknown = 1;
-       flags.is_raw = 1;
-       flags.type_size = 1;
-       flags.length = 1;
-
-       /*
-        *      Vendor attributes can occur under VSA or EVS attributes.
-        */
-       switch (parent->type) {
-       case PW_TYPE_VSA:
-       case PW_TYPE_EVS:
-               if (!fr_cond_assert(!parent->flags.is_unknown)) return -1;
-
-               vendor_da = fr_dict_attr_child_by_num(parent, vendor);
-               if (vendor_da) {
-                       if (!fr_cond_assert(vendor_da->type == PW_TYPE_VENDOR)) return -1;
-                       *out = vendor_da;
-                       return 0;
-               }
-               break;
-
-       /*
-        *      NOOP (maybe)
-        */
-       case PW_TYPE_VENDOR:
-               if (!fr_cond_assert(!parent->flags.is_unknown)) return -1;
-
-               if (parent->attr == vendor) {
-                       *out = parent;
-                       return 0;
-               }
-               fr_strerror_printf("Unknown vendor cannot be parented by another vendor");
-               return -1;
-
-       default:
-               fr_strerror_printf("Unknown vendors can only be parented by 'vsa' or 'evs' "
-                                  "attributes, not '%s'", fr_int2str(dict_attr_types, parent->type, "?Unknown?"));
-               return -1;
-       }
-
-       new = fr_dict_attr_alloc(ctx, "unknown-vendor", 0, vendor, PW_TYPE_VENDOR, flags);
-       new->parent = parent;
-       new->depth = parent->depth + 1;
-
-       *out = new;
-
-       return 0;
-}
-
-/** Build the tlv_stack for the specified DA and encode the path in OID form
- *
- * @param[out] out Where to write the OID.
- * @param[in] outlen Length of the output buffer.
- * @param[in] ancestor If not NULL, only print OID portion between ancestor and da.
- * @param[in] da to print OID string for.
- * @return the number of bytes written to the buffer.
- */
-size_t dict_print_attr_oid(char *out, size_t outlen,
-                          fr_dict_attr_t const *ancestor, fr_dict_attr_t const *da)
-{
-       size_t                  len;
-       char                    *p = out, *end = p + outlen;
-       int                     i;
-       int                     depth = 0;
-       fr_dict_attr_t const    *tlv_stack[FR_DICT_MAX_TLV_STACK + 1];
-
-       if (!outlen) return 0;
-
-       /*
-        *      If the ancestor and the DA match, there's
-        *      no OID string to print.
-        */
-       if (ancestor == da) {
-               out[0] = '\0';
-               return 0;
-       }
-
-       fr_proto_tlv_stack_build(tlv_stack, da);
-
-       if (ancestor) {
-               if (tlv_stack[ancestor->depth - 1] != ancestor) {
-                       fr_strerror_printf("Attribute \"%s\" is not a descendent of \"%s\"", da->name, ancestor->name);
-                       return -1;
-               }
-               depth = ancestor->depth;
-       }
-
-       /*
-        *      We don't print the ancestor, we print the OID
-        *      between it and the da.
-        */
-       len = snprintf(p, end - p, "%u", tlv_stack[depth]->attr);
-       if ((p + len) >= end) return p - out;
-       p += len;
-
-
-       for (i = depth + 1; i < (int)da->depth; i++) {
-               len = snprintf(p, end - p, ".%u", tlv_stack[i]->attr);
-               if ((p + len) >= end) return p - out;
-               p += len;
-       }
-
-       return p - out;
-}
-
 /** Initialises an unknown attribute
  *
  * Initialises a dict attr for an unknown attribute/vendor/type without adding
@@ -3022,8 +3013,8 @@ size_t dict_print_attr_oid(char *out, size_t outlen,
  * @param[in] vendor number.
  * @return 0 on success.
  */
-int fr_dict_unknown_from_fields(fr_dict_attr_t *da, fr_dict_attr_t const *parent, unsigned int vendor,
-                               unsigned int attr)
+static int fr_dict_unknown_from_fields(fr_dict_attr_t *da, fr_dict_attr_t const *parent,
+                                      unsigned int vendor, unsigned int attr)
 {
        char *p;
        size_t len = 0;
@@ -3068,8 +3059,9 @@ fr_dict_attr_t *fr_dict_unknown_afrom_fields(TALLOC_CTX *ctx, fr_dict_attr_t con
                                             unsigned int vendor, unsigned int attr)
 {
        uint8_t                 *p;
-       fr_dict_attr_t          *da;
-       fr_dict_attr_t const    *new_parent = NULL;
+       fr_dict_attr_t const    *da;
+       fr_dict_attr_t          *n;
+       fr_dict_attr_t          *new_parent = NULL;
 
        /*
         *      If there's a vendor specified, we check to see
@@ -3083,11 +3075,13 @@ fr_dict_attr_t *fr_dict_unknown_afrom_fields(TALLOC_CTX *ctx, fr_dict_attr_t con
         *      and we don't need to modify the parent.
         */
        if (vendor && ((parent->type == PW_TYPE_VSA) || (parent->type == PW_TYPE_EVS))) {
-               new_parent = fr_dict_attr_child_by_num(parent, vendor);
-               if (!new_parent && (fr_dict_unknown_vendor_afrom_num(ctx, &new_parent, parent, vendor) < 0)) {
-                       return NULL;
+               da = fr_dict_attr_child_by_num(parent, vendor);
+               if (!da) {
+                       if (fr_dict_unknown_vendor_afrom_num(ctx, &new_parent, parent, vendor) < 0) return NULL;
+                       da = new_parent;
                }
-               parent = new_parent;
+               parent = da;
+
        /*
         *      Need to clone the unknown hierachy, as unknown
         *      attributes must parent the complete heirachy,
@@ -3102,20 +3096,22 @@ fr_dict_attr_t *fr_dict_unknown_afrom_fields(TALLOC_CTX *ctx, fr_dict_attr_t con
        p = talloc_zero_array(ctx, uint8_t, FR_DICT_ATTR_SIZE);
        if (!p) {
                fr_strerror_printf("Out of memory");
-               fr_dict_unknown_free(&new_parent);
+               parent = new_parent;    /* Stupid const rules */
+               fr_dict_unknown_free(&parent);
                return NULL;
        }
-       da = (fr_dict_attr_t *)p;
-       talloc_set_type(da, fr_dict_attr_t);
+       n = (fr_dict_attr_t *)p;
+       talloc_set_type(n, fr_dict_attr_t);
 
        if (!fr_cond_assert(parent)) { /* coverity */
                talloc_free(p);
                return NULL;
        }
 
-       if (fr_dict_unknown_from_fields(da, parent, vendor, attr) < 0) {
+       if (fr_dict_unknown_from_fields(n, parent, vendor, attr) < 0) {
                talloc_free(p);
-               fr_dict_unknown_free(&new_parent);
+               parent = new_parent;    /* Stupid const rules */
+               fr_dict_unknown_free(&parent);
                return NULL;
        }
 
@@ -3124,362 +3120,294 @@ fr_dict_attr_t *fr_dict_unknown_afrom_fields(TALLOC_CTX *ctx, fr_dict_attr_t con
         *      unknown DA.  This should be OK as we never parent
         *      multiple unknown attributes off the same parent.
         */
-       if (new_parent && new_parent->flags.is_unknown) talloc_steal(da, new_parent);
+       if (new_parent && new_parent->flags.is_unknown) talloc_steal(n, new_parent);
 
-       return da;
+       return n;
 }
 
-/** Initialise a fr_dict_attr_t from an ASCII attribute and value
- *
- * Where the attribute name is in the form:
- *  - Attr-%d
- *  - Attr-%d.%d.%d...
+/** Build an unknown vendor, parented by a VSA or EVS attribute
  *
- * @copybrief fr_dict_unknown_from_fields
+ * This allows us to complete the path back to the dictionary root in the case
+ * of unknown attributes with unknown vendors.
  *
- * @note We can't validate attribute numbers here as a dictionary
- *      lookup is required to determine if the attribute
- *      has been marked as internal.
- *      Even validating numbers based on dv_type which is the
- *      length of the vendor field is wrong. Attribute number
- *      checks must be done by the caller.
+ * @note Will return known vendors attributes where possible.  Do not free directly,
+ *     use #fr_dict_unknown_free.
  *
- * @param[in] dict of protocol context we're operating in.  If NULL the internal
- *     dictionary will be used.
- * @param[in] vendor_da to initialise.
- * @param[in] da to initialise.
- * @param[in] parent of the unknown attribute (may also be unknown).
- * @param[in] name of attribute.
+ * @param[in] ctx to allocate the vendor attribute in.
+ * @param[out] out Where to write point to new unknown dict attr representing the unknown vendor.
+ * @param[in] parent of the vendor attribute, either an EVS or VSA attribute.
+ * @param[in] vendor id.
  * @return
  *     - 0 on success.
  *     - -1 on failure.
  */
-int fr_dict_unknown_from_oid(fr_dict_t *dict, fr_dict_attr_t *vendor_da, fr_dict_attr_t *da,
-                            fr_dict_attr_t const *parent, char const *name)
+int fr_dict_unknown_vendor_afrom_num(TALLOC_CTX *ctx, fr_dict_attr_t **out,
+                                    fr_dict_attr_t const *parent, unsigned int vendor)
 {
-       unsigned int    attr, vendor;
-       unsigned long   num;
-
-       char const      *p = name;
-       char            *q;
+       fr_dict_attr_flags_t    flags = {
+                                       .is_unknown = true,
+                                       .is_raw = true,
+                                       .type_size = true,
+                                       .length = true
+                               };
+       *out = NULL;
 
-       fr_dict_attr_t const    *child;
+       /*
+        *      Vendor attributes can occur under VSA or EVS attributes.
+        */
+       switch (parent->type) {
+       case PW_TYPE_VSA:
+       case PW_TYPE_EVS:
+               if (!fr_cond_assert(!parent->flags.is_unknown)) return -1;
 
-       INTERNAL_IF_NULL(dict);
+               *out = fr_dict_attr_alloc(ctx, parent, NULL, 0, vendor, PW_TYPE_VENDOR, &flags);
 
-       if (fr_dict_valid_name(name) < 0) return -1;
+               return 0;
 
-       if (vendor_da) memset(vendor_da, 0, sizeof(*vendor_da));
-       if (da) memset(da, 0, sizeof(*da));
+       case PW_TYPE_VENDOR:
+               if (!fr_cond_assert(!parent->flags.is_unknown)) return -1;
+               fr_strerror_printf("Unknown vendor cannot be parented by another vendor");
+               return -1;
 
-       /*
-        *      All raw attributes are of the form "Attr-#-#-#-#"
-        */
-       if (strncasecmp(p, "Attr-", 5) != 0) {
-               fr_strerror_printf("Unknown attribute '%s'", name);
+       default:
+               fr_strerror_printf("Unknown vendors can only be parented by 'vsa' or 'evs' "
+                                  "attributes, not '%s'", fr_int2str(dict_attr_types, parent->type, "?Unknown?"));
                return -1;
        }
 
-       num = strtoul(p + 5, &q, 10);
-       if (!num || (num >= UINT_MAX)) {
-               fr_strerror_printf("Invalid value in attribute name '%s'", name);
 
-               return -1;
-       }
+       return 0;
+}
 
-       attr = num;
-       vendor = 0;
+/** Initialise a fr_dict_attr_t from an ASCII attribute and value
+ *
+ * Where the attribute name is in the form:
+ *  - Attr-%d
+ *  - Attr-%d.%d.%d...
+ *
+ * @copybrief fr_dict_unknown_from_fields
+ *
+ * @param[in] ctx      to allocate the attribute in.
+ * @param[out] out     Where to write the new attribute to.
+ * @param[in] parent   of the unknown attribute (may also be unknown).
+ * @param[in] num      of the unknown attribute.
+ * @return
+ *     - 0 on success.
+ *     - -1 on failure.
+ */
+static int fr_dict_unknown_attr_afrom_num(TALLOC_CTX *ctx, fr_dict_attr_t **out,
+                                         fr_dict_attr_t const *parent, unsigned long num)
+{
+       fr_dict_attr_t          *da;
+       unsigned long           vendor = 0;
+       fr_dict_attr_flags_t    flags = {
+                                       .is_unknown = true,
+                                       .is_raw = true,
+                                       .is_pointer = true
+                               };
 
-       p = q;
+       if (parent->type == PW_TYPE_VENDOR) vendor = parent->attr;
 
-       /*
-        *      The common case: Attr-X,  Just create it and go.
-        */
-       if (!*p) {
-do_create:
-               if (!da) {
-                       fr_strerror_printf("Failed creating attribute");
-                       return -1;
-               }
+       da = fr_dict_attr_alloc(ctx, parent, NULL, vendor, num, PW_TYPE_OCTETS, &flags);
+       if (!da) return -1;
 
-               return fr_dict_unknown_from_fields(da, parent, 0, attr);
-       }
+       *out = da;
 
-       /*
-        *      Allow only Attr-%d.%d.%d
+       return 0;
+}
 
-        */
-       if (*p != '.') {
-       invalid:
-               fr_strerror_printf("Invalid OID");
-               return -1;
-       }
+/** Create a fr_dict_attr_t from an ASCII attribute and value
+ *
+ * Where the attribute name is in the form:
+ *  - Attr-%d
+ *  - Attr-%d.%d.%d...
+ *
+ * @copybrief fr_dict_unknown_from_fields
+ *
+ * @note If vendor != 0, an unknown vendor (may) also be created, parented by
+ *     the correct EVS or VSA attribute. This is accessible via vp->parent,
+ *     and will be use the unknown da as its talloc parent.
+ *
+ * @param[in] ctx      to alloc new attribute in.
+ * @param[out] out     Where to write the head of the chain unknown dictionary attributes.
+ * @param[in] parent   Attribute to use as the root for resolving OIDs in.  Usually
+ *                     the root of a protocol dictionary.
+ * @param[in] oid_str  of attribute.
+ * @return
+ *     - The number of bytes parsed on success.
+ *     - <= 0 on failure.  Negative offset indicates parse error position.
+ */
+ssize_t fr_dict_unknown_afrom_oid_str(TALLOC_CTX *ctx, fr_dict_attr_t **out,
+                                     fr_dict_attr_t const *parent, char const *oid_str)
+{
+       char const              *p = oid_str, *end = oid_str + strlen(oid_str);
+       char                    *q;
+       fr_dict_attr_t const    *our_parent = parent;
+       TALLOC_CTX              *top_ctx = NULL, *our_ctx = ctx;
 
-       /*
-        *      Look for OIDs.  Require the "Attr-26.Vendor-Id.type"
-        *      format.
-        *
-        *      This section parses the Vendor-Id portion of
-        *      Attr-%d.%d.  where the first number is 26, *or* an
-        *      extended name of the "evs" found type.
-        */
-       child = fr_dict_attr_child_by_num(parent, attr);
-       if (!child) {
-fail:
-               fr_strerror_printf("Cannot parse names without dictionaries");
-               return -1;
-       }
+       fr_dict_attr_t          *n = NULL;
 
-       switch (child->type) {
-       case PW_TYPE_STRUCTURAL:
-               break;
+       *out = NULL;
 
-       default:
-               fr_strerror_printf("Attributes of simple data types cannot use OIDs");
-               return -1;
-       }
+       if (fr_dict_valid_name(oid_str) < 0) return -1;
 
        /*
-        *      Attr-241.X
-        *
-        *      X may be 26, in which case we allow unknown vendors under it.
+        *      All raw attributes are of the form "Attr-#-#-#-#"
         */
-       if ((child->type == PW_TYPE_EXTENDED) || (child->type == PW_TYPE_LONG_EXTENDED)) {
-               parent = child;
+       if (strncasecmp(p, "Attr-", 5) != 0) {
+               fr_strerror_printf("Unknown attribute '%s'", oid_str);
+               return 0;
+       }
 
-               num = strtoul(p + 5, &q, 10);
-               if (!num || (num >= UINT_MAX)) {
-                       fr_strerror_printf("Invalid value in attribute name '%s'", name);
-                       return -1;
-               }
+       p += 5;
 
-               attr = num;
-               p = q;
+       do {
+               unsigned long           num;
+               fr_dict_attr_t const    *da = NULL;
 
-               /*
-                *      The common case: Attr-241.X,  Just create it and go.
-                */
-               if (!*p) goto do_create;
+               num = strtoul(p, &q, 10);
+               if ((p == q) || ((*q != '\0') && (*q != '.'))) {
+                       fr_strerror_printf("OID component must be an integer");
+               error:
+                       talloc_free(top_ctx);
+                       return -(p - oid_str);
+               }
+               p = q + 1;
 
+               switch (*q) {
                /*
-                *      Attr-241.X.  Does X exist?
+                *      Structural attribute
                 */
-               child = fr_dict_attr_child_by_num(parent, attr);
-               if (!child) goto fail;
+               case '.':
+                       da = fr_dict_attr_child_by_num(our_parent, num);
+                       if (!da) {      /* Unknown component */
+                               if (!our_parent) goto is_root;
 
-               /*
-                *      Fall through to checking for EVS.
-                */
-       }
+                               switch (our_parent->type) {
+                               case PW_TYPE_EVS:
+                               case PW_TYPE_VSA:
+                                       da = fr_dict_attr_child_by_num(our_parent, num);
+                                       if (!fr_cond_assert(!da || (da->type == PW_TYPE_VENDOR))) goto error;
+
+                                       if (!da) {
+                                               if (fr_dict_unknown_vendor_afrom_num(our_ctx, &n,
+                                                                                    our_parent, num) < 0) {
+                                                       goto error;
+                                               }
+                                               da = n;
+                                       }
+                                       break;
 
-       /*
-        *      Attr-26 means that the following data is a 32-bit vendor ID.
-        */
-       if ((child->type == PW_TYPE_VSA) ||
-           (child->type == PW_TYPE_EVS)) {
-               fr_dict_attr_t const *dv;
+                               case PW_TYPE_TLV:
+                               case PW_TYPE_EXTENDED:
+                               case PW_TYPE_LONG_EXTENDED:
+                               is_root:
+                                       if (fr_dict_unknown_attr_afrom_num(our_ctx, &n, our_parent, num) < 0) {
+                                               goto error;
+                                       }
 
-               num = strtoul(p + 1, &q, 10);
-               if (!num || (num >=  UINT_MAX)) {
-                       fr_strerror_printf("Invalid vendor");
+                                       da = n;
+                                       break;
 
-                       return -1;
-               }
-               vendor = num;
+                               /*
+                                *      Can't have a PW_TYPE_STRING inside a
+                                *      PW_TYPE_STRING (for example)
+                                */
+                               default:
+                                       fr_strerror_printf("Previous OID component specified a non-structural type");
+                                       goto error;
+                               }
+                       }
+                       our_parent = da;
 
-               /*
-                *      &Attr-26.11344 is invalid.
-                */
-               if (*q != '.') goto invalid;
+                       if (n && n->flags.is_unknown) {
+                               if (top_ctx == NULL) top_ctx = n;       /* Track first unknown */
+                               our_ctx = n;
+                       }
 
-               p = q;
 
-               attr = 0;
+                       break;
 
                /*
-                *      See if there is a vendor already defined.  If
-                *      not, we may be able to add one.
+                *      Leaf attribute
                 */
-               dv = fr_dict_attr_child_by_num(child, vendor);
-               if (dv) {
-                       child = dv;
-
-               } else {
-                       if (!vendor_da) {
-                               fr_strerror_printf("Unknown vendor %u", vendor);
-                               return -1;
-                       }
-
-                       vendor_da->attr = vendor;
-                       vendor_da->type = PW_TYPE_VENDOR;
-                       vendor_da->parent = child;
-                       vendor_da->depth = child->depth + 1;
-                       vendor_da->flags.is_unknown = 1;
-                       vendor_da->flags.is_raw = 1;
-                       vendor_da->flags.type_size = 1;
-                       vendor_da->flags.length = 1;
-                       snprintf(vendor_da->name, FR_DICT_ATTR_MAX_NAME_LEN, "Vendor-%u", vendor);
-
-                       child = vendor_da;
+               case '\0':
+                       if (fr_dict_unknown_attr_afrom_num(our_ctx, &n, our_parent, num) < 0) goto error;
+                       da = n;
+                       break;
                }
-       }
-
-       /*
-        *      Reparent the attribute based on the child we found.
-        */
-       parent = child;
+       } while (p < end);
 
-       if (*p == '.') {
-               if (fr_dict_attr_by_oid(dict, &parent, &vendor, &attr, p + 1) < 0) {
-                       return -1;
-               }
-       }
+       if (!n) return 0;
 
        /*
-        *      If the caller doesn't provide a fr_dict_attr_t
-        *      we can't call fr_dict_unknown_from_fields.
+        *      Invert the talloc hierarchy, so that if the unknown
+        *      attribute is freed, any unknown parents are also freed.
         */
-       if (!da) {
-               fr_strerror_printf("Unknown attributes disallowed");
-               return -1;
-       }
-
-       return fr_dict_unknown_from_fields(da, parent, vendor, attr);
-}
-
-/** Create a fr_dict_attr_t from an ASCII attribute and value
- *
- * Where the attribute name is in the form:
- *  - Attr-%d
- *  - Attr-%d.%d.%d...
- *  - Vendor-%d-Attr-%d
- *  - VendorName-Attr-%d
- *
- * @copybrief fr_dict_unknown_from_fields
- *
- * @note If vendor != 0, an unknown vendor (may) also be created, parented by
- *     the correct EVS or VSA attribute. This is accessible via vp->parent,
- *     and will be use the unknown da as its talloc parent.
- *
- * @param[in] dict of protocol context we're operating in.  If NULL the internal
- *     dictionary will be used.
- * @param[in] ctx to alloc new attribute in.
- * @param[in] parent Attribute to use as the root for resolving OIDs in.  Usually
- *     the root of a protocol dictionary.
- * @param[in] name of attribute.
- * @return
- *     - 0 on success.
- *     - -1 on failure.
- */
-fr_dict_attr_t const *fr_dict_unknown_afrom_oid(TALLOC_CTX *ctx, fr_dict_t *dict,
-                                               fr_dict_attr_t const *parent, char const *name)
-{
-       uint8_t                 *p;
-       uint8_t                 vendor_buff[FR_DICT_ATTR_SIZE];
-       fr_dict_attr_t          *vendor = (fr_dict_attr_t *)&vendor_buff;
-       fr_dict_attr_t          *da;
-       fr_dict_attr_t const    *new_parent = NULL;
+       for (our_parent = n->parent, our_ctx = n;
+            our_parent && our_parent->flags.is_unknown;
+            our_parent = our_parent->parent) {
+               fr_dict_attr_t *tmp;
 
-       p = talloc_zero_array(ctx, uint8_t, FR_DICT_ATTR_SIZE);
-       if (!p) {
-               fr_strerror_printf("Out of memory");
-               return NULL;
-       }
-       da = (fr_dict_attr_t *)p;
-       talloc_set_type(da, fr_dict_attr_t);
+               memcpy(&tmp, &our_parent, sizeof(tmp));                 /* const issues *sigh* */
 
-       if (fr_dict_unknown_from_oid(dict, vendor, da, parent, name) < 0) {
-               talloc_free(p);
-               return NULL;
+               our_ctx = talloc_steal(our_ctx, tmp);
        }
 
-       /*
-        *      Unknown attributes are always rooted in known
-        *      attributes, so we don't need to clone anything
-        *      here.
-        */
-       if (vendor->flags.is_unknown) {
-               new_parent = fr_dict_unknown_acopy(p, vendor);
-               if (!new_parent) {
-                       talloc_free(p);
-                       return NULL;
-               }
-               da->parent = new_parent;
-       /*
-        *      Need to clone the unknown hierachy, as unknown
-        *      attributes must parent the complete heirachy,
-        *      and cannot share any parts with any other unknown
-        *      attributes.
-        */
-       } else if (parent->flags.is_unknown) {
-               new_parent = fr_dict_unknown_acopy(ctx, parent);
-               da->parent = new_parent;
-
-               /*
-                *      Ensure the parent is freed at the same time as the
-                *      unknown DA.  This should be OK as we never parent
-                *      multiple unknown attributes off the same parent.
-                */
-               if (new_parent->flags.is_unknown) talloc_steal(da, new_parent);
-       }
+       VERIFY_DA(n);
 
-       VERIFY_DA(da);
+       *out = n;
 
-       return da;
+       return end - oid_str;
 }
 
 /** Create a dictionary attribute by name embedded in another string
  *
- * Find the first invalid attribute name char in the string pointed
- * to by name.
+ * Find the first invalid attribute name char in the string pointed to by name.
  *
- * Copy the characters between the start of the name string and the first
- * none dict_attr_allowed_char to a buffer and initialise da as an
- * unknown attribute.
+ * Copy the characters between the start of the name string and the first none
+ * #dict_attr_allowed_char to a buffer and initialise da as an unknown attribute.
  *
- * @param[in] dict of protocol context we're operating in.  If NULL the internal
- *     dictionary will be used.
- * @param[out] vendor_da will be filled in if a vendor is found.
- * @param[out] da will be filled in with the da at the end of the OID chain.
- * @param[in]  parent Attribute to use as the root for resolving OIDs in.  Usually
- *     the root of a protocol dictionary.
- * @param[in,out] name string start.
+ * @param[in] ctx      To allocate unknown #fr_dict_attr_t in.
+ * @param[out] out     Where to write the head of the chain unknown dictionary attributes.
+ * @param[in] parent   Attribute to use as the root for resolving OIDs in.  Usually
+ *                     the root of a protocol dictionary.
+ * @param[in] name     string start.
  * @return
- *     - 0 on success.
- *     - -1 on failure.
+ *     - <= 0 on failure.
+ *     - The number of bytes of name consumed on success.
  */
-int fr_dict_unknown_from_suboid(fr_dict_t *dict, fr_dict_attr_t *vendor_da, fr_dict_attr_t *da,
-                               fr_dict_attr_t const *parent, char const **name)
+ssize_t fr_dict_unknown_afrom_oid_substr(TALLOC_CTX *ctx, fr_dict_attr_t **out,
+                                        fr_dict_attr_t const *parent, char const *name)
 {
-       char const *p;
-       size_t len;
-       char buffer[FR_DICT_ATTR_MAX_NAME_LEN + 1];
+       char const      *p;
+       size_t          len;
+       char            buffer[FR_DICT_ATTR_MAX_NAME_LEN + 1];
+       ssize_t         slen;
 
-       if (!name || !*name) return -1;
-       INTERNAL_IF_NULL(dict);
+       if (!name || !*name) return 0;
 
        /*
         *      Advance p until we get something that's not part of
         *      the dictionary attribute name.
         */
-       for (p = *name; fr_dict_attr_allowed_chars[(int)*p] || (*p == '.') || (*p == '-'); p++);
+       for (p = name; fr_dict_attr_allowed_chars[(int)*p] || (*p == '.') || (*p == '-'); p++);
 
-       len = p - *name;
+       len = p - name;
        if (len > FR_DICT_ATTR_MAX_NAME_LEN) {
                fr_strerror_printf("Attribute name too long");
-               return -1;
+               return 0;
        }
        if (len == 0) {
                fr_strerror_printf("Invalid attribute name");
-               return -1;
+               return 0;
        }
-       strlcpy(buffer, *name, len + 1);
+       strlcpy(buffer, name, len + 1);
 
-       if (fr_dict_unknown_from_oid(dict, vendor_da, da, parent, buffer) < 0) return -1;
+       slen = fr_dict_unknown_afrom_oid_str(ctx, out, parent, buffer);
+       if (slen <= 0) return slen;
 
-       *name = p;
-
-       return 0;
+       return p - name;
 }
 
 
index ba036f754c45752441f1c9318227b41359ec3aa3..7d50165506838bd5d4497160d364ab5277134fc8 100644 (file)
@@ -228,18 +228,20 @@ VALUE_PAIR *fr_pair_copy(TALLOC_CTX *ctx, VALUE_PAIR const *vp)
        memcpy(n, vp, sizeof(*n));
 
        /*
-        *      If the DA is unknown, steal "n" to "ctx".  This does
-        *      nothing for "n", but will also copy the unknown "da".
+        *      Copy the unknown attribute hierarchy
         */
        if (n->da->flags.is_unknown) {
-               fr_pair_steal(ctx, n);
-       }
+               n->da = fr_dict_unknown_acopy(n, n->da);
+               if (!n->da) talloc_free(n);
 
+               return NULL;
+       }
        n->next = NULL;
 
        /*
-        *      If it's an xlat, copy the raw string and return early,
-        *      so we don't pre-expand or otherwise mangle the VALUE_PAIR.
+        *      If it's an xlat, copy the raw string and return
+        *      early, so we don't pre-expand or otherwise mangle
+        *      the VALUE_PAIR.
         */
        if (vp->type == VT_XLAT) {
                n->xlat = talloc_typed_strdup(n, n->xlat);
@@ -318,16 +320,17 @@ static VALUE_PAIR *fr_pair_make_unknown(TALLOC_CTX *ctx,
        ssize_t                 len;
        VALUE_PAIR              *vp, *vp2;
        fr_dict_attr_t const    *da;
+       fr_dict_attr_t          *n;
        vp_cursor_t             cursor;
 
        vp = fr_pair_alloc(ctx);
        if (!vp) return NULL;
 
-       vp->da = fr_dict_unknown_afrom_oid(ctx, fr_dict_internal, fr_dict_root(fr_dict_internal), attribute);
-       if (!vp->da) {
+       if (fr_dict_unknown_afrom_oid_str(vp, &n, fr_dict_root(fr_dict_internal), attribute) <= 0) {
                talloc_free(vp);
                return NULL;
        }
+       vp->da = n;
 
        /*
         *      No value.  Nothing more to do.
index 8d84fa8177eaf1fb271a615974a4a81343b80bd4..5c16df87befd11778f86db84f7e694cbe7d292e2 100644 (file)
@@ -797,6 +797,7 @@ static ssize_t decode_vsa(TALLOC_CTX *ctx, vp_cursor_t *cursor, fr_dict_attr_t c
         */
        vendor_da = fr_dict_attr_child_by_num(parent, vendor);
        if (!vendor_da) {
+               fr_dict_attr_t *n;
                /*
                 *      RFC format is 1 octet type, 1 octet length
                 */
@@ -805,7 +806,8 @@ static ssize_t decode_vsa(TALLOC_CTX *ctx, vp_cursor_t *cursor, fr_dict_attr_t c
                        return -1;
                }
 
-               if (fr_dict_unknown_vendor_afrom_num(ctx, &vendor_da, parent, vendor) < 0) return -1;
+               if (fr_dict_unknown_vendor_afrom_num(ctx, &n, parent, vendor) < 0) return -1;
+               vendor_da = n;
 
                /*
                 *      Create an unknown DV too...
index cca898283ff8c387a500744bc77ebfc031a73f20..e2fce56c0d7c1bdf3ba88cf6bc4f46c6a6301a18 100644 (file)
@@ -642,7 +642,7 @@ int tmpl_afrom_value_box(TALLOC_CTX *ctx, vp_tmpl_t **out, value_box_t *data, bo
  * @param[in] list_def The default list to set if no #pair_lists qualifiers are found in
  *     name.
  * @param[in] allow_unknown If true attributes in the format accepted by
- *     #fr_dict_unknown_from_suboid will be allowed, even if they're not in the main
+ *     #fr_dict_unknown_afrom_suboid will be allowed, even if they're not in the main
  *     dictionaries.
  *     If an unknown attribute is found a #TMPL_TYPE_ATTR #vp_tmpl_t will be
  *     produced with the unknown #fr_dict_attr_t stored in the ``unknown.da`` buffer.
@@ -717,22 +717,16 @@ ssize_t tmpl_afrom_attr_substr(TALLOC_CTX *ctx, vp_tmpl_t **out, char const *nam
 
        vpt->tmpl_da = fr_dict_attr_by_name_substr(NULL, &p);
        if (!vpt->tmpl_da) {
-               char const *a;
                char const *q;
 
-               /*
-                *      Record start of attribute in case we need to error out.
-                */
-               a = p;
-
                fr_strerror();  /* Clear out any existing errors */
 
+               slen = fr_dict_unknown_afrom_oid_substr(vpt, &vpt->tmpl_unknown,
+                                                       fr_dict_root(fr_dict_internal), p);
                /*
                 *      Attr-1.2.3.4 is OK.
                 */
-               if (fr_dict_unknown_from_suboid(NULL, (fr_dict_attr_t *)&vpt->tmpl_unknown_vendor,
-                                               (fr_dict_attr_t *)&vpt->tmpl_unknown, fr_dict_root(fr_dict_internal),
-                                               &p) == 0) {
+               if (slen > 0) {
                        /*
                         *      Check what we just parsed really hasn't been defined
                         *      in the main dictionaries.
@@ -740,7 +734,7 @@ ssize_t tmpl_afrom_attr_substr(TALLOC_CTX *ctx, vp_tmpl_t **out, char const *nam
                         *      If it has, parsing is the same as if the attribute
                         *      name had been used instead of its OID.
                         */
-                       vpt->tmpl_da = fr_dict_attr_by_name(NULL, a);
+                       vpt->tmpl_da = fr_dict_attr_by_name(NULL, p);
                        if (vpt->tmpl_da) {
                                vpt->auto_converted = true;
                                goto do_num;
@@ -748,7 +742,7 @@ ssize_t tmpl_afrom_attr_substr(TALLOC_CTX *ctx, vp_tmpl_t **out, char const *nam
 
                        if (!allow_unknown) {
                                fr_strerror_printf("Unknown attribute");
-                               slen = -(a - name);
+                               slen = -(p - name);
                                goto error;
                        }
 
@@ -756,8 +750,10 @@ ssize_t tmpl_afrom_attr_substr(TALLOC_CTX *ctx, vp_tmpl_t **out, char const *nam
                         *      Unknown attributes can't be encoded, as we don't
                         *      know how to encode them!
                         */
-                       ((fr_dict_attr_t *)vpt->tmpl_unknown)->flags.internal = 1;
-                       vpt->tmpl_da = (fr_dict_attr_t *)&vpt->tmpl_unknown;
+                       vpt->tmpl_unknown->flags.internal = 1;
+                       vpt->tmpl_da = vpt->tmpl_unknown;
+
+                       p += slen;
 
                        goto do_num; /* unknown attributes can't have tags */
                }
@@ -771,7 +767,7 @@ ssize_t tmpl_afrom_attr_substr(TALLOC_CTX *ctx, vp_tmpl_t **out, char const *nam
                 */
                if (!allow_undefined) {
                        fr_strerror_printf("Undefined attributes not allowed here");
-                       slen = -(a - name);
+                       slen = -(p - name);
                        goto error;
                }
 
@@ -881,9 +877,7 @@ finish:
         *      Copy over the attribute definition, now we're
         *      sure what we were passed is valid.
         */
-       if ((vpt->type == TMPL_TYPE_ATTR) && vpt->tmpl_da->flags.is_unknown) {
-               vpt->tmpl_da = (fr_dict_attr_t *)&vpt->tmpl_unknown;
-       }
+       if ((vpt->type == TMPL_TYPE_ATTR) && vpt->tmpl_da->flags.is_unknown) vpt->tmpl_da = vpt->tmpl_unknown;
 
        VERIFY_TMPL(vpt);       /* Because we want to ensure we produced something sane */
 
@@ -908,7 +902,7 @@ ssize_t tmpl_afrom_attr_str(TALLOC_CTX *ctx, vp_tmpl_t **out, char const *name,
        slen = tmpl_afrom_attr_substr(ctx, out, name, request_def, list_def, allow_unknown, allow_undefined);
        if (slen <= 0) return slen;
 
-       if (name[slen] != '\0') {
+       if (slen != (ssize_t)strlen(name)) {
                /* This looks wrong, but it produces meaningful errors for unknown attrs with tags */
                fr_strerror_printf("Unexpected text after %s", fr_int2str(tmpl_names, (*out)->type, "<INVALID>"));
                return -slen;
@@ -2514,10 +2508,10 @@ void tmpl_verify(char const *file, int line, vp_tmpl_t const *vpt)
                }
 
                if (vpt->tmpl_da->flags.is_unknown) {
-                       if (vpt->tmpl_da != (fr_dict_attr_t const *)&vpt->data.attribute.unknown.da) {
+                       if (vpt->tmpl_da != vpt->tmpl_unknown) {
                                FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_ATTR "
-                                            "da is marked as unknown, but does not point to the template's "
-                                            "unknown da buffer", file, line);
+                                            "da is marked as unknown, but address is not equal to the template's "
+                                            "unknown da pointer", file, line);
                                if (!fr_cond_assert(0)) fr_exit_now(1);
                        }
 
index f0645c1c46d02ead0037272b883ebc6e15969080..a8a1cf8b4f112d866cf1a49d922ccd74f68d3986 100644 (file)
@@ -6,12 +6,3 @@ data fr_radius_decode_pair: Insufficient data
 
 decode 01 01 00
 data fr_radius_decode_pair: Insufficient data
-
-encode Attr-240.1 = 0x01
-data Attributes of simple data types cannot use OIDs
-
-encode Attr-26.1.1 = 0x01
-data 1a 09 00 00 00 01 01 03 01
-
-encode Attr-26.1344.1 = 0x01020304
-data 1a 0c 00 00 05 40 01 06 01 02 03 04
diff --git a/src/tests/unit/unknown.txt b/src/tests/unit/unknown.txt
new file mode 100644 (file)
index 0000000..d0ae304
--- /dev/null
@@ -0,0 +1,17 @@
+encode Attr-240.1.1 = 0x01
+data Previous OID component specified a non-structural type
+
+encode Attr-240.26.1.1.1 = 0x01
+data Previous OID component specified a non-structural type
+
+encode Attr-246.26.1.1.1 = 0x01
+data Previous OID component specified a non-structural type
+
+encode Attr-240.9999999999999999999.1.1 = 0x01
+data Previous OID component specified a non-structural type
+
+encode Attr-26.1.1 = 0x01
+data 1a 09 00 00 00 01 01 03 01
+
+encode Attr-26.1344.1 = 0x01020304
+data 1a 0c 00 00 05 40 01 06 01 02 03 04
\ No newline at end of file