Next: , Previous: , Up: MFL   [Contents][Index]

4.14 Handlers

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:

Handler: connect (string $1, number $2, number $3, string $4)
Invocation:

This handler is called once at the beginning of each SMTP connection.

Arguments:
  1. 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]’).
  2. number; Socket address family. You need to require the ‘status’ module to get symbolic definitions for the address families. Supported families are:
    ConstantValueMeaning
    FAMILY_STDIO0Standard input/output (the MTA is run with -bs option)
    FAMILY_UNIX1UNIX socket
    FAMILY_INET2IPv4 protocol
    FAMILY_INET63IPv6 protocol

    Table 4.3: Supported socket families

  3. number; Port number if ‘$2’ is ‘FAMILY_INET’.
  4. 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 following: QUIT, HELO, NOOP, which are processed as usual.

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 mailfromd 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: etc/sendmail-8.13.7.connect.diff, for Sendmail versions 8.13.x, and etc/sendmail-8.14.3.connect.diff, for Sendmail versions 8.14.3.

Handler: helo (string $1)
Invocation:

This handler is called whenever the SMTP client sends HELO or EHLO command. Depending on the actual MTA configuration, it can be called several times or even not at all.

Arguments:
  1. string; Argument to HELO (EHLO) commands.
Notes:

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 heloarg_test function (see heloarg_test) if you wish to analyze this value.

Handler: envfrom (string $1, string $2)
Invocation:

Called when the SMTP client sends MAIL FROM command, i.e. once at the beginning of each message.

Arguments:
  1. string; First argument to the MAIL FROM command, i.e. the email address of the sender.
  2. string; Rest of arguments to MAIL FROM separated by space character. This argument can be ‘""’.
Notes
  1. $1 is not the same as $f Sendmail variable, because the latter contains the sender email after address rewriting and normalization, while $1 contains exactly the value given by sending party.
  2. When the array type is implemented, $2 will contain an array of arguments.
Handler: envrcpt (string $1, string $2)
Invocation:

Called once for each RCPT TO command, i.e. once for each recipient, immediately after envfrom.

Arguments:
  1. string; First argument to the RCPT TO command, i.e. the email address of the recipient.
  2. string; Rest of arguments to RCPT TO separated by space character. This argument can be ‘""’.
Notes:

When the array type is implemented, $2 will contain an array of arguments.

Handler: data ()
Invocation:

Called after the MTA receives SMTPDATA’ command. Notice that this handler is not supported by Sendmail versions prior to 8.14.0 and Postfix versions prior to 2.5.

Arguments:

None

Handler: header (string $1, string $2)
Invocation:

Called once for each header line received after SMTP DATA command.

Arguments:
  1. string; Header field name.
  2. 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.
Handler: eoh
Invocation:

This handler is called once per message, after all headers have been sent and processed.

Arguments:

None.

Handler: body (pointer $1, number $2)
Invocation:

This header is called zero or more times, for each piece of the message body obtained from the remote host.

Arguments:
  1. pointer; Piece of body text. See ‘Notes’ below.
  2. number; Length of data pointed to by $1, in bytes.
Notes:

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 eom handler (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
Handler: eom
Invocation:

This handler is called once per message, when the terminating dot after DATA command has been received.

Arguments:

None

Notes:

This handler is useful for calling message capturing functions, such as sa or 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:

Handler$1$2$3$4
connectHostnameSocket FamilyPortRemote address
heloHELO domainN/AN/AN/A
envfromSender email addressRest of argumentsN/AN/A
envrcptRecipient email addressRest of argumentsN/AN/A
headerHeader nameHeader valueN/AN/A
eohN/AN/AN/AN/A
bodyBody segment (pointer)Length of the segment (numeric)N/AN/A
eomN/AN/AN/AN/A

Table 4.4: State Handler Arguments


Up: Handlers   [Contents][Index]

4.14.1 Multiple Handler Definitions

Any handler may be declared multiple times. When compiling the filter program, mailfromd combines the code from all prog declarations having the same handler name into one code block and compiles it. The resulting code is guaranteed to be executed in the order in which it appears in the source files.


Up: Handlers   [Contents][Index]