2015-09-28 04:01:17 +02:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.IO;
|
|
|
|
|
|
using Ionic.Crc;
|
|
|
|
|
|
|
|
|
|
|
|
namespace TLSharp.Core.Network
|
|
|
|
|
|
{
|
2016-04-18 12:50:57 +02:00
|
|
|
|
public class TcpMessage
|
|
|
|
|
|
{
|
|
|
|
|
|
public int SequneceNumber { get; private set; }
|
|
|
|
|
|
public byte[] Body { get; private set; }
|
2015-09-28 04:01:17 +02:00
|
|
|
|
|
2016-04-18 12:50:57 +02:00
|
|
|
|
public TcpMessage(int seqNumber, byte[] body)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (body == null)
|
|
|
|
|
|
throw new ArgumentNullException(nameof(body));
|
2015-09-28 04:01:17 +02:00
|
|
|
|
|
2016-04-18 12:50:57 +02:00
|
|
|
|
SequneceNumber = seqNumber;
|
|
|
|
|
|
Body = body;
|
|
|
|
|
|
}
|
2015-09-28 04:01:17 +02:00
|
|
|
|
|
2016-04-18 12:50:57 +02:00
|
|
|
|
public byte[] Encode()
|
|
|
|
|
|
{
|
|
|
|
|
|
using (var memoryStream = new MemoryStream())
|
|
|
|
|
|
{
|
|
|
|
|
|
using (var binaryWriter = new BinaryWriter(memoryStream))
|
|
|
|
|
|
{
|
|
|
|
|
|
// https://core.telegram.org/mtproto#tcp-transport
|
|
|
|
|
|
/*
|
2015-09-28 04:01:17 +02:00
|
|
|
|
4 length bytes are added at the front
|
|
|
|
|
|
(to include the length, the sequence number, and CRC32; always divisible by 4)
|
|
|
|
|
|
and 4 bytes with the packet sequence number within this TCP connection
|
|
|
|
|
|
(the first packet sent is numbered 0, the next one 1, etc.),
|
|
|
|
|
|
and 4 CRC32 bytes at the end (length, sequence number, and payload together).
|
|
|
|
|
|
*/
|
2016-04-18 12:50:57 +02:00
|
|
|
|
binaryWriter.Write(Body.Length + 12);
|
|
|
|
|
|
binaryWriter.Write(SequneceNumber);
|
|
|
|
|
|
binaryWriter.Write(Body);
|
|
|
|
|
|
var crc32 = new CRC32();
|
|
|
|
|
|
crc32.SlurpBlock(memoryStream.GetBuffer(), 0, 8 + Body.Length);
|
|
|
|
|
|
binaryWriter.Write(crc32.Crc32Result);
|
2015-09-28 04:01:17 +02:00
|
|
|
|
|
2016-04-18 12:50:57 +02:00
|
|
|
|
var transportPacket = memoryStream.ToArray();
|
2015-09-28 04:01:17 +02:00
|
|
|
|
|
2016-04-18 12:50:57 +02:00
|
|
|
|
// Debug.WriteLine("Tcp packet #{0}\n{1}", SequneceNumber, BitConverter.ToString(transportPacket));
|
2015-09-28 04:01:17 +02:00
|
|
|
|
|
2016-04-18 12:50:57 +02:00
|
|
|
|
return transportPacket;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2015-09-28 04:01:17 +02:00
|
|
|
|
|
2016-04-18 12:50:57 +02:00
|
|
|
|
public static TcpMessage Decode(byte[] body)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (body == null)
|
|
|
|
|
|
throw new ArgumentNullException(nameof(body));
|
2015-09-28 04:01:17 +02:00
|
|
|
|
|
2016-04-18 12:50:57 +02:00
|
|
|
|
if (body.Length < 12)
|
|
|
|
|
|
throw new InvalidOperationException("Ops, wrong size of input packet");
|
2015-09-28 04:01:17 +02:00
|
|
|
|
|
2016-04-18 12:50:57 +02:00
|
|
|
|
using (var memoryStream = new MemoryStream(body))
|
|
|
|
|
|
{
|
|
|
|
|
|
using (var binaryReader = new BinaryReader(memoryStream))
|
|
|
|
|
|
{
|
|
|
|
|
|
var packetLength = binaryReader.ReadInt32();
|
2015-09-28 04:01:17 +02:00
|
|
|
|
|
2016-04-18 12:50:57 +02:00
|
|
|
|
if (packetLength < 12)
|
|
|
|
|
|
throw new InvalidOperationException(string.Format("invalid packet length: {0}", packetLength));
|
2015-09-28 04:01:17 +02:00
|
|
|
|
|
2016-04-18 12:50:57 +02:00
|
|
|
|
var seq = binaryReader.ReadInt32();
|
|
|
|
|
|
byte[] packet = binaryReader.ReadBytes(packetLength - 12);
|
|
|
|
|
|
var checksum = (int)binaryReader.ReadInt32();
|
2015-09-28 04:01:17 +02:00
|
|
|
|
|
2016-04-18 12:50:57 +02:00
|
|
|
|
var crc32 = new CRC32();
|
|
|
|
|
|
crc32.SlurpBlock(body, 0, packetLength - 4);
|
|
|
|
|
|
var validChecksum = crc32.Crc32Result;
|
2015-09-28 04:01:17 +02:00
|
|
|
|
|
2016-04-18 12:50:57 +02:00
|
|
|
|
if (checksum != validChecksum)
|
|
|
|
|
|
{
|
|
|
|
|
|
throw new InvalidOperationException("invalid checksum! skip");
|
|
|
|
|
|
}
|
2015-09-28 04:01:17 +02:00
|
|
|
|
|
2016-04-18 12:50:57 +02:00
|
|
|
|
return new TcpMessage(seq, packet);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2015-09-28 04:01:17 +02:00
|
|
|
|
}
|