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;