#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" #include "../../lib/srutils/sruid.h" #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 "../ndb_redis/api.h" #include "m2_mnp.h" #define each_param(i) for(i = 0; i < 1; i++) static str o_values_param[1] = { str_init("$avp(m2_mnp:cache_status)"), }; pv_spec_t o_pv_values[1]; // DB variables MYSQL* mysql; redisContext *local_c; // internal global variables struct tm_binds rd_tmb; //imported functions from tm textops_api_t txt_api; //imported functions from textops MODULE_VERSION static int mod_init(void) { if(validate_params()){ LM_CRIT("ERROR options validation\n"); goto error; } /* load the TM API */ if (load_tm_api(&rd_tmb) != 0) { LM_ERR("failed to load TM API\n"); goto error; } /* load the TEXTOPS API */ if (load_textops_api(&txt_api) != 0){ LM_ERR("failed to load textops API\n"); goto error; } /* load avp variables */ int i; each_param(i) { if (pv_parse_spec(&o_values_param[i], &o_pv_values[i]) <0 || o_pv_values[i].type != PVT_AVP) { LM_ERR("cannot parse values avp\n"); goto error; } } return 0; error: return -1; } /* 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 (open_mysql_connection()) return -1; */ if (open_redis_connection()) return -1; return 0; } /* Validate kamailio.cfg parameters */ int validate_params() { /* if (dbhost.len <= 0) { LM_NOTICE("dbhost must be provided\n"); return 1; } if (dbuser.len <= 0) { LM_NOTICE("dbuser must be provided\n"); return 1; } if (dbname.len <= 0) { LM_NOTICE("dbname must be provided\n"); return 1; } if (dbport == 0) { LM_NOTICE("dbport must be provided\n"); return 1; } if (dbpassword.len <= 0) { dbpassword.s = NULL; return 0; } */ return 0; } /* Kamailio.cfg Function, to get Contacts header and cache it in Redis. */ int mnp_get_contacts( struct sip_msg *msg) { LM_NOTICE("mnp_get_contacts: start\n"); int first_branch; int i; hdr_field_t *contacts = 0; struct cell *t; lnp_contact_t *parsed_contacts; LM_NOTICE("mnp_get_contacts: getting transaction\n"); t = rd_tmb.t_gett(); LM_NOTICE("mnp_get_contacts: getting transaction done\n"); if (t==T_UNDEFINED || t==T_NULL_CELL) { LM_CRIT("no current transaction found\n"); goto error; } for(first_branch = t->nr_of_outgoings - 1; first_branch >= 0; first_branch --) if (t->uac[first_branch].flags & TM_UAC_FLAG_FB) break; if (first_branch < 0) { LM_CRIT("no current first branch found\n"); goto error; } LM_DBG("first branch = %d\n", first_branch); for (i = first_branch; i < t->nr_of_outgoings; i++) { // is it 302 response? if (t->uac[i].last_received != 302) continue; LM_DBG("branch = %d has 302 response\n", i); contacts = get_contact_header(msg, t->uac[i].reply); LM_DBG("mnp_get_contacts: get_contact_header done\n"); if (contacts == 0) { cache_empty_contact_transfer_to_redis(msg); goto error; } parsed_contacts = get_contacts_list(contacts); if (parsed_contacts == 0) { cache_empty_contact_transfer_to_redis(msg); continue; } cache_contact_transfer_to_redis(parsed_contacts, msg); LM_DBG("mnp_get_contacts: add_header\n"); add_lnp_header(msg, parsed_contacts->uri.s); pkg_free(parsed_contacts); LM_DBG("mnp_get_contacts: get_contacts_list done\n"); } LM_DBG("mnp_get_contacts: done\n"); return 1; error: LM_DBG("mnp_get_contacts: something went wrong! \n"); return -1; } /* Get parsed contacts list */ lnp_contact_t *get_contacts_list(hdr_field_t *contact_hdr) { hdr_field_t *hdr; contact_t *parsed_contacts_list; lnp_contact_t *contacts = 0; contacts = pkg_malloc(sizeof(lnp_contact_t)); contacts->next = 0; hdr = contact_hdr; // if (hdr->type != HDR_CONTACT_T) // continue; parsed_contacts_list = ((contact_body_t*)hdr->parsed)->contacts; for (; parsed_contacts_list; parsed_contacts_list = parsed_contacts_list->next) { contacts->uri = parsed_contacts_list->uri; contacts->name = parsed_contacts_list->name; break; contacts->next = pkg_malloc(sizeof(lnp_contact_t)); contacts = contacts->next; } return contacts; } /* Get first contact header. */ hdr_field_t *get_contact_header(struct sip_msg *msg, struct sip_msg *rpl_msg) { static struct sip_msg dup_rpl; struct hdr_field *contact_hdr; struct hdr_field *hdr; contact_t *contacts; contact_hdr = 0; LM_DBG("get_contact_header: start\n"); if (rpl_msg == 0 || rpl_msg == FAKED_REPLY) return NULL; LM_DBG("get_contact_header: rpl_msg check\n"); // contact header is not parsed if (rpl_msg->contact == 0) { if (rpl_msg->msg_flags & FL_SHM_CLONE) { memcpy(&dup_rpl, rpl_msg, sizeof(struct sip_msg)); if (parse_headers(&dup_rpl, HDR_EOH_F, 0) < 0) { LM_ERR("dup_rpl parse error\n"); goto ret; } if (dup_rpl.contact == 0) { LM_DBG("contact header not found in dup_rpl\n"); goto ret; } contact_hdr = dup_rpl.contact; } else { if (parse_headers(rpl_msg, HDR_EOH_F, 0) < 0) { LM_ERR("rpl_msg parse error\n"); goto ret; } if (rpl_msg->contact == 0) { LM_DBG("contact header not found in rpl_msg\n"); goto ret; } contact_hdr = rpl_msg->contact; } } else { contact_hdr = rpl_msg->contact; } LM_DBG("get_contact_header: rpl_msg parsing done\n"); LM_DBG("contact_hdr: %.*s", contact_hdr->body.len, contact_hdr->body.s); // parse body of contact header hdr = contact_hdr; while(hdr) { if (hdr->type == HDR_CONTACT_T) { if (hdr->parsed == 0) { if(parse_contact(hdr) < 0) { LM_ERR("failed to parse Contact body\n"); goto ret; } } } hdr = hdr->next; } LM_DBG("get_contact_header: hdr parsing done\n"); contacts = ((contact_body_t*)contact_hdr->parsed)->contacts; if (contacts == 0) { LM_DBG("No contacts\n"); goto ret; } return contact_hdr; ret: return 0; } int open_mysql_connection() { // MYSQL *con = (MYSQL*)pkg_malloc(sizeof(MYSQL)); // mysql_init(con); // if (!mysql_init(mysql)) { // LM_NOTICE("%s\n", mysql_error(mysql)); // return 1; // } // if (!mysql_real_connect(&mysql, "localhost", "root", NULL, "mor", 3306, NULL, 0)) { // LM_NOTICE("%s\n", mysql_error(&mysql)); // return 1; // } // if (mysql_query(&mysql, "SET NAMES utf8")) { // LM_NOTICE("%s\n", mysql_error(&mysql)); // return 1; // } return 0; } /* Open Redis Connection. */ int open_redis_connection() { local_c = redisConnect(RHOST, REDPORT); if (local_c != NULL && local_c->err) { LM_ERR("Redis Connection Error %s:%d: %s\n", RHOST, REDPORT,local_c->errstr); return 1; } else { redisReply *reply = redisCommand(local_c, "AUTH %s", RPASS); if (reply->type == REDIS_REPLY_ERROR) { LM_NOTICE("Authentication failed"); } freeReplyObject(reply); LM_NOTICE("Connected to Redis\n"); } return 0; } /* Kamailio.cdg function to find contact info in cache and set avp value */ static int m2_mnp(struct sip_msg *msg) { LM_NOTICE("M2_MNP MODULE VERSION: %s", M2_MNP_VERSION); calldata_t *cd = pkg_malloc(sizeof(calldata_t)); cd->msg = msg; cd->cache_code = find_contact_transfer_in_redis(cd); if (cd->cache_code < 0) goto error; if(process_lnp(cd) != 0) goto error; return 1; error: return 0; } /* Check cache and process lnp. */ int process_lnp(calldata_t *cd) { int_str o_val, o_values_avp_name; unsigned short o_values_name_type; if (pv_get_avp_name(cd->msg, &o_pv_values[0].pvp, &o_values_avp_name, &o_values_name_type) < 0) { LM_ERR("cannot get values avp name\n"); goto error; } LM_NOTICE("Proccess LNP"); switch (cd->cache_code) { case CACHE_EMPTY : o_val.s = (str) {AVP_CACHE_EMPTY, strlen(AVP_CACHE_EMPTY)}; break; case CACHE_VALUE_FOUND : add_lnp_header(cd->msg, cd->redis_value); o_val.s = (str) {AVP_CACHE_FOUND, strlen(AVP_CACHE_FOUND)}; break; case CACHE_NO_LNP : o_val.s = (str) {AVP_CACHE_NO_LNP, strlen(AVP_CACHE_NO_LNP)}; } add_avp(o_values_name_type|AVP_VAL_STR, o_values_avp_name, o_val); return 0; error: return -1; } /* Add lnp header to SIP INVITE */ int add_lnp_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; LM_NOTICE("Header was added %s", formated_header); return 1; } /* Find cached contact in redis. */ int find_contact_transfer_in_redis(calldata_t *cd) { char *key; redisReply *reply; char *value; int return_code; key = redis_get_key(cd->msg); reply = redisCommand(local_c, "GET lnp_contacts:%s", key); cd->key = key; switch (reply->type) { case REDIS_REPLY_NIL: LM_NOTICE("Redis has no value, check LNP server\n"); return_code = CACHE_EMPTY; break; case REDIS_REPLY_STRING: value = pkg_malloc(sizeof(char)*(strlen(reply->str) + 1)); strcpy(value, reply->str); cd->redis_value = value; if (check_if_response_with_no_lnp(cd->redis_value)) { return_code = CACHE_NO_LNP; } else { return_code = CACHE_VALUE_FOUND; } break; case REDIS_REPLY_INTEGER: return_code = CACHE_NO_LNP; break; default: freeReplyObject(reply); goto error; } freeReplyObject(reply); pkg_free(key); return return_code; error: pkg_free(key); return -1; } /* */ int check_if_response_with_no_lnp(char *response) { if (strcmp(response, CACHE_REDIS_NO_LNP) == 0) { return 1; } return 0; } /* Redis key formatter */ char *redis_get_key(struct sip_msg *msg) { hdr_field_t *to_header; hdr_field_t *from_header; str key = {0, 0}; char *cmd; cmd = pkg_malloc(sizeof(char) * 256); to_header = get_to_header(msg); if (to_header == 0) goto error; from_header = get_from_header(msg); if (from_header == 0) goto error; key = to_header->body; sprintf(cmd, "%.*s", STR_FMT(&key)); return cmd; error: return 0; } /* 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 0; } /* Get "from" header from SIP INVITE */ hdr_field_t *get_from_header(struct sip_msg *msg) { hdr_field_t *from_header; if (msg == 0) goto error; if (msg->from == 0) { goto error; } else { from_header = msg->from; } return from_header; error: return 0; } /* Cache contact to Redis. */ int cache_contact_transfer_to_redis(lnp_contact_t *contacts, struct sip_msg *msg) { char *key; char cmd[512]; key = redis_get_key(msg); sprintf(cmd, "SET lnp_contacts:%s %.*s>", key, contacts->uri.len, contacts->uri.s); LM_NOTICE("REDIS CMD: %s", cmd); redisCommand(local_c, cmd); return 0; error: return -1; } /* Cache empty contact to Redis. */ int cache_empty_contact_transfer_to_redis(struct sip_msg *msg) { char *key; char cmd[512]; key = redis_get_key(msg); sprintf(cmd, "SET lnp_contacts:%s %s", key, CACHE_REDIS_NO_LNP); LM_NOTICE("REDIS CMD: %s", cmd); redisCommand(local_c, cmd); return 0; error: return -1; } int find_cip_in_redis (calldata_t *cd) { return 0; } void cache_cip_to_redis (calldata_t *cd) { } void add_cip_to_invite(calldata_t *cd) { } /* */ static void mod_destroy(void) { }