From a314f26e991fe5907820f29a8dfe0a80a48b48ba Mon Sep 17 00:00:00 2001 From: Christian Marangi Date: Tue, 7 Nov 2023 01:17:25 +0100 Subject: [PATCH] snort3: add patch and move to PCRE2 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 --- net/snort3/Makefile | 2 +- .../900-core-convert-project-to-PCRE2.patch | 2052 +++++++++++++++++ 2 files changed, 2053 insertions(+), 1 deletion(-) create mode 100644 net/snort3/patches/900-core-convert-project-to-PCRE2.patch diff --git a/net/snort3/Makefile b/net/snort3/Makefile index b9c85922b0..48ce85b456 100644 --- a/net/snort3/Makefile +++ b/net/snort3/Makefile @@ -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 index 0000000000..01199f87cd --- /dev/null +++ b/net/snort3/patches/900-core-convert-project-to-PCRE2.patch @@ -0,0 +1,2052 @@ +From a71cca137eb33f659354ce0ebda4951cb26485df Mon Sep 17 00:00:00 2001 +From: Christian Marangi +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 +--- + 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 ++#define PCRE2_CODE_UNIT_WIDTH 8 ++#include + + #include + +@@ -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 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& 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& 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 + #include + #include +-#include ++#define PCRE2_CODE_UNIT_WIDTH 8 ++#include + #include + #include + #include +@@ -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 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 +-#include ++#define PCRE2_CODE_UNIT_WIDTH 8 ++#include + #include + + #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::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 + #include + #include +-#include ++#define PCRE2_CODE_UNIT_WIDTH 8 ++#include + #include + #include + #include +@@ -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 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 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 +- +-#include +-#include +- +-#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 ++ ++#include ++#include ++ ++#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; -- 2.30.2