github.com/yaling888/clash@v1.53.0/component/dialer/bind_darwin.go (about)

     1  package dialer
     2  
     3  import (
     4  	"net"
     5  	"net/netip"
     6  	"syscall"
     7  
     8  	"golang.org/x/sys/unix"
     9  
    10  	"github.com/yaling888/clash/component/iface"
    11  )
    12  
    13  type controlFn = func(network, address string, c syscall.RawConn) error
    14  
    15  func bindControl(ifaceIdx int, chain controlFn) controlFn {
    16  	return func(network, address string, c syscall.RawConn) (err error) {
    17  		defer func() {
    18  			if err == nil && chain != nil {
    19  				err = chain(network, address, c)
    20  			}
    21  		}()
    22  
    23  		addrPort, err := netip.ParseAddrPort(address)
    24  		if err == nil && !addrPort.Addr().IsGlobalUnicast() {
    25  			return
    26  		}
    27  
    28  		var innerErr error
    29  		err = c.Control(func(fd uintptr) {
    30  			switch network {
    31  			case "tcp4", "udp4":
    32  				innerErr = unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_BOUND_IF, ifaceIdx)
    33  			case "tcp6", "udp6":
    34  				innerErr = unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_BOUND_IF, ifaceIdx)
    35  			}
    36  		})
    37  
    38  		if innerErr != nil {
    39  			err = innerErr
    40  		}
    41  
    42  		return
    43  	}
    44  }
    45  
    46  func bindIfaceToDialer(ifaceName string, dialer *net.Dialer, _ string, _ netip.Addr) error {
    47  	ifaceObj, err := iface.ResolveInterface(ifaceName)
    48  	if err != nil {
    49  		return err
    50  	}
    51  
    52  	dialer.Control = bindControl(ifaceObj.Index, dialer.Control)
    53  	return nil
    54  }
    55  
    56  func bindIfaceToListenConfig(ifaceName string, lc *net.ListenConfig, _, address string) (string, error) {
    57  	ifaceObj, err := iface.ResolveInterface(ifaceName)
    58  	if err != nil {
    59  		return "", err
    60  	}
    61  
    62  	lc.Control = bindControl(ifaceObj.Index, lc.Control)
    63  	return address, nil
    64  }
    65  
    66  func WithBindToInterfaceControlFn(interfaceName string) func(network, address string, c syscall.RawConn) (err error) {
    67  	return func(network, address string, c syscall.RawConn) (err error) {
    68  		if interfaceName == "" {
    69  			return nil
    70  		}
    71  
    72  		var (
    73  			innerErr error
    74  			ifaceObj *iface.Interface
    75  		)
    76  
    77  		ifaceObj, err = iface.ResolveInterface(interfaceName)
    78  		if err != nil {
    79  			return
    80  		}
    81  
    82  		err = c.Control(func(fd uintptr) {
    83  			switch network {
    84  			case "udp4":
    85  				innerErr = unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_BOUND_IF, ifaceObj.Index)
    86  			case "udp6":
    87  				innerErr = unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_BOUND_IF, ifaceObj.Index)
    88  			}
    89  		})
    90  
    91  		if innerErr != nil {
    92  			err = innerErr
    93  		}
    94  
    95  		return
    96  	}
    97  }