From: Arran Cudbard-Bell Date: Tue, 30 Nov 2021 19:00:55 +0000 (-0600) Subject: Add copy functions for tmpls and xlats X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=61b765a66e22446f6dc7c74bb22c405aa0cead86;p=thirdparty%2Ffreeradius-server.git Add copy functions for tmpls and xlats --- diff --git a/src/lib/server/tmpl.h b/src/lib/server/tmpl.h index 7d77a6f1b9d..12f5bb9ebc9 100644 --- a/src/lib/server/tmpl.h +++ b/src/lib/server/tmpl.h @@ -487,17 +487,29 @@ struct tmpl_s { */ fr_value_box_t literal; //!< Value data. - _CONST struct { - xlat_exp_t *ex; //!< pre-parsed xlat_exp_t - xlat_flags_t flags; //!< Flags controlling evaluation - ///< and expansion. - } xlat; + struct { + union { + _CONST struct { + xlat_exp_t *ex; //!< pre-parsed xlat_exp_t + xlat_flags_t flags; //!< Flags controlling evaluation + ///< and expansion. + } xlat; #ifdef HAVE_REGEX - _CONST struct { - regex_t *ex; //!< pre-parsed regex_t - fr_regex_flags_t flags; //!< Flags for regular expressions. - } reg; + _CONST struct { + char *src; //!< Original unescaped source string. + regex_t *ex; //!< pre-parsed regex_t + bool subcaptures; //!< Whether the regex was compiled with + ///< subcaptures. + } reg; #endif + }; + fr_regex_flags_t reg_flags; //!< Flags for regular expressions. + ///< Used by: + ///< - TMPL_TYPE_REGEX_XLAT + ///< - TMPL_TYPE_REGEX_UNCOMPILED + ///< - TMPL_TYPE_REGEX + ///< - TMPL_TYPE_REGEX_XLAT_UNRESOLVED + }; } data; fr_type_t _CONST cast; //!< Type we should interpret unresolved data with. @@ -698,7 +710,7 @@ static inline tmpl_pair_list_t tmpl_list(tmpl_t const *vpt) */ #ifdef HAVE_REGEX # define tmpl_regex(_tmpl) (_tmpl)->data.reg.ex //!< #TMPL_TYPE_REGEX only. -# define tmpl_regex_flags(_tmpl) (&(_tmpl)->data.reg.flags) +# define tmpl_regex_flags(_tmpl) (&(_tmpl)->data.reg_flags) #endif /** @} */ @@ -928,6 +940,8 @@ ssize_t tmpl_afrom_substr(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules); +tmpl_t *tmpl_copy(TALLOC_CTX *ctx, tmpl_t const *in); + ssize_t tmpl_cast_from_substr(fr_type_t *out, fr_sbuff_t *in); /* Parses cast string */ int tmpl_cast_set(tmpl_t *vpt, fr_type_t type) CC_HINT(nonnull); /* Sets cast type */ diff --git a/src/lib/server/tmpl_tokenize.c b/src/lib/server/tmpl_tokenize.c index 004c5863651..433100e94b7 100644 --- a/src/lib/server/tmpl_tokenize.c +++ b/src/lib/server/tmpl_tokenize.c @@ -2778,6 +2778,72 @@ ssize_t tmpl_afrom_substr(TALLOC_CTX *ctx, tmpl_t **out, return fr_sbuff_set(in, &our_in); } +/** Copy a tmpl + * + * Fully duplicates the contents of a tmpl including any nested attribute + * references. + * + * @param[in] ctx to perform allocations under. + * @param[in] in tmpl to duplicate. + * @return + * - NULL on error. + * - A new tmpl on success. + */ +tmpl_t *tmpl_copy(TALLOC_CTX *ctx, tmpl_t const *in) +{ + tmpl_t *vpt; + + MEM(vpt = tmpl_alloc(ctx, in->type, in->quote, in->name, in->len)); + vpt->cast = in->cast; + vpt->enumv = in->enumv; + vpt->rules = in->rules; + + /* + * Copy over the unescaped data + */ + if (tmpl_is_unresolved(vpt) || tmpl_is_regex_uncompiled(vpt)) { + if (unlikely(!(vpt->data.unescaped = talloc_bstrdup(vpt, in->data.unescaped)))) { + error: + talloc_free(vpt); + return NULL; + } + } + + /* + * Copy attribute references + */ + if (tmpl_contains_attr(vpt) && unlikely(tmpl_attr_copy(vpt, in) < 0)) goto error; + + /* + * Copy flags for all regex flavours (and possibly recompile the regex) + */ + if (tmpl_contains_regex(vpt)) { + vpt->data.reg_flags = in->data.reg_flags; + + /* + * If the tmpl contains a _compiled_ regex + * then convert it back to an uncompiled + * regex and recompile. + * + * Most of the regex libraries don't allow + * copying compiled expressions. + */ + if (tmpl_is_regex(vpt)) { + vpt->type = TMPL_TYPE_REGEX_UNCOMPILED; + if (unlikely(!(vpt->data.unescaped = talloc_bstrdup(vpt, in->data.reg.src)))) goto error; + if (unlikely(tmpl_regex_compile(vpt, vpt->data.reg.subcaptures) < 0)) goto error; + return vpt; + } + } + + /* + * Copy the xlat component + */ + if (tmpl_contains_xlat(vpt) && unlikely(xlat_copy(vpt, &vpt->data.xlat.ex, in->data.xlat.ex) < 0)) goto error; + + return vpt; +} + /** Parse a cast specifier * * @param[out] out Where to write the cast type. @@ -2925,7 +2991,7 @@ ssize_t tmpl_regex_flags_substr(tmpl_t *vpt, fr_sbuff_t *in, fr_sbuff_term_t con fr_assert(tmpl_is_regex_uncompiled(vpt) || tmpl_is_regex_xlat(vpt) || tmpl_is_regex_xlat_unresolved(vpt)); - slen = regex_flags_parse(&err, &vpt->data.reg.flags, in, terminals, true); + slen = regex_flags_parse(&err, &vpt->data.reg_flags, in, terminals, true); switch (err) { case 0: break; @@ -3612,11 +3678,12 @@ ssize_t tmpl_regex_compile(tmpl_t *vpt, bool subcaptures) slen = regex_compile(vpt, &vpt->data.reg.ex, unescaped, talloc_array_length(unescaped) - 1, - &vpt->data.reg.flags, subcaptures, vpt->rules.at_runtime); + &vpt->data.reg_flags, subcaptures, vpt->rules.at_runtime); if (slen <= 0) return vpt->quote != T_BARE_WORD ? slen - 1 : slen; /* Account for the quoting */ - talloc_free(unescaped); vpt->type = TMPL_TYPE_REGEX; + vpt->data.reg.src = unescaped; /* Keep this around for debugging and copying */ + vpt->data.reg.subcaptures = subcaptures; TMPL_VERIFY(vpt); diff --git a/src/lib/unlang/xlat.h b/src/lib/unlang/xlat.h index fc77f4e56de..aaca9d3adda 100644 --- a/src/lib/unlang/xlat.h +++ b/src/lib/unlang/xlat.h @@ -431,6 +431,8 @@ tmpl_t *xlat_to_tmpl_attr(TALLOC_CTX *ctx, xlat_exp_t *xlat); int xlat_from_tmpl_attr(TALLOC_CTX *ctx, xlat_exp_t **head, xlat_flags_t *flags, tmpl_t **vpt_p); +int xlat_copy(TALLOC_CTX *ctx, xlat_exp_t **out, xlat_exp_t *in); + /* * xlat_inst.c */ diff --git a/src/lib/unlang/xlat_tokenize.c b/src/lib/unlang/xlat_tokenize.c index 2e4a11883d4..7a21f8b4f8d 100644 --- a/src/lib/unlang/xlat_tokenize.c +++ b/src/lib/unlang/xlat_tokenize.c @@ -1875,3 +1875,92 @@ int xlat_from_tmpl_attr(TALLOC_CTX *ctx, xlat_exp_t **head, xlat_flags_t *flags, return 0; } + +/** Copy all nodes in the input list to the output list + * + * @param[in] ctx to allocate new nodes in. + * @param[out] out Where to write new nodes. + * @param[in] in Input nodes. + * @return + * - 0 on success. + * - -1 on failure. + */ +int xlat_copy(TALLOC_CTX *ctx, xlat_exp_t **out, xlat_exp_t *in) +{ + xlat_exp_t *p; + fr_cursor_t out_cursor, in_cursor; + + if (!in) { + *out = NULL; + return 0; + } + + fr_cursor_talloc_init(&out_cursor, out, xlat_exp_t); + fr_cursor_talloc_init(&in_cursor, &in, xlat_exp_t); + + /* + * Copy everything in the list of nodes + */ + while ((p = fr_cursor_next(&in_cursor))) { + xlat_exp_t *node; + + MEM(node = xlat_exp_alloc(ctx, p->type, p->fmt, talloc_array_length(p->fmt) - 1)); + node->quote = p->quote; + node->flags = p->flags; + + switch (p->type) { + case XLAT_INVALID: + fr_strerror_printf("Cannot copy xlat node of type \"invalid\""); + error: + fr_cursor_head(&out_cursor); + fr_cursor_free_list(&out_cursor); + return -1; + + case XLAT_BOX: + if (unlikely(fr_value_box_copy(node, &node->data, &p->data) < 0)) goto error; + continue; + + case XLAT_ONE_LETTER: /* Done with format */ + case XLAT_FUNC_UNRESOLVED: + case XLAT_VIRTUAL_UNRESOLVED: + continue; + + case XLAT_FUNC: + case XLAT_VIRTUAL: + /* + * Only copy the function pointer, and whether this + * is ephemeral. + * + * All instance data is specific to the xlat node and + * cannot be duplicated. + * + * The node xlat nodes will need to be registered in + * the xlat instantiation table later. + */ + node->call.func = p->call.func; + node->call.ephemeral = p->call.ephemeral; + if (unlikely(xlat_copy(node, &node->child, p->child) < 0)) goto error; + continue; + + case XLAT_ATTRIBUTE: + node->attr = tmpl_copy(node, p->attr); + continue; + +#ifdef HAVE_REGEX + case XLAT_REGEX: + node->regex_index = p->regex_index; + continue; +#endif + + case XLAT_ALTERNATE: + if (unlikely(xlat_copy(node, &node->alternate, p->alternate) < 0)) goto error; + continue; + + case XLAT_GROUP: + if (unlikely(xlat_copy(node, &node->child, p->child) < 0)) goto error; + continue; + } + } + + return 0; +}