1
0
mirror of https://github.com/jhillyerd/inbucket.git synced 2026-10-29 13:42:30 +00:00

fix(pop3): reject deleted messages and failed TLS handshakes (#620)

- RETR and TOP now respond "-ERR You deleted message N" for messages
  marked deleted, as RFC 1939 sections 5 (RETR) and 7 (TOP) require;
  previously the message content was still sent.
- STLS: a failed TLS handshake previously continued the session with
  the dead TLS connection installed as s.conn and a bogus tlsState,
  producing confusing follow-on errors. RFC 2595 section 4 requires
  the negotiation to complete before further commands, so a failure
  is now fatal: log it and terminate the session.

Updates the scripted tests that previously pinned the old behavior,
and adds a STLS handshake failure regression test.

Signed-off-by: James Hillyerd <james@hillyerd.com>
This commit is contained in:
James Hillyerd
2026-09-16 18:10:40 -07:00
committed by GitHub
parent 8bad098be9
commit a85a4ab996
3 changed files with 73 additions and 16 deletions
+45
View File
@@ -288,6 +288,51 @@ func TestForceTLS(t *testing.T) {
}
}
// RFC 2595 §4: after the +OK response to STLS, a TLS negotiation begins
// immediately and the client must not issue further commands until it
// completes. A failed negotiation is therefore fatal to the session: the
// server must not keep the session running on the dead connection.
func TestSTLSHandshakeFailure(t *testing.T) {
ds := test.NewStore()
server := setupPOPServer(t, ds, true, false)
pipe := setupPOPSession(t, server)
c := textproto.NewConn(pipe)
defer func() {
_ = c.Close()
server.Drain()
}()
reply, err := c.ReadLine()
if err != nil {
t.Fatalf("Reading initial line failed %v", err)
}
if !strings.HasPrefix(reply, "+OK") {
t.Fatalf("Initial line is not +OK")
}
if err := c.PrintfLine("STLS"); err != nil {
t.Fatalf("Failed to send STLS; %v.", err)
}
reply, err = c.ReadLine()
if err != nil {
t.Fatalf("Reading STLS reply line failed %v", err)
}
if !strings.HasPrefix(reply, "+OK") {
t.Fatalf("STLS failed: %s", reply)
}
// Send bytes that cannot begin a TLS handshake.
if _, err := pipe.Write([]byte("this is not a TLS handshake\r\n")); err != nil {
t.Fatalf("Failed to write garbage; %v.", err)
}
// The server must terminate the session without further responses.
_, err = c.ReadLine()
if err == nil {
t.Fatal("Expected connection close after failed handshake")
}
}
// net.Pipe does not implement deadlines
type mockConn struct {
net.Conn