diff --git a/net/memnet/listener.go b/net/memnet/listener.go index d84a2e443..202026e16 100644 --- a/net/memnet/listener.go +++ b/net/memnet/listener.go @@ -22,6 +22,7 @@ type Listener struct { ch chan Conn closeOnce sync.Once closed chan struct{} + onClose func() // or nil // NewConn, if non-nil, is called to create a new pair of connections // when dialing. If nil, NewConn is used. @@ -44,9 +45,14 @@ func (l *Listener) Addr() net.Addr { // Close closes the pipe listener. func (l *Listener) Close() error { + var cleanup func() l.closeOnce.Do(func() { + cleanup = l.onClose close(l.closed) }) + if cleanup != nil { + cleanup() + } return nil } diff --git a/net/memnet/memnet.go b/net/memnet/memnet.go index 7c2435684..1e43df2da 100644 --- a/net/memnet/memnet.go +++ b/net/memnet/memnet.go @@ -61,6 +61,11 @@ func (m *Network) Listen(network, address string) (net.Listener, error) { } ln := Listen(key) m.lns[key] = ln + ln.onClose = func() { + m.mu.Lock() + delete(m.lns, key) + m.mu.Unlock() + } return ln, nil } } diff --git a/net/memnet/memnet_test.go b/net/memnet/memnet_test.go new file mode 100644 index 000000000..38086cec0 --- /dev/null +++ b/net/memnet/memnet_test.go @@ -0,0 +1,23 @@ +// Copyright (c) Tailscale Inc & AUTHORS +// SPDX-License-Identifier: BSD-3-Clause + +package memnet + +import "testing" + +func TestListenAddressReuse(t *testing.T) { + var nw Network + ln1, err := nw.Listen("tcp", "127.0.0.1:80") + if err != nil { + t.Fatalf("listen failed: %v", err) + } + if _, err := nw.Listen("tcp", "127.0.0.1:80"); err == nil { + t.Errorf("listen on in-use address succeeded") + } + if err := ln1.Close(); err != nil { + t.Fatalf("close failed: %v", err) + } + if _, err := nw.Listen("tcp", "127.0.0.1:80"); err != nil { + t.Errorf("listen on same address after close failed: %v", err) + } +}