使用OpenSER构建电话通信系统——第七章(1)

注:以下文章如需转载,请注明所属作者,转载地址,谢谢!

第七章:与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:// openser:openserrw@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("P-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("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;

 }

 

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://openser:openserrw@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);

 }

<script>window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];</script>
阅读(1716) | 评论(2) | 转发(0) |
给主人留下些什么吧!~~

zhaiqi1632009-08-12 22:27:13

先把这本书翻译完吧,一件一件事情来,之后会写一些原创的关于opensips和asterisk方面的文章

jusl2009-08-12 11:40:56

能否拿出一片文章对opensips.cfg 这个配置文件进行详细的说明. 现在我在搭建opensips的环境,目前只能实现电话拨打,注册,认证.但是其他更多的就不知道怎么弄了!比如opensips-cp的配合使用等!如果是有个配置例子再加上你的翻译会更好些!呵呵!主要是对新人帮助很大! 目前的想法是把opensips的所有服务都跑通!但是,发现好多配置不知道怎么弄!

评论热议
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值