Commit graph

78 commits

Author SHA1 Message Date
Kirill Minovsky
24512b9731 fix(ui): open the test 7 settings screen only on a real terminal
The screen was gated on raw-mode availability alone, so `-t 7` with stdout
redirected (`> out.txt`, `| tee`) still painted it and then waited forever for a
keypress nobody could send. It now requires a terminal on both ends — stdin to
answer it and stdout to draw it — and falls back to the CLI flags otherwise,
which is what `--burst`/`--burst-timeout`/`--burst-profiles` are for.

Found by the release smoke test: the piped run hung until the harness deadline.
2026-09-13 20:02:56 +03:00
Kirill Minovsky
e3c86c306c feat(ui): test 7 screen and report, fingerprint row, wrapped summary
Test 7 gets its own settings screen (digit 7 in the menu, or `-t 7` on a
terminal): simultaneous requests and per-attempt timeout on `←→`, a grey input
box for the host that goes active on `→` (caret, letters) and back to grey on
`←` (so `w/a/s/d/q` are host characters while typing, and arrow keys keep
navigating), a profile cycler shaped like the menu's fingerprint row
(`all` → RUSTLS → FIREFOX → CHROME → SAFARI), and the default the empty field
falls back to printed under it. `Q`/`Esc` cancels and skips the test. The
screen's rows are built by a pure function, so its layout (fixed box width, the
prompt under the input box, the footer inside 80 columns) is covered by tests.

The report gains the burst table (`answered/total` per profile, the dominant
failure in the detail column, Latin tokens per rule 4), a Stress row in the
summary, and an additive `results.fingerprint_burst` payload; the machine
schema is otherwise untouched.

