由于现所在的公司的应用需要,因而有机会自己接触到了MSN 通信协议。前2周左右的时间里,自己完成了部分的功能,算是对协议的一次验证吧。在此贴出本文,希望能有抛砖引玉之效,言归正传吧。
与客户端交互的MSN的服务器种类有三种:分派服务器(Dispatch Server, DS),通知服务器(Notification Server, NS),以及接线服务器(Switchboard Server, SS)。相关定义介绍可以看这里http://ericzhi-room.spaces.live.com/blog/cns!A1EE72D8F7FD1!217.entry?sa=842045214 。
MSN 客户端登陆过程如下(以MSNP8为例)
1. 以TCP连接(DS)messenger.hotmail.com:1863,发送指令:
C>> VER 1 MSNP8 CVR0(\r\n)
DS<< VER 1 MSNP8
2. 客户端发送CVR指令说明当前使用的环境
C>> CVR 2 0x0804 win 5.1.2600 i386 MSNMSGR 7.0.0813 MSMSGS [email protected]
DS<< CVR 2 1.0.0000 1.0.0000 1.0.0000 http://msgr.dlservice.microsoft.com http://download.live.com/?sku=messenger
3. 客户端发送USR说明身份
C>> USR 3 TWN I [email protected]
DS>> XFR 3 NS 207.46.124.105:1863 0 64.4.9.254:1863
// XFR 命令指出客户端可以与NS 服务器进行通讯连接了。其中第三个参数为NS服务器的通讯地址,第五个是当前连接的实际服务器IP与端口。
4. 客户端关闭与以上DS的连接
C>> OUT
5. 客户端与NS服务器建立连接,发送之前的指令消息:
C>> VER 1 MSNP8 CVR0(\r\n)
NS<< VER 1 MSNP8
C>> CVR 2 0x0804 win 5.1.2600 i386 MSNMSGR 7.0.0813 MSMSGS [email protected]
NS<< CVR 2 1.0.0000 1.0.0000 1.0.0000 http://msgr.dlservice.microsoft.com http://download.live.com/?sku=messenger
C>> USR 3 TWN I [email protected]
NS<< USR 3 TWN S ct = 127137673, rver=5.5.4182.0, wp=FS_40SEC_0_COMPACT,lc=1033,id=507,ru=http:%2F%2messenger.msn.com, tw=0,kpp=1,kv=4,ver=2.1.6000.1,rn=1lgjbfIL,tpf=b073adghiuhwg142614
// 注意到TWN命令S 表示后面的身份验证过程开始,它之后的字符串被称为Challenge String,用于后面的SSL 身份验证 .
6. 使用SSL协议进行身份验证的一般过程
(1)以SSL连接到[https://]nexus.passport.com:443端口,发送指令:
GET /rdr/pprdr.asp HTTP/1.0\r\n \r\n
(2)从服务器返回的信息中取得PassportURLs字段中的DALogin部分,取得需要连接的服务器(如login.passport.com/login2.srf)
(3)客户端以SSL连接(2)中得到的服务器的443端口,进行如下请求:
GET /login2.srf HTTP/1.1\r\n
Authorization: OrgVerb=GET,OrgURL=%2F%2messenger.msn.com(与先前DS返回的ChallengeString保持一致),sign- [email protected],pwd=******, tw=0,kpp=1,kv=4,ver=2.1.6000.1,rn=1lgjbfIL,tpf=b073adghiuhwg142614
User-Agent: MSMSGS\r\n
Host: login.passport.com\r\n
Connection: Keep-Alive\r\n
Cache-Control: no-cache\r\n
//接下来,还是有可能失败,需要重定向到另外的服务器进行验证。如果成功,服务器返回的信息中会包含ticket信息 在from-PP='t=*********'中,保存取得的单引号中的信息即为所需ticket。
(4)关闭SSL连接,回到NS服务器上,发送包含ticket的指令:
C>> USR 4 TWN S t=*********
// 认证成功NS可收到如下返回信息,认为登陆成功。
DS<< USR 4 OK [email protected] nickname 1 0
7. 客户端发送指令同步本地联系人
C>> SYN 5 0 0\r\n
NS<< SYN 5 2010-04-15T03:52:46.047-07:00 2010-04-15T03:52:45.833-07:00 6 1
GTC A
BLP BL
// DS发送回相关的联系人的信息,联系人所在列表,群的信息等
8. 发上线的通知
//在收到DS的同步相应消息后,客户端可向服务器发送指令:
C>> CHG 6 NLN
DS<< CHS 6 NLN 0
通知在联系人列表中的contact当前用户已经上线。
MSN 连接状态的保持
1. 在上线后的一段时间内,会收到NS发送的验证信息:
DS<< CHL 0 23535 7436571354157672341575625
该命令的最后参数为随机的数字字符串(a challenge),你需要在50s 内发回QRY的应答消息,不回答或是答错都将会被NS断开连接,并且没有任何出错提示。
Client ID string | Client ID code |
---|---|
[email protected] | Q1P7W2E4J9R8U3S5 |
PROD0038W!61ZTF9 | VT6PX?UQTM4WM%YR |
PROD0058#7IL2{QD | QHDCY@7R1TB6W?5B |
PROD0061VRRZH@4F | JXQ6J@TUOGYV@N0M |
可以从以上的组合中选出一组,在响应的QRY命令中要求Client ID String, 而对应的ID Code 将与服务器发送过来的challenge组成 challenge + Client ID Code组合字符串。随后利用MD5加密算法计算这个字符串 32位的digest。发回响应的命令:
C>> QRY trID Client_ID_String 32\r\n digest
验证成功会收到NS的返回信息
NS<< QRY trID \r\n
2. 为防止端口被关闭,客户端可以定时向NS发送心跳包, 以测试当前TCP的连接状态
C>> PNG\r\n
NS<< QNG\r\n
总结:
1> 在实现协议交互的过程中,以上登陆的阶段是近乎同步的通信过程,单独得响应过程应该不会有太大的难点。着重说明的还有发送的消息格式要严格遵循,每条消息后一般都会有\r\n结尾,另外实际利用socket发送出去的数据的实际大小也要一致。否则,你的连接会被服务器悄无声息地断掉 ;)
2> 另外在利用MD5算法时,可以利用现有的开源代码。这个在协议MSNP11中验证的计算方式发生了变化,有兴趣的园友可以研究下。
3> 在以SSL方式验证时可以调用WinINet中的相关的函数(InternetOpen,InternetOpenUrl,HttpQueryInfo),获取验证服务器返回的ticket。然而这在C#中利用 WebService 却可以得到更快的实现。
4> 最重要的会是网络通信模型的选择以及多线程的应用,因为与NS的连接要一直保存下去;而对于建立/发起外部的会话,这就要关联到与不同的SS交互的过程。最后与MSN Activity 展开交互的部分,仍然成为了现阶段需要解决的难点。
Ref URL:
MSN协议分析的最佳网站
1. http://www.hypothetic.org/docs/msn/index.php
2. http://msnpiki.msnfanatic.com/index.php/MSN_Protocol_Version_8
支持MSN Messenger Client 的开源作品
3. http://www.miranda-im.org/
4. http://code.google.com/p/msnp-sharp/