mirror of
https://github.com/mjl-/mox.git
synced 2024-12-27 08:53:48 +03:00
in smtpserver, accept delivery to postmaster@<hostname>, and also postmaster@ addresses for domains that don't have a postmaster address configured.
This commit is contained in:
parent
74dab5fc39
commit
c1753b369d
8 changed files with 85 additions and 5 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -17,6 +17,7 @@
|
||||||
/testdata/smtp/datajunk/
|
/testdata/smtp/datajunk/
|
||||||
/testdata/smtp/sendlimit/data/
|
/testdata/smtp/sendlimit/data/
|
||||||
/testdata/smtp/catchall/data/
|
/testdata/smtp/catchall/data/
|
||||||
|
/testdata/smtp/postmaster/
|
||||||
/testdata/store/data/
|
/testdata/store/data/
|
||||||
/testdata/train/
|
/testdata/train/
|
||||||
/cover.out
|
/cover.out
|
||||||
|
|
|
@ -52,7 +52,7 @@ type Static struct {
|
||||||
Postmaster struct {
|
Postmaster struct {
|
||||||
Account string
|
Account string
|
||||||
Mailbox string `sconf-doc:"E.g. Postmaster or Inbox."`
|
Mailbox string `sconf-doc:"E.g. Postmaster or Inbox."`
|
||||||
} `sconf-doc:"Destination for emails delivered to postmaster address."`
|
} `sconf-doc:"Destination for emails delivered to postmaster addresses: a plain 'postmaster' without domain, 'postmaster@<hostname>' (also for each listener with SMTP enabled), and as fallback for each domain without explicitly configured postmaster destination."`
|
||||||
DefaultMailboxes []string `sconf:"optional" sconf-doc:"Mailboxes to create when adding an account. Inbox is always created. If no mailboxes are specified, the following are automatically created: Sent, Archive, Trash, Drafts and Junk."`
|
DefaultMailboxes []string `sconf:"optional" sconf-doc:"Mailboxes to create when adding an account. Inbox is always created. If no mailboxes are specified, the following are automatically created: Sent, Archive, Trash, Drafts and Junk."`
|
||||||
|
|
||||||
// All IPs that were explicitly listen on for external SMTP. Only set when there
|
// All IPs that were explicitly listen on for external SMTP. Only set when there
|
||||||
|
|
|
@ -321,7 +321,10 @@ describe-static" and "mox config describe-domains":
|
||||||
# Port for HTTPS webserver. (optional)
|
# Port for HTTPS webserver. (optional)
|
||||||
Port: 0
|
Port: 0
|
||||||
|
|
||||||
# Destination for emails delivered to postmaster address.
|
# Destination for emails delivered to postmaster addresses: a plain 'postmaster'
|
||||||
|
# without domain, 'postmaster@<hostname>' (also for each listener with SMTP
|
||||||
|
# enabled), and as fallback for each domain without explicitly configured
|
||||||
|
# postmaster destination.
|
||||||
Postmaster:
|
Postmaster:
|
||||||
Account:
|
Account:
|
||||||
|
|
||||||
|
|
|
@ -270,7 +270,7 @@ func MakeDomainConfig(ctx context.Context, domain, hostname dns.Domain, accountN
|
||||||
// DomainAdd adds the domain to the domains config, rewriting domains.conf and
|
// DomainAdd adds the domain to the domains config, rewriting domains.conf and
|
||||||
// marking it loaded.
|
// marking it loaded.
|
||||||
//
|
//
|
||||||
// accountName is used for DMARC/TLS report.
|
// accountName is used for DMARC/TLS report and potentially for the postmaster address.
|
||||||
// If the account does not exist, it is created with localpart. Localpart must be
|
// If the account does not exist, it is created with localpart. Localpart must be
|
||||||
// set only if the account does not yet exist.
|
// set only if the account does not yet exist.
|
||||||
func DomainAdd(ctx context.Context, domain dns.Domain, accountName string, localpart smtp.Localpart) (rerr error) {
|
func DomainAdd(ctx context.Context, domain dns.Domain, accountName string, localpart smtp.Localpart) (rerr error) {
|
||||||
|
@ -325,6 +325,16 @@ func DomainAdd(ctx context.Context, domain dns.Domain, accountName string, local
|
||||||
return fmt.Errorf("account name is empty")
|
return fmt.Errorf("account name is empty")
|
||||||
} else if !ok {
|
} else if !ok {
|
||||||
nc.Accounts[accountName] = MakeAccountConfig(smtp.Address{Localpart: localpart, Domain: domain})
|
nc.Accounts[accountName] = MakeAccountConfig(smtp.Address{Localpart: localpart, Domain: domain})
|
||||||
|
} else if accountName != Conf.Static.Postmaster.Account {
|
||||||
|
nacc := nc.Accounts[accountName]
|
||||||
|
nd := map[string]config.Destination{}
|
||||||
|
for k, v := range nacc.Destinations {
|
||||||
|
nd[k] = v
|
||||||
|
}
|
||||||
|
pmaddr := smtp.Address{Localpart: "postmaster", Domain: domain}
|
||||||
|
nd[pmaddr.String()] = config.Destination{}
|
||||||
|
nacc.Destinations = nd
|
||||||
|
nc.Accounts[accountName] = nacc
|
||||||
}
|
}
|
||||||
|
|
||||||
nc.Domains[domain.Name()] = confDomain
|
nc.Domains[domain.Name()] = confDomain
|
||||||
|
|
|
@ -22,8 +22,21 @@ func FindAccount(localpart smtp.Localpart, domain dns.Domain, allowPostmaster bo
|
||||||
if strings.EqualFold(string(localpart), "postmaster") {
|
if strings.EqualFold(string(localpart), "postmaster") {
|
||||||
localpart = "postmaster"
|
localpart = "postmaster"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
postmasterDomain := func() bool {
|
||||||
var zerodomain dns.Domain
|
var zerodomain dns.Domain
|
||||||
if localpart == "postmaster" && domain == zerodomain {
|
if domain == zerodomain || domain == Conf.Static.HostnameDomain {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for _, l := range Conf.Static.Listeners {
|
||||||
|
if l.SMTP.Enabled && domain == l.HostnameDomain {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if localpart == "postmaster" && postmasterDomain() {
|
||||||
if !allowPostmaster {
|
if !allowPostmaster {
|
||||||
return "", "", config.Destination{}, ErrAccountNotFound
|
return "", "", config.Destination{}, ErrAccountNotFound
|
||||||
}
|
}
|
||||||
|
@ -44,6 +57,9 @@ func FindAccount(localpart smtp.Localpart, domain dns.Domain, allowPostmaster bo
|
||||||
accAddr, ok := Conf.AccountDestination(canonical)
|
accAddr, ok := Conf.AccountDestination(canonical)
|
||||||
if !ok {
|
if !ok {
|
||||||
if accAddr, ok = Conf.AccountDestination("@" + domain.Name()); !ok {
|
if accAddr, ok = Conf.AccountDestination("@" + domain.Name()); !ok {
|
||||||
|
if localpart == "postmaster" && allowPostmaster {
|
||||||
|
return Conf.Static.Postmaster.Account, "postmaster", config.Destination{Mailbox: Conf.Static.Postmaster.Mailbox}, nil
|
||||||
|
}
|
||||||
return "", "", config.Destination{}, ErrAccountNotFound
|
return "", "", config.Destination{}, ErrAccountNotFound
|
||||||
}
|
}
|
||||||
canonical = "@" + domain.Name()
|
canonical = "@" + domain.Name()
|
||||||
|
|
|
@ -1097,3 +1097,37 @@ test email
|
||||||
testSubmit("mjl@mox.example", "mjl@mox.example")
|
testSubmit("mjl@mox.example", "mjl@mox.example")
|
||||||
testSubmit("mjl@mox.example", "mjl@mox2.example") // DKIM signature will be for mox2.example.
|
testSubmit("mjl@mox.example", "mjl@mox2.example") // DKIM signature will be for mox2.example.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test to postmaster addresses.
|
||||||
|
func TestPostmaster(t *testing.T) {
|
||||||
|
resolver := dns.MockResolver{
|
||||||
|
A: map[string][]string{
|
||||||
|
"other.example.": {"127.0.0.10"}, // For mx check.
|
||||||
|
},
|
||||||
|
PTR: map[string][]string{
|
||||||
|
"127.0.0.10": {"other.example."},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
ts := newTestServer(t, "../testdata/smtp/postmaster/mox.conf", resolver)
|
||||||
|
defer ts.close()
|
||||||
|
|
||||||
|
testDeliver := func(rcptTo string, expErr *smtpclient.Error) {
|
||||||
|
t.Helper()
|
||||||
|
ts.run(func(err error, client *smtpclient.Client) {
|
||||||
|
t.Helper()
|
||||||
|
mailFrom := "mjl@other.example"
|
||||||
|
if err == nil {
|
||||||
|
err = client.Deliver(context.Background(), mailFrom, rcptTo, int64(len(deliverMessage)), strings.NewReader(deliverMessage), false, false)
|
||||||
|
}
|
||||||
|
var cerr smtpclient.Error
|
||||||
|
if expErr == nil && err != nil || expErr != nil && (err == nil || !errors.As(err, &cerr) || cerr.Code != expErr.Code || cerr.Secode != expErr.Secode) {
|
||||||
|
t.Fatalf("got err %#v, expected %#v", err, expErr)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
testDeliver("postmaster", nil) // Plain postmaster address without domain.
|
||||||
|
testDeliver("postmaster@host.mox.example", nil) // Postmaster address with configured mail server hostname.
|
||||||
|
testDeliver("postmaster@mox.example", nil) // Postmaster address without explicitly configured destination.
|
||||||
|
testDeliver("postmaster@unknown.example", &smtpclient.Error{Code: smtp.C550MailboxUnavail, Secode: smtp.SeAddr1UnknownDestMailbox1})
|
||||||
|
}
|
||||||
|
|
7
testdata/smtp/postmaster/domains.conf
vendored
Normal file
7
testdata/smtp/postmaster/domains.conf
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
Domains:
|
||||||
|
mox.example: nil
|
||||||
|
Accounts:
|
||||||
|
mjl:
|
||||||
|
Domain: mox.example
|
||||||
|
Destinations:
|
||||||
|
mjl@mox.example: nil
|
9
testdata/smtp/postmaster/mox.conf
vendored
Normal file
9
testdata/smtp/postmaster/mox.conf
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
DataDir: data
|
||||||
|
User: 1000
|
||||||
|
LogLevel: trace
|
||||||
|
Hostname: host.mox.example
|
||||||
|
Postmaster:
|
||||||
|
Account: mjl
|
||||||
|
Mailbox: postmaster
|
||||||
|
Listeners:
|
||||||
|
local: nil
|
Loading…
Reference in a new issue