# TGLock v2: переписал обход Telegram с нуля — теперь работает на маке, и один прокси на всю квартиру **Простой · 7 мин · Rust · Open source · macOS · Сетевые технологии** **TL;DR:** Полмесяца назад я выложил TGLock — обход блокировки Telegram через WebSocket-туннель. Статья залетела на 183K просмотров. А потом всё сломалось. Соединения рвались через 2 минуты, DC определялся неправильно, маководы плакали в комментах. Переписал с нуля. 350 строк. Работает на macOS, Windows, Linux. Один прокси — все устройства в квартире. Код: [github.com/by-sonic/tglock](https://github.com/by-sonic/tglock). --- ## Что случилось после первой статьи Первая версия TGLock делала простую вещь: SOCKS5-прокси заворачивал MTProto в WebSocket через `web.telegram.org`. Провайдер видит HTTPS — Telegram работает. Концепция правильная. Реализация — нет. Через неделю после релиза прилетело: > «Работает 2 минуты, потом Telegram пишет ошибку прокси» > «По умолчанию выбирает неправильное сетевое подключение, вешается на VMware-адаптер» > «Порт 1080 занят, как поменять?» > «А на маке будет?» Последний вопрос задавали чаще всего. GoodbyeDPI — только Windows. Zapret — есть tpws, но это терминал и ручная настройка. GUI для обхода Telegram на маке — **не существует**. Вообще. Решил: не патчить старый код. Переписать с нуля. ## Что было не так с v1 ### Обрыв через 2 минуты Главный баг. WebSocket-серверы Telegram (`kws*.web.telegram.org`) шлют **Ping-фреймы** каждые ~60 секунд. Если клиент не отвечает Pong — сервер закрывает соединение. В v1 я использовал `split()` из `futures` чтобы разделить WebSocket-стрим на два потока — чтение и запись. Красиво, идиоматично, по учебнику. И сломано. Ping приходит в read-поток. Pong нужно отправить через write-поток. Между ними — channel или shared state. В теории работает. На практике — Pong опаздывает на десятки миллисекунд, сервер считает клиента мёртвым. **Решение в v2:** один `tokio::select!` цикл, без split. WebSocket остаётся единым объектом. `biased` приоритизирует чтение WS — Pong улетает мгновенно: ```rust loop { tokio::select! { biased; msg = ws.next() => match msg { Some(Ok(Message::Binary(data))) => { tcp_w.write_all(data.as_ref()).await?; tcp_w.flush().await?; } Some(Ok(Message::Ping(p))) => { ws.send(Message::Pong(p)).await?; } _ => break, }, n = tcp_r.read(&mut buf) => match n { Ok(0) | Err(_) => break, Ok(n) => { ws.send(Message::Binary(buf[..n].to_vec())).await?; } }, } } ``` Обратите внимание на `flush()` после каждого write в TCP. Без него tokio буферизует данные, Telegram Desktop ждёт ответ, не дожидается, переподключается. Ещё один баг v1, который маскировался под «нестабильное соединение». ### Неправильный DC В v1 DC определялся по IP-адресу. Таблица маппинга из документации Telegram: ``` 149.154.160-163 → DC1 149.154.164-167 → DC2 91.108.56-59 → DC5 ... ``` Проблема: в подсети `149.154.164-167` живут **и DC2, и DC4**. Telegram Desktop мог коннектиться к IP, который по таблице выглядит как DC2, а на самом деле хочет DC4. Прокси открывает WebSocket к `kws2.web.telegram.org`, отправляет данные — сервер дропает соединение. Пользователь видит «прокси не настроен». **Решение в v2:** не угадывать DC по IP. Telegram Desktop использует **obfuscated2** транспорт. Первые 64 байта — зашифрованный init-пакет. Внутри — настоящий DC ID. Ключ: байты `[8..40]`, IV: `[40..56]`. Алгоритм: AES-256-CTR. DC ID: `i32` в байтах `[60..64]` расшифрованного пакета. ```rust fn dc_from_init(init: &[u8; 64]) -> Option { use aes::Aes256; use cipher::{KeyIvInit, StreamCipher}; let mut dec = *init; let mut c = ctr::Ctr128BE::::new( init[8..40].into(), init[40..56].into(), ); c.apply_keystream(&mut dec); let id = i32::from_le_bytes([dec[60], dec[61], dec[62], dec[63]]); let dc = id.unsigned_abs() as u8; (1..=5).contains(&dc).then_some(dc) } ``` 12 строк. Три крейта (`aes`, `ctr`, `cipher`). Зато DC определяется **точно**, а не «скорее всего». IP-маппинг остался как fallback — на случай если init-пакет повреждён (что на практике не случается). ### Привязка к Windows v1 использовала: - `netsh` для смены DNS - Хардкоженный путь к шрифтам Windows - `taskkill` для остановки процессов - `ipconfig /flushdns` Ни одна из этих вещей не нужна для WebSocket-прокси. DNS менять не надо — `web.telegram.org` резолвится нормально. Шрифты — egui использует встроенные. Весь платформо-специфичный код был мусором. **v2: 0 строк платформо-специфичного кода.** Один и тот же бинарник компилируется на Windows, macOS и Linux без единого `#[cfg(target_os)]`. ## macOS: почему это важно Среди разработчиков, дизайнеров, людей из IT — процент маководов огромный. А инструментов для обхода блокировки Telegram на маке — ноль целых, ноль десятых. - **GoodbyeDPI** — Windows only. Даже не обсуждается. - **Zapret** — есть `tpws` для macOS, но это CLI. Нужно: `brew install`, `sudo`, правка конфигов, ручная настройка системного прокси через `networksetup`. Для техничных людей — ОК. Для остальных — нет. - **VPN** — работает, но гонит весь трафик. Для одного Telegram — оверкилл за $5/мес. TGLock v2: скачал бинарник, запустил, нажал кнопку. Всё. Никакого `brew`, никакого `sudo`, никаких конфигов. На Apple Silicon (M1–M4) — нативный ARM-бинарник, ~6 МБ. На Intel-маках — x86_64 билд. Оба собираются автоматически в GitHub Actions. ### Gatekeeper Apple блокирует неподписанные приложения. Developer ID стоит $99/год. Для бесплатного open-source — не вариант. Решение стандартное: ```bash xattr -cr ~/Downloads/tglock-macos-arm64 chmod +x ~/Downloads/tglock-macos-arm64 ``` Две команды, один раз. ## LAN-режим: один прокси на всю квартиру Это была самая частая просьба в ишью: > «Цель: пустить все домашние устройства с Telegram через один такой прокси на компе в локальной сети» В v1 прокси слушал `127.0.0.1:1080` — только локально. Устройства в сети не могли подключиться. В v2 — чекбокс **LAN** в интерфейсе. Включаешь — прокси биндится на `0.0.0.0`. Все устройства в домашней сети могут использовать прокси. ``` Телефон ──┐ Планшет ───┤── SOCKS5 → 192.168.1.42:1080 ──► TGLock ──► WSS → DC Ноутбук ───┘ ``` Приложение автоматически определяет LAN IP и показывает его в интерфейсе. На телефоне в настройках Telegram: SOCKS5, адрес — IP компьютера, порт — тот что в приложении. Один компьютер. Все устройства. Без VPN, без роутера, без конфигов. ### Настраиваемый порт Ещё один ишью: «порт 1080 занят другим сервисом». Теперь порт можно менять прямо в интерфейсе. По умолчанию 1080, но если занят — ставишь любой другой. Порт валидируется при старте, в настройках Telegram автоматически отображается текущий. ## Архитектура v2 Два файла. 350 строк. Всё. ### proxy.rs (~160 строк) ``` TcpListener (0.0.0.0 | 127.0.0.1 : port) │ ▼ SOCKS5 handshake │ ├── IP ∈ Telegram? ──► read 64-byte init │ │ │ ▼ │ dc_from_init (AES-256-CTR) │ │ │ ▼ │ WSS kws{dc}.web.telegram.org │ │ │ ▼ │ select! { ws ⟷ tcp } │ └── IP ∉ Telegram? ──► direct TCP relay ``` Никаких абстракций, traits, generics. Прямолинейный async-код. Каждое соединение — один `tokio::spawn`, один `select!` цикл. ### main.rs (~190 строк) GUI на egui. Тёмная тема (GitHub Dark). Статистика в реальном времени: активные соединения, WS-туннели, текущий DC, аптайм. Кнопка «Настроить автоматически» — открывает Telegram через `tg://socks?server=...&port=...`. Один клик до рабочего Telegram. ## CI/CD: бинарники для всех GitHub Actions при пуше тега `v*` собирает: | Файл | Платформа | |---|---| | `tglock.exe` | Windows x64 | | `tglock-macos-arm64` | macOS Apple Silicon | | `tglock-macos-x64` | macOS Intel | | `tglock-linux-x64` | Linux x64 | Четыре платформы, один workflow, автоматические релизы. Скачал — запустил — работает. ```yaml strategy: matrix: include: - os: windows-latest target: x86_64-pc-windows-msvc artifact: tglock.exe - os: macos-latest target: aarch64-apple-darwin artifact: tglock-macos-arm64 - os: macos-latest target: x86_64-apple-darwin artifact: tglock-macos-x64 - os: ubuntu-latest target: x86_64-unknown-linux-gnu artifact: tglock-linux-x64 ``` ## Что изменилось: v1 vs v2 | | v1 | v2 | |---|---|---| | DC detection | IP-маппинг (ненадёжен) | AES-256-CTR из init (точно) | | Ping/Pong | Игнорировался → обрыв через 2 мин | `biased` select → мгновенный Pong | | flush() | Нет → потеря данных | Явный flush после каждого write | | WS timeout | Нет → бесконечное зависание | 10 секунд | | Платформы | Windows | Windows, macOS, Linux | | DNS | Менял системный DNS | Не трогает | | Адаптеры | Определял сетевой адаптер (баг с VMware) | Не определяет, не нужно | | Порт | Хардкод 1080 | Настраиваемый | | LAN | Нет | Чекбокс, 0.0.0.0 | | Файлов | 4 модуля + bat-скрипт | 2 файла | | Строк | ~800 | ~350 | Половину кода удалил. Стало стабильнее. ## Сравнение с альтернативами (2026) | | GoodbyeDPI | Zapret | VPN | **TGLock v2** | |---|---|---|---|---| | Подход | Фрагментация | Desync | Туннель | WebSocket | | IP-шейпинг | Не обходит | Не обходит | Обходит | **Обходит** | | macOS | Нет | CLI | Да | **GUI** | | Нужен сервер | Нет | Нет | Да | **Нет** | | Весь трафик | Нет | Нет | Да | **Только Telegram** | | LAN-шаринг | Нет | Можно настроить | Да | **Чекбокс** | | Стоимость | 0₽ | 0₽ | $3–10/мес | **0₽** | ## Цифры v2 - **350** строк кода - **2** файла - **4** платформы (Win x64, macOS ARM64, macOS x64, Linux x64) - **0** строк платформо-специфичного кода - **0** серверов - **0₽** ## Скачать **[github.com/by-sonic/tglock](https://github.com/by-sonic/tglock)** → [Releases](https://github.com/by-sonic/tglock/releases/latest) Или собрать: ```bash git clone https://github.com/by-sonic/tglock.git cd tglock cargo build --release ``` macOS: после скачивания `xattr -cr tglock-macos-arm64 && chmod +x tglock-macos-arm64` **P.S.** Для полного обхода блокировок (YouTube, Discord, Instagram и всё остальное) — **[RoseVPN](https://t.me/rosevpnru_bot)**. --- *by sonic* **Теги:** telegram, rust, websocket, macos, socks5, mtproto, обход блокировок, open-source, кроссплатформенность **Хабы:** Rust · Open source · macOS · Сетевые технологии