mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-11-02 19:25:05 +03:00
8bbfda0510
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.
96 lines
2.3 KiB
Go
96 lines
2.3 KiB
Go
package email
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"errors"
|
|
"net"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestSendPlainReturnsOnStalledServer(t *testing.T) {
|
|
orig := smtpDeadline
|
|
smtpDeadline = 300 * time.Millisecond
|
|
t.Cleanup(func() { smtpDeadline = orig })
|
|
|
|
ln, err := net.Listen("tcp", "127.0.0.1:0")
|
|
if err != nil {
|
|
t.Fatalf("listen: %v", err)
|
|
}
|
|
defer ln.Close()
|
|
|
|
stall := make(chan struct{})
|
|
defer close(stall)
|
|
go func() {
|
|
conn, err := ln.Accept()
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer conn.Close()
|
|
<-stall
|
|
}()
|
|
|
|
s := &EmailService{}
|
|
done := make(chan error, 1)
|
|
go func() {
|
|
done <- s.sendPlain(ln.Addr().String(), nil, "from@example.com",
|
|
[]string{"to@example.com"}, []byte("body"), "example.com")
|
|
}()
|
|
|
|
select {
|
|
case err := <-done:
|
|
if err == nil {
|
|
t.Fatal("expected an error from a silent SMTP server, got nil")
|
|
}
|
|
case <-time.After(3 * time.Second):
|
|
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)
|
|
}
|
|
})
|
|
}
|
|
}
|