#import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import "MTBuffer.h" #import "MTInternalMessageParser.h" #import "MTMsgContainerMessage.h" #import "MTMessage.h" #import "MTBadMsgNotificationMessage.h" #import "MTMsgsAckMessage.h" #import "MTMsgDetailedInfoMessage.h" #import "MTNewSessionCreatedMessage.h" #import "MTPongMessage.h" #import "MTRpcResultMessage.h" #import #import "MTConnectionProbing.h" #import #import #import #import typedef enum { MTProtoStateAwaitingDatacenterScheme = 1, MTProtoStateAwaitingDatacenterAuthorization = 2, MTProtoStateAwaitingDatacenterAuthToken = 8, MTProtoStateAwaitingTimeFixAndSalts = 16, MTProtoStateAwaitingLostMessages = 32, MTProtoStateStopped = 64, MTProtoStatePaused = 128 } MTProtoState; static const NSUInteger MTMaxContainerSize = 3 * 1024; static const NSUInteger MTMaxUnacknowledgedMessageSize = 1 * 1024 * 1024; static const NSUInteger MTMaxUnacknowledgedMessageCount = 64; @implementation MTProtoConnectionState - (instancetype)initWithIsConnected:(bool)isConnected proxyAddress:(NSString *)proxyAddress proxyHasConnectionIssues:(bool)proxyHasConnectionIssues { self = [super init]; if (self != nil) { _isConnected = isConnected; _proxyAddress = proxyAddress; _proxyHasConnectionIssues = proxyHasConnectionIssues; } return self; } @end @interface MTProtoValidAuthInfo : NSObject @property (nonatomic, strong, readonly) MTDatacenterAuthInfo *authInfo; @property (nonatomic, readonly) MTDatacenterAuthInfoSelector selector; @end @implementation MTProtoValidAuthInfo - (instancetype)initWithAuthInfo:(MTDatacenterAuthInfo *)authInfo selector:(MTDatacenterAuthInfoSelector)selector { self = [super init]; if (self != nil) { _authInfo = authInfo; _selector = selector; } return self; } @end @interface MTProto () { NSMutableArray *_messageServices; MTProtoValidAuthInfo *_validAuthInfo; NSNumber *_awaitingAuthInfoForSelector; MTSessionInfo *_sessionInfo; MTTimeFixContext *_timeFixContext; MTTransport *_transport; int _mtState; bool _willRequestTransactionOnNextQueuePass; MTNetworkUsageCalculationInfo *_usageCalculationInfo; MTProtoConnectionState *_connectionState; bool _isProbing; MTMetaDisposable *_probingDisposable; NSNumber *_probingStatus; bool _isConnectionThrottled; MTTimer *_unthrottleConnectionTimer; } @end @implementation MTProto + (MTQueue *)managerQueue { static MTQueue *queue = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^ { queue = [[MTQueue alloc] initWithName:"org.mtproto.managerQueue"]; }); return queue; } - (instancetype)initWithContext:(MTContext *)context datacenterId:(NSInteger)datacenterId usageCalculationInfo:(MTNetworkUsageCalculationInfo *)usageCalculationInfo requiredAuthToken:(id)requiredAuthToken authTokenMasterDatacenterId:(NSInteger)authTokenMasterDatacenterId { #ifdef DEBUG NSAssert(context != nil, @"context should not be nil"); NSAssert(context.serialization != nil, @"context serialization should not be nil"); NSAssert(datacenterId != 0, @"datacenterId should not be 0"); #endif self = [super init]; if (self != nil) { _context = context; _datacenterId = datacenterId; _usageCalculationInfo = usageCalculationInfo; _apiEnvironment = context.apiEnvironment; _requiredAuthToken = requiredAuthToken; _authTokenMasterDatacenterId = authTokenMasterDatacenterId; [_context addChangeListener:self]; _messageServices = [[NSMutableArray alloc] init]; _sessionInfo = [[MTSessionInfo alloc] initWithRandomSessionIdAndContext:_context]; _shouldStayConnected = true; _mtState |= MTProtoStatePaused; [self setMtState:_mtState | MTProtoStatePaused]; } return self; } - (void)dealloc { MTTransport *transport = _transport; _transport.delegate = nil; _transport = nil; id probingDisposable = _probingDisposable; [[MTProto managerQueue] dispatchOnQueue:^ { [transport stop]; [probingDisposable dispose]; }]; } - (void)setUsageCalculationInfo:(MTNetworkUsageCalculationInfo *)usageCalculationInfo { [[MTProto managerQueue] dispatchOnQueue:^{ _usageCalculationInfo = usageCalculationInfo; [_transport setUsageCalculationInfo:usageCalculationInfo]; }]; } - (void)pause { [[MTProto managerQueue] dispatchOnQueue:^ { if ((_mtState & MTProtoStatePaused) == 0) { if (MTLogEnabled()) { MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p pause]", self, _context); } MTShortLog(@"[MTProto#%p@%p pause]", self, _context); _mtState |= MTProtoStatePaused; [self setMtState:_mtState | MTProtoStatePaused]; [self setTransport:nil keepTransportActive:false]; } }]; } - (void)resume { [[MTProto managerQueue] dispatchOnQueue:^ { if (_mtState & MTProtoStatePaused) { if (MTLogEnabled()) { MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p resume]", self, _context); } MTShortLog(@"[MTProto#%p@%p resume]", self, _context); [self setMtState:_mtState & (~MTProtoStatePaused)]; [self resetTransport]; [self requestTransportTransaction]; } }]; } - (void)stop { [[MTProto managerQueue] dispatchOnQueue:^ { if ((_mtState & MTProtoStateStopped) == 0) { [self setMtState:_mtState | MTProtoStateStopped]; [_context removeChangeListener:self]; if (_transport != nil) { _transport.delegate = nil; [_transport stop]; [self setTransport:nil keepTransportActive:false]; } } }]; } - (void)updateConnectionState { [[MTProto managerQueue] dispatchOnQueue:^ { if (_transport != nil) [_transport updateConnectionState]; else { id delegate = _delegate; if ([delegate respondsToSelector:@selector(mtProtoNetworkAvailabilityChanged:isNetworkAvailable:)]) [delegate mtProtoNetworkAvailabilityChanged:self isNetworkAvailable:false]; if ([delegate respondsToSelector:@selector(mtProtoConnectionStateChanged:state:)]) [delegate mtProtoConnectionStateChanged:self state:nil]; if ([delegate respondsToSelector:@selector(mtProtoConnectionContextUpdateStateChanged:isUpdatingConnectionContext:)]) [delegate mtProtoConnectionContextUpdateStateChanged:self isUpdatingConnectionContext:false]; } }]; } - (void)setTransport:(MTTransport *)transport keepTransportActive:(bool)keepTransportActive { [[MTProto managerQueue] dispatchOnQueue:^ { if (MTLogEnabled()) { MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p changing transport %@#%p to %@#%p]", self, _context, [_transport class] == nil ? @"" : NSStringFromClass([_transport class]), _transport, [transport class] == nil ? @"" : NSStringFromClass([transport class]), transport); } [self allTransactionsMayHaveFailed]; MTTransport *previousTransport = _transport; [_transport activeTransactionIds:^(NSArray *transactionIds) { [self transportTransactionsMayHaveFailed:previousTransport transactionIds:transactionIds]; }]; _timeFixContext = nil; if (_transport != nil) [self removeMessageService:_transport]; _transport = transport; if (!keepTransportActive) { [previousTransport stop]; } if (_transport != nil) [self addMessageService:_transport]; [self updateConnectionState]; }]; } - (void)resetTransport { [[MTProto managerQueue] dispatchOnQueue:^ { if (_mtState & MTProtoStateStopped) return; if (_transport != nil) { _transport.delegate = nil; [_transport stop]; [self setTransport:nil keepTransportActive:false]; } if (_tempConnectionForReuse != nil) { MTTransport *tempTransport = _tempConnectionForReuse; _tempConnectionForReuse = nil; tempTransport.delegate = self; [self setTransport:tempTransport keepTransportActive:false]; } else { NSArray *transportSchemes = [_context transportSchemesForDatacenterWithId:_datacenterId media:_media enforceMedia:_enforceMedia isProxy:_apiEnvironment.socksProxySettings != nil]; if (transportSchemes.count == 0) { if ((_mtState & MTProtoStateAwaitingDatacenterScheme) == 0) { [self setMtState:_mtState | MTProtoStateAwaitingDatacenterScheme]; [_context transportSchemeForDatacenterWithIdRequired:_datacenterId media:_media]; } } else if (_requiredAuthToken != nil && !_useUnauthorizedMode && ![_requiredAuthToken isEqual:[_context authTokenForDatacenterWithId:_datacenterId]]) { if ((_mtState & MTProtoStateAwaitingDatacenterAuthToken) == 0) { [self setMtState:_mtState | MTProtoStateAwaitingDatacenterAuthToken]; [_context authTokenForDatacenterWithIdRequired:_datacenterId authToken:_requiredAuthToken masterDatacenterId:_authTokenMasterDatacenterId]; } } else { assert(transportSchemes.count != 0); MTTransport *transport = [[MTTcpTransport alloc] initWithDelegate:self context:_context datacenterId:_datacenterId schemes:transportSchemes proxySettings:_context.apiEnvironment.socksProxySettings usageCalculationInfo:_usageCalculationInfo getLogPrefix:_getLogPrefix]; [self setTransport:transport keepTransportActive:false]; } } }]; } - (void)resetSessionInfo { [[MTProto managerQueue] dispatchOnQueue:^ { if (MTLogEnabled()) { MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p resetting session]", self, _context); } MTShortLog(@"[MTProto#%p@%p resetting session]", self, _context); _sessionInfo = [[MTSessionInfo alloc] initWithRandomSessionIdAndContext:_context]; _timeFixContext = nil; for (NSInteger i = (NSInteger)_messageServices.count - 1; i >= 0; i--) { id messageService = _messageServices[(NSUInteger)i]; if ([messageService respondsToSelector:@selector(mtProtoDidChangeSession:)]) [messageService mtProtoDidChangeSession:self]; } [self resetTransport]; [self requestTransportTransaction]; }]; } - (void)finalizeSession { } - (void)requestTimeResync { [[MTProto managerQueue] dispatchOnQueue:^ { bool alreadySyncing = false; for (NSInteger i = (NSInteger)_messageServices.count - 1; i >= 0; i--) { id messageService = _messageServices[(NSUInteger)i]; if ([messageService isKindOfClass:[MTTimeSyncMessageService class]]) { alreadySyncing = true; break; } } if (!alreadySyncing) { if (MTLogEnabled()) { MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p begin time sync]", self, _context); } MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p begin time sync]", self, _context); MTTimeSyncMessageService *timeSyncService = [[MTTimeSyncMessageService alloc] init]; timeSyncService.delegate = self; [self addMessageService:timeSyncService]; } }]; } - (void)setMtState:(int)mtState { bool wasPerformingServiceTasks = _mtState & MTProtoStateAwaitingTimeFixAndSalts; _mtState = mtState; bool performingServiceTasks = _mtState & MTProtoStateAwaitingTimeFixAndSalts; if (performingServiceTasks != wasPerformingServiceTasks) { bool haveResendMessagesPending = false; for (id messageService in _messageServices) { if ([messageService isKindOfClass:[MTResendMessageService class]]) { haveResendMessagesPending = true; break; } } if (MTLogEnabled()) { MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p service tasks state: %d, resend: %s]", self, _context, _mtState, haveResendMessagesPending ? "yes" : "no"); } MTShortLog(@"[MTProto#%p@%p service tasks state: %d, resend: %s]", self, _context, _mtState, haveResendMessagesPending ? "yes" : "no"); for (id messageService in _messageServices) { if ([messageService respondsToSelector:@selector(mtProtoServiceTasksStateChanged:isPerformingServiceTasks:)]) [messageService mtProtoServiceTasksStateChanged:self isPerformingServiceTasks:performingServiceTasks || haveResendMessagesPending]; } id delegate = _delegate; if ([delegate respondsToSelector:@selector(mtProtoServiceTasksStateChanged:isPerformingServiceTasks:)]) [delegate mtProtoServiceTasksStateChanged:self isPerformingServiceTasks:performingServiceTasks || haveResendMessagesPending]; } } - (void)addMessageService:(id)messageService { if ([messageService respondsToSelector:@selector(mtProtoWillAddService:)]) [messageService mtProtoWillAddService:self]; [[MTProto managerQueue] dispatchOnQueue:^ { bool notifyAboutServiceTask = false; if ([messageService isKindOfClass:[MTResendMessageService class]]) { notifyAboutServiceTask = true; for (id currentService in _messageServices) { if ([currentService isKindOfClass:[MTResendMessageService class]]) { notifyAboutServiceTask = false; break; } } } if (![_messageServices containsObject:messageService]) { [_messageServices addObject:messageService]; if ([messageService respondsToSelector:@selector(mtProtoDidAddService:)]) [messageService mtProtoDidAddService:self]; } if (notifyAboutServiceTask) { if (MTLogEnabled()) { MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p service tasks state: %d, resend: %s]", self, _context, _mtState, "yes"); } MTShortLog(@"[MTProto#%p@%p service tasks state: %d, resend: %s]", self, _context, _mtState, "yes"); for (id messageService in _messageServices) { if ([messageService respondsToSelector:@selector(mtProtoServiceTasksStateChanged:isPerformingServiceTasks:)]) [messageService mtProtoServiceTasksStateChanged:self isPerformingServiceTasks:true]; } id delegate = _delegate; if ([delegate respondsToSelector:@selector(mtProtoServiceTasksStateChanged:isPerformingServiceTasks:)]) [delegate mtProtoServiceTasksStateChanged:self isPerformingServiceTasks:true]; } }]; } - (void)removeMessageService:(id)messageService { if (messageService == nil) return; [[MTProto managerQueue] dispatchOnQueue:^ { if ([_messageServices containsObject:messageService]) { [_messageServices removeObject:messageService]; if ([messageService respondsToSelector:@selector(mtProtoDidRemoveService:)]) [messageService mtProtoDidRemoveService:self]; bool notifyAboutServiceTask = false; if ([messageService isKindOfClass:[MTResendMessageService class]]) { notifyAboutServiceTask = true; for (id currentService in _messageServices) { if ([currentService isKindOfClass:[MTResendMessageService class]]) { notifyAboutServiceTask = false; break; } } } if (notifyAboutServiceTask) { bool performingServiceTasks = _mtState & MTProtoStateAwaitingTimeFixAndSalts; if (!performingServiceTasks) { if (MTLogEnabled()) { MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p service tasks state: %d, resend: %s]", self, _context, _mtState, "no"); } MTShortLog(@"[MTProto#%p@%p service tasks state: %d, resend: %s]", self, _context, _mtState, "no"); for (id messageService in _messageServices) { if ([messageService respondsToSelector:@selector(mtProtoServiceTasksStateChanged:isPerformingServiceTasks:)]) [messageService mtProtoServiceTasksStateChanged:self isPerformingServiceTasks:false]; } id delegate = _delegate; if ([delegate respondsToSelector:@selector(mtProtoServiceTasksStateChanged:isPerformingServiceTasks:)]) [delegate mtProtoServiceTasksStateChanged:self isPerformingServiceTasks:false]; } } } }]; } - (MTQueue *)messageServiceQueue { return [MTProto managerQueue]; } - (void)initiateTimeSync { [[MTProto managerQueue] dispatchOnQueue:^ { if ((_mtState & MTProtoStateAwaitingTimeFixAndSalts) == 0) { [self setMtState:_mtState | MTProtoStateAwaitingTimeFixAndSalts]; [self requestTimeResync]; } }]; } - (void)completeTimeSync { [[MTProto managerQueue] dispatchOnQueue:^ { if ((_mtState & MTProtoStateAwaitingTimeFixAndSalts) != 0) { [self setMtState:_mtState & (~MTProtoStateAwaitingTimeFixAndSalts)]; for (NSInteger i = (NSInteger)_messageServices.count - 1; i >= 0; i--) { id messageService = _messageServices[(NSUInteger)i]; if ([messageService isKindOfClass:[MTTimeSyncMessageService class]]) { ((MTTimeSyncMessageService *)messageService).delegate = nil; [self removeMessageService:messageService]; } } } }]; } - (void)requestMessageWithId:(int64_t)messageId { bool alreadyRequestingThisMessage = false; for (id messageService in _messageServices) { if ([messageService isKindOfClass:[MTResendMessageService class]]) { if (((MTResendMessageService *)messageService).messageId == messageId) { alreadyRequestingThisMessage = true; break; } } } if (!alreadyRequestingThisMessage && ![_sessionInfo messageProcessed:messageId]) { MTResendMessageService *resendService = [[MTResendMessageService alloc] initWithMessageId:messageId]; resendService.delegate = self; [self addMessageService:resendService]; } } - (void)resendMessageServiceCompleted:(MTResendMessageService *)resendService { [[MTProto managerQueue] dispatchOnQueue:^ { resendService.delegate = nil; [self removeMessageService:resendService]; }]; } - (void)requestTransportTransaction { [[MTProto managerQueue] dispatchOnQueue:^ { if (!_willRequestTransactionOnNextQueuePass) { _willRequestTransactionOnNextQueuePass = true; dispatch_async([MTProto managerQueue].nativeQueue, ^ { _willRequestTransactionOnNextQueuePass = false; if ([self isStopped] || [self isPaused] || _isConnectionThrottled) { return; } if (_transport == nil) [self resetTransport]; [_transport setDelegateNeedsTransaction]; }); } }]; } - (void)requestSecureTransportReset { [[MTProto managerQueue] dispatchOnQueue:^ { if ([self isStopped]) return; if (_transport != nil) [_transport reset]; }]; } - (void)simulateDisconnection { [[MTProto managerQueue] dispatchOnQueue:^ { if (_transport != nil) { [_transport simulateDisconnection]; } }]; } - (MTTransport *)takeConnectionForReusing { __block MTTransport *result = nil; [[MTProto managerQueue] dispatchOnQueue:^{ result = _transport; [self setTransport:nil keepTransportActive:true]; } synchronous:true]; return result; } - (bool)canAskForTransactions { return (_mtState & (MTProtoStateAwaitingDatacenterScheme | MTProtoStateAwaitingDatacenterAuthorization | MTProtoStateAwaitingDatacenterAuthToken | MTProtoStateAwaitingTimeFixAndSalts | MTProtoStateStopped)) == 0; } - (bool)canAskForServiceTransactions { return (_mtState & (MTProtoStateAwaitingDatacenterScheme | MTProtoStateAwaitingDatacenterAuthorization | MTProtoStateAwaitingDatacenterAuthToken | MTProtoStateStopped)) == 0; } - (bool)timeFixOrSaltsMissing { return _mtState & MTProtoStateAwaitingTimeFixAndSalts; } - (bool)isStopped { return (_mtState & MTProtoStateStopped) != 0; } - (bool)isPaused { return (_mtState & MTProtoStatePaused) != 0; } - (void)transportNetworkAvailabilityChanged:(MTTransport *)transport isNetworkAvailable:(bool)isNetworkAvailable { [[MTProto managerQueue] dispatchOnQueue:^ { if (transport != _transport) return; if (MTLogEnabled()) { MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p network state: %s]", self, _context, isNetworkAvailable ? "available" : "waiting"); } for (id messageService in _messageServices) { if ([messageService respondsToSelector:@selector(mtProtoNetworkAvailabilityChanged:isNetworkAvailable:)]) [messageService mtProtoNetworkAvailabilityChanged:self isNetworkAvailable:isNetworkAvailable]; } id delegate = _delegate; if ([delegate respondsToSelector:@selector(mtProtoNetworkAvailabilityChanged:isNetworkAvailable:)]) [delegate mtProtoNetworkAvailabilityChanged:self isNetworkAvailable:isNetworkAvailable]; }]; } - (void)transportConnectionFailed:(MTTransport *)transport scheme:(MTTransportScheme *)scheme { [[MTProto managerQueue] dispatchOnQueue:^{ if (transport != _transport) { return; } if (_useUnauthorizedMode) { return; } [_context reportTransportSchemeFailureForDatacenterId:_datacenterId transportScheme:scheme]; }]; } - (void)transportConnectionStateChanged:(MTTransport *)transport isConnected:(bool)isConnected proxySettings:(MTSocksProxySettings *)proxySettings { [[MTProto managerQueue] dispatchOnQueue:^ { if (transport != _transport) return; if (MTLogEnabled()) { MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p transport #%p connection state: %s]", self, _context, transport, isConnected ? "connected" : "connecting"); } for (id messageService in _messageServices) { if ([messageService respondsToSelector:@selector(mtProtoConnectionStateChanged:isConnected:)]) [messageService mtProtoConnectionStateChanged:self isConnected:isConnected]; } MTProtoConnectionState *connectionState = [[MTProtoConnectionState alloc] initWithIsConnected:isConnected proxyAddress:proxySettings.ip proxyHasConnectionIssues:[_probingStatus boolValue]]; _connectionState = connectionState; id delegate = _delegate; if ([delegate respondsToSelector:@selector(mtProtoConnectionStateChanged:state:)]) { [delegate mtProtoConnectionStateChanged:self state:connectionState]; } }]; } - (void)transportConnectionContextUpdateStateChanged:(MTTransport *)transport isUpdatingConnectionContext:(bool)isUpdatingConnectionContext { [[MTProto managerQueue] dispatchOnQueue:^ { if (transport != _transport) return; if (MTLogEnabled()) { MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p connection context update state: %s]", self, _context, isUpdatingConnectionContext ? "updating" : "up to date"); } for (id messageService in _messageServices) { if ([messageService respondsToSelector:@selector(mtProtoConnectionContextUpdateStateChanged:isUpdatingConnectionContext:)]) [messageService mtProtoConnectionContextUpdateStateChanged:self isUpdatingConnectionContext:isUpdatingConnectionContext]; } id delegate = _delegate; if ([delegate respondsToSelector:@selector(mtProtoConnectionContextUpdateStateChanged:isUpdatingConnectionContext:)]) [delegate mtProtoConnectionContextUpdateStateChanged:self isUpdatingConnectionContext:isUpdatingConnectionContext]; }]; } - (void)transportConnectionProblemsStatusChanged:(MTTransport *)transport scheme:(MTTransportScheme *)scheme hasConnectionProblems:(bool)hasConnectionProblems isProbablyHttp:(bool)isProbablyHttp { [[MTProto managerQueue] dispatchOnQueue:^ { if (_transport != transport) { return; } if (_useUnauthorizedMode) { #if DEBUG #else return; #endif } if (hasConnectionProblems) { [_context reportTransportSchemeFailureForDatacenterId:_datacenterId transportScheme:scheme]; [_context invalidateTransportSchemeForDatacenterId:_datacenterId transportScheme:scheme isProbablyHttp:isProbablyHttp media:_media]; } else { [_context revalidateTransportSchemeForDatacenterId:_datacenterId transportScheme:scheme media:_media]; } if (!hasConnectionProblems || transport.proxySettings == nil || !_checkForProxyConnectionIssues) { if (_isProbing) { _isProbing = false; [_probingDisposable setDisposable:nil]; if (_probingStatus != nil) { _probingStatus = nil; [self _updateConnectionIssuesStatus:false]; } } } else { if (!_isProbing) { _isProbing = true; __weak MTProto *weakSelf = self; MTSignal *checkSignal = [[MTConnectionProbing probeProxyWithContext:_context datacenterId:_datacenterId settings:transport.proxySettings] delay:5.0 onQueue:[MTQueue concurrentDefaultQueue]]; checkSignal = [[checkSignal then:[[MTSignal complete] delay:20.0 onQueue:[MTQueue concurrentDefaultQueue]]] restart]; [_probingDisposable setDisposable:[checkSignal startWithNext:^(NSNumber *next) { [[MTProto managerQueue] dispatchOnQueue:^{ __strong MTProto *strongSelf = weakSelf; if (strongSelf == nil) { return; } if (strongSelf->_isProbing) { strongSelf->_probingStatus = next; [strongSelf _updateConnectionIssuesStatus:[strongSelf->_probingStatus boolValue]]; } }]; }]]; } } }]; } - (void)_updateConnectionIssuesStatus:(bool)value { if (_connectionState != nil) { _connectionState = [[MTProtoConnectionState alloc] initWithIsConnected:_connectionState.isConnected proxyAddress:_connectionState.proxyAddress proxyHasConnectionIssues:value]; id delegate = _delegate; if ([delegate respondsToSelector:@selector(mtProtoConnectionStateChanged:state:)]) { [delegate mtProtoConnectionStateChanged:self state:_connectionState]; } } } - (NSString *)outgoingMessageDescription:(MTOutgoingMessage *)message messageId:(int64_t)messageId messageSeqNo:(int32_t)messageSeqNo { return [[NSString alloc] initWithFormat:@"%@%@ (%" PRId64 "/%" PRId32 ")", message.metadata, message.additionalDebugDescription != nil ? message.additionalDebugDescription : @"", message.messageId == 0 ? messageId : message.messageId, message.messageSeqNo == 0 ? message.messageSeqNo : messageSeqNo]; } - (NSString *)outgoingShortMessageDescription:(MTOutgoingMessage *)message messageId:(int64_t)messageId messageSeqNo:(int32_t)messageSeqNo { return [[NSString alloc] initWithFormat:@"%@ (%" PRId64 "/%" PRId32 ")", message.shortMetadata, message.messageId == 0 ? messageId : message.messageId, message.messageSeqNo == 0 ? message.messageSeqNo : messageSeqNo]; } - (NSString *)incomingMessageDescription:(MTIncomingMessage *)message { return [[NSString alloc] initWithFormat:@"%@ (%" PRId64", %" PRId64"/%" PRId64")", message.body, message.messageId, message.authKeyId, message.sessionId]; } - (MTDatacenterAuthKey *)getAuthKeyForCurrentScheme:(MTTransportScheme *)scheme createIfNeeded:(bool)createIfNeeded authInfoSelector:(MTDatacenterAuthInfoSelector *)authInfoSelector { if (_useExplicitAuthKey) { MTDatacenterAuthInfoSelector selector = MTDatacenterAuthInfoSelectorEphemeralMain; if (authInfoSelector != nil) { *authInfoSelector = selector; } if (_validAuthInfo != nil && _validAuthInfo.selector == selector) { return [[MTDatacenterAuthKey alloc] initWithAuthKey:_validAuthInfo.authInfo.authKey authKeyId:_validAuthInfo.authInfo.authKeyId validUntilTimestamp:_validAuthInfo.authInfo.validUntilTimestamp notBound:false]; } MTDatacenterAuthInfo *authInfo = [[MTDatacenterAuthInfo alloc] initWithAuthKey:_useExplicitAuthKey.authKey authKeyId:_useExplicitAuthKey.authKeyId validUntilTimestamp:_useExplicitAuthKey.validUntilTimestamp saltSet:@[[[MTDatacenterSaltInfo alloc] initWithSalt:0 firstValidMessageId:0 lastValidMessageId:0]] authKeyAttributes:nil]; _validAuthInfo = [[MTProtoValidAuthInfo alloc] initWithAuthInfo:authInfo selector:selector]; return [[MTDatacenterAuthKey alloc] initWithAuthKey:_validAuthInfo.authInfo.authKey authKeyId:_validAuthInfo.authInfo.authKeyId validUntilTimestamp:_validAuthInfo.authInfo.validUntilTimestamp notBound:false]; } else { MTDatacenterAuthInfoSelector selector = MTDatacenterAuthInfoSelectorPersistent; if (_cdn) { selector = MTDatacenterAuthInfoSelectorPersistent; } else { if (_useTempAuthKeys) { if (scheme.address.preferForMedia) { selector = MTDatacenterAuthInfoSelectorEphemeralMedia; } else { selector = MTDatacenterAuthInfoSelectorEphemeralMain; } } else { selector = MTDatacenterAuthInfoSelectorPersistent; } } if (authInfoSelector != nil) { *authInfoSelector = selector; } if (_validAuthInfo != nil && _validAuthInfo.selector == selector) { return [[MTDatacenterAuthKey alloc] initWithAuthKey:_validAuthInfo.authInfo.authKey authKeyId:_validAuthInfo.authInfo.authKeyId validUntilTimestamp:_validAuthInfo.authInfo.validUntilTimestamp notBound:false]; } else { _validAuthInfo = nil; if (createIfNeeded) { MTDatacenterAuthInfo *authInfo = [_context authInfoForDatacenterWithId:_datacenterId selector:selector]; if (authInfo != nil) { _validAuthInfo = [[MTProtoValidAuthInfo alloc] initWithAuthInfo:authInfo selector:selector]; return [[MTDatacenterAuthKey alloc] initWithAuthKey:_validAuthInfo.authInfo.authKey authKeyId:_validAuthInfo.authInfo.authKeyId validUntilTimestamp:_validAuthInfo.authInfo.validUntilTimestamp notBound:false]; } else { [_context performBatchUpdates:^{ [_context updateAuthInfoForDatacenterWithId:_datacenterId authInfo:nil selector:selector]; [_context authInfoForDatacenterWithIdRequired:_datacenterId isCdn:_cdn selector:selector allowUnboundEphemeralKeys:_allowUnboundEphemeralKeys]; }]; _mtState |= MTProtoStateAwaitingDatacenterAuthorization; _awaitingAuthInfoForSelector = @(selector); return nil; } } else { return nil; } } } } - (void)transportReadyForTransaction:(MTTransport *)transport scheme:(MTTransportScheme *)scheme transportSpecificTransaction:(MTMessageTransaction *)transportSpecificTransaction forceConfirmations:(bool)forceConfirmations transactionReady:(void (^)(NSArray *))transactionReady { [[MTProto managerQueue] dispatchOnQueue:^ { if (_transport != transport) { if (transactionReady) transactionReady(nil); return; } if (!([self canAskForServiceTransactions] || [self canAskForTransactions])) { if (transactionReady) { transactionReady(nil); } return; } MTDatacenterAuthKey *authKey = nil; MTDatacenterAuthInfoSelector authInfoSelector = MTDatacenterAuthInfoSelectorPersistent; if (!_useUnauthorizedMode) { authKey = [self getAuthKeyForCurrentScheme:scheme createIfNeeded:true authInfoSelector:&authInfoSelector]; if (authKey == nil) { if (transactionReady) { transactionReady(nil); } return; } } bool extendedPadding = false; if (transport.proxySettings != nil && transport.proxySettings.secret != nil) { MTProxySecret *parsedSecret = [MTProxySecret parseData:transport.proxySettings.secret]; if ([parsedSecret isKindOfClass:[MTProxySecretType1 class]] || [parsedSecret isKindOfClass:[MTProxySecretType2 class]]) { extendedPadding = true; } } else if (scheme.address.secret != nil) { MTProxySecret *parsedSecret = [MTProxySecret parseData:scheme.address.secret]; if ([parsedSecret isKindOfClass:[MTProxySecretType1 class]] || [parsedSecret isKindOfClass:[MTProxySecretType2 class]]) { extendedPadding = true; } } if ([self canAskForTransactions]) { MTSessionInfo *transactionSessionInfo = _sessionInfo; NSMutableArray *messageTransactions = [[NSMutableArray alloc] init]; if (transportSpecificTransaction != nil) { if (!(transportSpecificTransaction.requiresEncryption && _useUnauthorizedMode) && (!transportSpecificTransaction.requiresEncryption || authKey != nil)) { [messageTransactions addObject:transportSpecificTransaction]; } } bool anyTransactionHasHighPriorityMessages = false; NSMutableArray *messageServiceTransactions = [[NSMutableArray alloc] init]; for (id messageService in _messageServices) { if ([messageService respondsToSelector:@selector(mtProtoMessageTransaction:authInfoSelector:sessionInfo:scheme:)]) { MTMessageTransaction *messageTransaction = [messageService mtProtoMessageTransaction:self authInfoSelector:authInfoSelector sessionInfo:transactionSessionInfo scheme:scheme]; if (messageTransaction != nil) { for (MTOutgoingMessage *message in messageTransaction.messagePayload) { if (message.hasHighPriority) { anyTransactionHasHighPriorityMessages = true; break; } } [messageServiceTransactions addObject:messageTransaction]; } } } if (forceConfirmations || !anyTransactionHasHighPriorityMessages || [transactionSessionInfo scheduledMessageConfirmationsExceedSize:MTMaxUnacknowledgedMessageSize orCount:MTMaxUnacknowledgedMessageCount]) { NSArray *scheduledMessageConfirmations = [transactionSessionInfo scheduledMessageConfirmations]; if (scheduledMessageConfirmations.count != 0) { MTBuffer *msgsAckBuffer = [[MTBuffer alloc] init]; [msgsAckBuffer appendInt32:(int32_t)0x62d6b459]; [msgsAckBuffer appendInt32:481674261]; [msgsAckBuffer appendInt32:(int32_t)scheduledMessageConfirmations.count]; for (NSNumber *nMessageId in scheduledMessageConfirmations) { [msgsAckBuffer appendInt64:(int64_t)[nMessageId longLongValue]]; } MTOutgoingMessage *outgoingMessage = [[MTOutgoingMessage alloc] initWithData:msgsAckBuffer.data metadata:@"msgsAck" additionalDebugDescription:nil shortMetadata:@"msgsAck"]; outgoingMessage.requiresConfirmation = false; [messageTransactions addObject:[[MTMessageTransaction alloc] initWithMessagePayload:@[outgoingMessage] prepared:nil failed:nil completion:^(__unused NSDictionary *messageInternalIdToTransactionId, NSDictionary *messageInternalIdToPreparedMessage, __unused NSDictionary *messageInternalIdToQuickAckId) { if (messageInternalIdToTransactionId[outgoingMessage.internalId] != nil && messageInternalIdToPreparedMessage[outgoingMessage.internalId] != nil) { [transactionSessionInfo assignTransactionId:messageInternalIdToTransactionId[outgoingMessage.internalId] toScheduledMessageConfirmationsWithIds:scheduledMessageConfirmations]; } }]]; } } [messageTransactions addObjectsFromArray:messageServiceTransactions]; NSMutableArray *transactionMessageList = [[NSMutableArray alloc] init]; NSMutableDictionary *messageInternalIdToPreparedMessage = [[NSMutableDictionary alloc] init]; NSMutableDictionary *preparedMessageInternalIdToMessageInternalId = [[NSMutableDictionary alloc] init]; bool monotonityViolated = false; bool saltSetEmpty = false; int64_t messageSalt = 0; if (!_useUnauthorizedMode) { messageSalt = [_validAuthInfo.authInfo authSaltForMessageId:[transactionSessionInfo actualClientMessagId]]; if (messageSalt == 0) saltSetEmpty = true; } bool transactionNeedsQuickAck = false; bool transactionExpectsDataInResponse = false; for (MTMessageTransaction *messageTransaction in messageTransactions) { for (MTOutgoingMessage *outgoingMessage in messageTransaction.messagePayload) { int64_t messageId = 0; int32_t messageSeqNo = 0; if (outgoingMessage.messageId == 0) { messageId = [transactionSessionInfo generateClientMessageId:&monotonityViolated]; messageSeqNo = [transactionSessionInfo takeSeqNo:outgoingMessage.requiresConfirmation]; } else { messageId = outgoingMessage.messageId; messageSeqNo = outgoingMessage.messageSeqNo; } NSData *messageData = outgoingMessage.data; if (outgoingMessage.dynamicDecorator != nil) { id decoratedData = outgoingMessage.dynamicDecorator(messageId, messageData, messageInternalIdToPreparedMessage); if (decoratedData != nil) messageData = decoratedData; } NSData *data = messageData; if (MTLogEnabled()) { NSString *messageDescription = [self outgoingMessageDescription:outgoingMessage messageId:messageId messageSeqNo:messageSeqNo]; MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p preparing %@]", self, _context, messageDescription); } NSString *shortMessageDescription = [self outgoingShortMessageDescription:outgoingMessage messageId:messageId messageSeqNo:messageSeqNo]; MTShortLog(@"[MTProto#%p@%p preparing %@]", self, _context, shortMessageDescription); if (!monotonityViolated || _useUnauthorizedMode) { MTPreparedMessage *preparedMessage = [[MTPreparedMessage alloc] initWithData:data messageId:messageId seqNo:messageSeqNo salt:messageSalt requiresConfirmation:outgoingMessage.requiresConfirmation hasHighPriority:outgoingMessage.hasHighPriority inResponseToMessageId:outgoingMessage.inResponseToMessageId]; if (outgoingMessage.needsQuickAck) transactionNeedsQuickAck = true; if (outgoingMessage.requiresConfirmation) transactionExpectsDataInResponse = true; messageInternalIdToPreparedMessage[outgoingMessage.internalId] = preparedMessage; preparedMessageInternalIdToMessageInternalId[preparedMessage.internalId] = outgoingMessage.internalId; [transactionMessageList addObject:preparedMessage]; } } if ([transport needsParityCorrection] && !transactionExpectsDataInResponse) transactionNeedsQuickAck = true; } for (MTMessageTransaction *messageTransaction in messageTransactions) { if (messageTransaction.prepared) { messageTransaction.prepared(messageInternalIdToPreparedMessage); } } if (monotonityViolated || saltSetEmpty) { for (MTMessageTransaction *messageTransaction in messageTransactions) { if (messageTransaction.completion) messageTransaction.completion(nil, nil, nil); } if (transactionReady != nil) transactionReady(nil); if (monotonityViolated) { if (MTLogEnabled()) { MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p client message id monotonity violated]", self, _context); } MTShortLog(@"[MTProto#%p@%p client message id monotonity violated]", self, _context); [self resetSessionInfo]; } else if (saltSetEmpty) [self initiateTimeSync]; } else if (transactionReady != nil) { if (transactionMessageList.count != 0) { if (transactionMessageList.count != 1) { [transactionMessageList sortUsingComparator:^NSComparisonResult(MTPreparedMessage *message1, MTPreparedMessage *message2) { return message1.messageId < message2.messageId ? NSOrderedAscending : NSOrderedDescending; }]; if (!forceConfirmations) { NSMutableArray *highPriorityMessages = nil; for (NSInteger i = 0; i < (NSInteger)transactionMessageList.count; i++) { MTPreparedMessage *preparedMessage = transactionMessageList[(NSUInteger)i]; if (preparedMessage.hasHighPriority) { if (highPriorityMessages == nil) highPriorityMessages = [[NSMutableArray alloc] init]; [highPriorityMessages addObject:preparedMessage]; [transactionMessageList removeObjectAtIndex:(NSUInteger)i]; i--; } } if (highPriorityMessages != nil) { [highPriorityMessages addObjectsFromArray:transactionMessageList]; transactionMessageList = highPriorityMessages; } } } NSMutableArray *transactionPayloadList = [[NSMutableArray alloc] init]; NSMutableArray *messageInternalIdsByPayload = [[NSMutableArray alloc] init]; NSMutableArray *quickAckIdsByPayload = [[NSMutableArray alloc] init]; bool currentlyProcessingHighPriority = false; for (NSUInteger i = 0; i < transactionMessageList.count; ) { if (!_useUnauthorizedMode) { NSMutableArray *currentContainerMessages = [[NSMutableArray alloc] init]; NSUInteger currentContainerSize = 0; for (NSUInteger j = i; j < transactionMessageList.count; j++, i++) { MTPreparedMessage *preparedMessage = transactionMessageList[j]; bool breakContainer = false; if (!forceConfirmations) { if (preparedMessage.hasHighPriority) currentlyProcessingHighPriority = true; else if (currentlyProcessingHighPriority) { currentlyProcessingHighPriority = false; breakContainer = true; } } if (currentContainerSize + preparedMessage.data.length > MTMaxContainerSize || (breakContainer && currentContainerSize != 0)) { if (currentContainerSize == 0) { [currentContainerMessages addObject:preparedMessage]; currentContainerSize += preparedMessage.data.length; i++; } break; } else { [currentContainerMessages addObject:preparedMessage]; currentContainerSize += preparedMessage.data.length; } } if (currentContainerMessages.count == 1 && ![transactionSessionInfo wasMessageSentOnce:currentContainerMessages[0].messageId]) { [transactionSessionInfo setMessageWasSentOnce:currentContainerMessages[0].messageId]; int32_t quickAckId = 0; NSData *messageData = [self _dataForEncryptedMessage:currentContainerMessages[0] authKey:authKey sessionInfo:transactionSessionInfo quickAckId:&quickAckId address:scheme.address extendedPadding:extendedPadding]; if (messageData != nil) { [transactionPayloadList addObject:messageData]; [messageInternalIdsByPayload addObject:@[preparedMessageInternalIdToMessageInternalId[((MTPreparedMessage *)currentContainerMessages[0]).internalId]]]; [quickAckIdsByPayload addObject:@(quickAckId)]; } } else if (currentContainerMessages.count != 0) { int32_t quickAckId = 0; NSData *containerData = [self _dataForEncryptedContainerWithMessages:currentContainerMessages authKey:authKey sessionInfo:transactionSessionInfo quickAckId:&quickAckId address:scheme.address extendedPadding:extendedPadding]; if (containerData != nil) { [transactionPayloadList addObject:containerData]; NSMutableArray *messageInternalIds = [[NSMutableArray alloc] initWithCapacity:currentContainerMessages.count]; for (MTPreparedMessage *preparedMessage in currentContainerMessages) { [messageInternalIds addObject:preparedMessageInternalIdToMessageInternalId[preparedMessage.internalId]]; } [messageInternalIdsByPayload addObject:messageInternalIds]; [quickAckIdsByPayload addObject:@(quickAckId)]; } } } else { MTPreparedMessage *preparedMessage = transactionMessageList[i]; NSData *messageData = [self _dataForPlainMessage:preparedMessage extendedPadding:extendedPadding]; i++; if (messageData != nil) { [transactionPayloadList addObject:messageData]; [messageInternalIdsByPayload addObject:@[preparedMessageInternalIdToMessageInternalId[preparedMessage.internalId]]]; [quickAckIdsByPayload addObject:@(0)]; } } } if (transactionPayloadList.count != 0) { NSMutableArray *transportTransactions = [[NSMutableArray alloc] initWithCapacity:transactionPayloadList.count]; for (NSUInteger i = 0; i < transactionPayloadList.count; i++) { [transportTransactions addObject:[[MTTransportTransaction alloc] initWithPayload:transactionPayloadList[i] completion:^(bool success, id transactionId) { [[MTProto managerQueue] dispatchOnQueue:^ { if (success) { NSMutableDictionary *messageInternalIdToTransactionId = [[NSMutableDictionary alloc] init]; NSMutableDictionary *messageInternalIdToQuickAckId = [[NSMutableDictionary alloc] init]; NSMutableDictionary *transactionMessageInternalIdToPreparedMessage = [[NSMutableDictionary alloc] init]; for (id messageInternalId in messageInternalIdsByPayload[i]) { messageInternalIdToTransactionId[messageInternalId] = transactionId; messageInternalIdToQuickAckId[messageInternalId] = quickAckIdsByPayload[i]; MTPreparedMessage *preparedMessage = messageInternalIdToPreparedMessage[messageInternalId]; if (preparedMessage != nil) transactionMessageInternalIdToPreparedMessage[messageInternalId] = preparedMessage; } for (MTMessageTransaction *messageTransaction in messageTransactions) { if (messageTransaction.completion) messageTransaction.completion(messageInternalIdToTransactionId, transactionMessageInternalIdToPreparedMessage, messageInternalIdToQuickAckId); } } else { NSMutableString *idsString = [[NSMutableString alloc] init]; for (id messageInternalId in messageInternalIdsByPayload[i]) { MTOutgoingMessage *outgoingMessage = messageInternalIdToPreparedMessage[messageInternalId]; if (outgoingMessage != nil) { if (idsString.length != 0) [idsString appendString:@", "]; [idsString appendFormat:@"%" PRId64 "", outgoingMessage.messageId]; } } for (MTMessageTransaction *messageTransaction in messageTransactions) { if (messageTransaction.completion) { messageTransaction.completion(nil, nil, nil); } } if (MTLogEnabled()) { MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p transport did not accept transactions with messages (%@)]", self, _context, idsString); } } }]; } needsQuickAck:transactionNeedsQuickAck expectsDataInResponse:transactionExpectsDataInResponse]]; } transactionReady(transportTransactions); } else { for (MTMessageTransaction *messageTransaction in messageTransactions) { if (messageTransaction.completion) messageTransaction.completion(nil, nil, nil); } transactionReady(nil); } } else { for (MTMessageTransaction *messageTransaction in messageTransactions) { if (messageTransaction.completion) { messageTransaction.completion(nil, nil, nil); } } transactionReady(nil); } } else { for (MTMessageTransaction *messageTransaction in messageTransactions) { if (messageTransaction.completion) { messageTransaction.completion(nil, nil, nil); } } } } else if ([self timeFixOrSaltsMissing] && [self canAskForServiceTransactions] && (_timeFixContext == nil || _timeFixContext.transactionId == nil)) { int64_t timeFixMessageId = [_sessionInfo generateClientMessageId:NULL]; int32_t timeFixSeqNo = [_sessionInfo takeSeqNo:false]; int64_t randomId = 0; arc4random_buf(&randomId, 8); MTBuffer *pingBuffer = [[MTBuffer alloc] init]; [pingBuffer appendInt32:(int32_t)0x7abe77ec]; [pingBuffer appendInt64:randomId]; NSData *messageData = pingBuffer.data; MTOutputStream *decryptedOs = [[MTOutputStream alloc] init]; if (MTLogEnabled()) { MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p sending time fix ping (%" PRId64 "/%" PRId32 ", %" PRId64 ")]", self, _context, timeFixMessageId, timeFixSeqNo, _sessionInfo.sessionId); } MTShortLog(@"[MTProto#%p@%p sending time fix ping (%" PRId64 "/%" PRId32 ", %" PRId64 ")]", self, _context, timeFixMessageId, timeFixSeqNo, _sessionInfo.sessionId); [decryptedOs writeInt64:[_validAuthInfo.authInfo authSaltForMessageId:timeFixMessageId]]; // salt [decryptedOs writeInt64:_sessionInfo.sessionId]; [decryptedOs writeInt64:timeFixMessageId]; [decryptedOs writeInt32:timeFixSeqNo]; [decryptedOs writeInt32:(int32_t)messageData.length]; [decryptedOs writeData:messageData]; NSData *decryptedData = [self paddedData:[decryptedOs currentBytes] extendedPadding:extendedPadding]; MTDatacenterAuthKey *effectiveAuthKey = authKey; int xValue = 0; NSMutableData *msgKeyLargeData = [[NSMutableData alloc] init]; [msgKeyLargeData appendBytes:effectiveAuthKey.authKey.bytes + 88 + xValue length:32]; [msgKeyLargeData appendData:decryptedData]; NSData *msgKeyLarge = MTSha256(msgKeyLargeData); NSData *messageKey = [msgKeyLarge subdataWithRange:NSMakeRange(8, 16)]; MTMessageEncryptionKey *encryptionKey = [MTMessageEncryptionKey messageEncryptionKeyV2ForAuthKey:effectiveAuthKey.authKey messageKey:messageKey toClient:false]; NSData *transactionData = nil; if (encryptionKey != nil) { NSMutableData *encryptedData = [[NSMutableData alloc] initWithCapacity:14 + decryptedData.length]; [encryptedData appendData:decryptedData]; MTAesEncryptInplace(encryptedData, encryptionKey.key, encryptionKey.iv); int64_t authKeyId = effectiveAuthKey.authKeyId; [encryptedData replaceBytesInRange:NSMakeRange(0, 0) withBytes:&authKeyId length:8]; [encryptedData replaceBytesInRange:NSMakeRange(8, 0) withBytes:messageKey.bytes length:messageKey.length]; transactionData = encryptedData; } if (transactionReady != nil) { if (transactionData != nil) { __weak MTProto *weakSelf = self; transactionReady(@[[[MTTransportTransaction alloc] initWithPayload:transactionData completion:^(bool success, id transactionId) { [[MTProto managerQueue] dispatchOnQueue:^ { if (success) { if (transactionId != nil) { _timeFixContext = [[MTTimeFixContext alloc] initWithMessageId:timeFixMessageId messageSeqNo:timeFixSeqNo transactionId:transactionId timeFixAbsoluteStartTime:MTAbsoluteSystemTime()]; } } else { __strong MTProto *strongSelf = weakSelf; [strongSelf requestTransportTransaction]; } }]; } needsQuickAck:false expectsDataInResponse:true]]); } else transactionReady(nil); } } else if (transactionReady != nil) transactionReady(nil); /*if (debugResetTransport) { [self resetTransport]; [self requestTransportTransaction]; }*/ }]; } - (NSData *)_dataForEncryptedContainerWithMessages:(NSArray *)preparedMessages authKey:(MTDatacenterAuthKey *)authKey sessionInfo:(MTSessionInfo *)sessionInfo quickAckId:(int32_t *)quickAckId address:(MTDatacenterAddress *)address extendedPadding:(bool)extendedPadding { MTDatacenterAuthKey *effectiveAuthKey = authKey; NSMutableArray *containerMessageIds = [[NSMutableArray alloc] init]; MTOutputStream *containerOs = [[MTOutputStream alloc] init]; [containerOs writeInt32:0x73f1f8dc]; // msg_container [containerOs writeInt32:(int32_t)preparedMessages.count]; int64_t salt = 0; for (MTPreparedMessage *preparedMessage in preparedMessages) { salt = preparedMessage.salt; [containerOs writeInt64:preparedMessage.messageId]; [containerOs writeInt32:preparedMessage.seqNo]; [containerOs writeInt32:(int32_t)preparedMessage.data.length]; [containerOs writeData:preparedMessage.data]; if (preparedMessage.requiresConfirmation) [containerMessageIds addObject:@(preparedMessage.messageId)]; } NSData *containerData = [containerOs currentBytes]; MTOutputStream *decryptedOs = [[MTOutputStream alloc] init]; int64_t containerMessageId = [sessionInfo generateClientMessageId:NULL]; if (containerMessageIds.count != 0) { [sessionInfo addContainerMessageIdMapping:containerMessageId childMessageIds:containerMessageIds]; NSMutableString *idsString = [[NSMutableString alloc] init]; for (NSNumber *nMessageId in containerMessageIds) { if (idsString.length != 0) [idsString appendString:@","]; [idsString appendFormat:@"%lld", [nMessageId longLongValue]]; } if (MTLogEnabled()) { MTLogWithPrefix(_getLogPrefix, @" container (%" PRId64 ") of (%@), in %" PRId64 "", containerMessageId, idsString, sessionInfo.sessionId); } MTShortLog(@" container (%" PRId64 ") of (%@), in %" PRId64 "", containerMessageId, idsString, sessionInfo.sessionId); } [decryptedOs writeInt64:salt]; [decryptedOs writeInt64:sessionInfo.sessionId]; [decryptedOs writeInt64:containerMessageId]; [decryptedOs writeInt32:[sessionInfo takeSeqNo:false]]; [decryptedOs writeInt32:(int32_t)containerData.length]; [decryptedOs writeData:containerData]; NSData *decryptedData = [self paddedData:[decryptedOs currentBytes] extendedPadding:extendedPadding]; int xValue = 0; NSMutableData *msgKeyLargeData = [[NSMutableData alloc] init]; [msgKeyLargeData appendBytes:effectiveAuthKey.authKey.bytes + 88 + xValue length:32]; [msgKeyLargeData appendData:decryptedData]; NSData *msgKeyLarge = MTSha256(msgKeyLargeData); NSData *messageKey = [msgKeyLarge subdataWithRange:NSMakeRange(8, 16)]; MTMessageEncryptionKey *encryptionKey = [MTMessageEncryptionKey messageEncryptionKeyV2ForAuthKey:effectiveAuthKey.authKey messageKey:messageKey toClient:false]; int32_t nQuickAckId = *((int32_t *)(msgKeyLarge.bytes)); nQuickAckId = nQuickAckId & 0x7fffffff; if (quickAckId != NULL) *quickAckId = nQuickAckId; if (encryptionKey != nil) { NSMutableData *encryptedData = [[NSMutableData alloc] init]; [encryptedData appendData:decryptedData]; MTAesEncryptInplace(encryptedData, encryptionKey.key, encryptionKey.iv); int64_t authKeyId = effectiveAuthKey.authKeyId; [encryptedData replaceBytesInRange:NSMakeRange(0, 0) withBytes:&authKeyId length:8]; [encryptedData replaceBytesInRange:NSMakeRange(8, 0) withBytes:messageKey.bytes length:messageKey.length]; return encryptedData; } return nil; } - (NSData *)_dataForPlainMessage:(MTPreparedMessage *)preparedMessage extendedPadding:(bool)extendedPadding { MTOutputStream *os = [[MTOutputStream alloc] init]; [os writeInt64:0]; [os writeInt64:preparedMessage.messageId]; [os writeInt32:(int32_t)preparedMessage.data.length]; [os writeData:preparedMessage.data]; uint32_t paddingSize = 0; if (extendedPadding) { paddingSize = arc4random_uniform((256 - 16) / 4) * 4; } uint8_t padding[256]; if (paddingSize > 0) { arc4random_buf(padding, paddingSize); [os write:padding maxLength:paddingSize]; } NSData *messageData = [os currentBytes]; return messageData; } + (NSData *)paddedDataV1:(NSData *)data { NSMutableData *padded = [[NSMutableData alloc] initWithData:data]; uint8_t randomBytes[128]; arc4random_buf(randomBytes, 128); for (int i = 0; ((int)data.length + i) % 16 != 0; i++) { [padded appendBytes:randomBytes + i length:1]; } return padded; } - (NSData *)paddedData:(NSData *)data extendedPadding:(bool)extendedPadding { NSMutableData *padded = [[NSMutableData alloc] initWithData:data]; uint8_t randomBytes[256]; arc4random_buf(randomBytes, 256); int take = 0; while (take < 12) { [padded appendBytes:randomBytes + take length:1]; take++; } while (padded.length % 16 != 0) { [padded appendBytes:randomBytes + take length:1]; take++; } uint32_t extraPaddingSize = 72; if (extendedPadding) { extraPaddingSize = 256; } int remainingCount = arc4random_uniform(extraPaddingSize + 1 - take); while (remainingCount % 16 != 0) { remainingCount--; } for (int i = 0; i < remainingCount; i++) { [padded appendBytes:randomBytes + take length:1]; take++; } assert(padded.length % 16 == 0); return padded; } + (NSData *)_manuallyEncryptedMessage:(NSData *)preparedData messageId:(int64_t)messageId authKey:(MTDatacenterAuthKey *)authKey { MTOutputStream *decryptedOs = [[MTOutputStream alloc] init]; int64_t random1 = 0; int64_t random2 = 0; arc4random_buf(&random1, 8); arc4random_buf(&random2, 8); [decryptedOs writeInt64:random1]; [decryptedOs writeInt64:random2]; [decryptedOs writeInt64:messageId]; [decryptedOs writeInt32:0]; [decryptedOs writeInt32:(int32_t)preparedData.length]; [decryptedOs writeData:preparedData]; NSData *decryptedData = [MTProto paddedDataV1:[decryptedOs currentBytes]]; NSData *messageKeyFull = MTSubdataSha1(decryptedData, 0, 32 + preparedData.length); NSData *messageKey = [[NSData alloc] initWithBytes:(((int8_t *)messageKeyFull.bytes) + messageKeyFull.length - 16) length:16]; MTMessageEncryptionKey *encryptionKey = [MTMessageEncryptionKey messageEncryptionKeyForAuthKey:authKey.authKey messageKey:messageKey toClient:false]; if (encryptionKey != nil) { NSMutableData *encryptedData = [[NSMutableData alloc] initWithCapacity:14 + decryptedData.length]; [encryptedData appendData:decryptedData]; MTAesEncryptInplace(encryptedData, encryptionKey.key, encryptionKey.iv); int64_t authKeyId = authKey.authKeyId; [encryptedData replaceBytesInRange:NSMakeRange(0, 0) withBytes:&authKeyId length:8]; [encryptedData replaceBytesInRange:NSMakeRange(8, 0) withBytes:messageKey.bytes length:messageKey.length]; return encryptedData; } else return nil; } - (NSData *)_dataForEncryptedMessage:(MTPreparedMessage *)preparedMessage authKey:(MTDatacenterAuthKey *)authKey sessionInfo:(MTSessionInfo *)sessionInfo quickAckId:(int32_t *)quickAckId address:(MTDatacenterAddress *)address extendedPadding:(bool)extendedPadding { MTDatacenterAuthKey *effectiveAuthKey = authKey; NSAssert(effectiveAuthKey != nil, @"effectiveAuthKey == nil"); MTOutputStream *decryptedOs = [[MTOutputStream alloc] init]; [decryptedOs writeInt64:preparedMessage.salt]; [decryptedOs writeInt64:sessionInfo.sessionId]; [decryptedOs writeInt64:preparedMessage.messageId]; [decryptedOs writeInt32:preparedMessage.seqNo]; [decryptedOs writeInt32:(int32_t)preparedMessage.data.length]; [decryptedOs writeData:preparedMessage.data]; NSData *decryptedData = [self paddedData:[decryptedOs currentBytes] extendedPadding:extendedPadding]; int xValue = 0; NSMutableData *msgKeyLargeData = [[NSMutableData alloc] init]; [msgKeyLargeData appendBytes:effectiveAuthKey.authKey.bytes + 88 + xValue length:32]; [msgKeyLargeData appendData:decryptedData]; NSData *msgKeyLarge = MTSha256(msgKeyLargeData); NSData *messageKey = [msgKeyLarge subdataWithRange:NSMakeRange(8, 16)]; MTMessageEncryptionKey *encryptionKey = [MTMessageEncryptionKey messageEncryptionKeyV2ForAuthKey:effectiveAuthKey.authKey messageKey:messageKey toClient:false]; if (encryptionKey != nil) { NSMutableData *encryptedData = [[NSMutableData alloc] initWithCapacity:14 + decryptedData.length]; [encryptedData appendData:decryptedData]; MTAesEncryptInplace(encryptedData, encryptionKey.key, encryptionKey.iv); int64_t authKeyId = effectiveAuthKey.authKeyId; [encryptedData replaceBytesInRange:NSMakeRange(0, 0) withBytes:&authKeyId length:8]; [encryptedData replaceBytesInRange:NSMakeRange(8, 0) withBytes:messageKey.bytes length:messageKey.length]; return encryptedData; } else return nil; } - (void)transportTransactionsMayHaveFailed:(MTTransport *)__unused transport transactionIds:(NSArray *)transactionIds { [[MTProto managerQueue] dispatchOnQueue:^ { if ([self isStopped]) return; bool requestTransaction = false; if (_timeFixContext != nil && _timeFixContext.transactionId != nil && [transactionIds containsObject:_timeFixContext.transactionId]) { _timeFixContext = nil; requestTransaction = true; } for (NSInteger i = (NSInteger)_messageServices.count - 1; i >= 0; i--) { id messageService = _messageServices[(NSUInteger)i]; if ([messageService respondsToSelector:@selector(mtProto:transactionsMayHaveFailed:)]) [messageService mtProto:self transactionsMayHaveFailed:transactionIds]; } if (requestTransaction && ![self isPaused]) [self requestTransportTransaction]; }]; } - (void)allTransactionsMayHaveFailed { if ([self isStopped]) return; bool requestTransaction = false; if (_timeFixContext != nil && _timeFixContext.transactionId != nil) { _timeFixContext = nil; requestTransaction = true; } for (NSInteger i = (NSInteger)_messageServices.count - 1; i >= 0; i--) { id messageService = _messageServices[(NSUInteger)i]; if ([messageService respondsToSelector:@selector(mtProtoAllTransactionsMayHaveFailed:)]) [messageService mtProtoAllTransactionsMayHaveFailed:self]; } if (requestTransaction && ![self isPaused]) [self requestTransportTransaction]; } - (void)transportReceivedQuickAck:(MTTransport *)transport quickAckId:(int32_t)quickAckId { [[MTProto managerQueue] dispatchOnQueue:^ { if (_transport != transport || [self isStopped]) return; for (NSInteger i = (NSInteger)_messageServices.count - 1; i >= 0; i--) { id messageService = _messageServices[(NSUInteger)i]; if ([messageService respondsToSelector:@selector(mtProto:receivedQuickAck:)]) [messageService mtProto:self receivedQuickAck:quickAckId]; } }]; } - (void)transportDecodeProgressToken:(MTTransport *)transport scheme:(MTTransportScheme *)scheme data:(NSData *)data token:(int64_t)token completion:(void (^)(int64_t token, id progressToken))completion { [[MTProto managerQueue] dispatchOnQueue:^ { if (transport != _transport || completion == nil) { return; } if (_useUnauthorizedMode) { return; } MTDatacenterAuthKey *authKey = [self getAuthKeyForCurrentScheme:scheme createIfNeeded:false authInfoSelector:nil]; if (authKey == nil) { return; } MTDatacenterAuthKey *effectiveAuthKey = authKey; MTInputStream *is = [[MTInputStream alloc] initWithData:data]; int64_t keyId = [is readInt64]; if (keyId == authKey.authKeyId) { NSData *messageKey = [is readData:16]; MTMessageEncryptionKey *encryptionKey = [MTMessageEncryptionKey messageEncryptionKeyV2ForAuthKey:effectiveAuthKey.authKey messageKey:messageKey toClient:true]; NSMutableData *encryptedMessageData = [is readMutableData:(data.length - 24)]; while (encryptedMessageData.length % 16 != 0) { [encryptedMessageData setLength:encryptedMessageData.length - 1]; } if (encryptedMessageData.length != 0) { NSData *decryptedData = MTAesDecrypt(encryptedMessageData, encryptionKey.key, encryptionKey.iv); MTInputStream *messageIs = [[MTInputStream alloc] initWithData:decryptedData]; [messageIs readInt64]; [messageIs readInt64]; [messageIs readInt64]; [messageIs readInt32]; [messageIs readInt32]; bool stop = false; int64_t reqMsgId = 0; /*if (true) {*/ while (!stop && reqMsgId == 0) { int32_t signature = [messageIs readInt32:&stop]; [self findReqMsgId:messageIs signature:signature reqMsgId:&reqMsgId failed:&stop]; } /*} else { int32_t signature = [messageIs readInt32]; if (signature == (int)0xf35c6d01) reqMsgId = [messageIs readInt64]; else if (signature == (int)0x73f1f8dc) { int count = [messageIs readInt32]; if (count != 0) { [messageIs readInt64]; [messageIs readInt32]; [messageIs readInt32]; signature = [messageIs readInt32]; if (signature == (int)0xf35c6d01) reqMsgId = [messageIs readInt64]; } } }*/ if (reqMsgId != 0) completion(token, @(reqMsgId)); } } }]; } - (void)findReqMsgId:(MTInputStream *)is signature:(int32_t)signature reqMsgId:(int64_t *)reqMsgId failed:(bool *)failed { if (signature == (int)0x73f1f8dc) //msg_container { int count = [is readInt32:failed]; if (*failed) return; for (int i = 0; i < count; i++) { [is readInt64:failed]; [is readInt32:failed]; [is readInt32:failed]; if (*failed) return; int innerSignature = [is readInt32:failed]; if (*failed) return; [self findReqMsgId:is signature:innerSignature reqMsgId:reqMsgId failed:failed]; if (*failed || *reqMsgId != 0) return; } } else if (signature == (int)0xf35c6d01) //rpc_result { int64_t value = [is readInt64:failed]; if (*failed) return; *reqMsgId = value; } else if (signature == (int)0x62d6b459) // msgs_ack { [is readInt32:failed]; if (*failed) return; int count = [is readInt32:failed]; if (*failed) return; for (int i = 0; i < count; i++) { [is readInt32:failed]; if (*failed) return; } } else if (signature == (int)0x347773c5) // pong { [is readInt64:failed]; [is readInt64:failed]; if (*failed) return; } } - (void)transportUpdatedDataReceiveProgress:(MTTransport *)transport progressToken:(id)progressToken packetLength:(NSInteger)packetLength progress:(float)progress { [[MTProto managerQueue] dispatchOnQueue:^ { if (transport != _transport) return; for (id messageService in _messageServices) { if ([messageService respondsToSelector:@selector(mtProto:updateReceiveProgressForToken:progress:packetLength:)]) [messageService mtProto:self updateReceiveProgressForToken:progressToken progress:progress packetLength:packetLength]; } }]; } - (void)transportActivityUpdated:(MTTransport * _Nonnull)transport { [[MTProto managerQueue] dispatchOnQueue:^ { if (transport != _transport) return; for (id messageService in _messageServices) { if ([messageService respondsToSelector:@selector(mtProtoTransportActivityUpdated:)]) [messageService mtProtoTransportActivityUpdated:self]; } }]; } - (void)transportTransactionsSucceeded:(NSArray *)transactionIds { [[MTProto managerQueue] dispatchOnQueue:^ { [_sessionInfo removeScheduledMessageConfirmationsWithTransactionIds:transactionIds]; }]; } static NSString *dumpHexString(NSData *data, int maxLength) { const unsigned char *dataBuffer = (const unsigned char *)[data bytes]; if (dataBuffer == NULL) return [NSString string]; NSUInteger dataLength = MIN(data.length, 128); NSMutableString *hexString = [NSMutableString stringWithCapacity:(dataLength * 2)]; for (int i = 0; i < (int)dataLength; i++) { [hexString appendString:[NSString stringWithFormat:@"%02lx", (unsigned long)dataBuffer[i]]]; } if (dataLength < data.length) { [hexString appendString:@"..."]; } return hexString; } - (void)transportHasIncomingData:(MTTransport *)transport scheme:(MTTransportScheme *)scheme networkType:(int32_t)networkType data:(NSData *)data transactionId:(id)transactionId requestTransactionAfterProcessing:(bool)requestTransactionAfterProcessing decodeResult:(void (^)(id transactionId, bool success))decodeResult { /*__block bool simulateError = false; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ simulateError = true; }); if (simulateError) { int32_t protocolErrorCode = -404; data = [NSData dataWithBytes:&protocolErrorCode length:4]; }*/ [[MTProto managerQueue] dispatchOnQueue:^ { if (_transport != transport || [self isStopped]) return; _transport.simultaneousTransactionsEnabled = true; if (data.length <= 4 + 15) { int32_t protocolErrorCode = 0; [data getBytes:&protocolErrorCode range:NSMakeRange(0, 4)]; if (MTLogEnabled()) { MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p protocol error %" PRId32 "", self, _context, protocolErrorCode); } MTShortLog(@"[MTProto#%p@%p protocol error %" PRId32 "", self, _context, protocolErrorCode); if (decodeResult != nil) decodeResult(transactionId, false); id currentTransport = _transport; for (NSInteger i = (NSInteger)_messageServices.count - 1; i >= 0; i--) { id messageService = _messageServices[(NSUInteger)i]; if ([messageService respondsToSelector:@selector(mtProto:protocolErrorReceived:)]) [messageService mtProto:self protocolErrorReceived:protocolErrorCode]; } if (protocolErrorCode == -404) { [self handleMissingKey:scheme]; } if (protocolErrorCode == -429) { _isConnectionThrottled = true; if (_unthrottleConnectionTimer == nil) { __weak MTProto *weakSelf = self; _unthrottleConnectionTimer = [[MTTimer alloc] initWithTimeout:5.0 repeat:false completion:^{ __strong MTProto *strongSelf = weakSelf; if (strongSelf == nil) { return; } strongSelf->_isConnectionThrottled = false; strongSelf->_unthrottleConnectionTimer = nil; [strongSelf requestTransportTransaction]; } queue:[MTProto managerQueue].nativeQueue]; } } else { if (currentTransport == _transport) { [self requestSecureTransportReset]; } [self transportTransactionsMayHaveFailed:transport transactionIds:@[transactionId]]; } return; } NSData *decryptedData = nil; int64_t embeddedAuthKeyId = 0; MTDatacenterAuthInfoSelector authInfoSelector = MTDatacenterAuthInfoSelectorPersistent; if (_useUnauthorizedMode) { decryptedData = data; } else { MTDatacenterAuthKey *authKey = [self getAuthKeyForCurrentScheme:scheme createIfNeeded:false authInfoSelector:&authInfoSelector]; if (authKey != nil) { embeddedAuthKeyId = authKey.authKeyId; decryptedData = [self _decryptIncomingTransportData:data address:scheme.address authKey:authKey]; } } if (decryptedData != nil) { if (decodeResult != nil) decodeResult(transactionId, true); int64_t dataMessageId = 0; bool parseError = false; NSArray *parsedMessages = [self _parseIncomingMessages:decryptedData dataMessageId:&dataMessageId embeddedAuthKeyId:embeddedAuthKeyId parseError:&parseError]; for (MTIncomingMessage *message in parsedMessages) { if ([message.body isKindOfClass:[MTRpcResultMessage class]]) { MTRpcResultMessage *rpcResultMessage = message.body; id maybeInternalMessage = [MTInternalMessageParser parseMessage:rpcResultMessage.data]; if ([maybeInternalMessage isKindOfClass:[MTRpcError class]]) { MTRpcError *rpcError = maybeInternalMessage; if (rpcError.errorCode == 401 && [rpcError.errorDescription isEqualToString:@"AUTH_KEY_PERM_EMPTY"]) { if (MTLogEnabled()) { MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p received AUTH_KEY_PERM_EMPTY]", self, _context); } MTShortLog(@"[MTProto#%p@%p received AUTH_KEY_PERM_EMPTY]", self, _context); [self handleMissingKey:scheme]; [self requestSecureTransportReset]; return; } } } } if (parseError) { if (MTLogEnabled()) { MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p incoming data parse error, header: %d:%@]", self, _context, (int)decryptedData.length, dumpHexString(decryptedData, 128)); } MTShortLog(@"[MTProto#%p@%p incoming data parse error, header: %d:%@]", self, _context, (int)decryptedData.length, dumpHexString(decryptedData, 128)); if (!_useUnauthorizedMode) { [_context reportTransportSchemeFailureForDatacenterId:_datacenterId transportScheme:scheme]; } [self transportTransactionsMayHaveFailed:transport transactionIds:@[transactionId]]; [self resetSessionInfo]; } else { [_context reportTransportSchemeSuccessForDatacenterId:_datacenterId transportScheme:scheme]; [self transportTransactionsSucceeded:@[transactionId]]; for (MTIncomingMessage *incomingMessage in parsedMessages) { [self _processIncomingMessage:incomingMessage totalSize:(int)data.length withTransactionId:transactionId address:scheme.address authInfoSelector:authInfoSelector networkType:networkType]; } if (requestTransactionAfterProcessing) [self requestTransportTransaction]; } } else { if (MTLogEnabled()) { MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p couldn't decrypt incoming data]", self, _context); } MTShortLog(@"[MTProto#%p@%p couldn't decrypt incoming data]", self, _context); if (decodeResult != nil) decodeResult(transactionId, false); [self transportTransactionsMayHaveFailed:transport transactionIds:@[transactionId]]; if (!_useUnauthorizedMode) { [_context reportTransportSchemeFailureForDatacenterId:_datacenterId transportScheme:scheme]; } [self requestSecureTransportReset]; } }]; } - (void)handleMissingKey:(MTTransportScheme *)scheme { NSAssert([[MTProto managerQueue] isCurrentQueue], @"invalid queue"); if (_useUnauthorizedMode) { if (MTLogEnabled()) { MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p don't handleMissingKey when useUnauthorizedMode]", self, _context); } return; } MTDatacenterAuthInfoSelector authInfoSelector; [self getAuthKeyForCurrentScheme:scheme createIfNeeded:false authInfoSelector:&authInfoSelector]; if (MTLogEnabled()) { MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p missing key %lld selector %d useExplicitAuthKey: %lld, canResetAuthData: %s]", self, _context, _validAuthInfo.authInfo.authKeyId, authInfoSelector, _useExplicitAuthKey.authKeyId, _canResetAuthData ? "true" : "false"); } if (_useExplicitAuthKey != nil) { } else if (_cdn) { _validAuthInfo = nil; [_context performBatchUpdates:^{ [_context updateAuthInfoForDatacenterWithId:_datacenterId authInfo:nil selector:authInfoSelector]; [_context authInfoForDatacenterWithIdRequired:_datacenterId isCdn:true selector:authInfoSelector allowUnboundEphemeralKeys:_allowUnboundEphemeralKeys]; }]; _mtState |= MTProtoStateAwaitingDatacenterAuthorization; _awaitingAuthInfoForSelector = @(authInfoSelector); } else { MTDatacenterAuthInfoSelector authInfoSelector; [self getAuthKeyForCurrentScheme:scheme createIfNeeded:false authInfoSelector:&authInfoSelector]; if (_requiredAuthToken != nil && _authTokenMasterDatacenterId != _datacenterId) { _validAuthInfo = nil; [_context removeTokenForDatacenterWithId:_datacenterId]; [_context performBatchUpdates:^{ [_context updateAuthInfoForDatacenterWithId:_datacenterId authInfo:nil selector:authInfoSelector]; [_context authInfoForDatacenterWithIdRequired:_datacenterId isCdn:false selector:authInfoSelector allowUnboundEphemeralKeys:_allowUnboundEphemeralKeys]; }]; _mtState |= MTProtoStateAwaitingDatacenterAuthorization; _awaitingAuthInfoForSelector = @(authInfoSelector); } else if (_canResetAuthData) { _validAuthInfo = nil; [_context performBatchUpdates:^{ [_context updateAuthInfoForDatacenterWithId:_datacenterId authInfo:nil selector:authInfoSelector]; [_context authInfoForDatacenterWithIdRequired:_datacenterId isCdn:false selector:authInfoSelector allowUnboundEphemeralKeys:_allowUnboundEphemeralKeys]; }]; _mtState |= MTProtoStateAwaitingDatacenterAuthorization; _awaitingAuthInfoForSelector = @(authInfoSelector); } else { switch (authInfoSelector) { case MTDatacenterAuthInfoSelectorEphemeralMain: case MTDatacenterAuthInfoSelectorEphemeralMedia: { _validAuthInfo = nil; [_context performBatchUpdates:^{ [_context updateAuthInfoForDatacenterWithId:_datacenterId authInfo:nil selector:authInfoSelector]; [_context authInfoForDatacenterWithIdRequired:_datacenterId isCdn:false selector:authInfoSelector allowUnboundEphemeralKeys:_allowUnboundEphemeralKeys]; }]; _mtState |= MTProtoStateAwaitingDatacenterAuthorization; _awaitingAuthInfoForSelector = @(authInfoSelector); break; } default: [_context checkIfLoggedOut:_datacenterId]; break; } } } } static bool isDataEqualToDataConstTime(NSData *data1, NSData *data2) { if (data1.length != data2.length) { return false; } uint8_t const *bytes1 = data1.bytes; uint8_t const *bytes2 = data2.bytes; int result = 0; for (int i = 0; i < data1.length; i++) { result |= bytes1[i] != bytes2[i]; } return result == 0; } - (NSData *)_decryptIncomingTransportData:(NSData *)transportData address:(MTDatacenterAddress *)address authKey:(MTDatacenterAuthKey *)authKey { MTDatacenterAuthKey *effectiveAuthKey = authKey; if (effectiveAuthKey == nil) return nil; if (transportData.length < 24 + 36) return nil; int64_t authKeyId = 0; [transportData getBytes:&authKeyId range:NSMakeRange(0, 8)]; if (authKeyId != effectiveAuthKey.authKeyId) return nil; NSData *embeddedMessageKey = [transportData subdataWithRange:NSMakeRange(8, 16)]; MTMessageEncryptionKey *encryptionKey = [MTMessageEncryptionKey messageEncryptionKeyV2ForAuthKey:effectiveAuthKey.authKey messageKey:embeddedMessageKey toClient:true]; if (encryptionKey == nil) return nil; NSData *dataToDecrypt = [transportData subdataWithRange:NSMakeRange(24, ((int32_t)(transportData.length - 24)) & (~15))]; NSData *decryptedData = MTAesDecrypt(dataToDecrypt, encryptionKey.key, encryptionKey.iv); int xValue = 8; NSMutableData *msgKeyLargeData = [[NSMutableData alloc] init]; [msgKeyLargeData appendBytes:effectiveAuthKey.authKey.bytes + 88 + xValue length:32]; [msgKeyLargeData appendData:decryptedData]; NSData *msgKeyLarge = MTSha256(msgKeyLargeData); NSData *messageKey = [msgKeyLarge subdataWithRange:NSMakeRange(8, 16)]; if (!isDataEqualToDataConstTime(messageKey, embeddedMessageKey)) { return nil; } int32_t messageDataLength = 0; [decryptedData getBytes:&messageDataLength range:NSMakeRange(28, 4)]; int32_t paddingLength = ((int32_t)decryptedData.length) - messageDataLength; if (paddingLength < 12 || paddingLength > 1024) { return nil; } if (messageDataLength < 0 || messageDataLength > (int32_t)decryptedData.length) { return nil; } return decryptedData; } - (id)parseMessage:(NSData *)data { NSData *unwrappedData = [MTInternalMessageParser unwrapMessage:data]; id internalMessage = [MTInternalMessageParser parseMessage:unwrappedData]; if (internalMessage != nil) return internalMessage; return [_context.serialization parseMessage:unwrappedData]; } - (NSArray *)_parseIncomingMessages:(NSData *)data dataMessageId:(out int64_t *)dataMessageId embeddedAuthKeyId:(int64_t)embeddedAuthKeyId parseError:(out bool *)parseError { MTInputStream *is = [[MTInputStream alloc] initWithData:data]; bool readError = false; int64_t embeddedSessionId = 0; int64_t embeddedMessageId = 0; int32_t embeddedSeqNo = 0; int64_t embeddedSalt = 0; int32_t topMessageSize = 0; if (_useUnauthorizedMode) { int64_t authKeyId = [is readInt64]; if (authKeyId != 0) { if (parseError != NULL) *parseError = true; return nil; } embeddedMessageId = [is readInt64:&readError]; if (readError) { if (parseError != NULL) *parseError = true; return nil; } topMessageSize = [is readInt32:&readError]; if (readError || topMessageSize < 4) { if (parseError != NULL) *parseError = true; return nil; } if (dataMessageId != 0) *dataMessageId = embeddedMessageId; } else { embeddedSalt = [is readInt64:&readError]; if (readError) { if (parseError != NULL) *parseError = true; return nil; } embeddedSessionId = [is readInt64:&readError]; if (readError) { if (parseError != NULL) *parseError = true; return nil; } if (embeddedSessionId != _sessionInfo.sessionId) { if (parseError != NULL) *parseError = true; return nil; } embeddedMessageId = [is readInt64:&readError]; if (readError) { if (parseError != NULL) *parseError = true; return nil; } embeddedSeqNo = [is readInt32:&readError]; if (readError) { if (parseError != NULL) *parseError = true; return nil; } [is readInt32:&readError]; if (readError) { if (parseError != NULL) *parseError = true; return nil; } } NSMutableData *topMessageData = [[NSMutableData alloc] init]; uint8_t buffer[128]; while (true) { NSInteger readBytes = [[is wrappedInputStream] read:buffer maxLength:128]; if (readBytes <= 0) break; [topMessageData appendBytes:buffer length:readBytes]; } id topObject = [self parseMessage:topMessageData]; if (topObject == nil) { if (parseError != NULL) *parseError = true; return nil; } NSMutableArray *messages = [[NSMutableArray alloc] init]; NSTimeInterval timestamp = embeddedMessageId / 4294967296.0; if ([topObject isKindOfClass:[MTMsgContainerMessage class]]) { for (MTMessage *subMessage in ((MTMsgContainerMessage *)topObject).messages) { id subObject = [self parseMessage:subMessage.data]; if (subObject == nil) { if (parseError != NULL) *parseError = true; return nil; } int64_t subMessageId = subMessage.messageId; int32_t subMessageSeqNo = subMessage.seqNo; int32_t subMessageLength = (int32_t)subMessage.data.length; [messages addObject:[[MTIncomingMessage alloc] initWithMessageId:subMessageId seqNo:subMessageSeqNo authKeyId:embeddedAuthKeyId sessionId:embeddedSessionId salt:embeddedSalt timestamp:timestamp size:subMessageLength body:subObject]]; } } else if ([topObject isKindOfClass:[MTMessage class]]) { MTMessage *message = topObject; id subObject = [self parseMessage:message.data]; if (subObject == nil) { if (parseError != NULL) *parseError = true; return nil; } int64_t subMessageId = message.messageId; int32_t subMessageSeqNo = message.seqNo; int32_t subMessageLength = (int32_t)message.data.length; [messages addObject:[[MTIncomingMessage alloc] initWithMessageId:subMessageId seqNo:subMessageSeqNo authKeyId:embeddedAuthKeyId sessionId:embeddedSessionId salt:embeddedSalt timestamp:timestamp size:subMessageLength body:subObject]]; } else [messages addObject:[[MTIncomingMessage alloc] initWithMessageId:embeddedMessageId seqNo:embeddedSeqNo authKeyId:embeddedAuthKeyId sessionId:embeddedSessionId salt:embeddedSalt timestamp:timestamp size:topMessageSize body:topObject]]; return messages; } - (void)_processIncomingMessage:(MTIncomingMessage *)incomingMessage totalSize:(int)totalSize withTransactionId:(id)transactionId address:(MTDatacenterAddress *)address authInfoSelector:(MTDatacenterAuthInfoSelector)authInfoSelector networkType:(int32_t)networkType { if ([_sessionInfo messageProcessed:incomingMessage.messageId]) { if (MTLogEnabled()) { MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p received duplicate message %" PRId64 "]", self, _context, incomingMessage.messageId); } MTShortLog(@"[MTProto#%p@%p received duplicate message %" PRId64 "]", self, _context, incomingMessage.messageId); [_sessionInfo scheduleMessageConfirmation:incomingMessage.messageId size:incomingMessage.size]; if ([_sessionInfo scheduledMessageConfirmationsExceedSize:MTMaxUnacknowledgedMessageSize orCount:MTMaxUnacknowledgedMessageCount]) [self requestTransportTransaction]; return; } if (MTLogEnabled()) { MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p [%d] received %@]", self, _context, totalSize, [self incomingMessageDescription:incomingMessage]); } [_sessionInfo setMessageProcessed:incomingMessage.messageId]; if (!_useUnauthorizedMode && incomingMessage.seqNo % 2 != 0) { [_sessionInfo scheduleMessageConfirmation:incomingMessage.messageId size:incomingMessage.size]; if ([_sessionInfo scheduledMessageConfirmationsExceedSize:MTMaxUnacknowledgedMessageSize orCount:MTMaxUnacknowledgedMessageCount]) [self requestTransportTransaction]; } if (!_useUnauthorizedMode && [incomingMessage.body isKindOfClass:[MTBadMsgNotificationMessage class]]) { MTBadMsgNotificationMessage *badMsgNotification = incomingMessage.body; int64_t badMessageId = badMsgNotification.badMessageId; NSArray *containerMessageIds = [_sessionInfo messageIdsInContainer:badMessageId]; if ([badMsgNotification isKindOfClass:[MTBadServerSaltNotificationMessage class]]) { if (_timeFixContext != nil && badMessageId == _timeFixContext.messageId) { _timeFixContext = nil; int64_t validSalt = ((MTBadServerSaltNotificationMessage *)badMsgNotification).nextServerSalt; NSTimeInterval timeDifference = incomingMessage.messageId / 4294967296.0 - [[NSDate date] timeIntervalSince1970]; [self completeTimeSync]; [self timeSyncInfoChanged:timeDifference saltList:@[[[MTDatacenterSaltInfo alloc] initWithSalt:validSalt firstValidMessageId:incomingMessage.messageId lastValidMessageId:incomingMessage.messageId + (4294967296 * 30 * 60)]] authInfoSelector:authInfoSelector]; } else [self initiateTimeSync]; } else { switch (badMsgNotification.errorCode) { case 16: case 17: { if (_timeFixContext != nil && badMessageId == _timeFixContext.messageId) { _timeFixContext = nil; NSTimeInterval timeDifference = incomingMessage.messageId / 4294967296.0 - [[NSDate date] timeIntervalSince1970]; [self completeTimeSync]; [self timeSyncInfoChanged:timeDifference saltList:nil authInfoSelector:authInfoSelector]; } else [self initiateTimeSync]; break; } case 32: case 33: { [self resetSessionInfo]; [self initiateTimeSync]; break; } case 48: { [self initiateTimeSync]; break; } default: break; } } for (NSInteger i = (NSInteger)_messageServices.count - 1; i >= 0; i--) { id messageService = _messageServices[(NSUInteger)i]; if ([messageService respondsToSelector:@selector(mtProto:messageDeliveryFailed:)]) { [messageService mtProto:self messageDeliveryFailed:badMessageId]; if (containerMessageIds != nil) { for (NSNumber *nMessageId in containerMessageIds) [messageService mtProto:self messageDeliveryFailed:(int64_t)[nMessageId longLongValue]]; } } } if ([self canAskForTransactions] || [self canAskForServiceTransactions]) [self requestTransportTransaction]; } else if ([incomingMessage.body isKindOfClass:[MTMsgsAckMessage class]]) { NSArray *messageIds = ((MTMsgsAckMessage *)incomingMessage.body).messageIds; for (NSInteger i = (NSInteger)_messageServices.count - 1; i >= 0; i--) { id messageService = _messageServices[(NSUInteger)i]; if ([messageService respondsToSelector:@selector(mtProto:messageDeliveryConfirmed:)]) [messageService mtProto:self messageDeliveryConfirmed:messageIds]; } } else if ([incomingMessage.body isKindOfClass:[MTMsgDetailedInfoMessage class]]) { MTMsgDetailedInfoMessage *detailedInfoMessage = incomingMessage.body; bool shouldRequest = false; if ([detailedInfoMessage isKindOfClass:[MTMsgDetailedResponseInfoMessage class]]) { int64_t requestMessageId = ((MTMsgDetailedResponseInfoMessage *)detailedInfoMessage).requestMessageId; if (MTLogEnabled()) { MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p detailed info %" PRId64 " is for %" PRId64 "", self, _context, incomingMessage.messageId, requestMessageId); } MTShortLog(@"[MTProto#%p@%p detailed info %" PRId64 " is for %" PRId64 "", self, _context, incomingMessage.messageId, requestMessageId); for (id messageService in _messageServices) { if ([messageService respondsToSelector:@selector(mtProto:shouldRequestMessageWithId:inResponseToMessageId:currentTransactionId:)]) { if ([messageService mtProto:self shouldRequestMessageWithId:detailedInfoMessage.responseMessageId inResponseToMessageId:requestMessageId currentTransactionId:transactionId]) { shouldRequest = true; break; } } } } else shouldRequest = true; if (shouldRequest) { [self requestMessageWithId:detailedInfoMessage.responseMessageId]; if (MTLogEnabled()) { MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p will request message %" PRId64 "", self, _context, detailedInfoMessage.responseMessageId); } MTShortLog(@"[MTProto#%p@%p will request message %" PRId64 "", self, _context, detailedInfoMessage.responseMessageId); } else { [_sessionInfo scheduleMessageConfirmation:detailedInfoMessage.responseMessageId size:(NSInteger)detailedInfoMessage.responseLength]; [self requestTransportTransaction]; } } else if ([incomingMessage.body isKindOfClass:[MTNewSessionCreatedMessage class]]) { int64_t firstValidMessageId = ((MTNewSessionCreatedMessage *)incomingMessage.body).firstMessageId; for (NSInteger i = (NSInteger)_messageServices.count - 1; i >= 0; i--) { id messageService = _messageServices[(NSUInteger)i]; if ([messageService respondsToSelector:@selector(mtProtoServerDidChangeSession:firstValidMessageId:otherValidMessageIds:)]) [messageService mtProtoServerDidChangeSession:self firstValidMessageId:firstValidMessageId otherValidMessageIds:[_sessionInfo messageIdsInContainersAfterMessageId:firstValidMessageId]]; } } else { for (NSInteger i = (NSInteger)_messageServices.count - 1; i >= 0; i--) { id messageService = _messageServices[(NSUInteger)i]; if ([messageService respondsToSelector:@selector(mtProto:receivedMessage:authInfoSelector:networkType:)]) [messageService mtProto:self receivedMessage:incomingMessage authInfoSelector:authInfoSelector networkType:networkType]; } if (_timeFixContext != nil && [incomingMessage.body isKindOfClass:[MTPongMessage class]] && ((MTPongMessage *)incomingMessage.body).messageId == _timeFixContext.messageId) { _timeFixContext = nil; [self completeTimeSync]; if ([self canAskForTransactions] || [self canAskForServiceTransactions]) [self requestTransportTransaction]; } } } - (void)contextDatacenterTransportSchemesUpdated:(MTContext *)context datacenterId:(NSInteger)datacenterId shouldReset:(bool)shouldReset { [[MTProto managerQueue] dispatchOnQueue:^ { if (context == _context && datacenterId == _datacenterId && ![self isStopped]) { bool resolvedShouldReset = shouldReset; if (_mtState & MTProtoStateAwaitingDatacenterScheme) { [self setMtState:_mtState & (~MTProtoStateAwaitingDatacenterScheme)]; resolvedShouldReset = true; } if ((_mtState & MTProtoStateAwaitingDatacenterAuthorization) == 0 && (_mtState & MTProtoStatePaused) == 0) { if (resolvedShouldReset) { [self resetTransport]; [self requestTransportTransaction]; } } } }]; } - (void)contextDatacenterAuthInfoUpdated:(MTContext *)context datacenterId:(NSInteger)datacenterId authInfo:(MTDatacenterAuthInfo *)authInfo selector:(MTDatacenterAuthInfoSelector)selector { [[MTProto managerQueue] dispatchOnQueue:^ { if (!_useUnauthorizedMode && context == _context && datacenterId == _datacenterId) { if (_awaitingAuthInfoForSelector != nil) { if ([_awaitingAuthInfoForSelector intValue] != selector) { return; } else if (authInfo != nil) { _awaitingAuthInfoForSelector = nil; } } else if (_validAuthInfo != nil) { if (_validAuthInfo.selector != selector) { return; } } else { return; } if (authInfo == nil) { _validAuthInfo = nil; _mtState |= MTProtoStateAwaitingDatacenterAuthorization; _awaitingAuthInfoForSelector = @(selector); } bool wasSuspended = _mtState & (MTProtoStateAwaitingDatacenterAuthorization); if (authInfo != nil) { if (_mtState & MTProtoStateAwaitingDatacenterAuthorization) { [self setMtState:_mtState & (~MTProtoStateAwaitingDatacenterAuthorization)]; } if ((_mtState & (MTProtoStateAwaitingDatacenterAuthorization)) == 0) { if (wasSuspended) { [self resetTransport]; [self requestTransportTransaction]; } } } else { [self resetTransport]; } } }]; } - (void)contextDatacenterAuthTokenUpdated:(MTContext *)context datacenterId:(NSInteger)datacenterId authToken:(id)authToken { [[MTProto managerQueue] dispatchOnQueue:^ { if (!_useUnauthorizedMode && context == _context && datacenterId == _datacenterId && _requiredAuthToken != nil && [_requiredAuthToken isEqual:authToken]) { if (_mtState & MTProtoStateAwaitingDatacenterAuthToken) { [self setMtState:_mtState & (~MTProtoStateAwaitingDatacenterAuthToken)]; [self resetTransport]; [self requestTransportTransaction]; } for (NSInteger i = (NSInteger)_messageServices.count - 1; i >= 0; i--) { id messageService = _messageServices[(NSUInteger)i]; if ([messageService respondsToSelector:@selector(mtProtoAuthTokenUpdated:)]) [messageService mtProtoAuthTokenUpdated:self]; } } }]; } - (void)timeSyncServiceCompleted:(MTTimeSyncMessageService *)timeSyncService timeDifference:(NSTimeInterval)timeDifference saltList:(NSArray *)saltList authInfoSelector:(MTDatacenterAuthInfoSelector)authInfoSelector { if ([_messageServices containsObject:timeSyncService]) { [self completeTimeSync]; [_messageServices removeObject:timeSyncService]; [self timeSyncInfoChanged:timeDifference saltList:saltList authInfoSelector:authInfoSelector]; } } - (void)timeSyncInfoChanged:(NSTimeInterval)timeDifference saltList:(NSArray *)saltList authInfoSelector:(MTDatacenterAuthInfoSelector)authInfoSelector { [_context setGlobalTimeDifference:timeDifference]; if (!_useUnauthorizedMode && saltList != nil) { if (_useExplicitAuthKey) { if (_validAuthInfo != nil && _validAuthInfo.selector == authInfoSelector) { MTDatacenterAuthInfo *updatedAuthInfo = [_validAuthInfo.authInfo mergeSaltSet:saltList forTimestamp:[_context globalTime]]; _validAuthInfo = [[MTProtoValidAuthInfo alloc] initWithAuthInfo:updatedAuthInfo selector:authInfoSelector]; } } else { MTDatacenterAuthInfo *authInfo = [_context authInfoForDatacenterWithId:_datacenterId selector:authInfoSelector]; if (authInfo != nil) { MTDatacenterAuthInfo *updatedAuthInfo = [authInfo mergeSaltSet:saltList forTimestamp:[_context globalTime]]; [_context updateAuthInfoForDatacenterWithId:_datacenterId authInfo:updatedAuthInfo selector:authInfoSelector]; if (_validAuthInfo != nil && _validAuthInfo.selector == authInfoSelector) { _validAuthInfo = [[MTProtoValidAuthInfo alloc] initWithAuthInfo:updatedAuthInfo selector:authInfoSelector]; } } } } if ([self canAskForTransactions] || [self canAskForServiceTransactions]) [self requestTransportTransaction]; } - (void)_messageResendRequestFailed:(int64_t)messageId { [[MTProto managerQueue] dispatchOnQueue:^ { for (id service in _messageServices) { if ([service respondsToSelector:@selector(mtProto:messageResendRequestFailed:)]) { [service mtProto:self messageResendRequestFailed:messageId]; } } }]; } - (void)contextDatacenterPublicKeysUpdated:(MTContext *)context datacenterId:(NSInteger)datacenterId publicKeys:(NSArray *)publicKeys { [[MTProto managerQueue] dispatchOnQueue:^{ for (id service in _messageServices) { if ([service respondsToSelector:@selector(mtProtoPublicKeysUpdated:datacenterId:publicKeys:)]) { [service mtProtoPublicKeysUpdated:self datacenterId:datacenterId publicKeys:publicKeys]; } } }]; } - (void)contextApiEnvironmentUpdated:(MTContext *)context apiEnvironment:(MTApiEnvironment *)apiEnvironment { [[MTProto managerQueue] dispatchOnQueue:^{ NSString *previousLangPackCode = _apiEnvironment.langPackCode; MTSocksProxySettings *previousSocksProxySettings = _apiEnvironment.socksProxySettings; _apiEnvironment = apiEnvironment; bool resetConnection = false; if ((_apiEnvironment.socksProxySettings != nil) != (previousSocksProxySettings != nil) || (previousSocksProxySettings != nil && ![_apiEnvironment.socksProxySettings isEqual:previousSocksProxySettings])) { resetConnection = true; } if (![_apiEnvironment.langPackCode isEqualToString:previousLangPackCode]) { resetConnection = true; } for (id service in _messageServices) { if ([service respondsToSelector:@selector(mtProtoApiEnvironmentUpdated:apiEnvironment:)]) { [service mtProtoApiEnvironmentUpdated:self apiEnvironment:apiEnvironment]; } } if (resetConnection) { [self resetTransport]; [self requestTransportTransaction]; } }]; } @end