]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
add FR_TYPE_ATTR
authorAlan T. DeKok <aland@freeradius.org>
Mon, 28 Jul 2025 19:44:37 +0000 (15:44 -0400)
committerAlan T. DeKok <aland@freeradius.org>
Mon, 28 Jul 2025 21:47:01 +0000 (17:47 -0400)
no tests.  will do soon.

13 files changed:
src/bin/unit_test_attribute.c
src/lib/json/json.c
src/lib/unlang/xlat_builtin.c
src/lib/util/htrie.h
src/lib/util/pair.h
src/lib/util/types.c
src/lib/util/types.h
src/lib/util/value.c
src/lib/util/value.h
src/modules/rlm_lua/lua.c
src/modules/rlm_mruby/mruby.c
src/modules/rlm_perl/rlm_perl.c
src/modules/rlm_python/rlm_python.c

index c9a7790205178773393b674781293ae74340be82..8ede049f2bee46dcd9d52f779d9233b0065f2d36 100644 (file)
@@ -1045,13 +1045,14 @@ static int dictionary_load_common(command_result_t *result, command_file_ctx_t *
        RETURN_OK(0);
 }
 
-static size_t parse_typed_value(command_result_t *result, fr_value_box_t *box, char const **out, char const *in, size_t inlen)
+static size_t parse_typed_value(command_result_t *result, command_file_ctx_t *cc, fr_value_box_t *box, char const **out, char const *in, size_t inlen)
 {
        fr_type_t       type;
        size_t          match_len;
        ssize_t         slen;
        char const      *p;
        fr_sbuff_t      sbuff;
+       fr_dict_attr_t const *enumv = NULL;
 
        /*
         *      Parse data types
@@ -1074,7 +1075,7 @@ static size_t parse_typed_value(command_result_t *result, fr_value_box_t *box, c
        if (*p == '"'){
                p++;
                sbuff = FR_SBUFF_IN(p, strlen(p));
-               slen = fr_value_box_from_substr(box, box, FR_TYPE_STRING, NULL,
+               slen = fr_value_box_from_substr(box, box, FR_TYPE_STRING, enumv,
                                                &sbuff,
                                                &value_parse_rules_double_quoted);
                if (slen < 0) {
@@ -1096,7 +1097,23 @@ static size_t parse_typed_value(command_result_t *result, fr_value_box_t *box, c
        } else {
                sbuff = FR_SBUFF_IN(p, strlen(p));
 
-               slen = fr_value_box_from_substr(box, box, type, NULL,
+               /*
+                *      We have no other way to pass the dict to the value-box parse function.
+                */
+               if (type == FR_TYPE_ATTR) {
+                       fr_dict_t const *dict = dictionary_current(cc);
+
+                       if (!dict) {
+                               fr_strerror_const("proto-dictionary must be defined");
+                               RETURN_PARSE_ERROR(0);
+                       }
+
+                       enumv = fr_dict_root(dict);
+               }
+
+               fprintf(stderr, "PARSE TYPE %s %p\n", fr_type_to_str(type), enumv);
+
+               slen = fr_value_box_from_substr(box, box, type, enumv,
                                                &sbuff,
                                                &value_parse_rules_bareword_unquoted);
                if (slen < 0) {
@@ -1239,7 +1256,7 @@ static size_t command_calc(command_result_t *result, command_file_ctx_t *cc,
        p = in;
        end = in + inlen;
 
-       match_len = parse_typed_value(result, a, &value, p, end - p);
+       match_len = parse_typed_value(result, cc, a, &value, p, end - p);
        if (match_len == 0) return 0; /* errors have already been updated */
 
        p += match_len;
@@ -1262,7 +1279,7 @@ static size_t command_calc(command_result_t *result, command_file_ctx_t *cc,
        }
        fr_skip_whitespace(p);
 
-       match_len = parse_typed_value(result, b, &value, p, end - p);
+       match_len = parse_typed_value(result, cc, b, &value, p, end - p);
        if (match_len == 0) return 0;
 
        p += match_len;
@@ -1338,7 +1355,7 @@ static size_t command_calc_nary(command_result_t *result, command_file_ctx_t *cc
 
                a = talloc_zero(group, fr_value_box_t);
 
-               match_len = parse_typed_value(result, a, &value, p, end - p);
+               match_len = parse_typed_value(result, cc, a, &value, p, end - p);
                if (match_len == 0) return 0; /* errors have already been updated */
 
                fr_value_box_list_insert_tail(&group->vb_group, a);
@@ -1386,7 +1403,7 @@ static size_t command_cast(command_result_t *result, command_file_ctx_t *cc,
        p = in;
        end = in + inlen;
 
-       match_len = parse_typed_value(result, a, &value, p, end - p);
+       match_len = parse_typed_value(result, cc, a, &value, p, end - p);
        if (match_len == 0) return 0; /* errors have already been updated */
 
        p += match_len;
@@ -2816,7 +2833,7 @@ static size_t command_value_box_normalise(command_result_t *result, command_file
        ssize_t         slen;
        fr_type_t       type;
 
-       match_len = parse_typed_value(result, box, &value, in, strlen(in));
+       match_len = parse_typed_value(result, cc, box, &value, in, strlen(in));
        if (match_len == 0) {
                talloc_free(box);
                return 0;       /* errors have already been updated */
@@ -2844,7 +2861,7 @@ static size_t command_value_box_normalise(command_result_t *result, command_file
         *      box as last time.
         */
        box2 = talloc_zero(NULL, fr_value_box_t);
-       if (fr_value_box_from_str(box2, box2, type, NULL,
+       if (fr_value_box_from_str(box2, box2, type, box->enumv,
                                  data, slen,
                                  &fr_value_unescape_double) < 0) {
                talloc_free(box2);
index 079588c1d73de9f9c6f953de2540117a8f082530..595008430eab2f4cc238242e23c6fe146bdf469b 100644 (file)
@@ -443,6 +443,7 @@ fr_slen_t fr_json_str_from_value(fr_sbuff_t *out, fr_value_box_t *vb, bool inclu
                fr_strerror_const("Structural boxes not yet supported");
                return -1;
 
+       case FR_TYPE_ATTR:
        case FR_TYPE_INTERNAL:
                fr_strerror_printf("Box type %s cannot be converted to string", fr_type_to_str(vb->type));
                return -1;
index f9b869e969848fa36b56c257d791afa01bc211f0..98ba109544ee1c7459a537e80db2e9b54a19c619 100644 (file)
@@ -366,6 +366,7 @@ static int CC_HINT(nonnull(2,3)) filename_xlat_escape(UNUSED request_t *request,
        case FR_TYPE_IPV4_PREFIX:
        case FR_TYPE_IPV6_ADDR:
        case FR_TYPE_IPV6_PREFIX:
+       case FR_TYPE_ATTR:
                /*
                 *      Printing prefixes etc. does NOT result in the escape function being called!  So
                 *      instead, we cast the results to a string, and then escape the string.
index 0ed68ef27d14e12cae93a991ea6af66d7276716d..191ae182267657d538816be4dbf8c531a0cea2d4 100644 (file)
@@ -164,6 +164,7 @@ static inline fr_htrie_type_t fr_htrie_hint(fr_type_t type)
        case FR_TYPE_FLOAT64:
                return FR_HTRIE_RB;
 
+       case FR_TYPE_ATTR:
        case FR_TYPE_NON_LEAF:
                break;
        }
index eccaaa407a53eae62e03939630bba741f4bd2ab2..9b5db0ee7401e1e88a174ca27110a0af5b6631e0 100644 (file)
@@ -131,6 +131,8 @@ struct value_pair_s {
 #define vp_float32             data.vb_float32
 #define vp_float64             data.vb_float64
 
+#define vp_attr                        data.vb_attr
+
 #define vp_date                        data.vb_date
 #define vp_time_delta          data.vb_time_delta
 
index 105000b6b5e24670fa401eef416f716bb606b1d0..55ed550bb7ce86826da5f15cf6353ac8d9f65067 100644 (file)
@@ -70,6 +70,8 @@ fr_table_num_ordered_t const fr_type_table[] = {
        { L("vendor"),          FR_TYPE_VENDOR          },
        { L("group"),           FR_TYPE_GROUP           },
 
+       { L("attribute"),       FR_TYPE_ATTR            },
+
        /*
         *      Alternative names
         */
@@ -119,6 +121,7 @@ static char const *fr_type_to_c_type[] = {
        [FR_TYPE_TIME_DELTA]                    = "fr_time_delta_t",
        [FR_TYPE_SIZE]                          = "size_t",
        [FR_TYPE_VALUE_BOX]                     = "fr_value_box_t",
+       [FR_TYPE_ATTR]                          = "fr_dict_attr_t *",
        [FR_TYPE_VOID]                          = "void *",
 
        [FR_TYPE_VALUE_BOX_CURSOR]              = "fr_dcursor_t *",
@@ -162,6 +165,7 @@ static size_t const fr_type_to_c_size[] = {
        [FR_TYPE_TIME_DELTA]                    = sizeof(fr_time_delta_t),
        [FR_TYPE_SIZE]                          = sizeof(size_t),
        [FR_TYPE_VALUE_BOX]                     = sizeof(fr_value_box_t),
+       [FR_TYPE_ATTR]                          = sizeof(fr_dict_attr_t *),
        [FR_TYPE_VOID]                          = sizeof(void *),
 
        [FR_TYPE_VALUE_BOX_CURSOR]              = sizeof(void *),
index 0c9dadd2d22167a59e0729c9d13579a6edba8a8b..00d91f6a3656ae989a8a6c484fb8eb7a3e332fc5 100644 (file)
@@ -80,6 +80,7 @@ typedef enum {
 
        FR_TYPE_GROUP,                          //!< A grouping of other attributes
        FR_TYPE_VALUE_BOX,                      //!< A boxed value.
+       FR_TYPE_ATTR,                           //!< A contains an attribute reference
 
        FR_TYPE_VOID,                           //!< User data.  Should be a talloced chunk
                                                ///< assigned to the ptr value of the union.
@@ -256,6 +257,7 @@ typedef enum {
 #define FR_TYPE_LEAF_DEF(_beg, _mid, _end) \
        _beg(FR_TYPE_ETHERNET) \
        _mid(FR_TYPE_IFID) \
+       _mid(FR_TYPE_ATTR) \
        FR_TYPE_IP_DEF(_mid, _mid, _mid) \
        FR_TYPE_VARIABLE_SIZE_DEF(_mid, _mid, _mid) \
        FR_TYPE_NUMERIC_DEF(_mid, _mid, _end)
index 44d5334fd515652a16dde2813361bf6d9453574a..5edffdb0a515656211e5b9435a248a09666e224f 100644 (file)
@@ -829,6 +829,9 @@ int8_t fr_value_box_cmp(fr_value_box_t const *a, fr_value_box_t const *b)
        case FR_TYPE_NULL:      /* NULLs are not comparable */
                return -2;
 
+       case FR_TYPE_ATTR:
+               return CMP(a->vb_attr, b->vb_attr);
+
        /*
         *      These should be handled at some point
         */
@@ -1631,6 +1634,48 @@ ssize_t fr_value_box_to_network(fr_dbuff_t *dbuff, fr_value_box_t const *value)
        }
                break;
 
+       case FR_TYPE_ATTR:
+       {
+               fr_value_box_t tmp, base;
+
+               /*
+                *      For now, we only encode at depth 1.  The protocol-specific encoders need to do
+                *      something special for attributes at other depths.
+                */
+               if (value->vb_attr->depth != 1) {
+                       fr_strerror_printf("Unsupported depth '%u' for attribute %s",
+                                          value->vb_attr->depth, value->vb_attr->name);
+                       return 0;
+               }
+
+               switch (value->vb_attr->flags.length) {
+               case 1:
+                       fr_value_box_init(&base, FR_TYPE_UINT8, NULL, false);
+                       base.vb_uint8 = value->vb_attr->attr;
+                       break;
+
+               case 2:
+                       fr_value_box_init(&base, FR_TYPE_UINT16, NULL, false);
+                       base.vb_uint16 = value->vb_attr->attr;
+                       break;
+
+               case 4:
+                       fr_value_box_init(&base, FR_TYPE_UINT32, NULL, false);
+                       base.vb_uint32 = value->vb_attr->attr;
+                       break;
+
+               default:
+                       fr_strerror_printf("Unsupported length '%d' for attribute %s",
+                                          value->vb_attr->flags.length, value->vb_attr->name);
+                       return 0;
+               }
+
+               fr_value_box_hton(&tmp, &base);
+
+               FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, fr_value_box_raw(&tmp, tmp.type), min);
+       }
+               break;
+
        /*
         *      Dates and deltas are stored internally as
         *      64-bit nanoseconds.  We have to convert to the
@@ -2026,6 +2071,10 @@ ssize_t fr_value_box_from_network(TALLOC_CTX *ctx,
                FR_DBUFF_OUT_RETURN(&dst->vb_float64, &work_dbuff);
                break;
 
+       case FR_TYPE_ATTR:
+               fr_strerror_const("Unsupported ntoh from FR_TYPE_ATTR");
+               return 0;
+
        /*
         *      Dates and deltas are stored internally as
         *      64-bit nanoseconds.  We have to convert from
@@ -3708,6 +3757,41 @@ int fr_value_box_cast(TALLOC_CTX *ctx, fr_value_box_t *dst,
                }
                break;          /* use generic string/octets stuff below */
 
+       case FR_TYPE_ATTR:
+               if (src->vb_attr->depth != 1) {
+                       fr_strerror_printf("Unsupported depth '%d' for attribute %s",
+                                          src->vb_attr->depth, src->vb_attr->name);
+                       return 0;
+
+               }
+
+               /*
+                *      Convert it to an integer of the correct lenght. Then, cast it in place.
+                */
+               switch (src->vb_attr->flags.length) {
+               case 1:
+                       fr_value_box_init(dst, FR_TYPE_UINT8, NULL, false);
+                       dst->vb_uint8 = src->vb_attr->attr;
+                       break;
+
+               case 2:
+                       fr_value_box_init(dst, FR_TYPE_UINT16, NULL, false);
+                       dst->vb_uint16 = src->vb_attr->attr;
+                       break;
+
+               case 4:
+                       fr_value_box_init(dst, FR_TYPE_UINT32, NULL, false);
+                       dst->vb_uint32 = src->vb_attr->attr;
+                       break;
+
+               default:
+                       fr_strerror_printf("Unsupported length '%d' for attribute %s",
+                                          src->vb_attr->flags.length, src->vb_attr->name);
+                       return 0;
+               }
+
+               return fr_value_box_cast_in_place(ctx, dst, dst_type, dst_enumv);
+
        /*
         *      Invalid types for casting (were caught earlier)
         */
@@ -5378,6 +5462,33 @@ parse:
                fr_strerror_const("Unexpected value for data type NULL");
                return -1;
 
+       case FR_TYPE_ATTR:
+               if (!dst_enumv) {
+                       fr_strerror_const("No dictionary passed for data type 'attr'");
+                       return -1;
+               }
+
+               if (!dst_enumv->flags.is_root) {
+                       fr_strerror_const("Can only start from dictioanry root for data type 'attr'");
+                       return -1;
+               }
+
+               if (!fr_sbuff_next_if_char(&our_in, '@')) {
+                       fr_strerror_const("Expected '@...' for attribute reference");
+                       return -1;
+               }
+               
+               fr_value_box_init(dst, dst_type, dst_enumv, false);
+
+               slen = fr_dict_attr_by_name_substr(NULL, &dst->vb_attr, dst_enumv, &our_in, rules->terminals);
+               if (slen <= 0) {
+                       fr_strerror_printf("Failed to find the named attribute in %s", dst_enumv->name);
+                       return -2;
+               }
+               fr_assert(dst->vb_attr != NULL);
+
+               FR_SBUFF_SET_RETURN(in, &our_in);
+
        /*
         *      Dealt with below
         */
@@ -5650,6 +5761,18 @@ ssize_t fr_value_box_print(fr_sbuff_t *out, fr_value_box_t const *data, fr_sbuff
                FR_SBUFF_IN_CHAR_RETURN(&our_out, '}');
                break;
 
+       case FR_TYPE_ATTR:
+               if (data->vb_attr->depth != 1) {
+                       fr_strerror_printf("Unsupported depth '%u' for attribute %s",
+                                          data->vb_attr->depth, data->vb_attr->name);
+                       return 0;
+               }
+
+               FR_SBUFF_IN_CHAR_RETURN(&our_out, '@');
+               FR_SBUFF_IN_ESCAPE_RETURN(&our_out, data->vb_attr->name,
+                                         strlen(data->vb_attr->name), e_rules);
+               break;
+
        case FR_TYPE_NULL:
                FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "NULL");
                break;
index fa0b25ae2c3be82fa56a9eef2c1698b2b352052c..cab2b355f59797708c38d2d69e268b68349bead5 100644 (file)
@@ -112,8 +112,9 @@ typedef union {
        };
 
        struct {
-               void                   * _CONST cursor;                 //!< cursors
-               char const             * _CONST name;                   //!< name of the current cursor
+               void                    * _CONST cursor;                //!< cursors
+               char const              * _CONST name;                  //!< name of the current cursor
+               fr_dict_attr_t const    * _CONST da;                    //!< dictionary reference
        };
 
        /*
@@ -258,6 +259,7 @@ typedef enum {
 #define vb_octets                              datum.octets
 #define vb_void                                        datum.ptr
 #define vb_group                               datum.children
+#define vb_attr                                        datum.da
 
 #define vb_ip                                  datum.ip
 
index dcb60f682a62bfc91bfb1916f069f9171e4cd1bb..91b7ad88ffe453e3a2d6fe33eb8e34dbf7299aec 100644 (file)
@@ -193,6 +193,7 @@ static int fr_lua_marshall(request_t *request, lua_State *L, fr_pair_t const *vp
                lua_pushinteger(L, (lua_Integer)vp->vp_size);
                break;
 
+       case FR_TYPE_ATTR:
        case FR_TYPE_NON_LEAF:
                REDEBUG("Cannot convert %s to Lua type", fr_type_to_str(vp->vp_type));
                return -1;
index 80c39121fda98495a0d6d48878439252a6566598..86aa51237182174b8541ea29e8c13a53be76ec66 100644 (file)
@@ -261,10 +261,12 @@ static mrb_value mruby_pair_value_to_ruby(mrb_state *mrb, request_t *request, fr
                return value;
        }
 
-       default:
-               fr_assert(0);
+       case FR_TYPE_ATTR:
+       case FR_TYPE_NON_LEAF:
+               REDEBUG("Cannot convert %s to ruby type", fr_type_to_str(vp->vp_type));
                break;
        }
+
        return mrb_nil_value();
 }
 
index d9248469ad551ce1d29be288a17464ed36bccce9..f9e6ea7bb63c35c0c293f35cbb7d25db3d871aa0 100644 (file)
@@ -576,6 +576,11 @@ static int perl_value_marshal(fr_pair_t *vp, SV **value)
        PERLINT(32)
        PERLINT(64)
 
+
+       case FR_TYPE_SIZE:
+               *value = sv_2mortal(newSVuv(vp->vp_size));
+               break;
+
        case FR_TYPE_BOOL:
                *value = sv_2mortal(newSVuv(vp->vp_bool));
                break;
@@ -614,8 +619,9 @@ static int perl_value_marshal(fr_pair_t *vp, SV **value)
                break;
 
        /* Only leaf nodes should be able to call this */
-       default:
-               fr_assert(0);
+       case FR_TYPE_ATTR:
+       case FR_TYPE_NON_LEAF:
+               croak("Cannot convert %s to Perl type", fr_type_to_str(vp->vp_type));
                return -1;
        }
 
index a34634062ab1f8d53b68df3dd7f2708f9c401c0b..94f4f476a52e0eea2ace2b3f2b59c1e26a7b9fce 100644 (file)
@@ -878,6 +878,7 @@ static PyObject *py_freeradius_pair_getvalue(PyObject *self, UNUSED void *closur
        case FR_TYPE_COMBO_IP_ADDR:
        case FR_TYPE_COMBO_IP_PREFIX:
        case FR_TYPE_ETHERNET:
+       case FR_TYPE_ATTR:
        {
                ssize_t slen;
                char buffer[1024];
@@ -1040,6 +1041,7 @@ static int py_freeradius_pair_setvalue(PyObject *self, PyObject *value, UNUSED v
        case FR_TYPE_COMBO_IP_ADDR:
        case FR_TYPE_COMBO_IP_PREFIX:
        case FR_TYPE_ETHERNET:
+       case FR_TYPE_ATTR:
        {
                char const      *val;
                ssize_t         len;