&our_name, p_rules, at_rules, 0);
if (ret < 0) goto error;
- /*
- * Check to see if the user wants the leaf
- * attribute to be raw.
- *
- * We can only do the conversion now _if_
- * the complete hierarchy has been resolved
- * otherwise we'll need to do the conversion
- * later.
- */
- if (tmpl_is_attr(vpt) && is_raw) {
- ret = attr_to_raw(vpt, tmpl_attr_list_tail(tmpl_attr(vpt)));
- if (ret < 0) goto error;
- }
-
- /*
- * Local variables cannot be given a list modifier.
- */
- if (tmpl_is_attr(vpt) && tmpl_attr_tail_da(vpt)) {
- tmpl_attr_t *ar = tmpl_attr_list_head(tmpl_attr(vpt));
- bool is_local = ar->ar_da->flags.local;
-
- for (; ar != NULL;
- ar = tmpl_attr_list_next(tmpl_attr(vpt), ar)) {
- if (!ar->ar_da->flags.local ||
- (ar->ar_da->flags.local && is_local)) continue;
-
- fr_strerror_printf("Local attributes cannot be used in any list");
- if (err) *err = TMPL_ATTR_ERROR_FOREIGN_NOT_ALLOWED;
- fr_sbuff_set(&our_name, &m_l);
- goto error;
- }
-
- /*
- * That being said, local variables are named "foo", but are always put into the local list.
- */
- if (is_local && (at_rules->list_presence != TMPL_ATTR_LIST_FORBID)) {
- MEM(ar = talloc(vpt, tmpl_attr_t));
- *ar = (tmpl_attr_t){
- .ar_type = TMPL_ATTR_TYPE_NORMAL,
- .ar_da = request_attr_local,
- .ar_parent = fr_dict_root(fr_dict_internal())
- };
-
- /*
- * Prepend the local list ref so it gets evaluated
- * first.
- */
- tmpl_attr_list_insert_head(tmpl_attr(vpt), ar);
- }
+ 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;
+ goto error;
}
/*
break;
}
+ tmpl_set_name(vpt, T_BARE_WORD, fr_sbuff_start(&our_name), fr_sbuff_used(&our_name));
+ vpt->rules = *t_rules; /* Record the rules */
+
+ /*
+ * Check to see if the user wants the leaf
+ * attribute to be raw.
+ *
+ * We can only do the conversion now _if_
+ * the complete hierarchy has been resolved
+ * otherwise we'll need to do the conversion
+ * later.
+ */
+ if (tmpl_is_attr(vpt)) {
+ if (is_raw) {
+ ret = attr_to_raw(vpt, tmpl_attr_list_tail(tmpl_attr(vpt)));
+ if (ret < 0) goto error;
+ }
+
+ /*
+ * Local variables cannot be given a list modifier.
+ */
+ if (tmpl_attr_tail_da(vpt)) {
+ tmpl_attr_t *ar = tmpl_attr_list_head(tmpl_attr(vpt));
+ bool is_local = ar->ar_da->flags.local;
+ bool allow_local = is_local;
+
+ if (is_local && is_raw) {
+ fr_strerror_printf("Local attributes cannot be 'raw'");
+ if (err) *err = TMPL_ATTR_ERROR_UNKNOWN_NOT_ALLOWED;
+ fr_sbuff_set(&our_name, &m_l);
+ goto error;
+ }
+
+ /*
+ * We can transition from local to non-local, but not the other way around.
+ */
+ for (;
+ ar != NULL;
+ ar = tmpl_attr_list_next(tmpl_attr(vpt), ar)) {
+ if (ar->ar_da->flags.local == allow_local) continue;
+
+ if (!ar->ar_da->flags.local && allow_local) {
+ allow_local = false;
+ continue;
+ }
+
+ if (ar->ar_da->flags.local) {
+ fr_strerror_printf("Local attributes cannot be used in any list");
+ if (err) *err = TMPL_ATTR_ERROR_FOREIGN_NOT_ALLOWED;
+ fr_sbuff_set(&our_name, &m_l);
+ goto error;
+ }
+ }
+
+ /*
+ * Local variables are named "foo", but are always put into the local list.
+ *
+ * We add the list after checking for non-local -> local transition, as
+ * request_attr_local isn't a local attribute.
+ *
+ * When the list is forbidden, we're creating a local attribute inside of a local
+ * TLV.
+ */
+ if (is_local && (at_rules->list_presence != TMPL_ATTR_LIST_FORBID)) {
+ MEM(ar = talloc(vpt, tmpl_attr_t));
+ *ar = (tmpl_attr_t){
+ .ar_type = TMPL_ATTR_TYPE_NORMAL,
+ .ar_da = request_attr_local,
+ .ar_parent = fr_dict_root(fr_dict_internal())
+ };
+
+ /*
+ * Prepend the local list ref so it gets evaluated
+ * first.
+ */
+ tmpl_attr_list_insert_head(tmpl_attr(vpt), ar);
+ }
+ }
+ }
+
/*
- * If we're using lists, ensure that the default list is specified.
+ * Ensure that the default list is specified.
*/
if (!tmpl_attr_is_list_attr(tmpl_attr_list_head(tmpl_attr(vpt)))) {
tmpl_attr_t *ar;
tmpl_attr_list_insert_head(tmpl_attr(vpt), ar);
}
- tmpl_set_name(vpt, T_BARE_WORD, fr_sbuff_start(&our_name), fr_sbuff_used(&our_name));
- vpt->rules = *t_rules; /* Record the rules */
-
/*
- * If there are actual requests, duplicate them
- * and move them into the list.
+ * If there is a default request (parent, outer, etc.), duplicate them and move them into the
+ * list.
*
- * A NULL request_def pointer is equivalent to the
- * current request.
+ * A NULL request_def pointer is equivalent to the current request.
*/
if (t_rules->attr.request_def) {
tmpl_request_ref_list_acopy(vpt, &vpt->rules.attr.request_def, t_rules->attr.request_def);
}
+ /*
+ * Now that all of the lists are set correctly, do some final validation and updates on the
+ * attribute.
+ */
if (tmpl_is_attr(vpt)) {
tmpl_attr_t *ar;
- /*
- * Suppress useless casts.
- */
- if (tmpl_attr_tail_is_normal(vpt) && (tmpl_attr_tail_da(vpt)->type == tmpl_rules_cast(vpt))) {
- vpt->rules.cast = FR_TYPE_NULL;
- }
-
/*
* Ensure that the list is set correctly, so that
* the returned vpt just doesn't just match the
fr_assert(ar != NULL);
if (tmpl_attr_is_list_attr(ar)) vpt->rules.attr.list_def = ar->ar_da;
- }
- 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;
- goto error;
- }
+ if (tmpl_attr_tail_is_normal(vpt)) {
+ /*
+ * Suppress useless casts.
+ */
+ if (tmpl_attr_tail_da(vpt)->type == tmpl_rules_cast(vpt)) {
+ vpt->rules.cast = FR_TYPE_NULL;
+ }
- /*
- * 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_attr_tail_da(vpt)->type)) {
- fr_strerror_printf("Cannot cast type '%s' to '%s'",
- fr_type_to_str(tmpl_attr_tail_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;
+ /*
+ * Check if the cast is allowed. This lets us give better errors at compile time.
+ */
+ if ((tmpl_rules_cast(vpt)!= FR_TYPE_NULL) &&
+ !fr_type_cast(tmpl_rules_cast(vpt), tmpl_attr_tail_da(vpt)->type)) {
+ fr_strerror_printf("Cannot cast type '%s' to '%s'",
+ fr_type_to_str(tmpl_attr_tail_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 */