mirror of
https://github.com/axllent/mailpit.git
synced 2026-03-03 03:57:01 +00:00
Chore: Increase allowed SMTP email address length to 1024 chars & return clearer SMTP responses for failures (#620)
This goes against the RFC5321 recommendation, however enforcing the recommended limits is clearly causing issues with users, and it appears no investigated SMTP servers enforce the strict limits either.
This commit is contained in:
@@ -422,9 +422,13 @@ loop:
|
||||
break
|
||||
}
|
||||
|
||||
match := extractAndValidateAddress(mailFromRE, args)
|
||||
match, err := extractAndValidateAddress(mailFromRE, args)
|
||||
if match == nil {
|
||||
s.writef("501 5.5.4 Syntax error in parameters or arguments (invalid FROM parameter)")
|
||||
if err != nil {
|
||||
s.writef("%s", err.Error())
|
||||
} else {
|
||||
s.writef("501 5.5.4 Syntax error in parameters or arguments (invalid FROM parameter)")
|
||||
}
|
||||
} else {
|
||||
// Mailpit Chaos
|
||||
if fail, code := chaos.Config.Sender.Trigger(); fail {
|
||||
@@ -478,9 +482,13 @@ loop:
|
||||
break
|
||||
}
|
||||
|
||||
match := extractAndValidateAddress(rcptToRE, args)
|
||||
match, err := extractAndValidateAddress(rcptToRE, args)
|
||||
if match == nil {
|
||||
s.writef("501 5.5.4 Syntax error in parameters or arguments (invalid TO parameter)")
|
||||
if err != nil {
|
||||
s.writef("%s", err.Error())
|
||||
} else {
|
||||
s.writef("501 5.5.4 Syntax error in parameters or arguments (invalid TO parameter)")
|
||||
}
|
||||
} else {
|
||||
// Mailpit Chaos
|
||||
if fail, code := chaos.Config.Recipient.Trigger(); fail {
|
||||
@@ -1017,31 +1025,33 @@ func (s *session) handleAuthCramMD5() (bool, error) {
|
||||
}
|
||||
|
||||
// Extract and validate email address from a regex match.
|
||||
// This ensures that only RFC 5322 email addresses are accepted (if set).
|
||||
func extractAndValidateAddress(re *regexp.Regexp, args string) []string {
|
||||
// This ensures that only RFC 5322 compliant email addresses are accepted (if set).
|
||||
func extractAndValidateAddress(re *regexp.Regexp, args string) ([]string, error) {
|
||||
match := re.FindStringSubmatch(args)
|
||||
if match == nil || strings.Contains(match[1], " ") {
|
||||
return nil
|
||||
if match == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if strings.Contains(match[1], " ") {
|
||||
return nil, errors.New("553 5.1.3 The address is not a valid RFC 5321 address")
|
||||
}
|
||||
|
||||
// first argument will be the email address, validate it if not empty
|
||||
if match[1] != "" {
|
||||
a, err := mail.ParseAddress(match[1])
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
parts := strings.SplitN(a.Address, "@", 2)
|
||||
|
||||
if len(parts) != 2 {
|
||||
return nil
|
||||
return nil, errors.New("553 5.1.3 The address is not a valid RFC 5321 address")
|
||||
}
|
||||
|
||||
// https://datatracker.ietf.org/doc/html/rfc5321#section-4.5.3.1
|
||||
if len(parts[0]) > 64 || len(parts[1]) > 255 || len(a.Address) > 256 {
|
||||
return nil
|
||||
// RFC states that the local part of an email address SHOULD not exceed 64 characters
|
||||
// and the domain part SHOULD not exceed 255 characters, however as per https://github.com/axllent/mailpit/issues/620
|
||||
// it appears that investigated mail servers do not actually implement this limit, but rather enforce
|
||||
// a much larger limit (ie: 1024 characters).
|
||||
if len(a.Address) > 1024 {
|
||||
return nil, errors.New("500 The address is too long")
|
||||
}
|
||||
}
|
||||
|
||||
return match
|
||||
return match, nil
|
||||
}
|
||||
|
||||
@@ -106,15 +106,14 @@ func TestCmdEHLO(t *testing.T) {
|
||||
cmdCode(t, conn, "RCPT TO:<recipient@example.com>", "250")
|
||||
|
||||
// test invalid addresses & header injection
|
||||
cmdCode(t, conn, "RCPT TO: <recipientrecipientrecipientrecipientrecipientrecipientrecipientrecipientrecipientrecipientrecipientrecipient@exampleexampleexampleexampleexampleexampleexample.com>", "501") // too long
|
||||
cmdCode(t, conn, "RCPT TO: <recipientrecipientrecipientreciprecipientrecipientrecipientrecipt@exaample.com>", "501")
|
||||
cmdCode(t, conn, "RCPT TO: <recipient@examplexampleexampleexampleexampleexampleexampleexamplexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexamplexampleexampleexampleexampleexampleexampleexamplexampleexampleexampleexampleexampleexampleexample.com>", "501")
|
||||
cmdCode(t, conn, "RCPT TO: <r@examplexampleexampleexampleexampleexampleexampleexamplexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexamplexampleexampleexampleexampleexampleexampleexamplexampleexampleexampleexampleexampleexampleexample.com>", "250") // valid
|
||||
cmdCode(t, conn, "RCPT TO:<recipientexample.com>", "501")
|
||||
cmdCode(t, conn, "RCPT TO: <recipientexample.com>", "501")
|
||||
cmdCode(t, conn, "RCPT TO: <recipientrecipientrecipientreciprecipientrecipientrecipientreciptrecipientrecipientrecipientreciprecipientrecipientrecipientreciptrecipientrecipientrecipientreciprecipientrecipientrecipientreciptrecipientrecipientrecipientreciprecipientrecipientrecipientreciptrecipientrecipientrecipientreciprecipientrecipientrecipientreciptrecipientrecipientrecipientreciprecipientrecipientrecipientreciptrecipientrecipientrecipientreciprecipientrecipientrecipientreciptrecipientrecipientrecipientreciprecipientrecipientrecipientreciptrecipientrecipientrecipientreciprecipientrecipientrecipientreciptrecipientrecipientrecipientreciprecipientrecipientrecipientreciptrecipientrecipientrecipientreciprecipientrecipientrecipientreciptrecipientrecipientrecipientreciprecipientrecipientrecipientreciptrecipientrecipientrecipientreciprecipientrecipientrecipientreciptrecipientrecipientrecipientreciprecipientrecipientrecipientreciptrecipientrecipientrecipient@exampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexample.com>", "500") // too long
|
||||
cmdCode(t, conn, "RCPT TO:<recipientexample.com>", "553")
|
||||
cmdCode(t, conn, "RCPT TO: <recipient@test@example.com>", "553")
|
||||
cmdCode(t, conn, "RCPT TO: <recipient@@example.com>", "553")
|
||||
cmdCode(t, conn, "RCPT TO: <recipientexample.com>", "553")
|
||||
cmdCode(t, conn, "RCPT TO: <recipientexample.com>", "501")
|
||||
cmdCode(t, conn, "RCPT TO:<recipient\rexample.com>", "501")
|
||||
cmdCode(t, conn, "RCPT TO: <recipient\rexample.com>", "501")
|
||||
cmdCode(t, conn, "RCPT TO:<recipient\rexample.com>", "553")
|
||||
cmdCode(t, conn, "RCPT TO: <recipient\rexample.com>", "553")
|
||||
cmdCode(t, conn, "RCPT TO: <recipient\rexample.com>", "501")
|
||||
cmdCode(t, conn, "RCPT TO: <>", "501") // empty address not allowed here
|
||||
|
||||
@@ -145,7 +144,7 @@ func TestCmdMAIL(t *testing.T) {
|
||||
|
||||
// MAIL with no FROM arg should return 501 syntax error
|
||||
cmdCode(t, conn, "MAIL", "501")
|
||||
// MAIL with empty FROM arg should return 501 syntax error
|
||||
// // MAIL with empty FROM arg should return 501 syntax error
|
||||
cmdCode(t, conn, "MAIL FROM:", "501")
|
||||
cmdCode(t, conn, "MAIL FROM: ", "501")
|
||||
cmdCode(t, conn, "MAIL FROM: ", "501")
|
||||
@@ -160,19 +159,18 @@ func TestCmdMAIL(t *testing.T) {
|
||||
cmdCode(t, conn, "MAIL FROM: <sender@example.com>", "501")
|
||||
|
||||
// test invalid addresses & header injection
|
||||
cmdCode(t, conn, "MAIL FROM: <sendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersender@exampleexampleexampleexampleexampleexampleexample.com>", "501") // too long
|
||||
cmdCode(t, conn, "MAIL FROM: <sendersendersendersendersendersendersendersendersendersendersender@exaample.com>", "501")
|
||||
cmdCode(t, conn, "MAIL FROM: <sender@examplexampleexampleexampleexampleexampleexampleexamplexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexamplexampleexampleexampleexampleexampleexampleexamplexampleexampleexampleexampleexampleexampleexample.com>", "501")
|
||||
cmdCode(t, conn, "MAIL FROM: <s@examplexampleexampleexampleexampleexampleexampleexamplexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexamplexampleexampleexampleexampleexampleexampleexamplexampleexampleexampleexampleexampleexampleexample.com>", "250") // valid
|
||||
cmdCode(t, conn, "MAIL FROM:<sender\rexample.com>", "501")
|
||||
cmdCode(t, conn, "MAIL FROM: <sender\rexample.com>", "501")
|
||||
cmdCode(t, conn, "MAIL FROM: <sendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersender@exampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexample.com>", "500") // too long
|
||||
cmdCode(t, conn, "MAIL FROM:<sender\rexample.com>", "553")
|
||||
cmdCode(t, conn, "MAIL FROM: <sender\rexample.com>", "553")
|
||||
cmdCode(t, conn, "MAIL FROM: <sender\rexample.com>", "501")
|
||||
cmdCode(t, conn, "MAIL FROM:<senderexample.com>", "501")
|
||||
cmdCode(t, conn, "MAIL FROM: <senderexample.com>", "501")
|
||||
cmdCode(t, conn, "MAIL FROM:<senderexample.com>", "553")
|
||||
cmdCode(t, conn, "MAIL FROM: <sender@@example.com>", "553")
|
||||
cmdCode(t, conn, "MAIL FROM: <sender@test@example.com>", "553")
|
||||
cmdCode(t, conn, "MAIL FROM: <senderexample.com>", "553")
|
||||
cmdCode(t, conn, "MAIL FROM: <senderexample.com>", "501")
|
||||
cmdCode(t, conn, "MAIL FROM: < sender@example.com >", "501")
|
||||
cmdCode(t, conn, "MAIL FROM: < sender@example.com>", "501")
|
||||
cmdCode(t, conn, "MAIL FROM: <sender@example.com >", "501")
|
||||
cmdCode(t, conn, "MAIL FROM: < sender@example.com >", "553")
|
||||
cmdCode(t, conn, "MAIL FROM: < sender@example.com>", "553")
|
||||
cmdCode(t, conn, "MAIL FROM: <sender@example.com >", "553")
|
||||
|
||||
// MAIL with valid SIZE parameter should return 250 Ok
|
||||
cmdCode(t, conn, "MAIL FROM:<sender@example.com> SIZE=1000", "250")
|
||||
@@ -241,6 +239,7 @@ func TestCmdRCPT(t *testing.T) {
|
||||
cmdCode(t, conn, "RCPT TO:", "501")
|
||||
cmdCode(t, conn, "RCPT TO: ", "501")
|
||||
cmdCode(t, conn, "RCPT TO: ", "501")
|
||||
cmdCode(t, conn, "RCPT TO:<@route.example user@example.com>", "553")
|
||||
|
||||
// RCPT with valid TO arg should return 250 Ok
|
||||
cmdCode(t, conn, "RCPT TO:<recipient@example.com>", "250")
|
||||
|
||||
Reference in New Issue
Block a user