initial commit

This commit is contained in:
i2p
2026-08-27 11:22:54 -06:00
commit 3d81b11e14
2281 changed files with 54227 additions and 0 deletions
@@ -0,0 +1,32 @@
namespace Crysome.Common.Network.FileTransfer;
internal static class Crc32
{
private static readonly uint[] _table = Build();
private static uint[] Build()
{
uint[] array = new uint[256];
for (uint num = 0u; num < 256; num++)
{
uint num2 = num;
for (int i = 0; i < 8; i++)
{
num2 = (((num2 & 1) != 0) ? (0xEDB88320u ^ (num2 >> 1)) : (num2 >> 1));
}
array[num] = num2;
}
return array;
}
internal static uint Compute(byte[] data, int offset, int len)
{
uint num = uint.MaxValue;
int num2 = offset + len;
for (int i = offset; i < num2; i++)
{
num = (num >> 8) ^ _table[(num ^ data[i]) & 0xFF];
}
return num ^ 0xFFFFFFFFu;
}
}
@@ -0,0 +1,23 @@
using System.IO;
using Crysome.Common.Network.Packets;
namespace Crysome.Common.Network.FileTransfer;
public sealed class FtAbortPacket : IPacket
{
public uint TransferId;
public string Reason;
public void Write(BinaryWriter w)
{
w.Write(TransferId);
w.Write(Reason ?? "");
}
public void Read(BinaryReader r)
{
TransferId = r.ReadUInt32();
Reason = r.ReadString();
}
}
@@ -0,0 +1,38 @@
using System;
using System.IO;
using Crysome.Common.Network.Packets;
namespace Crysome.Common.Network.FileTransfer;
public sealed class FtChunkPacket : IPacket
{
public uint TransferId;
public int ChunkIdx;
public uint Crc32;
public byte[] Data;
public void Write(BinaryWriter w)
{
w.Write(TransferId);
w.Write(ChunkIdx);
w.Write(Crc32);
int num = ((Data != null) ? Data.Length : 0);
w.Write(num);
if (num > 0)
{
w.Write(Data);
}
}
public void Read(BinaryReader r)
{
TransferId = r.ReadUInt32();
ChunkIdx = r.ReadInt32();
Crc32 = r.ReadUInt32();
int num = r.ReadInt32();
Data = ((num > 0) ? r.ReadBytes(num) : Array.Empty<byte>());
}
}
@@ -0,0 +1,27 @@
using System.IO;
using Crysome.Common.Network.Packets;
namespace Crysome.Common.Network.FileTransfer;
public sealed class FtDonePacket : IPacket
{
public uint TransferId;
public bool Success;
public string Message;
public void Write(BinaryWriter w)
{
w.Write(TransferId);
w.Write(Success);
w.Write(Message ?? "");
}
public void Read(BinaryReader r)
{
TransferId = r.ReadUInt32();
Success = r.ReadBoolean();
Message = r.ReadString();
}
}
@@ -0,0 +1,162 @@
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Threading;
namespace Crysome.Common.Network.FileTransfer;
public sealed class FtReceiver
{
private const int SackIntervalMs = 200;
private readonly CrysomeClient _client;
private uint _id;
private string _fileName;
private int _totalChunks;
private long _totalBytes;
private byte[] _expectedSha256;
private byte[][] _chunks;
private bool[] _received;
private int _receivedCount;
private int _highestContiguous = -1;
private int _lastSackTick;
private bool _finalised;
public Action<string, byte[]> OnComplete;
public Action<string> OnError;
public FtReceiver(CrysomeClient client)
{
_client = client;
}
public void HandleStart(FtStartPacket p)
{
_id = p.TransferId;
_fileName = p.FileName;
_totalChunks = Math.Max(1, p.TotalChunks);
_totalBytes = p.TotalBytes;
_expectedSha256 = p.Sha256;
_chunks = new byte[_totalChunks][];
_received = new bool[_totalChunks];
_receivedCount = 0;
_highestContiguous = -1;
_lastSackTick = Environment.TickCount - 400;
_finalised = false;
}
public void HandleChunk(FtChunkPacket p)
{
if (_chunks == null || p.TransferId != _id || _finalised)
{
return;
}
int chunkIdx = p.ChunkIdx;
if (chunkIdx >= 0 && chunkIdx < _totalChunks && !_received[chunkIdx] && p.Data != null && Crc32.Compute(p.Data, 0, p.Data.Length) == p.Crc32)
{
_chunks[chunkIdx] = p.Data;
_received[chunkIdx] = true;
Interlocked.Increment(ref _receivedCount);
while (_highestContiguous + 1 < _totalChunks && _received[_highestContiguous + 1])
{
_highestContiguous++;
}
int tickCount = Environment.TickCount;
bool num = _receivedCount == _totalChunks;
if (num || tickCount - _lastSackTick >= 200)
{
_lastSackTick = tickCount;
SendSack();
}
if (num)
{
Finalise();
}
}
}
public void HandleAbort(FtAbortPacket p)
{
if (p.TransferId == _id)
{
OnError?.Invoke("Transfer aborted by sender: " + p.Reason);
}
}
private void SendSack()
{
List<int> list = new List<int>();
int num = _highestContiguous + 1;
int num2 = Math.Min(_totalChunks, num + 64);
for (int i = num; i < num2; i++)
{
if (!_received[i])
{
list.Add(i);
}
}
_client.SendPacket(new FtSackPacket
{
TransferId = _id,
HighestContiguous = _highestContiguous,
NackList = list.ToArray()
});
}
private void Finalise()
{
if (_finalised)
{
return;
}
_finalised = true;
byte[] array = new byte[_totalBytes];
long num = 0L;
for (int i = 0; i < _totalChunks; i++)
{
byte[] array2 = _chunks[i];
Buffer.BlockCopy(array2, 0, array, (int)num, array2.Length);
num += array2.Length;
}
byte[] array3;
using (SHA256 sHA = SHA256.Create())
{
array3 = sHA.ComputeHash(array);
}
bool flag = true;
for (int j = 0; j < 32; j++)
{
if (array3[j] != _expectedSha256[j])
{
flag = false;
break;
}
}
_client.SendPacket(new FtDonePacket
{
TransferId = _id,
Success = flag,
Message = (flag ? "OK" : "HASH_MISMATCH")
});
if (flag)
{
OnComplete?.Invoke(_fileName, array);
}
else
{
OnError?.Invoke("SHA-256 mismatch — file rejected");
}
}
}
@@ -0,0 +1,38 @@
using System;
using System.IO;
using Crysome.Common.Network.Packets;
namespace Crysome.Common.Network.FileTransfer;
public sealed class FtSackPacket : IPacket
{
public uint TransferId;
public int HighestContiguous;
public int[] NackList;
public void Write(BinaryWriter w)
{
w.Write(TransferId);
w.Write(HighestContiguous);
short num = (short)((NackList != null) ? Math.Min(NackList.Length, 128) : 0);
w.Write(num);
for (int i = 0; i < num; i++)
{
w.Write(NackList[i]);
}
}
public void Read(BinaryReader r)
{
TransferId = r.ReadUInt32();
HighestContiguous = r.ReadInt32();
int num = r.ReadInt16();
NackList = new int[num];
for (int i = 0; i < num; i++)
{
NackList[i] = r.ReadInt32();
}
}
}
@@ -0,0 +1,172 @@
using System;
using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
namespace Crysome.Common.Network.FileTransfer;
public sealed class FtSender
{
public const int ChunkSize = 1200;
private const int AbortTimeoutMs = 600000;
private const int SackWaitMs = 500;
private const int ChunkPaceMs = 2;
private readonly CrysomeClient _client;
private readonly byte[] _data;
private readonly string _fileName;
private readonly byte[] _sha256;
private volatile bool _done;
private volatile int _ackedChunks;
public Action<uint, bool, string> OnDone;
private static int _nextId;
public uint TransferId { get; }
public int TotalChunks => Math.Max(1, (_data.Length + 1200 - 1) / 1200);
public FtSender(CrysomeClient client, string fileName, byte[] data)
{
_client = client;
_data = data;
_fileName = fileName;
TransferId = (uint)Interlocked.Increment(ref _nextId);
using SHA256 sHA = SHA256.Create();
_sha256 = sHA.ComputeHash(data);
}
public void Start()
{
Task.Run((Action)Run);
}
public void HandleDone(FtDonePacket p)
{
if (p.TransferId == TransferId)
{
_done = true;
OnDone?.Invoke(TransferId, p.Success, p.Message);
}
}
public void HandleSack(FtSackPacket p)
{
if (p.TransferId != TransferId || _done)
{
return;
}
if (p.HighestContiguous >= 0)
{
_ackedChunks = Math.Max(_ackedChunks, p.HighestContiguous + 1);
}
if (p.NackList == null)
{
return;
}
int[] nackList = p.NackList;
foreach (int num in nackList)
{
if (num >= 0 && num < TotalChunks)
{
SendChunk(num);
}
}
}
private void Run()
{
try
{
int totalChunks = TotalChunks;
_client.SendPacket(new FtStartPacket
{
TransferId = TransferId,
FileName = _fileName,
TotalChunks = totalChunks,
ChunkSize = 1200,
TotalBytes = _data.Length,
Sha256 = _sha256
});
Thread.Sleep(50);
DateTime utcNow = DateTime.UtcNow;
int num = 0;
DateTime utcNow2 = DateTime.UtcNow;
for (int i = 0; i < totalChunks; i++)
{
if (_done)
{
break;
}
SendChunk(i);
if (i % 32 == 31)
{
Thread.Sleep(2);
}
if ((DateTime.UtcNow - utcNow).TotalMilliseconds > 600000.0)
{
_client.SendPacket(new FtAbortPacket
{
TransferId = TransferId,
Reason = "Sender timeout"
});
OnDone?.Invoke(TransferId, arg2: false, "ERR: Send timeout");
return;
}
}
utcNow2 = DateTime.UtcNow;
num = _ackedChunks;
while (!_done)
{
Thread.Sleep(500);
int ackedChunks = _ackedChunks;
if (ackedChunks > num)
{
num = ackedChunks;
utcNow2 = DateTime.UtcNow;
}
double totalMilliseconds = (DateTime.UtcNow - utcNow2).TotalMilliseconds;
double totalMilliseconds2 = (DateTime.UtcNow - utcNow).TotalMilliseconds;
if (totalMilliseconds > 60000.0 || totalMilliseconds2 > 600000.0)
{
_client.SendPacket(new FtAbortPacket
{
TransferId = TransferId,
Reason = "Done-ack timeout"
});
OnDone?.Invoke(TransferId, arg2: false, "ERR: No confirmation from client");
break;
}
}
}
catch (Exception ex)
{
_done = true;
OnDone?.Invoke(TransferId, arg2: false, "ERR: " + ex.Message);
}
}
private void SendChunk(int idx)
{
int num = idx * 1200;
int num2 = Math.Min(1200, _data.Length - num);
byte[] array = new byte[num2];
Buffer.BlockCopy(_data, num, array, 0, num2);
_client.SendPacket(new FtChunkPacket
{
TransferId = TransferId,
ChunkIdx = idx,
Crc32 = Crc32.Compute(_data, num, num2),
Data = array
});
}
}
@@ -0,0 +1,39 @@
using System.IO;
using Crysome.Common.Network.Packets;
namespace Crysome.Common.Network.FileTransfer;
public sealed class FtStartPacket : IPacket
{
public uint TransferId;
public string FileName;
public int TotalChunks;
public int ChunkSize;
public long TotalBytes;
public byte[] Sha256;
public void Write(BinaryWriter w)
{
w.Write(TransferId);
w.Write(FileName ?? "");
w.Write(TotalChunks);
w.Write(ChunkSize);
w.Write(TotalBytes);
w.Write(Sha256 ?? new byte[32]);
}
public void Read(BinaryReader r)
{
TransferId = r.ReadUInt32();
FileName = r.ReadString();
TotalChunks = r.ReadInt32();
ChunkSize = r.ReadInt32();
TotalBytes = r.ReadInt64();
Sha256 = r.ReadBytes(32);
}
}