diff --git a/cmd/tsconnect/wasm/wasm_js.go b/cmd/tsconnect/wasm/wasm_js.go index 054bcbea2..9dcea4d20 100644 --- a/cmd/tsconnect/wasm/wasm_js.go +++ b/cmd/tsconnect/wasm/wasm_js.go @@ -637,8 +637,12 @@ func (i *jsIPN) logout() { func (i *jsIPN) shutdown() js.Value { return makePromise(func() (any, error) { i.shutdownOnce.Do(func() { - i.lb.Shutdown() - i.ln.Close() + if i.lb != nil { + i.lb.Shutdown() + } + if i.ln != nil { + i.ln.Close() + } close(i.shutdownCh) }) return nil, nil @@ -873,6 +877,11 @@ func (i *jsIPN) listen(network, addr string) js.Value { if n == "tcp" { n = "tcp4" } + // netstack.ListenTCP requires a full host:port; normalise the + // standard net.Listen form ":port" that omits the host. + if strings.HasPrefix(addr, ":") { + addr = "0.0.0.0" + addr + } ln, err := i.ns.ListenTCP(n, addr) if err != nil { return nil, err diff --git a/safesocket/safesocket_js.go b/safesocket/safesocket_js.go index 746fea511..0807885a8 100644 --- a/safesocket/safesocket_js.go +++ b/safesocket/safesocket_js.go @@ -5,15 +5,22 @@ package safesocket import ( "context" + "fmt" "net" + "sync/atomic" "github.com/akutz/memconn" ) const memName = "Tailscale-IPN" +// memSeq ensures each IPN instance in the same WASM process gets a distinct +// memconn address, so concurrent instances do not conflict on the registry. +var memSeq atomic.Int64 + func listen(path string) (net.Listener, error) { - return memconn.Listen("memu", memName) + name := fmt.Sprintf("%s-%d", memName, memSeq.Add(1)) + return memconn.Listen("memu", name) } func connect(ctx context.Context, _ string) (net.Conn, error) {