TUN inbound's autoSystemWfpBlockLeak: Add "allexceptlan"

"allexceptlan" is "all" but for the local network: traffic to and from
private (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, fc00::/7), link-local
(169.254.0.0/16, fe80::/10), multicast (224.0.0.0/4, ff00::/8) and broadcast
(255.255.255.255) addresses stays allowed, so that printers, the router's
settings page, or casting to a TV keep working while nothing else leaves
outside the TUN.

It includes "dns": a resolver on the local network, like the router's,
would forward the queries outside the TUN, so DNS stays blocked there. The
LAN permit sits below the other values, like the permit through the TUN, so
they still apply on the local network, e.g. "tcp/445" blocks Windows file
sharing with it. The DNS Client service keeps mDNS and LLMNR, to the local
network's addresses only. "all" overrides it.

The addresses are matched with FWP_V4_ADDR_MASK and FWP_V6_ADDR_MASK
conditions on the remote address, which match when any of them does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
patterniha
2026-11-02 11:50:18 +03:30
co-authored by Claude Opus 5.5
parent 468f745fc2
commit bb4cd4b6d3
8 changed files with 150 additions and 20 deletions
+1 -1
View File
@@ -52,7 +52,7 @@ func (v *TunConfig) Build() (proto.Message, error) {
return nil, errors.New("autoSystemWfpBlockLeak needs autoSystemRoutingTable to be set")
}
if tun.BlocksSystemDNS(leaks) && len(v.DNS) == 0 {
return nil, errors.New(`autoSystemWfpBlockLeak "all", "dns", "windnsclient", or TCP or UDP port 53, needs dns to be set`)
return nil, errors.New(`autoSystemWfpBlockLeak "all", "allexceptlan", "dns", "windnsclient", or TCP or UDP port 53, needs dns to be set`)
}
case "linux", "freebsd":
if v.AutoSystemDnsToGateway && len(v.Gateway) == 0 {
+7
View File
@@ -46,6 +46,11 @@ func TestTunConfigAutoSystem(t *testing.T) {
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"}},
},
{
Input: `{"name": "xray0", "dns": ["1.1.1.1"], "autoSystemRoutingTable": ["0.0.0.0/0"], "autoSystemWfpBlockLeak": ["AllExceptLAN"]}`,
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{"allexceptlan", "udp/53", "tcp/53", "windnsclient", "tcp/853", "udp/853"}},
},
})
}
@@ -67,6 +72,8 @@ func TestTunConfigAutoSystemNeeds(t *testing.T) {
{`{"name": "xray0", "autoSystemRoutingTable": ["0.0.0.0/0"], "autoSystemWfpBlockLeak": ["tcp/50-60"]}`, []string{"windows"}},
{`{"name": "xray0", "autoSystemRoutingTable": ["0.0.0.0/0"], "autoSystemWfpBlockLeak": ["tcp/54-60", "icmp"]}`, nil},
{`{"name": "xray0", "autoSystemWfpBlockLeak": ["all"]}`, []string{"windows"}},
{`{"name": "xray0", "autoSystemRoutingTable": ["0.0.0.0/0"], "autoSystemWfpBlockLeak": ["allexceptlan"]}`, []string{"windows"}},
{`{"name": "xray0", "dns": ["1.1.1.1"], "autoSystemRoutingTable": ["0.0.0.0/0"], "autoSystemWfpBlockLeak": ["allexceptlan"]}`, nil},
{`{"name": "xray0", "autoSystemDnsToGateway": true}`, []string{"linux", "freebsd"}},
} {
config := new(TunConfig)
+4 -3
View File
@@ -232,18 +232,19 @@ With `autoSystemWfpBlockLeak`, which needs `autoSystemRoutingTable` (the config
- `"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.
- `"misconfigtun"`: an IP version without routes in `autoSystemRoutingTable`, IPv4 or IPv6, is blocked entirely outside the TUN, in both directions, as it would bypass the TUN. An address of that version in `gateway` is not needed: without one, Windows gives the TUN link-local addresses itself, an IPv6 one at once and an IPv4 one from `169.254.0.0/16` after some seconds (until then, IPv4 routed to the TUN is unreachable), and what is routed to the TUN goes through it with those.
- `"all"`: everything outside the TUN, IPv4 and IPv6, in both directions, so nothing but Xray leaves except through the TUN. Windows' own route to the local network is more specific than the TUN's, so access to the local network breaks unless it is routed into the TUN as well, and so do incoming connections to other programs, name resolution on the local network, other VPNs, and virtual machines whose NAT runs as a program on the host.
- `"allexceptlan"`: like `"all"`, but traffic to and from the local network stays allowed, e.g. for printers, the router's settings page, or casting to a TV: private addresses (`10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`, `fc00::/7`), link-local ones (`169.254.0.0/16`, `fe80::/10`), multicast (`224.0.0.0/4`, `ff00::/8`) and broadcast (`255.255.255.255`). It includes `"dns"`, so name queries to a resolver on the local network, like the router's, stay blocked, and the other values apply there as well, e.g. `"tcp/445"` blocks Windows file sharing with it. Devices on the local network are only reached through such addresses, not through public ones like global IPv6 addresses. `"all"` overrides it.
Whatever is blocked, loopback, which cannot leave the system, and what Windows itself needs on the local link stay allowed: DHCP, DHCPv6, and IPv6 neighbor and multicast listener discovery.
Keeping the system's DNS inside the TUN, with `"all"`, `"dns"`, `"windnsclient"`, or anything covering TCP or UDP port 53, needs `dns` (the config is rejected otherwise). The `dns` servers then have to lie within `gateway` or `autoSystemRoutingTable` (a warning is logged otherwise), and DNS servers that should be reached directly belong in Xray's own `dns` settings.
Keeping the system's DNS inside the TUN, with `"all"`, `"allexceptlan"`, `"dns"`, `"windnsclient"`, or anything covering TCP or UDP port 53, needs `dns` (the config is rejected otherwise). The `dns` servers then have to lie within `gateway` or `autoSystemRoutingTable` (a warning is logged otherwise), and DNS servers that should be reached directly belong in Xray's own `dns` settings.
With the filters in place, Xray's own connections out also get past Windows Firewall's block rules (other firewalls may still block them), while connections to Xray's inbounds stay subject to them.
Names that Xray resolves through the system resolver, such as an outbound's server address given as a domain with the default `AsIs` domain strategy, would be looked up by Windows on Xray's behalf, and those queries would then go into the TUN too. While DNS is restricted this way and `autoOutboundsInterface` is in use (the default with `autoSystemRoutingTable`), Xray therefore resolves them itself, with its own queries to the DNS servers of the other interfaces. That bypasses Windows' DNS cache, and its name resolution on the local network (LLMNR, mDNS): a server address given as a domain is looked up again for every connection, and a DNS server that does not answer delays each lookup. Having Xray's own `dns` resolve it, through the outbound's `sockopt.domainStrategy`, avoids that. The `localhost` DNS server queries the same servers whenever `autoOutboundsInterface` is in use. Both skip the TUN's own DNS servers, unless another interface uses them as well: queried from Xray itself, they would lead back into it, or nowhere.
If the filters cannot be added, Xray does not start. They are removed when Xray exits. Not covered is name resolution on the local network (LLMNR, mDNS, NetBIOS), except over an IP version that is blocked.
If the filters cannot be added, Xray does not start. They are removed when Xray exits. Not covered is name resolution on the local network (LLMNR, mDNS, NetBIOS), except over an IP version that is blocked entirely.
`autoSystemWfpBlockLeak` (Windows only) is empty by default, as the filters break some setups: with `"dns"`, a local DNS resolver that sends its own queries to port 53 or 853 through another interface, the DNS of another VPN on its own interface, virtual machines whose NAT resolves names on the host, or signing in to a captive portal; with `"misconfigtun"`, IPv4 or IPv6 on the local network while no route of that version leads to the TUN; with `"all"`, what is listed above. Without the filters, DNS may leak as described above. To keep an IP version out of the TUN on purpose while still blocking DNS leaks, use only `["dns"]`.
`autoSystemWfpBlockLeak` (Windows only) is empty by default, as the filters break some setups: with `"dns"`, a local DNS resolver that sends its own queries to port 53 or 853 through another interface, the DNS of another VPN on its own interface, virtual machines whose NAT resolves names on the host, or signing in to a captive portal; with `"misconfigtun"`, IPv4 or IPv6 on the local network while no route of that version leads to the TUN; with `"all"` and `"allexceptlan"`, what is listed above. Without the filters, DNS may leak as described above. To keep an IP version out of the TUN on purpose while still blocking DNS leaks, use only `["dns"]`.
`autoOutboundsInterface` (the default with `autoSystemRoutingTable`) keeps Xray's own connections out of the TUN by binding them to another interface, which Windows only honors while that interface has weak host send and forwarding off for the IP versions routed to the TUN. Otherwise, Windows sends them into the TUN, from that interface's address, and they stall. While the TUN runs, Xray therefore turns weak host send off on that interface, and on again when it stops or another interface takes over. Forwarding cannot be turned off this way, as Mobile Hotspot and Internet Connection Sharing need it, so a warning is logged while it is on. Having the hotspot share the TUN instead of that interface (Settings, Mobile hotspot, Share my internet connection from) moves forwarding to the TUN, where it does no harm, and sends the hotspot's devices through Xray as well.
+4 -3
View File
@@ -249,9 +249,10 @@ startOver:
// With autoSystemWfpBlockLeak, once the system routes lead to the TUN,
// keep the protocols and ports given ("dns" stands for those of DNS),
// Windows' DNS Client service, an IP version no route of which leads to
// the TUN ("misconfigtun"), or everything ("all"), from leaving through
// the other interfaces. Addresses do not matter: without one of a version
// in gateway, Windows gives the TUN a link-local one.
// the TUN ("misconfigtun"), or everything ("all", or "allexceptlan" but
// for the local network), from leaving through the other interfaces.
// Addresses do not matter: without one of a version in gateway, Windows
// gives the TUN a link-local one.
leaks, err := ExpandWfpBlockLeak(t.options.AutoSystemWfpBlockLeak)
if err != nil {
return err
+93 -12
View File
@@ -3,6 +3,7 @@
package tun
import (
"encoding/binary"
"net/netip"
"os"
"runtime"
@@ -44,6 +45,8 @@ const (
fwpByteArray16Type = 11 // FWP_BYTE_ARRAY16_TYPE
fwpByteBlobType = 12 // FWP_BYTE_BLOB_TYPE
fwpSecurityDescriptorType = 14 // FWP_SECURITY_DESCRIPTOR_TYPE
fwpV4AddrMask = 0x100 // FWP_V4_ADDR_MASK
fwpV6AddrMask = 0x101 // FWP_V6_ADDR_MASK
fwpRangeType = 0x102 // FWP_RANGE_TYPE
fwpMatchEqual = 0 // FWP_MATCH_EQUAL
@@ -101,6 +104,32 @@ type fwpRange0 struct {
low, high fwpValue0
}
// fwpV4AddrAndMask is FWP_V4_ADDR_AND_MASK, in host byte order.
type fwpV4AddrAndMask struct {
addr, mask uint32
}
// fwpV6AddrAndMask is FWP_V6_ADDR_AND_MASK.
type fwpV6AddrAndMask struct {
addr [16]byte
prefixLength uint8
}
// lanPrefixes are what "allexceptlan" leaves to the local network: private
// and link-local addresses, and multicast and broadcast, which discovery on
// it uses.
var lanPrefixes = []netip.Prefix{
netip.MustParsePrefix("10.0.0.0/8"),
netip.MustParsePrefix("172.16.0.0/12"),
netip.MustParsePrefix("192.168.0.0/16"),
netip.MustParsePrefix("169.254.0.0/16"),
netip.MustParsePrefix("224.0.0.0/4"),
netip.MustParsePrefix("255.255.255.255/32"),
netip.MustParsePrefix("fc00::/7"),
netip.MustParsePrefix("fe80::/10"),
netip.MustParsePrefix("ff00::/8"),
}
type fwpmDisplayData0 struct {
name *uint16
description *uint16
@@ -183,12 +212,14 @@ type leakFilters struct {
traffic []leakTraffic // connections of these protocols and ports
dnsClient bool // Windows' DNS Client service
ipv4, ipv6 bool // an IP version altogether
lan bool // except to and from the local network
}
// newLeakFilters turns expanded autoSystemWfpBlockLeak values into the filters
// for a TUN that the routes of route4 (IPv4) and route6 (IPv6) lead to.
func newLeakFilters(leaks []string, route4, route6 bool) leakFilters {
var block leakFilters
var all, allExceptLAN bool
for _, leak := range leaks {
switch leak {
case "windnsclient":
@@ -196,13 +227,19 @@ func newLeakFilters(leaks []string, route4, route6 bool) leakFilters {
case "misconfigtun":
block.ipv4, block.ipv6 = block.ipv4 || !route4, block.ipv6 || !route6
case "all":
block.ipv4, block.ipv6 = true, true
all = true
case "allexceptlan":
allExceptLAN = true
default:
if traffic, ok := parseLeakTraffic(leak); ok {
block.traffic = append(block.traffic, traffic)
}
}
}
if all || allExceptLAN {
block.ipv4, block.ipv6 = true, true
block.lan = !all
}
return block
}
@@ -212,10 +249,14 @@ func (block leakFilters) String() string {
for _, traffic := range block.traffic {
blocked = append(blocked, traffic.String())
}
exceptLAN := ""
if block.lan {
exceptLAN = " except LAN"
}
for _, b := range []struct {
on bool
what string
}{{block.dnsClient, "windnsclient"}, {block.ipv4, "IPv4"}, {block.ipv6, "IPv6"}} {
}{{block.dnsClient, "windnsclient"}, {block.ipv4, "IPv4" + exceptLAN}, {block.ipv6, "IPv6" + exceptLAN}} {
if b.on {
blocked = append(blocked, b.what)
}
@@ -224,9 +265,10 @@ func (block leakFilters) String() string {
}
// systemDNS tells whether block keeps the system's DNS inside the TUN: port
// 53, the DNS Client service, or both IP versions altogether.
// 53, the DNS Client service, or both IP versions altogether, though not on
// the local network, where a resolver like the router's may be.
func (block leakFilters) systemDNS() bool {
return block.dnsClient || block.ipv4 && block.ipv6 || slices.ContainsFunc(block.traffic, func(traffic leakTraffic) bool {
return block.dnsClient || block.ipv4 && block.ipv6 && !block.lan || slices.ContainsFunc(block.traffic, func(traffic leakTraffic) bool {
return traffic.covers("udp", 53) || traffic.covers("tcp", 53)
})
}
@@ -261,10 +303,12 @@ func dnsClientUsesAnyPort() bool {
// link (mDNS, LLMNR).
// - ipv4, ipv6: no IPv4, or no IPv6, at all outside the TUN, in either
// direction: for a TUN that no route of that version leads to, or for
// everything with "all". The TUN carries what is routed to it even without
// an address of that IP version in gateway: Windows gives it link-local
// ones itself, an IPv6 one at once, an IPv4 one from 169.254.0.0/16 after
// some seconds (until then, IPv4 routed to the TUN is unreachable).
// everything with "all", or with "allexceptlan" (lan) except to and from
// the local network, below the other filters, which so still apply there.
// The TUN carries what is routed to it even without an address of that
// IP version in gateway: Windows gives it link-local ones itself, an IPv6
// one at once, an IPv4 one from 169.254.0.0/16 after some seconds (until
// then, IPv4 routed to the TUN is unreachable).
//
// Whatever is blocked, loopback, which cannot leave the system, and what
// Windows itself needs on the local link stay allowed: DHCP, and for IPv6
@@ -369,6 +413,30 @@ func addLeakFilters(engine windows.Handle, tun winipcfg.LUID, block leakFilters)
onTUN := func(field *windows.GUID) fwpmFilterCondition0 {
return condition(field, fwpUint64, uintptr(unsafe.Pointer(tunLUID)))
}
// toLAN matches the remote addresses of lanPrefixes of an IP version:
// conditions on the same field match when any of them does.
toLAN := func(ipv6 bool) []fwpmFilterCondition0 {
var conditions []fwpmFilterCondition0
for _, prefix := range lanPrefixes {
if prefix.Addr().Is6() != ipv6 {
continue
}
var value uintptr
typ := uint32(fwpV4AddrMask)
if ipv6 {
addr := &fwpV6AddrAndMask{addr: prefix.Addr().As16(), prefixLength: uint8(prefix.Bits())}
pinner.Pin(addr) // the condition only holds it as uintptr
value, typ = uintptr(unsafe.Pointer(addr)), fwpV6AddrMask
} else {
a := prefix.Addr().As4()
addr := &fwpV4AddrAndMask{addr: binary.BigEndian.Uint32(a[:]), mask: ^uint32(0) << (32 - prefix.Bits())}
pinner.Pin(addr) // the condition only holds it as uintptr
value = uintptr(unsafe.Pointer(addr))
}
conditions = append(conditions, condition(&fwpmConditionIPRemoteAddress, typ, value))
}
return conditions
}
// toPorts matches the ports of traffic, if not all of them.
toPorts := func(traffic leakTraffic) []fwpmFilterCondition0 {
switch {
@@ -471,6 +539,13 @@ func addLeakFilters(engine windows.Handle, tun winipcfg.LUID, block leakFilters)
if err := add(layer.key, "permit "+family+" through the TUN", 0, fwpActionPermit, 1, throughTUN...); err != nil {
return err
}
// Below the other values, which so still apply on the local
// network, DNS with "allexceptlan" among them.
if block.lan {
if err := add(layer.key, "permit "+family+" LAN", 0, fwpActionPermit, 1, toLAN(layer.ipv6)...); err != nil {
return err
}
}
if err := add(layer.key, "block "+family, 0, fwpActionBlock, 0); err != nil {
return err
}
@@ -481,7 +556,8 @@ func addLeakFilters(engine windows.Handle, tun winipcfg.LUID, block leakFilters)
// the queries for an interface's servers over HTTPS or TLS, out through
// that interface and to any port. So there it may only connect through
// the TUN, except for mDNS and LLMNR, which stay on the local link (over
// an IP version only while it is not blocked altogether). Like Windows
// an IP version only while it is not blocked altogether, or with
// "allexceptlan" to the local network's addresses). Like Windows
// Firewall's rules for it, the filters recognize the service by its SID,
// which Windows puts in the token of its process: the security descriptor
// grants that SID the right to match (FWP_ACTRL_MATCH_FILTER, CC in SDDL).
@@ -497,16 +573,21 @@ func addLeakFilters(engine windows.Handle, tun winipcfg.LUID, block leakFilters)
mdnsLLMNR := []fwpmFilterCondition0{dnsClient, condition(&fwpmConditionIPRemotePort, fwpUint16, 5353), condition(&fwpmConditionIPRemotePort, fwpUint16, 5355)}
for _, layer := range []struct {
key *windows.GUID
ipv6 bool
localLink bool
}{
{&fwpmLayerALEAuthConnectV4, !block.ipv4},
{&fwpmLayerALEAuthConnectV6, !block.ipv6},
{&fwpmLayerALEAuthConnectV4, false, !block.ipv4 || block.lan},
{&fwpmLayerALEAuthConnectV6, true, !block.ipv6 || block.lan},
} {
if err := add(layer.key, "permit the DNS Client service through the TUN", 0, fwpActionPermit, 3, dnsClient, onTUN(&fwpmConditionIPLocalInterface), onTUN(&fwpmConditionIPNexthopInterface)); err != nil {
return err
}
if layer.localLink {
if err := add(layer.key, "permit the DNS Client service's mDNS and LLMNR", 0, fwpActionPermit, 3, mdnsLLMNR...); err != nil {
localLink := mdnsLLMNR
if block.lan {
localLink = slices.Concat(mdnsLLMNR, toLAN(layer.ipv6))
}
if err := add(layer.key, "permit the DNS Client service's mDNS and LLMNR", 0, fwpActionPermit, 3, localLink...); err != nil {
return err
}
}
+30
View File
@@ -45,6 +45,16 @@ func TestWFPStructLayout(t *testing.T) {
[]uintptr{unsafe.Sizeof(ports), unsafe.Offsetof(ports.high)},
[]uintptr{32, 16}, []uintptr{16, 8})
var v4 fwpV4AddrAndMask
check("FWP_V4_ADDR_AND_MASK",
[]uintptr{unsafe.Sizeof(v4), unsafe.Offsetof(v4.mask)},
[]uintptr{8, 4}, []uintptr{8, 4})
var v6 fwpV6AddrAndMask
check("FWP_V6_ADDR_AND_MASK",
[]uintptr{unsafe.Sizeof(v6), unsafe.Offsetof(v6.prefixLength)},
[]uintptr{17, 16}, []uintptr{17, 16})
var display fwpmDisplayData0
check("FWPM_DISPLAY_DATA0",
[]uintptr{unsafe.Sizeof(display), unsafe.Offsetof(display.description)},
@@ -128,6 +138,18 @@ func TestLeakFiltersAccepted(t *testing.T) {
if err := addLeakFilters(engine, loopback, block); err != nil {
skipUnlessElevated(err)
}
// The local network's addresses, with another sublayer.
if leaks, err = ExpandWfpBlockLeak([]string{"allexceptlan", "udp/1000-2000", "icmp"}); err != nil {
t.Fatal(err)
}
block = newLeakFilters(leaks, true, false)
block.dnsClient = true
if !block.lan {
t.Fatal("allexceptlan left the local network blocked")
}
if err := addLeakFilters(engine, loopback, block); err != nil {
skipUnlessElevated(err)
}
}
func TestNewLeakFilters(t *testing.T) {
@@ -148,6 +170,9 @@ func TestNewLeakFilters(t *testing.T) {
{[]string{"misconfigtun"}, true, false, "IPv6", false},
{[]string{"tcp/10-20", "udp", "icmp"}, true, true, "tcp/10-20, udp, icmp", true},
{[]string{"tcp/54-60", "icmp"}, true, true, "tcp/54-60, icmp", false},
{[]string{"allexceptlan"}, true, true, "udp/53, tcp/53, tcp/853, udp/853" + windnsclient + ", IPv4 except LAN, IPv6 except LAN", true},
{[]string{"allexceptlan", "all"}, true, true, "udp/53, tcp/53, tcp/853, udp/853" + windnsclient + ", IPv4, IPv6", true},
{[]string{"misconfigtun", "allexceptlan"}, true, false, "udp/53, tcp/53, tcp/853, udp/853" + windnsclient + ", IPv4 except LAN, IPv6 except LAN", true},
} {
leaks, err := ExpandWfpBlockLeak(c.values)
if err != nil {
@@ -161,6 +186,11 @@ func TestNewLeakFilters(t *testing.T) {
t.Errorf("%v: systemDNS() = %v", c.values, got)
}
}
// Without the port 53 values "allexceptlan" brings along, a resolver on
// the local network, like the router's, stays reachable.
if (leakFilters{ipv4: true, ipv6: true, lan: true}).systemDNS() {
t.Error("systemDNS() = true with the local network open")
}
}
func TestDNSClientSID(t *testing.T) {
+9 -1
View File
@@ -72,7 +72,8 @@ 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, and ports written the same way, "tcp/0" as "tcp" say.
// what it stands for, "allexceptlan" followed by that, and ports written the
// same way, "tcp/0" as "tcp" say.
func ExpandWfpBlockLeak(values []string) ([]string, error) {
var leaks []string
add := func(leak string) {
@@ -88,6 +89,13 @@ func ExpandWfpBlockLeak(values []string) ([]string, error) {
}
case "windnsclient", "misconfigtun", "all":
add(value)
case "allexceptlan":
// The local network is no way out for DNS, which a resolver
// there, like the router, would forward outside the TUN.
add(value)
for _, leak := range dnsLeaks {
add(leak)
}
default:
traffic, ok := parseLeakTraffic(value)
if !ok {
+2
View File
@@ -25,6 +25,8 @@ func TestExpandWfpBlockLeak(t *testing.T) {
{[]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},
{[]string{"ICMP", "icmp"}, []string{"icmp"}, false},
{[]string{"AllExceptLAN"}, []string{"allexceptlan", "udp/53", "tcp/53", "windnsclient", "tcp/853", "udp/853"}, true},
{[]string{"dns", "allexceptlan", "all"}, []string{"udp/53", "tcp/53", "windnsclient", "tcp/853", "udp/853", "allexceptlan", "all"}, true},
} {
leaks, err := ExpandWfpBlockLeak(c.values)
if err != nil {