Every session dialled a proxy on its own with nothing in between, so a cold start or a proxy switch opened one socket per session per account in the same millisecond. A public mtproxy answers about two parallel handshakes and silently drops the rest, which the client read back as client_hello_sent_no_server_hello or connected_no_mtproto_data across the whole batch - including on a dd-secret proxy that sends no ClientHello at all and therefore cannot be fingerprint-blocked. Replace the machinery that was supposed to prevent this with a rate limiter that actually does. ProxyDialLease keeps at most two unproven handshakes in flight per proxy server, spaces the rest apart and stretches the spacing after a run of attempts that proved nothing, so a blackholed proxy is no longer redialled by every session on its own eight second timer. Everything that reacted to failure by changing its own behaviour is gone: EndpointAdmissionArbiter, EndpointLivePool, ConnectionBroker, session_proxy_adapter, SessionProxyPort, HandshakeGate, open_scheduler, endpoint_health, adaptive_policy and ProxyRotationManager. A fingerprint filter is deterministic, so rotating emulated ClientHello profiles only hands the other side more of them, and escalated recipes (fragmentation, pacing) make the flow less browser-like rather than more - while the signal that drove the escalation could not tell a DPI box from a proxy refusing extra connections. The ClientHello templates themselves are untouched and the profile is now whatever the user configured, fixed. Also: ServerHello budget 2.5s -> 5s, mtproxy status reduces through the same ProxyConnectionStatus path as every other proxy type, and route memory is wired back up so an address that answered is dialled first and one that failed is dialled last. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
221 lines
8.5 KiB
Python
221 lines
8.5 KiB
Python
from pathlib import Path
|
|
|
|
|
|
SOURCE_DIR = Path(__file__).resolve().parents[1]
|
|
ROOT = SOURCE_DIR.parents[1]
|
|
CMAKE = ROOT / "Telegram" / "CMakeLists.txt"
|
|
PROXY_DIR = SOURCE_DIR / "mtproto" / "proxy"
|
|
CAPABILITIES_H = PROXY_DIR / "capabilities.h"
|
|
CAPABILITIES_CPP = PROXY_DIR / "capabilities.cpp"
|
|
PROXY_SERVICES_CPP = PROXY_DIR / "proxy_services.cpp"
|
|
PROXY_SERVICES_H = PROXY_DIR / "proxy_services.h"
|
|
RUNTIME_CPP = SOURCE_DIR / "mtproto" / "runtime" / "runtime_environment.cpp"
|
|
TRANSPORT_POLICY_CPP = PROXY_DIR / "transport_policy.cpp"
|
|
ENDPOINT_HEALTH_LIFECYCLE_CPP = (
|
|
PROXY_DIR / "mtproxy" / "endpoint_health_lifecycle.cpp")
|
|
ENDPOINT_HEALTH_CAPABILITIES_CPP = (
|
|
PROXY_DIR / "mtproxy" / "endpoint_health_capabilities.cpp")
|
|
TLS_SOCKET_CPP = PROXY_DIR / "mtproxy" / "tls_socket.cpp"
|
|
TLS_SOCKET_RECORDS_CPP = PROXY_DIR / "mtproxy" / "tls_socket_records.cpp"
|
|
|
|
|
|
def read(path):
|
|
assert path.exists(), f"missing expected source file: {path}"
|
|
return path.read_text(encoding="utf-8")
|
|
|
|
|
|
def read_endpoint_health_sources():
|
|
return "\n".join(read(path) for path in (
|
|
ENDPOINT_HEALTH_CPP,
|
|
ENDPOINT_HEALTH_LIFECYCLE_CPP,
|
|
))
|
|
|
|
|
|
def function_body(source, signature):
|
|
start = source.index(signature)
|
|
brace = source.index("{", start)
|
|
depth = 0
|
|
for i in range(brace, len(source)):
|
|
if source[i] == "{":
|
|
depth += 1
|
|
elif source[i] == "}":
|
|
depth -= 1
|
|
if depth == 0:
|
|
return source[brace:i + 1]
|
|
raise AssertionError(f"function body not found: {signature}")
|
|
|
|
|
|
def test_capability_cache_module_is_file_backed_and_registered():
|
|
header = read(CAPABILITIES_H)
|
|
source = read(CAPABILITIES_CPP)
|
|
runtime = read(RUNTIME_CPP)
|
|
services = read(PROXY_SERVICES_CPP)
|
|
cmake = read(CMAKE)
|
|
|
|
assert "mtproto/proxy/capabilities.cpp" in cmake
|
|
assert "mtproto/proxy/capabilities.h" in cmake
|
|
assert "struct ProxyCapabilityCard" in header
|
|
assert "class ProxyCapabilityCache final" in header
|
|
assert "ProxyCapabilityCache &Instance()" not in header
|
|
assert "ProxyCapabilityCache _capabilities;" in read(PROXY_SERVICES_H)
|
|
assert "QJsonDocument" in source
|
|
assert "QSaveFile" in source
|
|
assert 'u"proxy-capabilities.json"_q' in runtime
|
|
assert "cWorkingDir() + u\"tdata/\"_q" in runtime
|
|
assert "QDir().mkpath(" in runtime
|
|
assert "_capabilities(runtime->proxyCapabilities().path)" in services
|
|
assert "SetProxyCapabilityPathProvider" not in runtime
|
|
assert "CapabilitiesPath()" not in source
|
|
assert "QString ProxyCapabilityCache::path() const" in source
|
|
assert "load()" in source
|
|
assert "save()" in source
|
|
|
|
|
|
def test_capability_card_contains_transport_profile_flags_and_routes():
|
|
header = read(CAPABILITIES_H)
|
|
source = read(CAPABILITIES_CPP)
|
|
|
|
for field in (
|
|
"QString proxyKey;",
|
|
"ProxyCapabilityTransport lastGoodTransport",
|
|
"QString lastGoodRoute;",
|
|
"ProxyTlsProfile lastGoodProfile",
|
|
"int lastGoodRecipeLevel",
|
|
"bool relayProven",
|
|
"bool autoRotateAllowed",
|
|
"bool wssAllowed",
|
|
"bool syntheticPskAllowed",
|
|
"bool fragmentationAllowed",
|
|
"crl::time lastSuccessAt",
|
|
"crl::time relayProvenAt",
|
|
"QString lastFailureClass;",
|
|
"std::vector<QString> badRoutes;",
|
|
"std::vector<QString> goodRoutes;",
|
|
"crl::time wssBlockedUntil",
|
|
):
|
|
assert field in header
|
|
for json_key in (
|
|
'"proxyKey"',
|
|
'"lastGoodTransport"',
|
|
'"lastGoodRoute"',
|
|
'"lastGoodProfile"',
|
|
'"lastGoodRecipeLevel"',
|
|
'"relayProven"',
|
|
'"autoRotateAllowed"',
|
|
'"wssAllowed"',
|
|
'"syntheticPskAllowed"',
|
|
'"fragmentationAllowed"',
|
|
'"lastSuccessAt"',
|
|
'"relayProvenAt"',
|
|
'"lastFailureClass"',
|
|
'"badRoutes"',
|
|
'"goodRoutes"',
|
|
'"wssBlockedUntil"',
|
|
):
|
|
assert json_key in source
|
|
assert 'u"MtproxyFakeTlsTcp"_q' in source
|
|
assert 'u"Wss"_q' in source
|
|
|
|
|
|
def test_proxy_capability_key_uses_canonical_identity_not_route_ip():
|
|
source = read(CAPABILITIES_CPP)
|
|
body = function_body(source, "QString ProxyCapabilityKey(")
|
|
|
|
assert "ProxyCapabilityHost(proxy)" in body
|
|
assert "proxy.originalHost.isEmpty()" in source
|
|
assert "? proxy.host" in source
|
|
assert ": proxy.originalHost" in source
|
|
assert "proxy.host +" not in body
|
|
assert "QString::number(int(proxy.type))" in body
|
|
assert "QString::number(proxy.port)" in body
|
|
assert "ProxyCapabilitySecretHash(proxy)" in body
|
|
assert "QCryptographicHash::Sha256" in source
|
|
|
|
|
|
def test_wss_remote_closed_is_persisted_with_ttl_per_proxy():
|
|
source = read(TRANSPORT_POLICY_CPP)
|
|
capabilities = read(CAPABILITIES_CPP)
|
|
note_body = function_body(source, "void NoteProxyWssRemoteClosed(")
|
|
allowed_body = function_body(source, "bool ProxyWssAllowed(")
|
|
|
|
assert "kWssRemoteClosedTtl = crl::time(" in source
|
|
assert "runtime->proxyServices().capabilities().noteWssRemoteClosed(" in (
|
|
note_body)
|
|
assert "kWssRemoteClosedTtl" in note_body
|
|
assert "runtime->proxyServices().capabilities().wssAllowed(proxy)" in (
|
|
allowed_body)
|
|
assert "std::set<QString> WssForbiddenProxyKeys" not in source
|
|
assert "QMutex WssForbiddenProxyKeysMutex" not in source
|
|
assert "card.wssAllowed = false;" in capabilities
|
|
assert "card.wssBlockedUntil = crl::now() + ttl;" in capabilities
|
|
|
|
|
|
def test_mtproxy_relay_success_persists_boring_last_good_path():
|
|
header = read(CAPABILITIES_H)
|
|
source = read(CAPABILITIES_CPP)
|
|
success = function_body(source, "void ProxyCapabilityCache::noteMtproxySuccess(")
|
|
read_card = function_body(source, "ProxyCapabilityCard ReadCard(")
|
|
write_card = function_body(source, "QJsonObject WriteCard(")
|
|
|
|
assert "QString lastGoodRoute;" in header
|
|
assert "int lastGoodRecipeLevel = 0;" in header
|
|
assert "bool relayProven = false;" in header
|
|
assert "crl::time relayProvenAt = 0;" in header
|
|
assert "bool autoRotateAllowed = true;" in header
|
|
|
|
for json_key in (
|
|
'"lastGoodRoute"',
|
|
'"lastGoodRecipeLevel"',
|
|
'"relayProven"',
|
|
'"relayProvenAt"',
|
|
'"autoRotateAllowed"',
|
|
):
|
|
assert json_key in read_card
|
|
assert json_key in write_card
|
|
|
|
assert "const QString &lastGoodRoute" in source
|
|
assert "int recipeLevel" in source
|
|
assert "bool relayProven" in source
|
|
assert "const auto now = crl::now();" in success
|
|
assert "card.lastGoodRoute = lastGoodRoute;" in success
|
|
assert "card.lastGoodRecipeLevel = recipeLevel;" in success
|
|
assert "card.relayProven = relayProven;" in success
|
|
assert "card.relayProvenAt = relayProven ? now : 0;" in success
|
|
assert "card.autoRotateAllowed = false;" in success
|
|
assert "card.syntheticPskAllowed = stealth.syntheticPsk;" in success
|
|
assert ("card.fragmentationAllowed = (stealth.clientHelloFragmentation\n"
|
|
"\t\t!= ProxyClientHelloFragmentation::Off);") in success
|
|
assert "if (stealth.syntheticPsk) {" not in success
|
|
assert "card.syntheticPskAllowed = true;" not in success
|
|
assert "card.fragmentationAllowed = true;" not in success
|
|
|
|
|
|
def test_legacy_relay_cache_uses_last_success_as_proof_time():
|
|
source = read(CAPABILITIES_CPP)
|
|
read_card = function_body(source, "ProxyCapabilityCard ReadCard(")
|
|
|
|
assert "const auto relayProvenAt = crl::time(" in read_card
|
|
assert "object.value(\"relayProvenAt\").toDouble()" in read_card
|
|
assert "result.relayProven" in read_card
|
|
assert "result.lastSuccessAt" in read_card
|
|
|
|
|
|
def test_last_good_capability_is_used_before_saved_mtproxy_experiments():
|
|
source = read(TRANSPORT_POLICY_CPP)
|
|
body = function_body(source, "ProxyStealthOptions EffectiveProxyStealthOptions(")
|
|
mtproxy_branch = body.split(
|
|
"proxy.type == ProxyData::Type::Mtproto) {", 1)[1].split(
|
|
"if (settings == ProxyData::Settings::Enabled", 1)[0]
|
|
|
|
assert "runtime->proxyServices().capabilities().lookup(proxy)" in body
|
|
assert "capability.lastGoodTransport" in body
|
|
assert "ProxyCapabilityTransport::MtproxyFakeTlsTcp" in body
|
|
assert "capability.relayProven" in body
|
|
assert "FreshMtproxyRelayProof(card)" in read(CAPABILITIES_CPP)
|
|
assert "capability.lastGoodRecipeLevel == 0" in body
|
|
assert "!capability.autoRotateAllowed" in body
|
|
assert "capability.lastGoodProfile" in body
|
|
assert "CompatStrictProxyStealthOptions(std::move(result))" in body
|
|
assert "capability.syntheticPskAllowed" not in mtproxy_branch
|
|
assert "capability.fragmentationAllowed" not in mtproxy_branch
|
|
assert "result.level == ProxyStealthLevel::Experimental" not in mtproxy_branch
|