飞信的 SIP 协议分析

来源:互联网 发布:制作个人简历的软件 编辑:程序博客网 时间:2024/04/27 16:11

进到新的团队后,我的项目是为省级部门做一套企业级 IM 工具,这个工具同时和我们做的机构管理网站后台交互,为用户提供一套机构人员查询和交流的桌面访问入口。最近的任务是重新设计 IM 通信协议,项目拟化在 飞鸽传书 (IpMsg) 的协议上改造,加入跨域和互联网支持,因为默认的飞鸽协议只能在三级网络设备之下的网段内通信。我准备在 飞鸽 和 中国电信的飞信 (Fetion) 的协议上研究一番,因为飞信是基于 SIP 协议的,和飞鸽相比 SIP 协议规格是开放的,又是类似 HTTP 的文本化协议,分析起来较容易,加上之前也搞过关于 SIP 的东西,觉得比较好上手,于是就从它开始了。

下面的协议分析参考了 nathan 的 Fetion 协议分析 系列,原文一共 10 篇,作者从 飞信的协议类型开始,讲述了包括 飞信协议所支持的 SIP 方法和消息头域 等各方面。在此佩服作者 nathan 的分析能力和毅力,毕竟这是一个耗时间的活。我这里照着原文的思路用工具分析了几遍,对原文稍加删改,把重点内容罗列如下:

 

  • 飞信的协议类型

  • 飞信的配置文件和服务器地址

  • 飞信的配置文件和用户密码的加密方法

  • 飞信协议支持的 SIP 方法及消息头域

  • 飞信的文本消息聊天交互过程

  • 飞信手机语聊的 SIP 交互过程

  • 飞信登录时的身份验证

飞信的协议类型  返回目录

1. HTTP 进行获取系统配置、更新程序、注册用户。

2. HTTPS 进行登录时密码验证。

3. 用户通信时(用户发消息、发短信),协议是 SIP,但不是标准的,称为 SIP-C。

飞信的 SIP 协议承载有两种,TCP、HTTP:

1. TCP 承载:连接服务器的 8080 端口,这时在客户端的“网络设置”中显示的是“TCP 直接连接”,SIP 信令直接就放在 TCP 的包中。

2. HTTP承载:连接服务器的 80 端口,采用 POST 方式,将信令包在 POST 请求中,这时在客户端的“网络设置”中显示的是“HTTP 直接连接”。

飞信的配置文件和服务器地址  返回目录

飞信工作时连的是 221.130.45.203 这个服务器,这个IP地址从哪来的呢?会变吗?

飞信在开始时解析过一次域名:nav.fetion.com.cn,这个域名的 IP 是 221.130.45.201。

既然只解析过 nav.fetion.com.cn,那么 221.130.45.203 这个工作服务器 (SIP Proxy Server) 地址,就应该是nav.fetion.com.cn 返回来的了。确实是,但只是新安装的飞信第一次登录时返回,并保存在了本地。后面再登录时,如果版本不更新,是不会再返回这些系统配置信息的。

飞信本地配置文件在 %USERPROFILE%/Application/Fetion 目录下。这个目录下有 configuration.dat 和飞信的用户目录,每个飞信用户目录下有 configuration.dat、contacts.dat、userinfo.dat 这三个配置文件,看名字就知道是与用户相关的系统配置文件、好友列表文件、用户的个人信息文件。

这些 XML 配置文件内容是经过变换的,可以通过下面两个办法知道里面的内容(另外后面章节还说明了变化的方法,和解密成明文的工具):

  1. 修改 FetionFx.exe 文件。用 ildasm 将FeionFX.exe 反汇编,将其中的 Imps.Client.Pc.PersistentManager.EncodeMode1 和 Imps.Client.Pc.PersistentManager.DecodeMode1 这两个函数改掉,将这两个函数体改成以下内容:

    .maxstack 1
    IL_0000: ldarg.0
    IL_0001: ret

    即立即将参数返回。然后再用 ilasm工具重新汇编生成 FetionFX.exe 文件,覆盖掉以前那个,然后再运行飞信,所有配置文件就不会再加密了。

  2. 构造一个请求,发给 nav.fetion.com.cn。请求的内容很简单,取系统配置的请求过程是:

    (1). 请求:

    local:port → 221.130.45.201:80

    POST /nav/getsystemconfig.aspx HTTP/1.1
    User-Agent: IIC2.0/PC 2.1.0.0
    Host: nav.fetion.com.cn
    Content-Length: 233
    Connection: Keep-Alive

    (2). 响应:

    xxx.xxx.xxx.xxx:xxxx ← 221.130.45.201:80

    HTTP/1.1 100 Continue

    (3). 再请求:

    local:port → 221.130.45.201:80

    POST Content:

    查看源代码
    打印帮助
    <config>
        <user mobile-no="139xxxxxxxx" />
        <client type="PC" version="2.1.0.0" platform="W5.1" />
        <servers version="12" />
        <service-no version="1" />
        <parameters version="4" />
        <hints version="4" />
        <http-applications version="5" />
    </config>

    将以上内容中的从 servers version 开始的 version 全置为 "0",服务器就会返回配置信息。配置信息是 UTF-8 编码的。用 nc (netcat) 就可构造一个 HTTP POST 请求。

