915dca44fe
Each call to newIPN() starts an independent Go backend that calls safesocket.Listen() to serve the ipnserver IPC channel. Because memName was a global constant, the second instance would fail with "addr unavailable" and log.Fatal the whole WASM process. Use an atomic counter to give each listener a distinct name (Tailscale-IPN-1, Tailscale-IPN-2, …). The connect() path is unchanged: in the wasm/tsconnect build all LocalAPI calls go through the in-process httptest handler, so connect() is never called. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
29 lines
660 B
Go
29 lines
660 B
Go
// Copyright (c) Tailscale Inc & contributors
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
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) {
|
|
name := fmt.Sprintf("%s-%d", memName, memSeq.Add(1))
|
|
return memconn.Listen("memu", name)
|
|
}
|
|
|
|
func connect(ctx context.Context, _ string) (net.Conn, error) {
|
|
return memconn.DialContext(ctx, "memu", memName)
|
|
}
|