package windows
import (
+ "errors"
"sync"
"syscall"
"unsafe"
//go:noescape
func rtlGetNtVersionNumbers(majorVersion *uint32, minorVersion *uint32, buildNumber *uint32)
-// SupportFullTCPKeepAlive indicates whether the current Windows version
-// supports the full TCP keep-alive configurations.
-// The minimal requirement is Windows 10.0.16299.
-var SupportFullTCPKeepAlive = sync.OnceValue(func() bool {
- major, _, build := version()
- return major >= 10 && build >= 16299
+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)
})
+// SupportTCPKeepAliveInterval 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.
if err := setKeepAlive(c.fd, config.Enable); err != nil {
return &OpError{Op: "set", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
}
- if windows.SupportFullTCPKeepAlive() {
+ if windows.SupportTCPKeepAliveIdle() && windows.SupportTCPKeepAliveInterval() {
if err := setKeepAliveIdle(c.fd, config.Idle); err != nil {
return &OpError{Op: "set", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
}
)
func setKeepAliveIdle(fd *netFD, d time.Duration) error {
- if !windows.SupportFullTCPKeepAlive() {
+ if !windows.SupportTCPKeepAliveIdle() {
return setKeepAliveIdleAndInterval(fd, d, -1)
}
}
func setKeepAliveInterval(fd *netFD, d time.Duration) error {
- if !windows.SupportFullTCPKeepAlive() {
+ if !windows.SupportTCPKeepAliveInterval() {
return setKeepAliveIdleAndInterval(fd, -1, d)
}
return nil
}
- // This value is not capable to be changed on old versions of Windows.
- if !windows.SupportFullTCPKeepAlive() {
- return syscall.WSAENOPROTOOPT
- }
- // It is illegal to set TCP_KEEPCNT to a value greater than 255.
- if n > 255 {
- return syscall.EINVAL
- }
-
err := fd.pfd.SetsockoptInt(syscall.IPPROTO_TCP, windows.TCP_KEEPCNT, n)
runtime.KeepAlive(fd)
return os.NewSyscallError("setsockopt", err)