From 00d16d6ae259348b2223c9885a7739820284fc0b Mon Sep 17 00:00:00 2001 From: Codinget Date: Sun, 30 Aug 2026 00:59:47 +0000 Subject: [PATCH] refactor(tsconnect/wasm): drop the unreachable LocalAPI socket jsIPN.localAPI serves localapi.Handler in-process through an httptest.ResponseRecorder, so nothing ever dialled the safesocket listener that run() opened. The other in-tree callers of safesocket.ConnectContext do not apply either: driveimpl's FileSystemForRemote is replaced by jsFileSystemForRemote in this build, and logpolicy's fallback is behind version.IsWindowsGUI. Remove the listener, and with it ipnserver, whose only remaining use was serving that listener. ipnserver.New builds a struct and SetLocalBackend stores a pointer, so dropping both leaves LocalBackend untouched. Two behaviours go with it: srv.Run's deferred lb.Shutdown, which made shutdown call lb.Shutdown twice, and its localapi.Shutdown bus subscription, which nothing in this build emits. safesocket's generated per-listener name existed so several IPNs could share one runtime. Each runtime has its own Go heap and its own memconn registry, so the fixed name never conflicted between runtimes, and there is now at most one IPN in each. Restoring the fixed name returns safesocket_js.go to its upstream contents. Co-Authored-By: claude-opus-5 --- cmd/tsconnect/wasm/wasm_js.go | 24 ------------------------ safesocket/safesocket_js.go | 9 +-------- 2 files changed, 1 insertion(+), 32 deletions(-) diff --git a/cmd/tsconnect/wasm/wasm_js.go b/cmd/tsconnect/wasm/wasm_js.go index 1778e597e..67208be18 100644 --- a/cmd/tsconnect/wasm/wasm_js.go +++ b/cmd/tsconnect/wasm/wasm_js.go @@ -44,7 +44,6 @@ import ( "tailscale.com/ipn" "tailscale.com/ipn/ipnauth" "tailscale.com/ipn/ipnlocal" - "tailscale.com/ipn/ipnserver" "tailscale.com/ipn/localapi" "tailscale.com/ipn/store/mem" "tailscale.com/logpolicy" @@ -53,7 +52,6 @@ import ( "tailscale.com/net/netns" "tailscale.com/net/tsaddr" "tailscale.com/net/tsdial" - "tailscale.com/safesocket" "tailscale.com/tailcfg" "tailscale.com/tsd" "tailscale.com/types/logid" @@ -205,7 +203,6 @@ func newIPN(jsConfig js.Value, shutdownCh chan struct{}) (map[string]any, error) // initDriveForRemote must be called before NewLocalBackend (SubSystem is set-once). driveFS := initDriveForRemote(sys) - srv := ipnserver.New(logf, logid, sys.Bus.Get(), sys.NetMon.Get()) lb, err := ipnlocal.NewLocalBackend(logf, logid, sys, controlclient.LoginEphemeral) if err != nil { return nil, fmt.Errorf("ipnlocal.NewLocalBackend: %w", err) @@ -214,11 +211,9 @@ func newIPN(jsConfig js.Value, shutdownCh chan struct{}) (map[string]any, error) return nil, fmt.Errorf("starting netstack: %w", err) } wireTaildropFileOps(lb, jsConfig.Get("fileOps")) - srv.SetLocalBackend(lb) jsIPN := &jsIPN{ dialer: dialer, - srv: srv, lb: lb, ns: ns, controlURL: controlURL, @@ -411,7 +406,6 @@ func newIPN(jsConfig js.Value, shutdownCh chan struct{}) (map[string]any, error) type jsIPN struct { dialer *tsdial.Dialer - srv *ipnserver.Server lb *ipnlocal.LocalBackend ns *netstack.Impl controlURL string @@ -422,9 +416,6 @@ type jsIPN struct { funnelMu sync.Mutex funnelPorts map[uint16]*funnelListenerEntry - // ln is the safesocket listener created by run(); stored here so shutdown - // can close it and unblock srv.Run. - ln net.Listener shutdownCh chan struct{} // closed by shutdown() to unblock main() shutdownOnce sync.Once } @@ -617,18 +608,6 @@ func (i *jsIPN) run(jsCallbacks js.Value) { } }() - ln, err := safesocket.Listen("") - if err != nil { - log.Fatalf("safesocket.Listen: %v", err) - } - i.ln = ln - - go func() { - err := i.srv.Run(context.Background(), ln) - if err != nil && !errors.Is(err, net.ErrClosed) { - log.Fatalf("ipnserver.Run exited: %v", err) - } - }() } func (i *jsIPN) login() { @@ -652,9 +631,6 @@ func (i *jsIPN) shutdown() js.Value { if i.lb != nil { i.lb.Shutdown() } - if i.ln != nil { - i.ln.Close() - } close(i.shutdownCh) }) return nil, nil diff --git a/safesocket/safesocket_js.go b/safesocket/safesocket_js.go index 0807885a8..746fea511 100644 --- a/safesocket/safesocket_js.go +++ b/safesocket/safesocket_js.go @@ -5,22 +5,15 @@ 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) + return memconn.Listen("memu", memName) } func connect(ctx context.Context, _ string) (net.Conn, error) {