Implement QVPN Windows client for BlancVPN with resilient VPN service
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
# QVPN: обследование BlancVPN и проверяемые решения
|
||||
|
||||
Дата обследования: 4 октября 2026 года, Москва. В этом документе нет ссылки подписки, UUID, ключей Reality и адресов серверов из личной подписки.
|
||||
|
||||
## Что подтверждено на подключённом телефоне
|
||||
|
||||
`dumpsys package` установленного `com.blancvpn.app` показал `versionName=1.8.7`, `versionCode=189`, `minSdk=28`, `targetSdk=36`. Запись сохранена в `phone-package.txt`. Основное окно на `phone.png` показывает установленное VPN-соединение, выбранную локацию, большой переключатель подключения, переход к локациям и режим «Российские сервисы открываются без VPN».
|
||||
|
||||
Из APK исследованы имена компонентов и файлов, без переноса программного кода или ресурсов в QVPN. Обнаружены `XrayVpnService`, `AwgVpnService`, Flutter-ресурсы и библиотеки `libflutter.so`, `libapp.so`, `libgojni.so`, `libhev-socks5-tunnel.so`, `libam-go.so`, `libam-quick.so`, `libam.so`. Это подтверждает наличие Flutter-интерфейса и отдельных нативных VPN-компонентов. Имена файлов сами по себе не устанавливают точную версию ядра Xray, его текущие настройки или причину стабильности телефонного приложения.
|
||||
|
||||
В APK также обнаружены названия встроенных правил `ru_merged_geoip.dat`, `ru_merged_geosite.dat`, `ru_whitelist_rules_v1.json` и `ru_app_ids.json`. Они указывают на отдельную маршрутизацию российских ресурсов. Их содержимое не используется как база правил QVPN; для Windows выбраны открытые наборы и собственные правила. Фирменные изображения, шрифты, код приложения, токены встроенных сервисов и закрытые списки BlancVPN не копируются.
|
||||
|
||||
## Что подтверждено по подписке
|
||||
|
||||
Приватно полученное содержимое содержит 50 локаций VLESS с Reality поверх TCP. Парсер QVPN обработал эти 50 записей; диагностический вывод содержит только `LIVE_PARSE nodes=50`. Ключ не включён в исходники, тестовые фикстуры и отчёт.
|
||||
|
||||
Импорт ограничен HTTPS-ссылкой домена `withblancvpn.online` или его поддомена, маршрутом `/s/…`, стандартным портом, без userinfo, фрагмента и query. Отклоняются чужие домены, подмена суффикса, Unicode/punycode-подмена имени провайдера, HTTP и перенаправления HTTP. Для этой версии принимаются только проверенный формат VLESS/Reality TCP и публичные IP-адреса серверов. Локальные, loopback, private и link-local адреса не могут стать целью привилегированного VPN-процесса через подписку. Это намеренно узкая совместимость с обследованной подпиской; новые форматы провайдера требуют отдельной реализации.
|
||||
|
||||
## Почему Happ разрывает соединение
|
||||
|
||||
Причина пока не установлена. В проведённой проверке Windows за семь дней среди последних 300 записей ошибок приложений не найдено подтверждённого падения Happ. Ограниченное окно журнала не доказывает отсутствие всех падений и не отличает остановку ядра, сетевую ошибку, сон, обновление маршрутов и ручное отключение. Нельзя утверждать, что найдена и устранена конкретная ошибка Happ.
|
||||
|
||||
Для QVPN выбран Windows Service, работающий отдельно от окна. Закрытие интерфейса не должно прекращать VPN. Доступность проверяется запросом через отдельный локальный прокси, принудительно направленный в VPN; прямой доступ российских сервисов не должен дать ложное подтверждение исправности туннеля. Восстановление после ошибки использует ограниченную задержку 2, 4, 8, 16, 32, 60 секунд и смену локации при разрешённом автоматическом переключении. Успешная единичная проверка сбрасывает серию ошибок, но не накопленный backoff. Намерение пользователя «подключено» хранится отдельно от фактического состояния.
|
||||
|
||||
Восстановление процесса, реальная передача данных, рестарт службы, сон/пробуждение и многочасовая стабильность — разные границы проверки. Тест алгоритма восстановления не подтверждает работу сети, а краткий успешный сетевой тест не подтверждает многочасовую стабильность. В итоговой передаче следует указывать отдельно фактически выполненные проверки.
|
||||
|
||||
## Ядро и маршрутизация
|
||||
|
||||
QVPN запускает неизменённый официальный Windows x64 `sing-box 1.14.2` отдельным процессом. На момент обследования это последний стабильный релиз, отмеченный upstream как Latest. Источник: [официальный релиз v1.14.2](https://github.com/SagerNet/sing-box/releases/tag/v1.14.2).
|
||||
|
||||
SHA-256 архива `sing-box-1.14.2-windows-amd64.zip` повторно вычислен локально и совпал с `digest` релизного API:
|
||||
|
||||
`c2d8bfff918755808781dfdeeb8581b6c91eb3a243d9a7b55483cfc0c0684d32`.
|
||||
|
||||
SHA-256 распакованного `sing-box.exe`:
|
||||
|
||||
`7bbef1dea9189ee12799ae834ea4b4658355da25c47a21ad8804904c0ccd9410`.
|
||||
|
||||
Конфигурация QVPN включает TUN с IPv4/IPv6, `auto_route`, `strict_route`, перехват DNS, DoH через VPN, обход локальных IP и управляемый обход российских доменов/открытых rule-set. Официальная документация описывает [TUN и ограничения Windows](https://sing-box.sagernet.org/configuration/inbound/tun/), [VLESS](https://sing-box.sagernet.org/configuration/outbound/vless/), [Reality/TLS](https://sing-box.sagernet.org/configuration/shared/tls/), [DoH](https://sing-box.sagernet.org/configuration/dns/server/https/) и [маршрутизацию](https://sing-box.sagernet.org/configuration/route/). Документация публикуется для нескольких версий; допустимость созданного JSON должна проверяться именно бинарником 1.14.2.
|
||||
|
||||
## Лицензия и исходники ядра
|
||||
|
||||
Upstream `LICENSE` для точного тега разрешает sing-box по GPL версии 3 или более поздней, сохраняет copyright и отдельно ограничивает использование имени/ассоциации производных работ: [LICENSE v1.14.2](https://raw.githubusercontent.com/SagerNet/sing-box/v1.14.2/LICENSE). QVPN не использует имя ядра как своё название и не заявляет принадлежность к SagerNet или BlancVPN.
|
||||
|
||||
В пакет с ядром следует включать upstream `LICENSE`, полный текст GPL-3.0 и уведомление: «sing-box 1.14.2, Copyright (C) 2022 nekohasekai. Ядро распространяется без изменений. Лицензия GPL-3.0-or-later».
|
||||
|
||||
Точный исходный тег: [SagerNet/sing-box v1.14.2](https://github.com/SagerNet/sing-box/tree/v1.14.2); архив исходников: [v1.14.2.zip](https://github.com/SagerNet/sing-box/archive/refs/tags/v1.14.2.zip). При распространении бинарников необходимо сохранить лицензионные уведомления и обеспечить соответствующий исходный код и материалы сборки по условиям GPL. Одна ссылка на репозиторий не заменяет проверку полноты исходного комплекта для выбранного способа распространения.
|
||||
|
||||
## Автоматические проверки QVPN.Core
|
||||
|
||||
`dotnet run --project tests/QVPN.Tests/QVPN.Tests.csproj -c Release` — 16 групп проверок, 16 успешно, 0 ошибок. Внешний тестовый фреймворк не требуется.
|
||||
|
||||
Проверены допустимая подписка и запреты импорта, 50 синтетических записей в открытом и base64-виде, удаление дублей, очистка имён, ошибочные Reality-параметры, запрет локальных адресов, DPAPI roundtrip/замена/backup/повреждение и наследуемые ACL защищённого каталога, отсутствие открытого текста в файлах, двусторонний протокол IPC и ограничения размера, маршрутизация health-проверки, TUN/DNS, переключатель российских ресурсов и политика восстановления. DPAPI `LocalMachine` требует защищённого каталога службы: шифрование само по себе не ограничивает другие локальные учётные записи.
|
||||
|
||||
Для приватной проверки содержимого: `QVPN.Tests --live-parse <private-file>`. Для интеграционной проверки службы предусмотрено `QVPN.Tests --client <command> --value-file <private-file>`; запрос и значение ключа не выводятся, ответ содержит только публичный Status. Отчёт не является доказательством многочасовой работы VPN.
|
||||
|
||||
`QVPN.Tests --engine-check <sing-box.exe>` успешно проверил JSON официальным бинарником 1.14.2 для четырёх сочетаний TUN on/off и российского обхода on/off, все `exit=0`. Проверка использует только синтетический ключ и не подключается к серверу. Она подтверждает допустимость конфигурации для выбранного ядра, отдельно от сетевой работы.
|
||||
|
||||
После единичной ошибки загрузки в .NET проведено повторное обследование командой `QVPN.Tests --download --value-file <private-file> --diagnose`. Непосредственный `Subscription.DownloadAsync` получил 50 локаций. Диагностические запросы direct, system-proxy и IPv4-direct вернули HTTP 200 с корректной TLS-цепочкой (`SslPolicyErrors.None`). DNS на момент проверки вернул один IPv4 и не вернул IPv6. Ошибка не воспроизведена; изменение проверки сертификатов или принудительное отключение IPv6 этим замером не обосновано. Диагностика выводит только типы исключений, HRESULT и значения enum, без `Exception.Message`, URL, адресов и credentials. Устойчивость загрузки должна обеспечиваться повтором временной сетевой ошибки и сохранением последней рабочей подписки.
|
||||
|
||||
## Проверка обработчика службы после исправлений
|
||||
|
||||
В review найдены и исправлены: потеря IPC-слушателя при `JsonException`, ожидание бесконечного соседнего цикла вместо реакции SCM на fatal, сохранение работающего ядра после ошибки записи при отключении и неограниченное ожидание TCP-измерений при остановке. Дополнительно исправлены права `CreateNewInstance` диагностического сервера: полный доступ получает текущая серверная учётная запись, тогда как пользовательский клиент в режиме службы SYSTEM сохраняет только клиентские права.
|
||||
|
||||
Реальная команда `QVPN.Tests --malformed-service --evidence-file research/service-regression.json` отправила восемь повреждённых JSON-фреймов с клиентским `TokenImpersonationLevel.Identification` в уже запущенный обработчик. Каждый запрос был закрыт, после каждого `status` успешно ответил; до и после оставались 50 локаций и состояние `disconnected`. Во время проверки ядро не было запущено, поэтому этот замер подтверждает живучесть IPC и сохранение состояния, а не непрерывность активного туннеля. Команда не запускала TUN, не меняла настройки и не перезапускала службу. Публичный JSON содержит только время, счётчики, состояния и PID.
|
||||
|
||||
## Проверка установленного приложения, 4 октября 2026
|
||||
|
||||
Установленная служба LocalSystem и системный адаптер QVPN работают. HTTPS без явного прокси через системную маршрутизацию вернул 204. На рабочем ключе доступны 50 локаций.
|
||||
|
||||
Принудительное завершение установленного TUN-ядра: восстановление за 11.1 секунды без ввода ключа. Принудительное завершение службы: SCM восстановил службу и подключение за 11.4 секунды, дочернее ядро предыдущей службы не осталось работать. Последующий HTTPS-запрос через системный маршрут вернул 204. Свидетельство: installed-recovery-verification.json.
|
||||
|
||||
Старый installed-verification.json содержит ошибочный отказ теста: после завершения ядра проверка сразу прочитала предыдущий снимок connected со старым PID. Исправлено ожидание нового PID одновременно с проверенным состоянием connected. Это ошибка первоначального проверочного скрипта, а не подтвержденный отказ восстановления приложения.
|
||||
|
||||
В конце проверки VPN оставлен включённым, локация Цюрих, Швейцария. Отрисовка установленного интерфейса по реальному статусу — installed-live.png, не демонстрационный режим. Многосуточная эксплуатация и реальный цикл сна/пробуждения этой короткой проверкой не подтверждаются.
|
||||
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"tag": "v1.14.2",
|
||||
"assets": [
|
||||
{
|
||||
"url": "https://api.github.com/repos/SagerNet/sing-box/releases/assets/585841082",
|
||||
"id": 585841082,
|
||||
"node_id": "RA_kwDOHlge-M4i6zm6",
|
||||
"name": "sing-box-1.14.2-windows-amd64.zip",
|
||||
"label": "",
|
||||
"uploader": {
|
||||
"login": "github-actions[bot]",
|
||||
"id": 41898282,
|
||||
"node_id": "MDM6Qm90NDE4OTgyODI=",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/github-actions%5Bbot%5D",
|
||||
"html_url": "https://github.com/apps/github-actions",
|
||||
"followers_url": "https://api.github.com/users/github-actions%5Bbot%5D/followers",
|
||||
"following_url": "https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/github-actions%5Bbot%5D/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/github-actions%5Bbot%5D/orgs",
|
||||
"repos_url": "https://api.github.com/users/github-actions%5Bbot%5D/repos",
|
||||
"events_url": "https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/github-actions%5Bbot%5D/received_events",
|
||||
"type": "Bot",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"content_type": "application/zip",
|
||||
"state": "uploaded",
|
||||
"size": 32857903,
|
||||
"digest": "sha256:c2d8bfff918755808781dfdeeb8581b6c91eb3a243d9a7b55483cfc0c0684d32",
|
||||
"download_count": 128475,
|
||||
"created_at": "2026-09-24T11:43:37Z",
|
||||
"updated_at": "2026-09-24T11:43:38Z",
|
||||
"browser_download_url": "https://github.com/SagerNet/sing-box/releases/download/v1.14.2/sing-box-1.14.2-windows-amd64.zip"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
[
|
||||
{
|
||||
"name": "geoip-ru.srs",
|
||||
"url": "https://raw.githubusercontent.com/SagerNet/sing-geoip/7fe82a879ad2666526730c195b55a6d8d9147908/geoip-ru.srs",
|
||||
"sha256": "6e23f5580dd443e2f9c4895adafee8d199c1a3487182ad5f0a2256cf59c7e53a"
|
||||
},
|
||||
{
|
||||
"name": "geosite-category-ru.srs",
|
||||
"url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/53e0e99be15953f1865c4f3f4cffe8c8f8543dff/geosite-category-ru.srs",
|
||||
"sha256": "91cac7d9735f43d54d26102015f17c8a90cb740086e1bde290e2c03a4eb003a9"
|
||||
}
|
||||
]
|
||||
Reference in New Issue
Block a user