Skip to content

MtProtoKit: dont drop the salt from bad_server_salt on normal requests (fixes infinite "Connecting" after backgrounding on iOS)#2197

Open
sleep3r wants to merge 1 commit into
TelegramMessenger:masterfrom
sleep3r:fix-bad-server-salt-resume-hang
Open

MtProtoKit: dont drop the salt from bad_server_salt on normal requests (fixes infinite "Connecting" after backgrounding on iOS)#2197
sleep3r wants to merge 1 commit into
TelegramMessenger:masterfrom
sleep3r:fix-bad-server-salt-resume-hang

Conversation

@sleep3r

@sleep3r sleep3r commented Jun 12, 2026

Copy link
Copy Markdown

looked into the thing from telemt/telemt#582 (ios gets stuck on "Connecting"/"Updating" basically forever after the app has been in background for a while, force-killing the app fixes it, android on the same network and same server is totally fine). turns out it's a salt handling bug in MtProtoKit and has nothing to do with the proxy

what's going on

after a longer background the cached server salt is stale. on the new connection the dc answers bad_server_salt almost immediately and that message already carries a fresh valid salt. but in -[MTProto _processIncomingMessage:] the bad_server_salt branch only applies the salt when the rejected message is the in-flight time fix ping:

if (_timeFixContext != nil && badMessageId == _timeFixContext.messageId) {
    // ...applies nextServerSalt...
} else {
    [self initiateTimeSync]; // the salt the server just handed us gets thrown away
}

on resume what you actually send is normal requests, so it goes into the else, drops the salt and calls initiateTimeSync, which sets MTProtoStateAwaitingTimeFixAndSalts. while that bit is set canAskForTransactions is false, so none of the queued requests ever leave. from there recovery depends entirely on the time fix ping coming back, and nothing rescues it if it doesnt:

  • _timeFixContext has no timeout (timeFixAbsoluteStartTime is stored but never read anywhere)
  • the 12s / 5s response/request timers get disarmed by the incoming reply and never re-armed, since nothing more is sent
  • the sleep watchdog in MTTcpTransport is behind #if false

so the session just sits there silent until the dc closes the socket from its side (~90-120s), and only the reconnect after that actually works. that's the stuck "Connecting"/"Updating" people are seeing. android (tgnet) doesnt hit it because it drops idle connections a few seconds into background, so it always comes back on a fresh session

the fix

the salt in bad_server_salt comes inside the aes-ige envelope under the auth key, so it's exactly as trustworthy when it rejects a normal request as when it rejects the time fix ping. so just apply it in both cases and keep the _timeFixContext check only for clearing the context:

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];
}

completeTimeSync is a no-op when the awaiting flag isnt set, and timeSyncInfoChanged: already merges the salt and calls requestTransportTransaction, so the queued requests go straight out with the right salt. the matched (time fix) path behaves exactly like before. left the bad_msg codes 16/17/32/33/48 as is since those are about msg id / time, not the salt

with this the whole resume batch (getDifference included) goes through in well under a second on the first bad_server_salt, instead of hanging until the server gives up on the connection

…only the time-fix ping

fixes the infinite Connecting/Updating on iOS after backgrounding (telemt/telemt#582)
@arsenyinfo

Copy link
Copy Markdown

No findings. Great job! 🎉


🔍 Reviewed by nitpicker

@CLAassistant

CLAassistant commented Jun 15, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants