]> Cypherpunks repositories - gostls13.git/commitdiff
net,internal/syscall/windows: prove that keep alive options exists
authorqmuntal <quimmuntal@gmail.com>
Fri, 8 Mar 2024 14:40:34 +0000 (15:40 +0100)
committerQuim Muntal <quimmuntal@gmail.com>
Thu, 21 Mar 2024 11:49:35 +0000 (11:49 +0000)
The net package currently uses windows.SupportFullTCPKeepAlive to
know if TCP_KEEPIDLE, TCP_KEEPINTVL, and TCP_KEEPCNT are available.
This function is a wrapper over the undocumented RtlGetNtVersionNumbers
API, which tests if the Windows version is at least 10.0.16299. This
approach artificially limits the use of TCP_KEEPCNT, which is
available since Windows 10.0.15063. It also uses an undocumented API,
which is not something we want to rely on.

This CL removes windows.SupportFullTCPKeepAlive in favor of dedicated
proves for each option which are not based on the Windows version.

While here, remove some assertions in setKeepAliveCount. It is better
to let the system decide if the value is valid or not.

Updates #65817.

Cq-Include-Trybots: luci.golang.try:gotip-windows-arm64
Change-Id: I0fe70d46c8675eab06c0e4628cf68571b6e50b80
Reviewed-on: https://go-review.googlesource.com/c/go/+/570077
Reviewed-by: Than McIntosh <thanm@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: David Chase <drchase@google.com>
Reviewed-by: Alex Brainman <alex.brainman@gmail.com>
src/internal/syscall/windows/version_windows.go
src/net/tcpconn_keepalive_conf_windows_test.go
src/net/tcpsock_windows.go
src/net/tcpsockopt_windows.go

index 6ff9d523dfd42168c26d127043eddad6c4e15566..6da2d585492b94fae202728b8fc43f600c13917b 100644 (file)
@@ -5,6 +5,7 @@
 package windows
 
 import (
+       "errors"
        "sync"
        "syscall"
        "unsafe"
@@ -23,14 +24,54 @@ func version() (major, minor, build uint32) {
 //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.
index 72ebdc8567ff2cd6f12f798d636ab36bec7522ea..20e9f9267c044444f801a4eb1708f3f0e77e9b5c 100644 (file)
@@ -25,7 +25,7 @@ func maybeSkipKeepAliveTest(t *testing.T) {
        //      doesn't provide any ways to retrieve the current TCP keep-alive settings, therefore
        //      we're not able to run the test suite similar to Unix-like OS's on Windows.
        //  Try to find another proper approach to test the keep-alive settings on old Windows.
-       if !windows.SupportFullTCPKeepAlive() {
+       if !windows.SupportTCPKeepAliveIdle() || !windows.SupportTCPKeepAliveInterval() || !windows.SupportTCPKeepAliveCount() {
                t.Skip("skipping on windows")
        }
 }
index 2a4429579b74d10c4ccdde9c55c1618ef9c3a100..70e8ea23e5b7fc430a67f0d11bbec22fbdae3eee 100644 (file)
@@ -18,7 +18,7 @@ func (c *TCPConn) SetKeepAliveConfig(config KeepAliveConfig) error {
        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}
                }
index d15e0a7c289f4ad2addade2aea9fa898c8a9277e..f635d47999a47d8a1830a0664ef8d936fce92f3b 100644 (file)
@@ -21,7 +21,7 @@ const (
 )
 
 func setKeepAliveIdle(fd *netFD, d time.Duration) error {
-       if !windows.SupportFullTCPKeepAlive() {
+       if !windows.SupportTCPKeepAliveIdle() {
                return setKeepAliveIdleAndInterval(fd, d, -1)
        }
 
@@ -38,7 +38,7 @@ func setKeepAliveIdle(fd *netFD, d time.Duration) error {
 }
 
 func setKeepAliveInterval(fd *netFD, d time.Duration) error {
-       if !windows.SupportFullTCPKeepAlive() {
+       if !windows.SupportTCPKeepAliveInterval() {
                return setKeepAliveIdleAndInterval(fd, -1, d)
        }
 
@@ -61,15 +61,6 @@ func setKeepAliveCount(fd *netFD, n int) error {
                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)