]> Cypherpunks repositories - gostls13.git/commitdiff
net: avoid using Windows' TransmitFile on non-server machines
authorShibi J M <shibisjm@gmail.com>
Tue, 20 May 2025 03:59:15 +0000 (03:59 +0000)
committerGopher Robot <gobot@golang.org>
Tue, 20 May 2025 18:06:59 +0000 (11:06 -0700)
Windows API's TransmitFile function is limited to two concurrent
operations on workstation and client versions of Windows. This change
modifies the net.sendFile function to perform no work in such cases
so that TransmitFile is avoided.

Fixes #73746

Change-Id: Iba70d5d2758bf986e80c78254c8e9e10b39bb368
GitHub-Last-Rev: 315ddc0cd8034f52632dc31baf35057a8bad9bcd
GitHub-Pull-Request: golang/go#73758
Reviewed-on: https://go-review.googlesource.com/c/go/+/673855
Reviewed-by: Alex Brainman <alex.brainman@gmail.com>
Auto-Submit: Damien Neil <dneil@google.com>
Reviewed-by: Quim Muntal <quimmuntal@gmail.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: David Chase <drchase@google.com>
Reviewed-by: Damien Neil <dneil@google.com>
src/internal/syscall/windows/types_windows.go
src/internal/syscall/windows/version_windows.go
src/internal/syscall/windows/zsyscall_windows.go
src/net/sendfile.go
src/net/sendfile_nonwindows.go [new file with mode: 0644]
src/net/sendfile_stub.go
src/net/sendfile_test.go
src/net/sendfile_windows.go [new file with mode: 0644]

index 9f8f61f6d918b5687ea1b544db78e9f7b78d1fd6..93664b4b7da8ca5f2ca963f305ddd223142f0c3a 100644 (file)
@@ -256,3 +256,7 @@ type FILE_COMPLETION_INFORMATION struct {
        Port syscall.Handle
        Key  uintptr
 }
+
+// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-osversioninfoexa
+// https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_osversioninfoexw
+const VER_NT_WORKSTATION = 0x0000001
index cb5f6ba6cd30e4a18d53cb909852764b7fb9f81d..5edf7a01e270d498cb9432908d6169d96adf78db 100644 (file)
@@ -11,28 +11,53 @@ import (
        "unsafe"
 )
 
-// https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_osversioninfow
-type _OSVERSIONINFOW struct {
+// 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 *_OSVERSIONINFOW) = ntdll.RtlGetVersion
+//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 := _OSVERSIONINFOW{}
-       info.osVersionInfoSize = uint32(unsafe.Sizeof(info))
-       rtlGetVersion(&info)
+       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
index 8dcb377c3e7e8d9f7ad7652df43617761db8f204..90cf0b92a49bc44b8296929d2248f66dac206f40 100644 (file)
@@ -539,7 +539,7 @@ func NtSetInformationFile(handle syscall.Handle, iosb *IO_STATUS_BLOCK, inBuffer
        return
 }
 
-func rtlGetVersion(info *_OSVERSIONINFOW) {
+func rtlGetVersion(info *_OSVERSIONINFOEXW) {
        syscall.Syscall(procRtlGetVersion.Addr(), 1, uintptr(unsafe.Pointer(info)), 0, 0)
        return
 }
index 0a41241561bada566420bc53eb480a09b4e2b04a..0e0fcc40fff4a3237a7b733494b8300308d60d1f 100644 (file)
@@ -12,8 +12,6 @@ import (
        "syscall"
 )
 
-const supportsSendfile = true
-
 // sendFile copies the contents of r to c using the sendfile
 // system call to minimize copies.
 //
@@ -22,6 +20,9 @@ const supportsSendfile = true
 //
 // if handled == false, sendFile performed no work.
 func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) {
+       if !supportsSendfile() {
+               return 0, nil, false
+       }
        var remain int64 = 0 // 0 writes the entire file
        lr, ok := r.(*io.LimitedReader)
        if ok {
diff --git a/src/net/sendfile_nonwindows.go b/src/net/sendfile_nonwindows.go
new file mode 100644 (file)
index 0000000..2106d37
--- /dev/null
@@ -0,0 +1,12 @@
+// Copyright 2025 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.
+
+//go:build linux || (darwin && !ios) || dragonfly || freebsd || solaris
+
+package net
+
+// Always true except for workstation and client versions of Windows
+func supportsSendfile() bool {
+       return true
+}
index 7f31cc63e1ed6a909a98270f4611a257e662cffd..17d8d5448f77d9beaaa5bdcfb02c317152b95a6d 100644 (file)
@@ -8,7 +8,9 @@ package net
 
 import "io"
 
-const supportsSendfile = false
+func supportsSendfile() bool {
+       return false
+}
 
 func sendFile(c *netFD, r io.Reader) (n int64, err error, handled bool) {
        return 0, nil, false
index b5039ff1d18774730216f90e445019d8ddbae3a3..437d181508045e0be796375616d2dfda3bc91403 100644 (file)
@@ -31,11 +31,11 @@ const (
 // expectSendfile runs f, and verifies that internal/poll.SendFile successfully handles
 // a write to wantConn during f's execution.
 //
-// On platforms where supportsSendfile is false, expectSendfile runs f but does not
+// On platforms where supportsSendfile() is false, expectSendfile runs f but does not
 // expect a call to SendFile.
 func expectSendfile(t *testing.T, wantConn Conn, f func()) {
        t.Helper()
-       if !supportsSendfile {
+       if !supportsSendfile() {
                f()
                return
        }
diff --git a/src/net/sendfile_windows.go b/src/net/sendfile_windows.go
new file mode 100644 (file)
index 0000000..44ddb42
--- /dev/null
@@ -0,0 +1,16 @@
+// Copyright 2025 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 net
+
+import "internal/syscall/windows"
+
+// 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
+func supportsSendfile() bool {
+       return windows.SupportUnlimitedTransmitFile()
+}