]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Support multiple levels of request qualifiers
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Fri, 24 Jun 2022 18:51:35 +0000 (13:51 -0500)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Fri, 24 Jun 2022 18:52:00 +0000 (13:52 -0500)
src/bin/unit_test_attribute.c
src/lib/server/map.c
src/lib/server/map_async.c
src/lib/server/tmpl.h
src/lib/server/tmpl_dcursor.c
src/lib/server/tmpl_eval.c
src/lib/server/tmpl_tokenize.c
src/lib/server/users_file.c
src/lib/unlang/compile.c
src/lib/unlang/xlat_tokenize.c
src/tests/keywords/subrequest-request-qualifiers [new file with mode: 0644]

index 50a4ae27edeac4c58a9a2f8197ca056aa1d8af2f..e038d9bc6782b45762f7a1b067a7c36a5ef1403d 100644 (file)
@@ -2417,9 +2417,9 @@ static size_t command_touch(command_result_t *result, UNUSED command_file_ctx_t
 /** Callback for a tmpl rule parser
  *
  */
-typedef ssize_t(*command_tmpl_rule_func)(tmpl_rules_t *rules, fr_sbuff_t *value);
+typedef ssize_t(*command_tmpl_rule_func)(TALLOC_CTX *ctx, tmpl_rules_t *rules, fr_sbuff_t *value);
 
-static ssize_t command_tmpl_rule_allow_foreign(tmpl_rules_t *rules, fr_sbuff_t *value)
+static ssize_t command_tmpl_rule_allow_foreign(UNUSED TALLOC_CTX *ctx, tmpl_rules_t *rules, fr_sbuff_t *value)
 {
        bool res;
        ssize_t slen;
@@ -2429,7 +2429,7 @@ static ssize_t command_tmpl_rule_allow_foreign(tmpl_rules_t *rules, fr_sbuff_t *
        return slen;
 }
 
-static ssize_t command_tmpl_rule_allow_unknown(tmpl_rules_t *rules, fr_sbuff_t *value)
+static ssize_t command_tmpl_rule_allow_unknown(UNUSED TALLOC_CTX *ctx, tmpl_rules_t *rules, fr_sbuff_t *value)
 {
        bool res;
        ssize_t slen;
@@ -2439,7 +2439,7 @@ static ssize_t command_tmpl_rule_allow_unknown(tmpl_rules_t *rules, fr_sbuff_t *
        return slen;
 }
 
-static ssize_t command_tmpl_rule_allow_unresolved(tmpl_rules_t *rules, fr_sbuff_t *value)
+static ssize_t command_tmpl_rule_allow_unresolved(UNUSED TALLOC_CTX *ctx, tmpl_rules_t *rules, fr_sbuff_t *value)
 {
        bool res;
        ssize_t slen;
@@ -2449,7 +2449,7 @@ static ssize_t command_tmpl_rule_allow_unresolved(tmpl_rules_t *rules, fr_sbuff_
        return slen;
 }
 
-static ssize_t command_tmpl_rule_attr_parent(tmpl_rules_t *rules, fr_sbuff_t *value)
+static ssize_t command_tmpl_rule_attr_parent(UNUSED TALLOC_CTX *ctx, tmpl_rules_t *rules, fr_sbuff_t *value)
 {
        return fr_dict_attr_by_oid_substr(NULL,
                                          &rules->attr.parent,
@@ -2458,7 +2458,7 @@ static ssize_t command_tmpl_rule_attr_parent(tmpl_rules_t *rules, fr_sbuff_t *va
                                          value, NULL);
 }
 
-static ssize_t command_tmpl_rule_disallow_internal(tmpl_rules_t *rules, fr_sbuff_t *value)
+static ssize_t command_tmpl_rule_disallow_internal(UNUSED TALLOC_CTX *ctx, tmpl_rules_t *rules, fr_sbuff_t *value)
 {
        bool res;
        ssize_t slen;
@@ -2468,7 +2468,7 @@ static ssize_t command_tmpl_rule_disallow_internal(tmpl_rules_t *rules, fr_sbuff
        return slen;
 }
 
-static ssize_t command_tmpl_rule_disallow_qualifiers(tmpl_rules_t *rules, fr_sbuff_t *value)
+static ssize_t command_tmpl_rule_disallow_qualifiers(UNUSED TALLOC_CTX *ctx, tmpl_rules_t *rules, fr_sbuff_t *value)
 {
        bool res;
        ssize_t slen;
@@ -2478,7 +2478,7 @@ static ssize_t command_tmpl_rule_disallow_qualifiers(tmpl_rules_t *rules, fr_sbu
        return slen;
 }
 
-static ssize_t command_tmpl_rule_list_def(tmpl_rules_t *rules, fr_sbuff_t *value)
+static ssize_t command_tmpl_rule_list_def(UNUSED TALLOC_CTX *ctx, tmpl_rules_t *rules, fr_sbuff_t *value)
 {
        ssize_t slen;
 
@@ -2492,13 +2492,16 @@ static ssize_t command_tmpl_rule_list_def(tmpl_rules_t *rules, fr_sbuff_t *value
        return slen;
 }
 
-static ssize_t command_tmpl_rule_request_def(tmpl_rules_t *rules, fr_sbuff_t *value)
+static ssize_t command_tmpl_rule_request_def(TALLOC_CTX *ctx, tmpl_rules_t *rules, fr_sbuff_t *value)
 {
-       ssize_t slen;
-
-       fr_sbuff_out_by_longest_prefix(&slen, &rules->attr.request_def, tmpl_request_ref_table, value, REQUEST_UNKNOWN);
+       fr_slen_t                        slen;
 
-       if (rules->attr.request_def == REQUEST_UNKNOWN) {
+       slen = tmpl_request_ref_list_afrom_substr(ctx, NULL,
+                                                 &rules->attr.request_def,
+                                                 value,
+                                                 NULL,
+                                                 NULL);
+       if (slen < 0) {
                fr_strerror_printf("Invalid request specifier \"%pV\"",
                                   fr_box_strvalue_len(fr_sbuff_current(value), fr_sbuff_remaining(value)));
        }
@@ -2547,7 +2550,7 @@ static size_t command_tmpl_rules(command_result_t *result, command_file_ctx_t *c
 
                fr_sbuff_adv_past_whitespace(&sbuff, SIZE_MAX, NULL);
 
-               if (func(&cc->tmpl_rules, &sbuff) <= 0) RETURN_COMMAND_ERROR();
+               if (func(cc->tmp_ctx, &cc->tmpl_rules, &sbuff) <= 0) RETURN_COMMAND_ERROR();
        }
 
        return fr_sbuff_used(&sbuff);
index 41984470dd1800a3a84260bc2b4c32bec733e8e8..a7d5acd6de0107d0b55c693643b468db6b63999b 100644 (file)
@@ -657,6 +657,7 @@ static int _map_afrom_cs(TALLOC_CTX *ctx, map_list_t *out, map_t *parent, CONF_S
        TALLOC_CTX      *parent_ctx;
 
        tmpl_rules_t    our_lhs_rules = *lhs_rules;     /* Mutable copy of the destination */
+       TALLOC_CTX      *tmp_ctx = NULL;                /* Temporary context for request lists */
 
        /*
         *      The first map has ctx as the parent context.
@@ -671,15 +672,23 @@ static int _map_afrom_cs(TALLOC_CTX *ctx, map_list_t *out, map_t *parent, CONF_S
         */
        cs_list = p = cf_section_name2(cs);
        if (cs_list && (strcmp(cf_section_name1(cs), "update") == 0)) {
-               p += tmpl_request_ref_by_name(&our_lhs_rules.attr.request_def, p, REQUEST_CURRENT);
-               if (our_lhs_rules.attr.request_def == REQUEST_UNKNOWN) {
+               fr_slen_t slen;
+
+               MEM(tmp_ctx = talloc_init_const("tmp"));
+
+               slen = tmpl_request_ref_list_afrom_substr(ctx, NULL, &our_lhs_rules.attr.request_def,
+                                                         &FR_SBUFF_IN(p, strlen(p)), NULL, NULL);
+               if (slen < 0) {
                        cf_log_err(ci, "Default request specified in mapping section is invalid");
+                       talloc_free(tmp_ctx);
                        return -1;
                }
+               p += slen;
 
                our_lhs_rules.attr.list_def = fr_table_value_by_str(pair_list_table, p, PAIR_LIST_UNKNOWN);
                if (our_lhs_rules.attr.list_def == PAIR_LIST_UNKNOWN) {
                        cf_log_err(ci, "Default list \"%s\" specified in mapping section is invalid", p);
+                       talloc_free(tmp_ctx);
                        return -1;
                }
        }
@@ -694,6 +703,7 @@ static int _map_afrom_cs(TALLOC_CTX *ctx, map_list_t *out, map_t *parent, CONF_S
                         *      Free in reverse as successive entries have their
                         *      prececessors as talloc parent contexts
                         */
+                       talloc_free(tmp_ctx);
                        map_list_talloc_reverse_free(out);
                        return -1;
                }
@@ -819,6 +829,7 @@ static int _map_afrom_cs(TALLOC_CTX *ctx, map_list_t *out, map_t *parent, CONF_S
                map_list_insert_tail(out, map);
        }
 
+       talloc_free(tmp_ctx);
        return 0;
 
 }
@@ -969,7 +980,7 @@ int map_afrom_vp(TALLOC_CTX *ctx, map_t **out, fr_pair_t *vp, tmpl_rules_t const
        tmpl_attr_set_leaf_da(map->lhs, vp->da);
        tmpl_attr_set_leaf_num(map->lhs, NUM_ANY);
 
-       tmpl_attr_set_request(map->lhs, rules->attr.request_def);
+       tmpl_attr_set_request_ref(map->lhs, rules->attr.request_def);
        tmpl_attr_set_list(map->lhs, rules->attr.list_def);
 
        tmpl_print(&FR_SBUFF_OUT(buffer, sizeof(buffer)), map->lhs, TMPL_ATTR_REF_PREFIX_YES, NULL);
@@ -1404,7 +1415,6 @@ int map_to_request(request_t *request, map_t const *map, radius_map_getvalue_t f
 
        map_t                   exp_map;
        tmpl_t                  *exp_lhs;
-       tmpl_request_ref_t      request_ref;
        tmpl_pair_list_t        list_ref;
 
        tmpl_dcursor_ctx_t      cc = {};
@@ -1488,11 +1498,9 @@ int map_to_request(request_t *request, map_t const *map, radius_map_getvalue_t f
        }
 
        context = request;
-       request_ref = tmpl_request(map->lhs);
-       if (tmpl_request_ptr(&context, request_ref) < 0) {
-               REDEBUG("Mapping \"%.*s\" -> \"%.*s\" cannot be performed due to invalid request reference \"%s\" in left side of map",
-                       (int)map->rhs->len, map->rhs->name, (int)map->lhs->len, map->lhs->name,
-                       fr_table_str_by_value(tmpl_request_ref_table, request_ref, "<INVALID>"));
+       if (tmpl_request_ptr(&context, tmpl_request(map->lhs)) < 0) {
+               RPEDEBUG("Mapping \"%.*s\" -> \"%.*s\" cannot be performed due to error in left side of map",
+                       (int)map->rhs->len, map->rhs->name, (int)map->lhs->len, map->lhs->name);
                rcode = -2;
                goto finish;
        }
index 771d885cf2166849a3401df2d8adc97c5d133251..3ab991224e6d289861c907f6924220c9f49a6bb7 100644 (file)
@@ -212,14 +212,11 @@ static inline fr_pair_list_t *map_check_src_or_dst(request_t *request, map_t con
 {
        request_t               *context = request;
        fr_pair_list_t          *list;
-       tmpl_request_ref_t      request_ref;
        tmpl_pair_list_t        list_ref;
 
-       request_ref = tmpl_request(src_dst);
-       if (tmpl_request_ptr(&context, request_ref) < 0) {
-               REDEBUG("Mapping \"%.*s\" -> \"%.*s\" cannot be performed due to invalid request reference \"%s\"",
-                       (int)map->rhs->len, map->rhs->name, (int)map->lhs->len, map->lhs->name,
-                       fr_table_str_by_value(tmpl_request_ref_table, request_ref, "<INVALID>"));
+       if (tmpl_request_ptr(&context, tmpl_request(src_dst)) < 0) {
+               RPEDEBUG("Mapping \"%.*s\" -> \"%.*s\" cannot be performed",
+                       (int)map->rhs->len, map->rhs->name, (int)map->lhs->len, map->lhs->name);
                return NULL;
        }
 
index da08345fbbaaefe6a71dc6ec05b0e770a8794849..bcca9d9b5c7b96afe136a9a3a3edf77f44f8876c 100644 (file)
@@ -280,6 +280,11 @@ typedef enum {
        TMPL_ATTR_REF_PREFIX_AUTO                       //!< Attribute refs may have a '&' prefix.
 } tmpl_attr_prefix_t;
 
+/** Define entry and head types for tmpl request references
+ *
+ */
+FR_DLIST_TYPES(tmpl_request_list)
+
 struct tmpl_attr_rules_s {
        fr_dict_t const         *dict_def;              //!< Default dictionary to use
                                                        ///< with unqualified attribute references.
@@ -290,8 +295,18 @@ struct tmpl_attr_rules_s {
                                                        ///< and the presence of any of those qualifiers
                                                        ///< will be treated as an error.
 
-       tmpl_request_ref_t      request_def;            //!< Default request to use with
+       FR_DLIST_HEAD(tmpl_request_list) _CONST *request_def;   //!< Default request to use with
                                                        ///< unqualified attribute references.
+                                                       ///< If NULL the request is assumed to
+                                                       ///< but the current request.
+                                                       ///< Usually this will be one of
+                                                       ///< - tmpl_request_def_current
+                                                       ///< - tmpl_request_def_outer
+                                                       ///< - tmpl_request_def_parent
+                                                       ///< If a custom list needs to be
+                                                       ///< used it should be allocated on
+                                                       ///< the stack and a pointer to it
+                                                       ///< placed here.
 
        tmpl_pair_list_t        list_def;               //!< Default list to use with unqualified
                                                        ///< attribute reference.
@@ -434,11 +449,6 @@ typedef struct {
  */
 FR_DLIST_FUNCS(tmpl_attr_list, tmpl_attr_t, entry)
 
-/** Define entry and head types for tmpl request references
- *
- */
-FR_DLIST_TYPES(tmpl_request_list)
-
 /** An element in a list of request references
  *
  */
@@ -575,7 +585,7 @@ typedef struct {
                                                ///< were evaluated.
 
        TALLOC_CTX              *list_ctx;      //!< Where to allocate new attributes if building
-                                               ///< out from the current extents of the tree.X
+                                               ///< out from the current extents of the tree.
        fr_pair_list_t          *list;          //!< List that we tried to evaluate ar in and failed.
                                                ///< Or if ar is NULL, the list that represents the
                                                ///< deepest grouping or TLV attribute the chain of
@@ -618,13 +628,13 @@ static inline tmpl_type_t tmpl_type_from_str(char const *type)
  *
  * @{
  */
-static inline tmpl_request_ref_t tmpl_request(tmpl_t const *vpt)
+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));
 
-       return ((tmpl_request_t *)tmpl_request_list_tail(&vpt->data.attribute.rr))->request;
+       return &vpt->data.attribute.rr;
 }
 
 /** The number of request references contained within a tmpl
@@ -892,10 +902,6 @@ TALLOC_CTX         *tmpl_list_ctx(request_t *request, tmpl_pair_list_t list_name);
 
 size_t                 tmpl_pair_list_name(tmpl_pair_list_t *out, char const *name, tmpl_pair_list_t default_list) CC_HINT(nonnull);
 
-int                    tmpl_request_ptr(request_t **request, tmpl_request_ref_t name) CC_HINT(nonnull);
-
-size_t                 tmpl_request_ref_by_name(tmpl_request_ref_t *out, char const *name, tmpl_request_ref_t unknown) CC_HINT(nonnull);
-
 tmpl_t                 *tmpl_init_printf(tmpl_t *vpt, tmpl_type_t type, fr_token_t quote, char const *fmt, ...) CC_HINT(nonnull(1,4));
 
 tmpl_t                 *tmpl_init_shallow(tmpl_t *vpt, tmpl_type_t type, fr_token_t quote,
@@ -908,6 +914,70 @@ tmpl_t                     *tmpl_init(tmpl_t *vpt, tmpl_type_t type, fr_token_t quote,
 
 tmpl_t                 *tmpl_alloc(TALLOC_CTX *ctx, tmpl_type_t type, fr_token_t quote, char const *name, ssize_t len);
 
+/** @name Parse request qualifiers
+ *
+ * @{
+ */
+/** Static default request ref list for the current request
+ *
+ * Passed as request_def in tmpl_attr_rules_t.
+ */
+extern FR_DLIST_HEAD(tmpl_request_list) tmpl_request_def_current;
+
+/** Static default request ref list for the outer request
+ *
+ * Passed as request_def in tmpl_attr_rules_t.
+ */
+extern FR_DLIST_HEAD(tmpl_request_list) tmpl_request_def_outer;
+
+/** Static default request ref list for the parent request
+ *
+ * Passed as request_def in tmpl_attr_rules_t.
+ */
+extern FR_DLIST_HEAD(tmpl_request_list) tmpl_request_def_parent;
+
+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);
+
+int8_t                 tmpl_request_ref_list_cmp(FR_DLIST_HEAD(tmpl_request_list) const *a,
+                                                 FR_DLIST_HEAD(tmpl_request_list) const *b);
+
+/** Returns true if the specified qualifier list points to the current request
+ *
+ * @param[in] _list    to check.
+ * @return
+ *     - true if the list only contains a current request qualifier.
+ *     - false otherwise.
+ */
+#define tmpl_request_ref_is_current(_list) (tmpl_request_ref_list_cmp(_list, &tmpl_request_def_current) == 0)
+
+/** Returns true if the specified qualifier list points to the parent request
+ *
+ * @param[in] _list    to check.
+ * @return
+ *     - true if the list only contains a parent request qualifier.
+ *     - false otherwise.
+ */
+#define tmpl_request_ref_is_parent(_list) (tmpl_request_ref_list_cmp(_list, &tmpl_request_def_parent) == 0)
+
+/** Returns true if the specified qualifier list points to the outer request
+ *
+ * @param[in] _list    to check.
+ * @return
+ *     - true if the list only contains a outer request qualifier.
+ *     - false otherwise.
+ */
+#define tmpl_request_ref_is_outer(_list) (tmpl_request_ref_list_cmp(_list, &tmpl_request_def_outer) == 0)
+
+
+fr_slen_t              tmpl_request_ref_list_afrom_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err,
+                                                          FR_DLIST_HEAD(tmpl_request_list) _CONST **out,
+                                                          fr_sbuff_t *in,
+                                                          fr_sbuff_parse_rules_t const *p_rules,
+                                                          tmpl_attr_rules_t const *t_rules);
+/** @} */
+
 void                   tmpl_set_name_printf(tmpl_t *vpt, fr_token_t quote, char const *fmt, ...) CC_HINT(nonnull(1,3));
 
 void                   tmpl_set_name_shallow(tmpl_t *vpt, fr_token_t quote, char const *name, ssize_t len) CC_HINT(nonnull);
@@ -936,7 +1006,7 @@ void                       tmpl_attr_rewrite_leaf_num(tmpl_t *vpt, int16_t from, int16_t to) CC_HINT
 
 void                   tmpl_attr_rewrite_num(tmpl_t *vpt, int16_t from, int16_t to) CC_HINT(nonnull);
 
-void                   tmpl_attr_set_request(tmpl_t *vpt, tmpl_request_ref_t request) CC_HINT(nonnull);
+void                   tmpl_attr_set_request_ref(tmpl_t *vpt, FR_DLIST_HEAD(tmpl_request_list) const *request_def) CC_HINT(nonnull);
 
 void                   tmpl_attr_set_list(tmpl_t *vpt, tmpl_pair_list_t list) CC_HINT(nonnull);
 
index 9bdcfdb400425b93af1a0a1a39d4f177ed620d46..2135d8cd2dfb07a1c9931d399ac973132d12f99b 100644 (file)
@@ -340,7 +340,6 @@ fr_pair_t *tmpl_dcursor_init(int *err, TALLOC_CTX *ctx, tmpl_dcursor_ctx_t *cc,
 {
        fr_pair_t               *vp = NULL;
        fr_pair_list_t          *list_head;
-       tmpl_request_t          *rr = NULL;
        TALLOC_CTX              *list_ctx;
 
        TMPL_VERIFY(vpt);
@@ -352,17 +351,11 @@ fr_pair_t *tmpl_dcursor_init(int *err, TALLOC_CTX *ctx, tmpl_dcursor_ctx_t *cc,
        /*
         *      Navigate to the correct request context
         */
-       while ((rr = tmpl_request_list_next(&vpt->data.attribute.rr, rr))) {
-               if (tmpl_request_ptr(&request, rr->request) < 0) {
-                       if (err) {
-                               *err = -3;
-                               fr_strerror_printf("Request context \"%s\" not available",
-                                                  fr_table_str_by_value(tmpl_request_ref_table, rr->request, "<INVALID>"));
-                       }
-               error:
-                       memset(cc, 0, sizeof(*cc));     /* so tmpl_dursor_clear doesn't explode */
-                       return NULL;
-               }
+       if (tmpl_request_ptr(&request, tmpl_request(vpt)) < 0) {
+               if (err) *err = -3;
+       error:
+               memset(cc, 0, sizeof(*cc));     /* so tmpl_dursor_clear doesn't explode */
+               return NULL;
        }
 
        /*
@@ -497,7 +490,6 @@ int tmpl_extents_find(TALLOC_CTX *ctx,
        tmpl_dcursor_ctx_t      cc;
        tmpl_dcursor_nested_t   *ns = NULL;
 
-       tmpl_request_t          *rr = NULL;
        tmpl_attr_t const       *ar = NULL;
 
        TMPL_VERIFY(vpt);
@@ -507,13 +499,7 @@ int tmpl_extents_find(TALLOC_CTX *ctx,
        /*
         *      Navigate to the correct request context
         */
-       while ((rr = tmpl_request_list_next(&vpt->data.attribute.rr, rr))) {
-               if (tmpl_request_ptr(&request, rr->request) < 0) {
-                       fr_strerror_printf("Request context \"%s\" not available",
-                                          fr_table_str_by_value(tmpl_request_ref_table, rr->request, "<INVALID>"));
-                       return -3;
-               }
-       }
+       if (tmpl_request_ptr(&request, tmpl_request(vpt)) < 0) return -3;
 
        if (!vpt->rules.attr.list_as_attr) {
                /*
index 40d7465c65d00ced21c57fb434cf5bfe0a89257a..57e961e2b3d19eba2b244a56ac23ee6eff6eebcb 100644 (file)
@@ -199,38 +199,42 @@ fr_radius_packet_t *tmpl_packet_ptr(request_t *request, tmpl_pair_list_t list)
  * to a #request_t higher in the chain than the current #request_t.
  *
  * @see tmpl_pair_list
- * @param[in,out] context #request_t to start resolving from, and where to write
- *     a pointer to the resolved #request_t back to.
- * @param[in] name (request) to resolve.
+ * @param[in,out] context      #request_t to start resolving from, and where to write
+ *                             a pointer to the resolved #request_t back to.
+ * @param[in] rql              list of request qualifiers to follow.
  * @return
  *     - 0 if request is valid in this context.
  *     - -1 if request is not valid in this context.
  */
-int tmpl_request_ptr(request_t **context, tmpl_request_ref_t name)
+int tmpl_request_ptr(request_t **context, FR_DLIST_HEAD(tmpl_request_list) const *rql)
 {
+       tmpl_request_t *rr = NULL;
        request_t *request = *context;
 
-       switch (name) {
-       case REQUEST_CURRENT:
-               return 0;
+       while ((rr = tmpl_request_list_next(rql, rr))) {
+               switch (rr->request) {
+               case REQUEST_CURRENT:
+                       continue;       /* noop */
 
-       case REQUEST_PARENT:    /* Navigate up one level */
-               if (!request->parent) return -1;
-               *context = request->parent;
-               break;
+               case REQUEST_PARENT:    /* Navigate up one level */
+                       if (!request->parent) return -1;
+                       request = request->parent;
+                       break;
 
-       case REQUEST_OUTER:     /* Navigate to the outermost request */
-               if (!request->parent) return -1;
-               while (request->parent) request = request->parent;
-               *context = request;
-               break;
+               case REQUEST_OUTER:     /* Navigate to the outermost request */
+                       if (!request->parent) return -1;
+                       while (request->parent) request = request->parent;
+                       break;
 
-       case REQUEST_UNKNOWN:
-       default:
-               fr_assert(0);
-               return -1;
+               case REQUEST_UNKNOWN:
+               default:
+                       fr_assert(0);
+                       return -1;
+               }
        }
 
+       *context = request;
+
        return 0;
 }
 
index 22491ea67542ac65a8a80dcc6bf73f49a29f3fd8..b2d74e63ba003c47d57976685fa3d1367ca70855 100644 (file)
@@ -40,6 +40,55 @@ RCSID("$Id$")
 
 #include <ctype.h>
 
+/** Define a global variable for specifying a default request reference
+ *
+ * @param[in] _name    what the global variable should be called.
+ * @param[in] _ref     one of the values of tmpl_request_ref_t
+ *                     - REQUEST_CURRENT
+ *                     - REQUEST_OUTER,
+ *                     - REQUEST_PARENT,
+ *                     - REQUEST_UNKNOWN
+ */
+#define TMPL_REQUEST_REF_DEF(_name, _def) \
+extern FR_DLIST_HEAD(tmpl_request_list) _name; \
+static tmpl_request_t _name ## _entry = { \
+       .entry = { \
+               .entry = { \
+                       .next = &_name.head.entry, \
+                       .prev = &_name.head.entry \
+               } \
+       }, \
+       .request = _def \
+}; \
+FR_DLIST_HEAD(tmpl_request_list) _name = { \
+       .head = { \
+               .offset = offsetof(tmpl_request_t, entry), \
+               .entry = { \
+                       .next = &_name ## _entry.entry.entry, \
+                       .prev = &_name ## _entry.entry.entry, \
+               }, \
+               .num_elements = 1, \
+       } \
+}
+
+/** Use the current request as the default
+ *
+ * Used as .attr.request_def = &tmpl_request_def_current;
+ */
+TMPL_REQUEST_REF_DEF(tmpl_request_def_current, REQUEST_CURRENT);
+
+/** Use the outer request as the default
+ *
+ * Used as .attr.request_def = &tmpl_request_def_outer;
+ */
+TMPL_REQUEST_REF_DEF(tmpl_request_def_outer, REQUEST_OUTER);
+
+/** Use the parent request as the default
+ *
+ * Used as .attr.request_def = &tmpl_request_def_parent;
+ */
+TMPL_REQUEST_REF_DEF(tmpl_request_def_parent, REQUEST_PARENT);
+
 /** Default parser rules
  *
  * Because this is getting to be a ridiculous number of parsing rules
@@ -47,13 +96,11 @@ RCSID("$Id$")
  *
  * Defaults are used if a NULL rules pointer is passed to the parsing function.
  */
-static tmpl_rules_t const default_attr_rules = {
-       .attr = {
-               .request_def = REQUEST_CURRENT,
-               .list_def = PAIR_LIST_REQUEST
-       }
+static tmpl_rules_t const default_rules = {
+
 };
 
+
 /* clang-format off */
 /** Map #tmpl_type_t values to descriptive strings
  */
@@ -130,6 +177,30 @@ static void attr_to_raw(tmpl_t *vpt, tmpl_attr_t *ref);
 #define UNRESOLVED_SET(_flags) (*(_flags) = (*(_flags) | TMPL_FLAG_UNRESOLVED))
 #define RESOLVED_SET(_flags) (*(_flags) = (*(_flags) & ~TMPL_FLAG_UNRESOLVED))
 
+/** Verify, after skipping whitespace, that a substring ends in a terminal char, or ends without further chars
+ *
+ * @param[in] in       the sbuff to check.
+ * @param[in] p_rules  to use terminals from.
+ * @return
+ *     - true if substr is terminated correctly.
+ *     - false if subst is not terminated correctly.
+ */
+static inline bool CC_HINT(always_inline) tmpl_substr_terminal_check(fr_sbuff_t *in,
+                                                                    fr_sbuff_parse_rules_t const *p_rules)
+{
+       fr_sbuff_marker_t       m;
+       bool                    ret;
+
+       if (!fr_sbuff_extend(in)) return true;          /* we're at the end of the string */
+       if (!p_rules || !p_rules->terminals) return false;      /* more stuff to parse but don't have a terminal set */
+
+       fr_sbuff_marker(&m, in);
+       ret = fr_sbuff_is_terminal(in, p_rules->terminals);
+       fr_sbuff_set(in, &m);
+       fr_sbuff_marker_release(&m);
+       return ret;
+}
+
 void tmpl_attr_ref_debug(const tmpl_attr_t *ar, int i)
 {
        char buffer[sizeof(STRINGIFY(INT16_MAX)) + 1];
@@ -403,54 +474,252 @@ size_t tmpl_pair_list_name(tmpl_pair_list_t *out, char const *name, tmpl_pair_li
        }
 }
 
-/** Resolve attribute name to a #tmpl_request_ref_t value.
- *
- * Check the name string for qualifiers that reference a parent #request_t.
+ /** Allocate a new request reference and add it to the end of the attribute reference list
  *
- * If we find a string that matches a #tmpl_request_ref_t qualifier, return the number of chars
- * we consumed.
+ */
+static inline CC_HINT(always_inline) CC_HINT(nonnull(2,3))
+void tmpl_request_ref_list_copy(TALLOC_CTX *ctx,
+                               FR_DLIST_HEAD(tmpl_request_list) *out, FR_DLIST_HEAD(tmpl_request_list) const *in)
+{
+       tmpl_request_t  *rr = NULL;
+       tmpl_request_t  *n_rr = NULL;
+
+       /*
+        *      Duplicate the complete default list
+        */
+       while ((rr = tmpl_request_list_next(in, rr))) {
+               MEM(n_rr = talloc(ctx, tmpl_request_t));
+               *n_rr = (tmpl_request_t){
+                       .request = rr->request
+               };
+               tmpl_request_list_insert_tail(out, n_rr);
+               ctx = n_rr;     /* Chain the contexts */
+       }
+}
+
+ /** Allocate a new request reference list and copy request references into it
  *
- * If we're sure we've definitely found a list qualifier token delimiter (``*``) but the
- * qualifier doesn't match one of the #tmpl_request_ref_t qualifiers, return 0 and set out to
- * #REQUEST_UNKNOWN.
+ */
+static inline CC_HINT(always_inline) CC_HINT(nonnull(2,3))
+void tmpl_request_ref_list_acopy(TALLOC_CTX *ctx,
+                                FR_DLIST_HEAD(tmpl_request_list) **out, FR_DLIST_HEAD(tmpl_request_list) const *in)
+{
+       FR_DLIST_HEAD(tmpl_request_list) *rql;
+
+       MEM(rql = talloc_zero(ctx, FR_DLIST_HEAD(tmpl_request_list)));
+       tmpl_request_list_talloc_init(rql);
+
+       tmpl_request_ref_list_copy(rql, rql, in);
+
+       *out = rql;
+}
+
+/** Dump a request list to stderr
  *
- * If we can't find a string that looks like a request qualifier, set out to def, and
- * return 0.
+ */
+void tmpl_request_ref_list_debug(FR_DLIST_HEAD(tmpl_request_list) const *rql)
+{
+       tmpl_request_t *rr = NULL;
+
+       while ((rr = tmpl_request_list_next(rql, rr))) {
+               FR_FAULT_LOG("request - %s (%u)",
+                            fr_table_str_by_value(tmpl_request_ref_table, rr->request, "<INVALID>"),
+                            rr->request);
+       }
+}
+
+/** Compare a list of request qualifiers
  *
- * @param[out] out The #tmpl_request_ref_t value the name resolved to (or #REQUEST_UNKNOWN).
- * @param[in] name of attribute.
- * @param[in] def default request ref to return if no request qualifier is present.
- * @return 0 if no valid request qualifier could be found, else the number of bytes consumed.
- *     The caller may then advanced the name pointer by the value returned, to get the
- *     start of the attribute list or attribute name(if any).
+ * @param[in] a                first list.  If NULL tmpl_request_def_current will be used.
+ * @param[in] b                second list.  If NULL tmpl_request_def_current will be used.
+ * @return
+ *     - >0 a > b
+ *     - 0 a == b
+ *     - <0 a < b
+ */
+int8_t tmpl_request_ref_list_cmp(FR_DLIST_HEAD(tmpl_request_list) const *a, FR_DLIST_HEAD(tmpl_request_list) const *b)
+{
+       tmpl_request_t *a_rr = NULL, *b_rr = NULL;
+
+       /*
+        *      NULL, uninit, empty are all equivalent
+        *      to tmpl_request_def_current.
+        *
+        *      We need all these equivalent checks to
+        *      deal with uninitialised tmpl rules.
+        */
+       if (!a || !tmpl_request_list_initialised(a) || tmpl_request_list_empty(a)) a = &tmpl_request_def_current;
+       if (!b || !tmpl_request_list_initialised(b) || tmpl_request_list_empty(b)) b = &tmpl_request_def_current;
+
+       /*
+        *      Fast path...
+        */
+       if (a == b) return 0;
+
+       for (;;) {
+               a_rr = tmpl_request_list_next(a, a_rr);
+               b_rr = tmpl_request_list_next(b, b_rr);
+
+               if (!a_rr || !b_rr) return CMP(tmpl_request_list_num_elements(a), tmpl_request_list_num_elements(b));
+
+               CMP_RETURN(a_rr, b_rr, request);
+       }
+}
+
+/** Parse one or more request references, writing the list to out
  *
- * @see tmpl_pair_list_name
- * @see tmpl_request_ref_table
+ * @parma[in] ctx      to allocate request refs in.
+ * @param[out] err     If !NULL where to write the parsing error.
+ * @param[in] in       Sbuff to read request references from.
+ * @param[in] p_rules  Parse rules.
+ * @param[in] at_rules Default list and other rules.
+ * @return
+ *     - >= 0 the number of bytes parsed.
+ *      - <0 negative offset for where the error occurred
  */
-size_t tmpl_request_ref_by_name(tmpl_request_ref_t *out, char const *name, tmpl_request_ref_t def)
+static fr_slen_t tmpl_request_ref_list_from_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err,
+                                                  FR_DLIST_HEAD(tmpl_request_list) *out,
+                                                  fr_sbuff_t *in,
+                                                  fr_sbuff_parse_rules_t const *p_rules,
+                                                  tmpl_attr_rules_t const *at_rules)
 {
-       char const *p, *q;
+       tmpl_request_ref_t      ref;
+       tmpl_request_t          *rr;
+       size_t                  ref_len;
+       fr_sbuff_t              our_in = FR_SBUFF(in);
+       tmpl_request_t          *tail = tmpl_request_list_tail(out);
+       unsigned int            depth = 0;
+       fr_sbuff_marker_t       m;
+
+       if (!at_rules) at_rules = &default_rules.attr;
 
-       p = name;
        /*
-        *      Try and determine the end of the token
+        *      We could make the caller do this but as this
+        *      function is intended to help populate tmpl rules,
+        *      just be nice...
         */
-       for (q = p; fr_dict_attr_allowed_chars[(uint8_t) *q] && (*q != '.') && (*q != '-'); q++);
+       if (!tmpl_request_list_initialised(out)) tmpl_request_list_talloc_init(out);
+
+       fr_sbuff_marker(&m, &our_in);
+       for (depth = 0; depth < TMPL_MAX_REQUEST_REF_NESTING; depth++) {
+               bool end;
+
+
+               /*
+                *      Search for a known request reference like
+                *      'current', or 'parent'.
+                */
+               fr_sbuff_out_by_longest_prefix(&ref_len, &ref, tmpl_request_ref_table, &our_in, REQUEST_UNKNOWN);
+
+               /*
+                *      No match
+                */
+               if (ref_len == 0) {
+                       /*
+                        *      If depth == 0, we're at the start
+                        *      so just use the default request
+                        *      reference.
+                        */
+               default_ref:
+                       if ((depth == 0) && at_rules->request_def) {
+                               tmpl_request_ref_list_copy(ctx, out, at_rules->request_def);
+                       }
+                       break;
+               }
+
+               /*
+                *      We don't want to misidentify the list
+                *      as being part of an attribute.
+                */
+               if (!fr_sbuff_is_char(&our_in, '.') && !tmpl_substr_terminal_check(&our_in, p_rules)) {
+                       goto default_ref;
+               }
+
+               if (at_rules->parent || at_rules->disallow_qualifiers) {
+                       fr_strerror_const("It is not permitted to specify a request reference here");
+                       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);
+                       return fr_sbuff_error(&our_in);
+               }
+
+               /*
+                *      Add a new entry to the dlist
+                */
+               MEM(rr = talloc(ctx, tmpl_request_t));
+               *rr = (tmpl_request_t){
+                       .request = ref
+               };
+               tmpl_request_list_insert_tail(out, rr);
+
+               /*
+                *      Advance past the separator (if there is one)
+                */
+               end = !fr_sbuff_next_if_char(&our_in, '.');
+
+               /*
+                *      Update to the last successfully parsed component
+                *
+                *      This makes it easy to backtrack from refs like
+                *
+                *              parent.outer-realm-name
+                */
+               fr_sbuff_set(&m, &our_in);
+
+               if (end) break;
+       }
 
        /*
-        *      First token delimiter wasn't a '.'
+        *      Nesting level too deep
         */
-       if (*q != '.') {
-               *out = def;
-               return 0;
+       if (depth > TMPL_MAX_REQUEST_REF_NESTING) {
+               fr_strerror_const("Request ref nesting too deep");
+               if (err) *err = TMPL_ATTR_ERROR_NESTING_TOO_DEEP;
+               goto error;     /* Leave marker at the end */
        }
 
-       *out = fr_table_value_by_substr(tmpl_request_ref_table, name, q - p, REQUEST_UNKNOWN);
-       if (*out == REQUEST_UNKNOWN) return 0;
+       return fr_sbuff_set(in, &m);
 
-       return (q + 1) - p;
 }
 
+/** Parse one or more request references, allocing a new list and adding the references to it
+ *
+ * This can be used to create request ref lists for rules and for tmpls.
+ *
+ * @parma[in] ctx      to allocate request refs in.
+ * @param[out] err     If !NULL where to write the parsing error.
+ * @param[in] in       Sbuff to read request references from.
+ * @param[in] p_rules  Parse rules.
+ * @param[in] at_rules Default list and other rules.
+ * @return
+ *     - >= 0 the number of bytes parsed.
+ *      - <0 negative offset for where the error occurred
+ */
+fr_slen_t tmpl_request_ref_list_afrom_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err,
+                                            FR_DLIST_HEAD(tmpl_request_list) **out,
+                                            fr_sbuff_t *in,
+                                            fr_sbuff_parse_rules_t const *p_rules,
+                                            tmpl_attr_rules_t const *at_rules)
+{
+       fr_slen_t       slen;
+
+       FR_DLIST_HEAD(tmpl_request_list) *rql;
+
+       MEM(rql = talloc_zero(ctx, FR_DLIST_HEAD(tmpl_request_list)));
+       tmpl_request_list_talloc_init(rql);
+
+       slen = tmpl_request_ref_list_from_substr(rql, err, rql, in, p_rules, at_rules);
+       if (slen < 0) {
+               talloc_free(rql);
+               return slen;
+       }
+
+       *out = rql;
+
+       return slen;
+}
 /** @} */
 
 /** @name Alloc or initialise #tmpl_t
@@ -682,29 +951,6 @@ tmpl_t *tmpl_alloc(TALLOC_CTX *ctx, tmpl_type_t type, fr_token_t quote, char con
  * @{
  */
 
- /** Allocate a new request reference and add it to the end of the attribute reference list
- *
- */
-static tmpl_request_t *tmpl_req_ref_add(tmpl_t *vpt, tmpl_request_ref_t request)
-{
-       tmpl_request_t  *rr;
-       TALLOC_CTX      *ctx;
-
-       if (tmpl_request_list_num_elements(&vpt->data.attribute.rr) == 0) {
-               ctx = vpt;
-       } else {
-               ctx = tmpl_request_list_tail(&vpt->data.attribute.rr);
-       }
-
-       MEM(rr = talloc(ctx, tmpl_request_t));
-       *rr = (tmpl_request_t){
-               .request = request
-       };
-       tmpl_request_list_insert_tail(&vpt->data.attribute.rr, rr);
-
-       return rr;
-}
-
 /** Allocate a new attribute reference and add it to the end of the attribute reference list
  *
  */
@@ -772,8 +1018,7 @@ int tmpl_afrom_value_box(TALLOC_CTX *ctx, tmpl_t **out, fr_value_box_t *data, bo
  */
 int tmpl_attr_copy(tmpl_t *dst, tmpl_t const *src)
 {
-       tmpl_attr_t             *src_ar = NULL, *dst_ar;
-       tmpl_request_t  *src_rr = NULL, *dst_rr;
+       tmpl_attr_t *src_ar = NULL, *dst_ar;
 
        /*
         *      Clear any existing attribute references
@@ -804,14 +1049,10 @@ int tmpl_attr_copy(tmpl_t *dst, tmpl_t const *src)
 
        /*
         *      Clear any existing request references
+        *      and copy the ones from the source.
         */
-       if (tmpl_request_list_num_elements(&dst->data.attribute.rr) > 0) {
-               tmpl_request_list_talloc_reverse_free(&dst->data.attribute.rr);
-       }
-
-       while ((src_rr = tmpl_request_list_next(&src->data.attribute.rr, src_rr))) {
-               MEM(dst_rr = tmpl_req_ref_add(dst, src_rr->request));
-       }
+       tmpl_request_list_talloc_reverse_free(&dst->data.attribute.rr);
+       tmpl_request_ref_list_copy(dst, &dst->data.attribute.rr, &src->data.attribute.rr);
 
        /*
         *      Remove me...
@@ -962,14 +1203,16 @@ void tmpl_attr_rewrite_num(tmpl_t *vpt, int16_t from, int16_t to)
 /** Set the request for an attribute ref
  *
  */
-void tmpl_attr_set_request(tmpl_t *vpt, tmpl_request_ref_t request)
+void tmpl_attr_set_request_ref(tmpl_t *vpt, FR_DLIST_HEAD(tmpl_request_list) const *request_def)
 {
        fr_assert_msg(tmpl_is_attr(vpt), "Expected tmpl type 'attr', got '%s'",
                      tmpl_type_to_str(vpt->type));
 
-       if (tmpl_request_list_num_elements(&vpt->data.attribute.rr) > 0) tmpl_request_list_talloc_reverse_free(&vpt->data.attribute.rr);
-
-       tmpl_req_ref_add(vpt, request);
+       /*
+        *      Clear any existing request references
+        */
+       tmpl_request_list_talloc_reverse_free(&vpt->data.attribute.rr);
+       tmpl_request_ref_list_copy(vpt, &vpt->data.attribute.rr, request_def);
 
        TMPL_ATTR_VERIFY(vpt);
 }
@@ -1026,30 +1269,6 @@ int tmpl_attr_afrom_list(TALLOC_CTX *ctx, tmpl_t **out, tmpl_t const *list, fr_d
 }
 /** @} */
 
-/** Verify, after skipping whitespace, that a substring ends in a terminal char, or ends without further chars
- *
- * @param[in] in       the sbuff to check.
- * @param[in] p_rules  to use terminals from.
- * @return
- *     - true if substr is terminated correctly.
- *     - false if subst is not terminated correctly.
- */
-static inline bool CC_HINT(always_inline) tmpl_substr_terminal_check(fr_sbuff_t *in,
-                                                                    fr_sbuff_parse_rules_t const *p_rules)
-{
-       fr_sbuff_marker_t       m;
-       bool                    ret;
-
-       if (!fr_sbuff_extend(in)) return true;          /* we're at the end of the string */
-       if (!p_rules || !p_rules->terminals) return false;      /* more stuff to parse but don't have a terminal set */
-
-       fr_sbuff_marker(&m, in);
-       ret = fr_sbuff_is_terminal(in, p_rules->terminals);
-       fr_sbuff_set(in, &m);
-       fr_sbuff_marker_release(&m);
-       return ret;
-}
-
 /** Descriptive return values for filter parsing
  *
  */
@@ -1692,94 +1911,6 @@ do_suffix:
        return 0;
 }
 
-static inline int tmpl_request_ref_afrom_attr_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err,
-                                                    tmpl_t *vpt,
-                                                    fr_sbuff_t *name,
-                                                    fr_sbuff_parse_rules_t const *p_rules,
-                                                    tmpl_attr_rules_t const **pt_rules,
-                                                    unsigned int depth)
-{
-       tmpl_request_ref_t                      ref;
-       size_t                                  ref_len;
-       tmpl_request_t                          *rr;
-       FR_DLIST_HEAD(tmpl_request_list)        *list = &vpt->data.attribute.rr;
-       fr_sbuff_marker_t                       s_m;
-       tmpl_attr_rules_t const                 *t_rules = *pt_rules;
-
-       fr_sbuff_marker(&s_m, name);
-       fr_sbuff_out_by_longest_prefix(&ref_len, &ref, tmpl_request_ref_table, name, t_rules->request_def);
-
-       /*
-        *      No match
-        */
-       if (ref_len == 0) {
-               /*
-                *      If depth == 0, then just use
-                *      the default request reference.
-                */
-       default_ref:
-               if (depth == 0) {
-                       MEM(rr = talloc(ctx, tmpl_request_t));
-                       *rr = (tmpl_request_t){
-                               .request = ref
-                       };
-                       tmpl_request_list_insert_tail(list, rr);
-               }
-               fr_sbuff_marker_release(&s_m);
-
-               return 0;
-       }
-
-       /*
-        *      We don't want to misidentify the list
-        *      as being part of an attribute.
-        */
-       if (!fr_sbuff_is_char(name, '.') &&
-           ((!p_rules || !p_rules->terminals) ||
-           !tmpl_substr_terminal_check(name, p_rules))) {
-               fr_sbuff_set(name, &s_m);
-               goto default_ref;
-       }
-
-       fr_sbuff_marker_release(&s_m);
-
-       /*
-        *      Nesting level too deep
-        */
-       if (depth > TMPL_MAX_REQUEST_REF_NESTING) {
-               fr_strerror_const("Request ref nesting too deep");
-               if (err) *err = TMPL_ATTR_ERROR_NESTING_TOO_DEEP;
-               return -1;
-       }
-
-       if (t_rules->parent || t_rules->disallow_qualifiers) {
-               fr_strerror_const("It is not permitted to specify a request reference here");
-               if (err) *err = TMPL_ATTR_ERROR_INVALID_LIST_QUALIFIER;
-               return -1;
-       }
-
-       /*
-        *      Add a new entry to the dlist
-        */
-       MEM(rr = talloc(ctx, tmpl_request_t));
-       *rr = (tmpl_request_t){
-               .request = ref
-       };
-       tmpl_request_list_insert_tail(list, rr);
-
-       /*
-        *      Advance past the separator (if there is one)
-        */
-       if (fr_sbuff_next_if_char(name, '.')) {
-               if (tmpl_request_ref_afrom_attr_substr(ctx, err, vpt, name, p_rules, pt_rules, depth + 1) < 0) {
-                       tmpl_request_list_talloc_free_tail(list); /* Remove and free rr */
-                       return -1;
-               }
-       }
-
-       return 0;
-}
-
 /** Parse a string into a TMPL_TYPE_ATTR_* or #TMPL_TYPE_LIST type #tmpl_t
  *
  * @param[in,out] ctx          to allocate #tmpl_t in.
@@ -1839,9 +1970,11 @@ ssize_t tmpl_afrom_attr_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err,
        tmpl_attr_rules_t const         *t_attr_rules;
        fr_sbuff_marker_t               m_l;
 
-       if (!t_rules) t_rules = &default_attr_rules;    /* Use the defaults */
-
-       t_attr_rules = &t_rules->attr;
+       if (!t_rules) {
+               t_attr_rules = &default_rules.attr;     /* Use the defaults */
+       } else {
+               t_attr_rules = &t_rules->attr;
+       }
 
        if (err) *err = TMPL_ATTR_ERROR_NONE;
 
@@ -1894,7 +2027,11 @@ ssize_t tmpl_afrom_attr_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err,
        /*
         *      Parse one or more request references
         */
-       ret = tmpl_request_ref_afrom_attr_substr(vpt, err, vpt, &our_name, p_rules, &t_attr_rules, 0);
+       ret = tmpl_request_ref_list_from_substr(vpt, NULL,
+                                             &vpt->data.attribute.rr,
+                                             &our_name,
+                                             p_rules,
+                                             t_attr_rules);
        if (ret < 0) {
        error:
                *out = NULL;
@@ -2044,6 +2181,18 @@ ssize_t tmpl_afrom_attr_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err,
 
        tmpl_set_name(vpt, T_BARE_WORD, fr_sbuff_start(&our_name), fr_sbuff_used(&our_name));
        vpt->rules = *t_rules;  /* Record the rules */
+
+       /*
+        *      If there are actual requests, duplicate them
+        *      and move them into the list.
+        *
+        *      A NULL request_def pointer is equivalent to the
+        *      current request.
+        */
+       if (t_rules->attr.request_def) {
+               tmpl_request_ref_list_acopy(vpt, &vpt->rules.attr.request_def, t_rules->attr.request_def);
+       }
+
        if (tmpl_is_attr(vpt) && (tmpl_da(vpt)->type == tmpl_rules_cast(vpt))) vpt->rules.cast = FR_TYPE_NULL;
 
        if (!tmpl_substr_terminal_check(&our_name, p_rules)) {
@@ -2090,7 +2239,7 @@ ssize_t tmpl_afrom_attr_str(TALLOC_CTX *ctx, tmpl_attr_error_t *err,
 {
        ssize_t slen, name_len;
 
-       if (!t_rules) t_rules = &default_attr_rules;    /* Use the defaults */
+       if (!t_rules) t_rules = &default_rules; /* Use the defaults */
 
        name_len = strlen(name);
        slen = tmpl_afrom_attr_substr(ctx, err, out, &FR_SBUFF_IN(name, name_len), NULL, t_rules);
@@ -2688,7 +2837,7 @@ ssize_t tmpl_afrom_substr(TALLOC_CTX *ctx, tmpl_t **out,
 
        tmpl_t                  *vpt = NULL;
 
-       if (!t_rules) t_rules = &default_attr_rules;    /* Use the defaults */
+       if (!t_rules) t_rules = &default_rules; /* Use the defaults */
 
        *out = NULL;
 
index 1d937bfae5569c0ee9f2da56f7993fd9007ce815..fc957c8dc89e30a209fb3350a0449ae9e1f4a45d 100644 (file)
@@ -271,7 +271,6 @@ int pairlist_read(TALLOC_CTX *ctx, fr_dict_t const *dict, char const *file, PAIR
        lhs_rules = (tmpl_rules_t) {
                .attr = {
                        .dict_def = dict,
-                       .request_def = REQUEST_CURRENT,
                        .prefix = TMPL_ATTR_REF_PREFIX_NO,
                        .disallow_qualifiers = true, /* for now, until more tests are made */
 
@@ -288,7 +287,6 @@ int pairlist_read(TALLOC_CTX *ctx, fr_dict_t const *dict, char const *file, PAIR
        rhs_rules = (tmpl_rules_t) {
                .attr = {
                        .dict_def = dict,
-                       .request_def = REQUEST_CURRENT,
                        .prefix = TMPL_ATTR_REF_PREFIX_YES,
                        .disallow_qualifiers = true, /* for now, until rlm_files supports it */
                }
index 76170b74a662a03bae5c0680bface047a32f4aae..970daa95cbf390bb16c0ea78aa4cbb8623bd93eb 100644 (file)
@@ -485,7 +485,7 @@ static bool pass2_fixup_cond_map(fr_cond_t *c, CONF_ITEM *ci, fr_dict_t const *d
         *      with the request pairs.
         */
        if (!tmpl_is_attr(map->lhs) ||
-           (tmpl_request(map->lhs) != REQUEST_CURRENT) ||
+           !tmpl_request_ref_is_current(tmpl_request(map->lhs)) ||
            (tmpl_list(map->lhs) != PAIR_LIST_REQUEST)) {
                return true;
        }
index 717092c570ed9edff36fce519356d1e5ffd17233..f53ff884252d28b32c7a27dc8036feaf35ddc36b 100644 (file)
@@ -1102,12 +1102,23 @@ static void _xlat_debug(xlat_exp_head_t const *head, int depth)
                        break;
 
                case XLAT_TMPL:
+               {
                        if (tmpl_is_attr(node->vpt)) {
                                fr_assert(!node->flags.pure);
                                INFO_INDENT("attribute (%s)", tmpl_da(node->vpt)->name);
                                if (tmpl_num(node->vpt) != NUM_ANY) {
+                                       FR_DLIST_HEAD(tmpl_request_list) const *list;
+                                       tmpl_request_t *rr = NULL;
+
                                        INFO_INDENT("{");
-                                       INFO_INDENT("ref  %d", tmpl_request(node->vpt));
+
+                                       /*
+                                        *      Loop over the request references
+                                        */
+                                       list = tmpl_request(node->vpt);
+                                       while ((rr = tmpl_request_list_next(list, rr))) {
+                                               INFO_INDENT("ref  %d", rr->request);
+                                       }
                                        INFO_INDENT("list %d", tmpl_list(node->vpt));
                                        if (tmpl_num(node->vpt) != NUM_ANY) {
                                                if (tmpl_num(node->vpt) == NUM_COUNT) {
@@ -1125,6 +1136,7 @@ static void _xlat_debug(xlat_exp_head_t const *head, int depth)
                        } else {
                                INFO_INDENT("tmpl (%s)", node->fmt);
                        }
+               }
                        break;
 
                case XLAT_VIRTUAL:
diff --git a/src/tests/keywords/subrequest-request-qualifiers b/src/tests/keywords/subrequest-request-qualifiers
new file mode 100644 (file)
index 0000000..24e8ec7
--- /dev/null
@@ -0,0 +1,79 @@
+subrequest Access-Request {
+       #
+       #  Prefix/attribute parsing disambiguation
+       #
+       update outer.request {
+               &Outer-Realm-Name := 'testing123'
+       }
+
+       update {
+               &User-Name := "obo"
+       }
+
+       subrequest Access-Request {
+               update {
+                       &User-Name := "jim"
+               }
+
+               subrequest Access-Request {
+                       update {
+                               &User-Name := "joe"
+                       }
+
+                       update outer.request {
+                               &Tmp-String-0 := &current.User-Name
+                               &Tmp-String-1 := &parent.User-Name
+                               &Tmp-String-2 := &parent.parent.User-Name
+                               &Tmp-String-3 := &parent.parent.parent.User-Name
+                               &Tmp-String-4 := &outer.User-Name
+                               &Tmp-String-5 := &current.outer.User-Name
+                               &Tmp-String-6 := &parent.current.outer.User-Name
+                               &Tmp-String-7 := "%{parent.parent.parent.parent.User-Name}"
+                               &Tmp-String-8 := "%{outer.parent.User-Name}"
+
+                       }
+               }
+       }
+}
+
+if (&Outer-Realm-Name != 'testing123') {
+       test_fail
+}
+
+if (&Tmp-String-0 != 'joe') {
+       test_fail
+}
+
+if (&Tmp-String-1 != 'jim') {
+       test_fail
+}
+
+if (&tmp-String-2 != 'obo') {
+       test_fail
+}
+
+if (&Tmp-String-3 != 'bob') {
+       test_fail
+}
+
+if (&Tmp-String-4 != 'bob') {
+       test_fail
+}
+
+if (&Tmp-String-5 != 'bob') {
+       test_fail
+}
+
+if (&Tmp-String-6 != 'bob') {
+       test_fail
+}
+
+if (&Tmp-String-7 != '') {
+       test_fail
+}
+
+if (&Tmp-String-8 != '') {
+       test_fail
+}
+
+success