Files
Xray-core/proxy/tun/wfp_leak.go
T
patternihaandClaude Opus 5.5 f2f292c90c TUN inbound: Support "tcp/port" and "udp/port" in autoSystemWfpBlockLeak on Windows; "dns" covers DoT and DoQ as well
"dns" in autoSystemWfpBlockLeak only blocked port 53 outside the TUN, plus
the DNS Client service on Windows 11 and Server 2022 and later, so other
programs' DNS over TLS (TCP 853) and DNS over QUIC (UDP 853) could still
leave outside the TUN.

autoSystemWfpBlockLeak now takes:

- "tcp/<port>", "udp/<port>": connections to that port may only go through
  the TUN (or come from Xray itself).
- "windnsclient": Windows' DNS Client service may only connect through the
  TUN, except for mDNS and LLMNR. As before, this only applies from Windows
  11 and Server 2022 on, where the service may use DoH or DoT to any port;
  earlier versions only query port 53.
- "dns": stands for "udp/53", "tcp/53", "windnsclient", "tcp/853" and
  "udp/853".
- "misconfigtun": unchanged.

Values are case-insensitive, deduplicated, and anything else, such as
"tcp/0" or "icmp/1", is rejected. Keeping the system's DNS inside the TUN,
with "udp/53", "tcp/53" or "windnsclient", needs dns to be set, as "dns"
did; ports like 853 alone do not. Each port gets its own permit-through-TUN
and block filters with a protocol condition, where port 53 used to have one
for both protocols, and the log lists what is blocked, e.g. "[tun] blocked
outside the TUN: udp/53, tcp/53, tcp/853, udp/853, windnsclient".

Tested on Windows 11 with a Wintun adapter as the outside interface: with
"tcp/853", "udp/853", "tcp/7215" and "udp/7216", those are blocked outside
the TUN over IPv4 and IPv6, while tcp/7216, udp/7215 and 443 are not, port
853 works through the TUN, Xray's own connections to port 853 still leave
outside it, and nothing is blocked after Xray stops.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 14:52:11 +03:30

80 lines
2.2 KiB
Go

package tun
import (
"slices"
"strconv"
"strings"
"github.com/xtls/xray-core/common/errors"
)
// dnsLeaks is what "dns" in autoSystemWfpBlockLeak stands for: DNS over UDP and
// TCP, Windows' DNS Client service, which may also use other protocols and
// ports, DNS over TLS, and DNS over QUIC.
var dnsLeaks = []string{"udp/53", "tcp/53", "windnsclient", "tcp/853", "udp/853"}
// leakPort is a "tcp/<port>" or "udp/<port>" value of autoSystemWfpBlockLeak:
// connections to that port may only go through the TUN.
type leakPort struct {
tcp bool
port uint16
}
func (p leakPort) String() string {
if p.tcp {
return "tcp/" + strconv.Itoa(int(p.port))
}
return "udp/" + strconv.Itoa(int(p.port))
}
// parseLeakPort parses a lower case "tcp/<port>" or "udp/<port>" value.
func parseLeakPort(value string) (leakPort, bool) {
protocol, port, ok := strings.Cut(value, "/")
if !ok || protocol != "tcp" && protocol != "udp" {
return leakPort{}, false
}
n, err := strconv.ParseUint(port, 10, 16)
if err != nil || n == 0 {
return leakPort{}, false
}
return leakPort{tcp: protocol == "tcp", port: uint16(n)}, true
}
// ExpandWfpBlockLeak checks the values of autoSystemWfpBlockLeak and returns
// them in lower case, in order and without duplicates, with "dns" replaced by
// what it stands for.
func ExpandWfpBlockLeak(values []string) ([]string, error) {
var leaks []string
add := func(leak string) {
if !slices.Contains(leaks, leak) {
leaks = append(leaks, leak)
}
}
for _, value := range values {
switch value = strings.ToLower(value); value {
case "dns":
for _, leak := range dnsLeaks {
add(leak)
}
case "windnsclient", "misconfigtun":
add(value)
default:
port, ok := parseLeakPort(value)
if !ok {
return nil, errors.New("unknown autoSystemWfpBlockLeak value: ", value)
}
add(port.String())
}
}
return leaks, nil
}
// BlocksSystemDNS tells whether expanded autoSystemWfpBlockLeak values keep the
// system's DNS (port 53, or Windows' DNS Client service) inside the TUN, which
// then needs DNS servers of its own.
func BlocksSystemDNS(leaks []string) bool {
return slices.ContainsFunc(leaks, func(leak string) bool {
return leak == "udp/53" || leak == "tcp/53" || leak == "windnsclient"
})
}