rkn-block-checker/README.md
2026-04-29 22:31:04 +03:00

171 lines
6.3 KiB
Markdown

# RKN Block Checker
[![CI](https://github.com/MayersScott/rkn-block-checker/actions/workflows/ci.yml/badge.svg)](https://github.com/MayersScott/rkn-block-checker/actions/workflows/ci.yml)
[![Python](https://img.shields.io/badge/python-3.10%2B-blue)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
A small CLI that figures out whether the connection you're sitting on is in an
RKN/TSPU-blocked zone — and, more usefully, **what kind** of block it is
(DNS poisoning, TCP reset, TLS DPI on SNI, or an ISP stub page).
The point isn't "site X doesn't open." Browsers already tell you that. The
point is to look at each layer of the stack independently and report *where*
it broke. That tells you a lot more about your situation than a generic
"this site can't be reached" page.
## Example output
```text
======================================================================
RKN Block Checker
======================================================================
IP: 95.165.xxx.xxx
ISP: AS12389 Rostelecom
Location: Moscow, Moscow, RU
----------------------------------------------------------------------
Whitelist (should always work)
name verdict TCP TLS PLT status
------------------------------------------------------------
gosuslugi ✓ OK 18ms 42ms 380ms 200
yandex ✓ OK 8ms 25ms 95ms 200
sberbank ✓ OK 12ms 38ms 250ms 200
vk ✓ OK 9ms 28ms 180ms 200
...
Blacklist (RKN-restricted)
name verdict TCP TLS PLT status
------------------------------------------------------------
instagram ✗ TLS BLOCK 22ms — — —
└ TLS reset — DPI cutting on SNI (typical RKN/TSPU)
twitter/x ✗ TLS BLOCK 24ms — — —
└ TLS timeout — silent drop after ClientHello
rutracker ✗ HTTP STUB 18ms 45ms 120ms 200
└ response body matches an ISP stub-page marker
protonvpn ✗ DNS BLOCK — — — —
└ system DNS doesn't resolve, DoH does — DNS poisoning
...
======================================================================
Summary
----------------------------------------------------------------------
Whitelist: 21/21 working
Blacklist: 3/15 open, 12/15 blocked
→ You ARE in an RKN-blocked zone.
Block types in the blacklist:
✗ TLS BLOCK: 8
✗ DNS BLOCK: 2
✗ HTTP STUB: 2
======================================================================
```
## Install
Python 3.10+.
```bash
git clone https://github.com/MayersScott/rkn-block-checker.git
cd rkn-block-checker
pip install -r requirements.txt
python -m rkn_checker
```
Or as a package:
```bash
pip install -e .
rkn-check
```
## Usage
```text
rkn-check [-h] [--json] [--white] [--black] [--timeout TIMEOUT]
[--workers WORKERS] [-v]
```
| flag | what it does |
|------|--------------|
| `--json` | machine-readable JSON instead of the colored report |
| `--white` | only the control (whitelist) targets |
| `--black` | only the blacklist targets |
| `--timeout` | per-probe timeout in seconds (default 5.0) |
| `--workers` | thread pool size for parallel checks (default 10) |
| `-v` / `-vv` | logging at INFO / DEBUG |
JSON output pipes nicely into `jq`:
```bash
rkn-check --json | jq '.blacklist[] | select(.verdict != "OK") | .name'
```
## How it works
For each target the tool walks DNS → TCP → TLS → HTTP and stops at the first
thing that fails. Whichever layer broke becomes the verdict.
| layer | probe | what a failure means |
|------:|-------|----------------------|
| DNS | system resolver vs Cloudflare DoH | if only the system fails, the ISP is poisoning DNS — the cheapest, oldest form of blocking |
| TCP | plain TCP handshake on :443 | a `RST` is IP-level blackholing. Rare — most ISPs don't bother |
| TLS | TLS handshake with SNI = target host | reset/timeout *here* (with TCP working fine) is the classic TSPU/DPI signature: the middlebox sees the SNI and tears the connection down |
| HTTP | `GET` after handshake completes | 451, or an ISP stub page returning 200 with a "blocked by RKN" body |
Two probes are worth calling out:
**System DNS vs DoH.** The cheapest way to "block" a site is to make the
ISP's DNS lie. Every host is resolved twice — once via `socket` (which uses
whatever resolver the OS is configured for, usually the ISP's) and once via
Cloudflare's DoH endpoint, which the ISP can't intercept. Disagreement is
the smoking gun.
**TLS handshake with SNI.** Modern TSPU equipment doesn't drop the TCP
connection — it lets you connect, reads the SNI extension out of the
ClientHello, and *then* sends a RST or simply stops responding. So we have
to actually start the TLS handshake to see this. A `TLS_BLOCK` after a clean
`TCP_OK` is the unambiguous fingerprint of DPI-based blocking.
## Layout
```text
rkn_checker/
__main__.py # python -m rkn_checker
cli.py # argparse + entry point
core.py # orchestrates DNS -> TCP -> TLS -> HTTP
dns.py # system resolver + Cloudflare DoH
network.py # raw TCP and TLS probes
http.py # HTTP GET + stub-page detection
output.py # colored CLI report
targets.py # whitelist, blacklist, stub markers
models.py # CheckResult, Verdict
tests/ # pytest, all network calls mocked
```
## Tests
```bash
pip install -r requirements-dev.txt
pytest
```
No network calls in the test suite — every probe is mocked, so it runs the
same in CI, on a plane, or behind a corporate proxy.
## Caveats
- IPv4 only. Some Russian ISPs treat IPv6 differently (often less filtered)
but the v4 path is what users actually experience in practice.
- The target lists are hard-coded (~20 sites per category). That's enough
for a verdict but won't catch a block that affects only one specific
resource. To extend — `rkn_checker/targets.py`.
- One-shot snapshot, no retries, no longitudinal tracking. If you want to
monitor a connection over time, run `rkn-check --json` from cron.
- Stub markers are mostly Russian-language phrases; false positives on
unrelated sites that happen to contain the same words are theoretically
possible but I haven't seen one yet.
## License
MIT.