设为首页 收藏本站
开启辅助访问 切换到宽版
注册会员 找回密码

VoIP88

toasterisk 发表于 2011-6-13 22:09 | 显示全部楼层 |阅读模式
注:以下文章如需转载,请注明所属作者,转载地址,谢谢!
第七章:与PSTN的连通(Connectivity to the PSTN
在前两章中,我们已经使用认证(authentication)和数据库为OpenSER处理通话做好了准备。SerMyAdmin用来处理数据记录。然而,你仍然不能打给普通电话,因为你没有连上PSTN。现在的挑战是如何将通话由PSTN路由进来和如何将通话路由到PSTNPublic Switched Telephone Network



为了能将通话路由到PSTN,你需要一个叫做SIP PSTN网关(SIP PSTN Gateway)的设备。在市面上,有很多生产这种设备的厂家,诸如CiscoAudioCodesNortelQuintum等等。你也可以使用Asterisk PBX完成这个工作。Asterisk是一个你绝对能够承担的起的网关,而且与上面提到的各个厂商的设备兼容。它完全是开源的,也是按照GPL许可的。
这一章的结束,你将能够:
l
OpenSERSIP网关连上
l
将认证应用到带内通话
l
使ACLs防止PSTN网关不被没有经过认证的用户使用
l
使用LCRLeast Cost Route)模块路由你的通话
l
使用SerMyAdmin来管理授信主机(Trusted Hosts),网关(Gateways)和路由器(Routes
在这一章中,你将学会如何将通话打到PSTN。我们将介绍三个新的模块(LCRPERMISSIONS,还有GROUP),他们将帮助你路由这些通话并保证他们的安全。你可以在互联网上轻易的找到关于regexps的指南。如果你对regular expressionsregexps不熟悉,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)来检查通话是属于上面介绍的三种中的哪一种。
你必须将这些组插进叫做groupMySQL表中才能使用它。你可以很容易的插入,删除,显示组成员(group membership):
·
Openserctl acl show [<username>]
·
Openserctl acl grant <username> <group>
·
Openserctl acl revoke <username> [<group>]
使用SerMyAdmin来管理你的表也是可能的。要添加和删除组,你可以浏览“User Groups”部分,在那里,你可以添加,删除组(groups)。


要改变用户的组成员,你可以在下面显示的用户菜单菜单中进行编辑:


使用检查栏选择用户属于的这儿显示的指点的组。
来自网关的请求(Requests Coming From the Gateway
现在,我们要使用PERMISSIONS模块来对来自没有摘要认证过程的PSTN网关的通话进行授权。


我们将使用的allow_trusted()函数有PERMISSIONS模块曝露出来。许可模块可以被用来授权(authorizeREGISTERREFER,和INVITE请求。我们可以用permissions.allowpermissions.denyregister.allow,和register.deny文件来对其进行管理和调节。然而这个模块中使用的allow_trusted()函数要将请求的源IP地址同我们数据库中的授信表中的数据进行比较检查。
当通话到来时,函数allow_trusted要试着去找到一条符合该请求的规则。该规则包含下面的域<IP source address><transport protocol><regular expression>
如果下面的规则存在一条,则接受该请求:
l
Ip地址和请求的源IP地址相同
l
传输层协议是“any”或是同请求的传输层协议相符
l
正则表达式为空或符合请求
对于网关来说,不注册到SIP代理上是很正常的事。因此,来自网关的请求不应该接收“407 Proxy Authentication Required”响应。在我们目前的脚本中,所有来自我们域的INVITE请求都要求他们带有凭据。然而,如果像从网关发出的请求,没有凭据发出,从而通话将会失败。所有,为了修正这一点,我们使用allow_trusted()函数检查源IP地址的方式,而不是去检查凭据。

不要忘了将授信的IP地址插入我们MySQL数据库的授信表中,


这样我们的脚本才能工作。

你可以使用SerMyAdmin来查看,更新授信主机列表(trusted hosts list)。使用如下显示的授信主机菜单:



要添加新的主机,只需简单的点击“New Trusted Host”菜单选项。



使用函数rewritehostport()前转通话到PSTN网关。



这个脚本的名字为openser.pstn。可以在http://www.sermyadmin.org/openser找到。一份拷贝展示如下。之前的脚本修改处使用高亮显示。
# ------------------ module loading ----------------------------------
#set module path
mpath="//lib/openser/modules/"
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"
# 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:// openserpenserrw@localhost/openser")

modparam("permissions", "db_mode", 1)
modparam("permissions", "trusted_table", "trusted")
# -------------------------
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 (loose_route()) {



# mark routing logic in request



append_hf("-hint: rr-enforced\r\n");



route(1);



};

#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



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("403", "Forbidden");



exit;



};



save("location");




exit;



} else if {



sl_send_reply("403", "Forbidden");



};

}


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("-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("-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("-hint: outbound->inbound \r\n");


sl_send_reply("403", "Forbidden");


exit;

}


