From 824ba77e12e5c4eafe4cb50acd0a8e86052897c9 Mon Sep 17 00:00:00 2001 From: Ilya Pirozhneko Date: Wed, 27 Jan 2016 14:02:18 +0300 Subject: [PATCH] Added auto dc switching --- TLSharp.Core/MTProto/TL.cs | 8 +- TLSharp.Core/Network/MtProtoSender.cs | 20 ++- TLSharp.Core/Network/TcpTransport.cs | 4 +- TLSharp.Core/Session.cs | 22 ++- TLSharp.Core/TelegramClient.cs | 203 +++++++++++++++--------- TLSharp.Tests/NotificatioClientTests.cs | 51 ++---- TLSharp.Tests/app.config | 3 +- 7 files changed, 179 insertions(+), 132 deletions(-) diff --git a/TLSharp.Core/MTProto/TL.cs b/TLSharp.Core/MTProto/TL.cs index e80e7fe..2253f67 100644 --- a/TLSharp.Core/MTProto/TL.cs +++ b/TLSharp.Core/MTProto/TL.cs @@ -4321,8 +4321,8 @@ namespace TLSharp.Core.MTProto Serializers.String.write(writer, this.first_name); Serializers.String.write(writer, this.last_name); Serializers.String.write(writer, this.phone); - this.photo.Write(writer); - this.status.Write(writer); + //this.photo.Write(writer); + //this.status.Write(writer); writer.Write(this.inactive ? 0x997275b5 : 0xbc799737); } @@ -4332,8 +4332,8 @@ namespace TLSharp.Core.MTProto this.first_name = Serializers.String.read(reader); this.last_name = Serializers.String.read(reader); this.phone = Serializers.String.read(reader); - this.photo = TL.Parse(reader); - this.status = TL.Parse(reader); + //this.photo = TL.Parse(reader); + //this.status = TL.Parse(reader); this.inactive = reader.ReadUInt32() == 0x997275b5; } diff --git a/TLSharp.Core/Network/MtProtoSender.cs b/TLSharp.Core/Network/MtProtoSender.cs index 581b4db..fe2ead7 100644 --- a/TLSharp.Core/Network/MtProtoSender.cs +++ b/TLSharp.Core/Network/MtProtoSender.cs @@ -29,6 +29,11 @@ namespace TLSharp.Core.Network _session = session; } + public void ChangeTransport(TcpTransport transport) + { + _transport = transport; + } + private int GenerateSequence(bool confirmed) { return confirmed ? _session.Sequence++ * 2 + 1 : _session.Sequence * 2; @@ -101,9 +106,12 @@ namespace TLSharp.Core.Network ulong remoteMessageId; int remoteSequence; - using (MemoryStream inputStream = new MemoryStream(body)) - using (BinaryReader inputReader = new BinaryReader(inputStream)) + 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); @@ -130,8 +138,8 @@ namespace TLSharp.Core.Network { var result = DecodeMessage((await _transport.Receieve()).Body); - using (MemoryStream messageStream = new MemoryStream(result.Item1, false)) - using (BinaryReader messageReader = new BinaryReader(messageStream)) + using (var messageStream = new MemoryStream(result.Item1, false)) + using (var messageReader = new BinaryReader(messageStream)) { processMessage(result.Item2, result.Item3, messageReader, request); } @@ -271,7 +279,9 @@ namespace TLSharp.Core.Network { var resultString = Regex.Match(errorMessage, @"\d+").Value; var dcIdx = int.Parse(resultString); - throw new InvalidOperationException($"Your phone number registered to {dcIdx} dc. Please update settings. See https://github.com/sochix/TLSharp#i-get-an-error-migrate_x for details."); + var exception = new InvalidOperationException($"Your phone number registered to {dcIdx} dc. Please update settings. See https://github.com/sochix/TLSharp#i-get-an-error-migrate_x for details."); + exception.Data.Add("dcId", dcIdx); + throw exception; } else { diff --git a/TLSharp.Core/Network/TcpTransport.cs b/TLSharp.Core/Network/TcpTransport.cs index a32181f..a1db9e1 100644 --- a/TLSharp.Core/Network/TcpTransport.cs +++ b/TLSharp.Core/Network/TcpTransport.cs @@ -8,12 +8,10 @@ namespace TLSharp.Core.Network { public class TcpTransport : IDisposable { - private const string defaultConnectionAddress = "91.108.56.165"; - private const int defaultConnectionPort = 443; private readonly TcpClient _tcpClient; private int sendCounter = 0; - public TcpTransport(string address = defaultConnectionAddress, int port = defaultConnectionPort) + public TcpTransport(string address, int port) { _tcpClient = new TcpClient(); diff --git a/TLSharp.Core/Session.cs b/TLSharp.Core/Session.cs index 21e07a9..c7776d1 100644 --- a/TLSharp.Core/Session.cs +++ b/TLSharp.Core/Session.cs @@ -49,7 +49,12 @@ namespace TLSharp.Core public class Session { + private const string defaultConnectionAddress = "91.108.56.165"; + private const int defaultConnectionPort = 443; + public string SessionUserId { get; set; } + public string ServerAddress { get; set; } + public int Port { get; set; } public AuthKey AuthKey { get; set; } public ulong Id { get; set; } public int Sequence { get; set; } @@ -78,6 +83,9 @@ namespace TLSharp.Core writer.Write(Salt); writer.Write(LastMessageId); writer.Write(TimeOffset); + Serializers.String.write(writer, ServerAddress); + writer.Write(Port); + if (User != null) { writer.Write(1); @@ -105,6 +113,8 @@ namespace TLSharp.Core var salt = reader.ReadUInt64(); var lastMessageId = reader.ReadInt64(); var timeOffset = reader.ReadInt32(); + var serverAddress = Serializers.String.read(reader); + var port = reader.ReadInt32(); var isAuthExsist = reader.ReadInt32() == 1; int sessionExpires = 0; @@ -127,7 +137,9 @@ namespace TLSharp.Core TimeOffset = timeOffset, SessionExpires = sessionExpires, User = user, - SessionUserId = sessionUserId + SessionUserId = sessionUserId, + ServerAddress = serverAddress, + Port = port }; } } @@ -147,7 +159,13 @@ namespace TLSharp.Core } catch { - session = new Session(store) { Id = GenerateRandomUlong(), SessionUserId = sessionUserId }; + session = new Session(store) + { + Id = GenerateRandomUlong(), + SessionUserId = sessionUserId, + ServerAddress = defaultConnectionAddress, + Port = defaultConnectionPort + }; } return session; diff --git a/TLSharp.Core/TelegramClient.cs b/TLSharp.Core/TelegramClient.cs index 44e1252..6a65392 100644 --- a/TLSharp.Core/TelegramClient.cs +++ b/TLSharp.Core/TelegramClient.cs @@ -8,6 +8,7 @@ using TLSharp.Core.MTProto; using TLSharp.Core.MTProto.Crypto; using TLSharp.Core.Network; using TLSharp.Core.Requests; +using MD5 = System.Security.Cryptography.MD5; namespace TLSharp.Core { @@ -15,17 +16,18 @@ namespace TLSharp.Core { private MtProtoSender _sender; private AuthKey _key; - private readonly TcpTransport _transport; + private TcpTransport _transport; private string _apiHash = "a2514f96431a228e4b9ee473f6c51945"; private int _apiId = 19474; private Session _session; + private List dcOptions; - public User loggedUser { get { return _session.User; } } + public User loggedUser { get { return _session.User; } } - public List chats; - public List users; + public List chats; + public List users; - public TelegramClient(ISessionStore store, string sessionUserId) + public TelegramClient(ISessionStore store, string sessionUserId) { if (_apiId == 0) throw new InvalidOperationException("Your API_ID is invalid. Do a configuration first https://github.com/sochix/TLSharp#quick-configuration"); @@ -33,30 +35,49 @@ namespace TLSharp.Core if (string.IsNullOrEmpty(_apiHash)) throw new InvalidOperationException("Your API_ID is invalid. Do a configuration first https://github.com/sochix/TLSharp#quick-configuration"); - _transport = new TcpTransport(); _session = Session.TryLoadOrCreateNew(store, sessionUserId); + _transport = new TcpTransport(_session.ServerAddress, _session.Port); } - public async Task Connect() + + public async Task Connect(bool reconnect = false) { - if (_session.AuthKey == null) + if (_session.AuthKey == null || reconnect) { var result = await Authenticator.DoAuthentication(_transport); _session.AuthKey = result.AuthKey; _session.TimeOffset = result.TimeOffset; } - _sender = new MtProtoSender(_transport, _session); - var request = new InitConnectionRequest(_apiId); + if (!reconnect) + { + var request = new InitConnectionRequest(_apiId); - await _sender.Send(request); - await _sender.Recieve(request); + await _sender.Send(request); + await _sender.Recieve(request); + + dcOptions = request.ConfigConstructor.dc_options; + } return true; } + private async Task ReconnectToDc(int dcId) + { + if (dcOptions == null || !dcOptions.Any()) + throw new InvalidOperationException($"Can't reconnect. Establish initial connection first."); + + var dc = dcOptions.Cast().First(d => d.id == dcId); + + _transport = new TcpTransport(dc.ip_address, dc.port); + _session.ServerAddress = dc.ip_address; + _session.Port = dc.port; + + await Connect(true); + } + public bool IsUserAuthorized() { return _session.User != null; @@ -76,9 +97,34 @@ namespace TLSharp.Core public async Task SendCodeRequest(string phoneNumber) { - var request = new AuthSendCodeRequest(phoneNumber, 5, _apiId, _apiHash, "en"); - await _sender.Send(request); - await _sender.Recieve(request); + var completed = false; + + AuthSendCodeRequest request = null; + + while (!completed) + { + request = new AuthSendCodeRequest(phoneNumber, 5, _apiId, _apiHash, "en"); + try + { + + + await _sender.Send(request); + await _sender.Recieve(request); + + completed = true; + } + catch (InvalidOperationException ex) + { + if (ex.Message.StartsWith("Your phone number registered to") && ex.Data["dcId"] != null) + { + await ReconnectToDc((int) ex.Data["dcId"]); + } + else + { + throw; + } + } + } return request._phoneCodeHash; } @@ -96,101 +142,100 @@ namespace TLSharp.Core return request.user; } + - + public async Task UploadFile(string name, byte[] data) + { + var partSize = 65536; - public async Task UploadFile(string name, byte[] data) - { - var partSize = 65536; + var file_id = DateTime.Now.Ticks; - var file_id = DateTime.Now.Ticks; + var partedData = new Dictionary(); + var parts = Convert.ToInt32(Math.Ceiling(Convert.ToDouble(data.Length) / Convert.ToDouble(partSize))); + var remainBytes = data.Length; + for (int i = 0; i < parts; i++) + { + partedData.Add(i, data + .Skip(i * partSize) + .Take(remainBytes < partSize ? remainBytes : partSize) + .ToArray()); - var partedData = new Dictionary(); - var parts = Convert.ToInt32(Math.Ceiling(Convert.ToDouble(data.Length) / Convert.ToDouble(partSize))); - var remainBytes = data.Length; - for (int i = 0; i < parts; i++) - { - partedData.Add(i, data - .Skip(i * partSize) - .Take(remainBytes < partSize ? remainBytes : partSize) - .ToArray()); + remainBytes -= partSize; + } - remainBytes -= partSize; - } - - for(int i =0;i SendMediaMessage(InputPeer inputPeer, InputMedia inputMedia) - { - var request = new Message_SendMediaRequest(inputPeer, inputMedia); - await _sender.Send(request); - await _sender.Recieve(request); + public async Task SendMediaMessage(InputPeer inputPeer, InputMedia inputMedia) + { + var request = new Message_SendMediaRequest(inputPeer, inputMedia); + await _sender.Send(request); + await _sender.Recieve(request); - return request.StatedMessage; - } + return request.StatedMessage; + } public async Task ImportContact(string phoneNumber) { - var request = new ImportContactRequest(new InputPhoneContactConstructor(0, phoneNumber, "My Test Name", string.Empty)); + var request = new ImportContactRequest(new InputPhoneContactConstructor(0, phoneNumber, "My Test Name", String.Empty)); await _sender.Send(request); await _sender.Recieve(request); var importedUser = request.users.FirstOrDefault(); - return importedUser == null ? (int?) null : ((UserContactConstructor) importedUser).id; + return importedUser == null ? (int?)null : ((UserContactConstructor)importedUser).id; } public async Task SendMessage(int id, string message) { - var request = new SendMessageRequest(new InputPeerContactConstructor(id), message ); + var request = new SendMessageRequest(new InputPeerContactConstructor(id), message); await _sender.Send(request); await _sender.Recieve(request); } - public async Task LoadChatsAndUsers(int offset, int max_id, int limit) - { - // GetDialogs - var request = new GetDialogsRequest(offset, max_id, limit); - await _sender.Send(request); - await _sender.Recieve(request); + public async Task LoadChatsAndUsers(int offset, int max_id, int limit) + { + // GetDialogs + var request = new GetDialogsRequest(offset, max_id, limit); + await _sender.Send(request); + await _sender.Recieve(request); - chats = request.chats; - users = request.users; - } + chats = request.chats; + users = request.users; + } - public async Task> GetHistory(int user_id, int offset, int max_id, int limit) - { - var request = new GetHistoryRequest(new InputPeerContactConstructor(user_id), offset, max_id, limit); - await _sender.Send(request); - await _sender.Recieve(request); + public async Task> GetHistory(int user_id, int offset, int max_id, int limit) + { + var request = new GetHistoryRequest(new InputPeerContactConstructor(user_id), offset, max_id, limit); + await _sender.Send(request); + await _sender.Recieve(request); - return request.messages; - } - } + return request.messages; + } + } } diff --git a/TLSharp.Tests/NotificatioClientTests.cs b/TLSharp.Tests/NotificatioClientTests.cs index 800ba47..c406f58 100644 --- a/TLSharp.Tests/NotificatioClientTests.cs +++ b/TLSharp.Tests/NotificatioClientTests.cs @@ -12,9 +12,9 @@ namespace TLSharp.Tests [TestClass] public class NotificatioClientTests { - public string NumberToAuthenticate { get; set; } - public string RegisteredNumber { get; set; } - public string UnregisteredNumber { get; set; } + private string NumberToSendMessage { get; set; } + + private string NumberToAuthenticate { get; set; } [TestInitialize] public void Init() @@ -24,19 +24,9 @@ namespace TLSharp.Tests if (string.IsNullOrEmpty(NumberToAuthenticate)) throw new InvalidOperationException("NumberToAuthenticate is null"); - RegisteredNumber = ConfigurationManager.AppSettings["registeredNumber"]; - if (string.IsNullOrEmpty(RegisteredNumber)) - throw new InvalidOperationException("RegisteredNumber is null"); - - UnregisteredNumber = ConfigurationManager.AppSettings["unregisteredNumber"]; - if (string.IsNullOrEmpty(UnregisteredNumber)) - throw new InvalidOperationException("UnregisteredNumber is null"); - - if (NumberToAuthenticate == UnregisteredNumber) - throw new InvalidOperationException("NumberToAuthenticate eqauls UnregisteredNumber but shouldn't!"); - - if (RegisteredNumber == UnregisteredNumber) - throw new InvalidOperationException("RegisteredNumber eqauls UnregisteredNumber but shouldn't!"); + NumberToSendMessage = ConfigurationManager.AppSettings["numberToSendMessage"]; + if (string.IsNullOrEmpty(NumberToSendMessage)) + throw new InvalidOperationException("NumberToSendMessage is null"); } [TestMethod] @@ -62,20 +52,8 @@ namespace TLSharp.Tests var client = new Core.TelegramClient(store, "session"); await client.Connect(); - var phoneList = new string[] - { - RegisteredNumber, - NumberToAuthenticate - }; - - var rand = new Random(); - foreach (var phone in phoneList) - { - var result = await client.IsPhoneRegistered(phone); - Thread.Sleep(rand.Next(9) * 1000); - if (result) - Console.WriteLine($"{phone} - OK"); - } + var result = await client.IsPhoneRegistered(NumberToAuthenticate); + Assert.IsTrue(result); } [TestMethod] @@ -84,16 +62,15 @@ namespace TLSharp.Tests // User should be already authenticated! var store = new FileSessionStore(); - var client = new Core.TelegramClient(store, "session"); + var client = new TelegramClient(store, "session"); await client.Connect(); Assert.IsTrue(client.IsUserAuthorized()); - var res = await client.ImportContact(RegisteredNumber); + var res = await client.ImportContact(NumberToSendMessage); Assert.IsNotNull(res); - } [TestMethod] @@ -102,12 +79,12 @@ namespace TLSharp.Tests // User should be already authenticated! var store = new FileSessionStore(); - var client = new Core.TelegramClient(store, "session"); + var client = new TelegramClient(store, "session"); await client.Connect(); Assert.IsTrue(client.IsUserAuthorized()); - var res = await client.ImportContact(RegisteredNumber); + var res = await client.ImportContact(NumberToSendMessage); Assert.IsNotNull(res); @@ -118,7 +95,7 @@ namespace TLSharp.Tests public async Task TestConnection() { var store = new FakeSessionStore(); - var client = new Core.TelegramClient(store, ""); + var client = new TelegramClient(store, ""); Assert.IsTrue(await client.Connect()); } @@ -126,7 +103,7 @@ namespace TLSharp.Tests [TestMethod] public async Task AuthenticationWorks() { - using (var transport = new TcpTransport()) + using (var transport = new TcpTransport("91.108.56.165", 443)) { var authKey = await Authenticator.DoAuthentication(transport); diff --git a/TLSharp.Tests/app.config b/TLSharp.Tests/app.config index dfa3046..2ef5789 100644 --- a/TLSharp.Tests/app.config +++ b/TLSharp.Tests/app.config @@ -2,7 +2,6 @@ - - +