TUN inbound's autoSystemWfpBlockLeak: Drop "/0"; Ports have to be 1-65535

"tcp/0" and "udp/0" no longer stand for all ports, which "tcp" and "udp"
already say: no syntactic sugar. Port 0 is rejected like any port outside
1-65535. Ranges keep being checked strictly: both ends within 1-65535, the
left one not greater than the right one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
patterniha
2026-11-02 12:49:56 +03:30
co-authored by Claude Opus 5.5
parent 2933b0c8d5
commit 9ffa0f0552
4 changed files with 12 additions and 10 deletions
+2 -2
View File
@@ -42,7 +42,7 @@ func TestTunConfigAutoSystem(t *testing.T) {
Output: &tun.Config{Name: "xray0", Desc: "Wintun", MTU: 1500, AutoSystemRoutingTable: []string{"0.0.0.0/0"}, AutoOutboundsInterface: "auto", AutoSystemWfpBlockLeak: []string{"tcp/853", "udp/853"}},
},
{
Input: `{"name": "xray0", "dns": ["1.1.1.1"], "autoSystemRoutingTable": ["0.0.0.0/0"], "autoSystemWfpBlockLeak": ["All", "UDP/0", "tcp/8000-9000", "icmp"]}`,
Input: `{"name": "xray0", "dns": ["1.1.1.1"], "autoSystemRoutingTable": ["0.0.0.0/0"], "autoSystemWfpBlockLeak": ["All", "UDP", "tcp/8000-9000", "icmp"]}`,
Parser: loadJSON(creator),
Output: &tun.Config{Name: "xray0", Desc: "Wintun", MTU: 1500, DNS: []string{"1.1.1.1"}, AutoSystemRoutingTable: []string{"0.0.0.0/0"}, AutoOutboundsInterface: "auto", AutoSystemWfpBlockLeak: []string{"all", "udp", "tcp/8000-9000", "icmp"}},
},
@@ -87,7 +87,7 @@ func TestTunConfigAutoSystemNeeds(t *testing.T) {
}
func TestTunConfigAutoSystemWfpBlockLeakUnknown(t *testing.T) {
for _, value := range []string{"ip", "tcp/0-10", "icmp/1", "udp/20-10"} {
for _, value := range []string{"ip", "tcp/0", "tcp/0-10", "icmp/1", "udp/20-10"} {
config := new(TunConfig)
if err := json.Unmarshal([]byte(`{"name": "xray0", "autoSystemWfpBlockLeak": ["dns", "`+value+`"]}`), config); err != nil {
t.Fatal(err)
+1 -1
View File
@@ -226,7 +226,7 @@ With IPv6 disabled system-wide (the `DisabledComponents` registry value), the ad
When `dns` is set, those servers are applied to the adapter. Windows is kept from registering the TUN's addresses in DNS, and its DNS cache is flushed when the TUN starts and stops.
With `autoSystemWfpBlockLeak`, which needs `autoSystemRoutingTable` (the config is rejected otherwise), Xray also adds Windows Filtering Platform filters that keep traffic of every program but Xray itself from leaving outside the TUN, each kind chosen by a value in the list, e.g. `"autoSystemWfpBlockLeak": ["dns", "misconfigtun"]`:
- `"tcp"`, `"udp"`: connections of that protocol only go through the TUN, to all ports, or with `/<port>` or `/<low>-<high>` to some, e.g. `"tcp/853"` or `"udp/10-20"`. `"tcp/0"` stands for all ports, like `"tcp"`.
- `"tcp"`, `"udp"`: connections of that protocol only go through the TUN, to all ports, or with `/<port>` or `/<low>-<high>` to some, e.g. `"tcp/853"` or `"udp/10-20"`. Ports go from 1 to 65535, and the low end of a range is not above the high end.
- `"icmp"`: ICMP and ICMPv6, e.g. `ping`, only go through the TUN.
- `"windnsclient"`: Windows' DNS Client service cannot connect outside the TUN at all, except for name resolution on the local network (LLMNR, mDNS). On Windows 11 and Server 2022 and later, it may send name queries over HTTPS or TLS, to any port. Earlier versions only query port 53, so there it does nothing.
- `"dns"`: stands for `"udp/53"`, `"tcp/53"`, `"windnsclient"`, `"tcp/853"` (DNS over TLS) and `"udp/853"` (DNS over QUIC). Windows keeps sending name queries to the DNS servers of the other interfaces as well, out through those interfaces whatever the routes say, and other programs reach a resolver on the local network (e.g. `192.168.1.1` handed out by DHCP) through its more specific LAN route instead of the TUN.
+7 -6
View File
@@ -39,8 +39,9 @@ func (t leakTraffic) covers(protocol string, port uint16) bool {
}
// parseLeakTraffic parses a lower case "tcp", "udp" or "icmp" value. The former
// two may end in "/<port>" or "/<low>-<high>", where "/0" stands for all ports,
// as do "/1-65535" and no port at all; ICMP has no ports.
// two may end in "/<port>" or "/<low>-<high>", with ports from 1 to 65535 and
// low not above high, where "/1-65535" stands for all ports, as does no port
// at all; ICMP has no ports.
func parseLeakTraffic(value string) (leakTraffic, bool) {
protocol, ports, hasPorts := strings.Cut(value, "/")
switch protocol {
@@ -55,16 +56,16 @@ func parseLeakTraffic(value string) (leakTraffic, bool) {
}
lowText, highText, isRange := strings.Cut(ports, "-")
low, err := strconv.ParseUint(lowText, 10, 16)
if err != nil {
if err != nil || low == 0 {
return leakTraffic{}, false
}
high := low
if isRange {
if high, err = strconv.ParseUint(highText, 10, 16); err != nil || low == 0 || high < low {
if high, err = strconv.ParseUint(highText, 10, 16); err != nil || high < low {
return leakTraffic{}, false
}
}
if low == 0 || low == 1 && high == 65535 {
if low == 1 && high == 65535 {
return leakTraffic{protocol: protocol}, true
}
return leakTraffic{protocol: protocol, low: uint16(low), high: uint16(high)}, true
@@ -73,7 +74,7 @@ func parseLeakTraffic(value string) (leakTraffic, bool) {
// 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, "allexceptlan" followed by that, and ports written the
// same way, "tcp/0" as "tcp" say.
// same way, "tcp/1-65535" as "tcp" say.
func ExpandWfpBlockLeak(values []string) ([]string, error) {
var leaks []string
add := func(leak string) {
+2 -1
View File
@@ -20,7 +20,7 @@ func TestExpandWfpBlockLeak(t *testing.T) {
{[]string{"tcp/53"}, []string{"tcp/53"}, true},
{[]string{"windnsclient"}, []string{"windnsclient"}, true},
{[]string{"ALL"}, []string{"all"}, true},
{[]string{"tcp", "TCP/0", "udp/0", "UDP"}, []string{"tcp", "udp"}, true},
{[]string{"tcp", "TCP", "UDP"}, []string{"tcp", "udp"}, true},
{[]string{"tcp/1-65535", "udp/1-65535"}, []string{"tcp", "udp"}, true},
{[]string{"udp/10-20", "udp/53-53", "tcp/50-60"}, []string{"udp/10-20", "udp/53", "tcp/50-60"}, true},
{[]string{"tcp/54-60", "udp/1-52"}, []string{"tcp/54-60", "udp/1-52"}, false},
@@ -43,6 +43,7 @@ func TestExpandWfpBlockLeak(t *testing.T) {
for _, value := range []string{
"", "ip", "tcp/", "udp/65536", "tcp/-1", "tcp/+53", "tcp/53/1", "udp/abc", "icmp/0", "icmp/8", "tcp:53", " dns",
"tcp/0-10", "tcp/20-10", "tcp/5-", "tcp/-5", "tcp/1-2-3", "tcp/1-65536", "udp/ 53", "all/0",
"tcp/0", "udp/0", "tcp/00", "udp/0-0", "udp/65535-1",
} {
if _, err := ExpandWfpBlockLeak([]string{value}); err == nil {
t.Errorf("%q was accepted", value)