方法 1,可以查看和修改所有配置信息;方法 2 仅能查看系统配置信息。

与服务器地址相关的信息在飞信用户目录下的 configuration.dat 中,其明文如下:

查看源代码
打印帮助
1<sipc-proxy>221.130.45.203:8080</sipc-proxy>                   TCP 承载时的服务器地址
2<http-tunnel>HTTP://221.130.45.203/ht/sd.aspx</http-tunnel>    HTTP 承载时的入口地址
3<get-pic-code>HTTP://221.130.45.201/nav/GetPicCode.aspx</get-pic-code>    注册时,取验证代码图片的 URL
4<get-system-status>HTTP://221.130.45.201/nav/GetSystemStatus.aspx</get-system-status>    取系统状态

这个配置信息放到飞信用户目录下是有原因的:SIP 能够支持,登录的服务器是可以分用户群的,不同的用户可以登录不同的 Proxy,每个飞信用户(手机)可以分别登录到本省的 Proxy,就如同手机和电话网络一样。

用户的密码保存在 Application Data/Fetion 目录下的 configuration.dat 中,这个密码明文进行了加密(三重 DES,后面有说明,并给出解密方法)。

飞信的配置文件和用户密码的加密方法  返回目录

飞信配置文件的变换方法

飞信的配置文件其实不是加密,只是进行了一下变换。这种变换的编码方法是:将原来的 XML 文本 (UTF-8) 进行压缩,然后将压缩后的结果进行 BASE64 编码,编码后的结果作为 XML 的 DocumentElement 的值,同时在 XML 的 DocumentElement 的 Attributes 中,加上 StoreMode=1。

压缩采用的是标准 GZIP 算法,飞信用的是 .NET 的类库 System.IO.Compression.GZipStream。

nathan 写了一个工具 FetionCfgEditor 可以打开编辑这里的文件。

可以用这个修改飞信的群发短信的人数限制。目前,飞信群发短信时只能发给 8 个人,可以用 FetionCfgEditor 打开 configuration.dat,然后修改:<batch-sms-max-receivers>8</batch-sms-max-receivers>

飞信用户密码的加密方法

可以从 configuration.dat 得到用户的密码,这个是用 TripleDES 加密过的密码,飞信使用 .NET 的 System.Security.Cryptography。

TripleDES 是对称加密算法,只要知道 KEY 和 IV 就可以将其解密。

IV 在飞信中是固定的值。

KEY 的算法是:取当前用户的 Windows SID,表示成字符形式 "S-1-5-xx-xxxxxxxxx-xxxxxxxxx...",求这个字串的MD5,KEY 即为这个字符串的 MD5 散列值。

飞信协议支持的 SIP 方法及消息头域  返回目录

飞信所使用的协议版本标记是 "SIP-C/2.0",协议栈中标记的版权信息是 "Copyright (c) 2004-2006 China Mobile Limited. All rights reserved."。飞信对标准 SIP 协议 (RFC3261) 作了改动,是非标准的 SIP 协议。另外,飞信的设计者来自微软,所以可以参考微软的实时通信协议:Microsoft Real-Time Communications: Protocols and Technologies。

飞信的 SIP-C/2.0 协议中用到的头域有:

全称              飞信的缩写Authorization      ACall-ID            IContact            MContentEncoding    EContentLength      LContentType        CCSeq               QDate               DEndPoints          EPEvent              NExpires            XFrom               FMessageID          XIReferredBy         RBReferTo            RTRequire            RQRosterManager      RMSource             SOSupported          KTo                 TUnsupported        UKWWWAuthenticate    W

