zapret-kvn/xray_fluent/engines/zapret/blobs.py
loop-uh d5a4715771
Some checks failed
Windows project source guards / test (push) Has been cancelled
feat: use official Amnezia transport and organize runtime modules
2026-09-06 00:14:08 +03:00

209 lines
8.1 KiB
Python

"""Central winws2 blob and lua-extension registry.
winws2 resolves ``blob=<name>`` references inside ``--lua-desync`` arguments
against ``--blob=<name>:<source>`` definitions that must be present in the
global part of the command line. Upstream zapret spreads those definitions
across every preset file; keeping a single registry here means a strategy
imported from the catalog stays runnable regardless of which preset hosts it.
"""
from __future__ import annotations
import re
from typing import Iterable
from ...constants import BASE_DIR
_BIN_DIR = BASE_DIR / "zapret" / "bin"
_LUA_DIR = BASE_DIR / "zapret" / "lua"
#: Blob name -> ``--blob=`` source, either ``@bin/<file>`` or an inline hex literal.
BLOB_REGISTRY: dict[str, str] = {
"discord_active": "@bin/ACTIVE_DISCORD_UDP.bin",
"dtls_w3": "@bin/dtls_clienthello_w3_org.bin",
"fake_dbankcloud": "@bin/quic_initial_dbankcloud_ru.bin",
"fake_http_max": "@bin/tls_clienthello_max_ru.bin",
"fake_quic": "@bin/fake_quic.bin",
"fake_quic_1": "@bin/fake_quic_1.bin",
"fake_quic_2": "@bin/fake_quic_2.bin",
"fake_quic_3": "@bin/fake_quic_3.bin",
"fake_stun": "@bin/stun.bin",
"fake_stun_as_tls": "@bin/stun.bin",
"fake_tls": "@bin/fake_tls_1.bin",
"fake_tls_1": "@bin/fake_tls_1.bin",
"fake_tls_2": "@bin/fake_tls_2.bin",
"fake_tls_3": "@bin/fake_tls_3.bin",
"fake_tls_4": "@bin/fake_tls_4.bin",
"fake_tls_5": "@bin/fake_tls_5.bin",
"fake_tls_6": "@bin/fake_tls_6.bin",
"fake_tls_7": "@bin/fake_tls_7.bin",
"fake_tls_8": "@bin/fake_tls_8.bin",
"game_active": "@bin/ACTIVE_GAME_UDP.bin",
"http_max": "@bin/tls_clienthello_max_ru.bin",
"http_req": "@bin/http_iana_org.bin",
"quic1": "@bin/quic_1.bin",
"quic2": "@bin/quic_2.bin",
"quic3": "@bin/quic_3.bin",
"quic4": "@bin/quic_4.bin",
"quic5": "@bin/quic_5.bin",
"quic6": "@bin/quic_6.bin",
"quic7": "@bin/quic_7.bin",
"quic_4pda": "@bin/quic_initial_4pda.to.bin",
"quic_google": "@bin/quic_initial_www_google_com.bin",
"quic_test": "@bin/quic_test_00.bin",
"quic_vk": "@bin/quic_initial_vk_com.bin",
"stun2": "@bin/stun2.bin",
"stun_pat": "@bin/stun.bin",
"syn_packet": "@bin/syn_packet.bin",
"syndata3": "@bin/tls_clienthello_3.bin",
"tls1": "@bin/tls_clienthello_1.bin",
"tls10": "@bin/tls_clienthello_10.bin",
"tls11": "@bin/tls_clienthello_11.bin",
"tls12": "@bin/tls_clienthello_12.bin",
"tls13": "@bin/tls_clienthello_13.bin",
"tls14": "@bin/tls_clienthello_14.bin",
"tls17": "@bin/tls_clienthello_17.bin",
"tls18": "@bin/tls_clienthello_18.bin",
"tls2": "@bin/tls_clienthello_2.bin",
"tls2n": "@bin/tls_clienthello_2n.bin",
"tls3": "@bin/tls_clienthello_3.bin",
"tls4": "@bin/tls_clienthello_4.bin",
"tls5": "@bin/tls_clienthello_5.bin",
"tls6": "@bin/tls_clienthello_6.bin",
"tls7": "@bin/tls_clienthello_7.bin",
"tls8": "@bin/tls_clienthello_8.bin",
"tls9": "@bin/tls_clienthello_9.bin",
"tls_4pda": "@bin/tls_clienthello_4pda_to.bin",
"tls_deepseek": "@bin/tls_clienthello_chat_deepseek_com.bin",
"tls_google": "@bin/tls_clienthello_www_google_com.bin",
"tls_gosuslugi": "@bin/tls_clienthello_gosuslugi_ru.bin",
"tls_iana": "@bin/tls_clienthello_iana_org.bin",
"tls_max": "@bin/tls_clienthello_max_ru.bin",
"tls_sber": "@bin/tls_clienthello_sberbank_ru.bin",
"tls_sber_v2": "@bin/tls_clienthello_sberbank_ru_v2.bin",
"tls_stun": "@bin/stun.bin",
"tls_vk": "@bin/tls_clienthello_vk_com.bin",
"tls_vk_kyber": "@bin/tls_clienthello_vk_com_kyber.bin",
"zero256": "@bin/zero_256.bin",
"fake_default_udp": "0x00000000000000000000000000000000",
"hex_00": "0x00",
"hex_0e0e0f0e": "0x0E0E0F0E",
"hex_0f0e0e0f": "0x0F0E0E0F",
"hex_0f0f0f0f": "0x0F0F0F0F",
"tls_zero4": "0x00000000",
"zero64": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
}
#: Names the engine provides on its own. Declaring them breaks winws2 startup.
BUILTIN_BLOBS = frozenset({
"fake_default_tls",
"fake_default_quic",
"fake_default_http",
"fake_unknown_256",
"fake_zero64",
})
#: Always loaded, in this order, before any strategy argument.
CORE_LUA_FILES: tuple[str, ...] = ("zapret-lib.lua", "zapret-antidpi.lua")
#: desync function -> lua file that defines it, for functions living outside the core.
#: ``tests/test_zapret_blobs.py`` re-derives this map from the shipped lua sources.
LUA_EXTENSION_FUNCS: dict[str, str] = {
"discord_ecn_exploit": "custom_funcs.lua",
"discord_router_alert": "custom_funcs.lua",
"discord_timestamp_travel": "custom_funcs.lua",
"discord_ultimate_combo": "custom_funcs.lua",
"discord_urgent_sni": "custom_funcs.lua",
"discord_window_collapse": "custom_funcs.lua",
"fakemultidisorder": "fakemultidisorder.lua",
"fakemultisplit": "fakemultisplit.lua",
"hostfakesplit_multi": "zapret-multishake.lua",
"multisplit_tls": "custom_funcs.lua",
"tls_aggressive": "custom_funcs.lua",
"tls_disorder_gentle": "custom_funcs.lua",
"tls_fake_disorder_gentle": "custom_funcs.lua",
"tls_fake_flood": "custom_funcs.lua",
"tls_fake_simple": "custom_funcs.lua",
"tls_fake_split": "custom_funcs.lua",
"tls_multisplit_sni": "custom_funcs.lua",
"tls_split_gentle": "custom_funcs.lua",
}
# ``fake_blob=`` and ``fakedsplit_pattern=`` are covered by the two alternatives
# below because both end in ``blob=`` / ``pattern=`` respectively.
_BLOB_REFERENCE_RE = re.compile(r"(?:blob|pattern)=([A-Za-z0-9_]+)")
_DESYNC_FUNC_RE = re.compile(r"--lua-desync=([a-z0-9_]+)", re.IGNORECASE)
def blob_names_in_args(args: Iterable[str]) -> tuple[str, ...]:
"""Blob names a strategy actually references, in first-seen order.
The catalog's own ``blobs =`` metadata is unreliable — 14 names used by
winws2 arguments are never declared there — so dependencies are derived
from the arguments themselves. Inline ``0x…`` literals need no definition.
"""
found: dict[str, None] = {}
for argument in args:
for name in _BLOB_REFERENCE_RE.findall(str(argument)):
if name.lower().startswith("0x") or name in BUILTIN_BLOBS:
continue
found.setdefault(name, None)
return tuple(found)
def blob_argument(name: str) -> str | None:
"""``--blob=`` definition for one name, or None when it is not resolvable."""
source = BLOB_REGISTRY.get(name)
return f"--blob={name}:{source}" if source else None
def blob_arguments(names: Iterable[str]) -> tuple[str, ...]:
"""``--blob=`` definitions for every resolvable name, deduplicated."""
seen: dict[str, None] = {}
for name in names:
argument = blob_argument(name)
if argument:
seen.setdefault(argument, None)
return tuple(seen)
def unresolved_blob_names(names: Iterable[str]) -> tuple[str, ...]:
"""Names that are neither engine builtins nor present in the registry."""
return tuple(
name for name in dict.fromkeys(names)
if name not in BLOB_REGISTRY and name not in BUILTIN_BLOBS
)
def missing_blob_files() -> tuple[str, ...]:
"""Registry entries whose backing ``@bin`` file is absent from the shipment."""
missing = []
for name, source in sorted(BLOB_REGISTRY.items()):
if not source.startswith("@bin/"):
continue
if not (_BIN_DIR / source.removeprefix("@bin/")).is_file():
missing.append(name)
return tuple(missing)
def lua_files_for_args(args: Iterable[str]) -> tuple[str, ...]:
"""Extension lua files required by the desync functions used in ``args``."""
seen: dict[str, None] = {}
for argument in args:
for func in _DESYNC_FUNC_RE.findall(str(argument)):
lua_file = LUA_EXTENSION_FUNCS.get(func.lower())
if lua_file and (_LUA_DIR / lua_file).is_file():
seen.setdefault(lua_file, None)
return tuple(seen)
def lua_init_arguments(args: Iterable[str]) -> tuple[str, ...]:
"""``--lua-init=`` arguments needed on top of the core ones for ``args``."""
return tuple(f"--lua-init=@lua/{name}" for name in lua_files_for_args(args))