<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">From e3e2c41e8c46a13bad18dd40fd9e3540020dd5eb Mon Sep 17 00:00:00 2001
From: Victor Seva &lt;linuxmaniac@torreviejawireless.org&gt;
Date: Mon, 21 Aug 2023 13:30:36 +0200
Subject: [PATCH] lcr: pcre2 migration

---
 src/modules/lcr/Makefile  |  12 +--
 src/modules/lcr/hash.c    |  24 ++---
 src/modules/lcr/hash.h    |  10 +-
 src/modules/lcr/lcr_mod.c | 187 +++++++++++++++++++++++++-------------
 src/modules/lcr/lcr_mod.h |   8 +-
 5 files changed, 150 insertions(+), 91 deletions(-)

--- a/src/modules/lcr/Makefile
+++ b/src/modules/lcr/Makefile
@@ -9,20 +9,15 @@ auto_gen=
 NAME=lcr.so
 
 ifeq ($(CROSS_COMPILE),)
-PCRE_BUILDER = $(shell \
-	if pkg-config --exists libcre; then \
-		echo 'pkg-config libpcre'; \
-	else \
-		which pcre-config; \
-	fi)
+PCRE_BUILDER = $(shell command -v pcre2-config)
 endif
 
 ifeq ($(PCRE_BUILDER),)
 	PCREDEFS=-I$(LOCALBASE)/include
-	PCRELIBS=-L$(LOCALBASE)/lib -lpcre
+	PCRELIBS=-L$(LOCALBASE)/lib -lpcre2-8
 else
 	PCREDEFS = $(shell $(PCRE_BUILDER) --cflags)
-	PCRELIBS = $(shell $(PCRE_BUILDER) --libs)
+	PCRELIBS = $(shell $(PCRE_BUILDER) --libs8)
 endif
 
 DEFS+=$(PCREDEFS)
@@ -31,4 +26,3 @@ LIBS+=$(PCRELIBS)
 SERLIBPATH=../../lib
 SER_LIBS+=$(SERLIBPATH)/srdb1/srdb1
 include ../../Makefile.modules
-
--- a/src/modules/lcr/hash.c
+++ b/src/modules/lcr/hash.c
@@ -15,8 +15,8 @@
  * 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 
+ * 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
  */
 
