-
Notifications
You must be signed in to change notification settings - Fork 17.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
net: implement wasip1 FileListener and FileConn
Implements net.FileListener and net.FileConn for wasip1. net.FileListener can be used with a pre-opened socket. If the WASM module knows the file descriptor, a listener can be constructed with: l, err := net.FileListener(os.NewFile(fd, "")) If the WASM module does not know the file descriptor, but knows that at least one of the preopens is a socket, it can find the file descriptor and construct a listener like so: func findListener() (net.Listener, error) { // We start looking for pre-opened sockets at fd=3 because 0, 1, // and 2 are reserved for stdio. Pre-opened directories also // start at fd=3, so we skip fds that aren't sockets. Once we // reach EBADF we know there are no more pre-opens. for preopenFd := uintptr(3); ; preopenFd++ { l, err := net.FileListener(os.NewFile(preopenFd, "")) var se syscall.Errno switch errors.As(err, &se); se { case syscall.ENOTSOCK: continue case syscall.EBADF: err = nil } return l, err } } A similar strategy can be used with net.FileConn and pre-opened connection sockets. The wasmtime runtime supports pre-opening listener sockets: $ wasmtime --tcplisten 127.0.0.1:8080 module.wasm Change-Id: Iec6ae4ffa84b3753cce4f56a2817e150445db643 Reviewed-on: https://go-review.googlesource.com/c/go/+/493358 Reviewed-by: Matthew Dempsky <mdempsky@google.com> Run-TryBot: Ian Lance Taylor <iant@golang.org> Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org> TryBot-Bypass: Dmitri Shuralyov <dmitshur@google.com> Reviewed-by: Johan Brandhorst-Satzkorn <johan.brandhorst@gmail.com> Auto-Submit: Johan Brandhorst-Satzkorn <johan.brandhorst@gmail.com> Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
- Loading branch information
Showing
16 changed files
with
665 additions
and
176 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
// Copyright 2023 The Go Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
//go:build wasip1 | ||
|
||
package net | ||
|
||
import ( | ||
"internal/poll" | ||
"runtime" | ||
"syscall" | ||
"time" | ||
) | ||
|
||
const ( | ||
readSyscallName = "fd_read" | ||
writeSyscallName = "fd_write" | ||
) | ||
|
||
// Network file descriptor. | ||
type netFD struct { | ||
pfd poll.FD | ||
|
||
// immutable until Close | ||
family int | ||
sotype int | ||
isConnected bool // handshake completed or use of association with peer | ||
net string | ||
laddr Addr | ||
raddr Addr | ||
|
||
// The only networking available in WASI preview 1 is the ability to | ||
// sock_accept on an pre-opened socket, and then fd_read, fd_write, | ||
// fd_close, and sock_shutdown on the resulting connection. We | ||
// intercept applicable netFD calls on this instance, and then pass | ||
// the remainder of the netFD calls to fakeNetFD. | ||
*fakeNetFD | ||
} | ||
|
||
func newFD(sysfd int) (*netFD, error) { | ||
return newPollFD(poll.FD{ | ||
Sysfd: sysfd, | ||
IsStream: true, | ||
ZeroReadIsEOF: true, | ||
}) | ||
} | ||
|
||
func newPollFD(pfd poll.FD) (*netFD, error) { | ||
ret := &netFD{ | ||
pfd: pfd, | ||
net: "tcp", | ||
laddr: unknownAddr{}, | ||
raddr: unknownAddr{}, | ||
} | ||
return ret, nil | ||
} | ||
|
||
func (fd *netFD) init() error { | ||
return fd.pfd.Init(fd.net, true) | ||
} | ||
|
||
func (fd *netFD) name() string { | ||
return "unknown" | ||
} | ||
|
||
func (fd *netFD) accept() (netfd *netFD, err error) { | ||
if fd.fakeNetFD != nil { | ||
return fd.fakeNetFD.accept() | ||
} | ||
d, _, errcall, err := fd.pfd.Accept() | ||
if err != nil { | ||
if errcall != "" { | ||
err = wrapSyscallError(errcall, err) | ||
} | ||
return nil, err | ||
} | ||
if netfd, err = newFD(d); err != nil { | ||
poll.CloseFunc(d) | ||
return nil, err | ||
} | ||
if err = netfd.init(); err != nil { | ||
netfd.Close() | ||
return nil, err | ||
} | ||
return netfd, nil | ||
} | ||
|
||
func (fd *netFD) setAddr(laddr, raddr Addr) { | ||
fd.laddr = laddr | ||
fd.raddr = raddr | ||
runtime.SetFinalizer(fd, (*netFD).Close) | ||
} | ||
|
||
func (fd *netFD) Close() error { | ||
if fd.fakeNetFD != nil { | ||
return fd.fakeNetFD.Close() | ||
} | ||
runtime.SetFinalizer(fd, nil) | ||
return fd.pfd.Close() | ||
} | ||
|
||
func (fd *netFD) shutdown(how int) error { | ||
if fd.fakeNetFD != nil { | ||
return nil | ||
} | ||
err := fd.pfd.Shutdown(how) | ||
runtime.KeepAlive(fd) | ||
return wrapSyscallError("shutdown", err) | ||
} | ||
|
||
func (fd *netFD) closeRead() error { | ||
if fd.fakeNetFD != nil { | ||
return fd.fakeNetFD.closeRead() | ||
} | ||
return fd.shutdown(syscall.SHUT_RD) | ||
} | ||
|
||
func (fd *netFD) closeWrite() error { | ||
if fd.fakeNetFD != nil { | ||
return fd.fakeNetFD.closeWrite() | ||
} | ||
return fd.shutdown(syscall.SHUT_WR) | ||
} | ||
|
||
func (fd *netFD) Read(p []byte) (n int, err error) { | ||
if fd.fakeNetFD != nil { | ||
return fd.fakeNetFD.Read(p) | ||
} | ||
n, err = fd.pfd.Read(p) | ||
runtime.KeepAlive(fd) | ||
return n, wrapSyscallError(readSyscallName, err) | ||
} | ||
|
||
func (fd *netFD) Write(p []byte) (nn int, err error) { | ||
if fd.fakeNetFD != nil { | ||
return fd.fakeNetFD.Write(p) | ||
} | ||
nn, err = fd.pfd.Write(p) | ||
runtime.KeepAlive(fd) | ||
return nn, wrapSyscallError(writeSyscallName, err) | ||
} | ||
|
||
func (fd *netFD) SetDeadline(t time.Time) error { | ||
if fd.fakeNetFD != nil { | ||
return fd.fakeNetFD.SetDeadline(t) | ||
} | ||
return fd.pfd.SetDeadline(t) | ||
} | ||
|
||
func (fd *netFD) SetReadDeadline(t time.Time) error { | ||
if fd.fakeNetFD != nil { | ||
return fd.fakeNetFD.SetReadDeadline(t) | ||
} | ||
return fd.pfd.SetReadDeadline(t) | ||
} | ||
|
||
func (fd *netFD) SetWriteDeadline(t time.Time) error { | ||
if fd.fakeNetFD != nil { | ||
return fd.fakeNetFD.SetWriteDeadline(t) | ||
} | ||
return fd.pfd.SetWriteDeadline(t) | ||
} | ||
|
||
type unknownAddr struct{} | ||
|
||
func (unknownAddr) Network() string { return "unknown" } | ||
func (unknownAddr) String() string { return "unknown" } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
// Copyright 2023 The Go Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
//go:build wasip1 | ||
|
||
package net | ||
|
||
import ( | ||
"os" | ||
"syscall" | ||
_ "unsafe" // for go:linkname | ||
) | ||
|
||
func fileListener(f *os.File) (Listener, error) { | ||
fd, err := newFileFD(f) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return &TCPListener{fd: fd}, nil | ||
} | ||
|
||
func fileConn(f *os.File) (Conn, error) { | ||
fd, err := newFileFD(f) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return &TCPConn{conn{fd: fd}}, nil | ||
} | ||
|
||
func filePacketConn(f *os.File) (PacketConn, error) { return nil, syscall.ENOPROTOOPT } | ||
|
||
func newFileFD(f *os.File) (fd *netFD, err error) { | ||
pfd := f.PollFD().Copy() | ||
defer func() { | ||
if err != nil { | ||
pfd.Close() | ||
} | ||
}() | ||
filetype, err := fd_fdstat_get_type(pfd.Sysfd) | ||
if err != nil { | ||
return nil, err | ||
} | ||
if filetype != syscall.FILETYPE_SOCKET_STREAM { | ||
return nil, syscall.ENOTSOCK | ||
} | ||
fd, err = newPollFD(pfd) | ||
if err != nil { | ||
return nil, err | ||
} | ||
if err := fd.init(); err != nil { | ||
return nil, err | ||
} | ||
return fd, nil | ||
} | ||
|
||
// This helper is implemented in the syscall package. It means we don't have | ||
// to redefine the fd_fdstat_get host import or the fdstat struct it | ||
// populates. | ||
// | ||
//go:linkname fd_fdstat_get_type syscall.fd_fdstat_get_type | ||
func fd_fdstat_get_type(fd int) (uint8, error) |
Oops, something went wrong.