注:以下文章如需转载,请注明所属作者,转载地址,谢谢!第七章:与PSTN的连通(Connectivity to the PSTN)
在前两章中,我们已经使用认证(authentication)和数据库为OpenSER处理通话做好了准备。SerMyAdmin用来处理数据记录。然而,你仍然不能打给普通电话,因为你没有连上PSTN。现在的挑战是如何将通话由PSTN路由进来和如何将通话路由到PSTN(Public
Switched Telephone Network)
为了能将通话路由到PSTN,你需要一个叫做SIP
PSTN网关(SIP PSTN Gateway)的设备。在市面上,有很多生产这种设备的厂家,诸如Cisco,AudioCodes,Nortel,Quintum等等。你也可以使用Asterisk
PBX完成这个工作。Asterisk是一个你绝对能够承担的起的网关,而且与上面提到的各个厂商的设备兼容。它完全是开源的,也是按照GPL许可的。
这一章的结束,你将能够:
l
将OpenSER与SIP网关连上
l
将认证应用到带内通话
l
使ACLs防止PSTN网关不被没有经过认证的用户使用
l
使用LCR(Least
Cost Route)模块路由你的通话
l
使用SerMyAdmin来管理授信主机(Trusted
Hosts),网关(Gateways)和路由器(Routes)
在这一章中,你将学会如何将通话打到PSTN。我们将介绍三个新的模块(LCR,PERMISSIONS,还有GROUP),他们将帮助你路由这些通话并保证他们的安全。你可以在互联网上轻易的找到关于regexps的指南。如果你对regular
expressions或regexps不熟悉,http://www.visibone.com/regular-expressions/这条廉洁可以作为参考。
我们在哪儿?(Where Are We?)
VoIP服务提供商的方案中有很多的部件。为了避免迷失,我们将在每一章中展示下面的这张图片。在这一章中,我们将利用SIP代理部件和PSTN网关一起工作。
本章之后,我们的VoIP提供者将能够使用SIP网关将通话打给PSTN。
发往网关的请求(Requests Sent to the Gateway)
在标明给网关的请求中,我们必须验证该用户属于哪一组(group),以查看其是否被允许使用PSTN。
要达到这个目的,我们要使用到‘group’模块。这个暴露了函数is_user_in(“credentials”,
“group”)用来检查用户是否属于指定组。在上面的例子中,我们已经创建了3个组:local代表本地通话,ld代表长途,int代表国际长途。脚本中,我们使用正则表达式(regular
expressions)来检查通话是属于上面介绍的三种中的哪一种。
你必须将这些组插进叫做group的MySQL表中才能使用它。你可以很容易的插入,删除,显示组成员(group
membership):
Openserctl acl show
[]
Openserctl acl grant
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");
};
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
rewritehostport("10.1.30.45");
route(1);
}
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")) {
route(4);
exit;
} else {
sl_send_reply("403", "No permissions for local
calls");
exit;
};
};
if
(uri=~"^sip:1[2-9][1-9]{9}@") {
if
(is_user_in("credentials","ld")) {
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")) {
route(4);
exit;
} else {
sl_send_reply("403", "No permissions for
international calls");
};
};
if
(!lookup("location")) {
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;
}
当的代理服务器收到INVITE请求后,通常的行为是向UAC请求凭据。然而,PSTN网关通常并不对认证进行响应。因此,你需要采取特殊的处理过程。函数allow_trusted()将INVITE请求的源IP地址同数据库中授信表进行对比检查。如果符合,则接受之。如果不符合,则再请求凭据。
if(!allow_trusted()){
if
(!proxy_authorize("","subscriber")) {
proxy_challenge("","0");
exit;
} else if
(!check_from()) {`
sl_send_reply("403","Forbidden, use FROM=ID");
exit;
};
};
| 网关的IP地址插入数据库是很重要的 |
你可以使用诸如SerMyAdmin或是phpMyAdmin的工具来维护数据库。这样可比手动在MySQL的CLI(Command
line interface)操作容易的多。
在授信表中,插入的是网关的IP地址,传输层协议(udp,tcp,tls,any)和正则表达式。
下面是我们按照正则表达式进行通话的路由:
if (uri=~"^sip:[2-9][0-9]{6}@") {
if
(is_user_in("credentials","local")) {
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")) {
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")) {
route(4);
exit;
} else {
sl_send_reply("403", "No permissions for
internat. calls");
exit;
};
};