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  }