github.com/wasilibs/wazerox@v0.0.0-20240124024944-4923be63ab5f/imports/wasi_snapshot_preview1/sock.go (about) 1 package wasi_snapshot_preview1 2 3 import ( 4 "context" 5 6 "github.com/wasilibs/wazerox/api" 7 "github.com/wasilibs/wazerox/experimental/sys" 8 socketapi "github.com/wasilibs/wazerox/internal/sock" 9 "github.com/wasilibs/wazerox/internal/sysfs" 10 "github.com/wasilibs/wazerox/internal/wasip1" 11 "github.com/wasilibs/wazerox/internal/wasm" 12 ) 13 14 // sockAccept is the WASI function named SockAcceptName which accepts a new 15 // incoming connection. 16 // 17 // See: https://github.com/WebAssembly/WASI/blob/0ba0c5e2e37625ca5a6d3e4255a998dfaa3efc52/phases/snapshot/docs.md#sock_accept 18 // and https://github.com/WebAssembly/WASI/pull/458 19 var sockAccept = newHostFunc( 20 wasip1.SockAcceptName, 21 sockAcceptFn, 22 []wasm.ValueType{i32, i32, i32}, 23 "fd", "flags", "result.fd", 24 ) 25 26 func sockAcceptFn(_ context.Context, mod api.Module, params []uint64) (errno sys.Errno) { 27 mem := mod.Memory() 28 fsc := mod.(*wasm.ModuleInstance).Sys.FS() 29 30 fd := int32(params[0]) 31 flags := uint32(params[1]) 32 resultFd := uint32(params[2]) 33 nonblock := flags&uint32(wasip1.FD_NONBLOCK) != 0 34 35 var connFD int32 36 if connFD, errno = fsc.SockAccept(fd, nonblock); errno == 0 { 37 mem.WriteUint32Le(resultFd, uint32(connFD)) 38 } 39 return 40 } 41 42 // sockRecv is the WASI function named SockRecvName which receives a 43 // message from a socket. 44 // 45 // See: https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-sock_recvfd-fd-ri_data-iovec_array-ri_flags-riflags---errno-size-roflags 46 var sockRecv = newHostFunc( 47 wasip1.SockRecvName, 48 sockRecvFn, 49 []wasm.ValueType{i32, i32, i32, i32, i32, i32}, 50 "fd", "ri_data", "ri_data_len", "ri_flags", "result.ro_datalen", "result.ro_flags", 51 ) 52 53 func sockRecvFn(_ context.Context, mod api.Module, params []uint64) sys.Errno { 54 mem := mod.Memory() 55 fsc := mod.(*wasm.ModuleInstance).Sys.FS() 56 57 fd := int32(params[0]) 58 riData := uint32(params[1]) 59 riDataCount := uint32(params[2]) 60 riFlags := uint8(params[3]) 61 resultRoDatalen := uint32(params[4]) 62 resultRoFlags := uint32(params[5]) 63 64 var conn socketapi.TCPConn 65 if e, ok := fsc.LookupFile(fd); !ok { 66 return sys.EBADF // Not open 67 } else if conn, ok = e.File.(socketapi.TCPConn); !ok { 68 return sys.EBADF // Not a conn 69 } 70 71 if riFlags & ^(wasip1.RI_RECV_PEEK|wasip1.RI_RECV_WAITALL) != 0 { 72 return sys.ENOTSUP 73 } 74 75 if riFlags&wasip1.RI_RECV_PEEK != 0 { 76 // Each record in riData is of the form: 77 // type iovec struct { buf *uint8; bufLen uint32 } 78 // This means that the first `uint32` is a `buf *uint8`. 79 firstIovecBufAddr, ok := mem.ReadUint32Le(riData) 80 if !ok { 81 return sys.EINVAL 82 } 83 // Read bufLen 84 firstIovecBufLen, ok := mem.ReadUint32Le(riData + 4) 85 if !ok { 86 return sys.EINVAL 87 } 88 firstIovecBuf, ok := mem.Read(firstIovecBufAddr, firstIovecBufLen) 89 if !ok { 90 return sys.EINVAL 91 } 92 n, err := conn.Recvfrom(firstIovecBuf, sysfs.MSG_PEEK) 93 if err != 0 { 94 return err 95 } 96 mem.WriteUint32Le(resultRoDatalen, uint32(n)) 97 mem.WriteUint16Le(resultRoFlags, 0) 98 return 0 99 } 100 101 // If riFlags&wasip1.RECV_WAITALL != 0 then we should 102 // do a blocking operation until all data has been retrieved; 103 // otherwise we are able to return earlier. 104 // For simplicity, we currently wait all regardless the flag. 105 bufSize, errno := readv(mem, riData, riDataCount, conn.Read) 106 if errno != 0 { 107 return errno 108 } 109 mem.WriteUint32Le(resultRoDatalen, bufSize) 110 mem.WriteUint16Le(resultRoFlags, 0) 111 return 0 112 } 113 114 // sockSend is the WASI function named SockSendName which sends a message 115 // on a socket. 116 // 117 // See: https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-sock_sendfd-fd-si_data-ciovec_array-si_flags-siflags---errno-size 118 var sockSend = newHostFunc( 119 wasip1.SockSendName, 120 sockSendFn, 121 []wasm.ValueType{i32, i32, i32, i32, i32}, 122 "fd", "si_data", "si_data_len", "si_flags", "result.so_datalen", 123 ) 124 125 func sockSendFn(_ context.Context, mod api.Module, params []uint64) sys.Errno { 126 mem := mod.Memory() 127 fsc := mod.(*wasm.ModuleInstance).Sys.FS() 128 129 fd := int32(params[0]) 130 siData := uint32(params[1]) 131 siDataCount := uint32(params[2]) 132 siFlags := uint32(params[3]) 133 resultSoDatalen := uint32(params[4]) 134 135 if siFlags != 0 { 136 return sys.ENOTSUP 137 } 138 139 var conn socketapi.TCPConn 140 if e, ok := fsc.LookupFile(fd); !ok { 141 return sys.EBADF // Not open 142 } else if conn, ok = e.File.(socketapi.TCPConn); !ok { 143 return sys.EBADF // Not a conn 144 } 145 146 bufSize, errno := writev(mem, siData, siDataCount, conn.Write) 147 if errno != 0 { 148 return errno 149 } 150 mem.WriteUint32Le(resultSoDatalen, bufSize) 151 return 0 152 } 153 154 // sockShutdown is the WASI function named SockShutdownName which shuts 155 // down socket send and receive channels. 156 // 157 // See: https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-sock_shutdownfd-fd-how-sdflags---errno 158 var sockShutdown = newHostFunc(wasip1.SockShutdownName, sockShutdownFn, []wasm.ValueType{i32, i32}, "fd", "how") 159 160 func sockShutdownFn(_ context.Context, mod api.Module, params []uint64) sys.Errno { 161 fsc := mod.(*wasm.ModuleInstance).Sys.FS() 162 163 fd := int32(params[0]) 164 how := uint8(params[1]) 165 166 var conn socketapi.TCPConn 167 if e, ok := fsc.LookupFile(fd); !ok { 168 return sys.EBADF // Not open 169 } else if conn, ok = e.File.(socketapi.TCPConn); !ok { 170 return sys.EBADF // Not a conn 171 } 172 173 sysHow := 0 174 175 switch how { 176 case wasip1.SD_RD | wasip1.SD_WR: 177 sysHow = socketapi.SHUT_RD | socketapi.SHUT_WR 178 case wasip1.SD_RD: 179 sysHow = socketapi.SHUT_RD 180 case wasip1.SD_WR: 181 sysHow = socketapi.SHUT_WR 182 default: 183 return sys.EINVAL 184 } 185 186 // TODO: Map this instead of relying on syscall symbols. 187 return conn.Shutdown(sysHow) 188 }