Edge脚本
# VERSION=20.05.06.1
#-------------------------------------------------
# variables
# - CF_INNER_IP x.x.x.x
# - CF_PUBLIC_IP x.x.x.x
# - CF_DB mysql://root@xxx:3306/db
# - CF_HOMER x.x.x.x:9060
# - CF_INFLUXDB_URL http://x.x.x.x:port/write?db=dbName
#------------------------------------------------
log_level=3 #log输出等级
log_stderror=no #将opensips的标准错误写入日志yes no
log_facility=LOG_LOCAL0 #控制记录工具 默认工具LOG_LOCAL0
children=4 #定义UDP 或者SCTP接口工作子进程
dns_try_ipv6=no #对ipv6进行查找
auto_aliases=no #自动别名 默认值on
server_header="Server:WSS" #公司的sip代理
user_agent_header="User-Agent:WSS" #以UAC发送请求时生成的User-Agent标头字段的主体
listen=udp:192.168.60.101:18627 as 192.168.60.101:18627
listen=tcp:192.168.60.101:18627 as 192.168.60.101:18627
listen=hep_udp:192.168.60.101:9061
#监听以上端口
#设置模块默认加载地址
mpath="/usr/local/lib64/opensips/modules/"
#基于TCP的SIP通信传输模块,仅提供tcp读取和写入
loadmodule "proto_tcp.so"
#基于udp的sip通信传输模块,仅提供udp
loadmodule="proto_udp.so"
#对数据库进行连接
loadmodule="db_mysql.so"
#SIP信令模块
loadmodule="signaling.so"
#无状态回复
loadmodule="sl.so"
#事务
loadmodule="tm.so"
#如果没有最终的请求答复或否定的INVITE答复的ACK到达,则触发超时
modparam("tm", "fr_timeout", 2)
#记录路由和路由模块
loadmodule "rr.so"
#允许插入两条record-route 默认值1
modparam("rr", "enable_double_rr", 1)
#如果打开,则请求的from-tag会附加到record-route 默认值1
modparam("rr", "append_fromtag", 1)
#通过uri操作模块
loadmodule "uri.so"
#对话框支持模块
loadmodule "dialog.so"
#将对话框的信息从内存中推入数据库 0-no_db ,1-实时任何对话信息更爱理科反映到数据库中,2-延时基于定时器返回数据库,3-仅对话框关闭时才刷新到db
modparam("dialog", "db_mode", 0)
#读取数据库
modparam("dialog", "db_url", "mysql://root:wellcloud@192.168.60.132:3306/core_2_4")
#设置与对话框匹配,0-仅基于DID进行匹配,1-先根据DID尝试匹配,2-基于SIP元素匹配
modparam("dialog","dlg_match_mode",1)
#默认对话框超时时间,毫秒
modparam("dialog","default_timeout",21600)
#设置名称列表默认值为空
modparam("dialog","profiles_with_value","domain")
#最大转接处理器模块
loadmodule "maxfwd.so"
#文本操作模块
loadmodule "textops.so"
#管理接口fifo支持
loadmodule "mi_fifo.so"
#创建用于监听和读取外部命令的FIFO文件名称
modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo")
#分发器模块
loadmodule "dispatcher.so"
#设置数据库
modparam("dispatcher", "db_url", "mysql://root:wellcloud@192.168.60.132:3306/core_2_4")
#ping的方式,默认值options
modparam("dispatcher", "ds_ping_method", "OPTIONS")
#定义将请求发送到故障网关的时间间隔
modparam("dispatcher", "ds_ping_interval", 5)
#网关设置探测模式时的探测次数
modparam("dispatcher", "ds_probing_threshhold", 2)
#控制测试哪些网关以查看它们是否可访问
modparam("dispatcher", "ds_probing_mode", 1)
#SIP操作模块
loadmodule "sipmsgops.so"
#NAT遍历help模块
loadmodule "nathelper.so"
#AVP操作模块
loadmodule "avpops.so"
#设置avp数据库
modparam("avpops", "db_url", "mysql://root:wellcloud@192.168.60.132:3306/core_2_4")
#设置avp所在表名
modparam("avpops", "avp_table", "dbaliases")
#rtp代理模块
#loadmodule "rtpproxy.so"
#modparam("rtpproxy","rtpproxy_sock","udp:CF_INNER_IP:7890")
#rtpengine外部rtp继电器连接器
loadmodule "rtpengine.so"
#连接rtp代理套接字定义
modparam("rtpengine", "rtpengine_sock", "udp:192.168.60.101:7890")
#SIP前端路径支持
loadmodule "path.so"
#评估第一个Route URI 默认值0
modparam("path", "use_received", 1)
#嵌入式HTTP服务器
loadmodule "httpd.so"
#管理接口的http支持
loadmodule "mi_http.so"
#多域支持模块
loadmodule "domain.so"
#数据库支持
modparam("domain", "db_url","mysql://root:wellcloud@192.168.60.132:3306/core_2_4")
#数据库模式:0表示不缓存,1表示缓存 默认值0
modparam("domain", "db_mode", 0)
#动态路由
loadmodule "drouting.so"
#是否使用域匹配,默认值是1
modparam("drouting", "use_domain", 1)
#数据库支持
modparam("drouting", "db_url","mysql://root:wellcloud@192.168.60.132:3306/core_2_4")
#HTTP客户端实现
loadmodule "rest_client.so"
#允许最长时间(秒),默认20
modparam("rest_client", "curl_timeout", 5)
#最大连接时间(秒),默认20
modparam("rest_client", "connection_timeout", 3)
#emmm,没找到该模块?
loadmodule "statistics.so"
#HEP协议模块
loadmodule "proto_hep.so"
#指定HEP数据包的目的地和所使用的HEP协议的版本
modparam("proto_hep", "hep_id","[hep_dst] 192.168.40.79:9060;transport=udp;version=3")
#SIPTrace模块
loadmodule "siptrace.so"
#指定跟踪的目的地
modparam("siptrace", "trace_id","[tid]uri=hep:hep_dst")
#设置use_domain域是1
modparam("auth_db|usrloc|uri", "use_domain", 1)
route[r_invite_to_core]{
if(is_method("INVITE")){
#$cfg_line当前脚本运行行;$cs CSeq number;$rm request method;$fu From Uri,$tu To Uri
xlog("$cfg_line-$cs $rm $fu->$tu r_invite_from_out");
#该函数为当前处理的请求创建对话框。该请求必须是初始请求。B是超时再见
#如果创建失败,返回500,超时发送bye
if(!create_dialog("B")){
xlog("$cfg_line-$cs $rm $fu->$tu exec send_reply 500 Internal Server Error");
send_reply("500","Internal Server Error");
exit;
}
#如果含有sdp
if(has_body("application/sdp")){
xlog("$cfg_line-$cs $rm $fu->$tu exec rtpproxy_offer!");
#rtpproxy_offer("oc","CF_INNER_IP");
#重写sdp,使媒体能通过rtp传递。ICE=remove丢弃并且不插入任何ice值
rtpengine_offer("ICE=remove");
#记录rtp流
rtpengine_start_recording();
t_on_reply();
}
#为当前处理的消息设置一个标志
setflag(10);
#通过dispatch查找组1,运用0的计算方法
if(!ds_select_dst("1","0")){
send_reply("502","Service Unavailable");
exit;
}
}
#判断invite请求是否来自core
route[r_invite_from_core]{
if(ds_is_in_list("$si","$sp","1")){
xlog(" enter in fs callout >>>");
#just select gateway by request_url
if(is_method("INVITE") && has_body("appkication/sdp")){
xlog("exec rtpproxy_offer!");
#rtpproxy_offer("ro","CF_PUBLIC_IP");
#重写sdp,使媒体能通过rtp传递。ICE=remove丢弃并且不插入任何ice值
rtpengine_offer("ICE=remove");
#记录rtp
rtpengine_start_recording();
t_on_reply("r_rw_sdp");
}
#交由opensips处理
t_relay();
exit;
}
}
#这部分有点问题
route[r_seq_request]{
#是否有tag标签,是否不是notify 通知所有用户
if(has_totag()&&!is_method("NOtIFY")){
#失去了route,不清楚
if(loose_route()){
#没找到,有application/sdp
if(has_body("application/sdp")){
#通过dispatch判断
if(ds_is_in_list("$si","$sp","1")){
#rtpproxy_offer("ro","CF_PUBLIC_IP");
rtpengine_offer("ICE=remove");
}else{
#rtpproxy_offer("oc","CF_INNER_IP");
#重写sdp,使媒体能通过rtp传递。ICE=remove丢弃并且不插入任何ice值
rtpengine_offer("ICE_reomver");
}
#记录
rtpengine_start_recording();
if(is_method("INVITE")){
t_on_reply("r_rw_sdp");
}
}
#如果是bye,结束rtp代理
if(is_method("BYE")){
#rtpproxy_unforce();
rtpengine_delete();
}
#该功能对照其所属的对话框(内部数据)检查当前收到的请求。
#$DLG_status==null说明没有dialog。
if($DLG_status!=NULL && !validate_dialog()){
xlog("In-Dialog $rm from $si (callid=$ci) is not valid according to dialog\n")
#该函数强制对话框中的SIP消息包含ruri,路由头和dst_uri,防止虚假注入的恶意请求如bye
fix_route_dialog();
}
t_relay();
exit;
}else{
#ACK|BYE方法判段;判段当前是否与事务关联
if(is_method("ACK|BYE") && t_check_trans()){
t_relay();
exit;
}
#stateless无状态回复
sl_send_reply("404","Not here");
}
exit;
}
}
route[r_fix_nat_contact]{
#19表示1+2+16,其中1、2、16分别代表了不同的NAT检测条件,重要参考地址
#https://www.yuque.com/wangdd/opensips/olt40x
#测试请求是否源于nat
if(nat_uac_test("19")){
xlog("find a NAT address,fix it...si=$si sp=$sp rm=$rm");
#重写URI Contact HF以包含请求的源地址
#Rewrites the URI Contact HF to contain request's source address
fix_nated_contact();
}
route[r_check_ip]{
#对invite判断;是否来自domain表;
if(is_method("INVITE")&&!is_from_local()&&!ds_is_in_list("$si","$sp","1")){
#是否来自某个网关 gateway,-1全部检查,n忽略端口
if(!is_from_gw("-1","n")){
xlog("L_ERR","caller is not local and Not in whrite list!");
#SIP信令模块发送reply,403
send_reply("403","Forbidden IP");
}
}
}
route[r_cancel]{
#对cancel方法进行判断
if(is_method("CANCEL")){
#判断当前是否与事物transaction关联
if(t_check_trans()){
#交由opensips处理
t_relay();
}
exit;
}
}
route[r_notify]{
#方法为notify,通知所有用户事件;根据domain表查询;通过dispatcher表查询source IP,source port,组号
if(is_method("NOTIFY")&&!is_from_local()&&!ds_is_in_list("$si","$sp","1")){
#是否来自某个网关 gateway,-1全部检查,n忽略端口
if(!is_from_gw("-1","n")){
xlog("L_ERR","caller is not local and Not in whrite list!");
#SIP信令模块发送reply,403
send_reply("403","Forbidden IP");
}
}
}
#对options进行判断,查询服务器的能力
route[r_options]{
if(is_method("OPTIONS")){
#stateless 无状态回复
sl_send_reply("200","ok");
exit();
}
}
#修改User-Agent防止被攻击
route[r_hide_fs_ua]{
#User-Agent header
if($ua=~'^FreeSWITCH'){
#remove head from
remove_hf("User-Agent");
#添加头
append_hf("User-Agent:WSS\r\n","CSeq");
}
}
route[r_check_maxfwd]{
#判断最大转接数
if(!mf_prcess_maxfwd_head("10")){
#483 过多转接退出opensips
sl_send_reply("483","Too Many Hops");
exit;
}
}
#对确定地址的数据进行写入数据库操作
rout[r_homer_capture]{
if(is_method("INVITE|ACK|BYE")){
xlog("<<<<<<<<<<welcome new request>>>>>>>>>>>>>>");
#$rm request method, $fu from uri,$tu to uri,$cs Csq number
xlog("$rm $fu->$tu $cs");
#sip capture
$var(enableHomer)="192.168.40.79:9060";
#has_totag确定是否有头,是否为初始请求
if($var(enableHomer) && !has_totag()){
#https://opensips.org/docs/modules/2.4.x/siptrace.html#func_sip_trace
#在数据库中存储或重新部署当前已处理的SIP消息,事务或对话框
#指定跟踪位置 的trace_id的名称,
#d'/'D'跟踪对话框(m'/'M'跟踪消息,'t'/'T'跟踪交易。),
#sip-启用sip消息跟踪;xlog-启用在当前范围内(日志,事务或消息)的xlog消息跟踪;休息-启用休息消息跟踪;
sip_trace("tid","d,"sip|xlog|rest");
}
}
}
#回复路由
onreply_route[r_rw_sdp]{
#修复nat contact头
fix_nated_contact();
#如果是bye或者cancel 卸下rtp代理
if(is_method("BYE|CANCEL")){
#rtpproxy_unforce();
rtpengine_delete();
}
#如果带有sdp信息
if(has_body("application/sdp")){
xlog("onreply_route1 rtpproxy_answer----si=$si");
#如果源地址在记录中
if(ds_is_in_list("$si","$sp","1")){
#rtpproxy_answer("ro","CF_PUBLIC_IP");
#重写sdp头,使信息能通过rtp代理
rtpengine_answer();
#这句代码不知道什么意义
replace_all("sip:192.168.60.101","sip:192.168.60.101");
}else{
#rtpproxy_answer("oc","CF_INNER_IP");
#这里ifelse好像没什么意义
rtpengine_answer();
replace_all("sip:192.168.60.101","sip:192.168.60.101");
}
}
}
onreply_route{
route(r_hide_fs_ua);
}
route{
#将rport参数添加到第一个Via标头(使sip消息能够正确返回)
force_rport();
#对确定的地址进行消息跟踪
route(r_homer_capture);
#确定转接是否超过界限,是,退出opensips主机
route(r_check_maxfwd);
#修改User-Agent防止被攻击
route(r_hide_fs_ua);
#对options进行无状态回复
route(r_options);
#通知所有事件
route(r_notify);
#对cancel(取消任何悬而未决的请求)
route(r_cancel);
#对invite请求判断是否来自fs
route(r_check_ip);
#修复contact头,实现sip信息的nat穿透
route(r_fix_nat_contact);
#请求判断?
route(r_seq_request);
#记录返回地址
record_route();
#判断invite请求是否来自core
route(r_invite_from_core);
#处理发送core的invite请求
route(r_invite_to_core);
#
route(r_register);
#是否进入opensips操作
if(!t_relay()){
xlog("L_ERROR","reply error!!");
#stateless无协议回复错误
sl_reply_error();
}
}
core脚本问题
# VERSION=core-2.4.7.21
log_level=3
log_stderror=yes
log_facility=LOG_LOCAL0
children=4
auto_aliases=no
server_header="Server:WSS"
user_agent_header="User-Agent:WMS"
listen=udp:192.168.40.20:18627
listen=hep_udp:192.168.40.20:9060
mpath="/usr/local/lib64/opensips/modules/"
loadmodule "proto_udp.so"
loadmodule "proto_tcp.so"
loadmodule "signaling.so"
loadmodule "regex.so"
loadmodule "sl.so"
loadmodule "tm.so"
loadmodule "rr.so"
loadmodule "uac.so"
loadmodule "maxfwd.so"
loadmodule "sipmsgops.so"
loadmodule "mi_fifo.so"
loadmodule "dispatcher.so"
loadmodule "rest_client.so"
loadmodule "uri.so"
loadmodule "db_mysql.so"
loadmodule "textops.so"
loadmodule "usrloc.so"
loadmodule "registrar.so"
loadmodule "acc.so"
loadmodule "auth.so"
loadmodule "auth_db.so"
loadmodule "alias_db.so"
loadmodule "domain.so"
loadmodule "topology_hiding.so"
loadmodule "dialog.so"
loadmodule "nathelper.so"
loadmodule "carrierroute.so"
loadmodule "avpops.so"
loadmodule "dialplan.so"
loadmodule "permissions.so"
loadmodule "drouting.so"
loadmodule "group.so"
loadmodule "load_balancer.so"
loadmodule "httpd.so"
loadmodule "mi_http.so"
loadmodule "mi_json.so"
loadmodule "path.so"
loadmodule "exec.so"
loadmodule "statistics.so"
loadmodule "cachedb_local.so"
#loadmodule "cachedb_redis.so"
loadmodule "proto_hep.so"
loadmodule "siptrace.so"
modparam("tm", "fr_timeout", 5)
modparam("tm", "fr_inv_timeout", 60)
modparam("tm", "restart_fr_on_each_reply", 0)
modparam("tm", "onreply_avp_mode", 1)
modparam("dispatcher", "db_url", "mysql://root:111111@192.168.40.191:3306/core_2_4")
modparam("dispatcher", "ds_ping_method", "OPTIONS")
modparam("dispatcher", "ds_ping_interval", 5)
modparam("dispatcher", "ds_probing_threshhold", 2)
modparam("dispatcher", "hash_pvar", "$avp(select_fs_strategy_hash)")
modparam("dispatcher", "ds_probing_mode", 1)
modparam("rr", "append_fromtag", 1)
modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo")
modparam("mi_fifo", "fifo_mode", 0666)
modparam("uri", "use_uri_table", 0)
modparam("uri", "db_url","mysql://root:111111@192.168.40.191:3306/core_2_4")
modparam("usrloc", "nat_bflag", "NAT")
modparam("usrloc", "working_mode_preset","sql-only")
modparam("usrloc", "db_url","mysql://root:111111@192.168.40.191:3306/core_2_4") # CUSTOMIZE ME
modparam("registrar", "tcp_persistent_flag", "TCP_PERSISTENT")
modparam("registrar", "received_avp", "$avp(received_nh)")
modparam("registrar", "max_contacts", 1)
modparam("registrar", "min_expires", 120)
modparam("registrar", "max_expires", 180)
modparam("registrar", "default_expires", 150)
modparam("registrar", "attr_avp", "$avp(attr)")
modparam("acc", "early_media", 0)
modparam("acc", "report_cancels", 1)
modparam("acc", "detect_direction", 1)
modparam("acc", "db_url","mysql://root:111111@192.168.40.191:3306/core_2_4") # CUSTOMIZE ME
modparam("auth_db", "calculate_ha1", yes)
modparam("auth_db", "password_column", "password")
modparam("auth_db", "db_url","mysql://root:111111@192.168.40.191:3306/core_2_4") # CUSTOMIZE ME
modparam("auth_db", "load_credentials", "")
modparam("alias_db", "db_url","mysql://root:111111@192.168.40.191:3306/core_2_4") # CUSTOMIZE ME
modparam("domain", "db_url","mysql://root:111111@192.168.40.191:3306/core_2_4") # CUSTOMIZE ME
modparam("domain", "db_mode", 0) # 0: witchout cache, 1: Use caching
modparam("auth_db|usrloc|uri", "use_domain", 1)
modparam("dialog", "dlg_match_mode", 1)
modparam("dialog", "default_timeout", 7200)
modparam("dialog", "db_mode", 0) # 0:no_db, 1:realtime, 2:delayed, 3:shutdown
modparam("dialog", "profiles_with_value", "domain")
modparam("dialog", "db_url","mysql://root:111111@192.168.40.191:3306/core_2_4") # CUSTOMIZE ME
#modparam("dialog", "cachedb_url", "redis://CF_REDIS_DB/")
modparam("nathelper", "natping_interval", 10)
modparam("nathelper", "ping_nated_only", 1)
modparam("nathelper", "received_avp", "$avp(received_nh)")
modparam("carrierroute", "db_url", "mysql://root:111111@192.168.40.191:3306/core_2_4")
modparam("carrierroute", "config_source", "db")
modparam("carrierroute", "use_domain", 1)
modparam("carrierroute", "db_failure_table", "carrierfailureroute")
modparam("avpops", "db_url", "mysql://root:111111@192.168.40.191:3306/core_2_4")
modparam("avpops", "avp_table", "dbaliases")
modparam("dialplan", "db_url", "mysql://root:111111@192.168.40.191:3306/core_2_4")
modparam("drouting", "use_domain", 1)
modparam("drouting", "db_url","mysql://root:111111@192.168.40.191:3306/core_2_4")
modparam("group", "use_domain", 1)
modparam("group", "db_url", "mysql://root:111111@192.168.40.191:3306/core_2_4")
modparam("load_balancer", "db_url", "mysql://root:111111@192.168.40.191:3306/core_2_4")
modparam("load_balancer", "probing_reply_codes", "501, 403")
modparam("load_balancer", "probing_interval", 30)
modparam("rr", "enable_double_rr", 1)
modparam("path", "use_received", 1)
modparam("exec", "time_to_kill", 10)
modparam("rest_client", "curl_timeout", 5)
modparam("statistics", "stat_groups", "wj")
modparam("proto_hep", "hep_id","[hep_dst] 192.168.60.228:9060;transport=udp;version=3")
modparam("siptrace", "trace_id","[tid]uri=hep:hep_dst")
modparam("cachedb_local", "cachedb_url", "local://")
modparam("cachedb_local", "cache_clean_period", 120)
route{
#在Via头中添加rport参数使sip消息能够正确返回
force_rport();
#跟踪记录注册的sip信息
route(r_siphub_capture);
#不支持PUBLISH|SUBSCRIBE方法进行
route(r_not_support);
#内存溢出提示
route(r_options);
#修复notify请求下$rU request username 为空的情况
route(r_fix_notify);
#修复nat问题,
route(r_nat_uac_test);
#过滤回环
route(r_max_fwd);
#匹配现有对话,如果存在对话使用t_repley并结束
route(r_fix_route_dialog);
xlog("$cfg_line-$cs $rm $fu->$tu processing \n");
#处理消息
route(r_relay_seq_request);
#为cancel方法,
if (is_method("CANCEL")) {
if (t_check_trans())
t_relay();
exit;
}
#如果过是重传请求则中断脚本
t_check_trans();
if (!is_method("REGISTER")) {
if (is_from_local()) {
#使用子路由,本地请求入口
route(r_author_request_from_edge);
} else {
# is not from fs, may be from edge
#非本地请求,调用子路由
if (!lb_is_destination("$si","$sp","1") ) {
route(r_did);
}
}
}
#抛弃表头
loose_route();
#方法是REGISTER|MESSAGE记录返回地址
if (!is_method("REGISTER|MESSAGE")){
record_route();
}
#方法是INVITE发送消息退出
if (is_method("INVITE")) {
if ( !create_dialog("B") ) {
xlog("create dialog Error \n");
send_reply("500","Internal Server Error");
exit;
}
route(r_do_accounting);
}
#方法是PUBLIST|SUBSCRIBE
if (is_method("PUBLISH|SUBSCRIBE")){
sl_send_reply("503", "Service Unavailable");
exit;
}
# handle registerr_registerr_register
route(r_register);
if ($rU==NULL) {
xlog("rU is null \n");
sl_send_reply("484","Address Incomplete");
exit;
}
#是本地domain表
if (is_from_local()){
avp_db_query("SELECT limit_num,trunk_group FROM domain WHERE domain='$fd'","$avp(t_limit);$avp(t_trunk_group)");
if(is_avp_set("$avp(t_limit)") && $avp(t_limit)>0 ){
xlog("$fd limit $avp(t_limit) !\n");
$var(t_domain) = $fd;
route(r_do_limit, $var(t_domain), $avp(t_limit));
}
#记录返回头
if (is_method("INVITE") && has_body("application/sdp")){
record_route();
}
#回复路由
t_on_reply("r_delete_trunk_info");
#平衡配置
if (!load_balance("1","trunk")) {
xlog("load_balance failed: check fs is ok\n");
#无状发送消息
sl_send_reply("500","Service full.");
exit;
}
t_relay();
} else {
$rd = $td;
#查询本地路由
if (!lookup("location","m")) {
#判断数据库中是否存在
if (!db_does_uri_exist()) {
# -1 outbound reg: [56789][0-9]*
if($rU=~'^[56789][0-9]+$'){
$avp(call_origin_rU)=$rU;
route(r_query_route);
if(!is_avp_set("$avp(trunk_group)")) {
sl_send_reply("402", "no trunk group");
exit();
}
#移除标头
remove_hf("P-Preferred-Identity");
remove_hf("P-Asserted-Identity");
xlog("begin route... carrier=$avp(carrier) trunk_group=$avp(trunk_group) request_username=$rU host=$avp(host)\n");
#通过在初始请求上调用此函数,模块将隐藏拓扑,这意味着它将剥离并还原所有Via,Record-Route和Route标头,并将联系人替换为请求所在接口的IP地址。收到。
topology_hiding();
#将所有"WSS"替换为Freeswitch
replace_all("FreeSWITCH", "WMS");
#此函数在给定的载波树中的给定域中搜索在prefix_matching中给定的用户的最长匹配项
if(!cr_route("carrier1","$avp(trunk_group)", "$rU", "$rU", "call_id", "$avp(host)")){
xlog("L_ERR","cr_route failed! send reply 485.\n");
sl_send_reply("485", "route ambiguous!");
}
else {
#setflag(ACC_DO);
#setflag(ACC_MISSED);
# In case of failure, re-route the request
$avp(outbound_rU) = $rU;
route(r_find_real_rU);
$var(new_to_same) = "sip:"+ $rU + "@" + $td;
#替换to标头中的uri
uac_replace_to("$rU", "$var(new_to_same)");
#使用failure,reply的分支路由
t_on_failure("missed_call");
t_on_reply("r_add_trunk_info");
route(r_set_edge_ip);
t_relay();
}
exit;
}
else{
xlog("L_WARN","send reply 420 Bad Extension ! $ru\n");
send_reply("420","Bad Extension");
exit;
}
}
else{
if (is_method("INVITE")) {
route(r_ext_bind_phone);
}
#为当前处理的SIP请求创建SIP事务
t_newtran();
xlog("L_WARN","404 not found !\n");
t_reply("404", "Not Found");
exit;
}
}
#
if (isbflagset(10))
setflag(10);
#如果没有tag
if(!has_totag() && is_method("INVITE")) {
#隐藏拓扑
topology_hiding();
}
replace_all("FreeSWITCH", "WMS");
route(relay);
}
}
onreply_route[r_add_trunk_info]{
if($avp(trunk_group)){
append_hf("X-trunk-name: $avp(trunk_group)\r\n");
append_hf("X-calltype: callout\r\n");
}
if($avp(host)){
append_hf("X-th: $avp(host)\r\n");
}
if($avp(outbound_tU)){
append_hf("X-tu: $avp(outbound_tU)\r\n");
}
if($avp(x_r_rU)){
append_hf("X-ru: $avp(x_r_rU)\r\n");
}
}
route[r_fix_notify]{
#request Uri username
if (is_method("NOTIFY") && $rU==NULL) {
$rU=$tU;
}
}
route[r_options]{
#内存超过10M,返回503提示
if(is_method("OPTIONS")) {
if ($stat(free_size) < 10240000) {
sl_send_reply("503", "free_size_limit");
} else {
sl_send_reply("200", "ok");
}
exit();
}
}
route[r_nat_uac_test]{
#解决信令问题,符合1、2、16情况,参考https://www.yuque.com/wangdd/opensips/olt40x
if (nat_uac_test("19")) {
if (is_method("REGISTER")) {
fix_nated_register();
setbflag(10);
} else {
# this function will cause ext cannot drop
# fix_nated_contact();
setflag(10);
}
}
}
route [r_fix_route_dialog] {
#是否含有tag标签,是否是本地,是否属于以下方法
if (has_totag() && is_myself("$rd") && is_method("INFO|INVITE|ACK|BYE|UPDATE|REFER")){
#匹配现有对话
if(match_dialog()){
#会话存在且合法
if ( $DLG_status!=NULL && !validate_dialog() ){
#强制对话框,防止虚假注入的对话请求
fix_route_dialog();
}
#发送请求
t_relay();
exit;
}
}
}
#判断数据跳过多
route [r_max_fwd] {
if (!mf_process_maxfwd_header("10")) {
sl_send_reply("483","Too Many Hops");
exit;
}
}
route [r_relay_seq_request] {
#是否有tag标签,并且不是notify请求
if (has_totag() && !is_method("NOTIFY")) {
#将所有WMS替换为FreeSwitch
replace_all("FreeSWITCH", "WMS");
#判断route请求是否丢失
if (loose_route()) {
xlog("L_INFO","r_relay_seq_request: $rm $ru enter loose route");
if (is_method("INVITE")) {
record_route();
} else {
t_relay();
exit;
}
#检查route头是否与给定正则表达式匹配,判断nat是否开启,开启设置flag为10
if (check_route_param("nat=yes")){
setflag(10);
}
#调用relay子路由
route(relay);
} else {
#判断是否存在invite事务,且方法为ACK和BYE,发送请求
if(t_check_trans() && is_method("ACK|BYE")){
t_relay();
} else {
exit;
}
sl_send_reply("404","Not here");
}
exit;
}
}
#
route[r_author_request_from_edge]{
route(r_not_register_h5);
#检查给定的IP和PORT是否属于负载均衡器列表中配置的目标
if(!lb_is_destination("$si","0","1")){
if (!proxy_authorize("", "subscriber")) {
xlog("$cfg_line-$cs $rm $fu->$tu start authorize\n");
proxy_challenge("", "0");
exit;
}
#Auth whole username
if($aU =~'^gateway'){
#删除认证头
consume_credentials();
if (!load_balance("1","trunk")) {
xlog("L_ERR","load_balance:1,trunk is failed !!,exec sl_send_reply:500 Service full\n");
sl_send_reply("500","Service full");
exit;
}
record_route();
t_relay();
exit();
}else{
if (!db_check_from()) {
sl_send_reply("403","Forbidden auth ID");
exit;
}
consume_credentials();
}
}
}
route[r_did]{
#对数据库进行查询赋值给三个数据
avp_db_query("SELECT username,domain,select_fs_strategy FROM dbaliases WHERE alias_username='$rU'","$avp(t_username);$avp(t_domain);$avp(select_fs_strategy)");
# is did number from edge
#检查是否设置了t_username
if(is_avp_set("$avp(t_username)")){
# this will exit
#赋值
$avp(select_fs_strategy_hash) = $rU;
$rU = $avp(t_username);
xlog("find did corrt_username $avp(t_username) $avp(t_domain) $fu $tu ru=$ru\n");
#$ru request uri设置该地址
$ru = "sip:"+$avp(t_username) + "@" + $avp(t_domain) + ";transport="+ $pr;
xlog("repair after: ru=$ru\n");
$avp(from_ru) = "sip:"+ $fU + "@" + $avp(t_domain);
$avp(to_ru) = "sip:"+ $tU + "@" + $avp(t_domain);
xlog("from_ru=$avp(from_ru) to_ru=$avp(to_ru)\n");
#替换from标头中的uri部分
uac_replace_from("$fU" , "$avp(from_ru)");
#替换to标头红的uri部分
uac_replace_to("$tU" , "$avp(to_ru)");
# ces user x-trunkgroup header to set calltype for inbound
# so do't remove it
#添加表头
append_hf("X-trunk-name: $si\r\n");
append_hf("X-calltype: callin\r\n");
#记录返回地址
record_route();
if ($avp(select_fs_strategy) == "d") {
# 3 meaning fs group
# 7 hash over hash_pvar
xlog("$cfg_line <r_did> $rm $fu->$tu selec fs use dispatcher\n");
#dispatch查找组名中为3的地址
if (!ds_select_dst("3","7")) {
send_reply("502","Service Unavailable");
exit;
}
t_relay();
exit;
}
xlog("$cfg_line <r_did> $rm $fu->$tu selec fs use load_balance\n");
#平衡配置
if (!load_balance("1","trunk")) {
xlog("L_ERR","load_balance:1,trunk is failed !!,exec sl_send_reply:500 Service full\n");
sl_send_reply("500","Service full");
exit;
}
t_relay();
exit();
}
# invite from fs will continue route
}
route [r_register] {
if (is_method("REGISTER")){
#?
route(r_register_h5);
#认证auth_code
$var(auth_code) = www_authorize("", "subscriber");
if ( $var(auth_code) == -1 || $var(auth_code) == -2 ) {
xlog("L_NOTICE","Auth error for $fU@$fd from $si cause $var(auth_code)\n");
}
if ( $var(auth_code) < 0 ) {
www_challenge("", "0");
exit;
}
#根据uri表检查数据库
if (!db_check_to()) {
xlog("exec sl_send_reply:403 Forbidden auth ID\n");
sl_send_reply("403","Forbidden auth ID");
exit;
}
#设置一个标签
if ( $pr=="TCP" || 0 ) setflag(TCP_PERSISTENT);
$avp(attr)="sip:"+$si+":"+$sp;
#如果一个文件中存在邮箱present
if(is_present_hf("X-Enable-Kickout")){
#移除head from
remove_hf("X-Enable-Kickout");
#保存于location表中
if (!save("location","p1vf")){
xlog("exec sl_reply_error\n");
sl_send_reply("503","too many registered");
}
}
#如果过注册了来自某个AOR的联系人
else if (is_contact_registered("location","$fu",,"$ci")) {
xlog("[r_register] $fu is already, use force register");
if (!save("location","p1vf")){
sl_send_reply("503","failed");
}
}
else{
if (!save("location","p1v")){
xlog("exec sl_reply_error\n");
sl_send_reply("503","too many registered");
}
}
#$fU ffrom URI username
if($fU =~"^gateway"){
xlog("gateway>> update , $ct.fields(uri) si=$si sp=$sp fu=$fu tu=$tu ru=$ru\n");
$var(rw_uri)=$ct.fields(uri);
$var(ipstr)=$(var(rw_uri){s.select,1,@});
$avp(ctip)=$(var(ipstr){s.select,0,;});
xlog("gateway>> SELECT id, rewrite_host FROM carrierroute WHERE uri_username='$fU' and uri_domain='$fd' and rewrite_host!='$avp(ctip)'\n");
#数据库查询语句
avp_db_query("SELECT id, rewrite_host FROM carrierroute WHERE uri_username='$fU' and uri_domain='$fd' and rewrite_host!='$avp(ctip)'","$avp(lid);$avp(old_rh)");
xlog("gateway>> update $avp(ctip) fU=$fU uri_domain=$fd lid=$avp(lid), $var(old_rh)\n");
#avp是否设置
if(is_avp_set("$avp(lid)")){
xlog("gateway>> update rewrite_host : $avp(old_rh) > $avp(ctip)\n");
avp_db_query("update carrierroute set rewrite_host='$avp(ctip)' WHERE uri_username='$fU' and uri_domain='$fd' ");
update_stat("wj:update_gateway_count", "+1");
}
}
exit;
}
}
route[r_do_limit] {
#将当前对话框插入配置文件
set_dlg_profile("domain","$param(1)");
#返回属于配置文件的对话框数
get_profile_size("domain","$param(1)","$var(domain)");
xlog("L_INFO","r_do_limit: User $param(1) has $var(domain) ongoing calls so far, limit is $param(2)\n");
# check within limit
if( $var(domain)>$param(2) ) {
xlog("L_WARN","do_limit: user $param(1) exceeded number of calls [$var(domain)/$param(2)]\n");
sl_send_reply("487", "Request Terminated: Channel limit exceeded\n");
exit;
# terminating this call will automatically remove the call from the profile
}
# call was added to the profile without exceeding the limit, simply continue
}
route[r_query_route]{
xlog("L_INFO","query route fU=$fU rd=$rd\n");
$var(extNumber)="";
$var(extRE)="^8[0-9]{3,7}$";
if ($fU=~$var(extRE)) {
$var(extNumber)=$fU;
} else if ($tU=~$var(extRE)) {
$var(extNumber)=$tU;
# fix no condition transfer 302 aws vos
#uac_replace_to("$tU" , "$rU");
}
if ($var(extNumber)) {
avp_db_query("SELECT id,caller_id_dpid,callee_id_dpid,trunk_group FROM wj_v_exten_route WHERE username='$var(extNumber)' and domain='$rd'","$avp(r_id);$avp(caller_id_dpid);$avp(callee_id_dpid);$avp(trunk_group)");
}
# query ext route
xlog("db_query_ext_route_result: $var(extNumber) $avp(r_id);$avp(caller_id_dpid);$avp(callee_id_dpid);$avp(trunk_group)\n");
# query ext route by tu@t
if(!is_avp_set("$avp(r_id)")){
#对数据库进行查询
avp_db_query("SELECT id,caller_id_dpid,callee_id_dpid,trunk_group FROM wj_v_domain_route WHERE domain='$rd'","$avp(r_id);$avp(caller_id_dpid);$avp(callee_id_dpid);$avp(trunk_group)");
xlog("db_query_domain_route_result: $avp(r_id);$avp(caller_id_dpid);$avp(callee_id_dpid);$avp(trunk_group)\n");
}
xlog("db_query_route_result : $avp(r_id);$avp(caller_id_dpid);$avp(callee_id_dpid);$avp(trunk_group)\n");
if(is_avp_set("$avp(r_id)")){
$var(nto)="";
$var(nfrom)="";
# change to header
if ($hdr(bindPhone)) {
#将尝试根据拨号规则ID等于id的转换规则将src字符串转换为dest字符串
dp_translate("$avp(callee_id_dpid)","$hdr(bindPhone)/$var(nto)");
} else if ($tU=~$var(extRE)) {
dp_translate("$avp(callee_id_dpid)", "$rU/$var(nto)");
}else {
dp_translate("$avp(callee_id_dpid)","$tU/$var(nto)");
}
xlog("$cfg_line-$cs $rm $fu->$tu in request nto=$var(nto)\n");
if($var(nto)){
$var(new_to) = "sip:"+ $var(nto) + "@" + $rd;
$avp(outbound_tU) = $var(nto);
xlog("$cfg_line-$cs $rm $fu->$tu in request outbound_tU=$avp(outbound_tU)\n");
#uac_replace_to("$tU" , "$var(new_to)");
}
# change from header
if ($hdr(displayOrigin)) {
$var(nfrom)=$hdr(displayOrigin);
} else {
dp_translate("$avp(caller_id_dpid)","$fU/$var(nfrom)");
}
if($var(nfrom)){
$var(new_uri) = "sip:" +$var(nfrom)+ "@" + $fd;
#对from的uri进行替换
uac_replace_from("$var(nfrom)" , "$var(new_uri)");
}
remove_hf("Remote-Party-ID");
remove_hf("displayOrigin");
remove_hf("bindPhone");
}
}
route [r_set_edge_ip] {
#该函数从类似于存储器高速缓存的存储系统中获取属性的值
cache_fetch("local","cache_$avp(host)", $avp(edge));
#是否判断函数是否存在
if(is_avp_set("$avp(edge)")){
xlog("L_INFO","cache_fetch find host $avp(host) $avp(edge), no need sql query");
}else {
xlog("L_INFO","cache_fetch host not find, need sql query");
avp_db_query("SELECT edge_ip FROM carrierroute WHERE rewrite_host='$avp(host)' and edge_ip is not null order by rand() limit 1","$avp(edge)");
}
if(is_avp_set("$avp(edge)")){
xlog("L_INFO","r_set_edge_ip $ci edge addr is $avp(edge)");
cache_store("local","cache_$avp(host)", "$avp(edge)");
if ($avp(edge)=~'^sip') {
$du=$avp(edge);
} else {
# use dispatcher send to edge just for trunk
xlog("L_INFO", "r_set_edge_ip $ci use dipatcher to select edge");
if (!ds_select_dst("$avp(edge)","0")) {
send_reply("502","Service Unavailable");
cache_remove("loacal","cache_$avp(host)");
exit;
}
}
}
else{
avp_db_query("SELECT attr FROM location WHERE username='$fU' and domain='$td'","$avp(edge)");
if(is_avp_set("$avp(edge)")){
$du=$avp(edge);
}else{
xlog("L_ERR","$fU@$td is Not Registed!!\n");
}
}
}
route[r_ext_bind_phone]{
$avp(transfer_num) = $hdr(bindPhone);
if (!$avp(transfer_num)) {
avp_db_query("SELECT bind_mobile,bind_mobile_type from subscriber WHERE username='$tU' and domain='$td'","$avp(transfer_num);$avp(bmt)");
xlog("L_INFO","r_ext_bind_phone bind_mobile=$avp(transfer_num)\n");
}
if($avp(bmt)=="disabled"){
$avp(transfer_num):="";
}
if($avp(transfer_num) != "" ){
$du="sip:"+$si+":"+$sp;
$ru="sip:"+$avp(transfer_num)+"@"+$Ri+":"+$Rp;
sl_send_reply("302","LCR Redirect");
exit;
}
}
route[relay] {
xlog("L_INFO","$cfg_line-$cs $rm $fu->$tu: enter route relay \n");
replace_all("FreeSWITCH", "WMS");
if(is_present_hf("Remote-Party-ID")){
remove_hf("Remote-Party-ID");
}
if (is_method("INVITE")) {
t_on_reply("handle_nat");
t_on_failure("missed_call");
}
if (isflagset(10)) {
add_rr_param(";nat=yes");
}
if ($avp(attr)) {
$du=$avp(attr);
xlog("L_INFO","$cfg_line-$cs $rm $fu->$tu: get edge address from attr\n");
} else {
xlog("L_INFO", "$cfg_line-$cs $rm $fu->$tu: get edge address from database\n");
avp_db_query("SELECT attr FROM location WHERE username='$tU' and domain='$td'","$avp(edge)");
if ($avp(edge)) {
$du=$avp(edge);
} else {
xlog("$cfg_line-$cs $rm $fu->$tu: can not get edge address\n");
}
}
if (!t_relay()) {
xlog("send reply 500 Internal Error!\n");
send_reply("500","Internal Error");
};
exit;
}
onreply_route[handle_nat] {
route(r_delete_sip_header_to_ext);
if($rs==302){
remove_hf("contact");
$var(movedNum)=$(ct.fields(uri){uri.user});
$var(new_contact)=$var(movedNum)+"@"+"192.168.40.20:18627";
append_hf("Contact: <sip:$var(new_contact)>\r\n", "Call-ID");
exit;
}
}
failure_route[missed_call] {
if (t_was_cancelled()) {
exit;
}
xlog("L_ERR","failure_route reply $(err.rcode) - $(err.rreason) -$(err.info) -$(err.class) \n");
if (isflagset(12)) {
xlog("L_ERR","t_reply: 503 Service not available, no more gateways\n");
t_reply("503", "Service not available, no more gateways");
exit;
}
}
local_route {
if (is_method("BYE") && $DLG_dir=="UPSTREAM") {
#根据请求进行报告
acc_db_request("200 Dialog Timeout", "acc");
}
}
route [r_not_support] {
if (is_method("PUBLISH|SUBSCRIBE")){
sl_send_reply("503", "Service Unavailable");
exit;
}
}
#记录消息
route[r_do_accounting] {
$var(enableAcc)=0;
if($var(enableAcc)){
do_accounting("db|log", "cdr|missed");
}
}
route[r_find_real_rU]{
xlog("L_INFO","r_find_real_rU $avp(ru_strip)\n");
$avp(ru_strip)=$(ru{uri.param,s});
switch ($avp(ru_strip)) {
case "1":
$avp(x_r_rU)=$(avp(call_origin_rU){s.substr,1,0});
break;
case "2":
$avp(x_r_rU)=$(avp(call_origin_rU){s.substr,2,0});
break;
case "3":
$avp(x_r_rU)=$(avp(call_origin_rU){s.substr,3,0});
break;
case "4":
$avp(x_r_rU)=$(avp(call_origin_rU){s.substr,4,0});
break;
case "5":
$avp(x_r_rU)=$(avp(call_origin_rU){s.substr,5,0});
break;
case "6":
$avp(x_r_rU)=$(avp(call_origin_rU){s.substr,6,0});
break;
case "7":
$avp(x_r_rU)=$(avp(call_origin_rU){s.substr,7,0});
break;
case "8":
$avp(x_r_rU)=$(avp(call_origin_rU){s.substr,8,0});
break;
case "9":
$avp(x_r_rU)=$(avp(call_origin_rU){s.substr,9,0});
break;
default:
xlog("strip $avp(ru_strip) it not support\n");
}
}
timer_route[wj_cr_reload, 600]{
if (0 && $stat(wj:update_gateway_count) > 0) {
xlog("L_INFO", "update_gateway_count: $stat(wj:update_gateway_count)\n");
#执行外部命令
exec("opensipsctl fifo cr_reload_routes");
$stat(wj:update_gateway_count) = 0;
}
}
timer_route[r_influxdb_monitor, 5] {
$var(db_url)="";
if ($var(db_url)) {
$var(reqbody)="opensips,type=core,ip=192.168.40.20 real_used_size=" + $stat(real_used_size)
+ ",active_dialogs=" + $stat(active_dialogs)
+ ",inuse_transactions=" + $stat(inuse_transactions)
+ ",waiting_udp=" + $stat(waiting_udp)
+ ",cps_all=" + $stat(wj:cps_all) + ",cps_trunk=" + $stat(wj:cps_trunk);
$var(rc) = rest_post("","$var(reqbody)","text/plain","$var(body)","$var(ct)","$var(rcode)");
if (!$var(rc)) {
xlog("L_ERR", "write to influx db error, please check the influxdb url\n");
}
$stat(wj:cps_all) = 0;
$stat(wj:cps_trunk) = 0;
}
}
route [r_delete_sip_header_to_ext] {
remove_hf("X-trunk-name");
remove_hf("X-trunk-direction");
remove_hf("X-calltype");
remove_hf("X-th");
remove_hf("X-ru");
remove_hf("X-tu");
remove_hf("Remote-Party-ID");
}
onreply_route[r_delete_trunk_info]{
route(r_delete_sip_header_to_ext);
}
#对register表头进行记录
route[r_siphub_capture]{
if(!is_method("REGISTER") && !has_totag()){
sip_trace("tid", "d", "sip");
}
}
#使用
route[r_register_h5]{
#如果邮件中存在标头字段,则返回true &&名称和副名称
if (is_present_hf("Authorization") && $hdr(Authorization)=~"^webview") {
if ($hdr(Authorization) == "webview2020") {
$avp(attr)="sip:"+$si+":"+$sp;
#没有保存在本地
if (!save("location","p1vf")){
sl_send_reply("401","Auth h5 failed");
}
} else {
sl_send_reply("401","Auth h5 failed");
}
exit;
}
}
route[r_not_register_h5]{
if (is_present_hf("Authorization") && $hdr(Authorization) == "webview2020") {
remove_hf("Authorization");
# hand invite
if (is_method("INVITE") && has_body("application/sdp")){
xlog("L_INFO", "$cfg_line enter h5 invite $ru");
record_route();
# go to fs
if (!load_balance("1","trunk")) {
xlog("L_INFO", "load_balance failed: check fs is ok\n");
sl_send_reply("500","service full");
exit;
}
t_relay();
exit;
}
# hand message
# todo
else if (is_method("MESSAGE")) {
xlog("L_INFO", "$cfg_line enter h5 message $ru");
route(r_message);
}
exit;
}
}
route[r_message]{
xlog("$cfg_line $rm $fu $tu $rb \n");
$avp(message_body) = $rb;
avp_db_query("select contact,attr from location where username='$rU' AND domain='$rd'","$avp(i_contact);$avp(i_attr)");
xlog("$rm $fU $tU db query result $avp(i_contact) $avp(i_attr) \n");
if (!is_avp_set("$avp(i_contact)")) {
route(r_save_msg_to_db, "404");
xlog("$cfg_line $rm $fu $tu: status of lookup is $avp(sql_re) \n");
t_reply("404", "Not Found");
exit;
}
t_on_reply("r_save_msg_record");
$ru=$avp(i_contact);
$du=$avp(i_attr);
if (!t_relay()) {
send_reply("500","Internal Error");
}
exit;
}
onreply_route[r_save_msg_record]{
route(r_save_msg_to_db, $rs);
}
route[r_save_msg_to_db]{
$var(sip_callid) = $(ci{s.escape.common});
$var(fs_callid) = $(hdr(fs_callid){s.escape.common});
$var(fu) = $(fU{s.escape.common}) + "@" + $(fd{s.escape.common});
$var(tu) = $(tU{s.escape.common}) + "@" + $(td{s.escape.common});
$var(msg) = $(avp(message_body){s.escape.common});
xlog("$var(sip_callid), $var(fs_callid), $var(fu), $var(tu), $var(msg)");
avp_db_query("insert into message_record(sip_callid,fs_callid,time,fu,tu,status,msg) values('$var(sip_callid)','$var(fs_callid)',now(),'$var(fu)','$var(tu)','$param(1)','$var(msg)')");
xlog("message insert db message_record result: $rc");
}
1.is_from_local()判断是否来自domain表
2.t_check_trans()判断事务
3.t_on_relay和sl_send_reply,send_reply