zapret-kvn/tests/test_yandex_music_direct_templates.py
loop-uh f49275b2ba
Some checks failed
Windows project source guards / test (push) Failing after 1m8s
fix: resolve tunnel names on the node instead of DoH through the proxy
`proxy-dns` был DoH к 8.8.8.8/1.1.1.1/9.9.9.9 через outbound `proxy`. Узел
перенаправляет на свой resolver только DNS на порт 53, а зашифрованный запрос
проходит мимо: клиент получал настоящий origin управляемого имени и шёл к нему
через тот же узел, получая региональный отказ. Через Hysteria 2 sing-box отдаёт
ядру уже готовый IP, поэтому подменить адрес на узле было нечем.

`proxy-dns` теперь перебирает два транспорта до resolver'а узла: udp, затем tcp
для сетей, где UDP через прокси не проходит. Прежний тихий откат на
`bootstrap-dns` убран — после шестисекундного таймаута он уводил резолв наружу
из туннеля. `bootstrap-dns` и `direct-doh` не тронуты: они поднимают туннель и
обязаны пережить подмену DNS провайдером.

Точечный отказ DoT на 853 возвращает приложения на 53, который перехватывает
`hijack-dns`. Встроенный DoH приложений на 443 перехвату не поддаётся — это
записано в docs/sing-box/runtime-config.md.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 17:28:33 +03:00

67 lines
3 KiB
Python

from __future__ import annotations
import json
from pathlib import Path
import unittest
ROOT = Path(__file__).resolve().parents[1]
YANDEX_MUSIC_PROCESSES = {"Яндекс Музыка.exe", "yandexmusic.exe"}
class YandexMusicDirectTemplateTests(unittest.TestCase):
def test_all_singbox_templates_route_yandex_music_direct_first(self) -> None:
template_dir = ROOT / "data" / "templates" / "sing-box"
for path in sorted(template_dir.glob("*.json")):
with self.subTest(template=path.name):
payload = json.loads(path.read_text(encoding="utf-8"))
rules = payload["route"]["rules"]
direct_index = next(
index
for index, rule in enumerate(rules)
if set(rule.get("process_name", [])) == YANDEX_MUSIC_PROCESSES
)
direct_rule = rules[direct_index]
self.assertEqual(direct_rule.get("action"), "route")
self.assertEqual(direct_rule.get("outbound"), "direct")
self.assertTrue(
all(
rule.get("action") in {"sniff", "hijack-dns"}
or rule.get("outbound") == "block"
# Точечный отказ DoT — тоже обработка протокола: он
# возвращает приложение на 53, который перехватывает
# hijack-dns.
or (rule.get("action") == "reject" and rule.get("port") == 853)
for rule in rules[:direct_index]
),
"Only protocol handling and narrow block rules may precede Yandex Music direct",
)
def test_all_xray_templates_route_yandex_music_direct_first(self) -> None:
template_dir = ROOT / "data" / "templates" / "xray"
for path in sorted(template_dir.glob("*.json")):
with self.subTest(template=path.name):
payload = json.loads(path.read_text(encoding="utf-8"))
rules = payload["routing"]["rules"]
direct_index = next(
index
for index, rule in enumerate(rules)
if set(rule.get("process", [])) == YANDEX_MUSIC_PROCESSES
)
direct_rule = rules[direct_index]
self.assertEqual(direct_rule.get("type"), "field")
self.assertEqual(set(direct_rule.get("process", [])), YANDEX_MUSIC_PROCESSES)
self.assertEqual(direct_rule.get("network"), "tcp,udp")
self.assertEqual(direct_rule.get("outboundTag"), "direct")
self.assertTrue(
all(rule.get("outboundTag") == "block" for rule in rules[:direct_index]),
"Only narrow block rules may precede Yandex Music direct",
)
if __name__ == "__main__":
unittest.main()