github.com/wasilibs/wazerox@v0.0.0-20240124024944-4923be63ab5f/internal/sysfs/sock_unix.go (about)

     1  //go:build linux || darwin
     2  
     3  package sysfs
     4  
     5  import (
     6  	"net"
     7  	"syscall"
     8  
     9  	"github.com/wasilibs/wazerox/experimental/sys"
    10  	"github.com/wasilibs/wazerox/internal/fsapi"
    11  	socketapi "github.com/wasilibs/wazerox/internal/sock"
    12  )
    13  
    14  // MSG_PEEK is the constant syscall.MSG_PEEK
    15  const MSG_PEEK = syscall.MSG_PEEK
    16  
    17  // newTCPListenerFile is a constructor for a socketapi.TCPSock.
    18  //
    19  // Note: the implementation of socketapi.TCPSock goes straight
    20  // to the syscall layer, bypassing most of the Go library.
    21  // For an alternative approach, consider winTcpListenerFile
    22  // where most APIs are implemented with regular Go std-lib calls.
    23  func newTCPListenerFile(tl *net.TCPListener) socketapi.TCPSock {
    24  	conn, err := tl.File()
    25  	if err != nil {
    26  		panic(err)
    27  	}
    28  	fd := conn.Fd()
    29  	// We need to duplicate this file handle, or the lifecycle will be tied
    30  	// to the TCPListener. We rely on the TCPListener only to set up
    31  	// the connection correctly and parse/resolve the TCP Address
    32  	// (notice we actually rely on the listener in the Windows implementation).
    33  	sysfd, err := syscall.Dup(int(fd))
    34  	if err != nil {
    35  		panic(err)
    36  	}
    37  	return &tcpListenerFile{fd: uintptr(sysfd), addr: tl.Addr().(*net.TCPAddr)}
    38  }
    39  
    40  var _ socketapi.TCPSock = (*tcpListenerFile)(nil)
    41  
    42  type tcpListenerFile struct {
    43  	baseSockFile
    44  
    45  	fd       uintptr
    46  	addr     *net.TCPAddr
    47  	nonblock bool
    48  }
    49  
    50  // Accept implements the same method as documented on socketapi.TCPSock
    51  func (f *tcpListenerFile) Accept() (socketapi.TCPConn, sys.Errno) {
    52  	nfd, _, err := syscall.Accept(int(f.fd))
    53  	errno := sys.UnwrapOSError(err)
    54  	if errno != 0 {
    55  		return nil, errno
    56  	}
    57  	return &tcpConnFile{fd: uintptr(nfd)}, 0
    58  }
    59  
    60  // Close implements the same method as documented on sys.File
    61  func (f *tcpListenerFile) Close() sys.Errno {
    62  	return sys.UnwrapOSError(syscall.Close(int(f.fd)))
    63  }
    64  
    65  // Addr is exposed for testing.
    66  func (f *tcpListenerFile) Addr() *net.TCPAddr {
    67  	return f.addr
    68  }
    69  
    70  // SetNonblock implements the same method as documented on fsapi.File
    71  func (f *tcpListenerFile) SetNonblock(enabled bool) sys.Errno {
    72  	f.nonblock = enabled
    73  	return sys.UnwrapOSError(setNonblock(f.fd, enabled))
    74  }
    75  
    76  // IsNonblock implements the same method as documented on fsapi.File
    77  func (f *tcpListenerFile) IsNonblock() bool {
    78  	return f.nonblock
    79  }
    80  
    81  // Poll implements the same method as documented on fsapi.File
    82  func (f *tcpListenerFile) Poll(flag fsapi.Pflag, timeoutMillis int32) (ready bool, errno sys.Errno) {
    83  	return false, sys.ENOSYS
    84  }
    85  
    86  var _ socketapi.TCPConn = (*tcpConnFile)(nil)
    87  
    88  type tcpConnFile struct {
    89  	baseSockFile
    90  
    91  	fd       uintptr
    92  	nonblock bool
    93  
    94  	// closed is true when closed was called. This ensures proper sys.EBADF
    95  	closed bool
    96  }
    97  
    98  func newTcpConn(tc *net.TCPConn) socketapi.TCPConn {
    99  	f, err := tc.File()
   100  	if err != nil {
   101  		panic(err)
   102  	}
   103  	return &tcpConnFile{fd: f.Fd()}
   104  }
   105  
   106  // Read implements the same method as documented on sys.File
   107  func (f *tcpConnFile) Read(buf []byte) (n int, errno sys.Errno) {
   108  	n, err := syscall.Read(int(f.fd), buf)
   109  	if err != nil {
   110  		// Defer validation overhead until we've already had an error.
   111  		errno = sys.UnwrapOSError(err)
   112  		errno = fileError(f, f.closed, errno)
   113  	}
   114  	return n, errno
   115  }
   116  
   117  // Write implements the same method as documented on sys.File
   118  func (f *tcpConnFile) Write(buf []byte) (n int, errno sys.Errno) {
   119  	n, err := syscall.Write(int(f.fd), buf)
   120  	if err != nil {
   121  		// Defer validation overhead until we've already had an error.
   122  		errno = sys.UnwrapOSError(err)
   123  		errno = fileError(f, f.closed, errno)
   124  	}
   125  	return n, errno
   126  }
   127  
   128  // Recvfrom implements the same method as documented on socketapi.TCPConn
   129  func (f *tcpConnFile) Recvfrom(p []byte, flags int) (n int, errno sys.Errno) {
   130  	if flags != MSG_PEEK {
   131  		errno = sys.EINVAL
   132  		return
   133  	}
   134  	n, _, recvfromErr := syscall.Recvfrom(int(f.fd), p, MSG_PEEK)
   135  	errno = sys.UnwrapOSError(recvfromErr)
   136  	return n, errno
   137  }
   138  
   139  // Shutdown implements the same method as documented on sys.Conn
   140  func (f *tcpConnFile) Shutdown(how int) sys.Errno {
   141  	var err error
   142  	switch how {
   143  	case syscall.SHUT_RD, syscall.SHUT_WR:
   144  		err = syscall.Shutdown(int(f.fd), how)
   145  	case syscall.SHUT_RDWR:
   146  		return f.close()
   147  	default:
   148  		return sys.EINVAL
   149  	}
   150  	return sys.UnwrapOSError(err)
   151  }
   152  
   153  // Close implements the same method as documented on sys.File
   154  func (f *tcpConnFile) Close() sys.Errno {
   155  	return f.close()
   156  }
   157  
   158  func (f *tcpConnFile) close() sys.Errno {
   159  	if f.closed {
   160  		return 0
   161  	}
   162  	f.closed = true
   163  	return sys.UnwrapOSError(syscall.Shutdown(int(f.fd), syscall.SHUT_RDWR))
   164  }
   165  
   166  // SetNonblock implements the same method as documented on fsapi.File
   167  func (f *tcpConnFile) SetNonblock(enabled bool) (errno sys.Errno) {
   168  	f.nonblock = enabled
   169  	return sys.UnwrapOSError(setNonblock(f.fd, enabled))
   170  }
   171  
   172  // IsNonblock implements the same method as documented on fsapi.File
   173  func (f *tcpConnFile) IsNonblock() bool {
   174  	return f.nonblock
   175  }
   176  
   177  // Poll implements the same method as documented on fsapi.File
   178  func (f *tcpConnFile) Poll(flag fsapi.Pflag, timeoutMillis int32) (ready bool, errno sys.Errno) {
   179  	return false, sys.ENOSYS
   180  }