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