RealmSession.cs 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. using System;
  2. using System.Linq;
  3. using System.Net.Sockets;
  4. using System.Numerics;
  5. using System.Security.Cryptography;
  6. using System.Threading.Tasks;
  7. using Helper;
  8. using Log;
  9. using Robot.Protos;
  10. namespace Robot
  11. {
  12. public class RealmSession: IDisposable
  13. {
  14. private readonly TcpClient tcpClient = new TcpClient();
  15. private NetworkStream networkStream;
  16. private readonly RealmInfo realmInfo = new RealmInfo();
  17. public void Dispose()
  18. {
  19. this.tcpClient.Close();
  20. this.networkStream.Dispose();
  21. }
  22. public async void SendMessage<T>(ushort opcode, T message)
  23. {
  24. byte[] protoBytes = ProtobufHelper.ToBytes(message);
  25. var neworkBytes = new byte[sizeof (int) + sizeof (ushort) + protoBytes.Length];
  26. int totalSize = sizeof (ushort) + protoBytes.Length;
  27. var totalSizeBytes = BitConverter.GetBytes(totalSize);
  28. totalSizeBytes.CopyTo(neworkBytes, 0);
  29. var opcodeBytes = BitConverter.GetBytes(opcode);
  30. opcodeBytes.CopyTo(neworkBytes, sizeof (int));
  31. protoBytes.CopyTo(neworkBytes, sizeof (int) + sizeof (ushort));
  32. await this.networkStream.WriteAsync(neworkBytes, 0, neworkBytes.Length);
  33. }
  34. public async Task<SMSG_Password_Protect_Type> Handle_CMSG_AuthLogonPermit_Response()
  35. {
  36. var result = await this.RecvMessage();
  37. ushort opcode = result.Item1;
  38. byte[] message = result.Item2;
  39. if (opcode != MessageOpcode.SMSG_PASSWORD_PROTECT_TYPE)
  40. {
  41. Logger.Trace("opcode: {0}", opcode);
  42. throw new RealmException(string.Format("error opcode: {0}", opcode));
  43. }
  44. var smsgPasswordProtectType =
  45. ProtobufHelper.FromBytes<SMSG_Password_Protect_Type>(message);
  46. Logger.Trace("message: {0}", JsonHelper.ToString(smsgPasswordProtectType));
  47. if (smsgPasswordProtectType.Code != 200)
  48. {
  49. throw new RealmException(string.Format(
  50. "SMSG_Lock_For_Safe_Time: {0}",
  51. JsonHelper.ToString(smsgPasswordProtectType)));
  52. }
  53. return smsgPasswordProtectType;
  54. }
  55. public async Task<SMSG_Auth_Logon_Challenge_Response>
  56. Handle_SMSG_Auth_Logon_Challenge_Response()
  57. {
  58. var result = await this.RecvMessage();
  59. ushort opcode = result.Item1;
  60. byte[] message = result.Item2;
  61. if (opcode != MessageOpcode.SMSG_AUTH_LOGON_CHALLENGE_RESPONSE)
  62. {
  63. Logger.Trace("opcode: {0}", opcode);
  64. }
  65. Logger.Trace("message: {0}", message.ToHex());
  66. var smsgAuthLogonChallengeResponse =
  67. ProtobufHelper.FromBytes<SMSG_Auth_Logon_Challenge_Response>(message);
  68. if (smsgAuthLogonChallengeResponse.ErrorCode != ErrorCode.REALM_AUTH_SUCCESS)
  69. {
  70. Logger.Trace("error code: {0}", smsgAuthLogonChallengeResponse.ErrorCode);
  71. throw new RealmException(
  72. string.Format("SMSG_Auth_Logon_Challenge_Response ErrorCode: {0}",
  73. JsonHelper.ToString(smsgAuthLogonChallengeResponse)));
  74. }
  75. Logger.Debug("SMSG_Auth_Logon_Challenge_Response: \n{0}",
  76. JsonHelper.ToString(smsgAuthLogonChallengeResponse));
  77. return smsgAuthLogonChallengeResponse;
  78. }
  79. public async Task<Tuple<ushort, byte[]>> RecvMessage()
  80. {
  81. int totalReadSize = 0;
  82. int needReadSize = sizeof (int);
  83. var packetBytes = new byte[needReadSize];
  84. while (totalReadSize != needReadSize)
  85. {
  86. int readSize = await this.networkStream.ReadAsync(
  87. packetBytes, totalReadSize, packetBytes.Length);
  88. if (readSize == 0)
  89. {
  90. throw new RealmException("connection closed!");
  91. }
  92. totalReadSize += readSize;
  93. }
  94. int packetSize = BitConverter.ToInt32(packetBytes, 0);
  95. Logger.Debug("packet size: {0}", packetSize);
  96. // 读opcode和message
  97. totalReadSize = 0;
  98. needReadSize = packetSize;
  99. var contentBytes = new byte[needReadSize];
  100. while (totalReadSize != needReadSize)
  101. {
  102. int readSize = await this.networkStream.ReadAsync(
  103. contentBytes, totalReadSize, contentBytes.Length);
  104. if (readSize == 0)
  105. {
  106. throw new RealmException("connection closed!");
  107. }
  108. totalReadSize += readSize;
  109. }
  110. ushort opcode = BitConverter.ToUInt16(contentBytes, 0);
  111. Logger.Debug("opcode: {0}", opcode);
  112. var messageBytes = new byte[needReadSize - sizeof (ushort)];
  113. Array.Copy(contentBytes, sizeof (ushort), messageBytes, 0, messageBytes.Length);
  114. return new Tuple<ushort, byte[]>(opcode, messageBytes);
  115. }
  116. public async Task ConnectAsync(string hostName, ushort port)
  117. {
  118. await this.tcpClient.ConnectAsync(hostName, port);
  119. this.networkStream = this.tcpClient.GetStream();
  120. }
  121. public async void Login(string account, string password)
  122. {
  123. byte[] passwordBytes = password.ToByteArray();
  124. MD5 md5 = MD5.Create();
  125. byte[] passwordMd5 = md5.ComputeHash(passwordBytes);
  126. byte[] passwordMd5Hex = passwordMd5.ToHex().ToLower().ToByteArray();
  127. // 发送帐号和密码MD5
  128. var cmsgAuthLogonPermit = new CMSG_Auth_Logon_Permit
  129. {
  130. Account = account.ToByteArray(),
  131. PasswordMd5 = passwordMd5Hex
  132. };
  133. Logger.Trace("account: {0}, password: {1}",
  134. cmsgAuthLogonPermit.Account, cmsgAuthLogonPermit.PasswordMd5.ToStr());
  135. this.SendMessage(MessageOpcode.CMSG_AUTH_LOGON_PERMIT, cmsgAuthLogonPermit);
  136. await this.Handle_CMSG_AuthLogonPermit_Response();
  137. // 这个消息已经没有作用,只用来保持原有的代码流程
  138. var cmsgAuthLogonChallenge = new CMSG_Auth_Logon_Challenge();
  139. this.SendMessage(MessageOpcode.CMSG_AUTH_LOGON_CHALLENGE, cmsgAuthLogonChallenge);
  140. var smsgAuthLogonChallengeResponse =
  141. await this.Handle_SMSG_Auth_Logon_Challenge_Response();
  142. // 以下是SRP6处理过程
  143. var n = smsgAuthLogonChallengeResponse.N.ToUBigInteger();
  144. var g = smsgAuthLogonChallengeResponse.G.ToUBigInteger();
  145. var b = smsgAuthLogonChallengeResponse.B.ToUBigInteger();
  146. var salt = smsgAuthLogonChallengeResponse.S.ToUBigInteger();
  147. var srp6Client = new SRP6Client(
  148. new SHA1Managed(), n, g, b, salt, account.ToByteArray(), passwordMd5Hex);
  149. Logger.Debug("s: {0}\nN: {1}\nG: {2}\nB: {3}\nA: {4}\nS: {5}\nK: {6}\nm: {7}",
  150. srp6Client.Salt.ToUBigIntegerArray().ToHex(),
  151. srp6Client.N.ToUBigIntegerArray().ToHex(),
  152. srp6Client.G.ToUBigIntegerArray().ToHex(),
  153. srp6Client.B.ToUBigIntegerArray().ToHex(),
  154. srp6Client.A.ToUBigIntegerArray().ToHex(),
  155. srp6Client.S.ToUBigIntegerArray().ToHex(),
  156. srp6Client.K.ToUBigIntegerArray().ToHex(),
  157. srp6Client.M.ToUBigIntegerArray().ToHex());
  158. var cmsgAuthLogonProof = new CMSG_Auth_Logon_Proof
  159. {
  160. A = srp6Client.A.ToUBigIntegerArray(),
  161. M = srp6Client.M.ToUBigIntegerArray()
  162. };
  163. this.SendMessage(MessageOpcode.CMSG_AUTH_LOGON_PROOF, cmsgAuthLogonProof);
  164. }
  165. }
  166. }