From: Alan T. DeKok Date: Sat, 21 May 2022 14:20:03 +0000 (-0400) Subject: add custom resolve callback handler X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=dfa26c0df497778e102f718866eed8d1dda8a202;p=thirdparty%2Ffreeradius-server.git add custom resolve callback handler --- diff --git a/src/lib/unlang/xlat_expr.c b/src/lib/unlang/xlat_expr.c index f7ac21b2c4d..12911b81d20 100644 --- a/src/lib/unlang/xlat_expr.c +++ b/src/lib/unlang/xlat_expr.c @@ -101,6 +101,72 @@ static fr_slen_t xlat_expr_print_binary(fr_sbuff_t *out, xlat_exp_t const *node, return fr_sbuff_used_total(out) - at_in; } +static int xlat_expr_resolve_binary(xlat_exp_t *self, UNUSED void *inst, xlat_res_rules_t const *xr_rules) +{ + xlat_exp_t *arg1, *arg2; + xlat_exp_t *a, *b; + tmpl_res_rules_t my_tr_rules; + + arg1 = xlat_exp_head(self->call.args); + fr_assert(arg1); + fr_assert(arg1->type == XLAT_GROUP); + + arg2 = xlat_exp_next(self->call.args, arg1); + fr_assert(arg2); + fr_assert(arg2->type == XLAT_GROUP); + + a = xlat_exp_head(arg1->group); + b = xlat_exp_head(arg2->group); + + /* + * We have many things here, just call resolve recursively. + */ + if (xlat_exp_next(arg1->group, a) || (xlat_exp_next(arg2->group, b))) { + return xlat_resolve(self->call.args, xr_rules); + } + + /* + * Anything else must get resolved at run time. + */ + if ((a->type != XLAT_TMPL) || (b->type != XLAT_TMPL)) { + return xlat_resolve(self->call.args, xr_rules); + } + + /* + * The tr_rules should always contain dict_def + */ + fr_assert(xr_rules); /* always set by xlat_resolve() */ + if (xr_rules->tr_rules) { + my_tr_rules = *xr_rules->tr_rules; + } else { + my_tr_rules = (tmpl_res_rules_t) { }; + } + + /* + * The LHS attribute dictates the enumv for the RHS one. + */ + if (tmpl_contains_attr(a->vpt)) { + if (tmpl_resolve(a->vpt, &my_tr_rules) < 0) return -1; + + my_tr_rules.enumv = tmpl_da(a->vpt); + + return tmpl_resolve(b->vpt, &my_tr_rules); + } + + if (tmpl_contains_attr(b->vpt)) { + if (tmpl_resolve(b->vpt, &my_tr_rules) < 0) return -1; + + my_tr_rules.enumv = tmpl_da(b->vpt); + + return tmpl_resolve(a->vpt, &my_tr_rules); + } + + if (tmpl_resolve(a->vpt, xr_rules->tr_rules) < 0) return -1; + + return tmpl_resolve(b->vpt, xr_rules->tr_rules); +} + + static void xlat_func_append_arg(xlat_exp_t *head, xlat_exp_t *node) { xlat_exp_t *group; @@ -484,6 +550,7 @@ do { \ xlat_func_args(xlat, binary_op_xlat_args); \ xlat_internal(xlat); \ xlat_print_set(xlat, xlat_expr_print_binary); \ + xlat_resolve_set(xlat, xlat_expr_resolve_binary); \ xlat->token = _op; \ } while (0) diff --git a/src/lib/unlang/xlat_priv.h b/src/lib/unlang/xlat_priv.h index 8ace3051d68..356e9c371de 100644 --- a/src/lib/unlang/xlat_priv.h +++ b/src/lib/unlang/xlat_priv.h @@ -39,6 +39,7 @@ extern "C" { #endif typedef fr_slen_t (*xlat_print_t)(fr_sbuff_t *in, xlat_exp_t const *self, void *inst, fr_sbuff_escape_rules_t const *e_rules); +typedef int (*xlat_resolve_t)(xlat_exp_t *self, void *inst, xlat_res_rules_t const *xr_rules); typedef struct xlat_s { fr_rb_node_t node; //!< Entry in the xlat function tree. @@ -65,6 +66,7 @@ typedef struct xlat_s { void *thread_uctx; //!< uctx to pass to instantiation functions. xlat_print_t print; //!< function to call when printing + xlat_resolve_t resolve; //!< function to call when resolving xlat_flags_t flags; //!< various flags @@ -261,7 +263,7 @@ static inline void xlat_internal(xlat_t *xlat) /** Set a print routine for an xlat function. * - * @param[in] xlat to mark as internal. + * @param[in] xlat to set */ static inline void xlat_print_set(xlat_t *xlat, xlat_print_t func) { @@ -269,6 +271,16 @@ static inline void xlat_print_set(xlat_t *xlat, xlat_print_t func) } +/** Set a resolve routine for an xlat function. + * + * @param[in] xlat to set + */ +static inline void xlat_resolve_set(xlat_t *xlat, xlat_resolve_t func) +{ + xlat->resolve = func; +} + + /** Walker callback for xlat_walk() * * @param[in] exp being evaluated. diff --git a/src/lib/unlang/xlat_tokenize.c b/src/lib/unlang/xlat_tokenize.c index 18f3e0a772d..52839155065 100644 --- a/src/lib/unlang/xlat_tokenize.c +++ b/src/lib/unlang/xlat_tokenize.c @@ -1656,7 +1656,13 @@ int xlat_resolve(xlat_exp_head_t *head, xlat_res_rules_t const *xr_rules) * A resolved function with unresolved args */ case XLAT_FUNC: - if (xlat_resolve(node->call.args, xr_rules) < 0) return -1; + if (node->call.func->resolve) { + void *inst = node->call.inst ? node->call.inst->data : NULL; + + if (node->call.func->resolve(node, inst, xr_rules) < 0) return -1; + } else { + if (xlat_resolve(node->call.args, xr_rules) < 0) return -1; + } node->flags = node->call.func->flags; xlat_flags_merge(&node->flags, &node->call.args->flags); diff --git a/src/tests/unit/xlat/purify.txt b/src/tests/unit/xlat/purify.txt index 27e34b1fdf0..e758eba8dfc 100644 --- a/src/tests/unit/xlat/purify.txt +++ b/src/tests/unit/xlat/purify.txt @@ -48,8 +48,7 @@ match yes # * the tokenizer needs to track offsets, so it can return the offset which cause the error. # xlat_purify &Service-Type == Framed-User -#match (%{Service-Type} == Framed-User) -match ERROR offset 1: Invalid attribute reference, missing '&' prefix: Failed resolving attribute in expansion: Framed-User +match (%{Service-Type} == Framed-User) #xlat_purify 1 + (&Service-Type == Framed-User) #match (1 + (%{Service-Type} == Framed-User)) @@ -132,5 +131,20 @@ match yes xlat_purify 1 < 2 < 3 match yes +xlat_purify &Service-Type == 1 +match (%{Service-Type} == 1) + +# +# Convert the RHS to a simpler version +# +xlat_purify &Service-Type == (1 + 2) +match (%{Service-Type} == 3) + +# +# This is so wrong... +# +xlat_purify &Reply-Message == "foo" +match ERROR offset 1: Failed resolving attribute in expansion: Message + count -match 51 +match 57