- Go 84.2%
- Nix 15.8%
Replace the dummy TLS record injection mechanism with a minimum chunk count approach. Instead of prepending fake TLS records before the ClientHello, the new approach ensures the ClientHello itself is split into at least --refrag chunks, which is more effective and idiomatic. - Default --refrag changed from 1 to 0 (disabled) - Removed buildDummyRecords() and the associated dummy record injection - Added splitRange() helper function - Split() now enforces a minimum chunk count via re-splitting when needed |
||
|---|---|---|
| cmd/ihtc | ||
| docs/superpowers | ||
| internal | ||
| modules | ||
| .gitignore | ||
| flake.lock | ||
| flake.nix | ||
| go.mod | ||
| LICENSE | ||
| README.md | ||
ihtc
Local HTTP forward proxy for bypassing SNI-based DPI censorship. Splits the TLS ClientHello across multiple TCP packets to evade deep packet inspection.
Quick Start
go build -o ihtc ./cmd/ihtc/
./ihtc --auto-proxy
This binds 127.0.0.1:8080, serves a PAC file, and configures macOS auto proxy for all network services. Browsers route traffic through ihtc automatically.
On Ctrl+C, the proxy is disabled and browsers fall back to direct connections.
Usage
ihtc [flags]
--listen string Address to bind (default "127.0.0.1:8080")
--min-chunk int Minimum bytes per fragment (default 3)
--max-chunk int Maximum bytes per fragment (default 8)
--delay-us int Max microsecond delay between fragments (default 500)
--refrag int Dummy TLS records before ClientHello (default 1, disabled)
--regex string Only fragment hosts matching this regex
--verbose Enable debug logging
--auto-proxy Set macOS auto proxy configuration
Without --auto-proxy, point your browser/app at 127.0.0.1:8080 as an HTTP proxy manually.
How It Works
When a browser opens an HTTPS connection through the proxy:
- The browser sends a TLS ClientHello containing the target hostname (SNI)
- ihtc reads the complete ClientHello, then writes it in small random chunks (2-8 bytes by default) with microsecond delays between each
- TCP sends each chunk as its own segment — simple DPIs see only fragments, not the hostname
- After the handshake, ihtc relays data transparently in both directions
Limitations
- TCP only (no QUIC/HTTP3)
- Not effective against DPIs that perform TCP stream reassembly
- For reassembling DPIs, see re-fragmentation
- No authentication (local-only, trusted environment)
Advanced
Fragmenting only blocked domains
Use --regex to limit fragmentation to specific hosts. All other traffic passes through unfragmented:
./ihtc --auto-proxy --regex 'discord\.com|discord\.gg|twitter\.com'
Re-fragmentation
For DPIs that reassemble TCP streams, enable re-fragmentation with --refrag N:
./ihtc --refrag 3
This inserts N-1 deliberately invalid TLS records before the real ClientHello, each in its own TCP segment. A reassembling DPI must inspect multiple nearly-identical records and choose the correct one — while the TLS server ignores the invalid records and processes only the final valid one.
Set N based on DPI aggressiveness (2-5 is typical). Higher values add latency with diminishing returns.
License
MIT — see LICENSE.