#include "../../core/sr_module.h" #include "../../core/xavp.h" #include "../pv/pv_xavp.h" #include "../../core/route_struct.h" #include "../../core/str.h" #include #include "../../core/parser/digest/digest.h" #include "../../core/parser/parse_uri.h" #include "../../core/parser/parse_from.h" #include "../../core/parser/parse_to.h" #include "../../modules/tm/tm_load.h" #include "../../core/parser/contact/parse_contact.h" #ifdef CENTOS_7 #include "../../lib/srutils/sruid.h" #endif #include "../../core/dset.h" #include "../../core/dprint.h" #include "../../core/qvalue.h" #include "../../core/parser/msg_parser.h" #include "../../core/usr_avp.h" #include "../../modules/textops/api.h" #include #include #include "m2_mnp.h" // Locks static gen_lock_t *gui_mysql_lock = 0; static gen_lock_t *trie_lock = 0; // DB variables MYSQL *gui_sql; MYSQL *mnp_sql; //Local variables char *cip; // internal global variables textops_api_t txt_api; //imported functions from textops shd_t *shared_data = NULL; int not_loaded = 0; MODULE_VERSION static int mod_init(void) { if(validate_params()){ LM_CRIT("ERROR options validation\n"); goto error; } /* load the TEXTOPS API */ if (load_textops_api(&txt_api) != 0){ LM_ERR("failed to load textops API\n"); goto error; } if (open_gui_mysql_connection()) { LM_ERR("failed to load mysql"); goto error; } if (init_locks()) { LM_ERR("failed to init locks"); goto error; } if (get_mnp_credentials()) { LM_ERR("failed to get mnp server credentials"); goto error; } if ((shared_data = (shd_t*) shm_malloc(sizeof(shd_t))) == NULL) { LM_ERR("failed to allocate shared_data"); goto error; } if (*mnp_supported_prefixes) { if (load_prefixes()) { LM_ERR("failed to load prefixes"); goto error; } } return 0; error: LM_NOTICE("m2_mnp module will not be loaded"); not_loaded = 1; destroy_locks(); return 0; } /* Function initiates children processes on kamailio starts */ static int child_init(int rank) { /* skip child init for non-worker process ranks */ if (rank==PROC_INIT || rank==PROC_MAIN || rank==PROC_TCP_MAIN) return 0; if (not_loaded) { LM_NOTICE("Children of m2_mnp will not be loaded"); return 0; } if (open_mnp_mysql_connection()) { return -1; } return 0; } /* Function to load prefixes from db */ int load_prefixes() { if (shared_data == NULL) return 1; shared_data->main_n = initialize_main_node(); if (shared_data->main_n == NULL) return 1; if (get_prefixes_from_gui(shared_data->main_n)) { return 1; } return 0; } /* Get mnp mysql server credentials */ int get_mnp_credentials() { char query[1024]; MYSQL_RES *result; MYSQL_ROW row; int found = 0; mnpdb_host = (char *) shm_malloc(128); mnpdb_user = (char *) shm_malloc(128); mnpdb_pass = (char *) shm_malloc(128); mnpdb_name = (char *) shm_malloc(128); mnpdb_port = (int*) shm_malloc(sizeof(int)); search_field = (char *) shm_malloc(128); result_field = (char *) shm_malloc(128); table_name = (char *) shm_malloc(128); mnp_supported_prefixes = (int *) shm_malloc(sizeof(int)); use_mnp = (int *) shm_malloc(sizeof(int)); sprintf(query, "SELECT name, value FROM conflines WHERE name = 'MNP_Server_IP' " "OR name = 'MNP_Port' " "OR name = 'MNP_Username' " "OR name = 'MNP_Password' " "OR name = 'MNP_DB_NAME' " "OR name = 'MNP_Table_Name' " "OR name = 'MNP_Search_Field' " "OR name = 'MNP_Result_Field' " "OR name = 'MNP_Supported_Prefixes' " "OR name = 'Use_Number_Portability'"); if (mnp_mysql_query(query, gui_sql)) { return 1; } result = mysql_store_result(gui_sql); if (result) { while ((row = mysql_fetch_row(result))) { found = 1; if (row[0]) { if(strcmp("MNP_Server_IP", row[0]) == 0) { if (row[1]) strcpy(mnpdb_host, row[1]); } if(strcmp("MNP_Username", row[0]) == 0) { if (row[1]) strcpy(mnpdb_user, row[1]); } if(strcmp("MNP_Password", row[0]) == 0) { if (row[1]) strcpy(mnpdb_pass, row[1]); } if(strcmp("MNP_DB_NAME", row[0]) == 0) { if (row[1]) strcpy(mnpdb_name, row[1]); } if(strcmp("MNP_Table_Name", row[0]) == 0) { if (row[1]) strcpy(table_name, row[1]); } if(strcmp("MNP_Search_Field", row[0]) == 0) { if (row[1]) strcpy(search_field, row[1]); } if(strcmp("MNP_Result_Field", row[0]) == 0) { if (row[1]) strcpy(result_field, row[1]); } if(strcmp("MNP_Supported_Prefixes", row[0]) == 0) { if (row[1]) *mnp_supported_prefixes = atoi(row[1]); } if(strcmp("Use_Number_Portability", row[0]) == 0) { if (row[1]) *use_mnp = atoi(row[1]); } } } } if (found) { LM_NOTICE("mnp_server_ip: %s, username: %s, password: %s, db_name: %s, table_name: %s, search_field: %s, result_field: %s, Use_Number_Portability: %d", mnpdb_host, mnpdb_user, mnpdb_pass, mnpdb_name, table_name, search_field, result_field, *use_mnp); } else { LM_NOTICE("MNP settings was not found"); return 1; } return 0; } /* Get MNP_Supported_Prefixes setting status */ void get_supported_prefixes_status() { char query[1024]; MYSQL_RES *result; MYSQL_ROW row; sprintf(query, "SELECT name, value FROM conflines WHERE name = 'MNP_Supported_Prefixes' "); if (mnp_mysql_query(query, gui_sql)) { return; } result = mysql_store_result(gui_sql); if (result) { if ((row = mysql_fetch_row(result))) { *mnp_supported_prefixes = atoi(row[1]); } } LM_NOTICE("MNP_Supported_Prefixes: %d", *mnp_supported_prefixes); } /* Get MNP_Supported_Prefixes setting status */ void get_mnp_status() { char query[1024]; MYSQL_RES *result; MYSQL_ROW row; sprintf(query, "SELECT name, value FROM conflines WHERE name = 'Use_Number_Portability' "); if (mnp_mysql_query(query, gui_sql)) { return; } result = mysql_store_result(gui_sql); if (result) { if ((row = mysql_fetch_row(result))) { *use_mnp = atoi(row[1]); } } LM_NOTICE("Use_Number_Portability: %d", *use_mnp); } /* MYSQL function */ int mnp_mysql_query(char *query, MYSQL *con) { int retries = 5; int lost_connection = 0; int mnp_reconnect = 0; if (SHOW_SQL) { LM_NOTICE("Query: %s", query); } db_reconnect: if (mysql_query(con, query)) { LM_ERR("%s\n", mysql_error(con)); LM_ERR("%s\n", query); // maybe query failed because of mysql timeout? if (mysql_ping(con)) { int mysql_err = mysql_errno(con); if (mysql_err == CR_SERVER_GONE_ERROR || mysql_err == CR_SERVER_LOST) { lost_connection = 1; if (mysql_err == CR_SERVER_GONE_ERROR) { LM_ERR("Server has gone away. Attempting to reconnect.\n"); } else { LM_ERR("Server lost. Attempting to reconnect.\n"); } if (con == mnp_sql) { mnp_reconnect = 1; } // attempt to connect mysql_init(con); if (mnp_reconnect) { if (mysql_real_connect(con, mnpdb_host, mnpdb_user, mnpdb_pass, mnpdb_name, *mnpdb_port, NULL, 0)) { mysql_query(con, "SET NAMES utf8"); } else { LM_ERR("Cannot connect to database server %s.\n", guidb_host.s); } } else { if (mysql_real_connect(con, guidb_host.s, guidb_user.s, guidb_pass.s, guidb_name.s, guidb_port, NULL, 0)) { mysql_query(con, "SET NAMES utf8"); } else { LM_ERR("Cannot connect to database server %s.\n", guidb_host.s); } } retries--; if (retries) { goto db_reconnect; } else { LM_ERR("Retried to connect fives times, giving up.\n"); return 1; } } } } if (lost_connection) { LM_NOTICE("Successfully reconnected to MySQL\n"); } return 0; } int open_gui_mysql_connection() { if ((gui_sql = open_mysql_connection(guidb_host.s, guidb_user.s, guidb_pass.s, guidb_name.s, guidb_port, 0)) == NULL) { LM_ERR("failed to open gui mysql connection"); return 1; } else { LM_NOTICE("gui mysql connection"); } return 0; } int open_mnp_mysql_connection() { if ((mnp_sql = open_mysql_connection(mnpdb_host, mnpdb_user, mnpdb_pass, mnpdb_name, *mnpdb_port, 1)) == NULL) { LM_ERR("failed to open mnp mysql connection"); return 1; } else { LM_NOTICE("mnp mysql connection"); } return 0; } MYSQL *open_mysql_connection(char *dbhost, char *dbuser, char *dbpass, char *dbname, int dbport, int local) { MYSQL *mysql; if (local) { mysql = (MYSQL*) pkg_malloc(sizeof(MYSQL)); } else { mysql = (MYSQL*) shm_malloc(sizeof(MYSQL)); } if (!mysql_init(mysql)) { LM_ERR("MySQL error %s\n", mysql_error(mysql)); return NULL; } if (!mysql_real_connect(mysql, dbhost, dbuser, dbpass, dbname, dbport, NULL, 0)) { LM_ERR("MySQL error: %s\n", mysql_error(mysql)); return NULL; } if (mysql_query(mysql, "SET NAMES utf8")) { LM_ERR("MySQL error: %s\n", mysql_error(mysql)); return NULL; } return mysql; } /* Init locks */ int init_locks() { if ((gui_mysql_lock=lock_alloc()) == 0) { LM_CRIT("failed to alloc lock\n"); return 1; } if (lock_init(gui_mysql_lock) == 0) { LM_CRIT("failed to init lock\n"); return 1; } if ((trie_lock=lock_alloc()) == 0) { LM_CRIT("fialed to alloc trie lock\n"); } if (lock_init(trie_lock) == 0) { LM_CRIT("faled to init trie lock\n"); } return 0; } void destroy_locks() { if (gui_mysql_lock) { lock_destroy( gui_mysql_lock ); lock_dealloc( gui_mysql_lock ); gui_mysql_lock = 0; } } /* Validate kamailio.cfg parameters */ int validate_params() { if (guidb_host.len <= 0) { LM_NOTICE("guidb_host must be provided\n"); return 1; } if (guidb_user.len <= 0) { LM_NOTICE("guidb_user must be provided\n"); return 1; } if (guidb_name.len <= 0) { LM_NOTICE("guidb_name must be provided\n"); return 1; } if (guidb_port == 0) { LM_NOTICE("guidb_port must be provided\n"); return 1; } if (guidb_pass.len <= 0) { LM_NOTICE("Connetion without password\n"); guidb_pass.s = NULL; return 0; } return 0; } /* Kamailio.cdg function to find contact info in mnp database and set cip header */ static int m2_mnp(struct sip_msg *msg) { LM_NOTICE("M2_MNP MODULE VERSION: %s", M2_MNP_VERSION); if (not_loaded) { LM_NOTICE("M2_MNP module not loaded"); return 1; } if (*use_mnp == 0) { LM_NOTICE("M2_MNP is turned off"); return 1; } hdr_field_t *to_header = get_to_header(msg); str dst_str = {0, 0}; char dst[64] = ""; cip = (char*) pkg_malloc(64); char parsed_dst[128]; if (to_header == NULL) goto error; dst_str = to_header->body; sprintf(dst, "%.*s", STR_FMT(&dst_str)); parse_header_number_part(dst, parsed_dst); LM_NOTICE("Parsed dst number:%s;", parsed_dst); if (*mnp_supported_prefixes == 0) { if(get_cip_from_mnp(parsed_dst)) goto no_connection; add_mnp_header(msg, cip); } else { lock_get(trie_lock); if(is_prefix_in_trie(parsed_dst)) { LM_NOTICE("Prefix was found in trie"); lock_release(trie_lock); if(get_cip_from_mnp(parsed_dst)) goto no_connection; LM_NOTICE("CIP: %s", cip); add_mnp_header(msg, cip); } else { lock_release(trie_lock); LM_NOTICE("Prefix was not found \n"); } } return 1; error: LM_NOTICE("Error"); return 0; no_connection: LM_NOTICE("Mysql Error, header will not be added"); return 1; } /* Get "to" header from SIP INVITE */ hdr_field_t *get_to_header(struct sip_msg *msg) { hdr_field_t *to_header; if (msg == 0) goto error; if (msg->to == 0) { goto error; } else { to_header = msg->to; } return to_header; error: return NULL; } /* Parse number from header to */ int parse_header_number_part(char *header, char *number) { const char *uris[] = {"sip:", "sips:", "tel:"}; int size = sizeof(uris) / sizeof(uris[0]); int i = 0; for (i = 0; i < size; i++) { char *uri_start_pos = strstr(header, uris[i]); if (uri_start_pos) { char *number_end_pos1 = strchr(uri_start_pos, '>'); if (number_end_pos1) { *number_end_pos1 = '\0'; } char *number_end_pos2 = strchr(uri_start_pos, '@'); if (number_end_pos2) { *number_end_pos2 = '\0'; } char *number_end_pos3 = strchr(uri_start_pos, ';'); if (number_end_pos3) { *number_end_pos3 = '\0'; } strncpy(number, uri_start_pos + strlen(uris[i]), strlen(header)); return 1; } } return 0; } /* Get cip from mnp database */ int get_cip_from_mnp(char *dst) { char query[128]; MYSQL_RES *result; MYSQL_ROW row; sprintf(query, "SELECT %s FROM %s WHERE %s = '%s' LIMIT 1", result_field, table_name, search_field, dst); if (mnp_mysql_query(query, mnp_sql)) { return 1; } result = mysql_store_result(mnp_sql); if (result){ row = mysql_fetch_row(result); if (row && row[0]) { strcpy(cip, row[0]); } else { strcpy(cip, ""); } } return 0; } /* Get prefixes from gui database */ int get_prefixes_from_gui(main_t *node) { char query[128]; MYSQL_RES *result; MYSQL_ROW row; sprintf(query, "SELECT prefix FROM mnp_prefixes"); if (mnp_mysql_query(query, gui_sql)) { return 1; } result = mysql_store_result(gui_sql); if (result) { while ((row = mysql_fetch_row(result))) { if (row[0]) insert_prefix(row[0], node); } } return 0; } // TRIE FUNCTIONS void trie_free(main_t *node) { int i; if (node == NULL) return; for(i = 0; i < 10; i++) { if(node->sub_node[i] != NULL && node->sub_node[i]->child != NULL) { trie_free(node->sub_node[i]->child); } if (node->sub_node[i] != NULL) { shm_free(node->sub_node[i]); node->sub_node[i] = NULL; } } shm_free(node); node = NULL; } void insert_prefix(char *prefix, main_t* node) { int len = strlen(prefix); int i = 0; main_t *current_node = node; prefix_t *current_sub_node; LM_NOTICE("Adding prefix: %s", prefix); if (node == NULL && prefix == NULL) { LM_NOTICE("Bad parametres\n"); return; } for (i = 0; i < len; i++) { int number = prefix[i] - '0'; current_sub_node = current_node->sub_node[number]; if (current_sub_node == NULL) { current_node->sub_node[number] = add_subnode(); current_sub_node = current_node->sub_node[number]; } if (current_sub_node == NULL) { LM_ERR("Something went wrong\n"); return; } current_sub_node->number = number; current_node = current_sub_node->child; } current_sub_node->exist = 1; LM_NOTICE("Prefix added"); } prefix_t *add_subnode() { prefix_t *sub_node = (prefix_t *) shm_malloc(sizeof(prefix_t)); if(sub_node == NULL) { LM_ERR("No more shared memory!\n"); exit(-1); } memset(sub_node, 0, sizeof(prefix_t)); sub_node->exist = 0; sub_node->child = initialize_main_node(); return sub_node; } main_t *initialize_main_node() { main_t *node = (main_t*) shm_malloc(sizeof(main_t)); memset(node, 0, sizeof(main_t)); null_subnodes(node); return node; } void null_subnodes(main_t *node) { int i; for (i = 0; i < 10; i++) { node->sub_node[i] = NULL; } } int is_prefix_in_trie(char *prefix) { int i = 0; int number; int len = strlen(prefix); if (shared_data == NULL) return 0; main_t *node = shared_data->main_n; for (i = 0; i < len; i++) { number = (int)(prefix[i] - '0'); if (is_allowed_number(number) == 0) break; if (node->sub_node[number] != NULL && node->sub_node[number]->exist == 1) { return 1; } if (node->sub_node[number] != NULL) { node = node->sub_node[number]->child; if (node == NULL) { return 0; } } else { return 0; } } return 0; } int is_allowed_number(int n) { int ok = 0; int i; for (i = 0; i < 10; i++) { if (n == i) ok = 1; } return ok; } //============================ /* Add mnp header */ int add_mnp_header(struct sip_msg *msg, char *value) { char *formated_header; formated_header = pkg_malloc(sizeof(char)*512); sprintf(formated_header, "%s: %s\r\n", SIP_HEADER, value); str hf = {formated_header, strlen(formated_header)}; LM_NOTICE("Header will be added %s", formated_header); if (txt_api.append_hf(msg, &hf) == 1) return 0; return 1; } /* Update rpc command */ void update_rpc(rpc_t* rpc, void* ctx) { if (not_loaded) { LM_NOTICE("M2_MNP module not loaded"); return; } get_supported_prefixes_status(); get_mnp_status(); if (*mnp_supported_prefixes == 0 || *use_mnp == 0) { return; } main_t *tmp_node = initialize_main_node(); LM_NOTICE("Getting prefixes from gui server"); if (get_prefixes_from_gui(tmp_node)) { LM_NOTICE("Unable to get prefixes!!! \n"); return; } if (shared_data ==NULL) return; LM_NOTICE("Write prefixes to the main trie"); lock_get(trie_lock); trie_free(shared_data->main_n); shared_data->main_n = tmp_node; lock_release(trie_lock); } /* */ void mod_destroy(void) { destroy_locks(); }