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 5package windows 6 7import ( 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 15type _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. 29func 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 36var ( 37 supportTCPKeepAliveIdle bool 38 supportTCPKeepAliveInterval bool 39 supportTCPKeepAliveCount bool 40) 41 42var 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. 64func 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. 71func 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. 79func 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. 87var 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. 95var 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