1
0
mirror of https://github.com/jhillyerd/inbucket.git synced 2025-12-17 09:37:02 +00:00

add reject from origin domain feature (#375)

Add a new feature to be able to reject email *from* specific domains.

Co-authored-by: Cyril DUPONT <cyd@9bis.com>
This commit is contained in:
Cyd
2023-08-26 20:05:20 +02:00
committed by GitHub
parent 7c13a98ad2
commit 06ec140e72
9 changed files with 127 additions and 38 deletions

View File

@@ -106,7 +106,7 @@ type Session struct {
sendError error // Last network send error.
state State // Session state machine.
reader *bufio.Reader // Buffered reading for TCP conn.
from string // Sender from MAIL command.
from *policy.Origin // Sender from MAIL command.
recipients []*policy.Recipient // Recipients from RCPT commands.
logger zerolog.Logger // Session specific logger.
debug bool // Print network traffic to stdout.
@@ -384,7 +384,10 @@ func (s *Session) readyHandler(cmd string, arg string) {
return
}
from := m[1]
s.logger.Debug().Msgf("Mail sender is %v", from)
localpart, domain, err := policy.ParseEmailAddress(from)
s.logger.Debug().Msgf("Origin domain is %v", domain)
if from != "" && err != nil {
s.send("501 Bad sender address syntax")
s.logger.Warn().Msgf("Bad address as MAIL arg: %q, %s", from, err)
@@ -424,7 +427,19 @@ func (s *Session) readyHandler(cmd string, arg string) {
if extResult == nil || *extResult {
// Permitted by extension, or none had an opinion.
s.from = from
origin, err := s.addrPolicy.ParseOrigin(from)
if err != nil {
s.send("501 Bad origin address syntax")
s.logger.Warn().Str("from", from).Err(err).Msg("Bad address as MAIL arg")
return
}
s.from = origin
if !s.from.ShouldAccept() {
s.send("501 Unauthorized domain")
s.logger.Warn().Msgf("Bad domain sender %s", domain)
return
}
s.logger.Info().Msgf("Mail from: %v", from)
s.send(fmt.Sprintf("250 Roger, accepting mail from <%v>", from))
s.enterState(MAIL)
@@ -601,6 +616,7 @@ func (s *Session) readLine() (line string, err error) {
func (s *Session) parseCmd(line string) (cmd string, arg string, ok bool) {
line = strings.TrimRight(line, "\r\n")
s.logger.Debug().Msgf("Line received: %v", line)
// Find length of command or entire line.
hasArg := true
@@ -649,7 +665,7 @@ func (s *Session) parseArgs(arg string) (args map[string]string, ok bool) {
func (s *Session) reset() {
s.enterState(READY)
s.from = ""
s.from = nil
s.recipients = nil
}

View File

@@ -92,7 +92,7 @@ func TestEmptyEnvelope(t *testing.T) {
// Test out some empty envelope without blanks
script := []scriptStep{
{"HELO localhost", 250},
{"MAIL FROM:<>", 250},
{"MAIL FROM:<>", 501},
}
if err := playSession(t, server, script); err != nil {
t.Error(err)
@@ -101,7 +101,7 @@ func TestEmptyEnvelope(t *testing.T) {
// Test out some empty envelope with blanks
script = []scriptStep{
{"HELO localhost", 250},
{"MAIL FROM: <>", 250},
{"MAIL FROM: <>", 501},
}
if err := playSession(t, server, script); err != nil {
t.Error(err)
@@ -198,6 +198,31 @@ func TestReadyStateValidCommands(t *testing.T) {
}
}
// Test invalid domains in READY state.
func TestReadyStateRejectedDomains(t *testing.T) {
ds := test.NewStore()
server := setupSMTPServer(ds, extension.NewHost())
tests := []scriptStep{
{"MAIL FROM: <john@validdomain.com>", 250},
{"MAIL FROM: <john@invalidomain.com>", 501},
}
for _, tc := range tests {
t.Run(tc.send, func(t *testing.T) {
defer server.Drain()
script := []scriptStep{
{"HELO localhost", 250},
tc,
{"QUIT", 221}}
if err := playSession(t, server, script); err != nil {
t.Error(err)
}
})
}
}
// Test invalid commands in READY state.
func TestReadyStateInvalidCommands(t *testing.T) {
ds := test.NewStore()
@@ -557,6 +582,7 @@ func setupSMTPServer(ds storage.Store, extHost *extension.Host) *Server {
MaxMessageBytes: 5000,
DefaultAccept: true,
RejectDomains: []string{"deny.com"},
RejectOriginDomains: []string{"invalidomain.com"},
Timeout: 5,
},
}