]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Remove final tmpl_list uses list_as_attr
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Tue, 3 Jan 2023 17:50:35 +0000 (12:50 -0500)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Tue, 3 Jan 2023 17:50:35 +0000 (12:50 -0500)
36 files changed:
src/bin/unit_test_attribute.c
src/bin/unit_test_map.c
src/bin/unit_test_module.c
src/lib/io/master.c
src/lib/ldap/map.c
src/lib/redis/redis.c
src/lib/server/cf_file.c
src/lib/server/cf_parse.c
src/lib/server/map.c
src/lib/server/map_async.c
src/lib/server/tmpl.h
src/lib/server/tmpl_dcursor_tests.c
src/lib/server/tmpl_eval.c
src/lib/server/tmpl_tokenize.c
src/lib/server/users_file.c
src/lib/server/virtual_servers.c
src/lib/unlang/compile.c
src/lib/unlang/edit.c
src/lib/unlang/xlat_builtin.c
src/lib/unlang/xlat_eval.c
src/lib/unlang/xlat_expr.c
src/lib/unlang/xlat_tokenize.c
src/lib/util/debug.h
src/listen/ldap_sync/proto_ldap_sync.c
src/modules/rlm_cache/rlm_cache.c
src/modules/rlm_cache/serialize.c
src/modules/rlm_couchbase/mod.c
src/modules/rlm_couchbase/mod.h
src/modules/rlm_couchbase/rlm_couchbase.c
src/modules/rlm_exec/rlm_exec.c
src/modules/rlm_files/rlm_files.c
src/modules/rlm_json/rlm_json.c
src/modules/rlm_linelog/rlm_linelog.c
src/modules/rlm_mruby/rlm_mruby.c
src/modules/rlm_python/rlm_python.c
src/modules/rlm_rest/rest.c

