Stub filter
Filters support for smtpd(8) landed in OpenBSD tree. gilles@ published a Python bridge and a case study using it with rspamd. Don't miss updates and other interesting entries from his blog.
A stub filter, as the name suggests, registers for the hooks it's interested in and lets the SMTP session go through unaltered. A version from an earlier iteration was instrumental in learning how filters fit in. Writing a similar one with the current code is even more simpler. Filters are standalone programs that smtpd(8) fork/execs and the filter communicates with smtpd(8) via its stdin/stdout. With this basic /etc/mail/smtpd.conf…
table aliases file:/etc/mail/aliases
filter stub proc-exec "/usr/local/bin/stub"
listen on lo0 filter stub
action "local" mbox alias <aliases>;
action "relay" relay
match for local action "local"
match for any action "relay"
build the following golang code and place the executable in /usr/local/bin as stub…
package main
import (
"bufio"
"fmt"
"os"
"strings"
)
func main() {
hooks := []string{
"connect",
"ehlo",
"mail-from",
"rcpt-to",
"data",
"data-line",
"quit",
}
for _, hook := range hooks {
fmt.Printf("register|filter|smtp-in|%s\n", hook)
}
fmt.Println("register|ready")
stdin := bufio.NewScanner(os.Stdin)
for stdin.Scan() {
fields := strings.Split(stdin.Text(), "|")
event, token, session_id := fields[4], fields[5], fields[6]
switch event {
case "data-line":
fmt.Printf("filter-dataline|%s|%s|%s\n",
token, session_id,
strings.Join(fields[7:], "|"))
default:
fmt.Printf("filter-result|%s|%s|proceed\n",
token, session_id)
}
}
}
The stub registration, its ready notification and communicating with smtpd(8) during an SMTP session appear like this…
client - smtpd smtpd - filter
-------------- --------------
F: register|filter|smtp-in|connect
F: register|filter|smtp-in|ehlo
F: register|filter|smtp-in|mail-from
F: register|filter|smtp-in|rcpt-to
F: register|filter|smtp-in|data
F: register|filter|smtp-in|data-line
F: register|filter|smtp-in|quit
F: register|ready
C: nc localhost 25
S: filter|1|15455|smtp...
F: filter-result|9405c|49974f|proceed
S: 220 foo ESMTP OpenSMTPD
C: EHLO localhost
S: filter|1|15455|smtp-in|ehlo|9405c|49974f|localhost
F: filter-result|9405c1f7|d9d49974f|proceed
S: 250-foo Hello localhost [127.0.0.1], pleased to meet you
S: 250-8BITMIME
S: 250-ENHANCEDSTATUSCODES
S: 250-SIZE 36700160
S: 250-DSN
S: 250 HELP
...
...
Where C = client, S = smtpd, F = Stub filter