github.com/kelleygo/clashcore@v1.0.2/component/dialer/bind_windows.go (about) 1 package dialer 2 3 import ( 4 "context" 5 "encoding/binary" 6 "fmt" 7 "net" 8 "net/netip" 9 "syscall" 10 "unsafe" 11 12 "github.com/kelleygo/clashcore/component/iface" 13 ) 14 15 const ( 16 IP_UNICAST_IF = 31 17 IPV6_UNICAST_IF = 31 18 ) 19 20 func bind4(handle syscall.Handle, ifaceIdx int) error { 21 var bytes [4]byte 22 binary.BigEndian.PutUint32(bytes[:], uint32(ifaceIdx)) 23 idx := *(*uint32)(unsafe.Pointer(&bytes[0])) 24 err := syscall.SetsockoptInt(handle, syscall.IPPROTO_IP, IP_UNICAST_IF, int(idx)) 25 if err != nil { 26 err = fmt.Errorf("bind4: %w", err) 27 } 28 return err 29 } 30 31 func bind6(handle syscall.Handle, ifaceIdx int) error { 32 err := syscall.SetsockoptInt(handle, syscall.IPPROTO_IPV6, IPV6_UNICAST_IF, ifaceIdx) 33 if err != nil { 34 err = fmt.Errorf("bind6: %w", err) 35 } 36 return err 37 } 38 39 func bindControl(ifaceIdx int, rAddrPort netip.AddrPort) controlFn { 40 return func(ctx context.Context, network, address string, c syscall.RawConn) (err error) { 41 addrPort, err := netip.ParseAddrPort(address) 42 if err == nil && !addrPort.Addr().IsGlobalUnicast() { 43 return 44 } 45 46 var innerErr error 47 err = c.Control(func(fd uintptr) { 48 handle := syscall.Handle(fd) 49 bind6err := bind6(handle, ifaceIdx) 50 bind4err := bind4(handle, ifaceIdx) 51 switch network { 52 case "ip6", "tcp6": 53 innerErr = bind6err 54 case "ip4", "tcp4", "udp4": 55 innerErr = bind4err 56 case "udp6": 57 // golang will set network to udp6 when listenUDP on wildcard ip (eg: ":0", "") 58 if (!addrPort.Addr().IsValid() || addrPort.Addr().IsUnspecified()) && bind6err != nil && rAddrPort.Addr().Unmap().Is4() { 59 // try bind ipv6, if failed, ignore. it's a workaround for windows disable interface ipv6 60 if bind4err != nil { 61 innerErr = fmt.Errorf("%w (%s)", bind6err, bind4err) 62 } else { 63 innerErr = nil 64 } 65 } else { 66 innerErr = bind6err 67 } 68 } 69 }) 70 71 if innerErr != nil { 72 err = innerErr 73 } 74 75 return 76 } 77 } 78 79 func bindIfaceToDialer(ifaceName string, dialer *net.Dialer, _ string, destination netip.Addr) error { 80 ifaceObj, err := iface.ResolveInterface(ifaceName) 81 if err != nil { 82 return err 83 } 84 85 addControlToDialer(dialer, bindControl(ifaceObj.Index, netip.AddrPortFrom(destination, 0))) 86 return nil 87 } 88 89 func bindIfaceToListenConfig(ifaceName string, lc *net.ListenConfig, _, address string, rAddrPort netip.AddrPort) (string, error) { 90 ifaceObj, err := iface.ResolveInterface(ifaceName) 91 if err != nil { 92 return "", err 93 } 94 95 addControlToListenConfig(lc, bindControl(ifaceObj.Index, rAddrPort)) 96 return address, nil 97 } 98 99 func ParseNetwork(network string, addr netip.Addr) string { 100 return network 101 }