首先,让我们来实现呼叫盲转服务。在INVITE请求的处理过程中,我们将从数据库中的用户优先表中加载名称为callfwd的AVP。如果callfwd被设置给了指定的用户,那么我们在对该请求进行前转前将它“推进”(push)到R-URI中。
if(avp_db_load("$ruri/username","s:callfwd")){
#Check the existence of the callfwd attribute on the
#usr_preferences table. If found, assign the vaue to the AVP
# and push the value to the ruri of the SIP header.
avp_pushto("$ruri","s:callfwd");
route(1);
exit;
};
为了能让这个特性得以工作,重要点在于在数据库中插入正确的入口点。AVPs使用的表叫做usr_preferences。
你可以在SerMyAdmin的帮助下来对用户的喜好进行修改;只需要浏览菜单中“user preferences”即可。在那个选单中你可以查看所有的用户喜好,编辑它们,还可以创建新的。
如果你工作在多域环境中,请将模块AVPOPS的多域参数功能打开,并用域名来填充数据库。
在上面的记录中,我们告诉系统将任何呼给1001的呼叫都呼给URI:sip:1004@yourdomain。
步骤1:让我们使用在第六章里第一次见到的SerMyAdmin接口来插入AVP对。
你的浏览器中访问SerWEB的管理接口为:
http://
步骤2:使用一个具有“全局管理员”角色的用户登录接口。
步骤3:点击用户喜好(User preferences)标签。在这个菜单中,点击“New Preference”按钮,为你想要前转通话的用户来创建AVP;在这个例子中,应该是[email protected]。它的属性比较取名为callfwd,值是你要前转通话的URI;这里应该设置成[email protected]。
步骤4:编辑openser.cfg以包含上面做的那些说明解释。文件应该以下面的代码结束。在openser.cfg文件中包含下面的代码行。或是简单地从http://www.sermayadmin.org/openser拷贝openser.callfwd1到openser.cfg。
在模块加载部分:
loadmodule "avpops.so"
loadmodule "xlog.so"
在模块参数部分:
modparam("avpops", "avp_url", "mysql://openser:openserrw@localhost/openser")
modparam("avpops", "avp_table", "usr_preferences")
在route[3]部分:
route[3] {
#
# -- INVITE request handler --
#
if (is_from_local()){
# From an internal domain -> check the credentials and the FROM
if(!allow_trusted()){
if (!proxy_authorize("","subscriber")) {
proxy_challenge("","1");
exit;
} else if (!check_from()) {
sl_send_reply("403", "Forbidden, use From=ID");
exit;
};
} else {
log("Request bypassed the auth. using allow_trusted");
};
if(avp_db_load("$ru/username","$avp(s:callfwd)")) {
avp_pushto("$ru", "$avp(s:callfwd)");
xlog("forwarded to: $avp(s:callfwd)");
route(1);
exit;
}
consume_credentials();
步骤5:注册分机1001和1004。使用1001打给1000,这时候这通通话就应该按照usr_preferences表中说明被前转到1004。
在忙线或无人接听的时候实现呼叫前转( Implementing Call Forward on Busy or Unanswered )这是这一章的第二部分。现在我们将介绍两个新的重要概念。第一个是failure_route,第二个则是append_branch,其被用来派生通话(fork the call)。我们将处理下面的出错情况:
l 408 – Timeout
l 486 – Busy Here
l 487 – Request Cancelled
为了实现在忙线和无人接听的情况下的通话前转,我们将使用出错路由(failure route)的概念。
下面的逻辑中,在发送INVITE到标准处理过程前,我们将调用函数t_on_failure(“1”)。这允许我们在failure_route[1]中处理SIP出错消息(返回的消息高于299的,也叫做负应答negative replies)
当在这种情况下收到一通通话时,我们将其前转到一个语音邮箱系统。Asterisk能够充当一个相当好的语音邮箱系统。让我们来看看如何整合asterisk来记录语音邮箱消息。我们将在URI使用b(busy)前缀来告诉asterisk服务器播放busy消息,使用u(unanswered)来播放无应答消息。Asterisk将分别使用应用voicemail(b${EXTEN})和voicemail(u${EXTEN})来处理忙线和无人接听的语音邮箱请求。
下面是一个完整的脚本,修改部分使用高亮显示。
#
#
# $Id: openser.cfg 1676 2007-02-21 13:16:34Z bogdan_iancu $
#
# simple quick-start config script
# Please refer to the Core CookBook at http://www.openser.org/dokuwiki/doku.php
# for a explanation of possible statements, functions and parameters.
#
# ----------- global configuration parameters ------------------------
debug=3 # debug level (cmd line: -dddddddddd)
fork=yes
log_stderror=no # (cmd line: -E)
children=4
port=5060
# ------------------ module loading ----------------------------------
#set module path
mpath="//lib/openser/modules/"
# Uncomment this if you want to use SQL database
#loadmodule "mysql.so"
loadmodule "mysql.so"
loadmodule "sl.so"
loadmodule "tm.so"
loadmodule "rr.so"
loadmodule "maxfwd.so"
loadmodule "usrloc.so"
loadmodule "registrar.so"
loadmodule "textops.so"
loadmodule "uri.so"
loadmodule "uri_db.so"
loadmodule "domain.so"
loadmodule "permissions.so"
loadmodule "group.so"
loadmodule "mi_fifo.so"
loadmodule "lcr.so"
loadmodule "avpops.so"
loadmodule "xlog.so"
# Uncomment this if you want digest authentication
# mysql.so must be loaded !
loadmodule "auth.so"
loadmodule "auth_db.so"
# ----------------- setting module-specific parameters ---------------
modparam("mi_fifo", "fifo_name", "/tmp/openser_fifo")
modparam("usrloc", "db_mode", 2)
modparam("auth_db", "calculate_ha1", yes)
modparam("auth_db", "password_column", "password")
modparam("rr", "enable_full_lr", 1)
modparam("auth_db|permissions|uri_db|usrloc","db_url","mysql://openser:openserrw@localhost/openser")
modparam("permissions", "db_mode", 1)
modparam("permissions", "trusted_table", "trusted")
modparam("avpops", "avp_url", "mysql://openser:openserrw@localhost/openser")
modparam("avpops", "avp_table", "usr_preferences")
# ------------------------- request routing logic -------------------
# main routing logic
route{
#
# -- 1 -- Request Validation
#
if (!mf_process_maxfwd_header("10")) {
sl_send_reply("483","Too Many Hops");
exit;
};
if (msg:len >= 2048 ) {
sl_send_reply("513", "Message too big");
exit;
};
#
# -- 2 -- Routing Preprocessing
#
## Record-route all except Register
if (!method=="REGISTER") record_route();
##Loose_route packets
if (has_totag()) {
#sequential request withing a dialog should
# take the path determined by record-routing
if (loose_route()) {
#Check authentication of re-invites
if(method=="INVITE" && (!allow_trusted())) {
if (!proxy_authorize("","subscriber")) {
proxy_challenge("","1");
exit;
} else if (!check_from()) {
sl_send_reply("403", "Forbidden, use From=ID");
exit;
};
};
route(1);
} else {
sl_send_reply("404","Not here");
}
exit;
}
#CANCEL processing
if (is_method("CANCEL")) {
if (t_check_trans()) t_relay();
exit;
};
t_check_trans();
#
# -- 3 -- Determine Request Target
#
if (method=="REGISTER") {
route(2);
} else {
route(3);
};
}
route[1] {
#
# -- 4 -- Forward request to target
#
## Forward statefully
t_on_failure("1");
if (!t_relay()) {
sl_reply_error();
};
exit;
}
route[2] {
## Register request handler
if (is_uri_host_local()) {
if (!www_authorize("", "subscriber")) {
www_challenge("", "1");
exit;
};
if (!check_to()) {
sl_send_reply("401", "Unauthorized");
exit;
};
save("location");
exit;
} else if {
sl_send_reply("401", "Unauthorized");
};
}
route[3] {
## Non-Register request handler
if (is_from_local()){
# From an internal domain -> check the credentials and FROM
if(!allow_trusted()){
if (!proxy_authorize("","subscriber")) {
proxy_challenge("","1");
exit;
} else if (!check_from()) {
sl_send_reply("403", "Forbidden, use From=ID");
exit;
};
} else {
log("Request bypassed the auth.using allow_trusted");
};
if(avp_db_load("$ru/username","$avp(s:callfwd)")) {
avp_pushto("$ru", "$avp(s:callfwd)");
route(1);
exit;
}
consume_credentials();
#Verify aliases, if found replace R-URI.
lookup("aliases");
if (is_uri_host_local()) {
# -- Inbound to Inbound
route(10);
} else {
# -- Inbound to outbound
route(11);
};
} else {
#From an external domain ->do not check credentials
#Verify aliases, if found replace R-URI.
lookup("aliases");
if (is_uri_host_local()) {
#-- Outbound to inbound
route(12);
} else {
# -- Outbound to outbound
route(13);
};
};
}
route[4] {
# routing to the public network
if (!load_gws()) {
sl_send_reply("503", "Unable to load gateways");
exit;
}
if(!next_gw()){
sl_send_reply("503", "Unable to find a gateway");
exit;
}
route(5);
exit;
}
route[5] {
#
# -- 4 -- T_relay for gateways
#
## Forward statefully, if failure load other gateways
t_on_failure("2");
if (!t_relay()) {
sl_reply_error();
};
exit;
}
route[10] {
#from an internal domain -> inbound
#Native SIP destinations are handled using the location table
#Gateway destinations are handled by regular expressions
append_hf("P-hint: inbound->inbound \r\n");
if (uri=~"^sip:[2-9][0-9]{6}@") {
if (is_user_in("credentials","local")) {
prefix("+1");
route(4);
exit;
} else {
sl_send_reply("403", "No permissions for local calls");
exit;
};
};
if (uri=~"^sip:1[2-9][0-9]{9}@") {
if (is_user_in("credentials","ld")) {
strip(1);
prefix("+1");
route(4);
exit;
} else {
sl_send_reply("403", "No permissions for long distance");
exit;
};
};
if (uri=~"^sip:011[0-9]*@") {
if (is_user_in("credentials","int")) {
strip(3);
prefix("+");
route(4);
exit;
} else {
sl_send_reply("403", "No perm. for internat.calls");
};
};
if (!lookup("location")) {
if (does_uri_exist()) {
## User not registered at this time.
## Use the IP Address of your e-mail server
revert_uri();
prefix("u");
rewritehostport("192.168.1.171"); #Use the voicemail IP
route(1);
} else {
sl_send_reply("404", "Not Found");
exit;
}
sl_send_reply("404", "Not Found");
exit;
};
route(1);
}
route[11] {
# from an internal domain -> outbound
# Simply route the call outbound using DNS search
append_hf("P-hint: inbound->outbound \r\n");
route(1);
}
route[12] {
# From an external domain -> inbound
# Verify aliases, if found replace R-URI.
lookup("aliases");
if (!lookup("location")) {
sl_send_reply("404", "Not Found");
exit;
};
route(1);
}
route[13] {
#From an external domain outbound
#we are not accepting these calls
append_hf("P-hint: outbound->inbound \r\n");
sl_send_reply("403", "Forbidden");
exit;
}
failure_route[1] {
##--
##-- If cancelled, exit.
##--
if (t_check_status("487")) {
exit;
};
##--
##-- If busy send to the e-mail server, prefix the "b"
##-- character to indicate busy.
##--
if (t_check_status("486")) {
revert_uri();
prefix("b");
xlog("L_ERR","Stepped into the 486 ruri=<$ru>");
rewritehostport("192.168.1.171");
append_branch();
route(1);
exit;
};
##--
##-- If timeout (408) or unavailable temporarily (480),
##-- prefix the uri with the "u"character to indicate
##-- unanswered and send to the e-mail
##-- sever
##--
if (t_check_status("408") || t_check_status("480")) {
revert_uri();
prefix("u");
xlog("L_ERR","Stepped into the 480 ruri=<$ru>");
rewritehostport("192.168.1.171");
append_branch();
route(1);
exit;
};
}
failure_route[2] {
if(!next_gw()) {
t_reply("503", "Service not available, no more gateways");
exit;
}
t_on_failure("1");
t_relay();
}
上一篇:使用OpenSER构建电话通信系统——第八章(1)
下一篇:使用OpenSER构建电话通信系统——第八章(3)