os: remove NewFile socket detection on Windows

NewFile was recently updated (in CL 668195) to detect whether the
handle is a socket or not. This special case is not really necessary,
given that socket handles can be used as if they were normal file
handles on all functions supported by os.File (see https://learn.microsoft.com/en-us/windows/win32/winsock/socket-handles-2).

Not only is not necessary, but is can also be problematic, as there is
no way to reliably detect whether a handle is a socket or not. For
example, the test failure reported in #73630 is caused by a named pipe
wrongly detected as a socket.

This aligns with the Unix NewFile behavior of returning an os.File that
identifies itself as a file handle even if it is a socket. This makes
os.File.Close to always return os.ErrClosed in case of multiple calls
rather than sometimes returning "use of closed network connection".

Updates #10350.
Fixes #73630.

Change-Id: Ia8329783d5c8ef6dac34ef69ed1ce9d2a9862e11
Reviewed-on: https://go-review.googlesource.com/c/go/+/671455
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
Reviewed-by: Damien Neil <dneil@google.com>
This commit is contained in:
qmuntal 2025-05-09 16:55:00 +02:00 committed by Quim Muntal
parent 176a2154aa
commit 41bd52b3fa

View file

@ -82,49 +82,14 @@ func newConsoleFile(h syscall.Handle, name string) *File {
return newFile(h, name, "console", false) return newFile(h, name, "console", false)
} }
var wsaLoaded atomic.Bool
// isWSALoaded returns true if the ws2_32.dll module is loaded.
func isWSALoaded() bool {
// ws2_32.dll may be delay loaded, we can only short-circuit
// if we know it is loaded.
if wsaLoaded.Load() {
return true
}
var ws2_32_dll = [...]uint16{'w', 's', '2', '_', '3', '2', '.', 'd', 'l', 'l', 0}
_, err := windows.GetModuleHandle(unsafe.SliceData(ws2_32_dll[:]))
wsaLoaded.Store(err == nil)
return err == nil
}
// newFileFromNewFile is called by [NewFile]. // newFileFromNewFile is called by [NewFile].
func newFileFromNewFile(fd uintptr, name string) *File { func newFileFromNewFile(fd uintptr, name string) *File {
h := syscall.Handle(fd) h := syscall.Handle(fd)
if h == syscall.InvalidHandle { if h == syscall.InvalidHandle {
return nil return nil
} }
kind := "file"
var sotype int
if t, err := syscall.GetFileType(h); err == nil && t == syscall.FILE_TYPE_PIPE {
kind = "pipe"
// Windows reports sockets as FILE_TYPE_PIPE.
// We need to call getsockopt and check the socket type to distinguish between sockets and pipes.
// If the call fails, we assume it's a pipe.
// Avoid calling getsockopt if the WSA module is not loaded, it is a heavy dependency
// and sockets can only be created using that module.
if isWSALoaded() {
if sotype, err = syscall.GetsockoptInt(h, syscall.SOL_SOCKET, windows.SO_TYPE); err == nil {
kind = "net"
}
}
}
nonBlocking, _ := windows.IsNonblock(syscall.Handle(fd)) nonBlocking, _ := windows.IsNonblock(syscall.Handle(fd))
f := newFile(h, name, kind, nonBlocking) return newFile(h, name, "file", nonBlocking)
if kind == "net" {
f.pfd.IsStream = sotype == syscall.SOCK_STREAM
f.pfd.ZeroReadIsEOF = sotype != syscall.SOCK_DGRAM && sotype != syscall.SOCK_RAW
}
return f
} }
func epipecheck(file *File, e error) { func epipecheck(file *File, e error) {