]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Only allow things which look like enums during tokenization
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Thu, 27 Jan 2022 18:30:01 +0000 (12:30 -0600)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Fri, 28 Jan 2022 21:38:54 +0000 (15:38 -0600)
34 files changed:
src/bin/all.mk
src/bin/unit_test_attribute.c
src/lib/redis/redis.c
src/lib/server/cf_parse.c
src/lib/server/cond_tokenize.c
src/lib/server/map.c
src/lib/server/map_async.c
src/lib/server/tmpl.h
src/lib/server/tmpl_tokenize.c
src/lib/unlang/compile.c
src/lib/unlang/edit.c
src/lib/unlang/switch.c
src/lib/unlang/xlat.h
src/lib/unlang/xlat_builtin.c
src/lib/unlang/xlat_expr.c
src/lib/unlang/xlat_tokenize.c
src/lib/util/dict.h
src/lib/util/dict_tokenize.c
src/lib/util/dict_util.c
src/lib/util/sbuff.h
src/modules/rlm_cache/drivers/rlm_cache_redis/rlm_cache_redis.c
src/modules/rlm_cache/rlm_cache.c
src/modules/rlm_expr/rlm_expr.c
src/modules/rlm_json/rlm_json.c
src/modules/rlm_linelog/rlm_linelog.c
src/modules/rlm_mruby/rlm_mruby.c
src/modules/rlm_python/rlm_python.c
src/modules/rlm_redis_ippool/rlm_redis_ippool.c
src/modules/rlm_rest/rest.c
src/tests/unit/condition/base.txt
src/tests/unit/condition/casts.txt
src/tests/unit/condition/escape.txt
src/tests/unit/condition/ip.txt
src/tests/unit/condition/regex.txt

index 4dfdfa6acba13cda57fb829218a594de7f67fcf9..f94ef0b35bdba5684de965c7d7455582955ef291 100644 (file)
@@ -6,6 +6,7 @@ SUBMAKEFILES := \
     radlock.mk \
     radsniff.mk \
     radsnmp.mk \
+    radsizes.mk \
     radwho.mk \
     radtest.mk \
     radzap.mk \
