Merge pull request #840 from Ansuel/pcre-kamailio
[feed/telephony.git] / net / kamailio / patches / 011-dialplan-migrate-to-pcre2.patch
diff --git a/net/kamailio/patches/011-dialplan-migrate-to-pcre2.patch b/net/kamailio/patches/011-dialplan-migrate-to-pcre2.patch
new file mode 100644 (file)
index 0000000..23eede0
--- /dev/null
@@ -0,0 +1,456 @@
+From 374227b15ff7fbed8660beb93d52da15dcb4ba9e Mon Sep 17 00:00:00 2001
+From: Victor Seva <linuxmaniac@torreviejawireless.org>
+Date: Mon, 21 Aug 2023 12:27:43 +0200
+Subject: [PATCH] dialplan: migrate to pcre2
+
+---
+ src/modules/dialplan/Makefile   |  11 +---
+ src/modules/dialplan/dialplan.c |   5 ++
+ src/modules/dialplan/dialplan.h |  20 ++++---
+ src/modules/dialplan/dp_db.c    | 103 +++++++++++++++++++-------------
+ src/modules/dialplan/dp_repl.c  |  56 +++++++++++------
+ 5 files changed, 121 insertions(+), 74 deletions(-)
+
+--- a/src/modules/dialplan/Makefile
++++ b/src/modules/dialplan/Makefile
+@@ -6,20 +6,15 @@ auto_gen=
+ NAME=dialplan.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)
+ LIBS=$(PCRELIBS)
+--- a/src/modules/dialplan/dialplan.c
++++ b/src/modules/dialplan/dialplan.c
+@@ -5,6 +5,8 @@
+  *
+  * Copyright (C)  2014 Olle E. Johansson, Edvina AB
+  *
++ * Copyright (C)  2023 Victor Seva
++ *
+  * This file is part of Kamailio, a free SIP server.
+  *
+  * Kamailio is free software; you can redistribute it and/or modify
+@@ -79,6 +81,9 @@ static int ki_dp_translate_vars(
+ int dp_replace_fixup(void **param, int param_no);
+ int dp_replace_fixup_free(void **param, int param_no);
++pcre2_general_context *dpl_gctx = NULL;
++pcre2_compile_context *dpl_ctx = NULL;
++
+ str dp_attr_pvar_s = STR_NULL;
+ pv_spec_t *dp_attr_pvar = NULL;
+--- a/src/modules/dialplan/dialplan.h
++++ b/src/modules/dialplan/dialplan.h
+@@ -13,8 +13,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
+  *
+  */
+@@ -30,7 +30,8 @@
+ #ifndef _DP_DIALPLAN_H
+ #define _DP_DIALPLAN_H
+-#include <pcre.h>
++#define PCRE2_CODE_UNIT_WIDTH 8
++#include <pcre2.h>
+ #include "../../core/pvar.h"
+ #include "../../core/parser/msg_parser.h"
+@@ -43,6 +44,9 @@
+ #define DP_TFLAGS_PV_MATCH (1 << 0)
+ #define DP_TFLAGS_PV_SUBST (1 << 1)
++extern pcre2_general_context *dpl_gctx;
++extern pcre2_compile_context *dpl_ctx;
++
+ typedef struct dpl_node
+ {
+       int dpid;                                         /* dialplan id */
+@@ -52,8 +56,8 @@ typedef struct dpl_node
+       str match_exp;                            /* match-first string */
+       str subst_exp;                            /* match string with subtitution groupping */
+       str repl_exp;                             /* replacement expression string */
+-      pcre *match_comp;                         /* compiled matching expression */
+-      pcre *subst_comp;                         /* compiled substitution expression */
++      pcre2_code *match_comp;           /* compiled matching expression */
++      pcre2_code *subst_comp;           /* compiled substitution expression */
+       struct subst_expr *repl_comp; /* compiled replacement */
+       str attrs;                                        /* attributes string */
+       unsigned int tflags;              /* flags for type of values for matching */
+@@ -103,8 +107,8 @@ struct subst_expr *repl_exp_parse(str su
+ void repl_expr_free(struct subst_expr *se);
+ int dp_translate_helper(
+               sip_msg_t *msg, str *user_name, str *repl_user, dpl_id_p idp, str *);
+-int rule_translate(
+-              sip_msg_t *msg, str *instr, dpl_node_t *rule, pcre *subst_comp, str *);
++int rule_translate(sip_msg_t *msg, str *instr, dpl_node_t *rule,
++              pcre2_code *subst_comp, str *);
+-pcre *reg_ex_comp(const char *pattern, int *cap_cnt, int mtype);
++pcre2_code *reg_ex_comp(const char *pattern, int *cap_cnt, int mtype);
+ #endif
+--- a/src/modules/dialplan/dp_db.c
++++ b/src/modules/dialplan/dp_db.c
+@@ -196,11 +196,31 @@ void dp_disconnect_db(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;
++}
+ int init_data(void)
+ {
+       int *p;
++      if((dpl_gctx = pcre2_general_context_create(pcre2_malloc, pcre2_free, NULL))
++                      == NULL) {
++              LM_ERR("pcre2 general context creation failed\n");
++              return -1;
++      }
++      if((dpl_ctx = pcre2_compile_context_create(dpl_gctx)) == NULL) {
++              LM_ERR("pcre2 compile context creation failed\n");
++              return -1;
++      }
++
+       dp_rules_hash = (dpl_id_p *)shm_malloc(2 * sizeof(dpl_id_p));
+       if(!dp_rules_hash) {
+               LM_ERR("out of shm memory\n");
+@@ -227,6 +247,14 @@ int init_data(void)
+ void destroy_data(void)
+ {
++      if(dpl_ctx) {
++              pcre2_compile_context_free(dpl_ctx);
++      }
++
++      if(dpl_gctx) {
++              pcre2_general_context_free(dpl_gctx);
++      }
++
+       if(dp_rules_hash) {
+               destroy_hash(0);
+               destroy_hash(1);
+@@ -373,55 +401,50 @@ int dpl_str_to_shm(str src, str *dest, i
+ /* Compile pcre pattern
+- * if mtype==0 - return pointer to shm copy of result
+- * if mtype==1 - return pcre pointer that has to be pcre_free() */
+-pcre *reg_ex_comp(const char *pattern, int *cap_cnt, int mtype)
+-{
+-      pcre *re, *result;
+-      const char *error;
+-      int rc, err_offset;
+-      size_t size;
++ * if mtype==0 - return pointer using shm
++ * if mtype==1 - return pcre2_code pointer that has to be pcre2_code_free() */
++pcre2_code *reg_ex_comp(const char *pattern, int *cap_cnt, int mtype)
++{
++      pcre2_code *re;
++      int pcre_error_num = 0;
++      char pcre_error[128];
++      size_t pcre_erroffset;
++      int rc;
+-      re = pcre_compile(pattern, 0, &error, &err_offset, NULL);
++      re = pcre2_compile((PCRE2_SPTR)pattern, PCRE2_ZERO_TERMINATED, 0,
++                      &pcre_error_num, &pcre_erroffset, mtype == 0 ? dpl_ctx : 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, &size);
+-      if(rc != 0) {
+-              pcre_free(re);
+-              LM_ERR("pcre_fullinfo on compiled pattern '%s' yielded error: %d\n",
+-                              pattern, rc);
+-              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;
+       }
+-      rc = pcre_fullinfo(re, NULL, PCRE_INFO_CAPTURECOUNT, cap_cnt);
++      rc = pcre2_pattern_info(re, PCRE2_INFO_CAPTURECOUNT, cap_cnt);
+       if(rc != 0) {
+-              pcre_free(re);
++              pcre2_code_free(re);
+               LM_ERR("pcre_fullinfo on compiled pattern '%s' yielded error: %d\n",
+                               pattern, rc);
+-              return (pcre *)0;
+-      }
+-      if(mtype == 0) {
+-              result = (pcre *)shm_malloc(size);
+-              if(result == NULL) {
+-                      pcre_free(re);
+-                      LM_ERR("not enough shared memory for compiled PCRE pattern\n");
+-                      return (pcre *)0;
+-              }
+-              memcpy(result, re, size);
+-              pcre_free(re);
+-              return result;
+-      } else {
+-              return re;
++              return NULL;
+       }
++      return re;
+ }
+ /*compile the expressions, and if ok, build the rule */
+ dpl_node_t *build_rule(db_val_t *values)
+ {
+-      pcre *match_comp, *subst_comp;
++      pcre2_code *match_comp, *subst_comp;
+       struct subst_expr *repl_comp;
+       dpl_node_t *new_rule;
+       str match_exp, subst_exp, repl_exp, attrs;
+@@ -544,9 +567,9 @@ dpl_node_t *build_rule(db_val_t *values)
+ err:
+       if(match_comp)
+-              shm_free(match_comp);
++              pcre2_code_free(match_comp);
+       if(subst_comp)
+-              shm_free(subst_comp);
++              pcre2_code_free(subst_comp);
+       if(repl_comp)
+               repl_expr_free(repl_comp);
+       if(new_rule)
+@@ -692,10 +715,10 @@ void destroy_rule(dpl_node_t *rule)
+       LM_DBG("destroying rule with priority %i\n", rule->pr);
+       if(rule->match_comp)
+-              shm_free(rule->match_comp);
++              pcre2_code_free(rule->match_comp);
+       if(rule->subst_comp)
+-              shm_free(rule->subst_comp);
++              pcre2_code_free(rule->subst_comp);
+       /*destroy repl_exp*/
+       if(rule->repl_comp)
+--- a/src/modules/dialplan/dp_repl.c
++++ b/src/modules/dialplan/dp_repl.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
+  *
+  */
+@@ -38,7 +38,7 @@
+ typedef struct dpl_dyn_pcre
+ {
+-      pcre *re;
++      pcre2_code *re;
+       int cnt;
+       str expr;
+@@ -186,9 +186,10 @@ int dpl_detect_avp_indx(const pv_elem_p
+       return 0;
+ }
+-pcre *dpl_dyn_pcre_comp(sip_msg_t *msg, str *expr, str *vexpr, int *cap_cnt)
++pcre2_code *dpl_dyn_pcre_comp(
++              sip_msg_t *msg, str *expr, str *vexpr, int *cap_cnt)
+ {
+-      pcre *re = NULL;
++      pcre2_code *re = NULL;
+       int ccnt = 0;
+       if(expr == NULL || expr->s == NULL || expr->len <= 0 || vexpr == NULL
+@@ -225,7 +226,7 @@ dpl_dyn_pcre_p dpl_dynamic_pcre_list(sip
+       dpl_dyn_pcre_p rt = NULL;
+       struct str_list *l = NULL;
+       struct str_list *t = NULL;
+-      pcre *re = NULL;
++      pcre2_code *re = NULL;
+       int cnt = 0;
+       str vexpr = STR_NULL;
+@@ -294,7 +295,7 @@ error:
+       while(re_list) {
+               rt = re_list->next;
+               if(re_list->re)
+-                      pcre_free(re_list->re);
++                      pcre2_code_free(re_list->re);
+               pkg_free(re_list);
+               re_list = rt;
+       }
+@@ -400,15 +401,16 @@ error:
+ #define MAX_PHONE_NB_DIGITS 127
+ static char dp_output_buf[MAX_PHONE_NB_DIGITS + 1];
+ int rule_translate(sip_msg_t *msg, str *instr, dpl_node_t *rule,
+-              pcre *subst_comp, str *result)
++              pcre2_code *subst_comp, str *result)
+ {
+       int repl_nb, offset, match_nb, rc, cap_cnt;
+       struct replace_with token;
+       struct subst_expr *repl_comp;
++      pcre2_match_data *pcre_md = NULL;
+       str match;
+       pv_value_t sv;
+       str *uri;
+-      int ovector[3 * (MAX_REPLACE_WITH + 1)];
++      PCRE2_SIZE *ovector = NULL;
+       char *p;
+       int size;
+@@ -424,7 +426,7 @@ int rule_translate(sip_msg_t *msg, str *
+       if(subst_comp) {
+               /*just in case something went wrong at load time*/
+-              rc = pcre_fullinfo(subst_comp, NULL, PCRE_INFO_CAPTURECOUNT, &cap_cnt);
++              rc = pcre2_pattern_info(subst_comp, PCRE2_INFO_CAPTURECOUNT, &cap_cnt);
+               if(rc != 0) {
+                       LM_ERR("pcre_fullinfo on compiled pattern yielded error: %d\n", rc);
+                       return -1;
+@@ -441,15 +443,19 @@ int rule_translate(sip_msg_t *msg, str *
+               }
+               /*search for the pattern from the compiled subst_exp*/
+-              if(pcre_exec(subst_comp, NULL, instr->s, instr->len, 0, 0, ovector,
+-                                 3 * (MAX_REPLACE_WITH + 1))
++              pcre_md = pcre2_match_data_create_from_pattern(subst_comp, NULL);
++              if(pcre2_match(subst_comp, (PCRE2_SPTR)instr->s, (PCRE2_SIZE)instr->len,
++                                 0, 0, pcre_md, NULL)
+                               <= 0) {
+                       LM_DBG("the string %.*s matched "
+                                  "the match_exp %.*s but not the subst_exp %.*s!\n",
+                                       instr->len, instr->s, rule->match_exp.len,
+                                       rule->match_exp.s, rule->subst_exp.len, rule->subst_exp.s);
++                      if(pcre_md)
++                              pcre2_match_data_free(pcre_md);
+                       return -1;
+               }
++              ovector = pcre2_get_ovector_pointer(pcre_md);
+       }
+       /*simply copy from the replacing string*/
+@@ -463,6 +469,8 @@ int rule_translate(sip_msg_t *msg, str *
+               memcpy(result->s, repl_comp->replacement.s, repl_comp->replacement.len);
+               result->len = repl_comp->replacement.len;
+               result->s[result->len] = '\0';
++              if(pcre_md)
++                      pcre2_match_data_free(pcre_md);
+               return 0;
+       }
+@@ -571,11 +579,15 @@ int rule_translate(sip_msg_t *msg, str *
+       }
+       result->s[result->len] = '\0';
++      if(pcre_md)
++              pcre2_match_data_free(pcre_md);
+       return 0;
+ error:
+       result->s = 0;
+       result->len = 0;
++      if(pcre_md)
++              pcre2_match_data_free(pcre_md);
+       return -1;
+ }
+@@ -584,6 +596,7 @@ static char dp_attrs_buf[DP_MAX_ATTRS_LE
+ int dp_translate_helper(
+               sip_msg_t *msg, str *input, str *output, dpl_id_p idp, str *attrs)
+ {
++      pcre2_match_data *pcre_md = NULL;
+       dpl_node_p rulep;
+       dpl_index_p indexp;
+       int user_len, rez;
+@@ -624,21 +637,28 @@ search_rule:
+                                       rez = -1;
+                                       do {
+                                               if(rez < 0) {
+-                                                      rez = pcre_exec(re_list->re, NULL, input->s,
+-                                                                      input->len, 0, 0, NULL, 0);
++                                                      pcre_md = pcre2_match_data_create_from_pattern(
++                                                                      re_list->re, NULL);
++                                                      rez = pcre2_match(re_list->re, (PCRE2_SPTR)input->s,
++                                                                      (PCRE2_SIZE)input->len, 0, 0, pcre_md,
++                                                                      NULL);
+                                                       LM_DBG("match check: [%.*s] %d\n",
+                                                                       re_list->expr.len, re_list->expr.s, rez);
+                                               } else
+                                                       LM_DBG("match check skipped: [%.*s] %d\n",
+                                                                       re_list->expr.len, re_list->expr.s, rez);
+                                               rt = re_list->next;
+-                                              pcre_free(re_list->re);
++                                              pcre2_match_data_free(pcre_md);
++                                              pcre2_code_free(re_list->re);
+                                               pkg_free(re_list);
+                                               re_list = rt;
+                                       } while(re_list);
+                               } else {
+-                                      rez = pcre_exec(rulep->match_comp, NULL, input->s,
+-                                                      input->len, 0, 0, NULL, 0);
++                                      pcre_md = pcre2_match_data_create_from_pattern(
++                                                      rulep->match_comp, NULL);
++                                      rez = pcre2_match(rulep->match_comp, (PCRE2_SPTR)input->s,
++                                                      (PCRE2_SIZE)input->len, 0, 0, pcre_md, 0);
++                                      pcre2_match_data_free(pcre_md);
+                               }
+                               break;
+@@ -728,7 +748,7 @@ repl:
+                               LM_DBG("subst check skipped: [%.*s] %d\n", re_list->expr.len,
+                                               re_list->expr.s, rez);
+                       rt = re_list->next;
+-                      pcre_free(re_list->re);
++                      pcre2_code_free(re_list->re);
+                       pkg_free(re_list);
+                       re_list = rt;
+               } while(re_list);