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