netaddr: greet a tcp peer off the accept path (V-581)
A peer that connected and then said nothing froze the whole seam. The token handshake ran inline in Listener.Accept, so the five seconds of handshakeTimeout the silent peer was owed were five seconds no other connection could be accepted. One unauthenticated stranger holding a socket open was a denial of service on every daemon behind a tcp seam, which is the path V-515 is about to put mavwaked and mavenclient on. Accept now takes authorized connections off a channel. A background loop pulls from the wrapped listener and greets each connection in its own goroutine, so a slow greeting costs only its own connection. Listener.Close releases anything still waiting to be handed over. A unix seam delegates straight to the wrapped listener and grows no machinery, because it has no handshake to run. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"net"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// A scheme-less address must stay unix. Every deploy in the tree writes a bare
|
||||
@@ -140,6 +141,41 @@ func TestTCPUngreetedPeerDoesNotKillTheListener(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// A peer that connects and never speaks must not hold the seam. The greeting
|
||||
// it owes is bounded by handshakeTimeout, so serving it on the accept path
|
||||
// costs every later connection those five seconds.
|
||||
func TestTCPSilentPeerDoesNotStallTheSeam(t *testing.T) {
|
||||
ln, addr := listenLoopback(t, "s3cret")
|
||||
defer ln.Close()
|
||||
go echoOnce(ln)
|
||||
|
||||
mute, err := net.Dial("tcp", addr.Address)
|
||||
if err != nil {
|
||||
t.Fatalf("mute dial: %v", err)
|
||||
}
|
||||
defer mute.Close()
|
||||
|
||||
done := make(chan string, 1)
|
||||
go func() {
|
||||
c, err := Dial(addr)
|
||||
if err != nil {
|
||||
done <- "dial: " + err.Error()
|
||||
return
|
||||
}
|
||||
defer c.Close()
|
||||
done <- roundTrip(t, c, "still here")
|
||||
}()
|
||||
|
||||
select {
|
||||
case got := <-done:
|
||||
if got != "still here" {
|
||||
t.Fatalf("got %q", got)
|
||||
}
|
||||
case <-time.After(handshakeTimeout / 2):
|
||||
t.Fatal("a silent peer stalled the listener")
|
||||
}
|
||||
}
|
||||
|
||||
// A tcp seam with no token is a misconfiguration, and it must fail at bind
|
||||
// rather than serve the owner's turns to anyone who connects.
|
||||
func TestTCPListenRequiresToken(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user