From cad0292a16fad1bd3157a5fa12260fb1da6fb381 Mon Sep 17 00:00:00 2001 From: Parantido Julius De Rica Date: Mon, 15 Feb 2016 16:22:18 +0100 Subject: [PATCH] MOD DIALPLAN v2.1.2: * Rules preload/translate alghoritm reworked: no more needs to have different buckets for REGEXP and BEST MATCH ones. All rules belong from same bucket. This way opens to many different scenarios and future development without afflicting performances (this rework will also fix issue #696 https://github.com/OpenSIPS/opensips/issues/696). * New "match_var" database column introduced. This column will override "src/dest" dp_translate parameter when filled. It respects same syntax (wrong syntax will cause rule to being discarded at preload time). * New "matched_pvar" AVP introduced. It will be filled with last matched rule id. AVP name can be set by modparam directive: modparam("dialplan", "matched_pvar", "$avp(dp_ruleid)") * New "continue_search" database column introduced. When filled to "1", rule matching will continue through other rules. ATTRS and MATCHED_PVAR will be updated as soon as rule matches (next rule match will works on PVAR updated values). After a dp_translate ATTRS and MATCHED_PVAR will be updated with last rule match values. All new features and code rework default behaviour respects old syntaxes. No database entries or opensips.cfg scripts needs to be changed on module upgrade. --- modules/dialplan/dialplan.c | 237 ++++++++++++++++++------------- modules/dialplan/dialplan.h | 22 +-- modules/dialplan/dp_db.c | 201 +++++++++++++++------------ modules/dialplan/dp_db.h | 23 ++-- modules/dialplan/dp_repl.c | 268 +++++++++++++++++++++++------------- 5 files changed, 455 insertions(+), 296 deletions(-) diff --git a/modules/dialplan/dialplan.c b/modules/dialplan/dialplan.c index 2b4bab66256..7cf58a9a8c7 100644 --- a/modules/dialplan/dialplan.c +++ b/modules/dialplan/dialplan.c @@ -22,7 +22,6 @@ * 2007-08-01 initial version (ancuta onofrei) */ - #include #include #include @@ -49,14 +48,15 @@ #define DEFAULT_PARTITION "default" #define PARAM_URL "db_url" #define PARAM_TABLE "table_name" +#define DEF_MATCH_RULE "$avp(mtcrl)" #define DP_CHAR_COLON ':' #define DP_CHAR_SLASH '/' #define DP_CHAR_EQUAL '=' #define DP_CHAR_SCOLON ';' #define DP_TYPE_URL 0 #define DP_TYPE_TABLE 1 -#define is_space(p) (*(p) == ' ' || *(p) == '\t' || \ - *(p) == '\r' || *(p) == '\n') +#define is_space(p) (*(p) == ' ' || *(p) == '\t' || *(p) == '\r' || *(p) == '\n') + static int mod_init(void); static int child_init(int rank); @@ -76,28 +76,34 @@ str default_dp_partition = {NULL, 0}; dp_param_p default_par2 = NULL; static str database_url = {NULL, 0}; +/* Matched Rule Id PVAR */ +int avp_name = -1; +str matched_rule_pvar; static param_export_t mod_params[]={ - { "partition", STR_PARAM|USE_FUNC_PARAM, - (void*)dp_set_partition}, - { "db_url", STR_PARAM, &default_dp_db_url.s}, - { "table_name", STR_PARAM, &default_dp_table.s }, - { "dpid_col", STR_PARAM, &dpid_column.s }, - { "pr_col", STR_PARAM, &pr_column.s }, - { "match_op_col", STR_PARAM, &match_op_column.s }, - { "match_exp_col", STR_PARAM, &match_exp_column.s }, - { "match_flags_col", STR_PARAM, &match_flags_column.s }, - { "subst_exp_col", STR_PARAM, &subst_exp_column.s }, - { "repl_exp_col", STR_PARAM, &repl_exp_column.s }, - { "attrs_col", STR_PARAM, &attrs_column.s }, - { "timerec_col", STR_PARAM, &timerec_column.s }, - { "disabled_col", STR_PARAM, &disabled_column.s}, - {0,0,0} + { "partition", STR_PARAM|USE_FUNC_PARAM, (void*)dp_set_partition}, + { "db_url", STR_PARAM, &default_dp_db_url.s}, + { "table_name", STR_PARAM, &default_dp_table.s }, + { "matched_pvar", STR_PARAM, &matched_rule_pvar.s }, + { "id_col", STR_PARAM, &id_column.s }, + { "dpid_col", STR_PARAM, &dpid_column.s }, + { "pr_col", STR_PARAM, &pr_column.s }, + { "match_op_col", STR_PARAM, &match_op_column.s }, + { "match_exp_col", STR_PARAM, &match_exp_column.s }, + { "match_flags_col", STR_PARAM, &match_flags_column.s }, + { "subst_exp_col", STR_PARAM, &subst_exp_column.s }, + { "repl_exp_col", STR_PARAM, &repl_exp_column.s }, + { "attrs_col", STR_PARAM, &attrs_column.s }, + { "timerec_col", STR_PARAM, &timerec_column.s }, + { "match_var_col", STR_PARAM, &match_var_column.s }, + { "continue_search_col", STR_PARAM, &continue_search_column.s }, + { "disabled_col", STR_PARAM, &disabled_column.s}, + {0, 0, 0} }; static mi_export_t mi_cmds[] = { - { "dp_reload", 0, mi_reload_rules, 0, 0, mi_child_init}, - { "dp_translate", 0, mi_translate, 0, 0, 0}, + { "dp_reload", 0, mi_reload_rules, 0, 0, mi_child_init}, + { "dp_translate", 0, mi_translate, 0, 0, 0}, { "dp_show_partition", 0, mi_show_partition, 0, 0, 0}, { 0, 0, 0, 0, 0, 0} }; @@ -125,21 +131,21 @@ static dep_export_t deps = { }, }; -struct module_exports exports= { - "dialplan", /* module's name */ - MOD_TYPE_DEFAULT,/* class of this module */ +struct module_exports exports = { + "dialplan", /* module's name */ + MOD_TYPE_DEFAULT, /* class of this module */ MODULE_VERSION, - DEFAULT_DLFLAGS, /* dlopen flags */ - &deps, /* OpenSIPS module dependencies */ - cmds, /* exported functions */ - 0, /* exported async functions */ - mod_params, /* param exports */ - 0, /* exported statistics */ + DEFAULT_DLFLAGS, /* dlopen flags */ + &deps, /* OpenSIPS module dependencies */ + cmds, /* exported functions */ + 0, /* exported async functions */ + mod_params, /* param exports */ + 0, /* exported statistics */ mi_cmds, /* exported MI functions */ - 0, /* exported pseudo-variables */ - 0, /* additional processes */ + 0, /* exported pseudo-variables */ + 0, /* additional processes */ mod_init, /* module initialization function */ - 0, /* reply processing function */ + 0, /* reply processing function */ mod_destroy, child_init /* per-child init function */ }; @@ -371,16 +377,19 @@ static int mod_init(void) init_db_url( default_dp_db_url , 0 /*can be null*/); - dpid_column.len = strlen(dpid_column.s); - pr_column.len = strlen(pr_column.s); - match_op_column.len = strlen(match_op_column.s); - match_exp_column.len = strlen(match_exp_column.s); - match_flags_column.len = strlen(match_flags_column.s); - subst_exp_column.len = strlen(subst_exp_column.s); - repl_exp_column.len = strlen(repl_exp_column.s); - attrs_column.len = strlen(attrs_column.s); - timerec_column.len = strlen(timerec_column.s); - disabled_column.len = strlen(disabled_column.s); + id_column.len = strlen(id_column.s); + dpid_column.len = strlen(dpid_column.s); + pr_column.len = strlen(pr_column.s); + match_op_column.len = strlen(match_op_column.s); + match_exp_column.len = strlen(match_exp_column.s); + match_flags_column.len = strlen(match_flags_column.s); + subst_exp_column.len = strlen(subst_exp_column.s); + repl_exp_column.len = strlen(repl_exp_column.s); + attrs_column.len = strlen(attrs_column.s); + timerec_column.len = strlen(timerec_column.s); + match_var_column.len = strlen(match_var_column.s); + continue_search_column.len = strlen(continue_search_column.s); + disabled_column.len = strlen(disabled_column.s); if (default_dp_db_url.s) { default_dp_db_url.len = strlen(default_dp_db_url.s); @@ -443,6 +452,20 @@ static int mod_init(void) } + /* Initialize matched_rule_pvar modparam structure */ + if(strlen(matched_rule_pvar.s) <= 0) { + + matched_rule_pvar.len = sizeof(DEF_MATCH_RULE) - 1; + matched_rule_pvar.s = pkg_malloc(matched_rule_pvar.len); + + if (!matched_rule_pvar.s) { + LM_ERR("No more pkg memory\n"); + return -1; + } + + memcpy(matched_rule_pvar.s, DEF_MATCH_RULE, matched_rule_pvar.len); + + } else matched_rule_pvar.len = strlen(matched_rule_pvar.s); default_par2 = (dp_param_p)shm_malloc(sizeof(dp_param_t)); if(default_par2 == NULL){ LM_ERR("no shm more memory\n"); @@ -592,9 +615,7 @@ static int dp_get_svalue(struct sip_msg * msg, pv_spec_t spec, str* val) return 0; } - -static int dp_update(struct sip_msg * msg, pv_spec_t * src, pv_spec_t * dest, - str * repl) +int dp_update(struct sip_msg * msg, pv_spec_t * src, pv_spec_t * dest, str * repl) { pv_value_t val; @@ -610,11 +631,10 @@ static int dp_update(struct sip_msg * msg, pv_spec_t * src, pv_spec_t * dest, return 0; } -static int dp_translate_f(struct sip_msg *msg, char *str1, char *str2, - char *attr_spec) +static int dp_translate_f(struct sip_msg *msg, char *str1, char *str2, char *attr_spec) { - int dpid; + int dpid, dbmatch; str input, output; dpl_id_p idp; dp_param_p id_par, repl_par; @@ -623,6 +643,8 @@ static int dp_translate_f(struct sip_msg *msg, char *str1, char *str2, pv_value_t pval; str partition_name; + unsigned short avp_type; + pv_spec_t avp_spec; if (!msg) return -1; @@ -682,21 +704,34 @@ static int dp_translate_f(struct sip_msg *msg, char *str1, char *str2, LM_DBG("Checking with dpid %i\n", idp->dp_id); attrs_par = attr_spec ? &attrs : NULL; - if (translate(msg, input, &output, idp, attrs_par) != 0) { - LM_DBG("could not translate %.*s " - "with dpid %i\n", input.len, input.s, idp->dp_id); - goto error; + + /* Retrieve Matched PVAR Reference */ + if (pv_parse_spec(&matched_rule_pvar, &avp_spec) == 0 || avp_spec.type != PVT_AVP) { + LM_ERR("malformed or non AVP %.*s AVP definition\n", matched_rule_pvar.len, matched_rule_pvar.s); + return -1; } - LM_DBG("input %.*s with dpid %i => output %.*s\n", - input.len, input.s, idp->dp_id, output.len, output.s); + /* Retrieve Matched PVAR id */ + if(pv_get_avp_name(0, &(avp_spec.pvp), &avp_name, &avp_type) != 0) { + LM_ERR("[%.*s]- invalid AVP definition\n", matched_rule_pvar.len, matched_rule_pvar.s); + return -1; + } - /* set the output */ - if (dp_update(msg, &repl_par->v.sp[0], &repl_par->v.sp[1], &output) != 0) { - LM_ERR("cannot set the output\n"); + /* Doing translate */ + if ((dbmatch = translate(msg, input, &output, idp, attrs_par, avp_name)) < 0) { + LM_DBG("could not translate %.*s with dpid %i\n", input.len, input.s, idp->dp_id); goto error; } + LM_DBG("input %.*s with dpid %i => output %.*s dbmatch = %i\n", input.len, input.s, idp->dp_id, output.len, output.s, dbmatch); + + /* Set the output PVAR if not already done on DB side output PVAR*/ + if (dbmatch == 0) + if (dp_update(msg, &repl_par->v.sp[0], &repl_par->v.sp[1], &output) != 0) { + LM_ERR("cannot set the output\n"); + goto error; + } + /* we are done reading -> unref the data */ lock_stop_read( connection->ref_lock ); @@ -774,6 +809,34 @@ static char *parse_dp_command(char * p, int len, str * partition_name) #undef is_space +/** + * This function will check for input pvar parameter validity + * @return dp_parm_p valued structure + */ +int check_input_param(dp_param_p dp_par, char* p) { + str lstr; + char *s = NULL; + + if(((s = strchr(p, '/')) == 0) || (*(s+1)=='\0')) return E_UNSPEC; + *s = '\0'; s++; + + lstr.s = p; lstr.len = strlen(p); + if(pv_parse_spec(&lstr, &dp_par->v.sp[0]) == NULL) return E_UNSPEC; + verify_par_type(dp_par->v.sp[0]); + + lstr.s = s; lstr.len = strlen(s); + if (pv_parse_spec(&lstr, &dp_par->v.sp[1]) == NULL) return E_UNSPEC; + verify_par_type(dp_par->v.sp[1]); + + if (dp_par->v.sp[1].setf == NULL) { + LM_ERR("the output PV is read-only!!\n"); + return E_CFG; + } + + dp_par->type = DP_VAL_SPEC; + + return 1; +} /* first param: DPID: type: INT, AVP, SVAR * second param: SRC/DST type: RURI, RURI_USERNAME, AVP, SVAR * default value for the second param: $ru.user/$ru.user @@ -782,7 +845,7 @@ static int dp_trans_fixup(void ** param, int param_no){ int dpid; dp_param_p dp_par= NULL; - char *p, *s = NULL; + char *p = NULL; str lstr, partition_name; dp_connection_list_t *list = NULL; @@ -873,27 +936,8 @@ static int dp_trans_fixup(void ** param, int param_no){ break; case 2: - if( ((s = strchr(p, '/')) == 0) ||( *(s+1)=='\0')) - goto error; - *s = '\0'; s++; - - lstr.s = p; lstr.len = strlen(p); - if(pv_parse_spec( &lstr, &dp_par->v.sp[0])==NULL) - goto error; - - verify_par_type(dp_par->v.sp[0]); - - lstr.s = s; lstr.len = strlen(s); - if (pv_parse_spec( &lstr, &dp_par->v.sp[1] )==NULL) - goto error; - - verify_par_type(dp_par->v.sp[1]); - if (dp_par->v.sp[1].setf==NULL) { - LM_ERR("the output PV is read-only!!\n"); - return E_CFG; - } - - dp_par->type = DP_VAL_SPEC; + /* Validate and collect SRC/DST (input/output) PVs */ + if(check_input_param(dp_par, p) <= 0) goto error; break; case 3: @@ -1026,31 +1070,32 @@ static struct mi_root * mi_reload_rules(struct mi_root *cmd_tree, void *param) struct mi_root *rpl_tree = NULL; dp_connection_list_t *el; + LM_INFO("dp_reload MI command received!\n"); if (cmd_tree) node = cmd_tree->node.kids; if (node == NULL) { - /* Reload rules from all partitions */ - if(dp_load_all_db() != 0){ - LM_ERR("failed to reload database\n"); - return 0; - } + /* Reload rules from all partitions */ + if(dp_load_all_db() != 0){ + LM_ERR("failed to reload database\n"); + return 0; + } } else if (node->value.s == NULL || node->value.len == 0) { - return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN); + return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN); } else { - el = dp_get_connection(&node->value); - if (!el) - return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN); - /* Reload rules from specified partition */ - LM_DBG("Reloading rules from table %.*s\n", node->value.len, node->value.s); - if(dp_load_db(el) != 0){ - LM_ERR("failed to reload database data\n"); - return 0; - } + el = dp_get_connection(&node->value); + if (!el) + return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN); + /* Reload rules from specified partition */ + LM_DBG("Reloading rules from table %.*s\n", node->value.len, node->value.s); + if(dp_load_db(el) != 0){ + LM_ERR("failed to reload database data\n"); + return 0; + } } - rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN); + rpl_tree = init_mi_tree(200, MI_OK_S, MI_OK_LEN); if (rpl_tree==0) return 0; @@ -1135,7 +1180,7 @@ static struct mi_root * mi_translate(struct mi_root *cmd, void *param) return init_mi_tree(404, "No information available for dpid", 33); } - if (translate(NULL, input, &output, idp, &attrs)!=0){ + if (translate(NULL, input, &output, idp, &attrs, 0) < 0 ) { LM_DBG("could not translate %.*s with dpid %i\n", input.len, input.s, idp->dp_id); lock_stop_read( connection->ref_lock ); diff --git a/modules/dialplan/dialplan.h b/modules/dialplan/dialplan.h index 59965238563..51f783c999c 100644 --- a/modules/dialplan/dialplan.h +++ b/modules/dialplan/dialplan.h @@ -39,29 +39,30 @@ #define DP_CASE_INSENSITIVE 1 #define DP_INDEX_HASH_SIZE 16 -typedef struct dpl_node{ +typedef struct dpl_node { + int id; int dpid; - int table_id; /*choose between matching regexp/strings with same priority*/ + int table_id; /* choose between matching regexp/strings with same priority */ int pr; int matchop; int match_flags; - str match_exp, subst_exp, repl_exp; /*keeping the original strings*/ + str match_exp, subst_exp, repl_exp, attrs, timerec; /*keeping the original strings*/ pcre * match_comp, * subst_comp; /*compiled patterns*/ struct subst_expr * repl_comp; - str attrs; - str timerec; tmrec_t *parsed_timerec; + str match_var; + int continue_search; struct dpl_node * next; /*next rule*/ -}dpl_node_t, *dpl_node_p; +} dpl_node_t, *dpl_node_p; /* HASH_SIZE buckets of matching strings (lowercase hashing) 1 bucket of regexps (index: HASH_SIZE) */ -typedef struct dpl_index{ +typedef struct dpl_index { dpl_node_t * first_rule; dpl_node_t * last_rule; -}dpl_index_t, *dpl_index_p; +} dpl_index_t, *dpl_index_p; /*For every DPID*/ typedef struct dpl_id{ @@ -117,10 +118,11 @@ dpl_id_p select_dpid(dp_connection_list_p table, int id, int index); struct subst_expr* repl_exp_parse(str subst); void repl_expr_free(struct subst_expr *se); -int translate(struct sip_msg *msg, str user_name, str* repl_user, dpl_id_p idp, str *); +int translate(struct sip_msg *msg, str user_name, str* repl_user, dpl_id_p idp, str *, int); +int check_input_param(dp_param_p, char *); int rule_translate(struct sip_msg *msg, str , dpl_node_t * rule, str *); int test_match(str string, pcre * exp, int * out, int out_max); - +int dp_update(struct sip_msg * msg, pv_spec_t * src, pv_spec_t * dest, str * repl); typedef void * (*func_malloc)(size_t ); typedef void (*func_free)(void * ); diff --git a/modules/dialplan/dp_db.c b/modules/dialplan/dp_db.c index af1d79eb487..d433eb9ea5a 100644 --- a/modules/dialplan/dp_db.c +++ b/modules/dialplan/dp_db.c @@ -33,20 +33,23 @@ dp_head_p dp_hlist = NULL; -str default_dp_db_url = {NULL, 0}; +str default_dp_db_url = {NULL, 0}; str default_dp_table = {NULL, 0}; -str dp_table_name = str_init(DP_TABLE_NAME); -str dpid_column = str_init(DPID_COL); -str pr_column = str_init(PR_COL); -str match_op_column = str_init(MATCH_OP_COL); -str match_exp_column = str_init(MATCH_EXP_COL); -str match_flags_column = str_init(MATCH_FLAGS_COL); -str subst_exp_column = str_init(SUBST_EXP_COL); -str repl_exp_column = str_init(REPL_EXP_COL); -str disabled_column = str_init(DISABLED_COL); -str attrs_column = str_init(ATTRS_COL); -str timerec_column = str_init(TIMEREC_COL); - +str dp_table_name = str_init(DP_TABLE_NAME); +str id_column = str_init(ID_COL); +str dpid_column = str_init(DPID_COL); +str pr_column = str_init(PR_COL); +str match_op_column = str_init(MATCH_OP_COL); +str src_match_exp_column = str_init(SRC_MATCH_EXP_COL); +str match_exp_column = str_init(MATCH_EXP_COL); +str match_flags_column = str_init(MATCH_FLAGS_COL); +str subst_exp_column = str_init(SUBST_EXP_COL); +str repl_exp_column = str_init(REPL_EXP_COL); +str disabled_column = str_init(DISABLED_COL); +str attrs_column = str_init(ATTRS_COL); +str timerec_column = str_init(TIMEREC_COL); +str match_var_column = str_init(MATCH_VAR_COL); +str continue_search_column = str_init(CONTINUE_SEARCH_COL); #define GET_STR_VALUE(_res, _values, _index)\ do{\ @@ -58,6 +61,16 @@ str timerec_column = str_init(TIMEREC_COL); (_res).len = strlen(VAL_STR((_values)+ (_index)).s);\ }while(0); +#define GET_STR_VALUE_NULLALLOWED(_res, _values, _index)\ + do{\ + if (VAL_NULL((_values) + (_index))) { \ + (_res).s = ""; \ + (_res).len = 0; \ + } else { \ + (_res).s = VAL_STR((_values)+ (_index)).s;\ + (_res).len = strlen(VAL_STR((_values)+ (_index)).s);\ + } \ + }while(0); void destroy_rule(dpl_node_t * rule); void destroy_hash(dpl_id_t **rules_hash); @@ -233,10 +246,14 @@ int dp_load_db(dp_connection_list_p dp_conn) db_res_t * res = 0; db_val_t * values; db_row_t * rows; + /* Database to datastructure relationship */ db_key_t query_cols[DP_TABLE_COL_NO] = { - &dpid_column, &pr_column, + &id_column, &dpid_column, &pr_column, &match_op_column, &match_exp_column, &match_flags_column, - &subst_exp_column, &repl_exp_column, &attrs_column, &timerec_column }; + &subst_exp_column, &repl_exp_column, &attrs_column, + &timerec_column, &match_var_column, &continue_search_column + }; + db_key_t order = &pr_column; /* disabled condition */ db_key_t cond_cols[1] = { &disabled_column }; @@ -444,74 +461,76 @@ static inline tmrec_t* parse_time_def(char *time_str) { dpl_node_t * build_rule(db_val_t * values) { tmrec_t *parsed_timerec; - pcre * match_comp, *subst_comp; + pcre *match_comp, *subst_comp; struct subst_expr * repl_comp; dpl_node_t * new_rule; - str match_exp, subst_exp, repl_exp, attrs, timerec; + str match_exp, subst_exp, repl_exp, attrs, timerec, match_var; int matchop; int namecount; - matchop = VAL_INT(values+2); + matchop = VAL_INT(values+3); if((matchop != REGEX_OP) && (matchop!=EQUAL_OP)){ LM_ERR("invalid value for match operator\n"); return NULL; } + /* These Lines are just for debugging purpose */ + LM_DBG("[**] STARTING RULE BUILDING --> Rule Id: %d Dialplan Id: %d Dialplan Priority: %d\n", VAL_INT(values), VAL_INT(values+1), VAL_INT(values+2)); parsed_timerec = 0; match_comp = subst_comp = 0; repl_comp = 0; new_rule = 0; - GET_STR_VALUE(match_exp, values, 3); - if(matchop == REGEX_OP){ + /* Get Destination Matching Expression */ + GET_STR_VALUE(match_exp, values, 4); + LM_DBG("[ii] MATCH EXPRESSION --> %.*s \n", match_exp.len, match_exp.s); - LM_DBG("Compiling %.*s expression with flag: %d\n", - match_exp.len, match_exp.s, VAL_INT(values+4)); + /* Evaluating Match Expression */ + if(matchop == REGEX_OP) { + LM_DBG("Compiling %.*s expression with flag: %d\n", match_exp.len, match_exp.s, VAL_INT(values+5)); - match_comp = wrap_pcre_compile(match_exp.s, VAL_INT(values+4)); + match_comp = wrap_pcre_compile(match_exp.s, VAL_INT(values+5)); - if(!match_comp){ - LM_ERR("failed to compile match expression \"%.*s\"\n", - match_exp.len, match_exp.s); + if(!match_comp) { + LM_ERR("failed to compile match expression %.*s\n", match_exp.len, match_exp.s); goto err; } } - LM_DBG("building subst rule\n"); - GET_STR_VALUE(subst_exp, values, 5); + /* Retrieving Substitution Expression */ + GET_STR_VALUE(subst_exp, values, 6); + LM_DBG("[ii] SUBSTITUTION EXPRESSION --> %.*s \n", subst_exp.len, subst_exp.s); + if(subst_exp.s && subst_exp.len){ /* subst regexp */ - subst_comp = wrap_pcre_compile(subst_exp.s, VAL_INT(values+4)); + subst_comp = wrap_pcre_compile(subst_exp.s, VAL_INT(values+5)); if(subst_comp == NULL){ - LM_ERR("failed to compile subst expression \"%.*s\"\n", - subst_exp.len, subst_exp.s); + LM_ERR("failed to compile subst expression\n"); goto err; } } - /* replace exp */ - GET_STR_VALUE(repl_exp, values, 6); + /* Retrieving Replace Expression */ + GET_STR_VALUE(repl_exp, values, 7); + LM_DBG("[ii] REPLACE EXPRESSION --> %.*s \n", repl_exp.len, repl_exp.s); + if(repl_exp.len && repl_exp.s){ repl_comp = repl_exp_parse(repl_exp); if(!repl_comp){ - LM_ERR("failed to compile replacing expression \"%.*s\"\n", - repl_exp.len, repl_exp.s); + LM_ERR("failed to compile replacing expression %.*s\n", repl_exp.len, repl_exp.s); goto err; } } - pcre_fullinfo( - subst_comp, /* the compiled pattern */ - NULL, /* no extra data - we didn't study the pattern */ - PCRE_INFO_CAPTURECOUNT, /* number of named substrings */ - &namecount); /* where to put the answer */ + pcre_fullinfo ( + subst_comp, /* the compiled pattern */ + NULL, /* no extra data - we didn't study the pattern */ + PCRE_INFO_CAPTURECOUNT, /* number of named substrings */ + &namecount /* where to put the answer */ + ); - LM_DBG("references:%d , max:%d\n",namecount, - repl_comp?repl_comp->max_pmatch:0); - - if ( (repl_comp!=NULL) && (namecountmax_pmatch) && - (repl_comp->max_pmatch!=0) ){ + if((repl_comp != NULL) && (namecount < repl_comp->max_pmatch) && (repl_comp->max_pmatch != 0)) { LM_ERR("repl_exp uses a non existing subexpression\n"); goto err; } @@ -534,24 +553,28 @@ dpl_node_t * build_rule(db_val_t * values) goto err; /*set the rest of the rule fields*/ - new_rule->dpid = VAL_INT(values); - new_rule->pr = VAL_INT(values+1); - new_rule->match_flags = VAL_INT(values+4); - new_rule->matchop = matchop; - GET_STR_VALUE(attrs, values, 7); - if(str_to_shm(attrs, &new_rule->attrs)!=0) - goto err; + new_rule->id = VAL_INT(values); + new_rule->dpid = VAL_INT(values+1); + new_rule->pr = VAL_INT(values+2); + new_rule->match_flags = VAL_INT(values+5); + new_rule->matchop = matchop; + + /* Retrieve Rule Attrs Value */ + GET_STR_VALUE(attrs, values, 8); + LM_DBG("[ii] RULE ATTRIBUTES --> %.*s \n", attrs.len, attrs.s); - LM_DBG("attrs are %.*s\n", - new_rule->attrs.len, new_rule->attrs.s); + if(str_to_shm(attrs, &new_rule->attrs)!=0) goto err; + + LM_DBG("ATTRS are %.*s\n", new_rule->attrs.len, new_rule->attrs.s); /* Retrieve and Parse Timerec Matching Pattern */ - GET_STR_VALUE(timerec, values, 8); + GET_STR_VALUE(timerec, values, 9); + LM_DBG("[ii] RULE TIMEREC --> %.*s \n", timerec.len, timerec.s); + if(timerec.len && timerec.s) { parsed_timerec = parse_time_def(timerec.s); if(!parsed_timerec) { - LM_ERR("failed to parse timerec pattern %.*s\n", - timerec.len, timerec.s); + LM_ERR("failed to parse timerec pattern %.*s\n", timerec.len, timerec.s); goto err; } @@ -560,10 +583,19 @@ dpl_node_t * build_rule(db_val_t * values) new_rule->parsed_timerec = parsed_timerec; - LM_DBG("timerecs are %.*s\n", - new_rule->timerec.len, new_rule->timerec.s); + LM_DBG("timerecs are %.*s\n", new_rule->timerec.len, new_rule->timerec.s); } + /* Retrieve Matching Var */ + GET_STR_VALUE_NULLALLOWED(match_var, values, 10); + LM_DBG("[ii] MATCHING VARIABLE --> %.*s \n", match_var.len, match_var.s); + + if(str_to_shm(match_var, &new_rule->match_var) != 0) goto err; + + /* Retrieve Continue Search Flag */ + new_rule->continue_search = VAL_INT(values+11); + LM_DBG("[ii] CONTINUE SEARCH --> %d \n", VAL_INT(values+11)); + if (match_comp) new_rule->match_comp = match_comp; @@ -585,11 +617,11 @@ dpl_node_t * build_rule(db_val_t * values) } -int add_rule2hash(dpl_node_t * rule, dp_connection_list_t *conn, int index) -{ +int add_rule2hash(dpl_node_t * rule, dp_connection_list_t *conn, int index) { + + int new_id; dpl_id_p crt_idp; dpl_index_p indexp; - int new_id, bucket = 0; if(!conn){ LM_ERR("data not allocated\n"); @@ -599,7 +631,8 @@ int add_rule2hash(dpl_node_t * rule, dp_connection_list_t *conn, int index) new_id = 0; crt_idp = select_dpid(conn, rule->dpid, index); - /*didn't find a dpl_id*/ + + /* dpl_id not found */ if(!crt_idp){ crt_idp = shm_malloc(sizeof(dpl_id_t) + (DP_INDEX_HASH_SIZE+1) * sizeof(dpl_index_t)); if(!crt_idp){ @@ -615,22 +648,12 @@ int add_rule2hash(dpl_node_t * rule, dp_connection_list_t *conn, int index) switch (rule->matchop) { case REGEX_OP: - indexp = &crt_idp->rule_hash[DP_INDEX_HASH_SIZE]; - break; - case EQUAL_OP: - if (rule->match_exp.s == NULL || rule->match_exp.len == 0) { - LM_ERR("NULL matching expressions in database not accepted!!!\n"); - return -1; - } - bucket = core_case_hash(&rule->match_exp, NULL, DP_INDEX_HASH_SIZE); - - indexp = &crt_idp->rule_hash[bucket]; + indexp = &crt_idp->rule_hash[DP_INDEX_HASH_SIZE]; break; default: - LM_ERR("SKIPPED RULE. Unsupported match operator (%d).\n", - rule->matchop); + LM_ERR("SKIPPED RULE. Unsupported match operator (%d).\n", rule->matchop); goto err; } @@ -649,9 +672,8 @@ int add_rule2hash(dpl_node_t * rule, dp_connection_list_t *conn, int index) crt_idp->next = conn->hash[conn->next_index]; conn->hash[conn->next_index] = crt_idp; } - LM_DBG("added the rule id %i pr %i next %p to the " - " %i bucket\n", rule->dpid, - rule->pr, rule->next, rule->matchop == REGEX_OP ? DP_INDEX_HASH_SIZE : bucket); + + LM_DBG("added the rule id %i pr %i next %p to the %i bucket\n", rule->dpid, rule->pr, rule->next, DP_INDEX_HASH_SIZE); return 0; @@ -699,11 +721,10 @@ void destroy_hash(dpl_id_t **rules_hash) void destroy_rule(dpl_node_t * rule){ - if(!rule) - return; + if(!rule) return; - LM_DBG("destroying rule with priority %i\n", - rule->pr); + /* Debug */ + LM_DBG("Destroying rule %i dpid %i with priority %i\n", rule->id, rule->dpid, rule->pr); if(rule->match_comp) wrap_pcre_free(rule->match_comp); @@ -711,7 +732,6 @@ void destroy_rule(dpl_node_t * rule){ if(rule->subst_comp) wrap_pcre_free(rule->subst_comp); - /*destroy repl_exp*/ if(rule->repl_comp) repl_expr_free(rule->repl_comp); @@ -732,6 +752,8 @@ void destroy_rule(dpl_node_t * rule){ if(rule->parsed_timerec) shm_free(rule->parsed_timerec); + if(rule->match_var.s) + shm_free(rule->match_var.s); } @@ -782,17 +804,20 @@ void list_hash(dpl_id_t * hash, rw_lock_t * ref_lock) } -void list_rule(dpl_node_t * rule) -{ - LM_DBG("RULE %p: pr %i next %p match_exp %.*s match_flags %d, " - "subst_exp %.*s, repl_exp %.*s and attrs %.*s and timerec %.*s\n", rule, +void list_rule(dpl_node_t * rule) { + LM_DBG( "RULE %p: id %i dpid %i pr %i next %p match_exp %.*s match_flags %d, " + "subst_exp %.*s, repl_exp %.*s and attrs %.*s and timerec %.*s match_var %.*s continue_search %i\n", + rule, rule->id, rule->dpid, rule->pr, rule->next, rule->match_exp.len, rule->match_exp.s, rule->match_flags, rule->subst_exp.len, rule->subst_exp.s, rule->repl_exp.len, rule->repl_exp.s, rule->attrs.len, rule->attrs.s, - rule->timerec.len, rule->timerec.s); + rule->timerec.len, rule->timerec.s, + rule->match_var.len, rule->match_var.s, + rule->continue_search + ); } /* Retrieves the corresponding entry of the given partition name */ diff --git a/modules/dialplan/dp_db.h b/modules/dialplan/dp_db.h index 2954631d8aa..f1cfe84e27b 100644 --- a/modules/dialplan/dp_db.h +++ b/modules/dialplan/dp_db.h @@ -31,23 +31,26 @@ #define DP_PARTITION "default" #define DP_TABLE_NAME "dialplan" -#define DPID_COL "dpid" -#define PR_COL "pr" +#define ID_COL "id" +#define DPID_COL "dpid" +#define PR_COL "pr" #define MATCH_OP_COL "match_op" +#define SRC_MATCH_EXP_COL "src_exp" #define MATCH_EXP_COL "match_exp" #define MATCH_FLAGS_COL "match_flags" #define SUBST_EXP_COL "subst_exp" #define REPL_EXP_COL "repl_exp" #define DISABLED_COL "disabled" -#define ATTRS_COL "attrs" -#define TIMEREC_COL "timerec" +#define ATTRS_COL "attrs" +#define TIMEREC_COL "timerec" +#define MATCH_VAR_COL "match_var" +#define CONTINUE_SEARCH_COL "continue_search" - -#define DP_TABLE_VERSION 5 -#define DP_TABLE_COL_NO 9 +#define DP_TABLE_VERSION 7 +#define DP_TABLE_COL_NO 12 typedef struct dp_head{ - str partition;/*Attribute that uniquely identifies head*/ + str partition; /* Attribute that uniquely identifies head */ str dp_db_url; str dp_table_name; struct dp_head* next; @@ -59,15 +62,19 @@ extern dp_connection_list_p dp_conns; extern str default_dp_db_url; extern str default_dp_table; extern str dp_table_name; +extern str id_column; extern str dpid_column; extern str pr_column; extern str match_op_column; +extern str src_match_exp_column; extern str match_exp_column; extern str match_flags_column; extern str subst_exp_column; extern str repl_exp_column; extern str attrs_column; extern str timerec_column; +extern str match_var_column; +extern str continue_search_column; extern str disabled_column; struct dp_param_list; diff --git a/modules/dialplan/dp_repl.c b/modules/dialplan/dp_repl.c index e7b8dd31383..a983d1ac394 100644 --- a/modules/dialplan/dp_repl.c +++ b/modules/dialplan/dp_repl.c @@ -105,9 +105,8 @@ struct subst_expr* repl_exp_parse(str subst) static char dp_output_buf[MAX_PHONE_NB_DIGITS+1]; static int matches[MAX_MATCHES]; -int rule_translate(struct sip_msg *msg, str string, dpl_node_t * rule, - str * result) -{ +int rule_translate(struct sip_msg * msg, str string, dpl_node_t * rule, str * result) { + int repl_nb, offset, match_nb; struct replace_with token; pcre * subst_comp; @@ -360,57 +359,93 @@ static inline int check_time(tmrec_t *time_rec) { return 1; } -#define DP_MAX_ATTRS_LEN 32 -static char dp_attrs_buf[DP_MAX_ATTRS_LEN+1]; -int translate(struct sip_msg *msg, str input, str * output, dpl_id_p idp, str * attrs) { +/** + * Check for match_var SRC/DST variable. On success replace + * input/output placeholders + */ +static int check_match_var(struct sip_msg *msg, char * match_var, str * input, dp_param_p dp_par) { - dpl_node_p rulep, rrulep; - int string_res = -1, regexp_res = -1, bucket; + pv_value_t value; - if(!input.s || !input.len) { - LM_ERR("invalid input string\n"); + /* Backup Original Source */ + char *backup = pkg_malloc(sizeof(char) * strlen(match_var)); + strcpy(backup, match_var); + + /* Check for input string validity */ + if(check_input_param(dp_par, backup) <= 0) { + LM_ERR("wrong match_var syntax ... skipping rule!\n"); return -1; } - bucket = core_case_hash(&input, NULL, DP_INDEX_HASH_SIZE); + /* Free backup memory */ + pkg_free(backup); - /* try to match the input in the corresponding string bucket */ - for (rulep = idp->rule_hash[bucket].first_rule; rulep; rulep=rulep->next) { + /* Unable to retrieve input PV stored value */ + if (pv_get_spec_value(msg, &dp_par->v.sp[0], &value) != 0) { + LM_ERR("no input match_var PV found ... skipping rule!\n"); + return -1; + } - LM_DBG("Equal operator testing\n"); + /* Input PV stored value was empty or NULL ... unusable for pattern matching */ + if (value.flags &(PV_VAL_NULL|PV_VAL_EMPTY)) { + LM_ERR("NULL or empty input match_var ... skipping rule!\n"); + return -1; + } - if(rulep->match_exp.len != input.len) - continue; + /* Rewrite input PV string */ + input->s = value.rs.s; + memcpy(input->s, value.rs.s, value.rs.len * sizeof(char)); + input->len = value.rs.len; - LM_DBG("Comparing (input %.*s) with (rule %.*s) [%d] and timerec %.*s\n", - input.len, input.s, rulep->match_exp.len, rulep->match_exp.s, - rulep->match_flags, rulep->timerec.len, rulep->timerec.s); + /* Debug */ + LM_DBG("PV input changed to %.*s due to match_var option!\n", input->len, input->s); - // Check for Time Period if Set - if(rulep->parsed_timerec) { - LM_DBG("Timerec exists for rule checking: %.*s\n", rulep->timerec.len, rulep->timerec.s); - // Doesn't matches time period continue with next rule - if(!check_time(rulep->parsed_timerec)) { - LM_DBG("Time rule doesn't match: skip next!\n"); - continue; - } - } + /* Success */ + return 1; +} - if (rulep->match_flags & DP_CASE_INSENSITIVE) { - string_res = strncasecmp(rulep->match_exp.s,input.s,input.len); - } else { - string_res = strncmp(rulep->match_exp.s,input.s,input.len); - } +#define DP_MAX_ATTRS_LEN 32 +static char dp_attrs_buf[DP_MAX_ATTRS_LEN+1]; +int translate(struct sip_msg *msg, str input, str * output, dpl_id_p idp, str * attrs, int matched_rule_id) { - if (string_res == 0) { - break; - } + int_str val; + int dbmatch = 0; + dpl_node_p rrulep = NULL; + dp_param_p rdp_par = NULL; + struct usr_avp *ruleid_avp = NULL; + int string_res = -1, regexp_res = -1, matched = 0; + + if(!input.s || !input.len) { + LM_ERR("invalid input string\n"); + return -1; } /* try to match the input in the regexp bucket */ for (rrulep = idp->rule_hash[DP_INDEX_HASH_SIZE].first_rule; rrulep; rrulep=rrulep->next) { - // Check for Time Period if Set + /* Debug */ + LM_DBG("Comparing (match op: %i continue search: %i) input %.*s with rule %.*s [%d] and timerec %.*s\n", + rrulep->matchop, rrulep->continue_search, input.len, input.s, rrulep->match_exp.len, rrulep->match_exp.s, + rrulep->match_flags, rrulep->timerec.len, rrulep->timerec.s); + + /* Check for match_var existance. If true override input search string and output pointer */ + if(rrulep->match_var.len > 0) { + /* Build a parameters data structure and allocate it */ + rdp_par = (dp_param_p) pkg_malloc(sizeof(dp_param_t)); + if(rdp_par == NULL) { LM_ERR("no more pkg memory\n"); goto err; } + memset(rdp_par, 0, sizeof(dp_param_t)); + + if(check_match_var(msg, rrulep->match_var.s, &input, rdp_par) <= 0) continue; + } + + /* Lenght match failed on a best match rule ... skipping rule */ + if(rrulep->matchop == EQUAL_OP) + if(rrulep->match_exp.len != input.len) { + LM_DBG("Match length failed ... discarding rule!\n"); + continue; + } + + /* Check for Time Period if Set */ if(rrulep->parsed_timerec) { LM_DBG("Timerec exists for rule checking: %.*s\n", rrulep->timerec.len, rrulep->timerec.s); // Doesn't matches time period continue with next rule @@ -420,63 +455,107 @@ int translate(struct sip_msg *msg, str input, str * output, dpl_id_p idp, str * } } - regexp_res = (test_match(input, rrulep->match_comp, matches, MAX_MATCHES) - >= 0 ? 0 : -1); - - LM_DBG("Regex operator testing. Got result: %d\n", regexp_res); + /* Reset Return Values */ + string_res = regexp_res = -1; - if (regexp_res == 0) { - break; + /* Check wich match to apply */ + if(rrulep->matchop == EQUAL_OP) { + /* Doing Best Match */ + if (rrulep->match_flags & DP_CASE_INSENSITIVE) + string_res = strncasecmp(rrulep->match_exp.s,input.s,input.len); + else + string_res = strncmp(rrulep->match_exp.s,input.s,input.len); + } else { + /* Doing RegEXP Match */ + regexp_res = (test_match(input, rrulep->match_comp, matches, MAX_MATCHES) >= 0 ? 0 : -1); } - } - if (string_res != 0 && regexp_res != 0) { - LM_DBG("No matching rule for input %.*s\n", input.len, input.s); - return -1; - } + /* If a match occours apply translation */ + if (string_res == 0 || regexp_res == 0) { - /* pick the rule with lowest table index if both match and prio are equal */ - if ((string_res | regexp_res) == 0) { - if (rulep->pr < rrulep->pr) { - rulep = rrulep; - } else if (rrulep->pr == rulep->pr && - rrulep->table_id < rulep->table_id) { - rulep = rrulep; - } - } + /* Update Match Counter */ + ++matched; - if (!rulep) - rulep = rrulep; - - LM_DBG("Found a matching rule %p: pr %i, match_exp %.*s\n", - rulep, rulep->pr, rulep->match_exp.len, rulep->match_exp.s); - - if(attrs){ - attrs->len = 0; - attrs->s = 0; - if(rulep->attrs.len>0) { - LM_DBG("the rule's attrs are %.*s\n", - rulep->attrs.len, rulep->attrs.s); - if(rulep->attrs.len >= DP_MAX_ATTRS_LEN) { - LM_ERR("EXCEEDED Max attribute length.\n"); - return -1; + /* Applying translate on input string */ + if(rule_translate(msg, input, rrulep, output) != 0){ + LM_ERR("could not build the output\n"); + goto err; + } + + /* Update database retrieved output PV */ + if(rrulep->match_var.len > 0 && rdp_par != NULL) { + if (dp_update(msg, &rdp_par->v.sp[0], &rdp_par->v.sp[1], output) != 0) + LM_ERR("Unable to update database retrieved input/output PVs!\n"); + + /* Free Datastructure */ + pkg_free(rdp_par); + + /* Set as Matched */ + dbmatch = 1; + } + + /* Check for a valid matching rule avp id */ + if(matched_rule_id > 0) { + /* Check if AVP already exists ... */ + ruleid_avp = search_first_avp(0, matched_rule_id, &val, NULL); + + /* ... and destroy it!!! */ + if(ruleid_avp && !(is_avp_str_val(ruleid_avp) == 0)) { + LM_DBG("AVP %i already exists with value %d\n", matched_rule_id, val.n); + destroy_avp(ruleid_avp); + ruleid_avp = NULL; + } + + /* Validate AVP value */ + val.n = rrulep->id; + + /* Add AVP */ + if (add_avp(0, matched_rule_id, val) < 0) { + LM_ERR("unable to add AVP"); + goto err; + } } - attrs->s = dp_attrs_buf; - memcpy(attrs->s, rulep->attrs.s, rulep->attrs.len*sizeof(char)); - attrs->len = rulep->attrs.len; - attrs->s[attrs->len] = '\0'; - LM_DBG("the copied attributes are: %.*s\n", - attrs->len, attrs->s); + /* A Rule Was Found ... build ATTRS PVAR content */ + if(attrs) { + attrs->len = 0; + attrs->s = 0; + + if(rrulep->attrs.len > 0) { + LM_DBG("the rule's attrs are %.*s\n", rrulep->attrs.len, rrulep->attrs.s); + + if(rrulep->attrs.len >= DP_MAX_ATTRS_LEN) { + LM_ERR("EXCEEDED Max attribute length.\n"); + goto err; + } + + attrs->s = dp_attrs_buf; + memcpy(attrs->s, rrulep->attrs.s, rrulep->attrs.len*sizeof(char)); + attrs->len = rrulep->attrs.len; + attrs->s[attrs->len] = '\0'; + + LM_DBG("the copied attributes are: %.*s\n", attrs->len, attrs->s); + } + } + + /* Check if continue searching through dialplan rules */ + if(rrulep->continue_search == 0) break; } } - if(rule_translate(msg, input, rulep, output)!=0){ - LM_ERR("could not build the output\n"); - return -1; + /* No Rule Found ... */ + if (matched <= 0) { + LM_DBG("No matching rule for input %.*s\n", input.len, input.s); + goto err; } - return 0; + /* Return Value */ + return dbmatch; + +/* Purge on error */ +err: + if(rdp_par) pkg_free(rdp_par); + return -1; } @@ -493,20 +572,21 @@ int test_match(str string, pcre * exp, int * out, int out_max) return -1; } - result_count = pcre_exec( - exp, /* the compiled pattern */ - NULL, /* no extra data - we didn't study the pattern */ - string.s, /* the subject string */ - string.len, /* the length of the subject */ - 0, /* start at offset 0 in the subject */ - 0, /* default options */ - out, /* output vector for substring information */ - out_max); /* number of elements in the output vector */ - - if( result_count < 0 ) + result_count = pcre_exec ( + exp, /* the compiled pattern */ + NULL, /* no extra data - we didn't study the pattern */ + string.s, /* the subject string */ + string.len, /* the length of the subject */ + 0, /* start at offset 0 in the subject */ + 0, /* default options */ + out, /* output vector for substring information */ + out_max /* number of elements in the output vector */ + ); + + if(result_count < 0) return result_count; - if( result_count == 0) + if(result_count == 0) { LM_ERR("Not enough space for mathing\n"); return result_count;