]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Add wrapper for pcre2_substitute
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Tue, 19 Mar 2019 00:37:03 +0000 (08:37 +0800)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Tue, 19 Mar 2019 02:21:10 +0000 (10:21 +0800)
src/lib/server/tmpl.h
src/lib/util/regex.c
src/lib/util/regex.h

index bde384f03f3a2334be6857bd6ea12396a525e56c..fe81787a45fabff0c8ad9eaf6b48de962a2f7222 100644 (file)
@@ -187,11 +187,11 @@ struct vp_tmpl_s {
                 */
                fr_value_box_t  literal;                         //!< Value data.
 
-               xlat_exp_t      *xlat;   //!< pre-parsed xlat_exp_t
+               xlat_exp_t      *xlat;                          //!< pre-parsed xlat_exp_t
 
 #ifdef HAVE_REGEX
                struct {
-                       regex_t         *preg;          //!< pre-parsed regex_t
+                       regex_t                 *preg;          //!< pre-parsed regex_t
                        fr_regex_flags_t        regex_flags;    //!< Flags for regular expressions.
                };
 #endif
index e491936284f6bb3836c54eeb95f63a1a11a18346..9de4cafd301575179743f47b72421637d6d88a3c 100644 (file)
@@ -302,6 +302,10 @@ int regex_exec(regex_t *preg, char const *subject, size_t len, fr_regmatch_t *re
                 *      in the regmatch structure.
                 */
                subject = our_subject = talloc_bstrndup(regmatch, subject, len);
+               if (!subject) {
+                       fr_strerror_printf("Out of memory");
+                       return -1;
+               }
 #else
                /*
                 *      If PCRE2_COPY_MATCHED_SUBJECT is available
@@ -333,7 +337,10 @@ int regex_exec(regex_t *preg, char const *subject, size_t len, fr_regmatch_t *re
                talloc_free(our_subject);
 #endif
 
-               if (ret == PCRE2_ERROR_NOMATCH) return 0;
+               if (ret == PCRE2_ERROR_NOMATCH) {
+                       if (regmatch) regmatch->used = 0;
+                       return 0;
+               }
 
                pcre2_get_error_message(ret, errbuff, sizeof(errbuff));
                fr_strerror_printf("regex evaluation failed with code (%i): %s", ret, errbuff);
@@ -346,6 +353,158 @@ int regex_exec(regex_t *preg, char const *subject, size_t len, fr_regmatch_t *re
        return 1;
 }
 
+/** Wrapper around pcre2_substitute
+ *
+ * @param[in] ctx              to allocate output string in.
+ * @param[out] out             Output string with replacements performed.
+ * @param[in] max_out          Maximum length of output buffer.  If this is 0 then
+ *                             the output length is unlimited.
+ * @param[in] preg             The compiled expression.
+ * @param[in] flags            that affect matching.
+ * @param[in] subject          to perform replacements on.
+ * @param[in] subject_len      the length of the subject.
+ * @param[in] replacement      replacement string containing substitution
+ *                             markers.
+ * @param[in] replacement_len  Length of the replacement string.
+ * @param[in] regmatch         Array of match pointers.
+ * @return
+ *     - >= 0 the length of the output string.
+ *     - < 0 on error.
+ */
+int regex_substitute(TALLOC_CTX *ctx, char **out, size_t max_out, regex_t *preg, fr_regex_flags_t *flags,
+                    char const *subject, size_t subject_len,
+                    char const *replacement, size_t replacement_len,
+                    fr_regmatch_t *regmatch)
+{
+       int                     ret;
+       uint32_t                options = 0;
+       size_t                  buff_len, actual_len;
+       char                    *buff;
+
+#ifndef PCRE2_COPY_MATCHED_SUBJECT
+       char                    *our_subject = NULL;
+#endif
+
+       /*
+        *      Thread local initialisation
+        */
+       if (!fr_pcre2_tls && (fr_pcre2_tls_init() < 0)) return -1;
+
+       /*
+        *      Internally pcre2_substitute just calls pcre2_match to
+        *      generate the match data, so the same hack as the
+        *      regex_exec function above is required.
+        */
+       if (regmatch) {
+#ifndef PCRE2_COPY_MATCHED_SUBJECT
+               /*
+                *      We have to dup and operate on the duplicate
+                *      of the subject, because pcre2_jit_match and
+                *      pcre2_match store a pointer to the subject
+                *      in the regmatch structure.
+                */
+               subject = our_subject = talloc_bstrndup(regmatch, subject, subject_len);
+               if (!subject) {
+                       fr_strerror_printf("Out of memory");
+                       return -1;
+               }
+#else
+               /*
+                *      If PCRE2_COPY_MATCHED_SUBJECT is available
+                *      and set as an options flag, pcre2_match will
+                *      strdup the subject string if pcre2_match is
+                *      successful and store a pointer to it in the
+                *      regmatch struct.
+                *
+                *      The lifetime of the string memory will be
+                *      bound to the regmatch struct.  This is more
+                *      efficient that doing it ourselves, as the
+                *      strdup only occurs if the subject matches.
+                */
+               options |= PCRE2_COPY_MATCHED_SUBJECT;
+#endif
+       }
+
+       /*
+        *      Guess (badly) what the length of the output buffer should be
+        */
+       actual_len = buff_len = subject_len + 1;
+       buff = talloc_array(ctx, char, buff_len);
+       if (!buff) {
+#ifndef PCRE2_COPY_MATCHED_SUBJECT
+               talloc_free(our_subject);
+#endif
+               fr_strerror_printf("Out of memory");
+               return -1;
+       }
+
+       options |= PCRE2_SUBSTITUTE_OVERFLOW_LENGTH;
+       if (flags->global) options |= PCRE2_SUBSTITUTE_GLOBAL;
+
+again:
+       ret = pcre2_substitute(preg->compiled,
+                              (PCRE2_SPTR8)subject, (PCRE2_SIZE)subject_len, 0,
+                              options, regmatch ? regmatch->match_data : NULL, fr_pcre2_tls->mcontext,
+                              (PCRE2_UCHAR const *)replacement, replacement_len, (PCRE2_UCHAR *)buff, &actual_len);
+
+       if (ret < 0) {
+               PCRE2_UCHAR errbuff[128];
+
+#ifndef PCRE2_COPY_MATCHED_SUBJECT
+               talloc_free(our_subject);
+#endif
+               talloc_free(buff);
+
+               if (ret == PCRE2_ERROR_NOMEMORY) {
+                       if ((max_out > 0) && (actual_len > max_out)) {
+                               fr_strerror_printf("String length with substitutions (%zu) "
+                                                   "exceeds max string length (%zu)", actual_len - 1, max_out - 1);
+                               return -1;
+                       }
+
+                       /*
+                        *      Check that actual_len != buff_len as that'd be
+                        *      an actual error.
+                        */
+                       if (actual_len == buff_len) {
+                               fr_strerror_printf("libpcre2 out of memory");
+                               return -1;
+                       }
+
+                       talloc_free(buff);
+                       buff_len = actual_len;
+                       buff = talloc_array(ctx, char, buff_len);
+                       goto again;
+               }
+
+               if (ret == PCRE2_ERROR_NOMATCH) {
+                       if (regmatch) regmatch->used = 0;
+                       return 0;
+               }
+
+               pcre2_get_error_message(ret, errbuff, sizeof(errbuff));
+               fr_strerror_printf("regex evaluation failed with code (%i): %s", ret, errbuff);
+               return -1;
+       }
+
+       /*
+        *      Trim the replacement buffer to the correct length
+        */
+       if (actual_len < buff_len) {
+               buff = talloc_realloc_bstr(buff, actual_len - 1);       /* actual_len has space for '\0' */
+               if (!buff) {
+                       fr_strerror_printf("reallocing pcre2_substitute result buffer failed");
+                       return -1;
+               }
+       }
+
+       if (regmatch) regmatch->used = ret;
+       *out = buff;
+
+       return 1;
+}
+
+
 /** Returns the number of subcapture groups
  *
  * @return
index 349d5388d666f7a888e90735c9e3db96d0ca26f7..8e6aa6a923241e67b157f8a4eb1fad7775eaa556 100644 (file)
@@ -167,6 +167,12 @@ size_t             regex_flags_snprint(char *out, size_t outlen, fr_regex_flags_t const *fl
 ssize_t                regex_compile(TALLOC_CTX *ctx, regex_t **out, char const *pattern, size_t len,
                              fr_regex_flags_t const *flags, bool subcaptures, bool runtime);
 int            regex_exec(regex_t *preg, char const *subject, size_t len, fr_regmatch_t *regmatch);
+#ifdef HAVE_REGEX_PCRE2
+int            regex_substitute(TALLOC_CTX *ctx, char **out, size_t max_out, regex_t *preg, fr_regex_flags_t *flags,
+                                char const *subject, size_t subject_len,
+                                char const *replacement, size_t replacement_len,
+                                fr_regmatch_t *regmatch);
+#endif
 uint32_t       regex_subcapture_count(regex_t const *preg);
 fr_regmatch_t  *regex_match_data_alloc(TALLOC_CTX *ctx, uint32_t count);
 #  ifdef __cplusplus