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