/// <summary> /// Обрабатывает новое сообщение, полученное по TCP чата /// </summary> unsafe void OnChatReceived(TcpPortHandler handler, TcpClientNode node, byte[] buffer, int count) { // Дешифруем строку, если чат шифрованный if (_chatEncoded) { byte *bytesPtr = stackalloc byte[count]; for (int i = 0; i < count; i++) { bytesPtr[i] = buffer[i]; } ChatCrypt.GSEncodeDecode(_chatClientKey, bytesPtr, count); for (int i = 0; i < count; i++) { buffer[i] = bytesPtr[i]; } } var str = buffer.ToUtf8(count); LogTrace(">>>>> " + str); // var lines = str.Split(_chatSplitChars, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < lines.Length; i++) { HandleChatLine(handler, node, lines[i]); } }
unsafe void SendToClientChat(TcpClientNode node, string message) { LogTrace("<<<<<<<<<<< " + message); var bytesToSend = message.ToUTF8Bytes(); // Шифруем сообщение, если соединение уже с режиме шифрование if (_chatEncoded) fixed(byte *bytesToSendPtr = bytesToSend) ChatCrypt.GSEncodeDecode(_chatServerKey, bytesToSendPtr, bytesToSend.Length); _chat.Send(node, bytesToSend); }
/// <summary> /// Обрабатывает команду перехода в шифрованный чат /// </summary> unsafe void HandleCryptCommand(TcpPortHandler handler, TcpClientNode node, string[] values) { _chatEncoded = true; _gameGSkeyBytes = null; // В зависимости от игры. Байты шифрования отличаются. Таблица есть в инете // https://gamerecon.net/support/topic/gamespy-supported-games-list/ // https://github.com/luisj135/nintendo_dwc_emulator/blob/master/gslist.cfg // Dawn of War if (values.Contains("dow")) { _gameGSkey = "dow"; _gameGSNameBytes = "dow".ToAsciiBytes(); _gameGSkeyBytes = "VLxgwe".ToAsciiBytes(); } // Dawn of War (более похоже на правду) if (values.Contains("whammer40000")) { _gameGSkey = "whammer40000"; _gameGSNameBytes = "whammer40000".ToAsciiBytes(); _gameGSkeyBytes = "uJ8d3N".ToAsciiBytes(); } // Winter Assault // ключа почему-то нет в таблице. Возьмем ключ 1 дова. // TODO: Надо потестить WA if (values.Contains("dowwad")) { _gameGSkey = "dowwad"; _gameGSNameBytes = "dowwad".ToAsciiBytes(); _gameGSkeyBytes = "uJ8d3N".ToAsciiBytes(); } // Dark Crusade if (values.Contains("whammer40kdc")) { _gameGSkey = "whammer40kdc"; _gameGSNameBytes = "whammer40kdc".ToAsciiBytes(); _gameGSkeyBytes = "Ue9v3H".ToAsciiBytes(); } // Soulstorm if (values.Contains("whamdowfr")) { _gameGSkey = "whamdowfr"; _gameGSNameBytes = "whamdowfr".ToAsciiBytes(); _gameGSkeyBytes = "pXL838".ToAsciiBytes(); } if (_gameGSkeyBytes == null) { Restart(); return; } // Ключ шифрования. Для простоты просто нули. Не тестил, что будет, если не нули. var chall = "0000000000000000".ToAsciiBytes(); var clientKey = new ChatCrypt.GDCryptKey(); var serverKey = new ChatCrypt.GDCryptKey(); // Инициализируем структуры-ключи для выполнения алгоритма спая fixed(byte *challPtr = chall) { fixed(byte *gamekeyPtr = _gameGSkeyBytes) { ChatCrypt.GSCryptKeyInit(clientKey, challPtr, gamekeyPtr, _gameGSkeyBytes.Length); ChatCrypt.GSCryptKeyInit(serverKey, challPtr, gamekeyPtr, _gameGSkeyBytes.Length); } } // Сохраняем структуры _chatClientKey = clientKey; _chatServerKey = serverKey; // Отправляем идентичные ключи для сервера и клиента. Шифрование в итоге будет полностью совпадать. // С этого момента чат шифрованный handler.SendAskii(node, ":s 705 * 0000000000000000 0000000000000000\r\n"); }
void OnServerRetrieveReceived(TcpPortHandler handler, TcpClientNode node, byte[] buffer, int count) { var str = buffer.ToASCII(count); LogTrace("RETRIEVE " + str); var endPoint = node.RemoteEndPoint; if (endPoint == null) { handler.KillClient(node); return; } string[] data = str.Split(new char[] { '\x00' }, StringSplitOptions.RemoveEmptyEntries); string validate = data[4]; string filter = null; bool isAutomatch = false; if (validate.Length > 8) { filter = validate.Substring(8); validate = validate.Substring(0, 8); } else { //Log(Category, "ROOMS REQUEST - "+ data[2]); isAutomatch = data[2].EndsWith("am"); if (!isAutomatch) { SendChatRooms(handler, node, validate); return; } } var lobbies = _emulationAdapter.GetOpenedLobbies(); try { // var currentRating = ServerContext.ChatServer.CurrentRating; /*for (int i = 0; i < lobbies.Length; i++) * { * var server = lobbies[i]; * * //server["score_"] = GetCurrentRating(server.MaxPlayers); * }*/ var fields = data[5].Split(new char[] { '\\' }, StringSplitOptions.RemoveEmptyEntries); var unencryptedBytes = ParseHelper.PackServerList(endPoint, lobbies, fields, isAutomatch); _lastLoadedLobbies.Clear(); for (int i = 0; i < lobbies.Length; i++) { var server = lobbies[i]; var address = server.HostIP ?? server.LocalIP; var port = ushort.Parse(server.HostPort ?? server.LocalPort); var channelHash = ChatCrypt.PiStagingRoomHash(address, address, port); Log($"HASHFOR {address}:{port} {channelHash}"); server.RoomHash = channelHash; _lastLoadedLobbies[channelHash] = server; } Log("SERVERS VALIDATE VALUE ~" + validate + "~"); var encryptedBytes = GSEncoding.Encode(_gameGSkeyBytes, validate.ToAsciiBytes(), unencryptedBytes, unencryptedBytes.LongLength); Log("SERVERS bytes " + encryptedBytes.Length); int autoGames = 0; int customGames = 0; // TODO вынести в отдельно for (int i = 0; i < lobbies.Length; i++) { var server = lobbies[i]; if (server.Ranked) { autoGames++; } else { customGames++; } } //CoreContext.ClientServer.SendAsServerMessage( // "Received game list: " + customGames + " - custom; " + autoGames + // " - auto; Mod: "+ CoreContext.ThunderHawkModManager.CurrentModName); handler.Send(node, encryptedBytes); } finally { handler.KillClient(node); } }