github.com/Asutorufa/yuhaiin@v0.3.6-0.20240502055049-7984da7023a0/pkg/net/dialer/setopt_windows.go (about)

     1  package dialer
     2  
     3  import (
     4  	"encoding/binary"
     5  	"net"
     6  	"syscall"
     7  	"unsafe"
     8  
     9  	"golang.org/x/sys/windows"
    10  )
    11  
    12  const (
    13  	IP_UNICAST_IF   = 31
    14  	IPV6_UNICAST_IF = 31
    15  )
    16  
    17  func setSocketOptions(network, address string, c syscall.RawConn, opts *Options) (err error) {
    18  	if opts == nil || !isTCPSocket(network) && !isUDPSocket(network) {
    19  		return
    20  	}
    21  
    22  	var innerErr error
    23  	err = c.Control(func(fd uintptr) {
    24  		if opts.listener {
    25  			_ = windows.SetsockoptInt(windows.Handle(fd), windows.SOL_SOCKET, windows.SO_RCVBUF, SocketBufferSize)
    26  			_ = windows.SetsockoptInt(windows.Handle(fd), windows.SOL_SOCKET, windows.SO_SNDBUF, SocketBufferSize)
    27  		}
    28  
    29  		host, _, _ := net.SplitHostPort(address)
    30  		ip := net.ParseIP(host)
    31  		if ip != nil && !ip.IsGlobalUnicast() {
    32  			return
    33  		}
    34  
    35  		if opts.InterfaceIndex == 0 && opts.InterfaceName != "" {
    36  			if iface, err := net.InterfaceByName(opts.InterfaceName); err == nil {
    37  				opts.InterfaceIndex = iface.Index
    38  			}
    39  		}
    40  
    41  		if opts.InterfaceIndex != 0 {
    42  			switch network {
    43  			case "tcp4", "udp4":
    44  				innerErr = bindSocketToInterface4(windows.Handle(fd), uint32(opts.InterfaceIndex))
    45  			case "tcp6", "udp6":
    46  				innerErr = bindSocketToInterface6(windows.Handle(fd), uint32(opts.InterfaceIndex))
    47  				if network == "udp6" && ip == nil {
    48  					// The underlying IP net maybe IPv4 even if the `network` param is `udp6`,
    49  					// so we should bind socket to interface4 at the same time.
    50  					innerErr = bindSocketToInterface4(windows.Handle(fd), uint32(opts.InterfaceIndex))
    51  				}
    52  			}
    53  		}
    54  	})
    55  
    56  	if innerErr != nil {
    57  		err = innerErr
    58  	}
    59  	return
    60  }
    61  
    62  func bindSocketToInterface4(handle windows.Handle, index uint32) error {
    63  	// For IPv4, this parameter must be an interface index in network byte order.
    64  	// Ref: https://learn.microsoft.com/en-us/windows/win32/winsock/ipproto-ip-socket-options
    65  	var bytes [4]byte
    66  	binary.BigEndian.PutUint32(bytes[:], index)
    67  	index = *(*uint32)(unsafe.Pointer(&bytes[0]))
    68  	return windows.SetsockoptInt(handle, windows.IPPROTO_IP, IP_UNICAST_IF, int(index))
    69  }
    70  
    71  func bindSocketToInterface6(handle windows.Handle, index uint32) error {
    72  	return windows.SetsockoptInt(handle, windows.IPPROTO_IPV6, IPV6_UNICAST_IF, int(index))
    73  }