From 7b6be951250bc748d4e498e34e206e849d7d6a13 Mon Sep 17 00:00:00 2001 From: Aivars Sterns Date: Wed, 29 Oct 2025 11:50:30 +0200 Subject: [PATCH] feat: add alias file support --- aliases.go | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++ config.go | 17 +++++++++++++++++ main.go | 11 +++++++++++ smtp.go | 2 +- smtprelay.ini | 10 +++++++--- 5 files changed, 88 insertions(+), 4 deletions(-) create mode 100644 aliases.go diff --git a/aliases.go b/aliases.go new file mode 100644 index 0000000..7ff27ae --- /dev/null +++ b/aliases.go @@ -0,0 +1,52 @@ +package main + +import ( + "bufio" + "fmt" + "os" + "strings" +) + +type AliasMap map[string]string + +func AliasLoadFile(file string) (AliasMap, error) { + aliasMap := make(AliasMap) + count := 0 + log.Info(). + Str("file", file). + Msg("Loading aliases file") + + f, err := os.Open(file) + if err != nil { + log.Fatal(). + Str("file", file). + Err(err). + Msg("cannot load aliases file") + } + defer f.Close() + + scanner := bufio.NewScanner(f) + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + if line == "" { + continue + } + + parts := strings.Fields(line) + if len(parts) >= 2 { + aliasMap[parts[0]] = parts[1] + count++ + } + } + log.Info(). + Str("file", file). + Msg(fmt.Sprintf("Loaded %d aliases from file", count)) + + if err := scanner.Err(); err != nil { + log.Fatal(). + Str("file", file). + Err(err). + Msg("cannot load aliases file") + } + return aliasMap, nil +} diff --git a/config.go b/config.go index ac0ddb1..4c0e65e 100644 --- a/config.go +++ b/config.go @@ -42,6 +42,7 @@ var ( allowedSenderStr = flagset.String("allowed_sender", "", "Regular expression for valid FROM EMail addresses") allowedRecipStr = flagset.String("allowed_recipients", "", "Regular expression for valid TO EMail addresses") allowedUsers = flagset.String("allowed_users", "", "Path to file with valid users/passwords") + aliasesFile = flagset.String("aliases_file", "", "Path to aliases file") command = flagset.String("command", "", "Path to pipe command") remotesStr = flagset.String("remotes", "", "Outgoing SMTP servers") strictSender = flagset.Bool("strict_sender", false, "Use only SMTP servers with Sender matches to From") @@ -59,12 +60,27 @@ var ( allowedSender *regexp.Regexp allowedRecipients *regexp.Regexp remotes = []*Remote{} + aliasesList = AliasMap{} ) func localAuthRequired() bool { return *allowedUsers != "" } +func setupAliases() { + if *aliasesFile != "" { + aliases := make(AliasMap) + aliases, err := AliasLoadFile(*aliasesFile) + if err != nil { + log.Fatal(). + Str("file", *aliasesFile). + Err(err). + Msg("cannot load aliases file") + } + aliasesList = aliases + } +} + func setupAllowedNetworks() { for _, netstr := range splitstr(*allowedNetsStr, ' ') { baseIP, allowedNet, err := net.ParseCIDR(netstr) @@ -240,6 +256,7 @@ func ConfigLoad() { setupAllowedNetworks() setupAllowedPatterns() + setupAliases() setupRemotes() setupListeners() setupTimeouts() diff --git a/main.go b/main.go index aa4e306..0d8735a 100644 --- a/main.go +++ b/main.go @@ -159,6 +159,17 @@ func mailHandler(peer smtpd.Peer, env smtpd.Envelope) error { peerIP = addr.IP.String() } + // Check for aliases + for i, recipient := range env.Recipients { + if alias, exists := aliasesList[recipient]; exists { + env.Recipients[i] = alias + log.Info(). + Str("original_recipient", recipient). + Str("aliased_recipient", alias). + Msg("Recipient address aliased") + } + } + logger := log.With(). Str("from", env.Sender). Strs("to", env.Recipients). diff --git a/smtp.go b/smtp.go index 628e124..bee2bf1 100644 --- a/smtp.go +++ b/smtp.go @@ -200,7 +200,7 @@ func (c *Client) Auth(a smtp.Auth) error { return err } encoding := base64.StdEncoding - mech, resp, err := a.Start(&smtp.ServerInfo{c.serverName, c.tls, c.auth}) + mech, resp, err := a.Start(&smtp.ServerInfo{Name: c.serverName, TLS: c.tls, Auth: c.auth}) if err != nil { c.Quit() return err diff --git a/smtprelay.ini b/smtprelay.ini index a7183ee..93187ff 100644 --- a/smtprelay.ini +++ b/smtprelay.ini @@ -8,10 +8,13 @@ ;logfile = ; Log format: default, plain (no timestamp), json -;log_format = default +log_format = default ; Log level: panic, fatal, error, warn, info, debug, trace -;log_level = info +log_level = info + +; path to alias file +aliases_file = aliases.txt ; Hostname for this SMTP server ;hostname = localhost.localdomain @@ -21,7 +24,8 @@ ; Listen on the following addresses for incoming ; unencrypted connections. -;listen = 127.0.0.1:25 [::1]:25 +listen = 127.0.0.1:8025 +#[::1]:25 ; STARTTLS and TLS are also supported but need a ; SSL certificate and key.