- Go 57.8%
- Nim 21.9%
- Nix 19.6%
- Makefile 0.7%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
Some checks failed
Nix build / build (ubuntu-latest, x86_64-linux) (push) Failing after 1m10s
Nix build / build (macos-13, x86_64-darwin) (push) Has been cancelled
Nix build / build (macos-latest, aarch64-darwin) (push) Has been cancelled
Nix build / build (ubuntu-24.04-arm, aarch64-linux) (push) Has been cancelled
|
||
| .github/workflows | ||
| bpf | ||
| cmd/ihtc | ||
| docs/superpowers | ||
| internal | ||
| modules | ||
| .gitignore | ||
| flake.lock | ||
| flake.nix | ||
| go.mod | ||
| go.sum | ||
| 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
--backend string Backend to use: proxy or tc (default "proxy")
--tc-interface str Network interface for the Linux TC backend
--tc-object string nlvm-generated TC eBPF object (default "bpf/tc_egress.o")
Without --auto-proxy, point your browser/app at 127.0.0.1:8080 as an HTTP proxy manually.
Linux TC eBPF backend
The optional tc backend is independent of the HTTP proxy. Build its TC
program with the sibling nlvm checkout, then attach it to a network interface:
make -C bpf NLVM=../../nlvm/nlvm/nlvm
sudo ./ihtc --backend tc --tc-interface eth0 --tc-object bpf/tc_egress.o
It requires Linux 6.6+ (TCX), CAP_NET_ADMIN, and CAP_BPF. The program
currently handles IPv4 TCP flows whose complete first ClientHello is in one
packet; unsupported traffic is passed unchanged. Use --backend proxy for the
userspace implementation.
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.