mirror of
https://github.com/mjl-/mox.git
synced 2025-07-12 17:44:35 +03:00
expose fewer internals in packages, for easier software reuse
- prometheus is now behind an interface, they aren't dependencies for the reusable components anymore. - some dependencies have been inverted: instead of packages importing a main package to get configuration, the main package now sets configuration in these packages. that means fewer internals are pulled in. - some functions now have new parameters for values that were retrieved from package "mox-".
This commit is contained in:
@ -90,14 +90,14 @@ var (
|
||||
)
|
||||
|
||||
// todo: rename function, perhaps put some of the params in a delivery struct so we don't pass all the params all the time?
|
||||
func fail(qlog mlog.Log, m Msg, backoff time.Duration, permanent bool, remoteMTA dsn.NameIP, secodeOpt, errmsg string) {
|
||||
func fail(ctx context.Context, qlog mlog.Log, m Msg, backoff time.Duration, permanent bool, remoteMTA dsn.NameIP, secodeOpt, errmsg string) {
|
||||
// todo future: when we implement relaying, we should be able to send DSNs to non-local users. and possibly specify a null mailfrom. ../rfc/5321:1503
|
||||
// todo future: when we implement relaying, and a dsn cannot be delivered, and requiretls was active, we cannot drop the message. instead deliver to local postmaster? though ../rfc/8689:383 may intend to say the dsn should be delivered without requiretls?
|
||||
// todo future: when we implement smtp dsn extension, parameter RET=FULL must be disregarded for messages with REQUIRETLS. ../rfc/8689:379
|
||||
|
||||
if permanent || m.MaxAttempts == 0 && m.Attempts >= 8 || m.MaxAttempts > 0 && m.Attempts >= m.MaxAttempts {
|
||||
qlog.Errorx("permanent failure delivering from queue", errors.New(errmsg))
|
||||
deliverDSNFailure(qlog, m, remoteMTA, secodeOpt, errmsg)
|
||||
deliverDSNFailure(ctx, qlog, m, remoteMTA, secodeOpt, errmsg)
|
||||
|
||||
if err := queueDelete(context.Background(), m.ID); err != nil {
|
||||
qlog.Errorx("deleting message from queue after permanent failure", err)
|
||||
@ -117,7 +117,7 @@ func fail(qlog mlog.Log, m Msg, backoff time.Duration, permanent bool, remoteMTA
|
||||
qlog.Errorx("temporary failure delivering from queue, sending delayed dsn", errors.New(errmsg), slog.Duration("backoff", backoff))
|
||||
|
||||
retryUntil := m.LastAttempt.Add((4 + 8 + 16) * time.Hour)
|
||||
deliverDSNDelay(qlog, m, remoteMTA, secodeOpt, errmsg, retryUntil)
|
||||
deliverDSNDelay(ctx, qlog, m, remoteMTA, secodeOpt, errmsg, retryUntil)
|
||||
} else {
|
||||
qlog.Errorx("temporary failure delivering from queue", errors.New(errmsg), slog.Duration("backoff", backoff), slog.Time("nextattempt", m.NextAttempt))
|
||||
}
|
||||
@ -169,7 +169,7 @@ func deliverDirect(qlog mlog.Log, resolver dns.Resolver, dialer smtpclient.Diale
|
||||
recipientDomainResult.Summary.TotalFailureSessionCount++
|
||||
}
|
||||
|
||||
fail(qlog, m, backoff, permanent, dsn.NameIP{}, "", err.Error())
|
||||
fail(ctx, qlog, m, backoff, permanent, dsn.NameIP{}, "", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
@ -189,7 +189,7 @@ func deliverDirect(qlog mlog.Log, resolver dns.Resolver, dialer smtpclient.Diale
|
||||
} else {
|
||||
qlog.Infox("mtasts lookup temporary error, aborting delivery attempt", err, slog.Any("domain", origNextHop))
|
||||
recipientDomainResult.Summary.TotalFailureSessionCount++
|
||||
fail(qlog, m, backoff, false, dsn.NameIP{}, "", err.Error())
|
||||
fail(ctx, qlog, m, backoff, false, dsn.NameIP{}, "", err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -333,7 +333,7 @@ func deliverDirect(qlog mlog.Log, resolver dns.Resolver, dialer smtpclient.Diale
|
||||
permanent = true
|
||||
}
|
||||
|
||||
fail(qlog, m, backoff, permanent, remoteMTA, secodeOpt, errmsg)
|
||||
fail(ctx, qlog, m, backoff, permanent, remoteMTA, secodeOpt, errmsg)
|
||||
return
|
||||
}
|
||||
|
||||
@ -527,7 +527,7 @@ func deliverHost(log mlog.Log, resolver dns.Resolver, dialer smtpclient.Dialer,
|
||||
if m.DialedIPs == nil {
|
||||
m.DialedIPs = map[string][]net.IP{}
|
||||
}
|
||||
conn, remoteIP, err = smtpclient.Dial(ctx, log.Logger, dialer, host, ips, 25, m.DialedIPs)
|
||||
conn, remoteIP, err = smtpclient.Dial(ctx, log.Logger, dialer, host, ips, 25, m.DialedIPs, mox.Conf.Static.SpecifiedSMTPListenIPs)
|
||||
}
|
||||
cancel()
|
||||
|
||||
|
14
queue/dsn.go
14
queue/dsn.go
@ -2,6 +2,7 @@ package queue
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
@ -29,7 +30,7 @@ var (
|
||||
)
|
||||
)
|
||||
|
||||
func deliverDSNFailure(log mlog.Log, m Msg, remoteMTA dsn.NameIP, secodeOpt, errmsg string) {
|
||||
func deliverDSNFailure(ctx context.Context, log mlog.Log, m Msg, remoteMTA dsn.NameIP, secodeOpt, errmsg string) {
|
||||
const subject = "mail delivery failed"
|
||||
message := fmt.Sprintf(`
|
||||
Delivery has failed permanently for your email to:
|
||||
@ -43,10 +44,10 @@ Error during the last delivery attempt:
|
||||
%s
|
||||
`, m.Recipient().XString(m.SMTPUTF8), errmsg)
|
||||
|
||||
deliverDSN(log, m, remoteMTA, secodeOpt, errmsg, true, nil, subject, message)
|
||||
deliverDSN(ctx, log, m, remoteMTA, secodeOpt, errmsg, true, nil, subject, message)
|
||||
}
|
||||
|
||||
func deliverDSNDelay(log mlog.Log, m Msg, remoteMTA dsn.NameIP, secodeOpt, errmsg string, retryUntil time.Time) {
|
||||
func deliverDSNDelay(ctx context.Context, log mlog.Log, m Msg, remoteMTA dsn.NameIP, secodeOpt, errmsg string, retryUntil time.Time) {
|
||||
// Should not happen, but doesn't hurt to prevent sending delayed delivery
|
||||
// notifications for DMARC reports. We don't want to waste postmaster attention.
|
||||
if m.IsDMARCReport {
|
||||
@ -67,14 +68,14 @@ Error during the last delivery attempt:
|
||||
%s
|
||||
`, m.Recipient().XString(false), errmsg)
|
||||
|
||||
deliverDSN(log, m, remoteMTA, secodeOpt, errmsg, false, &retryUntil, subject, message)
|
||||
deliverDSN(ctx, log, m, remoteMTA, secodeOpt, errmsg, false, &retryUntil, subject, message)
|
||||
}
|
||||
|
||||
// We only queue DSNs for delivery failures for emails submitted by authenticated
|
||||
// users. So we are delivering to local users. ../rfc/5321:1466
|
||||
// ../rfc/5321:1494
|
||||
// ../rfc/7208:490
|
||||
func deliverDSN(log mlog.Log, m Msg, remoteMTA dsn.NameIP, secodeOpt, errmsg string, permanent bool, retryUntil *time.Time, subject, textBody string) {
|
||||
func deliverDSN(ctx context.Context, log mlog.Log, m Msg, remoteMTA dsn.NameIP, secodeOpt, errmsg string, permanent bool, retryUntil *time.Time, subject, textBody string) {
|
||||
kind := "delayed delivery"
|
||||
if permanent {
|
||||
kind = "failure"
|
||||
@ -124,6 +125,7 @@ func deliverDSN(log mlog.Log, m Msg, remoteMTA dsn.NameIP, secodeOpt, errmsg str
|
||||
From: smtp.Path{Localpart: "postmaster", IPDomain: dns.IPDomain{Domain: mox.Conf.Static.HostnameDomain}},
|
||||
To: m.Sender(),
|
||||
Subject: subject,
|
||||
MessageID: mox.MessageIDGen(false),
|
||||
References: m.MessageID,
|
||||
TextBody: textBody,
|
||||
|
||||
@ -150,7 +152,7 @@ func deliverDSN(log mlog.Log, m Msg, remoteMTA dsn.NameIP, secodeOpt, errmsg str
|
||||
return
|
||||
}
|
||||
|
||||
msgData = append(msgData, []byte("Return-Path: <"+dsnMsg.From.XString(m.SMTPUTF8)+">\r\n")...)
|
||||
msgData = append([]byte("Return-Path: <"+dsnMsg.From.XString(m.SMTPUTF8)+">\r\n"), msgData...)
|
||||
|
||||
mailbox := "Inbox"
|
||||
senderAccount := m.SenderAccount
|
||||
|
@ -525,6 +525,8 @@ func queueDelete(ctx context.Context, msgID int64) error {
|
||||
// The queue is updated, either by removing a delivered or permanently failed
|
||||
// message, or updating the time for the next attempt. A DSN may be sent.
|
||||
func deliver(log mlog.Log, resolver dns.Resolver, m Msg) {
|
||||
ctx := mox.Shutdown
|
||||
|
||||
qlog := log.WithCid(mox.Cid()).With(slog.Any("from", m.Sender()),
|
||||
slog.Any("recipient", m.Recipient()),
|
||||
slog.Int("attempts", m.Attempts),
|
||||
@ -572,7 +574,7 @@ func deliver(log mlog.Log, resolver dns.Resolver, m Msg) {
|
||||
transport, ok = mox.Conf.Static.Transports[m.Transport]
|
||||
if !ok {
|
||||
var remoteMTA dsn.NameIP // Zero value, will not be included in DSN. ../rfc/3464:1027
|
||||
fail(qlog, m, backoff, false, remoteMTA, "", fmt.Sprintf("cannot find transport %q", m.Transport))
|
||||
fail(ctx, qlog, m, backoff, false, remoteMTA, "", fmt.Sprintf("cannot find transport %q", m.Transport))
|
||||
return
|
||||
}
|
||||
transportName = m.Transport
|
||||
@ -683,10 +685,10 @@ func deliver(log mlog.Log, resolver dns.Resolver, m Msg) {
|
||||
if transport.Socks != nil {
|
||||
socksdialer, err := proxy.SOCKS5("tcp", transport.Socks.Address, nil, &net.Dialer{})
|
||||
if err != nil {
|
||||
fail(qlog, m, backoff, false, dsn.NameIP{}, "", fmt.Sprintf("socks dialer: %v", err))
|
||||
fail(ctx, qlog, m, backoff, false, dsn.NameIP{}, "", fmt.Sprintf("socks dialer: %v", err))
|
||||
return
|
||||
} else if d, ok := socksdialer.(smtpclient.Dialer); !ok {
|
||||
fail(qlog, m, backoff, false, dsn.NameIP{}, "", "socks dialer is not a contextdialer")
|
||||
fail(ctx, qlog, m, backoff, false, dsn.NameIP{}, "", "socks dialer is not a contextdialer")
|
||||
return
|
||||
} else {
|
||||
dialer = d
|
||||
|
@ -70,16 +70,18 @@ func deliverSubmit(qlog mlog.Log, resolver dns.Resolver, dialer smtpclient.Diale
|
||||
|
||||
// todo: for submission, understand SRV records, and even DANE.
|
||||
|
||||
ctx := mox.Shutdown
|
||||
|
||||
// If submit was done with REQUIRETLS extension for SMTP, we must verify TLS
|
||||
// certificates. If our submission connection is not configured that way, abort.
|
||||
requireTLS := m.RequireTLS != nil && *m.RequireTLS
|
||||
if requireTLS && (tlsMode != smtpclient.TLSRequiredStartTLS && tlsMode != smtpclient.TLSImmediate || !tlsPKIX) {
|
||||
errmsg = fmt.Sprintf("transport %s: message requires verified tls but transport does not verify tls", transportName)
|
||||
fail(qlog, m, backoff, true, dsn.NameIP{}, smtp.SePol7MissingReqTLS, errmsg)
|
||||
fail(ctx, qlog, m, backoff, true, dsn.NameIP{}, smtp.SePol7MissingReqTLS, errmsg)
|
||||
return
|
||||
}
|
||||
|
||||
dialctx, dialcancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
dialctx, dialcancel := context.WithTimeout(ctx, 30*time.Second)
|
||||
defer dialcancel()
|
||||
if m.DialedIPs == nil {
|
||||
m.DialedIPs = map[string][]net.IP{}
|
||||
@ -90,7 +92,7 @@ func deliverSubmit(qlog mlog.Log, resolver dns.Resolver, dialer smtpclient.Diale
|
||||
if m.DialedIPs == nil {
|
||||
m.DialedIPs = map[string][]net.IP{}
|
||||
}
|
||||
conn, _, err = smtpclient.Dial(dialctx, qlog.Logger, dialer, dns.IPDomain{Domain: transport.DNSHost}, ips, port, m.DialedIPs)
|
||||
conn, _, err = smtpclient.Dial(dialctx, qlog.Logger, dialer, dns.IPDomain{Domain: transport.DNSHost}, ips, port, m.DialedIPs, mox.Conf.Static.SpecifiedSMTPListenIPs)
|
||||
}
|
||||
addr := net.JoinHostPort(transport.Host, fmt.Sprintf("%d", port))
|
||||
var result string
|
||||
@ -112,7 +114,7 @@ func deliverSubmit(qlog mlog.Log, resolver dns.Resolver, dialer smtpclient.Diale
|
||||
}
|
||||
qlog.Errorx("dialing for submission", err, slog.String("remote", addr))
|
||||
errmsg = fmt.Sprintf("transport %s: dialing %s for submission: %v", transportName, addr, err)
|
||||
fail(qlog, m, backoff, false, dsn.NameIP{}, "", errmsg)
|
||||
fail(ctx, qlog, m, backoff, false, dsn.NameIP{}, "", errmsg)
|
||||
return
|
||||
}
|
||||
dialcancel()
|
||||
@ -134,7 +136,7 @@ func deliverSubmit(qlog mlog.Log, resolver dns.Resolver, dialer smtpclient.Diale
|
||||
// Should not happen.
|
||||
qlog.Error("missing smtp authentication mechanisms implementation", slog.String("mechanism", mech))
|
||||
errmsg = fmt.Sprintf("transport %s: authentication mechanisms %q not implemented", transportName, mech)
|
||||
fail(qlog, m, backoff, false, dsn.NameIP{}, "", errmsg)
|
||||
fail(ctx, qlog, m, backoff, false, dsn.NameIP{}, "", errmsg)
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -155,7 +157,7 @@ func deliverSubmit(qlog mlog.Log, resolver dns.Resolver, dialer smtpclient.Diale
|
||||
qlog.Errorx("establishing smtp session for submission", err, slog.String("remote", addr))
|
||||
errmsg = fmt.Sprintf("transport %s: establishing smtp session with %s for submission: %v", transportName, addr, err)
|
||||
secodeOpt = smtperr.Secode
|
||||
fail(qlog, m, backoff, false, remoteMTA, secodeOpt, errmsg)
|
||||
fail(ctx, qlog, m, backoff, false, remoteMTA, secodeOpt, errmsg)
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
@ -180,7 +182,7 @@ func deliverSubmit(qlog mlog.Log, resolver dns.Resolver, dialer smtpclient.Diale
|
||||
if err != nil {
|
||||
qlog.Errorx("opening message for delivery", err, slog.String("remote", addr), slog.String("path", p))
|
||||
errmsg = fmt.Sprintf("transport %s: opening message file for submission: %v", transportName, err)
|
||||
fail(qlog, m, backoff, false, dsn.NameIP{}, "", errmsg)
|
||||
fail(ctx, qlog, m, backoff, false, dsn.NameIP{}, "", errmsg)
|
||||
return
|
||||
}
|
||||
msgr = store.FileMsgReader(m.MsgPrefix, f)
|
||||
@ -223,7 +225,7 @@ func deliverSubmit(qlog mlog.Log, resolver dns.Resolver, dialer smtpclient.Diale
|
||||
permanent = smtperr.Permanent
|
||||
secodeOpt = smtperr.Secode
|
||||
errmsg = fmt.Sprintf("transport %s: submitting email to %s: %v", transportName, addr, err)
|
||||
fail(qlog, m, backoff, permanent, remoteMTA, secodeOpt, errmsg)
|
||||
fail(ctx, qlog, m, backoff, permanent, remoteMTA, secodeOpt, errmsg)
|
||||
return
|
||||
}
|
||||
qlog.Info("delivered from queue with transport")
|
||||
|
Reference in New Issue
Block a user