github.com/tetratelabs/wazero@v1.2.1/internal/sysfs/sock_windows.go (about) 1 //go:build windows 2 3 package sysfs 4 5 import ( 6 "net" 7 "syscall" 8 "unsafe" 9 10 "github.com/tetratelabs/wazero/internal/platform" 11 socketapi "github.com/tetratelabs/wazero/internal/sock" 12 ) 13 14 // MSG_PEEK is the flag PEEK for syscall.Recvfrom on Windows. 15 // This constant is not exported on this platform. 16 const MSG_PEEK = 0x2 17 18 var ( 19 // modws2_32 is WinSock. 20 modws2_32 = syscall.NewLazyDLL("ws2_32.dll") 21 // procrecvfrom exposes recvfrom from WinSock. 22 procrecvfrom = modws2_32.NewProc("recvfrom") 23 ) 24 25 // recvfrom exposes the underlying syscall in Windows. 26 // 27 // Note: since we are only using this to expose MSG_PEEK, 28 // we do not need really need all the parameters that are actually 29 // allowed in WinSock. 30 // We ignore `from *sockaddr` and `fromlen *int`. 31 func recvfrom(s syscall.Handle, buf []byte, flags int32) (n int, errno syscall.Errno) { 32 var _p0 *byte 33 if len(buf) > 0 { 34 _p0 = &buf[0] 35 } 36 r0, _, e1 := syscall.SyscallN( 37 procrecvfrom.Addr(), 38 uintptr(s), 39 uintptr(unsafe.Pointer(_p0)), 40 uintptr(len(buf)), 41 uintptr(flags), 42 0, // from *sockaddr (optional) 43 0) // fromlen *int (optional) 44 return int(r0), e1 45 } 46 47 // newTCPListenerFile is a constructor for a socketapi.TCPSock. 48 // 49 // Note: currently the Windows implementation of socketapi.TCPSock 50 // returns a winTcpListenerFile, which is a specialized TCPSock 51 // that delegates to a .net.TCPListener. 52 // The current strategy is to delegate most behavior to the Go 53 // standard library, instead of invoke syscalls/Win32 APIs 54 // because they are sensibly different from Unix's. 55 func newTCPListenerFile(tl *net.TCPListener) socketapi.TCPSock { 56 return &winTcpListenerFile{tl: tl} 57 } 58 59 var _ socketapi.TCPSock = (*winTcpListenerFile)(nil) 60 61 type winTcpListenerFile struct { 62 baseSockFile 63 64 tl *net.TCPListener 65 } 66 67 // Accept implements the same method as documented on socketapi.TCPSock 68 func (f *winTcpListenerFile) Accept() (socketapi.TCPConn, syscall.Errno) { 69 conn, err := f.tl.Accept() 70 if err != nil { 71 return nil, platform.UnwrapOSError(err) 72 } 73 return &winTcpConnFile{tc: conn.(*net.TCPConn)}, 0 74 } 75 76 // SetNonblock implements the same method as documented on fsapi.File 77 func (f *winTcpListenerFile) SetNonblock(enabled bool) syscall.Errno { 78 return 0 // setNonblock() is a no-op on Windows 79 } 80 81 // Close implements the same method as documented on fsapi.File 82 func (f *winTcpListenerFile) Close() syscall.Errno { 83 return platform.UnwrapOSError(f.tl.Close()) 84 } 85 86 // Addr is exposed for testing. 87 func (f *winTcpListenerFile) Addr() *net.TCPAddr { 88 return f.tl.Addr().(*net.TCPAddr) 89 } 90 91 var _ socketapi.TCPConn = (*winTcpConnFile)(nil) 92 93 type winTcpConnFile struct { 94 baseSockFile 95 96 tc *net.TCPConn 97 98 // closed is true when closed was called. This ensures proper syscall.EBADF 99 closed bool 100 } 101 102 func newTcpConn(tc *net.TCPConn) socketapi.TCPConn { 103 return &winTcpConnFile{tc: tc} 104 } 105 106 // SetNonblock implements the same method as documented on fsapi.File 107 func (f *winTcpConnFile) SetNonblock(enabled bool) (errno syscall.Errno) { 108 syscallConn, err := f.tc.SyscallConn() 109 if err != nil { 110 return platform.UnwrapOSError(err) 111 } 112 113 // Prioritize the error from setNonblock over Control 114 if controlErr := syscallConn.Control(func(fd uintptr) { 115 errno = platform.UnwrapOSError(setNonblock(fd, enabled)) 116 }); errno == 0 { 117 errno = platform.UnwrapOSError(controlErr) 118 } 119 return 120 } 121 122 // Read implements the same method as documented on fsapi.File 123 func (f *winTcpConnFile) Read(buf []byte) (n int, errno syscall.Errno) { 124 if n, errno = read(f.tc, buf); errno != 0 { 125 // Defer validation overhead until we've already had an error. 126 errno = fileError(f, f.closed, errno) 127 } 128 return 129 } 130 131 // Write implements the same method as documented on fsapi.File 132 func (f *winTcpConnFile) Write(buf []byte) (n int, errno syscall.Errno) { 133 if n, errno = write(f.tc, buf); errno != 0 { 134 // Defer validation overhead until we've already had an error. 135 errno = fileError(f, f.closed, errno) 136 } 137 return 138 } 139 140 // Recvfrom implements the same method as documented on socketapi.TCPConn 141 func (f *winTcpConnFile) Recvfrom(p []byte, flags int) (n int, errno syscall.Errno) { 142 if flags != MSG_PEEK { 143 errno = syscall.EINVAL 144 return 145 } 146 conn := f.tc 147 syscallConn, err := conn.SyscallConn() 148 if err != nil { 149 errno = platform.UnwrapOSError(err) 150 return 151 } 152 153 // Prioritize the error from recvfrom over Control 154 if controlErr := syscallConn.Control(func(fd uintptr) { 155 var recvfromErr error 156 n, recvfromErr = recvfrom(syscall.Handle(fd), p, MSG_PEEK) 157 errno = platform.UnwrapOSError(recvfromErr) 158 }); errno == 0 { 159 errno = platform.UnwrapOSError(controlErr) 160 } 161 return 162 } 163 164 // Shutdown implements the same method as documented on fsapi.Conn 165 func (f *winTcpConnFile) Shutdown(how int) syscall.Errno { 166 // FIXME: can userland shutdown listeners? 167 var err error 168 switch how { 169 case syscall.SHUT_RD: 170 err = f.tc.CloseRead() 171 case syscall.SHUT_WR: 172 err = f.tc.CloseWrite() 173 case syscall.SHUT_RDWR: 174 return f.close() 175 default: 176 return syscall.EINVAL 177 } 178 return platform.UnwrapOSError(err) 179 } 180 181 // Close implements the same method as documented on fsapi.File 182 func (f *winTcpConnFile) Close() syscall.Errno { 183 return f.close() 184 } 185 186 func (f *winTcpConnFile) close() syscall.Errno { 187 if f.closed { 188 return 0 189 } 190 f.closed = true 191 return f.Shutdown(syscall.SHUT_RDWR) 192 }