Commit Graph

6 Commits

Author SHA1 Message Date
Никита Sonic b92403b4c9 fix(proxy): клиент, с которым не договорились, закрывался молча (#42) (#45)
Диагностика от @alexsagaidak в #42 показала третий случай, которого ни один
счётчик не различал. У него ноль отклонённых и живые туннели, то есть оба
показателя говорят «всё хорошо»:

    соединений 17 · туннелей 6 · DC4 · Запасной Telegram IP · сбоев 0 ·
    падений маршрутов 26 · отклонено 0

Клиент, который дошёл до прокси, но не сумел договориться, не попадал ни в
blocked, ни в ws_failures. Ошибка из handle() выбрасывалась в `let _ =`,
соединение закрывалось, и наружу это выглядело как active, дёрнувшийся вверх и
обратно. По диагностике неотличимо от клиента, который подключился и работает.

Теперь такие клиенты считает unknown_clients, а журнал называет адрес и
причину. Причин две:

MTProto-init не разбирается под текущим секретом. Почти всегда это ссылка
tg://proxy от прошлого запуска: секрет — её половина, и клиент с сохранённой
старой ссылкой попадает ровно сюда. Со стороны Telegram это и есть «прокси
настроен неверно и будет отключён» — то, с чем пришли в #37 и что до сих пор
нельзя было подтвердить со стороны прокси.

SOCKS5-приветствие не разбирается. Сюда же попадает MTProto-соединение,
ушедшее в SOCKS5-ветку по неоднозначному первому байту, если полный init не
успел прийти за PROTOCOL_PROBE_TIMEOUT. На loopback этого не бывает, а через
Wi-Fi с телефона — уже вопрос задержки.

От ws_failures отличается тем, что тот растёт после успешного рукопожатия с
клиентом: там договорились с клиентом, но не смогли с Telegram. Различать их
важно, иначе непонятно, в какую сторону смотреть.

В интерфейсе — метрика «Не опознаны» с пояснением, в строке статуса CLI —
поле «не опознано N».

Co-authored-by: by-sonic <171230345+by-sonic@users.noreply.github.com>
2026-08-19 14:32:34 +03:00
Никита Sonic 945e794eb3 fix(proxy): LAN-режим отклонял настоящие адреса Telegram (#42) (#43)
@alexsagaidak: с компьютера прокси работает, с телефона — нет. Адрес машины
вписан верно, порт верный, брандмауэр выключен, порт добавлен в исключения.

Причина нашлась в проверке «это Telegram?». Она сравнивала два первых октета:

    (149, 154) => Some(...)
    (91, 108)  => Some(...)

То есть «телеграмом» считались целиком четыре /16 — десятки тысяч чужих
адресов, — а IPv6 не распознавался вообще ни один. Telegram владеет
149.154.160.0/20, шестью /22 в 91.108, 91.105.192.0/23, 185.76.151.0/24 и пятью
блоками IPv6.

Ошибка в обе стороны, и на loopback она не видна. Там allow_direct включён, и
неопознанный адрес всё равно релеится напрямую — соединение просто работает.
На сетевом слушателе allow_direct выключен, и тот же адрес получает SOCKS5
0x02. Отсюда ровно то, что описал репортёр: на компьютере работает, с телефона
нет. Заодно чужие адреса внутри этих /16 уходили в MTProto-туннель и умирали
там.

Список сетей теперь опубликованный самим Telegram
(core.telegram.org/resources/cidr.txt), проверка по маске префикса, IPv4 и
IPv6, с тестами на края блоков и на соседей за границей.

Отдельно разрешены имена веб-инфраструктуры: telegram.org, t.me, telegram.me,
telesco.pe, cdn-telegram.org. Это не MTProto, а обычный HTTPS — клиент ходит
туда за конфигурацией, превью и файлами CDN, и на телефоне эти запросы идут
через тот же прокси. Заворачивать их в туннель нельзя, поэтому они идут прямым
релеем. Совпадение по границе метки, так что telegram.org.example.com —
посторонний домен. В ограниченном режиме имя разрешается заранее и адреса
внутри локальной сети отбрасываются: назначение выбирает чужое устройство, и
DNS-ответ не должен открывать доступ к 192.168.х этой машины.

И главное для разбора следующего такого случая: отказ перестал быть
молчаливым. Появился счётчик «Отклонено» в диагностике и в строке статуса CLI,
отклонённый адрес один раз называется в журнале, и отдельно отмечается первое
подключение с каждого сетевого адреса. Без этого «с телефона не работает»
неразличимо распадалось на два случая: телефон не дошёл до машины — и дошёл,
но попросил адрес, который мы не пускаем. Теперь первый виден как ноль
соединений и ноль отказов, второй — как соединения есть, отказы растут.

Co-authored-by: by-sonic <171230345+by-sonic@users.noreply.github.com>
2026-08-19 09:07:50 +03:00
Никита Sonic 1a56e1c96e feat(gui): показывать адрес для других устройств в LAN-режиме (#40)
Второй человек за неделю не смог подключить телефон, потому что адрес негде
взять. В #36 @modx-arseniy вписал в Telegram на Android настройки «как в
клиентах на винде» — то есть 127.0.0.1, который на другом устройстве означает
само это устройство, а не компьютер с прокси. Раньше в README было написано,
что адрес показывается в интерфейсе; это оказалось неправдой, и я тогда
исправил README вместо приложения. Теперь исправлено приложение.

В LAN-режиме на главном экране появляется карточка с адресом вида
192.168.1.7:1080. Нажатие копирует его в буфер. Под адресом — предупреждение
про 127.0.0.1, потому что именно туда люди и уходят.

Адрес берётся из работающего слушателя, а не из текущих настроек: если человек
поменял порт, но не перезапустил прокси, показать надо тот, на котором прокси
реально поднят.

Логика вынесена в чистую функцию share_address и покрыта тестами: на выключенном
прокси и на loopback делиться нечем, в LAN-режиме адрес обязан содержать порт и
не быть ни 0.0.0.0, ни 127.0.0.1.

Co-authored-by: by-sonic <171230345+by-sonic@users.noreply.github.com>
2026-08-09 18:50:02 +03:00
Никита Sonic fe85784550 fix: диагностика перестаёт врать — падения маршрутов и потеря секрета (#38)
Два дефекта одного класса: состояние, которое не отражает реальность. Оба
найдены по данным из #32 и #37.

#32. Присланный лог показывал «сбоев 0» при том, что DC2 и DC4 всегда шли через
«Запасной Telegram IP», а DC203 всегда через «Системный DNS» — то есть
основные закреплённые адреса не использовались ни разу. Причина в счётчике:
ws_failures растёт только когда упали ВСЕ маршруты и соединение не состоялось.
Падения отдельных маршрутов через record_failure не попадали никуда, поэтому
перебор с откатом на запасной адрес выглядел как полное отсутствие проблем.

Добавлен route_failures: растёт на каждое падение маршрута, виден в строке
статуса CLI и в диагностике интерфейса. Теперь по логу сразу видно, что
закреплённый адрес мёртв, а не приходится это выводить.

#37. Симптом: Telegram пишет «прокси настроен неверно и будет отключён», при
этом Check status показывает Available. Это картина несовпадения секрета: TCP
проходит, init не разбирается под другим секретом, соединение закрывается.

Секрет мог меняться молча:

  #[cfg(not(unix))]
  fn write_secret_file(path: &Path, value: &str) {
      let _ = std::fs::write(path, value);   // ошибка выброшена
  }

create_dir_all рядом — так же. Если запись в %APPDATA%\TGLock\secret не
удавалась, программа генерировала новый секрет при каждом запуске и ничего об
этом не сообщала.

Теперь write_secret_file возвращает Result, load_or_create_secret_at отдаёт
StoredSecret с полем write_error, а оба интерфейса показывают предупреждение:
CLI строкой при старте, GUI записью в журнал. Прокси при этом продолжает
работать — просто до перезапуска.

Тесты: every_route_failure_is_counted, a_failed_write_is_reported_instead_of
_swallowed (родитель пути — файл, поэтому каталог не создать),
a_successful_write_reports_no_error (секрет переживает второй запуск).

В диагностике интерфейса добавлена подсказка: падения маршрутов больше нуля
при работающем Telegram — норма, значит закреплённый адрес недоступен и
подключение идёт через запасной.

Co-authored-by: by-sonic <171230345+by-sonic@users.noreply.github.com>
2026-08-04 19:43:21 +03:00
Никита Sonic 9bacc488a5 docs: синхронизировать документацию с состоянием кода (#27)
Аудит репозитория после #25 и #26. Расхождения между тем, что написано, и тем,
что есть:

- README обещал `tglock-cli-*` в таблице загрузок, но в релизе
  v2.0.0-beta.1 такого артефакта нет: задача `cli` в release.yml сработает
  только на следующем теге. Заменено на честную формулировку с командой
  сборки из main.
- ui/main.ts держал начальным значением маршрута строку «Автоматический
  маршрут», которую бэкенд больше не отдаёт: при коде 0 возвращается
  «Маршрут ещё не выбран». Иначе до первого опроса статуса интерфейс
  показывал название несуществующего маршрута.
- ARCHITECTURE_V2.md не упоминал ни разделения на библиотеку и два бинаря, ни
  фичи `gui`, ни правила различения протоколов, ни политики прямого релея, ни
  того, что GUI не запускается без WebView. Добавлены разделы, а последнее
  внесено в Current limitations вместе с отсутствием подписи бинарей.
- ISSUE_AUDIT.md описывал состояние на 29 июля. Сам аудит оставлен как
  фиксация на дату, сверху добавлен статус на 30 июля: что закрыто, что
  осталось открытым и почему, и что исправлено вне списка issues.
- HABR.md — черновик статьи, а не документация, но был указан в README как
  «подробный технический разбор». В нём «два файла, 350 строк, четыре
  платформы», тогда как сейчас 2872 строки Rust, семь файлов и три платформы
  плюс headless. Добавлена шапка с поправкой, ссылка в README переписана так,
  чтобы читателя не отправляли к устаревшим числам за документацией.

Проверено: fmt, clippy в обоих вариантах сборки, 47 + 12 тестов, npm run build,
все ссылки на файлы в .md существуют.

Co-authored-by: by-sonic <171230345+by-sonic@users.noreply.github.com>
2026-07-30 13:34:43 +03:00
Никита Митусов c25bea1d92 feat: rebuild TGLock with adaptive transport and Tauri UI 2026-07-29 15:22:54 +03:00