以上有些头域在 RFC3261 中没有定义,其它 SIP 相关协议草案中有定义,如 Event。

消息体通过是用 SDP 协议 (RFC2327) 描述的,当然也可以是直接文本或 XML,如果是 SDP,按照协议应该由头部的 Content-Type (MIME) 来决定,但飞信不是。

飞信所支持的 SIP Request Method 有包括:

  • ACK | A 方法

    在飞信 SIP-C/2.0 中,缩写为 A,用以 Session 发起方向 SIP Proxy 确认会话建立。

    以下是一个 ACK 消息:

    查看源代码
    打印帮助
    A fetion.com.cn SIP-C/2.0
    I: 16
    Q: 1 A
    T: sip:987654321@fetion.com.cn;p=1234
    F: 123456789

    其中 123456789 是发起方 (From) 的 SIP 地址,这个没按 SIP 标准来表达,只是用了一个飞信号码。

    而接收目标地址 (To) 的 SIP 地址是完全的 SIP 地址 (SIP/SIPS URI)。SIP/SIPS URI 的规范参考 RFC3261 19 节。

  • BENOTIFY | BN 方法

    这不是个标准的 SIP 方法,既没在 RFC 中定义,也没出现在 IETF 的协议草案中,这是微软在其 LCS 中定义的:

    From: MSDN

    BENOTIFY ("best effort" notify) enhances server performance by eliminating the response requirement. Otherwise, a BENOTIFY request has the same behavior as NOTIFY. Applications that require NOTIFY support need to implement similar processing for BENOTIFY."

    BENotify 是个不需要回复的 NOTIFY,微软扩展出这个方法是为了支持大量用户。以下是飞信的一个 BENotify 消息,表示用户 987654321 的在线状态的变化:

    查看源代码
    打印帮助
    BN 123456789 SIP-C/2.0
    Q: 13 BN
    N: presence
    X: xxxx
    I: 9
    L: xxx
      
    <events><event type="PresenceChanged"><presence uri="sip:987654321@fetion.com.cn;p=xxxx"><basic value="100" device-id="PCCL025722" device-type="PC" device-caps="simple-im,im-session,temp-group" /></presence></event></events>
  • BYE | B 方法

    以下是一个 BYE 的请求消息,用以结束会话:

    查看源代码
    打印帮助
    B fetion.com.cn SIP-C/2.0
    F: 123456789
    I: 11
    Q: 2 B
    T: sip:987654321@fetion.com.cn;p=1234
  • CANCEL | C 方法

    用以取消正在进行中的 INVITE 请求。

  • INFO | IN 方法

    用以在 SIP 协议中支持应用相关的控制信息,飞信传文件时,用到了这个方法。这是 RFC 中的一个扩展。以下是一个 INFO 方法的消息:

    查看源代码
    打印帮助
    IN fetion.com.cn SIP-C/2.0
    F: 123456789
    I: 22
    Q: 3 IN
    T: sip:987654321@fetion.com.cn;p=1234
    L: 266
      
    <action type="share-content" method="request"><transmit type="relay" session-id="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" /><file name="xxxxxx.xx" size="xxx" url="HTTP://221.130.45.206/hds/RamRelayDownloadShareContent.aspx?FileUri=xxxxxxxxxxxxx" /></action>
  • INVITE | I 方法

    这是用发起会话建立过程的 SIP 方法。以下是飞信的 INVITE 方法的一个协议消息,表示用户 123456789 要求和 987654321 建立会话,准备进行文本消息的通信:

    查看源代码
    打印帮助
    I fetion.com.cn SIP-C/2.0
    F: 123456789
    I: 16
    Q: 1 I
    T: sip:987654321@fetion.com.cn;p=1234
    K: text/html-fragment
    K: multiparty
    L: 137
      
    v=0
    o=-0 0 IN xxx.xxx.xxx.xxx:xxxx
    s=session
    c=IN IP4 xxx.xxx.xxx.xxx:xxxx
    t=0 0
    m=message 1769 sip sip:123456789@fetion.com.cn;p=xxxx
  • MESSAGE | M 方法

    这是一个标准的 SIP 扩展,用以支持即时消息。以下是飞信的一个 MESSAGE 消息,用户 123456789 向用户 987654321 发消息说“Hello!你好”:

    查看源代码
    打印帮助
    M fetion.com.cn SIP-C/2.0
    F: 123456789
    I: 16
    Q: 2 M
    T: sip:987654321@fetion.com.cn;p=1972
    C: text/html-fragment
    K: SaveHistory
    L: 121
      
    <Font Face='Arial' Color='-16777216' Size='9'>Hello!</Font><Font Face='SimSun' Color='-16777216' Size='12'>你好</Font>
  • NEGOTIATE | NEG 方法

    这是一个扩展 SIP 方法,用以在会话建立 (INVITE) 前协商会话相关的参数。

  • NOTIFY | N 方法

    这是一个扩展 SIP 方法 (RFC3265)。消息见前面的 BENOTIFY。这个消息是需要进行回复的。

  • OPTIONS | O 方法

    标准的 SIP 方法,用来查询对端或服务器的能力。比如了解对方支持什么编码类型。在飞信传文件时使用了以下消息:

    查看源代码
    打印帮助
    O fetion.com.cn SIP-C/2.0
    F: 123456789
    I: 22
    Q: 2 O
    K: ShareContent
    T: sip:987654321@fetion.com.cn;p=xxxx
  • REFER | REF 方法

    这是一个扩展 SIP 方法,其功能是要求接受方通过使用在请求中提供的联系地址信息联系第三方。

  • REGISTER | R 方法

    这是 SIP 标准方法,用来向服务器登记。如以下飞信在注册时发出的消息:

    查看源代码
    打印帮助
    R fetion.com.cn SIP-C/2.0
    F: 123456789
    I: 1
    Q: 2 R
    A: Digest response="xxxxxxxxxxxxxxxxxxxxxxxxxx",cnonce="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
    L: 147
      
    <args><device type="PC" version="131" /><caps value="simple-im;im-session;temp-group" /><events value="contact;permission;system-message" /></args>
  • SERVICE | S 方法

    这是一个扩展 SIP 方法,用来向 SIP 服务器请求额外的服务。如以下飞信发出的消息:

    (1). 飞信客户端要求服务器返回用户信息

    查看源代码
    打印帮助
    S fetion.com.cn SIP-C/2.0
    F: 123456789
    I: 2
    Q: 1 S
    N: GetPersonalInfo
    L: 172
      
    <args><personal version="11" attributes="all" /><services version="11" attributes="all" /><config version="109" attributes="all" /><mobile-device attributes="all" /></args>

    (2). 通过服务器开始飞信语聊

    查看源代码
    打印帮助
    S fetion.com.cn SIP-C/2.0
    F: 123456789
    I: 12
    Q: 1 S
    N: StartVoiceChat
    L: 103
      
    <args><voice-chat begin-date="2007-01-01 00:00:00.1234" /><users><user sid="987654321" /></users></args>
  • SUBSCRIBE | SUB 方法

    这是一个扩展 SIP 方法 (RFC3265)。这个方法被用来向服务器订阅事件异步通知。服务器会用 NOTIFY 或 BENOTIFY 方法,将事件通知给飞信客户端。如飞信订阅用户的 Presence 事件,比如上线、下线:

    查看源代码
    打印帮助
    SUB fetion.com.cn SIP-C/2.0
    F: 123456789
    I: 1
    Q: 1 SUB
    N: presence
    L: xxx
      
    <args><subscription><contacts><contact uri="sip:xxxxxxxx@fetion.com.cn;p=xxxx" /><presence><basic attributes="all" /><personal attributes="all" /><extended types="sms;location;listening;ring-back-tone" /></presence></subscription></args>

