The mail filters ([[milter]]) of an SMTP session, such as Rspamd,
OpenDKIM, OpenDMARC, or ClamAV-milter, run with Sovite.Milter as
Postfix runs smtpd_milters.
Each milter of the listener (listener.milters, by default all of
them except on LMTP and re-injection listeners) gets its own
connection when the client connects, and sees every step of the
session: the client, EHLO, MAIL, each RCPT, then the header
fields and body of the message as they arrive. Milters run in order;
the first that rejects decides. Each sees the message as received.
Replies:
- reject:
550 5.7.1 Command rejected; temporary failure:451 4.7.1 Service unavailable - try again later; or the milter's own reply. AtRCPT, for that recipient only. - discard: the message is accepted and dropped.
- quarantine: the message goes to the hold queue.
- shutdown:
421and the session closes.
At the end of the message, the milters' changes are applied in order:
header fields added, inserted, changed, or deleted, recipients added
or removed, the sender changed, and the body replaced. The message is
then written again (Sovite.Queue.Spool.replace/3).
A milter that cannot be reached, times out, or breaks the protocol
gets its default_action: tempfail (the default) answers every
later command of the session with 451 4.7.1, reject with 550 5.7.1, and accept goes on without it.
Macros
Milters get the macros Postfix sends by default: j, {daemon_name},
{daemon_addr}, v, _, {client_addr}, {client_name},
{client_port}, {client_ptr} at connect; {tls_version},
{cipher}, {cipher_bits} at EHLO; i, {auth_type},
{auth_authen}, {mail_addr}, {mail_host}, {mail_mailer} at
MAIL; {rcpt_addr}, {rcpt_host}, {rcpt_mailer} at RCPT; and
i with the data, the end of the header, and the end of the message.
i is the queue ID.
Summary
Types
What the SMTP handler knows, for the macros: :hostname,
:connection, :client_dns, :tls, :identity, :mechanism,
:queue_id.
One milter of the session.
The outcome of a step.
The milters of a session: nil when the listener has none.
The outcome of the end of the message: what to do with it, and the changes to make.
Functions
The transaction ended without a message (RSET, or an aborted
message): the milters forget it.
A part of the body.
The session ended.
Connects to the milters and tells them about the client. Returns
{:close, reply, milters} when one refuses it.
DATA.
The end of the message. Returns the verdict and the changes the milters asked for, in order.
The header fields of the message (Sovite.Message.Headers fields,
Received: first), then the end of the header.
EHLO or HELO.
MAIL FROM.
The [[milter]] sections a listener uses, by name, in order.
RCPT TO. A rejection is about this recipient only.
Types
@type info() :: map()
What the SMTP handler knows, for the macros: :hostname,
:connection, :client_dns, :tls, :identity, :mechanism,
:queue_id.
@type milter() :: %{ config: map(), client: Sovite.Milter.t() | nil, failed: nil | :tempfail | :reject }
One milter of the session.
@type result() :: {:ok, t()} | {:reply, Sovite.SMTP.Reply.t(), t()} | {:close, Sovite.SMTP.Reply.t(), t()}
The outcome of a step.
@type t() :: [milter()] | nil
The milters of a session: nil when the listener has none.
@type verdict() :: :ok | {:reject, Sovite.SMTP.Reply.t()} | {:discard, String.t()} | {:hold, String.t()}
The outcome of the end of the message: what to do with it, and the changes to make.
Functions
The transaction ended without a message (RSET, or an aborted
message): the milters forget it.
A part of the body.
@spec close(t()) :: :ok
The session ended.
Connects to the milters and tells them about the client. Returns
{:close, reply, milters} when one refuses it.
DATA.
@spec end_of_message(t(), info()) :: {verdict(), [Sovite.Milter.modification()], t()}
The end of the message. Returns the verdict and the changes the milters asked for, in order.
@spec header(t(), [Sovite.Message.Headers.field()], info()) :: result()
The header fields of the message (Sovite.Message.Headers fields,
Received: first), then the end of the header.
EHLO or HELO.
MAIL FROM.
@spec opts(Sovite.Core.Config.t(), [String.t()]) :: [map()]
The [[milter]] sections a listener uses, by name, in order.
RCPT TO. A rejection is about this recipient only.