General-Purpose Mail Filter
Milter stage handler (or handler, for short) is a subroutine responsible for processing a particular milter state. There are eight handlers available. Their order of invocation and arguments are described in Figure 3.1.
A handler is defined using the following construct:
prog handler-name do handler-body done
where handler-name is the name of the handler (see handler names), handler-body is the list of filter statements composing the handler body. Some handlers take arguments, which can be accessed within the handler-body using the notation $n, where n is the ordinal number of the argument. Here we describe the available handlers and their arguments:
This handler is called once at the beginning of each SMTP connection.
string; The host name of the message sender, as reported by MTA. Usually it is determined by a reverse lookup on the host address. If the reverse lookup fails, ‘$1’ will contain the message sender’s IP address enclosed in square brackets (e.g. ‘[127.0.0.1]’).
number; Socket address family. You need to require the ‘status’ module to get symbolic definitions for the address families. Supported families are:
number; Port number if ‘$2’ is ‘FAMILY_INET’.
string; Remote IP address if ‘$2’ is ‘FAMILY_INET’ or full file name of the socket if ‘$2’ is ‘FAMILY_UNIX’. If ‘$2’ is ‘FAMILY_STDIO’, ‘$4’ is an empty string.
The actions (see Actions) appearing in this handler
are handled by Sendmail in a special way. First of all, any textual
message is ignored. Secondly, the only action that immediately closes
the connection is
tempfail 421. Any other reply codes result in
Sendmail switching to nullserver mode, where it accepts any
commands, but answers with a failure to any of them, except for the
NOOP, which are processed
The following table summarizes the Sendmail behavior depending on the action used:
tempfail 421 excode message
The caller is returned the following error message:
421 4.7.0 hostname closing connection
Both excode and message are ignored.
tempfail 4xx excode message
(where xx represents any digits, except ‘21’) Both excode and message are ignored. Sendmail switches to nullserver mode. Any subsequent command, excepting the ones listed above, is answered with
454 4.3.0 Please try again later
reject 5xx excode message
(where xx represents any digits). All arguments are ignored. Sendmail switches to nullserver mode. Any subsequent command, excepting ones listed above, is answered with
550 5.0.0 Command rejected
Regarding reply codes, this behavior complies with RFC 2821 (section 3.9), which states:
An SMTP server must not intentionally close the connection except:
- After detecting the need to shut down the SMTP service and returning a 421 response code. This response code can be issued after the server receives any command or, if necessary, asynchronously from command receipt (on the assumption that the client will receive it after the next command is issued).
However, the RFC says nothing about textual messages and
extended error codes, therefore Sendmail’s ignoring of these is,
in my opinion, absurd. My practice shows that it is often reasonable,
and even necessary, to return a meaningful textual message if the
initial connection is declined. The opinion of
users seems to support this view. Bearing this in mind,
mailfromd is shipped with a patch for Sendmail,
which makes it honor both extended return code and textual message given
with the action. Two versions are provided:
Sendmail versions 8.13.x, and
etc/sendmail-8.14.3.connect.diff, for Sendmail versions 8.14.3.
This handler is called whenever the SMTP client sends
EHLO command. Depending on the actual MTA configuration, it
can be called several times or even not at all.
string; Argument to
According to RFC 28221,
$1 must be domain name of the
sending host, or, in case this is not available, its IP address
enclosed in square brackets. Be careful when taking decisions based
on this value, because in practice many hosts send arbitrary strings.
We recommend to use
(see heloarg_test) if you wish to analyze this value.
Called when the SMTP client sends
MAIL FROM command, i.e. once
at the beginning of each message.
string; First argument to the
MAIL FROMcommand, i.e. the email address of the sender.
string; Rest of arguments to
MAIL FROMseparated by space character. This argument can be ‘""’.
$1is not the same as
$fSendmail variable, because the latter contains the sender email after address rewriting and normalization, while
$1contains exactly the value given by sending party.
$2will contain an array of arguments.
Called once for each
RCPT TO command, i.e. once for each
recipient, immediately after
string; First argument to the
RCPT TOcommand, i.e. the email address of the recipient.
string; Rest of arguments to
RCPT TOseparated by space character. This argument can be ‘""’.
When the array type is implemented,
$2 will contain
an array of arguments.
Called after the MTA receives SMTP ‘DATA’ command. Notice that this handler is not supported by Sendmail versions prior to 8.14.0 and Postfix versions prior to 2.5.
Called once for each header line received after SMTP
string; Header field name.
string; Header field value. The content of the header may include folded white space, i.e., multiple lines with following white space where lines are separated by LF (ASCII 10). The trailing line terminator (CR/LF) is removed.
This handler is called once per message, after all headers have been sent and processed.
This header is called zero or more times, for each piece of the message body obtained from the remote host.
pointer; Piece of body text. See ‘Notes’ below.
number; Length of data pointed to by
$1, in bytes.
The first argument points to the body chunk. Its size may be quite
considerable and passing it as a string may be costly both in terms of
memory and execution time. For this reason it is not passed as a
string, but rather as a generic pointer, i.e. an object having
the same size as
number, which can be used to retrieve the
actual contents of the body chunk if the need arises.
A special function
body_string is provided to convert this
object to a regular MFL string (see Mail body functions). Using it you can collect the entire body text into a
single global variable, as illustrated by the following example:
string text prog body do set text text . body_string($1,$2) done
The text collected this way can then be used in the
(see below) to parse and analyze it.
If you wish to analyze both the headers and mail body, the following code fragment will do that for you:
string text # Collect all headers. prog header do set text text . $1 . ": " . $2 . "\n" done # Append terminating newline to the headers. prog eoh do set text "%text\n" done # Collect message body. prog body do set text text . body_string($1, $2) done
This handler is called once per message, when the terminating dot
DATA command has been received.
This handler is useful for calling message capturing functions,
clamav. For more information about these,
refer to Interfaces to Third-Party Programs.
For your reference, the following table shows each handler with its arguments:
|connect||Hostname||Socket Family||Port||Remote address|
|envfrom||Sender email address||Rest of arguments||N/A||N/A|
|envrcpt||Recipient email address||Rest of arguments||N/A||N/A|
|header||Header name||Header value||N/A||N/A|
|body||Body segment (pointer)||Length of the segment (numeric)||N/A||N/A|
This document was generated on January 3, 2019 using makeinfo.Verbatim copying and distribution of this entire article is permitted in any medium, provided this notice is preserved.