From: Arran Cudbard-Bell Date: Tue, 19 Mar 2019 00:37:03 +0000 (+0800) Subject: Add wrapper for pcre2_substitute X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0a2e48b71195098e4a0def7e9ae3d7dc7eb4282b;p=thirdparty%2Ffreeradius-server.git Add wrapper for pcre2_substitute --- diff --git a/src/lib/server/tmpl.h b/src/lib/server/tmpl.h index bde384f03f3..fe81787a45f 100644 --- a/src/lib/server/tmpl.h +++ b/src/lib/server/tmpl.h @@ -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 diff --git a/src/lib/util/regex.c b/src/lib/util/regex.c index e491936284f..9de4cafd301 100644 --- a/src/lib/util/regex.c +++ b/src/lib/util/regex.c @@ -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 diff --git a/src/lib/util/regex.h b/src/lib/util/regex.h index 349d5388d66..8e6aa6a9232 100644 --- a/src/lib/util/regex.h +++ b/src/lib/util/regex.h @@ -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