仿Drouting在OpenSER中加个Nrouting模块
在《实时检测,自动实现话务转接:OpenSER的Drouting模块应用》中,我们简单讲述了drouting,其实在实际应用中,我可能偏load balancer或dispatcher,毕竟drouting真的要用好的话,也比较难,但有时需要自定义相关的路由,在OpenSER体系中,脚本才是我们最重要的工具,可出错,或折腾来折腾去等很多的问题就跟着出现了,所以我们实现了一个Nrouting模块,用于按主叫,来源Ip,被叫三者进行主叫改号改地址,被叫改号改地址等相关操作。
表结构:
	
	
	
前端界面:
	
	
	
	
	
opensips模块代码(kamailio差不多):
\int nr_mi_param =0;////////////////////////////////////////static str db_url = {NULL,0}; //默认数据库连接,如果指定了系统默认的,那么就用系统默认的,切记,一定一定要用Postgresqlstatic str use_domain = {"0",1};//////////////////////////////////////static int fixup_av_mgm(void** param);static int fixup_nway_qop(void** param);static int fixup_nway_alg(void** param);/////static int mod_init(void);static void mod_destroy(void);static int child_init(int rank);static int nr_route_function(struct sip_msg* msg, char* param, char* value); //路由static int nr_auth_function(struct sip_msg* msg, char* param, char* value); //认证static int nr_challenge_function(struct sip_msg *msg, str *realm, qop_type_t qop, intptr_t algmask); //挑战static int nr_route_media_function(struct sip_msg* msg, char* param, char* value); //从媒体服务器过来static int nr_mi_function(struct mi_root* cmd_root, void* param, unsigned int param_no);mi_response_t *nr_reload_cmd(const mi_params_t *params,struct mi_handler *async_hdl);static cmd_export_t cmds[] = {{"nr_route", (cmd_function)nr_route_function, {{CMD_PARAM_STR|CMD_PARAM_OPT,0,0}, {0,0,0}},REQUEST_ROUTE},{"nr_auth", (cmd_function)nr_auth_function, {{CMD_PARAM_STR, 0, 0},{0,0,0}},REQUEST_ROUTE},{"nr_challenge", (cmd_function)nr_challenge_function, {{CMD_PARAM_STR|CMD_PARAM_OPT, 0, 0}, /* realm */{CMD_PARAM_STR|CMD_PARAM_OPT|CMD_PARAM_FIX_NULL, fixup_nway_qop, 0},/* qop */{CMD_PARAM_STR|CMD_PARAM_OPT|CMD_PARAM_FIX_NULL, fixup_nway_alg, 0},/* alg */{0,0,0}},REQUEST_ROUTE},{"nr_route_media", (cmd_function)nr_route_media_function, {{CMD_PARAM_STR|CMD_PARAM_OPT,0,0}, {0,0,0}},REQUEST_ROUTE},{0,0,{{0,0,0}},0}};static const param_export_t params[] = {{"db_url", STR_PARAM, &db_url.s },{"use_domain", STR_PARAM, &use_domain.s },{0, 0, 0}};struct module_exports exports = {"nrouting",MOD_TYPE_DEFAULT,MODULE_VERSION,DEFAULT_DLFLAGS,0,0,cmds, /* module commands */NULL, /* Exported async functions */params, /* Exported parameters */NULL, /* exported statistics */NULL, /* exported MI functions */NULL, /* exported pseudo-variables */NULL, /* exported transformations */0, /* extra processes */0, /* module pre-initialization function */mod_init, /* module initialization function */0, /* response function */mod_destroy, /* destroy function */child_init, /* child initialization function */NULL /* reload confirm function */};static int child_init(int rank){return 0;}static int mod_init(void){LM_WARN("nrouting: module initialized\n");return 1;}static void mod_destroy(void){LM_INFO("nrouting: module destroyed\n");}static int nr_route_function(struct sip_msg* msg, char* param, char* value){LM_WARN("nr_route_function called with param: %s, value: %s\n", param, value);return 1;}static int nr_auth_function(struct sip_msg* msg, char* domain, char* value){return 1;}static int fixup_nway_qop(void** param){return 0;}static int fixup_nway_alg(void** param){return 0;}//challengestatic char* generate_secret_key() {char *key = (char *)pkg_malloc(33); // 32 bytes + null terminatorif (!key) {return NULL;}// 初始化随机数生成器srand(time(NULL));// 生成32字节的随机数据for (int i = 0; i < 16; i++) {unsigned int random_value = rand();sprintf(&key[i * 2], "%02x", random_value & 0xFF);}key[32] = '\0'; // null terminatorreturn key;}// 生成noncechar* generate_nonce(nway_challenge_t *n) {char *nonce = (char *)malloc(256);if (!nonce) {return NULL;}// 使用当前时间和一个随机数生成noncesrand(time(NULL));int random_value = rand();snprintf(nonce, 256, "%ld:%d:%s", time(NULL), random_value, n->secret_key);return nonce;}//////////////////////////static int nr_challenge_function(struct sip_msg* msg, str *realm, qop_type_t qop, intptr_t algmask){return 1;}static int nr_route_media_function(struct sip_msg* msg, char* param, char* value){return 1;}mi_response_t *nr_reload_cmd(const mi_params_t *params,struct mi_handler *async_hdl){return init_mi_result_ok();}
路由方式:
#### 加载宁卫专有模块loadmodule "nrouting.so"modparam("nrouting","use_domain","0")route{...if (is_method("INVITE") ) {if (nr_route_media()){t_relay();}else if (nr_route()){t_relay();}else{xlog("L_INFO","呼叫来源于非媒体服务,也非网关,进行相关验证!\n");if (nr_auth("")){t_relay();}else{xlog("L_INFO","非法呼叫$fu!\n");}}}
那么以上方式再结合其实一些功能模块,对于OpenSER来说,就可以更好地控制和使用。
以上模块均结合golang实现相关的逻辑,在golang中解析我们的sip消息:
type SIPMessage struct {Method stringURI stringVersion stringHeaders map[string][]string}func NwayParseSIPMessage(message string) SIPMessage {lines := strings.Split(message, "\n")requestLine := strings.Fields(lines[0])sipMessage := SIPMessage{Method: requestLine[0],URI: requestLine[1],Version: requestLine[2],Headers: make(map[string][]string),}for _, line := range lines[1:] {if line == "" {continue}header := strings.SplitN(line, ":", 2)if len(header) == 2 {key := strings.TrimSpace(header[0])value := strings.TrimSpace(header[1])sipMessage.Headers[key] = append(sipMessage.Headers[key], value)}}return sipMessage}
这样在开发时,相对省时省力也性能比较高效。
宁卫通信