diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c749110..81cad86 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -25,10 +25,10 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: - go-version: "1.21.x" + go-version: "1.22.x" - uses: acifani/setup-tinygo@v2 with: - tinygo-version: '0.30.0' + tinygo-version: '0.31.1' - name: Build WATM Artifacts run: tinygo build -o ../../../${{ matrix.watm.name }}.v0.tinygo.wasm -target=wasi -no-debug -scheduler=${{ matrix.watm.scheduler }} diff --git a/.github/workflows/watm.yml b/.github/workflows/watm.yml index 247af38..c1a29ba 100644 --- a/.github/workflows/watm.yml +++ b/.github/workflows/watm.yml @@ -43,8 +43,8 @@ jobs: strategy: fail-fast: true matrix: - tinygo: [ "0.30.0" ] # latest tinygo version ONLY (1) - go: [ "1.20.x", "1.21.x" ] # latest 2 stable versions of Go. TODO: bump to 1.22.x once tinygo support added. + tinygo: [ "0.31.1" ] # latest tinygo version ONLY (1) + go: [ "1.21.x", "1.22.x" ] # latest 2 stable versions of Go. TODO: bump to 1.22.x once tinygo support added. examples: [ "plain", "reverse", "utls" ] # Add examples here per ones under tinygo/v0/examples steps: - uses: actions/checkout@v4 diff --git a/.gitignore b/.gitignore index 5959b9c..06125e0 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ # Go workspace file go.work +go.work.sum # Generated by Cargo # will have compiled files and executables diff --git a/tinygo/v0/dialer.go b/tinygo/v0/dialer.go index 7165741..0a8d0ba 100644 --- a/tinygo/v0/dialer.go +++ b/tinygo/v0/dialer.go @@ -1,13 +1,5 @@ package v0 -import ( - "log" - "syscall" - - v0net "github.com/refraction-networking/watm/tinygo/v0/net" - "github.com/refraction-networking/watm/wasip1" -) - type dialer struct { wt WrappingTransport // dt DialingTransport @@ -58,45 +50,3 @@ func BuildDialerWithDialingTransport(dt DialingTransport) { // d.wt = nil panic("BuildDialerWithDialingTransport: not implemented") } - -//export _water_dial -func _water_dial(internalFd int32) (networkFd int32) { - if workerIdentity != identity_uninitialized { - return wasip1.EncodeWATERError(syscall.EBUSY) // device or resource busy (worker already initialized) - } - - // wrap the internalFd into a v0net.Conn - sourceConn = v0net.RebuildTCPConn(internalFd) - err := sourceConn.(*v0net.TCPConn).SetNonBlock(true) - if err != nil { - log.Printf("dial: sourceConn.SetNonblock: %v", err) - return wasip1.EncodeWATERError(err.(syscall.Errno)) - } - - if d.wt != nil { - // call v0net.Dial - rawNetworkConn, err := v0net.Dial("", "") - if err != nil { - log.Printf("dial: v0net.Dial: %v", err) - return wasip1.EncodeWATERError(err.(syscall.Errno)) - } - networkFd = rawNetworkConn.Fd() - - // Note: here we are not setting nonblocking mode on the - // networkConn -- it depends on the WrappingTransport to - // determine whether to set nonblocking mode or not. - - // wrap - remoteConn, err = d.wt.Wrap(rawNetworkConn) - if err != nil { - log.Printf("dial: d.wt.Wrap: %v", err) - return wasip1.EncodeWATERError(syscall.EPROTO) // protocol error - } - // TODO: implement _water_dial with DialingTransport - } else { - return wasip1.EncodeWATERError(syscall.EPERM) // operation not permitted - } - - workerIdentity = identity_dialer - return networkFd -} diff --git a/tinygo/v0/examples/plain/README.md b/tinygo/v0/examples/plain/README.md index b19533f..3a8cd82 100644 --- a/tinygo/v0/examples/plain/README.md +++ b/tinygo/v0/examples/plain/README.md @@ -4,7 +4,7 @@ This example shows how to build a minimal WATM with TinyGo which passes through ## Build -Go 1.20/1.21 is required to build this example until TinyGo supports Go 1.22. +Go 1.20/1.21/1.22 is required to build this example. TinyGo started to support Go 1.22 in v0.31.0. ### Debug diff --git a/tinygo/v0/examples/reverse/README.md b/tinygo/v0/examples/reverse/README.md index 3d2be6f..d2e3dfb 100644 --- a/tinygo/v0/examples/reverse/README.md +++ b/tinygo/v0/examples/reverse/README.md @@ -4,7 +4,7 @@ This example shows how to build a minimal WATM with TinyGo which reverse the rec ## Build -Go 1.20/1.21 is required to build this example until TinyGo supports Go 1.22. +Go 1.20/1.21/1.22 is required to build this example. TinyGo started to support Go 1.22 in v0.31.0. ### Debug diff --git a/tinygo/v0/examples/utls/README.md b/tinygo/v0/examples/utls/README.md index 737bf56..611fbcf 100644 --- a/tinygo/v0/examples/utls/README.md +++ b/tinygo/v0/examples/utls/README.md @@ -4,7 +4,7 @@ This example shows how to build a fully functional TLS client with TinyGo from [ ## Build -Go 1.20/1.21 is required to build this example until TinyGo supports Go 1.22. +Go 1.20/1.21/1.22 is required to build this example. TinyGo started to support Go 1.22 in v0.31.0. ### Debug diff --git a/tinygo/v0/exports.go b/tinygo/v0/exports.go index a22c596..23ca5b0 100644 --- a/tinygo/v0/exports.go +++ b/tinygo/v0/exports.go @@ -94,3 +94,142 @@ func _water_cancel_with(cancelFd int32) int32 { return 0 // ESUCCESS } + +//export _water_dial +func _water_dial(internalFd int32) (networkFd int32) { + if workerIdentity != identity_uninitialized { + return wasip1.EncodeWATERError(syscall.EBUSY) // device or resource busy (worker already initialized) + } + + // wrap the internalFd into a v0net.Conn + sourceConn = v0net.RebuildTCPConn(internalFd) + err := sourceConn.(*v0net.TCPConn).SetNonBlock(true) + if err != nil { + log.Printf("dial: sourceConn.SetNonblock: %v", err) + return wasip1.EncodeWATERError(err.(syscall.Errno)) + } + + if d.wt != nil { + // call v0net.Dial + rawNetworkConn, err := v0net.Dial("", "") + if err != nil { + log.Printf("dial: v0net.Dial: %v", err) + return wasip1.EncodeWATERError(err.(syscall.Errno)) + } + networkFd = rawNetworkConn.Fd() + + // Note: here we are not setting nonblocking mode on the + // networkConn -- it depends on the WrappingTransport to + // determine whether to set nonblocking mode or not. + + // wrap + remoteConn, err = d.wt.Wrap(rawNetworkConn) + if err != nil { + log.Printf("dial: d.wt.Wrap: %v", err) + return wasip1.EncodeWATERError(syscall.EPROTO) // protocol error + } + // TODO: implement _water_dial with DialingTransport + } else { + return wasip1.EncodeWATERError(syscall.EPERM) // operation not permitted + } + + workerIdentity = identity_dialer + return networkFd +} + +//export _water_accept +func _water_accept(internalFd int32) (networkFd int32) { + if workerIdentity != identity_uninitialized { + return wasip1.EncodeWATERError(syscall.EBUSY) // device or resource busy (worker already initialized) + } + + // wrap the internalFd into a v0net.Conn + sourceConn = v0net.RebuildTCPConn(internalFd) + err := sourceConn.(*v0net.TCPConn).SetNonBlock(true) + if err != nil { + log.Printf("dial: sourceConn.SetNonblock: %v", err) + return wasip1.EncodeWATERError(err.(syscall.Errno)) + } + + if d.wt != nil { + var lis v0net.Listener = &v0net.TCPListener{} + // call v0net.Listener.Accept + rawNetworkConn, err := lis.Accept() + if err != nil { + log.Printf("dial: v0net.Listener.Accept: %v", err) + return wasip1.EncodeWATERError(err.(syscall.Errno)) + } + networkFd = rawNetworkConn.Fd() + + // Note: here we are not setting nonblocking mode on the + // networkConn -- it depends on the WrappingTransport to + // determine whether to set nonblocking mode or not. + + // wrap + remoteConn, err = d.wt.Wrap(rawNetworkConn) + if err != nil { + log.Printf("dial: d.wt.Wrap: %v", err) + return wasip1.EncodeWATERError(syscall.EPROTO) // protocol error + } + // TODO: implement _water_accept with ListeningTransport + } else { + return wasip1.EncodeWATERError(syscall.EPERM) // operation not permitted + } + + workerIdentity = identity_listener + return networkFd +} + +//export _water_associate +func _water_associate() int32 { + if workerIdentity != identity_uninitialized { + return wasip1.EncodeWATERError(syscall.EBUSY) // device or resource busy (worker already initialized) + } + + if r.wt != nil { + var err error + var lis v0net.Listener = &v0net.TCPListener{} + sourceConn, err = lis.Accept() + if err != nil { + log.Printf("dial: v0net.Listener.Accept: %v", err) + return wasip1.EncodeWATERError(err.(syscall.Errno)) + } + + remoteConn, err = v0net.Dial("", "") + if err != nil { + log.Printf("dial: v0net.Dial: %v", err) + return wasip1.EncodeWATERError(err.(syscall.Errno)) + } + + if r.wrapSelection == RelayWrapRemote { + // wrap remoteConn + remoteConn, err = r.wt.Wrap(remoteConn.(*v0net.TCPConn)) + // set sourceConn, the not-wrapped one, to non-blocking mode + sourceConn.(*v0net.TCPConn).SetNonBlock(true) + } else { + // wrap sourceConn + sourceConn, err = r.wt.Wrap(sourceConn.(*v0net.TCPConn)) + // set remoteConn, the not-wrapped one, to non-blocking mode + remoteConn.(*v0net.TCPConn).SetNonBlock(true) + } + if err != nil { + log.Printf("dial: r.wt.Wrap: %v", err) + return wasip1.EncodeWATERError(syscall.EPROTO) // protocol error + } + } else { + return wasip1.EncodeWATERError(syscall.EPERM) // operation not permitted + } + + workerIdentity = identity_relay + return 0 +} + +//export _water_worker +func _water_worker() int32 { + if workerIdentity == identity_uninitialized { + log.Println("worker: uninitialized") + return wasip1.EncodeWATERError(syscall.ENOTCONN) // socket not connected + } + log.Printf("worker: working as %s", identityStrings[workerIdentity]) + return worker() +} diff --git a/tinygo/v0/listener.go b/tinygo/v0/listener.go index 9a974e6..2c333f2 100644 --- a/tinygo/v0/listener.go +++ b/tinygo/v0/listener.go @@ -1,13 +1,5 @@ package v0 -import ( - "log" - "syscall" - - v0net "github.com/refraction-networking/watm/tinygo/v0/net" - "github.com/refraction-networking/watm/wasip1" -) - type listener struct { wt WrappingTransport // lt ListeningTransport @@ -58,46 +50,3 @@ func BuildListenerWithListeningTransport(lt ListeningTransport) { // l.wt = nil panic("BuildListenerWithListeningTransport: not implemented") } - -//export _water_accept -func _water_accept(internalFd int32) (networkFd int32) { - if workerIdentity != identity_uninitialized { - return wasip1.EncodeWATERError(syscall.EBUSY) // device or resource busy (worker already initialized) - } - - // wrap the internalFd into a v0net.Conn - sourceConn = v0net.RebuildTCPConn(internalFd) - err := sourceConn.(*v0net.TCPConn).SetNonBlock(true) - if err != nil { - log.Printf("dial: sourceConn.SetNonblock: %v", err) - return wasip1.EncodeWATERError(err.(syscall.Errno)) - } - - if d.wt != nil { - var lis v0net.Listener = &v0net.TCPListener{} - // call v0net.Listener.Accept - rawNetworkConn, err := lis.Accept() - if err != nil { - log.Printf("dial: v0net.Listener.Accept: %v", err) - return wasip1.EncodeWATERError(err.(syscall.Errno)) - } - networkFd = rawNetworkConn.Fd() - - // Note: here we are not setting nonblocking mode on the - // networkConn -- it depends on the WrappingTransport to - // determine whether to set nonblocking mode or not. - - // wrap - remoteConn, err = d.wt.Wrap(rawNetworkConn) - if err != nil { - log.Printf("dial: d.wt.Wrap: %v", err) - return wasip1.EncodeWATERError(syscall.EPROTO) // protocol error - } - // TODO: implement _water_accept with ListeningTransport - } else { - return wasip1.EncodeWATERError(syscall.EPERM) // operation not permitted - } - - workerIdentity = identity_listener - return networkFd -} diff --git a/tinygo/v0/net/conn.go b/tinygo/v0/net/conn.go index c4b734a..91dd5e5 100644 --- a/tinygo/v0/net/conn.go +++ b/tinygo/v0/net/conn.go @@ -46,7 +46,7 @@ func (c *TCPConn) Read(b []byte) (n int, err error) { // if no deadline set, behavior depends on blocking mode of the // underlying file descriptor. return syscallFnFd(c.rawConn, func(fd uintptr) (int, error) { - n, err := syscall.Read(int(fd), b) + n, err := syscall.Read(syscallFd(fd), b) if n == 0 && err == nil { err = io.EOF } @@ -60,7 +60,7 @@ func (c *TCPConn) Read(b []byte) (n int, err error) { // we retry until the deadline is reached. for { if n, err = syscallFnFd(c.rawConn, func(fd uintptr) (int, error) { - n, err := syscall.Read(int(fd), b) + n, err := syscall.Read(syscallFd(fd), b) if n == 0 && err == nil { err = io.EOF } @@ -84,13 +84,13 @@ func (c *TCPConn) Write(b []byte) (n int, writeErr error) { // if no deadline set, behavior depends on blocking mode of the // underlying file descriptor. return syscallFnFd(c.rawConn, func(fd uintptr) (int, error) { - return syscall.Write(int(fd), b) + return syscall.Write(syscallFd(fd), b) }) } else { // writeDeadline is set, if EAGAIN/EWOULDBLOCK is returned, // we retry until the deadline is reached. if err := c.rawConn.Write(func(fd uintptr) (done bool) { - n, writeErr = syscall.Write(int(fd), b) + n, writeErr = syscall.Write(syscallFd(fd), b) if errors.Is(writeErr, syscall.EAGAIN) { if time.Now().Before(wdl) { return false @@ -117,7 +117,7 @@ func (c *TCPConn) Close() error { // }) // } return syscallControlFd(c.rawConn, func(fd uintptr) error { - return syscall.Close(int(fd)) + return syscall.Close(syscallFd(fd)) }) } diff --git a/tinygo/v0/net/syscall.go b/tinygo/v0/net/syscall.go index a373a96..d8b6b8e 100644 --- a/tinygo/v0/net/syscall.go +++ b/tinygo/v0/net/syscall.go @@ -1,7 +1,6 @@ package net import ( - "fmt" "syscall" ) @@ -9,7 +8,7 @@ func syscallControlFd(rawConn syscall.RawConn, f func(fd uintptr) error) (err er if controlErr := rawConn.Control(func(fd uintptr) { err = f(fd) }); controlErr != nil { - panic(fmt.Sprintf("controlErr = %v", controlErr)) + // panic(fmt.Sprintf("controlErr = %v", controlErr)) return controlErr } return err diff --git a/tinygo/v0/net/syscall_unix.go b/tinygo/v0/net/syscall_unix.go index 7044136..faa6406 100644 --- a/tinygo/v0/net/syscall_unix.go +++ b/tinygo/v0/net/syscall_unix.go @@ -7,3 +7,5 @@ import "syscall" func syscallSetNonblock(fd uintptr, nonblocking bool) (err error) { return syscall.SetNonblock(int(fd), nonblocking) } + +type syscallFd = int diff --git a/tinygo/v0/net/syscall_wasi.go b/tinygo/v0/net/syscall_wasi.go index 1431490..245b48c 100644 --- a/tinygo/v0/net/syscall_wasi.go +++ b/tinygo/v0/net/syscall_wasi.go @@ -44,3 +44,5 @@ const ( FDFLAG_RSYNC = 0x0008 FDFLAG_SYNC = 0x0010 ) + +type syscallFd = int diff --git a/tinygo/v0/net/syscall_windows.go b/tinygo/v0/net/syscall_windows.go index f4c0595..1de3513 100644 --- a/tinygo/v0/net/syscall_windows.go +++ b/tinygo/v0/net/syscall_windows.go @@ -31,3 +31,5 @@ func syscallSetNonblock(fd uintptr, nonblocking bool) (err error) { uintptr(unsafe.Pointer(&opt))) return errno } + +type syscallFd = syscall.Handle diff --git a/tinygo/v0/relay.go b/tinygo/v0/relay.go index 0fc59b7..835d416 100644 --- a/tinygo/v0/relay.go +++ b/tinygo/v0/relay.go @@ -1,13 +1,5 @@ package v0 -import ( - "log" - "syscall" - - v0net "github.com/refraction-networking/watm/tinygo/v0/net" - "github.com/refraction-networking/watm/wasip1" -) - type RelayWrapSelection bool const ( @@ -84,47 +76,3 @@ func BuildRelayWithListeningDialingTransport(lt ListeningTransport, dt DialingTr // r.wt = nil panic("BuildRelayWithListeningDialingTransport: not implemented") } - -//export _water_associate -func _water_associate() int32 { - if workerIdentity != identity_uninitialized { - return wasip1.EncodeWATERError(syscall.EBUSY) // device or resource busy (worker already initialized) - } - - if r.wt != nil { - var err error - var lis v0net.Listener = &v0net.TCPListener{} - sourceConn, err = lis.Accept() - if err != nil { - log.Printf("dial: v0net.Listener.Accept: %v", err) - return wasip1.EncodeWATERError(err.(syscall.Errno)) - } - - remoteConn, err = v0net.Dial("", "") - if err != nil { - log.Printf("dial: v0net.Dial: %v", err) - return wasip1.EncodeWATERError(err.(syscall.Errno)) - } - - if r.wrapSelection == RelayWrapRemote { - // wrap remoteConn - remoteConn, err = r.wt.Wrap(remoteConn.(*v0net.TCPConn)) - // set sourceConn, the not-wrapped one, to non-blocking mode - sourceConn.(*v0net.TCPConn).SetNonBlock(true) - } else { - // wrap sourceConn - sourceConn, err = r.wt.Wrap(sourceConn.(*v0net.TCPConn)) - // set remoteConn, the not-wrapped one, to non-blocking mode - remoteConn.(*v0net.TCPConn).SetNonBlock(true) - } - if err != nil { - log.Printf("dial: r.wt.Wrap: %v", err) - return wasip1.EncodeWATERError(syscall.EPROTO) // protocol error - } - } else { - return wasip1.EncodeWATERError(syscall.EPERM) // operation not permitted - } - - workerIdentity = identity_relay - return 0 -} diff --git a/tinygo/v0/worker.go b/tinygo/v0/worker.go index c338d3c..f05a572 100644 --- a/tinygo/v0/worker.go +++ b/tinygo/v0/worker.go @@ -51,16 +51,6 @@ func WorkerFairness(fair bool) { } } -//export _water_worker -func _water_worker() int32 { - if workerIdentity == identity_uninitialized { - log.Println("worker: uninitialized") - return wasip1.EncodeWATERError(syscall.ENOTCONN) // socket not connected - } - log.Printf("worker: working as %s", identityStrings[workerIdentity]) - return worker() -} - func worker() int32 { defer _import_host_defer()