github.com/metacubex/mihomo@v1.18.5/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/metacubex/mihomo/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  }