Source file src/internal/syscall/windows/version_windows.go

     1  // Copyright 2024 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package windows
     6  
     7  import (
     8  	"errors"
     9  	"sync"
    10  	"syscall"
    11  	"unsafe"
    12  )
    13  
    14  // https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_osversioninfow
    15  type _OSVERSIONINFOW struct {
    16  	osVersionInfoSize uint32
    17  	majorVersion      uint32
    18  	minorVersion      uint32
    19  	buildNumber       uint32
    20  	platformId        uint32
    21  	csdVersion        [128]uint16
    22  }
    23  
    24  // According to documentation, RtlGetVersion function always succeeds.
    25  //sys	rtlGetVersion(info *_OSVERSIONINFOW) = ntdll.RtlGetVersion
    26  
    27  // version retrieves the major, minor, and build version numbers
    28  // of the current Windows OS from the RtlGetVersion API.
    29  func version() (major, minor, build uint32) {
    30  	info := _OSVERSIONINFOW{}
    31  	info.osVersionInfoSize = uint32(unsafe.Sizeof(info))
    32  	rtlGetVersion(&info)
    33  	return info.majorVersion, info.minorVersion, info.buildNumber
    34  }
    35  
    36  var (
    37  	supportTCPKeepAliveIdle     bool
    38  	supportTCPKeepAliveInterval bool
    39  	supportTCPKeepAliveCount    bool
    40  )
    41  
    42  var initTCPKeepAlive = sync.OnceFunc(func() {
    43  	s, err := WSASocket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP, nil, 0, WSA_FLAG_NO_HANDLE_INHERIT)
    44  	if err != nil {
    45  		// Fallback to checking the Windows version.
    46  		major, _, build := version()
    47  		supportTCPKeepAliveIdle = major >= 10 && build >= 16299
    48  		supportTCPKeepAliveInterval = major >= 10 && build >= 16299
    49  		supportTCPKeepAliveCount = major >= 10 && build >= 15063
    50  		return
    51  	}
    52  	defer syscall.Closesocket(s)
    53  	var optSupported = func(opt int) bool {
    54  		err := syscall.SetsockoptInt(s, syscall.IPPROTO_TCP, opt, 1)
    55  		return !errors.Is(err, syscall.WSAENOPROTOOPT)
    56  	}
    57  	supportTCPKeepAliveIdle = optSupported(TCP_KEEPIDLE)
    58  	supportTCPKeepAliveInterval = optSupported(TCP_KEEPINTVL)
    59  	supportTCPKeepAliveCount = optSupported(TCP_KEEPCNT)
    60  })
    61  
    62  // SupportTCPKeepAliveInterval indicates whether TCP_KEEPIDLE is supported.
    63  // The minimal requirement is Windows 10.0.16299.
    64  func SupportTCPKeepAliveIdle() bool {
    65  	initTCPKeepAlive()
    66  	return supportTCPKeepAliveIdle
    67  }
    68  
    69  // SupportTCPKeepAliveInterval indicates whether TCP_KEEPINTVL is supported.
    70  // The minimal requirement is Windows 10.0.16299.
    71  func SupportTCPKeepAliveInterval() bool {
    72  	initTCPKeepAlive()
    73  	return supportTCPKeepAliveInterval
    74  }
    75  
    76  // SupportTCPKeepAliveCount indicates whether TCP_KEEPCNT is supported.
    77  // supports TCP_KEEPCNT.
    78  // The minimal requirement is Windows 10.0.15063.
    79  func SupportTCPKeepAliveCount() bool {
    80  	initTCPKeepAlive()
    81  	return supportTCPKeepAliveCount
    82  }
    83  
    84  // SupportTCPInitialRTONoSYNRetransmissions indicates whether the current
    85  // Windows version supports the TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS.
    86  // The minimal requirement is Windows 10.0.16299.
    87  var SupportTCPInitialRTONoSYNRetransmissions = sync.OnceValue(func() bool {
    88  	major, _, build := version()
    89  	return major >= 10 && build >= 16299
    90  })
    91  
    92  // SupportUnixSocket indicates whether the current Windows version supports
    93  // Unix Domain Sockets.
    94  // The minimal requirement is Windows 10.0.17063.
    95  var SupportUnixSocket = sync.OnceValue(func() bool {
    96  	var size uint32
    97  	// First call to get the required buffer size in bytes.
    98  	// Ignore the error, it will always fail.
    99  	_, _ = syscall.WSAEnumProtocols(nil, nil, &size)
   100  	n := int32(size) / int32(unsafe.Sizeof(syscall.WSAProtocolInfo{}))
   101  	// Second call to get the actual protocols.
   102  	buf := make([]syscall.WSAProtocolInfo, n)
   103  	n, err := syscall.WSAEnumProtocols(nil, &buf[0], &size)
   104  	if err != nil {
   105  		return false
   106  	}
   107  	for i := int32(0); i < n; i++ {
   108  		if buf[i].AddressFamily == syscall.AF_UNIX {
   109  			return true
   110  		}
   111  	}
   112  	return false
   113  })
   114  

View as plain text