// 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 })