]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Add support for call_env callbacks
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Fri, 24 Nov 2023 22:52:41 +0000 (16:52 -0600)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Sat, 25 Nov 2023 01:11:33 +0000 (19:11 -0600)
These can either emit a tmpl_t which'll later be processed during evaluation, or emit a custom structure, which the module or xlat can access at runtime.

src/lib/unlang/call_env.c
src/lib/unlang/call_env.h
src/modules/rlm_cache/rlm_cache.c
src/modules/rlm_cache/rlm_cache.h
src/modules/rlm_chap/rlm_chap.c
src/modules/rlm_exec/rlm_exec.c
src/modules/rlm_ldap/rlm_ldap.c
src/modules/rlm_mschap/rlm_mschap.c
src/modules/rlm_pap/rlm_pap.c
src/modules/rlm_redis_ippool/rlm_redis_ippool.c
src/modules/rlm_smtp/rlm_smtp.c

index c8d8e0b8737baa7e50eb1fb6bb2804879d9d3599..08b58470b1831d4886c8fb1891987f0b2ffcd20b 100644 (file)
 RCSID("$Id$")
 
 #include <freeradius-devel/server/log.h>
+#include <freeradius-devel/server/cf_util.h>
+#include <freeradius-devel/server/tmpl.h>
 #include <freeradius-devel/unlang/tmpl.h>
 #include <freeradius-devel/unlang/function.h>
 #include <freeradius-devel/unlang/interpret.h>
+#include <freeradius-devel/unlang/call_env.h>
+#include <freeradius-devel/util/token.h>
+
 #include <talloc.h>
 #include "call_env.h"
 
+struct call_env_parsed_s {
+       call_env_parsed_entry_t         entry;          //!< Entry in list of parsed call_env_parsers.
+
+       union {
+               tmpl_t                          *tmpl;          //!< Tmpl produced from parsing conf pair.
+               void                            *ptr;           //!< Data produced from parsing conf pair.
+       } data;
+
+       size_t                          count;          //!< Number of CONF_PAIRs found, matching the #call_env_parser_t.
+       size_t                          multi_index;    //!< Array index for this instance.
+       call_env_parser_t const         *rule;          //!< Used to produce this.
+};
+FR_DLIST_FUNCS(call_env_parsed, call_env_parsed_t, entry)
 
 /** Parse the result of call_env tmpl expansion
  */
 static inline CC_HINT(always_inline)
