]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
allow casts for expressions
authorAlan T. DeKok <aland@freeradius.org>
Thu, 9 Jun 2022 00:39:33 +0000 (20:39 -0400)
committerAlan T. DeKok <aland@freeradius.org>
Mon, 20 Jun 2022 15:46:58 +0000 (11:46 -0400)
(uint32) ( a + b + c...)

which is a bit different from casting a bare tmpl

src/lib/unlang/xlat_eval.c
src/lib/unlang/xlat_expr.c
src/lib/unlang/xlat_priv.h
src/tests/unit/xlat/purify.txt
src/tests/xlat/expr.txt

index 2607046a87976f7b6a812b837d9895aa3231bb9a..66d1ea56e2a180957cd2eeee9d76b6dbd9bb99aa 100644 (file)
@@ -61,6 +61,7 @@ static fr_dict_attr_t const *attr_packet_authentication_vector;
 static fr_dict_attr_t const *attr_packet_type;
 fr_dict_attr_t const       *attr_expr_bool_enum; /* xlat_expr.c */
 fr_dict_attr_t const           *attr_module_return_code; /* xlat_expr.c */
+fr_dict_attr_t const           *attr_cast_base; /* xlat_expr.c */
 
 static fr_dict_attr_autoload_t xlat_eval_dict_attr[] = {
        { .out = &attr_client_ip_address, .name = "Client-IP-Address", .type = FR_TYPE_IPV4_ADDR, .dict = &dict_freeradius },
@@ -78,6 +79,7 @@ static fr_dict_attr_autoload_t xlat_eval_dict_attr[] = {
        { .out = &attr_packet_authentication_vector, .name = "Packet-Authentication-Vector", .type = FR_TYPE_OCTETS, .dict = &dict_radius },
        { .out = &attr_packet_type, .name = "Packet-Type", .type = FR_TYPE_UINT32, .dict = &dict_radius },
        { .out = &attr_expr_bool_enum, .name = "Expr-Bool-Enum", .type = FR_TYPE_BOOL, .dict = &dict_freeradius },
+       { .out = &attr_cast_base, .name = "Cast-Base", .type = FR_TYPE_UINT8, .dict = &dict_freeradius },
        { NULL }
 };
 
index b5f8aa8dd4398fd3ebc73df46908e9150ba180e1..06e9ef0a4dd1f6c9d217b5eadd27f7f673dc8e93 100644 (file)
@@ -1076,7 +1076,10 @@ static xlat_action_t xlat_func_unary_op(TALLOC_CTX *ctx, fr_dcursor_t *out,
         *      ~NULL is an error
         *      !NULL is handled by xlat_func_unary_not
         */
-       if (!vb) return XLAT_ACTION_FAIL;
+       if (!vb) {
+               fr_strerror_printf("Input is empty");
+               return XLAT_ACTION_FAIL;
+       }
 
        if (!fr_type_is_leaf(vb->type) || fr_type_is_variable_size(vb->type)) {
                REDEBUG("Cannot perform operation on data type %s", fr_type_to_str(vb->type));
@@ -1569,6 +1572,47 @@ static ssize_t tokenize_unary(xlat_exp_head_t *head, xlat_exp_t **out, fr_sbuff_
        return fr_sbuff_set(in, &our_in);
 }
 
+/** Allocate a specific cast node.
+ *
+ *  With the first argument being a UINT8 of the data type.
+ *  See xlat_func_cast() for the implementation.
+ *
+ */
+static xlat_exp_t *expr_cast_alloc(TALLOC_CTX *ctx, fr_type_t type)
+{
+       xlat_exp_t *cast, *node;
+
+       /*
+        *      Create a "cast" node.  The first argument is a UINT8 value-box of the cast type.  The RHS is
+        *      whatever "node" comes next.
+        */
+       MEM(cast = xlat_exp_alloc(ctx, XLAT_FUNC, "cast", 4));
+       MEM(cast->call.args = xlat_exp_head_alloc(cast));
+       MEM(cast->call.func = xlat_func_find("cast", 4));
+       fr_assert(cast->call.func != NULL);
+       cast->flags = cast->call.func->flags;
+
+       /*
+        *      Create argv[0] UINT8, with "Cast-Base" as
+        *      the "da".  This allows the printing routines
+        *      to print the name of the type, and not the
+        *      number.
+        */
+       MEM(node = xlat_exp_alloc_null(cast));
+       xlat_exp_set_type(node, XLAT_BOX);
+       xlat_exp_set_name_buffer_shallow(node,
+                                        talloc_strdup(node,
+                                                      fr_table_str_by_value(fr_type_table,
+                                                                            type, "<INVALID>")));
+
+       fr_value_box_init(&node->data, FR_TYPE_UINT8, attr_cast_base, false);
+       node->data.vb_uint8 = type;
+
+       xlat_func_append_arg(cast, node);
+
+       return cast;
+}
+
 static ssize_t expr_cast_from_substr(fr_type_t *cast, fr_sbuff_t *in)
 {
        char                    close = '\0';
@@ -1645,14 +1689,32 @@ static ssize_t tokenize_field(xlat_exp_head_t *head, xlat_exp_t **out, fr_sbuff_
         *      value when we get the output of the expression.
         */
        if (fr_sbuff_next_if_char(&our_in, '(')) {
-               slen = tokenize_expression(head, &node, &our_in, bracket_rules, t_rules, T_INVALID, bracket_rules, input_rules, false, depth + 1);
+               xlat_exp_t *cast = NULL;
+               xlat_exp_head_t *my_head = head;
+
+               if (!fr_type_is_null(our_t_rules.cast)) {
+                       MEM(cast = expr_cast_alloc(head, our_t_rules.cast));
+                       my_head = cast->call.args;
+               }
+
+               slen = tokenize_expression(my_head, &node, &our_in, bracket_rules, t_rules, T_INVALID, bracket_rules, input_rules, false, depth + 1);
                if (slen <= 0) {
+                       talloc_free(cast);
                        FR_SBUFF_ERROR_RETURN_ADJ(&our_in, slen);
                }
 
                if (!fr_sbuff_next_if_char(&our_in, ')')) {
+                       talloc_free(cast);
                        fr_strerror_printf("Failed to find trailing ')'");
-                       FR_SBUFF_ERROR_RETURN_ADJ(&our_in, slen);
+                       FR_SBUFF_ERROR_RETURN_ADJ(&our_in, -slen);
+               }
+
+               /*
+                *      Wrap the entire sub-expression in a "cast", and then return the cast as the parsed node.
+                */
+               if (cast) {
+                       xlat_func_append_arg(cast, node);
+                       node = cast;
                }
 
                /*
index 51c6fd7009853e9956751bc5818293ece3859bf9..8e93ae4d475bfaa8d076a99153cb6666775bb470 100644 (file)
@@ -319,6 +319,7 @@ xlat_t      *xlat_func_find(char const *name, ssize_t namelen);
  */
 extern fr_dict_attr_t const *attr_expr_bool_enum;
 extern fr_dict_attr_t const *attr_module_return_code;
+extern fr_dict_attr_t const *attr_cast_base;
 
 void           xlat_signal(xlat_func_signal_t signal, xlat_exp_t const *exp,
                            request_t *request, void *rctx, fr_state_signal_t action);
index 45ae70d7ec44cfb6cd4fb6ff236e218c70d1b92c..fec1407871a8edf631fc2d53d17af4fe37003911 100644 (file)
@@ -180,5 +180,14 @@ match false
 xlat_purify !true
 match false
 
+xlat_purify -fail
+match ERROR offset 9: Invalid operation on module return code
+
+xlat_purify ~fail
+match ERROR offset 9: Invalid operation on module return code
+
+xlat_purify !fail
+match !%{rcode:'fail'}
+
 count
-match 79
+match 85
index 350570f6a15b68e372cc1fc37acea7fd94dfebf7..590d95625225020bbc637e79ef5c82a17a6ad450 100644 (file)
@@ -76,3 +76,12 @@ match foo 0
 #
 xlat_expr `/bin/rm this_file_should_not_exist_if_it_does_too_bad_for_you`
 match ERROR expanding xlat: Program failed with status 1
+
+#
+#  NAS-IP-Address doesn't exist.
+#
+xlat_expr -&NAS-IP-Address
+match ERROR expanding xlat: Input is empty
+
+xlat_expr (uint16) ((uint32) 1 + (uint8) 2)
+match 3