From: Alan T. DeKok Date: Thu, 9 Jun 2022 00:39:33 +0000 (-0400) Subject: allow casts for expressions X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=32682537e0509ef3c02d46bc7da7f8d3d27490b5;p=thirdparty%2Ffreeradius-server.git allow casts for expressions (uint32) ( a + b + c...) which is a bit different from casting a bare tmpl --- diff --git a/src/lib/unlang/xlat_eval.c b/src/lib/unlang/xlat_eval.c index 2607046a879..66d1ea56e2a 100644 --- a/src/lib/unlang/xlat_eval.c +++ b/src/lib/unlang/xlat_eval.c @@ -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 } }; diff --git a/src/lib/unlang/xlat_expr.c b/src/lib/unlang/xlat_expr.c index b5f8aa8dd43..06e9ef0a4dd 100644 --- a/src/lib/unlang/xlat_expr.c +++ b/src/lib/unlang/xlat_expr.c @@ -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, ""))); + + 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; } /* diff --git a/src/lib/unlang/xlat_priv.h b/src/lib/unlang/xlat_priv.h index 51c6fd70098..8e93ae4d475 100644 --- a/src/lib/unlang/xlat_priv.h +++ b/src/lib/unlang/xlat_priv.h @@ -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); diff --git a/src/tests/unit/xlat/purify.txt b/src/tests/unit/xlat/purify.txt index 45ae70d7ec4..fec1407871a 100644 --- a/src/tests/unit/xlat/purify.txt +++ b/src/tests/unit/xlat/purify.txt @@ -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 diff --git a/src/tests/xlat/expr.txt b/src/tests/xlat/expr.txt index 350570f6a15..590d9562522 100644 --- a/src/tests/xlat/expr.txt +++ b/src/tests/xlat/expr.txt @@ -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