4 Commits
Author SHA1 Message Date
zhangthree78 734792cfc2 fix: complete per-query log gating, clean residual DNS rules on stop, dedupe marked client
Follow-up review of 525654e found:

1. Log gating was incomplete: only the direct/route path was gated;
   cache-hit/cache-miss, proxy and dispatcher per-query logs were still
   unconditional log.Printf (the hottest paths under load). Gate them
   behind V2RAYA_DNS_DEBUG=1 too; error logs stay visible.

2. Stopping the transparent proxy left the direct nat OUTPUT/PREROUTING
   REDIRECT --to-port 52353 rules and the fwmark 0x80 exemption rules in
   place (Tproxy/Redirect GetCleanCommands only clean their own chains).
   Once the DNS module exits, local DNS queries hit a dead port and DNS
   fails. Add cleanDnsRedirectRules() and run it on the normal stop path.

3. module.go bootstrap client inlined its own markFd + marked dialer;
   reuse newMarkedDnsClient (keeping the 3s fail-fast timeout).

4. dnsLogf/dnsDebugEnabled were duplicated in socket_unix.go /
   socket_windows.go; move them to a common debug.go.

5. No unit tests for newMarkedDnsClient; add tests asserting
   Net/UDPSize/Timeout and a non-nil Dialer.Control.

Verified: go build ./dns/... ./kernel/v2ray/... ; go test ./dns/...
2026-08-17 00:49:48 +08:00
zhangthree78 525654ec9b fix: prevent DNS redirect loop causing unbounded memory growth
Fixes defects in the v2.4.8+ transparent-proxy DNS path that cause the
core to exhaust memory within seconds on low-RAM hosts (see discussion
#1915: '2.4.9 内存占用过高' / '有严重的内存泄漏,别用了。本地DNS完全废掉了'):

1. core/dns: upstream queries from NewUpstreamManager /
   GetOrCreateUpstream / getClientForProtocol used bare &dns.Client{}
   with no Dialer configured, so their sockets never carried
   SO_MARK=0x80. The unmarked queries were hijacked by the module's own
   transparent-proxy DNS redirect rules and looped back into :52353 —
   an infinite self-query loop and memory snowball.

2. service/kernel/v2ray/transparent.go: the unconditional DNS hijack
   rules (REDIRECT --to-port 52353) had no fwmark-0x80 exemption, and
   being inserted with -I they sat at the head of nat OUTPUT/PREROUTING,
   so marked traffic was looped back as well.

3. core/dns: per-query info logging (query/route/upstream) produced
   thousands of lines/second under load, amplifying CPU/IO/memory
   pressure.

Fix:
- Route every upstream client through newMarkedDnsClient, whose
  net.Dialer sets SO_MARK=0x80 on all sockets. miekg/dns dials UDP
  through net.Dialer too, so Dialer.Control covers UDP sockets — no
  manual socket path is needed. UDPSize=4096 keeps a sane receive
  buffer for queries without an EDNS0 OPT record.
- Append the DNS redirects with -A and insert the mark exemption with
  -I so the exemption is matched first; also delete the exemption
  rules in the residual-rule cleanup so they don't accumulate across
  restarts.
- redirect mode: insert the DNS_REDIRECT jumps after the TP_PRE/TP_OUT
  jumps so they actually take precedence (-I inserts at chain head),
  keeping local and LAN TCP DNS flowing to the DNS module (:52353)
  instead of the proxy inbound (:52345).
- Gate per-query info logs behind V2RAYA_DNS_DEBUG=1; error logs stay
  always visible.

Verified: unpatched v2.4.11 core RSS 230MB->1GB in ~6s then OOM/hang;
patched build stable at ~25MB idle / ~243MB loaded, DNS and SOCKS proxy
working.
2026-08-16 23:59:43 +08:00
Markson Joe Hon 9fcbd6a5db fix(core/dns): fix DNS split routing, bootstrap loop, and response corruption
## Issue 1: DNS rule matching logic (MatchAll + OrMatcher)

BuildMatchers flattened all geosite-expanded domains into a single list,
and the old MatchAny used AND logic (all matchers must match). This
made geosite:cn rules impossible to hit (e.g. qq.com and baidu.com
can never match simultaneously).

Fix: introduce OrMatcher to group matchers from the same rule field
with OR semantics, while different field groups use AND. The thousands
of domains from geosite:cn expansion are wrapped in a single OrMatcher
so matching any one domain satisfies the rule.

## Issue 2: Bootstrap self-reference loop (SO_MARK + public DNS fallback)

Bootstrap resolution runs before the DNS listener starts, but iptables
already hijacks :53 traffic:
- bootstrap query to system DNS(:53) → iptables redirects to 127.2.0.17:53
- DNS module not yet listening → connection refused → bootstrap fails
- Domain upstreams (e.g. dns.google) remain unresolved → relies on proxy
- Proxy DNS may re-enter DNS module → loop or REFUSED

Fix:
1. Add SO_MARK=0x80 to bootstrap DNS client to bypass iptables redirect
2. collectBootstrapDns() with 3-tier priority (config > system > hardcoded)
3. Hardcoded fallback includes Alibaba/Tencent/Google/Cloudflare: 13 servers
4. isOwnAddress() filters self-referencing addresses (127.0.0.1:53 etc.)

## Issue 3: handlePacket mutates cached response in-place

m = resp.RawMsg; m.SetReply(msg) directly modifies the *dns.Msg shared
with the cache, corrupting subsequent cache hits. SetReply also forces
Rcode=RcodeSuccess, overwriting NXDOMAIN etc. from upstream.

Fix: Pack/Unpack deep copy before modification; preserve original Rcode.

## Issue 4: Double OPT record causes FORMERR

Three exchange functions called AddECSSubnet BEFORE SetEdns0.
miekg/dns v1.1.72's SetEdns0 always appends a new OPT record without
checking for existing ones, producing a malformed query with two OPT
records.

Fix: call SetEdns0 before AddECSSubnet; use UDP size 4096 in
AddECSSubnet instead of 512 to preserve the size set by SetEdns0.

## Issue 5: localhost upstream skipped in template (template_dns.go)

localhost upstream was continue-skipped during config generation,
making the geosite:private rule reference a non-existent upstream.

Fix: map localhost to 127.0.0.1, keep it as a valid upstream.

## Incidental fixes

- handler.go: remove duplicate :53 in log format (dns.google:53:53)
- response.go: use 4096 instead of dns.DefaultMsgSize(512) for ECS
2026-07-22 14:01:39 +08:00
Markson Joe Hon 47f1462baf feat: init a new for v2raya core and fix a firewall mistake 2026-07-02 13:10:12 +08:00