Major architectural refactoring of MTProxy (FakeTLS proxy) implementation to improve modularity, testability, and evidence-based failure handling: Native C++ changes: - Extract handshake scheduler from ConnectionSocket into MtProxyHandshakeScheduler (mutex, endpoints, queuing, cooldown logic) - Extract server-flight parsing and HMAC verification into MtProxyServerFlightParser - Extract data-path shaping (record sizing, timing) into MtProxyDataPathShaper with policy decisions - Introduce typed failure evidence classification (MtProxyFailureEvidence) mapping phases to evidence kinds - Introduce typed recovery actions (MtProxyRecoveryPolicy) to gate recipe cursor movement - Create MtProxyHandshakePlan to capture immutable handshake selections per attempt - Create MtProxySocketPublisher facade for high-risk phase observations - Introduce MtProxyRequestClass enum to replace raw priority integers in scheduler API - Move startup cover state from pendingWrite to fakeTls in state machine Java changes: - Create ProxyEventReducer to centralize native event reduction logic - Create ProxyVisibleStateStore to own visible proxy state, DNS debounce, and mirrorVisiblePhase orchestration - Refactor ProxyRuntimeStateStore to delegate event routing and visible writes to these modules - Add ProxyPhasePolicy.evidenceForPhase() to map phases to typed evidence classes - Update ProxyHealthStore logging to include typed evidence in backoff decisions - Update ProxyRotationEngine to track and log failure evidence with rotation triggers Plugin API: - Upgrade client_utils to export RequestCallback class for externally-managed request callbacks - Add compatibility guard check_plugin_client_utils_contract.py Tooling: - Enhance runtime log analyzer to track failure evidence and response bytes - Improve runtime verifier to validate evidence mappings and recipe cursor gating - Add Stage 2 runtime proof checklist for FakeTLS usability verification
857 lines
67 KiB
Markdown
857 lines
67 KiB
Markdown
# ZaStoGram — Telegram для Android с усиленной маскировкой MTProxy
|
||
|
||
<img width="1916" height="821" alt="image" src="https://github.com/user-attachments/assets/0850c5cd-6d7f-4304-9347-2cc54d5ba416" />
|
||
|
||
> Экспериментальный форк официального Telegram для Android. Цель форка —
|
||
> сделать подключение к MTProxy FakeTLS (`ee`-секрет) менее похожим на
|
||
> стандартный Telegram MTProxy-трафик и ближе к обычному браузерному HTTPS.
|
||
|
||
Это не новый мессенджер и не отдельный протокол. База остаётся официальным
|
||
Telegram Android, а основные изменения сосредоточены вокруг MTProxy/FakeTLS,
|
||
отдельного WSS-транспорта и клиентских privacy/UX-настроек форка.
|
||
|
||
## Простыми словами
|
||
|
||
- **MTProxy** — прокси для Telegram.
|
||
- **FakeTLS** — режим MTProxy, где начало соединения выглядит как TLS/HTTPS.
|
||
- **WSS** — WebSocket-транспорт поверх настоящего TLS. В этом форке он живёт
|
||
отдельно от MTProxy/FakeTLS и обычного SOCKS5.
|
||
- **DPI / ТСПУ** — оборудование провайдера, которое классифицирует и режет
|
||
трафик по признакам на проводе.
|
||
- **JA4** — отпечаток TLS ClientHello. По нему можно отличить настоящий браузер
|
||
от синтетического FakeTLS, если ClientHello сделан плохо.
|
||
|
||
Проблема MTProxy не только в одном хеше. DPI может смотреть на TLS-рукопожатие,
|
||
временные интервалы, размеры записей, количество одновременных соединений и
|
||
поведение после рукопожатия. Поэтому здесь важна аккуратная маскировка без
|
||
поломки MTProto.
|
||
|
||
## Что сейчас реализовано
|
||
|
||
### Где мы сейчас
|
||
|
||
Текущая архитектура разделяет сбои MTProxy по фазам, чтобы не лечить DNS или
|
||
TCP через JA4:
|
||
|
||
- `host_resolve_failed` — слой DNS/endpoint stability: `sslip.io` разбирается
|
||
локально без DNS-запроса, регистр суффикса не важен, а доменные прокси могут
|
||
использовать последний успешный IPv4 из кэша. Повторный холодный DNS lookup
|
||
для того же `host:port` коротко склеивается, чтобы не стрелять пачкой
|
||
одинаковых DNS-запросов;
|
||
- `tcp_not_connected` — слой endpoint circuit-breaker: один плохой endpoint не
|
||
должен получать пачку быстрых `connect()` от разных аккаунтов, DC и
|
||
download/upload-потоков. Активный TCP-start gate держит только фазу
|
||
`connect()` и освобождается сразу после `socket_connected` или закрытия
|
||
сокета;
|
||
- `client_hello_sent_no_server_hello` — слой phase-adaptive FakeTLS: следующий
|
||
запуск осторожно меняет только рецепт рукопожатия, а не весь транспорт сразу;
|
||
- `mtproxy_packet_sent_no_response` — слой обычного `dd`/legacy MTProxy:
|
||
проблема уже после TCP и первого MTProxy-пакета, поэтому JA4/ClientHello тут
|
||
не участвуют. Если первый MTProxy-пакет отправлен, но ответа нет, соединение
|
||
закрывается отдельным коротким таймаутом этой фазы, а не висит до общего
|
||
socket timeout.
|
||
|
||
GUI и `logcat` используют одни и те же строковые фазы. Если прокси зависает,
|
||
первым делом смотрим именно фазу, а не общий текст "подключаемся".
|
||
|
||
DRS пока не первым: для него уже есть базовый runtime-режим и guard на
|
||
завершённые FakeTLS-записи, но агрессивные профили размеров записей будем
|
||
включать только после отдельной проверки record-boundary на всём пути
|
||
`build -> partial send -> complete TLS frame -> discard`.
|
||
|
||
### Быстрая проверка перед сборкой
|
||
|
||
Перед сборкой APK можно прогнать все статические guards по MTProxy-архитектуре
|
||
одной командой:
|
||
|
||
```sh
|
||
python3 Tools/check_mtproxy_all.py
|
||
```
|
||
|
||
Этот suite проверяет FakeTLS-путь, профили JA4, фрагментацию ClientHello,
|
||
паттерн открытия соединений, startup cover, data-aware IPT, endpoint resilience,
|
||
границы завершённых FakeTLS-записей, `dd`/legacy lifecycle, live-статусы GUI,
|
||
scheduler проверок прокси и анализатор логов. Он не заменяет установку APK и
|
||
реальные логи, но быстро ловит разъезд слоёв в коде.
|
||
|
||
Для соседних поверхностей есть отдельные быстрые guards, которые не требуют
|
||
полной Android-сборки:
|
||
|
||
```sh
|
||
python3 Tools/check_login_proxy_button.py
|
||
python3 Tools/check_single_active_presence.py
|
||
python3 Tools/check_background_network_policy.py
|
||
python3 Tools/check_hide_main_screen_stories.py
|
||
python3 Tools/check_zasto_edit_history_contract.py
|
||
python3 Tools/check_app_log_cleanup.py
|
||
python3 Tools/check_contacts_permission_prompt.py
|
||
python3 Tools/check_splash_icon.py
|
||
python3 Tools/check_settings_durov_links.py
|
||
python3 Tools/check_zapret_proxy_sponsor.py
|
||
python3 Tools/check_plugin_client_utils_contract.py
|
||
python3 Tools/check_plugin_python_deps.py
|
||
python3 Tools/check_plugin_utils_javadoc.py
|
||
python3 Tools/check_build_apk_workflow.py
|
||
```
|
||
|
||
### Пользовательские функции форка
|
||
|
||
Помимо MTProxy-слоя в клиенте есть отдельные пользовательские изменения:
|
||
|
||
- экран `ZaSto Приватность` с переключателями, которые по умолчанию включают
|
||
клиентские override'ы: сохранять удалённые сообщения, сохранять
|
||
самоуничтожающиеся / view-once материалы, хранить локальную историю
|
||
редактирования сообщений, разрешать сохранение и пересылку защищённого
|
||
контента и историй, разрешать скриншоты, не отправлять уведомление о
|
||
скриншоте в секретных чатах и отключать рекламные/промо-поверхности Telegram;
|
||
- настройка чатов `Скрыть истории на главном экране`: она прячет stories row,
|
||
кнопку создания истории и hint только на главном списке диалогов, не выключая
|
||
сторис глобально;
|
||
- настройка уведомлений `Всегда держать сеть в фоне`: клиент не вызывает свой
|
||
`native_pauseNetwork()` при уходе приложения в фон. Это помогает не ронять
|
||
живые подключения самим приложением, но не отменяет системные ограничения
|
||
Android по батарее и процессам;
|
||
- отдельная строка `Zapret VPNs` / `Спонсор прокси` в верхней части списка
|
||
чатов с переходом на `zapretvpns_bot`. Она управляется тем же общим
|
||
анти-рекламным переключателем: если `Отключить рекламу` включено, локальная
|
||
sponsor-строка тоже скрывается;
|
||
- дополнительный блок в настройках со ссылками `Наш канал`, `Наш VPN` и
|
||
`Донаты & поддержать`;
|
||
- раздел `Плагины` в настройках: установка Python `.plugin`-файлов,
|
||
совместимых с базовой моделью exteraGram, включение/выключение, удаление и
|
||
отдельные экраны настроек плагинов;
|
||
- экран входа показывает кнопку прокси сразу в обычном login-flow и при отмене
|
||
удаления аккаунта, даже если прокси ещё не настроен;
|
||
- открытие контактов больше не вызывает автоматический lifecycle-показ
|
||
объяснения про доступ к контактам;
|
||
- только выбранный аккаунт отправляет online presence. Фоновый keep-awake не
|
||
должен превращать остальные аккаунты в постоянно online;
|
||
- Android 12+ splash использует логотип форка со стаканом, а не стандартный
|
||
самолётик Telegram;
|
||
- файловые логи приложения чистятся на старте процесса до первой новой записи,
|
||
чтобы свежий запуск не смешивался со старыми локальными логами.
|
||
|
||
### Плагины exteraGram
|
||
|
||
В ZaStoGram встроен Python-движок плагинов для `.plugin`-файлов, близких к
|
||
формату exteraGram. Он живёт внутри основного Android-клиента, а не как
|
||
отдельный бот или внешний скрипт:
|
||
|
||
- CPython запускается через Chaquopy в модуле `TMessagesProj`; текущий runtime
|
||
— Python 3.11;
|
||
- пользовательские `.plugin`-файлы ставятся из интерфейса приложения и хранятся
|
||
в data-директории приложения, а не зашиваются в APK;
|
||
- каждый плагин — один Python-файл с метаданными вроде `__id__`, `__name__`,
|
||
`__version__`, `__min_version__`, `__icon__` и классом-наследником
|
||
`BasePlugin`;
|
||
- жизненный цикл плагина: `on_plugin_load()`, `on_plugin_unload()` и
|
||
`create_settings()` для собственного экрана настроек;
|
||
- настройки плагинов рендерятся нативными Telegram-ячейками через `ui.settings`
|
||
(`Header`, `Switch`, `Input`, `Selector`, `Text`, `Divider`) и сохраняются
|
||
отдельно для каждого id плагина;
|
||
- доступны helper-модули `client_utils`, `android_utils`, `hook_utils`,
|
||
`file_utils`, `ui.alert` и `ui.bulletin`;
|
||
- `client_utils` экспортирует `send_request()` и `RequestCallback` для
|
||
exteraGram-плагинов, которые импортируют request callback вручную;
|
||
- в APK заранее кладутся Python-зависимости `requests`, `Pillow`/`PIL` и
|
||
`pyfiglet`, потому что runtime-установки через pip на устройстве нет;
|
||
- хуки Java-методов работают через Pine с Xposed-совместимым API:
|
||
`MethodHook`, `XposedHook`, `hook_method()` и `hook_all_constructors()`;
|
||
- для сетевых запросов сейчас подключён post-response hook:
|
||
`post_request_hook()` может пропустить, заменить или отменить ответ через
|
||
`HookStrategy`.
|
||
|
||
Путь в приложении:
|
||
**Настройки -> Плагины -> + -> выбрать `.plugin` файл**. После установки
|
||
плагин можно открыть, включить/выключить, удалить или настроить, если сам
|
||
плагин отдаёт модель настроек.
|
||
|
||
Это совместимость с реализованной здесь базовой поверхностью exteraGram, а не
|
||
обещание полной поддержки каждого публичного плагина. Если внешний плагин
|
||
использует API, которого нет в `TMessagesProj/src/main/python` или Java-мостах
|
||
`org.telegram.plugins`, его нужно адаптировать.
|
||
|
||
### Отдельный WSS-транспорт
|
||
|
||
WSS — это не MTProxy FakeTLS и не Python/локальный relay. В форке он вынесен в
|
||
нативный путь `WssTransport.cpp` и управляется отдельным состоянием:
|
||
|
||
- режим WSS выбирается в списке прокси отдельной секцией: `Выкл`, официальный
|
||
WSS Telegram или свой WSS gateway;
|
||
- WSS-настройки сохраняются отдельно от обычного `currentProxy`, включая
|
||
`wssHost`, `wssPath`, `wssUseForMiniApps` и выбранный SOCKS5 upstream;
|
||
- если WSS включён, legacy proxy path отключается, а MTProxy-настройки не
|
||
показываются как активные для этого режима;
|
||
- официальный WSS использует реальный TLS/WebSocket upgrade и каталог
|
||
Telegram WSS route'ов, а для DC без стабильного WSS route может уйти в
|
||
обычный fallback;
|
||
- выбранный SOCKS5 для WSS подключается до TLS/WebSocket как upstream. Это не
|
||
то же самое, что "SOCKS внутри WSS", и не должно перетирать обычный список
|
||
прокси Telegram;
|
||
- для mini apps есть отдельный `wssUseForMiniApps` hook, который использует тот
|
||
же выбранный WSS SOCKS upstream, а не случайный legacy-прокси.
|
||
|
||
### Путь MTProxy FakeTLS от tsrman-коммита
|
||
|
||
В активном пути транспорта сохранено поведение, близкое к рабочему
|
||
`tsrman/tg`-варианту:
|
||
|
||
- ClientHello держится в отдельном буфере до полной отправки: если `send()`
|
||
отправил только часть данных, остаток досылается на следующем `EPOLLOUT`;
|
||
- по умолчанию фаза данных использует стабильное фиксированное ограничение
|
||
`2878` байт;
|
||
- дополнительные слои фазы данных включаются только через явные настройки GUI и
|
||
не меняют MTProto-формат.
|
||
|
||
Это важно для надёжности: сначала соединение должно стабильно работать, и только
|
||
потом можно включать дополнительные слои и сравнивать результат на конкретном
|
||
провайдере или прокси.
|
||
|
||
### Стабильный выбор TLS-профиля
|
||
|
||
Для MTProxy FakeTLS выбранный TLS-профиль теперь не меняется хаотично на каждое
|
||
соединение. Профиль выбирается стабильно по адресу прокси, секрету и локальной
|
||
соли.
|
||
|
||
В автоматическом пуле сейчас два профиля, которые показали себя наиболее
|
||
безопасными для текущей диагностики:
|
||
|
||
- Firefox Android;
|
||
- Yandex.
|
||
|
||
Так разные установки и разные прокси могут получать разные сетевые отпечатки, но
|
||
один конкретный адрес прокси не прыгает между профилями каждую секунду.
|
||
Android Chrome, Android OkHttp и Desktop Firefox оставлены в ручном режиме для
|
||
сравнительных тестов. Android OkHttp пока не входит в Auto, потому что этот
|
||
профиль нужно дополнительно проверить на стабильность с разными MTProxy.
|
||
|
||
В настройках списка прокси добавлен выбор `JA4 / TLS-профиль`.
|
||
|
||
- `Auto` — режим по умолчанию. Клиент сам выбирает стабильный профиль для
|
||
конкретного `адрес:порт:секрет`;
|
||
- `Auto rotate` — стабильный профиль для адреса прокси, но после подозрительного
|
||
FakeTLS-сбоя клиент переключает этот адрес на другой профиль из безопасного
|
||
пула. Ошибки до открытия TCP не вращают JA4, потому что ClientHello в таком
|
||
случае ещё не участвовал. `dropped_early_after_appdata` ускоряет fallback и
|
||
endpoint-backoff, но тоже не вращает JA4: первые MTProto-данные уже пошли,
|
||
значит проблему надо искать в post-handshake lifecycle, размерах и таймингах,
|
||
а не в ClientHello. Поздний `dropped_after_appdata` остаётся диагностикой
|
||
позднего разрыва без смены рецепта;
|
||
- `Chrome`, `Ffox A`, `OkHttp`, `Firefox`, `Yandex` — ручной режим для тестов.
|
||
В этом режиме один выбранный профиль принудительно используется для всех
|
||
FakeTLS-прокси, а служебная проверка прокси использует тот же профиль.
|
||
|
||
Это нужно, чтобы на одном APK быстро сравнивать, какой отпечаток лучше проходит
|
||
у конкретного провайдера или набора прокси, не пересобирая приложение.
|
||
|
||
### Защитные проверки ClientHello
|
||
|
||
Перед отправкой ClientHello проверяется на базовую совместимость с MTProxy:
|
||
|
||
- корректная TLS-запись и длины;
|
||
- ожидаемое смещение списка наборов шифров;
|
||
- первый шифр не из GREASE относится к TLS 1.3 `TLS_AES_*`;
|
||
- наличие SNI-домена из `ee`-секрета;
|
||
- размер в пределах серверного лимита.
|
||
|
||
Это защищает от красивого, но несовместимого ClientHello.
|
||
|
||
### Рандомизация внутри ClientHello
|
||
|
||
Оставлены низкорисковые элементы маскировки:
|
||
|
||
- случайные поля ClientHello;
|
||
- случайные ECH-данные там, где профиль их использует;
|
||
- случайная цель расширения заполнения вместо старого фиксированного размера;
|
||
- безопасная генерация GREASE-значений без выхода за границы массива.
|
||
|
||
### Управляемая мягкая фрагментация ClientHello
|
||
|
||
В настройках списка прокси добавлен переключатель
|
||
`Мягкая фрагментация ClientHello`. Он действует только на MTProxy FakeTLS
|
||
(`ee`-секреты).
|
||
|
||
Когда режим включён, уже собранный и проверенный ClientHello отправляется не
|
||
одним логическим залпом, а двумя небольшими TCP-отправками с коротким
|
||
неблокирующим интервалом. TLS-структура, SNI, HMAC и MTProto-трафик после
|
||
подключения не меняются.
|
||
|
||
Режим нужен для A/B-тестов против DPI: можно собрать один APK и сравнивать
|
||
прокси с фрагментацией и без неё прямо из GUI, не откатывая код. По умолчанию
|
||
режим выключен, чтобы рабочий транспорт оставался максимально близким к
|
||
стабильному пути.
|
||
|
||
### Управляемый размер TLS-записей
|
||
|
||
В настройках списка прокси добавлен режим `Размер TLS-записей`. Он действует
|
||
только после успешного FakeTLS-рукопожатия и меняет размер следующих
|
||
`ApplicationData`-записей, в которые упаковывается MTProto-трафик.
|
||
|
||
Доступные варианты:
|
||
|
||
- `Выкл` — старое стабильное поведение с верхней границей `2878` байт;
|
||
- `Мягко` — выбирает размер из небольшого набора безопасных значений;
|
||
- `Разно` — использует более широкий диапазон размеров.
|
||
|
||
Этот слой не перепаковывает MTProto и не держит отдельное состояние
|
||
"продолжения" TLS-записи. Сначала собирается полная TLS-запись в pending-буфере,
|
||
потом она досылается до конца, и только после полной отправки полезная нагрузка
|
||
выбрасывается из очереди MTProto. Это снижает риск поломать partial-send и
|
||
позволяет тестировать размер записей прямо из GUI. Завершённая FakeTLS-запись
|
||
логируется как `mtproxy_data tls_frame_complete`; этот marker пишется только
|
||
после полной отправки frame и списания payload из очереди.
|
||
|
||
### Управляемые тайминги данных
|
||
|
||
В настройках списка прокси добавлен режим `Тайминги трафика`. Он добавляет
|
||
короткую неблокирующую паузу только между полностью отправленными FakeTLS
|
||
`ApplicationData`-записями.
|
||
|
||
Доступные варианты:
|
||
|
||
- `Выкл` — без пауз;
|
||
- `Мягко` — короткие интервалы для осторожного A/B-теста;
|
||
- `Средне` — более заметное растягивание последовательности записей.
|
||
|
||
Неполная TLS-запись не задерживается и всегда досылается целиком. Это важно,
|
||
чтобы не ломать MTProto и не превращать сетевой поток в состояние, где часть
|
||
payload уже выкинута из очереди, но не дошла до сокета.
|
||
|
||
### Стартовая маскировка MTProxy
|
||
|
||
В настройках списка прокси добавлен режим `Стартовая маскировка MTProxy`. Он
|
||
включается только для `ee` FakeTLS и только после успешного
|
||
`server_hello_hmac_ok`, то есть рукопожатие ClientHello/ServerHello не
|
||
трогается.
|
||
|
||
Доступные варианты:
|
||
|
||
- `Выкл` — не меняет первые `ApplicationData`-записи;
|
||
- `Мягко` — временно включает осторожные размеры TLS-записей и короткие
|
||
неблокирующие паузы для первых записей;
|
||
- `Строго` — сильнее меняет первые записи, но только в коротком стартовом
|
||
окне.
|
||
|
||
Этот слой не создаёт фоновые проверки прокси, не открывает сторонние сайты и не
|
||
генерирует отдельный "веб-трафик". Он меняет только упаковку первых реальных
|
||
MTProto-данных внутри уже установленного FakeTLS-соединения, чтобы старт меньше
|
||
выглядел как типичный MTProxy-залп.
|
||
|
||
### Мягкое сокращение параллельных каналов MTProxy
|
||
|
||
Когда включён MTProxy, загрузки и отправки файлов больше не создают отдельный
|
||
набор параллельных каналов скачивания и выгрузки для каждого запроса. Они
|
||
мягко сводятся к основному каналу скачивания/выгрузки, а прямые соединения без
|
||
MTProxy сохраняют прежнее распределение.
|
||
|
||
В настройках списка прокси слой называется
|
||
`Меньше параллельных каналов MTProxy`. По умолчанию он включён, но его можно
|
||
выключить без пересборки, если нужно сравнить поведение конкретного прокси.
|
||
|
||
Это не настоящее мультиплексирование протокола и не перепаковка MTProto. Это
|
||
первый безопасный слой против сетевого шаблона, где клиент почти одновременно
|
||
создаёт несколько похожих потоков к одному прокси. Цель — уменьшить залп и
|
||
проверить эффект на блокировках, не меняя формат MTProto-пакетов и не ломая
|
||
сервер MTProxy.
|
||
|
||
### Паттерн MTProxy-подключений
|
||
|
||
Для `ee` FakeTLS добавлен управляемый планировщик открытия соединений к одному
|
||
адресу прокси. Он ограничивает не скорость уже установленного MTProto-трафика,
|
||
а только залп одинаковых попыток
|
||
`DNS/resolve -> connect -> ClientHello -> server_hello_hmac_ok`.
|
||
|
||
Ключ планировщика: `адрес прокси:порт:SNI`.
|
||
|
||
В настройках списка прокси слой называется `Паттерн подключений MTProxy`. Это
|
||
не флажок, а режим:
|
||
|
||
- `Обычный` — без очереди, максимально близко к базовому транспорту;
|
||
- `Мягкий` — лёгкий контроллер допуска: до двух активных FakeTLS-рукопожатий
|
||
на адрес прокси без штрафной паузы;
|
||
- `Браузерный` — сначала одно настоящее FakeTLS-рукопожатие, после первого
|
||
успешного `server_hello_hmac_ok` плавно допускается второй слот; тяжёлые
|
||
соединения скачивания и выгрузки стартуют позже обычных и медийных;
|
||
- `Тихий` — одно активное рукопожатие и более медленная последовательная выдача
|
||
слотов; после недавних успехов допускается два;
|
||
- `Строгий` — одно активное рукопожатие, ограничение быстрых последовательных
|
||
стартов `connect()`, более длинные паузы для очереди и короткая ограниченная
|
||
штрафная пауза после таймаута или зависания рукопожатия.
|
||
|
||
Старый флаг `mtProxyHandshakeAdmission` мигрируется в `Мягкий` режим, если он
|
||
был включён. Новый режим хранится как `mtProxyConnectionPatternMode`,
|
||
передаётся в родной C++ слой через `setProxySettings` и перезапускает соединения
|
||
при изменении.
|
||
|
||
Когда режим не `Обычный`, задача планировщика:
|
||
|
||
- начинать delegate DNS-разрешение доменных `ee` FakeTLS-прокси только после
|
||
допуска в очередь, чтобы DNS/TCP-отказы не обходили браузерный backoff;
|
||
- держать максимум два активных FakeTLS-рукопожатия в `Мягком` режиме и максимум
|
||
одно в `Браузерном`/`Тихом`/`Строгом`, кроме недавних успешных
|
||
`server_hello_hmac_ok`, после которых `Браузерный` и `Тихий` режимы могут
|
||
открыть второй слот;
|
||
- ставить новые рукопожатия в очередь по приоритету:
|
||
`Generic/Temp -> GenericMedia -> Push -> Download -> Upload`;
|
||
- не пропускать служебную проверку прокси через жёсткую очередь, чтобы не
|
||
получать ложный результат "прокси не работает" из-за ожидания слота;
|
||
- освобождать слот сразу после `server_hello_hmac_ok`, чтобы загрузка и отправка
|
||
файлов после подключения шли параллельно как раньше.
|
||
|
||
Зависания в фазе `client_hello_sent` в `Обычном` и `Мягком` режимах в основном
|
||
логируются. В `Браузерном`, `Тихом` и `Строгом` режимах они дополнительно дают
|
||
короткую ограниченную штрафную паузу для низкоприоритетных попыток, чтобы
|
||
клиент не создавал быстрый повторяемый шум на одном адресе прокси. Успешный
|
||
`server_hello_hmac_ok` сбрасывает штраф, чтобы рабочий прокси не оставался
|
||
искусственно медленным.
|
||
|
||
Отдельно обрабатываются повторы `tcp_not_connected`, когда попытка умерла до
|
||
отправки `ClientHello`. Общий endpoint-слой ставит короткую паузу для всех
|
||
режимов, включая `Обычный`, а `Браузерный`, `Тихий` и `Строгий` режимы делают
|
||
эту паузу заметнее. Это снижает шторм повторных DNS/`connect()` по одному
|
||
адресу прокси, но не смешивает сетевое душение до ClientHello с
|
||
JA4/ServerHello-ошибками.
|
||
|
||
Это сделано против узнаваемого залпового шаблона, когда клиент почти
|
||
одновременно открывает много одинаковых FakeTLS-соединений к одному прокси.
|
||
Приложение не управляет TCP SYN-пакетами напрямую: оно управляет стартом
|
||
`connect()`, а retransmit на уровне TCP делает ОС. Поэтому строгий режим
|
||
приближает поведение к редким открытиям соединений, но не обещает точное
|
||
правило "один SYN в секунду".
|
||
|
||
### Устойчивость endpoint'а
|
||
|
||
Поверх планировщика FakeTLS добавлен общий слой устойчивости endpoint'а. Он
|
||
работает для всех MTProxy-секретов, включая обычные `dd`/legacy, и не меняет
|
||
формат MTProto-пакетов.
|
||
|
||
У слоя два разных ключа, потому что разные фазы видят разный объём данных:
|
||
|
||
- сетевой ключ `host:port` используется для DNS, `tcp_not_connected` и
|
||
ограничения одновременных TCP connect-попыток. На этой фазе ещё нет
|
||
ClientHello, SNI и JA4, поэтому нельзя дробить state по секрету или TLS-профилю;
|
||
- FakeTLS-рецепт использует ключ `host:port:тип секрета:SNI`. Этот state нужен
|
||
только после post-ClientHello сбоев, где уже действительно участвовали SNI,
|
||
ClientHello и выбранный TLS-профиль.
|
||
|
||
Что делает слой:
|
||
|
||
- запоминает последний успешно разрешённый IPv4 для доменного прокси по ключу
|
||
`host:port` и может использовать его как fallback, если следующий DNS lookup
|
||
не дал адрес. Этот DNS-кэш намеренно не зависит от `ee`/`dd`, секрета и SNI:
|
||
если один вариант того же прокси уже дал IP, другой вариант может переиспользовать
|
||
его без лишнего DNS-шума;
|
||
- коротко склеивает одновременные холодные DNS-разрешения одного `host:port`:
|
||
первая попытка идёт в resolver, следующие ждут `dns_coalesce_wait`, затем
|
||
сначала пробуют свежий кэш и только потом снова обращаются к DNS;
|
||
- после фаз `host_resolve_failed`, `tcp_not_connected`,
|
||
`client_hello_sent_no_server_hello` и `mtproxy_packet_sent_no_response`
|
||
ставит короткую паузу именно на том уровне, где произошёл сбой: DNS/TCP
|
||
ошибки идут на `host:port`, а FakeTLS/post-ClientHello ошибки — на
|
||
`host:port:тип секрета:SNI`. В `Обычном`/`Мягком` режимах пауза короче, в
|
||
`Браузерном`/`Тихом`/`Строгом` — длиннее;
|
||
- не запускает пачку одновременных TCP connect-попыток к одному MTProxy
|
||
`host:port`. `tcp_connect_gate` удерживает только pre-TCP фазу и освобождается
|
||
на `socket_connected` или `closeSocket`, поэтому уже установленная MTProto
|
||
сессия и передача файлов не ограничиваются этим слоем;
|
||
- для FakeTLS после повторяющихся post-ClientHello сбоев мягко меняет следующий
|
||
рецепт запуска: сначала включает мягкую фрагментацию ClientHello, затем
|
||
переключает `Auto`/`Auto rotate` на другой Android TLS-профиль, затем включает
|
||
более тихий темп открытия соединений. Ручной профиль из GUI не перетирается;
|
||
- сбрасывает штраф после реального ответа прокси:
|
||
`server_hello_hmac_ok`, `first_tls_app_recv` или
|
||
`first_mtproxy_packet_recv`.
|
||
|
||
Это не DRS и не мультиплексирование. Слой не трогает уже установленный
|
||
MTProto-трафик и не держит слот до конца TCP-сессии. Его задача — уменьшить
|
||
шум повторных попыток и дать GUI/логам понятную фазу:
|
||
`endpoint_cooldown`, `dns_cache_hit`, `dns_cache_store`,
|
||
`phase_adaptive_recipe`.
|
||
|
||
Java-планировщик проверок хранит свой endpoint-state для фоновых и ручных
|
||
proxy-check. Автопереключение прокси учитывает этот state: fallback-ротация не
|
||
выбирает endpoint, который находится в свежем failure-backoff после проверки или
|
||
в свежем native `endpoint_cooldown`. Короткая пауза после успешной проверки
|
||
нужна только чтобы не перепроверять рабочий прокси слишком часто; она не
|
||
считается failure-backoff и не мешает выбрать свежий рабочий прокси.
|
||
Если scheduler пропускает повторную проверку из-за backoff, строка прокси
|
||
получает тот же видимый статус `endpoint_cooldown`, чтобы список не выглядел как
|
||
"не проверено" во время намеренной паузы.
|
||
|
||
Java backoff использует ту же фазовую идею ключей: pre-TLS, `dd` no-response и
|
||
ранние post-appdata срывы (`host_resolve_failed`, `tcp_not_connected`,
|
||
`network_block_suspected`, `tcp_connected_no_pong`,
|
||
`mtproxy_packet_sent_no_response`, `dropped_early_after_appdata`) пишутся в
|
||
`host:port` state, чтобы не проверять тот же сетевой endpoint пачкой через
|
||
другой секрет. При этом активные proxy-check запросы склеиваются по полному
|
||
ключу `host:port:username:password:secret`, чтобы результат проверки одного
|
||
секрета не применился к другому секрету.
|
||
|
||
Если текущее подключение публикует terminal-фазу вроде `host_resolve_failed`,
|
||
`tcp_not_connected`, `client_hello_sent_no_server_hello` или
|
||
`mtproxy_packet_sent_no_response`, автопереключение не ждёт общий длинный таймаут
|
||
статуса "подключаемся". Оно ставит короткий delayed fallback и выбирает другой
|
||
кандидат через те же фильтры freshness/failure/cooldown/backoff. Это особенно
|
||
важно для `dd`/legacy MTProxy: `mtproxy_packet_sent_no_response` возникает уже
|
||
после TCP и первого MTProxy-пакета, поэтому JA4 и ClientHello тут не помогут.
|
||
Native-слой дополнительно ограничивает ожидание первого ответа для plain
|
||
MTProxy: если после отправки первого MTProxy-пакета входящих байтов нет, фаза
|
||
быстро становится terminal-ошибкой и попадает в общий backoff/fallback, вместо
|
||
того чтобы несколько секунд выглядеть как обычное "подключаемся".
|
||
Такая live terminal-фаза также попадает в endpoint-state `ProxyCheckScheduler`,
|
||
чтобы fallback не перескочил на другую строку списка с тем же endpoint'ом.
|
||
Повтор той же фазы в пределах короткого окна de-dup не увеличивает backoff
|
||
повторно: это защищает от двойного счёта, когда native сначала публикует фазу,
|
||
а затем почти сразу закрывает socket с тем же diagnostic.
|
||
Общее состояние Telegram `Connected` считается более слабым сигналом, чем
|
||
свежая конкретная proxy-фаза: generic `Connected` не затирает ни живые стадии
|
||
вроде `client_hello_sent` / `first_tls_app_sent`, ни terminal-ошибки вроде
|
||
`host_resolve_failed`, `tcp_not_connected` или `mtproxy_packet_sent_no_response`
|
||
в GUI. Ошибку снимает только конкретный успешный proxy-check или новая data-path
|
||
фаза этого же прокси, например `first_tls_app_recv` /
|
||
`first_mtproxy_packet_recv`; `server_hello_hmac_ok` остаётся стадией
|
||
рукопожатия, а не доказательством рабочего трафика. Явный новый старт подключения
|
||
из GUI или rotation публикует `connect_start`, чтобы пользователь видел новую
|
||
попытку вместо старой terminal-ошибки выбранной строки, но не стирает свежий
|
||
usable success: в течение hold-окна после `first_tls_app_recv` /
|
||
`first_mtproxy_packet_recv` локальный `connect_start` удерживается как live
|
||
telemetry.
|
||
Автопереключение также не выбирает кандидата со свежей незавершённой live-фазой:
|
||
старый `available=true` не должен перебивать `client_hello_sent`,
|
||
`first_tls_app_sent` или ожидание первого plain MTProxy-ответа.
|
||
|
||
### Диагностика запуска подключения
|
||
|
||
Добавлены диагностические точки для MTProxy-подключения:
|
||
|
||
- `connect_start`;
|
||
- `endpoint_cooldown`;
|
||
- `tcp_connect_gate`;
|
||
- `dns_coalesce_wait`;
|
||
- `dns_cache_hit`, `dns_cache_store`;
|
||
- `phase_adaptive_recipe`;
|
||
- `socket_connected`;
|
||
- `client_hello_sent`;
|
||
- `server_hello_hmac_ok`;
|
||
- `server_hello_hmac_timeout`, если ServerHello структурно получен, но HMAC не
|
||
совпал за короткое диагностическое окно;
|
||
- `on_connected`;
|
||
- `first_tls_app_sent`, когда первый MTProto-пакет ушёл внутри TLS ApplicationData;
|
||
- `first_tls_app_recv`, когда от сервера пришла первая полезная TLS ApplicationData;
|
||
- `mtproxy_disconnect` с причиной, состоянием и флагами первой MTProto-активности
|
||
внутри TLS (`first_tls_sent`, `first_tls_recv`);
|
||
- `admission_grant`, `admission_queue`, `admission_release`,
|
||
`admission_disabled`, `admission_freeze_detected`,
|
||
`admission_freeze_observed`, `admission_tcp_failure_cooldown`,
|
||
`admission_failure_cooldown`.
|
||
В admission-маркерах есть `admission_mode` и `connection_pattern`.
|
||
|
||
Эти же live-фазы прокидываются в Java для выбранного текущего прокси. Поэтому
|
||
окно прокси показывает не один общий текст "подключаемся", а конкретную стадию:
|
||
ожидание слота, DNS-разрешение, открытие TCP, отправка `ClientHello`, ожидание
|
||
`ServerHello`, запуск MTProto и первые MTProto-данные. Служебные проверки
|
||
списка прокси не меняют live-стадию выбранного подключения.
|
||
Native live-stage передаётся с ключом `host:port:тип секрета:SNI` для MTProxy
|
||
(`host:port` остаётся только fallback для сетевых слоёв), поэтому поздняя фаза
|
||
от другого секрета на том же IP:port не перетирает текущий выбранный прокси.
|
||
|
||
Это нужно, чтобы отличать DPI-обрыв от багов в нашем транспортном пути.
|
||
|
||
### Архитектура проверки прокси
|
||
|
||
Проверка прокси разделена на три слоя, чтобы GUI не запускал лишние соединения и
|
||
не спорил с native-состоянием:
|
||
|
||
- `ProxyCheckScheduler` в Java принимает ручные проверки из bottom sheet,
|
||
фоновые проверки из списка прокси и проверки ротации. Он держит один активный
|
||
check, склеивает одинаковые endpoint'ы, хранит владельцев проверки и отменяет
|
||
active check через `cancelProxyCheck()`.
|
||
- Native-слой `ConnectionsManager` завершает проверку через единый
|
||
`finishProxyCheck`: успех по `TL_pong`, сетевой обрыв, timeout, start-fail и
|
||
явная отмена проходят через одну точку очистки request/token/socket.
|
||
- `Tools/analyze_mtproxy_markers.py` сводит Java scheduler, native proxy-check,
|
||
rotation и FakeTLS-маркеры в один отчёт. Важный verdict:
|
||
`connected_without_socket_connected_marker` означает, что лог дошёл до
|
||
`on_connected`, но в этом срезе нет маркера `socket_connected`; такой случай
|
||
нельзя считать TCP-недоступностью.
|
||
|
||
В отчёте есть два разных среза:
|
||
|
||
- `FakeTLS endpoint phases` — фазы настоящего FakeTLS-подключения по адресу
|
||
прокси: TCP/connect, отправка ClientHello, проверка ServerHello/HMAC, первая
|
||
MTProto ApplicationData и поздний disconnect;
|
||
- `Plain MTProxy lifecycle` — обычные `dd`/legacy MTProxy-секреты отдельно от
|
||
FakeTLS. Этот срез группируется по `endpoint`, аккаунту, DC и типу соединения,
|
||
чтобы отличать "проверка прокси доступна" от ситуации, где, например,
|
||
`Generic` получает RPC-ответы, а `Download` много отправляет и почти ничего не
|
||
получает;
|
||
- `Native endpoint outcomes` — результат служебной проверки прокси. Значение
|
||
`fail:tcp_not_connected` относится к TCP/connect-слою, а
|
||
`fail:tcp_connected_no_pong` означает, что TCP открылся, но MTProxy ping не
|
||
завершился.
|
||
|
||
`Tools/collect_mtproxy_logs.ps1` строит основной `mtproxy_markers.txt` только по
|
||
текущему `logcat.txt`. Сырые device logs всё ещё подтягиваются в папку сессии,
|
||
но не смешиваются с live-статистикой, чтобы старые файлы не портили выводы.
|
||
|
||
### Единая карта ошибок прокси
|
||
|
||
Статус прокси теперь передаётся не числовыми кодами, а строковой фазой. Один и
|
||
тот же словарь используется в native-логе, Java scheduler, GUI и анализаторе:
|
||
|
||
- `tcp_not_connected` — TCP-соединение не открылось;
|
||
- `tcp_connected_no_pong` — TCP открылся, но MTProxy ping не завершился;
|
||
- `mtproxy_packet_sent_no_response` — для обычного `dd`/legacy MTProxy: TCP
|
||
открылся, первый MTProxy-пакет отправлен, но ответ сервера не пришёл;
|
||
- `endpoint_cooldown` — клиент выдерживает короткую паузу перед новой попыткой
|
||
к этому же endpoint'у после свежего фазового сбоя;
|
||
- `tcp_connect_gate` — клиент ждёт, пока закончится уже активная TCP
|
||
connect-попытка к тому же MTProxy endpoint'у;
|
||
- `dns_coalesce_wait` — клиент ждёт уже запущенное DNS-разрешение того же
|
||
доменного прокси, чтобы не создавать пачку одинаковых DNS-запросов;
|
||
- `dns_cache_hit` / `dns_cache_store` — клиент использовал или обновил
|
||
сохранённый IP доменного прокси;
|
||
- `phase_adaptive_recipe` — клиент изменил следующий мягкий рецепт FakeTLS
|
||
после сбоя на фазе рукопожатия или первых данных: фрагментация ClientHello,
|
||
Android TLS-профиль или темп открытия соединения;
|
||
- `network_block_suspected` — повторный TCP-срыв на том же endpoint, похожий на
|
||
фильтрацию пути до ответа MTProxy;
|
||
- `waiting_tcp` — текущий выбранный прокси ещё ждёт TCP-подключение;
|
||
- `client_hello_sent_no_server_hello` — ClientHello отправлен, валидный
|
||
ServerHello не пришёл;
|
||
- `server_hello_hmac_mismatch` — ServerHello пришёл, но не прошёл HMAC-проверку
|
||
MTProxy;
|
||
- `post_handshake_no_appdata` — рукопожатие прошло, но первые MTProto-данные не
|
||
пришли;
|
||
- `dropped_early_after_appdata` — первые MTProto-данные пришли, но соединение
|
||
быстро умерло; это post-handshake lifecycle/endpoint-сигнал, а не повод
|
||
менять JA4;
|
||
- `dropped_after_appdata` — соединение поднялось и отвалилось уже после первых
|
||
MTProto-данных.
|
||
|
||
Единый источник GUI-текстов — `ProxyCheckDiagnostics`. Поэтому список прокси,
|
||
bottom sheet проверки и логи используют одинаковые имена фаз. Это нужно, чтобы
|
||
по экрану и по `logcat` было видно, где именно умирает прокси и похоже ли это
|
||
на недоступный сервер, несовместимость транспорта или фильтрацию пути.
|
||
|
||
### Корректная досылка неполной TLS-записи
|
||
|
||
Стартовый FakeTLS `ClientHello` отправляется через отдельный ожидающий буфер:
|
||
если неблокирующий `send()` принял только часть hello, остаток досылается на
|
||
следующем `EPOLLOUT`, а состояние `client_hello_sent` логируется только после
|
||
полной отправки.
|
||
|
||
Если peer закрыл TCP-соединение (`recv() == 0`), сокет закрывается сразу с
|
||
меткой `recv_eof`, а не ждёт общего таймаута подключения.
|
||
|
||
Если после полной отправки `ClientHello` нет корректного `server_hello_hmac_ok`
|
||
за время freeze-таймера, попытка закрывается с меткой
|
||
`server_hello_timeout_close`, чтобы не держать мёртвое рукопожатие до общего
|
||
таймаута.
|
||
|
||
Проверка HMAC для ServerHello теперь привязана к границам TLS-записей, а не к
|
||
случайному размеру одного `recv()`. Это сохраняет совместимость с официальным
|
||
MTProxy, где ответ состоит из `ServerHello + ChangeCipherSpec + ApplicationData`,
|
||
и не ломается на telemt-подобных ответах с дополнительными профилированными
|
||
ApplicationData-записями в серверном handshake flight.
|
||
|
||
После установленного FakeTLS-соединения reader принимает только полезные
|
||
`ApplicationData`-записи MTProto, но корректно пропускает служебный
|
||
`ChangeCipherSpec`, не отдаёт пустые TLS-записи в MTProto-парсер и закрывает
|
||
соединение с отдельной меткой `tls_alert`, если peer прислал TLS Alert.
|
||
|
||
Для обёрнутых TLS-записей добавлена очередь ожидающей отправки записи: если
|
||
`send()` отправил только часть TLS-записи, остаток досылается как продолжение той
|
||
же записи, а полезная нагрузка MTProto не выбрасывается раньше полной отправки.
|
||
Успешная досылка обновляет таймер активности соединения, чтобы активная отправка
|
||
не считалась простым бездействием.
|
||
|
||
## Сборка
|
||
|
||
Закоммиченного APK здесь намеренно нет. APK собирается из исходников локально
|
||
или через GitHub Actions.
|
||
|
||
1. Получи `api_id` и `api_hash` на [my.telegram.org](https://my.telegram.org).
|
||
2. Укажи свои значения в проекте Telegram Android.
|
||
3. Подставь свой ключ подписи релизной сборки.
|
||
4. Собери через Android Studio или запусти workflow `Build ZaStoGram APK` в
|
||
GitHub Actions.
|
||
|
||
GitHub Actions настроен на ccache с `CCACHE_COMPILERCHECK=content`, чтобы
|
||
повторные сборки нативного кода были заметно быстрее после прогрева кэша.
|
||
Workflow собирает отдельные standalone APK под ABI:
|
||
|
||
- `ZaStoGram-standalone-arm64-v8a.apk`;
|
||
- `ZaStoGram-standalone-armeabi-v7a.apk`;
|
||
- `ZaStoGram-standalone-x86.apk`;
|
||
- `ZaStoGram-standalone-x86_64.apk`.
|
||
|
||
После успешного запуска создаётся отдельный prerelease для этого run'а, и APK
|
||
прикладываются к нему напрямую. GitHub artifacts всё ещё скачиваются как ZIP, а
|
||
release asset — это уже сам `.apk`.
|
||
|
||
На время диагностики MTProxy GitHub Actions включает сетевые логи Telegram прямо
|
||
в обычных ABI-артефактах. После установки такого APK можно собрать маркеры:
|
||
|
||
```powershell
|
||
D:\bin\platform-tools\adb.exe devices -l
|
||
```
|
||
|
||
Из WSL:
|
||
|
||
```bash
|
||
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "$(wslpath -w Tools/collect_mtproxy_logs.ps1)" -Adb "D:\bin\platform-tools\adb.exe" -Package org.zastogram.messenger -Seconds 180
|
||
```
|
||
|
||
Во время записи надо открыть приложение и повторить подключение к MTProxy.
|
||
Главный файл для разбора: `mtproxy_markers.txt`.
|
||
Скрипт также создаёт `mtproxy_analysis.txt`: он группирует попытки по
|
||
`ConnectionSocket` и показывает, где остановилось подключение:
|
||
TCP/connect, отправка `ClientHello`, проверка `server_hello_hmac_ok`, переход в
|
||
`on_connected` или первые TLS `ApplicationData`-записи MTProto.
|
||
|
||
Рядом создаётся `mtproxy_runtime_contract.txt`. Это строгая проверка live-лога:
|
||
в `mtproxy_markers.txt` должны быть `transport_state=...`,
|
||
`endpoint_handshake_ok` и `endpoint_data_path_success`, причём
|
||
`endpoint_data_path_success` не может идти с `reason=server_hello_hmac_ok`.
|
||
`endpoint_data_path_success` должен появляться только после первого `first_tls_app_recv`
|
||
или `first_mtproxy_packet_recv` на том же native connection: успешный
|
||
`server_hello_hmac_ok` сам по себе подтверждает только рукопожатие, а не
|
||
рабочий data-path.
|
||
Если надо перепроверить уже сохранённую сессию вручную:
|
||
|
||
```bash
|
||
python3 Tools/verify_mtproxy_runtime_logs.py mtproxy-logs-live/<session>
|
||
```
|
||
|
||
В отчёте есть блок `Layer recommendations`. Он не делает вид, что автоматически
|
||
доказал DPI, а раскладывает фазы по слоям ремонта:
|
||
`dns_endpoint_stability` для DNS/TCP до ClientHello,
|
||
`faketls_handshake_recipe` для pre-ServerHello FakeTLS-сбоев,
|
||
`plain_dd_endpoint_backoff` для обычных `dd` no-response и
|
||
`faketls_data_path` для обрывов после завершённых FakeTLS ApplicationData.
|
||
В этот же endpoint-слой попадают native proxy-check отказы
|
||
`fail:tcp_not_connected` и `fail:tcp_connected_no_pong`, чтобы GUI-ошибка
|
||
"TCP не открылся" учитывалась в общей картине, даже если полноценная
|
||
`ConnectionSocket`-попытка ещё не дошла до FakeTLS.
|
||
|
||
Для сравнения профилей и endpoint'ов рядом создаются CSV:
|
||
|
||
- `mtproxy_attempts.csv` — каждая FakeTLS-попытка: endpoint, профиль, фаза
|
||
отказа, задержка до HMAC, close/error и набор увиденных маркеров;
|
||
- `mtproxy_endpoint_profile_stats.csv` — агрегат по `endpoint + profile`:
|
||
количество попыток, процент успеха, топ отказов, медиана HMAC и максимальный
|
||
burst рукопожатий за 1 и 5 секунд. В этом же файле есть
|
||
`tls_frames_completed`, чтобы отличать обрывы до data-path от обрывов после
|
||
реально завершённых FakeTLS `ApplicationData`-записей. Для длинных сессий
|
||
счётчик берётся из итогового `mtproxy_disconnect tls_frames_completed=...`,
|
||
поэтому анализатор не зависит только от первых подробных per-frame markers;
|
||
- `mtproxy_plain_account_stats.csv` — отдельный срез обычных `dd`/legacy
|
||
MTProxy по account/DC/type: первый пакет отправлен, пришёл ли первый ответ,
|
||
были ли `invalid_packet_length`, `auth_404` и ранние disconnect;
|
||
- `mtproxy_proxy_check_stats.csv` — агрегат native proxy-check по endpoint:
|
||
`ok`, `tcp_not_connected`, `tcp_connected_no_pong` и close-reasons. Этот файл
|
||
нужен, когда GUI показывает "TCP не открылся" ещё до полноценной FakeTLS или
|
||
MTProto-попытки;
|
||
- `mtproxy_scheduler_stats.csv` — агрегат Java scheduler по endpoint:
|
||
`enqueue`, `start`, `finish_ok`, `finish_fail`, `backoff`, `skip_backoff`,
|
||
`finish_keep_connected` и `phase_*`. Этот файл показывает, создаёт ли список
|
||
прокси лишний шум проверками или, наоборот, правильно сдерживает повторные
|
||
попытки после сетевых фаз.
|
||
|
||
Именно эти файлы нужны, чтобы отделить нашу ошибку транспорта от внешней
|
||
блокировки или нестабильного прокси.
|
||
Перед публичным релизом это нужно выключить обратно, чтобы не оставлять
|
||
подробные сетевые логи в обычной сборке.
|
||
|
||
## Как пользоваться
|
||
|
||
1. Подними MTProxy с FakeTLS вне зоны блокировки.
|
||
2. Используй `ee`-секрет с доменом.
|
||
3. В приложении добавь MTProto-прокси обычным способом:
|
||
**Настройки -> Данные и память -> Прокси -> Добавить прокси -> MTProto**.
|
||
|
||
Маскировка из этого форка применяется именно к FakeTLS/`ee`-секрету. Для обычных
|
||
`dd`-секретов и других вариантов без FakeTLS эти изменения не дают того же
|
||
эффекта.
|
||
|
||
Для WSS режима открой:
|
||
**Настройки -> Данные и память -> Прокси -> WSS-транспорт**. Официальный WSS
|
||
не требует MTProxy-секрета. Для своего gateway укажи host/port/path, а SOCKS5
|
||
upstream выбери отдельно в WSS-секции списка прокси, если он нужен.
|
||
|
||
Для плагинов exteraGram открой:
|
||
**Настройки -> Плагины -> +** и выбери локальный `.plugin` файл. Ставь только
|
||
доверенные плагины: они выполняются внутри процесса приложения и могут
|
||
рефлексией или hooks менять поведение клиента.
|
||
|
||
## Честные ограничения
|
||
|
||
- SNI не ротируется сам по себе. Он берётся из `ee`-секрета.
|
||
- Фаза данных пока не имитирует настоящий HTTP/2 или HTTP/1.1.
|
||
- WSS — отдельный транспорт, а не волшебная замена MTProxy. Официальные WSS
|
||
route'ы ограничены теми DC, где они реально поддержаны; для остальных путей
|
||
нужен fallback или свой gateway.
|
||
- Управляемый размер TLS-записей и тайминги данных включаются вручную и нужны
|
||
для диагностики, а не как доказанная универсальная защита.
|
||
- Стартовая маскировка действует только в начале установленного FakeTLS-сеанса
|
||
и не добавляет внешний браузерный трафик.
|
||
- Мягкое сокращение каналов уменьшает параллельность download/upload при
|
||
активном MTProxy, но не превращает MTProto в браузерный HTTP-трафик.
|
||
- Планировщик рукопожатий выключен по умолчанию и включается в GUI. Даже во
|
||
включённом виде он ограничивает только фазу открытия FakeTLS-соединения и не
|
||
маскирует всю статистику установленного MTProto-трафика.
|
||
- `ZaSto Приватность` — это клиентские локальные override'ы. Они не меняют
|
||
серверную политику Telegram и не являются полноценным архивным хранилищем для
|
||
всех видов историй, сторис и прочих ephemeral-сценариев.
|
||
- Совместимость с плагинами exteraGram покрывает реализованные helper-модули,
|
||
lifecycle, settings, post-response hooks и Xposed-style method hooks, но не
|
||
гарантирует работу любого публичного `.plugin` без адаптации.
|
||
- Плагины выполняются внутри процесса приложения. Ошибочный или вредоносный
|
||
плагин может ломать UI, сетевую логику или приватность аккаунта.
|
||
- Фоновый keep-awake отключает паузу сети на стороне клиента, но Android всё
|
||
равно может ограничивать процесс системными настройками батареи.
|
||
- Это не гарантия вечного обхода блокировок: DPI-правила меняются.
|
||
- Это неофициальный форк, использовать его нужно с пониманием рисков.
|
||
|
||
## Потом реализуем
|
||
|
||
- Расширить DRS из простого runtime-режима в фазовую модель: разные профили для
|
||
старта, интерактивного трафика, простоя и передачи файлов, защита от
|
||
повторяющихся шаблонов и сброс состояния после долгой паузы. Базовый guard на
|
||
цепочку `partial send -> complete TLS frame -> discard payload -> next record`
|
||
уже есть; следующий шаг — использовать его для более смелых DRS-профилей.
|
||
- Расширить IPT из простых неблокирующих пауз между TLS-записями в модель
|
||
межпакетных интервалов с разными правилами для рукопожатия, поддержания
|
||
соединения, интерактивного трафика и передачи файлов. Этот слой должен быть
|
||
data-aware: если есть ожидающие данные, разрешены только короткие burst-паузы
|
||
между уже завершёнными TLS-записями; длинный idle-режим не должен задерживать
|
||
активный MTProto-трафик.
|
||
- Для startup-паттерна использовать ту же дисциплину, что в TeleMT-плане:
|
||
сначала фиксировать измеримые фазы и инварианты, затем включать адаптацию.
|
||
Любая новая задержка должна иметь источник сигнала: есть ожидающие данные,
|
||
нет ожидающих данных, первый ответ получен, endpoint в cooldown. Случайные
|
||
паузы без такого сигнала считаются небезопасными для MTProto.
|
||
- Больше стабильных браузерных профилей из `telemt/tdlib-obf`: Android Brave,
|
||
дополнительные Chrome/Firefox/Yandex-варианты, Windows/iOS профили, но только
|
||
через защитные тесты совместимости с MTProxy.
|
||
- Политика ECH с учётом маршрута и автоматическим отключением неудачного
|
||
варианта, чтобы не долбить сеть ECH-профилем, если конкретный маршрут его
|
||
режет.
|
||
- Ограничение MSS как отдельный эксперимент после проверки фрагментации
|
||
ClientHello: оно меняет TCP-поведение ниже уровня FakeTLS и поэтому требует
|
||
отдельного сравнения с VPN/без VPN.
|
||
- Маскировка прикладного уровня: обрамление HTTP/2 или HTTP/1.1 поверх
|
||
TLS-подобного транспорта. Это самый глубокий и самый рискованный слой, его
|
||
нельзя делать косметически.
|
||
- Заполняющий трафик во время простоя и маскировка жизненного цикла соединений,
|
||
если они не будут ломать MTProto и батарею на Android.
|
||
|
||
## Основа и благодарности
|
||
|
||
- [DrKLO/Telegram](https://github.com/DrKLO/Telegram) — официальный Telegram
|
||
Android.
|
||
- [tsrman/tg](https://github.com/tsrman/tg) — рабочая база изменений FakeTLS,
|
||
JA4 и временных интервалов.
|
||
- [telemt/tdlib-obf](https://github.com/telemt/tdlib-obf) — идеи и референсы по
|
||
профилям маскировки, DRS/IPT и реестру профилей.
|