This commit is contained in:
parent
dfd27a34b6
commit
d4c3994102
@ -127,6 +127,40 @@ type MailPorts struct {
|
||||
POP3 int
|
||||
}
|
||||
|
||||
type MailDomains struct {
|
||||
Primary string
|
||||
Accepted []string
|
||||
}
|
||||
|
||||
func CurrentMailDomains() MailDomains {
|
||||
manager := CurrentManager()
|
||||
if manager != nil {
|
||||
runtime := manager.Snapshot()
|
||||
if runtime.Generation != 0 {
|
||||
return MailDomains{
|
||||
Primary: runtime.Root.Domains.Primary,
|
||||
Accepted: append([]string(nil), runtime.Root.Domains.Accepted...),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cfg := CurrentConfig()
|
||||
if cfg == nil {
|
||||
return MailDomains{}
|
||||
}
|
||||
domains := MailDomains{
|
||||
Primary: cfg.Domain,
|
||||
Accepted: append([]string(nil), cfg.Domains...),
|
||||
}
|
||||
if domains.Primary == "" && len(domains.Accepted) > 0 {
|
||||
domains.Primary = domains.Accepted[0]
|
||||
}
|
||||
if len(domains.Accepted) == 0 && domains.Primary != "" {
|
||||
domains.Accepted = []string{domains.Primary}
|
||||
}
|
||||
return domains
|
||||
}
|
||||
|
||||
type SMTPRuntimeSettings struct {
|
||||
Relay SMTPConfig
|
||||
Password string
|
||||
|
||||
@ -129,6 +129,47 @@ func TestCurrentMailPortsUsesDefaultsForUnavailableSnapshot(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCurrentMailDomainsReadsPartitionSnapshot(t *testing.T) {
|
||||
previousManager := CurrentManager()
|
||||
defer SetRuntimeManager(previousManager)
|
||||
|
||||
runtime := validRuntimeConfig()
|
||||
runtime.Root.Domains = DomainConfig{
|
||||
Primary: "partition.example.com",
|
||||
Accepted: []string{"partition.example.com", "alias.example.com"},
|
||||
}
|
||||
SetRuntimeManager(&ConfigManager{snapshots: runtimeAccessSnapshotStore{runtime: runtime}})
|
||||
|
||||
got := CurrentMailDomains()
|
||||
if got.Primary != "partition.example.com" || len(got.Accepted) != 2 || got.Accepted[1] != "alias.example.com" {
|
||||
t.Fatalf("mail domains = %+v, want partition snapshot", got)
|
||||
}
|
||||
|
||||
got.Accepted[0] = "changed.example.com"
|
||||
if runtime.Root.Domains.Accepted[0] != "partition.example.com" {
|
||||
t.Fatal("mail domain snapshot leaked mutable accepted domains")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCurrentMailDomainsFallsBackToLegacyConfig(t *testing.T) {
|
||||
previousManager := CurrentManager()
|
||||
previousInstance := Instance
|
||||
defer func() {
|
||||
SetRuntimeManager(previousManager)
|
||||
Instance = previousInstance
|
||||
PublishConfigSnapshot(previousInstance)
|
||||
}()
|
||||
SetRuntimeManager(nil)
|
||||
|
||||
Instance = &Config{Domain: "legacy.example.com"}
|
||||
PublishConfigSnapshot(Instance)
|
||||
|
||||
got := CurrentMailDomains()
|
||||
if got.Primary != "legacy.example.com" || len(got.Accepted) != 1 || got.Accepted[0] != got.Primary {
|
||||
t.Fatalf("legacy mail domains = %+v, want normalized fallback", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCurrentSMTPRuntimeSettingsReadsPartitionSnapshot(t *testing.T) {
|
||||
previousManager := CurrentManager()
|
||||
defer SetRuntimeManager(previousManager)
|
||||
|
||||
@ -100,8 +100,8 @@ func (p *Dkim) Sign(msgData string) ([]byte, error) {
|
||||
if p == nil || p.privateKey == nil {
|
||||
return nil, fmt.Errorf("DKIM signer is not initialized")
|
||||
}
|
||||
cfg := config.CurrentConfig()
|
||||
if cfg == nil || strings.TrimSpace(cfg.Domain) == "" {
|
||||
domains := config.CurrentMailDomains()
|
||||
if strings.TrimSpace(domains.Primary) == "" {
|
||||
return nil, fmt.Errorf("DKIM domain is not configured")
|
||||
}
|
||||
|
||||
@ -109,7 +109,7 @@ func (p *Dkim) Sign(msgData string) ([]byte, error) {
|
||||
r := strings.NewReader(msgData)
|
||||
|
||||
options := &dkim.SignOptions{
|
||||
Domain: cfg.Domain,
|
||||
Domain: domains.Primary,
|
||||
Selector: "default",
|
||||
Signer: p.privateKey,
|
||||
}
|
||||
|
||||
@ -18,6 +18,10 @@ func StartWithTls() error {
|
||||
if cfg == nil {
|
||||
return fmt.Errorf("runtime configuration is unavailable")
|
||||
}
|
||||
domains := config.CurrentMailDomains()
|
||||
if domains.Primary == "" {
|
||||
return fmt.Errorf("mail domain is not configured")
|
||||
}
|
||||
crt, err := config.LoadRuntimeTLSCertificate(cfg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("load POP3 TLS certificate: %w", err)
|
||||
@ -26,7 +30,7 @@ func StartWithTls() error {
|
||||
tlsConfig.Certificates = []tls.Certificate{crt}
|
||||
tlsConfig.Time = time.Now
|
||||
tlsConfig.Rand = rand.Reader
|
||||
instanceTls = gopop.NewPop3Server(995, "pop."+cfg.Domain, true, tlsConfig, action{})
|
||||
instanceTls = gopop.NewPop3Server(995, "pop."+domains.Primary, true, tlsConfig, action{})
|
||||
instanceTls.ConnectAliveTime = 5 * time.Minute
|
||||
|
||||
log.Infof("POP3 With TLS Server Start On Port :995")
|
||||
@ -43,6 +47,10 @@ func Start() error {
|
||||
if cfg == nil {
|
||||
return fmt.Errorf("runtime configuration is unavailable")
|
||||
}
|
||||
domains := config.CurrentMailDomains()
|
||||
if domains.Primary == "" {
|
||||
return fmt.Errorf("mail domain is not configured")
|
||||
}
|
||||
crt, err := config.LoadRuntimeTLSCertificate(cfg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("load POP3 certificate: %w", err)
|
||||
@ -52,7 +60,7 @@ func Start() error {
|
||||
tlsConfig.Time = time.Now
|
||||
tlsConfig.Rand = rand.Reader
|
||||
port := config.CurrentMailPorts().POP3
|
||||
instance = gopop.NewPop3Server(port, "pop."+cfg.Domain, false, tlsConfig, action{})
|
||||
instance = gopop.NewPop3Server(port, "pop."+domains.Primary, false, tlsConfig, action{})
|
||||
instance.ConnectAliveTime = 5 * time.Minute
|
||||
log.Infof("POP3 Server Start On Port :%d", port)
|
||||
|
||||
|
||||
@ -123,8 +123,8 @@ func (s *Session) Data(r io.Reader) error {
|
||||
SPFStatus = spfCheck(s.RemoteAddress.String(), email.Sender, email.Sender.EmailAddress)
|
||||
|
||||
_, formDomain := email.From.GetDomainAccount()
|
||||
cfg := config.CurrentConfig()
|
||||
spoofed := cfg != nil && array.InArray(formDomain, cfg.Domains) && SPFStatus == false
|
||||
domains := config.CurrentMailDomains()
|
||||
spoofed := array.InArray(formDomain, domains.Accepted) && SPFStatus == false
|
||||
if spoofed {
|
||||
dkimStatus = false
|
||||
}
|
||||
@ -227,17 +227,17 @@ func validateOutgoingEmail(email *parsemail.Email) error {
|
||||
}
|
||||
|
||||
func authorizedSubmissionEmail(email *parsemail.Email, envelopeFrom string, ctx *context.Context) bool {
|
||||
cfg := config.CurrentConfig()
|
||||
if email == nil || email.From == nil || email.Sender == nil || ctx == nil || cfg == nil {
|
||||
domains := config.CurrentMailDomains()
|
||||
if email == nil || email.From == nil || email.Sender == nil || ctx == nil || len(domains.Accepted) == 0 {
|
||||
return false
|
||||
}
|
||||
if !validation.IsAuthorizedSender(email.From.EmailAddress, ctx.UserAccount, ctx.IsAdmin, cfg.Domains) {
|
||||
if !validation.IsAuthorizedSender(email.From.EmailAddress, ctx.UserAccount, ctx.IsAdmin, domains.Accepted) {
|
||||
return false
|
||||
}
|
||||
if !validation.IsAuthorizedSender(email.Sender.EmailAddress, ctx.UserAccount, ctx.IsAdmin, cfg.Domains) {
|
||||
if !validation.IsAuthorizedSender(email.Sender.EmailAddress, ctx.UserAccount, ctx.IsAdmin, domains.Accepted) {
|
||||
return false
|
||||
}
|
||||
return envelopeFrom == "" || validation.IsAuthorizedSender(envelopeFrom, ctx.UserAccount, ctx.IsAdmin, cfg.Domains)
|
||||
return envelopeFrom == "" || validation.IsAuthorizedSender(envelopeFrom, ctx.UserAccount, ctx.IsAdmin, domains.Accepted)
|
||||
}
|
||||
|
||||
func saveEmail(ctx *context.Context, size int, email *parsemail.Email, sendUserID int, emailType int, reallyTo []string, SPFStatus, dkimStatus bool) ([]*models.User, *models.Email, error) {
|
||||
@ -259,11 +259,11 @@ func saveEmail(ctx *context.Context, size int, email *parsemail.Email, sendUserI
|
||||
|
||||
msgID := email.MsgID
|
||||
if msgID == "" {
|
||||
cfg := config.CurrentConfig()
|
||||
if cfg == nil || cfg.Domain == "" {
|
||||
domains := config.CurrentMailDomains()
|
||||
if domains.Primary == "" {
|
||||
return nil, nil, fmt.Errorf("runtime configuration is unavailable")
|
||||
}
|
||||
msgID, err = parsemail.GenerateMsgID(cfg.Domain)
|
||||
msgID, err = parsemail.GenerateMsgID(domains.Primary)
|
||||
if err != nil {
|
||||
return users, nil, fmt.Errorf("generate message ID: %w", err)
|
||||
}
|
||||
|
||||
@ -26,12 +26,16 @@ func StartWithTLSNew() error {
|
||||
if cfg == nil {
|
||||
return fmt.Errorf("runtime configuration is unavailable")
|
||||
}
|
||||
domains := config.CurrentMailDomains()
|
||||
if domains.Primary == "" {
|
||||
return fmt.Errorf("mail domain is not configured")
|
||||
}
|
||||
be := &Backend{}
|
||||
|
||||
instanceTlsNew = smtp.NewServer(be)
|
||||
|
||||
instanceTlsNew.Addr = ":587"
|
||||
instanceTlsNew.Domain = cfg.Domain
|
||||
instanceTlsNew.Domain = domains.Primary
|
||||
instanceTlsNew.ReadTimeout = 10 * time.Second
|
||||
instanceTlsNew.WriteTimeout = 10 * time.Second
|
||||
instanceTlsNew.MaxMessageBytes = maxSMTPMessageBytes
|
||||
@ -59,12 +63,16 @@ func StartWithTLS() error {
|
||||
if cfg == nil {
|
||||
return fmt.Errorf("runtime configuration is unavailable")
|
||||
}
|
||||
domains := config.CurrentMailDomains()
|
||||
if domains.Primary == "" {
|
||||
return fmt.Errorf("mail domain is not configured")
|
||||
}
|
||||
be := &Backend{}
|
||||
|
||||
instanceTls = smtp.NewServer(be)
|
||||
|
||||
instanceTls.Addr = ":465"
|
||||
instanceTls.Domain = cfg.Domain
|
||||
instanceTls.Domain = domains.Primary
|
||||
instanceTls.ReadTimeout = 10 * time.Second
|
||||
instanceTls.WriteTimeout = 10 * time.Second
|
||||
instanceTls.MaxMessageBytes = maxSMTPMessageBytes
|
||||
@ -91,12 +99,16 @@ func Start() error {
|
||||
if cfg == nil {
|
||||
return fmt.Errorf("runtime configuration is unavailable")
|
||||
}
|
||||
domains := config.CurrentMailDomains()
|
||||
if domains.Primary == "" {
|
||||
return fmt.Errorf("mail domain is not configured")
|
||||
}
|
||||
be := &Backend{}
|
||||
|
||||
instance = smtp.NewServer(be)
|
||||
|
||||
instance.Addr = fmt.Sprintf(":%d", config.CurrentMailPorts().SMTP)
|
||||
instance.Domain = cfg.Domain
|
||||
instance.Domain = domains.Primary
|
||||
instance.ReadTimeout = 10 * time.Second
|
||||
instance.WriteTimeout = 10 * time.Second
|
||||
instance.MaxMessageBytes = maxSMTPMessageBytes
|
||||
|
||||
@ -51,23 +51,23 @@ func Forward(ctx *context.Context, e *parsemail.Email, forwardAddress string, us
|
||||
|
||||
log.WithContext(ctx).Debug("forward message prepared")
|
||||
|
||||
cfg := config.CurrentConfig()
|
||||
if cfg == nil || len(cfg.Domains) == 0 {
|
||||
domains := config.CurrentMailDomains()
|
||||
if len(domains.Accepted) == 0 {
|
||||
return errors.New("runtime configuration is unavailable")
|
||||
}
|
||||
from := user.Account + "@" + cfg.Domains[0]
|
||||
return forwardData(ctx, cfg.Domains[0], b, forwardAddress, from)
|
||||
from := user.Account + "@" + domains.Primary
|
||||
return forwardData(ctx, domains.Primary, b, forwardAddress, from)
|
||||
}
|
||||
|
||||
func ForwardRaw(ctx *context.Context, e *parsemail.Email, rawEmailData []byte, forwardAddress string, user *models.User) error {
|
||||
log.WithContext(ctx).Debugf("开始原始邮件转发")
|
||||
|
||||
cfg := config.CurrentConfig()
|
||||
if cfg == nil || len(cfg.Domains) == 0 {
|
||||
domains := config.CurrentMailDomains()
|
||||
if len(domains.Accepted) == 0 {
|
||||
return errors.New("runtime configuration is unavailable")
|
||||
}
|
||||
from := user.Account + "@" + cfg.Domains[0]
|
||||
return forwardData(ctx, cfg.Domains[0], rawEmailData, forwardAddress, from)
|
||||
from := user.Account + "@" + domains.Primary
|
||||
return forwardData(ctx, domains.Primary, rawEmailData, forwardAddress, from)
|
||||
}
|
||||
|
||||
func forwardData(ctx *context.Context, fromDomain string, data []byte, forwardAddress string, from string) error {
|
||||
|
||||
@ -221,8 +221,8 @@ func NewClient(conn net.Conn, host, fromDomain string) (*Client, error) {
|
||||
|
||||
if fromDomain != "" {
|
||||
localName = fromDomain
|
||||
} else if cfg := config.CurrentConfig(); cfg != nil && cfg.Domain != "" {
|
||||
localName = cfg.Domain
|
||||
} else if domains := config.CurrentMailDomains(); domains.Primary != "" {
|
||||
localName = domains.Primary
|
||||
}
|
||||
|
||||
c := &Client{Text: text, conn: conn, serverName: host, localName: localName}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user