milter, postfix, app, domain: cite the source of what they rely on (assumptions part B)
Comments only, no behaviour changed. go-milter v0.4.1 (session.go: one session per connection, replaced after a non-continue reply; macros per stage), Postfix 3.7.11 (milter8.c, smtpd_check.c reject_auth_sender_login_mismatch(), mail_addr_find.c, showq_compat.c, mail_conf_time.c, mail_params.h), Cyrus SASL 2.1.28 (saslpasswd.c, plugins/sasldb.c: deleting a missing login is not an error), OpenDKIM 2.11.0~beta2 (opendkim-db.c, opendkim-genkey), Berkeley DB 5.3 db_dump, rspamd 3.11.0. Comments that claimed what the source denies are corrected (sasldb keeps the password as given, not hashed; an embedded newline is kept, not truncated; rspamd sorts symbols by absolute weight); fixtures say seen or composed. The sender check's From: parsing gets no citations: it is replaced by the port of OpenDKIM's dkim_mail_parse next. Contradictions left for their own commits: B-G5 cleanAddress, B-PS1 first key wins, B-PQ1 the forced-expire flag, B-PQ10/PQ11 Postfix's time units. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -9,6 +9,10 @@
|
||||
// or Accept so a failure of this milter can never block the relay. Postfix is
|
||||
// configured with default_action=accept for this milter's socket, so even a
|
||||
// crash or hang fails open (architecture.md § Mail path).
|
||||
//
|
||||
// Postfix 3.7.11, src/milter/milter8.c, milter8_comm_error(): a milter that
|
||||
// cannot be reached, or does not answer within milter_*_timeout, gives no
|
||||
// reply with default_action=accept - the message goes on.
|
||||
package milter
|
||||
|
||||
import (
|
||||
@@ -45,6 +49,12 @@ type Store interface {
|
||||
// the queue-id at end-of-message. go-milter creates one session per
|
||||
// connection; a connection may carry several messages, so per-message fields
|
||||
// are reset at MailFrom (the start of every transaction).
|
||||
//
|
||||
// go-milter v0.4.1, session.go, Process(), case CodeMacro: every macro packet
|
||||
// replaces the map; server.go, Serve(): NewMilter() for each connection.
|
||||
// Postfix 3.7.11, src/milter/milter8.c, milter8_event(): a macro packet, empty
|
||||
// or not, goes before every event (milter.c, milter_macro_lookup(), never
|
||||
// returns null).
|
||||
type session struct {
|
||||
milter.NoOpMilter
|
||||
rec Store
|
||||
@@ -56,7 +66,9 @@ type session struct {
|
||||
// clientIP is the client's address. Postfix gives it at Connect, once per
|
||||
// connection, and again with every MAIL FROM as the {client_addr} macro
|
||||
// (milter_mail_macros in build/postfix-config.sh). The second is the one
|
||||
// that can be relied on: see MailFrom.
|
||||
// that can be relied on: see MailFrom. Postfix 3.7.11,
|
||||
// src/smtpd/smtpd_milter.c, smtpd_milter_eval(): {client_addr} is
|
||||
// state->rfc_addr (see clientAddr).
|
||||
clientIP string
|
||||
|
||||
login string
|
||||
@@ -68,6 +80,11 @@ type session struct {
|
||||
|
||||
// Connect captures the client IP of a connection's first message. It is not
|
||||
// enough on its own - see MailFrom.
|
||||
//
|
||||
// Postfix 3.7.11, src/smtpd/smtpd.c, smtpd_proto(): milter_conn_event() is
|
||||
// given state->addr, the address without "IPv6:". go-milter v0.4.1,
|
||||
// session.go, case CodeConn: net.ParseIP of it - nil for "unknown" and for a
|
||||
// family that sends no address.
|
||||
func (s *session) Connect(host, family string, port uint16, addr net.IP, m *milter.Modifier) (milter.Response, error) {
|
||||
if addr != nil {
|
||||
s.clientIP = addr.String()
|
||||
@@ -92,6 +109,17 @@ func (s *session) Connect(host, family string, port uint16, addr net.IP, m *milt
|
||||
// client address: the level-2 limit and an application's client-address list
|
||||
// both let it through. (Found on 2026-10-12; test/e2e sends two MAIL FROM on
|
||||
// one connection.)
|
||||
//
|
||||
// go-milter v0.4.1, session.go, HandleMilterCommands(): after a reply whose
|
||||
// Continue() is false (response.go: accept, discard, reject, tempfail) the
|
||||
// backend is replaced by NewMilter(). Postfix 3.7.11, src/milter/milter8.c,
|
||||
// milter8_conn_event(): CONNECT goes once per milter connection.
|
||||
// {auth_authen}: src/smtpd/smtpd_milter.c, smtpd_milter_eval(), gives
|
||||
// state->sasl_username, null without authentication, and then milter.c,
|
||||
// milter_macro_lookup(), sends no such macro.
|
||||
// The tempfail: milter8.c, milter8_event(), SMFIR_TEMPFAIL is "451 4.7.1";
|
||||
// src/smtpd/smtpd.c, mail_cmd(), replies it and calls mail_reset() - the
|
||||
// transaction ends, the connection goes on.
|
||||
func (s *session) MailFrom(from string, m *milter.Modifier) (milter.Response, error) {
|
||||
s.releaseReservations() // a previous transaction that ended without EOM/ABORT
|
||||
if ip := clientAddr(m); ip != "" {
|
||||
@@ -114,6 +142,10 @@ func (s *session) MailFrom(from string, m *milter.Modifier) (milter.Response, er
|
||||
|
||||
// RcptTo records each recipient. Postfix calls this once per recipient, which
|
||||
// is what lets the journal keep a separate row per (queue-id, recipient).
|
||||
//
|
||||
// Postfix 3.7.11, src/smtpd/smtpd.c, rcpt_cmd(): milter_rcpt_event() once per
|
||||
// RCPT TO; a recipient Postfix refused goes only to a milter that asked for
|
||||
// SMFIP_RCPT_REJ, which Serve's Protocol does not.
|
||||
func (s *session) RcptTo(rcpt string, m *milter.Modifier) (milter.Response, error) {
|
||||
s.rcpts = append(s.rcpts, cleanAddress(rcpt))
|
||||
return milter.RespContinue, nil
|
||||
@@ -132,6 +164,12 @@ func (s *session) Header(name, value string, m *milter.Modifier) (milter.Respons
|
||||
// rows are written. The reply is "continue", which at end-of-message lets the
|
||||
// message through just as "accept" does - and, unlike it, leaves this session
|
||||
// in place for the connection's next message (see MailFrom).
|
||||
//
|
||||
// go-milter v0.4.1, session.go, case CodeEOB: Body(). Postfix 3.7.11,
|
||||
// src/global/mail_params.h: DEF_MILT_EOD_MACROS "i", which
|
||||
// src/cleanup/cleanup_milter.c, cleanup_milter_eval(), answers with the queue
|
||||
// id. src/milter/milter8.c, milter8_event(): SMFIR_CONTINUE returns the
|
||||
// milter's default reply, none, and the message goes on.
|
||||
func (s *session) Body(m *milter.Modifier) (milter.Response, error) {
|
||||
s.record(macro(m, "i"))
|
||||
// The rows are in the send log now, so the stored count sees this message
|
||||
@@ -143,6 +181,11 @@ func (s *session) Body(m *milter.Modifier) (milter.Response, error) {
|
||||
// Abort ends the current transaction without an end-of-message (client RSET, or
|
||||
// Postfix rejecting the message for its own reasons). No send-log row will be
|
||||
// written, so the level-2 slots this message held must go back.
|
||||
//
|
||||
// Postfix 3.7.11, src/smtpd/smtpd.c: rset_cmd() and the refusals call
|
||||
// mail_reset(), which calls milter_abort() (src/milter/milter8.c,
|
||||
// milter8_abort(): SMFIC_ABORT); so do the end of every transaction and the
|
||||
// end of the session in smtpd_proto().
|
||||
func (s *session) Abort(m *milter.Modifier) error {
|
||||
s.releaseReservations()
|
||||
s.rcpts = nil
|
||||
@@ -154,6 +197,10 @@ func (s *session) Abort(m *milter.Modifier) error {
|
||||
// multi-character macro names in curly braces (e.g. {auth_authen}) while
|
||||
// single-character names (e.g. i) arrive bare. go-milter stores whatever name
|
||||
// Postfix sends verbatim, so a lookup must try both forms.
|
||||
//
|
||||
// Postfix 3.7.11, src/milter/milter.c, milter_macro_lookup(): each name is
|
||||
// sent as milter_*_macros spells it. go-milter v0.4.1, session.go, case
|
||||
// CodeMacro: m.macros[name] = value, the name as received.
|
||||
func macro(m *milter.Modifier, name string) string {
|
||||
if v, ok := m.Macros[name]; ok {
|
||||
return v
|
||||
@@ -164,6 +211,10 @@ func macro(m *milter.Modifier, name string) string {
|
||||
// clientAddr is the client's address from the {client_addr} macro, or "" when
|
||||
// Postfix did not send one that reads as an address. An IPv6 address may come
|
||||
// in Sendmail's form, with "IPv6:" in front.
|
||||
//
|
||||
// Postfix 3.7.11, src/smtpd/smtpd_peer.c, smtpd_peer_sockaddr_to_hostaddr():
|
||||
// rfc_addr is the dotted quad, "IPv6:" + the address for IPv6, a V4-mapped
|
||||
// address rewritten to IPv4, or "unknown".
|
||||
func clientAddr(m *milter.Modifier) string {
|
||||
raw := strings.TrimSpace(macro(m, "client_addr"))
|
||||
if len(raw) > 5 && strings.EqualFold(raw[:5], "IPv6:") {
|
||||
@@ -248,6 +299,12 @@ func domainOf(addr string) string {
|
||||
|
||||
// Serve runs the journal-milter on ln until ctx is cancelled. Each connection
|
||||
// gets a fresh session bound to rec. It returns nil on a clean shutdown.
|
||||
//
|
||||
// go-milter v0.4.1, server.go: Close() closes the listener and Serve() then
|
||||
// returns ErrServerClosed. Postfix 3.7.11, src/milter/milter8.c,
|
||||
// milter8_message(): SMFIP_NOBODY leaves out only the body chunks - headers,
|
||||
// end-of-headers and end-of-body (milter8_eob()) still go; milter8_event()
|
||||
// takes a reply code (SMFIR_REPLYCODE) whatever the action mask.
|
||||
func Serve(ctx context.Context, ln net.Listener, rec Store) error {
|
||||
flight := &inflight{} // shared: the level-2 window spans all connections
|
||||
srv := &milter.Server{
|
||||
|
||||
Reference in New Issue
Block a user