radlock.mk \
radsniff.mk \
radsnmp.mk \
+ radsizes.mk \
radwho.mk \
radtest.mk \
radzap.mk \
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);
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());
.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);
* 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:
return -1;
}
- if (tmpl_cast_set(vpt, cast) < 0) {
- cf_log_perr(cp, "Failed setting tmpl type");
- return -1;
- }
-
/*
* Non-blocking xlat's
*/
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) {
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;
}
*/
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;
}
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("!="),
L(">"),
L(">="),
L("||"), /* Logical operator */
+ L("") /* Hack for EOF */
);
fr_sbuff_t our_in = FR_SBUFF(in);
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);
#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);
*/
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;
}
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;
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;
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));
}
/*
* 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".
*/
*/
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;
}
}
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;
}
}
* 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());
}
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);
}
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",
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;
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.
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.
#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
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
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);
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,
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 */
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:");
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:
* @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;
}
* @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;
}
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;
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;
/*
* 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");
/*
* 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
*
* 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
!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);
(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;
/*
* 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);
.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;
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;
}
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 */
* 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);
* @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
*/
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;
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 */
* 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
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;
/*
* 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
// 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
*/
* 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);
/*
* 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;
* 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);
}
/*
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;
}
*/
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;
}
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;
}
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);
* 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;
}
*/
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;
}
* 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);
close = ')';
} else {
- if (out) *out = FR_TYPE_NULL;
+ if (rules) rules->cast = FR_TYPE_NULL;
return 0;
}
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);
}
}
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);
}
}
done:
- vpt->rules.data.cast = dst_type;
+ vpt->rules.cast = dst_type;
return 0;
}
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:
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);
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:
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;
* 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);
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))) {
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>
*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);
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;
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;
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;
*
* (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));
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);
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;
}
/*
* 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);
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
* 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;
}
* @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.
* - <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;
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;
}
*/
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);
}
///< 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
/*
* 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;
* 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;
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");
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);
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));
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;
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;
}
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;
.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
.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) {
.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>"));
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());
};
- 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) {
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);
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;
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
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
# 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
# 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
# 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'
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.
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
# 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
# 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
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
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...
#
# 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"
match (&User-Name == "bob") && ((&User-Password == "bob") || &EAP-Message)
count
-match 319
+match 309
#
-# Tests for parsing conditional expressions.
+# Tests for casts
#
# $Id$
#
# 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'
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
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
# 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}