public static TcpMessage Decode(byte[] body) { if (body == null) throw new ArgumentNullException(nameof(body)); if (body.Length < 12) throw new InvalidOperationException("Ops, wrong size of input packet"); using (var memoryStream = new MemoryStream(body)) { using (var binaryReader = new BinaryReader(memoryStream)) { var packetLength = binaryReader.ReadInt32(); if (packetLength < 12) throw new InvalidOperationException(string.Format("invalid packet length: {0}", packetLength)); var seq = binaryReader.ReadInt32(); byte[] packet = binaryReader.ReadBytes(packetLength - 12); var checksum = (int)binaryReader.ReadInt32(); var crc32 = new CRC32(); crc32.SlurpBlock(body, 0, packetLength - 4); var validChecksum = crc32.Crc32Result; if (checksum != validChecksum) { throw new InvalidOperationException("invalid checksum! skip"); } return new TcpMessage(seq, packet); } } }
public byte[] Encode() { using (var memoryStream = new MemoryStream()) { using (var binaryWriter = new BinaryWriter(memoryStream)) { // https://core.telegram.org/mtproto#tcp-transport /* 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). */ 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); var transportPacket = memoryStream.ToArray(); // Debug.WriteLine("Tcp packet #{0}\n{1}", SequneceNumber, BitConverter.ToString(transportPacket)); return transportPacket; } } }
private static async Task <TcpMessage> Receieve(TcpClient tcpClient) { var stream = tcpClient.GetStream(); var packetLengthBytes = new byte[4]; if (await stream.ReadAsync(packetLengthBytes, 0, 4) != 4) { throw new InvalidOperationException("Couldn't read the packet length"); } int packetLength = BitConverter.ToInt32(packetLengthBytes, 0); var seqBytes = new byte[4]; if (await stream.ReadAsync(seqBytes, 0, 4) != 4) { throw new InvalidOperationException("Couldn't read the sequence"); } int seq = BitConverter.ToInt32(seqBytes, 0); int readBytes = 0; var body = new byte[packetLength - 12]; int neededToRead = packetLength - 12; do { var bodyByte = new byte[packetLength - 12]; var availableBytes = await stream.ReadAsync(bodyByte, 0, neededToRead); neededToRead -= availableBytes; Buffer.BlockCopy(bodyByte, 0, body, readBytes, availableBytes); readBytes += availableBytes; }while (readBytes != packetLength - 12); var crcBytes = new byte[4]; if (await stream.ReadAsync(crcBytes, 0, 4) != 4) { throw new InvalidOperationException("Couldn't read the crc"); } int checksum = BitConverter.ToInt32(crcBytes, 0); byte[] rv = new byte[packetLengthBytes.Length + seqBytes.Length + body.Length]; Buffer.BlockCopy(packetLengthBytes, 0, rv, 0, packetLengthBytes.Length); Buffer.BlockCopy(seqBytes, 0, rv, packetLengthBytes.Length, seqBytes.Length); Buffer.BlockCopy(body, 0, rv, packetLengthBytes.Length + seqBytes.Length, body.Length); var crc32 = new Ionic.Crc.CRC32(); crc32.SlurpBlock(rv, 0, rv.Length); var validChecksum = crc32.Crc32Result; if (checksum != validChecksum) { throw new InvalidOperationException("invalid checksum! skip"); } return(new TcpMessage(seq, body)); }
// This ctor is private - no validation is done here. This is to allow the use // of a (specific) negative value for the _lengthLimit, to indicate that there // is no length set. So we validate the length limit in those ctors that use an // explicit param, otherwise we don't validate, because it could be our special // value. private CrcCalculatorStream (bool leaveOpen, Int64 length, System.IO.Stream stream, CRC32 crc32) : base() { _innerStream = stream; _Crc32 = crc32 ?? new CRC32(); _lengthLimit = length; _leaveOpen = leaveOpen; }
/// <summary> /// A constructor allowing the specification of the length of the stream /// to read, as well as whether to keep the underlying stream open upon /// Close(), and the CRC32 instance to use. /// </summary> /// <remarks> /// <para> /// The stream uses the specified CRC32 instance, which allows the /// application to specify how the CRC gets calculated. /// </para> /// </remarks> /// <param name="stream">The underlying stream</param> /// <param name="length">The length of the stream to slurp</param> /// <param name="leaveOpen">true to leave the underlying stream /// open upon close of the <c>CrcCalculatorStream</c>; false otherwise.</param> /// <param name="crc32">the CRC32 instance to use to calculate the CRC32</param> public CrcCalculatorStream(System.IO.Stream stream, Int64 length, bool leaveOpen, CRC32 crc32) : this(leaveOpen, length, stream, crc32) { if (length < 0) throw new ArgumentException("length"); }
public int Crc32(int currentReadPositionOffset, int length) { //Crc32 crc32 = new Crc32(); //logger.info("calc crc32 offset {0}, length {1} on: {2}", currentReadPositionOffset, length, BitConverter.ToString(buffer, readIndex + currentReadPositionOffset, length)); //return crc32.ComputeHash(buffer, readIndex + currentReadPositionOffset, length).Reverse().ToArray(); CRC32 crc32 = new CRC32(); crc32.SlurpBlock(buffer, readIndex+currentReadPositionOffset, length); return crc32.Crc32Result; }
// This ctor is private - no validation except null is done here. // This is to allow the use // of a (specific) negative value for the _lengthLimit, to indicate that there // is no length set. So we validate the length limit in those ctors that use an // explicit param, otherwise we don't validate, because it could be our special // value. CrcCalculatorStream(bool leaveOpen, Int64 length, System.IO.Stream stream, CRC32 crc32) { if (stream == null) throw new ArgumentNullException("stream"); _innerStream = stream; _crc32 = crc32 ?? new CRC32(); _lengthLimit = length; _leaveOpen = leaveOpen; }
public async Task Send(byte[] packet) { AsyncSocket socketToSend; logger.debug("transport send..."); lock (this) { if (socket == null) { logger.debug("socket == null"); throw new MTProtoTransportException("not connected"); } logger.debug("socket != null"); socketToSend = socket; } try { using (MemoryStream memoryStream = new MemoryStream()) { using (BinaryWriter binaryWriter = new BinaryWriter(memoryStream)) { CRC32 crc32 = new CRC32(); binaryWriter.Write(packet.Length + 12); binaryWriter.Write(Interlocked.Increment(ref sendCounter) - 1); binaryWriter.Write(packet); crc32.SlurpBlock(memoryStream.GetBuffer(), 0, 8 + packet.Length); binaryWriter.Write(crc32.Crc32Result); logger.debug("sending to socket: {0}", BitConverter.ToString(memoryStream.GetBuffer(), 0, (int) memoryStream.Position).Replace("-","").ToLower()); await socketToSend.Send(memoryStream.GetBuffer(), 0, (int) memoryStream.Position); } } } catch (Exception e) { throw new MTProtoTransportException("unable to send packet", e); } }
public bool TransportSend(byte[] packet) { //logger.debug("network send packet"); lock (this) { if (state != NetworkGatewayState.ESTABLISHED) { logger.warning("send error, state not established"); return false; } using (MemoryStream memoryStream = new MemoryStream()) { using (BinaryWriter binaryWriter = new BinaryWriter(memoryStream)) { //Crc32 crc32 = new Crc32(); CRC32 crc32 = new CRC32(); binaryWriter.Write(packet.Length + 12); binaryWriter.Write(sendCounter); binaryWriter.Write(packet); crc32.SlurpBlock(memoryStream.GetBuffer(), 0, 8+packet.Length); binaryWriter.Write(crc32.Crc32Result); byte[] transportPacket = memoryStream.ToArray(); //logger.info("send transport packet: {0}", BitConverter.ToString(transportPacket).Replace("-","")); var args = new SocketAsyncEventArgs(); args.SetBuffer(transportPacket, 0, transportPacket.Length); try { socket.SendAsync(args); } catch (Exception e) { logger.warning("transport packet send error: {0}", e); /* if(state != NetworkGatewayState.DISPOSED) { state = NetworkGatewayState.INIT; Connect(host, port); }*/ TryReconnect(); return false; } sendCounter++; return true; } } } }