index a251718339974193f3f1aa624948a307aad17a12..0232aa4bfdcb1c5d33f477763b4410291670b788 100644 (file)
@@ -2671,11 +2671,11 @@ static size_t command_xlat_expr(command_result_t *result, command_file_ctx_t *cc
 
        dec_len = xlat_tokenize_expression(cc->tmp_ctx, &head, NULL, &FR_SBUFF_IN(in, input_len), NULL,
                                           &(tmpl_rules_t) {
-                                                  .attr = {
+                                               .attr = {
                                                        .dict_def = cc->tmpl_rules.attr.dict_def ?
                                                           cc->tmpl_rules.attr.dict_def : cc->config->dict,
                                                        .allow_unresolved = cc->tmpl_rules.attr.allow_unresolved
-                                                  },
+                                               }
                                           });
        if (dec_len <= 0) {
                fr_strerror_printf_push_head("ERROR offset %d", (int) -dec_len);
index b352fc20e306b590f28342e243054635b255c106..8d8a7e6ba1732b6605f776b39552064ca6787de3 100644 (file)
@@ -384,9 +384,11 @@ int fr_redis_reply_to_map(TALLOC_CTX *ctx, map_list_t *out, request_t *request,
 
        MEM(map = talloc_zero(ctx, map_t));
        slen = tmpl_afrom_attr_str(map, NULL, &map->lhs, key->str,
-                                  &(tmpl_attr_rules_t){
-                                       .prefix = TMPL_ATTR_REF_PREFIX_NO,
-                                       .dict_def = request->dict
+                                  &(tmpl_rules_t){
+                                       .attr = {
+                                               .prefix = TMPL_ATTR_REF_PREFIX_NO,
+                                               .dict_def = request->dict
+                                       }
                                   });
        if (slen <= 0) {
                REMARKER(key->str, -slen, "%s", fr_strerror());
index dddaf6310537ab0ea74f39ffc3fbd469f601c69d..b53c8819e92d363dbd46329b2364cf71a76e7c63 100644 (file)
@@ -216,7 +216,6 @@ int cf_pair_parse_value(TALLOC_CTX *ctx, void *out, UNUSED void *base, CONF_ITEM
                                                        .allow_foreign = true
                                                }
                                        };
-               fr_type_t               cast = FR_TYPE_NULL;
                fr_sbuff_t              sbuff = FR_SBUFF_IN(cp->value, strlen(cp->value));
 
                if (!cp->printed) cf_pair_debug(cs, cp, secret);
@@ -225,7 +224,7 @@ int cf_pair_parse_value(TALLOC_CTX *ctx, void *out, UNUSED void *base, CONF_ITEM
                 *      Parse the cast operator for barewords
                 */
                if (cp->rhs_quote == T_BARE_WORD) {
-                       slen = tmpl_cast_from_substr(&cast, &sbuff);
+                       slen = tmpl_cast_from_substr(&rules, &sbuff);
                        if (slen < 0) {
                                char *spaces, *text;
                        tmpl_error:
@@ -256,11 +255,6 @@ int cf_pair_parse_value(TALLOC_CTX *ctx, void *out, UNUSED void *base, CONF_ITEM
                        return -1;
                }
 
-               if (tmpl_cast_set(vpt, cast) < 0) {
-                       cf_log_perr(cp, "Failed setting tmpl type");
-                       return -1;
-               }
-
                /*
                 *      Non-blocking xlat's
                 */
@@ -431,12 +425,14 @@ int cf_pair_parse_value(TALLOC_CTX *ctx, void *out, UNUSED void *base, CONF_ITEM
                int             af = AF_UNSPEC;
                fr_type_t       our_type = FR_TYPE_NULL;
                fr_sbuff_t      sbuff = FR_SBUFF_IN(cp->value, strlen(cp->value));
+               tmpl_rules_t    rules = {};
 
-               slen = tmpl_cast_from_substr(&our_type, &sbuff);
+               slen = tmpl_cast_from_substr(&rules, &sbuff);
                if (slen < 0) {
                        cf_log_perr(cp, "Failed parsing config item");
                        goto error;
                }
+               our_type = rules.cast;
 
                if (slen > 0) {
                        if (type == FR_TYPE_COMBO_IP_ADDR) {
index bae2bc63f9a95105303e6b32183fb0037ae6f08f..25c937881fe054ef846e446cda3296415e88a0fd 100644 (file)
@@ -241,7 +241,7 @@ int fr_cond_promote_types(fr_cond_t *c, fr_sbuff_t *in, fr_sbuff_marker_t *m_lhs
                if (cast_type != FR_TYPE_NULL) {
                        if ((cast_type != FR_TYPE_STRING) &&
                            (cast_type != FR_TYPE_OCTETS)) {
-                               fr_strerror_const("Invalid cast used with regular expression");
+                               fr_strerror_const("Casts cannot be used with regular expressions");
                                if (in) fr_sbuff_set(in, m_lhs);
                                return -1;
                        }
@@ -331,7 +331,9 @@ int fr_cond_promote_types(fr_cond_t *c, fr_sbuff_t *in, fr_sbuff_marker_t *m_lhs
                 */
                if (tmpl_rules_cast(c->data.map->rhs) != FR_TYPE_NULL) {
                        if (tmpl_rules_cast(c->data.map->rhs) != lhs_type) {
-                               fr_strerror_const("Incompatible casts");
+                               fr_strerror_printf("Incompatible casts '%s' and '%s'",
+                                                  fr_type_to_str(tmpl_rules_cast(c->data.map->rhs)),
+                                                  fr_type_to_str(lhs_type));
                                if (in) fr_sbuff_set(in, fr_sbuff_start(in));
                                return -1;
                        }
@@ -926,12 +928,12 @@ static int cond_forbid_groups(tmpl_t *vpt, fr_sbuff_t *in, fr_sbuff_marker_t *m_
 
 static ssize_t cond_tokenize_operand(fr_cond_t *c, tmpl_t **out,
                                     fr_sbuff_marker_t *opd_start, fr_sbuff_t *in,
-                                    tmpl_rules_t const *rules)
+                                    tmpl_rules_t const *t_rules)
 {
        fr_sbuff_term_t const           bareword_terminals =
                                        FR_SBUFF_TERMS(
-                                               L("\t"),
                                                L("\n"),
+                                               L("\t"),
                                                L(" "),
                                                L("!*"),
                                                L("!="),
@@ -949,6 +951,7 @@ static ssize_t cond_tokenize_operand(fr_cond_t *c, tmpl_t **out,
                                                L(">"),
                                                L(">="),
                                                L("||"),                /* Logical operator */
+                                               L("")                   /* Hack for EOF */
                                        );
 
        fr_sbuff_t                      our_in = FR_SBUFF(in);
@@ -959,13 +962,14 @@ static ssize_t cond_tokenize_operand(fr_cond_t *c, tmpl_t **out,
        fr_sbuff_parse_rules_t          tmp_p_rules;
        fr_sbuff_parse_rules_t const    *p_rules;
        ssize_t                         slen;
+       tmpl_rules_t                    our_t_rules = *t_rules;
 
        *out = NULL;
 
        /*
         *      Parse (optional) cast
         */
-       slen = tmpl_cast_from_substr(&cast, &our_in);
+       slen = tmpl_cast_from_substr(&our_t_rules, &our_in);
        if (slen < 0) return slen;
 
        fr_sbuff_adv_past_whitespace(&our_in, SIZE_MAX, NULL);
@@ -1002,7 +1006,7 @@ static ssize_t cond_tokenize_operand(fr_cond_t *c, tmpl_t **out,
 #endif
        }
 
-       slen = tmpl_afrom_substr(c, &vpt, &our_in, type, p_rules, rules);
+       slen = tmpl_afrom_substr(c, &vpt, &our_in, type, p_rules, &our_t_rules);
        if (!vpt) {
                fr_sbuff_advance(&our_in, slen * -1);
 
@@ -1028,7 +1032,7 @@ static ssize_t cond_tokenize_operand(fr_cond_t *c, tmpl_t **out,
         */
        if (type == T_SOLIDUS_QUOTED_STRING) {
                if (cast != FR_TYPE_NULL) {
-                       fr_strerror_const("Invalid cast used with regular expression");
+                       fr_strerror_const("Casts cannot be used with regular expressions");
                        fr_sbuff_set(&our_in, &m);
                        goto error;
                }
@@ -1076,13 +1080,6 @@ static ssize_t cond_tokenize_operand(fr_cond_t *c, tmpl_t **out,
                        fr_sbuff_set(&our_in, &m);
                        goto error;
                }
-
-               cast = FR_TYPE_NULL;
-       }
-
-       if (tmpl_cast_set(vpt, cast) < 0) {
-               fr_sbuff_set(&our_in, &m);      /* Reset to start of cast */
-               goto error;
        }
 
        if (tmpl_is_attr_unresolved(vpt)) c->pass2_fixup = PASS2_FIXUP_ATTR;
index 82bc43213e411cb20b9c92058666a9b5ce18aac3..711ecebfc67fc45ad74f1cf5eb976c00e66575e7 100644 (file)
@@ -146,7 +146,7 @@ int map_afrom_cp(TALLOC_CTX *ctx, map_t **out, map_t *parent, CONF_PAIR *cp,
                break;
 
        default:
-               slen = tmpl_afrom_attr_str(ctx, NULL, &map->lhs, attr, &lhs_rules->attr);
+               slen = tmpl_afrom_attr_str(ctx, NULL, &map->lhs, attr, lhs_rules);
                if (slen <= 0) {
                        cf_log_err(cp, "Failed parsing attribute reference %s - %s", attr, fr_strerror());
                        marker_subject = attr;
@@ -356,12 +356,12 @@ ssize_t map_afrom_substr(TALLOC_CTX *ctx, map_t **out, map_t **parent_p, fr_sbuf
 
        default:
        {
-               tmpl_attr_rules_t our_lhs_attr_rules;
+               tmpl_rules_t our_lhs_rules;
 
                if (lhs_rules) {
-                       our_lhs_attr_rules = lhs_rules->attr;
+                       our_lhs_rules = *lhs_rules;
                } else {
-                       memset(&our_lhs_attr_rules, 0, sizeof(our_lhs_attr_rules));
+                       memset(&our_lhs_rules, 0, sizeof(our_lhs_rules));
                }
 
                /*
@@ -369,7 +369,7 @@ ssize_t map_afrom_substr(TALLOC_CTX *ctx, map_t **out, map_t **parent_p, fr_sbuf
                 *      parents list.  Allow for "..foo" to refer to
                 *      the grandparent list.
                 */
-               if (our_lhs_attr_rules.prefix == TMPL_ATTR_REF_PREFIX_NO) {
+               if (our_lhs_rules.attr.prefix == TMPL_ATTR_REF_PREFIX_NO) {
                        /*
                         *      One '.' means "the current parent".
                         */
@@ -401,10 +401,10 @@ ssize_t map_afrom_substr(TALLOC_CTX *ctx, map_t **out, map_t **parent_p, fr_sbuf
                         */
                        if (is_child) {
                                fr_assert(tmpl_is_attr(parent->lhs));
-                               our_lhs_attr_rules.parent = tmpl_da(parent->lhs);
+                               our_lhs_rules.attr.parent = tmpl_da(parent->lhs);
 
                                slen = tmpl_afrom_attr_substr(map, NULL, &map->lhs, &our_in,
-                                                             &map_parse_rules_bareword_quoted, &our_lhs_attr_rules);
+                                                             &map_parse_rules_bareword_quoted, &our_lhs_rules);
                                break;
                        }
 
@@ -417,7 +417,7 @@ ssize_t map_afrom_substr(TALLOC_CTX *ctx, map_t **out, map_t **parent_p, fr_sbuf
                }
 
                slen = tmpl_afrom_attr_substr(map, NULL, &map->lhs, &our_in,
-                                             &map_parse_rules_bareword_quoted, &our_lhs_attr_rules);
+                                             &map_parse_rules_bareword_quoted, &our_lhs_rules);
                break;
        }
        }
@@ -729,7 +729,7 @@ static int _map_afrom_cs(TALLOC_CTX *ctx, map_list_t *out, map_t *parent, CONF_S
                         *      them for now.  Once the functionality
                         *      is tested and used, we can allow that.
                         */
-                       slen = tmpl_afrom_attr_str(ctx, NULL, &map->lhs, cf_section_name1(subcs), &our_lhs_rules.attr);
+                       slen = tmpl_afrom_attr_str(ctx, NULL, &map->lhs, cf_section_name1(subcs), &our_lhs_rules);
                        if (slen <= 0) {
                                cf_log_err(ci, "Failed parsing attribute reference for list %s - %s",
                                           cf_section_name1(subcs), fr_strerror());
@@ -1448,9 +1448,11 @@ int map_to_request(request_t *request, map_t const *map, radius_map_getvalue_t f
                }
 
                slen = tmpl_afrom_attr_str(tmp_ctx, NULL, &exp_lhs, attr_str,
-                                          &(tmpl_attr_rules_t){
-                                               .dict_def = request->dict,
-                                               .prefix = TMPL_ATTR_REF_PREFIX_NO
+                                          &(tmpl_rules_t){
+                                               .attr = {
+                                                       .dict_def = request->dict,
+                                                       .prefix = TMPL_ATTR_REF_PREFIX_NO
+                                               }
                                           });
                if (slen <= 0) {
                        RPEDEBUG("Left side expansion result \"%s\" is not an attribute reference", attr_str);
index 2d32b9fac7d893c18dd55b571dc6b7e80ee9aff8..771d885cf2166849a3401df2d8adc97c5d133251 100644 (file)
@@ -329,9 +329,11 @@ int map_to_list_mod(TALLOC_CTX *ctx, vp_list_mod_t **out,
                }
 
                slen = tmpl_afrom_attr_str(tmp_ctx, NULL, &map_tmp.lhs, lhs_result_head->vb_strvalue,
-                                          &(tmpl_attr_rules_t){
-                                               .dict_def = request->dict,
-                                               .prefix = TMPL_ATTR_REF_PREFIX_NO
+                                          &(tmpl_rules_t){
+                                               .attr = {
+                                                       .dict_def = request->dict,
+                                                       .prefix = TMPL_ATTR_REF_PREFIX_NO
+                                               }
                                           });
                if (slen <= 0) {
                        RPEDEBUG("Left side expansion result \"%s\" is not an attribute reference",
index 8e98b1d058c281126fc0cded2df031a70395e937..5256a5a2377b0f7f0c1878eeddd6e2b9d37ef325 100644 (file)
@@ -250,7 +250,6 @@ extern size_t tmpl_type_table_len;
 
 typedef struct tmpl_rules_s tmpl_rules_t;
 typedef struct tmpl_attr_rules_s tmpl_attr_rules_t;
-typedef struct tmpl_data_rules_s tmpl_data_rules_t;
 typedef struct tmpl_xlat_rules_s tmpl_xlat_rules_t;
 typedef struct tmpl_res_rules_s tmpl_res_rules_t;
 typedef struct tmpl_s tmpl_t;
@@ -319,14 +318,6 @@ struct tmpl_attr_rules_s {
        uint8_t                 list_as_attr:1;         //!< return #TMPL_TYPE_ATTR for lists, and not #TMPL_TYPE_LIST
 };
 
-struct tmpl_data_rules_s {
-       fr_dict_attr_t const    *enumv;                 //!< Enumeration attribute used to resolve enum values.
-
-       fr_type_t               cast;                   //!< Whether there was an explicit cast.
-                                                       ///< Used to determine if barewords or other values
-                                                       ///< should be converted to an internal data type.
-};
-
 struct tmpl_xlat_rules_s {
        fr_event_list_t         *runtime_el;            //!< The eventlist to use for runtime instantiation
                                                        ///< of xlats.
@@ -339,9 +330,14 @@ struct tmpl_rules_s {
        tmpl_rules_t const      *parent;                //!< for parent / child relationships
 
        tmpl_attr_rules_t       attr;                   //!< Rules/data for parsing attribute references.
-       tmpl_data_rules_t       data;                   //!< Rules/data for parsing attribute data and enumerations.
        tmpl_xlat_rules_t       xlat;                   //!< Rules/data for parsing xlats.
 
+       fr_dict_attr_t const    *enumv;                 //!< Enumeration attribute used to resolve enum values.
+
+       fr_type_t               cast;                   //!< Whether there was an explicit cast.
+                                                       ///< Used to determine if barewords or other values
+                                                       ///< should be converted to an internal data type.
+
        bool                    at_runtime;             //!< Produce an ephemeral/runtime tmpl.
                                                        ///< Instantiated xlats are not added to the global
                                                        ///< trees, regexes are not JIT'd.
@@ -716,8 +712,8 @@ static inline tmpl_pair_list_t tmpl_list(tmpl_t const *vpt)
 #define tmpl_value_type(_tmpl)                 (_tmpl)->data.literal.type
 #define tmpl_value_enumv(_tmpl)                        (_tmpl)->data.literal.enumv
 
-#define tmpl_rules_cast(_tmpl)                 (_tmpl)->rules.data.cast
-#define tmpl_rules_enumv(_tmpl)                        (_tmpl)->rules.data.enumv
+#define tmpl_rules_cast(_tmpl)                 (_tmpl)->rules.cast
+#define tmpl_rules_enumv(_tmpl)                        (_tmpl)->rules.enumv
 
 /*
  *     Temporary macros to track where we do assignments
@@ -838,7 +834,8 @@ typedef enum {
        TMPL_ATTR_ERROR_FILTER_NOT_ALLOWED,             //!< Filters disallowed by rules.
        TMPL_ATTR_ERROR_INVALID_ARRAY_INDEX,            //!< Invalid array index.
        TMPL_ATTR_ERROR_NESTING_TOO_DEEP,               //!< Too many levels of nesting.
-       TMPL_ATTR_ERROR_MISSING_TERMINATOR              //!< Unexpected text found after attribute reference
+       TMPL_ATTR_ERROR_MISSING_TERMINATOR,             //!< Unexpected text found after attribute reference
+       TMPL_ATTR_ERROR_BAD_CAST                        //!< Specified cast was invalid.
 } tmpl_attr_error_t;
 
 /** Map ptr type to a boxed type
@@ -901,10 +898,13 @@ size_t                    tmpl_request_ref_by_name(tmpl_request_ref_t *out, char const *name, tmp
 
 tmpl_t                 *tmpl_init_printf(tmpl_t *vpt, tmpl_type_t type, fr_token_t quote, char const *fmt, ...) CC_HINT(nonnull(1,4));
 
-tmpl_t                 *tmpl_init_shallow(tmpl_t *vpt, tmpl_type_t type,
-                                          fr_token_t quote, char const *name, ssize_t len) CC_HINT(nonnull);
+tmpl_t                 *tmpl_init_shallow(tmpl_t *vpt, tmpl_type_t type, fr_token_t quote,
+                                          char const *name, ssize_t len,
+                                          tmpl_rules_t const *t_rules) CC_HINT(nonnull(1,4));
 
-tmpl_t                 *tmpl_init(tmpl_t *vpt, tmpl_type_t type, fr_token_t quote, char const *name, ssize_t len) CC_HINT(nonnull);
+tmpl_t                 *tmpl_init(tmpl_t *vpt, tmpl_type_t type, fr_token_t quote,
+                                  char const *name, ssize_t len,
+                                  tmpl_rules_t const *t_rules) CC_HINT(nonnull);
 
 tmpl_t                 *tmpl_alloc(TALLOC_CTX *ctx, tmpl_type_t type, fr_token_t quote, char const *name, ssize_t len);
 
@@ -950,11 +950,11 @@ int                       tmpl_attr_afrom_list(TALLOC_CTX *ctx, tmpl_t **out, tmpl_t const *list,
 ssize_t                        tmpl_afrom_attr_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err,
                                               tmpl_t **out, fr_sbuff_t *name,
                                               fr_sbuff_parse_rules_t const *p_rules,
-                                              tmpl_attr_rules_t const *t_rules) CC_HINT(nonnull(3,4));
+                                              tmpl_rules_t const *t_rules) CC_HINT(nonnull(3,4));
 
 ssize_t                        tmpl_afrom_attr_str(TALLOC_CTX *ctx, tmpl_attr_error_t *err,
                                            tmpl_t **out, char const *name,
-                                           tmpl_attr_rules_t const *rules) CC_HINT(nonnull (3, 4));
+                                           tmpl_rules_t const *rules) CC_HINT(nonnull (3, 4));
 
 ssize_t                        tmpl_afrom_substr(TALLOC_CTX *ctx, tmpl_t **out,
                                          fr_sbuff_t *in,
@@ -964,7 +964,7 @@ ssize_t                     tmpl_afrom_substr(TALLOC_CTX *ctx, tmpl_t **out,
 
 tmpl_t                 *tmpl_copy(TALLOC_CTX *ctx, tmpl_t const *in) CC_HINT(nonnull);
 
-ssize_t                        tmpl_cast_from_substr(fr_type_t *out, fr_sbuff_t *in) CC_HINT(nonnull(2));              /* Parses cast string */
+ssize_t                        tmpl_cast_from_substr(tmpl_rules_t *t_rules, fr_sbuff_t *in) CC_HINT(nonnull(2));               /* Parses cast string */
 
 int                    tmpl_cast_set(tmpl_t *vpt, fr_type_t type) CC_HINT(nonnull);    /* Sets cast type */
 
index 9d3555e9e7f4c73caaa9099f28be787dcc83061f..e964a11f376004b81bdf5a2303a84558f199caf9 100644 (file)
@@ -226,7 +226,8 @@ void tmpl_attr_debug(tmpl_t const *vpt)
                     vpt->type,
                     fr_box_strvalue_len(vpt->name, vpt->len), vpt);
 
-       FR_FAULT_LOG("\tquote  : %s", fr_table_str_by_value(fr_token_quotes_table, vpt->quote, "<INVALID>"));
+       FR_FAULT_LOG("\tcast       : %s", fr_type_to_str(tmpl_rules_cast(vpt)));
+       FR_FAULT_LOG("\tquote      : %s", fr_table_str_by_value(fr_token_quotes_table, vpt->quote, "<INVALID>"));
 
        FR_FAULT_LOG("request references:");
 
@@ -261,6 +262,7 @@ void tmpl_debug(tmpl_t const *vpt)
                     vpt->type,
                     fr_box_strvalue_len(vpt->name, vpt->len), vpt);
 
+       FR_FAULT_LOG("\tcast       : %s", fr_type_to_str(tmpl_rules_cast(vpt)));
        FR_FAULT_LOG("\tquote      : %s", fr_table_str_by_value(fr_token_quotes_table, vpt->quote, "<INVALID>"));
        switch (vpt->type) {
        case TMPL_TYPE_NULL:
@@ -592,13 +594,16 @@ tmpl_t *tmpl_init_printf(tmpl_t *vpt, tmpl_type_t type, fr_token_t quote, char c
  * @param[in] name     of the #tmpl_t.
  * @param[in] len      The length of the buffer (or a substring of the buffer) pointed to by name.
  *                     If < 0 strlen will be used to determine the length.
+ * @param[in] t_rules  used during parsing.
  * @return a pointer to the initialised #tmpl_t. The same value as vpt.
  */
-tmpl_t *tmpl_init_shallow(tmpl_t *vpt, tmpl_type_t type, fr_token_t quote, char const *name, ssize_t len)
+tmpl_t *tmpl_init_shallow(tmpl_t *vpt, tmpl_type_t type, fr_token_t quote,
+                         char const *name, ssize_t len, tmpl_rules_t const *t_rules)
 {
        memset(vpt, 0, sizeof(*vpt));
        tmpl_type_init(vpt, type);
        tmpl_set_name_shallow(vpt, quote, name, len);
+       if (t_rules) vpt->rules = *t_rules;
 
        return vpt;
 }
@@ -611,13 +616,16 @@ tmpl_t *tmpl_init_shallow(tmpl_t *vpt, tmpl_type_t type, fr_token_t quote, char
  * @param[in] name     to set for the tmpl.
  * @param[in] len      Name length.  If < 0 strlen will be used
  *                     to determine the name.
+ * @param[in] t_rules  used during parsing.
  * @return A pointer to the newly initialised tmpl.
  */
-tmpl_t *tmpl_init(tmpl_t *vpt, tmpl_type_t type, fr_token_t quote, char const *name, ssize_t len)
+tmpl_t *tmpl_init(tmpl_t *vpt, tmpl_type_t type, fr_token_t quote,
+                 char const *name, ssize_t len, tmpl_rules_t const *t_rules)
 {
        memset(vpt, 0, sizeof(*vpt));
        tmpl_type_init(vpt, type);
        tmpl_set_name(vpt, quote, name, len);
+       if (t_rules) vpt->rules = *t_rules;
 
        return vpt;
 }
@@ -747,7 +755,7 @@ int tmpl_afrom_value_box(TALLOC_CTX *ctx, tmpl_t **out, fr_value_box_t *data, bo
                return -1;
        }
 
-       tmpl_init_shallow(vpt, TMPL_TYPE_DATA, quote, name, slen);
+       tmpl_init_shallow(vpt, TMPL_TYPE_DATA, quote, name, slen, NULL);
 
        if (steal) {
                if (fr_value_box_steal(vpt, tmpl_value(vpt), data) < 0) goto error;
@@ -1818,17 +1826,20 @@ static inline int tmpl_request_ref_afrom_attr_substr(TALLOC_CTX *ctx, tmpl_attr_
 ssize_t tmpl_afrom_attr_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err,
                               tmpl_t **out, fr_sbuff_t *name,
                               fr_sbuff_parse_rules_t const *p_rules,
-                              tmpl_attr_rules_t const *t_rules)
+                              tmpl_rules_t const *t_rules)
 {
-       int                     ret;
-       size_t                  list_len = 0;
-       tmpl_t                  *vpt;
-       fr_sbuff_t              our_name = FR_SBUFF(name);      /* Take a local copy in case we need to back track */
-       bool                    ref_prefix = false;
-       bool                    is_raw = false;
-       fr_sbuff_marker_t       m_l;
+       int                             ret;
+       size_t                          list_len = 0;
+       tmpl_t                          *vpt;
+       fr_sbuff_t                      our_name = FR_SBUFF(name);      /* Take a local copy in case we need to back track */
+       bool                            ref_prefix = false;
+       bool                            is_raw = false;
+       tmpl_attr_rules_t const         *t_attr_rules;
+       fr_sbuff_marker_t               m_l;
 
-       if (!t_rules) t_rules = &default_attr_rules.attr;       /* Use the defaults */
+       if (!t_rules) t_rules = &default_attr_rules;    /* Use the defaults */
+
+       t_attr_rules = &t_rules->attr;
 
        if (err) *err = TMPL_ATTR_ERROR_NONE;
 
@@ -1841,7 +1852,7 @@ ssize_t tmpl_afrom_attr_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err,
        /*
         *      Check to see if we expect a reference prefix
         */
-       switch (t_rules->prefix) {
+       switch (t_attr_rules->prefix) {
        case TMPL_ATTR_REF_PREFIX_YES:
                if (!fr_sbuff_next_if_char(&our_name, '&')) {
                        fr_strerror_const("Invalid attribute reference, missing '&' prefix");
@@ -1878,16 +1889,17 @@ ssize_t tmpl_afrom_attr_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err,
        /*
         *      Parse one or more request references
         */
-       ret = tmpl_request_ref_afrom_attr_substr(vpt, err, vpt, &our_name, p_rules, &t_rules, 0);
+       ret = tmpl_request_ref_afrom_attr_substr(vpt, err, vpt, &our_name, p_rules, &t_attr_rules, 0);
        if (ret < 0) {
        error:
+               *out = NULL;
                talloc_free(vpt);
                FR_SBUFF_ERROR_RETURN(&our_name);
        }
 
        fr_sbuff_marker(&m_l, &our_name);
 
-       if (!t_rules->list_as_attr) {
+       if (!t_attr_rules->list_as_attr) {
                /*
                 *      Parse the list reference
                 *
@@ -1895,7 +1907,7 @@ ssize_t tmpl_afrom_attr_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err,
                 *      are integrated into attribute references.
                 */
                fr_sbuff_out_by_longest_prefix(&list_len, &vpt->data.attribute.list, pair_list_table,
-                                              &our_name, t_rules->list_def);
+                                              &our_name, t_attr_rules->list_def);
 
                /*
                 *      Check if we need to backtrack
@@ -1911,10 +1923,10 @@ ssize_t tmpl_afrom_attr_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err,
                    !fr_sbuff_is_char(&our_name, '[') && !tmpl_substr_terminal_check(&our_name, p_rules)) {
                        fr_sbuff_set(&our_name, &m_l);
                        list_len = 0;
-                       vpt->data.attribute.list = t_rules->list_def;
+                       vpt->data.attribute.list = t_attr_rules->list_def;
                }
 
-               if ((t_rules->parent || t_rules->disallow_qualifiers) && (list_len > 0)) {
+               if ((t_attr_rules->parent || t_attr_rules->disallow_qualifiers) && (list_len > 0)) {
                        fr_strerror_const("It is not permitted to specify a pair list here");
                        if (err) *err = TMPL_ATTR_ERROR_INVALID_LIST_QUALIFIER;
                        talloc_free(vpt);
@@ -1933,8 +1945,8 @@ ssize_t tmpl_afrom_attr_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err,
            (fr_sbuff_next_if_char(&our_name, '.') && fr_sbuff_is_in_charset(&our_name, fr_dict_attr_allowed_chars))) {
                ret = tmpl_attr_afrom_attr_substr(vpt, err,
                                                  vpt,
-                                                 t_rules->parent, t_rules->parent,
-                                                 &our_name, p_rules, t_rules, 0);
+                                                 t_attr_rules->parent, t_attr_rules->parent,
+                                                 &our_name, p_rules, t_attr_rules, 0);
                if (ret < 0) goto error;
 
                /*
@@ -1954,7 +1966,7 @@ ssize_t tmpl_afrom_attr_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err,
                 *      and we're parsing in list_as_attr mode, then
                 *      we need to add in a default list.
                 */
-               if (t_rules->list_as_attr) {
+               if (t_attr_rules->list_as_attr) {
                        tmpl_attr_t *ar;
 
                        ar = tmpl_attr_list_head(&vpt->data.attribute.ar);
@@ -1971,7 +1983,7 @@ ssize_t tmpl_afrom_attr_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err,
                                        .ar_parent = fr_dict_root(fr_dict_internal())
                                };
 
-                               switch (t_rules->list_def) {
+                               switch (t_attr_rules->list_def) {
                                default:
                                case PAIR_LIST_REQUEST:
                                        ar->ar_da = request_attr_request;
@@ -2009,7 +2021,7 @@ ssize_t tmpl_afrom_attr_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err,
                tmpl_attr_t *ar;
 
                MEM(ar = talloc_zero(vpt, tmpl_attr_t));
-               switch (tmpl_attr_parse_filter(err, ar, &our_name, t_rules)) {
+               switch (tmpl_attr_parse_filter(err, ar, &our_name, t_attr_rules)) {
                case 0:                                         /* No filter */
                        talloc_free(ar);
                        break;
@@ -2026,14 +2038,25 @@ ssize_t tmpl_afrom_attr_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err,
        }
 
        tmpl_set_name(vpt, T_BARE_WORD, fr_sbuff_start(&our_name), fr_sbuff_used(&our_name));
-       vpt->rules.attr = *t_rules;     /* Record the rules */
+       vpt->rules = *t_rules;  /* Record the rules */
 
        if (!tmpl_substr_terminal_check(&our_name, p_rules)) {
                fr_strerror_const("Unexpected text after attribute reference");
                if (err) *err = TMPL_ATTR_ERROR_MISSING_TERMINATOR;
-               talloc_free(vpt);
-               *out = NULL;
-               return -fr_sbuff_used(&our_name);
+               goto error;
+       }
+
+       /*
+        *      If everything was resolved correctly
+        *      we now need to check the cast type.
+        */
+       if (!tmpl_needs_resolving(vpt) && !fr_type_is_null(t_rules->cast) &&
+           !fr_type_cast(t_rules->cast, tmpl_da(vpt)->type)) {
+               fr_strerror_printf("Cannot cast type '%s' to '%s'",
+                                  fr_type_to_str(tmpl_da(vpt)->type), fr_type_to_str(t_rules->cast));
+               if (err) *err = TMPL_ATTR_ERROR_BAD_CAST;
+               fr_sbuff_set_to_start(&our_name);
+               goto error;
        }
 
        TMPL_VERIFY(vpt);       /* Because we want to ensure we produced something sane */
@@ -2057,11 +2080,11 @@ ssize_t tmpl_afrom_attr_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err,
  *     name string isn't parsed.
  */
 ssize_t tmpl_afrom_attr_str(TALLOC_CTX *ctx, tmpl_attr_error_t *err,
-                           tmpl_t **out, char const *name, tmpl_attr_rules_t const *t_rules)
+                           tmpl_t **out, char const *name, tmpl_rules_t const *t_rules)
 {
        ssize_t slen, name_len;
 
-       if (!t_rules) t_rules = &default_attr_rules.attr;       /* Use the defaults */
+       if (!t_rules) t_rules = &default_attr_rules;    /* Use the defaults */
 
        name_len = strlen(name);
        slen = tmpl_afrom_attr_substr(ctx, err, out, &FR_SBUFF_IN(name, name_len), NULL, t_rules);
@@ -2087,7 +2110,7 @@ ssize_t tmpl_afrom_attr_str(TALLOC_CTX *ctx, tmpl_attr_error_t *err,
  * @param[out] out             where to write tmpl.
  * @param[in] in               sbuff to parse.
  * @param[in] quote            surrounding the operand to parse.
- * @param[in] d_rules          specifying the cast and any enumeration values.
+ * @param[in] t_rules          specifying the cast and any enumeration values.
  * @param[in] allow_enum       Whether parsing the value as an enum should be allowed.
  * @param[in] p_rules          formatting rules.
  * @return
@@ -2096,33 +2119,32 @@ ssize_t tmpl_afrom_attr_str(TALLOC_CTX *ctx, tmpl_attr_error_t *err,
  */
 static fr_slen_t tmpl_afrom_value_substr(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in,
                                         fr_token_t quote,
-                                        tmpl_data_rules_t const *d_rules, bool allow_enum,
+                                        tmpl_rules_t const *t_rules, bool allow_enum,
                                         fr_sbuff_parse_rules_t const *p_rules)
 {
        fr_sbuff_t      our_in = FR_SBUFF(in);
-       fr_value_box_t  tmp, *actual;
+       fr_value_box_t  tmp;
        tmpl_t          *vpt;
        fr_slen_t       slen;
 
-       if (!fr_type_is_leaf(d_rules->cast)) {
+       if (!fr_type_is_leaf(t_rules->cast)) {
                fr_strerror_printf("%s is not a valid cast type",
-                                  fr_type_to_str(d_rules->cast));
+                                  fr_type_to_str(t_rules->cast));
                return 0;
        }
 
        vpt = tmpl_alloc_null(ctx);
        slen = fr_value_box_from_substr(vpt, &tmp,
-                                       d_rules->cast, allow_enum ? d_rules->enumv : NULL,
+                                       t_rules->cast, allow_enum ? t_rules->enumv : NULL,
                                        &our_in, p_rules, false);
        if (slen < 0) {
                talloc_free(vpt);
                return slen;
        }
 
-       tmpl_init(vpt, TMPL_TYPE_DATA, quote, fr_sbuff_start(&our_in), fr_sbuff_used(&our_in));
+       tmpl_init(vpt, TMPL_TYPE_DATA, quote, fr_sbuff_start(&our_in), fr_sbuff_used(&our_in), t_rules);
 
-       actual = tmpl_value(vpt);
-       fr_value_box_copy_shallow(NULL, actual, &tmp);
+       fr_value_box_copy_shallow(NULL, tmpl_value(vpt), &tmp);
 
        *out = vpt;
 
@@ -2599,12 +2621,13 @@ ssize_t tmpl_afrom_substr(TALLOC_CTX *ctx, tmpl_t **out,
                          fr_sbuff_parse_rules_t const *p_rules,
                          tmpl_rules_t const *t_rules)
 {
-       fr_sbuff_t      our_in = FR_SBUFF(in);
+       fr_sbuff_t              our_in = FR_SBUFF(in);
 
-       fr_slen_t       slen;
-       char            *str;
+       fr_slen_t               slen;
+       fr_sbuff_parse_error_t  sberr;
+       char                    *str;
 
-       tmpl_t          *vpt = NULL;
+       tmpl_t                  *vpt = NULL;
 
        if (!t_rules) t_rules = &default_attr_rules;    /* Use the defaults */
 
@@ -2617,7 +2640,7 @@ ssize_t tmpl_afrom_substr(TALLOC_CTX *ctx, tmpl_t **out,
                 *      we find a '&' prefix.
                 */
                if (fr_sbuff_is_char(&our_in, '&')) return tmpl_afrom_attr_substr(ctx, NULL, out, in,
-                                                                                 p_rules, &t_rules->attr);
+                                                                                 p_rules, t_rules);
 
                /*
                 *      Allow bareword xlats if we
@@ -2641,7 +2664,7 @@ ssize_t tmpl_afrom_substr(TALLOC_CTX *ctx, tmpl_t **out,
 
                        if (flags.needs_resolving) UNRESOLVED_SET(&type);
 
-                       tmpl_init(vpt, type, quote, fr_sbuff_start(&our_in), slen);
+                       tmpl_init(vpt, type, quote, fr_sbuff_start(&our_in), slen, t_rules);
                        vpt->data.xlat.ex = head;
                        vpt->data.xlat.flags = flags;
 
@@ -2655,8 +2678,8 @@ ssize_t tmpl_afrom_substr(TALLOC_CTX *ctx, tmpl_t **out,
                /*
                 *      Deal with explicit casts...
                 */
-               if (!fr_type_is_null(t_rules->data.cast)) return tmpl_afrom_value_substr(ctx, out, in, quote,
-                                                                                        &t_rules->data, true, p_rules);
+               if (!fr_type_is_null(t_rules->cast)) return tmpl_afrom_value_substr(ctx, out, in, quote,
+                                                                                   t_rules, true, p_rules);
 
                /*
                 *      See if it's a boolean value
@@ -2708,7 +2731,6 @@ ssize_t tmpl_afrom_substr(TALLOC_CTX *ctx, tmpl_t **out,
 //             slen = tmpl_afrom_float_substr(ctx, out, &our_in);
 //             if (slen > 0) return fr_sbuff_set(in, &our_in);
 
-
                /*
                 *      See if it's a integer
                 */
@@ -2720,10 +2742,13 @@ ssize_t tmpl_afrom_substr(TALLOC_CTX *ctx, tmpl_t **out,
                 *      See if it's an attribute reference
                 *      without the prefix.
                 */
-               slen = tmpl_afrom_attr_substr(ctx, NULL, out, &our_in, p_rules, &t_rules->attr);
+               slen = tmpl_afrom_attr_substr(ctx, NULL, out, &our_in, p_rules, t_rules);
                if (slen > 0) goto done_bareword;
                fr_assert(!*out);
 
+               /*
+                *      Attempt to resolve enumeration values
+                */
                vpt = tmpl_alloc_null(ctx);
 
                /*
@@ -2731,37 +2756,76 @@ ssize_t tmpl_afrom_substr(TALLOC_CTX *ctx, tmpl_t **out,
                 *      of bareword, assume it's an enum
                 *      value.
                 */
-               slen = fr_sbuff_out_aunescape_until(vpt, &str, &our_in, SIZE_MAX,
-                                                   p_rules ? p_rules->terminals : NULL,
-                                                   p_rules ? p_rules->escapes : NULL);
-               if (slen == 0) {
-                       fr_strerror_const("Empty bareword is invalid");
+               slen = fr_dict_enum_name_afrom_substr(vpt, &str, &sberr, &our_in, p_rules ? p_rules->terminals : NULL);
+               if (slen < 0) {
+                       /*
+                        *      Produce our own errors which make
+                        *      more sense in the context of tmpls
+                        */
+                       switch (sberr) {
+                       case FR_SBUFF_PARSE_ERROR_NOT_FOUND:
+                               fr_strerror_const("No operand found.  Expected &ref, literal, "
+                                                 "'quoted literal', \"%{expansion}\", or enum value");
+                               break;
+
+                       case FR_SBUFF_PARSE_ERROR_FORMAT:
+                               fr_strerror_const("enum values must contain at least one alpha character");
+                               break;
+
+                       default:
+                               fr_strerror_const("Unexpected text after enum value.  Expected operator");
+                               break;
+                       }
+               bareword_error:
                        talloc_free(vpt);
-                       return 0;
+                       return slen;
                }
 
-               tmpl_init(vpt, TMPL_TYPE_UNRESOLVED, T_BARE_WORD, fr_sbuff_start(&our_in), slen);
-               vpt->data.unescaped = str;
+               /*
+                *      If we have an enumv in the rules then
+                *      do the lookup now and fail early.
+                */
+               if (t_rules->enumv) {
+                       fr_dict_enum_value_t *dv;
+
+                       dv = fr_dict_enum_by_name(t_rules->enumv, str, slen);
+                       if (!dv) {
+                               fr_strerror_printf("enum value '%s' is not an enumeration of attribute '%s'",
+                                                  vpt->data.unescaped, t_rules->enumv->name);
+                               goto bareword_error;
+                       }
+
+                       if (unlikely(fr_value_box_copy(vpt, tmpl_value(vpt), dv->value) < 0)) {
+                               fr_strerror_const("Failed copying enum");
+                               goto bareword_error;
+                       }
+                       tmpl_init(vpt, TMPL_TYPE_DATA, quote,
+                                 fr_sbuff_start(&our_in), fr_sbuff_used(&our_in), t_rules);
+
+                       talloc_free(str);
+               } else {
+                       tmpl_init(vpt, TMPL_TYPE_UNRESOLVED, quote,
+                                 fr_sbuff_start(&our_in), fr_sbuff_used(&our_in), t_rules);
+                       vpt->data.unescaped = str;
+               }
                *out = vpt;
 
                return fr_sbuff_set(in, &our_in);
 
        case T_SINGLE_QUOTED_STRING:
                /*
-                *      Single quoted strings can be
-                *      cast to a specific data type
-                *      immediately as they cannot contain
-                *      expansions.
+                *      Single quoted strings can be cast
+                *      to a specific data type immediately
+                *      as they cannot contain expansions.
                 */
-               if (!fr_type_is_null(t_rules->data.cast)) return tmpl_afrom_value_substr(ctx, out, in, quote,
-                                                                                        &t_rules->data, false,
-                                                                                        p_rules);
-
+               if (!fr_type_is_null(t_rules->cast)) return tmpl_afrom_value_substr(ctx, out, in, quote,
+                                                                                   t_rules, false,
+                                                                                   p_rules);
                vpt = tmpl_alloc_null(ctx);
                slen = fr_sbuff_out_aunescape_until(vpt, &str, &our_in, SIZE_MAX,
                                                    p_rules ? p_rules->terminals : NULL,
                                                    p_rules ? p_rules->escapes : NULL);
-               tmpl_init(vpt, TMPL_TYPE_UNRESOLVED, quote, fr_sbuff_start(&our_in), slen);
+               tmpl_init(vpt, TMPL_TYPE_UNRESOLVED, quote, fr_sbuff_start(&our_in), slen, t_rules);
                vpt->data.unescaped = str;
                break;
 
@@ -2787,12 +2851,12 @@ ssize_t tmpl_afrom_substr(TALLOC_CTX *ctx, tmpl_t **out,
                 *      type, then do the conversion now.
                 */
                if (xlat_is_literal(head)) {
-                       if (!fr_type_is_null(t_rules->data.cast)) {
+                       if (!fr_type_is_null(t_rules->cast)) {
                                talloc_free(vpt);               /* Also frees any nodes */
 
                                return tmpl_afrom_value_substr(ctx, out,
                                                               in, quote,
-                                                              &t_rules->data, false, p_rules);
+                                                              t_rules, false, p_rules);
                        }
 
                        /*
@@ -2803,7 +2867,8 @@ ssize_t tmpl_afrom_substr(TALLOC_CTX *ctx, tmpl_t **out,
                        if (xlat_to_string(vpt, &str, &head)) {
                                xlat_exp_free(&head);           /* Free up any memory */
 
-                               tmpl_init(vpt, TMPL_TYPE_UNRESOLVED, quote, fr_sbuff_start(&our_in), slen);
+                               tmpl_init(vpt, TMPL_TYPE_UNRESOLVED, quote,
+                                        fr_sbuff_start(&our_in), slen, t_rules);
                                vpt->data.unescaped = str;      /* Store the unescaped string for parsing later */
                                break;
                        }
@@ -2815,7 +2880,7 @@ ssize_t tmpl_afrom_substr(TALLOC_CTX *ctx, tmpl_t **out,
                 */
                if (flags.needs_resolving) UNRESOLVED_SET(&type);
 
-               tmpl_init(vpt, type, quote, fr_sbuff_start(&our_in), slen);
+               tmpl_init(vpt, type, quote, fr_sbuff_start(&our_in), slen, t_rules);
                vpt->data.xlat.ex = head;
                vpt->data.xlat.flags = flags;
        }
@@ -2846,7 +2911,7 @@ ssize_t tmpl_afrom_substr(TALLOC_CTX *ctx, tmpl_t **out,
 
                if (flags.needs_resolving) UNRESOLVED_SET(&type);
 
-               tmpl_init(vpt, type, quote, fr_sbuff_start(&our_in), slen);
+               tmpl_init(vpt, type, quote, fr_sbuff_start(&our_in), slen, t_rules);
                vpt->data.xlat.ex = head;
                vpt->data.xlat.flags = flags;
        }
@@ -2859,6 +2924,11 @@ ssize_t tmpl_afrom_substr(TALLOC_CTX *ctx, tmpl_t **out,
                xlat_flags_t            flags = {};
                tmpl_type_t             type = TMPL_TYPE_REGEX_XLAT;
 
+               if (!fr_type_is_null(t_rules->cast)) {
+                       fr_strerror_const("Casts cannot be used with regular expressions");
+                       return -1;
+               }
+
                vpt = tmpl_alloc_null(ctx);
 
                slen = xlat_tokenize(vpt, &head, &flags, &our_in, p_rules, &t_rules->attr);
@@ -2875,7 +2945,8 @@ ssize_t tmpl_afrom_substr(TALLOC_CTX *ctx, tmpl_t **out,
                 *      return.
                 */
                if (xlat_to_string(vpt, &str, &head)) {
-                       tmpl_init(vpt, TMPL_TYPE_REGEX_UNCOMPILED, quote, fr_sbuff_start(&our_in), slen);
+                       tmpl_init(vpt, TMPL_TYPE_REGEX_UNCOMPILED, quote,
+                                 fr_sbuff_start(&our_in), slen, t_rules);
                        vpt->data.unescaped = str;      /* Store the unescaped string for compilation later */
                        break;
                }
@@ -2886,7 +2957,7 @@ ssize_t tmpl_afrom_substr(TALLOC_CTX *ctx, tmpl_t **out,
                 */
                if (flags.needs_resolving) UNRESOLVED_SET(&type);
 
-               tmpl_init(vpt, type, quote, fr_sbuff_start(&our_in), slen);
+               tmpl_init(vpt, type, quote, fr_sbuff_start(&our_in), slen, t_rules);
                vpt->data.xlat.ex = head;
                vpt->data.xlat.flags = flags;
        }
@@ -2980,15 +3051,14 @@ tmpl_t *tmpl_copy(TALLOC_CTX *ctx, tmpl_t const *in)
  *  Not for any particular reason, but to emphasize a bit that they're
  *  not mathematical expressions.
  *
- * @param[out] out     Where to write the cast type.
- *                     Will default to FR_TYPE_NULL.
+ * @param[out] rules   to set the cast type in.
  * @param[in] in       String containing the cast marker.
  * @return
  *     - 0 no cast specifier found.
  *     - >0 the number of bytes parsed.
  *     - <0 offset of parse error.
  */
-ssize_t tmpl_cast_from_substr(fr_type_t *out, fr_sbuff_t *in)
+ssize_t tmpl_cast_from_substr(tmpl_rules_t *rules, fr_sbuff_t *in)
 {
        char                    close = '\0';
        fr_sbuff_t              our_in = FR_SBUFF(in);
@@ -3003,7 +3073,7 @@ ssize_t tmpl_cast_from_substr(fr_type_t *out, fr_sbuff_t *in)
                close = ')';
 
        } else {
-               if (out) *out = FR_TYPE_NULL;
+               if (rules) rules->cast = FR_TYPE_NULL;
                return 0;
        }
 
@@ -3014,7 +3084,7 @@ ssize_t tmpl_cast_from_substr(fr_type_t *out, fr_sbuff_t *in)
                FR_SBUFF_ERROR_RETURN(&our_in);
        }
        if (fr_type_is_non_leaf(cast)) {
-               fr_strerror_const("Forbidden data type in cast");
+               fr_strerror_printf("Forbidden data type '%s' in cast", fr_type_to_str(cast));
                FR_SBUFF_MARKER_ERROR_RETURN(&m);
        }
 
@@ -3024,7 +3094,7 @@ ssize_t tmpl_cast_from_substr(fr_type_t *out, fr_sbuff_t *in)
        }
        fr_sbuff_adv_past_whitespace(&our_in, SIZE_MAX, NULL);
 
-       if (out) *out = cast;
+       if (rules) rules->cast = cast;
 
        return fr_sbuff_set(in, &our_in);
 }
@@ -3115,7 +3185,7 @@ int tmpl_cast_set(tmpl_t *vpt, fr_type_t dst_type)
        }
 
 done:
-       vpt->rules.data.cast = dst_type;
+       vpt->rules.cast = dst_type;
        return 0;
 }
 
@@ -3278,7 +3348,7 @@ int tmpl_cast_in_place(tmpl_t *vpt, fr_type_t type, fr_dict_attr_t const *enumv)
 
        case TMPL_TYPE_ATTR:    /* FIXME - We should check cast compatibility with resolved attrs */
        case TMPL_TYPE_ATTR_UNRESOLVED:
-               vpt->rules.data.cast = type;
+               vpt->rules.cast = type;
                break;
 
        default:
@@ -3682,6 +3752,11 @@ int tmpl_attr_unknown_add(tmpl_t *vpt)
 
        if (!vpt) return 1;
 
+       /*
+        *      Can't do this for expressions parsed at runtime
+        */
+       if (vpt->rules.at_runtime) return 1;
+
        tmpl_assert_type(tmpl_is_attr(vpt));
 
        TMPL_VERIFY(vpt);
index 849bd3bcb1b44103351908c3d4a5c645daaf163c..a40c745e79674b51b82cb7a63f04b1df1bf7aeb0 100644 (file)
@@ -1710,7 +1710,7 @@ static unlang_t *compile_edit_section(unlang_t *parent, unlang_compile_t *unlang
 
        name = cf_section_name1(cs);
 
-       slen = tmpl_afrom_attr_str(map, NULL, &map->lhs, name, &t_rules.attr);
+       slen = tmpl_afrom_attr_str(map, NULL, &map->lhs, name, &t_rules);
        if (slen <= 0) {
                cf_log_err(cs, "Failed parsing list reference %s", name);
        fail:
@@ -3454,7 +3454,7 @@ static unlang_t *compile_subrequest(unlang_t *parent, unlang_compile_t *unlang_c
 
                slen = tmpl_afrom_attr_substr(parent, NULL, &vpt,
                                              &FR_SBUFF_IN(name2, talloc_array_length(name2) - 1),
-                                             NULL, &unlang_ctx->rules->attr);
+                                             NULL, unlang_ctx->rules);
                if (slen <= 0) {
                        cf_log_perr(cs, "Invalid argument to 'subrequest', failed parsing packet-type");
                        return NULL;
index 81955e7e52c458eae3e3629224ffbe668bcec0d0..6a71e442df00a388fab46a2b384098260486e43a 100644 (file)
@@ -88,9 +88,11 @@ static int templatize_lhs(TALLOC_CTX *ctx, edit_result_t *out, request_t *reques
         *      anything else.
         */
        slen = tmpl_afrom_attr_str(ctx, NULL, &out->to_free, box->vb_strvalue,
-                                  &(tmpl_attr_rules_t){
-                                          .dict_def = request->dict,
-                                          .prefix = TMPL_ATTR_REF_PREFIX_NO
+                                  &(tmpl_rules_t){
+                                       .attr = {
+                                               .dict_def = request->dict,
+                                               .prefix = TMPL_ATTR_REF_PREFIX_NO
+                                       }
                                   });
        if (slen <= 0) {
                RPEDEBUG("Left side expansion result \"%s\" is not an attribute reference", box->vb_strvalue);
index 06f5e9ecdbcf958c0d31acd91a226eafcc15fcec..eb3adecdd471407c6a8955bddab5bfe09877e686 100644 (file)
@@ -88,7 +88,7 @@ static unlang_action_t unlang_switch(rlm_rcode_t *p_result, request_t *request,
                len = tmpl_aexpand(request, &p, request, switch_gext->vpt, NULL, NULL);
                if (len < 0) goto find_null_case;
 
-               tmpl_init_shallow(&vpt, TMPL_TYPE_DATA, T_SINGLE_QUOTED_STRING, p, len);
+               tmpl_init_shallow(&vpt, TMPL_TYPE_DATA, T_SINGLE_QUOTED_STRING, p, len, NULL);
                fr_value_box_bstrndup_shallow(&vpt.data.literal, NULL, p, len, false);
                box = tmpl_value(&vpt);
        } else if (!fr_cond_assert_msg(0, "Invalid tmpl type %s", tmpl_type_to_str(switch_gext->vpt->type))) {
index fec8514e925c8a11ca9ca784dadef0ab1a6dfae1..a8217bd5593a6d7a615b502d82e577020935ddae 100644 (file)
@@ -50,7 +50,6 @@ typedef enum {
 
 typedef struct xlat_inst xlat_inst_t;
 typedef struct xlat_thread_inst xlat_thread_inst_t;
-typedef struct xlat_exp xlat_exp_t;
 
 #include <freeradius-devel/server/cf_util.h>
 #include <freeradius-devel/server/request.h>
index a0c636c9e9f7304bb73533005ba57b62f0ea2d24..c54f5fdb4d22a8015e50bebf8d8edcff14e182e7 100644 (file)
@@ -76,9 +76,11 @@ int xlat_fmt_get_vp(fr_pair_t **out, request_t *request, char const *name)
        *out = NULL;
 
        if (tmpl_afrom_attr_str(request, NULL, &vpt, name,
-                               &(tmpl_attr_rules_t){
-                                       .dict_def = request->dict,
-                                       .prefix = TMPL_ATTR_REF_PREFIX_AUTO
+                               &(tmpl_rules_t){
+                                       .attr = {
+                                               .dict_def = request->dict,
+                                               .prefix = TMPL_ATTR_REF_PREFIX_AUTO
+                                       }
                                }) <= 0) return -4;
 
        ret = tmpl_find_vp(out, request, vpt);
@@ -1004,9 +1006,11 @@ static xlat_action_t xlat_func_debug_attr(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcur
        fmt = attr->vb_strvalue;
 
        if (tmpl_afrom_attr_str(request, NULL, &vpt, fmt,
-                               &(tmpl_attr_rules_t){
-                                       .dict_def = request->dict,
-                                       .prefix = TMPL_ATTR_REF_PREFIX_AUTO
+                               &(tmpl_rules_t){
+                                       .attr = {
+                                               .dict_def = request->dict,
+                                               .prefix = TMPL_ATTR_REF_PREFIX_AUTO
+                                       }
                                }) <= 0) {
                RPEDEBUG("Invalid input");
                return XLAT_ACTION_FAIL;
@@ -2345,9 +2349,11 @@ static xlat_action_t xlat_func_pairs(TALLOC_CTX *ctx, fr_dcursor_t *out,
        fr_pair_t *vp;
 
        if (tmpl_afrom_attr_str(ctx, NULL, &vpt, in_head->vb_strvalue,
-                               &(tmpl_attr_rules_t){
-                                       .dict_def = request->dict,
-                                       .prefix = TMPL_ATTR_REF_PREFIX_AUTO
+                               &(tmpl_rules_t){
+                                       .attr = {
+                                               .dict_def = request->dict,
+                                               .prefix = TMPL_ATTR_REF_PREFIX_AUTO
+                                       }
                                }) <= 0) {
                RPEDEBUG("Invalid input");
                return XLAT_ACTION_FAIL;
@@ -3403,9 +3409,11 @@ static xlat_action_t protocol_encode_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out,
        memcpy(&tp_encode, xctx->inst, sizeof(tp_encode)); /* const issues */
 
        if (tmpl_afrom_attr_str(ctx, NULL, &vpt, in_head->vb_strvalue,
-                               &(tmpl_attr_rules_t){
-                                       .dict_def = request->dict,
-                                       .prefix = TMPL_ATTR_REF_PREFIX_AUTO
+                               &(tmpl_rules_t){
+                                       .attr = {
+                                               .dict_def = request->dict,
+                                               .prefix = TMPL_ATTR_REF_PREFIX_AUTO
+                                       }
                                }) <= 0) {
                RPEDEBUG("Failed parsing attribute reference");
                return XLAT_ACTION_FAIL;
index c00d7d258b747e8e15079b16eeb0520404f2fef9..072db28bf81bd2e36681c09f0ea9c2facb6f3d97 100644 (file)
@@ -777,7 +777,17 @@ static ssize_t tokenize_field(TALLOC_CTX *input_ctx, xlat_exp_t **head, xlat_fla
         *
         *      (foo) is an expression.  (uint32) is a cast.
         */
-       slen = tmpl_cast_from_substr(&cast_type, &in);
+       /*
+        *      Parse (optional) cast
+        */
+       {
+               tmpl_rules_t tmp_rules = {};
+
+               slen = tmpl_cast_from_substr(&tmp_rules, &in);
+
+               cast_type = tmp_rules.cast;
+       }
+
        if (slen > 0) {
                fr_assert(fr_type_is_leaf(cast_type));
 
@@ -857,7 +867,7 @@ static ssize_t tokenize_field(TALLOC_CTX *input_ctx, xlat_exp_t **head, xlat_fla
                MEM(node = xlat_exp_alloc_null(ctx));
                xlat_exp_set_type(node, XLAT_TMPL);
 
-               slen = tmpl_afrom_attr_substr(node, NULL, &vpt, &in, p_rules, &t_rules->attr);
+               slen = tmpl_afrom_attr_substr(node, NULL, &vpt, &in, p_rules, t_rules);
                if (slen <= 0) {
                        talloc_free(node);
                        talloc_free(free_ctx);
@@ -939,23 +949,23 @@ static ssize_t tokenize_field(TALLOC_CTX *input_ctx, xlat_exp_t **head, xlat_fla
 
                my_rules = *t_rules;
                my_rules.parent = t_rules;
-               my_rules.data.enumv = da;
+               my_rules.enumv = da;
 
                /*
                 *      Force parsing as a particular type.
                 */
                if (cast_type != FR_TYPE_NULL) {
-                       my_rules.data.cast = cast_type;
+                       my_rules.cast = cast_type;
 
                } else if (da) {
-                       my_rules.data.cast = da->type;
+                       my_rules.cast = da->type;
 
                } else {
                        /*
                         *      Cast it to the data type we were asked
                         *      to use.
                         */
-                       my_rules.data.cast = type;
+                       my_rules.cast = type;
                }
 
                /*
index 9e7f814d6cc275bf9314f3cd900e2bf36c12554f..fb224d43813ad4ae22967eefd2acb0f131780f7f 100644 (file)
@@ -585,16 +585,17 @@ static inline int xlat_tokenize_attribute(TALLOC_CTX *ctx, xlat_exp_t **head, xl
         *      and instead are "virtual" attributes like
         *      Foreach-Variable-N.
         */
-       tmpl_attr_rules_t                our_t_rules;
+       tmpl_rules_t             our_t_rules;
 
        if (t_rules) {
-               memcpy(&our_t_rules, t_rules, sizeof(our_t_rules));
+               memset(&our_t_rules, 0, sizeof(our_t_rules));
+               our_t_rules.attr = *t_rules;
        } else {
                memset(&our_t_rules, 0, sizeof(our_t_rules));
        }
 
-       our_t_rules.allow_unresolved = true;            /* So we can check for virtual attributes later */
-       our_t_rules.prefix = TMPL_ATTR_REF_PREFIX_NO;   /* Must be NO to stop %{&User-Name} */
+       our_t_rules.attr.allow_unresolved = true;               /* So we can check for virtual attributes later */
+       our_t_rules.attr.prefix = TMPL_ATTR_REF_PREFIX_NO;      /* Must be NO to stop %{&User-Name} */
 
        fr_sbuff_marker(&m_s, in);
 
index 4a9ce71064cdabbd94d3b739d47dd32efa6d1b6b..c130cea61e88a4839c530efd3de2138da2324471 100644 (file)
@@ -567,10 +567,12 @@ fr_dict_enum_value_t              *fr_dict_enum_by_name(fr_dict_attr_t const *da, char const
 
 ssize_t                        fr_dict_enum_by_name_substr(fr_dict_enum_value_t **out, fr_dict_attr_t const *da, fr_sbuff_t *in);
 
-fr_slen_t              fr_dict_enum_name_from_substr(fr_sbuff_t *out, fr_sbuff_t *in, fr_sbuff_term_t const *tt);
+fr_slen_t              fr_dict_enum_name_from_substr(fr_sbuff_t *out, fr_sbuff_parse_error_t *err,
+                                                     fr_sbuff_t *in, fr_sbuff_term_t const *tt);
 
-static inline fr_slen_t fr_dict_enum_name_afrom_substr(TALLOC_CTX *ctx, char **out, fr_sbuff_t *in, fr_sbuff_term_t const *tt)
-                       SBUFF_OUT_TALLOC_FUNC_NO_LEN_DEF(fr_dict_enum_name_from_substr, in, tt)
+static inline fr_slen_t fr_dict_enum_name_afrom_substr(TALLOC_CTX *ctx, char **out, fr_sbuff_parse_error_t *err,
+                                                      fr_sbuff_t *in, fr_sbuff_term_t const *tt)
+                       SBUFF_OUT_TALLOC_FUNC_NO_LEN_DEF(fr_dict_enum_name_from_substr, err, in, tt)
 /** @} */
 
 /** @name Dictionary and protocol loading
index d148b8b0d5c00b179ddfaf97a3651ae725d86ab8..ac11db0f6f4f4157511e51528768f59059165501 100644 (file)
@@ -1286,7 +1286,7 @@ static int dict_read_process_value(dict_tokenize_ctx_t *ctx, char **argv, int ar
         *      Verify the enum name matches the expected from.
         */
        enum_len = (fr_slen_t)strlen(argv[1]);
-       if (fr_dict_enum_name_from_substr(NULL, &FR_SBUFF_IN(argv[1], enum_len), NULL) != enum_len) {
+       if (fr_dict_enum_name_from_substr(NULL, NULL, &FR_SBUFF_IN(argv[1], enum_len), NULL) != enum_len) {
                fr_strerror_printf_push("Invalid VALUE name '%s' for attribute '%s'", argv[1], da->name);
                return -1;
        }
index 53194e3100ef5f8ee536c6640db1ac27360b2fa2..93b9a271e7f6706934f1ba0326aa31436e45a346 100644 (file)
@@ -2984,6 +2984,7 @@ ssize_t   fr_dict_enum_by_name_substr(fr_dict_enum_value_t **out, fr_dict_attr_t c
  * @param[out] out     The name string we managed to extract.
  *                     May be NULL in which case only the length of the name
  *                     will be returned.
+ * @param[out] err     Type of parsing error which occurred.  May be NULL.
  * @param[in] in       The string containing the enum identifier.
  * @param[in] tt       If non-null verify that a terminal sequence occurs
  *                     after the enumeration name.
@@ -2991,7 +2992,8 @@ ssize_t   fr_dict_enum_by_name_substr(fr_dict_enum_value_t **out, fr_dict_attr_t c
  *     - <0 the offset at which the parse error occurred.
  *     - >1 the number of bytes parsed.
  */
-fr_slen_t fr_dict_enum_name_from_substr(fr_sbuff_t *out, fr_sbuff_t *in, fr_sbuff_term_t const *tt)
+fr_slen_t fr_dict_enum_name_from_substr(fr_sbuff_t *out, fr_sbuff_parse_error_t *err,
+                                       fr_sbuff_t *in, fr_sbuff_term_t const *tt)
 {
        fr_sbuff_t our_in = FR_SBUFF(in);
        bool seen_alpha = false;
@@ -3003,11 +3005,13 @@ fr_slen_t fr_dict_enum_name_from_substr(fr_sbuff_t *out, fr_sbuff_t *in, fr_sbuf
 
        if (!seen_alpha) {
                if (fr_sbuff_used(&our_in) == 0) {
-                       fr_strerror_const("VALUE name empty");
+                       fr_strerror_const("VALUE name is empty");
+                       if (err) *err = FR_SBUFF_PARSE_ERROR_NOT_FOUND;
                        return -1;
                }
 
                fr_strerror_const("VALUE name must contain at least one alpha character");
+               if (err) *err = FR_SBUFF_PARSE_ERROR_FORMAT;
                return -1;
        }
 
@@ -3016,11 +3020,14 @@ fr_slen_t fr_dict_enum_name_from_substr(fr_sbuff_t *out, fr_sbuff_t *in, fr_sbuf
         */
        if (tt && !fr_sbuff_is_terminal(&our_in, tt)) {
                fr_strerror_const("VALUE name has trailing text");
+               if (err) *err = FR_SBUFF_PARSE_ERROR_TRAILING;
                return fr_sbuff_error(&our_in);
        }
 
        if (out) return fr_sbuff_out_bstrncpy_exact(out, in, fr_sbuff_used(&our_in));
 
+       if (err) *err = FR_SBUFF_PARSE_OK;
+
        return fr_sbuff_set(in, &our_in);
 }
 
index eed91a7dae02e3eee8eaa3f555c610c74dae9c39..d30fb3f1769013628e3396fd59169a371cef94b2 100644 (file)
@@ -217,6 +217,9 @@ typedef struct {
                                                        ///< that a token is not complete.
 } fr_sbuff_parse_rules_t;
 
+/** Standard parsing errors to be used by sbuff functions and other sbuff based parsing functions
+ *
+ */
 typedef enum {
        FR_SBUFF_PARSE_OK                       = 0,            //!< No error.
        FR_SBUFF_PARSE_ERROR_NOT_FOUND          = -1,           //!< String does not contain a token
index 23e59952bf226d61ad5b1bfe0c3875b7a1ea2ba8..7b328873154012af77c1319fc001f0291c5010ba 100644 (file)
@@ -292,7 +292,7 @@ static cache_status_t cache_entry_insert(UNUSED rlm_cache_config_t const *config
        /*
         *      Encode the entry created date
         */
-       tmpl_init_shallow(&created_value, TMPL_TYPE_DATA, T_BARE_WORD, "<TEMP>", 6);
+       tmpl_init_shallow(&created_value, TMPL_TYPE_DATA, T_BARE_WORD, "<TEMP>", 6, NULL);
        fr_value_box_init(&created_value.data.literal, FR_TYPE_DATE, NULL, true);
        tmpl_value(&created_value)->vb_date = c->created;
 
@@ -302,7 +302,7 @@ static cache_status_t cache_entry_insert(UNUSED rlm_cache_config_t const *config
         *      Although Redis objects expire on their own, we still need this
         *      to ignore entries that were created before the last epoch.
         */
-       tmpl_init_shallow(&expires_value, TMPL_TYPE_DATA, T_BARE_WORD, "<TEMP>", 6);
+       tmpl_init_shallow(&expires_value, TMPL_TYPE_DATA, T_BARE_WORD, "<TEMP>", 6, NULL);
        fr_value_box_init(&expires_value.data.literal, FR_TYPE_DATE, NULL, true);
        tmpl_value(&expires_value)->vb_date = c->expires;
 
index f44e4378bdfffc588452542507b8dccec9ca5f92..e6b629e3f187edf32d53b4275642ef526e0d9296 100644 (file)
@@ -841,9 +841,11 @@ xlat_action_t cache_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out,
        slen = tmpl_afrom_attr_substr(ctx, NULL, &target,
                                      &FR_SBUFF_IN(attr->vb_strvalue, attr->vb_length),
                                      NULL,
-                                     &(tmpl_attr_rules_t){
+                                     &(tmpl_rules_t){
+                                       .attr = {
                                                .dict_def = request->dict,
                                                .prefix = TMPL_ATTR_REF_PREFIX_AUTO
+                                       }
                                      });
        if (slen <= 0) {
                RPEDEBUG("Invalid key");
index 5eaa20b8f0fd49128667a6721f0f9259c740dec1..92e95d654e73a68292ee561eea2e2b07ff5194a5 100644 (file)
@@ -217,8 +217,10 @@ static bool get_number(request_t *request, char const **string, int64_t *answer)
                slen = tmpl_afrom_attr_substr(request, NULL, &vpt,
                                              &FR_SBUFF_IN(p, strlen(p)),
                                              &p_rules,
-                                             &(tmpl_attr_rules_t){
-                                               .dict_def = request->dict
+                                             &(tmpl_rules_t){
+                                               .attr = {
+                                                       .dict_def = request->dict
+                                               }
                                              });
                if (slen <= 0) {
                        RPEDEBUG("Failed parsing attribute name '%s'", p);
index 187073430d034617dfed213a554ca0a2f085fe36..934365c3ce79b8919397be32fec8347296d795b3 100644 (file)
@@ -198,8 +198,10 @@ static xlat_action_t json_encode_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out,
                slen = tmpl_afrom_attr_substr(ctx, NULL, &vpt,
                                              &sbuff,
                                              &json_arg_parse_rules,
-                                             &(tmpl_attr_rules_t){
-                                               .dict_def = request->dict
+                                             &(tmpl_rules_t){
+                                               .attr = {
+                                                       .dict_def = request->dict
+                                               }
                                              });
                if (slen <= 0) {
                        fr_sbuff_set(&sbuff, (size_t)(slen * -1));
index 2abb1e4dcd2f37f0bb58efc4cb1f00c48bccf985..73ef2e11ca0415c1634d00a894ea2110f6c6158e 100644 (file)
@@ -509,7 +509,7 @@ static unlang_action_t CC_HINT(nonnull) mod_do_linelog(rlm_rcode_t *p_result, mo
                tmpl_str = cf_pair_value(cp);
                if (!tmpl_str || (tmpl_str[0] == '\0')) {
                        RDEBUG2("Path \"%s\" resolves to an empty config pair", p);
-                       vpt_p = tmpl_init_shallow(&empty, TMPL_TYPE_DATA, T_DOUBLE_QUOTED_STRING, "", 0);
+                       vpt_p = tmpl_init_shallow(&empty, TMPL_TYPE_DATA, T_DOUBLE_QUOTED_STRING, "", 0, NULL);
                        fr_value_box_init_null(&empty.data.literal);
                        fr_value_box_strdup_shallow(&empty.data.literal, NULL, "", false);
                        goto build_vector;
index d7cc28462ca9706fa8995342c9684de2f938176e..b58af6aabce856846eb7ec93939ab052a438920e 100644 (file)
@@ -359,9 +359,11 @@ static void add_vp_tuple(TALLOC_CTX *ctx, request_t *request, fr_pair_list_t *vp
                DEBUG("%s: %s %s %s", function_name, ckey, fr_table_str_by_value(fr_tokens_table, op, "="), cval);
 
                if (tmpl_afrom_attr_str(request, NULL, &dst, ckey,
-                                       &(tmpl_attr_rules_t){
-                                               .dict_def = request->dict,
-                                               .list_def = PAIR_LIST_REPLY
+                                       &(tmpl_rules_t){
+                                               .attr = {
+                                                       .dict_def = request->dict,
+                                                       .list_def = PAIR_LIST_REPLY
+                                               }
                                        }) <= 0) {
                        ERROR("Failed to find attribute %s", ckey);
                        continue;
index 93fe9f4588f0a57967527b223c431f43496ce2e4..e89bf776206b1f09e832164b917bef8a26904f93 100644 (file)
@@ -324,9 +324,11 @@ static void mod_vptuple(TALLOC_CTX *ctx, module_ctx_t const *mctx, request_t *re
                }
 
                if (tmpl_afrom_attr_str(ctx, NULL, &dst, s1,
-                                       &(tmpl_attr_rules_t){
-                                               .dict_def = request->dict,
-                                               .list_def = PAIR_LIST_REPLY
+                                       &(tmpl_rules_t){
+                                               .attr = {
+                                                       .dict_def = request->dict,
+                                                       .list_def = PAIR_LIST_REPLY
+                                               }
                                        }) <= 0) {
                        ERROR("%s - Failed to find attribute %s.%s", funcname, list_name, s1);
                        continue;
index ff661afa98f7a78acf9a6931619ce66186367187..938a6d4fda7e5ad0aaedce8feb545d2085266231 100644 (file)
@@ -684,7 +684,7 @@ static ippool_rcode_t redis_ippool_allocate(rlm_redis_ippool_t const *inst, requ
                        .rhs = &ip_rhs
                };
 
-               tmpl_init_shallow(&ip_rhs, TMPL_TYPE_DATA, T_BARE_WORD, "", 0);
+               tmpl_init_shallow(&ip_rhs, TMPL_TYPE_DATA, T_BARE_WORD, "", 0, NULL);
                switch (reply->element[1]->type) {
                /*
                 *      Destination attribute may not be IPv4, in which case
@@ -747,7 +747,7 @@ static ippool_rcode_t redis_ippool_allocate(rlm_redis_ippool_t const *inst, requ
                                .rhs = &range_rhs
                        };
 
-                       tmpl_init_shallow(&range_rhs, TMPL_TYPE_DATA, T_DOUBLE_QUOTED_STRING, "", 0);
+                       tmpl_init_shallow(&range_rhs, TMPL_TYPE_DATA, T_DOUBLE_QUOTED_STRING, "", 0, NULL);
                        fr_value_box_bstrndup_shallow(&range_map.rhs->data.literal,
                                                      NULL, reply->element[2]->str, reply->element[2]->len, true);
                        if (map_to_request(request, &range_map, map_to_vp, NULL) < 0) {
@@ -779,7 +779,7 @@ static ippool_rcode_t redis_ippool_allocate(rlm_redis_ippool_t const *inst, requ
                        .rhs = &expiry_rhs
                };
 
-               tmpl_init_shallow(&expiry_rhs, TMPL_TYPE_DATA, T_DOUBLE_QUOTED_STRING, "", 0);
+               tmpl_init_shallow(&expiry_rhs, TMPL_TYPE_DATA, T_DOUBLE_QUOTED_STRING, "", 0, NULL);
                if (reply->element[3]->type != REDIS_REPLY_INTEGER) {
                        REDEBUG("Server returned unexpected type \"%s\" for expiry element (result[3])",
                                fr_table_str_by_value(redis_reply_types, reply->element[3]->type, "<UNKNOWN>"));
@@ -817,7 +817,7 @@ static ippool_rcode_t redis_ippool_update(rlm_redis_ippool_t const *inst, reques
        tmpl_t          range_rhs;
        map_t           range_map = { .lhs = inst->range_attr, .op = T_OP_SET, .rhs = &range_rhs };
 
-       tmpl_init_shallow(&range_rhs, TMPL_TYPE_DATA, T_DOUBLE_QUOTED_STRING, "", 0);
+       tmpl_init_shallow(&range_rhs, TMPL_TYPE_DATA, T_DOUBLE_QUOTED_STRING, "", 0, NULL);
 
        now = fr_time_to_timeval(fr_time());
 
@@ -925,7 +925,7 @@ static ippool_rcode_t redis_ippool_update(rlm_redis_ippool_t const *inst, reques
                };
 
 
-               tmpl_init_shallow(&expiry_rhs, TMPL_TYPE_DATA, T_DOUBLE_QUOTED_STRING, "", 0);
+               tmpl_init_shallow(&expiry_rhs, TMPL_TYPE_DATA, T_DOUBLE_QUOTED_STRING, "", 0, NULL);
 
                fr_value_box_shallow(&expiry_map.rhs->data.literal, expires, false);
                if (map_to_request(request, &expiry_map, map_to_vp, NULL) < 0) {
index 673955850f928bf8f861905fcf16d0ad2f8d40f5..8bb4502d222731c3e51f2667530d04717424d0ce 100644 (file)
@@ -736,10 +736,12 @@ static int rest_decode_post(UNUSED rlm_rest_t const *instance, UNUSED rlm_rest_s
                RDEBUG2("Parsing attribute \"%pV\"", fr_box_strvalue_len(name, curl_len));
 
                if (tmpl_afrom_attr_str(request, NULL, &dst, name,
-                                       &(tmpl_attr_rules_t){
-                                               .prefix = TMPL_ATTR_REF_PREFIX_NO,
-                                               .dict_def = request->dict,
-                                               .list_def = PAIR_LIST_REPLY
+                                       &(tmpl_rules_t){
+                                               .attr = {
+                                                       .prefix = TMPL_ATTR_REF_PREFIX_NO,
+                                                       .dict_def = request->dict,
+                                                       .list_def = PAIR_LIST_REPLY
+                                               }
                                        }) <= 0) {
                        RPWDEBUG("Failed parsing attribute (skipping)");
                        talloc_free(dst);
@@ -1024,10 +1026,12 @@ static int json_pair_alloc(rlm_rest_t const *instance, rlm_rest_section_t const
                RDEBUG2("Parsing attribute \"%s\"", name);
 
                if (tmpl_afrom_attr_str(request, NULL, &dst, name,
-                                       &(tmpl_attr_rules_t){
-                                               .prefix = TMPL_ATTR_REF_PREFIX_NO,
-                                               .dict_def = request->dict,
-                                               .list_def = PAIR_LIST_REPLY
+                                       &(tmpl_rules_t){
+                                               .attr = {
+                                                       .prefix = TMPL_ATTR_REF_PREFIX_NO,
+                                                       .dict_def = request->dict,
+                                                       .list_def = PAIR_LIST_REPLY
+                                               }
                                        }) <= 0) {
                        RPWDEBUG("Failed parsing attribute (skipping)");
                        continue;
index 9c58934d1fed39e4c80855518e2853e98308fe65..b9b8e4e98804d07a094a013a2a160bff8ed6a201 100644 (file)
@@ -18,21 +18,24 @@ condition ("foo
 match ERROR offset 2: Unterminated string
 
 condition ()
-match ERROR offset 1: Empty bareword is invalid
+match ERROR offset 2: No operand found.  Expected &ref, literal, 'quoted literal', "%{expansion}", or enum value
 
 condition (!)
-match ERROR offset 2: Empty bareword is invalid
+match ERROR offset 3: No operand found.  Expected &ref, literal, 'quoted literal', "%{expansion}", or enum value
 
 
 condition (|| b)
-match ERROR offset 1: Empty bareword is invalid
+match ERROR offset 2: No operand found.  Expected &ref, literal, 'quoted literal', "%{expansion}", or enum value
 
 condition ((ok || handled) foo)
 match ERROR offset 17: Unexpected text after condition
 
 # escapes in names are illegal
 condition (ok\ foo || handled)
-match ERROR offset 5: Invalid operator
+match ERROR offset 4: Unexpected text after enum value.  Expected operator
+
+condition (Service-Type == 000-111)
+match ERROR offset 18: enum values must contain at least one alpha character
 
 condition (ok FOO handled)
 match ERROR offset 4: Invalid operator
@@ -128,7 +131,7 @@ match !&User-Name == &User-Password
 condition !&User-Name != &User-Password
 match &User-Name == &User-Password
 
-condition <ipv6addr>foo
+condition <ipv6addr>::1
 match ERROR offset 0: Cannot do cast for existence check
 
 condition <ipaddr>&Filter-Id == &Framed-IP-Address
@@ -141,9 +144,6 @@ match <ipaddr>&Filter-Id == &Framed-IP-Address
 #  We can automatically promote things as needed.  But if the
 #  user forces incompatible types, then that's an error.
 #
-condition <ipaddr>&Filter-Id == <integer>&Framed-IP-Address
-match ERROR offset 0: Incompatible casts
-
 condition <ipaddr>&Filter-Id == <blerg>&Framed-IP-Address
 match ERROR offset 23: Unknown data type
 
@@ -154,7 +154,7 @@ match ERROR offset 1: Unknown data type
 #  Normalize things
 #
 condition <ipaddr>127.0.0.1 < &Framed-IP-Address
-match &Framed-IP-Address > 127.0.0.1
+match &Framed-IP-Address > <ipaddr>127.0.0.1
 
 # =* and !* are only for attrs / lists
 condition "foo" !* bar
@@ -219,7 +219,7 @@ condition &Event-Timestamp == 'January 1, 2012'
 
 # literals are parsed when the conditions are parsed
 condition <integer>X == 1
-match ERROR offset 9: Failed parsing string as type 'uint32'
+match ERROR offset 10: Failed parsing string as type 'uint32'
 
 condition &NAS-Port == X
 match ERROR offset 13: Failed parsing string as type 'uint32'
@@ -232,25 +232,25 @@ condition <ipaddr>127.0.0.1 == "127.0.0.1"
 match true
 
 condition <ipaddr>127.0.0.1 == "%{md4: 127.0.0.1}"
-match 127.0.0.1 == "%{md4: 127.0.0.1}"
+match <ipaddr>127.0.0.1 == "%{md4: 127.0.0.1}"
 
 #
 #  Bare %{...} is allowed.
 #
 condition <ipaddr>127.0.0.1 == %{md4:127.0.0.1}
-match 127.0.0.1 == %{md4:127.0.0.1}
+match <ipaddr>127.0.0.1 == %{md4:127.0.0.1}
 
 condition <ipaddr>127.0.0.1 == %{md4: SELECT user FROM table WHERE user='%{User-Name}'}
-match 127.0.0.1 == %{md4: SELECT user FROM table WHERE user='%{User-Name}'}
+match <ipaddr>127.0.0.1 == %{md4: SELECT user FROM table WHERE user='%{User-Name}'}
 
 condition <ether> 00:11:22:33:44:55 == "00:11:22:33:44:55"
 match true
 
-condition <ether> 00:11:22:33:44:55 == "%{md4:00:11:22:33:44:55}"
-match 00:11:22:33:44:55 == "%{md4:00:11:22:33:44:55}"
+condition <ether>00:11:22:33:44:55 == "%{md4:00:11:22:33:44:55}"
+match <ether>00:11:22:33:44:55 == "%{md4:00:11:22:33:44:55}"
 
 condition <ether> 00:XX:22:33:44:55 == 00:11:22:33:44:55
-match ERROR offset 8: Missing separator, expected ':'
+match ERROR offset 12: Missing separator, expected ':'
 
 #
 #  Tests for boolean data types.
@@ -331,7 +331,7 @@ condition <ipaddr>127.0.0.1/32 == 127.0.0.1
 match true
 
 condition <ipaddr>127.0.0.1/327 == 127.0.0.1
-match ERROR offset 8: Invalid IPv4 mask length "/327".  Should be between 0-32
+match ERROR offset 9: Invalid IPv4 mask length "/327".  Should be between 0-32
 
 condition <ipaddr>127.0.0.1/32 == 127.0.0.1
 match true
@@ -397,7 +397,7 @@ match &User-Name
 #  Forbidden data types in cast
 #
 condition (<vsa>"foo" == &User-Name)
-match ERROR offset 2: Forbidden data type in cast
+match ERROR offset 2: Forbidden data type 'vsa' in cast
 
 #
 #  If the LHS is a cast to a type, and the RHS is an attribute
@@ -405,7 +405,7 @@ match ERROR offset 2: Forbidden data type in cast
 #  is on the LHS of the condition.
 #
 condition <string>"foo" == &User-Name
-match &User-Name == "foo"
+match &User-Name == <string>"foo"
 
 # This used to be expr, but expr isn't a builtin, so it failed...
 condition <integer>"%{md4: 1 + 1}" < &NAS-Port
@@ -418,7 +418,7 @@ condition &Filter-Id == &Framed-IP-Address
 match <ipaddr>&Filter-Id == &Framed-IP-Address
 
 condition <ipaddr>127.0.0.1 == &Filter-Id
-match <ipaddr>&Filter-Id == 127.0.0.1
+match <ipaddr>&Filter-Id == <ipaddr>127.0.0.1
 
 condition &Tmp-uint64-0 == &request.Foo-Stuff-Bar
 match &Tmp-uint64-0 == &Foo-Stuff-Bar
@@ -511,15 +511,6 @@ match ERROR offset 11: Invalid array index
 condition &User-Name == &Filter-Id[a]
 match ERROR offset 25: Invalid array index
 
-#
-#  Attributes without an '&' prefix are
-#  just barewords.  So this is a comparison
-#  of 'User-Name[a]' == 'bob' which is
-#  false.T
-#
-condition User-Name[a] == 'bob'
-match false
-
 #
 #  Bounds checks...
 #
@@ -603,22 +594,7 @@ match <ipv4prefix>&NAS-IP-Address == 192.168.0.0/24
 #  is parsed as ipv4prefix
 #
 condition <ipv4prefix>192.168.0.0/24 > &NAS-IP-Address
-match <ipv4prefix>&NAS-IP-Address < 192.168.0.0/24
-
-#
-#  We add casts to the LHS if necessary
-#
-condition &NAS-IP-Address < &PMIP6-Home-IPv4-HoA
-match <ipv4prefix>&NAS-IP-Address < &PMIP6-Home-IPv4-HoA
-
-condition &NAS-IP-Address < 192.168/16
-match <ipv4prefix>&NAS-IP-Address < 192.168.0.0/16
-
-condition &NAS-IP-Address < "%{string: 192.168/16}"
-match <ipv4prefix>&NAS-IP-Address < "%{string: 192.168/16}"
-
-condition &NAS-IP-Address < `/bin/echo 192.168/16`
-match <ipv4prefix>&NAS-IP-Address < `/bin/echo 192.168/16`
+match <ipv4prefix>&NAS-IP-Address < <ipv4prefix>192.168.0.0/24
 
 #
 #  This is allowed and means "the list is not empty"
@@ -664,4 +640,4 @@ condition (&User-Name == "bob") && ((&User-Password == "bob") || &EAP-Message)
 match (&User-Name == "bob") && ((&User-Password == "bob") || &EAP-Message)
 
 count
-match 319
+match 309
index 945a16509f6763e0e34b5defd68f7983e31051ef..07cada746f3d3cbf20f4574b4cf3fdb170f8a402 100644 (file)
@@ -1,5 +1,5 @@
 #
-#  Tests for parsing conditional expressions.
+#  Tests for casts
 #
 #  $Id$
 #
@@ -9,11 +9,11 @@ tmpl-rules allow_unresolved=yes allow_unknown=yes
 
 # Forcefully cast RHS bareword
 condition &User-Name == <ipaddr>192.168.0.1
-match <ipaddr>&User-Name == 192.168.0.1
+match <ipaddr>&User-Name == <ipaddr>192.168.0.1
 
 # Forcefully cast LHS bareword
 condition <ipaddr>192.168.0.1 == &User-Name
-match <ipaddr>&User-Name == 192.168.0.1
+match <ipaddr>&User-Name == <ipaddr>192.168.0.1
 
 # Forcefully cast RHS single quotes
 #condition &Framed-IP-Address == <ipaddr>'192.168.0.1'
index bf75375d6d0e226e9ade24df4b50c1fd31623e34..535278ef1b34d786573946d108a83e393c423f5c 100644 (file)
@@ -62,10 +62,10 @@ condition  0x626f62 == bob
 match true
 
 condition  \n == 0x5c6e
-match true
+match ERROR offset 1: No operand found.  Expected &ref, literal, 'quoted literal', "%{expansion}", or enum value
 
 condition  a\n == 0x615c6e
-match true
+match ERROR offset 2: Unexpected text after enum value.  Expected operator
 
 count
 match 40
index 66a8a439e3e22a2105eba9d3924666055515f396..4d664ba4cbecebfdcf29cd10c8bcbed5cee51c5e 100644 (file)
@@ -10,8 +10,8 @@ proto-dictionary radius
 condition &Packet-Src-IPv6-Address == ::
 match &Packet-Src-IPv6-Address == ::
 
-condition &Packet-Src-IP-Address == *
-match &Packet-Src-IP-Address == 0.0.0.0
+condition &Packet-Src-IP-Address == <ipaddr>*
+match &Packet-Src-IP-Address == <ipaddr>0.0.0.0
 
 condition &Packet-Src-IP-Address == 0.0.0.0
 match &Packet-Src-IP-Address == 0.0.0.0
index 5062fc9c5391a976718ee7e65bd103e44fb8fea9..aadc5ed0b17037a481d073aa43ee1d6f363cb589 100644 (file)
@@ -69,10 +69,10 @@ match <string>&Tmp-Integer-0 =~ /%{Tmp-Integer-0}/
 #  Cannot add a bad cast
 #
 condition <integer>&Tmp-String-0 =~ /foo/
-match ERROR offset 9: Invalid cast used with regular expression
+match ERROR offset 9: Casts cannot be used with regular expressions
 
 condition &Tmp-String-0 =~ <integer>/foo/
-match ERROR offset 26: Invalid cast used with regular expression
+match ERROR offset 28: Casts cannot be used with regular expressions
 
 
 xlat %{1}