mirror of
https://github.com/wiz0u/WTelegramClient.git
synced 2025-12-06 06:52:01 +01:00
Switch to MTProto Intermediate transport protocol (more lightweight/adequate for TCP)
This commit is contained in:
parent
a403a462db
commit
661b5223ac
|
|
@ -15,6 +15,9 @@ using System.Threading.Tasks;
|
||||||
using TL;
|
using TL;
|
||||||
using static WTelegram.Encryption;
|
using static WTelegram.Encryption;
|
||||||
|
|
||||||
|
// necessary for .NET Standard 2.0 compilation:
|
||||||
|
#pragma warning disable CA1835 // Prefer the 'Memory'-based overloads for 'ReadAsync' and 'WriteAsync'
|
||||||
|
|
||||||
namespace WTelegram
|
namespace WTelegram
|
||||||
{
|
{
|
||||||
public sealed partial class Client : IDisposable
|
public sealed partial class Client : IDisposable
|
||||||
|
|
@ -27,9 +30,9 @@ namespace WTelegram
|
||||||
private readonly int _apiId;
|
private readonly int _apiId;
|
||||||
private readonly string _apiHash;
|
private readonly string _apiHash;
|
||||||
private readonly Session _session;
|
private readonly Session _session;
|
||||||
|
private static readonly byte[] IntermediateHeader = new byte[4] { 0xee, 0xee, 0xee, 0xee };
|
||||||
private TcpClient _tcpClient;
|
private TcpClient _tcpClient;
|
||||||
private NetworkStream _networkStream;
|
private NetworkStream _networkStream;
|
||||||
private int _frame_seqTx = 0, _frame_seqRx = 0;
|
|
||||||
private ITLFunction _lastSentMsg;
|
private ITLFunction _lastSentMsg;
|
||||||
private long _lastRecvMsgId;
|
private long _lastRecvMsgId;
|
||||||
private readonly List<long> _msgsToAck = new();
|
private readonly List<long> _msgsToAck = new();
|
||||||
|
|
@ -119,7 +122,7 @@ namespace WTelegram
|
||||||
_tcpClient = new TcpClient(endpoint.AddressFamily);
|
_tcpClient = new TcpClient(endpoint.AddressFamily);
|
||||||
await _tcpClient.ConnectAsync(endpoint.Address, endpoint.Port);
|
await _tcpClient.ConnectAsync(endpoint.Address, endpoint.Port);
|
||||||
_networkStream = _tcpClient.GetStream();
|
_networkStream = _tcpClient.GetStream();
|
||||||
_frame_seqTx = _frame_seqRx = 0;
|
await _networkStream.WriteAsync(IntermediateHeader, 0, 4);
|
||||||
_cts = new();
|
_cts = new();
|
||||||
_reactorTask = Reactor(_networkStream, _cts);
|
_reactorTask = Reactor(_networkStream, _cts);
|
||||||
_sendSemaphore.Release();
|
_sendSemaphore.Release();
|
||||||
|
|
@ -316,33 +319,20 @@ namespace WTelegram
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private async Task<byte[]> RecvFrameAsync(NetworkStream stream, CancellationToken ct)
|
private static async Task<byte[]> RecvFrameAsync(NetworkStream stream, CancellationToken ct)
|
||||||
{
|
{
|
||||||
byte[] frame = new byte[8];
|
byte[] overhead = new byte[4];
|
||||||
if (await FullReadAsync(stream, frame, 8, ct) != 8)
|
if (await FullReadAsync(stream, overhead, 4, ct) != 4)
|
||||||
throw new ApplicationException("Could not read frame prefix : Connection shut down");
|
throw new ApplicationException("Could not read payload length : Connection shut down");
|
||||||
int length = BinaryPrimitives.ReadInt32LittleEndian(frame) - 12;
|
int length = BinaryPrimitives.ReadInt32LittleEndian(overhead);
|
||||||
if (length <= 0 || length >= 0x10000)
|
if (length <= 0 || length >= 0x10000)
|
||||||
throw new ApplicationException("Invalid frame_len");
|
throw new ApplicationException("Invalid frame_len");
|
||||||
int seqno = BinaryPrimitives.ReadInt32LittleEndian(frame.AsSpan(4));
|
|
||||||
if (seqno != _frame_seqRx++)
|
|
||||||
{
|
|
||||||
Trace.TraceWarning($"Unexpected frame_seq received: {seqno} instead of {_frame_seqRx}");
|
|
||||||
_frame_seqRx = seqno + 1;
|
|
||||||
}
|
|
||||||
var payload = new byte[length];
|
var payload = new byte[length];
|
||||||
if (await FullReadAsync(stream, payload, length, ct) != length)
|
if (await FullReadAsync(stream, payload, length, ct) != length)
|
||||||
throw new ApplicationException("Could not read frame data : Connection shut down");
|
throw new ApplicationException("Could not read frame data : Connection shut down");
|
||||||
uint crc32 = Compat.UpdateCrc32(0, frame, 0, 8);
|
|
||||||
crc32 = Compat.UpdateCrc32(crc32, payload, 0, payload.Length);
|
|
||||||
if (await FullReadAsync(stream, frame, 4, ct) != 4)
|
|
||||||
throw new ApplicationException("Could not read frame CRC : Connection shut down");
|
|
||||||
if (crc32 != BinaryPrimitives.ReadUInt32LittleEndian(frame))
|
|
||||||
throw new ApplicationException("Invalid envelope CRC32");
|
|
||||||
return payload;
|
return payload;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma warning disable CA1835 // necessary for .NET Standard 2.0 compilation
|
|
||||||
private static async Task<int> FullReadAsync(Stream stream, byte[] buffer, int length, CancellationToken ct = default)
|
private static async Task<int> FullReadAsync(Stream stream, byte[] buffer, int length, CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
for (int offset = 0; offset != length;)
|
for (int offset = 0; offset != length;)
|
||||||
|
|
@ -369,8 +359,7 @@ namespace WTelegram
|
||||||
{
|
{
|
||||||
using var memStream = new MemoryStream(1024);
|
using var memStream = new MemoryStream(1024);
|
||||||
using var writer = new BinaryWriter(memStream, Encoding.UTF8);
|
using var writer = new BinaryWriter(memStream, Encoding.UTF8);
|
||||||
writer.Write(0); // int32 frame_len (to be patched with full frame length)
|
writer.Write(0); // int32 payload_len (to be patched with payload length)
|
||||||
writer.Write(_frame_seqTx++); // int32 frame_seq
|
|
||||||
|
|
||||||
if (_session.AuthKeyID == 0) // send unencrypted message
|
if (_session.AuthKeyID == 0) // send unencrypted message
|
||||||
{
|
{
|
||||||
|
|
@ -425,12 +414,10 @@ namespace WTelegram
|
||||||
}
|
}
|
||||||
var buffer = memStream.GetBuffer();
|
var buffer = memStream.GetBuffer();
|
||||||
int frameLength = (int)memStream.Length;
|
int frameLength = (int)memStream.Length;
|
||||||
BinaryPrimitives.WriteInt32LittleEndian(buffer, frameLength + 4); // patch frame_len with correct value
|
BinaryPrimitives.WriteInt32LittleEndian(buffer, frameLength - 4); // patch payload_len with correct value
|
||||||
uint crc = Compat.UpdateCrc32(0, buffer, 0, frameLength);
|
|
||||||
writer.Write(crc); // int32 frame_crc
|
|
||||||
//TODO: support Transport obfuscation?
|
//TODO: support Transport obfuscation?
|
||||||
|
|
||||||
await _networkStream.WriteAsync(memStream.GetBuffer(), 0, frameLength + 4);
|
await _networkStream.WriteAsync(memStream.GetBuffer(), 0, frameLength);
|
||||||
_lastSentMsg = func;
|
_lastSentMsg = func;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
|
@ -439,7 +426,6 @@ namespace WTelegram
|
||||||
}
|
}
|
||||||
return msgId;
|
return msgId;
|
||||||
}
|
}
|
||||||
#pragma warning restore CA1835
|
|
||||||
|
|
||||||
private static ITLFunction MakeFunction(ITLObject msg)
|
private static ITLFunction MakeFunction(ITLObject msg)
|
||||||
=> writer =>
|
=> writer =>
|
||||||
|
|
|
||||||
|
|
@ -9,12 +9,6 @@ namespace WTelegram
|
||||||
{
|
{
|
||||||
static class Compat
|
static class Compat
|
||||||
{
|
{
|
||||||
// see also https://github.com/dotnet/runtime/issues/2036 and https://github.com/dotnet/runtime/pull/53623
|
|
||||||
internal static readonly Func<uint, byte[], int, int, uint> UpdateCrc32 = (Func<uint, byte[], int, int, uint>)
|
|
||||||
typeof(System.IO.Compression.ZipArchive).Assembly.GetType("System.IO.Compression.Crc32Helper")
|
|
||||||
.GetMethod("UpdateCrc32", new[] { typeof(uint), typeof(byte[]), typeof(int), typeof(int) })
|
|
||||||
.CreateDelegate(typeof(Func<uint, byte[], int, int, uint>));
|
|
||||||
|
|
||||||
#if NETCOREAPP2_1_OR_GREATER
|
#if NETCOREAPP2_1_OR_GREATER
|
||||||
internal static IPEndPoint IPEndPoint_Parse(string addr) => IPEndPoint.Parse(addr);
|
internal static IPEndPoint IPEndPoint_Parse(string addr) => IPEndPoint.Parse(addr);
|
||||||
internal static BigInteger BigEndianInteger(byte[] value) => new(value, true, true);
|
internal static BigInteger BigEndianInteger(byte[] value) => new(value, true, true);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue