Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: build
on:
push:
tags:
- v*
branches:
- main
pull_request:
branches:
- main

concurrency:
group: ${{ github.workflow }}-${{ github.event.number || github.ref }}
cancel-in-progress: true

jobs:
test:
name: Go Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
with:
go-version-file: go.mod
check-latest: true

- name: Install Go tip
run: |
curl -sL https://storage.googleapis.com/go-build-snap/go/linux-amd64/$(git ls-remote https://github.com/golang/go.git HEAD | awk '{print $1;}').tar.gz -o gotip.tar.gz
ls -lah gotip.tar.gz
mkdir -p $HOME/gotip
tar -C $HOME/gotip -xzf gotip.tar.gz
- run: make test GO=$HOME/gotip/bin/go GOPATH=$HOME/gotip
19 changes: 19 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
wasirun

# Test binary, built with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Dependency directories (remove the comment below to include it)
# vendor/

# Emacs
*~
15 changes: 15 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
.PHONY: test lint wasirun

GOPATH ?= $(shell $(GO) env GOPATH)
wasirun = $(GOPATH)/bin/wasirun

wasip1.test: go.mod $(wildcard wasip1/*.go)
GOARCH=wasm GOOS=wasip1 $(GO) test -c ./wasip1

test: wasirun wasip1.test
$(wasirun) wasip1.test -test.v

wasirun: $(wasirun)

$(wasirun):
$(GO) install github.com/stealthrocket/wasi-go/cmd/wasirun@latest
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
module github.com/stealthrocket/net

go 1.20

require golang.org/x/net v0.10.0 // indirect
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
154 changes: 54 additions & 100 deletions wasip1/dial_wasip1.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,37 +9,33 @@ import (

// Dial connects to the address on the named network.
func Dial(network, address string) (net.Conn, error) {
return DialContext(context.Background(), network, address)
}

// DialContext is a variant of Dial that accepts a context.
func DialContext(ctx context.Context, network, address string) (net.Conn, error) {
addr, err := lookupAddr("dial", network, address)
if err != nil {
addr := &netAddr{network, address}
return nil, dialErr(addr, err)
}
conn, err := dialAddr(addr)
conn, err := dialAddr(ctx, addr)
if err != nil {
return nil, dialErr(addr, err)
}
return conn, nil
}

// DialContext is a variant of Dial that accepts a context.
func DialContext(ctx context.Context, network, address string) (net.Conn, error) {
select {
case <-ctx.Done():
addr := &netAddr{network, address}
return nil, dialErr(addr, context.Cause(ctx))
default:
return Dial(network, address)
}
}

func dialErr(addr net.Addr, err error) error {
return newOpError("dial", addr, err)
}

func dialAddr(addr net.Addr) (net.Conn, error) {
func dialAddr(ctx context.Context, addr net.Addr) (net.Conn, error) {
proto := family(addr)
sotype := socketType(addr)

sotype, err := socketType(addr)
if err != nil {
return nil, os.NewSyscallError("socket", err)
}
fd, err := socket(proto, sotype, 0)
if err != nil {
return nil, os.NewSyscallError("socket", err)
Expand All @@ -49,7 +45,6 @@ func dialAddr(addr net.Addr) (net.Conn, error) {
syscall.Close(fd)
return nil, os.NewSyscallError("setnonblock", err)
}

if sotype == SOCK_DGRAM && proto != AF_UNIX {
if err := setsockopt(fd, SOL_SOCKET, SO_BROADCAST, 1); err != nil {
syscall.Close(fd)
Expand All @@ -61,7 +56,6 @@ func dialAddr(addr net.Addr) (net.Conn, error) {
if err != nil {
return nil, os.NewSyscallError("connect", err)
}

var inProgress bool
switch err := connect(fd, connectAddr); err {
case nil:
Expand All @@ -80,96 +74,56 @@ func dialAddr(addr net.Addr) (net.Conn, error) {
if err != nil {
return nil, err
}
rawConnErr := rawConn.Write(func(fd uintptr) bool {
var value int
value, err = getsockopt(int(fd), SOL_SOCKET, SO_ERROR)
if err != nil {
return true // done

errch := make(chan error)
go func() {
var err error
rawConnErr := rawConn.Write(func(fd uintptr) bool {
var value int
value, err = getsockopt(int(fd), SOL_SOCKET, SO_ERROR)
if err != nil {
return true // done
}
switch syscall.Errno(value) {
case syscall.EINPROGRESS, syscall.EINTR:
return false // continue
case syscall.EISCONN:
err = nil
return true
case syscall.Errno(0):
// The net poller can wake up spuriously. Check that we are
// are really connected.
_, err := getpeername(int(fd))
return err == nil
default:
return true
}
})
if err == nil {
err = rawConnErr
}
switch syscall.Errno(value) {
case syscall.EINPROGRESS, syscall.EINTR:
return false // continue
case syscall.EISCONN:
err = nil
return true
case syscall.Errno(0):
// The net poller can wake up spuriously. Check that we are
// are really connected.
_, err := getpeername(int(fd))
return err == nil
default:
return true
errch <- err
}()

select {
case err := <-errch:
if err != nil {
return nil, os.NewSyscallError("connect", err)
}
})
if err == nil {
err = rawConnErr
}
if err != nil {
return nil, os.NewSyscallError("connect", err)
case <-ctx.Done():
// This should interrupt the async connect operation handled by the
// goroutine.
f.Close()
// Wait for the goroutine to complete, we can safely discard the
// error here because we don't care about the socket anymore.
<-errch
return nil, context.Cause(ctx)
}
}

c, err := net.FileConn(f)
if err != nil {
return nil, err
}

// TODO: get local+peer address; wrap FileConn to implement LocalAddr() and RemoteAddr()
return c, nil
}

func family(addr net.Addr) int {
var ip net.IP
switch a := addr.(type) {
case *net.UnixAddr:
return AF_UNIX
case *net.TCPAddr:
ip = a.IP
case *net.UDPAddr:
ip = a.IP
case *net.IPAddr:
ip = a.IP
}
if ip.To4() != nil {
return AF_INET
} else if len(ip) == net.IPv6len {
return AF_INET6
}
return AF_INET
}

func socketType(addr net.Addr) int {
switch addr.Network() {
case "tcp", "unix":
return SOCK_STREAM
case "udp", "unixgram":
return SOCK_DGRAM
default:
panic("not implemented")
}
}

func socketAddress(addr net.Addr) (sockaddr, error) {
var ip net.IP
var port int
switch a := addr.(type) {
case *net.UnixAddr:
return &sockaddrUnix{name: a.Name}, nil
case *net.TCPAddr:
ip, port = a.IP, a.Port
case *net.UDPAddr:
ip, port = a.IP, a.Port
case *net.IPAddr:
ip = a.IP
}
if ipv4 := ip.To4(); ipv4 != nil {
return &sockaddrInet4{addr: ([4]byte)(ipv4), port: port}, nil
} else if len(ip) == net.IPv6len {
return &sockaddrInet6{addr: ([16]byte)(ip), port: port}, nil
} else {
return nil, &net.AddrError{
Err: "unsupported address type",
Addr: addr.String(),
}
}
return makeConn(c)
}
27 changes: 20 additions & 7 deletions wasip1/listen_wasip1.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@ func listenErr(addr net.Addr, err error) error {
}

func listenAddr(addr net.Addr) (net.Listener, error) {
fd, err := socket(family(addr), socketType(addr), 0)
sotype, err := socketType(addr)
if err != nil {
return nil, os.NewSyscallError("socket", err)
}
fd, err := socket(family(addr), sotype, 0)
if err != nil {
return nil, os.NewSyscallError("socket", err)
}
Expand All @@ -39,29 +43,39 @@ func listenAddr(addr net.Addr) (net.Listener, error) {
return nil, os.NewSyscallError("setsockopt", err)
}

listenAddr, err := socketAddress(addr)
bindAddr, err := socketAddress(addr)
if err != nil {
return nil, os.NewSyscallError("bind", err)
}

if err := bind(fd, listenAddr); err != nil {
if err := bind(fd, bindAddr); err != nil {
syscall.Close(fd)
return nil, os.NewSyscallError("bind", err)
}

const backlog = 64 // TODO: configurable?
if err := listen(fd, backlog); err != nil {
syscall.Close(fd)
return nil, os.NewSyscallError("listen", err)
}

sockaddr, err := getsockname(fd)
if err != nil {
syscall.Close(fd)
return nil, os.NewSyscallError("getsockname", err)
}

f := os.NewFile(uintptr(fd), "")
defer f.Close()

l, err := net.FileListener(f)
if err != nil {
return nil, err
}
switch l.(type) {
case *net.UnixListener:
addr = sockaddrToUnixAddr(sockaddr)
case *net.TCPListener:
addr = sockaddrToTCPAddr(sockaddr)
}
return &listener{l, addr}, nil
}

Expand All @@ -75,8 +89,7 @@ func (l *listener) Accept() (net.Conn, error) {
if err != nil {
return nil, err
}
// TODO: get local+peer address; wrap Conn to implement LocalAddr() and RemoteAddr()
return c, nil
return makeConn(c)
}

func (l *listener) Addr() net.Addr {
Expand Down
Loading