1
0
mirror of https://blitiri.com.ar/repos/chasquid synced 2026-01-09 17:55:57 +00:00

aliases: Implement "via" aliases

This patch implements "via" aliases, which let us explicitly select a
server to use for delivery.

This feature is useful in different scenarios, such as a secondary MX
server that forwards all incoming email to a primary.

For now, it is experimental and the syntax and semantics are subject to
change.
This commit is contained in:
Alberto Bertogli
2025-04-06 12:35:51 +01:00
parent 1cf24ba94a
commit 9999a69086
28 changed files with 671 additions and 74 deletions

View File

@@ -8,4 +8,9 @@ type Courier interface {
// Deliver mail to a recipient. Return the error (if any), and whether it
// is permanent (true) or transient (false).
Deliver(from string, to string, data []byte) (error, bool)
// Forward mail using the given servers.
// Return the error (if any), and whether it is permanent (true) or
// transient (false).
Forward(from string, to string, data []byte, servers []string) (error, bool)
}

View File

@@ -3,6 +3,7 @@ package courier
import (
"bytes"
"context"
"errors"
"fmt"
"os/exec"
"strings"
@@ -108,3 +109,11 @@ func sanitizeForMDA(s string) string {
}
return strings.Map(valid, s)
}
var errForwardNotSupported = errors.New(
"forwarding not supported by the MDA courier")
// Forward is not supported by the MDA courier.
func (p *MDA) Forward(from string, to string, data []byte, servers []string) (error, bool) {
return errForwardNotSupported, true
}

View File

@@ -123,3 +123,15 @@ func TestSanitize(t *testing.T) {
}
}
}
func TestForward(t *testing.T) {
p := MDA{"thisdoesnotexist", nil, 1 * time.Minute}
err, permanent := p.Forward(
"from", "to", []byte("data"), []string{"server"})
if err != errForwardNotSupported {
t.Errorf("unexpected error: %v", err)
}
if !permanent {
t.Errorf("expected permanent, got transient")
}
}

View File

@@ -63,7 +63,7 @@ func (s *SMTP) Deliver(from string, to string, data []byte) (error, bool) {
to: to,
toDomain: envelope.DomainOf(to),
data: data,
tr: trace.New("Courier.SMTP", to),
tr: trace.New("Courier.SMTP.Deliver", to),
}
defer a.tr.Finish()
a.tr.Debugf("%s -> %s", from, to)
@@ -105,6 +105,42 @@ func (s *SMTP) Deliver(from string, to string, data []byte) (error, bool) {
return a.tr.Errorf("all MXs returned transient failures (last: %v)", err), false
}
// Forward an email. On failures, returns an error, and whether or not it is
// permanent.
func (s *SMTP) Forward(from string, to string, data []byte, servers []string) (error, bool) {
a := &attempt{
courier: s,
from: from,
to: to,
toDomain: envelope.DomainOf(to),
data: data,
tr: trace.New("Courier.SMTP.Forward", to),
}
defer a.tr.Finish()
a.tr.Debugf("%s -> %s", from, to)
// smtp.Client.Mail will add the <> for us when the address is empty.
if a.from == "<>" {
a.from = ""
}
var err error
for _, server := range servers {
var permanent bool
err, permanent = a.deliver(server)
if err == nil {
return nil, false
}
if permanent {
return err, true
}
a.tr.Errorf("%q returned transient error: %v", server, err)
}
// We exhausted all servers, try again later.
return a.tr.Errorf("all servers returned transient failures (last: %v)", err), false
}
type attempt struct {
courier *SMTP