From feb8e6c37947b21baaa7dcf724ade0f2435a8280 Mon Sep 17 00:00:00 2001 From: Laurent Meunier Date: Tue, 9 Apr 2024 18:44:26 +0200 Subject: [PATCH] queue: retry with another IP when first attempt fails for dualstack remote servers mox was already giving another try for received errors after the `HELO`/`EHLO` command. Now mox do the same for received errors when trying to deliver the message to the remote SMTP server. This should help to deliver messages to SMTP server that rejects incoming messages because of bad ipv4 or ipv6 configuration (for example for servers checking reverse DNS records). mox will now try to deliver messages on both ip family instead before considering the error as permanent. fix #149 --- queue/direct.go | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/queue/direct.go b/queue/direct.go index fa1df5d..7be87ca 100644 --- a/queue/direct.go +++ b/queue/direct.go @@ -639,13 +639,15 @@ func deliverHost(log mlog.Log, resolver dns.Resolver, dialer smtpclient.Dialer, err = fmt.Errorf("storing recipient domain tls status: %w", err) } } - if err != nil { + + inspectError := func(err error) error { if cerr, ok := err.(smtpclient.Error); ok { // If we are being rejected due to policy reasons on the first // attempt and remote has both IPv4 and IPv6, we'll give it // another try. Our first IP may be in a block list, the address for // the other family perhaps is not. if cerr.Permanent && m0.Attempts == 1 && dualstack && strings.HasPrefix(cerr.Secode, "7.") { + log.Debugx("change error type from permanent to transient", err, slog.Any("host", host), slog.Any("secode", cerr.Secode)) cerr.Permanent = false } // If server does not implement requiretls, respond with that code. ../rfc/8689:301 @@ -653,9 +655,13 @@ func deliverHost(log mlog.Log, resolver dns.Resolver, dialer smtpclient.Dialer, cerr.Secode = smtp.SePol7MissingReqTLS30 metricRequireTLSUnsupported.WithLabelValues("norequiretls").Inc() } - err = cerr + return cerr } - return deliverResult{err: err} + return err + } + + if err != nil { + return deliverResult{err: inspectError(err)} } // SMTP session is ready. Finally try to actually deliver. @@ -695,7 +701,7 @@ func deliverHost(log mlog.Log, resolver dns.Resolver, dialer smtpclient.Dialer, resps, err := sc.DeliverMultiple(ctx, mailFrom, rcpts, size, msg, has8bit, smtputf8, m0.RequireTLS != nil && *m0.RequireTLS) if err != nil && len(resps) == len(msgResps) { // If error and it applies to all recipients, return a single error. - return deliverResult{err: err} + return deliverResult{err: inspectError(err)} } var ntodo []*msgResp for i, mr := range todo[:n] {