diff --git a/infra/conf/tun_test.go b/infra/conf/tun_test.go index 8c2042314..a9490fc5f 100644 --- a/infra/conf/tun_test.go +++ b/infra/conf/tun_test.go @@ -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) diff --git a/proxy/tun/README.md b/proxy/tun/README.md index 0ac786dd3..96aad7dae 100644 --- a/proxy/tun/README.md +++ b/proxy/tun/README.md @@ -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 `/` or `/-` 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 `/` or `/-` 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. diff --git a/proxy/tun/wfp_leak.go b/proxy/tun/wfp_leak.go index 2d2889ffe..7025adff4 100644 --- a/proxy/tun/wfp_leak.go +++ b/proxy/tun/wfp_leak.go @@ -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 "/" or "/-", where "/0" stands for all ports, -// as do "/1-65535" and no port at all; ICMP has no ports. +// two may end in "/" or "/-", 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) { diff --git a/proxy/tun/wfp_leak_test.go b/proxy/tun/wfp_leak_test.go index 15e11ad3d..bc1f8bcd9 100644 --- a/proxy/tun/wfp_leak_test.go +++ b/proxy/tun/wfp_leak_test.go @@ -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)