#ifdef __clangd__
# undef HAVE_SANITIZER_LSAN_INTERFACE_H
#endif
+
#ifdef HAVE_SANITIZER_LSAN_INTERFACE_H
# include <sanitizer/asan_interface.h>
#endif
fr_strerror_printf("Invalid list specifier \"%pV\"",
fr_box_strvalue_len(fr_sbuff_current(value), fr_sbuff_remaining(value)));
}
-
+
return slen;
}
.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,
- .list_as_attr = true,
}
});
if (dec_len <= 0) {
.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,
- .list_as_attr = true,
},
});
if (dec_len <= 0) {
EXIT_WITH_FAILURE;
}
+ request_global_init();
+
if (log_global_init(&default_log, false) < 0) {
EXIT_WITH_FAILURE;
}
if (map_proc_register(NULL, "test-fail", mod_map_proc, map_proc_verify, 0) < 0) {
EXIT_WITH_FAILURE;
}
-
+
/*
* Initialise the interpreter, registering operations.
* This initialises
*/
log_global_free();
+ request_global_free();
+
server_free();
/*
fr_pair_list_init(&tmp_list);
switch (map->lhs->type) {
- /*
- * This is a mapping in the form of:
- * <list>. += <ldap attr>
- *
- * Where <ldap attr> is:
- * <list>.<attr> <op> <value>
- *
- * It is to allow for legacy installations which stored
- * RADIUS control and reply attributes in separate LDAP
- * attributes.
- */
- case TMPL_TYPE_LIST:
- for (i = 0; i < self->count; i++) {
- map_t *attr = NULL;
- char *attr_str;
-
- tmpl_rules_t lhs_rules = {
- .attr = {
- .dict_def = request->dict,
- .request_def = tmpl_request(map->lhs),
- .list_def = tmpl_list(map->lhs),
- .prefix = TMPL_ATTR_REF_PREFIX_AUTO
- },
- .at_runtime = true,
- };
-
- tmpl_rules_t rhs_rules = {
- .attr = {
- .dict_def = request->dict
- },
- .at_runtime = true,
- };
-
- RDEBUG3("Parsing valuepair string \"%pV\"",
- fr_box_strvalue_len(self->values[i]->bv_val, self->values[i]->bv_len));
-
- /*
- * bv_val is NOT \0 terminated, so we need to make it
- * safe (\0 terminate it) before passing it to any
- * functions which take C strings and no lengths.
- */
- attr_str = talloc_bstrndup(NULL, self->values[i]->bv_val, self->values[i]->bv_len);
- if (!attr_str) {
- RWDEBUG("Failed making attribute string safe");
- continue;
- }
-
- if (map_afrom_attr_str(ctx, &attr,
- attr_str,
- &lhs_rules, &rhs_rules) < 0) {
- RPWDEBUG("Failed parsing \"%pV\" as valuepair, skipping...",
- fr_box_strvalue_len(self->values[i]->bv_val, self->values[i]->bv_len));
- talloc_free(attr_str);
- continue;
- }
-
- talloc_free(attr_str);
-
- if (tmpl_is_unresolved(attr->lhs)) {
- RWDEBUG("Failed parsing left side of \"%pV\", skipping...",
- fr_box_strvalue_len(self->values[i]->bv_val, self->values[i]->bv_len));
- talloc_free(attr);
- continue;
- }
-
- if (tmpl_request_ref_list_cmp(tmpl_request(attr->lhs), tmpl_request(map->lhs)) != 0) {
- char *attr_request;
- char *map_request;
-
- tmpl_request_ref_list_aprint(NULL, &attr_request, tmpl_request(attr->lhs));
- tmpl_request_ref_list_aprint(NULL, &map_request, tmpl_request(map->lhs));
-
- RWDEBUG("valuepair \"%pV\" has conflicting request qualifier (%s vs %s), skipping...",
- fr_box_strvalue_len(self->values[i]->bv_val, self->values[i]->bv_len),
- attr_request, map_request);
-
- talloc_free(attr_request);
- talloc_free(map_request);
-
- next_pair:
- talloc_free(attr);
- continue;
- }
-
- if ((tmpl_list(attr->lhs) != tmpl_list(map->lhs))) {
- RWDEBUG("valuepair \"%pV\" has conflicting list qualifier (%s vs %s), skipping...",
- fr_box_strvalue_len(self->values[i]->bv_val, self->values[i]->bv_len),
- fr_table_str_by_value(pair_list_table, tmpl_list(attr->lhs), "<INVALID>"),
- fr_table_str_by_value(pair_list_table, tmpl_list(map->lhs), "<INVALID>"));
- goto next_pair;
- }
-
- if (map_to_vp(ctx, &tmp_list, request, attr, NULL) < 0) {
- RWDEBUG("Failed creating attribute for valuepair \"%pV\", skipping...",
- fr_box_strvalue_len(self->values[i]->bv_val, self->values[i]->bv_len));
- goto next_pair;
- }
-
- fr_pair_list_append(&head, &tmp_list);
- talloc_free(attr);
-
- /*
- * Only process the first value, unless the operator is +=
- */
- if (map->op != T_OP_ADD_EQ) break;
- }
- break;
-
/*
* Iterate over all the retrieved values,
* don't try and be clever about changing operators
* create using LDAP values.
*/
switch (map->lhs->type) {
- case TMPL_TYPE_LIST:
case TMPL_TYPE_ATTR:
break;
break;
case TMPL_TYPE_ATTR:
- case TMPL_TYPE_LIST:
case TMPL_TYPE_DATA:
case TMPL_TYPE_EXEC:
case TMPL_TYPE_EXEC_UNRESOLVED:
switch (vpt->type) {
case TMPL_TYPE_ATTR:
- case TMPL_TYPE_LIST:
/*
* No cast means that it's an existence check.
*/
/*
* These are handled elsewhere.
*/
- case TMPL_TYPE_LIST:
#ifdef HAVE_REGEX
case TMPL_TYPE_REGEX:
#endif
/*
* LHS is an attribute or list
*/
- case TMPL_TYPE_LIST:
case TMPL_TYPE_ATTR:
{
fr_pair_t *vp;
*/
if (tmpl_contains_regex(c->data.map->rhs)) {
fr_assert((c->data.map->op == T_OP_REG_EQ) || (c->data.map->op == T_OP_REG_NE));
- fr_assert(!tmpl_is_list(c->data.map->lhs));
fr_assert(fr_type_is_null(tmpl_rules_cast(c->data.map->rhs)));
/*
case TMPL_TYPE_XLAT_UNRESOLVED:
case TMPL_TYPE_ATTR:
case TMPL_TYPE_ATTR_UNRESOLVED:
- case TMPL_TYPE_LIST:
case TMPL_TYPE_EXEC:
break;
static CC_HINT(nonnull) int cond_forbid_groups(tmpl_t *vpt, fr_sbuff_t *in, fr_sbuff_marker_t *m_lhs)
{
- if (tmpl_is_list(vpt)) {
- fr_strerror_const("Cannot use list references in condition");
- fr_sbuff_set(in, m_lhs);
- return -1;
- }
-
- if (!tmpl_is_attr(vpt)) return 0;
+ if (!tmpl_is_attr(vpt) || tmpl_attr_tail_is_unspecified(vpt)) return 0;
switch (tmpl_attr_tail_da(vpt)->type) {
case FR_TYPE_LEAF:
break;
default:
- fr_strerror_printf("Nesting types such as groups or TLVs cannot "
- "be used in condition comparisons");
+ fr_strerror_printf("Nesting types such as groups or TLVs cannot be compared");
fr_sbuff_set(in, m_lhs);
return -1;
}
*/
if (!rhs_rules) {
rhs_rules = lhs_rules;
- fr_assert(lhs_rules->attr.list_as_attr); /* so we don't worry about list_def? */
}
MEM(child_ctx = talloc(map, uint8_t));
* parsed in the context of the LHS, but only if
* the LHS attribute was a group / structural attribute.
*/
- if (!input_rhs_rules && lhs_rules->attr.list_as_attr) {
+ if (!input_rhs_rules) {
tmpl_rules_child_init(child_ctx, &my_rhs_rules, lhs_rules, map->lhs);
rhs_rules = &my_rhs_rules;
}
* @param[in,out] ctx to allocate new #fr_pair_t (s) in.
* @param[out] out Where to write the #fr_pair_t (s).
* @param[in] request structure (used only for talloc).
- * @param[in] map the map. The LHS (dst) must be #TMPL_TYPE_ATTR or #TMPL_TYPE_LIST.
+ * @param[in] map the map. The LHS (dst) must be #TMPL_TYPE_ATTR
* The RHS (src) must be #TMPL_TYPE_EXEC.
* @return
* - 0 on success.
fr_assert(map->rhs); /* Quite clang scan */
fr_assert(tmpl_is_exec(map->rhs));
- fr_assert(tmpl_is_attr(map->lhs) || tmpl_is_list(map->lhs));
+ fr_assert(tmpl_is_attr(map->lhs));
/*
* We always put the request pairs into the environment
* call fr_pair_value_from_str on the output of the script.
*/
result = radius_exec_program_legacy(ctx, answer, sizeof(answer),
- tmpl_is_list(map->lhs) ? &output_pairs : NULL,
- request, map->rhs->name, input_pairs ? input_pairs : NULL,
- true, true, fr_time_delta_from_sec(EXEC_TIMEOUT));
+ tmpl_attr_tail_da_is_structural(map->lhs) ? &output_pairs : NULL,
+ request, map->rhs->name, input_pairs ? input_pairs : NULL,
+ true, true, fr_time_delta_from_sec(EXEC_TIMEOUT));
talloc_free(expanded);
if (result != 0) {
REDEBUG("Exec failed with code (%i)", result);
}
switch (map->lhs->type) {
- case TMPL_TYPE_LIST:
- if (fr_pair_list_empty(&output_pairs)) {
- REDEBUG("No valid attributes received from program");
- return -2;
- }
- fr_pair_list_append(out, &output_pairs);
- return 0;
-
case TMPL_TYPE_ATTR:
{
fr_pair_t *vp;
* @param[in,out] ctx to allocate #fr_pair_t (s) in.
* @param[out] out Where to write the #fr_pair_t (s), which may be NULL if not found
* @param[in] request The current request.
- * @param[in] map the map. The LHS (dst) has to be #TMPL_TYPE_ATTR or #TMPL_TYPE_LIST.
+ * @param[in] map the map. The LHS (dst) has to be #TMPL_TYPE_ATTR.
* @param[in] uctx unused.
* @return
* - 0 on success.
MAP_VERIFY(map);
if (!fr_cond_assert(map->lhs != NULL)) return -1;
- fr_assert(tmpl_is_list(map->lhs) || tmpl_is_attr(map->lhs));
+ fr_assert(tmpl_is_attr(map->lhs));
/*
* Special case for !*, we don't need to parse RHS as this is a unary operator.
* to allocate any attributes, just finding the current list, and change
* the op.
*/
- if (tmpl_is_list(map->lhs) && tmpl_is_list(map->rhs)) {
+ if (tmpl_attr_tail_da_is_structural(map->lhs) && tmpl_attr_tail_da_is_structural(map->rhs)) {
fr_pair_list_t *from = NULL;
if (tmpl_request_ptr(&context, tmpl_request(map->rhs)) == 0) {
fr_pair_t *vp;
fr_dcursor_t from;
- fr_assert((tmpl_is_attr(map->lhs) && tmpl_attr_tail_da(map->lhs)) ||
- (tmpl_is_list(map->lhs) && !tmpl_attr_tail_da(map->lhs)));
+ fr_assert(tmpl_is_attr(map->lhs) && tmpl_attr_tail_da(map->lhs));
/*
* @todo should log error, and return -1 for v3.1 (causes update to fail)
/*
* Already in the correct form.
*/
- case TMPL_TYPE_LIST:
case TMPL_TYPE_ATTR:
break;
rcode = -1;
goto finish;
}
- fr_assert(tmpl_is_attr(exp_lhs) || tmpl_is_list(exp_lhs));
+ fr_assert(tmpl_is_attr(exp_lhs));
memcpy(&exp_map, map, sizeof(exp_map));
exp_map.lhs = exp_lhs;
* Sanity check inputs. We can have a list or attribute
* as a destination.
*/
- if (!tmpl_is_list(map->lhs) &&
- !tmpl_is_attr(map->lhs)) {
+ if (!tmpl_is_attr(map->lhs)) {
REDEBUG("Left side \"%.*s\" of map should be an attr or list but is an %s",
(int)map->lhs->len, map->lhs->name,
tmpl_type_to_str(map->lhs->type));
/*
* The destination is a list (which is a completely different set of operations)
*/
- if (tmpl_is_list(map->lhs)) {
+ if (tmpl_attr_tail_da_is_structural(map->lhs)) {
switch (map->op) {
case T_OP_CMP_FALSE:
/* We don't need the src VPs (should just be 'ANY') */
goto finish;
case T_OP_SET:
- if (tmpl_is_list(map->rhs)) {
+ if (tmpl_attr_tail_da_is_structural(map->rhs)) {
fr_pair_list_free(list);
fr_pair_list_append(list, &src_list);
fr_pair_list_init(&src_list);
fr_pair_aprint_value_quoted(request, &rhs, vp, map->rhs->quote);
break;
- /*
- * For the lists, we can't use the original name, and have to
- * rebuild it using tmpl_print, for each attribute we're
- * copying.
- */
- case TMPL_TYPE_LIST:
- {
- tmpl_t *vpt;
- fr_token_t quote;
-
- switch (vp->vp_type) {
- case FR_TYPE_QUOTED:
- quote = T_DOUBLE_QUOTED_STRING;
- break;
- default:
- quote = T_BARE_WORD;
- break;
- }
-
- vpt = tmpl_alloc(request, TMPL_TYPE_ATTR, quote, map->rhs->name, strlen(map->rhs->name));
-
- /*
- * Fudge a temporary tmpl that describes the attribute we're copying
- * this is a combination of the original list tmpl, and values from
- * the fr_pair_t.
- */
- tmpl_attr_copy(vpt, map->rhs);
- tmpl_attr_set_leaf_da(vpt, vp->da);
- tmpl_attr_set_leaf_num(vpt, NUM_UNSPEC);
-
- /*
- * Not appropriate to use map->rhs->quote here, as that's the quoting
- * around the list ref. The attribute value has no quoting, so we choose
- * the quoting based on the data type.
- */
- fr_pair_aprint_value_quoted(request, &value, vp, quote);
- tmpl_print(&FR_SBUFF_OUT(buffer, sizeof(buffer)), vpt, TMPL_ATTR_REF_PREFIX_YES, NULL);
- rhs = talloc_typed_asprintf(request, "%s -> %s", buffer, value);
-
- talloc_free(vpt);
- }
- break;
-
case TMPL_TYPE_ATTR:
{
fr_token_t quote;
}
switch (map->lhs->type) {
- case TMPL_TYPE_LIST:
- /*
- * The MAP may have said "list", but if there's a
- * VP, it has it's own name, which isn't in the
- * map name.
- */
- if (vp) {
- tmpl_print(&FR_SBUFF_OUT(buffer, sizeof(buffer)), map->lhs, TMPL_ATTR_REF_PREFIX_YES, NULL); /* Fixme - bad escaping */
- RDEBUG2("%s%s %s %s", buffer, vp->da->name, fr_table_str_by_value(fr_tokens_table, vp->op, "<INVALID>"), rhs);
- break;
- }
- FALL_THROUGH;
-
case TMPL_TYPE_ATTR:
tmpl_print(&FR_SBUFF_OUT(buffer, sizeof(buffer)), map->lhs, TMPL_ATTR_REF_PREFIX_YES, NULL);
RDEBUG2("%s %s %s", buffer, fr_table_str_by_value(fr_tokens_table, vp ? vp->op : map->op, "<INVALID>"), rhs);
* @param[in,out] ctx to allocate modification maps in.
* @param[out] out Where to write the #fr_pair_t (s), which may be NULL if not found
* @param[in] request The current request.
- * @param[in] original the map. The LHS (dst) has to be #TMPL_TYPE_ATTR or #TMPL_TYPE_LIST.
+ * @param[in] original the map. The LHS (dst) has to be #TMPL_TYPE_ATTR.
* @param[in] lhs_result of previous stack based rhs evaluation.
* Must be provided for rhs types:
* - TMPL_TYPE_XLAT
if (!fr_cond_assert(original->lhs != NULL)) return -1;
if (!fr_cond_assert(original->rhs != NULL)) return -1;
- fr_assert(tmpl_is_list(original->lhs) ||
- tmpl_is_attr(original->lhs) ||
- tmpl_is_xlat(original->lhs));
+ fr_assert(tmpl_is_attr(original->lhs) || tmpl_is_xlat(original->lhs));
*out = NULL;
fr_value_box_list_init(&head);
/*
* Already in the correct form.
*/
- case TMPL_TYPE_LIST:
case TMPL_TYPE_ATTR:
break;
fr_value_box_list_talloc_free(lhs_result);
goto error;
}
- fr_assert(tmpl_is_attr(mutated->lhs) || tmpl_is_list(mutated->lhs));
+ fr_assert(tmpl_is_attr(mutated->lhs));
}
break;
/*
* List to list copy.
*/
- if (tmpl_is_list(mutated->lhs) && tmpl_is_list(mutated->rhs)) {
+ if (tmpl_attr_tail_da_is_structural(mutated->lhs) && tmpl_attr_tail_da_is_structural(mutated->rhs)) {
fr_pair_list_t *list = NULL;
fr_pair_t *vp = NULL;
int err;
fr_assert(fr_value_box_list_empty(rhs_result));
- fr_assert((tmpl_is_attr(mutated->lhs) && tmpl_attr_tail_da(mutated->lhs)) ||
- (tmpl_is_list(mutated->lhs) && !tmpl_attr_tail_da(mutated->lhs)));
/*
* Check source list
*/
if (tmpl_is_attr(mutated->lhs)) goto assign_values;
- fr_assert(tmpl_is_list(mutated->lhs));
-
/*
* Empty value - Try and cast an empty string
* to the destination type, and see what
rhs = fr_asprintf(request, "%s%pV%s", quote, vb, quote);
break;
- /*
- * For the lists, we can't use the original name, and have to
- * rebuild it using tmpl_print, for each attribute we're
- * copying.
- */
- case TMPL_TYPE_LIST:
- {
- char buffer[256];
-
- tmpl_print(&FR_SBUFF_OUT(buffer, sizeof(buffer)), map->rhs, TMPL_ATTR_REF_PREFIX_YES, NULL);
- rhs = fr_asprintf(request, "%s -> %s%pV%s", buffer, quote, vb, quote);
- }
- break;
-
case TMPL_TYPE_ATTR:
rhs = fr_asprintf(request, "%s -> %s%pV%s", map->rhs->name, quote, vb, quote);
break;
switch (map->lhs->type) {
case TMPL_TYPE_ATTR:
- case TMPL_TYPE_LIST:
RDEBUG2("%s %s %s", map->lhs->name, fr_table_str_by_value(fr_tokens_table, mod->op, "<INVALID>"), rhs);
break;
fr_assert(mod->lhs != NULL);
fr_assert(mod->rhs != NULL);
- fr_assert(tmpl_is_attr(mod->lhs) || tmpl_is_list(mod->lhs));
+ fr_assert(tmpl_is_attr(mod->lhs));
fr_assert(((mod->op == T_OP_CMP_FALSE) && tmpl_is_null(mod->rhs)) ||
tmpl_is_data(mod->rhs));
/*
* The destination is a list (which is a completely different set of operations)
*/
- if (tmpl_is_list(map->lhs)) {
+ if (tmpl_attr_tail_da_is_structural(map->lhs)) {
switch (mod->op) {
case T_OP_CMP_FALSE:
fr_pair_list_free(vp_list); /* Clear the entire list */
* #tmpl_t (VPTs) specify either a data source, or a data sink.
*
* Examples of sources are #TMPL_TYPE_XLAT_UNRESOLVED, #TMPL_TYPE_EXEC and #TMPL_TYPE_ATTR.
- * Examples of sinks are #TMPL_TYPE_ATTR, #TMPL_TYPE_LIST.
+ * Examples of sinks are #TMPL_TYPE_ATTR.
*
* VPTs are used to gather values or attributes for evaluation, or copying, and to specify
* where values or #fr_pair_t should be copied to.
* @see tmpl_afrom_substr
* @see tmpl_afrom_attr_str
*
- * In the case of #TMPL_TYPE_ATTR and #TMPL_TYPE_LIST, there are special cursor overlay
+ * In the case of #TMPL_TYPE_ATTR there are special cursor overlay
* functions which can be used to iterate over only the #fr_pair_t that match a
* tmpl_t in a given list.
*
*
* @copyright 2014-2015 The FreeRADIUS server project
*/
+#include "lib/server/tmpl.h"
RCSIDH(tmpl_h, "$Id$")
#ifdef __cplusplus
*/
TMPL_TYPE_DATA = 0x0002,
- /** Reference to an attribute list
- */
- TMPL_TYPE_LIST = 0x0004 | TMPL_FLAG_ATTR,
-
/** Reference to one or more attributes
*/
TMPL_TYPE_ATTR = 0x0008 | TMPL_FLAG_ATTR,
#define tmpl_is_data(vpt) (vpt->type == TMPL_TYPE_DATA)
#define tmpl_is_attr(vpt) (vpt->type == TMPL_TYPE_ATTR)
-#define tmpl_is_list(vpt) (vpt->type == TMPL_TYPE_LIST)
#define tmpl_is_xlat(vpt) (vpt->type == TMPL_TYPE_XLAT)
#define tmpl_is_exec(vpt) (vpt->type == TMPL_TYPE_EXEC)
#include <freeradius-devel/util/packet.h>
#include <freeradius-devel/util/proto.h>
#include <freeradius-devel/util/regex.h>
-
+#include <freeradius-devel/server/request.h>
/*
* Allow public and private versions of the same structures
*/
uint8_t disallow_qualifiers:1; //!< disallow request / list qualifiers
uint8_t disallow_filters:1; //!< disallow filters.
-
- uint8_t list_as_attr:1; //!< return #TMPL_TYPE_ATTR for lists, and not #TMPL_TYPE_LIST
};
struct tmpl_xlat_rules_s {
#define ar_is_unknown(_ar) ((_ar)->ar_type == TMPL_ATTR_TYPE_UNKNOWN)
#define ar_is_unresolved(_ar) ((_ar)->ar_type == TMPL_ATTR_TYPE_UNRESOLVED)
+/** Indicate whether an attribute reference is raw
+ *
+ * Determining whether an attribute reference is raw, is slightly more complex
+ * given the raw flag is either coming from the attribute or an internal
+ * "is_raw" flag in the unresolved entry.
+ *
+ * @param[in] ar to check for rawness.
+ * @return
+ * - true if the attribute reference is raw.
+ * - false if the attribute reference is not raw.
+ */
+static inline bool ar_is_raw(tmpl_attr_t const *ar)
+{
+ switch (ar->ar_type) {
+ case TMPL_ATTR_TYPE_NORMAL:
+ case TMPL_ATTR_TYPE_UNKNOWN:
+ return ar->ar_da->flags.is_raw;
+
+ case TMPL_ATTR_TYPE_UNRESOLVED:
+ return ar->ar_unresolved_raw;
+
+ case TMPL_ATTR_TYPE_UNSPEC:
+ return false;
+ }
+
+ fr_assert_fail("ar type (%i) is invalid", (int)ar->ar_type);
+}
+
+bool ar_is_list_attr(tmpl_attr_t const *ar);
+
#define ar_num filter.num
#define ar_filter_type filter.type
* @section update_maps Use in update map_t
* When used on the LHS it describes an attribute to create and should be one of these types:
* - #TMPL_TYPE_ATTR
- * - #TMPL_TYPE_LIST
*
* When used on the RHS it describes the value to assign to the attribute being created and
* should be one of these types:
* - #TMPL_TYPE_UNRESOLVED
* - #TMPL_TYPE_XLAT_UNRESOLVED
* - #TMPL_TYPE_ATTR
- * - #TMPL_TYPE_LIST
* - #TMPL_TYPE_EXEC
* - #TMPL_TYPE_DATA
* - #TMPL_TYPE_XLAT (pre-parsed xlat)
}
/** @} */
-/** @name Field accessors for #TMPL_TYPE_ATTR, #TMPL_TYPE_ATTR_UNRESOLVED, #TMPL_TYPE_LIST
+/** @name Field accessors for #TMPL_TYPE_ATTR, #TMPL_TYPE_ATTR_UNRESOLVED
*
* @{
*/
static inline FR_DLIST_HEAD(tmpl_request_list) const *tmpl_request(tmpl_t const *vpt)
{
- tmpl_assert_type(tmpl_is_attr(vpt) ||
- tmpl_is_attr_unresolved(vpt) ||
- tmpl_is_list(vpt));
+ tmpl_assert_type(tmpl_contains_attr(vpt));
return &vpt->data.attribute.rr;
}
*/
static inline size_t tmpl_request_ref_count(tmpl_t const *vpt)
{
- tmpl_assert_type(tmpl_is_attr(vpt) ||
- tmpl_is_attr_unresolved(vpt) ||
- tmpl_is_list(vpt));
+ tmpl_assert_type(tmpl_contains_attr(vpt));
return tmpl_request_list_num_elements(&vpt->data.attribute.rr);
}
+/** Return true if the last attribute reference is "normal"
+ *
+ * @hidecallergraph
+ */
+static inline bool tmpl_attr_head_is_list(tmpl_t const *vpt)
+{
+ tmpl_attr_t *ar;
+
+ tmpl_assert_type(tmpl_contains_attr(vpt));
+
+ ar = tmpl_attr_list_head(tmpl_attr(vpt));
+ if (unlikely(!ar)) return false;
+
+ return ar_is_list_attr(ar);
+}
+
/** Return true if the last attribute reference is "normal"
*
* @hidecallergraph
{
tmpl_attr_t *ar;
- tmpl_assert_type(tmpl_is_attr(vpt));
+ tmpl_assert_type(tmpl_contains_attr(vpt));
ar = tmpl_attr_list_tail(tmpl_attr(vpt));
if (unlikely(!ar)) return false;
{
tmpl_attr_t *ar;
- tmpl_assert_type(tmpl_is_attr(vpt));
+ tmpl_assert_type(tmpl_contains_attr(vpt));
ar = tmpl_attr_list_tail(tmpl_attr(vpt));
if (unlikely(!ar)) return false;
{
tmpl_attr_t *ar;
- tmpl_assert_type(tmpl_is_attr(vpt));
+ tmpl_assert_type(tmpl_contains_attr(vpt));
ar = tmpl_attr_list_tail(tmpl_attr(vpt));
if (unlikely(!ar)) return false;
{
tmpl_attr_t *ar;
- tmpl_assert_type(tmpl_is_attr_unresolved(vpt));
+ tmpl_assert_type(tmpl_contains_attr(vpt));
ar = tmpl_attr_list_tail(tmpl_attr(vpt));
if (unlikely(!ar)) return false;
return ar_is_normal(ar);
}
+/** Return true if the last attribute reference is "raw"
+ *
+ * @hidecallergraph
+ */
+static inline bool tmpl_attr_tail_is_raw(tmpl_t const *vpt)
+{
+ tmpl_attr_t *ar;
+
+ tmpl_assert_type(tmpl_contains_attr(vpt));
+
+ ar = tmpl_attr_list_tail(tmpl_attr(vpt));
+ if (unlikely(!ar)) return false;
+
+ return ar_is_raw(ar);
+}
+
+
/** Return the last attribute reference
*
* @hidecallergraph
*/
static inline tmpl_attr_t const *tmpl_attr_tail(tmpl_t const *vpt)
{
- tmpl_assert_type(tmpl_is_attr(vpt));
+ tmpl_assert_type(tmpl_contains_attr(vpt));
return tmpl_attr_list_tail(tmpl_attr(vpt));
}
{
tmpl_attr_t *ar;
- tmpl_assert_type(tmpl_is_attr(vpt));
+ tmpl_assert_type(tmpl_contains_attr(vpt));
ar = tmpl_attr_list_tail(tmpl_attr(vpt));
if (!ar) return NULL;
return ar->ar_da;
}
+/** Return true if the the last attribute reference is a leaf attribute
+ *
+ * @hidecallergraph
+ */
+static inline bool tmpl_attr_tail_da_is_leaf(tmpl_t const *vpt)
+{
+ tmpl_attr_t *ar;
+
+ tmpl_assert_type(tmpl_contains_attr(vpt));
+
+ ar = tmpl_attr_list_tail(tmpl_attr(vpt));
+ if (!ar) return false;
+
+ fr_assert(ar_is_normal(ar) || ar_is_unknown(ar));
+
+ return fr_type_is_leaf(ar->ar_da->type);
+}
+
+/** Return true if the the last attribute reference is a structural attribute
+ *
+ * @hidecallergraph
+ */
+static inline bool tmpl_attr_tail_da_is_structural(tmpl_t const *vpt)
+{
+ tmpl_attr_t *ar;
+
+ tmpl_assert_type(tmpl_contains_attr(vpt));
+
+ ar = tmpl_attr_list_tail(tmpl_attr(vpt));
+ if (!ar) return false;
+
+ fr_assert(ar_is_normal(ar) || ar_is_unknown(ar));
+
+ return fr_type_is_structural(ar->ar_da->type);
+}
+
/** Return the last attribute reference unknown da
*
* @hidecallergraph
{
tmpl_attr_t *ar;
- tmpl_assert_type(tmpl_is_attr(vpt));
+ tmpl_assert_type(tmpl_contains_attr(vpt));
ar = tmpl_attr_list_tail(tmpl_attr(vpt));
if (!ar) return NULL;
*/
static inline int16_t tmpl_attr_tail_num(tmpl_t const *vpt)
{
- tmpl_assert_type(tmpl_is_attr(vpt) ||
- tmpl_is_attr_unresolved(vpt) ||
- tmpl_is_list(vpt));
-
- if (tmpl_is_list(vpt) && (tmpl_attr_list_num_elements(tmpl_attr(vpt)) == 0)) return NUM_ALL;
+ tmpl_assert_type(tmpl_is_attr(vpt) || tmpl_is_attr_unresolved(vpt));
return tmpl_attr_list_tail(tmpl_attr(vpt))->ar_num;
}
static inline tmpl_pair_list_t tmpl_list(tmpl_t const *vpt)
{
tmpl_assert_type(tmpl_is_attr(vpt) ||
- tmpl_is_attr_unresolved(vpt) || /* Remove once list is part of ar dlist */
- tmpl_is_list(vpt));
+ tmpl_is_attr_unresolved(vpt));
return vpt->data.attribute.list;
}
void tmpl_verify(char const *file, int line, tmpl_t const *vpt);
#endif
-/** Produces an initialiser for static #TMPL_TYPE_LIST type #tmpl_t
- *
- * Example:
- @code{.c}
- static tmpl_t list = tmpl_init_initialiser_list(CURRENT_REQUEST, PAIR_LIST_REQUEST);
- fr_dcursor_t cursor;
- tmpl_dcursor_ctx_t cc,
- fr_pair_t *vp;
-
- // Iterate over all pairs in the request list
- for (vp = tmpl_dcursor_init(NULL, &cursor, request, &list);
- vp;
- vp = tmpl_cursor_next(&cursor, &list)) {
- // Do something
- }
- tmpl_dcursor_clear(&cc);
- @endcode
- *
- * @param _request to locate the list in.
- * @param _list to set as the target for the template.
- * @see tmpl_dcursor_init
- * @see tmpl_cursor_next
- */
-#define tmpl_init_initialiser_list(_request, _list)\
-{ \
- .name = "static", \
- .len = sizeof("static") - 1, \
- .type = TMPL_TYPE_LIST, \
- .quote = T_SINGLE_QUOTED_STRING, \
- .data = { \
- .attribute = { \
- .request = _request, \
- .list = _list \
- } \
- } \
-}
-
/** Determine the correct context and list head
*
* Used in conjunction with the fr_dcursor functions to determine the correct list
*/
extern FR_DLIST_HEAD(tmpl_request_list) tmpl_request_def_parent;
+/** Placeholder attribute for unspecified attribute refs
+ *
+ */
+extern fr_dict_attr_t const *tmpl_attr_unspec;
+
int tmpl_request_ptr(request_t **request, FR_DLIST_HEAD(tmpl_request_list) const *rql) CC_HINT(nonnull);
void tmpl_request_ref_list_debug(FR_DLIST_HEAD(tmpl_request_list) const *rql);
/*
* Iterates over attributes of a specific type
*/
- if (tmpl_is_list(cc->vpt) || ar_is_normal(ar)) {
+ if (ar_is_normal(ar)) {
fr_pair_dcursor_iter_init(&ns->cursor, list, _tmpl_cursor_child_next, ns);
/*
* Iterates over all attributes at this level
pop = true;
}
break;
- } else goto all_inst; /* Used for TMPL_TYPE_LIST */
+ } else goto all_inst;
/*
* If no pair was found and there is a fill
fr_pair_t *vp;
- switch (vpt->type) {
- case TMPL_TYPE_ATTR:
- {
- tmpl_attr_t const *ar = NULL;
- tmpl_dcursor_nested_t *ns = NULL;
-
- /*
- * - Continue until there are no evaluation contexts
- * - Push a evaluation context if evaluating the head of the
- * stack yields a VP and we're not at the deepest attribute
- * reference.
- * - Return if we have a VP and there are no more attribute
- * references to push, i.e. we're at the deepest attribute
- * reference.
- */
- while ((ns = fr_dlist_tail(&cc->nested))) {
- ar = ns->ar;
- vp = _tmpl_cursor_eval(curr, cc);
- if (!vp) continue;
-
- ar = tmpl_attr_list_next(&vpt->data.attribute.ar, ar);
- if (ar) {
- fr_pair_list_t *list_head;
-
- list_head = &vp->vp_group;
- _tmpl_cursor_pair_init(vp, list_head, ar, cc);
- curr = fr_pair_list_head(list_head);
- continue;
- }
-
- return vp;
- }
-
- null_result:
- return NULL;
- }
+ tmpl_attr_t const *ar = NULL;
+ tmpl_dcursor_nested_t *ns = NULL;
/*
- * Hacks for evaluating lists
- * Hopefully this tmpl type goes away soon...
+ * - Continue until there are no evaluation contexts
+ * - Push a evaluation context if evaluating the head of the
+ * stack yields a VP and we're not at the deepest attribute
+ * reference.
+ * - Return if we have a VP and there are no more attribute
+ * references to push, i.e. we're at the deepest attribute
+ * reference.
*/
- case TMPL_TYPE_LIST:
- if (!fr_dlist_tail(&cc->nested)) goto null_result; /* end of list */
-
+ while ((ns = fr_dlist_tail(&cc->nested))) {
+ ar = ns->ar;
vp = _tmpl_cursor_eval(curr, cc);
- if (!vp) goto null_result;
+ if (!vp) continue;
- return vp;
+ ar = tmpl_attr_list_next(&vpt->data.attribute.ar, ar);
+ if (ar) {
+ fr_pair_list_t *list_head;
- default:
- fr_assert(0);
+ list_head = &vp->vp_group;
+ _tmpl_cursor_pair_init(vp, list_head, ar, cc);
+ curr = fr_pair_list_head(list_head);
+ continue;
+ }
+
+ return vp;
}
return NULL;
*/
switch (vpt->type) {
case TMPL_TYPE_ATTR:
- case TMPL_TYPE_LIST:
_tmpl_cursor_pair_init(list, cc->list, tmpl_attr_list_head(&vpt->data.attribute.ar), cc);
break;
if (!vp) {
if (err) {
*err = -1;
- if (tmpl_is_list(vpt)) {
- fr_strerror_printf("List \"%s\" is empty", vpt->name);
- } else {
- fr_strerror_printf("No matching \"%s\" pairs found", tmpl_attr_tail_da(vpt)->name);
- }
+ fr_strerror_printf("No matching \"%s\" pairs found", tmpl_attr_tail_da(vpt)->name);
}
return NULL;
}
{
fr_pair_t *list;
- fr_assert(tmpl_is_attr(vpt) || tmpl_is_list(vpt));
+ fr_assert(tmpl_is_attr(vpt));
if (err) *err = 0;
*/
if (tmpl_request_ptr(&request, tmpl_request(vpt)) < 0) {
if (err) *err = -3;
- error:
memset(cc, 0, sizeof(*cc)); /* so tmpl_dcursor_clear doesn't explode */
return NULL;
}
- /*
- * Get the right list in the specified context
- */
- if (!vpt->rules.attr.list_as_attr) {
- list = tmpl_get_list(request, vpt);
- if (!list) {
- fr_strerror_printf("List \"%s\" not available in this context",
- fr_table_str_by_value(pair_list_table, tmpl_list(vpt), "<INVALID>"));
- if (err) *err = -2;
- goto error;
- }
- } else {
- list = request->pair_root;
- }
+ list = request->pair_root;
return tmpl_dcursor_init_relative(err, ctx, cc, cursor, request, list, vpt, build, uctx);
}
TMPL_VERIFY(vpt);
- fr_assert(tmpl_is_attr(vpt) || tmpl_is_list(vpt));
+ fr_assert(tmpl_is_attr(vpt));
/*
* Navigate to the correct request context
*/
if (tmpl_request_ptr(&request, tmpl_request(vpt)) < 0) return -3;
- if (!vpt->rules.attr.list_as_attr) {
- /*
- * Get the right list in the specified context
- */
- list_head = tmpl_list_head(request, tmpl_list(vpt));
- if (!list_head) {
- fr_strerror_printf("List \"%s\" not available in this context",
- fr_table_str_by_value(pair_list_table, tmpl_list(vpt), "<INVALID>"));
- return -2;
- }
- list_ctx = tmpl_list_ctx(request, tmpl_list(vpt));
- } else {
- list_head = &request->pair_root->vp_group;
- list_ctx = request->pair_root;
- }
-
- /*
- * If it's a list, just return the list head
- */
- if (vpt->type == TMPL_TYPE_LIST) {
- do_list:
- if (existing) EXTENT_ADD(existing, NULL, list_ctx, list_head);
- return 0;
- }
+ list_head = &request->pair_root->vp_group;
+ list_ctx = request->pair_root;
/*
* If it's a leaf skip all the expensive
break;
default:
- goto do_list;
+ if (existing) EXTENT_ADD(existing, NULL, list_ctx, list_head);
+ return 0;
}
/*
static inline CC_HINT(always_inline)
int _tmpl_setup_and_cursor_init(fr_pair_t **vp_out, tmpl_dcursor_vars_t *vars, request_t *request, char const *ref)
{
- tmpl_afrom_attr_substr(autofree, NULL, &vars->vpt, &FR_SBUFF_IN(ref, strlen(ref)), NULL, &(tmpl_rules_t){.attr = {.dict_def = test_dict}});
+ tmpl_afrom_attr_substr(autofree, NULL, &vars->vpt, &FR_SBUFF_IN(ref, strlen(ref)), NULL,
+ &(tmpl_rules_t){
+ .attr = {
+ .dict_def = test_dict,
+ .list_def = PAIR_LIST_REQUEST
+ }
+ });
TEST_CHECK(vars->vpt!= NULL);
TEST_MSG("Failed creating tmpl from %s: %s", ref, fr_strerror());
if (!vars->vpt) {
static inline CC_HINT(always_inline)
int _tmpl_setup_and_cursor_build_init(fr_pair_t **vp_out, tmpl_dcursor_vars_t *vars, request_t *request, char const *ref)
{
- tmpl_afrom_attr_substr(autofree, NULL, &vars->vpt, &FR_SBUFF_IN(ref, strlen(ref)), NULL, &(tmpl_rules_t){.attr = {.dict_def = test_dict}});
- TEST_CHECK(vars->vpt!= NULL);
+ tmpl_afrom_attr_substr(autofree, NULL, &vars->vpt, &FR_SBUFF_IN(ref, strlen(ref)), NULL,
+ &(tmpl_rules_t){
+ .attr = {
+ .dict_def = test_dict,
+ .list_def = PAIR_LIST_REQUEST
+ }
+ });
+ TEST_CHECK(vars->vpt != NULL);
TEST_MSG("Failed creating tmpl from %s: %s", ref, fr_strerror());
if (!vars->vpt) {
*vp_out = NULL;
*/
fr_pair_t *tmpl_get_list(request_t *request, tmpl_t const *vpt)
{
- tmpl_pair_list_t list;
+ fr_dict_attr_t const *da;
if (!request) return NULL;
- if (vpt->rules.attr.list_as_attr) {
- fr_dict_attr_t const *da;
- da = ((tmpl_attr_t *)tmpl_attr_list_head(&vpt->data.attribute.ar))->ar_da;
+ da = ((tmpl_attr_t *)tmpl_attr_list_head(&vpt->data.attribute.ar))->ar_da;
- if (da == request_attr_request) return request->pair_list.request;
- if (da == request_attr_reply) return request->pair_list.reply;
- if (da == request_attr_control) return request->pair_list.control;
- if (da == request_attr_state) return request->pair_list.state;
-
- return NULL;
- }
-
- list = tmpl_list(vpt);
-
- switch (list) {
- /* Don't add default */
- case PAIR_LIST_UNKNOWN:
- break;
-
- case PAIR_LIST_REQUEST:
- return request->pair_list.request;
-
- case PAIR_LIST_REPLY:
- return request->pair_list.reply;
-
- case PAIR_LIST_CONTROL:
- return request->pair_list.control;
-
- case PAIR_LIST_STATE:
- return request->pair_list.state;
- }
-
- RWDEBUG2("List \"%s\" is not available",
- fr_table_str_by_value(pair_list_table, list, "<INVALID>"));
+ if (da == request_attr_request) return request->pair_list.request;
+ if (da == request_attr_reply) return request->pair_list.reply;
+ if (da == request_attr_control) return request->pair_list.control;
+ if (da == request_attr_state) return request->pair_list.state;
return NULL;
}
TMPL_VERIFY(vpt);
- fr_assert(!tmpl_is_list(vpt));
fr_assert(!buff || (bufflen >= 2));
switch (vpt->type) {
*/
case TMPL_TYPE_UNINITIALISED:
case TMPL_TYPE_NULL:
- case TMPL_TYPE_LIST:
case TMPL_TYPE_EXEC_UNRESOLVED:
case TMPL_TYPE_ATTR_UNRESOLVED:
case TMPL_TYPE_XLAT_UNRESOLVED:
*/
case TMPL_TYPE_UNINITIALISED:
case TMPL_TYPE_NULL:
- case TMPL_TYPE_LIST:
case TMPL_TYPE_EXEC_UNRESOLVED:
case TMPL_TYPE_REGEX:
case TMPL_TYPE_REGEX_UNCOMPILED:
* @param request The current #request_t.
* @param vpt specifying the #fr_pair_t type or list to copy.
* Must be one of the following types:
- * - #TMPL_TYPE_LIST
* - #TMPL_TYPE_ATTR
* @return
* - -1 if no matching #fr_pair_t could be found.
TMPL_VERIFY(vpt);
- fr_assert(tmpl_is_attr(vpt) || tmpl_is_list(vpt));
+ fr_assert(tmpl_is_attr(vpt));
for (vp = tmpl_dcursor_init(&err, NULL, &cc, &from, request, vpt);
vp;
* @param request The current #request_t.
* @param vpt specifying the #fr_pair_t type or list to copy.
* Must be one of the following types:
- * - #TMPL_TYPE_LIST
* - #TMPL_TYPE_ATTR
* @return
* - -1 if no matching #fr_pair_t could be found.
TMPL_VERIFY(vpt);
- fr_assert(tmpl_is_attr(vpt) || tmpl_is_list(vpt));
+ fr_assert(tmpl_is_attr(vpt));
fr_pair_list_free(out);
* @param[in] request The current #request_t.
* @param[in] vpt specifying the #fr_pair_t type to find.
* Must be one of the following types:
- * - #TMPL_TYPE_LIST
* - #TMPL_TYPE_ATTR
* @return
* - 0 on success (found matching #fr_pair_t).
int ret = 0;
FR_DLIST_HEAD(fr_value_box_list) list;
- fr_assert(tmpl_is_attr(vpt) || tmpl_is_list(vpt));
+ fr_assert(tmpl_is_attr(vpt));
fr_value_box_list_init(&list);
return -1;
}
- if (tmpl_is_attr(vpt) || tmpl_is_list(vpt)) {
- return tmpl_eval_pair(ctx, out, request, vpt);
- }
+ if (tmpl_is_attr(vpt)) return tmpl_eval_pair(ctx, out, request, vpt);
if (tmpl_is_data(vpt)) {
MEM(value = fr_value_box_alloc(ctx, tmpl_value_type(vpt), NULL,
*
* @copyright 2014-2020 The FreeRADIUS server project
*/
+#include "lib/util/strerror.h"
RCSID("$Id$")
#define _TMPL_PRIVATE 1
{ L("data"), TMPL_TYPE_DATA },
{ L("attr"), TMPL_TYPE_ATTR },
- { L("list"), TMPL_TYPE_LIST },
{ L("exec"), TMPL_TYPE_EXEC },
{ L("xlat"), TMPL_TYPE_XLAT },
switch (vpt->type) {
case TMPL_TYPE_ATTR:
case TMPL_TYPE_ATTR_UNRESOLVED:
- case TMPL_TYPE_LIST:
break;
default:
{
switch (vpt->type) {
case TMPL_TYPE_ATTR:
- case TMPL_TYPE_LIST:
case TMPL_TYPE_ATTR_UNRESOLVED:
tmpl_attr_debug(vpt);
return;
}
}
+/** Indicate whether this attribute reference represents one of packets or legacy lists
+ *
+ * @note This cannot be converted to static inline in tmpl.h because the visibility of
+ * the dictionary attributes are set to hidden so they can't be accessed by code
+ * including tmpl.h.
+ */
+bool ar_is_list_attr(tmpl_attr_t const *ar)
+{
+ if (!ar_is_normal(ar)) return false;
+
+ return (ar->ar_da == request_attr_request) ||
+ (ar->ar_da == request_attr_reply) ||
+ (ar->ar_da == request_attr_control) ||
+ (ar->ar_da == request_attr_state);
+}
+
/** @name Parse list and request qualifiers to #pair_list_t and #tmpl_request_ref_t values
*
* These functions also resolve #pair_list_t and #tmpl_request_ref_t values to #request_t
tmpl_request_t *tail = tmpl_request_list_tail(out);
unsigned int depth = 0;
fr_sbuff_marker_t m;
+ bool seen_outer = false;
if (!at_rules) at_rules = &default_rules.attr;
break;
}
+ switch (ref) {
+ case REQUEST_PARENT:
+ {
+ if (seen_outer) {
+ fr_strerror_const("\"parent\" qualifiers not allowed after \"outer\" qualifier");
+ goto error;
+ }
+ }
+ break;
+
+ case REQUEST_OUTER:
+ {
+ if (seen_outer) {
+ fr_strerror_const("Duplicate \"outer\" request qualifiers not allowed");
+ error:
+ tmpl_request_list_talloc_free_to_tail(out, tail);
+ FR_SBUFF_ERROR_RETURN(&our_in);
+ }
+ seen_outer = true;
+ }
+ break;
+
+ case REQUEST_CURRENT:
+ break;
+
+ case REQUEST_UNKNOWN:
+ fr_assert(0);
+ goto error;
+ }
+
/*
* We don't want to misidentify the list
* as being part of an attribute.
if (err) *err = TMPL_ATTR_ERROR_INVALID_LIST_QUALIFIER;
fr_sbuff_set(&our_in, in); /* Marker at the start */
- error:
- tmpl_request_list_talloc_free_to_tail(out, tail);
- FR_SBUFF_ERROR_RETURN(&our_in);
+ goto error;
}
/*
case TMPL_TYPE_ATTR:
case TMPL_TYPE_ATTR_UNRESOLVED:
- case TMPL_TYPE_LIST:
tmpl_attr_list_talloc_init(tmpl_attr(vpt));
tmpl_request_list_talloc_init(&vpt->data.attribute.rr);
break;
{
tmpl_attr_t *ar;
- tmpl_assert_type(tmpl_is_attr(vpt) || tmpl_is_list(vpt) || tmpl_is_attr_unresolved(vpt));
+ tmpl_assert_type(tmpl_is_attr(vpt) || tmpl_is_attr_unresolved(vpt));
if (tmpl_attr_list_num_elements(tmpl_attr(vpt)) == 0) {
ar = tmpl_attr_add(vpt, TMPL_ATTR_TYPE_UNKNOWN);
{
tmpl_attr_t *ref = NULL;
- tmpl_assert_type(tmpl_is_attr(vpt) || tmpl_is_list(vpt) || tmpl_is_attr_unresolved(vpt));
+ tmpl_assert_type(tmpl_is_attr(vpt) || tmpl_is_attr_unresolved(vpt));
if (tmpl_attr_list_num_elements(tmpl_attr(vpt)) == 0) return;
{
tmpl_attr_t *ref = NULL;
- tmpl_assert_type(tmpl_is_attr(vpt) || tmpl_is_list(vpt) || tmpl_is_attr_unresolved(vpt));
+ tmpl_assert_type(tmpl_is_attr(vpt) || tmpl_is_attr_unresolved(vpt));
while ((ref = tmpl_attr_list_next(tmpl_attr(vpt), ref))) if (ref->ar_num == from) ref->ar_num = to;
while ((ar = tmpl_attr_list_prev(tmpl_attr(vpt), ar))) ar->resolve_only = true;
break;
}
+ TMPL_ATTR_VERIFY(vpt);
}
/** Parse array subscript and in future other filters
FR_SBUFF_SET_RETURN(name, &our_name);
}
-extern fr_dict_attr_t const *tmpl_attr_unspec;
-
static inline CC_HINT(nonnull(3,4))
fr_slen_t tmpl_attr_ref_from_unspecified_substr(tmpl_attr_t *ar, tmpl_attr_error_t *err,
tmpl_t *vpt,
tmpl_attr_insert(vpt, ar);
}
- if (tmpl_is_attr(vpt) && (tmpl_rules_cast(vpt) == tmpl_attr_tail_da(vpt)->type)) vpt->rules.cast = FR_TYPE_NULL;
+ if (tmpl_is_attr(vpt) && tmpl_attr_tail_is_normal(vpt) &&
+ (tmpl_rules_cast(vpt) == tmpl_attr_tail_da(vpt)->type)) vpt->rules.cast = FR_TYPE_NULL;
fr_sbuff_marker_release(&m_s);
return 0;
}
-/** Parse a string into a TMPL_TYPE_ATTR_* or #TMPL_TYPE_LIST type #tmpl_t
+/** Parse a string into a TMPL_TYPE_ATTR_* of type #tmpl_t
*
* @param[in,out] ctx to allocate #tmpl_t in.
* @param[out] err May be NULL. Provides the exact error that the parser hit
* when processing the attribute ref.
* @param[out] out Where to write pointer to new #tmpl_t.
* @param[in] name of attribute including #tmpl_request_ref_t and #pair_list_t qualifiers.
- * If only #tmpl_request_ref_t #pair_list_t qualifiers are found,
- * a #TMPL_TYPE_LIST #tmpl_t will be produced.
* @param[in] p_rules Formatting rules used to check for trailing garbage.
* @param[in] t_rules Rules which control parsing:
* - dict_def The default dictionary to use if attributes
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;
tmpl_attr_rules_t const *at_rules;
fr_sbuff_marker_t m_l;
+ tmpl_attr_t *ar;
+
+ fr_assert_msg((request_attr_request != NULL) &&
+ (request_attr_reply != NULL) &&
+ (request_attr_control != NULL) &&
+ (request_attr_state != NULL), "missing list attr. request_global_init not called");
if (!t_rules) t_rules = &default_rules;
at_rules = &t_rules->attr;
* Parse one or more request references
*/
ret = tmpl_request_ref_list_from_substr(vpt, NULL,
- &vpt->data.attribute.rr,
- &our_name,
- p_rules,
- at_rules);
+ &vpt->data.attribute.rr,
+ &our_name,
+ p_rules,
+ at_rules);
if (ret < 0) {
error:
*out = NULL;
fr_sbuff_marker(&m_l, &our_name);
- if (!at_rules->list_as_attr) {
- /*
- * Parse the list reference
- *
- * This code should be removed when lists
- * are integrated into attribute references.
- */
- fr_sbuff_out_by_longest_prefix(&list_len, &vpt->data.attribute.list, pair_list_table,
- &our_name, at_rules->list_def);
-
- /*
- * Check if we need to backtrack
- *
- * Lists can be followed by a '.', '[', or the end of the attribute reference
- *
- * If we don't find any of those things it wasn't an actual list match
- * but one of the list identifiers matched part of an attribute reference.
- *
- * i.e. reply with reply-message.
- */
- if ((list_len > 0) && !fr_sbuff_is_char(&our_name, '.') &&
- !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 = at_rules->list_def;
- }
-
- if ((at_rules->parent || at_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_ERROR_RETURN(&our_name);
- }
- }
-
/*
- * Parse the attribute reference
+ * Start parsing attribute references
*
- * This will either be after:
- * - A zero length list, i.e. just after the prefix '&', in which case we require an attribue
- * - '.' and then an allowed char, so we're sure it's not just a bare list ref.
+ * This includes lists, like request,
+ * reply, control etc...
*/
- if ((list_len == 0) ||
- (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,
- at_rules->parent, at_rules->parent,
- &our_name, p_rules, at_rules, 0);
- if (ret < 0) goto error;
+ ret = tmpl_attr_afrom_attr_substr(vpt, err,
+ vpt,
+ at_rules->parent, at_rules->parent,
+ &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) tmpl_attr_to_raw(vpt);
+ /*
+ * 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) tmpl_attr_to_raw(vpt);
- /*
- * Check to see what the first attribute reference
- * was. If it wasn't a known list group attribute
- * and we're parsing in list_as_attr mode, then
- * we need to add in a default list.
- */
- if (at_rules->list_as_attr) {
- tmpl_attr_t *ar;
-
- ar = tmpl_attr_list_head(tmpl_attr(vpt));
- fr_assert(ar != NULL);
-
- if ((ar->ar_type != TMPL_ATTR_TYPE_NORMAL) ||
- ((ar->ar_da != request_attr_request) &&
- (ar->ar_da != request_attr_reply) &&
- (ar->ar_da != request_attr_control) &&
- (ar->ar_da != request_attr_state))) {
- MEM(ar = talloc(vpt, tmpl_attr_t));
- *ar = (tmpl_attr_t){
- .ar_type = TMPL_ATTR_TYPE_NORMAL,
- .ar_parent = fr_dict_root(fr_dict_internal())
- };
-
- switch (at_rules->list_def) {
- default:
- case PAIR_LIST_REQUEST:
- ar->ar_da = request_attr_request;
- break;
+ /*
+ * Add the default list qualifier if a list
+ * wasn't specified.
+ *
+ * This will likely go away when we have
+ * local attributes/variables.
+ */
+ if (!tmpl_attr_head_is_list(vpt)) {
+ MEM(ar = talloc(vpt, tmpl_attr_t));
+ *ar = (tmpl_attr_t){
+ .ar_type = TMPL_ATTR_TYPE_NORMAL,
+ .ar_num = NUM_UNSPEC,
+ .ar_parent = fr_dict_root(fr_dict_internal())
+ };
- case PAIR_LIST_REPLY:
- ar->ar_da = request_attr_reply;
- break;
+ switch (at_rules->list_def) {
+ default:
+ case PAIR_LIST_REQUEST:
+ ar->ar_da = request_attr_request;
+ break;
- case PAIR_LIST_CONTROL:
- ar->ar_da = request_attr_control;
- break;
+ case PAIR_LIST_REPLY:
+ ar->ar_da = request_attr_reply;
+ break;
- case PAIR_LIST_STATE:
- ar->ar_da = request_attr_state;
- break;
- }
+ case PAIR_LIST_CONTROL:
+ ar->ar_da = request_attr_control;
+ break;
- /*
- * Prepend the list ref so it gets evaluated
- * first.
- */
- tmpl_attr_list_insert_head(tmpl_attr(vpt), ar);
- }
+ case PAIR_LIST_STATE:
+ ar->ar_da = request_attr_state;
+ break;
}
- }
- /*
- * If there's no attribute references
- * treat this as a list reference.
- *
- * Eventually we'll remove TMPL_TYPE_LIST
- */
- if (!at_rules->list_as_attr && (tmpl_attr_list_num_elements(tmpl_attr(vpt)) == 0)) {
- tmpl_attr_t *ar;
- fr_slen_t slen;
-
- MEM(ar = talloc_zero(vpt, tmpl_attr_t));
- slen = tmpl_attr_parse_filter(err, ar, &our_name, at_rules);
- if (slen == 0) { /* No filter */
- talloc_free(ar);
- } else if (slen > 0) { /* Found a filter */
- tmpl_attr_list_insert_tail(&vpt->data.attribute.ar, ar);
- } else if (slen < 0) { /* Filter error */
- goto error;
- }
- vpt->type = TMPL_TYPE_LIST;
+ /*
+ * Prepend the list ref so it gets evaluated
+ * first.
+ */
+ tmpl_attr_list_insert_head(&vpt->data.attribute.ar, ar);
+ TMPL_ATTR_VERIFY(vpt);
}
tmpl_set_name(vpt, T_BARE_WORD, fr_sbuff_start(&our_name), fr_sbuff_used(&our_name));
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
- * input rules, it is also internally consistent.
- */
- if (at_rules->list_as_attr) {
- tmpl_attr_t *ar;
-
- ar = tmpl_attr_list_head(tmpl_attr(vpt));
- fr_assert(ar != NULL);
-
- if (ar->ar_da == request_attr_request) {
- vpt->rules.attr.list_def = PAIR_LIST_REQUEST;
-
- } else if (ar->ar_da == request_attr_reply) {
- vpt->rules.attr.list_def = PAIR_LIST_REPLY;
-
- } else if (ar->ar_da == request_attr_control) {
- vpt->rules.attr.list_def = PAIR_LIST_CONTROL;
-
- } else if (ar->ar_da == request_attr_state) {
- vpt->rules.attr.list_def = PAIR_LIST_STATE;
- }
-
- vpt->data.attribute.list = vpt->rules.attr.list_def;
- }
}
if (!tmpl_substr_terminal_check(&our_name, p_rules)) {
FR_SBUFF_SET_RETURN(name, &our_name);
}
-/** Parse a string into a TMPL_TYPE_ATTR_* or #TMPL_TYPE_LIST type #tmpl_t
+/** Parse a string into a TMPL_TYPE_ATTR_* #tmpl_t
*
* @param[in,out] ctx to allocate #tmpl_t in.
* @param[out] err May be NULL. Provides the exact error that the parser hit
* when processing the attribute ref.
* @param[out] out Where to write pointer to new #tmpl_t.
- * @param[in] name of attribute including #tmpl_request_ref_t and #pair_list_t qualifiers.
- * If only #tmpl_request_ref_t #pair_list_t qualifiers are found,
- * a #TMPL_TYPE_LIST #tmpl_t will be produced.
+ * @param[in] name of attribute including #tmpl_request_ref_t qualifiers.
* @param[in] t_rules Rules which control parsing. See tmpl_afrom_attr_substr() for details.
*
* @note Unlike #tmpl_afrom_attr_substr this function will error out if the entire
* These types contain dynamically allocated
* attribute and request references.
*/
- case TMPL_TYPE_LIST:
case TMPL_TYPE_ATTR:
case TMPL_TYPE_ATTR_UNRESOLVED:
tmpl_attr_list_talloc_free(tmpl_attr(vpt));
TMPL_VERIFY(vpt);
- if (!tmpl_attr_tail_da(vpt)->flags.is_unknown) return 1; /* Ensure at least the leaf is unknown */
+ if (!tmpl_attr_tail_is_unknown(vpt)) return 1; /* Ensure at least the leaf is unknown */
while ((ar = tmpl_attr_list_next(tmpl_attr(vpt), ar))) {
fr_dict_attr_t const *unknown, *known;
*/
fr_slen_t tmpl_attr_print(fr_sbuff_t *out, tmpl_t const *vpt, tmpl_attr_prefix_t ar_prefix)
{
- tmpl_attr_t *ar = NULL;
+ tmpl_attr_t const *ar = NULL;
fr_da_stack_t stack;
char printed_rr = false;
fr_sbuff_t our_out = FR_SBUFF(out);
* Only print things we can print...
*/
switch (vpt->type) {
- case TMPL_TYPE_LIST:
case TMPL_TYPE_ATTR_UNRESOLVED:
case TMPL_TYPE_ATTR:
break;
* we add the .unknown prefix.
*
*/
- if (!tmpl_is_list(vpt) && (ar = tmpl_attr_list_tail(tmpl_attr(vpt)))) {
- switch (ar->type) {
- case TMPL_ATTR_TYPE_NORMAL:
- case TMPL_ATTR_TYPE_UNSPEC:
- case TMPL_ATTR_TYPE_UNKNOWN:
- if (ar->ar_da->flags.is_raw) FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "raw.");
- break;
-
- case TMPL_ATTR_TYPE_UNRESOLVED:
- if (ar->ar_unresolved_raw) FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "raw.");
- break;
- }
- }
+ if (tmpl_attr_tail_is_raw(vpt)) FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "raw.");
/*
* Print attribute identifiers
*/
ar = NULL;
while ((ar = tmpl_attr_list_next(tmpl_attr(vpt), ar))) {
- if (!tmpl_is_list(vpt)) switch(ar->type) {
+ switch(ar->type) {
case TMPL_ATTR_TYPE_UNSPEC:
break;
TMPL_VERIFY(vpt);
switch (vpt->type) {
- case TMPL_TYPE_LIST:
case TMPL_TYPE_ATTR_UNRESOLVED:
case TMPL_TYPE_ATTR:
FR_SBUFF_RETURN(tmpl_attr_print, &our_out, vpt, ar_prefix);
tmpl_attr_t *seen_unknown = NULL;
tmpl_attr_t *seen_unresolved = NULL;
- fr_assert(tmpl_is_attr_unresolved(vpt) || tmpl_is_attr(vpt) || tmpl_is_list(vpt));
+ fr_assert(tmpl_is_attr_unresolved(vpt) || tmpl_is_attr(vpt));
/*
* Loop detection
* Known attribute cannot come after unresolved or unknown attributes
* Unknown attributes cannot come after unresolved attributes
*/
- if (!tmpl_is_list(vpt)) while ((ar = tmpl_attr_list_next(tmpl_attr(vpt), ar))) {
+ while ((ar = tmpl_attr_list_next(tmpl_attr(vpt), ar))) {
switch (ar->type) {
case TMPL_ATTR_TYPE_NORMAL:
if (seen_unknown) {
fr_fatal_assert_msg(ar->ar_parent,
"CONSISTENCY CHECK FAILED %s[%u]: attr ref missing parent",
file, line);
+ DA_VERIFY(ar->ar_da);
break;
case TMPL_ATTR_TYPE_UNSPEC:
da, da->name,
fr_type_to_str(da->type));
}
-
- if (!vpt->rules.attr.list_as_attr && (tmpl_list(vpt) >= PAIR_LIST_UNKNOWN)) {
- fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_ATTR "
- "attribute \"%s\" has invalid list (%i)",
- file, line, tmpl_attr_tail_da(vpt)->name, tmpl_list(vpt));
- }
-
tmpl_attr_verify(file, line, vpt);
}
break;
- case TMPL_TYPE_LIST:
- if ((nz = CHECK_ZEROED(vpt, attribute))) {
- PRINT_NON_ZEROED(vpt, attribute, nz);
- fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_LIST"
- "has non-zero bytes after the data.attribute struct in the union",
- file, line);
- }
-
- if ((tmpl_attr_list_num_elements(tmpl_attr(vpt)) > 0) &&
- ((tmpl_attr_t *)tmpl_attr_list_tail(tmpl_attr(vpt)))->da) {
-#ifndef NDEBUG
- tmpl_attr_debug(vpt);
-#endif
- fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_LIST contains %u "
- "references", file, line, tmpl_attr_list_num_elements(tmpl_attr(vpt)));
- }
- break;
-
case TMPL_TYPE_DATA:
if ((nz = CHECK_ZEROED(vpt, literal))) {
PRINT_NON_ZEROED(vpt, literal, nz);
if (!ctx) {
switch (map->lhs->type) {
case TMPL_TYPE_ATTR:
- case TMPL_TYPE_LIST:
tmpl_attr_rewrite_leaf_num(map->lhs, NUM_UNSPEC, NUM_ALL);
break;
*/
switch (map->rhs->type) {
case TMPL_TYPE_ATTR:
- case TMPL_TYPE_LIST:
tmpl_attr_rewrite_leaf_num(map->rhs, NUM_UNSPEC, NUM_ALL);
break;
* Lots of sanity checks for insane people...
*/
- /*
- * What exactly where you expecting to happen here?
- */
- if (tmpl_is_attr(map->lhs) &&
- tmpl_is_list(map->rhs)) {
- cf_log_err(map->ci, "Can't copy list into an attribute");
- return -1;
- }
-
/*
* Depending on the attribute type, some operators are disallowed.
*/
}
}
- if (tmpl_is_list(map->lhs)) {
- /*
- * Can't copy an xlat expansion or literal into a list,
- * we don't know what type of attribute we'd need
- * to create.
- *
- * The only exception is where were using a unary
- * operator like !*.
- */
- if (map->op != T_OP_CMP_FALSE) switch (map->rhs->type) {
- case TMPL_TYPE_XLAT_UNRESOLVED:
- case TMPL_TYPE_UNRESOLVED:
- cf_log_err(map->ci, "Can't copy value into list (we don't know which attribute to create)");
- return -1;
-
- default:
- break;
- }
-
- /*
- * Only += and :=, and !*, and ^= operators are supported
- * for lists.
- */
- switch (map->op) {
- case T_OP_CMP_FALSE:
- break;
-
- case T_OP_ADD_EQ:
- if (!tmpl_is_list(map->rhs) &&
- !tmpl_is_exec(map->rhs)) {
- cf_log_err(map->ci, "Invalid source for list assignment '%s += ...'", map->lhs->name);
- return -1;
- }
- break;
-
- case T_OP_SET:
- if (tmpl_is_exec(map->rhs)) {
- WARN("%s[%d]: Please change ':=' to '=' for list assignment",
- cf_filename(cp), cf_lineno(cp));
- }
-
- if (!tmpl_is_list(map->rhs)) {
- cf_log_err(map->ci, "Invalid source for list assignment '%s := ...'", map->lhs->name);
- return -1;
- }
- break;
-
- case T_OP_EQ:
- if (!tmpl_is_exec(map->rhs)) {
- cf_log_err(map->ci, "Invalid source for list assignment '%s = ...'", map->lhs->name);
- return -1;
- }
- break;
-
- case T_OP_PREPEND:
- if (!tmpl_is_list(map->rhs) &&
- !tmpl_is_exec(map->rhs)) {
- cf_log_err(map->ci, "Invalid source for list assignment '%s ^= ...'", map->lhs->name);
- return -1;
- }
- break;
-
- default:
- cf_log_err(map->ci, "Operator \"%s\" not allowed for list assignment",
- fr_table_str_by_value(fr_tokens_table, map->op, "<INVALID>"));
- return -1;
- }
- }
-
/*
* If the map has a unary operator there's no further
* processing we need to, as RHS is unused.
return 0;
}
-
static unlang_group_t *group_allocate(unlang_t *parent, CONF_SECTION *cs, unlang_ext_t const *ext)
{
unlang_group_t *g;
*/
t_rules = *(unlang_ctx->rules);
t_rules.attr.allow_unknown = true;
- t_rules.attr.list_as_attr = true;
RULES_VERIFY(&t_rules);
c = *prev;
*/
t_rules = *(unlang_ctx->rules);
t_rules.attr.allow_unknown = true;
- t_rules.attr.list_as_attr = true;
RULES_VERIFY(&t_rules);
/*
return NULL;
}
- if (tmpl_is_list(gext->vpt)) {
- cf_log_err(cs, "Cannot use list for 'switch' statement");
- goto error;
- }
-
if (tmpl_contains_regex(gext->vpt)) {
cf_log_err(cs, "Cannot use regular expression for 'switch' statement");
goto error;
return NULL;
}
- if (tmpl_is_list(vpt)) {
- cf_log_err(cs, "Cannot use list as argument for 'timeout' statement");
- goto error;
- }
-
if (tmpl_contains_regex(vpt)) {
cf_log_err(cs, "Cannot use regular expression as argument for 'timeout' statement");
goto error;
return NULL;
}
- if (tmpl_is_list(vpt)) {
- cf_log_err(cs, "Cannot use list as argument for 'limit' statement");
- goto error;
- }
-
if (tmpl_contains_regex(vpt)) {
cf_log_err(cs, "Cannot use regular expression as argument for 'limit' statement");
goto error;
*/
fr_assert(vpt);
- if (!tmpl_is_attr(vpt) && !tmpl_is_list(vpt)) {
- cf_log_err(cs, "MUST use attribute or list reference (not %s) in 'foreach'",
+ if (!tmpl_is_attr(vpt)) {
+ cf_log_err(cs, "MUST use attribute reference (not %s) in 'foreach'",
tmpl_type_to_str(vpt->type));
talloc_free(vpt);
return NULL;
fr_canonicalize_error(cs, &spaces, &text, slen, name2);
- cf_log_err(cs, "Parse error in condition!!!!");
+ cf_log_err(cs, "Parse error in condition");
cf_log_err(cs, "%s", text);
cf_log_err(cs, "%s^ %s", spaces, fr_strerror());
fr_pair_append(&child->request_pairs, vp);
if (gext->src) {
- if (tmpl_is_list(gext->src)) {
- if (tmpl_copy_pairs(child->request_ctx, &child->request_pairs, request, gext->src) < -1) {
- RPEDEBUG("Failed copying source attributes into subrequest");
- goto fail;
- }
- } else {
- if (tmpl_copy_pair_children(child->request_ctx, &child->request_pairs, request, gext->src) < -1) {
- RPEDEBUG("Failed copying source attributes into subrequest");
- goto fail;
- }
+ if (tmpl_copy_pair_children(child->request_ctx, &child->request_pairs, request, gext->src) < -1) {
+ RPEDEBUG("Failed copying source attributes into subrequest");
+ goto fail;
}
}
switch (map->lhs->type) {
case TMPL_TYPE_ATTR:
- case TMPL_TYPE_LIST:
case TMPL_TYPE_XLAT:
break;
case TMPL_TYPE_ATTR:
case TMPL_TYPE_EXEC:
case TMPL_TYPE_DATA:
- case TMPL_TYPE_LIST:
case TMPL_TYPE_REGEX_XLAT_UNRESOLVED:
case TMPL_TYPE_UNRESOLVED:
case TMPL_TYPE_XLAT:
fr_value_box_list_move((FR_DLIST_HEAD(fr_value_box_list) *)out->dlist, &result);
continue;
- } else if (tmpl_is_attr(node->vpt) || tmpl_is_list(node->vpt)) {
+ } else if (tmpl_is_attr(node->vpt)) {
if (node->fmt[0] == '&') {
XLAT_DEBUG("** [%i] %s(attribute) - %s", unlang_interpret_stack_depth(request), __FUNCTION__,
node->fmt);
.prefix = TMPL_ATTR_REF_PREFIX_AUTO,
.allow_unknown = false,
.allow_unresolved = false,
- .list_as_attr = true,
},
});
if (slen <= 0) goto fail;
/*
* Convert raw rcodes to xlat's.
*
- * @todo - if it's '!', and the node is tmpl_is_list, or tmpl_contains_attr
+ * @todo - if it's '!', and the node is tmpl_contains_attr
* re-write it to an existence check function, with node->fmt the node->vpt->name.
*
*/
if (node->type != XLAT_TMPL) return true;
- if (tmpl_is_list(node->vpt)) {
- list:
- fr_strerror_const("Cannot use list references in condition");
- return false;
- }
-
if (!tmpl_is_attr(node->vpt)) return true;
da = tmpl_attr_tail_da(node->vpt);
if (fr_type_is_structural(da->type)) {
- if (da->dict == fr_dict_internal()) goto list;
-
- fr_strerror_const("Cannot use structural types in condition");
+ fr_strerror_const("Cannot compare structural types");
return false;
}
* Deal with virtual attributes.
*/
if (tmpl_is_attr(vpt) && tmpl_attr_tail_da(vpt)->flags.virtual) {
- if (tmpl_attr_num_elements(vpt) > (size_t) (1 + vpt->rules.attr.list_as_attr)) {
- fr_strerror_const("Virtual attributes cannot be nested.");
+ if (tmpl_attr_num_elements(vpt) > (size_t)(tmpl_attr_head_is_list(vpt) ? 2 : 1)) {
+ fr_strerror_const("Virtual attributes cannot be nested");
goto error;
}
/*
* Could it be a virtual attribute?
*/
- if ((tmpl_attr_num_elements(vpt) == (size_t) (1 + vpt->rules.attr.list_as_attr)) && (xlat_resolve_virtual_attribute(node, vpt) == 0)) goto done;
+ if ((tmpl_attr_num_elements(vpt) == (size_t)2) && (xlat_resolve_virtual_attribute(node, vpt) == 0)) goto done;
if (!t_rules || !t_rules->attr.allow_unresolved) {
talloc_free(vpt);
*/
fr_assert(!tmpl_contains_regex(node->vpt));
- // attr or list
- fr_assert(tmpl_is_list(node->vpt) || tmpl_is_attr(node->vpt));
+ // attr
+ fr_assert(tmpl_is_attr(node->vpt));
fr_assert(talloc_parent(node->vpt) == node);
fr_assert(!node->flags.pure);
# define PAIR_LIST_VERIFY(_x) fr_pair_list_nonnull_assert(_x)
#endif
-
#ifdef TEST_CHECK
/** Macro for use in acutest tests
*/
* Prevent people from accidentally caching
* cache control attributes.
*/
- if (tmpl_is_list(map->rhs)) switch (vp->da->attr) {
+ switch (vp->da->attr) {
case FR_CACHE_TTL:
case FR_CACHE_STATUS_ONLY:
case FR_CACHE_MERGE_NEW:
{
fr_token_t quote;
c_map->lhs = map->lhs; /* lhs shouldn't be touched, so this is ok */
- do_rhs:
+
if (vp->vp_type == FR_TYPE_STRING) {
quote = is_printable(vp->vp_strvalue, vp->vp_length) ?
T_SINGLE_QUOTED_STRING : T_DOUBLE_QUOTED_STRING;
TMPL_TYPE_DATA, quote, map->rhs->name, map->rhs->len));
if (fr_value_box_copy(c_map->rhs, tmpl_value(c_map->rhs), &vp->data) < 0) {
REDEBUG("Failed copying attribute value");
- error:
talloc_free(pool);
talloc_free(c);
RETURN_MODULE_FAIL;
}
break;
- /*
- * Lists are weird... We need to fudge a new LHS template,
- * which is a combination of the LHS list and the attribute.
- */
- case TMPL_TYPE_LIST:
- if (tmpl_attr_afrom_list(c_map, &c_map->lhs, map->lhs, vp->da) < 0) {
- RPERROR("Failed attribute -> list copy");
- goto error;
- }
- goto do_rhs;
-
default:
fr_assert(0);
}
{
if (unlang_fixup_update(map, ctx) < 0) return -1;
- if (!tmpl_is_attr(map->lhs) &&
- !tmpl_is_list(map->lhs)) {
- cf_log_err(map->ci, "Destination must be an attribute ref or a list");
+ if (tmpl_is_attr(map->lhs)) {
+ cf_log_err(map->ci, "Destination must be an attribute ref");
return -1;
}
type = tmpl_attr_tail_da(map->lhs)->type;
break;
- case TMPL_TYPE_LIST:
- break;
-
case TMPL_TYPE_ATTR_UNRESOLVED:
cf_log_err(map->ci, "Unknown attribute %s", tmpl_attr_tail_unresolved(map->lhs));
return -1;
*/
switch (vpt_p->type) {
case TMPL_TYPE_ATTR:
- case TMPL_TYPE_LIST:
{
#define VECTOR_INCREMENT 20
fr_dcursor_t cursor;
goto finish;
}
- if(tmpl_is_list(vpt)) {
- ret = -1;
- goto finish;
- }
-
/* Only string values should be used for SMTP components */
if(tmpl_expanded_type(vpt) != FR_TYPE_STRING) {
cf_log_err(cp, "Attribute reference must be a string");
--- /dev/null
+#
+# PRE: if
+#
+
+&reply.Reply-Message = 'foo'
+&reply.User-Name = 'bar'
+
+if (&reply.[0] != 'foo') {
+ test_fail
+}
+
+if (&reply.[1] != 'bar') {
+ test_fail
+}
+
+success
\ No newline at end of file
tmpl-rules allow_unresolved=yes allow_unknown=yes
condition &request.User-Name == &reply.User-Name
-match &User-Name == &reply.User-Name
+match &request.User-Name == &reply.User-Name
# All IP address literals should be parsed as prefixes
condition ("foo\
#
# Extra braces get squashed
#
-condition (&User-Name == &User-Password)
-match &User-Name == &User-Password
+condition (&request.User-Name == &request.User-Password)
+match &request.User-Name == &request.User-Password
condition (!ok)
match !ok
#
# These next two are identical after normalization
#
-condition (&User-Name == &User-Password || &Filter-Id == &Reply-Message)
-match &User-Name == &User-Password || &Filter-Id == &Reply-Message
+condition (&request.User-Name == &request.User-Password || &request.Filter-Id == &request.Reply-Message)
+match &request.User-Name == &request.User-Password || &request.Filter-Id == &request.Reply-Message
-condition ((&User-Name == &User-Password) || (&Filter-Id == &Reply-Message))
-match (&User-Name == &User-Password) || (&Filter-Id == &Reply-Message)
+condition ((&request.User-Name == &request.User-Password) || (&request.Filter-Id == &request.Reply-Message))
+match (&request.User-Name == &request.User-Password) || (&request.Filter-Id == &request.Reply-Message)
-condition (!(&User-Name == &User-Password) || (&Filter-Id == &Reply-Message))
-match !(&User-Name == &User-Password) || (&Filter-Id == &Reply-Message)
+condition (!(&request.User-Name == &request.User-Password) || (&request.Filter-Id == &request.Reply-Message))
+match !(&request.User-Name == &request.User-Password) || (&request.Filter-Id == &request.Reply-Message)
# different from the previous ones.
-condition (!((&User-Name == &User-Password) || (&Filter-Id == &Reply-Message)))
-match !((&User-Name == &User-Password) || (&Filter-Id == &Reply-Message))
+condition (!((&request.User-Name == &request.User-Password) || (&request.Filter-Id == &request.Reply-Message)))
+match !((&request.User-Name == &request.User-Password) || (&request.Filter-Id == &request.Reply-Message))
-condition (!(&User-Name == &User-Password) || (&Filter-Id == &Reply-Message))
-match !(&User-Name == &User-Password) || (&Filter-Id == &Reply-Message)
+condition (!(&request.User-Name == &request.User-Password) || (&request.Filter-Id == &request.Reply-Message))
+match !(&request.User-Name == &request.User-Password) || (&request.Filter-Id == &request.Reply-Message)
condition ((a == b) || (c == d)))
match ERROR offset 23: Unexpected closing brace
-condition (handled && (&Packet-Type == Access-Challenge))
-match handled && (&Packet-Type == Access-Challenge)
+condition (handled && (&request.Packet-Type == Access-Challenge))
+match handled && (&request.Packet-Type == Access-Challenge)
# This is OK, without the braces
-condition handled && &Packet-Type == Access-Challenge
-match handled && &Packet-Type == Access-Challenge
+condition handled && &request.Packet-Type == Access-Challenge
+match handled && &request.Packet-Type == Access-Challenge
# and this, though it's not a good idea.
-condition handled &&&Packet-Type == Access-Challenge
-match handled && &Packet-Type == Access-Challenge
+condition handled &&&request.Packet-Type == Access-Challenge
+match handled && &request.Packet-Type == Access-Challenge
condition &reply == &request
-match ERROR offset 1: Cannot use list references in condition
+match ERROR offset 1: Nesting types such as groups or TLVs cannot be compared
condition &reply == "hello"
-match ERROR offset 1: Cannot use list references in condition
+match ERROR offset 1: Nesting types such as groups or TLVs cannot be compared
condition "hello" == &reply
-match ERROR offset 12: Cannot use list references in condition
+match ERROR offset 12: Nesting types such as groups or TLVs cannot be compared
#
# Convert != to !(COND) for normal checks
#
-condition &User-Name == &User-Password
-match &User-Name == &User-Password
+condition &request.User-Name == &request.User-Password
+match &request.User-Name == &request.User-Password
-condition &User-Name != &User-Password
-match !&User-Name == &User-Password
+condition &request.User-Name != &request.User-Password
+match !&request.User-Name == &request.User-Password
-condition !&User-Name != &User-Password
-match &User-Name == &User-Password
+condition !&request.User-Name != &request.User-Password
+match &request.User-Name == &request.User-Password
condition <ipv6addr>::1
match ERROR offset 1: Invalid cast from ipv6addr to bool. Unsupported
-condition <ipaddr>&Filter-Id == &Framed-IP-Address
-match <ipaddr>&Filter-Id == &Framed-IP-Address
+condition <ipaddr>&request.Filter-Id == &request.Framed-IP-Address
+match <ipaddr>&request.Filter-Id == &request.Framed-IP-Address
-condition <ipaddr>&Filter-Id == &Framed-IP-Address
-match <ipaddr>&Filter-Id == &Framed-IP-Address
+condition <ipaddr>&request.Filter-Id == &request.Framed-IP-Address
+match <ipaddr>&request.Filter-Id == &request.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 == <blerg>&Framed-IP-Address
-match ERROR offset 24: Unknown data type
+condition <ipaddr>&request.Filter-Id == <blerg>&request.Framed-IP-Address
+match ERROR offset 32: Unknown data type
-condition <blerg>&Filter-Id == "foo"
+condition <blerg>&request.Filter-Id == "foo"
match ERROR offset 2: Unknown data type
#
# Normalize things
#
-condition <ipaddr>127.0.0.1 < &Framed-IP-Address
-match &Framed-IP-Address > 127.0.0.1
+condition <ipaddr>127.0.0.1 < &request.Framed-IP-Address
+match &request.Framed-IP-Address > 127.0.0.1
# =* and !* are only for attrs / lists
condition "foo" !* bar
match ERROR offset 7: Invalid operator =*
# existence checks don't need the RHS
-condition &User-Name =* bar
-match ERROR offset 12: Invalid operator =*
+condition &request.User-Name =* bar
+match ERROR offset 20: Invalid operator =*
-condition &User-Name !* bar
-match ERROR offset 12: Invalid operator !*
+condition &request.User-Name !* bar
+match ERROR offset 20: Invalid operator !*
-condition !&User-Name =* bar
-match ERROR offset 13: Invalid operator =*
+condition !&request.User-Name =* bar
+match ERROR offset 21: Invalid operator =*
-condition !&User-Name !* bar
-match ERROR offset 13: Invalid operator !*
+condition !&request.User-Name !* bar
+match ERROR offset 21: Invalid operator !*
# redundant casts get squashed
-condition <ipaddr>&Framed-IP-Address == 127.0.0.1
-match &Framed-IP-Address == 127.0.0.1
+condition <ipaddr>&request.Framed-IP-Address == 127.0.0.1
+match &request.Framed-IP-Address == 127.0.0.1
-condition <cidr>&Framed-IP-Address <= 192.168.0.0/16
-match <ipv4prefix>&Framed-IP-Address <= 192.168.0.0/16
+condition <cidr>&request.Framed-IP-Address <= 192.168.0.0/16
+match <ipv4prefix>&request.Framed-IP-Address <= 192.168.0.0/16
# All IP address literals should be parsed as prefixes
-condition &Framed-IP-Address <= 192.168.0.0/16
-match <ipv4prefix>&Framed-IP-Address <= 192.168.0.0/16
+condition &request.Framed-IP-Address <= 192.168.0.0/16
+match <ipv4prefix>&request.Framed-IP-Address <= 192.168.0.0/16
# string attributes must be string
-condition &User-Name == "bob"
-match &User-Name == "bob"
+condition &request.User-Name == "bob"
+match &request.User-Name == "bob"
-condition &User-Name == `bob`
-match &User-Name == `bob`
+condition &request.User-Name == `bob`
+match &request.User-Name == `bob`
-condition &User-Name == 'bob'
-match &User-Name == 'bob'
+condition &request.User-Name == 'bob'
+match &request.User-Name == 'bob'
-condition &User-Name == bob
-match &User-Name == 'bob'
+condition &request.User-Name == bob
+match &request.User-Name == 'bob'
# Integer (etc.) types must be "bare"
condition &Session-Timeout == 10
-match &Session-Timeout == 10
+match &request.Session-Timeout == 10
# Automatic type inference means this is fine
condition &Session-Timeout == '10'
-match &Session-Timeout == 10
+match &request.Session-Timeout == 10
# Except for dates, which can be humanly readable!
# This one is be an expansion, so it's left as-is.
-condition &Event-Timestamp == "January 1, 2012 %{blah}"
-match &Event-Timestamp == "January 1, 2012 %{blah}"
+condition &request.Event-Timestamp == "January 1, 2012 %{blah}"
+match &request.Event-Timestamp == "January 1, 2012 %{blah}"
# This one is NOT an expansion, so it's parsed into normal form
-condition &Event-Timestamp == 'January 1, 2012'
-#data &Event-Timestamp == 'Jan 1 2012 00:00:00 EST'
+condition &request.Event-Timestamp == 'January 1, 2012'
+#data &request.Event-Timestamp == 'Jan 1 2012 00:00:00 EST'
# literals are parsed when the conditions are parsed
condition <integer>X == 1
condition <ipaddr>127.0.0.1 == %{md4:127.0.0.1}
match 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}'}
+condition <ipaddr>127.0.0.1 == %{md4: SELECT user FROM table WHERE user='%{request.User-Name}'}
+match 127.0.0.1 == %{md4: SELECT user FROM table WHERE user='%{request.User-Name}'}
condition <ether> 00:11:22:33:44:55 == "00:11:22:33:44:55"
match true
condition 0
match false
-condition true && (&User-Name == "bob")
-match &User-Name == "bob"
+condition true && (&request.User-Name == "bob")
+match &request.User-Name == "bob"
-condition false && (&User-Name == "bob")
+condition false && (&request.User-Name == "bob")
match false
-condition false || (&User-Name == "bob")
-match &User-Name == "bob"
+condition false || (&request.User-Name == "bob")
+match &request.User-Name == "bob"
-condition true || (&User-Name == "bob")
+condition true || (&request.User-Name == "bob")
match true
#
# a double-quoted string. Except that there's no '%'
# in it, so it reverts back to a literal.
#
-condition (&User-Name == "bob")
-match &User-Name == "bob"
+condition (&request.User-Name == "bob")
+match &request.User-Name == "bob"
-condition (&User-Name == "%{md4: blah}")
-match &User-Name == "%{md4: blah}"
+condition (&request.User-Name == "%{md4: blah}")
+match &request.User-Name == "%{md4: blah}"
condition <ipaddr>127.0.0.1 == 2130706433
match true
match `a`
condition (&User-name)
-match &User-Name
+match &request.User-Name
#
# Forbidden data types in cast
#
-condition (<vsa>"foo" == &User-Name)
+condition (<vsa>"foo" == &request.User-Name)
match ERROR offset 3: Forbidden data type 'vsa' in cast
#
# of the same type, then re-write it so that the attribute
# is on the LHS of the condition.
#
-condition <string>"foo" == &User-Name
-match &User-Name == "foo"
+condition <string>"foo" == &request.User-Name
+match &request.User-Name == "foo"
# This used to be expr, but expr isn't a builtin, so it failed...
-condition <integer>"%{md4: 1 + 1}" < &NAS-Port
-match &NAS-Port > <uint32>"%{md4: 1 + 1}"
+condition <integer>"%{md4: 1 + 1}" < &request.NAS-Port
+match &request.NAS-Port > <uint32>"%{md4: 1 + 1}"
#
# The string gets parsed as an IP address.
#
-condition &Filter-Id == &Framed-IP-Address
-match <ipaddr>&Filter-Id == &Framed-IP-Address
+condition &request.Filter-Id == &request.Framed-IP-Address
+match <ipaddr>&request.Filter-Id == &request.Framed-IP-Address
-condition <ipaddr>127.0.0.1 == &Filter-Id
-match <ipaddr>&Filter-Id == 127.0.0.1
+condition <ipaddr>127.0.0.1 == &request.Filter-Id
+match <ipaddr>&request.Filter-Id == 127.0.0.1
-condition &Tmp-uint64-0 == &request.Foo-Stuff-Bar
-match &Tmp-uint64-0 == &Foo-Stuff-Bar
+condition &request.Tmp-uint64-0 == &request.Foo-Stuff-Bar
+match &request.Tmp-uint64-0 == &request.Foo-Stuff-Bar
-condition &Tmp-uint64-0 == &reply.Foo-Stuff-Bar
-match &Tmp-uint64-0 == &reply.Foo-Stuff-Bar
+condition &request.Tmp-uint64-0 == &reply.Foo-Stuff-Bar
+match &request.Tmp-uint64-0 == &reply.Foo-Stuff-Bar
#
# Casting attributes of different size
#
-condition <ipaddr>&Tmp-uint64-0 == &Framed-IP-Address
+condition <ipaddr>&request.Tmp-uint64-0 == &request.Framed-IP-Address
match ERROR offset 9: Cannot cast type 'uint64' to 'ipaddr'
#
# if the prefix is /32. We don't know enough at compile time,
# so this may be a run-time failure.
#
-condition <ipaddr>&PMIP6-Home-IPv4-HoA == &Framed-IP-Address
-match <ipaddr>&PMIP6-Home-IPv4-HoA == &Framed-IP-Address
+condition <ipaddr>&request.PMIP6-Home-IPv4-HoA == &request.Framed-IP-Address
+match <ipaddr>&request.PMIP6-Home-IPv4-HoA == &request.Framed-IP-Address
# but these are allowed
-condition <ether>&Tmp-uint64-0 == "%{module: foo}"
-match <ether>&Tmp-uint64-0 == "%{module: foo}"
+condition <ether>&request.Tmp-uint64-0 == "%{module: foo}"
+match <ether>&request.Tmp-uint64-0 == "%{module: foo}"
-condition <ipaddr>&Filter-Id == &Framed-IP-Address
-match <ipaddr>&Filter-Id == &Framed-IP-Address
+condition <ipaddr>&request.Filter-Id == &request.Framed-IP-Address
+match <ipaddr>&request.Filter-Id == &request.Framed-IP-Address
-condition <ipaddr>&Class == &Framed-IP-Address
-match <ipaddr>&Class == &Framed-IP-Address
+condition <ipaddr>&request.Class == &request.Framed-IP-Address
+match <ipaddr>&request.Class == &request.Framed-IP-Address
#
# zero offset into arrays get parsed and ignored
#
-condition &User-Name[0] == "bob"
-match &User-Name[0] == "bob"
+condition &request.User-Name[0] == "bob"
+match &request.User-Name[0] == "bob"
-condition &User-Name[1] == "bob"
-match &User-Name[1] == "bob"
+condition &request.User-Name[1] == "bob"
+match &request.User-Name[1] == "bob"
-condition &User-Name[n] == "bob"
-match &User-Name[n] == "bob"
+condition &request.User-Name[n] == "bob"
+match &request.User-Name[n] == "bob"
#
# This is allowed for pass2-fixups. Foo-Bar MAY be an attribute.
# If so allow it so that pass2 can fix it up. Until then,
# it's an unknown attribute
#
-condition &Foo-Bar
-match &Foo-Bar
+condition &request.Foo-Bar
+match &request.Foo-Bar
# Same types are optimized
#
# FIXME: the tests don't currently run the "pass2" checks.
# This test should really be:
#
-# data &Acct-Input-Octets > &Session-Timeout
+# data &request.Acct-Input-Octets > &request.Session-Timeout
#
-condition &Acct-Input-Octets > "%{Session-Timeout}"
-match &Acct-Input-Octets > "%{Session-Timeout}"
+condition &request.Acct-Input-Octets > "%{request.Session-Timeout}"
+match &request.Acct-Input-Octets > "%{request.Session-Timeout}"
# Separate types aren't optimized
-condition &Acct-Input-Octets-64 > "%{Session-Timeout}"
-match &Acct-Input-Octets-64 > "%{Session-Timeout}"
+condition &request.Acct-Input-Octets-64 > "%{request.Session-Timeout}"
+match &request.Acct-Input-Octets-64 > "%{request.Session-Timeout}"
#
# Parse OIDs into known attributes, where possible.
#
condition &26.24757.84.9.5.4 == 0x1a99
-match &Vendor-Specific.WiMAX.Packet-Flow-Descriptor-v2.Classifier.Src-Spec.Port == 6809
+match &request.Vendor-Specific.WiMAX.Packet-Flow-Descriptor-v2.Classifier.Src-Spec.Port == 6809
#
# This OID is known, but the data is malformed.
# Allow it so that we can look for malformed attributes
# in packets.
#
-condition &raw.26.24757.84.9.5.7 == 0x1a99
-match &raw.Vendor-Specific.WiMAX.Packet-Flow-Descriptor-v2.Classifier.Src-Spec.Assigned == 0x1a99
+condition &raw.request.26.24757.84.9.5.7 == 0x1a99
+match &raw.request.Vendor-Specific.WiMAX.Packet-Flow-Descriptor-v2.Classifier.Src-Spec.Assigned == 0x1a99
# This one is really unknown
-condition &26.24757.84.9.5.15 == 0x1a99
-match &Vendor-Specific.WiMAX.Packet-Flow-Descriptor-v2.Classifier.Src-Spec.15 == 0x1a99
+condition &request.26.24757.84.9.5.15 == 0x1a99
+match &request.Vendor-Specific.WiMAX.Packet-Flow-Descriptor-v2.Classifier.Src-Spec.15 == 0x1a99
#
# Invalid array references.
#
-condition &User-Name[a] == 'bob'
-match ERROR offset 12: Invalid array index
+condition &request.User-Name[a] == 'bob'
+match ERROR offset 20: Invalid array index
-condition &User-Name == &Filter-Id[a]
-match ERROR offset 26: Invalid array index
+condition &request.User-Name == &request.Filter-Id[a]
+match ERROR offset 42: Invalid array index
#
# Bounds checks...
#
-condition &User-Name[1001] == 'bob'
-match ERROR offset 12: Invalid array index '1001' (should be between 0-1000)
+condition &request.User-Name[1001] == 'bob'
+match ERROR offset 20: Invalid array index '1001' (should be between 0-1000)
-condition &User-Name[-1] == 'bob'
-match ERROR offset 12: Invalid array index '-1' (should be between 0-1000)
+condition &request.User-Name[-1] == 'bob'
+match ERROR offset 20: Invalid array index '-1' (should be between 0-1000)
#
# Sometimes the attribute/condition parser needs to fallback to bare words
match ERROR offset 12: Unexpected text after attribute reference
# . is a valid dictionary name attribute, so we can't error out in pass1
-condition ¬-a-packet.User-Name == ¬-a-packet.User-Name
-match ¬-a-packet.User-Name == ¬-a-packet.User-Name
+condition &request.not-a-packet.User-Name == &request.not-a-packet.User-Name
+match &request.not-a-packet.User-Name == &request.not-a-packet.User-Name
#
# The LHS is a string with ASCII 5C 30 30 30 inside of it vs the RHS which should contain ASCII 0.
#
# 'Unknown' attributes which are defined in the main dictionary
# should be resolved to their real names.
-condition &1 == 0x616263
-match &User-Name == 'abc'
+condition &request.1 == 0x616263
+match &request.User-Name == 'abc'
-condition &26.11344.1 == 0x7f000001
-match &Vendor-Specific.FreeRADIUS.Proxied-To == 127.0.0.1
+condition &request.26.11344.1 == 0x7f000001
+match &request.Vendor-Specific.FreeRADIUS.Proxied-To == 127.0.0.1
#
# Escape the backslashes correctly
# And print them correctly
#
-condition &User-Name == '\\'
-match &User-Name == '\\'
+condition &request.User-Name == '\\'
+match &request.User-Name == '\\'
-condition &User-Name == "@|\\"
-match &User-Name == "@|\\"
+condition &request.User-Name == "@|\\"
+match &request.User-Name == "@|\\"
-condition &User-Name != "foo\nbar"
-match !&User-Name == "foo\nbar"
+condition &request.User-Name != "foo\nbar"
+match !&request.User-Name == "foo\nbar"
#
# We infer that the LHS is a prefix and the RHS is
condition <ipv4prefix>192.168.0.0/16 > 192.168.1.2
match true
-condition <ipv4prefix>&NAS-IP-Address == 192.168.0.0/24
-match <ipv4prefix>&NAS-IP-Address == 192.168.0.0/24
+condition <ipv4prefix>&request.NAS-IP-Address == 192.168.0.0/24
+match <ipv4prefix>&request.NAS-IP-Address == 192.168.0.0/24
#
# rewrite so that the attribute is on the LHS
# and, move the cast to the attribute, as the RHS
# is parsed as ipv4prefix
#
-condition <ipv4prefix>192.168.0.0/24 > &NAS-IP-Address
-match <ipv4prefix>&NAS-IP-Address < 192.168.0.0/24
+condition <ipv4prefix>192.168.0.0/24 > &request.NAS-IP-Address
+match <ipv4prefix>&request.NAS-IP-Address < 192.168.0.0/24
#
# This is allowed and means "the list is not empty"
#
condition (&reply.)
-match &reply
+match &reply.
#
# Expansions of environment variables
# Attributes with a protocol namespace
#
condition &radius.User-Name == 'bob'
-match &User-Name == 'bob'
+match &request.User-Name == 'bob'
condition !(!(0))
match false
#
# More short-circuit evaluations
#
-condition (&User-Name == "bob") && (false)
+condition (&request.User-Name == "bob") && (false)
match false
-condition (&User-Name == "bob") || (true)
+condition (&request.User-Name == "bob") || (true)
match true
#
# 0 && (1 || 1) = 0 && 1 == 0
# (0 && 1) || 1 = 0 || 1 == 1
#
-condition (&User-Name == "bob") && ((&User-Password == "bob") || &EAP-Message)
-match (&User-Name == "bob") && ((&User-Password == "bob") || &EAP-Message)
+condition (&request.User-Name == "bob") && ((&request.User-Password == "bob") || &request.EAP-Message)
+match (&request.User-Name == "bob") && ((&request.User-Password == "bob") || &request.EAP-Message)
count
match 307
tmpl-rules allow_unresolved=yes allow_unknown=yes
# Forcefully cast RHS bareword
-condition &User-Name == <ipaddr>192.168.0.1
-match <ipaddr>&User-Name == 192.168.0.1
+condition &request.User-Name == <ipaddr>192.168.0.1
+match <ipaddr>&request.User-Name == 192.168.0.1
# Forcefully cast LHS bareword
-condition <ipaddr>192.168.0.1 == &User-Name
-match <ipaddr>&User-Name == 192.168.0.1
+condition <ipaddr>192.168.0.1 == &request.User-Name
+match <ipaddr>&request.User-Name == 192.168.0.1
# Forcefully cast RHS single quotes
-#condition &Framed-IP-Address == <ipaddr>'192.168.0.1'
-#match &Framed-IP-Address == <ipaddr>'192.168.0.1'
+#condition &request.Framed-IP-Address == <ipaddr>'192.168.0.1'
+#match &request.Framed-IP-Address == <ipaddr>'192.168.0.1'
# Forcefully cast LHS single quotes
-#condition <ipaddr>'192.168.0.1' == &Framed-IP-Address
-#match <ipaddr>'192.168.0.1' == &Framed-IP-Address
+#condition <ipaddr>'192.168.0.1' == &request.Framed-IP-Address
+#match <ipaddr>'192.168.0.1' == &request.Framed-IP-Address
# Forcefully cast RHS double quotes
-#condition &User-Name == <ipaddr>"192.168.0.1"
-#match &User-Name == <ipaddr>"192.168.0.1"
+#condition &request.User-Name == <ipaddr>"192.168.0.1"
+#match &request.User-Name == <ipaddr>"192.168.0.1"
# Forcefully cast LHS single quotes
-#condition <ipaddr>"192.168.0.1" == &User-Name
-#match <ipaddr>"192.168.0.1" == &User-Name
+#condition <ipaddr>"192.168.0.1" == &request.User-Name
+#match <ipaddr>"192.168.0.1" == &request.User-Name
count
match 6
proto-dictionary dhcpv6
tmpl-rules allow_unresolved=no allow_unknown=no
-condition &IA-NA.Options.Tmp-String-0 == 'foo'
-match &IA-NA.Options.Tmp-String-0 == 'foo'
+condition &request.IA-NA.Options.Tmp-String-0 == 'foo'
+match &request.IA-NA.Options.Tmp-String-0 == 'foo'
# Seen in the wild. Array subscript on child produces an error
-condition &IA-NA.Options.IA-Addr[0].IPv6-Address == ::
-match &IA-NA.Options.IA-Addr[0].IPv6-Address == ::
+condition &request.IA-NA.Options.IA-Addr[0].IPv6-Address == ::
+match &request.IA-NA.Options.IA-Addr[0].IPv6-Address == ::
count
match 6
proto-dictionary radius
# Check wildcards are OK
-condition &Packet-Src-IPv6-Address == ::
-match &Packet-Src-IPv6-Address == ::
+condition &request.Packet-Src-IPv6-Address == ::
+match &request.Packet-Src-IPv6-Address == ::
-condition &Packet-Src-IP-Address == <ipaddr>*
-match &Packet-Src-IP-Address == 0.0.0.0
+condition &request.Packet-Src-IP-Address == <ipaddr>*
+match &request.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
+condition &request.Packet-Src-IP-Address == 0.0.0.0
+match &request.Packet-Src-IP-Address == 0.0.0.0
proto-dictionary radius
# A nested attribute with partial path
-condition &Vendor-Specific.WiMAX.Packet-Flow-Descriptor.Uplink-QOS-Id
-match &Vendor-Specific.WiMAX.Packet-Flow-Descriptor.Uplink-QOS-Id
+condition &request.Vendor-Specific.WiMAX.Packet-Flow-Descriptor.Uplink-QOS-Id
+match &request.Vendor-Specific.WiMAX.Packet-Flow-Descriptor.Uplink-QOS-Id
# A nested attribute with a full path (and no filters)
-condition &Vendor-Specific.WiMAX.Packet-Flow-Descriptor.Uplink-QOS-Id
-match &Vendor-Specific.WiMAX.Packet-Flow-Descriptor.Uplink-QOS-Id
+condition &request.Vendor-Specific.WiMAX.Packet-Flow-Descriptor.Uplink-QOS-Id
+match &request.Vendor-Specific.WiMAX.Packet-Flow-Descriptor.Uplink-QOS-Id
# A nested attribute with an instance selector
-condition &Vendor-Specific.WiMAX.Packet-Flow-Descriptor[5].Uplink-QOS-Id
-match &Vendor-Specific.WiMAX.Packet-Flow-Descriptor[5].Uplink-QOS-Id
+condition &request.Vendor-Specific.WiMAX.Packet-Flow-Descriptor[5].Uplink-QOS-Id
+match &request.Vendor-Specific.WiMAX.Packet-Flow-Descriptor[5].Uplink-QOS-Id
# A nested attribute with an instance selector at both levels
-condition &Vendor-Specific.WiMAX.Packet-Flow-Descriptor[*].Uplink-QOS-Id[0]
-match &Vendor-Specific.WiMAX.Packet-Flow-Descriptor[*].Uplink-QOS-Id[0]
+condition &request.Vendor-Specific.WiMAX.Packet-Flow-Descriptor[*].Uplink-QOS-Id[0]
+match &request.Vendor-Specific.WiMAX.Packet-Flow-Descriptor[*].Uplink-QOS-Id[0]
# Unparsed child - We won't know this is an error until pass2
tmpl-rules allow_unresolved=yes
-condition &Unparsed-Child.Uplink-QOS-Id[0]
-match &Unparsed-Child.Uplink-QOS-Id[0]
+condition &request.Unparsed-Child.Uplink-QOS-Id[0]
+match &request.Unparsed-Child.Uplink-QOS-Id[0]
tmpl-rules allow_unresolved=no
# Errors
#
# Malformed filter
-condition &Vendor-Specific.WiMAX.Packet-Flow-Descriptor[*.Uplink-QOS-Id[0]
-match ERROR offset 48: No closing ']' for array index
+condition &request.Vendor-Specific.WiMAX.Packet-Flow-Descriptor[*.Uplink-QOS-Id[0]
+match ERROR offset 56: No closing ']' for array index
# Too many dots, point to the thing that's wrong
-condition &Vendor-Specific.WiMAX.Packet-Flow-Descriptor..Uplink-QOS-Id[0]
-match ERROR offset 47: Zero length attribute name: Unresolved attributes are not allowed here
+condition &request.Vendor-Specific.WiMAX.Packet-Flow-Descriptor..Uplink-QOS-Id[0]
+match ERROR offset 55: Zero length attribute name: Unresolved attributes are not allowed here
# Trailing dots, point to the dot that's an error
-condition &Vendor-Specific.WiMAX.Packet-Flow-Descriptor.Uplink-QOS-Id[0].
-match ERROR offset 63: Parent type of nested attribute Uplink-QOS-Id must be of type "struct", "tlv", "vendor", "vsa" or "group", got "uint8"
+condition &request.Vendor-Specific.WiMAX.Packet-Flow-Descriptor.Uplink-QOS-Id[0].
+match ERROR offset 71: Parent type of nested attribute Uplink-QOS-Id must be of type "struct", "tlv", "vendor", "vsa" or "group", got "uint8"
count
match 19
proto-dictionary radius
-condition &User-Name !~ /^foo\nbar$/
-match !&User-Name =~ /^foo\nbar$/
+condition &request.User-Name !~ /^foo\nbar$/
+match !&request.User-Name =~ /^foo\nbar$/
condition (ok =~ handled)
match ERROR offset 8: Expected regular expression
# And print them correctly
#
-condition &User-Name =~ /@|./
-match &User-Name =~ /@|./
+condition &request.User-Name =~ /@|./
+match &request.User-Name =~ /@|./
-condition &User-Name =~ /@|\\/
-match &User-Name =~ /@|\\/
+condition &request.User-Name =~ /@|\\/
+match &request.User-Name =~ /@|\\/
-condition &User-Name =~ /^([^\\]*)\\(.*)$/
-match &User-Name =~ /^([^\\]*)\\(.*)$/
+condition &request.User-Name =~ /^([^\\]*)\\(.*)$/
+match &request.User-Name =~ /^([^\\]*)\\(.*)$/
#
# Non-integer types get cast to string.
#
-condition &Tmp-Integer-0 =~ /%{Tmp-Integer-0}/
-match <string>&Tmp-Integer-0 =~ /%{Tmp-Integer-0}/
+condition &request.Tmp-Integer-0 =~ /%{request.Tmp-Integer-0}/
+match <string>&request.Tmp-Integer-0 =~ /%{request.Tmp-Integer-0}/
#
# Cannot add a bad cast
#
-condition <integer>&Tmp-String-0 =~ /foo/
+condition <integer>&request.Tmp-String-0 =~ /foo/
match ERROR offset 10: Casts cannot be used with regular expressions
-condition &Tmp-String-0 =~ <integer>/foo/
-match ERROR offset 28: Casts cannot be used with regular expressions
+condition &request.Tmp-String-0 =~ <integer>/foo/
+match ERROR offset 36: Casts cannot be used with regular expressions
xlat %{1}
xlat %{33}
match ERROR offset 3: Invalid regex reference. Must be in range 0-32
-condition &User-Name == /foo/
-match ERROR offset 15: Unexpected regular expression
+condition &request.User-Name == /foo/
+match ERROR offset 23: Unexpected regular expression
count
match 43
--- /dev/null
+proto-dictionary radius
+tmpl-rules allow_unresolved=no allow_unknown=yes
+
+# Our parent is fine
+condition &parent.request.User-Name == &parent.reply.User-Name
+match &parent.request.User-Name == &parent.reply.User-Name
+
+# The parent of our parent is fine
+condition &parent.parent.request.User-Name == &parent.parent.reply.User-Name
+match &parent.parent.request.User-Name == &parent.parent.reply.User-Name
+
+# Current request is fine
+condition ¤t.request.User-Name == ¤t.reply.User-Name
+match ¤t.request.User-Name == ¤t.reply.User-Name
+
+# Current is pretty much meaningless
+condition &parent.current.parent.request.User-Name == &parent.current.parent.reply.User-Name
+match &parent.current.parent.request.User-Name == &parent.current.parent.reply.User-Name
+
+# Outer lists are fine...
+condition &outer.request.User-Name == &outer.reply.User-Name
+match &outer.request.User-Name == &outer.reply.User-Name
+
+# We _can_ navigate to the parent and _then_ the outer request
+condition &parent.outer.request.User-Name == &parent.outer.reply.User-Name
+match &parent.outer.request.User-Name == &parent.outer.reply.User-Name
+
+# We _can_ navigate to the parent and _then the outer request and then the current request
+condition &parent.outer.current.request.User-Name == &parent.outer.current.reply.User-Name
+match &parent.outer.current.request.User-Name == &parent.outer.current.reply.User-Name
+
+# But we can't go above the outer request
+condition &outer.parent.User-Name == &outer.parent.reply.User-Name
+match ERROR offset 14: "parent" qualifiers not allowed after "outer" qualifier
+
+# ...and we can't have multiple outers
+condition &outer.outer.request.User-Name == &outer.outer.request.User-Name
+match ERROR offset 13: Duplicate "outer" request qualifiers not allowed
+
+# If we don't have a default list, for now, we should default to request, this may change later
+# when we fully implement local variables.
+condition &outer.User-Name == &outer.User-Name
+match &outer.request.User-Name == &outer.request.User-Name
\ No newline at end of file
#
# Literals mixed with attributes
#
-xlat literal%{User-Password}
-match literal%{User-Password}
+xlat literal%{request.User-Password}
+match literal%{request.User-Password}
-xlat {literal}%{User-Password}
-match {literal}%{User-Password}
+xlat {literal}%{request.User-Password}
+match {literal}%{request.User-Password}
-xlat literal%{User-Password}{literal}
-match literal%{User-Password}{literal}
+xlat literal%{request.User-Password}{literal}
+match literal%{request.User-Password}{literal}
-xlat {literal%{User-Password}literal}
-match {literal%{User-Password}literal}
+xlat {literal%{request.User-Password}literal}
+match {literal%{request.User-Password}literal}
-xlat \%{literal%{User-Password}literal}
-match \%{literal%{User-Password}literal}
+xlat \%{literal%{request.User-Password}literal}
+match \%{literal%{request.User-Password}literal}
-xlat literal%{%{User-Password}:-literal}
-match literal%{%{User-Password}:-literal}
+xlat literal%{%{request.User-Password}:-literal}
+match literal%{%{request.User-Password}:-literal}
-xlat literal%{%{User-Password}:-\%{lit}eral}
-match literal%{%{User-Password}:-\%{lit}eral}
+xlat literal%{%{request.User-Password}:-\%{lit}eral}
+match literal%{%{request.User-Password}:-\%{lit}eral}
-xlat literal%{%{User-Password}:-%{User-Name}\%literal}
-match literal%{%{User-Password}:-%{User-Name}\%literal}
+xlat literal%{%{request.User-Password}:-%{request.User-Name}\%literal}
+match literal%{%{request.User-Password}:-%{request.User-Name}\%literal}
-xlat literal%{%{User-Password}:-\%literal}
-match literal%{%{User-Password}:-\%literal}
+xlat literal%{%{request.User-Password}:-\%literal}
+match literal%{%{request.User-Password}:-\%literal}
#
# Regex capture groups
#
#
# Tests for xlat expansion
#
-xlat %{Tunnel-Password}
-match %{Tunnel-Password}
+xlat %{request.Tunnel-Password}
+match %{request.Tunnel-Password}
xlat %{test:bar}
match %{test:bar}
-xlat %{Tunnel-Password}
-match %{Tunnel-Password}
+xlat %{request.Tunnel-Password}
+match %{request.Tunnel-Password}
-xlat %{reply.Tunnel-Password}
-match %{reply.Tunnel-Password}
+xlat %{reply.request.Tunnel-Password}
+match %{reply.request.Tunnel-Password}
-xlat %{User-Name[3]}
-match %{User-Name[3]}
+xlat %{request.User-Name[3]}
+match %{request.User-Name[3]}
-xlat %{User-Name[*]}
-match %{User-Name[*]}
+xlat %{request.User-Name[*]}
+match %{request.User-Name[*]}
-xlat %{User-Name[#]}
-match %{User-Name[#]}
+xlat %{request.User-Name[#]}
+match %{request.User-Name[#]}
-xlat %{User-Name[n]}
-match %{User-Name[n]}
+xlat %{request.User-Name[n]}
+match %{request.User-Name[n]}
xlat %{request.User-Name[3]}
-match %{User-Name[3]}
+match %{request.User-Name[3]}
xlat %{request.User-Name[*]}
-match %{User-Name[*]}
+match %{request.User-Name[*]}
xlat %{request.User-Name[#]}
-match %{User-Name[#]}
+match %{request.User-Name[#]}
-xlat %{Vendor-Specific.3GPP.SGSN-Address}
-match %{Vendor-Specific.3GPP.SGSN-Address}
+xlat %{request.Vendor-Specific.3GPP.SGSN-Address}
+match %{request.Vendor-Specific.3GPP.SGSN-Address}
xlat \"Hello %S goo\"
match \"Hello %S goo\"
# 3GPP stuff
#
xlat \"%{request.Vendor-Specific.3GPP.IMSI}\"
-match \"%{Vendor-Specific.3GPP.IMSI}\"
+match \"%{request.Vendor-Specific.3GPP.IMSI}\"
xlat \"%{reply.Vendor-Specific.3GPP.IMSI}\"
match \"%{reply.Vendor-Specific.3GPP.IMSI}\"
xlat \"%{reply.Vendor-Specific.3GPP.IMSI[2]}\"
match \"%{reply.Vendor-Specific.3GPP.IMSI[2]}\"
-xlat /([A-Z0-9\\-]*)_%{Calling-Station-Id}/
-match /([A-Z0-9\\-]*)_%{Calling-Station-Id}/
+xlat /([A-Z0-9\\-]*)_%{request.Calling-Station-Id}/
+match /([A-Z0-9\\-]*)_%{request.Calling-Station-Id}/
xlat %(length:)
match %(length:)
match \"%t\tfoo\"
allow-unresolved yes
-xlat \"%t\t%{Client-IP-Address}\"
-match \"%t\t%{Client-IP-Address}\"
+xlat \"%t\t%{request.Client-IP-Address}\"
+match \"%t\t%{request.Client-IP-Address}\"
allow-unresolved no
xlat \"foo %{test:foo}\"
xlat %{%{foo}:-%{bar}}
match ERROR offset 5: Unresolved attributes not allowed in expansions here
-xlat %{%{User-Name}:-%{bar}}
-match ERROR offset 19: Unresolved attributes not allowed in expansions here
+xlat %{%{request.User-Name}:-%{bar}}
+match ERROR offset 27: Unresolved attributes not allowed in expansions here
-xlat %{%{User-Name}:-bar}
-match %{%{User-Name}:-bar}
+xlat %{%{request.User-Name}:-bar}
+match %{%{request.User-Name}:-bar}
-xlat foo %{%{User-Name}:-bar} baz
-match foo %{%{User-Name}:-bar} baz
+xlat foo %{%{request.User-Name}:-bar} baz
+match foo %{%{request.User-Name}:-bar} baz
-xlat %{%{test:bar}:-%{User-Name}}
-match %{%{test:bar}:-%{User-Name}}
+xlat %{%{test:bar}:-%{request.User-Name}}
+match %{%{test:bar}:-%{request.User-Name}}
-xlat %{%{test:bar}:-%{%{User-Name}:-bar}}
-match %{%{test:bar}:-%{%{User-Name}:-bar}}
+xlat %{%{test:bar}:-%{%{request.User-Name}:-bar}}
+match %{%{test:bar}:-%{%{request.User-Name}:-bar}}
-xlat %{%{User-Name}:-}
-match %{%{User-Name}:-}
+xlat %{%{request.User-Name}:-}
+match %{%{request.User-Name}:-}
-xlat %{%{Operator-Name}:-}
-match %{%{Operator-Name}:-}
+xlat %{%{request.Operator-Name}:-}
+match %{%{request.Operator-Name}:-}
-xlat %{%{%{User-Name}:-foo}:-bar}
-match %{%{%{User-Name}:-foo}:-bar}
+xlat %{%{%{request.User-Name}:-foo}:-bar}
+match %{%{%{request.User-Name}:-foo}:-bar}
-xlat %{%{%{User-Name}:-foo}:-%{%{test:bar}:-%{User-Name}}}
-match %{%{%{User-Name}:-foo}:-%{%{test:bar}:-%{User-Name}}}
+xlat %{%{%{request.User-Name}:-foo}:-%{%{test:bar}:-%{request.User-Name}}}
+match %{%{%{request.User-Name}:-foo}:-%{%{test:bar}:-%{request.User-Name}}}
xlat %{:-}
match ERROR offset 3: First item in alternation cannot be empty
-xlat %{:-%{User-Name}}
+xlat %{:-%{request.User-Name}}
match ERROR offset 3: First item in alternation cannot be empty
xlat %{%{}:-}
xlat %{%{}:-foo}
match ERROR offset 5: Empty expression is invalid
-xlat %{%{User-Name}:-
-match ERROR offset 17: Missing closing brace
+xlat %{%{request.User-Name}:-
+match ERROR offset 25: Missing closing brace
# Discuss - Not sure the offset/message is correct here, but not sure if we can determine the correct offset either
-xlat %{%{User-Name}:-foo
-match ERROR offset 20: Missing closing brace
+xlat %{%{request.User-Name}:-foo
+match ERROR offset 28: Missing closing brace
-xlat %{%{User-Name}:}
-match ERROR offset 15: Expected ':-' after first expansion
+xlat %{%{request.User-Name}:}
+match ERROR offset 23: Expected ':-' after first expansion
-xlat %{%{User-Name}}
-match ERROR offset 15: Expected ':-' after first expansion
+xlat %{%{request.User-Name}}
+match ERROR offset 23: Expected ':-' after first expansion
#
xlat %{test:
match ERROR offset 8: Missing closing brace
-xlat %{test:%{User-Name
-match ERROR offset 19: Missing closing brace
+xlat %{test:%{request.User-Name
+match ERROR offset 27: Missing closing brace
# Discuss - Not sure the offset/message is correct here, but not sure if we can determine the correct offset either
-xlat %{test:%{User-Name}
-match ERROR offset 20: Missing closing brace
+xlat %{test:%{request.User-Name}
+match ERROR offset 28: Missing closing brace
xlat %{myfirstxlat
match ERROR offset 14: Missing closing brace
#
# API to split xlat strings into argv-style arguments.
#
-xlat_argv /bin/sh %{User-Name}
-match [0]{ /bin/sh }, [1]{ %{User-Name} }
+xlat_argv /bin/sh %{request.User-Name}
+match [0]{ /bin/sh }, [1]{ %{request.User-Name} }
-xlat_argv /bin/sh "%{User-Name}"
-match [0]{ /bin/sh }, [1]{ %{User-Name} }
+xlat_argv /bin/sh "%{request.User-Name}"
+match [0]{ /bin/sh }, [1]{ %{request.User-Name} }
# with quotes
-xlat_argv /bin/sh "foo" "%{User-Name}"
-match [0]{ /bin/sh }, [1]{ foo }, [2]{ %{User-Name} }
+xlat_argv /bin/sh "foo" "%{request.User-Name}"
+match [0]{ /bin/sh }, [1]{ foo }, [2]{ %{request.User-Name} }
# many spaces between arguments
-xlat_argv /bin/sh "foo" "%{User-Name}"
-match [0]{ /bin/sh }, [1]{ foo }, [2]{ %{User-Name} }
+xlat_argv /bin/sh "foo" "%{request.User-Name}"
+match [0]{ /bin/sh }, [1]{ foo }, [2]{ %{request.User-Name} }
# and spaces in quotes
-xlat_argv /bin/sh "foo bar" "%{User-Name}"
-match [0]{ /bin/sh }, [1]{ foo bar }, [2]{ %{User-Name} }
+xlat_argv /bin/sh "foo bar" "%{request.User-Name}"
+match [0]{ /bin/sh }, [1]{ foo bar }, [2]{ %{request.User-Name} }
# and multiple xlats inside of quotes
-xlat_argv /bin/sh "foo bar" "%{User-Name} %{Filter-Id}"
-match [0]{ /bin/sh }, [1]{ foo bar }, [2]{ %{User-Name} %{Filter-Id} }
+xlat_argv /bin/sh "foo bar" "%{request.User-Name} %{Filter-Id}"
+match [0]{ /bin/sh }, [1]{ foo bar }, [2]{ %{request.User-Name} %{request.Filter-Id} }
# and errors
-xlat_argv /bin/sh "foo bar" "%{User-Name} %{Filter-Id"
-match ERROR offset 45: Missing closing brace
+xlat_argv /bin/sh "foo bar" "%{request.User-Name} %{Filter-Id"
+match ERROR offset 53: Missing closing brace
# and text immediately after a variable expansion
xlat_argv echo hello %{Tmp-String-0}:1234 world
-match [0]{ echo }, [1]{ hello }, [2]{ %{Tmp-String-0}:1234 }, [3]{ world }
+match [0]{ echo }, [1]{ hello }, [2]{ %{request.Tmp-String-0}:1234 }, [3]{ world }
xlat %(debug: 5)
match %(debug: 5)
#
# This is correct.
#
-xlat %(rpad:&User-Name 5 x)
-match %(rpad:&User-Name 5 x)
+xlat %(rpad:&request.User-Name 5 x)
+match %(rpad:&request.User-Name 5 x)
#
# The second argument should be an integer.
# @todo - we don't currently track string offsets for intermediate nodes,
# so the "offset 23" is wrong. It also doesn't say *which* string is wrong. We'll fix that later.
#
-xlat %(rpad:&User-Name foo x)
-match ERROR offset 24: Failed parsing argument 1 as type 'uint64'
+xlat %(rpad:&request.User-Name foo x)
+match ERROR offset 32: Failed parsing argument 1 as type 'uint64'
count
match 187
match (&Packet-Type == Access-Challenge)
xlat_purify &reply == &request
-match ERROR offset 1: Cannot use list references in condition
+match ERROR offset 1: Cannot compare structural types
xlat_purify &reply == "hello"
-match ERROR offset 1: Cannot use list references in condition
+match ERROR offset 1: Cannot compare structural types
xlat_purify "hello" == &reply
-match ERROR offset 12: Cannot use list references in condition
+match ERROR offset 12: Cannot compare structural types
#
#
# @todo - yuck. Suppress full path?
#
-xlat_purify &Event-Timestamp == "January 1, 2012 %{User-Name}"
-match (&Event-Timestamp == %(cast:string "January 1, 2012 %{request[0].User-Name}"))
+xlat_purify &Event-Timestamp == "January 1, 2012 %{request.User-Name}"
+match (&Event-Timestamp == %(cast:string "January 1, 2012 %{request.User-Name}"))
# This one is NOT an expansion, so it's parsed into normal form
xlat_purify &Event-Timestamp == 'January 1 2012'
match NULL
# @todo - yuck, don't print full path?
-xlat_purify <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='%{request[0].User-Name}'})
+xlat_purify <ipaddr>127.0.0.1 == %{md4: SELECT user FROM table WHERE user='%{request.User-Name}'}
+match (127.0.0.1 == %{md4: SELECT user FROM table WHERE user='%{request.User-Name}'})
xlat_purify <ether> 00:11:22:33:44:55 == "00:11:22:33:44:55"
match true
#xlat_purify &Foo-Bar
#match &Foo-Bar
-xlat_purify &Acct-Input-Octets > "%{Session-Timeout}"
-match (&Acct-Input-Octets > %(cast:string "%{request[0].Session-Timeout}"))
+xlat_purify &Acct-Input-Octets > "%{request.Session-Timeout}"
+match (&Acct-Input-Octets > %(cast:string "%{request.Session-Timeout}"))
xlat_purify &Acct-Input-Octets > &Session-Timeout
match (&Acct-Input-Octets > &Session-Timeout)