2015-09-28 04:01:17 +02:00
using System ;
2020-04-05 13:33:18 +02:00
using System.Linq ;
2015-09-28 04:01:17 +02:00
using System.Net ;
using System.Net.Sockets ;
2020-01-27 08:03:45 +01:00
using System.Threading ;
2015-09-28 04:01:17 +02:00
using System.Threading.Tasks ;
2020-04-05 13:33:18 +02:00
using TLSharp.Core.MTProto.Crypto ;
2015-09-28 04:01:17 +02:00
namespace TLSharp.Core.Network
{
2017-01-27 10:58:47 +01:00
public delegate TcpClient TcpClientConnectionHandler ( string address , int port ) ;
2016-04-18 12:50:57 +02:00
public class TcpTransport : IDisposable
{
2020-04-08 21:20:54 +02:00
private TcpClient tcpClient ;
private NetworkStream stream ;
2016-04-18 12:50:57 +02:00
private int sendCounter = 0 ;
2020-04-08 21:20:54 +02:00
TcpClientConnectionHandler handler ;
string address ;
int port ;
IPAddress ipAddress ;
2016-04-18 12:50:57 +02:00
2017-01-27 10:58:47 +01:00
public TcpTransport ( string address , int port , TcpClientConnectionHandler handler = null )
2020-04-08 21:20:54 +02:00
{
this . handler = handler ;
this . address = address ;
this . port = port ;
ipAddress = IPAddress . Parse ( address ) ;
}
public async Task Connect ( )
2016-04-18 12:50:57 +02:00
{
2017-01-27 10:58:47 +01:00
if ( handler = = null )
{
2020-04-08 21:20:54 +02:00
if ( tcpClient ! = null )
{
tcpClient . Close ( ) ;
}
2020-01-31 07:39:19 +01:00
tcpClient = new TcpClient ( ipAddress . AddressFamily ) ;
2020-04-09 02:47:41 +02:00
sendCounter = 0 ;
2020-04-05 17:22:36 +02:00
2020-04-09 02:47:41 +02:00
try
{
2020-04-08 21:20:54 +02:00
await tcpClient . ConnectAsync ( ipAddress , port ) ;
2020-04-05 17:22:36 +02:00
} catch ( Exception ex ) {
2020-04-08 21:20:54 +02:00
throw new Exception ( $"Problem when trying to connect to {ipAddress}:{port}; either there's no internet connection or the IP address version is not compatible (if the latter, consider using DataCenterIPVersion enum)" ,
2020-04-05 17:22:36 +02:00
ex ) ;
}
2017-01-27 10:58:47 +01:00
}
else
2020-01-31 07:39:19 +01:00
tcpClient = handler ( address , port ) ;
2019-10-02 10:36:18 +02:00
2020-01-31 07:39:19 +01:00
if ( tcpClient . Connected )
2019-10-02 10:36:18 +02:00
{
2020-01-31 07:39:19 +01:00
stream = tcpClient . GetStream ( ) ;
2019-10-02 10:36:18 +02:00
}
2016-04-18 12:50:57 +02:00
}
2020-01-27 08:03:45 +01:00
public async Task Send ( byte [ ] packet , CancellationToken token = default ( CancellationToken ) )
2016-04-18 12:50:57 +02:00
{
2020-01-31 07:39:19 +01:00
if ( ! tcpClient . Connected )
2016-04-18 12:50:57 +02:00
throw new InvalidOperationException ( "Client not connected to server." ) ;
var tcpMessage = new TcpMessage ( sendCounter , packet ) ;
2020-01-31 07:39:19 +01:00
await stream . WriteAsync ( tcpMessage . Encode ( ) , 0 , tcpMessage . Encode ( ) . Length , token ) . ConfigureAwait ( false ) ;
2016-04-18 12:50:57 +02:00
sendCounter + + ;
}
2020-01-27 08:03:45 +01:00
public async Task < TcpMessage > Receive ( CancellationToken token = default ( CancellationToken ) )
2016-01-09 12:05:00 +01:00
{
var packetLengthBytes = new byte [ 4 ] ;
2020-01-31 07:39:19 +01:00
if ( await stream . ReadAsync ( packetLengthBytes , 0 , 4 , token ) . ConfigureAwait ( false ) ! = 4 )
2016-01-09 12:05:00 +01:00
throw new InvalidOperationException ( "Couldn't read the packet length" ) ;
int packetLength = BitConverter . ToInt32 ( packetLengthBytes , 0 ) ;
2015-09-28 04:01:17 +02:00
2016-01-09 12:05:00 +01:00
var seqBytes = new byte [ 4 ] ;
2020-01-31 07:39:19 +01:00
if ( await stream . ReadAsync ( seqBytes , 0 , 4 , token ) . ConfigureAwait ( false ) ! = 4 )
2016-01-09 12:05:00 +01:00
throw new InvalidOperationException ( "Couldn't read the sequence" ) ;
int seq = BitConverter . ToInt32 ( seqBytes , 0 ) ;
2015-09-28 04:01:17 +02:00
2016-01-09 12:05:00 +01:00
int readBytes = 0 ;
var body = new byte [ packetLength - 12 ] ;
int neededToRead = packetLength - 12 ;
do
{
var bodyByte = new byte [ packetLength - 12 ] ;
2020-01-31 07:39:19 +01:00
var availableBytes = await stream . ReadAsync ( bodyByte , 0 , neededToRead , token ) . ConfigureAwait ( false ) ;
2016-01-09 12:05:00 +01:00
neededToRead - = availableBytes ;
Buffer . BlockCopy ( bodyByte , 0 , body , readBytes , availableBytes ) ;
readBytes + = availableBytes ;
}
while ( readBytes ! = packetLength - 12 ) ;
var crcBytes = new byte [ 4 ] ;
2020-01-31 07:39:19 +01:00
if ( await stream . ReadAsync ( crcBytes , 0 , 4 , token ) . ConfigureAwait ( false ) ! = 4 )
2016-01-09 12:05:00 +01:00
throw new InvalidOperationException ( "Couldn't read the crc" ) ;
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 ) ;
2020-04-05 13:33:18 +02:00
var crc32 = new Crc32 ( ) ;
var computedChecksum = crc32 . ComputeHash ( rv ) . Reverse ( ) ;
2016-01-09 12:05:00 +01:00
2020-04-05 13:33:18 +02:00
if ( ! crcBytes . SequenceEqual ( computedChecksum ) )
2016-01-09 12:05:00 +01:00
{
throw new InvalidOperationException ( "invalid checksum! skip" ) ;
}
return new TcpMessage ( seq , body ) ;
2018-03-03 17:38:51 +01:00
}
public bool IsConnected
{
get
{
2020-04-08 21:20:54 +02:00
return this . tcpClient ! = null & & this . tcpClient . Connected ;
2018-03-03 17:38:51 +01:00
}
}
2016-04-18 12:50:57 +02:00
public void Dispose ( )
{
2020-01-31 07:39:19 +01:00
if ( tcpClient . Connected )
2019-10-02 10:36:18 +02:00
{
2020-01-31 07:39:19 +01:00
stream . Close ( ) ;
tcpClient . Close ( ) ;
2019-10-02 10:36:18 +02:00
}
2018-03-03 17:38:51 +01:00
}
2016-04-18 12:50:57 +02:00
}
2015-09-28 04:01:17 +02:00
}