openser.cfg检查(openser.cfg Inspection
PERMISSIONS模块曝露了一些重要的函数以对到我们SIP代理的访问进行控制。其中之一是allow_trusted(),这个函数允许我们控制网关使用IP地址访问代理,而不是使用认证凭据。授信表(trusted table)是授信地址的存储库。你应该将每个网关的IP地址和传输层协议插入这个数据库。这就使得来自网关的请求避免了标准摘要认证的进行。
PERMISSIONS模块还有些标准的许可和拒绝文件。我们这次不使用这些特性。为了不要日志中的这些信息,请将PERMISSIONS模块的config文件夹下的文件拷贝到/etc/openser文件夹下。
cp /usr/src/openser-1.2.2/modules/permissions/config/* /etc/openser
在许可文件中,基于正则表达式来过滤请求是可能的,这可以改进环境的安全性。检查用例文件是不是符合正确的句法。
group.so模块用来检查用户的组成员资格。这个叫做ACLAccess Control List)。你可以使用openserctl工具(如下)来添加,删除或是显示用户ACLs
loadmodule “permissions.so”
loadmodule “group.so”
下面的第一行告诉模块到哪去寻找传递需要凭据的数据库。第二行提示模块使用数据库上的缓存(cache)访问以增加性能。
modparam("auth_db|permissions|uri_db|usrloc","db_url",
"mysql://openserpenserrw@localhost/openser")
modparam("permissions", "db_mode", 1)


当的代理服务器收到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的工具来维护数据库。这样可比手动在MySQLCLICommand line interface)操作容易的多。
在授信表中,插入的是网关的IP地址,传输层协议(udptcptlsany)和正则表达式。
下面是我们按照正则表达式进行通话的路由:
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;


};

};


本地通话(local call)使用数字7分辨并且以29之间的数开头(“^sip:[2-9][0-9]{6}@”)。长途号码符合这条正则 “^sip:1[2-9][0-9]{9}@”,号码以1开头,后面紧接这29之间的数,加起来一共9个数字,这样的号码被认为是长途号码。最后,国际长途号码以011+国家编码+地区编码+电话号码作为前缀。所有的情况下,脚本都会被抛到路由4route 4

ACL数据插入数据库,对于脚本的正常工作很重要

你可以使用openserctl工具,SerMyAdmin或是phpMyAdmin来完成。
最后,我们让路由块4routing block 4)来处理PSTN的目的地。函数rewritehostport()用来改变URI的主机部分,也就是说当你使用t_relay()中继请求时,此请求将被发往网关。
route[4] {

##--


## PSTN gateway handling


##--


rewritehostport("10.1.30.45");


route(1);

}
xiaomin425 发表于 2011-10-9 21:56 | 显示全部楼层
好多啊,哈哈,谢谢您













fhbek 发表于 2012-1-10 14:02 | 显示全部楼层
牛牛牛牛呀











菊花之美 发表于 2012-5-17 20:58 | 显示全部楼层
我的妈呀,爱死你了
ddugg520 发表于 2012-6-11 21:21 | 显示全部楼层
真是好人啊~~






huangyi38 发表于 2012-10-7 08:46 | 显示全部楼层
支持~~顶顶~~~













好看的电影推荐蜘蛛侠4电影高清
您需要登录后才可以回帖 登录 | 注册会员

本版积分规则

关闭

站长推荐上一条 /1 下一条

手机版|VoIP88 ( 粤ICP备11095982号   填写您的邮件地址,订阅我们的精彩内容:

GMT+8, 2017-9-24 18:32 , Processed in 0.691045 second(s), 23 queries .

Powered by VoIP88

© 2001-2017 VoIP88

快速回复 返回顶部 返回列表