zapret-kvn/tests/test_tls_fingerprint.py
loop-uh 70ac787146
Some checks failed
Windows project source guards / test (push) Has been cancelled
Проверки не выдают серверы: рукопожатие Chrome, имя сервера не уходит в открытый DNS
Требование незаметности: ни проверки, ни подключение не должны выдавать
провайдеру (ТСПУ) VPN-серверы.

- Без fp в ссылке рукопожатие с сервером — uTLS chrome, а не ClientHello
  библиотеки Go, который отличим с первого пакета. Значение из ссылки
  сохраняется, QUIC (HTTP/3, mKCP) не трогаем, хранимый узел не меняется —
  отпечаток подставляется во все конфиги ядра (sing-box, Xray, пул, замер).
- Фоновое обновление адреса больше не спрашивает системный резолвер про
  имя сервера при каждом подключении. Подмена DNS выводится только из
  запроса, который всё равно понадобился (DoH недоступен, провайдер говорит
  «имени нет», а кеш его знает).
- После подключения имена всех серверов пула больше не резолвятся (DoH к
  пяти резолверам на каждое имя) ради маскировки журнала: регистрируются
  только адреса из кеша; цель горячего переключения резолвится перед ним.
- Подписка по http:// к адресу в интернете грузится только через туннель:
  напрямую провайдер увидел бы весь список серверов; редирект на http://
  на прямом пути отклоняется. Локальная сеть — как раньше.
- Проверка связи — браузерный User-Agent вместо ZapretKVN/0.4.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 17:57:00 +03:00

65 lines
3.3 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from __future__ import annotations
import unittest
from xray_fluent.engines.singbox.config_builder import build_singbox_outbound
from xray_fluent.engines.tls_fingerprint import BROWSER_TLS_FINGERPRINT, apply_browser_fingerprint
from xray_fluent.engines.xray.config_builder import build_xray_config
from xray_fluent.importer.link_parser import parse_single
from xray_fluent.network.speed_test_worker import build_speed_test_config
from xray_fluent.profiles.models import AppSettings, RoutingSettings
UUID = "11111111-2222-3333-4444-555555555555"
REALITY = (
f"vless://{UUID}@vpn.example.com:443?security=reality&sni=www.example.com"
"&pbk=Z84J2IelR9ch3k8VtlVhhs5ycBUlXA7wHBWcBrjqnAw&sid=ab&type=tcp#r"
)
def _proxy(config: dict) -> dict:
return next(item for item in config["outbounds"] if item.get("tag") == "proxy")
class SingBoxFingerprintTests(unittest.TestCase):
"""Без fp в ссылке рукопожатие с сервером — как у Chrome, а не ClientHello Go."""
def test_trojan_without_fp_gets_chrome(self) -> None:
node = parse_single("trojan://secret@vpn.example.com:443?sni=vpn.example.com#t")
tls = build_singbox_outbound(node)["tls"]
self.assertEqual(tls["utls"], {"enabled": True, "fingerprint": BROWSER_TLS_FINGERPRINT})
def test_explicit_fp_is_kept(self) -> None:
node = parse_single("trojan://secret@vpn.example.com:443?sni=vpn.example.com&fp=firefox#t")
self.assertEqual(build_singbox_outbound(node)["tls"]["utls"]["fingerprint"], "firefox")
def test_plain_tcp_without_tls_is_untouched(self) -> None:
node = parse_single("ss://YWVzLTI1Ni1nY206c2VjcmV0@vpn.example.com:8388#s")
self.assertNotIn("tls", build_singbox_outbound(node))
class XrayFingerprintTests(unittest.TestCase):
def test_reality_without_fp_gets_chrome_in_runtime_config(self) -> None:
node = parse_single(REALITY)
self.assertNotIn("fingerprint", node.outbound["streamSettings"]["realitySettings"])
proxy = _proxy(build_xray_config(node, RoutingSettings(), AppSettings()))
self.assertEqual(proxy["streamSettings"]["realitySettings"]["fingerprint"], BROWSER_TLS_FINGERPRINT)
# Хранимый узел не меняется: отпечаток подставляется только в конфиг ядра.
self.assertNotIn("fingerprint", node.outbound["streamSettings"]["realitySettings"])
def test_speed_test_core_uses_the_same_handshake(self) -> None:
proxy = _proxy(build_speed_test_config(parse_single(REALITY), 18080))
self.assertEqual(proxy["streamSettings"]["realitySettings"]["fingerprint"], BROWSER_TLS_FINGERPRINT)
def test_quic_transport_is_left_alone(self) -> None:
outbound = {"streamSettings": {"network": "xhttp", "security": "tls", "tlsSettings": {"alpn": ["h3"]}}}
apply_browser_fingerprint(outbound)
self.assertNotIn("fingerprint", outbound["streamSettings"]["tlsSettings"])
def test_explicit_fingerprint_is_kept(self) -> None:
outbound = {"streamSettings": {"security": "tls", "tlsSettings": {"fingerprint": "safari"}}}
apply_browser_fingerprint(outbound)
self.assertEqual(outbound["streamSettings"]["tlsSettings"]["fingerprint"], "safari")
if __name__ == "__main__":
unittest.main()