-call_env_result_t call_env_value_parse(TALLOC_CTX *ctx, request_t *request, void *out,
-                                      void **tmpl_out, call_env_parsed_t const *env,
-                                      fr_value_box_list_t *tmpl_expanded)
+call_env_result_t call_env_result(TALLOC_CTX *ctx, request_t *request, void *out,
+                                 void **tmpl_out, call_env_parsed_t const *env,
+                                 fr_value_box_list_t *tmpl_expanded)
 {
        fr_value_box_t  *vb;
 
-       if (tmpl_out) *tmpl_out = env->tmpl;
-       if (env->tmpl_only) return CALL_ENV_SUCCESS;
+       if (tmpl_out) *tmpl_out = env->data.tmpl;
+       if (call_env_parse_only(env->rule->flags)) return CALL_ENV_SUCCESS;
 
        vb = fr_value_box_list_head(tmpl_expanded);
        if (!vb) {
                if (!call_env_nullable(env->rule->flags)) {
-                       RPEDEBUG("Failed to evaluate required module option %s = %s", env->rule->name, env->tmpl->name);
+                       RPEDEBUG("Failed to evaluate required module option %s = %s", env->rule->name, env->data.tmpl->name);
                        return CALL_ENV_MISSING;
                }
                return CALL_ENV_SUCCESS;
@@ -71,16 +89,16 @@ call_env_result_t call_env_value_parse(TALLOC_CTX *ctx, request_t *request, void
 
        while ((vb = fr_value_box_list_pop_head(tmpl_expanded))) {
                switch (env->rule->pair.type) {
-               case CALL_ENV_TYPE_VALUE_BOX:
+               case CALL_ENV_RESULT_TYPE_VALUE_BOX:
                        fr_value_box_copy_shallow(ctx, (fr_value_box_t *)(out), vb);
                        break;
 
-               case CALL_ENV_TYPE_VALUE_BOX_LIST:
+               case CALL_ENV_RESULT_TYPE_VALUE_BOX_LIST:
                        if (!fr_value_box_list_initialised((fr_value_box_list_t *)out)) fr_value_box_list_init((fr_value_box_list_t *)out);
                        fr_value_box_list_insert_tail((fr_value_box_list_t *)out, vb);
                        break;
 
-               case CALL_ENV_TYPE_TMPL_ONLY:
+               default:
                        fr_assert(0);
                        break;
                }
@@ -116,13 +134,37 @@ static unlang_action_t call_env_expand_start(UNUSED rlm_rcode_t *p_result, UNUSE
                env = call_env_rctx->last_expanded;
                fr_assert(env != NULL);
 
-               if (!env->tmpl_only) break;
+               /*
+                *      Subsections are expanded during parsing to produce a list of
+                *      call_env_parsed_t.  They are not expanded at runtime.
+                */
+               fr_assert_msg(call_env_is_subsection(env->rule->flags) == false, "Subsections cannot be expanded at runtime");
 
                /*
-                *      If we only need the tmpl, just set the pointer and move the next.
+                *      If there's an offset to copy the output to, do that.
+                *      We may also need to expand the tmpl_t and write out the result
+                *      to the pair offset.
                 */
-               out = (void **)((uint8_t *)*call_env_rctx->data + env->rule->parsed_offset);
-               *out = env->tmpl;
+               if (env->rule->pair.parsed.offset >= 0) {
+                       /*
+                        *      If we only need the tmpl or data, just set the pointer and move the next.
+                        */
+                       out = (void **)((uint8_t *)*call_env_rctx->data + env->rule->pair.parsed.offset);
+                       switch (env->rule->pair.parsed.type) {
+                       case CALL_ENV_PARSE_TYPE_TMPL:
+                               *out = env->data.tmpl;
+                               break;
+
+                       case CALL_ENV_PARSE_TYPE_VOID:
+                               *out = env->data.ptr;
+                               break;
+                       }
+               }
+
+               /*
+                *      If this is not parse_only, we need to expand the tmpl.
+                */
+               if (!call_env_parse_only(env->rule->flags)) break;
        }
 
        if (!call_env_rctx->last_expanded) {    /* No more! */
@@ -138,7 +180,7 @@ static unlang_action_t call_env_expand_start(UNUSED rlm_rcode_t *p_result, UNUSE
         *      Multi pair options should allocate boxes in the context of the array
         */
        if (call_env_multi(env->rule->flags)) {
-               out = (void **)((uint8_t *)(*call_env_rctx->data) + env->rule->pair.result_offset);
+               out = (void **)((uint8_t *)(*call_env_rctx->data) + env->rule->pair.offset);
 
                /*
                 *      For multi pair options, allocate the array before expanding the first entry.
@@ -152,7 +194,7 @@ static unlang_action_t call_env_expand_start(UNUSED rlm_rcode_t *p_result, UNUSE
                ctx = *out;
        }
 
-       if (unlang_tmpl_push(ctx, &call_env_rctx->tmpl_expanded, request, call_env_rctx->last_expanded->tmpl,
+       if (unlang_tmpl_push(ctx, &call_env_rctx->tmpl_expanded, request, call_env_rctx->last_expanded->data.tmpl,
                             NULL) < 0) return UNLANG_ACTION_FAIL;
 
        return UNLANG_ACTION_PUSHED_CHILD;
@@ -173,11 +215,11 @@ static unlang_action_t call_env_expand_repeat(UNUSED rlm_rcode_t *p_result, UNUS
        env = call_env_rctx->last_expanded;
        if (!env) return UNLANG_ACTION_CALCULATE_RESULT;
 
-       if (env->tmpl_only) goto tmpl_only;
+       if (call_env_parse_only(env->rule->flags)) goto parse_only;
        /*
         *      Find the location of the output
         */
-       out = ((uint8_t*)(*call_env_rctx->data)) + env->rule->pair.result_offset;
+       out = ((uint8_t*)(*call_env_rctx->data)) + env->rule->pair.offset;
 
        /*
         *      If this is a multi pair option, the output is an array.
@@ -188,10 +230,10 @@ static unlang_action_t call_env_expand_repeat(UNUSED rlm_rcode_t *p_result, UNUS
                out = ((uint8_t *)array) + env->rule->pair.size * env->multi_index;
        }
 
-tmpl_only:
-       if (env->rule->parsed_offset >= 0) tmpl_out = ((uint8_t *)*call_env_rctx->data) + env->rule->parsed_offset;
+parse_only:
+       if (env->rule->pair.parsed.offset >= 0) tmpl_out = ((uint8_t *)*call_env_rctx->data) + env->rule->pair.parsed.offset;
 
-       result = call_env_value_parse(*call_env_rctx->data, request, out, tmpl_out, env, &call_env_rctx->tmpl_expanded);
+       result = call_env_result(*call_env_rctx->data, request, out, tmpl_out, env, &call_env_rctx->tmpl_expanded);
        if (result != CALL_ENV_SUCCESS) {
                if (call_env_rctx->result) *call_env_rctx->result = result;
                return UNLANG_ACTION_FAIL;
@@ -232,6 +274,53 @@ unlang_action_t call_env_expand(TALLOC_CTX *ctx, request_t *request, call_env_re
                                    call_env_rctx);
 }
 
+/** Allocates a new call env parsed struct
+ *
+ */
+static inline CC_HINT(always_inline)
+call_env_parsed_t *call_env_parsed_alloc(TALLOC_CTX *ctx, call_env_parser_t const *rule)
+{
+       call_env_parsed_t       *call_env_parsed;
+
+       MEM(call_env_parsed = talloc_zero(ctx, call_env_parsed_t));
+       call_env_parsed->rule = rule;
+       call_env_parsed->count = 1;
+       call_env_parsed->multi_index = 0;
+
+       return call_env_parsed;
+}
+
+static inline CC_HINT(always_inline)
+int call_env_parsed_valid(call_env_parsed_t const *parsed, CONF_ITEM const *ci, call_env_parser_t const *rule)
+{
+       tmpl_t *tmpl;
+
+       if (rule->pair.parsed.type == CALL_ENV_PARSE_TYPE_VOID) return 0;
+
+       tmpl = parsed->data.tmpl;
+       switch (tmpl->type) {
+       case TMPL_TYPE_DATA:
+       case TMPL_TYPE_EXEC:
+       case TMPL_TYPE_XLAT:
+               if (call_env_attribute(rule->flags)) {
+                       cf_log_perr(ci, "'%s' expands to %s - attribute reference required", tmpl->name,
+                                   tmpl_type_to_str(tmpl->type));
+                       return -1;
+               }
+               FALL_THROUGH;
+
+       case TMPL_TYPE_ATTR:
+               break;
+
+       default:
+               cf_log_err(ci, "'%s' expands to invalid tmpl type %s", tmpl->name,
+                          tmpl_type_to_str(tmpl->type));
+               return -1;
+       }
+
+       return 0;
+}
+
 /** Parse per call env
  *
  * Used for config options which must be parsed in the context in which
@@ -240,42 +329,77 @@ unlang_action_t call_env_expand(TALLOC_CTX *ctx, request_t *request, call_env_re
  * @param[in] ctx              To allocate parsed environment in.
  * @param[out] parsed          Where to write parsed environment.
  * @param[in] name             Module name for error messages.
- * @param[in] dict_def         Default dictionary to use when tokenizing tmpls.
+ * @param[in] namespace                we're operating in.
  * @param[in] cs               Module config.
- * @param[in] call_env         to parse.
+ * @param[in] rule             to parse.
  * @return
  *     - 0 on success;
  *     - <0 on failure;
  */
-static int call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *parsed, char const *name, fr_dict_t const *dict_def,
-                         CONF_SECTION const *cs, call_env_parser_t const *call_env) {
+static int call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *parsed, char const *name, fr_dict_t const *namespace,
+                         CONF_SECTION const *cs, call_env_parser_t const *rule) {
        CONF_PAIR const         *cp, *next;
        call_env_parsed_t       *call_env_parsed;
-       ssize_t                 len, count, multi_index;
-       char const              *value;
-       fr_token_t              quote;
+       ssize_t                 count, multi_index;
        fr_type_t               type;
 
-       while (call_env->name) {
-               if (call_env_is_subsection(call_env->flags)) {
+       while (rule->name) {
+               if (call_env_is_subsection(rule->flags)) {
                        CONF_SECTION const *subcs;
-                       subcs = cf_section_find(cs, call_env->name, call_env->section.ident2);
+                       subcs = cf_section_find(cs, rule->name, rule->section.ident2);
                        if (!subcs) {
-                               if (!call_env_required(call_env->flags)) goto next;
-                               cf_log_err(cs, "Module %s missing required section %s", name, call_env->name);
+                               if (!call_env_required(rule->flags)) goto next;
+                               cf_log_err(cs, "Module %s missing required section \"%s\"", name, rule->name);
                                return -1;
                        }
 
-                       if (call_env_parse(ctx, parsed, name, dict_def, subcs, call_env->section.subcs) < 0) return -1;
+                       /*
+                        *      Hand off to custom parsing function if there is one...
+                        */
+                       if (rule->section.func) {
+                               /*
+                                *      Record our position so we can process any new entries
+                                *      after the callback returns.
+                                */
+                               call_env_parsed_t *last = call_env_parsed_tail(parsed);
+
+                               if (rule->section.func(ctx, parsed, namespace, cf_section_to_item(subcs), rule) < 0) {
+                                       cf_log_perr(cs, "Failed parsing configuration section %s", rule->name);
+                                       talloc_free(call_env_parsed);
+                                       return -1;
+                               }
+
+                               call_env_parsed = last;
+                               while ((call_env_parsed = call_env_parsed_prev(parsed, call_env_parsed))) {
+                                       if (call_env_parsed_valid(call_env_parsed, cf_section_to_item(subcs), rule) < 0) {
+                                               cf_log_err(cf_section_to_item(subcs), "Invalid data produced by %s", rule->name);
+                                               return -1;
+                                       }
+                                       count++; /* Get the total */
+                               }
+
+                               /*
+                                *      Now fixup the count and multi_index for each entry
+                                *      produced by the subsection callback.
+                                */
+                               call_env_parsed = last;
+                               while ((call_env_parsed = call_env_parsed_prev(parsed, call_env_parsed))) {
+                                       call_env_parsed->count = count;
+                                       call_env_parsed->multi_index = multi_index++;
+                               }
+                               goto next;
+                       }
+
+                       if (call_env_parse(ctx, parsed, name, namespace, subcs, rule->section.subcs) < 0) return -1;
                        goto next;
                }
 
-               cp = cf_pair_find(cs, call_env->name);
+               cp = cf_pair_find(cs, rule->name);
 
-               if (!cp && !call_env->pair.dflt) {
-                       if (!call_env_required(call_env->flags)) goto next;
+               if (!cp && !rule->pair.dflt) {
+                       if (!call_env_required(rule->flags)) goto next;
 
-                       cf_log_err(cs, "Module %s missing required option %s", name, call_env->name);
+                       cf_log_err(cs, "Module %s missing required option %s", name, rule->name);
                        return -1;
                }
 
@@ -283,76 +407,82 @@ static int call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *parsed, char
                 *      Check for additional conf pairs and error
                 *      if there is one and multi is not allowed.
                 */
-               if (!call_env_multi(call_env->flags) && ((next = cf_pair_find_next(cs, cp, call_env->name)))) {
-                       cf_log_err(cf_pair_to_item(next), "Invalid duplicate configuration item '%s'", call_env->name);
+               if (!call_env_multi(rule->flags) && ((next = cf_pair_find_next(cs, cp, rule->name)))) {
+                       cf_log_err(cf_pair_to_item(next), "Invalid duplicate configuration item '%s'", rule->name);
                        return -1;
                }
 
-               count = cf_pair_count(cs, call_env->name);
+               count = cf_pair_count(cs, rule->name);
                if (count == 0) count = 1;
 
-               for (multi_index = 0; multi_index < count; multi_index ++) {
-                       MEM(call_env_parsed = talloc_zero(ctx, call_env_parsed_t));
-                       call_env_parsed->rule = call_env;
+               for (multi_index = 0; multi_index < count; multi_index++) {
+                       CONF_PAIR *tmp_cp = NULL;
+                       CONF_PAIR const *to_parse;
+
+                       call_env_parsed = call_env_parsed_alloc(ctx, rule);
                        call_env_parsed->count = count;
                        call_env_parsed->multi_index = multi_index;
-                       if (call_env->pair.type == CALL_ENV_TYPE_TMPL_ONLY) call_env_parsed->tmpl_only = true;
 
+                       /*
+                        *      With the conf_parser code we can add default pairs
+                        *      if they don't exist, but as the same CONF_SECTIONs
+                        *      are evaluated multiple times for each module call
+                        *      we can't do that here.
+                        */
                        if (cp) {
-                               value = cf_pair_value(cp);
-                               len = talloc_array_length(value) - 1;
-                               quote = call_env_force_quote(call_env->flags) ? call_env->pair.dflt_quote : cf_pair_value_quote(cp);
+                               if (call_env_force_quote(rule->flags)) {
+                                       to_parse = tmp_cp = cf_pair_alloc(NULL,
+                                                                         cf_pair_attr(cp), cf_pair_value(cp), cf_pair_operator(cp),
+                                                                         cf_pair_attr_quote(cp),
+                                                                         call_env_force_quote(rule->flags) ? rule->pair.dflt_quote : cf_pair_value_quote(cp));
+                               } else {
+                                       to_parse = cp;
+                               }
                        } else {
-                               value = call_env->pair.dflt;
-                               len = strlen(value);
-                               quote = call_env->pair.dflt_quote;
-                       }
-
-                       type = call_env->pair.cast_type;
-                       if (tmpl_afrom_substr(call_env_parsed, &call_env_parsed->tmpl, &FR_SBUFF_IN(value, len),
-                                             quote, NULL, &(tmpl_rules_t){
-                                                       .cast = (type == FR_TYPE_VOID ? FR_TYPE_NULL : type),
-                                                       .attr = {
-                                                               .list_def = request_attr_request,
-                                                               .dict_def = dict_def
-                                                       }
-                                               }) < 0) {
-                       error:
-                               talloc_free(call_env_parsed);
-                               cf_log_perr(cp, "Failed to parse configuration item '%s = %s'", call_env->name, value);
-                               return -1;
+                               to_parse = tmp_cp = cf_pair_alloc(NULL,
+                                                                 rule->name, rule->pair.dflt, T_OP_EQ,
+                                                                 T_BARE_WORD, rule->pair.dflt_quote);
                        }
 
                        /*
-                        *      Ensure only valid TMPL types are produced.
+                        *      The parsing function can either produce a tmpl_t as tmpl_afrom_substr
+                        *      would, or produce a custom structure, which will be copied into the
+                        *      result structure.
                         */
-                       switch (call_env_parsed->tmpl->type) {
-                       case TMPL_TYPE_DATA:
-                       case TMPL_TYPE_EXEC:
-                       case TMPL_TYPE_XLAT:
-                               if (call_env_attribute(call_env->flags)) {
-                                       cf_log_perr(cp, "'%s' expands to %s - attribute reference required", value,
-                                                   fr_table_str_by_value(tmpl_type_table, call_env_parsed->tmpl->type,
-                                                                         "<INVALID>"));
+                       if (rule->pair.func) {
+                               if (unlikely(rule->pair.func(ctx, &call_env_parsed->data, namespace, cf_pair_to_item(to_parse), rule) < 0)) {
+                               error:
+                                       cf_log_perr(to_parse, "Failed to parse configuration item '%s = %s'", rule->name, cf_pair_value(to_parse));
+                                       talloc_free(call_env_parsed);
+                                       talloc_free(tmp_cp);
+                                       return -1;
+                               }
+                       } else {
+                               type = rule->pair.cast_type;
+                               if (tmpl_afrom_substr(call_env_parsed, &call_env_parsed->data.tmpl,
+                                                     &FR_SBUFF_IN(cf_pair_value(to_parse), talloc_array_length(cf_pair_value(to_parse)) - 1),
+                                                     cf_pair_value_quote(to_parse), NULL, &(tmpl_rules_t){
+                                                               .cast = ((type == FR_TYPE_VOID) ? FR_TYPE_NULL : type),
+                                                               .attr = {
+                                                                       .list_def = request_attr_request,
+                                                                       .dict_def = namespace
+                                                               }
+                                               }) < 0) {
                                        goto error;
                                }
-                               FALL_THROUGH;
-
-                       case TMPL_TYPE_ATTR:
-                               break;
-
-                       default:
-                               cf_log_err(cp, "'%s' expands to invalid tmpl type %s", value,
-                                          fr_table_str_by_value(tmpl_type_table, call_env_parsed->tmpl->type, "<INVALID>"));
-                               goto error;
                        }
 
-                       call_env_parsed_insert_tail(parsed, call_env_parsed);
+                       /*
+                        *      Ensure only valid data is produced.
+                        */
+                       if (call_env_parsed_valid(call_env_parsed, cf_pair_to_item(to_parse), rule) < 0) goto error;
 
-                       cp = cf_pair_find_next(cs, cp, call_env->name);
+                       talloc_free(tmp_cp);
+                       call_env_parsed_insert_tail(parsed, call_env_parsed);
+                       cp = cf_pair_find_next(cs, cp, rule->name);
                }
        next:
-               call_env++;
+               rule++;
        }
 
        return 0;
@@ -380,7 +510,10 @@ static size_t call_env_count(size_t *names_len, CONF_SECTION const *cs, call_env
                        subcs = cf_section_find(cs, call_env->name, call_env->section.ident2);
                        if (!subcs) goto next;
 
-                       tmpl_count += call_env_count(names_len, subcs, call_env->section.subcs);
+                       /*
+                        *      May only be a callback...
+                        */
+                       if (call_env->section.subcs) tmpl_count += call_env_count(names_len, subcs, call_env->section.subcs);
                        goto next;
                }
                pair_count = 0;
@@ -401,6 +534,60 @@ static size_t call_env_count(size_t *names_len, CONF_SECTION const *cs, call_env
        return tmpl_count;
 }
 
+/** Allocate a new call_env_parsed_t structure and add it to the list of parsed call envs
+ *
+ * @note tmpl_t and void * should be allocated in the context of the call_env_parsed_t
+ *
+ * @param[in] ctx      to allocate the new call_env_parsed_t in.
+ * @param[out] head    to add the new call_env_parsed_t to.
+ * @param[in] rule     to base call_env_parsed_t around.  MUST NOT BE THE RULE PASSED TO THE CALLBACK.
+ *                     The rule passed to the callback describes how to parse a subsection, but the
+ *                     subsection callback is adding rules describing how to parse its children.
+ * @return             The new call_env_parsed_t.
+ */
+call_env_parsed_t *call_env_parsed_add(TALLOC_CTX *ctx, call_env_parsed_head_t *head, call_env_parser_t const *rule)
+{
+       call_env_parsed_t       *call_env_parsed;
+       call_env_parser_t       *our_rules;
+
+       fr_assert_msg(call_env_is_subsection(rule->flags) == false, "Rules added by subsection callbacks cannot be subsections themselves");
+
+       MEM(call_env_parsed = call_env_parsed_alloc(ctx, rule));
+
+       /*
+        *      Copy the rule the callback provided, there's no guarantee
+        *      it's not stack allocated, or in some way ephemeral.
+        */
+       MEM(our_rules = talloc(call_env_parsed, call_env_parser_t));
+       memcpy(our_rules, rule, sizeof(*our_rules));
+       call_env_parsed->rule = our_rules;
+       call_env_parsed_insert_tail(head, call_env_parsed);
+
+       return call_env_parsed;
+}
+
+/** Assign a tmpl to a call_env_parsed_t
+ *
+ * @param[in] parsed           to assign the tmpl to.
+ * @param[in] tmpl             to assign.
+ */
+void call_env_parsed_set_tmpl(call_env_parsed_t *parsed, tmpl_t *tmpl)
+{
+       fr_assert_msg(parsed->rule->pair.parsed.type == CALL_ENV_PARSE_TYPE_TMPL, "Rule must indicate parsed output is a tmpl_t");
+       parsed->data.tmpl = tmpl;
+};
+
+/** Assign data to a call_env_parsed_t
+ *
+ * @param[in] parsed           to assign the tmpl to.
+ * @param[in] data             to assign.
+ */
+void call_env_parsed_set_data(call_env_parsed_t *parsed, void *data)
+{
+       fr_assert_msg(parsed->rule->pair.parsed.type == CALL_ENV_PARSE_TYPE_VOID, "Rule must indicate parsed output is a void *");
+       parsed->data.ptr = data;
+};
+
 /** Given a call_env_method, parse all call_env_pair_t in the context of a specific call to an xlat or module method
  *
  * @param[in] ctx              to allocate the call_env_t in.
index 5107dd349c99f3bbac696f952ab60a4f2ffd7fd3..d2dd9e562eb36e7a7f2d74b53c4cdb29010495f8 100644 (file)
@@ -50,11 +50,19 @@ typedef enum {
        CALL_ENV_INVALID = -2
 } call_env_result_t;
 
+/** What type of structure is produced by the parsing phase
+ */
 typedef enum {
-       CALL_ENV_TYPE_VALUE_BOX = 1,
-       CALL_ENV_TYPE_VALUE_BOX_LIST,
-       CALL_ENV_TYPE_TMPL_ONLY
-} call_env_dst_t;
+       CALL_ENV_PARSE_TYPE_TMPL = 1,                           //!< Output of the parsing phase is a tmpl_t.
+       CALL_ENV_PARSE_TYPE_VOID                                //!< Output of the parsing phase is undefined (a custom structure).
+} call_env_parse_type_t;
+
+/** What type of structure is produced by the evaluation phase
+ */
+typedef enum {
+       CALL_ENV_RESULT_TYPE_VALUE_BOX = 1,                     //!< Output of the evaluation phase is a single value box.
+       CALL_ENV_RESULT_TYPE_VALUE_BOX_LIST,                    //!< Output of the evaluation phase is a list of value boxes.
+} call_env_result_type_t;
 
 DIAG_OFF(attributes)
 typedef enum CC_HINT(flag_enum) {
@@ -69,8 +77,9 @@ typedef enum CC_HINT(flag_enum) {
                                                                ///< where tmpls should always be parsed with a particular quoting
                                                                ///< regardless of how they are in the config file.  E.g. the `program`
                                                                ///< option of `rlm_exec` should always be parsed as T_BACK_QUOTED_STRING.
-       CALL_ENV_FLAG_ATTRIBUTE         = (1 << 6),             //!< Tmpl must contain an attribute reference.
-       CALL_ENV_FLAG_SUBSECTION        = (1 << 7)              //!< This is a subsection.
+       CALL_ENV_FLAG_PARSE_ONLY        = (1 << 6),             //!< The result of parsing will not be evaluated at runtime.
+       CALL_ENV_FLAG_ATTRIBUTE         = (1 << 7),             //!< Tmpl must contain an attribute reference.
+       CALL_ENV_FLAG_SUBSECTION        = (1 << 8)              //!< This is a subsection.
 } call_env_flags_t;
 DIAG_ON(attributes)
 
@@ -103,11 +112,43 @@ DIAG_ON(attributes)
 
 #define call_env_force_quote(_flags)           ((_flags) & CALL_ENV_FLAG_FORCE_QUOTE)
 
+#define call_env_parse_only(_flags)            ((_flags) & CALL_ENV_FLAG_PARSE_ONLY)
+
 #define call_env_attribute(_flags)             ((_flags) & CALL_ENV_FLAG_ATTRIBUTE)
 
 #define call_env_is_subsection(_flags)         ((_flags) & CALL_ENV_FLAG_SUBSECTION)
 /** @} */
 
+/** Callback for performing custom parsing of a #CONF_PAIR
+ *
+ * @param[in] ctx              to allocate any data in.
+ * @param[out] out             Where to write the result of parsing.
+ * @param[in] namespace                we're operating in.
+ * @param[in] ci               The #CONF_SECTION or #CONF_PAIR to parse.
+ * @param[in] rule             Parse rules - How the #CONF_PAIR or #CONF_SECTION should be converted.
+ * @return
+ *     - 0 on success.
+ *     - -1 on failure.
+ */
+typedef int (*call_env_parse_pair_t)(TALLOC_CTX *ctx, void *out, fr_dict_t const *namespace, CONF_ITEM *ci, call_env_parser_t const *rule);
+
+/** Callback for performing custom parsing of a #CONF_SECTION
+ *
+ * The callback function is expected to call call_env_parsed_add to allocate a new
+ * call_env_parsed_t, and either call_env_parsed_set_tmpl, or call_env_parsed_set_data to populate
+ * the call env_parsed_t structure.
+ *
+ * @param[in] ctx              to allocate any data in.
+ * @param[out] out             Where to write the result of parsing.
+ * @param[in] namespace                we're operating in.
+ * @param[in] ci               The #CONF_SECTION or #CONF_PAIR to parse.
+ * @param[in] rule             Parse rules - How the #CONF_PAIR or #CONF_SECTION should be converted.
+ * @return
+ *     - 0 on success.
+ *     - -1 on failure.
+ */
+typedef int (*call_env_parse_section_t)(TALLOC_CTX *ctx, call_env_parsed_head_t *out, fr_dict_t const *namespace, CONF_ITEM *ci, call_env_parser_t const *rule);
+
 /** Per method call config
  *
  * Similar to a conf_parser_t used to hold details of conf pairs
@@ -120,44 +161,40 @@ struct call_env_parser_s {
        char const              *name;                  //!< Of conf pair to pass to tmpl_tokenizer.
        call_env_flags_t        flags;                  //!< Flags controlling parser behaviour.
 
-       ssize_t                 parsed_offset;          //!< Where to write the result of the parsing phase.
-                                                       ///< This is usually a tmpl_t, but could be other things when a callback
-                                                       ///< function is used to parse the CONF_SECTION or CONF_PAIR.
-
        union {
                struct {
-                       fr_type_t       cast_type;              //!< To cast boxes to. Also contains flags controlling parser behaviour.
+                       fr_type_t                       cast_type;      //!< To cast boxes to. Also contains flags controlling parser behaviour.
+
+                       call_env_result_type_t          type;           //!< Type of structure boxes will be written to.
+                       size_t                          size;           //!< Size of structure boxes will be written to.
+                       char const                      *type_name;     //!< Name of structure type boxes will be written to.
+                       size_t                          offset;         //!< Where to write the result of evaluating the tmpl_t produced in the parsing phase.
+
+                       char const                      *dflt;          //!< Default string to pass to the tmpl_tokenizer if no CONF_PAIR found.
+                       fr_token_t                      dflt_quote;     //!< Quoting for the default string.
 
-                       call_env_dst_t  type;                   //!< Type of structure boxes will be written to.
-                       size_t          size;                   //!< Size of structure boxes will be written to.
-                       char const      *type_name;             //!< Name of structure type boxes will be written to.
-                       size_t          result_offset;          //!< Where to write the result of evaluating the tmpl_t produced in the parsing phase.
+                       call_env_parse_pair_t           func;           //!< Callback for parsing a CONF_PAIR
 
-                       char const      *dflt;                  //!< Default string to pass to the tmpl_tokenizer if no CONF_PAIR found.
-                       fr_token_t      dflt_quote;             //!< Quoting for the default string.
+                       struct {
+                               ssize_t                         offset;         //!< Where to write the result of the parsing phase.
+                                                                               ///< This is usually a tmpl_t, but could be other things when a callback
+                                                                               ///< function is used to parse the CONF_SECTION or CONF_PAIR.
+
+                               call_env_parse_type_t           type;           //!< What type of output the parsing phase is expected to produce.
+                       } parsed;
                } pair;
 
                struct {
-                       char const              *ident2;        //!< Second identifier for a section
-                       call_env_parser_t const *subcs;         //!< Nested definitions for subsection.
+                       char const                      *ident2;        //!< Second identifier for a section
+                       call_env_parser_t const         *subcs;         //!< Nested definitions for subsection.
+
+                       call_env_parse_section_t        func;           //!< Callback for parsing CONF_SECTION.
                } section;
        };
 };
 
 #define CALL_ENV_TERMINATOR { NULL }
 
-struct call_env_parsed_s {
-       call_env_parsed_entry_t         entry;          //!< Entry in list of parsed call_env_parsers.
-       tmpl_t                          *tmpl;          //!< Tmpl produced from parsing conf pair.
-       size_t                          count;          //!< Number of CONF_PAIRs found, matching the #call_env_parser_t.
-       size_t                          multi_index;    //!< Array index for this instance.
-       call_env_parser_t const         *rule;          //!< Used to produce this.
-       bool                            tmpl_only;      //!< Don't evaluate before module / xlat call.
-                                                       ///< Only the tmpl reference is needed.
-};
-
-FR_DLIST_FUNCS(call_env_parsed, call_env_parsed_t, entry)
-
 /** Helper macro for populating the size/type fields of a #call_env_method_t from the output structure type
  */
 #define FR_CALL_ENV_METHOD_OUT(_inst) \
@@ -177,6 +214,14 @@ struct call_env_s {
        call_env_method_t const         *method;                //!< The method this call env is for.
 };
 
+/** Where we're specifying a parsing phase output field, determine its type
+ */
+#define CALL_ENV_PARSE_TYPE(_s, _f) \
+_Generic((((_s *)NULL)->_f), \
+       tmpl_t *                        : CALL_ENV_PARSE_TYPE_TMPL, \
+       default                         : CALL_ENV_PARSE_TYPE_VOID \
+)
+
 /** Derive whether tmpl can only emit a single box.
  */
 #define FR_CALL_ENV_SINGLE(_s, _f, _c) \
@@ -184,7 +229,8 @@ _Generic((((_s *)NULL)->_f), \
        fr_value_box_t                  : __builtin_choose_expr(_c, CALL_ENV_FLAG_NONE, CALL_ENV_FLAG_SINGLE), \
        fr_value_box_t *                : __builtin_choose_expr(_c, CALL_ENV_FLAG_NONE, CALL_ENV_FLAG_SINGLE), \
        fr_value_box_list_t             : CALL_ENV_FLAG_NONE, \
-       fr_value_box_list_t *           : CALL_ENV_FLAG_SINGLE \
+       fr_value_box_list_t *           : CALL_ENV_FLAG_SINGLE, \
+       default                         : CALL_ENV_FLAG_NONE \
 )
 
 /** Derive whether multi conf pairs are allowed from target field type.
@@ -208,10 +254,10 @@ __builtin_choose_expr(_c, (void)0, false)))
  */
 #define FR_CALL_ENV_DST_TYPE(_s, _f) \
 _Generic((((_s *)NULL)->_f), \
-       fr_value_box_t                  : CALL_ENV_TYPE_VALUE_BOX, \
-       fr_value_box_t *                : CALL_ENV_TYPE_VALUE_BOX, \
-       fr_value_box_list_t             : CALL_ENV_TYPE_VALUE_BOX_LIST, \
-       fr_value_box_list_t *           : CALL_ENV_TYPE_VALUE_BOX_LIST \
+       fr_value_box_t                  : CALL_ENV_RESULT_TYPE_VALUE_BOX, \
+       fr_value_box_t *                : CALL_ENV_RESULT_TYPE_VALUE_BOX, \
+       fr_value_box_list_t             : CALL_ENV_RESULT_TYPE_VALUE_BOX_LIST, \
+       fr_value_box_list_t *           : CALL_ENV_RESULT_TYPE_VALUE_BOX_LIST \
 )
 
 #define FR_CALL_ENV_DST_SIZE(_s, _f) \
@@ -219,15 +265,17 @@ _Generic((((_s *)NULL)->_f), \
        fr_value_box_t                  : sizeof(fr_value_box_t), \
        fr_value_box_t *                : sizeof(fr_value_box_t), \
        fr_value_box_list_t             : sizeof(fr_value_box_list_t), \
-       fr_value_box_list_t *           : sizeof(fr_value_box_list_t) \
+       fr_value_box_list_t *           : sizeof(fr_value_box_list_t), \
+       default                         : 0 \
 )
 
-#define FR_CALL_ENV_DST_TYPE_NAME(_s, _f) \
+#define FR_CALL_ENV_RESULT_TYPE_NAME(_s, _f) \
 _Generic((((_s *)NULL)->_f), \
        fr_value_box_t                  : "fr_value_box_t", \
        fr_value_box_t *                : "fr_value_box_t", \
        fr_value_box_list_t             : "fr_value_box_list_t", \
-       fr_value_box_list_t *           : "fr_value_box_list_t" \
+       fr_value_box_list_t *           : "fr_value_box_list_t", \
+       default                         : NULL \
 )
 
 typedef void _mismatch_flags;          //!< Dummy type used to indicate bad flags.
@@ -238,43 +286,76 @@ typedef void _mismatch_flags;             //!< Dummy type used to indicate bad flags.
                        FR_CALL_ENV_MULTI(_struct, _field) |\
                        ((_flags) & ~CALL_ENV_FLAG_CONCAT)) \
 
+/** Specify a call_env_parser_t which writes out runtime results to the specified field
+ *
+ * @param[in] _name            of the conf pair to parse.
+ * @param[in] _cast_type       Cast any value boxes produced to this type.
+ * @param[in] _flags           controlling parser behaviour.
+ * @param[in] _struct          which contains the field to write the result of the evaluation phase to.
+ * @param[in] _field           where to write the result.
+ */
 #define FR_CALL_ENV_OFFSET(_name, _cast_type, _flags, _struct, _field) \
        .name = _name, \
        .flags = CALL_ENV_FLAGS(_cast_type, _flags, _struct, _field), \
-       .parsed_offset = -1, \
        .pair = { \
                .cast_type = _cast_type, \
                .type = FR_CALL_ENV_DST_TYPE(_struct, _field), \
                .size = FR_CALL_ENV_DST_SIZE(_struct, _field), \
-               .type_name = FR_CALL_ENV_DST_TYPE_NAME(_struct, _field), \
-               .result_offset = offsetof(_struct, _field), \
+               .type_name = FR_CALL_ENV_RESULT_TYPE_NAME(_struct, _field), \
+               .offset = offsetof(_struct, _field), \
+               .parsed = { \
+                       .offset = -1, \
+                       .type = CALL_ENV_PARSE_TYPE_TMPL \
+               } \
        }
 
-/** Version of the above which sets optional field for pointer to tmpl
+/** Specify a call_env_parser_t which writes out runtime results and the result of the parsing phase to the fields specified
+ *
+ * @param[in] _name            of the conf pair to parse.
+ * @param[in] _cast_type       Cast any value boxes produced to this type.
+ * @param[in] _flags           controlling parser behaviour.
+ * @param[in] _struct          which contains the field to write the result of the evaluation phase to.
+ * @param[in] _field           where to write the result.
+ * @param[in] _parse_field     where to write the result of the parsing phase.
+ *                             This must be a field in the specified _struct.
  */
-#define FR_CALL_ENV_TMPL_OFFSET(_name, _cast_type, _flags, _struct, _field, _tmpl_field) \
+#define FR_CALL_ENV_PARSE_OFFSET(_name, _cast_type, _flags, _struct, _field, _parse_field) \
        .name = _name, \
        .flags = CALL_ENV_FLAGS(_cast_type, _flags, _struct, _field), \
-       .parsed_offset = offsetof(_struct, _tmpl_field), \
        .pair = { \
                .cast_type = _cast_type, \
                .type = FR_CALL_ENV_DST_TYPE(_struct, _field), \
                .size = FR_CALL_ENV_DST_SIZE(_struct, _field), \
-               .type_name = FR_CALL_ENV_DST_TYPE_NAME(_struct, _field), \
-               .result_offset = offsetof(_struct, _field), \
+               .type_name = FR_CALL_ENV_RESULT_TYPE_NAME(_struct, _field), \
+               .offset = offsetof(_struct, _field), \
+               .parsed = { \
+                       .offset = offsetof(_struct, _parse_field), \
+                       .type = CALL_ENV_PARSE_TYPE(_struct, _parse_field) \
+               } \
        }
 
-/** Version of the above which only sets the field for a pointer to the tmpl
+/** Specify a call_env_parser_t which writes out the result of the parsing phase to the field specified
+ *
+ * @param[in] _name            of the conf pair to parse.
+ * @param[in] _cast_type       Sets the cast used by the tmpl.
+ * @param[in] _flags           controlling parser behaviour.
+ * @param[in] _struct          which contains the field to write the result of the evaluation phase to.
+ * @param[in] _parse_field     where to write the result of the parsing phase.
+ *                             This must be a field in the specified _struct.
  */
-#define FR_CALL_ENV_TMPL_ONLY_OFFSET(_name, _cast_type, _flags, _struct, _tmpl_field) \
+#define FR_CALL_ENV_PARSE_ONLY_OFFSET(_name, _cast_type, _flags, _struct, _parse_field) \
        .name = _name, \
-       .flags = _flags, \
-       .parsed_offset = offsetof(_struct, _tmpl_field), \
+       .flags = (_flags) | CALL_ENV_FLAG_PARSE_ONLY, \
        .pair = { \
                .cast_type = _cast_type, \
-               .type = CALL_ENV_TYPE_TMPL_ONLY \
+               .parsed = { \
+                       .offset = offsetof(_struct, _parse_field), \
+                       .type = CALL_ENV_PARSE_TYPE(_struct, _parse_field) \
+               } \
        }
 
+/** Specify a call_env_parser_t which defines a nested subsection
+ */
 #define FR_CALL_ENV_SUBSECTION(_name, _ident2, _flags, _subcs ) \
        .name = _name, \
        .flags = CALL_ENV_FLAG_SUBSECTION | (_flags), \
@@ -283,8 +364,24 @@ typedef void _mismatch_flags;              //!< Dummy type used to indicate bad flags.
                .subcs = _subcs, \
        }
 
+/** Specify a call_env_parser_t which parses a subsection using a callback function
+ */
+#define FR_CALL_ENV_SUBSECTION_FUNC(_name, _ident2, _flags, _func) \
+       .name = _name, \
+       .flags = CALL_ENV_FLAG_SUBSECTION | (_flags), \
+       .section = { \
+               .ident2 = _ident2, \
+               .func = _func \
+       }
+
 unlang_action_t call_env_expand(TALLOC_CTX *ctx, request_t *request, call_env_result_t *result, void **env_data, call_env_t const *call_env);
 
+call_env_parsed_t *call_env_parsed_add(TALLOC_CTX *ctx, call_env_parsed_head_t *head, call_env_parser_t const *rule);
+
+void call_env_parsed_set_tmpl(call_env_parsed_t *parsed, tmpl_t *tmpl);
+
+void call_env_parsed_set_data(call_env_parsed_t *parsed, void *data);
+
 call_env_t *call_env_alloc(TALLOC_CTX *ctx, char const *name, call_env_method_t const *call_env_method,
                           fr_dict_t const *namespace, CONF_SECTION *cs) CC_HINT(nonnull(3,4,5));
 
index 6793f11d674f730df22135d2e27a6e262813fa8d..d4676b1077ed8a974817d433bdeeb14d21dfb6cd 100644 (file)
@@ -38,6 +38,8 @@ RCSID("$Id$")
 
 extern module_rlm_t rlm_cache;
 
+static int update_section_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, fr_dict_t const *namespace, CONF_ITEM *ci, UNUSED call_env_parser_t const *rule);
+
 static const conf_parser_t module_config[] = {
        { FR_CONF_OFFSET_TYPE_FLAGS("driver", FR_TYPE_VOID, 0, rlm_cache_t, driver_submodule), .dflt = "rbtree",
                         .func = module_rlm_submodule_parse },
@@ -51,13 +53,15 @@ static const conf_parser_t module_config[] = {
 };
 
 typedef struct {
-       fr_value_box_t  *key;
+       fr_value_box_t          *key;                   //!< To lookup the cache entry with.
+       map_list_t              *maps;                  //!< Attribute map applied to cache entries.
 } cache_call_env_t;
 
 static const call_env_method_t cache_method_env = {
        FR_CALL_ENV_METHOD_OUT(cache_call_env_t),
        .env = (call_env_parser_t[]) {
                { FR_CALL_ENV_OFFSET("key", FR_TYPE_STRING, CALL_ENV_FLAG_REQUIRED | CALL_ENV_FLAG_CONCAT, cache_call_env_t, key) },
+               { FR_CALL_ENV_SUBSECTION_FUNC("update", CF_IDENT_ANY, CALL_ENV_FLAG_REQUIRED, update_section_parse) },
                CALL_ENV_TERMINATOR
        }
 };
@@ -313,7 +317,7 @@ static unlang_action_t cache_expire(rlm_rcode_t *p_result,
  */
 static unlang_action_t cache_insert(rlm_rcode_t *p_result,
                                    rlm_cache_t const *inst, request_t *request, rlm_cache_handle_t **handle,
-                                   fr_value_box_t const *key, fr_time_delta_t ttl)
+                                   fr_value_box_t const *key, map_list_t const *maps, fr_time_delta_t ttl)
 {
        map_t                   const *map = NULL;
        map_t                   *c_map;
@@ -354,7 +358,7 @@ static unlang_action_t cache_insert(rlm_rcode_t *p_result,
         *      gathering fr_pair_ts to cache.
         */
        pool = talloc_pool(NULL, 2048);
-       while ((map = map_list_next(&inst->maps, map))) {
+       while ((map = map_list_next(maps, map))) {
                fr_pair_list_t  to_cache;
 
                fr_pair_list_init(&to_cache);
@@ -528,21 +532,6 @@ static unlang_action_t cache_set_ttl(rlm_rcode_t *p_result,
        }
 }
 
-/** Verify that a map in the cache section makes sense
- *
- */
-static int cache_verify(map_t *map, void *ctx)
-{
-       if (unlang_fixup_update(map, ctx) < 0) return -1;
-
-       if (!tmpl_is_attr(map->lhs)) {
-               cf_log_err(map->ci, "Destination must be an attribute ref or a list");
-               return -1;
-       }
-
-       return 0;
-}
-
 /** Do caching checks
  *
  * Since we can update ANY VP list, we do exactly the same thing for all sections
@@ -756,7 +745,7 @@ static unlang_action_t CC_HINT(nonnull) mod_cache_it(rlm_rcode_t *p_result, modu
        if (insert && (exists == 0)) {
                rlm_rcode_t tmp;
 
-               cache_insert(&tmp, inst, request, &handle, env->key, ttl);
+               cache_insert(&tmp, inst, request, &handle, env->key, env->maps, ttl);
                switch (tmp) {
                case RLM_MODULE_FAIL:
                        rcode = RLM_MODULE_FAIL;
@@ -1097,7 +1086,7 @@ static unlang_action_t CC_HINT(nonnull) mod_method_store(rlm_rcode_t *p_result,
         *      setting the TTL, which precludes performing an
         *      insert.
         */
-       cache_insert(&rcode, inst, request, &handle, env->key, ttl);
+       cache_insert(&rcode, inst, request, &handle, env->key, env->maps, ttl);
        if (rcode == RLM_MODULE_OK) rcode = RLM_MODULE_UPDATED;
 
 finish:
@@ -1238,30 +1227,31 @@ static int mod_detach(module_detach_ctx_t const *mctx)
        return 0;
 }
 
-/** Create a new rlm_cache_instance
+/** Verify that a map in the cache section makes sense
  *
  */
-static int mod_instantiate(module_inst_ctx_t const *mctx)
+static int cache_verify(map_t *map, void *uctx)
 {
-       rlm_cache_t     *inst = talloc_get_type_abort(mctx->inst->data, rlm_cache_t);
-       CONF_SECTION    *conf = mctx->inst->conf;
-       CONF_SECTION    *update;
+       if (unlang_fixup_update(map, uctx) < 0) return -1;
 
-       if (!fr_time_delta_ispos(inst->config.ttl)) {
-               cf_log_err(conf, "Must set 'ttl' to non-zero");
+       if (!tmpl_is_attr(map->lhs)) {
+               cf_log_err(map->ci, "Destination must be an attribute ref or a list");
                return -1;
        }
 
-       if (inst->config.epoch != 0) {
-               cf_log_err(conf, "Must not set 'epoch' in the configuration files");
-               return -1;
-       }
+       return 0;
+}
 
-       update = cf_section_find(conf, "update", CF_IDENT_ANY);
-       if (!update) {
-               cf_log_err(conf, "Must have an 'update' section in order to cache anything");
-               return -1;
-       }
+static int update_section_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, fr_dict_t const *namespace, CONF_ITEM *ci, UNUSED call_env_parser_t const *rule)
+{
+       map_list_t              *maps;
+       CONF_SECTION            *update = cf_item_to_section(ci);
+       call_env_parsed_t       *call_env_parsed;
+
+       MEM(call_env_parsed = call_env_parsed_add(ctx, out,
+                                                 &(call_env_parser_t){ FR_CALL_ENV_PARSE_ONLY_OFFSET("update", FR_TYPE_VOID, 0, cache_call_env_t, maps)}));
+
+       MEM(maps = talloc_zero(call_env_parsed, map_list_t));
 
        /*
         *      Make sure the users don't screw up too badly.
@@ -1269,22 +1259,45 @@ static int mod_instantiate(module_inst_ctx_t const *mctx)
        {
                tmpl_rules_t    parse_rules = {
                        .attr = {
+                               .dict_def = namespace,
                                .list_def = request_attr_request,
                                .allow_wildcard = true,
                                .allow_foreign = true   /* Because we don't know where we'll be called */
                        }
                };
 
-               map_list_init(&inst->maps);
-               if (map_afrom_cs(inst, &inst->maps, update,
-                                &parse_rules, &parse_rules, cache_verify, inst, MAX_ATTRMAP) < 0) {
+               map_list_init(maps);
+               if (map_afrom_cs(ctx, maps, update,
+                                &parse_rules, &parse_rules, cache_verify, NULL, MAX_ATTRMAP) < 0) {
                        return -1;
                }
        }
 
-       if (map_list_empty(&inst->maps)) {
-               cf_log_err(conf, "Cache config must contain an update section, and "
-                             "that section must not be empty");
+       if (map_list_empty(maps)) {
+               cf_log_err(update, "Update section must not be empty");
+               return -1;
+       }
+
+       call_env_parsed_set_data(call_env_parsed, maps);
+
+       return 0;
+}
+
+/** Create a new rlm_cache_instance
+ *
+ */
+static int mod_instantiate(module_inst_ctx_t const *mctx)
+{
+       rlm_cache_t     *inst = talloc_get_type_abort(mctx->inst->data, rlm_cache_t);
+       CONF_SECTION    *conf = mctx->inst->conf;
+
+       if (!fr_time_delta_ispos(inst->config.ttl)) {
+               cf_log_err(conf, "Must set 'ttl' to non-zero");
+               return -1;
+       }
+
+       if (inst->config.epoch != 0) {
+               cf_log_err(conf, "Must not set 'epoch' in the configuration files");
                return -1;
        }
 
index 3802cbc59a04a0008389901c23d66fd101c71fce..4ca7ac216f8876e5c24e1ec23c25e97b3b6bb0d0 100644 (file)
@@ -67,9 +67,6 @@ typedef struct {
 
        module_instance_t       *driver_submodule;      //!< Driver's instance data.
        rlm_cache_driver_t const *driver;               //!< Driver's exported interface.
-
-       map_list_t              maps;                   //!< Attribute map applied to users.
-                                                       //!< and profiles.
 } rlm_cache_t;
 
 typedef struct {
index 1c929626c74de45f4cc859455625924bbc7a257a..166437cc2a7a80ad030c9f66f449b939ebca6f0d 100644 (file)
@@ -71,7 +71,7 @@ static const call_env_method_t chap_autz_method_env = { \
                                     CALL_ENV_FLAG_ATTRIBUTE | CALL_ENV_FLAG_REQUIRED | CALL_ENV_FLAG_NULLABLE | CALL_ENV_FLAG_CONCAT,
                                     chap_autz_call_env_t, chap_password),
                                     .pair.dflt = "&Chap-Password", .pair.dflt_quote = T_BARE_WORD },
-               { FR_CALL_ENV_TMPL_OFFSET("chap_challenge", FR_TYPE_OCTETS,
+               { FR_CALL_ENV_PARSE_OFFSET("chap_challenge", FR_TYPE_OCTETS,
                                          CALL_ENV_FLAG_ATTRIBUTE | CALL_ENV_FLAG_REQUIRED | CALL_ENV_FLAG_NULLABLE | CALL_ENV_FLAG_CONCAT,
                                          chap_autz_call_env_t, chap_challenge, chap_challenge_tmpl),
                                          .pair.dflt = "&Chap-Challenge", .pair.dflt_quote = T_BARE_WORD },
index 81c9fd92d78233c84ab248ace2469852055c3cd2..ef2c01130f4fc205bdaa1f047ad669c6502f867f 100644 (file)
@@ -72,7 +72,7 @@ typedef struct {
 static const call_env_method_t exec_method_env = {
        FR_CALL_ENV_METHOD_OUT(exec_call_env_t),
        .env = (call_env_parser_t[]){
-               { FR_CALL_ENV_TMPL_ONLY_OFFSET("program", FR_TYPE_STRING, CALL_ENV_FLAG_FORCE_QUOTE, exec_call_env_t, program), .pair.dflt_quote = T_BACK_QUOTED_STRING },
+               { FR_CALL_ENV_PARSE_ONLY_OFFSET("program", FR_TYPE_STRING, CALL_ENV_FLAG_FORCE_QUOTE, exec_call_env_t, program), .pair.dflt_quote = T_BACK_QUOTED_STRING },
                CALL_ENV_TERMINATOR
        }
 };
index 386f2d1f71defa3451dd49d991ededb453fdc633..42050b1c8e67239a17564fa12aaf8d8fc7027eae 100644 (file)
@@ -179,7 +179,7 @@ static const call_env_method_t authenticate_method_env = {
                { FR_CALL_ENV_SUBSECTION("user", NULL, CALL_ENV_FLAG_REQUIRED,
                                         ((call_env_parser_t[]) {
                                                USER_CALL_ENV_COMMON(ldap_auth_call_env_t),
-                                               { FR_CALL_ENV_TMPL_OFFSET("password_attribute", FR_TYPE_STRING,
+                                               { FR_CALL_ENV_PARSE_OFFSET("password_attribute", FR_TYPE_STRING,
                                                                          CALL_ENV_FLAG_ATTRIBUTE | CALL_ENV_FLAG_REQUIRED | CALL_ENV_FLAG_NULLABLE | CALL_ENV_FLAG_CONCAT,
                                                                          ldap_auth_call_env_t, password, password_tmpl),
                                                                          .pair.dflt = "&User-Password", .pair.dflt_quote = T_BARE_WORD },
index 9691ec7e9af8e572131da9246ea4c245b0bcfcce..bd8596148726203ada5df1abc461ba79b249cb01 100644 (file)
@@ -143,15 +143,15 @@ static const call_env_method_t mschap_ ## _x ## _method_env = { \
 }
 
 #define MSCHAP_COMMON_CALL_ENV(_x) \
-{ FR_CALL_ENV_TMPL_ONLY_OFFSET("chap_challenge", FR_TYPE_OCTETS, CALL_ENV_FLAG_ATTRIBUTE | CALL_ENV_FLAG_REQUIRED, mschap_ ## _x ## _call_env_t, chap_challenge), \
+{ FR_CALL_ENV_PARSE_ONLY_OFFSET("chap_challenge", FR_TYPE_OCTETS, CALL_ENV_FLAG_ATTRIBUTE | CALL_ENV_FLAG_REQUIRED, mschap_ ## _x ## _call_env_t, chap_challenge), \
                               .pair.dflt = "&Vendor-Specific.Microsoft.CHAP-Challenge", .pair.dflt_quote = T_BARE_WORD }, \
-{ FR_CALL_ENV_TMPL_ONLY_OFFSET("chap_response", FR_TYPE_OCTETS, CALL_ENV_FLAG_ATTRIBUTE | CALL_ENV_FLAG_REQUIRED, mschap_ ## _x ## _call_env_t, chap_response), \
+{ FR_CALL_ENV_PARSE_ONLY_OFFSET("chap_response", FR_TYPE_OCTETS, CALL_ENV_FLAG_ATTRIBUTE | CALL_ENV_FLAG_REQUIRED, mschap_ ## _x ## _call_env_t, chap_response), \
                               .pair.dflt = "&Vendor-Specific.Microsoft.CHAP-Response", .pair.dflt_quote = T_BARE_WORD }, \
-{ FR_CALL_ENV_TMPL_ONLY_OFFSET("chap2_response", FR_TYPE_OCTETS, CALL_ENV_FLAG_ATTRIBUTE | CALL_ENV_FLAG_REQUIRED, mschap_ ## _x ## _call_env_t, chap2_response), \
+{ FR_CALL_ENV_PARSE_ONLY_OFFSET("chap2_response", FR_TYPE_OCTETS, CALL_ENV_FLAG_ATTRIBUTE | CALL_ENV_FLAG_REQUIRED, mschap_ ## _x ## _call_env_t, chap2_response), \
                               .pair.dflt = "&Vendor-Specific.Microsoft.CHAP2-Response", .pair.dflt_quote = T_BARE_WORD }
 
 #define MSCHAP_OPT_CALL_ENV(_opt, _x) \
-{ FR_CALL_ENV_TMPL_ONLY_OFFSET(STRINGIFY(_opt), FR_TYPE_OCTETS, CALL_ENV_FLAG_ATTRIBUTE, mschap_ ## _x ## _call_env_t, _opt) }
+{ FR_CALL_ENV_PARSE_ONLY_OFFSET(STRINGIFY(_opt), FR_TYPE_OCTETS, CALL_ENV_FLAG_ATTRIBUTE, mschap_ ## _x ## _call_env_t, _opt) }
 
 typedef struct {
        tmpl_t const    *username;
@@ -161,7 +161,7 @@ typedef struct {
 } mschap_xlat_call_env_t;
 
 static const call_env_parser_t xlat_call_env[] = {
-       { FR_CALL_ENV_TMPL_ONLY_OFFSET("username", FR_TYPE_STRING, CALL_ENV_FLAG_ATTRIBUTE | CALL_ENV_FLAG_REQUIRED, mschap_xlat_call_env_t, username), .pair.dflt = "&User-Name", .pair.dflt_quote = T_BARE_WORD },
+       { FR_CALL_ENV_PARSE_ONLY_OFFSET("username", FR_TYPE_STRING, CALL_ENV_FLAG_ATTRIBUTE | CALL_ENV_FLAG_REQUIRED, mschap_xlat_call_env_t, username), .pair.dflt = "&User-Name", .pair.dflt_quote = T_BARE_WORD },
        MSCHAP_COMMON_CALL_ENV(xlat),
        CALL_ENV_TERMINATOR
 };
@@ -169,7 +169,7 @@ static const call_env_parser_t xlat_call_env[] = {
 MSCHAP_CALL_ENV(xlat);
 
 static const call_env_parser_t auth_call_env[] = {
-       { FR_CALL_ENV_TMPL_ONLY_OFFSET("username", FR_TYPE_STRING, CALL_ENV_FLAG_ATTRIBUTE | CALL_ENV_FLAG_REQUIRED, mschap_auth_call_env_t, username), .pair.dflt = "&User-Name", .pair.dflt_quote = T_BARE_WORD },
+       { FR_CALL_ENV_PARSE_ONLY_OFFSET("username", FR_TYPE_STRING, CALL_ENV_FLAG_ATTRIBUTE | CALL_ENV_FLAG_REQUIRED, mschap_auth_call_env_t, username), .pair.dflt = "&User-Name", .pair.dflt_quote = T_BARE_WORD },
        MSCHAP_COMMON_CALL_ENV(auth),
        MSCHAP_OPT_CALL_ENV(chap2_success, auth),
        MSCHAP_OPT_CALL_ENV(chap_mppe_keys, auth),
index 215d815d0073599aabb50f5ccabde9958141a821..827675a08bbe5611935d024f729b9f6de7b4ab1f 100644 (file)
@@ -91,7 +91,7 @@ static const call_env_method_t pap_method_env = {
        .inst_size = sizeof(pap_call_env_t),
        .inst_type = "pap_call_env_t",
        .env = (call_env_parser_t[]) {
-               { FR_CALL_ENV_TMPL_OFFSET("password_attribute", FR_TYPE_STRING,
+               { FR_CALL_ENV_PARSE_OFFSET("password_attribute", FR_TYPE_STRING,
                                          CALL_ENV_FLAG_ATTRIBUTE | CALL_ENV_FLAG_REQUIRED | CALL_ENV_FLAG_NULLABLE | CALL_ENV_FLAG_CONCAT,
                                          pap_call_env_t, password, password_tmpl), .pair.dflt = "&User-Password", .pair.dflt_quote = T_BARE_WORD },
                CALL_ENV_TERMINATOR
index ac864315a6eda02ba86767bcbaa2fa994a382966..8e499b09b5b913909c364060214da42199618706 100644 (file)
@@ -194,10 +194,10 @@ static const call_env_method_t redis_ippool_alloc_method_env = {
                { FR_CALL_ENV_OFFSET("lease_time", FR_TYPE_UINT32, CALL_ENV_FLAG_REQUIRED, redis_ippool_alloc_call_env_t, lease_time) },
                { FR_CALL_ENV_OFFSET("requested_address", FR_TYPE_STRING, CALL_ENV_FLAG_REQUIRED | CALL_ENV_FLAG_NULLABLE, redis_ippool_alloc_call_env_t, requested_address ),
                                     .pair.dflt = "%{%{Requested-IP-Address} || %{Net.Src.IP}}", .pair.dflt_quote = T_DOUBLE_QUOTED_STRING },
-               { FR_CALL_ENV_TMPL_ONLY_OFFSET("allocated_address_attr", FR_TYPE_VOID, CALL_ENV_FLAG_ATTRIBUTE | CALL_ENV_FLAG_REQUIRED, redis_ippool_alloc_call_env_t, allocated_address_attr) },
-               { FR_CALL_ENV_TMPL_ONLY_OFFSET("range_attr", FR_TYPE_VOID, CALL_ENV_FLAG_ATTRIBUTE | CALL_ENV_FLAG_REQUIRED, redis_ippool_alloc_call_env_t, range_attr),
+               { FR_CALL_ENV_PARSE_ONLY_OFFSET("allocated_address_attr", FR_TYPE_VOID, CALL_ENV_FLAG_ATTRIBUTE | CALL_ENV_FLAG_REQUIRED, redis_ippool_alloc_call_env_t, allocated_address_attr) },
+               { FR_CALL_ENV_PARSE_ONLY_OFFSET("range_attr", FR_TYPE_VOID, CALL_ENV_FLAG_ATTRIBUTE | CALL_ENV_FLAG_REQUIRED, redis_ippool_alloc_call_env_t, range_attr),
                                               .pair.dflt = "&reply.IP-Pool.Range", .pair.dflt_quote = T_BARE_WORD },
-               { FR_CALL_ENV_TMPL_ONLY_OFFSET("expiry_attr", FR_TYPE_VOID, CALL_ENV_FLAG_ATTRIBUTE, redis_ippool_alloc_call_env_t, expiry_attr) },
+               { FR_CALL_ENV_PARSE_ONLY_OFFSET("expiry_attr", FR_TYPE_VOID, CALL_ENV_FLAG_ATTRIBUTE, redis_ippool_alloc_call_env_t, expiry_attr) },
                CALL_ENV_TERMINATOR
        }
 };
@@ -212,10 +212,10 @@ static const call_env_method_t redis_ippool_update_method_env = {
                { FR_CALL_ENV_OFFSET("lease_time", FR_TYPE_UINT32, CALL_ENV_FLAG_REQUIRED,  redis_ippool_update_call_env_t, lease_time) },
                { FR_CALL_ENV_OFFSET("requested_address", FR_TYPE_STRING, CALL_ENV_FLAG_REQUIRED | CALL_ENV_FLAG_NULLABLE, redis_ippool_update_call_env_t, requested_address),
                                     .pair.dflt = "%{%{Requested-IP-Address} || %{Net.Src.IP}}", .pair.dflt_quote = T_DOUBLE_QUOTED_STRING },
-               { FR_CALL_ENV_TMPL_ONLY_OFFSET("allocated_address_attr", FR_TYPE_VOID, CALL_ENV_FLAG_ATTRIBUTE | CALL_ENV_FLAG_REQUIRED, redis_ippool_update_call_env_t, allocated_address_attr) },
-               { FR_CALL_ENV_TMPL_ONLY_OFFSET("range_attr", FR_TYPE_VOID, CALL_ENV_FLAG_ATTRIBUTE | CALL_ENV_FLAG_REQUIRED, redis_ippool_update_call_env_t, range_attr),
+               { FR_CALL_ENV_PARSE_ONLY_OFFSET("allocated_address_attr", FR_TYPE_VOID, CALL_ENV_FLAG_ATTRIBUTE | CALL_ENV_FLAG_REQUIRED, redis_ippool_update_call_env_t, allocated_address_attr) },
+               { FR_CALL_ENV_PARSE_ONLY_OFFSET("range_attr", FR_TYPE_VOID, CALL_ENV_FLAG_ATTRIBUTE | CALL_ENV_FLAG_REQUIRED, redis_ippool_update_call_env_t, range_attr),
                                               .pair.dflt = "&reply.IP-Pool.Range", .pair.dflt_quote = T_BARE_WORD },
-               { FR_CALL_ENV_TMPL_ONLY_OFFSET("expiry_attr", FR_TYPE_VOID, CALL_ENV_FLAG_ATTRIBUTE, redis_ippool_update_call_env_t, expiry_attr) },
+               { FR_CALL_ENV_PARSE_ONLY_OFFSET("expiry_attr", FR_TYPE_VOID, CALL_ENV_FLAG_ATTRIBUTE, redis_ippool_update_call_env_t, expiry_attr) },
                CALL_ENV_TERMINATOR
        }
 };
index 18b862d2327aebdbb89fa4b561154b0ca5f0a200..8fc2b74c23be1f6213900e302700e9cdd2ffc191 100644 (file)
@@ -1181,7 +1181,7 @@ static int mod_thread_detach(module_thread_inst_ctx_t const *mctx)
 static const call_env_method_t method_env = {
        FR_CALL_ENV_METHOD_OUT(rlm_smtp_env_t),
        .env = (call_env_parser_t[]) {
-               { FR_CALL_ENV_TMPL_OFFSET("username", FR_TYPE_STRING, CALL_ENV_FLAG_NULLABLE | CALL_ENV_FLAG_CONCAT, rlm_smtp_env_t, username, username_tmpl),
+               { FR_CALL_ENV_PARSE_OFFSET("username", FR_TYPE_STRING, CALL_ENV_FLAG_NULLABLE | CALL_ENV_FLAG_CONCAT, rlm_smtp_env_t, username, username_tmpl),
                                          .pair.dflt_quote = T_DOUBLE_QUOTED_STRING },
                { FR_CALL_ENV_OFFSET("password", FR_TYPE_STRING, CALL_ENV_FLAG_NULLABLE | CALL_ENV_FLAG_CONCAT, rlm_smtp_env_t, password), .pair.dflt_quote = T_DOUBLE_QUOTED_STRING },
                CALL_ENV_TERMINATOR