Exemple #1
0
        public ulong GetNextMessageId()
        {
            // Documentation says that message id should be unixtime * 2^32.
            // But the real world calculations in other client software looking very weird.
            // Have no idea how it is actually calculated.
            ulong messageId = UnixTimeUtils.GetCurrentUnixTimestampMilliseconds();

            messageId = (messageId * 4294967 + (messageId * 296 / 1000)) & X4Mask;
            if (messageId <= _lastMessageId)
            {
                messageId = _lastMessageId + 4;
            }
            _lastMessageId = messageId;
            return(messageId);
        }
        protected override async Task HandleInternalAsync(IMessage responseMessage)
        {
            #region Notice of Ignored Error Message

            /* In certain cases, a server may notify a client that its incoming message was ignored for whatever reason.
             * Note that such a notification cannot be generated unless a message is correctly decoded by the server.
             *
             * bad_msg_notification#a7eff811 bad_msg_id:long bad_msg_seqno:int error_code:int = BadMsgNotification;
             * bad_server_salt#edab447b bad_msg_id:long bad_msg_seqno:int error_code:int new_server_salt:long = BadMsgNotification;
             *
             * Here, error_code can also take on the following values:
             *
             * 16: 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)
             * 17: 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)
             * 18: incorrect two lower order msg_id bits (the server expects client message msg_id to be divisible by 4)
             * 19: container msg_id is the same as msg_id of a previously received message (this must never happen)
             * 20: message too old, and it cannot be verified whether the server has received a message with this msg_id or not
             * 32: 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)
             * 33: 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)
             * 34: an even msg_seqno expected (irrelevant message), but odd received
             * 35: odd msg_seqno expected (relevant message), but even received
             * 48: 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)
             * 64: invalid container.
             * The intention is that error_code values are grouped (error_code >> 4): for example, the codes 0x40 - 0x4f correspond to errors in container decomposition.
             *
             * Notifications of an ignored message do not require acknowledgment (i.e., are irrelevant).
             *
             * Important: if server_salt has changed on the server or if client time is incorrect, any query will result in a notification in the above format.
             * The client must check that it has, in fact, recently sent a message with the specified msg_id, and if that is the case,
             * update its time correction value (the difference between the client’s and the server’s clocks) and the server salt based on msg_id
             * and the server_salt notification, so as to use these to (re)send future messages.
             * In the meantime, the original message (the one that caused the error message to be returned) must also be re-sent with a better msg_id and/or server_salt.
             *
             * In addition, the client can update the server_salt value used to send messages to the server,
             * based on the values of RPC responses or containers carrying an RPC response,
             * provided that this RPC response is actually a match for the query sent recently.
             * (If there is doubt, it is best not to update since there is risk of a replay attack).
             *
             * https://core.telegram.org/mtproto/service_messages_about_messages#notice-of-ignored-error-message
             */
            #endregion

            var response = (IBadMsgNotification)responseMessage.Body;

            var errorCode = (ErrorCode)response.ErrorCode;
            Console.WriteLine(string.Format("Bad message notification received with error code: {0} ({1}).", response.ErrorCode, errorCode));

            Console.WriteLine("Searching for bad message in the request manager...");

            IRequest request = _requestsManager.Get(response.BadMsgId);
            if (request == null)
            {
                Console.WriteLine(string.Format("Bad message (MsgId: 0x{0:X}) was NOT found. Ignored.", response.BadMsgId));
                return;
            }
            if (request.Message.Seqno != response.BadMsgSeqno)
            {
                Console.WriteLine(
                    string.Format(
                        "Bad message (MsgId: 0x{0:X}) was found, but message sequence number is not the same ({1}) as server expected ({2}). Ignored.",
                        response.BadMsgId,
                        request.Message.Seqno,
                        response.BadMsgSeqno));
                return;
            }

            var badServerSalt = response as BadServerSalt;
            if (badServerSalt != null)
            {
                if (errorCode != ErrorCode.IncorrectServerSalt)
                {
                    Console.WriteLine(string.Format("Error code must be '{0}' for a BadServerSalt notification, but found '{1}'.", ErrorCode.IncorrectServerSalt, errorCode));
                }

                Console.WriteLine(
                    string.Format(
                        "Bad server salt was in outgoing message (MsgId = 0x{0:X}, Seqno = {1}). Error code = {2}.",
                        badServerSalt.BadMsgId,
                        badServerSalt.BadMsgSeqno,
                        errorCode));

                Console.WriteLine("Setting new salt.");

                _connection.UpdateSalt(badServerSalt.NewServerSalt);

                Console.WriteLine("Resending bad message with the new salt.");

                await request.SendAsync();

                return;
            }

            var badMsgNotification = response as BadMsgNotification;
            if (badMsgNotification != null)
            {
                // TODO: implement.
                switch (errorCode)
                {
                case ErrorCode.MsgIdIsTooSmall:
                case ErrorCode.MsgIdIsTooBig:
                case ErrorCode.MsgIdDuplicate:
                case ErrorCode.MsgTooOld:
                case ErrorCode.MsgIdBadTwoLowBytes:
                    ulong time        = (ulong)(responseMessage.MsgId / 4294967296.0 * 1000);
                    ulong currentTime = UnixTimeUtils.GetCurrentUnixTimestampMilliseconds();
                    var   timeDelta   = ((long)time - (long)currentTime);
                    _connection.MessageIdGenerator.TimeDifference = timeDelta;
                    Disa.Framework.Utils.DebugPrint("Time drift set to: " + _connection.MessageIdGenerator.TimeDifference);
                    var newMessageId = _connection.MessageIdGenerator.GetNextMessageId();
                    var oldMessageId = request.Message.MsgId;
                    var message      = new Message(newMessageId, request.Message.Seqno, request.Message.Body);
                    request.UpdateMessage(message);
                    _requestsManager.Change(newMessageId, oldMessageId);
                    await request.SendAsync();

                    break;

                case ErrorCode.MsgSeqnoIsTooLow:
                case ErrorCode.MsgSeqnoIsTooBig:
                case ErrorCode.MsgSeqnoNotEven:
                case ErrorCode.MsgSeqnoNotOdd:
                case ErrorCode.IncorrectServerSalt:
                case ErrorCode.InvalidContainer:
                    throw new NotImplementedException();

                default:
                    throw new ArgumentOutOfRangeException();
                }
            }
        }