This commit is contained in:
parent
7612e1002b
commit
9e78499a65
@ -107,6 +107,27 @@ func (s *Session) Rcpt(to string, opts *smtp.RcptOptions) error {
|
||||
if err := s.checkAuthorization(); err != nil {
|
||||
return err
|
||||
}
|
||||
if !validation.IsMailboxAddress(to) {
|
||||
return &smtp.SMTPError{Code: 550, EnhancedCode: smtp.EnhancedCode{5, 1, 3}, Message: "Invalid recipient address"}
|
||||
}
|
||||
// 匿名连接仅接收受管域名的邮件,不能等到正文阶段再静默丢弃外域收件人。
|
||||
if s.authorization == nil {
|
||||
domains := config.CurrentMailDomains().Accepted
|
||||
if len(domains) == 0 {
|
||||
return &smtp.SMTPError{Code: 451, EnhancedCode: smtp.EnhancedCode{4, 3, 0}, Message: "Mail domains are unavailable"}
|
||||
}
|
||||
domain := to[strings.LastIndex(to, "@")+1:]
|
||||
local := false
|
||||
for _, accepted := range domains {
|
||||
if strings.EqualFold(domain, accepted) {
|
||||
local = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !local {
|
||||
return &smtp.SMTPError{Code: 550, EnhancedCode: smtp.EnhancedCode{5, 7, 1}, Message: "Relay access denied"}
|
||||
}
|
||||
}
|
||||
if validation.IsNotificationAddress(to, config.CurrentMailDomains().Accepted) {
|
||||
return &smtp.SMTPError{Code: 550, EnhancedCode: smtp.EnhancedCode{5, 1, 1}, Message: "Notification address does not accept mail"}
|
||||
}
|
||||
|
||||
@ -5,10 +5,44 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.pcux.cn/xuans/Smail/config"
|
||||
"git.pcux.cn/xuans/Smail/utils/context"
|
||||
"github.com/emersion/go-smtp"
|
||||
)
|
||||
|
||||
func TestAnonymousRcptRejectsForeignDomains(t *testing.T) {
|
||||
oldConfig := config.Instance
|
||||
t.Cleanup(func() { config.Instance = oldConfig })
|
||||
config.Instance = &config.Config{Domain: "example.com", Domains: []string{"example.com", "alias.example"}}
|
||||
for _, tt := range []struct {
|
||||
address string
|
||||
allowed bool
|
||||
}{
|
||||
{"alice@example.com", true},
|
||||
{"alice@EXAMPLE.COM", true},
|
||||
{"alice@alias.example", true},
|
||||
{"alice@external.example", false},
|
||||
{"alice@example.com.attacker.test", false},
|
||||
{"alice@sub.example.com", false},
|
||||
{"not-an-address", false},
|
||||
} {
|
||||
t.Run(tt.address, func(t *testing.T) {
|
||||
session := &Session{Ctx: &context.Context{}, To: []string{"existing@example.com"}}
|
||||
err := session.Rcpt(tt.address, nil)
|
||||
if tt.allowed {
|
||||
if err != nil || len(session.To) != 2 {
|
||||
t.Fatalf("受管域名收件失败:%v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
var denied *smtp.SMTPError
|
||||
if !errors.As(err, &denied) || denied.Code != 550 || len(session.To) != 1 {
|
||||
t.Fatalf("匿名外域收件未明确拒绝或污染收件列表:%v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSessionResetClearsTransactionStateOnly(t *testing.T) {
|
||||
ctx := &context.Context{UserID: 42, UserAccount: "smail", IsAdmin: true}
|
||||
session := &Session{
|
||||
|
||||
@ -287,10 +287,22 @@ func doSend(ctx *context.Context, fromDomain string, data []byte, to []*parsemai
|
||||
}
|
||||
err = deliverMXContext(deliveryContext, hosts, func(host, port string) error {
|
||||
address := net.JoinHostPort(host, port)
|
||||
var attemptErr error
|
||||
if port == "465" {
|
||||
return smtp.SendMailWithTLSContext(deliveryContext, host, address, nil, from, fromDomain, buildAddress(tos), data)
|
||||
attemptErr = smtp.SendMailWithTLSContext(deliveryContext, host, address, nil, from, fromDomain, buildAddress(tos), data)
|
||||
} else {
|
||||
attemptErr = smtp.SendMailContext(deliveryContext, host, address, nil, from, fromDomain, buildAddress(tos), data)
|
||||
}
|
||||
return smtp.SendMailContext(deliveryContext, host, address, nil, from, fromDomain, buildAddress(tos), data)
|
||||
if attemptErr != nil {
|
||||
// 分端口记录网络失败,不记录邮件内容、凭据及远端 SMTP 响应原文。
|
||||
fields := log.Fields{"action": "外域邮件投递尝试", "mxHost": host, "mxPort": port, "reason": logstore.MailFailureReason(attemptErr)}
|
||||
var networkErr *net.OpError
|
||||
if errors.As(attemptErr, &networkErr) {
|
||||
fields["networkError"] = networkErr.Error()
|
||||
}
|
||||
log.WithContext(ctx).WithFields(fields).Warn("外域邮件投递通道失败")
|
||||
}
|
||||
return attemptErr
|
||||
})
|
||||
if err != nil {
|
||||
recordFailure(err)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user