From 083fb518a207f6cf7ebf08c70c3575f30f74485a Mon Sep 17 00:00:00 2001 From: Gaukas Wang Date: Mon, 22 Jul 2024 22:02:18 -0600 Subject: [PATCH] update: major revision reflecting design from doc Signed-off-by: Gaukas Wang --- tinygo/snippets/go.mod | 4 +- tinygo/v0/README.md | 3 - tinygo/v0/dialer.go | 52 --- tinygo/v0/examples/go.mod | 22 - tinygo/v0/examples/go.sum | 14 - tinygo/v0/examples/plain/README.md | 19 - tinygo/v0/examples/plain/main.go | 12 - tinygo/v0/examples/plain/wrapper_transport.go | 21 - tinygo/v0/examples/reverse/README.md | 19 - tinygo/v0/examples/reverse/main.go | 11 - .../v0/examples/reverse/wrapper_transport.go | 46 -- tinygo/v0/examples/utls/README.md | 23 - tinygo/v0/examples/utls/lib/lib.go | 63 --- tinygo/v0/examples/utls/lib/lib_tinyjson.go | 310 ------------- tinygo/v0/examples/utls/main.go | 11 - tinygo/v0/examples/utls/wrapper_transport.go | 69 --- tinygo/v0/exports.go | 235 ---------- tinygo/v0/imports_unsupported.go | 31 -- tinygo/v0/imports_wasi.go | 17 - tinygo/v0/listener.go | 52 --- tinygo/v0/net/conn.go | 224 ---------- tinygo/v0/net/conn_test.go | 267 ----------- tinygo/v0/net/dial.go | 24 - tinygo/v0/net/fd.go | 47 -- tinygo/v0/net/import.go | 29 -- tinygo/v0/net/import_wasi.go | 31 -- tinygo/v0/net/listener.go | 33 -- tinygo/v0/net/syscall.go | 24 - tinygo/v0/net/syscall_unix.go | 11 - tinygo/v0/net/syscall_wasi.go | 48 -- tinygo/v0/net/syscall_windows.go | 35 -- tinygo/v0/netpoll.go | 137 ------ tinygo/v0/os.go | 133 ------ tinygo/v0/pluggable_transports.go | 90 ---- tinygo/v0/relay.go | 78 ---- tinygo/v0/worker.go | 294 ------------- tinygo/v1/config.go | 44 ++ tinygo/v1/exports.go | 144 +++--- tinygo/v1/net/dial.go | 14 +- tinygo/v1/net/listener.go | 6 +- tinygo/v1/net/network_type.go | 6 +- tinygo/v1/wasiimport/import.go | 20 +- .../{symbols.go => import_exported.go} | 19 +- tinygo/v1/wasiimport/import_wasi.go | 41 +- tinygo/v1/worker.go | 49 +-- wasip1/errno.go | 15 +- wasip1/error.go | 14 + wasip1/tables.go | 415 +++++++++--------- 48 files changed, 421 insertions(+), 2905 deletions(-) delete mode 100644 tinygo/v0/README.md delete mode 100644 tinygo/v0/dialer.go delete mode 100644 tinygo/v0/examples/go.mod delete mode 100644 tinygo/v0/examples/go.sum delete mode 100644 tinygo/v0/examples/plain/README.md delete mode 100644 tinygo/v0/examples/plain/main.go delete mode 100644 tinygo/v0/examples/plain/wrapper_transport.go delete mode 100644 tinygo/v0/examples/reverse/README.md delete mode 100644 tinygo/v0/examples/reverse/main.go delete mode 100644 tinygo/v0/examples/reverse/wrapper_transport.go delete mode 100644 tinygo/v0/examples/utls/README.md delete mode 100644 tinygo/v0/examples/utls/lib/lib.go delete mode 100644 tinygo/v0/examples/utls/lib/lib_tinyjson.go delete mode 100644 tinygo/v0/examples/utls/main.go delete mode 100644 tinygo/v0/examples/utls/wrapper_transport.go delete mode 100644 tinygo/v0/exports.go delete mode 100644 tinygo/v0/imports_unsupported.go delete mode 100644 tinygo/v0/imports_wasi.go delete mode 100644 tinygo/v0/listener.go delete mode 100644 tinygo/v0/net/conn.go delete mode 100644 tinygo/v0/net/conn_test.go delete mode 100644 tinygo/v0/net/dial.go delete mode 100644 tinygo/v0/net/fd.go delete mode 100644 tinygo/v0/net/import.go delete mode 100644 tinygo/v0/net/import_wasi.go delete mode 100644 tinygo/v0/net/listener.go delete mode 100644 tinygo/v0/net/syscall.go delete mode 100644 tinygo/v0/net/syscall_unix.go delete mode 100644 tinygo/v0/net/syscall_wasi.go delete mode 100644 tinygo/v0/net/syscall_windows.go delete mode 100644 tinygo/v0/netpoll.go delete mode 100644 tinygo/v0/os.go delete mode 100644 tinygo/v0/pluggable_transports.go delete mode 100644 tinygo/v0/relay.go delete mode 100644 tinygo/v0/worker.go create mode 100644 tinygo/v1/config.go rename tinygo/v1/wasiimport/{symbols.go => import_exported.go} (64%) create mode 100644 wasip1/error.go diff --git a/tinygo/snippets/go.mod b/tinygo/snippets/go.mod index fe62ab7..3a26f76 100644 --- a/tinygo/snippets/go.mod +++ b/tinygo/snippets/go.mod @@ -2,6 +2,6 @@ module github.com/refraction-networking/watm/tinygo/snippets go 1.20 -replace github.com/tetratelabs/wazero v1.6.0 => github.com/refraction-networking/wazero v1.6.6-w +replace github.com/tetratelabs/wazero v1.7.3 => github.com/refraction-networking/wazero v1.7.3-w -require github.com/tetratelabs/wazero v1.6.0 +require github.com/tetratelabs/wazero v1.7.3 diff --git a/tinygo/v0/README.md b/tinygo/v0/README.md deleted file mode 100644 index afad012..0000000 --- a/tinygo/v0/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# `tinygo/v0` - -This directory contains package you might find useful when building a v0 WATM with TinyGo. \ No newline at end of file diff --git a/tinygo/v0/dialer.go b/tinygo/v0/dialer.go deleted file mode 100644 index 0a8d0ba..0000000 --- a/tinygo/v0/dialer.go +++ /dev/null @@ -1,52 +0,0 @@ -package v0 - -type dialer struct { - wt WrappingTransport - // dt DialingTransport -} - -func (d *dialer) ConfigurableTransport() ConfigurableTransport { - if d.wt != nil { - if wt, ok := d.wt.(ConfigurableTransport); ok { - return wt - } - } - - // if d.dt != nil { - // if dt, ok := d.dt.(ConfigurableTransport); ok { - // return dt - // } - // } - - return nil -} - -func (d *dialer) Initialize() { - // TODO: allow initialization on dialer -} - -var d dialer - -// BuildDialerWithWrappingTransport arms the dialer with a -// [WrappingTransport] that is used to wrap a [v0net.Conn] into -// another [net.Conn] by providing some high-level application -// layer protocol. -// -// Mutually exclusive with [BuildDialerWithDialingTransport]. -func BuildDialerWithWrappingTransport(wt WrappingTransport) { - d.wt = wt - // d.dt = nil -} - -// BuildDialerWithDialingTransport arms the dialer with a -// [DialingTransport] that is used to dial a remote address and -// provide high-level application layer protocol over the dialed -// connection. -// -// Mutually exclusive with [BuildDialerWithWrappingTransport]. -func BuildDialerWithDialingTransport(dt DialingTransport) { - // TODO: implement BuildDialerWithDialingTransport - // d.dt = dt - // d.wt = nil - panic("BuildDialerWithDialingTransport: not implemented") -} diff --git a/tinygo/v0/examples/go.mod b/tinygo/v0/examples/go.mod deleted file mode 100644 index 2213dcf..0000000 --- a/tinygo/v0/examples/go.mod +++ /dev/null @@ -1,22 +0,0 @@ -module github.com/refraction-networking/watm/tinygo/v0/examples - -go 1.21 - -replace golang.org/x/sys => ../../replaced/golang.org/x/sys@v0.19.0 - -replace github.com/refraction-networking/watm => ../../../ - -require ( - github.com/CosmWasm/tinyjson v0.9.0 - github.com/refraction-networking/utls v1.6.6-wasm - github.com/refraction-networking/watm v0.6.5 -) - -require ( - github.com/andybalholm/brotli v1.1.0 // indirect - github.com/cloudflare/circl v1.3.9 // indirect - github.com/josharian/intern v1.0.0 // indirect - github.com/klauspost/compress v1.17.9 // indirect - golang.org/x/crypto v0.24.0 // indirect - golang.org/x/sys v0.21.0 // indirect -) diff --git a/tinygo/v0/examples/go.sum b/tinygo/v0/examples/go.sum deleted file mode 100644 index 5149b10..0000000 --- a/tinygo/v0/examples/go.sum +++ /dev/null @@ -1,14 +0,0 @@ -github.com/CosmWasm/tinyjson v0.9.0 h1:sPjgikATp5W0vD/v/Qz99uQ6G/lh/SuK0Wfskqua4Co= -github.com/CosmWasm/tinyjson v0.9.0/go.mod h1:5+7QnSKrkIWnpIdhUT2t2EYzXnII3/3MlM0oDsBSbc8= -github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= -github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= -github.com/cloudflare/circl v1.3.9 h1:QFrlgFYf2Qpi8bSpVPK1HBvWpx16v/1TZivyo7pGuBE= -github.com/cloudflare/circl v1.3.9/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU= -github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= -github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= -github.com/refraction-networking/utls v1.6.6-wasm h1:fayTy1wZzzNSZqJ9rzMgDOoGVLS9/u0YIRQ9fUurlc0= -github.com/refraction-networking/utls v1.6.6-wasm/go.mod h1:BC3O4vQzye5hqpmDTWUqi4P5DDhzJfkV1tdqtawQIH0= -golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= -golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= diff --git a/tinygo/v0/examples/plain/README.md b/tinygo/v0/examples/plain/README.md deleted file mode 100644 index 3a8cd82..0000000 --- a/tinygo/v0/examples/plain/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# Example: `plain.wasm` - -This example shows how to build a minimal WATM with TinyGo which passes through the received/sent data without any processing. - -## Build - -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 - -```bash -tinygo build -o plain.wasm -target=wasi -scheduler=none -gc=conservative . -``` - -### Release - -```bash -tinygo build -o plain.wasm -target=wasi -no-debug -scheduler=none -gc=conservative . -``` \ No newline at end of file diff --git a/tinygo/v0/examples/plain/main.go b/tinygo/v0/examples/plain/main.go deleted file mode 100644 index 3544652..0000000 --- a/tinygo/v0/examples/plain/main.go +++ /dev/null @@ -1,12 +0,0 @@ -package main - -import v0 "github.com/refraction-networking/watm/tinygo/v0" - -func init() { - v0.WorkerFairness(false) // by default, use unfairWorker for better performance - v0.BuildDialerWithWrappingTransport(&PlainWrappingTransport{}) - v0.BuildListenerWithWrappingTransport(&PlainWrappingTransport{}) - v0.BuildRelayWithWrappingTransport(&PlainWrappingTransport{}, v0.RelayWrapRemote) -} - -func main() {} diff --git a/tinygo/v0/examples/plain/wrapper_transport.go b/tinygo/v0/examples/plain/wrapper_transport.go deleted file mode 100644 index 8c98754..0000000 --- a/tinygo/v0/examples/plain/wrapper_transport.go +++ /dev/null @@ -1,21 +0,0 @@ -package main - -import ( - v0 "github.com/refraction-networking/watm/tinygo/v0" - v0net "github.com/refraction-networking/watm/tinygo/v0/net" -) - -// type guard: PlainWrappingTransport must implement [v0.WrappingTransport]. -var _ v0.WrappingTransport = (*PlainWrappingTransport)(nil) - -type PlainWrappingTransport struct { -} - -func (rwt *PlainWrappingTransport) Wrap(conn v0net.Conn) (v0net.Conn, error) { - return &PlainConn{conn}, conn.SetNonBlock(true) // must set non-block, otherwise will block on read and lose fairness -} - -// PlainConn simply passes through the underlying Conn. -type PlainConn struct { - v0net.Conn // embedded Conn -} diff --git a/tinygo/v0/examples/reverse/README.md b/tinygo/v0/examples/reverse/README.md deleted file mode 100644 index d2e3dfb..0000000 --- a/tinygo/v0/examples/reverse/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# Example: `reverse.wasm` - -This example shows how to build a minimal WATM with TinyGo which reverse the received string. - -## Build - -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 - -```bash -tinygo build -o reverse.wasm -target=wasi -scheduler=none -gc=conservative . -``` - -### Release - -```bash -tinygo build -o reverse.wasm -target=wasi -no-debug -scheduler=none -gc=conservative . -``` \ No newline at end of file diff --git a/tinygo/v0/examples/reverse/main.go b/tinygo/v0/examples/reverse/main.go deleted file mode 100644 index dd0ec7a..0000000 --- a/tinygo/v0/examples/reverse/main.go +++ /dev/null @@ -1,11 +0,0 @@ -package main - -import v0 "github.com/refraction-networking/watm/tinygo/v0" - -func init() { - v0.BuildDialerWithWrappingTransport(&ReverseWrappingTransport{}) - v0.BuildListenerWithWrappingTransport(&ReverseWrappingTransport{}) - v0.BuildRelayWithWrappingTransport(&ReverseWrappingTransport{}, v0.RelayWrapRemote) -} - -func main() {} diff --git a/tinygo/v0/examples/reverse/wrapper_transport.go b/tinygo/v0/examples/reverse/wrapper_transport.go deleted file mode 100644 index a2f8927..0000000 --- a/tinygo/v0/examples/reverse/wrapper_transport.go +++ /dev/null @@ -1,46 +0,0 @@ -package main - -import ( - v0 "github.com/refraction-networking/watm/tinygo/v0" - v0net "github.com/refraction-networking/watm/tinygo/v0/net" -) - -// type guard: ReverseWrappingTransport must implement [v0.WrappingTransport]. -var _ v0.WrappingTransport = (*ReverseWrappingTransport)(nil) - -type ReverseWrappingTransport struct { -} - -func (rwt *ReverseWrappingTransport) Wrap(conn v0net.Conn) (v0net.Conn, error) { - return &ReverseConn{conn}, conn.SetNonBlock(true) // must set non-block, otherwise will block on read and lose fairness -} - -type ReverseConn struct { - v0net.Conn // embedded Conn -} - -func (rc *ReverseConn) Read(b []byte) (n int, err error) { - tmpBuf := make([]byte, len(b)) - n, err = rc.Conn.Read(tmpBuf) - if err != nil { - return 0, err - } - - // reverse all bytes read successfully so far - for i := 0; i < n; i++ { - b[i] = tmpBuf[n-i-1] - } - - return n, err -} - -func (rc *ReverseConn) Write(b []byte) (n int, err error) { - tmpBuf := make([]byte, len(b)) - - // reverse the bytes to be written - for i := 0; i < len(b); i++ { - tmpBuf[i] = b[len(b)-i-1] - } - - return rc.Conn.Write(tmpBuf[:len(b)]) -} diff --git a/tinygo/v0/examples/utls/README.md b/tinygo/v0/examples/utls/README.md deleted file mode 100644 index 611fbcf..0000000 --- a/tinygo/v0/examples/utls/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# Example: `uTLS.wasm` - -This example shows how to build a fully functional TLS client with TinyGo from [uTLS](https://github.com/refraction-networking/utls/tree/wasm). - -## Build - -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 - -```bash -tinygo build -o utls.wasm -target=wasi -scheduler=asyncify -gc=conservative -tags=purego . -``` - -### Release - -```bash -tinygo build -o utls.wasm -target=wasi -no-debug -scheduler=asyncify -gc=conservative -tags=purego . -``` - -## Dependencies - -The `utls` imported must be from the `wasm` branch of [uTLS](https://github.com/refraction-networking/utls/tree/wasm). You may use a replace directive in `go.mod` to your local clone of uTLS, or use a tagged version with suffix `-wasm` (e.g., `v1.6.2-wasm`) to make sure it is tagged from the correct branch. \ No newline at end of file diff --git a/tinygo/v0/examples/utls/lib/lib.go b/tinygo/v0/examples/utls/lib/lib.go deleted file mode 100644 index b2d73c0..0000000 --- a/tinygo/v0/examples/utls/lib/lib.go +++ /dev/null @@ -1,63 +0,0 @@ -package lib - -import ( - tls "github.com/refraction-networking/utls" -) - -//tinyjson:json -type TLSConfig struct { - NextProtos []string `json:"next_protos"` - ApplicationSettings map[string][]byte `json:"application_settings"` - ServerName string `json:"server_name"` - InsecureSkipVerify bool `json:"insecure_skip_verify"` - InsecureSkipTimeVerify bool `json:"insecure_skip_time_verify"` - OmitEmptyPsk bool `json:"omit_empty_psk"` - InsecureServerNameToVerify string `json:"insecure_server_name_to_verify"` - SessionTicketsDisabled bool `json:"session_tickets_disabled"` - PQSignatureSchemesEnabled bool `json:"pq_signature_schemes_enabled"` - DynamicRecordSizingDisabled bool `json:"dynamic_record_sizing_disabled"` - ECHConfigs []byte `json:"ech_configs"` -} - -//tinyjson:json -type Configurables struct { - TLSConfig *TLSConfig `json:"tls_config"` // will be converted to tls.Config - ClientHelloID string `json:"client_hello_id"` // will be converted to tls.ClientHelloID -} - -func (c *Configurables) GetTLSConfig() *tls.Config { - config := &tls.Config{ - NextProtos: c.TLSConfig.NextProtos, - ApplicationSettings: c.TLSConfig.ApplicationSettings, - ServerName: c.TLSConfig.ServerName, - InsecureSkipVerify: c.TLSConfig.InsecureSkipVerify, - InsecureSkipTimeVerify: c.TLSConfig.InsecureSkipTimeVerify, - OmitEmptyPsk: c.TLSConfig.OmitEmptyPsk, - InsecureServerNameToVerify: c.TLSConfig.InsecureServerNameToVerify, - SessionTicketsDisabled: c.TLSConfig.SessionTicketsDisabled, - PQSignatureSchemesEnabled: c.TLSConfig.PQSignatureSchemesEnabled, - DynamicRecordSizingDisabled: c.TLSConfig.DynamicRecordSizingDisabled, - } - - echConfigs, err := tls.UnmarshalECHConfigs(c.TLSConfig.ECHConfigs) - if err == nil { // otherwise do we need to return an error or just ignore it? - config.ECHConfigs = echConfigs - } - - return config -} - -func (c *Configurables) GetClientHelloID() tls.ClientHelloID { - switch c.ClientHelloID { - case "HelloChrome_Auto", "HelloChrome", "Chrome", "chrome": - return tls.HelloChrome_Auto - case "HelloEdge_Auto", "HelloEdge", "Edge", "edge": - return tls.HelloEdge_Auto - case "HelloFirefox_Auto", "HelloFirefox", "Firefox", "firefox": - return tls.HelloFirefox_Auto - case "HelloSafari_Auto", "HelloSafari", "Safari", "safari": - return tls.HelloSafari_Auto - default: - panic("unknown client hello id") - } -} diff --git a/tinygo/v0/examples/utls/lib/lib_tinyjson.go b/tinygo/v0/examples/utls/lib/lib_tinyjson.go deleted file mode 100644 index f828880..0000000 --- a/tinygo/v0/examples/utls/lib/lib_tinyjson.go +++ /dev/null @@ -1,310 +0,0 @@ -// Code generated by tinyjson for marshaling/unmarshaling. DO NOT EDIT. - -package lib - -import ( - tinyjson "github.com/CosmWasm/tinyjson" - jlexer "github.com/CosmWasm/tinyjson/jlexer" - jwriter "github.com/CosmWasm/tinyjson/jwriter" -) - -// suppress unused package warning -var ( - _ *jlexer.Lexer - _ *jwriter.Writer - _ tinyjson.Marshaler -) - -func tinyjsonAded76e7DecodeGithubComRefractionNetworkingWatmTinygoV0ExamplesUtlsLib(in *jlexer.Lexer, out *TLSConfig) { - isTopLevel := in.IsStart() - if in.IsNull() { - if isTopLevel { - in.Consumed() - } - in.Skip() - return - } - in.Delim('{') - for !in.IsDelim('}') { - key := in.UnsafeFieldName(false) - in.WantColon() - if in.IsNull() { - in.Skip() - in.WantComma() - continue - } - switch key { - case "next_protos": - if in.IsNull() { - in.Skip() - out.NextProtos = nil - } else { - in.Delim('[') - if out.NextProtos == nil { - if !in.IsDelim(']') { - out.NextProtos = make([]string, 0, 4) - } else { - out.NextProtos = []string{} - } - } else { - out.NextProtos = (out.NextProtos)[:0] - } - for !in.IsDelim(']') { - var v1 string - v1 = string(in.String()) - out.NextProtos = append(out.NextProtos, v1) - in.WantComma() - } - in.Delim(']') - } - case "application_settings": - if in.IsNull() { - in.Skip() - } else { - in.Delim('{') - out.ApplicationSettings = make(map[string][]uint8) - for !in.IsDelim('}') { - key := string(in.String()) - in.WantColon() - var v2 []uint8 - if in.IsNull() { - in.Skip() - v2 = nil - } else { - v2 = in.Bytes() - } - (out.ApplicationSettings)[key] = v2 - in.WantComma() - } - in.Delim('}') - } - case "server_name": - out.ServerName = string(in.String()) - case "insecure_skip_verify": - out.InsecureSkipVerify = bool(in.Bool()) - case "insecure_skip_time_verify": - out.InsecureSkipTimeVerify = bool(in.Bool()) - case "omit_empty_psk": - out.OmitEmptyPsk = bool(in.Bool()) - case "insecure_server_name_to_verify": - out.InsecureServerNameToVerify = string(in.String()) - case "session_tickets_disabled": - out.SessionTicketsDisabled = bool(in.Bool()) - case "pq_signature_schemes_enabled": - out.PQSignatureSchemesEnabled = bool(in.Bool()) - case "dynamic_record_sizing_disabled": - out.DynamicRecordSizingDisabled = bool(in.Bool()) - case "ech_configs": - if in.IsNull() { - in.Skip() - out.ECHConfigs = nil - } else { - out.ECHConfigs = in.Bytes() - } - default: - in.SkipRecursive() - } - in.WantComma() - } - in.Delim('}') - if isTopLevel { - in.Consumed() - } -} -func tinyjsonAded76e7EncodeGithubComRefractionNetworkingWatmTinygoV0ExamplesUtlsLib(out *jwriter.Writer, in TLSConfig) { - out.RawByte('{') - first := true - _ = first - { - const prefix string = ",\"next_protos\":" - out.RawString(prefix[1:]) - if in.NextProtos == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 { - out.RawString("null") - } else { - out.RawByte('[') - for v5, v6 := range in.NextProtos { - if v5 > 0 { - out.RawByte(',') - } - out.String(string(v6)) - } - out.RawByte(']') - } - } - { - const prefix string = ",\"application_settings\":" - out.RawString(prefix) - if in.ApplicationSettings == nil && (out.Flags&jwriter.NilMapAsEmpty) == 0 { - out.RawString(`null`) - } else { - out.RawByte('{') - v7First := true - for v7Name, v7Value := range in.ApplicationSettings { - if v7First { - v7First = false - } else { - out.RawByte(',') - } - out.String(string(v7Name)) - out.RawByte(':') - out.Base64Bytes(v7Value) - } - out.RawByte('}') - } - } - { - const prefix string = ",\"server_name\":" - out.RawString(prefix) - out.String(string(in.ServerName)) - } - { - const prefix string = ",\"insecure_skip_verify\":" - out.RawString(prefix) - out.Bool(bool(in.InsecureSkipVerify)) - } - { - const prefix string = ",\"insecure_skip_time_verify\":" - out.RawString(prefix) - out.Bool(bool(in.InsecureSkipTimeVerify)) - } - { - const prefix string = ",\"omit_empty_psk\":" - out.RawString(prefix) - out.Bool(bool(in.OmitEmptyPsk)) - } - { - const prefix string = ",\"insecure_server_name_to_verify\":" - out.RawString(prefix) - out.String(string(in.InsecureServerNameToVerify)) - } - { - const prefix string = ",\"session_tickets_disabled\":" - out.RawString(prefix) - out.Bool(bool(in.SessionTicketsDisabled)) - } - { - const prefix string = ",\"pq_signature_schemes_enabled\":" - out.RawString(prefix) - out.Bool(bool(in.PQSignatureSchemesEnabled)) - } - { - const prefix string = ",\"dynamic_record_sizing_disabled\":" - out.RawString(prefix) - out.Bool(bool(in.DynamicRecordSizingDisabled)) - } - { - const prefix string = ",\"ech_configs\":" - out.RawString(prefix) - out.Base64Bytes(in.ECHConfigs) - } - out.RawByte('}') -} - -// MarshalJSON supports json.Marshaler interface -func (v TLSConfig) MarshalJSON() ([]byte, error) { - w := jwriter.Writer{} - tinyjsonAded76e7EncodeGithubComRefractionNetworkingWatmTinygoV0ExamplesUtlsLib(&w, v) - return w.Buffer.BuildBytes(), w.Error -} - -// MarshalTinyJSON supports tinyjson.Marshaler interface -func (v TLSConfig) MarshalTinyJSON(w *jwriter.Writer) { - tinyjsonAded76e7EncodeGithubComRefractionNetworkingWatmTinygoV0ExamplesUtlsLib(w, v) -} - -// UnmarshalJSON supports json.Unmarshaler interface -func (v *TLSConfig) UnmarshalJSON(data []byte) error { - r := jlexer.Lexer{Data: data} - tinyjsonAded76e7DecodeGithubComRefractionNetworkingWatmTinygoV0ExamplesUtlsLib(&r, v) - return r.Error() -} - -// UnmarshalTinyJSON supports tinyjson.Unmarshaler interface -func (v *TLSConfig) UnmarshalTinyJSON(l *jlexer.Lexer) { - tinyjsonAded76e7DecodeGithubComRefractionNetworkingWatmTinygoV0ExamplesUtlsLib(l, v) -} -func tinyjsonAded76e7DecodeGithubComRefractionNetworkingWatmTinygoV0ExamplesUtlsLib1(in *jlexer.Lexer, out *Configurables) { - isTopLevel := in.IsStart() - if in.IsNull() { - if isTopLevel { - in.Consumed() - } - in.Skip() - return - } - in.Delim('{') - for !in.IsDelim('}') { - key := in.UnsafeFieldName(false) - in.WantColon() - if in.IsNull() { - in.Skip() - in.WantComma() - continue - } - switch key { - case "tls_config": - if in.IsNull() { - in.Skip() - out.TLSConfig = nil - } else { - if out.TLSConfig == nil { - out.TLSConfig = new(TLSConfig) - } - (*out.TLSConfig).UnmarshalTinyJSON(in) - } - case "client_hello_id": - out.ClientHelloID = string(in.String()) - default: - in.SkipRecursive() - } - in.WantComma() - } - in.Delim('}') - if isTopLevel { - in.Consumed() - } -} -func tinyjsonAded76e7EncodeGithubComRefractionNetworkingWatmTinygoV0ExamplesUtlsLib1(out *jwriter.Writer, in Configurables) { - out.RawByte('{') - first := true - _ = first - { - const prefix string = ",\"tls_config\":" - out.RawString(prefix[1:]) - if in.TLSConfig == nil { - out.RawString("null") - } else { - (*in.TLSConfig).MarshalTinyJSON(out) - } - } - { - const prefix string = ",\"client_hello_id\":" - out.RawString(prefix) - out.String(string(in.ClientHelloID)) - } - out.RawByte('}') -} - -// MarshalJSON supports json.Marshaler interface -func (v Configurables) MarshalJSON() ([]byte, error) { - w := jwriter.Writer{} - tinyjsonAded76e7EncodeGithubComRefractionNetworkingWatmTinygoV0ExamplesUtlsLib1(&w, v) - return w.Buffer.BuildBytes(), w.Error -} - -// MarshalTinyJSON supports tinyjson.Marshaler interface -func (v Configurables) MarshalTinyJSON(w *jwriter.Writer) { - tinyjsonAded76e7EncodeGithubComRefractionNetworkingWatmTinygoV0ExamplesUtlsLib1(w, v) -} - -// UnmarshalJSON supports json.Unmarshaler interface -func (v *Configurables) UnmarshalJSON(data []byte) error { - r := jlexer.Lexer{Data: data} - tinyjsonAded76e7DecodeGithubComRefractionNetworkingWatmTinygoV0ExamplesUtlsLib1(&r, v) - return r.Error() -} - -// UnmarshalTinyJSON supports tinyjson.Unmarshaler interface -func (v *Configurables) UnmarshalTinyJSON(l *jlexer.Lexer) { - tinyjsonAded76e7DecodeGithubComRefractionNetworkingWatmTinygoV0ExamplesUtlsLib1(l, v) -} diff --git a/tinygo/v0/examples/utls/main.go b/tinygo/v0/examples/utls/main.go deleted file mode 100644 index 2678c6a..0000000 --- a/tinygo/v0/examples/utls/main.go +++ /dev/null @@ -1,11 +0,0 @@ -package main - -import v0 "github.com/refraction-networking/watm/tinygo/v0" - -func init() { - v0.BuildDialerWithWrappingTransport(&UTLSClientWrappingTransport{}) - // v0.BuildListenerWithWrappingTransport(&UTLSClientWrappingTransport{}) - // v0.BuildRelayWithWrappingTransport(&UTLSClientWrappingTransport{}, v0.RelayWrapRemote) -} - -func main() {} diff --git a/tinygo/v0/examples/utls/wrapper_transport.go b/tinygo/v0/examples/utls/wrapper_transport.go deleted file mode 100644 index ea031b9..0000000 --- a/tinygo/v0/examples/utls/wrapper_transport.go +++ /dev/null @@ -1,69 +0,0 @@ -package main - -import ( - "github.com/CosmWasm/tinyjson" - tls "github.com/refraction-networking/utls" - v0 "github.com/refraction-networking/watm/tinygo/v0" - "github.com/refraction-networking/watm/tinygo/v0/examples/utls/lib" - v0net "github.com/refraction-networking/watm/tinygo/v0/net" -) - -// type guard: ReverseWrappingTransport must implement [v0.WrappingTransport]. -var _ v0.WrappingTransport = (*UTLSClientWrappingTransport)(nil) - -type UTLSClientWrappingTransport struct { - tlsConfig *tls.Config - clientHelloID tls.ClientHelloID -} - -func (uwt *UTLSClientWrappingTransport) Wrap(conn v0net.Conn) (v0net.Conn, error) { - if uwt.tlsConfig == nil { - uwt.tlsConfig = &tls.Config{InsecureSkipVerify: true} - } - - var emptyClientHelloID tls.ClientHelloID - if uwt.clientHelloID == emptyClientHelloID { - uwt.clientHelloID = tls.HelloChrome_Auto - } - - tlsConn := tls.UClient(conn, uwt.tlsConfig, uwt.clientHelloID) - if err := tlsConn.Handshake(); err != nil { - return nil, err - } - - if err := conn.SetNonBlock(true); err != nil { - return nil, err - } - - return &UTLSConn{ - Conn: conn, - tlsConn: tlsConn, - }, nil -} - -var _ v0.ConfigurableTransport = (*UTLSClientWrappingTransport)(nil) - -func (uwt *UTLSClientWrappingTransport) Configure(config []byte) error { - configurables := &lib.Configurables{} - if err := tinyjson.Unmarshal(config, configurables); err != nil { - return err - } - - uwt.tlsConfig = configurables.GetTLSConfig() - uwt.clientHelloID = configurables.GetClientHelloID() - - return nil -} - -type UTLSConn struct { - v0net.Conn // embedded Conn - tlsConn *tls.UConn -} - -func (uc *UTLSConn) Read(b []byte) (n int, err error) { - return uc.tlsConn.Read(b) -} - -func (uc *UTLSConn) Write(b []byte) (n int, err error) { - return uc.tlsConn.Write(b) -} diff --git a/tinygo/v0/exports.go b/tinygo/v0/exports.go deleted file mode 100644 index 23ca5b0..0000000 --- a/tinygo/v0/exports.go +++ /dev/null @@ -1,235 +0,0 @@ -package v0 - -import ( - "bytes" - "errors" - "log" - "os" - "syscall" - - v0net "github.com/refraction-networking/watm/tinygo/v0/net" - "github.com/refraction-networking/watm/wasip1" -) - -// Export the WATM version indicator. -// -// gaukas: I noticed that in Rust we can export a const variable -// but here in Go we have to export a function instead. Luckily -// in our standard we are not checking against its type but only -// the name. -// -//export _water_v0 -func _water_v0() {} - -//export _water_init -func _water_init() int32 { - // Check if dialer/listener/relay is configurable. If so, - // pull the config file from the host and configure them. - dct := d.ConfigurableTransport() - lct := l.ConfigurableTransport() - // rct := r.ConfigurableTransport() - if dct != nil || lct != nil /* || rct != nil */ { - config, err := readConfig() - if err == nil || config != nil { - if dct != nil { - dct.Configure(config) - } - - if lct != nil { - lct.Configure(config) - } - - // if rct != nil { - // rct.Configure(config) - // } - } else if !errors.Is(err, syscall.EACCES) { // EACCES means no config file provided by the host - return wasip1.EncodeWATERError(err.(syscall.Errno)) - } - } - - // TODO: initialize the dialer, listener, and relay - d.Initialize() - l.Initialize() - // r.Initialize() - - return 0 // ESUCCESS -} - -func readConfig() (config []byte, err error) { - fd, err := wasip1.DecodeWATERError(_import_pull_config()) - if err != nil { - return nil, err - } - - file := os.NewFile(uintptr(fd), "config") - if file == nil { - return nil, syscall.EBADF - } - - // read the config file - buf := new(bytes.Buffer) - _, err = buf.ReadFrom(file) - if err != nil { - log.Println("readConfig: (*bytes.Buffer).ReadFrom:", err) - return nil, syscall.EIO - } - - config = buf.Bytes() - - // close the file - if err := file.Close(); err != nil { - return config, syscall.EIO - } - - return config, nil -} - -//export _water_cancel_with -func _water_cancel_with(cancelFd int32) int32 { - cancelConn = v0net.RebuildTCPConn(cancelFd) - if err := cancelConn.(v0net.Conn).SetNonBlock(true); err != nil { - log.Printf("dial: cancelConn.SetNonblock: %v", err) - return wasip1.EncodeWATERError(err.(syscall.Errno)) - } - - 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/imports_unsupported.go b/tinygo/v0/imports_unsupported.go deleted file mode 100644 index b064e9d..0000000 --- a/tinygo/v0/imports_unsupported.go +++ /dev/null @@ -1,31 +0,0 @@ -//go:build !wasip1 && !wasi - -package v0 - -import ( - "syscall" - "time" - "unsafe" - - "github.com/refraction-networking/watm/wasip1" -) - -func _import_host_defer() { - // just do nothing, since nothing really matters if not - // commanded by the host. -} - -// emulate the behavior when no config is provided on -// the host side. -func _import_pull_config() (fd int32) { - return wasip1.EncodeWATERError(syscall.ENOENT) -} - -// emulate the behavior when no file descriptors are -// ready and the timeout expires immediately. -func poll_oneoff(in, out unsafe.Pointer, nsubscriptions uint32, nevents unsafe.Pointer) uint32 { - // wait for a very short period to simulate the polling - time.Sleep(50 * time.Millisecond) - *(*uint32)(nevents) = nsubscriptions - return 0 -} diff --git a/tinygo/v0/imports_wasi.go b/tinygo/v0/imports_wasi.go deleted file mode 100644 index 9e23938..0000000 --- a/tinygo/v0/imports_wasi.go +++ /dev/null @@ -1,17 +0,0 @@ -//go:build wasip1 || wasi - -package v0 - -import "unsafe" - -//go:wasmimport env host_defer -//go:noescape -func _import_host_defer() - -//go:wasmimport env pull_config -//go:noescape -func _import_pull_config() (fd int32) - -//go:wasmimport wasi_snapshot_preview1 poll_oneoff -//go:noescape -func poll_oneoff(in, out unsafe.Pointer, nsubscriptions size, nevents unsafe.Pointer) errno diff --git a/tinygo/v0/listener.go b/tinygo/v0/listener.go deleted file mode 100644 index 2c333f2..0000000 --- a/tinygo/v0/listener.go +++ /dev/null @@ -1,52 +0,0 @@ -package v0 - -type listener struct { - wt WrappingTransport - // lt ListeningTransport -} - -func (l *listener) ConfigurableTransport() ConfigurableTransport { - if l.wt != nil { - if wt, ok := l.wt.(ConfigurableTransport); ok { - return wt - } - } - - // if l.lt != nil { - // if lt, ok := l.lt.(ConfigurableTransport); ok { - // return lt - // } - // } - - return nil -} - -func (l *listener) Initialize() { - // TODO: allow initialization on listener -} - -var l listener - -// BuildListenerWithWrappingTransport arms the listener with a -// [WrappingTransport] that is used to wrap a [v0net.Conn] into -// another [net.Conn] by providing some high-level application -// layer protocol. -// -// Mutually exclusive with [BuildListenerWithListeningTransport]. -func BuildListenerWithWrappingTransport(wt WrappingTransport) { - l.wt = wt - // l.lt = nil -} - -// BuildListenerWithListeningTransport arms the listener with a -// [ListeningTransport] that is used to accept incoming connections -// on a local address and provide high-level application layer -// protocol over the accepted connection. -// -// Mutually exclusive with [BuildListenerWithWrappingTransport]. -func BuildListenerWithListeningTransport(lt ListeningTransport) { - // TODO: implement BuildListenerWithListeningTransport - // l.lt = lt - // l.wt = nil - panic("BuildListenerWithListeningTransport: not implemented") -} diff --git a/tinygo/v0/net/conn.go b/tinygo/v0/net/conn.go deleted file mode 100644 index b1ef0d4..0000000 --- a/tinygo/v0/net/conn.go +++ /dev/null @@ -1,224 +0,0 @@ -package net - -import ( - "errors" - "io" - "net" - "os" - "syscall" - "time" -) - -// Conn is the interface for a generic stream-oriented network connection. -type Conn interface { - net.Conn - syscall.Conn - SetNonBlock(nonblocking bool) error - Fd() int32 -} - -// type guard: *TCPConn must implement Conn -var _ Conn = (*TCPConn)(nil) - -// TCPConn is a wrapper around a file descriptor that implements the [net.Conn]. -// -// Despite the name, this type is not specific to TCP connections. It can be used -// for any file descriptor that is connection-oriented. -type TCPConn struct { - rawConn *rawTCPConn - - readDeadline time.Time - writeDeadline time.Time -} - -// RebuildTCPConn recovers a [TCPConn] from a file descriptor. -func RebuildTCPConn(fd int32) *TCPConn { - return &TCPConn{ - rawConn: &rawTCPConn{ - fd: fd, - }, - } -} - -// Read implements [net.Conn.Read]. -func (c *TCPConn) Read(b []byte) (n int, err error) { - if rdl := c.readDeadline; rdl.IsZero() { - // 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(syscallFd(fd), b) - if n == 0 && err == nil { - err = io.EOF - } - if n < 0 && err != nil { - n = 0 - } - return n, err - }) - } else { - // readDeadline is set, if EAGAIN/EWOULDBLOCK is returned, - // we retry until the deadline is reached. - for { - if n, err = syscallFnFd(c.rawConn, func(fd uintptr) (int, error) { - n, err := syscall.Read(syscallFd(fd), b) - if n == 0 && err == nil { - err = io.EOF - } - if n < 0 && err != nil { - n = 0 - } - return n, err - }); errors.Is(err, syscall.EAGAIN) { - if time.Now().Before(rdl) { - continue - } - } - return n, err - } - } -} - -// Write implements [net.Conn.Write]. -func (c *TCPConn) Write(b []byte) (n int, writeErr error) { - if err := c.rawConn.Write(func(fd uintptr) (done bool) { - n, writeErr = writeFD(fd, b) - if errors.Is(writeErr, syscall.EAGAIN) { - if wdl := c.writeDeadline; wdl.IsZero() || time.Now().Before(wdl) { - return false - } - writeErr = os.ErrDeadlineExceeded - } - return true - }); err != nil { - return n, err - } - return -} - -// Close implements [net.Conn.Close]. -func (c *TCPConn) Close() error { - // if shutdownErr := syscallControlFd(c.rawConn, func(fd uintptr) error { - // return syscall.Shutdown(int(fd), syscall.SHUT_RDWR) - // }); shutdownErr != nil { - // return shutdownErr - // } else { - // return syscallControlFd(c.rawConn, func(fd uintptr) error { - // return syscall.Close(int(fd)) - // }) - // } - return syscallControlFd(c.rawConn, func(fd uintptr) error { - return syscall.Close(syscallFd(fd)) - }) -} - -// LocalAddr implements [net.Conn.LocalAddr]. -// -// This function is currently not implemented, but may be fleshed out in the -// future should there be better support for getting the local address of a -// socket managed by the Go runtime. -func (c *TCPConn) LocalAddr() net.Addr { - return nil -} - -// RemoteAddr implements [net.Conn.RemoteAddr]. -// -// This function is currently not implemented, but may be fleshed out in the -// future should there be better support for getting the remote address of a -// socket managed by the Go runtime. -func (c *TCPConn) RemoteAddr() net.Addr { - return nil -} - -// SetDeadline implements [net.Conn.SetDeadline]. -func (c *TCPConn) SetDeadline(t time.Time) error { - c.readDeadline = t - c.writeDeadline = t - - // set deadline will enable non-blocking mode - return c.SetNonBlock(true) -} - -// SetReadDeadline implements [net.Conn.SetReadDeadline]. -func (c *TCPConn) SetReadDeadline(t time.Time) error { - c.readDeadline = t - - // set deadline will enable non-blocking mode - return c.SetNonBlock(true) -} - -// SetWriteDeadline implements [net.Conn.SetWriteDeadline]. -func (c *TCPConn) SetWriteDeadline(t time.Time) error { - c.writeDeadline = t - - return nil -} - -// SyscallConn implements [syscall.Conn]. -func (c *TCPConn) SyscallConn() (syscall.RawConn, error) { - return c.rawConn, nil -} - -// SetNonBlock sets the socket to blocking or non-blocking mode. -func (c *TCPConn) SetNonBlock(nonblocking bool) error { - return syscallControlFd(c.rawConn, func(fd uintptr) error { - if errno := syscallSetNonblock(fd, nonblocking); errno != nil && !errors.Is(errno, syscall.Errno(0)) { - return errno - } else { - return nil - } - }) -} - -// Fd returns the file descriptor of the socket. -func (c *TCPConn) Fd() int32 { - return c.rawConn.fd -} - -// type guard: *rawTCPConn must implement [syscall.RawConn]. -var _ syscall.RawConn = (*rawTCPConn)(nil) - -// rawTCPConn is a wrapper around a file descriptor that implements the -// [syscall.RawConn] interface for some syscalls. -type rawTCPConn struct { - fd int32 -} - -// Control implements [syscall.RawConn.Control]. -func (rt *rawTCPConn) Control(f func(fd uintptr)) error { - if rt.fd == 0 { - return syscall.EBADF - } - - f(uintptr(rt.fd)) - return nil -} - -// Read implements [syscall.RawConn.Read]. -// -// Deprecated: Use [net.Conn.Read] instead. -func (rt *rawTCPConn) Read(f func(fd uintptr) (done bool)) error { - if rt.fd == 0 { - return syscall.EBADF - } - - for { - if f(uintptr(rt.fd)) { - return nil - } - } -} - -// Write implements [syscall.RawConn.Write]. -// -// Deprecated: Use [net.Conn.Write] instead. -func (rt *rawTCPConn) Write(f func(fd uintptr) (done bool)) error { - if rt.fd == 0 { - return syscall.EBADF - } - - for { - if f(uintptr(rt.fd)) { - return nil - } - } -} diff --git a/tinygo/v0/net/conn_test.go b/tinygo/v0/net/conn_test.go deleted file mode 100644 index 2600f73..0000000 --- a/tinygo/v0/net/conn_test.go +++ /dev/null @@ -1,267 +0,0 @@ -//go:build !wasip1 && !wasi - -package net_test - -import ( - "bytes" - "errors" - "io" - "net" - "runtime" - "syscall" - "testing" - "time" - - v0net "github.com/refraction-networking/watm/tinygo/v0/net" -) - -func tcpConnPair() (*net.TCPConn, *net.TCPConn, error) { - l, err := net.Listen("tcp", "localhost:0") - if err != nil { - return nil, nil, err - } - - dialConn, err := net.Dial("tcp", l.Addr().String()) - if err != nil { - return nil, nil, err - } - - acceptConn, err := l.Accept() - if err != nil { - return nil, nil, err - } - - return dialConn.(*net.TCPConn), acceptConn.(*net.TCPConn), nil -} - -func TestTCPConn_Read(t *testing.T) { - conn1, conn2, err := tcpConnPair() - if err != nil { - t.Fatal(err) - } - defer conn1.Close() - defer conn2.Close() - - var msgWr = []byte("hello") - var bufRd = make([]byte, 16) - - if _, err := conn1.Write(msgWr); err != nil { - t.Fatal(err) - } - - // rebuild conn2 as a *TCPConn - var tcpConn2 *v0net.TCPConn - - // expose conn2's fd - rawConn2, err := conn2.SyscallConn() - if err != nil { - t.Fatal(err) - } else if err := rawConn2.Control(func(fd uintptr) { - tcpConn2 = v0net.RebuildTCPConn(int32(fd)) - }); err != nil { - t.Fatal(err) - } - - n, err := tcpConn2.Read(bufRd) - if err != nil { - t.Fatal(err) - } - - if !bytes.Equal(bufRd[:n], msgWr) { - t.Fatalf("read: expected %s, got %s", msgWr, bufRd[:n]) - } - - // close the peer connection and read again - conn1.Close() - if _, err := tcpConn2.Read(bufRd); err == nil { - t.Fatal("read after peer-close: expected error, got nil") - } - - runtime.KeepAlive(conn1) - runtime.KeepAlive(conn2) -} - -func TestTCPConn_Write(t *testing.T) { - conn1, conn2, err := tcpConnPair() - if err != nil { - t.Fatal(err) - } - defer conn1.Close() - defer conn2.Close() - - var msgWr = []byte("hello") - var bufRd = make([]byte, 16) - - // rebuild conn1 as a *TCPConn - var tcpConn1 *v0net.TCPConn - - // expose conn1's fd - rawConn1, err := conn1.SyscallConn() - if err != nil { - t.Fatal(err) - } else if err := rawConn1.Control(func(fd uintptr) { - tcpConn1 = v0net.RebuildTCPConn(int32(fd)) - }); err != nil { - t.Fatal(err) - } - - if _, err := tcpConn1.Write(msgWr); err != nil { - t.Fatal(err) - } - - n, err := conn2.Read(bufRd) - if err != nil { - t.Fatal(err) - } - - if !bytes.Equal(bufRd[:n], msgWr) { - t.Fatalf("expected %s, got %s", msgWr, bufRd[:n]) - } - - runtime.KeepAlive(conn1) - runtime.KeepAlive(conn2) -} - -func TestTCPConn_Close(t *testing.T) { - conn1, conn2, err := tcpConnPair() - if err != nil { - t.Fatal(err) - } - defer conn1.Close() - defer conn2.Close() - - // rebuild conn1 as a *TCPConn - var tcpConn1 *v0net.TCPConn - - // expose conn1's fd - rawConn1, err := conn1.SyscallConn() - if err != nil { - t.Fatal(err) - } else if err := rawConn1.Control(func(fd uintptr) { - tcpConn1 = v0net.RebuildTCPConn(int32(fd)) - }); err != nil { - t.Fatal(err) - } - - if err := tcpConn1.Close(); err != nil { - t.Fatal(err) - } - - // At this point, conn1 (and the rebuilt tcpConn1) should not be writable - if _, err := conn1.Write([]byte("hello")); err == nil { - t.Fatal("expected error, got nil") - } else if _, err := tcpConn1.Write([]byte("hello")); err == nil { - t.Fatal("expected error, got nil") - } - - // Similarly, all tcpConn1, conn1 and conn2 should not be readable - var bufRd = make([]byte, 16) - if _, err := tcpConn1.Read(bufRd); err == nil { - t.Fatal("expected error, got nil") - } else if _, err := conn1.Read(bufRd); err == nil { - t.Fatal("expected error, got nil") - } else if _, err := conn2.Read(bufRd); !errors.Is(err, io.EOF) && !errors.Is(err, syscall.ECONNRESET) { - t.Fatalf("expected io.EOF or syscall.ECONNRESET, got %v", err) - } - - runtime.KeepAlive(conn1) - runtime.KeepAlive(conn2) -} - -func TestTCPConn_SetNonBlock(t *testing.T) { - conn1, conn2, err := tcpConnPair() - if err != nil { - t.Fatal(err) - } - defer conn1.Close() - defer conn2.Close() - - // rebuild conn1 as a *TCPConn - var tcpConn1 *v0net.TCPConn - - // expose conn1's fd - rawConn1, err := conn1.SyscallConn() - if err != nil { - t.Fatal(err) - } else if err := rawConn1.Control(func(fd uintptr) { - tcpConn1 = v0net.RebuildTCPConn(int32(fd)) - }); err != nil { - t.Fatal(err) - } - - if err := tcpConn1.SetNonBlock(true); err != nil { - t.Fatal(err) - } - - // since conn2 has not been written to, tcpConn1 should not be readable. - if _, err := tcpConn1.Read(make([]byte, 16)); !errors.Is(err, syscall.EAGAIN) { - t.Fatalf("expected %s, got %s", syscall.EAGAIN, err) - } - - // write to conn2 - if _, err := conn2.Write([]byte("hello")); err != nil { - t.Fatal(err) - } - time.Sleep(10 * time.Microsecond) // wait for the packet to get through - - // now tcpConn1 should be readable - if _, err := tcpConn1.Read(make([]byte, 16)); err != nil { - t.Fatal(err) - } - - runtime.KeepAlive(conn1) - runtime.KeepAlive(conn2) -} - -func TestTCPConn_SetReadDeadline(t *testing.T) { - conn1, conn2, err := tcpConnPair() - if err != nil { - t.Fatal(err) - } - defer conn1.Close() - defer conn2.Close() - - // rebuild conn1 as a *TCPConn - var tcpConn1 *v0net.TCPConn - - // expose conn1's fd - rawConn1, err := conn1.SyscallConn() - if err != nil { - t.Fatal(err) - } else if err := rawConn1.Control(func(fd uintptr) { - tcpConn1 = v0net.RebuildTCPConn(int32(fd)) - }); err != nil { - t.Fatal(err) - } - - // set read deadline to 10ms later - timeStart := time.Now() - if err := tcpConn1.SetReadDeadline(timeStart.Add(10 * time.Millisecond)); err != nil { - t.Fatal(err) - } - - // since conn2 has not been written to, tcpConn1 should not be readable. - if _, err := tcpConn1.Read(make([]byte, 16)); !errors.Is(err, syscall.EAGAIN) { - t.Fatalf("expected %s, got %s", syscall.EAGAIN, err) - } - if time.Since(timeStart) < 10*time.Millisecond { - t.Fatalf("expected read to block for at least 10ms, but it only blocked for %s", time.Since(timeStart)) - } - - // write to conn2 - if _, err := conn2.Write([]byte("hello")); err != nil { - t.Fatal(err) - } - - // now tcpConn1 should be readable - if err := tcpConn1.SetReadDeadline(time.Now().Add(10 * time.Millisecond)); err != nil { - t.Fatal(err) - } - - if _, err := tcpConn1.Read(make([]byte, 16)); err != nil { - t.Fatal(err) - } - - runtime.KeepAlive(conn1) - runtime.KeepAlive(conn2) -} diff --git a/tinygo/v0/net/dial.go b/tinygo/v0/net/dial.go deleted file mode 100644 index b90d753..0000000 --- a/tinygo/v0/net/dial.go +++ /dev/null @@ -1,24 +0,0 @@ -package net - -import ( - "github.com/refraction-networking/watm/wasip1" -) - -// Dial dials a remote host for a network connection. -// -// In v0, network and address parameters are ignored, and -// it is internally called as [HostManagedDial]. -func Dial(_, _ string) (Conn, error) { - return HostManagedDial() -} - -// HostManagedDial asks the host to dial a remote host predefined -// by the host. -func HostManagedDial() (Conn, error) { - fd, err := wasip1.DecodeWATERError(_import_host_dial()) - if err != nil { - return nil, err - } - - return RebuildTCPConn(fd), nil -} diff --git a/tinygo/v0/net/fd.go b/tinygo/v0/net/fd.go deleted file mode 100644 index 73da9a8..0000000 --- a/tinygo/v0/net/fd.go +++ /dev/null @@ -1,47 +0,0 @@ -package net - -import "syscall" - -// writeFD writes data to the file descriptor fd. When a partial write occurs, -// it will continue with the remaining data until all data is written or an -// error occurs. If no progress is made in a single write call, it will return -// syscall.EIO. -// -// It is ported from (*FD).Write in golang/go/src/internal/poll/fd_unix.go -func writeFD(fd uintptr, p []byte) (int, error) { - var nn int - for { - n, err := ignoringEINTRIO(syscall.Write, syscallFd(fd), p[nn:]) - if n > 0 { - nn += n - } - if nn == len(p) { - return nn, err - } - if err != nil { - return nn, err - } - if n == 0 { - return nn, syscall.EIO - } - - // // TODO: retry if EAGAIN or no progress? - // if n == 0 { - // noprogress++ - // } - // if noprogress == 10 { - // return nn, syscall.EIO - // } - // runtime.Gosched() - } -} - -// ignoringEINTRIO is like ignoringEINTR, but just for IO calls. -func ignoringEINTRIO(fn func(fd syscallFd, p []byte) (int, error), fd syscallFd, p []byte) (int, error) { - for { - n, err := fn(fd, p) - if err != syscall.EINTR { - return n, err - } - } -} diff --git a/tinygo/v0/net/import.go b/tinygo/v0/net/import.go deleted file mode 100644 index 253851e..0000000 --- a/tinygo/v0/net/import.go +++ /dev/null @@ -1,29 +0,0 @@ -//go:build !wasip1 && !wasi - -package net - -var hostDialedFD int32 = -1 - -func SetHostDialedFD(fd int32) { - hostDialedFD = fd -} - -// This function should be imported from the host in WASI. -// On non-WASI platforms, it mimicks the behavior of the host -// by returning a file descriptor of preset value. -func _import_host_dial() (fd int32) { - return hostDialedFD -} - -var hostAcceptedFD int32 = -1 - -func SetHostAcceptedFD(fd int32) { - hostAcceptedFD = fd -} - -// This function should be imported from the host in WASI. -// On non-WASI platforms, it mimicks the behavior of the host -// by returning a file descriptor of preset value. -func _import_host_accept() (fd int32) { - return hostAcceptedFD -} diff --git a/tinygo/v0/net/import_wasi.go b/tinygo/v0/net/import_wasi.go deleted file mode 100644 index 34ee0fd..0000000 --- a/tinygo/v0/net/import_wasi.go +++ /dev/null @@ -1,31 +0,0 @@ -//go:build wasi || wasip1 - -package net - -import "unsafe" - -// Import the host-imported dialer function. -// -//go:wasmimport env host_dial -//go:noescape -func _import_host_dial() (fd int32) - -// Import the host-imported acceptor function. -// -//go:wasmimport env host_accept -//go:noescape -func _import_host_accept() (fd int32) - -// Import wasi_snapshot_preview1's fd_fdstat_set_flags function -// until tinygo supports it. -// -//go:wasmimport wasi_snapshot_preview1 fd_fdstat_set_flags -//go:noescape -func fd_fdstat_set_flags(fd int32, flags uint32) uint32 - -// Import wasi_snapshot_preview1's fd_fdstat_set_flags function -// until tinygo supports it. -// -//go:wasmimport wasi_snapshot_preview1 fd_fdstat_get -//go:noescape -func fd_fdstat_get(fd int32, buf unsafe.Pointer) uint32 diff --git a/tinygo/v0/net/listener.go b/tinygo/v0/net/listener.go deleted file mode 100644 index 9f61f71..0000000 --- a/tinygo/v0/net/listener.go +++ /dev/null @@ -1,33 +0,0 @@ -package net - -import "github.com/refraction-networking/watm/wasip1" - -// Listener is the interface for a generic network listener. -type Listener interface { - Accept() (Conn, error) -} - -// type guard: *TCPListener must implement Listener -var _ Listener = (*TCPListener)(nil) - -// TCPListener is a fake TCP listener which calls to the host -// to accept a connection. -// -// By saying "fake", it means that the file descriptor is not -// managed inside the WATM, but by the host application. -type TCPListener struct { -} - -func (l *TCPListener) Accept() (Conn, error) { - return HostManagedAccept() -} - -// HostManagedAccept asks the host to accept a connection. -func HostManagedAccept() (Conn, error) { - fd, err := wasip1.DecodeWATERError(_import_host_accept()) - if err != nil { - return nil, err - } - - return RebuildTCPConn(fd), nil -} diff --git a/tinygo/v0/net/syscall.go b/tinygo/v0/net/syscall.go deleted file mode 100644 index d8b6b8e..0000000 --- a/tinygo/v0/net/syscall.go +++ /dev/null @@ -1,24 +0,0 @@ -package net - -import ( - "syscall" -) - -func syscallControlFd(rawConn syscall.RawConn, f func(fd uintptr) error) (err error) { - if controlErr := rawConn.Control(func(fd uintptr) { - err = f(fd) - }); controlErr != nil { - // panic(fmt.Sprintf("controlErr = %v", controlErr)) - return controlErr - } - return err -} - -func syscallFnFd(rawConn syscall.RawConn, f func(fd uintptr) (int, error)) (n int, err error) { - if controlErr := rawConn.Control(func(fd uintptr) { - n, err = f(fd) - }); controlErr != nil { - return 0, controlErr - } - return n, err -} diff --git a/tinygo/v0/net/syscall_unix.go b/tinygo/v0/net/syscall_unix.go deleted file mode 100644 index faa6406..0000000 --- a/tinygo/v0/net/syscall_unix.go +++ /dev/null @@ -1,11 +0,0 @@ -//go:build unix && !wasi && !wasip1 - -package net - -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 deleted file mode 100644 index 245b48c..0000000 --- a/tinygo/v0/net/syscall_wasi.go +++ /dev/null @@ -1,48 +0,0 @@ -//go:build wasi || wasip1 - -package net - -import ( - "syscall" - "unsafe" -) - -func syscallSetNonblock(fd uintptr, nonblocking bool) error { - flags, err := fd_fdstat_get_flags(int32(fd)) - if err != nil { - return err - } - if nonblocking { - flags |= FDFLAG_NONBLOCK - } else { - flags &^= FDFLAG_NONBLOCK - } - errno := fd_fdstat_set_flags(int32(fd), flags) - return syscall.Errno(errno) -} - -func fd_fdstat_get_flags(fd int32) (uint32, error) { - var stat fdstat - errno := fd_fdstat_get(fd, unsafe.Pointer(&stat)) - if errno != 0 { - return 0, syscall.Errno(errno) - } - return uint32(stat.fdflags), nil -} - -type fdstat struct { - filetype uint8 - fdflags uint16 - rightsBase uint64 - rightsInheriting uint64 -} - -const ( - FDFLAG_APPEND = 0x0001 - FDFLAG_DSYNC = 0x0002 - FDFLAG_NONBLOCK = 0x0004 - 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 deleted file mode 100644 index 1de3513..0000000 --- a/tinygo/v0/net/syscall_windows.go +++ /dev/null @@ -1,35 +0,0 @@ -//go:build windows - -package net - -import ( - "syscall" - "unsafe" -) - -const ( - _FIONBIO = 0x8004667e -) - -var ( - // modws2_32 is WinSock. - modws2_32 = syscall.NewLazyDLL("ws2_32.dll") - // procioctlsocket exposes ioctlsocket from WinSock. - procioctlsocket = modws2_32.NewProc("ioctlsocket") -) - -func syscallSetNonblock(fd uintptr, nonblocking bool) (err error) { - opt := uint64(0) - if nonblocking { - opt = 1 - } - // ioctlsocket(fd, FIONBIO, &opt) - _, _, errno := syscall.SyscallN( - procioctlsocket.Addr(), - uintptr(fd), - uintptr(_FIONBIO), - uintptr(unsafe.Pointer(&opt))) - return errno -} - -type syscallFd = syscall.Handle diff --git a/tinygo/v0/netpoll.go b/tinygo/v0/netpoll.go deleted file mode 100644 index 3379051..0000000 --- a/tinygo/v0/netpoll.go +++ /dev/null @@ -1,137 +0,0 @@ -// 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. - -package v0 - -import ( - "fmt" - "syscall" - "unsafe" -) - -// WASI network poller. -// -// WASI preview 1 includes a poll_oneoff host function that behaves similarly -// to poll(2) on Linux. Like poll(2), poll_oneoff is level triggered. It -// accepts one or more subscriptions to FD read or write events. -// -// Major differences to poll(2): -// - the events are not written to the input entries (like pollfd.revents), and -// instead are appended to a separate events buffer. poll_oneoff writes zero -// or more events to the buffer (at most one per input subscription) and -// returns the number of events written. Although the index of the -// subscriptions might not match the index of the associated event in the -// events buffer, both the subscription and event structs contain a userdata -// field and when a subscription yields an event the userdata fields will -// match. -// - there's no explicit timeout parameter, although a time limit can be added -// by using "clock" subscriptions. -// - each FD subscription can either be for a read or a write, but not both. -// This is in contrast to poll(2) which accepts a mask with POLLIN and -// POLLOUT bits, allowing for a subscription to either, neither, or both -// reads and writes. -// -// Since poll_oneoff is similar to poll(2), the implementation here was derived -// from netpoll_aix.go. - -const ( - EventFdRead uint16 = iota + 1 // readable event - EventFdWrite // writeable event -) - -var ( - evts []event - subs []subscription -) - -type pollFd struct { - fd uintptr - events uint16 - revents uint16 // todo -} - -func _poll(fds []pollFd, maxTimeout int64) (nevents int32, err error) { - // Unlike poll(2), WASI's poll_oneoff doesn't accept a timeout directly. To - // prevent it from blocking indefinitely, a clock subscription with a - // timeout field needs to be submitted. Reserve a slot here for the clock - // subscription, and set fields that won't change between poll_oneoff calls. - - subs = make([]subscription, 1, 128) - evts = make([]event, 0, 128) - - timeout := &subs[0] - eventtype := timeout.u.eventtype() - *eventtype = eventtypeClock - clock := timeout.u.subscriptionClock() - clock.id = clockMonotonic - clock.precision = 1e3 - - // construct the subscriptions - for _, fd := range fds { - sub := subscription{} - sub.userdata = userdata(fd.fd) - subscription := sub.u.subscriptionFdReadwrite() - subscription.fd = int32(fd.fd) - if fd.events == EventFdRead { - eventtype := sub.u.eventtype() - *eventtype = eventtypeFdRead - } else if fd.events == EventFdWrite { - eventtype := sub.u.eventtype() - *eventtype = eventtypeFdWrite - } else { - panic(fmt.Sprintf("invalid event type: %d", fd.events)) - } - subs = append(subs, sub) - } - - // If maxTimeout >= 0, we include a subscription of type Clock that we use as - // a timeout. If maxTimeout < 0, we omit the subscription and allow poll_oneoff - // to block indefinitely. - pollsubs := subs - if maxTimeout >= 0 { - timeout := &subs[0] - clock := timeout.u.subscriptionClock() - clock.timeout = uint64(maxTimeout) - } else { - pollsubs = subs[1:] - } - - if len(pollsubs) == 0 { - return 0, nil - } - - evts = evts[:len(pollsubs)] - for i := range evts { - evts[i] = event{} - } - -retry: - errno := poll_oneoff(unsafe.Pointer(&pollsubs[0]), unsafe.Pointer(&evts[0]), uint32(len(pollsubs)), unsafe.Pointer(&nevents)) - if errno != 0 { - if errno != uint32(syscall.EINTR) { - return 0, syscall.Errno(errno) - } - - // If a timed sleep was interrupted, just return to - // let the caller retry. - if maxTimeout > 0 { - return 0, syscall.EAGAIN - } - goto retry - } - - // go through all events and see if any event.error is not ESUCCESS - lastEvtError := uint16(0) - for i, evt := range evts { - if evt.error != 0 { - fds[i].revents = evt.error - lastEvtError = evt.error - } - } - if lastEvtError != 0 { - return int32(nevents), syscall.Errno(lastEvtError) - } - - return int32(nevents), nil -} diff --git a/tinygo/v0/os.go b/tinygo/v0/os.go deleted file mode 100644 index c094cbb..0000000 --- a/tinygo/v0/os.go +++ /dev/null @@ -1,133 +0,0 @@ -// 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. - -package v0 - -import "unsafe" - -// GOARCH=wasm currently has 64 bits pointers, but the WebAssembly host expects -// pointers to be 32 bits so we use this type alias to represent pointers in -// structs and arrays passed as arguments to WASI functions. -// -// Note that the use of an integer type prevents the compiler from tracking -// pointers passed to WASI functions, so we must use KeepAlive to explicitly -// retain the objects that could otherwise be reclaimed by the GC. -type uintptr32 = uint32 - -// https://github.com/WebAssembly/WASI/blob/a2b96e81c0586125cc4dc79a5be0b78d9a059925/legacy/preview1/docs.md#-size-u32 -type size = uint32 - -// https://github.com/WebAssembly/WASI/blob/a2b96e81c0586125cc4dc79a5be0b78d9a059925/legacy/preview1/docs.md#-errno-variant -type errno = uint32 - -// https://github.com/WebAssembly/WASI/blob/a2b96e81c0586125cc4dc79a5be0b78d9a059925/legacy/preview1/docs.md#-filesize-u64 -type filesize = uint64 - -// https://github.com/WebAssembly/WASI/blob/a2b96e81c0586125cc4dc79a5be0b78d9a059925/legacy/preview1/docs.md#-timestamp-u64 -type timestamp = uint64 - -// https://github.com/WebAssembly/WASI/blob/a2b96e81c0586125cc4dc79a5be0b78d9a059925/legacy/preview1/docs.md#-clockid-variant -type clockid = uint32 - -const ( - clockRealtime clockid = 0 - clockMonotonic clockid = 1 -) - -// https://github.com/WebAssembly/WASI/blob/a2b96e81c0586125cc4dc79a5be0b78d9a059925/legacy/preview1/docs.md#-iovec-record -type iovec struct { - buf uintptr32 - bufLen size -} - -type eventtype = uint8 - -const ( - eventtypeClock eventtype = iota - eventtypeFdRead - eventtypeFdWrite -) - -type eventrwflags = uint16 - -const ( - fdReadwriteHangup eventrwflags = 1 << iota -) - -type userdata = uint64 - -// The go:wasmimport directive currently does not accept values of type uint16 -// in arguments or returns of the function signature. Most WASI imports return -// an errno value, which we have to define as uint32 because of that limitation. -// However, the WASI errno type is intended to be a 16 bits integer, and in the -// event struct the error field should be of type errno. If we used the errno -// type for the error field it would result in a mismatching field alignment and -// struct size because errno is declared as a 32 bits type, so we declare the -// error field as a plain uint16. -type event struct { - userdata userdata - error uint16 - typ eventtype - p [5]uint8 // padding to 16 bytes - fdReadwrite eventFdReadwrite -} - -type eventFdReadwrite struct { - nbytes filesize - flags eventrwflags -} - -type subclockflags = uint16 - -const ( - subscriptionClockAbstime subclockflags = 1 << iota -) - -type subscriptionClock struct { - id clockid - timeout timestamp - precision timestamp - flags subclockflags -} - -type subscriptionFdReadwrite struct { - fd int32 -} - -type subscription struct { - userdata userdata - u subscriptionUnion -} - -type subscriptionUnion [5]uint64 - -func (u *subscriptionUnion) eventtype() *eventtype { - return (*eventtype)(unsafe.Pointer(&u[0])) -} - -func (u *subscriptionUnion) subscriptionClock() *subscriptionClock { - return (*subscriptionClock)(unsafe.Pointer(&u[1])) -} - -func (u *subscriptionUnion) subscriptionFdReadwrite() *subscriptionFdReadwrite { - return (*subscriptionFdReadwrite)(unsafe.Pointer(&u[1])) -} - -func usleep(usec uint32) { - var in subscription - var out event - var nevents size - - eventtype := in.u.eventtype() - *eventtype = eventtypeClock - - subscription := in.u.subscriptionClock() - subscription.id = clockMonotonic - subscription.timeout = timestamp(usec) * 1e3 - subscription.precision = 1e3 - - if poll_oneoff(unsafe.Pointer(&in), unsafe.Pointer(&out), 1, unsafe.Pointer(&nevents)) != 0 { - panic("wasi_snapshot_preview1.poll_oneoff") - } -} diff --git a/tinygo/v0/pluggable_transports.go b/tinygo/v0/pluggable_transports.go deleted file mode 100644 index 36da358..0000000 --- a/tinygo/v0/pluggable_transports.go +++ /dev/null @@ -1,90 +0,0 @@ -package v0 - -import ( - v0net "github.com/refraction-networking/watm/tinygo/v0/net" -) - -// WrappingTransport is the most basic transport type. It wraps -// a [v0net.Conn] into another [v0net.Conn] by providing some -// high-level application layer protocol. -type WrappingTransport interface { - // Wrap wraps a v0net.Conn into another v0net.Conn with a protocol - // wrapper layer. - // - // The returned v0net.Conn is NOT by default set to non-blocking. - // It is the responsibility of the transport to make it - // non-blocking by calling v0net.Conn.SetNonblock. This is to - // allow some transport to perform blocking operations such as - // TLS handshake. - // - // The transport SHOULD provide non-blocking v0net.Conn.Read - // operation on the returned v0net.Conn if possible, otherwise - // the worker may block on reading from a blocking connection. - // And it is highly recommended to pass all funtions other than - // Read and Write to the underlying v0net.Conn from the underlying - // dialer function. - Wrap(v0net.Conn) (v0net.Conn, error) -} - -// DialingTransport is a transport type that can be used to dial -// a remote address and provide high-level application layer -// protocol over the dialed connection. -type DialingTransport interface { - // SetDialer sets the dialer function that is used to dial - // a remote address. - // - // In v0, the input parameter of the dialer function is - // unused inside the WATM, given the connection is always - // managed by the host application. - // - // The returned v0net.Conn is NOT by default set to non-blocking. - // It is the responsibility of the transport to make it - // non-blocking by calling v0net.Conn.SetNonblock. This is to - // allow some transport to perform blocking operations such as - // TLS handshake. - SetDialer(dialer func(network, address string) (v0net.Conn, error)) - - // Dial dials a remote address and returns a v0net.Conn that - // provides high-level application layer protocol over the - // dialed connection. - // - // The transport SHOULD provide non-blocking v0net.Conn.Read - // operation on the returned v0net.Conn if possible, otherwise - // the worker may block on reading from a blocking connection. - // And it is highly recommended to pass all funtions other than - // Read and Write to the underlying v0net.Conn from the underlying - // dialer function. - Dial(network, address string) (v0net.Conn, error) -} - -// ListeningTransport is a transport type that can be used to -// accept incoming connections on a local address and provide -// high-level application layer protocol over the accepted -// connection. -type ListeningTransport interface { - // SetListener sets the listener that is used to accept - // incoming connections. - // - // The returned v0net.Conn is not by default non-blocking. - // It is the responsibility of the transport to make it - // non-blocking if required by calling v0net.Conn.SetNonblock. - SetListener(listener v0net.Listener) - - // Accept accepts an incoming connection and returns a - // net.Conn that provides high-level application layer - // protocol over the accepted connection. - // - // The transport SHOULD provide non-blocking v0net.Conn.Read - // operation on the returned v0net.Conn if possible, otherwise - // the worker may block on reading from a blocking connection. - // And it is highly recommended to pass all funtions other than - // Read and Write to the underlying v0net.Conn from the underlying - // dialer function. - Accept() (v0net.Conn, error) -} - -// ConfigurableTransport is a transport type that can be configured -// with a config file in the form of a byte slice. -type ConfigurableTransport interface { - Configure(config []byte) error -} diff --git a/tinygo/v0/relay.go b/tinygo/v0/relay.go deleted file mode 100644 index 835d416..0000000 --- a/tinygo/v0/relay.go +++ /dev/null @@ -1,78 +0,0 @@ -package v0 - -type RelayWrapSelection bool - -const ( - RelayWrapRemote RelayWrapSelection = false - RelayWrapSource RelayWrapSelection = true -) - -type relay struct { - wt WrappingTransport - wrapSelection RelayWrapSelection - // lt ListeningTransport - // dt DialingTransport -} - -func (r *relay) ConfigurableTransport() ConfigurableTransport { - if r.wt != nil { - if wt, ok := r.wt.(ConfigurableTransport); ok { - return wt - } - } - - // if r.lt != nil { - // if lt, ok := r.lt.(ConfigurableTransport); ok { - // return lt - // } - // } - - // if r.dt != nil { - // if dt, ok := r.dt.(ConfigurableTransport); ok { - // return dt - // } - // } - - return nil -} - -func (r *relay) Initialize() { - // TODO: allow initialization on relay -} - -var r relay - -// BuildRelayWithWrappingTransport arms the relay with a -// [WrappingTransport] that is used to wrap a [v0net.Conn] into -// another [net.Conn] by providing some high-level application -// layer protocol. -// -// The caller MUST keep in mind that the [WrappingTransport] is -// used to wrap the connection to the remote address, not the -// connection from the source address (the dialing peer). -// To reverse this behavior, i.e., wrap the inbounding connection, -// set wrapSelection to [RelayWrapSource]. -// -// Mutually exclusive with [BuildRelayWithListeningDialingTransport]. -func BuildRelayWithWrappingTransport(wt WrappingTransport, wrapSelection RelayWrapSelection) { - r.wt = wt - r.wrapSelection = wrapSelection - // r.lt = nil - // r.dt = nil -} - -// BuildRelayWithListeningDialingTransport arms the relay with a -// [ListeningTransport] that is used to accept incoming connections -// on a local address and provide high-level application layer -// protocol over the accepted connection, and a [DialingTransport] -// that is used to dial a remote address and provide high-level -// application layer protocol over the dialed connection. -// -// Mutually exclusive with [BuildRelayWithWrappingTransport]. -func BuildRelayWithListeningDialingTransport(lt ListeningTransport, dt DialingTransport) { - // TODO: implement BuildRelayWithListeningDialingTransport - // r.lt = lt - // r.dt = dt - // r.wt = nil - panic("BuildRelayWithListeningDialingTransport: not implemented") -} diff --git a/tinygo/v0/worker.go b/tinygo/v0/worker.go deleted file mode 100644 index 203277a..0000000 --- a/tinygo/v0/worker.go +++ /dev/null @@ -1,294 +0,0 @@ -package v0 - -import ( - "io" - "log" - "net" - "runtime" - "syscall" - - v0net "github.com/refraction-networking/watm/tinygo/v0/net" - "github.com/refraction-networking/watm/wasip1" -) - -type identity uint8 - -var workerIdentity identity = identity_uninitialized - -const ( - identity_uninitialized identity = iota - identity_dialer - identity_listener - identity_relay -) - -var identityStrings = map[identity]string{ - identity_dialer: "dialer", - identity_listener: "listener", - identity_relay: "relay", -} - -var sourceConn v0net.Conn // sourceConn is used to communicate between WASM and the host application or a dialing party (for relay only) -var remoteConn v0net.Conn // remoteConn is used to communicate between WASM and a dialed remote destination (for dialer/relay) or a dialing party (for listener only) -var cancelConn v0net.Conn // cancelConn is used to cancel the entire worker. - -var workerFn func() int32 = unfairWorker // by default, use unfairWorker for better performance under mostly unidirectional I/O - -var readBuf []byte = make([]byte, 16384) // 16k buffer for reading - -// WorkerFairness sets the fairness of a worker. -// -// If sourceConn or remoteConn will not work in non-blocking mode, -// it is highly recommended to set fair to true, otherwise it is most -// likely that the worker will block on reading from a blocking -// connection forever and therefore make no progress in the other -// direction. -func WorkerFairness(fair bool) { - if fair { - workerFn = fairWorker - } else { - workerFn = unfairWorker - } -} - -func worker() int32 { - defer _import_host_defer() - - if sourceConn == nil || remoteConn == nil || cancelConn == nil { - log.Println("worker: worker: sourceConn, remoteConn, or cancelConn is nil") - return wasip1.EncodeWATERError(syscall.EBADF) // bad file descriptor - } - - return workerFn() -} - -// untilError executes the given function until non-nil error is returned -func untilError(f func() error) error { - var err error - for err == nil { - err = f() - } - return err -} - -// unfairWorker works on all three connections with a priority order -// of cancelConn > sourceConn > remoteConn. -// -// It keeps working on the current connection until it returns an error, -// and if the error is EAGAIN, it switches to the next connection. If the -// connection is not properly set to non-blocking mode, i.e., never returns -// EAGAIN, this function will block forever and never work on a lower priority -// connection. Thus it is called unfairWorker. -func unfairWorker() int32 { - for { - pollFd := []pollFd{ - { - fd: uintptr(cancelConn.Fd()), - events: EventFdRead, - }, - { - fd: uintptr(sourceConn.Fd()), - events: EventFdRead, - }, - { - fd: uintptr(remoteConn.Fd()), - events: EventFdRead, - }, - } - - n, err := _poll(pollFd, -1) - if n == 0 { // TODO: re-evaluate the condition - if err == nil || err == syscall.EAGAIN { - runtime.Gosched() // yield the current goroutine - continue - } - log.Println("worker: unfairWorker: _poll:", err) - return int32(err.(syscall.Errno)) - } - - // 1st priority: cancelConn - _, err = cancelConn.Read(readBuf) - if !(err == syscall.EAGAIN) { - if err == io.EOF || err == nil { - log.Println("worker: unfairWorker: cancelConn is closed") - return wasip1.EncodeWATERError(syscall.ECANCELED) // operation canceled - } - log.Println("worker: unfairWorker: cancelConn.Read:", err) - return wasip1.EncodeWATERError(syscall.EIO) // input/output error - } - - // 2nd priority: sourceConn - if err := untilError(func() error { - nRead, readErr := sourceConn.Read(readBuf) - if readErr != nil { - if readErr != syscall.EAGAIN { - log.Println("worker: unfairWorker: sourceConn.Read:", readErr) - } - return readErr - } - - nWritten, writeErr := remoteConn.Write(readBuf[:nRead]) - if writeErr != nil { - log.Println("worker: unfairWorker: remoteConn.Write:", writeErr) - return writeErr - } - - if nRead != nWritten { - log.Printf("worker: unfairWorker: nRead != nWritten") - return syscall.EMSGSIZE // message too long to fit in send buffer even after auto partial write - } - - return nil - }); err != syscall.EAGAIN { // silently ignore EAGAIN - if err == io.EOF { - log.Println("worker: unfairWorker: sourceConn is closed") - return wasip1.EncodeWATERError(0) // success, no error - } - if errno, ok := err.(syscall.Errno); ok { - return wasip1.EncodeWATERError(errno) - } - return wasip1.EncodeWATERError(syscall.EIO) // input/output error - } - - // 3rd priority: remoteConn - if err := untilError(func() error { - nRead, readErr := remoteConn.Read(readBuf) - if readErr != nil { - if readErr != syscall.EAGAIN { - log.Println("worker: unfairWorker: remoteConn.Read:", readErr) - } - return readErr - } - - nWrite, writeErr := sourceConn.Write(readBuf[:nRead]) - if writeErr != nil { - log.Println("worker: unfairWorker: sourceConn.Write:", writeErr) - return writeErr - } - - if nRead != nWrite { - log.Printf("worker: unfairWorker: nRead != nWrite") - return syscall.EMSGSIZE // message too long to fit in send buffer even after auto partial write - } - - return nil - }); err != syscall.EAGAIN { // silently ignore EAGAIN - if err == io.EOF { - log.Println("worker: unfairWorker: remoteConn is closed") - return wasip1.EncodeWATERError(0) // success, no error - } - if errno, ok := err.(syscall.Errno); ok { - return wasip1.EncodeWATERError(errno) - } - return wasip1.EncodeWATERError(syscall.EIO) // input/output error - } - } -} - -// like unfairWorker, fairWorker also works on all three connections with a priority order -// of cancelConn > sourceConn > remoteConn. -// -// But different from unfairWorker, fairWorker spend equal amount of turns on each connection -// for calling Read. Therefore it has a better fairness than unfairWorker, which may still -// make progress if one of the connection is not properly set to non-blocking mode. -func fairWorker() int32 { - for { - pollFd := []pollFd{ - { - fd: uintptr(cancelConn.Fd()), - events: EventFdRead, - }, - { - fd: uintptr(sourceConn.Fd()), - events: EventFdRead, - }, - { - fd: uintptr(remoteConn.Fd()), - events: EventFdRead, - }, - } - - n, err := _poll(pollFd, -1) - if n == 0 { // TODO: re-evaluate the condition - if err == nil || err == syscall.EAGAIN { - runtime.Gosched() // yield the current goroutine - continue - } - log.Println("worker: fairWorker: _poll:", err) - return int32(err.(syscall.Errno)) - } - - // 1st priority: cancelConn - _, err = cancelConn.Read(readBuf) - if !(err == syscall.EAGAIN) { - if err == io.EOF || err == nil { - log.Println("worker: fairWorker: cancelConn is closed") - return wasip1.EncodeWATERError(syscall.ECANCELED) // operation canceled - } - log.Println("worker: fairWorker: cancelConn.Read:", err) - return wasip1.EncodeWATERError(syscall.EIO) // input/output error - } - - // 2nd priority: sourceConn -> remoteConn - if err := copyOnce( - "remoteConn", // dstName - "sourceConn", // srcName - remoteConn, // dst - sourceConn, // src - readBuf); err != nil { - if err == io.EOF { - return wasip1.EncodeWATERError(0) // success, no error - } - if errno, ok := err.(syscall.Errno); ok { - return wasip1.EncodeWATERError(errno) - } - return wasip1.EncodeWATERError(syscall.EIO) // other input/output error - } - - // 3rd priority: remoteConn -> sourceConn - if err := copyOnce( - "sourceConn", // dstName - "remoteConn", // srcName - sourceConn, // dst - remoteConn, // src - readBuf); err != nil { - if err == io.EOF { - return wasip1.EncodeWATERError(0) - } - if errno, ok := err.(syscall.Errno); ok { - return wasip1.EncodeWATERError(errno) - } - return wasip1.EncodeWATERError(syscall.EIO) // other input/output error - } - } -} - -func copyOnce(dstName, srcName string, dst, src net.Conn, buf []byte) error { - if len(buf) == 0 { - buf = make([]byte, 16384) // 16k buffer for reading - } - - nRead, readErr := src.Read(buf) - if !(readErr == syscall.EAGAIN) { // if EAGAIN, do nothing and return - if readErr == io.EOF { - log.Printf("worker: copyOnce: EOF on %s", srcName) - return io.EOF - } else if readErr != nil { - log.Printf("worker: copyOnce: %s.Read: %v", srcName, readErr) - return syscall.EIO // input/output error - } - - nWritten, writeErr := dst.Write(buf[:nRead]) - if writeErr != nil { - log.Printf("worker: copyOnce: %s.Write: %v", dstName, writeErr) - return syscall.EIO // no matter input/output error or EAGAIN we cannot retry async write yet - } - - if nRead != nWritten { - log.Printf("worker: copyOnce: %s.nRead != %s.nWritten", srcName, dstName) - return syscall.EIO // input/output error - } - } - - return nil -} diff --git a/tinygo/v1/config.go b/tinygo/v1/config.go new file mode 100644 index 0000000..a04fa82 --- /dev/null +++ b/tinygo/v1/config.go @@ -0,0 +1,44 @@ +package v1 + +import ( + "bytes" + "log" + "os" + "syscall" +) + +func readInboundConfig() (config []byte, err error) { + // check if /conf/inbound.cfg exists + file, err := os.Open("/conf/inbound.cfg") + if err != nil { + return nil, syscall.EACCES + } + return readConfig(file) +} + +func readOutboundConfig() (config []byte, err error) { + // check if /conf/outbound.cfg exists + file, err := os.Open("/conf/outbound.cfg") + if err != nil { + return nil, syscall.EACCES + } + return readConfig(file) +} + +func readConfig(file *os.File) (config []byte, err error) { + // read the config file + buf := new(bytes.Buffer) + _, err = buf.ReadFrom(file) + if err != nil { + log.Println("readConfig: (*bytes.Buffer).ReadFrom:", err) + return nil, syscall.EIO + } + + config = buf.Bytes() + + // close the file + if err = file.Close(); err != nil { + err = syscall.EIO + } + return +} diff --git a/tinygo/v1/exports.go b/tinygo/v1/exports.go index 0535f66..7da87a2 100644 --- a/tinygo/v1/exports.go +++ b/tinygo/v1/exports.go @@ -1,77 +1,40 @@ package v1 import ( - "bytes" "errors" "log" - "os" "syscall" v1net "github.com/refraction-networking/watm/tinygo/v1/net" - "github.com/refraction-networking/watm/wasip1" ) -func readInboundConfig() (config []byte, err error) { - // check if /conf/inbound.cfg exists - file, err := os.Open("/conf/inbound.cfg") - if err != nil { - return nil, syscall.EACCES - } - return readConfig(file) -} - -func readOutboundConfig() (config []byte, err error) { - // check if /conf/outbound.cfg exists - file, err := os.Open("/conf/outbound.cfg") - if err != nil { - return nil, syscall.EACCES - } - return readConfig(file) -} - -func readConfig(file *os.File) (config []byte, err error) { - // read the config file - buf := new(bytes.Buffer) - _, err = buf.ReadFrom(file) - if err != nil { - log.Println("readConfig: (*bytes.Buffer).ReadFrom:", err) - return nil, syscall.EIO - } - - config = buf.Bytes() +const ESUCCESS uint32 = 0 +const INVALID_FD int32 = -1 - // close the file - if err := file.Close(); err != nil { - return config, syscall.EIO - } - - return config, nil -} +var lastError syscall.Errno //export watm_ctrlpipe_v1 -func _ctrlpipe(ctrlFd int32) int32 { +func _ctrlpipe(ctrlFd int32) uint32 { ctrlConn = v1net.RebuildTCPConn(ctrlFd) if err := ctrlConn.SetNonBlock(true); err != nil { log.Printf("dial: ctrlConn.SetNonblock: %v", err) - return wasip1.EncodeWATERError(err.(syscall.Errno)) + return saveAndReturnError(err) } - - return 0 // ESUCCESS + return ESUCCESS } //export watm_userpipe_v1 -func _userpipe(userFd int32) int32 { +func _userpipe(userFd int32) uint32 { if workerIdentity != identity_uninitialized { - return wasip1.EncodeWATERError(syscall.EBUSY) // device or resource busy (worker already initialized) + return saveAndReturnError(syscall.EBUSY) } sourceConn = v1net.RebuildTCPConn(userFd) if err := sourceConn.SetNonBlock(true); err != nil { log.Printf("internal_pipe: sourceConn.SetNonblock: %v", err) - return wasip1.EncodeWATERError(err.(syscall.Errno)) + return saveAndReturnError(err) } - - return 0 // ESUCCESS + return ESUCCESS } // _dial @@ -79,9 +42,10 @@ func _userpipe(userFd int32) int32 { // watm_dial_v1(networkType s32) -> s32 // //export watm_dial_v1 -func _dial(networkType int32) (networkFd int32) { +func _dial(networkType uint32) (networkFd int32) { if workerIdentity != identity_uninitialized { - return wasip1.EncodeWATERError(syscall.EBUSY) // device or resource busy (worker already initialized) + lastError = syscall.EBUSY + return INVALID_FD } if !globalDialer.locked { @@ -96,13 +60,15 @@ func _dial(networkType int32) (networkFd int32) { if err == nil || config != nil { configurableDialer.Configure(config) } else if !errors.Is(err, syscall.EACCES) { // EACCES means no config file provided by the host - return wasip1.EncodeWATERError(err.(syscall.Errno)) + lastError = err.(syscall.Errno) + return INVALID_FD } } if sourceConn == nil { log.Printf("internal_pipe: sourceConn is nil, _internal_pipe must be called first") - return wasip1.EncodeWATERError(syscall.ENOTCONN) // socket not connected + lastError = syscall.ENOTCONN + return INVALID_FD } var network string = v1net.ToNetworkTypeString(networkType) @@ -110,7 +76,8 @@ func _dial(networkType int32) (networkFd int32) { var err error if address, err = v1net.GetAddrSuggestion(); err != nil { log.Printf("dial: v1net.GetAddrSuggestion: %v", err) - return wasip1.EncodeWATERError(err.(syscall.Errno)) + lastError = err.(syscall.Errno) + return INVALID_FD } if globalDialer.wt != nil { @@ -118,7 +85,8 @@ func _dial(networkType int32) (networkFd int32) { rawNetworkConn, err := v1net.Dial(network, address) if err != nil { log.Printf("dial: v1net.Dial(%s, %s): %v", network, address, err) - return wasip1.EncodeWATERError(err.(syscall.Errno)) + lastError = err.(syscall.Errno) + return INVALID_FD } networkFd = rawNetworkConn.Fd() @@ -130,18 +98,21 @@ func _dial(networkType int32) (networkFd int32) { remoteConn, err = globalDialer.wt.Wrap(rawNetworkConn) if err != nil { log.Printf("dial: d.wt.Wrap: %v", err) - return wasip1.EncodeWATERError(syscall.EPROTO) // protocol error + lastError = syscall.EPROTO + return INVALID_FD } } else if globalDialer.dt != nil { // call dt.Dial remoteConn, err = globalDialer.dt.Dial(network, address) if err != nil { log.Printf("dial: d.dt.Dial(%s, %s): %v", network, address, err) - return wasip1.EncodeWATERError(err.(syscall.Errno)) + lastError = err.(syscall.Errno) + return INVALID_FD } networkFd = remoteConn.Fd() } else { - return wasip1.EncodeWATERError(syscall.EPERM) // operation not permitted + lastError = syscall.EPERM + return INVALID_FD } remoteConn.SetNonBlock(true) // at this point, it is safe to set non-blocking mode on remoteConn @@ -156,7 +127,8 @@ func _dial(networkType int32) (networkFd int32) { //export watm_accept_v1 func _accept() (networkFd int32) { if workerIdentity != identity_uninitialized { - return wasip1.EncodeWATERError(syscall.EBUSY) // device or resource busy (worker already initialized) + lastError = syscall.EBUSY + return INVALID_FD } if !globalListener.locked { @@ -171,13 +143,15 @@ func _accept() (networkFd int32) { if err == nil || config != nil { configurableListener.Configure(config) } else if !errors.Is(err, syscall.EACCES) { // EACCES means no config file provided by the host - return wasip1.EncodeWATERError(err.(syscall.Errno)) + lastError = err.(syscall.Errno) + return INVALID_FD } } if sourceConn == nil { log.Printf("internal_pipe: sourceConn is nil, _internal_pipe must be called first") - return wasip1.EncodeWATERError(syscall.ENOTCONN) // socket not connected + lastError = syscall.ENOTCONN + return INVALID_FD } if globalListener.wt != nil { @@ -186,7 +160,8 @@ func _accept() (networkFd int32) { rawNetworkConn, err := lis.Accept() if err != nil { log.Printf("dial: v1net.Listener.Accept: %v", err) - return wasip1.EncodeWATERError(err.(syscall.Errno)) + lastError = err.(syscall.Errno) + return INVALID_FD } networkFd = rawNetworkConn.Fd() @@ -198,20 +173,23 @@ func _accept() (networkFd int32) { remoteConn, err = globalListener.wt.Wrap(rawNetworkConn) if err != nil { log.Printf("dial: d.wt.Wrap: %v", err) - return wasip1.EncodeWATERError(syscall.EPROTO) // protocol error + lastError = syscall.EPROTO + return INVALID_FD } } else if globalListener.lt != nil { // call v1net.ListeningTransport.Accept wrappedNetworkConn, err := globalListener.lt.Accept() if err != nil { log.Printf("dial: v1net.Listener.Accept: %v", err) - return wasip1.EncodeWATERError(err.(syscall.Errno)) + lastError = err.(syscall.Errno) + return INVALID_FD } networkFd = wrappedNetworkConn.Fd() remoteConn = wrappedNetworkConn } else { - return wasip1.EncodeWATERError(syscall.EPERM) // operation not permitted + lastError = syscall.EPERM + return INVALID_FD } remoteConn.SetNonBlock(true) // at this point, it is safe to set non-blocking mode on remoteConn @@ -224,9 +202,9 @@ func _accept() (networkFd int32) { // watm_associate_v1(networkType s32) -> s32 // //export watm_associate_v1 -func _associate(networkType int32) int32 { +func _associate(networkType uint32) uint32 { if workerIdentity != identity_uninitialized { - return wasip1.EncodeWATERError(syscall.EBUSY) // device or resource busy (worker already initialized) + return saveAndReturnError(syscall.EBUSY) // device or resource busy (worker already initialized) } if !globalRelay.inboundLocked && !globalRelay.outboundLocked { @@ -241,7 +219,7 @@ func _associate(networkType int32) int32 { if err == nil || config != nil { configurableInbRelay.Configure(config) } else if !errors.Is(err, syscall.EACCES) { // EACCES means no config file provided by the host - return wasip1.EncodeWATERError(err.(syscall.Errno)) + return saveAndReturnError(err) } } @@ -251,7 +229,7 @@ func _associate(networkType int32) int32 { if err == nil || config != nil { configurableOutRelay.Configure(config) } else if !errors.Is(err, syscall.EACCES) { // EACCES means no config file provided by the host - return wasip1.EncodeWATERError(err.(syscall.Errno)) + return saveAndReturnError(err) } } @@ -261,20 +239,20 @@ func _associate(networkType int32) int32 { sourceConn, err = globalRelay.inboundListeningTransport.Accept() if err != nil { log.Printf("dial: relay.ListeningTransport.Accept: %v", err) - return wasip1.EncodeWATERError(err.(syscall.Errno)) + return saveAndReturnError(err) } } else { // we first accept the inbound connection, then wrap it if there's a wrapping transport set for it sourceConn, err = (&v1net.TCPListener{}).Accept() if err != nil { log.Printf("dial: v1net.TCPListener.Accept: %v", err) - return wasip1.EncodeWATERError(err.(syscall.Errno)) + return saveAndReturnError(err) } if globalRelay.inboundWrappingTransport != nil { sourceConn, err = globalRelay.inboundWrappingTransport.Wrap(sourceConn.(*v1net.TCPConn)) if err != nil { log.Printf("dial: r.inboundWrappingTransport.Wrap: %v", err) - return wasip1.EncodeWATERError(syscall.EPROTO) // protocol error + return saveAndReturnError(syscall.EPROTO) // protocol error } } } @@ -284,26 +262,26 @@ func _associate(networkType int32) int32 { var address string if address, err = v1net.GetAddrSuggestion(); err != nil { log.Printf("dial: v1net.GetAddrSuggestion: %v", err) - return wasip1.EncodeWATERError(err.(syscall.Errno)) + return saveAndReturnError(err) } if globalRelay.outboundDialingTransport != nil { remoteConn, err = globalRelay.outboundDialingTransport.Dial(network, address) if err != nil { log.Printf("dial: r.outboundDialingTransport.Dial: %v", err) - return wasip1.EncodeWATERError(err.(syscall.Errno)) + return saveAndReturnError(err) } } else { // we first dial the outbound connection, then wrap it if there's a wrapping transport set for it remoteConn, err = v1net.Dial(network, address) if err != nil { log.Printf("dial: v1net.Dial: %v", err) - return wasip1.EncodeWATERError(err.(syscall.Errno)) + return saveAndReturnError(err) } if globalRelay.outboundWrappingTransport != nil { remoteConn, err = globalRelay.outboundWrappingTransport.Wrap(remoteConn.(*v1net.TCPConn)) if err != nil { log.Printf("dial: r.outboundWrappingTransport.Wrap: %v", err) - return wasip1.EncodeWATERError(syscall.EPROTO) // protocol error + return saveAndReturnError(syscall.EPROTO) // protocol error } } } @@ -311,11 +289,11 @@ func _associate(networkType int32) int32 { // set non-blocking mode on both connections if err := sourceConn.SetNonBlock(true); err != nil { log.Printf("dial: sourceConn.SetNonblock: %v", err) - return wasip1.EncodeWATERError(err.(syscall.Errno)) + return saveAndReturnError(err) } if err := remoteConn.SetNonBlock(true); err != nil { log.Printf("dial: remoteConn.SetNonblock: %v", err) - return wasip1.EncodeWATERError(err.(syscall.Errno)) + return saveAndReturnError(err) } workerIdentity = identity_relay @@ -327,11 +305,21 @@ func _associate(networkType int32) int32 { // watm_start_v1() -> s32 // //export watm_start_v1 -func _start() int32 { +func _start() uint32 { if workerIdentity == identity_uninitialized { log.Println("worker: uninitialized") - return wasip1.EncodeWATERError(syscall.ENOTCONN) // socket not connected + return saveAndReturnError(syscall.ENOTCONN) // socket not connected } log.Printf("worker: working as %s", identityStrings[workerIdentity]) return worker() } + +func saveAndReturnError(err error) uint32 { + syscallErrno, ok := err.(syscall.Errno) + if !ok { + log.Printf("saveAndReturnError: %v is not a syscall.Errno", err) + lastError = syscall.ENOSYS + } + lastError = syscallErrno + return uint32(syscallErrno) +} diff --git a/tinygo/v1/net/dial.go b/tinygo/v1/net/dial.go index f36807b..3ef1a56 100644 --- a/tinygo/v1/net/dial.go +++ b/tinygo/v1/net/dial.go @@ -1,15 +1,19 @@ package net import ( + "unsafe" + "github.com/refraction-networking/watm/tinygo/v1/wasiimport" "github.com/refraction-networking/watm/wasip1" ) // Dial dials a remote host for a network connection. func Dial(network, address string) (Conn, error) { - fd, err := wasip1.DecodeWATERError(wasiimport.WaterDial( + var fd int32 + err := wasip1.ErrnoToError(wasiimport.WaterDial( ToNetworkTypeInt(network), makeIOVec([]byte(address)), 1, + unsafe.Pointer(&fd), )) if err != nil { return nil, err @@ -23,10 +27,14 @@ func Dial(network, address string) (Conn, error) { // must be called before watm_dial_v1 returns. func GetAddrSuggestion() (string, error) { var addrBuf []byte = make([]byte, 256) - n, err := wasip1.DecodeWATERError(wasiimport.WaterGetAddrSuggestion(makeIOVec(addrBuf), 1)) + var nread size + err := wasip1.ErrnoToError(wasiimport.WaterGetAddrSuggestion( + makeIOVec(addrBuf), 1, + unsafe.Pointer(&nread), + )) if err != nil { return "", err } - return string(addrBuf[:n]), nil + return string(addrBuf[:nread]), nil } diff --git a/tinygo/v1/net/listener.go b/tinygo/v1/net/listener.go index d22ab2e..c733a13 100644 --- a/tinygo/v1/net/listener.go +++ b/tinygo/v1/net/listener.go @@ -2,6 +2,7 @@ package net import ( "syscall" + "unsafe" "github.com/refraction-networking/watm/tinygo/v1/wasiimport" "github.com/refraction-networking/watm/wasip1" @@ -30,7 +31,10 @@ func (tl *TCPListener) Accept() (Conn, error) { return nil, syscall.EINVAL } - fd, err := wasip1.DecodeWATERError(wasiimport.WaterAccept()) + var fd int32 + err := wasip1.ErrnoToError(wasiimport.WaterAccept( + unsafe.Pointer(&fd), + )) if err != nil { return nil, err } diff --git a/tinygo/v1/net/network_type.go b/tinygo/v1/net/network_type.go index 97eab31..ce99ef7 100644 --- a/tinygo/v1/net/network_type.go +++ b/tinygo/v1/net/network_type.go @@ -1,13 +1,13 @@ package net const ( - NETWORK_UNKNOWN int32 = iota + NETWORK_UNKNOWN uint32 = iota NETWORK_TCP NETWORK_TCP4 NETWORK_TCP6 ) -func ToNetworkTypeString(networkType int32) string { +func ToNetworkTypeString(networkType uint32) string { switch networkType { case NETWORK_TCP: return "tcp" @@ -20,7 +20,7 @@ func ToNetworkTypeString(networkType int32) string { } } -func ToNetworkTypeInt(networkType string) int32 { +func ToNetworkTypeInt(networkType string) uint32 { switch networkType { case "tcp": return NETWORK_TCP diff --git a/tinygo/v1/wasiimport/import.go b/tinygo/v1/wasiimport/import.go index 4fc4b17..5b80d87 100644 --- a/tinygo/v1/wasiimport/import.go +++ b/tinygo/v1/wasiimport/import.go @@ -14,10 +14,12 @@ func SetWaterDialedFD(fd int32) { } func water_dial( - int32, - unsafe.Pointer, size, -) (fd int32) { - return waterDialedFD + _ uint32, + _ unsafe.Pointer, _ size, + fd unsafe.Pointer, +) errno { + *(*int32)(fd) = waterDialedFD + return 0 } var waterAcceptedFD int32 = -1 @@ -29,19 +31,21 @@ func SetWaterAcceptedFD(fd int32) { // This function should be imported from the host in WASI. // On non-WASI platforms, it mimicks the behavior of the host // by returning a file descriptor of preset value. -func water_accept() (fd int32) { - return waterAcceptedFD +func water_accept(fd unsafe.Pointer) errno { + *(*int32)(fd) = waterAcceptedFD + return 0 } func water_get_addr_suggestion( unsafe.Pointer, size, -) (n int32) { + unsafe.Pointer, +) errno { return 0 } // emulate the behavior when no file descriptors are // ready and the timeout expires immediately. -func poll_oneoff(_, _ unsafe.Pointer, nsubscriptions uint32, nevents unsafe.Pointer) uint32 { +func poll_oneoff(_, _ unsafe.Pointer, nsubscriptions uint32, nevents unsafe.Pointer) errno { // wait for a very short period to simulate the polling time.Sleep(50 * time.Millisecond) *(*uint32)(nevents) = nsubscriptions diff --git a/tinygo/v1/wasiimport/symbols.go b/tinygo/v1/wasiimport/import_exported.go similarity index 64% rename from tinygo/v1/wasiimport/symbols.go rename to tinygo/v1/wasiimport/import_exported.go index 94e1c5a..9e363e4 100644 --- a/tinygo/v1/wasiimport/symbols.go +++ b/tinygo/v1/wasiimport/import_exported.go @@ -3,20 +3,25 @@ package wasiimport import "unsafe" func WaterDial( - networkType int32, + networkType uint32, addressCiovs unsafe.Pointer, addressCiovsLen size, -) (fd int32) { - return water_dial(networkType, addressCiovs, addressCiovsLen) + fd unsafe.Pointer, +) errno { + return water_dial( + networkType, + addressCiovs, addressCiovsLen, + fd) } -func WaterAccept() (fd int32) { - return water_accept() +func WaterAccept(fd unsafe.Pointer) errno { + return water_accept(fd) } func WaterGetAddrSuggestion( addressIovs unsafe.Pointer, addressIovsLen size, -) (n int32) { - return water_get_addr_suggestion(addressIovs, addressIovsLen) + nread unsafe.Pointer, +) errno { + return water_get_addr_suggestion(addressIovs, addressIovsLen, nread) } func FdFdstatSetFlags(fd int32, flags uint32) uint32 { diff --git a/tinygo/v1/wasiimport/import_wasi.go b/tinygo/v1/wasiimport/import_wasi.go index 9b50ded..72d0241 100644 --- a/tinygo/v1/wasiimport/import_wasi.go +++ b/tinygo/v1/wasiimport/import_wasi.go @@ -4,60 +4,39 @@ package wasiimport import "unsafe" -// Import the Runtime-provided dial function, -// which takes iovs for network and address and -// returns a file descriptor for the dialed connection. -// -// Host is not expected to write to the ciovs. -// -// Used by dialers and relays. -// //go:wasmimport env water_dial //go:noescape func water_dial( - networkType int32, + networkType uint32, addressCiovs unsafe.Pointer, addressCiovsLen size, -) (fd int32) + fd unsafe.Pointer, +) errno -// Import the Runtime-provided accept function, -// which returns a file descriptor for the next incoming -// connection of a listener managed by the Runtime. -// -// Used by listeners and relays. -// //go:wasmimport env water_accept //go:noescape -func water_accept() (fd int32) +func water_accept(fd unsafe.Pointer) errno -// Import the Runtime-provided get address suggestion function, -// which does a scatter read of the suggested network address into -// the provided iovec. -// -// Before returning, the host is expected to write the address -// as a byte array to the iovec. -// -// Returns the length of the address written. -// //go:wasmimport env water_get_addr_suggestion //go:noescape func water_get_addr_suggestion( addressIovs unsafe.Pointer, addressIovsLen size, -) (n int32) + nread unsafe.Pointer, +) errno -// Import wasi_snapshot_preview1's fd_fdstat_set_flags function -// until tinygo supports it. +// TODO: remove this once tinygo provides wrapper for fd_fdstat_set // //go:wasmimport wasi_snapshot_preview1 fd_fdstat_set_flags //go:noescape func fd_fdstat_set_flags(fd int32, flags uint32) uint32 -// Import wasi_snapshot_preview1's fd_fdstat_set_flags function -// until tinygo supports it. +// TODO: remove this once tinygo provides wrapper for fd_fdstat_get // //go:wasmimport wasi_snapshot_preview1 fd_fdstat_get //go:noescape func fd_fdstat_get(fd int32, buf unsafe.Pointer) uint32 +// TODO: remove this once tinygo provides wrapper for poll_oneoff +// //go:wasmimport wasi_snapshot_preview1 poll_oneoff //go:noescape func poll_oneoff(in, out unsafe.Pointer, nsubscriptions size, nevents unsafe.Pointer) errno diff --git a/tinygo/v1/worker.go b/tinygo/v1/worker.go index bf60c43..f4fbc30 100644 --- a/tinygo/v1/worker.go +++ b/tinygo/v1/worker.go @@ -8,7 +8,6 @@ import ( "syscall" v1net "github.com/refraction-networking/watm/tinygo/v1/net" - "github.com/refraction-networking/watm/wasip1" ) type identity uint8 @@ -34,7 +33,7 @@ var sourceConn v1net.Conn // sourceConn is used to communicate between WASM and var remoteConn v1net.Conn // remoteConn is used to communicate between WASM and a dialed remote destination (for dialer/relay) or a dialing party (for listener only) var ctrlConn v1net.Conn // ctrlConn is used to control the entire worker with control messages -var workerFn func() int32 = unfairWorker // by default, use unfairWorker for better performance under mostly unidirectional I/O +var workerFn func() uint32 = unfairWorker // by default, use unfairWorker for better performance under mostly unidirectional I/O var readBuf []byte = make([]byte, 1024) // 1024B buffer for reading, size can be updated with [SetReadBufferSize] @@ -57,10 +56,10 @@ func WorkerFairness(fair bool) { } } -func worker() int32 { +func worker() uint32 { if sourceConn == nil || remoteConn == nil || ctrlConn == nil { - log.Println("worker: worker: sourceConn, remoteConn, or ctrlConn is nil") - return wasip1.EncodeWATERError(syscall.EBADF) // bad file descriptor + log.Println("worker: at least one of sourceConn, remoteConn, and ctrlConn is nil") + return saveAndReturnError(syscall.EBADF) // bad file descriptor } return workerFn() @@ -83,7 +82,7 @@ func untilError(f func() error) error { // connection is not properly set to non-blocking mode, i.e., never returns // EAGAIN, this function will block forever and never work on a lower priority // connection. Thus it is called unfairWorker. -func unfairWorker() int32 { +func unfairWorker() uint32 { conns := []v1net.Conn{ctrlConn, sourceConn, remoteConn} evts := []uint16{v1net.EventFdRead, v1net.EventFdRead, v1net.EventFdRead} @@ -95,7 +94,7 @@ func unfairWorker() int32 { continue } log.Println("worker: unfairWorker: _poll:", err) - return int32(err.(syscall.Errno)) + return saveAndReturnError(err) } // 1st priority: ctrlConn @@ -103,10 +102,10 @@ func unfairWorker() int32 { if !(err == syscall.EAGAIN) { if err == io.EOF || err == nil { log.Println("worker: unfairWorker: ctrlConn is closed") - return wasip1.EncodeWATERError(syscall.ECANCELED) // operation canceled + return saveAndReturnError(syscall.ECANCELED) // operation canceled } log.Println("worker: unfairWorker: ctrlConn.Read:", err) - return wasip1.EncodeWATERError(syscall.EIO) // input/output error + return saveAndReturnError(syscall.EIO) // input/output error } // 2nd priority: sourceConn @@ -134,12 +133,12 @@ func unfairWorker() int32 { }); err != syscall.EAGAIN { // silently ignore EAGAIN if err == io.EOF { log.Println("worker: unfairWorker: sourceConn is closed") - return wasip1.EncodeWATERError(0) // success, no error + return saveAndReturnError(syscall.Errno(0)) // success, no error } if errno, ok := err.(syscall.Errno); ok { - return wasip1.EncodeWATERError(errno) + return saveAndReturnError(errno) } - return wasip1.EncodeWATERError(syscall.EIO) // input/output error + return saveAndReturnError(syscall.EIO) // input/output error } // 3rd priority: remoteConn @@ -167,12 +166,12 @@ func unfairWorker() int32 { }); err != syscall.EAGAIN { // silently ignore EAGAIN if err == io.EOF { log.Println("worker: unfairWorker: remoteConn is closed") - return wasip1.EncodeWATERError(0) // success, no error + return saveAndReturnError(syscall.Errno(0)) // success, no error } if errno, ok := err.(syscall.Errno); ok { - return wasip1.EncodeWATERError(errno) + return saveAndReturnError(errno) } - return wasip1.EncodeWATERError(syscall.EIO) // input/output error + return saveAndReturnError(syscall.EIO) // input/output error } } } @@ -185,7 +184,7 @@ func unfairWorker() int32 { // make progress if one of the connection is not properly set to non-blocking mode. // // TODO: use poll_oneoff instead of busy polling -func fairWorker() int32 { +func fairWorker() uint32 { conns := []v1net.Conn{ctrlConn, sourceConn, remoteConn} evts := []uint16{v1net.EventFdRead, v1net.EventFdRead, v1net.EventFdRead} @@ -197,7 +196,7 @@ func fairWorker() int32 { continue } log.Println("worker: unfairWorker: _poll:", err) - return int32(err.(syscall.Errno)) + return saveAndReturnError(err) } // 1st priority: ctrlConn @@ -205,10 +204,10 @@ func fairWorker() int32 { if !(err == syscall.EAGAIN) { if err == io.EOF || err == nil { log.Println("worker: fairWorker: ctrlConn is closed") - return wasip1.EncodeWATERError(syscall.ECANCELED) // operation canceled + return saveAndReturnError(syscall.ECANCELED) // operation canceled } log.Println("worker: fairWorker: ctrlConn.Read:", err) - return wasip1.EncodeWATERError(syscall.EIO) // input/output error + return saveAndReturnError(syscall.EIO) // input/output error } // 2nd priority: sourceConn -> remoteConn @@ -219,12 +218,12 @@ func fairWorker() int32 { sourceConn, // src readBuf); err != nil { if err == io.EOF { - return wasip1.EncodeWATERError(0) // success, no error + return saveAndReturnError(syscall.Errno(0)) // success, no error } if errno, ok := err.(syscall.Errno); ok { - return wasip1.EncodeWATERError(errno) + return saveAndReturnError(errno) } - return wasip1.EncodeWATERError(syscall.EIO) // other input/output error + return saveAndReturnError(syscall.EIO) // other input/output error } // 3rd priority: remoteConn -> sourceConn @@ -235,12 +234,12 @@ func fairWorker() int32 { remoteConn, // src readBuf); err != nil { if err == io.EOF { - return wasip1.EncodeWATERError(0) + return saveAndReturnError(syscall.Errno(0)) // success, no error } if errno, ok := err.(syscall.Errno); ok { - return wasip1.EncodeWATERError(errno) + return saveAndReturnError(errno) } - return wasip1.EncodeWATERError(syscall.EIO) // other input/output error + return saveAndReturnError(syscall.EIO) // other input/output error } } } diff --git a/wasip1/errno.go b/wasip1/errno.go index c36795f..2acb7d8 100644 --- a/wasip1/errno.go +++ b/wasip1/errno.go @@ -5,17 +5,17 @@ import ( "syscall" ) -// errno is just a copy of syscall.Errno from the Go standard library. -// -// The values are defined in syscall/tables_wasip1.go. -type errno syscall.Errno - // DecodeWATERError converts a error code returned by WATER API // into a syscall.Errno or a higher-level error in Go. // // It automatically detects whether the error code is a WATER error // or a success code (positive). In case of a success code, it // returns the code itself and a nil error. +// +// Deprecated: starting from WATM v1 API, returned errno is always +// a ground truth syscall.Errno and not multiplexed with other positive +// return values. Positive return values are always set via a pointer +// as a parameter to the function. func DecodeWATERError(errorCode int32) (n int32, err error) { if errorCode >= 0 { n = errorCode // such that when error code is 0, it will return 0, nil @@ -34,6 +34,11 @@ func DecodeWATERError(errorCode int32) (n int32, err error) { return } +// EncodeWATERError converts a syscall.Errno (positive) into a error code +// returned by WATER API (negative). +// +// Deprecated: starting from WATM v1 API, returned errno is always a ground +// truth syscall.Errno and not multiplexed with other positive return values. func EncodeWATERError(errno syscall.Errno) int32 { if errno == 0 { return 0 diff --git a/wasip1/error.go b/wasip1/error.go new file mode 100644 index 0000000..0dc2f8f --- /dev/null +++ b/wasip1/error.go @@ -0,0 +1,14 @@ +package wasip1 + +import "fmt" + +func ErrnoToError(errno uint32) error { + if errno == 0 { + return nil + } + + if syscallErrno, ok := mapErrno2Syscall[errno]; ok { + return syscallErrno + } + return fmt.Errorf("unknown errno %d", errno) +} diff --git a/wasip1/tables.go b/wasip1/tables.go index ac6d9bb..c4153a0 100644 --- a/wasip1/tables.go +++ b/wasip1/tables.go @@ -4,6 +4,8 @@ import ( "syscall" ) +type errno = uint32 + // errno is just a copy of syscall.Errno from the Go standard library. // // WATER error code is defined as the negative value of errno. @@ -88,149 +90,148 @@ const ( EOPNOTSUPP = ENOTSUP ) -// TODO: Auto-generate some day. (Hard-coded in binaries so not likely to change.) -var errorstr = [...]string{ - E2BIG: "Argument list too long", - EACCES: "Permission denied", - EADDRINUSE: "Address already in use", - EADDRNOTAVAIL: "Address not available", - EAFNOSUPPORT: "Address family not supported by protocol family", - EAGAIN: "Try again", - EALREADY: "Socket already connected", - EBADF: "Bad file number", - EBADMSG: "Trying to read unreadable message", - EBUSY: "Device or resource busy", - ECANCELED: "Operation canceled.", - ECHILD: "No child processes", - ECONNABORTED: "Connection aborted", - ECONNREFUSED: "Connection refused", - ECONNRESET: "Connection reset by peer", - EDEADLK: "Deadlock condition", - EDESTADDRREQ: "Destination address required", - EDOM: "Math arg out of domain of func", - EDQUOT: "Quota exceeded", - EEXIST: "File exists", - EFAULT: "Bad address", - EFBIG: "File too large", - EHOSTUNREACH: "Host is unreachable", - EIDRM: "Identifier removed", - EILSEQ: "EILSEQ", - EINPROGRESS: "Connection already in progress", - EINTR: "Interrupted system call", - EINVAL: "Invalid argument", - EIO: "I/O error", - EISCONN: "Socket is already connected", - EISDIR: "Is a directory", - ELOOP: "Too many symbolic links", - EMFILE: "Too many open files", - EMLINK: "Too many links", - EMSGSIZE: "Message too long", - EMULTIHOP: "Multihop attempted", - ENAMETOOLONG: "File name too long", - ENETDOWN: "Network interface is not configured", - ENETRESET: "Network dropped connection on reset", - ENETUNREACH: "Network is unreachable", - ENFILE: "File table overflow", - ENOBUFS: "No buffer space available", - ENODEV: "No such device", - ENOENT: "No such file or directory", - ENOEXEC: "Exec format error", - ENOLCK: "No record locks available", - ENOLINK: "The link has been severed", - ENOMEM: "Out of memory", - ENOMSG: "No message of desired type", - ENOPROTOOPT: "Protocol not available", - ENOSPC: "No space left on device", - ENOSYS: "Not implemented on wasip1", - ENOTCONN: "Socket is not connected", - ENOTDIR: "Not a directory", - ENOTEMPTY: "Directory not empty", - ENOTRECOVERABLE: "State not recoverable", - ENOTSOCK: "Socket operation on non-socket", - ENOTSUP: "Not supported", - ENOTTY: "Not a typewriter", - ENXIO: "No such device or address", - EOVERFLOW: "Value too large for defined data type", - EOWNERDEAD: "Owner died", - EPERM: "Operation not permitted", - EPIPE: "Broken pipe", - EPROTO: "Protocol error", - EPROTONOSUPPORT: "Unknown protocol", - EPROTOTYPE: "Protocol wrong type for socket", - ERANGE: "Math result not representable", - EROFS: "Read-only file system", - ESPIPE: "Illegal seek", - ESRCH: "No such process", - ESTALE: "Stale file handle", - ETIMEDOUT: "Connection timed out", - ETXTBSY: "Text file busy", - EXDEV: "Cross-device link", - ENOTCAPABLE: "Capabilities insufficient", -} +// var errorstr = [...]string{ +// E2BIG: "Argument list too long", +// EACCES: "Permission denied", +// EADDRINUSE: "Address already in use", +// EADDRNOTAVAIL: "Address not available", +// EAFNOSUPPORT: "Address family not supported by protocol family", +// EAGAIN: "Try again", +// EALREADY: "Socket already connected", +// EBADF: "Bad file number", +// EBADMSG: "Trying to read unreadable message", +// EBUSY: "Device or resource busy", +// ECANCELED: "Operation canceled.", +// ECHILD: "No child processes", +// ECONNABORTED: "Connection aborted", +// ECONNREFUSED: "Connection refused", +// ECONNRESET: "Connection reset by peer", +// EDEADLK: "Deadlock condition", +// EDESTADDRREQ: "Destination address required", +// EDOM: "Math arg out of domain of func", +// EDQUOT: "Quota exceeded", +// EEXIST: "File exists", +// EFAULT: "Bad address", +// EFBIG: "File too large", +// EHOSTUNREACH: "Host is unreachable", +// EIDRM: "Identifier removed", +// EILSEQ: "EILSEQ", +// EINPROGRESS: "Connection already in progress", +// EINTR: "Interrupted system call", +// EINVAL: "Invalid argument", +// EIO: "I/O error", +// EISCONN: "Socket is already connected", +// EISDIR: "Is a directory", +// ELOOP: "Too many symbolic links", +// EMFILE: "Too many open files", +// EMLINK: "Too many links", +// EMSGSIZE: "Message too long", +// EMULTIHOP: "Multihop attempted", +// ENAMETOOLONG: "File name too long", +// ENETDOWN: "Network interface is not configured", +// ENETRESET: "Network dropped connection on reset", +// ENETUNREACH: "Network is unreachable", +// ENFILE: "File table overflow", +// ENOBUFS: "No buffer space available", +// ENODEV: "No such device", +// ENOENT: "No such file or directory", +// ENOEXEC: "Exec format error", +// ENOLCK: "No record locks available", +// ENOLINK: "The link has been severed", +// ENOMEM: "Out of memory", +// ENOMSG: "No message of desired type", +// ENOPROTOOPT: "Protocol not available", +// ENOSPC: "No space left on device", +// ENOSYS: "Not implemented on wasip1", +// ENOTCONN: "Socket is not connected", +// ENOTDIR: "Not a directory", +// ENOTEMPTY: "Directory not empty", +// ENOTRECOVERABLE: "State not recoverable", +// ENOTSOCK: "Socket operation on non-socket", +// ENOTSUP: "Not supported", +// ENOTTY: "Not a typewriter", +// ENXIO: "No such device or address", +// EOVERFLOW: "Value too large for defined data type", +// EOWNERDEAD: "Owner died", +// EPERM: "Operation not permitted", +// EPIPE: "Broken pipe", +// EPROTO: "Protocol error", +// EPROTONOSUPPORT: "Unknown protocol", +// EPROTOTYPE: "Protocol wrong type for socket", +// ERANGE: "Math result not representable", +// EROFS: "Read-only file system", +// ESPIPE: "Illegal seek", +// ESRCH: "No such process", +// ESTALE: "Stale file handle", +// ETIMEDOUT: "Connection timed out", +// ETXTBSY: "Text file busy", +// EXDEV: "Cross-device link", +// ENOTCAPABLE: "Capabilities insufficient", +// } var mapSyscall2Errno = map[syscall.Errno]errno{ - syscall.E2BIG: E2BIG, - syscall.EACCES: EACCES, - syscall.EADDRINUSE: EADDRINUSE, - syscall.EADDRNOTAVAIL: EADDRNOTAVAIL, - syscall.EAFNOSUPPORT: EAFNOSUPPORT, - syscall.EAGAIN: EAGAIN, - syscall.EALREADY: EALREADY, - syscall.EBADF: EBADF, - syscall.EBADMSG: EBADMSG, - syscall.EBUSY: EBUSY, - syscall.ECANCELED: ECANCELED, - syscall.ECHILD: ECHILD, - syscall.ECONNABORTED: ECONNABORTED, - syscall.ECONNREFUSED: ECONNREFUSED, - syscall.ECONNRESET: ECONNRESET, - syscall.EDEADLK: EDEADLK, - syscall.EDESTADDRREQ: EDESTADDRREQ, - syscall.EDOM: EDOM, - syscall.EDQUOT: EDQUOT, - syscall.EEXIST: EEXIST, - syscall.EFAULT: EFAULT, - syscall.EFBIG: EFBIG, - syscall.EHOSTUNREACH: EHOSTUNREACH, - syscall.EIDRM: EIDRM, - syscall.EILSEQ: EILSEQ, - syscall.EINPROGRESS: EINPROGRESS, - syscall.EINTR: EINTR, - syscall.EINVAL: EINVAL, - syscall.EIO: EIO, - syscall.EISCONN: EISCONN, - syscall.EISDIR: EISDIR, - syscall.ELOOP: ELOOP, - syscall.EMFILE: EMFILE, - syscall.EMLINK: EMLINK, - syscall.EMSGSIZE: EMSGSIZE, - syscall.EMULTIHOP: EMULTIHOP, - syscall.ENAMETOOLONG: ENAMETOOLONG, - syscall.ENETDOWN: ENETDOWN, - syscall.ENETRESET: ENETRESET, - syscall.ENETUNREACH: ENETUNREACH, - syscall.ENFILE: ENFILE, - syscall.ENOBUFS: ENOBUFS, - syscall.ENODEV: ENODEV, - syscall.ENOENT: ENOENT, - syscall.ENOEXEC: ENOEXEC, - syscall.ENOLCK: ENOLCK, - syscall.ENOLINK: ENOLINK, - syscall.ENOMEM: ENOMEM, - syscall.ENOMSG: ENOMSG, - syscall.ENOPROTOOPT: ENOPROTOOPT, - syscall.ENOSPC: ENOSPC, - syscall.ENOSYS: ENOSYS, - syscall.ENOTCONN: ENOTCONN, - syscall.ENOTDIR: ENOTDIR, - syscall.ENOTEMPTY: ENOTEMPTY, - // syscall.ENOTRECOVERABLE: ENOTRECOVERABLE, - syscall.ENOTSOCK: ENOTSOCK, - syscall.ENOTSUP: ENOTSUP, - syscall.ENOTTY: ENOTTY, - syscall.ENXIO: ENXIO, - syscall.EOVERFLOW: EOVERFLOW, - // syscall.EOWNERDEAD: EOWNERDEAD, + syscall.E2BIG: E2BIG, + syscall.EACCES: EACCES, + syscall.EADDRINUSE: EADDRINUSE, + syscall.EADDRNOTAVAIL: EADDRNOTAVAIL, + syscall.EAFNOSUPPORT: EAFNOSUPPORT, + syscall.EAGAIN: EAGAIN, + syscall.EALREADY: EALREADY, + syscall.EBADF: EBADF, + syscall.EBADMSG: EBADMSG, + syscall.EBUSY: EBUSY, + syscall.ECANCELED: ECANCELED, + syscall.ECHILD: ECHILD, + syscall.ECONNABORTED: ECONNABORTED, + syscall.ECONNREFUSED: ECONNREFUSED, + syscall.ECONNRESET: ECONNRESET, + syscall.EDEADLK: EDEADLK, + syscall.EDESTADDRREQ: EDESTADDRREQ, + syscall.EDOM: EDOM, + syscall.EDQUOT: EDQUOT, + syscall.EEXIST: EEXIST, + syscall.EFAULT: EFAULT, + syscall.EFBIG: EFBIG, + syscall.EHOSTUNREACH: EHOSTUNREACH, + syscall.EIDRM: EIDRM, + syscall.EILSEQ: EILSEQ, + syscall.EINPROGRESS: EINPROGRESS, + syscall.EINTR: EINTR, + syscall.EINVAL: EINVAL, + syscall.EIO: EIO, + syscall.EISCONN: EISCONN, + syscall.EISDIR: EISDIR, + syscall.ELOOP: ELOOP, + syscall.EMFILE: EMFILE, + syscall.EMLINK: EMLINK, + syscall.EMSGSIZE: EMSGSIZE, + syscall.EMULTIHOP: EMULTIHOP, + syscall.ENAMETOOLONG: ENAMETOOLONG, + syscall.ENETDOWN: ENETDOWN, + syscall.ENETRESET: ENETRESET, + syscall.ENETUNREACH: ENETUNREACH, + syscall.ENFILE: ENFILE, + syscall.ENOBUFS: ENOBUFS, + syscall.ENODEV: ENODEV, + syscall.ENOENT: ENOENT, + syscall.ENOEXEC: ENOEXEC, + syscall.ENOLCK: ENOLCK, + syscall.ENOLINK: ENOLINK, + syscall.ENOMEM: ENOMEM, + syscall.ENOMSG: ENOMSG, + syscall.ENOPROTOOPT: ENOPROTOOPT, + syscall.ENOSPC: ENOSPC, + syscall.ENOSYS: ENOSYS, + syscall.ENOTCONN: ENOTCONN, + syscall.ENOTDIR: ENOTDIR, + syscall.ENOTEMPTY: ENOTEMPTY, + syscall.ENOTRECOVERABLE: ENOTRECOVERABLE, + syscall.ENOTSOCK: ENOTSOCK, + syscall.ENOTSUP: ENOTSUP, + syscall.ENOTTY: ENOTTY, + syscall.ENXIO: ENXIO, + syscall.EOVERFLOW: EOVERFLOW, + syscall.EOWNERDEAD: EOWNERDEAD, syscall.EPERM: EPERM, syscall.EPIPE: EPIPE, syscall.EPROTO: EPROTO, @@ -242,73 +243,73 @@ var mapSyscall2Errno = map[syscall.Errno]errno{ syscall.ESRCH: ESRCH, syscall.ESTALE: ESTALE, syscall.ETIMEDOUT: ETIMEDOUT, - // syscall.ETXTBSY: ETXTBSY, - syscall.EXDEV: EXDEV, + syscall.ETXTBSY: ETXTBSY, + syscall.EXDEV: EXDEV, } var mapErrno2Syscall = map[errno]syscall.Errno{ - E2BIG: syscall.E2BIG, - EACCES: syscall.EACCES, - EADDRINUSE: syscall.EADDRINUSE, - EADDRNOTAVAIL: syscall.EADDRNOTAVAIL, - EAFNOSUPPORT: syscall.EAFNOSUPPORT, - EAGAIN: syscall.EAGAIN, - EALREADY: syscall.EALREADY, - EBADF: syscall.EBADF, - EBADMSG: syscall.EBADMSG, - EBUSY: syscall.EBUSY, - ECANCELED: syscall.ECANCELED, - ECHILD: syscall.ECHILD, - ECONNABORTED: syscall.ECONNABORTED, - ECONNREFUSED: syscall.ECONNREFUSED, - ECONNRESET: syscall.ECONNRESET, - EDEADLK: syscall.EDEADLK, - EDESTADDRREQ: syscall.EDESTADDRREQ, - EDOM: syscall.EDOM, - EDQUOT: syscall.EDQUOT, - EEXIST: syscall.EEXIST, - EFAULT: syscall.EFAULT, - EFBIG: syscall.EFBIG, - EHOSTUNREACH: syscall.EHOSTUNREACH, - EIDRM: syscall.EIDRM, - EILSEQ: syscall.EILSEQ, - EINPROGRESS: syscall.EINPROGRESS, - EINTR: syscall.EINTR, - EINVAL: syscall.EINVAL, - EIO: syscall.EIO, - EISCONN: syscall.EISCONN, - EISDIR: syscall.EISDIR, - ELOOP: syscall.ELOOP, - EMFILE: syscall.EMFILE, - EMLINK: syscall.EMLINK, - EMSGSIZE: syscall.EMSGSIZE, - EMULTIHOP: syscall.EMULTIHOP, - ENAMETOOLONG: syscall.ENAMETOOLONG, - ENETDOWN: syscall.ENETDOWN, - ENETRESET: syscall.ENETRESET, - ENETUNREACH: syscall.ENETUNREACH, - ENFILE: syscall.ENFILE, - ENOBUFS: syscall.ENOBUFS, - ENODEV: syscall.ENODEV, - ENOENT: syscall.ENOENT, - ENOEXEC: syscall.ENOEXEC, - ENOLCK: syscall.ENOLCK, - ENOLINK: syscall.ENOLINK, - ENOMEM: syscall.ENOMEM, - ENOMSG: syscall.ENOMSG, - ENOPROTOOPT: syscall.ENOPROTOOPT, - ENOSPC: syscall.ENOSPC, - ENOSYS: syscall.ENOSYS, - ENOTCONN: syscall.ENOTCONN, - ENOTDIR: syscall.ENOTDIR, - ENOTEMPTY: syscall.ENOTEMPTY, - // ENOTRECOVERABLE: syscall.ENOTRECOVERABLE, - ENOTSOCK: syscall.ENOTSOCK, - ENOTSUP: syscall.ENOTSUP, - ENOTTY: syscall.ENOTTY, - ENXIO: syscall.ENXIO, - EOVERFLOW: syscall.EOVERFLOW, - // EOWNERDEAD: syscall.EOWNERDEAD, + E2BIG: syscall.E2BIG, + EACCES: syscall.EACCES, + EADDRINUSE: syscall.EADDRINUSE, + EADDRNOTAVAIL: syscall.EADDRNOTAVAIL, + EAFNOSUPPORT: syscall.EAFNOSUPPORT, + EAGAIN: syscall.EAGAIN, + EALREADY: syscall.EALREADY, + EBADF: syscall.EBADF, + EBADMSG: syscall.EBADMSG, + EBUSY: syscall.EBUSY, + ECANCELED: syscall.ECANCELED, + ECHILD: syscall.ECHILD, + ECONNABORTED: syscall.ECONNABORTED, + ECONNREFUSED: syscall.ECONNREFUSED, + ECONNRESET: syscall.ECONNRESET, + EDEADLK: syscall.EDEADLK, + EDESTADDRREQ: syscall.EDESTADDRREQ, + EDOM: syscall.EDOM, + EDQUOT: syscall.EDQUOT, + EEXIST: syscall.EEXIST, + EFAULT: syscall.EFAULT, + EFBIG: syscall.EFBIG, + EHOSTUNREACH: syscall.EHOSTUNREACH, + EIDRM: syscall.EIDRM, + EILSEQ: syscall.EILSEQ, + EINPROGRESS: syscall.EINPROGRESS, + EINTR: syscall.EINTR, + EINVAL: syscall.EINVAL, + EIO: syscall.EIO, + EISCONN: syscall.EISCONN, + EISDIR: syscall.EISDIR, + ELOOP: syscall.ELOOP, + EMFILE: syscall.EMFILE, + EMLINK: syscall.EMLINK, + EMSGSIZE: syscall.EMSGSIZE, + EMULTIHOP: syscall.EMULTIHOP, + ENAMETOOLONG: syscall.ENAMETOOLONG, + ENETDOWN: syscall.ENETDOWN, + ENETRESET: syscall.ENETRESET, + ENETUNREACH: syscall.ENETUNREACH, + ENFILE: syscall.ENFILE, + ENOBUFS: syscall.ENOBUFS, + ENODEV: syscall.ENODEV, + ENOENT: syscall.ENOENT, + ENOEXEC: syscall.ENOEXEC, + ENOLCK: syscall.ENOLCK, + ENOLINK: syscall.ENOLINK, + ENOMEM: syscall.ENOMEM, + ENOMSG: syscall.ENOMSG, + ENOPROTOOPT: syscall.ENOPROTOOPT, + ENOSPC: syscall.ENOSPC, + ENOSYS: syscall.ENOSYS, + ENOTCONN: syscall.ENOTCONN, + ENOTDIR: syscall.ENOTDIR, + ENOTEMPTY: syscall.ENOTEMPTY, + ENOTRECOVERABLE: syscall.ENOTRECOVERABLE, + ENOTSOCK: syscall.ENOTSOCK, + ENOTSUP: syscall.ENOTSUP, + ENOTTY: syscall.ENOTTY, + ENXIO: syscall.ENXIO, + EOVERFLOW: syscall.EOVERFLOW, + EOWNERDEAD: syscall.EOWNERDEAD, EPERM: syscall.EPERM, EPIPE: syscall.EPIPE, EPROTO: syscall.EPROTO, @@ -320,6 +321,6 @@ var mapErrno2Syscall = map[errno]syscall.Errno{ ESRCH: syscall.ESRCH, ESTALE: syscall.ESTALE, ETIMEDOUT: syscall.ETIMEDOUT, - // ETXTBSY: syscall.ETXTBSY, - EXDEV: syscall.EXDEV, + ETXTBSY: syscall.ETXTBSY, + EXDEV: syscall.EXDEV, }