The human header names the active profile; long summary values wrap inside the
panel instead of pushing the border out (the resolver-hijack list is the one
that did), and every probe detail is rendered through the i18n detail map.
2026-09-13 19:43:48 +03:00
Kirill Minovsky
e649050292 fix(classify): verdicts from the OS error code, redirect rule for foreign hosts
Windows localizes socket errors, so a reset arriving after the ClientHello was
classified from its English text alone: it fell through to UNKNOWN and the cell
showed raw OS prose ("connection error | Удаленный хост принудительно разорвал
существующее подключение") instead of TLS RST. `hyper_err_info` (domains.rs) and
`hyper_info` (tcp16.rs) now return the `raw_os_error`/`ErrorKind` alongside the
message, and `classify_read_error` takes them, so the reset is recognised from
WSAECONNRESET/104 exactly like the Python prototype's `get_errno_from_chain`.

The redirect rule now matches the prototype in both phases: a redirect that
stays on the same host or a subdomain is a plain OK, a foreign one is a red
REDIR (`RedirSuspect`, `is_ok_status()` false). `resolve_location` follows
`urljoin` semantics — a protocol-relative `//host/x` belongs to the host it
names, a relative Location resolves against the phase's base — where the old
hand-rolled version treated `//evil.com/x` as a local path (green OK) and
claimed "→ https" for relative redirects in the HTTP phase.

The 17-case matrix in `domains::tests` pins the Python behaviour; the JSON token
for a suspect redirect stays the frozen `redir`.
2026-09-13 19:43:41 +03:00
Kirill Minovsky
09e8833a44 feat(test7): simultaneous-handshake fingerprint stress
Test 7 fires N handshakes from one fingerprint at one host and reports how many
were answered — the measurement behind "a few parallel handshakes with this
shape and the site goes dark", which no other test can express: test 2 makes two
handshakes per host, sequentially, and `--concurrency` spreads work across
*different* hosts.

`probe/burst.rs` connects every attempt in parallel, then releases the
handshakes together through a barrier, so they are simultaneous rather than
merely concurrent; each attempt is its own TCP connection with a fresh session
(no resumption) and sends no HTTP request on top. Verdicts come from the shared
classifiers (`DpiProbeStream` + the connect/TLS error classifiers), so a lost
handshake keeps the vocabulary of the report — TLS RST, TLS DROP, SYN DROP.

`BurstSettings::clamped` keeps interactive input inside the range the test stays
meaningful in (2-16 requests, 1-60 s per attempt), and the report exposes
answered/lost per profile plus the dominant failure, which is what the table
shows.
2026-09-13 19:43:41 +03:00
Kirill Minovsky
b82f6a3f4b feat(probes): SNI whitelist search rework, semver update channel, misc cleanups
Test 4's baseline no longer requires a 16-20 KB transfer-window kill: a
TLS-stage kill of the ClientHello (TLS RST, TLS DROP, TLS ABORT) also proves the
target is DPI-filtered and worth an SNI search, while plain connectivity
failures (SYN timeout, refused, unreachable) stay out — those are the
ban/rate-limit signal the batch loop aborts on.

The update check now compares semver properly (numeric vs alphanumeric
prerelease identifiers, derived order) and picks a channel: a prerelease build
is offered every published release, a stable build only stable ones.

Smaller cleanups: the Telegram probe uses the shared `DET_SYN_TIMEOUT_SHORT`
token and reports its phase through the progress callback; the public-IP fetch
applies its timeout to the whole redirect chain instead of once per hop (four
hops could spend four times the caller's deadline); the unused
`RegionProfile::display_name` is gone.
2026-09-13 19:43:32 +03:00
Kirill Minovsky
e113f567ce feat(dns): concurrent availability blocks, pinned resolution, HTTP/2 DoH
Test 1 now runs its blocks (DoH, DoT, UDP, egress) concurrently and reports all
of them on one live line: `ProgressBlock` in lib.rs names each counter with a
canonical token (rule 4), and the availability loop drives them through one
semaphore sized by MAX_CONCURRENT — the separate DNS_PROBE_CONCURRENCY cap is
gone (config.yml documents the change).

Two robustness fixes came out of that work: a DoH warmup window bounds the
throwaway query that only opens the HTTP/2 stream, and a budget check stops
starting new queries when the outer probe cap would cancel the whole future and
take the collected answers with it.

`dns/resolve.rs` pins resolved hosts (1 h, 256 entries, failures never cached):
the same DoH/DoT endpoint name is re-resolved for every retry, and each miss
costs a system-resolver timeout plus the bootstrap chain. `dns/doh.rs` exposes
`is_h2()` and the connection-poisoning state, because on HTTP/1.1 an abandoned
request leaves hyper unable to resynchronise the stream.
2026-09-13 19:43:32 +03:00
Kirill Minovsky
2661dae00f feat(tls): ClientHello fingerprint profiles (RUSTLS/FIREFOX/CHROME/SAFARI)
Four shapes a TLS probe can present, selectable per run with `--fingerprint`
(config `TLS_FINGERPRINT`, menu row, and the token in `--json`):

* `rustls` — the untouched baseline every earlier measurement was taken with;
* `custom` (shown as FIREFOX) — uTLS `HelloFirefox_148`, the shape Xray/REALITY
  clients present, including the hybrid X25519MLKEM768 key share;
* `chrome` / `safari` — the JA3s of the curl-impersonate profiles the reported
  TSPU trigger list names (chrome 99-116 / edge 99-101, safari 15.5-18.4).

`net/fingerprint.rs` holds the profile data, `net/ja3.rs` extracts JA3 from the
wire bytes so the tests can pin each profile to the JA3 of the client it
reproduces, and `net/pq_kx.rs` supplies the pure-Rust hybrid group (rustls ships
it only in the aws-lc-rs provider, which links C code).

Deliberate deviations, each measured: no ECH (a synthesized body makes
Cloudflare, Google and dns.google answer `fatal alert: DecodeError`), ALPN is
`http/1.1` only (the probes speak HTTP/1.1; JA3 hashes extension types, so the
extension-16 entry still matches), and no HTTP/2 fingerprint. The default
profile keeps `cert_decompressors` cleared, so enabling the compression
features here does not change what the baseline sends.

`cargo run --example tls_fingerprint dump|live <profile>` prints the wire shape
and runs real handshakes; the unit tests pin both curl JA3s and the Firefox
extension set.
2026-09-13 19:43:22 +03:00
Kirill Minovsky
31832fe717 feat(i18n): localized CLI help and probe details, config and README docs
The `clap` command is now built at runtime from `Messages`, so `--help` and
every value name follow `--lang` on the same command line (`--fingerprint`,
`--burst`, `--burst-timeout`, `--burst-profiles` included).

`i18n/details.rs` maps the canonical `DET_*` probe details to display text in
all four languages (Russian stays the source language and passes through), with
the new `DET_*` constants in `classify/constants.rs`; machine JSON keeps
emitting the identifiers verbatim (rule 5).

config.yml documents TLS_FINGERPRINT and the concurrency change (MAX_CONCURRENT
is now the cap for every test, DNS_PROBE_CONCURRENCY is gone), plus the DNS
truth-fallback keys; the README lists the new options and test 7.
2026-09-13 19:41:52 +03:00
Kirill Minovsky
f9c74c2967 build: vendor patched rustls, h2/compression features, release-local profile
`vendor/rustls` is upstream rustls 0.23.43 plus one hook: a ClientHello
profile override (`src/client/hello_profile.rs`) wired in through
`[patch.crates-io]`, so tokio-rustls, hyper and rustls-rustcrypto all
resolve to the same instance. Upstream gives no control over the ClientHello
(cipher list comes from the provider, extension order is randomized, browser
extensions are never emitted), and the ready-made impersonation crates are
either BoringSSL (C code, rules out the router targets) or pinned to an older
rustls. `exclude = ["vendor/rustls"]` keeps its upstream tests out of the
workspace; `PATCH.diff` reproduces the tree.

`brotli` + `zlib` let the profile advertise certificate compression and decode
what a server then sends; `ml-kem` + `x25519-dalek` back the pure-Rust
X25519MLKEM768 group the Firefox profile offers.

`.cargo/config.toml` pins rust-lld for x86_64-pc-windows-msvc (112 s -> 56 s
per fat-LTO relink here), and `[profile.release-local]` is the release knobs
minus whole-program LTO: 1.3 s per edit instead of 52 s, for iteration only
(it is not shippable, releases still build with `--release`).
2026-09-13 19:41:43 +03:00
Kirill Minovsky
49a3ed248f chore: release v5.0.0-alpha.7 2026-09-06 20:00:04 +03:00
Kirill Minovsky
1ac100fa8a feat(menu): make interactive menu fully async using crossterm EventStream and tokio::select 2026-09-05 20:31:57 +03:00
Kirill Minovsky
e981086b76 fix: restore mouse selection, fix tokio update check starvation, and use bullet radios on modern terminals 2026-09-05 20:16:04 +03:00
Kirill Minovsky
14eaea12be fix(menu): fix update check hang and switch radio buttons to classic (x)/( ) 2026-09-05 20:06:16 +03:00
Kirill Minovsky
6aaf9ecdaa fix(menu): pad hotkey and notice rows with spaces to prevent trailing artifacts on language switch 2026-09-05 19:57:04 +03:00
Kirill Minovsky
6cbc8c4429 feat(win7): add 16-color Win32 Console API emulation for legacy Windows 2026-09-05 19:50:36 +03:00
Kirill Minovsky
af769ff17b feat(render): preserve native CP866 single-line box drawing instead of crude ASCII 2026-09-05 19:36:18 +03:00
Kirill Minovsky
df1114cc0a feat(win7): disable QuickEdit mode, hide cursor, buffer menu writes, and support NO_COLOR 2026-09-05 19:28:14 +03:00
Kirill Minovsky
2b69e33a77 chore: remove Persian and Arabic language support and bidi dependencies 2026-09-05 19:14:47 +03:00
Kirill Minovsky
7b46f13862 fix(i18n): unify menu layout, remove ZWNJ, use Latin tech terms, and complete Persian/Arabic polish 2026-09-05 18:52:06 +03:00
Kirill Minovsky
f70c9bb164 fix(i18n): ensure final Yeh visibility in all fonts, remove colon issues, and translate live badge in menu 2026-09-05 17:39:23 +03:00
Kirill Minovsky
f6b1fdd158 fix(i18n): use pure Persian menu strings (Variant A) to eliminate BiDi direction-switching bugs 2026-09-05 17:24:59 +03:00
Kirill Minovsky
6d55936de1 fix(i18n): replace Persian localization with authentic native technical terms and fix key labels 2026-09-05 17:16:36 +03:00
Kirill Minovsky
050877a57f fix(ui): attach colons to the right of RTL labels and preserve dot numbers 2026-09-05 17:07:59 +03:00
Kirill Minovsky
e7e8f42393 fix(i18n): isolate digit dots with LRM, shape language labels, and fix LTR-embedded word order 2026-09-05 16:59:57 +03:00
Kirill Minovsky
58cb77e30b fix(ui): adopt natural RTL layout per reviewer step-by-step technical plan 2026-09-05 16:46:57 +03:00
Kirill Minovsky
58d5fa4a5d fix(ui): place RTL category cursor directly adjacent to label 2026-09-05 16:17:49 +03:00
Kirill Minovsky
33f20d81ad fix(ui): polish RTL layout with consistent radio bullets, clean IPv4/IPv6 row, and 2-space padding 2026-09-05 16:10:32 +03:00
Kirill Minovsky
2aaaf74002 fix(i18n): refine Persian RTL layout with atomic LTR spans, isolated hotkeys, and right-aligned descriptions 2026-09-05 16:02:49 +03:00
Kirill Minovsky
aff5744d4f feat(i18n): fix Persian/Arabic BiDi shaping and adapt menu layout to RTL 2026-09-05 15:53:11 +03:00
Kirill Minovsky
dcb52da647 chore(release): bump version to 5.0.0-alpha.5 2026-09-05 12:37:38 +03:00
Kirill Minovsky
6bf3e5c4dc fix(classify): map rustls TLS-version mismatch to NO TLS1.3 instead of UNKNOWN 2026-09-05 03:01:10 +03:00
Kirill Minovsky
5fb35f4658 Revert "fix(windows): resolve false-positive Wacatac.B!ml (lto = thin, strip = debuginfo, dynamic CRT, sync PE version)"
This reverts commit f57f8a28a5.
2026-09-05 03:00:49 +03:00
Kirill Minovsky
f57f8a28a5 fix(windows): resolve false-positive Wacatac.B!ml (lto = thin, strip = debuginfo, dynamic CRT, sync PE version) 2026-09-05 02:20:51 +03:00
Kirill Minovsky
2597cc259f perf(build): switch to tokio current_thread and strip unwind tables on linux targets 2026-09-05 00:12:04 +03:00
Kirill Minovsky
d1b5b7ec77 perf(build): optimize binary size (opt-level = z, deduplicate crates, compress icon) 2026-09-04 23:36:12 +03:00
Kirill Minovsky
2063cae5a2 feat(ui): update test suite names to unified availability style 2026-09-04 23:23:53 +03:00
Kirill Minovsky
80220e6220 fix(ui): preserve native language endonyms in menu without bidi reshaping 2026-09-04 23:07:12 +03:00
Kirill Minovsky
3e5b5786df refactor(cli): remove redundant --batch flag 2026-09-04 23:02:23 +03:00
Kirill Minovsky
db46bb53a0 feat(i18n): support Chinese fullwidth IME characters, Bopomofo, and European/Middle-Eastern layouts 2026-09-04 22:53:44 +03:00
Kirill Minovsky
4057bec98f feat(ui): support arrowless navigation (WASD, Vim, Tab) and multi-layout hotkeys 2026-09-04 22:47:24 +03:00
Kirill Minovsky
9cca52e1e9 fix(i18n): localize all progress phases, diagnostic messages, and detail strings 2026-09-04 22:43:16 +03:00
Kirill Minovsky
1bf1434ba1 fix(i18n): fallback to English by default when language cannot be determined 2026-09-04 22:20:00 +03:00
Kirill Minovsky
5d63dab013 fix(tui): enable libc feature on crossterm and default to Ru language on Ru profile 2026-09-04 22:02:45 +03:00
Kirill Minovsky
55a9788ccd fix(tui): enable use-libc on rustix to support tcgetattr ioctl on MIPS OpenWrt/Keenetic 2026-09-04 21:52:13 +03:00
Kirill Minovsky
963987a491 fix(main): show friendly message and exit when TUI is unavailable without args, and enable rustix_use_libc for cross 2026-09-04 21:50:14 +03:00
Kirill Minovsky
a58dff8f8b feat(win7): enable interactive TUI on Windows 7 with Win32 screen clear and clean borders 2026-09-04 20:57:48 +03:00
Kirill Minovsky
b0b98836c2 fix(win7): remove CP65001 bug, use safe write_all for stdio, and auto-run default suite without crash on legacy console 2026-09-04 19:28:07 +03:00
Kirill Minovsky
8e6e240e03 fix(main): add custom panic hook to log fatal errors and prevent instant console exit 2026-09-04 19:19:27 +03:00
Kirill Minovsky
a763a325be fix(render,main): auto-detect legacy Windows console, enable UTF-8 CP65001, and strip ANSI/use ASCII borders 2026-09-04 19:10:33 +03:00
Kirill Minovsky
6627c46462 feat(cli): make batch mode default on CLI args, accept commas in -t, and remove artificial sleep 2026-09-04 18:43:28 +03:00