飞信的文本消息聊天交互过程  返回目录

飞信的文本消息聊天的交互过程,对应于 RFC3261 中 SIP 梯形图 (SIP Trapezoid) 中的 Media Session 阶段

RFC3428 中对标准 SIP 扩展了一个 MESSAGE 方法,通过 MESSAGE 方法承载这种即时文本消息,如下:

        User 1                  Proxy                     User 2          | F1 MESSAGE           |                         |          |--------------------> | F2 MESSAGE              |          |                      | ----------------------->|          |                      |                         |          |                      | F3 200 OK               |          |                      | <-----------------------|          | F4 200 OK            |                         |          |<-------------------- |                         |          |                      |                         |

文本消息聊天示例

用例:

聊天双方的飞信均在 TCP 承载下工作。

用户 A,飞信号: 123456789, IP: 111.111.111.111:1111

用户 B,飞信号: 987654321, IP: 222.222.222.222:2222

用户 A 打开用户列表向 用户 B 发送文本消息 "Hello!测试",然后 用户 A 关闭了聊天窗口,结束了这次消息对话过程。

用户 A 与 SIP Proxy 的交互如下:

  1. 用户 A 的 INVITE 请求

    111.111.111.111:1111 → 221.130.45.203:8080 (SIP Proxy)

    查看源代码
    打印帮助
    I fetion.com.cn SIP-C/2.0
    F: 123456789
    I: 16
    Q: 1 I
    T: sip:987654321@fetion.com.cn;p=1972
    K: text/html-fragment
    K: multiparty
    L: 137
      
    v=0
    o=-0 0 IN 111.111.111.111:1111
    s=session
    c=IN IP4 111.111.111.111:1111
    t=0 0
    m=message 1111 sip sip:123456789@fetion.com.cn;p=xxxx

    上面是发到 Proxy 的第一条消息,是一个 INVITE 请求,发到飞信的服务器 221.130.45.203:8080。SIP 承载协议是 TCP,消息放在 TCP 的包中,使用 UTF-8 编码。

    第 1 行是请求行,I = INVITE,"fetion.com.cn" 是 Request-URI,"SIP-C/2.0" 是当前的 SIP 协议版本。

    第 2 行到 L: 137 之间是头域。

    F = From,标识从用户 A (123456789) 发出的请求。

    I = Call-ID,按标准的 SIP 的规定,这应该是一个随机产生的一个全局唯一的标识符,应该在客户端和服务器整个交互的过程中保持不变,但在飞信的 SIP-C 协议中,这是一个序列号,第一次与 Proxy 建立连接发出第一个请求时,这个值为 1,以后每次发出一个请求加 1。

    Q = CSeq,它由一个整数的序列号和一个 SIP 方法组成,SIP 方法与这个消息的 SIP 方法相同,这个序列号在一个会话过程中每次加 1,这样来标识 SIP 消息的顺序。

    T = To,逻辑通信目的地址,注意和 Request-URI 的区别:Request-URI 是下一跳的地址,To 是整个 Dialog 期望的目标地址。

    K = Supported,表示客户端所支持的扩展。

    L = Content-Length,消息体的长度。

    从 v=0 开始的是消息体,用 SDP 描述:

    v= (protocol version),在目前飞信的 INVITE 中,固定的 v=0。

    o= (owner/creator and session identifier),在目前飞信的 INVITE 中,前面是固定的,后面是发起人(用户 A)的 IP 地址和端口。

    s= (session name),在目前飞信的 INVITE 中,固定的为 s=session。

    c=* (connection information - not required if included in all media),在目前飞信的 INVITE 中,除后面的发起人(用户 A)的 IP 地址和端口外,前面是固定的。

    t= (time the session is active),在目前飞信的 INVITE 中,固定的为 "t=0 0"。

    m= (media name and transport address),在目前飞信的 INVITE 中,"1111" 是发起人的 TCP 端口,后面的 sip:xxxxxxxx,是发起人的 URI。

  2. Proxy 对 INVITE 的响应 (I:16, Q:1 I),表示正在查找 用户 B。

    111.111.111.111:1111 ← 221.130.45.203:8080

    查看源代码
    打印帮助
    SIP-C/2.0 100 Trying
    Q: 1 I
    T: sip:987654321@fetion.com.cn;p=xxxx
    I: 16
  3. Proxy 对 INVITE 的响应 (I:16, Q:1 I),表示 INVITE 执行成功 (200 OK)。其中 222.222.222.222:2222 是 用户 B 的 IP 和端口。

    111.111.111.111:1111 ← 221.130.45.203:8080

    查看源代码
    打印帮助
    SIP-C/2.0 200 OK
    Q: 1 I
    K: text/html-fragment
    K: multiparty
    T: sip:123456789@fetion.com.cn;p=xxx
    I: 16
    L: 135
      
    v=0
    o=-0 0 IN 222.222.222.222:2222
    s=session
    c=IN IP4 222.222.222.222:2222
    t=0 0
    m=message 2222 sip sip:987654321@fetion.com.cn;p=xxx
  4. 用户 A 向 Proxy 发出 ACK 消息。注意:INVITE-2xx-ACK 的特殊性,它是一个独立的 SIP 事务。

    111.111.111.111:1111 → 221.130.45.203:8080

    查看源代码
    打印帮助
    A fetion.com.cn SIP-C/2.0
    I: 16
    Q: 1 A
    T: sip:987654321@fetion.com.cn;p=xxx
    F: 123456789
  5. 用户 A 向 用户 B 发文本消息,内容是带 XML 格式的 "Hello!测试"。

    使用 MESSAGE | M 方法,消息体是 XML 表示的消息,消息头和 INVITE 相似。

    111.111.111.111:1111 → 221.130.45.203:8080

    查看源代码
    打印帮助
    M fetion.com.cn SIP-C/2.0
    F: 123456789
    I: 16
    Q: 2 M
    T: sip:987654321@fetion.com.cn;p=xxxx
    C: text/html-fragment
    K: SaveHistory
    L: 121
      
    <Font Face='Arial' Color='-16777216' Size='9'>Hello! </Font><Font Face='SimSun' Color='-16777216' Size='12'>测试</Font>
  6. 这是 用户 B 收到 用户 A 发出的文本消息后回过来的响应,结果是 200 OK,其中 XI = MessageID。

    111.111.111.111:1111 ← 221.130.45.203:8080

    查看源代码
    打印帮助
    SIP-C/2.0 200 OK
    Q: 2 M
    T: sip:123456798@fetion.com.cn;p=xxxx
    I: 16
    D: Mon, 01 Mar 2007 00:00:00 GMT
    XI: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  7. 用户 A 关闭聊天窗口结束聊天,向 用户 B 发出了一个 BYE 消息。

    111.111.111.111:1111 → 221.130.45.203:8080

    查看源代码
    打印帮助
    B fetion.com.cn SIP-C/2.0
    F: 123456789
    I: 16
    Q: 3 B
    T: sip:987654321@fetion.com.cn;p=xxxxx
  8. 用户 B 对 用户 A 的 BYE 的响应。

    111.111.111.111:1111 ← 221.130.45.203:8080

    查看源代码
    打印帮助
    SIP-C/2.0 200 OK
    Q: 3 B
    T: sip:123456789@fetion.com.cn;p=xxxx
    I: 16

