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>
49 lines
1.6 KiB
C++
49 lines
1.6 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
|
|
*/
|
|
#pragma once
|
|
|
|
#include "mtproto/proxy/data.h"
|
|
|
|
namespace MTP::details {
|
|
|
|
enum class ClientHelloProfileValidation {
|
|
Claimed,
|
|
Validated,
|
|
};
|
|
|
|
struct ClientHelloProfileInfo {
|
|
ProxyTlsProfile profile = ProxyTlsProfile::Auto;
|
|
const char *id = "";
|
|
ClientHelloProfileValidation validation
|
|
= ClientHelloProfileValidation::Claimed;
|
|
const char *captureSource = "";
|
|
const char *captureVersion = "";
|
|
const char *expectedJa4 = "";
|
|
// A fingerprint measured to be refused by a network the client has to
|
|
// work on. The template stays here - it is still a correct capture, and
|
|
// the JA4 guard still checks it - but nothing sends it any more. See
|
|
// kWithheldReason for what was measured.
|
|
bool withheld = false;
|
|
const char *withheldReason = "";
|
|
};
|
|
|
|
// Plain lookup: answers about the named profile, including a withheld one.
|
|
[[nodiscard]] const ClientHelloProfileInfo &ClientHelloProfile(
|
|
ProxyTlsProfile profile);
|
|
|
|
// What to actually send for a configured value: resolves the two auto names
|
|
// and steers away from a withheld fingerprint, so a setting saved before the
|
|
// measurement does not keep a client on a template no relay ever answers.
|
|
[[nodiscard]] ProxyTlsProfile EffectiveClientHelloProfile(
|
|
ProxyTlsProfile profile);
|
|
|
|
[[nodiscard]] ProxyTlsProfile DefaultClientHelloProfile();
|
|
|
|
[[nodiscard]] bool IsClientHelloProfileValidated(ProxyTlsProfile profile);
|
|
|
|
} // namespace MTP::details
|