From: Arran Cudbard-Bell Date: Thu, 27 Jan 2022 18:30:01 +0000 (-0600) Subject: Only allow things which look like enums during tokenization X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=43fe98420267630c8f57bce8e0736b277cb30981;p=thirdparty%2Ffreeradius-server.git Only allow things which look like enums during tokenization --- diff --git a/src/bin/all.mk b/src/bin/all.mk index 4dfdfa6acb..f94ef0b35b 100644 --- a/src/bin/all.mk +++ b/src/bin/all.mk @@ -6,6 +6,7 @@ SUBMAKEFILES := \ radlock.mk \ radsniff.mk \ radsnmp.mk \ + radsizes.mk \ radwho.mk \ radtest.mk \ radzap.mk \ diff --git a/src/bin/unit_test_attribute.c b/src/bin/unit_test_attribute.c index a251718339..0232aa4bfd 100644 --- a/src/bin/unit_test_attribute.c +++ b/src/bin/unit_test_attribute.c @@ -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); diff --git a/src/lib/redis/redis.c b/src/lib/redis/redis.c index b352fc20e3..8d8a7e6ba1 100644 --- a/src/lib/redis/redis.c +++ b/src/lib/redis/redis.c @@ -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()); diff --git a/src/lib/server/cf_parse.c b/src/lib/server/cf_parse.c index dddaf63105..b53c8819e9 100644 --- a/src/lib/server/cf_parse.c +++ b/src/lib/server/cf_parse.c @@ -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) { diff --git a/src/lib/server/cond_tokenize.c b/src/lib/server/cond_tokenize.c index bae2bc63f9..25c937881f 100644 --- a/src/lib/server/cond_tokenize.c +++ b/src/lib/server/cond_tokenize.c @@ -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; diff --git a/src/lib/server/map.c b/src/lib/server/map.c index 82bc43213e..711ecebfc6 100644 --- a/src/lib/server/map.c +++ b/src/lib/server/map.c @@ -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); diff --git a/src/lib/server/map_async.c b/src/lib/server/map_async.c index 2d32b9fac7..771d885cf2 100644 --- a/src/lib/server/map_async.c +++ b/src/lib/server/map_async.c @@ -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", diff --git a/src/lib/server/tmpl.h b/src/lib/server/tmpl.h index 8e98b1d058..5256a5a237 100644 --- a/src/lib/server/tmpl.h +++ b/src/lib/server/tmpl.h @@ -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 */ diff --git a/src/lib/server/tmpl_tokenize.c b/src/lib/server/tmpl_tokenize.c index 9d3555e9e7..e964a11f37 100644 --- a/src/lib/server/tmpl_tokenize.c +++ b/src/lib/server/tmpl_tokenize.c @@ -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, "")); + 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, "")); 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, "")); 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); diff --git a/src/lib/unlang/compile.c b/src/lib/unlang/compile.c index 849bd3bcb1..a40c745e79 100644 --- a/src/lib/unlang/compile.c +++ b/src/lib/unlang/compile.c @@ -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; diff --git a/src/lib/unlang/edit.c b/src/lib/unlang/edit.c index 81955e7e52..6a71e442df 100644 --- a/src/lib/unlang/edit.c +++ b/src/lib/unlang/edit.c @@ -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); diff --git a/src/lib/unlang/switch.c b/src/lib/unlang/switch.c index 06f5e9ecdb..eb3adecdd4 100644 --- a/src/lib/unlang/switch.c +++ b/src/lib/unlang/switch.c @@ -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))) { diff --git a/src/lib/unlang/xlat.h b/src/lib/unlang/xlat.h index fec8514e92..a8217bd559 100644 --- a/src/lib/unlang/xlat.h +++ b/src/lib/unlang/xlat.h @@ -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 #include diff --git a/src/lib/unlang/xlat_builtin.c b/src/lib/unlang/xlat_builtin.c index a0c636c9e9..c54f5fdb4d 100644 --- a/src/lib/unlang/xlat_builtin.c +++ b/src/lib/unlang/xlat_builtin.c @@ -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; diff --git a/src/lib/unlang/xlat_expr.c b/src/lib/unlang/xlat_expr.c index c00d7d258b..072db28bf8 100644 --- a/src/lib/unlang/xlat_expr.c +++ b/src/lib/unlang/xlat_expr.c @@ -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; } /* diff --git a/src/lib/unlang/xlat_tokenize.c b/src/lib/unlang/xlat_tokenize.c index 9e7f814d6c..fb224d4381 100644 --- a/src/lib/unlang/xlat_tokenize.c +++ b/src/lib/unlang/xlat_tokenize.c @@ -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); diff --git a/src/lib/util/dict.h b/src/lib/util/dict.h index 4a9ce71064..c130cea61e 100644 --- a/src/lib/util/dict.h +++ b/src/lib/util/dict.h @@ -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 diff --git a/src/lib/util/dict_tokenize.c b/src/lib/util/dict_tokenize.c index d148b8b0d5..ac11db0f6f 100644 --- a/src/lib/util/dict_tokenize.c +++ b/src/lib/util/dict_tokenize.c @@ -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; } diff --git a/src/lib/util/dict_util.c b/src/lib/util/dict_util.c index 53194e3100..93b9a271e7 100644 --- a/src/lib/util/dict_util.c +++ b/src/lib/util/dict_util.c @@ -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); } diff --git a/src/lib/util/sbuff.h b/src/lib/util/sbuff.h index eed91a7dae..d30fb3f176 100644 --- a/src/lib/util/sbuff.h +++ b/src/lib/util/sbuff.h @@ -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 diff --git a/src/modules/rlm_cache/drivers/rlm_cache_redis/rlm_cache_redis.c b/src/modules/rlm_cache/drivers/rlm_cache_redis/rlm_cache_redis.c index 23e59952bf..7b32887315 100644 --- a/src/modules/rlm_cache/drivers/rlm_cache_redis/rlm_cache_redis.c +++ b/src/modules/rlm_cache/drivers/rlm_cache_redis/rlm_cache_redis.c @@ -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, "", 6); + tmpl_init_shallow(&created_value, TMPL_TYPE_DATA, T_BARE_WORD, "", 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, "", 6); + tmpl_init_shallow(&expires_value, TMPL_TYPE_DATA, T_BARE_WORD, "", 6, NULL); fr_value_box_init(&expires_value.data.literal, FR_TYPE_DATE, NULL, true); tmpl_value(&expires_value)->vb_date = c->expires; diff --git a/src/modules/rlm_cache/rlm_cache.c b/src/modules/rlm_cache/rlm_cache.c index f44e4378bd..e6b629e3f1 100644 --- a/src/modules/rlm_cache/rlm_cache.c +++ b/src/modules/rlm_cache/rlm_cache.c @@ -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"); diff --git a/src/modules/rlm_expr/rlm_expr.c b/src/modules/rlm_expr/rlm_expr.c index 5eaa20b8f0..92e95d654e 100644 --- a/src/modules/rlm_expr/rlm_expr.c +++ b/src/modules/rlm_expr/rlm_expr.c @@ -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); diff --git a/src/modules/rlm_json/rlm_json.c b/src/modules/rlm_json/rlm_json.c index 187073430d..934365c3ce 100644 --- a/src/modules/rlm_json/rlm_json.c +++ b/src/modules/rlm_json/rlm_json.c @@ -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)); diff --git a/src/modules/rlm_linelog/rlm_linelog.c b/src/modules/rlm_linelog/rlm_linelog.c index 2abb1e4dcd..73ef2e11ca 100644 --- a/src/modules/rlm_linelog/rlm_linelog.c +++ b/src/modules/rlm_linelog/rlm_linelog.c @@ -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; diff --git a/src/modules/rlm_mruby/rlm_mruby.c b/src/modules/rlm_mruby/rlm_mruby.c index d7cc28462c..b58af6aabc 100644 --- a/src/modules/rlm_mruby/rlm_mruby.c +++ b/src/modules/rlm_mruby/rlm_mruby.c @@ -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; diff --git a/src/modules/rlm_python/rlm_python.c b/src/modules/rlm_python/rlm_python.c index 93fe9f4588..e89bf77620 100644 --- a/src/modules/rlm_python/rlm_python.c +++ b/src/modules/rlm_python/rlm_python.c @@ -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; diff --git a/src/modules/rlm_redis_ippool/rlm_redis_ippool.c b/src/modules/rlm_redis_ippool/rlm_redis_ippool.c index ff661afa98..938a6d4fda 100644 --- a/src/modules/rlm_redis_ippool/rlm_redis_ippool.c +++ b/src/modules/rlm_redis_ippool/rlm_redis_ippool.c @@ -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, "")); @@ -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) { diff --git a/src/modules/rlm_rest/rest.c b/src/modules/rlm_rest/rest.c index 673955850f..8bb4502d22 100644 --- a/src/modules/rlm_rest/rest.c +++ b/src/modules/rlm_rest/rest.c @@ -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; diff --git a/src/tests/unit/condition/base.txt b/src/tests/unit/condition/base.txt index 9c58934d1f..b9b8e4e988 100644 --- a/src/tests/unit/condition/base.txt +++ b/src/tests/unit/condition/base.txt @@ -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 foo +condition ::1 match ERROR offset 0: Cannot do cast for existence check condition &Filter-Id == &Framed-IP-Address @@ -141,9 +144,6 @@ match &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 &Filter-Id == &Framed-IP-Address -match ERROR offset 0: Incompatible casts - condition &Filter-Id == &Framed-IP-Address match ERROR offset 23: Unknown data type @@ -154,7 +154,7 @@ match ERROR offset 1: Unknown data type # Normalize things # condition 127.0.0.1 < &Framed-IP-Address -match &Framed-IP-Address > 127.0.0.1 +match &Framed-IP-Address > 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 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 127.0.0.1 == "127.0.0.1" match true condition 127.0.0.1 == "%{md4: 127.0.0.1}" -match 127.0.0.1 == "%{md4: 127.0.0.1}" +match 127.0.0.1 == "%{md4: 127.0.0.1}" # # Bare %{...} is allowed. # condition 127.0.0.1 == %{md4:127.0.0.1} -match 127.0.0.1 == %{md4:127.0.0.1} +match 127.0.0.1 == %{md4:127.0.0.1} condition 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 127.0.0.1 == %{md4: SELECT user FROM table WHERE user='%{User-Name}'} condition 00:11:22:33:44:55 == "00:11:22:33:44:55" match true -condition 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 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 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 127.0.0.1/32 == 127.0.0.1 match true condition 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 127.0.0.1/32 == 127.0.0.1 match true @@ -397,7 +397,7 @@ match &User-Name # Forbidden data types in cast # condition ("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 "foo" == &User-Name -match &User-Name == "foo" +match &User-Name == "foo" # This used to be expr, but expr isn't a builtin, so it failed... condition "%{md4: 1 + 1}" < &NAS-Port @@ -418,7 +418,7 @@ condition &Filter-Id == &Framed-IP-Address match &Filter-Id == &Framed-IP-Address condition 127.0.0.1 == &Filter-Id -match &Filter-Id == 127.0.0.1 +match &Filter-Id == 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 &NAS-IP-Address == 192.168.0.0/24 # is parsed as ipv4prefix # condition 192.168.0.0/24 > &NAS-IP-Address -match &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 &NAS-IP-Address < &PMIP6-Home-IPv4-HoA - -condition &NAS-IP-Address < 192.168/16 -match &NAS-IP-Address < 192.168.0.0/16 - -condition &NAS-IP-Address < "%{string: 192.168/16}" -match &NAS-IP-Address < "%{string: 192.168/16}" - -condition &NAS-IP-Address < `/bin/echo 192.168/16` -match &NAS-IP-Address < `/bin/echo 192.168/16` +match &NAS-IP-Address < 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 diff --git a/src/tests/unit/condition/casts.txt b/src/tests/unit/condition/casts.txt index 945a16509f..07cada746f 100644 --- a/src/tests/unit/condition/casts.txt +++ b/src/tests/unit/condition/casts.txt @@ -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 == 192.168.0.1 -match &User-Name == 192.168.0.1 +match &User-Name == 192.168.0.1 # Forcefully cast LHS bareword condition 192.168.0.1 == &User-Name -match &User-Name == 192.168.0.1 +match &User-Name == 192.168.0.1 # Forcefully cast RHS single quotes #condition &Framed-IP-Address == '192.168.0.1' diff --git a/src/tests/unit/condition/escape.txt b/src/tests/unit/condition/escape.txt index bf75375d6d..535278ef1b 100644 --- a/src/tests/unit/condition/escape.txt +++ b/src/tests/unit/condition/escape.txt @@ -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 diff --git a/src/tests/unit/condition/ip.txt b/src/tests/unit/condition/ip.txt index 66a8a439e3..4d664ba4cb 100644 --- a/src/tests/unit/condition/ip.txt +++ b/src/tests/unit/condition/ip.txt @@ -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 == * +match &Packet-Src-IP-Address == 0.0.0.0 condition &Packet-Src-IP-Address == 0.0.0.0 match &Packet-Src-IP-Address == 0.0.0.0 diff --git a/src/tests/unit/condition/regex.txt b/src/tests/unit/condition/regex.txt index 5062fc9c53..aadc5ed0b1 100644 --- a/src/tests/unit/condition/regex.txt +++ b/src/tests/unit/condition/regex.txt @@ -69,10 +69,10 @@ match &Tmp-Integer-0 =~ /%{Tmp-Integer-0}/ # Cannot add a bad cast # condition &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 =~ /foo/ -match ERROR offset 26: Invalid cast used with regular expression +match ERROR offset 28: Casts cannot be used with regular expressions xlat %{1}