smart_try_dir=$pcre_include_dir
+ac_safe=`echo "pcre2.h" | sed 'y%./+-%__pm%'`
+old_CPPFLAGS="$CPPFLAGS"
+smart_include=
+smart_include_dir="/usr/local/include /opt/include"
+
+_smart_try_dir=
+_smart_include_dir=
+
+for _prefix in $smart_prefix ""; do
+ for _dir in $smart_try_dir; do
+ _smart_try_dir="${_smart_try_dir} ${_dir}/${_prefix}"
+ done
+
+ for _dir in $smart_include_dir; do
+ _smart_include_dir="${_smart_include_dir} ${_dir}/${_prefix}"
+ done
+done
+
+if test "x$_smart_try_dir" != "x"; then
+ for try in $_smart_try_dir; do
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for pcre2.h in $try" >&5
+printf %s "checking for pcre2.h in $try... " >&6; }
+ CPPFLAGS="-isystem $try $old_CPPFLAGS"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#define PCRE2_CODE_UNIT_WIDTH (8)
+ #include <pcre2.h>
+int
+main (void)
+{
+int a = 1;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+
+ smart_include="-isystem $try"
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+ break
+
+else $as_nop
+
+ smart_include=
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+ done
+ CPPFLAGS="$old_CPPFLAGS"
+fi
+
+if test "x$smart_include" = "x"; then
+ for _prefix in $smart_prefix; do
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ${_prefix}/pcre2.h" >&5
+printf %s "checking for ${_prefix}/pcre2.h... " >&6; }
+
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#define PCRE2_CODE_UNIT_WIDTH (8)
+ #include <pcre2.h>
+int
+main (void)
+{
+int a = 1;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+
+ smart_include="-isystem ${_prefix}/"
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+ break
+
+else $as_nop
+
+ smart_include=
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+ done
+fi
+
+if test "x$smart_include" = "x"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for pcre2.h" >&5
+printf %s "checking for pcre2.h... " >&6; }
+
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#define PCRE2_CODE_UNIT_WIDTH (8)
+ #include <pcre2.h>
+int
+main (void)
+{
+int a = 1;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+
+ smart_include=" "
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+ break
+
+else $as_nop
+
+ smart_include=
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+if test "x$smart_include" = "x"; then
+
+ for try in $_smart_include_dir; do
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for pcre2.h in $try" >&5
+printf %s "checking for pcre2.h in $try... " >&6; }
+ CPPFLAGS="-isystem $try $old_CPPFLAGS"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#define PCRE2_CODE_UNIT_WIDTH (8)
+ #include <pcre2.h>
+int
+main (void)
+{
+int a = 1;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+
+ smart_include="-isystem $try"
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+ break
+
+else $as_nop
+
+ smart_include=
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+ done
+ CPPFLAGS="$old_CPPFLAGS"
+fi
+
+if test "x$smart_include" != "x"; then
+ eval "ac_cv_header_$ac_safe=yes"
+ CPPFLAGS="$smart_include $old_CPPFLAGS"
+ SMART_CPPFLAGS="$smart_include $SMART_CPPFLAGS"
+fi
+
+smart_prefix=
+
+ if test "x$ac_cv_header_pcre2_h" = "xyes"; then
+ smart_try_dir=$pcre_lib_dir
+
+
+sm_lib_safe=`echo "pcre2-8" | sed 'y%./+-%__p_%'`
+sm_func_safe=`echo "pcre2_compile_8" | sed 'y%./+-%__p_%'`
+
+old_LIBS="$LIBS"
+old_CPPFLAGS="$CPPFLAGS"
+smart_lib=
+smart_ldflags=
+smart_lib_dir=
+
+if test "x$smart_try_dir" != "x"; then
+ for try in $smart_try_dir; do
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for pcre2_compile_8 in -lpcre2-8 in $try" >&5
+printf %s "checking for pcre2_compile_8 in -lpcre2-8 in $try... " >&6; }
+ LIBS="-lpcre2-8 $old_LIBS"
+ CPPFLAGS="-L$try -Wl,-rpath,$try $old_CPPFLAGS"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+extern char pcre2_compile_8();
+int
+main (void)
+{
+pcre2_compile_8()
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+
+ smart_lib="-lpcre2-8"
+ smart_ldflags="-L$try -Wl,-rpath,$try"
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+ break
+
+else $as_nop
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+ conftest$ac_exeext conftest.$ac_ext
+ done
+ LIBS="$old_LIBS"
+ CPPFLAGS="$old_CPPFLAGS"
+fi
+
+if test "x$smart_lib" = "x"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for pcre2_compile_8 in -lpcre2-8" >&5
+printf %s "checking for pcre2_compile_8 in -lpcre2-8... " >&6; }
+ LIBS="-lpcre2-8 $old_LIBS"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+extern char pcre2_compile_8();
+int
+main (void)
+{
+pcre2_compile_8()
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+
+ smart_lib="-lpcre2-8"
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+
+else $as_nop
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+ conftest$ac_exeext conftest.$ac_ext
+ LIBS="$old_LIBS"
+fi
+
+if test "x$smart_lib" = "x"; then
+ for try in /usr/local/lib /opt/lib; do
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for pcre2_compile_8 in -lpcre2-8 in $try" >&5
+printf %s "checking for pcre2_compile_8 in -lpcre2-8 in $try... " >&6; }
+ LIBS="-lpcre2-8 $old_LIBS"
+ CPPFLAGS="-L$try -Wl,-rpath,$try $old_CPPFLAGS"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+extern char pcre2_compile_8();
+int
+main (void)
+{
+pcre2_compile_8()
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+
+ smart_lib="-lpcre2-8"
+ smart_ldflags="-L$try -Wl,-rpath,$try"
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+ break
+
+else $as_nop
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+ conftest$ac_exeext conftest.$ac_ext
+ done
+ LIBS="$old_LIBS"
+ CPPFLAGS="$old_CPPFLAGS"
+fi
+
+if test "x$smart_lib" != "x"; then
+ eval "ac_cv_lib_${sm_lib_safe}_${sm_func_safe}=yes"
+ LIBS="$smart_ldflags $smart_lib $old_LIBS"
+ SMART_LIBS="$smart_ldflags $smart_lib $SMART_LIBS"
+fi
+
+ if test "x$ac_cv_lib_pcre2_8_pcre2_compile_8" = "xyes"; then
+ REGEX=yes
+
+printf "%s\n" "#define HAVE_PCRE2 1" >>confdefs.h
+
+
+printf "%s\n" "#define HAVE_BINSAFE_REGEX 1" >>confdefs.h
+
+ fi
+ fi
+fi
+
+if test "x$REGEX" = "x" && test "x$PCRE" != "xno"; then
+ smart_try_dir=$pcre_include_dir
+
+
ac_safe=`echo "pcre.h" | sed 'y%./+-%__pm%'`
old_CPPFLAGS="$CPPFLAGS"
smart_include=
)
dnl #
-dnl # First look for PCRE
+dnl # First look for PCRE2
dnl #
if test "x$REGEX" != "xno" && test "x$PCRE" != "xno"; then
+ smart_try_dir=$pcre_include_dir
+ FR_SMART_CHECK_INCLUDE(pcre2.h,[#define PCRE2_CODE_UNIT_WIDTH (8)])
+ if test "x$ac_cv_header_pcre2_h" = "xyes"; then
+ smart_try_dir=$pcre_lib_dir
+ FR_SMART_CHECK_LIB(pcre2-8, pcre2_compile_8)
+ if test "x$ac_cv_lib_pcre2_8_pcre2_compile_8" = "xyes"; then
+ REGEX=yes
+ AC_DEFINE(HAVE_PCRE2, [1], [define this if we have libpcre2])
+ AC_DEFINE(HAVE_BINSAFE_REGEX, 1, [Define if we have a binary safe regular expression library])
+ fi
+ fi
+fi
+
+dnl #
+dnl # If no PCRE2, look for PCRE
+dnl #
+if test "x$REGEX" = "x" && test "x$PCRE" != "xno"; then
smart_try_dir=$pcre_include_dir
FR_SMART_CHECK_INCLUDE(pcre.h)
if test "x$ac_cv_header_pcre_h" = "xyes"; then
fi
dnl #
-dnl # If no PCRE, fallback to POSIX regular expressions
+dnl # If no PCRE2 or PCRE, fallback to POSIX regular expressions
dnl #
if test "x$REGEX" = "x"; then
smart_try_dir=
/* define this if we have libpcre */
#undef HAVE_PCRE
+/* define this if we have libpcre2 */
+#undef HAVE_PCRE2
+
/* Define to 1 if you have the <prot.h> header file. */
#undef HAVE_PROT_H
/*
* Named capture groups only supported by PCRE.
*/
-# ifdef HAVE_PCRE
+# if defined(HAVE_PCRE) || defined(HAVE_PCRE2)
int regex_request_to_sub_named(TALLOC_CTX *ctx, char **out, REQUEST *request, char const *name);
# endif
#endif
# ifdef __cplusplus
extern "C" {
# endif
-# ifdef HAVE_PCRE
-# include <pcre.h>
+# ifdef HAVE_PCRE2
+# define PCRE2_CODE_UNIT_WIDTH 8
+# include <pcre2.h>
+int fr_pcre2_gcontext_setup(void);
+void fr_pcre2_gcontext_free(void);
+
+typedef struct regmatch {
+ pcre2_match_data *match_data; //!< Match data containing the subject
+ ///< and various match offsets.
+#ifndef NDEBUG
+ char const *subject; //!< Here for debugging purposes if we explicitly duped the string.
+#endif
+} regmatch_t;
+
+typedef struct regex {
+ pcre2_code_8 *compiled; //!< Compiled regular expression.
+ uint32_t subcaptures; //!< Number of subcaptures contained within the expression.
+ bool precompiled; //!< Whether this regex was precompiled, or compiled for one of evaluation.
+ bool jitd; //!< Whether JIT data is available.
+} regex_t;
+
+regmatch_t *regex_match_data_alloc(TALLOC_CTX *ctx, uint32_t count);
+
+# elif defined (HAVE_PCRE)
+# include <pcre.h>
/*
* Versions older then 8.20 didn't have the JIT functionality
* gracefully degrade.
#include <freeradius-devel/libradius.h>
#include <freeradius-devel/regex.h>
+/*
+ * Wrapper functions for libpcre2. Much more powerful, and guaranteed
+ * to be binary safe for both patterns and subjects but require
+ * libpcre2.
+ */
+# ifdef HAVE_PCRE2
+/** Free regex_t structure
+ *
+ * Calls libpcre specific free functions for the expression and study.
+ *
+ * @param preg to free.
+ */
+static int _regex_free(regex_t *preg)
+{
+ if (preg->compiled) {
+ pcre2_code_free(preg->compiled);
+ preg->compiled = NULL;
+ }
+
+ return 0;
+}
+
+/** Talloc wrapper for pcre2 memory allocation
+ *
+ * @param[in] to_alloc How many bytes to alloc.
+ * @param[in] uctx UNUSED.
+ */
+static void *_pcre2_talloc(PCRE2_SIZE to_alloc, UNUSED void *uctx)
+{
+ return talloc_array(NULL, uint8_t, to_alloc);
+}
+
+/** Talloc wrapper for pcre2 memory freeing
+ *
+ * @param[in] to_free Memory to free.
+ * @param[in] uctx UNUSED.
+ */
+static void _pcre2_talloc_free(void *to_free, UNUSED void *uctx)
+{
+ talloc_free(to_free);
+}
+
+/*
+ * A PCRE2 general context used to point to our custom alloc / free functions
+ */
+static pcre2_general_context *_pcre2_gcontext = NULL;
+
+int fr_pcre2_gcontext_setup(void)
+{
+ _pcre2_gcontext = pcre2_general_context_create(_pcre2_talloc, _pcre2_talloc_free, NULL);
+ if (!_pcre2_gcontext) return -1;
+ return 1;
+}
+
+void fr_pcre2_gcontext_free(void)
+{
+ pcre2_general_context_free(_pcre2_gcontext);
+}
+
+/** Wrapper around pcre_compile
+ *
+ * Allows the rest of the code to do compilations using one function signature.
+ *
+ * @note Compiled expression must be freed with talloc_free.
+ *
+ * @param out Where to write out a pointer to the structure containing the compiled expression.
+ * @param pattern to compile.
+ * @param len of pattern.
+ * @param ignore_case whether to do case insensitive matching.
+ * @param multiline If true $ matches newlines.
+ * @param subcaptures Whether to compile the regular expression to store subcapture
+ * data.
+ * @param runtime If false run the pattern through the PCRE JIT to convert it to machine code.
+ * This trades startup time (longer) for runtime performance (better).
+ * @return >= 1 on success, <= 0 on error. Negative value is offset of parse error.
+ */
+ssize_t regex_compile(TALLOC_CTX *ctx, regex_t **out, char const *pattern, size_t len,
+ bool ignore_case, bool multiline, bool subcaptures, bool runtime)
+{
+ int ret;
+ PCRE2_SIZE offset;
+ int cflags = 0;
+ regex_t *preg;
+#ifdef PCRE2_CONFIG_JIT
+ static bool setup = false;
+ static bool do_jit = false;
+
+ if (!setup) {
+ pcre2_config(PCRE2_CONFIG_JIT, &do_jit);
+ setup = true;
+ }
+#endif
+
+ *out = NULL;
+
+ if (len == 0) {
+ fr_strerror_printf("Empty expression");
+ return 0;
+ }
+
+ if (ignore_case) cflags |= PCRE2_CASELESS;
+ if (multiline) cflags |= PCRE2_MULTILINE;
+ if (!subcaptures) cflags |= PCRE2_NO_AUTO_CAPTURE;
+
+ preg = talloc_zero(ctx, regex_t);
+ talloc_set_destructor(preg, _regex_free);
+
+ preg->compiled = pcre2_compile((PCRE2_SPTR8)pattern, len, cflags, &ret, &offset, NULL);
+ if (!preg->compiled) {
+ PCRE2_UCHAR errbuff[128];
+
+ pcre2_get_error_message(ret, errbuff, sizeof(errbuff));
+ fr_strerror_printf("%s", (char *)errbuff);
+ talloc_free(preg);
+
+ return -(ssize_t)offset;
+ }
+ if (!runtime) {
+ preg->precompiled = true;
+
+#ifdef PCRE2_CONFIG_JIT
+ /*
+ * This is expensive, so only do it for
+ * expressions that are going to be
+ * evaluated repeatedly.
+ */
+ if (do_jit) {
+ ret = pcre2_jit_compile(preg->compiled, PCRE2_JIT_COMPLETE);
+ if (ret < 0) {
+ PCRE2_UCHAR errbuff[128];
+
+ pcre2_get_error_message(ret, errbuff, sizeof(errbuff));
+ fr_strerror_printf("Pattern JIT failed: %s", (char *)errbuff);
+ talloc_free(preg);
+
+ return 0;
+ }
+ preg->jitd = true;
+ }
+#endif
+ }
+
+ *out = preg;
+
+ return len;
+}
+
+/** Wrapper around pcre2_match
+ *
+ * @param preg The compiled expression.
+ * @param subject to match.
+ * @param len Length of subject.
+ * @param pmatch Array of match pointers.
+ * @param nmatch How big the match array is. Updated to number of matches.
+ * @return -1 on error, 0 on no match, 1 on match.
+ */
+int regex_exec(regex_t *preg, char const *subject, size_t len, regmatch_t *pmatch, size_t *nmatch)
+{
+ int ret;
+ uint32_t options = 0;
+ bool dup_subject = true;
+ char *our_subject = NULL;
+ pcre2_match_data *match_data;
+
+ /*
+ * PCRE_NO_AUTO_CAPTURE is a compile time only flag,
+ * and can't be passed here.
+ * We rely on the fact that matches has been set to
+ * 0 as a hint that no subcapture data should be
+ * generated.
+ */
+ if (!pmatch || !nmatch) {
+ pmatch = NULL;
+ if (nmatch) *nmatch = 0;
+ }
+
+ if (pmatch) {
+#ifdef PCRE2_COPY_MATCHED_SUBJECT
+ /*
+ * This is apparently only supported for pcre2_match
+ * NOT pcre2_jit_match.
+ */
+# ifdef PCRE2_CONFIG_JIT
+ if (!preg->jitd) {
+# endif
+ dup_subject = false;
+
+ /*
+ * 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;
+# ifdef PCRE2_CONFIG_JIT
+ }
+# endif
+#endif
+ if (dup_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(pmatch, subject, len);
+ if (!subject) {
+ fr_strerror_printf("Out of memory");
+ return -1;
+ }
+#ifndef NDEBUG
+ pmatch->subject = subject; /* Stored only for tracking memory issues */
+#endif
+ }
+ }
+
+ /*
+ * If we weren't given match data we need to alloc it else pcre2_match
+ * fails when passed NULL match data.
+ */
+ if (!pmatch) {
+ match_data = pcre2_match_data_create_from_pattern(preg->compiled, _pcre2_gcontext);
+ if (!match_data) {
+ fr_strerror_printf("Failed allocating temporary match data");
+ return -1;
+ }
+ } else {
+ match_data = pmatch->match_data;
+ }
+
+#ifdef PCRE2_CONFIG_JIT
+ if (preg->jitd) {
+ ret = pcre2_jit_match(preg->compiled, (PCRE2_SPTR8)subject, len, 0, options,
+ match_data, NULL);
+ } else
+#endif
+ {
+ ret = pcre2_match(preg->compiled, (PCRE2_SPTR8)subject, len, 0, options,
+ match_data, NULL);
+ }
+ if (!pmatch) pcre2_match_data_free(match_data);
+ if (ret < 0) {
+ PCRE2_UCHAR errbuff[128];
+
+ if (dup_subject) talloc_free(our_subject);
+
+ if (ret == PCRE2_ERROR_NOMATCH) {
+ if (nmatch) *nmatch = 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;
+ }
+
+ if (nmatch && (ret > 0)) *nmatch = ret;
+
+ return 1;
+}
+
+/** Free libpcre2's matchdata
+ *
+ * @note Don't call directly, will be called if talloc_free is called on a #regmatch_t.
+ */
+static int _pcre2_match_data_free(regmatch_t *regmatch)
+{
+ pcre2_match_data_free(regmatch->match_data);
+ return 0;
+}
+
+/** Allocate vectors to fill with match data
+ *
+ * @param[in] ctx to allocate match vectors in.
+ * @param[in] count The number of vectors to allocate.
+ * @return
+ * - NULL on error.
+ * - Array of match vectors.
+ */
+regmatch_t *regex_match_data_alloc(TALLOC_CTX *ctx, uint32_t count)
+{
+ regmatch_t *regmatch;
+
+ regmatch = talloc(ctx, regmatch_t);
+ if (!regmatch) {
+ oom:
+ fr_strerror_printf("Out of memory");
+ return NULL;
+ }
+
+ regmatch->match_data = pcre2_match_data_create(count, _pcre2_gcontext);
+ if (!regmatch->match_data) {
+ talloc_free(regmatch);
+ goto oom;
+ }
+ talloc_set_type(regmatch->match_data, pcre2_match_data);
+
+ talloc_set_destructor(regmatch, _pcre2_match_data_free);
+
+ return regmatch;
+}
+
/*
* Wrapper functions for libpcre. Much more powerful, and guaranteed
* to be binary safe but require libpcre.
*/
-# ifdef HAVE_PCRE
+# elif defined(HAVE_PCRE)
/** Free regex_t structure
*
* Calls libpcre specific free functions for the expression and study.
int ret;
regex_t *preg, *rreg = NULL;
+#ifdef HAVE_PCRE2
+ regmatch_t *rxmatch;
+ size_t nmatch = REQUEST_MAX_REGEX + 1;
+#else
regmatch_t rxmatch[REQUEST_MAX_REGEX + 1]; /* +1 for %{0} (whole match) capture group */
size_t nmatch = sizeof(rxmatch) / sizeof(regmatch_t);
+#endif
if (!lhs || (lhs_type != PW_TYPE_STRING)) return -1;
switch (map->rhs->type) {
case TMPL_TYPE_REGEX_STRUCT: /* pre-compiled to a regex */
preg = map->rhs->tmpl_preg;
-#ifdef HAVE_PCRE
+#if defined(HAVE_PCRE) || defined(HAVE_PCRE2)
rad_assert(preg->precompiled);
#endif
break;
break;
}
+#ifdef HAVE_PCRE2
+ rxmatch = regex_match_data_alloc(request, REQUEST_MAX_REGEX + 1);
+#endif
ret = regex_exec(preg, lhs->strvalue, lhs_len, rxmatch, &nmatch);
switch (ret) {
case 0:
fr_cond_t *debug_condition = NULL; //!< Condition used to mark packets up for checking.
bool event_loop_started = false; //!< Whether the main event loop has been started yet.
+#ifdef HAVE_PCRE2
+# include <freeradius-devel/regex.h>
+#endif
+
typedef struct cached_config_t {
struct cached_config_t *next;
time_t created;
rad_assert(cs_cache == NULL);
cs_cache = cc;
+#ifdef HAVE_PCRE2
+ /*
+ * If pcre2 is being used for regex, we need to set up a global context
+ * to use our alloc / free routines.
+ * Since this is a library rather than module specific, it can't be done
+ * with a module bootstrap.
+ */
+ if (fr_pcre2_gcontext_setup() < 0) {
+ ERROR("Failed creating pcre2 general context");
+ return -1;
+ }
+#endif
+
/* Clear any unprocessed configuration errors */
(void) fr_strerror();
{
virtual_servers_free(0);
+#ifdef HAVE_PCRE2
+ fr_pcre2_gcontext_free();
+#endif
/*
* Clean up the configuration data
* structures.
#define REQUEST_DATA_REGEX (0xadbeef00)
typedef struct regcapture {
-#ifdef HAVE_PCRE
+#if defined(HAVE_PCRE) || defined(HAVE_PCRE2)
regex_t *preg; //!< Compiled pattern.
#endif
char const *value; //!< Original string.
*/
MEM(new_sc = talloc(request, regcapture_t));
+#ifdef HAVE_PCRE2
+ new_sc->rxmatch = talloc_steal(new_sc, rxmatch);
+#else
MEM(new_sc->rxmatch = talloc_memdup(new_sc, rxmatch, sizeof(rxmatch[0]) * nmatch));
talloc_set_type(new_sc->rxmatch, regmatch_t[]);
+#endif
MEM(p = talloc_array(new_sc, char, len + 1));
memcpy(p, value, len);
new_sc->value = p;
new_sc->nmatch = nmatch;
-#ifdef HAVE_PCRE
+#if defined(HAVE_PCRE) || defined(HAVE_PCRE2)
if (!(*preg)->precompiled) {
new_sc->preg = talloc_steal(new_sc, *preg);
*preg = NULL;
request_data_add(request, request, REQUEST_DATA_REGEX, new_sc, true);
}
-# ifdef HAVE_PCRE
+# ifdef HAVE_PCRE2
+/** Extract a subcapture value from the request
+ *
+ * @note This is the PCRE2 variant of the function.
+ *
+ * @param ctx To allocate subcapture buffer in.
+ * @param out Where to write the subcapture string.
+ * @param request to extract.
+ * @param num Subcapture index (0 for entire match).
+ * @return 0 on success, -1 on notfound.
+ */
+int regex_request_to_sub(TALLOC_CTX *ctx, char **out, REQUEST *request, uint32_t num)
+{
+ regcapture_t *cap;
+ char *buff;
+ int ret;
+ size_t len;
+ pcre2_match_data *match_data;
+
+ cap = request_data_reference(request, request, REQUEST_DATA_REGEX);
+ if (!cap) {
+ RDEBUG4("No subcapture data found");
+ *out = NULL;
+ return -1;
+ }
+ match_data = talloc_get_type_abort(cap->rxmatch->match_data, pcre2_match_data);
+
+ ret = pcre2_substring_length_bynumber(match_data, num, &len);
+ switch (ret) {
+ case PCRE2_ERROR_NOMEMORY:
+ MEM(NULL);
+ /* FALL-THROUGH */
+
+ /*
+ * Not finding a substring is fine
+ */
+ case PCRE2_ERROR_NOSUBSTRING:
+ RDEBUG4("%i/%zu Not found", num, cap->nmatch);
+ *out = NULL;
+ return -1;
+
+ default:
+ if (ret < 0) {
+ *out = NULL;
+ return -1;
+ }
+
+ MEM(buff = talloc_array(ctx, char, ++len)); /* +1 for \0, it'll get reset by pcre2_substring */
+ pcre2_substring_copy_bynumber(match_data, num, (PCRE2_UCHAR *)buff, &len); /* can't error */
+
+ memcpy(out, &buff, sizeof(*out));
+
+ RDEBUG4("%i/%zu Found: %s (%zu)", num, cap->nmatch, buff, talloc_array_length(buff));
+
+ return 0;
+ }
+}
+
+/** Extract a named subcapture value from the request
+ *
+ * @note This is the PCRE2 variant of the function.
+ *
+ * @param ctx To allocate subcapture buffer in.
+ * @param out Where to write the subcapture string.
+ * @param request to extract.
+ * @param name of subcapture.
+ * @return 0 on success, -1 on notfound.
+ */
+int regex_request_to_sub_named(TALLOC_CTX *ctx, char **out, REQUEST *request, char const *name)
+{
+ regcapture_t *cap;
+ char *buff;
+ int ret;
+ size_t len;
+ pcre2_match_data *match_data;
+
+ cap = request_data_reference(request, request, REQUEST_DATA_REGEX);
+ if (!cap) {
+ RDEBUG4("No subcapture data found");
+ *out = NULL;
+ return -1;
+ }
+ match_data = talloc_get_type_abort(cap->rxmatch->match_data, pcre2_match_data);
+
+ ret = pcre2_substring_length_byname(match_data, (PCRE2_UCHAR const *)name, &len);
+ switch (ret) {
+ case PCRE2_ERROR_NOMEMORY:
+ MEM(NULL);
+ /* FALL-THROUGH */
+
+ /*
+ * Not finding a substring is fine
+ */
+ case PCRE2_ERROR_NOSUBSTRING:
+ RDEBUG4("No named capture group \"%s\"", name);
+ *out = NULL;
+ return -1;
+
+ default:
+ if (ret < 0) {
+ *out = NULL;
+ return -1;
+ }
+
+ MEM(buff = talloc_array(ctx, char, ++len)); /* +1 for \0, it'll get reset by pcre2_substring */
+ pcre2_substring_copy_byname(match_data, (PCRE2_UCHAR const *)name, (PCRE2_UCHAR *)buff, &len); /* can't error */
+
+ memcpy(out, &buff, sizeof(*out));
+
+ RDEBUG4("Found \"%s\": %s (%zu)", name, buff, talloc_array_length(buff));
+
+ return 0;
+ }
+}
+# elif defined(HAVE_PCRE)
/** Extract a subcapture value from the request
*
* @note This is the PCRE variant of the function.