SNI Scanner is a native Python application for Windows users who want a local, structured way to discover TCP-reachable domain/IP/port candidates and compare those candidates with sanitized proxy configuration rows.
The fork no longer requires WSL, Bash, dig, nc/netcat, or curl for normal Windows usage. A PyInstaller build can create a portable unsigned Windows folder that runs without an installed Python interpreter.
SNI Scanner has two parts:
- Discovery Scan: reads
targets.txt, resolves domains with the Python standard library, checks TCP connectivity for each target/resolved IP/port combination, and writes structured JSONL and CSV files. - Config Compatibility Check: reads one proxy/config line per row from
configs.txt, parses only safe non-secret fields, and compares those sanitized rows with successful discovery candidates.
The default discovery ports are:
443,2053,2083,2087,2096,8443
This tool performs local TCP connect checks and local config-field matching only. It does not perform an end-to-end proxy session, does not validate account credentials, does not verify provider routing, and does not prove a remote network path will work for every application.
TCP reachable or config-compatible is not proof of full external routing.
Build the portable package on Windows:
python scripts\build_windows.pyThe build creates:
dist/SNI-Scanner-Windows/
SNI-Scanner.exe
targets.txt
configs.example.txt
README_WINDOWS.md
dist/SNI-Scanner-Windows.zip
Run the GUI:
.\dist\SNI-Scanner-Windows\SNI-Scanner.exe --guiRun the CLI:
.\dist\SNI-Scanner-Windows\SNI-Scanner.exe --scan --targets targets.txtUse one target per line. Empty lines and comments beginning with # are ignored.
example.com
203.0.113.10
IPv4 addresses are accepted directly. Domain names are resolved to IPv4 addresses using Python standard-library DNS functions.
Private, loopback, internal, reserved, link-local, multicast, and unspecified addresses are filtered by default. Use --include-private for controlled local tests.
Use one config per non-empty line. Supported safe parsers include:
vless://vmess://trojan://ss://- plain
host:port
Secrets are not written to output files or GUI tables. The parser intentionally omits UUIDs, passwords, private keys, raw share links, and subscription URLs.
Start the GUI with:
SNI-Scanner.exe --guiThe GUI includes:
- Discovery Scanner: edit or browse for
targets.txt, set ports, timeout, retries, worker count, and private/internal filtering, then start or stop a scan. - Config Compatibility Checker: edit or browse for
configs.txt, save/reload config text, check saved discovery results, or run discovery and then check configs. - Results tables: separate discovery and compatibility tables with vertical and horizontal scrollbars plus a visible log section.
Discovery results update live while scanning. Stopping a scan preserves partial JSONL/CSV output.
python main.py --version
python main.py --scan --targets targets.txt
python main.py --scan --targets targets.txt --ports 443,8443 --timeout 3 --retries 2 --workers 100
python main.py --check-configs --configs configs.txt --jsonl results.jsonl
python main.py --scan --check-configs --targets targets.txt --configs configs.txt
python main.py --guiImportant CLI options:
| Option | Default | Purpose |
|---|---|---|
--scan |
off | Run discovery scan. |
--check-configs |
off | Compare configs with discovery results. |
--targets |
targets.txt |
Discovery input file. |
--configs |
configs.txt |
Config input file. |
--ports |
443,2053,2083,2087,2096,8443 |
Comma-separated TCP ports. |
--timeout |
3 |
TCP connect timeout in seconds. |
--retries |
2 |
Retry count after the first attempt. |
--workers |
100 |
Maximum concurrent TCP checks. |
--include-private |
off | Include private/internal IPs for local tests. |
--jsonl |
results.jsonl |
Discovery JSONL output. |
--csv |
results.csv |
Discovery CSV output. |
--compat-jsonl |
compatibility_results.jsonl |
Compatibility JSONL output. |
--compat-csv |
compatibility_results.csv |
Compatibility CSV output. |
Ctrl+C requests graceful stop and keeps partial results.
Discovery outputs:
results.jsonlresults.csv
Discovery fields:
timestamp,target,resolved_ip,port,status,tcp_ok,rtt_ms,error,attempt_count,source,filtered_reason
Compatibility outputs:
compatibility_results.jsonlcompatibility_results.csv
Compatibility fields:
config_row,config_type,profile_name_or_tag,candidate_target,candidate_ip,candidate_port,candidate_sni,status,reason,safe_config_summary,discovery_status,rtt_ms
Discovery statuses:
ok: TCP connect succeeded; the candidate was locally reachable.failed: TCP connect did not succeed after the configured attempts.resolve_error: the target domain could not be resolved to IPv4.filtered: the resolved IP was private/internal/reserved/link-local/loopback or otherwise filtered.stopped: scanning stopped before the candidate completed.
Compatibility statuses:
compatible: sanitized config host/SNI/host-header/port fields match a reachable discovery candidate.partial: the port matches, but the host/SNI relationship is unknown.incompatible: no supported relationship matched.parse_error: the config row could not be safely parsed.
Again: TCP reachable or config-compatible is not proof of full external routing.
Run local tests:
python -m unittest discover -s tests -v
python -m py_compile main.py gui_app.py scanner_core.py config_parser.py compatibility_checker.py scripts/build_windows.py scripts/smoke_test_windows_package.pyThe GitHub Actions workflow runs unit tests and Python compilation checks on every push and pull request. On Windows it also builds the portable package, runs the local smoke test, and uploads the unsigned artifact.