- plan.md: architecture flow diagram - README.md: simple forwarder overview diagram - story.md: DNS censorship and SOCKS5 proxy diagrams - intra-comparison.md: all ASCII diagrams converted to Mermaid
5.9 KiB
The Story — Why This Project Exists
The Problem
In countries with internet censorship (Iran, China, Russia, and others), DNS is one of the first things controlled. ISPs intercept plain DNS queries (UDP/TCP port 53) and:
- Return fake IP addresses for blocked domains
- Log every domain you look up
- Inject ads or redirect to government portals
sequenceDiagram
participant User
participant ISP as ISP DNS
participant Real as Real Server
User->>ISP: youtube.com?
ISP-->>User: 1.2.3.4 (FAKE IP)
User->>Real: HTTPS to 1.2.3.4
Real-->>User: ❌ Connection failed / redirected
DNS-over-HTTPS (DoH) and DNS-over-TLS (DoT) solve this by encrypting DNS queries, making them invisible to the ISP's DNS layer.
What Already Exists
This is not a new problem. Mature tools already solve local DNS → DoH forwarding:
| Tool | What it does | Maintained by |
|---|---|---|
cloudflared |
Local DNS → DoH proxy (cloudflared proxy-dns) |
Cloudflare |
dnscrypt-proxy |
DoH + DoT + anonymized relays, blocklists | Frank Denis |
stubby |
Local DNS → DoT proxy | getdns team |
systemd-resolved |
Built into systemd, supports DoT | systemd team |
AdGuard Home |
DNS proxy + DoH/DoT + web UI + blocklists | AdGuard |
For simple "local DNS → DoH" forwarding, cloudflared proxy-dns does exactly
what this project does in one command:
cloudflared proxy-dns --port 5353 --upstream https://cloudflare-dns.com/dns-query
The Real Obstacle
The harder problem — and the one most relevant to censored regions — is that upstream DoH/DoT endpoints themselves get blocked:
| Technique | What gets blocked | How |
|---|---|---|
| SNI inspection | cloudflare-dns.com, dns.google |
TLS ClientHello reveals the domain |
| IP blocklist | Known resolver IPs (1.1.1.1, 8.8.8.8) | Direct IP blocking |
| TLS fingerprinting | DoH/DoT traffic patterns | JA3/JA4 fingerprint analysis |
| Deep packet inspection | HTTPS traffic to known DNS resolvers | Pattern matching on domain + path |
A local forwarder that sends POST https://cloudflare-dns.com/dns-query will
work in most of the world, but will likely fail in Iran because the
connection to Cloudflare itself is blocked.
sequenceDiagram
participant Client
participant FW as Firewall / DPI
participant CF as Cloudflare DoH
Client->>FW: POST https://cloudflare-dns.com/dns-query
FW->>FW: Inspect SNI: cloudflare-dns.com
FW-->>Client: ❌ BLOCKED (connection reset)
What Actually Works in Censored Environments
Tools that solve the upstream blocking problem:
-
Warp / WireGuard — tunnel all traffic (including DNS) through an encrypted VPN. The ISP sees WireGuard traffic but can't inspect it.
-
GoodbyeDPI / zapret — packet manipulation to evade DPI without a VPN. Modifies TCP flags, splits packets, fakes SNI.
-
dnscrypt-proxy with anonymized relays — DNS queries go through intermediate relays. The resolver never sees your IP, and the ISP never sees the resolver's IP.
-
Tor — route DNS through Tor exit nodes. Slow but highly resistant.
-
Encrypted Client Hello (ECH) — hides the SNI field in TLS handshakes. Still emerging; not widely supported.
So Is This Project Necessary?
Honestly: no, not for its current functionality.
If the goal is "use encrypted DNS from Linux in a censored region," install
cloudflared or dnscrypt-proxy + a Warp/WireGuard tunnel. These are
battle-tested, maintained by teams, and packaged in every distro.
Where This Project Becomes Useful
This project becomes genuinely valuable if it evolves beyond a basic forwarder. Directions that matter:
1. Upstream obfuscation (the real differentiator)
Route upstream DoH traffic through a proxy (SOCKS5, HTTP CONNECT, or WireGuard tunnel) instead of direct HTTPS. This bypasses SNI and IP blocking:
flowchart LR
client["DNS Client"] -- "UDP/TCP" --> forwarder["doh-forwarder"]
forwarder -- "SOCKS5/CONNECT" --> proxy["Proxy Server"]
proxy -- "HTTPS" --> doh["DoH Resolver"]
doh -- "DNS response" --> proxy
proxy -- "DNS response" --> forwarder
forwarder -- "DNS response" --> client
No existing lightweight Rust tool does this well. dnscrypt-proxy has
anonymized relays but no SOCKS5 support for DoH.
2. Pluggable transport layer
Support multiple upstream transports:
- Direct DoH (current — works in free internet)
- DoH over SOCKS5 (works behind blocks)
- DoH over WireGuard tunnel (works behind aggressive DPI)
- DNS-over-TLS (for resolvers that support it)
The user picks the transport based on their environment.
3. Smart upstream selection
Don't just round-robin — probe upstreams for latency and availability. Auto-switch when a resolver becomes unreachable. This matters when resolvers get blocked intermittently.
4. Built-in blocklist support
Ad-blocking and malware-blocking at the DNS level. dnscrypt-proxy and
AdGuard Home do this, but a lightweight Rust binary with this built-in
is appealing for embedded/resource-constrained systems.
5. Learning
Even if you never deploy it, building this teaches:
- DNS wire format (RFC 1035)
- DoH protocol (RFC 8484)
- Rust async (Tokio, Axum)
- Network programming
- Error handling patterns
That alone justifies the project.
Summary
| Question | Answer |
|---|---|
| Does this solve local DNS → DoH? | Yes, but cloudflared already does this |
| Does this bypass censorship? | No — upstream DoH endpoints get blocked |
| Is the project pointless? | No — it's a foundation for something better |
| What makes it worth building? | SOCKS5/proxy upstream support, pluggable transports, learning |
The project is a starting point, not a finished solution. The first 80% (local DNS → DoH forwarding) is solved by existing tools. The last 20% (bypassing upstream blocking) is where this project can differentiate.