blob: 5edf7a01e270d498cb9432908d6169d96adf78db [file] [log] [blame]
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package windows
import (
"errors"
"sync"
"syscall"
"unsafe"
)
// https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_osversioninfoexw
type _OSVERSIONINFOEXW struct {
osVersionInfoSize uint32
majorVersion uint32
minorVersion uint32
buildNumber uint32
platformId uint32
csdVersion [128]uint16
servicePackMajor uint16
servicePackMinor uint16
suiteMask uint16
productType byte
reserved byte
}
// According to documentation, RtlGetVersion function always succeeds.
//sys rtlGetVersion(info *_OSVERSIONINFOEXW) = ntdll.RtlGetVersion
// Retrieves version information of the current Windows OS
// from the RtlGetVersion API.
func getVersionInfo() *_OSVERSIONINFOEXW {
info := _OSVERSIONINFOEXW{}
info.osVersionInfoSize = uint32(unsafe.Sizeof(info))
rtlGetVersion(&info)
return &info
}
// Version retrieves the major, minor, and build version numbers
// of the current Windows OS from the RtlGetVersion API.
func Version() (major, minor, build uint32) {
info := getVersionInfo()
return info.majorVersion, info.minorVersion, info.buildNumber
}
// SupportUnlimitedTransmitFile indicates whether the current
// Windows version's TransmitFile function imposes any
// concurrent operation limits.
// Workstation and client versions of Windows limit the number
// of concurrent TransmitFile operations allowed on the system
// to a maximum of two. Please see:
// https://learn.microsoft.com/en-us/windows/win32/api/mswsock/nf-mswsock-transmitfile
// https://golang.org/issue/73746
var SupportUnlimitedTransmitFile = sync.OnceValue(func() bool {
info := getVersionInfo()
return info.productType != VER_NT_WORKSTATION
})
var (
supportTCPKeepAliveIdle bool
supportTCPKeepAliveInterval bool
supportTCPKeepAliveCount bool
)
var initTCPKeepAlive = sync.OnceFunc(func() {
s, err := WSASocket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP, nil, 0, WSA_FLAG_NO_HANDLE_INHERIT)
if err != nil {
// Fallback to checking the Windows version.
major, _, build := Version()
supportTCPKeepAliveIdle = major >= 10 && build >= 16299
supportTCPKeepAliveInterval = major >= 10 && build >= 16299
supportTCPKeepAliveCount = major >= 10 && build >= 15063
return
}
defer syscall.Closesocket(s)
var optSupported = func(opt int) bool {
err := syscall.SetsockoptInt(s, syscall.IPPROTO_TCP, opt, 1)
return !errors.Is(err, syscall.WSAENOPROTOOPT)
}
supportTCPKeepAliveIdle = optSupported(TCP_KEEPIDLE)
supportTCPKeepAliveInterval = optSupported(TCP_KEEPINTVL)
supportTCPKeepAliveCount = optSupported(TCP_KEEPCNT)
})
// SupportTCPKeepAliveIdle indicates whether TCP_KEEPIDLE is supported.
// The minimal requirement is Windows 10.0.16299.
func SupportTCPKeepAliveIdle() bool {
initTCPKeepAlive()
return supportTCPKeepAliveIdle
}
// SupportTCPKeepAliveInterval indicates whether TCP_KEEPINTVL is supported.
// The minimal requirement is Windows 10.0.16299.
func SupportTCPKeepAliveInterval() bool {
initTCPKeepAlive()
return supportTCPKeepAliveInterval
}
// SupportTCPKeepAliveCount indicates whether TCP_KEEPCNT is supported.
// supports TCP_KEEPCNT.
// The minimal requirement is Windows 10.0.15063.
func SupportTCPKeepAliveCount() bool {
initTCPKeepAlive()
return supportTCPKeepAliveCount
}
// SupportTCPInitialRTONoSYNRetransmissions indicates whether the current
// Windows version supports the TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS.
// The minimal requirement is Windows 10.0.16299.
var SupportTCPInitialRTONoSYNRetransmissions = sync.OnceValue(func() bool {
major, _, build := Version()
return major >= 10 && build >= 16299
})
// SupportUnixSocket indicates whether the current Windows version supports
// Unix Domain Sockets.
// The minimal requirement is Windows 10.0.17063.
var SupportUnixSocket = sync.OnceValue(func() bool {
var size uint32
// First call to get the required buffer size in bytes.
// Ignore the error, it will always fail.
_, _ = syscall.WSAEnumProtocols(nil, nil, &size)
n := int32(size) / int32(unsafe.Sizeof(syscall.WSAProtocolInfo{}))
// Second call to get the actual protocols.
buf := make([]syscall.WSAProtocolInfo, n)
n, err := syscall.WSAEnumProtocols(nil, &buf[0], &size)
if err != nil {
return false
}
for i := int32(0); i < n; i++ {
if buf[i].AddressFamily == syscall.AF_UNIX {
return true
}
}
return false
})