A review pass (Gemini) claimed a list of duplications, a keyboard-layout bug in the burst-settings screen and a diverged timeout classification. Verified each against the source; this is the part that held up. Bugs: * Burst settings answered only Latin w/s/a/d/q. The menus answer the physical key on six layouts by listing each layout's characters, and this screen simply had no such list -- so a user in a Cyrillic, Persian, Bopomofo or Hebrew layout could not move the cursor there. Key handling now resolves once (`nav_key`: IME width, then the Latin key at the same physical position) and every screen uses it; the per-layout character lists are gone. The domain box is exempt on purpose: inside it a letter is text. * `classify_connect_error_full` classified timeouts twice. The message-text branch knew the send/read stages, the OS-code branch did not, so a WSAETIMEDOUT (10060) while reading was reported as a bare `Timeout` instead of `ReadTimeout` -- on Windows, whose error text carries no "timeout". Both branches now return `timeout_at_stage(stage)`. Duplication, all verified present: * DNS A/AAAA extraction: four copies -> `DnsResponse::collect_ips`. * hyper io::Error unwrapping: `hyper_err_info` (domains) and `hyper_info` (tcp16) -> one `probe::http::hyper_err_info`. * ASN display: `asn_display_of` (core) and a copy in the run loop -> `Tcp16Target::display_asn`. * config list parsing: file and embedded variants of both the domain and the SNI lists -> `parse_domains` / `parse_whitelist_sni`. * box glyphs: three copies -> `render::box_chars`. * winreg REG_SZ/REG_MULTI_SZ reads: four copies -> `netinfo::reg_strings`. Cost: `create_verifying_tls_config` and the DoH variant rebuilt the WebPKI root store and its verifier index on every call -- once per DoT connection and per DoH request, ~70 times in a DNS run. Both are immutable and `Arc`-shared, so they are built once. Structure: main.rs 1778 -> 640 lines. `runner.rs` takes the run orchestration (`run_test_suite` and the burst plan), `menu.rs` takes the key handling and the post-run/legend screens, `terminal.rs` takes the Win32 console probe -- the only `unsafe` in the crate, now alone in its file. The single-threaded runtime keeps its `current_thread` flavour, with the reasoning (docs/OPTIMIZATIONS.md §2.5, ~50-120 KB, 1-2 core routers, auto_gate) written down at the attribute, since the review read it as an oversight. Verified: 107 + 34 tests, clippy --all-targets 0 warnings, a live `-t 3 --json` run, and a pty run of the TUI -- Cyrillic keys move the menu and the burst screen (they did not before), a full 35-domain x 4-profile test 6 run printed its table, and the domain box still inserts Latin text without transliterating. |
||
|---|---|---|
| .cargo | ||
| .github/workflows | ||
| crates | ||
| docs | ||
| vendor/rustls | ||
| .gitignore | ||
| Cargo.lock | ||
| Cargo.toml | ||
| config.yml | ||
| Cross.toml | ||
| domains.txt | ||
| install.ps1 | ||
| install.sh | ||
| README.md | ||
| tcp16.json | ||
| whitelist_sni.txt | ||
"Маяк у гаснущего горизонта свободного интернета"
Сквозь цифровые сумерки. Смотритель маяка, Runni
🦀 DPI Detector (Native Rust Engine)
Полностью переписанный нативный инструмент для анализа цензуры и блокировок трафика на чистом Rust:
- 0 зависимостей: Никакого Python, C/C++ библиотек или системного OpenSSL. Собрано на
rustls,ringиRustCrypto. - Легковесный: Размер бинарника всего ~3.2 МБ, потребление оперативной памяти в работе — 3–6 МБ RAM.
- Максимальная кросс-платформенность: Нативная поддержка роутеров (MIPS, ARM), Windows (от Windows 7 до 11), Linux musl и macOS.
- Интерактивный TUI + Batch: Полноценное стрелочное меню в терминале и быстрый тихий запуск при передаче аргументов командной строки.
🚀 Быстрый запуск в 1 строку (Без установки)
🐧 Linux, macOS, роутеры Keenetic, OpenWrt & Entware
curl -fsSL https://raw.githubusercontent.com/Runnin4ik/dpi-detector/rust/install.sh | sh
📱 Android (Termux)
curl -fsSL https://raw.githubusercontent.com/Runnin4ik/dpi-detector/rust/install.sh | sh
Скрипт автоматически определит Termux, установит нативный Android-бинарник в $PREFIX/bin/dpi-detector и запустит его. Для повторного запуска достаточно ввести dpi-detector.
🪟 Windows (PowerShell)
irm https://raw.githubusercontent.com/Runnin4ik/dpi-detector/rust/install.ps1 | iex
📦 Готовые бинарные сборки
Все сборки полностью статичны и не требуют внешних библиотек:
| Платформа / Устройство | Архитектура | Бинарник в релизах |
|---|---|---|
| Windows 10, 11, Server | x86_64 | dpi-detector-windows-x86_64.exe |
| Windows 7, 8, Server 2008/2012 | x86_64 | dpi-detector-windows-7-x86_64.exe |
| Linux (PC, Серверы, VPS) | x86_64 musl | dpi-detector-linux-x86_64 |
| Роутеры ARM64 (Keenetic Hopper/Titan 2, RPi, OpenWrt) | aarch64 musl | dpi-detector-linux-arm64 |
| Роутеры ARMv7 (Keenetic Titan 1/Hero, Asus RT-AX58U) | armv7hf musl | dpi-detector-linux-armv7 (и -upx) |
| Роутеры MIPS LE (Keenetic Viva/Giga, MT7621, OpenWrt) | mipsel musl | dpi-detector-linux-mipsel (и -upx) |
| Роутеры MIPS BE (Atheros, Qualcomm, OpenWrt) | mips musl | dpi-detector-linux-mips (и -upx) |
| macOS Apple Silicon (M1, M2, M3, M4) | aarch64 | dpi-detector-macos-arm64 |
| macOS Intel | x86_64 | dpi-detector-macos-intel |
| Android ARM64 (Смартфоны, планшеты, Termux, ADB) | aarch64 bionic | dpi-detector-android-arm64 |
| Android ARM32 (Старые устройства, Android TV) | armv7 bionic | dpi-detector-android-armv7 |
🛠️ Установка на роутеры (Keenetic / OpenWrt / Entware)
При запуске на роутере скрипт install.sh:
- Автоматически находит директорию
/opt/bin(Entware) и устанавливает бинарник туда (сохраняется после перезагрузки). - Контролирует свободное место: если на накопителе осталось
< 7 МБ, скрипт автоматически скачивает компактную версию со сжатием UPX (~1.4 МБ вместо ~3.9 МБ), защищая роутер от переполнения Flash илиtmpfs. - Заменяет файл атомарно (
.tmp.$$->dpi-detector) с проверкой архитектуры через--version(при сбое UPX автоматически переключается на стандартный бинарник). - Поддерживает принудительный выбор версии:
DPI_UPX=1(компактная) илиDPI_UPX=0(стандартная). - Поддерживает запуск напрямую через SSH:
# Установка и запуск меню
curl -fsSL https://raw.githubusercontent.com/Runnin4ik/dpi-detector/rust/install.sh | sh
# Повторный запуск после установки
/opt/bin/dpi-detector
⚙️ Параметры командной строки (CLI)
Использование: dpi-detector [ОПЦИИ]
Опции:
-t, --tests <TESTS> Номера тестов ('012', '1', '2' или "1,2,3")
--json Вывод в машиночитаемом JSON
-v, --verbose Подробное / отладочное логирование
-l, --lang <LANG> Язык интерфейса (ru, en, zh, fa, auto). По умолчанию auto
--profile <PROFILE> Региональный профиль цензуры (ru, ir, cn, global)
--legend Показать легенду статусов и выйти
-p, --proxy <URL> URL SOCKS5-прокси (socks5://127.0.0.1:1080)
-c, --concurrency <N> Лимит параллельных запросов
-d, --domain <DOMAIN> Тестируемый домен (флаг можно повторять)
-o, --output <PATH> Путь к файлу отчёта
--domains <PATH> Путь к файлу со списком доменов
--tcp16 <PATH> Путь к файлу целей TCP16
--ascii Только ASCII для старых консолей (без Unicode-глифов и рамок)
--fingerprint <PROFILE> Профиль отпечатка TLS ClientHello (rustls|custom|chrome|safari). custom — форма Firefox 133, chrome — Chrome 107 / Edge 99-101, safari — Safari 15.5-18.4 из curl-impersonate; все предлагают h2
--burst <N> Fingerprint/Сибирская блокировка (тест 6): одновременных запросов за раунд [по умолчанию: 4]
--burst-timeout <SECONDS> Тест 6: таймаут одного рукопожатия, секунды [по умолчанию: 8]
--burst-profiles <LIST> Fingerprint для теста 6: all|rustls,custom(firefox133),chrome(chrome107),safari(safari155) [по умолчанию: all]
--burst-tls <VERSION> Версия TLS для теста 6: 1.2|1.3 [по умолчанию: 1.3]
--burst-alpn <PROTOCOL> ALPN для теста 6: h2 (предлагает h2 с откатом на http/1.1)|http/1.1 (только http/1.1) [по умолчанию: h2]
-h, --help Показать справку
-V, --version Показать версию
Справка и все сообщения локализованы: dpi-detector --lang en --help, --lang zh --help, --lang fa --help. Любой другой текст интерфейса - таблицы, легенда, предупреждения - тоже следует за --lang; --json не зависит от языка (ключи и значения detail остаются каноническими).
Локализация (crates/dpi-core/src/i18n)
Messages- по одному полю на строку, четыре блока (En,Ru,Zh,Fa); шаблоны используют{}.legend_sections*()- таблицы--legend;details.rs- перевод канонических деталейDET_*(русский - исходный язык, выводится как есть).- Язык Farsi рендерится Finglish (латиница, без персидской графики и диакритики).
- Статусные бейджи и протоколы (
OK,BLOCKED,TLS RST,SNI,ClientHello) не переводятся никогда.
Номера доступных тестов:
0— Информация о сети и системе (внешний IP, провайдер, AS, страна, тип NAT)1— Доступность DNS-серверов (проверка UDP 53, DoH Cloudflare/Google/Quad9/Yandex/AdGuard)2— Доступность сайтов (HTTP, TLS 1.2, TLS 1.3, классификация блокировок DPI)3— Доступность CDN и хостингов (тест 16 KB) (детект сброса соединений после 14–34 КБ данных)4— Поиск белых SNI (перебор рабочих SNI для заблокированных AS: цель отбирается по 16 KB-обрыву, TLS RST и TLS DROP)5— Доступность Telegram (проверка доступности и скорости датацентров DC1–DC5)6— Fingerprint/Сибирская блокировка (N одновременных TLS-рукопожатий на один домен выбранными отпечатками: сколько ответили и что именно перестало отвечать)7— Легенда статусов (справка по вердиктам)
Тест 6: Fingerprint/Сибирская блокировка
При выборе теста 6 открывается свой экран настроек: число одновременных рукопожатий, таймаут одного рукопожатия в секундах, версия TLS (1.2/1.3), протокол HTTP в ALPN (h2 — как у браузеров, с откатом на http/1.1, или http/1.1 — только он), домен (в поле показан хост, который проверял прошлый прогон; первый набранный символ заменяет его, а пустое поле — весь список, как в тесте 2; вставленный URL приводится к имени хоста тем же очистителем, что и список из конфига) и Fingerprint (по умолчанию все). Поле домена подсвечивается: серый фон — строка выбрана, синий — в поле идёт ввод. Q/Esc отменяет тест.
На каждый домен и отпечаток выполняется N одновременных рукопожатий выбранной версии TLS — отдельные TCP-соединения, свежие сессии без resumption. Две оси дают 4 комбинации, и они измеряют разные вещи: 1.3 против 1.2 — реакцию на версию протокола, h2 против http/1.1 — реакцию на ALPN (некоторые триггеры срабатывают именно на h2-хелло; JA4 при этом меняет поле ALPN, h2 → h1). В таблице M/N по каждому отпечатку (зелёный — ответили все, жёлтый — часть, красный — никто), в колонке «Детали» — самый частый вердикт отказа (TLS RST, TLS DROP, SYN DROP, …). Тест идёт последним: серия рукопожатий может «сжечь» цель на время, и измерения после неё недействительны.
Во время прогона печатается одна живая строка Тестируем: CHROME 107 3/4 12/35 · 00:04 — какая форма сейчас в эфире, который это раунд из скольких, сколько хостов раунда готово и сколько идёт время. Отдельной шапки с настройками и блок «Итог» тест не выводит: отпечаток с версией и так виден в колонках таблицы, а пустой итог не рисуется.
Из неинтерактивного запуска настройки задаются флагами --burst, --burst-timeout, --burst-profiles, --burst-tls, --burst-alpn; в --json результат лежит в аддитивном ключе results.fingerprint_burst (вместе с tls и alpn этого прогона).
🏗️ Сборка из исходников
Для самостоятельной сборки потребуется установленный Rust (версии 1.80+):
git clone https://github.com/Runnin4ik/dpi-detector.git -b rust
cd dpi-detector
cargo build --release -p dpi-detector
Скомпилированный бинарник будет находиться в target/release/dpi-detector (dpi-detector.exe на Windows).
Для статической сборки под Windows без внешних зависимостей CRT:
RUSTFLAGS="-C target-feature=+crt-static" cargo build --release -p dpi-detector