]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Add copy functions for tmpls and xlats
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Tue, 30 Nov 2021 19:00:55 +0000 (13:00 -0600)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Tue, 30 Nov 2021 19:06:32 +0000 (13:06 -0600)
src/lib/server/tmpl.h
src/lib/server/tmpl_tokenize.c
src/lib/unlang/xlat.h
src/lib/unlang/xlat_tokenize.c

index 7d77a6f1b9d438fc8bc6d1c1edc04f22a768e550..12f5bb9ebc93726ace301aa009422bb09d0dcc68 100644 (file)
@@ -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 */
index 004c586365102f60c0bed0df7e75e75f15ee20bb..433100e94b77db38c1ce595ff8b22ca07f9f5cfa 100644 (file)
@@ -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);
 
index fc77f4e56dead24b95aaf7ae3c89422a5a1616b3..aaca9d3addaa1d4b49efa33d986aa2c26fa975cb 100644 (file)
@@ -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
  */
index 2e4a11883d409ba5706d797a137d6dd8f386a7e1..7a21f8b4f8d237a83e09c77f075e80300632296b 100644 (file)
@@ -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;
+}