mirror of
https://github.com/mjl-/mox.git
synced 2024-12-27 17:03:47 +03:00
1cf7477642
instead of skipping on any smtp and delivering messages to accounts. we dial the ip of the smtp listener, which is localhost:1025 by default. the smtp server now uses a mock dns resolver during spf & dkim verification for hosted domains (localhost by default), so they should pass. the advantage is that we get regular full smtp server behaviour for delivering in localserve, including webhooks, and potential first-time sender delays (though this is disabled by default now). incoming deliveries now go through normal address resolution, where before we would always deliver to mox@localhost. we still accept email for unknown recipients to mox@localhost. this will be useful upcoming alias/list functionality. localserve will now generate a dkim key when creating a new config. existing users may wish to reset (remove) their localserve directory, or add a dkim key.
103 lines
2.9 KiB
Go
103 lines
2.9 KiB
Go
package queue
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"log/slog"
|
|
"net"
|
|
"os"
|
|
"time"
|
|
|
|
"github.com/mjl-/mox/dns"
|
|
"github.com/mjl-/mox/dsn"
|
|
"github.com/mjl-/mox/mlog"
|
|
"github.com/mjl-/mox/mox-"
|
|
"github.com/mjl-/mox/smtpclient"
|
|
"github.com/mjl-/mox/store"
|
|
)
|
|
|
|
// We won't be dialing remote servers. We just connect the smtp port of the first
|
|
// ip in the "local" listener, with fallback to localhost:1025 for any destination
|
|
// address and try to deliver. Our smtpserver uses a mocked dns resolver to give
|
|
// spf/dkim a chance to pass.
|
|
func deliverLocalserve(ctx context.Context, log mlog.Log, msgs []*Msg, backoff time.Duration) {
|
|
m0 := msgs[0]
|
|
|
|
addr := "localhost:1025"
|
|
l, ok := mox.Conf.Static.Listeners["local"]
|
|
if ok && l.SMTP.Enabled {
|
|
port := 1025
|
|
if l.SMTP.Port != 0 {
|
|
port = l.SMTP.Port
|
|
}
|
|
addr = net.JoinHostPort(l.IPs[0], fmt.Sprintf("%d", port))
|
|
}
|
|
var d net.Dialer
|
|
dialctx, dialcancel := context.WithTimeout(ctx, 30*time.Second)
|
|
defer dialcancel()
|
|
conn, err := d.DialContext(dialctx, "tcp", addr)
|
|
dialcancel()
|
|
if err != nil {
|
|
failMsgsDB(log, msgs, m0.DialedIPs, backoff, dsn.NameIP{}, err)
|
|
return
|
|
}
|
|
defer func() {
|
|
if conn != nil {
|
|
err = conn.Close()
|
|
log.Check(err, "closing connection")
|
|
}
|
|
}()
|
|
|
|
clientctx, clientcancel := context.WithTimeout(context.Background(), 60*time.Second)
|
|
defer clientcancel()
|
|
localhost := dns.Domain{ASCII: "localhost"}
|
|
client, err := smtpclient.New(clientctx, log.Logger, conn, smtpclient.TLSOpportunistic, false, localhost, localhost, smtpclient.Opts{})
|
|
clientcancel()
|
|
if err != nil {
|
|
failMsgsDB(log, msgs, m0.DialedIPs, backoff, dsn.NameIP{}, err)
|
|
return
|
|
}
|
|
conn = nil // Will be closed when closing client.
|
|
defer func() {
|
|
err := client.Close()
|
|
log.Check(err, "closing smtp client")
|
|
}()
|
|
|
|
var msgr io.ReadCloser
|
|
var size int64
|
|
if len(m0.DSNUTF8) > 0 {
|
|
msgr = io.NopCloser(bytes.NewReader(m0.DSNUTF8))
|
|
size = int64(len(m0.DSNUTF8))
|
|
} else {
|
|
size = m0.Size
|
|
p := m0.MessagePath()
|
|
f, err := os.Open(p)
|
|
if err != nil {
|
|
log.Errorx("opening message for delivery", err, slog.String("remote", addr), slog.String("path", p))
|
|
err = fmt.Errorf("opening message file: %v", err)
|
|
failMsgsDB(log, msgs, m0.DialedIPs, backoff, dsn.NameIP{}, err)
|
|
return
|
|
}
|
|
msgr = store.FileMsgReader(m0.MsgPrefix, f)
|
|
defer func() {
|
|
err := msgr.Close()
|
|
log.Check(err, "closing message after delivery attempt")
|
|
}()
|
|
}
|
|
|
|
deliverctx, delivercancel := context.WithTimeout(context.Background(), 60*time.Second)
|
|
defer delivercancel()
|
|
requireTLS := m0.RequireTLS != nil && *m0.RequireTLS
|
|
rcpts := make([]string, len(msgs))
|
|
for i, m := range msgs {
|
|
rcpts[i] = m.Recipient().String()
|
|
}
|
|
rcptErrs, err := client.DeliverMultiple(deliverctx, m0.Sender().String(), rcpts, size, msgr, m0.Has8bit, m0.SMTPUTF8, requireTLS)
|
|
delivercancel()
|
|
if err != nil {
|
|
log.Infox("smtp transaction for delivery failed", err)
|
|
}
|
|
processDeliveries(log, m0, msgs, addr, "localhost", backoff, rcptErrs, err)
|
|
}
|