github.com/yaling888/clash@v1.53.0/component/dialer/bind_windows.go (about) 1 package dialer 2 3 import ( 4 "encoding/binary" 5 "net" 6 "net/netip" 7 "syscall" 8 9 "golang.org/x/sys/windows" 10 11 "github.com/yaling888/clash/component/iface" 12 ) 13 14 const ( 15 ipUnicastIf = 31 16 ipv6UnicastIf = 31 17 ) 18 19 type controlFn = func(network, address string, c syscall.RawConn) error 20 21 func bindControl(ifaceIdx int, chain controlFn) controlFn { 22 return func(network, address string, c syscall.RawConn) (err error) { 23 defer func() { 24 if err == nil && chain != nil { 25 err = chain(network, address, c) 26 } 27 }() 28 29 addrPort, err := netip.ParseAddrPort(address) 30 if err == nil && !addrPort.Addr().IsGlobalUnicast() { 31 return 32 } 33 34 var innerErr error 35 err = c.Control(func(fd uintptr) { 36 switch network { 37 case "tcp4", "udp4": 38 innerErr = bindSocketToInterface4(windows.Handle(fd), ifaceIdx) 39 case "tcp6", "udp6": 40 innerErr = bindSocketToInterface6(windows.Handle(fd), ifaceIdx) 41 if network == "udp6" && !addrPort.IsValid() { 42 // The underlying IP net maybe IPv4 even if the `network` param is `udp6`, 43 // so we should bind socket to interface4 at the same time. 44 innerErr = bindSocketToInterface4(windows.Handle(fd), ifaceIdx) 45 } 46 } 47 }) 48 49 if innerErr != nil { 50 err = innerErr 51 } 52 53 return 54 } 55 } 56 57 func bindSocketToInterface4(handle windows.Handle, index int) error { 58 // For IPv4, this parameter must be an interface index in network byte order. 59 // Ref: https://learn.microsoft.com/en-us/windows/win32/winsock/ipproto-ip-socket-options 60 buf := make([]byte, 4) 61 binary.BigEndian.PutUint32(buf, uint32(index)) 62 return windows.SetsockoptInt(handle, windows.IPPROTO_IP, ipUnicastIf, int(binary.NativeEndian.Uint32(buf))) 63 } 64 65 func bindSocketToInterface6(handle windows.Handle, index int) error { 66 return windows.SetsockoptInt(handle, windows.IPPROTO_IPV6, ipv6UnicastIf, index) 67 } 68 69 func bindIfaceToDialer(ifaceName string, dialer *net.Dialer, _ string, _ netip.Addr) error { 70 ifaceObj, err := iface.ResolveInterface(ifaceName) 71 if err != nil { 72 return err 73 } 74 75 dialer.Control = bindControl(ifaceObj.Index, dialer.Control) 76 return nil 77 } 78 79 func bindIfaceToListenConfig(ifaceName string, lc *net.ListenConfig, _, address string) (string, error) { 80 ifaceObj, err := iface.ResolveInterface(ifaceName) 81 if err != nil { 82 return "", err 83 } 84 85 lc.Control = bindControl(ifaceObj.Index, lc.Control) 86 return address, nil 87 } 88 89 func WithBindToInterfaceControlFn(interfaceName string) func(network, address string, c syscall.RawConn) (err error) { 90 return func(network, address string, c syscall.RawConn) (err error) { 91 if interfaceName == "" { 92 return nil 93 } 94 95 var ( 96 innerErr error 97 ifaceObj *iface.Interface 98 ) 99 100 ifaceObj, err = iface.ResolveInterface(interfaceName) 101 if err != nil { 102 return 103 } 104 105 err = c.Control(func(fd uintptr) { 106 switch network { 107 case "udp4": 108 innerErr = bindSocketToInterface4(windows.Handle(fd), ifaceObj.Index) 109 case "udp6": 110 innerErr = bindSocketToInterface6(windows.Handle(fd), ifaceObj.Index) 111 if innerErr != nil { 112 innerErr = syscall.EAFNOSUPPORT 113 return 114 } 115 } 116 }) 117 118 if innerErr != nil { 119 err = innerErr 120 } 121 122 return 123 } 124 }