ZaStoGram_desktop/Telegram/SourceFiles/mtproto/proxy/mtproxy/handshake_plan.cpp
loop-uh b334f9c137 Stop sending the fingerprint a filtered network refuses
Measured 26 July 2026 against one relay from two networks, twelve attempts
per profile per network, every hello built from the client's own templates.
Unfiltered, all six profiles are answered in under fifty milliseconds.
Filtered, the four that keep a fixed extension order are answered 12/12,
while chrome_modern and android_chrome are answered 3/12 and get silence
otherwise - same relay, same secret, same minutes, same machine. The
post-quantum key share is not the trait being refused: three of the four
that pass carry it too. Extension permutation is the only structural trait
the two refused ones share and the four survivors lack.

The client defaulted to chrome_modern, so on such a network no proxy ever
completed a handshake while a plain Python probe on the same machine in the
same minute did. That is what "our client breaks working proxies" was.

Auto now resolves to yandex, and a refused fingerprint is steered away from
at both places a setting becomes a hello, so a hand-picked chrome_modern
does not keep a client mute either. The templates stay in the table with
their capture metadata - the JA4 guard still checks one - they are simply
never sent.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 15:55:21 +03:00

51 lines
1.8 KiB
C++

/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "mtproto/proxy/mtproxy/handshake_plan.h"
#include "mtproto/proxy/mtproxy/client_hello_profile.h"
namespace MTP::details::MtProxy {
namespace {
// A proxy behind a slow or congested link needs more than a couple of
// seconds to relay the ServerHello back; cutting the attempt off early only
// burns another handshake against the same box.
constexpr auto kServerHelloTimeout = crl::time(5000);
} // namespace
MtProxyAttemptPlan MakeAttemptPlan(const ProxyStealthOptions &stealth) {
// The two auto entries are names for "whatever the client defaults to",
// so they resolve to a concrete template here - the plan is what the
// diagnostics report as the profile that was actually sent. A withheld
// fingerprint resolves away as well, including one a user selected before
// it was measured as refused.
const auto effective = EffectiveClientHelloProfile(stealth.tlsProfile);
auto planned = stealth;
planned.tlsProfile = effective;
return {
.admitted = true,
.configuredTlsProfile = stealth.tlsProfile,
.effectiveTlsProfile = effective,
.stealth = planned,
.serverHelloTimeout = kServerHelloTimeout,
};
}
crl::time ConnectionSpacing(ProxyConnectionPattern pattern) {
switch (pattern) {
case ProxyConnectionPattern::Soft: return crl::time(150);
case ProxyConnectionPattern::Quiet: return crl::time(400);
case ProxyConnectionPattern::Strict: return crl::time(700);
case ProxyConnectionPattern::Browser: return crl::time(250);
case ProxyConnectionPattern::Off: break;
}
return crl::time(0);
}
} // namespace MTP::details::MtProxy