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 }