Files
v2rayA/core/dns/socket_windows_test.go
Zakk a6c2d5f1d2 feat(core): bind the DNS module's own sockets to an egress interface
On Linux the module's upstream, bootstrap and health-check sockets carry
SO_MARK 0x80, which the transparent-proxy rules exempt. Windows and macOS
have no mark: once a TUN holds the default route, those sockets would send
the module's own queries into the TUN, where the core's port-53
interception hands them straight back to the module. The module config
gains egress_interface; when set, every dialer created by
newMarkedDnsClient binds to it with IP_UNICAST_IF / IPV6_UNICAST_IF on
Windows and IP_BOUND_IF / IPV6_BOUND_IF on macOS. Unset, the dialers are
unchanged, and Linux keeps the mark.

The darwin and windows tests dial through the dialer and read the option
back; they run on those platforms. The interface lookup itself is tested
everywhere.
2026-09-19 10:34:55 +10:00

55 lines
1.3 KiB
Go

//go:build windows
package dns
import (
"encoding/binary"
"net"
"syscall"
"testing"
"unsafe"
"golang.org/x/sys/windows"
)
// The dialer's sockets must carry IP_UNICAST_IF for the configured
// interface and nothing when none is configured.
func TestMarkedDialerBindsEgressInterface(t *testing.T) {
t.Cleanup(func() { SetEgressInterface("") })
pc, err := net.ListenPacket("udp4", "127.0.0.1:0")
if err != nil {
t.Fatal(err)
}
defer pc.Close()
boundIf := func() int {
conn, err := markedDialer().Dial("udp4", pc.LocalAddr().String())
if err != nil {
t.Fatal(err)
}
defer conn.Close()
raw, err := conn.(*net.UDPConn).SyscallConn()
if err != nil {
t.Fatal(err)
}
// The option is set in network byte order, but getsockopt hands
// the index back in host order.
var buf [4]byte
raw.Control(func(fd uintptr) {
n := int32(len(buf))
_ = windows.Getsockopt(windows.Handle(fd), syscall.IPPROTO_IP, ipUnicastIf, (*byte)(unsafe.Pointer(&buf[0])), &n)
})
return int(binary.LittleEndian.Uint32(buf[:]))
}
SetEgressInterface("")
if idx := boundIf(); idx != 0 {
t.Fatalf("unconfigured dialer bound to interface %d", idx)
}
lo := loopbackInterface(t)
SetEgressInterface(lo.Name)
if idx := boundIf(); idx != lo.Index {
t.Fatalf("bound to %d, want %d", idx, lo.Index)
}
}