在 用户 B 那边,整个交互过程和 用户 A 这边对应,例如:用户 A 在发出 INVITE,要求和 B 会话,而用户 B 这边则是回应邀请。

用飞信发短信时,基本流程类似,如发一个内容为 "test" 的短信到 用户 B 时,MESSAGE 消息如下:

111.111.111.111:1111 → 221.130.45.203:8080

查看源代码
打印帮助
M fetion.com.cn SIP-C/2.0
F: 12345678
I: 16
Q: 1 M
T: sip:987654321@fetion.com.cn;p=xxxx
N: SendSMS
L: 4
  
test

注意上面的 N = Event 头域的值 SendSMS,表示发送短信。

上面分析是飞信通过 TCP 承载 SIP 时的情况,如果飞信通过 HTTP 承载呢,过程跟上面类似,不同的是,连接的是 221.130.45.203:80,协议是HTTP,采用的 POST 请求如下:

查看源代码
打印帮助
POST /ht/sd.aspx?t=s&i=2 HTTP/1.1
Host: 221.130.45.203
...
  
(Post Content)

POST 的消息体就是上面的 SIP 消息。

飞信的文本聊天消息的内容是未加密又未变换的.

To 头域中的 p=xxxx (To tag),和手机号码号段有关系,手机号码前 6 位数字相同的 p 值是相同的(电信运营商经常把前 6 位配成一个号首集 DNSET)。p 的值和手机号码的对应关系如下:

