Files
mailcloak/internal/kcpolicy/socketmap.go
2026-01-18 14:53:59 +00:00

86 lines
1.6 KiB
Go

package kcpolicy
import (
"bufio"
"context"
"fmt"
"net"
"os"
"strings"
)
func RunSocketmap(ctx context.Context, cfg *Config, db *AliasDB) error {
sock := cfg.Sockets.SocketmapSocket
_ = os.Remove(sock)
l, err := net.Listen("unix", sock)
if err != nil {
return err
}
defer l.Close()
if err := ChownChmodSocket(sock, cfg); err != nil {
return err
}
for {
conn, err := l.Accept()
if err != nil {
select {
case <-ctx.Done():
return nil
default:
return err
}
}
go handleSocketmapConn(conn, cfg, db)
}
}
// Socketmap protocol: "mapname key\n" -> "OK value\n" or "NOTFOUND\n" or "TEMP\n"
func handleSocketmapConn(conn net.Conn, cfg *Config, db *AliasDB) {
defer conn.Close()
r := bufio.NewReader(conn)
for {
line, err := r.ReadString('\n')
if err != nil {
return
}
line = strings.TrimSpace(line)
if line == "" {
continue
}
parts := strings.SplitN(line, " ", 2)
if len(parts) != 2 {
fmt.Fprint(conn, "TEMP\n")
continue
}
mapName := parts[0]
key := strings.ToLower(strings.TrimSpace(parts[1]))
if mapName != "alias" {
fmt.Fprint(conn, "NOTFOUND\n")
continue
}
// Only handle our domain
if !strings.HasSuffix(key, "@"+strings.ToLower(cfg.Policy.Domain)) {
fmt.Fprint(conn, "NOTFOUND\n")
continue
}
username, ok, err := db.AliasOwner(key)
if err != nil {
fmt.Fprint(conn, "TEMP\n")
continue
}
if !ok {
fmt.Fprint(conn, "NOTFOUND\n")
continue
}
// rewrite alias -> primary rcpt (username@domain)
fmt.Fprintf(conn, "OK %s@%s\n", username, strings.ToLower(cfg.Policy.Domain))
}
}