@@ -36,10 +36,10 @@
 /* Add lcr entry into hash table */
 int rule_hash_table_insert(struct rule_info **hash_table, unsigned int lcr_id,
 		unsigned int rule_id, unsigned short prefix_len, char *prefix,
-		unsigned short from_uri_len, char *from_uri, pcre *from_uri_re,
+		unsigned short from_uri_len, char *from_uri, pcre2_code *from_uri_re,
 		unsigned short mt_tvalue_len, char *mt_tvalue,
-		unsigned short request_uri_len, char *request_uri, pcre *request_uri_re,
-		unsigned short stopper)
+		unsigned short request_uri_len, char *request_uri,
+		pcre2_code *request_uri_re, unsigned short stopper)
 {
 	struct rule_info *rule;
 	str prefix_str;
@@ -50,9 +50,9 @@ int rule_hash_table_insert(struct rule_i
 	if(rule == NULL) {
 		SHM_MEM_ERROR_FMT("for rule hash table entry\n");
 		if(from_uri_re)
-			shm_free(from_uri_re);
+			pcre2_code_free(from_uri_re);
 		if(request_uri_re)
-			shm_free(request_uri_re);
+			pcre2_code_free(request_uri_re);
 		return 0;
 	}
 	memset(rule, 0, sizeof(struct rule_info));
@@ -99,9 +99,9 @@ int rule_hash_table_insert(struct rule_i
 	if(rid == NULL) {
 		PKG_MEM_ERROR_FMT("for rule_id hash table entry\n");
 		if(from_uri_re)
-			shm_free(from_uri_re);
+			pcre2_code_free(from_uri_re);
 		if(request_uri_re)
-			shm_free(request_uri_re);
+			pcre2_code_free(request_uri_re);
 		shm_free(rule);
 		return 0;
 	}
@@ -180,7 +180,7 @@ int rule_hash_table_insert_target(struct
 }
 
 
-/* 
+/*
  * Return pointer to lcr hash table entry to which given prefix hashes to.
  */
 struct rule_info *rule_hash_table_lookup(
@@ -209,10 +209,10 @@ void rule_hash_table_contents_free(struc
 		r = hash_table[i];
 		while(r) {
 			if(r-&gt;from_uri_re) {
-				shm_free(r-&gt;from_uri_re);
+				pcre2_code_free(r-&gt;from_uri_re);
 			}
 			if(r-&gt;request_uri_re)
-				shm_free(r-&gt;request_uri_re);
+				pcre2_code_free(r-&gt;request_uri_re);
 			t = r-&gt;targets;
 			while(t) {
 				next_t = t-&gt;next;
--- a/src/modules/lcr/hash.h
+++ b/src/modules/lcr/hash.h
@@ -15,8 +15,8 @@
  * 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 
+ * 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
  */
 
@@ -34,10 +34,10 @@
 
 int rule_hash_table_insert(struct rule_info **hash_table, unsigned int lcr_id,
 		unsigned int rule_id, unsigned short prefix_len, char *prefix,
-		unsigned short from_uri_len, char *from_uri, pcre *from_uri_re,
+		unsigned short from_uri_len, char *from_uri, pcre2_code *from_uri_re,
 		unsigned short mt_tvalue_len, char *mt_tvalue,
-		unsigned short request_uri_len, char *request_uri, pcre *request_uri_re,
-		unsigned short stopper);
+		unsigned short request_uri_len, char *request_uri,
+		pcre2_code *request_uri_re, unsigned short stopper);
 
 int rule_hash_table_insert_target(struct rule_info **hash_table,
 		struct gw_info *gws, unsigned int rule_id, unsigned int gw_id,
--- a/src/modules/lcr/lcr_mod.c
+++ b/src/modules/lcr/lcr_mod.c
@@ -16,8 +16,8 @@
  * 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 
+ * 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
  *
  */
@@ -43,7 +43,8 @@
 #include &lt;stdlib.h&gt;
 #include &lt;string.h&gt;
 #include &lt;arpa/inet.h&gt;
-#include &lt;pcre.h&gt;
+#define PCRE2_CODE_UNIT_WIDTH 8
+#include &lt;pcre2.h&gt;
 #include "../../core/locking.h"
 #include "../../core/sr_module.h"
 #include "../../core/dprint.h"
@@ -204,6 +205,9 @@ static unsigned int priority_ordering_pa
 /* mtree tree name */
 str mtree_param = {"lcr", 3};
 
+static pcre2_general_context *lcr_gctx = NULL;
+static pcre2_compile_context *lcr_ctx = NULL;
+
 /*
  * Other module types and variables
  */
@@ -364,7 +368,7 @@ static param_export_t params[] = {
  * Module interface
  */
 struct module_exports exports = {
-	"lcr", 
+	"lcr",
 	DEFAULT_DLFLAGS, /* dlopen flags */
 	cmds,      	 /* Exported functions */
 	params,    	 /* Exported parameters */
@@ -422,6 +426,16 @@ static void lcr_db_close(void)
 	}
 }
 
+static void *pcre2_malloc(size_t size, void *ext)
+{
+	return shm_malloc(size);
+}
+
+static void pcre2_free(void *ptr, void *ext)
+{
+	shm_free(ptr);
+	ptr = NULL;
+}
 
 /*
  * Module initialization function that is called before the main process forks
@@ -703,7 +717,15 @@ static int mod_init(void)
 	lcr_db_close();
 
 	/* rule shared memory */
-
+	if((lcr_gctx = pcre2_general_context_create(pcre2_malloc, pcre2_free, NULL))
+			== NULL) {
+		LM_ERR("pcre2 general context creation failed\n");
+		goto err;
+	}
+	if((lcr_ctx = pcre2_compile_context_create(lcr_gctx)) == NULL) {
+		LM_ERR("pcre2 compile context creation failed\n");
+		goto err;
+	}
 	/* rule hash table pointer table */
 	/* pointer at index 0 points to temp rule hash table */
 	rule_pt = (struct rule_info ***)shm_malloc(
@@ -779,6 +801,12 @@ dberror:
 	lcr_db_close();
 
 err:
+	if(lcr_ctx) {
+		pcre2_compile_context_free(lcr_ctx);
+	}
+	if(lcr_gctx) {
+		pcre2_general_context_free(lcr_gctx);
+	}
 	free_shared_memory();
 	return -1;
 }
@@ -794,7 +822,12 @@ static int child_init(int rank)
 static void destroy(void)
 {
 	lcr_db_close();
-
+	if(lcr_ctx) {
+		pcre2_compile_context_free(lcr_ctx);
+	}
+	if(lcr_gctx) {
+		pcre2_general_context_free(lcr_gctx);
+	}
 	free_shared_memory();
 }
 
@@ -875,33 +908,32 @@ static int comp_matched(const void *m1,
 
 
 /* Compile pattern into shared memory and return pointer to it. */
-static pcre *reg_ex_comp(const char *pattern)
+static pcre2_code *reg_ex_comp(const char *pattern)
 {
-	pcre *re, *result;
-	const char *error;
-	int rc, err_offset;
-	size_t size;
-
-	re = pcre_compile(pattern, 0, &amp;error, &amp;err_offset, NULL);
-	if(re == NULL) {
-		LM_ERR("pcre compilation of '%s' failed at offset %d: %s\n", pattern,
-				err_offset, error);
-		return (pcre *)0;
-	}
-	rc = pcre_fullinfo(re, NULL, PCRE_INFO_SIZE, &amp;size);
-	if(rc != 0) {
-		LM_ERR("pcre_fullinfo on compiled pattern '%s' yielded error: %d\n",
-				pattern, rc);
-		return (pcre *)0;
-	}
-	result = (pcre *)shm_malloc(size);
+	pcre2_code *result;
+	int pcre_error_num = 0;
+	char pcre_error[128];
+	size_t pcre_erroffset;
+
+	result = pcre2_compile((PCRE2_SPTR)pattern, PCRE2_ZERO_TERMINATED, 0,
+			&amp;pcre_error_num, &amp;pcre_erroffset, lcr_ctx);
 	if(result == NULL) {
-		pcre_free(re);
-		SHM_MEM_ERROR_FMT("for compiled PCRE pattern\n");
-		return (pcre *)0;
+		switch(pcre2_get_error_message(
+				pcre_error_num, (PCRE2_UCHAR *)pcre_error, 128)) {
+			case PCRE2_ERROR_NOMEMORY:
+				snprintf(pcre_error, 128,
+						"unknown error[%d]: pcre2 error buffer too small",
+						pcre_error_num);
+				break;
+			case PCRE2_ERROR_BADDATA:
+				snprintf(pcre_error, 128, "unknown pcre2 error[%d]",
+						pcre_error_num);
+				break;
+		}
+		LM_ERR("pcre compilation of '%s' failed at offset %zu: %s\n", pattern,
+				pcre_erroffset, pcre_error);
+		return NULL;
 	}
-	memcpy(result, re, size);
-	pcre_free(re);
 	return result;
 }
 
@@ -950,7 +982,7 @@ static struct gw_info *find_gateway_by_i
 	return NULL;
 }
 
-/* 
+/*
  * Insert gw info into index i or gws table
  */
 static int insert_gw(struct gw_info *gws, unsigned int i, unsigned int gw_id,
@@ -1024,7 +1056,7 @@ static int insert_gw(struct gw_info *gws
 
 
 /*
- * Insert prefix_len into list pointed by last rule hash table entry 
+ * Insert prefix_len into list pointed by last rule hash table entry
  * if not there already. Keep list in decending prefix_len order.
  */
 static int prefix_len_insert(
@@ -1414,7 +1446,7 @@ int reload_tables()
 	db_key_t gw_cols[13];
 	db_key_t rule_cols[7];
 	db_key_t target_cols[4];
-	pcre *from_uri_re, *request_uri_re;
+	pcre2_code *from_uri_re, *request_uri_re;
 	struct gw_info *gws, *gw_pt_tmp;
 	struct rule_info **rules, **rule_pt_tmp;
 
@@ -2129,11 +2161,12 @@ void add_gws_into_avps(struct gw_info *g
 int load_gws_dummy(int lcr_id, str *ruri_user, str *from_uri, str *request_uri,
 		unsigned int *gw_indexes)
 {
-	int i, j;
+	int i, j, rc;
 	unsigned int gw_index, now, dex;
 	struct rule_info **rules, *rule, *pl;
 	struct gw_info *gws;
 	struct target *t;
+	pcre2_match_data *pcre_md = NULL;
 	struct matched_gw_info matched_gws[MAX_NO_OF_GWS + 1];
 	struct sip_uri furi;
 	struct usr_avp *avp;
@@ -2178,12 +2211,18 @@ int load_gws_dummy(int lcr_id, str *ruri
 					|| strncmp(rule-&gt;prefix, ruri_user-&gt;s, pl-&gt;prefix_len))
 				goto next;
 
-			if((rule-&gt;from_uri_len != 0)
-					&amp;&amp; (pcre_exec(rule-&gt;from_uri_re, NULL, from_uri-&gt;s,
-								from_uri-&gt;len, 0, 0, NULL, 0)
-							&lt; 0))
-				goto next;
-
+			if(rule-&gt;from_uri_len != 0) {
+				pcre_md = pcre2_match_data_create_from_pattern(
+						rule-&gt;from_uri_re, NULL);
+				rc = pcre2_match(rule-&gt;from_uri_re, (PCRE2_SPTR)from_uri-&gt;s,
+						(PCRE2_SIZE)from_uri-&gt;len, 0, 0, pcre_md, NULL);
+				if(pcre_md) {
+					pcre2_match_data_free(pcre_md);
+					pcre_md = NULL;
+				}
+				if(rc &lt; 0)
+					goto next;
+			}
 			if((from_uri-&gt;len &gt; 0) &amp;&amp; (rule-&gt;mt_tvalue_len &gt; 0)) {
 				if(mtree_api.mt_match(&amp;msg, &amp;mtree_param, &amp;(furi.user), 2)
 						== -1) {
@@ -2216,9 +2255,16 @@ int load_gws_dummy(int lcr_id, str *ruri
 						   "param has not been given.\n");
 					return -1;
 				}
-				if(pcre_exec(rule-&gt;request_uri_re, NULL, request_uri-&gt;s,
-						   request_uri-&gt;len, 0, 0, NULL, 0)
-						&lt; 0)
+				pcre_md = pcre2_match_data_create_from_pattern(
+						rule-&gt;request_uri_re, NULL);
+				rc = pcre2_match(rule-&gt;request_uri_re,
+						(PCRE2_SPTR)request_uri-&gt;s,
+						(PCRE2_SIZE)request_uri-&gt;len, 0, 0, pcre_md, NULL);
+				if(pcre_md) {
+					pcre2_match_data_free(pcre_md);
+					pcre_md = NULL;
+				}
+				if(rc &lt; 0)
 					goto next;
 			}
 
@@ -2282,9 +2328,10 @@ static int ki_load_gws_furi(
 		sip_msg_t *_m, int lcr_id, str *ruri_user, str *from_uri)
 {
 	str *request_uri;
-	int i, j;
+	int i, j, rc;
 	unsigned int gw_index, now, dex;
 	int_str val;
+	pcre2_match_data *pcre_md = NULL;
 	struct matched_gw_info matched_gws[MAX_NO_OF_GWS + 1];
 	struct rule_info **rules, *rule, *pl;
 	struct gw_info *gws;
@@ -2343,14 +2390,22 @@ static int ki_load_gws_furi(
 				goto next;
 
 			/* Match from uri */
-			if((rule-&gt;from_uri_len != 0)
-					&amp;&amp; (pcre_exec(rule-&gt;from_uri_re, NULL, from_uri-&gt;s,
-								from_uri-&gt;len, 0, 0, NULL, 0)
-							&lt; 0)) {
-				LM_DBG("from uri &lt;%.*s&gt; did not match to from regex &lt;%.*s&gt;\n",
-						from_uri-&gt;len, from_uri-&gt;s, rule-&gt;from_uri_len,
-						rule-&gt;from_uri);
-				goto next;
+			if(rule-&gt;from_uri_len != 0) {
+				pcre_md = pcre2_match_data_create_from_pattern(
+						rule-&gt;from_uri_re, NULL);
+				rc = pcre2_match(rule-&gt;from_uri_re, (PCRE2_SPTR)from_uri-&gt;s,
+						(PCRE2_SIZE)from_uri-&gt;len, 0, 0, pcre_md, NULL);
+				if(pcre_md) {
+					pcre2_match_data_free(pcre_md);
+					pcre_md = NULL;
+				}
+				if(rc &lt; 0) {
+					LM_DBG("from uri &lt;%.*s&gt; did not match to from regex "
+						   "&lt;%.*s&gt;\n",
+							from_uri-&gt;len, from_uri-&gt;s, rule-&gt;from_uri_len,
+							rule-&gt;from_uri);
+					goto next;
+				}
 			}
 
 			/* Match from uri user */
@@ -2379,15 +2434,23 @@ static int ki_load_gws_furi(
 			}
 
 			/* Match request uri */
-			if((rule-&gt;request_uri_len != 0)
-					&amp;&amp; (pcre_exec(rule-&gt;request_uri_re, NULL, request_uri-&gt;s,
-								request_uri-&gt;len, 0, 0, NULL, 0)
-							&lt; 0)) {
-				LM_DBG("request uri &lt;%.*s&gt; did not match to request regex "
-					   "&lt;%.*s&gt;\n",
-						request_uri-&gt;len, request_uri-&gt;s, rule-&gt;request_uri_len,
-						rule-&gt;request_uri);
-				goto next;
+			if(rule-&gt;request_uri_len != 0) {
+				pcre_md = pcre2_match_data_create_from_pattern(
+						rule-&gt;request_uri_re, NULL);
+				rc = pcre2_match(rule-&gt;request_uri_re,
+						(PCRE2_SPTR)request_uri-&gt;s,
+						(PCRE2_SIZE)request_uri-&gt;len, 0, 0, pcre_md, NULL);
+				if(pcre_md) {
+					pcre2_match_data_free(pcre_md);
+					pcre_md = NULL;
+				}
+				if(rc &lt; 0) {
+					LM_DBG("request uri &lt;%.*s&gt; did not match to request regex "
+						   "&lt;%.*s&gt;\n",
+							request_uri-&gt;len, request_uri-&gt;s,
+							rule-&gt;request_uri_len, rule-&gt;request_uri);
+					goto next;
+				}
 			}
 
 			/* Load gws associated with this rule */
@@ -3015,7 +3078,7 @@ static int ki_next_gw(sip_msg_t *_m)
 }
 
 /**
- * 
+ *
  */
 static int next_gw(struct sip_msg *_m, char *_s1, char *_s2)
 {
--- a/src/modules/lcr/lcr_mod.h
+++ b/src/modules/lcr/lcr_mod.h
@@ -2,6 +2,7 @@
  * Various lcr related constant, types, and external variables
  *
  * Copyright (C) 2005-2014 Juha Heinanen
+ * Copyright (C) 2023 Victor Seva
  *
  * This file is part of Kamailio, a free SIP server.
  *
@@ -33,7 +34,8 @@
 #define LCR_MOD_H
 
 #include &lt;stdio.h&gt;
-#include &lt;pcre.h&gt;
+#define PCRE2_CODE_UNIT_WIDTH 8
+#include &lt;pcre2.h&gt;
 #include "../../core/locking.h"
 #include "../../core/parser/parse_uri.h"
 #include "../../core/ip_addr.h"
@@ -60,10 +62,10 @@ struct rule_info
 	unsigned short from_uri_len;
 	char mt_tvalue[MAX_MT_TVALUE_LEN + 1];
 	unsigned short mt_tvalue_len;
-	pcre *from_uri_re;
+	pcre2_code *from_uri_re;
 	char request_uri[MAX_URI_LEN + 1];
 	unsigned short request_uri_len;
-	pcre *request_uri_re;
+	pcre2_code *request_uri_re;
 	unsigned short stopper;
 	unsigned int enabled;
 	struct target *targets;
</pre></body></html>