可以用上面提到的方法查看好友列表文件 contacts.dat,发现 p=[手机号码前 6 位] - 134099,估计 134100 是中移动的第一个号段。

飞信手机语聊的 SIP 交互过程  返回目录

用例:

用户 A,飞信号: 123456789, IP: 111.111.111.111:1111

用户 B,飞信号: 987654321, IP: 222.222.222.222:2222

用户 A 向 用户 B 发起手机语聊。

用户 A 的这边与 Proxy 的交互:

  1. 通过 Proxy 转发,发出 INVITE 到 用户 B:

    111.111.111.111:1111 → 221.130.45.203:8080

    查看源代码
    打印帮助
    I fetion.com.cn SIP-C/2.0
    F: 1234567890
    I: 11
    Q: 1 I
    T: sip:987654321@fetion.com.cn;p=xxxx
    L: 180
      
    v=0
    o=-0 0 IN P4111.111.111.111:1111
    s=VoiceChat
    c=IN IP4 111.111.111.111:1111
    t=0 0
    a=user:sip:987654321@fetion.com.cn;p=xxxx
    m=ivr 1111 sip sip:123456789@fetion.com.cn;p=yyyy
  2. 收到 Proxy 返回的 "正在联络 B":

    111.111.111.111:1111 ← 221.130.45.203:8080

    查看源代码
    打印帮助
    SIP-C/2.0 100 Trying
    Q: 1 I
    T: sip:987654321@fetion.com.cn;p=xxxx
    I: 11
  3. 收到 Proxy 转来的 用户 B 的同意消息:

    111.111.111.111:1111 ← 221.130.45.203:8080

    查看源代码
    打印帮助
    SIP-C/2.0 200 OK
    Q: 1 I
    T: sip:987654321@fetion.com.cn;p=xxxx
    I: 11
  4. 回复 用户 B 已收到其同意的消息 (ACK):

    111.111.111.111:1111 → 221.130.45.203:8080

    查看源代码
    打印帮助
    A fetion.com.cn SIP-C/2.0
    I: 11
    Q: 1 A
    T: sip:987654321@fetion.com.cn;p=xxxx
    F: 123456789
  5. 要求 Proxy 开始通过手机进行语聊,这里发出的是 S = Service 方法,用来请求服务器提供额外的服务,这个服务就是 N: StartVoiceChat。

    111.111.111.111:1111 → 221.130.45.203:8080

    查看源代码
    打印帮助
    S fetion.com.cn SIP-C/2.0
    F: 123456789
    I: 12
    Q: 1 S
    N: StartVoiceChat
    L: 103
      
    <args><voice-chat begin-date="2007-00-00 00:00:00.0000" /><users><user sid="987654321" /></users></args>

    到这里就跟飞信无关了,下面就是 Proxy 那边在通过 PLMN 网络接通两部手机,开始媒体会话。

