コード例 #1
0
ファイル: MetaData.cs プロジェクト: IdentErr/c-raft
 internal MetaData(PacketReader rx)
 {
     byte x;
     while ((x = rx.ReadByte()) != 0x7f)
     {
         switch (x >> 5)
         {
             case 0: Data[x & 0x1f] = rx.ReadByte(); break;
             case 1: Data[x & 0x1f] = rx.ReadShort(); break;
             case 2: Data[x & 0x1f] = rx.ReadInt(); break;
             case 3: Data[x & 0x1f] = rx.ReadFloat(); break;
             case 4: Data[x & 0x1f] = rx.ReadString16(64); break;
             default: Data[x & 0x1f] = null; break;
         }
     }
 }
コード例 #2
0
ファイル: Program.cs プロジェクト: JLChnToZ/chraft.fakeserver
 /// <summary>
 /// 有新資料到達時呼叫的程式
 /// </summary>
 /// <returns>旗標 (0=斷線, 1=不用回覆, 2=有資料等候回覆)</returns>
 /// <remarks>詳細運作方法, 請參考 http://wiki.vg/Protocol_Encryption </remarks>
 public int HandleStep() {
     byte version;
     bool isDisconnect = false;
     string ServerHost;
     int ServerPort;
     try {
         PacketReader PR = new PacketReader(Buffer, Buffer.Length);
         PacketWriter PW = PacketWriter.CreateInstance();
         Queue<byte[]> strings = new Queue<byte[]>();
         AppSettingsSection config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None).AppSettings;
         // 分析客戶端指令
         switch(Buffer[0]) {
             case 0xFE: // 查詢伺服器資料: 0xFE
                 ConsoleWriteTime();
                 Console.WriteLine("@{0} 接收伺服器資訊請求. ", UserName);
                 // 傳回踢走: 0xFF + 0x00 + (通訊協定版本) + 0x00 + (伺服器版本) + 0x00 + (伺服器名稱) + 0x00 + (在線玩家數) + 0x00 + (在線玩家上限)
                 PW = kick(String.Format("§1\0{0}\0{1}\0{2}\0{3}\0{4}",
                     51,
                     config.Settings["serverVersion"].Value,
                     config.Settings["motd"].Value,
                     config.Settings["onlinePlayers"].Value,
                     config.Settings["maxPlayers"].Value));
                 isDisconnect = true;
                 break;
             case 0x02: // 接收握手: 0x02 + (客戶端版本) + (玩家 ID) + (伺服器 IP / 網址) + (伺服器連接埠)
                 version = PR.ReadByte();
                 UserName = PR.ReadString16(64);
                 ServerHost = PR.ReadString16(64);
                 ServerPort = PR.ReadInt();
                 ConsoleWriteTime();
                 Console.WriteLine("@{1} 接收握手指令.\n  玩家 {1} 使用通訊協定版本 {0} 連接到 {2}:{3}.",
                     (int)version, UserName, ServerHost, ServerPort);
                 strings.Enqueue(ASCIIEncoding.BigEndianUnicode.GetBytes(ConnectionId));
                 // 傳回加密請求: 0xFD + (伺服器 ID) + (密鑰長度) + (密鑰) + (金幣長度) + (金幣) 
                 PW = PacketWriter.CreateInstance(7 + keyLength + tokenLength, strings);
                 PW.WriteByte(0xFD);
                 PW.Write(ConnectionId);
                 PW.Write(keyLength);
                 PW.Write(publicKey, 0, keyLength);
                 PW.Write(tokenLength);
                 PW.Write(token, 0, tokenLength);
                 ConsoleWriteTime();
                 Console.WriteLine("@{3} 送出加密請求指令.\n  伺服器 ID: {0}, 密鑰長度: {1}, 金幣長度: {2}.",
                     ConnectionId, keyLength, tokenLength, UserName);
                 break;
             case 0xFC: // 接受加密: 0xFC + (分享密碼長度) + (分享密碼) + (已加密的金幣長度) + (已加密的金幣)
                 short sharedSecretLength = PR.ReadShort();
                 byte[] sharedSecret = PR.ReadBytes(sharedSecretLength);
                 short variefyTokenLength = PR.ReadShort();
                 byte[] variefyToken = PacketCryptography.Decrypt(PR.ReadBytes(variefyTokenLength));
                 ConsoleWriteTime();
                 Console.WriteLine("@{2} 接收接受加密指令.\n  分享密碼長度: {0}, 已加密的金幣長度: {1}. ",
                     sharedSecretLength, variefyTokenLength, UserName);
                 // 檢查解密後的金幣是否跟傳出去的一樣
                 if(!variefyToken.SequenceEqual(PacketCryptography.VerifyToken)) {
                     // 不一樣, 密鑰無效
                     ConsoleWriteTime();
                     Console.Write("@{0} 金幣不正確.", UserName);
                     PW = kick(config.Settings["tokenInvalidText"].Value);
                 } else {
                     // 是一樣, 密鑰有效
                     ConsoleWriteTime();
                     Console.WriteLine("@{0} 金幣吻合.", UserName);
                     ConsoleWriteTime();
                     Console.WriteLine("@{0} 檢查玩家是否正版...", UserName);
                     // 檢查是否正版
                     // 查詢地址: http://session.minecraft.net/game/checkserver.jsp?user=(玩家ID)&serverId=(伺服器混湊值)
                     // 伺服器混湊值 = (伺服器 ID 的 ASCII 碼) + (客戶端傳來的分享密碼) + (伺服器密鑰) -> ...
                     // ... -> 轉換成 SHA -> 除掉前面的 0 再以二進制補碼補上負號
                     switch(verifyMinecraft(UserName, PacketCryptography.JavaHexDigest(
                         Encoding.UTF8.GetBytes(ConnectionId)
                         .Concat(PacketCryptography.Decrypt(sharedSecret))
                         .Concat(PacketCryptography.PublicKeyToAsn1(ServerKey)).ToArray()))) {
                         case -1: // 與官方伺服器連接失敗
                             ConsoleWriteTime();
                             Console.WriteLine("@{0} 連接失敗.", UserName);
                             PW = kick(config.Settings["connectionFailText"].Value);
                             break;
                         case 0: // 官方伺服器沒有玩家的登入進程記錄 (不是傳回 YES)
                             ConsoleWriteTime();
                             Console.WriteLine("@{0} 得出結果不是正版.", UserName);
                             PW = kick(config.Settings["verifyFailText"].Value);
                             break;
                         case 1: // 官方伺服器有玩家的登入進程記錄 (傳回 YES)
                             ConsoleWriteTime();
                             Console.WriteLine("@{0} 得出結果是正版.", UserName);
                             ConsoleWriteTime();
                             Console.WriteLine("@{0} 傳送驗證碼...", UserName);
                             // 傳送驗證碼到指定的網頁
                             string randomcode = getRandomCapcha();
                             if(sendCode(config.Settings["successWebPage"].Value, UserName, randomcode, remoteIP)) {
                                 // 傳送成功, 把傳送出去的驗證碼也傳給客戶端
                                 ConsoleWriteTime();
                                 Console.WriteLine("@{0} 驗證碼已傳送.", UserName);
                                 PW = kick(String.Format(config.Settings["successText"].Value, randomcode));
                             } else {
                                 // 傳送失敗
                                 ConsoleWriteTime();
                                 Console.WriteLine("@{0} 連接失敗.", UserName);
                                 PW = kick(config.Settings["connectionFailText"].Value);
                             }
                             break;
                     }
                 }
                 isDisconnect = true;
                 break;
             case 0xFF: // 取消: 0xFF
                 ConsoleWriteTime();
                 Console.WriteLine("@{0} 客戶端取消連接.", UserName);
                 isDisconnect = true;
                 break;
             default: // 其他
                 ConsoleWriteTime();
                 Console.WriteLine("@{1} 接收到無法辨識的請求, 以下是其內容:\n{0}", Utils.HexDump(Buffer), UserName);
                 break;
         }
         Stream US = PW.UnderlyingStream;
         // 如果有傳回的資料, 把它封裝輸出成二進制
         if(US.Length > 0) {
             US.Position = 0;
             _output = new byte[US.Length];
             US.Read(_output, 0, (int)US.Length);
             haveToDisconnect = isDisconnect;
             return 2;
         } else if(isDisconnect) return 0;
         else return 1;
     } catch(Exception ex) {
         Console.WriteLine("@{1} 發生錯誤!\n{0}", ex.ToString(), UserName);
     }
     return 0;
 }