index d2762b2222677b383596dd9b79799f91292aa1b8..3ada3c14d5ebf070617cdcfc648601c0fffc7aaa 100644 (file)
@@ -160,6 +160,8 @@ do { \
  */
 #define DEFAULT_BUFFER_SIZE    1024
 
+#define CURRENT_DICT(_cc) (tmpl_attr_ctx_rules_default_dict(&(_cc)->tmpl_rules.attr) ? tmpl_attr_ctx_rules_default_dict(&(_cc)->tmpl_rules.attr) : (_cc)->config->dict)
+
 typedef enum {
        RESULT_OK = 0,                          //!< Not an error - Result as expected.
        RESULT_NOOP,                            //!< Not an error - Did nothing...
@@ -984,8 +986,9 @@ static int dictionary_load_common(command_result_t *result, command_file_ctx_t *
        /*
         *      Decrease ref count if we're loading in a new dictionary
         */
-       if (cc->tmpl_rules.attr.dict_def) {
-               if (fr_dict_const_free(&cc->tmpl_rules.attr.dict_def, __FILE__) < 0) RETURN_COMMAND_ERROR();
+       if (tmpl_attr_ctx_rules_default_dict(&cc->tmpl_rules.attr)) {
+               fr_dict_t const *attr_dict = tmpl_attr_ctx_rules_default_dict(&cc->tmpl_rules.attr);
+               if (fr_dict_const_free(&attr_dict, __FILE__) < 0) RETURN_COMMAND_ERROR();
        }
 
        q = strchr(in, ' ');
@@ -1002,12 +1005,15 @@ static int dictionary_load_common(command_result_t *result, command_file_ctx_t *
        talloc_free(tmp);
        if (ret < 0) RETURN_COMMAND_ERROR();
 
-       cc->tmpl_rules.attr.dict_def = dict;
+
+       cc->tmpl_rules.attr.ctx = tmpl_attr_ctx_rules_default(tmpl_attr_ctx_rules_default_request(&cc->tmpl_rules.attr),
+                                                             tmpl_attr_ctx_rules_default_list(&cc->tmpl_rules.attr),
+                                                             dict);
 
        /*
         *      Dump the dictionary if we're in super debug mode
         */
-       if (fr_debug_lvl > 5) fr_dict_debug(cc->tmpl_rules.attr.dict_def);
+       if (fr_debug_lvl > 5) fr_dict_debug(dict);
 
        RETURN_OK(0);
 }
@@ -1181,7 +1187,7 @@ static size_t command_normalise_attribute(command_result_t *result, command_file
        ssize_t         slen;
        char            *p, *end;
        fr_pair_t       *vp;
-       fr_dict_t const *dict = cc->tmpl_rules.attr.dict_def ? cc->tmpl_rules.attr.dict_def : cc->config->dict;
+       fr_dict_t const *dict = CURRENT_DICT(cc);
 
        fr_pair_list_init(&head);
 
@@ -1465,7 +1471,7 @@ static size_t command_condition_normalise(command_result_t *result, command_file
        ssize_t                 dec_len;
        fr_cond_t               *cond;
        CONF_SECTION            *cs;
-       size_t                  len;
+       ssize_t                 slen;
 
        cs = cf_section_alloc(NULL, NULL, "if", "condition");
        if (!cs) {
@@ -1497,10 +1503,14 @@ static size_t command_condition_normalise(command_result_t *result, command_file
                goto return_error;
        }
 
-       len = cond_print(&FR_SBUFF_OUT(data, COMMAND_OUTPUT_MAX), cond);
+       slen = cond_print(&FR_SBUFF_OUT(data, COMMAND_OUTPUT_MAX), cond);
        talloc_free(cs);
 
-       RETURN_OK(len);
+       if (slen < 0) {
+               fr_strerror_printf_push_head("ERROR OOB offset %d", (int) -slen);
+               RETURN_COMMAND_ERROR();
+       }
+       RETURN_OK((size_t)slen);
 }
 
 static size_t command_count(command_result_t *result, command_file_ctx_t *cc,
@@ -1574,7 +1584,7 @@ static size_t command_decode_pair(command_result_t *result, command_file_ctx_t *
         *      point to produce fr_pair_ts.
         */
        while (to_dec < to_dec_end) {
-               slen = tp->func(cc->tmp_ctx, &head, fr_dict_root(cc->tmpl_rules.attr.dict_def ? cc->tmpl_rules.attr.dict_def : cc->config->dict),
+               slen = tp->func(cc->tmp_ctx, &head, fr_dict_root(CURRENT_DICT(cc)),
                                (uint8_t *)to_dec, (to_dec_end - to_dec), decode_ctx);
                cc->last_ret = slen;
                if (slen <= 0) {
@@ -1751,7 +1761,7 @@ static size_t command_dictionary_attribute_parse(command_result_t *result, comma
 static size_t command_dictionary_dump(command_result_t *result, command_file_ctx_t *cc,
                                      UNUSED char *data, size_t data_used, UNUSED char *in, UNUSED size_t inlen)
 {
-       fr_dict_debug(cc->tmpl_rules.attr.dict_def ? cc->tmpl_rules.attr.dict_def : cc->config->dict);
+       fr_dict_debug(CURRENT_DICT(cc));
 
        /*
         *      Don't modify the contents of the data buffer
@@ -1909,7 +1919,7 @@ static size_t command_encode_pair(command_result_t *result, command_file_ctx_t *
                RETURN_COMMAND_ERROR();
        }
 
-       dict = cc->tmpl_rules.attr.dict_def ? cc->tmpl_rules.attr.dict_def : cc->config->dict;
+       dict = CURRENT_DICT(cc);
        if (fr_pair_list_afrom_str(cc->tmp_ctx, fr_dict_root(dict),
                                   p, in + inlen - p, &head) != T_EOL) {
                CLEAR_TEST_POINT(cc);
@@ -1942,7 +1952,7 @@ static size_t command_encode_pair(command_result_t *result, command_file_ctx_t *
 
                for (vp = fr_pair_dcursor_iter_init(&cursor, &head,
                                                    tp->next_encodable ? tp->next_encodable : fr_proto_next_encodable,
-                                                   cc->tmpl_rules.attr.dict_def ? cc->tmpl_rules.attr.dict_def : cc->config->dict);
+                                                   CURRENT_DICT(cc));
                     vp;
                     vp = fr_dcursor_current(&cursor)) {
                        slen = tp->func(&FR_DBUFF_TMP(enc_p, enc_end), &cursor, encode_ctx);
@@ -2094,7 +2104,7 @@ static size_t command_flatten(command_result_t *result, command_file_ctx_t *cc,
 {
        fr_dict_attr_t  const *da;
        fr_pair_t       *head;
-       fr_dict_t const *dict = cc->tmpl_rules.attr.dict_def ? cc->tmpl_rules.attr.dict_def : cc->config->dict;
+       fr_dict_t const *dict = CURRENT_DICT(cc);
 
        da = fr_dict_attr_by_name(NULL, fr_dict_root(fr_dict_internal()), "request");
        fr_assert(da != NULL);
@@ -2118,7 +2128,7 @@ static size_t command_unflatten(command_result_t *result, command_file_ctx_t *cc
 {
        fr_dict_attr_t  const *da;
        fr_pair_t       *head;
-       fr_dict_t const *dict = cc->tmpl_rules.attr.dict_def ? cc->tmpl_rules.attr.dict_def : cc->config->dict;
+       fr_dict_t const *dict = CURRENT_DICT(cc);
 
        da = fr_dict_attr_by_name(NULL, fr_dict_root(fr_dict_internal()), "request");
        fr_assert(da != NULL);
@@ -2163,7 +2173,7 @@ static size_t command_encode_proto(command_result_t *result, command_file_ctx_t
                RETURN_COMMAND_ERROR();
        }
 
-       dict = cc->tmpl_rules.attr.dict_def ? cc->tmpl_rules.attr.dict_def : cc->config->dict;
+       dict = CURRENT_DICT(cc);
        if (fr_pair_list_afrom_str(cc->tmp_ctx, fr_dict_root(dict),
                                   p, in + inlen - p, &head) != T_EOL) {
                CLEAR_TEST_POINT(cc);
@@ -2307,7 +2317,7 @@ static size_t command_load_dictionary(command_result_t *result, command_file_ctx
                dir = cc->path;
        }
 
-       ret = fr_dict_read(UNCONST(fr_dict_t *, cc->tmpl_rules.attr.dict_def), dir, name);
+       ret = fr_dict_read(UNCONST(fr_dict_t *, tmpl_attr_ctx_rules_default_dict(&cc->tmpl_rules.attr)), dir, name);
        talloc_free(tmp);
        if (ret < 0) RETURN_COMMAND_ERROR();
 
@@ -2461,7 +2471,7 @@ static size_t command_pair(command_result_t *result, command_file_ctx_t *cc,
 
        fr_pair_list_init(&head);
        ctx.ctx = cc->tmp_ctx;
-       ctx.parent = fr_dict_root(cc->tmpl_rules.attr.dict_def);
+       ctx.parent = fr_dict_root(tmpl_attr_ctx_rules_default_dict(&cc->tmpl_rules.attr));
        ctx.list = &head;
 
        p = in;
@@ -2586,13 +2596,17 @@ static ssize_t command_tmpl_rule_attr_parent(UNUSED TALLOC_CTX *ctx, tmpl_rules_
 {
        fr_dict_attr_err_t      err;
        fr_slen_t               slen;
+       fr_dict_attr_t const    *da;
 
-       slen = fr_dict_attr_by_oid_substr(&err,
-                                         &rules->attr.parent,
-                                         rules->attr.dict_def ? fr_dict_root(rules->attr.dict_def) :
-                                                                fr_dict_root(fr_dict_internal()),
+       slen = fr_dict_attr_by_oid_substr(&err, &da,
+                                         tmpl_attr_ctx_rules_default_dict(&rules->attr) ?
+                                               fr_dict_root(tmpl_attr_ctx_rules_default_dict(&rules->attr)) : 
+                                               fr_dict_root(fr_dict_internal()),
                                          value, NULL);
        if (err != FR_DICT_ATTR_OK) FR_SBUFF_ERROR_RETURN(value);
+
+       rules->attr.ctx = tmpl_attr_ctx_rules_nested(da);
+
        return slen;
 }
 
@@ -2618,24 +2632,29 @@ static ssize_t command_tmpl_rule_disallow_qualifiers(UNUSED TALLOC_CTX *ctx, tmp
 
 static ssize_t command_tmpl_rule_list_def(UNUSED TALLOC_CTX *ctx, tmpl_rules_t *rules, fr_sbuff_t *value)
 {
-       ssize_t slen;
-
-       fr_sbuff_out_by_longest_prefix(&slen, &rules->attr.list_def, pair_list_table, value, PAIR_LIST_UNKNOWN);
+       ssize_t                 slen;
+       fr_dict_attr_t const    *list;
 
-       if (rules->attr.list_def == PAIR_LIST_UNKNOWN) {
+       slen = tmpl_attr_list_from_substr(&list, value);
+       if (slen == 0) {
                fr_strerror_printf("Invalid list specifier \"%pV\"",
                                   fr_box_strvalue_len(fr_sbuff_current(value), fr_sbuff_remaining(value)));
        }
 
+       rules->attr.ctx = tmpl_attr_ctx_rules_default(tmpl_attr_ctx_rules_default_request(&rules->attr),
+                                                     list,
+                                                     tmpl_attr_ctx_rules_default_dict(&rules->attr));
+
        return slen;
 }
 
 static ssize_t command_tmpl_rule_request_def(TALLOC_CTX *ctx, tmpl_rules_t *rules, fr_sbuff_t *value)
 {
-       fr_slen_t                        slen;
+       fr_slen_t                               slen;
+       FR_DLIST_HEAD(tmpl_request_list) const  *request_def;
 
        slen = tmpl_request_ref_list_afrom_substr(ctx, NULL,
-                                                 &rules->attr.request_def,
+                                                 &request_def,
                                                  value,
                                                  NULL,
                                                  NULL);
@@ -2644,6 +2663,10 @@ static ssize_t command_tmpl_rule_request_def(TALLOC_CTX *ctx, tmpl_rules_t *rule
                                   fr_box_strvalue_len(fr_sbuff_current(value), fr_sbuff_remaining(value)));
        }
 
+       rules->attr.ctx = tmpl_attr_ctx_rules_default(request_def,
+                                                     tmpl_attr_ctx_rules_default_list(&rules->attr),
+                                                     tmpl_attr_ctx_rules_default_dict(&rules->attr));
+
        return slen;
 }
 
@@ -2832,8 +2855,7 @@ static size_t command_xlat_normalise(command_result_t *result, command_file_ctx_
        dec_len = xlat_tokenize(cc->tmp_ctx, &head, &FR_SBUFF_IN(in, input_len), &p_rules,
                                &(tmpl_rules_t) {
                                        .attr = {
-                                               .dict_def = cc->tmpl_rules.attr.dict_def ?
-                                               cc->tmpl_rules.attr.dict_def : cc->config->dict,
+                                               .ctx = tmpl_attr_ctx_rules_default(NULL, NULL, CURRENT_DICT(cc)),
                                                .allow_unresolved = cc->tmpl_rules.attr.allow_unresolved
                                        },
                                });
@@ -2867,8 +2889,7 @@ static size_t command_xlat_expr(command_result_t *result, command_file_ctx_t *cc
        dec_len = xlat_tokenize_expression(cc->tmp_ctx, &head, &FR_SBUFF_IN(in, input_len), NULL,
                                           &(tmpl_rules_t) {
                                                .attr = {
-                                                       .dict_def = cc->tmpl_rules.attr.dict_def ?
-                                                          cc->tmpl_rules.attr.dict_def : cc->config->dict,
+                                                       .ctx = tmpl_attr_ctx_rules_default(NULL, NULL, CURRENT_DICT(cc)),
                                                        .allow_unresolved = cc->tmpl_rules.attr.allow_unresolved,
                                                }
                                           });
@@ -2906,8 +2927,7 @@ static size_t command_xlat_purify(command_result_t *result, command_file_ctx_t *
        dec_len = xlat_tokenize_ephemeral_expression(cc->tmp_ctx, &head, el, &FR_SBUFF_IN(in, input_len), NULL,
                                           &(tmpl_rules_t) {
                                                   .attr = {
-                                                       .dict_def = cc->tmpl_rules.attr.dict_def ?
-                                                          cc->tmpl_rules.attr.dict_def : cc->config->dict,
+                                                       .ctx = tmpl_attr_ctx_rules_default(NULL, NULL, CURRENT_DICT(cc)),
                                                        .allow_unresolved = cc->tmpl_rules.attr.allow_unresolved,
                                                   },
                                           });
@@ -2959,8 +2979,7 @@ static size_t command_xlat_argv(command_result_t *result, command_file_ctx_t *cc
                                  NULL,
                                  &(tmpl_rules_t) {
                                          .attr = {
-                                                 .dict_def = cc->tmpl_rules.attr.dict_def ?
-                                                 cc->tmpl_rules.attr.dict_def : cc->config->dict,
+                                                 .ctx = tmpl_attr_ctx_rules_default(NULL, NULL, CURRENT_DICT(cc)),
                                                  .allow_unresolved = cc->tmpl_rules.attr.allow_unresolved
                                          },
                                  });
@@ -3220,6 +3239,11 @@ size_t process_line(command_result_t *result, command_file_ctx_t *cc, char *data
        size_t                  match_len;
        char                    *p;
 
+       /*
+        *      Don't accidentally print irrelevant errors
+        */
+       fr_strerror_clear();
+
        p = in;
        fr_skip_whitespace(p);
        if (*p == '\0') RETURN_NOOP(data_used);
@@ -3262,7 +3286,8 @@ size_t process_line(command_result_t *result, command_file_ctx_t *cc, char *data
         */
        if (result->error_to_data) data_used = strerror_concat(data, COMMAND_OUTPUT_MAX);
 
-       fr_assert((size_t)data_used < COMMAND_OUTPUT_MAX);
+       fr_assert_msg((size_t)data_used < COMMAND_OUTPUT_MAX,
+                     "Command printed %zu bytes, buffer max was %zu bytes", data_used, (size_t)COMMAND_OUTPUT_MAX);
        data[data_used] = '\0';                 /* Ensure the data buffer is \0 terminated */
 
        if (data_used) {
@@ -3520,12 +3545,16 @@ static int process_file(bool *exit_now, TALLOC_CTX *ctx, command_config_t const
 finish:
        if (fp && (fp != stdin)) fclose(fp);
 
-       /*
-        *      Free any residual resources we loaded.
-        */
-       if (cc && (fr_dict_const_free(&cc->tmpl_rules.attr.dict_def, __FILE__) < 0)) {
-               fr_perror("unit_test_attribute");
-               ret = -1;
+       {
+               fr_dict_t const *dict = tmpl_attr_ctx_rules_default_dict(&cc->tmpl_rules.attr);
+
+               /*
+               *       Free any residual resources we loaded.
+               */
+               if (cc && (fr_dict_const_free(&dict, __FILE__) < 0)) {
+                       fr_perror("unit_test_attribute");
+                       ret = -1;
+               }
        }
 
        fr_dict_global_ctx_set(config->dict_gctx);      /* Switch back to the main dict ctx */
index 60f1ba8ad651a37ec73e5106baf19a97e7fa05ed..a8d32e993b63c01353fb14ccfa37ef4721a0ccbc 100644 (file)
@@ -83,7 +83,7 @@ static int process_file(char const *filename)
 
        tmpl_rules_t    parse_rules = {
                .attr = {
-                       .dict_def = dict_radius,
+                       .ctx = tmpl_attr_ctx_rules_default(NULL, NULL, dict_radius),
                        .allow_foreign = false, /* tests are in the RADIUS dictionary */
                }
        };
index 1b363a73df99dc2451eb0f10e37438301efa1115..05debe5f58a751f7bc244adea3354780db9167ca 100644 (file)
@@ -454,11 +454,11 @@ static bool do_xlats(fr_event_list_t *el, char const *filename, FILE *fp)
                                                                  &FR_SBUFF_IN(fmt, talloc_array_length(fmt) - 1),
                                                                  NULL,
                                                                  &(tmpl_rules_t) {
-                                                                         .attr = {
-                                                                                 .dict_def = dict_protocol,
-                                                                                 .allow_unresolved = true,
-                                                                         }
-                                                                                 }
+                                                                       .attr = {
+                                                                               .ctx = tmpl_attr_ctx_rules_default(NULL, NULL, dict_protocol),
+                                                                               .allow_unresolved = true,
+                                                                       }
+                                                                 }
                                                                );
                        if (slen <= 0) {
                                talloc_free(xlat_ctx);
index fe89530dd9099de09e5dfe53b802865f1aa939fb..a1a79fd24f84398400241711f866ec1f7acd2ec7 100644 (file)
@@ -2694,7 +2694,7 @@ static int mod_instantiate(module_inst_ctx_t const *mctx)
                if (app_process->compile_list) {
                        tmpl_rules_t    parse_rules = {
                                .attr = {
-                                       .dict_def = virtual_server_dict_by_name(cf_section_name2(inst->server_cs))
+                                       .ctx = tmpl_attr_ctx_rules_default(NULL, NULL, virtual_server_dict_by_name(cf_section_name2(inst->server_cs)))
                                }
                        };
 
index 51fd6d8338a971133c06c39c01cc608f5296048e..d63839b529423aa21be9e6d309472b9a7e260402 100644 (file)
@@ -273,7 +273,7 @@ int fr_ldap_map_do(request_t *request,
 
                        tmpl_rules_t parse_rules = {
                                .attr = {
-                                       .dict_def = request->dict,
+                                       .ctx = tmpl_attr_ctx_rules_default(NULL, NULL, request->dict),
                                        .prefix = TMPL_ATTR_REF_PREFIX_AUTO
                                }
                        };
index 10b4a97152c3a485cbe5e5ae90defbfb8cebf255..adb4aff33b5cb8f03eb8575b16e0f1cfa3a4d673 100644 (file)
@@ -23,6 +23,7 @@
  * @copyright 2000,2006,2015 The FreeRADIUS server project
  * @copyright 2011 TekSavvy Solutions (gabe@teksavvy.com)
  */
+#include "lib/server/tmpl.h"
 #include <freeradius-devel/redis/base.h>
 #include <freeradius-devel/util/debug.h>
 #include <freeradius-devel/util/value.h>
@@ -388,7 +389,7 @@ int fr_redis_reply_to_map(TALLOC_CTX *ctx, map_list_t *out, request_t *request,
                                   &(tmpl_rules_t){
                                        .attr = {
                                                .prefix = TMPL_ATTR_REF_PREFIX_NO,
-                                               .dict_def = request->dict
+                                               .ctx = tmpl_attr_ctx_rules_default(NULL, NULL, request->dict)
                                        }
                                   });
        if (slen <= 0) {
index ad1dcca55744813e997ac58097c85351b3a3fac0..0a80a7c6816bc409fecbf967feabdf324dd3c186 100644 (file)
@@ -144,14 +144,22 @@ typedef struct {
 
 static inline CC_HINT(always_inline) int cf_tmpl_rules_verify(CONF_SECTION *cs, tmpl_rules_t const *rules)
 {
+       fr_dict_t const *dict;
+
+       /*
+        *      Only validate if we're parsing with defaults
+        */
+       if (!tmpl_attr_ctx_rules_is_default(&rules->attr)) return 0;
+
+       dict = tmpl_attr_ctx_rules_default_dict(&rules->attr);
        if (cf_section_has_parent(cs, "policy", NULL)) {
-               if (!fr_cond_assert_msg(!rules->attr.dict_def || (rules->attr.dict_def == fr_dict_internal()),
+               if (!fr_cond_assert_msg(!dict || (dict == fr_dict_internal()),
                                        "Protocol dictionary must be NULL not %s",
-                                       fr_dict_root(rules->attr.dict_def)->name)) return -1;
+                                       fr_dict_root(dict)->name)) return -1;
 
        } else {
-               if (!fr_cond_assert_msg(rules->attr.dict_def, "No protocol dictionary set")) return -1;
-               if (!fr_cond_assert_msg(rules->attr.dict_def != fr_dict_internal(), "rules->attr.dict_def must not be the internal dictionary")) return -1;
+               if (!fr_cond_assert_msg(dict, "No protocol dictionary set")) return -1;
+               if (!fr_cond_assert_msg(dict != fr_dict_internal(), "rules->attr.dict_def must not be the internal dictionary")) return -1;
        }
 
        if (!fr_cond_assert_msg(!rules->attr.allow_foreign, "rules->allow_foreign must be false")) return -1;
@@ -1336,7 +1344,7 @@ static ssize_t fr_skip_condition(char const *start, char const *end, bool expect
                        was_regex = false;
                        continue;
                }
-               
+
                if (*p == ')') {
                        if (!depth) {
                                fr_strerror_const("Too many ')'");
@@ -1354,7 +1362,7 @@ static ssize_t fr_skip_condition(char const *start, char const *end, bool expect
                 */
                if ((*p == '$') || (*p == '%')) {
                        if (end && ((p + 2) >= end)) goto fail;
-                       
+
                        if ((p[1] == '{') || ((p[0] == '$') && (p[1] == '('))) {
                                slen = fr_skip_xlat(p, end);
 
@@ -1439,7 +1447,7 @@ static ssize_t fr_skip_condition(char const *start, char const *end, bool expect
         *      condition is done when it reaches end of string, or CR LF.
         */
        if (!expect_section && (depth == 0) && ((end && (p == end)) || (*p < ' '))) return p - start;
-               
+
        /*
         *      Unexpected end of condition.
         */
@@ -1476,7 +1484,7 @@ static CONF_ITEM *process_if(cf_stack_t *stack)
 
        t_rules = (tmpl_rules_t) {
                .attr = {
-                       .dict_def = dict,
+                       .ctx = tmpl_attr_ctx_rules_default(NULL, NULL, dict),
                        .allow_unresolved = true,
                        .allow_unknown = true
                }
index 978634cc3c076f1c075784b372ec864706311418..dbe5c254184e4a2f1a4f0ae93d3f6346b710109d 100644 (file)
@@ -1235,12 +1235,12 @@ int cf_section_parse_pass2(void *base, CONF_SECTION *cs)
                        slen = xlat_tokenize(cs, &xlat,
                                             &FR_SBUFF_IN(cp->value, talloc_array_length(cp->value) - 1), NULL,
                                             &(tmpl_rules_t) {
-                                                    .attr = {
-                                                            .dict_def = dict,
-                                                            .allow_unknown = false,
-                                                            .allow_unresolved = false,
-                                                            .allow_foreign = (dict == NULL)
-                                                    },
+                                                       .attr = {
+                                                               .ctx = tmpl_attr_ctx_rules_default(NULL, NULL, dict),
+                                                               .allow_unknown = false,
+                                                               .allow_unresolved = false,
+                                                               .allow_foreign = (dict == NULL)
+                                                       },
                                             });
                        if (slen < 0) {
                                char *spaces, *text;
index c818e057ef7473ae444d62f051831c62a4401788..157f813d8dc0eb94324161c64e12ef2e18802eab 100644 (file)
  * @copyright 2013 Alan DeKok (aland@freeradius.org)
  */
 
+
+#include "tmpl.h"
 RCSID("$Id$")
 
+
 #include <freeradius-devel/server/cond.h>
 #include <freeradius-devel/server/exec.h>
 #include <freeradius-devel/server/exec_legacy.h>
 #include <freeradius-devel/server/map.h>
 #include <freeradius-devel/server/paircmp.h>
 #include <freeradius-devel/server/tmpl.h>
+#include <freeradius-devel/server/tmpl.h>
 #include <freeradius-devel/server/tmpl_dcursor.h>
-
-#include <freeradius-devel/util/debug.h>
 #include <freeradius-devel/util/base16.h>
-#include <freeradius-devel/util/pair_legacy.h>
+#include <freeradius-devel/util/debug.h>
 #include <freeradius-devel/util/misc.h>
+#include <freeradius-devel/util/pair_legacy.h>
+#include <freeradius-devel/util/sbuff.h>
 
 #include <freeradius-devel/protocol/radius/rfc2865.h>
 #include <freeradius-devel/protocol/freeradius/freeradius.internal.h>
@@ -442,7 +446,7 @@ ssize_t map_afrom_substr(TALLOC_CTX *ctx, map_t **out, map_t **parent_p, fr_sbuf
                         */
                        if (is_child) {
                                fr_assert(tmpl_is_attr(parent->lhs));
-                               our_lhs_rules.attr.parent = tmpl_attr_tail_da(parent->lhs);
+                               our_lhs_rules.attr.ctx = tmpl_attr_ctx_rules_nested(tmpl_attr_tail_da(parent->lhs));
 
                                slen = tmpl_afrom_attr_substr(map, NULL, &map->lhs, &our_in,
                                                              &map_parse_rules_bareword_quoted, &our_lhs_rules);
@@ -711,28 +715,32 @@ static int _map_afrom_cs(TALLOC_CTX *ctx, map_list_t *out, map_t *parent, CONF_S
         */
        if (update) {
                fr_slen_t slen;
-               char const *p;
+               char const *name2;
+               FR_DLIST_HEAD(tmpl_request_list) const *rql;
+               fr_dict_attr_t const *list = NULL;
+               fr_sbuff_t qual;
 
-               p = cf_section_name2(cs);
-               if (!p) goto do_children;
+               name2 = cf_section_name2(cs);
+               if (!name2) goto do_children;
+               
+               qual = FR_SBUFF_IN(name2, strlen(name2));
 
                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);
+               slen = tmpl_request_ref_list_afrom_substr(ctx, NULL, &rql, &qual, 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);
+               slen = tmpl_attr_list_from_substr(&list, &qual);
+               if (slen == 0) {
+                       cf_log_err(ci, "Default list \"%.*s\" specified in mapping section is invalid", (unsigned int)fr_sbuff_remaining(&qual), fr_sbuff_current(&qual));
                        talloc_free(tmp_ctx);
                        return -1;
                }
+
+               our_lhs_rules.attr.ctx = tmpl_attr_ctx_rules_default(rql, list, NULL);
        }
 
 do_children:
@@ -826,24 +834,7 @@ do_children:
                         *      inner section.
                         */
                        our_lhs_rules.attr.prefix = TMPL_ATTR_REF_PREFIX_NO;
-                       our_lhs_rules.attr.parent = tmpl_attr_tail_da(map->lhs);
-
-                       /*
-                        *      Groups MAY change dictionaries.  If so, then swap the dictionary and the parent.
-                        */
-                       if (our_lhs_rules.attr.parent->type == FR_TYPE_GROUP) {
-                               fr_dict_attr_t const *ref;
-                               fr_dict_t const *dict, *internal;
-
-                               ref = fr_dict_attr_ref(our_lhs_rules.attr.parent);
-                               dict = fr_dict_by_da(ref);
-                               internal = fr_dict_internal();
-
-                               if ((dict != internal) && (dict != our_lhs_rules.attr.dict_def)) {
-                                       our_lhs_rules.attr.dict_def = dict;
-                                       our_lhs_rules.attr.parent = ref;
-                               }
-                       }
+                       our_lhs_rules.attr.ctx = tmpl_attr_ctx_rules_nested(tmpl_attr_tail_da(map->lhs));
 
                        /*
                         *      This prints out any relevant error
@@ -1247,9 +1238,9 @@ 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_UNSPEC);
 
-       tmpl_attr_set_request_ref(map->lhs, rules->attr.request_def);
-       tmpl_attr_set_list(map->lhs, rules->attr.list_def);
-
+       tmpl_attr_set_request_ref(map->lhs, tmpl_attr_ctx_rules_default_request(&rules->attr));
+       tmpl_attr_set_list(map->lhs, tmpl_attr_ctx_rules_default_list(&rules->attr));
+       
        tmpl_print(&FR_SBUFF_OUT(buffer, sizeof(buffer)), map->lhs, TMPL_ATTR_REF_PREFIX_YES, NULL);
        tmpl_set_name(map->lhs, T_BARE_WORD, buffer, -1);
 
@@ -1310,7 +1301,7 @@ static int map_exec_to_vp(TALLOC_CTX *ctx, fr_pair_list_t *out, request_t *reque
        /*
         *      We always put the request pairs into the environment
         */
-       input_pairs = tmpl_list_head(request, PAIR_LIST_REQUEST);
+       input_pairs = &request->pair_list.request->vp_group;
 
        /*
         *      Automagically switch output type depending on our destination
@@ -1452,7 +1443,7 @@ int map_to_vp(TALLOC_CTX *ctx, fr_pair_list_t *out, request_t *request, map_t co
                fr_pair_list_t *from = NULL;
 
                if (tmpl_request_ptr(&context, tmpl_request(map->rhs)) == 0) {
-                       from = tmpl_list_head(context, tmpl_list(map->rhs));
+                       from = tmpl_list_head(context, map->rhs);
                }
                if (!from) return 0;
 
@@ -1669,7 +1660,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_pair_list_t        list_ref;
 
        tmpl_dcursor_ctx_t      cc = {};
 
@@ -1713,7 +1703,7 @@ int map_to_request(request_t *request, map_t const *map, radius_map_getvalue_t f
                slen = tmpl_afrom_attr_str(tmp_ctx, NULL, &exp_lhs, attr_str,
                                           &(tmpl_rules_t){
                                                .attr = {
-                                                       .dict_def = request->dict,
+                                                       .ctx = tmpl_attr_ctx_rules_default(NULL, NULL, request->dict),
                                                        .prefix = TMPL_ATTR_REF_PREFIX_NO
                                                }
                                           });
@@ -1757,17 +1747,15 @@ int map_to_request(request_t *request, map_t const *map, radius_map_getvalue_t f
                goto finish;
        }
 
-       list_ref = tmpl_list(map->lhs);
-       list = tmpl_list_head(context, list_ref);
+       list = tmpl_list_head(context, map->lhs);
        if (!list) {
-               REDEBUG("Mapping \"%.*s\" -> \"%.*s\" cannot be performed due to to invalid list qualifier \"%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(pair_list_table, list_ref, "<INVALID>"));
+               REDEBUG("Mapping \"%.*s\" -> \"%.*s\" cannot be performed due to to invalid list qualifier in left side of map",
+                       (int)map->rhs->len, map->rhs->name, (int)map->lhs->len, map->lhs->name);
                rcode = -2;
                goto finish;
        }
 
-       parent = tmpl_list_ctx(context, tmpl_list(map->lhs));
+       parent = tmpl_list_ctx(context, map->lhs);
        fr_assert(parent);
 
        /*
index 9b1ae5551dd20d753dcbdfe8cf2014bcc17d7282..f357a45e7ea2394706c554c41649827f6491b257 100644 (file)
@@ -206,7 +206,6 @@ 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_pair_list_t        list_ref;
 
        if (tmpl_request_ptr(&context, tmpl_request(src_dst)) < 0) {
                RPEDEBUG("Mapping \"%.*s\" -> \"%.*s\" cannot be performed",
@@ -214,12 +213,10 @@ static inline fr_pair_list_t *map_check_src_or_dst(request_t *request, map_t con
                return NULL;
        }
 
-       list_ref = tmpl_list(src_dst);
-       list = tmpl_list_head(context, list_ref);
+       list = tmpl_list_head(request, src_dst);
        if (!list) {
-               REDEBUG("Mapping \"%.*s\" -> \"%.*s\" cannot be performed due to to invalid list qualifier \"%s\"",
-                       (int)map->rhs->len, map->rhs->name, (int)map->lhs->len, map->lhs->name,
-                       fr_table_str_by_value(pair_list_table, list_ref, "<INVALID>"));
+               REDEBUG("Mapping \"%.*s\" -> \"%.*s\" cannot be performed due to to invalid list qualifier",
+                       (int)map->rhs->len, map->rhs->name, (int)map->lhs->len, map->lhs->name);
                return NULL;
        }
 
@@ -319,7 +316,7 @@ int map_to_list_mod(TALLOC_CTX *ctx, vp_list_mod_t **out,
                slen = tmpl_afrom_attr_str(tmp_ctx, NULL, &map_tmp.lhs, lhs_result_head->vb_strvalue,
                                           &(tmpl_rules_t){
                                                .attr = {
-                                                       .dict_def = request->dict,
+                                                       .ctx = tmpl_attr_ctx_rules_default(NULL, NULL, request->dict),
                                                        .prefix = TMPL_ATTR_REF_PREFIX_NO
                                                }
                                           });
@@ -403,7 +400,8 @@ int map_to_list_mod(TALLOC_CTX *ctx, vp_list_mod_t **out,
                        n_mod->lhs = tmpl_alloc(n, TMPL_TYPE_ATTR, T_BARE_WORD, mutated->lhs->name, mutated->lhs->len);
                        if (!n_mod->lhs) goto error;
 
-                       if (tmpl_attr_copy(n_mod->lhs, mutated->lhs) < 0) goto error;
+                       tmpl_attr_replace(n_mod->lhs, tmpl_attr(mutated->lhs));
+                       tmpl_request_ref_replace(n_mod->lhs, tmpl_request(mutated->lhs));
 
                        tmpl_attr_set_leaf_da(n_mod->lhs, vp->da);
 
@@ -716,8 +714,7 @@ int map_to_list_mod(TALLOC_CTX *ctx, vp_list_mod_t **out,
                        map_t *mod;
                        tmpl_rules_t rules = {
                                .attr = {
-                                       .request_def = tmpl_request(mutated->lhs),
-                                       .list_def = tmpl_list(mutated->lhs)
+                                       .ctx = tmpl_attr_ctx_rules_default(tmpl_request(mutated->lhs), tmpl_list(mutated->lhs), NULL)
                                }
                        };
 
@@ -963,10 +960,10 @@ int map_list_mod_apply(request_t *request, vp_list_mod_t const *vlm)
        context = request;
        if (!fr_cond_assert(mod && tmpl_request_ptr(&context, tmpl_request(mod->lhs)) == 0)) return -1;
 
-       vp_list = tmpl_list_head(context, tmpl_list(mod->lhs));
+       vp_list = tmpl_list_head(context, mod->lhs);
        if (!fr_cond_assert(vp_list)) return -1;
 
-       parent = tmpl_list_ctx(context, tmpl_list(mod->lhs));
+       parent = tmpl_list_ctx(context, mod->lhs);
        fr_assert(parent);
 
        /*
index 606d65e52d7ea2adbd8e80307bd2acc1fe701151..e5ecb788fd3428609df4d1ac00978f5af6eecaea 100644 (file)
@@ -82,23 +82,6 @@ extern "C" {
  */
 #define TMPL_MAX_REQUEST_REF_NESTING   10
 
-/*
- *     Forward declarations
- */
-typedef enum pair_list_e {
-       PAIR_LIST_REQUEST = 0,          //!< Attributes in incoming or internally proxied
-                                       ///< request (default).
-       PAIR_LIST_REPLY,                //!< Attributes to send in the response.
-       PAIR_LIST_CONTROL,              //!< Attributes that change the behaviour of
-                                       ///< modules.
-       PAIR_LIST_STATE,                //!< Attributes to store multiple rounds of
-                                       ///< challenges/responses.
-       PAIR_LIST_UNKNOWN               //!< Unknown list.
-} tmpl_pair_list_t;
-
-extern fr_table_num_ordered_t const pair_list_table[];
-extern size_t pair_list_table_len;
-
 typedef enum requests_ref_e {
        REQUEST_CURRENT = 0,            //!< The current request (default).
        REQUEST_OUTER,                  //!< #request_t containing the outer layer of the EAP
@@ -245,10 +228,12 @@ extern fr_table_num_ordered_t const tmpl_type_table[];
 extern size_t tmpl_type_table_len;
 
 typedef struct tmpl_rules_s tmpl_rules_t;
+typedef struct tmpl_attr_ctx_rules_s tmpl_attr_ctx_rules_t;
 typedef struct tmpl_attr_rules_s tmpl_attr_rules_t;
 typedef struct tmpl_xlat_rules_s tmpl_xlat_rules_t;
 typedef struct tmpl_res_rules_s tmpl_res_rules_t;
 typedef struct tmpl_s tmpl_t;
+typedef struct tmpl_attr_s tmpl_attr_t;
 
 #include <freeradius-devel/unlang/xlat.h>
 #include <freeradius-devel/unlang/xlat_ctx.h>
@@ -268,6 +253,77 @@ typedef struct tmpl_s tmpl_t;
 #  define _CONST
 #endif
 
+/** Define entry and head types for tmpl request references
+ */
+FR_DLIST_TYPES(tmpl_request_list)
+#define tmpl_request_list_foreach(_list_head, _iter) fr_dlist_foreach(tmpl_request_list_dlist_head(_list_head), tmpl_request_t, _iter)
+#define tmpl_request_list_foreach_safe(_list_head, _iter) fr_dlist_foreach_safe(tmpl_request_list_dlist_head(_list_head), tmpl_request_t, _iter)
+#define tmpl_request_list_verify(_list_head) _tmpl_request_list_verify(__FILE__, __LINE__, _list_head)
+
+/** Define entry and head types for attribute reference lists
+ */
+FR_DLIST_TYPES(tmpl_attr_list)
+#define tmpl_attr_list_foreach(_list_head, _iter) fr_dlist_foreach(tmpl_attr_list_dlist_head(_list_head), tmpl_attr_t, _iter)
+#define tmpl_attr_list_foreach_safe(_list_head, _iter) fr_dlist_foreach_safe(tmpl_attr_list_dlist_head(_list_head), tmpl_attr_t, _iter)
+#define tmpl_attr_list_verify(_list_head) _tmpl_attr_list_verify(__FILE__, __LINE__, _list_head)
+
+/** Different ctx types in which attribute references can be resolved
+ */
+typedef enum {
+       TMPL_ATTR_RULES_CTX_DEFAULTS = 0,               //!< Start resolving references with a default dictionary
+                                                       ///< a default list, and a default request.
+       TMPL_ATTR_RULES_CTX_INHERIT,                    //!< Copy request refs and attr refs from another tmpl.
+       TMPL_ATTR_RULES_CTX_NESTED                      //!< Resove references within the context of another attribute.
+} tmpl_attr_ctx_rules_type_t;
+
+/** Context in which to evaluate other attribute references
+ */
+struct tmpl_attr_ctx_rules_s {
+       union {
+               /** Outer, un-nested references
+                */
+               struct {
+
+                       FR_DLIST_HEAD(tmpl_request_list) _CONST *request;       //!< 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.
+
+                       fr_dict_attr_t  const                   *list;          //!< A default list prefix, used if no context
+                                                                               ///< has been specified, and no list qualifiers
+                                                                               ///< were found.
+
+                       fr_dict_t const                         *dict;          //!< Default dictionary to use
+                                                                               ///< with unqualified attribute references.
+               } defaults;
+
+               /** Context is derived from another tmpl
+                *
+                * Usually when a LHS attribute is used as the context for a RHS attribute
+                */
+               struct {
+                       FR_DLIST_HEAD(tmpl_request_list) _CONST  *request;      //!< Request qualifiers to copy
+
+                       FR_DLIST_HEAD(tmpl_attr_list) _CONST    *attr;          //!< Attr qualifiers to copy into the attribute
+                                                                               ///< list. We 
+               } inherit;
+
+               /** Context is derived from nesting i.e. one tmpl inside another
+                */
+               fr_dict_attr_t const    *nested;        //!< Resume parsing using this 
+       };
+
+       tmpl_attr_ctx_rules_type_t type;                //!< What type of context was specified.
+};
+
 /** Specify whether attribute references require a prefix
  *
  */
@@ -277,36 +333,8 @@ 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.
-
-       fr_dict_attr_t const    *parent;                //!< Point in dictionary tree to resume parsing
-                                                       ///< from.  If this is provided then dict_def
-                                                       ///< request_def and list_def will be ignored
-                                                       ///< and the presence of any of those qualifiers
-                                                       ///< will be treated as an error.
-
-       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.
+       tmpl_attr_ctx_rules_t   ctx;                    //!< Parsing context for attribute tmpls.
 
        tmpl_attr_prefix_t      prefix;                 //!< Whether the attribute reference requires
                                                        ///< a prefix.
@@ -319,7 +347,7 @@ struct tmpl_attr_rules_s {
                                                        ///< This should be used as part of a multi-pass
                                                        ///< approach to parsing.
 
-       uint8_t                 allow_foreign:1;                //!< Allow arguments not found in dict_def.
+       uint8_t                 allow_foreign:1;        //!< Allow arguments not found in dict_def.
 
        uint8_t                 disallow_internal:1;    //!< Allow/fallback to internal attributes.
 
@@ -328,6 +356,130 @@ struct tmpl_attr_rules_s {
        uint8_t                 disallow_filters:1;     //!< disallow filters.
 };
 
+/** Initialise a defaults ctx with request/list/dict qualifiers
+ *
+ * @param[in] _request         May be NULL or one of the following static request qualifier lists:
+ *                             - tmpl_request_def_current
+ *                             - tmpl_request_def_outer
+ *                             - tmpl_request_def_parent
+ * @param[in] _list            May be NULL or one of the following static attr ref lists:
+ *                             - request_attr_request
+ *                             - request_attr_reply
+ *                             - request_attr_control
+ *                             - request_attr_state
+ * @param[in] _dict            May be NULL or any valid dictionary 
+ */
+#define tmpl_attr_ctx_rules_default(_request, _list, _dict) \
+       (tmpl_attr_ctx_rules_t){ \
+               .defaults = {  \
+                       .request = _request, \
+                       .list = _list, \
+                       .dict = _dict \
+               }, \
+               .type = TMPL_ATTR_RULES_CTX_DEFAULTS \
+       }
+
+/** Initialise an inehrit ctx from a tmpl
+ *
+ * @param[in] _tmpl            To copy request qualifiers and attr references from
+ */
+#define tmpl_attr_ctx_rules_inherit(_tmpl) \
+       (tmpl_attr_ctx_rules_t){ \
+               .copy = {  \
+                       .request = tmpl_request(_tmpl), \
+                       .attr = tmpl_attr(_tmpl) \
+               }, \
+               .type = TMPL_ATTR_RULES_CTX_COPY \
+       }
+
+/** Initialise a nested ctx from a da
+ *
+ * @param[in] _da              to use for namespacing.
+ */
+#define tmpl_attr_ctx_rules_nested(_da) \
+       (tmpl_attr_ctx_rules_t){ \
+               .nested = _da, \
+               .type = TMPL_ATTR_RULES_CTX_NESTED \
+       }
+
+/** Does this attr_rules structure contain a "default" ctx?
+ */
+#define tmpl_attr_ctx_rules_is_default(_attr_rules)    ((_attr_rules)->ctx.type == TMPL_ATTR_RULES_CTX_DEFAULTS)
+
+/** Does this attr_rules structure contain an "inherit" ctx
+ */
+#define tmpl_attr_ctx_rules_is_inherit(_attr_rules)    ((_attr_rules)->ctx.type == TMPL_ATTR_RULES_CTX_INHERIT)
+
+/** Does this attr_rules structure contain a "nested" ctx
+ */
+#define tmpl_attr_ctx_rules_is_nested(_attr_rules)     ((_attr_rules)->ctx.type == TMPL_ATTR_RULES_CTX_NESTED)
+
+/** Return the list of request qualifiers from a "default" ctx
+ *
+ * @param[in] a_rules  An attribute rules structure
+ */
+static inline FR_DLIST_HEAD(tmpl_request_list) const *tmpl_attr_ctx_rules_default_request(tmpl_attr_rules_t const *a_rules)
+{
+       if (unlikely(!fr_cond_assert_msg(tmpl_attr_ctx_rules_is_default(a_rules), "Attempted to access default.request, but not a default ctx"))) return NULL;
+
+       return a_rules->ctx.defaults.request;
+}
+
+/** Return the attribute list from a "default" ctx
+ *
+ * @param[in] a_rules  An attribute rules structure
+ */
+static inline fr_dict_attr_t const *tmpl_attr_ctx_rules_default_list(tmpl_attr_rules_t const *a_rules)
+{
+       if (unlikely(!fr_cond_assert_msg(tmpl_attr_ctx_rules_is_default(a_rules), "Attempted to access default.list, but not a default ctx"))) return NULL;
+
+       return a_rules->ctx.defaults.list;
+}
+
+/** Return the dictionary from a "default" ctx
+ *
+ * @param[in] a_rules  An attribute rules structure
+ */
+static inline fr_dict_t const *tmpl_attr_ctx_rules_default_dict(tmpl_attr_rules_t const *a_rules)
+{
+       if (unlikely(!fr_cond_assert_msg(tmpl_attr_ctx_rules_is_default(a_rules), "Attempted to access default.dict, but not a default ctx"))) return NULL;
+
+       return a_rules->ctx.defaults.dict;
+}
+
+/** Return the list of request qualifiers from an "inherit" ctx
+ *
+ * @param[in] a_rules  An attribute rules structure
+ */
+static inline FR_DLIST_HEAD(tmpl_request_list) const *tmpl_attr_ctx_rules_inherit_request(tmpl_attr_rules_t const *a_rules)
+{
+       if (unlikely(!fr_cond_assert_msg(tmpl_attr_ctx_rules_is_inherit(a_rules), "Attempted to access inherit.request, but not an inherit ctx"))) return NULL;
+
+       return a_rules->ctx.inherit.request;
+}
+
+/** Return the attribute reference list qualifiers from an "inherit" ctx
+ *
+ * @param[in] a_rules  An attribute rules structure
+ */
+static inline FR_DLIST_HEAD(tmpl_attr_list) const *tmpl_attr_ctx_rules_inherit_attr(tmpl_attr_rules_t const *a_rules)
+{
+       fr_assert_msg(tmpl_attr_ctx_rules_is_inherit(a_rules), "Attempted to access inherit.attr, but not an inherit ctx");
+
+       return a_rules->ctx.inherit.attr;
+}
+
+/** Return the attribute reference list qualifiers from a "nested" ctx
+ *
+ * @param[in] a_rules  An attribute rules structure
+ */
+static inline fr_dict_attr_t const *tmpl_attr_ctx_rules_nested_da(tmpl_attr_rules_t const *a_rules)
+{
+       fr_assert_msg(tmpl_attr_ctx_rules_is_nested(a_rules), "Attempted to access nested, but not a nested ctx");
+
+       return a_rules->ctx.nested;
+}
+
 struct tmpl_xlat_rules_s {
        fr_event_list_t         *runtime_el;            //!< The eventlist to use for runtime instantiation
                                                        ///< of xlats.
@@ -396,11 +548,6 @@ typedef enum {
 #define NUM_COUNT                      (INT16_MIN + 2)
 #define NUM_LAST                       (INT16_MIN + 3)
 
-/** Define entry and head types for attribute reference lists
- *
- */
-FR_DLIST_TYPES(tmpl_attr_list)
-
 /** Different types of filter that can be applied to an attribute reference
  *
  */
@@ -417,7 +564,7 @@ typedef struct {
 /** An element in a list of nested attribute references
  *
  */
-typedef struct {
+struct tmpl_attr_s {
        FR_DLIST_ENTRY(tmpl_attr_list)  _CONST entry;   //!< Entry in the doubly linked list
                                                        ///< of attribute references.
 
@@ -448,7 +595,7 @@ typedef struct {
        tmpl_attr_type_t        _CONST type;            //!< Type of attribute reference.
 
        tmpl_attr_filter_t      _CONST filter;          //!< Filter associated with the attribute reference.
-} tmpl_attr_t;
+};
 
 /** Define manipulation functions for the attribute reference list
  *
@@ -527,7 +674,7 @@ static inline bool ar_is_raw(tmpl_attr_t const *ar)
        return false;
 }
 
-bool ar_is_list_attr(tmpl_attr_t const *ar);
+bool tmpl_attr_is_list_attr(tmpl_attr_t const *ar);
 
 #define ar_num                         filter.num
 #define ar_filter_type                 filter.type
@@ -571,14 +718,11 @@ struct tmpl_s {
                char *unescaped;                //!< Unescaped form of the name, used for TMPL_TYPE_UNRESOLVED
                                                ///< and TMPL_TYPE_REGEX_UNCOMPILED.
 
-               _CONST struct {
+               struct {
                        bool                    ref_prefix;     //!< true if the reference was prefixed
                                                                ///< with a '&'.
                        bool                    was_oid;        //!< Was originally a numeric OID.
 
-                       tmpl_pair_list_t        list;           //!< List to search or insert in.
-                                                               ///< deprecated.
-
                        FR_DLIST_HEAD(tmpl_request_list)        rr;     //!< Request to search or insert in.
                        FR_DLIST_HEAD(tmpl_attr_list)           ar;     //!< Head of the attribute reference list.
                } attribute;
@@ -675,11 +819,11 @@ static inline tmpl_type_t tmpl_type_from_str(char const *type)
  */
  #define tmpl_attr(_tmpl)      &(_tmpl)->data.attribute.ar
 
-static inline FR_DLIST_HEAD(tmpl_request_list) const *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_contains_attr(vpt));
 
-       return &vpt->data.attribute.rr;
+       return &UNCONST(tmpl_t *, vpt)->data.attribute.rr;
 }
 
 /** The number of request references contained within a tmpl
@@ -705,7 +849,7 @@ static inline bool tmpl_attr_head_is_list(tmpl_t const *vpt)
        ar = tmpl_attr_list_head(tmpl_attr(vpt));
        if (unlikely(!ar)) return false;
 
-       return ar_is_list_attr(ar);
+       return tmpl_attr_is_list_attr(ar);
 }
 
 /** Return true if the last attribute reference is "normal"
@@ -905,14 +1049,6 @@ static inline size_t tmpl_attr_num_elements(tmpl_t const *vpt)
 
        return tmpl_attr_list_num_elements(tmpl_attr(vpt));
 }
-
-static inline tmpl_pair_list_t tmpl_list(tmpl_t const *vpt)
-{
-       tmpl_assert_type(tmpl_is_attr(vpt) ||
-                        tmpl_is_attr_unresolved(vpt));
-
-       return vpt->data.attribute.list;
-}
 /** @} */
 
 /** @name Field accessors for #TMPL_TYPE_XLAT
@@ -1060,13 +1196,18 @@ typedef enum {
 
 void                   tmpl_debug(tmpl_t const *vpt) CC_HINT(nonnull);
 
-fr_pair_list_t         *tmpl_list_head(request_t *request, tmpl_pair_list_t list);
+static inline CC_HINT(nonnull) fr_dict_attr_t const *tmpl_list(tmpl_t const *vpt)
+{
+       if (!tmpl_attr_head_is_list(vpt)) return NULL;
 
-fr_radius_packet_t     *tmpl_packet_ptr(request_t *request, tmpl_pair_list_t list_name) CC_HINT(nonnull);
+       return tmpl_attr_list_head(tmpl_attr(vpt))->ar_da;
+}
+
+TALLOC_CTX             *tmpl_list_ctx(request_t *request, tmpl_t const *vpt) CC_HINT(nonnull);
 
-TALLOC_CTX             *tmpl_list_ctx(request_t *request, tmpl_pair_list_t list_name);
+fr_pair_list_t         *tmpl_list_head(request_t *request, tmpl_t const *vpt) CC_HINT(nonnull);
 
-size_t                 tmpl_pair_list_name(tmpl_pair_list_t *out, char const *name, tmpl_pair_list_t default_list) CC_HINT(nonnull);
+fr_radius_packet_t     *tmpl_packet_ptr(request_t *request, tmpl_t const *vpt) 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));
 
@@ -1084,6 +1225,10 @@ tmpl_t                   *tmpl_alloc(TALLOC_CTX *ctx, tmpl_type_t type, fr_token_t quote, char c
  *
  * @{
  */
+bool                   tmpl_attr_is_list_attr(tmpl_attr_t const *ar);
+
+fr_slen_t              tmpl_attr_list_from_substr(fr_dict_attr_t const **da_p, fr_sbuff_t *in);
+
 /** Static default request ref list for the current request
  *
  * Passed as request_def in tmpl_attr_rules_t.
@@ -1104,6 +1249,8 @@ 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_replace(tmpl_t *dst, FR_DLIST_HEAD(tmpl_request_list) const *src);
+
 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,
@@ -1160,7 +1307,7 @@ void                      tmpl_attr_ref_list_debug(FR_DLIST_HEAD(tmpl_attr_list) const *ar_head) CC
 
 void                   tmpl_attr_debug(tmpl_t const *vpt) CC_HINT(nonnull);
 
-int                    tmpl_attr_copy(tmpl_t *dst, tmpl_t const *src) CC_HINT(nonnull);
+void                   tmpl_attr_replace(tmpl_t *dst, FR_DLIST_HEAD(tmpl_attr_list) const *src) CC_HINT(nonnull);
 
 int                    tmpl_attr_set_da(tmpl_t *vpt, fr_dict_attr_t const *da) CC_HINT(nonnull);
 
@@ -1174,7 +1321,7 @@ void                      tmpl_attr_rewrite_num(tmpl_t *vpt, int16_t from, int16_t to) CC_HINT(nonn
 
 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);
+void                   tmpl_attr_set_list(tmpl_t *vpt, fr_dict_attr_t const *list_da) CC_HINT(nonnull);
 
 int                    tmpl_attr_afrom_list(TALLOC_CTX *ctx, tmpl_t **out, tmpl_t const *list,
                                             fr_dict_attr_t const *da) CC_HINT(nonnull);
@@ -1317,8 +1464,6 @@ ssize_t                   tmpl_preparse(char const **out, size_t *outlen, char const *in, size_t
 
 bool                   tmpl_async_required(tmpl_t const *vpt) CC_HINT(nonnull);
 
-fr_pair_t              *tmpl_get_list(request_t *request, tmpl_t const *vpt) CC_HINT(nonnull(2)); /* temporary hack */
-
 int                    tmpl_value_list_insert_tail(FR_DLIST_HEAD(fr_value_box_list) *list, fr_value_box_t *vb, tmpl_t const *vpt) CC_HINT(nonnull);
 
 void                   tmpl_rules_child_init(TALLOC_CTX *ctx, tmpl_rules_t *out, tmpl_rules_t const *parent, tmpl_t *vpt) CC_HINT(nonnull);
index acf6dd4728fde15d1a0a0a84fd0a834f8cd89b4e..013a24e2ee538db7cfecd50278f29ee519fe1971 100644 (file)
@@ -7,12 +7,17 @@ static void test_init(void);
 #define TEST_INIT test_init()
 #endif
 
+/*
+ *     Must come first
+ */
 #include <freeradius-devel/util/acutest.h>
 #include <freeradius-devel/util/acutest_helpers.h>
-#include <freeradius-devel/util/dict_test.h>
-#include <freeradius-devel/server/tmpl_dcursor.h>
-#include <freeradius-devel/server/pair.h>
 
+#include <freeradius-devel/server/pair.h>
+#include <freeradius-devel/server/tmpl.h>
+#include <freeradius-devel/server/tmpl_dcursor.h>
+#include <freeradius-devel/util/dict_test.h>
+#include <freeradius-devel/util/pair.h>
 
 static TALLOC_CTX       *autofree;
 static fr_dict_t       *test_dict;
@@ -76,7 +81,7 @@ static request_t *request_fake_alloc(void)
        fr_pair_t       *leaf_int32_vp ## _x
 
 #define pair_populate(_x) \
-       pair_append_request(&int32_vp ## _x, fr_dict_attr_test_int32); \
+       pair_append_## request(&int32_vp ## _x, fr_dict_attr_test_int32); \
        pair_append_request(&string_vp ## _x, fr_dict_attr_test_string); \
        pair_append_request(&group_vp ## _x, fr_dict_attr_test_group); \
        fr_pair_append_by_da(group_vp ## _x, &child_vp ## _x, &group_vp ## _x->children, fr_dict_attr_test_int16); \
@@ -136,8 +141,7 @@ int _tmpl_setup_and_cursor_init(fr_pair_t **vp_out, tmpl_dcursor_vars_t *vars, r
        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
+                                               .ctx = tmpl_attr_ctx_rules_default(NULL, NULL, test_dict)
                                        }
                                }); 
        TEST_CHECK(vars->vpt!= NULL); 
@@ -166,8 +170,7 @@ int _tmpl_setup_and_cursor_build_init(fr_pair_t **vp_out, tmpl_dcursor_vars_t *v
        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
+                                               .ctx = tmpl_attr_ctx_rules_default(NULL, NULL, test_dict)
                                        }
                                }); 
        TEST_CHECK(vars->vpt != NULL); 
index 7658538d04bf69b3da5cc0e7d3ee1e062ef62d0f..94ec1b78757e9b047c75f52330af0ec15f4e9d70 100644 (file)
@@ -86,71 +86,29 @@ static fr_dict_attr_autoload_t tmpl_dict_attr[] = {
        { NULL }
 };
 
-/** Resolve a #tmpl_t into an #fr_pair_t
+/** Resolve a #tmpl_t into the head of a pair list
  *
  * @param[in] request containing the target lists.
  * @param[in] vpt tmpl to resolve
  * @return a pointer to the list in the #request_t.
- *
- * @This is just a temporary hack.
  */
-fr_pair_t *tmpl_get_list(request_t *request, tmpl_t const *vpt)
+fr_pair_list_t *tmpl_list_head(request_t *request, tmpl_t const *vpt)
 {
        fr_dict_attr_t const *da;
+       fr_pair_t *vp = NULL;
 
-       if (!request) return NULL;
+       if (!request || !tmpl_attr_head_is_list(vpt)) return NULL;
 
        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;
-}
-
-
-/** Resolve attribute #pair_list_t value to an attribute list.
- *
- * The value returned is a pointer to the pointer of the HEAD of a #fr_pair_t list in the
- * #request_t. If the head of the list changes, the pointer will still be valid.
- *
- * @param[in] request containing the target lists.
- * @param[in] list #pair_list_t value to resolve to #fr_pair_t list. Will be NULL if list
- *     name couldn't be resolved.
- * @return a pointer to the HEAD of a list in the #request_t.
- *
- * @see tmpl_dcursor_init
- */
-fr_pair_list_t *tmpl_list_head(request_t *request, tmpl_pair_list_t list)
-{
-       if (!request) return NULL;
-
-       switch (list) {
-       /* Don't add default */
-       case PAIR_LIST_UNKNOWN:
-               break;
-
-       case PAIR_LIST_REQUEST:
-               if (!request->packet) return NULL;
-               return &request->request_pairs;
-
-       case PAIR_LIST_REPLY:
-               if (!request->reply) return NULL;
-               return &request->reply_pairs;
+       if (da == request_attr_request) vp = request->pair_list.request;
+       if (da == request_attr_reply) vp = request->pair_list.reply;
+       if (da == request_attr_control) vp = request->pair_list.control;
+       if (da == request_attr_state) vp = request->pair_list.state;
 
-       case PAIR_LIST_CONTROL:
-               return &request->control_pairs;
+       if (!vp) RWDEBUG2("List \"%s\" is not available", da->name);
 
-       case PAIR_LIST_STATE:
-               return &request->session_state_pairs;
-       }
-
-       RWDEBUG2("List \"%s\" is not available",
-               fr_table_str_by_value(pair_list_table, list, "<INVALID>"));
-
-       return NULL;
+       return &vp->vp_group;
 }
 
 /** Return the correct TALLOC_CTX to alloc #fr_pair_t in, for a list
@@ -161,35 +119,26 @@ fr_pair_list_t *tmpl_list_head(request_t *request, tmpl_pair_list_t list)
  * freed too.
  *
  * @param[in] request containing the target lists.
- * @param[in] list #pair_list_t value to resolve to TALLOC_CTX.
+ * @param[in] vpt to resolve to a TALLOC_CTX.
  * @return
  *     - TALLOC_CTX on success.
  *     - NULL on failure.
  *
  * @see tmpl_pair_list
  */
-TALLOC_CTX *tmpl_list_ctx(request_t *request, tmpl_pair_list_t list)
+TALLOC_CTX *tmpl_list_ctx(request_t *request, tmpl_t const *vpt)
 {
-       if (!request) return NULL;
-
-       switch (list) {
-       case PAIR_LIST_REQUEST:
-               return request->request_ctx;
-
-       case PAIR_LIST_REPLY:
-               return request->reply_ctx;
-
-       case PAIR_LIST_CONTROL:
-               return request->control_ctx;
-
-       case PAIR_LIST_STATE:
-               return request->session_state_ctx;
+       fr_dict_attr_t const *da;
+       
+       if (!request || !tmpl_attr_head_is_list(vpt)) return NULL;
 
-       /* Don't add default */
-       case PAIR_LIST_UNKNOWN:
-               break;
-       }
+       da = ((tmpl_attr_t *)tmpl_attr_list_head(&vpt->data.attribute.ar))->ar_da;
 
+       if (da == request_attr_request) return request->request_ctx;
+       if (da == request_attr_reply) return request->reply_ctx;;
+       if (da == request_attr_control) return request->control_ctx;
+       if (da == request_attr_state) return request->session_state_ctx;
+               
        return NULL;
 }
 
@@ -199,28 +148,23 @@ TALLOC_CTX *tmpl_list_ctx(request_t *request, tmpl_pair_list_t list)
  * for the current #request_t.
  *
  * @param[in] request To resolve list in.
- * @param[in] list #pair_list_t value to resolve to #fr_radius_packet_t.
+ * @param[in] vpt #pair_list_t value to resolve to #fr_radius_packet_t.
  * @return
  *     - #fr_radius_packet_t on success.
  *     - NULL on failure.
  *
  * @see tmpl_pair_list
  */
-fr_radius_packet_t *tmpl_packet_ptr(request_t *request, tmpl_pair_list_t list)
+fr_radius_packet_t *tmpl_packet_ptr(request_t *request, tmpl_t const *vpt)
 {
-       switch (list) {
-       /* Don't add default */
-       case PAIR_LIST_STATE:
-       case PAIR_LIST_CONTROL:
-       case PAIR_LIST_UNKNOWN:
-               return NULL;
-
-       case PAIR_LIST_REQUEST:
-               return request->packet;
-
-       case PAIR_LIST_REPLY:
-               return request->reply;
-       }
+               fr_dict_attr_t const *da;
+       
+       if (!request || !tmpl_attr_head_is_list(vpt)) return NULL;
+
+       da = ((tmpl_attr_t *)tmpl_attr_list_head(&vpt->data.attribute.ar))->ar_da;
+
+       if (da == request_attr_request) return request->packet;
+       if (da == request_attr_reply) return request->reply;
 
        return NULL;
 }
@@ -1012,7 +956,7 @@ int tmpl_find_or_add_vp(fr_pair_t **out, request_t *request, tmpl_t const *vpt)
                TALLOC_CTX      *ctx;
                fr_pair_list_t  *head;
 
-               tmpl_pair_list_and_ctx(ctx, head, request, tmpl_request(vpt), tmpl_list(vpt));
+               tmpl_pair_list_and_ctx(ctx, head, request, tmpl_request(vpt), vpt);
                if (!head) return -1;
 
                MEM(vp = fr_pair_afrom_da(ctx, tmpl_attr_tail_da(vpt)));
@@ -1193,7 +1137,7 @@ static int tmpl_eval_pair_virtual(TALLOC_CTX *ctx, FR_DLIST_HEAD(fr_value_box_li
         *      If there's no packet, we can't print any attribute
         *      referencing it.
         */
-       packet = tmpl_packet_ptr(request, tmpl_list(vpt));
+       packet = tmpl_packet_ptr(request, vpt);
        if (!packet) return 0;
 
        if (tmpl_attr_tail_da(vpt) == attr_packet_type) {
index 3f6970e4006ccb86beaaa41073c8c8cbd081d33b..250daf9609897dcf625a84017147fe804f93a26f 100644 (file)
  *
  * @copyright 2014-2020 The FreeRADIUS server project
  */
+#include "lib/server/request.h"
+#include "lib/util/sbuff.h"
 #include "lib/util/strerror.h"
+#include "talloc.h"
 RCSID("$Id$")
 
 #define _TMPL_PRIVATE 1
@@ -88,12 +91,6 @@ TMPL_REQUEST_REF_DEF(tmpl_request_def_current, REQUEST_CURRENT);
  */
 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
@@ -142,17 +139,6 @@ static fr_table_num_ordered_t const attr_table[] = {
 };
 static size_t attr_table_len = NUM_ELEMENTS(attr_table);
 
-/** Map keywords to #pair_list_t values
- */
-fr_table_num_ordered_t const pair_list_table[] = {
-       { L("request"),         PAIR_LIST_REQUEST               },
-       { L("reply"),           PAIR_LIST_REPLY                 },
-       { L("control"),         PAIR_LIST_CONTROL               },              /* New name should have priority */
-       { L("config"),          PAIR_LIST_CONTROL               },
-       { L("session-state"),   PAIR_LIST_STATE                 },
-};
-size_t pair_list_table_len = NUM_ELEMENTS(pair_list_table);
-
 /** Map keywords to #tmpl_request_ref_t values
  */
 fr_table_num_sorted_t const tmpl_request_ref_table[] = {
@@ -316,7 +302,6 @@ void tmpl_attr_debug(tmpl_t const *vpt)
                i++;
        }
 
-       FR_FAULT_LOG("list: %s", fr_table_str_by_value(pair_list_table, vpt->data.attribute.list, "<INVALID>"));
        tmpl_attr_ref_list_debug(tmpl_attr(vpt));
 }
 
@@ -388,22 +373,6 @@ void tmpl_debug(tmpl_t const *vpt)
        }
 }
 
-/** 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
@@ -417,81 +386,46 @@ bool ar_is_list_attr(tmpl_attr_t const *ar)
  * @{
  */
 
-/** Resolve attribute name to a #pair_list_t value.
- *
- * Check the name string for #pair_list_t qualifiers and write a #pair_list_t value
- * for that list to out. This value may be passed to #tmpl_pair_list, along with the current
- * #request_t, to get a pointer to the actual list in the #request_t.
- *
- * If we're sure we've definitely found a list qualifier token delimiter (``:``) but the
- * string doesn't match a #tmpl_pair_list qualifier, return 0 and write #PAIR_LIST_UNKNOWN
- * to out.
- *
- * If we can't find a string that looks like a request qualifier, set out to def, and
- * return 0.
- *
- * @note #tmpl_pair_list_name should be called before passing a name string that may
- *     contain qualifiers to #fr_dict_attr_by_name.
- *
- * @param[out] out Where to write the list qualifier.
- * @param[in] name String containing list qualifiers to parse.
- * @param[in] def the list to return if no qualifiers were found.
- * @return 0 if no valid list 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 name (if any).
+
+/** Indicate whether this attribute reference represents one of packets or legacy lists
  *
- * @see pair_list
- * @see tmpl_pair_list
+ * @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.
  */
-size_t tmpl_pair_list_name(tmpl_pair_list_t *out, char const *name, tmpl_pair_list_t def)
+bool tmpl_attr_is_list_attr(tmpl_attr_t const *ar)
 {
-       char const *p = name;
-       char const *q;
-
-       /*
-        *      Try and determine the end of the token
-        */
-       for (q = p; fr_dict_attr_allowed_chars[(uint8_t) *q]; q++);
-
-       switch (*q) {
-       /*
-        *      It's a bareword made up entirely of dictionary chars
-        *      check and see if it's a list qualifier, and if it's
-        *      not, return the def and say we couldn't parse
-        *      anything.
-        */
-       case '\0':
-               *out = fr_table_value_by_substr(pair_list_table, p, (q - p), PAIR_LIST_UNKNOWN);
-               if (*out != PAIR_LIST_UNKNOWN) return q - p;
-               *out = def;
-               return 0;
-
-       /*
-        *      It may be a list qualifier delimiter
-        */
-       case ':':
-       {
-               char const *d = q + 1;
+       if (!ar_is_normal(ar)) return false;
 
-               if (isdigit((int) *d)) {
-                       while (isdigit((int) *d)) d++;
+       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);
+}
 
-                       if (!fr_dict_attr_allowed_chars[(uint8_t) *d]) {
-                               *out = def;
-                               return 0;
-                       }
-               }
 
-               *out = fr_table_value_by_substr(pair_list_table, p, (q - p), PAIR_LIST_UNKNOWN);
-               if (*out == PAIR_LIST_UNKNOWN) return 0;
+/** Parse one a single list reference
+ *
+ * @param[out] da_p    attribute representing a list.
+ * @param[in] in       Sbuff to read request references from.
+ * @return
+ *     - > 0 the number of bytes parsed.
+ *      - 0 no list qualifier found.
+ */
+fr_slen_t tmpl_attr_list_from_substr(fr_dict_attr_t const **da_p, fr_sbuff_t *in)
+{
+       fr_dict_attr_t const *da;
+       fr_sbuff_t our_in = FR_SBUFF(in);
 
-               return (q + 1) - name; /* Consume the list and delimiter */
+       if ((da = fr_sbuff_adv_past_strcase(&our_in, request_attr_request->name, request_attr_request->name_len)) ||
+           (da = fr_sbuff_adv_past_strcase(&our_in, request_attr_reply->name, request_attr_reply->name_len)) ||
+           (da = fr_sbuff_adv_past_strcase(&our_in, request_attr_control->name, request_attr_control->name_len)) ||
+           (da = fr_sbuff_adv_past_strcase(&our_in, request_attr_state->name, request_attr_state->name_len))) {
+               *da_p = da;
+               FR_SBUFF_SET_RETURN(in, &our_in);
        }
 
-       default:
-               *out = def;
-               return 0;
-       }
+       return 0;
 }
 
  /** Allocate a new request reference and add it to the end of the attribute reference list
@@ -534,6 +468,15 @@ void tmpl_request_ref_list_acopy(TALLOC_CTX *ctx,
        *out = rql;
 }
 
+/** Replace all existing request references
+ *
+ */
+void tmpl_request_ref_replace(tmpl_t *dst, FR_DLIST_HEAD(tmpl_request_list) const *src)
+{
+       tmpl_request_list_talloc_reverse_free(&dst->data.attribute.rr);
+       tmpl_request_ref_list_copy(dst, &dst->data.attribute.rr, src);
+}
+
 /** Dump a request list to stderr
  *
  */
@@ -612,7 +555,11 @@ static fr_slen_t tmpl_request_ref_list_from_substr(TALLOC_CTX *ctx, tmpl_attr_er
        fr_sbuff_marker_t       m;
        bool                    seen_outer = false;
 
-       if (!at_rules) at_rules = &default_rules.attr;
+       if (!at_rules) {
+               at_rules = &default_rules.attr;
+       } else {
+               fr_assert_msg(tmpl_attr_ctx_rules_is_default(at_rules), "attr rules ctx must be of type \"defaults\" to parse request qualifiers");
+       }
 
        /*
         *      We could make the caller do this but as this
@@ -625,7 +572,6 @@ static fr_slen_t tmpl_request_ref_list_from_substr(TALLOC_CTX *ctx, tmpl_attr_er
        for (depth = 0; depth < TMPL_MAX_REQUEST_REF_NESTING; depth++) {
                bool end;
 
-
                /*
                 *      Search for a known request reference like
                 *      'current', or 'parent'.
@@ -642,8 +588,8 @@ static fr_slen_t tmpl_request_ref_list_from_substr(TALLOC_CTX *ctx, tmpl_attr_er
                         *      reference.
                         */
                default_ref:
-                       if ((depth == 0) && at_rules->request_def) {
-                               tmpl_request_ref_list_copy(ctx, out, at_rules->request_def);
+                       if ((depth == 0) && at_rules->ctx.defaults.request) {
+                               tmpl_request_ref_list_copy(ctx, out, at_rules->ctx.defaults.request);
                        }
                        break;
                }
@@ -686,7 +632,7 @@ static fr_slen_t tmpl_request_ref_list_from_substr(TALLOC_CTX *ctx, tmpl_attr_er
                        goto default_ref;
                }
 
-               if (at_rules->parent || at_rules->disallow_qualifiers) {
+               if (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;
 
@@ -730,7 +676,6 @@ static fr_slen_t tmpl_request_ref_list_from_substr(TALLOC_CTX *ctx, tmpl_attr_er
        }
 
        FR_SBUFF_SET_RETURN(in, &m);
-
 }
 
 /** Parse one or more request references, allocing a new list and adding the references to it
@@ -872,7 +817,8 @@ void tmpl_set_name(tmpl_t *vpt, fr_token_t quote, char const *name, ssize_t len)
  */
 void tmpl_set_dict_def(tmpl_t *vpt, fr_dict_t const *dict)
 {
-       vpt->rules.attr.dict_def = dict;
+       fr_assert_msg(tmpl_attr_ctx_rules_is_default(&vpt->rules.attr), "can only set default dictionary on \"defaults\" ctx");
+       vpt->rules.attr.ctx.defaults.dict = dict;
 }
 
 /** Initialise a tmpl using a format string to create the name
@@ -998,11 +944,24 @@ tmpl_t *tmpl_alloc(TALLOC_CTX *ctx, tmpl_type_t type, fr_token_t quote, char con
  *
  * @{
  */
+static inline CC_HINT(always_inline)
+tmpl_attr_t *tmpl_attr_alloc(TALLOC_CTX *ctx, tmpl_attr_type_t type)
+{
+       tmpl_attr_t     *ar;
+       MEM(ar = talloc(ctx, tmpl_attr_t));
+       *ar = (tmpl_attr_t){
+               .type = type,
+               .filter = {
+                       .num = NUM_UNSPEC
+               }
+       };
+       return ar;
+}
 
 /** Allocate a new attribute reference and add it to the end of the attribute reference list
  *
  */
-static tmpl_attr_t *tmpl_attr_add(tmpl_t *vpt, tmpl_attr_type_t type)
+static tmpl_attr_t *tmpl_attr_ainsert_tail(tmpl_t *vpt, tmpl_attr_type_t type)
 {
        tmpl_attr_t     *ar;
        TALLOC_CTX      *ctx;
@@ -1013,15 +972,8 @@ static tmpl_attr_t *tmpl_attr_add(tmpl_t *vpt, tmpl_attr_type_t type)
                ctx = tmpl_attr_list_tail(tmpl_attr(vpt));
        }
 
-       MEM(ar = talloc(ctx, tmpl_attr_t));
-       *ar = (tmpl_attr_t){
-               .type = type,
-               .filter = {
-                       .num = NUM_UNSPEC
-               }
-       };
+       ar = tmpl_attr_alloc(ctx, type);
        tmpl_attr_list_insert_tail(tmpl_attr(vpt), ar);
-
        return ar;
 }
 
@@ -1063,58 +1015,62 @@ int tmpl_afrom_value_box(TALLOC_CTX *ctx, tmpl_t **out, fr_value_box_t *data, bo
        return 0;
 }
 
-/** Copy a list of attribute and request references from one tmpl to another
- *
+/** Copy an attribute reference
  */
-int tmpl_attr_copy(tmpl_t *dst, tmpl_t const *src)
+static tmpl_attr_t *tmpl_attr_acopy(TALLOC_CTX *ctx, tmpl_attr_t const *src_ar)
 {
-       tmpl_attr_t *src_ar = NULL, *dst_ar;
+       tmpl_attr_t *dst_ar;
 
-       /*
-        *      Clear any existing attribute references
-        */
-       if (tmpl_attr_list_num_elements(tmpl_attr(dst)) > 0) tmpl_attr_list_talloc_reverse_free(tmpl_attr(dst));
+       dst_ar = tmpl_attr_alloc(ctx, src_ar->ar_type);
+       switch (src_ar->type) {
+       case TMPL_ATTR_TYPE_NORMAL:
+               dst_ar->ar_da = src_ar->ar_da;
+               break;
 
-       while ((src_ar = tmpl_attr_list_next(tmpl_attr(src), src_ar))) {
-               dst_ar = tmpl_attr_add(dst, src_ar->type);
+       case TMPL_ATTR_TYPE_UNSPEC:     /* Nothing to copy */
+               break;
 
-               switch (src_ar->type) {
-               case TMPL_ATTR_TYPE_NORMAL:
-                       dst_ar->ar_da = src_ar->ar_da;
-                       break;
+       case TMPL_ATTR_TYPE_UNKNOWN:
+               dst_ar->ar_unknown = fr_dict_unknown_afrom_da(dst_ar, src_ar->ar_unknown);
+               break;
 
-               case TMPL_ATTR_TYPE_UNSPEC:     /* Nothing to copy */
-                       break;
+       case TMPL_ATTR_TYPE_UNRESOLVED:
+               dst_ar->ar_unresolved = talloc_bstrdup(dst_ar, src_ar->ar_unresolved);
+               break;
 
-               case TMPL_ATTR_TYPE_UNKNOWN:
-                       dst_ar->ar_unknown = fr_dict_unknown_afrom_da(dst_ar, src_ar->ar_unknown);
-                       break;
+       default:
+               if (!fr_cond_assert(0)) return -1;
+       }
+       dst_ar->ar_num = src_ar->ar_num;
 
-               case TMPL_ATTR_TYPE_UNRESOLVED:
-                       dst_ar->ar_unresolved = talloc_bstrdup(dst_ar, src_ar->ar_unresolved);
-                       break;
+       return dst_ar;
+}
 
-               default:
-                       if (!fr_cond_assert(0)) return -1;
-               }
-               dst_ar->ar_num = src_ar->ar_num;
+static void tmpl_attr_list_copy(TALLOC_CTX *ctx, FR_DLIST_HEAD(tmpl_attr_list) *dst, FR_DLIST_HEAD(tmpl_attr_list) const *src)
+{
+       tmpl_attr_t *tail = tmpl_attr_list_tail(dst);
+       TALLOC_CTX *ar_ctx = tail ? tail : ctx;
+
+       tmpl_attr_list_foreach(src, src_ar) {
+               tmpl_attr_t *dst_ar;
+               
+               ar_ctx = dst_ar = tmpl_attr_acopy(ar_ctx, src_ar);
+               tmpl_attr_list_insert_tail(dst, dst_ar);
        }
+}
 
+/** Replace the list of attribute references in one tmpl with the attribute refereces from another
+ *
+ */
+void tmpl_attr_replace(tmpl_t *dst, FR_DLIST_HEAD(tmpl_attr_list) const *src)
+{
        /*
-        *      Clear any existing request references
-        *      and copy the ones from the source.
-        */
-       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...
+        *      Clear any existing attribute references
         */
-       dst->data.attribute.list = src->data.attribute.list;
+       if (tmpl_attr_list_num_elements(tmpl_attr(dst)) > 0) tmpl_attr_list_talloc_reverse_free(tmpl_attr(dst));
+       tmpl_attr_list_copy(dst, tmpl_attr(dst), src);
 
        TMPL_ATTR_VERIFY(dst);
-
-       return 0;
 }
 
 /** Replace the current attribute reference
@@ -1137,10 +1093,10 @@ int tmpl_attr_set_da(tmpl_t *vpt, fr_dict_attr_t const *da)
         *      Unknown attributes get copied
         */
        if (da->flags.is_unknown) {
-               ref = tmpl_attr_add(vpt, TMPL_ATTR_TYPE_UNKNOWN);
+               ref = tmpl_attr_ainsert_tail(vpt, TMPL_ATTR_TYPE_UNKNOWN);
                ref->da = ref->ar_unknown = fr_dict_unknown_afrom_da(vpt, da);
        } else {
-               ref = tmpl_attr_add(vpt, TMPL_ATTR_TYPE_NORMAL);
+               ref = tmpl_attr_ainsert_tail(vpt, TMPL_ATTR_TYPE_NORMAL);
                ref->da = da;
        }
        ref->ar_parent = fr_dict_root(fr_dict_by_da(da));       /* Parent is the root of the dictionary */
@@ -1181,7 +1137,7 @@ int tmpl_attr_set_leaf_da(tmpl_t *vpt, fr_dict_attr_t const *da)
                 */
                talloc_free_children(ref);
        } else {
-               ref = tmpl_attr_add(vpt, da->flags.is_unknown ? TMPL_ATTR_TYPE_UNKNOWN : TMPL_ATTR_TYPE_NORMAL);
+               ref = tmpl_attr_ainsert_tail(vpt, da->flags.is_unknown ? TMPL_ATTR_TYPE_UNKNOWN : TMPL_ATTR_TYPE_NORMAL);
        }
 
 
@@ -1212,7 +1168,7 @@ void tmpl_attr_set_leaf_num(tmpl_t *vpt, int16_t num)
        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);
+               ar = tmpl_attr_ainsert_tail(vpt, TMPL_ATTR_TYPE_UNKNOWN);
        } else {
                ar = tmpl_attr_list_tail(tmpl_attr(vpt));
        }
@@ -1270,9 +1226,24 @@ void tmpl_attr_set_request_ref(tmpl_t *vpt, FR_DLIST_HEAD(tmpl_request_list) con
        TMPL_ATTR_VERIFY(vpt);
 }
 
-void tmpl_attr_set_list(tmpl_t *vpt, tmpl_pair_list_t list)
+void tmpl_attr_set_list(tmpl_t *vpt, fr_dict_attr_t const *list_da)
 {
-       vpt->data.attribute.list = list;
+       if (tmpl_attr_head_is_list(vpt)) {
+               tmpl_attr_t     *ar, *next;
+
+               tmpl_attr_list_pop_head(tmpl_attr(vpt));
+
+               ar = tmpl_attr_alloc(vpt, TMPL_ATTR_TYPE_NORMAL);
+               ar->ar_da = list_da;
+
+               tmpl_attr_list_insert_head(tmpl_attr(vpt), ar);
+
+               /*
+                *      Fixup the attribute hierarchy
+                */
+               next = tmpl_attr_list_next(tmpl_attr(vpt), ar);
+               if (next) talloc_steal(ar, next);
+       }
 
        TMPL_ATTR_VERIFY(vpt);
 }
@@ -1288,12 +1259,13 @@ int tmpl_attr_afrom_list(TALLOC_CTX *ctx, tmpl_t **out, tmpl_t const *list, fr_d
        ssize_t slen;
 
        MEM(vpt = tmpl_alloc(ctx, TMPL_TYPE_ATTR, T_BARE_WORD, NULL, 0));
-
+       
        /*
         *      Copies request refs and the list ref
         */
-       tmpl_attr_copy(vpt, list);
-       tmpl_attr_set_list(vpt, tmpl_list(list));       /* Remove when lists are attributes */
+       tmpl_attr_replace(vpt, tmpl_attr(list));
+       tmpl_request_ref_replace(vpt, tmpl_request(list));
+       
        tmpl_attr_set_leaf_da(vpt, da);                 /* This should add a new da when lists are attributes */
        tmpl_attr_set_leaf_num(vpt, tmpl_attr_tail_num(list));
 
@@ -1322,6 +1294,26 @@ int tmpl_attr_afrom_list(TALLOC_CTX *ctx, tmpl_t **out, tmpl_t const *list, fr_d
 }
 /** @} */
 
+/** Safely duplicate a set of attribute rules
+ */
+static void tmpl_attr_rules_copy(TALLOC_CTX *ctx, tmpl_attr_rules_t *dst, tmpl_attr_rules_t const *src)
+{
+       memcpy(dst, src, sizeof(*dst));
+
+       if (tmpl_attr_ctx_rules_is_default(src)) {
+               /*
+               *       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 (tmpl_attr_ctx_rules_default_request(src)) {
+                       tmpl_request_ref_list_acopy(ctx, &dst->ctx.defaults.request, src->ctx.defaults.request);
+               }
+       }
+}
+
 /** Insert an attribute reference into a tmpl
  *
  * Not all attribute references can be used to create new attributes,
@@ -1612,6 +1604,7 @@ static inline int tmpl_attr_afrom_attr_substr(TALLOC_CTX *ctx, tmpl_attr_error_t
        fr_sbuff_marker_t       m_s;
        fr_dict_attr_err_t      dict_err;
        fr_dict_attr_t const    *our_parent = parent;
+       fr_dict_t const         *dict_def = NULL;
 
        fr_sbuff_marker(&m_s, name);
 
@@ -1632,12 +1625,14 @@ static inline int tmpl_attr_afrom_attr_substr(TALLOC_CTX *ctx, tmpl_attr_error_t
                goto error;
        }
 
+       if (tmpl_attr_ctx_rules_is_default(at_rules)) dict_def = tmpl_attr_ctx_rules_default_dict(at_rules);
+
        /*
         *      No parent means we need to go hunting through all the dictionaries
         */
        if (!our_parent) {
                (void)fr_dict_attr_search_by_qualified_name_substr(&dict_err, &da,
-                                                                  at_rules->dict_def,
+                                                                  dict_def,
                                                                   name, p_rules ? p_rules->terminals : NULL,
                                                                   !at_rules->disallow_internal,
                                                                   at_rules->allow_foreign);
@@ -1729,7 +1724,7 @@ static inline int tmpl_attr_afrom_attr_substr(TALLOC_CTX *ctx, tmpl_attr_error_t
         *      first, and then run the rest of the logic in this
         *      function.
         */
-       if (!namespace && at_rules->dict_def) our_parent = namespace = fr_dict_root(at_rules->dict_def);
+       if (!namespace && dict_def) our_parent = namespace = fr_dict_root(dict_def);
        if (!namespace && !at_rules->disallow_internal) our_parent = namespace = fr_dict_root(fr_dict_internal());
        if (!namespace) {
                fr_strerror_const("Attribute references must be qualified with a protocol when used here");
@@ -1844,13 +1839,13 @@ check_attr:
         */
        if (!at_rules->allow_foreign || at_rules->disallow_internal) {
                fr_dict_t const *found_in = fr_dict_by_da(da);
-               fr_dict_t const *dict_def = at_rules->dict_def ? at_rules->dict_def : fr_dict_internal();
+               fr_dict_t const *our_dict_def = dict_def ? dict_def : fr_dict_internal();
 
                /*
                 *      Parent is the dict root if this is the first ref in the
                 *      chain.
                 */
-               if (!our_parent) our_parent = fr_dict_root(dict_def);
+               if (!our_parent) our_parent = fr_dict_root(our_dict_def);
 
                /*
                 *      Even if allow_foreign is false, if disallow_internal is not
@@ -1883,7 +1878,7 @@ check_attr:
                    !at_rules->allow_foreign && !fr_dict_compatible(found_in, fr_dict_by_da(our_parent))) {
                        fr_strerror_printf("Foreign %s attribute found.  Only %s attributes are allowed here",
                                           fr_dict_root(found_in)->name,
-                                          fr_dict_root(dict_def)->name);
+                                          fr_dict_root(our_dict_def)->name);
                        if (err) *err = TMPL_ATTR_ERROR_FOREIGN_NOT_ALLOWED;
                        fr_sbuff_set(name, &m_s);
                        goto error;
@@ -1931,8 +1926,8 @@ do_suffix:
                         *      if there's a real dictionary, and this reference is to group which is in fact
                         *      the internal dict, then just keep using our dict_def.
                         */
-                       if (at_rules->dict_def && (namespace == fr_dict_root(fr_dict_internal()))) {
-                               our_parent = namespace = fr_dict_root(at_rules->dict_def);
+                       if (dict_def && (namespace == fr_dict_root(fr_dict_internal()))) {
+                               our_parent = namespace = fr_dict_root(dict_def);
                        }
                        break;
 
@@ -2015,7 +2010,7 @@ do_suffix:
  *                                                     are unqualified.
  *                             - request_def           The default #request_t to set if no
  *                                                     #tmpl_request_ref_t qualifiers are found in name.
- *                             - list_def              The default list to set if no #pair_list_t
+ *                             - context               The default list to set if no #pair_list_t
  *                                                     qualifiers are found in the name.
  *                             - allow_unknown         If true attributes in the format accepted by
  *                                                     #fr_dict_unknown_afrom_oid_substr will be allowed,
@@ -2056,7 +2051,6 @@ ssize_t tmpl_afrom_attr_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err,
        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) &&
@@ -2077,6 +2071,9 @@ ssize_t tmpl_afrom_attr_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err,
 
        /*
         *      Check to see if we expect a reference prefix
+        *
+        *      FIXME - These should probably only be allowed
+        *      in the defaults context.
         */
        switch (at_rules->prefix) {
        case TMPL_ATTR_REF_PREFIX_YES:
@@ -2116,33 +2113,103 @@ ssize_t tmpl_afrom_attr_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err,
        if (fr_sbuff_adv_past_strcase_literal(&our_name, "raw.")) is_raw = true;
 
        /*
-        *      Parse one or more request references
+        *      Defaults ctx allow request and list
+        *      qualifiers.
+        *
+        *      First parse any request references,
+        *      then start on attribute references.
+        *
+        *      Finally if no list was added, prepend that.
         */
-       ret = tmpl_request_ref_list_from_substr(vpt, NULL,
-                                               &vpt->data.attribute.rr,
-                                               &our_name,
-                                               p_rules,
-                                               at_rules);
-       if (ret < 0) {
-       error:
-               *out = NULL;
-               talloc_free(vpt);
-               FR_SBUFF_ERROR_RETURN(&our_name);
-       }
+       if (tmpl_attr_ctx_rules_is_default(at_rules)) {
+               ret = tmpl_request_ref_list_from_substr(vpt, NULL,
+                                                       &vpt->data.attribute.rr,
+                                                       &our_name,
+                                                       p_rules,
+                                                       at_rules);
+               if (ret < 0) {
+               error:
+                       *out = NULL;
+                       talloc_free(vpt);
+                       FR_SBUFF_ERROR_RETURN(&our_name);
+               }
+
+               ret = tmpl_attr_afrom_attr_substr(vpt, err,
+                                                 vpt,
+                                                 NULL, NULL,
+                                                 &our_name, p_rules, at_rules, 0);
+               if (ret < 0) goto error;
 
-       fr_sbuff_marker(&m_l, &our_name);
+               /*
+               *       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_ctx_rules_is_default(at_rules) && !tmpl_attr_head_is_list(vpt)) {
+                       fr_dict_attr_t const *list_da;
 
+                       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())
+                       };
+
+                       /*
+                       *       If there's no default list, then we default
+                       *       to the request list.
+                       */
+                       list_da = tmpl_attr_ctx_rules_default_list(at_rules);
+                       if (list_da) {
+                               ar->ar_da = list_da;
+                       } else {
+                               ar->ar_da = request_attr_request;
+                       }
+
+                       /*
+                       *       Prepend the list ref so it gets evaluated
+                       *       first.
+                       */
+                       tmpl_attr_list_insert_head(&vpt->data.attribute.ar, ar);
+               }
        /*
-        *      Start parsing attribute references
-        *
-        *      This includes lists, like request,
-        *      reply, control etc...
+        *      Inherited ctx copy all the attribute
+        *      and request references from another
+        *      tmpl, and resume parsing in the context
+        *      of the final attr.
+        */
+       } else if (tmpl_attr_ctx_rules_is_inherit(at_rules)) {
+               FR_DLIST_HEAD(tmpl_request_list) const  *src_rq_list = tmpl_attr_ctx_rules_inherit_request(at_rules);
+               FR_DLIST_HEAD(tmpl_attr_list) const     *src_ar_list = tmpl_attr_ctx_rules_inherit_attr(at_rules);
+               FR_DLIST_HEAD(tmpl_request_list)        *dst_rq_list = tmpl_request(vpt);
+               FR_DLIST_HEAD(tmpl_attr_list)           *dst_ar_list = tmpl_attr(vpt);
+               fr_dict_attr_t const                    *parent;
+
+               if (src_rq_list) tmpl_request_ref_list_copy(vpt, dst_rq_list, src_rq_list);
+               if (src_ar_list) tmpl_attr_list_copy(vpt, dst_ar_list, src_ar_list);
+       
+               if (!tmpl_attr_list_empty(dst_ar_list)) parent = tmpl_attr_list_tail(dst_ar_list)->ar_da;
+
+               ret = tmpl_attr_afrom_attr_substr(vpt, err,
+                                                 vpt,
+                                                 parent, parent,
+                                                 &our_name, p_rules, at_rules, 0);
+               if (ret < 0) goto error;
+       /*
+        *      Nested ctx resume parsing in the
+        *      context of the DA passed in.
         */
-       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;
+       } else if (tmpl_attr_ctx_rules_is_nested(at_rules)) {
+               fr_dict_attr_t const            *parent = tmpl_attr_ctx_rules_nested_da(at_rules);
+
+               ret = tmpl_attr_afrom_attr_substr(vpt, err,
+                                                 vpt,
+                                                 parent, parent,
+                                                 &our_name, p_rules, at_rules, 0);
+               if (ret < 0) goto error;        
+       }
 
        /*
         *      Check to see if the user wants the leaf
@@ -2155,61 +2222,10 @@ ssize_t tmpl_afrom_attr_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err,
         */
        if (tmpl_is_attr(vpt) && is_raw) tmpl_attr_to_raw(vpt);
 
-       /*
-        *      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())
-               };
-
-               switch (at_rules->list_def) {
-               default:
-               case PAIR_LIST_REQUEST:
-                       ar->ar_da = request_attr_request;
-                       break;
-
-               case PAIR_LIST_REPLY:
-                       ar->ar_da = request_attr_reply;
-                       break;
-
-               case PAIR_LIST_CONTROL:
-                       ar->ar_da = request_attr_control;
-                       break;
-
-               case PAIR_LIST_STATE:
-                       ar->ar_da = request_attr_state;
-                       break;
-               }
-
-               /*
-                *      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));
-       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);
-       }
+       
+       vpt->rules = *t_rules;
+       tmpl_attr_rules_copy(vpt, &vpt->rules.attr, &t_rules->attr);
 
        if (tmpl_is_attr(vpt)) {
                /*
@@ -3245,7 +3261,7 @@ tmpl_t *tmpl_copy(TALLOC_CTX *ctx, tmpl_t const *in)
        /*
         *      Copy attribute references
         */
-       if (tmpl_contains_attr(vpt) && unlikely(tmpl_attr_copy(vpt, in) < 0)) goto error;
+       if (tmpl_contains_attr(vpt)) tmpl_attr_replace(vpt, tmpl_attr(in));
 
        /*
         *      Copy flags for all regex flavours (and possibly recompile the regex)
@@ -3631,53 +3647,55 @@ static inline CC_HINT(always_inline) int tmpl_attr_resolve(tmpl_t *vpt, tmpl_res
 {
        tmpl_attr_t             *ar = NULL, *next, *prev;
        fr_dict_attr_t const    *da;
-       fr_dict_t const         *dict_def;
+       fr_dict_t const         *dict_def = NULL;
 
        fr_assert(tmpl_is_attr_unresolved(vpt));
 
        TMPL_VERIFY(vpt);
 
-       dict_def = vpt->rules.attr.dict_def;
-       if (!dict_def || tr_rules->force_dict_def) dict_def = tr_rules->dict_def;
-
-       /*
-        *      First component is special becase we may need
-        *      to search for it in multiple dictionaries.
-        *
-        *      This emulates what's done in the initial
-        *      tokenizer function.
-        */
-       ar = tmpl_attr_list_head(tmpl_attr(vpt));
-       if (ar->type == TMPL_ATTR_TYPE_UNRESOLVED) {
-               (void)fr_dict_attr_search_by_name_substr(NULL,
-                                                        &da,
-                                                        dict_def,
-                                                        &FR_SBUFF_IN(ar->ar_unresolved,
-                                                                     talloc_array_length(ar->ar_unresolved) - 1),
-                                                        NULL,
-                                                        !vpt->rules.attr.disallow_internal,
-                                                        vpt->rules.attr.allow_foreign);
-               if (!da) return -2;     /* Can't resolve, maybe the caller can resolve later */
-
-               ar->ar_type = TMPL_ATTR_TYPE_NORMAL;
-               ar->ar_da = da;
-               ar->ar_parent = fr_dict_root(fr_dict_by_da(da));
+       if (tmpl_attr_ctx_rules_is_default(&vpt->rules.attr)) {
+               dict_def = tmpl_attr_ctx_rules_default_dict(&vpt->rules.attr);
+               if (!dict_def || tr_rules->force_dict_def) dict_def = tr_rules->dict_def;
 
                /*
-                *      Record the dictionary that was
-                *      successfully used for resolution.
-                */
-               vpt->rules.attr.dict_def = tr_rules->dict_def;
+               *       First component is special becase we may need
+               *       to search for it in multiple dictionaries.
+               *
+               *       This emulates what's done in the initial
+               *       tokenizer function.
+               */
+               ar = tmpl_attr_list_head(tmpl_attr(vpt));
+               if (ar->type == TMPL_ATTR_TYPE_UNRESOLVED) {
+                       (void)fr_dict_attr_search_by_name_substr(NULL,
+                                                               &da,
+                                                               dict_def,
+                                                               &FR_SBUFF_IN(ar->ar_unresolved,
+                                                                       talloc_array_length(ar->ar_unresolved) - 1),
+                                                               NULL,
+                                                               !vpt->rules.attr.disallow_internal,
+                                                               vpt->rules.attr.allow_foreign);
+                       if (!da) return -2;     /* Can't resolve, maybe the caller can resolve later */
+
+                       ar->ar_type = TMPL_ATTR_TYPE_NORMAL;
+                       ar->ar_da = da;
+                       ar->ar_parent = fr_dict_root(fr_dict_by_da(da));
 
-               /*
-                *      Reach into the next reference
-                *      and correct its parent and
-                *      namespace.
-                */
-               next = tmpl_attr_list_next(tmpl_attr(vpt), ar);
-               if (next) {
-                       next->ar_parent = da;
-                       next->ar_unresolved_namespace = da;
+                       /*
+                       *       Record the dictionary that was
+                       *       successfully used for resolution.
+                       */
+                       vpt->rules.attr.ctx.defaults.dict = tr_rules->dict_def;
+
+                       /*
+                       *       Reach into the next reference
+                       *       and correct its parent and
+                       *       namespace.
+                       */
+                       next = tmpl_attr_list_next(tmpl_attr(vpt), ar);
+                       if (next) {
+                               next->ar_parent = da;
+                               next->ar_unresolved_namespace = da;
+                       }
                }
        }
 
@@ -3710,14 +3728,14 @@ static inline CC_HINT(always_inline) int tmpl_attr_resolve(tmpl_t *vpt, tmpl_res
                 *
                 *      If it was, then we may be able to
                 *      fall back to resolving the attribute
-                *      in the internal dictionary.
+                *      in the group attribute's dictionary.
                 */
                if (!da) {
                        prev = tmpl_attr_list_prev(tmpl_attr(vpt), ar);
                        if (!vpt->rules.attr.disallow_internal && prev && (prev->ar_da->type == FR_TYPE_GROUP)) {
                                (void)fr_dict_attr_by_name_substr(NULL,
                                                                  &da,
-                                                                 fr_dict_root(fr_dict_internal()),
+                                                                 fr_dict_root(fr_dict_by_da(prev->ar_da)),
                                                                  &FR_SBUFF_IN(ar->ar_unresolved,
                                                                               talloc_array_length(ar->ar_unresolved) - 1),
                                                                  NULL);
@@ -4247,15 +4265,13 @@ ssize_t tmpl_regex_compile(tmpl_t *vpt, bool subcaptures)
 fr_slen_t tmpl_request_ref_list_print(fr_sbuff_t *out, FR_DLIST_HEAD(tmpl_request_list) const *rql)
 {
        fr_sbuff_t              our_out = FR_SBUFF(out);
-       tmpl_request_t          *rr = tmpl_request_list_head(rql);
 
        /*
         *      Print request references
         */
-       while (rr) {
+       tmpl_request_list_foreach(rql, rr) {
                FR_SBUFF_IN_TABLE_STR_RETURN(&our_out, tmpl_request_ref_table, rr->request, "<INVALID>");
-               rr = tmpl_request_list_next(rql, rr);
-               if (rr) FR_SBUFF_IN_CHAR_RETURN(&our_out, '.');
+               if (tmpl_request_list_next(rql, rr)) FR_SBUFF_IN_CHAR_RETURN(&our_out, '.');
        }
 
        FR_SBUFF_SET_RETURN(out, &our_out);
@@ -4282,7 +4298,6 @@ fr_slen_t tmpl_attr_print(fr_sbuff_t *out, tmpl_t const *vpt, tmpl_attr_prefix_t
 {
        tmpl_attr_t const       *ar = NULL;
        fr_da_stack_t           stack;
-       char                    printed_rr = false;
        fr_sbuff_t              our_out = FR_SBUFF(out);
        fr_slen_t               slen;
 
@@ -4309,29 +4324,6 @@ fr_slen_t tmpl_attr_print(fr_sbuff_t *out, tmpl_t const *vpt, tmpl_attr_prefix_t
                FR_SBUFF_IN_CHAR_RETURN(&our_out, '&');
        }
 
-       /*
-        *      Print request references
-        */
-       slen = tmpl_request_ref_list_print(&our_out, &vpt->data.attribute.rr);
-       if (slen > 0) printed_rr = true;
-       if (slen < 0) return slen;
-
-       /*
-        *      Print list
-        */
-       if (tmpl_list(vpt) != PAIR_LIST_REQUEST) {      /* Don't print the default list */
-               if (printed_rr) FR_SBUFF_IN_CHAR_RETURN(&our_out, '.');
-
-               FR_SBUFF_IN_TABLE_STR_RETURN(&our_out, pair_list_table, tmpl_list(vpt), "<INVALID>");
-               if (tmpl_attr_list_num_elements(tmpl_attr(vpt))) FR_SBUFF_IN_CHAR_RETURN(&our_out, '.');
-
-       /*
-        *      Request qualifier with no list qualifier
-        */
-       } else if (printed_rr) {
-               if (tmpl_attr_list_num_elements(tmpl_attr(vpt))) FR_SBUFF_IN_CHAR_RETURN(&our_out, '.');
-       }
-
        /*
         *
         *      If the leaf attribute is unknown and raw we
@@ -4343,6 +4335,13 @@ fr_slen_t tmpl_attr_print(fr_sbuff_t *out, tmpl_t const *vpt, tmpl_attr_prefix_t
         */
        if (tmpl_attr_tail_is_raw(vpt)) FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "raw.");
 
+       /*
+        *      Print request references
+        */
+       slen = tmpl_request_ref_list_print(&our_out, &vpt->data.attribute.rr);
+       if ((slen > 0) && tmpl_attr_list_num_elements(tmpl_attr(vpt))) FR_SBUFF_IN_CHAR_RETURN(&our_out, '.');
+       if (slen < 0) return slen;
+
        /*
         *      Print attribute identifiers
         */
@@ -5458,8 +5457,6 @@ bool tmpl_async_required(tmpl_t const *vpt)
 void tmpl_rules_child_init(TALLOC_CTX *ctx, tmpl_rules_t *out, tmpl_rules_t const *parent, tmpl_t *vpt)
 {
        fr_dict_attr_t const *da;
-       fr_dict_attr_t const *ref;
-       fr_dict_t const *dict, *internal;
 
        *out = *parent;
        out->parent = parent;
@@ -5472,41 +5469,19 @@ void tmpl_rules_child_init(TALLOC_CTX *ctx, tmpl_rules_t *out, tmpl_rules_t cons
         *      The input tmpl is a leaf.  We must parse the child as
         *      a normal attribute reference (as with the parent tmpl).
         */
-       if (!fr_type_structural[da->type]) {
-               return;
-       }
-
-       if (vpt->rules.attr.request_def) {
-               tmpl_request_ref_list_acopy(ctx, &out->attr.request_def, vpt->rules.attr.request_def);
-       }
-       out->attr.list_def = tmpl_list(vpt);
+       if (!fr_type_structural[da->type]) return;
 
        /*
-        *      Parse the child attributes in the context of the parent struct / tlv / whatever.
+        *      Copy the base rules...
         */
-       if (da->type != FR_TYPE_GROUP) {
-               out->attr.dict_def = fr_dict_by_da(da);
-               out->attr.parent = da;
-               return;
-       }
-
-       ref = fr_dict_attr_ref(da);
-       dict = fr_dict_by_da(ref);
-       internal = fr_dict_internal();
+       tmpl_attr_rules_copy(ctx, &out->attr, &parent->attr);
 
        /*
-        *      Groups MAY change dictionaries.  If so, then swap the dictionary and the parent.
+        *      Parse the child attributes in the context of the parent struct / tlv / whatever.
         */
-       if ((dict != internal) && (dict != out->attr.dict_def)) {
-               out->attr.dict_def = dict;
-               out->attr.parent = ref;
+       if (tmpl_attr_ctx_rules_is_default(&out->attr)) {
+               FR_DLIST_HEAD(tmpl_request_list) *rql = out->attr.ctx.defaults.request;
+               if (rql) tmpl_request_list_talloc_reverse_free(rql);    /* Will have been copied*/
+               out->attr.ctx = tmpl_attr_ctx_rules_nested(da);
        }
-
-       /*
-        *      Otherwise the reference is swapping FROM a protocol
-        *      dictionary TO the internal dictionary, and TO an
-        *      internal group.  We fall back to leaving well enough
-        *      alone, and leave things as-is.  This allows internal
-        *      grouping attributes to appear anywhere.
-        */
 }
index 86d2420a76bb1073e694843855f07f125695c55a..6d02fc875a795a13abd7aa0b99284b4f9f79a371 100644 (file)
@@ -22,6 +22,7 @@
  * @copyright 2000 Alan DeKok (aland@freeradius.org)
  */
 
+#include "lib/server/tmpl.h"
 RCSID("$Id$")
 
 #include <freeradius-devel/server/log.h>
@@ -270,7 +271,7 @@ int pairlist_read(TALLOC_CTX *ctx, fr_dict_t const *dict, char const *file, PAIR
 
        lhs_rules = (tmpl_rules_t) {
                .attr = {
-                       .dict_def = dict,
+                       .ctx = tmpl_attr_ctx_rules_default(NULL, NULL, dict),
                        .prefix = TMPL_ATTR_REF_PREFIX_NO,
                        .disallow_qualifiers = true, /* for now, until more tests are made */
 
@@ -286,7 +287,7 @@ int pairlist_read(TALLOC_CTX *ctx, fr_dict_t const *dict, char const *file, PAIR
        };
        rhs_rules = (tmpl_rules_t) {
                .attr = {
-                       .dict_def = dict,
+                       .ctx = tmpl_attr_ctx_rules_default(NULL, NULL, dict),
                        .prefix = TMPL_ATTR_REF_PREFIX_YES,
                        .disallow_qualifiers = true, /* for now, until rlm_files supports it */
                }
@@ -383,7 +384,7 @@ int pairlist_read(TALLOC_CTX *ctx, fr_dict_t const *dict, char const *file, PAIR
                }
                t->name = q;
 
-               lhs_rules.attr.list_def = PAIR_LIST_CONTROL;
+               lhs_rules.attr.ctx = tmpl_attr_ctx_rules_default(NULL, request_attr_control, dict);
                comma = false;
 
 check_item:
@@ -426,7 +427,7 @@ check_item:
                         *      for regexes we want to look at the
                         *      request list.
                         */
-                       tmpl_attr_set_list(new_map->lhs, PAIR_LIST_REQUEST);
+                       tmpl_attr_set_list(new_map->lhs, request_attr_request);
 
                        if (tmpl_is_regex_uncompiled(new_map->rhs) &&
                            (tmpl_regex_compile(new_map->rhs, false) < 0)) {
@@ -514,7 +515,7 @@ setup_reply:
                /*
                 *      Setup the reply items.
                 */
-               lhs_rules.attr.list_def = PAIR_LIST_REPLY;
+               lhs_rules.attr.ctx = tmpl_attr_ctx_rules_default(NULL, request_attr_reply, dict);
                comma = false;
 
 reply_item:
@@ -596,7 +597,7 @@ next_reply_item:
                        }
                }
 
-               fr_assert(tmpl_list(new_map->lhs) == PAIR_LIST_REPLY);
+               fr_assert(tmpl_list(new_map->lhs) == request_attr_reply);
 
                if (!new_map->parent) map_list_insert_tail(&t->reply, new_map);
 
index 5ae9ee979d6b52ad9ea848dc36bebd8ee6431114..b42cac369def8d5e1a6ff553d3b250b87f3185bb 100644 (file)
@@ -1219,8 +1219,9 @@ int virtual_servers_instantiate(void)
                        tmpl_rules_t            parse_rules;
 
                        memset(&parse_rules, 0, sizeof(parse_rules));
-                       parse_rules.attr.dict_def = *(process->dict);
-                       fr_assert(parse_rules.attr.dict_def != NULL);
+               
+                       fr_assert(*(process->dict) != NULL);
+                       parse_rules.attr.ctx = tmpl_attr_ctx_rules_default(NULL, NULL, *(process->dict));
 
                        if (virtual_server_compile_sections(server_cs, process->compile_list, &parse_rules,
                                                            vs->process_mi->dl_inst->data) < 0) {
index 67f0bc8c5ff0fccf58416469afe838eb5d680a83..753f914597e783b2a842a0d6f22defd3e90f7037 100644 (file)
@@ -22,6 +22,8 @@
  *
  * @copyright 2006-2016 The FreeRADIUS server project
  */
+#include "lib/server/request.h"
+#include "lib/server/tmpl.h"
 RCSID("$Id$")
 
 #include <freeradius-devel/protocol/freeradius/freeradius.internal.h>
@@ -212,8 +214,8 @@ static const unlang_actions_t default_actions[MOD_COUNT] =
 
 static inline CC_HINT(always_inline) int unlang_attr_rules_verify(tmpl_attr_rules_t const *rules)
 {
-       if (!fr_cond_assert_msg(rules->dict_def, "No protocol dictionary set")) return -1;
-       if (!fr_cond_assert_msg(rules->dict_def != fr_dict_internal(), "rules->attr.dict_def must not be the internal dictionary")) return -1;
+       if (!fr_cond_assert_msg(tmpl_attr_ctx_rules_default_dict(rules), "No protocol dictionary set")) return -1;
+       if (!fr_cond_assert_msg(tmpl_attr_ctx_rules_default_dict(rules) != fr_dict_internal(), "rules->attr.dict_def must not be the internal dictionary")) return -1;
        if (!fr_cond_assert_msg(!rules->allow_foreign, "rules->attr.allow_foreign must be false")) return -1;
 
        return 0;
@@ -240,7 +242,7 @@ static bool pass2_fixup_tmpl(TALLOC_CTX *ctx, tmpl_t **vpt_p, CONF_ITEM const *c
         *      We may now know the correct dictionary
         *      where we didn't before...
         */
-       if (!vpt->rules.attr.dict_def) tmpl_set_dict_def(vpt, dict);
+       if (!tmpl_attr_ctx_rules_default_dict(&vpt->rules.attr)) tmpl_set_dict_def(vpt, dict);
 
        /*
         *      Convert virtual &Attr-Foo to "%{Attr-Foo}"
@@ -495,7 +497,7 @@ static bool pass2_fixup_cond_map(fr_cond_t *c, CONF_ITEM *ci, fr_dict_t const *d
         */
        if (!tmpl_is_attr(map->lhs) ||
            !tmpl_request_ref_is_current(tmpl_request(map->lhs)) ||
-           (tmpl_list(map->lhs) != PAIR_LIST_REQUEST)) {
+           (tmpl_list(map->lhs) != request_attr_request)) {
                return true;
        }
 
@@ -546,7 +548,7 @@ static bool pass2_fixup_map(map_t *map, tmpl_rules_t const *rules, fr_dict_attr_
        RULES_VERIFY(rules);
 
        if (tmpl_is_unresolved(map->lhs)) {
-               if (!pass2_fixup_tmpl(map, &map->lhs, map->ci, rules->attr.dict_def)) {
+               if (!pass2_fixup_tmpl(map, &map->lhs, map->ci, tmpl_attr_ctx_rules_default_dict(&rules->attr))) {
                        return false;
                }
        }
@@ -567,7 +569,7 @@ static bool pass2_fixup_map(map_t *map, tmpl_rules_t const *rules, fr_dict_attr_
                if (tmpl_is_unresolved(map->rhs)) {
                        fr_assert(!tmpl_is_regex_xlat_unresolved(map->rhs));
 
-                       if (!pass2_fixup_tmpl(map, &map->rhs, map->ci, rules->attr.dict_def)) {
+                       if (!pass2_fixup_tmpl(map, &map->rhs, map->ci, tmpl_attr_ctx_rules_default_dict(&rules->attr))) {
                                return false;
                        }
                }
@@ -655,7 +657,7 @@ static bool pass2_fixup_map_rhs(unlang_group_t *g, tmpl_rules_t const *rules)
        if (!gext->vpt) return true;
 
        return pass2_fixup_tmpl(map_list_head(&gext->map)->ci, &gext->vpt,
-                               cf_section_to_item(g->cs), rules->attr.dict_def);
+                               cf_section_to_item(g->cs), tmpl_attr_ctx_rules_default_dict(&rules->attr));
 }
 
 static void unlang_dump(unlang_t *instruction, int depth)
@@ -1282,8 +1284,9 @@ static unlang_t *compile_update_to_edit(unlang_t *parent, unlang_compile_t *unla
         */
        if (name2) {
                snprintf(list_buffer, sizeof(list_buffer), "&%s", name2);
-       } else {
-               snprintf(list_buffer, sizeof(list_buffer), "&%s", fr_table_str_by_value(pair_list_table, unlang_ctx->rules->attr.list_def, "???"));
+       } else if (tmpl_attr_ctx_rules_default_list(&unlang_ctx->rules->attr)) {
+               fr_dict_attr_t const    *list_da = tmpl_attr_ctx_rules_default_list(&unlang_ctx->rules->attr);
+               snprintf(list_buffer, sizeof(list_buffer), "&%s", list_da->name);
        }
 
        /*
@@ -1348,6 +1351,7 @@ static unlang_t *compile_update_to_edit(unlang_t *parent, unlang_compile_t *unla
                         *
                         *      @todo - add support for &config?
                         */
+                       
                        if (fr_table_value_by_substr(pair_list_table, p, q - p, PAIR_LIST_UNKNOWN) != PAIR_LIST_UNKNOWN) {
                                if (*q) {
                                        p = q + 1;
@@ -1974,7 +1978,7 @@ static unlang_t *compile_variable(unlang_t *parent, unlang_compile_t *unlang_ctx
 
                var_free = var;
 
-               var->dict = fr_dict_protocol_alloc(unlang_ctx->rules->attr.dict_def);
+               var->dict = fr_dict_protocol_alloc(tmpl_attr_ctx_rules_default_dict(&unlang_ctx->rules->attr));
                if (!var->dict) {
                        talloc_free(var);
                        return NULL;
@@ -1991,8 +1995,7 @@ static unlang_t *compile_variable(unlang_t *parent, unlang_compile_t *unlang_ctx
                *t_rules = *unlang_ctx->rules;
                t_rules->parent = unlang_ctx->rules;
 
-               t_rules->attr.dict_def = var->dict;
-               t_rules->attr.parent = NULL;
+               t_rules->attr.ctx = tmpl_attr_ctx_rules_default(NULL, NULL, var->dict);
 
                unlang_ctx->rules = t_rules;
        }
@@ -2018,7 +2021,7 @@ invalid_type:
         *      in var->root will also check the protocol dictionary,
         *      so the check here is really only for better error messages.
         */
-       da = fr_dict_attr_by_name(NULL, fr_dict_root(t_rules->parent->attr.dict_def), value);
+       da = fr_dict_attr_by_name(NULL, fr_dict_root(tmpl_attr_ctx_rules_default_dict(&t_rules->parent->attr)), value);
        if (da) {
                talloc_free(var_free);
                cf_log_err(cp, "Local variable '%s' duplicates a dictionary attribute.", value);
@@ -2879,7 +2882,7 @@ static unlang_t *compile_switch(unlang_t *parent, unlang_compile_t *unlang_ctx,
         *      This is so that compile_case() can do attribute type
         *      checks / casts against us.
         */
-       if (!pass2_fixup_tmpl(g, &gext->vpt, cf_section_to_item(cs), unlang_ctx->rules->attr.dict_def)) {
+       if (!pass2_fixup_tmpl(g, &gext->vpt, cf_section_to_item(cs), tmpl_attr_ctx_rules_default_dict(&unlang_ctx->rules->attr))) {
        error:
                talloc_free(g);
                return NULL;
@@ -3205,7 +3208,7 @@ static unlang_t *compile_timeout(unlang_t *parent, unlang_compile_t *unlang_ctx,
                /*
                 *      Fixup the tmpl so that we know it's somewhat sane.
                 */
-               if (!pass2_fixup_tmpl(gext, &vpt, cf_section_to_item(cs), unlang_ctx->rules->attr.dict_def)) {
+               if (!pass2_fixup_tmpl(gext, &vpt, cf_section_to_item(cs), tmpl_attr_ctx_rules_default_dict(&unlang_ctx->rules->attr))) {
                error:
                        talloc_free(g);
                        return NULL;
@@ -3307,7 +3310,7 @@ static unlang_t *compile_limit(unlang_t *parent, unlang_compile_t *unlang_ctx, C
        /*
         *      Fixup the tmpl so that we know it's somewhat sane.
         */
-       if (!pass2_fixup_tmpl(gext, &vpt, cf_section_to_item(cs), unlang_ctx->rules->attr.dict_def)) {
+       if (!pass2_fixup_tmpl(gext, &vpt, cf_section_to_item(cs), tmpl_attr_ctx_rules_default_dict(&unlang_ctx->rules->attr))) {
        error:
                talloc_free(g);
                return NULL;
@@ -3611,7 +3614,7 @@ static unlang_t *compile_if_subsection(unlang_t *parent, unlang_compile_t *unlan
        bool                    is_truthy = false, value = false;
        xlat_res_rules_t        xr_rules = {
                .tr_rules = &(tmpl_res_rules_t) {
-                       .dict_def = unlang_ctx->rules->attr.dict_def,
+                       .dict_def = tmpl_attr_ctx_rules_default_dict(&unlang_ctx->rules->attr),
                },
        };
 
@@ -3629,7 +3632,7 @@ static unlang_t *compile_if_subsection(unlang_t *parent, unlang_compile_t *unlan
 
                tmpl_rules_t t_rules = (tmpl_rules_t) {
                        .attr = {
-                               .dict_def = xr_rules.tr_rules->dict_def,
+                               .ctx = tmpl_attr_ctx_rules_default(NULL, NULL, xr_rules.tr_rules->dict_def),
                                .allow_unresolved = true,
                                .allow_unknown = true
                        }
@@ -3711,7 +3714,7 @@ static unlang_t *compile_if_subsection(unlang_t *parent, unlang_compile_t *unlan
                        case COND_TYPE_TMPL:
                                fr_assert(!tmpl_is_regex_xlat_unresolved(leaf->data.vpt));
                                if (!pass2_fixup_tmpl(leaf, &leaf->data.vpt, cf_section_to_item(cs),
-                                                     unlang_ctx->rules->attr.dict_def)) return false;
+                                                     tmpl_attr_ctx_rules_default_dict(&unlang_ctx->rules->attr))) return false;
                                break;
 
                        /*
@@ -3719,7 +3722,7 @@ static unlang_t *compile_if_subsection(unlang_t *parent, unlang_compile_t *unlan
                         */
                        case COND_TYPE_MAP:
                                if (!pass2_fixup_cond_map(leaf, cf_section_to_item(cs),
-                                                         unlang_ctx->rules->attr.dict_def)) return false;
+                                                         tmpl_attr_ctx_rules_default_dict(&unlang_ctx->rules->attr))) return false;
                                break;
 
                        default:
@@ -3946,7 +3949,7 @@ static unlang_t *compile_load_balance_subsection(unlang_t *parent, unlang_compil
                /*
                 *      Fixup the templates
                 */
-               if (!pass2_fixup_tmpl(g, &gext->vpt, cf_section_to_item(cs), unlang_ctx->rules->attr.dict_def)) {
+               if (!pass2_fixup_tmpl(g, &gext->vpt, cf_section_to_item(cs), tmpl_attr_ctx_rules_default_dict(&unlang_ctx->rules->attr))) {
                        talloc_free(g);
                        return NULL;
                }
@@ -4092,7 +4095,7 @@ static unlang_t *compile_subrequest(unlang_t *parent, unlang_compile_t *unlang_c
         */
        name2 = cf_section_name2(cs);
        if (!name2) {
-               dict = unlang_ctx->rules->attr.dict_def;
+               dict = tmpl_attr_ctx_rules_default_dict(&unlang_ctx->rules->attr);
                packet_name = name2 = unlang_ctx->section_name2;
                goto get_packet_type;
        }
@@ -4129,7 +4132,7 @@ static unlang_t *compile_subrequest(unlang_t *parent, unlang_compile_t *unlang_c
                        return NULL;
                }
 
-               dict = unlang_ctx->rules->attr.dict_def;
+               dict = tmpl_attr_ctx_rules_default_dict(&unlang_ctx->rules->attr);
                packet_name = NULL;
                goto get_packet_type;
        }
@@ -4141,7 +4144,7 @@ static unlang_t *compile_subrequest(unlang_t *parent, unlang_compile_t *unlang_c
         */
        p = strchr(name2, '.');
        if (!p) {
-               dict = unlang_ctx->rules->attr.dict_def;
+               dict = tmpl_attr_ctx_rules_default_dict(&unlang_ctx->rules->attr);
                packet_name = name2;
 
        } else {
@@ -4248,7 +4251,7 @@ get_packet_type:
 
        t_rules = *unlang_ctx->rules;
        t_rules.parent = unlang_ctx->rules;
-       t_rules.attr.dict_def = dict;
+       t_rules.attr.ctx = tmpl_attr_ctx_rules_default(NULL, NULL, dict);
        t_rules.attr.allow_foreign = false;
 
        /*
@@ -4337,9 +4340,9 @@ static unlang_t *compile_call(unlang_t *parent, unlang_compile_t *unlang_ctx, CO
                return NULL;
        }
        if ((dict != fr_dict_internal()) && fr_dict_internal() &&
-           unlang_ctx->rules->attr.dict_def && (unlang_ctx->rules->attr.dict_def != dict)) {
+           tmpl_attr_ctx_rules_default_dict(&unlang_ctx->rules->attr) && (tmpl_attr_ctx_rules_default_dict(&unlang_ctx->rules->attr)!= dict)) {
                cf_log_err(cs, "Cannot call server %s with namespace '%s' from namespaces '%s' - they have incompatible protocols",
-                          server, fr_dict_root(dict)->name, fr_dict_root(unlang_ctx->rules->attr.dict_def)->name);
+                          server, fr_dict_root(dict)->name, fr_dict_root(tmpl_attr_ctx_rules_default_dict(&unlang_ctx->rules->attr))->name);
                return NULL;
        }
 
@@ -4408,7 +4411,8 @@ static unlang_t *compile_caller(unlang_t *parent, unlang_compile_t *unlang_ctx,
         */
        memcpy(&parent_rules, unlang_ctx->rules, sizeof(parent_rules));
        memcpy(&t_rules, unlang_ctx->rules, sizeof(t_rules));
-       parent_rules.attr.dict_def = dict;
+       
+       parent_rules.attr.ctx = tmpl_attr_ctx_rules_default(NULL, NULL, dict);
        t_rules.parent = &parent_rules;
 
        /*
@@ -4637,13 +4641,13 @@ static unlang_t *compile_module(unlang_t *parent, unlang_compile_t *unlang_ctx,
        /*
         *      Can't use "chap" in "dhcp".
         */
-       if (mrlm->dict && *mrlm->dict && unlang_ctx->rules && unlang_ctx->rules->attr.dict_def &&
-           (unlang_ctx->rules->attr.dict_def != fr_dict_internal()) &&
-           !fr_dict_compatible(*(mrlm->dict), unlang_ctx->rules->attr.dict_def)) {
+       if (mrlm->dict && *mrlm->dict && unlang_ctx->rules && tmpl_attr_ctx_rules_default_dict(&unlang_ctx->rules->attr) &&
+           (tmpl_attr_ctx_rules_default_dict(&unlang_ctx->rules->attr) != fr_dict_internal()) &&
+           !fr_dict_compatible(*(mrlm->dict), tmpl_attr_ctx_rules_default_dict(&unlang_ctx->rules->attr))) {
                cf_log_err(ci, "The \"%s\" module can only be used with 'namespace = %s'.  It cannot be used with 'namespace = %s'.",
                           inst->module->name,
                           fr_dict_root(*mrlm->dict)->name,
-                          fr_dict_root(unlang_ctx->rules->attr.dict_def)->name);
+                          fr_dict_root(tmpl_attr_ctx_rules_default_dict(&unlang_ctx->rules->attr))->name);
                return NULL;
        }
 
index f7b783e673476564526e0ff397622b3a2f87f71e..67eaabc025226b7ac3fd292265e3d0e2b4cf5959 100644 (file)
@@ -27,6 +27,8 @@ RCSID("$Id$")
 
 #include <freeradius-devel/server/base.h>
 #include <freeradius-devel/server/tmpl_dcursor.h>
+#undef NO_ASSERT
+
 #include <freeradius-devel/util/debug.h>
 #include <freeradius-devel/util/edit.h>
 #include <freeradius-devel/util/calc.h>
@@ -34,6 +36,8 @@ RCSID("$Id$")
 #include <freeradius-devel/unlang/unlang_priv.h>
 #include "edit_priv.h"
 
+
+
 typedef struct {
        FR_DLIST_HEAD(fr_value_box_list)        result;                 //!< result of expansion
        tmpl_t const            *vpt;                   //!< expanded tmpl
@@ -112,7 +116,7 @@ static int tmpl_attr_from_result(TALLOC_CTX *ctx, map_t const *map, edit_result_
        slen = tmpl_afrom_attr_str(ctx, NULL, &out->to_free, box->vb_strvalue,
                                   &(tmpl_rules_t){
                                        .attr = {
-                                               .dict_def = request->dict,
+                                               .ctx = tmpl_attr_ctx_rules_default(NULL, NULL, request->dict),
                                                .prefix = TMPL_ATTR_REF_PREFIX_NO
                                        }
                                   });
index 72102b18a9b6d94619abf75dab5cf86a47e2fd77..0d3293804722de01792f78bfeebd93c13624240c 100644 (file)
@@ -78,7 +78,7 @@ int xlat_fmt_get_vp(fr_pair_t **out, request_t *request, char const *name)
        if (tmpl_afrom_attr_str(request, NULL, &vpt, name,
                                &(tmpl_rules_t){
                                        .attr = {
-                                               .dict_def = request->dict,
+                                               .ctx = tmpl_attr_ctx_rules_default(NULL, NULL, request->dict),
                                                .prefix = TMPL_ATTR_REF_PREFIX_AUTO
                                        }
                                }) <= 0) return -4;
@@ -1153,7 +1153,7 @@ static xlat_action_t xlat_func_debug_attr(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcur
        if (tmpl_afrom_attr_str(request, NULL, &vpt, fmt,
                                &(tmpl_rules_t){
                                        .attr = {
-                                               .dict_def = request->dict,
+                                               .ctx = tmpl_attr_ctx_rules_default(NULL, NULL, request->dict),
                                                .prefix = TMPL_ATTR_REF_PREFIX_AUTO
                                        }
                                }) <= 0) {
@@ -1201,7 +1201,7 @@ static xlat_action_t xlat_func_flatten(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor
        if (tmpl_afrom_attr_str(request, NULL, &vpt, fmt,
                                &(tmpl_rules_t){
                                        .attr = {
-                                               .dict_def = request->dict,
+                                               .ctx = tmpl_attr_ctx_rules_default(NULL, NULL, request->dict),
                                                .prefix = TMPL_ATTR_REF_PREFIX_AUTO
                                        }
                                }) <= 0) {
@@ -1247,7 +1247,7 @@ static xlat_action_t xlat_func_unflatten(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcurs
        if (tmpl_afrom_attr_str(request, NULL, &vpt, fmt,
                                &(tmpl_rules_t){
                                        .attr = {
-                                               .dict_def = request->dict,
+                                               .ctx = tmpl_attr_ctx_rules_default(NULL, NULL, request->dict),
                                                .prefix = TMPL_ATTR_REF_PREFIX_AUTO
                                        }
                                }) <= 0) {
@@ -1540,7 +1540,7 @@ static xlat_action_t xlat_func_map(TALLOC_CTX *ctx, fr_dcursor_t *out,
 
        tmpl_rules_t    attr_rules = {
                .attr = {
-                       .dict_def = request->dict,
+                       .ctx = tmpl_attr_ctx_rules_default(NULL, NULL, request->dict),
                        .prefix = TMPL_ATTR_REF_PREFIX_AUTO
                }
        };
@@ -1754,7 +1754,7 @@ static xlat_action_t xlat_func_eval(TALLOC_CTX *ctx, fr_dcursor_t *out,
                                                .allow_unknown = false,
                                                .allow_unresolved = false,
                                                .allow_foreign = false,
-                                               .dict_def = request->dict
+                                               .ctx = tmpl_attr_ctx_rules_default(NULL, NULL, request->dict)
                                        },
                                        .at_runtime = true
                                    }) < 0) {
@@ -1830,7 +1830,7 @@ static xlat_action_t xlat_func_expr(TALLOC_CTX *ctx, fr_dcursor_t *out,
                                                .allow_unknown = false,
                                                .allow_unresolved = false,
                                                .allow_foreign = false,
-                                               .dict_def = request->dict
+                                               .ctx = tmpl_attr_ctx_rules_default(NULL, NULL, request->dict)
                                        },
                                        .at_runtime = true
                                    }) < 0) {
@@ -2743,7 +2743,7 @@ static xlat_action_t xlat_func_pairs(TALLOC_CTX *ctx, fr_dcursor_t *out,
        if (tmpl_afrom_attr_str(ctx, NULL, &vpt, in_head->vb_strvalue,
                                &(tmpl_rules_t){
                                        .attr = {
-                                               .dict_def = request->dict,
+                                               .ctx = tmpl_attr_ctx_rules_default(NULL, NULL, request->dict),
                                                .prefix = TMPL_ATTR_REF_PREFIX_AUTO
                                        }
                                }) <= 0) {
@@ -3802,7 +3802,7 @@ static xlat_action_t protocol_encode_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out,
        if (tmpl_afrom_attr_str(ctx, NULL, &vpt, in_head->vb_strvalue,
                                &(tmpl_rules_t){
                                        .attr = {
-                                               .dict_def = request->dict,
+                                               .ctx = tmpl_attr_ctx_rules_default(NULL, NULL, request->dict),
                                                .prefix = TMPL_ATTR_REF_PREFIX_AUTO
                                        }
                                }) <= 0) {
index 40d785812a0c3f3c901183561b912411d39afec3..035838dccedf58de6621f2f7f48a36b4a101a773 100644 (file)
@@ -1436,7 +1436,7 @@ ssize_t _xlat_eval(TALLOC_CTX *ctx, char **out, size_t outlen, request_t *reques
                                      NULL,
                                      &(tmpl_rules_t){
                                        .attr = {
-                                               .dict_def = request->dict
+                                               .ctx = tmpl_attr_ctx_rules_default(NULL, NULL, request->dict),
                                        }
                                      });
        if (len == 0) {
index 497a6a96644e7e17cf338623f5ab293df30a290a..35e2aed02c54d4abdcf3024da48a396f9a213559 100644 (file)
@@ -1558,9 +1558,7 @@ static xlat_action_t xlat_exists_resume(TALLOC_CTX *ctx, fr_dcursor_t *out,
        slen = tmpl_afrom_attr_str(ctx, NULL, &vpt, vb->vb_strvalue,
                                   &(tmpl_rules_t) {
                                           .attr = {
-                                                  .dict_def = request->dict,
-                                                  .request_def = &tmpl_request_def_current,
-                                                  .list_def = PAIR_LIST_REQUEST,
+                                                  .ctx = tmpl_attr_ctx_rules_default(NULL, NULL, request->dict),
                                                   .prefix = TMPL_ATTR_REF_PREFIX_AUTO,
                                                   .allow_unknown = false,
                                                   .allow_unresolved = false,
@@ -2783,7 +2781,7 @@ static fr_slen_t xlat_tokenize_expression_internal(TALLOC_CTX *ctx, xlat_exp_hea
 
        MEM(head = xlat_exp_head_alloc(ctx));
        if (t_rules) {
-               head->dict = t_rules->attr.dict_def;
+               head->dict = tmpl_attr_ctx_rules_default_dict(&t_rules->attr);
        } else {
                t_rules = &my_rules;
        }
@@ -2902,7 +2900,7 @@ fr_slen_t xlat_tokenize_ephemeral_expression(TALLOC_CTX *ctx, xlat_exp_head_t **
 
        if (t_rules) {
                my_rules = *t_rules;
-               head->dict = t_rules->attr.dict_def;
+               head->dict = tmpl_attr_ctx_rules_default_dict(&t_rules->attr);
        }
 
        my_rules.xlat.runtime_el = el;
index 8660430b66146aae6c6d26c864d916dcbfdc8243..091f18339493b981c85cafcc4fc758d986d56e1f 100644 (file)
@@ -1126,7 +1126,7 @@ static void _xlat_debug(xlat_exp_head_t const *head, int depth)
                                        while ((rr = tmpl_request_list_next(list, rr))) {
                                                INFO_INDENT("ref  %d", rr->request);
                                        }
-                                       INFO_INDENT("list %d", tmpl_list(node->vpt));
+                                       INFO_INDENT("list %s", tmpl_list(node->vpt)->name);
                                        if (tmpl_attr_tail_num(node->vpt) != NUM_UNSPEC) {
                                                if (tmpl_attr_tail_num(node->vpt) == NUM_COUNT) {
                                                        INFO_INDENT("[#]");
@@ -1434,7 +1434,7 @@ fr_slen_t xlat_tokenize_ephemeral(TALLOC_CTX *ctx, xlat_exp_head_t **out,
        MEM(head = xlat_exp_head_alloc(ctx));
 
        if (t_rules) {
-               head->dict = t_rules->attr.dict_def;
+               head->dict = tmpl_attr_ctx_rules_default_dict(&t_rules->attr);
                our_t_rules = *t_rules;
        }
 
@@ -1495,7 +1495,7 @@ fr_slen_t xlat_tokenize_argv(TALLOC_CTX *ctx, xlat_exp_head_t **out, fr_sbuff_t
        xlat_exp_head_t                 *head;
 
        MEM(head = xlat_exp_head_alloc(ctx));
-       if (t_rules) head->dict = t_rules->attr.dict_def;
+       if (t_rules) head->dict = tmpl_attr_ctx_rules_default_dict(&t_rules->attr);
 
        if (p_rules && p_rules->terminals) {
                tmp_p_rules = (fr_sbuff_parse_rules_t){ /* Stack allocated due to CL scope */
@@ -1660,7 +1660,7 @@ fr_slen_t xlat_tokenize(TALLOC_CTX *ctx, xlat_exp_head_t **out, fr_sbuff_t *in,
        xlat_exp_head_t *head;
 
        MEM(head = xlat_exp_head_alloc(ctx));
-       if (t_rules) head->dict = t_rules->attr.dict_def;
+       if (t_rules) head->dict = tmpl_attr_ctx_rules_default_dict(&t_rules->attr);
 
        fr_strerror_clear();    /* Clear error buffer */
 
@@ -1976,7 +1976,8 @@ tmpl_t *xlat_to_tmpl_attr(TALLOC_CTX *ctx, xlat_exp_head_t *head)
        vpt = tmpl_alloc(ctx, TMPL_TYPE_ATTR, T_BARE_WORD, node->fmt, talloc_array_length(node->fmt) - 1);
        if (!vpt) return NULL;
 
-       tmpl_attr_copy(vpt, node->vpt);
+       tmpl_attr_replace(vpt, tmpl_attr(node->vpt));
+       tmpl_request_ref_replace(vpt, tmpl_request(node->vpt));
 
        TMPL_VERIFY(vpt);
 
index 9978d97ff4ef6e3be9e8b224b81feb5248c10451..d440d7aab22e7637884fd8bc43a9c8d4dd8a36c1 100644 (file)
@@ -29,7 +29,12 @@ extern "C" {
 #include <freeradius-devel/missing.h>
 #include <freeradius-devel/util/fring.h>
 
-#ifdef NO_ASSERT
+/*
+ *     Setting this causes clangd to throw lots of errors, because it doesn't
+ *     seem to re-evaluate headers in the contexts of the files which include
+ *     them.
+ */
+#if defined(NO_ASSERT) && !defined(__clangd__)
 # define MEM(x) error "Use of MEM() not allowed in this source file.  Deal with memory allocation failure gracefully"
 #else
 # define MEM(x) do { if (!(x)) { fr_cond_assert_msg((x), "OUT OF MEMORY"); _fr_exit(__FILE__, __LINE__, EXIT_FAILURE, true); } } while (0)
index 2d456057f4871f40e77844f6e976368770fef829..78671a7771165acaa8040a5018eb83b116cd28d9 100644 (file)
@@ -285,7 +285,7 @@ static int mod_instantiate(module_inst_ctx_t const *mctx)
        tmpl_rules_t            parse_rules = {
                /* Strict rules for the update map as it's processed with limited functionality */
                .attr = {
-                       .dict_def = dict_ldap_sync,
+                       .ctx = tmpl_attr_ctx_rules_default(NULL, NULL, dict_ldap_sync),
                        .allow_foreign = false,
                        .allow_unknown = false,
                        .allow_unresolved = false,
index 4610e24ed9e891306f2abbe2fe502871e900e9dd..3ce3fd94557c885dfaf46e943de89641459489e2 100644 (file)
@@ -831,7 +831,7 @@ xlat_action_t cache_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out,
                                      NULL,
                                      &(tmpl_rules_t){
                                        .attr = {
-                                               .dict_def = request->dict,
+                                               .ctx = tmpl_attr_ctx_rules_default(NULL, NULL, request->dict),
                                                .prefix = TMPL_ATTR_REF_PREFIX_AUTO
                                        }
                                      });
index f755bd0ea1ee3646f078ebd39bc2106299cad04e..6d64755e0e41ef005c6960ec4879a9c6cada8e9f 100644 (file)
@@ -112,7 +112,7 @@ int cache_deserialize(rlm_cache_entry_t *c, fr_dict_t const *dict, char *in, ssi
                map_t   *map = NULL;
                tmpl_rules_t parse_rules = {
                        .attr = {
-                               .dict_def = dict,
+                               .ctx = tmpl_attr_ctx_rules_default(NULL, NULL, dict),
                                .prefix = TMPL_ATTR_REF_PREFIX_NO
                        }
                };
index 1379c6ac9ea05ef0efb41e16a5f90a53844a50be..8fdf2ca9a7347a412ea6c38bbbc48bef20bacb0c 100644 (file)
@@ -364,13 +364,13 @@ int mod_attribute_to_element(const char *name, json_object *map, void *buf)
  * @param[in] out      Cursor to append maps to.
  * @param[in] request  The request to which the generated pairs should be added.
  * @param[in] json     The JSON object representation of the user document.
- * @param[in] list     The pair list PAIR_LIST_CONTROL or PAIR_LIST_REPLY.
+ * @param[in] context  Prepended to all references in this map.
  * @return
  *     - 1 if no section found.
  *     - 0 on success.
  *     - <0 on error.
  */
-int mod_json_object_to_map(TALLOC_CTX *ctx, fr_dcursor_t *out, request_t *request, json_object *json, tmpl_pair_list_t list)
+int mod_json_object_to_map(TALLOC_CTX *ctx, fr_dcursor_t *out, request_t *request, json_object *json, FR_DLIST_HEAD(tmpl_attr_list) *context)
 {
        json_object     *list_obj;
        char const      *list_name = fr_table_str_by_value(pair_list_table, list, "<INVALID>");
@@ -483,7 +483,7 @@ int mod_json_object_to_map(TALLOC_CTX *ctx, fr_dcursor_t *out, request_t *reques
                                                &(tmpl_rules_t){
                                                        .attr = {
                                                                .dict_def = request->dict,
-                                                               .list_def = list
+                                                               .context = list
                                                        }
                                                },
                                                op,
index 72fb139c558c9ec524ca09b7ff8f841379f9bf58..470698e187ee146e09be7f037ab3728eae700739 100644 (file)
@@ -82,7 +82,7 @@ int mod_build_attribute_element_map(CONF_SECTION *conf, rlm_couchbase_t *inst);
 
 int mod_attribute_to_element(const char *name, json_object *map, void *buf);
 
-int mod_json_object_to_map(TALLOC_CTX *ctx, fr_dcursor_t *out, request_t *request, json_object *json, tmpl_pair_list_t list);
+int mod_json_object_to_map(TALLOC_CTX *ctx, fr_dcursor_t *out, request_t *request, json_object *json, FR_DLIST_HEAD(tmpl_attr_list) *context);
 
 json_object *mod_value_pair_to_json_object(request_t *request, fr_pair_t *vp);
 
index b5e3ec5124607a6bb317f6fd0d4dcf91fb575a95..8583fd189f9e4d652ef0eb524e05ba3d274d23c1 100644 (file)
@@ -158,10 +158,10 @@ static unlang_action_t mod_authorize(rlm_rcode_t *p_result, module_ctx_t const *
                /*
                 *      Convert JSON data into maps
                 */
-               if ((mod_json_object_to_map(pool, &maps, request, cookie->jobj, PAIR_LIST_CONTROL) < 0) ||
-                   (mod_json_object_to_map(pool, &maps, request, cookie->jobj, PAIR_LIST_REPLY) < 0) ||
-                   (mod_json_object_to_map(pool, &maps, request, cookie->jobj, PAIR_LIST_REQUEST) < 0) ||
-                   (mod_json_object_to_map(pool, &maps, request, cookie->jobj, PAIR_LIST_STATE) < 0)) {
+               if ((mod_json_object_to_map(pool, &maps, request, cookie->jobj, &tmpl_list_ctx_rcontrol) < 0) ||
+                   (mod_json_object_to_map(pool, &maps, request, cookie->jobj, &tmpl_attr_def_reply) < 0) ||
+                   (mod_json_object_to_map(pool, &maps, request, cookie->jobj, &tmpl_attr_def_request) < 0) ||
+                   (mod_json_object_to_map(pool, &maps, request, cookie->jobj, &tmpl_attr_def_state) < 0)) {
                invalid:
                        talloc_free(pool);
                        rcode = RLM_MODULE_INVALID;
index 7020209b854e9282c22886dc432cc7a700242bc5..0cf42ad20d7a32da982a9a052d1ec21c396889c9 100644 (file)
@@ -41,8 +41,8 @@ typedef struct {
        char const              *program;
        char const              *input;
        char const              *output;
-       tmpl_pair_list_t        input_list;
-       tmpl_pair_list_t        output_list;
+       tmpl_t                  *input_list;
+       tmpl_t                  *output_list;
        bool                    shell_escape;
        bool                    env_inherit;
        fr_time_delta_t         timeout;
@@ -164,25 +164,20 @@ static int mod_bootstrap(module_inst_ctx_t const *mctx)
        rlm_exec_t      *inst = talloc_get_type_abort(mctx->inst->data, rlm_exec_t);
        CONF_SECTION    *conf = mctx->inst->conf;
        xlat_t          *xlat;
-       char const      *p;
 
        xlat = xlat_register_module(NULL, mctx, mctx->inst->name, exec_xlat, FR_TYPE_STRING, XLAT_FLAG_NEEDS_ASYNC);
        xlat_func_args(xlat, exec_xlat_args);
 
        if (inst->input) {
-               p = inst->input;
-               p += tmpl_pair_list_name(&inst->input_list, p, PAIR_LIST_UNKNOWN);
-               if ((inst->input_list == PAIR_LIST_UNKNOWN) || (*p != '\0')) {
-                       cf_log_err(conf, "Invalid input list '%s'", inst->input);
+               if (tmpl_afrom_attr_substr(inst, NULL, &inst->input_list, &FR_SBUFF_IN(inst->input, strlen(inst->input)), NULL, NULL) < 0) {
+                       cf_log_perr(conf, "Invalid input list '%s'", inst->input);
                        return -1;
                }
        }
 
        if (inst->output) {
-               p = inst->output;
-               p += tmpl_pair_list_name(&inst->output_list, p, PAIR_LIST_UNKNOWN);
-               if ((inst->output_list == PAIR_LIST_UNKNOWN) || (*p != '\0')) {
-                       cf_log_err(conf, "Invalid output list '%s'", inst->output);
+               if (tmpl_afrom_attr_substr(inst, NULL, &inst->output_list, &FR_SBUFF_IN(inst->input, strlen(inst->output)), NULL, NULL) < 0) {
+                       cf_log_perr(conf, "Invalid output list '%s'", inst->input);
                        return -1;
                }
        }
index 27d6a1f37bd10f17b510fc2a595b8ac142b47694..c7f27936939fce73bc83c4c871dd0a2f1ae19540 100644 (file)
@@ -229,7 +229,7 @@ static int getusersfile(TALLOC_CTX *ctx, char const *filename, fr_htrie_t **ptre
                         *      assertion that they do not get out of
                         *      sync.
                         */
-                       fr_assert(tmpl_list(map->lhs) == PAIR_LIST_REPLY);
+                       fr_assert(tmpl_list(map->lhs) == request_attr_reply);
                }
        }
 
index 5315d26d23c35901cae31fca5d59671b6615b30a..944ea49429489b58ebb0c2ef3a1c3166bc561b9f 100644 (file)
@@ -200,7 +200,7 @@ static xlat_action_t json_encode_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out,
                                              &json_arg_parse_rules,
                                              &(tmpl_rules_t){
                                                .attr = {
-                                                       .dict_def = request->dict
+                                                       .ctx = tmpl_attr_ctx_rules_default(NULL, NULL, request->dict)
                                                }
                                              });
                if (slen <= 0) {
index f09f0d0cfa3aee56bf32dc9e66e452c7098a916d..fc1cdf57b4f16326e1f8dfda7a23f5d9bcd5e9b8 100644 (file)
@@ -528,7 +528,7 @@ static unlang_action_t CC_HINT(nonnull) mod_do_linelog(rlm_rcode_t *p_result, mo
                                         NULL,
                                         &(tmpl_rules_t){
                                                .attr = {
-                                                       .dict_def = request->dict,
+                                                       .ctx = tmpl_attr_ctx_rules_default(NULL, NULL, request->dict),
                                                        .allow_unknown = true,
                                                        .allow_unresolved = false,
                                                },
index 59d05c503d372f24e3cab6d73365afea30e4952b..e15fa38de217536ad4545fbaf024df04988db0f8 100644 (file)
@@ -362,7 +362,7 @@ static void add_vp_tuple(TALLOC_CTX *ctx, request_t *request, fr_pair_list_t *vp
                                        &(tmpl_rules_t){
                                                .attr = {
                                                        .dict_def = request->dict,
-                                                       .list_def = PAIR_LIST_REPLY
+                                                       .list_def = &tmpl_attr_def_reply
                                                }
                                        }) <= 0) {
                        ERROR("Failed to find attribute %s", ckey);
index 9601f37331a9923e6b542b49adb20f7bec1a8e5d..0cf41b6392029ad528875aaa505529fe1981fe06 100644 (file)
@@ -299,8 +299,7 @@ static void mod_vptuple(TALLOC_CTX *ctx, module_ctx_t const *mctx, request_t *re
                if (tmpl_afrom_attr_str(ctx, NULL, &dst, s1,
                                        &(tmpl_rules_t){
                                                .attr = {
-                                                       .dict_def = request->dict,
-                                                       .list_def = PAIR_LIST_REPLY
+                                                       .ctx = tmpl_attr_ctx_rules_default(NULL, request_attr_reply, request->dict)
                                                }
                                        }) <= 0) {
                        ERROR("%s - Failed to find attribute %s.%s", funcname, list_name, s1);
index d8cfaa77bc7bbf70df314880f4d5aa8533fc1e73..d88b5defa855908c5daae57d317a0cc106a335a4 100644 (file)
@@ -742,8 +742,7 @@ static int rest_decode_post(UNUSED rlm_rest_t const *instance, UNUSED rlm_rest_s
                                        &(tmpl_rules_t){
                                                .attr = {
                                                        .prefix = TMPL_ATTR_REF_PREFIX_NO,
-                                                       .dict_def = request->dict,
-                                                       .list_def = PAIR_LIST_REPLY
+                                                       .ctx = tmpl_attr_ctx_rules_default(NULL, request_attr_reply, request->dict)
                                                }
                                        }) <= 0) {
                        RPWDEBUG("Failed parsing attribute (skipping)");
@@ -757,13 +756,13 @@ static int rest_decode_post(UNUSED rlm_rest_t const *instance, UNUSED rlm_rest_s
                        goto skip;
                }
 
-               vps = tmpl_list_head(current, tmpl_list(dst));
+               vps = tmpl_list_head(current, dst);
                if (!vps) {
                        RWDEBUG("List not valid in this context (skipping)");
                        talloc_free(dst);
                        goto skip;
                }
-               ctx = tmpl_list_ctx(current, tmpl_list(dst));
+               ctx = tmpl_list_ctx(current, dst);
                da = tmpl_attr_tail_da(dst);
 
                fr_assert(vps);
@@ -1033,8 +1032,7 @@ static int json_pair_alloc(rlm_rest_t const *instance, rlm_rest_section_t const
                                                &(tmpl_rules_t){
                                                        .attr = {
                                                                .prefix = TMPL_ATTR_REF_PREFIX_NO,
-                                                               .dict_def = request->dict,
-                                                               .list_def = PAIR_LIST_REPLY
+                                                               .ctx = tmpl_attr_ctx_rules_default(NULL, request_attr_reply, request->dict)
                                                        }
                                                }) <= 0) {
                                RPWDEBUG("Failed parsing attribute (skipping)");
@@ -1046,12 +1044,12 @@ static int json_pair_alloc(rlm_rest_t const *instance, rlm_rest_section_t const
                                continue;
                        }
 
-                       vps = tmpl_list_head(current, tmpl_list(dst));
+                       vps = tmpl_list_head(current, dst);
                        if (!vps) {
                                RWDEBUG("List not valid in this context (skipping)");
                                continue;
                        }
-                       ctx = tmpl_list_ctx(current, tmpl_list(dst));
+                       ctx = tmpl_list_ctx(current, dst);
 
                        /*
                         *  Alternative JSON structure which allows operator,