用户 B 方面:

  1. 用户 B 收到 INVITE:

    222.222.222.222:2222 ← 221.130.45.203:8080

    查看源代码
    打印帮助
    I 123456789 SIP-C/2.0
    Q: 1 I
    F: sip:987654321@fetion.com.cn;p=xxxx
    I: -1
    L: 178
      
    v=0
    o=-0 0 IN P4222.222.222.222:2222
    s=VoiceChat
    c=IN IP4 222.222.222.222:2222
    t=0 0
    a=user:sip:123456789@fetion.com.cn;p=yyyy
    m=ivr 3418 sip sip:987654321@fetion.com.cn;p=xxxx

    收到这个后,用户 B 的飞信会弹出窗口,问用户 B 是否接受用户 A 发起的手机语聊。这个消息的消息体中的 a=,根据 SDP,这里使用 attribute:value 的形式。

  2. 用户 B 发出同意消息:

    222.222.222.222:2222 → 221.130.45.203:8080

    查看源代码
    打印帮助
    SIP-C/2.0 200 OK
    I: -1
    Q: 1 I
    F: sip:987654321@fetion.com.cn;p=xxxx
    L: 180
      
    v=0
    o=-0 0 IN P4222.222.222.222:2222
    s=VoiceChat
    c=IN IP4 222.222.222.222:2222
    t=0 0
    a=user:sip:987654321@fetion.com.cn;p=xxxx
    m=ivr 1082 sip sip:123456789@fetion.com.cn;p=yyyy
  3. 收到 用户 A 的 ACK 确认:

    222.222.222.222:2222 ← 221.130.45.203:8080

    查看源代码
    打印帮助
    A 123456789 SIP-C/2.0
    Q: 1 A
    F: sip:987654321@fetion.com.cn;p=xxxx
    I: -1

    到这里 用户 B 手机也就响了。

其实,上面这个过程比飞信的文本消息即时通信更能体现 SIP 的作用,SIP 的作用就是建立会话,至于会话的具体媒体,如:语音还是数据,这都不是 SIP 协议所关心的事情。

飞信登录时的身份验证  返回目录

