Skip to content

Commit

Permalink
Merge pull request #36 from motienko/master
Browse files Browse the repository at this point in the history
Added forwarding of errors from Handler to SMTP session
  • Loading branch information
mhale committed Sep 25, 2023
2 parents 438c8ed + 02b0ea5 commit 9bbe814
Showing 1 changed file with 47 additions and 1 deletion.
48 changes: 47 additions & 1 deletion smtpd.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ type Server struct {
openSessions int32 // count of open sessions
mu sync.Mutex
shutdownChan chan struct{} // let the sessions know we are shutting down

XClientAllowed []string // List of XCLIENT allowed IP addresses
}

// ConfigureTLS creates a TLS configuration from certificate and key files.
Expand Down Expand Up @@ -219,6 +221,10 @@ type session struct {
remoteIP string // Remote IP address
remoteHost string // Remote hostname according to reverse DNS lookup
remoteName string // Remote hostname as supplied with EHLO
xClient string // Information string as supplied with XCLIENT
xClientADDR string // Information string as supplied with XCLIENT ADDR
xClientNAME string // Information string as supplied with XCLIENT NAME
xClientTrust bool // Trust XCLIENT from current IP address
tls bool
authenticated bool
}
Expand All @@ -244,6 +250,11 @@ func (srv *Server) newSession(conn net.Conn) (s *session) {
// Set tls = true if TLS is already in use.
_, s.tls = s.conn.(*tls.Conn)

for _, checkIP := range srv.XClientAllowed {
if s.remoteIP == checkIP {
s.xClientTrust = true
}
}
return
}

Expand Down Expand Up @@ -475,7 +486,12 @@ loop:
if s.srv.Handler != nil {
err := s.srv.Handler(s.conn.RemoteAddr(), from, to, buffer.Bytes())
if err != nil {
s.writef("451 4.3.5 Unable to process mail")
checkErrFormat := regexp.MustCompile(`^([2-5][0-9]{2})[\s\-](.+)$`)
if checkErrFormat.MatchString(err.Error()) {
s.writef(err.Error())
} else {
s.writef("451 4.3.5 Unable to process mail")
}
break
}
}
Expand All @@ -501,6 +517,36 @@ loop:
buffer.Reset()
case "NOOP":
s.writef("250 2.0.0 Ok")
case "XCLIENT":
s.xClient = args
if s.xClientTrust {
xCArgs := strings.Split(args, " ")
for _, xCArg := range xCArgs {
xCParse := strings.Split(strings.TrimSpace(xCArg), "=")
if strings.ToUpper(xCParse[0]) == "ADDR" && (net.ParseIP(xCParse[1]) != nil) {
s.xClientADDR = xCParse[1]
}
if strings.ToUpper(xCParse[0]) == "NAME" && len(xCParse[1]) > 0 {
if xCParse[1] != "[UNAVAILABLE]" {
s.xClientNAME = xCParse[1]
}
}
}
if len(s.xClientADDR) > 7 {
s.remoteIP = s.xClientADDR
if len(s.xClientNAME) > 4 {
s.remoteHost = s.xClientNAME
} else {
names, err := net.LookupAddr(s.remoteIP)
if err == nil && len(names) > 0 {
s.remoteHost = names[0]
} else {
s.remoteHost = "unknown"
}
}
}
}
s.writef("250 2.0.0 Ok")
case "HELP", "VRFY", "EXPN":
// See RFC 5321 section 4.2.4 for usage of 500 & 502 response codes.
s.writef("502 5.5.1 Command not implemented")
Expand Down

0 comments on commit 9bbe814

Please sign in to comment.