snort3: add patch and move to PCRE2
authorChristian Marangi <ansuelsmth@gmail.com>
Tue, 7 Nov 2023 00:17:25 +0000 (01:17 +0100)
committerJosef Schlehofer <pepe.schlehofer@gmail.com>
Sat, 27 Apr 2024 10:27:02 +0000 (12:27 +0200)
Add experimental patch and move package to PCRE2 as PCRE is EOL and
won't receive any security updates anymore.

Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
net/snort3/Makefile
net/snort3/patches/900-core-convert-project-to-PCRE2.patch [new file with mode: 0644]

index b9c85922b09ba8165127552e817e0ea1ee81846d..48ce85b456df0feb86d277f8254a16b4cb086649 100644 (file)
@@ -29,7 +29,7 @@ define Package/snort3
   DEPENDS:= \
            +(TARGET_x86||TARGET_x86_64):hyperscan-runtime \
            +(TARGET_x86||TARGET_x86_64):gperftools-runtime \
-           +libstdcpp +libdaq3 +libdnet +libopenssl +libpcap +libpcre +libpthread \
+           +libstdcpp +libdaq3 +libdnet +libopenssl +libpcap +libpcre2 +libpthread \
            +libuuid +zlib +libhwloc +libtirpc @HAS_LUAJIT_ARCH +luajit +libatomic \
            +kmod-nft-queue +liblzma +ucode +ucode-mod-fs +ucode-mod-uci
   TITLE:=Lightweight Network Intrusion Detection System
