You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(proxy): extract SNI before dial for SNI-deferred connections (#14)
Restructure handleConnect so that SNI-deferred connections extract the
TLS ClientHello SNI BEFORE dialing through the MITM proxy. Previously,
dial was called first with the raw IP as the CONNECT target, causing
goproxy to use the IP for the upstream TLS ServerName. The real server's
cert has DNS SANs (e.g. *.telegram.org), not IP SANs, so TLS verification
failed with "cannot validate certificate for <IP>".
New flow for SNI-deferred connections:
1. Send SOCKS5 CONNECT success (so client starts TLS handshake)
2. Peek ClientHello to extract SNI hostname
3. Update context FQDN with recovered hostname
4. Evaluate policy with hostname
5. Dial through MITM with hostname (not IP)
Also: refactor relay logic into reusable relayData method, extract
sniSaveRule helper to reduce duplication.
Also: revised ECH DNS hostname recovery plan from review feedback.
# ECH-aware hostname recovery via HTTPS/SVCB DNS records
2
+
3
+
## Overview
4
+
5
+
Add HTTPS/SVCB DNS record parsing to the DNS interceptor. When a DNS response contains HTTPS/SVCB records with `ipv4hint`/`ipv6hint` SvcParams, extract the IP addresses and store hostname -> IP mappings in a separate SVCB cache. This cache is always populated but only used as a hostname source when SNI disagrees with it (ECH connections where the real SNI is encrypted and the outer SNI is a dummy).
6
+
7
+
The hostname recovery priority becomes:
8
+
1. FQDN from SOCKS5 CONNECT (if client sends hostname)
9
+
2. SNI from TLS ClientHello (happy path for most TLS)
10
+
3. SVCB DNS hint (when SNI disagrees with SVCB cache for the same IP, indicating ECH)
11
+
4. A/AAAA DNS reverse cache (existing, for non-TLS)
12
+
5. Raw IP (last resort)
13
+
14
+
## Context
15
+
16
+
- DNS interceptor: `internal/proxy/dns.go` (HandleQuery, forwards queries, gates PopulateFromResponse on A/AAAA types)
17
+
- DNS reverse cache: `internal/proxy/dns_reverse.go` (ReverseDNSCache, PopulateFromResponse, parseDNSName)
- DNS constants: `internal/proxy/dns_reverse.go` (dnsTypeA, dnsTypeAAAA, dnsHeaderLen)
20
+
21
+
## Development Approach
22
+
23
+
-**Testing approach**: Regular (code first, then tests)
24
+
- CRITICAL: every task MUST include new/updated tests
25
+
- CRITICAL: all tests must pass before starting next task
26
+
27
+
## Testing Strategy
28
+
29
+
- Unit tests for SVCB record parsing: `internal/proxy/dns_test.go`
30
+
- Unit tests for SVCB cache lookup: `internal/proxy/dns_reverse_test.go` (new file)
31
+
- Integration tests for ECH fallback flow: `internal/proxy/server_test.go`
32
+
33
+
## Solution Overview
34
+
35
+
1. Add a separate `svcbEntries` map to `ReverseDNSCache` with `StoreSVCB`/`LookupSVCB` methods
36
+
2. Extend `PopulateFromResponse` to parse HTTPS (type 65) and SVCB (type 64) records, using `parseDNSName` for the target name field (DNS name compression applies per RFC 9460)
37
+
3. Modify `HandleQuery` in `dns.go` to call `PopulateFromResponse` for HTTPS/SVCB query types (currently gated to A/AAAA only)
38
+
4. In `sniPolicyCheck`: after extracting SNI, compare it against the SVCB-cached hostname for the destination IP. If they differ, prefer the SVCB hostname (the SNI is likely a dummy ECH outer SNI)
39
+
40
+
**ECH detection approach**: no explicit ECH detection. Simply compare extracted SNI against SVCB-cached hostname for the same IP. If the SVCB cache says `149.154.167.220 -> example.com` but SNI says `cloudflare-ech.com`, prefer `example.com`. This works because SVCB hints are authoritative for the queried domain. No `HasECH` method needed.
41
+
42
+
## Progress Tracking
43
+
44
+
- Mark completed items with `[x]` immediately when done
45
+
- Add newly discovered tasks with + prefix
46
+
47
+
## Implementation Steps
48
+
49
+
### Task 1: Add SVCB cache and parse HTTPS/SVCB records
0 commit comments