Skip to content

Commit a1637ae

Browse files
committed
target/remote: Add smtp_ports option for remote targets
Allow ports other than 25 to be used for outgoing SMTP attempts. This change keeps the default behavior the same, but allows overriding it with configuration.
1 parent 95ba6bf commit a1637ae

File tree

4 files changed

+47
-8
lines changed

4 files changed

+47
-8
lines changed

dist/vim/syntax/maddy-conf.vim

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ syn keyword maddyModDir
198198
\ sig_expiry
199199
\ sign_fields
200200
\ sign_subdomains
201+
\ smtp_ports
201202
\ softfail_action
202203
\ SOME_action
203204
\ source

docs/reference/targets/remote.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,19 @@ Warning: this may break sending outgoing mail to IPv6-only SMTP servers.
5353

5454
---
5555

56+
### smtp_ports `string1 [ string2 [... stringN ]]`
57+
Default: `25`
58+
59+
List of ports to try for outgoing SMTP connections, in the order to be
60+
attempted.
61+
62+
For example, [best practices](https://www.cloudflare.com/learning/email-security/smtp-port-25-587/)
63+
suggest using SMTPS (port 587) by default. Use `587 465 25` to attempt the secure
64+
port first, then the alternate secure port, and then finally the non secure
65+
port 25 last.
66+
67+
---
68+
5669
### connect_timeout _duration_
5770
Default: `5m`
5871

internal/target/remote/connect.go

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,17 @@ func isVerifyError(err error) bool {
8383
// - tlsLevel TLS security level that was estabilished.
8484
// - tlsErr Error that prevented TLS from working if tlsLevel != TLSAuthenticated
8585
func (rd *remoteDelivery) connect(ctx context.Context, conn mxConn, host string, tlsCfg *tls.Config) (tlsLevel module.TLSLevel, tlsErr, err error) {
86+
return rd.connectPort(ctx, conn, host, smtpPort, tlsCfg)
87+
}
88+
89+
// connectPort attempts to connect to the MX, first trying STARTTLS with X.509
90+
// verification but falling back to unauthenticated TLS or plaintext as
91+
// necessary.
92+
//
93+
// Return values:
94+
// - tlsLevel TLS security level that was estabilished.
95+
// - tlsErr Error that prevented TLS from working if tlsLevel != TLSAuthenticated
96+
func (rd *remoteDelivery) connectPort(ctx context.Context, conn mxConn, host string, port string, tlsCfg *tls.Config) (tlsLevel module.TLSLevel, tlsErr, err error) {
8697
tlsLevel = module.TLSAuthenticated
8798
if rd.rt.tlsConfig != nil {
8899
tlsCfg = rd.rt.tlsConfig.Clone()
@@ -96,7 +107,7 @@ retry:
96107
// TLS errors separately hence starttls=false.
97108
_, err = conn.Connect(ctx, config.Endpoint{
98109
Host: host,
99-
Port: smtpPort,
110+
Port: port,
100111
}, false, nil)
101112
if err != nil {
102113
return module.TLSNone, nil, err
@@ -151,6 +162,10 @@ retry:
151162
}
152163

153164
func (rd *remoteDelivery) attemptMX(ctx context.Context, conn *mxConn, record *net.MX) error {
165+
return rd.attemptMXWithPort(ctx, conn, record, smtpPort)
166+
}
167+
168+
func (rd *remoteDelivery) attemptMXWithPort(ctx context.Context, conn *mxConn, record *net.MX, port string) error {
154169
mxLevel := module.MXNone
155170

156171
connCtx, cancel := context.WithCancel(ctx)
@@ -169,7 +184,7 @@ func (rd *remoteDelivery) attemptMX(ctx context.Context, conn *mxConn, record *n
169184
p.PrepareConn(ctx, record.Host)
170185
}
171186

172-
tlsLevel, tlsErr, err := rd.connect(connCtx, *conn, record.Host, rd.rt.tlsConfig)
187+
tlsLevel, tlsErr, err := rd.connectPort(connCtx, *conn, record.Host, port, rd.rt.tlsConfig)
173188
if err != nil {
174189
return err
175190
}
@@ -316,7 +331,12 @@ func (rd *remoteDelivery) newConn(ctx context.Context, domain string) (*mxConn,
316331
conn.dnssecOk = dnssecOk
317332

318333
var lastErr error
334+
ports := rd.rt.smtpPorts
335+
if len(ports) == 0 {
336+
ports = []string{smtpPort}
337+
}
319338
region = trace.StartRegion(ctx, "remote/Connect+TLS")
339+
recordsLoop:
320340
for _, record := range records {
321341
if record.Host == "." {
322342
return nil, &exterrors.SMTPError{
@@ -326,14 +346,16 @@ func (rd *remoteDelivery) newConn(ctx context.Context, domain string) (*mxConn,
326346
}
327347
}
328348

329-
if err := rd.attemptMX(ctx, &conn, record); err != nil {
330-
if len(records) != 0 {
331-
rd.Log.Error("cannot use MX", err, "remote_server", record.Host, "domain", domain)
349+
for _, port := range ports {
350+
if err := rd.attemptMXWithPort(ctx, &conn, record, port); err != nil {
351+
if len(records) != 0 {
352+
rd.Log.Error("cannot use MX", err, "remote_server", record.Host, "remote_port", port, "domain", domain)
353+
}
354+
lastErr = err
355+
continue
332356
}
333-
lastErr = err
334-
continue
357+
break recordsLoop
335358
}
336-
break
337359
}
338360
region.End()
339361

internal/target/remote/remote.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ type Target struct {
6666
ipv4 bool
6767
tlsConfig *tls.Config
6868

69+
smtpPorts []string
70+
6971
resolver dns.Resolver
7072
dialer func(ctx context.Context, network, addr string) (net.Conn, error)
7173
extResolver *dns.ExtResolver
@@ -112,6 +114,7 @@ func (rt *Target) Configure(inlineArgs []string, cfg *config.Map) error {
112114
cfg.String("local_ip", false, false, "", &rt.localIP)
113115
cfg.Bool("force_ipv4", false, false, &rt.ipv4)
114116
cfg.Bool("debug", true, false, &rt.Log.Debug)
117+
cfg.StringList("smtp_ports", false, true, []string{smtpPort}, &rt.smtpPorts)
115118
cfg.Custom("tls_client", true, false, func() (interface{}, error) {
116119
return &tls.Config{}, nil
117120
}, tls2.TLSClientBlock, &rt.tlsConfig)

0 commit comments

Comments
 (0)