Find a file
Kirill Minovsky 60c682ad24 docs(tls): write down the pinned-hello version deviation
Test 2 pins one version per phase, and rustls writes `supported_versions` from
the config, so a browser profile advertises `[GREASE, 0x0304]` where the Chrome
107 it imitates sends `[GREASE, 0x0304, 0x0303]` -- `[0x0304]` against
`[0x0304, 0x0303]` for Firefox, which greases nothing.

JA3 and JA4 cannot see it (they hash extension codes and the maximum version),
so the pinned bundle hashes still match, but a middlebox reading the body can.
It is kept: one hello per stack is what makes the TLS 1.3 column a TLS 1.3
verdict, since a hello offering both would let the server choose. Written next
to the builders that pin, and asserted in the test that pins the lists, so it
cannot drift unnoticed either way.
2026-09-14 00:04:57 +03:00
.cargo build: vendor patched rustls, h2/compression features, release-local profile 2026-09-13 19:41:43 +03:00
.github/workflows chore: release v5.0.0-alpha.8 (UPX for routers, low-space auto-selection) 2026-09-07 20:21:12 +03:00
crates docs(tls): write down the pinned-hello version deviation 2026-09-14 00:04:57 +03:00
docs feat(i18n): localized CLI help and probe details, config and README docs 2026-09-13 19:41:52 +03:00
vendor/rustls chore: untrack the rustls build directory a cargo check left behind 2026-09-13 22:13:59 +03:00
.gitignore chore: keep run exports out of git 2026-09-13 22:32:39 +03:00
Cargo.lock perf(tls): decode compressed certificates without brotli's encoder 2026-09-13 23:21:42 +03:00
Cargo.toml perf(tls): decode compressed certificates without brotli's encoder 2026-09-13 23:21:42 +03:00
config.yml feat(fingerprint): pinned curl-impersonate versions, and probes that speak h2 2026-09-13 20:49:16 +03:00
Cross.toml fix(ci): use edge container for aarch64-linux-android and clean job names in release workflow 2026-09-05 01:38:47 +03:00
domains.txt chore: ignore the crash log, drop a dead domain from the list 2026-09-13 19:43:48 +03:00
install.ps1 release: 5.0.0-alpha.11 2026-09-13 21:50:34 +03:00
install.sh release: 5.0.0-alpha.11 2026-09-13 21:50:34 +03:00
README.md fix(test6): the domain box now shows what it will probe, and a pasted URL survives 2026-09-13 22:32:21 +03:00
tcp16.json feat: Native Rust engine (v4.2.0) 2026-09-04 00:15:58 +03:00
whitelist_sni.txt feat: Native Rust engine (v4.2.0) 2026-09-04 00:15:58 +03:00


"Маяк у гаснущего горизонта свободного интернета"
Сквозь цифровые сумерки. Смотритель маяка, Runni

🦀 DPI Detector (Native Rust Engine)

Language: Rust License: MIT Release Telegram

Полностью переписанный нативный инструмент для анализа цензуры и блокировок трафика на чистом 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:

  1. Автоматически находит директорию /opt/bin (Entware) и устанавливает бинарник туда (сохраняется после перезагрузки).
  2. Контролирует свободное место: если на накопителе осталось < 7 МБ, скрипт автоматически скачивает компактную версию со сжатием UPX (~1.4 МБ вместо ~3.9 МБ), защищая роутер от переполнения Flash или tmpfs.
  3. Заменяет файл атомарно (.tmp.$$ -> dpi-detector) с проверкой архитектуры через --version (при сбое UPX автоматически переключается на стандартный бинарник).
  4. Поддерживает принудительный выбор версии: DPI_UPX=1 (компактная) или DPI_UPX=0 (стандартная).
  5. Поддерживает запуск напрямую через 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