mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-11-07 21:55:01 +03:00
fix(email): cap what one SMTP session reads from the server
net/smtp buffers a server reply of any length, so a mail server, or anyone on the path before STARTTLS, could send a greeting or EHLO reply that never ends and grow the panel's heap until the 30 s deadline, on every notification. Each session now reads at most 1 MiB from the server, below TLS, and fails with a clear error past it.
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
package email
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -44,3 +47,49 @@ func TestSendPlainReturnsOnStalledServer(t *testing.T) {
|
||||
t.Fatal("sendPlain did not return on a stalled server within the deadline")
|
||||
}
|
||||
}
|
||||
|
||||
// The flood is capped well above smtpMaxServerBytes: without the cap the client
|
||||
// buffers it all and then fails on the closed socket, not errSMTPReplyTooLarge.
|
||||
func TestSendPlainStopsReadingOversizedReply(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
greeting string
|
||||
flood []byte
|
||||
}{
|
||||
{"greeting line that never ends", "220 ", bytes.Repeat([]byte("a"), 64<<10)},
|
||||
{"EHLO reply that never ends", "220 ready\r\n", bytes.Repeat([]byte("250-X\r\n"), 8<<10)},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ln, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatalf("listen: %v", err)
|
||||
}
|
||||
defer ln.Close()
|
||||
|
||||
go func() {
|
||||
conn, err := ln.Accept()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
_, _ = conn.Write([]byte(tt.greeting))
|
||||
if tt.greeting[len(tt.greeting)-1] == '\n' {
|
||||
_, _ = bufio.NewReader(conn).ReadString('\n')
|
||||
}
|
||||
for sent := 0; sent < 16<<20; sent += len(tt.flood) {
|
||||
if _, err := conn.Write(tt.flood); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
s := &EmailService{}
|
||||
err = s.sendPlain(ln.Addr().String(), nil, "from@example.com",
|
||||
[]string{"to@example.com"}, []byte("body"), "example.com")
|
||||
if !errors.Is(err, errSMTPReplyTooLarge) {
|
||||
t.Fatalf("sendPlain error = %v, want %v", err, errSMTPReplyTooLarge)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user