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  }