General-Purpose Mail Filter
When the running program encounters a condition it is not able to handle, it signals an exception. To illustrate the concept, let’s consider the execution of the following code fragment:
if primitive_hasmx(domainpart($f)) accept fi
primitive_hasmx (see primitive_hasmx) tests whether the
domain name given as its argument has any ‘MX’ records. It should
return a boolean value. However, when querying the Domain Name
System, it may fail to get a definite result. For example, the DNS
server can be down or temporary unavailable. In other words,
primitive_hasmx can be in a situation when, instead of returning
‘yes’ or ‘no’, it has to return ‘don't know’. It has
no way of doing so, therefore it signals an exception.
Each exception is identified by exception type, an integer number associated with it.
The lowest 19 exception numbers are reserved for
built-in exceptions. These are declared in module status.mf.
The following table summarizes all built-in exception types implemented by
mailfromd version 8.7:
General database failure. For example, the database cannot be opened. This exception can be signaled by any function that queries any DBM database.
Division by zero.
This exception is emitted by
dbinsert built-in if the
requested key is already present in the database (see dbinsert).
Function reached end of file while reading. See I/O functions, for a description of functions that can signal this exception.
A general failure has occurred. In particular, this exception is
signaled by DNS lookup functions when any permanent failure occurs.
This exception can be signaled by any DNS-related function
poll, etc.) or operation (
Invalid input format. This exception is signaled if input data to a
function are improperly formatted. In version 8.7 it is
message_burst function if its input message is not
formatted according to RFC 934. See Message digest functions.
Invalid CIDR notation. This is signaled by
when its second argument is not a valid CIDR.
Invalid IP address. This is signaled by
when its first argument is not a valid IP address.
Invalid time interval specification. It is signaled by
interval function if its argument is not a valid time interval
(see time interval specification).
An error occurred during the input-output operation. See I/O functions, for a description of functions that can signal this exception.
A Sendmail macro is undefined.
The argument of a DNS-related function cannot be resolved to host
name or IP address. Currently only
ismx (see ismx) raises
The supplied argument is outside the allowed range. This is
signalled, for example, by
substring function (see substring).
Regular expression cannot be compiled. This can happen when a
regular expression (a right-hand argument of a
operator) is built at the runtime and the produced string is an
String-to-number conversion failed. This can be signaled when a string is used in numeric context which cannot be converted to the numeric data type. For example:
set x "10a" if x / 2 …
if condition will signal
ston_conv, since ‘10a’
cannot be converted to a number.
A temporary failure has occurred. This can be signaled by DNS-related functions or operations.
The supplied URL is invalid. See Interfaces to Third-Party Programs.
In addition to these, two symbols are defined that are not exception
types in the strict sense of the world, but are provided to make
writing filter scripts more convenient. These are
meaning successful return from a function, and
meaning that the required entity (e.g. domain name or email address)
was not found. See Figure 4.1, for an illustration on
how these can be used. For consistency with other exception codes,
these can be spelled as
You can define your own exception types using the
In this statement, type must be a valid MFL
identifier, not used for another constant (see Constants).
dclex statement defines a new exception identified by
the constant type and allocates a new exception number for it.
The type can subsequently be used in
catch statements, for example:
dclex myrange number fact(number val) returns number do if val < 0 throw myrange "fact argument is out of range" fi … done
Normally when an exception is signalled, the program execution is
terminated and the MTA is returned a
status. Additional information regarding the exception is then output
to the logging channel (see Logging and Debugging). However, the
user can intercept any exception by installing his own
An exception-handling routine is introduced by a try–catch statement, which has the following syntax:
try do stmtlist done catch exception-list do handler-body done
where stmtlist and handler-body are sequences of
MFL statements and exception-list is the list of
exception types, separated by the word
or. A special
exception-list ‘*’ is allowed and means all exceptions.
This construct works as follows. First, the statements from stmtlist are executed. If the execution finishes successfully, control is passed to the first statement after the ‘catch’ block. Otherwise, if an exception is signalled and this exception is listed in exception-list, the execution is passed to the handler-body. If the exception is not listed in exception-list, it is handled as usual.
The following example shows a ‘try--catch’ construct used for
handling eventual exceptions, signalled by
try do if primitive_hasmx(domainpart($f)) accept else reject fi done catch e_failure or e_temp_failure do echo "primitive_hasmx failed" continue done
The ‘try--catch’ statement can appear anywhere inside a function or a handler, but it cannot appear outside of them. It can also be nested within another ‘try--catch’, in either of its parts. Upon exit from a function or milter handler, all exceptions are restored to the state they had when it has been entered.
catch block can also be used alone, without preceding
part. Such a construct is called a standalone catch. It is
mostly useful for setting global exception handlers in a
statement (see begin/end). When used within a usual function or
handler, the exception handlers set by a standalone catch
remain in force until either another standalone catch appears further
in the same function or handler, or an end of the function is
encountered, whichever occurs first.
A standalone catch defined within a function must return from
it by executing
return statement. If it does not do that
explicitly, the default value of 1 is returned. A standalone catch
defined within a milter handler must end execution with any of the
tempfail. By default,
It is not recommended to mix ‘try--catch’ constructs and standalone catches. If a standalone catch appears within a ‘try--catch’ statement, its scope of visibility is undefined.
Upon entry to a handler-body, two implicit positional arguments
are defined, which can be referenced in handler-body as
$2. The first argument gives the numeric code of the
exception that has occurred. The second argument is a textual string
containing a human-readable description of the exception.
The following is an improved version of the previous example, which uses these parameters to supply more information about the failure:
try do if primitive_hasmx(domainpart($f)) accept else reject fi done catch e_failure or e_temp_failure do echo "Caught exception $1: $2" continue done
The following example defines the function
returns true if the domain part of its argument has any ‘MX’ records, and
false if it does not or if an exception occurs 15.
func hasmx (string s) returns number do try do return primitive_hasmx(domainpart(s)) done catch * do return 0 done done
The same function can written using standalone
func hasmx (string s) returns number do catch * do return 0 done return primitive_hasmx(domainpart(s)) done
All variables remain visible within
catch body, with the
exception of positional arguments of the enclosing handler. To access
positional arguments of a handler from the
catch body, assign
them to local variables prior to the ‘try--catch’ construct, e.g.:
prog header do string hname $1 string hvalue $2 try do … done catch * do echo "Exception $1 while processing header %hname: %hvalue" echo $2 tempfail done
You can also generate (or raise) exceptions explicitly in the
throw excode descr
The arguments correspond exactly to the positional parameters of the
catch statement: excode gives the numeric code of the
exception, descr gives its textual description. This statement
can be used in complex scripts to create non-local exits from deeply
Notice, that the the excode argument must be an immediate
value: an exception identifier (either a built-in one or one declared
previously using 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.