2015-09-28 04:01:17 +02:00
using System ;
using System.Collections.Generic ;
using System.Diagnostics ;
using System.IO ;
using System.Linq ;
using System.Text.RegularExpressions ;
using System.Threading ;
using System.Threading.Tasks ;
using Ionic.Zlib ;
using TLSharp.Core.MTProto ;
using TLSharp.Core.MTProto.Crypto ;
using TLSharp.Core.Requests ;
using TLSharp.Core.Utils ;
namespace TLSharp.Core.Network
{
2016-04-18 12:50:57 +02:00
public class MtProtoSender
{
//private ulong sessionId = GenerateRandomUlong();
2018-02-20 19:25:20 +01:00
private static NLog . Logger logger = NLog . LogManager . GetLogger ( "MTProto" ) ;
2018-01-02 21:04:21 +01:00
private readonly uint UpdatesTooLongID = ( uint ) new TeleSharp . TL . TLUpdatesTooLong ( ) . Constructor ;
2016-04-18 12:50:57 +02:00
private TcpTransport _transport ;
private Session _session ;
2017-12-28 21:15:13 +01:00
public delegate void HandleUpdates ( TeleSharp . TL . TLAbsUpdates updates ) ;
public event HandleUpdates UpdatesEvent ;
2016-04-18 12:50:57 +02:00
public List < ulong > needConfirmation = new List < ulong > ( ) ;
public MtProtoSender ( TcpTransport transport , Session session )
{
_transport = transport ;
_session = session ;
}
public void ChangeTransport ( TcpTransport transport )
{
_transport = transport ;
}
private int GenerateSequence ( bool confirmed )
{
return confirmed ? _session . Sequence + + * 2 + 1 : _session . Sequence * 2 ;
}
2016-09-24 15:38:26 +02:00
public async Task Send ( TeleSharp . TL . TLMethod request )
2016-04-18 12:50:57 +02:00
{
// TODO: refactor
if ( needConfirmation . Any ( ) )
{
var ackRequest = new AckRequest ( needConfirmation ) ;
using ( var memory = new MemoryStream ( ) )
using ( var writer = new BinaryWriter ( memory ) )
{
2016-09-24 15:38:26 +02:00
ackRequest . SerializeBody ( writer ) ;
2016-04-18 12:50:57 +02:00
await Send ( memory . ToArray ( ) , ackRequest ) ;
needConfirmation . Clear ( ) ;
}
}
2015-09-28 04:01:17 +02:00
2016-04-18 12:50:57 +02:00
using ( var memory = new MemoryStream ( ) )
using ( var writer = new BinaryWriter ( memory ) )
{
2016-09-24 15:38:26 +02:00
request . SerializeBody ( writer ) ;
2016-04-18 12:50:57 +02:00
await Send ( memory . ToArray ( ) , request ) ;
}
2015-09-28 04:01:17 +02:00
2016-04-18 12:50:57 +02:00
_session . Save ( ) ;
}
2016-09-24 15:38:26 +02:00
public async Task Send ( byte [ ] packet , TeleSharp . TL . TLMethod request )
2016-04-18 12:50:57 +02:00
{
request . MessageId = _session . GetNewMessageId ( ) ;
byte [ ] msgKey ;
byte [ ] ciphertext ;
using ( MemoryStream plaintextPacket = makeMemory ( 8 + 8 + 8 + 4 + 4 + packet . Length ) )
{
using ( BinaryWriter plaintextWriter = new BinaryWriter ( plaintextPacket ) )
{
plaintextWriter . Write ( _session . Salt ) ;
plaintextWriter . Write ( _session . Id ) ;
plaintextWriter . Write ( request . MessageId ) ;
plaintextWriter . Write ( GenerateSequence ( request . Confirmed ) ) ;
plaintextWriter . Write ( packet . Length ) ;
plaintextWriter . Write ( packet ) ;
2018-02-20 19:25:20 +01:00
var buffer = plaintextPacket . GetBuffer ( ) ;
2018-02-20 22:43:36 +01:00
logger . Debug ( "Send {0} {1}" , request , Sniffer . MessageOut ( buffer ) ) ;
2018-02-20 19:25:20 +01:00
msgKey = Helpers . CalcMsgKey ( buffer ) ;
2016-04-18 12:50:57 +02:00
ciphertext = AES . EncryptAES ( Helpers . CalcKey ( _session . AuthKey . Data , msgKey , true ) , plaintextPacket . GetBuffer ( ) ) ;
}
}
2015-09-28 04:01:17 +02:00
2016-04-18 12:50:57 +02:00
using ( MemoryStream ciphertextPacket = makeMemory ( 8 + 16 + ciphertext . Length ) )
{
using ( BinaryWriter writer = new BinaryWriter ( ciphertextPacket ) )
{
writer . Write ( _session . AuthKey . Id ) ;
writer . Write ( msgKey ) ;
writer . Write ( ciphertext ) ;
2015-09-28 04:01:17 +02:00
2016-04-18 12:50:57 +02:00
await _transport . Send ( ciphertextPacket . GetBuffer ( ) ) ;
}
}
}
private Tuple < byte [ ] , ulong , int > DecodeMessage ( byte [ ] body )
{
byte [ ] message ;
ulong remoteMessageId ;
int remoteSequence ;
using ( var inputStream = new MemoryStream ( body ) )
using ( var inputReader = new BinaryReader ( inputStream ) )
{
if ( inputReader . BaseStream . Length < 8 )
throw new InvalidOperationException ( $"Can't decode packet" ) ;
ulong remoteAuthKeyId = inputReader . ReadUInt64 ( ) ; // TODO: check auth key id
byte [ ] msgKey = inputReader . ReadBytes ( 16 ) ; // TODO: check msg_key correctness
AESKeyData keyData = Helpers . CalcKey ( _session . AuthKey . Data , msgKey , false ) ;
byte [ ] plaintext = AES . DecryptAES ( keyData , inputReader . ReadBytes ( ( int ) ( inputStream . Length - inputStream . Position ) ) ) ;
2018-02-20 19:25:20 +01:00
logger . Debug ( Sniffer . MessageIn ( plaintext ) ) ;
2016-04-18 12:50:57 +02:00
using ( MemoryStream plaintextStream = new MemoryStream ( plaintext ) )
using ( BinaryReader plaintextReader = new BinaryReader ( plaintextStream ) )
{
var remoteSalt = plaintextReader . ReadUInt64 ( ) ;
var remoteSessionId = plaintextReader . ReadUInt64 ( ) ;
remoteMessageId = plaintextReader . ReadUInt64 ( ) ;
remoteSequence = plaintextReader . ReadInt32 ( ) ;
int msgLen = plaintextReader . ReadInt32 ( ) ;
message = plaintextReader . ReadBytes ( msgLen ) ;
}
}
return new Tuple < byte [ ] , ulong , int > ( message , remoteMessageId , remoteSequence ) ;
}
2017-12-28 22:31:13 +01:00
public async Task < byte [ ] > Receive ( TeleSharp . TL . TLMethod request )
2016-04-18 12:50:57 +02:00
{
2017-12-28 22:31:13 +01:00
while ( ! request . ConfirmReceived )
2016-04-18 12:50:57 +02:00
{
2017-12-28 22:31:13 +01:00
var result = DecodeMessage ( ( await _transport . Receieve ( ) ) . Body ) ;
2016-04-18 12:50:57 +02:00
2017-12-28 22:31:13 +01:00
using ( var messageStream = new MemoryStream ( result . Item1 , false ) )
using ( var messageReader = new BinaryReader ( messageStream ) )
2016-04-18 12:50:57 +02:00
{
2017-12-29 14:39:50 +01:00
processMessage ( result . Item2 , result . Item3 , messageReader , request ) ;
2016-04-18 12:50:57 +02:00
}
}
2015-09-28 04:01:17 +02:00
2016-04-18 12:50:57 +02:00
return null ;
}
2018-02-22 15:12:32 +01:00
public async Task < byte [ ] > Receive ( int timeoutms )
2017-12-28 22:31:13 +01:00
{
2018-02-22 15:12:32 +01:00
var result = DecodeMessage ( ( await _transport . Receieve ( timeoutms ) ) . Body ) ;
2017-12-28 22:31:13 +01:00
using ( var messageStream = new MemoryStream ( result . Item1 , false ) )
using ( var messageReader = new BinaryReader ( messageStream ) )
{
2017-12-29 14:39:50 +01:00
processMessage ( result . Item2 , result . Item3 , messageReader , null ) ;
2017-12-28 22:31:13 +01:00
}
return null ;
}
2016-11-07 00:40:19 +01:00
public async Task SendPingAsync ( )
{
var pingRequest = new PingRequest ( ) ;
using ( var memory = new MemoryStream ( ) )
using ( var writer = new BinaryWriter ( memory ) )
{
pingRequest . SerializeBody ( writer ) ;
await Send ( memory . ToArray ( ) , pingRequest ) ;
}
await Receive ( pingRequest ) ;
}
2017-12-29 14:39:50 +01:00
private bool processMessage ( ulong messageId , int sequence , BinaryReader messageReader , TeleSharp . TL . TLMethod request )
2016-04-18 12:50:57 +02:00
{
// TODO: check salt
// TODO: check sessionid
// TODO: check seqno
2017-12-28 22:31:13 +01:00
2016-04-18 12:50:57 +02:00
//logger.debug("processMessage: msg_id {0}, sequence {1}, data {2}", BitConverter.ToString(((MemoryStream)messageReader.BaseStream).GetBuffer(), (int) messageReader.BaseStream.Position, (int) (messageReader.BaseStream.Length - messageReader.BaseStream.Position)).Replace("-","").ToLower());
needConfirmation . Add ( messageId ) ;
uint code = messageReader . ReadUInt32 ( ) ;
messageReader . BaseStream . Position - = 4 ;
switch ( code )
{
case 0x73f1f8dc : // container
//logger.debug("MSG container");
2017-12-29 14:39:50 +01:00
return HandleContainer ( messageId , sequence , messageReader , request ) ;
2016-04-18 12:50:57 +02:00
case 0x7abe77ec : // ping
//logger.debug("MSG ping");
return HandlePing ( messageId , sequence , messageReader ) ;
case 0x347773c5 : // pong
//logger.debug("MSG pong");
2016-11-07 00:40:19 +01:00
return HandlePong ( messageId , sequence , messageReader , request ) ;
2016-04-18 12:50:57 +02:00
case 0xae500895 : // future_salts
//logger.debug("MSG future_salts");
return HandleFutureSalts ( messageId , sequence , messageReader ) ;
case 0x9ec20908 : // new_session_created
//logger.debug("MSG new_session_created");
return HandleNewSessionCreated ( messageId , sequence , messageReader ) ;
case 0x62d6b459 : // msgs_ack
//logger.debug("MSG msds_ack");
return HandleMsgsAck ( messageId , sequence , messageReader ) ;
case 0xedab447b : // bad_server_salt
//logger.debug("MSG bad_server_salt");
return HandleBadServerSalt ( messageId , sequence , messageReader , request ) ;
case 0xa7eff811 : // bad_msg_notification
//logger.debug("MSG bad_msg_notification");
return HandleBadMsgNotification ( messageId , sequence , messageReader ) ;
case 0x276d3ec6 : // msg_detailed_info
//logger.debug("MSG msg_detailed_info");
return HandleMsgDetailedInfo ( messageId , sequence , messageReader ) ;
case 0xf35c6d01 : // rpc_result
//logger.debug("MSG rpc_result");
return HandleRpcResult ( messageId , sequence , messageReader , request ) ;
case 0x3072cfa1 : // gzip_packed
//logger.debug("MSG gzip_packed");
2017-12-29 14:39:50 +01:00
return HandleGzipPacked ( messageId , sequence , messageReader , request ) ;
2016-04-18 12:50:57 +02:00
case 0xe317af7e :
2018-01-02 21:04:21 +01:00
case 0x914fbf11 :
case 0x16812688 :
2016-04-18 12:50:57 +02:00
case 0x78d4dec1 :
case 0x725b04c3 :
case 0x74ae4240 :
2018-01-02 21:04:21 +01:00
case 0x11f1331c :
2017-12-29 14:39:50 +01:00
return HandleUpdate ( code , sequence , messageReader , request ) ;
2016-04-18 12:50:57 +02:00
default :
2018-01-02 21:04:21 +01:00
Console . WriteLine ( "Msg code: {0:x8}" , code ) ;
2016-04-18 12:50:57 +02:00
return false ;
}
}
2015-09-28 04:01:17 +02:00
2017-12-29 14:39:50 +01:00
private bool HandleUpdate ( uint code , int sequence , BinaryReader messageReader , TeleSharp . TL . TLMethod request )
2016-04-18 12:50:57 +02:00
{
2015-09-28 04:01:17 +02:00
try
{
2017-12-28 22:31:13 +01:00
var update = ParseUpdate ( code , messageReader ) ;
2018-01-02 21:04:21 +01:00
if ( update ! = null & & UpdatesEvent ! = null )
{
UpdatesEvent ( update ) ;
}
2015-09-28 04:01:17 +02:00
}
2018-01-02 21:04:21 +01:00
catch ( Exception ex )
2015-09-28 04:01:17 +02:00
{
2018-01-02 21:04:21 +01:00
Console . WriteLine ( ex ) ;
2015-09-28 04:01:17 +02:00
}
2017-12-28 21:15:13 +01:00
return false ;
}
2017-12-28 22:31:13 +01:00
private TeleSharp . TL . TLAbsUpdates ParseUpdate ( uint code , BinaryReader messageReader )
2017-12-28 21:15:13 +01:00
{
2017-12-28 22:31:13 +01:00
switch ( code )
2017-12-28 21:15:13 +01:00
{
case 0xe317af7e :
return DecodeUpdate < TeleSharp . TL . TLUpdatesTooLong > ( messageReader ) ;
2018-01-02 21:04:21 +01:00
case 0x914fbf11 :
2017-12-28 21:15:13 +01:00
return DecodeUpdate < TeleSharp . TL . TLUpdateShortMessage > ( messageReader ) ;
2018-01-02 21:04:21 +01:00
case 0x16812688 :
2017-12-28 21:15:13 +01:00
return DecodeUpdate < TeleSharp . TL . TLUpdateShortChatMessage > ( messageReader ) ;
case 0x78d4dec1 :
return DecodeUpdate < TeleSharp . TL . TLUpdateShort > ( messageReader ) ;
case 0x725b04c3 :
return DecodeUpdate < TeleSharp . TL . TLUpdatesCombined > ( messageReader ) ;
case 0x74ae4240 :
return DecodeUpdate < TeleSharp . TL . TLUpdates > ( messageReader ) ;
2018-01-02 21:04:21 +01:00
case 0x11f1331c :
return DecodeUpdate < TeleSharp . TL . TLUpdateShortSentMessage > ( messageReader ) ;
2017-12-28 21:15:13 +01:00
default :
return null ;
}
}
2018-01-02 21:04:21 +01:00
private TeleSharp . TL . TLAbsUpdates DecodeUpdate < T > ( BinaryReader messageReader ) where T : TeleSharp . TL . TLAbsUpdates
2017-12-28 21:15:13 +01:00
{
2018-01-02 21:04:21 +01:00
var ms = messageReader . BaseStream as MemoryStream ;
var update = ( T ) TeleSharp . TL . ObjectUtils . DeserializeObject ( messageReader ) ;
2017-12-28 21:15:13 +01:00
return update ;
2016-04-18 12:50:57 +02:00
}
2017-12-29 14:39:50 +01:00
private bool HandleGzipPacked ( ulong messageId , int sequence , BinaryReader messageReader , TeleSharp . TL . TLMethod request )
2016-04-18 12:50:57 +02:00
{
uint code = messageReader . ReadUInt32 ( ) ;
byte [ ] packedData = GZipStream . UncompressBuffer ( Serializers . Bytes . read ( messageReader ) ) ;
using ( MemoryStream packedStream = new MemoryStream ( packedData , false ) )
using ( BinaryReader compressedReader = new BinaryReader ( packedStream ) )
{
2017-12-29 14:39:50 +01:00
processMessage ( messageId , sequence , compressedReader , request ) ;
2016-04-18 12:50:57 +02:00
}
2015-09-28 04:01:17 +02:00
2016-04-18 12:50:57 +02:00
return true ;
}
2015-09-28 04:01:17 +02:00
2016-09-24 15:38:26 +02:00
private bool HandleRpcResult ( ulong messageId , int sequence , BinaryReader messageReader , TeleSharp . TL . TLMethod request )
2016-04-18 12:50:57 +02:00
{
uint code = messageReader . ReadUInt32 ( ) ;
ulong requestId = messageReader . ReadUInt64 ( ) ;
2015-09-28 04:01:17 +02:00
2016-04-18 12:50:57 +02:00
if ( requestId = = ( ulong ) request . MessageId )
request . ConfirmReceived = true ;
2015-09-28 04:01:17 +02:00
2016-04-18 12:50:57 +02:00
//throw new NotImplementedException();
/ *
2015-09-28 04:01:17 +02:00
lock ( runningRequests )
{
if ( ! runningRequests . ContainsKey ( requestId ) )
{
logger . warning ( "rpc response on unknown request: {0}" , requestId ) ;
messageReader . BaseStream . Position - = 12 ;
return false ;
}
request = runningRequests [ requestId ] ;
runningRequests . Remove ( requestId ) ;
}
* /
2016-04-18 12:50:57 +02:00
uint innerCode = messageReader . ReadUInt32 ( ) ;
if ( innerCode = = 0x2144ca19 )
{ // rpc_error
int errorCode = messageReader . ReadInt32 ( ) ;
string errorMessage = Serializers . String . read ( messageReader ) ;
2018-01-05 14:12:41 +01:00
Console . Error . WriteLine ( $"ERROR: {errorMessage} - {errorCode}" ) ;
2016-04-18 12:50:57 +02:00
if ( errorMessage . StartsWith ( "FLOOD_WAIT_" ) )
{
var resultString = Regex . Match ( errorMessage , @"\d+" ) . Value ;
var seconds = int . Parse ( resultString ) ;
Throw FloodException instead of calling Thread.Sleep()
Doing this is better when looking at the problem from at least
these 2 points of view:
a) You're working on TLSharp itself: You might be testing some
new things or running TLSharp's tests. Suddenly, if a FLOOD_WAIT
response happens, there's no clear way to know. You just see the
tests taking forever. But if a test has reached a situation in
which Telegram has returned a FLOOD_WAIT error, it's very likely
that what happens is that the TLSharp operation being tested is
actually buggy, which means that the test should FAIL FAST and
show a stacktrace of the problem so that you can see where in the
code was the FLOOD_WAIT received/caused. You shouldn't need to
kill the run of the test suite and hope to hit the problem again
only when you were using the debugger (to be able to pause
execution and examine a stacktrace of where the execution flow is).
b) You're using TLSharp: if you hit a FLOOD_WAIT problem it may
happen because:
b1) TLSharp has a bug: in this case it's better to throw an
exception so that the user can copy the stacktrace and paste
it into a new Github issue.
b2) Your program uses TLSharp sending excessive requests: you
want to have your program know when you hit the limit, to be
able to fix your program to not be so floody. But a call to
Thread.Sleep() doesn't help you to know this, you just know
that suddenly your program has hung, and you don't know why.
You cannot react to the problem, however with an exception you
can react to the problem (for example by examining the time
that the exception provides, as a TimeSpan property, to know
how much your program needs to wait to be able to use TLSharp
again).
2016-11-01 16:38:28 +01:00
throw new FloodException ( TimeSpan . FromSeconds ( seconds ) ) ;
2016-04-18 12:50:57 +02:00
}
else if ( errorMessage . StartsWith ( "PHONE_MIGRATE_" ) )
{
var resultString = Regex . Match ( errorMessage , @"\d+" ) . Value ;
var dcIdx = int . Parse ( resultString ) ;
2016-10-30 08:57:45 +01:00
throw new PhoneMigrationException ( dcIdx ) ;
2016-04-18 12:50:57 +02:00
}
2016-10-29 10:47:18 +02:00
else if ( errorMessage . StartsWith ( "FILE_MIGRATE_" ) )
{
var resultString = Regex . Match ( errorMessage , @"\d+" ) . Value ;
var dcIdx = int . Parse ( resultString ) ;
throw new FileMigrationException ( dcIdx ) ;
}
else if ( errorMessage . StartsWith ( "USER_MIGRATE_" ) )
{
var resultString = Regex . Match ( errorMessage , @"\d+" ) . Value ;
var dcIdx = int . Parse ( resultString ) ;
throw new UserMigrationException ( dcIdx ) ;
}
2017-09-23 14:06:54 +02:00
else if ( errorMessage . StartsWith ( "NETWORK_MIGRATE_" ) )
{
var resultString = Regex . Match ( errorMessage , @"\d+" ) . Value ;
var dcIdx = int . Parse ( resultString ) ;
throw new NetworkMigrationException ( dcIdx ) ;
}
2016-10-30 17:15:10 +01:00
else if ( errorMessage = = "PHONE_CODE_INVALID" )
{
throw new InvalidPhoneCodeException ( "The numeric code used to authenticate does not match the numeric code sent by SMS/Telegram" ) ;
}
2016-11-16 14:31:00 +01:00
else if ( errorMessage = = "SESSION_PASSWORD_NEEDED" )
{
throw new CloudPasswordNeededException ( "This Account has Cloud Password !" ) ;
}
2016-04-18 12:50:57 +02:00
else
{
throw new InvalidOperationException ( errorMessage ) ;
}
2015-09-28 04:01:17 +02:00
2016-04-18 12:50:57 +02:00
}
else if ( innerCode = = 0x3072cfa1 )
{
try
{
// gzip_packed
byte [ ] packedData = Serializers . Bytes . read ( messageReader ) ;
2016-07-19 15:47:08 +02:00
using ( var ms = new MemoryStream ( ) )
2016-04-18 12:50:57 +02:00
{
2016-07-19 15:47:08 +02:00
using ( var packedStream = new MemoryStream ( packedData , false ) )
using ( var zipStream = new GZipStream ( packedStream , CompressionMode . Decompress ) )
{
zipStream . CopyTo ( ms ) ;
ms . Position = 0 ;
}
using ( var compressedReader = new BinaryReader ( ms ) )
{
2017-11-09 11:35:15 +01:00
request . DeserializeResponse ( compressedReader ) ;
2016-07-19 15:47:08 +02:00
}
2016-04-18 12:50:57 +02:00
}
}
catch ( ZlibException ex )
{
}
}
else
{
messageReader . BaseStream . Position - = 4 ;
2017-11-09 11:35:15 +01:00
request . DeserializeResponse ( messageReader ) ;
2016-04-18 12:50:57 +02:00
}
2015-09-28 04:01:17 +02:00
2016-04-18 12:50:57 +02:00
return false ;
}
private bool HandleMsgDetailedInfo ( ulong messageId , int sequence , BinaryReader messageReader )
{
return false ;
}
private bool HandleBadMsgNotification ( ulong messageId , int sequence , BinaryReader messageReader )
{
uint code = messageReader . ReadUInt32 ( ) ;
ulong requestId = messageReader . ReadUInt64 ( ) ;
int requestSequence = messageReader . ReadInt32 ( ) ;
int errorCode = messageReader . ReadInt32 ( ) ;
switch ( errorCode )
{
case 16 :
2016-10-23 10:17:59 +02:00
throw new InvalidOperationException ( "msg_id too low (most likely, client time is wrong; it would be worthwhile to synchronize it using msg_id notifications and re-send the original message with the “correct” msg_id or wrap it in a container with a new msg_id if the original message had waited too long on the client to be transmitted)" ) ;
2016-04-18 12:50:57 +02:00
case 17 :
2016-10-23 10:17:59 +02:00
throw new InvalidOperationException ( "msg_id too high (similar to the previous case, the client time has to be synchronized, and the message re-sent with the correct msg_id)" ) ;
2016-04-18 12:50:57 +02:00
case 18 :
throw new InvalidOperationException ( "incorrect two lower order msg_id bits (the server expects client message msg_id to be divisible by 4)" ) ;
case 19 :
throw new InvalidOperationException ( "container msg_id is the same as msg_id of a previously received message (this must never happen)" ) ;
case 20 :
throw new InvalidOperationException ( "message too old, and it cannot be verified whether the server has received a message with this msg_id or not" ) ;
case 32 :
throw new InvalidOperationException ( "msg_seqno too low (the server has already received a message with a lower msg_id but with either a higher or an equal and odd seqno)" ) ;
case 33 :
throw new InvalidOperationException ( " msg_seqno too high (similarly, there is a message with a higher msg_id but with either a lower or an equal and odd seqno)" ) ;
case 34 :
throw new InvalidOperationException ( "an even msg_seqno expected (irrelevant message), but odd received" ) ;
case 35 :
throw new InvalidOperationException ( "odd msg_seqno expected (relevant message), but even received" ) ;
case 48 :
throw new InvalidOperationException ( "incorrect server salt (in this case, the bad_server_salt response is received with the correct salt, and the message is to be re-sent with it)" ) ;
case 64 :
throw new InvalidOperationException ( "invalid container" ) ;
2015-09-28 04:01:17 +02:00
2016-04-18 12:50:57 +02:00
}
throw new NotImplementedException ( "This should never happens" ) ;
/ *
2015-09-28 04:01:17 +02:00
logger . debug ( "bad_msg_notification: msgid {0}, seq {1}, errorcode {2}" , requestId , requestSequence ,
errorCode ) ;
* /
2016-04-18 12:50:57 +02:00
/ *
2015-09-28 04:01:17 +02:00
if ( ! runningRequests . ContainsKey ( requestId ) )
{
logger . debug ( "bad msg notification on unknown request" ) ;
return true ;
}
* /
2016-04-18 12:50:57 +02:00
//OnBrokenSessionEvent();
//MTProtoRequest request = runningRequests[requestId];
//request.OnException(new MTProtoBadMessageException(errorCode));
2015-09-28 04:01:17 +02:00
2016-04-18 12:50:57 +02:00
return true ;
}
2015-09-28 04:01:17 +02:00
2016-09-24 15:38:26 +02:00
private bool HandleBadServerSalt ( ulong messageId , int sequence , BinaryReader messageReader , TeleSharp . TL . TLMethod request )
2016-04-18 12:50:57 +02:00
{
uint code = messageReader . ReadUInt32 ( ) ;
ulong badMsgId = messageReader . ReadUInt64 ( ) ;
int badMsgSeqNo = messageReader . ReadInt32 ( ) ;
int errorCode = messageReader . ReadInt32 ( ) ;
ulong newSalt = messageReader . ReadUInt64 ( ) ;
2015-09-28 04:01:17 +02:00
2016-04-18 12:50:57 +02:00
//logger.debug("bad_server_salt: msgid {0}, seq {1}, errorcode {2}, newsalt {3}", badMsgId, badMsgSeqNo, errorCode, newSalt);
2015-09-28 04:01:17 +02:00
2016-04-18 12:50:57 +02:00
_session . Salt = newSalt ;
2015-09-28 04:01:17 +02:00
2016-04-18 12:50:57 +02:00
//resend
Send ( request ) ;
/ *
2015-09-28 04:01:17 +02:00
if ( ! runningRequests . ContainsKey ( badMsgId ) ) {
logger . debug ( "bad server salt on unknown message" ) ;
return true ;
}
* /
2016-04-18 12:50:57 +02:00
//MTProtoRequest request = runningRequests[badMsgId];
//request.OnException(new MTProtoBadServerSaltException(salt));
2015-09-28 04:01:17 +02:00
2016-04-18 12:50:57 +02:00
return true ;
}
2015-09-28 04:01:17 +02:00
2016-04-18 12:50:57 +02:00
private bool HandleMsgsAck ( ulong messageId , int sequence , BinaryReader messageReader )
{
return false ;
}
2015-09-28 04:01:17 +02:00
2016-04-18 12:50:57 +02:00
private bool HandleNewSessionCreated ( ulong messageId , int sequence , BinaryReader messageReader )
{
return false ;
}
2015-09-28 04:01:17 +02:00
2016-04-18 12:50:57 +02:00
private bool HandleFutureSalts ( ulong messageId , int sequence , BinaryReader messageReader )
{
uint code = messageReader . ReadUInt32 ( ) ;
ulong requestId = messageReader . ReadUInt64 ( ) ;
2015-09-28 04:01:17 +02:00
2016-04-18 12:50:57 +02:00
messageReader . BaseStream . Position - = 12 ;
2015-09-28 04:01:17 +02:00
2016-04-18 12:50:57 +02:00
throw new NotImplementedException ( "Handle future server salts function isn't implemented." ) ;
/ *
2015-09-28 04:01:17 +02:00
if ( ! runningRequests . ContainsKey ( requestId ) )
{
logger . info ( "future salts on unknown request" ) ;
return false ;
}
* /
2016-04-18 12:50:57 +02:00
// MTProtoRequest request = runningRequests[requestId];
// runningRequests.Remove(requestId);
// request.OnResponse(messageReader);
return true ;
}
2016-11-07 00:40:19 +01:00
private bool HandlePong ( ulong messageId , int sequence , BinaryReader messageReader , TeleSharp . TL . TLMethod request )
2016-04-18 12:50:57 +02:00
{
2016-11-07 00:40:19 +01:00
uint code = messageReader . ReadUInt32 ( ) ;
ulong msgId = messageReader . ReadUInt64 ( ) ;
if ( msgId = = ( ulong ) request . MessageId )
{
request . ConfirmReceived = true ;
}
2016-04-18 12:50:57 +02:00
return false ;
}
private bool HandlePing ( ulong messageId , int sequence , BinaryReader messageReader )
{
return false ;
}
2017-12-29 14:39:50 +01:00
private bool HandleContainer ( ulong messageId , int sequence , BinaryReader messageReader , TeleSharp . TL . TLMethod request )
2016-04-18 12:50:57 +02:00
{
uint code = messageReader . ReadUInt32 ( ) ;
int size = messageReader . ReadInt32 ( ) ;
for ( int i = 0 ; i < size ; i + + )
{
ulong innerMessageId = messageReader . ReadUInt64 ( ) ;
int innerSequence = messageReader . ReadInt32 ( ) ;
int innerLength = messageReader . ReadInt32 ( ) ;
long beginPosition = messageReader . BaseStream . Position ;
try
{
2017-12-29 14:39:50 +01:00
if ( ! processMessage ( innerMessageId , sequence , messageReader , request ) )
2016-04-18 12:50:57 +02:00
{
messageReader . BaseStream . Position = beginPosition + innerLength ;
}
}
2017-12-28 21:15:13 +01:00
catch ( Exception )
2016-04-18 12:50:57 +02:00
{
// logger.error("failed to process message in contailer: {0}", e);
messageReader . BaseStream . Position = beginPosition + innerLength ;
}
}
2015-09-28 04:01:17 +02:00
2016-04-18 12:50:57 +02:00
return false ;
}
2015-09-28 04:01:17 +02:00
2016-04-18 12:50:57 +02:00
private MemoryStream makeMemory ( int len )
{
return new MemoryStream ( new byte [ len ] , 0 , len , true , true ) ;
}
}
2016-10-22 16:00:15 +02:00
Throw FloodException instead of calling Thread.Sleep()
Doing this is better when looking at the problem from at least
these 2 points of view:
a) You're working on TLSharp itself: You might be testing some
new things or running TLSharp's tests. Suddenly, if a FLOOD_WAIT
response happens, there's no clear way to know. You just see the
tests taking forever. But if a test has reached a situation in
which Telegram has returned a FLOOD_WAIT error, it's very likely
that what happens is that the TLSharp operation being tested is
actually buggy, which means that the test should FAIL FAST and
show a stacktrace of the problem so that you can see where in the
code was the FLOOD_WAIT received/caused. You shouldn't need to
kill the run of the test suite and hope to hit the problem again
only when you were using the debugger (to be able to pause
execution and examine a stacktrace of where the execution flow is).
b) You're using TLSharp: if you hit a FLOOD_WAIT problem it may
happen because:
b1) TLSharp has a bug: in this case it's better to throw an
exception so that the user can copy the stacktrace and paste
it into a new Github issue.
b2) Your program uses TLSharp sending excessive requests: you
want to have your program know when you hit the limit, to be
able to fix your program to not be so floody. But a call to
Thread.Sleep() doesn't help you to know this, you just know
that suddenly your program has hung, and you don't know why.
You cannot react to the problem, however with an exception you
can react to the problem (for example by examining the time
that the exception provides, as a TimeSpan property, to know
how much your program needs to wait to be able to use TLSharp
again).
2016-11-01 16:38:28 +01:00
public class FloodException : Exception
{
public TimeSpan TimeToWait { get ; private set ; }
internal FloodException ( TimeSpan timeToWait )
: base ( $"Flood prevention. Telegram now requires your program to do requests again only after {timeToWait.TotalSeconds} seconds have passed ({nameof(TimeToWait)} property)." +
" If you think the culprit of this problem may lie in TLSharp's implementation, open a Github issue please." )
{
TimeToWait = timeToWait ;
}
}
2016-10-30 09:04:32 +01:00
internal abstract class DataCenterMigrationException : Exception
2016-10-22 16:00:15 +02:00
{
internal int DC { get ; private set ; }
2016-10-30 09:16:55 +01:00
private const string REPORT_MESSAGE =
2016-10-30 09:33:29 +01:00
" See: https://github.com/sochix/TLSharp#i-get-a-xxxmigrationexception-or-a-migrate_x-error" ;
2016-10-30 09:16:55 +01:00
protected DataCenterMigrationException ( string msg , int dc ) : base ( msg + REPORT_MESSAGE )
2016-10-22 16:00:15 +02:00
{
DC = dc ;
}
}
2016-10-29 10:47:18 +02:00
2016-10-30 09:04:32 +01:00
internal class PhoneMigrationException : DataCenterMigrationException
2016-10-29 10:47:18 +02:00
{
2016-10-30 09:04:32 +01:00
internal PhoneMigrationException ( int dc )
2016-10-30 09:16:55 +01:00
: base ( $"Phone number registered to a different DC: {dc}." , dc )
2016-10-30 09:04:32 +01:00
{
}
}
2016-10-29 10:47:18 +02:00
2016-10-30 09:04:32 +01:00
internal class FileMigrationException : DataCenterMigrationException
{
2016-10-29 10:47:18 +02:00
internal FileMigrationException ( int dc )
2016-10-30 09:16:55 +01:00
: base ( $"File located on a different DC: {dc}." , dc )
2016-10-29 10:47:18 +02:00
{
}
}
2016-10-30 09:04:32 +01:00
internal class UserMigrationException : DataCenterMigrationException
2016-10-29 10:47:18 +02:00
{
internal UserMigrationException ( int dc )
2016-10-30 09:16:55 +01:00
: base ( $"User located on a different DC: {dc}." , dc )
2016-10-29 10:47:18 +02:00
{
}
}
2017-09-23 14:06:54 +02:00
internal class NetworkMigrationException : DataCenterMigrationException
{
internal NetworkMigrationException ( int dc )
: base ( $"Network located on a different DC: {dc}." , dc )
{
}
}
}