飞信的整个登录过程包括取系统配置、验证身分、取联系人、取离线消息等过程,下面主要说明身份验证的过程。

飞信有两步验证身份,第一次是通过 SSL 向 SSIPortal 登录时,第二次是用 SIP 协议向 Proxy 注册时。

  1. 向 SSIPortal 登录

    飞信开始用户登录时,向 nav.fetion.com.cn 登录,这是飞信的 SSIPortal(单点登录入口)。使用 HTTP GET,如下:

    https://nav.fetion.com.cn/ssiportal/SSIAppSignIn.aspx?mobileno=13XXXXXXXXX&pwd=XXXX

    pwd 就是这个用户的密码。服务器验证通过后,就会返回 200 OK,Cookie 中就有关键的 SSIC 了。SSIC 应该是服务器给客户端的 Credential,以后再跟服务器以 HTTP 的方式打交道,就必须带上这个 Credential 了,如下一步可以向 geturi.aspx 发 SIP 请求:

    查看源代码
    打印帮助
    POST /hds/geturi.aspx HTTP/1.1
    User-Agent: IIC2.0/PC 2.1.0.0
    Host: 221.130.45.205
    Cookie: ssic=xxxxxxxxxxxxxxxx
    Content-Length: 0
    Connection: Keep-Alive

    如果飞信采用 HTTP 而非 TCP 的 SIP 承载,那么每次 POST 请求都必须带上这个 SSIC。

    除了获得 Credential 外,此阶段登录还获得用户的 SIP URI,如 "sip:123456789@fetion.com.cn;p=xxxx"。

  2. 向 SIP Proxy 注册

    完成上一阶段登录后,就开始 SIP-C 的协议交互过程了,即用 REGISTER 方法向 Proxy 注册:

    xxx.xxx.xxx.xxx:xxx → 221.130.45.203:8080

    查看源代码
    打印帮助
    R fetion.com.cn SIP-C/2.0
    F: 123456789
    I: 1
    Q: 1 R
    L: 249
      
    <args>.......</args>

    xxx.xxx.xxx.xxx:xxxx ← 221.130.45.203:8080

    查看源代码
    打印帮助
    SIP-C/2.0 401 Unauthorized
    I: 1
    Q: 1 R
    W: Digest algorithm="MD5-sess",nonce="xxxxxxxxxxxxxxxx"

    xxx.xxx.xxx.xxx:xxxx → 221.130.45.203:8080

    查看源代码
    打印帮助
    R fetion.com.cn SIP-C/2.0
    F: 123456789
    I: 1
    Q: 2 R
    A: Digest response="xxxxxxxxxxxxxxx",cnonce="xxxxxxxxxxxxxxxx"
    L: 249
      
    <args>...</args>

    xxx.xxx.xxx.xxx:xxxx ← 221.130.45.203:8080

    查看源代码
    打印帮助
    SIP-C/2.0 200 OK
    I: 1
    Q: 2 R
    X: 600
    L: 877
      
    <results>...</results>

    这是个标准的 Digest Authentication 验证过程,参考 SIP 或 HTTP 协议。

    在 W = WWWAuthenticate 这个头域中,nonce 是服务器给出的,那么 response 和 cnonce 是怎么计算出来的呢?

    计算 response 和 cnonce 需要用到 sid(指飞信号)、passwd、domain(用户所属的域,例如:fetion.com.cn)、nonce。飞信计算 cnonce 是用了 4 个随机数组成一个 16 进制的字符串然后求这个字符串的 MD5;response 的计算方法是:

    (1). 求 "sid:domain:passwd" 这个 UTF-8 字符串的 MD5 值,得出 key。

    (2). 求 "key:nonce:cnonce" 这个 UTF-8 字符串的 MD5 字符串,得出 H1。

    (3). 求 "REGISTER:sid" 这个 UTF-8 字符串的 MD5 字符串,得出 H2。

    (4). 求 "H1:nonce:H2" 这个 UTF-8 字符串的 MD5 字符串,即为 response。

转载或改编时,请务必以链接形式注明文章 原始出处、作者信息 和 著作声明:

文章标题:飞信的 SIP 协议分析

 收藏者:Breaker

著作声明:收藏 分类文章,请遵循原作者的著作声明

文章地址:http://codingdao.com/wp/post/fetion-sip-protocol-analysis/

 

 

转自

http://codingdao.com/wp/post/fetion-sip-protocol-analysis/