fix: медиа и «прокси не настроен»: пакет на кадр, настоящие фронты, kwsN-1 только для медиа; beta.19 (#75)

* fix(tunnel): медиа и «прокси не настроен»: один пакет на кадр, настоящие фронты, kwsN-1 только для медиа

Три причины, каждая проверена на живом Telegram запросом req_DH_params:
сервер сверяет номер DC внутри него с собой и отвечает -444 на чужой.

- WebSocket-фронт разбирает один пакет на входящий кадр, остаток ждёт
  следующего. Клиент пишет запросы пачками, TGLock слал их одним кадром,
  и всё, что шло вторым, застревало: текст проходил, файлы вставали.
  Теперь поток режется на пакеты, как в Flowseal/tg-ws-proxy. Пачка через
  beta.18 висит на всех DC, через эту сборку принимается на всех.
- Кластер kwsN-1 отвергает обычное соединение с -444, а Telegram Desktop
  в ответ выключает прокси как настроенный неверно. При сбое kwsN основное
  соединение откатывалось именно туда. Теперь kwsN-1 только для медиа.
- Адреса из списка DC не говорят на TLS ни из какой сети (проверено с чистой
  сети), поэтому первый маршрут каждого DC не работал никогда. Закреплены
  настоящие фронты, и DC1, DC3, DC5 стали доступны по WebSocket, чего не было.
  У CDN203 своего фронта нет: остались TCP, Worker и CF-прокси.

Обрыв туннеля, через который уже шли данные, больше не ставит маршрут на
паузу. Таймаут соединения 8 с, как в tg-ws-proxy. probe_proxy.mjs получил
--identity и --batched: они и нашли эти баги.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat: журнал ошибок в диагностике, --log-file для роутеров; 2.0.0-beta.19

«Скопировать диагностику» отдавала только счётчики, и по ней нельзя было
понять, какой маршрут не ответил. Теперь в отчёте есть строки сбоев туннелей,
а домены Worker и CF-прокси в них заменены на <домен>.

--log-file и --log-max-mb (log_file, log_max_mb в настройках), как в
tg-ws-proxy: журнал идёт в файл с ротацией, а не в системный журнал роутера.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(routes): запасной WebSocket на порту 80 родных адресов, включая CDN203

Родные адреса Telegram принимают WebSocket без TLS на порту 80. Измерено
req_DH_params: DC определяется адресом, имя хоста не важно, а 91.105.192.100
отдаёт ключи CDN. Для CDN203, откуда часто идёт видео, это первый прямой
WebSocket: на 443 фронта у него нет, и там, где провайдер закрыл только 443,
до него раньше было не достать без Worker.

Защита та же, что у родного TCP: внутри тот же обфусцированный MTProto. В Host
идёт адрес, имени Telegram в открытом виде нет. Маршрут разрешён только до
закреплённых адресов и пробуется последним.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(routes): паузы маршрутов по результатам ревью, пределы и журнал

- Обрыв не наказывает маршрут, только если туннель работал и получал ответы
  15 с. Одного ответа мало: маршрут, который ответил раз и умер, оставался
  предпочтительным навсегда.
- Клиент спросил, не дождался ответа за 5 с и ушёл: маршрут считается
  неработающим. Иначе CDN, принявший TCP и молчащий, держал первое место, и
  очередь до порта 80 не доходила.
- Предел пакета 2 МиБ вместо 16: чужое устройство в LAN-режиме не заставит
  роутер копить десятки мегабайт на соединение.
- Журнал: разовая ошибка записи его больше не выключает, неудачная ротация
  не теряет бэкап и держит предел, права 0600 и на уже существующем файле.
- В отчёт диагностики не попадает Worker, заданный адресом.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: babin <Thartewerner536e@engineer.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Никита Sonic
2026-10-31 07:32:03 +00:00
committed by GitHub
parent bc18509b9c
commit 8ce9355243
17 changed files with 1497 additions and 134 deletions
+42 -12
View File
@@ -200,25 +200,55 @@ Ping приходит в читающую половину, а отвечает
Для каждого DC и отдельно для media-соединений строится список маршрутов:
1. сохранённый успешный маршрут;
2. точный Telegram IP с `kwsN` или `kwsN-1` в TLS SNI и WebSocket Host;
3. явно настроенный пользователем Cloudflare Worker;
4. дополнительные Telegram IP и варианты хоста;
5. системный DNS (кроме CDN DC203).
2. основной WebSocket-фронт DC (`web_fronts`) с настоящим именем хоста в TLS SNI
и WebSocket Host;
3. явно настроенные пользователем CF-прокси и Cloudflare Worker;
4. запасные фронты DC;
5. системный DNS;
6. WebSocket без TLS на порту 80 адреса DC и родной MTProto TCP, только если всё
остальное не ответило.
Поддерживаются DC1–5 и CDN DC203. Для DC203 первым используется обычный MTProto
TCP к закреплённому `91.105.192.100:443`; этот сервер может принимать MTProto,
не принимая TLS/WebSocket. Резерв через Worker соединяется с тем же IP.
WebSocket-попытки также сохраняют закреплённый адрес. DNS fallback на DC2 для DC203 запрещён:
успешный WebSocket handshake с другим DC не доставляет CDN-запрос в нужный
датацентр. Worker также получает именно IP DC203.
Адреса фронтов и родные адреса DC разные. `149.154.167.51`, `.91`, `149.154.175.50`,
`.100`, `149.154.171.5` и `91.105.192.100` говорят только на MTProto и на TLS не
отвечают, в том числе из сети без блокировок. WebSocket принимают `149.154.167.220`
и `.99` (DC2, DC4), `149.154.174.100` и `.200`, `149.154.175.209` (DC1, DC3),
`149.154.170.100`, `.200` и `.96` (DC5).
Фронт выбирает дата-центр по имени хоста, а не по заголовку клиента, поэтому имя и
адрес нельзя менять местами. Какой DC стоит за маршрутом, проверяет
`probe_proxy.mjs --identity`: сервер сверяет номер DC внутри `req_DH_params` с собой
и отвечает `-444`, если номер чужой. `resPQ` этого не показывает, на него отвечает
любой сервер при любом номере в заголовке.
Кластер `kwsN-1` только для медиа: обычное соединение он отвергает с `-444`, а
Telegram Desktop на этот код выключает прокси как настроенный неверно. Основной
кластер `kwsN` принимает оба вида соединений и остаётся запасным для медиа.
Фронт разбирает один транспортный пакет на входящий кадр, остаток ждёт следующего
кадра. Клиенты пишут запросы пачками, поэтому поток режется на пакеты
(`PacketSplitter`), и каждый уходит отдельным кадром. Для Worker это не нужно: он
пишет кадры в обычный TCP.
На порту 80 родные адреса принимают WebSocket без TLS. Там DC определяет адрес, а
не имя хоста, поэтому в `Host` идёт сам адрес и имя Telegram в открытом виде не
появляется. Защита та же, что у родного TCP: внутри тот же обфусцированный MTProto,
а сервер подтверждает себя в самом протоколе. Маршрут разрешён только до
закреплённых адресов.
Поддерживаются DC1–5 и CDN DC203. У DC203 нет фронта на 443, а `kws2` на любом
фронте это обычный DC2, поэтому его маршруты: MTProto TCP к закреплённому
`91.105.192.100:443`, Worker, CF-прокси и порт 80 того же адреса.
После ошибки маршрут получает exponential cooldown от 30 секунд до 30 минут.
Успешный маршрут становится первым для следующего соединения того же DC и
типа трафика. Попытки запускаются с интервалом 250 мс, не больше трёх
одновременно на соединение; остальные отменяются после первого успеха.
Если все маршруты на паузе, возвращается причина и время до повторной
попытки. Новое подключение клиента не обходит cooldown. Ошибка upstream
после handshake также снимает предпочтение маршрута и добавляет cooldown.
попытки. Новое подключение клиента не обходит cooldown. Маршрут тоже теряет
предпочтение и получает cooldown, если Telegram закрыл соединение с кодом ошибки,
туннель оборвался, не проработав 15 секунд, или клиент спросил, не дождался ответа
за 5 секунд и ушёл сам. Обрыв туннеля, который работал дольше и получал ответы,
маршрут не наказывает: это обычное дело, а пауза оставила бы весь DC без связи.
## TLS policy