414 lines
16 KiB
C#
414 lines
16 KiB
C#
using System;
|
|
using System.Collections.Concurrent;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Net;
|
|
using System.Net.Sockets;
|
|
using System.Security.Cryptography;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace VideoReader
|
|
{
|
|
internal class InOutSocket
|
|
{
|
|
public int bitrateout = 0;
|
|
public int bitratein = 0;
|
|
|
|
private static byte[] keyByte = MD5("73!2#qweaSdzxc4r");
|
|
private static byte[] ivByte = MD5("0_=op[l:',./vf73");
|
|
|
|
private bool open;
|
|
private ServerConfig config;
|
|
private ConcurrentQueue<byte[]> outbuffer = new ConcurrentQueue<byte[]>();
|
|
private BinaryWriter bw;
|
|
private ConcurrentQueue<byte[]> output = new ConcurrentQueue<byte[]>();
|
|
private bool noblock = true;
|
|
|
|
// Новые поля для детального логгирования
|
|
private TcpClient tcpClient;
|
|
private NetworkStream networkStream;
|
|
private Thread receiveThread;
|
|
private Thread sendThread;
|
|
private bool isRunning = false;
|
|
private long packetCounter = 0;
|
|
private DateTime connectionStartTime;
|
|
|
|
public bool opened
|
|
{
|
|
get { return open; }
|
|
set { open = value; }
|
|
}
|
|
|
|
public InOutSocket()
|
|
{
|
|
config = ServerConfig.LoadConfig();
|
|
DetailedLogger.LogConnection($"InOutSocket initialized with server: {config.ServerAddress}:{config.Port}");
|
|
DetailedLogger.LogDebug($"Encryption keys initialized - Key: {BitConverter.ToString(keyByte)}, IV: {BitConverter.ToString(ivByte)}");
|
|
}
|
|
|
|
private static byte[] MD5(string input)
|
|
{
|
|
using (var md5 = System.Security.Cryptography.MD5.Create())
|
|
{
|
|
return md5.ComputeHash(Encoding.UTF8.GetBytes(input));
|
|
}
|
|
}
|
|
|
|
public void Connect()
|
|
{
|
|
try
|
|
{
|
|
connectionStartTime = DateTime.Now;
|
|
DetailedLogger.LogConnection($"Starting connection to {config.ServerAddress}:{config.Port}...");
|
|
|
|
// DNS Resolution
|
|
DetailedLogger.LogDebug($"Resolving DNS for {config.ServerAddress}...");
|
|
var addresses = Dns.GetHostAddresses(config.ServerAddress);
|
|
DetailedLogger.LogDebug($"DNS resolved to {addresses.Length} addresses: {string.Join(", ", addresses.Select(a => a.ToString()))}");
|
|
|
|
// TCP Connection
|
|
tcpClient = new TcpClient();
|
|
tcpClient.ReceiveTimeout = 30000; // 30 seconds
|
|
tcpClient.SendTimeout = 30000;
|
|
|
|
var connectTask = tcpClient.ConnectAsync(config.ServerAddress, config.Port);
|
|
var sw = Stopwatch.StartNew();
|
|
|
|
DetailedLogger.LogConnection($"Attempting TCP connection to {config.ServerAddress}:{config.Port}...");
|
|
connectTask.Wait(10000); // 10 second timeout
|
|
|
|
if (!tcpClient.Connected)
|
|
{
|
|
throw new Exception($"Failed to connect within timeout");
|
|
}
|
|
|
|
sw.Stop();
|
|
DetailedLogger.LogConnection($"TCP connection established in {sw.ElapsedMilliseconds}ms");
|
|
|
|
networkStream = tcpClient.GetStream();
|
|
DetailedLogger.LogDebug($"Network stream obtained, available: {networkStream.DataAvailable}");
|
|
|
|
// Send authorization bytes (as per original protocol)
|
|
DetailedLogger.LogDebug("Sending authorization bytes...");
|
|
networkStream.WriteByte(0); // Receiver type
|
|
networkStream.WriteByte((byte)config.Channel); // Channel number
|
|
networkStream.Flush();
|
|
DetailedLogger.LogConnection($"Authorization sent: ReceiverType=0, Channel={config.Channel}");
|
|
|
|
// Start monitoring threads
|
|
isRunning = true;
|
|
receiveThread = new Thread(ReceiveLoop) { IsBackground = true, Name = "ReceiveThread" };
|
|
sendThread = new Thread(SendLoop) { IsBackground = true, Name = "SendThread" };
|
|
|
|
receiveThread.Start();
|
|
sendThread.Start();
|
|
|
|
DetailedLogger.LogConnection("Monitoring threads started");
|
|
|
|
// Send initial handshake
|
|
SendInitialHandshake();
|
|
|
|
opened = true;
|
|
DetailedLogger.LogConnection("Connection established successfully");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
DetailedLogger.LogError($"Connection failed: {ex.Message}", ex);
|
|
opened = false;
|
|
Cleanup();
|
|
throw;
|
|
}
|
|
}
|
|
|
|
public void Disconnect()
|
|
{
|
|
DetailedLogger.LogConnection("Starting disconnect procedure...");
|
|
|
|
isRunning = false;
|
|
opened = false;
|
|
|
|
try
|
|
{
|
|
// Send disconnect packet if possible
|
|
if (networkStream != null && tcpClient != null && tcpClient.Connected)
|
|
{
|
|
var disconnectPacket = Encoding.UTF8.GetBytes("DISCONNECT");
|
|
SendPacket(disconnectPacket, "Disconnect command");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
DetailedLogger.LogError($"Error sending disconnect packet: {ex.Message}");
|
|
}
|
|
|
|
Cleanup();
|
|
|
|
var totalTime = DateTime.Now - connectionStartTime;
|
|
DetailedLogger.LogConnection($"Disconnected from server (session duration: {totalTime.TotalSeconds:F1}s, packets processed: {packetCounter})");
|
|
}
|
|
|
|
private void Cleanup()
|
|
{
|
|
try
|
|
{
|
|
networkStream?.Close();
|
|
networkStream?.Dispose();
|
|
tcpClient?.Close();
|
|
tcpClient?.Dispose();
|
|
|
|
// Wait for threads to finish
|
|
receiveThread?.Join(2000);
|
|
sendThread?.Join(2000);
|
|
|
|
DetailedLogger.LogDebug("Connection cleanup completed");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
DetailedLogger.LogError($"Error during cleanup: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
private void SendInitialHandshake()
|
|
{
|
|
try
|
|
{
|
|
DetailedLogger.LogConnection("Sending initial handshake...");
|
|
|
|
// Создаем пакет авторизации
|
|
var handshakeData = new byte[64];
|
|
var channelBytes = BitConverter.GetBytes(config.Channel);
|
|
var serverTypeBytes = Encoding.UTF8.GetBytes(config.ServerType.PadRight(16).Substring(0, 16));
|
|
|
|
Array.Copy(channelBytes, 0, handshakeData, 0, 4);
|
|
Array.Copy(serverTypeBytes, 0, handshakeData, 4, 16);
|
|
|
|
// Добавляем timestamp
|
|
var timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
|
var timestampBytes = BitConverter.GetBytes(timestamp);
|
|
Array.Copy(timestampBytes, 0, handshakeData, 20, 8);
|
|
|
|
SendPacket(handshakeData, "Initial handshake");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
DetailedLogger.LogError($"Failed to send handshake: {ex.Message}", ex);
|
|
}
|
|
}
|
|
|
|
private void SendPacket(byte[] data, string description)
|
|
{
|
|
try
|
|
{
|
|
var encryptedData = EncryptData(data);
|
|
DetailedLogger.LogPacket("OUTGOING", encryptedData, description);
|
|
|
|
networkStream.Write(encryptedData, 0, encryptedData.Length);
|
|
networkStream.Flush();
|
|
|
|
Interlocked.Add(ref bitrateout, encryptedData.Length);
|
|
Interlocked.Increment(ref packetCounter);
|
|
|
|
DetailedLogger.LogDebug($"Packet sent successfully ({encryptedData.Length} bytes)");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
DetailedLogger.LogError($"Failed to send packet: {ex.Message}", ex);
|
|
}
|
|
}
|
|
|
|
private void ReceiveLoop()
|
|
{
|
|
DetailedLogger.LogDebug("Receive thread started");
|
|
var buffer = new byte[8192];
|
|
|
|
while (isRunning && tcpClient?.Connected == true)
|
|
{
|
|
try
|
|
{
|
|
if (networkStream.DataAvailable)
|
|
{
|
|
var bytesRead = networkStream.Read(buffer, 0, buffer.Length);
|
|
if (bytesRead > 0)
|
|
{
|
|
var receivedData = new byte[bytesRead];
|
|
Array.Copy(buffer, receivedData, bytesRead);
|
|
|
|
ProcessReceivedPacket(receivedData);
|
|
Interlocked.Add(ref bitratein, bytesRead);
|
|
Interlocked.Increment(ref packetCounter);
|
|
}
|
|
}
|
|
|
|
Thread.Sleep(10); // Небольшая задержка чтобы не грузить CPU
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
if (isRunning) // Только логгируем если не идет остановка
|
|
{
|
|
DetailedLogger.LogError($"Error in receive loop: {ex.Message}");
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
DetailedLogger.LogDebug("Receive thread stopped");
|
|
}
|
|
|
|
private void SendLoop()
|
|
{
|
|
DetailedLogger.LogDebug("Send thread started");
|
|
|
|
while (isRunning && tcpClient?.Connected == true)
|
|
{
|
|
try
|
|
{
|
|
if (outbuffer.TryDequeue(out byte[] dataToSend))
|
|
{
|
|
SendPacket(dataToSend, "Queued data");
|
|
}
|
|
|
|
Thread.Sleep(10);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
if (isRunning)
|
|
{
|
|
DetailedLogger.LogError($"Error in send loop: {ex.Message}");
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
DetailedLogger.LogDebug("Send thread stopped");
|
|
}
|
|
|
|
private void ProcessReceivedPacket(byte[] rawData)
|
|
{
|
|
try
|
|
{
|
|
DetailedLogger.LogPacket("INCOMING", rawData, "Raw received data");
|
|
|
|
// Пытаемся расшифровать данные
|
|
var decryptedData = DecryptData(rawData);
|
|
if (decryptedData != null)
|
|
{
|
|
DetailedLogger.LogPacket("DECRYPTED", decryptedData, "Decrypted data");
|
|
AnalyzePacketContent(decryptedData);
|
|
}
|
|
else
|
|
{
|
|
DetailedLogger.LogDebug("Failed to decrypt packet, analyzing as plain data");
|
|
AnalyzePacketContent(rawData);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
DetailedLogger.LogError($"Error processing received packet: {ex.Message}", ex);
|
|
}
|
|
}
|
|
|
|
private void AnalyzePacketContent(byte[] data)
|
|
{
|
|
try
|
|
{
|
|
var dataStr = Encoding.UTF8.GetString(data);
|
|
|
|
// Проверяем на подключение телефона
|
|
if (dataStr.Contains("PHONE") || dataStr.Contains("DEVICE") || dataStr.Contains("CONNECT"))
|
|
{
|
|
DetailedLogger.LogPhoneConnection(dataStr, "Phone connection detected in packet");
|
|
}
|
|
|
|
// Проверяем другие типы пакетов
|
|
if (dataStr.Contains("VIDEO"))
|
|
{
|
|
DetailedLogger.LogDebug("Video data packet detected");
|
|
}
|
|
else if (dataStr.Contains("AUDIO"))
|
|
{
|
|
DetailedLogger.LogDebug("Audio data packet detected");
|
|
}
|
|
else if (dataStr.Contains("HEARTBEAT") || dataStr.Contains("PING"))
|
|
{
|
|
DetailedLogger.LogDebug("Heartbeat/ping packet detected");
|
|
}
|
|
else if (dataStr.Contains("AUTH") || dataStr.Contains("LOGIN"))
|
|
{
|
|
DetailedLogger.LogConnection("Authentication packet detected");
|
|
}
|
|
|
|
// Логгируем содержимое пакета
|
|
DetailedLogger.LogDebug($"Packet content analysis: {dataStr.Substring(0, Math.Min(100, dataStr.Length))}...");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
DetailedLogger.LogDebug($"Could not analyze packet as text: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
private byte[] EncryptData(byte[] data)
|
|
{
|
|
try
|
|
{
|
|
using (var aes = Aes.Create())
|
|
{
|
|
aes.Key = keyByte;
|
|
aes.IV = ivByte;
|
|
aes.Mode = CipherMode.CBC;
|
|
aes.Padding = PaddingMode.PKCS7;
|
|
|
|
using (var encryptor = aes.CreateEncryptor())
|
|
using (var ms = new MemoryStream())
|
|
using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
|
|
{
|
|
cs.Write(data, 0, data.Length);
|
|
cs.FlushFinalBlock();
|
|
return ms.ToArray();
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
DetailedLogger.LogError($"Encryption failed: {ex.Message}");
|
|
return data; // Возвращаем оригинальные данные если шифрование не удалось
|
|
}
|
|
}
|
|
|
|
private byte[] DecryptData(byte[] data)
|
|
{
|
|
try
|
|
{
|
|
using (var aes = Aes.Create())
|
|
{
|
|
aes.Key = keyByte;
|
|
aes.IV = ivByte;
|
|
aes.Mode = CipherMode.CBC;
|
|
aes.Padding = PaddingMode.PKCS7;
|
|
|
|
using (var decryptor = aes.CreateDecryptor())
|
|
using (var ms = new MemoryStream(data))
|
|
using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
|
|
using (var result = new MemoryStream())
|
|
{
|
|
cs.CopyTo(result);
|
|
return result.ToArray();
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
DetailedLogger.LogDebug($"Decryption failed (data might not be encrypted): {ex.Message}");
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public void QueueDataForSending(byte[] data)
|
|
{
|
|
outbuffer.Enqueue(data);
|
|
DetailedLogger.LogDebug($"Data queued for sending ({data.Length} bytes)");
|
|
}
|
|
}
|
|
} |