Skip to content

SVCB/HTTPS blanking and DNS-level blocking stop happening when WireGuard is on #736

Description

@kasnder

Summary

parse_dns_response does two things to a DNS reply: it blanks SVCB/HTTPS answers, and it synthesises an empty response for domains that is_domain_blocked rejects — dns.c:181. Neither happens while remote VPN (WireGuard) egress is on.

Root cause

parse_dns_response is reached from exactly two places, both on the userspace NAT path:

  • udp.c:134, on the reply of an established UDP session
  • tcp.c:608, on the reply of an established TCP session

A DNS query with WireGuard on never creates either session: handle_ip sends it to the tunnel and returns (ip.c:487, wg_dest is unconditionally true for is_dns). Replies come back through gotatun and are handled by the Rust inspector in wgbridge-rs/src/dns.rs, which is explicitly passive — its module doc says packets "are never modified or blocked here". It records answers for tracker attribution and forwards them untouched.

Consequences, in order of severity

SVCB/HTTPS blanking is lost outright. Nothing else suppresses those records, so ECH and HTTP/3 upgrade hints reach the app whenever the tunnel is on. This is the divergence that motivated unifying the two DNS parsers in the first place.

DNS-level domain blocking degrades rather than disappears. is_address_allowed still runs in handle_ip before the hijack, so a blocked domain's connections are still blocked by IP. What is lost is the NXDOMAIN-style answer, so an app sees a resolvable name whose connections then fail, instead of a clean resolution failure. Different failure shape, same end state.

Scope

Any WireGuard-enabled configuration. Present on master; not introduced by #730.

Direction

The two DNS parsers (jni/netguard/dns.c and wgbridge-rs/src/dns.rs) converge on ServiceSinkhole.dnsResolved but have drifted in what they do with a response. Either give the Rust inspector the same rewriting authority as dns.c — it already parses the answer section, so it is a matter of writing back — or retire dns.c in favour of a single shared implementation, which is the direction the routing policy has just taken (wgbridge-rs/src/policy.rs).

Root cause is shared with the two sibling issues linked below: handle_ip hands the packet to WireGuard before the DNS and session machinery runs, so everything that machinery provides disappears whenever the tunnel is on.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions