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.
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;
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;
}
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! */
* 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.
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;
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.
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;
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
* @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;
}
* 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;
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;
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.
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) {
///< 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)
#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
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) \
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) \
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.
*/
#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) \
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.
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), \
.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));
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 },
};
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
}
};
*/
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;
* 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);
}
}
-/** 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
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;
* 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:
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.
{
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;
}
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 {
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 },
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
}
};
{ 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 },
}
#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;
} 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
};
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),
.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
{ 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
}
};
{ 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
}
};
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