smtpd/smtpd_test.go

1549 lines
36 KiB
Go

package smtpd_test
import (
"bytes"
"crypto/tls"
"errors"
"fmt"
"log"
"net"
"net/smtp"
"net/textproto"
"os"
"strings"
"testing"
"time"
"git.tcp.direct/Mirrors/smtpd"
)
var localhostCert = []byte(`-----BEGIN CERTIFICATE-----
MIIFkzCCA3ugAwIBAgIUQvhoyGmvPHq8q6BHrygu4dPp0CkwDQYJKoZIhvcNAQEL
BQAwWTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MB4X
DTIwMDUyMTE2MzI1NVoXDTMwMDUxOTE2MzI1NVowWTELMAkGA1UEBhMCQVUxEzAR
BgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5
IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
MIICCgKCAgEAk773plyfK4u2uIIZ6H7vEnTb5qJT6R/KCY9yniRvCFV+jCrISAs9
0pgU+/P8iePnZRGbRCGGt1B+1/JAVLIYFZuawILHNs4yWKAwh0uNpR1Pec8v7vpq
NpdUzXKQKIqFynSkcLA8c2DOZwuhwVc8rZw50yY3r4i4Vxf0AARGXapnBfy6WerR
/6xT7y/OcK8+8aOirDQ9P6WlvZ0ynZKi5q2o1eEVypT2us9r+HsCYosKEEAnjzjJ
wP5rvredxUqb7OupIkgA4Nq80+4tqGGQfWetmoi3zXRhKpijKjgxBOYEqSUWm9ws
/aC91Iy5RawyTB0W064z75OgfuI5GwFUbyLD0YVN4DLSAI79GUfvc8NeLEXpQvYq
+f8P+O1Hbv2AQ28IdbyQrNefB+/WgjeTvXLploNlUihVhpmLpptqnauw/DY5Ix51
w60lHIZ6esNOmMQB+/z/IY5gpmuo66yH8aSCPSYBFxQebB7NMqYGOS9nXx62/Bn1
OUVXtdtrhfbbdQW6zMZjka0t8m83fnGw3ISyBK2NNnSzOgycu0ChsW6sk7lKyeWa
85eJGsQWIhkOeF9v9GAIH/qsrgVpToVC9Krbk+/gqYIYF330tHQrzp6M6LiG5OY1
P7grUBovN2ZFt10B97HxWKa2f/8t9sfHZuKbfLSFbDsyI2JyNDh+Vk0CAwEAAaNT
MFEwHQYDVR0OBBYEFOLdIQUr3gDQF5YBor75mlnCdKngMB8GA1UdIwQYMBaAFOLd
IQUr3gDQF5YBor75mlnCdKngMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL
BQADggIBAGddhQMVMZ14TY7bU8CMuc9IrXUwxp59QfqpcXCA2pHc2VOWkylv2dH7
ta6KooPMKwJ61d+coYPK1zMUvNHHJCYVpVK0r+IGzs8mzg91JJpX2gV5moJqNXvd
Fy6heQJuAvzbb0Tfsv8KN7U8zg/ovpS7MbY+8mRJTQINn2pCzt2y2C7EftLK36x0
KeBWqyXofBJoMy03VfCRqQlWK7VPqxluAbkH+bzji1g/BTkoCKzOitAbjS5lT3sk
oCrF9N6AcjpFOH2ZZmTO4cZ6TSWfrb/9OWFXl0TNR9+x5c/bUEKoGeSMV1YT1SlK
TNFMUlq0sPRgaITotRdcptc045M6KF777QVbrYm/VH1T3pwPGYu2kUdYHcteyX9P
8aRG4xsPGQ6DD7YjBFsif2fxlR3nQ+J/l/+eXHO4C+eRbxi15Z2NjwVjYpxZlUOq
HD96v516JkMJ63awbY+HkYdEUBKqR55tzcvNWnnfiboVmIecjAjoV4zStwDIti9u
14IgdqqAbnx0ALbUWnvfFloLdCzPPQhgLHpTeRSEDPljJWX8rmy8iQtRb0FWYQ3z
A2wsUyutzK19nt4hjVrTX0At9ku3gMmViXFlbvyA1Y4TuhdUYqJauMBrWKl2ybDW
yhdKg/V3yTwgBUtb3QO4m1khNQjQLuPFVxULGEA38Y5dXSONsYnt
-----END CERTIFICATE-----`)
var localhostKey = []byte(`-----BEGIN PRIVATE KEY-----
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
hBgHM6A0WJC9MO3aAKRBcp48y6DXNA==
-----END PRIVATE KEY-----`)
func cmd(c *textproto.Conn, expectedCode int, format string, args ...interface{}) error {
id, err := c.Cmd(format, args...)
if err != nil {
return err
}
c.StartResponse(id)
_, _, err = c.ReadResponse(expectedCode)
c.EndResponse(id)
return err
}
func runserver(t *testing.T, server *smtpd.Server) (addr string, closer func()) {
ln, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("Listen failed: %v", err)
}
go func() {
server.Serve(ln)
}()
done := make(chan bool)
go func() {
<-done
ln.Close()
}()
return ln.Addr().String(), func() {
done <- true
}
}
func runsslserver(t *testing.T, server *smtpd.Server) (addr string, closer func()) {
cert, err := tls.X509KeyPair(localhostCert, localhostKey)
if err != nil {
t.Fatalf("Cert load failed: %v", err)
}
server.TLSConfig = &tls.Config{
Certificates: []tls.Certificate{cert},
}
return runserver(t, server)
}
func TestSMTP(t *testing.T) {
addr, closer := runserver(t, &smtpd.Server{
ProtocolLogger: log.New(os.Stdout, "log: ", log.Lshortfile),
})
defer closer()
c, err := smtp.Dial(addr)
if err != nil {
t.Fatalf("Dial failed: %v", err)
}
if err := c.Hello("localhost"); err != nil {
t.Fatalf("HELO failed: %v", err)
}
if supported, _ := c.Extension("AUTH"); supported {
t.Fatal("AUTH supported before TLS")
}
if supported, _ := c.Extension("8BITMIME"); !supported {
t.Fatal("8BITMIME not supported")
}
if supported, _ := c.Extension("STARTTLS"); supported {
t.Fatal("STARTTLS supported")
}
if err := c.Mail("sender@example.org"); err != nil {
t.Fatalf("Mail failed: %v", err)
}
if err := c.Rcpt("recipient@example.net"); err != nil {
t.Fatalf("Rcpt failed: %v", err)
}
if err := c.Rcpt("recipient2@example.net"); err != nil {
t.Fatalf("Rcpt2 failed: %v", err)
}
wc, err := c.Data()
if err != nil {
t.Fatalf("Data failed: %v", err)
}
_, err = fmt.Fprintf(wc, "This is the email body")
if err != nil {
t.Fatalf("Data body failed: %v", err)
}
err = wc.Close()
if err != nil {
t.Fatalf("Data close failed: %v", err)
}
if err := c.Reset(); err != nil {
t.Fatalf("Reset failed: %v", err)
}
if err := c.Verify("foobar@example.net"); err == nil {
t.Fatal("Unexpected support for VRFY")
}
if err := cmd(c.Text, 250, "NOOP"); err != nil {
t.Fatalf("NOOP failed: %v", err)
}
if err := c.Quit(); err != nil {
t.Fatalf("Quit failed: %v", err)
}
}
func TestListenAndServe(t *testing.T) {
addr, closer := runserver(t, &smtpd.Server{})
closer()
server := &smtpd.Server{
ProtocolLogger: log.New(os.Stdout, "log: ", log.Lshortfile),
}
go func() {
server.ListenAndServe(addr)
}()
time.Sleep(100 * time.Millisecond)
c, err := smtp.Dial(addr)
if err != nil {
t.Fatalf("Dial failed: %v", err)
}
if err := c.Quit(); err != nil {
t.Fatalf("Quit failed: %v", err)
}
}
func TestSTARTTLS(t *testing.T) {
addr, closer := runsslserver(t, &smtpd.Server{
Authenticator: func(peer smtpd.Peer, username, password string) error { return nil },
ForceTLS: true,
ProtocolLogger: log.New(os.Stdout, "log: ", log.Lshortfile),
})
defer closer()
c, err := smtp.Dial(addr)
if err != nil {
t.Fatalf("Dial failed: %v", err)
}
if supported, _ := c.Extension("AUTH"); supported {
t.Fatal("AUTH supported before TLS")
}
if err := c.Mail("sender@example.org"); err == nil {
t.Fatal("Mail workded before TLS with ForceTLS")
}
if err := cmd(c.Text, 220, "STARTTLS"); err != nil {
t.Fatalf("STARTTLS failed: %v", err)
}
if err := cmd(c.Text, 250, "foobar"); err == nil {
t.Fatal("STARTTLS didn't fail with invalid handshake")
}
if err := c.StartTLS(&tls.Config{InsecureSkipVerify: true}); err != nil {
t.Fatalf("STARTTLS failed: %v", err)
}
if err := c.StartTLS(&tls.Config{InsecureSkipVerify: true}); err == nil {
t.Fatal("STARTTLS worked twice")
}
if supported, _ := c.Extension("AUTH"); !supported {
t.Fatal("AUTH not supported after TLS")
}
if _, mechs := c.Extension("AUTH"); !strings.Contains(mechs, "PLAIN") {
t.Fatal("PLAIN AUTH not supported after TLS")
}
if _, mechs := c.Extension("AUTH"); !strings.Contains(mechs, "LOGIN") {
t.Fatal("LOGIN AUTH not supported after TLS")
}
if err := c.Auth(smtp.PlainAuth("foo", "foo", "bar", "127.0.0.1")); err != nil {
t.Fatalf("Auth failed: %v", err)
}
if err := c.Mail("sender@example.org"); err != nil {
t.Fatalf("Mail failed: %v", err)
}
if err := c.Rcpt("recipient@example.net"); err != nil {
t.Fatalf("Rcpt failed: %v", err)
}
if err := c.Rcpt("recipient2@example.net"); err != nil {
t.Fatalf("Rcpt2 failed: %v", err)
}
wc, err := c.Data()
if err != nil {
t.Fatalf("Data failed: %v", err)
}
_, err = fmt.Fprintf(wc, "This is the email body")
if err != nil {
t.Fatalf("Data body failed: %v", err)
}
err = wc.Close()
if err != nil {
t.Fatalf("Data close failed: %v", err)
}
if err := c.Quit(); err != nil {
t.Fatalf("Quit failed: %v", err)
}
}
func TestAuthRejection(t *testing.T) {
addr, closer := runsslserver(t, &smtpd.Server{
Authenticator: func(peer smtpd.Peer, username, password string) error {
return smtpd.Error{Code: 550, Message: "Denied"}
},
ForceTLS: true,
ProtocolLogger: log.New(os.Stdout, "log: ", log.Lshortfile),
})
defer closer()
c, err := smtp.Dial(addr)
if err != nil {
t.Fatalf("Dial failed: %v", err)
}
if err := c.StartTLS(&tls.Config{InsecureSkipVerify: true}); err != nil {
t.Fatalf("STARTTLS failed: %v", err)
}
if err := c.Auth(smtp.PlainAuth("foo", "foo", "bar", "127.0.0.1")); err == nil {
t.Fatal("Auth worked despite rejection")
}
}
func TestAuthNotSupported(t *testing.T) {
addr, closer := runsslserver(t, &smtpd.Server{
ForceTLS: true,
ProtocolLogger: log.New(os.Stdout, "log: ", log.Lshortfile),
})
defer closer()
c, err := smtp.Dial(addr)
if err != nil {
t.Fatalf("Dial failed: %v", err)
}
if err := c.StartTLS(&tls.Config{InsecureSkipVerify: true}); err != nil {
t.Fatalf("STARTTLS failed: %v", err)
}
if err := c.Auth(smtp.PlainAuth("foo", "foo", "bar", "127.0.0.1")); err == nil {
t.Fatal("Auth worked despite no authenticator")
}
}
func TestAuthBypass(t *testing.T) {
addr, closer := runsslserver(t, &smtpd.Server{
Authenticator: func(peer smtpd.Peer, username, password string) error {
return smtpd.Error{Code: 550, Message: "Denied"}
},
ForceTLS: true,
ProtocolLogger: log.New(os.Stdout, "log: ", log.Lshortfile),
})
defer closer()
c, err := smtp.Dial(addr)
if err != nil {
t.Fatalf("Dial failed: %v", err)
}
if err := c.StartTLS(&tls.Config{InsecureSkipVerify: true}); err != nil {
t.Fatalf("STARTTLS failed: %v", err)
}
if err := c.Mail("sender@example.org"); err == nil {
t.Fatal("Unexpected MAIL success")
}
}
func TestConnectionCheck(t *testing.T) {
addr, closer := runserver(t, &smtpd.Server{
ConnectionChecker: func(peer smtpd.Peer) error {
return smtpd.Error{Code: 552, Message: "Denied"}
},
ProtocolLogger: log.New(os.Stdout, "log: ", log.Lshortfile),
})
defer closer()
if _, err := smtp.Dial(addr); err == nil {
t.Fatal("Dial succeeded despite ConnectionCheck")
}
}
func TestConnectionCheckSimpleError(t *testing.T) {
addr, closer := runserver(t, &smtpd.Server{
ConnectionChecker: func(peer smtpd.Peer) error {
return errors.New("Denied")
},
ProtocolLogger: log.New(os.Stdout, "log: ", log.Lshortfile),
})
defer closer()
if _, err := smtp.Dial(addr); err == nil {
t.Fatal("Dial succeeded despite ConnectionCheck")
}
}
func TestHELOCheck(t *testing.T) {
addr, closer := runserver(t, &smtpd.Server{
HeloChecker: func(peer smtpd.Peer, name string) error {
if name != "foobar.local" {
t.Fatal("Wrong HELO name")
}
return smtpd.Error{Code: 552, Message: "Denied"}
},
ProtocolLogger: log.New(os.Stdout, "log: ", log.Lshortfile),
})
defer closer()
c, err := smtp.Dial(addr)
if err != nil {
t.Fatalf("Dial failed: %v", err)
}
if err := c.Hello("foobar.local"); err == nil {
t.Fatal("Unexpected HELO success")
}
}
func TestSenderCheck(t *testing.T) {
addr, closer := runserver(t, &smtpd.Server{
SenderChecker: func(peer smtpd.Peer, addr string) error {
return smtpd.Error{Code: 552, Message: "Denied"}
},
ProtocolLogger: log.New(os.Stdout, "log: ", log.Lshortfile),
})
defer closer()
c, err := smtp.Dial(addr)
if err != nil {
t.Fatalf("Dial failed: %v", err)
}
if err := c.Mail("sender@example.org"); err == nil {
t.Fatal("Unexpected MAIL success")
}
}
func TestRecipientCheck(t *testing.T) {
addr, closer := runserver(t, &smtpd.Server{
RecipientChecker: func(peer smtpd.Peer, addr string) error {
return smtpd.Error{Code: 552, Message: "Denied"}
},
ProtocolLogger: log.New(os.Stdout, "log: ", log.Lshortfile),
})
defer closer()
c, err := smtp.Dial(addr)
if err != nil {
t.Fatalf("Dial failed: %v", err)
}
if err := c.Mail("sender@example.org"); err != nil {
t.Fatalf("Mail failed: %v", err)
}
if err := c.Rcpt("recipient@example.net"); err == nil {
t.Fatal("Unexpected RCPT success")
}
}
func TestMaxMessageSize(t *testing.T) {
addr, closer := runserver(t, &smtpd.Server{
MaxMessageSize: 5,
ProtocolLogger: log.New(os.Stdout, "log: ", log.Lshortfile),
})
defer closer()
c, err := smtp.Dial(addr)
if err != nil {
t.Fatalf("Dial failed: %v", err)
}
if err := c.Mail("sender@example.org"); err != nil {
t.Fatalf("MAIL failed: %v", err)
}
if err := c.Rcpt("recipient@example.net"); err != nil {
t.Fatalf("RCPT failed: %v", err)
}
wc, err := c.Data()
if err != nil {
t.Fatalf("Data failed: %v", err)
}
_, err = fmt.Fprintf(wc, "This is the email body")
if err != nil {
t.Fatalf("Data body failed: %v", err)
}
err = wc.Close()
if err == nil {
t.Fatal("Allowed message larger than 5 bytes to pass.")
}
if err := c.Quit(); err != nil {
t.Fatalf("QUIT failed: %v", err)
}
}
func TestHandler(t *testing.T) {
addr, closer := runserver(t, &smtpd.Server{
Handler: func(peer smtpd.Peer, env smtpd.Envelope) error {
if env.Sender != "sender@example.org" {
t.Fatalf("Unknown sender: %v", env.Sender)
}
if len(env.Recipients) != 1 {
t.Fatalf("Too many recipients: %d", len(env.Recipients))
}
if env.Recipients[0] != "recipient@example.net" {
t.Fatalf("Unknown recipient: %v", env.Recipients[0])
}
if string(env.Data) != "This is the email body\n" {
t.Fatalf("Wrong message body: %v", string(env.Data))
}
return nil
},
ProtocolLogger: log.New(os.Stdout, "log: ", log.Lshortfile),
})
defer closer()
c, err := smtp.Dial(addr)
if err != nil {
t.Fatalf("Dial failed: %v", err)
}
if err := c.Mail("sender@example.org"); err != nil {
t.Fatalf("MAIL failed: %v", err)
}
if err := c.Rcpt("recipient@example.net"); err != nil {
t.Fatalf("RCPT failed: %v", err)
}
wc, err := c.Data()
if err != nil {
t.Fatalf("Data failed: %v", err)
}
_, err = fmt.Fprintf(wc, "This is the email body")
if err != nil {
t.Fatalf("Data body failed: %v", err)
}
err = wc.Close()
if err != nil {
t.Fatalf("Data close failed: %v", err)
}
if err := c.Quit(); err != nil {
t.Fatalf("QUIT failed: %v", err)
}
}
func TestRejectHandler(t *testing.T) {
addr, closer := runserver(t, &smtpd.Server{
Handler: func(peer smtpd.Peer, env smtpd.Envelope) error {
return smtpd.Error{Code: 550, Message: "Rejected"}
},
ProtocolLogger: log.New(os.Stdout, "log: ", log.Lshortfile),
})
defer closer()
c, err := smtp.Dial(addr)
if err != nil {
t.Fatalf("Dial failed: %v", err)
}
if err := c.Mail("sender@example.org"); err != nil {
t.Fatalf("MAIL failed: %v", err)
}
if err := c.Rcpt("recipient@example.net"); err != nil {
t.Fatalf("RCPT failed: %v", err)
}
wc, err := c.Data()
if err != nil {
t.Fatalf("Data failed: %v", err)
}
_, err = fmt.Fprintf(wc, "This is the email body")
if err != nil {
t.Fatalf("Data body failed: %v", err)
}
err = wc.Close()
if err == nil {
t.Fatal("Unexpected accept of data")
}
if err := c.Quit(); err != nil {
t.Fatalf("QUIT failed: %v", err)
}
}
func TestMaxConnections(t *testing.T) {
addr, closer := runserver(t, &smtpd.Server{
MaxConnections: 1,
ProtocolLogger: log.New(os.Stdout, "log: ", log.Lshortfile),
})
defer closer()
c1, err := smtp.Dial(addr)
if err != nil {
t.Fatalf("Dial failed: %v", err)
}
_, err = smtp.Dial(addr)
if err == nil {
t.Fatal("Dial succeeded despite MaxConnections = 1")
}
c1.Close()
}
func TestNoMaxConnections(t *testing.T) {
addr, closer := runserver(t, &smtpd.Server{
MaxConnections: -1,
ProtocolLogger: log.New(os.Stdout, "log: ", log.Lshortfile),
})
defer closer()
c1, err := smtp.Dial(addr)
if err != nil {
t.Fatalf("Dial failed: %v", err)
}
c1.Close()
}
func TestMaxRecipients(t *testing.T) {
addr, closer := runserver(t, &smtpd.Server{
MaxRecipients: 1,
ProtocolLogger: log.New(os.Stdout, "log: ", log.Lshortfile),
})
defer closer()
c, err := smtp.Dial(addr)
if err != nil {
t.Fatalf("Dial failed: %v", err)
}
if err := c.Mail("sender@example.org"); err != nil {
t.Fatalf("MAIL failed: %v", err)
}
if err := c.Rcpt("recipient@example.net"); err != nil {
t.Fatalf("RCPT failed: %v", err)
}
if err := c.Rcpt("recipient@example.net"); err == nil {
t.Fatal("RCPT succeeded despite MaxRecipients = 1")
}
if err := c.Quit(); err != nil {
t.Fatalf("QUIT failed: %v", err)
}
}
func TestInvalidHelo(t *testing.T) {
addr, closer := runserver(t, &smtpd.Server{
ProtocolLogger: log.New(os.Stdout, "log: ", log.Lshortfile),
})
defer closer()
c, err := smtp.Dial(addr)
if err != nil {
t.Fatalf("Dial failed: %v", err)
}
if err := c.Hello(""); err == nil {
t.Fatal("Unexpected HELO success")
}
}
func TestInvalidSender(t *testing.T) {
addr, closer := runserver(t, &smtpd.Server{
ProtocolLogger: log.New(os.Stdout, "log: ", log.Lshortfile),
})
defer closer()
c, err := smtp.Dial(addr)
if err != nil {
t.Fatalf("Dial failed: %v", err)
}
if err := c.Mail("invalid@@example.org"); err == nil {
t.Fatal("Unexpected MAIL success")
}
}
func TestInvalidRecipient(t *testing.T) {
addr, closer := runserver(t, &smtpd.Server{
ProtocolLogger: log.New(os.Stdout, "log: ", log.Lshortfile),
})
defer closer()
c, err := smtp.Dial(addr)
if err != nil {
t.Fatalf("Dial failed: %v", err)
}
if err := c.Mail("sender@example.org"); err != nil {
t.Fatalf("Mail failed: %v", err)
}
if err := c.Rcpt("invalid@@example.org"); err == nil {
t.Fatal("Unexpected RCPT success")
}
}
func TestRCPTbeforeMAIL(t *testing.T) {
addr, closer := runserver(t, &smtpd.Server{
ProtocolLogger: log.New(os.Stdout, "log: ", log.Lshortfile),
})
defer closer()
c, err := smtp.Dial(addr)
if err != nil {
t.Fatalf("Dial failed: %v", err)
}
if err := c.Rcpt("recipient@example.net"); err == nil {
t.Fatal("Unexpected RCPT success")
}
}
func TestDATAbeforeRCPT(t *testing.T) {
addr, closer := runserver(t, &smtpd.Server{
ProtocolLogger: log.New(os.Stdout, "log: ", log.Lshortfile),
})
defer closer()
c, err := smtp.Dial(addr)
if err != nil {
t.Fatalf("Dial failed: %v", err)
}
if err := c.Mail("sender@example.org"); err != nil {
t.Fatalf("MAIL failed: %v", err)
}
if _, err := c.Data(); err == nil {
t.Fatal("Data accepted despite no recipients")
}
if err := c.Quit(); err != nil {
t.Fatalf("QUIT failed: %v", err)
}
}
func TestInterruptedDATA(t *testing.T) {
addr, closer := runserver(t, &smtpd.Server{
Handler: func(peer smtpd.Peer, env smtpd.Envelope) error {
t.Fatal("Accepted DATA despite disconnection")
return nil
},
ProtocolLogger: log.New(os.Stdout, "log: ", log.Lshortfile),
})
defer closer()
c, err := smtp.Dial(addr)
if err != nil {
t.Fatalf("Dial failed: %v", err)
}
if err := c.Mail("sender@example.org"); err != nil {
t.Fatalf("MAIL failed: %v", err)
}
if err := c.Rcpt("recipient@example.net"); err != nil {
t.Fatalf("RCPT failed: %v", err)
}
wc, err := c.Data()
if err != nil {
t.Fatalf("Data failed: %v", err)
}
_, err = fmt.Fprintf(wc, "This is the email body")
if err != nil {
t.Fatalf("Data body failed: %v", err)
}
c.Close()
}
func TestTimeoutClose(t *testing.T) {
addr, closer := runserver(t, &smtpd.Server{
MaxConnections: 1,
ReadTimeout: time.Second,
WriteTimeout: time.Second,
ProtocolLogger: log.New(os.Stdout, "log: ", log.Lshortfile),
})
defer closer()
c1, err := smtp.Dial(addr)
if err != nil {
t.Fatalf("Dial failed: %v", err)
}
time.Sleep(time.Second * 2)
c2, err := smtp.Dial(addr)
if err != nil {
t.Fatalf("Dial failed: %v", err)
}
if err := c1.Mail("sender@example.org"); err == nil {
t.Fatal("MAIL succeeded despite being timed out.")
}
if err := c2.Mail("sender@example.org"); err != nil {
t.Fatalf("MAIL failed: %v", err)
}
if err := c2.Quit(); err != nil {
t.Fatalf("Quit failed: %v", err)
}
c2.Close()
}
func TestTLSTimeout(t *testing.T) {
addr, closer := runsslserver(t, &smtpd.Server{
ReadTimeout: time.Second * 2,
WriteTimeout: time.Second * 2,
ProtocolLogger: log.New(os.Stdout, "log: ", log.Lshortfile),
})
defer closer()
c, err := smtp.Dial(addr)
if err != nil {
t.Fatalf("Dial failed: %v", err)
}
if err := c.StartTLS(&tls.Config{InsecureSkipVerify: true}); err != nil {
t.Fatalf("STARTTLS failed: %v", err)
}
time.Sleep(time.Second)
if err := c.Mail("sender@example.org"); err != nil {
t.Fatalf("MAIL failed: %v", err)
}
time.Sleep(time.Second)
if err := c.Rcpt("recipient@example.net"); err != nil {
t.Fatalf("RCPT failed: %v", err)
}
time.Sleep(time.Second)
if err := c.Rcpt("recipient@example.net"); err != nil {
t.Fatalf("RCPT failed: %v", err)
}
time.Sleep(time.Second)
if err := c.Quit(); err != nil {
t.Fatalf("Quit failed: %v", err)
}
}
func TestLongLine(t *testing.T) {
addr, closer := runserver(t, &smtpd.Server{
ProtocolLogger: log.New(os.Stdout, "log: ", log.Lshortfile),
})
defer closer()
c, err := smtp.Dial(addr)
if err != nil {
t.Fatalf("Dial failed: %v", err)
}
if err := c.Mail(fmt.Sprintf("%s@example.org", strings.Repeat("x", 65*1024))); err == nil {
t.Fatalf("MAIL failed: %v", err)
}
if err := c.Quit(); err != nil {
t.Fatalf("Quit failed: %v", err)
}
}
func TestXCLIENT(t *testing.T) {
addr, closer := runserver(t, &smtpd.Server{
EnableXCLIENT: true,
SenderChecker: func(peer smtpd.Peer, addr string) error {
if peer.HeloName != "new.example.net" {
t.Fatalf("Didn't override HELO name: %v", peer.HeloName)
}
if peer.Addr.String() != "42.42.42.42:4242" {
t.Fatalf("Didn't override IP/Port: %v", peer.Addr)
}
if peer.Username != "newusername" {
t.Fatalf("Didn't override username: %v", peer.Username)
}
if peer.Protocol != smtpd.SMTP {
t.Fatalf("Didn't override protocol: %v", peer.Protocol)
}
return nil
},
ProtocolLogger: log.New(os.Stdout, "log: ", log.Lshortfile),
})
defer closer()
c, err := smtp.Dial(addr)
if err != nil {
t.Fatalf("Dial failed: %v", err)
}
if supported, _ := c.Extension("XCLIENT"); !supported {
t.Fatal("XCLIENT not supported")
}
err = cmd(c.Text, 220, "XCLIENT NAME=ignored ADDR=42.42.42.42 PORT=4242 PROTO=SMTP HELO=new.example.net LOGIN=newusername")
if err != nil {
t.Fatalf("XCLIENT failed: %v", err)
}
if err := c.Mail("sender@example.org"); err != nil {
t.Fatalf("Mail failed: %v", err)
}
if err := c.Rcpt("recipient@example.net"); err != nil {
t.Fatalf("Rcpt failed: %v", err)
}
if err := c.Rcpt("recipient2@example.net"); err != nil {
t.Fatalf("Rcpt2 failed: %v", err)
}
wc, err := c.Data()
if err != nil {
t.Fatalf("Data failed: %v", err)
}
_, err = fmt.Fprintf(wc, "This is the email body")
if err != nil {
t.Fatalf("Data body failed: %v", err)
}
err = wc.Close()
if err != nil {
t.Fatalf("Data close failed: %v", err)
}
if err := c.Quit(); err != nil {
t.Fatalf("Quit failed: %v", err)
}
}
func TestEnvelopeReceived(t *testing.T) {
addr, closer := runsslserver(t, &smtpd.Server{
Hostname: "foobar.example.net",
Handler: func(peer smtpd.Peer, env smtpd.Envelope) error {
env.AddReceivedLine(peer)
if !bytes.HasPrefix(env.Data, []byte("Received: from localhost ([127.0.0.1]) by foobar.example.net with ESMTP;")) {
t.Fatal("Wrong received line.")
}
return nil
},
ForceTLS: true,
ProtocolLogger: log.New(os.Stdout, "log: ", log.Lshortfile),
})
defer closer()
c, err := smtp.Dial(addr)
if err != nil {
t.Fatalf("Dial failed: %v", err)
}
if err := c.StartTLS(&tls.Config{InsecureSkipVerify: true}); err != nil {
t.Fatalf("STARTTLS failed: %v", err)
}
if err := c.Mail("sender@example.org"); err != nil {
t.Fatalf("MAIL failed: %v", err)
}
if err := c.Rcpt("recipient@example.net"); err != nil {
t.Fatalf("RCPT failed: %v", err)
}
wc, err := c.Data()
if err != nil {
t.Fatalf("Data failed: %v", err)
}
_, err = fmt.Fprintf(wc, "This is the email body")
if err != nil {
t.Fatalf("Data body failed: %v", err)
}
err = wc.Close()
if err != nil {
t.Fatalf("Data close failed: %v", err)
}
if err := c.Quit(); err != nil {
t.Fatalf("QUIT failed: %v", err)
}
}
func TestHELO(t *testing.T) {
addr, closer := runserver(t, &smtpd.Server{
ProtocolLogger: log.New(os.Stdout, "log: ", log.Lshortfile),
})
defer closer()
c, err := smtp.Dial(addr)
if err != nil {
t.Fatalf("Dial failed: %v", err)
}
if err := cmd(c.Text, 502, "MAIL FROM:<test@example.org>"); err != nil {
t.Fatalf("MAIL before HELO didn't fail: %v", err)
}
if err := cmd(c.Text, 250, "HELO localhost"); err != nil {
t.Fatalf("HELO failed: %v", err)
}
if err := cmd(c.Text, 250, "MAIL FROM:<test@example.org>"); err != nil {
t.Fatalf("MAIL after HELO failed: %v", err)
}
if err := cmd(c.Text, 250, "HELO localhost"); err != nil {
t.Fatalf("double HELO failed: %v", err)
}
if err := c.Quit(); err != nil {
t.Fatalf("Quit failed: %v", err)
}
}
func TestLOGINAuth(t *testing.T) {
addr, closer := runsslserver(t, &smtpd.Server{
Authenticator: func(peer smtpd.Peer, username, password string) error { return nil },
ProtocolLogger: log.New(os.Stdout, "log: ", log.Lshortfile),
})
defer closer()
c, err := smtp.Dial(addr)
if err != nil {
t.Fatalf("Dial failed: %v", err)
}
if err := c.StartTLS(&tls.Config{InsecureSkipVerify: true}); err != nil {
t.Fatalf("STARTTLS failed: %v", err)
}
if err := cmd(c.Text, 334, "AUTH LOGIN"); err != nil {
t.Fatalf("AUTH didn't work: %v", err)
}
if err := cmd(c.Text, 502, "foo"); err != nil {
t.Fatalf("AUTH didn't fail: %v", err)
}
if err := cmd(c.Text, 334, "AUTH LOGIN"); err != nil {
t.Fatalf("AUTH didn't work: %v", err)
}
if err := cmd(c.Text, 334, "Zm9v"); err != nil {
t.Fatalf("AUTH didn't work: %v", err)
}
if err := cmd(c.Text, 502, "foo"); err != nil {
t.Fatalf("AUTH didn't fail: %v", err)
}
if err := cmd(c.Text, 334, "AUTH LOGIN"); err != nil {
t.Fatalf("AUTH didn't work: %v", err)
}
if err := cmd(c.Text, 334, "Zm9v"); err != nil {
t.Fatalf("AUTH didn't work: %v", err)
}
if err := cmd(c.Text, 235, "Zm9v"); err != nil {
t.Fatalf("AUTH didn't work: %v", err)
}
if err := c.Quit(); err != nil {
t.Fatalf("Quit failed: %v", err)
}
}
func TestNullSender(t *testing.T) {
addr, closer := runserver(t, &smtpd.Server{
ProtocolLogger: log.New(os.Stdout, "log: ", log.Lshortfile),
})
defer closer()
c, err := smtp.Dial(addr)
if err != nil {
t.Fatalf("Dial failed: %v", err)
}
if err := cmd(c.Text, 250, "HELO localhost"); err != nil {
t.Fatalf("HELO failed: %v", err)
}
if err := cmd(c.Text, 250, "MAIL FROM:<>"); err != nil {
t.Fatalf("MAIL with null sender failed: %v", err)
}
if err := c.Quit(); err != nil {
t.Fatalf("Quit failed: %v", err)
}
}
func TestNoBracketsSender(t *testing.T) {
addr, closer := runserver(t, &smtpd.Server{
ProtocolLogger: log.New(os.Stdout, "log: ", log.Lshortfile),
})
defer closer()
c, err := smtp.Dial(addr)
if err != nil {
t.Fatalf("Dial failed: %v", err)
}
if err := cmd(c.Text, 250, "HELO localhost"); err != nil {
t.Fatalf("HELO failed: %v", err)
}
if err := cmd(c.Text, 250, "MAIL FROM:test@example.org"); err != nil {
t.Fatalf("MAIL without brackets failed: %v", err)
}
if err := c.Quit(); err != nil {
t.Fatalf("Quit failed: %v", err)
}
}
func TestErrors(t *testing.T) {
cert, err := tls.X509KeyPair(localhostCert, localhostKey)
if err != nil {
t.Fatalf("Cert load failed: %v", err)
}
server := &smtpd.Server{
Authenticator: func(peer smtpd.Peer, username, password string) error { return nil },
ProtocolLogger: log.New(os.Stdout, "log: ", log.Lshortfile),
}
addr, closer := runserver(t, server)
defer closer()
c, err := smtp.Dial(addr)
if err != nil {
t.Fatalf("Dial failed: %v", err)
}
if err := cmd(c.Text, 502, "AUTH PLAIN foobar"); err != nil {
t.Fatalf("AUTH didn't fail: %v", err)
}
if err := c.Hello("localhost"); err != nil {
t.Fatalf("HELO failed: %v", err)
}
if err := cmd(c.Text, 502, "AUTH PLAIN foobar"); err != nil {
t.Fatalf("AUTH didn't fail: %v", err)
}
if err := c.Mail("sender@example.org"); err == nil {
t.Fatalf("MAIL didn't fail")
}
if err := cmd(c.Text, 502, "STARTTLS"); err != nil {
t.Fatalf("STARTTLS didn't fail: %v", err)
}
server.TLSConfig = &tls.Config{
Certificates: []tls.Certificate{cert},
}
if err := c.StartTLS(&tls.Config{InsecureSkipVerify: true}); err != nil {
t.Fatalf("STARTTLS failed: %v", err)
}
if err := cmd(c.Text, 502, "AUTH UNKNOWN"); err != nil {
t.Fatalf("AUTH didn't fail: %v", err)
}
if err := cmd(c.Text, 502, "AUTH PLAIN foobar"); err != nil {
t.Fatalf("AUTH didn't fail: %v", err)
}
if err := cmd(c.Text, 502, "AUTH PLAIN Zm9vAGJhcg=="); err != nil {
t.Fatalf("AUTH didn't fail: %v", err)
}
if err := cmd(c.Text, 334, "AUTH PLAIN"); err != nil {
t.Fatalf("AUTH didn't work: %v", err)
}
if err := cmd(c.Text, 235, "Zm9vAGJhcgBxdXV4"); err != nil {
t.Fatalf("AUTH didn't work: %v", err)
}
if err := c.Mail("sender@example.org"); err != nil {
t.Fatalf("MAIL failed: %v", err)
}
if err := c.Mail("sender@example.org"); err == nil {
t.Fatalf("Duplicate MAIL didn't fail")
}
if err := c.Quit(); err != nil {
t.Fatalf("Quit failed: %v", err)
}
}
func TestMailformedMAILFROM(t *testing.T) {
addr, closer := runserver(t, &smtpd.Server{
SenderChecker: func(peer smtpd.Peer, addr string) error {
if addr != "test@example.org" {
return smtpd.Error{Code: 502, Message: "Denied"}
}
return nil
},
ProtocolLogger: log.New(os.Stdout, "log: ", log.Lshortfile),
})
defer closer()
c, err := smtp.Dial(addr)
if err != nil {
t.Fatalf("Dial failed: %v", err)
}
if err := c.Hello("localhost"); err != nil {
t.Fatalf("HELO failed: %v", err)
}
if err := cmd(c.Text, 250, "MAIL FROM: <test@example.org>"); err != nil {
t.Fatalf("MAIL FROM failed with extra whitespace: %v", err)
}
if err := c.Quit(); err != nil {
t.Fatalf("Quit failed: %v", err)
}
}
func TestTLSListener(t *testing.T) {
cert, err := tls.X509KeyPair(localhostCert, localhostKey)
if err != nil {
t.Fatalf("Cert load failed: %v", err)
}
cfg := &tls.Config{
Certificates: []tls.Certificate{cert},
}
ln, err := tls.Listen("tcp", "127.0.0.1:0", cfg)
defer ln.Close()
addr := ln.Addr().String()
server := &smtpd.Server{
Authenticator: func(peer smtpd.Peer, username, password string) error {
if peer.TLS == nil {
t.Error("didn't correctly set connection state on TLS connection")
}
return nil
},
ProtocolLogger: log.New(os.Stdout, "log: ", log.Lshortfile),
}
go func() {
server.Serve(ln)
}()
conn, err := tls.Dial("tcp", addr, &tls.Config{InsecureSkipVerify: true})
if err != nil {
t.Fatalf("couldn't connect to tls socket: %v", err)
}
c, err := smtp.NewClient(conn, "localhost")
if err != nil {
t.Fatalf("couldn't create client: %v", err)
}
if err := c.Hello("localhost"); err != nil {
t.Fatalf("HELO failed: %v", err)
}
if err := cmd(c.Text, 334, "AUTH PLAIN"); err != nil {
t.Fatalf("AUTH didn't work: %v", err)
}
if err := cmd(c.Text, 235, "Zm9vAGJhcgBxdXV4"); err != nil {
t.Fatalf("AUTH didn't work: %v", err)
}
if err := c.Quit(); err != nil {
t.Fatalf("Quit failed: %v", err)
}
}
func TestShutdown(t *testing.T) {
fmt.Println("Starting test")
server := &smtpd.Server{
ProtocolLogger: log.New(os.Stdout, "log: ", log.Lshortfile),
}
ln, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("Listen failed: %v", err)
}
srvres := make(chan error)
go func() {
t.Log("Starting server")
srvres <- server.Serve(ln)
}()
// Connect a client
c, err := smtp.Dial(ln.Addr().String())
if err != nil {
t.Fatalf("Dial failed: %v", err)
}
if err := c.Hello("localhost"); err != nil {
t.Fatalf("HELO failed: %v", err)
}
// While the client connection is open, shut down the server (without
// waiting for it to finish)
err = server.Shutdown(false)
if err != nil {
t.Fatalf("Shutdown returned error: %v", err)
}
// Verify that Shutdown() worked by attempting to connect another client
_, err = smtp.Dial(ln.Addr().String())
if err == nil {
t.Fatalf("Dial did not fail as expected")
}
if _, typok := err.(*net.OpError); !typok {
t.Fatalf("Dial did not return net.OpError as expected: %v (%T)", err, err)
}
// Wait for shutdown to complete
shutres := make(chan error)
go func() {
t.Log("Waiting for server shutdown to finish")
shutres <- server.Wait()
}()
// Slight delay to ensure Shutdown() blocks
time.Sleep(250 * time.Millisecond)
// Wait() should not have returned yet due to open client conn
select {
case shuterr := <-shutres:
t.Fatalf("Wait() returned early w/ error: %v", shuterr)
default:
}
// Now close the client
t.Log("Closing client connection")
if err := c.Quit(); err != nil {
t.Fatalf("QUIT failed: %v", err)
}
c.Close()
// Wait for Wait() to return
t.Log("Waiting for Wait() to return")
select {
case shuterr := <-shutres:
if shuterr != nil {
t.Fatalf("Wait() returned error: %v", shuterr)
}
case <-time.After(15 * time.Second):
t.Fatalf("Timed out waiting for Wait() to return")
}
// Wait for Serve() to return
t.Log("Waiting for Serve() to return")
select {
case srverr := <-srvres:
if srverr != smtpd.ErrServerClosed {
t.Fatalf("Serve() returned error: %v", srverr)
}
case <-time.After(15 * time.Second):
t.Fatalf("Timed out waiting for Serve() to return")
}
}
func TestServeFailsIfShutdown(t *testing.T) {
server := &smtpd.Server{}
err := server.Shutdown(true)
if err != nil {
t.Fatalf("Shutdown() failed: %v", err)
}
err = server.Serve(nil)
if err != smtpd.ErrServerClosed {
t.Fatalf("Serve() did not return ErrServerClosed: %v", err)
}
}
func TestWaitFailsIfNotShutdown(t *testing.T) {
server := &smtpd.Server{}
err := server.Wait()
if err == nil {
t.Fatalf("Wait() did not fail as expected")
}
}