diff --git a/net/snort3/patches/900-core-convert-project-to-PCRE2.patch b/net/snort3/patches/900-core-convert-project-to-PCRE2.patch
new file mode 100644 (file)
index 0000000..01199f8
--- /dev/null
@@ -0,0 +1,2052 @@
+From a71cca137eb33f659354ce0ebda4951cb26485df Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Mon, 6 Nov 2023 22:43:59 +0100
+Subject: [PATCH] core: convert project to PCRE2
+
+Convert project to PCRE2 as PCRE is EOL and won't receive any security
+updates anymore.
+
+PCRE2 changed some API and concept. Mainly there isn't the concept of
+study anymore, replaced by match_context concept and match_data is used
+instead of ovector to handle results. Because of this the scratcher is
+not needed anymore and is replaced by a simple function to setup the max
+ovector size on end module init.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+---
+ README.md                                     |  17 +-
+ cmake/FindPCRE.cmake                          |  32 --
+ cmake/FindPCRE2.cmake                         |  32 ++
+ cmake/create_pkg_config.cmake                 |   4 +-
+ cmake/include_libraries.cmake                 |   2 +-
+ configure_cmake.sh                            |  16 +-
+ lua/balanced.lua                              |   2 +-
+ lua/max_detect.lua                            |   6 +-
+ lua/security.lua                              |   4 +-
+ snort.pc.in                                   |   4 +-
+ src/CMakeLists.txt                            |   4 +-
+ src/detection/detection_module.cc             |  48 +--
+ src/detection/detection_options.cc            |   6 +-
+ src/ips_options/ips_options.cc                |   4 +-
+ src/ips_options/ips_pcre.cc                   | 391 ++++++++----------
+ src/main/shell.cc                             |   9 +-
+ src/main/snort_config.h                       |  26 +-
+ .../appid/lua_detector_api.cc                 |  62 +--
+ src/parser/parse_rule.cc                      |   4 +-
+ src/parser/parse_stream.cc                    |   2 +-
+ src/search_engines/test/hyperscan_test.cc     |   2 +-
+ src/utils/stats.cc                            |   6 +-
+ src/utils/stats.h                             |   6 +-
+ src/utils/util.cc                             |   8 +-
+ tools/snort2lua/config_states/config_api.cc   |  12 +-
+ .../config_states/config_no_option.cc         |  14 +-
+ .../config_states/config_one_int_option.cc    |  24 +-
+ tools/snort2lua/rule_states/CMakeLists.txt    |   2 +-
+ tools/snort2lua/rule_states/rule_api.cc       |   4 +-
+ .../{rule_pcre.cc => rule_pcre2.cc}           |  40 +-
+ .../snort2lua/rule_states/rule_sd_pattern.cc  |   4 +-
+ 31 files changed, 393 insertions(+), 404 deletions(-)
+ delete mode 100644 cmake/FindPCRE.cmake
+ create mode 100644 cmake/FindPCRE2.cmake
+ rename tools/snort2lua/rule_states/{rule_pcre.cc => rule_pcre2.cc} (80%)
+
+--- a/README.md
++++ b/README.md
+@@ -8,13 +8,14 @@ topics:
+ ---
+-* [Overview](#overview)
+-* [Dependencies](#dependencies)
+-* [Download](#download)
+-* [Build Snort](#build-snort)
+-* [Run Snort](#run-snort)
+-* [Documentation](#documentation)
+-* [Squeal](#squeal)
++- [Snort++](#snort)
++- [OVERVIEW](#overview)
++- [DEPENDENCIES](#dependencies)
++- [DOWNLOAD](#download)
++- [BUILD SNORT](#build-snort)
++- [RUN SNORT](#run-snort)
++- [DOCUMENTATION](#documentation)
++- [SQUEAL](#squeal)
+ # OVERVIEW
+@@ -61,7 +62,7 @@ the latest:
+ * OpenSSL from https://www.openssl.org/source/ for SHA and MD5 file signatures,
+   the protected_content rule option, and SSL service detection
+ * pcap from http://www.tcpdump.org for tcpdump style logging
+-* pcre from http://www.pcre.org for regular expression pattern matching
++* pcre2 from http://www.pcre.org for regular expression pattern matching
+ * pkgconfig from https://www.freedesktop.org/wiki/Software/pkg-config/ to locate build dependencies
+ * zlib from http://www.zlib.net for decompression
+--- a/cmake/FindPCRE.cmake
++++ /dev/null
+@@ -1,32 +0,0 @@
+-# - Find pcre
+-# Find the native PCRE includes and library
+-#
+-#  PCRE_INCLUDE_DIR - where to find pcre.h, etc.
+-#  PCRE_LIBRARIES    - List of libraries when using pcre.
+-#  PCRE_FOUND        - True if pcre found.
+-
+-set(ERROR_MESSAGE
+-    "\n\tERROR!  Libpcre library not found.
+-    \tGet it from http://www.pcre.org\n"
+-)
+-
+-find_package(PkgConfig)
+-pkg_check_modules(PC_PCRE libpcre)
+-
+-# Use PCRE_INCLUDE_DIR_HINT and PCRE_LIBRARIES_DIR_HINT from configure_cmake.sh as primary hints
+-# and then package config information after that.
+-find_path(PCRE_INCLUDE_DIR pcre.h
+-    HINTS ${PCRE_INCLUDE_DIR_HINT} ${PC_PCRE_INCLUDEDIR} ${PC_PCRE_INCLUDE_DIRS})
+-find_library(PCRE_LIBRARIES NAMES pcre
+-    HINTS ${PCRE_LIBRARIES_DIR_HINT} ${PC_PCRE_LIBDIR} ${PC_PCRE_LIBRARY_DIRS})
+-
+-include(FindPackageHandleStandardArgs)
+-find_package_handle_standard_args(PCRE
+-    REQUIRED_VARS PCRE_INCLUDE_DIR PCRE_LIBRARIES
+-    FAIL_MESSAGE "${ERROR_MESSAGE}"
+-)
+-
+-mark_as_advanced(
+-    PCRE_LIBRARIES
+-    PCRE_INCLUDE_DIR
+-)
+--- /dev/null
++++ b/cmake/FindPCRE2.cmake
+@@ -0,0 +1,32 @@
++# - Find pcre2
++# Find the native PCRE2 includes and library
++#
++#  PCRE2_INCLUDE_DIR - where to find pcre2.h, etc.
++#  PCRE2_LIBRARIES    - List of libraries when using pcre2.
++#  PCRE2_FOUND        - True if pcre2 found.
++
++set(ERROR_MESSAGE
++    "\n\tERROR!  Libpcre2 library not found.
++    \tGet it from http://www.pcre.org\n"
++)
++
++find_package(PkgConfig)
++pkg_check_modules(PC_PCRE2 libpcre2-8)
++
++# Use PCRE2_INCLUDE_DIR_HINT and PCRE_LIBRARIES_DIR_HINT from configure_cmake.sh as primary hints
++# and then package config information after that.
++find_path(PCRE2_INCLUDE_DIR pcre2.h
++    HINTS ${PCRE2_INCLUDE_DIR_HINT} ${PC_PCRE2_INCLUDEDIR} ${PC_PCRE2_INCLUDE_DIRS})
++find_library(PCRE2_LIBRARIES NAMES pcre2-8
++    HINTS ${PCRE2_LIBRARIES_DIR_HINT} ${PC_PCRE2_LIBDIR} ${PC_PCRE2_LIBRARY_DIRS})
++
++include(FindPackageHandleStandardArgs)
++find_package_handle_standard_args(PCRE2-8
++    REQUIRED_VARS PCRE2_INCLUDE_DIR PCRE2_LIBRARIES
++    FAIL_MESSAGE "${ERROR_MESSAGE}"
++)
++
++mark_as_advanced(
++    PCRE2_LIBRARIES
++    PCRE2_INCLUDE_DIR
++)
+--- a/cmake/create_pkg_config.cmake
++++ b/cmake/create_pkg_config.cmake
+@@ -72,8 +72,8 @@ if(PCAP_INCLUDE_DIR)
+     set(PCAP_CPPFLAGS "-I${PCAP_INCLUDE_DIR}")
+ endif()
+-if(PCRE_INCLUDE_DIR)
+-    set(PCRE_CPPFLAGS "-I${PCRE_INCLUDE_DIR}")
++if(PCRE2_INCLUDE_DIR)
++    set(PCRE2_CPPFLAGS "-I${PCRE2_INCLUDE_DIR}")
+ endif()
+ if(UUID_INCLUDE_DIR)
+--- a/cmake/include_libraries.cmake
++++ b/cmake/include_libraries.cmake
+@@ -8,7 +8,7 @@ find_package(HWLOC REQUIRED)
+ find_package(LuaJIT REQUIRED)
+ find_package(OpenSSL 1.1.1 REQUIRED)
+ find_package(PCAP REQUIRED)
+-find_package(PCRE REQUIRED)
++find_package(PCRE2 REQUIRED)
+ find_package(ZLIB REQUIRED)
+ if (ENABLE_UNIT_TESTS)
+     find_package(CppUTest REQUIRED)
+--- a/configure_cmake.sh
++++ b/configure_cmake.sh
+@@ -90,10 +90,10 @@ Optional Packages:
+                             luajit include directory
+     --with-luajit-libraries=DIR
+                             luajit library directory
+-    --with-pcre-includes=DIR
+-                            libpcre include directory
+-    --with-pcre-libraries=DIR
+-                            libpcre library directory
++    --with-pcre2-includes=DIR
++                            libpcre2 include directory
++    --with-pcre2-libraries=DIR
++                            libpcre2 library directory
+     --with-dnet-includes=DIR
+                             libdnet include directory
+     --with-dnet-libraries=DIR
+@@ -417,11 +417,11 @@ while [ $# -ne 0 ]; do
+         --with-luajit-libraries=*)
+             append_cache_entry LUAJIT_LIBRARIES_DIR_HINT PATH $optarg
+             ;;
+-        --with-pcre-includes=*)
+-            append_cache_entry PCRE_INCLUDE_DIR_HINT PATH $optarg
++        --with-pcre2-includes=*)
++            append_cache_entry PCRE2_INCLUDE_DIR_HINT PATH $optarg
+             ;;
+-        --with-pcre-libraries=*)
+-            append_cache_entry PCRE_LIBRARIES_DIR_HINT PATH $optarg
++        --with-pcre2-libraries=*)
++            append_cache_entry PCRE2_LIBRARIES_DIR_HINT PATH $optarg
+             ;;
+         --with-dnet-includes=*)
+             append_cache_entry DNET_INCLUDE_DIR_HINT PATH $optarg
+--- a/lua/balanced.lua
++++ b/lua/balanced.lua
+@@ -5,7 +5,7 @@
+ arp_spoof = nil
+-detection = { pcre_override = false }
++detection = { pcre2_override = false }
+ http_inspect.request_depth = 300
+ http_inspect.response_depth = 500
+--- a/lua/max_detect.lua
++++ b/lua/max_detect.lua
+@@ -10,13 +10,13 @@ ftp_server.check_encrypted = true
+ detection =
+ {
+-    pcre_match_limit = 3500,
+-    pcre_match_limit_recursion = 3500,
++    pcre2_match_limit = 3500,
++    pcre2_match_limit_recursion = 3500,
+     -- enable for hyperscan for best throughput
+     -- use multiple packet threads for fast startup
+     --hyperscan_literals = true,
+-    --pcre_to_regex = true
++    --pcre2_to_regex = true
+ }
+ http_inspect.decompress_pdf = true
+--- a/lua/security.lua
++++ b/lua/security.lua
+@@ -9,8 +9,8 @@ ftp_server.check_encrypted = true
+ detection =
+ {
+-    pcre_match_limit = 3500,
+-    pcre_match_limit_recursion = 3500
++    pcre2_match_limit = 3500,
++    pcre2_match_limit_recursion = 3500
+ }
+ http_inspect.decompress_pdf = true
+--- a/snort.pc.in
++++ b/snort.pc.in
+@@ -9,7 +9,7 @@ mandir=@mandir@
+ infodir=@infodir@
+ cpp_opts=DAQ LUAJIT
+-cpp_opts_other=DNET HWLOC HYPERSCAN LZMA OPENSSL PCAP PCRE UUID
++cpp_opts_other=DNET HWLOC HYPERSCAN LZMA OPENSSL PCAP PCRE2 UUID
+ PCAP_CPPFLAGS=@PCAP_CPPFLAGS@
+ LUAJIT_CPPFLAGS=@LUAJIT_CPPFLAGS@
+@@ -18,7 +18,7 @@ DAQ_CPPFLAGS=@DAQ_CPPFLAGS@
+ FLEX_CPPFLAGS=@FLEX_CPPFLAGS@
+ OPENSSL_CPPFLAGS=@OPENSSL_CPPFLAGS@
+ HWLOC_CPPFLAGS=@HWLOC_CPPFLAGS@
+-PCRE_CPPFLAGS=@PCRE_CPPFLAGS@
++PCRE2_CPPFLAGS=@PCRE2_CPPFLAGS@
+ LZMA_CPPFLAGS=@LZMA_CPPFLAGS@
+ HYPERSCAN_CPPFLAGS=@HYPERSCAN_CPPFLAGS@
+ UUID_CPPFLAGS=@UUID_CPPFLAGS@
+--- a/src/CMakeLists.txt
++++ b/src/CMakeLists.txt
+@@ -10,7 +10,7 @@ set(EXTERNAL_LIBRARIES
+     ${LUAJIT_LIBRARIES}
+     ${OPENSSL_CRYPTO_LIBRARY}
+     ${PCAP_LIBRARIES}
+-    ${PCRE_LIBRARIES}
++    ${PCRE2_LIBRARIES}
+     ${ZLIB_LIBRARIES}
+ )
+@@ -21,7 +21,7 @@ set(EXTERNAL_INCLUDES
+     ${HWLOC_INCLUDE_DIRS}
+     ${OPENSSL_INCLUDE_DIR}
+     ${PCAP_INCLUDE_DIR}
+-    ${PCRE_INCLUDE_DIR}
++    ${PCRE2_INCLUDE_DIR}
+     ${ZLIB_INCLUDE_DIRS}
+ )
+--- a/src/detection/detection_module.cc
++++ b/src/detection/detection_module.cc
+@@ -96,21 +96,21 @@ static const Parameter detection_params[
+     { "offload_threads", Parameter::PT_INT, "0:max32", "0",
+       "maximum number of simultaneous offloads (defaults to disabled)" },
+-    { "pcre_enable", Parameter::PT_BOOL, nullptr, "true",
+-      "enable pcre pattern matching" },
++    { "pcre2_enable", Parameter::PT_BOOL, nullptr, "true",
++      "enable pcre2 pattern matching" },
+-    { "pcre_match_limit", Parameter::PT_INT, "0:max32", "1500",
+-      "limit pcre backtracking, 0 = off" },
++    { "pcre2_match_limit", Parameter::PT_INT, "0:max32", "1500",
++      "limit pcre2 backtracking, 0 = off" },
+-    { "pcre_match_limit_recursion", Parameter::PT_INT, "0:max32", "1500",
+-      "limit pcre stack consumption, 0 = off" },
++    { "pcre2_match_limit_recursion", Parameter::PT_INT, "0:max32", "1500",
++      "limit pcre2 stack consumption, 0 = off" },
+-    { "pcre_override", Parameter::PT_BOOL, nullptr, "true",
+-      "enable pcre match limit overrides when pattern matching (ie ignore /O)" },
++    { "pcre2_override", Parameter::PT_BOOL, nullptr, "true",
++      "enable pcre2 match limit overrides when pattern matching (ie ignore /O)" },
+ #ifdef HAVE_HYPERSCAN
+-    { "pcre_to_regex", Parameter::PT_BOOL, nullptr, "false",
+-      "enable the use of regex instead of pcre for compatible expressions" },
++    { "pcre2_to_regex", Parameter::PT_BOOL, nullptr, "false",
++      "enable the use of regex instead of pcre2 for compatible expressions" },
+ #endif
+     { "enable_address_anomaly_checks", Parameter::PT_BOOL, nullptr, "false",
+@@ -221,13 +221,13 @@ bool DetectionModule::set(const char*, V
+     else if ( v.is("offload_threads") )
+         sc->offload_threads = v.get_uint32();
+-    else if ( v.is("pcre_enable") )
+-        v.update_mask(sc->run_flags, RUN_FLAG__NO_PCRE, true);
++    else if ( v.is("pcre2_enable") )
++        v.update_mask(sc->run_flags, RUN_FLAG__NO_PCRE2, true);
+-    else if ( v.is("pcre_match_limit") )
+-        sc->pcre_match_limit = v.get_uint32();
++    else if ( v.is("pcre2_match_limit") )
++        sc->pcre2_match_limit = v.get_uint32();
+-    else if ( v.is("pcre_match_limit_recursion") )
++    else if ( v.is("pcre2_match_limit_recursion") )
+     {
+         // Cap the pcre recursion limit to not exceed the stack size.
+         //
+@@ -252,21 +252,21 @@ bool DetectionModule::set(const char*, V
+         if (max_rec < 0)
+             max_rec = 0;
+-        sc->pcre_match_limit_recursion = v.get_uint32();
+-        if (sc->pcre_match_limit_recursion > max_rec)
++        sc->pcre2_match_limit_recursion = v.get_uint32();
++        if (sc->pcre2_match_limit_recursion > max_rec)
+         {
+-            sc->pcre_match_limit_recursion = max_rec;
+-            LogMessage("Capping pcre_match_limit_recursion to %ld, thread stack_size %ld.\n",
+-                sc->pcre_match_limit_recursion, thread_stack_size);
++            sc->pcre2_match_limit_recursion = max_rec;
++            LogMessage("Capping pcre2_match_limit_recursion to %ld, thread stack_size %llu.\n",
++                sc->pcre2_match_limit_recursion, thread_stack_size);
+         }
+     }
+-    else if ( v.is("pcre_override") )
+-        sc->pcre_override = v.get_bool();
++    else if ( v.is("pcre2_override") )
++        sc->pcre2_override = v.get_bool();
+ #ifdef HAVE_HYPERSCAN
+-    else if ( v.is("pcre_to_regex") )
+-        sc->pcre_to_regex = v.get_bool();
++    else if ( v.is("pcre2_to_regex") )
++        sc->pcre2_to_regex = v.get_bool();
+ #endif
+     else if ( v.is("enable_address_anomaly_checks") )
+--- a/src/detection/detection_options.cc
++++ b/src/detection/detection_options.cc
+@@ -595,7 +595,7 @@ int detection_option_node_evaluate(
+                             {
+                                 if ( !child_node->is_relative )
+                                 {
+-                                    // If it's a non-relative content or pcre, no reason
++                                    // If it's a non-relative content or pcre2, no reason
+                                     // to check again.  Only increment result once.
+                                     // Should hit this condition on first loop iteration.
+                                     if ( loop_count == 1 )
+@@ -661,10 +661,10 @@ int detection_option_node_evaluate(
+                 }
+                 // If all children branches matched, we don't need to reeval any of
+-                // the children so don't need to reeval this content/pcre rule
++                // the children so don't need to reeval this content/pcre2 rule
+                 // option at a new offset.
+                 // Else, reset the DOE ptr to last eval for offset/depth,
+-                // distance/within adjustments for this same content/pcre rule option.
++                // distance/within adjustments for this same content/pcre2 rule option.
+                 // If the node and its sub-tree propagate MATCH back,
+                 // then all its continuations are recalled.
+                 if ( result == node->num_children )
+--- a/src/ips_options/ips_options.cc
++++ b/src/ips_options/ips_options.cc
+@@ -72,7 +72,7 @@ extern const BaseApi* ips_ip_proto[];
+ extern const BaseApi* ips_isdataat[];
+ extern const BaseApi* ips_itype[];
+ extern const BaseApi* ips_msg[];
+-extern const BaseApi* ips_pcre[];
++extern const BaseApi* ips_pcre2[];
+ extern const BaseApi* ips_priority[];
+ extern const BaseApi* ips_raw_data[];
+ extern const BaseApi* ips_rem[];
+@@ -146,7 +146,7 @@ void load_ips_options()
+     PluginManager::load_plugins(ips_isdataat);
+     PluginManager::load_plugins(ips_itype);
+     PluginManager::load_plugins(ips_msg);
+-    PluginManager::load_plugins(ips_pcre);
++    PluginManager::load_plugins(ips_pcre2);
+     PluginManager::load_plugins(ips_priority);
+     PluginManager::load_plugins(ips_raw_data);
+     PluginManager::load_plugins(ips_rem);
+--- a/src/ips_options/ips_pcre.cc
++++ b/src/ips_options/ips_pcre.cc
+@@ -23,7 +23,8 @@
+ #include "config.h"
+ #endif
+-#include <pcre.h>
++#define PCRE2_CODE_UNIT_WIDTH 8
++#include <pcre2.h>
+ #include <cassert>
+@@ -43,33 +44,31 @@
+ using namespace snort;
+-#ifndef PCRE_STUDY_JIT_COMPILE
+-#define PCRE_STUDY_JIT_COMPILE 0
++#ifndef PCRE2_STUDY_JIT_COMPILE
++#define PCRE2_STUDY_JIT_COMPILE 0
+ #endif
+ //#define NO_JIT // uncomment to disable JIT for Xcode
+ #ifdef NO_JIT
+-#define PCRE_STUDY_FLAGS 0
+-#define pcre_release(x) pcre_free(x)
++#define PCRE2_JIT 0
+ #else
+-#define PCRE_STUDY_FLAGS PCRE_STUDY_JIT_COMPILE
+-#define pcre_release(x) pcre_free_study(x)
++#define PCRE2_JIT PCRE2_STUDY_JIT_COMPILE
+ #endif
++#define pcre2_release(x) pcre2_code_free(x)
+ #define SNORT_PCRE_RELATIVE         0x00010 // relative to the end of the last match
+ #define SNORT_PCRE_INVERT           0x00020 // invert detect
+ #define SNORT_PCRE_ANCHORED         0x00040
+ #define SNORT_OVERRIDE_MATCH_LIMIT  0x00080 // Override default limits on match & match recursion
+-#define s_name "pcre"
++#define s_name "pcre2"
+ #define mod_regex_name "regex"
+-struct PcreData
++struct Pcre2Data
+ {
+-    pcre* re;           /* compiled regex */
+-    pcre_extra* pe;     /* studied regex foo */
+-    bool free_pe;
++    pcre2_code* re;     /* compiled regex */
++    pcre2_match_context* match_context; /* match_context for limits */
+     int options;        /* sp_pcre specific options (relative & inverse) */
+     char* expression;
+ };
+@@ -83,36 +82,32 @@ struct PcreData
+ // by verify; search uses the value in snort conf
+ static int s_ovector_max = -1;
+-static unsigned scratch_index;
+-static ScratchAllocator* scratcher = nullptr;
+-
+-static THREAD_LOCAL ProfileStats pcrePerfStats;
++static THREAD_LOCAL ProfileStats pcre2PerfStats;
+ //-------------------------------------------------------------------------
+ // implementation foo
+ //-------------------------------------------------------------------------
+-static void pcre_capture(
+-    const void* code, const void* extra)
++static void pcre2_capture(const void* code)
+ {
+     int tmp_ovector_size = 0;
+-    pcre_fullinfo((const pcre*)code, (const pcre_extra*)extra,
+-        PCRE_INFO_CAPTURECOUNT, &tmp_ovector_size);
++    pcre2_pattern_info((const pcre2_code *)code,
++        PCRE2_INFO_CAPTURECOUNT, &tmp_ovector_size);
+     if (tmp_ovector_size > s_ovector_max)
+         s_ovector_max = tmp_ovector_size;
+ }
+-static void pcre_check_anchored(PcreData* pcre_data)
++static void pcre2_check_anchored(Pcre2Data* pcre2_data)
+ {
+     int rc;
+     unsigned long int options = 0;
+-    if ((pcre_data == nullptr) || (pcre_data->re == nullptr) || (pcre_data->pe == nullptr))
++    if ((pcre2_data == nullptr) || (pcre2_data->re == nullptr))
+         return;
+-    rc = pcre_fullinfo(pcre_data->re, pcre_data->pe, PCRE_INFO_OPTIONS, (void*)&options);
++    rc = pcre2_pattern_info(pcre2_data->re, PCRE2_INFO_ARGOPTIONS, (void*)&options);
+     switch (rc)
+     {
+     /* pcre_fullinfo fails for the following:
+@@ -127,40 +122,41 @@ static void pcre_check_anchored(PcreData
+         /* This is the success code */
+         break;
+-    case PCRE_ERROR_NULL:
+-        ParseError("pcre_fullinfo: code and/or where were null.");
++    case PCRE2_ERROR_NULL:
++        ParseError("pcre2_fullinfo: code and/or where were null.");
+         return;
+-    case PCRE_ERROR_BADMAGIC:
+-        ParseError("pcre_fullinfo: compiled code didn't have correct magic.");
++    case PCRE2_ERROR_BADMAGIC:
++        ParseError("pcre2_fullinfo: compiled code didn't have correct magic.");
+         return;
+-    case PCRE_ERROR_BADOPTION:
+-        ParseError("pcre_fullinfo: option type is invalid.");
++    case PCRE2_ERROR_BADOPTION:
++        ParseError("pcre2_fullinfo: option type is invalid.");
+         return;
+     default:
+-        ParseError("pcre_fullinfo: Unknown error code.");
++        ParseError("pcre2_fullinfo: Unknown error code.");
+         return;
+     }
+-    if ((options & PCRE_ANCHORED) && !(options & PCRE_MULTILINE))
++    if ((options & PCRE2_ANCHORED) && !(options & PCRE2_MULTILINE))
+     {
+         /* This means that this pcre rule option shouldn't be EvalStatus
+          * even if any of it's relative children should fail to match.
+          * It is anchored to the cursor set by the previous cursor setting
+          * rule option */
+-        pcre_data->options |= SNORT_PCRE_ANCHORED;
++        pcre2_data->options |= SNORT_PCRE_ANCHORED;
+     }
+ }
+-static void pcre_parse(const SnortConfig* sc, const char* data, PcreData* pcre_data)
++static void pcre2_parse(const SnortConfig* sc, const char* data, Pcre2Data* pcre2_data)
+ {
+-    const char* error;
++    PCRE2_UCHAR error[128];
+     char* re, * free_me;
+     char* opts;
+     char delimit = '/';
+-    int erroffset;
++    int errorcode;
++    PCRE2_SIZE erroffset;
+     int compile_flags = 0;
+     if (data == nullptr)
+@@ -180,7 +176,7 @@ static void pcre_parse(const SnortConfig
+     if (*re == '!')
+     {
+-        pcre_data->options |= SNORT_PCRE_INVERT;
++        pcre2_data->options |= SNORT_PCRE_INVERT;
+         re++;
+         while (isspace((int)*re))
+             re++;
+@@ -212,7 +208,7 @@ static void pcre_parse(const SnortConfig
+     else if (*re != delimit)
+         goto syntax;
+-    pcre_data->expression = snort_strdup(re);
++    pcre2_data->expression = snort_strdup(re);
+     /* find ending delimiter, trim delimit chars */
+     opts = strrchr(re, delimit);
+@@ -230,25 +226,25 @@ static void pcre_parse(const SnortConfig
+     {
+         switch (*opts)
+         {
+-        case 'i':  compile_flags |= PCRE_CASELESS;            break;
+-        case 's':  compile_flags |= PCRE_DOTALL;              break;
+-        case 'm':  compile_flags |= PCRE_MULTILINE;           break;
+-        case 'x':  compile_flags |= PCRE_EXTENDED;            break;
++        case 'i':  compile_flags |= PCRE2_CASELESS;            break;
++        case 's':  compile_flags |= PCRE2_DOTALL;              break;
++        case 'm':  compile_flags |= PCRE2_MULTILINE;           break;
++        case 'x':  compile_flags |= PCRE2_EXTENDED;            break;
+         /*
+          * these are pcre specific... don't work with perl
+          */
+-        case 'A':  compile_flags |= PCRE_ANCHORED;            break;
+-        case 'E':  compile_flags |= PCRE_DOLLAR_ENDONLY;      break;
+-        case 'G':  compile_flags |= PCRE_UNGREEDY;            break;
++        case 'A':  compile_flags |= PCRE2_ANCHORED;            break;
++        case 'E':  compile_flags |= PCRE2_DOLLAR_ENDONLY;      break;
++        case 'G':  compile_flags |= PCRE2_UNGREEDY;            break;
+         /*
+-         * these are snort specific don't work with pcre or perl
++         * these are snort specific don't work with pcre2 or perl
+          */
+-        case 'R':  pcre_data->options |= SNORT_PCRE_RELATIVE; break;
++        case 'R':  pcre2_data->options |= SNORT_PCRE_RELATIVE; break;
+         case 'O':
+-            if ( sc->pcre_override )
+-                pcre_data->options |= SNORT_OVERRIDE_MATCH_LIMIT;
++            if ( sc->pcre2_override )
++                pcre2_data->options |= SNORT_OVERRIDE_MATCH_LIMIT;
+             break;
+         default:
+@@ -259,71 +255,68 @@ static void pcre_parse(const SnortConfig
+     }
+     /* now compile the re */
+-    pcre_data->re = pcre_compile(re, compile_flags, &error, &erroffset, nullptr);
++    pcre2_data->re = pcre2_compile((PCRE2_SPTR)re, PCRE2_ZERO_TERMINATED, compile_flags, &errorcode, &erroffset, nullptr);
++
++    if (pcre2_data->re == nullptr)
++    {
++        pcre2_get_error_message(errorcode, error, 128);
++        ParseError(": pcre2 compile of '%s' failed at offset "
++            "%zu : %s", re, erroffset, error);
++        return;
++    }
+-    if (pcre_data->re == nullptr)
++    /* now create match context */
++    pcre2_data->match_context = pcre2_match_context_create(NULL);
++    if(pcre2_data->match_context == NULL)
+     {
+-        ParseError(": pcre compile of '%s' failed at offset "
+-            "%d : %s", re, erroffset, error);
++        ParseError(": failed to allocate memory for match context");
+         return;
+     }
+     /* now study it... */
+-    pcre_data->pe = pcre_study(pcre_data->re, PCRE_STUDY_FLAGS, &error);
++    if (PCRE2_JIT)
++        errorcode = pcre2_jit_compile(pcre2_data->re, PCRE2_JIT_COMPLETE);
+-    if (pcre_data->pe)
++    if (PCRE2_JIT || errorcode)
+     {
+-        if ((sc->get_pcre_match_limit() != 0) &&
+-            !(pcre_data->options & SNORT_OVERRIDE_MATCH_LIMIT))
++        if ((sc->get_pcre2_match_limit() != 0) &&
++            !(pcre2_data->options & SNORT_OVERRIDE_MATCH_LIMIT))
+         {
+-            if ( !(pcre_data->pe->flags & PCRE_EXTRA_MATCH_LIMIT) )
+-                pcre_data->pe->flags |= PCRE_EXTRA_MATCH_LIMIT;
+-
+-            pcre_data->pe->match_limit = sc->get_pcre_match_limit();
++            pcre2_set_match_limit(pcre2_data->match_context, sc->get_pcre2_match_limit());
+         }
+-        if ((sc->get_pcre_match_limit_recursion() != 0) &&
+-            !(pcre_data->options & SNORT_OVERRIDE_MATCH_LIMIT))
++        if ((sc->get_pcre2_match_limit_recursion() != 0) &&
++            !(pcre2_data->options & SNORT_OVERRIDE_MATCH_LIMIT))
+         {
+-            if ( !(pcre_data->pe->flags & PCRE_EXTRA_MATCH_LIMIT_RECURSION) )
+-                pcre_data->pe->flags |= PCRE_EXTRA_MATCH_LIMIT_RECURSION;
+-
+-            pcre_data->pe->match_limit_recursion =
+-                sc->get_pcre_match_limit_recursion();
++            pcre2_set_match_limit(pcre2_data->match_context, sc->get_pcre2_match_limit_recursion());
+         }
+     }
+     else
+     {
+-        if (!(pcre_data->options & SNORT_OVERRIDE_MATCH_LIMIT) &&
+-            ((sc->get_pcre_match_limit() != 0) ||
+-             (sc->get_pcre_match_limit_recursion() != 0)))
++        if (!(pcre2_data->options & SNORT_OVERRIDE_MATCH_LIMIT) &&
++            ((sc->get_pcre2_match_limit() != 0) ||
++             (sc->get_pcre2_match_limit_recursion() != 0)))
+         {
+-            pcre_data->pe = (pcre_extra*)snort_calloc(sizeof(pcre_extra));
+-            pcre_data->free_pe = true;
+-
+-            if (sc->get_pcre_match_limit() != 0)
++            if (sc->get_pcre2_match_limit() != 0)
+             {
+-                pcre_data->pe->flags |= PCRE_EXTRA_MATCH_LIMIT;
+-                pcre_data->pe->match_limit = sc->get_pcre_match_limit();
++                pcre2_set_match_limit(pcre2_data->match_context, sc->get_pcre2_match_limit());
+             }
+-            if (sc->get_pcre_match_limit_recursion() != 0)
++            if (sc->get_pcre2_match_limit_recursion() != 0)
+             {
+-                pcre_data->pe->flags |= PCRE_EXTRA_MATCH_LIMIT_RECURSION;
+-                pcre_data->pe->match_limit_recursion =
+-                    sc->get_pcre_match_limit_recursion();
++                pcre2_set_match_limit(pcre2_data->match_context, sc->get_pcre2_match_limit_recursion());
+             }
+         }
+     }
+-    if (error != nullptr)
++    if (PCRE2_JIT && errorcode)
+     {
+-        ParseError("pcre study failed : %s", error);
++        ParseError("pcre2 JIT failed : %s", error);
+         return;
+     }
+-    pcre_capture(pcre_data->re, pcre_data->pe);
+-    pcre_check_anchored(pcre_data);
++    pcre2_capture(pcre2_data->re);
++    pcre2_check_anchored(pcre2_data);
+     snort_free(free_me);
+     return;
+@@ -332,40 +325,44 @@ syntax:
+     snort_free(free_me);
+     // ensure integrity from parse error to fatal error
+-    if ( !pcre_data->expression )
+-        pcre_data->expression = snort_strdup("");
++    if ( !pcre2_data->expression )
++        pcre2_data->expression = snort_strdup("");
+-    ParseError("unable to parse pcre %s", data);
++    ParseError("unable to parse pcre2 %s", data);
+ }
+ /*
+- * Perform a search of the PCRE data.
++ * Perform a search of the PCRE2 data.
+  * found_offset will be set to -1 when the find is unsuccessful OR the routine is inverted
+  */
+-static bool pcre_search(
++static bool pcre2_search(
+     Packet* p,
+-    const PcreData* pcre_data,
++    const Pcre2Data* pcre2_data,
+     const uint8_t* buf,
+     unsigned len,
+     unsigned start_offset,
+     int& found_offset)
+ {
++    pcre2_match_data *match_data;
++    PCRE2_SIZE *ovector;
+     bool matched;
+     found_offset = -1;
+-    std::vector<void *> ss = p->context->conf->state[get_instance_id()];
+-    assert(ss[scratch_index]);
++    match_data = pcre2_match_data_create(p->context->conf->pcre2_ovector_size, NULL);
++    if (match_data == nullptr) {
++        pc.pcre2_error++;
++        return false;
++    }
+-    int result = pcre_exec(
+-        pcre_data->re,  /* result of pcre_compile() */
+-        pcre_data->pe,  /* result of pcre_study()   */
+-        (const char*)buf, /* the subject string */
+-        len,            /* the length of the subject string */
+-        start_offset,   /* start at offset 0 in the subject */
+-        0,              /* options(handled at compile time */
+-        (int*)ss[scratch_index], /* vector for substring information */
+-        p->context->conf->pcre_ovector_size); /* number of elements in the vector */
++    int result = pcre2_match(
++        pcre2_data->re,  /* result of pcre_compile() */
++        (PCRE2_SPTR)buf, /* the subject string */
++        (PCRE2_SIZE)len, /* the length of the subject string */
++        (PCRE2_SIZE)start_offset, /* start at offset 0 in the subject */
++        0,               /* options(handled at compile time */
++        match_data,      /* match data to store the match results */
++        pcre2_data->match_context); /* match context for limits */
+     if (result >= 0)
+     {
+@@ -390,34 +387,37 @@ static bool pcre_search(
+          * and a single int for scratch space.
+          */
+-        found_offset = ((int*)ss[scratch_index])[1];
++        ovector = pcre2_get_ovector_pointer(match_data);
++        found_offset = ovector[1];
+     }
+-    else if (result == PCRE_ERROR_NOMATCH)
++    else if (result == PCRE2_ERROR_NOMATCH)
+     {
+         matched = false;
+     }
+-    else if (result == PCRE_ERROR_MATCHLIMIT)
++    else if (result == PCRE2_ERROR_MATCHLIMIT)
+     {
+-        pc.pcre_match_limit++;
++        pc.pcre2_match_limit++;
+         matched = false;
+     }
+-    else if (result == PCRE_ERROR_RECURSIONLIMIT)
++    else if (result == PCRE2_ERROR_RECURSIONLIMIT)
+     {
+-        pc.pcre_recursion_limit++;
++        pc.pcre2_recursion_limit++;
+         matched = false;
+     }
+     else
+     {
+-        pc.pcre_error++;
++        pc.pcre2_error++;
+         return false;
+     }
+     /* invert sense of match */
+-    if (pcre_data->options & SNORT_PCRE_INVERT)
++    if (pcre2_data->options & SNORT_PCRE_INVERT)
+     {
+         matched = !matched;
+     }
++    pcre2_match_data_free(match_data);
++
+     return matched;
+ }
+@@ -425,14 +425,14 @@ static bool pcre_search(
+ // class methods
+ //-------------------------------------------------------------------------
+-class PcreOption : public IpsOption
++class Pcre2Option : public IpsOption
+ {
+ public:
+-    PcreOption(PcreData* c) :
++    Pcre2Option(Pcre2Data* c) :
+         IpsOption(s_name, RULE_OPTION_TYPE_CONTENT)
+     { config = c; }
+-    ~PcreOption() override;
++    ~Pcre2Option() override;
+     uint32_t hash() const override;
+     bool operator==(const IpsOption&) const override;
+@@ -446,17 +446,17 @@ public:
+     EvalStatus eval(Cursor&, Packet*) override;
+     bool retry(Cursor&, const Cursor&) override;
+-    PcreData* get_data()
++    Pcre2Data* get_data()
+     { return config; }
+-    void set_data(PcreData* pcre)
++    void set_data(Pcre2Data* pcre)
+     { config = pcre; }
+ private:
+-    PcreData* config;
++    Pcre2Data* config;
+ };
+-PcreOption::~PcreOption()
++Pcre2Option::~Pcre2Option()
+ {
+     if ( !config )
+         return;
+@@ -464,21 +464,16 @@ PcreOption::~PcreOption()
+     if ( config->expression )
+         snort_free(config->expression);
+-    if ( config->pe )
+-    {
+-        if ( config->free_pe )
+-            snort_free(config->pe);
+-        else
+-            pcre_release(config->pe);
+-    }
++    if ( config->match_context )
++        pcre2_match_context_free(config->match_context);
+     if ( config->re )
+-        free(config->re);  // external allocation
++        pcre2_code_free(config->re);  // external allocation
+     snort_free(config);
+ }
+-uint32_t PcreOption::hash() const
++uint32_t Pcre2Option::hash() const
+ {
+     uint32_t a = 0, b = 0, c = 0;
+     int expression_len = strlen(config->expression);
+@@ -532,14 +527,14 @@ uint32_t PcreOption::hash() const
+     return c;
+ }
+-bool PcreOption::operator==(const IpsOption& ips) const
++bool Pcre2Option::operator==(const IpsOption& ips) const
+ {
+     if ( !IpsOption::operator==(ips) )
+         return false;
+-    const PcreOption& rhs = (const PcreOption&)ips;
+-    PcreData* left = config;
+-    PcreData* right = rhs.config;
++    const Pcre2Option& rhs = (const Pcre2Option&)ips;
++    Pcre2Data* left = config;
++    Pcre2Data* right = rhs.config;
+     if (( strcmp(left->expression, right->expression) == 0) &&
+         ( left->options == right->options))
+@@ -550,13 +545,13 @@ bool PcreOption::operator==(const IpsOpt
+     return false;
+ }
+-IpsOption::EvalStatus PcreOption::eval(Cursor& c, Packet* p)
++IpsOption::EvalStatus Pcre2Option::eval(Cursor& c, Packet* p)
+ {
+     // cppcheck-suppress unreadVariable
+-    RuleProfile profile(pcrePerfStats);
++    RuleProfile profile(pcre2PerfStats);
+-    // short circuit this for testing pcre performance impact
+-    if ( p->context->conf->no_pcre() )
++    // short circuit this for testing pcre2 performance impact
++    if ( p->context->conf->no_pcre2() )
+         return NO_MATCH;
+     unsigned pos = c.get_delta();
+@@ -570,7 +565,7 @@ IpsOption::EvalStatus PcreOption::eval(C
+     int found_offset = -1; // where is the ending location of the pattern
+-    if ( pcre_search(p, config, c.buffer()+adj, c.size()-adj, pos, found_offset) )
++    if ( pcre2_search(p, config, c.buffer()+adj, c.size()-adj, pos, found_offset) )
+     {
+         if ( found_offset > 0 )
+         {
+@@ -585,17 +580,17 @@ IpsOption::EvalStatus PcreOption::eval(C
+ }
+ // we always advance by found_offset so no adjustments to cursor are done
+-// here; note also that this means relative pcre matches on overlapping
++// here; note also that this means relative pcre2 matches on overlapping
+ // patterns won't work.  given the test pattern "ABABACD":
+ //
+ // ( sid:1; content:"ABA"; content:"C"; within:1; )
+-// ( sid:2; pcre:"/ABA/"; content:"C"; within:1; )
++// ( sid:2; pcre2:"/ABA/"; content:"C"; within:1; )
+ //
+ // sid 1 will fire but sid 2 will NOT.  this example is easily fixed by
+-// using content, but more advanced pcre won't work for the relative /
++// using content, but more advanced pcre2 won't work for the relative /
+ // overlap case.
+-bool PcreOption::retry(Cursor&, const Cursor&)
++bool Pcre2Option::retry(Cursor&, const Cursor&)
+ {
+     if ((config->options & (SNORT_PCRE_INVERT | SNORT_PCRE_ANCHORED)))
+     {
+@@ -616,46 +611,43 @@ static const Parameter s_params[] =
+     { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
+ };
+-struct PcreStats
++struct Pcre2Stats
+ {
+-    PegCount pcre_rules;
++    PegCount pcre2_rules;
+ #ifdef HAVE_HYPERSCAN
+-    PegCount pcre_to_hyper;
++    PegCount pcre2_to_hyper;
+ #endif
+-    PegCount pcre_native;
+-    PegCount pcre_negated;
++    PegCount pcre2_native;
++    PegCount pcre2_negated;
+ };
+ const PegInfo pcre_pegs[] =
+ {
+-    { CountType::SUM, "pcre_rules", "total rules processed with pcre option" },
++    { CountType::SUM, "pcre2_rules", "total rules processed with pcre2 option" },
+ #ifdef HAVE_HYPERSCAN
+-    { CountType::SUM, "pcre_to_hyper", "total pcre rules by hyperscan engine" },
++    { CountType::SUM, "pcre2_to_hyper", "total pcre2 rules by hyperscan engine" },
+ #endif
+-    { CountType::SUM, "pcre_native", "total pcre rules compiled by pcre engine" },
+-    { CountType::SUM, "pcre_negated", "total pcre rules using negation syntax" },
++    { CountType::SUM, "pcre2_native", "total pcre2 rules compiled by pcre engine" },
++    { CountType::SUM, "pcre2_negated", "total pcre2 rules using negation syntax" },
+     { CountType::END, nullptr, nullptr }
+ };
+-PcreStats pcre_stats;
++Pcre2Stats pcre2_stats;
+ #define s_help \
+-    "rule option for matching payload data with pcre"
++    "rule option for matching payload data with pcre2"
+-class PcreModule : public Module
++class Pcre2Module : public Module
+ {
+ public:
+-    PcreModule() : Module(s_name, s_help, s_params)
++    Pcre2Module() : Module(s_name, s_help, s_params)
+     {
+         data = nullptr;
+-        scratcher = new SimpleScratchAllocator(scratch_setup, scratch_cleanup);
+-        scratch_index = scratcher->get_id();
+     }
+-    ~PcreModule() override
++    ~Pcre2Module() override
+     {
+         delete data;
+-        delete scratcher;
+     }
+ #ifdef HAVE_HYPERSCAN
+@@ -665,12 +657,12 @@ public:
+     bool end(const char*, int, SnortConfig*) override;
+     ProfileStats* get_profile() const override
+-    { return &pcrePerfStats; }
++    { return &pcre2PerfStats; }
+     const PegInfo* get_pegs() const override;
+     PegCount* get_counts() const override;
+-    PcreData* get_data();
++    Pcre2Data* get_data();
+     bool global_stats() const override
+     { return true; }
+@@ -682,31 +674,28 @@ public:
+     { return mod_regex; }
+ private:
+-    PcreData* data;
++    Pcre2Data* data;
+     Module* mod_regex = nullptr;
+     std::string re;
+-
+-    static bool scratch_setup(SnortConfig*);
+-    static void scratch_cleanup(SnortConfig*);
+ };
+-PcreData* PcreModule::get_data()
++Pcre2Data* Pcre2Module::get_data()
+ {
+-    PcreData* tmp = data;
++    Pcre2Data* tmp = data;
+     data = nullptr;
+     return tmp;
+ }
+-const PegInfo* PcreModule::get_pegs() const
++const PegInfo* Pcre2Module::get_pegs() const
+ { return pcre_pegs; }
+-PegCount* PcreModule::get_counts() const
+-{ return (PegCount*)&pcre_stats; }
++PegCount* Pcre2Module::get_counts() const
++{ return (PegCount*)&pcre2_stats; }
+ #ifdef HAVE_HYPERSCAN
+-bool PcreModule::begin(const char* name, int v, SnortConfig* sc)
++bool Pcre2Module::begin(const char* name, int v, SnortConfig* sc)
+ {
+-    if ( sc->pcre_to_regex )
++    if ( sc->pcre2_to_regex )
+     {
+         if ( !mod_regex )
+             mod_regex = ModuleManager::get_module(mod_regex_name);
+@@ -718,7 +707,7 @@ bool PcreModule::begin(const char* name,
+ }
+ #endif
+-bool PcreModule::set(const char* name, Value& v, SnortConfig* sc)
++bool Pcre2Module::set(const char* name, Value& v, SnortConfig* sc)
+ {
+     assert(v.is("~re"));
+     re = v.get_string();
+@@ -729,50 +718,28 @@ bool PcreModule::set(const char* name, V
+     return true;
+ }
+-bool PcreModule::end(const char* name, int v, SnortConfig* sc)
++bool Pcre2Module::end(const char* name, int v, SnortConfig* sc)
+ {
+     if( mod_regex )
+         mod_regex = mod_regex->end(name, v, sc) ? mod_regex : nullptr;
+     if ( !mod_regex )
+     {
+-        data = (PcreData*)snort_calloc(sizeof(*data));
+-        pcre_parse(sc, re.c_str(), data);
++        data = (Pcre2Data*)snort_calloc(sizeof(*data));
++        pcre2_parse(sc, re.c_str(), data);
+     }
+-    return true;
+-}
+-
+-bool PcreModule::scratch_setup(SnortConfig* sc)
+-{
+-    if ( s_ovector_max < 0 )
+-        return false;
+-
+     // The pcre_fullinfo() function can be used to find out how many
+     // capturing subpatterns there are in a compiled pattern. The
+     // smallest size for ovector that will allow for n captured
+     // substrings, in addition to the offsets of the substring matched
+     // by the whole pattern is 3(n+1).
+-
+-    sc->pcre_ovector_size = 3 * (s_ovector_max + 1);
+-    s_ovector_max = -1;
+-
+-    for ( unsigned i = 0; i < sc->num_slots; ++i )
+-    {
+-        std::vector<void *>& ss = sc->state[i];
+-        ss[scratch_index] = snort_calloc(sc->pcre_ovector_size, sizeof(int));
++    if ( s_ovector_max >= 0 ) {
++        sc->pcre2_ovector_size = 3 * (s_ovector_max + 1);
++        s_ovector_max = -1;
+     }
+-    return true;
+-}
+-void PcreModule::scratch_cleanup(SnortConfig* sc)
+-{
+-    for ( unsigned i = 0; i < sc->num_slots; ++i )
+-    {
+-        std::vector<void *>& ss = sc->state[i];
+-        snort_free(ss[scratch_index]);
+-        ss[scratch_index] = nullptr;
+-    }
++    return true;
+ }
+ //-------------------------------------------------------------------------
+@@ -780,21 +747,21 @@ void PcreModule::scratch_cleanup(SnortCo
+ //-------------------------------------------------------------------------
+ static Module* mod_ctor()
+-{ return new PcreModule; }
++{ return new Pcre2Module; }
+ static void mod_dtor(Module* m)
+ { delete m; }
+-static IpsOption* pcre_ctor(Module* p, OptTreeNode* otn)
++static IpsOption* pcre2_ctor(Module* p, OptTreeNode* otn)
+ {
+-    pcre_stats.pcre_rules++;
+-    PcreModule* m = (PcreModule*)p;
++    pcre2_stats.pcre2_rules++;
++    Pcre2Module* m = (Pcre2Module*)p;
+ #ifdef HAVE_HYPERSCAN
+     Module* mod_regex = m->get_mod_regex();
+     if ( mod_regex )
+     {
+-        pcre_stats.pcre_to_hyper++;
++        pcre2_stats.pcre2_to_hyper++;
+         const IpsApi* opt_api = IpsManager::get_option_api(mod_regex_name);
+         return opt_api->ctor(mod_regex, otn);
+     }
+@@ -803,16 +770,16 @@ static IpsOption* pcre_ctor(Module* p, O
+     UNUSED(otn);
+ #endif
+     {
+-        pcre_stats.pcre_native++;
+-        PcreData* d = m->get_data();
+-        return new PcreOption(d);
++        pcre2_stats.pcre2_native++;
++        Pcre2Data* d = m->get_data();
++        return new Pcre2Option(d);
+     }
+ }
+-static void pcre_dtor(IpsOption* p)
++static void pcre2_dtor(IpsOption* p)
+ { delete p; }
+-static const IpsApi pcre_api =
++static const IpsApi pcre2_api =
+ {
+     {
+         PT_IPS_OPTION,
+@@ -832,17 +799,17 @@ static const IpsApi pcre_api =
+     nullptr,
+     nullptr,
+     nullptr,
+-    pcre_ctor,
+-    pcre_dtor,
++    pcre2_ctor,
++    pcre2_dtor,
+     nullptr
+ };
+ #ifdef BUILDING_SO
+ SO_PUBLIC const BaseApi* snort_plugins[] =
+ #else
+-const BaseApi* ips_pcre[] =
++const BaseApi* ips_pcre2[] =
+ #endif
+ {
+-    &pcre_api.base,
++    &pcre2_api.base,
+     nullptr
+ };
+--- a/src/main/shell.cc
++++ b/src/main/shell.cc
+@@ -29,7 +29,8 @@
+ #include <fstream>
+ #include <openssl/crypto.h>
+ #include <pcap.h>
+-#include <pcre.h>
++#define PCRE2_CODE_UNIT_WIDTH 8
++#include <pcre2.h>
+ #include <stdexcept>
+ #include <vector>
+ #include <zlib.h>
+@@ -138,13 +139,17 @@ static void install_version_strings(lua_
+ static void install_dependencies_strings(Shell* sh, lua_State* L)
+ {
++
+     assert(dep_versions[0]);
++    const char pcre2_version[32] = { 0 };
+     std::vector<const char*> vs;
+     const char* ljv = LUAJIT_VERSION;
+     const char* osv = OpenSSL_version(SSLEAY_VERSION);
+     const char* lpv = pcap_lib_version();
++    pcre2_config(PCRE2_CONFIG_VERSION, (PCRE2_UCHAR8 *)pcre2_version);
++
+     while (*ljv and !isdigit(*ljv))
+         ++ljv;
+     while (*osv and !isdigit(*osv))
+@@ -156,7 +161,7 @@ static void install_dependencies_strings
+     vs.push_back(ljv);
+     vs.push_back(osv);
+     vs.push_back(lpv);
+-    vs.push_back(pcre_version());
++    vs.push_back(pcre2_version);
+     vs.push_back(zlib_version);
+ #ifdef HAVE_HYPERSCAN
+     vs.push_back(hs_version());
+--- a/src/main/snort_config.h
++++ b/src/main/snort_config.h
+@@ -60,7 +60,7 @@ enum RunFlag
+     RUN_FLAG__PCAP_SHOW           = 0x00001000,
+     RUN_FLAG__SHOW_FILE_CODES     = 0x00002000,
+     RUN_FLAG__PAUSE               = 0x00004000,
+-    RUN_FLAG__NO_PCRE             = 0x00008000,
++    RUN_FLAG__NO_PCRE2            = 0x00008000,
+     RUN_FLAG__DUMP_RULE_STATE     = 0x00010000,
+     RUN_FLAG__DUMP_RULE_DEPS      = 0x00020000,
+@@ -214,13 +214,13 @@ public:
+     //------------------------------------------------------
+     // detection module stuff
+-    // FIXIT-L pcre_match_limit* are interdependent
++    // FIXIT-L pcre2_match_limit* are interdependent
+     // somehow a packet thread needs a much lower setting
+-    long int pcre_match_limit = 1500;
+-    long int pcre_match_limit_recursion = 1500;
++    long int pcre2_match_limit = 1500;
++    long int pcre2_match_limit_recursion = 1500;
+-    int pcre_ovector_size = 0;
+-    bool pcre_override = true;
++    int pcre2_ovector_size = 0;
++    bool pcre2_override = true;
+     uint32_t run_flags = 0;
+@@ -228,7 +228,7 @@ public:
+     unsigned offload_threads = 0;    // disabled
+     bool hyperscan_literals = false;
+-    bool pcre_to_regex = false;
++    bool pcre2_to_regex = false;
+     bool global_rule_state = false;
+     bool global_default_rule_state = true;
+@@ -600,8 +600,8 @@ public:
+     bool alert_before_pass() const
+     { return run_flags & RUN_FLAG__ALERT_BEFORE_PASS; }
+-    bool no_pcre() const
+-    { return run_flags & RUN_FLAG__NO_PCRE; }
++    bool no_pcre2() const
++    { return run_flags & RUN_FLAG__NO_PCRE2; }
+     bool conf_error_out() const
+     { return run_flags & RUN_FLAG__CONF_ERROR_OUT; }
+@@ -616,11 +616,11 @@ public:
+     uint8_t new_ttl() const
+     { return get_network_policy()->new_ttl; }
+-    long int get_pcre_match_limit() const
+-    { return pcre_match_limit; }
++    long int get_pcre2_match_limit() const
++    { return pcre2_match_limit; }
+-    long int get_pcre_match_limit_recursion() const
+-    { return pcre_match_limit_recursion; }
++    long int get_pcre2_match_limit_recursion() const
++    { return pcre2_match_limit_recursion; }
+     const ProfilerConfig* get_profiler() const
+     { return profiler; }
+--- a/src/network_inspectors/appid/lua_detector_api.cc
++++ b/src/network_inspectors/appid/lua_detector_api.cc
+@@ -25,7 +25,8 @@
+ #include "lua_detector_api.h"
+ #include <lua.hpp>
+-#include <pcre.h>
++#define PCRE2_CODE_UNIT_WIDTH 8
++#include <pcre2.h>
+ #include <unordered_map>
+ #include "detection/fp_config.h"
+@@ -714,7 +715,7 @@ static int detector_get_packet_direction
+     return 1;
+ }
+-/**Perform a pcre match with grouping. A simple regular expression match with no grouping
++/**Perform a pcre2 match with grouping. A simple regular expression match with no grouping
+  * can also be performed.
+  *
+  * @param Lua_State* - Lua state variable.
+@@ -723,41 +724,50 @@ static int detector_get_packet_direction
+  * @return matchedStrings/stack - matched strings are pushed on stack starting with group 0.
+  *     There may be 0 or more strings.
+  */
+-static int detector_get_pcre_groups(lua_State* L)
++static int detector_get_pcre2_groups(lua_State* L)
+ {
+     auto& ud = *UserData<LuaObject>::check(L, DETECTOR, 1);
+     // Verify detector user data and that we are in packet context
+     LuaStateDescriptor* lsd = ud->validate_lua_state(true);
+-    int ovector[OVECCOUNT];
+-    const char* error;
+-    int erroffset;
++    PCRE2_SIZE* ovector;
++    pcre2_match_data* match_data;
++    PCRE2_UCHAR error[128];
++    PCRE2_SIZE erroffset;
++    int errorcode;
+     const char* pattern = lua_tostring(L, 2);
+     unsigned int offset = lua_tonumber(L, 3);     /*offset can be zero, no check necessary. */
+     /*compile the regular expression pattern, and handle errors */
+-    pcre* re = pcre_compile(pattern,  // the pattern
+-        PCRE_DOTALL,                  // default options - dot matches all inc \n
+-        &error,                       // for error message
+-        &erroffset,                   // for error offset
+-        nullptr);                     // use default character tables
++    pcre2_code* re = pcre2_compile((PCRE2_SPTR)pattern,  // the pattern
++        PCRE2_ZERO_TERMINATED,         // assume zero terminated strings
++        PCRE2_DOTALL,                  // default options - dot matches all inc \n
++        &errorcode,                    // for error message
++        &erroffset,                    // for error offset
++        nullptr);                      // use default character tables
+     if (re == nullptr)
+     {
+-        appid_log(lsd->ldp.pkt, TRACE_ERROR_LEVEL, "PCRE compilation failed at offset %d: %s\n", erroffset, error);
++        pcre2_get_error_message(errorcode, error, 128);
++        appid_log(lsd->ldp.pkt, TRACE_ERROR_LEVEL, "PCRE2 compilation failed at offset %d: %s\n", erroffset, error);
++        return 0;
++    }
++
++    match_data = pcre2_match_data_create(OVECCOUNT, NULL);
++    if (match_data == nullptr) {
++        appid_log(lsd->ldp.pkt, TRACE_ERROR_LEVEL, "PCRE2 failed to allocate mem for match_data\n");
+         return 0;
+     }
+     /*pattern match against the subject string. */
+-    int rc = pcre_exec(re,            // compiled pattern
+-        nullptr,                      // no extra data
+-        (const char*)lsd->ldp.data,   // subject string
+-        lsd->ldp.size,                // length of the subject
+-        offset,                       // offset 0
+-        0,                            // default options
+-        ovector,                      // output vector for substring information
+-        OVECCOUNT);                   // number of elements in the output vector
++    int rc = pcre2_match(re,         // compiled pattern
++        (PCRE2_SPTR)lsd->ldp.data,   // subject string
++        (PCRE2_SIZE)lsd->ldp.size,   // length of the subject
++        (PCRE2_SIZE)offset,          // offset 0
++        0,                           // default options
++        match_data,                  // match data for match results
++        NULL);                       // no match context
+     if (rc >= 0)
+     {
+@@ -771,10 +781,11 @@ static int detector_get_pcre_groups(lua_
+         if (!lua_checkstack(L, rc))
+         {
+             appid_log(lsd->ldp.pkt, TRACE_WARNING_LEVEL, "Cannot grow Lua stack by %d slots to hold "
+-                "PCRE matches\n", rc);
++                "PCRE2 matches\n", rc);
+             return 0;
+         }
++        ovector = pcre2_get_ovector_pointer(match_data);
+         for (int i = 0; i < rc; i++)
+         {
+             lua_pushlstring(L, (const char*)lsd->ldp.data + ovector[2*i], ovector[2*i+1] -
+@@ -784,12 +795,13 @@ static int detector_get_pcre_groups(lua_
+     else
+     {
+         // log errors except no matches
+-        if (rc != PCRE_ERROR_NOMATCH)
+-            appid_log(lsd->ldp.pkt, TRACE_WARNING_LEVEL, "PCRE regular expression group match failed. rc: %d\n", rc);
++        if (rc != PCRE2_ERROR_NOMATCH)
++            appid_log(lsd->ldp.pkt, TRACE_WARNING_LEVEL, "PCRE2 regular expression group match failed. rc: %d\n", rc);
+         rc = 0;
+     }
+-    pcre_free(re);
++    pcre2_match_data_free(match_data);
++    pcre2_code_free(re);
+     return rc;
+ }
+@@ -3229,7 +3241,7 @@ static const luaL_Reg detector_methods[]
+     { "getPacketSize",            detector_get_packet_size },
+     { "getPacketDir",             detector_get_packet_direction },
+     { "matchSimplePattern",       detector_memcmp },
+-    { "getPcreGroups",            detector_get_pcre_groups },
++    { "getPcreGroups",            detector_get_pcre2_groups },
+     { "getL4Protocol",            detector_get_protocol_type },
+     { "getPktSrcAddr",            detector_get_packet_src_addr },
+     { "getPktDstAddr",            detector_get_packet_dst_addr },
+--- a/src/parser/parse_rule.cc
++++ b/src/parser/parse_rule.cc
+@@ -911,10 +911,10 @@ void parse_rule_dir(SnortConfig*, const
+         ParseError("illegal direction specifier: %s", s);
+ }
+-// Values of the rule options "pcre", "regex" and "sd_pattern" are already escaped
++// Values of the rule options "pcre2", "regex" and "sd_pattern" are already escaped
+ // They are not unescaped during the rule parsing
+ static bool is_already_escaped(const std::string& opt_key)
+-{ return opt_key == "pcre" or opt_key == "regex" or opt_key == "sd_pattern"; }
++{ return opt_key == "pcre2" or opt_key == "regex" or opt_key == "sd_pattern"; }
+ static std::string escape(const std::string& s)
+ {
+--- a/src/parser/parse_stream.cc
++++ b/src/parser/parse_stream.cc
+@@ -603,7 +603,7 @@ static bool exec(
+ // that individual rule options can do whatever
+ static int get_escape(const string& s)
+ {
+-    if ( s == "pcre" )
++    if ( s == "pcre2" )
+         return 0;  // no escape, option goes to ;
+     else if ( s == "regex" || s == "sd_pattern" )
+--- a/src/search_engines/test/hyperscan_test.cc
++++ b/src/search_engines/test/hyperscan_test.cc
+@@ -223,7 +223,7 @@ TEST(mpse_hs_match, regex)
+     CHECK(hits == 3);
+ }
+-TEST(mpse_hs_match, pcre)
++TEST(mpse_hs_match, pcre2)
+ {
+     Mpse::PatternDescriptor desc;
+--- a/src/utils/stats.cc
++++ b/src/utils/stats.cc
+@@ -227,9 +227,9 @@ const PegInfo pc_names[] =
+     { CountType::SUM, "offload_fallback", "fast pattern offload search fallback attempts" },
+     { CountType::SUM, "offload_failures", "fast pattern offload search failures" },
+     { CountType::SUM, "offload_suspends", "fast pattern search suspends due to offload context chains" },
+-    { CountType::SUM, "pcre_match_limit", "total number of times pcre hit the match limit" },
+-    { CountType::SUM, "pcre_recursion_limit", "total number of times pcre hit the recursion limit" },
+-    { CountType::SUM, "pcre_error", "total number of times pcre returns error" },
++    { CountType::SUM, "pcre2_match_limit", "total number of times pcre2 hit the match limit" },
++    { CountType::SUM, "pcre2_recursion_limit", "total number of times pcre2 hit the recursion limit" },
++    { CountType::SUM, "pcre2_error", "total number of times pcre2 returns error" },
+     { CountType::SUM, "cont_creations", "total number of continuations created" },
+     { CountType::SUM, "cont_recalls", "total number of continuations recalled" },
+     { CountType::SUM, "cont_flows", "total number of flows using continuation" },
+--- a/src/utils/stats.h
++++ b/src/utils/stats.h
+@@ -60,9 +60,9 @@ struct PacketCount
+     PegCount offload_fallback;
+     PegCount offload_failures;
+     PegCount offload_suspends;
+-    PegCount pcre_match_limit;
+-    PegCount pcre_recursion_limit;
+-    PegCount pcre_error;
++    PegCount pcre2_match_limit;
++    PegCount pcre2_recursion_limit;
++    PegCount pcre2_error;
+     PegCount cont_creations;
+     PegCount cont_recalls;
+     PegCount cont_flows;
+--- a/src/utils/util.cc
++++ b/src/utils/util.cc
+@@ -30,7 +30,8 @@
+ #include <netdb.h>
+ #include <openssl/crypto.h>
+ #include <pcap.h>
+-#include <pcre.h>
++#define PCRE2_CODE_UNIT_WIDTH 8
++#include <pcre2.h>
+ #include <pwd.h>
+ #include <sys/file.h>
+ #include <sys/resource.h>
+@@ -105,10 +106,13 @@ void StoreSnortInfoStrings()
+ int DisplayBanner()
+ {
++    PCRE2_UCHAR pcre2_version[32];
+     const char* ljv = LUAJIT_VERSION;
+     while ( *ljv && !isdigit(*ljv) )
+         ++ljv;
++    pcre2_config(PCRE2_CONFIG_VERSION, pcre2_version);
++
+     LogMessage("\n");
+     LogMessage("   ,,_     -*> Snort++ <*-\n");
+ #ifdef BUILD
+@@ -125,7 +129,7 @@ int DisplayBanner()
+     LogMessage("           Using LuaJIT version %s\n", ljv);
+     LogMessage("           Using %s\n", OpenSSL_version(SSLEAY_VERSION));
+     LogMessage("           Using %s\n", pcap_lib_version());
+-    LogMessage("           Using PCRE version %s\n", pcre_version());
++    LogMessage("           Using PCRE version %s\n", pcre2_version);
+     LogMessage("           Using ZLIB version %s\n", zlib_version);
+ #ifdef HAVE_HYPERSCAN
+     LogMessage("           Using Hyperscan version %s\n", hs_version());
+--- a/tools/snort2lua/config_states/config_api.cc
++++ b/tools/snort2lua/config_states/config_api.cc
+@@ -105,13 +105,13 @@ extern const ConvertMap* min_ttl_map;
+ extern const ConvertMap* na_policy_mode_map;
+ extern const ConvertMap* new_ttl_map;
+ extern const ConvertMap* nolog_map;
+-extern const ConvertMap* nopcre_map;
++extern const ConvertMap* nopcre2_map;
+ extern const ConvertMap* no_promisc_map;
+ extern const ConvertMap* obfuscate_map;
+ extern const ConvertMap* order_map;
+ extern const ConvertMap* paf_max_map;
+-extern const ConvertMap* pcre_match_limit_map;
+-extern const ConvertMap* pcre_match_limit_recursion_map;
++extern const ConvertMap* pcre2_match_limit_map;
++extern const ConvertMap* pcre2_match_limit_recursion_map;
+ extern const ConvertMap* pkt_count_map;
+ extern const ConvertMap* ppm_map;
+ extern const ConvertMap* policy_id_map;
+@@ -224,13 +224,13 @@ const std::vector<const ConvertMap*> con
+     na_policy_mode_map,
+     new_ttl_map,
+     nolog_map,
+-    nopcre_map,
++    nopcre2_map,
+     no_promisc_map,
+     obfuscate_map,
+     order_map,
+     paf_max_map,
+-    pcre_match_limit_map,
+-    pcre_match_limit_recursion_map,
++    pcre2_match_limit_map,
++    pcre2_match_limit_recursion_map,
+     pkt_count_map,
+     ppm_map,
+     policy_id_map,
+--- a/tools/snort2lua/config_states/config_no_option.cc
++++ b/tools/snort2lua/config_states/config_no_option.cc
+@@ -250,18 +250,18 @@ static const ConvertMap enable_mpls_over
+ const ConvertMap* enable_mpls_overlapping_ip_map = &enable_mpls_overlapping_ip_api;
+ /*************************************************
+- ********************  nopcre  *******************
++ ********************  nopcre2  *******************
+  *************************************************/
+-static const std::string nopcre = "nopcre";
+-static const std::string pcre_enable = "pcre_enable";
+-static const ConvertMap nopcre_api =
++static const std::string nopcre2 = "nopcre2";
++static const std::string pcre2_enable = "pcre2_enable";
++static const ConvertMap nopcre2_api =
+ {
+-    nopcre,
+-    config_false_no_opt_ctor<& nopcre, & detection, & pcre_enable>
++    nopcre2,
++    config_false_no_opt_ctor<& nopcre2, & detection, & pcre2_enable>
+ };
+-const ConvertMap* nopcre_map = &nopcre_api;
++const ConvertMap* nopcre2_map = &nopcre2_api;
+ /*************************************************
+  ******************  obfuscate  ******************
+--- a/tools/snort2lua/config_states/config_one_int_option.cc
++++ b/tools/snort2lua/config_states/config_one_int_option.cc
+@@ -217,30 +217,30 @@ static const ConvertMap new_ttl_api =
+ const ConvertMap* new_ttl_map = &new_ttl_api;
+ /*************************************************
+- **************  pcre_match_limit   **************
++ **************  pcre2_match_limit   **************
+  *************************************************/
+-static const std::string pcre_match_limit = "pcre_match_limit";
+-static const ConvertMap pcre_match_limit_api =
++static const std::string pcre2_match_limit = "pcre2_match_limit";
++static const ConvertMap pcre2_match_limit_api =
+ {
+-    pcre_match_limit,
+-    config_int_ctor<& pcre_match_limit, & detection>,
++    pcre2_match_limit,
++    config_int_ctor<& pcre2_match_limit, & detection>,
+ };
+-const ConvertMap* pcre_match_limit_map = &pcre_match_limit_api;
++const ConvertMap* pcre2_match_limit_map = &pcre2_match_limit_api;
+ /**************************************************
+- **********  pcre_match_limit_recursion  **********
++ **********  pcre2_match_limit_recursion  **********
+  **************************************************/
+-static const std::string pcre_match_limit_recursion = "pcre_match_limit_recursion";
+-static const ConvertMap pcre_match_limit_recursion_api =
++static const std::string pcre2_match_limit_recursion = "pcre_match_limit_recursion";
++static const ConvertMap pcre2_match_limit_recursion_api =
+ {
+-    pcre_match_limit_recursion,
+-    config_int_ctor<& pcre_match_limit_recursion, & detection>,
++    pcre2_match_limit_recursion,
++    config_int_ctor<& pcre2_match_limit_recursion, & detection>,
+ };
+-const ConvertMap* pcre_match_limit_recursion_map = &pcre_match_limit_recursion_api;
++const ConvertMap* pcre2_match_limit_recursion_map = &pcre2_match_limit_recursion_api;
+ /*************************************************
+  ******************  pkt_count   *****************
+--- a/tools/snort2lua/rule_states/CMakeLists.txt
++++ b/tools/snort2lua/rule_states/CMakeLists.txt
+@@ -12,7 +12,7 @@ add_library( rule_states OBJECT
+     rule_http_encode.cc
+     rule_isdataat.cc
+     rule_metadata.cc
+-    rule_pcre.cc
++    rule_pcre2.cc
+     rule_react.cc
+     rule_reference.cc
+     rule_replace.cc
+--- a/tools/snort2lua/rule_states/rule_api.cc
++++ b/tools/snort2lua/rule_states/rule_api.cc
+@@ -75,7 +75,7 @@ extern const ConvertMap* modbus_data_map
+ extern const ConvertMap* modbus_func_map;
+ extern const ConvertMap* modbus_unit_map;
+ extern const ConvertMap* msg_map;
+-extern const ConvertMap* pcre_map;
++extern const ConvertMap* pcre2_map;
+ extern const ConvertMap* pkt_data_map;
+ extern const ConvertMap* priority_map;
+ extern const ConvertMap* protected_content_map;
+@@ -159,7 +159,7 @@ const std::vector<const ConvertMap*> rul
+     modbus_func_map,
+     modbus_unit_map,
+     msg_map,
+-    pcre_map,
++    pcre2_map,
+     pkt_data_map,
+     priority_map,
+     protected_content_map,
+--- a/tools/snort2lua/rule_states/rule_pcre.cc
++++ /dev/null
+@@ -1,159 +0,0 @@
+-//--------------------------------------------------------------------------
+-// Copyright (C) 2014-2024 Cisco and/or its affiliates. All rights reserved.
+-//
+-// This program is free software; you can redistribute it and/or modify it
+-// under the terms of the GNU General Public License Version 2 as published
+-// by the Free Software Foundation.  You may not use, modify or distribute
+-// this program under any other version of the GNU General Public License.
+-//
+-// This program is distributed in the hope that it will be useful, but
+-// WITHOUT ANY WARRANTY; without even the implied warranty of
+-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+-// General Public License for more details.
+-//
+-// You should have received a copy of the GNU General Public License along
+-// with this program; if not, write to the Free Software Foundation, Inc.,
+-// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+-//--------------------------------------------------------------------------
+-// rule_pcre.cc author Josh Rosenbaum <jrosenba@cisco.com>
+-
+-#include <sstream>
+-#include <vector>
+-
+-#include "conversion_state.h"
+-#include "helpers/converter.h"
+-#include "helpers/s2l_util.h"
+-#include "rule_api.h"
+-
+-namespace rules
+-{
+-namespace
+-{
+-class Pcre : public ConversionState
+-{
+-public:
+-    Pcre(Converter& c) : ConversionState(c) { }
+-    bool convert(std::istringstream& data) override;
+-};
+-} // namespace
+-
+-bool Pcre::convert(std::istringstream& data_stream)
+-{
+-    bool sticky_buffer_set = false;
+-    std::string buffer = "pkt_data";
+-
+-    char delim = '/';
+-    std::string pcre_str = util::get_rule_option_args(data_stream);
+-    std::string pattern;
+-    std::string new_opts;
+-    std::string options;
+-
+-    if (pcre_str.front() == '!')
+-    {
+-        pattern += "!";
+-        pcre_str.erase(pcre_str.begin());
+-    }
+-
+-    if (pcre_str.front() != '"' || pcre_str.back() != '"')
+-    {
+-        rule_api.bad_rule(data_stream, "pattern must be enclosed in \"");
+-        return set_next_rule_state(data_stream);
+-    }
+-
+-    pcre_str.erase(pcre_str.begin());
+-    pattern += '"';
+-
+-    if (pcre_str.front() == 'm')
+-    {
+-        pcre_str.erase(pcre_str.begin());
+-        pattern += 'm';
+-        delim = pcre_str.front();
+-    }
+-
+-    const std::size_t pattern_end = pcre_str.rfind(delim);
+-    if ((pcre_str.front() != delim) || (pattern_end == 0))
+-    {
+-        std::string tmp = "Regex must be enclosed in delim '";
+-        tmp.append(delim, 1);
+-        rule_api.bad_rule(data_stream, tmp + "'");
+-        return set_next_rule_state(data_stream);
+-    }
+-
+-    pattern += pcre_str.substr(0, pattern_end + 1);
+-    options = pcre_str.substr(pattern_end + 1, std::string::npos);
+-    new_opts = "";
+-
+-    for (char c : options )
+-    {
+-        std::string sticky_buffer = std::string(); // empty string
+-
+-        switch (c)
+-        {
+-        case 'B': sticky_buffer = "raw_data"; break;
+-        case 'U': sticky_buffer = "http_uri"; break;
+-        case 'P': sticky_buffer = "pcre_P_option_body"; break;
+-        case 'H': sticky_buffer = "pcre_H_option_header"; break;
+-        case 'M': sticky_buffer = "http_method"; break;
+-        case 'C': sticky_buffer = "http_cookie"; break;
+-        case 'I': sticky_buffer = "http_raw_uri"; break;
+-        case 'D': sticky_buffer = "http_raw_header"; break;
+-        case 'K': sticky_buffer = "http_raw_cookie"; break;
+-        case 'S': sticky_buffer = "http_stat_code"; break;
+-        case 'Y': sticky_buffer = "http_stat_msg"; break;
+-        case 'i':
+-        case 's':
+-        case 'm':
+-        case 'x':
+-        case 'A':
+-        case 'E':
+-        case 'G':
+-        case 'O':
+-        case 'R':
+-        case '"':     // end of reg_ex
+-            new_opts += c;
+-            break;
+-        default:
+-        {
+-            std::string dlt_opt = "unknown option - '";
+-            dlt_opt.append(1, c);
+-            dlt_opt += "'";
+-            rule_api.bad_rule(data_stream, dlt_opt);
+-            break;
+-        }
+-        }
+-
+-        if (!sticky_buffer.empty())
+-        {
+-            buffer = sticky_buffer;
+-
+-            if (sticky_buffer_set)
+-                rule_api.bad_rule(data_stream,
+-                    "Two sticky buffers set for this regular expression!");
+-            else
+-                sticky_buffer_set = true;
+-        }
+-    }
+-
+-    rule_api.add_option("pcre", pattern + new_opts);
+-
+-    rule_api.set_curr_options_buffer(buffer);
+-
+-    return set_next_rule_state(data_stream);
+-}
+-
+-/**************************
+- *******  A P I ***********
+- **************************/
+-
+-static ConversionState* ctor(Converter& c)
+-{ return new Pcre(c); }
+-
+-static const ConvertMap pcre_api =
+-{
+-    "pcre",
+-    ctor,
+-};
+-
+-const ConvertMap* pcre_map = &pcre_api;
+-} // namespace rules
+-
+--- /dev/null
++++ b/tools/snort2lua/rule_states/rule_pcre2.cc
+@@ -0,0 +1,159 @@
++//--------------------------------------------------------------------------
++// Copyright (C) 2014-2024 Cisco and/or its affiliates. All rights reserved.
++//
++// This program is free software; you can redistribute it and/or modify it
++// under the terms of the GNU General Public License Version 2 as published
++// by the Free Software Foundation.  You may not use, modify or distribute
++// this program under any other version of the GNU General Public License.
++//
++// This program is distributed in the hope that it will be useful, but
++// WITHOUT ANY WARRANTY; without even the implied warranty of
++// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++// General Public License for more details.
++//
++// You should have received a copy of the GNU General Public License along
++// with this program; if not, write to the Free Software Foundation, Inc.,
++// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
++//--------------------------------------------------------------------------
++// rule_pcre2.cc author Josh Rosenbaum <jrosenba@cisco.com>
++
++#include <sstream>
++#include <vector>
++
++#include "conversion_state.h"
++#include "helpers/converter.h"
++#include "helpers/s2l_util.h"
++#include "rule_api.h"
++
++namespace rules
++{
++namespace
++{
++class Pcre2 : public ConversionState
++{
++public:
++    Pcre2(Converter& c) : ConversionState(c) { }
++    bool convert(std::istringstream& data) override;
++};
++} // namespace
++
++bool Pcre2::convert(std::istringstream& data_stream)
++{
++    bool sticky_buffer_set = false;
++    std::string buffer = "pkt_data";
++
++    char delim = '/';
++    std::string pcre2_str = util::get_rule_option_args(data_stream);
++    std::string pattern;
++    std::string new_opts;
++    std::string options;
++
++    if (pcre2_str.front() == '!')
++    {
++        pattern += "!";
++        pcre2_str.erase(pcre2_str.begin());
++    }
++
++    if (pcre2_str.front() != '"' || pcre2_str.back() != '"')
++    {
++        rule_api.bad_rule(data_stream, "pattern must be enclosed in \"");
++        return set_next_rule_state(data_stream);
++    }
++
++    pcre2_str.erase(pcre2_str.begin());
++    pattern += '"';
++
++    if (pcre2_str.front() == 'm')
++    {
++        pcre2_str.erase(pcre2_str.begin());
++        pattern += 'm';
++        delim = pcre2_str.front();
++    }
++
++    const std::size_t pattern_end = pcre2_str.rfind(delim);
++    if ((pcre2_str.front() != delim) || (pattern_end == 0))
++    {
++        std::string tmp = "Regex must be enclosed in delim '";
++        tmp.append(delim, 1);
++        rule_api.bad_rule(data_stream, tmp + "'");
++        return set_next_rule_state(data_stream);
++    }
++
++    pattern += pcre2_str.substr(0, pattern_end + 1);
++    options = pcre2_str.substr(pattern_end + 1, std::string::npos);
++    new_opts = "";
++
++    for (char c : options )
++    {
++        std::string sticky_buffer = std::string(); // empty string
++
++        switch (c)
++        {
++        case 'B': sticky_buffer = "raw_data"; break;
++        case 'U': sticky_buffer = "http_uri"; break;
++        case 'P': sticky_buffer = "pcre_P_option_body"; break;
++        case 'H': sticky_buffer = "pcre_H_option_header"; break;
++        case 'M': sticky_buffer = "http_method"; break;
++        case 'C': sticky_buffer = "http_cookie"; break;
++        case 'I': sticky_buffer = "http_raw_uri"; break;
++        case 'D': sticky_buffer = "http_raw_header"; break;
++        case 'K': sticky_buffer = "http_raw_cookie"; break;
++        case 'S': sticky_buffer = "http_stat_code"; break;
++        case 'Y': sticky_buffer = "http_stat_msg"; break;
++        case 'i':
++        case 's':
++        case 'm':
++        case 'x':
++        case 'A':
++        case 'E':
++        case 'G':
++        case 'O':
++        case 'R':
++        case '"':     // end of reg_ex
++            new_opts += c;
++            break;
++        default:
++        {
++            std::string dlt_opt = "unknown option - '";
++            dlt_opt.append(1, c);
++            dlt_opt += "'";
++            rule_api.bad_rule(data_stream, dlt_opt);
++            break;
++        }
++        }
++
++        if (!sticky_buffer.empty())
++        {
++            buffer = sticky_buffer;
++
++            if (sticky_buffer_set)
++                rule_api.bad_rule(data_stream,
++                    "Two sticky buffers set for this regular expression!");
++            else
++                sticky_buffer_set = true;
++        }
++    }
++
++    rule_api.add_option("pcre", pattern + new_opts);
++
++    rule_api.set_curr_options_buffer(buffer);
++
++    return set_next_rule_state(data_stream);
++}
++
++/**************************
++ *******  A P I ***********
++ **************************/
++
++static ConversionState* ctor(Converter& c)
++{ return new Pcre2(c); }
++
++static const ConvertMap pcre2_api =
++{
++    "pcre2",
++    ctor,
++};
++
++const ConvertMap* pcre2_map = &pcre2_api;
++} // namespace rules
++
+--- a/tools/snort2lua/rule_states/rule_sd_pattern.cc
++++ b/tools/snort2lua/rule_states/rule_sd_pattern.cc
+@@ -41,7 +41,7 @@ private:
+ std::string SDPattern::convert_pattern(const std::string& pattern)
+ {
+-    const std::string unused_pcre_tokens("()[].+*^$|");
++    const std::string unused_pcre2_tokens("()[].+*^$|");
+     std::string s3_pattern;
+@@ -100,7 +100,7 @@ std::string SDPattern::convert_pattern(c
+             break;
+         default:
+-            if (unused_pcre_tokens.find(sym) != std::string::npos)
++            if (unused_pcre2_tokens.find(sym) != std::string::npos)
+                 s3_pattern.push_back('\\');
+             s3_pattern.push_back(sym);
+             break;