diff --git a/cmd/root.go b/cmd/root.go index 27a1370..082ca57 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -126,7 +126,7 @@ func init() { rootCmd.Flags().BoolVar(&config.SMTPStrictRFCHeaders, "smtp-strict-rfc-headers", config.SMTPStrictRFCHeaders, "Return SMTP error if message headers contain ") rootCmd.Flags().IntVar(&config.SMTPMaxRecipients, "smtp-max-recipients", config.SMTPMaxRecipients, "Maximum SMTP recipients allowed") rootCmd.Flags().StringVar(&config.SMTPAllowedRecipients, "smtp-allowed-recipients", config.SMTPAllowedRecipients, "Only allow SMTP recipients matching a regular expression (default allow all)") - rootCmd.Flags().BoolVar(&config.SMTPSilentlyDropRejectedRecipients, "smtp-silently-drop-rejected-recipients", config.SMTPSilentlyDropRejectedRecipients, "Accept emails to rejected recipients with 2xx response but silently drop them") + rootCmd.Flags().BoolVar(&config.SMTPIgnoreRejectedRecipients, "smtp-ignore-rejected-recipients", config.SMTPIgnoreRejectedRecipients, "Ignore rejected SMTP recipients with 2xx response") rootCmd.Flags().BoolVar(&smtpd.DisableReverseDNS, "smtp-disable-rdns", smtpd.DisableReverseDNS, "Disable SMTP reverse DNS lookups") // SMTP relay @@ -302,8 +302,8 @@ func initConfigFromEnv() { if len(os.Getenv("MP_SMTP_ALLOWED_RECIPIENTS")) > 0 { config.SMTPAllowedRecipients = os.Getenv("MP_SMTP_ALLOWED_RECIPIENTS") } - if getEnabledFromEnv("MP_SMTP_SILENTLY_DROP_REJECTED_RECIPIENTS") { - config.SMTPSilentlyDropRejectedRecipients = true + if getEnabledFromEnv("MP_SMTP_IGNORE_REJECTED_RECIPIENTS") { + config.SMTPIgnoreRejectedRecipients = true } if getEnabledFromEnv("MP_SMTP_DISABLE_RDNS") { smtpd.DisableReverseDNS = true diff --git a/config/config.go b/config/config.go index fcba310..4a6a91b 100644 --- a/config/config.go +++ b/config/config.go @@ -181,8 +181,8 @@ var ( // SMTPAllowedRecipientsRegexp is the compiled version of SMTPAllowedRecipients SMTPAllowedRecipientsRegexp *regexp.Regexp - // SMTPSilentlyDropRejectedRecipients if true, will accept emails to rejected recipients with 2xx response but silently drop them - SMTPSilentlyDropRejectedRecipients bool + // SMTPIgnoreRejectedRecipients if true, will accept emails to rejected recipients with 2xx response but silently drop them + SMTPIgnoreRejectedRecipients bool // POP3Listen address - if set then Mailpit will start the POP3 server and listen on this address POP3Listen = "[::]:1110" @@ -584,6 +584,14 @@ func VerifyConfig() error { logger.Log().Infof("[smtp] only allowing recipients matching regexp: %s", SMTPAllowedRecipients) } + if SMTPIgnoreRejectedRecipients { + if SMTPAllowedRecipientsRegexp == nil { + logger.Log().Warn("[smtp] ignoring rejected recipients has no effect without setting smtp-allowed-recipients") + } else { + logger.Log().Info("[smtp] ignoring rejected recipients") + } + } + if err := parseRelayConfig(SMTPRelayConfigFile); err != nil { return err } diff --git a/internal/smtpd/main.go b/internal/smtpd/main.go index f093e8b..cec013c 100644 --- a/internal/smtpd/main.go +++ b/internal/smtpd/main.go @@ -194,16 +194,16 @@ func listenAndServe(addr string, handler MsgIDHandler, authHandler AuthHandler) Debug = true // to enable Mailpit logging srv := &Server{ - Addr: addr, - MsgIDHandler: handler, - HandlerRcpt: handlerRcpt, - AppName: "Mailpit", - Hostname: "", - AuthHandler: nil, - AuthRequired: false, - MaxRecipients: config.SMTPMaxRecipients, - SilentlyDropRejectedRecipients: config.SMTPSilentlyDropRejectedRecipients, - DisableReverseDNS: DisableReverseDNS, + Addr: addr, + MsgIDHandler: handler, + HandlerRcpt: handlerRcpt, + AppName: "Mailpit", + Hostname: "", + AuthHandler: nil, + AuthRequired: false, + MaxRecipients: config.SMTPMaxRecipients, + IgnoreRejectedRecipients: config.SMTPIgnoreRejectedRecipients, + DisableReverseDNS: DisableReverseDNS, LogRead: func(remoteIP, verb, line string) { logger.Log().Debugf("[smtpd] %s (%s) %s", verbLogTranslator(verb), remoteIP, line) }, diff --git a/internal/smtpd/smtpd.go b/internal/smtpd/smtpd.go index 4ed7223..da1b816 100644 --- a/internal/smtpd/smtpd.go +++ b/internal/smtpd/smtpd.go @@ -92,27 +92,27 @@ type LogFunc func(remoteIP, verb, line string) // Server is an SMTP server. type Server struct { - Addr string // TCP address to listen on, defaults to ":25" (all addresses, port 25) if empty - AppName string - AuthHandler AuthHandler - AuthMechs map[string]bool // Override list of allowed authentication mechanisms. Currently supported: LOGIN, PLAIN, CRAM-MD5. Enabling LOGIN and PLAIN will reduce RFC 4954 compliance. - AuthRequired bool // Require authentication for every command except AUTH, EHLO, HELO, NOOP, RSET or QUIT as per RFC 4954. Ignored if AuthHandler is not configured. - DisableReverseDNS bool // Disable reverse DNS lookups, enforces "unknown" hostname - Handler Handler - HandlerRcpt HandlerRcpt - Hostname string - LogRead LogFunc - LogWrite LogFunc - MaxSize int // Maximum message size allowed, in bytes - MaxRecipients int // Maximum number of recipients, defaults to 100. - MsgIDHandler MsgIDHandler - SilentlyDropRejectedRecipients bool // Accept emails to rejected recipients with 2xx response but silently drop them - Timeout time.Duration - TLSConfig *tls.Config - TLSListener bool // Listen for incoming TLS connections only (not recommended as it may reduce compatibility). Ignored if TLS is not configured. - TLSRequired bool // Require TLS for every command except NOOP, EHLO, STARTTLS, or QUIT as per RFC 3207. Ignored if TLS is not configured. - Protocol string // Default tcp, supports unix - SocketPerm fs.FileMode // if using Unix socket, socket permissions + Addr string // TCP address to listen on, defaults to ":25" (all addresses, port 25) if empty + AppName string + AuthHandler AuthHandler + AuthMechs map[string]bool // Override list of allowed authentication mechanisms. Currently supported: LOGIN, PLAIN, CRAM-MD5. Enabling LOGIN and PLAIN will reduce RFC 4954 compliance. + AuthRequired bool // Require authentication for every command except AUTH, EHLO, HELO, NOOP, RSET or QUIT as per RFC 4954. Ignored if AuthHandler is not configured. + DisableReverseDNS bool // Disable reverse DNS lookups, enforces "unknown" hostname + Handler Handler + HandlerRcpt HandlerRcpt + Hostname string + LogRead LogFunc + LogWrite LogFunc + MaxSize int // Maximum message size allowed, in bytes + MaxRecipients int // Maximum number of recipients, defaults to 100. + MsgIDHandler MsgIDHandler + IgnoreRejectedRecipients bool // Accept emails to rejected recipients with 2xx response but silently drop them + Timeout time.Duration + TLSConfig *tls.Config + TLSListener bool // Listen for incoming TLS connections only (not recommended as it may reduce compatibility). Ignored if TLS is not configured. + TLSRequired bool // Require TLS for every command except NOOP, EHLO, STARTTLS, or QUIT as per RFC 3207. Ignored if TLS is not configured. + Protocol string // Default tcp, supports unix + SocketPerm fs.FileMode // if using Unix socket, socket permissions inShutdown int32 // server was closed or shutdown openSessions int32 // count of open sessions @@ -497,7 +497,7 @@ loop: if accept { to = append(to, match[1]) s.writef("250 2.1.5 Ok") - } else if s.srv.SilentlyDropRejectedRecipients { + } else if s.srv.IgnoreRejectedRecipients { hasRejectedRecipients = true s.writef("250 2.1.5 Ok") } else { diff --git a/internal/smtpd/smtpd_test.go b/internal/smtpd/smtpd_test.go index 37a9e84..63b5219 100644 --- a/internal/smtpd/smtpd_test.go +++ b/internal/smtpd/smtpd_test.go @@ -1622,20 +1622,20 @@ func (m *mockDropRejectedHandler) msgIDHandler(remoteAddr net.Addr, from string, return "test-message-id", nil } -// Test the SilentlyDropRejectedRecipients option -func TestSilentlyDropRejectedRecipients(t *testing.T) { +// Test the IgnoreRejectedRecipients option +func TestIgnoreRejectedRecipients(t *testing.T) { tests := []struct { - name string - silentlyDropRejectedRecipients bool - handlerRcpt func(net.Addr, string, string) bool - rcptCommands []struct{ addr, expectedCode string } - expectedHandlerCalls int - expectedHandlerRecipients []string - useMsgIDHandler bool + name string + IgnoreRejectedRecipients bool + handlerRcpt func(net.Addr, string, string) bool + rcptCommands []struct{ addr, expectedCode string } + expectedHandlerCalls int + expectedHandlerRecipients []string + useMsgIDHandler bool }{ { - name: "Disabled_DefaultBehavior", - silentlyDropRejectedRecipients: false, + name: "Disabled_DefaultBehavior", + IgnoreRejectedRecipients: false, handlerRcpt: func(remoteAddr net.Addr, from string, to string) bool { return !strings.HasSuffix(to, "@rejected.com") }, @@ -1647,8 +1647,8 @@ func TestSilentlyDropRejectedRecipients(t *testing.T) { expectedHandlerRecipients: []string{"valid@example.com"}, }, { - name: "Enabled_MixedRecipients", - silentlyDropRejectedRecipients: true, + name: "Enabled_MixedRecipients", + IgnoreRejectedRecipients: true, handlerRcpt: func(remoteAddr net.Addr, from string, to string) bool { return !strings.HasSuffix(to, "@rejected.com") }, @@ -1662,8 +1662,8 @@ func TestSilentlyDropRejectedRecipients(t *testing.T) { expectedHandlerRecipients: []string{"valid1@example.com", "valid2@example.com"}, }, { - name: "Enabled_AllRejected", - silentlyDropRejectedRecipients: true, + name: "Enabled_AllRejected", + IgnoreRejectedRecipients: true, handlerRcpt: func(remoteAddr net.Addr, from string, to string) bool { return false // Reject all }, @@ -1675,8 +1675,8 @@ func TestSilentlyDropRejectedRecipients(t *testing.T) { expectedHandlerRecipients: nil, }, { - name: "Enabled_OnlyValid", - silentlyDropRejectedRecipients: true, + name: "Enabled_OnlyValid", + IgnoreRejectedRecipients: true, handlerRcpt: func(remoteAddr net.Addr, from string, to string) bool { return strings.HasSuffix(to, "@valid.com") }, @@ -1689,8 +1689,8 @@ func TestSilentlyDropRejectedRecipients(t *testing.T) { expectedHandlerRecipients: []string{"user1@valid.com", "user2@valid.com", "user3@valid.com"}, }, { - name: "Enabled_WithMsgIDHandler", - silentlyDropRejectedRecipients: true, + name: "Enabled_WithMsgIDHandler", + IgnoreRejectedRecipients: true, handlerRcpt: func(remoteAddr net.Addr, from string, to string) bool { return !strings.HasSuffix(to, "@rejected.com") }, @@ -1709,11 +1709,11 @@ func TestSilentlyDropRejectedRecipients(t *testing.T) { mock := &mockDropRejectedHandler{} server := &Server{ - Hostname: "mail.example.com", - AppName: "TestMail", - MaxRecipients: 100, - HandlerRcpt: tt.handlerRcpt, - SilentlyDropRejectedRecipients: tt.silentlyDropRejectedRecipients, + Hostname: "mail.example.com", + AppName: "TestMail", + MaxRecipients: 100, + HandlerRcpt: tt.handlerRcpt, + IgnoreRejectedRecipients: tt.IgnoreRejectedRecipients, } if tt.useMsgIDHandler {