var streamCtrl; var streamStatus; var streamLen; var streamData; function registerSocketBuffer(shared){ streamCtrl = new Int32Array(shared, 0, 1); streamStatus = new Int32Array(shared, 4, 1); streamLen = new Int32Array(shared, 8, 1); streamData = new Uint8Array(shared, 12); } var imagename; function serveIfInitMsg(msg) { const req_ = msg.data; if (typeof req_ == "object"){ if (req_.type == "init") { if (req_.buf) var shared = req_.buf; registerSocketBuffer(shared); if (req_.imagename) imagename = req_.imagename; return true; } } return false; } function getImagename() { return imagename; } const errStatus = { val: 0, }; function sockAccept(){ streamCtrl[0] = 0; postMessage({type: "accept"}); Atomics.wait(streamCtrl, 0, 0); return streamData[0] == 1; } function sockSend(data){ streamCtrl[0] = 0; postMessage({type: "send", buf: data}); Atomics.wait(streamCtrl, 0, 0); if (streamStatus[0] < 0) { errStatus.val = streamStatus[0] return errStatus; } } function sockRecv(len){ streamCtrl[0] = 0; postMessage({type: "recv", len: len}); Atomics.wait(streamCtrl, 0, 0); if (streamStatus[0] < 0) { errStatus.val = streamStatus[0] return errStatus; } let ddlen = streamLen[0]; var res = streamData.slice(0, ddlen); return res; } function sockWaitForReadable(timeout){ streamCtrl[0] = 0; postMessage({type: "recv-is-readable", timeout: timeout}); Atomics.wait(streamCtrl, 0, 0); if (streamStatus[0] < 0) { errStatus.val = streamStatus[0] return errStatus; } return streamData[0] == 1; } function sendCert(data){ streamCtrl[0] = 0; postMessage({type: "send_cert", buf: data}); Atomics.wait(streamCtrl, 0, 0); if (streamStatus[0] < 0) { errStatus.val = streamStatus[0] return errStatus; } } function recvCert(){ var buf = new Uint8Array(0); return new Promise((resolve, reject) => { function getCert(){ streamCtrl[0] = 0; postMessage({type: "recv_cert"}); Atomics.wait(streamCtrl, 0, 0); if (streamStatus[0] < 0) { setTimeout(getCert, 100); return; } var ddlen = streamLen[0]; buf = appendData(buf, streamData.slice(0, ddlen)); if (streamStatus[0] == 0) { resolve(buf); // EOF } else { setTimeout(getCert, 0); return; } } getCert(); }); } function appendData(data1, data2) { buf2 = new Uint8Array(data1.byteLength + data2.byteLength); buf2.set(new Uint8Array(data1), 0); buf2.set(new Uint8Array(data2), data1.byteLength); return buf2; } function getCertDir(cert) { var certDir = new PreopenDirectory("/.wasmenv", { "proxy.crt": new File(cert) }); var _path_open = certDir.path_open; certDir.path_open = (e, r, s, n, a, d) => { var ret = _path_open.apply(certDir, [e, r, s, n, a, d]); if (ret.fd_obj != null) { var o = ret.fd_obj; ret.fd_obj.fd_pread = (view8, iovs, offset) => { var old_offset = o.file_pos; var r = o.fd_seek(offset, WHENCE_SET); if (r.ret != 0) { return { ret: -1, nread: 0 }; } var read_ret = o.fd_read(view8, iovs); r = o.fd_seek(old_offset, WHENCE_SET); if (r.ret != 0) { return { ret: -1, nread: 0 }; } return read_ret; } } return ret; } certDir.dir.contents["."] = certDir.dir; return certDir; } function wasiHackSocket(wasi, listenfd, connfd) { // definition from wasi-libc https://github.com/WebAssembly/wasi-libc/blob/wasi-sdk-19/expected/wasm32-wasi/predefined-macros.txt const ERRNO_INVAL = 28; const ERRNO_AGAIN= 6; var connfdUsed = false; var connbuf = new Uint8Array(0); var _fd_close = wasi.wasiImport.fd_close; wasi.wasiImport.fd_close = (fd) => { if (fd == connfd) { connfdUsed = false; return 0; } return _fd_close.apply(wasi.wasiImport, [fd]); } var _fd_read = wasi.wasiImport.fd_read; wasi.wasiImport.fd_read = (fd, iovs_ptr, iovs_len, nread_ptr) => { if (fd == connfd) { return wasi.wasiImport.sock_recv(fd, iovs_ptr, iovs_len, 0, nread_ptr, 0); } return _fd_read.apply(wasi.wasiImport, [fd, iovs_ptr, iovs_len, nread_ptr]); } var _fd_write = wasi.wasiImport.fd_write; wasi.wasiImport.fd_write = (fd, iovs_ptr, iovs_len, nwritten_ptr) => { if (fd == connfd) { return wasi.wasiImport.sock_send(fd, iovs_ptr, iovs_len, 0, nwritten_ptr); } return _fd_write.apply(wasi.wasiImport, [fd, iovs_ptr, iovs_len, nwritten_ptr]); } var _fd_fdstat_get = wasi.wasiImport.fd_fdstat_get; wasi.wasiImport.fd_fdstat_get = (fd, fdstat_ptr) => { if ((fd == listenfd) || (fd == connfd) && connfdUsed){ let buffer = new DataView(wasi.inst.exports.memory.buffer); // https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-fdstat-struct buffer.setUint8(fdstat_ptr, 6); // filetype = 6 (socket_stream) buffer.setUint8(fdstat_ptr + 1, 2); // fdflags = 2 (nonblock) return 0; } return _fd_fdstat_get.apply(wasi.wasiImport, [fd, fdstat_ptr]); } var _fd_prestat_get = wasi.wasiImport.fd_prestat_get; wasi.wasiImport.fd_prestat_get = (fd, prestat_ptr) => { if ((fd == listenfd) || (fd == connfd)){ // reserve socket-related fds let buffer = new DataView(wasi.inst.exports.memory.buffer); buffer.setUint8(prestat_ptr, 1); return 0; } return _fd_prestat_get.apply(wasi.wasiImport, [fd, prestat_ptr]); } wasi.wasiImport.sock_accept = (fd, flags, fd_ptr) => { if (fd != listenfd) { console.log("sock_accept: unknown fd " + fd); return ERRNO_INVAL; } if (connfdUsed) { console.log("sock_accept: multi-connection is unsupported"); return ERRNO_INVAL; } if (!sockAccept()) { return ERRNO_AGAIN; } connfdUsed = true; var buffer = new DataView(wasi.inst.exports.memory.buffer); buffer.setUint32(fd_ptr, connfd, true); return 0; } wasi.wasiImport.sock_send = (fd, iovs_ptr, iovs_len, si_flags/*not defined*/, nwritten_ptr) => { if (fd != connfd) { console.log("sock_send: unknown fd " + fd); return ERRNO_INVAL; } var buffer = new DataView(wasi.inst.exports.memory.buffer); var buffer8 = new Uint8Array(wasi.inst.exports.memory.buffer); var iovecs = Ciovec.read_bytes_array(buffer, iovs_ptr, iovs_len); var wtotal = 0 for (i = 0; i < iovecs.length; i++) { var iovec = iovecs[i]; var buf = buffer8.slice(iovec.buf, iovec.buf + iovec.buf_len); if (buf.length == 0) { continue; } var ret = sockSend(buf.buffer.slice(0, iovec.buf_len)); if (ret == errStatus) { return ERRNO_INVAL; } wtotal += buf.length; } buffer.setUint32(nwritten_ptr, wtotal, true); return 0; } wasi.wasiImport.sock_recv = (fd, iovs_ptr, iovs_len, ri_flags, nread_ptr, ro_flags_ptr) => { if (ri_flags != 0) { console.log("ri_flags are unsupported"); // TODO } if (fd != connfd) { console.log("sock_recv: unknown fd " + fd); return ERRNO_INVAL; } var sockreadable = sockWaitForReadable(); if (sockreadable == errStatus) { return ERRNO_INVAL; } else if (sockreadable == false) { return ERRNO_AGAIN; } var buffer = new DataView(wasi.inst.exports.memory.buffer); var buffer8 = new Uint8Array(wasi.inst.exports.memory.buffer); var iovecs = Iovec.read_bytes_array(buffer, iovs_ptr, iovs_len); var nread = 0; for (i = 0; i < iovecs.length; i++) { var iovec = iovecs[i]; if (iovec.buf_len == 0) { continue; } var data = sockRecv(iovec.buf_len); if (data == errStatus) { return ERRNO_INVAL; } buffer8.set(data, iovec.buf); nread += data.length; } buffer.setUint32(nread_ptr, nread, true); // TODO: support ro_flags_ptr return 0; } wasi.wasiImport.sock_shutdown = (fd, sdflags) => { if (fd == connfd) { connfdUsed = false; } return 0; } }