mirror of
https://github.com/wneessen/go-mail.git
synced 2024-11-15 02:12:55 +01:00
Add new SMTP test cases for various failure scenarios
This commit introduces multiple test cases to handle SMTP failure scenarios such as invalid host, newline in addresses, EHLO/HELO failures, and malformed data injections. Additionally, it includes improvements in error handling for these specific conditions.
This commit is contained in:
parent
87accd289e
commit
1bfec504ed
1 changed files with 246 additions and 330 deletions
|
@ -2295,6 +2295,44 @@ func TestClient_Mail(t *testing.T) {
|
||||||
t.Errorf("expected mail from command to be %q, but sent %q", expected, resp[5])
|
t.Errorf("expected mail from command to be %q, but sent %q", expected, resp[5])
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
t.Run("from address and server supports SMTPUTF8 with unicode address", func(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
PortAdder.Add(1)
|
||||||
|
serverPort := int(TestServerPortBase + PortAdder.Load())
|
||||||
|
featureSet := "250-SMTPUTF8\r\n250 STARTTLS"
|
||||||
|
echoBuffer := bytes.NewBuffer(nil)
|
||||||
|
go func() {
|
||||||
|
if err := simpleSMTPServer(ctx, t, &serverProps{
|
||||||
|
EchoBuffer: echoBuffer,
|
||||||
|
FeatureSet: featureSet,
|
||||||
|
ListenPort: serverPort,
|
||||||
|
},
|
||||||
|
); err != nil {
|
||||||
|
t.Errorf("failed to start test server: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
time.Sleep(time.Millisecond * 30)
|
||||||
|
|
||||||
|
client, err := Dial(fmt.Sprintf("%s:%d", TestServerAddr, serverPort))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to dial to test server: %s", err)
|
||||||
|
}
|
||||||
|
t.Cleanup(func() {
|
||||||
|
if err = client.Close(); err != nil {
|
||||||
|
t.Errorf("failed to close client: %s", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if err = client.Mail("valid-from+📧@domain.tld"); err != nil {
|
||||||
|
t.Errorf("failed to set mail from address: %s", err)
|
||||||
|
}
|
||||||
|
expected := "MAIL FROM:<valid-from+📧@domain.tld> SMTPUTF8"
|
||||||
|
resp := strings.Split(echoBuffer.String(), "\r\n")
|
||||||
|
if !strings.EqualFold(resp[5], expected) {
|
||||||
|
t.Errorf("expected mail from command to be %q, but sent %q", expected, resp[5])
|
||||||
|
}
|
||||||
|
})
|
||||||
t.Run("from address and server supports DSN", func(t *testing.T) {
|
t.Run("from address and server supports DSN", func(t *testing.T) {
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
@ -2325,8 +2363,8 @@ func TestClient_Mail(t *testing.T) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
client.dsnmrtype = "FULL"
|
client.dsnmrtype = "FULL"
|
||||||
if err = client.Mail("valid-from@domain.tld"); err == nil {
|
if err = client.Mail("valid-from@domain.tld"); err != nil {
|
||||||
t.Error("server should echo the command as error but didn't")
|
t.Errorf("failed to set mail from address: %s", err)
|
||||||
}
|
}
|
||||||
expected := "MAIL FROM:<valid-from@domain.tld> RET=FULL"
|
expected := "MAIL FROM:<valid-from@domain.tld> RET=FULL"
|
||||||
resp := strings.Split(echoBuffer.String(), "\r\n")
|
resp := strings.Split(echoBuffer.String(), "\r\n")
|
||||||
|
@ -2364,8 +2402,8 @@ func TestClient_Mail(t *testing.T) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
client.dsnmrtype = "FULL"
|
client.dsnmrtype = "FULL"
|
||||||
if err = client.Mail("valid-from@domain.tld"); err == nil {
|
if err = client.Mail("valid-from@domain.tld"); err != nil {
|
||||||
t.Error("server should echo the command as error but didn't")
|
t.Errorf("failed to set mail from address: %s", err)
|
||||||
}
|
}
|
||||||
expected := "MAIL FROM:<valid-from@domain.tld> BODY=8BITMIME SMTPUTF8 RET=FULL"
|
expected := "MAIL FROM:<valid-from@domain.tld> BODY=8BITMIME SMTPUTF8 RET=FULL"
|
||||||
resp := strings.Split(echoBuffer.String(), "\r\n")
|
resp := strings.Split(echoBuffer.String(), "\r\n")
|
||||||
|
@ -2553,6 +2591,153 @@ func TestClient_Data(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSendMail(t *testing.T) {
|
func TestSendMail(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
featureSet string
|
||||||
|
hostname string
|
||||||
|
tlsConfig *tls.Config
|
||||||
|
props *serverProps
|
||||||
|
fromAddr string
|
||||||
|
toAddr string
|
||||||
|
message []byte
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"fail on newline in MAIL FROM address",
|
||||||
|
"250-AUTH LOGIN\r\n250-DSN\r\n250 STARTTLS",
|
||||||
|
TestServerAddr,
|
||||||
|
getTLSConfig(t),
|
||||||
|
&serverProps{},
|
||||||
|
"valid-from@domain.tld\r\n",
|
||||||
|
"valid-to@domain.tld",
|
||||||
|
[]byte("test message"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fail on newline in RCPT TO address",
|
||||||
|
"250-AUTH LOGIN\r\n250-DSN\r\n250 STARTTLS",
|
||||||
|
TestServerAddr,
|
||||||
|
getTLSConfig(t),
|
||||||
|
&serverProps{},
|
||||||
|
"valid-from@domain.tld",
|
||||||
|
"valid-to@domain.tld\r\n",
|
||||||
|
[]byte("test message"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fail on invalid host address",
|
||||||
|
"250-AUTH LOGIN\r\n250-DSN\r\n250 STARTTLS",
|
||||||
|
"invalid.invalid-host@domain.tld",
|
||||||
|
getTLSConfig(t),
|
||||||
|
&serverProps{},
|
||||||
|
"valid-from@domain.tld",
|
||||||
|
"valid-to@domain.tld",
|
||||||
|
[]byte("test message"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fail on EHLO/HELO",
|
||||||
|
"250-AUTH LOGIN\r\n250-DSN\r\n250 STARTTLS",
|
||||||
|
TestServerAddr,
|
||||||
|
getTLSConfig(t),
|
||||||
|
&serverProps{FailOnEhlo: true, FailOnHelo: true},
|
||||||
|
"valid-from@domain.tld",
|
||||||
|
"valid-to@domain.tld",
|
||||||
|
[]byte("test message"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fail on STARTTLS",
|
||||||
|
"250-AUTH LOGIN\r\n250-DSN\r\n250 STARTTLS",
|
||||||
|
TestServerAddr,
|
||||||
|
&tls.Config{ServerName: "invalid.invalid-host@domain.tld"},
|
||||||
|
&serverProps{},
|
||||||
|
"valid-from@domain.tld",
|
||||||
|
"valid-to@domain.tld",
|
||||||
|
[]byte("test message"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fail on no server AUTH support",
|
||||||
|
"250-DSN\r\n250 STARTTLS",
|
||||||
|
TestServerAddr,
|
||||||
|
getTLSConfig(t),
|
||||||
|
&serverProps{},
|
||||||
|
"valid-from@domain.tld",
|
||||||
|
"valid-to@domain.tld",
|
||||||
|
[]byte("test message"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fail on AUTH",
|
||||||
|
"250-AUTH LOGIN\r\n250-DSN\r\n250 STARTTLS",
|
||||||
|
TestServerAddr,
|
||||||
|
getTLSConfig(t),
|
||||||
|
&serverProps{FailOnAuth: true},
|
||||||
|
"valid-from@domain.tld",
|
||||||
|
"valid-to@domain.tld",
|
||||||
|
[]byte("test message"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fail on MAIL FROM",
|
||||||
|
"250-AUTH LOGIN\r\n250-DSN\r\n250 STARTTLS",
|
||||||
|
TestServerAddr,
|
||||||
|
getTLSConfig(t),
|
||||||
|
&serverProps{FailOnMailFrom: true},
|
||||||
|
"valid-from@domain.tld",
|
||||||
|
"valid-to@domain.tld",
|
||||||
|
[]byte("test message"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fail on RCPT TO",
|
||||||
|
"250-AUTH LOGIN\r\n250-DSN\r\n250 STARTTLS",
|
||||||
|
TestServerAddr,
|
||||||
|
getTLSConfig(t),
|
||||||
|
&serverProps{FailOnRcptTo: true},
|
||||||
|
"valid-from@domain.tld",
|
||||||
|
"valid-to@domain.tld",
|
||||||
|
[]byte("test message"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fail on DATA (init phase)",
|
||||||
|
"250-AUTH LOGIN\r\n250-DSN\r\n250 STARTTLS",
|
||||||
|
TestServerAddr,
|
||||||
|
getTLSConfig(t),
|
||||||
|
&serverProps{FailOnDataInit: true},
|
||||||
|
"valid-from@domain.tld",
|
||||||
|
"valid-to@domain.tld",
|
||||||
|
[]byte("test message"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fail on DATA (closing phase)",
|
||||||
|
"250-AUTH LOGIN\r\n250-DSN\r\n250 STARTTLS",
|
||||||
|
TestServerAddr,
|
||||||
|
getTLSConfig(t),
|
||||||
|
&serverProps{FailOnDataClose: true},
|
||||||
|
"valid-from@domain.tld",
|
||||||
|
"valid-to@domain.tld",
|
||||||
|
[]byte("test message"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
PortAdder.Add(1)
|
||||||
|
tt.props.ListenPort = int(TestServerPortBase + PortAdder.Load())
|
||||||
|
tt.props.FeatureSet = tt.featureSet
|
||||||
|
go func() {
|
||||||
|
if err := simpleSMTPServer(ctx, t, tt.props); err != nil {
|
||||||
|
t.Errorf("failed to start test server: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
time.Sleep(time.Millisecond * 30)
|
||||||
|
addr := fmt.Sprintf("%s:%d", tt.hostname, tt.props.ListenPort)
|
||||||
|
testHookStartTLS = func(config *tls.Config) {
|
||||||
|
config.ServerName = tt.tlsConfig.ServerName
|
||||||
|
config.RootCAs = tt.tlsConfig.RootCAs
|
||||||
|
config.Certificates = tt.tlsConfig.Certificates
|
||||||
|
}
|
||||||
|
auth := LoginAuth("username", "password", TestServerAddr, false)
|
||||||
|
if err := SendMail(addr, auth, tt.fromAddr, []string{tt.toAddr}, tt.message); err == nil {
|
||||||
|
t.Error("expected SendMail to " + tt.name)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
t.Run("full SendMail transaction with TLS and auth", func(t *testing.T) {
|
t.Run("full SendMail transaction with TLS and auth", func(t *testing.T) {
|
||||||
want := []string{
|
want := []string{
|
||||||
"220 go-mail test server ready ESMTP",
|
"220 go-mail test server ready ESMTP",
|
||||||
|
@ -2622,37 +2807,41 @@ func TestSendMail(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
t.Run("SendMail newline in from should fail", func(t *testing.T) {
|
t.Run("full SendMail transaction with leading dots", func(t *testing.T) {
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
want := []string{
|
||||||
defer cancel()
|
"220 go-mail test server ready ESMTP",
|
||||||
PortAdder.Add(1)
|
"EHLO localhost",
|
||||||
serverPort := int(TestServerPortBase + PortAdder.Load())
|
"250-localhost.localdomain",
|
||||||
featureSet := "250-AUTH LOGIN\r\n250-DSN\r\n250 STARTTLS"
|
"250-AUTH LOGIN",
|
||||||
go func() {
|
"250-DSN",
|
||||||
if err := simpleSMTPServer(ctx, t, &serverProps{
|
"250 STARTTLS",
|
||||||
FeatureSet: featureSet,
|
"STARTTLS",
|
||||||
ListenPort: serverPort,
|
"220 Ready to start TLS",
|
||||||
},
|
"EHLO localhost",
|
||||||
); err != nil {
|
"250-localhost.localdomain",
|
||||||
t.Errorf("failed to start test server: %s", err)
|
"250-AUTH LOGIN",
|
||||||
return
|
"250-DSN",
|
||||||
|
"250 STARTTLS",
|
||||||
|
"AUTH LOGIN",
|
||||||
|
"235 2.7.0 Authentication successful",
|
||||||
|
"MAIL FROM:<valid-from@domain.tld>",
|
||||||
|
"250 2.0.0 OK",
|
||||||
|
"RCPT TO:<valid-to@domain.tld>",
|
||||||
|
"250 2.0.0 OK",
|
||||||
|
"DATA",
|
||||||
|
"354 End data with <CR><LF>.<CR><LF>",
|
||||||
|
"From: user@gmail.com",
|
||||||
|
"To: golang-nuts@googlegroups.com",
|
||||||
|
"Subject: Hooray for Go",
|
||||||
|
"",
|
||||||
|
"Line 1",
|
||||||
|
"..Leading dot line .",
|
||||||
|
"Goodbye.",
|
||||||
|
".",
|
||||||
|
"250 2.0.0 Ok: queued as 1234567890",
|
||||||
|
"QUIT",
|
||||||
|
"221 2.0.0 Bye",
|
||||||
}
|
}
|
||||||
}()
|
|
||||||
time.Sleep(time.Millisecond * 30)
|
|
||||||
addr := fmt.Sprintf("%s:%d", TestServerAddr, serverPort)
|
|
||||||
testHookStartTLS = func(config *tls.Config) {
|
|
||||||
testConfig := getTLSConfig(t)
|
|
||||||
config.ServerName = testConfig.ServerName
|
|
||||||
config.RootCAs = testConfig.RootCAs
|
|
||||||
config.Certificates = testConfig.Certificates
|
|
||||||
}
|
|
||||||
auth := LoginAuth("username", "password", TestServerAddr, false)
|
|
||||||
if err := SendMail(addr, auth, "valid-from@domain.tld\r\n", []string{"valid-to@domain.tld"},
|
|
||||||
[]byte("test message")); err == nil {
|
|
||||||
t.Error("expected SendMail to fail with newlines in from address")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
t.Run("SendMail newline in to should fail", func(t *testing.T) {
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
PortAdder.Add(1)
|
PortAdder.Add(1)
|
||||||
|
@ -2678,309 +2867,31 @@ func TestSendMail(t *testing.T) {
|
||||||
config.RootCAs = testConfig.RootCAs
|
config.RootCAs = testConfig.RootCAs
|
||||||
config.Certificates = testConfig.Certificates
|
config.Certificates = testConfig.Certificates
|
||||||
}
|
}
|
||||||
auth := LoginAuth("username", "password", TestServerAddr, false)
|
message := []byte(`From: user@gmail.com
|
||||||
if err := SendMail(addr, auth, "valid-from@domain.tld", []string{"valid-to@domain.tld\r\n"},
|
|
||||||
[]byte("test message")); err == nil {
|
|
||||||
t.Error("expected SendMail to fail with newlines in to address")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
t.Run("SendMail with invalid hostname should fail", func(t *testing.T) {
|
|
||||||
addr := "invalid.invalid-hostname.tld:1234"
|
|
||||||
testHookStartTLS = func(config *tls.Config) {
|
|
||||||
testConfig := getTLSConfig(t)
|
|
||||||
config.ServerName = testConfig.ServerName
|
|
||||||
config.RootCAs = testConfig.RootCAs
|
|
||||||
config.Certificates = testConfig.Certificates
|
|
||||||
}
|
|
||||||
auth := LoginAuth("username", "password", TestServerAddr, false)
|
|
||||||
if err := SendMail(addr, auth, "valid-from@domain.tld", []string{"valid-to@domain.tld"},
|
|
||||||
[]byte("test message")); err == nil {
|
|
||||||
t.Error("expected SendMail to fail with invalid server address")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
t.Run("SendMail should fail on EHLO/HELO", func(t *testing.T) {
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
defer cancel()
|
|
||||||
PortAdder.Add(1)
|
|
||||||
serverPort := int(TestServerPortBase + PortAdder.Load())
|
|
||||||
featureSet := "250-AUTH LOGIN\r\n250-DSN\r\n250 STARTTLS"
|
|
||||||
go func() {
|
|
||||||
if err := simpleSMTPServer(ctx, t, &serverProps{
|
|
||||||
FailOnEhlo: true,
|
|
||||||
FailOnHelo: true,
|
|
||||||
FeatureSet: featureSet,
|
|
||||||
ListenPort: serverPort,
|
|
||||||
},
|
|
||||||
); err != nil {
|
|
||||||
t.Errorf("failed to start test server: %s", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
time.Sleep(time.Millisecond * 30)
|
|
||||||
addr := fmt.Sprintf("%s:%d", TestServerAddr, serverPort)
|
|
||||||
testHookStartTLS = func(config *tls.Config) {
|
|
||||||
testConfig := getTLSConfig(t)
|
|
||||||
config.ServerName = testConfig.ServerName
|
|
||||||
config.RootCAs = testConfig.RootCAs
|
|
||||||
config.Certificates = testConfig.Certificates
|
|
||||||
}
|
|
||||||
auth := LoginAuth("username", "password", TestServerAddr, false)
|
|
||||||
if err := SendMail(addr, auth, "valid-from@domain.tld", []string{"valid-to@domain.tld"},
|
|
||||||
[]byte("test message")); err == nil {
|
|
||||||
t.Error("expected SendMail to fail on EHLO/HELO")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
t.Run("SendMail should fail on STARTTLS", func(t *testing.T) {
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
defer cancel()
|
|
||||||
PortAdder.Add(1)
|
|
||||||
serverPort := int(TestServerPortBase + PortAdder.Load())
|
|
||||||
featureSet := "250-AUTH LOGIN\r\n250-DSN\r\n250 STARTTLS"
|
|
||||||
go func() {
|
|
||||||
if err := simpleSMTPServer(ctx, t, &serverProps{
|
|
||||||
FeatureSet: featureSet,
|
|
||||||
ListenPort: serverPort,
|
|
||||||
},
|
|
||||||
); err != nil {
|
|
||||||
t.Errorf("failed to start test server: %s", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
testHookStartTLS = func(config *tls.Config) {
|
|
||||||
config.ServerName = "invalid.invalid-hostname.tld"
|
|
||||||
config.RootCAs = nil
|
|
||||||
config.Certificates = nil
|
|
||||||
}
|
|
||||||
time.Sleep(time.Millisecond * 30)
|
|
||||||
addr := fmt.Sprintf("%s:%d", TestServerAddr, serverPort)
|
|
||||||
auth := LoginAuth("username", "password", TestServerAddr, false)
|
|
||||||
if err := SendMail(addr, auth, "valid-from@domain.tld", []string{"valid-to@domain.tld"},
|
|
||||||
[]byte("test message")); err == nil {
|
|
||||||
t.Error("expected SendMail to fail on STARTTLS")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
t.Run("SendMail should fail on no auth support", func(t *testing.T) {
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
defer cancel()
|
|
||||||
PortAdder.Add(1)
|
|
||||||
serverPort := int(TestServerPortBase + PortAdder.Load())
|
|
||||||
featureSet := "250-DSN\r\n250 STARTTLS"
|
|
||||||
go func() {
|
|
||||||
if err := simpleSMTPServer(ctx, t, &serverProps{
|
|
||||||
FeatureSet: featureSet,
|
|
||||||
ListenPort: serverPort,
|
|
||||||
},
|
|
||||||
); err != nil {
|
|
||||||
t.Errorf("failed to start test server: %s", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
time.Sleep(time.Millisecond * 30)
|
|
||||||
addr := fmt.Sprintf("%s:%d", TestServerAddr, serverPort)
|
|
||||||
testHookStartTLS = func(config *tls.Config) {
|
|
||||||
testConfig := getTLSConfig(t)
|
|
||||||
config.ServerName = testConfig.ServerName
|
|
||||||
config.RootCAs = testConfig.RootCAs
|
|
||||||
config.Certificates = testConfig.Certificates
|
|
||||||
}
|
|
||||||
auth := LoginAuth("username", "password", TestServerAddr, false)
|
|
||||||
if err := SendMail(addr, auth, "valid-from@domain.tld", []string{"valid-to@domain.tld"},
|
|
||||||
[]byte("test message")); err == nil {
|
|
||||||
t.Error("expected SendMail to fail on no auth support")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
t.Run("SendMail should fail on auth", func(t *testing.T) {
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
defer cancel()
|
|
||||||
PortAdder.Add(1)
|
|
||||||
serverPort := int(TestServerPortBase + PortAdder.Load())
|
|
||||||
featureSet := "250-AUTH LOGIN\r\n250-DSN\r\n250 STARTTLS"
|
|
||||||
go func() {
|
|
||||||
if err := simpleSMTPServer(ctx, t, &serverProps{
|
|
||||||
FailOnAuth: true,
|
|
||||||
FeatureSet: featureSet,
|
|
||||||
ListenPort: serverPort,
|
|
||||||
},
|
|
||||||
); err != nil {
|
|
||||||
t.Errorf("failed to start test server: %s", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
time.Sleep(time.Millisecond * 30)
|
|
||||||
addr := fmt.Sprintf("%s:%d", TestServerAddr, serverPort)
|
|
||||||
testHookStartTLS = func(config *tls.Config) {
|
|
||||||
testConfig := getTLSConfig(t)
|
|
||||||
config.ServerName = testConfig.ServerName
|
|
||||||
config.RootCAs = testConfig.RootCAs
|
|
||||||
config.Certificates = testConfig.Certificates
|
|
||||||
}
|
|
||||||
auth := LoginAuth("username", "password", TestServerAddr, false)
|
|
||||||
if err := SendMail(addr, auth, "valid-from@domain.tld", []string{"valid-to@domain.tld"},
|
|
||||||
[]byte("test message")); err == nil {
|
|
||||||
t.Error("expected SendMail to fail on auth")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
func TestBasic(t *testing.T) {
|
|
||||||
server := strings.Join(strings.Split(basicServer, "\n"), "\r\n")
|
|
||||||
client := strings.Join(strings.Split(basicClient, "\n"), "\r\n")
|
|
||||||
|
|
||||||
var cmdbuf strings.Builder
|
|
||||||
bcmdbuf := bufio.NewWriter(&cmdbuf)
|
|
||||||
var fake faker
|
|
||||||
fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(server)), bcmdbuf)
|
|
||||||
c := &Client{Text: textproto.NewConn(fake), localName: "localhost"}
|
|
||||||
|
|
||||||
if err := c.helo(); err != nil {
|
|
||||||
t.Fatalf("HELO failed: %s", err)
|
|
||||||
}
|
|
||||||
if err := c.ehlo(); err == nil {
|
|
||||||
t.Fatalf("Expected first EHLO to fail")
|
|
||||||
}
|
|
||||||
if err := c.ehlo(); err != nil {
|
|
||||||
t.Fatalf("Second EHLO failed: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.didHello = true
|
|
||||||
if ok, args := c.Extension("aUtH"); !ok || args != "LOGIN PLAIN" {
|
|
||||||
t.Fatalf("Expected AUTH supported")
|
|
||||||
}
|
|
||||||
if ok, _ := c.Extension("DSN"); ok {
|
|
||||||
t.Fatalf("Shouldn't support DSN")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := c.Mail("user@gmail.com"); err == nil {
|
|
||||||
t.Fatalf("MAIL should require authentication")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := c.Verify("user1@gmail.com"); err == nil {
|
|
||||||
t.Fatalf("First VRFY: expected no verification")
|
|
||||||
}
|
|
||||||
if err := c.Verify("user2@gmail.com>\r\nDATA\r\nAnother injected message body\r\n.\r\nQUIT\r\n"); err == nil {
|
|
||||||
t.Fatalf("VRFY should have failed due to a message injection attempt")
|
|
||||||
}
|
|
||||||
if err := c.Verify("user2@gmail.com"); err != nil {
|
|
||||||
t.Fatalf("Second VRFY: expected verification, got %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// fake TLS so authentication won't complain
|
|
||||||
c.tls = true
|
|
||||||
c.serverName = "smtp.google.com"
|
|
||||||
if err := c.Auth(PlainAuth("", "user", "pass", "smtp.google.com", false)); err != nil {
|
|
||||||
t.Fatalf("AUTH failed: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := c.Rcpt("golang-nuts@googlegroups.com>\r\nDATA\r\nInjected message body\r\n.\r\nQUIT\r\n"); err == nil {
|
|
||||||
t.Fatalf("RCPT should have failed due to a message injection attempt")
|
|
||||||
}
|
|
||||||
if err := c.Mail("user@gmail.com>\r\nDATA\r\nAnother injected message body\r\n.\r\nQUIT\r\n"); err == nil {
|
|
||||||
t.Fatalf("MAIL should have failed due to a message injection attempt")
|
|
||||||
}
|
|
||||||
if err := c.Mail("user@gmail.com"); err != nil {
|
|
||||||
t.Fatalf("MAIL failed: %s", err)
|
|
||||||
}
|
|
||||||
if err := c.Rcpt("golang-nuts@googlegroups.com"); err != nil {
|
|
||||||
t.Fatalf("RCPT failed: %s", err)
|
|
||||||
}
|
|
||||||
msg := `From: user@gmail.com
|
|
||||||
To: golang-nuts@googlegroups.com
|
To: golang-nuts@googlegroups.com
|
||||||
Subject: Hooray for Go
|
Subject: Hooray for Go
|
||||||
|
|
||||||
Line 1
|
Line 1
|
||||||
.Leading dot line .
|
.Leading dot line .
|
||||||
Goodbye.`
|
Goodbye.`)
|
||||||
w, err := c.Data()
|
auth := LoginAuth("username", "password", TestServerAddr, false)
|
||||||
if err != nil {
|
if err := SendMail(addr, auth, "valid-from@domain.tld", []string{"valid-to@domain.tld"}, message); err != nil {
|
||||||
t.Fatalf("DATA failed: %s", err)
|
t.Fatalf("failed to send mail: %s", err)
|
||||||
}
|
}
|
||||||
if _, err := w.Write([]byte(msg)); err != nil {
|
resp := strings.Split(echoBuffer.String(), "\r\n")
|
||||||
t.Fatalf("Data write failed: %s", err)
|
if len(resp)-1 != len(want) {
|
||||||
|
t.Errorf("expected %d lines, but got %d", len(want), len(resp))
|
||||||
}
|
}
|
||||||
if err := w.Close(); err != nil {
|
for i := 0; i < len(want); i++ {
|
||||||
t.Fatalf("Bad data response: %s", err)
|
if !strings.EqualFold(resp[i], want[i]) {
|
||||||
|
t.Errorf("expected line %d to be %q, but got %q", i, resp[i], want[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.Quit(); err != nil {
|
|
||||||
t.Fatalf("QUIT failed: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := bcmdbuf.Flush(); err != nil {
|
|
||||||
t.Errorf("flush failed: %s", err)
|
|
||||||
}
|
|
||||||
actualcmds := cmdbuf.String()
|
|
||||||
if client != actualcmds {
|
|
||||||
t.Fatalf("Got:\n%s\nExpected:\n%s", actualcmds, client)
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
var basicServer = `250 mx.google.com at your service
|
/*
|
||||||
502 Unrecognized command.
|
|
||||||
250-mx.google.com at your service
|
|
||||||
250-SIZE 35651584
|
|
||||||
250-AUTH LOGIN PLAIN
|
|
||||||
250 8BITMIME
|
|
||||||
530 Authentication required
|
|
||||||
252 Send some mail, I'll try my best
|
|
||||||
250 User is valid
|
|
||||||
235 Accepted
|
|
||||||
250 Sender OK
|
|
||||||
250 Receiver OK
|
|
||||||
354 Go ahead
|
|
||||||
250 Data OK
|
|
||||||
221 OK
|
|
||||||
`
|
|
||||||
|
|
||||||
var basicClient = `HELO localhost
|
|
||||||
EHLO localhost
|
|
||||||
EHLO localhost
|
|
||||||
MAIL FROM:<user@gmail.com> BODY=8BITMIME
|
|
||||||
VRFY user1@gmail.com
|
|
||||||
VRFY user2@gmail.com
|
|
||||||
AUTH PLAIN AHVzZXIAcGFzcw==
|
|
||||||
MAIL FROM:<user@gmail.com> BODY=8BITMIME
|
|
||||||
RCPT TO:<golang-nuts@googlegroups.com>
|
|
||||||
DATA
|
|
||||||
From: user@gmail.com
|
|
||||||
To: golang-nuts@googlegroups.com
|
|
||||||
Subject: Hooray for Go
|
|
||||||
|
|
||||||
Line 1
|
|
||||||
..Leading dot line .
|
|
||||||
Goodbye.
|
|
||||||
.
|
|
||||||
QUIT
|
|
||||||
`
|
|
||||||
|
|
||||||
func TestHELOFailed(t *testing.T) {
|
|
||||||
serverLines := `502 EH?
|
|
||||||
502 EH?
|
|
||||||
221 OK
|
|
||||||
`
|
|
||||||
clientLines := `EHLO localhost
|
|
||||||
HELO localhost
|
|
||||||
QUIT
|
|
||||||
`
|
|
||||||
server := strings.Join(strings.Split(serverLines, "\n"), "\r\n")
|
|
||||||
client := strings.Join(strings.Split(clientLines, "\n"), "\r\n")
|
|
||||||
var cmdbuf strings.Builder
|
|
||||||
bcmdbuf := bufio.NewWriter(&cmdbuf)
|
|
||||||
var fake faker
|
|
||||||
fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(server)), bcmdbuf)
|
|
||||||
c := &Client{Text: textproto.NewConn(fake), localName: "localhost"}
|
|
||||||
if err := c.Hello("localhost"); err == nil {
|
|
||||||
t.Fatal("expected EHLO to fail")
|
|
||||||
}
|
|
||||||
if err := c.Quit(); err != nil {
|
|
||||||
t.Errorf("QUIT failed: %s", err)
|
|
||||||
}
|
|
||||||
_ = bcmdbuf.Flush()
|
|
||||||
actual := cmdbuf.String()
|
|
||||||
if client != actual {
|
|
||||||
t.Errorf("Got:\n%s\nWant:\n%s", actual, client)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestExtensions(t *testing.T) {
|
func TestExtensions(t *testing.T) {
|
||||||
fake := func(server string) (c *Client, bcmdbuf *bufio.Writer, cmdbuf *strings.Builder) {
|
fake := func(server string) (c *Client, bcmdbuf *bufio.Writer, cmdbuf *strings.Builder) {
|
||||||
|
@ -4351,6 +4262,7 @@ type serverProps struct {
|
||||||
FailOnNoop bool
|
FailOnNoop bool
|
||||||
FailOnQuit bool
|
FailOnQuit bool
|
||||||
FailOnReset bool
|
FailOnReset bool
|
||||||
|
FailOnRcptTo bool
|
||||||
FailOnSTARTTLS bool
|
FailOnSTARTTLS bool
|
||||||
FailTemp bool
|
FailTemp bool
|
||||||
FeatureSet string
|
FeatureSet string
|
||||||
|
@ -4502,12 +4414,16 @@ func handleTestServerConnection(connection net.Conn, t *testing.T, props *server
|
||||||
from = strings.ReplaceAll(from, "RET=FULL", "")
|
from = strings.ReplaceAll(from, "RET=FULL", "")
|
||||||
}
|
}
|
||||||
from = strings.TrimSpace(from)
|
from = strings.TrimSpace(from)
|
||||||
if !strings.EqualFold(from, "<valid-from@domain.tld>") {
|
if !strings.HasPrefix(from, "<valid-from") && !strings.HasSuffix(from, "@domain.tld>") {
|
||||||
writeLine(fmt.Sprintf("503 5.1.2 Invalid from: %s", from))
|
writeLine(fmt.Sprintf("503 5.1.2 Invalid from: %s", from))
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
writeOK()
|
writeOK()
|
||||||
case strings.HasPrefix(data, "RCPT TO:"):
|
case strings.HasPrefix(data, "RCPT TO:"):
|
||||||
|
if props.FailOnRcptTo {
|
||||||
|
writeLine("500 5.5.2 Error: fail on RCPT TO")
|
||||||
|
break
|
||||||
|
}
|
||||||
to := strings.TrimPrefix(data, "RCPT TO:")
|
to := strings.TrimPrefix(data, "RCPT TO:")
|
||||||
if props.SupportDSN {
|
if props.SupportDSN {
|
||||||
to = strings.ReplaceAll(to, "NOTIFY=FAILURE,SUCCESS", "")
|
to = strings.ReplaceAll(to, "NOTIFY=FAILURE,SUCCESS", "")
|
||||||
|
|
Loading…
Reference in a new issue